/*
 * Постоянный каркас приложения.
 *
 * Раньше этих правил не было: и панель игрока, и обёртка раздела получали
 * размеры из строчных стилей, которые дописывал JavaScript на каждом переходе
 * (<mobitva style="...">, <div class="child_mb" style="...">). Из-за этого
 * ширина содержимого на первой загрузке отличалась от ширины после первого
 * перехода, а панель пересобиралась целиком.
 *
 * Теперь размеры заданы один раз здесь, разметка не пересобирается.
 */

/* ------------------------------------------------------------------ */
/* Экран стоит на месте                                                */
/* ------------------------------------------------------------------ */

/*
 * Три беды Android, от которых экран ездил сам собой. Размер страницы закреплён
 * в шапке (system/header.php), но одного viewport мало - остальное лечится тут.
 *
 * 1. Подтягивание страницы вниз. Потянув палец вниз в самом верху, игрок
 *    запускал обновление страницы: экран уезжал вниз и отпружинивал обратно. В
 *    бою, где по экрану водят пальцем постоянно, это срабатывало само.
 *    overscroll-behavior: none убирает и подтягивание, и отпружинивание у краёв.
 *
 * 2. Двойное касание. По кнопкам в игре бьют часто, и два быстрых нажатия
 *    Android принимает за просьбу увеличить. touch-action: manipulation говорит
 *    браузеру: касания - только для нажатий и прокрутки, увеличивать не нужно.
 *    Заодно пропадает задержка в треть секунды, которую браузер держал, ожидая
 *    второго касания, - нажатия стали отзываться сразу.
 *
 * 3. Свой размер шрифта. При повороте телефона Android самовольно укрупняет
 *    текст, чтобы тот "лучше читался", и разметка расползается.
 *    text-size-adjust: 100% запрещает эту самодеятельность.
 *
 * Прокрутку НЕ отключаем: сцена боя выше экрана, её доматывают, и без прокрутки
 * нижний ряд с кнопками удара стал бы недосягаем.
 */
html {
    overscroll-behavior: none;
    -webkit-text-size-adjust: 100%;
    text-size-adjust: 100%;
    /*
     * ЭКРАН НЕ ДОЛЖЕН ПРЫГАТЬ ПРИ ПЕРЕХОДЕ МЕЖДУ РАЗДЕЛАМИ.
     *
     * Симптом был такой: на странице входа ошибка "Пользователь не существует"
     * или нажатие "Начать игру" дёргало всю страницу вверх-вниз.
     *
     * Причина - смена высоты документа. Раздел вставляется через $body.html():
     * на один кадр старая разметка уже убрана, а новая ещё не разложена, и
     * документ становится короче экрана. Браузер тут же убирает полосу
     * прокрутки и подтягивает содержимое, потом новая разметка приходит,
     * документ снова удлиняется - полоса возвращается. Два сдвига подряд и
     * читаются как прыжок.
     *
     * Лечим двумя правилами вместе:
     *  - min-height: 100% держит документ не короче экрана, поэтому короткий
     *    раздел (форма входа, сообщение об ошибке) не сокращает страницу;
     *  - scrollbar-gutter: stable резервирует место под полосу прокрутки
     *    постоянно, поэтому её появление и исчезновение больше не сдвигает
     *    содержимое по горизонтали.
     *
     * На бой это не влияет: там min-height у .game-body_battle больше.
     *
     * ПОЧЕМУ ДВЕ СТРОКИ ВЫСОТЫ, А НЕ ОДНА - ЭТО ПРО ПРЫЖКИ НА ТЕЛЕФОНЕ.
     *
     * На ПК прыжок ушёл, а на телефоне остался - и виноват был как раз
     * min-height: 100%. На телефоне 100% считается от ПОЛНОЙ высоты экрана, то
     * есть от той, что бывает при СПРЯТАННОЙ адресной строке. Пока строка
     * видна, видимая часть ниже - и документ выходит чуть выше неё. Появляется
     * прокрутка на несколько десятков точек, телефон на неё отвечает тем, что
     * прячет адресную строку, видимая высота меняется - и страница дёргается.
     * Стоит отпустить палец, строка возвращается, и дёргает снова. На ПК
     * адресной строки в странице нет, поэтому там всё было ровно.
     *
     * svh - это высота при ВИДИМОЙ адресной строке, самая маленькая из
     * возможных. Документ при ней гарантированно не выше видимой части, лишняя
     * прокрутка не появляется, адресная строка не прячется - и прыжка нет.
     *
     * Первая строка (100%) оставлена запасом для старых браузеров, которые про
     * svh не знают: они её применят, а новые перебьют второй строкой.
     */
    min-height: 100%;
    min-height: 100svh;
    scrollbar-gutter: stable;
    /*
     * ВБОК ИГРА НЕ ТЯНЕТСЯ.
     *
     * Симптом: страницу можно было увести вправо пальцем, и часть разметки
     * уезжала за край - кнопки оказывались за пределами видимого.
     *
     * Причина - разделы, которые чуть шире экрана: сцена боя, широкие таблицы
     * списков, длинные имена без пробелов. Любой такой блок делает документ
     * шире окна, и браузер тут же разрешает горизонтальную прокрутку по всей
     * игре, даже там, где лишней ширины нет.
     *
     * overflow-x: clip обрезает вылезающее и НЕ создаёт прокрутку. Взят
     * именно clip, а не hidden: hidden на корневом элементе превращает его в
     * область прокрутки и ломает position: fixed у нижней панели игры
     * ("Меню / часы / Доп") - та начинала уезжать вместе с со��ержимым.
     *
     * Вертикальная прокрутка не тронута: сцена боя выше экрана, её доматывают.
     */
    overflow-x: clip;
}

body {
    overscroll-behavior: none;
    touch-action: manipulation;
    /* Тот же запрет на уровне тела - на случай, если корню его переопределят. */
    overflow-x: clip;
    /*
     * Тело не уже и не шире экрана.
     *
     * width: 100% брать нельзя: при вертикальной полосе прокрутки 100% берётся
     * от родителя без её учёта, и тело оказывается шире видимой области - как
     * раз то, что мы убираем. max-width привязывает к реальной ширине окна.
     */
    max-width: 100%;
    /*
     * Отступ сверху на вырез камеры.
     *
     * Он нужен из-за viewport-fit=cover в шапке: страница занимает весь экран и
     * заезжает под верхнюю полосу со часами и вырезом камеры. При запуске с
     * ярлыка (без адресной строки браузера) верхний край панели игрока попадал
     * бы прямо под них. Где выреза нет, значение равно нулю и ничего не меняется.
     */
    padding-top: env(safe-area-inset-top, 0px);
}

/* ------------------------------------------------------------------ */
/* Контейнер раздела                                                   */
/* ------------------------------------------------------------------ */

/*
 * Повторяет прежние строчные стили .child_mb (max-width 480, min-width 320,
 * margin auto), но действует с самой первой загрузки, а не со второго перехода.
 *
 * ВАЖНО: здесь нельзя использовать will-change, transform, filter, contain и
 * perspective. Любое из этих свойств делает контейнер точкой отсчёта для
 * вложенных элементов с position: fixed - а на них держится нижняя панель
 * игры ("Меню / часы / Доп", style/foot.css) и окно обрыва связи. С will-change
 * панель прижималась к низу этого блока вместо низа экрана и на коротких
 * разделах, например в списке охоты, оказывалась посреди страницы.
 */
.game-body {
    display: block;
    max-width: 480px;
    min-width: 320px;
    margin: 0 auto;
    /*
     * position: relative обязателен, на нём держится бой.
     *
     * hunt/battle.php оборачивает всю сцену боя в
     * <div style="position: absolute; left: 0; top: 0; width: 100%">.
     * Такой блок ищет ближайшего позиционированного родителя, и раньше им был
     * контейнер раздела: старый код в system/header.php после перехода явно
     * выполнял $("mobitva:eq(-1)").css({position: "relative"}).
     *
     * Когда контейнером стал #gameBody без позиционирования, точка отсчёта
     * поднялась до окна: сцена боя уезжала в левый верхний угол через всю
     * ширину экрана, поверх панели игрока, и переставала попадать в свой
     * контейнер - бой выглядел как не отрисованный.
     *
     * relative, в отличие от transform и will-change, задаёт отсчёт только для
     * position: absolute и НЕ трогает position: fixed. Поэтому нижняя панель
     * ("Меню / часы / Доп") по-прежнему считается от края экрана.
     */
    position: relative;
    /*
     * Запас снизу, чтобы содержимое не пряталось под нижней панелью.
     * Раньше в строчных стилях стоял padding-bottom: 500px - это создавало
     * пустоту почти на весь экран и лишнюю прокрутку.
     */
    padding-bottom: 56px;
}

/*
 * Высота под сцену боя.
 *
 * Обёртка боя вынута из потока (position: absolute), поэтому она не растягивает
 * контейнер: сам по себе #gameBody остался бы высотой в один padding. Пока идёт
 * бой, контейнеру нужна высота экрана, иначе сцена рисуется в области нулевой
 * высоты и обрезается.
 *
 * Класс ставит и снимает shell.js по наличию сцены боя в разделе.
 */
.game-body_battle {
    /*
     * svh, а не vh - по той же причине, что у html выше: vh на телефоне равен
     * высоте при спрятанной адресной строке, и контейнер выходил выше видимой
     * части даже без сцены. Это давало лишнюю прокрутку и дёрганье адресной
     * строки прямо в бою. svh берёт высоту при видимой строке.
     *
     * Прокрутку в бою это не отнимает: сцена сама выше экрана и растягивает
     * контейнер сверх этого минимума - домотать до нижнего ряда по-прежнему можно.
     */
    min-height: 100vh;
    min-height: 100svh;
    /*
     * Запас снизу здесь НЕ обнуляется, он наследуется от .game-body.
     *
     * Сцена боя выше экрана и прокручивается, а нижняя панель лежит поверх неё.
     * Без запаса нижний ряд боя (кнопки удара и пояс) оказывался под панелью, и
     * домотать до него было нельзя.
     */
}

/* ------------------------------------------------------------------ */
/* Слой раздела поверх боя                                             */
/* ------------------------------------------------------------------ */

/*
 * Чат и команды открываются НАД боем, а не вместо него: бой продолжает идти под
 * слоем и просвечивает сквозь фон, чтобы игрок его не терял.
 *
 * transform здесь применять нельзя ни в каком виде. Внутри слоя лежит нижняя
 * панель раздела (position: fixed), а любой transform у родителя делает его
 * точкой отсчёта для fixed - панель прижалась бы к низу слоя вместо низа
 * экрана. По этой же причине в оболочке убрано появление сдвигом.
 *
 * Своя прокрутка (overflow-y) нужна, потому что бо�� под слоем прокручен на
 * своё место: без неё длинный чат тянул бы за собой страницу с ареной.
 */
.game-overlay {
    display: block;
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 1100;
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
    /*
     * Фон - тот же песочный цвет, что у страниц игры, только полупрозрачный.
     * У body в default_v_1_0.css стоит background-color: #C8AC70, отсюда и
     * rgba(200, 172, 112, ...) - это ровно тот же цвет.
     *
     * Раньше здесь стояло rgba(26, 13, 2, 0.94) - тёмно-коричневый. Это была
     * ошибка: такого цвета в игре нет вообще, он был выдуман. А текст в игре
     * ЧЁРНЫЙ (у body color: black, заголовки #663300), он рассчитан на светлую
     * страницу. Чёрные буквы на почти чёрном фоне не читались: владелец проверил
     * на живой игре и не увидел ничего. На стенде это не всплыло, потому что там
     * под слоем лежала грубая тёмная заготовка вместо настоящей вёрстки чата.
     *
     * Чем меньше последнее число, тем лучше видно бой под слоем. 0.88: текст
     * остаётся таким же чётким, как на обычной странице, а бой просвечивает.
     */
    background-color: rgba(200, 172, 112, 0.88);
    /*
     * Слева и справа - как у раздела, чтобы содержимое не прилипало к краям.
     * Снизу запас под нижнюю панель, как у .game-body.
     */
    padding: 0 4px 56px;
}

/*
 * Содержимое слоя держим в той же колонке, что и обычные разделы: игра свёрстана
 * под 480 px, и без ограничения чат растянулся бы на всю ширину экрана.
 */
.game-overlay > * {
    max-width: 480px;
    min-width: 0;
    margin-left: auto;
    margin-right: auto;
}

/*
 * СВИТКИ ВНУТРИ СЛОЯ - БЕЗ СВОЕЙ ЗАЛИВКИ.
 *
 * Слой сам полупрозрачный (см. выше), поэтому "Общий зал" сквозь себя бой
 * показывал: chat.php приходит без свитка, одним содержимым. А "Задворки" и
 * "Клан" свёрстаны в свитке (table_block2 -> block105), и у заливки свитка в
 * default_v_1_0.css стоит ПЛОТНЫЙ цвет background-color:#c4ae7f. Эт�� заливка
 * ложится ПОВЕРХ полупрозрачного фона слоя и закрашивает арену наглухо -
 * владелец так и сказал: "в задворках прозрачности нет".
 *
 * Гасим только заливку серединок (block105/block05/block22). Углы и края
 * свитка - картинки (block101..109 и прочие), их не трогаем: рамка должна
 * остаться нарисованной, иначе чат в бою станет непохож на игру.
 *
 * Правило живёт здесь, а не в default_v_1_0.css, и действует ТОЛЬКО внутри
 * .game-overlay: на обычных страницах вне боя свитки остаются плотными.
 */
.game-overlay .block105,
.game-overlay .block05,
.game-overlay .block22 {
    background-color: transparent;
}

/*
 * ПОЛОСЫ И ПЛАШКИ ЧАТА - ТОЖЕ БЕЗ СВОЕЙ ЗАЛИВКИ.
 *
 * Свиток погашен правилом выше, но в "Задворках" и "Клане" поверх арены
 * оставались плотные горизонтальные полосы и закреплённая сводка по отборам:
 * их заливка стояла прямо в разметке (inline), а inline сильнее таблицы стилей
 * и не гасился ничем. Теперь цвет живёт в классах .hr_chat и .clan_svodka_boi
 * (см. default_v_1_0.css), и здесь их можно снять.
 *
 * .hr_01 - та же полоса в задворках, только она с самого начала была классом.
 *
 * Линии оставляем ВИДИМЫМИ, но полупрозрачными: совсем убрать - и чат в бою
 * теряет разделение на части, а плотные - закрывают бой. Сводку гасим целиком:
 * это широкая плашка на весь текст, под ней арены не видно вообще.
 */
.game-overlay .hr_chat,
.game-overlay .hr_01 {
    background-color: rgba(167, 147, 109, 0.45);
}

.game-overlay .clan_svodka_boi {
    background-color: transparent;
}

/*
 * ЛИЧНЫЕ СООБЩЕНИЯ ПОВЕРХ БОЯ - ПОЛУПРОЗРАЧНЫЕ СТРОКИ.
 *
 * В переписке (mail.php) арену закрывали не свитки, а сами строки: список бесед
 * и кнопка "Новости" свёрстаны классом .clanturblock с плотным фоном #c9b892, а
 * вкладки "Онлайн / Все / Друзья / Опции" - классом .shopminiblock с плотным
 * #c9ac71. Строк на экране много, поэтому вместе они давали ту же глухую
 * заливку, на которую владелец жаловался в задворках.
 *
 * Совсем убирать заливку тут НЕЛЬЗЯ, в отличие от свитка: строки - нажимаемые,
 * и по фону видно, где кончается одна беседа и начинается другая. Поэтому берём
 * тот же цвет, только полупрозрачный: рисунок арены читается сквозь список, а
 * границы строк остаются на месте.
 *
 * Наведение и выбор (:hover, :checked) оставляем плотнее фона строки - иначе
 * подсветка нажатой строки пропадает.
 */
.game-overlay .clanturblock {
    background-color: rgba(201, 184, 146, 0.55);
}

.game-overlay .clanturblock:hover,
.game-overlay .clanturblock:checked {
    background-color: rgba(221, 202, 159, 0.75);
}

.game-overlay .shopminiblock {
    background-color: rgba(201, 172, 113, 0.55);
}

.game-overlay .shopminiblock:hover,
.game-overlay .shopminiblock:checked {
    background-color: rgba(221, 202, 159, 0.75);
}

/*
 * Пока слой открыт, нижняя панель БОЯ убирается.
 *
 * Иначе внизу оказываются две панели сразу: своя у слоя и оставшаяся у арены
 * под ним. Обе прижаты к низу экрана (position: fixed) и обе с z-index около
 * 1000, поэтому кнопки боя налезали бы на кнопку "В бой" и перехватывали
 * нажатия. Скрываем показ, а не удаляем разметку: слой закроется - панель боя
 * вернётся на место сама, без перезагрузки арены.
 */
body.has-overlay #gameBody .footlinetime,
body.has-overlay #gameBody .footlmenut,
body.has-overlay #gameBody .footrmenut {
    display: none;
}

/*
 * Снег поверх раздела.
 *
 * Слой снега лежал над содержимым и перехватывал нажатия: клик по кнопке
 * попадал в canvas, а не в кнопку. Проверка подтвердила - нажатие на "Охота"
 * уходило в canvas.canvas_snow. Снег декоративный и не должен ловить нажатия.
 */
canvas.canvas_snow {
    pointer-events: none;
}

/* ------------------------------------------------------------------ */
/* Панель игрока                                                       */
/* ------------------------------------------------------------------ */

/*
 * Панель отдаётся сервером один раз и дальше только обновляет числа.
 *
 * Прежде здесь стоял сдвиг top: -5px, поднимавший панель за верхний край
 * окна, - из-за него верхний валик свитка оказывался срезан. Парный к нему
 * подъём блока локации (.ramka_dvig, margin-top: -40px) наезжал на панель
 * снизу. У mmobitva.ru нет ни того, ни другого, панель стоит целиком, а
 * локация идёт под ней с обычным отступом - поэтому оба сдвига убраны.
 *
 * z-index оставлен: панель должна лежать поверх рамки локации, иначе рамка
 * перекроет её край.
 *
 * Небольшой отступ сверху - чтобы верхний валик свитка не касался кромки
 * окна: вплотную к краю он снова читается как срезанный, хотя цел.
 */
.game-hud {
    position: relative;
    z-index: 99999;
    /*
     * Отступ сверху 1 пиксель, как у эталона (там это margin-top: 1px у
     * .pstatus). Раньше стояло 6 пикселей - на них вся игра и была опущена
     * ниже эталонной, потому что за панелью вниз сдвигалось всё остальное.
     */
    padding-top: 1px;
}

/*
 * Панель нужна только на главной и в разделе "Персонаж", в остальных разделах
 * она получает атрибут hidden.
 *
 * Правило задано явно, хотя браузер и сам прячет hidden-элементы: у hidden
 * самый низкий приоритет, и любое правило с display перекрыло бы его. Эта
 * строка страхует панель от такого случая.
 *
 * Панель именно скрывается, а не удаляется: разметка и иконки остаются в
 * док��менте, поэтому возврат на главную происходит мгновенно и без подскока.
 */
.game-hud[hidden] {
    display: none;
}

.game-hud__frame {
    margin: 0 auto;
    border-spacing: 0;
    height: 40px;
    max-width: 100%;
}

/*
 * Ячейки выстроены через flex, а не пробелами между тегами.
 * Пробелы между <img> и числом раньше давали разный отступ в зависимости от
 * переносов строк в PHP-шаблоне, из-за чего панель слегка съезжала.
 */
.game-hud__row {
    display: flex;
    align-items: center;
    justify-content: center;
    flex-wrap: nowrap;
    gap: 8px;
    /*
     * У эталона надписи игры набраны Roboto. Раньше здесь стоял декоративный
     * Anfisa (это mobitva.ttf), которого у эталона нет вовсе - отсюда и
     * расхождение в цифрах на панели.
     */
    font-family: "Roboto", sans-serif;
    /*
     * 16 пикселей - столько же, сколько у эталона: там цифры панели своего
     * размера не имеют и берут его у страницы (body, Roboto 16px). У нас стояло
     * 14, поэтому числа выглядели мельче эталонных.
     */
    font-size: 16px;
    line-height: 1;
    white-space: nowrap;
}

.game-hud__cell {
    display: inline-flex;
    align-items: center;
    gap: 3px;
}

.game-hud__cell[hidden] {
    display: none;
}

/*
 * У иконок панели заданы атрибуты width и height в разметке.
 * height: auto здесь нужен, чтобы при сжатии на узком экране пропорции
 * сохранялись, а место под иконку резервировалось до её загрузки.
 */
.game-hud__ico {
    display: block;
    height: auto;
    flex: none;
}

/* ------------------------------------------------------------------ */
/* Устойчивость разметки                                               */
/* ------------------------------------------------------------------ */

/*
 * Картинки локации служат распорками: они прозрачные (opacity: 0) и задают
 * высоту блоку, поверх которого рисуется фон через background-image.
 *
 * Раньше у них была только ширина в процентах. Высоту браузер узнавал лишь
 * после загрузки файла, поэтому в этот момент вся страница подпрыгивала.
 * Атрибуты width и height в разметке теперь задают пропорции заранее,
 * а это правило не даёт строчным стилям сломать их.
 */
.location img,
.ramka_dvig img {
    height: auto;
}

/*
 * ==== ПОЛОСА ЗАГРУЗКИ СНИЗУ ====
 *
 * Показывает и снимает shell.js: сам при каждом запросе игры, плюс на всё время
 * перехода между разделами.
 *
 * Почему у этого элемента появился вид. Индикатор - это <img> с классом
 * loading, и оформления у класса не было НИ В ОДНОМ файле стилей игры:
 * .linefooter, которым его когда-то подпирали, живёт только в disconnect.css, а
 * его грузит одна страница обрыва связи. В самой игре индикатор оставался
 * невидимой точкой 1x1 в потоке страницы - то есть отклика не было вообще,
 * сколько бы правильно его ни включали. Поэтому вид задан здесь.
 *
 * Сама картинка (/img/loading.gif) не показывается: её размер и вид неизвестны,
 * а нужна ровная полоса. Растим элемент до полосы, а бегущий блик рисуем фоном.
 */
img.loading {
    pointer-events: none;
    /*
     * Выше слоя поверх боя (z-index 1100).
     *
     * Индикатор добавляется в конец <body> ДО слоя, поэтому при равных z-index
     * слой оказывался бы сверху и накрывал его. Тогда переход внутри слоя
     * (чат -> профиль -> снаряжение) шёл бы вообще без признака загрузки -
     * ровно та немота, из-за которой непонятно, нажалось или нет.
     */
    z-index: 1200;

    /*
     * ==== ПОЛОСА ЛЕЖИТ НА КНОПКЕ "НАЗАД", КАК БЫЛО В ИГРЕ ИЗНАЧАЛЬНО ====
     *
     * Была моя ошибка: я растянул её на всю ширину и оставил 4 пикселя у самого
     * низа экрана. Владелец с��азал прямо - "съехала в самый низ, почти не
     * видно". Так и есть: тонкая линия у нижней кромки попадает на край корпуса
     * телефона, на скруглении экрана её не разглядеть, а смотрит игрок в это
     * время на кнопку, которую нажал.
     *
     * В исходной игре индикатор стоял right: 0, width: 40% - то есть ровно на
     * нижней правой кнопке, а это и есть "Назад" (первый пункт списка R в
     * system/foot/foot.php рисуется последней строкой .footrmenut, а сама
     * колонка - fixed bottom right, width 40%). Возвращаю ту же геометрию:
     * взгляд уже там, и полосу нельзя не заметить.
     *
     * Высота берётся из --footBtnH. Раньше её присылал resizer() из
     * system/header.php, считая от innerHeight; теперь она объявлена в
     * style/foot.css рядом с самой высотой кнопок - одним источником для обоих.
     * Там стоит 6svh: эта единица отмерена от неподвижного состояния окна,
     * поэтому полоса совпадает с кнопкой и не пляшет при прокрутке, в отличие
     * от 6vh, которое на телефоне больше настоящей высоты окна из-за адресной
     * строки. Запасное 6vh здесь нужно для страниц без style/foot.css.
     */
    position: fixed;
    right: 0;
    bottom: 0;
    width: 40%;
    height: var(--footBtnH, 6vh);

    /*
     * ==== САМУ ГИФКУ НЕ ПОКАЗЫВАЕМ, ТОЛЬКО РАСТИМ ЭЛЕМЕНТ ПОД ПОЛОСУ ====
     *
     * Здесь стояло object-fit: cover - и это была ошибка, обратная замыслу:
     * cover РАСТЯГИВАЕТ /img/loading.gif на весь элемент, то есть на всю кнопку
     * "Назад". Надпись оказалась бы закрыта картинкой - ровно то, чего мы
     * добивались избежать, отказавшись от сплошной подложки.
     *
     * text-indent и overflow тут н�� помогают вовсе: они прячут alt-текст, когда
     * файл не загрузился, а загруженную картинку не убирают.
     *
     * Убираем картинку двумя путями сразу, чтобы сработало в любом браузере:
     * content: "" подменяет содержимое пустым (Chrome, Safari, Firefox), а
     * object-position уводит кадр за пределы элемента там, где content у
     * картинок не поддержан. Элемент остаётся на месте и сохраняет размеры -
     * значит блик и чёрная кромка рисуются как задумано.
     */
    content: "";
    object-fit: none;
    object-position: -9999px -9999px;
    text-indent: -9999px;
    overflow: hidden;

    /*
     * ПОДЛОЖКИ НЕТ - ПОЛОСА ПРОСВЕЧИВАЕТ.
     *
     * Раньше стоял сплошной тёмный фон: для линии в 4 пикселя это годилось, а
     * теперь полоса накрывает кнопку целиком и таким фоном стёрла бы саму
     * надпись "Назад". Оставляем только бегущий блик - кнопка читается, а
     * движение видно.
     *
     * Блик светлый и полупрозрачный (#f0dcae - песочный цвет игры). Идёт слева
     * направо по кнопке: движение само по себе говорит "идёт загрузка" лучше,
     * чем любой значок.
     *
     * Тёмная черта сверху совпадает с той, что у нижнего ряда панели
     * (border-top: 2px solid black в style/foot.css) - иначе на время загрузки
     * кромка кнопки словно раздваивалась.
     */
    box-sizing: border-box;
    border-top: 2px solid #000;
    /*
     * Боковые и нижнюю рамки гасим явно.
     *
     * Прежде в шапке лежал строчный блок .loading с border-bottom: 1px solid
     * black - от него у нижней кромки экрана шла вторая чёрная черта. Тот блок
     * из system/header.php убран (строчный стиль всё равно побеждал этот файл, и
     * править вид полосы было нельзя), но обнуление оставлено: полосу показывают
     * и старые страницы в admin/, у них свои строчные рамки.
     */
    border-bottom: none;
    border-left: none;
    border-right: none;
    background-color: transparent;
    background-image: linear-gradient(90deg,
            rgba(240, 220, 174, 0) 0%,
            rgba(240, 220, 174, 0.65) 50%,
            rgba(240, 220, 174, 0) 100%);
    background-repeat: no-repeat;
    background-size: 45% 100%;
    animation: shellLoaderRun 0.9s linear infinite;
}

/*
 * Блик уходит за оба края кнопки: начинает левее её начала и заканчивает правее
 * конца. Числа согласованы с background-size (45%), иначе на краях он появлялся
 * и исчезал рывком, а не выплывал.
 */
/*
 * ==== ПЛАШКА ЗАМЕРА ОТКЛИКА (видна только владельцу) ====
 *
 * Подсобный инструмент: показывает, сколько думал сервер, сколько запросов к базе
 * он сделал и какие из них самые медленные. Разбор - в system/zamer.php.
 *
 * Держим её В��ЕРХУ: низ занят панелью с кнопками и полосой загрузки, там плашка
 * закрыла бы игру. Нажатие убирает её (обработчик в javascript/shell.js).
 *
 * pointer-events нарочно оставлены обычными - по плашке надо иметь возможность
 * нажать, чтобы убрать. Поэтому она узкая и прижата к левому краю: так почти не
 * перехватывает нажатия по самой игре.
 */
#zamer {
    position: fixed;
    top: 0;
    left: 0;
    max-width: 74%;
    z-index: 1300;
    padding: 2px 6px;
    background-color: rgba(0, 0, 0, 0.82);
    color: #ffe9a8;
    font-size: 10px;
    line-height: 1.35;
    font-family: monospace;
    border-bottom-right-radius: 4px;
    cursor: pointer;
    /* длинные запросы не должны растягивать плашку за край экрана */
    overflow: hidden;
    word-break: break-all;
}

#zamer .zamerRow {
    color: #c9b98a;
    font-size: 9px;
}

@keyframes shellLoaderRun {
    from { background-position: -45% 0; }
    to   { background-position: 145% 0; }
}

/*
 * Снег - украшение, он не должен перехватывать касания.
 *
 * Холст со снежинками растягивался поверх картинки локации, а у контейнера
 * стоит pointer-events: all. В итоге снег забирал себе нажатия, и кнопки
 * "Охота" и "Дуэли" под ним не срабатывали: игрок нажимал, а игра не
 * реагировала. Правило !important нужно, потому что pointer-events у
 * контейнера задан строчным стилем прямо в разметке.
 */
.canvas_snow,
.snowConteiner {
    pointer-events: none !important;
}

/* ------------------------------------------------------------------ */
/* Попадание по кнопкам                                                */
/* ------------------------------------------------------------------ */

/*
 * Игра рассчитана на телефон, но многие переходы висят на onclick у мелких
 * блоков. Небольшая подсветка нажатия даёт понять, что касание засчитано,
 * -webkit-tap-highlight-color убирает синий прямоугольник поверх арта.
 */
.game-body [onclick] {
    -webkit-tap-highlight-color: transparent;
    cursor: pointer;
}

/*
 * Отклик на нажатие через opacity, а не filter.
 *
 * filter, как и transform, делает элемент точкой отсчёта для вложенных
 * position: fixed. Кнопки нижней панели сами имеют onclick, поэтому filter на
 * них мог бы дёрнуть разметку в момент нажатия. opacity безопасна.
 */
.game-body [onclick]:active {
    opacity: 0.85;
}

/*
 * ==== КНОПКА ЖДЁТ ОТВЕТА (класс is-busy) ====
 *
 * Ставит и снимает shell.js на время запроса, ушедшего с этой кнопки.
 *
 * Зачем отдельно от полосы снизу. Полоса говорит "игра работает", но при двух
 * кнопках рядом по ней не понять, какая сработала. Занятая кнопка отвечает на
 * второй вопрос - "принято именно моё нажатие".
 *
 * Кнопка притушена и медленно дышит, но НЕ заперта: нажатие остаётся возможным,
 * иначе оборвавшийся запрос оставил бы игрока с мёртвой кнопкой. Дыхание тем же
 * ритмом, что у кнопки автобоя (style/battle.css) - язык ожидания в игре один.
 *
 * opacity, а не filter: filter делает элемент точкой отсчёта для вложенных
 * position: fixed, и на кнопках нижней панели он дёрнул бы разметку.
 */
.is-busy {
    animation: shellBusyWait 0.9s ease-in-out infinite;
}

@keyframes shellBusyWait {
    0%, 100% { opacity: 1; }
    50% { opacity: 0.72; }
}

/* ------------------------------------------------------------------ */
/* Поиск жертвы для грабежа                                            */
/* ------------------------------------------------------------------ */

/*
 * ==== "ВЫСЛЕЖИВАЕМ ЖЕРТВУ" ====
 *
 * Замечание владельца: "грабитель когда нажимает напасть, то у него не просто
 * пишет что никого нет, а идёт динамическая загрузка поиск противника".
 * Раньше на месте поиска была одна строчка текста: сервер отвечает быстро, и
 * игрок видел готовый отказ мгновенно - будто кнопка сработала впустую.
 *
 * Рисуем поиск САМИМ ДЕЙСТВИЕМ грабителя: по тёмному переулку ходит луч
 * фонаря, а вдоль него зажигаются следы. Ничего не грузится с сервера - только
 * CSS, поэтому на слабом телефоне это не стоит ни байта.
 *
 * Палитра - ровно та же, что у всей оболочки: песочная подложка #C4AE7F,
 * коричневая обводка #663300 и разделители #a7936d, как у кнопок и заголовков
 * игры. Сначала поле было целиком тёмно-коричневым со чёрной рамкой, и владелец
 * сказал "как-то аккуратнее, чтобы в дизайн подходить": на песочных страницах
 * такой прямоугольник смотрелся вставкой из другой игры.
 *
 * Тёмным остался только сам переулок внутри - это картинка сцены, и она обязана
 * быть тёмной, иначе луч фонаря нечем показать. Получается как у остальных
 * разделов: обычная панель игры, а внутри неё окошко с происходящим.
 */
.grabSearch {
    /* края поля переулка - чтобы луч уходил "за кулисы", а не обрывался */
    overflow: hidden;
    margin: 8px auto;
    max-width: 320px;
    padding: 10px 10px 9px;
    border: 2px solid #663300;
    border-radius: 4px;
    background-color: #C4AE7F;
    color: #3b2a12;
    text-align: center;
}

.grabSearchTitle {
    font-weight: bold;
    font-size: 15px;
    letter-spacing: 0.4px;
    /*
     * Подчёркиваем заголовок той же линией, что делит содержимое во всей игре
     * (.hr_01, цвет #a7936d). Своей рамки не рисуем: одинаковых линий в игре
     * должно быть немного, иначе раздел начинает выбиваться.
     */
    padding-bottom: 6px;
    border-bottom: 1px solid #a7936d;
}

/*
 * Переулок с лучом фонаря.
 *
 * Луч - тот же приём, что у полосы загрузки снизу: светлое пятно едет слева
 * направо. Здесь он ярче и уже, поэтому читается как направленный свет, а не
 * как подсветка кнопки.
 */
.grabSearchAlley {
    position: relative;
    height: 46px;
    margin: 9px 0 7px;
    /*
     * Тонкая тёмная обводка - чтобы окошко переулка читалось врезкой в песочной
     * панели, а не дыркой в ней. Тот же приём, что у полей ввода в игре.
     */
    border: 1px solid #6b5a33;
    border-radius: 3px;
    background-color: #1d160f;
    /* кирпичная кладка переулка: две тонкие полосы, никакой картинки */
    background-image: repeating-linear-gradient(90deg,
            rgba(240, 220, 174, 0.07) 0 26px,
            rgba(0, 0, 0, 0) 26px 28px),
        repeating-linear-gradient(180deg,
            rgba(240, 220, 174, 0.07) 0 14px,
            rgba(0, 0, 0, 0) 14px 16px);
    overflow: hidden;
}

.grabSearchBeam {
    position: absolute;
    top: 0;
    bottom: 0;
    width: 38%;
    /* мягкий конус света: к краям гаснет, поэтому не выглядит наклейкой */
    background-image: linear-gradient(90deg,
            rgba(255, 233, 168, 0) 0%,
            rgba(255, 233, 168, 0.42) 50%,
            rgba(255, 233, 168, 0) 100%);
    animation: grabBeamRun 1.8s ease-in-out infinite;
}

/*
 * Луч ходит ТУ��А-ОБРАТНО (alternate дал бы паузу на развороте, поэтому обе
 * половины прописаны вручную). Обыск - движение возвратное: так это и читается.
 */
@keyframes grabBeamRun {
    0%   { left: -38%; }
    50%  { left: 100%; }
    100% { left: -38%; }
}

/* Следы на земле: зажигаются по очереди, будто идём по цепочке. */
.grabSearchSteps {
    position: absolute;
    left: 0;
    right: 0;
    bottom: 7px;
    display: flex;
    justify-content: center;
    gap: 12px;
}

.grabSearchSteps i {
    width: 7px;
    height: 7px;
    border-radius: 50%;
    background-color: #f0dcae;
    opacity: 0.18;
    animation: grabStepGlow 1.8s ease-in-out infinite;
}

/* Задержки согласованы с ходом луча: след вспыхивает, когда свет над ним. */
.grabSearchSteps i:nth-child(2) { animation-delay: 0.25s; }
.grabSearchSteps i:nth-child(3) { animation-delay: 0.5s; }
.grabSearchSteps i:nth-child(4) { animation-delay: 0.75s; }

@keyframes grabStepGlow {
    0%, 60%, 100% { opacity: 0.18; transform: scale(1); }
    20%           { opacity: 1;    transform: scale(1.45); }
}

/*
 * Строка о том, чем занят грабитель. Меняется на ходу (текст подставляет
 * search.php), поэтому держим ей постоянную высоту: иначе поле дёргалось бы
 * вверх-вниз при каждой смене надписи.
 */
.grabSearchWord {
    min-height: 18px;
    font-size: 13px;
    /* приглушённый коричневый по песочной подложке - как пояснения в игре */
    color: #5d4826;
}

/*
 * "Пока рядом никого. Ищем дальше".
 *
 * Владелец: "если мы на странице поиска, то она должна обновляться, пока не
 * выйдем". Поиск теперь ходит по кругу сам, и через несколько пустых кругов
 * search.php открывает эту строку - иначе человек смотрел бы на бегающий луч и
 * не знал, ищет игра или зависла.
 *
 * Держим её спокойной: та же линия-разделитель, что у заголовка, и тот же
 * приглушённый цвет. Это не тревога и не ошибка, а просто пояснение, поэтому ни
 * красн��го, ни жирного здесь быть не должно.
 */
.grabSearchTihoTut {
    margin-top: 7px;
    padding-top: 6px;
    border-top: 1px solid #a7936d;
    font-size: 12px;
    color: #5d4826;
}
