/* 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; }
}

.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: none;
  grid-auto-flow: row;
  justify-items: center;
  gap: 2px;
  pointer-events: none;
  white-space: nowrap;
}
.pin.has-label .pin-label { display: grid; }

.pin-name {
  font-size: 11px;
  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.4; }
.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-template-rows: 1fr auto;
  gap: 8px;
  padding: 8px 10px calc(8px + env(safe-area-inset-bottom));
  overflow: hidden;
}

/* Карта занимает всё свободное место, сохраняя пропорции рисунка: иначе
   река перестала бы делить город пополам, а точки разъехались бы с пятнами
   районов на подложке. */
.mapview-plate {
  position: relative;
  justify-self: center;
  align-self: center;
  width: 100%;
  max-width: min(100%, calc(100% * 1));
  aspect-ratio: 720 / 760;
  max-height: 100%;
  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: 42px;
  min-height: 42px;
  padding: 0;
  font-size: 20px;
  line-height: 1;
  background: rgba(11, 18, 25, 0.82);
  backdrop-filter: blur(6px);
}
.bigmap-zoom-value {
  min-width: 42px;
  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);
}

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

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

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

.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;
  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; }
@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: grid; gap: 6px; border-top: 1px solid var(--line); }
.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; }
/* 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-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; }

.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);
}
.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; }

/* Полоса подвкладок внутри группы.
   Кнопок в группе до пяти, на узком экране они переносятся — поэтому 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); }
.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); }

/* Строка банды под картой. */
.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; }
/* 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; }
