:root {
  /* Design system — раунд 14 (ТЗ_ВИЗУАЛ_ПОД_ОБЛОЖКУ.md): «сепия/дерево под
     обложку» — тёплое тёмное дерево, пергамент, бронза и золото; тревожный
     акцент — приглушённый красно-оранжевый. Никаких насыщенных синих/
     фиолетовых (прежний «вариант 2» в духе Clash Royale, раунд 4b, ушёл в
     платную тему body.theme-blue-red ниже). Применяется только к DOM-
     интерфейсу; canvas поля боя красится из AGES/ART в data.js. Имена
     переменных (royal/cta/secondary/alert) сохранены, чтобы тема из
     магазина продолжала переопределять их без правок разметки. */
  --ink: #1a120a;
  --paper: #f3e7c8;
  --r-lg: 20px;
  --r-xl: 28px;

  --royal-1: #7a5636;
  --royal-2: #4a3220;
  --royal-3: #2a1a0e;

  --cta-1: #ffe08a;
  --cta-2: #e0a030;
  --cta-shadow: #7a4a10;

  --secondary-1: #8a6a48;
  --secondary-2: #5a4028;
  --secondary-shadow: #2e1e10;

  --alert-1: #ff8a5c;
  --alert-2: #c8401e;
  --alert-shadow: #6a1e0a;

  --gold-text: #ffd77a;

  /* Древесное зерно — накладывается поверх градиента панелей/кнопок
     (несколько фонов через запятую); бронзовая рамка панелей. */
  --grain: repeating-linear-gradient(90deg, rgba(0,0,0,.07) 0 2px, rgba(255,225,180,.035) 2px 3px, transparent 3px 6px);
  --bronze-dark: #4a3018;
  --edge-light: rgba(255,225,180,.16);
}

* { box-sizing: border-box; }
html, body {
  margin: 0; padding: 0;
  /* Утро (баг-репорт основателя с реального телефона, скриншоты меню и
     карты глав): 100% от html/body мобильные браузеры часто считают по
     "большому" вьюпорту, как будто адресной строки нет, даже когда она
     реально видна и съедает часть экрана — контент выше/ниже границы
     обрезается без скролла. 100dvh — актуальная видимая высота, с 100%
     как фоллбэком для браузеров без поддержки dvh. */
  height: 100%; height: 100dvh;
  background: #15100b; color: var(--paper);
  font-family: 'Fredoka', 'Segoe UI', Tahoma, Verdana, sans-serif;
  overflow: hidden;
  -webkit-tap-highlight-color: transparent;
  /* Модерация (2026-09-14, замечания 6-7, п.1.6.2.7/1.6.1.8): раньше
     user-select:none стоял только на .arena — клик/долгий тач ЛЮБОГО
     другого места интерфейса (HUD, магазин, попапы) выделял текст на
     десктопе и открывал iOS-коллаут "Скопировать/Найти" с ручками
     выделения. Нет ни одного текстового поля в игре (см. index.html —
     инпутов нет), так что выделение текста нигде не нужно функционально.
     -webkit-user-select — для Safari/iOS, обычный user-select — для
     остальных. -webkit-touch-callout — отдельное от user-select свойство
     именно для iOS-коллаута по долгому тапу, без него один user-select не
     убирает меню "Скопировать" на iPhone/iPad. */
  user-select: none;
  -webkit-user-select: none;
  -webkit-touch-callout: none;
}
#stage {
  height: 100%; display: flex; align-items: center; justify-content: center;
  position: relative;
}
/* Раунд 15 (И1, ТЗ_КАЧЕСТВО_CRAZYGAMES, П1): кадр на весь экран. Раньше
   #arenaWrap держал аспект 2.5:1 (width: min(100%, 250dvh)), а полосы
   сверху/снизу закрашивал отдельный #backdrop своим генератором — на 16:9
   и в iframe CrazyGames это читалось как «игра в полосе» со швом. Теперь
   канвас боя, HUD и тач-управление занимают весь вьюпорт, а вписывание
   мира делает камера в game.js (resizeCanvas/applyViewTransform): вся
   ширина арены в кадре, по высоте — продолжение неба и земли. Логические
   координаты мира не меняются. #backdrop удалён. */
#arenaWrap {
  position: absolute; inset: 0; z-index: 1;
  background: #15100b;
  user-select: none;
}

/* Игра — альбомная по решению ГДД (см. КОНЦЕПТ_ГДД.md, «Платформа и
   стек»); в портретной ориентации на телефоне арена по аспекту 2.5:1
   схлопывается до нескольких десятков пикселей и ломает все экраны —
   вместо того чтобы пытаться уместить ландшафтную игру в портрет,
   честно просим повернуть устройство (типовой паттерн жанра). */
#rotatePrompt { display: none; }
@media (pointer: coarse) and (orientation: portrait) {
  #stage { display: none; }
  #rotatePrompt {
    display: flex; height: 100%; flex-direction: column; align-items: center;
    justify-content: center; gap: 1em; text-align: center; padding: 2em;
    background: #15100b; color: var(--paper);
  }
  #rotatePrompt .icon { font-size: 3em; }
}
canvas#game {
  width: 100%; height: 100%; display: block;
  image-rendering: -webkit-optimize-contrast;
}

.hidden { display: none !important; }

@keyframes bounceIn {
  0% { transform: scale(.6) rotate(-3deg); opacity: 0; }
  50% { transform: scale(1.08) rotate(1.5deg); opacity: 1; }
  75% { transform: scale(.95) rotate(-.5deg); }
  100% { transform: scale(1) rotate(0); }
}
@keyframes popIn {
  0% { transform: scale(.85); opacity: 0; }
  100% { transform: scale(1); opacity: 1; }
}
@keyframes glowPulse {
  0%,100% { box-shadow: 0 0 22px 2px rgba(255,215,122,.55), 0 14px 34px rgba(0,0,0,.55); }
  50% { box-shadow: 0 0 34px 8px rgba(255,215,122,.8), 0 14px 34px rgba(0,0,0,.55); }
}
@keyframes alertShake {
  0%,100% { transform: translateX(0) rotate(0); }
  20% { transform: translateX(-4px) rotate(-1deg); }
  40% { transform: translateX(4px) rotate(1deg); }
  60% { transform: translateX(-3px) rotate(-.6deg); }
  80% { transform: translateX(3px) rotate(.6deg); }
}

/* ---------- Кнопки: толстая тёмная обводка, объём, пружинящий hover ---------- */
.menu-btn, .tool-btn, .icon-btn, .si-buy, .touch-btn {
  border: 3px solid var(--ink);
  word-spacing: .16em; font-family: 'Lilita One', 'Fredoka', sans-serif;
  font-weight: 400;
  letter-spacing: .02em;
  cursor: pointer;
  position: relative;
  transition: transform .22s cubic-bezier(.34,1.56,.64,1), box-shadow .18s ease, filter .12s ease;
  -webkit-tap-highlight-color: transparent;
}
.menu-btn, .tool-btn, .icon-btn, .si-buy {
  border-radius: var(--r-lg);
}
/* Мягкий блик сверху — как отполированная бронза/лакированное дерево
   (раунд 14: приглушён относительно «гача»-глянца раунда 4b). */
.menu-btn::before, .si-buy::before {
  content: ''; position: absolute; left: 6%; right: 6%; top: 8%; height: 32%;
  background: linear-gradient(rgba(255,245,220,.42), rgba(255,245,220,0));
  border-radius: 999px; pointer-events: none;
}
/* Утро (баг-репорт основателя с телефона): :hover на боевых кнопках без
   media-запроса — классический источник "первый тап уходит в hover,
   второй в click" на некоторых мобильных браузерах (даже без JS
   mouseenter-слушателей, которые уже убраны из buildToolbar/способностей
   выше). Ограничиваем hover устройствами, которые реально умеют наводить
   мышью, а не эмулируют это из тача. */
@media (hover: hover) and (pointer: fine) {
  .menu-btn:hover:not(:disabled), .tool-btn:hover:not(.disabled), .icon-btn:hover,
  .si-buy:hover:not(:disabled):not(.owned), .touch-btn:hover {
    transform: translateY(-2px) scale(1.07);
    filter: brightness(1.07);
  }
}
.menu-btn:active:not(:disabled), .tool-btn:active:not(.disabled), .icon-btn:active,
.si-buy:active:not(:disabled):not(.owned), .touch-btn:active {
  transform: translateY(4px) scale(.95);
  filter: brightness(.95);
  transition-duration: .05s;
}

.menu-btn {
  min-height: 48px;
  font-size: 1.15em; padding: .6em 2em;
  background: linear-gradient(var(--cta-1), var(--cta-2));
  color: #4a2c00;
  text-shadow: 0 1px 0 rgba(255,255,255,.4);
  box-shadow: inset 0 -2px 0 rgba(0,0,0,.18), 0 6px 0 0 var(--cta-shadow), 0 10px 18px rgba(0,0,0,.45);
}
.menu-btn:active:not(:disabled) { box-shadow: inset 0 -2px 0 rgba(0,0,0,.18), 0 2px 0 0 var(--cta-shadow), 0 4px 8px rgba(0,0,0,.35); }
/* Утро (баг-репорт с телефона): "Играть" была МЕНЬШЕ тумблеров звука —
   иерархия читалась наоборот тому, что нужно. "Играть" — главное
   действие, крупнее всех остальных кнопок меню. */
.menu-btn.primary-big { font-size: 1.55em; min-height: 60px; padding: .55em 2.4em; }
.menu-btn.secondary {
  background: var(--grain), linear-gradient(var(--secondary-1), var(--secondary-2));
  color: var(--paper); font-weight: 400; font-size: 1em; text-shadow: 0 1px 2px rgba(0,0,0,.5);
  box-shadow: inset 0 1px 0 var(--edge-light), inset 0 -2px 0 rgba(0,0,0,.25), 0 6px 0 0 var(--secondary-shadow), 0 10px 18px rgba(0,0,0,.4);
}
.menu-btn.secondary:active:not(:disabled) { box-shadow: inset 0 1px 0 var(--edge-light), 0 2px 0 0 var(--secondary-shadow), 0 4px 8px rgba(0,0,0,.3); }

/* ---------- HUD ---------- */
/* Раунд 15 (И1): масштаб интерфейса — от высоты вьюпорта (без потолка
   15px, из-за которого на 1080p HUD был мелким), тонкие полосы HP без
   деревянных плашек, золото крупно над панелью покупки. */
#hud {
  position: absolute; inset: 0; pointer-events: none;
  display: flex; flex-direction: column; justify-content: space-between;
  /* r15 И10 (куратор: на 800×450 цифры HP/цены/подписи 7–10 px): база HUD
     не мельче 13px — любые .85em-подписи внутри остаются ≥12px. */
  font-size: clamp(13px, 2.6vh, 24px);
}
.hud-top {
  display: flex; align-items: flex-start; justify-content: space-between;
  padding: .55em .8em;
  gap: .8em;
  position: relative; /* точка отсчёта для .hero-revive-box, см. ниже */
}
.core-box { width: 27%; min-width: 9em; pointer-events: auto; }
.core-head {
  display: flex; align-items: center; gap: .35em; line-height: 1.1; margin-bottom: .18em;
  word-spacing: .16em; font-family: 'Lilita One', 'Fredoka', sans-serif; font-size: max(13px, .9em); white-space: nowrap;
  color: #fff; text-shadow: 0 1px 0 var(--ink), 0 0 3px rgba(0,0,0,.7), 1px 0 0 var(--ink), -1px 0 0 var(--ink);
}
.core-enemy .core-head { justify-content: flex-end; }
.core-flag { width: .8em; height: .8em; border-radius: 2px; border: 1.5px solid var(--ink); background: #3fae6b; flex-shrink: 0; }
.core-enemy .core-flag { background: #d8402a; }
.core-label { letter-spacing: .08em; opacity: .95; }
.core-enemy { text-align: right; }
.bar { position: relative; height: .62em; background: rgba(26,18,10,.55); border: 2px solid var(--ink); border-radius: 999px; overflow: hidden; box-shadow: 0 1px 3px rgba(0,0,0,.35); }
.bar-fill { position: absolute; top:0; left:0; bottom:0; width: 100%; transition: width .15s ease-out; border-radius: 999px; }
.bar-hp-player .bar-fill { background: linear-gradient(#9af0a4,#35a552); left:0; }
.bar-hp-enemy .bar-fill { background: linear-gradient(#ff9a7c,#d03a1e); right:0; left:auto; }
.core-enemy .bar-fill { left: auto; right: 0; }
.bar-text { opacity: .95; }
/* Золото — главный ресурс боя: крупно, прямо над кнопками покупки. */
.gold-row {
  display:flex; align-items:center; justify-content: center; gap:.3em; padding: .05em .6em;
  word-spacing: .16em; font-family: 'Lilita One', sans-serif; font-size: 1.55em; line-height: 1.1; color: var(--gold-text);
  text-shadow: 0 2px 0 var(--ink), 0 0 4px rgba(0,0,0,.6), 1.5px 0 0 var(--ink), -1.5px 0 0 var(--ink);
  border-radius: 999px; transition: background .2s ease; pointer-events: auto;
}
/* Видимый сигнал штрафа за копление золота (ночная правка, находка
   ревьюера №1 — механика реальна, но раньше никак не показывалась). */
@keyframes goldHoardPulse { 0%,100% { background: rgba(200,64,30,0); } 50% { background: rgba(200,64,30,.45); } }
.gold-row.gold-hoard-flash { animation: goldHoardPulse .35s ease-in-out 4; }
.gold-dot { width:.9em; height:.9em; border-radius:50%; background: radial-gradient(circle at 35% 30%, #fff6d0, #f2c14a 45%, #c8821e); display:inline-block; border:2px solid var(--ink); flex-shrink: 0; }
/* Валюта магазина — «Кристаллы»/«Gems» (раунд 15, одно имя и одна иконка
   везде: HUD, магазин, итог, реклама за награду) — нарисованный огранённый
   кристалл (SVG), а не повёрнутый квадрат/эмодзи. */
.diamond-row { margin-top: .3em; display:inline-flex; align-items:center; gap:.3em; word-spacing: .16em; font-family: 'Lilita One', sans-serif; font-size: max(13px, .9em); color: #bfefff; text-shadow: 0 1px 0 var(--ink), 1px 0 0 var(--ink), -1px 0 0 var(--ink), 0 0 3px rgba(0,0,0,.6); }
.diamond-dot {
  width: 1.05em; height: 1.05em; display: inline-block; flex-shrink: 0; vertical-align: -.18em;
  background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M6 3h12l4.5 6L12 21.5 1.5 9z' fill='%234cc3ef'/%3E%3Cpath d='M6 3h12l-2.5 6h-7z' fill='%23c8f3ff'/%3E%3Cpath d='M1.5 9h7L12 21.5z' fill='%232a9fd6'/%3E%3Cpath d='M22.5 9h-7L12 21.5z' fill='%23187fb8'/%3E%3Cpath d='M8.5 9h7L12 21.5z' fill='%236fd8fa'/%3E%3Cpath d='M6 3h12l4.5 6L12 21.5 1.5 9z' fill='none' stroke='%231a120a' stroke-width='1.7' stroke-linejoin='round'/%3E%3Cpath d='M9 5.2l1.6 1.6' stroke='%23fff' stroke-width='1.3' stroke-linecap='round'/%3E%3C/svg%3E") center / contain no-repeat;
}
.mission-title {
  flex: 1 1 0; min-width: 0; text-align: center; word-spacing: .16em; font-family: 'Lilita One', sans-serif; font-size: 1.1em; letter-spacing: .03em;
  padding: .1em .4em; margin-top: 0; color: var(--gold-text);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  text-shadow: 0 2px 0 var(--ink), 1.5px 0 0 var(--ink), -1.5px 0 0 var(--ink), 0 -1px 0 var(--ink);
}
.icon-btn {
  pointer-events: auto; background: var(--grain), linear-gradient(var(--secondary-1), var(--secondary-2));
  color: var(--paper); min-width: 44px; min-height: 44px; width: 2.4em; height: 2.4em; font-size: 1em;
  display: inline-flex; align-items: center; justify-content: center; padding: 0;
  box-shadow: inset 0 1px 0 var(--edge-light), inset 0 -2px 0 rgba(0,0,0,.25), 0 4px 0 0 var(--secondary-shadow), 0 6px 10px rgba(0,0,0,.35);
}
.icon-btn.hud-round { border-radius: 50%; flex-shrink: 0; word-spacing: .16em; font-family: 'Lilita One', sans-serif; font-size: 1.05em; }
.icon-btn:active { box-shadow: 0 2px 0 0 var(--secondary-shadow); }
/* SVG-иконки из спрайта (index.html) — размер от шрифта кнопки. */
svg.ico { width: 1.15em; height: 1.15em; display: inline-block; vertical-align: -.2em; color: var(--gold-text); pointer-events: none; }
.icon-btn svg.ico { width: 55%; height: 55%; color: var(--paper); }

.hud-bottom {
  display: flex; flex-direction: column; align-items: center; gap: .4em;
  padding: 0 .6em .6em;
  min-width: 0; /* иначе flex-контейнер не даёт себе сжаться теснее содержимого — сам ряд ниже не заскроллится */
}
/* Баг-репорт основателя (третий той же породы за сутки — обрезанное меню,
   магазин с одним товаром, теперь этот ряд): раскладка была рассчитана на
   ФИКСИРОВАННОЕ число кнопок, а не на любое. .toolbar (вложенный
   flex-контейнер с юнитами и апгрейдом) не был защищён от сжатия —
   родительский .hud-bottom-row на тесном экране душил его ниже
   естественной ширины (тот самый паттерн "flex-shrink давит контейнер",
   что уже чинили для .shop-list), и последняя кнопка тулбара (апгрейд, $)
   вылезала из своих же границ и рисовалась ПОД следующей кнопкой
   (удар героем) — не просто обрезка, а видимое наложение спрайтов.
   Системный фикс на будущее (любое число юнитов/способностей): ряд не
   душит своих детей (flex-shrink:0 на каждом), а вместо этого скроллится
   по горизонтали, если не помещается целиком — кнопки остаются
   касабельного размера, ничего не накладывается друг на друга ни при
   каком количестве. `safe center` — центрирует, когда влезает целиком, и
   не мешает доскроллить до начала ряда, когда не влезает (без `safe`
   часть контента слева иногда становится недостижимой при скролле). */
.hud-bottom-row {
  display: flex; align-items: flex-end; justify-content: center; justify-content: safe center;
  gap: .5em; pointer-events: auto;
  max-width: 100%; overflow-x: auto; overflow-y: hidden;
  -webkit-overflow-scrolling: touch; scrollbar-width: thin;
  /* Баг-репорт основателя (видео 09.09): hover на крайней кнопке (transform:
     scale(1.07) в правиле .tool-btn:hover) на паре px раздвигал scrollable
     overflow ряда за границу его же shrink-to-fit контейнера — overflow-x:auto
     честно рисовал полосу прокрутки под тулбаром на пустом месте. Боковой
     padding — запас, который hover-раздутие крайних кнопок гасит, не долетая
     до края; сам overflow-x:auto остаётся живым (фикс от 08.09, узкие экраны
     с реально не помещающимся рядом кнопок скроллятся как раньше). */
  padding: 0 6px 2px;
}
.hud-bottom-row > * { flex-shrink: 0; }
@media (pointer: coarse) {
  /* .hud-bottom{align-items:center} центрирует ряд как ЦЕЛОЕ (вместе с
     его margin-полем) — margin-left в одиночку не "отодвигает от левого
     края", а сдвигает уже центрированный бокс, реального якоря к краю не
     даёт (это и было причиной первой неудачной попытки). align-self:
     stretch — ряд сначала занимает всю ширину .hud-bottom, тогда
     margin-left/margin-right (оба, выставляются в game.js,
     layoutHudBottomRow()) уже честно отступают от настоящих левого и
     правого краёв. */
  .hud-bottom-row { align-self: stretch; }
}
/* Второй заход (основатель прислал новый скриншот — наложение осталось):
   первый фикс убрал внутреннее сжатие ряда, но проблема была НЕ во
   внутреннем сжатии — тулбар и на реальном устройстве, и в тестах после
   первого фикса свою естественную ширину держал честно. Наложение — со
   СОВСЕМ ДРУГИМ элементом: `.touch-actions` (кнопки удар/спец/кирка/клич
   на touch) и `#joyBase` (джойстик) лежат АБСОЛЮТНО, вне потока
   `.hud-bottom-row` вообще (см. #touchControls ниже) — координатор
   подсказал верно: "не позиционируется ли группа способностей абсолютно,
   не выносится ли из потока". Отцентрованный `.hud-bottom-row` на узком
   touch-экране физически заезжает под них (проверено: на 900/800/750/700
   px ширины реальное наложение, на 850px — 7px просвета чисто случайно,
   поэтому прошлая проверка на пяти viewport его не поймала). Резервируем
   те же зоны теми же выражениями, что задают сами джойстик/тач-кнопки
   (не подобранными на глаз числами) — гарантирует отсутствие наложения
   по построению, а не по совпадению конкретной ширины экрана. */
/* CSS-формула (margin/width от %/vmin, параллельная джойстику и
   тач-кнопкам) не давала гарантии совпадения с их РЕАЛЬНЫМИ отрисованными
   границами — разошлась на практике (см. layoutHudBottomRow() в game.js).
   Резервирование места под .hero-mini/.toolbar теперь считается в JS по
   настоящим getBoundingClientRect() джойстика и .touch-actions, не по
   отдельной формуле, которая может незаметно разойтись с оригиналом. */
/* Раунд 15 (И5): индикатор героя, а не кнопка — без деревянной плашки и
   тени «нажми меня»: круглый портрет головы героя (золотой, как в риге) и
   прижатая к нему полоска HP, как рамка юнита в RTS. Пока герой мёртв
   (.dead из updateHud) — портрет серый, поверх него секунды до респауна. */
.hero-mini {
  display:flex; flex-direction:row; align-items:center; pointer-events:auto; cursor: default; margin-right:.6em;
  align-self: center;
}
.hero-mini-icon {
  position: relative; z-index: 1; flex-shrink: 0;
  width:2.5em; height:2.5em; border-radius:50%; overflow: hidden;
  background: radial-gradient(circle at 50% 35%, #8fc3ea, #3b74c4);
  border: 3px solid var(--gold-text); box-shadow: 0 0 0 2px var(--ink), 0 3px 8px rgba(0,0,0,.4);
  display:flex; align-items:flex-end; justify-content:center;
}
.hero-mini-icon svg.ico { width: 100%; height: 100%; vertical-align: 0; }
.hero-mini-timer {
  position: absolute; inset: 0; display: none; align-items: center; justify-content: center;
  word-spacing: .16em; font-family: 'Lilita One', sans-serif; font-size: 1.15em; color: #fff3c4;
  text-shadow: 0 2px 0 var(--ink), 2px 0 0 var(--ink), -2px 0 0 var(--ink), 0 -2px 0 var(--ink);
}
.hero-mini.dead .hero-mini-icon { border-color: #b9a78a; background: radial-gradient(circle at 50% 35%, #7a7068, #3a322c); }
.hero-mini.dead .hero-mini-icon svg.ico { filter: grayscale(1) brightness(.55); }
.hero-mini.dead .hero-mini-timer { display: flex; z-index: 2; }
/* r15 И16: кольцо оставшегося времени респауна поверх серого портрета. */
.hero-mini-ring { position: absolute; inset: 0; width: 100%; height: 100%; display: none; pointer-events: none; z-index: 1; }
.hero-mini.dead .hero-mini-ring { display: block; }
#heroMiniRing { transition: stroke-dashoffset .25s linear; }
/* r15 И16: слот под своей полосой HP (тач) — портрет героя + кнопка выкупа. */
.hero-slot-top { display: flex; align-items: center; gap: .5em; margin-top: .3em; pointer-events: auto; }
.hero-slot-top:empty { display: none; }
.hero-slot-top .hero-mini { margin-right: 0; }
.bar-hp-hero { width: 3.2em; height: .8em; margin-left: -.55em; padding: 0; }
.bar-hp-hero .bar-fill { background: linear-gradient(var(--cta-1), var(--cta-2)); }
.hero-mini.dead .bar-hp-hero { background: rgba(120,30,20,.55); }

/* Таймер автовоскрешения героя + досрочный выкуп (раунд 7, баг-репорт:
   герой раньше воскресал незаметно быстро). Утро (баг-репорт основателя
   с телефона, скриншот боя — "ДИКО НЕУДОБНО"): раньше лежала широкой
   панелью ПОСЕРЕДИНЕ поля боя, закрывая юнитов на линии столкновения —
   что бы ни делали с flex-потоком нижнего ряда, сама позиция (по центру,
   на высоте боя) была неверной в принципе. Перенесли в верхнюю
   "чердачную" зону HUD — но там вылезла ВТОРАЯ проблема (баг-репорт,
   скриншот 1788791898991.jpg): фиксированный `top` в em не учитывал, что
   на реальном узком телефоне заголовок миссии переносится на 2 строки и
   .hud-top становится выше — плашка налезала на полосу HP врага. Теперь
   `#heroReviveBox` — последний ребёнок именно `.hud-top` (см. index.html),
   а не отдельный элемент с угаданным отступом: `top:100%` берёт РЕАЛЬНУЮ
   высоту этого ряда, какой бы она ни была (1 или 2 строки заголовка),
   и сама подстраивается. Не на линии боя, не над тулбаром, не поверх
   кнопки "Продолжить" на паузе (там вообще не показывается, см.
   updateHud() в game.js). Компактный размер + перенос кнопки строкой ниже. */
/* r15 И16 (куратор №5: «красная панель „Captain down“ на 20 с закрывает
   верх-центр поля при каждой смерти героя»): панели больше нет. Таймер —
   кольцо + секунды на портрете героя, здесь — только компактная кнопка
   выкупа рядом с портретом. ПК: #heroReviveBox — ребёнок .hud-bottom, над
   портретом (left/bottom выставляет positionReviveBox() в game.js по
   реальному rect портрета). Тач: переезжает в #heroSlotTop справа от
   портрета, под свою полосу HP (.in-slot, в потоке). */
.hud-bottom { position: relative; }
.hero-revive-box {
  position: absolute; left: 0; bottom: 100%; z-index: 6;
  display: flex; align-items: center; pointer-events: auto; white-space: nowrap;
  font-size: max(13px, .95em);
  animation: popIn .2s ease-out;
}
.hero-revive-box.in-slot { position: static; font-size: 14px; }
.hero-revive-box .menu-btn { animation: heroDownGlow 1.1s ease-in-out .2s infinite; }
/* r15 И10 (куратор, мобильный: «(B)» и текст ~7px): кнопка «Воскресить 30»
   крупная, без упоминания клавиш; хоткей B — отдельным бейджем, только на
   устройствах с клавиатурой (см. .key-badge ниже и @media pointer:coarse). */
.hero-revive-box .menu-btn {
  display: inline-flex; align-items: center; gap: .35em;
  min-height: 44px; padding: .2em .8em; font-size: max(14px, 1em);
}
.hero-revive-box .menu-btn .gold-dot { width: .95em; height: .95em; }
.key-badge {
  display: inline-block; min-width: 1.45em; padding: 0 .25em; text-align: center;
  font-family: 'Fredoka', sans-serif; font-weight: 700; font-size: max(13px, .8em); line-height: 1.35; /* r15 И18: 13px */
  background: rgba(26,18,10,.72); color: var(--paper); border: 1px solid rgba(255,215,122,.45); border-radius: 5px;
  text-shadow: none;
}
@media (pointer: coarse) {
  .key-badge { display: none; }
  .hero-revive-box .menu-btn { min-height: 44px; font-size: 16px; padding: .15em .75em; }
}
/* Стадия 2 (баг-репорт): текст статуса героя в углу HUD легко пропустить,
   если взгляд игрока на другом конце арены — коробка выкупа теперь
   постоянно пульсирует, пока герой мёртв, не только при появлении. */
@keyframes heroDownGlow {
  0%,100% { box-shadow: 0 0 0 0 rgba(200,64,30,.6); }
  50% { box-shadow: 0 0 16px 5px rgba(200,64,30,.75); }
}
/* Разовая вспышка на весь экран в момент смерти героя — сигнал заметен,
   даже если игрок смотрит в другой конец арены, не только мелкий текст HUD. */
#heroDownFlash {
  position: absolute; inset: 0; pointer-events: none; z-index: 5;
  background: radial-gradient(ellipse at center, rgba(200,64,30,0) 55%, rgba(200,64,30,.55) 100%);
  opacity: 0;
}
#heroDownFlash.flash { animation: heroDownFlashFade 1s ease-out; }
@keyframes heroDownFlashFade { 0% { opacity: 1; } 100% { opacity: 0; } }
.toolbar { display: flex; gap: .4em; pointer-events: auto; }
.tool-btn {
  position: relative; min-width: 48px; min-height: 48px; width: 3.6em; height: 3.8em;
  flex-shrink: 0; /* min-width уже держал пол, но явный флаг — тот же принцип защиты, что и на .toolbar/.hud-bottom-row выше */
  background: var(--grain), linear-gradient(var(--secondary-1), var(--secondary-2)); color: var(--paper);
  display:flex; flex-direction:column; align-items:center; justify-content:center;
  padding: .2em 0 .15em; font-size: 1em; overflow: hidden;
  box-shadow: inset 0 1px 0 var(--edge-light), inset 0 -2px 0 rgba(0,0,0,.25), 0 4px 0 0 var(--secondary-shadow), 0 6px 10px rgba(0,0,0,.35);
}
@media (hover: hover) and (pointer: fine) {
  .tool-btn:hover:not(.disabled) { border-color: var(--gold-text); }
}
/* r15 И10: недоступная кнопка — приглушена, но подпись и цена читаются
   (при .45 «Залп»/«Эра» на тёмном дереве сливались с фоном). */
.tool-btn.disabled { opacity: .58; cursor: not-allowed; transform: none !important; filter: saturate(.55) !important; }
.tool-btn.pressed { transform: translateY(3px); box-shadow: none; }
.tool-icon { font-size: 1.3em; line-height:1; }
/* И5: иконки способностей — того же размера, что стопка монет апгрейда
   (1.35em), и в одном стиле (SVG-спрайт index.html, тёмный контур). */
.tool-icon svg.ico { width: 1.35em; height: 1.35em; vertical-align: 0; display: block; color: var(--gold-text); }
/* Раунд 15 (И1): апгрейд дохода рисовался эмодзи «💲» (разметка — в
   buildToolbar, И2) — глиф прячем, вместо него нарисованная стопка монет. */
#upgradeBtn .tool-icon {
  width: 1.35em; height: 1.35em; overflow: hidden; text-indent: -9em; white-space: nowrap;
  background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 48 48'%3E%3Cg stroke='%231a120a' stroke-width='2.4'%3E%3Cellipse cx='20' cy='37' rx='13' ry='5.5' fill='%23e0a030'/%3E%3Cellipse cx='20' cy='31' rx='13' ry='5.5' fill='%23ffd35c'/%3E%3Cellipse cx='30' cy='22' rx='13' ry='5.5' fill='%23e0a030'/%3E%3Cellipse cx='30' cy='15' rx='13' ry='5.5' fill='%23ffe08a'/%3E%3C/g%3E%3Cpath d='M38 3v10M33 8h10' stroke='%237bd98a' stroke-width='3.4' stroke-linecap='round'/%3E%3C/svg%3E") center / contain no-repeat;
}
/* И7 (куратор: на 800×450 иконка ~12px, подписи 7–8px): иконка крупнее,
   цена и хоткей не мельче 11px, HP в шапке — не мельче 12px. */
.tool-icon-canvas { width: 2.4em; height: 2.64em; display:block; margin-top: -.1em; }
.tool-cost { font-size: max(12px, .78em); line-height: 1.1; color: var(--gold-text); margin-top: .1em; font-weight: 700; font-family: 'Fredoka', sans-serif; }
/* И5: хоткей — один бейдж у всех кнопок тулбара (юниты 1–4, Q, V, T, R, F,
   E): тот же угол, размер и цвет, поверх кольца эры и шторки кулдауна;
   на тач-экранах клавиатуры нет — бейджи скрыты (см. @media ниже). */
.tool-key {
  position: absolute; top: 3px; left: 3px; z-index: 2; min-width: 1.3em; text-align: center;
  font-size: max(13px, .66em); line-height: 1.2; font-weight: 700; color: var(--paper); /* r15 И18: 13px */
  background: rgba(26,18,10,.7); border: 1px solid rgba(255,215,122,.35); border-radius: 4px; padding: 0 .2em; font-family: 'Fredoka', sans-serif;
}
.tool-btn .tool-cost { position: relative; z-index: 1; }
@media (hover: none) and (pointer: coarse) { .tool-key { display: none; } }
/* r15 И10 (куратор CrazyGames: на 800×450 кнопки ~48px, цены/подписи/клавиши
   7–9px): на устройствах с мышью кнопки тулбара не меньше 58×60px, иконки
   крупнее, цена и подпись ≥13px, бейдж клавиши ≥12px. 10 кнопок + индикатор
   героя при 800px занимают ~730px — без горизонтальной прокрутки ряда. На
   тач-экранах (pointer: coarse) — прежние 48px: там ряд делит ширину с
   джойстиком и тач-кнопками (layoutHudBottomRow). */
@media not all and (pointer: coarse) {
  .tool-btn { min-width: 58px; min-height: 60px; width: 3.9em; height: 4.1em; padding: .25em 0 .2em; }
  .tool-icon-canvas { width: max(34px, 2.5em); height: max(37px, 2.75em); }
  .tool-icon svg.ico { width: max(22px, 1.6em); height: max(22px, 1.6em); }
  #upgradeBtn .tool-icon { width: max(24px, 1.7em); height: max(24px, 1.7em); }
  .tool-cost { font-size: max(13px, .8em); }
  .tool-key { top: 2px; left: 2px; }
  .hero-mini-icon { width: max(40px, 2.9em); height: max(40px, 2.9em); }
  .bar-hp-hero { width: max(40px, 3.2em); height: max(10px, .8em); }
  .hud-bottom-row { gap: max(6px, .45em); }
  .toolbar { gap: max(5px, .38em); }
}
.special-btn { background: var(--grain), linear-gradient(var(--royal-1), var(--royal-3)); border-color: var(--ink); }
.cd-overlay {
  position: absolute; left: 0; right: 0; bottom: 0; height: 0%;
  background: rgba(0,0,0,.65); border-radius: 0 0 calc(var(--r-lg) - 3px) calc(var(--r-lg) - 3px); pointer-events: none;
}
@keyframes shakeBtn { 0%,100%{transform:translateX(0)} 25%{transform:translateX(-3px)} 75%{transform:translateX(3px)} }
.tool-btn.denied { animation: shakeBtn .2s; }

/* ---------- Экраны ---------- */
.screen {
  /* Вечер 08.09.2026 (баг-репорт с реальной ВК-площадки — «600px это вся
     игра, а само игровое окно лежит внутри и не хватает места»): раньше
     .screen был position:absolute внутри #arenaWrap — того же контейнера,
     что жёстко держит аспект 2.5:1 канваса боя (см. #arenaWrap ниже). На
     контейнере 1000×600 (ВК-iframe, 5:3) это давало #arenaWrap ровно
     1000×400 (250px×2.5=нет, 1000/2.5=400) — и меню/магазин/карта глав,
     как потомки #arenaWrap, оказывались зажаты в те же 400px, с ровно
     100px пустых декоративных полей сверху и снизу (число подтверждено
     живым Playwright-замером #screenMenu: top:100, bottom:500). Экраны
     (меню/магазин/карта глав/пауза/итог/справка/плейлист) — НЕ канвас: их
     разметка не завязана на пиксельную сетку боя и не обязана держать
     2.5:1. position:fixed вместо absolute — контекст позиционирования
     становится вьюпорт iframe целиком (тут нет ни одного предка с
     transform/filter/will-change, ломающего это), а не letterbox-бокс
     арены, поэтому inset:0 теперь означает «весь кадр 1000×600», а не
     «внутренние 1000×400». #arenaWrap (и сам канвас боя, и HUD — они
     видны только во время match/paused/help, см. showScreen() в game.js)
     остаётся ПОЛНОСТЬЮ нетронутым — 2.5:1 канваса и его выравнивание с
     HUD не меняются ни на пиксель, риска исказить графику боя нет. */
  position: fixed; inset: 0; display: flex; flex-direction: column;
  align-items: center; justify-content: center; text-align: center; gap: .7em;
  padding: 1em;
  /* Утро (баг-репорт с телефона, скриншоты меню/карты глав): раньше при
     нехватке высоты контент просто обрезался краем экрана без скролла
     (html/body — overflow:hidden). Теперь, если не влезло даже после
     компактного режима ниже (@media max-height), можно докрутить —
     обрезки в принципе больше не бывает, максимум неидеальное
     центрирование. */
  overflow-y: auto;
  /* Раунд 15 (И1): размер текста экранов — от высоты вьюпорта (на 1080p
     меню/магазин были мелкими), и мягкое появление экрана за 150 мс. */
  font-size: clamp(13px, 2.3vh, 24px);
  animation: screenFade .15s ease-out;
  /* Раунд 15 (И7, куратор CrazyGames): экраны — поверх HUD. Раньше у
     .screen не было z-index, а у .tool-key/.tool-cost тулбара — 1–2, и
     цифры клавиш и цены просвечивали сквозь справку/паузу/магазин/итог.
     10 — выше HUD, тач-кнопок (4), отсчёта (6) и слоя обучения (7), ниже
     окна тестовой рекламы (500) и уведомлений (600). */
  z-index: 10;
}
@keyframes screenFade { from { opacity: 0; } to { opacity: 1; } }
.screen.overlay { background: rgba(12,8,4,.85); }
/* Экран итога (раунд 5-6, повторная жалоба основателя): фон на самом деле
   уже анимируется (день/ночь, ветер, флаги, юниты) — просто 85%-затемнение
   паузы делало это незаметным глазу. Для итога держим фон заметно светлее,
   чтобы «живая» сцена позади карточки реально читалась. */
#screenResult.overlay { background: rgba(12,8,4,.4); }

/* Карточная панель — списки миссий/магазина */
.panel-card {
  display: flex; flex-direction: column; align-items: center; gap: .7em;
  background: var(--grain), linear-gradient(var(--royal-1), var(--royal-3));
  border: 4px solid var(--bronze-dark); border-radius: var(--r-xl);
  padding: 1.2em 1.4em; box-shadow: inset 0 0 0 1px var(--edge-light), 0 12px 30px rgba(0,0,0,.5);
  max-width: 94vw; animation: popIn .18s ease-out;
  /* Утро (баг-репорт с телефона) — сама карточка тоже не имела потолка по
     высоте, только вложенные списки (.chapter-trail/.shop-list) скроллились
     сами по себе; на низком вьюпорте заголовок+список+кнопка «Назад» вместе
     всё равно превышали экран. */
  max-height: 94vh; max-height: 94dvh; overflow-y: auto;
  /* Утро 08.09.2026: .panel-card теперь единственный скролл экранов
     выбора главы/магазина (см. .chapter-trail/.shop-list) — даём ему тот
     же фирменный скроллбар, что раньше был у вложенных списков, вместо
     системного по умолчанию. */
  scrollbar-width: thin; scrollbar-color: var(--cta-2) rgba(0,0,0,.25);
}
.panel-card::-webkit-scrollbar { width: 10px; }
.panel-card::-webkit-scrollbar-track { background: rgba(0,0,0,.25); border-radius: 6px; }
.panel-card::-webkit-scrollbar-thumb { background: linear-gradient(var(--cta-1), var(--cta-2)); border-radius: 6px; border: 2px solid var(--ink); }
/* Раунд 14: заголовки панелей — кремовый текст с тёмной тенью, как
   «ДВЕ КРЕПОСТИ» на обложке (жёлтая пилюля раунда 4b убрана; padding
   сохранён — геометрия карточек не меняется). */
.panel-card h2 {
  margin: 0 0 .1em; padding: .25em 1.1em; border-radius: 999px;
  background: none;
  color: var(--paper); text-shadow: 0 2px 0 var(--ink), 0 4px 10px rgba(0,0,0,.5);
  word-spacing: .16em; font-family: 'Lilita One', sans-serif; font-weight: 400; font-size: 1.25em; letter-spacing: .06em;
  box-shadow: none;
}

/* Всплывающие модалки (пауза, итог миссии) — упругий bounce при появлении */
.popup-card {
  display: flex; flex-direction: column; align-items: center; gap: .7em;
  background: var(--grain), linear-gradient(var(--royal-1), var(--royal-3));
  border: 4px solid var(--bronze-dark); border-radius: var(--r-xl);
  padding: 1.4em 1.8em; box-shadow: inset 0 0 0 1px var(--edge-light), 0 14px 34px rgba(0,0,0,.55);
  max-width: 94vw; animation: bounceIn .45s cubic-bezier(.34,1.56,.64,1);
  max-height: 94vh; max-height: 94dvh; overflow-y: auto;
}
.popup-card h2 {
  margin: 0 0 .1em; padding: .25em 1.2em; border-radius: 999px;
  background: none;
  color: var(--paper); text-shadow: 0 2px 0 var(--ink), 0 4px 10px rgba(0,0,0,.5);
  word-spacing: .16em; font-family: 'Lilita One', sans-serif; font-weight: 400; font-size: 1.35em; letter-spacing: .06em;
  box-shadow: none;
}
.popup-card p { max-width: 26em; opacity: .95; }
/* Крупная выделенная награда на экране итога (утро 08.09.2026, баг-репорт
   основателя — окно победы «явно слишком длинное»): одно название и один
   значок валюты сквозь всю игру (см. КОНЦЕПТ_ГДД.md, «Допущения») — тот же
   .diamond-dot, что в HUD боя и в шапке магазина, не отдельная иконка. */
.result-reward {
  display: flex; align-items: baseline; gap: .5em; margin: -.2em 0 .1em;
}
.result-reward-label { font-size: .85em; opacity: .85; }
.result-reward-value {
  display: inline-flex; align-items: center; gap: .3em;
  word-spacing: .16em; font-family: 'Lilita One', sans-serif; font-size: 1.7em; color: var(--gold-text);
  text-shadow: 0 2px 0 var(--ink), 0 4px 10px rgba(0,0,0,.5);
}
.result-reward-value .diamond-dot { width: 1.1em; height: 1.1em; }
.result-reward-value #resultRewardAmount::before { content: '+'; }
/* Победа — золотое сияние; поражение — тревожный красно-оранжевый
   (тревожные элементы — отдельным акцентным цветом палитры) */
.popup-card.win { border-color: var(--gold-text); animation: bounceIn .45s cubic-bezier(.34,1.56,.64,1), glowPulse 1.6s ease-in-out .45s infinite; }
.popup-card.win h2 { color: var(--gold-text); }
.popup-card.lose {
  border-color: var(--alert-2);
  background: var(--grain), linear-gradient(#3e1a12, var(--royal-3));
  animation: bounceIn .45s cubic-bezier(.34,1.56,.64,1), alertShake .5s ease .45s 1;
}
.popup-card.lose h2 { color: var(--alert-1); }

/* r15 И10: экран загрузки (до PLATFORM.ready) — затемнённая сцена меню,
   вращающаяся эмблема и бегущая полоса; ни текста, ни кнопок. */
#screenLoading { background: radial-gradient(ellipse at center, rgba(21,16,11,.35), rgba(21,16,11,.8)); gap: 1.2em; animation: none; }
.loading-emblem {
  width: max(64px, 13vh); height: max(64px, 13vh); border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  background: var(--grain), linear-gradient(var(--secondary-1), var(--secondary-2));
  border: 4px solid var(--ink); box-shadow: 0 0 0 3px var(--gold-text), 0 10px 24px rgba(0,0,0,.5);
  animation: loadingSpin 1.6s cubic-bezier(.6,.1,.4,.9) infinite;
}
.loading-emblem svg.ico { width: 68%; height: 68%; color: var(--gold-text); }
.loading-bar { width: min(60vw, 280px); height: 12px; border-radius: 999px; overflow: hidden; background: rgba(0,0,0,.45); border: 2px solid var(--ink); }
.loading-bar-fill { width: 35%; height: 100%; border-radius: 999px; background: linear-gradient(90deg, var(--cta-1), var(--cta-2)); animation: loadingRun 1.1s ease-in-out infinite; }
@keyframes loadingSpin { 0% { transform: rotate(0); } 60%, 100% { transform: rotate(360deg); } }
@keyframes loadingRun { 0% { transform: translateX(-110%); } 100% { transform: translateX(300%); } }

/* Раунд 15 (И1): главное меню. Содержимое — в верхних ~55% кадра, ниже —
   живая сцена на канвасе (крепости, герой, отряды — drawMenuScene в game.js).
   Логотип — два слоя одного текста: задний даёт толстую тёмную обводку и
   тень, передний — золотую градиентную заливку. Ничего не переносится. */
#screenMenu { justify-content: flex-start; padding-top: max(1em, 7vh); gap: 0; }
#screenMenu .menu-main { display: flex; flex-direction: column; align-items: center; gap: max(8px, 2.2vh); }
#screenMenu .logo {
  display: grid; margin: 0 0 max(4px, 1.2vh); word-spacing: .16em; font-family: 'Lilita One', 'Fredoka', sans-serif; font-weight: 400;
  font-size: min(11vh, 8.4vw); line-height: 1.05; letter-spacing: .04em; white-space: nowrap;
  transform: rotate(-1.5deg);
}
#screenMenu .logo > span { grid-area: 1 / 1; padding: 0 .12em; }
#screenMenu .logo-back {
  /* обводка кольцом теней, а не -webkit-text-stroke: у того на острых
     углах кириллицы («И», «К») вылезали шипы-митры */
  color: #2a1608;
  text-shadow:
    .07em 0 0 #2a1608, -.07em 0 0 #2a1608, 0 .07em 0 #2a1608, 0 -.07em 0 #2a1608,
    .05em .05em 0 #2a1608, -.05em .05em 0 #2a1608, .05em -.05em 0 #2a1608, -.05em -.05em 0 #2a1608,
    0 .14em 0 #2a1608, .06em .13em 0 #2a1608, -.06em .13em 0 #2a1608,
    0 .2em .3em rgba(0,0,0,.45);
}
#screenMenu .logo-front {
  background: linear-gradient(#fffbe6 0%, #ffe38f 40%, #f4ae3e 62%, #e07a26 100%);
  -webkit-background-clip: text; background-clip: text; color: transparent;
}
#screenMenu .menu-btn { white-space: nowrap; }
#screenMenu .menu-btn.primary-big { font-size: max(20px, 4.6vh); min-height: 0; padding: .38em 2.8em; min-width: 7.5em; }
#screenMenu .menu-row { display: flex; gap: max(10px, 1.6vh); }
#screenMenu .menu-row .menu-btn { font-size: max(15px, 2.9vh); min-height: 0; padding: .45em 1.4em; min-width: 7.6em; }
/* Настройки — шестерёнка в правом верхнем углу, поповер под ней. */
.settings-btn { position: absolute; top: max(10px, 2vh); right: max(10px, 2vh); font-size: max(15px, 2.4vh); z-index: 3; }
.settings-btn.active { border-color: var(--gold-text); }
.settings-btn svg.ico { width: 62%; height: 62%; }
.settings-pop {
  position: absolute; top: calc(max(10px, 2vh) + max(15px, 2.4vh) * 2.4 + 10px); right: max(10px, 2vh); z-index: 3;
  display: flex; flex-direction: column; gap: .45em; min-width: 13em; padding: .8em .9em;
  background: var(--grain), linear-gradient(var(--royal-1), var(--royal-3));
  border: 3px solid var(--bronze-dark); border-radius: var(--r-lg);
  box-shadow: inset 0 0 0 1px var(--edge-light), 0 10px 26px rgba(0,0,0,.5);
  animation: popIn .15s ease-out; text-align: left;
}
.settings-title { word-spacing: .16em; font-family: 'Lilita One', sans-serif; color: var(--gold-text); text-shadow: 0 1px 0 var(--ink); margin-bottom: .1em; }
.setting-row {
  display: flex; align-items: center; justify-content: space-between; gap: 1em; width: 100%;
  background: rgba(0,0,0,.18); border: 2px solid rgba(26,18,10,.6); border-radius: 12px;
  color: var(--paper); font-family: 'Fredoka', sans-serif; font-weight: 600; font-size: 1em;
  padding: .45em .6em; min-height: 44px; cursor: pointer; white-space: nowrap;
}
.setting-row .toggle-switch { display: inline-block; pointer-events: none; }
.settings-pop .menu-btn.small { width: 100%; margin-top: .2em; }
.mission-list { display: flex; flex-direction: column; gap: .5em; max-height: 55vh; overflow-y: auto; width: min(90vw, 26em); }
.mission-item {
  display:flex; justify-content:space-between; align-items:center; gap:1em;
  padding: .6em 1em; border-radius: var(--r-lg); background: rgba(255,255,255,.08);
  border: 3px solid var(--ink); cursor: pointer; text-align: left; min-height: 48px;
  transition: transform .18s cubic-bezier(.34,1.56,.64,1), border-color .12s ease;
}
.mission-item:hover:not(.locked) { border-color: var(--gold-text); transform: scale(1.03); }
.mission-item:active:not(.locked) { transform: scale(.97); }
.mission-item.locked { opacity: .45; cursor: not-allowed; }
.mission-item .mi-name { font-size: .85em; opacity: .85; }

/* Карта глав (раунд 5, доработана в раунде 7): SVG-полоса на главу —
   фон-«местность», узлы со смещением по высоте, волнистые пунктирные
   дорожки между ними (см. chapterRowSVG в game.js). */
.chapter-trail {
  display: flex; flex-direction: column; gap: .3em; width: min(94vw, 34em);
  /* Утро 08.09.2026 (баг-репорт основателя — «двойной скролл в компании»):
     раньше был И свой overflow-y:auto (со скроллбаром), И родительская
     .panel-card тоже скроллилась — два независимых скролла одновременно.
     Единственный источник скролла для этого экрана теперь — .panel-card;
     flex-shrink:0 остаётся (профилактика того же класса бага, что и раньше:
     родитель не должен «душить» список ниже естественной высоты). */
  flex-shrink: 0;
}
.chapter-row { display: flex; flex-direction: column; align-items: stretch; }
.chapter-label { font-size: .85em; font-weight: 600; opacity: .9; margin-bottom: -.2em; }
.chapter-svg { width: 100%; height: auto; aspect-ratio: 520 / 114; /* r15 И16: 100 → 114, крупнее звёзды */ display: block; border-radius: 14px; background: linear-gradient(rgba(255,225,180,.07), rgba(0,0,0,.18)); box-shadow: inset 0 0 0 1px rgba(255,225,180,.12); }
.chapter-row.chapter-locked { opacity: .6; }
.chapter-row.chapter-locked .chapter-label {
  margin: 0; padding: .45em .8em; border: 2px dashed rgba(255,225,180,.22); border-radius: 12px; background: rgba(0,0,0,.14);
}
.chapter-label svg.ico { width: .95em; height: .95em; margin-right: .3em; color: #b9a78a; }
.chapter-label { margin-bottom: .15em; text-align: left; padding-left: .3em; }
@keyframes nodePulse { 0%,100% { opacity: .9; transform: scale(1); } 50% { opacity: .3; transform: scale(1.12); } }
.trail-node-svg .node-pulse { transform-origin: center; transform-box: fill-box; animation: nodePulse 1.4s ease-in-out infinite; }
.trail-node-svg:not(.locked):hover .node-pulse { transform: none; }
.trail-node-svg circle { transition: transform .15s cubic-bezier(.34,1.56,.64,1); transform-origin: center; transform-box: fill-box; }
.trail-node-svg:not(.locked) { cursor: pointer; }
.trail-node-svg:not(.locked):hover circle { transform: scale(1.12); }
.trail-node-svg:not(.locked):active circle { transform: scale(.94); }
.trail-node-svg.locked circle { cursor: not-allowed; }
.trail-node-svg text { pointer-events: none; user-select: none; }

/* Переключатель музыкального трека в паузе (раунд 5). */
/* Раунд 10 — два независимых переключателя звука рядом (SFX/музыка),
   и полноценная вкладка «Плейлист» вместо простого пикера в паузе. */
.mute-row { display: flex; gap: .5em; }
/* Сброс прогресса (ночная находка ревьюера) — намеренно неприметная
   текстовая ссылка, не полноценная кнопка: страховка, не приглашение. */
.link-btn {
  background: none; border: none; color: rgba(255,255,255,.4); font-size: .75em;
  text-decoration: underline; cursor: pointer; margin-top: .6em; padding: .4em;
  font-family: 'Fredoka', sans-serif;
}
.link-btn:hover { color: rgba(255,255,255,.7); }
.link-btn.confirming { color: var(--alert-1); text-decoration: none; font-weight: 600; }
/* И7: «Sound: on» переносился на две строки в паузе — без переноса, поля уже. */
.mute-row .menu-btn { flex: 1 1 auto; font-size: .85em; padding: .6em .7em; white-space: nowrap; }
.popup-card .mute-row { justify-content: center; }

.playlist-list {
  display: flex; flex-direction: column; gap: .5em; width: min(92vw, 28em); max-height: 50vh; overflow-y: auto;
  flex-shrink: 0; /* тот же профилактический фикс, что у .chapter-trail — растёт вместе с числом купленных треков */
}
.playlist-row {
  display: flex; align-items: center; gap: .6em;
  padding: .5em .8em; border-radius: var(--r-lg); background: rgba(255,255,255,.08);
  border: 3px solid var(--ink); min-height: 48px;
}
.playlist-name { flex: 1; text-align: left; font-weight: 600; }
.toggle-switch {
  width: 2.6em; height: 1.5em; border-radius: 999px; border: 2px solid var(--ink);
  background: #4a3a2c; position: relative; flex-shrink: 0; cursor: pointer; padding: 0;
}
.toggle-switch::after {
  content: ''; position: absolute; top: 1px; left: 1px; width: 1.1em; height: 1.1em;
  border-radius: 50%; background: #d8c8a8; transition: transform .18s ease;
}
.toggle-switch.on { background: linear-gradient(#7bd98a, #2f8f45); }
.toggle-switch.on::after { transform: translateX(1.1em); background: #fff; }
.icon-btn.small { width: 2em; height: 2em; min-width: 36px; min-height: 36px; font-size: .8em; }
#btnShuffle.active { outline: 3px solid var(--gold-text); }

/* Отсчёт 3…2…1 перед первым заходом новичка (раунд 5). */
#countdownOverlay {
  position: absolute; inset: 0; display: flex; align-items: center; justify-content: center;
  background: rgba(12,8,4,.25); z-index: 6; pointer-events: none; /* И1: ≤ .3 — сцена видна за отсчётом */
}
#countdownNum {
  word-spacing: .16em; font-family: 'Lilita One', sans-serif; font-size: clamp(3em, 14vw, 7em); color: var(--gold-text);
  -webkit-text-stroke: 3px var(--ink); text-shadow: 0 6px 0 var(--ink);
  animation: popIn .3s ease-out;
}
/* Раунд 15 (И4): увеличенный тулбар с затемнением на время подсказки
   новичку (раунд 5) убран вместе с подсказкой — вместо них пошаговый
   туториал со стрелками (js/tutorial.js, стили — блок «r15 И4» в конце). */

/* Раунд 8, п.G — рамка вокруг счётчика очков, чтобы визуально
   отделить его от заголовка "Магазин" и от списка карточек снизу (были
   слитными, жалоба основателя). */
.shop-currency {
  color: var(--gold-text); font-size: 1.1em; word-spacing: .16em; font-family: 'Lilita One', sans-serif;
  text-shadow: 0 1px 2px rgba(0,0,0,.6);
  border: 2px solid var(--ink); border-radius: var(--r-lg);
  background: rgba(0,0,0,.2); padding: .3em .9em; margin: 0;
}
/* Утро 08.09.2026 (баг-репорт основателя): планка "Магазин"+счётчик должна
   быть закреплена сверху при пролистывании списка, а кнопка "Назад"
   переехала сюда же, в правый край той же строки (была отдельной кнопкой
   под списком). Sticky работает относительно .panel-card — единственного
   скролла этого экрана (см. .shop-list ниже) — со своим непрозрачным
   фоном, повторяющим фон карточки, чтобы список не «просвечивал» под
   прилипшей панелью. */
/* Вечер 08.09.2026 (баг-репорт с реальной ВК/Яндекс площадки, видео
   основателя — верх предыдущей карточки виден НАД прилипшей шапкой):
   причина — `.shop-header-row` тянул верхний отступ карточки на себя
   отрицательным margin-top (-1.2em), чтобы визуально встать вплотную к
   скруглению `.panel-card` НЕСМОТРЯ на padding-top карточки. Для
   position:sticky это ловушка: offset "top" считается от margin-края, а
   отрицательный margin-top при этом ИГНОРИРУЕТСЯ (проверено живым
   Playwright-замером getBoundingClientRect — прилипшая шапка стояла на
   ~23px НИЖЕ реального верха .panel-card, ровно на величину этого
   margin-top). Именно в этом зазоре и был виден скроллящийся контент —
   шапка физически не долетала до самого верха, а не «недостаточно
   непрозрачная». Фикс: убираем padding-top у .panel-card ИМЕННО на экране
   магазина (обычным screen'ам вроде выбора главы этот padding нужен как
   был) и переносим ту же величину внутрь `.shop-header-row` как её
   СОБСТВЕННЫЙ padding-top — та же картинка в состоянии покоя, но без
   отрицательного margin-top, о который спотыкался sticky. */
#screenShop .panel-card { padding-top: 0; }
.shop-header-row {
  display: flex; align-items: center; justify-content: space-between; gap: .6em;
  width: 100%; flex-shrink: 0;
  position: sticky; top: 0; z-index: 2;
  background: var(--grain), linear-gradient(var(--royal-1), var(--royal-3));
  margin: 0 -1.4em 0; padding: 1.2em 1.4em .6em;
  border-bottom: 2px solid var(--edge-light);
  /* Скруглённые ВЕРХНИЕ углы ровно в радиус самой карточки — без этого в
     угловом зазоре на 1-2px просвечивал край прокручиваемого списка под
     прилипшей шапкой (видно на скриншоте живого теста). */
  border-radius: var(--r-xl) var(--r-xl) 0 0;
}
.shop-header-left { display: flex; align-items: center; gap: .6em; }
.shop-header-row h2 { margin: 0; }
.shop-header-row .shop-currency { font-size: .95em; padding: .25em .6em; }
.menu-btn.small { min-height: 36px; padding: .3em 1em; font-size: max(12px, .85em); } /* r15 И10: ≥12px */
/* Список карточек — своя рамка-«окно» (была ещё и своим скроллом — баг
   "двойной скролл в компании/магазине", утро 08.09.2026: .shop-list имел
   собственный overflow-y:auto ОДНОВРЕМЕННО с .panel-card — убрано, скролл
   теперь только у .panel-card, см. её правила выше). Раунд 11: карточка
   расширена (основатель — «грамотнее расширить карточку, чем резать
   текст»), было 30em. */
.shop-list {
  display: flex; flex-direction: column; gap: .5em;
  flex-shrink: 0;
  width: min(94vw, 36em); padding: .6em; border: 2px solid var(--edge-light);
  border-radius: var(--r-lg); background: rgba(0,0,0,.18);
}
/* Заголовки групп (раунд 11, баг-репорт: однотипные покупки — башни,
   капканы и т.п. — стояли в разных углах списка) — визуально отделяют
   секции магазина друг от друга. */
.shop-section-title {
  flex-shrink: 0;
  margin: .5em 0 -.1em; padding: .15em .3em; word-spacing: .16em; font-family: 'Lilita One', sans-serif;
  font-size: .85em; color: var(--gold-text); text-shadow: 0 1px 2px rgba(0,0,0,.6);
  border-bottom: 2px solid rgba(255,225,180,.22);
}
.shop-section-title:first-child { margin-top: 0; }
/* Раунд 11 (баг-репорт: цена наезжала на длинное описание при переносе на
   2 строки) — заголовок+цена своей строкой сверху, описание отдельной
   строкой во всю ширину карточки снизу: наложение больше невозможно
   структурно, независимо от длины текста. */
.shop-item {
  display: flex; flex-direction: column; gap: .25em; flex-shrink: 0;
  padding: .55em .9em; border-radius: var(--r-lg); background: rgba(255,255,255,.08);
  border: 3px solid var(--ink); text-align: left; min-height: 48px;
}
.shop-item .si-header { display: flex; align-items: center; justify-content: space-between; gap: 1em; }
.shop-item .si-title { flex: 1 1 auto; min-width: 0; }
.shop-item .si-buy { flex-shrink: 0; }
/* Утро — справка «?»: строка юнита/героя с живым анимированным значком
   слева (тот же риг стикмена, что и в бою — см. renderHelp()) и текстом
   справа, вместо колонки заголовок/описание/кнопка, как у обычных
   товаров магазина. */
.help-item { flex-direction: row; align-items: center; gap: .7em; }
.help-icon-canvas { flex-shrink: 0; width: 48px; height: 52px; background: rgba(0,0,0,.2); border-radius: 10px; }
.help-item-text { flex: 1 1 auto; min-width: 0; }
/* Переключатель "Время суток" (ночная правка) — три мини-кнопки в ряд. */
.time-select-group { display: flex; gap: .4em; flex-wrap: wrap; }
.time-select-group .si-buy, .toggle-inline { padding: .3em .7em; min-height: 36px; font-size: .85em; }
/* И7: не мельче 11px (на 800×450 было ~10px), тексты сокращены до одной строки. */
.shop-item .si-desc { font-size: max(11px, .8em); opacity: .85; overflow-wrap: break-word; }
/* Раунд 15 (И1): карточка магазина — иконка | название, описание,
   «было → станет» | цена с кристаллом. Вкладки «Улучшения» / «Музыка и
   стиль»; запертые по главе позиции — компактной строкой внизу секции. */
.shop-tabs { display: flex; gap: .4em; width: min(94vw, 36em); flex-shrink: 0; margin-bottom: -.3em; }
.shop-tab {
  flex: 1; padding: .4em .8em; border: 3px solid var(--ink); border-radius: 14px 14px 6px 6px;
  background: rgba(0,0,0,.28); color: var(--paper); word-spacing: .16em; font-family: 'Lilita One', sans-serif; font-size: 1em;
  cursor: pointer; white-space: nowrap; opacity: .85;
}
.shop-tab.active { background: linear-gradient(var(--cta-1), var(--cta-2)); color: #4a2c00; opacity: 1; box-shadow: 0 3px 0 var(--cta-shadow); }
.shop-item.has-icon { display: grid; grid-template-columns: auto minmax(0, 1fr) auto; align-items: center; column-gap: .75em; }
.si-icon {
  width: 2.7em; height: 2.7em; padding: .22em; border-radius: 12px; flex-shrink: 0;
  background: radial-gradient(circle at 50% 38%, rgba(255,235,190,.22), rgba(0,0,0,.28));
  border: 2px solid rgba(26,18,10,.6);
}
.si-body { min-width: 0; display: flex; flex-direction: column; gap: .14em; }
.si-tier { font-size: .78em; color: var(--gold-text); margin-left: .25em; }
.si-stat { font-size: .82em; word-spacing: .16em; font-family: 'Lilita One', sans-serif; letter-spacing: .02em; }
.si-stat-label { opacity: .75; font-family: 'Fredoka', sans-serif; font-weight: 600; }
.si-from { opacity: .7; font-weight: 400; }
.si-arrow { color: var(--gold-text); margin: 0 .3em; }
.si-to { color: #9af0a4; font-weight: 400; }
.si-buy .diamond-dot { margin-left: .25em; }
.si-buy { display: inline-flex; align-items: center; justify-content: center; }
.shop-item.is-owned { opacity: .82; }
.shop-locked-strip {
  flex-shrink: 0; font-size: .78em; opacity: .75; padding: .35em .7em; text-align: left; line-height: 1.55;
  border: 2px dashed rgba(255,225,180,.22); border-radius: 12px;
}
.shop-locked-strip svg.ico { width: 1em; height: 1em; margin-right: .3em; color: #b9a78a; }
.sls-label { font-weight: 600; margin-right: .2em; }
.sls-item em { font-style: normal; opacity: .75; }
.sls-sep { margin: 0 .45em; opacity: .5; }
.option-item .time-select-group { margin-top: .3em; }
.shop-item .si-buy {
  padding: .35em .9em; min-height: 40px;
  background: linear-gradient(var(--cta-1), var(--cta-2));
  color: #4a2c00; word-spacing: .16em; font-family: 'Lilita One', sans-serif; font-size: .9em; white-space: nowrap;
  box-shadow: 0 4px 0 0 var(--cta-shadow), 0 5px 8px rgba(0,0,0,.35);
}
.shop-item .si-buy:active:not(:disabled):not(.owned) { box-shadow: 0 1px 0 0 var(--cta-shadow); }
.shop-item .si-buy:disabled {
  background: linear-gradient(#6a5a48, #3e3226); color: #b8a890; border-color: var(--ink);
  box-shadow: 0 4px 0 0 #241a10; cursor: not-allowed;
}
.shop-item .si-buy.owned {
  background: linear-gradient(#7bd98a,#2f8f45); color: #0a2a0f; border-color: var(--ink);
  box-shadow: 0 4px 0 0 #1c4a22; cursor: default;
}
.shop-note {
  flex-shrink: 0; margin: 0 0 .2em; padding: 0 .3em; font-size: .75em; opacity: .65; font-style: italic;
}
/* Утренняя правка: тумблер DLC вкл/выкл — зелёная "Включено" (переиспользует
   .owned), красная "Выключено" — своя палитра студии (--alert-1/2), не
   голый #f00. */
.shop-item .si-buy.toggle-inline.off {
  background: linear-gradient(var(--alert-1), var(--alert-2)); color: #fff; border-color: var(--ink);
  box-shadow: 0 4px 0 0 var(--alert-shadow), 0 5px 8px rgba(0,0,0,.35);
}
/* Предупреждение о недоступном магазине Яндекса (ночь 07→08.09.2026) —
   тревожный акцент студии (--alert-1), но не отдельная кнопка, просто
   заметная строка под ценой DLC. */
.si-warning { color: var(--alert-1); opacity: 1; font-style: normal; font-weight: 600; }

/* Реклама за вознаграждение на экране итога миссии (ночь 07→08.09.2026) —
   отдельный визуальный акцент (не золотой CTA, чтобы не путать с «Далее»):
   бирюзово-зелёный, ассоциация с «бонус», кнопка явно называет и рекламу,
   и награду текстом (требование площадок, не просто "x3"). */
.ad-reward-row { display: flex; flex-direction: column; gap: .5em; margin: .2em 0; }
.ad-reward-btn {
  background: linear-gradient(#8fe6c0, #2a9a78); color: #04322a;
  box-shadow: 0 6px 0 0 #0e5c4a, 0 10px 18px rgba(0,0,0,.4);
  font-size: 1em;
}
.ad-reward-btn:active:not(:disabled) { box-shadow: 0 2px 0 0 #0e5c4a, 0 4px 8px rgba(0,0,0,.3); }
.ad-reward-btn:disabled {
  background: linear-gradient(#6a5a48, #3e3226); color: #b8a890; border-color: var(--ink);
  box-shadow: 0 4px 0 0 #241a10; cursor: not-allowed;
}
.ad-reward-amount { opacity: .85; font-size: max(12px, .9em); } /* r15 И18: не мельче 12px */
/* Утро 08.09.2026 (просьба основателя — «сделать слегка пульсирующей, как
   бы привлекая внимание»): лёгкое дыхание масштабом, не агрессивный bounce
   — эта кнопка встречается только на экране победы, поэтому применяется
   безусловно всем :not(:disabled) экземплярам (без ожидания рекламы
   класс .unavailable уже сам по себе — :disabled, пульс не идёт вхолостую). */
@keyframes adRewardPulse {
  0%, 100% { transform: scale(1); }
  50% { transform: scale(1.035); }
}
.ad-reward-btn:not(:disabled) { animation: adRewardPulse 1.7s ease-in-out infinite; }
.ad-reward-btn:not(:disabled):hover,
.ad-reward-btn:not(:disabled):active { animation-play-state: paused; }

/* Тестовая заглушка рекламы (площадка не подключена — локальный запуск/QA)
   — явно помечена как тест, чтобы никто не спутал с настоящим показом. */
.ad-test-overlay {
  position: fixed; inset: 0; z-index: 500; display: flex; align-items: center; justify-content: center;
  background: rgba(12,8,4,.88);
}
.ad-test-box {
  max-width: 22em; text-align: center; padding: 1.4em 1.6em; border-radius: var(--r-xl);
  background: var(--grain), linear-gradient(var(--royal-1), var(--royal-3)); border: 4px solid var(--bronze-dark);
  box-shadow: 0 12px 28px rgba(0,0,0,.5);
}
.ad-test-badge {
  display: inline-block; word-spacing: .16em; font-family: 'Lilita One', sans-serif; font-size: 1.1em;
  color: var(--gold-text); margin-bottom: .4em;
}
.ad-test-box p { font-size: .85em; opacity: .9; margin: 0 0 .8em; }
.ad-test-bar { height: .6em; border-radius: 999px; background: rgba(0,0,0,.35); border: 2px solid var(--ink); overflow: hidden; }
.ad-test-bar-fill {
  height: 100%; width: 0%; background: linear-gradient(90deg, var(--cta-1), var(--cta-2));
  animation: adTestFill 2.1s linear forwards;
}
@keyframes adTestFill { from { width: 0%; } to { width: 100%; } }

/* Громкое уведомление (ошибка покупки/рекламы) — не может быть тихим по
   требованию задачи: недоступный товар/реклама должны быть явно видны
   игроку, а не проглочены молча. */
.loud-notice {
  position: fixed; left: 50%; bottom: 1.5em; transform: translate(-50%, 20px);
  max-width: min(30em, 90vw); z-index: 600; text-align: center;
  padding: .8em 1.2em; border-radius: var(--r-lg);
  background: linear-gradient(var(--alert-1), var(--alert-2)); color: #3a0d02;
  border: 3px solid var(--ink); box-shadow: 0 8px 20px rgba(0,0,0,.5);
  font-weight: 600; opacity: 0; transition: opacity .25s ease, transform .25s ease;
}
.loud-notice.show { opacity: 1; transform: translate(-50%, 0); }

/* Утренняя правка основателя: сине-красная гамма была вчера жёстко вшита
   только в магазин — теперь это ПОКУПНАЯ тема на весь интерфейс (меню,
   магазин, HUD, пауза, итоги), по умолчанию — классика. Механизм: почти
   вся разметка уже красится через переменные (royal, cta, secondary,
   alert, gold-text — см. :root выше) — тема лишь переопределяет их под
   классом на body (см. THEMES в data.js, applyTheme() в game.js).
   Третья/четвёртая тема — просто ещё один такой блок плюс запись в
   THEMES, без правок разметки. */
body.theme-blue-red {
  --royal-1: #3a4fd0;
  --royal-2: #1a2470;
  --royal-3: #0c1442;
  --cta-1: #ff7a45;
  --cta-2: #e8391f;
  --cta-shadow: #8a1c0e;
  --secondary-1: #4f6fd8;
  --secondary-2: #26327a;
  --secondary-shadow: #131a4a;
  --alert-1: #ff5470;
  --alert-2: #c81f3f;
  --alert-shadow: #6e0f22;
  --gold-text: #8fe0ff;
}

/* ---------- Touch controls ---------- */
/* bottom-офсет учитывает safe-area (вырез/домашняя полоса) — без этого на
   части телефонов джойстик и кнопки атаки/спец-удара уходили за нижний
   край экрана и не отрисовывались (жалоба основателя, раунд 5). */
#touchControls { position: absolute; inset: 0; pointer-events: none; z-index: 4; }
#joyBase {
  position: absolute; left: 4%; bottom: calc(8% + env(safe-area-inset-bottom, 0px));
  width: 22vmin; height: 22vmin; max-width:110px; max-height:110px;
  border-radius: 50%; background: rgba(255,255,255,.1); border: 3px solid var(--ink);
  pointer-events: auto; touch-action: none;
}
#joyStick {
  position: absolute; left: 50%; top: 50%; width: 45%; height: 45%; border-radius: 50%;
  background: rgba(255,224,138,.65); border: 2px solid var(--ink); transform: translate(-50%,-50%);
}
/* Стадия 2 (баг-репорт, третье замечание про эту зону подряд): джойстик в
   самом левом нижнем углу стоял прямо на спрайте своей крепости/героя и
   полностью его закрывал весь матч. Сдвигаем правее+ниже (уходит с базы,
   ближе к самому краю экрана) и делаем заметно прозрачнее — тач-зона та
   же, видно сквозь неё. */
@media (pointer: coarse) {
  #joyBase {
    left: 9%; bottom: calc(3% + env(safe-area-inset-bottom, 0px));
    background: rgba(255,255,255,.06); border-color: rgba(26,18,10,.55);
  }
  /* r15 И16: 9% → 5% — +30…40px ширины нижнему ряду на телефоне; джойстик
     ниже линии земли и полупрозрачный, крепость им не закрывается. */
  #joyBase { left: max(12px, 5%, env(safe-area-inset-left, 0px)); }
  #joyStick { background: rgba(255,224,138,.4); border-color: rgba(26,18,10,.55); }
}
/* r15 И16 (куратор №5, блокер: «на 844×390 с м3 не нажать „Эра“ — #ageBtn
   под #touchPickaxe»): тач-кнопки больше не стоят в РЯД (4 кнопки ≈ 280px
   ширины — тулбару из 7 кнопок не хватало места), а веером вокруг большой
   «Атаки» в правом нижнем углу — как в мобильных экшенах: вихрь слева от
   атаки, кирка по диагонали, клич над атакой. Занятая ширина ≈ атака +
   одна малая кнопка. Радиус веера — не меньше 1.42 малой кнопки: тогда
   даже ПРЯМОУГОЛЬНИКИ соседних кнопок не пересекаются (проверка
   getBoundingClientRect в tmp/i16/overlap_check.py), не только круги. */
/* r15 И18 (куратор №6: «Кирка/Вихрь закрывают правую четверть поля — там
   крепость врага и точка спавна»): второстепенные кнопки — 48px и ОДНИМ
   рядом слева от «Атаки», по нижнему краю (кирка · вихрь · АТАКА): выше
   ~линии ног юнитов ничего, кроме самой «Атаки», не поднимается. Клич
   (только если куплен) — над «Атакой», у самого правого края. Не готовые
   (кулдаун) — полупрозрачные (.cd-wait, updateHud). */
.touch-actions {
  --tb: 48px;                        /* малая кнопка */
  --tm: clamp(60px, 19vmin, 92px);   /* атака */
  --tg: clamp(6px, 1.6vmin, 10px);
  position: absolute; right: max(10px, 3%); right: max(10px, 3%, env(safe-area-inset-right, 0px));
  bottom: calc(4% + env(safe-area-inset-bottom, 0px));
  width: calc(var(--tm) + 2 * (var(--tb) + var(--tg)));
  height: var(--tm);
  pointer-events: none;
}
.touch-actions .touch-btn { position: absolute; pointer-events: auto; transition: opacity .2s ease; }
.touch-actions .touch-btn.cd-wait { opacity: .75; }
#touchAttack { right: 0; bottom: 0; }
#touchSpecial { right: calc(var(--tm) + var(--tg)); bottom: 0; }
#touchPickaxe { right: calc(var(--tm) + 2 * var(--tg) + var(--tb)); bottom: 0; }
#touchCry { right: calc((var(--tm) - var(--tb)) / 2); bottom: calc(var(--tm) + var(--tg)); }
.touch-btn {
  position: relative; min-width: 48px; min-height: 48px; width: var(--tb, 15vmin); height: var(--tb, 15vmin); border-radius: 50%;
  background: var(--grain), linear-gradient(var(--secondary-1), var(--secondary-2)); color: var(--gold-text);
  font-size: 1.6em; touch-action: none; overflow: hidden;
  box-shadow: inset 0 1px 0 var(--edge-light), inset 0 -2px 0 rgba(0,0,0,.25), 0 4px 0 0 var(--secondary-shadow), 0 6px 10px rgba(0,0,0,.35);
  display: inline-flex; align-items: center; justify-content: center; padding: 0;
}
/* Раунд 15 (И1): иконки тач-кнопок — SVG из спрайта (были юникод-глифы и
   эмодзи), главная «Атака» крупнее и золотая — палец находит её не глядя. */
.touch-btn svg.ico { width: 52%; height: 52%; color: var(--gold-text); }
.touch-btn.touch-main { width: var(--tm, 19vmin); height: var(--tm, 19vmin); background: var(--grain), linear-gradient(var(--cta-1), var(--cta-2)); box-shadow: inset 0 1px 0 rgba(255,255,255,.4), 0 4px 0 0 var(--cta-shadow), 0 6px 10px rgba(0,0,0,.35); }
.touch-btn.touch-main svg.ico { color: #fff6d8; }
.touch-actions { align-items: flex-end; }
/* Кулдаун атаки/спец-удара/кирки — прямо на самих кнопках (раунд 5), не на
   перках нижней панели (та скрыта на мобильном, см. ниже). */
.cd-overlay-round {
  position: absolute; left: 0; right: 0; bottom: 0; height: 0%;
  background: rgba(0,0,0,.65); pointer-events: none;
}
/* На очень невысоких альбомных экранах (маленькие телефоны) поджимаем
   отступ и размер, чтобы кнопки не подъезжали к самому краю. */
@media (pointer: coarse) and (orientation: landscape) and (max-height: 420px) {
  #joyBase, .touch-actions { bottom: calc(4% + env(safe-area-inset-bottom, 0px)); }
}

/* Раунд 5 — на мобильном перк-кнопки спец-удара/кирки на нижней панели
   скрыты: их кулдаун переехал на кнопки атаки/спец/копать в touch-actions
   (см. cd-overlay-round выше); сама панель покупки юнитов (toolbar) на
   мобильном остаётся — это не «перк», а основной способ играть. */
@media (pointer: coarse) {
  #specialBtn, #pickaxeBtn, #cryBtn { display: none; }
}

/* Компактный режим на низких вьюпортах — та самая ситуация с телефона
   (широкий, но невысокий альбомный экран, адресная строка съедает высоту
   сильнее, чем в эмуляторе): уменьшаем отступы/шрифты/зазоры, чтобы контент
   в принципе имел шанс поместиться без скролла, а не полагались только на
   overflow-y выше. Блок специально в САМОМ КОНЦЕ файла: раньше стоял
   раньше базовых правил .panel-card/.shop-list/.menu-btn и МОЛЧА проигрывал
   им по правилу "при равной специфичности побеждает более поздний в
   источнике" — переопределения физически не применялись, хотя выглядели
   правильными по коду (найдено не чтением кода, а тем, что счётчик
   карточек в живом тесте не менялся при правках — верный признак, что CSS
   не доходит до элемента). */
@media (max-height: 460px) {
  .screen { gap: .35em; padding: .4em; }
  .screen h1 { font-size: 1.4em; margin: 0; }
  .panel-card, .popup-card { padding: .6em .8em; gap: .35em; max-height: 96vh; }
  .panel-card h2, .popup-card h2 { font-size: .95em; margin: 0; padding: .15em .8em; }
  .menu-btn { min-height: 36px; padding: .3em .8em; font-size: .85em; }
  /* Без этих трёх строк компактный режим уравнивал ВСЕ .menu-btn к одной
     высоте (правило выше побеждает более специфичные .primary-big/.small
     по specificity ниже, но здесь наоборот — эти явно перечисляют классы,
     специфичнее голого .menu-btn, поэтому сохраняют иерархию размеров
     даже на низком экране, не только на десктопе). */
  .menu-btn.primary-big { min-height: 46px; font-size: 1.25em; padding: .35em 1.4em; }
  .menu-btn.small { min-height: 28px; padding: .2em .7em; font-size: max(12px, .78em); }
  .shop-list { max-height: none; padding: .4em; gap: .35em; }
  .shop-item { padding: .35em .6em; min-height: 40px; gap: .15em; }
  .shop-section-title { margin: .3em 0 -.05em; padding: .1em .2em; }
  .shop-header-row .shop-currency { padding: .15em .5em; font-size: .85em; }
  /* Утро 08.09.2026 (баг-репорт основателя — скролл на экране победы на
     мобильном ландшафте): на финале главы там 2 кнопки рекламы, стопкой
     они не помещаются по высоте даже после общего сжатия. В ряд вместо
     столбца — та же суммарная информация, но одна строка высоты вместо
     двух. */
  .ad-reward-row { flex-direction: row; flex-wrap: wrap; justify-content: center; gap: .3em; }
  .ad-reward-btn { font-size: max(12px, .78em); padding: .3em .7em; min-height: 34px; }
  .result-reward-value { font-size: 1.3em; }
}

/* ===================================================================
   RTL (арабский) — ТЗ_ЛОКАЛИЗАЦИЯ_11_ЯЗЫКОВ.md, п.4: зеркалим ТОЛЬКО
   текстовые контейнеры интерфейса (меню/магазин/попапы/справка) — им
   i18n.js (I18N.applyDirection()) ставит dir="rtl" на .screen-элементы.
   Игровое поле (#stage/#hud/тулбар/джойстик) — отдельные соседние узлы
   вне .screen, dir на них не попадает и не должен: «слева игрок, справа
   враг» — часть игровой механики, не UI-условность (см. КОНЦЕПТ_ГДД.md,
   «Допущения»). Flex-строки (.si-header/.help-item/.playlist-row/…)
   мирроятся автоматически через CSS direction, отдельных row-reverse не
   нужно — переопределяем только явные text-align:left, которые иначе не
   подхватывают direction сами (по умолчанию у остальных элементов
   text-align:start и так следует за direction). */
[dir="rtl"] {
  direction: rtl;
}
[dir="rtl"] .shop-item,
[dir="rtl"] .playlist-name {
  text-align: right;
}

/* r15 И4 */
/* ===================================================================
   Раунд 15, И4 «Первые 60 секунд и отдача» (ТЗ_КАЧЕСТВО_CRAZYGAMES):
   туториал миссии 1 (js/tutorial.js), финал боя, экран итога со звёздами.
   =================================================================== */

/* --- Туториал: надпись ≤4 слов + прыгающая стрелка на цель --- */
.tut-layer { position: absolute; inset: 0; pointer-events: none; z-index: 7; overflow: hidden; }
.tut-bubble, .tut-arrow { position: absolute; left: 0; top: 0; will-change: transform; }
.tut-bubble {
  display: inline-flex; align-items: center; gap: .45em; white-space: nowrap;
  padding: .3em .8em; border-radius: 999px; border: 3px solid var(--ink);
  background: linear-gradient(#fff6d8, #ffd77a); color: #3a2200;
  word-spacing: .16em; font-family: 'Lilita One', 'Fredoka', sans-serif; font-size: max(16px, 3.3vh); letter-spacing: .02em;
  box-shadow: 0 4px 0 0 #7a4a10, 0 8px 18px rgba(0,0,0,.4);
}
.tut-keys { display: inline-flex; gap: .25em; }
.tut-keys kbd {
  display: inline-block; min-width: 1.5em; padding: .05em .4em; text-align: center;
  font-family: 'Fredoka', sans-serif; font-weight: 700; font-size: .72em; line-height: 1.4;
  background: #fff; color: #2a1608; border: 2px solid #2a1608; border-radius: .35em;
  box-shadow: 0 2px 0 #2a1608;
}
.tut-swipe { display: inline-flex; width: 2.1em; color: #2a1608; animation: tutSwipe 1s ease-in-out infinite; }
.tut-swipe svg { width: 100%; height: auto; }
.tut-arrow { width: max(30px, 6vh); }
.tut-arrow svg { display: block; width: 100%; height: auto; animation: tutBob .7s ease-in-out infinite alternate; filter: drop-shadow(0 3px 3px rgba(0,0,0,.45)); }
.tut-layer.tut-in .tut-text { animation: tutPop .35s cubic-bezier(.34,1.56,.64,1); display: inline-block; }
.tut-layer.tut-done .tut-bubble, .tut-layer.tut-done .tut-arrow { opacity: 0; transition: opacity .3s ease; }
@keyframes tutBob { from { transform: translateY(-10px); } to { transform: translateY(0); } }
@keyframes tutPop { 0% { transform: scale(.6); opacity: 0; } 100% { transform: scale(1); opacity: 1; } }
@keyframes tutSwipe { 0%, 100% { transform: translateX(-4px); } 50% { transform: translateX(4px); } }

/* --- Финал боя: HUD уходит, сцена крупным планом --- */
#hud .hud-bottom, #hud .hud-top { transition: opacity .35s ease; }
#hud.r15-finale .hud-bottom, #hud.r15-finale .hud-top { opacity: 0; pointer-events: none; }

/* --- Экран итога --- */
#resultCard { min-width: min(22em, 90vw); }
#resultCard h2.result-title {
  font-size: max(28px, 7.2vh); line-height: 1; padding: .05em .3em; margin: 0;
  text-transform: uppercase; letter-spacing: .04em;
  text-shadow: 0 3px 0 var(--ink), 0 0 .08em var(--ink), 0 6px 16px rgba(0,0,0,.55);
  animation: resultTitleIn .5s cubic-bezier(.34,1.56,.64,1);
}
#resultCard.win h2.result-title { color: var(--gold-text); }
#resultCard.lose h2.result-title { color: var(--alert-1); }
@keyframes resultTitleIn { 0% { transform: scale(1.8); opacity: 0; } 100% { transform: scale(1); opacity: 1; } }

.result-stars { display: flex; align-items: flex-end; justify-content: center; gap: .25em; margin: -.1em 0 0; }
.r-star { display: inline-block; width: max(34px, 7vh); height: max(34px, 7vh); color: #6b5a44; opacity: 0; animation: rStarIn .45s cubic-bezier(.34,1.56,.64,1) forwards; }
.r-star:nth-child(2) { width: max(42px, 8.8vh); height: max(42px, 8.8vh); margin-bottom: .25em; }
.r-star svg { width: 100%; height: 100%; display: block; filter: drop-shadow(0 3px 0 rgba(0,0,0,.45)); }
.r-star.on { color: #ffd23a; }
.r-star.on svg { filter: drop-shadow(0 3px 0 rgba(0,0,0,.45)) drop-shadow(0 0 8px rgba(255,210,58,.75)); }
@keyframes rStarIn {
  0% { opacity: 0; transform: scale(2.2) rotate(-25deg); }
  60% { opacity: 1; transform: scale(.9) rotate(6deg); }
  100% { opacity: 1; transform: scale(1) rotate(0); }
}

.result-reward.counted .result-reward-value { animation: rewardPulse .35s ease-out; }
@keyframes rewardPulse { 0% { transform: scale(1); } 45% { transform: scale(1.22); } 100% { transform: scale(1); } }

#resultText.result-tip {
  margin: 0; padding: .3em .9em; border-radius: 999px; max-width: 30em;
  background: rgba(0,0,0,.28); color: var(--paper); font-size: max(14px, 2.4vh);
}

/* Главная кнопка — первая и крупная; реклама x2 — вторичная под ней;
   «Магазин» / «В меню» — мелкие в ряд. */
#resultCard .menu-btn.result-main { font-size: max(20px, 3.6vh); min-height: max(52px, 8.5vh); padding: .4em 2.6em; min-width: 9em; }
#resultCard .ad-reward-row { margin: 0; }
#resultCard .ad-reward-btn {
  display: inline-flex; align-items: center; justify-content: center; gap: .4em;
  font-size: max(13px, 2.2vh); min-height: 0; padding: .35em 1.1em;
  box-shadow: 0 4px 0 0 #0e5c4a, 0 6px 12px rgba(0,0,0,.35);
}
#resultCard .ad-reward-btn .ad-ico { width: 1.25em; height: 1.25em; flex-shrink: 0; }
.result-minor-row { display: flex; gap: .6em; justify-content: center; flex-wrap: wrap; margin-top: .1em; }
.result-minor-row .menu-btn.small { min-width: 7em; }
#resultCard .result-minor-row .menu-btn.as-main {
  background: linear-gradient(var(--cta-1), var(--cta-2)); color: #4a2c00; text-shadow: 0 1px 0 rgba(255,255,255,.4);
  box-shadow: inset 0 -2px 0 rgba(0,0,0,.18), 0 6px 0 0 var(--cta-shadow), 0 10px 18px rgba(0,0,0,.45);
  font-size: max(18px, 3.2vh); min-height: max(48px, 8vh); padding: .35em 2em;
}

@media (max-height: 460px) {
  #resultCard h2.result-title { font-size: max(26px, 9vh); padding: 0 .3em; }
  #resultCard .menu-btn.result-main { font-size: 1.1em; min-height: 40px; padding: .25em 2em; }
  #resultCard .ad-reward-btn { font-size: max(12px, .78em); min-height: 30px; padding: .2em .8em; }
  .r-star { width: 30px; height: 30px; }
  .r-star:nth-child(2) { width: 36px; height: 36px; }
  #resultText.result-tip { font-size: .8em; }
}
@media (prefers-reduced-motion: reduce) {
  .tut-arrow svg, .tut-swipe { animation: none; }
}

/* r15 И10 */
/* --- Разовый тост «эпоха сбрасывается» (js/tutorial.js, ageTipTick) --- */
.age-tip {
  position: absolute; left: 50%; top: 17%; z-index: 7; pointer-events: none;
  transform: translateX(-50%); max-width: min(92vw, 36em);
  display: flex; align-items: center; gap: .55em;
  padding: .5em 1.1em; border-radius: 999px; border: 3px solid var(--ink);
  background: linear-gradient(#fff6d8, #ffd77a); color: #3a2200;
  font-family: 'Lilita One', 'Fredoka', sans-serif; word-spacing: .16em; letter-spacing: .02em;
  font-size: max(15px, 3vh); line-height: 1.2; text-align: center;
  box-shadow: 0 4px 0 0 #7a4a10, 0 10px 22px rgba(0,0,0,.45);
  animation: ageTipIn .45s cubic-bezier(.34,1.56,.64,1);
  transition: opacity .35s ease;
}
.age-tip svg.ico { width: 1.5em; height: 1.5em; flex-shrink: 0; color: #e0a030; vertical-align: 0; }
.age-tip.out { opacity: 0; }
@keyframes ageTipIn { 0% { transform: translateX(-50%) translateY(-14px) scale(.8); opacity: 0; } 100% { transform: translateX(-50%) translateY(0) scale(1); opacity: 1; } }

/* --- Праздник победы: лучи за карточкой, пульс звёзд, крупные кристаллы,
       конфетти — канвас #confettiCanvas поверх (game.js, startConfetti) --- */
#screenResult { overflow: hidden; }
.result-rays {
  position: absolute; left: 50%; top: 50%; width: 170vmax; height: 170vmax; z-index: 0;
  margin: -85vmax 0 0 -85vmax; pointer-events: none; opacity: 0;
  background: repeating-conic-gradient(from 0deg, rgba(255,226,140,.26) 0deg 7deg, rgba(255,226,140,0) 7deg 20deg);
  -webkit-mask-image: radial-gradient(circle, #000 0, rgba(0,0,0,.85) 14%, transparent 42%);
  mask-image: radial-gradient(circle, #000 0, rgba(0,0,0,.85) 14%, transparent 42%);
}
#screenResult.celebrate .result-rays { opacity: 1; animation: raysSpin 26s linear infinite, raysFade .6s ease-out; }
@keyframes raysSpin { to { transform: rotate(360deg); } }
@keyframes raysFade { from { opacity: 0; } to { opacity: 1; } }
#screenResult #resultCard { position: relative; z-index: 2; }
/* Конфетти под карточкой: не перечёркивает текст кнопок «Далее»/«x2». */
#confettiCanvas { position: absolute; inset: 0; width: 100%; height: 100%; z-index: 1; pointer-events: none; }
#screenResult.celebrate .result-reward-value { font-size: 2.5em; color: #bff0ff; text-shadow: 0 3px 0 var(--ink), 0 0 14px rgba(111,216,250,.6), 0 6px 14px rgba(0,0,0,.5); }
#screenResult.celebrate .result-reward-label { font-size: max(13px, .95em); opacity: .95; }
.r-star.on { animation: rStarIn .45s cubic-bezier(.34,1.56,.64,1) forwards, rStarPulse 1.5s ease-in-out infinite; }
@keyframes rStarPulse { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.14); } }
/* Реклама x2 — появляется, когда счётчик кристаллов докрутился: на экране
   никогда нет «+4» рядом с обещанием «x2 (+10)» (куратор, окно поражения). */
#resultAdRow.ad-row-in { animation: adRowIn .35s ease-out both; }
@keyframes adRowIn { from { opacity: 0; transform: translateY(8px); visibility: hidden; } to { opacity: 1; transform: none; visibility: visible; } }
@media (max-height: 460px) {
  #screenResult.celebrate .result-reward-value { font-size: 1.9em; }
}
@media (prefers-reduced-motion: reduce) {
  #screenResult.celebrate .result-rays { animation: raysFade .6s ease-out; }
  .r-star.on { animation: rStarIn .45s ease forwards; }
}

/* --- Карта глав: запертые главы читаемы (≥13px, светлый текст) --- */
.chapter-row.chapter-locked { opacity: 1; }
.chapter-row.chapter-locked .chapter-label {
  font-size: max(13px, .95em); font-weight: 600; opacity: 1; color: #e2d4b4;
  background: rgba(0,0,0,.26); border-color: rgba(255,225,180,.3);
}
.chapter-row.chapter-locked .chapter-label svg.ico { color: #d2bf98; }
.chapter-label { font-size: max(13px, .9em); }

/* r15 И12 */
/* ===================================================================
   Раунд 15, И12 — замечания куратора CrazyGames №3 по интерфейсу.
   Блок в самом конце файла: побеждает и базовые правила, и компактный
   режим @media (max-height: 460px) выше (тот же урок, что в его шапке).
   =================================================================== */

/* --- П3: любой видимый текст ≥12px на 800×450 (база .screen там 13px,
       и все .75–.85em-подписи уходили в 9.8–11px). Пол — max(12px, …),
       на больших экранах пропорции прежние. --- */
.shop-item .si-desc { font-size: max(12px, .8em); }
.si-tier { font-size: max(12px, .78em); }
.si-stat { font-size: max(12px, .82em); }
.shop-section-title { font-size: max(12.5px, .85em); }
.shop-locked-strip { font-size: max(12px, .8em); }
.shop-note { font-size: max(12px, .75em); }
.shop-item .si-buy { font-size: max(13px, .9em); }
.time-select-group .si-buy, .toggle-inline { font-size: max(12px, .85em); }
.icon-btn.small { font-size: max(12px, .8em); }
.result-reward-label { font-size: max(12px, .85em); }
#resultText.result-tip { font-size: max(13px, 2.4vh); }
.tut-keys kbd { font-size: max(13px, .72em); } /* r15 И18 */
.link-btn { font-size: max(12px, .75em); }
.ad-test-box p { font-size: max(12px, .85em); }
@media (max-height: 460px) {
  .panel-card h2, .popup-card h2 { font-size: max(14px, .95em); }
  .menu-btn { font-size: max(12px, .85em); }
  #resultText.result-tip { font-size: 13px; }
  #resultCard .ad-reward-btn, .ad-reward-btn { font-size: max(12.5px, .78em); }
  .shop-header-row .shop-currency { font-size: max(12px, .85em); }
}
/* Иконки из SVG-спрайта вместо эмодзи в строках справки (П10). */
.shop-section-title svg.ico { width: 1.1em; height: 1.1em; margin-right: .35em; vertical-align: -.2em; }
svg.ico.ico-inline { width: 1.25em; height: 1.25em; vertical-align: -.28em; margin: 0 .1em; }

/* --- П8: пауза — заметная панель, кнопки ≥48px, текст ≥15px --- */
#screenPause .popup-card {
  min-width: min(92vw, 23em); padding: 1.1em 1.6em 1.3em; gap: .75em;
}
#screenPause .popup-card h2 { font-size: max(22px, 1.6em); padding: .1em .8em; }
#screenPause .menu-btn { min-height: 50px; font-size: max(16px, 1.05em); padding: .35em 1.2em; width: 100%; }
#screenPause #btnResume { min-height: 56px; font-size: max(19px, 1.3em); }
#screenPause .mute-row { width: 100%; gap: .6em; }
#screenPause .mute-row .menu-btn { font-size: max(15px, .95em); padding: .3em .6em; }
/* Справка — шапка закреплена (как у магазина): заголовок и крестик всегда
   на виду, прокручивается только список. */
#screenHelp .panel-card { padding-top: 0; }
.help-header-row {
  display: flex; align-items: center; justify-content: center; width: 100%; flex-shrink: 0;
  position: sticky; top: 0; z-index: 2; margin: 0 -1.4em; padding: .5em 3.8em .4em; min-height: 58px;
  background: var(--grain), linear-gradient(var(--royal-1), var(--royal-3));
  border-bottom: 2px solid var(--edge-light); border-radius: var(--r-xl) var(--r-xl) 0 0;
}
.help-header-row h2, #screenHelp .help-header-row h2 { margin: 0; font-size: max(17px, 1.25em); padding: 0 .6em; }
.help-close-btn { position: absolute; right: .6em; top: 50%; transform: translateY(-50%); width: 44px; height: 44px; }
.help-close-btn svg.ico { width: 50%; height: 50%; color: var(--paper); }
@media (hover: hover) and (pointer: fine) { .help-close-btn:hover { transform: translateY(-50%) scale(1.07); } }
.help-close-btn:active { transform: translateY(-50%) scale(.95); }
@media (max-height: 460px) { .help-header-row { padding: .35em 3.6em .3em; min-height: 54px; } }

/* --- П5: «Воскресить 30», когда золота не хватает — приглушённая кнопка
       с заливкой накопленного и недостачей «(−12)». --- */
.hero-revive-box .menu-btn.is-short {
  background:
    linear-gradient(90deg, rgba(255,215,122,.38) 0 var(--fill, 0%), transparent var(--fill, 0%)),
    linear-gradient(#6a5a48, #3e3226);
  color: #cbbd9f; text-shadow: 0 1px 1px rgba(0,0,0,.6); cursor: not-allowed;
  box-shadow: inset 0 -2px 0 rgba(0,0,0,.25), 0 4px 0 0 #241a10, 0 6px 12px rgba(0,0,0,.35);
  filter: none; transform: none;
}
.hero-revive-box .menu-btn.is-short .gold-dot { filter: grayscale(.6) brightness(.85); }
.buyback-short { color: #ffb08a; word-spacing: 0; white-space: nowrap; }
/* r15 И18: «Воскресить 30 · не хватает 3» — на таче (кнопка под своей
   полосой HP, узкая колонка) недостача второй строкой внутри кнопки. */
.bb-main { display: inline-flex; align-items: center; gap: .3em; white-space: nowrap; }
.hero-slot-top .hero-revive-box { flex-shrink: 0; }
.hero-revive-box.in-slot .menu-btn.is-short { flex-direction: column; justify-content: center; gap: 0; line-height: 1.08; padding-top: .12em; padding-bottom: .15em; }
.hero-revive-box.in-slot .buyback-short { text-align: center; font-size: 13px; word-spacing: .25em; }
.hero-revive-box.in-slot .bs-sep { display: none; }
.hero-revive-box .menu-btn:not(.is-short) .buyback-short { display: none; }
@keyframes buybackDeny { 0%,100% { transform: translateX(0); } 25% { transform: translateX(-4px); } 75% { transform: translateX(4px); } }
.hero-revive-box .menu-btn.denied { animation: buybackDeny .2s; }

/* --- П9: полоска у портрета героя — с подписью «HP» (как у крепостей). --- */
.hero-mini-bar-col { display: flex; flex-direction: column; align-items: flex-start; margin-left: -.55em; }
.hero-mini-bar-col .bar-hp-hero { margin-left: 0; }
.hero-mini-label {
  padding-left: .75em; margin-bottom: .1em; line-height: 1;
  font-family: 'Lilita One', 'Fredoka', sans-serif; font-size: max(13px, .72em); letter-spacing: .06em; color: #fff; /* r15 И18: 13px */
  text-shadow: 0 1px 0 var(--ink), 1px 0 0 var(--ink), -1px 0 0 var(--ink), 0 0 3px rgba(0,0,0,.7);
}
.hero-mini.dead .hero-mini-label { color: #ffb08a; }

/* --- П7: подсказки «Залп»/«Новая эра» — компактно у самой кнопки, не над
       центром боя; сама кнопка подсвечена, пока висит подсказка. --- */
.tut-layer.tut-brief .tut-arrow { width: max(22px, 4.4vh); }
.tut-layer.tut-brief .tut-bubble { font-size: max(14px, 2.6vh); padding: .2em .65em; }
@keyframes tutGlow { 0%,100% { box-shadow: 0 0 0 2px var(--gold-text), 0 0 10px 2px rgba(255,215,122,.55); } 50% { box-shadow: 0 0 0 3px #fff3c4, 0 0 20px 6px rgba(255,215,122,.9); } }
.tool-btn.tut-glow { animation: tutGlow 1s ease-in-out infinite; }

/* --- П6: ряд рекламы на итоге не держит пустое место до своего появления --- */
#resultAdRow.ad-row-wait { display: none; }
#resultAdRow.ad-row-in { animation: adRowGrow .35s ease-out both; }
@keyframes adRowGrow { from { opacity: 0; transform: translateY(6px) scale(.96); } to { opacity: 1; transform: none; } }

/* r15 И14 */
/* --- Условия звёзд под звёздами на экране итога (game.js, renderStarGoals):
       галочка — выполнено, пустой кружок — нет; текст ≥12px на 800×450. --- */
.result-star-goals {
  list-style: none; margin: 0; padding: 0;
  display: flex; flex-direction: column; align-items: flex-start; gap: .1em;
  font-family: 'Fredoka', 'Segoe UI', sans-serif; font-weight: 600;
  font-size: max(13px, 2.3vh); line-height: 1.2;
}
.result-star-goals li {
  display: flex; align-items: center; gap: .4em;
  opacity: 0; animation: starGoalIn .3s ease-out forwards;
}
.result-star-goals svg { width: 1.15em; height: 1.15em; flex-shrink: 0; }
.result-star-goals li.ok { color: #fff3cf; }
.result-star-goals li.ok svg circle { fill: #3bb26b; stroke: var(--ink); stroke-width: 1.5; }
.result-star-goals li.ok svg path { fill: none; stroke: #fff; stroke-width: 2.4; stroke-linecap: round; stroke-linejoin: round; }
.result-star-goals li.no { color: rgba(243,231,200,.72); }
.result-star-goals li.no svg circle { fill: rgba(0,0,0,.25); stroke: rgba(243,231,200,.55); stroke-width: 1.6; }
@keyframes starGoalIn { from { opacity: 0; transform: translateX(-6px); } to { opacity: 1; transform: none; } }
@media (max-height: 460px) {
  .result-star-goals { font-size: 12.5px; gap: 0; }
}
@media (prefers-reduced-motion: reduce) {
  .result-star-goals li { animation: none; opacity: 1; }
}

/* r15 И13 */
/* Предупреждение о копящемся золоте (куратор №4: «штраф за копление не виден»):
   плашка над счётчиком золота, когда золота > 70 % порога GOLD_HOARD
   (game.js, updateHud); сам счётчик пульсирует красным. */
.gold-row { position: relative; }
/* r15 И20 (куратор №7: «счётчик золота висит над полем боя и перекрывает
   юнитов»): тёмная полупрозрачная капсула под счётчиком — читается как
   элемент интерфейса, а не как надпись поверх бойцов; фигуры за ней видны.
   Пульс предупреждения/штрафа (goldWarnPulse/goldHoardPulse) по-прежнему
   перекрашивает фон капсулы. */
.gold-row {
  background: rgba(26,18,10,.58); border: 1.5px solid rgba(255,215,122,.3);
  box-shadow: 0 2px 6px rgba(0,0,0,.3);
  padding: .02em .55em .02em .35em; font-size: 1.4em;
}
/* r15 И16 (куратор №5: «плашка „Spend gold…“ висит над полем»): не над
   счётчиком, а СПРАВА от него, по центру строки золота — счётчик не
   сдвигается. Постоянно — 2–3 слова (hud.goldSpendShort/goldHoardShort),
   полная фраза — только первые 4 с (.full, updateGoldWarn), с переносом в
   пределах --warn-max (на таче — до тач-кнопок, layoutHudBottomRow). */
.gold-warn {
  position: absolute; left: calc(100% + 4px); top: 50%; transform: translateY(-50%);
  width: max-content; max-width: var(--warn-max, 240px); white-space: nowrap; text-align: left;
  font-family: 'Fredoka', 'Segoe UI', sans-serif; font-weight: 700; font-size: max(12px, .5em); line-height: 1.2;
  color: #fff4e6; text-shadow: 0 1px 0 rgba(0,0,0,.55); word-spacing: normal; letter-spacing: 0;
  background: linear-gradient(#d4492a, #9c2412); border: 2px solid var(--ink); border-radius: 10px;
  padding: .2em .6em; box-shadow: 0 2px 0 rgba(0,0,0,.35); pointer-events: none;
  animation: goldWarnIn .25s ease-out;
}
.gold-warn.full { white-space: normal; }
.gold-warn[hidden] { display: none; }
.gold-warn::after {
  content: ''; position: absolute; right: 100%; top: 50%; transform: translateY(-50%);
  border: 5px solid transparent; border-right-color: #b8331a;
}
@keyframes goldWarnIn { from { opacity: 0; transform: translate(-6px, -50%); } to { opacity: 1; transform: translate(0, -50%); } }
@keyframes goldWarnPulse { 0%,100% { background: rgba(200,64,30,.15); } 50% { background: rgba(200,64,30,.55); } }
.gold-row.gold-warn-on { color: #ffd0b8; animation: goldWarnPulse 1s ease-in-out infinite; }
.gold-row.gold-warn-on.gold-hoard-flash { animation: goldHoardPulse .35s ease-in-out 4; } /* сработавший штраф важнее */
@media (prefers-reduced-motion: reduce) {
  .gold-warn { animation: none; }
  .gold-row.gold-warn-on { animation: none; background: rgba(200,64,30,.4); }
}

/* r15 И16 */
/* --- Нижний ряд на таче: если тулбар не влез между джойстиком и веером
       тач-кнопок (layoutHudBottomRow меряет scrollWidth), сначала кнопки
       44px с узкими зазорами (.row-compact), потом «Залп»/«Эра» — строкой
       НАД тулбаром (.row-stack, wrap-reverse). Тач-цели не меньше 44px. --- */
/* «Залп»/«Эра» — ширина по подписи (DE «Zeitalter», ES «Andanada», EN
   «Volley» не влезали в 44–58px и обрезались), не уже соседних кнопок. */
#volleyBtn, #ageBtn { width: auto; padding-left: .35em; padding-right: .35em; }
#volleyBtn .tool-cost, #ageBtn .tool-cost { white-space: nowrap; }
.hud-bottom-row.row-compact { gap: 4px; }
.hud-bottom-row.row-compact .toolbar { gap: 3px; }
.hud-bottom-row.row-compact .tool-btn { min-width: 44px; min-height: 44px; width: 44px; height: 46px; }
.hud-bottom-row.row-compact #volleyBtn, .hud-bottom-row.row-compact #ageBtn { width: auto; padding-left: 4px; padding-right: 4px; }
.hud-bottom-row.row-compact .tool-icon-canvas { width: 28px; height: 31px; }
.hud-bottom-row.row-stack { flex-wrap: wrap-reverse; row-gap: 6px; align-content: flex-end; }
.hud-bottom-row.row-stack .toolbar { flex-basis: 100%; justify-content: center; }
/* r15 И20 (куратор №7: на 667×375 «Volley»/«Age» — вторым рядом над полем):
   когда ряд не влезает, «Залп» и «Эра» — в правом тач-кластере круглыми
   кнопками над «Вихрь»/«Кирка» (placeVolleyAge в game.js). Подпись скрыта,
   как у остальных тач-кнопок; у «Эры» — кольцо опыта по кромке круга. */
.touch-actions > #volleyBtn, .touch-actions > #ageBtn {
  position: absolute; pointer-events: auto;
  width: var(--tb); height: var(--tb); min-width: 44px; min-height: 44px;
  border-radius: 50%; padding: 0; font-size: 1em;
  bottom: calc(var(--tb) + var(--tg));
}
.touch-actions > #volleyBtn { right: calc(var(--tm) + var(--tg)); }
.touch-actions > #ageBtn { right: calc(var(--tm) + 2 * var(--tg) + var(--tb)); }
.touch-actions > #volleyBtn .tool-cost, .touch-actions > #ageBtn .tool-cost { display: none; }
.touch-actions > #volleyBtn .tool-icon svg.ico, .touch-actions > #ageBtn .tool-icon svg.ico { width: 26px; height: 26px; }
.touch-actions > #volleyBtn .cd-overlay { border-radius: 0; }

/* --- «Крепость атакуют!» (updateFortAlert в game.js): тост под
       заголовком, мигание полосы HP своей крепости, лёгкий красный край
       экрана со стороны своей крепости. Ничего не ловит касания. --- */
.fort-alert {
  position: absolute; top: calc(100% - .1em); left: 50%; transform: translateX(-50%); z-index: 6;
  pointer-events: none; white-space: nowrap;
  font-family: 'Lilita One', 'Fredoka', sans-serif; font-size: max(14px, 1em); letter-spacing: .03em; word-spacing: .16em;
  color: #fff4e6; text-shadow: 0 1px 0 var(--ink), 1px 0 0 var(--ink), -1px 0 0 var(--ink);
  background: linear-gradient(#d4492a, #8e1f10); border: 2px solid var(--ink); border-radius: 999px;
  padding: .2em .9em .25em; box-shadow: 0 3px 0 rgba(0,0,0,.35), 0 0 14px rgba(220,60,30,.55);
}
.fort-alert.show { animation: fortAlertIn 2.4s ease-out both; }
@keyframes fortAlertIn {
  0% { opacity: 0; transform: translate(-50%, -8px) scale(.85); }
  8% { opacity: 1; transform: translate(-50%, 0) scale(1.06); }
  14% { transform: translate(-50%, 0) scale(1); }
  82% { opacity: 1; }
  100% { opacity: 0; transform: translate(-50%, -4px); }
}
@keyframes fortBarBlink { 0%,100% { box-shadow: 0 1px 3px rgba(0,0,0,.35); background: rgba(26,18,10,.55); } 50% { box-shadow: 0 0 10px 3px rgba(255,60,30,.85); background: rgba(200,40,20,.75); } }
.core-player .bar.fort-hit { animation: fortBarBlink .3s ease-in-out 2; }
/* r15 И18 (куратор №6: «полоса HP своей крепости оранжевая при 1000/1000»):
   раньше filter стоял на классе .fort-hit, а класс снимался только
   следующим ударом — после сигнала полоса так и оставалась оранжевой.
   Теперь красный оттенок — только внутри анимации мигания, по её концу
   заливка снова зелёная (и game.js снимает класс по animationend). */
.core-player .bar.fort-hit .bar-fill { animation: fortFillBlink .3s ease-in-out 2; }
@keyframes fortFillBlink { 0%,100% { filter: none; } 50% { filter: hue-rotate(-100deg) saturate(1.6); } }
#fortEdge {
  position: absolute; left: 0; top: 0; bottom: 0; width: 14%; pointer-events: none; z-index: 3; opacity: 0;
  background: linear-gradient(90deg, rgba(220,40,20,.42), rgba(220,40,20,0));
}
#fortEdge.pulse { animation: fortEdgePulse 1.1s ease-in-out 2; }
@keyframes fortEdgePulse { 0%,100% { opacity: 0; } 45% { opacity: 1; } }

/* --- Заголовок миссии показывает ТЕКУЩУЮ эпоху игрока — при «Новой эре»
       короткая вспышка (updateHudStatic). --- */
.mission-title.age-pop { animation: titleAgePop .9s ease-out; }
/* Эпоха в заголовке теперь меняется в бою — длинные «Бронзовый век»,
   «Industriezeitalter» на телефоне обрезались многоточием: до 2 строк. */
/* r15 И18 (куратор №6: «„Mission 3 — Bronze Age“ переносится на 2 строки на
   667×375»): снова ОДНА строка. fitMissionTitle() в game.js ужимает кегль
   (не мельче 12px) и межбуквенные отступы, пока строка не влезет; если не
   влезла и на 12px — многоточие. */
.mission-title {
  white-space: nowrap; line-height: 1.15; text-overflow: ellipsis; overflow: hidden;
}
.mission-title.fit-tight { letter-spacing: 0; word-spacing: 0; padding-left: .15em; padding-right: .15em; }
/* Узкий альбомный телефон: заголовку — больше места между полосами HP
   («Миссия 13 — Индустриальный век» на 667×375 иначе не влезал и в 12px). */
@media (max-width: 780px) and (orientation: landscape) {
  .hud-top { gap: .35em; }
  .core-box { width: 24.5%; }
}
@keyframes titleAgePop { 0% { transform: scale(1); color: #fff; } 25% { transform: scale(1.18); color: #fff3c4; } 100% { transform: scale(1); } }

@media (prefers-reduced-motion: reduce) {
  .fort-alert.show { animation: none; }
  #fortEdge.pulse { animation: none; }
  .mission-title.age-pop { animation: none; }
}

/* --- r15 И18: строка цели эпох под заголовком на старте боя
       (showAgeGoal/updateAgeGoal в game.js) — «Старт: Каменный век → дойди
       до Железного!». Мелко, ≥13px, не ловит касания, тает через ~5 с. --- */
.age-goal {
  position: absolute; top: 2.2em; left: 50%; transform: translateX(-50%); z-index: 5;
  pointer-events: none; white-space: nowrap; max-width: 92vw; overflow: hidden; text-overflow: ellipsis;
  font-family: 'Fredoka', sans-serif; font-weight: 600; font-size: max(13px, .82em); line-height: 1.25;
  color: #fff4e0; text-shadow: 0 1px 0 var(--ink), 1px 0 0 var(--ink), -1px 0 0 var(--ink);
  background: rgba(26,18,10,.55); border-radius: 999px; padding: .12em .8em .15em;
  opacity: 1; transition: opacity .4s ease;
  animation: ageGoalIn .35s ease-out backwards;
}
.age-goal.out { opacity: 0; }
@keyframes ageGoalIn { from { opacity: 0; transform: translate(-50%, -4px); } to { opacity: 1; transform: translate(-50%, 0); } }
@media (prefers-reduced-motion: reduce) { .age-goal { animation: none; } }

/* --- r15 И20 п.8: карта глав — «Назад» в закреплённой шапке (как у
       магазина: .shop-header-row sticky, свой фон), без верхнего отступа
       карточки — иначе над прилипшей шапкой просвечивал бы список. --- */
#screenMissions .panel-card { padding-top: 0; }
.chapters-header-row { width: calc(100% + 2.8em); }
@media (max-height: 460px) {
  #screenMissions .shop-header-row { margin: 0 -.8em; padding: .45em .8em .4em; width: calc(100% + 1.6em); }
}
/* Узел карты глав: невидимый круг-«мишень» r=27 (chapterRowSVG) — цель
   касания ≥44px при визуальном круге 34px. */
.trail-node-svg .node-hit { fill: transparent; stroke: none; }
.trail-node-svg:not(.locked):hover .node-hit, .trail-node-svg:not(.locked):active .node-hit { transform: none; }

/* --- r15 И20 п.9 (куратор №7: «мелкие тач-цели: Back в магазине 48×28,
       вкладки магазина 31, Playlist 28»): на тач-экранах все интерактивные
       элементы меню/настроек/магазина/глав/плейлиста/паузы/справки/итога —
       не ниже 44px (проверка: tmp/i20/touch_targets.py). Блок в конце файла —
       побеждает компактный режим @media (max-height: 460px) выше. --- */
@media (pointer: coarse) {
  .menu-btn, .menu-btn.small, .menu-btn.primary-big, .shop-tab, .si-buy, .shop-item .si-buy,
  .time-select-group .si-buy, .toggle-inline, .icon-btn, .icon-btn.small, .setting-row,
  .ad-reward-btn, .link-btn { min-height: 44px; }
  .menu-btn.small, .icon-btn.small, .si-buy { min-width: 44px; }
  #screenMenu .menu-btn.primary-big { min-height: 50px; }
  #screenMenu .menu-row .menu-btn { min-height: 44px; }
  .shop-tab { padding-top: .3em; padding-bottom: .3em; }
  /* переключатель трека в плейлисте — крупнее (56×44), бегунок по размеру */
  .playlist-row .toggle-switch { width: 56px; height: 44px; }
  .playlist-row .toggle-switch::after { width: 36px; height: 36px; top: 2px; left: 2px; }
  .playlist-row .toggle-switch.on::after { transform: translateX(12px); }
}

/* r15 И21 */
/* --- П6 (куратор №8: «на итоге на телефоне „Next“ 40px, „Watch ad“ 30px»):
       правила #resultCard (компактный режим max-height: 460px) сильнее
       общего пола 44px И20 — здесь тем же весом, в конце файла. Карточка
       влезает в 667×375 (проверка: tmp/i21/result_fit.py). --- */
@media (pointer: coarse) {
  #resultCard .menu-btn.result-main, #resultCard .ad-reward-btn,
  #resultCard .result-minor-row .menu-btn { min-height: 44px; }
}
/* --- П7 (куратор №8: «кольцо „Эры“ закрывает подпись — „Aɢe“»): прогресс
       опыта — рамкой по кромке кнопки (conic-gradient по --age-p, game.js
       updateHudR15), середина прозрачна (маска) — подпись и иконка чистые.
       В тач-кластере кнопка круглая — рамка тоже. --- */
.age-ring {
  position: absolute; inset: 0; z-index: 0; pointer-events: none;
  border-radius: calc(var(--r-lg) - 3px); padding: 3px;
  background: conic-gradient(var(--gold-text, #f6c94a) calc(var(--age-p, 0) * 360deg), rgba(0,0,0,.4) 0);
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask: linear-gradient(#000 0 0) content-box exclude, linear-gradient(#000 0 0);
}
.touch-actions > #ageBtn .age-ring { border-radius: 50%; }
#ageBtn .tool-icon { position: relative; z-index: 1; }
/* компактная кнопка 44×46: подпись на 2px выше — строка не заходит на рамку */
.hud-bottom-row.row-compact #ageBtn { padding-top: 0; padding-bottom: 4px; }
