@import url('https://fonts.googleapis.com/css2?family=Press+Start+2P&family=Be+Vietnam+Pro:wght@400;700&display=swap');
@font-face{
  font-family: 'MEK';
  src: url('../fonts/MEK.woff2') format('woff2');
  font-weight: normal;
  font-style: normal;
}
:root{  
  --card-h: 25vh;
  --card-w: calc(var(--card-h) * 0.716);
  --cat-card-w: calc(var(--card-h) * 0.716);
  --card-action-btn-w: calc(var(--card-w) * 0.75 * 44 / 122);
  --cat-card-h: var(--card-h);
  --card-small-h: 18vh;
  --card-small-w: calc(var(--card-small-h) * 0.856);
  --card-small-art-h: calc(var(--card-small-h) * 0.622);
  --card-art-size: calc(var(--card-h) * 0.535);
  --card-pad: calc(var(--card-h) * 0.021);
  --card-name-h: calc(var(--card-h) * 0.096);
  --card-stats-h: calc(var(--card-h) * 0.085);
  --card-stats-w: 70%;
  --card-small-stats-w: 85%;
  --card-text-h: calc(var(--card-h) * 0.24);
  --card-small-name-h: calc(var(--card-small-h) * 0.133);
  --card-small-stats-h: calc(var(--card-small-h) * 0.122);
  --pcard-w: calc(var(--card-small-h) * 0.8);
  --pcard-h: calc(var(--card-small-h) * 0.22);
  --card-small-name-fs: calc(var(--card-small-h) * 0.1);
  --card-small-stat-fs: calc(var(--card-small-h) * 0.15);
  --card-world-name-h: calc(var(--card-h) * 0.096);
  --card-world-text-h: calc(var(--card-h) * 0.16);
  --shard-active: #B44FD4;
  --altar-active: #B44FD4;
  --tea-shadow: #2D7B3522;
  --jeet-shadow: #7B2D8B22;
  --modal-border-w: clamp(54px, 4vw, 81px);
  --modal-footer-border-w: calc(var(--modal-border-w) * 7 / 54);
  --modal-footer-btn-h: calc(var(--modal-border-w) * 40 / 54);
  --modal-h2-fs: calc(var(--modal-border-w) * 22 / 54);
  --color-cream: #E8E0C8;
  --jeet-main: #08050A;
  /* Ширина боковых колонок арены (2026-08-03) — План Б из обсуждения с автором: колонка
     ФИКСИРОВАННОЙ ширины (не "сколько там останется от карт"), карты подстраиваются под
     то, что осталось (см. max-width у .battlefield выше/ниже). 8vh с потолком 96px и полом
     56px — на самом тесном проверенном случае (телефон-портрет, мёртвая зона ~76-83px)
     остаётся зазор в 8-16px между колонкой и картами; на широких/landscape-экранах
     сознательно НЕ растёт вместе с гигантской свободной мёртвой зоной (там её как раз
     с большим запасом) — предсказуемый размер под один комплект арта на все экраны.
     Автор рисует новый арт под эту ширину — если понадобится другое число, менять только
     здесь. */
  --arena-col-w: clamp(56px, 8vh, 96px);
  /* Отдельная переменная под кнопку лога (2026-08-04, по прямому запросу автора —
     "верни размер для кнопки лог как был, его не надо было увеличивать"): --arena-col-w
     в лендскейпе переопределяется на бОльшее число (см. @media(orientation:landscape)
     ниже) — раньше кнопка лога считалась от неё же (calc(var(--arena-col-w)*0.85)) и
     невольно росла вместе с колонкой. Эта переменная НИГДЕ не переопределяется по
     ориентации — кнопка лога всегда одного размера, независимо от того, насколько шире
     стала сама колонка под кладбище/деку. */
  --arena-log-w: clamp(56px, 8vh, 96px);
  /* Конец хода — заморожена на СЕГОДНЯШНЕМ размере --arena-col-w (2026-08-04, по прямому
     запросу автора — боксы кладбища/деки увеличиваются вслед за увеличенной
     --arena-col-w в лендскейпе, см. её отдельное правило ниже по файлу, а конец хода
     расти дальше не должен, он и так уже отдельно зафиксирован по высоте раньше). Три
     формулы здесь — те же самые три, что были у --arena-col-w ДО сегодняшнего
     увеличения (root + landscape + phone-landscape), просто скопированы под новым
     именем и больше не трогаются, когда --arena-col-w меняется. */
  --arena-endturn-w: clamp(56px, 8vh, 96px);
  /* Боксы кладбища/деки (2026-08-04) — 4 фиксированных куска, из суммы которых считается
     сам размер .arena-pair (см. её комментарий в styles.css) — --arena-box-pad ОДНА
     переменная и для padding бокса, и для gap иконка↔каунтер, гарантированно одинаковый
     отступ с обеих сторон, как и просил автор. --arena-box-counter-w — вдвое больше
     старого значения (было 0.75*0.26≈0.195 от --arena-col-w, стало 0.39). */
  --arena-box-pad: calc(var(--arena-col-w) * 0.05);
  --arena-box-icon: calc(var(--arena-col-w) * 0.35);
  --arena-box-counter-w: calc(var(--arena-col-w) * 0.39);
  --arena-box-counter-h: calc(var(--arena-col-w) * 0.245);
}
*{box-sizing:border-box;margin:0;padding:0;-webkit-tap-highlight-color: transparent;-webkit-touch-callout: none;}
html{overflow:hidden;width:100%;height:100%;}
body{background:#0a0a12;color:#E8E0C8;font-family:'MEK',monospace;height:100%;overflow:hidden;touch-action:pan-y;}
.stars{position:fixed; inset:0; background:#0a0a12; z-index:0;}
.stars::after{content:''; position:absolute; inset:0; background:url('../img/space_bg.png') center center/cover no-repeat; opacity:0.5;}
/*── Вертикальный скролл поля боя заблокирован (2026-08-04, по прямому запросу автора —
   "карты на поле мы уже зафиксировали по рядам, скролл больше не нужен"). Раньше здесь
   было min-height:100vh + overflow-y:auto — но min-height НИЧЕГО не ограничивает (контейнер
   просто рос выше 100vh, если контенту нужно больше места), реальный скролл в итоге шёл на
   уровне html/body (обычный поток документа), а не внутри .game — так что одного
   overflow-y:hidden на .game было бы недостаточно. Теперь height:100vh (жёсткий потолок)
   + overflow-y:hidden — если что-то всё же не влезет, обрежется, а не скроллится, что сразу
   заметно (а не тихо доступно свайпом). html/body тоже на overflow:hidden — не мешает
   .screen/.landing/#mulliganScreen (все они position:fixed с собственным overflow-y:auto,
   скроллятся внутри себя независимо от html/body), но и не даёт странице скроллиться
   через них, если .game вдруг переполнится. Внутренний скролл кладбища/лога/лора/каталога/
   деках-билдера — не трогали, у них свой overflow-y:auto на других элементах. ──*/
.game{position:relative;z-index:1;width:100%;padding:0;display:flex;flex-direction:column;gap:0;height:100vh;overflow-y:hidden;overflow-x:hidden;}
html, body, body *{user-select: none;-webkit-user-select: none;}
.card-small{width:var(--card-small-w);height:var(--card-small-h);border:1px solid;padding:2px;cursor:pointer;position:relative;background:transparent;background-size:cover;background-position:center;font-size:12px;border-radius:8%;-webkit-touch-callout:none;-webkit-user-select:none;user-select:none;touch-action:manipulation;}
.btn:hover{filter:brightness(1.25) drop-shadow(0 0 4px #c8a84b88);}
.btn:active{transform:scale(0.95) translateY(2px);}
.btn{font-family:'MEK',monospace;font-size:30px;border:1px solid;background:transparent;cursor:pointer;transition:all .15s;color:#e8e0c8;}
.hidden{display:none!important;}
.cat-search-input,.cat-search-input *{user-select: text;-webkit-user-select: text;}
img{-webkit-user-drag:none; user-select:none; -webkit-user-select:none; pointer-events:none;}
.modal h1, .modal h2, .modal h3, .modal p, .modal span, .modal div, .modal button{user-select: none !important; -webkit-user-select: none !important;}
/* ═══════════════════════════════════════════════════════════════════════
   LANDING PAGE — стартовый экран, переходы, иллюминатор, врата
   ═══════════════════════════════════════════════════════════════════════ */
/*── Кнопки выбора фракции (VS AI picker) — квадратные, кастомный арт ────────*/
.modal-faction-btn{width:var(--modal-footer-btn-h);height:var(--modal-footer-btn-h); flex-shrink:0; border:none;background-color:transparent; background-repeat:no-repeat;background-position:center;background-size:100% 100%; cursor:pointer;padding:0;transition:none;}
/*── Композиция лендинга — единая сетка: 256 условных единиц по ширине,
   560 по высоте (32 потолок + 256 стена + 240 стол + 32 дно, см. разбор
   в HTML-комментарии выше и чат 2026-07-26). --lw — единственный юнит на
   ВЕСЬ лендинг (раньше был отдельный --lw-bottom = --lw×1.2 для стола —
   убрано вместе с буфером, теперь всё в одном масштабе, стык-в-стык):
   растёт вместе с высотой вьюпорта (100vh × 256/560 — ровно столько,
   чтобы вся колонка из 560 юнитов заняла 100% высоты), но не дальше
   100vw и не дальше 600px (см. договорённость с автором 2026-07-13) —
   если ширина или 600px-кап становятся у́же, чем vh-формула, сверху/снизу
   остаётся воздух (неизбежно при жёстком соотношении сторон без резинового
   буфера). Все "жёсткие" размеры ниже — calc(var(--lw) * N / 256), где
   N — юнит из ТЗ автора; --lw-bottom оставлен только для обратной
   совместимости старых calc()-формул у стола/выносов, равен --lw. ────*/
.landing{--lw: min(calc(100vh * 256 / 560), 100vw, 600px); --lw-bottom: var(--lw); position:fixed;inset:0;z-index:200; display:flex;flex-direction:column;align-items:center; overflow-y:auto; overflow-x:hidden; background-color:#0c1013; background-size:cover; background-position:center;}
/*── ВЕРХ — потолок(32) + стена-с-иллюминатором(256), оба фикс, никогда не
    сжимаются (буфер между ними убран 2026-07-26 — раньше был гибким
    "воздухом", теперь всё стык-в-стык, высоту берёт на себя единый --lw). ──*/
/*── Стена ровно 256 (не 192) — иллюминатор сам квадратный 256x256 и должен
    ложиться встык по ШИРИНЕ с потолком (тоже 256 шириной), так что стене
    нужно ровно столько же высоты, сколько ширины у всей композиции, иначе
    иллюминатор либо обрежется, либо не совпадёт по краю (см. чат
    2026-07-14 — ровно это и произошло при 192). ─────────────────────────*/
.landing-top{width:var(--lw); flex:0 0 auto; display:flex;flex-direction:column;}
.landing-ceiling{flex:0 0 calc(var(--lw) * 32 / 256); width:100%; background-image:url('../img/land_fonar.gif'); background-size:100% 100%; background-repeat:no-repeat; background-position:center;}
.landing-wall{flex:0 0 var(--lw); width:100%; position:relative; /* плейсхолдер — арт стены ещё не готов, прозрачно */ display:flex;align-items:center;justify-content:center;}
/*── Иллюминатор — раньше сидел на height:100% (от высоты .landing-wall,
    192/256 lw) с width:auto — из-за этого его ФАКТИЧЕСКАЯ ширина выходила
    меньше ширины потолка (те же 256 lw), хотя оба ассета нарисованы
    одинаковой шириной. Теперь считаем от width:100% (= var(--lw), та же
    величина, что и у потолка) — с aspect-ratio:1/1 высота подтягивается
    автоматически и получается идеальный var(--lw) × var(--lw) квадрат,
    встык с потолком по ширине. ────────────────────────────────────────*/
.landing-porthole{position:relative;z-index:1; width:100%; aspect-ratio:1/1; height:auto; flex-shrink:0;}
.porthole-space{position:absolute;inset:0;z-index:0; background-image:url('../img/logo_space.png'); background-size:100% auto; background-position:center; background-repeat:no-repeat;}
.porthole-logo-wrap{position:absolute;inset:0;z-index:2; display:flex;align-items:center;justify-content:center; pointer-events:none;}
.porthole-logo{max-width:65%; display:flex;flex-direction:column;align-items:center;}
/*── Логотип в иллюминаторе — раньше жил на clamp(px,vw,px), т.е. не был
    завязан на --lw (ту же переменную, что держит пропорции ВСЕЙ
    остальной композиции лендинга — стены/потолок/кнопки и т.д.).
    На десктопе --lw считается по min() от vh И vw (см. комментарий выше
    про --top-vh/--bottom-vh), а раньше лого следило только за vw —
    отсюда рассинхрон с размером самого иллюминатора на нестандартных
    пропорциях экрана. Переводим на calc(var(--lw) * N) — та же формула,
    что и у всех остальных элементов группы. ───────────────────────────*/
.landing-title{font-family:'MEK',monospace;font-size:calc(var(--lw) * 0.0972); color:#E8E0C8;letter-spacing:calc(var(--lw) * 0.02109375);text-align:center; text-shadow:0 0 20px #c8a84b66; pointer-events:none;}
.landing-sub{font-family:'MEK',monospace;font-size:calc(var(--lw) * 0.054); color:#9b937f;letter-spacing:calc(var(--lw) * 0.02109375);text-align:center;margin-top:calc(var(--lw) * 0.0421875); pointer-events:none;}
.porthole-glass{position:absolute;inset:0;z-index:1; background:radial-gradient(circle at 35% 30%, rgba(255,255,255,0.10), rgba(180,200,255,0.04) 55%, rgba(0,0,0,0.06) 100%); backdrop-filter:blur(0.6px); -webkit-backdrop-filter:blur(0.6px); overflow:hidden; pointer-events:none;}
.porthole-glass::before{content:''; position:absolute; top:-60%;left:-150%; width:55%;height:220%; background:linear-gradient(75deg, transparent 40%, rgba(255,255,255,0.35) 50%, transparent 60%); transform:rotate(20deg); animation:portholeGlare 13s ease-in-out infinite;}
.porthole-frame{position:absolute;inset:0;z-index:3; background-image:url('../img/logo_window.png'); background-size:100% 100%; background-repeat:no-repeat; pointer-events:none;}
/*── НИЗ — единый функциональный блок, 240/256 высоты, НИКОГДА не сжимается.
    Раньше эта группа была физически на 20% крупнее (--lw-bottom = --lw×1.2,
    только на десктопе) с компенсирующим отрицательным margin-top — убрано
    2026-07-26 вместе с буфером: теперь --lw-bottom всегда равен --lw,
    стол в той же шкале, что и всё остальное, стык-в-стык без нахлёстов. ──*/
.landing-bottom{width:var(--lw-bottom); flex:0 0 auto; position:relative; z-index:2; display:flex;flex-direction:column;}
.landing-strip{width:100%; flex:0 0 auto;}
.landing-strip-perspective{height:calc(var(--lw-bottom) * 8 / 256); background-image:url('../img/top_table.png'); background-size:100% 100%; background-repeat:no-repeat;}
.landing-strip-play{height:calc(var(--lw-bottom) * 64 / 256); position:relative; display:flex;align-items:center;justify-content:center;}
.landing-strip-edge{height:calc(var(--lw-bottom) * 8 / 256); background-image:url('../img/front_table.png'); background-size:100% 100%; background-repeat:no-repeat;}
/*── Play-ворота — раньше фикс 240×60px, теперь тянутся на всю ширину полосы;
    4:1 у самого арта = ровно 64/256 высоты полосы, без искажений. Механика
    (гейт-спрайт, рамка, hover, 3 кнопки режима) не менялась. ─────────────*/
.play-gate-wrap{position:relative; width:70%; aspect-ratio:4/1; flex-shrink:0; z-index:2;}
.play-mode-buttons{position:absolute;inset:0;z-index:1; display:flex;flex-direction:row;align-items:center;justify-content:space-evenly; pointer-events:none; background-color:#3d494f;}
.art-btn-square.art-btn-mode{flex:none; height:76.667%; width:auto; aspect-ratio:1/1;}
.play-gate-sprite{position:absolute;inset:0;z-index:3;cursor:pointer; background-image:url('../img/btn_playgame_gates_sheet.png'); background-size:700% 100%;background-repeat:no-repeat; background-color:transparent;border:none;}
/* Убираем плейсхолдер-текст: арт уже есть в PNG */
.play-gate-sprite::after{content:none;}
.play-gate-wrap.gate-idle:hover .play-gate-sprite{background-image:url('../img/btn_playgame_hover.png'); background-size:100% 100%; background-position:0 0;}
.play-gate-sprite.gate-frame-2{background-position:16.667% 0;}
.play-gate-sprite.gate-frame-3{background-position:33.333% 0;}
.play-gate-sprite.gate-frame-4{background-position:50% 0;}
.play-gate-sprite.gate-frame-5{background-position:66.667% 0;}
.play-gate-sprite.gate-frame-6{background-position:83.333% 0;}
.play-gate-sprite.gate-frame-7{background-position:100% 0; pointer-events:none;}
.play-gate-wrap.gates-open .play-mode-buttons{pointer-events:all;}
.play-gate-frame{position:absolute;inset:0;z-index:4; background-image:url('../img/btn_playgame_frame.png'); background-size:100% 100%;background-repeat:no-repeat; pointer-events:none;}
/*── Music/SFX поверх play-бокса (2026-07-26) — впритык к левому/правому
    краю landing-strip-play, z-index выше рамки (4), сама рамка/ворота/3
    кнопки режима не сдвигаются (position:absolute не влияет на flex-
    центровку .play-gate-wrap). Размер квадрата = высота бокса (64/256),
    т.е. точно тот же размер, что был у этих кнопок на полосе audio. ──*/
.landing-strip-play .play-audio-btn{position:absolute; top:0; height:100%; width:calc(var(--lw-bottom) * 32 / 256); z-index:5;}
.landing-strip-play .play-audio-btn-left{left:0;}
.landing-strip-play .play-audio-btn-right{right:0;}
/*── Ряд Rules — 64(1:1, плейсхолдер stolL) + 128(2:1) + 64(1:1, плейсхолдер
    stolR) = 256, край-в-край. Music/SFX отсюда убраны (см. play-audio-btn
    выше), боксы стали чисто декоративными плейсхолдерами под арт стола. ──*/
.landing-row-audio{width:100%; height:calc(var(--lw-bottom) * 64 / 256); display:flex;flex-direction:row;}
.landing-row-audio .art-btn-square{width:calc(var(--lw-bottom) * 64 / 256); height:100%;}
.landing-row-audio .art-btn-rules{width:calc(var(--lw-bottom) * 128 / 256); height:100%;}
/*── Плейсхолдеры стола вместо старых music/sfx — некликабельны, ждут арт
    stolL.gif/stolR.gif (заменить url, когда файлы появятся в img/). ──*/
.landing-row-audio .landing-desk-deco{background-color:#3d494f; background-repeat:no-repeat; background-size:100% 100%; cursor:default;}
.landing-row-audio .landing-desk-deco:hover{filter:none;}
.landing-desk-deco-left{background-image:url('../img/stolL.gif');}
.landing-desk-deco-right{background-image:url('../img/stolR.gif');}
/*── Ряд Lore/декор/Catalog — 64(2:3) + 128(4:3) + 64(2:3) = 256, край-в-край ──*/
.landing-row-nav{width:100%; height:calc(var(--lw-bottom) * 96 / 256); display:flex;flex-direction:row;}
.landing-row-nav .art-btn-lore,.landing-row-nav .art-btn-catalog{width:calc(var(--lw-bottom) * 64 / 256); height:100%;}
.landing-nav-decor{width:calc(var(--lw-bottom) * 128 / 256); height:100%; background-image:url('../img/bot_table.png'); background-size:100% 100%; background-repeat:no-repeat;}
/*── Выносы стола (2026-07-26: были видны только ≥600px, теперь ВСЕГДА —
    на узких экранах, где --lw < 100vw (см. формулу --lw в .landing выше),
    сбоку от центральной колонки остаётся зазор до края вьюпорта; раньше
    там просто светился фон .landing (сплошной цвет), теперь туда всегда
    рендерится этот арт — а .landing уже держит overflow-x:hidden, так что
    видна ровно та часть выноса, что физически влезает в оставшийся зазор
    (по сути "выглядывающий краешек"), без изменений в разметке. ─────*/
.landing-bottom-ext{position:absolute; top:0; height:calc(var(--lw-bottom) * 240 / 256); width:calc(var(--lw-bottom) * 64 / 256); background-size:100% 100%; background-repeat:no-repeat;}
.landing-bottom-ext-left{left:calc(var(--lw-bottom) * -64 / 256); background-image:url('../img/left_table.gif');}
.landing-bottom-ext-right{right:calc(var(--lw-bottom) * -64 / 256); background-image:url('../img/right_table.png');}
/*── Три новых парных выноса (2026-07-14), та же техника, что у
    .landing-bottom-ext выше — absolute-позиционирование относительно
    ближайшего НЕ-overflow:hidden предка, никакого пиксельного размера,
    всё через calc(var(--lw-N) * N / 256).
    1/2 — "стена+фонарь" (288 = потолок 32 + стена 256 по высоте), встык с
    .landing-wall, сдвинуты вверх на высоту потолка, чтобы верхний край
    совпал с самым верхом композиции, а не с верхом .landing-wall.
    ШИРИНА выровнена до 304 (2026-07-26, по запросу автора) — раньше была
    288 (квадрат к своей же высоте), из-за чего внешний борт не совпадал
    с бортом стола/дна-выносов ниже (те 304 = 64+240): получалась ступенька
    на стыке "стена → стол". Теперь внешняя кромка одна прямая линия
    сверху донизу (0→560), высота осталась 288 (не квадрат, а 304×288).
    ОСТАЁТСЯ на --lw (не --lw-bottom) — структурно это часть ВЕРХНЕЙ
    группы (буквально вложен в .landing-wall в HTML), должен совпадать
    по размеру с окном/потолком, а не со столом. ─────────────────────*/
.landing-wall-ext{position:absolute; top:calc(var(--lw) * -32 / 256); height:calc(var(--lw) * 288 / 256); width:calc(var(--lw) * 304 / 256); background-size:100% 100%; background-repeat:no-repeat;}
.landing-wall-ext-left{left:calc(var(--lw) * -304 / 256); background-image:url('../img/land_left_wall.gif');}
.landing-wall-ext-right{right:calc(var(--lw) * -304 / 256); background-image:url('../img/land_right_wall.gif');}
/*── 3/4 — "стол-стол" (240, та же высота, что у .landing-bottom и у
    существующих *-table-выносов), сидят встык СНАРУЖИ от них (левее
    left_table / правее right_table), поэтому смещение -304 = -(64+240):
    крайняя точка существующего 64-выноса. На --lw-bottom, как и стол. ───*/
.landing-bottom-ext2{position:absolute; top:0; height:calc(var(--lw-bottom) * 240 / 256); width:calc(var(--lw-bottom) * 240 / 256); background-size:100% 100%; background-repeat:no-repeat;}
.landing-bottom-ext2-left{left:calc(var(--lw-bottom) * -304 / 256); background-image:url('../img/land_left_left_table.gif');}
.landing-bottom-ext2-right{right:calc(var(--lw-bottom) * -304 / 256); background-image:url('../img/land_right_right_table.gif');}
/*── 5/6 — "дно" под уже упомянутыми выносами. Высота ЗАФИКСИРОВАНА к той же
    сетке --lw-bottom (32/256), что и сама .landing-tail ниже — раньше была
    100% от .landing-tail (которая "плавала" от vh), теперь оба гарантированно
    совпадают, т.к. считаются одной и той же формулой (2026-07-26, по
    запросу автора). Ширина = 64+240=304, та же, что у пары выносов над ним.
    На --lw-bottom — визуально продолжает стол, должен совпадать по
    ширине с .landing-bottom-ext/-ext2, а не с исходным --lw. ──────────*/
.landing-tail-ext{position:absolute; top:0; height:calc(var(--lw-bottom) * 32 / 256); width:calc(var(--lw-bottom) * 304 / 256); background-size:100% 100%; background-repeat:no-repeat;}
.landing-tail-ext-left{left:calc(var(--lw-bottom) * -304 / 256); background-image:url('../img/land_left_dno.gif');}
.landing-tail-ext-right{right:calc(var(--lw-bottom) * -304 / 256); background-image:url('../img/land_right_dno.gif');}
/*── ХВОСТ — вне сетки, но высота теперь ЗАФИКСИРОВАНА к сетке --lw-bottom
    (32/256), а не к vh-паддингам (2026-07-26, по запросу автора — раньше
    "плавала" вместе с высотой вьюпорта, что не совпадало с left/right dno
    выше). Safe-area снизу — margin-bottom СНАРУЖИ бокса (2026-07-26,
    было padding-bottom — из-за этого align-items:center центрировал
    иконки/текст по высоте МИНУС отступ, а не по всей высоте бокса,
    контент visually "уезжал" вверх на устройствах с safe-area >0;
    margin эту область не трогает, центрирование теперь честное). ─────*/
.landing-tail{width:var(--lw); flex:0 0 auto; height:calc(var(--lw-bottom) * 32 / 256); position:relative; padding:0 12px; margin-bottom:env(safe-area-inset-bottom, 0px); box-sizing:border-box; display:flex;flex-direction:row;align-items:center;justify-content:space-between;
  /*── Шрифт (текст + △◎▣-символы) и иконки теперь РАЗНЫЕ величины
      (2026-07-26, по запросу автора — иконки-то ок на 65%, а вот текст
      попросили ужать до 40% высоты бокса). Иконки больше не в em от
      font-size — им нужна отдельная переменная --tail-icon-size, чтобы
      уменьшение шрифта их не утягивало вниз вместе с собой. ─────────*/
  --tail-icon-size:calc(var(--lw-bottom) * 32 / 256 * 0.65);
  font-size:calc(var(--lw-bottom) * 32 / 256 * 0.4);
  letter-spacing:.3px; color:#ffffff66; font-family:'MEK',monospace; line-height:1.2; background-image:url('../img/land_dno.gif'); background-size:100% 100%; background-repeat:no-repeat;}
/*── Соцсети слева (2026-07-26) — иконки временно общие SVG (см. комментарий
    в HTML), размер = --tail-icon-size (независим от font-size, см. выше).
    Заменить href на реальные ссылки и SVG на пиксельный арт позже. ────*/
.landing-tail-social{display:flex;flex-direction:row;align-items:center;gap:calc(var(--tail-icon-size) * 0.4);}
.landing-tail-icon{width:var(--tail-icon-size);height:var(--tail-icon-size);display:inline-flex;align-items:center;justify-content:center;color:#ffffff66;text-decoration:none;flex-shrink:0;}
.landing-tail-icon:hover{color:#ffffffb3;}
.landing-tail-icon svg{width:100%;height:100%;display:block;}
/*── Версия + копирайт справа, сгруппированы вместе (небольшой gap между
    ними), а не растянуты по всей ширине через justify-content родителя —
    иначе от иконок до "vX.XX" и от "vX.XX" до "© ..." был бы одинаковый
    зазор, а в референсе версия и копирайт стоят плотной парой. ───────*/
.landing-tail-info{display:flex;flex-direction:row;align-items:center;gap:0.6em;}
/*── Переходы экранов (зум+блюр «в направлении ящика», см. showScreen/hideScreen
    в ui.js — они задают landing.style.transformOrigin по месту клика). ────*/
.landing{transition:transform 0.315s cubic-bezier(0.4,0,0.2,1) 0s, opacity 0.22s ease 0.08s, filter 0.2s ease 0.09s;}
.landing.exit-right,.landing.exit-left,.landing.exit-down{transform:scale(1.45);opacity:0;filter:blur(24px);pointer-events:none;}
.landing.exit-center{transform:scale(0.55);opacity:0;pointer-events:none;}
.screen{position:fixed;inset:0;z-index:201;background:#0a0a12; overflow-y:auto;padding:4vw;display:none;}
.screen.active{display:block;}
.screen.slide-in-right,.screen.slide-in-left,.screen.slide-in-down{animation:screenZoomIn 0.315s cubic-bezier(0.4,0,0.2,1) forwards;}
.screen.slide-out-right,.screen.slide-out-left,.screen.slide-out-down{animation:screenZoomOut 0.245s cubic-bezier(0.4,0,0.2,1) forwards;}
@keyframes screenZoomIn{
  0%{transform:scale(1.3);filter:blur(22px);opacity:0;}
  55%{filter:blur(9px);}
  100%{transform:scale(1);filter:blur(0px);opacity:1;}
}
@keyframes screenZoomOut{
  0%{transform:scale(1) translateY(0);filter:blur(0px);opacity:1;}
  30%{transform:scale(1.02) translateY(-2.5%);filter:blur(0px);opacity:1;}
  100%{transform:scale(1.08) translateY(-7%);filter:blur(22px);opacity:0;}
}
/*── Кастомные арт-кнопки лендинга — базовые/дефолтные размеры (fallback,
    реальный размер на лендинге задаётся контекстно в .landing-row-audio/
    .landing-row-nav выше; эти значения остаются на случай использования
    вне новой сетки). ──────────────────────────────────────────────────*/
.art-btn{width:162px; height:54px; border:none; background-color:transparent; background-repeat:no-repeat; background-position:center; background-size:100% 100%; cursor:pointer; padding:0; transition:none; filter:brightness(1);}
.art-btn:not(.art-btn-catalog):not(.art-btn-rules):not(.art-btn-lore):hover{filter:brightness(1.25);}
.art-btn-playgame{background-image:url('../img/btn_playgame1.png');}
.art-btn-playgame:active{background-image:url('../img/btn_playgame2.png');}
.art-btn-rules{background-image:url('../img/btn_rules1.png');}
.art-btn-rules:hover{filter:brightness(1);background-image:url('../img/btn_rulesH.png');}
.art-btn-rules:active{background-image:url('../img/btn_rules2.png');}
.art-btn-lore{background-image:url('../img/btn_lore1.png');}
.art-btn-lore:hover{filter:brightness(1);background-image:url('../img/btn_loreH.png');}
.art-btn-lore:active{background-image:url('../img/btn_lore2.png');}
.art-btn-square{width:54px; height:54px; border:none; background-color:transparent; background-repeat:no-repeat; background-position:center; background-size:100% 100%; cursor:pointer; padding:0; transition:none; filter:brightness(1);}
.art-btn-square:hover{filter:brightness(1.25);}
.art-btn-square.music-on{background-image:url('../img/btn_music_on1.png');}
.art-btn-square.music-on:hover{background-image:url('../img/btn_music_on2.png');filter:brightness(1);}
.art-btn-square.music-off{background-image:url('../img/btn_music_off1.png');}
.art-btn-square.music-off:hover{background-image:url('../img/btn_music_off2.png');filter:brightness(1);}
/*── SFX кнопка ──────────────────────────────────────────────────*/
.art-btn-square.sfx-on{background-image:url('../img/btn_sfx_on1.png');}
.art-btn-square.sfx-on:hover{background-image:url('../img/btn_sfx_on2.png');filter:brightness(1);}
.art-btn-square.sfx-off{background-image:url('../img/btn_sfx_off1.png');}
.art-btn-square.sfx-off:hover{background-image:url('../img/btn_sfx_off2.png');filter:brightness(1);}
/*── Кнопки старта игры ──────────────────────────────────────────*/
.art-btn-hotseat,.art-btn-vsai,.art-btn-online{background-color:transparent;}
.art-btn-online{cursor:default;} 
.art-btn-vsai{cursor:pointer;}
.art-btn-hotseat:hover,.art-btn-vsai:hover,.art-btn-online:hover{filter:none;}
.art-btn-hotseat:hover{background-image:url('../img/btn_hotseatH.png');}
.art-btn-hotseat{background-image:url('../img/btn_hotseat1.png');}
.art-btn-hotseat:active{background-image:url('../img/btn_hotseat2.png');}
.art-btn-vsai{background-image:url('../img/btn_vsai1.png');}
.art-btn-vsai:hover{background-image:url('../img/btn_vsaiH.png');}
.art-btn-vsai:active{background-image:url('../img/btn_vsai2.png');}
.art-btn-online{background-image:url('../img/btn_online1.gif');}
.screen{position:fixed;inset:0;z-index:201;background:#0a0a12;overflow-y:auto;padding:4vw;display:none;}
.screen.active{display:block;}
.start-group{display:flex; flex-direction:column; align-items:center; gap:10px;}
.start-options{display:flex; flex-direction:row; gap:10px; flex-wrap:wrap; justify-content:center; animation:fadeInScale 0.2s ease-out;}
@keyframes fadeInScale{
  from{opacity:0;transform:scale(0.9);}
  to{opacity:1;transform:scale(1);}
}
@keyframes portholeGlare{
  0%,85%{left:-150%; opacity:0;}
  90%{opacity:0.85;}
  97%{left:150%; opacity:0.85;}
  100%{left:150%; opacity:0;}
}
/* ═══════════════════════════════════════════════════════════════════════
   SCREENS — общий каркас полноэкранных вкладок (Rules/Lore/Catalog)
   ═══════════════════════════════════════════════════════════════════════ */
.screen-header{display:flex;align-items:center;justify-content:center;gap:4vw;margin-bottom:3vw;padding-bottom:2vw;border-bottom:1px solid #c8a84b22;position:relative;}
.screen-title{font-family:'MEK',monospace;font-size:6vw;color:#E8E0C8;text-align:center;}
.screen-back{font-family:'MEK',monospace;font-size:3.5vw;padding:1vw 2.5vw;border:1px solid #555;background:transparent;color:#888;cursor:pointer;position:absolute;left:0;}
.screen-back:hover{color:#c8a84b;border-color:#c8a84b;}
.screen-lang-btn{font-family:'MEK',monospace;font-size:3.5vw;padding:1vw 2.5vw;border:1px solid #c8a84b44;background:transparent;color:#c8a84b;cursor:pointer;position:absolute;right:0;}
.screen-lang-btn:hover{border-color:#c8a84b;background:#c8a84b18;}
.screen-title.rus-title{font-family:'Press Start 2P',monospace;font-size:calc(6vw * 0.65);}
@media(min-width:600px){
  .screen-title.rus-title{font-size:calc(2.2vw * 0.65);}
}
@media(min-width:600px){
  .screen-lang-btn{font-size:1.1vw;padding:0.4vw 1vw;}
}
.screen-body{max-width:700px;margin:0 auto;font-size:4.56vw;line-height:1.9;color:#9B937F;}
.screen-body h2{font-family:'MEK',monospace;font-size:6vw;color:#c8a84b;margin:5vw 0 2vw;}
.screen-body h3{font-family:'MEK',monospace;font-size:4.56vw;color:#e8e0c8;margin:4vw 0 1.5vw;letter-spacing:1px;}
.screen-body p{margin-bottom:2vw;}
.screen-body ul{margin:1.5vw 0 2.5vw 4vw;}
.screen-body li{margin-bottom:1vw;}
#loreScreen .screen-body{font-size:calc(4.56vw * 1.7 * 0.5);line-height:0.95;color:#3b2a1a;}
#loreScreen .screen-body h2{font-size:calc(6vw * 1.2 * 0.5);color:#5c2415;}
@media(min-width:600px){
  .screen-title{font-size:2.2vw;}
  .screen-back{font-size:1.1vw;padding:0.4vw 1vw;}
  .screen-body{font-size:1.1vw;}
  .screen-body h2{font-size:1.8vw;margin:2vw 0 0.8vw;}
  .screen-body h3{font-size:1.1vw;margin:1.5vw 0 0.5vw;}
  .screen-body p{margin-bottom:0.8vw;}
  #loreScreen .screen-body{font-size:calc(1.1vw * 1.7);}
  #loreScreen .screen-body h2{font-size:calc(1.8vw * 1.2);}
}
/* ═══════════════════════════════════════════════════════════════════════
   RULES SCREEN
   ═══════════════════════════════════════════════════════════════════════ */
/*── .rules-source (ENG/RUS/POR/VN) больше никогда не показываются напрямую —
    это просто источник текста для js/rules-book.js, который клонирует их
    h2/h3/p в реальные страницы книги (.rules-page-inner, стили ниже). ──*/
.rules-source{display:none !important;}
.rules-icon{height:var(--ricon-h, 1em);width:auto;vertical-align:-0.15em;margin-right:0.4em;image-rendering:pixelated;flex-shrink:0;}
.rules-icon-pair{display:inline-flex;align-items:center;gap:0.15em;margin-right:0.4em;}
.rules-icon-pair .rules-icon{margin-right:0;}
/*── Внутритекстовые ссылки на термины/разделы (например "[Эссенции]" →
    прыжок на нужную страницу, см. rulesGotoTerm в rules-book.js). Цвет
    чуть темнее тела текста + подчёркивание, при наведении — кремовый,
    тот же, что и у пунктов оглавления. ────────────────────────────────*/
.rules-gloss-link{color:inherit;text-decoration:underline;text-decoration-style:dotted;text-underline-offset:2px;cursor:pointer;transition:color .15s;}
.rules-gloss-link:hover{color:#E8E0C8;}

/*═══════════════════════════════════════════════════════════════════════
  RULES BOOK — обложка + постраничный разворот
  ---------------------------------------------------------------------
  Размер "страницы" (--rpage-w/--rpage-h) — это размер ОДНОГО объекта
  ratio 3:4 (высота:ширина): на обложке она же и есть вся книга, на
  развороте это половина (разворот = 2 таких объекта рядом). На мобиле
  видно только .rules-page-right (левая скрыта медиа-запросом); на
  десктопе видно обе, левая зеркалит СВОЙ ФОН через двойной scaleX(-1)
  (контент внутри остаётся читаемым, не зеркалится).
  ═══════════════════════════════════════════════════════════════════════*/
#rulesScreen{--rpage-w:80vw; --rpage-h:calc(80vw * 4 / 3); --rborder:42px; --rnavbtn:min(5vh,9vw); --rnavbox-border:calc(var(--rnavbtn) / 3);}
@media(min-width:601px){
  #rulesScreen{--rpage-h:80vh; --rpage-w:calc(80vh * 3 / 4); --rborder:calc(var(--rpage-h) * 7 / 60);}
}
/*── Вертикальный iPad (и планшеты того же класса) — книга правил ведёт себя как на
    телефоне: одна страница за раз, а не разворот из двух. Тот же порог "планшет-портрет",
    что уже используется по всему проекту (601px = не телефон по ширине, 1024px = ширина
    iPad Pro 12.9" в портрете, самый широкий реальный планшет в этой ориентации) — просто
    откатывает geometry обратно на телефонную (80vw) формулу поверх блока выше. Автор
    попросил 2026-07-17: разворот из двух узких страниц в портретной ориентации читался
    хуже, чем одна широкая. ──*/
@media(min-width:601px) and (max-width:1024px) and (orientation:portrait){
  #rulesScreen{--rpage-w:80vw; --rpage-h:calc(80vw * 4 / 3); --rborder:42px;}
}
#rulesScreen.active{display:flex;align-items:center;justify-content:center;padding:0;background:#829797 url('../img/rules_bg.png') repeat center;}
/*── Открыто прямо из матча, не с лендинга (2026-08-12, по прямому запросу автора,
    rulesOpenFromGame()/RB.fromGame в rules-book.js) — глушим фоновую текстуру
    лендинга (иначе видна поверх поля боя вместо самого поля) и поднимаем z-index
    выше игрового экрана (у обоих .screen одинаковый z-index:201 — раньше на экране
    всегда был активен только один .screen сразу, это первый случай, когда их два
    одновременно). Селектор специфичнее/той же специфичности что и .active выше
    (#id+class у обоих) — важно, что это правило идёт ПОСЛЕ него в файле, иначе бы
    не победило в каскаде. */
#rulesScreen.rules-ingame{background:rgba(10,10,18,0.35);backdrop-filter:blur(6px);-webkit-backdrop-filter:blur(6px);z-index:850;opacity:0;transition:opacity .28s ease;}
/*── Анимация появления/исчезновения книги при открытии из матча (2026-08-12, по
    прямому запросу автора) — книга "выскакивает" из центра экрана от scale(0) к
    полному размеру, и наоборот при закрытии. Управляется классом .rules-pop-in
    (rules-book.js, rulesOpenFromGame()/rulesCloseOverlay()) — добавляется НЕ сразу
    вместе с .active/.rules-ingame, а через двойной requestAnimationFrame (тот же
    приём, что и в hideScreen() выше по файлу, ui.js) — иначе браузер схлопывает
    scale(0)→scale(1) в один кадр без анимации, т.к. стартовое и целевое состояние
    применились бы одновременно, до первой отрисовки. Скейлится .rules-book-wrap
    целиком (2026-08-12, по прямому запросу автора — раньше скейлилась только сама
    .rules-book, навбокс с кнопками Back/X/Fwd оставался статичным и "отклеивался"
    от книги при анимации) — .rules-book-wrap уже и так единый родитель для книги
    И навбокса (см. разметку в index.html), поэтому один transform на обоих сразу,
    без отдельной синхронизации. Фон (у #rulesScreen.rules-ingame выше) анимируется
    параллельно, от того же класса. */
#rulesScreen.rules-ingame.rules-pop-in{opacity:1;}
#rulesScreen.rules-ingame .rules-book-wrap{transform:scale(0);transition:transform .28s ease;}
#rulesScreen.rules-ingame.rules-pop-in .rules-book-wrap{transform:scale(1);}
.rules-book-wrap{position:relative;display:inline-flex;}
.rules-book{position:relative;display:flex;flex-direction:row;}

/*── Обложка ─────────────────────────────────────────────────────────*/
.rules-cover{
  width:var(--rpage-w); height:var(--rpage-h); box-sizing:border-box;
  position:relative; display:flex; align-items:center; justify-content:center;
  border-style:solid; border-width:var(--rborder);
  border-image-source:url('../img/rules_cover.png'); border-image-slice:108 fill;
  border-image-width:var(--rborder); border-image-outset:0; border-image-repeat:round;
  cursor:default;
}
.rules-book.open .rules-cover{display:none;}
.rules-cover-title{
  font-family:'MEK',monospace; color:#0c1013; text-align:center; line-height:1;
  font-size:calc(var(--rpage-h) * 0.14); padding:0; word-break:break-word;
  display:inline-block; box-sizing:content-box; flex-shrink:0;
  border-style:solid; border-width:calc(1em / 6);
  border-image-source:url('../img/rules_title_bg.png'); border-image-slice:40 fill;
  border-image-width:calc(1em / 6); border-image-outset:0; border-image-repeat:stretch;
}
/*── RUS-рамка автору нравится как есть (1em/3 от своего, уже некрупного,
    шрифта — впритык к тексту) — явно фиксируем её отдельно, раз базовое
    правило выше поменялось (стало 1em/6) под остальные языки. ──────────*/
.rules-cover-title.rus-title{font-family:'Press Start 2P',monospace;font-size:calc(var(--rpage-h) * 0.050968);border-width:calc(1em / 3);border-image-width:calc(1em / 3);}
.rules-cover-title.vn-title{font-family:'Be Vietnam Pro',sans-serif;font-weight:700;font-size:calc(var(--rpage-h) * 0.098);}
/*── Языковые кнопки — по верхней кромке обложки, квадратные, наполовину
    сидят поверх 9-slice рамки (translateY(-50%) от верхнего края). ────*/
.rules-cover-langs{
  /* top:0 у absolute-потомка отсчитывается от padding-edge родителя, т.е.
     ВНУТРИ его собственной border-width — поэтому сдвигаем на -rborder,
     чтобы верх кнопок совпал с самым верхним пикселем фона обложки. */
  position:absolute; top:calc(-1 * var(--rborder)); left:50%; transform:translate(-50%,0);
  display:flex; gap:calc(var(--rnavbtn) * 0.25); z-index:2;
}
.rules-cover-lang{
  width:var(--rnavbtn); height:var(--rnavbtn); box-sizing:border-box;
  font-family:'MEK',monospace; font-size:calc(var(--rnavbtn) * 0.34);
  border:none; background-color:transparent; background-repeat:no-repeat;
  background-position:center; background-size:100% 100%; color:#000; cursor:pointer;
  display:flex; align-items:center; justify-content:center; padding:0;
}
.rules-cover-lang[data-lang="ENG"]{background-image:url('../img/rules_eng1.png');}
.rules-cover-lang[data-lang="ENG"]:hover{background-image:url('../img/rules_engH.png');}
.rules-cover-lang[data-lang="ENG"]:active,.rules-cover-lang[data-lang="ENG"].active{background-image:url('../img/rules_eng2.png');}
.rules-cover-lang[data-lang="RUS"]{background-image:url('../img/rules_rus1.png');}
.rules-cover-lang[data-lang="RUS"]:hover{background-image:url('../img/rules_rusH.png');}
.rules-cover-lang[data-lang="RUS"]:active,.rules-cover-lang[data-lang="RUS"].active{background-image:url('../img/rules_rus2.png');}
.rules-cover-lang[data-lang="POR"]{background-image:url('../img/rules_por1.png');}
.rules-cover-lang[data-lang="POR"]:hover{background-image:url('../img/rules_porH.png');}
.rules-cover-lang[data-lang="POR"]:active,.rules-cover-lang[data-lang="POR"].active{background-image:url('../img/rules_por2.png');}
.rules-cover-lang[data-lang="VN"]{background-image:url('../img/rules_vn1.png');}
.rules-cover-lang[data-lang="VN"]:hover{background-image:url('../img/rules_vnH.png');}
.rules-cover-lang[data-lang="VN"]:active,.rules-cover-lang[data-lang="VN"].active{background-image:url('../img/rules_vn2.png');}
/* GER (2026-08-12) — та же конвенция 1/H/2, автор кладёт файлы в img/ локально;
   до тех пор фон прозрачный (несуществующий url просто не подгружается), текстовая
   подпись "GER" на кнопке остаётся видна как фолбэк — тот же принцип, что и у
   остальных кнопок без готового арта (см. CLAUDE.md, "Плейсхолдеры под будущий арт"). */
.rules-cover-lang[data-lang="GER"]{background-image:url('../img/rules_ger1.png');}
.rules-cover-lang[data-lang="GER"]:hover{background-image:url('../img/rules_gerH.png');}
.rules-cover-lang[data-lang="GER"]:active,.rules-cover-lang[data-lang="GER"].active{background-image:url('../img/rules_ger2.png');}
.rules-cover-lang:hover,.rules-cover-lang.active{color:#E8E0C8;}

/*── Страницы ────────────────────────────────────────────────────────*/
.rules-pages{display:none;flex-direction:row;}
.rules-book.open .rules-pages{display:flex;}
.rules-page{
  width:var(--rpage-w); height:var(--rpage-h); box-sizing:border-box; position:relative; overflow:hidden;
  border-style:solid; border-width:var(--rborder);
  border-image-source:url('../img/rules_pages.png'); border-image-slice:54 fill;
  border-image-width:var(--rborder); border-image-outset:0; border-image-repeat:round;
}
/*── 4 варианта текстуры страницы — какой достанется конкретной странице
    решается один раз при пагинации (см. rulesPaginate()/RB.pageVariants в
    rules-book.js) и не меняется при пролистывании туда-сюда, только при
    пересборке книги (смена языка/ресайз). variant-1/2/3 переопределяют
    только источник картинки, cut и repeat/border-width — те же. ────────*/
.rules-page.variant-1{border-image-source:url('../img/rules_pages1.png');}
.rules-page.variant-2{border-image-source:url('../img/rules_pages2.png');}
.rules-page.variant-3{border-image-source:url('../img/rules_pages3.png');}
.rules-page-left{display:none;}
@media(min-width:601px){
  .rules-page-left{display:block;transform:scaleX(-1);} /* зеркалим фон/рамку страницы */
  .rules-page-left .rules-page-inner{transform:scaleX(-1);} /* и обратно зеркалим контент, чтобы текст читался нормально */
}
@media(min-width:601px) and (max-width:1024px) and (orientation:portrait){
  .rules-page-left{display:none;} /* вертикальный iPad — одна страница, как на телефоне (см. geometry override выше) */
}
.rules-page-inner{
  width:100%; height:100%; box-sizing:border-box; overflow:hidden;
  padding:3% 5%; font-family:'MEK',monospace; color:#5d2f35;
  font-size:calc(var(--rpage-h) * 0.028); --ricon-h:calc(var(--rpage-h) * 0.028); line-height:1.35;
  display:flex; flex-direction:column;
}
/*── flex-column только чтобы .rules-full-art (см. ниже) могло flex:1 и
    дозаполнить остаток страницы под заголовком — на h2/h3/p/остальные
    блоки это влиять не должно, поэтому у них flex:0 0 auto (= обычная
    block-высота по контенту, как было и до перехода на flex). ─────────*/
.rules-page-inner > *{flex:0 0 auto;}
/*── Размер иконок (--ricon-h) НАМЕРЕННО отвязан от размера шрифта — так
    можно крутить их независимо по языку/брейкпоинту (просьба автора
    2026-07-15: где-то текст уменьшить, а иконки при этом увеличить —
    через 1em (как было раньше) это невозможно, т.к. они жёстко связаны). */
.rules-page-inner.rus-lang, .rules-page-inner.rus-lang h2, .rules-page-inner.rus-lang h3{font-family:'Press Start 2P',monospace;}
.rules-page-inner.rus-lang{font-size:calc(var(--rpage-h) * 0.0216); --ricon-h:calc(var(--rpage-h) * 0.0216 * 1.44);} /* телефон: шрифт как раньше, иконки — было ×1.2, теперь ещё +20% (×1.44 итого) */
.rules-page-inner.eng-lang, .rules-page-inner.por-lang, .rules-page-inner.ger-lang{font-size:calc(var(--rpage-h) * 0.028 * 1.2); --ricon-h:calc(var(--rpage-h) * 0.028 * 1.32);} /* телефон: шрифт +20%, иконки — было ×1.1, теперь ещё +20% (×1.32 итого). GER добавлен 2026-08-12 — тот же MEK-шрифт, что ENG/POR */
.rules-page-inner.vn-lang, .rules-page-inner.vn-lang h2, .rules-page-inner.vn-lang h3{font-family:'Be Vietnam Pro',sans-serif;}
.rules-page-inner.vn-lang h2, .rules-page-inner.vn-lang h3{font-weight:700;}
.rules-page-inner.vn-lang{--ricon-h:calc(var(--rpage-h) * 0.028 * 1.2);} /* телефон: раньше не было отдельного буста иконок — теперь +20%, шрифт не трогаю */
.rules-page-inner h2{font-size:2.04em;color:#322030;margin:0 0 0.5em;line-height:1.15;text-align:center;}
.rules-page-inner h3{font-size:1.2em;color:#492833;margin:0.9em 0 0.3em;letter-spacing:0.5px;}
.rules-page-inner p{margin:0 0 0.65em;}
/*── Полностраничный арт-атомик (сейчас: rules_card.png в главе "Карта").
    Всегда идёт сразу под h2 и должен дозаполнить всю оставшуюся высоту
    страницы, сохранив родные пропорции 3:4 (у арта уже есть свои
    прозрачные поля на случай несовпадения с реальной высотой контейнера
    на разных экранах — оттого object-fit:contain, а не cover/fill).
    flex:1 переопределяет общее flex:0 0 auto выше — см. правило
    .rules-page-inner > *. Соответствующий page-break форсируется в JS
    (rulesPaginate) сразу после этого узла — см. комментарий там же. ───*/
.rules-full-art{width:100%;flex:1 1 0;min-height:0;object-fit:contain;display:block;}
/*── Десктопные переопределения размера шрифта/иконок по языку — ВАЖНО:
    этот блок должен стоять ПОСЛЕ базовых .rules-page-inner.xxx-lang правил
    выше (а не до них, как было по ошибке 2026-07-15) — при равной
    специфичности класс-селекторов побеждает тот, что позже в файле,
    независимо от того, внутри media query он или нет. Раньше эти
    десктопные переопределения были в @media-блоке ВЫШЕ по файлу и
    базовые мобильные правила их всегда перебивали, даже на десктопе. ──*/
@media(min-width:601px){
  .rules-page-inner.rus-lang{font-size:calc(var(--rpage-h) * 0.018 * 0.8); --ricon-h:calc(var(--rpage-h) * 0.018 * 1.44);} /* текст -20% (как раньше), иконки — было ×1.2, теперь ещё +20% (×1.44 итого) */
  .rules-page-inner.eng-lang, .rules-page-inner.por-lang, .rules-page-inner.ger-lang{font-size:calc(var(--rpage-h) * 0.028); --ricon-h:calc(var(--rpage-h) * 0.028);} /* без изменений — буст только для телефона */
  .rules-page-inner.vn-lang{--ricon-h:calc(var(--rpage-h) * 0.028);} /* без изменений — буст иконок только для телефона, как у остальных языков */
}
/*── Вертикальный iPad — страница у нас уже телефонного размера (см. geometry override
    выше), так что шрифты тоже возвращаем на телефонные формулы, а не десктопные (те
    рассчитаны на щедрый 80vh). ДОЛЖНО идти именно здесь, ПОСЛЕ блока выше — при равной
    специфичности класса побеждает то, что позже в файле (см. комментарий над этим блоком). ──*/
@media(min-width:601px) and (max-width:1024px) and (orientation:portrait){
  .rules-page-inner.rus-lang{font-size:calc(var(--rpage-h) * 0.0216); --ricon-h:calc(var(--rpage-h) * 0.0216 * 1.44);}
  .rules-page-inner.eng-lang, .rules-page-inner.por-lang, .rules-page-inner.ger-lang{font-size:calc(var(--rpage-h) * 0.028 * 1.2); --ricon-h:calc(var(--rpage-h) * 0.028 * 1.32);}
  .rules-page-inner.vn-lang{--ricon-h:calc(var(--rpage-h) * 0.028 * 1.2);}
}
/* Измерительный клон — идентичная структура/шрифты, но невидим и вне потока */
.rules-page-measure{position:fixed;left:-9999px;top:0;visibility:hidden;pointer-events:none;}

/*── Стр.1 — пустая, плейсхолдер под цитату ─────────────────────────*/
.rules-quote-page{height:100%;display:flex;align-items:flex-end;justify-content:flex-end;text-align:right;}
.rules-quote-placeholder{font-style:italic;opacity:0.35;font-size:1.1em;line-height:1.6;}

/*── Стр.2 — оглавление ──────────────────────────────────────────────*/
.rules-toc-title{text-align:center;}
.rules-toc-list{list-style:none;margin:0;padding:0;}
.rules-toc-entry{
  display:flex;justify-content:space-between;align-items:baseline;gap:0.5em;
  padding:0.45em 0;border-bottom:1px dotted #5d2f35;cursor:pointer;
  color:#5d2f35;transition:color .15s,border-color .15s;
}
.rules-toc-entry:hover{color:#E8E0C8;border-bottom-color:#E8E0C8;}
.rules-toc-entry .rules-toc-num{font-size:0.85em;flex-shrink:0;}

/*── Навигационный бокс — по правому краю книги, наполовину выходит за
    пределы (translate(50%,-50%) от правого-центра книги). Размер бокса
    ПОЛНОСТЬЮ определяется кнопками: без отступов по краям (padding:0),
    только зазор МЕЖДУ кнопками, равный четверти их высоты — итого высота
    контента = 3 кнопки + 2 зазора по 0.25 = 3.5 высоты кнопки. ─────────*/
.rules-navbox{
  position:absolute; top:50%; right:0; transform:translate(50%,-50%); z-index:5;
  display:flex; flex-direction:column; gap:calc(var(--rnavbtn) * 0.25);
  padding:0; box-sizing:content-box;
  border-style:solid; border-width:var(--rnavbox-border);
  border-image-source:url('../img/rules_navigation_box.png'); border-image-slice:20 fill;
  border-image-width:var(--rnavbox-border); border-image-outset:0; border-image-repeat:stretch;
}
.rules-navbtn{
  width:var(--rnavbtn); height:var(--rnavbtn); flex-shrink:0; padding:0; cursor:pointer;
  border:none; background-color:transparent; background-repeat:no-repeat;
  background-position:center; background-size:100% 100%;
}
.rules-navbtn:disabled{cursor:default;}
.rules-navbtn-back{background-image:url('../img/rules_btn_left1.png');}
.rules-navbtn-back:hover:not(:disabled){background-image:url('../img/rules_btn_leftH.png');}
.rules-navbtn-back:active:not(:disabled){background-image:url('../img/rules_btn_left2.png');}
.rules-navbtn-back:disabled{background-image:url('../img/rules_btn_leftD.png');}
.rules-navbtn-fwd{background-image:url('../img/rules_btn_right1.png');}
.rules-navbtn-fwd:hover:not(:disabled){background-image:url('../img/rules_btn_rightH.png');}
.rules-navbtn-fwd:active:not(:disabled){background-image:url('../img/rules_btn_right2.png');}
.rules-navbtn-fwd:disabled{background-image:url('../img/rules_btn_rightD.png');}
.rules-navbtn-home{background-image:url('../img/rules_btn_home1.png');}
.rules-navbtn-home:hover:not(:disabled){background-image:url('../img/rules_btn_homeH.png');}
.rules-navbtn-home:active:not(:disabled){background-image:url('../img/rules_btn_home2.png');}
/*── Начиная со страниц глав (index>=2) та же кнопка "домой" ведёт назад
    в оглавление (см. rulesGoHome() в rules-book.js) — подменяем и арт. ──*/
.rules-navbtn-home.rules-navbtn-home-toc{background-image:url('../img/rules_btn_oglav1.png');}
.rules-navbtn-home.rules-navbtn-home-toc:hover:not(:disabled){background-image:url('../img/rules_btn_oglavH.png');}
.rules-navbtn-home.rules-navbtn-home-toc:active:not(:disabled){background-image:url('../img/rules_btn_oglav2.png');}
/*── Крестик вместо домика (2026-08-12, по прямому запросу автора) — та же кнопка/
    слот, третий вариант иконки (см. RB.fromGame/closeMode в rules-book.js). Автор
    кладёт rules_btn_close1/H/2.png в img/ локально сам — та же 1/H/2-конвенция, что
    у всех остальных кастомных кнопок проекта. */
.rules-navbtn-home.rules-navbtn-home-close{background-image:url('../img/rules_btn_close1.png');}
.rules-navbtn-home.rules-navbtn-home-close:hover:not(:disabled){background-image:url('../img/rules_btn_closeH.png');}
.rules-navbtn-home.rules-navbtn-home-close:active:not(:disabled){background-image:url('../img/rules_btn_close2.png');}
/*── На десктопе, при открытом развороте, бокс должен "висеть" именно у
    правой страницы — т.к. .rules-book при открытии и так становится
    шириной в 2 страницы, right:0 у .rules-navbox уже прибивает его к
    правому краю книги = правому краю правой страницы. Ничего доп. не
    нужно, оставлено для ясности намерения. ───────────────────────────*/
/* ═══════════════════════════════════════════════════════════════════════
   LORE SCREEN
   ═══════════════════════════════════════════════════════════════════════ */
/*── LORE/CATALOG — общая архитектура "ящик" ──────────────────────────────
    Теперь скроллится сама СТРАНИЦА (#loreScreen/#catalogScreen), а не
    контент внутри ящика — .crate-modal больше не имеет max-height и растёт
    вместе с контентом. Клик по страничкам разворачивает стопку → ящик
    визуально "выдвигается" (растёт вниз) → ручка, сидящая сразу под ним,
    уезжает вместе с ним ниже по странице → скроллим страницу вниз, чтобы
    её увидеть. Рамка прибита ровно к верху (0 сверху, никакого центрирования).
    --crate-w расшарен на уровне экрана, чтобы и ящик, и ручка масштабировались
    от одной и той же ширины. ────────────────────────────────────────────*/
#loreScreen, #catalogScreen{--crate-w: min(92vw,760px); --handle-w: min(92vw,760px); padding:0 0 5vh; overflow-y:auto; overflow-x:hidden; -webkit-overflow-scrolling:touch; overscroll-behavior:contain; background-color:#181f27; background-image:url('../img/floor_bg.png'); background-repeat:repeat; image-rendering:pixelated; flex-direction:column; align-items:center; justify-content:flex-start; gap:0;}
#catalogScreen{--crate-w: min(94vw,980px);} /* каталогу нужна сетка карт пошире — хедер там донастроим отдельным заходом. --handle-w НЕ переопределяем — ручка Back должна остаться того же физического размера, что и в лоре, а не раздуваться вместе с более широким ящиком (см. чат 2026-07-14) */
#loreScreen.active, #catalogScreen.active{display:flex;}
/*── Кастомный квадратный (без скруглений) скролл теперь на самой странице ──*/
#loreScreen, #catalogScreen{scrollbar-width:thin; scrollbar-color:#829797 #181f27;}
#loreScreen::-webkit-scrollbar, #catalogScreen::-webkit-scrollbar{width:10px;}
#loreScreen::-webkit-scrollbar-track, #catalogScreen::-webkit-scrollbar-track{background:#181f27;}
#loreScreen::-webkit-scrollbar-thumb, #catalogScreen::-webkit-scrollbar-thumb{background:#829797; border-radius:0;}
.crate-modal{position:relative; width:var(--crate-w); flex-shrink:0; overflow:hidden; display:flex; flex-direction:column; box-sizing:border-box; --modal-border-w:12px; border-style:solid; border-width:var(--modal-border-w); border-image-source:url('../img/bg_lore_modal.png'); border-image-slice:48 fill; border-image-width:var(--modal-border-w); border-image-outset:0; border-image-repeat:stretch;}
@media(min-width:600px){ .crate-modal{--modal-border-w:24px;} }
.crate-modal .screen-body{margin:0; width:100%; box-sizing:border-box;}
/*── Ручка — масштабируется вместе с --crate-w (тот же токен, что у ящика).
    Ratio читаю как "высота:ширина" 1:13 (широкая тонкая планка-хват, не
    наоборот — узкая цифра всегда высота, раз это ручка чемодана). ────────*/
.crate-handle{display:block; width:calc(var(--handle-w) * 0.68); aspect-ratio:13/1; flex-shrink:0; border:none; background-color:transparent; padding:0; cursor:pointer; background-repeat:no-repeat; background-position:center; background-size:100% 100%; background-image:url('../img/btn_lore_ru4ka1.png');}
.crate-handle:hover{background-image:url('../img/btn_lore_ru4kaH.png');}
.crate-handle:active{background-image:url('../img/btn_lore_ru4ka2.png');}
/*── Lore больше не имеет хедера вообще — кнопка "назад" убрана из DOM,
    единственный путь назад — ручка (.crate-handle) внизу ящика. Заголовок
    тоже убран (понятно, что мы в Lore, раз ящик открылся). ──────────────*/
/*── ARCHIVE — арт-баттон (idle/hover/active), впритык к нижнему краю ящика
    (screen-body держит padding-bottom:0), паддинги слева/справа вдвое
    меньше, чем у остального контента. Ratio теперь явно ширина:высота = 5:1. */
.crate-modal .screen-body{padding:0 4px 0;}
/*── Lore-экран: 100% ширины, вплотную к краям (2026-08-09, по прямому запросу
    автора — "чтобы заполняли место в области для контента у 9-слайфона...
    убрать какие-либо падинги по краям, чтобы эти ассеты вплотную были слева
    и справа к краям"). #loreModal специфичнее общего .crate-modal .screen-
    body{padding:0 4px 0} выше — тот же класс .crate-modal используется и на
    Catalog-экране (#catalogModal), так что обнулять паддинг там, где он не
    просили, нельзя — обнуляем только внутри #loreModal. */
/* max-width:none (2026-08-10, багфикс той же задачи "вплотную к краям", 2026-08-09) —
   padding обнулили тогда же, но забыли про max-width:700px из базового .screen-body
   (styles.css:371) — на широких экранах контент-бокс #loreModal (--crate-w=760, минус
   2×24 рамки = 712px) шире 700px, так что .screen-body всё равно зажимался до 700 и
   центрировался (margin:0 auto), оставляя симметричный зазор по бокам вместо "вплотную". */
#loreModal .screen-body{padding:0; max-width:none;}
.lore-archive-btn{display:block; width:100%; margin:4vw 0 0 0; aspect-ratio:5/1; border:none; padding:0; cursor:pointer; background-repeat:no-repeat; background-position:center; background-size:100% 100%; background-image:url('../img/btn_lore_archive1.png');}
/*── Плейсхолдер над стопкой страниц — та же ширина/боковые отступы, что
    у кнопки Archive ниже, но высокий (1:2) и без верхнего отступа —
    только нижний, чтобы отделить от страниц под ним. ─────────────────*/
.lore-books-placeholder{width:100%; margin:0 0 4vw 0; aspect-ratio:5/2; background-repeat:no-repeat; background-position:center; background-size:100% 100%; background-image:url('../img/lore_books.png');}
/*── Текст-мостик в промежутке между последней страницей и кнопкой Archive.
    #loreStack уже резервирует под собой margin-bottom (7.5vw, у каждой
    .lore-page) + собственный padding-bottom (6vw) — это больше глубины
    самой нижней страницы в ЗАКРЫТОЙ стопке (offset 7.2vw), так что этот
    текст, сидящий сразу ПОСЛЕ #loreStack, физически не может оказаться
    под стопкой ни в открытом, ни в закрытом виде. ────────────────────*/
.lore-archive-intro{font-family:'MEK',monospace; font-size:4.56vw; line-height:1.4; color:#E8E0C8; text-align:center; margin:0 calc(1vw - 4px);}
@media(min-width:600px){
  .lore-archive-intro{font-size:1.1vw;}
}
.lore-archive-btn:hover{background-image:url('../img/btn_lore_archiveH.png');}
.lore-archive-btn:active{background-image:url('../img/btn_lore_archive2.png');}
/*── Раскрытые страницы (2026-08-09, по прямому запросу автора — "сделать все
    страницы без наклонов") — --r здесь принудительно 0deg ТОЛЬКО для потока
    ВНЕ закрытой стопки: когда пользователь читает текст, все 4 карточки идут
    строго вертикально, без книжного "рассыпанного" наклона. Закрытая стопка
    (#loreStack.closed ниже) наклон СОХРАНЯЕТ по прямому решению автора — та
    остаётся "книгой на столе", легко наклонённой, до открытия. nth-of-type
    правила ниже (--r:-1.6deg и т.п.) поэтому больше не трогаем — они реально
    применяются только внутри #loreStack.closed через более специфичный
    селектор ниже; здесь достаточно нулевого дефолта. */
.lore-page{--r: 0deg; position:relative; margin:0 0 7.5vw; padding:0; border-style:solid; border-color:transparent; border-width:25px; border-image-source:url('../img/lore_pages.png'); border-image-slice:50 fill; border-image-width:25px; border-image-outset:0; border-image-repeat:repeat; opacity:0; transform:rotate(var(--r)) translateY(14px);}
/*── Наклон закрытой стопки (2026-08-09) — вынесен из общих .lore-page:nth-of-
    type(N) в #loreStack.closed .lore-page:nth-of-type(N) специально, чтобы
    раскрытый поток (правило выше) оставался строго прямым (--r:0deg), а
    "книга на столе" в закрытом виде сохраняла лёгкий наклон каждой "обложки"
    в стопке — те же самые градусы, что были раньше, просто теперь не
    протекают в раскрытый вид. */
#loreStack.closed .lore-page:nth-of-type(1){--r:-1.6deg;}
#loreStack.closed .lore-page:nth-of-type(2){--r:1.1deg;}
#loreStack.closed .lore-page:nth-of-type(3){--r:-0.9deg;}
#loreStack.closed .lore-page:nth-of-type(4){--r:1.4deg;}
#loreStack.closed .lore-page:nth-of-type(5){--r:-1.2deg;}
/*── Закрытая стопка (обложка книги) — .lore-page складываются друг на друга
    внутри #loreStack, кликом раскрываются в обычный поток. Разворот сам —
    FLIP-анимация в openLoreStack() (ui.js), эти правила отвечают только за
    ЗАКРЫТЫЙ вид. opacity:1 здесь принудительно — иначе карточки, попадая
    внутрь #loreStack, наследовали бы opacity:0 из базового .lore-page
    (тот ждёт .revealed от scroll-in-view наблюдателя) и стопка была бы
    невидимой; сам глитч-reveal теперь взводится ПОСЛЕ разворота (см.
    _armLoreReveal() call в openLoreStack()), а не сразу при открытии
    экрана — иначе он сыграл бы ещё на закрытой стопке. ──────────────────*/
#loreStack{position:relative; padding-top:8px; padding-bottom:6vw;}
#loreStack .lore-page{opacity:1; transform:rotate(var(--r)) translateY(0);}
#loreStack.closed{cursor:pointer;}
#loreStack.closed .lore-page:nth-of-type(2),
#loreStack.closed .lore-page:nth-of-type(3),
#loreStack.closed .lore-page:nth-of-type(4){position:absolute; top:0; left:0; right:0; margin:0;}
#loreStack.closed .lore-page:nth-of-type(1){position:relative; z-index:4;}
#loreStack.closed .lore-page:nth-of-type(2){z-index:3; transform:rotate(var(--r)) translateY(2.4vw);}
#loreStack.closed .lore-page:nth-of-type(3){z-index:2; transform:rotate(var(--r)) translateY(4.8vw);}
#loreStack.closed .lore-page:nth-of-type(4){z-index:1; transform:rotate(var(--r)) translateY(7.2vw);}
.lore-page.revealed{animation:loreReveal 0.7s steps(4,end) forwards;}
.lore-page h2{margin-top:0;text-align:center;}
@media(min-width:600px){
  .lore-page{margin-bottom:3vw;}
}
@keyframes loreReveal{
  0%{text-shadow:none;}
  22%{text-shadow:1px 0 #ff0044,-1px 0 #00e5ff;}
  48%{text-shadow:-1px 0 #ff0044,1px 0 #00e5ff;}
  74%{text-shadow:0.5px 0 #ff0044,-0.5px 0 #00e5ff;}
  100%{text-shadow:none;}
}
/* ═══════════════════════════════════════════════════════════════════════
   CATALOG SCREEN
   ═══════════════════════════════════════════════════════════════════════ */
.card.tea-card,.card-small.tea-card,.cat-card.tea-card{background-image:url('../img/card_tea.png')!important;background-size:cover!important;background-position:center!important;background-repeat:no-repeat!important;}
.card.jeet-card,.card-small.jeet-card,.cat-card.jeet-card{background-image:url('../img/card_jeet.png')!important;background-size:cover!important;background-position:center!important;background-repeat:no-repeat!important;}
/* Golden Travelers (2026-07-30) — тот же принцип, что и у обычного фона выше, только
   .golden в довесок к .tea-card/.jeet-card — правило ниже идёт ПОСЛЕ базового, поэтому
   !important против !important побеждает более специфичный/более поздний селектор. */
.card.tea-card.golden,.card-small.tea-card.golden,.cat-card.tea-card.golden{background-image:url('../img/card_tea_gold.png')!important;background-size:cover!important;background-position:center!important;background-repeat:no-repeat!important;}
.card.jeet-card.golden,.card-small.jeet-card.golden,.cat-card.jeet-card.golden{background-image:url('../img/card_jeet_gold.png')!important;background-size:cover!important;background-position:center!important;background-repeat:no-repeat!important;}
/*── Модалки: иконки-кнопки вместо списков (win/deck-builder/deck-picker) ──*/
.modal-icon-btn{border:none;background:transparent;padding:2px;cursor:pointer;flex-shrink:0; background-size:100% 100%;background-repeat:no-repeat;background-position:center; transition:transform .1s,filter .1s; position:relative;}
.art-btn-catalog{background-image:url('../img/btn_catalog1.png');}
.art-btn-catalog:hover{filter:brightness(1);background-image:url('../img/btn_catalogH.png');}
.art-btn-catalog:active{background-image:url('../img/btn_catalog2.png');}
.cat-card{width:var(--card-w)!important;height:var(--card-h)!important;padding:var(--card-pad)!important;cursor:pointer!important;}
/*── КАТАЛОГ — хедер из 6 функциональных слоёв + 5 декор-слоёв между ними,
    см. комментарий в index.html. Каждый .cat-row — свой full-width фон
    (пока плейсхолдер, ждёт арт per-layer), внутри флексом сидят .cat-box —
    у каждого свой aspect-ratio (2:1/3:1/4:1/9:1 по таблице из чата, плюс
    исключения: back 2:1 своей высоты, title 6:1). box-sizing:border-box
    и НИГДЕ padding на .cat-row/.cat-box — только gap между боксами внутри
    ряда. Row-5 (счётчик+поиск) — единственный нестандартный по высоте:
    3vh моб / 6vh лаптоп (остальные функциональные ряды и декор — 2vh моб
    / 4vh лаптоп через media-запрос ниже). ────────────────────────────*/
.cat-header{width:100%; display:flex; flex-direction:column;}
.cat-row{width:100%; box-sizing:border-box; display:flex; align-items:center; justify-content:center; gap:0.6vh; background-color:#1c222b; background-size:100% 100%; background-repeat:no-repeat; background-position:center; flex-shrink:0;}
.cat-row-1{height:4vh;}
.cat-row-2{height:2vh;}
.cat-row-3{height:2vh;}
/*── Тип-фильтры — 6 боксов фикс-ширины (aspect-ratio держит их пропорцию
    к высоте ряда, а не к ширине контейнера), поэтому на узких/высоких
    экранах (iPad portrait и т.п.) сумма их ширин может не влезать —
    последний бокс просто обрезался бы. container query по СОБСТВЕННОЙ
    ширине ряда (не viewport) — если места не хватает, кнопки съезжаются
    внахлёст (как веер вкладок, отрицательный margin, обычный DOM-порядок
    рисует более поздние поверх более ранних — z-index не нужен); если
    места достаточно, gap остаётся обычным (0.6vh/1.2vh). Два порога —
    грубая, но безопасная ступенчатая деградация без точной физики
    переполнения (её нельзя точно посчитать в чистом CSS, т.к. ширина
    каждого бокса зависит от vh, а не от cqw). Если на практике вылезет
    другой брейкпоинт — подправить cqw-пороги ниже. ────────────────────*/
#typeFilters{container-type:inline-size; container-name:catTypeRow;}
@container catTypeRow (max-width: 760px){
  #typeFilters{gap:0;}
  #typeFilters .filter-btn:not(:first-child){margin-left:-2.6vh;}
  #typeFilters .filter-btn{position:relative; z-index:1;}
  #typeFilters .filter-btn.active{z-index:2;}
}
@container catTypeRow (max-width: 600px){
  #typeFilters .filter-btn:not(:first-child){margin-left:-1.4vh;}
}
.cat-row-4{height:2vh;}
.cat-row-5{height:3vh; justify-content:flex-start;}
.cat-row-6{height:2vh; justify-content:flex-end;}
/*── Декор-слои — чисто фоновые прокладки между функциональными рядами,
    без боксов внутри, той же тёмной заливкой во всех местах. ───────────*/
.cat-row-decor{height:2vh; background-color:#151a21;}
/*── Каждая из 12 строк хедера сверху вниз получает свой персональный фон
    (порядок строго по вёрстке в index.html — см. классы .cat-line-N там).
    background-image ложится ПОВЕРХ background-color с .cat-row/
    .cat-row-decor — та заливка остаётся как fallback, пока/если в PNG
    есть прозрачные места. Repeat-x + auto-ширина (не 100% 100% как у
    базового .cat-row) — картинки сделаны достаточно широкими сами по
    себе, при нехватке ширины на большом экране лучше честно повторить
    тайл, чем растянуть и размазать линию. ────────────────────────────*/
.cat-line-1,.cat-line-2,.cat-line-3,.cat-line-4,.cat-line-5,.cat-line-6,
.cat-line-7,.cat-line-8,.cat-line-9,.cat-line-10,.cat-line-11,.cat-line-12{background-repeat:repeat-x; background-size:auto 100%;}
.cat-line-1{background-image:url('../img/cat_1line.png');}
.cat-line-2{background-image:url('../img/cat_2line.png');}
.cat-line-3{background-image:url('../img/cat_3line.png');}
.cat-line-4{background-image:url('../img/cat_4line.png');}
.cat-line-5{background-image:url('../img/cat_5line.png');}
.cat-line-6{background-image:url('../img/cat_6line.png');}
.cat-line-7{background-image:url('../img/cat_7line.png');}
.cat-line-8{background-image:url('../img/cat_8line.png');}
.cat-line-9{background-image:url('../img/cat_9line.png');}
.cat-line-10{background-image:url('../img/cat_10line.png');}
.cat-line-11{background-image:url('../img/cat_11line.png');}
.cat-line-12{background-image:url('../img/cat_12line.png');}
/*── Back прибит слева абсолютным позиционированием внутри верхней
    декор-строки (не толкает соседей — там больше некому толкаться).
    Title сидит по центру своей строки за счёт justify-content:center
    на .cat-row (дефолт). ────────────────────────────────────────────*/
.cat-row-decor-top{position:relative;}
.cat-back{position:absolute; left:2vh; top:0;}
/*── Базовый бокс — БЕЗ рамки/цветной заливки/transition-эффектов, это
    теперь просто контейнер под background-image (арт). Текст везде
    чёрный (кроме инпута поиска — у него свой цвет, см. ниже). Явный
    сброс appearance/border/background — иначе на кнопках (<button>)
    остаётся нативный белый фон + 1px рамка от браузера, который
    просвечивает сквозь прозрачные углы трапециевидных PNG. ──────────*/
.cat-box{height:100%; box-sizing:border-box; display:flex; align-items:center; justify-content:center; font-family:'MEK',monospace; color:#0c1013; cursor:pointer; padding:0; margin:0; flex-shrink:0; white-space:nowrap; overflow:hidden; word-spacing:-0.3em; background-repeat:no-repeat; background-position:center; background-size:100% 100%; border:none; background-color:transparent; -webkit-appearance:none; -moz-appearance:none; appearance:none; image-rendering:pixelated;}
.cat-box.filter-btn{font-size:1.3vh;}
.cat-back{font-size:0.8vh; color:#E8E0C8;}
.cat-title{font-size:3vh;}
.cat-sort-label{cursor:default;}
.cat-count{cursor:default; color:#0c1013;}
/*── Ratio-классы: aspect-ratio + фон-плейсхолдер (idle) по системе
    cat_N_1_X.png — N=ratio (2/3/4), X=1(idle)/H(hover)/2(active-клик).
    Кнопки (.filter-btn и .cat-back) получают hover/click-состояния;
    некликабельные боксы того же ratio (например .cat-sort-label)
    молча используют тот же idle-фон, без hover/active правил. ───────*/
.cat-w2{aspect-ratio:2/1; background-image:url('../img/cat_2_1_1.png');}
.cat-w3{aspect-ratio:3/1; background-image:url('../img/cat_3_1_1.png');}
.cat-w4{aspect-ratio:4/1; background-image:url('../img/cat_4_1_1.png');}
.cat-w5{aspect-ratio:5/1;}
/* #gateFilters — 2026-07-24, по прямому запросу автора: кнопки-фильтры Врат сидят в
   decor-строке (cat-row-decor), которая на широких экранах становится 4vh (см. media query
   ниже) — при height:100% (унаследовано от .cat-box) кнопки раздувались на всю эту высоту
   и по умолчанию центрировались (align-items:center), оставляя видимый зазор и сверху, и
   снизу. Фикс: height:25% (кнопки на 75% меньше) + align-items:flex-end (прижаты к низу
   строки, весь лишний зазор уходит наверх, снизу — впритык). Работает одинаково что при
   2vh, что при 4vh строке — height в % от родителя, не фиксированное значение. */
#gateFilters{align-items:flex-end;container-type:inline-size;container-name:catGateRow;}
#gateFilters .cat-box{height:90%;}
/* Тот же оверлап-приём, что у #typeFilters выше (container query + отрицательный
   margin-left + z-index на .active) — 2026-07-24, по прямому запросу автора: кнопки
   Врат сближаются и выбранная показывается поверх соседних, адаптивно под ширину экрана. */
@container catGateRow (max-width: 760px){
  #gateFilters{gap:0;}
  #gateFilters .filter-btn:not(:first-child){margin-left:-2.6vh;}
  #gateFilters .filter-btn{position:relative; z-index:1;}
  #gateFilters .filter-btn.active{z-index:2;}
}
@container catGateRow (max-width: 600px){
  #gateFilters .filter-btn:not(:first-child){margin-left:-1.4vh;}
}
.cat-back{aspect-ratio:2/1; cursor:pointer; background-image:url('../img/cat_back_1.png');}
.cat-back:hover{background-image:url('../img/cat_back_H.png');}
.cat-back:active{background-image:url('../img/cat_back_2.png');}
.cat-w2.filter-btn:hover{background-image:url('../img/cat_2_1_H.png');}
.cat-w2.filter-btn:active,.cat-w2.filter-btn.active{background-image:url('../img/cat_2_1_2.png');}
.cat-w3.filter-btn:hover{background-image:url('../img/cat_3_1_H.png');}
.cat-w3.filter-btn:active,.cat-w3.filter-btn.active{background-image:url('../img/cat_3_1_2.png');}
.cat-w4.filter-btn:hover{background-image:url('../img/cat_4_1_H.png');}
.cat-w4.filter-btn:active,.cat-w4.filter-btn.active{background-image:url('../img/cat_4_1_2.png');}
/*── Некликабельные боксы со своим именованным ассетом (не по системе
    cat_N_1_X, т.к. у них нет idle/hover/active — только один фон). ───*/
.cat-title{aspect-ratio:6/1; cursor:default; background-image:url('../img/cat_title_6_1.png');}
.cat-count{background-image:url('../img/cat_counter_5_1.png'); margin-right:2vh;}
.cat-sort-label{background-image:url('../img/cat_sort.png');}
/*── Поиск — двойной бокс. .cat-search-wrap — 7:1, свой отдельный фон
    (cat_7_1.png, тоже без hover/active — не кликабелен), даёт запас
    слева/справа/сверху/снизу под рамку. Внутри него сидит сам инпут —
    высота ФИКСИРОВАНА (не 100% ряда и не завязана на ratio обёртки),
    поэтому смена 9:1→7:1 тут никак не влияет на размер самого инпута —
    просто у обёртки меньше запаса по бокам. ────────────────────────*/
.cat-w7{aspect-ratio:7/1;}
.cat-search-wrap{padding:0; margin-left:2vh; background-image:url('../img/cat_7_1.png');}
.cat-search-input{height:2vh; aspect-ratio:8/1; box-sizing:border-box; flex-shrink:0; border:none; background-color:#0c1013; font-family:'MEK',monospace; font-size:1.5vh; color:#E8E0C8; text-align:left; padding:0 1vh; -webkit-appearance:none; -moz-appearance:none; appearance:none; border-radius:0;}
.cat-search-input:focus{outline:none;}
.cat-search-input,.cat-search-input *{user-select:text;-webkit-user-select:text;}
/*── Лаптоп/десктоп — хедер каталога выглядел слишком мелко на широких
    экранах (vh привязан к высоте вьюпорта, а не к --crate-w), поэтому
    ТОЛЬКО от 600px удваиваем высоты рядов (4vh→8vh у row-1, 2vh→4vh у
    всех остальных функциональных и декор-рядов) и вслед за высотой —
    размеры шрифта в боксах (родные размеры завязаны на vh, а не на
    высоту бокса, так что сами по себе от роста рядов не подрастут).
    Мобила (<600px) не трогается вообще. ─────────────────────────────*/
@media(min-width:600px){
  .cat-row{gap:1.2vh;}
  .cat-row-1{height:8vh;}
  .cat-row-2{height:4vh;}
  .cat-row-3{height:4vh;}
  .cat-row-4{height:4vh;}
  .cat-row-5{height:6vh;}
  .cat-row-6{height:4vh;}
  .cat-row-decor{height:4vh;}
  .cat-box.filter-btn{font-size:2.6vh;}
  .cat-back{font-size:1.6vh;}
  .cat-title{font-size:6vh;}
  .cat-search-input{height:4vh; font-size:3vh;}
}
.catalog-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(var(--cat-card-w),1fr));gap:10px;container-type:inline-size;}
/*── .screen-body (родитель) красит весь текст в #9B937F — это глушило
    кремовый цвет стоимости карт, который в игре/деккбилдере приходит
    напрямую от body{color:#E8E0C8}, т.к. там карты не сидят внутри
    .screen-body. Возвращаем тот же кремовый явно, только для .card-cost
    внутри каталога. ──────────────────────────────────────────────────*/
.catalog-grid .card-cost{color:#E8E0C8;}
@media(max-width:600px){
  .catalog-grid{grid-template-columns:repeat(3, 1fr);row-gap:16px;}
  .cat-card{--card-w: calc((100cqw - 20px) / 3); --card-h: calc(var(--card-w) / 0.716); --card-art-size: calc(var(--card-h) * 0.535); --card-pad: calc(var(--card-h) * 0.021); --card-name-h: calc(var(--card-h) * 0.096); --card-stats-h: calc(var(--card-h) * 0.085); --card-text-h: calc(var(--card-h) * 0.24); --card-world-name-h: calc(var(--card-h) * 0.096); --card-world-text-h: calc(var(--card-h) * 0.16); width: var(--card-w) !important; height: var(--card-h) !important; padding: var(--card-pad) !important; cursor: pointer !important; overflow: hidden !important; margin: 0 auto;}
}
#deckBuilderModal{--modal-border-w: clamp(20px, 1.4815vw, 30px); --modal-footer-btn-h: clamp(40px, 2.963vw, 60px); --modal-footer-border-w: clamp(7px, 0.5185vw, 10.5px);}
.db-filter-btn.sort-all{background-image:url('../img/sort_all1.png');}
.db-filter-btn.sort-all:hover{background-image:url('../img/sort_allH.png');}
.db-filter-btn.sort-all:active{background-image:url('../img/sort_all2.png');}
.db-filter-btn.sort-all.active{background-image:url('../img/sort_allH.png');}
.db-filter-btn.sort-traveler{background-image:url('../img/sort_traveler1.png');}
.db-filter-btn.sort-traveler:hover{background-image:url('../img/sort_travelerH.png');}
.db-filter-btn.sort-traveler:active{background-image:url('../img/sort_traveler2.png');}
.db-filter-btn.sort-traveler.active{background-image:url('../img/sort_travelerH.png');}
.db-filter-btn.sort-unique{background-image:url('../img/sort_unique1.png');}
.db-filter-btn.sort-unique:hover{background-image:url('../img/sort_uniqueH.png');}
.db-filter-btn.sort-unique:active{background-image:url('../img/sort_unique2.png');}
.db-filter-btn.sort-unique.active{background-image:url('../img/sort_uniqueH.png');}
.db-filter-btn.sort-spell{background-image:url('../img/sort_spell1.png');}
.db-filter-btn.sort-spell:hover{background-image:url('../img/sort_spellH.png');}
.db-filter-btn.sort-spell:active{background-image:url('../img/sort_spell2.png');}
.db-filter-btn.sort-spell.active{background-image:url('../img/sort_spellH.png');}
.db-filter-btn.sort-world{background-image:url('../img/sort_world1.png');}
.db-filter-btn.sort-world:hover{background-image:url('../img/sort_worldH.png');}
.db-filter-btn.sort-world:active{background-image:url('../img/sort_world2.png');}
.db-filter-btn.sort-world.active{background-image:url('../img/sort_worldH.png');}
.db-filter-btn.sort-artifact{background-image:url('../img/sort_artifact1.png');}
.db-filter-btn.sort-artifact:hover{background-image:url('../img/sort_artifactH.png');}
.db-filter-btn.sort-artifact:active{background-image:url('../img/sort_artifact2.png');}
.db-filter-btn.sort-artifact.active{background-image:url('../img/sort_artifactH.png');}
/* ═══════════════════════════════════════════════════════════════════════
   DECKBUILDER (Rush) — сборка колоды
   ═══════════════════════════════════════════════════════════════════════ */
.modal-icon-btn.btn-ok:disabled{background-image:url('../img/btn_ok1.png');pointer-events:none;}
/*── Кнопка "назад" В УГЛУ МОДАЛКИ — deckPickerModal/vsAiPickerModal (НЕ mulliganScreen,*/
.modal-icon-btn.corner-back{position:absolute; bottom:0; left:0; width:var(--modal-border-w); height:var(--modal-border-w); z-index:6;}
/*── Кнопка "назад" В ФУТЕРЕ (deckBuilderModal — первая в общем ряду кнопок, того же*/
.modal-icon-btn.btn-back.placeholder::after{content:'⬅';font-size:60%;}
/*── Кнопка "очистить" В ФУТЕРЕ (deckBuilderModal, тот же ряд, что Back/Import/Export/OK).*/
.modal-icon-btn.btn-clear{background-image:url('../img/btn_clean1.png');}
#deckBuilderModal .modal-icon-btn.sq{width:var(--modal-footer-btn-h); height:var(--modal-footer-btn-h);}
/*Отдельный арт рамки — ТОЛЬКО для самого окна выбора карт (деккбилдер), не для всей*/
#deckBuilderModal .modal{border-image-source:url('../img/bg_modal_deck.png');}
/*── "Столбики" по бокам футера деккбилдера (bg_jest2_border.png) — отдельный 9-slice*/
#deckBuilderModal .db-footer-pillars{position:absolute; top:0; bottom:0; left:calc(-1 * var(--modal-footer-border-w)); right:calc(-1 * var(--modal-footer-border-w)); pointer-events:none; border-style:solid; border-width:var(--modal-footer-border-w); border-image-source:url('../img/bg_jest2_border.png'); border-image-slice:14 fill; border-image-width:var(--modal-footer-border-w); border-image-outset:0; border-image-repeat:stretch repeat; z-index:2;}
#deckBuilderModal .modal{border-image-slice: 40 fill; width:95vw; height:88vh; max-width:95vw; max-height:88vh; display:flex; flex-direction:column;}
#deckBuilderModal .modal-body{flex:1 1 auto; min-height:0; overflow:hidden;}
.db-header{flex:0 0 auto; background:#3d494f; border:2px solid #a9b5b2;}
.db-header-title{font-family:'MEK',monospace; font-size:2.8vw; letter-spacing:.5px; color:#0c1013; text-align:center; line-height:1; flex:1 1 0; display:flex; align-items:center; justify-content:center; box-sizing:border-box; border-style:solid; border-width:20px; border-image-source:url('../img/bg_jest.png'); border-image-slice:40 fill; border-image-width:20px; border-image-outset:0; border-image-repeat:stretch;}
.db-header-title-divider{display:none;}
.db-filters-col{flex:1 1 0; min-width:0; display:flex; flex-direction:column; gap:4px;}
.db-header-row2{display:flex; align-items:stretch; padding:8px; gap:0;}
.db-header-left{flex:1 1 0; min-width:0; display:flex; justify-content:space-evenly; align-items:center; gap:10px; padding-right:16px;}
.db-decor{display:none;}
/*Только лаптоп/десктоп (2026-07-08): 3 чёрных плейсхолдера под будущий декор-арт*/
@media(min-width:601px){
  .db-header-row2{height:20vh;}
  .db-header-left{container-type:inline-size; justify-content:space-between;}
  .db-decor{display:block; height:100%; background:#0d0d1a;}
  .db-decor-left,.db-decor-right{flex:0 0 auto; aspect-ratio:1/3;}
  .db-decor-mid{flex:0 0 auto; aspect-ratio:1/2;}
  .db-decor-right{background-image:url('../img/db_decorR.png');background-size:100% 100%; background-repeat:no-repeat; flex-shrink:0;}
  .db-curve{height:100%;}
  .db-filters-col{gap:8px;justify-content:space-between;}
  .db-header-title{flex:0 0 auto;align-self:center;}
  .db-header-title-divider{display:block;flex:0 0 auto;align-self:stretch;height:2px;background:#181f27;}
}
.db-divider-h{flex:0 0 auto; height:2px; background:#3d494f;}
.db-divider-v{align-self:stretch; width:2px; flex:0 0 auto; background:#3d494f;}
#deckBuilderNextBtn:disabled{cursor:not-allowed;filter:none!important;}
.db-split{display:flex;gap:16px;align-items:stretch;height:100%;}
.db-pane{flex:1 1 0;min-width:0;overflow-y:auto;height:100%; scrollbar-width:thin; scrollbar-color:#5a7278 #0c1013;}
.db-pane::-webkit-scrollbar{width:8px;}
.db-pane::-webkit-scrollbar-track{background:#0c1013;border-radius:0;}
.db-pane::-webkit-scrollbar-thumb{background:#5a7278;border-radius:0;}
.db-pane::-webkit-scrollbar-thumb:hover{background:#5a7278;}
.db-filters{display:flex;gap:4px;flex:1 1 0;min-width:0;box-sizing:border-box;padding:10px 10px;background:#0c1013;background-clip:content-box;}
.db-filter-btn{flex:1 1 0; min-width:0; aspect-ratio:2/1; border:none; background-color:transparent; background-size:100% 100%; background-repeat:no-repeat; background-position:center; font-size:0; line-height:0; padding:0; cursor:pointer; transition:filter .1s;}
.db-filter-btn:hover{filter:brightness(1);}
.db-filter-btn:active{filter:brightness(1);}
@media(min-width:601px){
  .db-filters{justify-content:center;align-items:center;flex:0 0 auto;align-self:center;}
  .db-filter-btn{flex:0 0 auto;height:4vh;align-self:center;}
}
.db-grid{display:grid; grid-template-columns:repeat(3, 1fr); row-gap:10px; column-gap:2.5px; container-type:inline-size; padding:4px 6px;}
.db-stack{--card-w: min(calc((100cqw - 2 * 2.5px) / 3), var(--cat-card-w)); --card-h: calc(var(--card-w) / 0.716); --card-art-size: calc(var(--card-h) * 0.535); --card-pad: calc(var(--card-h) * 0.021); --card-name-h: calc(var(--card-h) * 0.096); --card-stats-h: calc(var(--card-h) * 0.085); --card-text-h: calc(var(--card-h) * 0.24); --card-world-name-h: calc(var(--card-h) * 0.096); --card-world-text-h: calc(var(--card-h) * 0.16); position:relative; width:var(--card-w); height:var(--card-h); margin:0 auto;}
.db-card{width:100%!important; height:100%!important; padding:var(--card-pad)!important; position:relative; z-index:1; transition:filter .12s;}
.db-card:hover{filter:brightness(1.12);}
@media(max-width:600px){
  .db-split{flex-direction:column;gap:6px;}
  .db-pane{flex:1 1 0;min-height:0;height:auto;}
  .db-pane-pool{order:-2;}
  .db-split > .db-divider-v{order:-1; width:auto; align-self:stretch; height:2px; background:#5a7278;}
  .db-pane-chosen{order:0;}
  .db-grid{grid-template-columns:repeat(3, 1fr);gap:5px;}
  .db-stack{--card-w: min(calc((100cqw - 2 * 5px) / 3), var(--cat-card-w));}
  .db-header-row2{flex-wrap:wrap;row-gap:8px;}
  .db-filters-col{display:contents;}
  .db-header-title{order:-3;flex:1 1 100%;font-size:7vw;}
  .db-header-left{order:-2; flex:1 1 100%; padding-right:0; position:relative; padding-top:8px;}
  .db-header-left::before{content:''; position:absolute; top:0; left:6px; right:6px; height:2px; background:#212e37;}
  .db-header-row2 > .db-divider-v{display:none;}
  .db-filters{order:0; flex:1 1 100%; flex-wrap:nowrap; position:relative; padding-top:8px;}
  .db-filters::before{content:''; position:absolute; top:0; left:6px; right:6px; height:2px; background:#212e37;}
}
/*── Стопки копий (заклинания с max>1, см. getRushPool в deck.js) ────────────────*/
.db-stack-layer{position:absolute; inset:0; border-radius:5%; filter:brightness(.4) saturate(.6); transform:translate(calc(var(--layer-i) * 4px), calc(var(--layer-i) * 4px)); z-index:0;}
.db-stack-layer.tea-card{background-image:url('../img/card_tea.png');background-size:cover;background-position:center;}
.db-stack-layer.jeet-card{background-image:url('../img/card_jeet.png');background-size:cover;background-position:center;}
.db-stack-layer.tea-card.golden{background-image:url('../img/card_tea_gold.png');background-size:cover;background-position:center;}
.db-stack-layer.jeet-card.golden{background-image:url('../img/card_jeet_gold.png');background-size:cover;background-position:center;}
/*── Футер контента: статистика (слева, построчно) + кривая маны (справа, чистый CSS) ──*/
.db-stats{flex:0 0 auto;font-size:11px;color:#E8E0C8;line-height:1.2;justify-content:center; padding:4px 8px; border-radius:2px; border:1px solid #a9b5b2; box-shadow:inset 0 0 0 2px #000;}
.db-stat-line.db-count-ok{color:#5BDF7A;}
.db-curve{flex:0 0 auto; aspect-ratio:1/1; height:100%; display:flex; align-items:flex-end; gap:3px; padding:4px; box-sizing:border-box; border-radius:2px; border:1px solid #a9b5b2; box-shadow:inset 0 0 0 2px #000;}
.db-curve-bar-wrap{flex:1 1 0;display:flex;flex-direction:column;align-items:center;justify-content:flex-end;height:100%;}
.db-curve-bar{width:100%; background:#c8a84b99; border-top:2px solid #c8a84b; transition:height .15s;}
.db-curve.tea-curve .db-curve-bar{background:#E8E0C899;border-top-color:#E8E0C8;}
.db-curve.jeet-curve .db-curve-bar{background:#E8E0C899;border-top-color:#E8E0C8;}
.db-curve-n{font-size:9px;color:#E8E0C8;height:11px;}
.db-curve-label{font-size:9px;color:#E8E0C8;}
@media(min-width:601px){
  .db-stats{flex:0 0 auto;aspect-ratio:4/3;box-sizing:border-box;font-size:2.2cqh;height:100%;overflow:hidden;container-type:size;}
  .db-curve-n{font-size:1.7cqw;}
  .db-curve-label{font-size:1.7cqw;}
}
/* ═══════════════════════════════════════════════════════════════════════
   MODALS — общая система (стек/футер/иконки-кнопки/CRT) + confirm/win/mulligan/pass/graveyard/battle-log
   ═══════════════════════════════════════════════════════════════════════ */
.hand .card.affordable, #mulliganCards .card.affordable{animation:goldPulseWeak 1.8s ease-in-out infinite;}
.modal-icon-btn.sq{width:6vh;height:6vh;}
#winModal .modal-icon-btn.sq{width:var(--modal-footer-btn-h);height:var(--modal-footer-btn-h);}
.modal-icon-btn.wide{height:var(--modal-footer-btn-h);aspect-ratio:2/1;}
.modal-icon-btn.placeholder{border:1px solid #E8E0C8;background-color:#0d0d1a;}
.modal-icon-btn.placeholder::after{content:''; position:absolute;inset:0; display:flex;align-items:center;justify-content:center; font-size:calc(6vh * 0.55);}
.modal-icon-btn.wide.placeholder::after{font-size:13px;line-height:1.3;text-align:center;padding:4px;white-space:normal;}
.modal-icon-btn.placeholder:hover{filter:brightness(1.25);}
.modal-icon-btn.placeholder:active{transform:scale(0.92) translateY(1px);}
.modal-icon-btn.btn-home{background-image:url('../img/btn_home1.png');}
.modal-icon-btn.btn-home:hover{background-image:url('../img/btn_homeH.png');filter:brightness(1);}
.modal-icon-btn.btn-home:active{background-image:url('../img/btn_home2.png');filter:brightness(1);transform:none;}
.modal-icon-btn.btn-repeat{background-image:url('../img/btn_repeat1.png');}
.modal-icon-btn.btn-repeat:hover{background-image:url('../img/btn_repeatH.png');filter:brightness(1);}
.modal-icon-btn.btn-repeat:active{background-image:url('../img/btn_repeat2.png');filter:brightness(1);transform:none;}
.modal-icon-btn.btn-save{background-image:url('../img/btn_save1.png');}
.modal-icon-btn.btn-save:hover{background-image:url('../img/btn_saveH.png');filter:brightness(1);}
.modal-icon-btn.btn-save:active{background-image:url('../img/btn_save2.png');filter:brightness(1);transform:none;}
.modal-icon-btn.btn-classic{background-image:url('../img/btn_classic1.png');}
.modal-icon-btn.btn-classic:hover{background-image:url('../img/btn_classicH.png');filter:brightness(1);}
.modal-icon-btn.btn-classic:active{background-image:url('../img/btn_classic2.png');filter:brightness(1);transform:none;}
.modal-icon-btn.btn-rush{background-image:url('../img/btn_rush1.png');}
.modal-icon-btn.btn-rush:hover{background-image:url('../img/btn_rushH.png');filter:brightness(1);}
.modal-icon-btn.btn-rush:active{background-image:url('../img/btn_rush2.png');filter:brightness(1);transform:none;}
/* Story под-экран (2026-08-08) — .modal-faction-btn, тот же класс/размер, что и кнопки
   выбора фракции в vsAiPickerModal (см. .modal-faction-btn выше по файлу), по прямому
   запросу автора. Ассеты автор кладёт в img/ локально — три состояния idle/hover/active
   на каждую, тот же btn_XXX1/XXXH/XXX2 конвеншн, что и у остальных кастомных кнопок выше
   (btn-classic/btn-rush/btn-home и т.д.) — простая смена background-image, без
   filter/transform на hover/active (тут .modal-faction-btn и так transition:none по
   умолчанию, в отличие от .modal-icon-btn — ничего гасить не требуется).*/
.modal-faction-btn.btn-story-burnfear{background-image:url('../img/btn_burnfear1.png');}
.modal-faction-btn.btn-story-burnfear:hover{background-image:url('../img/btn_burnfearH.png');}
.modal-faction-btn.btn-story-burnfear:active{background-image:url('../img/btn_burnfear2.png');}
.modal-faction-btn.btn-story-sagafoxy{background-image:url('../img/btn_sagafoxy1.png');}
.modal-faction-btn.btn-story-sagafoxy:hover{background-image:url('../img/btn_sagafoxyH.png');}
.modal-faction-btn.btn-story-sagafoxy:active{background-image:url('../img/btn_sagafoxy2.png');}
.modal-icon-btn.btn-imp{background-image:url('../img/btn_imp1.png');}
.modal-icon-btn.btn-imp:hover{background-image:url('../img/btn_impH.png');filter:brightness(1);}
.modal-icon-btn.btn-imp:active{background-image:url('../img/btn_imp2.png');filter:brightness(1);transform:none;}
.modal-icon-btn.btn-exp{background-image:url('../img/btn_exp1.png');}
.modal-icon-btn.btn-exp:hover{background-image:url('../img/btn_expH.png');filter:brightness(1);}
.modal-icon-btn.btn-exp:active{background-image:url('../img/btn_exp2.png');filter:brightness(1);transform:none;}
.modal-icon-btn.btn-ok{background-image:url('../img/btn_ok1_1.png');}
.modal-icon-btn.btn-ok:hover{background-image:url('../img/btn_okH.png');filter:brightness(1);}
.modal-icon-btn.btn-ok:active{background-image:url('../img/btn_ok2.png');filter:brightness(1);transform:none;}
.modal-icon-btn.btn-back-corner{background-image:url('../img/btn_back_corner1.png');}
.modal-icon-btn.btn-back-corner:hover{background-image:url('../img/btn_back_cornerH.png');filter:brightness(1);}
.modal-icon-btn.btn-back-corner:active{background-image:url('../img/btn_back_corner2.png');filter:brightness(1);transform:none;}
.modal-icon-btn.btn-back{background-image:url('../img/btn_back1.png');}
.modal-icon-btn.btn-back:hover{background-image:url('../img/btn_backH.png');filter:brightness(1);}
.modal-icon-btn.btn-back:active{background-image:url('../img/btn_back2.png');filter:brightness(1);transform:none;}
.modal-icon-btn.btn-clear:hover{background-image:url('../img/btn_cleanH.png');filter:brightness(1);}
.modal-icon-btn.btn-clear:active{background-image:url('../img/btn_clean2.png');filter:brightness(1);transform:none;}
/*── Лог-панель (2026-08-03, по прямому запросу автора): раньше сама была position:fixed,
   прибита в правый верхний угол, закрывалась только по крестику. Теперь обёрнута в
   #logOverlay (.modal-overlay — тот же паттерн, что у GRAVEYARD MODAL: fixed+flex-центр
   на весь экран, klик по фону = закрыть) — сама панель просто блок нормального размера
   внутри центрированного оверлея, без своего fixed/z-index/top/right. ──*/
.log-panel{width:min(max(calc(2 * var(--card-w) + 44px), 280px), 92vw); background:transparent; border-style:solid; border-width:20px; border-image-source:url('../img/log_frame.png'); border-image-slice:40 fill; border-image-width:20px; border-image-outset:0; border-image-repeat:stretch; box-sizing:border-box; filter:drop-shadow(0 4px 20px #000000aa);}
/* .jeet (2026-08-06, по прямому запросу автора) — тот же приём, что у .grave-modal.jeet
   выше: свой ассет 9-слайса рамки для баттл-лога у джит (log_frame_jeet.png), те же
   параметры border-image, что у общей .log-panel. Класс .jeet/.tea навешивается в
   render.js (см. её комментарий у logPanelEl рядом с скином кнопки лога). */
.log-panel.jeet{border-image-source:url('../img/log_frame_jeet.png');}
.log-panel.open{display:block;}
.log-panel.open .log{animation:crtFlicker 6s ease-in-out infinite;}
/*── Хедер лог-панели ─────────────────────────────────────────────────────────*/
.log-panel-header{background:transparent; border-style:solid; border-width:10px; border-image-source:url('../img/bg_jest_log.png'); border-image-slice:20 fill; border-image-width:10px; border-image-outset:0; border-image-repeat:stretch; box-sizing:border-box; display:flex;justify-content:center;align-items:center; padding:0 6px; position:relative;}
/* .jeet (2026-08-08, по прямому запросу автора) — тот же приём, что у .log-panel.jeet
   (рамка) и .grave-modal.jeet .grave-modal-header (хидер кладбища) выше: свой ассет для
   хидера баттл-лога джит (bg_jest_log_jeet.png), остальные border-image-параметры те же. */
.log-panel.jeet .log-panel-header{border-image-source:url('../img/bg_jest_log_jeet.png');}
.log-panel-title{font-size:max(calc(5vh * 0.665), calc(2.5vw * 0.665));color:#0c1013;font-family:'MEK',monospace;text-align:center;
  /* 2026-07-17 (баг, автор — крестик наезжает на заголовок на вертикальном iPad): .log-panel-close
     позиционируется absolute и выпадает из flex-потока, поэтому центрированный текст заголовка
     ничего не "знает" про него и может залезть под кнопку на широком шрифте/узкой панели.
     max-width резервирует место под кнопку с обеих сторон (панель зеркально симметрична,
     кнопка только справа, но текст всё равно центрирован — проще подрезать с обеих сторон
     одинаково, чем городить asymmetric margin), плюс safety-net на переполнение — теперь
     заголовок физически не может залезть под кнопку, независимо от того, насколько большим
     окажется шрифт на каком-то экране. */
  max-width:calc(100% - 2 * max(calc(5vh * 0.798), calc(2.5vw * 0.798)) - 12px);
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap;}
/* .jeet (2026-08-08, по прямому запросу автора) — заголовок баттл-лога джит кремовым
   (--color-cream), тот же принцип, что у .grave-modal.jeet .grave-modal-title выше. */
.log-panel.jeet .log-panel-title{color:var(--color-cream);}
/*── БЫЛО: "Вертикальный iPad — панель лога фиксированной ширины (320px)..." — этот костыль
   уменьшал шрифт заголовка лога специально на планшете-портрете, компенсируя то, что
   .log-panel тогда был жёстко 320px и мог не вписать vh-формулу шрифта. С тех пор
   .log-panel уже переехал на ту же responsive-формулу ширины, что у .grave-modal (см. её
   правило выше по файлу) — сама причина костыля исчезла, а override остался и продолжал
   резать шрифт заголовка лога именно на этом диапазоне экранов (601-1024px, портрет),
   из-за чего "Battle Log" всё ещё выглядел мельче "Graveyard" даже после фикса ширины
   панели (автор поймал это ЖИВЬЁМ на iPad, сравнил заголовки в упор — они физически
   были разного размера, не показалось). Убрано целиком (2026-08-04) — теперь
   .log-panel-title всегда считается той же безусловной max(5vh,2.5vw) формулой, что и
   .grave-modal-title, без скидок под конкретный диапазон экранов. ──*/
.log-panel-close{position:absolute;right:6px;top:50%;transform:translateY(-50%);background-image:url('../img/btn_X.png');background-size:100% 100%;background-color:transparent;width:max(calc(5vh * 0.798), calc(2.5vw * 0.798));height:max(calc(5vh * 0.798), calc(2.5vw * 0.798));cursor:pointer;border:none;padding:0;font-size:0;line-height:0;}
.log-panel-close:hover{background-image:url('../img/btn_Xh.png');}
.log-panel-close:active{background-image:url('../img/btn_X2.png');}
.log-panel .log{height:180px;border:none;}
.log::-webkit-scrollbar{width:4px;}
.log::-webkit-scrollbar-track{background:#0a0a12;}
.log::-webkit-scrollbar-thumb{background:#1a7a2e;border-radius:0;}
.log::-webkit-scrollbar-thumb:hover{background:#5BDF7A;}
.log{position:relative; background:#03150a; border:none; padding:7px; height:85px; overflow-y:auto; font-size:12px; line-height:1.9; color:#33ff66;}
.log::after{content:''; position:absolute; inset:0; background:repeating-linear-gradient( to bottom, transparent 0px, transparent 3px, rgba(0,0,0,0.16) 3px, rgba(0,0,0,0.16) 6px ); pointer-events:none; z-index:1;} /* линии сканирования ослаблены вдвое (было 0.32) — автор 2026-07-17, читаемость лога */
.log::before{content:''; position:absolute; inset:0; background:radial-gradient(ellipse at center,transparent 55%,rgba(0,0,0,0.25) 100%); pointer-events:none; z-index:2;} /* виньетка ослаблена вдвое (было 0.5) */
.log .le{color:#1a7a2e;}
.log .le.imp{color:#33ff66;}
.log .le.dmg{color:#ff4466;}
.log .le.hl{color:#66ffaa;}
.log .le.die{color:#ff3333;}
.log .le.trn{color:#55ff88; border-top:1px solid #1a4422; padding-top:3px; margin-top:3px;}
.le{color:#555;}
.le.imp{color:#c8a84b;}
.le.dmg{color:#e05555;}
.le.hl{color:#5BDF7A;}
.le.die{color:#ff6666;}
.le.trn{color:#BFCBFF;border-top:1px solid #ffffff11;padding-top:3px;margin-top:3px;}
/* z-index:10000 (2026-08-06, багфикс по прямому запросу автора — "победная модалка на
   чёрном фоне, а сквозь неё видно оставшиеся цифры финального урона по базе"). Раньше было
   650 — этого хватало против обычного игрового UI, но .float-number-base (см. flashBase()
   в game.js) с 2026-08-05 живёт как position:fixed на document.body с z-index:9999 (см.
   .float-number в styles.css) — чтобы гарантированно не потеряться под соседними
   элементами интерфейса, а не потому что должен быть ВЫШЕ модалок. Модалка по своей роли
   обязана перекрывать вообще всё, включая любые количество переходных игровых эффектов
   (текущий максимум в проекте — 9999 у float-number) — 10000 держит это с запасом. */
.modal-overlay{position:fixed;inset:0;background:url('../img/bg_corridor.png') center/cover no-repeat;z-index:10000;display:flex;align-items:center;justify-content:center;overflow:hidden;}
/*── .modal-overlay-ingame (2026-08-14, по прямому запросу автора — "Restart/Menu из
   бургер-меню должны вылезать поверх игры с заблюренным фоном, как правила из бургера")
   — модификатор ПОВЕРХ базового .modal-overlay выше: перебивает url()-фон коридора на
   полупрозрачную темноту + backdrop-filter blur, чтобы сквозь модалку было видно (размыто)
   само игровое поле, а не декоративный коридор лендинга. Значения (rgba/blur) взяты
   1-в-1 у #rulesScreen.rules-ingame (rules-book.js/styles.css) — тот же приём уже
   применён к экрану правил, открытому из активного матча через бургер-меню, для
   визуальной консистентности между двумя путями "открыть оверлей поверх игры".
   z-index не трогаем — базовый 10000 у .modal-overlay и так уже гарантированно перекрывает
   игровое поле (см. комментарий чуть выше про 10000 vs 9999 у float-number), менять нечего,
   разница только в background/backdrop-filter. #confirmModal переиспользуется и ВНЕ игры
   (импорт колоды в deckbuilder.js) — там класс не навешивается, остаётся обычный коридор.*/
.modal-overlay.modal-overlay-ingame{background:rgba(10,10,18,0.35); backdrop-filter:blur(6px); -webkit-backdrop-filter:blur(6px);}
/*── Размер карт муллигана "пляшет" от экрана к экрану (2026-08-04, автор попросил
   "лекарство" из соседнего разговора про --card-h) — они используют тот же .card, что и
   рука/кладбище, а у него --card-h чистый 25vh БЕЗ min/max, в отличие от аккуратно
   зажатых clamp()-формул у карт поля. Замерено живьём: ширина карты муллигана гуляла от
   56px (телефон лёжа) до 322px (высокий десктоп-портрет) — почти 6-кратный разброс.
   Скопировано ЗДЕСЬ, а не в :root, по прямой просьбе автора ("попробуем для мулиган
   окна") — не трогает --card-h руки/кладбища нигде за пределами этого экрана.
   ВАЖНО (грабли №1, была починена не до конца — автор поймал живьём вторым заходом,
   "фон карт и размер встал правильно, а все ассеты внутри ужались", телефон-лендскейп):
   --card-w и ВСЕ card-name-h/stats-h/art-size/pad/text-h/world-* в :root заданы через
   calc(var(--card-h) * X) — но CSS-переменные не "пересчитываются реактивно" у потомков,
   каждая из них вычисляется ОДИН раз в scope :root (используя ТОГДАШНИЙ --card-h, в
   лендскейпе это 20vh из отдельного @media ниже по файлу) и наследуется уже готовым
   числом. В прошлый раз переопределил только --card-w здесь — этого хватило на размер
   САМОЙ карты (совпадает с новым --card-h), но ВСЁ содержимое внутри (арт/имя/статы/
   текст способности) по-прежнему считало отступы от старой 20vh-версии — отсюда и
   "ужалось" (числа мельче, чем нужно новому увеличенному размеру карты, внутри
   образовалась пустота). Теперь переопределены ВСЕ card-*-h/w/size/pad переменные разом,
   той же формулой, что в :root — каждая пересчитывается уже от локального --card-h.
   Числа (140-300px) ориентировочные — стоит ещё раз сверить живьём под реальный контент
   модалки, если автор попросит подвинуть. ──*/
#mulliganScreen{overflow-y:auto;align-items:safe center;padding:24px 0;
  --card-h: clamp(140px, 25vh, 300px);
  --card-w: calc(var(--card-h) * 0.716);
  --card-action-btn-w: calc(var(--card-w) * 0.75 * 44 / 122);
  --card-art-size: calc(var(--card-h) * 0.535);
  --card-pad: calc(var(--card-h) * 0.021);
  --card-name-h: calc(var(--card-h) * 0.096);
  --card-stats-h: calc(var(--card-h) * 0.085);
  --card-text-h: calc(var(--card-h) * 0.24);
  --card-world-name-h: calc(var(--card-h) * 0.096);
  --card-world-text-h: calc(var(--card-h) * 0.16);}
.modal{position:relative; background:transparent; border-style:solid; border-width:var(--modal-border-w); border-image-source:url('../img/bg_modal.png'); border-image-slice:108 fill; border-image-width:var(--modal-border-w); border-image-outset:0; border-image-repeat:repeat; padding:0; box-sizing:border-box; width:min(calc(300px + 2 * var(--modal-border-w)), 92vw); max-width:calc(300px + 2 * var(--modal-border-w)); text-align:center; display:flex; flex-direction:column; overflow:hidden;}
/*── Белое свечение вокруг модалок (2026-08-14, по прямому запросу автора;
   явно ИСКЛЮЧЕНО из .crate-modal/Lore/Catalog тем же заходом — автор
   попросил свечение только на .modal) ────────────────────────────────────
   box-shadow, не filter:drop-shadow — border-image у .modal не режется
   border-radius (углы прямоугольные), так что обычный прямоугольный
   box-shadow ложится ровно по границе рамки без артефактов. Два слоя —
   плотное ядро у самой рамки + мягкий дальний ореол — читается как единое
   свечение, а не жёсткая обводка.*/
.modal{box-shadow: 0 0 16px 2px rgba(255,255,255,0.45), 0 0 42px 10px rgba(255,255,255,0.22);}
/*── Декоративные плейсхолдеры поверх рамки/фона модалки ─────────────────────*/
.modal-decor{position:absolute; background:#000; pointer-events:none; z-index:5;}
.modal-decor-square{width:var(--modal-border-w); height:var(--modal-border-w);}
.modal-decor-wide{width:calc(var(--modal-border-w) * 2); height:var(--modal-border-w);}
/*── "Обвес"-футер: жесть, прибитая поверх нижней кромки рамки ────────────────*/
.modal-stack{position:relative; display:inline-flex; flex-direction:column; align-items:center;}
.modal-footer-plate{position:absolute; bottom:0; left:50%; transform:translateX(-50%); z-index:6;}
.modal-decor-tl{top:calc(-1 * var(--modal-border-w));left:calc(-1 * var(--modal-border-w));}
.modal-decor-tr{top:calc(-1 * var(--modal-border-w));right:calc(-1 * var(--modal-border-w));}
.modal-decor-bl{bottom:calc(-1 * var(--modal-border-w));left:calc(-1 * var(--modal-border-w));}
.modal-decor-br{bottom:calc(-1 * var(--modal-border-w));right:calc(-1 * var(--modal-border-w));}
.modal-decor-top{top:calc(-1 * var(--modal-border-w));left:50%;transform:translateX(-50%);}
.modal-decor-bottom{bottom:calc(-1 * var(--modal-border-w));left:50%;transform:translateX(-50%);}
/*── Арт-кнопки внутри modal-footer ────────────────────────────────────────────*/
.modal-art-btn{background-size:100% 100%; background-repeat:no-repeat; background-position:center; background-color:transparent; border:none; height:var(--modal-footer-btn-h); aspect-ratio:3/1; font-size:0; line-height:0; padding:0; cursor:pointer; transition:filter 0.1s;}
.modal-art-btn:hover{filter:brightness(1.2);}
.modal-art-btn:active{filter:brightness(0.9);}
.modal-art-btn.btn-yes{aspect-ratio:2/1; background-image:url('../img/btn_yes1.png');}
.modal-art-btn.btn-yes:hover{background-image:url('../img/btn_yesH.png');}
.modal-art-btn.btn-yes:active{background-image:url('../img/btn_yes2.png');}
.modal-art-btn.btn-cancel{background-image:url('../img/btn_cancel1.png');}
.modal-art-btn.btn-cancel:hover{background-image:url('../img/btn_cancelH.png');}
.modal-art-btn.btn-cancel:active{background-image:url('../img/btn_cancel2.png');}
.modal-art-btn.btn-mulligan{aspect-ratio:2/1; background-image:url('../img/btn_mulligan1.png');}
.modal-art-btn.btn-mulligan:hover{background-image:url('../img/btn_mulliganH.png');}
.modal-art-btn.btn-mulligan:active{background-image:url('../img/btn_mulligan2.png');}
.modal-art-btn.btn-mulligan:disabled{background-image:url('../img/btn_mulliganD.png'); cursor:not-allowed; pointer-events:none;}
.modal-art-btn.btn-ready{aspect-ratio:2/1; background-image:url('../img/btn_ready1.png');}
.modal-art-btn.btn-ready:hover{background-image:url('../img/btn_readyH.png');}
.modal-art-btn.btn-ready:active{background-image:url('../img/btn_ready2.png');}
.modal-art-btn.btn-win{aspect-ratio:2/1; background-image:url('../img/btn_ready1.png');}
.modal-art-btn.btn-win:hover{background-image:url('../img/btn_readyH.png');}
.modal-art-btn.btn-win:active{background-image:url('../img/btn_ready2.png');}
.modal-text-btn{font-family:'MEK',monospace; font-size:14px; padding:10px 24px; border:1px solid #c8a84b88; background:transparent; color:#c8a84b; cursor:pointer; letter-spacing:2px; transition:all .15s;}
.modal-text-btn:hover{background:#c8a84b18;border-color:#c8a84b;}
.modal-faction-btn.btn-tea{background-image:url('../img/btn_tea1.png');}
.modal-faction-btn.btn-tea:hover{background-image:url('../img/btn_teaH.png');}
.modal-faction-btn.btn-tea:active{background-image:url('../img/btn_tea2.png');}
.modal-faction-btn.btn-jeet{background-image:url('../img/btn_jeet1.png');}
.modal-faction-btn.btn-jeet:hover{background-image:url('../img/btn_jeetH.png');}
.modal-faction-btn.btn-jeet:active{background-image:url('../img/btn_jeet2.png');}
.modal-body{padding:clamp(12px,1.5vw,18px); flex:1;}
.modal-stack .modal-body{display:flex; flex-direction:column; justify-content:center;}
.modal h2{font-family:'MEK',monospace;font-size:var(--modal-h2-fs);color:#D9d89e;margin-bottom:5px;}
.modal p{font-size:clamp(13px,3.2vw,16px);color:#a2bf5e;margin-bottom:18px;line-height:1.6;}
.modal p.deck-picker-line{margin-bottom:6px;}
.deck-picker-line strong{color:#c8a84b;}
.modal-crt-screen > *:last-child{margin-bottom:0;}
/*── ".crt-bg" — переиспользуемый "голый" CRT-фон (цвет + линии скана + виньетка +*/
.crt-bg{position:relative;background:#0a2f18;overflow:hidden;}
.crt-bg::after{content:'';position:absolute;inset:0;z-index:1;pointer-events:none;background:repeating-linear-gradient(to bottom,transparent 0px,transparent 3px,rgba(0,0,0,0.24) 3px,rgba(0,0,0,0.24) 6px);animation:crtFlicker 6s ease-in-out infinite;}
.crt-bg::before{content:'';position:absolute;inset:0;z-index:2;pointer-events:none;background:radial-gradient(ellipse at center,transparent 55%,rgba(0,0,0,0.35) 100%);}
.crt-bg > *{position:relative;z-index:3;}
/*── "CRT-экран" под текстом простых модалок (deckPicker/vsAiPicker/confirm/win — только*/
.modal-crt-screen{position:relative; background:#03150a; width:100%; height:100%; box-sizing:border-box; display:flex; flex-direction:column; justify-content:center; padding:14px; animation:crtFlicker 6s ease-in-out infinite;}
.modal-crt-screen::after{content:''; position:absolute; inset:0; background:repeating-linear-gradient( to bottom, transparent 0px, transparent 3px, rgba(0,0,0,0.32) 3px, rgba(0,0,0,0.32) 6px ); pointer-events:none; z-index:1;}
.modal-crt-screen::before{content:''; position:absolute; inset:0; background:radial-gradient(ellipse at center,transparent 55%,rgba(0,0,0,0.5) 100%); pointer-events:none; z-index:2;}
/*── .crt-weak — та же CRT-разметка, но линии сканирования/виньетка вдвое слабее.
    Только для мулиган-окна (автор 2026-07-17, читаемость рук в мулигане) — остальные
    5 модалок на .modal-crt-screen (deckPicker/vsAiPicker/confirm/win/pass) не трогаем,
    им отдельного запроса не было. Порядок в файле — ПОСЛЕ базовых правил выше, чтобы
    переопределить их (тот же принцип "позже в файле побеждает", что и у языковых
    шрифтов Rules Book). ──*/
.modal-crt-screen.crt-weak::after{background:repeating-linear-gradient( to bottom, transparent 0px, transparent 3px, rgba(0,0,0,0.16) 3px, rgba(0,0,0,0.16) 6px );}
.modal-crt-screen.crt-weak::before{background:radial-gradient(ellipse at center,transparent 55%,rgba(0,0,0,0.25) 100%);}
.modal-crt-screen h2,.modal-crt-screen p{position:relative;z-index:3;color:#4bbf72;}
.modal-crt-screen p{color:#2f7a4a;}
#mulliganCards{position:relative;z-index:3;}
#deckPickerModal .modal-body, #vsAiPickerModal .modal-body, #confirmModal .modal-body, #winModal .modal-body, #mulliganScreen .modal-body, #passScreen .modal-body, #orderRollModal .modal-body{padding:0;}

/* ── Order roll (dice-off for turn order) ─────────────────────────────── */
.modal-art-btn:disabled{filter:brightness(0.5) saturate(0.4);cursor:not-allowed;pointer-events:none;}
.modal-art-btn.btn-mulligan:disabled{filter:none;} /* уже есть готовый спрайт btn_mulliganD.png — общий dimming-фильтр поверх него выглядел как лишняя прозрачность */
.modal-art-btn.btn-ready:disabled{filter:none;} /* пока ждём анимацию кубиков (order-roll) — просто её обычный idle-спрайт, без дополнительного затемнения */
.order-roll-row{display:flex;align-items:center;justify-content:center;gap:clamp(10px,3vw,20px);margin:8px 0 10px;position:relative;z-index:3;}
.order-roll-side{display:flex;flex-direction:column;align-items:center;gap:6px;transition:opacity .2s;}
.order-roll-label{font-family:'MEK',monospace;font-size:clamp(11px,2.6vw,14px);letter-spacing:.05em;color:#4bbf72;}
.order-roll-die{width:clamp(46px,12vw,58px);height:clamp(46px,12vw,58px);
  background-repeat:no-repeat;background-position:center;background-size:contain;
  filter:drop-shadow(0 0 4px rgba(0,0,0,0.5));transition:filter .15s;}
.order-roll-vs{font-family:'MEK',monospace;font-size:clamp(14px,3vw,18px);color:#2f7a4a;position:relative;z-index:3;margin-top:18px;}
.order-roll-side.order-roll-winner .order-roll-die{filter:drop-shadow(0 0 8px rgba(232,201,90,0.85)) drop-shadow(0 0 3px rgba(232,201,90,0.9));}
.order-roll-side.order-roll-winner .order-roll-label{color:#e8c95a;}
.order-roll-side.order-roll-loser{opacity:0.55;}
.order-roll-result{position:relative;z-index:3;min-height:1.6em;font-family:'MEK',monospace;font-size:clamp(12px,2.8vw,15px);color:#4bbf72;white-space:pre;}
.order-roll-result::after{content:'';display:inline-block;width:0.5em;height:1em;background:#4bbf72;vertical-align:-2px;margin-left:2px;animation:orderRollCursorBlink .8s steps(1) infinite;}
.order-roll-result.done::after{display:none;}
@keyframes orderRollCursorBlink{0%,49%{opacity:1;}50%,100%{opacity:0;}}

/* ── Generic CRT typing effect — reused on deckPicker/vsAiPicker copy ───── */
.type-fx{position:relative;min-height:1.3em;}
.type-fx::after{content:'';display:inline-block;width:0.5em;height:1em;background:#2f7a4a;vertical-align:-2px;margin-left:2px;animation:orderRollCursorBlink .8s steps(1) infinite;}
.type-fx.done::after{display:none;}
.type-fx.typing-hidden::after{display:none;}
/*── Футер для классических модалок (confirm/win/pass — кнопки внизу) ──────────*/
/*── Бокс с кнопками модалок (confirm/win/mulligan/pass) ──────────────────*/
.modal-footer{align-self:center; width:auto; background:transparent; border-style:solid; border-width:var(--modal-footer-border-w); border-image-source:url('../img/bg_jest2.png'); border-image-slice:14 fill; border-image-width:var(--modal-footer-border-w); border-image-outset:0; border-image-repeat:stretch; box-sizing:border-box; padding:0; display:flex; gap:var(--modal-footer-border-w); justify-content:center; align-items:center;}
/*── Graveyard modal — рамка и хедер как у battle log ────────────────*/
.grave-modal{position:relative; box-sizing:border-box; width:min(max(calc(2 * var(--card-w) + 44px), 280px), 92vw); max-height:65vh; overflow-y:hidden; background:transparent; border-style:solid; border-width:20px; border-image-source:url('../img/grav_frame.png'); border-image-slice:40 fill; border-image-width:20px; border-image-outset:0; border-image-repeat:stretch; padding:0; pointer-events:all; z-index:650; display:flex; flex-direction:column; filter:drop-shadow(0 4px 20px #000000aa);}
/* .jeet (2026-08-06, по прямому запросу автора) — свой ассет 9-слайса рамки для кладбища
   джит, те же параметры border-image, что у общей .grave-modal выше, просто другой файл
   (grav_frame_jeet.png). Класс .jeet/.tea навешивается в openGraveModal() (game.js) по
   тому, чью именно фракцию открыли. */
.grave-modal.jeet{border-image-source:url('../img/grav_frame_jeet.png');}
.grave-modal-header{background:transparent; border-style:solid; border-width:10px; border-image-source:url('../img/bg_jest_grav.png'); border-image-slice:20 fill; border-image-width:10px; border-image-outset:0; border-image-repeat:stretch; box-sizing:border-box; display:flex; justify-content:center; align-items:center; padding:0 8px; position:relative; flex-shrink:0;}
/* .jeet (2026-08-08, по прямому запросу автора) — тот же приём, что у .grave-modal.jeet/
   .grave-modal.jeet .grave-modal-footer выше: свой ассет для хидера кладбища джит
   (bg_jest_grav_jeet.png), остальные border-image-параметры те же. */
.grave-modal.jeet .grave-modal-header{border-image-source:url('../img/bg_jest_grav_jeet.png');}
.grave-close-btn{position:absolute; right:6px;top:50%;transform:translateY(-50%); background-image:url('../img/btn_X.png'); background-size:100% 100%; background-color:transparent; border:none;padding:0; width:max(calc(5vh * 0.798), calc(2.5vw * 0.798));height:max(calc(5vh * 0.798), calc(2.5vw * 0.798)); font-size:0;line-height:0; cursor:pointer;}
.grave-close-btn:hover{background-image:url('../img/btn_Xh.png');}
.grave-close-btn:active{background-image:url('../img/btn_X2.png');}
/*── Modal pop-in/pop-out ────────────────────────────────────────────*/
@keyframes modalPopIn{
  from{opacity:0; transform:scale(0.85);}
  to{opacity:1; transform:scale(1);}
}
.modal-pop-in{animation: modalPopIn 0.5s cubic-bezier(0.34,1.56,0.64,1) forwards;}
.modal-pop-out{animation: modalPopOut 0.25s ease-in forwards;}
.modal-pop-in-fast{animation: modalPopIn 0.25s cubic-bezier(0.34,1.56,0.64,1) forwards;}
.modal-pop-out-fast{animation: modalPopOut 0.125s ease-in forwards;}
.modal-icon-btn.btn-clear.placeholder::after{content:'🗑';}
@keyframes crtFlicker{
  0%,100%{opacity:1; filter:brightness(1);}
  6%{opacity:0.88; filter:brightness(0.90);}
  6.5%{opacity:1; filter:brightness(1.05);}
  7%{opacity:0.95; filter:brightness(0.97);}
  7.5%{opacity:1; filter:brightness(1);}
  38%{opacity:1; filter:brightness(1);}
  39%{opacity:0.91; filter:brightness(0.92);}
  40%{opacity:1; filter:brightness(1);}
  68%{opacity:1; filter:brightness(1);}
  69%{opacity:0.93; filter:brightness(0.93);}
  70%{opacity:1; filter:brightness(1.03);}
  70.5%{opacity:0.96; filter:brightness(0.98);}
  71%{opacity:1; filter:brightness(1);}
}
@keyframes modalPopOut{
  from{opacity:1; transform:scale(1);}
  to{opacity:0; transform:scale(0.9);}
}
.grave-modal-title{font-family:'MEK',monospace; font-size:max(calc(5vh * 0.665), calc(2.5vw * 0.665)); color:#0c1013; text-align:center;}
/* .jeet (2026-08-08, по прямому запросу автора) — заголовок кладбища джит кремовым
   (--color-cream), тот же принцип, что у остальных .jeet-переопределений этой модалки
   выше (рамка/хидер/футер) — просто тут меняется color текста, а не фоновый ассет. */
.grave-modal.jeet .grave-modal-title{color:var(--color-cream);}
.grave-modal-body{padding:10px 4px 34px; overflow-y:auto; flex:1; display:flex; flex-wrap:wrap; gap:4px; justify-content:center; align-content:flex-start;}
.grave-modal-body::-webkit-scrollbar{width:4px;}
.grave-modal-body::-webkit-scrollbar-track{background:#020d02;}
.grave-modal-body::-webkit-scrollbar-thumb{background:#E8E0C888;border-radius:0;}
.grave-modal-body::-webkit-scrollbar-thumb:hover{background:#E8E0C8;}
.grave-modal-footer{position:absolute; left:0;right:0;bottom:0; background-image:url('../img/bones.png'); background-size:auto 100%; background-repeat:repeat-x; background-position:left center; height:34px; pointer-events:none; z-index:5;}
/* .jeet (2026-08-06, по прямому запросу автора) — тот же приём, что у .grave-modal.jeet
   выше: свой ассет для полоски костей снизу (bones_jeet.png), остальные параметры те же. */
.grave-modal.jeet .grave-modal-footer{background-image:url('../img/bones_jeet.png');}
/* ═══════════════════════════════════════════════════════════════════════
   HEADER — шапка игрового экрана
   ═══════════════════════════════════════════════════════════════════════ */
.header{display:flex;justify-content:space-between;align-items:center;padding:4px 4px;border:none;background:#414B5A4D;position:relative;height:max(5vh, 2.5vw, 40px);}
.title{font-family:'MEK',monospace;font-size:max(calc(5vh * 0.38), calc(2.5vw * 0.38));color:#E8E0C8;letter-spacing:2px;padding-left:6px; display:flex; align-items:center;}
.title-glitch-wrap{position:relative;display:inline-block;}
.title-glitch-wrap::before, .title-glitch-wrap::after{content:attr(data-text); position:absolute; left:0;top:0; opacity:0; pointer-events:none; white-space:nowrap;}
.title-glitch-wrap.glitching{animation:titleGlitchShake 0.35s steps(2,end);}
.title-glitch-wrap.glitching::before{color:#ff0044;mix-blend-mode:screen;animation:titleGlitchBefore 0.35s steps(2,end);}
.title-glitch-wrap.glitching::after{color:#00e5ff;mix-blend-mode:screen;animation:titleGlitchAfter 0.35s steps(2,end);}
#turnInfo{position:absolute;left:50%;transform:translateX(-50%);font-size:max(calc(5vh * 0.35), calc(2.5vw * 0.35));color:#888;}
#turnInfo span{color:#E8E0C8;font-weight:bold;}
@media(max-width:600px){
  .header-desktop-btns{display:none!important;}
  .header-mobile-btn{display:block!important;}
  #turnInfo{position:static;transform:none;margin-left:auto;margin-right:8px;font-size:calc(5vh * 0.32);}
  .header{position:relative;}
  :root{--card-small-h: 11.52vh;}
  .battlefield{gap:0;}
}
.header-desktop-btns{display:none;}
.header-mobile-btn{display:block;}
.hamburger-btn{width: max(calc(5vh * 0.8), calc(2.5vw * 0.8), 30px);height: max(calc(5vh * 0.8), calc(2.5vw * 0.8), 30px);border:1px solid #E8E0C8;background:transparent;cursor:pointer;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:12%;padding:16%;transition:all .2s;position:relative;margin:1px;box-sizing:border-box;}
.hamburger-btn:hover{border-color:#F2ECD8;box-shadow:0 0 8px #CBBE9A66, 0 0 16px #8A7D6244;}
.hamburger-btn:active{transform:scale(0.92) translateY(1px);}
.ham-line{width:100%;height:12%;background:#E8E0C8;border-radius:1px;transition:transform .3s,opacity .3s;transform-origin:center;flex-shrink:0;}
.hamburger-btn.open .ham-line:nth-child(1){transform:translateY(200%) rotate(45deg);}
.hamburger-btn.open .ham-line:nth-child(2){opacity:0;transform:scaleX(0);}
.hamburger-btn.open .ham-line:nth-child(3){transform:translateY(-200%) rotate(-45deg);}
.ham-menu{position:absolute;top:100%;right:0;background:#0d0d1a;border:1px solid #E8E0C8;min-width:130px;overflow:hidden;max-height:0;opacity:0;transition:max-height .3s ease,opacity .2s ease;z-index:700;}
.ham-menu.open{max-height:400px;opacity:1;}
.btn.ham-item{border:none;border-bottom:1px solid #ffffff11;width:100%;text-align:left;padding:8px 14px;font-size:max(calc(5vh * 0.532), calc(2.5vw * 0.532));color:#e8e0c8;display:block;}
@media(min-width:601px){
  .btn.ham-item{font-size:max(calc(5vh * 0.4256), calc(2.5vw * 0.4256));}
}
.ham-item:last-child{border-bottom:none;}
.ham-item:hover{background:#5A5448;color:#F2ECD8;}
@keyframes titleGlitchShake{
  0%,100%{transform:translate(0);}
  20%{transform:translate(-2px,1px);}
  40%{transform:translate(2px,-1px);}
  60%{transform:translate(-1px,0);}
  80%{transform:translate(1px,1px);}
}
@keyframes titleGlitchBefore{
  0%{opacity:0; clip-path:inset(0 0 85% 0); transform:translate(0);}
  10%{opacity:0.9; clip-path:inset(10% 0 60% 0); transform:translate(-4px,0);}
  25%{opacity:0.9; clip-path:inset(55% 0 20% 0); transform:translate(3px,0);}
  40%{opacity:0.9; clip-path:inset(20% 0 65% 0); transform:translate(-3px,0);}
  55%{opacity:0.9; clip-path:inset(70% 0 5% 0); transform:translate(4px,0);}
  70%,100%{opacity:0;}
}
@keyframes titleGlitchAfter{
  0%{opacity:0; clip-path:inset(60% 0 10% 0); transform:translate(0);}
  10%{opacity:0.9; clip-path:inset(5% 0 70% 0); transform:translate(4px,0);}
  25%{opacity:0.9; clip-path:inset(40% 0 35% 0); transform:translate(-3px,0);}
  40%{opacity:0.9; clip-path:inset(75% 0 8% 0); transform:translate(3px,0);}
  55%{opacity:0.9; clip-path:inset(15% 0 55% 0); transform:translate(-4px,0);}
  70%,100%{opacity:0;}
}
/* ═══════════════════════════════════════════════════════════════════════
   GAME BOARD — карты, рука, поле, стат-бары, таргетинг, тултипы, FX
   ═══════════════════════════════════════════════════════════════════════ */
.persist{display:flex;gap:5px;flex-wrap:wrap;min-height:0;height:0;overflow:hidden;padding:0;background:transparent;border:none;align-items:center;justify-content:center;}
.pcard{width:var(--pcard-w);height:var(--pcard-h);padding:1px 1px;border:1px solid;border-radius:6px;font-size:calc(var(--pcard-h) * 0.75);display:flex;align-items:center;justify-content:center;gap:4px;background:url('../img/pcard_bg.png') center/100% 100% no-repeat;}
/*Компактная версия для стат-бара — ФИКСИРОВАННАЯ ширина (одна и та же что у пустого плейсхолдера,*/
.pcard-inline{height:max(3.2vh,1.6vw) !important; width:calc(max(3.2vh,1.6vw) * 3) !important; font-size:max(calc(3.2vh * 0.52),calc(1.6vw * 0.52)) !important; padding:1px 1px !important; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; flex-shrink:0;}
.pcard.tcp{border-color:#c8a84b44;color:#c8a84b88;}
.pcard.jcp{border-color:#7b2d8b44;color:#b44fd488;}
.pcard-placeholder{pointer-events:none;}
.pcard-text{display:inline-block;transition:transform 0.08s;}
.pcard.sleeping .pcard-text{opacity:0.45;}
.pcard.pcard-active:active .pcard-text{transform:scale(0.9);}
/*── Спрайт-лист "створки": один PNG на фракцию, 4 кадра подряд по горизонтали —*/
.pcard.tcp,.pcard-placeholder.tcp{background-image:url('../img/pcard_tea_shutter_sheet.png'); background-repeat:no-repeat; background-size:400% 100%;}
.pcard.jcp,.pcard-placeholder.jcp{background-image:url('../img/pcard_jeet_shutter_sheet.png'); background-repeat:no-repeat; background-size:400% 100%;}
.pcard.tcp{background-position:100% 0;border-color:transparent;color:#4DB854;}
.pcard.jcp{background-position:100% 0;border-color:transparent;color:#b44fd4;}
.pcard-placeholder.tcp,.pcard-placeholder.jcp{background-position:0% 0;}
.pcard.pcard-entering{animation: pcardShutterFrames 1s steps(3) forwards;}
.pcard.pcard-entering .pcard-text{opacity:0; animation: pcardTextReveal 0.5s steps(2,end) 0.8s forwards;}
.pcard.pcard-entering.sleeping .pcard-text{animation: pcardTextRevealDim 0.5s steps(2,end) 0.8s forwards;}
.pcard.pcard-active{cursor: pointer; transition: filter 0.08s;}
.pcard.tcp.pcard-active{animation: pcardReadyPulseWhite 1.8s ease-in-out infinite;}
.pcard.jcp.pcard-active{animation: pcardReadyPulsePurple 1.8s ease-in-out infinite;}
.pcard.pcard-active:hover{filter: brightness(1.3);}
/* .pcard.aim-target — 2026-07-24, по прямому запросу автора: та же красная подсветка-цель,
   что у .card-small.aim-target (см. mishenBlink чуть ниже), но как обводка/glow вместо
   иконки-мишени поверх картинки — pcard слишком мал для отдельного значка того же размера.
   Используется SUNDER/BLIGHT (уничтожение вражеского Мира/Артефакта). */
.pcard.aim-target{outline:2px solid #ff3b3b;box-shadow:0 0 6px 2px rgba(255,59,59,0.8);cursor:pointer;animation:mishenBlink 1.1s ease-in-out infinite;}
.pcard-targeting .pcard-text{animation: pcardTargetingBlink 1.2s ease-in-out infinite;}
.empty-persist{font-size:14px;color:#2a2a2a;}
.hand-mini{display:flex;flex-wrap:nowrap;min-height:0;padding:0 4px 4px;background:transparent;border:none;justify-content:center;overflow:hidden;align-items:flex-start;}
.hand-mini .card-mini{flex-shrink:0;min-width:calc(6vh * 0.9);width:calc(6vh * 0.9);}
/* border:none/background:transparent (2026-08-05, багфикс по прямому запросу автора) —
   раньше тут были border:1px solid + сплошной/градиентный фон — сама рубашка (кастомный
   арт runaha.png, ставится инлайново через backgroundImage в rHiddenHand()/render.js) уже
   содержит СВОЮ рамку/фон, нарисованные прямо в картинке — CSS-рамка/фон поверх неё
   давали лишний дублирующийся контур/подложку вокруг арта. Сам арт (background-image)
   не трогаем — только убираем то, что рисовал CSS ДО/ПОД ним.  */
.card-mini{width:calc(6vh * 0.9);height:6vh;border:none;background:transparent;display:flex;align-items:flex-start;justify-content:center;padding-top:6px;font-size:calc(6vh * 0.5);overflow:hidden;flex-shrink:0;}
.card-mini.tea-mini{border:none;background:transparent;}
.card-mini.jeet-mini{border:none;background:transparent;}
.card-mini.entering{animation:cardEnter 0.35s ease-out forwards;}
.card-back.tea-back{border-color:#c8a84b22;background:linear-gradient(135deg,#0d0d1a,#1a1200);}
.card-back.jeet-back{border-color:#7b2d8b22;background:linear-gradient(135deg,#0d0d1a,#1a001a);}
.card-back-symbol{font-size:44px;opacity:0.3;}
.card-back-dots{font-size:14px;color:#333;letter-spacing:2px;}
.card-back{width:calc(6vh * 1.4);border:1px solid #ffffff11;padding:5px;background:#0d0d1a;height:6vh;display:flex;align-items:center;justify-content:center;flex-direction:column;gap:6px;}
.field-preview-card{position:fixed;pointer-events:none;margin:0;box-shadow:0 10px 40px #000000cc;}
.card-preview-backdrop{position:fixed;inset:0;background:rgba(0,0,0,0.55);z-index:5999;transition:opacity .15s ease-out;}
/*── Панель-легенда бафов/дебафов под зумленной картой (showFieldCardPreview,*/
.card-status-panel{display:flex; flex-direction:column; gap:6px; pointer-events:none; opacity:0; transform:translateX(-50%) translateY(-6px); transition:opacity .18s ease-out, transform .18s ease-out; max-width:min(320px, 82vw);}
.card-status-panel.card-status-panel-visible{opacity:1; transform:translateX(-50%) translateY(0);}
.card-status-row{display:flex; flex-direction:row; align-items:center; gap:8px; background:#0c0c18ee; border:1px solid #9B937F; border-radius:6px; padding:6px 10px; box-shadow:0 4px 16px #CBBE9A66;}
.card-status-icon{flex:none; width:22px; height:22px; display:flex; align-items:center; justify-content:center; font-size:16px; line-height:1;}
.card-status-icon img{width:100%;height:100%;object-fit:contain;}
.card-status-text{font-family:'MEK',monospace; font-size:12px; line-height:1.35; color:#E8E0C8; text-align:left;}
.card-small.tea-card{border-color:transparent;}
.card-small.jeet-card{border-color:transparent;}
.card-small:hover{transform:translateY(-2px);}
/* z-index:30 (2026-08-05, багфикс по прямому запросу автора) — попап способности карты
   (.field-ability-popup/.card-actions-popup*, z-index:2001) — потомок ЭТОЙ карты, но без
   собственного z-index у самой .card-small их сравнение с соседними картами по стеку решает
   не z-index попапа (он локален внутри своего же контекста), а порядок в DOM/стекинг самих
   .card-small — на поле в 2 ряда попап карты из 2-го ряда мог визуально уйти ПОД
   .card-dim-overlay соседней карты 1-го ряда. Тот же приём, что уже есть у .hand
   .card.previewed{z-index:2000} для попапов в руке — поднимаем весь стекинг-контекст
   выбранной карты целиком, так что её попап гарантированно выше любых соседей, независимо
   от DOM-порядка. 30 — с запасом выше .card-dim-overlay(4)/card-small-feared и т.п.(5)/
   frost-overlay(20)/aim-target mishen(25), но далеко ниже настоящих полноэкранных слоёв
   (модалки от 650, previewed-рука 2000+). */
.card-small.selected{box-shadow:0 0 6px #c8a84b;border-color:#c8a84b;transform:translateY(-4px);z-index:30;}
.card-small.targetable{box-shadow:0 0 6px #e05555;border-color:#e05555;animation:pulse .8s infinite alternate;}
/*── Мишень поверх карты-цели для точечных заклинаний/кнопок-активок/артефактов
   (см. комментарий у mkSmallEl в render.js — aim-target/aim-heal). Размер — половина
   ширины маленькой карты (арт мишеней 1:1). Позиция — ПО ЦЕНТРУ АРТА, а не всей карты:
   card-small-cost/type-dot/tag-icons/burning/feared/zzz — все position:absolute, значит
   в обычном потоке первый элемент — card-small-art, его верх = padding-top карты (2px,
   т.к. top:0 у abs-потомка совпадает с padding-box родителя, т.е. с концом border) —
   дальше просто половина высоты арта (--card-small-art-h). Клики не перехватывает. ──*/
.card-small.aim-target::after,.card-small.aim-heal::after,.card-small.aim-attack::after{content:'';position:absolute;top:calc(2px + var(--card-small-art-h) / 2);left:50%;width:calc(var(--card-small-w) / 2);height:calc(var(--card-small-w) / 2);transform:translate(-50%,-50%);background-size:contain;background-repeat:no-repeat;background-position:center;pointer-events:none;z-index:25;animation:mishenBlink 1.1s ease-in-out infinite;} /* z-index 5→25 (2026-07-27, автор поймал живьём) — мишень рисовалась ПОД .frost-overlay (z-index:20), на замороженной цели выглядела блёкло; теперь мишень поверх ВСЕГО на карте, как и дебафф-бэйджи */
.card-small.aim-target::after{background-image:url('../img/mishen_red.png');}
/* aim-attack (2026-08-05, по прямому запросу автора) — тот же оверлей-паттерн, что у
   aim-target/aim-heal выше (тот же ::after, та же анимация мигания), но для ОБЫЧНОЙ атаки
   (phase==='selectTarget' в render.js), не для точечных заклинаний/активок. Свой отдельный
   арт (swords.png), не переиспользует mishen_red.png — визуально должно читаться как
   "сюда можно ударить", а не "это цель заклинания". Показывается ТОЛЬКО там, где атака
   реально возможна (та же targetableS-фильтрация, что и раньше давала просто красную
   подсветку без иконки — см. mkSmallEl() в render.js).*/
.card-small.aim-attack::after{background-image:url('../img/swords.png');}
/* Мишень-череп (2026-07-27, по прямому запросу автора) — для JUDGMENT/DEATHBLOW (и
   аналогов, VERDICT/DAMNATION): та же красная мишень, но с черепом — только на целях,
   которые реально УМРУТ от этого конкретного каста (JUDGMENT — не всегда: сперва Bolt 1,
   добивание срабатывает только если это опустит цель до ≤50% maxHP, см. mkSmallEl() в
   render.js; VERDICT/DAMNATION — insta-kill 999 dmg, всегда убивает ЛЮБУЮ валидную цель,
   так что у них череп будет на 100% валидных целей поголовно). Класс navешивается ДОПОЛНИТЕЛЬНО
   к aim-target, не вместо — просто переопределяет background-image тем же селектором с
   большей специфичностью. */
.card-small.aim-target.aim-target-kill::after{background-image:url('../img/mishen_red2.png');}
/* IMMUNE-надпись (2026-07-27, по прямому запросу автора) — раньше карта, недоступная целью
   из-за Ward/Frost/активного Solana Shield, просто оставалась "пустой" (без мишени) — на
   глаз не всегда очевидно, что это НАМЕРЕННЫЙ иммунитет, а не баг/недогруз. Тот же принцип
   мигания, что у мишени (mishenBlink), но текстом поверх карты вместо картинки прицела —
   тот же z-index:25, тоже поверх всего на карте. Класс навешивается ТОЛЬКО когда карта
   реально была бы валидной вражеской целью (не своя, не спелл/мир/артефакт), просто
   заблокирована конкретно иммунитетом — см. mkSmallEl() (render.js).*/
.card-small.immune-target::after{
  content:'IMMUNE';
  position:absolute; top:50%; left:50%; transform:translate(-50%,-50%);
  color:#5be3ff; font-family:'MEK',monospace; font-weight:bold;
  font-size:calc(var(--card-small-w) * 0.13);
  text-shadow:0 0 5px #000,0 0 3px #000;
  pointer-events:none; z-index:25; white-space:nowrap;
  animation:mishenBlink 1.1s ease-in-out infinite;
}
.card-small.aim-heal::after{background-image:url('../img/mishen_green.png');}
.card-dim-overlay{position:absolute;inset:0;border-radius:inherit;background:rgba(0,0,0,.4);pointer-events:none;z-index:4;display:none;}
.card.exhausted .card-dim-overlay,.card.sleeping .card-dim-overlay,.card.feared .card-dim-overlay,
.card-small.exhausted .card-dim-overlay,.card-small.sleeping .card-dim-overlay,.card-small.feared .card-dim-overlay{display:block;}
.card-small.feared{border-style:solid;box-shadow:0 0 8px #66666688;}
/* invisible-visual/stealth-visual (2026-07-19, по прямому запросу автора) — box-shadow
   индикатор убран целиком (сперва была яркая cream-рамка, потом тёмный shadow box) —
   классы ниже намеренно пустые, но продолжают навешиваться в mkSmallEl() (render.js) как
   маркер состояния (на случай будущей точечной стилизации по классу). Реальный визуальный
   индикатор с 2026-07-27 — картинка-оверлей .invis-overlay (см. ниже), не CSS-стиль этих
   классов. */
.card-small.invisible-visual{}
.card-small.stealth-visual{}
/* Invis/Stealth overlay box (2026-07-27, по прямому запросу автора) — box ТОГО ЖЕ размера,
   что и сама card-small (100%, НЕ 110% как у .frost-overlay — тот специально чуть больше
   карты, этот — вровень с ней), картинка invis.png уже содержит нужную прозрачность, никаких
   доп. эффектов сюда не добавляем. z-index между обычным содержимым карты и дебафф-бэйджами
   (card-small-feared/-burning/-tauntbroken, z-index:5) и Frost-оверлеем (z-index:20) — "поверх
   всего, кроме дебафов и фрост ассета", по прямому запросу автора: те продолжают рисоваться
   НАД этим боксом, а не под ним. Появляется/исчезает мгновенно вместе с обычным render() —
   никакой отдельной анимации не просили (в отличие от Frost). */
.invis-overlay{position:absolute;top:0;left:0;width:100%;height:100%;background:url('../img/invis.png') center/100% 100% no-repeat;pointer-events:none;z-index:3;}
/* MonoMEK Метка (2026-07-30) — тот же низкий слой, что у .invis-overlay (НЕ z-index:20, как
   у .frost-overlay ниже) — по прямому запросу автора остальные дебафф-бэйджи (Fear/Burn/
   TauntBroken/мишень таргетинга) должны оставаться визуально ПОВЕРХ этого оверлея. */
.mek-overlay{position:absolute;top:0;left:0;width:100%;height:100%;background:url('../img/mek.png') center/100% 100% no-repeat;pointer-events:none;z-index:3;}
.card-small-feared{position:absolute;top:calc(var(--card-small-h) * -0.085);left:20%;transform:translateX(-50%);width:calc(var(--card-small-h) * 0.155);height:calc(var(--card-small-h) * 0.155);z-index:5;}
.card-small-burning{position:absolute;top:calc(var(--card-small-h) * -0.085);left:80%;transform:translateX(-50%);width:calc(var(--card-small-h) * 0.155);height:calc(var(--card-small-h) * 0.155);z-index:5;}
.card-small-tauntbroken{position:absolute;top:calc(var(--card-small-h) * -0.085);left:50%;transform:translateX(-50%);width:calc(var(--card-small-h) * 0.155);height:calc(var(--card-small-h) * 0.155);z-index:5;}
.card-small.burning{box-shadow:0 0 5px #ff6600 !important;}
.card-small.healable{box-shadow:0 0 6px #44ff88 !important;border-color:#44ff88 !important;opacity:1 !important;}
.card-small-art{width:var(--card-small-art-h);height:var(--card-small-art-h);display:flex;align-items:center;justify-content:center;font-size:28px;margin:0 auto 0 auto;border:1px solid #ffffff11;background:transparent;overflow:hidden;}
.card-small-name{font-family:'MEK',monospace;font-size: var(--card-small-name-fs);text-align:center;margin:0 0;line-height:1.3;min-height:12px;display:flex;align-items:center;justify-content:center;}
.tea-card .card-small-name{color:#4db854;}
.jeet-card .card-small-name{color:#b44fd4;}
.neutral-card .card-small-name{color:var(--color-cream);}
.card-small-stats{width:var(--card-small-stats-w);height:var(--card-small-stats-h);margin:0 auto;display:flex;justify-content:space-between;flex-shrink:0;padding:0;}
.card-small-hp{color:#e05555;font-size: var(--card-small-stat-fs);}
.card-small-shield-icon{height:80%;width:auto;object-fit:contain;}
.card-small-atk{color:#55aaff;font-size: var(--card-small-stat-fs);}
.card-small-cost{position:absolute;top:calc(var(--card-small-h) * 0.023);left: calc(var(--card-small-w) * 0.048);width:calc(var(--card-small-h)*0.14);height:calc(var(--card-small-h)*0.14*2.18);display:flex;align-items:flex-start;justify-content:center;font-family:'MEK',monospace;font-size:calc(var(--card-small-h) * 0.122);font-weight:bold;line-height: 1;background-size:100% 100%;background-repeat:no-repeat;}
.tea-card .card-small-cost{background-image:url('../img/bg_cost_tea.png');}
.jeet-card .card-small-cost{background-image:url('../img/bg_cost_jeet.png');}
.neutral-card .card-small-cost{background-image:url('../img/bg_cost_neutral.png');}
.card-small .card-type-dot{width:calc(var(--card-small-h)*0.13);height:calc(var(--card-small-h)*0.13);top:calc(var(--card-small-h) * 0.023);right:calc(var(--card-small-w) * 0.048);}
.card-small .card-tag-icons{right:calc(var(--card-small-w)*0.048);top:calc(var(--card-small-h)*0.165);gap:calc(var(--card-small-h)*0.01);}
.card-small .card-tag-icon{width:calc(var(--card-small-h)*0.13);height:calc(var(--card-small-h)*0.13);}
.card-small .card-tag-count{font-size:calc(var(--card-small-h)*0.13*0.5*0.7);}
.card-small.hit{animation: hitShake .25s ease;}
.burning-icon{position:absolute;top:-6px;right:-6px;width:calc(var(--card-h)*0.107);height:calc(var(--card-h)*0.107);z-index:5;}
/* Frost overlay box (2026-07-27, "Frost Attack") — art-only box drawn OVER the whole card,
   110% of its size, centered (frost.png already has its own transparency/glow baked in — no
   extra effects layered on top here). Appears with a scale-in pop, disappears with the same
   animation reversed. No sound yet (по прямому запросу автора). */
.frost-overlay{position:absolute;top:-5%;left:-5%;width:110%;height:110%;background:url('../img/frost.png') center/100% 100% no-repeat;pointer-events:none;z-index:20;animation:frostIn 0.25s ease-out;}
.frost-overlay.frost-leaving{animation:frostOut 0.3s ease-in forwards;}
@keyframes frostIn{from{transform:scale(0.01);opacity:0;}to{transform:scale(1);opacity:1;}}
@keyframes frostOut{from{transform:scale(1);opacity:1;}to{transform:scale(0.01);opacity:0;}}
.card-small.entering{animation: cardEnter 0.35s ease-out forwards;}
.card-small.entering:not(.sleeping){animation: cardEnterAwake 0.35s ease-out forwards;}
.card-small .stat-icon{width:0.6em;height:0.6em;}
.card-small-name-box{width:100%;height:var(--card-small-name-h);background:url('../img/card_name_bg.png') center/100% 100% no-repeat;overflow:hidden;flex-shrink:0;padding:0;display:flex;align-items:center;justify-content:center;}
.card-small-hp-box{width:48%;height:100%;background:url('../img/card_stat_bg.png') center/100% 100% no-repeat;display:flex;flex-direction:row;align-items:center;justify-content:center;gap:1px;padding:1px 1px 3.5px 1px;}
.card-small-atk-box{width:48%;height:100%;background:url('../img/card_stat_bg.png') center/100% 100% no-repeat;display:flex;flex-direction:row;align-items:center;justify-content:center;gap:1px;padding:1px 1px 3.5px 1px;}
.card{width:var(--card-w);height:var(--card-h);border:1px solid;padding:var(--card-pad);cursor:pointer;transition:transform .2s;position:relative;background:transparent;background-size:cover;background-position:center;font-size:16px;display:flex;flex-direction:column;border-radius:5%;}
.card.tea-card{border-color:transparent;}
.card.jeet-card{border-color:transparent;}
.card.selected{box-shadow:0 0 8px #c8a84b;border-color:#c8a84b;}
.card.sleeping{border-style:dashed;}
.card.feared{border-style:dotted;}
.card.burning{box-shadow:0 0 6px #ff6600 !important;}
.card-cost{position:absolute;top:calc(var(--card-h) * 0.0125);left:calc(var(--card-w) * 0.038);width:calc(var(--card-h)*0.12);height:calc(var(--card-h)*0.12*2.18);display:flex;align-items:flex-start;justify-content:center;font-family:'MEK',monospace;font-size:calc(var(--card-h) * 0.11);font-weight:bold;line-height: 1;background-size:100% 100%;background-repeat:no-repeat;}
.tea-card .card-cost{background-image:url('../img/bg_cost_tea.png');}
.jeet-card .card-cost{background-image:url('../img/bg_cost_jeet.png');}
.neutral-card .card-cost{background-image:url('../img/bg_cost_neutral.png');}
.card-type-dot{position:absolute;top:calc(var(--card-h) * 0.0125);right:calc(var(--card-w) * 0.038);width:calc(var(--card-h)*0.108);height:calc(var(--card-h)*0.108);border:none;z-index:2;}
.card-art{width:var(--card-art-size);height:var(--card-art-size);display:flex;align-items:center;justify-content:center;font-size:48px;margin:0 auto;border:none;background:transparent;overflow:hidden;flex-shrink:0;}
.card-name-box{width:95%;height:var(--card-name-h);background:url('../img/card_name_bg.png') center/100% 100% no-repeat;overflow:hidden;flex-shrink:0;padding:0;display:flex;align-items:center;justify-content:center;}
.card-name{font-family:'MEK',monospace;font-size:calc(var(--card-h) * 0.08);text-align:center;line-height:1.2;width:100%;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}
.tea-card .card-name{color:#4DB854;}
.jeet-card .card-name{color:#B44FD4;}
.neutral-card .card-name{color:var(--color-cream);}
.card-stats{width:var(--card-stats-w);height:var(--card-stats-h);margin:0 auto;display:flex;justify-content:space-between;flex-shrink:0;padding:0;}
.card-hp-box{width:48%;height:100%;background:url('../img/card_stat_bg.png') center/100% 100% no-repeat;display:flex;flex-direction:row;align-items:center;justify-content:center;gap:1px;padding:1px 1px 4.5px 1px;}
.card-atk-box{width:48%;height:100%;background:url('../img/card_stat_bg.png') center/100% 100% no-repeat;display:flex;flex-direction:row;align-items:center;justify-content:center;gap:1px;padding:1px 1px 4.5px 1px;}
.card-hp{color:#e05555;font-size:calc(var(--card-h) * 0.11);display:flex;align-items:center;gap:1px;}
.card-atk{color:#55aaff;font-size:calc(var(--card-h) * 0.11);display:flex;align-items:center;gap:1px;}
.stat-icon{width:0.45em;height:0.45em;vertical-align:middle;margin-right:1px;image-rendering:pixelated;}

/* ── Armor box — left side, under cost, ratio 2:1, same height as a tag-icon box.
   Icon+text+padding/font-size copied from .card-hp-box/.card-hp (same convention as
   HP/ATK), only rendered when the card actually carries an `armor` tag. ── */
.card-armor-box{position:absolute;left:calc(var(--card-w)*0.038);
  top:calc(var(--card-pad) + var(--card-art-size) - (var(--card-h)*0.108));
  width:calc(var(--card-h)*0.108*2);height:calc(var(--card-h)*0.108);
  background:url('../img/armor_bg.png') center/100% 100% no-repeat;
  display:flex;align-items:center;justify-content:center;padding:1px 1px 4.5px 1px;z-index:3;}
.card-armor{color:#cbd8e3;font-size:calc(var(--card-h) * 0.11);display:flex;align-items:center;gap:1px;}
.card-armor .stat-icon{margin-top:0.12em;} /* иконка внутри бокса съезжала чуть выше цифры — нудж вниз */
.card-small-armor-box{position:absolute;left:calc(var(--card-small-w)*0.048);
  top:calc(2px + var(--card-small-art-h) - (var(--card-small-h)*0.13));
  width:calc(var(--card-small-h)*0.13*2);height:calc(var(--card-small-h)*0.13);
  background:url('../img/armor_bg.png') center/100% 100% no-repeat;
  display:flex;align-items:center;justify-content:center;padding:1px 1px 3.5px 1px;z-index:3;}
.card-small-armor{color:#cbd8e3;font-size:var(--card-small-stat-fs);display:flex;align-items:center;gap:1px;}
/* ── Incarnation badge — только в модалке кладбища (zone==='grave', mkEl), показывает
   сколько ходов владельца осталось до self-revive. Нет готового арта — plain-фолбэк
   (фон+рамка+эмодзи), симметрично card-cost в правом верхнем углу, как временная замена
   до готового арта (тот же принцип, что и placeholder-кнопки, см. CLAUDE.md). ── */
.card-incarn-badge{position:absolute;top:calc(var(--card-h) * 0.0125);right:calc(var(--card-w) * 0.038);
  width:calc(var(--card-h)*0.12);height:calc(var(--card-h)*0.12*2.18);
  display:flex;flex-direction:column;align-items:center;justify-content:center;gap:1px;
  font-family:'MEK',monospace;font-size:calc(var(--card-h) * 0.09);font-weight:bold;line-height:1;
  overflow:hidden;border-radius:4px;color:#e0c8ff;z-index:4;}
/* armor_bg.png — горизонтальный 2:1 ассет, тот же что у Брони, но здесь бокс вертикальный —
   повёрнут на 90° через псевдоэлемент-подложку (ширина/высота свопнуты ДО поворота, чтобы
   после rotate(90deg) итоговый повёрнутый прямоугольник точно перекрыл сам бокс).
   2026-07-24 (по прямому запросу автора): весь бокс уменьшен на 50% — убран множитель *2,
   который раньше был у каждого размера ниже. */
.card-incarn-badge::before{content:'';position:absolute;top:50%;left:50%;
  width:calc(var(--card-h)*0.12*2.18);height:calc(var(--card-h)*0.12);
  background-image:url('../img/armor_bg.png');background-size:100% 100%;background-repeat:no-repeat;
  transform:translate(-50%,-50%) rotate(90deg);z-index:-1;}
.card-incarn-badge .card-incarn-icon{font-size:calc(var(--card-h)*0.1);}
.card-ability-box{position:relative;width:100%;height:var(--card-text-h);background:url('../img/card_text_bg.png') center/100% 100% no-repeat;overflow:hidden;flex-shrink:0;padding:calc(var(--card-h) * 0.012) calc(var(--card-h) * 0.028);display:flex;align-items:center;justify-content:center;}
.card-ability{font-size:calc(var(--card-h) * 0.064);color:#E8E0C8;text-align:center;line-height:0.7;word-spacing:-1.5px;width:100%;word-wrap:break-word;overflow-wrap:break-word;white-space:normal;}
/* card-gate-box — 2026-07-23, по прямому запросу автора: ярлык Врат (архетипа) поверх
   рядовых Traveler-карт. Центрирован по ширине card-ability-box. ИСПРАВЛЕНО (2026-07-23):
   верх бокса теперь стыкуется с верхом card-ability-box (top:0, без Y-сдвига) — раньше
   был translateY(-70%), из-за чего бокс стыковался НИЗОМ, а не верхом, как было нужно.
   Бокс сидит внутри верхней части card-ability-box, визуально над самим текстом абилки
   (тот вертикально центрирован ниже). Высота — чуть больше самого текста (line-height 1 +
   небольшой padding), ratio строго 4:1 (ширина = 4×высота). Шрифт — половина от .card-name
   (var(--card-h)*0.08 → *0.04 здесь).*/
.card-gate-box{position:absolute;top:0;left:50%;transform:translateX(-50%);
  height:calc(var(--card-h) * 0.052);width:calc(var(--card-h) * 0.052 * 4);
  background:url('../img/bg_armor.png') center/100% 100% no-repeat;
  display:flex;align-items:center;justify-content:center;z-index:2;}
.card-gate-label{font-family:'MEK',monospace;font-size:calc(var(--card-h) * 0.04);color:var(--color-cream);text-align:center;line-height:1;white-space:nowrap;}
.ab-icon-trigger{height:1.15em;width:auto;vertical-align:-0.2em;margin-right:1px;}
.card-world-art{width:100%;flex:1;overflow:hidden;display:flex;align-items:center;justify-content:center;font-size:48px;}
.card.world-card{padding:4px;justify-content:space-between;background-size:cover;background-position:center;background-repeat:no-repeat;}
.card.world-card .card-name-box{height:var(--card-world-name-h);width:95%;background-image:url('../img/card_name_world_bg.png');}
.card.world-card .card-ability-box{height:var(--card-world-text-h);background:url('../img/card_text_world_bg.png') center/100% 100% no-repeat;overflow:hidden;flex-shrink:0;padding:calc(var(--card-h) * 0.012) calc(var(--card-h) * 0.028);display:flex;align-items:center;justify-content:center;}
.card.world-card.tea-card, .card.world-card.jeet-card{background-image:none !important;}
.card-stats-icon{width:var(--card-stats-h);height:var(--card-stats-h);object-fit:cover;flex-shrink:0;}
.card-tag-icons{position:absolute;right:calc(var(--card-w)*0.038);top:calc(var(--card-h)*0.135);display:flex;flex-direction:column;gap:calc(var(--card-h)*0.012);z-index:3;}
.card-tag-icon{width:calc(var(--card-h)*0.108);height:calc(var(--card-h)*0.108);display:flex;align-items:center;justify-content:center;font-size:10px;background:url('../img/tag_bg.png') center/100% 100% no-repeat;position:relative;}
/*── Бейдж со значением X поверх тег-иконки (2026-08-12, по прямому запросу автора) —
    рендерится только для тегов с реально переменным числом на карте, см.
    DYNAMIC_NUM_TAGS в render.js/catalog.js/deckbuilder.js. Ratio 1:1, размер вдвое
    меньше самой иконки (.card-tag-icon), сидит в правом нижнем углу иконки — отсюда
    width/height:50% (от родителя). right/bottom:-12.5% (2026-08-12, было -25% —
    автор прислал скриншот, на нём бейдж сидел практически впритык углом к иконке,
    почти без нахлёста, вместо ожидаемого нахлёста наполовину своего размера; -25%
    было верно по чистой геометрии CSS (right/bottom для абсолютно позиционированного
    потомка считаются от %-ширины/высоты containing block, т.е. .card-tag-icon), но
    по факту в браузере давало вдвое больший вынос наружу, чем на бумаге — сама причина
    расхождения не выяснена (возможные кандидаты: sub-pixel rounding на маленьком
    реальном размере иконки на телефоне, либо особенность вычисления containing block
    у abs-потомка внутри flex-родителя в конкретном движке рендеринга) — не стали
    гадать дальше и просто уменьшили офсет вдвое по прямому ощущению с фото автора).
    Цвет цифры — var(--color-cream), тот же кремовый, что и везде по интерфейсу. Размер
    цифры — 70% от бейджа (по просьбе автора "на 30% меньше самого бокса"), формула
    дублируется под .card-small ниже (там своя переменная --card-small-h), т.к. сам
    .card-tag-icon тоже так живёт (см. .card-small .card-tag-icon чуть ниже). */
.card-tag-count{position:absolute;right:-12.5%;bottom:-12.5%;width:50%;height:50%;background:url('../img/tag_count_bg.png') center/100% 100% no-repeat;display:flex;align-items:center;justify-content:center;color:var(--color-cream);font-family:'MEK',monospace;font-weight:bold;line-height:1;z-index:1;font-size:calc(var(--card-h)*0.108*0.5*0.7);}
@media(min-width:601px){
  .hand{justify-content:center !important;gap:8px;}
}
.hand{display:flex;flex-wrap:nowrap;min-height:var(--card-h);padding:3px;background:transparent;justify-content:space-between;align-items:flex-end;overflow:visible;position:relative;width:100%;box-sizing:border-box;}
.hand .card{flex-shrink:0;transition:transform .2s;position:relative;width:var(--card-w)!important;min-width:var(--card-w)!important;}
.card-actions-popup{animation: popupSlideUp 0.18s ease-out forwards;}
/*── Сжигание карты из руки (doBurnCard() в game.js) ────────────────────*/
@keyframes burnCard{
  0%{opacity:1; transform:scale(1); filter:brightness(1) saturate(1); box-shadow:none;}
  20%{opacity:1; transform:scale(1.03); filter:brightness(1.8) saturate(2); box-shadow:0 0 6px #ffd36b, 0 0 16px #ff8c00;}
  55%{opacity:0.9; transform:scale(0.96); filter:brightness(0.7) sepia(0.9); box-shadow:0 0 10px #ff5c00;}
  100%{opacity:0; transform:scale(0.82) rotate(-3deg); filter:brightness(0) grayscale(1); box-shadow:none;}
}
.hand .card.burning-out{animation:burnCard 0.45s ease forwards; pointer-events:none; z-index:1500!important;}
/*── Исчезновение НЕ-таргетируемого спелла из руки (2026-07-19, по прямому запросу автора) —
   зеркало @keyframes burnCard выше, но в ХОЛОДНЫХ синих тонах (та же техника, что и
   revealVanish ниже по файлу — там она для клона карты ИИ в центре поля, тут — для
   настоящей карты прямо в руке игрока, никакого fixed-position/translate трюка не нужно,
   карта уже стоит на своём месте во flex-раскладке руки). Применяется ТОЛЬКО к спеллам,
   которые резолвятся МГНОВЕННО (эссенция, добор/сброс карт, AOE и т.п.) — таргетируемые
   спеллы (Bolt/Dmg/Buff/Dispel/...) и так уже покидают руку в момент выбора цели, тут
   доп. анимация не нужна (см. doPlay() в game.js, isPlainInstantSpell). ────────────*/
@keyframes spellCastOut{
  0%{opacity:1; transform:scale(1); filter:brightness(1) saturate(1); box-shadow:none;}
  20%{opacity:1; transform:scale(1.03); filter:brightness(1.8) saturate(2) hue-rotate(180deg); box-shadow:0 0 6px #6bc8ff, 0 0 16px #0080ff;}
  55%{opacity:0.9; transform:scale(0.96); filter:brightness(0.7) saturate(1.4) hue-rotate(180deg); box-shadow:0 0 10px #0066ff;}
  100%{opacity:0; transform:scale(0.82) rotate(-3deg); filter:brightness(0) grayscale(1); box-shadow:none;}
}
.hand .card.spell-cast-out{animation:spellCastOut 0.45s ease forwards; pointer-events:none; z-index:1500!important;}
.hand .card.previewed{transform:translateY(calc(var(--card-h) * -0.34));z-index:2000!important;animation:goldPulse 1.8s ease-in-out infinite;}
.hand .card.previewed{position:relative;border-color:#E8E0C8 !important;}
@media(hover:hover){
  .hand .card:not(.previewed):hover{transform:translateY(-10px) !important;z-index:100 !important;}
}
/* .cursor-hover (2026-07-19) — тот же лифт, что и :hover выше, но навешивается из JS
   через Pointer Events (см. ui.js) вместо native :hover — нужно на iPad с курсором
   (трекпад/мышь), где WebKit не всегда активирует :hover на делегированных кликах
   (см. комментарий у слушателя pointerover/pointerout в ui.js за подробностями). На
   обычном десктопе оба правила просто дублируют друг друга, конфликта нет. */
.hand .card.cursor-hover:not(.previewed){transform:translateY(-10px) !important;z-index:100 !important;}
.field-ability-popup{position:absolute;bottom:calc(100% + 4px);left:50%;transform:translateX(-50%);display:flex;flex-direction:column;gap:3px;align-items:center;padding:4px;z-index:2001;width:70px;pointer-events:all;}
.card-actions-popup{position:absolute;bottom:calc(100% + 4px);left:50%;transform:translateX(-50%);display:flex;flex-direction:column;gap:3px;align-items:center;padding:4px;z-index:2001;width:var(--card-w);box-sizing:border-box;pointer-events:all;}
.card-actions-popup-left{position:absolute;right:calc(100% + 6px);top:0;display:flex;flex-direction:column;gap:3px;align-items:center;z-index:2001;pointer-events:all;}
.card-actions-popup-right{position:absolute;left:calc(100% + 6px);top:0;display:flex;flex-direction:column;gap:3px;align-items:center;z-index:2001;pointer-events:all;}
.cap-btn{display:block;border:none;background:transparent;background-size:contain;background-repeat:no-repeat;background-position:center;cursor:pointer;padding:0;transition:filter .1s, transform .1s;}
.cap-btn.play{width:calc(var(--card-w) * 0.75);height:calc(var(--card-w) * 0.75 * 44/122);background-image:url('../img/btn_play.png');}
.cap-btn.burn{width:var(--card-action-btn-w);height:var(--card-action-btn-w);background-image:url('../img/btn_burn.png');}
.cap-btn.zoom{width:var(--card-action-btn-w);height:var(--card-action-btn-w);background-image:url('../img/btn_zoom.png');}
.fab-btn{display:block;width:calc(var(--card-small-w) * 0.65);height:calc(var(--card-small-w) * 0.65 * 44/122);border:none;background:transparent;background-size:contain;background-repeat:no-repeat;background-position:center;cursor:pointer;padding:0;transition:filter .1s, transform .1s;}
.fab-btn.umbasir{background-image:url('../img/btn_spell.png');}
.fab-btn.mechird{background-image:url('../img/btn_shot.png');} /* Mechird Shot (2026-08-04) — своя иконка, в отличие от Bolt пока не переиспользует btn_spell.png плейсхолдер */
.fab-btn.vardan{background-image:url('../img/btn_spell.png');}
.fab-btn.bounce_ally{background-image:url('../img/btn_spell.png');} /* TEANTIST "Return your ally" (2026-07-30) — тот же ассет */
/*Плейсхолдер под btn_heal.png — файла пока нет, а background-image с несуществующим*/
.fab-btn.heal{background-image:url('../img/btn_heal.png');}
.fab-btn.cancel{background-image:url('../img/btn_spell_cncl.png')}
.fab-btn:hover{filter:brightness(1.25) drop-shadow(0 0 4px #c8a84b88);}
.fab-btn:active{transform:scale(0.95) translateY(2px);}
.cap-btn:hover{filter:brightness(1.25) drop-shadow(0 0 4px #c8a84b88);}
.cap-btn:active{transform:scale(0.95) translateY(2px);}
.card-actions-popup:has(.cap-no-ess){width:var(--card-w);}
.battlefield{min-height:60px;display:flex;gap:4px;align-items:center;flex-wrap:wrap;justify-content:center;flex:1;}
/*── Поле боя — раунд 4 (2026-08-03, автор поймал живьём: на ПУСТОМ поле снизу экрана
    висело ~7vh чёрного пространства — это был баг, не свободное место. .arena (см. её
    комментарий ниже) была position:relative БЕЗ display:flex, так что flex:1 у
    oppFieldZone/playerFieldZone молча игнорировался (родитель не flex — flex:1 не
    работает), оба поля падали на min-height:20vh вместо реального растягивания. На
    ЗАБИТОМ 6v6 поле разница не была видна (контент — 2 ряда карт — сам по себе близок
    к нужной высоте), поэтому баг не поймался на предыдущих раундах правки, где всегда
    тестировался забитый борт. После фикса .arena живой бинарный поиск ЗАНОВО (то же
    самое 6v6, тот же метод) на референсном 390×844 дал НЕ 10.6vh, а 12.35vh безопасного
    максимума — то есть раунд 3 был занижен из-за бага, реального места было больше.
    Взял с тем же запасом, что и раньше (~0.3-0.35vh): 12.0vh.
    ЛЕНДСКЕЙП (новое — раньше это был плоский НЕпроверенный --card-small-h:18vh из :root,
    без спец-правила вообще): та же логика, что портрет, но БЕЗ деления на 2 ряда — в
    лендскейпе места на ширину всегда хватает на 6 карт в один ряд (нет 3-карточного
    max-width-капа), так что упирается высота только в "1 ряд на зону", а не "2 ряда на
    зону" — отсюда и куда больший запас. Проверено бинарным поиском на 6 разных
    лендскейп-экранах (телефон лёжа 844×390/896×414, iPad 1180×820/1366×1024, ноутбук
    1440×900, широкий 1920×1080) — безопасный максимум на самом тесном из них (телефон
    лёжа) — ~20vh, взял с запасом 19.5vh, единое число для всех, тем же принципом, что и
    портретная константа (короткие секции все на vh, процент не плывёт с ростом экрана).
    align-self:center — .field-zone{display:flex;flex-direction:column} по умолчанию
    растягивает детей на всю ширину (align-items:stretch), без этого урезанный по
    max-width .battlefield прилипает к левому краю вместо центра (актуально только для
    портрета — в лендскейпе max-width не задан, стретчить нечего). ──*/
@media(orientation:portrait){
  .battlefield{
    --card-small-h: 12vh;
    --card-small-w: calc(12vh * 0.856);
    --card-small-art-h: calc(12vh * 0.622);
    --card-small-name-h: calc(12vh * 0.133);
    --card-small-stats-h: calc(12vh * 0.122);
    --card-small-name-fs: calc(12vh * 0.1);
    --card-small-stat-fs: calc(12vh * 0.15);
    max-width: calc(3 * var(--card-small-w) + 8px);
    align-self: center;
  }
}
@media(orientation:landscape){
  .battlefield{
    --card-small-h: 19.5vh;
    --card-small-w: calc(19.5vh * 0.856);
    --card-small-art-h: calc(19.5vh * 0.622);
    --card-small-name-h: calc(19.5vh * 0.133);
    --card-small-stats-h: calc(19.5vh * 0.122);
    --card-small-name-fs: calc(19.5vh * 0.1);
    --card-small-stat-fs: calc(19.5vh * 0.15);
  }
}
.stats-bar{position:relative;z-index:0;height:max(4vh, 2vw);font-size:max(calc(4vh * 0.38), calc(2vw * 0.38));display:flex;flex-direction:row;align-items:center;justify-content:center;gap:5px;padding:0 10px;flex-wrap:wrap;border:none;outline:none;box-shadow:none;}
.stats-bar::before{content:''; position:absolute; inset:0; z-index:-1; pointer-events:none; background-size:100% 100%; background-position:center; background-repeat:no-repeat;}
/* ПЛЕЙСХОЛДЕР, прибитый к правому краю статус-бара (небольшой отступ 6px от края,
   не влияет на центрирование остального ряда — стоит вне потока через position:absolute).
   1:1, сторона = 2/3 высоты .stats-bar. Только лаптоп/десктоп. */
.statbar-edge-right{display:none; position:absolute; right:6px;top:50%; transform:translateY(-50%); width:calc(max(4vh, 2vw) * 2 / 3); height:calc(max(4vh, 2vw) * 2 / 3); background:#000;}
@media(min-width:601px){
  .statbar-edge-right{display:block;}
}
/* Tea/Jeet — оба на всю высоту статус-бара (2026-08-08, по прямому запросу автора —
   "чтобы на всю высоту статбара были"), top:0/bottom:0 вместо top:50%+translateY,
   height:100% вместо фиксированной доли от max(4vh,2vw). Jeet теперь той же ШИРИНЫ, что
   Tea (var(--card-action-btn-w) + 2px, тем же принципом, что уже был у Tea) — раньше был
   на своей отдельной формуле (2/3 * 2), автор попросил унифицировать под ширину Tea.
   right:0/left:0 (не 6px, как у самого общего .statbar-edge-right/-left выше) — та же
   логика, что уже была у Tea: ассет должен доходить ровно до края бара, не оставляя зазор. */
.stats-bar.jeet .statbar-edge-right{top:0; bottom:0; right:0; height:100%; transform:none; width:calc(var(--card-action-btn-w) + 2px); background-color:transparent; background-image:url('../img/trubi1_jeet.png'); background-size:100% 100%; background-repeat:no-repeat; background-position:center;}
.stats-bar.tea .statbar-edge-right{top:0; bottom:0; right:0; height:100%; transform:none; width:calc(var(--card-action-btn-w) + 2px); background-color:transparent; background-image:url('../img/trubi1.png'); background-size:100% 100%; background-repeat:no-repeat; background-position:center;}
/* Tea-only: левый плейсхолдер, симметричный правому выше — до этого у Tea вообще не было
   левого edge-элемента (см. render.js, edgeLeft раньше рендерился только для jeet). */
.stats-bar.tea .statbar-edge-left{top:0; bottom:0; left:0; height:100%; transform:none; width:calc(var(--card-action-btn-w) + 2px); background-color:transparent; background-image:url('../img/left_tea.png'); background-size:100% 100%; background-repeat:no-repeat; background-position:center;}
/* Jeet-only: левый край — та же полная высота/ширина-как-у-Tea модель, что у правого края
   выше (2026-08-08, по прямому запросу автора — "у ассетов аналогичных у Джит
   соответственно тоже"). left_tea_jeet.png — новый ассет, аналог left_tea.png для Jeet. */
.stats-bar.jeet .statbar-edge-left{top:0; bottom:0; left:0; height:100%; transform:none; width:calc(var(--card-action-btn-w) + 2px); background-color:transparent; background-image:url('../img/left_tea_jeet.png'); background-size:100% 100%; background-repeat:no-repeat; background-position:center;}
/* Jeet-only: квадратный плейсхолдер у левого края — зеркало правого. Белый фон — временный,
   пока не пришёл реальный арт (statbar_edge_left_jeet.png). */
.statbar-edge-left{display:none; position:absolute; left:6px;top:50%; transform:translateY(-50%); width:calc(max(4vh, 2vw) * 0.5); height:calc(max(4vh, 2vw) * 0.5); background-color:transparent; background-image:url('../img/statbar_edge_left_jeet.png'); background-size:100% 100%; background-repeat:no-repeat; background-position:center;}
@media(min-width:601px){
  .statbar-edge-left{display:block;}
}
.stats-bar.jeet{}
.stats-bar.tea{}
.stats-bar.jeet{background-color:var(--jeet-main);}
.stats-bar.tea{background-color:#5a7278;}
.stats-bar.tea.hp-tier-1::before{background-image:url('../img/bg_statbar_tea1.png');}
.stats-bar.tea.hp-tier-2::before{background-image:url('../img/bg_statbar_tea2.png');}
.stats-bar.tea.hp-tier-3::before{background-image:url('../img/bg_statbar_tea3.png');}
.stats-bar.tea.hp-tier-4::before{background-image:url('../img/bg_statbar_tea4.png');}
.stats-bar.tea.hp-tier-5::before{background-image:url('../img/bg_statbar_tea5.png');}
.stats-bar.jeet.hp-tier-1::before{background-image:url('../img/bg_statbar_jeet1.png');}
.stats-bar.jeet.hp-tier-2::before{background-image:url('../img/bg_statbar_jeet2.png');}
.stats-bar.jeet.hp-tier-3::before{background-image:url('../img/bg_statbar_jeet3.png');}
.stats-bar.jeet.hp-tier-4::before{background-image:url('../img/bg_statbar_jeet4.png');}
.stats-bar.jeet.hp-tier-5::before{background-image:url('../img/bg_statbar_jeet5.png');}
#oppStats::before{transform:scale(-1,-1);}
/*Декоративные плейсхолдеры без текста у бара сверху (#oppStats) отражаются ПОЛНОСТЬЮ —*/
#oppStats .hp-placeholder, #oppStats .statbar-extra{transform:scale(-1,-1);}
/* Jeet edges (2026-08-08) — обновлено вместе с растяжкой на полную высоту выше: раньше тут
   стоял старый top:50%+translateY(-50%)-центрирующий вариант (та же модель, что до правки
   была у .statbar-edge-left/-right в общем виде) — теперь и Tea, и Jeet edges у СЕБЯ уже
   full-height (top:0;bottom:0;left:0/right:0, см. .stats-bar.tea/.jeet .statbar-edge-left/
   -right выше по файлу), так что зеркало #oppStats должно просто флипать по обеим осям
   (scale(-1,-1)) БЕЗ отдельного translateY — та же логика, что уже стояла у Tea-веток
   двумя строками ниже, теперь единообразно для обеих фракций.*/
#oppStats .statbar-edge-left{left:auto; right:0; top:auto; bottom:0; transform:scale(-1,-1);}
#oppStats .statbar-edge-right{right:auto; left:0; top:auto; bottom:0; transform:scale(-1,-1);}
.stat-val{font-weight:bold;}
.stats-bar .stat-icon{width:0.65em;height:0.65em;}
.player-name-box.flash-red, .stat-hp-box.flash-red{animation: flashRed 0.5s ease-out forwards;}
.player-name-box.flash-green, .stat-hp-box.flash-green{animation: flashGreen 0.5s ease-out forwards;}
.stat-ess-box.flash-green{animation: flashGreen 0.5s ease-out forwards;}
.hp-val{color:#e05555;}
.ess-val{color:#E8E0C8;}
.stat-hp-box,.stat-ess-box{height:max(calc(4vh * 0.6), calc(2vw * 0.6));background-repeat:no-repeat;background-position:center;background-size:100% 100%;padding:3px 3px;}
.stat-hp-box{width:calc(max(4vh, 2vw) * 1.53 + 9px); justify-content:center; flex-shrink:0; position:relative;}
.stat-ess-box{width:calc(max(4vh, 2vw) * 1.53 + 9px); justify-content:center; flex-shrink:0;}
.stat-hp-box{background-image:url('../img/bg_statbar_hp.png');}
.stat-ess-box{background-image:url('../img/bg_statbar_hp.png');}
.stats-bar.jeet .stat-hp-box, .stats-bar.jeet .stat-ess-box{background-color:transparent; background-image:url('../img/bg_counters_jeet.png');}
/* Плейсхолдер слева от HP-индикатора — Tea использует существующий boltik.png, Jeet —
   свой файл boltik_jeet.png. Размер — квадрат, сторона = половина высоты .stat-hp-box
   (та же формула × 0.5). */
.hp-placeholder{display:none; width:max(calc(4vh * 0.25), calc(2vw * 0.25)); height:max(calc(4vh * 0.25), calc(2vw * 0.25)); background-size:100% 100%; background-repeat:no-repeat; flex-shrink:0;}
.stats-bar.tea .hp-placeholder{background-image:url('../img/boltik.png');}
.stats-bar.jeet .hp-placeholder{background-image:url('../img/boltik_jeet.png');}
@media(min-width:601px){
  .hp-placeholder{display:block;}
}
/* ПЛЕЙСХОЛДЕРЫ по бокам от pcard-слотов (world/artifact) в статус-барах — 4:1,
   высота = половина высоты .stats-bar (та же формула × 0.5, откуда и высота
   у .hp-placeholder выше). Только лаптоп/десктоп — на телефоне не показываем.
   Jeet — свой готовый арт (statbar_extra_jeet.png, 2026-08-08 — CSS-заглушка
   background-color убрана, ассет уже в репо). Tea — пока остаётся на drill.png
   (statbar_extra_tea.png ещё не пришёл), background-color:#000 у базового правила ниже —
   это фон САМОГО drill.png (не заглушка на будущее, реальный фон текущего ассета Tea). */
.statbar-extra{display:none; width:calc(max(4vh, 2vw) * 0.5 * 4); height:calc(max(4vh, 2vw) * 0.5); background-color:#000; background-size:100% 100%; background-repeat:no-repeat; flex-shrink:0;}
.stats-bar.tea .statbar-extra{background-image:url('../img/drill.png');}
.stats-bar.jeet .statbar-extra{background-color:transparent; background-image:url('../img/statbar_extra_jeet.png');}
@media(min-width:601px){
  .statbar-extra{display:block;}
}
.statbar-core{display:flex;align-items:center;gap:0;flex-shrink:0;}
.player-name-box{width:calc(max(4vh, 2vw) * 2);height:max(4vh, 2vw);background-repeat:no-repeat;background-position:center;background-size:100% 100%;flex-shrink:0;transition:transform .1s;cursor:pointer;position:relative;z-index:2;}
/* Base "portal" icon (.player-name-box) tiers — PLACEHOLDER: all 5 tiers point
   to the same original single-state art for now. Swap in 5 real per-tier
   files later, same naming pattern as the panel background above. */
.tea-name-box.hp-tier-1, .tea-name-box.hp-tier-2, .tea-name-box.hp-tier-3, .tea-name-box.hp-tier-4, .tea-name-box.hp-tier-5{background-image:url('../img/bg_statbar_tea.png');}
.jeet-name-box.hp-tier-1, .jeet-name-box.hp-tier-2, .jeet-name-box.hp-tier-3, .jeet-name-box.hp-tier-4, .jeet-name-box.hp-tier-5{background-image:url('../img/bg_statbar_jeet.png');}
.player-name-box:active{transform:scale(0.92);}
#oppStats .player-name-box{transform:scale(-1,-1);}
#oppStats .player-name-box:active{transform:scale(-1,-1) scale(0.92);}
.player-name-box.base-targetable{position:relative; cursor:crosshair; border-radius:6px; animation:pulseNameBox .8s infinite alternate;}
.player-name-box.base-targetable::after{content:''; position:absolute;inset:0; background:#ff0000; border-radius:6px; pointer-events:none; animation:baseBlinkRed .8s infinite alternate;}
/* Высота — БЫЛА чистым `7vh` (2026-08-11, по прямому запросу автора — фон "уезжал"/
   резался в лендскейпе на телефоне/айпаде, хотя выставлялся под лаптоп). Причина: чистый
   vh не даёт никакой опоры на ШИРИНУ экрана — на широком-но-невысоком лендскейп-вьюпорте
   бокс (тянется почти на всю ширину арены) становился экстремально широким и низким,
   аспект уезжал далеко от того, что было на лаптопе, а background-size:cover на таком
   боксе обрезает высоту картинки куда агрессивнее, чем на лаптопном аспекте — виден уже
   другой, смещённый срез арта. У .stats-bar/.header ЭТОЙ проблемы нет по той же причине,
   по которой она есть здесь: обе используют `max(Xvh, Yvw)` — vw-слагаемое не даёт боксу
   схлопнуться на широких-невысоких экранах, аспект остаётся ближе к лаптопному. Тот же
   приём применён и здесь: `max(7vh, 3.5vw)` — то же соотношение vh:vw (2:1), что уже
   используется в .stats-bar (4vh/2vw) и .header (5vh/2.5vw), просто с базой 7vh (родное
   значение этого бокса) вместо их 4vh/5vh. На лаптопе/портрете vw-слагаемое обычно меньше
   vh (там аспект и так безопасный) — там ничего визуально не меняется, эффект включается
   именно там, где раньше был баг. */
.opp-hand-zone{height:max(7vh, 3.5vw);padding:0 6px 4px;border:none;display:flex;align-items:flex-start;justify-content:center;overflow:hidden;position:relative;}
/*── Фон hand-zone — по фракции, которая сейчас занимает позицию (см. reorderZones() в
   render.js, className выставляется туда динамически) ──*/
/*── background-size:cover → 100% 100% (2026-08-12, по прямому запросу автора, баг
    подтверждён скриншотами с айпада и телефона) — у opp-hand-zone/player-hand-zone
    высота фиксирована (max(7vh,3.5vw)), а ширина = 100% экрана, поэтому пропорции
    контейнера сильно разные на разных устройствах. background-size:cover в этом
    случае сам выбирает, по какой оси растягивать (чтобы гарантированно "накрыть"
    контейнер), и обрезает лишнее по другой — а точка обрезки по ширине "плавает" в
    зависимости от aspect ratio экрана, отсюда горизонтальный съезд рисунка именно
    на айпаде/телефоне. У соседней .stats-bar::before (строка сразу под этой, с
    которой фон должен визуально стыковаться) уже стоит background-size:100% 100% —
    жёсткое растяжение без "умного" кропа, поэтому она всегда садится одинаково
    независимо от устройства. Приводим сюда же для консистентности. */
.opp-hand-zone.tea{background:url('../img/bg_handO_bar.png');background-position:center;background-repeat:no-repeat;background-size:100% 100%;}
.opp-hand-zone.jeet{background:url('../img/bg_handO_bar_jeet.png');background-position:center;background-repeat:no-repeat;background-size:100% 100%;}
.field-zone{border:none;background:transparent;overflow:visible;display:flex;flex-direction:column;position:relative;z-index:0;padding:2px;}
/*── Резиновая арена (2026-08-02, по прямому запросу автора — под END TURN оставалась
    пустая полоса ~6vh, потому что все ОСТАЛЬНЫЕ секции .game — header/stats-bar×2/
    opp-hand/player-hand/bottom-bar — фиксированной высоты, а .field-zone только
    min-height:20vh без flex:1, так что при сумме < 100vh остаток просто повисал внизу
    (.game{min-height:100vh}, дети паkуются от начала, flex-grow ни у кого не стоял).
    flex:1 здесь — не "запас на будущее", а буквально более короткий путь к результату:
    не нужно руками высчитывать точное число под конкретные пропорции экрана (и оно бы
    всё равно поехало на очень широких мониторах — у .header формула max(5vh,2.5vw,40px),
    на ультравайде vw-слагаемое перевешивает vh и шапка становится выше, чем на iPad/
    лаптопе, так что фикс-число, подогнанное под один экран, на другом бы снова оставило
    щель). flex:1 гарантированно дотягивает до края при любых пропорциях, без вычислений.
    min-height:20vh оставлен как подстраховка снизу (не даст полю схлопнуться меньше
    прежнего, даже если flex почему-то даст 0). ──*/
#oppFieldZone{min-height:20vh;height:auto;overflow:visible;border:none;flex:1;}
#playerFieldZone{min-height:20vh;height:auto;overflow:visible;border:none;margin-top:6px;flex:1;}
.field-zone:first-of-type{border-bottom:1px solid #ffffff11;}

/* ═══════════════════════════════════════════════════════════════════════
   ARENA SIDE COLUMNS — замена bottom-bar (2026-08-03, по прямому запросу автора)
   .arena оборачивает oppFieldZone+playerFieldZone (сама раскладка полей не менялась,
   просто теперь у неё общий relative-родитель) — .arena-col-left/right лежат
   position:absolute поверх "мёртвой зоны" по краям арены, на всю высоту ОБЕИХ полей
   разом (включая шов между ними), т.к. .arena сама растягивается на их суммарную высоту
   через обычный document flow (oppFieldZone/playerFieldZone внутри неё как и раньше).
   РАУНД 2 (2026-08-03, автор прислал референс-скрин): кнопка+каунтер сведены В ОДНУ
   строку (.arena-pair) вместо отдельных грид-ячеек — 3 равные строки вместо 1:1:2:1:1
   на 5. Каунтеры "смотрят" на карты — у кладбища (слева) он справа от кнопки, у колоды
   (справа) он слева от плейсхолдера — см. порядок детей .arena-pair в index.html.
   ═══════════════════════════════════════════════════════════════════════ */
/*── БАГ (найден автором 2026-08-03 живьём — пустое поле, Turn 1, внизу экрана лишние
   ~7vh чёрного пространства): .arena раньше был просто position:relative, БЕЗ display:flex.
   oppFieldZone/playerFieldZone внутри него — со своим flex:1, но flex:1 работает ТОЛЬКО
   если родитель сам display:flex — тут родитель им не был, так что flex:1 тихо
   игнорировался, оба поля падали на min-height:20vh вместо реального растягивания на всю
   доступную высоту. При забитом 6v6 поле разница почти не видна (контент — 2 ряда карт —
   сам по себе близок к нужной высоте), но на пустом поле (мало контента) вылезает как
   пустой хвост внизу экрана. Фикс: .arena сам становится flex-колонкой (и сам получает
   flex:1 от .game, как раньше получали id напрямую oppFieldZone/playerFieldZone) —
   position:relative остаётся (нужен как контекст для position:absolute колонок-кнопок,
   которые НЕ flex-айтемы, т.к. abs-позиционирование выводит их из потока — friend не
   мешают друг другу). min-height:0 — стандартный fix для flex-column, чтобы дети с
   собственным flex:1 могли реально сжиматься/расти, а не упирались в auto-минимум. ──*/
.arena{position:relative;display:flex;flex-direction:column;flex:1;min-height:0;}
/*── РАУНД 5 (2026-08-04, по прямому запросу автора — "нужен фикс с понятным ратио, чтобы
   я мог точно нарисовать ассет" + "каунтеры не резиновые, как у ХП/эссенции"):
   1) .arena-pair больше НЕ auto/content-fit (раунд 4) — теперь ФИКСИРОВАННЫЙ width +
      aspect-ratio 8:5 (1.6:1, ширина:высота). Одно и то же соотношение сторон в обеих
      ориентациях (автору нужен ОДИН понятный ратио под ассет) — меняется только
      абсолютный масштаб (доля от --arena-col-w, своя для портрета/лендскейпа, см. ниже),
      сама форма бокса — нет. Раз ширина фиксирована, а не "сколько нужно контенту" —
      2-значный каунтер (было "бокс растягивается и соперничает с картами") больше
      физически не может раздвинуть бокс шире, чем 1-значный.
   2) Каунтер (.arena-pair .stat-badge) — тот же приём, что уже есть у .stat-hp-box/
      .stat-ess-box в шапке (см. их комментарий ниже по файлу): явные width И height
      вместо auto/min-width. "1" и "35" внутри теперь занимают ОДИН и тот же бокс,
      просто текст центрируется — сам каунтер больше не "резиновый".
   3) .arena-col-left/right: align-items с center на flex-start/flex-end — бокс
      (уже не 100% ширины колонки, а 75%/55% от неё) теперь прижат к ВНЕШНЕМУ краю
      экрана, а не центрирован в колонке. Освободившиеся ~25-45% ширины колонки уходят
      на сторону карт — доп. запас от "соперничества с полем" сверх самого уменьшения
      бокса. Кнопки лога/конца хода — туда же, тот же align-items, тот же запрос автора
      ("сами боксы, а также кнопки лога и конец хода, надо прибить к краям экрана"). ──*/
.arena-col{
  position:absolute;top:0;bottom:0;width:var(--arena-col-w);
  display:flex;flex-direction:column;justify-content:space-between;
  padding:0;box-sizing:border-box;z-index:15;
}
.arena-col-left{left:0;align-items:flex-start;}
.arena-col-right{right:0;align-items:flex-end;}

/* Бокс "кнопка + каунтер" — плейсхолдер под будущий кастомный фон (автор: "как
   продолжение статус-бара") — Чай серый, Джит чёрный (.tea/.jeet, выставляется в
   reorderZones()).
   2026-08-04 (по прямому запросу автора — "какое расстояние между каунтером и кнопкой
   кладбища/деки? Надо такой же отступ добавить слева и справа внутри бокса" + "фон
   каунтера сделать в два раза шире"): раньше размер бокса задавался через aspect-ratio
   от ширины, а иконка/каунтер — независимо друг от друга (иконка — от высоты бокса,
   каунтер — своя формула) — при таком подходе нельзя было просто "сделать каунтер шире"
   без риска вылезти за бокс, и гарантировать, что паддинг слева/справа РОВНО равен
   зазору между иконкой и каунтером, тоже было не через что. Теперь наоборот: сначала
   считаются 4 фиксированных куска (паддинг/иконка/каунтер-ширина/каунтер-высота) через
   --arena-box-* переменные, а РАЗМЕР БОКСА — это их сумма (3 одинаковых зазора: левый
   край→иконка, иконка→каунтер, каунтер→правый край + сама иконка + сам каунтер). Один
   и тот же --arena-box-pad используется и как padding бокса, и как gap между иконкой и
   каунтером — гарантированно один и тот же отступ с обеих сторон, буквально одна
   переменная, а не "подогнанные вручную похожие числа".
   2026-08-11 (по прямому запросу автора — правка выравнивания каунтера по вертикали к
   краю кнопки, см. #arenaDeckPairOpp/#arenaGravePairOpp/... .stat-badge ниже): padding
   был `0 var(--arena-box-pad)` — ТОЛЬКО горизонтальный, высота бокса при этом всё равно
   считалась как `icon + 2*pad` (задумывалось как декоративный отступ вокруг центрированной
   кнопки, а не как настоящий CSS padding). Из-за этого у align-items:center кнопка и так
   висела с зазором в 1×pad от истинного верха/низа бокса, а align-self:flex-start/flex-end
   на каунтере целился в истинный край БОКСА — на 1×pad ДАЛЬШЕ, чем реальный край кнопки:
   каунтер уезжал за кнопку, а не вставал с ней на одну линию. Теперь padding — на все 4
   стороны (было бы то же самое число и раньше, просто буквы `0` не было бы) — при
   box-sizing:border-box это делает content-box РОВНО = icon (высота без остатка, слэка
   больше нет), кнопка/иконка занимает его целиком без центрирования по вертикали, а
   align-self:flex-start/flex-end на каунтере теперь целится в ту же самую границу
   content-box, где стоит кнопка — верх с верхом, низ с низом, гарантированно на одной
   линии. */
.arena-pair{display:flex;flex-direction:row;align-items:center;justify-content:center;
  width: calc(3 * var(--arena-box-pad) + var(--arena-box-icon) + var(--arena-box-counter-w));
  height: calc(var(--arena-box-icon) + 2 * var(--arena-box-pad));
  flex-shrink:0;box-sizing:border-box;
  position:relative;z-index:0;
  padding: var(--arena-box-pad);
  gap: var(--arena-box-pad);
}
/* Фон — на ::before, не напрямую на .arena-pair (2026-08-04, по прямому запросу автора —
   ассеты bg_grav_tea/jeet.png, bg_deck_tea/jeet.png + отражение по X/Y для противника).
   ПОЧЕМУ ::before, а не просто background на самом .arena-pair: зеркалить нужно ТОЛЬКО
   фон, а не иконку/каунтер внутри (те должны остаться читаемыми) — тот же приём, что уже
   применяется у #oppStats::before/.player-name-box и т.п. (см. их комментарий выше по
   файлу) — реальный DOM-контент (кнопка+каунтер) не трогаем, крутим отдельный слой ПОД
   ним. .grave-pair/.deck-pair — статичные маркеры, проставлены в разметке (не меняются
   при reorderZones() — это какой СЛОТ, кладбище или колода, а не какая фракция сейчас
   в нём сидит), .tea/.jeet — динамические, как и раньше. */
.arena-pair::before{content:'';position:absolute;inset:0;z-index:-1;
  background-size:100% 100%;background-repeat:no-repeat;background-position:center;}
.arena-pair.grave-pair.tea::before{background-image:url('../img/bg_grav_tea.png');}
.arena-pair.grave-pair.jeet::before{background-image:url('../img/bg_grav_jeet.png');}
.arena-pair.deck-pair.tea::before{background-image:url('../img/bg_deck_tea.png');}
.arena-pair.deck-pair.jeet::before{background-image:url('../img/bg_deck_jeet.png');}
/* Отражение по X и Y (2026-08-04, по прямому запросу автора — "чтоб они тоже как
   статус-бар/база/плейсхолдеры зеркалились") — ТОЛЬКО у боксов ПРОТИВНИКА, тот же
   scale(-1,-1), что уже стоит на #oppStats и её содержимом. ID, не позиция в колонке —
   после того как оппонентские боксы поменялись местами между левой/правой колонкой
   (см. разметку — arenaDeckPairOpp теперь слева, arenaGravePairOpp справа), только id
   надёжно говорит "это бокс противника", left/right больше ни о чём не говорит. */
#arenaGravePairOpp::before, #arenaDeckPairOpp::before{transform:scale(-1,-1);}
.arena-pair > *{flex-shrink:0;position:relative;z-index:1;}
/* Вертикальное прижатие каунтера к краю бокса, ближнему к "своему" игроку
   (2026-08-11, по прямому запросу автора) — кнопка/иконка (button_grav_ и deck_)
   не трогается. Работает ТОЛЬКО вместе с правкой padding на .arena-pair чуть выше
   (см. её комментарий 2026-08-11) — раз padding теперь настоящий (все 4 стороны, не
   только left/right), content-box своей высотой РОВНО = высоте иконки, слэка по
   вертикали больше нет, и align-self на каунтере целится в ту же границу, где уже
   стоит кнопка (без этой предпосылки align-self целился бы в край САМОГО бокса, который
   на 1×pad дальше от кнопки, чем нужно — каунтер уезжал бы за кнопку, см. историю правок).
   Меняется только .stat-badge через align-self (переопределяет align-items:center на
   .arena-pair только для этого одного ребёнка).
   Opp-пары — физически ВСЕГДА верхние боксы колонки (deckPairOpp/gravePairOpp —
   статичные слоты, см. комментарий у #arenaGravePairOpp::before выше — reorderZones()
   меняет только tea/jeet-скин и данные внутри, не позицию), поэтому каунтер там —
   flex-start (к потолку бокса). Player-пары — всегда нижние (gravePairPlayer/
   deckPairPlayer), каунтер — flex-end (к полу бокса), на одной линии с низом кнопки. */
#arenaDeckPairOpp .stat-badge, #arenaGravePairOpp .stat-badge{align-self:flex-start;}
#arenaGravePairPlayer .stat-badge, #arenaDeckPairPlayer .stat-badge{align-self:flex-end;}

/* Кладбище (слева) — переиспользует существующий арт button_grav_*.png (был в
   bottom-bar, просто теперь на 2 динамических слота Opp/Player вместо статичных
   tea/jeet — см. reorderZones() в render.js, которая на каждый рендер решает, какая
   фракция сейчас "opp", а какая "player", и перевешивает className/onclick/data-faction
   на #arenaGraveOpp/#arenaGravePlayer соответственно). */
.arena-grave-btn{width:var(--arena-box-icon);height:var(--arena-box-icon);border:none;
  background-color:transparent;
  background-size:contain;background-repeat:no-repeat;background-position:center;
  padding:0;cursor:pointer;transition:transform .1s,filter .1s;flex-shrink:0;}
.arena-grave-btn.tea{background-image:url('../img/button_grav_1.png');}
.arena-grave-btn.tea:hover{filter:brightness(1.2);}
.arena-grave-btn.tea:active,.arena-grave-btn.tea.open{background-image:url('../img/button_grav_2.png');}
.arena-grave-btn.jeet{background-image:url('../img/button_grav_1_jeet.png');}
.arena-grave-btn.jeet:hover{filter:brightness(1.2);}
.arena-grave-btn.jeet:active,.arena-grave-btn.jeet.open{background-image:url('../img/button_grav_2_jeet.png');}

/* Колода (справа) — тот же плейсхолдер-арт, что был в bottom-bar (deck.png/deck_jeet.png,
   раньше ID-скоуп #deckPlaceholderJ, теперь класс — позиция Opp/Player не привязана к
   фракции намертво). Чисто визуальный якорь + мишень для анимации добора карты
   (_deckPlaceholderRect() в render.js), не кликабелен. */
.arena-deck-slot{width:var(--arena-box-icon);height:var(--arena-box-icon);
  background-image:url('../img/deck.png');
  background-size:contain;background-repeat:no-repeat;background-position:center;
  pointer-events:none;flex-shrink:0;}
.arena-deck-slot.jeet{background-image:url('../img/deck_jeet.png');}

/* Каунтеры — переиспользуют .stat-badge (тот же ЛСД-вид/blink на пустой колоде, см.
   .stat-badge.deck-count-empty ниже по файлу), tea/jeet-скин раньше был приколочен к
   #teaBottomBar/#jeetBottomBar — теперь классы, т.к. позиция Opp/Player больше не
   привязана намертво к фракции. width/height ЯВНО заданы (не auto/min-width) — тот же
   приём, что у .stat-hp-box/.stat-ess-box чуть ниже по файлу — "1" и "35" внутри не
   меняют размер самого бокса, только своё положение внутри него (justify/align-center
   у базового .stat-badge, см. её правило). Ширина — в 2 раза больше, чем была
   (2026-08-04, по прямому запросу автора, см. --arena-box-counter-w). */
.arena-pair .stat-badge{width:var(--arena-box-counter-w);height:var(--arena-box-counter-h);
  min-width:0;flex-shrink:0;}
.stat-badge.tea{color:#4DB854;}
.stat-badge.jeet{color:#b44fd4;background-color:transparent;background-image:url('../img/bg_counters_jeet.png');}

/* Боевой лог (слева, средний слот) — кастомные арт-ассеты (2026-08-04, по прямому
   запросу автора — убрана вся плейсхолдер-обвязка: рамка/фон/эмодзи-заглушка и
   transition/filter-эффекты на hover/active — визуальная обратная связь теперь целиком
   в самих ассетах (btn_logH/2_*.png), CSS поверх ничего не подмешивает).
   width — от --arena-log-w (см. её комментарий у :root), НЕ от --arena-col-w — кнопка
   лога зафиксирована в одном размере независимо от ориентации/ширины колонки.
   Скин — playerK (см. её комментарий в reorderZones(), js/render.js — фиксирован в VS AI,
   меняется по факту смены устройства в хотсите). */
.arena-log-btn{width:calc(var(--arena-log-w) * 0.85);aspect-ratio:1/1;border:none;
  background-color:transparent;
  background-size:contain;background-repeat:no-repeat;background-position:center;
  padding:0;cursor:pointer;position:relative;}
.arena-log-btn.tea{background-image:url('../img/btn_log1_tea.png');}
.arena-log-btn.tea:hover{background-image:url('../img/btn_logH_tea.png');}
.arena-log-btn.tea:active{background-image:url('../img/btn_log2_tea.png');}
.arena-log-btn.jeet{background-image:url('../img/btn_log1_jeet.png');}
.arena-log-btn.jeet:hover{background-image:url('../img/btn_logH_jeet.png');}
.arena-log-btn.jeet:active{background-image:url('../img/btn_log2_jeet.png');}

/* Attack Rush — "Атаковать базу всеми" (2026-08-08, по прямому запросу автора). Тот же
   паттерн, что arena-log-btn (кастомные арт-ассеты на все состояния, CSS ничего поверх не
   подмешивает — ни filter, ни transform) — просто у этой кнопки ЕЩЁ и 4-е состояние,
   .disabled, свой отдельный ассет (btn_allrushD.png), а не просто приглушённый idle.
   Тот же --arena-log-w-размер, что у лога (автор попросил "размер такой же"). Disabled —
   через класс, не через атрибут disabled на <button> (кнопка ВСЁ РАВНО должна быть
   кликабельна с точки зрения DOM, чтобы playSfx/attackBaseWithAll() успели проверить
   актуальные условия и молча выйти, а не полагаться на браузерный disabled — тот же
   принцип, по которому в проекте нигде не используется нативный disabled на игровых
   кнопках, см. .btn-end/.modal-art-btn). */
.arena-rush-btn{width:calc(var(--arena-log-w) * 0.85);aspect-ratio:1/1;border:none;
  background-color:transparent;
  background-size:contain;background-repeat:no-repeat;background-position:center;
  padding:0;cursor:pointer;position:relative;}
.arena-rush-btn.tea{background-image:url('../img/btn_allrush_tea1.png');}
.arena-rush-btn.tea:hover{background-image:url('../img/btn_allrush_teaH.png');}
.arena-rush-btn.tea:active{background-image:url('../img/btn_allrush_tea2.png');}
.arena-rush-btn.tea.disabled{background-image:url('../img/btn_allrush_teaD.png');cursor:default;}
.arena-rush-btn.jeet{background-image:url('../img/btn_allrush_jeet1.png');}
.arena-rush-btn.jeet:hover{background-image:url('../img/btn_allrush_jeetH.png');}
.arena-rush-btn.jeet:active{background-image:url('../img/btn_allrush_jeet2.png');}
.arena-rush-btn.jeet.disabled{background-image:url('../img/btn_allrush_jeetD.png');cursor:default;}

/* Конец хода (справа, средний слот) — КВАДРАТНОЙ была по запросу автора 2026-08-03,
   2026-08-04 автор попросил ВЫСОКУЮ 1:1.5 (ширина:высота) вместо квадрата — старый
   button_1.png/_jeet.png и так растягивается без сохранения пропорций
   (background-size:100% 100%, автор в курсе, рисует новый под актуальную форму).
   Ширина — от --arena-col-w (в отличие от лога, автор эту кнопку увеличивать не
   просил обратно уменьшать — она и задумывалась расти вместе с колонкой в лендскейпе),
   высота — от ширины через aspect-ratio, поэтому её отдельно считать не нужно. */
.arena-endturn-slot{width:calc(var(--arena-endturn-w) * 0.85);aspect-ratio:1/1.5;position:relative;}
.arena-endturn-slot .btn-end{position:absolute;inset:0;width:100%;height:100%;
  transform:none;background-size:100% 100%;}

/* Плейсхолдер-заглушка ПОКА нет реального арта под конкретный слот (используется, если
   автор захочет временно спрятать слот тем же .placeholder-конвеншном, что у остальных
   кнопок проекта) — не применяется по умолчанию ни к одному из элементов выше, кроме
   .arena-log-btn (см. её собственный .placeholder-блок выше), оставлено про запас. */

/*── ЛЕНДСКЕЙП — та же схема "сумма фиксированных кусков", просто своя (меньшая) доля от
   --arena-col-w на каждый кусок (2026-08-04) — колонка сама по себе уже большая в
   лендскейпе (План Б), поэтому абсолютный масштаб всех --arena-box-* меньше, но
   ПРОПОРЦИИ между ними те же самые (все 4 числа домножены на один и тот же коэффициент
   ~0.73 — тот же, что раньше отличал 0.55 от 0.75 у ширины бокса), так что итоговый
   ratio бокса остаётся тем же, просто меньше в пикселях. ──*/
@media(orientation:landscape){
  :root{
    /* --arena-endturn-w заморожена ЗДЕСЬ на СТАРОЙ формуле --arena-col-w (та, что была
       тут до сегодняшнего расширения, clamp(120px,15vh,155px)) — если её не переопределить
       именно в этом блоке, она бы упала обратно на портретную формулу из :root
       (clamp(56px,8vh,96px)), и конец хода в лендскейпе внезапно ужался бы, а не остался
       прежним. --arena-col-w ниже теперь растёт только для колонки/боксов кладбища-деки. */
    --arena-endturn-w: clamp(120px, 15vh, 155px);
    --arena-col-w: clamp(120px, 20vh, 165px);
    /* Доли бокса пересчитаны (2026-08-04, по прямому запросу автора — "боксы на
       лаптопе... сделал бы крупнее... чтобы по длине было как у End Turn"): сумма долей
       (3×pad+icon+counter-w) с 0.6497 поднята до ~0.93 (тем же коэффициентом ×1.4315
       умножены ВСЕ 4 доли разом — соотношение между ними, а значит и ratio самого бокса
       (по-прежнему ~1.98:1), не изменилось, "сохранили и ратио, и все внутренние
       пропорции", как просили). 0.93, а не 1.0 — небольшой запас, чтобы бокс не упирался
       ровно в край колонки. Итог не дотягивает ТОЧНО до высоты End Turn на самом тесном
       экране (iPad-лендскейп, мёртвая зона всего 169px — жёстче, чем требует эта
       формула) — там результат нужно будет сверить живьём отдельно, см. ответ в чате. */
    --arena-box-pad: calc(var(--arena-col-w) * 0.05225);
    --arena-box-icon: calc(var(--arena-col-w) * 0.36575);
    --arena-box-counter-w: calc(var(--arena-col-w) * 0.40754);
    --arena-box-counter-h: calc(var(--arena-col-w) * 0.25610);
  }
}

/*── ПЛАНШЕТ-ПОРТРЕТ (2026-08-11, по прямому запросу автора — на iPad в портрете между
   боксами кладбища/деки и кнопками лог/раш/конец хода огромные пустые зазоры, автор
   прислал скрин с обведёнными зонами). Причина: базовая формула --arena-col-w в :root
   (`clamp(56px, 8vh, 96px)`) ОДНА на все портретные экраны — и телефон, и планшет.
   На телефоне (портретная высота ~700-900px) 8vh держится в середине клампа, колонка
   заполняется соразмерно. На высоком айпаде (портретная высота ~1150-1200px+) 8vh уже
   ~95-96px — формула упирается В ПОТОЛОК клампа, и БОКС, И КНОПКА ЛОГА выходят
   практически одного размера (~95-96px, ratio ~1:1) — при этом сама колонка (`.arena-col`,
   `top:0;bottom:0`) всё равно тянется на весь рост экрана через justify-content:
   space-between, так что расти в такой ситуации может только ЗАЗОР между тремя группами
   (боксы/лог+раш/боксы), а не сам контент — отсюда и мёртвая зона со скрина.
   Автор подтвердил ориентир на глаз — "1.4-1.6x от текущих 96px", БЕЗ оглядки на
   landscape-цифры (тот блок целился в другое соотношение бокс:кнопка, ~2.4-2.5:1,
   но при переносе на портретную — более узкую по ширине — колонку с текущей портретной
   базой кнопки (~95px) это дало бы потолок ~230-240px и обкорнало бы игровое поле).
   `clamp(96px, 12vh, 150px)` — на устройстве автора (портретная высота ≈1194px) даёт
   ≈143px (12% от 1194), это ×1.49 от старых 96px — середина запрошенного диапазона. Пол
   96px — это старый потолок телефона, ниже он не упадёт ни на одном планшете (12vh уже
   даёт 96px при высоте всего ~800px, с которой не столкнётся ни один планшет-портрет).
   Потолок 150px — небольшой запас над теми же ≈143px, чтобы на более высоких айпадах
   (iPad Pro 13" портрет, ~1366px высоты, 12vh≈164px) рост не продолжался безлимитно.
   ВАЖНО: --arena-log-w/--arena-endturn-w (кнопки лог/раш/конец хода) ЗДЕСЬ НЕ трогаются —
   по прямому запросу автора остаются на портретной формуле из :root как есть. --arena-box-*
   (паддинг/иконка/каунтер) НЕ переопределяются отдельно — они уже завязаны на
   `calc(var(--arena-col-w) * доля)` в :root (те же портретные доли 0.05/0.35/0.39/0.245,
   что и на телефоне) и подхватят новый --arena-col-w автоматически через каскад
   custom-свойств — те же пропорции бокса, что на телефоне сегодня, просто в бОльшем
   масштабе, а не новая форма бокса.
   Порог `min-width:601px` — тот же, что уже используется в проекте для разделения
   телефон/планшет (см. `.player-hand-zone`/`.opp-hand-zone` чуть ниже по файлу) — ни один
   телефон в портрете за него не заходит (даже iPhone Pro Max ~430px шириной), так что
   телефонная раскладка этим блоком не затрагивается. ──*/
@media(orientation:portrait) and (min-width:601px){
  :root{
    --arena-col-w: clamp(96px, 12vh, 150px);
  }
}

.player-hand-zone{height:27vh;padding:6px 6px;border:none;position:relative;}
.player-hand-zone.tea{background:url('../img/bg_handP_bar.png');background-position:center;background-repeat:no-repeat;background-size:100% 100%;}
.player-hand-zone.jeet{background:url('../img/bg_handP_bar_jeet.png');background-position:center;background-repeat:no-repeat;background-size:100% 100%;}
/*── Горизонтальный телефон (2026-07-17, автор — "карты руки наезжают на статусбар в
    горизонтальном режиме"): --card-h (25vh, :root) и .player-hand-zone (27vh) — оба чисто
    vh-based, без общего запаса на паддинг/рамку. На высоком портретном экране разницы в 2vh
    с запасом хватает, но на низком landscape-вьюпорте (у телефона в альбомной ориентации vh
    маленький в абсолютных px) 2vh — это считаные пиксели, а после вычета padding/бордера
    карта (min-height:var(--card-h) у .hand) физически не помещается в зону и вылезает вниз —
    #playerStats идёт следующим по разметке сразу под .player-hand-zone, поэтому "наезд"
    именно на статусбар, а не куда-то ещё. Порог max-height:500px отсекает именно телефоны
    (реальные телефоны в landscape — примерно 360-430px высоты), айпад в landscape заметно
    выше (768px+) и это правило его не касается. ──*/
@media(max-height:500px) and (orientation:landscape){
  :root{ --card-h: 24vh; }
  /*── Хидер и боковые арена-ассеты — та же болезнь, что у --card-h выше (2026-08-04,
     автор поймал живьём — хидер и End Turn ВДВОЕ толще/выше положенного именно на
     телефоне лёжа): .header{height:max(5vh,2.5vw,40px)} и --arena-col-w:
     clamp(120px,15vh,155px) — оба посчитаны с полом/минимумом, подобранным под
     iPad/ноутбук в лендскейпе (там 5vh/15vh САМИ по себе уже больше пола/минимума,
     клампы ни на что не влияют). На низком телефон-вьюпорте (390px высоты) 5vh=19.5px
     и 15vh=58.5px — заметно МЕНЬШЕ полов (40px/120px), так что клампы принудительно
     раздувают оба элемента до пола, который на iPad/ноуте никогда не активировался —
     отсюда и "вдвое толще" (замерено живьём: хидер 10.3% высоты экрана вместо 5%,
     End Turn 39.2% вместо 19%). Здесь оба ограничения снижены под именно этот диапазон
     экранов — то же соотношение к vh, что и у iPad/ноута, без непропорционального пола. ──*/
  .header{height:max(5vh, 2.5vw);}
  .hamburger-btn{width:max(5vh, 2.5vw);height:max(5vh, 2.5vw);}
  :root{
    /* --arena-endturn-w заморожена на СТАРОЙ формуле именно этого брейкпоинта
       (clamp(56px,15vh,96px), была тут до сегодняшнего расширения --arena-col-w) — тот
       же принцип, что у общего лендскейп-блока выше по файлу (см. её комментарий там).
       --arena-box-* (доли бокса) отдельно тут не переопределяем — уже унаследованы из
       общего @media(orientation:landscape) блока выше (телефон лёжа тоже landscape,
       оба блока применяются к :root, этот всего лишь more specific по max-height и
       переопределяет только то, что явно указано). */
    --arena-endturn-w: clamp(56px, 15vh, 96px);
    --arena-col-w: clamp(56px, 20vh, 130px);
  }
}
@media(min-width:601px){
  .player-hand-zone{padding:6px calc(var(--card-action-btn-w) + 8px);}
  .opp-hand-zone{padding:0 15px 4px;}
}
/*── Hand-zone side rails — Tea использует существующие файлы (были плейсхолдером,
   теперь официально "тиевский" арт), Jeet — новые _jeet файлы. ──*/
.player-hand-zone.tea::after{content:''; position:absolute; inset:0; z-index:600; pointer-events:none; border-style:solid; border-width:calc(var(--card-action-btn-w) + 2px); border-image-source:url('../img/hands_border.png'); border-image-slice:60; border-image-width:calc(var(--card-action-btn-w) + 2px); border-image-outset:0; border-image-repeat:round;}
.player-hand-zone.jeet::after{content:''; position:absolute; inset:0; z-index:600; pointer-events:none; border-style:solid; border-width:calc(var(--card-action-btn-w) + 2px); border-image-source:url('../img/hands_border_jeet.png'); border-image-slice:60; border-image-width:calc(var(--card-action-btn-w) + 2px); border-image-outset:0; border-image-repeat:round;}
.opp-hand-zone.tea::after{content:''; position:absolute; inset:0; z-index:600; pointer-events:none; border-style:solid; border-width:15px; border-image-source:url('../img/hands_border2.png'); border-image-slice:30; border-image-width:15px; border-image-outset:0; border-image-repeat:round;}
.opp-hand-zone.jeet::after{content:''; position:absolute; inset:0; z-index:600; pointer-events:none; border-style:solid; border-width:15px; border-image-source:url('../img/hands_border2_jeet.png'); border-image-slice:30; border-image-width:15px; border-image-outset:0; border-image-repeat:round;}
@media(max-width:600px){
  /* На мобиле player-hand-zone переключается на "border2"-арт (тот же, что у opp-hand-zone),
     просто тоньше — как и было до разделения по фракциям, только теперь для каждой фракции
     свой border2-файл. */
  .player-hand-zone.tea::after{border-width:15px; border-image-source:url('../img/hands_border2.png'); border-image-slice:30; border-image-width:15px;}
  .player-hand-zone.jeet::after{border-width:15px; border-image-source:url('../img/hands_border2_jeet.png'); border-image-slice:30; border-image-width:15px;}
  .opp-hand-zone.tea::after{border-width:7.5px; border-image-width:7.5px;}
  .opp-hand-zone.jeet::after{border-width:7.5px; border-image-width:7.5px;}
}
/*── Target-prompt overlay — точечные заклинания (ARCHIVE/JOURNEY/OBLIVION/dispel) ──*/
.target-prompt-overlay{position:absolute; inset:0; z-index:620; display:flex; flex-direction:column; align-items:center; justify-content:center; cursor:pointer; padding:0 4px; box-sizing:border-box; background:rgba(120,0,0,0.12); animation:targetPromptPulseBg 1.6s ease-in-out infinite;}
.target-prompt-overlay.hidden{display:none;}
.target-prompt-box{background:rgba(10,4,4,1); border:1px solid #e14c4399; border-radius:10px; padding:10px 18px; box-shadow:0 4px 18px #00000088, 0 0 14px #e14c4333;}
.target-prompt-main{font-family:'MEK',monospace; font-size:min(7.5vw, 34px); line-height:1.05; color:#e14c43; letter-spacing:1.5px; text-align:center; text-shadow:0 0 10px #e14c4377, 0 2px 4px #000; animation:targetPromptBlink 1.6s ease-in-out infinite;}
.target-prompt-sub{font-family:'MEK',monospace; font-size:min(3.6vw, 17px); line-height:1.2; color:#e14c43; letter-spacing:0.8px; text-align:center; margin-top:4px; text-shadow:0 0 6px #e14c4366, 0 2px 3px #000; animation:targetPromptBlink 1.6s ease-in-out infinite;}
@keyframes targetPromptBlink{
  0%,100%{opacity:1;}
  50%{opacity:0.55;}
}
@keyframes targetPromptPulseBg{
  0%,100%{background:rgba(120,0,0,0.10);}
  50%{background:rgba(150,0,0,0.18);}
}
.actions{display:flex;flex-direction:column;gap:6px;align-items:center;background:#06060e;border-top:1px solid #ffffff11;border-bottom:1px solid #ffffff11;}
.action-btns{display:flex;gap:5px;flex-wrap:wrap;justify-content:center;align-items:center;}
/*── End Turn — базовый (Tea) арт + hover/active/waiting; позиционирование теперь задаёт
   .arena-endturn-slot .btn-end (см. блок ARENA SIDE COLUMNS выше) — здесь только сам вид
   кнопки, унаследованный без изменений от bottom-bar. ──*/
.btn-end{border:none;background:url('../img/button_1.png') center/contain no-repeat;color:transparent; transition:transform .1s,filter .1s; flex-shrink:0;}
.btn-end:hover{filter:brightness(1.25);}
.btn-end:active{background-image:url('../img/button_2.png');}
/*── Jeet — свой арт для End Turn. Раньше скоуп шёл через родителя (#jeetBottomBar
   .btn-end), теперь оба #teaEndTurnBtn/#jeetEndTurnBtn — статичные элементы в ОДНОМ
   слоте (см. render.js — только один из двух показан за раз), скоуп по id напрямую. ──*/
#jeetEndTurnBtn{background-image:url('../img/button_1_jeet.png');}
#jeetEndTurnBtn:active{background-image:url('../img/button_2_jeet.png');}
/*── Состояние "ход ИИ" (VS AI) — панель игрока не скрывается целиком, вместо*/
.btn-end.btn-waiting{background-image:url('../img/btn_wait.gif') !important;cursor:default;pointer-events:none;filter:none !important;}
.btn-end.btn-waiting:hover,.btn-end.btn-waiting:active{filter:none;}
#jeetEndTurnBtn.btn-waiting{background-image:url('../img/btn_wait_jeet.gif') !important;}
/*── Stat-badge — база переиспользуется для арена-каунтеров кладбища/колоды (см. блок
   ARENA SIDE COLUMNS выше за .tea/.jeet скин, который раньше был приколочен к
   #teaBottomBar/#jeetBottomBar). ──*/
.stat-badge{display:flex; align-items:center; justify-content:center; background-image:url('../img/bg_statbar_hp.png'); background-size:100% 100%; background-repeat:no-repeat; background-position:center; min-width:3.5vh; height:2.8vh; font-family:'MEK',monospace; font-size:2.2vh; font-weight:bold; pointer-events:none; padding:0 0.6vh; flex-shrink:0;}
.card-detail-overlay{position:fixed;inset:0;background:#000000cc;z-index:300;display:flex;align-items:center;justify-content:center;padding:20px;}
.card-detail-box{position:relative;display:flex;align-items:center;justify-content:center;}
.card-detail-close{position:absolute;top:-32px;right:-8px;font-size:28px;font-family:'MEK',monospace;cursor:pointer;color:#888;background:none;border:none;z-index:10;}
.card-detail-close:hover{color:#c8a84b;}
.card-detail-scaled{--card-h: min(68vw, 52vh); --card-w: calc(var(--card-h) * 0.716); --card-art-size: calc(var(--card-h) * 0.535); --card-pad: calc(var(--card-h) * 0.021); --card-name-h: calc(var(--card-h) * 0.096); --card-stats-h: calc(var(--card-h) * 0.085); --card-text-h: calc(var(--card-h) * 0.24); --card-world-name-h: calc(var(--card-h) * 0.096); --card-world-text-h: calc(var(--card-h) * 0.16); width: var(--card-w) !important; height: var(--card-h) !important; cursor: default !important; transform: none !important; opacity: 1 !important; font-size: calc(var(--card-h) * 0.05) !important;}
@media(max-width:600px){
  .card-detail-scaled{--card-h: min(calc((100vw - 40px) / 0.716), 68vh);}
}
@keyframes pcardTextReveal{
  0%{opacity:0; transform:translate(0); text-shadow:none;}
  1%{opacity:1;}
  15%{transform:translate(-2px,1px); text-shadow:2px 0 #ff0044,-2px 0 #00e5ff;}
  30%{transform:translate(2px,-1px); text-shadow:-2px 0 #ff0044,2px 0 #00e5ff;}
  45%{transform:translate(-1px,0); text-shadow:1px 0 #ff0044,-1px 0 #00e5ff;}
  60%{transform:translate(1px,1px); text-shadow:-1px 0 #ff0044,1px 0 #00e5ff;}
  100%{opacity:1; transform:translate(0); text-shadow:none;}
}
.game-fade-in{animation: gameFieldFadeIn 0.5s ease-out forwards;}
@keyframes arenaSlideDownIn{
  from{transform: translateY(var(--slide-dist, -100%)); opacity: 0;}
  to{transform: translateY(0); opacity: 1;}
}
.arena-slide-down-in{animation: arenaSlideDownIn 1.43s cubic-bezier(0.22,0.68,0,1) both;}
.arena-slide-up-in{animation: arenaSlideUpIn 1.43s cubic-bezier(0.22,0.68,0,1) both;}
.arena-header-fade-in{animation: gameFieldFadeIn 1.43s ease-out both;}
/*── Горизонтальный въезд лога/конца хода (2026-08-04, по прямому запросу автора —
   "красиво будет") — тот же принцип, что у arenaSlideDown/UpIn (translate по X вместо Y,
   свой --slide-dist-x на элемент, см. playArenaRevealAnimation() в ui.js). Лог и конец
   хода — единичные элементы (не группа из нескольких полос, как рука+статбар), так что
   для каждого используется СВОЙ offsetWidth, а не сумма группы. ──*/
@keyframes arenaSlideLeftIn{
  from{transform: translateX(var(--slide-dist-x, -100%)); opacity: 0;}
  to{transform: translateX(0); opacity: 1;}
}
@keyframes arenaSlideRightIn{
  from{transform: translateX(var(--slide-dist-x, 100%)); opacity: 0;}
  to{transform: translateX(0); opacity: 1;}
}
.arena-slide-left-in{animation: arenaSlideLeftIn 1.43s cubic-bezier(0.22,0.68,0,1) both;}
.arena-slide-right-in{animation: arenaSlideRightIn 1.43s cubic-bezier(0.22,0.68,0,1) both;}
.field-star.field-star-grow-in{animation-name: starGrowIn; animation-timing-function: cubic-bezier(0.34,1.56,0.64,1); animation-fill-mode: both; animation-iteration-count: 1;}
.battle-begins-text{position:fixed; left:50%; top:50%; transform:translate(-50%,-50%); display:flex; align-items:center; justify-content:center; z-index:60; pointer-events:none;}
.battle-begins-text.hidden{display:none;}
/* 2026-08-05, багфикс по прямому запросу автора — на портретных телефонах длинный текст
   предупреждения о пустой колоде ("ENEMY OUT OF CARDS! They lose in N more turns!") обрезался
   по краям экрана. Причина — _playCenterBannerText() (ui.js) подгоняет font-size под фактически
   измеренную ширину текста, но letter-spacing:2px ниже — это ФИКСИРОВАННЫЕ пиксели, которые НЕ
   уменьшаются вместе с font-size, так что при сильном уменьшении шрифта (длинный текст на узком
   экране) letter-spacing начинает занимать непропорционально большую долю ширины, и реальная
   отрисованная ширина превышает то, что посчитал JS. JS-часть фикса (ui.js) добавляет
   корректирующий второй проход — это работает на ЛЮБОМ размере экрана и ничего тут не трогает.
   CSS-подстраховка (перенос строки вместо обрезания, на случай если что-то всё равно не
   поместится) — ТОЛЬКО у мобильного @media(max-width:600px) ниже по файлу (тот же брейкпоинт,
   что и у остальных portrait-переопределений в этом файле) — по прямому запросу автора
   правка нужна была строго для вертикального телефона; на десктопе/лендскейпе баннер (в т.ч.
   "Battle begins!") остаётся ровно как было — nowrap, без ограничения ширины. */
.battle-begins-inner{font-family:'MEK',monospace; color:#e14c43; text-shadow:0 0 12px #e14c4399, 0 0 28px #e14c4355; text-align:center; letter-spacing:2px; white-space:nowrap; display:inline-block;}
@media(max-width:600px){
  .battle-begins-text{max-width:92vw; box-sizing:border-box;}
  .battle-begins-inner{white-space:normal; overflow-wrap:break-word; max-width:100%;}
}
.battle-begins-inner.battle-begins-in{animation: battleBeginsIn 0.5s cubic-bezier(0.34,1.56,0.64,1) both;}
.battle-begins-inner.battle-begins-pulse{animation: battleBeginsPulse 1s ease-in-out infinite;}
.battle-begins-inner.battle-begins-out{animation: battleBeginsOut 0.5s ease-in forwards;}
.field-ability-popup{animation: popupSlideUp 0.18s ease-out forwards;}
.card-actions-popup-right{animation: popupSlideRight 0.18s ease-out forwards;}
.card-actions-popup-left{animation: popupSlideLeft 0.18s ease-out forwards;}
.float-number{position:absolute; font-family:'MEK',monospace; font-weight:bold; font-size:20px; pointer-events:none; z-index:9999; animation: floatUp 1.5s ease-out forwards;}
.float-number.dmg{color:#e05555; left:calc(28% + var(--dmg-jitter-x, 0px)); top:calc(74% + var(--dmg-jitter-y, 0px)); transform:translate(-50%,-50%);}
.float-number.heal{color:#44ff88; left:28%; top:81%; transform:translate(-50%,-50%);}
.float-number.atk{color:#55aaff; left:76%; top:81%; transform:translate(-50%,-50%);}
.float-number.maxhp{color:#44ff88; left:28%; top:81%; transform:translate(-50%,-50%);}
.float-number.armoraura{color:var(--color-cream); left:28%; top:81%; transform:translate(-50%,-50%);}
.float-number.armorloss{color:var(--color-cream);
  left:calc(var(--card-small-w)*0.048 + var(--card-small-h)*0.13);
  top:calc(2px + var(--card-small-art-h) - (var(--card-small-h)*0.065));
  transform:translate(-50%,-50%);}
.float-number-base{left:50%; top:-4px; transform:translateX(-50%); font-size:16px;}
.float-number-base.fnb-dmg{color:#e05555;}
.float-number-base.fnb-heal{color:#44ff88;}
/* Squad-activated / Fear-applied overlay — text placeholder for now, swap the
   textContent for an <img class="field-fx-gif"> once the real gif art exists.
   Sits centered over the creature (per spec: "gif appearing over the character"). */
@keyframes fieldFxPop{
  0%{opacity:0; transform:translate(-50%,-50%) scale(0.6);}
  15%{opacity:1; transform:translate(-50%,-50%) scale(1.1);}
  30%{opacity:1; transform:translate(-50%,-50%) scale(1);}
  80%{opacity:1; transform:translate(-50%,-50%) scale(1);}
  100%{opacity:0; transform:translate(-50%,-50%) scale(1);}
}
.field-fx-popup{position:absolute; left:50%;top:50%; transform:translate(-50%,-50%); z-index:500; pointer-events:none; font-family:'MEK',monospace; font-weight:bold; font-size:calc(var(--card-small-h) * 0.14); text-align:center; white-space:nowrap; text-shadow:0 0 6px #000, 0 0 3px #000; animation:fieldFxPop 1.2s ease-out forwards;}
.field-fx-popup.fx-squad{color:#ffd700;}
.field-fx-popup.fx-squad-lost{color:#999999;}
.field-fx-popup.fx-cleaned{color:#5be3ff;}
.field-fx-popup.fx-fear{color:#b44fd4;}
/* Текстовые плейсхолдеры (автор потом заменит на кастомные гифки, как SQUAD!/FEARED!/
   CLEANED выше) — см. CLAUDE.md, "Анимации" в Art backlog. */
.field-fx-popup.fx-untap{color:#ffe066;}
.field-fx-popup.fx-sacrifice{color:#c94f4f;}
.field-fx-popup.fx-shard{color:#ff9944;}
.field-fx-popup.fx-spell-dmg{color:#e05555;}
.field-fx-popup.fx-spell-buff{color:#55aaff;}
.field-fx-popup.fx-miss{color:#e8e8e8;} /* Foxy Trick (2026-07-27) — нейтрально-серый, отличается от красного fx-fear/fx-spell-dmg, чтобы промах читался не как урон */
.field-fx-popup.fx-immune{color:#5be3ff;} /* Ward-иммунитет (2026-07-27, по прямому запросу автора) — тот же голубой, что у fx-cleaned, читается как "защитный", не как урон/дебафф */
.field-fx-popup.fx-absorb{color:#ffd700;} /* Solana Shield поглощает удар (2026-07-27, по прямому запросу автора) — золотой, как fx-squad, читается как "мощная защитная реакция" */
.field-fx-popup.fx-market-up{color:#4caf50;} /* Game of Market (2026-07-28) — рынок вверх, бонус-урон по цели — зелёный */
.field-fx-popup.fx-sagaup{color:#ffffff;} /* Saga Up / Stand Alone (2026-08-06, по прямому запросу автора) — тот же field-fx-popup стиль/позиция, что у fx-market-up, просто белый цвет */
.field-fx-popup.fx-market-down{color:#e05555;} /* Game of Market (2026-07-28) — рынок вниз, самоурон — тот же красный, что fx-spell-dmg */
.card-small.dying{animation: cardDie 0.4s ease-in forwards; pointer-events:none;}
/* dying-hit (2026-08-06, смена подхода — см. подробный комментарий в render.js/rZone() —
   вместо dying-pulse теперь используем обычный hitShake перед полётом на кладбище).
   Специфичность (3 класса: card-small+dying+dying-hit) выше базового .card-small.dying
   (2 класса) — нужно явно перебить cardDie, иначе с .hit была бы ничья по специфичности
   (тоже 2 класса) и побеждал бы cardDie как объявленный ниже в файле. */
.card-small.dying.dying-hit{animation: hitShake .25s ease; pointer-events:none;}
/*── Смерть — полёт на кладбище / сожжение в Войд (2026-08-05, по прямому запросу автора)
   Оба варианта запускаются МГНОВЕННО, без паузы — dying-hold (пауза + шейк-совместимость),
   которая тут раньше стояла, убрана целиком (см. историю правок render.js/game.js — автор
   попросил откатить, "карта слишком долго умирает"). ──*/
/* card-death-fly — сам летящий клон (position:fixed выставляется инлайн в JS вместе с
   left/top/width/height под стартовую позицию — тут только базовые константы). */
.card-small.card-death-fly{position:fixed; margin:0; pointer-events:none; z-index:1600;}
/* dying-pulse (2026-08-05, по прямому запросу автора — "выглядит массивом, не успеваешь
   понять что карта умерла") — короткий "вздох" НА СВОЁМ МЕСТЕ перед полётом на кладбище:
   чистое уменьшение до SHRINK_PULSE_SCALE (render.js), БЕЗ фейда (opacity держится 1 всю
   паузу — это не про "исчезновение", а про "вот эта карта сейчас умирает", читаемость).
   Специфичность (3 класса) выше базового .card-small.dying (2 класса) — корректно
   переопределяет cardDie на время паузы; после её конца JS сам убирает cardEl и подменяет
   его летящим клоном (_flyCardToGrave(), который стартует ИМЕННО с этого же scale —
   ноль видимого "скачка" между концом паузы и стартом полёта). */
@keyframes cardShrinkPulse{
  0%{transform:scale(1); opacity:1;}
  100%{transform:scale(0.82); opacity:1;}
}
/* z-index:1600 (2026-08-05, багфикс по прямому запросу автора, вместе с position:fixed-
   "прибиванием" в render.js — см. её комментарий у rZone()/dying-pulse) — раз карта на время
   паузы теперь position:fixed (вне flex-раскладки ряда), даём ей тот же z-index, что и у
   последующего .card-death-fly клона (см. styles.css выше), чтобы не было провала/скачка
   z-порядка в момент передачи эстафеты паузы клону. */
.card-small.dying.dying-pulse{animation:cardShrinkPulse 140ms ease-in forwards; pointer-events:none; z-index:1600;}
/* dying-void — уход в Войд: та же техника, что и .hand .card.burning-out (@keyframes
   burnCard выше по файлу), но в ЧЁРНО-СЕРЫХ тонах вместо оранжевых — см. cardVoidBurn.
   Специфичность та же, что у .card-small.dying (2 класса каждая) — побеждает по порядку в
   файле (эта ниже), так что cardVoidBurn корректно переопределяет cardDie без доп. классов. */
@keyframes cardVoidBurn{
  0%{opacity:1; transform:scale(1); filter:brightness(1) saturate(1) grayscale(0); box-shadow:none;}
  20%{opacity:1; transform:scale(1.03); filter:brightness(1.6) saturate(0) grayscale(1); box-shadow:0 0 6px #999, 0 0 16px #000;}
  55%{opacity:0.9; transform:scale(0.96); filter:brightness(0.5) saturate(0) grayscale(1); box-shadow:0 0 10px #000;}
  100%{opacity:0; transform:scale(0.82) rotate(-3deg); filter:brightness(0) grayscale(1); box-shadow:none;}
}
.card-small.dying-void{animation:cardVoidBurn 0.45s ease forwards; pointer-events:none;}
.card-small.activating{animation: cardActivate 0.5s ease-in-out forwards; z-index: 10;}
@keyframes cardEnter{
  from{opacity:0; transform:scale(.7); filter:brightness(.55) blur(4px);}
  to{opacity:1; transform:scale(1); filter:brightness(.55) blur(0);}
}
.card.card-drawn{animation: cardDrawn 0.3s ease-out forwards;}
/*── Клон реальной карты, летящий из колоды в руку (замена обезличенного
   card-fly-sprite/runaha.png — автор попросил 2026-07-13 показывать настоящий
   арт сразу). Сам визуал — это склонированный .card, тут только позиционирование
   поверх интерфейса + тень; transform/left/top/opacity анимируются инлайново
   из _flyCardFromDeck (render.js). ──────────────────────────────────────────*/
.card-fly-clone{z-index:5500; pointer-events:none; box-shadow:0 6px 18px #000000aa;}
/* NANA (2026-07-29, "Nanas from SMB") — арт-ассет nana.gif, физически летящий от карты-
   носителя тега к выбранной цели (карте или базе), см. throwBananaFx() в game.js. Тот же
   position:fixed + left/top-transition приём, что у .card-fly-clone выше. Квадратный gif,
   размер — calc() от var(--card-small-h) (переопределяется в адаптивном брейкпоинте выше
   по файлу), а НЕ vh/vw напрямую — по прямому запросу автора, чтобы пропорция к размеру
   карты на поле оставалась одинаковой на любом устройстве, а не только к вьюпорту. Изначально
   было 0.34 (подобрано под прежний визуальный размер эмодзи-плейсхолдера, который заменяет),
   уменьшено на 25% (2026-07-30, по прямому запросу автора) → 0.255. */
.nana-banana-fly{position:fixed; z-index:5500; pointer-events:none; width:calc(var(--card-small-h) * 0.255); height:calc(var(--card-small-h) * 0.255); object-fit:contain; filter:drop-shadow(0 2px 4px #000000aa);}
/* BOLT (Umbasir, 2026-07-30) — арт bolt.gif, летящий от карты-кастера к цели, см.
   throwBoltFx() в game.js. Тот же position:fixed + left/top-transition приём, что у
   .nana-banana-fly выше — ширина сознательно = ширине банана (по прямому запросу автора),
   высота НЕ фиксируется числом, а считается через aspect-ratio (артборд 2:1, высокий) —
   так пропорция картинки не искажается, чем бы ни задавалась ширина. */
.bolt-fly{position:fixed; z-index:5500; pointer-events:none; width:calc(var(--card-small-h) * 0.255); aspect-ratio:1/2; object-fit:contain; filter:drop-shadow(0 2px 4px #000000aa);}
/* BULLET (Mechird Shot, 2026-08-04) — арт bullet.gif, летящий от карты-кастера к цели, см.
   throwShotFx() в game.js. Точный клон .bolt-fly выше, только вдвое уже (0.255→0.1275) и
   квадратный артборд (1:1, а не 1:2 как у bolt.gif — по прямому запросу автора). */
.bullet-fly{position:fixed; z-index:5500; pointer-events:none; width:calc(var(--card-small-h) * 0.1275); aspect-ratio:1/1; object-fit:contain; filter:drop-shadow(0 2px 4px #000000aa);}
/*── Раскрытие спелла ИИ (2026-07-19, по прямому запросу автора, VS AI режим ТОЛЬКО —
   см. playSpellRevealAnimation() в render.js) — клон реальной карты вылетает из центра
   скрытой руки ИИ (oppHandZone) к центру поля, зависает лицом кверху ~0.4с, затем
   растворяется. revealVanish — зеркало @keyframes burnCard выше по файлу, но в
   ХОЛОДНЫХ синих тонах (а не оранжевых) — чтобы "раскрытие спелла" визуально не
   путалось со сжиганием карты в руке, хотя техника дисолва та же самая.
   ВАЖНО (баг-фикс 2026-07-19, автор нашёл живьём — "смещена ниже и правее"): CSS-
   анимация ПОЛНОСТЬЮ подменяет свойство transform значением из текущего кадра, а не
   складывается с инлайновым transform, который выставляет playSpellRevealAnimation()
   (там `translate(-50%,-50%) scale(1)` — это и есть центрирование клона на точке
   left/top). Если в кадрах ниже писать только `scale(...)` без `translate(-50%,-50%)`,
   в момент старта анимации центрирующий сдвиг обнуляется, и клон визуально прыгает на
   половину своей ширины/высоты вниз-вправо (left/top остаются точкой ЦЕНТРА, но без
   translate элемент считает точкой привязки свой ЛЕВЫЙ ВЕРХНИЙ угол). Поэтому каждый
   кадр здесь явно повторяет `translate(-50%,-50%)` перед scale/rotate. ──────────*/
.spell-reveal-clone{z-index:6500; pointer-events:none; box-shadow:0 6px 20px #000000cc;}
@keyframes revealVanish{
  0%{opacity:1; transform:translate(-50%,-50%) scale(var(--reveal-scale, 2.08)); filter:brightness(1) saturate(1); box-shadow:none;}
  20%{opacity:1; transform:translate(-50%,-50%) scale(calc(var(--reveal-scale, 2.08) * 1.03)); filter:brightness(1.7) saturate(1.8) hue-rotate(180deg); box-shadow:0 0 6px #6bc8ff, 0 0 16px #0080ff;}
  55%{opacity:0.9; transform:translate(-50%,-50%) scale(calc(var(--reveal-scale, 2.08) * 0.96)); filter:brightness(0.75) saturate(1.3) hue-rotate(180deg); box-shadow:0 0 10px #0066ff;}
  100%{opacity:0; transform:translate(-50%,-50%) scale(calc(var(--reveal-scale, 2.08) * 0.82)) rotate(3deg); filter:brightness(0) grayscale(1); box-shadow:none;}
}
.spell-reveal-clone.vanishing{animation:revealVanish 0.45s ease forwards;}
.screen-edge-flash{position:fixed; inset:0; z-index:9999; pointer-events:none;}
.screen-edge-flash.flash-red{animation: screenFlashRed 0.5s ease-out forwards;}
.screen-edge-flash.flash-green{animation: screenFlashGreen 0.5s ease-out forwards;}
.zone-shake{animation: zoneShakeImpact 0.35s ease-out;}
.zone-shake-up{animation: zoneShakeImpactUp 0.35s ease-out;}
/* БАГФИКС (2026-08-06, найдено при расследовании смещения Vanguard/death-анимаций) —
   добавлен :not(.activating) в цепочку исключений у всех 4 позиций ряда. Раньше .activating
   (рывок "подъёма для удара", @keyframes cardActivate, 500мс, translateY до -15px) тут не
   исключался — а специфичность этого селектора (9 :not()-условий + nth-child = 10
   селекторов уровня класса) НАМНОГО выше .card-small.activating (2 класса), поэтому
   idle-покачивание (cardFloatA-D, амплитуда 2-4px) ВСЕГДА выигрывало и полностью
   перекрывало анимацию рывка атаки — она тихо ни разу не отыгрывала визуально, что бы ни
   стояло в activateCard()/JS. Обнаружено эмпирически (getComputedStyle().animationName
   показывал cardFloatB вместо cardActivate на карте с классом activating). */
/* БАГФИКС (2026-08-06, найдено по [FLYDEBUG]-логу автора — Vanguard-клон рендерился РОВНО
   в центре цели вместо top-left, т.е. без translate(-50%,-50%)) — добавлен :not(.card-fly-
   clone):not(.card-death-fly). Летящие клоны (_playFieldFlyIfPending/_flyCardToGrave и т.д.,
   render.js) подвешиваются напрямую в document.body, а не в ряд поля — их позиция среди
   ДЕТЕЙ body может случайно совпасть с nth-child(4n+N), и тогда idle-покачивание поля
   (более высокая специфичность, см. комментарий у :not(.activating) ниже) перехватывает
   `transform` у клона — CSS-анимация всегда побеждает inline/transition на том же свойстве,
   даже если сама этого свойства почти не меняет. Побеждая, она стирает наш
   translate(-50%,-50%), которым клон центрируется относительно left/top — клон визуально
   схлопывается в угол своего "невидимого" бокса, что и выглядит как смещение точно на
   половину размера карты. Совпадение по nth-child — вопрос того, сколько ещё элементов
   болтается в body в этот момент, отсюда и "иногда, без явного паттерна". */
.card-small:not(.exhausted):not(.sleeping):not(.selected):not(.targetable):not(.healable):not(.hit):not(.entering):not(.dying):not(.activating):not(.card-fly-clone):not(.card-death-fly):nth-child(4n+1){animation:cardFloatA 3s ease-in-out infinite;}
.card-small:not(.exhausted):not(.sleeping):not(.selected):not(.targetable):not(.healable):not(.hit):not(.entering):not(.dying):not(.activating):not(.card-fly-clone):not(.card-death-fly):nth-child(4n+2){animation:cardFloatB 3.4s ease-in-out -0.8s infinite;}
.card-small:not(.exhausted):not(.sleeping):not(.selected):not(.targetable):not(.healable):not(.hit):not(.entering):not(.dying):not(.activating):not(.card-fly-clone):not(.card-death-fly):nth-child(4n+3){animation:cardFloatC 2.7s ease-in-out -1.5s infinite;}
.card-small:not(.exhausted):not(.sleeping):not(.selected):not(.targetable):not(.healable):not(.hit):not(.entering):not(.dying):not(.activating):not(.card-fly-clone):not(.card-death-fly):nth-child(4n+4){animation:cardFloatD 3.8s ease-in-out -2.2s infinite;}
.field-nebula{position: absolute; width: 200px; height: 100px; border-radius: 50%; filter: blur(40px); opacity: 0.08; pointer-events: none; z-index: -1; animation: nebulaDrift 20s ease-in-out infinite;}
.field-nebula.tea{background: #4DB854;}
.field-nebula.jeet{background: #B44FD4;}
.field-star{position: absolute; width: 3px; height: 3px; background: #fff; pointer-events: none; z-index: -1; animation: starTwinkle 3s ease-in-out infinite;}
.glitch-text{animation:textGlitch 0.25s steps(2, end); display:inline-block;}
/*── Card tag tooltip ───────────────────────────────────────────── (×1.5 размер)*/
.card-tooltip{position:fixed; z-index:10501; pointer-events:none; background:#0c0c18ee; border:1px solid #9B937F; border-radius:6px; padding:10.5px 16.5px; max-width:285px; box-shadow:0 4px 16px #CBBE9A66; opacity:0; transform:scale(0.85); transform-origin:center center; transition:opacity .15s ease, transform .15s ease;}
.card-tooltip.tt-visible{opacity:1; transform:scale(1);}
.tt-ess-icon{width:14px;height:14px;vertical-align:middle;margin:0 2px;image-rendering:pixelated;}
.tt-name{font-family:'MEK',monospace; font-size:18px; color:#E8E0C8; margin-bottom:6px; letter-spacing:1px;}
.tt-desc{font-family:'MEK',monospace; font-size:15px; color:#9B937F; line-height:1.55;}
/*── "z Z Z" на спящих картах ────────────────────────────────────────────────*/
.card-zzz{position:absolute; top:30%; right:12%; z-index:5; pointer-events:none; font-family:'MEK',monospace; color:#ffffff; text-shadow:0 0 4px #000, 0 0 2px #000; line-height:1;}
.card-zzz span{display:inline-block; opacity:0; animation:zzzFloat 2.4s ease-out infinite;}
.card-zzz span:nth-child(1){font-size:calc(var(--card-small-h, 80px) * 0.10);animation-delay:0s;}
.card-zzz span:nth-child(2){font-size:calc(var(--card-small-h, 80px) * 0.13);animation-delay:0.4s;}
.card-zzz span:nth-child(3){font-size:calc(var(--card-small-h, 80px) * 0.17);animation-delay:0.8s;}
@keyframes pcardReadyPulseWhite{
  0%,100%{box-shadow: 0 0 6px #FFFFFFAA, 0 0 14px #FFFFFFCC;}
  50%{box-shadow: 0 0 14px #FFFFFFFF, 0 0 28px #FFFFFFDD;}
}
@keyframes pcardReadyPulsePurple{
  0%,100%{box-shadow: 0 0 6px #B44FD4AA, 0 0 14px #B44FD4CC;}
  50%{box-shadow: 0 0 14px #B44FD4FF, 0 0 28px #B44FD4DD;}
}
@keyframes pcardTargetingBlink{
  0%,100%{color:#fff; opacity:1;}
  50%{color:#fff; opacity:0.4;}
}
@keyframes goldPulseWeak{
  0%,100%{box-shadow: 0 0 6px #d9b95a99, 0 0 14px #d9b95a55;}
  50%{box-shadow: 0 0 10px #d9b95aff, 0 0 22px #d9b95a88;}
}
@keyframes hitShake{
  0%{transform:scale(1);}
  20%{transform:translateX(-4px) scale(.97);}
  40%{transform:translateX(4px) scale(.97);}
  70%{transform:translateX(-2px) scale(.99);}
  100%{transform:translateX(0) scale(1);}
}
@keyframes pulse{
  from{box-shadow:0 0 4px #e05555;}
  to{box-shadow:0 0 12px #e05555;}
}
@keyframes mishenBlink{
  0%,100%{opacity:1;}
  50%{opacity:0.55;}
}
/* Пустая колода (2026-07-27, по прямому запросу автора — ИСПРАВЛЕНО в той же сессии: раньше
   мигала opacity ВСЕГО элемента, включая background-image "экран дисплея" у .stat-badge —
   выглядело как будто дисплей то и дело становится прозрачным, неестественно. Теперь мигает
   ТОЛЬКО цвет/свечение самой цифры (color+text-shadow, не opacity — фон background-image
   этим свойством вообще не затрагивается), а у бэйджа с реальным "экраном" (.stat-badge)
   ДОПОЛНИТЕЛЬНО статичный (не мигающий) красный оттенок+свечение самого бокса. */
.deck-count-empty{animation:deckCountTextBlink 1.1s ease-in-out infinite;}
@keyframes deckCountTextBlink{
  0%,100%{color:#ff3b3b;text-shadow:0 0 5px #ff3b3b,0 0 10px #ff3b3b;}
  50%{color:#7a1010;text-shadow:none;}
}
.stat-badge.deck-count-empty{box-shadow:inset 0 0 0 999px rgba(255,20,20,0.4),0 0 8px 2px rgba(255,59,59,0.75);}
@keyframes pulseG{
  from{box-shadow:0 0 4px #44ff88;}
  to{box-shadow:0 0 12px #44ff88;}
}
@keyframes goldPulse{
  0%{box-shadow:0 0 4px #C8A84B44,0 0 8px #C8A84B66;}
  50%{box-shadow:0 0 10px #C8A84BCC,0 0 22px #C8A84B88,0 0 34px #C8A84B33;}
  100%{box-shadow:0 0 4px #C8A84B44,0 0 8px #C8A84B66;}
}
@keyframes silverPulse{
  0%{box-shadow: 0 0 4px #BFCED944, 0 0 8px #BFCED966;}
  50%{box-shadow: 0 0 10px #BFCED9CC, 0 0 22px #BFCED988, 0 0 34px #BFCED933;}
  100%{box-shadow: 0 0 4px #BFCED944, 0 0 8px #BFCED966;}
}
@keyframes selected{
  0%,100%{transform:translateY(0) scale(1);}
  50%{transform:translateY(-2px) scale(1.015);}
}
@keyframes pcardShutterFrames{
  0%{background-position:0% 0;}
  100%{background-position:100% 0;}
}
@keyframes pcardTextRevealDim{
  0%{opacity:0; transform:translate(0); text-shadow:none;}
  1%{opacity:0.45;}
  15%{transform:translate(-2px,1px); text-shadow:2px 0 #ff0044,-2px 0 #00e5ff;}
  30%{transform:translate(2px,-1px); text-shadow:-2px 0 #ff0044,2px 0 #00e5ff;}
  45%{transform:translate(-1px,0); text-shadow:1px 0 #ff0044,-1px 0 #00e5ff;}
  60%{transform:translate(1px,1px); text-shadow:-1px 0 #ff0044,1px 0 #00e5ff;}
  100%{opacity:0.45; transform:translate(0); text-shadow:none;}
}
@keyframes gameFieldFadeIn{
  from{opacity:0;}
  to{opacity:1;}
}
@keyframes arenaSlideUpIn{
  from{transform: translateY(var(--slide-dist, 100%)); opacity: 0;}
  to{transform: translateY(0); opacity: 1;}
}
@keyframes starGrowIn{
  0%{opacity: 0; transform: scale(0);}
  65%{opacity: 1; transform: scale(1.5);}
  100%{opacity: 0.6; transform: scale(1);}
}
@keyframes battleBeginsIn{
  0%{opacity:0; transform:scale(0);}
  60%{opacity:1; transform:scale(1.12);}
  100%{opacity:1; transform:scale(1);}
}
@keyframes battleBeginsPulse{
  0%,100%{text-shadow:0 0 12px #e0303099, 0 0 28px #e0303055; transform:scale(1);}
  50%{text-shadow:0 0 20px #e03030cc, 0 0 44px #e0303088; transform:scale(1.04);}
}
@keyframes battleBeginsOut{
  from{opacity:1;}
  to{opacity:0;}
}
@keyframes popupSlideUp{
  from{opacity:0; transform: translateX(-50%) translateY(16px);}
  to{opacity:1; transform: translateX(-50%) translateY(0);}
}
@keyframes popupSlideUpLifted{
  from{opacity:0; transform: translateX(-50%) translateY(16px);}
  to{opacity:1; transform: translateX(-50%) translateY(0);}
}
@keyframes popupSlideRight{
  from{opacity:0; transform:translateX(-12px);}
  to{opacity:1; transform:translateX(0);}
}
@keyframes popupSlideLeft{
  from{opacity:0; transform:translateX(12px);}
  to{opacity:1; transform:translateX(0);}
}
@keyframes floatUp{
  0%{opacity:1; transform:translateY(0) scale(1);}
  60%{opacity:1; transform:translateY(-20px) scale(1.2);}
  100%{opacity:0; transform:translateY(-30px) scale(1);}
}
@keyframes cardDie{
  0%{opacity:1; transform:scale(1);}
  100%{opacity:0; transform:scale(0);}
}
@keyframes cardActivate{
  0%{transform: translateY(-4px);}
  40%{transform: translateY(-15px);}
  70%{transform: translateY(-15px);}
  100%{transform: translateY(0);}
}
@keyframes cardEnterAwake{
  from{opacity:0; transform:scale(.7); filter:blur(4px);}
  to{opacity:1; transform:scale(1); filter:blur(0);}
}
@keyframes cardDrawn{
  from{opacity:0; top:20px;}
  to{opacity:1; top:0;}
}
@keyframes flashRed{
  0%{box-shadow:inset 0 0 16px 4px rgba(224,85,85,0.9), 0 0 16px 4px rgba(224,85,85,0.7);}
  100%{box-shadow:none;}
}
@keyframes flashGreen{
  0%{box-shadow:inset 0 0 16px 4px rgba(68,255,136,0.9), 0 0 16px 4px rgba(68,255,136,0.7);}
  100%{box-shadow:none;}
}
@keyframes screenFlashRed{
  0%{box-shadow: inset 0 0 120px 30px rgba(224,85,85,0.55);}
  100%{box-shadow: inset 0 0 120px 30px rgba(224,85,85,0);}
}
@keyframes screenFlashGreen{
  0%{box-shadow: inset 0 0 120px 30px rgba(68,255,136,0.275);}
  100%{box-shadow: inset 0 0 120px 30px rgba(68,255,136,0);}
}
@keyframes zoneShakeImpact{
  0%{transform: translateY(0);}
  20%{transform: translateY(6px);}
  45%{transform: translateY(-2px);}
  70%{transform: translateY(2px);}
  100%{transform: translateY(0);}
}
@keyframes zoneShakeImpactUp{
  0%{transform: translateY(0);}
  20%{transform: translateY(-6px);}
  45%{transform: translateY(2px);}
  70%{transform: translateY(-2px);}
  100%{transform: translateY(0);}
}
@keyframes pulseNameBox{
  from{box-shadow:0 0 10px 3px #e05555, inset 0 0 4px #e05555aa;}
  to{box-shadow:0 0 26px 8px #ff2222, inset 0 0 8px #ff2222cc;}
}
@keyframes baseBlinkRed{
  from{opacity:0;}
  to{opacity:0.5;}
}
@keyframes cardFloatA{
  0%,100%{transform:translateY(0);}
  50%{transform:translateY(-3px);}
}
@keyframes cardFloatB{
  0%,100%{transform:translateY(0) translateX(0);}
  50%{transform:translateY(-4px) translateX(1px);}
}
@keyframes cardFloatC{
  0%,100%{transform:translateY(0);}
  50%{transform:translateY(-2.2px);}
}
@keyframes cardFloatD{
  0%,100%{transform:translateY(0) translateX(0);}
  50%{transform:translateY(-3.6px) translateX(-1px);}
}
@keyframes nebulaDrift{
  0%,100%{transform: translate(0, 0);}
  33%{transform: translate(30px, -15px);}
  66%{transform: translate(-20px, 10px);}
}
@keyframes starTwinkle{
  0%,100%{opacity: 0.1; transform: scale(1);}
  50%{opacity: 0.9; transform: scale(1.5);}
}
@keyframes textGlitch{
  0%{transform:translate(0); text-shadow:none;}
  15%{transform:translate(-2px,1px); text-shadow:2px 0 #ff0044,-2px 0 #00e5ff;}
  30%{transform:translate(2px,-1px); text-shadow:-2px 0 #ff0044,2px 0 #00e5ff;}
  45%{transform:translate(-1px,0); text-shadow:1px 0 #ff0044,-1px 0 #00e5ff;}
  60%{transform:translate(1px,1px); text-shadow:-1px 0 #ff0044,1px 0 #00e5ff;}
  100%{transform:translate(0); text-shadow:none;}
}
@keyframes zzzFloat{
  0%{opacity:0; transform:translateY(0) rotate(0deg);}
  20%{opacity:.95;}
  70%{opacity:.4;}
  100%{opacity:0; transform:translateY(-55%) rotate(12deg);}
}
.cap-no-ess{display:flex; flex-wrap:wrap; align-items:center; justify-content:center; gap:4px; font-family:'MEK',monospace; font-size:clamp(9px, calc(var(--card-w) * 0.15), 17px); color:#E8E0C8; background:rgba(8,8,16,0.85); border:1px solid rgba(232,224,200,0.35); border-radius:6px; box-shadow:0 2px 10px rgba(0,0,0,0.5); padding:6px 8px; white-space:normal; text-align:center; width:100%; box-sizing:border-box; pointer-events:none; text-shadow:0 0 8px #8A7D62;}
.cap-no-ess.cap-field-full{white-space:normal; text-align:center; line-height:1.15;}
.cap-no-ess-icon{width:1.1em; height:1.1em; object-fit:contain; vertical-align:middle; filter:drop-shadow(0 0 3px #8A7D62);}
.stat{display:flex;align-items:center;gap:4px;font-size:max(calc(4vh * 0.75), calc(2vw * 0.75));}
