/* ===== InStory NEW — базовые стили каркаса (Фаза 2.1) ===== */

:root {
  /* ===== InStory 4 — тёплая редакционная палитра (фаза 1, 08.10.2026) =====
     По решению владельца приложение выглядит как тёплое киношное издание:
     фон — тёплая слоновая кость, основной акцент действия — лесной зелёный,
     а янтарь сдержанный: только тихие меты (волосок-разделитель, точка эфира,
     метка активной вкладки), не крупные плоскости. Тёмный ночной вид остался
     сменной темой (.theme-night), как и «Пергамент» (.theme-sepia) — светлая
     база не отняла у читателя ночь. Цвета берём ТОЛЬКО отсюда (docs/UI_KIT.md).

     Размеры-ступени, грамматическая подсветка и вспышки по частям речи ниже
     заданы тёмными версиями: раньше база была ночной, теперь светлая, и на
     светлом фоне пастель не читается. */
  /* Фаза 3 (08.10.2026): палитра стала нейтральнее и тише. Бумага — «тёплый
     камень» (не розовая, не кремовая), текст — угольный графит, акцент
     действия — сдержанная бирюза (не лесной зелёный и не янтарь), и только
     янтарь/бронза остаются тихой метой внимания. Розовых и лиловых плоскостей
     в оформлении нет: розовый и лиловый живут ТОЛЬКО в грамматической
     подсветке частей речи (--flash-prep / --flash-pron) — там это смысл
     обучения, а не украшение, и смысл сохраняется. */
  --bg: #EDE7DC;
  --bg-elevated: #F7F3EB;
  --bg-sheet: #E3DCCE;
  --text: #221F1A;
  --text-dim: #6A6458;
  --accent: #2C5E56;
  --accent-soft: rgba(44, 94, 86, 0.09);
  --on-accent: #F6F3EC;
  --amber: #9C6E2A;
  --amber-soft: rgba(156, 110, 42, 0.12);
  /* Метка «живого» (точка и слово «в эфире»). Цвет тёплый, но читаемый на
     бумаге: прежний #E05D5D светился и давал 3.14 — ниже порога для мелкого
     текста. Ночная тема возвращает прежний тон (на тёмном он верен). */
  --live: #B23A2E;
  --success: #3F7D57;
  --success-soft: rgba(63, 125, 87, 0.16);
  --border: #DED7C9;
  --font-serif: -apple-system, BlinkMacSystemFont, "SF Pro Text", "SF Pro Display", "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  --font-ui: -apple-system, BlinkMacSystemFont, "SF Pro Text", "SF Pro Display", "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  /* Грамматическая подсветка: цвет ТЕКСТА по группам (см. блок внизу файла).
     База светлая — цвета тёмные (те же, что у «Пергамента»). */
  --g-verb: #2e7d44;
  --g-noun: #2b5fa8;
  --g-toinf: #a26a1c;
  /* Палитра вспышек по частям речи — мигает ТОЛЬКО при .translating-active.
     Глагол/сущ/артикуль совпадают с цветами групп (--g-*), остальные —
     утверждённая палитра других частей речи (см. docs/construction-flash-summary.md). */
  --flash-verb: var(--g-verb);      /* зелёный  — глагол */
  --flash-noun: var(--g-noun);      /* синий    — существительное, артикль */
  --flash-pron: #7a4fb0;            /* лиловый  — местоимение */
  --flash-adj: #b04a3a;             /* терракотовый — прилагательное */
  --flash-adv: #2a8a84;             /* бирюзовый — наречие */
  --flash-prep: #a8487a;            /* розовый  — предлог */
  --flash-conj: #4a6a9a;            /* серо-голубой — союз */
  --flash-num: #6a8a2e;             /* оливковый — числительное */
  --flash-intj: #a07a1e;            /* янтарный  — междометие */
  /* ЗОЛОТАЯ ШКАЛА РАЗМЕРОВ (владелец 29.09.2026: «шрифт перевода и шрифт разбора
     надо увеличить, используй правило золотого сечения по возможности»).

     Отсчёт — основной текст приложения, 14px. Каждая ступень шкалы получается
     умножением на золотое сечение φ = 1.618…; чтобы ступени не прыгали слишком
     крупно, шаг делим пополам — множитель √φ = 1.272 (это ровно «полшага»
     золотой пропорции):

       14 × √φ ≈ 17,8  → 18px   перевод под тапом (--fs-golden)
       14 × φ^(1/4) ≈ 15,8 → 16px  разбор: крупнее основного текста, но тише перевода
       14 ÷ √φ ≈ 11px   служебные подписи (время, подсказки) — уже таковы

     Так размеры связаны одним правилом, а не подобраны на глаз: следующий шаг
     вверх от 18 по той же лестнице — 23px, и он оставлен заголовкам экранов. */
  --fs-base: 14px;      /* основной текст: реплики, абзацы урока */
  --fs-half: 16px;      /* 14 × φ^(1/4): разбор слов и перевод целой реплики */
  --fs-golden: 18px;    /* 14 × √φ: перевод под тапом — то, ради чего тапнули */
  --safe-bottom: env(safe-area-inset-bottom, 0px);
  --safe-top: env(safe-area-inset-top, 0px);
  --header-h: 48px;
  --bottombar-h: 60px;
  /* Запас внизу прокручиваемых экранов.
     S1 (17.09): плавающая кнопка городской ленты (.city-chat-fab) удалена —
     вход теперь третий таб нижнего ряда. Большой запас (96px) больше не нужен;
     оставлен небольшой, чтобы временные плавающие элементы (toast, статус
     перевода) не перекрывали последнюю строку контента.
     История: до фазы 8.12 над FAB ленты стопкой стоял FAB Марты — было 168px. */

  --fab-stack-clearance: calc(24px + var(--safe-bottom));
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
  /* Фон «стола» вокруг телефона. InStory 4 — только телефон: рамки телефона на
     широком экране больше нет (тёмного «стола» и бордюров не держим), вокруг
     та же тёплая бумага, что и внутри. */
  background: var(--bg-sheet);
  color: var(--text);
  font-family: var(--font-ui);
  height: 100%;
  overflow: hidden;
}

/* ===== ФОРМАТ МОБИЛЬНОГО ТЕЛЕФОНА =====
   #app — всегда колонка шириной до 430px, по центру. На десктопе это
   выглядит как экран телефона; все фиксированные оверлеи позиционируются
   ОТНОСИТЕЛЬНО #app (position:absolute), поэтому не вылезают за рамку. */
#app {
  position: relative;
  height: 100vh;
  height: 100dvh;
  max-width: 430px;
  margin: 0 auto;
  background: var(--bg);
  display: flex;
  flex-direction: column;
  overflow: hidden;
}
/* InStory 4 — только телефон. Широкий экран не становится десктопной
   версией: колонка остаётся телефоном, но без «стола» и рамки вокруг. */
@media (min-width: 768px) {
  #app {
    box-shadow: 0 0 0 1px var(--border);
  }
}

/* ===== HEADER ===== */
#header {
  /* П.41-дожим (владелец: «оно читается неполностью в шапке»): шапка
     ПОДРАСТАЕТ по высоте, если название книги переносится на вторую строку;
     на одной-двух строках высота остаётся штатной (--header-h).
     InStory 4: шапка компактнее и тише — бумажный фон, тонкий волосок снизу,
     никаких крупных плоскостей. Боковые поля держат вырез экрана (safe-area). */
  min-height: var(--header-h);
  padding-top: var(--safe-top);
  padding-left: calc(12px + env(safe-area-inset-left, 0px));
  padding-right: calc(12px + env(safe-area-inset-right, 0px));
  display: flex;
  align-items: center;
  gap: 8px;
  border-bottom: 1px solid var(--border);
  background: var(--bg-elevated);
  flex-shrink: 0;
  z-index: 10;
}

#header-title {
  flex: 1;
  /* П.41-дожим: название произведения читается ЦЕЛИКОМ — как и подпись главы
     (#reader-chapter) оно переносится на вторую строку; многоточие и обрезание
     запрещены владельцем («что НЕ подходит: многоточие, обрезание ...»).
     InStory 4: название — как шапка издания, засечным шрифтом в цвете леса. */
  font-family: var(--font-serif);
  font-size: 15.5px;
  line-height: 1.25;
  font-weight: 600;
  font-style: italic;
  letter-spacing: 0.01em;
  color: var(--text);
  text-align: center;
  white-space: normal;
  overflow-wrap: break-word;
}

.nav-btn {
  background: none;
  border: none;
  color: var(--text);
  font-size: 18px;
  padding: 6px 10px;
  min-width: 44px;
  min-height: 44px;
  cursor: pointer;
  border-radius: 8px;
}
.nav-btn:active { background: var(--accent-soft); }

/* Меню библиотеки: видимая подпись словом (тултип на телефоне не виден).
   Тап-таргет остаётся ≥44px по высоте. */
.nav-btn-labeled {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-width: 0;
  padding: 6px 8px;
}
.nav-btn-labeled .nav-btn-label {
  font-size: 13px;
  font-weight: 600;
  white-space: nowrap;
}
@media (max-width: 380px) {
  .nav-btn-labeled .nav-btn-label { font-size: 12px; }
}

/* =====================================================================
   UI-KIT: базовые компоненты (.mh-*)
   Реестр и правила применения — в docs/UI_KIT.md.
   Новые модули собираются ТОЛЬКО из этих классов; модульные классы
   допустимы только для размеров/отступов, цвета и фон — отсюда.
   ===================================================================== */

/* Поле ввода. Тап-таргет 44px, пилюля. */
.mh-input {
  flex: 1;
  height: 44px;
  background: var(--bg-sheet);
  border: 1px solid var(--border);
  border-radius: 20px;
  padding: 10px 16px;
  color: var(--text);
  font-size: 15px;
  outline: none;
}

/* Ряд «поле ввода + кнопка отправки» */
.mh-input-row {
  display: flex;
  gap: 8px;
  padding: 10px 12px;
  border-top: 1px solid var(--border);
  flex-shrink: 0;
}
/* Вариант у нижнего края окна: фон + отступ под safe-area */
.mh-input-row--bottom {
  background: var(--bg);
  padding-bottom: calc(10px + var(--safe-bottom));
  z-index: 2;
}
.mh-input-row.hidden { display: none; }

/* Кнопка-иконка (стрелка отправки). Тап-таргет 44px. */
.mh-btn-icon {
  background: none;
  border: none;
  color: var(--text);
  font-size: 18px;
  padding: 6px 10px;
  min-width: 44px;
  min-height: 44px;
  cursor: pointer;
  border-radius: 8px;
}
.mh-btn-icon:active { background: var(--accent-soft); }

/* Акцентная кнопка — основа. Размеры задают модификаторы. */
.mh-btn-primary {
  background: var(--accent);
  border: none;
  color: var(--on-accent);
  font-weight: 600;
  cursor: pointer;
}
.mh-btn-primary:active { opacity: 0.85; }
/* Пилюля в строке (кнопка «← Меню» в прогресс-баре урока) */
.mh-btn-primary--pill {
  flex-shrink: 0;
  height: 38px;
  padding: 0 14px;
  border-radius: 19px;
  font-size: 14px;
  font-weight: 700;
  white-space: nowrap;
}
/* Полноширинная кнопка действия («Дальше →») */
.mh-btn-primary--block {
  display: block;
  width: 100%;
  padding: 13px;
  border-radius: 22px;
  font-size: 15px;
  margin-top: 4px;
}
.mh-btn-primary.hidden { display: none; }

/* Закреплённая сверху панель (прогресс-бар урока) */
.mh-sticky-top {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 14px;
  background: var(--bg);
  border-bottom: 1px solid var(--border);
  flex-shrink: 0;
  z-index: 2;
}

/* Карточка контента */
.mh-card {
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 14px 16px;
  margin-bottom: 12px;
}

/* Пузыри чата с Мартой */
.mh-bubble {
  max-width: 80%;
  padding: 10px 14px;
  border-radius: 14px;
  font-size: 14px;
  line-height: 1.45;
  white-space: pre-line;
}
.mh-bubble-tutor {
  align-self: flex-start;
  background: var(--bg-sheet);
  border: 1px solid var(--border);
  border-radius: 14px 14px 14px 4px;
}
.mh-bubble-user {
  align-self: flex-end;
  background: var(--accent-soft);
  border-radius: 14px 14px 4px 14px;
}
/* Реплика с подсветкой из сценария онбординга (highlight) */
.mh-bubble-hl {
  border-color: color-mix(in srgb, var(--amber) 55%, transparent) !important;
  background: var(--amber-soft) !important;
}
.mh-bubble-typing {
  font-style: italic;
  color: var(--text-dim);
}

/* ===== КОНЕЦ UI-KIT ===== */

#btn-audio {
  background: var(--accent-soft);
  border: 1px solid var(--accent);
  color: var(--accent);
  font-weight: 600;
  border-radius: 999px;
  padding: 6px 14px;
}

/* ===== SCREENS ===== */
.screen {
  display: none;
  flex: 1;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
}
.screen.active { display: block; animation: screen-fade 0.15s ease; }
@keyframes screen-fade { from { opacity: 0; } to { opacity: 1; } }

.screen-title {
  padding: 20px 22px 4px;
  font-family: var(--font-serif);
  font-size: 26px;
  line-height: 1.1;
  font-weight: 600;
  letter-spacing: -0.01em;
  color: var(--text);
}

/* Пояснение под заголовком экрана: что это за список и что с ним делать. */
.screen-sub {
  padding: 2px 22px 14px;
  font-size: 13.5px;
  line-height: 1.5;
  color: var(--text-dim);
}

/* ===== TOC ===== */
#toc-list { padding: 8px 12px; }
.continue-btn {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 3px;
  width: calc(100% - 32px);
  margin: 6px 16px 10px;
  padding: 16px 18px;
  background: var(--bg-elevated);
  border: 0;
  border-radius: 18px;
  box-shadow: 0 1px 2px rgba(34, 31, 26, 0.05), 0 8px 24px rgba(34, 31, 26, 0.04);
  color: var(--text);
  cursor: pointer;
  text-align: left;
}
.continue-btn-label { font-family: var(--font-serif); font-size: 17px; font-weight: 600; color: var(--text); }
.continue-btn-chapter { font-size: 13px; color: var(--text-dim); }
.continue-btn:active { background: var(--accent); }
.continue-btn:active .continue-btn-label,
.continue-btn:active .continue-btn-chapter { color: var(--on-accent); }
.toc-item {
  margin: 0 16px;
  padding: 15px 4px;
  border-bottom: 1px solid var(--border);
  cursor: pointer;
  display: flex;
  justify-content: space-between;
  align-items: center;
}
.toc-item-title { font-family: var(--font-serif); font-size: 16.5px; }
.toc-item-meta { font-size: 12px; color: var(--text-dim); }

/* Книжная полка: секция произведения (видна только когда книг > 1) */
.book-head {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 18px 12px 8px;
  margin-top: 10px;
  border-top: 1px solid var(--border);
}
.book-head:first-child { margin-top: 0; border-top: none; }
.book-spine {
  width: 4px;
  height: 30px;
  border-radius: 2px;
  flex: 0 0 auto;
}
.book-head-text { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.book-head-title {
  font-family: var(--font-serif);
  font-size: 18px;
  font-weight: 600;
  color: var(--text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.book-head-sub { font-size: 12px; color: var(--text-dim); }
/* Аннотация произведения в библиотеке: 1–3 предложения по-русски. */
.book-head-annotation {
  font-size: 13px;
  line-height: 1.45;
  color: var(--text-dim);
  margin-top: 2px;
  white-space: normal;
}

/* ===== Библиотека двух уровней (п.31) =====
   Уровень 1: карточка произведения — маленькая обложка (первая иллюстрация
   эпизодов книги) + название + аннотация. Уровень 2: содержание произведения
   (свои главы с нумерацией 1..N) и возврат «← К списку произведений». */
.lib-book {
  gap: 12px;
  align-items: flex-start;
}
.lib-book-cover {
  width: 52px;
  height: 70px;
  flex: 0 0 auto;
  border-radius: 6px;
  overflow: hidden;
}
.lib-book-cover-img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.lib-book-info {
  display: flex;
  flex-direction: column;
  gap: 3px;
  min-width: 0;
  flex: 1;
}
.lib-book-head {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  padding: 10px 12px 14px;
  border-bottom: 1px solid var(--border);
}
/* П.Б (владелец, 26.09): «в меню глав давай тоже добавим фото и аннотации» —
   фото произведения в шапке содержания крупнее, чем в карточке списка. */
.lib-book-head .lib-book-cover {
  width: 64px;
  height: 88px;
}
.lib-back {
  display: block;
  width: 100%;
  padding: 12px;
  margin-bottom: 4px;
  background: none;
  border: none;
  border-bottom: 1px solid var(--border);
  color: var(--accent);
  font-size: 14px;
  text-align: left;
  cursor: pointer;
}
.lib-back:active { color: var(--text); }

/* ===== READER ===== */
#reader-location {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 2px;
  padding: 22px calc(24px + env(safe-area-inset-right, 0px)) 6px calc(24px + env(safe-area-inset-left, 0px));
  border-bottom: 0;
}
.reader-kicker {
  font-size: 11px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--amber);
  font-weight: 600;
}
.reader-chapter {
  font-family: var(--font-serif);
  font-size: 28px;
  line-height: 1.15;
  font-weight: 600;
  letter-spacing: -0.015em;
  color: var(--text);
}
#reader-text-wrap {
  position: relative;
  padding: 8px calc(24px + env(safe-area-inset-right, 0px)) var(--fab-stack-clearance) calc(24px + env(safe-area-inset-left, 0px));
}
.reader-text {
  font-family: 'Iowan Old Style', 'Palatino Linotype', Georgia, 'Times New Roman', serif;
  font-size: 18px;
  line-height: 1.75;
  color: var(--text);
  max-width: 680px;
  margin: 0 auto;
}
.reader-text p {
  margin: 0 0 1.1em;
}
.reader-word {
  cursor: pointer;
  border-radius: 3px;
  transition: background 0.15s;
  display: inline-block;
  min-width: 1ch;
  text-align: center;
  color: inherit;
  -webkit-user-select: none;
  user-select: none;
  -webkit-touch-callout: none;
  touch-action: manipulation;
}
/* Изученное слово больше не выделяется цветом — единственный маркер слова
   в тексте это мигание перевода (см. .translating-active). Класс .learned
   остаётся в DOM для логики, но визуальных изменений не даёт. */
.reader-word.learned {
  color: inherit;
}

/* Обёртка слова с фиксированной шириной — гарантирует сдвиг текста */
.reader-word-wrapper {
  display: inline-block;
  vertical-align: top;
  margin: 0 1px;
}

/* ===== СЦЕНЫ-«ПОСТЫ» (ФАЗА 2) =====
   Каждый эпизод главы — отдельная рамка, как пост в мессенджере: сверху
   иллюстрация сцены, ниже — только текст (абзацы). Подписей и описаний
   сцены нет (слово владельца). На телефоне — карточка: скруглённый фон,
   воздух между «постами», читаемая типографика главы без изменений. */
.scene-post {
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: 18px;
  overflow: hidden;
  margin: 0 0 18px;
}
/* Номер эпизода в главе (владелец 08.10.2026: «в главах надо пронумеровать
   эпизоды»). Только номер — место и время сцены не пишем (прежнее слово
   владельца: «подписей и описаний сцены нет»). */
.scene-episode {
  padding: 10px 18px 0;
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: lowercase;
  color: var(--text-dim);
}
.scene-post:last-child {
  margin-bottom: 4px;
}
.scene-body {
  padding: 16px 18px 8px;
}
.scene-body p {
  margin: 0 0 1.1em;
}
.scene-body p:last-child {
  margin-bottom: 0.35em;
}
/* Иллюстрация сцены — «фото» поста сверху: без подписи, без рамки;
   углы рамки поста уже скруглены (overflow:hidden у .scene-post). */
/* D100 (владелец 05.10): реплика с аватаркой начинается с НОВОЙ строки —
   аватарка никогда не оказывается посреди текста. */
.reader-turn-has-avatar {
  display: block;
  margin: 8px 0 2px;
}
.reader-turn-has-avatar .turn-avatar {
  vertical-align: -4px;
  margin-right: 6px;
}

.scene-img-wrap {
  width: 100%;
  line-height: 0;
}
.scene-img {
  display: block;
  width: 100%;
  height: clamp(180px, 46vw, 300px);
  object-fit: cover;
  background: var(--bg);
}

/* ===== ФАЗА 3: тап-перевод ПРЕДЛОЖЕНИЕМ (движок чата в «Истории») =====
   Единица тапа — предложение (span.reader-sentence). Подсветка — мягкий фон
   предложения БЕЗ рамки + ПОСТОЯННЫЕ цвета частей речи: класс .chat-pos-on
   на предложении открывает те же правила палитры, что в чате (10 частей
   речи, цвет = часть речи), а mh-g-* / mh-flash-<pos> ставят grammar.js и
   reader_tap.js. Мигания и рамок вокруг слов НЕТ — правило владельца из
   чата (2866dca) действует и здесь. */
.reader-sentence { cursor: pointer; }
.reader-sentence.chat-sentence-active {
  background: var(--accent-soft);
  border-radius: 3px;
}

/* ===== Оформление диалогов (владелец 03.10: «кроме 4 пункта всё делай») =====
   1. Тап = реплика: span.reader-turn — ход говорящего целиком.
   2. Мысли курсивом: span.reader-thought.
   3. Голос радиостанции (обломки эфира) — особым стилем: span.reader-voice.
   5. Аватарки: .turn-avatar у реплики, .scene-who — «кто в сцене». */
.reader-turn { cursor: pointer; }
/* Речь героя жирнее текста автора (владелец 03.10: «помечать более жирным
   текстом речь героя»). 600 — заметно жирнее обычного 400; атрибуция-вставка
   автора внутри реплики остаётся обычным начертанием. */
.reader-turn.reader-speech {
  font-weight: 600;
}
/* Обложки радио в строках списков убраны (владелец 08.10.2026: «в списке картинки
   нифига не видно, смысла в них нет»). Обложка живёт только крупным кадром того,
   что играет сейчас (~96–220 px) и миниатюрой в баре плеера (34 px). */

/* Крупный кадр того, что играет сейчас (владелец 08.10.2026): «сделать крупной
   картинку исполняемой песни, чтобы её можно было рассмотреть». Кадр стоит под
   строкой «что в эфире», квадратом, и занимает столько места, сколько даёт
   экран, — список эфира считает остаток сам (broadcastRowCounts). */
.broadcast-cover {
  display: flex;
  align-items: center;
  gap: 12px;
  margin: 10px 0 12px;
}
/* Гашение по соглашению проекта: у блока свой display, поэтому .hidden
   прописывается вместе с ним (иначе flex перебивает общее правило). */
.broadcast-cover.hidden { display: none; }
.broadcast-cover-btn {
  padding: 0;
  border: 0;
  background: none;
  cursor: zoom-in;
  border-radius: 14px;
  flex: 0 0 auto;
  /* Размер кадра: не шире 40% ширины и не выше 22% высоты экрана (под телефон и
     под десктоп), в пределах 96–220 px — список эфира остаётся видимым. */
  width: clamp(96px, min(40vw, 22vh), 220px);
  aspect-ratio: 1 / 1;
  overflow: hidden;
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.28);
}
.broadcast-cover-img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.broadcast-cover-meta {
  min-width: 0;
}
.broadcast-cover-title {
  font-size: 1.05em;
  font-weight: 600;
  line-height: 1.25;
}
.broadcast-cover-kind {
  margin-top: 4px;
  font-size: 0.82em;
  opacity: 0.7;
}

/* «Рассмотреть картинку»: тот же кадр во весь экран. Тёмная подложка, кадр
   вписан целиком (contain — ничего не обрезаем), закрытие тапом/Esc/крестиком. */
.art-full {
  position: fixed;
  inset: 0;
  z-index: 4000;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
  background: rgba(0, 0, 0, 0.92);
  cursor: zoom-out;
}
.art-full.hidden { display: none; }

/* Миниатюра эфира в баре плеера (владелец 08.10.2026: «вверху в плеере не вижу»).
   Тот же кадр, что и на экране радио, — бар появляется и на других экранах. */
.broadcast-bar-art {
  width: 34px;
  height: 34px;
  border-radius: 8px;
  object-fit: cover;
  flex: 0 0 auto;
  margin-right: 8px;
}
.broadcast-bar-art.hidden { display: none; }
.art-full-img {
  max-width: 100%;
  max-height: 100%;
  border-radius: 16px;
  object-fit: contain;
}
.art-full-caption {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 18px;
  padding: 0 56px;
  text-align: center;
  font-size: 0.95em;
  color: #fff;
  opacity: 0.85;
}
.art-full-close {
  position: absolute;
  top: 14px;
  right: 14px;
  width: 40px;
  height: 40px;
  border: 0;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.14);
  color: #fff;
  font-size: 1.1em;
  cursor: pointer;
}

/* Жирность по кускам реплики: речь героя — 600, атрибуция автора
   («Nora said») и повествование — 400. */
.reader-say-speech {
  font-weight: 600;
}
.reader-say-attr,
.reader-say-narr {
  font-weight: 400;
}
.reader-turn.chat-sentence-active {
  background: var(--accent-soft);
  border-radius: 4px;
}
.reader-thought {
  font-style: italic;
  opacity: 0.92;
}
.reader-voice {
  display: block;
  margin: 0.55em 0;
  padding-left: 0.85em;
  /* Фаза 2: было белое-на-белом (rgba(255,255,255,...)) — на светлой бумаге
     голос героя пропадал. Теперь цвет темы. */
  border-left: 2px solid var(--border);
  font-style: italic;
  color: var(--text-dim);
  letter-spacing: 0.015em;
}
.turn-avatar {
  width: 22px;
  height: 22px;
  border-radius: 50%;
  object-fit: cover;
  vertical-align: -4px;
  margin-right: 6px;
  opacity: 0.95;
}
.scene-who {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  padding: 8px 12px 0;
}
.who-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 2px 10px 2px 3px;
  border-radius: 999px;
  background: var(--bg-sheet);
  font-size: 12px;
  color: var(--text);
}
.who-avatar {
  width: 22px;
  height: 22px;
  border-radius: 50%;
  object-fit: cover;
}
/* Плашка перевода предложения — после абзаца (одна на страницу), как note
   под баблом в чате: строка «перевод: …» и кнопки «🔊 Озвучить» / «Разобрать»
   рядом. Оформление кнопок и панели разбора — общие .chat-speak-* /
   .chat-breakdown-* (один и тот же движок — один и тот же вид).

   Размер — золотая ступень (--fs-golden), та же, что у перевода под тапом в
   чате: владелец 29.09.2026 просил увеличить перевод «везде — в истории, в
   радио, в чате», а здесь было 13px — МЕНЬШЕ, чем даже в чате. Отступы
   переведены из em в px: при 18px em-отступы раздулись бы вдвое и плашка
   отъехала бы от абзаца, к которому относится. */
.reader-sent-note {
  font-size: var(--fs-golden);
  line-height: 1.45;
  color: var(--text-dim);
  margin: -8px 0 17px;
  padding: 8px 12px;
  background: var(--bg-sheet);
  border-radius: 12px;
}
.reader-sent-note-text { font-style: italic; }

/* ===== BOTTOM BAR — нижний ряд S1: История / Радио / Чат / Прогресс =====
   InStory 4, фаза 1: ряд — «полка издания». Бумажный фон, тонкий волосок
   сверху, боковые поля под вырез экрана. Активная вкладка — цветом леса и
   янтарной меткой над значком (та же метка, что у станции в эфире: янтарь —
   только точка внимания, не плоскость). Тап-таргет каждой вкладки не ниже
   48px: палец попадает без прицела. */
#bottom-bar {
  min-height: var(--bottombar-h);
  padding-bottom: var(--safe-bottom);
  padding-left: env(safe-area-inset-left, 0px);
  padding-right: env(safe-area-inset-right, 0px);
  display: flex;
  align-items: stretch;
  border-top: 1px solid var(--border);
  background: var(--bg-elevated);
  flex-shrink: 0;
  z-index: 10;
}
.tab-btn {
  position: relative;
  flex: 1;
  min-height: 48px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 3px;
  padding: 6px 4px;
  border: none;
  background: transparent;
  color: var(--text-dim);
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition: color 0.15s ease, background 0.15s ease;
}
.tab-btn:active { background: var(--accent-soft); }
.tab-btn .tab-icon { font-size: 18px; line-height: 1; opacity: 0.85; }
.tab-btn .tab-label { font-size: 11px; line-height: 1.2; letter-spacing: 0.02em; }
/* 360px: 4 таба без горизонтального скролла — подписи ужимаются, не обрезаются */
@media (max-width: 380px) {
  #bottom-bar { gap: 0; }
  .tab-btn { min-width: 0; padding: 6px 2px; }
  .tab-btn .tab-label { font-size: 10px; letter-spacing: -0.01em; }
}
/* Метка активной вкладки: янтарный волосок над значком. */
.tab-btn::before {
  content: "";
  position: absolute;
  top: 0;
  left: 50%;
  transform: translateX(-50%) scaleX(0);
  width: 26px;
  height: 3px;
  border-radius: 0 0 3px 3px;
  background: var(--amber);
  transition: transform 0.18s ease;
}
.tab-btn.active { color: var(--text); }
.tab-btn.active .tab-icon { opacity: 1; }
.tab-btn.active::before { transform: translateX(-50%) scaleX(1); }
.tab-btn.active .tab-label { font-weight: 600; }

/* Полосы быстрого входа (§38, «Мой прогресс» / «Эфир Санта-Марины»)
   больше нет: владелец 28.09.2026 убрал обе кнопки как дубль нижнего ряда
   вкладок. Стилей полосы и гашения полосы стеной больше не держим. */
/* П.41 (владелец, 26.09): строка «Глава N из M» (#progress-info) убрана
   совсем; под шапкой теперь только название главы — и оно должно читаться
   целиком: переносится на вторую строку, без nowrap и многоточия. */
#reader-chapter {
  display: block;
  /* InStory 4: «Глава N — Название» как подзаголовок издания — засечным
     шрифтом, чуть крупнее служебного текста, читается целиком. */
  font-family: var(--font-serif);
  font-size: 15px;
  line-height: 1.45;
  color: var(--text);
  overflow-wrap: break-word;
}

/* ===== АУДИО-БАРЫ (было: audio intensives + broadcast) =====
   Карточки-эпизоды и кнопки «Слушать» удалены из читалки 2026-09-10;
   стили .audio-bar* остались — их использует broadcast-bar (MH Radio):
   базовый layout (.audio-bar), .audio-bar-info, .audio-bar-btn. */
.audio-bar-btn {
  flex-shrink: 0;
  width: 36px;
  height: 36px;
  border-radius: 50%;
  border: 1px solid var(--border);
  background: var(--bg-sheet);
  color: var(--accent);
  font-size: 14px;
  cursor: pointer;
}
.audio-bar-btn:active { transform: scale(0.92); }

/* ===== ГЛОБАЛЬНЫЕ АУДИО-БАРЫ =====
   Живут в потоке #app сразу под хедером (вперёд экранов), поэтому ничего не
   перекрывают: экраны просто сдвигаются вниз. Наверху они потому, что внизу
   стояли бы поверх панели ввода городского чата. */
.audio-bar {
  position: relative;
  flex-shrink: 0;
  z-index: 40;
  margin: 8px 8px 0;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 12px;
  background: var(--bg-sheet);
  border: 1px solid var(--border);
  border-radius: 14px;
  box-shadow: 0 4px 16px rgba(0,0,0,0.4);
  z-index: 30;
}
.audio-bar.hidden { display: none; }
.audio-bar-info {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 5px;
}
.audio-bar-info > span {
  font-size: 12px;
  color: var(--text);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* Бар управляется на весу — кнопка чуть крупнее базовой. */
.audio-bar-btn { width: 40px; height: 40px; font-size: 17px; }

/* ===== РАДИО-СТРАНИЦА ===== */
#audio-chapters { padding: 4px 12px var(--fab-stack-clearance); }
.audio-play-all {
  display: block;
  width: 100%;
  margin: 4px 0 14px;
  padding: 14px 16px;
  background: var(--accent-soft);
  border: 1px solid var(--accent);
  border-radius: 12px;
  color: var(--text);
  font-size: 14px;
  font-weight: 600;
  cursor: pointer;
}
.audio-play-all:active { transform: scale(0.98); }
.radio-chapter { margin-bottom: 18px; }
.radio-chapter-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  margin-bottom: 6px;
}
.radio-chapter-title {
  font-size: 13px;
  font-weight: 600;
  color: var(--text-dim);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.radio-play-chapter {
  flex-shrink: 0;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--bg-elevated);
  color: var(--accent);
  font-size: 12px;
  padding: 6px 10px;
  min-height: 36px;
  cursor: pointer;
}

/* ===== ОЗВУЧКА СЛОВА (M6.1) =====
   Длинный тап (~450 мс) произносит слово; на время звука слово «дышит» —
   мягкая подложка в такт, без смены цвета текста (цвет текста меняется
   только у мигания перевода, это единственный смысловой маркер).
   Класс .mh-speaking ставит/снимает JS (setSpeaking в app.js). */
.mh-speaking {
  background: var(--accent-soft);
  border-radius: 3px;
  animation: mh-speaking-breathe 1.1s ease-in-out infinite;
}
@keyframes mh-speaking-breathe {
  0%, 100% { background: var(--accent-soft); }
  50%      { background: color-mix(in srgb, var(--accent) 34%, transparent); }
}
@media (prefers-reduced-motion: reduce) {
  .mh-speaking { animation: none; }
}

/* ===== Перевод: мигание слова RU↔EN =====
   Реализация мигания — в JS (global translation clock): текст слова меняем
   через setTranslationSide(), а само мигающее слово помечаем классом
   .translating-active. Цвет текста при этом НЕ меняется — меняем только
   текст и тонкую жёлтую рамку. */

/* Класс для "текущего" слова при переводе — жёлтая рамка.
   Горизонтальные отступы дают воздух внутри рамки: и RU-, и EN-вариант
   текста не прилегают к ней вплотную и не сливаются с соседними словами.
   Класс навешивается ДО замера ширины в toggleTranslation(), поэтому
   padding автоматически учтён в замороженном minWidth — текст вокруг
   не дёргается при смене стороны. */
.reader-word.translating-active {
  box-shadow: 0 0 0 2px #f59e0b;
  padding-left: 4px;
  padding-right: 4px;
}

/* Плавный кроссфейд при смене языка (Шаг 4). Длительность задаётся из JS
   переменной --tr-fade; при prefers-reduced-motion JS отключает анимацию. */
.reader-word.translating-active {
  transition: opacity var(--tr-fade, 0.2s) ease;
}
@media (prefers-reduced-motion: reduce) {
  .reader-word.translating-active { transition: none; }
}

/* Статус-бар перевода внизу экрана */
#translation-status {
  position: absolute;
  bottom: calc(var(--bottombar-h) + 8px);
  left: 50%;
  transform: translateX(-50%);
  background: var(--bg-sheet);
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 8px 18px;
  font-size: 16px;
  font-weight: 600;
  color: var(--accent);
  box-shadow: 0 4px 16px rgba(0,0,0,0.4);
  z-index: 35;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.2s;
  text-align: center;
  max-width: 80vw;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
#translation-status.visible {
  opacity: 1;
}

/* ===== Бейдж непрочитанного на табе «Чат» =====
   Вход в чат с Кейт — третий таб нижнего ряда (data-tab="city": имя таба
   сохранено ради реестра MH). Бейдж #chat-badge заполняет chat.js: текст =
   число непрочитанного, класс hidden снимается/ставится там же. */
.tab-icon-wrap {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.tab-badge {
  position: absolute;
  top: -5px;
  right: -8px;
  min-width: 16px;
  height: 16px;
  padding: 0 4px;
  border-radius: 8px;
  background: var(--accent);
  color: var(--on-accent);
  font-size: 10px;
  font-weight: 700;
  line-height: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 2px solid var(--bg);
  box-sizing: content-box;
  pointer-events: none;
}
.tab-badge.hidden { display: none; }

/* Вход в «Занятия» — в шапке городского чата (S1, 17.09: перенесён с Полки).
   Шапка не flex-контейнер, кнопки текут inline; отступ слева 8px задаёт зазор
   от края, справа 8px — от соседней «Правила» (у неё margin-left:0). */
.chat-lessons-toggle {
  flex-shrink: 0;
  align-self: center;
  margin: 0 4px 0 8px;
  padding: 7px 13px;
  font-size: 12.5px;
  font-weight: 600;
  color: var(--accent);
  background: transparent;
  border: 1px solid var(--border);
  border-radius: 999px;
  cursor: pointer;
}
.chat-lessons-toggle:active { background: var(--accent); color: var(--on-accent); }

/* ===== OVERLAY (settings) ===== */
.overlay {
  position: absolute;
  inset: 0;
  z-index: 200;
  display: flex;
  align-items: center;
  justify-content: center;
}
.overlay.hidden {
  display: none;
}
.overlay-backdrop {
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0.48);
  backdrop-filter: blur(3px);
}
.settings-panel {
  position: relative;
  background: var(--bg-elevated);
  border: 0;
  border-radius: 22px;
  padding: 26px 22px;
  max-width: 340px;
  width: 90%;
  box-shadow: 0 24px 60px rgba(0, 0, 0, 0.28);
}
.settings-title {
  margin: 0 0 18px;
  font-size: 19px;
  font-weight: 700;
  letter-spacing: -0.01em;
  color: var(--text);
  text-align: center;
}
/* Настройки (08.10.2026, D196): нормальный список — группы, короткие подписи,
   строка «подпись + выбор». Простыня «Правила чата» из настроек убрана. */
.settings-group-title {
  margin: 18px 0 10px;
  font-size: 11px;
  line-height: 13px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-dim);
}
.settings-panel > .settings-group-title:first-of-type { margin-top: 2px; }
.setting-row {
  margin-bottom: 16px;
}
.setting-row-line {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 10px;
}
.setting-label {
  display: block;
  font-size: 14px;
  font-weight: 600;
  color: var(--text);
  margin-bottom: 8px;
}
.setting-row-line .setting-label { margin: 0; flex: 0 1 auto; }
.settings-range {
  width: 100%;
  height: 6px;
  -webkit-appearance: none;
  appearance: none;
  background: var(--border);
  border-radius: 3px;
  outline: none;
}
.settings-range::-webkit-slider-thumb {
  -webkit-appearance: none;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  background: var(--accent);
  cursor: pointer;
}
.settings-select {
  width: 100%;
  padding: 11px 14px;
  min-height: 44px;
  background: var(--bg-sheet);
  border: 0;
  border-radius: 12px;
  color: var(--text);
  font-size: 15px;
  outline: none;
}
.settings-select:focus { box-shadow: 0 0 0 1.5px var(--accent); }
.setting-row-line .settings-select {
  width: auto;
  min-width: 138px;
  flex: 0 0 auto;
  padding: 9px 12px;
  min-height: 40px;
}
.setting-row-line .chat-feedback-toggle {
  min-height: 40px;
  padding: 9px 14px;
}
.settings-save-btn {
  width: 100%;
  padding: 13px;
  margin-top: 10px;
  background: var(--accent);
  color: var(--on-accent);
  font-size: 16px;
  font-weight: 600;
  border: none;
  border-radius: 22px;
  cursor: pointer;
}
.settings-save-btn:active {
  opacity: 0.88;
}

/* ===== Экран «Радио» ===== */
/* П.В (владелец, 26.09): «на радио слишком большой список программ и сверху
   и снизу от эфира надо чтобы в режиме без раскрытых аккордеонов всё
   вмещалось в один экран без скролинга». Все высоты ниже заданы явно
   (line-height в px), чтобы расчёт «360×640 = шапка 52 + экран 528 +
   нижний ряд 60» был точным. */
/* Заголовок экрана радио: компактные padding И нулевой отступ h2 (иначе
   сверху и снизу добавляется UA-margin 0.83em ≈ 18 px — по нему экран и не
   влезал в 528 px видимой области). Дизайн 26.09: единый ритм — блоки в
   12 px от края, между блоками 8 px, заголовок — первый уровень иерархии. */
#screen-radio .screen-title {
  margin: 0;
  padding: 0 12px;
  font-size: 12px;
  line-height: 14px;
  font-weight: 600;
}

/* ===== MH RADIO: ЖИВОЙ ЭФИР ===== */
/* Карточка «В ЭФИРЕ» — компактная сетка: слева в две строки «● В ЭФИРЕ ·
   название» и «что играет», справа кнопка. П.42 (владелец, 26.09):
   «кнопки и ссылки ... не должны быть слишком маленькими» — кнопка ≥ 40 px,
   поэтому тексты собраны в левую колонку, а кнопка занимает оба ряда. */
.broadcast-card {
  display: flex;
  flex-direction: column;
  gap: 12px;
  align-items: stretch;
  margin: 4px 16px 0;
  padding: 18px;
  border: 0;
  border-radius: 18px;
  background: var(--bg-elevated);
  box-shadow: 0 1px 2px rgba(34, 31, 26, 0.05), 0 8px 24px rgba(34, 31, 26, 0.04);
}
.broadcast-head {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 7px;
  margin: 0;
  min-width: 0;
}
.broadcast-live {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 4px 10px;
  border-radius: 999px;
  background: color-mix(in srgb, var(--live) 14%, transparent);
  font-size: 10px;
  line-height: 1;
  letter-spacing: 0.09em;
  font-weight: 700;
  color: var(--live);
  text-transform: uppercase;
}
.broadcast-live-dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--live);
}
@keyframes live-pulse {
  0%, 100% { opacity: 1; box-shadow: 0 0 0 0 rgba(224,93,93,0.5); }
  50% { opacity: 0.55; box-shadow: 0 0 0 5px rgba(224,93,93,0); }
}
.broadcast-title {
  margin: 0;
  min-width: 0;
  font-family: var(--font-serif);
  font-size: 21px;
  line-height: 1.15;
  font-weight: 700;
  letter-spacing: -0.01em;
  color: var(--text);
  white-space: normal;
  overflow-wrap: anywhere;
}
.broadcast-now {
  font-size: 14px;
  line-height: 1.45;
  color: var(--text-dim);
  margin: 0;
  min-height: 0;
  white-space: normal;
  overflow-wrap: anywhere;
}
.broadcast-controls {
  display: flex;
  align-items: center;
  gap: 8px;
}

/* ТРЕБОВАНИЕ ВЛАДЕЛЬЦА 26.09: на самом экране радио бар эфира не показывается
   («в самом радио уже есть кнопка вклччения выключения») — поэтому карточка
   «В ЭФИРЕ» НИКОГДА не сворачивается: её кнопка ▶/⏸ и есть орган управления
   эфиром на радио. Экономия высоты — за счёт адаптивного числа рядов списка. */
.broadcast-toggle {
  flex: 1;
  padding: 8px 14px;
  min-height: 42px;
  border: 0;
  border-radius: 21px;
  background: var(--accent);
  color: var(--on-accent);
  font-size: 13px;
  line-height: 1.2;
  font-weight: 600;
  cursor: pointer;
}
.broadcast-toggle.on {
  background: var(--accent);
  color: var(--on-accent);
}
.broadcast-eq {
  display: flex;
  align-items: flex-end;
  gap: 2px;
  height: 18px;
}
.broadcast-eq.hidden { display: none; }
.broadcast-eq span {
  width: 3px;
  border-radius: 2px;
  background: var(--accent);
  animation: eq-bounce 0.9s ease-in-out infinite;
}
.broadcast-eq span:nth-child(1) { animation-delay: 0s; }
.broadcast-eq span:nth-child(2) { animation-delay: 0.25s; }
.broadcast-eq span:nth-child(3) { animation-delay: 0.5s; }
.broadcast-eq span:nth-child(4) { animation-delay: 0.15s; }
@keyframes eq-bounce {
  0%, 100% { height: 4px; }
  50% { height: 16px; }
}
.broadcast-section-title {
  font-size: 10px;
  line-height: 10px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-dim);
  margin: 1px 16px 1px;
}
/* П.42 (владелец, 26.09): «когда нет раскрытых аккордеонов тогда вместе с
   баром должно все влести на одну страницу а сейчас я вижу просто пустоту
   внизу это не правильно». Экран радио — колонка: заголовок, карточка «В
   ЭФИРЕ» и секции фиксированы, список программ забирает ОСТАТОК высоты и
   заполняет его рядами. Число и высоту рядов считает app.js
   (renderBroadcastSchedule): рядов столько, сколько влезает при 32 px
   минимум, высота делит доступное пространство ровно — пустоты внизу нет.
   Раскрытые аккордеоны растят список наружу — тогда экран скроллится,
   это нормально (владелец: «это нормально»). */
#screen-radio.active { display: flex; flex-direction: column; }
#screen-radio > * { flex: 0 0 auto; }
#screen-radio > #broadcast-schedule { flex: 1 0 auto; }
.broadcast-schedule {
  display: flex;
  flex-direction: column;
  /* П.42: зазоры между рядами убраны — высота ряда задаётся в JS так, чтобы
     ряды делили доступное пространство РОВНО (остаток пикселей получают
     первые ряды +1 px). 40 px — минимум тап-зоны, его не уменьшаем.
     Дизайн 26.09: список — в общем ритме 12 px от края, 8 px между блоками. */
  gap: 0;
  margin-top: 8px;
  padding: 0 12px 0;
}
.broadcast-slot {
  display: flex;
  align-items: center;
  gap: 2px;
  /* П.42: вертикального отступа нет — шапка и ссылка «текст …» добирают всю
     высоту строки и не проседают ниже тап-зоны 40 px. */
  padding: 0 8px;
  /* П.42: рамка строки — ring (box-shadow), а не border: border съедал бы
     2 px высоты, и шапка строки не добирала бы до тап-зоны 40 px. */
  border: 0;
  box-shadow: 0 0 0 1px var(--border);
  border-radius: 8px;
  background: var(--bg-sheet);
  font-size: 12px;
  min-height: 40px;
  flex: 0 0 auto;
}
/* Раскрытая строка (текст передачи/песни) растёт сама — высоту из JS
   (которая делит экран ровно) не применяем к ней. */
.broadcast-slot[data-expanded="1"] { height: auto; }
/* П.42: шапка строки и ссылка «текст …» — тап-зона на всю высоту строки
   (≥ 40 px), ссылка дополнительно не уже 44 px. Строка — колонка (см.
   «Таймлайн эфира» ниже), поэтому шапка растёт flex'ом по вертикали.
   В раскрытой строке геометрию не трогаем — там владелец разрешил
   «как получится». */
.broadcast-slot:not([data-expanded="1"]) .broadcast-slot-head { flex: 1 1 auto; }
.broadcast-slot:not([data-expanded="1"]) .broadcast-slot-hint-inline {
  align-self: stretch;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 44px;
  padding: 0 6px;
}
/* Дизайн 26.09: «сейчас» — акцентная полоса слева и чуть более светлый фон,
   без мигания; прошедшие программы визуально тише будущих (у раскрытой
   строки панель текста не тускнеем — читать её должно быть комфортно). */
.broadcast-slot.current {
  box-shadow: 0 0 0 1px var(--border), inset 3px 0 0 var(--accent);
  background: var(--accent-soft);
}
.broadcast-slot--past:not([data-expanded="1"]) { opacity: 0.62; }
/* Дизайн 26.09: типографическая иерархия строки — время вторичным цветом и
   моноширинно (табличные цифры), название основным, метка типа третьим
   уровнем (тихая, без рамки): раньше всё сливалось в одно пятно. */
.broadcast-slot-time {
  color: var(--text-dim);
  font-family: ui-monospace, 'SF Mono', Menlo, Consolas, monospace;
  font-variant-numeric: tabular-nums;
  font-size: 11px;
  line-height: 15px;
  min-width: 38px;
}
.broadcast-slot-kind {
  font-size: 9px;
  line-height: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  padding: 0;
  border: 0;
  color: var(--text-dim);
  opacity: 0.8;
  white-space: nowrap;
}
.broadcast-slot-title {
  color: var(--text);
  font-size: 12px;
  line-height: 15px;
  font-weight: 500;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.broadcast-slot-hint { font-size: 11px; color: var(--text-dim); }

/* ── Таймлайн эфира: прошлое / текущий слот / будущее ─────────────────── */
.broadcast-slot {
  flex-direction: column;
  align-items: stretch;
  gap: 2px;
}
.broadcast-slot-head {
  display: flex;
  align-items: center;
  gap: 6px;
  width: 100%;
}
.broadcast-slot-main {
  display: flex;
  flex-direction: column;
  gap: 1px;
  min-width: 0;
  flex: 1;
}
.broadcast-slot--past { opacity: 0.6; }
.broadcast-slot--future { opacity: 0.75; }
.broadcast-slot[data-kind="podcast"] .broadcast-slot-head { cursor: pointer; }
.broadcast-slot-live {
  font-size: 8px;
  line-height: 10px;
  letter-spacing: 0.5px;
  padding: 0 5px;
  border-radius: 999px;
  border: 1px solid var(--accent);
  color: var(--accent);
  white-space: nowrap;
}
.broadcast-slot-caret {
  font-size: 9px;
  line-height: 12px;
  color: var(--text-dim);
  transition: transform 0.15s ease;
}
.broadcast-slot[data-expanded="1"] { border-color: var(--accent); opacity: 1; }
.broadcast-slot[data-expanded="1"] .broadcast-slot-caret { transform: rotate(180deg); }
.broadcast-slot-hint-inline {
  /* Единый вид подписей «текст передачи» / «текст песни» — и в сетке эфира,
     и в плейлисте: размер, цвет, курсор и подсветка открытой ссылки.
     Дизайн 26.09: читается как ссылка (акцент + подчёркивание), а зона
     нажатия — вся строка ≥ 40×44 px (п.42). */
  font-size: 9px;
  line-height: 12px;
  color: var(--accent);
  text-decoration: underline;
  text-underline-offset: 2px;
  white-space: nowrap;
  cursor: pointer;
  transition: color 0.15s ease;
}
.broadcast-slot-hint-inline:hover { color: var(--text); }
.broadcast-slot-hint-inline.on { color: var(--accent); font-weight: 600; }

/* Текст передачи: пергамент своего цвета, скроллится внутри себя */
/* Текст передачи развёрнут прямо в сетке — у панели нет своего окна прокрутки */
.transcript-open .broadcast-schedule {
  max-height: none;
  overflow: visible;
}

.tscript-panel {
  /* Тот же фон, что у текста в читалке: панель лежит поверх слоя #app,
     у которого background: var(--bg). Явной заливки нет ни у #stage,
     ни у .radio-panel, поэтому текст радио inherits фон страницы 1-в-1
     и в тёмной теме, и в сепии. */
  overflow: visible;
  border: 1px solid var(--border);
  border-radius: 10px;
  /* Дизайн 26.09: читаемый межстрочный интервал панели текста. */
  line-height: 1.55;
  padding: 10px 12px 0;
  color: var(--text);
}
.tscript-panel p { margin: 0 0 8px; }
.tscript-loading { font-size: 12px; color: var(--text-dim); padding: 6px 0 14px; }
.tscript-seg { margin-bottom: 10px; }
.tscript-speaker {
  display: block;
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.6px;
  color: var(--text-dim);
  margin-bottom: 2px;
}
.tscript-text {
  font-size: 14px;
  line-height: 1.62;
  color: var(--text);
}
.tscript-text .reader-word { color: var(--text); }

/* Развёрнутый текст читаем без затемнения */
.broadcast-slot[data-expanded="1"],
.broadcast-slot--past[data-expanded="1"] {
  opacity: 1;
}
.tscript-foot {
  font-size: 10px;
  color: var(--text-dim);
  padding: 4px 0 10px;
}
.tscript-collapse {
  position: sticky;
  bottom: -1px;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  padding: 9px 0 10px;
  font-size: 11px;
  color: var(--text);
  background: linear-gradient(transparent, var(--bg-elevated));
  border-top: 1px dashed var(--border);
  cursor: pointer;
}
.tscript-collapse b { font-size: 10px; }

/* ── Плейлист эфира: все песни станции ─────────────────────────────────── */
/* Аккордеон песен: блок в общем ритме (12 px от края, 8 px между блоками),
   шапка — с понятной стрелкой и тап-зоной ≥ 40 px. */
.radio-playlist { display: flex; flex-direction: column; gap: 2px; margin: 8px 12px 0; }
.playlist-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  width: 100%;
  /* П.42: тап-зона ≥ 40 px. */
  min-height: 40px;
  padding: 8px 8px;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--bg-sheet);
  color: var(--text);
  font-size: 12px;
  font-weight: 500;
  font-size: 12px;
  line-height: 16px;
  cursor: pointer;
}
.playlist-caret { font-size: 11px; color: var(--accent); }
.playlist-track {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 11px 4px;
  border: 0;
  border-bottom: 1px solid var(--border);
  border-radius: 0;
  background: transparent;
  font-size: 14px;
  cursor: pointer;
}
.playlist-track-title {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-family: var(--font-serif);
  font-size: 15px;
  color: var(--text);
}
.playlist-track-dur { color: var(--text-dim); font-variant-numeric: tabular-nums; font-size: 12px; }
.playlist-track.playing { background: var(--accent-soft); border-radius: 10px; padding-left: 10px; padding-right: 10px; }
.playlist-play { flex: 0 0 auto; }

/* ── Toast — короткое сообщение-заглушка ───────────────────────────────── */
.toast {
  position: absolute;
  left: 50%;
  bottom: calc(var(--bottombar-h) + var(--safe-bottom) + 14px);
  transform: translateX(-50%);
  max-width: 82%;
  padding: 9px 14px;
  border-radius: 12px;
  background: rgba(24, 22, 20, 0.94);
  color: #f4efe6;
  font-size: 12px;
  line-height: 1.4;
  text-align: center;
  border: 1px solid rgba(255, 255, 255, 0.14);
  box-shadow: 0 6px 22px rgba(0, 0, 0, 0.4);
  z-index: 90;
  pointer-events: none;
}
.toast.hidden { display: none; }

/* Глобальный бар эфира */
.broadcast-bar-live {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  border-radius: 50%;
  background: rgba(224,93,93,0.14);
  border: 1px solid rgba(224,93,93,0.4);
  flex-shrink: 0;
}
.broadcast-bar-now {
  font-size: 11px;
  color: var(--text-dim);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* П.39 (владелец, 26.09): «и при играющем эфире тоже: сделать бар компактным».
   Одна строка: точка «В ЭФИРЕ» · бегущее название передачи (многоточие
   допустимо — это название, а не текст для чтения) · пауза · крестик.
   Наружная высота 34 px (было 66): кнопки 32 px — минимум тап-зоны.
   Переопределения только для #broadcast-bar: общий .audio-bar не трогаем,
   им пользуются аудио-бары читалки и разбора. */
#broadcast-bar {
  /* Дизайн 26.09 (владелец: «вцелом оцени все радио мне не нравится сейчас
     бар там все скучено ... сделай все хорошо по дизайнерски»): сетка с ясной
     иерархией — сначала станция/состояние, затем название программы во всю
     ширину, затем кнопка. Горизонтальные отступы между элементами ≥ 8 px,
     строки текста одинаковой высоты (16 px). Кнопка — 44 px с состояниями
     «▶ Слушать эфир» / «⏸ Пауза», ширина не прыгает между состояниями.
     Высота бара ≈ 71 px — коридор владельца 56–72 px. */
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto auto;
  grid-template-rows: auto auto;
  column-gap: 10px;
  row-gap: 3px;
  align-items: center;
  margin: 0 8px;
  padding: 3px 10px;
  border-radius: 12px;
}
#broadcast-bar .broadcast-bar-live {
  grid-row: 1;
  grid-column: 1;
  width: 16px;
  height: 16px;
  display: flex;
  align-items: center;
  justify-content: center;
}
#broadcast-bar .audio-bar-info {
  grid-row: 1;
  grid-column: 2;
  display: flex;
  align-items: center;
  gap: 8px;
  min-width: 0;
}
#broadcast-bar .audio-bar-info > span {
  font-size: 11px;
  line-height: 16px;
  font-weight: 600;
  letter-spacing: 0.04em;
  color: var(--text);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
#broadcast-bar #broadcast-bar-toggle {
  grid-row: 1;
  grid-column: 3;
  width: auto;
  min-width: 116px;
  padding: 0 14px;
  font-size: 13px;
  font-weight: 600;
}
#broadcast-bar #broadcast-bar-close {
  grid-row: 1;
  grid-column: 4;
}
/* Название программы — во всю ширину: многоточие только если не влезло
   и в одну строку (владелец просил не обрезать без нужды). */
#broadcast-bar .broadcast-bar-now {
  grid-row: 2;
  grid-column: 1 / -1;
  font-size: 12px;
  line-height: 16px;
  color: var(--text-dim);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* Тап-зоны бара: кнопка ≥ 44 px (владелец), крестик 44×44 (п.42: ≥ 40). */
#broadcast-bar .audio-bar-btn {
  height: 44px;
  min-width: 44px;
  font-size: 15px;
}
/* .hidden обязан побеждать display:grid у #broadcast-bar: id-селектор
   сильнее класса, поэтому скрытие бара прописываем явно. */
#broadcast-bar.hidden { display: none; }

/* ---------------------------------------------------------------------------
   Режим погружения (фаза 3): тап по тексту читалки прячет хедер и таб-бар
--------------------------------------------------------------------------- */
#app.immersive #header { display: none; }
/* Нижний бар виден ВСЕГДА (решение Шаг 5.1): не прячем его в иммерсиве. */

/* ---------------------------------------------------------------------------
   Тема «Ночная» (InStory 4): прежний ночной вид приложения, теперь сменная
   тема. Светлая база (фаза 1) не отняла ночь: кто читает в темноте, выбирает
   её в настройках. Значения — ровно те, что раньше стояли в :root.
--------------------------------------------------------------------------- */
.theme-night {
  --bg: #0E0F12;
  --bg-elevated: #17191E;
  --bg-sheet: #20232A;
  --text: #F3F0E9;
  --text-dim: #9AA0A6;
  --accent: #D4AF37;
  --accent-soft: rgba(212, 175, 55, 0.15);
  --on-accent: #14120A;
  --amber: #D4AF37;
  --amber-soft: rgba(212, 175, 55, 0.15);
  --live: #E06A5E;
  --success: #6aab79;
  --success-soft: rgba(106, 171, 121, 0.2);
  --border: #2A2D34;
  --g-verb: #7ecf8f;
  --g-noun: #7db4f2;
  --g-toinf: #e0b26a;
  --flash-verb: #7ecf8f;
  --flash-noun: #7db4f2;
  --flash-pron: #c79df2;
  --flash-adj: #f29080;
  --flash-adv: #74d8d2;
  --flash-prep: #e890b8;
  --flash-conj: #9fb4d8;
  --flash-num: #c8e07e;
  --flash-intj: #f2c14e;
  color-scheme: dark;
}

/* ---------------------------------------------------------------------------
   Тема «Пергамент» (фаза 3): тёплая светлая палитра для долгого чтения
--------------------------------------------------------------------------- */
.theme-sepia {
  --bg: #f3ead7;
  --bg-elevated: #eadfc6;
  --bg-sheet: #e4d7ba;
  --text: #3f3527;
  --text-dim: #695a40;          /* фаза 2: было #8a7c63 — на пергаменте давало
                                   2.86–3.41 (ниже AA), «долгое чтение» страдало
                                   больше всех. Теперь >=4.5 везде. */
  --accent: #2F6B63;
  --accent-soft: rgba(47, 107, 99, 0.12);
  --on-accent: #f6f1e7;
  --amber: #b0782e;
  --amber-soft: rgba(176, 120, 46, 0.16);
  --live: #C0392B;
  --success: #4e8a5e;
  --border: #d5c6a5;
  /* Грамматическая подсветка: тёмные версии на светлом фоне */
  --g-verb: #2e7d44;
  --g-noun: #2b5fa8;
  --g-toinf: #a26a1c;
  /* Вспышки по частям речи: тёмные версии для сепии */
  --flash-verb: #2e7d44;
  --flash-noun: #2b5fa8;
  --flash-pron: #7a4fb0;
  --flash-adj: #b04a3a;
  --flash-adv: #2a8a84;
  --flash-prep: #a8487a;
  --flash-conj: #4a6a9a;
  --flash-num: #6a8a2e;
  --flash-intj: #a07a1e;
  color-scheme: light;
}

/* ===== AUTH — обязательный вход/регистрация ===== */
.auth-overlay {
  position: absolute;
  inset: 0;
  z-index: 100;
  background: var(--bg);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 20px;
}
.auth-overlay.hidden { display: none; }
.auth-card {
  width: 100%;
  max-width: 360px;
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: 16px;
  padding: 28px 24px;
  text-align: center;
}
.auth-logo { font-size: 40px; margin-bottom: 8px; }
.auth-title { font-size: 20px; font-weight: 700; color: var(--text); }
.auth-sub { font-size: 13px; color: var(--text-dim); margin: 4px 0 2px; }
/* Строка мира (владелец, 26.09.2026): «Сейчас ты живёшь в Санта-Марине».
   Отдельная строка, потому что мир — сменная часть: придёт второй мир
   внутри instory — меняется только она, название платформы и уровень нет. */
.auth-level { font-size: 12px; color: var(--text-dim); opacity: .75; margin: 2px 0 0; }
.auth-world { font-size: 12.5px; color: var(--text-dim); opacity: .8;
  margin: 0 0 18px; }
.auth-tabs { display: flex; gap: 6px; margin-bottom: 16px; }
.auth-tab {
  flex: 1;
  padding: 8px 0;
  border: 1px solid var(--border);
  background: transparent;
  color: var(--text-dim);
  border-radius: 8px;
  font-size: 13px;
  cursor: pointer;
}
.auth-tab.active { background: var(--accent-soft); color: var(--accent); border-color: var(--accent); }
.auth-form { display: flex; flex-direction: column; gap: 10px; }
.auth-input {
  padding: 12px 14px;
  border: 1px solid var(--border);
  background: var(--bg-sheet);
  color: var(--text);
  border-radius: 10px;
  font-size: 15px;
  outline: none;
}
.auth-input:focus { border-color: var(--accent); }
.auth-error { font-size: 13px; color: #e07a6a; }
.auth-error.hidden { display: none; }
.auth-submit {
  padding: 12px;
  border: none;
  background: var(--accent);
  color: var(--on-accent);
  border-radius: 10px;
  font-size: 15px;
  font-weight: 600;
  cursor: pointer;
}
.auth-submit:disabled { opacity: 0.6; cursor: default; }

/* ===== Регистрация: почта, обязательный пол, согласие =====================
   Поля регистрации отдельным блоком (.auth-register-only): при входе они
   скрыты целиком, поэтому «Вход» остался прежним экраном из двух полей. */
.auth-register-only { display: flex; flex-direction: column; gap: 10px; }
.auth-register-only.hidden { display: none; }
.auth-gender-label {
  font-size: 13px;
  color: var(--text);
  text-align: left;
  margin-top: 2px;
}
.auth-required { color: #e07a6a; }
.auth-gender-hint {
  font-size: 11px;
  color: var(--text-dim);
  text-align: left;
  margin-top: -6px;
}
/* Две крупные карточки вместо выпадающего списка: выбор из двух вариантов
   виден целиком и не требует лишнего нажатия (решение владельца). */
.auth-gender { display: flex; gap: 10px; }
.auth-gender-card {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  padding: 14px 8px;
  border: 2px solid var(--border);
  background: var(--bg-sheet);
  color: var(--text-dim);
  border-radius: 12px;
  font-size: 14px;
  font-family: inherit;
  cursor: pointer;
  transition: border-color 0.12s ease, background 0.12s ease, color 0.12s ease;
}
.auth-gender-card:hover { border-color: var(--text-dim); }
.auth-gender-card.selected {
  border-color: var(--accent);
  background: var(--accent-soft);
  color: var(--accent);
}
.auth-gender-icon { font-size: 26px; line-height: 1; }
.auth-gender-name { font-weight: 600; }
.auth-consent {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  text-align: left;
  font-size: 12px;
  color: var(--text-dim);
  line-height: 1.45;
  cursor: pointer;
}
.auth-consent-box { margin: 1px 0 0; flex: 0 0 auto; width: 16px; height: 16px; }
/* Галочку согласия на экране дозаполнения скрываем, если согласие уже дано
   (см. showProfileScreen): повторно требовать его нельзя, но и показывать
   неактивную галочку — вранье. Явное правило нужно потому, что общего
   .hidden { display:none } в этом файле нет. */
.auth-consent.hidden { display: none; }
/* Правило входа 28.09.2026: поле пароля живёт только для ВХОДА — при
   регистрации его скрывает chat.js. */
.auth-input.hidden { display: none; }
/* УСЛОВИЯ ТРИАЛА на форме входа: «24 часа бесплатно…» — объявление показано
   до нажатия кнопки, а согласие — действие. */
.auth-terms {
  margin: 2px 0 0;
  padding: 10px 12px;
  border: 1px solid var(--line, rgba(0, 0, 0, 0.14));
  border-radius: 10px;
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.auth-terms.hidden { display: none; }
.auth-terms-text { font-size: 13px; line-height: 1.45; }
.auth-terms-note { font-size: 12px; line-height: 1.4; opacity: 0.8; }
.auth-consent-text { flex: 1; }
/* Ссылка на политику обработки данных — заметная и в одном месте: URL
   приходит из GET /api/auth/providers (chat_api.PRIVACY_POLICY_URL). */
.auth-consent-link { color: var(--accent); text-decoration: underline; }

/* ===== Регистрация: аватарка (владелец 26.09) =============================
   Дословно: «предложить аватарку, но она не обязательно, можно оставить без
   фото»; «не смайлик, а несколько 5–6 аватарок разного рода, мужские и
   женские соответственно, после выбора пола». Сетка 3 в ряд × 2 ряда на
   телефоне; тап-зона каждой кнопки не меньше 44 px (п.42 «кнопки и ссылки...
   не должны быть слишком маленькими»); выбранная — рамка и галочка. */
.auth-avatar-block.hidden { display: none; }
.auth-optional {
  color: var(--text-dim);
  font-size: 11px;
  font-weight: 400;
  margin-left: 6px;
}
.auth-avatar-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 10px;
  justify-items: center;
}
.auth-avatar-btn {
  position: relative;
  width: 68px;
  height: 68px;
  min-width: 44px;
  min-height: 44px;
  padding: 0;
  border: 2px solid var(--border);
  background: var(--bg-sheet);
  border-radius: 50%;
  cursor: pointer;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 1px;
  font-family: inherit;
  transition: border-color 0.12s ease, box-shadow 0.12s ease;
}
.auth-avatar-btn:hover { border-color: var(--text-dim); }
.auth-avatar-btn.selected {
  border-color: var(--accent);
  box-shadow: 0 0 0 2px var(--accent-soft);
}
/* Галочка выбранной аватарки — поверх круга, у нижнего края. */
.auth-avatar-btn.selected::after {
  content: '✓';
  position: absolute;
  right: -3px;
  bottom: -3px;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  background: var(--accent);
  color: #fff;
  font-size: 13px;
  line-height: 22px;
  text-align: center;
}
/* Картинка — тем же круглым слоем, что портреты жителей в ленте
   (.chat-avatar-img): cover, без искажений, поверх фона кнопки. */
.auth-avatar-btn .chat-avatar-img { border: 0; }
/* Третий вариант «Без фото»: кружок с первой буквой имени (не смайлик). */
.auth-avatar-letter {
  width: 36px;
  height: 36px;
  border-radius: 50%;
  background: var(--accent-soft);
  color: var(--accent);
  font-size: 18px;
  font-weight: 700;
  display: flex;
  align-items: center;
  justify-content: center;
}
.auth-avatar-caption {
  font-size: 10px;
  color: var(--text-dim);
  line-height: 1;
}
.auth-providers { margin-top: 16px; }
.auth-providers.hidden { display: none; }
.auth-providers-sep {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 11px;
  color: var(--text-dim);
  margin-bottom: 10px;
}
.auth-providers-sep::before,
.auth-providers-sep::after {
  content: '';
  flex: 1;
  height: 1px;
  background: var(--border);
}
.auth-providers-list { display: flex; flex-direction: column; gap: 8px; }
.auth-provider-btn {
  padding: 10px 12px;
  border: 1px solid var(--border);
  background: var(--bg-sheet);
  color: var(--text);
  border-radius: 10px;
  font-size: 14px;
  font-family: inherit;
  cursor: pointer;
}
.auth-provider-btn:disabled { opacity: 0.6; cursor: default; }
.auth-profile-logout {
  background: none;
  border: none;
  color: var(--text-dim);
  font-size: 12px;
  font-family: inherit;
  text-decoration: underline;
  cursor: pointer;
  padding: 4px 0 0;
}

/* ===== ВОССТАНОВЛЕНИЕ ДОСТУПА (сброс пароля, письмо администрации) ===== */

/* «Забыл пароль?» — под кнопкой входа. Отдельная строка, чтобы не сдвигать
   основную кнопку и не выглядеть частью формы. */
.auth-forgot-row {
  text-align: center;
  padding: 10px 0 0;
}
.auth-link {
  background: none;
  border: none;
  color: var(--text-dim);
  font-size: 12px;
  font-family: inherit;
  text-decoration: underline;
  cursor: pointer;
  padding: 4px 6px;
}
.auth-link:hover { color: var(--text); }
.auth-link.hidden { display: none; }

/* ===== Регистрация в два шага (решение владельца 27.09.2026) =============
   Шаг 1 — имя и пароль, шаг 2 — пол и согласие. Оба шага живут в одной форме
   и переключаются классом, поэтому введённое не теряется: поля не
   пересоздаются, меняется только видимость блоков.
   Подсказка про пароль («от 4 знаков — хоть простой») стоит рядом с полем:
   правило видно заранее, и оно намеренно мягкое. */
.auth-step { display: flex; flex-direction: column; gap: 10px; }
.auth-step.hidden { display: none; }
.auth-hint {
  font-size: 12px;
  color: var(--text-dim);
  text-align: left;
  margin-top: -4px;
  line-height: 1.4;
}
.auth-hint.hidden { display: none; }
/* Кнопка шага: та же крупная кнопка формы, но у «Дальше» и у отправки общее
   состояние disabled — правила у них одинаковые. */
.auth-submit.hidden { display: none; }

/* ===== Вход через аккаунт RuStore: честная заглушка ======================
   Кнопка есть, но подпись прямо говорит «появится к выпуску»: обещать вход,
   которого нет, хуже, чем честно сказать о сроке. Механика RuStore ID нашими
   документами не подтверждена, поэтому протокол не выдумывается — ответ даёт
   сервер (POST /api/auth/rustore), и он же показывается человеку. */
.auth-rustore-block { margin-top: 14px; }
.auth-rustore-block.hidden { display: none; }
.auth-rustore-btn {
  width: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  padding: 12px;
  border: 1px solid var(--border);
  background: transparent;
  color: var(--text);
  border-radius: 10px;
  font-size: 14px;
  font-family: inherit;
  cursor: pointer;
}
.auth-rustore-btn:disabled { opacity: 0.6; cursor: default; }
.auth-rustore-soon {
  font-size: 11px;
  color: var(--text-dim);
  opacity: 0.85;
}

/* Крестик «закрыть и продолжить читать» на карточке входа. Экран входа больше
   не обязателен (гостевой режим), поэтому у него есть выход: карточка получает
   относительное позиционирование, а кнопка садится в её правый верхний угол.
   Тап-зона не меньше 44 px — тем же правилом, что у остальных кнопок (п.42). */
.auth-card { position: relative; }
.auth-close {
  position: absolute;
  top: 6px;
  right: 6px;
  width: 44px;
  height: 44px;
  border: none;
  background: transparent;
  color: var(--text-dim);
  font-size: 16px;
  line-height: 1;
  border-radius: 50%;
  cursor: pointer;
}
.auth-close:hover { color: var(--text); }

/* ===== Гостевой мостик: одна строка, ничего не блокирует =================
   Чтение открыто без аккаунта, и это приглашение — а не условие. Ноль модалок,
   ноль заслонок: строка стоит ниже шапки и не закрывает ни слова текста.
   Тон — «заведи аккаунт — прогресс останется с тобой»: у гостя есть гостевой
   вход (POST /api/auth/guest, app/chat.js) и установочный id (app/install.js),
   прогресс записывается сразу и при регистрации переезжает в аккаунт, поэтому
   пугать потерей нечем и незачем. */
.guest-bridge {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 6px 12px;
  font-size: 12px;
  color: var(--text-dim);
  background: var(--accent-soft);
  border-bottom: 1px solid var(--border);
}
.guest-bridge.hidden { display: none; }
.guest-bridge-text { line-height: 1.35; }
.guest-bridge-cta {
  flex: 0 0 auto;
  padding: 6px 10px;
  border: 1px solid var(--accent);
  background: transparent;
  color: var(--accent);
  border-radius: 8px;
  font-size: 12px;
  font-family: inherit;
  cursor: pointer;
}

/* ===== Почта в настройках: спокойный необязательный блок ================= */
.settings-hint {
  font-size: 12px;
  color: var(--text-dim);
  line-height: 1.45;
  margin: 4px 0 8px;
}
.settings-email-line { display: flex; gap: 8px; align-items: center; }
.settings-email-line .auth-input { flex: 1; min-width: 0; }

/* Виды внутри карточки: запрос сброса и ввод нового пароля. Это не отдельные
   экраны, а подмена содержимого — иначе пришлось бы дублировать рамку. */
.auth-view { display: flex; flex-direction: column; gap: 10px; }
.auth-view.hidden { display: none; }
.auth-view-title {
  font-size: 15px;
  font-weight: 600;
  text-align: center;
}
.auth-view-hint {
  font-size: 12px;
  color: var(--text-dim);
  text-align: center;
  line-height: 1.45;
}

/* Текстовое поле обращения: та же рамка, что у input, но выше и с переносом. */
.auth-textarea {
  resize: vertical;
  min-height: 88px;
  font-family: inherit;
  line-height: 1.4;
}

/* Сообщение об успехе. Отличается от .auth-error цветом и тем, что НЕ
   предлагает ничего исправить: «письмо отправлено» — это не ошибка. */
.auth-note {
  font-size: 12px;
  color: #4ade80;
  line-height: 1.45;
  text-align: center;
}
.auth-note.hidden { display: none; }

/* «Не получается войти? Написать администрации» — на обоих экранах входа. */
.auth-support-row {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: center;
  gap: 2px;
  padding: 12px 0 0;
}
.auth-support-q { font-size: 12px; color: var(--text-dim); }

/* ===== CHAT — живой чат на английском ===== */
#screen-chat {
  display: none;
  flex: 1;
  flex-direction: column;
  overflow: hidden;
}
#screen-chat.active { display: flex; animation: screen-fade 0.15s ease; }
.chat-feed {
  flex: 1;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  padding: 16px 14px;
  display: flex;
  flex-direction: column;
  gap: 14px;
}
.chat-msg { display: flex; gap: 10px; align-items: flex-start; }
.chat-msg.mine { flex-direction: row-reverse; }
.chat-avatar {
  position: relative;
  /* Владелец 03.10: крупнее, чтобы было видно лицо говорящего (было 36px). */
  width: 52px;
  height: 52px;
  flex-shrink: 0;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 18px;
}
.chat-body { display: flex; flex-direction: column; gap: 3px; max-width: 78%; }
.chat-msg.mine .chat-body { align-items: flex-end; }

/* Фаза 8.9: портрет жителя поверх эмодзи-аватара. Круглая картинка закрывает
   цветной фон и эмодзи полностью; онлайн-точка (.chat-participant-dot) лежит
   поверх и НЕ затирается, потому что img — sibling, а не parent. */
.chat-avatar-img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  border-radius: 50%;
  object-fit: cover;
  display: block;
  pointer-events: none;
}
.chat-name { font-size: 12px; font-weight: 600; }
.chat-bubble {
  background: var(--bg-elevated);
  border: 0;
  border-radius: 4px 16px 16px 16px;
  box-shadow: 0 1px 2px rgba(34, 31, 26, 0.05);
  padding: 10px 14px;
  font-size: 15.5px;
  line-height: 1.5;
  color: var(--text);
  word-wrap: break-word;
}
.chat-msg.mine .chat-bubble {
  background: var(--accent-soft);
  border-radius: 16px 16px 4px 16px;
}
/* Ступень разбора (--fs-half), а не основного текста: на одном экране не
   должно быть двух размеров для одного и того же (владелец 29.09.2026). */
/* Владелец 06.10.2026: «зачем в чате открыт перевод каждого предложения без тапа —
   надо убрать». Строка перевода остаётся в разметке (её используют тап по слову,
   озвучка и разбор), но сама не показывается: её открывает тап по реплике.
   Повторный тап закрывает. Класс chat-ru-open ставят chat.js и chat_tap.js. */
.chat-ru-note { font-size: var(--fs-half); color: var(--text-dim); font-style: italic; }
/* Владелец 06.10.2026: рамка разговора перед первой репликой сцены — «понятно,
   о чём говорят». Второе замечание владельца: «снизу сообщения русский перевод
   без тапа». Причина — рамка стояла ВНУТРИ пузыря первой реплики, то есть под
   последней репликой прошлого разговора, и читалась как её перевод. Теперь это
   отдельный разделитель ленты: подписан словом «Разговор», отделён отступом и
   фоном, на тап не реагирует. */
.chat-msg .chat-ru-note { display: none; }
.chat-msg.chat-ru-open .chat-ru-note { display: block; }
/* Этап 1 унификации (24.09.2026): тап по слову НЕ создаёт вторую русскую
   строку — он указывает на постоянную (.chat-ru-note), коротко подсвечивая
   её. Класс вешает и снимает сам chat_tap.js (700 мс), у узла нет
   владельца-выборки, который убрал бы класс вместе с подсветкой. */
.chat-ru-note-pulse {
  animation: chat-tap-pulse 0.7s ease-in-out 1;
}

/* Этап 4 плана латентности: реплика сохранена сразу, перевод едет фоном.
   Плашка честно называет состояние вместо «перевод: <русский>». */
.chat-translating-note { opacity: 0.75; }
/* Этап 2 плана перевода (24.09.2026): свой исходный русский под СВОИМ
   английским. Это не перевод чужой реплики, а то, что автор сам написал,
   поэтому без курсива и подписи «перевод:»; чуть светлее — читается как своя
   же фраза. Текст лежит в .chat-ru-own и НЕ дублирует пузырь. */
.chat-ru-own { font-style: normal; color: var(--text); opacity: 0.85; }
/* Этап 4 плана перевода (24.09.2026): кнопка «разобрать» и её панель.
   Кнопка живёт в СТРОКЕ активной выборки — .chat-speak-row, рядом с
   «🔊 Озвучить», — и её строит app/chat_tap.js в момент тапа, в клиенте того,
   кто тапнул. В разметке реплики и в серверном HTML кнопки нет вообще, поэтому
   «спрятать CSS» тут нечего: чужой клиент её не получает. Плашка выборки —
   курсивная (.chat-sentence-note), поэтому блок панели выпрямляем.
   Панель — ОТДЕЛЬНЫЙ узел под строкой кнопок (в flex-строку блочный список не
   влезает) и НЕ карта тапа: тап по слову по-прежнему показывает готовый
   перевод всего предложения. */
.chat-breakdown { margin-top: 4px; font-style: normal; }
/* Кнопка разбора: 14px — ступень основного текста (--fs-base), поле 5px 11px:
   палец на телефоне попадает не по пикселю, а по площади (владелец 29.09.2026). */
.chat-breakdown-btn {
  font: inherit;
  font-size: var(--fs-base);
  color: var(--text-dim);
  background: transparent;
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 5px 11px;
  cursor: pointer;
}
.chat-breakdown-btn:hover:not(:disabled) { color: var(--text); }
.chat-breakdown-btn:disabled { cursor: default; opacity: 0.75; }
.chat-breakdown-panel {
  margin-top: 4px;
  border-left: 2px solid color-mix(in srgb, var(--accent) 42%, transparent);
  padding-left: 8px;
}
/* Владелец 29.09.2026: разбор читают с телефона и просили крупнее. Было 12px,
   стало 16px — четверть золотого шага от основного текста (--fs-half): крупнее
   текста реплик, но тише перевода, который читают первым. */
.chat-breakdown-row { font-size: var(--fs-half); line-height: 1.5; }
.chat-breakdown-word { color: var(--text); font-weight: 600; }
.chat-breakdown-tr { color: var(--text); margin-left: 6px; }
.chat-breakdown-why {
  color: var(--text-dim);
  font-style: italic;
  display: block;
}
/* Этап 1 + 2 п.3: перевод честно сорвался. Не «переводится…» навсегда и не
   поддельный английский: короткая строка с точечной рамкой снизу. */
.chat-translation-failed-note {
  font-style: normal;
  opacity: 0.9;
  border-bottom: 1px dashed var(--border);
}
.chat-time { font-size: 11px; color: var(--text-dim); opacity: 0.7; }

/* Фаза 8.6: карточка урока «📖 Разбор» в ленте города — видна как объявление
   занятия (решение №10): акцентная рамка, заголовок, разбор по абзацам,
   призыв ответить по-русски и кнопка открытия слоя урока. */
.chat-msg-lesson .chat-body { max-width: 86%; }
.chat-lesson-card {
  background: var(--bg-elevated);
  border: 1px solid var(--accent);
  border-left: 3px solid var(--accent);
  border-radius: 12px;
  padding: 10px 12px;
  display: flex;
  flex-direction: column;
  gap: 7px;
}
.chat-lesson-head {
  font-size: 12px;
  font-weight: 700;
  color: var(--accent);
  text-transform: uppercase;
  letter-spacing: 0.05em;
}
.chat-lesson-text { display: flex; flex-direction: column; gap: 5px; }
.chat-lesson-para { font-size: 15px; line-height: 1.45; color: var(--text); word-wrap: break-word; }
.chat-lesson-ask {
  font-size: 13px;
  line-height: 1.4;
  color: var(--text-dim);
  font-style: italic;
  border-top: 1px dashed var(--border);
  padding-top: 7px;
}
.chat-lesson-open {
  align-self: flex-start;
  background: var(--accent);
  color: var(--on-accent);
  border: 0;
  border-radius: 999px;
  padding: 7px 14px;
  font-size: 13px;
  font-weight: 600;
  font-family: inherit;
  cursor: pointer;
}
.chat-lesson-open:active { opacity: 0.85; }

.chat-word { cursor: pointer; border-radius: 3px; }
.chat-word:active { background: var(--accent-soft); }

/* Stage E: loading/error индикаторы динамического tap-to-translate.
   Ремарка Сергея (24.09.2026): слово в ленте не мигает — пульсация
   (animation: chat-tap-pulse ... infinite) снята, осталась статичная мягкая
   подложка «идёт запрос». Умолчание чата вообще не показывает на слове
   никакого состояния: тап идёт через ChatTap.quickTap в тихом режиме. */
.chat-word-loading {
  opacity: 0.55;
  background: var(--accent-soft);
}
@keyframes chat-tap-pulse {
  0%, 100% { opacity: 0.4; }
  50% { opacity: 0.75; }
}
.chat-word-error {
  opacity: 0.65;
  color: var(--flash-verb); /* ошибка — заметный цвет, но не кричащий */
  font-style: italic;
}

/* ===== Этап 3b (ремарка Сергея, 24.09.2026): цвета частей речи на тапнутой реплике =====
   В ленте чата вспышка перевода выключена (слово не мигает и не подменяется
   русским), поэтому цвет больше НЕ может жить под .translating-active: он
   держится на постоянном классе реплики .chat-pos-on. Класс ставит
   chat_tap.js на .chat-bubble в момент тапа и снимает вместе с выборкой
   (повторный тап по тому же предложению / flush / смена epoch).
   Групповые цвета (глагол / существительное / to-инфинитив) дают постоянные
   классы mh-g-* из grammar.js, одиночные слова — mh-flash-<pos>, которые на
   тап ставит сам chat_tap.js (раньше их ставил флеш-движок app.js).
   Рамок у цвета нет: только цвет, никакого box-shadow. */
.chat-pos-on .reader-word.mh-g-verb { color: var(--g-verb); }
.chat-pos-on .reader-word.mh-g-noun { color: var(--g-noun); }
.chat-pos-on .reader-word.mh-g-toinf { color: var(--g-toinf); }
.chat-pos-on .reader-word.mh-flash-noun { color: var(--flash-noun); }
.chat-pos-on .reader-word.mh-flash-verb { color: var(--flash-verb); }
.chat-pos-on .reader-word.mh-flash-pron { color: var(--flash-pron); }
.chat-pos-on .reader-word.mh-flash-adj  { color: var(--flash-adj); }
.chat-pos-on .reader-word.mh-flash-adv  { color: var(--flash-adv); }
.chat-pos-on .reader-word.mh-flash-prep { color: var(--flash-prep); }
.chat-pos-on .reader-word.mh-flash-conj { color: var(--flash-conj); }
.chat-pos-on .reader-word.mh-flash-num  { color: var(--flash-num); }
.chat-pos-on .reader-word.mh-flash-intj { color: var(--flash-intj); }
/* Выключатель подсветки в настройках работает и здесь: при off цвета частей
   речи в тапнутой реплике обычные (по образцу .translating-active-правил).
   Классы mh-g-* / mh-flash-* и grammar.js не трогаем и не удаляем.
   :not(.karaoke-active) — слово, которое звучит прямо сейчас в тексте
   радио, сохраняет подсветку караоке даже при выключенной грамматике
   (караоке не ломаем — правило владельца). */
html.grammar-off .chat-pos-on .reader-word[class*="mh-"]:not(.karaoke-active) { color: inherit; }

/* В ленте чата вспышка перевода не применяется вовсе. Даже если класс
   .translating-active когда-нибудь окажется на слове реплики (например,
   явный откат localStorage.chat_tap_legacy_v1), жёлтой рамки #f59e0b и
   «воздуха» из padding там нет: читалка и радио свои правила сохраняют
   целиком (ремарка: «рамки уберем» — про тап в чате). */
#chat-feed .reader-word.translating-active {
  box-shadow: none;
  padding-left: 0;
  padding-right: 0;
}

/* 2B-2: подсветка всего предложения при тапе по слову (городской чат).
   Ремарка Сергея (24.09.2026): рамки убраны — остался только мягкий фон
   (box-shadow из рамки снят; рамка и мягкий фон вместе читались как обводка). */
.chat-sentence-active {
  background: var(--accent-soft);
  border-radius: 3px;
}
/* 2B-3: note с переводом выбранного предложения под баблом (city chat).
   Оформлен как .chat-ru-note (перевод всей реплики), но относится только
   к подсвеченному предложению; активна одна на страницу. */
/* Владелец 29.09.2026: «шрифт перевода после тапа на предложение очень мелкий,
   на телефоне будет плохо видать»; затем «увеличить, используй правило золотого
   сечения». Было 12px — мельче подписей; стало 18px: ступень √φ от основного
   текста 14px (--fs-golden). Перевод под тапом — то, ради чего тапнули, и он
   читается как текст, а не как сноска. */
.chat-sentence-note {
  font-size: var(--fs-golden);
  line-height: 1.35;
  color: var(--text-dim);
  font-style: italic;
}
/* Этап 3d: озвучка выбранного предложения. Кнопка живёт ВНУТРИ .chat-sentence-note
   (та же плашка активной выборки, гаснет вместе с ней), но текст перевода под ней
   остаётся курсивом — саму плашку озвучки выпрямляем (font-style: normal).
   Оформление кнопки — как у .chat-breakdown-btn (та же «тихая» пилюля). */
.chat-speak-row {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-top: 4px;
  font-style: normal;
  flex-wrap: wrap;   /* «🔊 Озвучить» + «Разобрать» в одной строке, но не в обрез */
}
.chat-speak-btn {
  font: inherit;
  font-size: var(--fs-base);
  color: var(--text-dim);
  background: transparent;
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 5px 11px;
  cursor: pointer;
}
.chat-speak-btn:hover { color: var(--text); }
.chat-speak-btn.mh-speaking { color: var(--text); border-color: var(--accent-soft); }
/* П.30 (арбитраж голосов): пока слышно радио (эфир или караоке плейлиста),
   кнопка озвучки предложения неактивна — визуально приглушена и не кликается;
   под ней понятная подсказка .chat-speak-hint, почему она выключена. */
.chat-speak-btn:disabled {
  opacity: 0.45;
  cursor: default;
}
.chat-speak-btn:disabled:hover { color: var(--text-dim); }
.chat-speak-hint {
  font-size: 12px;
  line-height: 1.4;
  color: var(--text-dim);
  opacity: 0.85;
  margin-top: 2px;
}
.chat-input-bar {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 10px 14px calc(12px + var(--safe-bottom));
  border-top: 0;
  background: var(--bg-elevated);
  flex-shrink: 0;
}
.chat-input {
  flex: 1;
  height: 46px;
  padding: 12px 16px;
  border: 0;
  background: var(--bg-sheet);
  color: var(--text);
  border-radius: 23px;
  font-size: 15px;
  outline: none;
}
.chat-input:focus { box-shadow: 0 0 0 1.5px var(--accent); }
.chat-send {
  /* Владелец 08.10.2026: «кнопка, которой отправляем сообщение, вытянута —
     сделай её просто круглой, удобной для нажатия». Ровный круг 46×46 в высоту
     поля ввода (П.42: зона нажатия ≥ 40 px), стрелка по центру. */
  width: 46px;
  height: 46px;
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  border: none;
  background: var(--accent);
  color: var(--on-accent);
  border-radius: 50%;
  font-size: 17px;
  line-height: 1;
  cursor: pointer;
}
.chat-send:disabled { opacity: 0.6; }

/* ===== Чат с Кейт: шапка собеседника, «пишет…», цитата, значок ===== */

/* Шапка: собеседник один — учительница Кейт (городского списка участников
   больше нет: он удалён вместе с городским чатом). */
.chat-header {
  flex-shrink: 0;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 12px 14px;
  border-bottom: 0;
  background: var(--bg-elevated);
}
.chat-kate {
  display: flex;
  align-items: center;
  gap: 9px;
  min-width: 0;
  flex: 1 1 auto;
}
.chat-kate-avatar {
  width: 44px;
  height: 44px;
  flex-shrink: 0;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 20px;
  background: var(--bg-sheet);
  border: 0;
  box-shadow: 0 0 0 1px var(--border);
  overflow: hidden;
}
.chat-kate-avatar img { width: 100%; height: 100%; object-fit: cover; }
.chat-kate-text {
  display: flex;
  flex-direction: column;
  line-height: 1.2;
  min-width: 0;
}
.chat-kate-name { font-family: var(--font-serif); font-size: 19px; font-weight: 600; color: var(--text); }
.chat-kate-role { font-size: 12px; color: var(--text-dim); margin-top: 1px; }

/* Значки AI и языкового помощника */
.chat-ai-badge {
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 0.5px;
  padding: 1px 5px;
  border-radius: 6px;
  background: var(--bg-sheet);
  color: var(--text-dim);
  border: 1px solid var(--border);
}
.chat-helper-badge {
  font-size: 10px;
  padding: 1px 6px;
  border-radius: 6px;
  background: var(--accent-soft);
  color: var(--accent);
  border: 1px solid color-mix(in srgb, var(--accent) 32%, transparent);
}

/* «Печатает…» */
.chat-typing {
  flex-shrink: 0;
  padding: 4px 16px 6px;
  font-size: 12px;
  font-style: italic;
  color: var(--text-dim);
}
.chat-typing.hidden { display: none; }
.chat-typing::after {
  content: '…';
  animation: typing-blink 1.2s ease-in-out infinite;
}
@keyframes typing-blink {
  0%, 100% { opacity: 0.3; }
  50% { opacity: 1; }
}

/* Цитата в сообщении */
.chat-quote {
  border-left: 3px solid var(--accent);
  background: var(--accent-soft);
  border-radius: 0 8px 8px 0;
  padding: 5px 9px;
  font-size: 12px;
  max-width: 100%;
  cursor: pointer;
  display: flex;
  flex-direction: column;
  gap: 1px;
}
.chat-quote-name { font-weight: 600; }
.chat-quote-text {
  color: var(--text-dim);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  max-width: 240px;
}
.chat-quote-gone { color: var(--text-dim); font-style: italic; }

/* Превью цитаты над полем ввода */
.chat-reply-preview {
  flex-shrink: 0;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 7px 12px;
  border-top: 1px solid var(--border);
  background: var(--bg-elevated);
}
.chat-reply-preview.hidden { display: none; }
.chat-reply-preview-text {
  flex: 1;
  min-width: 0;
  border-left: 3px solid var(--accent);
  padding-left: 9px;
  display: flex;
  flex-direction: column;
}
.chat-reply-preview-label { font-size: 10px; color: var(--text-dim); text-transform: uppercase; letter-spacing: 0.5px; }
.chat-reply-preview-name { font-size: 12px; font-weight: 600; }
.chat-reply-preview-body {
  font-size: 12px;
  color: var(--text-dim);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.chat-reply-cancel {
  background: none;
  border: none;
  color: var(--text-dim);
  font-size: 14px;
  cursor: pointer;
  padding: 6px;
}

/* §3.7 / §40: МЯГКАЯ СТЕНА ПОДПИСКИ — одна на всё приложение.
   Стоит поверх экранов (z-index 260: выше оверлеев 200 и слоёв занятий 70,
   ниже заставки 300 — на холодном старте заставка ещё играет) и появляется
   ТОЛЬКО по отказу сервера: пока сутки идут или подписка активна, карточки на
   экране нет. Оформление нарочно спокойное: это приглашение, а не
   предупреждение — ни красного, ни мигания, ни таймера. Кегль обычный и
   одинаковый: мелкого шрифта со сносками у нас нет (§15 п.4). */
.access-wall {
  position: absolute;
  inset: 0;
  z-index: 260;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 14px;
  padding: 28px 22px calc(28px + var(--safe-bottom));
  box-sizing: border-box;
  overflow-y: auto;
  text-align: center;
  background: var(--bg);
}
/* Класс И атрибут: .hidden в style.css общий только для конкретных блоков,
   поэтому карточку прячет именно это правило (см. app/access_wall.js,
   hidePaywallCard). */
.access-wall.hidden { display: none; }
.access-wall-hint {
  max-width: 30em;
  font-size: 18px;
  line-height: 1.4;
  color: var(--text);
}
/* Строка «что ты успел за сутки» — тише намёка: факты поддерживают разговор,
   а не спорят с ним. */
.access-wall-facts {
  max-width: 30em;
  font-size: 14px;
  line-height: 1.4;
  color: var(--text-dim);
}
.access-wall-facts.hidden { display: none; }
/* Строка-указатель «где лежит весь итог» — тише фактов: это короткая
   подсказка «куда дальше», а не новость суток. */
.access-wall-where {
  max-width: 30em;
  font-size: 13px;
  line-height: 1.4;
  color: var(--text-dim);
}
.access-wall-where.hidden { display: none; }
/* Кнопка-ссылка «О подписке» — тап-таргет UI-кита (44 px), как у .mh-btn. */
.access-wall-subscribe {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 44px;
  padding: 0 20px;
  border: 1px solid var(--accent);
  border-radius: 22px;
  color: var(--accent);
  font-size: 16px;
  text-decoration: none;
}
/* ЧТО ДЕЛАЕТ СТЕНА С ПРИЛОЖЕНИЕМ. Продуктовые поверхности за ней выключаются
   ЦЕЛИКОМ — именно display:none, а не opacity: полупрозрачный живой экран
   выглядит рабочим и врёт человеку («серый призрак»). Здесь ровно продукты:
   экраны чтения, эфира, города и «Прогресса», гостевой мостик, полоса эфира и
   нижний ряд вкладок (все четыре ведут в продукт). Страниц подписки и правил,
   политики и аккаунта в этом списке НЕТ и не будет: они открыты всегда (§3.7 —
   политика и удаление данных это закон), и это отдельные страницы, которых
   стена не касается. */
#app.access-wall-up .screen,
#app.access-wall-up .guest-bridge,
#app.access-wall-up #broadcast-bar,
#app.access-wall-up .tab-bar {
  display: none;
}

/* Подсветка пузыря при выборе и при переходе по цитате */
.chat-bubble-picked { outline: 2px solid var(--accent); outline-offset: 1px; }
.chat-msg-flash .chat-bubble { animation: msg-flash 1.5s ease; }
@keyframes msg-flash {
  0% { box-shadow: 0 0 0 3px rgba(216, 166, 87, 0.55); }
  100% { box-shadow: 0 0 0 0 rgba(216, 166, 87, 0); }
}

/* Значок непрочитанного чата — .tab-badge на табе «Чат» (#chat-badge внутри
   .tab-icon-wrap); отдельной плавающей кнопки у чата нет. */

/* ===== СЛОЙ УРОКА (Фаза 8.6) =====
   Полноэкранный слой занятия: вход — карточка «📖 Разбор» в ленте городского
   чата. Перекрывает нижний бар (bottom:0) — у урока своё поле ответа снизу, —
   но начинается под аудио-барами вверху (--bars-top).
   Слои: splash 300 > settings 200 > auth 100 > тост 90 > урок 70 > нижний бар 10. */
.lesson-layer {
  position: absolute;
  top: var(--bars-top, 0px);
  left: 0;
  right: 0;
  bottom: 0;
  background: var(--bg);
  z-index: 70;
  display: flex;
  flex-direction: column;
}
.lesson-layer.hidden { display: none; }
.lesson-layer-header {
  height: calc(var(--header-h) + var(--safe-top));
  padding-top: var(--safe-top);
  display: flex;
  align-items: center;
  gap: 10px;
  padding-left: 16px;
  padding-right: 8px;
  border-bottom: 1px solid var(--border);
  flex-shrink: 0;
}
.lesson-layer-title {
  flex: 1;
  font-size: 16px;
  font-weight: 600;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* ===== LESSONS — занятия (8.12: список — слой #lessons-layer, урок — #lesson-layer) ===== */
.lessons-layer {
  position: absolute;
  top: var(--bars-top, 0px);
  left: 0;
  right: 0;
  bottom: 0;
  background: var(--bg);
  z-index: 70;
  display: flex;
  flex-direction: column;
}
.lessons-layer.hidden { display: none; }
.lessons-scroll {
  flex: 1;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  padding: 14px 14px calc(16px + var(--safe-bottom));
}
.lesson-today-card.hidden,
.lesson-view.hidden { display: none; }
.lessons-counter {
  font-size: 12px;
  color: var(--text-dim);
  margin: 4px 2px 10px;
}
/* Кнопка «Продолжить занятие» (.lesson-today-resume) удалена вместе с шагами:
   возобновлять в одноэкранном уроке нечего (владелец, 03.10.2026). */

.lesson-today-card {
  background: linear-gradient(135deg, var(--accent-soft), var(--amber-soft));
  border: 1px solid color-mix(in srgb, var(--amber) 30%, transparent);
  border-radius: 14px;
  padding: 16px;
  margin-bottom: 18px;
}
.lesson-today-label { font-size: 12px; color: var(--text-dim); text-transform: uppercase; letter-spacing: 0.05em; }
.lesson-today-title { font-size: 18px; font-weight: 700; color: var(--text); margin: 6px 0 2px; }
.lesson-today-en { font-size: 14px; color: var(--accent); font-style: italic; margin-bottom: 12px; }
.lesson-today-btn {
  background: var(--accent); color: var(--on-accent); border: none; border-radius: 10px;
  padding: 9px 16px; font-size: 14px; font-weight: 600; cursor: pointer;
}
.lesson-today-btn:active { opacity: 0.85; }

.lessons-section-title {
  font-size: 13px; color: var(--text-dim); text-transform: uppercase;
  letter-spacing: 0.05em; margin: 0 2px 8px;
}

.lesson-row {
  display: flex; align-items: center; gap: 12px;
  background: var(--bg-elevated); border: 1px solid var(--border);
  border-radius: 12px; padding: 11px 14px; margin-bottom: 8px; cursor: pointer;
}
.lesson-row:active { background: var(--bg-sheet); }
.lesson-row.current { border-color: color-mix(in srgb, var(--amber) 45%, transparent); }
.lesson-row.locked { opacity: 0.45; cursor: default; }
/* Бейдж номера занятия (.lesson-row-num) удалён: владелец (26.09) —
   «все занятия марты должны быть без номеров». */
.lesson-row-titles { flex: 1; min-width: 0; }
.lesson-row-title { font-size: 14px; font-weight: 600; color: var(--text); }
.lesson-row-en { font-size: 12px; color: var(--text-dim); font-style: italic; }
.lesson-row-state { flex-shrink: 0; font-size: 15px; }

/* --- просмотр урока --- */
/* Флекс-колонка: прогресс-бар закреплён сверху, контент скроллится
   (#lesson-scroll), поле ответа закреплено снизу. */
.lesson-view {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
}

/* Полосы шагов и кнопок «Дальше/Назад» в уроке больше нет: урок — одна
   страница, читатель идёт обычным скроллом, закрывает крестик в шапке слоя
   (владелец, 03.10.2026). Вместе с ними ушли .lesson-progress-label,
   .lesson-dots и .lesson-dot — правила удалены, а не спрятаны: мёртвая
   вёрстка шагов возвращает шаги. */

/* Единственная скроллящаяся область урока */
.lesson-scroll {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  padding: 12px 14px calc(16px + var(--safe-bottom));
}

/* Карточка урока — компонент .mh-card из UI-KIT.
   Название урока живёт в шапке слоя (.lesson-layer-title), поэтому в теле
   урока заголовка нет: раньше он дублировался прямо под своей же копией. */
.lesson-overline {
  font-size: 11px;
  color: var(--text-dim);
  text-transform: uppercase;
  letter-spacing: 0.1em;
  margin-bottom: 8px;
}
.lesson-section-title { font-size: 12px; color: var(--text-dim); margin-bottom: 10px; text-transform: uppercase; letter-spacing: 0.08em; }

/* Реплики ведущей — обычный текст: без значка на каждой строке */
/* Реплики учительницы — плотнее остальных блоков: шесть абзацев подряд не должны
   занимать половину первого экрана (замечание владельца, 03.10.2026). */
.lesson-intro-block { display: flex; flex-direction: column; gap: 7px; }
.lesson-intro-line { font-size: 15px; line-height: 1.5; color: var(--text); margin: 0; }

/* Карточка «как это сказать» — смысловой центр урока: фраза, цитата из книги,
   схема. Рамка чуть заметнее остальных, чтобы взгляд находил её сразу. */
.lesson-key-card { border-color: color-mix(in srgb, var(--amber) 38%, transparent); }
.lesson-key-quote { margin-top: 14px; padding-top: 12px; border-top: 1px solid var(--border); }
/* Главная фраза урока: русская мысль → английская. Английский — крупнее всех,
   потому что это и есть то, чему учит занятие. */
.lesson-compare { text-align: center; padding: 2px 0 4px; }
.lesson-compare-ru { font-size: 15px; line-height: 1.5; color: var(--text-dim); font-style: italic; }
.lesson-compare-arrow { font-size: 13px; color: var(--text-dim); margin: 8px 0; }
.lesson-compare-en { font-size: 22px; font-weight: 700; line-height: 1.3; color: var(--text); }

/* «Обрати внимание» — подраздел внутри примеров, а не пятый заголовок урока. */
.lesson-notice { margin-top: 14px; padding-top: 12px; border-top: 1px solid var(--border); }
.lesson-section-title--sub { color: var(--accent); margin-bottom: 8px; }

/* Итоговая строка: урок закрывается фразой, а не обрывается на диалоге. */
.lesson-summary {
  display: flex;
  align-items: baseline;
  gap: 10px;
  margin-top: 14px;
  padding-top: 12px;
  border-top: 1px solid var(--border);
}
.lesson-summary-label { font-size: 11px; color: var(--text-dim); text-transform: uppercase; letter-spacing: 0.1em; flex-shrink: 0; }
.lesson-summary-en { font-size: 16px; font-weight: 600; line-height: 1.4; color: var(--accent); }

/* Схема — фигура внутри карточки «как это сказать», а не отдельная карточка:
   раньше она занимала 433px и оттесняла примеры на второй экран (владелец
   одобрил уменьшение 03.10.2026). */
.lesson-scheme {
  margin: 14px 0 0;
  padding-top: 12px;
  border-top: 1px solid var(--border);
  text-align: center;
}
.lesson-infographic-img { display: block; width: 100%; max-width: 300px; height: auto; margin: 0 auto 8px; border-radius: 10px; }
.lesson-scheme-caption { font-size: 13px; line-height: 1.55; color: var(--text-dim); text-align: left; }
/* Примеры — строки одного раздела, разделённые волоском, а не шесть карточек
   внутри карточки: вложенная рамка съедала экран и делала примеры похожими
   на шесть разных блоков. Английский крупнее русского: учим английский. */
.lesson-example { padding: 12px 0; border-top: 1px solid var(--border); }
.lesson-example:first-of-type { border-top: 0; padding-top: 2px; }
.lesson-example:last-of-type { padding-bottom: 0; }
.lesson-example-label { font-size: 11px; color: var(--text-dim); letter-spacing: 0.04em; margin-bottom: 6px; }
.lesson-ref { color: var(--accent); }
.lesson-example-en { font-size: 16px; font-weight: 600; line-height: 1.5; color: var(--text); margin-bottom: 5px; }
.lesson-example-ru { font-size: 13px; line-height: 1.5; color: var(--text-dim); font-style: italic; }

.lesson-contrast-line { font-size: 14px; line-height: 1.6; margin-bottom: 6px; color: var(--text); }
.lesson-contrast-en { font-weight: 600; }
.lesson-contrast-note { color: var(--text-dim); }

/* Мини-диалог: подпись говорящего — колонка фиксированной ширины. Было 44px,
   и слово «Собеседник» в неё не влезало: ломалось посреди строки реплики.
   Цветом отмечен второй голос; имя прежнего произведения не употребляется (D69). */
.lesson-dialogue-line { display: flex; gap: 10px; margin-bottom: 10px; font-size: 15px; line-height: 1.5; }
.lesson-dialogue-line:last-child { margin-bottom: 0; }
.lesson-dialogue-who { flex-shrink: 0; width: 80px; font-size: 12px; color: var(--text-dim); padding-top: 2px; }
.lesson-dialogue-line.friend .lesson-dialogue-text { color: var(--accent); }
.lesson-dialogue-text { color: var(--text); }

/* Карточки вопроса и поля ответа удалены (владелец, 03.10.2026: «у нас нет
   тестов и проверок»); правила .lesson-question-card и .lesson-chat-thread
   вычищены вместе с вёрсткой. */

.lesson-error { padding: 24px 16px; text-align: center; color: var(--text-dim); font-size: 14px; }

/* Coach-mark удалён в M5: подсказку про жесты читалки даёт гид-экскурсия
   (app/tour.js, шаг «Тапни по слову»). */

/* В плейлисте «текст песни» — та же ссылка, что и в сетке: стиль даёт
   .broadcast-slot-hint-inline, здесь только участие во flex-строке. */
.playlist-lyrics { flex: 0 0 auto; }

/* Караоке в тексте песни: активное слово крупнее и с подложкой,
   пропетые — полупрозрачные. */
.tscript-panel .reader-word.karaoke-active {
  opacity: 1;
  color: var(--accent);
  background: var(--accent-soft);
  border-radius: 3px;
}
.tscript-panel .reader-word.karaoke-past { opacity: 0.45; }

/* ===== Грамматическая подсветка групп слов (см. grammar.js) =====
   Цвет появляется ТОЛЬКО на время вспышки перевода (.translating-active),
   в покое текст обычный. Классы групп (см. grammar.js):
   зелёный  — глагол / глагольная цепь (включая aux и одиночные формы),
   синий    — существительное (с артиклем и без),
   янтарный — to-инфинитив. Цвета заданы переменными в :root / .theme-sepia.
   Специфика (0,3,0) выше, чем у .tscript-text .reader-word и у рамки
   вспышки, поэтому во время мигания группа перекрашивается целиком. */
.reader-word.translating-active.mh-g-verb { color: var(--g-verb); }
.reader-word.translating-active.mh-g-noun { color: var(--g-noun); }
.reader-word.translating-active.mh-g-toinf { color: var(--g-toinf); }
/* Подсветка выключена в настройках: даже во время вспышки цвет обычный
   (рамка и перевод-мигание остаются, но без перекраски группы). */
html.grammar-off .reader-word.translating-active.mh-g-verb,
html.grammar-off .reader-word.translating-active.mh-g-noun,
html.grammar-off .reader-word.translating-active.mh-g-toinf {
  color: inherit;
}
/* Одиночные слова по части речи — тоже подчиняются выключателю подсветки. */
html.grammar-off .reader-word.translating-active[class*="mh-flash-"] {
  color: inherit;
}

/* ===== Вспышка конструкции-цвета по части речи (POS flash) =====
   Класс mh-flash-<pos> ставится на слово при мигании в составе конструкции.
   Цвет проявляется ТОЛЬКО при .translating-active — в покое текст обычный. */
.reader-word.translating-active.mh-flash-noun { color: var(--flash-noun); }
.reader-word.translating-active.mh-flash-verb { color: var(--flash-verb); }
.reader-word.translating-active.mh-flash-pron { color: var(--flash-pron); }
.reader-word.translating-active.mh-flash-adj  { color: var(--flash-adj); }
.reader-word.translating-active.mh-flash-adv  { color: var(--flash-adv); }
.reader-word.translating-active.mh-flash-prep { color: var(--flash-prep); }
.reader-word.translating-active.mh-flash-conj { color: var(--flash-conj); }
.reader-word.translating-active.mh-flash-num  { color: var(--flash-num); }
.reader-word.translating-active.mh-flash-intj { color: var(--flash-intj); }

/* ===== M4 (07.09): нижний ряд «История·Радио·Полка·Прогресс» — новые экраны ===== */

/* ПОЛКА — утренние записки Марты (M1a) */
.shelf-hint {
  margin: 0 20px 12px;
  font-size: 12px;
  line-height: 1.5;
  color: var(--text-dim);
}
.shelf-lessons-btn {
  display: block;
  margin: 0 16px 14px;
  padding: 10px 16px;
  border: 1px solid color-mix(in srgb, var(--amber) 45%, transparent);
  border-radius: 12px;
  background: var(--accent-soft);
  color: var(--accent);
  font-size: 14px;
  font-weight: 600;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.shelf-lessons-btn:active { opacity: 0.75; }
.shelf-notes { padding: 0 16px var(--fab-stack-clearance); }
.shelf-note {
  margin-bottom: 14px;
  padding: 16px;
  border: 1px solid var(--border);
  border-left: 3px solid var(--accent);
  border-radius: 14px;
  background: var(--bg-elevated);
}
.shelf-note-date {
  display: block;
  margin-bottom: 8px;
  font-size: 11px;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--text-dim);
}
.shelf-note-text {
  font-size: 17px;
  line-height: 1.65;
  color: var(--text);
}
.shelf-empty {
  padding: 40px 24px;
  text-align: center;
  color: var(--text-dim);
  font-size: 14px;
  line-height: 1.6;
}

/* Сегодняшняя записка — та, что пришла утром: чуть заметнее. */
.shelf-note-today { border-left-color: var(--accent); }
.shelf-note-today .shelf-note-date { color: var(--accent); }

/* Слово дня: лемма + перевод, дальше 2–3 встречи в живых предложениях книги. */
.shelf-block {
  margin-top: 14px;
  padding-top: 12px;
  border-top: 1px solid var(--border);
}
.shelf-block:first-of-type { margin-top: 10px; }
.shelf-block-head {
  display: flex;
  align-items: baseline;
  gap: 8px;
  flex-wrap: wrap;
  margin-bottom: 8px;
}
.shelf-block-lemma {
  font-size: 19px;
  font-weight: 650;
  color: var(--text);
}
.shelf-block-ru {
  font-size: 14px;
  color: var(--text-dim);
}
.shelf-block-hint {
  margin-bottom: 8px;
  font-size: 12px;
  line-height: 1.45;
  color: var(--text-dim);
}
.shelf-sent { margin-bottom: 8px; }
.shelf-sent:last-child { margin-bottom: 0; }
.shelf-sent-en {
  font-size: 16px;
  line-height: 1.6;
  color: var(--text);
}
/* Жесты единые (M6): тап — перевод, удержание — озвучка. */
.shelf-word { cursor: pointer; border-radius: 3px; }
.shelf-word:active { background: var(--accent-soft); }

/* Плашка-подсказка скрывается, когда записок нет вовсе.
   Глобальный .hidden не вводим — см. комментарий у .shelf-empty.hidden. */
.shelf-hint.hidden { display: none; }

/* ПРОГРЕСС — три блока (контракт PROGRESS-TAB.md) */
.progress-stale {
  margin: 8px 20px 0;
  padding: 8px 12px;
  border-radius: 10px;
  background: var(--accent-soft);
  color: var(--accent);
  font-size: 12px;
  line-height: 1.4;
}
.progress-blocks { padding: 12px 16px var(--fab-stack-clearance); }
.progress-card {
  margin-bottom: 12px;
  padding: 18px 18px 16px;
  border: 0;
  border-radius: 18px;
  background: var(--bg-elevated);
  box-shadow: 0 1px 2px rgba(34, 31, 26, 0.05), 0 8px 24px rgba(34, 31, 26, 0.04);
}
.progress-card-title {
  margin-bottom: 10px;
  /* D200: единая шкала шрифтов прогресса. Заголовок — системный шрифт
     (засечные только на тексте книги), 16px/600. */
  font-family: var(--font-ui);
  font-size: 16px;
  font-weight: 600;
  line-height: 1.25;
  letter-spacing: 0;
  text-transform: none;
  color: var(--text);
}

.progress-card-note {
  margin-top: 10px;
  font-size: 12px;
  line-height: 1.5;
  color: var(--text-dim);
}

/* Прогресс: таймлайн дня — хронология действий из событий дневника. Строка
   «время + действие»: время прижато к левому краю одной колонкой, текст —
   рядом. Никаких оценок: только то, что человек реально делал и когда.
   D199 (владелец 08.10.2026): раздел СВЁРНУТ по умолчанию — «пусть раздел
   будет в свернутом виде по умолчанию если надо открыл посмотрел». */
.progress-timeline-toggle {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  cursor: pointer;
}
.progress-timeline-caret {
  flex-shrink: 0;
  color: var(--text-dim);
  font-size: 13px;
  line-height: 1;
  transition: transform 0.15s ease;
  transform: rotate(-90deg); /* свёрнут */
}
#progress-timeline-card.timeline-open .progress-timeline-caret { transform: none; }
.progress-timeline-body { display: none; }
#progress-timeline-card.timeline-open .progress-timeline-body { display: block; }
.progress-timeline {
  list-style: none;
  margin: 12px 0 0;
  padding: 0;
}
.progress-timeline-item {
  display: flex;
  align-items: baseline;
  gap: 12px;
  padding: 7px 0;
  border-bottom: 1px solid var(--border);
  font-size: 14px;
  line-height: 1.45;
}
.progress-timeline-item:last-child { border-bottom: 0; }
.progress-timeline-time {
  flex: none;
  font-size: 12px;
  letter-spacing: 0.02em;
  color: var(--amber);
  font-variant-numeric: tabular-nums;
}
.progress-timeline-text {
  color: var(--text);
}

/* Прогресс: факты дня без оценок (владелец 26.09: звёзды, баллы, шкалы и
   уровни удалены — экран читается как спокойный список чисел). */
.progress-facts {
  list-style: none;
  margin: 0;
  padding: 0;
}
.progress-fact {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 14px;
  padding: 12px 0;
  border-bottom: 1px solid var(--border);
  font-size: 14px;
  line-height: 1.5;
}
.progress-fact-value {
  flex-shrink: 0;
  /* D200: число факта — системный шрифт 17px/600 (засечные — только книга). */
  font-family: var(--font-ui);
  font-size: 17px;
  font-weight: 600;
  color: var(--text);
  text-align: right;
}
.progress-fact:first-child { padding-top: 2px; }
.progress-fact:last-child { border-bottom: none; padding-bottom: 2px; }
.progress-fact-label {
  color: var(--text-dim);
  line-height: 1.4;
}
/* Итог дня от ИИ — спокойный абзац, без рамок и оценок.
   D200: единый размер прозы прогресса — 15px/1.6. */
.progress-report-text {
  font-family: var(--font-serif);
  font-size: 15px;
  line-height: 1.6;
  color: var(--text);
}
/* ── ПРОГРЕСС: динамические блоки (отчёты, советы, «перечитать», сравнение) ──
   Эти узлы progress.js создаёт в рантайме, поэтому их классы обязаны быть
   описаны здесь — иначе получаются голые <button>/<div> во всю ширину и
   горизонтальный скролл (владелец 08.10.2026: «горизонтальный скролл — ужас»).
   Правило переполнения: любой текстовый узел умеет переноситься и сжиматься. */
#progress-blocks { overflow-x: clip; }
#progress-blocks > * { max-width: 100%; box-sizing: border-box; }

/* Кнопки-действия прогресса («Сформировать отчёт», «продолжить в книге»…) */
.progress-report-btn {
  display: block;
  width: 100%;
  margin: 0 0 12px;
  padding: 13px 16px;
  border: 0;
  border-radius: 16px;
  background: var(--accent);
  color: var(--on-accent);
  font-family: var(--font-ui);
  font-size: 15px;
  font-weight: 600;
  cursor: pointer;
}
.progress-report-btn:active { opacity: 0.88; }

/* Список прошлых отчётов: аккордеон «период → текст» */
.progress-report-item {
  border-top: 1px solid var(--border);
}
.progress-report-item:first-child { border-top: 0; }
.progress-report-item-head {
  display: block;
  width: 100%;
  padding: 13px 0;
  border: 0;
  background: transparent;
  color: var(--text);
  font-family: var(--font-ui);
  font-size: 15px;
  font-weight: 600;
  text-align: left;
  cursor: pointer;
  overflow-wrap: anywhere;
}
.progress-report-item-text {
  padding: 0 0 14px;
  font-family: var(--font-serif);
  font-size: 15px;
  line-height: 1.6;
  color: var(--text);
  overflow-wrap: anywhere;
}
.progress-report-item-text.hidden { display: none; }

/* Советы «Если захочется» */
.progress-advice-item {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 12px 0;
  border-top: 1px solid var(--border);
  min-width: 0;
}
.progress-advice-item:first-child { border-top: 0; padding-top: 2px; }
.progress-advice-text {
  font-family: var(--font-serif);
  font-size: 15px;
  line-height: 1.6;
  color: var(--text);
  min-width: 0;
  overflow-wrap: anywhere;
}
.progress-advice-link {
  align-self: flex-start;
  padding: 8px 14px;
  border: 1px solid var(--border);
  border-radius: 999px;
  background: transparent;
  color: var(--accent);
  font-family: var(--font-ui);
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
  max-width: 100%;
  overflow-wrap: anywhere;
}

/* Режим «перечитать». D200: проза прогресса — единый 15px/1.6. */
.progress-review-body {
  font-family: var(--font-serif);
  font-size: 15px;
  line-height: 1.6;
  color: var(--text);
  overflow-wrap: anywhere;
}
.progress-review-foot {
  display: flex;
  gap: 8px;
  margin-top: 14px;
  flex-wrap: wrap;
}
.progress-review-foot .progress-report-btn { width: auto; flex: 1 1 140px; margin: 0; }

/* Факты: сжимаем подписи, чтобы длинный текст не рвал строку вбок */
.progress-fact { overflow: hidden; }
.progress-fact-label { min-width: 0; overflow-wrap: anywhere; }
.progress-fact-value { max-width: 58%; overflow-wrap: anywhere; }
.progress-timeline-item { overflow: hidden; }
.progress-timeline-text { min-width: 0; overflow-wrap: anywhere; }

.progress-tour-btn {
  display: block;
  width: 100%;
  padding: 13px 16px;
  border: 1px solid var(--border);
  border-radius: 12px;
  background: transparent;
  color: var(--text-dim);
  font-size: 13px;
  font-family: var(--font-ui);
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.progress-tour-btn:active { background: var(--accent-soft); color: var(--accent); }

/* D200: «перечитать» — аудио-эпизод и сообщение из чата. Классы создаёт
   progress.js, стилей у них не было — блоки выходили голыми div/p. */
.progress-review-audio {
  display: flex;
  flex-direction: column;
  gap: 10px;
  min-width: 0;
}
.progress-review-audio-title {
  font-family: var(--font-ui);
  font-size: 13px;
  font-weight: 600;
  line-height: 1.4;
  color: var(--text-dim);
}
.progress-review-audio audio {
  width: 100%;
  max-width: 100%;
}
.progress-review-city {
  display: flex;
  flex-direction: column;
  gap: 6px;
  min-width: 0;
}
.progress-review-city-author {
  font-family: var(--font-ui);
  font-size: 13px;
  font-weight: 600;
  line-height: 1.4;
  color: var(--text-dim);
}
.progress-review-city-text {
  margin: 0;
  font-family: var(--font-serif);
  font-size: 15px;
  line-height: 1.6;
  color: var(--text);
  overflow-wrap: anywhere;
}

/* D202: плитки фактов дня и график роста (владелец 09.10.2026: «в прогрессе
   даем общее время чтения общее количество просмотренных слов время слушания
   радио песен и подкастов реплик в чат, прочитанных занятий … все больше
   ничего», «у нас прогресс слишком скучно оформлен пожалуйста»). */
.progress-tiles {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 10px;
}
.progress-tile {
  padding: 14px;
  border: 1px solid var(--border);
  border-radius: 14px;
  background: var(--bg);
  min-width: 0;
}
/* Первая плитка — время чтения — во всю ширину: это главный факт дня. */
.progress-tile:first-child { grid-column: 1 / -1; }
.progress-tile-value {
  font-family: var(--font-ui);
  font-size: 22px;
  font-weight: 600;
  line-height: 1.1;
  color: var(--accent);
  font-variant-numeric: tabular-nums;
  overflow-wrap: anywhere;
}
.progress-tile:first-child .progress-tile-value { font-size: 28px; }
.progress-tile-label {
  margin-top: 4px;
  font-family: var(--font-ui);
  font-size: 12px;
  line-height: 1.35;
  color: var(--text-dim);
}

/* D202: график роста — сразу под фактами. Шкала оси спрятана намеренно:
   показатель роста тайный («пусть он не знает о том как считается»). */
.progress-chart-wrap {
  margin-top: 12px;
}
.progress-chart-svg {
  display: block;
  width: 100%;
  height: 170px;
}
.progress-chart-svg .chart-line {
  fill: none;
  stroke: var(--accent);
  stroke-width: 2;
  stroke-linejoin: round;
  stroke-linecap: round;
}
.progress-chart-svg .chart-area { fill: url(#chart-gold); }
.progress-chart-svg .chart-stop { stop-color: var(--accent); }
.progress-chart-svg .chart-grid {
  stroke: var(--border);
  stroke-width: 1;
}
.progress-chart-svg .chart-dot {
  fill: var(--accent);
  stroke: var(--bg);
  stroke-width: 2;
}
.progress-chart-svg .chart-halo {
  fill: var(--accent);
  opacity: 0.2;
}
.progress-chart-svg .chart-label {
  fill: var(--text-dim);
  font-family: var(--font-ui);
  font-size: 11px;
}
.progress-chart-range {
  display: flex;
  gap: 8px;
  margin-top: 12px;
}
.progress-chart-range button {
  flex: 1;
  min-height: 42px;
  border: 1px solid var(--border);
  border-radius: 12px;
  background: transparent;
  color: var(--text-dim);
  font-family: var(--font-ui);
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.progress-chart-range button.active {
  background: var(--accent-soft);
  border-color: var(--accent);
  color: var(--accent);
}
.progress-chart-note {
  margin-top: 10px;
}

/* M4: скрытие пустого состояния Полки и плашки «данные вчерашние».
   Локальные .hidden (глобального класса .hidden в проекте нет).

   ДОДЕЛКА 28.09.2026 (владелец: «вкладка Прогресс — там ничего не работает»).
   Причина была именно здесь: экран «Прогресса» ставит класс hidden из js
   (Итог дня, режим «перечитать», советы, «тогда и сейчас», отчёты, заметки,
   кнопки, правила подписки), а правил для их классов не было — поэтому
   пустые рамки стояли на экране вечно и выглядели как сломанная вкладка.
   Нижние четыре строки и есть вся починка; остальные .hidden-правила проекта
   точечные и живут рядом со своими классами. */
.shelf-empty.hidden,
.progress-stale.hidden,
.progress-card.hidden,
.progress-card-note.hidden,
.progress-report-btn.hidden,
.progress-tour-btn.hidden { display: none; }

/* ===== SPLASH (M5) — брендовая заставка на каждый cold start =====
   Белый фон, «InStory» чёрным Georgia (тот же шрифт, что в читалке —
   системный, без CDN), буквы выплывают по одной снизу вверх, под словом —
   тонкий индикатор загрузки (полоска бегает, пока грузятся данные).
   Названия чужих вселенных нет: InStory не привязан к чужим мирам.
   Живёт ~2.4 с: fade-in 300 мс → пауза → fade-out 400 мс; данные грузятся
   под ней параллельно. Поверх всего (z-index 300 > overlay 200 > auth 100) —
   иначе всплывёт оглавление. */
#splash {
  position: absolute;
  inset: 0;
  z-index: 300;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 26px;
  background: #FFFFFF;
  /* pointer-events: заставка закрывает приложение на свою пару секунд —
     тапы не должны «проваливаться» в оглавление под ней. */
  pointer-events: auto;
  animation: splash-life 2.4s ease forwards;
}
.splash-word {
  font-family: Georgia, 'Iowan Old Style', 'Times New Roman', serif;
  font-size: clamp(44px, 12vw, 72px);
  letter-spacing: 0.06em;
  color: #000;
  line-height: 1;
  /* небольшой сдвиг вправо: letter-spacing добавляет хвостовой пробел,
     без него слово выглядит нецентрированным */
  text-indent: 0.06em;
  display: inline-block;
}
.splash-word span {
  display: inline-block;
  opacity: 0;
  transform: translateY(16px);
  animation: splash-letter 0.62s cubic-bezier(0.22, 0.9, 0.28, 1) forwards;
}
@keyframes splash-letter {
  0%   { opacity: 0; transform: translateY(16px); }
  60%  { opacity: 1; }
  100% { opacity: 1; transform: translateY(0); }
}
/* Индикатор загрузки: узкий жёлоб, внутри полоска ездит туда-сюда. */
.splash-loader {
  width: 92px;
  height: 2px;
  border-radius: 2px;
  background: rgba(0, 0, 0, 0.09);
  overflow: hidden;
  opacity: 0;
  animation: splash-fadein 0.5s ease 0.62s forwards;
}
.splash-loader i {
  display: block;
  width: 38%;
  height: 100%;
  border-radius: 2px;
  background: #000;
  animation: splash-slide 1.15s cubic-bezier(0.5, 0, 0.5, 1) 0.62s infinite;
}
@keyframes splash-slide {
  0%   { transform: translateX(-110%); }
  100% { transform: translateX(270%); }
}
@keyframes splash-fadein {
  to { opacity: 1; }
}
/* Конечный кадр — полностью прозрачно; класс .gone (ставит JS по animationend
   и/или по страховочному таймеру) убирает элемент из потока, чтобы после
   заставки не осталось белого «дырового» кадра поверх приложения. */
@keyframes splash-life {
  0%   { opacity: 0; }
  12%  { opacity: 1; }   /* ~300мс fade-in */
  83%  { opacity: 1; }   /* пауза — слово и загрузчик видны ~1.7 с */
  100% { opacity: 0; }   /* ~400мс fade-out */
}
#splash.gone { display: none; }

@media (prefers-reduced-motion: reduce) {
  /* Меньше движения: буквы сразу на месте, полоска не ездит, но заставка
     всё же видна и уходит плавно. */
  #splash { animation-duration: 1s; }
  .splash-word span { opacity: 1; transform: none; animation: none; }
  .splash-loader { opacity: 1; animation: none; }
  .splash-loader i { animation: none; width: 100%; }
}

/* ===== ГИД-ЭКСКУРСИЯ (M5, app/tour.js) =====
   Затемнение — четыре панели вокруг «дырки» (не box-shadow): панели сами
   перехватывают тапы, поэтому промах НЕ проваливается в приложение, а в
   дырке клики доходят до настоящей цели (шаги-действия).
   Слои: splash 300 > чип 250 > карточка 245 > вуаль 240 > settings 200 >
   auth 100 > FAB 30. */
#tour-veil {
  position: absolute;
  inset: 0;
  z-index: 240;
  pointer-events: none;   /* перехватывают только сами панели */
}
#tour-veil.hidden { display: none; }
.tour-dim {
  position: absolute;
  background: rgba(6, 7, 9, 0.68);
  pointer-events: auto;
  transition: all 0.18s ease-out;
}
.tour-ring {
  position: absolute;
  border: 2px solid var(--accent);
  border-radius: 12px;
  pointer-events: none;   /* кольцо — только подсветка, клики идут цели */
  box-shadow: 0 0 0 1px rgba(216, 166, 87, 0.35), 0 0 18px rgba(216, 166, 87, 0.35);
  animation: tour-ring-pulse 1.6s ease-in-out infinite;
  transition: all 0.18s ease-out;
}
.tour-ring.hidden { display: none; }
@keyframes tour-ring-pulse {
  0%, 100% { box-shadow: 0 0 0 1px rgba(216, 166, 87, 0.35), 0 0 12px rgba(216, 166, 87, 0.3); }
  50%      { box-shadow: 0 0 0 3px rgba(216, 166, 87, 0.28), 0 0 22px rgba(216, 166, 87, 0.45); }
}

#tour-card {
  position: absolute;
  z-index: 245;
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: 16px;
  padding: 16px 16px 12px;
  box-shadow: 0 12px 32px rgba(0, 0, 0, 0.45);
  max-width: 320px;
}
#tour-card.hidden { display: none; }
.tour-text {
  font-size: 17px;          /* приёмка: шрифт карточки ≥ 16px */
  line-height: 1.45;
  color: var(--text);
  margin-right: 24px;       /* место под крестик */
}
.tour-row {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-top: 12px;
}
.tour-dots {
  flex: 1;
  font-size: 11px;
  letter-spacing: 2px;
  color: var(--text-dim);
  overflow: hidden;
  white-space: nowrap;
}
.tour-next, .tour-skip, .tour-close, .tour-chip-skip {
  font-family: inherit;
  border: none;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.tour-next {
  min-height: 40px;
  padding: 8px 16px;
  border-radius: 12px;
  background: var(--accent);
  color: #1a1408;
  font-size: 15px;
  font-weight: 600;
}
.tour-skip, .tour-chip-skip {
  min-height: 40px;
  padding: 8px 12px;
  border-radius: 12px;
  background: transparent;
  color: var(--text-dim);
  font-size: 14px;
}
.tour-skip:active, .tour-chip-skip:active { background: var(--accent-soft); color: var(--accent); }
.tour-close {
  position: absolute;
  top: 8px;
  right: 8px;
  width: 32px;
  height: 32px;
  border-radius: 50%;
  background: transparent;
  color: var(--text-dim);
  font-size: 15px;
  line-height: 1;
}
.tour-close:active { background: var(--accent-soft); color: var(--accent); }



/* Пока идёт гид, не даём нижнему ряду и FAB ловить случайные жесты мимо
   дырки — за это отвечают панели вуали, а курсор подсказывает кликабельность. */
body.tour-on .tab-btn { cursor: pointer; }

@media (prefers-reduced-motion: reduce) {
  .tour-ring { animation: none; }
  .tour-dim, .tour-ring { transition: none; }
}

/* ===== Фаза 8.7b: правила города, меню реплики, жалобы, «на проверке» ===== */
/* Решение Сергея №12: модерация «по-взрослому». Панели наследуют вид участников
   (--bg-elevated, рамка --border, радиус 12px). Точки скрытия локальные:
   глобального .hidden в проекте нет, поэтому добавляем .<класс>.hidden. */

/* Кнопка «Правила» в шапке ленты — рядом с переключателем участников */
.chat-rules-toggle {
  flex-shrink: 0;
  align-self: center;
  margin: 4px 8px 4px 0;
  padding: 5px 11px;
  font-size: 12px;
  color: var(--accent);
  background: var(--accent-soft);
  border: 1px solid var(--accent-soft);
  border-radius: 999px;
  cursor: pointer;
}
.chat-rules-toggle:active { background: var(--accent); color: var(--on-accent); }

/* Панель правил (та же «карточка», что и у участников) */
.chat-rules-panel {
  position: relative;
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: 12px;
  max-width: 380px;
  width: 92%;
  max-height: 82vh;
  display: flex;
  flex-direction: column;
  box-shadow: 0 8px 32px rgba(0,0,0,0.5);
}
.chat-rules-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 14px 16px 8px;
}
.chat-rules-title { margin: 0; font-size: 17px; }
.chat-rules-close {
  background: none;
  border: none;
  color: var(--text-dim);
  font-size: 16px;
  cursor: pointer;
  padding: 4px 8px;
}
.chat-rules-close.hidden { display: none; }
.chat-rules-intro {
  padding: 0 16px 10px;
  font-size: 13px;
  line-height: 1.5;
  color: var(--text-dim);
  border-bottom: 1px solid var(--border);
}
.chat-rules-list { overflow-y: auto; padding: 8px 16px 4px; }
.chat-rule { padding: 8px 0; border-bottom: 1px solid var(--border); }
.chat-rule:last-child { border-bottom: none; }
.chat-rule-title { font-size: 14px; font-weight: 600; color: var(--text); margin-bottom: 3px; }
.chat-rule-text { font-size: 13px; line-height: 1.5; color: var(--text-dim); }
.chat-rules-daily {
  padding: 10px 16px;
  font-size: 12px;
  line-height: 1.5;
  color: var(--accent);
  border-top: 1px solid var(--border);
}
.chat-rules-daily.hidden { display: none; }
.chat-rules-accept {
  margin: 8px 16px 14px;
  padding: 11px 16px;
  font-size: 14px;
  font-weight: 600;
  color: var(--on-accent);
  background: var(--accent);
  border: none;
  border-radius: 10px;
  cursor: pointer;
}
.chat-rules-accept:active { background: #c2944c; }

/* Плашка «на проверке» (премодерация) — приглушённый пузырёк с пояснением */
.chat-msg-pending .chat-bubble {
  opacity: 0.55;
  border-style: dashed;
}
.chat-pending-note {
  margin-top: 4px;
  font-size: 11px;
  color: var(--accent);
  font-style: italic;
}


/* ===== Фаза 8.8: закрытая форма «Предложения» ===== */
/* Решение Сергея №18: обратная связь — отдельная закрытая форма, НЕ лента
   города. Тот же UI-кит, что у правил и жалоб (--bg-elevated, рамка --border,
   радиус 12px, голова .chat-rules-head), чтобы не плодить второй стиль.
   Точки скрытия локальные: глобального .hidden в проекте нет. */

/* Кнопка «Предложения» в шапке ленты — рядом с «Правилами» */
.chat-feedback-toggle {
  flex-shrink: 0;
  align-self: center;
  margin: 4px 8px 4px 0;
  padding: 5px 11px;
  font-size: 12px;
  color: var(--accent);
  background: var(--accent-soft);
  border: 1px solid var(--accent-soft);
  border-radius: 999px;
  cursor: pointer;
}
.chat-feedback-toggle:active { background: var(--accent); color: var(--on-accent); }

.chat-feedback-panel {
  position: relative;
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: 12px;
  max-width: 380px;
  width: 92%;
  max-height: 86vh;
  display: flex;
  flex-direction: column;
  box-shadow: 0 8px 32px rgba(0,0,0,0.5);
}
.chat-feedback-intro {
  padding: 0 16px 10px;
  font-size: 13px;
  line-height: 1.5;
  color: var(--text-dim);
  border-bottom: 1px solid var(--border);
}
.chat-feedback-body {
  overflow-y: auto;
  padding: 10px 16px 4px;
  display: flex;
  flex-direction: column;
  gap: 7px;
}
/* Переключатели типа обращения: предложение / сломалось / вопрос */
.chat-feedback-kinds { display: flex; flex-wrap: wrap; gap: 6px; }
.chat-feedback-kind {
  padding: 6px 11px;
  font-size: 12px;
  color: var(--text-dim);
  background: var(--bg-sheet);
  border: 1px solid var(--border);
  border-radius: 999px;
  cursor: pointer;
}
.chat-feedback-kind-on {
  color: var(--accent);
  background: var(--accent-soft);
  border-color: var(--accent-soft);
}
.chat-feedback-kind:active { background: var(--accent-soft); }
.chat-feedback-label { font-size: 12px; color: var(--text-dim); }
.chat-feedback-text,
.chat-feedback-contact {
  width: 100%;
  box-sizing: border-box;
  padding: 9px 11px;
  font-size: 14px;
  font-family: inherit;
  color: var(--text);
  background: var(--bg-sheet);
  border: 1px solid var(--border);
  border-radius: 8px;
  resize: vertical;
}
.chat-feedback-text:focus,
.chat-feedback-contact:focus { outline: none; border-color: var(--accent-soft); }
.chat-feedback-hint { font-size: 11px; color: var(--text-dim); opacity: 0.75; }
.chat-feedback-hint.hidden { display: none; }
.chat-feedback-note {
  padding: 0 16px 8px;
  font-size: 13px;
  line-height: 1.4;
  color: var(--success);
}
.chat-feedback-note.hidden { display: none; }
/* Ответ backend 400 («Пустое сообщение», «Слишком длинно…») — тот же красный,
   что и у ошибки входа (.auth-error), без новой переменной палитры. */
.chat-feedback-note-error { color: #e07a6a; }
.chat-feedback-submit {
  margin: 8px 16px 14px;
  padding: 12px;
  border: none;
  background: var(--accent);
  color: var(--on-accent);
  border-radius: 10px;
  font-size: 15px;
  font-weight: 600;
  cursor: pointer;
}
.chat-feedback-submit:active { background: #c2944c; }
.chat-feedback-submit[disabled] { opacity: 0.45; cursor: default; }
/* Кнопка внутри правила «О приложении — не в город» (rule.action_ru из данных) */
.chat-rule-action {
  margin-top: 7px;
  padding: 7px 12px;
  font-size: 13px;
  color: var(--accent);
  background: var(--accent-soft);
  border: 1px solid var(--accent-soft);
  border-radius: 8px;
  cursor: pointer;
}
.chat-rule-action:active { background: var(--accent); color: var(--on-accent); }


/* ─── «Как это работает»: тихая справка на каждой вкладке (28.09) ───────── */
/* Тихий знак «?» в шапке раздела и одна нижняя шторка на все вкладки.
   Правило скрытия: у каждого класса со .hidden своё display:none — глобального
   .hidden в проекте нет (его отсутствие держит tests/test_progress_hidden_client.py). */
.screen-title-row {
  display: flex; align-items: flex-end; justify-content: space-between; gap: 8px;
  padding-right: 16px;
}
.screen-title-row .screen-title { margin: 0; }
#reader-location { position: relative; }
#reader-location .tab-help-btn { position: absolute; top: 16px; right: 14px; }
.tab-help-btn {
  flex-shrink: 0; margin-left: auto; /* без заголовка в строке — прижат вправо */
  width: 28px; height: 28px; border-radius: 50%;
  border: 1px solid var(--border); background: transparent;
  color: var(--text-dim); font-size: 15px; line-height: 1; cursor: pointer;
}
.tab-help-btn:active { color: var(--accent); border-color: var(--accent-soft); }
.tab-help {
  position: fixed; inset: 0; z-index: 60;
  display: flex; align-items: flex-end; justify-content: center;
}
.tab-help.hidden { display: none; }
.tab-help-backdrop {
  position: absolute; inset: 0; background: rgba(9, 10, 12, 0.45);
}
.tab-help-sheet {
  position: relative; width: 100%; max-width: 560px;
  background: var(--bg-sheet); border-radius: 18px 18px 0 0;
  border-top: 1px solid var(--border);
  padding: 18px 18px calc(18px + env(safe-area-inset-bottom));
}
.tab-help-title { margin: 0 0 10px; font-size: 18px; color: var(--text); }
.tab-help-text { margin: 0 0 14px; line-height: 1.5; color: var(--text-dim); }
.tab-help-text.hidden { display: none; }
.tab-help-ok {
  width: 100%; padding: 12px; border-radius: 12px; border: 0;
  background: var(--accent); color: var(--on-accent); font-size: 15px; cursor: pointer;
}

/* ─── Порог: условия суток для гостя (решение владельца 29.09.2026) ───────────
   Карточка внизу экрана, а не стена: человек пришёл читать и читает сразу, а
   условия суток — короткая честная строка под рукой. Показывается один раз
   (локальный флаг в app/chat.js), уходит по «Понятно» и не возвращается.
   Живёт над полосой вкладок, чтобы не закрывать навигацию. */
.guest-note {
  position: absolute;
  left: 12px;
  right: 12px;
  bottom: calc(76px + var(--safe-bottom));
  z-index: 240;
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 14px 16px;
  box-sizing: border-box;
  background: var(--bg-sheet);
  border: 1px solid var(--border);
  border-radius: 16px;
  box-shadow: 0 12px 32px rgba(0, 0, 0, 0.35);
  text-align: left;
}
.guest-note.hidden { display: none; }
/* Фаза 2 (находка проверки): на экране чата карточка условий суток лежала
   ПОВЕРХ поля ввода и кнопки отправки — гость не мог написать Кейт, пока не
   закроет карточку (elementFromPoint в центре поля возвращал .guest-note-actions).
   Теперь на активном чате карточка встаёт НАД полосой ввода: 62px полоса вкладок
   + 65px полоса ввода + 8px воздух = 135px. На остальных экранах (где полосы
   ввода нет) прежний зазор 76px — карточка висит так же низко, ничего не
   перекрывая. */
#screen-chat.active ~ #guest-note {
  bottom: calc(135px + var(--safe-bottom));
}
.guest-note-text {
  font-size: 15px;
  line-height: 1.4;
  color: var(--text);
}
.guest-note-actions {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}
.guest-note-ok {
  min-height: 40px;
  padding: 0 18px;
  border: 1px solid var(--accent);
  border-radius: 20px;
  background: transparent;
  color: var(--accent);
  font-size: 15px;
  cursor: pointer;
}
/* Тихая дверь для тех, у кого аккаунт уже есть: не кнопка наравне с «Понятно»,
   а именно ссылка — приглашать к аккаунту на пороге мы не хотим. */
.guest-note-link {
  background: none;
  border: 0;
  padding: 0;
  color: var(--text-dim);
  font-size: 13px;
  text-decoration: underline;
  cursor: pointer;
}
