/* Минималистичный светлый «фэнтезийный» интерфейс: полупрозрачные панели,
   тёплое золото по светлому пергаменту, ничего лишнего на игровом поле. */

:root {
  --bg: #12100c;
  --glass: rgba(34, 28, 21, 0.78);
  --glass-2: rgba(58, 48, 34, 0.72);
  --edge: rgba(226, 197, 128, 0.45);
  --edge-hot: #f0d68a;
  --gold: #ffe6a6;
  --text: #f3ecd9;
  --dim: #c9bda0;
  --danger: #ff7a6a;
  --mana: #8fa0ff;
  --stam: #a8e86a;
  --font: "Trebuchet MS", "Segoe UI", sans-serif;
  --mono: "Consolas", "DejaVu Sans Mono", monospace;
}

* { box-sizing: border-box; }
html, body { height: 100%; margin: 0; }
body {
  background: var(--bg); color: var(--text); font-family: var(--font); font-size: 13px;
  overflow: hidden; user-select: none;
}
.hidden { display: none !important; }
body.targeting, body.targeting * { cursor: crosshair !important; }

/*
   Размытия подложки на игровых панелях нет намеренно.

   Свойство backdrop-filter заставляет браузер пересобирать размытый кусок
   фона каждый кадр — а фон здесь это канва во весь экран, которая и так
   перерисовывается каждый кадр. На экране 4K это несколько размытий по всей
   ширине в каждом кадре, и стоят они больше, чем весь игровой такт вместе с
   отрисовкой. Взамен поднята плотность подложки: на глаз разница невелика, а
   кадр перестаёт тратиться впустую. Размытие осталось только там, где
   картинка под ним замирает, — на входе и в меню профиля.
*/
/* Полупрозрачная панель — общий стиль. */
.panel {
  background: var(--glass);
  border: 1px solid var(--edge);
  border-radius: 8px;
  box-shadow: 0 2px 14px rgba(0,0,0,.35);
}

/* --- Экран входа ---------------------------------------------------------- */
#login-screen {
  position: fixed; inset: 0; z-index: 1000; display: none;
  background: radial-gradient(circle at 50% 35%, #3a2f22, #0b0908 70%);
  place-items: center;
}
#login-screen.open { display: grid; }
#login-box {
  width: 340px; padding: 26px 28px; display: flex; flex-direction: column; gap: 10px;
  background: rgba(48, 40, 28, .72); border: 1px solid var(--edge);
  border-radius: 10px; backdrop-filter: blur(6px); box-shadow: 0 10px 50px #000;
}
/* Имя длинное и в две строки — разрядка в восемь пунктов рвала бы его на
   куски. Ей на смену размер: название и так занимает всю ширину. */
.lb-title { font-size: 23px; letter-spacing: 2px; line-height: 1.15; color: var(--gold); text-align: center; }
.lb-title span { color: var(--text); }
.lb-sub { text-align: center; color: var(--dim); font-size: 11px; margin-bottom: 12px; }

/* Описание игры под кнопкой: мелко, но читаемо. Список — обещание, и в нём
   ровно то, что в игре есть. */
.lb-what { margin: 10px 0 2px; color: var(--dim); font-size: 11px; line-height: 1.5; }
.lb-what ul { margin: 6px 0 0; padding-left: 16px; }
.lb-what li { margin-bottom: 2px; }
#login-box label { display: flex; flex-direction: column; gap: 4px; color: var(--dim); font-size: 12px; }
#login-box input {
  background: rgba(20,16,10,.6); border: 1px solid var(--edge); color: var(--text);
  padding: 7px 9px; font-family: var(--mono); font-size: 14px; border-radius: 4px;
}
#login-box input:focus { outline: none; border-color: var(--edge-hot); }
#login-go {
  margin-top: 8px; padding: 9px; background: rgba(226,197,128,.18);
  border: 1px solid var(--edge-hot); color: var(--gold); font-size: 14px; letter-spacing: 2px;
  cursor: pointer; border-radius: 6px; font-family: var(--font);
}
#login-go:hover { background: rgba(226,197,128,.32); color: #fff; }
/* «Играть» — главная кнопка экрана и единственное, что человек обязан на нём
   понять. Поэтому она крупнее всего остального и стоит первой; форма входа
   спрятана за неприметной ссылкой, потому что нужна она немногим. */
.lb-play {
  margin-top: 4px; padding: 14px; background: rgba(226,197,128,.24);
  border: 1px solid var(--edge-hot); color: var(--gold);
  font-size: 18px; letter-spacing: 4px; cursor: pointer; border-radius: 8px;
  font-family: var(--font);
}
.lb-play:hover { background: rgba(226,197,128,.4); color: #fff; }
.lb-hint { color: var(--dim); font-size: 11px; text-align: center; }
.lb-link {
  background: none; border: none; color: var(--dim); font-size: 11px;
  text-decoration: underline; cursor: pointer; font-family: var(--font); padding: 2px;
}
.lb-link:hover { color: var(--gold); }
/* Ссылка-кнопка стоит в том же ряду, что и кнопки: без этого она прилипала бы
   к левому краю и читалась как оговорка, а не как второй путь. */
a.lb-link { display: block; text-align: center; margin-top: 2px; }
#login-fields { display: flex; flex-direction: column; gap: 10px; }

/* «Установить игру» — вторая по важности кнопка экрана входа, поэтому тише
   главной: рамка вместо заливки. Появляется только когда установка возможна. */
.lb-install {
  background: transparent; border: 1px solid var(--edge); color: var(--gold);
  padding: 9px; border-radius: 8px; cursor: pointer;
  font-family: var(--font); font-size: 12px; letter-spacing: 1px;
}
.lb-install:hover { background: rgba(226,197,128,.14); border-color: var(--edge-hot); }
.lb-msg { color: var(--dim); font-size: 11px; text-align: center; min-height: 15px; }
.lb-msg.err { color: var(--danger); }
.lb-mode { color: #9fb08a; font-size: 10px; text-align: center; }
/* Подпись подложки. Поперёк игрового поля ей делать нечего — там она мешала бы
   каждый кадр, — но подложку рисуют люди, и лицензия OSM требует ссылки.
   Место для неё — экран входа: видно всем, кто заходит, и никому не мешает. */
.lb-fine { color: #6f6a5a; font-size: 10px; text-align: center; margin-top: 2px; }
.lb-fine a { color: #8d8570; }

/* Подписи Leaflet на игровом поле быть не должно ни при каких условиях: карта
   здесь — подложка боя, и полоса поверх неё читается как часть интерфейса. */
.leaflet-control-attribution { display: none !important; }

/* --- Игровое поле --------------------------------------------------------- */
#app { height: 100vh; }
#stage { position: absolute; inset: 0; overflow: hidden; background: #0b0908; }
.stage-layer { position: absolute; inset: 0; opacity: 0; pointer-events: none; transition: opacity .18s; }
.stage-layer.active { opacity: 1; pointer-events: auto; }
/* Фон под тайлами — цвет суши, а не чернота. Пока тайл нового масштаба
   грузится, зритель видит именно фон; тёмно-бурый #14100c читался как
   «чёрный экран» при каждом изменении зума. Светлая земля сливается с картой,
   и пропуск перестаёт бросаться в глаза. */
#map { position: absolute; inset: 0; background: #dfe3d0; }
#dungeon-canvas { position: absolute; inset: 0; width: 100%; height: 100%; cursor: crosshair; }
/* Карта — ярче и фантастичнее: подняли насыщенность и добавили тёплый оттенок. */
/* Ярче и теплее оригинала, но без кислотности: правим насыщенность и контраст. */
.osm-tiles { filter: saturate(1.28) contrast(1.06) brightness(0.97) sepia(0.12); }
/* Подложка — тот же город грубым уровнем, всегда загруженный. Её задача не
   быть красивой, а не давать образоваться дыре в тот миг, когда основной слой
   переключает уровень тайлов. Слегка размываем: так подмена уровня читается
   как расфокус объектива, а не как ошибка загрузки. */
.osm-base { filter: saturate(1.15) contrast(1.02) sepia(0.12) blur(1.5px); }
.world-overlay { position: absolute; inset: 0; z-index: 650; pointer-events: none; }
.leaflet-container { background: #dfe3d0; cursor: crosshair; }
/* Тайлы показываем сразу. Штатное проявление за 200 мс на каждом шаге
   плавного зума превращалось в непрерывное мигание фона. */
.leaflet-tile { opacity: 1 !important; transition: none !important; }
path.geo-building { fill: #ffd98a; fill-opacity: .22; stroke: #ffe6a6; stroke-width: 1.2; }
path.geo-building.cleared { fill: #7dffb0; stroke: #a6ffd0; fill-opacity: .18; }
path.geo-wall { fill: none; stroke: #d9b070; stroke-width: 2; }

/* Подсказка взаимодействия — она же кнопка. На планшете клавиатуры нет,
   поэтому по подсказке можно просто нажать пальцем; на десктопе рядом
   остаётся значок горячей клавиши. */
.prompt {
  background: rgba(38,32,24,.86); border: 1px solid var(--edge-hot); color: var(--gold);
  padding: 6px 16px; display: none; letter-spacing: 1px; border-radius: 6px;
  align-items: center; gap: 10px; cursor: pointer; white-space: nowrap;
  font-family: var(--font); font-size: 13px;
  touch-action: manipulation; -webkit-tap-highlight-color: transparent;
}
.prompt:hover { background: rgba(58,48,34,.95); }
.prompt.pressed { background: rgba(78,64,42,.98); transform: scale(.96); }
.pr-key {
  border: 1px solid var(--edge); border-radius: 4px; padding: 1px 7px;
  font-family: var(--mono); font-size: 11px; color: var(--text); letter-spacing: 0;
}

/* Сенсорный экран: палец толще курсора — увеличиваем кнопку и поднимаем её
   над нижними панелями, чтобы не промахиваться. */
@media (hover: none), (pointer: coarse) {
  .prompt { padding: 13px 22px; font-size: 16px; min-height: 48px; }
  .pr-key { font-size: 15px; padding: 2px 9px; }
}

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

   Сам слой мыши не ловит, иначе он накрыл бы карту целиком; ловят её только
   панели внутри.
*/
#hud { position: absolute; inset: 0; z-index: 700; pointer-events: none; }
.dock {
  position: absolute; display: flex; flex-direction: column; gap: 6px;
  max-width: calc(50vw - 16px);
}
.dock > * { pointer-events: auto; }
/* Скрыть панели: поле остаётся видно, а вернуть их можно только кнопкой в
   `#hud-corner` — поэтому она одна не гаснет вместе с остальными доками
   (кнопка её и включает, см. `hud.js`). */
body.панели-скрыты .dock > *:not(#hud-corner) { display: none; }
.dock-tl { left: 12px; top: 12px; align-items: flex-start; max-height: calc(100vh - 150px); }
.dock-tr { right: 12px; top: 12px; align-items: flex-end; }
.dock-bl { left: 12px; bottom: 12px; align-items: flex-start; }
.dock-br { right: 12px; bottom: 12px; align-items: flex-end; }
.dock-bc {
  left: 50%; bottom: calc(12px + var(--keyboard, 0px)); transform: translateX(-50%);
  align-items: center; max-width: min(560px, 60vw);
  transition: bottom .16s ease-out;
}
/* Пока открыта клавиатура, из нижней колонки нужна ровно строка речи: шкала
   боя и пульт под клавиатурой всё равно не видны, а места они отнимают
   столько, что строка снова уезжает под неё. */
body.keyboard-open .dock-bc > :not(.say-bar) { display: none; }
.dock-tc {
  left: 50%; top: 12px; transform: translateX(-50%);
  align-items: center; max-width: min(620px, 70vw);
}

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

   Закрывается крестиком и больше не возвращается в этой сессии: тому, кто уже
   разобрался, подсказка мешает.
*/
.tutor {
  display: none; flex-direction: column; gap: 3px;
  padding: 7px 10px; max-width: 100%;
  background: rgba(24, 19, 13, .93); border: 1px solid var(--gold);
  border-radius: 8px;
  box-shadow: 0 2px 18px rgba(0,0,0,.55);
}
.tutor.on { display: flex; }
.tu-head { display: flex; align-items: center; gap: 8px; }
.tu-mark { color: var(--gold); }
.tu-text { color: #ffe6a6; font-size: 13px; font-weight: 700; }
.tu-key {
  padding: 1px 6px; border-radius: 4px; border: 1px solid var(--edge);
  color: var(--gold); font-family: var(--mono); font-size: 11px; white-space: nowrap;
}
.tu-why { color: var(--dim); font-size: 11px; line-height: 1.35; }
.tu-close {
  margin-left: 4px; background: none; border: none; cursor: pointer;
  color: var(--dim); font-size: 12px; line-height: 1; padding: 2px 4px;
}
.tu-close:hover { color: var(--gold); }
/* Шаг закрыт — подсказка на мгновение зеленеет и говорит, что получилось. */
.tutor.done { border-color: #b6f09a; box-shadow: 0 0 18px rgba(182,240,154,.35); }
.tutor.done .tu-text { color: #b6f09a; }
.tutor.done .tu-mark { color: #b6f09a; }

/* --- Панель активного существа -------------------------------------------- */
#unit-panel {
  position: relative;
  width: 236px; padding: 7px; display: grid; grid-template-columns: 76px 1fr; gap: 8px;
}
#unit-portrait { background: rgba(20,16,10,.5); border: 1px solid var(--edge); border-radius: 5px; }
#aim-overlay { position: absolute; left: 7px; top: 7px; pointer-events: none; display: none; }
#unit-panel.aiming #aim-overlay { display: block; }
#unit-panel.aiming { cursor: crosshair; }
.aim-hint {
  position: absolute; left: 7px; right: 7px; bottom: -16px; text-align: center;
  font-size: 9px; color: var(--dim); opacity: 0; transition: opacity .15s;
}
#unit-panel:hover .aim-hint { opacity: .8; }
.up-name { color: var(--gold); font-size: 14px; font-weight: 700; margin-bottom: 5px; }
/* Полосы существа тоньше прежнего вдвое. Толщина ничего не сообщала — доля
   видна и на тонкой полосе, — а высоты панель занимала как портрет. */
.up-bars { display: flex; flex-direction: column; gap: 3px; }
.bar { display: grid; grid-template-columns: 1fr auto; gap: 5px; align-items: center; font-size: 10px; }
.bar .b-track { height: 4px; background: rgba(20,16,10,.55); border: 1px solid rgba(226,197,128,.3); border-radius: 3px; overflow: hidden; }
.bar .b-track i { display: block; height: 100%; }
.bar .b-num { color: var(--dim); font-family: var(--mono); font-size: 9px; }

/* --- Угол экрана: полный экран и скрытие панелей --------------------------- */
/* Сидит первой строкой дока справа вверху, над миникартой. */
.hud-corner { display: flex; gap: 6px; }

/* --- Миникарта ------------------------------------------------------------ */
#minimap-panel { padding: 7px; }
#minimap { display: block; width: var(--mm, 150px); height: var(--mm, 150px); background: rgba(20,16,10,.5); border: 1px solid var(--edge); border-radius: 50%; }
.mm-label { text-align: center; color: var(--dim); font-size: 9px; margin-top: 4px; max-width: var(--mm, 150px); }

/* --- Журнал --------------------------------------------------------------- */
/*
   Журнал — не бегущая строка, а место, где идёт разговор: по нему листают и
   в него отвечают. Поэтому он ловит мышь. Плата известна: клик в его углу не
   двигает персонажа. Она оправдана — иначе ответить собеседнику можно было
   только зная про Enter, а прочесть уехавшую строку нельзя было вовсе.

   Полупрозрачный, пока на него не смотрят: на игровом поле не должно быть
   лишних окон.
*/
.log {
  width: min(290px, 40vw); max-height: 26vh; min-height: 40px;
  overflow-y: auto; overscroll-behavior: contain;
  font-size: 11px; line-height: 1.35; cursor: text;
  padding: 4px 6px; border: 1px solid transparent; border-radius: 8px;
  /* Подложка есть всегда, пусть и еле заметная. Карта под журналом светлая
     (трава, дороги), и светлый текст по ней читался как грязь на стекле —
     видно на снимках живого прогона. Одной тени мало: она даёт контур, но не
     фон. */
  background: rgba(24, 19, 13, .42);
  text-shadow: 0 1px 3px rgba(0,0,0,.9);
  scrollbar-width: thin; scrollbar-color: rgba(226,197,128,.45) transparent;
  mask-image: linear-gradient(180deg, transparent, #000 22%);
  transition: background .15s, border-color .15s;
}
.log:hover {
  background: rgba(24, 19, 13, .88); border-color: var(--edge);
  /* Затенение верхних строк мешает читать то, к чему отлистали. */
  mask-image: none;
}
.log::-webkit-scrollbar { width: 6px; }
.log::-webkit-scrollbar-thumb { background: rgba(226,197,128,.45); border-radius: 3px; }
.log::-webkit-scrollbar-track { background: transparent; }
.log-line { color: #ded3b8; padding: 1px 0; }
.log-line.good { color: #b6f09a; }
.log-line.warn { color: var(--gold); }
.log-line.bad { color: #ff9a8a; }
/* Чужая речь заметно отличается от игровых сообщений: иначе разговор тонет
   среди «Локация зачищена» и не читается как чей-то голос. */
.log-line.chat { color: #9fd8ff; }
.log-line.chat .who { color: #ffd479; }

/* --- Строка речи ---------------------------------------------------------- */
/* Держится над панелью команд, по центру нижней части: поле боя не
   загораживает, а глаз находит её сразу, потому что она одна такая. */
/* Строка речи одинаково работает мышью и пальцем: ширина тянется по месту, а
   отправка и закрытие есть кнопками — на телефоне ни Escape, ни уверенности в
   том, что делает клавиша ввода. */
.say-bar {
  display: flex; align-items: center; gap: 6px;
  padding: 6px 8px; width: min(520px, calc(100vw - 24px));
  background: rgba(24, 19, 13, .95); border: 1px solid var(--edge);
  border-radius: 8px;
}
.say-bar.hidden { display: none; }
#say-input {
  flex: 1; min-width: 0; background: none; border: none; outline: none;
  color: var(--text); font: inherit; font-size: 14px;
}
#say-input::placeholder { color: var(--dim); }
.say-btn {
  width: 34px; height: 34px; flex: none; border-radius: 7px; cursor: pointer;
  background: rgba(226,197,128,.12); border: 1px solid var(--edge); color: var(--gold);
  font-size: 14px; touch-action: manipulation; -webkit-tap-highlight-color: transparent;
}
.say-btn:hover { background: rgba(226,197,128,.3); }

/* --- Шкала угроз ---------------------------------------------------------- */
/*
   Ближайшее будущее одной строкой. Слева отметка «сейчас», вправо уходит
   время; всё, что вот-вот случится, едет справа налево и доезжает до отметки
   ровно тогда, когда случится.

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

   Ширина фишки есть окно: пока фишка стоит на отметке, нажатие засчитывается.
   Буквы на фишке — чем можно ответить; горит та, что сработает.

   Она же кнопка: на сенсорном экране клавиш нет, а отбивать удар нужно и там.
*/
.tline {
  display: none; flex-direction: column; gap: 3px;
  /* Чем длиннее шкала, тем больше пикселей на секунду и тем точнее видно, когда
     фишка дойдёт до «сейчас». Упирается в ширину экрана, а не в вкус. */
  padding: 6px 8px 4px; width: min(560px, 92vw); cursor: pointer; user-select: none;
  background: rgba(24, 19, 13, .92); border: 1px solid var(--edge);
  border-radius: 8px;
  transition: border-color .12s, box-shadow .12s;
}
.tline.on { display: flex; }
.tline.hot { border-color: #8dff9a; box-shadow: 0 0 18px rgba(120,255,150,.3); }
.tline.hot .tl-text { color: #b6f09a; }
.tline.hit { border-color: #b6f09a; box-shadow: 0 0 16px rgba(182,240,154,.45); }
.tline.hit .tl-text { color: #b6f09a; }
.tline.miss { border-color: #ff9a8a; }
.tline.miss .tl-text { color: #ff9a8a; }

.tl-bar {
  position: relative; height: 20px; overflow: hidden;
  border-radius: 5px;
  /* Деления по секундам: без них полоса — просто тёмный прямоугольник, и по
     ней не понять, через сколько дойдёт фишка. Шаг задаёт `renderLine`: в
     пикселях он зависит от ширины экрана, а в секундах — нет. */
  background:
    repeating-linear-gradient(90deg,
      rgba(226,197,128,.16) 0 1px, transparent 1px var(--сек, 60px)),
    linear-gradient(90deg, rgba(20,16,10,.8), rgba(20,16,10,.3));
  transition: height .12s;
}
/* Отметка «сейчас» — она же ЗОНА ПОПАДАНИЯ, левый край шкалы.
   Не линия посреди: время читается слева направо, и будущее должно лежать там,
   куда смотрят.

   Красится в три состояния, и это главное, чего не хватало: по одной серой
   полосе было не понять, когда жать. Тускло-серая — ловить нечего; янтарная —
   что-то подъезжает, приготовьтесь; зелёная — нажатие засчитывается ПРЯМО
   СЕЙЧАС. Зелёный тут не украшение, а единственный цвет, который в этой игре
   больше нигде не значит «жми». */
.tl-now {
  position: absolute; left: 0; top: 0; bottom: 0; width: 22px; z-index: 2;
  pointer-events: none; border-left: 3px solid rgba(226,197,128,.5);
  background: linear-gradient(90deg, rgba(226,197,128,.12), transparent);
  transition: background .1s, border-color .1s, box-shadow .1s;
}
.tl-now.soon {
  border-left-color: var(--gold);
  background: linear-gradient(90deg, rgba(255,212,121,.34), transparent);
}
.tl-now.ready {
  border-left: 3px solid #8dff9a; width: 30px;
  background: linear-gradient(90deg, rgba(120,255,150,.55), rgba(120,255,150,.08));
  box-shadow: 0 0 18px rgba(120,255,150,.65);
}
.tl-chips { position: absolute; inset: 0 0 0 3px; }

.tl-chip {
  position: absolute; left: 0; top: 0; height: 18px;
  display: flex; align-items: center; gap: 3px; padding: 0 3px;
  /* Содержимое НЕ обрезается по ширине окна, а вылезает вправо.
     Ширина фишки — это окно, и врать ею нельзя: растяни её под три буквы, и
     удар на экране придёт позже, чем на самом деле. Но и обрезать буквы нельзя
     — по ним и решают, чем отвечать. Поэтому коробка честная, а буквы просто
     выходят за её край; у каждой своя подложка, и читаются они и на весу. */
  overflow: visible;
  border-radius: 4px; border: 1px solid rgba(226,197,128,.35);
  /* Левый край — тот самый миг, когда окно откроется, и он же первым доезжает
     до «сейчас». Поэтому он один и подчёркнут: по нему и целятся. */
  border-left-width: 3px;
  background: rgba(60,48,28,.55); color: var(--dim);
  font-size: 10px; line-height: 1; white-space: nowrap;
  /* Плавности намеренно нет: фишка обязана стоять там, где стоит расчёт.
     Сдвинь её анимация хоть на кадр — игрок начнёт жать по картинке и
     промахиваться мимо окна. */
}
.tl-icon { font-size: 12px; text-shadow: 0 1px 3px rgba(0,0,0,.9); }
.tl-key {
  min-width: 12px; text-align: center; padding: 0 2px; border-radius: 3px;
  font-family: var(--mono); font-size: 11px; color: #8b8378;
  border: 1px solid rgba(226,197,128,.2); background: rgba(20,16,10,.85);
}
/* Ответ можно дать прямо сейчас — буква горит золотом. Скоро пригодится —
   видна, но приглушена. Уже дан — зелёная. Не сработает — почти не видна. */
.tl-key.soon { color: #c9bda0; }
/* Зелёный — тот же, что у зоны попадания: буква и зона загораются вместе, и
   связь «эта фишка дошла — эта кнопка сработает» читается без объяснений. */
.tl-key.ready { color: #0d2a12; background: #8dff9a; border-color: #8dff9a; font-weight: 700; }
.tl-key.done { color: #b6f09a; border-color: rgba(182,240,154,.5); }
.tl-key.off { opacity: .3; }
/* Чужой замах — красный, свой — золотой, цепочка — та же золотая нить, что и
   кольцо на поле, подходящий противник — тусклая тень без окна. */
.tl-chip.чужой { border-color: rgba(255,120,90,.6); background: rgba(90,34,26,.6); }
.tl-chip.свой { border-color: rgba(226,197,128,.6); background: rgba(70,56,30,.6); }
.tl-chip.цепь { border-color: rgba(255,212,121,.7); background: rgba(80,62,20,.7); }
.tl-chip.подход { border-color: rgba(160,150,130,.35); background: rgba(40,34,26,.5); opacity: .75; }
.tl-chip.ready { box-shadow: 0 0 14px rgba(120,255,150,.6); border-color: #8dff9a; }
.tl-chip.done { border-color: rgba(182,240,154,.6); }

.tl-text {
  min-height: 14px; text-align: center; font-size: 12px; color: #c9bda0;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* --- Вмешательства в бой -------------------------------------------------- */
/*
   Одна кнопка сводила бой к рефлексу: загорелось — жми. Здесь пять, и у
   каждой своё положение: усилить свой замах, отбить чужой, уйти с линии,
   встретить разбег, добить сбитого. Гаснут, когда нажимать бессмысленно, —
   панель не обещает того, чего расчёт не сделает.

   Живут внутри шкалы и вместе с ней исчезают: вне боя это пять серых плашек,
   которые ничего не делают и занимают низ экрана. Оттого и мелкие — они не
   подпись к бою, а подпись к фишке, которая едет прямо над ними.
*/
.rhythm-acts { display: flex; gap: 4px; width: 100%; }
.ra-btn {
  flex: 1; min-width: 0;
  display: flex; flex-direction: column; align-items: center; gap: 1px;
  padding: 3px 2px; cursor: pointer; border-radius: 5px;
  background: rgba(18,14,10,.5); border: 1px solid rgba(226,197,128,.25);
  color: var(--dim); font-family: var(--font); opacity: .45;
  transition: opacity .12s, border-color .12s, box-shadow .12s;
  touch-action: manipulation; -webkit-tap-highlight-color: transparent;
}
.ra-key { font-family: var(--mono); font-size: 11px; line-height: 1.1; color: var(--gold); }
.ra-name { font-size: 8px; letter-spacing: .5px; text-transform: uppercase; line-height: 1.1; }
/* Скоро пригодится — видно, но приглушено; можно жать — горит. */
.ra-btn.soon { opacity: .8; border-color: rgba(226,197,128,.5); }
.ra-btn.ready {
  opacity: 1; border-color: var(--edge-hot); color: var(--gold);
  box-shadow: 0 0 14px rgba(255,214,138,.4); background: rgba(60,48,28,.6);
}
.ra-btn.done { opacity: .55; border-color: #b6f09a; color: #b6f09a; }

/* --- Счётчик скорости ----------------------------------------------------- */
/* Не для игры, а для разговора о том, что тормозит: [F8]. Моноширинный и
   тусклый — он не должен претендовать на внимание, пока его не позвали. */
.perf {
  padding: 4px 7px; border-radius: 6px; white-space: pre;
  background: rgba(10,8,6,.8); border: 1px solid rgba(226,197,128,.3);
  color: #9fd8a0; font-family: var(--mono); font-size: 10px; line-height: 1.35;
}

/* --- Быстрые кнопки и выпадающие панели ----------------------------------- */
/*
   Положение тела и команду меняют редко, а восемь иконок висели на экране
   постоянно. Осталось две кнопки с текущим значением; четвёрки раскрываются по
   нажатию и закрываются сразу после выбора. На телефоне это разница между
   «палец попадает» и «палец не попадает», на компьютере — просто чище поле.
*/
.quick-btn {
  position: relative;
  width: var(--big, 44px); height: var(--big, 44px); border-radius: 8px; cursor: pointer;
  font-size: 21px; line-height: 1;
  background: rgba(226,197,128,.12); border: 1px solid var(--edge); color: var(--gold);
  touch-action: manipulation; -webkit-tap-highlight-color: transparent;
  display: grid; place-items: center; flex: none;
}
.quick-btn:hover { background: rgba(226,197,128,.28); }
.quick-btn.active { border-color: var(--edge-hot); background: rgba(226,197,128,.35); }
/* Команда — уголком на той же кнопке: два режима, одно место. */
.qb-sub {
  position: absolute; right: -2px; bottom: -3px; font-size: 11px; font-style: normal;
  line-height: 1; text-shadow: 0 0 3px #000, 0 0 3px #000;
}

/* --- Лист режимов --------------------------------------------------------- */
/*
   Положение тела и поведение выбирают вместе, поэтому и живут в одной панели
   с подписями. Она раскрывается над пультом и закрывается по первому же
   выбору или тапу мимо: держать её открытой незачем, а поле боя она закрывает.
*/
.modes-sheet {
  display: none;
  flex-direction: column; gap: 10px; padding: 10px 12px;
  background: rgba(18, 14, 10, .92);
  border-color: rgba(226,197,128,.55);
  box-shadow: 0 8px 28px rgba(0,0,0,.6);
}
.modes-sheet.open { display: flex; }
.ms-group { display: flex; flex-direction: column; gap: 5px; }
.ms-title {
  font-size: 10px; letter-spacing: .12em; text-transform: uppercase; color: var(--dim);
}
.ms-toggle {
  padding: 10px 12px; border-radius: 8px; cursor: pointer; text-align: left;
  background: rgba(226,197,128,.1); border: 1px solid var(--edge); color: var(--text);
  font-family: var(--font); font-size: 12px;
  touch-action: manipulation; -webkit-tap-highlight-color: transparent;
}
.ms-toggle.on { border-color: var(--edge-hot); background: rgba(240,214,138,.28); color: var(--gold); }

/* Взятая вещь и ячейки-приёмники: то же правило, что и у навыков — пока что-то
   «в руке», видно и что взято, и куда это можно положить. */
.cell.taken { border-color: var(--edge-hot); box-shadow: 0 0 12px rgba(240,214,138,.7); }
body.moving-item .cell { border-color: rgba(240,214,138,.45); }

#command-bar {
  display: grid; grid-template-columns: repeat(4, var(--big, 44px)); gap: 6px;
}
.cmd-btn {
  width: var(--big, 44px); height: var(--big, 44px); border-radius: 8px; cursor: pointer; font-size: 20px;
  background: rgba(226,197,128,.1); border: 1px solid var(--edge); color: var(--gold);
  touch-action: manipulation; -webkit-tap-highlight-color: transparent;
}
.cmd-btn:hover { background: rgba(226,197,128,.25); }
.cmd-btn.active { border-color: var(--edge-hot); background: rgba(226,197,128,.35); box-shadow: 0 0 8px rgba(240,214,138,.4); }

/* --- Пульт персонажа ------------------------------------------------------ */
/*
   Показания и действия в одной плашке: полосы жизни, сил и пси — и тут же
   слоты умений. Раньше взгляд метался между левым нижним углом (полосы) и
   правым нижним (кружки умений), а решение принимается по обоим сразу.

   Фон почти прозрачный, свет идёт от рамки и от самих цифр: панель не
   загораживает поле, а лежит на нём стеклом.
*/
/*
   Пульт персонажа держится узким и низким намеренно.

   Он висит на экране постоянно, а нужен целиком раз в минуту — и при этом
   занимал снизу столько, что в бою поле было не разглядеть. Имя и подписи
   «жизнь/силы/пси» переехали в профиль и в `title`: на поле от них остаются три
   полоски, три числа и портрет.
*/
#hero-plate {
  display: flex; flex-direction: column; gap: 5px; padding: 5px 8px;
  background: rgba(18, 14, 10, .42);
  border-color: rgba(226,197,128,.55);
  box-shadow: 0 0 22px rgba(0,0,0,.5), inset 0 0 24px rgba(226,197,128,.05);
}
.hp-top { display: flex; align-items: center; gap: 8px; }
/* Портрет несёт на себе три кольца ресурсов, поэтому фон и рамку рисует канва:
   круглая рамка поверх колец резала бы их пополам. */
#hero-portrait { width: var(--face, 60px); height: var(--face, 60px); flex: none; }

/* Полос больше нет — от них остались только числа, узкой колонкой у портрета.
   Что есть что, говорит цвет, и он тот же, что на кольце: искать подпись
   словами не приходится, а ширины пульт занимает вчетверо меньше. */
.hp-vitals {
  display: flex; flex-direction: column; gap: 1px;
  font-family: var(--mono); font-size: 11px; line-height: 1.25;
  font-variant-numeric: tabular-nums;
}
.vit-n span { color: var(--dim); font-size: 9px; }

.profile-btn {
  width: 30px; height: 30px; border-radius: 8px; cursor: pointer; font-size: 16px;
  background: rgba(226,197,128,.1); border: 1px solid var(--edge); color: var(--gold);
}
.profile-btn:hover { background: rgba(226,197,128,.3); }
.profile-btn.active { border-color: var(--edge-hot); background: rgba(226,197,128,.35); }

/* --- Навыки, положение тела, часы ----------------------------------------- */
#stance-bar { grid-template-columns: repeat(2, var(--btn, 30px)); }
/* Слоты умений живут в плашке героя и потому выстраиваются в одну строку:
   два ряда кружков разрывали бы пульт пополам. */
.skillbar { display: flex; gap: 5px; justify-content: center; flex-wrap: wrap; }
.skill-slot {
  position: relative; width: var(--slot, 44px); height: var(--slot, 44px);
  border: 1px solid var(--edge); border-radius: 50%;
  background: rgba(226,197,128,.1); display: grid; place-items: center; cursor: pointer;
}
.skill-slot .key { position: absolute; left: 4px; top: 0; font-size: 9px; color: var(--dim); }
.skill-slot .icon { font-size: 17px; }
.skill-slot:hover { background: rgba(226,197,128,.25); }
.skill-slot.armed { border-color: var(--edge-hot); box-shadow: 0 0 12px rgba(240,214,138,.65); }
.skill-slot.disabled { opacity: .35; }
.skill-slot.empty .icon { color: rgba(226,197,128,.25); }
.skill-slot.drop { border-color: var(--edge-hot); }
.stance-bar { display: grid; gap: 4px; padding: 5px; }
.stance-btn {
  width: var(--btn, 30px); height: var(--btn, 30px); border-radius: 5px; cursor: pointer; font-size: 14px;
  background: rgba(226,197,128,.1); border: 1px solid var(--edge); color: var(--text);
}
.stance-btn:hover { background: rgba(226,197,128,.25); }
.stance-btn.active { border-color: var(--edge-hot); background: rgba(226,197,128,.35); }
/* Заметность. Стоит над панелью положения тела: выбор и его последствие
   должны быть в одном месте, иначе связь между ними приходится угадывать. */
#stealth-meter {
  width: 146px; padding: 5px 7px; font-family: var(--mono); font-size: 10px;
  display: flex; flex-direction: column; gap: 3px;
}
.sm-line { display: flex; justify-content: space-between; gap: 6px; white-space: nowrap; }
.sm-state { letter-spacing: 1px; }
.sm-num { color: var(--dim); }
.sm-bar { height: 4px; background: rgba(0, 0, 0, .55); border-radius: 2px; overflow: hidden; }
.sm-bar i { display: block; height: 100%; transition: width .12s linear; }

#clock-panel { padding: 5px; display: flex; flex-direction: column; align-items: center; gap: 2px; }
#clock-text { font-family: var(--mono); font-size: 10px; color: var(--gold); }

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

   Порог по ВЫСОТЕ отдельно от ширины: телефон в горизонтали широкий, но
   низкий, и именно по высоте панели начинают отъедать поле боя.
*/
:root { --btn: 34px; --slot: 36px; --mm: 150px; --big: 44px; --face: 60px; }

@media (max-width: 1100px) {
  :root { --btn: 30px; --slot: 34px; --mm: 120px; --big: 46px; --face: 56px; }
  #unit-panel { width: 200px; grid-template-columns: 64px 1fr; }
  #unit-portrait, #aim-overlay { width: 64px; height: 80px; }
  .log { width: min(240px, 38vw); max-height: 22vh; }
}

/* Планшет и телефон: палец толще курсора. Кнопки, по которым жмут в бою,
   растут, а не сжимаются вместе с экраном — промах по кнопке отбива стоит
   жизни, промах по «положению тела» стоит секунды. */
@media (hover: none), (pointer: coarse) {
  :root { --slot: 46px; --big: 56px; --btn: 44px; }
  .quick-btn { font-size: 26px; }
  .skill-slot .icon { font-size: 22px; }
  /* Шкала на пальце: строки выше, буквы крупнее — по ним читают на ходу. */
  .tline { width: min(560px, 94vw); }
  .tl-text { font-size: 13px; }
  .tl-key { font-size: 12px; min-width: 13px; }
  .profile-btn { width: 44px; height: 44px; font-size: 20px; }
  /* Наведение на сенсорном экране не существует: подсказки, живущие по
     `:hover`, там просто недоступны. Значит, журнал всегда с подложкой. */
  .log { background: rgba(24, 19, 13, .72); border-color: var(--edge); }
}

@media (max-width: 720px), (max-height: 560px) {
  :root { --btn: 28px; --slot: 36px; --mm: 96px; --face: 52px; }
  .dock { gap: 4px; }
  .dock-tl, .dock-tr, .dock-bl, .dock-br { margin: 0; }
  /* Отступы считаем от безопасной зоны, а не от края стекла: у телефона внизу
     полоса жеста, а сверху вырез, и панель, отмеренная в чистых пикселях,
     оказывается либо под пальцем системы, либо «висит» в пустоте над ней. */
  .dock-tl { left: calc(8px + env(safe-area-inset-left)); top: calc(8px + env(safe-area-inset-top)); }
  .dock-tr { right: calc(8px + env(safe-area-inset-right)); top: calc(8px + env(safe-area-inset-top)); }
  .dock-bl { left: calc(8px + env(safe-area-inset-left)); bottom: calc(8px + env(safe-area-inset-bottom)); }
  .dock-br { right: calc(8px + env(safe-area-inset-right)); bottom: calc(8px + env(safe-area-inset-bottom)); }
  /* Пульт на телефоне прижат к низу во всю ширину: остров посреди пустоты
     читается как «висит», а полоса по краю — как дно экрана.
     Соседние доки при этом отходят вверх ровно на его высоту — её меряет
     `следитьЗаПультом` и кладёт в `--plate-h`. Отмерять здесь в пикселях
     нельзя: колонка растёт, когда в бою появляется шкала угроз. */
  .dock-bc {
    bottom: calc(env(safe-area-inset-bottom) + var(--keyboard, 0px));
    left: 0; right: 0; transform: none;
    max-width: none; width: 100%;
  }
  .dock-bl, .dock-br {
    bottom: calc(var(--plate-h, 0px) + 8px + env(safe-area-inset-bottom));
  }
  .dock-bc > #hero-plate {
    width: 100%; border-radius: 12px 12px 0 0; border-bottom: none;
    padding-bottom: max(6px, env(safe-area-inset-bottom));
  }
  .dock-bc > .modes-sheet { width: calc(100% - 16px); }
  /* Подсказка обучения на узком экране: она важнее миникарты, поэтому не
     ужимается в ничто, а занимает всю ширину между углами. */
  .dock-tc { top: 8px; max-width: calc(100vw - 16px); }
  .tutor { padding: 5px 8px; }
  .tu-text { font-size: 11px; }
  .tu-why { font-size: 10px; }
  #unit-panel { width: 168px; grid-template-columns: 52px 1fr; padding: 5px; gap: 6px; }
  #unit-portrait, #aim-overlay { width: 52px; height: 66px; }
  #aim-overlay { left: 5px; top: 5px; }
  .up-name { font-size: 12px; margin-bottom: 3px; }
  .bar .b-num { display: none; }          /* числа не влезают, полоски читаются */
  .log { width: min(200px, 46vw); max-height: 18vh; font-size: 10px; }
  #stealth-meter { width: 124px; font-size: 9px; }
  .mm-label { display: none; }            /* координаты на телефоне не нужны */
  #hero-plate { padding: 4px 7px 4px 4px; gap: 6px; }
  #hero-portrait { width: 36px; height: 36px; }
  .vial { width: 52px; }
  .aim-hint { display: none; }
}

/* Совсем узкий экран (телефон): на поле остаётся только то, чем пользуются в
   бою. Всё остальное не уменьшается до нечитаемого, а убирается — в профиль
   [☰] или под кнопку раскрытия.

   Слоты навыков: четыре вместо восьми. Пятый-восьмой — это призыв, лечение,
   клич и шаг тени; в бою рука тянется к первым четырём, а редкое всегда можно
   применить из профиля. Восемь кружков по 56 пикселей на телефоне занимают
   треть экрана — ровно ту треть, где идёт драка. */
@media (max-width: 560px) {
  #stealth-meter { display: none; }
  .skill-slot:nth-child(n+5) { display: none; }
  /* Часы на телефоне остаются, но становятся значком.
     Раньше их прятали совсем: «время видно по свету на карте». Это было верно
     ровно до появления ночной жизни. Теперь ночь — это твари, которые уходят с
     рассветом, и «сколько ещё продержаться» — главный вопрос ночи. Отвечать на
     него по яркости пикселей человек не должен. */
  #clock-panel { padding: 3px; gap: 0; }
  #clock-dial { width: 34px; height: 34px; }
  #clock-text { font-size: 9px; }
  #minimap-panel { padding: 4px; }

  /* Верх экрана. Углы на телефоне не помещаются рядом: подсказка обучения,
     панель цели и миникарта перекрывали друг друга. Подсказка забирает всю
     ширину (она важнее всего, пока идёт обучение), миникарта уходит под неё,
     а панель цели прячется — здоровье противника и так видно над его головой,
     это единственное, что на ней читают в бою. */
  #unit-panel { display: none; }
  .dock-tc { left: 8px; right: 8px; transform: none; max-width: none; }
  /* Отступ под подсказку обучения нужен, только пока она правда висит на
     экране (класс .on): панель существа здесь спрятана, и без активной
     подсказки карте с журналом уступать верх нечему. Раньше оба садились
     на 92px безусловно и после обучения так и оставались — отступ ел
     обзор всем, кто его уже прошёл. */
  #hud:has(.tutor.on) .dock-tl, #hud:has(.tutor.on) .dock-tr { top: 92px; }
  .log { width: min(58vw, 240px); max-height: 16vh; }

  /* Низ экрана. Пульт персонажа во всю ширину, а кнопки положения и команды
     поднимаются НАД ним: иначе плашка ложится поверх них, и до половины
     кнопок не дотянуться пальцем. */
  .dock-bc { left: 8px; right: 8px; transform: none; max-width: none; }
  #hero-plate { width: 100%; }
  .skillbar { justify-content: space-around; }
  /* Над всей колонкой пульта: подсказка ритма, кнопки вмешательств и плашка
     занимают снизу около двухсот пикселей. Меньше — и кнопки положения тела
     оказываются под ними. Панель выбора добавляет к колонке ещё сотню, и
     считать надо по ней: она появляется ровно в тот миг, когда загораживать
     что-либо нельзя. */
  .dock-bl { bottom: 240px; }
  /* Подсказка взаимодействия прижимается вправо: колонка на телефоне во всю
     ширину, и по центру она ложилась ровно на кнопки положения тела в левом
     углу — две кнопки одна поверх другой, обе мимо пальца. */
  .prompt { align-self: flex-end; }
  .rhythm-acts { width: 100%; }
  .ra-btn { flex: 1; min-width: 0; padding: 5px 2px; }
  .ra-name { letter-spacing: 0; }
  /* Шкала на телефоне — во всю ширину: чем длиннее, тем больше пикселей на
     секунду и тем точнее видно, когда фишка дойдёт до «сейчас». */
  .tline { width: 100%; }
}

/* Телефон в горизонтали: широко, но очень низко. Панель существа и журнал
   отъедают именно высоту, поэтому здесь режем их, а не ширину. */
@media (max-height: 460px) {
  .log { max-height: 11vh; }
  #unit-panel { width: 150px; grid-template-columns: 44px 1fr; }
  #unit-portrait, #aim-overlay { width: 44px; height: 56px; }
  .tu-why { display: none; }               /* «зачем» читают один раз, «что» — каждый раз */
}

/* --- Меню профиля --------------------------------------------------------- */
#profile-menu { position: fixed; inset: 0; z-index: 900; display: none; background: rgba(10,8,6,.55); }
#profile-menu.open { display: grid; place-items: center; }
.pm-box { width: min(1120px, 94vw); height: min(82vh, 760px); display: flex; flex-direction: column; padding: 0; }

/*
   Панель умений остаётся на экране, пока открыт профиль.
   Иначе перетаскивать некуда: заклинание берут во вкладке «Навыки», зелье —
   в «Инвентаре», а класть их надо в ту самую четвёрку кружков, которую
   профиль до сих пор собой закрывал. Поднимаем док с пультом над накладкой и
   отодвигаем окно профиля, чтобы оно не пряталось под ним.
*/
body.profile-open .dock-bc {
  z-index: 940;
  pointer-events: auto;
}
/* Всё, кроме пульта, при открытом профиле не нужно: шкала боя, подсказка и
   строка речи только мешали бы перетаскиванию. */
body.profile-open .dock-bc > :not(#hero-plate) { display: none; }
body.profile-open .pm-box { height: min(72vh, 700px); margin-bottom: 96px; }
/* Кружок-приёмник заметен, пока тащат: пустой слот иначе не отличить от фона. */
body.profile-open .skill-slot { border-color: var(--edge-hot); }

/* Пока навык или предмет «взят в руку», ячейки зовут к себе: без подсветки
   непонятно, что тап уже что-то сделал и чего игра теперь ждёт. */
/* Тик прокачки: короткая строка, которая гаснет сама. Прокачка идёт от
   применения и мелкими долями — без этого уровень однажды просто оказывался
   другим, и связи с тем, что делал игрок, не возникало. */
.xp-ticks { display: flex; flex-direction: column; align-items: center; gap: 2px; pointer-events: none; }
.xp-tick {
  padding: 2px 8px; border-radius: 4px;
  background: rgba(24,19,13,.8); border: 1px solid rgba(150,240,160,.45);
  color: #a8e86a; font-family: var(--mono); font-size: 11px;
  animation: тикВверх 1.8s ease-out forwards;
}
@keyframes тикВверх {
  0%   { opacity: 0; transform: translateY(6px); }
  15%  { opacity: 1; transform: translateY(0); }
  70%  { opacity: 1; }
  100% { opacity: 0; transform: translateY(-10px); }
}
@media (prefers-reduced-motion: reduce) {
  .xp-tick { animation: none; opacity: 1; }
}

/* Счётчик вытесненных угроз: их не нарисовать без наложения, но и промолчать
   о них нельзя. Стоит у правого края — там, где кончается видимое будущее. */
.tl-more {
  position: absolute; right: 2px; top: 0;
  padding: 1px 5px; border-radius: 4px;
  background: rgba(120,30,20,.85); border: 1px solid rgba(255,120,90,.7);
  color: #ffb9a6; font-family: var(--mono); font-size: 10px; line-height: 15px;
  pointer-events: auto;
}

.assign-hint {
  padding: 8px 14px; color: #14100c; background: var(--gold);
  font-size: 12px; font-weight: 600; text-align: center;
}
body.assigning .skill-slot {
  border-color: var(--edge-hot);
  box-shadow: 0 0 12px rgba(240,214,138,.6);
  animation: зовёт 1.1s ease-in-out infinite;
}
.skill-card.picked { border-color: var(--edge-hot); background: rgba(240,214,138,.22); }
@keyframes зовёт {
  0%, 100% { box-shadow: 0 0 8px rgba(240,214,138,.4); }
  50%      { box-shadow: 0 0 18px rgba(240,214,138,.85); }
}
@media (prefers-reduced-motion: reduce) {
  body.assigning .skill-slot { animation: none; }
}
.pm-head { display: flex; align-items: center; gap: 12px; padding: 10px 14px; border-bottom: 1px solid var(--edge); }
.pm-head b { color: var(--gold); letter-spacing: 2px; }
/* Вкладки листаются вбок, а не переносятся и не обрезаются.
   На телефоне их семь, и в строку они не влезают: «Задания» и «Справка»
   уезжали за край экрана, и попасть в них было нечем — ни переноса, ни
   прокрутки. Полоса с прокруткой честнее переноса: заголовок остаётся одной
   строкой, а до дальних вкладок доводит палец. */
#pm-tabs {
  display: flex; gap: 4px; margin-left: 10px;
  flex: 1 1 auto; min-width: 0;
  overflow-x: auto; overflow-y: hidden;
  scrollbar-width: thin; -webkit-overflow-scrolling: touch;
}
#pm-tabs button { flex: 0 0 auto; white-space: nowrap; }
#pm-tabs button {
  background: transparent; border: 1px solid transparent; color: var(--dim);
  padding: 4px 10px; cursor: pointer; border-radius: 5px; font-family: var(--font); font-size: 12px;
}
#pm-tabs button:hover { color: var(--text); border-color: var(--edge); }
#pm-tabs button.active { color: var(--gold); border-color: var(--edge-hot); background: rgba(226,197,128,.15); }
#pm-close {
  margin-left: auto; background: none; border: 1px solid var(--edge); color: var(--dim);
  cursor: pointer; width: 24px; height: 24px; border-radius: 5px;
}
.pm-body { flex: 1; overflow-y: auto; padding: 14px 18px; }
.pm-cols { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; align-items: start; }
.hint { color: var(--dim); font-size: 11px; margin-bottom: 8px; }
.sect { color: var(--gold); font-size: 11px; letter-spacing: 2px; margin: 14px 0 6px; }

.stat-row { display: flex; justify-content: space-between; font-size: 12px; padding: 2px 0; border-bottom: 1px dotted rgba(226,197,128,.15); }
.stat-row b { color: #fff; font-family: var(--mono); }

.skill-card { border: 1px solid var(--edge); background: rgba(226,197,128,.06); padding: 7px; margin-bottom: 6px; cursor: grab; border-radius: 6px; }
.skill-card:hover { border-color: var(--edge-hot); }
.skill-card.off { opacity: .45; }
.sc-head { display: flex; gap: 6px; align-items: center; color: var(--gold); }
.sc-desc { color: var(--dim); font-size: 11px; margin: 4px 0; line-height: 1.35; }
.sc-time { display: flex; gap: 10px; font-size: 11px; color: var(--text); }
.sc-time .cost { color: var(--mana); margin-left: auto; }

.skill-line { display: grid; grid-template-columns: 1fr 26px 100px; gap: 8px; align-items: center; font-size: 12px; padding: 3px 0; }
.skill-line b { text-align: right; color: #fff; font-family: var(--mono); }
.skill-line.warn b { color: var(--gold); }
.skill-line.bad b { color: var(--danger); }
.xp { height: 6px; background: rgba(20,16,10,.5); border: 1px solid rgba(226,197,128,.3); display: block; border-radius: 3px; overflow: hidden; }
.xp i { display: block; height: 100%; background: linear-gradient(90deg, #a88b3c, var(--gold)); }
.traits { display: flex; flex-wrap: wrap; gap: 5px; }
.trait { border: 1px solid var(--edge); padding: 2px 8px; font-size: 11px; color: var(--gold); border-radius: 10px; }
.trait.gene { color: #c8bcff; border-color: rgba(160,150,255,.45); }

.perk-pool { border: 1px solid var(--edge-hot); background: rgba(226,197,128,.1); padding: 7px 12px; margin-bottom: 10px; border-radius: 6px; }
.perk-pool b { color: var(--gold); font-size: 17px; }
.perk-pool span { display: block; color: var(--dim); font-size: 10px; }
.perk { border: 1px solid var(--edge); padding: 6px 8px; margin-bottom: 6px; background: rgba(226,197,128,.05); border-radius: 6px; }
.perk.owned { border-color: var(--edge-hot); }
.perk.off { opacity: .5; }
.p-head { display: flex; align-items: center; gap: 6px; color: #fff; }
.p-head .ok { margin-left: auto; color: #b6f09a; font-size: 10px; }
.buy-btn {
  margin-left: auto; background: rgba(226,197,128,.16); border: 1px solid var(--edge-hot);
  color: var(--gold); font-family: var(--font); font-size: 11px; padding: 2px 9px; cursor: pointer; border-radius: 5px;
}
.buy-btn.off { border-color: rgba(226,197,128,.25); color: rgba(226,197,128,.4); cursor: not-allowed; }
.p-desc { color: var(--dim); font-size: 11px; margin: 2px 0; }
.p-req { color: #9c9078; font-size: 10px; }

.quest { border-left: 2px solid var(--edge-hot); padding: 5px 10px; margin-bottom: 8px; background: rgba(226,197,128,.05); border-radius: 0 6px 6px 0; }
.quest.done { border-color: #7dffb0; opacity: .7; }
.q-head { color: #fff; }
.q-desc { color: var(--dim); font-size: 11px; margin: 3px 0; line-height: 1.35; }
.q-prog { color: var(--gold); font-size: 11px; }
/* Шаги задания: сделанное вычеркнуто, текущее выделено, дальнейшее приглушено —
   чтобы «что делать сейчас» не приходилось угадывать. */
.q-step { font-size: 11px; line-height: 1.5; }
.q-step.done { color: #8fbf7a; text-decoration: line-through; opacity: .75; }
.q-step.now { color: var(--gold); }
.q-step.next { color: var(--dim); opacity: .5; }
.loc-line { display: flex; flex-direction: column; padding: 5px 0; border-bottom: 1px dotted rgba(226,197,128,.15); font-size: 12px; }
.loc-line span { color: var(--dim); font-size: 11px; }
.loc-line.cleared b { color: #7dffb0; }

.help-box h3 { color: var(--gold); font-size: 13px; letter-spacing: 1px; margin: 14px 0 6px; }
.help-box ul { padding-left: 18px; color: var(--dim); margin: 0; }
.help-box li { margin-bottom: 4px; }
.help-box p { color: var(--dim); font-size: 12px; line-height: 1.5; }
.help-box code { color: var(--gold); font-family: var(--mono); }

/* --- Инвентарь ------------------------------------------------------------ */
#inventory-screen { position: fixed; inset: 0; background: rgba(10,8,6,.75); z-index: 920; display: none; padding: 28px; }
#inventory-screen.open { display: block; }
.inv-wrap {
  height: 100%; border: 1px solid var(--edge); border-radius: 10px;
  background: var(--glass); backdrop-filter: blur(6px); display: flex; flex-direction: column;
}
.inv-head { display: flex; align-items: center; gap: 16px; padding: 10px 14px; border-bottom: 1px solid var(--edge); letter-spacing: 2px; color: var(--gold); }
.inv-head span { color: var(--dim); letter-spacing: 0; font-size: 11px; }
#inv-close {
  margin-left: auto; background: rgba(226,197,128,.16); border: 1px solid var(--edge-hot);
  color: var(--gold); padding: 4px 12px; cursor: pointer; border-radius: 5px;
}
.inv-main { flex: 1; display: grid; grid-template-columns: 250px 1fr 250px; gap: 14px; padding: 14px; min-height: 0; }
.inv-col { overflow-y: auto; }
.inv-sect { color: var(--gold); font-size: 11px; letter-spacing: 2px; margin: 6px 0; }
/*
   Ячейки инвентаря.

   Раньше сетки тянулись долями (`1fr`), и на широком экране ячейка вырастала
   до полутора сотен пикселей, а значок внутри оставался прежним — вещь
   терялась в пустой рамке. Теперь размер ячейки задан явно (`--cell`) и
   колонки под него подстраиваются: сетка занимает столько, сколько нужно, а
   не всю доступную ширину.
*/
.paperdoll, .pockets, .grid { display: grid; gap: 6px; justify-content: start; }
/* Кукла и карманы — строгие ряды: слоты в них парные по смыслу (голова/торс,
   левая/правая рука), и рассыпать их по ширине нельзя. */
.paperdoll { grid-template-columns: repeat(2, var(--cell, 56px)); }
.pockets { grid-template-columns: repeat(4, var(--cell, 56px)); }
/* Рюкзак и земля переносятся по месту: сколько ячеек влезло в колонку,
   столько в ряду. Жёсткие восемь столбцов распирали среднюю колонку шире
   окна, и карточку предмета выдавливало за край — она обрезалась.  */
.grid { grid-template-columns: repeat(auto-fill, var(--cell, 56px)); }
.cell {
  width: var(--cell, 56px); height: var(--cell, 56px);
  background: rgba(20,16,10,.45); border: 1px solid var(--edge); border-radius: 5px;
  display: grid; place-items: center; position: relative;
  /* Значок занимает ячейку, а не теряется в ней. */
  font-size: calc(var(--cell, 56px) * 0.56);
  line-height: 1;
}
.cell.filled { border-color: var(--edge-hot); cursor: grab; }
.cell.drop { border-color: var(--gold); background: rgba(226,197,128,.2); }
.cell .lbl { font-size: 9px; color: rgba(226,197,128,.45); text-align: center; padding: 2px; }
/* Счётчик поднят над полоской прочности: они делили один угол. */
.cell .cnt { position: absolute; right: 3px; bottom: 6px; font-size: 10px; color: var(--gold); }
.inv-tip { color: var(--dim); font-size: 11px; margin-top: 10px; }
.inv-col.info { border-left: 1px solid var(--edge); padding-left: 14px; }
.ii-head { display: flex; gap: 8px; align-items: center; color: #fff; font-size: 14px; }
.ii-ico { font-size: 26px; }
.ii-row { color: var(--dim); font-size: 11px; margin-top: 4px; }
.ii-sect { color: var(--gold); font-size: 11px; margin: 8px 0 4px; letter-spacing: 1px; }

::-webkit-scrollbar { width: 8px; height: 8px; }
::-webkit-scrollbar-track { background: rgba(20,16,10,.4); }
::-webkit-scrollbar-thumb { background: rgba(226,197,128,.35); border-radius: 4px; }


/* --- Меню поверженного тела ----------------------------------------------- */
.corpse-menu {
  position: fixed; z-index: 850; min-width: 170px; padding: 6px;
  display: flex; flex-direction: column; gap: 3px;
}
.cm-head { color: var(--gold); font-size: 12px; padding: 2px 6px 5px; border-bottom: 1px solid var(--edge); }
/* Сколько тел под этим: куча разбирается сверху вниз, и знать глубину надо
   до того, как начнёшь копать. */
.cm-deep { color: var(--dim); font-size: 10px; }
.cm-btn {
  background: rgba(226,197,128,.1); border: 1px solid var(--edge); color: var(--text);
  padding: 5px 8px; text-align: left; cursor: pointer; border-radius: 4px; font-family: var(--font); font-size: 12px;
}
.cm-btn:hover:not(.off) { background: rgba(226,197,128,.28); }
.cm-btn.off { opacity: .4; cursor: not-allowed; }
.cm-close { color: var(--dim); }

/* --- Инвентарь в духе «Проклятых земель» ---------------------------------- */
/* min-height, а не height: столбец с описанием прилипает к верху, и для этого
   сетка должна быть НЕ НИЖЕ своего содержимого. С жёсткой высотой прилипать
   было бы не к чему. */
/* minmax(0, 1fr) для середины: без нуля в минимуме колонка не может стать
   уже своего содержимого и распирает всю сетку. */
.inv-pz { display: grid; grid-template-columns: 200px minmax(0, 1fr) 240px; gap: 16px; min-height: 100%; }
.inv-stats { border-right: 1px solid var(--edge); padding-right: 12px; }
.st-row {
  display: grid; grid-template-columns: 1fr auto auto; gap: 6px; align-items: baseline;
  font-size: 12px; padding: 3px 0; border-bottom: 1px dotted rgba(226,197,128,.12);
}
.st-row span { color: var(--dim); }
.st-row b { color: #fff; font-family: var(--mono); }
.st-row i { font-style: normal; font-family: var(--mono); font-size: 11px; min-width: 44px; text-align: right; }
.st-row i.up { color: #8ef0a0; }
.st-row i.down { color: #ff8a7a; }
.inv-center { display: flex; flex-direction: column; min-height: 0; }
/* Кукла стоит вплотную к ячейкам экипировки, а не в другом конце строки:
   между ними взгляд ходит чаще всего — что надето и как это выглядит. */
.inv-doll-row { display: grid; grid-template-columns: auto auto; gap: 16px; align-items: start; justify-content: start; }
#inv-figure { background: rgba(20,16,10,.35); border: 1px solid var(--edge); border-radius: 6px; }
/*
   Карточка предмета прилипает к верху колонки.

   Прокрутка в инвентаре одна на всё окно, и стоило спуститься к рюкзаку, как
   описание предмета уезжало за экран: наводишь на вещь — и не видишь, что о
   ней написано. Ради этого описание и открывают.
*/
.inv-info {
  border-left: 1px solid var(--edge); padding-left: 14px;
  position: sticky; top: 0; align-self: start; max-height: 100%; overflow-y: auto;
}
.ground-title { color: #ffcf8a; }

/*
   Инвентарь на узком экране.

   Раскладка была жёстко трёхколонной (200px / 1fr / 240px) без единого порога,
   и на телефоне три колонки просто не помещались: рамка окна оставалась на
   месте, а содержимое вылезало вбок — кукла оказывалась за краем панели, а
   ячейки, вместо того чтобы переноситься, вытягивались в одну строку с
   горизонтальной прокруткой.

   Порог по ширине САМОГО окна профиля мерить нечем (media-запросы смотрят на
   экран), но окно и так занимает 94vw — значит порог экрана годится.
*/
@media (max-width: 900px) {
  .inv-pz { grid-template-columns: minmax(0, 1fr); gap: 12px; }
  /* Характеристики уезжают вниз: в бою из инвентаря смотрят на вещи, а не на
     таблицу чисел, — и первым экраном должна быть экипировка. */
  .inv-stats { order: 3; border-right: none; padding-right: 0; border-top: 1px solid var(--edge); padding-top: 10px; }
  .inv-center { order: 1; }
  .inv-info { order: 2; border-left: none; padding-left: 0; position: static; max-height: none; }
  .inv-doll-row { grid-template-columns: auto minmax(0, 1fr); gap: 10px; }
  #inv-figure { width: 100%; height: auto; max-width: 150px; }
}

/* Совсем узкий экран: кукла уходит под ячейки, иначе на них не остаётся места. */
@media (max-width: 560px) {
  .inv-doll-row { grid-template-columns: minmax(0, 1fr); justify-items: center; }
  #inv-figure { max-width: 120px; }
  .pm-body { padding: 10px 12px; }
  /* Слово «Профиль» рядом с семью вкладками съедало треть строки. На телефоне
     заголовок и так очевиден: открыто то, что открыл. */
  .pm-head { gap: 6px; padding: 8px 10px; }
  .pm-head b { display: none; }
  #pm-tabs { margin-left: 0; }
  #pm-tabs button { padding: 6px 9px; }
  /* Две колонки на узком экране превращались в две сплюснутые полоски, и
     страницу приходилось возить вбок. */
  .pm-cols { grid-template-columns: minmax(0, 1fr); gap: 12px; }
}


/* --- Прочность вещей ------------------------------------------------------ */
/*
   Полоска в углу ячейки и рамка по состоянию. Показываем только надломленное:
   у целой вещи полоска была бы шумом, а вот «уже вполсилы» и «сломано» игрок
   обязан заметить раньше, чем это выяснится в бою.
*/
.cell .dur {
  position: absolute; left: 3px; right: 3px; bottom: 2px; height: 3px;
  background: rgba(20,16,10,.7); border-radius: 2px; overflow: hidden;
}
.cell .dur b { display: block; height: 100%; }
.cell.worn { border-color: rgba(255,212,121,.7); }
.cell.broken { border-color: #ff6b6b; }
.cell.broken .ic { filter: grayscale(1) brightness(.7); }
.repair-btn {
  margin: 6px 0 2px; padding: 6px 8px; width: 100%; cursor: pointer; border-radius: 5px;
  /* Надпись переносится: «Починить: 3 × ржавых деталей (+54%)» в одну строку
     не влезает и вылезала за край панели. */
  white-space: normal; line-height: 1.3; text-align: center;
  background: rgba(226,197,128,.16); border: 1px solid var(--edge); color: var(--gold);
  font-family: var(--font); font-size: 11px;
}
.repair-btn:hover { background: rgba(226,197,128,.3); }
.repair-btn.off { opacity: .45; cursor: default; }
.ii-row.dim { color: var(--dim); font-size: 10px; }

/* Неизученный навык: видно, что он есть, и написано, откуда берётся. На вопрос
   «как получить новое заклинание» ответ «нигде не сказано» — худший из
   возможных, поэтому карточка называет книгу или честно признаёт, что книги
   для него в игре пока нет. */
.skill-card.unknown { opacity: .62; border-style: dashed; cursor: default; }
.skill-card.unknown:hover { opacity: .85; }
.sc-source { color: var(--gold); font-size: 11px; margin-top: 6px; }

/* Состояние тела в меню разделки: словами, а не числом. Игроку важно одно —
   успеет он взять своё или падальщики уже добрались. */
.cm-state { color: var(--dim); font-size: 11px; padding: 2px 8px 6px; }

/* Круговая защита на шкале: полоса от «сейчас» на всю оставшуюся длительность.
   Всё, что доедет до отметки внутри неё, отобьётся само — полоса и есть
   обещание, и стоит за ним тот же расчёт, что отводит удар. */
.tl-ward {
  position: absolute; left: 0; top: 0; bottom: 0; z-index: 0;
  background: linear-gradient(90deg, rgba(120,200,255,.34), rgba(120,200,255,.06));
  border-right: 2px solid rgba(159,220,255,.75);
  border-radius: 3px; pointer-events: none;
}

/* Предложение закрепить гостя за собой. Живёт в меню профиля, а не окном
   посреди игры: окно требует ответа сейчас, а человек занят тем, ради чего
   пришёл. */
.claim {
  border: 1px solid var(--edge-hot); border-radius: 6px;
  padding: 8px 10px; margin-bottom: 10px; background: rgba(226,197,128,.08);
}
.claim-why { color: var(--dim); font-size: 11px; line-height: 1.4; margin-bottom: 6px; }
.claim-row { display: flex; gap: 6px; }
.claim-row input {
  flex: 1; min-width: 0; background: rgba(20,16,10,.6); border: 1px solid var(--edge);
  color: var(--text); padding: 5px 7px; font-family: var(--mono); font-size: 12px; border-radius: 4px;
}
.claim-row button {
  background: rgba(226,197,128,.2); border: 1px solid var(--edge-hot); color: var(--gold);
  padding: 5px 10px; border-radius: 4px; cursor: pointer; font-family: var(--font); font-size: 12px;
}
.claim-row button:hover { background: rgba(226,197,128,.36); color: #fff; }
.claim-msg { color: var(--dim); font-size: 11px; min-height: 14px; margin-top: 4px; }
.claim-msg.err { color: var(--danger); }

/* ─── Сцена сказки ──────────────────────────────────────────────────────────
   Панель поверх поля, но не во весь экран: игрок стоит в том самом саду, о
   котором читает, и терять его из виду нельзя. Размытия подложки здесь нет
   намеренно — под панелью живая канва во весь экран, и размывать её каждый
   кадр дороже, чем весь игровой такт. */
.scene {
  position: absolute; left: 50%; transform: translateX(-50%);
  bottom: 96px; width: min(620px, 92vw); z-index: 900;
  pointer-events: auto;
  background: rgba(28, 22, 14, .93); border: 1px solid var(--edge);
  border-radius: 10px; padding: 14px 16px; box-shadow: 0 12px 40px #000;
  display: flex; flex-direction: column; gap: 10px;
}
.scene.hidden { display: none; }
.sc-head { color: var(--gold); font-size: 14px; letter-spacing: 1px; }
.sc-place { color: var(--dim); font-size: 11px; letter-spacing: 0; }
.sc-text {
  color: var(--text); font-size: 13px; line-height: 1.5; white-space: pre-line;
  max-height: 34vh; overflow-y: auto;
}
.sc-choices { display: flex; flex-direction: column; gap: 6px; }
.sc-btn {
  text-align: left; padding: 9px 11px; border-radius: 6px; cursor: pointer;
  background: rgba(226,197,128,.14); border: 1px solid var(--edge);
  color: var(--text); font-family: var(--font); font-size: 13px;
}
.sc-btn:hover { background: rgba(226,197,128,.3); border-color: var(--edge-hot); }
/* Погашенный ответ объясняет, чего не хватает: «нельзя» без причины читается
   как поломка игры, а не как выбор, к которому надо дорасти. */
.sc-btn[disabled] { opacity: .45; cursor: default; background: transparent; }
.sc-why { color: var(--dim); font-size: 11px; margin-left: 6px; }
