/* lvl up × PTJ — интерфейс мини-приложения.
 *
 * Визуальный язык взят не с потолка, а с самих карт Системы: угловатые рамки
 * со срезанными углами, тонкая светящаяся линия, бирюзовое свечение, фиолетовый
 * только у UPGRADE. Поэтому здесь нигде нет border-radius — все панели режутся
 * clip-path, ровно как рамка карты.
 *
 * Тема одна и намеренно тёмная: окно Системы в манхве всегда полупрозрачный
 * тёмный голографический экран. Поэтому фон и все цвета заданы явно, и страница
 * держится на любом фоне Telegram, светлом или тёмном.
 */

:root {
  /* Земля. Сине-чёрный, а не серый: нейтраль уведена в сторону акцента. */
  --void:        #0B1219;
  --void-2:      #111B24;
  --panel:       #17242F;
  --panel-up:    #21323F;
  --line:        #4F7288;
  --line-soft:   #2C465A;

  /* Бирюза Системы — единственный акцент интерфейса. */
  --sys:         #4FE3D8;
  --sys-deep:    #35C2BB;
  --sys-glow:    rgba(79, 227, 216, 0.28);

  /* Смысловые цвета. Не акцент — они говорят о состоянии, а не о стиле. */
  --blood:       #FF4D4D;
  --gold:        #FFC65C;
  --good:        #5BD98A;
  --upgrade:     #B98CFF;

  --ink:         #EAF3F8;
  --ink-dim:     #A3B7C4;
  --ink-faint:   #7C93A2;

  /* Редкости карт — канонная лестница QUESTISM. */
  --bronze:      #C08551;
  --silver:      #B8C4CC;
  --r-gold:      #E8B54B;
  --platinum:    #7FD4E8;
  --diamond:     #6FA8FF;
  --master:      #C77DFF;
  --challenger:  #FF7A59;

  --font-display: "Segoe UI Variable Display", "SF Pro Display", system-ui,
                  -apple-system, Roboto, "Helvetica Neue", sans-serif;
  --font-body:    "Segoe UI Variable Text", "SF Pro Text", system-ui,
                  -apple-system, Roboto, "Helvetica Neue", sans-serif;
  --font-data:    ui-monospace, "SF Mono", "Cascadia Mono", "Segoe UI Mono",
                  "Roboto Mono", Consolas, monospace;

  --hud-h: 56px;
  --tabs-h: 56px;
  --gap: 12px;

  /* Зона выреза и полосы жеста. На вебвью без выреза оба нуля. */
  --safe-top: env(safe-area-inset-top, 0px);
  --safe-bottom: env(safe-area-inset-bottom, 0px);
  --safe-left: env(safe-area-inset-left, 0px);
  --safe-right: env(safe-area-inset-right, 0px);

  /* Срез угла у панелей. Одна величина на весь интерфейс — иначе рамки
     перестают читаться как один материал. */
  --cut: 10px;
  --cut-sm: 6px;
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
  height: 100%;
  overscroll-behavior: none;
}

body {
  background: var(--void);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: 15px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  -webkit-tap-highlight-color: transparent;
}

/* Сетка Системы на фоне: едва различима, но убирает ощущение пустого чёрного. */
body::before {
  content: "";
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 0;
  background:
    linear-gradient(var(--line-soft) 1px, transparent 1px) 0 0 / 100% 44px,
    radial-gradient(ellipse at 50% 0%, rgba(79, 227, 216, 0.07), transparent 60%);
  opacity: 0.5;
}

button, input { font: inherit; color: inherit; }
button { cursor: pointer; border: 0; background: none; }
:focus-visible { outline: 2px solid var(--sys); outline-offset: 2px; }

.data { font-family: var(--font-data); font-variant-numeric: tabular-nums; }
.muted { color: var(--ink-dim); }
.small { font-size: 13px; }
.error { color: var(--blood); font-size: 13px; }
.unit { color: var(--ink-faint); font-size: 12px; }

/* --------------------------------------------------------------------------
   Панель — базовый материал интерфейса
   -------------------------------------------------------------------------- */

.panel {
  position: relative;
  background: var(--panel);
  border: 1px solid var(--line);
  padding: 16px;
  clip-path: polygon(
    var(--cut) 0, 100% 0, 100% calc(100% - var(--cut)),
    calc(100% - var(--cut)) 100%, 0 100%, 0 var(--cut)
  );
}

/* Уголок-засечка сверху слева: та же деталь, что на рамке карты. */
.panel::after {
  content: "";
  position: absolute;
  top: 0; left: 0;
  width: 22px; height: 1px;
  background: var(--sys);
  opacity: 0.7;
  transform: rotate(-45deg);
  transform-origin: left top;
  translate: 1px calc(var(--cut) + 1px);
}

.sys-label {
  margin: 0 0 6px;
  font-family: var(--font-data);
  font-size: 10px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--sys);
  opacity: 0.85;
}

h1, h2, h3 {
  font-family: var(--font-display);
  font-weight: 800;
  letter-spacing: -0.01em;
  text-wrap: balance;
  margin: 0;
}
h1 { font-size: 24px; }
h2 { font-size: 18px; }
h3 { font-size: 15px; }

.section-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 8px;
  margin-bottom: 8px;
}

/* --------------------------------------------------------------------------
   Кнопки
   -------------------------------------------------------------------------- */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 11px 16px;
  background: var(--panel-up);
  border: 1px solid var(--line);
  color: var(--ink);
  font-weight: 600;
  font-size: 14px;
  min-height: 44px;
  transition: background 0.14s, border-color 0.14s, color 0.14s;
  clip-path: polygon(
    var(--cut-sm) 0, 100% 0, 100% calc(100% - var(--cut-sm)),
    calc(100% - var(--cut-sm)) 100%, 0 100%, 0 var(--cut-sm)
  );
}
.btn:hover:not(:disabled) { border-color: var(--sys-deep); background: #2A3F50; }
.btn:active:not(:disabled) { translate: 0 1px; }
.btn:disabled { opacity: 0.38; cursor: not-allowed; }
.btn-wide { width: 100%; }

.btn-primary {
  background: linear-gradient(180deg, #1a5f5e, #114542);
  border-color: var(--sys-deep);
  color: #EAFFFD;
  box-shadow: inset 0 1px 0 var(--sys-glow);
}
.btn-primary:hover:not(:disabled) { background: linear-gradient(180deg, #227673, #16524f); }

.btn-danger { border-color: #5D2027; color: #FFC9C9; background: #1E1013; }
.btn-danger:hover:not(:disabled) { border-color: var(--blood); background: #2A1418; }

.btn-ghost { background: transparent; }

/* Кнопка в строке заголовка: рядом с подписью раздела, поэтому мельче.
   Зону нажатия при этом не режем — она остаётся в 40 px по высоте. */
.btn-small {
  font-size: 12px;
  padding: 6px 10px;
  /* 44 — нижняя граница, ниже которой палец начинает промахиваться. */
  min-height: 44px;
  min-width: 44px;
}

.btn-row { display: flex; gap: 8px; flex-wrap: wrap; }
.btn-row > .btn { flex: 1 1 auto; }

/* --------------------------------------------------------------------------
   Скрытые слои
   --------------------------------------------------------------------------

   Все полноэкранные слои — загрузка, вход, рождение, бой, вскрытие карты,
   нижняя шторка — прячутся атрибутом `hidden`, и разметка на это полагается.

   Но `hidden` — это всего лишь `display: none` из таблицы стилей браузера, и
   любое авторское правило его перебивает. Ниже каждому слою задан `display:
   grid`, поэтому атрибут переставал работать вовсе: все шесть слоёв
   показывались разом, друг поверх друга. Сверху оказывался `.reveal` с почти
   непрозрачным чёрным фоном — и вся игра выглядела как чёрный экран, сквозь
   который еле проступает текст.

   Одна строка чинит всё: она возвращает `hidden` силу, которой ему не хватало.
   -------------------------------------------------------------------------- */

[hidden] { display: none !important; }

/* --------------------------------------------------------------------------
   Загрузка и вход
   -------------------------------------------------------------------------- */

.boot, .gate {
  position: fixed;
  inset: 0;
  z-index: 60;
  display: grid;
  place-items: center;
  background: var(--void);
  padding: 24px;
}

.boot-window { text-align: center; display: grid; gap: 16px; justify-items: center; }

/* Загрузка открывается лого Lookism War (26.09.2026). Фон — тот же, что у
   хаба, который идёт следом: и лого, и переход в хаб без шва. Края картинки
   растворены в ней самой, поэтому рамки не видно. */
.boot { background: #09090B; }
.boot-logo { width: min(86vw, 400px); height: auto; display: block; }
.boot-window .boot-track i { background: #B3121B; }

.boot-text {
  margin: 0;
  font-family: var(--font-data);
  font-size: 11px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--ink-dim);
}

.boot-track { width: 180px; height: 2px; background: var(--line); overflow: hidden; }
.boot-track i { display: block; height: 100%; width: 40%; background: var(--sys);
                animation: sweep 1.1s ease-in-out infinite; }
@keyframes sweep { from { transform: translateX(-100%); } to { transform: translateX(350%); } }

.gate-panel { max-width: 420px; display: grid; gap: 10px; }
.gate-title { font-size: 20px; }

/* --------------------------------------------------------------------------
   Рождение
   -------------------------------------------------------------------------- */

.screen { position: fixed; inset: 0; z-index: 50; overflow-y: auto;
          padding: 24px; display: grid; place-items: center; background: var(--void); }
.birth-panel { max-width: 440px; width: 100%; display: grid; gap: 14px; }
.birth-title { font-size: 26px; }

.field { display: grid; gap: 6px; }
.field-label {
  font-family: var(--font-data); font-size: 10px;
  letter-spacing: 0.18em; text-transform: uppercase; color: var(--ink-dim);
}
.field input {
  background: var(--void-2);
  border: 1px solid var(--line);
  padding: 12px 14px;
  color: var(--ink);
  font-size: 16px; /* 16px, иначе iOS зумит поле при фокусе */
}
.field input:focus { outline: none; border-color: var(--sys-deep); }

/* --------------------------------------------------------------------------
   HUD
   -------------------------------------------------------------------------- */

#app { position: relative; z-index: 1; min-height: 100%;
       padding-top: calc(var(--hud-h) + var(--safe-top));
       padding-bottom: calc(var(--tabs-h) + var(--safe-bottom)); }

.hud {
  position: fixed;
  top: 0; left: 0; right: 0;
  z-index: 20;
  height: calc(var(--hud-h) + var(--safe-top));
  padding-top: var(--safe-top);
  display: grid;
  grid-template-columns: auto auto 1fr auto;
  align-items: center;
  gap: 12px;
  padding-left: calc(14px + var(--safe-left));
  padding-right: calc(14px + var(--safe-right));
  background: rgba(10, 17, 24, 0.94);
  backdrop-filter: blur(12px);
  border-bottom: 1px solid var(--line);
}

/* --- Матовое стекло (20.09.2026) ---------------------------------------------
   Владелец спрашивал про «жидкое стекло». Полное — преломление и блики по
   краю — делается теми же фильтрами, что тормозят на Android, и не в языке
   игры (у нас тёмное стекло окна Системы, а не прозрачное стекло Apple).
   Поэтому матовое, и только там, где стекло по смыслу: шапка, вкладки и
   листы снизу. В полном режиме — просвечивают с размытием и бликом по
   верхней кромке; в лёгком — сплошной тёмный материал, размытия нет: оно
   пересчитывается на каждый кадр прокрутки и было главной ценой. */
html[data-fx="full"] .hud {
  background: rgba(10, 17, 24, 0.80);
  -webkit-backdrop-filter: blur(10px) saturate(140%);
          backdrop-filter: blur(10px) saturate(140%);
  box-shadow: inset 0 -1px 0 rgba(255, 255, 255, 0.04);
}
html[data-fx="full"] .tabs {
  background: rgba(10, 17, 24, 0.82);
  -webkit-backdrop-filter: blur(10px) saturate(140%);
          backdrop-filter: blur(10px) saturate(140%);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.05);
}
html[data-fx="full"] .sheet-body {
  background: rgba(23, 36, 47, 0.88);
  -webkit-backdrop-filter: blur(14px) saturate(140%);
          backdrop-filter: blur(14px) saturate(140%);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.06);
}
html[data-fx="lite"] .hud, html[data-fx="off"] .hud,
html[data-fx="lite"] .tabs, html[data-fx="off"] .tabs {
  background: rgba(10, 17, 24, 0.985);
  -webkit-backdrop-filter: none;
          backdrop-filter: none;
}

/* Стрелка в хаб. Нажимать есть куда — 32×40, а видимая галка прижата к краю:
   отрицательный отступ съедает поле шапки, и полосам место не уходит. */
.hud-back {
  width: 32px; height: 40px; margin: 0 -10px 0 -8px;
  display: grid; place-items: center;
  color: var(--sys); background: none; border: 0; padding: 0;
}
.hud-back .ic { width: 22px; height: 22px; stroke-width: 2.2; }
.hud-back:active { opacity: 0.6; }
.hud-id { display: flex; align-items: center; gap: 6px; min-width: 0; }
.hud-name {
  font-family: var(--font-display); font-weight: 800; font-size: 14px;
  text-transform: uppercase; letter-spacing: 0.04em;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 8ch;
}

.chip {
  font-family: var(--font-data); font-size: 10px;
  padding: 2px 6px; white-space: nowrap;
  border: 1px solid var(--line); color: var(--ink-dim);
}
.chip-level { color: var(--sys); border-color: var(--sys-deep); }
.chip-stage { color: var(--upgrade); border-color: #4A2E6E; }

/* Выбор количества на прилавке. Множитель один на всю лавку: на телефоне
   плитки мелкие, и степпер внутри каждой превратил бы прилавок в кашу. */
.chip-on { color: var(--gold); border-color: var(--gold); }
.buy-count { display: flex; flex-wrap: wrap; gap: 6px; align-items: center;
             margin: 8px 0 4px; }

/* Поиск по бойцам в Кодексе: при четырёхстах именах одна развёртка дела не
   решает — чтобы найти Ган Пака, надо знать, что он в Мёндоне. */
.team-field { width: 100%; box-sizing: border-box; margin: 4px 0 8px;
               padding: 9px 12px; border-radius: 10px;
               border: 1px solid var(--line); background: var(--panel);
               color: var(--text); font: inherit; }
.fold-body { padding-top: 6px; }
.team-row { padding: 5px 0; border-bottom: 1px solid var(--line); }
.team-row:last-of-type { border-bottom: 0; }

.codex-find { width: 100%; box-sizing: border-box; margin: 6px 0 10px;
              padding: 9px 12px; border-radius: 10px;
              border: 1px solid var(--line); background: var(--panel);
              color: var(--text); font: inherit; }

/* Связи происхождения. Цвет тут несёт смысл, а не украшает: свои и чужие
   должны читаться с одного взгляда, потому что от них зависят цена дороги
   и ставка за смену. */
.chips { display: flex; flex-wrap: wrap; gap: 4px; }
.chip-ally { color: var(--good); border-color: #2F6B45; }
.chip-foe { color: var(--blood); border-color: #7A2E2E; }

/* Полосам — не меньше 120 px: иначе длинное имя и большие деньги сжимали
   их до полоски, на которой цифра закрывала заполнение целиком. */
.hud-meters { display: grid; gap: 4px; min-width: 120px; }

/* Узкий телефон: имя короче, «вон» прячется, поля ýже — полосы остаются. */
@media (max-width: 400px) {
  .hud { gap: 8px; padding-left: calc(10px + var(--safe-left)); padding-right: calc(10px + var(--safe-right)); }
  .hud-name { max-width: 7ch; font-size: 13px; }
  .hud-money .unit { display: none; }
  .hud-money .data { font-size: 13px; }
}
@media (max-width: 360px) {
  .hud-name { max-width: 6ch; }
  .hud-id .chip-stage { display: none; }
}
/* Смертельная рана на узком экране встаёт на место уровня: имя иначе
   сжималось до нуля (замер 04.10.2026: 375 px — 0 px на имя). Уровень
   виден во вкладке «Игрок», а таймер раны — только здесь. */
@media (max-width: 440px) {
  .hud-id.has-wound .chip-level { display: none; }
  .chip-wound { padding-left: 5px; padding-right: 5px; }
}

.meter {
  position: relative;
  height: 9px;
  background: var(--void-2);
  border: 1px solid var(--line);
  overflow: hidden;
}
.meter i {
  position: absolute; inset: 0 auto 0 0; display: block;
  /* Полоса всегда во всю ширину, а её длину задаёт scaleX.
     Ширина — свойство вёрстки: анимируя её, браузер пересчитывает
     раскладку каждый кадр, и на телефоне это видно как рывки. */
  width: 100%; transform-origin: left center;
}
.meter-fill {
  transition: transform 0.35s cubic-bezier(0.2, 0.8, 0.3, 1);
  will-change: transform;
}
/* Призрачная полоса догоняет медленнее — так видно, сколько только что сняли. */
.meter-ghost {
  background: rgba(255, 77, 77, 0.35);
  transition: transform 0.8s ease-out 0.2s;
}
.meter-hp .meter-fill { background: linear-gradient(90deg, #FF6B6B, #FF3B3B); }
.meter-sta { height: 5px; }
.meter-sta .meter-fill { background: linear-gradient(90deg, var(--sys-deep), var(--sys)); }
.meter-text {
  position: absolute; right: 5px; top: 50%; translate: 0 -50%;
  font-family: var(--font-data); font-size: 8px; color: var(--ink);
  /* Строчный блок ровно по кеглю. Без этого он около десяти пикселей, а
     полоса — девять с обрезкой, и цифры подрезаны сверху и снизу. В бою
     этого не видно (там полоса 13 px), у босса было видно всегда. */
  line-height: 1;
  text-shadow: 0 0 4px #000; letter-spacing: 0.02em;
}
/* Полоса, внутри которой стоят цифры. Девяти пикселей им мало при любом
   line-height: остаются рамка сверху и снизу. Отдельный класс, а не
   :has(.meter-text), — вебвью Телеграма бывает старым. */
.meter-labeled { height: 13px; }
.meter-sta .meter-text { display: none; }

.hud-side { display: flex; align-items: center; gap: 7px; }
.hud-side .data { color: var(--gold); font-size: 13px; }

.care-row { display: grid; grid-template-columns: 1fr 1fr; gap: 4px; }
.meter-food, .meter-spirit { height: 4px; }
.meter-food .meter-fill { background: var(--gold); }
.meter-spirit .meter-fill { background: var(--upgrade); }

.care-btn {
  /* Нажимается 44 px, а видно 28. Расширить зону через ::after нельзя:
     clip-path на кнопке режет и псевдоэлемент вместе с ней. Поэтому сама
     кнопка прозрачная и большая, а плашку со срезанным углом рисует ::before.
     Отрицательные поля не дают ей растолкать шапку. */
  position: relative;
  width: 44px; height: 44px;
  margin: -8px;
  display: grid; place-items: center;
  border: 0;
  background: none;
  color: var(--ink);
}
.care-btn::before {
  content: "";
  position: absolute;
  width: 28px; height: 28px;
  border: 1px solid var(--line);
  background: var(--panel-up);
  clip-path: polygon(4px 0, 100% 0, 100% calc(100% - 4px), calc(100% - 4px) 100%, 0 100%, 0 4px);
  transition: border-color 0.14s;
}
.care-btn .ic { position: relative; }
.care-btn:hover::before { border-color: var(--sys-deep); }

/* --------------------------------------------------------------------------
   Карта города
   -------------------------------------------------------------------------- */

.map { overflow-x: auto; }

.map-plate {
  position: relative;
  width: 100%;
  aspect-ratio: 720 / 760;   /* совпадает с viewBox рисованной карты */
  min-width: 280px;
  background-color: var(--void-2);
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
  border: 1px solid var(--line);
  clip-path: polygon(
    var(--cut) 0, 100% 0, 100% calc(100% - var(--cut)),
    calc(100% - var(--cut)) 100%, 0 100%, 0 var(--cut)
  );
}

/* Хан делит город надвое ровно посередине — как в каноне. */
.map-river {
  position: absolute;
  left: 0; right: 0; bottom: 50%;
  height: 12px;
  translate: 0 50%;
  background: linear-gradient(180deg, transparent, rgba(79, 227, 216, 0.16), transparent);
  border-top: 1px solid rgba(79, 227, 216, 0.28);
  border-bottom: 1px solid rgba(79, 227, 216, 0.28);
}

.map-label {
  position: absolute;
  left: 8px;
  font-family: var(--font-data);
  font-size: 9px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ink-faint);
}
.map-north { top: 6px; }
.map-south { bottom: 6px; }

.pin {
  position: absolute;
  translate: -50% 50%;
  display: grid;
  justify-items: center;
  place-items: center;
  z-index: 1;
  /* Кнопка сама по себе размера не имеет — её держит .pin-hit. */
  width: 0; height: 0;
  padding: 0;
  border: 0;
  background: none;
}
.pin:hover, .pin:focus-visible { z-index: 3; }
.pin.is-picked { z-index: 4; }

/* Зона нажатия. Видно ромб в 15 px, а палец в него не попадает: 44 px —
   тот минимум, ниже которого начинают мазать. Поэтому зона отдельно от
   рисунка, невидимая и заметно больше него. */
.pin-hit {
  position: absolute;
  width: 44px; height: 44px;
  left: 50%; top: 50%;
  translate: -50% -50%;
  border-radius: 50%;
}
.pin:active .pin-hit { background: var(--sys-glow); }

.pin-dot {
  width: 15px; height: 15px;
  /* Цвет точки — опасность района: от спокойного бирюзового к красному. */
  background: color-mix(in oklab, var(--sys) calc(100% - var(--danger) * 10%), var(--blood));
  border: 1px solid var(--void);
  clip-path: polygon(50% 0, 100% 50%, 50% 100%, 0 50%);
  transition: scale 0.14s;
}
.pin:hover .pin-dot { scale: 1.35; }

/* Название и цена дороги живут на самой точке, а не на подложке: подложка —
   картинка, её не потрогаешь, а игроку нужно видеть, куда он может дойти.

   Но показывать все восемнадцать разом нельзя: районы стоят по канонной
   географии, у реки их несколько в одном углу, и подписи слипаются в кашу.
   Поэтому подпись видна только у отмеченных — где стоишь и что выбрал. */
.pin-label {
  position: absolute;
  top: 14px;
  display: grid;
  grid-auto-flow: row;
  justify-items: center;
  gap: 2px;
  pointer-events: none;
  white-space: nowrap;
}
/* Подпись есть у всех; лишние, что налезают на важные, прячет
   declutterLabels() в app.js. Цена дороги — только у выбранного. */
.pin.lbl-off .pin-label { display: none; }
/* Если под точкой занято, подпись встаёт сверху, справа или слева. */
.pin.lbl-up .pin-label { top: auto; bottom: 12px; }
.pin.lbl-right .pin-label { top: -8px; left: 12px; justify-items: start; }
.pin.lbl-left .pin-label { top: -8px; right: 12px; left: auto; justify-items: end; }
.pin .pin-cost { display: none; }
.pin.is-picked .pin-cost { display: inline-flex; align-items: center; gap: 2px; }
/* Город масштабируется, а точки с подписями — нет: --inv = 1 / масштаб. */
.pin { scale: var(--inv, 1); }

.pin-name {
  font-size: 10.5px;
  font-weight: 600;
  line-height: 1.1;
  color: var(--ink);
  white-space: nowrap;
  text-shadow: 0 0 4px var(--void), 0 0 8px var(--void), 0 1px 2px var(--void);
}

/* Цена дороги — плашка, а не шёпот. Раньше здесь был текст 8px тёмным по
   тёмному, и энергия на карте просто не читалась. */
.pin-cost {
  font-size: 11px;
  font-weight: 700;
  line-height: 1;
  padding: 2px 6px;
  color: var(--sys);
  background: rgba(11, 18, 25, 0.9);
  border: 1px solid var(--sys-deep);
  white-space: nowrap;
}
.pin.is-far .pin-cost {
  color: var(--blood);
  border-color: var(--blood);
}
.pin.is-here .pin-name { color: var(--sys); }

.pin.is-here { z-index: 2; }
.pin.is-here .pin-dot {
  background: var(--sys);
  box-shadow: 0 0 0 3px rgba(79, 227, 216, 0.22);
  scale: 1.3;
}
.pin.is-here .pin-name { color: var(--sys); font-weight: 700; }
.pin.is-far { opacity: 0.55; }
.pin.is-far .pin-name { color: var(--ink-faint); }
/* Чей район — ромб цветом держателя под ромбом опасности. Обводкой его не
   сделать: точка обрезана clip-path, и обводка обрезалась бы вместе с ней. */
.pin::before {
  content: '';
  position: absolute;
  left: 50%; top: 50%;
  width: 23px; height: 23px;
  translate: -50% -50%;
  background: var(--tint, transparent);
  clip-path: polygon(50% 0, 100% 50%, 50% 100%, 0 50%);
  pointer-events: none;
}
/* Ромб опасности — точно по центру точки, поверх ромба держателя. В сетке
   нулевого размера он съезжал вправо-вниз. */
.pin-dot {
  position: absolute;
  left: 50%; top: 50%;
  translate: -50% -50%;
}
.pin.is-new .pin-name { font-style: italic; }

/* Выбранный район: кольцо вокруг ромба, чтобы взгляд не терял его, пока
   читаешь карточку внизу. */
.pin.is-picked .pin-dot {
  scale: 1.5;
  box-shadow: 0 0 0 4px var(--sys-glow), 0 0 12px var(--sys);
}
.pin.is-picked { opacity: 1; }

/* --------------------------------------------------------------------------
   Карта во весь экран
   --------------------------------------------------------------------------

   Маленькая карта во вкладке хороша, чтобы понять, где ты. Но выбирать по ней
   дорогу неудобно: районы мельче пальца. Поэтому есть вторая, во весь экран —
   там же живут подробности района и кнопка поездки.
   -------------------------------------------------------------------------- */

/* Вкладка карты забирает всё, что осталось между HUD и полосой вкладок.
   Своей прокрутки у неё нет: карту таскают, а не листают. */
.view-flush {
  padding: 0;
  overflow: hidden;
}

/* Карта сверху, под ней карточка района и список. Раньше карта забирала
   экран целиком, а под ней оставалась пустота с одной подсказкой; теперь
   вкладка листается, и всё, что нужно для выбора дороги, — на одном экране. */
.mapview {
  height: 100%;
  display: grid;
  grid-auto-rows: max-content;
  gap: 8px;
  padding: 8px 10px calc(12px + env(safe-area-inset-bottom));
  overflow-y: auto;
  overscroll-behavior: contain;
}

.map-legend {
  display: flex;
  flex-wrap: wrap;
  gap: 4px 10px;
  font-size: 10.5px;
  color: var(--ink-dim);
}
.map-legend .lg { display: inline-flex; align-items: center; gap: 5px; }
.map-legend i {
  display: inline-block;
  width: 10px; height: 10px;
  clip-path: polygon(50% 0, 100% 50%, 50% 100%, 0 50%);
}
.map-legend .lg-here { background: var(--sys); }
.map-legend .lg-danger {
  width: 26px;
  clip-path: none;
  background: linear-gradient(90deg, var(--sys), var(--blood));
}
.map-legend .lg-far { background: var(--ink-faint); opacity: 0.6; }
.map-legend .lg-new { font-style: italic; }

/* Пунктир маршрута: от тебя до выбранного района. */
.map-route {
  position: absolute;
  inset: 0;
  width: 100%; height: 100%;
  pointer-events: none;
  overflow: visible;
}
.map-route line {
  stroke: var(--sys);
  stroke-width: 2;
  stroke-dasharray: 6 5;
  vector-effect: non-scaling-stroke;
  opacity: 0.85;
}
.map-route.is-far line { stroke: var(--blood); }

/* Список районов под картой. */
.maplist { display: grid; gap: 6px; }
.maplist .section-head { align-items: center; flex-wrap: wrap; gap: 6px; }
.maplist .chips { display: flex; gap: 6px; flex-wrap: wrap; }
.maplist .chip { cursor: pointer; }
.maplist .chip.is-on { color: var(--void); background: var(--sys); border-color: var(--sys); }
.maprow {
  display: grid;
  grid-template-columns: 4px 1fr auto;
  gap: 10px;
  align-items: center;
  width: 100%;
  padding: 8px 10px 8px 0;
  text-align: left;
  color: var(--ink);
  background: var(--panel);
  border: 1px solid var(--line-soft);
  font: inherit;
  cursor: pointer;
}
.maprow.is-picked { border-color: var(--sys); box-shadow: 0 0 0 1px var(--sys-glow) inset; }
.maprow.is-far { opacity: 0.6; }
.maprow-tint { align-self: stretch; background: var(--tint); }
.maprow-main { display: grid; gap: 2px; min-width: 0; }
.maprow-name { font-weight: 600; }
.maprow-name.is-new { font-style: italic; }
.maprow-side { display: grid; justify-items: end; gap: 4px; }
.maprow-side .danger i { width: 4px; }

/* Карта занимает всё свободное место, сохраняя пропорции рисунка: иначе
   река перестала бы делить город пополам, а точки разъехались бы с пятнами
   районов на подложке. */
.mapview-plate {
  position: relative;
  justify-self: center;
  align-self: center;
  /* Не выше 58 % экрана: ниже должно остаться место под карточку района
     и начало списка. Ширина считается от высоты, чтобы пропорции рисунка
     (720×760) не ломались. */
  width: min(100%, calc(58vh * 720 / 760));
  aspect-ratio: 720 / 760;
  background-color: var(--void-2);
  border: 1px solid var(--line);
  overflow: hidden;
  touch-action: none;   /* щипок и перетаскивание наши, не браузера */
  clip-path: polygon(
    var(--cut) 0, 100% 0, 100% calc(100% - var(--cut)),
    calc(100% - var(--cut)) 100%, 0 100%, 0 var(--cut)
  );
}
.mapview-plate.is-zoomed { cursor: grab; }
.mapview-plate.is-zoomed:active { cursor: grabbing; }

/* Точки едут вместе с подложкой: масштабируется этот слой, а фон плитки —
   через background-size, поэтому они всегда совпадают. */
.bigmap-inner {
  position: absolute;
  inset: 0;
  transform-origin: 50% 50%;
  will-change: transform;
  background-size: 100% 100%;
  background-position: center;
  background-repeat: no-repeat;
}

/* Кнопки масштаба лежат на самой карте, а не отдельной строкой: на телефоне
   каждая строка отнимает высоту у того, ради чего сюда пришли. */
.mapview-zoom {
  position: absolute;
  right: 8px; bottom: 8px;
  z-index: 2;
  display: grid;
  justify-items: center;
  gap: 4px;
}
.mapview-zoom .btn {
  min-width: 36px;
  min-height: 36px;
  padding: 0;
  font-size: 20px;
  line-height: 1;
  background: rgba(11, 18, 25, 0.82);
  backdrop-filter: blur(6px);
}
.bigmap-zoom-value {
  min-width: 36px;
  text-align: center;
  font-size: 11px;
  color: var(--sys);
  background: rgba(11, 18, 25, 0.82);
  padding: 2px 0;
}

.bigmap-card {
  display: grid;
  gap: 8px;
  padding: 12px;
  background: var(--panel);
  border: 1px solid var(--line-soft);
  clip-path: polygon(
    var(--cut) 0, 100% 0, 100% calc(100% - var(--cut)),
    calc(100% - var(--cut)) 100%, 0 100%, 0 var(--cut)
  );
  animation: card-slide 0.18s ease-out;
}
.bigmap-card h2 { font-size: 19px; margin: 0; }
.bigmap-hint { background: transparent; border-style: dashed; }

.bigmap-facts {
  display: flex;
  flex-wrap: wrap;
  gap: 6px 12px;
  align-items: center;
}
.bigmap-facts .danger { color: var(--blood); }

@keyframes card-slide { from { opacity: 0; translate: 0 10px; } }

/* --------------------------------------------------------------------------
   Вкладки
   -------------------------------------------------------------------------- */

.tabs {
  position: fixed;
  bottom: 0; left: 0; right: 0;
  z-index: 20;
  height: calc(var(--tabs-h) + var(--safe-bottom));
  padding-bottom: var(--safe-bottom);
  display: grid;
  /* Каждая механика — своя вкладка, и их стало восемь. На 375 px это по 47 px
     на вкладку: подпись перестаёт читаться. Поэтому полоса прокручивается вбок,
     а вкладка держит свою ширину. */
  grid-template-columns: none;
  grid-auto-flow: column;
  grid-auto-columns: minmax(58px, 1fr);
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scrollbar-width: none;
  background: rgba(10, 17, 24, 0.96);
  backdrop-filter: blur(12px);
  border-top: 1px solid var(--line);
  padding-bottom: env(safe-area-inset-bottom);
}

.tabs::-webkit-scrollbar { display: none; }

.tab {
  display: grid;
  scroll-snap-align: center;
  place-items: center;
  gap: 5px;
  font-family: var(--font-data);
  font-size: 10px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-faint);
  transition: color 0.16s;
}
.tab-mark {
  width: 7px; height: 7px;
  border: 1px solid currentColor;
  clip-path: polygon(50% 0, 100% 50%, 50% 100%, 0 50%);
  transition: background 0.16s;
}
.tab-icon {
  display: flex;
  line-height: 1;
  opacity: 0.6;
  transition: opacity 0.16s, scale 0.16s;
}
.tab-icon .ic { width: 21px; height: 21px; }
.tab-text { font-size: 9px; letter-spacing: 0.06em; }
.tab.is-active { color: var(--sys); }
.tab.is-active .tab-mark { background: var(--sys); }
.tab.is-active .tab-icon { opacity: 1; scale: 1.1; }
.tab:active .tab-icon { scale: 0.92; }

/* --------------------------------------------------------------------------
   Содержимое
   -------------------------------------------------------------------------- */

.view { padding: var(--gap); display: grid; gap: var(--gap); max-width: 640px; margin: 0 auto; }

/* Панель района с артом на фоне. Арты районов уже нарисованы и лежат в
   assets — до сих пор их видел только чат. Текст поверх держится за счёт
   тёмной заливки: без неё описание тонет в картинке. */
.panel-hero {
  background-image:
    linear-gradient(180deg, rgba(23, 36, 47, 0.82), rgba(23, 36, 47, 0.97)),
    var(--hero);
  background-size: cover;
  background-position: center 30%;
}

.stack { display: grid; gap: 10px; }
.row { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
/* Кнопка в строке с текстом не сжимается и не выезжает за панель: текст
   уступает ей место. На 360 px «выключен» в настройках уезжал за край. */
.row > .btn { flex: 0 0 auto; }
.row > div { min-width: 0; }

/* Статы */
.stats { display: grid; gap: 1px; background: var(--line-soft); }
.stat {
  display: grid;
  grid-template-columns: 1fr auto auto;
  align-items: center;
  gap: 10px;
  padding: 9px 12px;
  background: var(--panel);
}
.stat-name { font-size: 14px; }
.stat-num { font-family: var(--font-data); font-size: 12px; color: var(--ink-faint); }

/* Грейд — фирменный знак Системы: шестиугольный жетон с моношрифтом. */
.grade {
  display: inline-grid;
  place-items: center;
  min-width: 46px;
  padding: 3px 8px;
  font-family: var(--font-data);
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.04em;
  color: var(--sys);
  background: rgba(79, 227, 216, 0.09);
  border: 1px solid var(--sys-deep);
  clip-path: polygon(6px 0, calc(100% - 6px) 0, 100% 50%,
                     calc(100% - 6px) 100%, 6px 100%, 0 50%);
}
.grade-high { color: var(--upgrade); border-color: #4A2E6E; background: rgba(185, 140, 255, 0.1); }

.plus {
  width: 30px; height: 30px;
  display: grid; place-items: center;
  border: 1px solid var(--sys-deep);
  color: var(--sys);
  font-size: 16px; font-weight: 700;
  background: rgba(79, 227, 216, 0.06);
}
.plus:disabled { opacity: 0.25; }

/* Полоска опыта */
.xp { height: 3px; background: var(--void-2); border: 1px solid var(--line); }
.xp i {
  display: block; height: 100%; width: 100%;
  background: var(--sys-deep);
  transform-origin: left center;
  transition: transform 0.35s ease-out;
}

/* Список-плитка */
.tiles { display: grid; gap: 8px; }
.tile {
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  gap: 12px;
  padding: 12px;
  background: var(--panel);
  border: 1px solid var(--line);
  text-align: left;
  transition: border-color 0.14s, background 0.14s;
  clip-path: polygon(
    var(--cut-sm) 0, 100% 0, 100% calc(100% - var(--cut-sm)),
    calc(100% - var(--cut-sm)) 100%, 0 100%, 0 var(--cut-sm)
  );
}
.tile:hover { border-color: var(--sys-deep); }
.tile.is-here { border-color: var(--sys-deep); background: var(--panel-up); }
.tile.is-locked { opacity: 0.45; }
.tile-title { font-weight: 600; }
.tile-sub { font-size: 12px; color: var(--ink-dim); }

/* Портреты бойцов: рисуются сервером, поэтому размер задаём здесь. */
.face-chip {
  width: 34px;
  height: 40px;
  object-fit: cover;
  border: 1px solid var(--line);
  background: var(--void-2);
  display: block;
}
.tile:hover .face-chip { border-color: var(--sys-deep); }
.is-locked .face-chip { filter: grayscale(1) brightness(0.4); }

.face-big {
  width: min(46vw, 168px);
  justify-self: center;
  display: block;
  border: 1px solid var(--line);
  background: var(--void-2);
}

/* Шкала опасности: угловые засечки, а не точки — тот же язык, что у панелей. */
.danger { display: flex; gap: 2px; align-items: center; }
.danger i {
  width: 4px; height: 12px;
  background: var(--line);
  clip-path: polygon(0 0, 100% 0, 100% 80%, 50% 100%, 0 80%);
}
.danger i.on { background: var(--blood); }

/* Карты */
.card-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(96px, 1fr)); gap: 8px; }
.card-cell {
  position: relative;
  aspect-ratio: 3 / 4.2;
  background: var(--void-2);
  border: 1px solid var(--line);
  overflow: hidden;
  padding: 0;
  transition: border-color 0.14s, translate 0.14s;
}
.card-cell:hover { translate: 0 -2px; border-color: var(--sys-deep); }
.card-cell img { width: 100%; height: 100%; object-fit: cover; display: block; }
.card-cell.is-locked img { filter: grayscale(1) brightness(0.28); }
.card-cell .count {
  position: absolute; right: 3px; bottom: 3px;
  font-family: var(--font-data); font-size: 10px;
  padding: 1px 4px; background: rgba(7, 11, 16, 0.86); color: var(--ink);
}
.card-cell .rarity-bar { position: absolute; inset: auto 0 0 0; height: 2px; }

.r-bronze { background: var(--bronze); }
.r-silver { background: var(--silver); }
.r-gold { background: var(--r-gold); }
.r-platinum { background: var(--platinum); }
.r-diamond { background: var(--diamond); }
.r-master { background: var(--master); }
.r-challenger { background: var(--challenger); }

.rarity-name { font-family: var(--font-data); font-size: 10px;
               letter-spacing: 0.18em; text-transform: uppercase; }

/* Уровень подписки в шапке. Золотом, как деньги: это про них, и путать его
   со ступенью потенциала нельзя. */
.chip-sub {
  color: var(--gold);
  border-color: color-mix(in srgb, var(--gold) 45%, transparent);
  background: color-mix(in srgb, var(--gold) 12%, transparent);
}

/* Смертельная рана в шапке. Кровью и с пульсом: это не украшение, а «лечи
   сегодня». Пульс выключается для тех, кто просил меньше движения. */
.chip-wound {
  color: var(--blood);
  border-color: color-mix(in srgb, var(--blood) 55%, transparent);
  background: color-mix(in srgb, var(--blood) 14%, transparent);
  cursor: pointer;
  font: inherit;
  animation: wound-pulse 1.6s ease-in-out infinite;
  white-space: nowrap;
  flex-shrink: 0;
}
@keyframes wound-pulse { 50% { opacity: 0.55; } }
@media (prefers-reduced-motion: reduce) { .chip-wound { animation: none; } }

/* --------------------------------------------------------------------------
   Разговор
   --------------------------------------------------------------------------

   Тот же приём, что и у боя: отдельный слой поверх всего. Разговор, который
   можно смахнуть пальцем, перестаёт быть выбором — поэтому он занимает
   экран целиком и уходит только ответом.

   Реплика прижата книзу, к кнопкам: на телефоне палец там, и читать сверху
   вниз до ответа удобнее, чем возвращаться взглядом.
   -------------------------------------------------------------------------- */

.scene {
  position: fixed;
  inset: 0;
  z-index: 45;
  background: var(--void);
  display: grid;
  align-items: end;
  overflow-y: auto;
}

.scene-body {
  display: grid;
  gap: 14px;
  padding: 20px 16px calc(20px + env(safe-area-inset-bottom));
}

.scene-who { display: flex; align-items: center; gap: 10px; }

.scene-face {
  width: 54px; height: 54px;
  border-radius: 10px;
  border: 1px solid var(--line);
  background: var(--panel);
}

.scene-name {
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 15px;
  letter-spacing: .02em;
}

.scene-text {
  margin: 0;
  font-size: 16px;
  line-height: 1.5;
  border-left: 2px solid var(--line);
  padding-left: 12px;
}

/* Реплика Системы — окном Системы, а не человеческой речью. */
.scene-sys {
  font-family: var(--font-mono, monospace);
  color: var(--sys);
  border-left-color: var(--sys);
  font-size: 14px;
}

.scene-choices { display: grid; gap: 8px; }

.scene-choice {
  text-align: left;
  white-space: normal;
  line-height: 1.4;
  padding: 12px 14px;
}

/* --------------------------------------------------------------------------
   Бой
   -------------------------------------------------------------------------- */

.fight {
  position: fixed;
  inset: 0;
  z-index: 40;
  background: var(--void);
  /* Колонка, а не сетка «auto 1fr auto»: у сетки 1fr доставался сцене с
     портретами, а лог рос как хотел — и на 390×700 сцена сжималась до 16 px,
     бойцов не было вовсе. Теперь сцена держит свою высоту, а лишнее место
     берёт лог, и он же прокручивается. Строка модификатора и подсказка —
     необязательные дети, колонка их переживает без переписывания рядов. */
  display: flex;
  flex-direction: column;
  overflow: hidden;
}
.fight > * { flex: 0 0 auto; }
/* Лишнее место делят сцена и лог: сцена — до 240 px, чтобы портреты были
   крупными на высоком экране, остальное — логу, и он же прокручивается. */
.fight > .fight-scene { flex: 1 3 110px; min-height: 0; max-height: 240px; }
.fight > .fight-log { flex: 2 1 90px; min-height: 66px; }

/* Шапка боя плотная: на 360×640 после неё должны уместиться сцена, лог и
   два ряда кнопок. Каждый лишний пиксель здесь отнимается у лога. */
.fight-top { padding: 8px 14px 6px; display: grid; gap: 6px;
             border-bottom: 1px solid var(--line); }

.combatant { display: grid; gap: 3px; position: relative; }
.combatant-head { display: flex; align-items: center; gap: 7px; flex-wrap: wrap; }
.combatant-name {
  font-family: var(--font-display); font-weight: 800; font-size: 14px;
  text-transform: uppercase; letter-spacing: 0.05em;
}
.combatant .meter { height: 13px; }
.combatant .meter-text { font-size: 9px; }
.combatant-tags { display: flex; gap: 5px; }

.tag {
  font-family: var(--font-data); font-size: 9px;
  letter-spacing: 0.1em; text-transform: uppercase;
  padding: 1px 5px; border: 1px solid currentColor;
}
.tag-awake { color: var(--gold); }
.tag-ui { color: var(--upgrade); }
.tag-open { color: var(--blood); }
.tag-path { color: var(--sys); }

/* Кампания: полоса глав и список шагов. */
.strip { display: flex; gap: 4px; }
.strip-seg { display: block; flex: 1; height: 6px; background: var(--line-soft); }
.strip-seg.is-done { background: var(--sys); }
.strip-seg.is-now { background: var(--gold); }
.steps { display: flex; flex-direction: column; gap: 6px; }
.step { display: flex; align-items: center; gap: 10px; padding: 6px 10px;
        background: var(--void-2); border: 1px solid var(--line-soft); }
.step-now { border-color: var(--sys); }
.step-done { opacity: 0.6; }
.step-later { opacity: 0.55; }
.step-mark { width: 18px; height: 18px; flex: none; display: grid; place-items: center; }
.step-text { flex: 1; min-width: 0; }
.panel-sys { border-color: var(--sys-deep); box-shadow: inset 0 0 0 1px var(--sys-glow); }

/* Место с объектами: сетка в две колонки, лица тех, кто тут стоит. */
.tiles-2 { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; }
.tile.obj { min-height: 96px; align-items: flex-start; grid-template-columns: auto 1fr auto; }
.tile.obj .tile-sub { display: block; }
.tile.obj.is-now { border-color: var(--sys); box-shadow: 0 0 0 1px var(--sys-glow), 0 0 18px var(--sys-glow); }
.tile.obj.is-locked { opacity: 0.45; }
.face-row { display: flex; gap: 4px; margin-top: 6px; }
.face-chip { width: 26px; height: 26px; border: 1px solid var(--line-soft); background: var(--void); object-fit: cover; }
.face-chip.is-beaten { opacity: 0.45; }
.person .face-chip { width: 40px; height: 40px; }
.person.is-beaten { opacity: 0.7; }
.obj-badge { display: inline-flex; align-items: center; gap: 2px; }

.round {
  font-family: var(--font-data); font-size: 10px;
  letter-spacing: 0.22em; text-transform: uppercase; color: var(--ink-faint);
  text-align: center;
}

.fight-log {
  overflow-y: auto;
  padding: 12px 14px;
  display: flex;
  flex-direction: column;
  /* К низу прижимает margin-top: auto у первой строки, а не justify-content:
     flex-end — с ним переполненный лог уезжал вверх за край, и первые
     строки раунда нельзя было прокрутить. */
  gap: 7px;
  font-size: 14px;
  line-height: 1.45;
}
.fight-log p { margin: 0; animation: line-in 0.24s ease-out both; }
.fight-log > :first-child { margin-top: auto; }
@keyframes line-in { from { opacity: 0; translate: 0 6px; } }
.fight-log b { color: var(--blood); font-family: var(--font-data); }
.fight-log i { color: var(--ink-dim); }

.hint {
  padding: 5px 14px;
  font-size: 12px; font-style: italic; color: var(--ink-dim);
  border-top: 1px solid var(--line-soft);
}

.fight-actions { padding: 8px 12px calc(8px + env(safe-area-inset-bottom));
                 display: flex; flex-direction: column; gap: 6px;
                 border-top: 1px solid var(--line); }
/* Кнопки боя ужимаются, а не вылезают за экран: у .fight overflow hidden,
   и раскрытая рука выталкивала «Статы» и «Сбежать» за нижний край — ни
   листнуть, ни сбежать (владелец 28.09.2026). Теперь ужимается только рука
   карт и листается сама, а Удар/Блок/Уклон и нижний ряд видны всегда. */
.fight > .fight-actions { flex: 0 1 auto; min-height: 0; }
.fight-actions > * { flex: 0 0 auto; }
.fight-actions > .card-row.card-wrap {
  flex: 0 1 auto; min-height: 46px; max-height: 38vh;
  overflow-y: auto; overscroll-behavior: contain; touch-action: pan-y;
  align-content: flex-start;
}
.fight-actions .btn-row > .btn { flex: 1 1 0; padding: 8px 6px; }

/* Колода в бою прокручивается вбок: карт бывает много, а экран один. */
.card-row {
  display: flex;
  gap: 7px;
  overflow-x: auto;
  padding-bottom: 3px;
  scrollbar-width: none;
}
.card-row::-webkit-scrollbar { display: none; }
/* Рука в бою переносится строками: листать вбок каждый раунд — это то,
   на что жаловались игроки. */
.card-row.card-wrap { flex-wrap: wrap; overflow-x: visible; }
.card-row.card-wrap > .btn { flex: 1 1 auto; }
.fight-kinds { margin: 6px 0 4px; }
/* Ход ушёл на сервер: кнопки гаснут, пока не придёт ответ. */
.fight-actions.is-busy { opacity: 0.55; pointer-events: none; transition: opacity 0.15s; }
.fight-kinds .chip { padding: 6px 10px; font-size: 11px; }

/* Колода: поиск, порядок и компактный список. */
.deck-tools { display: grid; gap: 8px; margin: 0 0 8px; }
.deck-tools .rarity-row { margin: 0; }
.deck-tools .chip { padding: 6px 10px; font-size: 11px; }
.card-list { display: grid; gap: 4px; }
.card-line {
  display: flex; align-items: center; gap: 10px; width: 100%; text-align: left;
  background: var(--void-2); border: 1px solid var(--line); color: var(--ink);
  padding: 8px 10px; min-height: 44px; cursor: pointer; font: inherit;
}
.card-line:hover { border-color: var(--sys-deep); }
.card-line-text { display: grid; gap: 2px; min-width: 0; }
.card-line-text .small { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rarity-dot { flex: 0 0 8px; height: 8px; border-radius: 50%; }
/* 44 px по высоте — та же нижняя граница, что и у остальных кнопок:
   в бою промах пальцем стоит хода. */
.card-row > .btn { flex: 0 0 auto; padding: 10px 12px; font-size: 13px; min-height: 44px; }
.card-row > .skill-btn { min-height: 44px; }

/* Карта, которая бьёт, отличается от карты, которая только накладывает эффект. */
.btn-move { border-color: var(--sys-deep); background: #1D3444; }
.btn-move:hover:not(:disabled) { background: #26455A; }

/* Карта, заточенная против школы ЭТОГО противника.
   Без пометки заточка была бы скрытой лотереей: прибавка есть, а понять,
   откуда она взялась и как её добиться, нельзя. */
.btn-reads { border-color: var(--upgrade); box-shadow: 0 0 0 1px var(--upgrade) inset; }
/* Школа — в строке с именем, справа: отдельная строка стоила 24 px на
   каждого бойца, и на низком телефоне их не хватало логу. */
.combatant-school { margin-left: auto; opacity: .75; font-size: 12px; }

/* Всплывающий урон */
.pop {
  position: absolute;
  right: 8px; top: 0;
  font-family: var(--font-data); font-weight: 700; font-size: 19px;
  color: var(--blood);
  text-shadow: 0 0 10px rgba(255, 77, 77, 0.5);
  pointer-events: none;
  animation: pop-up 0.9s cubic-bezier(0.2, 0.8, 0.3, 1) forwards;
}
.pop.heal { color: var(--good); text-shadow: 0 0 10px rgba(91, 217, 138, 0.5); }
@keyframes pop-up {
  0%   { opacity: 0; translate: 0 6px; scale: 0.8; }
  22%  { opacity: 1; translate: 0 -4px; scale: 1.1; }
  100% { opacity: 0; translate: 0 -34px; scale: 1; }
}

/* Вспышка экрана при попадании по игроку */
.fight.shake { animation: shake 0.24s; }
@keyframes shake {
  25% { translate: -4px 0; } 50% { translate: 4px 0; } 75% { translate: -2px 0; }
}

/* --------------------------------------------------------------------------
   Вскрытие карты — главный момент гачи, поэтому во весь экран
   -------------------------------------------------------------------------- */

.reveal {
  position: fixed; inset: 0; z-index: 70;
  background: rgba(7, 11, 16, 0.97);
  display: grid; place-items: center; gap: 18px;
  padding: 28px;
  align-content: center;
}
.reveal-burst {
  position: absolute; inset: 0;
  background: radial-gradient(circle at 50% 42%, var(--burst, var(--sys-glow)), transparent 62%);
  animation: burst 0.7s ease-out both;
  pointer-events: none;
}
@keyframes burst { from { opacity: 0; scale: 0.5; } 60% { opacity: 1; } to { opacity: 0.55; scale: 1; } }

.reveal-card {
  width: min(70vw, 260px);
  aspect-ratio: 3 / 4.2;
  object-fit: contain;
  animation: card-in 0.62s cubic-bezier(0.2, 0.9, 0.25, 1) both;
  filter: drop-shadow(0 0 26px var(--burst, var(--sys-glow)));
}
@keyframes card-in {
  from { opacity: 0; transform: rotateY(90deg) scale(0.82); }
  to   { opacity: 1; transform: rotateY(0) scale(1); }
}
.reveal-grid {
  position: relative; display: grid; gap: 8px;
  grid-template-columns: repeat(auto-fill, minmax(84px, 1fr));
  width: min(100%, 520px); max-height: 70vh; overflow-y: auto;
}
.reveal-mini { margin: 0; text-align: center; animation: card-in 0.5s ease-out both; }
.reveal-mini img { width: 100%; aspect-ratio: 3 / 4.2; object-fit: contain; }
.reveal-mini figcaption { font-size: 11px; line-height: 1.25; color: var(--ink); }
.reveal-meta { text-align: center; display: grid; gap: 6px; position: relative;
               animation: line-in 0.4s 0.4s ease-out both; }

/* --------------------------------------------------------------------------
   Карточка карты снизу
   -------------------------------------------------------------------------- */

.sheet {
  position: fixed; inset: 0; z-index: 65;
  background: rgba(7, 11, 16, 0.8);
  display: grid; align-items: end;
}
.sheet-body {
  background: var(--panel);
  border-top: 1px solid var(--sys-deep);
  padding: 18px 16px calc(20px + env(safe-area-inset-bottom));
  display: grid; gap: 12px;
  max-height: 86vh; overflow-y: auto;
  /* Выезд снизу с торможением в конце, как у системных листов: transform,
     а не translate-свойство, чтобы ехало на видеокарте. */
  animation: sheet-in 0.34s cubic-bezier(0.22, 1, 0.36, 1) both;
  will-change: transform;
}
@keyframes sheet-in { from { transform: translateY(100%); } to { transform: none; } }
html[data-fx="off"] .sheet-body { animation-duration: 0.16s; }
.sheet-art { width: min(56vw, 200px); justify-self: center; display: block; }
/* Карточка нового подопечного: «Дальше» всегда видно внизу листа, даже
   когда черты не влезают в невысокий экран (showBirthResult). */
.sheet-body.birth-sheet > .birth-next-row {
  position: sticky; bottom: 0; z-index: 1;
  margin: 0 -16px;
  padding: 10px 16px 0;
  background: var(--panel);
  border-top: 1px solid rgba(255, 255, 255, 0.06);
  /* закрыть и нижний отступ листа, чтобы текст не просвечивал под кнопкой */
  box-shadow: 0 48px 0 var(--panel);
}

.kv { display: grid; grid-template-columns: auto 1fr; gap: 4px 12px; font-size: 13px; }
.kv dt { color: var(--ink-dim); }
.kv dd { margin: 0; }

/* --------------------------------------------------------------------------
   Всплывающее сообщение
   -------------------------------------------------------------------------- */

.toast {
  position: fixed;
  left: 50%; bottom: calc(var(--tabs-h) + 16px);
  translate: -50% 0;
  z-index: 80;
  max-width: min(92vw, 480px);
  padding: 11px 15px;
  background: var(--panel-up);
  border: 1px solid var(--sys-deep);
  font-size: 13px;
  line-height: 1.45;
  box-shadow: 0 8px 28px rgba(0, 0, 0, 0.6);
  animation: line-in 0.2s ease-out both;
  clip-path: polygon(
    var(--cut-sm) 0, 100% 0, 100% calc(100% - var(--cut-sm)),
    calc(100% - var(--cut-sm)) 100%, 0 100%, 0 var(--cut-sm)
  );
}
.toast.bad { border-color: var(--blood); }
.toast b { color: var(--sys); }

.empty { padding: 22px 12px; text-align: center; color: var(--ink-faint); font-size: 13px; }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

/* --------------------------------------------------------------------------
   Стена памяти
   --------------------------------------------------------------------------

   Смерть в игре окончательна, и это единственное, на чём всё держится. В чате
   у неё был некролог, а в вебе подопечные уходили бесследно — теперь стена
   есть и здесь.
   -------------------------------------------------------------------------- */

.wall {
  margin: 0;
  padding: 0;
  list-style: none;
  display: grid;
  gap: 1px;
  background: var(--line-soft);
}

.wall-row {
  display: grid;
  gap: 2px;
  padding: 10px 12px;
  background: var(--panel);
  border-left: 2px solid var(--line-soft);
}
.wall-row:first-child { border-left-color: var(--blood); }

.wall-name { font-weight: 700; }
.wall-fact { font-family: var(--font-data); }
.wall-reason { color: var(--ink-dim); font-style: italic; }
.wall-keep { color: var(--gold); opacity: 0.85; }

/* Метка улучшенной карты: фиолетовая, как баннер UPGRADE на самих артах. */
.up-badge {
  position: absolute;
  top: 4px; left: 4px;
  padding: 1px 5px;
  font-family: var(--font-data);
  font-size: 8px;
  letter-spacing: 0.1em;
  color: #150E22;
  background: var(--upgrade);
}
.deck-badge {
  position: absolute; right: 4px; top: 4px; padding: 1px 5px;
  font-family: var(--font-data); font-size: 8px; letter-spacing: 0.1em;
  color: #071017; background: var(--sys);
}
.card-cell.is-deck { border-color: var(--sys); }
.deck-panel { min-width: 0; max-width: 100%; }
.deck-row { max-width: 100%; display: flex; gap: 6px; overflow-x: auto; padding: 6px 0; }
.deck-thumb { position: relative; flex: 0 0 52px; aspect-ratio: 3 / 4.2; padding: 0;
  border: 1px solid var(--line); background: var(--bg-2, transparent); overflow: hidden; }
.deck-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.deck-thumb .rarity-bar { position: absolute; inset: auto 0 0 0; height: 2px; }
.card-cell.is-upgraded { box-shadow: 0 0 0 1px var(--upgrade), 0 0 14px rgba(185, 140, 255, 0.35); }

/* Метка главного задания. Красная не для красоты: провал главного закрывает
   Систему навсегда, и это должно быть видно до, а не после. */
.tag-main {
  padding: 1px 7px;
  font-family: var(--font-data);
  font-size: 9px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--blood);
  border: 1px solid var(--blood);
}

/* --------------------------------------------------------------------------
   Сцена боя и панели манхвы
   --------------------------------------------------------------------------

   Лог из одних строк — это протокол: событие пересказано словами вместо того
   чтобы быть показанным. В манхве удар показывают линиями скорости, вспышкой
   и крупной цифрой, и половина впечатления держится на них.

   Сцена сверху даёт бою лица: два бойца стоят друг против друга, и попадание
   видно по тому, как отшатывается портрет.
   -------------------------------------------------------------------------- */

/* Сцена боя. Раньше звалась .scene, как и окно разговора (#scene), и её
   position: relative перебивал fixed у разговора — окно Системы ложилось
   под низ страницы тремя колонками. */
.fight-scene {
  position: relative;
  /* Строка, не сетка: у строки высота после раскладки считается заданной, и
     «height: 100%» у бойца берётся от неё — портреты растут вместе со
     сценой, когда экран высокий, и держат 92 px, когда низкий. */
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 14px 4px;
  overflow: hidden;
}
.fight-scene .fighter { flex: 1 1 0; height: 100%; min-height: 56px; max-height: 200px; }
.fight-scene .scene-mid { flex: 0 0 auto; }

.fighter {
  position: relative;
  height: 92px;
  /* Колонка, не сетка: у сетки строка auto растёт под картинку, и
     «height: 100%» у картинки считался от неё же — портрет вырастал до
     230 px и резался, как только сцена перестала растягиваться. */
  display: flex;
  align-items: center;
  justify-content: center;
}
.fighter img {
  height: 100%;
  width: auto;
  max-width: 100%;
  object-fit: contain;
  filter: drop-shadow(0 4px 12px rgba(0, 0, 0, 0.6));
  transition: filter 0.25s, translate 0.12s;
}
/* Противник смотрит на игрока: зеркалим, чтобы бойцы стояли лицом к лицу. */
.fighter-foe img { scale: -1 1; }

/* На краю — портрет гаснет и уходит в красное. Видно, кто доживает. */
.fighter.is-hurt img {
  filter: drop-shadow(0 4px 12px rgba(255, 77, 77, 0.35))
          saturate(0.5) brightness(0.75);
}

/* Отдача от попадания. Класс вешает скрипт, снимает таймером. */
@keyframes recoil-you { 40% { translate: -10px 2px; } }
@keyframes recoil-foe { 40% { translate: 10px 2px; } }
.fighter-you.is-hit img { animation: recoil-you 0.22s ease-out; }
.fighter-foe.is-hit img { animation: recoil-foe 0.22s ease-out; }

.scene-mid { display: grid; place-items: center; }
.scene-round {
  font-size: 10px;
  letter-spacing: 0.3em;
  text-transform: uppercase;
  color: var(--ink-faint);
}

.scene-fx { position: absolute; inset: 0; pointer-events: none; z-index: 2; }

/* --- Панель удара ------------------------------------------------------- */

.panel-hit {
  position: relative;
  height: 84px;
  margin: 2px 0 6px;
  overflow: hidden;
  background: var(--void-2);
  border: 1px solid currentColor;
  display: grid;
  place-items: center;
  clip-path: polygon(
    var(--cut) 0, 100% 0, 100% calc(100% - var(--cut)),
    calc(100% - var(--cut)) 100%, 0 100%, 0 var(--cut)
  );
  animation: panel-in 0.34s cubic-bezier(0.16, 0.8, 0.3, 1) both;
}
@keyframes panel-in { from { opacity: 0; scale: 0.94; clip-path: inset(0 50% 0 50%); } }

/* Линии идут от того, кто ударил: свои слева, чужие справа. */
.ph-lines { position: absolute; inset: 0; width: 100%; height: 100%; }
.from-foe .ph-lines { scale: -1 1; }

.ph-burst {
  position: absolute;
  width: 96px; height: 96px;
  opacity: 0.22;
  animation: burst-in 0.5s ease-out both;
}
@keyframes burst-in { from { scale: 0.3; opacity: 0.6; } }

.ph-num {
  position: relative;
  z-index: 2;
  font-size: 34px;
  font-weight: 800;
  line-height: 1;
  text-shadow: 0 0 18px currentColor, 0 2px 0 var(--void);
}

/* Звукоподражание — как нарисованные буквы на странице. */
.ph-word {
  position: absolute;
  right: 12px; bottom: 8px;
  z-index: 2;
  font-family: var(--font-data);
  font-size: 11px;
  letter-spacing: 0.24em;
  opacity: 0.85;
  text-shadow: 0 0 8px var(--void);
}


/* --------------------------------------------------------------------------
   Мастерства — пороги, а не статы
   --------------------------------------------------------------------------
   Поэтому и рисуются иначе: не полосой, которая заполняется, а кольцом,
   которое либо горит, либо нет. Цвет кольца — канонная аура домена, по ней
   в манхве и опознают, что порог взят: «confirmed by the red effect that
   surrounded him». Взятый порог светится своим цветом, «путь» — пунктиром.
   -------------------------------------------------------------------------- */

.mastery-grid {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  gap: 6px;
  margin-top: 12px;
}

.mastery {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  padding: 10px 2px 8px;
  background: var(--void-2);
  border: 1px solid var(--line-soft);
  clip-path: polygon(
    var(--cut-sm) 0, 100% 0, 100% calc(100% - var(--cut-sm)),
    calc(100% - var(--cut-sm)) 100%, 0 100%, 0 var(--cut-sm)
  );
}

.mastery-ring {
  width: 26px;
  height: 26px;
  border-radius: 50%;
  border: 2px solid var(--line-soft);
  background: transparent;
  transition: box-shadow .25s ease, border-color .25s ease;
}

.mastery.is-on .mastery-ring {
  border-color: var(--aura);
  background: color-mix(in srgb, var(--aura) 22%, transparent);
  box-shadow: 0 0 10px var(--aura), inset 0 0 6px var(--aura);
}

/* Путь к Мастерству — ступень, а не порог. Пунктир читается как «ещё не». */
.mastery.is-path .mastery-ring {
  border-style: dashed;
  border-color: var(--aura);
  opacity: .75;
}

.mastery.is-on { border-color: color-mix(in srgb, var(--aura) 45%, var(--line-soft)); }

.mastery-name {
  font-size: 11px;
  line-height: 1.15;
  text-align: center;
  color: var(--ink-dim);
}

.mastery.is-on .mastery-name { color: var(--ink); }

.mastery-state {
  font-family: var(--font-data);
  font-size: 9px;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--ink-faint);
}

.mastery.is-on .mastery-state { color: var(--aura); }

.skill-list { margin-top: 12px; display: grid; gap: 10px; }

.skill {
  padding: 10px 12px;
  background: var(--void-2);
  border-left: 2px solid var(--upgrade);
}

.skill h3 { margin: 0; font-size: 14px; }

.chip-skill {
  font-size: 10px;
  color: var(--upgrade);
  border-color: color-mix(in srgb, var(--upgrade) 40%, transparent);
}

/* Аура на портрете бойца в бою: порог видно со стороны, и только так. */
.aura-marks {
  position: absolute;
  top: 4px;
  left: 50%;
  transform: translateX(-50%);
  display: flex;
  gap: 3px;
  pointer-events: none;
}

.aura-dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--aura);
  box-shadow: 0 0 6px var(--aura);
}

/* Уникальный приём в бою: он не карта — не тратится и не лежит в колоде,
   поэтому и кнопка у него другая, без рамки редкости. */
.skill-btn {
  flex: 0 0 auto;
  min-width: 116px;
  padding: 8px 10px;
  text-align: left;
  background: linear-gradient(160deg,
              color-mix(in srgb, var(--upgrade) 18%, var(--panel)), var(--panel));
  border: 1px solid color-mix(in srgb, var(--upgrade) 50%, transparent);
  clip-path: polygon(
    var(--cut-sm) 0, 100% 0, 100% calc(100% - var(--cut-sm)),
    calc(100% - var(--cut-sm)) 100%, 0 100%, 0 var(--cut-sm)
  );
}

.skill-btn[disabled] { opacity: .4; }
.skill-btn b { display: block; font-size: 12px; }
.skill-btn span { font-size: 10px; color: var(--ink-faint); font-family: var(--font-data); }

/* Кольцо-герой в карточке порога: тот же язык, что и в сетке, но крупно. */
.mastery-hero {
  display: flex;
  justify-content: center;
  padding: 6px 0 2px;
}

.mastery-hero i {
  width: 64px;
  height: 64px;
  border-radius: 50%;
  border: 3px solid var(--aura);
  background: radial-gradient(circle at 50% 50%,
              color-mix(in srgb, var(--aura) 30%, transparent), transparent 70%);
  box-shadow: 0 0 22px var(--aura);
}


/* --------------------------------------------------------------------------
   NewTube — чужая эстетика внутри окна Системы
   --------------------------------------------------------------------------
   Канал не принадлежит Системе, поэтому и не притворяется ею: числа крупные,
   как на счётчике просмотров, а виральный ролик подсвечен золотом. Но рамка
   и срез углов те же — иначе панель выпадет из интерфейса.
   -------------------------------------------------------------------------- */

.tube-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 8px;
  margin-top: 12px;
}

.tube-cell {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  padding: 10px 4px;
  background: var(--void-2);
  border: 1px solid var(--line-soft);
}

.tube-num { font-size: 19px; font-weight: 600; color: var(--ink); }
.tube-label { font-size: 10px; color: var(--ink-faint); text-align: center; }

.feed { margin-top: 12px; display: grid; gap: 1px; background: var(--line-soft); }

.feed-row {
  display: flex;
  justify-content: space-between;
  gap: 10px;
  padding: 7px 10px;
  background: var(--void-2);
  font-size: 12px;
}

.feed-row.is-viral { background: color-mix(in srgb, var(--gold) 12%, var(--void-2)); }
.feed-title { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.feed-num { color: var(--ink-dim); flex: 0 0 auto; }

.panel-dead { border-color: var(--blood); opacity: .8; }

.btn-tube {
  border-color: color-mix(in srgb, var(--gold) 55%, transparent);
  color: var(--gold);
}

/* Красная точка записи мигает — иначе её не отличить от обычной подписи. */
.rec { color: var(--blood); font-size: 11px; letter-spacing: .06em; }
@media (prefers-reduced-motion: no-preference) {
  .rec { animation: rec-blink 1.6s ease-in-out infinite; }
}
@keyframes rec-blink { 0%, 100% { opacity: 1 } 50% { opacity: .35 } }

/* Переключатель съёмки: решение принимается до боя и не переигрывается,
   поэтому он стоит прямо над кнопкой «Драться», а не в настройках. */
.rec-toggle {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 4px 10px;
  align-items: center;
  padding: 10px 12px;
  margin: 4px 0;
  background: var(--void-2);
  border: 1px solid var(--line-soft);
}

.rec-toggle input { width: 18px; height: 18px; accent-color: var(--blood); grid-row: span 2; }
.rec-toggle span:nth-of-type(2) { grid-column: 2; font-size: 11px; }


/* --------------------------------------------------------------------------
   Рисованные значки
   --------------------------------------------------------------------------
   Эмодзи в вебе не осталось: они разные в каждой системе, цветные там, где
   интерфейс одноцветный, и первыми выдают, что окно Системы — не окно, а
   вёрстка. Набор нарисован под тот же материал, что и панели: угловатая
   геометрия, один штрих, срезанные углы, никаких скруглений.

   Значок берёт цвет строки (`currentColor`) — поэтому один и тот же символ
   работает и на акценте, и на приглушённом тексте, и на красном.
   -------------------------------------------------------------------------- */

.sprite { position: absolute; width: 0; height: 0; overflow: hidden; }

.ic {
  width: 1.05em;
  height: 1.05em;
  flex: 0 0 auto;
  vertical-align: -0.15em;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.7;
  stroke-linecap: square;
  stroke-linejoin: miter;
  /* На мелком кегле штрих 1.7 при масштабе 24→14 px истончается до серого.
     Векторные эффекты не масштабируют его вместе с фигурой — так значок
     остаётся читаемым и в подписи 11 px, и в заголовке. */
  vector-effect: non-scaling-stroke;
}

/* Значок в плитке — крупнее и в своей колонке, как раньше был эмодзи. */
.tile-ic {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  color: var(--sys);
}
.tile-ic .ic { width: 22px; height: 22px; }
.tile[disabled] .tile-ic { color: var(--ink-faint); }
.tile.is-locked .tile-ic { color: var(--ink-faint); }

/* Кнопка ухода в шапке: значок вместо миски-эмодзи. */
.care-btn .ic { width: 20px; height: 20px; }

/* Значок внутри кнопки не должен растаскивать её по высоте. */
.btn .ic { margin-right: 2px; }

/* Предупреждение и запрет всегда идут своим цветом, где бы ни стояли. */
.ic-warn { color: var(--gold); }
.ic-bad { color: var(--blood); }

/* Свой путь — не порог и не приём, поэтому и выглядит иначе: не кольцо в
   сетке, а отдельная плашка золотой чертой. Она у бойца одна на всю жизнь. */
.own-path {
  margin-top: 12px;
  padding: 12px;
  background: var(--void-2);
  border-left: 2px solid var(--gold);
}
.own-path h3 { margin: 0; font-size: 14px; display: flex; align-items: center; gap: 6px; }
.chip-path {
  font-size: 10px;
  color: var(--gold);
  border-color: color-mix(in srgb, var(--gold) 40%, transparent);
}

/* --------------------------------------------------------------------------
   Панель владельца
   --------------------------------------------------------------------------

   Своя разметка, а не переиспользование игровых панелей: тут таблица и формы,
   а не карточки бойцов. Смешивать было бы дешевле в строках, но дороже в
   чтении — служебный экран должен и выглядеть служебным. */

.admin-subs { display: flex; gap: 6px; flex-wrap: wrap; margin-top: 8px; }
.btn-on { border-color: var(--sys-deep); background: #1D3444; }

.admin-search { display: grid; grid-template-columns: 1fr auto; gap: 6px; }

/* Поля ввода панели: тот же язык, что у формы рождения, но без её отступов —
   здесь их много подряд, и воздух между ними только растягивал бы экран. */
.input {
  width: 100%;
  background: var(--void-2);
  border: 1px solid var(--line);
  color: var(--ink);
  padding: 9px 10px;
  min-height: 40px;
  /* 16px обязательны: на меньшем размере iOS зумит страницу при фокусе и
     ломает вёрстку. Та же причина, что и у полей формы рождения выше. */
  font-size: 16px;
}
.input:focus { outline: none; border-color: var(--sys-deep); }
textarea.input { resize: vertical; line-height: 1.45; }

.admin-list { display: grid; gap: 4px; margin-top: 8px; }
.admin-row {
  display: grid; grid-template-columns: 1fr auto; gap: 8px;
  align-items: center; text-align: left;
  background: var(--void-2); border: 1px solid var(--line);
  padding: 9px 10px; min-height: 44px;
  transition: border-color 0.14s;
}
.admin-row:hover { border-color: var(--sys-deep); }
.admin-row-main { display: grid; gap: 1px; min-width: 0; }
.admin-row-main b { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.admin-row-num { display: grid; gap: 1px; justify-items: end; text-align: right; }

.admin-grants { display: grid; grid-template-columns: repeat(3, 1fr) auto; gap: 6px; }
@media (max-width: 420px) {
  .admin-grants { grid-template-columns: 1fr 1fr; }
}

/* Описание карты.
   .......................................................................
   Стояло мелким и слева, в общем потоке. Игроки читают карту от центра —
   туда падает взгляд после арта, — и описание там же, где имя, работает
   лучше, чем прижатое к краю. Размер поднят с 13 до 15: это единственный
   текст, ради которого карточку и открывают. */
.card-desc {
  margin: 0;
  text-align: center;
  font-size: 15px;
  line-height: 1.5;
  color: var(--ink);
  max-width: 34ch;
  justify-self: center;
}

/* Название характеристики теперь кнопка: по ней открывается пояснение.
   Игроки писали, что не понимают, чем Техника отличается от Чутья, — а
   объяснять это в подписи негде, строка и так узкая. Вопросительный знак
   мелкий и приглушённый: подсказка должна быть заметна тому, кто её ищет,
   и не мозолить глаза тому, кто уже разобрался. */
.stat-help {
  background: none; border: 0; padding: 0;
  text-align: left; cursor: pointer;
  display: inline-flex; align-items: baseline; gap: 4px;
}
.stat-help i {
  font-style: normal; font-size: 10px;
  color: var(--ink-dim); border: 1px solid var(--line);
  border-radius: 50%; width: 13px; height: 13px;
  display: inline-flex; align-items: center; justify-content: center;
  line-height: 1;
}
.stat-help:hover i { color: var(--sys); border-color: var(--sys-deep); }

/* Переключатель языка. Кнопки в ряд, а не выпадающий список: языков два,
   и список ради двух пунктов — лишнее нажатие на пустом месте. */
.lang-row { display: flex; gap: 6px; margin-top: 6px; flex-wrap: wrap; }

/* Конструктор бойца: поля в два и три столбца.
   Заполнять его — работа на минуту, и растягивать её в один длинный столбец
   значит заставлять листать там, где всё помещается на экран. */
.build-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; margin-top: 6px; }
.build-stats { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; margin-top: 6px; }
.build-grid .field-label, .build-stats .field-label { display: grid; gap: 3px; }
@media (max-width: 380px) {
  .build-stats { grid-template-columns: 1fr 1fr; }
}

/* Гайд. Строка «название — объяснение» вместо таблицы: объяснения разной
   длины, и в две колонки они рвутся некрасиво на узком экране. */
.guide-head {
  width: 100%; background: none; border: 0; padding: 0;
  display: flex; justify-content: space-between; align-items: baseline;
  cursor: pointer; text-align: left;
}
.guide-row { display: grid; gap: 2px; padding: 7px 0; border-bottom: 1px solid var(--line); }
.guide-row:last-child { border-bottom: 0; }
.guide-stats dd { margin-bottom: 6px; }

/* Промокод: поле и кнопка в строку — код короткий, переносить нечего. */
.promo-row { display: flex; gap: 6px; margin-top: 6px; }
.promo-row .input { flex: 1; text-transform: uppercase; }
.admin-row.is-dead { opacity: 0.5; }

/* Свежая глава — подсветить, чтобы не искать глазами в списке. */
.admin-row.is-fresh { border-left: 2px solid var(--sys); padding-left: 8px; }

/* --------------------------------------------------------------------------
   Живая лента: что происходит у других игроков

   Строки нарочно тише всего остального на экране — это фон, а не действие.
   Никаких кнопок: смотреть можно, нажать нельзя, чтобы лента не отвлекала
   от своей игры.
   -------------------------------------------------------------------------- */
.feed { display: flex; flex-direction: column; gap: 1px; }

.feed-row {
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  gap: 10px;
  padding: 7px 12px;
  background: var(--panel);
}

.feed-ic { display: flex; color: var(--ink-faint); }
.feed-ic .ic { width: 14px; height: 14px; }

.feed-text {
  font-size: 13px;
  color: var(--ink-dim);
  /* Длинное имя не должно ломать строку и растягивать панель. */
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.feed-at { white-space: nowrap; font-size: 11px; }

/* Своя команда в таблице войны — единственная строка, которую выделяем. */
.feed-row.is-mine { border-left: 2px solid var(--sys); }
.feed-row.is-mine .feed-text { color: var(--ink); }

/* Две кнопки прибавки в одной ячейке сетки статов.
   Сетка `.stat` рассчитана на фиксированное число ячеек, поэтому вторую
   кнопку кладём рядом с первой, а не отдельной колонкой. */
.plus-row { display: flex; gap: 4px; }
.plus-all { font-size: 13px; letter-spacing: -1px; }

/* Ряд фильтров по редкости над колодой. Переносится на узком экране —
   редкостей семь, и в одну строку они не встают. */
.rarity-row { display: flex; flex-wrap: wrap; gap: 6px; margin: 0 0 8px; }
.rarity-row .chip { cursor: pointer; background: none; }
.rarity-row .chip.is-on { background: rgba(79, 227, 216, 0.12); color: var(--sys); }

/* Список привилегий уровня подписки. Компактный, без маркеров-точек —
   их роль играет тире, как в остальном интерфейсе. */
.perks { margin: 6px 0 0; padding: 0; list-style: none; }
.perks li { font-size: 12px; color: var(--ink-faint); padding-left: 12px; position: relative; }
.perks li::before { content: '—'; position: absolute; left: 0; }

/* Галочки особенностей в конструкторе бойца. Их пятнадцать, поэтому
   раскладываем в несколько колонок и не растягиваем на всю ширину. */
.picks { display: flex; flex-wrap: wrap; gap: 6px 14px; margin: 6px 0 10px; }
.pick { display: flex; align-items: center; gap: 5px; font-size: 12px; }

/* Сворачиваемые редкости в Кодексе. Одной сеткой из ста семидесяти карт
   пользоваться нельзя — разворачиваем по одной. */
.codex-group { margin-bottom: 6px; }
.codex-head {
  display: flex; align-items: center; gap: 8px; width: 100%;
  padding: 9px 12px; background: var(--panel); border: 1px solid var(--line);
  color: inherit; font-size: 13px; text-align: left;
}
.codex-name { flex: 1; }
.codex-arrow { font-family: var(--font-data); color: var(--ink-faint); }
.rarity-dot { width: 8px; height: 8px; display: inline-block; }

/* Прибавка от снаряжения рядом с числом характеристики. Своим цветом —
   чтобы было видно, что это не само тело, а вещь при нём. */
.stat-add { font-style: normal; color: var(--sys); margin-left: 3px; font-size: 10px; }
.stat-add.is-minus { color: var(--blood); }

/* Полоса подвкладок внутри группы.
   Кнопок в группе до пяти, на узком экране они переносятся — поэтому flex
   с переносом, а не сетка с фиксированными колонками. */
/* Полоса подвкладок — сетка в три колонки: две строки, всё видно без
   прокрутки, кнопки во всю ширину колонки. Владелец: «верхние вкладки
   сделай в длину больше, в 2 строки, чтобы всё было видно». «Прочее» (≡)
   стоит последней и в любой группе одна. */
.subbar {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 5px;
  padding: 6px 8px; margin-bottom: 6px;
}
.subbar > .btn {
  width: 100%;
  min-width: 0;
  /* Ниже общих 44 px: две строки полосы не должны съедать четверть экрана
     низкого телефона. Ширина колонки при этом держит зону нажатия. */
  min-height: 34px;
  padding: 6px 6px;
  font-size: 13px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.subbar > .subbar-more { gap: 5px; }
.subbar > .subbar-more .ic { width: 16px; height: 16px; flex: 0 0 auto; }
/* Внизу теперь четыре вкладки — им хватает места без прокрутки. */
.tabs { grid-auto-columns: minmax(0, 1fr); overflow-x: hidden; }

/* ===================================================================
   Движение
   ===================================================================
   Интерфейс был полностью статичным: экран менялся мгновенной подменой
   разметки, и понять, что вообще произошло, можно было только прочитав
   текст. Здесь ровно те эффекты, которые что-то говорят, — появление
   нового экрана, вспышка Обратных глаз, удар при добивании. Ничего
   декоративного: анимация без смысла на телефоне только мешает.

   Всё уважает системную настройку «меньше движения» — см. в конце. */

@keyframes rise {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: none; }
}

/* Панели появляются снизу вверх, по очереди. Задержка крошечная: это не
   спектакль, а подсказка глазу, что содержимое сменилось. */
#view > .panel,
#view > div,
#view > section {
  animation: rise 0.18s ease-out both;
}
#view > *:nth-child(2) { animation-delay: 0.03s; }
#view > *:nth-child(3) { animation-delay: 0.06s; }
#view > *:nth-child(4) { animation-delay: 0.09s; }

/* Обратные глаза. Бирюзовая вспышка по краю экрана — то же свечение, что у
   Системы, только резче: состояние включилось само, и игрок должен это
   заметить, даже если смотрел на полосу здоровья. */
@keyframes ui-flash {
  0%   { box-shadow: inset 0 0 0 0 rgba(79, 227, 216, 0); }
  25%  { box-shadow: inset 0 0 90px 10px rgba(79, 227, 216, 0.55); }
  100% { box-shadow: inset 0 0 0 0 rgba(79, 227, 216, 0); }
}
.fx-ui { animation: ui-flash 0.9s ease-out; }

/* Добивание. Короткий толчок вбок — удар, а не тряска: длинная вибрация на
   телефоне читается как поломка приложения. */
@keyframes hit-shake {
  0%, 100% { transform: none; }
  20%      { transform: translateX(-5px); }
  45%      { transform: translateX(4px); }
  70%      { transform: translateX(-2px); }
}
.fx-hit { animation: hit-shake 0.32s ease-in-out; }

/* Красная вспышка, когда подопечный гибнет. */
@keyframes death-flash {
  0%   { box-shadow: inset 0 0 0 0 rgba(255, 90, 90, 0); }
  30%  { box-shadow: inset 0 0 120px 16px rgba(255, 90, 90, 0.5); }
  100% { box-shadow: inset 0 0 0 0 rgba(255, 90, 90, 0); }
}
.fx-death { animation: death-flash 1.1s ease-out; }

/* Полоса здоровья ползёт, а не прыгает: видно, сколько сняли за раунд. */
.meter-fill { transition: transform 0.35s cubic-bezier(0.22, 1, 0.36, 1); }

/* Полоса общего противника дышит, пока он жив, — событие идёт прямо сейчас,
   и это единственное место, где постоянное движение уместно. */
@keyframes boss-pulse {
  0%, 100% { filter: brightness(1); }
  50%      { filter: brightness(1.25); }
}
.boss-alive .meter-fill { animation: boss-pulse 2.4s ease-in-out infinite; }

/* Нажатие отзывается: кнопка проседает под пальцем. */
.btn:active:not(:disabled), .tile:active:not(:disabled) { transform: scale(0.985); }
.btn, .tile { transition: transform 0.08s ease-out; }

/* Кому системно не нужно движение — тому его и не будет. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}




/* --------------------------------------------------------------------------
   Свет порога
   --------------------------------------------------------------------------
   Порог берут раз в несколько часов игры, и это единственное событие, которое
   меняет тело необратимо. Поэтому оно занимает весь экран, а не строку в логе.
   Цвет — та же аура, что красит кольцо на плитке Мастерства: одно событие не
   должно говорить двумя разными цветами. */
.threshold-flash {
  position: fixed; inset: 0; z-index: 90; pointer-events: none;
  background:
    radial-gradient(120% 80% at 50% 50%,
      color-mix(in srgb, var(--aura) 42%, transparent) 0%,
      transparent 62%);
  animation: threshold-in 1.15s cubic-bezier(0.16, 1, 0.3, 1) forwards;
}
/* Второй порог светит дольше и ярче: он того стоит. */
.threshold-flash.is-deep { animation-duration: 1.8s; }
.threshold-flash.is-deep::after {
  content: ''; position: absolute; inset: 0;
  border: 2px solid color-mix(in srgb, var(--aura) 70%, transparent);
  animation: threshold-ring 1.8s ease-out forwards;
}
@keyframes threshold-in {
  0%   { opacity: 0; }
  18%  { opacity: 1; }
  100% { opacity: 0; }
}
@keyframes threshold-ring {
  0%   { opacity: 0.9; scale: 0.94; }
  100% { opacity: 0; scale: 1.03; }
}

/* Подложка под цвет взятого Мастерства.
   Свет слабый и уведён по краям: это фон, а не украшение поверх текста. */
body::before {
  content: ''; position: fixed; inset: 0; z-index: 0; pointer-events: none;
  background:
    radial-gradient(90% 60% at 50% 0%,
      color-mix(in srgb, var(--aura-bg, transparent)
        calc(var(--aura-bg-strength, 0) * 100%), transparent) 0%,
      transparent 70%);
  transition: background 0.8s ease-out;
}

/* --------------------------------------------------------------------------
   Карта войны
   --------------------------------------------------------------------------
   Высота задана явно и в пикселях. Раньше стояло aspect-ratio вместе с
   height: auto — в этом контейнере оно схлопывалось в ноль, и карты не было
   вовсе: игрок смотрел на пустое место между блоками.

   Участок — прямоугольник с именем внутри, а не точка с подписью сбоку.
   Подпись внутри блока не может налезть на соседа, а районы на карте города
   стоят близко, и раньше имена наезжали друг на друга. */
.warmap {
  display: block; width: 100%;
  /* Высота под телефон: рядов теперь ровно два на берег, и растягивать
     полотно незачем — от этого только пустота сверху и снизу. */
  height: 48vh; max-height: 400px; min-height: 290px;
  margin-top: 4px;
}
.warmap-ground { fill: var(--void-2); stroke: var(--line-soft); stroke-width: 0.5; }
.warmap-river { fill: rgba(79, 227, 216, 0.13); }
.warmap-river-label {
  fill: var(--sys-deep); font-size: 3px; letter-spacing: 1px;
  font-family: inherit; font-weight: 700;
}
.warmap-side {
  fill: var(--ink-faint); font-size: 2.8px; text-anchor: end;
  font-family: inherit; letter-spacing: 0.6px;
}
.warmap-plate {
  fill: var(--panel); stroke: var(--line-soft); stroke-width: 0.5;
}
.warmap-name {
  fill: var(--ink); font-size: 3.6px; text-anchor: middle;
  font-family: inherit; font-weight: 600;
}
.warmap-note {
  fill: var(--ink-faint); font-size: 2.6px; text-anchor: middle;
  font-family: inherit;
}
.warmap-note.is-mine { fill: var(--sys); }
.warmap-legend {
  fill: var(--ink-dim); font-size: 3px; font-family: inherit;
}
.warmap-front { cursor: pointer; transition: opacity 0.16s; }
.warmap-front:hover .warmap-plate { fill: var(--panel-up); }
.warmap-front.is-closed { cursor: default; opacity: 0.42; }
/* Свой наряд обведён и подписан словом: одной рамки мало, если цветов много. */
.warmap-ring {
  fill: none; stroke: var(--sys); stroke-width: 0.8;
  animation: warmap-ring 2.6s ease-in-out infinite;
}
@keyframes warmap-ring { 50% { opacity: 0.4; } }

/* Панель победы над общим противником.
 *
 * Отдельный вид нужен, потому что этот экран показывается ровно там, где
 * раньше стояла жалоба «Сейчас тихо». Тем же серым он читался бы как
 * поломка, а это итог общей недели. */
.panel-win {
  border-color: var(--good);
  background: linear-gradient(180deg,
    color-mix(in srgb, var(--good) 10%, var(--panel)) 0%,
    var(--panel) 60%);
}
.panel-win::after { background: var(--good); opacity: 1; }

/* ==========================================================================
   Разделы переработки (направление А). Каждый экран дописывает СВОЙ раздел,
   а не хвост файла: так правки разных экранов не сталкиваются при слиянии.
   ========================================================================== */

/* === РАЗДЕЛ: подопечный (статус, отряд, наставник) === */

/* Отступы и цвета классами вместо инлайновых style=: на одном экране статуса
   их было сорок пять, и каждый — повод для расхождения. Цвета — только из
   уже заведённых токенов. */
.m0 { margin: 0; }
.p-tight { margin: 2px 0 0; }
.p-gap { margin: 6px 0 0; }
.p-gap-lg { margin: 8px 0 0; }
.ink-sys { color: var(--sys); }
.ink-blood { color: var(--blood); }
.ink-good { color: var(--good); }
.ink-gold { color: var(--gold); }
.ink-upgrade { color: var(--upgrade); }

/* Полоса опыта — как HP в шапке: 13 px с цифрами внутри, а не трёхпиксельная
   линия, которую на телефоне читали как разделитель. */
.meter-xp .meter-fill { background: linear-gradient(90deg, var(--sys-deep), var(--sys)); }

/* Раскрывашки Статуса. Заголовок — тот же .codex-head, что у Кодекса: приём
   один на всё приложение. Итог справа — бирюзой, чтобы читался как данные. */
.hero-fold .codex-head { font-size: 14px; }
.hero-fold.is-open .codex-head { border-color: var(--sys-deep); }
.hero-sum { color: var(--sys); }
.hero-fold .fold-body {
  padding: 10px 12px 12px;
  border: 1px solid var(--line); border-top: 0;
  background: var(--panel);
}

/* Характеристики внутри раскрывашки: число — главная цифра экрана, поэтому
   основным цветом и кеглем; грейд остаётся чипом. Четвёртая колонка — под
   «+»/«все», иначе они падали на вторую строку сетки. */
.hero-stats .stat { grid-template-columns: 1fr auto auto auto; }
.hero-stats .stat-num { font-size: 14px; color: var(--ink); }
.hero-stats .stat.is-fixed { border-top: 1px solid var(--line); }
.hero-stats .stat-add.muted { color: var(--ink-faint); }
/* Шкала тренировки «0 из 100» — тонкая полоска по низу строки стата. */
.hero-stats .stat { position: relative; }
.stat-prog { position: absolute; left: 12px; right: 12px; bottom: 2px; height: 2px;
  background: var(--line-soft); overflow: hidden; pointer-events: none; }
.stat-prog b { display: block; height: 100%; background: var(--sys); transform-origin: left; }
.crew-stats .stat { grid-template-columns: 1fr auto auto; }
.hero-stats .plus-all { width: auto; padding: 0 8px; letter-spacing: 0; }

/* Шаги «как получить наставника» — списком, а не прозой. */
.hero-steps { margin: 6px 0 0; padding-left: 18px; }
.hero-steps li { margin: 2px 0; }

/* Мораль: полоса с серединой в нуле — середину надо нарисовать, иначе «+3»
   при заполнении наполовину сбивает. */
.meter-mid::after {
  content: ''; position: absolute; left: 50%; top: 0; bottom: 0;
  width: 1px; background: var(--ink-faint);
}
.moral-edges { display: flex; justify-content: space-between; font-size: 11px; margin-top: 3px; }

/* Плитка, которую нельзя нажать, должна выглядеть так: наёмники не по
   карману гасились только иконкой, а иконки у них не было. */
.tile[disabled] { opacity: 0.45; }

/* === РАЗДЕЛ: колода и лавка === */

/* Отступы классами, а не style= в разметке: экран колоды собирался из
   двух десятков инлайновых margin, и править их можно было только поиском. */
.tight { margin: 0; }
.panel-note { margin: 0 0 8px; }
.deck-gap { margin-top: 8px; }
.card-actions { margin: 10px 0; }
.set-row { margin-top: 6px; }
.set-note { margin: 4px 0 0; }
.trade-give { margin: 0 0 6px; }
.trade-own { margin-top: 14px; }
.relic-lost { margin: 4px 0 0; color: var(--gold); }
.relic-meter { margin-top: 6px; }
.sheet-title { text-align: center; }
.sheet-title .rarity-name { margin: 0 0 4px; }

/* Цвет текста и рамки по редкости — как .r-{редкость} для фона. */
.c-bronze { color: var(--bronze); }
.c-silver { color: var(--silver); }
.c-gold { color: var(--r-gold); }
.c-platinum { color: var(--platinum); }
.c-diamond { color: var(--diamond); }
.c-master { color: var(--master); }
.c-challenger { color: var(--challenger); }
.b-bronze { border-color: var(--bronze); }
.b-silver { border-color: var(--silver); }
.b-gold { border-color: var(--r-gold); }
.b-platinum { border-color: var(--platinum); }
.b-diamond { border-color: var(--diamond); }
.b-master { border-color: var(--master); }
.b-challenger { border-color: var(--challenger); }
.c-warn { color: var(--gold); }
.txt-blood { color: var(--blood); }
.txt-upgrade { color: var(--upgrade); }

/* Счёт очков Системы над колодами — тем же бирюзовым, что и сама Система. */
.deck-pts { color: var(--sys); font-size: 13px; }

/* Выключенная плитка гасится ЦЕЛИКОМ. Раньше серел только значок, заголовок
   оставался ярким — и молчащая при нажатии кнопка выглядела сломанной. */
.tile[disabled] { opacity: 0.5; cursor: not-allowed; }
.tile[disabled]:hover { border-color: var(--line); }
.pack-tile .tile-sub { display: inline-block; margin-top: 2px; }
.pack-open { color: var(--sys); text-align: right; font-size: 13px; line-height: 1.2; white-space: nowrap; }
.pack-open.muted { color: var(--ink-dim); }

/* Имя карты поверх нижнего края ячейки: на телефоне title не увидеть. */
.card-cell .card-name {
  position: absolute; inset: auto 0 0 0;
  padding: 10px 4px 5px;
  font-size: 11px; line-height: 1.15; text-align: left;
  color: var(--ink);
  background: linear-gradient(transparent, rgba(7, 11, 16, 0.9));
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.card-cell .count { bottom: auto; top: 4px; }

/* Полки лавки: по одной на редкость, сверху вниз в порядке RARITY. */
.shelf { margin-top: 10px; }
.shelf-head { margin-bottom: 6px; }

/* Момент выпадения: описание и эффект тем же кеглем, что и текст, а не
   серой подсказкой — это самая важная строка на экране. */
.reveal-desc { font-size: 14px; color: var(--ink); margin: 0; }
.reveal-effect { font-size: 14px; margin: 0; }

/* === РАЗДЕЛ: бой и шапка === */

/* Шапка: значок перед каждой полосой и цифра внутри. Полосы были 9, 5 и
   4 px без подписей — царапины, а не показатели; что это здоровье, силы,
   сытость и дух, игрок узнавал только из шторки ухода. */
.hud-row { display: grid; grid-template-columns: 12px 1fr; align-items: center; gap: 4px; min-width: 0; }
.hud-ic { width: 11px; height: 11px; color: var(--ink-dim); }
.hud .meter-labeled { height: 12px; }
.hud .meter-text { font-size: 10px; display: block; }
.hud .meter-food, .hud .meter-spirit { height: 12px; }
.hud-money { display: inline-flex; align-items: baseline; gap: 3px; white-space: nowrap; }
.hud-money .unit { font-size: 10px; color: var(--ink-dim); }

/* Бой: цифры у обеих полос, читаемые на телефоне. Девять пикселей у
   «42/60» не читались, у полосы сил цифры не было вовсе. */
.combatant .meter-text { font-size: 11px; display: inline-flex; align-items: center; gap: 3px; }
.combatant .meter-sta { height: 13px; }
.combatant .meter-sta .meter-text { display: inline-flex; }
.combatant-tags { display: inline-flex; flex-wrap: wrap; gap: 5px; }
.combatant .tag { font-size: 11px; letter-spacing: 0.06em; }

/* Кнопка в две строки: название и под ним цена с эффектом. Одна строка
   на три кнопки в ряд не помещается, поэтому подпись переносится сама. */
.btn-act { flex-direction: column; gap: 2px; line-height: 1.15; }
.btn-act .act-sub {
  font-family: var(--font-data); font-weight: 400; font-size: 10px;
  color: var(--ink-dim); white-space: normal; text-align: center; letter-spacing: 0;
}
.btn-primary.btn-act .act-sub { color: inherit; opacity: 0.8; }

/* Легенда первого раунда — вместо пустого лога. Жирное здесь — названия
   кнопок, а не урон, поэтому не красное и не моноширинное. */
.fight-log .legend { font-size: 13px; color: var(--ink-dim); }
.fight-log .legend b { color: var(--ink); font-family: inherit; }

/* Экран рождения: ряды облика без инлайновых стилей, подпись над ними. */
.look-label { margin: 10px 0 4px; }
.look-preview { display: grid; justify-items: center; gap: 6px; margin-bottom: 6px; }
.look-preview .face-big { width: min(52vw, 190px); }
.hero-head { gap: 12px; justify-content: flex-start; }
.hero-face { width: 64px; height: 74px; flex: 0 0 auto; }
.hero-head + .chip { align-self: flex-start; margin-top: 2px; }
.look-note { margin: 0 0 6px; }
.look-row { flex-wrap: wrap; gap: 6px; }
.look-row .btn-act { align-items: flex-start; }
.look-row .btn-act .act-sub { text-align: left; }

/* === РАЗДЕЛ: уведомления (Настройки) === */
.notify-row {
  width: 100%; text-align: left; padding: 9px 10px;
  background: var(--void-2); border: 1px solid var(--line-soft);
  color: var(--ink); min-height: 44px;
}
.notify-row.is-off { opacity: 0.55; }
.notify-row:disabled { opacity: 0.35; cursor: not-allowed; }
.chip-on { border-color: var(--sys-deep); color: var(--sys); }

/* === РАЗДЕЛ: модификаторы боёв === */

/* Правило именного боя: одна строка «Модификатор · Название — эффект».
   Золотом, как прочие предупреждения (оружие в руках, недостаток черты):
   это не ошибка и не награда, а условие, которое стоит прочесть до удара.
   Слева — тонкая полоска того же цвета, чтобы строка отделялась от подписи
   под именем и не сливалась с «мощь … против твоих». */
.mod-line {
  margin: 6px 0 8px;
  padding: 4px 0 4px 10px;
  border-left: 2px solid var(--gold);
  color: var(--gold);
}
/* В бою строка стоит между сценой и логом на всю ширину экрана — ей нужны
   те же поля, что у лога, и поменьше воздуха: высота экрана на счету. */
.fight > .mod-line { margin: 2px 14px; padding: 2px 0 2px 8px; font-size: 12px; }

/* === РАЗДЕЛ: Улица (участки Сеула) === */

/* Карточка «Сейчас»: одна строка-глагол крупно, остальное — подписи.
   Игрок открывает экран ради одного ответа «что жать», и он должен читаться
   первым, раньше карты. */
.street-now .street-line {
  font-family: var(--font-display);
  font-size: 18px; font-weight: 700; line-height: 1.25;
  margin: 2px 0 6px;
}
.street-now .btn-wide { margin-top: 8px; }
.street-hungry { margin: 4px 0; }

/* Карта: четыре ряда по три плитки. Три колонки при 360 px дают плитке
   около 100 px — имя участка и «сбито 2 из 5» умещаются, портрет сверху.
   Заглушки и запертые — серые, свои — рамка Системы, сбитые — золото:
   состояние читается по цвету раньше, чем по подписи. */
.street-tiles { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 6px; }
.street-tile {
  grid-template-columns: 1fr;
  justify-items: start;
  gap: 6px;
  padding: 8px;
  min-height: 100px;
  min-width: 0;
}
.street-tile .face-chip { width: 30px; height: 36px; }
/* Хозяин без портрета (у организации нет лидера в ростере): первая буква
   имени вместо тёмного квадрата, который читается как непрогрузившаяся
   картинка. */
.face-chip-blank {
  display: grid; place-items: center;
  font-family: var(--font-display); font-weight: 700; font-size: 16px;
  color: var(--ink-dim);
}
.street-tile .tile-body { display: grid; gap: 2px; min-width: 0; width: 100%; }
.street-tile .tile-title { font-size: 13px; line-height: 1.2; overflow-wrap: anywhere; }
.street-tile .tile-sub { display: block; font-size: 11px; line-height: 1.25; overflow-wrap: anywhere; }
.street-tile.is-soon, .street-tile.is-locked { opacity: 0.45; filter: grayscale(1); }
.street-tile.is-mine { border-color: var(--sys); }
.street-tile.is-down { border-color: var(--gold); }
.street-tile.is-siege { border-color: var(--blood); }
/* Выкат 3: участок другой банды игроков — зелёным, брошенный свой — золотом
   пунктиром (сбитый — золото сплошное): легенда под картой, дизайн
   «Улица 3–5» (StreetMap). */
.street-tile.is-held { border-color: var(--good); }
.street-tile.is-abandoned { border-color: var(--gold); border-style: dashed; }
.street-legend { display: flex; flex-wrap: wrap; gap: 4px 14px; margin: 0 2px; color: var(--ink-dim); }
.street-legend .is-mine { color: var(--sys); }
.street-legend .is-held { color: var(--good); }
.street-legend .is-canon { color: var(--ink-faint); }
.street-legend .is-abandoned { color: var(--gold); }

/* Брошенный участок на листе (StreetPatrol): рамка и полоса золотом —
   полоса показывает, сколько срока до прихода команды уже прошло. */
/* Улица, выкат 4: осада банды на банду (доски StreetDefense, StreetAssault,
 * StreetGang). Вызов на свою землю — красная рамка, свой вызов — бирюза. */
/* Улица, выкат 5 (доска StreetMap): Король Сеула золотом, Великое зло красным. */
.seoul-king { display: flex; gap: 10px; align-items: center; border-color: var(--gold);
  background: linear-gradient(135deg, rgba(255, 198, 92, .16), var(--panel)); }
.seoul-king .sys-label { color: var(--gold); }
.seoul-king-ic .ic { width: 26px; height: 26px; color: var(--gold); }
.seoul-evil-tag { font-size: 10px; color: var(--blood); border: 1px solid var(--blood); padding: 2px 6px; }
.seoul-evil-tag .ic { width: 11px; height: 11px; }
.panel.is-evil { border-color: var(--blood); }
.seoul-evil-row { gap: 8px; margin: 4px 0 8px; }
/* Дань со школ в экране отряда. */
.school-row { gap: 8px; padding: 6px 0; border-top: 1px solid var(--line-soft); align-items: center; }
.school-acts { display: flex; flex-wrap: wrap; gap: 6px; justify-content: flex-end; }
/* «Загрузка»: выбор карт напарника. */
.crew-load-list { gap: 4px; margin: 8px 0; }
.crew-load-row { min-height: 44px; padding: 4px 8px; border: 1px solid var(--line-soft); cursor: pointer; }
.crew-load-box { width: 22px; height: 22px; accent-color: var(--sys); }
.war-banner, .war-phase { display: grid; gap: 6px; }
.war-banner.is-defend, .war-phase.is-defend { border-color: var(--blood); }
.war-banner.is-defend .sys-label, .war-phase.is-defend .sys-label { color: var(--blood); }
.war-banner.is-attack, .war-phase.is-attack { border-color: var(--sys); }
.war-phase .meter-fill { background: var(--sys); }
.war-n { width: 18px; color: var(--sys); font-weight: 800; }
.war-empty { border: 1px dashed var(--line); background: transparent; }
.gang-tag { font-family: var(--font-data); font-size: 10px; letter-spacing: .12em; }
.gang-tag.is-leader { color: var(--gold); }
.gang-tag.is-second { color: var(--sys); }
.coup-panel { display: grid; gap: 8px; border-color: var(--gold); margin: 8px 0; }
.coup-panel .sys-label { color: var(--gold); }
.coup-panel .meter-fill { background: var(--gold); }
.coup-yes { color: var(--good); }
.coup-no { color: var(--blood); }
.street-abandon { border-color: var(--gold); display: grid; gap: 6px; }
.street-abandon .sys-label { color: var(--gold); }
.street-abandon .street-line {
  font-family: var(--font-display); font-size: 18px; font-weight: 700; line-height: 1.25;
}
.street-abandon-meter .meter-fill { background: var(--gold); }
.street-patrol-row { justify-content: flex-start; gap: 10px; padding: 4px 0; }
.street-patrol-row .face-chip { flex: 0 0 auto; }
.street-ward { display: grid; gap: 6px; }

/* Воскресная выплата (StreetPayout): крупная сумма дани, своя доля зелёным,
   банда полосами. */
.street-pay-big { font-size: 30px; color: var(--sys); margin: 4px 0; }
.street-pay-mine { font-size: 20px; color: var(--good); }
.street-pay-row { gap: 10px; padding: 3px 0; }
.street-pay-name { flex: 0 0 34%; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.street-pay-meter { flex: 1 1 auto; }
.street-pay-meter .meter-fill { background: var(--sys); }
.street-team-btns { display: flex; gap: 6px; flex: 0 0 auto; }
.street-pay-row .data { flex: 0 0 auto; min-width: 72px; text-align: right; }

/* Строка банды под картой. */
.street-team { flex-wrap: wrap; }
.street-team > span { flex: 1 1 180px; min-width: 0; }

/* Лестница на листе участка: имя, правило, полоса остатка. Сбитые —
   приглушены и зачёркнуты, текущая — красный бордюр слева, как строка
   модификатора, только цветом крови: сюда бьют. */
.ladder { display: grid; gap: 8px; }
.ladder-step { align-items: flex-start; padding: 6px 0 6px 8px; border-left: 2px solid transparent; }
.ladder-step .face-chip { flex: 0 0 auto; }
.ladder-body { gap: 3px; min-width: 0; flex: 1 1 auto; }
.ladder-step.is-down { opacity: 0.55; }
.ladder-step.is-down s { color: var(--ink-dim); }
.ladder-step.is-current { border-left-color: var(--blood); }
.ladder-step .mod-line { margin: 2px 0; padding: 2px 0 2px 8px; }
.ladder-step .meter { margin-top: 2px; }

/* Экран захода: итог над логом. */
.strike-sum { font-family: var(--font-data); font-size: 16px; color: var(--sys); }
/* Лог захода прокручивается к концу, а начало остаётся достижимым: у
   боевого лога `justify-content: flex-end` прячет верх за пределами
   прокрутки (переполнение колонки уходит вверх), здесь он не нужен. */
.street-log { max-height: 48vh; justify-content: flex-start; padding: 8px 0; }
.street-biz { display: grid; gap: 6px; padding-top: 8px; border-top: 1px solid var(--line-soft); }

/* === РАЗДЕЛ: арена и ещё === */

/* === РАЗДЕЛ: хаб (CHOICE APP, H1) === */
/* Первый экран в духе кинотеатров: премьера на весь экран, ряды постеров,
   «продолжить просмотр». Материал игры (срезанные углы, бирюза) остаётся
   внутри игры; у хаба свой — скругления, чёрный, тёплый акцент. У каждого
   модуля собственный стиль постера, и модуль, открывшись, возьмёт его же. */

.hub {
  --h-bg: #09090B;
  --h-card: #17171C;
  --h-line: rgba(255, 255, 255, 0.08);
  --h-text: #F5F5F7;
  --h-dim: #A1A1AA;
  --h-accent: #FF5A36;
  --h-accent-2: #FF2E63;
  --h-tint: rgba(255, 120, 60, 0.18);
  position: fixed; inset: 0; z-index: 45;
  overflow-y: auto; overflow-x: hidden; overscroll-behavior: contain;
  background: var(--h-bg); color: var(--h-text);
  font-family: var(--font-body);
  padding: 0 var(--safe-right) var(--safe-bottom) var(--safe-left);
}
/* Время года — оттенок света на премьере, не картинка. */
.hub.look-winter { --h-tint: rgba(110, 170, 255, 0.20); }
.hub.look-spring { --h-tint: rgba(110, 230, 160, 0.16); }
.hub.look-summer { --h-tint: rgba(255, 205, 90, 0.18); }
.hub.look-autumn { --h-tint: rgba(255, 120, 60, 0.18); }

/* Шапка поверх премьеры. */
.hub-top {
  position: sticky; top: 0; z-index: 5;
  display: flex; align-items: center; justify-content: space-between;
  padding: calc(10px + var(--safe-top)) 16px 10px;
  /* Матовое стекло: при прокрутке ряды уходят под шапку и не читаются
     сквозь логотип. */
  background: rgba(9, 9, 11, 0.72);
  -webkit-backdrop-filter: blur(16px); backdrop-filter: blur(16px);
}
.hub-logo {
  display: inline-flex; align-items: center; gap: 6px;
  font-family: var(--font-display); font-weight: 900; font-size: 17px;
  letter-spacing: 0.06em;
}
/* Знак — голова пса с WAR из лого владельца. 46 px: ниже морда и буквы
   сливаются в пятно; отрицательные поля не дают шапке вырасти. */
.hub-logo img { height: 46px; width: auto; display: block; margin: -8px 0 -8px -8px; }
.hub-top-side { display: flex; align-items: center; gap: 10px; }
.hub-pill {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 6px 12px; border-radius: 999px; font-size: 13px;
  background: rgba(255, 255, 255, 0.1); backdrop-filter: blur(8px);
}
.hub-pill .ic { width: 16px; height: 16px; color: #7CE8DF; }
.hub-ava {
  width: 44px; height: 44px; border-radius: 50%; overflow: hidden;
  display: grid; place-items: center; padding: 0;
  background: #26262E; border: 2px solid rgba(255, 255, 255, 0.28);
}
.hub-ava-img { width: 100%; height: 100%; object-fit: cover; display: block; }
.hub-ava-letter { font-weight: 800; font-size: 18px; color: var(--h-text); }
/* Рамка по подписке — цвета лестницы редкостей карт. */
.frame-npc { border-color: var(--silver) !important; }
.frame-player { border-color: var(--sys) !important; }
.frame-mod { border-color: var(--diamond) !important; }
.frame-admin { border-color: var(--r-gold) !important; }
.frame-vvip { border-color: var(--master) !important; box-shadow: 0 0 14px rgba(199, 125, 255, 0.6); }

/* Премьера. */
.hub-hero {
  position: relative; margin-top: -64px;
  min-height: min(76vh, 600px);
  display: flex; align-items: flex-end;
  overflow: hidden;
}
.hero-art { position: absolute; inset: 0; overflow: hidden; }
.hero-back {
  position: absolute; inset: -10%; width: 120%; height: 80%; object-fit: cover;
  filter: blur(26px) brightness(0.5) saturate(1.2);
}
.hero-art::before {
  content: ""; position: absolute; inset: 0;
  background: radial-gradient(90% 60% at 50% 30%, var(--h-tint), transparent 70%);
}
.hub-star {
  position: absolute; top: 60px; left: 50%; translate: -50% 0;
  width: min(74%, 320px); z-index: 1;
  /* Кадр из манхвы с тонкой рамкой окна: срезаем её, иначе на тёмном
     фоне рамка читается светлой обводкой. */
  clip-path: inset(3px);
  /* Края кадра растворяются во все стороны: у панели манхвы светлый фон,
     и жёсткий край читался бы вырезкой, а не афишей. */
  -webkit-mask-image: linear-gradient(to bottom, #000 62%, transparent 100%),
                      linear-gradient(to right, transparent, #000 16%, #000 84%, transparent);
  -webkit-mask-composite: source-in;
          mask-image: linear-gradient(to bottom, #000 62%, transparent 100%),
                      linear-gradient(to right, transparent, #000 16%, #000 84%, transparent);
          mask-composite: intersect;
}
.hub-hero::after {
  content: ""; position: absolute; inset: 0; z-index: 3; pointer-events: none;
  background: linear-gradient(to top, var(--h-bg) 0%, var(--h-bg) 20%,
                              rgba(9, 9, 11, 0.55) 40%, rgba(9, 9, 11, 0) 58%);
}
.hero-body {
  position: relative; z-index: 4; width: 100%;
  padding: 0 20px 6px; display: grid; gap: 8px;
}
.hero-tag {
  justify-self: start; display: inline-flex; align-items: center; gap: 8px;
  font-size: 11px; font-weight: 800; letter-spacing: 0.16em; text-transform: uppercase;
  color: var(--h-text);
}
.hero-tag::before {
  content: ""; width: 4px; height: 14px; border-radius: 2px;
  background: linear-gradient(var(--h-accent), var(--h-accent-2));
}
.hero-title {
  font-family: var(--font-display); font-weight: 900; font-size: 48px; line-height: 1;
  letter-spacing: -0.02em; text-transform: uppercase;
  text-shadow: 0 4px 30px rgba(0, 0, 0, 0.6);
}
.hero-meta { margin: 0; font-size: 13px; color: var(--h-dim); }
.hero-meta span { color: #7CE8DF; }
.hero-line { margin: 0; font-size: 15px; line-height: 1.45; color: #E4E4E7; max-width: 36ch; }
.hero-actions { display: flex; gap: 10px; margin-top: 6px; }
.hero-play, .hero-more {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  min-height: 48px; padding: 0 20px; border-radius: 10px;
  font-weight: 800; font-size: 15px; transition: transform 0.12s, background 0.12s;
}
.hero-play { flex: 1; background: #FFFFFF; color: #0A0A0A; }
.hero-play .ic { width: 18px; height: 18px; fill: currentColor; }
.hero-more { background: rgba(255, 255, 255, 0.16); color: var(--h-text); backdrop-filter: blur(10px); }
.hero-play:active, .hero-more:active { transform: scale(0.97); }
.hero-ward { margin: 2px 0 0; font-size: 12px; color: var(--h-dim); }

/* Ряды. */
.hub-rows { display: grid; gap: 26px; padding: 22px 16px 32px; }
.hub-row { min-width: 0; }
.row-title { font-family: var(--font-display); font-size: 18px; font-weight: 800; margin: 0 0 12px; }

/* «Продолжить сезон» — как карточка недосмотренного фильма. */
.season-card {
  display: grid; grid-template-columns: 92px minmax(0, 1fr);
  background: var(--h-card); border: 1px solid var(--h-line);
  border-radius: 12px; overflow: hidden;
}
.season-thumb {
  display: grid; place-items: center; min-height: 92px;
  background: linear-gradient(135deg, var(--h-accent), var(--h-accent-2));
  font-size: 22px; font-weight: 800; color: #FFFFFF;
}
.season-info { padding: 12px 14px; display: grid; gap: 8px; align-content: center; }
.season-name { margin: 0; font-weight: 700; font-size: 15px; }
.season-name span { display: block; font-weight: 500; color: var(--h-dim); font-size: 13px; }
.season-bar { height: 4px; border-radius: 2px; background: rgba(255, 255, 255, 0.14); overflow: hidden; }
.season-bar i {
  display: block; height: 100%; transform-origin: left;
  background: linear-gradient(90deg, var(--h-accent), var(--h-accent-2));
}
.season-next { margin: 0; font-size: 12px; color: var(--h-dim); }

/* Постеры: горизонтальная лента с прилипанием. */
.row-scroll {
  display: flex; gap: 12px; overflow-x: auto; scroll-snap-type: x mandatory;
  margin: 0 -16px; padding: 2px 16px 6px; scrollbar-width: none;
}
.row-scroll::-webkit-scrollbar { display: none; }
.poster {
  position: relative; flex: 0 0 148px; aspect-ratio: 2 / 3;
  border-radius: 12px; overflow: hidden; padding: 0;
  scroll-snap-align: start; text-align: left;
  border: 1px solid var(--h-line);
  box-shadow: 0 10px 24px rgba(0, 0, 0, 0.45);
  transition: transform 0.14s;
}
.poster:active:not(:disabled) { transform: scale(0.97); }
.poster.is-soon { cursor: default; }
.poster-art { position: absolute; inset: 0; overflow: hidden; }
.poster-face {
  position: absolute; left: 0; top: 0; width: 100%; height: 74%;
  object-fit: cover; clip-path: inset(3px);
  -webkit-mask-image: linear-gradient(to bottom, #000 60%, transparent);
          mask-image: linear-gradient(to bottom, #000 60%, transparent);
}
.poster-face-1, .poster-face-2 { display: none; }
.poster-deco { position: absolute; inset: 0; pointer-events: none; }
.poster-badge {
  position: absolute; top: 8px; left: 8px; z-index: 2;
  padding: 3px 7px; border-radius: 5px;
  font-size: 10px; font-weight: 800; letter-spacing: 0.08em; text-transform: uppercase;
  background: rgba(0, 0, 0, 0.62); color: #FFFFFF; backdrop-filter: blur(6px);
}
.poster:not(.is-soon) .poster-badge { background: linear-gradient(135deg, var(--h-accent), var(--h-accent-2)); }
.poster-body {
  position: absolute; left: 0; right: 0; bottom: 0; z-index: 2;
  display: grid; gap: 2px; padding: 26px 10px 10px;
}
.poster-name { font-family: var(--font-display); font-weight: 900; font-size: 19px; line-height: 1.05; }
.poster-meta { font-size: 11px; opacity: 0.8; }

/* Lvl Up — окно Системы: бирюзовое свечение и сетка. */
.poster.theme-system { background: radial-gradient(120% 80% at 50% 10%, #1E5358, #0B1219 70%); color: #EAF3F8; }
.poster.theme-system .poster-deco {
  background:
    linear-gradient(rgba(79, 227, 216, 0.07) 1px, transparent 1px) 0 0 / 100% 14px,
    linear-gradient(to top, #0B1219 12%, transparent 50%);
}
.poster.theme-system .poster-name {
  font-family: var(--font-data); letter-spacing: 0.08em; text-transform: uppercase;
  color: #4FE3D8; text-shadow: 0 0 14px rgba(79, 227, 216, 0.6);
}

/* W-lib — бумага и тушь вебтуна: кремовый лист, линии скорости, буква. */
.poster.theme-ink { background: #EFE7D6; color: #1E1A16; }
.poster.theme-ink .poster-deco {
  background:
    repeating-linear-gradient(-62deg, transparent 0 9px, rgba(30, 26, 22, 0.16) 9px 10px) 0 0 / 100% 62%,
    linear-gradient(to top, #EFE7D6 30%, rgba(239, 231, 214, 0) 60%);
  background-repeat: no-repeat;
}
.poster.theme-ink .poster-deco::before {
  content: "W"; position: absolute; top: 12%; left: 50%; translate: -50% 0;
  font-family: Georgia, "Times New Roman", serif; font-weight: 900; font-size: 118px; line-height: 1;
  color: #1E1A16; text-shadow: 4px 4px 0 #D8352A;
}
.poster.theme-ink .poster-name { font-family: Georgia, "Times New Roman", serif; font-size: 24px; }
.poster.theme-ink .poster-badge { background: #1E1A16; }

/* Вики — досье: портрет в сером, сетка, красный штамп. */
.poster.theme-dossier { background: #0E1612; color: #CFE8D6; }
.poster.theme-dossier .poster-face { filter: grayscale(1) contrast(1.25) brightness(0.9); }
.poster.theme-dossier .poster-deco {
  background:
    repeating-linear-gradient(0deg, rgba(159, 224, 176, 0.08) 0 1px, transparent 1px 16px),
    linear-gradient(to top, #0E1612 26%, transparent 60%);
}
.poster.theme-dossier .poster-deco::after {
  content: "ДОСЬЕ"; position: absolute; top: 38%; right: 8px; rotate: -12deg;
  padding: 2px 8px; border: 2px solid #E5484D; border-radius: 4px;
  font-family: var(--font-data); font-weight: 800; font-size: 13px; letter-spacing: 0.18em;
  color: #E5484D; opacity: 0.9;
}
.poster.theme-dossier .poster-name {
  font-family: var(--font-data); font-size: 17px; letter-spacing: 0.14em;
  text-transform: uppercase; color: #9FE0B0;
}

/* Lookism rate — глянец и неон: дуотон розовый/циан, свечение. */
.poster.theme-neon { background: linear-gradient(160deg, #FF2EA6, #7B2FF7 55%, #16D9E3); color: #FFFFFF; }
.poster.theme-neon .poster-face {
  height: 80%; filter: grayscale(1) contrast(1.3); mix-blend-mode: multiply;
}
.poster.theme-neon .poster-deco {
  background:
    radial-gradient(60% 40% at 80% 10%, rgba(255, 255, 255, 0.35), transparent 70%),
    linear-gradient(to top, rgba(20, 6, 40, 0.95) 22%, transparent 58%);
}
.poster.theme-neon .poster-name {
  font-style: italic; letter-spacing: 0.02em; color: #FFE3F6;
  text-shadow: 0 0 10px #FF2EA6, 0 0 22px rgba(255, 46, 166, 0.6);
}

/* Обложка вместо стиля модуля: картинка на весь постер, название поверх. */
.poster-cover { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.poster.has-cover .poster-deco { display: none; }
.poster.has-cover .poster-body {
  background: linear-gradient(to top, rgba(0, 0, 0, 0.85) 20%, rgba(0, 0, 0, 0));
  color: #FFFFFF;
}
.hero-cover {
  position: absolute; left: 0; top: 0; width: 100%; height: auto; min-height: 100%;
  object-fit: cover; object-position: center top;
}

/* Выбор модуля: большие карточки во всю ширину, листаются пальцем.
   Соседняя карточка выглядывает справа — видно, что есть куда листать. */
.hub-tabs {
  display: flex; gap: 18px; padding: 2px 16px 12px;
  overflow-x: auto; scrollbar-width: none;
}
.hub-tabs::-webkit-scrollbar { display: none; }
.hub-tab {
  flex: 0 0 auto; padding: 0 0 6px; white-space: nowrap;
  font-weight: 800; font-size: 14px; color: #71717A;
  border-bottom: 2px solid transparent; transition: color 0.15s, border-color 0.15s;
}
.hub-tab.is-on { color: var(--h-text); border-color: var(--h-accent-2); }
.hub-track {
  display: flex; gap: 10px; padding: 0 16px 4px;
  overflow-x: auto; scroll-snap-type: x mandatory; scroll-padding: 0 16px;
  overscroll-behavior-x: contain; scrollbar-width: none;
}
.hub-track::-webkit-scrollbar { display: none; }
.hub-track::after { content: ""; flex: 0 0 6px; }
.poster.hub-slide {
  flex: 0 0 91%; max-width: 440px; aspect-ratio: auto;
  height: clamp(440px, 68vh, 580px);
  display: flex; flex-direction: column; justify-content: flex-end;
  border-radius: 18px; scroll-snap-align: start; scroll-snap-stop: always;
}
.hub-slide .poster-face { height: 70%; }
.hub-slide .poster-badge { top: 12px; left: 12px; font-size: 11px; padding: 4px 9px; }
.slide-body {
  position: relative; z-index: 2;
  display: grid; gap: 8px; padding: 60px 18px 18px;
}
.hub-slide .poster-name { font-size: 42px; line-height: 0.98; margin: 0; }
.slide-meta { margin: 0; font-size: 13px; opacity: 0.75; }
.slide-meta span { color: #7CE8DF; }
.slide-line { margin: 0; font-size: 14px; line-height: 1.45; opacity: 0.92; }
.hub-slide .hero-actions { margin-top: 4px; }
.poster.hub-slide .hero-play.is-soon {
  background: rgba(255, 255, 255, 0.14); color: inherit; cursor: default;
  font-size: 13px; letter-spacing: 0.08em; text-transform: uppercase;
}
.poster.hub-slide.has-cover .slide-body {
  background: linear-gradient(to top, rgba(0, 0, 0, 0.92) 35%, rgba(0, 0, 0, 0));
  color: #FFFFFF;
}
/* Lvl Up — окно Системы. */
.hub-slide.theme-system { border-color: rgba(79, 227, 216, 0.25); }
.hub-slide.theme-system .poster-name { font-size: 44px; }
/* W-lib — бумага и тушь: обложка сверху, лист снизу, жёсткая рамка. */
.hub-slide.theme-ink { border: 3px solid #1E1A16; }
.hub-slide.theme-ink .poster-art { position: relative; flex: 1; border-bottom: 3px solid #1E1A16; }
.hub-slide.theme-ink .poster-deco::before { font-size: 220px; top: 8%; }
.poster.hub-slide.theme-ink .slide-body,
.poster.hub-slide.theme-ink.has-cover .slide-body {
  background: #F2EBDD; color: #1E1A16; padding-top: 14px;
}
.hub-slide.theme-ink .poster-name {
  font-family: "Playfair Display", Georgia, serif; font-size: 42px;
  text-shadow: 3px 3px 0 #D8352A;
}
.hub-slide.theme-ink .slide-meta, .hub-slide.theme-ink .slide-line { font-family: "PT Serif", Georgia, serif; }
.hub-slide.theme-ink .hero-play { background: #1E1A16; color: #F2EBDD; border-radius: 0; }
.poster.hub-slide.theme-ink .hero-play.is-soon { background: rgba(30, 26, 22, 0.1); color: #1E1A16; }
.hub-slide.theme-ink .poster-badge { border-radius: 0; background: #D8352A; }
/* W-lib с обложкой — на всю карточку, как Wiki и Lookism rate (владелец,
   26.09.2026: «исправь превью W-lib»). Обложка сидела в верхней половине и
   резала головы. Её нижняя треть — пустой лист, текст ложится прямо на него. */
.poster.hub-slide.theme-ink.has-cover .poster-art {
  position: absolute; inset: 0; flex: none; border-bottom: 0;
}
.poster.hub-slide.theme-ink.has-cover .poster-cover { object-fit: cover; object-position: center top; }
.poster.hub-slide.theme-ink.has-cover .slide-body {
  background: linear-gradient(to top, #F2EBDD 62%, rgba(242, 235, 221, 0));
  padding-top: 48px;
}
/* Wiki — досье. */
.hub-slide.theme-dossier .poster-name { font-size: 32px; }
.hub-slide.theme-dossier .poster-deco::after { top: 34%; font-size: 18px; }
.hub-slide.theme-dossier .hero-play { background: #9FE0B0; color: #0E1612; }
/* Lookism rate — неон. */
.hub-slide.theme-neon .poster-name { font-size: 38px; }
.hub-slide.theme-neon .hero-play { background: #FFFFFF; color: #7B2FF7; }
.hub-dots { display: flex; justify-content: center; gap: 6px; padding: 12px 0 0; }
.hub-dots i {
  width: 7px; height: 7px; border-radius: 4px;
  background: rgba(255, 255, 255, 0.28); transition: width 0.2s, background 0.2s;
}
.hub-dots i.is-on { width: 22px; background: #FFFFFF; }

/* Донат и настройки — две плитки под кошельком. */
.hub-tools { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.hub-tool {
  display: grid; justify-items: start; gap: 6px; padding: 16px 14px;
  border-radius: 12px; text-align: left; color: var(--h-text);
  background: var(--h-card); border: 1px solid var(--h-line);
}
.hub-tool .ic { width: 24px; height: 24px; }
.hub-tool b { font-size: 16px; }
.hub-tool span { font-size: 12px; color: var(--h-dim); line-height: 1.35; }
.hub-tool-donate { background: linear-gradient(135deg, var(--h-accent), var(--h-accent-2)); border: 0; }
.hub-tool-donate span { color: rgba(255, 255, 255, 0.9); }
.hub-tool:active { transform: scale(0.97); }

/* Страница хаба: настройки или донат. Панели те же, что в игре. */
.hub-page { padding: 4px 16px 32px; display: grid; gap: 12px; }
.hub-back { justify-self: start; padding: 6px 0; font-weight: 700; font-size: 14px; color: var(--h-dim); }
.hub-page-title { margin: 0 0 4px; font-family: var(--font-display); font-size: 28px; font-weight: 900; }

/* Страницы хаба в его стиле: те же панели игры, но без окна Системы —
   тёмные карточки со скруглением, подписи серым, кнопки-таблетки. */
.hub-page .panel, .hub-page .panel-inset {
  clip-path: none; border-radius: 14px;
  background: var(--h-card); border: 1px solid var(--h-line);
  padding: 16px; box-shadow: none;
}
.hub-page .panel::after { display: none; }
.hub-page .sys-label {
  font-family: var(--font-body); font-size: 11px; font-weight: 800;
  letter-spacing: 0.14em; color: var(--h-dim); opacity: 1;
}
.hub-page .donate-group { margin: 8px 0 -4px; }
.hub-page .muted, .hub-page .small.muted { color: var(--h-dim); }
.hub-page b, .hub-page h3 { color: var(--h-text); }
.hub-page .btn {
  clip-path: none; border-radius: 999px; min-height: 40px;
  padding: 9px 16px; font-size: 13px; font-weight: 800;
  background: #FFFFFF; color: #0A0A0A; border: 0;
}
.hub-page .btn-ghost { background: rgba(255, 255, 255, 0.08); color: var(--h-text); }
.hub-page .btn-on { background: #FFFFFF; color: #0A0A0A; }
.hub-page .btn:hover:not(:disabled) { background: #E4E4E7; }
.hub-page .btn-ghost:hover:not(:disabled) { background: rgba(255, 255, 255, 0.14); }
.hub-page .btn-wide { border-radius: 10px; min-height: 46px; }
.hub-page a.btn { text-decoration: none; }
.hub-page .lang-row { gap: 8px; }
.hub-page .input {
  border-radius: 10px; background: #0E0E12; border: 1px solid rgba(255, 255, 255, 0.12);
  color: var(--h-text);
}
.hub-page .promo-row { gap: 8px; }
.hub-page .promo-row .btn { border-radius: 10px; }
.hub-page .stack { gap: 0; }
/* Уведомления: строка с переключателем вместо «вкл/выкл». */
.hub-page .notify-row {
  background: transparent; border: 0; border-top: 1px solid rgba(255, 255, 255, 0.06);
  border-radius: 0; padding: 12px 0; gap: 12px; align-items: center;
}
.hub-page .notify-row.is-off { opacity: 1; }
.hub-page .notify-row .chip {
  flex: 0 0 44px; height: 26px; padding: 0; border: 0; border-radius: 13px;
  font-size: 0; position: relative; background: rgba(255, 255, 255, 0.14);
}
.hub-page .notify-row .chip::after {
  content: ""; position: absolute; top: 3px; left: 3px;
  width: 20px; height: 20px; border-radius: 50%; background: #FFFFFF;
  transition: left 0.15s;
}
.hub-page .notify-row .chip-on { background: linear-gradient(135deg, var(--h-accent), var(--h-accent-2)); }
.hub-page .notify-row .chip-on::after { left: 21px; }
/* Донат: цена — белая таблетка со звездой, первая панель — акцентная. */
.hub-page .panel-inset .chip, .hub-page .panel-inset .btn-small {
  display: inline-block; padding: 8px 12px; border-radius: 999px; border: 0;
  background: #FFFFFF; color: #0A0A0A; font-family: var(--font-body);
  font-size: 13px; font-weight: 800; min-height: 0;
}
.hub-page-donate > .panel:first-of-type {
  background: linear-gradient(135deg, var(--h-accent), var(--h-accent-2)); border: 0;
}
.hub-page-donate > .panel:first-of-type .sys-label,
.hub-page-donate > .panel:first-of-type .muted { color: rgba(255, 255, 255, 0.9); }
.hub-page-donate > .panel:first-of-type .btn { background: rgba(0, 0, 0, 0.22); color: #FFFFFF; }
.hub-page .perks li { color: var(--h-dim); }
.hub-page .perks li::before { content: "•"; }
.hub-page .data { font-family: var(--font-display); }

/* Кошелёк. */
.wallet-row { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.wallet {
  display: grid; gap: 4px; padding: 14px; min-width: 0;
  border-radius: 12px; border: 1px solid var(--h-line);
}
.wallet-won { background: linear-gradient(145deg, #2E2212, var(--h-card) 70%); }
.wallet-points { background: linear-gradient(145deg, #0F2E2E, var(--h-card) 70%); }
.wallet-label { font-size: 11px; font-weight: 800; letter-spacing: 0.12em; text-transform: uppercase; }
.wallet-won .wallet-label { color: var(--gold); }
.wallet-points .wallet-label { color: #7CE8DF; }
.wallet-sum { font-size: 24px; font-weight: 700; overflow-wrap: anywhere; }
.wallet-note { font-size: 12px; color: var(--h-dim); line-height: 1.35; }

.hub-owner {
  display: flex; align-items: center; justify-content: center; gap: 8px;
  min-height: 48px; border-radius: 10px;
  background: rgba(255, 255, 255, 0.06); color: var(--h-dim); font-weight: 700;
}

/* Шторки хаба — в материале хаба, а не окна Системы. */
.sheet-body.hub-sheet {
  background: #17171C; border-top: 0; border-radius: 18px 18px 0 0;
  font-family: var(--font-body);
}
.hub-sheet .sys-label { color: var(--h-accent, #FF5A36); }
.hub-sheet .tile, .hub-sheet .btn, .hub-sheet .team-field {
  clip-path: none; border-radius: 12px;
}
.hub-sheet .tile { background: #222228; border-color: transparent; }
.hub-sheet .tile.is-here { background: #2A2A32; border-color: #FF5A36; }
.hub-sheet .tile-ic .ic { color: #FF7A59; }
.hub-sheet .btn-primary {
  background: #FFFFFF; color: #0A0A0A; border-color: transparent; box-shadow: none;
}
.hub-sheet .btn-ghost { background: rgba(255, 255, 255, 0.08); border-color: transparent; }
.hub-sheet-name { font-size: 22px; }
.hub-tier {
  justify-self: start; padding: 3px 10px; border-radius: 999px;
  border: 1px solid; font-size: 12px; font-weight: 700;
}

@media (max-width: 360px) {
  .hero-title { font-size: 40px; }
  .poster { flex-basis: 132px; }
  .wallet-row { grid-template-columns: 1fr; }
}

/* Панель владельца: обложки модулей. */
.admin-cover { display: grid; grid-template-columns: 96px minmax(0, 1fr); gap: 14px; align-items: start; }
.admin-cover-pic {
  width: 96px; aspect-ratio: 2 / 3; display: grid; place-items: center; overflow: hidden;
  background: var(--void-2); border: 1px solid var(--line); text-align: center;
}
.admin-cover-pic img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* Профиль хаба: ник, номер и дата — строкой под именем (26.09.2026). */
.hub-sheet-who { display: flex; flex-wrap: wrap; gap: 4px 12px; margin: -4px 0 8px; font-size: 13px; color: var(--ink-dim); }
.hub-sheet-who b { color: var(--ink); font-weight: 700; }

/* Отряды: состав врага до боя и оба отряда в бою (предложения игрока 28.09.2026). */
.crew-faces { display: flex; gap: 8px; overflow-x: auto; padding: 4px 0 6px; }
.crew-face { flex: 0 0 76px; display: grid; gap: 2px; text-align: center; }
.crew-face img { width: 76px; height: 76px; object-fit: cover; border: 1px solid var(--line); background: var(--void-2); }
.crew-face b { font-size: 12px; line-height: 1.2; }
.fight-crews { padding: 2px 14px 0; }
.crew-strip { display: flex; align-items: center; gap: 5px; overflow-x: auto; scrollbar-width: none; }
.crew-strip::-webkit-scrollbar { display: none; }
.crew-tag { font-family: var(--font-data); font-size: 10px; color: var(--ink-dim); text-transform: uppercase; margin-left: 4px; }
.crew-tag:first-child { margin-left: 0; }
.crew-mini {
  position: relative; flex: 0 0 38px; width: 38px; height: 38px; padding: 0; background: var(--void-2);
  border: 1px solid var(--line); cursor: pointer; overflow: hidden;
}
.crew-mini img { width: 100%; height: 100%; object-fit: cover; display: block; }
.crew-mini-hp { position: absolute; left: 0; right: 0; bottom: 0; height: 3px; background: var(--blood); transform-origin: left; }
.crew-mini.is-down { opacity: 0.35; filter: grayscale(1); }
.crew-mini.is-focus { border-color: var(--gold); box-shadow: 0 0 0 1px var(--gold); }
.crew-mini:disabled { cursor: default; }
.crew-note { margin: 2px 0 0; font-size: 11px; color: var(--ink-dim); line-height: 1.3; }

/* Неделя серии заходов (идея роста №6, 29.09.2026). */
.streak-week { display: grid; grid-template-columns: repeat(7, 1fr); gap: 4px; margin: 6px 0; }
.streak-day {
  display: grid; gap: 2px; place-items: center; padding: 6px 2px; min-height: 52px;
  border: 1px solid var(--line); background: var(--void-2); text-align: center;
}
.streak-day b { font-family: var(--font-data); font-size: 13px; }
.streak-day span { font-size: 9px; line-height: 1.15; color: var(--ink-dim); }
.streak-day.is-done { border-color: var(--sys-deep); }
.streak-day.is-done b { color: var(--sys); }
.streak-day.is-today { box-shadow: 0 0 0 1px var(--sys); }
.streak-day.is-big { border-color: var(--gold); }
.streak-day.is-big span { color: var(--gold); }

/* «В сторис» — заметная, но не главная кнопка (идея роста №2). */
.btn-story { border-color: var(--gold); color: var(--gold); }

/* Турнир недели (bot/game/cup.py). */
.cup-prizes { margin: 6px 0; }
.cup-prizes dd { font-size: 12px; }

/* «Что нового» (webapp/news.js, 29.09.2026). */
.news-dot { display: inline-block; width: 8px; height: 8px; border-radius: 50%; background: #ff4d4d;
  margin-right: 6px; vertical-align: middle; box-shadow: 0 0 0 2px rgba(255, 77, 77, .25); }
.tile .news-dot { margin-left: auto; }
.news-list { display: grid; gap: 10px; }
.news-card { border: 1px solid var(--line); background: var(--void-2); padding: 12px 14px; }
.news-card.is-big { border-color: var(--sys-deep); }
.news-meta { margin: 0; font-size: 12px; color: var(--ink-dim); display: flex; gap: 8px; align-items: center; }
.news-title { margin: 6px 0 6px; font-size: 16px; line-height: 1.3; }
.news-lines { margin: 0; padding-left: 18px; font-size: 13px; line-height: 1.45; }
.news-lines li { margin: 3px 0; }
.news-site { margin-top: 12px; }
.news-sheet .news-card { border: 0; padding: 0; background: none; margin-bottom: 12px; max-height: 60vh; overflow: auto; }
.nw-card { display: grid; gap: 8px; }
.nw-card.is-hidden { opacity: .6; }
.nw-row { display: flex; gap: 8px; flex-wrap: wrap; }
.nw-row .input { flex: 1 1 140px; }

/* Текст раздела до запуска мини-приложения (bot/web/seo.py → pre_html).
   Видят поисковики и тот, у кого скрипт не загрузился; приложение его убирает. */
.seo-pre { max-width: 760px; margin: 0 auto; padding: 24px 16px 40px; color: var(--ink, #e8e8e8);
  font: 15px/1.55 system-ui, -apple-system, "Segoe UI", sans-serif; }
.seo-pre h2 { margin: 22px 0 8px; }
.seo-pre ul { margin: 0; padding-left: 20px; }
.seo-pre li { margin: 3px 0; }
.seo-pre a { color: var(--sys, #5fe0c8); }

/* --------------------------------------------------------------------------
   Рука веером (владелец 13.10.2026): средняя карта сверху, остальные позади,
   листается свайпом и стрелками. Раскладку ставит layoutHand() в app.js.
   -------------------------------------------------------------------------- */
.hand-fan { position: relative; display: flex; align-items: center; gap: 4px; }
.fan-stage {
  position: relative; flex: 1 1 auto; height: 128px; overflow: hidden;
  touch-action: pan-y; user-select: none; -webkit-user-select: none;
}
.fan-card {
  position: absolute; left: 50%; top: 4px; width: 82px; aspect-ratio: 3 / 4.2;
  padding: 0; border: 1px solid var(--line); background: var(--panel); overflow: hidden;
  transform-origin: 50% 100%;
  transition: transform 0.32s cubic-bezier(0.2, 0.9, 0.25, 1), opacity 0.32s, filter 0.32s, box-shadow 0.32s;
  filter: brightness(0.62) saturate(0.8);
  cursor: pointer;
}
.fan-card img { width: 100%; height: 100%; object-fit: cover; display: block; pointer-events: none; }
.fan-card.is-top { filter: none; box-shadow: 0 6px 22px rgba(0, 0, 0, 0.6), 0 0 16px var(--sys-glow, rgba(79, 227, 216, 0.35)); border-color: var(--sys); }
.fan-card.is-cold img { filter: grayscale(1) brightness(0.55); }
.fan-card.is-reads { border-color: var(--upgrade); }
.fan-card.r-edge-gold.is-top { border-color: var(--gold); }
.fan-card.r-edge-platinum.is-top, .fan-card.r-edge-diamond.is-top { border-color: #7FD4E8; }
.fan-card.r-edge-master.is-top { border-color: #C77DFF; }
.fan-card.r-edge-challenger.is-top { border-color: #FF7A59; }
.fan-mult, .fan-cost {
  position: absolute; font-size: 10px; padding: 1px 4px; background: rgba(7, 11, 16, 0.86); color: var(--ink);
}
.fan-mult { left: 3px; top: 3px; color: var(--sys); }
.fan-seen { position: absolute; right: 3px; top: 3px; font-size: 10px; padding: 1px 4px;
  background: rgba(7, 11, 16, 0.86); color: var(--gold); display: inline-flex; gap: 2px; align-items: center; }
.fan-cost { right: 3px; bottom: 3px; display: inline-flex; align-items: center; gap: 2px; }
.fan-cold {
  position: absolute; inset: 0; display: grid; place-items: center;
  font-family: var(--font-data); font-size: 26px; color: var(--ink); text-shadow: 0 2px 8px #000;
}
.fan-arrow {
  flex: 0 0 30px; height: 64px; border: 1px solid var(--line); background: var(--panel);
  color: var(--ink); font-size: 22px; line-height: 1; padding: 0; z-index: 120;
}
.fan-info { display: flex; gap: 8px; align-items: center; min-height: 52px; }
.fan-text { flex: 1 1 auto; display: grid; gap: 1px; min-width: 0; }
.fan-text > * { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.fan-text > .fan-effect { white-space: normal; }
.fan-effect { color: var(--ink-dim); display: -webkit-box; -webkit-line-clamp: 1; -webkit-box-orient: vertical; }
.fan-play { flex: 0 0 auto; min-width: 104px; }

/* Колода в бою и сеты школ одной строкой. */
.deck-strip { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; color: var(--ink-dim); }
.deck-set { font-size: 11px; padding: 1px 6px; border: 1px solid var(--gold); color: var(--gold); }

/* Карты, сыгранные в раунде, — влетают со своей стороны. */
.played-strip { display: flex; justify-content: space-between; gap: 8px; padding: 2px 14px; }
.fight > .played-strip { flex: 0 0 auto; }
.played { display: flex; align-items: center; gap: 6px; max-width: 48%; }
.played img { width: 34px; aspect-ratio: 3 / 4.2; object-fit: cover; border: 1px solid var(--line); }
.played-you { animation: played-in-left 0.45s ease-out both; }
.played-foe { margin-left: auto; flex-direction: row-reverse; text-align: right; animation: played-in-right 0.45s ease-out both; }
.played-you img { border-color: var(--sys); }
.played-foe img { border-color: var(--blood); }
@keyframes played-in-left { from { opacity: 0; transform: translateX(-40px) rotate(-12deg); } }
@keyframes played-in-right { from { opacity: 0; transform: translateX(40px) rotate(12deg); } }

/* Лог пузырями: свои слева бирюзой, его — справа кровью. */
.fight-log .log-line { padding: 5px 9px; max-width: 88%; border-left: 2px solid transparent; background: rgba(255, 255, 255, 0.025); }
.fight-log .log-line.from-you { align-self: flex-start; border-left-color: var(--sys); }
.fight-log .log-line.from-foe { align-self: flex-end; border-left: 0; border-right: 2px solid var(--blood); text-align: right; }
.fight-log .log-line.from-sys { align-self: center; text-align: center; color: var(--ink-dim); font-size: 13px; background: none; }
.fight-log .log-line.from-you b { color: var(--sys); }
.fight-log .log-round {
  align-self: center; font-family: var(--font-data); font-size: 10px; letter-spacing: 0.22em;
  text-transform: uppercase; color: var(--ink-faint);
}

/* Веер забирает высоту у сцены, а не у лога: сцена сжимается первой. */
.fight:has(.hand-fan) > .fight-scene { flex: 1 4 84px; max-height: 190px; }

/* Владелец 13.10.2026: «в бою две нижние кнопки не видны». Веер (128 px) и
   строка карты (52+ px) не умели ужиматься, и на 390×640 — это Telegram на
   обычном телефоне — «Статы противника» и «Сбежать» уезжали за нижний край, а
   сцена сплющивалась до полоски. Теперь рука — свой блок между рядами кнопок:
   он ужимается и листается сам, а Удар/Блок/Уклон и выход видны всегда. */
.fight-actions > .fight-hand {
  flex: 0 1 auto; min-height: 0; overflow-y: auto; overscroll-behavior: contain;
  display: flex; flex-direction: column; gap: 6px; scrollbar-width: none;
}
.fight-hand > * { flex: 0 0 auto; }
.fight:has(.hand-fan) > .fight-scene { min-height: 56px; }
/* Кнопкам — приоритет: сперва ужимаются сцена и лог до своих минимумов, потом рука.
   Коэффициент кнопкам — 1, а не дробь: при сумме меньше единицы flexbox ужимает
   лишь долю нехватки, и низ снова уезжал (360×560). */
.fight:has(.hand-fan) > .fight-scene { flex-shrink: 1000; }
.fight:has(.hand-fan) > .fight-log { flex-shrink: 250; }
.fight:has(.hand-fan) > .fight-actions { flex-shrink: 1; }

/* Невысокий экран: веер и строка карты компактнее, шапка плотнее. */
@media (max-height: 760px) {
  .fan-stage { height: 104px; }
  .fan-card { width: 66px; }
  .fan-arrow { height: 54px; }
  .fan-info { min-height: 40px; }
  .fan-text .fan-effect { display: none; }
  .fight-hand .deck-strip { font-size: 12px; }
}
@media (max-height: 680px) {
  .fan-stage { height: 86px; }
  .fan-card { width: 54px; }
  .fan-arrow { height: 46px; flex-basis: 26px; }
  .fan-mult, .fan-cost { font-size: 9px; padding: 0 3px; }
  .fan-play { min-width: 92px; }
  .fight-top { padding: 6px 14px 4px; gap: 4px; }
  .fight > .hint { padding: 3px 14px; font-size: 11px; }
  .fight-actions { gap: 5px; padding-top: 6px; }
  .fight-actions .btn-act { padding-top: 6px; padding-bottom: 6px; }
  /* Строка колоды повторяет «1/N» под веером; сеты школ — в карточке колоды. */
  .fight-hand .deck-strip { display: none; }
  .fan-play { padding-top: 6px; padding-bottom: 6px; }
}
/* Совсем низкий экран (Android 360×640 минус шапка Telegram): подсказка уходит,
   модификатор — в одну строку, веер ещё мельче. */
@media (max-height: 600px) {
  .fight > .hint { display: none; }
  .fight > .mod-line { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .fan-stage { height: 74px; }
  .fan-card { width: 46px; }
  .fan-arrow { height: 40px; }
  .combatant-name { font-size: 13px; }
}
.fight .played span.small { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.fight .played { min-width: 0; }

/* Сохранённые колоды (deck.PRESETS). */
.deck-presets { display: grid; gap: 6px; margin-top: 8px; }
.deck-preset { display: flex; gap: 6px; align-items: center; flex-wrap: wrap; }
.deck-preset > span { flex: 1 1 140px; min-width: 0; }

/* Розыгрыш карты: призрак верхней карты летит в центр сцены (flyCard). */
.card-fly { position: fixed; z-index: 200; aspect-ratio: 3 / 4.2; object-fit: cover; pointer-events: none;
  box-shadow: 0 0 24px var(--sys-glow); border: 1px solid var(--sys); }

/* Владелец 13.10.2026: «в бою из-за вкладки уникальных техник ломаются
   кнопки, также с союзниками». Приём — узкая четвёртая кнопка в ряду
   действий; отряды — плотная полоса, подсказка в одну строку. */
.fight-actions .btn-row > .btn-skill { flex: 0 0 52px; padding: 6px 4px; gap: 1px;
  border-color: color-mix(in srgb, var(--upgrade) 55%, transparent);
  background: color-mix(in srgb, var(--upgrade) 16%, var(--panel)); }
.btn-skill .icon, .btn-skill svg { width: 20px; height: 20px; }
.skill-line { width: 100%; display: grid; gap: 2px; min-height: 52px; }
.skill-line b { font-size: 14px; }
.skill-line .skill-desc { font-family: inherit; font-size: 12px; color: var(--ink-dim); line-height: 1.3; }
.crew-mini { flex-basis: 32px; width: 32px; height: 32px; }
.crew-note { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; font-size: 10px; }
@media (max-height: 760px) {
  .crew-mini { flex-basis: 28px; width: 28px; height: 28px; }
}
.fight-crews { padding-bottom: 4px; border-bottom: 1px solid var(--line-soft); }
/* Низкий экран с фильтром «Все/Удары/Эффекты» и кнопкой приёма: подпись под
   действием — не больше двух строк, фильтр плотнее, иначе «Разыграть»
   уезжает за прокрутку руки (360×560, 13.10.2026). */
@media (max-height: 680px) {
  .fight-actions .btn-act .act-sub { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
  .fight-hand .fight-kinds { margin: 0; }
  .fight-hand .fight-kinds .chip { padding: 4px 8px; }
}
@media (max-height: 600px) {
  .fan-stage { height: 66px; }
  .fan-card { width: 42px; }
}
/* Совсем низкий экран: подсказку про навал прячем, пока навала нет — она
   повторена в подсказке над кнопкой врага (title). */
@media (max-height: 600px) {
  .fight-crews:not(.is-focus) .crew-note { display: none; }
}

/* Вся рука сеткой (бой, кнопка «все»). */
.hand-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(96px, 1fr));
  gap: 8px;
  max-height: 60vh;
  overflow-y: auto;
}
.hand-cell {
  display: grid;
  gap: 2px;
  justify-items: center;
  padding: 4px;
  text-align: center;
  color: var(--ink);
  background: var(--panel-2, rgba(255, 255, 255, 0.03));
  border: 1px solid var(--line-soft);
  font: inherit;
  cursor: pointer;
}
.hand-cell img { width: 100%; aspect-ratio: 5 / 7; object-fit: cover; }
.hand-cell.is-cold { opacity: 0.55; }
.hand-cell-name { font-weight: 600; line-height: 1.15; }

/* Банда: звания, титулы, наёмные, «как устроено» (14.10.2026). */
.gang-rank {
  display: inline-block;
  font-size: 11px;
  padding: 1px 6px;
  color: var(--ink-dim);
  border: 1px solid var(--line-soft);
}
.gang-rank.is-titled { color: var(--gold, #E8B54B); border-color: currentColor; }
.gang-rank.is-hire { color: var(--sys); border-color: var(--sys-deep); }
.gang-title-btn { padding: 0 6px; min-height: 0; font-size: 11px; }
.gang-how { margin: 8px 0; }
.gang-how-head {
  width: 100%;
  justify-content: space-between;
  background: none;
  border: 0;
  color: inherit;
  font: inherit;
  padding: 0;
  cursor: pointer;
}
.gang-how-list { margin: 8px 0 0; padding-left: 18px; display: grid; gap: 6px; }

/* Улица на карте Сеула (14.10.2026). */
.street-plate {
  position: relative;
  width: 100%;
  aspect-ratio: 720 / 760;
  max-height: 70vh;
  background: var(--void-2);
  border: 1px solid var(--line);
  overflow: hidden;
}
.street-pin .pin-dot { width: 17px; height: 17px; background: var(--ink-faint); }
.street-pin.is-mine .pin-dot { background: var(--sys); box-shadow: 0 0 10px var(--sys); }
.street-pin.is-held .pin-dot { background: var(--good); }
.street-pin.is-down .pin-dot { background: var(--gold, #E8B54B); }
.street-pin.is-abandoned .pin-dot { background: var(--blood); }
.street-pin.is-locked { opacity: 0.45; }
.pin-sub {
  font-size: 10px;
  line-height: 1.1;
  color: var(--ink-dim);
  text-shadow: 0 0 4px var(--void), 0 0 8px var(--void);
  white-space: nowrap;
}
.street-pin.is-mine .pin-name { color: var(--sys); }

/* Дела банды, уровень, соперник, лента (bot/game/gang_jobs.py, 14.10.2026). */
.gang-level { margin: 8px 0 4px; }
.gang-job { margin-top: 8px; }
.gang-job.is-done { opacity: .72; border-color: var(--good); }
.gang-job.is-done .season-bar i { background: var(--good); }
.gang-rival { margin-top: 10px; }
.gang-log { display: grid; gap: 4px; max-height: 240px; overflow-y: auto; }
.gang-log-row { display: grid; grid-template-columns: 86px 1fr; gap: 8px; align-items: baseline; }

/* «Перед боем» на экране кампании (14.10.2026): голод, дух и раны видны там,
   где игрок решает драться. */
.ready-warn { border-color: color-mix(in srgb, var(--blood) 55%, var(--line)); background: color-mix(in srgb, var(--blood) 10%, var(--panel)); }

/* Тема боя по состоянию подопечного (14.10.2026). Меняются фон и акцент
   экрана, портреты не трогаем — без фильтров. */
.fight.theme-ui { --sys: #c9b8ff; --sys-deep: #6b55c9; --line: #3a3352;
  background: radial-gradient(120% 70% at 50% 0%, #1b1530 0%, #07060c 60%, #000 100%); }
.fight.theme-ui-control { --sys: #a98bff; --sys-deep: #5b3fd1; --line: #3d2f6e;
  background: radial-gradient(120% 70% at 50% 0%, #241a4a 0%, #0b0818 65%); }
.fight.theme-awake { --sys: #ffd36a; --sys-deep: #b98a1d; --line: #4a3d1c;
  background: radial-gradient(120% 70% at 50% 0%, #2c2410 0%, #0d0b06 65%); }
.fight.theme-rage { --sys: #ff6b5e; --sys-deep: #a8261d; --line: #4d1f1c;
  background: radial-gradient(120% 70% at 50% 0%, #2e0f0d 0%, #0d0606 65%); }
.fight.theme-path { --sys: #5ef0d8; --sys-deep: #1c9c8a;
  background: radial-gradient(120% 70% at 50% 0%, #0d2a27 0%, var(--void) 65%); }
.fight[class*="theme-"] { transition: background 0.6s ease; }
.ui-skip { margin: 0 0 10px; padding: 10px 12px; border: 1px solid var(--sys-deep);
  background: color-mix(in srgb, var(--sys) 10%, transparent); font-size: 13px; line-height: 1.45; }
.ui-skip details { margin-top: 6px; }
.ui-skip summary { cursor: pointer; color: var(--sys); }
.ui-skip details p { margin: 4px 0 0; font-size: 12px; color: var(--ink-dim); }
.ui-skip b { color: var(--sys); }

/* Карта мира — одна на Lvl Up («Города») и WAR («Мир»), 17.10.2026.
   Тема — переменными: здесь окно Системы, в webapp/war.css — тёплая. */
.wm {
  --wm-sea0: #0d2230; --wm-sea1: #040a0f; --wm-land: #0f1c25; --wm-land-line: #22394a; --wm-key: #123033;
  --wm-key-line: #3c7a73; --wm-label: #4f7f7a; --wm-hatch: rgba(94, 240, 216, 0.05); --wm-line: #1f3a44;
  --wm-on: var(--sys); --wm-on-ink: #04201c; --wm-bg: #040a0f;
  position: relative; overflow: hidden; padding: 30px 0 46px; border-radius: 16px; border: 1px solid var(--wm-line);
  background: var(--wm-bg); box-shadow: 0 10px 30px rgba(0, 0, 0, 0.4), inset 0 0 40px rgba(0, 0, 0, 0.6);
}
.wm.is-loading { padding: 40px 16px; }
.wm-skel { display: block; height: 120px; border-radius: 10px; background: linear-gradient(90deg, var(--wm-land), var(--wm-key), var(--wm-land)) 0 0 / 200% 100%; animation: wm-shimmer 1.4s linear infinite; }
@keyframes wm-shimmer { to { background-position: -200% 0; } }
.wm-svg { display: block; width: 100%; height: auto; touch-action: manipulation; }
.wm-sea0 { stop-color: var(--wm-sea0); }
.wm-sea1 { stop-color: var(--wm-sea1); }
.wm-hatch { stroke: var(--wm-hatch); }
.wm-land { fill: var(--wm-land); stroke: var(--wm-land-line); }
.wm-key { fill: var(--wm-key); stroke: var(--wm-key-line); }
.wm-label { fill: var(--wm-label); opacity: 0.6; font-family: var(--font-data); letter-spacing: 0.25em; text-anchor: middle;
  font-weight: 800; pointer-events: none; }
.wm-pt { cursor: pointer; }
.wm-hit { fill: transparent; }
.wm-dot { fill: var(--c); stroke: #05080a; filter: drop-shadow(0 0 2px var(--c)); }
.wm-star { fill: none; stroke: var(--gold); opacity: 0.9; }
.wm-ev { fill: none; stroke: var(--blood); opacity: 0.8; transform-box: fill-box; transform-origin: center; }
.wm-here { fill: none; stroke: #f0c35a; transform-box: fill-box; transform-origin: center; }
.wm-pt text { fill: var(--ink); paint-order: stroke; stroke: #05080a; pointer-events: none; font-weight: 700; }
.wm-pt.is-mine text, .wm-pt.is-here text { fill: var(--gold); }
.wm-pt.is-far { opacity: 0.55; }
.wm-pt.is-pick .wm-dot { stroke: #fff3e6; transform-box: fill-box; transform-origin: center; }
.wm-pt.is-seoul .wm-dot { stroke: #fff3e6; }
.wm-zoom { position: absolute; left: 8px; right: 8px; bottom: 8px; display: flex; gap: 4px; overflow-x: auto; scrollbar-width: none; }
.wm-zoom::-webkit-scrollbar { display: none; }
.wm-zoom button {
  flex: 0 0 auto; min-height: 30px; padding: 4px 10px; border-radius: 15px; font-size: 11px; font-weight: 800;
  color: var(--ink-dim); background: rgba(5, 8, 10, 0.8); border: 1px solid var(--wm-line); backdrop-filter: blur(4px);
}
.wm-zoom button.is-on { color: var(--wm-on-ink); background: var(--wm-on); border-color: var(--wm-on); }
.wm-legend { position: absolute; top: 8px; left: 8px; right: 8px; display: flex; flex-wrap: wrap; gap: 4px 10px;
  font-size: 10px; color: var(--ink-dim); pointer-events: none; }
.wm-legend span { display: inline-flex; align-items: center; gap: 4px; text-shadow: 0 1px 2px #000; }
.wm-legend span::before { content: ""; width: 8px; height: 8px; border-radius: 50%; background: var(--c); box-shadow: 0 0 4px var(--c); }
@media (prefers-reduced-motion: no-preference) {
  html:not([data-fx="off"]) .wm-ev { animation: wm-ping 1.8s ease-out infinite; }
  html:not([data-fx="off"]) .wm-here { animation: wm-ping 2.2s ease-out infinite; }
  html:not([data-fx="off"]) .wm-pt.is-pick .wm-dot { animation: wm-beat 1.4s ease-in-out infinite; }
}
@keyframes wm-ping { from { transform: scale(0.6); opacity: 0.9; } to { transform: scale(1.8); opacity: 0; } }
@keyframes wm-beat { 50% { transform: scale(1.35); } }

/* ==========================================================================
   Lvl Up: вид 17.10.2026 — «таким же методом» (как WAR): значки, плитки,
   строки, свёрнутые правила, текстура и движение. Тема — окно Системы.
   ========================================================================== */

/* Текстура: зерно и голографическая сетка поверх старой линовки. */
#app::before {
  content: ""; position: fixed; inset: 0; pointer-events: none; z-index: -1;
  background:
    url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='180' height='180'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='.9' numOctaves='2' stitchTiles='stitch'/><feColorMatrix values='0 0 0 0 .7 0 0 0 0 1 0 0 0 0 1 0 0 0 .07 0'/></filter><rect width='100%25' height='100%25' filter='url(%23n)'/></svg>"),
    repeating-linear-gradient(90deg, rgba(79, 227, 216, 0.018) 0 1px, transparent 1px 44px);
}
#view .panel {
  background: linear-gradient(180deg, rgba(79, 227, 216, 0.045), transparent 42%), var(--panel);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.04);
}

/* Шапка секции: значок в срезанной рамке. */
.lv-sec { display: grid; gap: 10px; }
.lv-head { display: flex; align-items: center; gap: 10px; }
.lv-head-main { flex: 1; min-width: 0; }
.lv-head-ic {
  width: 34px; height: 34px; padding: 7px; flex: 0 0 auto; color: var(--sys);
  background: rgba(79, 227, 216, 0.1); box-shadow: inset 0 0 0 1px rgba(79, 227, 216, 0.35);
  clip-path: polygon(6px 0, 100% 0, 100% calc(100% - 6px), calc(100% - 6px) 100%, 0 100%, 0 6px);
}
.lv-sub { margin: 2px 0 0; font-size: 13px; color: var(--ink-dim); }
.lv-sec.is-hot .lv-head-ic { color: var(--blood); background: rgba(255, 77, 77, 0.1); box-shadow: inset 0 0 0 1px rgba(255, 77, 77, 0.4); }
.lv-sec.is-gold .lv-head-ic { color: var(--gold); background: rgba(255, 198, 92, 0.1); box-shadow: inset 0 0 0 1px rgba(255, 198, 92, 0.4); }

/* Плитки чисел */
.lv-tiles { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 6px; }
.lv-tiles.is-2 { grid-template-columns: 1fr 1fr; }
.lv-tiles.is-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
.lv-tile {
  display: grid; justify-items: center; gap: 1px; padding: 9px 4px 8px; text-align: center; min-width: 0;
  background: linear-gradient(180deg, rgba(79, 227, 216, 0.06), transparent), var(--void-2);
  border: 1px solid var(--line-soft);
  clip-path: polygon(6px 0, 100% 0, 100% calc(100% - 6px), calc(100% - 6px) 100%, 0 100%, 0 6px);
}
.lv-tile .ic { width: 18px; height: 18px; color: var(--sys); margin-bottom: 2px; }
.lv-tile b { font-family: var(--font-data); font-size: 16px; line-height: 1.2; max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.lv-tile span { font-size: 10px; color: var(--ink-faint); text-transform: uppercase; letter-spacing: 0.05em; line-height: 1.2; }
.lv-tile.is-gold .ic, .lv-tile.is-gold b { color: var(--gold); }
.lv-tile.is-hot .ic, .lv-tile.is-hot b { color: var(--blood); }
.lv-tile.is-good .ic, .lv-tile.is-good b { color: var(--good); }
.lv-tile.is-dim { opacity: 0.6; }

/* Строки со значком */
.lv-rows { list-style: none; margin: 0; padding: 0; display: grid; gap: 4px; }
.lv-rows li {
  display: flex; align-items: center; gap: 10px; min-height: 38px; padding: 7px 10px; font-size: 14px; line-height: 1.35;
  background: var(--void-2); border-left: 2px solid var(--line-soft);
}
.lv-rows li > .ic { color: var(--sys); flex: 0 0 auto; }
.lv-rows li > span { flex: 1; min-width: 0; }
.lv-rows li > em { font-style: normal; font-family: var(--font-data); font-size: 12px; color: var(--ink-dim); white-space: nowrap; }
.lv-rows li.is-done { border-left-color: var(--good); }
.lv-rows li.is-done > .ic { color: var(--good); }
.lv-rows li.is-now { border-left-color: var(--sys); background: linear-gradient(90deg, rgba(79, 227, 216, 0.12), transparent 70%), var(--void-2); }
.lv-rows li.is-lock { opacity: 0.55; }
.lv-rows li.is-lock > .ic { color: var(--ink-faint); }
.lv-rows li.is-hot { border-left-color: var(--blood); }
.lv-rows li.is-hot > .ic { color: var(--blood); }
.lv-rows li.is-gold > .ic { color: var(--gold); }

/* «Как это работает» */
.lv-how { border-top: 1px solid var(--line-soft); padding-top: 2px; }
.lv-how summary { display: flex; align-items: center; gap: 8px; min-height: 40px; cursor: pointer; list-style: none;
  color: var(--ink-dim); font-size: 13px; font-weight: 700; }
.lv-how summary::-webkit-details-marker { display: none; }
.lv-how summary > span { flex: 1; }
.lv-how-arrow { transition: transform 0.3s cubic-bezier(0.22, 1, 0.36, 1); }
.lv-how[open] .lv-how-arrow { transform: rotate(180deg); }
.lv-how[open] summary { color: var(--sys); }
.lv-how .lv-rows li { font-size: 13px; color: var(--ink-dim); }
.lv-how .lv-rows li b { color: var(--ink); }
.lv-guide.panel { padding: 4px 14px; }
.lv-guide.panel .lv-how { border-top: 0; }

/* Полоса, портрет, фишки */
.lv-bar { position: relative; height: 6px; background: var(--void-2); box-shadow: inset 0 0 0 1px var(--line-soft); overflow: hidden; }
.lv-bar i { position: absolute; inset: 0 auto 0 0; width: var(--w); transform-origin: left;
  background: linear-gradient(90deg, var(--sys-deep), var(--sys)); box-shadow: 0 0 8px var(--sys-glow); }
.lv-bar.is-gold i { background: linear-gradient(90deg, #c58f2e, var(--gold)); box-shadow: 0 0 8px rgba(255, 198, 92, 0.35); }
.lv-bar.is-hot i { background: linear-gradient(90deg, #b62f2f, var(--blood)); box-shadow: 0 0 8px rgba(255, 77, 77, 0.35); }
.lv-face {
  position: relative; display: inline-grid; place-items: center; flex: 0 0 auto; width: 44px; height: 44px; overflow: hidden;
  background: var(--void-2); box-shadow: 0 0 0 1px var(--sys), 0 0 14px var(--sys-glow);
  clip-path: polygon(8px 0, 100% 0, 100% calc(100% - 8px), calc(100% - 8px) 100%, 0 100%, 0 8px);
}
.lv-face img { width: 100%; height: 100%; object-fit: cover; }
.lv-face i { display: none; font-style: normal; font-weight: 900; color: var(--ink-dim); }
.lv-face.is-blank img { display: none; }
.lv-face.is-blank i { display: block; }
.lv-face.is-big { width: 68px; height: 68px; }
.lv-face.is-hot { box-shadow: 0 0 0 1px var(--blood), 0 0 16px rgba(255, 77, 77, 0.3); }
.lv-chips { display: flex; flex-wrap: wrap; gap: 6px; }
.lv-chip {
  display: inline-flex; align-items: center; gap: 5px; padding: 4px 9px; font-size: 12px; font-weight: 700;
  color: var(--ink); background: rgba(79, 227, 216, 0.08); border: 1px solid rgba(79, 227, 216, 0.3);
}
.lv-chip .ic { width: 14px; height: 14px; color: var(--sys); }
.lv-chip.is-gold { border-color: rgba(255, 198, 92, 0.45); background: rgba(255, 198, 92, 0.08); }
.lv-chip.is-gold .ic { color: var(--gold); }
.lv-chip.is-hot { border-color: rgba(255, 77, 77, 0.45); background: rgba(255, 77, 77, 0.08); }
.lv-chip.is-hot .ic { color: var(--blood); }

/* Карточка «противник / цель»: портрет слева, текст и кнопка справа. */
.lv-foe { display: grid; grid-template-columns: auto 1fr; gap: 12px; align-items: center; }
.lv-foe-main { display: grid; gap: 4px; min-width: 0; }
.lv-foe-main h2, .lv-foe-main h3 { margin: 0; }
.lv-foe-sub { font-size: 13px; color: var(--ink-dim); }
.lv-vs { display: grid; grid-template-columns: 1fr auto 1fr; align-items: center; gap: 6px; font-family: var(--font-data); }
.lv-vs b { font-size: 18px; }
.lv-vs i { font-style: normal; font-weight: 900; color: var(--sys); text-shadow: 0 0 10px var(--sys-glow); }

/* Нажатие — пружиной; кнопки, плитки, строки-кнопки. */
#view .btn, #view .tile, .lv-tile { transition: transform 0.15s cubic-bezier(0.22, 1, 0.36, 1), box-shadow 0.2s, border-color 0.2s; }
#view .btn:active:not(:disabled), #view .tile:active { transform: scale(0.97); }

/* Движение: только при входе на экран (#view.view-enter, fx.css), лесенкой по --i. */
@media (prefers-reduced-motion: no-preference) {
  html:not([data-fx="off"]) #view.view-enter [style*="--i"] { animation: lv-rise 0.4s cubic-bezier(0.22, 1, 0.36, 1) both;
    animation-delay: calc(0.12s + var(--i) * 0.04s); }
  html:not([data-fx="off"]) #view.view-enter .lv-bar i { animation: lv-grow 0.9s cubic-bezier(0.22, 1, 0.36, 1) 0.2s both; }
  html:not([data-fx="off"]) #view.view-enter .lv-face.is-big { animation: lv-pop 0.5s cubic-bezier(0.22, 1, 0.36, 1) 0.1s both; }
  html:not([data-fx="off"]) .lv-how[open] .lv-rows li { animation: lv-slide 0.3s cubic-bezier(0.22, 1, 0.36, 1) both; animation-delay: calc(var(--i) * 0.035s); }
  html:not([data-fx="off"]) .lv-head-ic { animation: lv-glint 5s ease-in-out infinite; }
}
@keyframes lv-rise { from { opacity: 0; transform: translateY(8px); } }
@keyframes lv-slide { from { opacity: 0; transform: translateX(-6px); } }
@keyframes lv-grow { from { transform: scaleX(0); } }
@keyframes lv-pop { from { opacity: 0; transform: scale(0.7); } }
@keyframes lv-glint { 0%, 88%, 100% { filter: none; } 94% { filter: drop-shadow(0 0 6px var(--sys)); } }

@media (max-width: 380px) {
  .lv-tiles.is-4 { grid-template-columns: 1fr 1fr; }
  .lv-tile b { font-size: 14px; }
}

/* Глава: блок «Сейчас» */
.lv-now { display: grid; gap: 10px; background: radial-gradient(120% 120% at 0% 0%, rgba(79, 227, 216, 0.14), transparent 60%), var(--panel) !important; }
.lv-now-top { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.lv-now-top .sys-label { display: inline-flex; align-items: center; gap: 6px; }
.lv-act { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin: 0; font-size: 17px; }
.lv-intro { margin: 0; padding: 10px 12px; font-size: 14px; line-height: 1.5; color: var(--ink); font-style: italic;
  border-left: 2px solid var(--sys); background: rgba(79, 227, 216, 0.05); }
.lv-row-sub { display: flex; align-items: flex-start; gap: 8px; margin: 0; font-size: 14px; color: var(--ink-dim); }
.lv-row-sub .ic { color: var(--sys); margin-top: 2px; flex: 0 0 auto; }
.lv-now .btn-primary { min-height: 48px; font-size: 15px; }

/* Города: карточка и строки списка */
.lv-city { display: grid; gap: 10px; border-left: 3px solid var(--c); }
.lv-city-dot { width: 52px; height: 52px; border-radius: 50%; background: radial-gradient(circle, var(--c), transparent 70%); box-shadow: 0 0 20px var(--c); }
.lv-city .lv-holder > .ic { color: var(--c); }
.lv-cities { display: grid; gap: 4px; padding-top: 4px; }
.lv-crow {
  display: grid; grid-template-columns: auto 1fr auto; gap: 10px; align-items: center; padding: 9px 10px; text-align: left;
  color: var(--ink); font: inherit; background: var(--void-2); border: 0; border-left: 2px solid var(--c);
}
.lv-crow.is-pick { background: linear-gradient(90deg, rgba(79, 227, 216, 0.12), transparent 70%), var(--void-2); }
.lv-crow.is-here { box-shadow: inset 0 0 0 1px rgba(240, 195, 90, 0.5); }
.lv-crow-dot { width: 10px; height: 10px; border-radius: 50%; background: var(--c); box-shadow: 0 0 8px var(--c); }
.lv-crow-name { display: flex; flex-wrap: wrap; align-items: center; gap: 2px 6px; min-width: 0; }
.lv-crow-name b { font-size: 14px; }
.lv-crow-name .ic { width: 13px; height: 13px; color: var(--gold); }
.lv-crow-name .lv-crow-ev { color: var(--blood); }
.lv-crow-name em { flex-basis: 100%; font-style: normal; font-size: 11px; color: var(--ink-faint); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.lv-crow-cost { display: inline-flex; align-items: center; gap: 3px; font-family: var(--font-data); font-size: 12px; color: var(--ink-dim); white-space: nowrap; }
.lv-crow-cost .ic { width: 13px; height: 13px; color: var(--sys); }

/* Полоса подвкладок: сетка в две строки остаётся (решение владельца), у
   каждой вкладки — значок, выбранная подсвечена. */
.subbar > .btn { display: inline-flex; align-items: center; justify-content: center; gap: 5px; }
.subbar > .btn .ic { width: 15px; height: 15px; flex: 0 0 auto; }
.subbar > .btn:not(.btn-ghost) { color: var(--sys); box-shadow: inset 0 -2px 0 var(--sys), 0 0 12px var(--sys-glow); }

/* Задания */
.lv-quest { display: grid; gap: 10px; }
.lv-quest-bar { display: grid; grid-template-columns: 1fr auto; gap: 10px; align-items: center; }
.lv-quest-bar .lv-bar { height: 10px; }
.lv-quest-bar b { font-family: var(--font-data); color: var(--sys); }
.lv-routes { display: grid; gap: 8px; }
.lv-route {
  display: grid; grid-template-columns: auto 1fr; gap: 10px 12px; align-items: center; padding: 12px; text-align: left;
  color: var(--ink); font: inherit; background: linear-gradient(90deg, rgba(79, 227, 216, 0.07), transparent 60%), var(--void-2);
  border: 1px solid var(--line-soft);
  clip-path: polygon(8px 0, 100% 0, 100% calc(100% - 8px), calc(100% - 8px) 100%, 0 100%, 0 8px);
}
.lv-route:hover { border-color: var(--sys); }
.lv-route > .lv-chip { grid-column: 2; justify-self: start; }
.lv-route-diff { grid-row: span 2; display: grid; gap: 3px; }
.lv-route-diff i { width: 8px; height: 8px; transform: rotate(45deg); background: var(--line-soft); }
.lv-route-diff i.is-on { background: var(--sys); box-shadow: 0 0 6px var(--sys); }
.lv-route-main { display: grid; gap: 3px; min-width: 0; }
.lv-route-main b { font-size: 15px; }
.lv-route-main > span { display: flex; align-items: flex-start; gap: 6px; font-size: 13px; color: var(--ink-dim); }
.lv-route-main .ic { width: 14px; height: 14px; color: var(--sys); flex: 0 0 auto; margin-top: 2px; }
.lv-route-ch { color: var(--sys) !important; }
.tile-ic:has(.lv-face) { width: auto; }
.tile .lv-face { width: 40px; height: 40px; }

/* Артефакты, лавка */
.lv-relic { display: grid; grid-template-columns: auto 1fr; gap: 12px; align-items: start; padding: 10px; background: var(--void-2);
  border-left: 2px solid var(--line-soft); }
.lv-relic.is-mine { border-left-color: var(--gold); background: linear-gradient(90deg, rgba(255, 198, 92, 0.08), transparent 60%), var(--void-2); }
.lv-relic.is-locked .lv-face { filter: grayscale(0.7); }
.lv-relic .row { gap: 8px; }
.lv-face.is-gold { box-shadow: 0 0 0 1px var(--gold), 0 0 14px rgba(255, 198, 92, 0.3); }
.lv-face i .ic { width: 22px; height: 22px; color: var(--gold); }
.lv-cardtile { grid-template-columns: auto 1fr auto; }
.lv-thumb { display: block; width: 42px; height: 58px; overflow: hidden; flex: 0 0 auto; box-shadow: 0 0 0 1px var(--line), 0 4px 10px rgba(0, 0, 0, 0.4); }
.lv-thumb img { width: 100%; height: 100%; object-fit: cover; }
.lv-price { display: inline-flex; align-items: center; gap: 4px; font-family: var(--font-data); font-size: 13px; color: var(--gold); white-space: nowrap; }
.lv-price .ic { width: 14px; height: 14px; }
.lv-trade { display: grid; grid-template-columns: auto 1fr; gap: 12px; align-items: start; }

/* Приложение в хабе (bot/web/pwa.py, 17.10.2026) */
.hub-app { display: grid; grid-template-columns: 56px 1fr auto auto; gap: 12px; align-items: center;
  padding: 12px; border: 1px solid var(--line); border-radius: 16px; background: var(--void-2); }
.hub-app img { border-radius: 14px; }
.hub-app-text { display: grid; gap: 2px; min-width: 0; }
.hub-app-text small { color: var(--ink-dim); }

/* Арена и заявки: лица бойцов (18.10.2026). */
.lv-duel { display: grid; grid-template-columns: 1fr auto 1fr; gap: 10px; align-items: start; }
.lv-duel-side { display: grid; justify-items: center; gap: 6px; text-align: center; min-width: 0; }
.lv-duel-side .meter { width: 100%; }
.lv-duel-side.is-foe .lv-face { box-shadow: 0 0 0 1px var(--blood), 0 0 16px rgba(255, 77, 77, 0.25); }
.lv-duel-vs { align-self: center; font-weight: 900; letter-spacing: 0.12em; color: var(--ink-dim); font-size: 13px; }
.lv-call { gap: 12px; align-items: center; }
.lv-call .lv-face.is-big { width: 52px; height: 52px; flex: none; }
.lv-call-main { flex: 1; min-width: 0; }

/* Задания: лицо цели (18.10.2026). */
.lv-quest-head { display: flex; gap: 12px; align-items: center; justify-content: flex-start; }
.lv-quest-head h3 { flex: 1; min-width: 0; }
.lv-route:has(.lv-route-face) { grid-template-columns: auto auto 1fr; }
.lv-route .lv-route-face { width: 48px; height: 48px; grid-row: span 2; }
.lv-route:has(.lv-route-face) > .lv-chip { grid-column: 3; }

/* Доска заказов района (18.10.2026). */
.board-list { display: grid; gap: 8px; }
.board-order { display: grid; grid-template-columns: auto 1fr auto; gap: 12px; align-items: center; padding: 10px;
  background: var(--void-2); border-left: 2px solid var(--sys); animation: lv-pop 0.4s ease both;
  animation-delay: calc(var(--i, 0) * 60ms); }
.board-order.is-done { border-left-color: var(--good, #5BD38A); opacity: 0.6; }
.board-main { display: grid; gap: 2px; min-width: 0; }
.board-pay { display: flex; align-items: center; gap: 4px; font-size: 12px; color: var(--gold); }
.board-pay .ic { width: 14px; height: 14px; }
.board-done { color: var(--good, #5BD38A); }
.board-done .ic { width: 22px; height: 22px; }

/* Трофеи: охота и коллекции (18.10.2026). */
.feat-hunt, .feat-col { display: grid; grid-template-columns: auto 1fr auto; gap: 12px; align-items: center; padding: 10px;
  background: var(--void-2); border-left: 2px solid var(--line-soft); animation: lv-pop 0.4s ease both;
  animation-delay: calc(min(var(--i, 0), 12) * 40ms); }
.feat-hunt.is-on { border-left-color: var(--gold); background: linear-gradient(90deg, rgba(255, 198, 92, 0.1), transparent 60%), var(--void-2); }
.feat-hunt.is-done, .feat-col.is-done { border-left-color: var(--good, #5BD38A); }
.feat-main { display: grid; gap: 3px; min-width: 0; }
.feat-chance { display: flex; align-items: center; gap: 4px; font-size: 12px; color: var(--gold); }
.feat-faces { display: flex; }
.feat-faces img { width: 30px; height: 30px; border-radius: 50%; object-fit: cover; margin-left: -10px; border: 2px solid var(--void-2); }
.feat-faces img:first-child { margin-left: 0; }

/* Донат (18.10.2026): ссылки и две цены у набора. */
.donate-link-row { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
.donate-link-row .btn { display: inline-flex; align-items: center; justify-content: center; gap: 6px; text-decoration: none; }
.donate-buy { display: flex; flex-direction: column; gap: 6px; align-items: stretch; min-width: 86px; }

.trials-ahead { align-items: center; gap: 8px; margin-top: 6px; }
.trials-ahead img { width: 26px; height: 26px; }

/* Голод и раны в карточке противника (18.10.2026). */
.fight-warn { display: grid; grid-template-columns: auto 1fr auto; gap: 10px; align-items: center; padding: 10px 12px; margin: 8px 0;
  border-left: 3px solid var(--blood); background: linear-gradient(90deg, rgba(255, 77, 77, 0.14), transparent 70%), var(--void-2);
  animation: lv-pop 0.35s ease both; }
.fight-warn > .ic { width: 22px; height: 22px; color: var(--blood); }
.fight-warn span { display: grid; gap: 2px; }
.fight-warn small { font-size: 12px; color: var(--ink-dim); }
