/* ---------- Игровой экран (по мотивам прототипа BSOnline) ---------- */

.game-page {
    height: 100vh;
    display: flex;
    flex-direction: column;
    padding: 6px;
    gap: 6px;
    min-width: 1100px;
}

.game-topbar {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 0 8px;
    height: 36px;
    flex-shrink: 0;
}
.game-title { font-family: var(--font-title); color: var(--heading); letter-spacing: .08em; margin-right: auto; text-shadow: 0 2px 4px #000; }
.connection-warning { color: #d9a45a; font-size: 13px; }
/* Число игроков — ровно посередине панели, что бы ни стояло по бокам. */
.game-topbar { position: relative; }
.game-online { position: absolute; left: 50%; transform: translateX(-50%); color: var(--text-2); font-size: 13px; white-space: nowrap; }
.game-topbar .btn { height: 30px; font-size: 13px; }

/* Phaser рисует поверх нативных элементов, которые сохраняют фокус, подсказки и нажатия. */
.view-tabs:has(> .phaser-ui-layer), .character-box:has(> .phaser-ui-layer), .action-surface { position: relative; }
.phaser-ui-layer { position: absolute; inset: 0; overflow: hidden; pointer-events: none; visibility: hidden; z-index: 1; }
.phaser-ui-layer[data-ui-renderer="phaser"] { visibility: inherit; }
.crafts-sheet > .phaser-ui-layer { z-index: 4; }
.phaser-ui-layer > canvas { display: block; position: absolute; }
.modal:has(> .phaser-ui-layer) { position: relative; }
[data-phaser-ink] { -webkit-text-fill-color: transparent; }
[data-phaser-ink], [data-phaser-ink] * { text-shadow: none !important; }
.view-tabs:has(> .phaser-ui-layer[data-ui-renderer="phaser"]) > button,
.character-box:has(> .phaser-ui-layer[data-ui-renderer="phaser"]) .bar,
.action-surface:has(> .phaser-ui-layer[data-ui-renderer="phaser"]) > .action-panel .action-box { opacity: 0; }
.inventory-box:has(> .phaser-ui-layer[data-ui-renderer="phaser"]) > :is(.box-header, .inv-filters, .box-content),
.holder-box:has(> .phaser-ui-layer[data-ui-renderer="phaser"]) > :is(.box-header, .box-content),
.phaser-storage:has(> .phaser-ui-layer[data-ui-renderer="phaser"]) > :is(.box-header:not(:has(button, input, select, textarea)), .inv-filters, .bar),
.phaser-storage:has(> .phaser-ui-layer[data-ui-renderer="phaser"]) > .box-content > :is(.empty-note, .pay-line, .market-hint, .counter-group, .tannery-slot.empty, .item-card, div:has(> .item-card):not(.market-offer, .tannery-slot)),
.phaser-storage:has(> .phaser-ui-layer[data-ui-renderer="phaser"]) > .box-content > :is(.market-offer, .tannery-slot) > .item-card,
.phaser-storage:has(> .phaser-ui-layer[data-ui-renderer="phaser"]) > .box-content > .counter-line :is(.entity-icon, .text-0, .text-1, .text-2, .bar, .range-value),
.chat-panel:has(> .phaser-ui-layer[data-ui-renderer="phaser"]) > .chat-main > .chat-tabs,
.chat-panel:has(> .phaser-ui-layer[data-ui-renderer="phaser"]) > .chat-grip { opacity: 0; }
.phaser-storage { position: relative; }
.building-head:has(> .phaser-ui-layer), .phaser-window-content, [data-phaser-surface] { position: relative; }
:is(.building-head, .phaser-window-content, [data-phaser-surface]):has(> .phaser-ui-layer[data-ui-renderer="phaser"]) [data-phaser-window],
[data-phaser-portrait]:has(> .phaser-ui-layer[data-ui-renderer="phaser"]) > :is(.horse-portrait, .creature-portrait),
.gear-doll:has(> .phaser-ui-layer[data-ui-renderer="phaser"]) > :is(.gear-horse, .gear-threads, .gear-slot) { opacity: 0; }

/* Переключатель языка: две метки одной группой, текущая горит золотом. */
.lang-switch { display: inline-flex; border: 1px solid var(--border-hover); border-radius: 4px; overflow: hidden; }
.lang-switch button {
    background: transparent;
    border: 0;
    color: var(--text-2);
    font: inherit;
    font-size: 12px;
    letter-spacing: .06em;
    padding: 4px 8px;
    cursor: pointer;
}
.lang-switch button + button { border-left: 1px solid var(--border-hover); }
.lang-switch button:hover:not(:disabled) { color: var(--heading); }
.lang-switch button.on { background: linear-gradient(#6b5231, #3f2f1b); color: #f0e2c4; cursor: default; }
/* Выбор сейчас не к месту (волны на простой карте): гаснет вся группа. */
.lang-switch.dim { opacity: .45; }

/* Меню игры (по образцу автора, 01.11.2026): тёмная панель в тонкой золотой рамке, заголовок с засечками и крестик;
   пункты со значками группами «Общение» и «Игра»; «Сменить персонажа» и «Выйти» — отдельной группой ниже, за чертой
   (автор, 09.10.2026): чтобы не нажать нечаянно. Раздел встаёт на место меню; прокручивается только список. */
.game-menu { display: flex; flex-direction: column; max-height: calc(100vh - 32px); padding: 22px 26px 18px;
    border-color: #4a3f2e; background: linear-gradient(rgba(26, 24, 22, .97), rgba(16, 15, 14, .97)); }
.game-menu.gm-main { width: 440px; }
.game-menu.gm-page { width: 680px; max-width: none; overflow-y: auto; scrollbar-width: thin; }
.game-menu.gm-settings { width: 760px; max-width: none; }
.gm-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; }
.gm-head h2 { margin: 0; font: 400 32px/1.15 var(--font-title); color: #e6d8bb; letter-spacing: .01em; }
.gm-page .gm-head h2 { font-size: 28px; }
.gm-close { flex: none; width: 30px; height: 30px; margin: -2px -6px 0 0; padding: 4px; border: 0; background: none; color: #a89e8c; cursor: pointer; }
.gm-close:hover { color: var(--heading); }
.menu-icon { display: block; width: 26px; height: 26px; flex: none; }
.menu-icon .line { fill: none; stroke: currentColor; stroke-width: 2.6; stroke-linecap: round; stroke-linejoin: round; }
.gm-sub { margin: 2px 0 10px; color: var(--text-2); font-size: 14px; }

/* «Продолжить» — широкая золотая, с меткой Esc справа. */
.game-menu .gm-continue { justify-content: space-between; width: 100%; height: 54px; margin-top: 18px; padding: 0 18px 0 26px; font-size: 20px; text-transform: none; }
.gm-continue kbd { padding: 3px 9px; border: 1px solid rgba(240, 226, 196, .35); border-radius: 4px; background: rgba(0, 0, 0, .3);
    font: 500 14px var(--font-body); color: #e8dcc2; }

/* Подпись группы: прописными вразрядку, черта до края. */
.gm-section { display: flex; align-items: center; gap: 14px; margin: 22px 0 6px; color: #8d8578; font: 500 12px var(--font-body);
    letter-spacing: .22em; text-transform: uppercase; }
.gm-section::after { content: ""; flex: 1; height: 1px; background: #34302b; }
.gm-settings .gm-section:first-child { margin-top: 6px; }

/* Пункт меню: значок, надпись, справа «›». */
.gm-item { display: flex; align-items: center; gap: 16px; width: 100%; min-height: 46px; padding: 6px 8px 6px 4px; border: 0; border-radius: 6px;
    background: none; color: #ddd3bf; font: 400 17px var(--font-body); text-align: left; text-decoration: none; cursor: pointer; }
.gm-item > .menu-icon:first-child { color: #c8bea7; }
.gm-item:hover { background: rgba(176, 141, 87, .08); color: #f0e2c4; }
.gm-label { flex: 1; }
.gm-chevron { width: 18px; height: 18px; color: #8d8578; }
.gm-item:hover .gm-chevron { color: var(--gold-hover); }
.gm-pill { padding: 3px 12px; border: 1px solid #8a6a3a; border-radius: 6px; background: linear-gradient(#6b5231, #4a3820); color: #f0e2c4; font-size: 13px; }
.gm-leave { margin-top: 16px; padding-top: 12px; border-top: 1px solid #34302b; }

/* Низ окна: режим админа на главной; «В меню» и «Готово» — у раздела. */
.gm-foot { display: flex; align-items: center; justify-content: flex-end; gap: 12px; margin: 16px -26px 0; padding: 14px 26px 0; border-top: 1px solid #34302b; flex: none; }
.gm-foot.gm-admin { justify-content: space-between; color: var(--text-2); font-size: 15px; }
.game-menu .gm-foot .btn { height: 40px; min-width: 120px; }
.game-menu .gm-foot .btn.primary { min-width: 160px; font-size: 18px; text-transform: none; }
.gm-back { margin-right: auto; }
.gm-back .menu-icon { width: 18px; height: 18px; }
.gm-saved { display: flex; align-items: center; gap: 10px; margin-right: auto; color: var(--text-2); font-size: 14px; }
.gm-saved .menu-icon { width: 26px; height: 26px; padding: 5px; border: 1.5px solid var(--gold); border-radius: 50%; color: var(--gold-hover); }
.gm-saved + .gm-back { margin-right: 0; }

/* Выключатель: золотой — включено. */
.menu-switch { position: relative; flex: none; width: 52px; height: 28px; padding: 0; border: 1px solid #3c3833; border-radius: 14px; background: #1f1d1b;
    box-shadow: inset 0 1px 3px rgba(0, 0, 0, .6); cursor: pointer; transition: background .15s, border-color .15s; }
.menu-switch span { position: absolute; top: 3px; left: 3px; width: 20px; height: 20px; border-radius: 50%; background: #5d5852; transition: transform .15s, background .15s; }
.menu-switch.on { border-color: #a07e4a; background: linear-gradient(#8a6a3a, #6b5231); }
.menu-switch.on span { transform: translateX(24px); background: #f3ead8; }
.menu-switch:disabled { opacity: .4; cursor: not-allowed; }
.menu-switch:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }
@media (prefers-reduced-motion: reduce) { .menu-switch, .menu-switch span { transition: none; } }

/* Вкладки настроек: золотая черта под открытой. */
.gm-tabs { display: flex; gap: 6px; margin: 14px 0 0; border-bottom: 1px solid #34302b; flex: none; }
.gm-tabs button { padding: 10px 22px; border: 0; border-bottom: 2px solid transparent; margin-bottom: -1px; background: none;
    color: #a39a8b; font: 400 17px var(--font-body); cursor: pointer; }
.gm-tabs button:hover { color: var(--heading); }
.gm-tabs button.active { color: #e2c48c; border-bottom-color: var(--gold-hover); }
.gm-scroll { flex: 1; min-height: 0; overflow-y: auto; padding: 12px 2px 0; scrollbar-width: thin; }

/* Строка настройки: название и пояснение слева, выбор справа; строки разделены чертой. */
.gm-opt { display: flex; align-items: center; justify-content: space-between; gap: 20px; padding: 12px 0; }
.gm-opt + .gm-opt { border-top: 1px solid #2a2724; }
.gm-opt.dim .gm-opt-text { opacity: .55; }
.gm-opt-text { min-width: 0; }
.gm-opt-text h3 { margin: 0; font: 400 18px var(--font-body); color: #e3d8c3; }
.gm-opt-text p { margin: 3px 0 0; color: var(--text-2); font-size: 14px; }
.gm-opt-icons { align-items: flex-start; }
.gm-choice { display: inline-flex; flex: none; border: 1px solid #4b4540; border-radius: 6px; overflow: hidden; }
.gm-choice button { display: inline-flex; align-items: center; justify-content: center; gap: 10px; min-width: 150px; height: 44px; padding: 0 20px;
    border: 0; background: transparent; color: #cfc5b2; font: 400 17px var(--font-body); cursor: pointer; }
.gm-choice button + button { border-left: 1px solid #4b4540; }
.gm-choice button:hover:not(.on) { color: var(--heading); background: rgba(255, 255, 255, .03); }
.gm-choice button.on { background: linear-gradient(#6b5231, #45341e); color: #f3e6c9; box-shadow: inset 0 0 0 1px #a07e4a; cursor: default; }
.gm-choice .menu-icon { width: 18px; height: 18px; }
.gm-icons-pick { display: flex; flex-direction: column; gap: 10px; }
.gm-icons-pick .gm-choice button { min-width: 0; flex: 1; }
.gm-icons-pick .gm-choice { display: flex; }
.gm-samples { display: grid; grid-template-columns: 1fr 1fr; }
.gm-samples > span { display: flex; justify-content: center; gap: 10px; padding: 0 12px; cursor: pointer; opacity: .55; }
.gm-samples > span + span { border-left: 1px solid #34302b; }
.gm-samples > span.on { opacity: 1; }
.gm-samples img { width: 40px; height: 40px; border: 1px solid #5a4a30; border-radius: 4px; object-fit: cover; }
.gm-settings .keys-list { margin-top: 4px; }

/* Списки разделов: друзья, противники, игнорируемые. Прокручивается только список. */
.gm-page .gm-scroll, .gm-list { scrollbar-width: thin; }
.gm-h3 { display: flex; align-items: center; gap: 10px; margin: 0; font: 400 20px var(--font-title); color: #e6d8bb; }
.gm-h3.gm-rule, .gm-opt.gm-rule { margin-top: 14px; padding-top: 12px; border-top: 1px solid #34302b; }
.gm-count { min-width: 24px; height: 24px; padding: 0 7px; border-radius: 12px; background: #3d3122; color: #e8d6b0; font: 500 13px/24px var(--font-body); text-align: center; }
.gm-box { margin-bottom: 12px; padding: 10px 14px 6px; border: 1px solid #3a342c; border-radius: 6px; background: rgba(0, 0, 0, .2); }
.gm-box .gm-h3 { padding-bottom: 8px; border-bottom: 1px solid #2e2a25; font-size: 18px; }
.gm-list-head { display: flex; align-items: center; justify-content: space-between; margin-top: 4px; }
.gm-search { display: flex; align-items: center; gap: 10px; margin: 10px 0 8px; padding: 0 12px; border: 1px solid #3c3833; border-radius: 6px; background: rgba(0, 0, 0, .25); }
.gm-search .menu-icon { width: 18px; height: 18px; color: #8d8578; }
.gm-search input { flex: 1; height: 36px; border: 0; padding: 0; background: none; outline: none; }
.gm-list { flex: 1 1 auto; min-height: 180px; max-height: 420px; overflow-y: auto; border: 1px solid #3a342c; border-radius: 6px; background: rgba(0, 0, 0, .2); }
.gm-row { display: flex; align-items: center; gap: 12px; min-height: 52px; padding: 6px 12px; }
.gm-list .gm-row + .gm-row, .gm-box .gm-row + .gm-row { border-top: 1px solid #2a2724; }
.gm-row-name { color: #e3d8c3; font: 400 17px Georgia, "Times New Roman", serif; overflow-wrap: anywhere; }
.gm-list > .gm-row > .gm-row-name { flex: 1; }
.gm-box .gm-row-name { flex: 1; }
.gm-who { display: flex; flex-direction: column; flex: 1; min-width: 0; }
.gm-where { color: var(--text-2); font-size: 13px; line-height: 1.35; }
.gm-dot { flex: none; width: 10px; height: 10px; border-radius: 50%; background: #5d5852; }
.gm-dot.online { background: #6cc04a; box-shadow: 0 0 6px rgba(108, 192, 74, .55); }
.gm-row-buttons { display: flex; align-items: center; gap: 8px; flex: none; }
.game-menu .gm-row-buttons .btn { height: 34px; padding: 0 16px; }
.gm-mark { display: flex; align-items: center; gap: 8px; margin-right: 4px; color: var(--text-2); font-size: 13px; }
.gm-mark .menu-switch { width: 42px; height: 24px; }
.gm-mark .menu-switch span { width: 16px; height: 16px; }
.gm-mark .menu-switch.on span { transform: translateX(18px); }
.gm-mark.dim > span { opacity: .45; }
.game-menu .gm-more { width: 40px; padding: 0; }
.game-menu .gm-more.on { border-color: var(--gold); color: var(--heading); }
.gm-more .menu-icon { width: 20px; height: 20px; }
.gm-add { display: flex; gap: 12px; margin-top: 10px; }
.gm-add input { flex: 1; height: 40px; }
.game-menu .gm-add .btn { height: 40px; text-transform: none; }
.gm-page .gm-opt { padding-bottom: 0; }
.gm-page .gm-opt-text p { font-size: 13px; }
.gm-switch-label { display: flex; align-items: center; gap: 10px; color: var(--text-2); }
.game-menu .field-error { margin: 8px 0 0; }
.game-menu .settings-hint { margin-top: 6px; }
@media (max-width: 800px) {
    .game-menu.gm-page, .game-menu.gm-settings { width: 100%; }
    .gm-row { flex-wrap: wrap; }
    .gm-mark > span { display: none; }
}
.keys-list { display: flex; flex-direction: column; gap: 8px; margin-top: 14px; }
.keys-list > div { display: flex; align-items: baseline; gap: 6px; }
.keys-list span { margin-left: 8px; color: var(--text-2); font-size: 13px; }
.keys-list kbd { min-width: 22px; padding: 1px 6px; border: 1px solid #5a3a10; border-radius: 4px; background: rgba(12, 10, 8, .78);
    color: #d9c8a4; font: 600 12px var(--font-body); text-align: center; }
/* Почта: ящик с письмами и стол, за которым пишут. Письмо — окно с текстом как он написан, с переводами строк. */
.post-box { width: 100%; }
.post-letters { display: flex; flex-direction: column; gap: 4px; max-height: 190px; overflow-y: auto; padding: 4px; scrollbar-width: thin; }
.post-letter { display: flex; align-items: center; gap: 8px; min-width: 0; }
.post-letter .entity-icon { width: 30px; height: 30px; flex: none; }
.post-letter-actions { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: 4px; flex: none; }
.post-date { margin-left: 8px; }
.post-empty { margin: 6px 8px; }
.post-write { display: flex; flex-direction: column; gap: 6px; width: 100%; }
.post-to { display: flex; align-items: center; gap: 8px; }
.post-to input { width: 200px; height: 30px; }
/* Стенд «Разыскивается»: подписи шире почтовой «Кому» — «Ставка, медных» не должна ломаться в две строки. */
.board .post-to label { flex: 0 0 120px; }
.board .post-to select { flex: 1; height: 30px; }
.post-write textarea { width: 100%; box-sizing: border-box; resize: vertical; min-height: 96px; padding: 6px 8px; font: inherit; line-height: 1.4;
    color: inherit; background: #0e0e0e; border: 1px solid #323232; border-radius: 4px; }
.post-write .building-actions { display: flex; align-items: center; justify-content: space-between; }
.letter { width: 520px; }
.letter-text { max-height: 46vh; overflow-y: auto; margin: 10px 0; padding: 12px 14px; white-space: pre-wrap; overflow-wrap: anywhere; line-height: 1.5;
    color: #2a2118; background: #d8c9a8; border: 1px solid #8a7450; border-radius: 3px; scrollbar-width: thin; }
/* Летопись (docs/LIBRARY.md): записка и страница книги — на пергаменте автора; у страницы — буквица, как в древней рукописи
   (автор, 28.10.2026: «первая буква — произведение искусства»). */
/* Рамка вокруг листа — уже (автор, 28.10.2026): пергамент почти во всё окно. Записки — рукописным Bad Script, книга — книжной
   антиквой Kurale (app.css); буквица на странице книги — гравюра автора (art/ui/initials), первая буква текста прячется под ней. */
.modal.note { width: 580px; padding: 10px 12px 12px; }
.modal.note h2 { margin: 2px 0 6px; font-size: 18px; }
.modal.note .actions { margin-top: 6px; }
.letter-text.parchment { max-height: 62vh; margin: 4px 0; padding: 22px 26px; color: #2b1d10; font-family: "Marck Script", Georgia, "Times New Roman", serif; font-size: 18px; line-height: 1.5;
    background: #e7d7b4 url("images/ui/library/parchment.webp") center / cover; border-color: #7a6039; box-shadow: inset 0 0 30px rgba(90, 60, 20, .35); }
.letter-text.manuscript { font-family: Kurale, Georgia, "Times New Roman", serif; font-size: 16px; line-height: 1.55; }
/* Страница-гравюра (автор, 28.10.2026: «иллюстрации отдельными страницами»): одна картина во весь лист, чернила без бумаги. */
.letter-text.plate-page { display: flex; align-items: center; justify-content: center; padding: 14px; }
.letter-text.plate-page .book-plate { margin: 0; border: none; box-shadow: none; max-height: 58vh; width: auto; max-width: 100%; }
.letter-text.manuscript .initial { float: left; width: 84px; height: auto; margin: 2px 12px 4px 0; border: 1px solid #5a4328; box-shadow: 1px 2px 3px rgba(0, 0, 0, .35); }
.letter-text.manuscript .initial-glyph { display: none; }
/* Гравюры автора в книге (art/ui/library/plates): к странице — во всю ширину листа над текстом, виньетка — по центру под ним,
   герб — концовка последней страницы. Лёгкий тон, чтобы пергамент просвечивал. */
.book-plate { display: block; width: 100%; height: auto; margin: 0 0 14px; mix-blend-mode: multiply; }
.book-vignette { display: block; width: 150px; height: auto; margin: 14px auto 0; mix-blend-mode: multiply; opacity: .9; }
.book-vignette.arms { width: 200px; margin-top: 22px; }
.book-nav { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.book-nav .text-2 { color: var(--muted); }
/* Рецепт на пергаменте (шаг 3): строки «что и сколько класть»; у котелка — кнопки известных рецептов. */
.recipe-sheet { white-space: normal; }
.recipe-sheet p { margin: 6px 0; }
.recipe-where { margin-bottom: 8px; color: #5a4328; font-style: italic; }
.pot-recipes { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; padding: 6px 8px 2px; }
.pot-recipe { padding: 3px 8px; font-size: 12px; }
/* Полка читальни над разговором: фолиант автора со страницами и записки листами. */
.library-shelf { display: flex; flex-wrap: wrap; gap: 10px; align-items: stretch; }
.library-book { display: flex; align-items: center; gap: 10px; padding: 8px 10px; border: 1px solid #2e2c2a; border-radius: 6px; background: rgba(0, 0, 0, .35); }
.library-art { width: 72px; height: 72px; object-fit: contain; filter: drop-shadow(0 2px 3px rgba(0, 0, 0, .6)); }
.library-pages { display: flex; gap: 6px; margin-top: 6px; }
.library-page { min-width: 34px; padding: 4px 8px; }
.library-note { display: flex; flex-direction: column; align-items: center; gap: 2px; padding: 6px 8px; border: 1px solid #2e2c2a; border-radius: 6px;
    background: rgba(0, 0, 0, .35); color: inherit; cursor: pointer; font: inherit; }
.library-note:hover { border-color: #8a7450; background: rgba(60, 45, 20, .45); }
.library-note .library-art { width: 56px; height: 56px; }
/* Рынок: палатки и товар с ценой справа. */
/* Вход: две большие кнопки с подписью под названием. */
.market-entry .btn { flex: 1; display: flex; flex-direction: column; align-items: center; gap: 4px; padding: 14px 10px; }
.storage-box.market-box { flex-shrink: 0; }
.market-box .box-content { min-height: 96px; }
.market-offer { display: flex; align-items: center; gap: 8px; }
.market-till { display: flex; align-items: center; justify-content: space-between; gap: 12px; text-align: left; }
.market-till .btn { flex: none; width: 170px; }
.market-hint { padding: 2px 6px 6px; text-align: left; }
.market-offer > .item-card { flex: 1; min-width: 0; }
.market-offer.expired { opacity: .6; }
.market-price { flex: none; min-width: 150px; display: flex; flex-direction: column; align-items: flex-end; gap: 2px; padding-right: 8px; color: var(--gold); font: 600 14px var(--font-body); }
.market-partial { display: inline-flex; align-items: center; gap: 6px; font: 400 12px var(--font-body); color: var(--text-1, #cfc6ba); cursor: pointer; }
.market-name { display: flex; align-items: center; gap: 8px; padding: 6px 0; }
.market-name input { flex: 1; min-width: 0; }
.market-rows { text-align: left; }
.market-rows .vessel-row { margin: 4px 6px; width: calc(100% - 12px); }
.market-price-row { display: flex; align-items: center; gap: 10px; margin: 8px 0; }
.market-price-row input { width: 140px; }
/* Ряды (19.10.2026): вкладки «Палатки» и «Товары» со строкой поиска над списком. */
.market-tools { display: flex; align-items: center; gap: 8px; padding: 2px 0 8px; }
.market-tabs { display: flex; gap: 4px; flex: none; }
.market-tabs .btn { padding: 0 12px; height: 30px; }
.market-tabs .btn .count { margin-left: 5px; font-size: 12px; opacity: .7; }
.market-search { flex: 1; min-width: 0; height: 30px; }
.market-rows .inv-filters { margin: -4px 0 4px; }
/* Палатка в рядах: сама строка — кнопка; звёздочка избранного лежит в её правом верхнем углу,
   как у карточки вещи (автор, 19.10.2026), и так же показывается при наведении или когда стоит.
   Кнопку в кнопку класть нельзя — звезда соседка строки, наложенная сверху. */
.market-rows .stall-row { position: relative; margin: 4px 6px; }
.market-rows .stall-row .vessel-row { margin: 0; width: 100%; padding-right: 28px; }
.market-rows .stall-row.closed .vessel-row { opacity: .55; cursor: default; }
.market-rows .stall-row.closed .vessel-row:hover { border-color: #2e2c2a; }
.stall-fav { position: absolute; top: 5px; right: 6px; font-size: 16px; }
.stall-row:hover .stall-fav, .stall-fav.on, .stall-fav:focus-visible { opacity: 1; }
.stall-owner { display: inline-flex; align-items: center; gap: 4px; }
.stall-owner .stall-fav { position: static; opacity: 1; font-size: 15px; line-height: 1; }
.stall-badge { display: inline-block; margin-left: 6px; padding: 0 6px; border: 1px solid rgba(63, 138, 192, .6); border-radius: 8px; font: 400 11px var(--font-body); color: #8cc8ea; vertical-align: middle; }
/* Что лежит в палатке — значками, без захода в неё. */
.stall-goods { display: flex; flex-wrap: wrap; align-items: center; gap: 3px; margin-top: 4px; }
.stall-goods .entity-icon { width: 26px; height: 26px; margin: 0; font-size: 13px; border-radius: 4px; }
.stall-goods .more { padding-left: 4px; }
/* Витрина и «Товары» (автор, 19.10.2026): товар — карточкой вещи с её подсказкой, справа вместо стрелки цена;
   по ней кладут на прилавок. Уже лежащее на прилавке приглушено, как в сумах у лавки. */
.market-rows .market-good { margin: 0 6px; }
.card-price { flex: none; align-self: center; margin-right: 4px; padding: 2px 6px; background: none; border: 1px solid transparent; border-radius: 5px; color: var(--gold); font: 600 13px var(--font-body); white-space: nowrap; cursor: pointer; }
.card-price:hover:not(:disabled) { border-color: var(--gold); color: var(--heading); }
.card-price:disabled { cursor: default; }
.card-note { color: var(--gold); }
.market-offer.on-counter .item-card { --item-card-opacity: .65; filter: grayscale(.6); }
.trade-box .market-offer { padding: 2px 4px; border-bottom: 1px solid #2e2c2a; }
/* Цена своего товара правится на месте: щёлкнул — поле. */
.market-price-btn { display: inline-flex; align-items: center; gap: 4px; padding: 0; background: none; border: 0; color: inherit; font: inherit; cursor: pointer; }
.market-price-btn .pen { font-size: 12px; opacity: .45; }
.market-price-btn:hover { color: var(--heading); }
.market-price-btn:hover .pen { opacity: 1; }
.market-price-edit { display: flex; align-items: center; gap: 4px; }
.market-price-edit input { width: 92px; height: 28px; }
.market-price-edit .btn { height: 28px; padding: 0 8px; }
/* Окно перелива: две ёмкости по сторонам, ползунок делит налитое между ними. */
.pour-sides { display: flex; align-items: stretch; gap: 10px; margin: 8px 0 4px; }
.pour-side { flex: 1; display: flex; flex-direction: column; align-items: center; gap: 2px; padding: 8px; border: 1px solid #2c2c2c; border-radius: 6px; background: rgba(0, 0, 0, .25); }
.pour-side .entity-icon { width: 44px; height: 44px; }
.pour-side strong { font: 600 20px var(--font-title); color: var(--gold); }
.pour-side .bar { width: 100%; margin-top: 4px; }
.pour-arrow { align-self: center; font-size: 22px; color: var(--gold); min-width: 24px; text-align: center; }
.pour-range input { width: 100%; }
/* Увеличить размер: линия размеров. Пройденные и ещё не открытые метки тусклые, нынешняя светлая, следующая золотая. */
.enlarge-track { width: 100%; }
/* Метки стоят под ходом ползунка: он короче линии на свою ширину, отсюда поля по краям. */
.enlarge-marks { position: relative; height: 18px; margin: 4px 9px 0; font: 600 12px var(--font-body); font-variant-numeric: tabular-nums; }
.enlarge-mark { position: absolute; top: 0; transform: translateX(-50%); white-space: nowrap; color: var(--text-2, #9a9186); opacity: .45; }
/* Пояснение бывает в одну строку и в три, отказ — есть или нет: место держим под самое длинное. */
.enlarge-note { min-height: 4.5em; margin: 0; }
.enlarge-error { min-height: 1.5em; margin: 0; }
.enlarge-mark.current { color: var(--heading); opacity: 1; }
.enlarge-mark.next { color: var(--gold); opacity: 1; }
.enlarge-unit { text-align: center; padding-top: 2px; }
/* Выбор ёмкости: строка-кнопка на каждую. */
.vessel-row { width: 100%; text-align: left; cursor: pointer; color: inherit; font: inherit; }
.vessel-row:hover { border-color: var(--gold); }
.vessel-row .entity-info { display: flex; flex-direction: column; }
/* Окно котелка: над костром и в среднем окне. */
.storage-box.pot-box { margin-bottom: 8px; flex: 0 0 auto; }
.storage-box.pot-box .box-content { flex: 0 0 auto; min-height: 96px; max-height: 220px; overflow-y: auto; }
.pot-box .empty-note { display: flex; align-items: center; justify-content: center; min-height: 84px; }
/* Кормушка в конюшне (StableRules): как котелок — ящик своей высоты с окошком, куда перетаскивают зерно и солому. */
.storage-box.manger-box { margin: 8px 0; flex: 0 0 auto; }
.storage-box.manger-box .box-content { flex: 0 0 auto; min-height: 96px; max-height: 220px; overflow-y: auto; }
.manger-box .empty-note { display: flex; align-items: center; justify-content: center; min-height: 84px; margin: 6px; border: 1px dashed rgba(201, 164, 104, .4); border-radius: 6px; }
.manger-box.drop-target .empty-note { border-color: var(--gold); }
.stable-pay { margin: 8px 0; flex: 0 0 auto; }
.stable-pay .box-content { text-align: left; }
.stable-pay .pay-line { margin: 6px 0 2px; color: var(--heading); font-variant-numeric: tabular-nums; }
.pot-box .pot-icon { display: inline-block; width: 22px; height: 22px; vertical-align: middle; margin-right: 6px; border: 0; background-color: transparent; background-size: contain; }
.pot-row { display: flex; align-items: center; gap: 8px; padding: 4px 6px; }
.pot-row .entity-icon { width: 34px; height: 34px; flex: none; }
.pot-what { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.pot-what .heat-bar { margin: 2px 0 0; }
/* Ширина — как у экранов строений (склад, костёр): 560 по центру; заголовок не слипается. */
.pot-screen { width: 100%; height: 100%; box-sizing: border-box; overflow-y: auto; scrollbar-gutter: stable; padding: 10px max(10px, calc(50% - 280px)) 12px; }
.pot-box .box-header { display: flex; justify-content: space-between; align-items: center; gap: 12px; }
/* Набор ювелира (docs/JEWELER.md): окошко, как у котелка, плюс дела под содержимым — кнопки и рецепты. */
.jewel-box .jewel-deeds { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; padding: 8px 0 4px; }
.jewel-box .jewel-deeds .text-2 { flex-basis: 100%; }
.jewel-box .recipe-list { margin-top: 6px; }
/* Окно еды: подсказка «досыта» и кнопка при ней. */
.eat-enough .btn { padding: 2px 10px; margin-left: 8px; font-size: 12px; }
/* Значок писем слева от «Меню»: конверт и число в кружке. */
.game-topbar .mail-open { position: relative; width: 38px; padding: 0; color: #d9c8a4; }
.mail-open svg { width: 20px; height: 16px; fill: none; stroke: currentColor; stroke-width: 1.5; stroke-linejoin: round; vertical-align: middle; }
.mail-count { position: absolute; right: 1px; top: 1px; min-width: 15px; height: 15px; padding: 0 3px; box-sizing: border-box; border-radius: 8px;
    background: var(--gold); color: #1a1408; font: 700 10px/15px var(--font-body); text-align: center; }
.game-topbar .menu-open .chat-unread { margin-left: 6px; }
.menu-mute { display: flex; gap: 6px; margin-top: 12px; }
.menu-mute input { flex: 1; height: 30px; }
.menu-mute .btn { height: 30px; }
.settings { width: 440px; }
.settings h2 { margin-top: 0; }
.settings-row { display: flex; align-items: center; justify-content: space-between; gap: 16px; margin-top: 14px; }
.settings-name { color: var(--heading); }
.settings .lang-switch button { padding: 4px 12px; }
.settings-hint { margin: 4px 0 0; color: var(--text-2); font-size: 13px; }

.game-board {
    flex: 1;
    min-height: 0;
    display: grid;
    grid-template-columns: 300px minmax(0, 1fr) 300px;
    gap: 0;
    background: rgba(0, 0, 0, .6);
    border: 4px double #1e1e1e;
    border-radius: 8px;
}

.game-col { min-height: 0; display: flex; flex-direction: column; padding: 3px; }
.game-col.left { border-right: 1px solid transparent; border-image: linear-gradient(transparent, #3a3633, transparent) 1; }
.game-col.right { border-left: 1px solid transparent; border-image: linear-gradient(transparent, #3a3633, transparent) 1; }
.right-scroll { display: flex; flex: 1; flex-direction: column; min-height: 0; overflow-y: auto; scrollbar-width: none; }
.right-scroll::-webkit-scrollbar { display: none; }

.col-scroll { flex: 1; min-height: 0; overflow-y: auto; scrollbar-width: none; }
.col-scroll::-webkit-scrollbar { display: none; }

/* ---------- Текст ---------- */
.text-0, .text-1, .text-2 {
    color: #b0b0b0;
    text-overflow: ellipsis;
    overflow: hidden;
    text-shadow: #000 1px 0 0, #000 0 1px 0, #000 -1px 0 0, #000 0 -1px 0;
}
.text-0 { font-size: 15px; white-space: nowrap; }
.text-1 { font-size: 12px; }
.text-2 { font-size: 11px; color: #8a8580; }

/* ---------- Блоки ---------- */
.box {
    position: relative;
    display: block;
    width: calc(100% - 6px);
    margin: 3px;
    padding: 4px;
    background: linear-gradient(to bottom, #161616 0%, #1c1c1c 7%, #131313 100%);
    border: 1px solid #2e2e2e;
    border-radius: 6px 6px 0 6px;
    box-shadow: 0 5px 6px -3px #000;
    color: inherit;
    font: inherit;
    text-align: left;
}
button.box { cursor: pointer; }
button.box:hover, .box.selected { border-color: #4b4b4b; background: linear-gradient(to bottom, #3c3c3c 0%, #202020 7%, #161616 100%); }

.box-header {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    border-bottom: 1px solid #2e2e2e;
    box-shadow: 0 5px 6px -3px #000;
    padding: 1px 4px 3px;
    margin-bottom: 4px;
}
.box-content { background: rgba(0, 0, 0, .4); border-radius: 4px; }
/* Сворачиваемый заголовок (BoxHeader, автор 09.10.2026): щелчок прячет содержимое панели. Стрелка —
   у названия, свёрнутая панель — одна строка заголовка без черты под ним. */
.box-header.collapsible { cursor: pointer; user-select: none; }
.box-header.collapsible > :first-child::before { content: "▾"; display: inline-block; width: 1em; color: #5d5750; }
.box-header.collapsible.collapsed > :first-child::before { content: "▸"; }
.box-header.collapsible:hover > :first-child::before { color: var(--gold); }
.box-header.collapsed { margin-bottom: 0; border-bottom-color: transparent; box-shadow: none; }
.box-header.alarm { background: rgba(160, 58, 50, .25); box-shadow: inset 0 0 0 1px rgba(160, 58, 50, .6); border-radius: 4px; }
.box-header.alarm > * { color: var(--danger-text); }
.empty-note { padding: 6px; text-align: center; }

/* ---------- Полоски ---------- */
.bar {
    position: relative;
    margin: 2px;
    padding: 2px;
    background: #0e0e0e;
    border: 1px solid #323232;
    border-radius: 4px;
}
.bar > span {
    display: block;
    height: 3px;
    border-radius: 2px;
    transition: width 150ms;
}
.bar > em {
    position: absolute;
    inset: 0;
    font: normal 10px/14px var(--font-body);
    text-align: center;
    color: #ddd;
    text-shadow: 0 0 2px #000, 0 0 2px #000;
}
.bar:has(em) > span { height: 10px; }
/* Срезанный ранами потолок (сознание): серая ретушь справа, той же маской, что заполненность
   желудка. Ширина — от ширины полосы без отступов, чтобы сходилось с заполненной частью. */
.bar > i.cut {
    position: absolute;
    top: 2px;
    bottom: 2px;
    right: 2px;
    background: rgba(170, 170, 170, .45);
    -webkit-mask: var(--art-fullness-mask) repeat;
    mask: var(--art-fullness-mask) repeat;
    transition: width 150ms;
    pointer-events: none;
}
/* Две полосы одного тела: сознание сбивают первым, поэтому оно светлее и стоит сверху. */
.bar-mind > span { background-image: linear-gradient(90deg, #6b0d0d 0%, #ef3b3b 50%, #6b0d0d 100%); }
/* Жизнь ярче, чем была (#9c0f0f): полная выглядела потухшей, будто на исходе (автор, 09.10.2026). */
.bar-health > span { background-image: linear-gradient(90deg, #4a0404 0%, #d41a1a 50%, #4a0404 100%); }

/* Сознание и жизнь стоят вплотную и выглядят одной рамкой, а не двумя полосками. */
.vitals .bar { margin-top: 0; margin-bottom: 0; }
.vitals .bar:not(:last-child) { border-bottom: 0; border-bottom-left-radius: 0; border-bottom-right-radius: 0; }
.vitals .bar:not(:first-child) { border-top-left-radius: 0; border-top-right-radius: 0; }
.vitals { margin: 2px 0; }
.bar-stamina > span {
    height: 4px;
    background-image:
        repeating-linear-gradient(90deg, transparent 0, transparent 5px, #000 5px, #000 7px),
        linear-gradient(90deg, #713d01 0%, #d69143 50%, #713d01 100%);
}
.bar-stamina > .stamina-reserve {
    position: absolute;
    inset: 2px 2px 2px auto;
    pointer-events: none;
}
.stamina-reserve > span {
    display: block;
    height: 100%;
    background: #e9a63e;
    border-radius: 2px;
    -webkit-mask: var(--art-fullness-mask) repeat;
    mask: var(--art-fullness-mask) repeat;
    transition: width 150ms;
}
.bar-mana > span {
    height: 4px;
    background-image:
        repeating-linear-gradient(90deg, transparent 0, transparent 5px, #000 5px, #000 7px),
        linear-gradient(90deg, #2a0a6e 0%, #7b4dff 50%, #2a0a6e 100%);
}
/* Мана фиолетовая, сытость оливковая: прежние синяя и золотая сливались с водой и выносливостью. */
.bar-hunger > span { height: 3px; background-image: linear-gradient(90deg, #3f4a14 0%, #a3b443 50%, #3f4a14 100%); }
.bar-thirst > span { height: 3px; background-image: linear-gradient(90deg, #15455c 0%, #4fb3d9 50%, #15455c 100%); }
.bar.warn { border-color: #8a2a24; background: #160b0a; }
.bar-digest > span { background-image: linear-gradient(90deg, #3a5a1e 0%, #8fbf4a 50%, #3a5a1e 100%); }
.bar-organ > span { height: 2px; background-image: linear-gradient(90deg, #660000 0%, #ec1616 50%, #660000 100%); }

/* ---------- Персонаж и карточки ---------- */
.character-head { display: flex; gap: 4px; }
.entity-info { flex: 1; min-width: 0; padding: 0 2px; display: flex; flex-direction: column; justify-content: center; overflow: hidden; }

.entity-icon {
    flex-shrink: 0;
    width: 50px;
    height: 50px;
    margin: 2px;
    border-radius: 5px;
    /* Чуть светлее того, что под ней: так подложка не проваливается, на каком бы фоне ни лежала. */
    background: rgba(255, 255, 255, .05);
    border: 1px solid rgba(255, 255, 255, .09);
    display: flex;
    align-items: center;
    justify-content: center;
    overflow: hidden;
    color: var(--gold);
    font-size: 26px;
}
.entity-icon .horse-portrait { width: 100%; }
/* У строения картинка лежит на тёмной подложке. var(--art, none) обязателен: без запасного
   значения строка ломается там, где значка ещё нет, и подложка пропадает вместе с ним. */
.entity-icon.building {
    background-image: var(--art, none), radial-gradient(circle at 50% 40%, #2b2418, #0b0a08);
    background-size: 82% auto, cover;
    background-position: center, center;
    background-repeat: no-repeat, no-repeat;
}

.entity-card {
    display: flex;
    gap: 4px;
    width: calc(100% - 6px);
    margin: 3px;
    padding: 2px;
    background: #161616;
    border: 1px solid #2e2e2e;
    border-radius: 6px 0 0 6px;
    box-shadow: 0 5px 6px -3px #000;
    color: inherit;
    font: inherit;
    text-align: left;
    cursor: pointer;
    /* Все карточки ровно в высоту иконки: список выходит ровным, а не лесенкой. */
    height: 60px;
    align-items: center;
    overflow: hidden;
}
.entity-card { position: relative; }
.entity-card .text-0 { line-height: 16px; }
.entity-card .text-1 { line-height: 14px; }
.entity-card .text-2 { line-height: 14px; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; white-space: normal; }
.entity-card .bar { margin: 1px 2px; padding: 1px; }
/* Имя и уровень одной строкой: длинное имя обрезается многоточием, уровень справа цел. */
.entity-card .card-name, .character-box .card-name { display: flex; align-items: baseline; gap: 6px; overflow: visible; }
.card-name > span:first-child { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.card-level { flex: none; padding-right: 2px; font: 600 11px var(--font-body); color: #c9a468; font-variant-numeric: tabular-nums; }
/* Счёт одинаковой живности в брюхе (docs/FAUNA.md): «× 20» справа от имени, как уровень. */
.card-count { flex: none; padding-right: 2px; font: 600 12px var(--font-body); color: var(--text); font-variant-numeric: tabular-nums; }
.card-line { display: flex; align-items: center; gap: 6px; min-width: 0; }
.card-line .text-1 { flex-shrink: 0; }
/* Занят не тобой (12.10.2026): жёлтая метка с именем того, с кем он дерётся; в замахе на него — ярче.
   Красный фон карточки — только тому, кто идёт на тебя. */
.card-foe { flex: none; margin-left: auto; min-width: 0; max-width: 55%; display: inline-flex; align-items: center; gap: 3px;
    padding-right: 2px; font: 600 11px var(--font-body); color: #b8973f; opacity: .8; transition: color .2s, opacity .2s; }
.card-foe-icon { flex: none; width: 12px; height: 12px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.card-foe-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.card-foe:hover { opacity: 1; color: #ecc24f; }
.card-ribbon { flex: none; display: inline-flex; align-self: center; color: #e0344a; }
.card-ribbon svg { width: 12px; height: 12px; fill: none; stroke: currentColor; stroke-width: 2.2; stroke-linecap: round; stroke-linejoin: round; }
/* Метка «!» на коне (docs/PVP.md): бледная — нападает на игроков, красная — убийца; свой противник — красный со
   свечением. Свечение неподвижное: его видно и при выключенной анимации боя. */
.card-karma { flex: none; align-self: center; font: 800 13px/12px var(--font-body); padding: 0 1px; }
.card-karma.pale { color: #e6a59c; }
.card-karma.red, .card-karma.enemy { color: #ff4a3d; text-shadow: 0 0 4px rgba(255, 60, 40, .85), 0 0 1px #000; }
.card-karma.blue { color: #6ec6ff; font-size: 11px; text-shadow: 0 0 4px rgba(110, 198, 255, .7); } /* карма от 50 (docs/PVP.md) */
.card-wanted { flex: none; align-self: center; padding: 0 4px; border: 1px solid #8a2a24; border-radius: 3px; font: 700 10px/14px var(--font-body); color: #ff6a5c; text-transform: lowercase; } /* разыскивается (docs/PVP.md) */
.card-injured { flex: none; align-self: center; padding: 0 4px; border: 1px solid #6a4a3a; border-radius: 3px; font: 700 10px/14px var(--font-body); color: #d29a7a; text-transform: lowercase; } /* ранен после ликвидации (docs/PVP.md) */
/* Мышь на метке — тот, с кем он дерётся, краснеет в клетке (Session.Hinted), чтобы найти его глазами в толпе. */
.entity-card.hinted { background: linear-gradient(90deg, rgba(200, 34, 34, .62), rgba(120, 20, 20, .36)); border-color: #e04040; box-shadow: 0 0 10px rgba(224, 64, 64, .45); }
.card-foe.winding { color: #ecc24f; opacity: 1; text-shadow: 0 0 6px rgba(236, 194, 79, .55); }
.entity-card:hover { background: #191919; border-color: #4b4b4b; }
.entity-card.selected { border-color: var(--gold); }
.entity-card.self .text-0 { color: #c9b48a; }
/* Босс: имя золотом — это не «олень», а тот самый олень. */
.entity-card.boss .text-0 { color: var(--gold); }
/* Вышел из игры, но ещё стоит на гексе: съесть его всё ещё можно. */
.entity-card.offline { opacity: .65; }

/* ---------- Инвентарь и экипировка ---------- */
.inventory-box .box-content { min-height: 30px; }
.bar-durability > span { background-image: linear-gradient(90deg, #6f6f6f 0%, #f2f2f2 50%, #6f6f6f 100%); }
.bar-durability > em { color: #111; text-shadow: 0 0 2px #fff; }
/* Склянка облика: было → станет, два портрета рядом. */
.elixir-preview { display: flex; align-items: center; gap: 12px; margin: 8px 0; }
.elixir-preview figure { flex: 1; margin: 0; display: flex; flex-direction: column; align-items: center; gap: 4px; }
.elixir-choices { display: flex; flex-direction: column; gap: 6px; margin: 6px 0; }
.elixir-choice { display: flex; gap: 8px; align-items: center; padding: 6px 8px; border: 1px solid #3a3632; border-radius: 6px; cursor: pointer; }
.elixir-choice.picked { border-color: var(--gold); }
.bar-water > span { background-image: linear-gradient(90deg, #15455c 0%, #4fb3d9 50%, #15455c 100%); }
/* Бульон — янтарный: его не спутать с водой. */
.bar-broth > span { background-image: linear-gradient(90deg, #5c3a12 0%, #e0a13a 50%, #5c3a12 100%); }
/* Крепость бульона — густотой цвета: жидкий бледный, крепкий тёмный. */
.bar-broth-thin > span { background-image: linear-gradient(90deg, #6e6040 0%, #e8d7a4 50%, #6e6040 100%); }
.bar-broth-strong > span { background-image: linear-gradient(90deg, #3d1c05 0%, #c9701c 50%, #3d1c05 100%); }
/* Отвар дуба — бурый, как дублёная кожа; слабый — бледнее. */
.bar-brew > span { background-image: linear-gradient(90deg, #2e1a0c 0%, #8a5a2c 50%, #2e1a0c 100%); }
.bar-brew-thin > span { background-image: linear-gradient(90deg, #4a3a2a 0%, #b08a62 50%, #4a3a2a 100%); }
/* Бурда — мутная, серо-бурая с хлопьями. */
.bar-swill > span { background-image: linear-gradient(90deg, #3a3530 0%, #7d7260 50%, #3a3530 100%); }
/* Сломанный инструмент: полоса того же размера, во всю длину тёмно-красная; «сломан» — рядом с весом. */
.bar-broken { border-color: #5a1712; }
.bar-broken > span { width: 100%; background: linear-gradient(90deg, #4a100c, #7a1a14 50%, #4a100c); }
.broken-word { color: #c4574a; }
/* Стойка и чан дубильни: сколько ещё сохнуть или мокнуть; готово — тёплое золото. */
.bar-tanning > span { background: linear-gradient(90deg, #5a4630, #a8875a); }
.bar-tanning.done > span { background: linear-gradient(90deg, #8a6420, #e0a53a); box-shadow: 0 0 6px #c8862a; }
.tanning-word { color: #c9a468; }
/* Ячейки стойки и чана — сеткой, пустые видны: пунктир и подсказка, той же высоты, что карточка. */
.tannery-slots { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0 4px; align-content: start; }
.tannery-slot { height: 66px; }
.tannery-slot:not(.empty) { display: flex; align-items: center; }
.tannery-slot > .item-card { flex: 1; min-width: 0; }
.tannery-slot > .btn { flex: none; min-height: 44px; margin-inline-end: 3px; }
.tannery-slot.empty { display: flex; align-items: center; justify-content: center; height: 60px; margin: 3px; border: 1px dashed #3a3530; border-radius: 6px; text-align: center; padding: 0 8px; }
.drop-target .tannery-slot.empty { border-color: var(--gold); }
.tannery-box > .box-header { flex-wrap: wrap; gap: 4px 8px; }
.tannery-load { display: flex; flex-wrap: wrap; align-items: center; justify-content: flex-end; gap: 4px 8px; margin-inline-start: auto; }
.tannery-load-btn { height: 24px; padding: 0 10px; font-size: 12px; }
@media (max-width: 600px) { .tannery-slots { grid-template-columns: minmax(0, 1fr); } }
/* Ветки на дереве — тёплым деревом, чтобы не спутать с серым запасом древесины над ними. */
.bar-branches > span { background-image: linear-gradient(90deg, #4a3016 0%, #b98a4e 50%, #4a3016 100%); }
/* Значок предмета — картинка на общей подложке; символ остаётся запасным вариантом. */
.entity-icon.item, .entity-icon.resource, .entity-icon.lair { font-size: 22px; background-image: var(--art); background-size: 78%; background-position: center; background-repeat: no-repeat; }
/* Дерево и жила — не вещи в суме, а то, что стоит вокруг: подложка потемнее, как у строений. */
.entity-icon.resource { background-color: rgba(40, 52, 34, .5); }
.entity-icon.lair { background-color: rgba(58, 30, 24, .55); }
/* Вещь клада: что это — не видно, только золотой «!». */
.entity-icon.hoard { background-color: rgba(58, 44, 20, .55); color: var(--gold); font: 800 26px var(--font-title); }

/* Зверь, который нападает на тебя: карточку видно сразу, даже в длинном списке. */
.entity-card.hostile { background: linear-gradient(90deg, rgba(150, 28, 28, .45), rgba(90, 18, 18, .25)); border-color: #8a2a24; }
.entity-card.hostile:hover { background: linear-gradient(90deg, rgba(170, 34, 34, .55), rgba(100, 22, 22, .32)); }
.entity-card.hostile.selected { border-color: var(--gold); }
/* Замахивается на тебя (StrikeRules): краснеет сильнее схватки и пульсирует — знак неподвижный,
   поэтому виден и при выключенной анимации боя (BLOCK.md, решение автора). */
/* Замах на тебя — свечение вокруг карточки (автор, 12.10.2026: заливка была как у злости, разницы не видно):
   фон остаётся «злым», а вокруг разгорается красный ореол и рамка. */
.entity-card.winding { background: linear-gradient(90deg, rgba(150, 28, 28, .45), rgba(90, 18, 18, .25)); border-color: #e04040;
    box-shadow: 0 5px 6px -3px #000, 0 0 12px 2px rgba(224, 64, 64, .7); animation: wind-card .6s ease-in-out infinite alternate; }
@keyframes wind-card { to { box-shadow: 0 5px 6px -3px #000, 0 0 20px 5px rgba(255, 80, 60, .95); border-color: #ff8a7a; } }
/* Сильный замах зверя (StrikeRules.HeavyWindup): мерцает чаще и ярче — пора блокировать. */
/* Сильный замах — тот же ореол и небольшое мерцание самой карточки (яркость), а не дёрганье заливки. */
.entity-card.winding.heavy { border-color: #ff6a5a; box-shadow: 0 5px 6px -3px #000, 0 0 18px 4px rgba(255, 80, 60, .9);
    animation: wind-card-heavy .18s ease-in-out infinite alternate; }
@keyframes wind-card-heavy { from { filter: brightness(1); } to { filter: brightness(1.22); border-color: #ffb0a0; } }
@media (prefers-reduced-motion: reduce) { .entity-card.winding, .entity-card.winding.heavy { animation: none; } }

/* Текст карточек не выделяется: двойной клик («забрать», «снять») выделял слово, и следующее
   перетаскивание браузер начинал как перенос текста, а не карточки, — бросок срывался. */
.item-card { cursor: grab; opacity: var(--item-card-opacity, 1); -webkit-user-select: none; user-select: none; }
.counter-line, .tannery-slot { -webkit-user-select: none; user-select: none; }
.item-card:active { cursor: grabbing; }
.drop-target { outline: 2px solid var(--gold); outline-offset: -3px; background: rgba(201, 164, 104, .08); }
.item-drag-preview, .item-drag-transparent { position: fixed; left: 0; top: 0; pointer-events: none; }
.item-drag-preview { z-index: 1000; opacity: .8; }
.item-drag-preview > canvas { display: block; }
.entity-card.drop-target { background: rgba(201, 164, 104, .15); }
.ground-panel .box-content, .inventory-box .box-content { min-height: 34px; }
/* «Под ногами» растягивается на всё, что осталось в правой колонке под списками: бросать можно в
   любое пустое место ниже. Других мест для броска в колонке нет — прежде такой бросок просто
   пропадал. Списки (персонажи, строения) не трогаются; где они длинные, блок — обычного размера. */
.right-scroll > .cell-content { flex: 1 0 auto; display: flex; flex-direction: column; }
/* Растёт сам блок, а не его содержимое: вещи и кнопки «Подобрать» остаются наверху, рядом
   друг с другом, — пустое место под ними тоже принимает бросок. */
.ground-panel { flex: 1 0 auto; }
/* Под землёй кнопки в ряд: подобрать выбранное, подобрать всё. */
.item-actions { display: flex; gap: 4px; margin: 4px 3px 0; }
.item-actions .btn { flex: 1; height: 26px; font-size: 12px; padding: 0 6px; }
/* Чужая добыча лежит на виду, но приглушена: её время ещё не пришло. */
.item-card.claimed { --item-card-opacity: .55; }
/* В торговле: этого торговец не берёт. Тускло и серо, но выбрать и переложить можно. */
.item-card.untradable { --item-card-opacity: .4; filter: grayscale(1); }
/* Уже на прилавке: тоже приглушено, но светлее того, что торговец не берёт. */
.item-card.on-counter { --item-card-opacity: .65; filter: grayscale(.6); }
.item-card .counter-note { color: var(--gold); }
/* Избранное: звёздочка видна всегда у помеченного, у прочих — при наведении. */
.favorite-toggle { flex-shrink: 0; align-self: flex-start; padding: 0 4px; background: none; border: 0; cursor: pointer; font-size: 16px; line-height: 1.2; color: #6b645c; opacity: 0; }
.item-card:hover .favorite-toggle, .favorite-toggle:focus-visible, .favorite-toggle.on { opacity: 1; }
.favorite-toggle.on { color: var(--gold); }
/* Правый край карточки: звёздочка сверху, молоточек — в нижнем углу под ней. */
.card-side { flex-shrink: 0; align-self: stretch; display: flex; flex-direction: column; justify-content: space-between; align-items: center; }
/* Починить: у сломанного виден всегда, у поношенного — при наведении, как звёздочка: иначе молоточки на каждой карточке. */
.repair-toggle { padding: 0 4px; background: none; border: 0; cursor: pointer; font-size: 15px; line-height: 1.25; color: #b58a2e; opacity: 0; }
.item-card:hover .repair-toggle, .repair-toggle:focus-visible, .repair-toggle.urgent { opacity: 1; }
.repair-toggle:hover { color: var(--heading); }
.favorite-toggle:hover { color: var(--heading); }
.quest-buttons { display: flex; flex-direction: column; gap: 4px; }
.claim-note { color: #d9b36a; }

/* ---------- Контейнеры тела ---------- */
.container-box { border-radius: 0 0 0 6px; overflow: hidden; }
/* Подложка заполненности занимает ровно заголовок: он поднят к верхнему краю блока и той же
   высоты, что подложка (плюс черта снизу), — название и масса стоят посередине неё. */
.container-box .box-header { margin-top: -4px; height: 25px; padding: 0 4px; align-items: center; }
.container-box .fullness {
    position: absolute;
    top: 0;
    left: 0;
    height: 24px;
    background: rgba(64, 184, 30, .3);
    -webkit-mask: var(--art-fullness-mask) repeat;
    mask: var(--art-fullness-mask) repeat;
    transition: width 150ms;
    pointer-events: none;
}

/* ---------- Обзор ---------- */
.view-panel { flex: 1; min-height: 300px; display: flex; flex-direction: column; }
.view-tabs { display: flex; gap: 2px; border-bottom: 1px solid #2e2e2e; margin-bottom: 6px; }
.view-tabs button {
    background: transparent;
    border: 1px solid transparent;
    border-bottom: 0;
    border-radius: 4px 4px 0 0;
    color: var(--text-muted);
    font: 500 13px var(--font-body);
    padding: 5px 14px;
    cursor: pointer;
}
.view-tabs button:hover:not(:disabled) { color: var(--heading); }
.view-tabs button.active { color: var(--heading); border-color: #2e2e2e; background: rgba(255, 255, 255, .03); }
.view-tabs button:disabled { opacity: .4; cursor: default; }
.view-body { flex: 1; min-height: 0; display: flex; align-items: center; justify-content: center; overflow: auto; }
.view-body:has(.self-view) { align-items: stretch; overflow: hidden; }

.versus, .self-view { display: flex; align-items: center; justify-content: center; gap: 24px; width: 100%; }
.self-view { display: grid; grid-template-columns: minmax(0, 1fr); align-items: stretch; gap: 12px; height: 100%; }
/* «Обзор»: две равные половины — конь по центру левой, цифры по центру правой, как кукла
   в «Экипировке» стоит по центру своей сцены. Узко — половины встают одна под другой.
   Ровно две колонки, а не repeat(auto-fit, …): тот на широкой панели заводит пустые колонки
   нулевой ширины, и их зазоры в DevTools выглядят так, будто сетка съехала вправо. */
.self-main { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); align-items: center; justify-items: center; gap: 12px 24px; min-width: 0; overflow-y: auto; container-type: size; }
.self-view { container-type: inline-size; }
/* Портрет не крупнее 600 px, как в «Экипировке» и «Навыках». Высота .self-main ограничивает
   рисунок в пропорциях 660 / 510 и оставляет место для имени, роста и отступов. */
.self-main .figure { width: 100%; max-width: min(600px, (100cqh - 120px) * 660 / 510); min-width: 0; display: flex; flex-direction: column; justify-content: flex-end; }
.self-main .figure .horse-portrait, .self-main .figure .creature-portrait { flex: 1 1 auto; min-height: 140px; width: 100%; height: auto; }
/* Кнопки «Статы / Награды / Летопись» прибиты к верху: страницы разной высоты, и по центру кнопки прыгали.
   Длинная страница прокручивается под ними, а не двигает их. */
.self-main .self-side { align-self: stretch; justify-self: stretch; display: flex; flex-direction: column; align-items: center; gap: 12px; min-width: 0; min-height: 0;
    max-width: 520px; width: 100%; margin: 0 auto; padding-top: 16px; }
.self-main .self-side > nav { flex: none; }
.self-main .self-side > :not(nav) { flex: 0 1 auto; min-height: 0; overflow-y: auto; width: 100%; }
.self-main .self-side .medal-grid { width: 100%; }
/* Узко конь не берёт всю высоту (как в «Осмотреть»): иначе страница под кнопками сжималась в ноль.
   Ему — ряд в 300 px, страница — своей высоты, прокручивается весь «Обзор». */
@container (max-width: 640px) {
    .self-main { grid-template-columns: minmax(0, 1fr); grid-template-rows: 300px auto; align-items: end; }
    .self-main .figure { max-width: min(600px, 180px * 660 / 510); height: 100%; }
    .self-main .self-side { min-height: auto; align-self: start; padding-bottom: 12px; }
    .self-main .self-side > :not(nav) { flex: none; overflow-y: visible; }
}
.figure { width: 260px; text-align: center; }
.figure .horse-portrait, .figure .creature-portrait { width: auto; height: 190px; }
.figure-name { font-family: var(--font-title); font-size: 20px; color: var(--heading); }
.figure .entity-icon { width: 160px; height: 160px; margin: 0 auto 8px; font-size: 80px; }
/* «Цель»: фигуры в общем масштабе, стоят на одной земле внизу */
.view-body:has(.versus-stage) { align-items: stretch; }
/* Задник сцены (автор, 05.10.2026 — по картинке, где конь стоит в тёплой студийной тьме): бурая
   тьма и лёгкое зерно холста. Источников света два — по одному за каждым из двоих, а не один
   посередине (автор): светлое пятно за спиной и тёплый отсвет пола под ногами. Кончается
   чуть ниже «земли» и тает: подписи с полосами остаются на тёмном, как были. */
.versus-stage { position: relative; overflow: hidden; } /* свет шире фигуры — за край сцены не вылезает и прокрутку не заводит */
.versus-stage::before { content: ""; position: absolute; inset: 0 0 86px; z-index: 0; border-radius: 6px; pointer-events: none;
    background:
        url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.9' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0 0 0 0 .75  0 0 0 0 .5  0 0 0 0 .28  0 0 0 .09 0'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23n)'/%3E%3C/svg%3E"),
        radial-gradient(ellipse 32% 17% at 24% calc(100% - 38px), rgba(222, 138, 60, .34), rgba(160, 88, 34, .13) 55%, transparent 78%),
        radial-gradient(ellipse 32% 17% at 76% calc(100% - 38px), rgba(222, 138, 60, .34), rgba(160, 88, 34, .13) 55%, transparent 78%),
        radial-gradient(ellipse 40% 74% at 24% 46%, #432a15 0%, rgba(46, 28, 14, .7) 45%, transparent 80%),
        radial-gradient(ellipse 40% 74% at 76% 46%, #432a15 0%, rgba(46, 28, 14, .7) 45%, transparent 80%),
        #150c07;
    -webkit-mask-image: linear-gradient(to bottom, #000 calc(100% - 26px), transparent);
    mask-image: linear-gradient(to bottom, #000 calc(100% - 26px), transparent); }
.versus-stage > * { position: relative; z-index: 1; }
.versus-stage > .phaser-ui-layer { position: absolute; }
.versus-stage > .phaser-battle-fx { z-index: 3; }
.stage-glow[data-phaser-motion] > .stage-scale {
    transform: var(--impact-transform) !important;
    filter: var(--impact-filter) !important;
    transition-property: height;
}
.dmg-float[data-phaser-float], .versus-stage [data-phaser-particles-painted] { visibility: hidden; }
.versus-stage { flex: 1; display: grid; grid-template-columns: minmax(0, 1fr) 170px minmax(0, 1fr); grid-template-rows: minmax(0, 1fr); align-items: stretch; gap: 12px; padding: 8px 12px 10px; min-height: 0; }
.stage-actor { display: flex; flex-direction: column; align-items: center; justify-content: flex-end; height: 100%; min-height: 0; }
.stage-art { flex: 1; min-height: 120px; width: 100%; display: flex; align-items: flex-end; justify-content: center; position: relative;
    --art-recoil: clamp(-9px, -2%, -5px); }
/* Тень под ногами — по ширине самой фигуры и вокруг копыт, а не полосой ниже них: с тенью под
   всей колонкой и ниже ног конь «парил» над полом (автор, 05.10.2026). Задние и передние ноги стоят
   на разной высоте рисунка, поэтому тень высокая и заходит под них снизу вверх. */
.stage-scale { position: relative; isolation: isolate; --hit-recoil: clamp(-18px, -8%, -5px); --jolt-recoil: clamp(-10px, -5%, -3px); }
.stage-scale::after { content: ""; position: absolute; left: -4%; right: -4%; bottom: -6px; height: clamp(16px, 7%, 44px); z-index: -1; pointer-events: none;
    background: radial-gradient(ellipse 50% 50% at 50% 50%, rgba(0, 0, 0, .72), rgba(0, 0, 0, .38) 48%, transparent 74%); }
.stage-scale { display: flex; align-items: flex-end; justify-content: center; max-width: 100%; transition: height 300ms ease; }
.stage-scale .horse-portrait, .stage-scale .creature-portrait { height: 100%; width: auto; max-width: 100%; display: block; }
/* Правый боец смотрит на левого. Отражается только рисунок — подпись и полосы нет. */
.stage-actor.target .stage-art { transform: scaleX(-1); --art-recoil: clamp(5px, 2%, 9px); }

/* Удар: замах назад со золотым шлейфом (до ~52 %), короткая пауза, рывок на врага (удар на 70 %, это
   0,7 с), возврат. Цифры вылетают ровно в момент удара. Ролики лежат парами -a/-b: код чередует имя,
   и подряд идущие удары перезапускают анимацию, не пересоздавая рисунок. Рисунок правого бойца
   отражён, но в его собственной системе «+X» тоже смотрит на врага — те же ключи годятся обоим. */
.stage-scale.lunge-a { animation: lunge-a 1s linear -.55s; }
.stage-scale.lunge-b { animation: lunge-b 1s linear -.55s; }
.stage-scale.hurt-a { animation: hurt-a 1s linear -.53s; }
.stage-scale.hurt-b { animation: hurt-b 1s linear -.53s; }
.stage-scale.dodge-a { animation: dodge-a 1s linear -.47s; }
.stage-scale.dodge-b { animation: dodge-b 1s linear -.47s; }
@keyframes lunge-a { 0% { transform: translateX(0); filter: var(--trail-none); animation-timing-function: ease-out; }
    8% { filter: var(--trail-back); }
    52% { transform: translateX(-14%); filter: var(--trail-back); animation-timing-function: linear; }
    60% { transform: translateX(-14%); filter: var(--trail-none); animation-timing-function: cubic-bezier(.9, 0, 1, .6); }
    70% { transform: translateX(24%); filter: var(--trail-fwd); animation-timing-function: linear; }
    74% { transform: translateX(24%); filter: var(--trail-none); animation-timing-function: ease-in-out; }
    100% { transform: translateX(0); filter: var(--trail-none); } }
@keyframes lunge-b { 0% { transform: translateX(0); filter: var(--trail-none); animation-timing-function: ease-out; }
    8% { filter: var(--trail-back); }
    52% { transform: translateX(-14%); filter: var(--trail-back); animation-timing-function: linear; }
    60% { transform: translateX(-14%); filter: var(--trail-none); animation-timing-function: cubic-bezier(.9, 0, 1, .6); }
    70% { transform: translateX(24%); filter: var(--trail-fwd); animation-timing-function: linear; }
    74% { transform: translateX(24%); filter: var(--trail-none); animation-timing-function: ease-in-out; }
    100% { transform: translateX(0); filter: var(--trail-none); } }
/* Стойка и оглушение (BlockRules) — ореол по силуэту (drop-shadow) на обёртке рисунка (.stage-glow):
   у обёртки свой фильтр, и ролики ударов на самом рисунке его не перекрывают, так что ореол виден с
   нажатия «Блокировать» до конца стойки (автор, 11.10.2026: не круг за спиной, а белый ореол вокруг
   коня). Встал в стойку — вспышка, потом ровное дыхание; оглушён — серый. */
.stage-glow { display: flex; align-items: flex-end; justify-content: center; height: 100%; max-width: 100%; transition: filter .35s; }
.stage-glow.guard {
    filter: drop-shadow(0 0 6px rgba(255, 255, 255, 1)) drop-shadow(0 0 18px rgba(255, 255, 255, .75));
    animation: glow-on .45s ease-out, glow-breathe .7s ease-in-out .45s infinite alternate;
}
@keyframes glow-on { 0% { filter: drop-shadow(0 0 0 rgba(255, 255, 255, 0)); } 30% { filter: drop-shadow(0 0 14px rgba(255, 255, 255, 1)) drop-shadow(0 0 40px rgba(255, 255, 255, 1)); } 100% { filter: drop-shadow(0 0 6px rgba(255, 255, 255, 1)) drop-shadow(0 0 18px rgba(255, 255, 255, .75)); } }
@keyframes glow-breathe { from { filter: drop-shadow(0 0 5px rgba(255, 255, 255, .9)) drop-shadow(0 0 14px rgba(255, 255, 255, .55)); } to { filter: drop-shadow(0 0 8px rgba(255, 255, 255, 1)) drop-shadow(0 0 26px rgba(255, 255, 255, .9)); } }
.stage-glow.stunned { filter: grayscale(.7) brightness(.8) drop-shadow(0 0 10px rgba(140, 140, 150, .8)); }
/* Без чувств — тёмный ореол, медленно дышит (автор, 11.10.2026: не тёмная заливка). Сам рисунок не
   трогаем. Появляется с задержкой — после вспышки удара в ролике faint. */
.stage-glow.down {
    filter: drop-shadow(0 0 7px rgba(10, 2, 16, 1)) drop-shadow(0 0 22px rgba(10, 2, 16, .9));
    transition: filter .6s ease .5s;
    animation: down-breathe 2.4s ease-in-out .5s infinite alternate;
}
@keyframes down-breathe { from { filter: drop-shadow(0 0 6px rgba(10, 2, 16, 1)) drop-shadow(0 0 18px rgba(10, 2, 16, .8)); }
    to { filter: drop-shadow(0 0 9px rgba(10, 2, 16, 1)) drop-shadow(0 0 30px rgba(10, 2, 16, 1)); } }
@media (prefers-reduced-motion: reduce) { .stage-glow.guard, .stage-glow.down { animation: none; } }
/* Замах (StrikeRules.Phases): боец всё время замаха (--wind-ms, по серверу: укус 0,6 с, копыто 1 с, бросок телом
   1,2 с) отходит назад, а вокруг него разгорается красноватое свечение (автор, 11.10.2026: заливка читалась как
   «игра зависла»). Дальше проведение (swing) и удар — см. ниже. Удар без замаха (lunge) остался как был. */
.stage-scale.wind-a { animation: wind-a var(--wind-ms, 1000ms) linear forwards; animation-delay: var(--wind-offset, 0ms); }
.stage-scale.wind-b { animation: wind-b var(--wind-ms, 1000ms) linear forwards; animation-delay: var(--wind-offset, 0ms); }
@keyframes wind-a { 0% { transform: translateX(0); filter: var(--trail-none) drop-shadow(0 0 0 rgba(255, 70, 50, 0)); }
    30% { transform: translateX(-6%); filter: var(--trail-back) drop-shadow(0 0 10px rgba(255, 70, 50, .7)); }
    100% { transform: translateX(-16%); filter: var(--trail-back) drop-shadow(0 0 20px rgba(255, 70, 50, 1)); } }
@keyframes wind-b { 0% { transform: translateX(0); filter: var(--trail-none) drop-shadow(0 0 0 rgba(255, 70, 50, 0)); }
    30% { transform: translateX(-6%); filter: var(--trail-back) drop-shadow(0 0 10px rgba(255, 70, 50, .7)); }
    100% { transform: translateX(-16%); filter: var(--trail-back) drop-shadow(0 0 20px rgba(255, 70, 50, 1)); } }
/* Сильный удар зверя (StrikeRules.HeavyWindup, 1,5 с; автор, 11.10.2026): отходит дальше обычного и всё
   время дрожит, красный ореол мерцает — его надо узнать и встретить блоком. Удар после (strike-heavy) —
   рывок дальше обычного. Попадание на тех же 70 %. */
.stage-scale.wind-heavy-a { animation: wind-heavy-a var(--wind-ms, 1000ms) linear forwards; animation-delay: var(--wind-offset, 0ms); }
.stage-scale.wind-heavy-b { animation: wind-heavy-b var(--wind-ms, 1000ms) linear forwards; animation-delay: var(--wind-offset, 0ms); }
@keyframes wind-heavy-a { 0% { transform: translateX(0); filter: var(--trail-none) drop-shadow(0 0 0 rgba(255, 60, 40, 0)); }
    18% { transform: translateX(-8%); filter: var(--trail-back) drop-shadow(0 0 14px rgba(255, 60, 40, .9)); }
    26% { transform: translateX(-9%) translateY(1%); filter: var(--trail-back) drop-shadow(0 0 6px rgba(255, 60, 40, .5)); }
    34% { transform: translateX(-11%) translateY(-1%); filter: var(--trail-back) drop-shadow(0 0 20px rgba(255, 60, 40, 1)); }
    42% { transform: translateX(-12%) translateY(1%); filter: var(--trail-back) drop-shadow(0 0 8px rgba(255, 60, 40, .55)); }
    50% { transform: translateX(-14%) translateY(-1%); filter: var(--trail-back) drop-shadow(0 0 24px rgba(255, 60, 40, 1)); }
    58% { transform: translateX(-15%) translateY(1%); filter: var(--trail-back) drop-shadow(0 0 8px rgba(255, 60, 40, .55)); }
    66% { transform: translateX(-17%) translateY(-1%); filter: var(--trail-back) drop-shadow(0 0 28px rgba(255, 60, 40, 1)); }
    74% { transform: translateX(-18%) translateY(1%); filter: var(--trail-back) drop-shadow(0 0 10px rgba(255, 60, 40, .6)); }
    82% { transform: translateX(-20%) translateY(-1%); filter: var(--trail-back) drop-shadow(0 0 32px rgba(255, 60, 40, 1)); }
    90% { transform: translateX(-21%) translateY(1%); filter: var(--trail-back) drop-shadow(0 0 12px rgba(255, 60, 40, .65)); }
    100% { transform: translateX(-22%); filter: var(--trail-back) drop-shadow(0 0 34px rgba(255, 60, 40, 1)); } }
@keyframes wind-heavy-b { 0% { transform: translateX(0); filter: var(--trail-none) drop-shadow(0 0 0 rgba(255, 60, 40, 0)); }
    18% { transform: translateX(-8%); filter: var(--trail-back) drop-shadow(0 0 14px rgba(255, 60, 40, .9)); }
    26% { transform: translateX(-9%) translateY(1%); filter: var(--trail-back) drop-shadow(0 0 6px rgba(255, 60, 40, .5)); }
    34% { transform: translateX(-11%) translateY(-1%); filter: var(--trail-back) drop-shadow(0 0 20px rgba(255, 60, 40, 1)); }
    42% { transform: translateX(-12%) translateY(1%); filter: var(--trail-back) drop-shadow(0 0 8px rgba(255, 60, 40, .55)); }
    50% { transform: translateX(-14%) translateY(-1%); filter: var(--trail-back) drop-shadow(0 0 24px rgba(255, 60, 40, 1)); }
    58% { transform: translateX(-15%) translateY(1%); filter: var(--trail-back) drop-shadow(0 0 8px rgba(255, 60, 40, .55)); }
    66% { transform: translateX(-17%) translateY(-1%); filter: var(--trail-back) drop-shadow(0 0 28px rgba(255, 60, 40, 1)); }
    74% { transform: translateX(-18%) translateY(1%); filter: var(--trail-back) drop-shadow(0 0 10px rgba(255, 60, 40, .6)); }
    82% { transform: translateX(-20%) translateY(-1%); filter: var(--trail-back) drop-shadow(0 0 32px rgba(255, 60, 40, 1)); }
    90% { transform: translateX(-21%) translateY(1%); filter: var(--trail-back) drop-shadow(0 0 12px rgba(255, 60, 40, .65)); }
    100% { transform: translateX(-22%); filter: var(--trail-back) drop-shadow(0 0 34px rgba(255, 60, 40, 1)); } }
/* Замах глотком (wind-maw): подаётся вперёд и чуть приподнимается, как в начале глотка (gulp); удар
   после него (strike-maw) — рывок с этого положения. Замах крупом (wind-rear): разворачивается задом,
   как в gulp-rear, и приседает; удар после (strike-rear) — толчок крупом и разворот обратно. */
.stage-scale.wind-maw-a { animation: wind-maw-a var(--wind-ms, 1000ms) ease-out forwards; animation-delay: var(--wind-offset, 0ms); }
.stage-scale.wind-maw-b { animation: wind-maw-b var(--wind-ms, 1000ms) ease-out forwards; animation-delay: var(--wind-offset, 0ms); }
@keyframes wind-maw-a { 0% { transform: none; filter: var(--trail-none) drop-shadow(0 0 0 rgba(255, 70, 50, 0)); }
    100% { transform: translateX(6%) translateY(-4%) scale(1.04, .99); filter: var(--trail-none) drop-shadow(0 0 20px rgba(255, 70, 50, 1)); } }
@keyframes wind-maw-b { 0% { transform: none; filter: var(--trail-none) drop-shadow(0 0 0 rgba(255, 70, 50, 0)); }
    100% { transform: translateX(6%) translateY(-4%) scale(1.04, .99); filter: var(--trail-none) drop-shadow(0 0 20px rgba(255, 70, 50, 1)); } }
.stage-scale[class*="wind-rear-"], .stage-scale[class*="unwind-rear-"] { transform-origin: 50% 100%; }
/* Проведение (StrikeRules.Phases, 12.10.2026; переделано 13.10.2026 — автор: «у укуса иногда подвисает конец
   анимации удара»): боец не вылетает вперёд заранее, а за время проведения (--swing-ms) подбирается — уходит ещё
   чуть назад и напрягается, и так ждёт. Сам рывок вперёд (strike) делает удар (Blow): попадание на 30 % его
   полусекунды (0,15 с), туда же цифра и вспышка на жертве (отрицательные задержки −.53 с). Так опоздавший на
   дрожание тика удар читается как напряжение перед броском, а не как зависший в воздухе конь. Отрицательная
   задержка --wind-offset — подхват посреди фазы, когда сцену открыли на ходу. */
.stage-scale.swing-a, .stage-scale.swing-b { animation: swing-a var(--swing-ms, 400ms) ease-out forwards; animation-delay: var(--wind-offset, 0ms); }
.stage-scale.swing-b { animation-name: swing-b; }
.stage-scale.strike-a, .stage-scale.strike-b { animation: strike-a .5s linear; }
.stage-scale.strike-b { animation-name: strike-b; }
@keyframes swing-a { 0% { transform: translateX(-16%); filter: var(--trail-back) drop-shadow(0 0 20px rgba(255, 70, 50, 1)); }
    100% { transform: translateX(-20%) scale(1.02, .98); filter: var(--trail-none) drop-shadow(0 0 26px rgba(255, 70, 50, 1)); } }
@keyframes swing-b { 0% { transform: translateX(-16%); filter: var(--trail-back) drop-shadow(0 0 20px rgba(255, 70, 50, 1)); }
    100% { transform: translateX(-20%) scale(1.02, .98); filter: var(--trail-none) drop-shadow(0 0 26px rgba(255, 70, 50, 1)); } }
@keyframes strike-a { 0% { transform: translateX(-20%) scale(1.02, .98); filter: var(--trail-none); animation-timing-function: cubic-bezier(.9, 0, 1, .6); }
    30% { transform: translateX(24%); filter: var(--trail-fwd); animation-timing-function: linear; }
    40% { transform: translateX(24%); filter: var(--trail-none); animation-timing-function: ease-in-out; }
    100% { transform: translateX(0); filter: var(--trail-none); } }
@keyframes strike-b { 0% { transform: translateX(-20%) scale(1.02, .98); filter: var(--trail-none); animation-timing-function: cubic-bezier(.9, 0, 1, .6); }
    30% { transform: translateX(24%); filter: var(--trail-fwd); animation-timing-function: linear; }
    40% { transform: translateX(24%); filter: var(--trail-none); animation-timing-function: ease-in-out; }
    100% { transform: translateX(0); filter: var(--trail-none); } }
.stage-scale.swing-heavy-a, .stage-scale.swing-heavy-b { animation: swing-heavy-a var(--swing-ms, 500ms) ease-out forwards; animation-delay: var(--wind-offset, 0ms); }
.stage-scale.swing-heavy-b { animation-name: swing-heavy-b; }
.stage-scale.strike-heavy-a, .stage-scale.strike-heavy-b { animation: strike-heavy-a .55s linear; }
.stage-scale.strike-heavy-b { animation-name: strike-heavy-b; }
@keyframes swing-heavy-a { 0% { transform: translateX(-22%); filter: var(--trail-back) drop-shadow(0 0 34px rgba(255, 60, 40, 1)); }
    100% { transform: translateX(-26%) scale(1.03, .97); filter: var(--trail-none) drop-shadow(0 0 40px rgba(255, 60, 40, 1)); } }
@keyframes swing-heavy-b { 0% { transform: translateX(-22%); filter: var(--trail-back) drop-shadow(0 0 34px rgba(255, 60, 40, 1)); }
    100% { transform: translateX(-26%) scale(1.03, .97); filter: var(--trail-none) drop-shadow(0 0 40px rgba(255, 60, 40, 1)); } }
@keyframes strike-heavy-a { 0% { transform: translateX(-26%) scale(1.03, .97); filter: var(--trail-none); animation-timing-function: cubic-bezier(.9, 0, 1, .6); }
    30% { transform: translateX(30%) scale(1.06, .97); filter: var(--trail-fwd) drop-shadow(0 0 24px rgba(255, 60, 40, .9)); animation-timing-function: linear; }
    40% { transform: translateX(30%) scale(1.06, .97); filter: var(--trail-none); animation-timing-function: ease-in-out; }
    100% { transform: translateX(0); filter: var(--trail-none); } }
@keyframes strike-heavy-b { 0% { transform: translateX(-26%) scale(1.03, .97); filter: var(--trail-none); animation-timing-function: cubic-bezier(.9, 0, 1, .6); }
    30% { transform: translateX(30%) scale(1.06, .97); filter: var(--trail-fwd) drop-shadow(0 0 24px rgba(255, 60, 40, .9)); animation-timing-function: linear; }
    40% { transform: translateX(30%) scale(1.06, .97); filter: var(--trail-none); animation-timing-function: ease-in-out; }
    100% { transform: translateX(0); filter: var(--trail-none); } }
.stage-scale.swing-maw-a, .stage-scale.swing-maw-b { animation: swing-maw-a var(--swing-ms, 600ms) ease-out forwards; animation-delay: var(--wind-offset, 0ms); }
.stage-scale.swing-maw-b { animation-name: swing-maw-b; }
.stage-scale.strike-maw-a, .stage-scale.strike-maw-b { animation: strike-maw-a .5s linear; }
.stage-scale.strike-maw-b { animation-name: strike-maw-b; }
@keyframes swing-maw-a { 0% { transform: translateX(6%) translateY(-4%) scale(1.04, .99); filter: var(--trail-none) drop-shadow(0 0 20px rgba(255, 70, 50, 1)); }
    100% { transform: translateX(3%) translateY(-5%) scale(1.05, .98); filter: var(--trail-none) drop-shadow(0 0 26px rgba(255, 70, 50, 1)); } }
@keyframes swing-maw-b { 0% { transform: translateX(6%) translateY(-4%) scale(1.04, .99); filter: var(--trail-none) drop-shadow(0 0 20px rgba(255, 70, 50, 1)); }
    100% { transform: translateX(3%) translateY(-5%) scale(1.05, .98); filter: var(--trail-none) drop-shadow(0 0 26px rgba(255, 70, 50, 1)); } }
@keyframes strike-maw-a { 0% { transform: translateX(3%) translateY(-5%) scale(1.05, .98); filter: var(--trail-none); animation-timing-function: cubic-bezier(.9, 0, 1, .6); }
    30% { transform: translateX(24%) scale(1.06, .98); filter: var(--trail-fwd); animation-timing-function: linear; }
    40% { transform: translateX(24%) scale(1.06, .98); filter: var(--trail-none); animation-timing-function: ease-in-out; }
    100% { transform: none; filter: var(--trail-none); } }
@keyframes strike-maw-b { 0% { transform: translateX(3%) translateY(-5%) scale(1.05, .98); filter: var(--trail-none); animation-timing-function: cubic-bezier(.9, 0, 1, .6); }
    30% { transform: translateX(24%) scale(1.06, .98); filter: var(--trail-fwd); animation-timing-function: linear; }
    40% { transform: translateX(24%) scale(1.06, .98); filter: var(--trail-none); animation-timing-function: ease-in-out; }
    100% { transform: none; filter: var(--trail-none); } }
.stage-scale.swing-rear-a, .stage-scale.swing-rear-b { animation: swing-rear-a var(--swing-ms, 600ms) ease-out forwards; animation-delay: var(--wind-offset, 0ms); transform-origin: 50% 100%; }
.stage-scale.swing-rear-b { animation-name: swing-rear-b; }
.stage-scale.strike-rear-a, .stage-scale.strike-rear-b { animation: strike-rear-a .6s linear; transform-origin: 50% 100%; }
.stage-scale.strike-rear-b { animation-name: strike-rear-b; }
@keyframes swing-rear-a { 0% { transform: scale(-1, .9); filter: drop-shadow(0 0 20px rgba(255, 70, 50, 1)); }
    100% { transform: scale(-1, .86); filter: drop-shadow(0 0 26px rgba(255, 70, 50, 1)); } }
@keyframes swing-rear-b { 0% { transform: scale(-1, .9); filter: drop-shadow(0 0 20px rgba(255, 70, 50, 1)); }
    100% { transform: scale(-1, .86); filter: drop-shadow(0 0 26px rgba(255, 70, 50, 1)); } }
@keyframes strike-rear-a { 0% { transform: scale(-1, .86); filter: none; animation-timing-function: cubic-bezier(.9, 0, 1, .6); }
    25% { transform: scale(-1.06, 1.02) translateX(-14%); filter: brightness(1.15); animation-timing-function: linear; }
    40% { transform: scale(-1.02, 1) translateX(-10%); filter: none; animation-timing-function: ease-in-out; }
    69% { transform: scale(-.74, 1) translateX(-6%); animation-timing-function: step-end; }
    70% { transform: scale(.74, 1) translateX(6%); }
    100% { transform: scale(1, 1); filter: none; } }
@keyframes strike-rear-b { 0% { transform: scale(-1, .86); filter: none; animation-timing-function: cubic-bezier(.9, 0, 1, .6); }
    25% { transform: scale(-1.06, 1.02) translateX(-14%); filter: brightness(1.15); animation-timing-function: linear; }
    40% { transform: scale(-1.02, 1) translateX(-10%); filter: none; animation-timing-function: ease-in-out; }
    69% { transform: scale(-.74, 1) translateX(-6%); animation-timing-function: step-end; }
    70% { transform: scale(.74, 1) translateX(6%); }
    100% { transform: scale(1, 1); filter: none; } }
/* Замах без удара (цель ушла, замах сорван, бросок не удался): боец возвращается на место роликом,
   а не скачком — после замаха крупом он стоит задом (автор, 11.10.2026). */
.stage-scale.unwind-a, .stage-scale.unwind-b { animation: unwind .5s ease-out; }
.stage-scale.unwind-heavy-a, .stage-scale.unwind-heavy-b { animation: unwind-heavy .5s ease-out; }
.stage-scale.unwind-maw-a, .stage-scale.unwind-maw-b { animation: unwind-maw .5s ease-out; }
.stage-scale.unwind-rear-a, .stage-scale.unwind-rear-b { animation: unwind-rear .6s ease-in-out; }
@keyframes unwind { from { transform: translateX(-16%); filter: var(--trail-back) drop-shadow(0 0 20px rgba(255, 70, 50, 1)); } to { transform: none; filter: none; } }
@keyframes unwind-heavy { from { transform: translateX(-22%); filter: var(--trail-back) drop-shadow(0 0 34px rgba(255, 60, 40, 1)); } to { transform: none; filter: none; } }
@keyframes unwind-maw { from { transform: translateX(6%) translateY(-4%) scale(1.04, .99); filter: drop-shadow(0 0 20px rgba(255, 70, 50, 1)); } to { transform: none; filter: none; } }
@keyframes unwind-rear { 0% { transform: scale(-1, .9); filter: drop-shadow(0 0 20px rgba(255, 70, 50, 1)); }
    40% { transform: scale(-1, 1); filter: none; }
    69% { transform: scale(-.74, 1); animation-timing-function: step-end; }
    70% { transform: scale(.74, 1); }
    100% { transform: scale(1, 1); filter: none; } }
.stage-scale.wind-rear-a { animation: wind-rear-a var(--wind-ms, 1000ms) ease-in-out forwards; animation-delay: var(--wind-offset, 0ms); }
.stage-scale.wind-rear-b { animation: wind-rear-b var(--wind-ms, 1000ms) ease-in-out forwards; animation-delay: var(--wind-offset, 0ms); }
/* В развороте силуэт чуть сужается, затем направление меняется одним шагом.
   Плавный переход между положительным и отрицательным масштабом делал фигуру невидимой. */
@keyframes wind-rear-a { 0% { transform: scale(1, 1); filter: drop-shadow(0 0 0 rgba(255, 70, 50, 0)); }
    17% { transform: scale(.74, 1); animation-timing-function: step-end; }
    18% { transform: scale(-.74, 1); }
    35% { transform: scale(-1, 1); filter: drop-shadow(0 0 10px rgba(255, 70, 50, .7)); }
    100% { transform: scale(-1, .9); filter: drop-shadow(0 0 20px rgba(255, 70, 50, 1)); } }
@keyframes wind-rear-b { 0% { transform: scale(1, 1); filter: drop-shadow(0 0 0 rgba(255, 70, 50, 0)); }
    17% { transform: scale(.74, 1); animation-timing-function: step-end; }
    18% { transform: scale(-.74, 1); }
    35% { transform: scale(-1, 1); filter: drop-shadow(0 0 10px rgba(255, 70, 50, .7)); }
    100% { transform: scale(-1, .9); filter: drop-shadow(0 0 20px rgba(255, 70, 50, 1)); } }
/* Попадание по замахивающемуся отзывается на обёртке. Отдельный translate сохраняет отражение
   правого бойца, а движение рисунка продолжает свой замах. Вспышка и толчок совпадают с цифрой. */
.stage-art.flash-a { animation: art-flash-a 1s linear -.53s; }
.stage-art.flash-b { animation: art-flash-b 1s linear -.53s; }
.stage-art.recoil-a { animation: art-flash-a 1s linear -.53s, art-recoil-a 1s linear -.53s; }
.stage-art.recoil-b { animation: art-flash-b 1s linear -.53s, art-recoil-b 1s linear -.53s; }
@keyframes art-flash-a { 0%, 68% { filter: brightness(1); } 70% { filter: brightness(2.2) sepia(1) saturate(5) hue-rotate(-35deg); } 86% { filter: brightness(1.2); } 100% { filter: brightness(1); } }
@keyframes art-flash-b { 0%, 68% { filter: brightness(1); } 70% { filter: brightness(2.2) sepia(1) saturate(5) hue-rotate(-35deg); } 86% { filter: brightness(1.2); } 100% { filter: brightness(1); } }
@keyframes art-recoil-a { 0%, 68%, 100% { translate: 0; } 70%, 74% { translate: var(--art-recoil); animation-timing-function: ease-out; } }
@keyframes art-recoil-b { 0%, 68%, 100% { translate: 0; } 70%, 74% { translate: var(--art-recoil); animation-timing-function: ease-out; } }
@keyframes hurt-a { 0%, 68% { transform: translateX(0); filter: brightness(1); }
    70%, 74% { transform: translateX(var(--hit-recoil)); filter: brightness(2.2) sepia(1) saturate(5) hue-rotate(-35deg); animation-timing-function: ease-out; }
    86% { filter: brightness(1); }
    100% { transform: translateX(0); filter: brightness(1); } }
@keyframes hurt-b { 0%, 68% { transform: translateX(0); filter: brightness(1); }
    70%, 74% { transform: translateX(var(--hit-recoil)); filter: brightness(2.2) sepia(1) saturate(5) hue-rotate(-35deg); animation-timing-function: ease-out; }
    86% { filter: brightness(1); }
    100% { transform: translateX(0); filter: brightness(1); } }
@keyframes dodge-a { 0%, 62% { transform: translate(0, 0); } 72% { transform: translate(-12%, -5%); } 100% { transform: translate(0, 0); } }
@keyframes dodge-b { 0%, 62% { transform: translate(0, 0); } 72% { transform: translate(-12%, -5%); } 100% { transform: translate(0, 0); } }
.stage-scale { --trail-none: drop-shadow(0 0 0 rgba(214, 168, 86, 0)) drop-shadow(0 0 0 rgba(214, 168, 86, 0));
    --trail-back: drop-shadow(10px 0 0 rgba(214, 168, 86, .38)) drop-shadow(22px 0 0 rgba(214, 168, 86, .18));
    --trail-fwd: drop-shadow(-14px 0 0 rgba(214, 168, 86, .45)) drop-shadow(-30px 0 0 rgba(214, 168, 86, .2)); }

/* Уменьшение. Искры сходятся 0,6 с, на вспышке жертва сжимается — рост меняется с задержкой и перебором
   («рывком»): сервер присылает его сразу, но класс shrink-* уже стоит, и задержка 0,6 с держит старый рост. */
.stage-scale[class*="shrink-"] { transition: height 800ms cubic-bezier(.34, 1.7, .5, 1) 600ms; }
.stage-scale.shrink-a { animation: shrink-a 1.6s linear; }
.stage-scale.shrink-b { animation: shrink-b 1.6s linear; }
.stage-scale.cast-a { animation: cast-a 1s ease-in-out; }
.stage-scale.cast-b { animation: cast-b 1s ease-in-out; }
@keyframes shrink-a { 0% { filter: none; transform: none; }
    8% { filter: drop-shadow(0 0 14px #b57bff) brightness(1.15); }
    14% { transform: translateX(1.5%); } 20% { transform: translateX(-1.5%); } 26% { transform: translateX(1.2%); } 32% { transform: translateX(-1%); }
    38% { filter: drop-shadow(0 0 22px #e0c4ff) brightness(2.2) saturate(.4); transform: none; }
    50% { filter: drop-shadow(0 0 14px #b57bff) brightness(1.3); }
    100% { filter: none; transform: none; } }
@keyframes shrink-b { 0% { filter: none; transform: none; }
    8% { filter: drop-shadow(0 0 14px #b57bff) brightness(1.15); }
    14% { transform: translateX(1.5%); } 20% { transform: translateX(-1.5%); } 26% { transform: translateX(1.2%); } 32% { transform: translateX(-1%); }
    38% { filter: drop-shadow(0 0 22px #e0c4ff) brightness(2.2) saturate(.4); transform: none; }
    50% { filter: drop-shadow(0 0 14px #b57bff) brightness(1.3); }
    100% { filter: none; transform: none; } }
/* Каст — только свечение, без приподнимания (автор, 13.10.2026). */
@keyframes cast-a { 0%, 100% { filter: none; } 25%, 60% { filter: drop-shadow(0 0 12px #b57bff) drop-shadow(0 0 28px rgba(181, 123, 255, .6)) brightness(1.12); } }
@keyframes cast-b { 0%, 100% { filter: none; } 25%, 60% { filter: drop-shadow(0 0 12px #b57bff) drop-shadow(0 0 28px rgba(181, 123, 255, .6)) brightness(1.12); } }
.sparks { position: absolute; left: 50%; top: 52%; width: 0; height: 0; }
.sparks i { position: absolute; width: 6px; height: 6px; margin: -3px; border-radius: 50%; background: #e0c4ff; box-shadow: 0 0 8px 2px #a56bff;
    opacity: 0; animation: spark-in .7s ease-in var(--d) both; }
@keyframes spark-in { 0% { opacity: 0; transform: rotate(var(--a)) translateX(120px) scale(.6); }
    25% { opacity: 1; }
    100% { opacity: 0; transform: rotate(var(--a)) translateX(0) scale(1.3); } }
.dmg-float.spell { color: #c79bff; font-size: 24px; }

/* Ярость чудовища (VoreRules.BossRages, автор 15.10.2026): сознание кончилось, а оно подпрыгивает и быстро
   приземляется в снопе искр, с недолгим золотым ореолом. Присед, прыжок, на приземлении сплющивается — и
   рост меняется именно тогда: сервер присылает новый сразу, а переход высоты ждёт приземления (0,43 с). */
.stage-scale[class*="rage-"] { transform-origin: 50% 100%; transition: height 260ms cubic-bezier(.3, 1.6, .5, 1) 430ms; }
.stage-scale.rage-a { animation: rage-a .95s linear; }
.stage-scale.rage-b { animation: rage-b .95s linear; }
@keyframes rage-a { 0% { transform: none; }
    10% { transform: translateY(3%) scale(1.03, .94); animation-timing-function: cubic-bezier(.2, .7, .4, 1); }
    32% { transform: translateY(-30%) scale(.97, 1.04); animation-timing-function: cubic-bezier(.6, 0, 1, .6); }
    45% { transform: translateY(0) scale(1.08, .86); }
    58% { transform: translateY(-2%) scale(.98, 1.03); }
    70%, 100% { transform: none; } }
@keyframes rage-b { 0% { transform: none; }
    10% { transform: translateY(3%) scale(1.03, .94); animation-timing-function: cubic-bezier(.2, .7, .4, 1); }
    32% { transform: translateY(-30%) scale(.97, 1.04); animation-timing-function: cubic-bezier(.6, 0, 1, .6); }
    45% { transform: translateY(0) scale(1.08, .86); }
    58% { transform: translateY(-2%) scale(.98, 1.03); }
    70%, 100% { transform: none; } }
/* Искры разлетаются от копыт в миг приземления — золотые, а не сиреневые, как у чар. */
.sparks.burst { top: 96%; }
.sparks.burst i { background: #ffd27a; box-shadow: 0 0 8px 2px #ff9a2e; animation: spark-out .75s ease-out calc(430ms + var(--d)) both; }
@keyframes spark-out { 0% { opacity: 0; transform: rotate(var(--a)) translateX(0) scale(1.3); }
    15% { opacity: 1; }
    100% { opacity: 0; transform: rotate(var(--a)) translateX(150px) scale(.5); } }
/* Ореол — вспышкой на приземлении (31 % от 1,4 с — те же 0,43 с) и гаснет за секунду, на обёртке (.stage-glow), как стойка. */
.stage-glow.rage { animation: rage-glow 1.4s ease-out both; }
.stage-glow.rage.stomp { transform-origin: 50% 100%; animation: rage-glow 1.4s ease-out both, king-stomp .95s linear; }
@keyframes king-stomp {
    0% { transform: none; }
    10% { transform: translateY(2%) scale(1.03, .94); animation-timing-function: cubic-bezier(.2, .7, .4, 1); }
    32% { transform: translateY(-20%) scale(.97, 1.04); animation-timing-function: cubic-bezier(.6, 0, 1, .6); }
    45% { transform: scale(1.08, .86); }
    70%, 100% { transform: none; }
}
@media (prefers-reduced-motion: reduce) { .stage-glow.rage.stomp { animation: rage-glow 1.4s ease-out both; } }
@keyframes rage-glow { 0%, 27% { filter: drop-shadow(0 0 0 rgba(255, 190, 80, 0)); }
    31% { filter: drop-shadow(0 0 16px rgba(255, 200, 90, 1)) drop-shadow(0 0 42px rgba(255, 140, 40, .85)) brightness(1.25); }
    100% { filter: drop-shadow(0 0 0 rgba(255, 190, 80, 0)); } }
.dmg-float.rage { color: #ffc861; font-size: 24px; text-shadow: 0 0 10px rgba(255, 150, 40, .9), 0 2px 0 #000; }

/* Глоток (1,8 с). Хищник подаётся к жертве, раскрыв пасть, затем напрягается — волна по корпусу. Жертва
   без борьбы втягивается: летит к пасти, сжимаясь и темнея, и гаснет; рисунок не вращается. В круп:
   хищник сперва разворачивается хвостом к жертве (зеркально, как делает лошадь, подставляя круп), и жертва
   летит прямо к нему; после — разворачивается обратно. Исчезнув, жертва остаётся скрытой до конца
   сцены (fill: forwards). Ролики -a/-b — парные, см. выше. */
.stage-scale.gulp-a { animation: gulp-a 1.8s ease-in-out; }
.stage-scale.gulp-b { animation: gulp-b 1.8s ease-in-out; }
.stage-scale.gulp-rear-a { animation: gulp-rear-a 1.8s ease-in-out; }
.stage-scale.gulp-rear-b { animation: gulp-rear-b 1.8s ease-in-out; }
.stage-scale.engulf-a, .stage-scale.engulf-rear-a { animation: engulf-a 1.8s ease-in forwards; }
.stage-scale.engulf-b, .stage-scale.engulf-rear-b { animation: engulf-b 1.8s ease-in forwards; }
.stage-scale.engulf-fly-a { animation: engulf-fly-a 1.8s linear forwards; }
.stage-scale.engulf-fly-b { animation: engulf-fly-b 1.8s linear forwards; }
.stage-scale[class*="gulp-"], .stage-scale[class*="engulf-"] { transform-origin: 50% 100%; }
/* Жертва схлопывается к своему центру и уходит на уровень пасти (голова высоко) или крупа (середина корпуса). */
.stage-scale[class*="engulf-"] { transform-origin: 50% 50%; --lift: -22%; }
.stage-scale[class*="engulf-rear-"] { --lift: -6%; }
@keyframes gulp-a { 0%, 100% { transform: none; filter: none; }
    18%, 42% { transform: translateX(8%); }
    58% { transform: translateX(5%) scale(1.05, .97); filter: brightness(1.12); }
    74% { transform: translateX(2%) scale(1.07, .98); filter: brightness(1.18); }
    88% { transform: scale(1.03, 1); filter: brightness(1.05); } }
@keyframes gulp-b { 0%, 100% { transform: none; filter: none; }
    18%, 42% { transform: translateX(8%); }
    58% { transform: translateX(5%) scale(1.05, .97); filter: brightness(1.12); }
    74% { transform: translateX(2%) scale(1.07, .98); filter: brightness(1.18); }
    88% { transform: scale(1.03, 1); filter: brightness(1.05); } }
@keyframes gulp-rear-a { 0% { transform: scale(1, 1); filter: none; }
    6% { transform: scale(.74, 1); animation-timing-function: step-end; }
    7% { transform: scale(-.74, 1); }
    14%, 28% { transform: scale(-1, 1); }
    44% { transform: scale(-1.02, .93); }
    70% { transform: scale(-1.06, .96); filter: brightness(1.15); }
    84% { transform: scale(-1.02, 1); filter: brightness(1.05); }
    91% { transform: scale(-.74, 1); animation-timing-function: step-end; }
    92% { transform: scale(.74, 1); }
    100% { transform: scale(1, 1); filter: none; } }
@keyframes gulp-rear-b { 0% { transform: scale(1, 1); filter: none; }
    6% { transform: scale(.74, 1); animation-timing-function: step-end; }
    7% { transform: scale(-.74, 1); }
    14%, 28% { transform: scale(-1, 1); }
    44% { transform: scale(-1.02, .93); }
    70% { transform: scale(-1.06, .96); filter: brightness(1.15); }
    84% { transform: scale(-1.02, 1); filter: brightness(1.05); }
    91% { transform: scale(-.74, 1); animation-timing-function: step-end; }
    92% { transform: scale(.74, 1); }
    100% { transform: scale(1, 1); filter: none; } }
/* Глоток сразу после замаха тем же органом (wind-maw / wind-rear): начинается с положения замаха —
   без нового разворота или подачи вперёд (автор, 11.10.2026: «делает ещё один разворот»). */
.stage-scale.gulp-w-a { animation: gulp-w-a 1.8s ease-in-out; }
.stage-scale.gulp-w-b { animation: gulp-w-b 1.8s ease-in-out; }
.stage-scale.gulp-rear-w-a { animation: gulp-rear-w-a 1.8s ease-in-out; }
.stage-scale.gulp-rear-w-b { animation: gulp-rear-w-b 1.8s ease-in-out; }
@keyframes gulp-w-a { 0% { transform: translateX(6%) translateY(-4%) scale(1.04, .99); filter: drop-shadow(0 0 20px rgba(255, 70, 50, 1)); }
    18%, 42% { transform: translateX(8%); filter: none; }
    58% { transform: translateX(5%) scale(1.05, .97); filter: brightness(1.12); }
    74% { transform: translateX(2%) scale(1.07, .98); filter: brightness(1.18); }
    88% { transform: scale(1.03, 1); filter: brightness(1.05); }
    100% { transform: none; filter: none; } }
@keyframes gulp-w-b { 0% { transform: translateX(6%) translateY(-4%) scale(1.04, .99); filter: drop-shadow(0 0 20px rgba(255, 70, 50, 1)); }
    18%, 42% { transform: translateX(8%); filter: none; }
    58% { transform: translateX(5%) scale(1.05, .97); filter: brightness(1.12); }
    74% { transform: translateX(2%) scale(1.07, .98); filter: brightness(1.18); }
    88% { transform: scale(1.03, 1); filter: brightness(1.05); }
    100% { transform: none; filter: none; } }
@keyframes gulp-rear-w-a { 0% { transform: scale(-1, .9); filter: drop-shadow(0 0 20px rgba(255, 70, 50, 1)); }
    14%, 28% { transform: scale(-1, 1); filter: none; }
    44% { transform: scale(-1.02, .93); }
    70% { transform: scale(-1.06, .96); filter: brightness(1.15); }
    84% { transform: scale(-1.02, 1); filter: brightness(1.05); }
    91% { transform: scale(-.74, 1); animation-timing-function: step-end; }
    92% { transform: scale(.74, 1); }
    100% { transform: scale(1, 1); filter: none; } }
@keyframes gulp-rear-w-b { 0% { transform: scale(-1, .9); filter: drop-shadow(0 0 20px rgba(255, 70, 50, 1)); }
    14%, 28% { transform: scale(-1, 1); filter: none; }
    44% { transform: scale(-1.02, .93); }
    70% { transform: scale(-1.06, .96); filter: brightness(1.15); }
    84% { transform: scale(-1.02, 1); filter: brightness(1.05); }
    91% { transform: scale(-.74, 1); animation-timing-function: step-end; }
    92% { transform: scale(.74, 1); }
    100% { transform: scale(1, 1); filter: none; } }
@keyframes engulf-a { 0% { transform: none; opacity: 1; filter: none; }
    10% { transform: translateX(-1.5%); } 18% { transform: translateX(1%); }
    30% { transform: translateX(4%); }
    68% { transform: translateX(calc(45% + 60px)) translateY(calc(var(--lift) * .5)) scale(.5); opacity: .85; filter: brightness(.6); }
    92% { transform: translateX(calc(75% + 110px)) translateY(var(--lift)) scale(.1); opacity: .15; filter: brightness(.3); }
    100% { transform: translateX(calc(75% + 110px)) translateY(var(--lift)) scale(.05); opacity: 0; } }
@keyframes engulf-b { 0% { transform: none; opacity: 1; filter: none; }
    10% { transform: translateX(-1.5%); } 18% { transform: translateX(1%); }
    30% { transform: translateX(4%); }
    68% { transform: translateX(calc(45% + 60px)) translateY(calc(var(--lift) * .5)) scale(.5); opacity: .85; filter: brightness(.6); }
    92% { transform: translateX(calc(75% + 110px)) translateY(var(--lift)) scale(.1); opacity: .15; filter: brightness(.3); }
    100% { transform: translateX(calc(75% + 110px)) translateY(var(--lift)) scale(.05); opacity: 0; } }

/* Обморок. Удар, сваливший бойца, играет ролик faint: дёрнулся на ударе (0,7 с); над ним вылетает
   надпись, как цифры урона. Пока он без чувств, вокруг тёмный ореол (.stage-glow.down), сам рисунок
   не темнеет (автор, 11.10.2026). Удар по уже лежачему только вспыхивает (thud). */
.stage-scale { transition: height 300ms ease, filter 450ms ease; }
.stage-scale.faint-a { animation: faint-a 1.3s ease-in -.55s forwards; }
.stage-scale.faint-b { animation: faint-b 1.3s ease-in -.55s forwards; }
.stage-scale.thud-a { animation: thud-a 1s linear -.53s; }
.stage-scale.thud-b { animation: thud-b 1s linear -.53s; }
@keyframes faint-a { 0%, 54% { transform: none; filter: none; }
    57% { transform: translateX(-8%); filter: brightness(2.2) sepia(1) saturate(5) hue-rotate(-35deg); }
    64% { transform: translateX(2%); filter: brightness(1.2); }
    100% { transform: none; filter: none; } }
@keyframes faint-b { 0%, 54% { transform: none; filter: none; }
    57% { transform: translateX(-8%); filter: brightness(2.2) sepia(1) saturate(5) hue-rotate(-35deg); }
    64% { transform: translateX(2%); filter: brightness(1.2); }
    100% { transform: none; filter: none; } }
/* Свалили на замахе (faint-w…): поза замаха держится до попадания (54 %), там вспышка — и боец разом
   на месте и без чувств (автор, 11.10.2026). По виду замаха: отход, пасть, круп, сильный удар зверя. */
.stage-scale.faint-w-a, .stage-scale.faint-w-b { animation: faint-w-a 1.3s ease-in -.55s forwards; }
.stage-scale.faint-w-b { animation-name: faint-w-b; }
.stage-scale.faint-w-heavy-a, .stage-scale.faint-w-heavy-b { animation: faint-w-heavy-a 1.3s ease-in -.55s forwards; }
.stage-scale.faint-w-heavy-b { animation-name: faint-w-heavy-b; }
.stage-scale.faint-w-maw-a, .stage-scale.faint-w-maw-b { animation: faint-w-maw-a 1.3s ease-in -.55s forwards; }
.stage-scale.faint-w-maw-b { animation-name: faint-w-maw-b; }
.stage-scale.faint-w-rear-a, .stage-scale.faint-w-rear-b { animation: faint-w-rear-a 1.3s ease-in -.55s forwards; transform-origin: 50% 100%; }
.stage-scale.faint-w-rear-b { animation-name: faint-w-rear-b; }
@keyframes faint-w-a { 0%, 54% { transform: translateX(-16%); filter: var(--trail-back) drop-shadow(0 0 20px rgba(255, 70, 50, 1)); }
    57% { transform: translateX(-22%); filter: brightness(2.2) sepia(1) saturate(5) hue-rotate(-35deg); }
    66% { transform: translateX(1%); filter: brightness(1.2); }
    100% { transform: none; filter: none; } }
@keyframes faint-w-b { 0%, 54% { transform: translateX(-16%); filter: var(--trail-back) drop-shadow(0 0 20px rgba(255, 70, 50, 1)); }
    57% { transform: translateX(-22%); filter: brightness(2.2) sepia(1) saturate(5) hue-rotate(-35deg); }
    66% { transform: translateX(1%); filter: brightness(1.2); }
    100% { transform: none; filter: none; } }
@keyframes faint-w-heavy-a { 0%, 54% { transform: translateX(-22%); filter: var(--trail-back) drop-shadow(0 0 34px rgba(255, 60, 40, 1)); }
    57% { transform: translateX(-28%); filter: brightness(2.2) sepia(1) saturate(5) hue-rotate(-35deg); }
    66% { transform: translateX(1%); filter: brightness(1.2); }
    100% { transform: none; filter: none; } }
@keyframes faint-w-heavy-b { 0%, 54% { transform: translateX(-22%); filter: var(--trail-back) drop-shadow(0 0 34px rgba(255, 60, 40, 1)); }
    57% { transform: translateX(-28%); filter: brightness(2.2) sepia(1) saturate(5) hue-rotate(-35deg); }
    66% { transform: translateX(1%); filter: brightness(1.2); }
    100% { transform: none; filter: none; } }
@keyframes faint-w-maw-a { 0%, 54% { transform: translateX(6%) translateY(-4%) scale(1.04, .99); filter: drop-shadow(0 0 20px rgba(255, 70, 50, 1)); }
    57% { transform: translateX(-4%) translateY(-2%); filter: brightness(2.2) sepia(1) saturate(5) hue-rotate(-35deg); }
    66% { transform: translateX(1%); filter: brightness(1.2); }
    100% { transform: none; filter: none; } }
@keyframes faint-w-maw-b { 0%, 54% { transform: translateX(6%) translateY(-4%) scale(1.04, .99); filter: drop-shadow(0 0 20px rgba(255, 70, 50, 1)); }
    57% { transform: translateX(-4%) translateY(-2%); filter: brightness(2.2) sepia(1) saturate(5) hue-rotate(-35deg); }
    66% { transform: translateX(1%); filter: brightness(1.2); }
    100% { transform: none; filter: none; } }
@keyframes faint-w-rear-a { 0%, 54% { transform: scale(-1, .9); filter: drop-shadow(0 0 20px rgba(255, 70, 50, 1)); }
    57% { transform: scale(-1, .84); filter: brightness(2.2) sepia(1) saturate(5) hue-rotate(-35deg); }
    62% { transform: scale(-.74, .91); animation-timing-function: step-end; }
    63% { transform: scale(.74, .91); }
    68% { transform: scale(1, 1); filter: brightness(1.2); }
    100% { transform: scale(1, 1); filter: none; } }
@keyframes faint-w-rear-b { 0%, 54% { transform: scale(-1, .9); filter: drop-shadow(0 0 20px rgba(255, 70, 50, 1)); }
    57% { transform: scale(-1, .84); filter: brightness(2.2) sepia(1) saturate(5) hue-rotate(-35deg); }
    62% { transform: scale(-.74, .91); animation-timing-function: step-end; }
    63% { transform: scale(.74, .91); }
    68% { transform: scale(1, 1); filter: brightness(1.2); }
    100% { transform: scale(1, 1); filter: none; } }
@keyframes thud-a { 0%, 68% { filter: none; } 72% { filter: brightness(2) sepia(1) saturate(5) hue-rotate(-35deg); } 100% { filter: none; } }
@keyframes thud-b { 0%, 68% { filter: none; } 72% { filter: brightness(2) sepia(1) saturate(5) hue-rotate(-35deg); } 100% { filter: none; } }

@keyframes engulf-fly-a { 0% { transform: none; opacity: 1; } 10% { transform: translateX(-1.5%); } 18% { transform: translateX(1%); }
    27% { transform: translateX(3%); opacity: 1; } 28%, 100% { transform: translateX(3%); opacity: 0; } }
@keyframes engulf-fly-b { 0% { transform: none; opacity: 1; } 10% { transform: translateX(-1.5%); } 18% { transform: translateX(1%); }
    27% { transform: translateX(3%); opacity: 1; } 28%, 100% { transform: translateX(3%); opacity: 0; } }
.container-box.swallow-arrive { animation: swallow-arrive .7s ease-out; }
@keyframes swallow-arrive { 0% { box-shadow: 0 0 0 0 rgba(201, 164, 104, 0); } 25% { box-shadow: 0 0 14px 3px rgba(201, 164, 104, .7); filter: brightness(1.25); } 100% { box-shadow: 0 0 0 0 rgba(201, 164, 104, 0); } }
.stage-actor { position: relative; }
.dmg-layer { position: absolute; inset: 0 0 104px; pointer-events: none; z-index: 3; }
.dmg-float { position: absolute; left: calc(50% + var(--dx, 0px)); top: calc(26% + var(--dy, 0px)); font-family: var(--font-title); font-size: 30px; font-weight: 700; white-space: nowrap;
    text-shadow: 0 2px 0 #000, 0 0 8px #000; opacity: 0; animation: dmg-rise 2.2s ease-out -.53s both; }
/* Цвета — как в ленте (автор, 14.10.2026): жизнь тёмно-красным, сознание ярко-красным (до того синим). */
.dmg-float.hp { color: #b8332a; }
.dmg-float.crit { color: #ffb347; font-size: 44px; }
.dmg-float.heavy { font-size: 40px; } /* сильный удар зверя (StrikeRules.HeavyDamage) */
.dmg-float.mind { color: #ff4a3d; }
.dmg-float.faint { top: 10%; color: #9fb0c9; font-size: 22px; font-style: italic; animation-delay: -.33s; }
.dmg-float.miss { color: #c9c1b3; font-size: 22px; font-style: italic; }
/* Погашено стойкой — белым «блок −N%» (автор, 11.10.2026: без вспышки, стойка и так светится; 12.10.2026: словом,
   а не голой цифрой, — её читали как рану). Что прошло сквозь стойку, летит своей цифрой: тёмно-красной (жизнь) или ярко-красной (сознание). */
.dmg-float.block { color: #ffffff; font-size: 24px; font-style: italic; text-shadow: 0 0 10px rgba(255, 255, 255, .9), 0 2px 0 #000; }
/* Сбили замах — белая надпись, как «мимо». */
.dmg-float.broke { color: #ffffff; font-size: 22px; font-style: italic; }
/* Ролик с паузой до 31 % (0,7 с) остался от попадания на 70 % выпада; теперь он стартует с задержкой −0,68 с, и цифра выскакивает сразу на ударе (12.10.2026). */
@keyframes dmg-rise { 0%, 31% { opacity: 0; transform: translate(-50%, 0) scale(.6); }
    34% { opacity: 1; transform: translate(-50%, 0) scale(1.5); }
    42% { opacity: 1; transform: translate(-50%, -4px) scale(1); }
    82% { opacity: 1; transform: translate(-50%, -44px) scale(1); }
    100% { opacity: 0; transform: translate(-50%, -64px) scale(1); } }
/* Карточки в клетке — то же зрелище, поменьше (1 с, удар на 0,7 с). Двигается вся карточка, не иконка
   (автор, 12.10.2026: один язык на списке): бьющий отводится и делает рывок к центру экрана, где стоит твой
   конь, с красной вспышкой рамки; по жертве — толчок и вспышка; увернувшийся отскакивает. Упавший лежит на боку (.fainted). */
.entity-card[class*="card-hurt-"], .entity-card[class*="card-shrink-"] { animation-duration: 1s; animation-timing-function: linear; }
.entity-card[class*="card-hurt-"] { animation-delay: -.53s; } /* удар — сразу (12.10.2026) */
.entity-card.card-hurt-a { animation-name: card-hurt-a; } .entity-card.card-hurt-b { animation-name: card-hurt-b; }
.entity-card.card-shrink-a { animation-name: card-shrink-a; } .entity-card.card-shrink-b { animation-name: card-shrink-b; }
.entity-card.card-strike-a { animation: card-strike-a 1s linear -.45s; }
.entity-card.card-strike-b { animation: card-strike-b 1s linear -.45s; }
/* Блок на карточке: белая вспышка рамки и иконки на 70 %, без красноты. */
.entity-card.card-parry-a { animation: card-parry-a 1s linear -.53s; }
.entity-card.card-parry-b { animation: card-parry-b 1s linear -.53s; }
@keyframes card-parry-a { 0%, 68% { filter: none; } 70% { filter: brightness(2.2) drop-shadow(0 0 10px #fff); } 100% { filter: none; } }
@keyframes card-parry-b { 0%, 68% { filter: none; } 70% { filter: brightness(2.2) drop-shadow(0 0 10px #fff); } 100% { filter: none; } }
.entity-card.card-dodge-a { animation: card-dodge-a 1s ease-in-out -.47s; }
.entity-card.card-dodge-b { animation: card-dodge-b 1s ease-in-out -.47s; }
.entity-card.card-cast-a .entity-icon { animation: icon-cast-a 1s ease-in-out; }
.entity-card.card-cast-b .entity-icon { animation: icon-cast-b 1s ease-in-out; }
/* Бьющий: отвод на 4 px до 0,6 с, рывок на 12 px к центру на 0,7 с с красной вспышкой рамки — в толпе видно, чья
   именно карточка бьёт, — и возврат. */
@keyframes card-strike-a { 0%, 100% { transform: none; box-shadow: 0 5px 6px -3px #000; } 8%, 60% { transform: translateX(4px); box-shadow: 0 5px 6px -3px #000; }
    70% { transform: translateX(-12px); box-shadow: 0 5px 6px -3px #000, 0 0 10px 2px rgba(224, 87, 74, .85); } 78% { transform: translateX(-12px); box-shadow: 0 5px 6px -3px #000; } }
@keyframes card-strike-b { 0%, 100% { transform: none; box-shadow: 0 5px 6px -3px #000; } 8%, 60% { transform: translateX(4px); box-shadow: 0 5px 6px -3px #000; }
    70% { transform: translateX(-12px); box-shadow: 0 5px 6px -3px #000, 0 0 10px 2px rgba(224, 87, 74, .85); } 78% { transform: translateX(-12px); box-shadow: 0 5px 6px -3px #000; } }
/* Увернувшийся: карточка отскакивает вверх-влево на ударе. */
@keyframes card-dodge-a { 0%, 62%, 100% { transform: none; } 72% { transform: translate(-7px, -4px); } }
@keyframes card-dodge-b { 0%, 62%, 100% { transform: none; } 72% { transform: translate(-7px, -4px); } }
@keyframes card-hurt-a { 0%, 68% { transform: none; filter: none; } 70% { transform: translateX(-5px); filter: brightness(1.7) sepia(1) saturate(4) hue-rotate(-35deg); }
    78% { transform: translateX(3px); filter: brightness(1.2); } 88% { transform: translateX(-2px); filter: none; } 100% { transform: none; filter: none; } }
@keyframes card-hurt-b { 0%, 68% { transform: none; filter: none; } 70% { transform: translateX(-5px); filter: brightness(1.7) sepia(1) saturate(4) hue-rotate(-35deg); }
    78% { transform: translateX(3px); filter: brightness(1.2); } 88% { transform: translateX(-2px); filter: none; } 100% { transform: none; filter: none; } }
@keyframes card-shrink-a { 0% { filter: none; } 10%, 55% { filter: drop-shadow(0 0 6px #b57bff) brightness(1.15); } 62% { filter: brightness(2) saturate(.4); } 100% { filter: none; } }
@keyframes card-shrink-b { 0% { filter: none; } 10%, 55% { filter: drop-shadow(0 0 6px #b57bff) brightness(1.15); } 62% { filter: brightness(2) saturate(.4); } 100% { filter: none; } }
@keyframes icon-cast-a { 0%, 100% { filter: none; } 25%, 60% { filter: drop-shadow(0 0 6px #b57bff) brightness(1.15); } }
@keyframes icon-cast-b { 0%, 100% { filter: none; } 25%, 60% { filter: drop-shadow(0 0 6px #b57bff) brightness(1.15); } }
.entity-card.fainted .entity-icon { filter: grayscale(.55) brightness(.6); transition: filter .45s ease .7s; }
@media (prefers-reduced-motion: reduce) {
    .stage-art[class*="recoil-"] { animation: none; }
    .stage-scale[class*="lunge-"], .stage-scale[class*="hurt-"], .stage-scale[class*="dodge-"], .stage-scale[class*="cast-"],
    .stage-scale[class*="shrink-"], .stage-scale[class*="gulp-"], .stage-scale[class*="thud-"], .stage-scale[class*="faint-"] { animation: none; }
    .entity-card[class*="card-"], .entity-card[class*="card-"] .entity-icon { animation: none; }
    .stage-scale[class*="engulf-"] { animation-duration: .01s; }
}
.stage-glyph { font-size: 60px; line-height: 1; color: var(--gold); }
.stage-caption { width: min(260px, 100%); height: 104px; text-align: center; padding-top: 6px; flex-shrink: 0; }
.stage-mid { align-self: end; display: flex; flex-direction: column; align-items: center; gap: 6px; text-align: center; font-size: 12px; padding-bottom: 116px; }
.versus-mid { display: flex; flex-direction: column; align-items: center; gap: 6px; min-width: 150px; text-align: center; }
.versus-label { font-family: var(--font-title); color: #6b645c; letter-spacing: .2em; }
.size-compare { display: flex; flex-direction: column; gap: 4px; font-size: 12px; color: var(--text); }
.can-swallow { color: #8fbf6a; }
.danger-note { color: var(--danger-text); }

.self-stats { padding-bottom: 8px; }
.self-stats dl { display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: center; gap: 6px 12px; margin: 0; font-size: 13px; }
.self-stats dt { color: var(--text); overflow-wrap: anywhere; }
.self-stats dd { margin: 0; color: var(--heading); text-align: right; font-variant-numeric: tabular-nums; }
.self-stat-group { padding: 10px 12px; border: 1px solid var(--border); border-radius: 4px; background: rgba(0, 0, 0, .16); }
.self-stat-group h3 { margin: 0 0 8px; font-size: 16px; }
.self-vitals dt { display: flex; align-items: center; gap: 8px; }
.self-vitals dd, .self-mass-current { font-weight: 500; white-space: nowrap; }
.self-vitals dd:focus-visible { outline: 1px solid var(--focus); outline-offset: 2px; }
.self-mass-current { font-size: 16px; }
.self-stat-details { padding: 4px 12px; }
/* Ремёсла: имя, уровень и тонкая кованая полоса с тлеющим золотом — до следующего уровня. */
.craft-skills-title { margin: 0 0 6px; font: 600 12px/1.2 var(--font-body); letter-spacing: .08em; text-transform: uppercase; color: var(--text-muted); }
/* Ремёсла линиями: цепочка — строка, ремесло — узел с уровнем, между узлами нить. Нить тусклая,
   пока предыдущее ниже 30, и тлеет золотом, когда следующее растёт свободно. Узел — цветом своей
   ступени, теми же, что у качества; придержанное цепочкой — тусклее. */
/* Цепочки — по центру и с запасом: по высоте расходятся на доступное место (не выше 700 px), но не больше —
   прокрутки не появится; нити одной длины, так столбцы ремёсел стоят ровно (автор, 10.10.2026). */
/* Слой Phaser должен покрывать всё содержимое ремёсел внутри прокручиваемой .self-page. */
.crafts-sheet { align-self: flex-start; }
.crafts-sheet:not(.craft-page) { min-height: 100%; box-sizing: border-box; }
.craft-chains { flex: 1 0 auto; max-height: 700px; display: grid; align-content: space-evenly; gap: 16px; width: max-content; max-width: 100%; margin: 2px auto 4px; }
.craft-chain { display: flex; align-items: flex-start; }
/* Узел — кнопка: щелчок открывает страницу ремесла (автор, 10.10.2026). */
.craft-node { flex: 0 0 112px; display: grid; justify-items: center; gap: 3px; text-align: center;
    padding: 0; border: 0; background: none; color: inherit; font: inherit; cursor: pointer; }
.craft-node:hover .craft-node-name, .craft-node:focus-visible .craft-node-name { color: var(--gold-hover); }
.craft-node:focus-visible { outline: none; }
.recipe-lock { color: #c98a5a; }
/* Уровень ремесла — ромбом (автор, 10.10.2026): квадрат с кованой рамкой навыков, повёрнутый на 45°; внутри
   значок ремесла (art/crafts), уровень — пилюлей в нижнем углу. Ступень (ржавое…белое) — оттенком рамки,
   у золота и белого ещё и свечение. Сторона 44 — диагональ ~62; рамка 7, чтобы значку хватило места. */
.craft-node-level { position: relative; width: 44px; height: 44px; margin: 9px 0 10px; display: grid; place-items: center;
    color: var(--heading); font: 600 14px/1 var(--font-body); font-variant-numeric: tabular-nums; --frame: none; z-index: 0; }
.craft-node-level::before, .craft-node-level::after { content: ""; position: absolute; inset: 0; z-index: -1; transform: rotate(45deg); pointer-events: none; }
.craft-node-level::before { inset: 3px; background: radial-gradient(circle at 50% 40%, #26221d, #0b0a09 80%); }
.craft-node-level::after { border: 10px solid transparent; border-image: var(--art-action-frame) 18% / 10px stretch; filter: var(--frame); }
.craft-art { position: absolute; left: 50%; top: 50%; width: 42px; height: 42px; transform: translate(-50%, -50%); z-index: -1;
    clip-path: polygon(50% 0, 100% 50%, 50% 100%, 0 50%); pointer-events: none; }
/* Уровень — ромбиком в нижнем левом углу, снаружи ромба (автор, 10.10.2026; ромбиком — 13.10.2026, для
   симметрии с кнопкой «Качать» справа). Кованая рамка, как у ромбиков ступеней на странице ремесла. */
.craft-node-pill, .craft-pour { position: absolute; top: calc(50% + 26px); transform: translate(-50%, -50%); z-index: 2;
    width: 22px; height: 22px; display: grid; place-items: center; padding: 0; border: 0; background: none;
    font: normal 600 10px/10px var(--font-body); color: inherit; --frame: none; }
.craft-node-pill { left: calc(50% - 27px); }
.craft-node-pill::before, .craft-node-pill::after, .craft-pour::before, .craft-pour::after { content: ""; position: absolute; inset: 0; z-index: -1; transform: rotate(45deg); pointer-events: none; }
.craft-node-pill::before, .craft-pour::before { inset: 2px; background: radial-gradient(circle at 50% 40%, #26221d, #0b0a09 80%); }
/* Рамка у маленьких ромбиков — обычная линия, не кованая: кованая в 22 px не читается (автор, 13.10.2026). */
.craft-node-pill::after, .craft-pour::after { border: 1px solid #7a6540; box-shadow: 0 0 2px rgba(0, 0, 0, .9); filter: var(--frame); }
/* Имя — ниже ромбиков уровня и «Качать» в нижних углах: они на 57px от верха ячейки, имя — с 72px (автор: налезали). */
.craft-node-name { margin-top: 10px; font-size: 12px; line-height: 1.15; color: var(--heading); }
/* Полоса по краю ромба — до следующей ступени (30, 60, 90, 100), начинается с верхнего угла и идёт по часовой. */
.craft-ring { position: absolute; left: 50%; top: 50%; width: 70px; height: 70px; transform: translate(-50%, -50%); z-index: 1;
    overflow: visible; pointer-events: none; }
.craft-ring path { fill: none; stroke-width: 3.2; stroke-linejoin: round; }
.craft-ring .track { stroke: rgba(0, 0, 0, .55); }
.craft-ring .fill { stroke: #e0a53a; filter: drop-shadow(0 0 2px rgba(200, 134, 42, .9)); transition: stroke-dasharray 400ms; }
/* Цвета ступеней у ромбов ремёсел сняты (автор, 13.10.2026): ромб один для всех уровней; о ступени говорят полоса по краю и нить. */
/* Закрыто: предыдущее в цепочке не начато — серым и неактивным (автор, 13.10.2026). */
.craft-node.craft-locked .craft-node-level { filter: grayscale(1) brightness(.7); }
.craft-node.craft-locked .craft-node-name { color: var(--text-muted); }
/* Ремесло без дела (Фермер, Лекарь…): стоит в дереве цветным, как открытое, — серым оно читалось как закрытое
   (автор, 13.10.2026); о пустоте говорят только курсив имени и подсказка. */
.craft-node.craft-idle .craft-node-name { font-style: italic; }
/* Семь цепочек вразбежку (набросок автора, 13.10.2026): чётные строки сдвинуты на полшага ромба и подтянуты
   вверх — ромбы ложатся решёткой, нити остаются горизонтальными. */
.craft-chains { gap: 0; }
.craft-chain + .craft-chain { margin-top: -6px; } /* чуть воздуха (автор, 13.10.2026), но без прокрутки */
.craft-chain:nth-child(even) { margin-left: 56px; }
/* Ячейка: ромб с именем, кнопка «Качать» — ромбиком у правого нижнего угла (зеркально пилюле уровня).
   Ширина ячейки задана жёстко, как и нити, — иначе столбцы плыли бы от длины имени (автор, 13.10.2026). */
.craft-cell { position: relative; display: grid; justify-items: center; flex: 0 0 112px; width: 112px; }
.craft-cell .craft-node-name { max-width: 112px; }
/* Кнопка «Качать» — такой же ромбик справа (ячейка — position: relative, ромб в ней с полем 9px сверху, его центр на 31px). */
.craft-pour { left: calc(50% + 27px); top: 57px; z-index: 3; color: #c9a96a; cursor: pointer; }
.craft-pour > span { font-size: 9px; line-height: 1; }
.craft-pour:hover:not(:disabled) { color: var(--gold-hover); --frame: sepia(1) saturate(2) hue-rotate(-12deg) brightness(1.15); }
.craft-pour:disabled { cursor: default; color: #5a554d; --frame: grayscale(1) brightness(.6); }
.craft-pour.on { color: #ffd36a; --frame: sepia(1) saturate(2.6) hue-rotate(-12deg) brightness(1.25) drop-shadow(0 0 4px rgba(224, 165, 58, .9)); }
.craft-pour.on::before { background: radial-gradient(circle at 50% 40%, #5a4320, #1c150c 85%); }
/* Запас опыта (автор, 13.10.2026): полоса на сутки переливания, тлеющее золото, как у полосы уровня. */
.craft-pool { margin-bottom: 10px; }
.craft-pool-time { color: var(--heading); font: 600 12px var(--font-body); font-variant-numeric: tabular-nums; }
.craft-pool-bar { position: relative; height: 10px; border-radius: 5px; background: #16130f; box-shadow: inset 0 1px 3px rgba(0, 0, 0, .8); overflow: hidden; }
.craft-pool-bar > span { position: absolute; inset: 0 auto 0 0; border-radius: 5px; background: linear-gradient(90deg, #5a3a10, #e0a53a); box-shadow: 0 0 6px #c8862a; transition: width 600ms; }
.craft-pool-note { margin: 6px 0 0; }
/* Куда качается — полоса ромба горит ярче. */
.craft-node.pouring .craft-ring .fill { stroke: #ffd36a; filter: drop-shadow(0 0 4px rgba(255, 200, 80, 1)); }
.craft-link { flex: 0 0 136px; width: 136px; height: 2px; margin: 30px -26px 0; background: #333; border-radius: 1px; }
/* Нить: тусклая, пока предыдущее ниже 30; тлеет с 30 (следующее открыто до 29,9); горит с 60 (качай хоть до ста). */
/* Свечение ровное по всей длине (автор, 13.10.2026), без градиента. */
.craft-link.lit { background: #8a6224; }
.craft-link.open { background: #e0a53a; box-shadow: 0 0 6px #c8862a; }
/* Страница одного ремесла: ромб крупно, что даёт, рецепты по ступеням. */
.craft-back { flex: none; width: auto; align-self: flex-start; margin-bottom: 12px; padding: 0 14px; }
.craft-page-head { flex: none; margin-bottom: 16px; }
.craft-page-title { display: flex; flex-wrap: wrap; align-items: flex-start; justify-content: space-between; gap: 8px 16px; }
.craft-train { display: grid; justify-items: start; gap: 4px; }
.craft-train-button { width: auto; min-width: 112px; height: 30px; padding: 0 12px; }
.craft-train-button.on { border-color: var(--gold); color: var(--gold-hover); }
.craft-big { flex: 0 0 auto; cursor: default; }
.craft-big .craft-node-level { transform: scale(1.6); margin: 32px; }
/* Цепочка ступеней на странице ремесла: крупный ромб → линия → ромбики 30, 60, 90, 100. */
.craft-track { display: flex; align-items: center; margin: 0 0 6px; }
/* Узлы — над линией: линия уходит под полосу прогресса ромба, а не ложится поверх. */
.craft-track .craft-big, .craft-step-node { position: relative; z-index: 1; }
.craft-track .craft-big { cursor: pointer; }
.craft-track .craft-big .craft-node-level { margin: 32px 28px 32px 32px; }
.craft-track-link { position: relative; flex: 1 1 40px; max-width: 96px; min-width: 18px; height: 3px; margin: 0 -2px; background: #2e2b28; border-radius: 2px; overflow: hidden; }
.craft-track-link > span { position: absolute; inset: 0 auto 0 0; background: linear-gradient(90deg, #5a3a10, #e0a53a); box-shadow: 0 0 6px #c8862a; }
.craft-step-node { flex: 0 0 auto; padding: 9px 5px; border: 0; background: none; cursor: pointer; transition: transform 120ms; }
.craft-step-node.picked { transform: scale(1.22); }
.craft-step-node:focus-visible { outline: none; }
.craft-step-diamond { position: relative; z-index: 0; width: 26px; height: 26px; display: grid; place-items: center;
    font: 600 10px/1 var(--font-body); font-variant-numeric: tabular-nums; color: #8a847c; --frame: grayscale(1) brightness(.7); }
.craft-step-diamond::before, .craft-step-diamond::after { content: ""; position: absolute; inset: 0; z-index: -1; transform: rotate(45deg); pointer-events: none; }
.craft-step-diamond::before { inset: 2px; background: radial-gradient(circle at 50% 40%, #26221d, #0b0a09 80%); }
.craft-step-diamond::after { border: 6px solid transparent; border-image: var(--art-action-frame) 18% / 6px stretch; filter: var(--frame); }
/* Взятая ступень — без цветов полос (автор, 10.10.2026): рамка светится золотом, фон с числом чуть залит золотом. */
.craft-step-node.reached .craft-step-diamond { color: #f2d48a;
    --frame: sepia(1) saturate(2.6) hue-rotate(-12deg) brightness(1.2) drop-shadow(0 0 3px rgba(217, 164, 65, .7)); }
.craft-step-node.reached .craft-step-diamond::before { background: radial-gradient(circle at 50% 40%, #5a4320, #1c150c 85%); }
.craft-step-node:hover .craft-step-diamond, .craft-step-node.picked .craft-step-diamond { color: #fff; }
.craft-track.craft-locked .craft-node-level { filter: grayscale(1) brightness(.7); }
.craft-page-text h3 { margin: 0 0 4px; font: 600 18px var(--font-title); color: var(--heading); }
.craft-page-text p { margin: 6px 0 0; }
.craft-held-note { color: #c98a5a; }
/* Что открывает выбранный узел цепочки: рецепты значком и названием; ещё не взятое — бледнее. */
.craft-picked { display: flex; flex-direction: column; gap: 6px; }
.craft-picked p { margin: 0; }
/* Колонкой, значок как в сумах; под названием мелкие значки — инструмент и материалы с количеством (16.10.2026). */
.craft-recipes { display: flex; flex-direction: column; gap: 6px; }
.craft-recipe { display: flex; align-items: center; gap: 10px; font-size: 13px; color: var(--text); }
.craft-recipe > .entity-icon { width: 50px; height: 50px; margin: 0; flex: none; }
.craft-recipe-text { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.craft-needs { display: flex; flex-wrap: wrap; align-items: center; gap: 3px 10px; font-size: 12px; color: var(--text-muted); }
.craft-need { display: inline-flex; align-items: center; gap: 4px; }
.craft-need .entity-icon { width: 22px; height: 22px; margin: 0; border-radius: 3px; background-size: 86%; }
.craft-need.tool .entity-icon { box-shadow: inset 0 0 0 1px #6b5328; }
.craft-need.station .entity-icon { width: 22px; height: 22px; margin: 0; border-radius: 3px; background-size: 92% auto, cover; }
/* Где и чем — слева, из чего — справа, между ними тонкая черта. */
.craft-need-sep { align-self: stretch; width: 1px; min-height: 18px; background: #4a4036; }
.craft-remelt { display: grid; place-items: center; color: var(--gold); }
.craft-picked.ahead .craft-recipes { opacity: .55; }
.craft-bar { grid-column: 1 / -1; position: relative; height: 4px; background: #0e0e0e; border: 1px solid #323232; border-radius: 3px; overflow: hidden; }
.craft-bar > span { position: absolute; inset: 0 auto 0 0; width: var(--fill); background: linear-gradient(90deg, #5a3a10, #e0a53a); box-shadow: 0 0 6px #c8862a; transition: width 400ms; }
.placeholder { border: 1px dashed #3a3633; border-radius: 6px; padding: 18px; color: var(--text-muted); text-align: center; font-size: 13px; }

/* ---------- Карта ---------- */
.view-body:has(.hex-map) { align-items: stretch; overflow: hidden; }
.hex-map { flex: 1; display: flex; flex-direction: column; min-height: 0; }
.hex-map svg { flex: 1; min-height: 0; width: 100%; user-select: none; }
/* Карта с облаками над ней (см. HexMap). Плитка теней 800 × 560 (tools/cloud-shadows.py); слой
   больше окна на плитку с каждой стороны и за 90 с сдвигается ровно на плитку — по ветру 35°,
   как волна на воде, — поэтому петля бесшовная. Двигается только transform: карту не перерисовывает. */
.map-view { flex: 1; min-height: 0; position: relative; overflow: clip; display: flex; }
/* Слои карты (HexMap, 02.11.2026): земля, дымка, существа, свой конь, стрелки — стопкой, каждый во всю карту и с тем
   же viewBox. Едут HTML-слои, а не группы SVG: их transform браузер ведёт сам, и фризы главного потока ход не держат.
   --u — сколько пикселей в единице карты: SVG вписан по меньшей стороне (meet), поэтому min по ширине и высоте
   (размер карты — из контейнерных единиц, viewBox — из --vbw/--vbh от HexMap);
   начало координат (там конь) — посередине по ширине и на --vboy единиц ниже середины по высоте. */
.map-view { container-type: size; --u: min(calc(100cqw / var(--vbw)), calc(100cqh / var(--vbh))); }
.map-world { position: absolute; inset: 0; transform: scale(var(--map-zoom, 1));
    transform-origin: 50% calc(50% + var(--vboy) * var(--u)); transition: transform .15s ease-out; }
.map-loading { display: none; }
.map-view:has(> .map-world > .map-ground > .phaser-ground[data-renderer-pending]) > .map-world {
    visibility: hidden;
    opacity: 0;
}
.map-view:has(> .map-world > .map-ground > .phaser-ground[data-renderer-pending]) > .map-loading {
    position: absolute;
    inset: 0;
    z-index: 1;
    display: grid;
    place-items: center;
}
.map-layer { position: absolute; inset: 0; }
.map-ground, .map-things, .pin-slide { will-change: transform; }
/* Без обрезки по рамке SVG: слой едет целиком, и запасное кольцо плит за ней должно заехать в окно. С обрезкой у
   переднего края на шаг открывалась пустая полоса — новые гексы мигали чёрным (игрок, 02.11.2026). Окно карты
   обрезает .map-view. */
.hex-map .map-layer > svg, .hex-map svg.map-layer { display: block; width: 100%; height: 100%; overflow: visible; }
.map-layer:not(.map-ground) { pointer-events: none; }
.phaser-ground {
    position: absolute;
    pointer-events: none;
    visibility: hidden;
    left: calc(50% + var(--u) * var(--phaser-left, 0));
    top: calc(50% + var(--u) * (var(--vboy) + var(--phaser-top, 0)));
    width: calc(var(--u) * var(--phaser-width, 0));
    height: calc(var(--u) * var(--phaser-height, 0));
}
.phaser-ground[data-renderer="phaser"] { visibility: visible; }
.phaser-ground > canvas { display: block; width: 100% !important; height: 100% !important; }
.map-ground > .phaser-ground {
    left: calc(50% + var(--u) * (var(--phaser-left, 0) - var(--map-origin-x)));
    top: calc(50% + var(--u) * (var(--vboy) + var(--phaser-top, 0) - var(--map-origin-y)));
}
.map-ground:has(> .phaser-ground) > svg { position: relative; }
.map-ground:has(> .phaser-ground[data-renderer="phaser"]) > svg .terrain-sprite { visibility: hidden; }
.map-things > .map-paths { position: absolute; inset: 0; }
.map-things > .map-marks { position: relative; }
.map-things:has(> .phaser-ground) { isolation: isolate; }
.map-things:has(> .phaser-ground) > .map-marks { z-index: 1; }
.map-things:has(> .phaser-ground) > .map-movers { z-index: 2; }
.map-things:has(> .phaser-ground[data-renderer="phaser"]) .phaser-mark { visibility: hidden; }
.phaser-map-actor:not([data-renderer="phaser"]),
.phaser-map-actor-source:has(+ .phaser-map-actor[data-renderer="phaser"]) { visibility: hidden; }
.map-clouds {
    position: absolute;
    inset: -560px -800px;
    background: var(--clouds) repeat;
    background-size: 800px 560px;
    pointer-events: none;
    will-change: transform;
    animation: map-clouds 90s linear infinite;
}
@keyframes map-clouds { from { transform: translate3d(0, 0, 0); } to { transform: translate3d(800px, 560px, 0); } }
@media (prefers-reduced-motion: reduce) { .map-clouds { animation: none; } }
/* Движение выключено в настройках — тени стоят на месте. */
.map-clouds.still { animation: none; }
.map-status { flex-shrink: 0; min-height: 26px; display: flex; flex-wrap: wrap; gap: 4px 10px; align-items: center; justify-content: center; padding: 2px 8px; }
.map-target-hit { pointer-events: all; cursor: crosshair; fill: transparent; }
.map-target-outline { visibility: hidden; pointer-events: none; fill: #ffd47b; opacity: .28; }
.map-target-hit:has(+ .map-target-outline):focus { outline: none; }
.map-target-outline.selected, .map-target-hit:is(:hover, :focus-visible) + .map-target-outline { visibility: visible; }
.hex-map.aiming-bite :is(.map-view, .hex, .map-target-hit) { cursor: crosshair; }
.map-target-hit:focus-visible + .map-target-outline,
.hex-map.aiming-bite .map-target-hit:hover + .map-target-outline {
    opacity: .45;
}
.map-target-status { display: inline-flex; align-items: center; gap: 6px; }
.map-target-status meter { width: 70px; }
.map-status .combat-status { color: #d98a6a; }
.map-status .route-stop { margin-left: 10px; padding: 2px 10px; background: rgba(0, 0, 0, .35); border: 1px solid var(--border-hover); border-radius: 5px; color: var(--text-1); font: 600 12px var(--font-body); cursor: pointer; }
.map-status .route-stop:hover { color: var(--heading); border-color: var(--gold-hover); }
/* Стрелки приближаются вместе с миром; указатели у края компенсируют масштаб.
   У облаков transform занят ветром, поэтому им — свойство scale вокруг середины. */
@property --pin-zoom { syntax: '<number>'; inherits: true; initial-value: 1; }
.map-pins { --pin-zoom: var(--map-zoom, 1); transition: --pin-zoom .15s ease-out; }
.map-clouds { scale: var(--map-zoom, 1); transition: scale .15s ease-out; }
.map-zoom-buttons { position: absolute; right: 8px; bottom: 8px; z-index: 2; display: flex; flex-direction: column; gap: 3px; }
.map-zoom-buttons button { width: 26px; height: 26px; padding: 0; border: 1px solid #5a3a10; border-radius: 5px; background: rgba(12, 10, 8, .78);
    color: #d9c8a4; font: 600 17px/1 var(--font-body); cursor: pointer; }
.map-zoom-buttons button:hover { color: #f3e8d0; border-color: var(--gold); }
/* Кнопки рамок над приближением: горит — рамки показаны, погасла — скрыты (см. HexMap). */
.map-frame-buttons { display: flex; flex-direction: column; gap: 3px; margin-bottom: 9px; }
.map-frame-buttons button { display: grid; place-items: center; color: #6a5d48; }
.map-frame-buttons button.on { color: #e8c877; border-color: #8a6420; }
.map-frame-buttons svg { width: 16px; height: 16px; fill: none; stroke: currentColor; stroke-width: 1.4; stroke-linejoin: round; stroke-linecap: round; }
html[data-map-zoom-edge="max"] .map-zoom-buttons [data-map-zoom="in"],
html[data-map-zoom-edge="min"] .map-zoom-buttons [data-map-zoom="out"] { opacity: .35; pointer-events: none; }
/* Полоски ступеней между «+» и «−»: горящая — та, где карта сейчас; клик по полоске — на неё. */
.map-zoom-buttons .zoom-levels { box-sizing: border-box; width: 26px; display: flex; flex-direction: column; padding: 2px 0;
    border: 1px solid #5a3a10; border-radius: 5px; background: rgba(12, 10, 8, .78); }
.zoom-levels button { position: relative; width: 100%; height: 7px; padding: 0; border: 0; background: none; cursor: pointer; }
.zoom-levels button::after { content: ""; position: absolute; left: 6px; right: 6px; top: 2px; height: 3px; border-radius: 2px; background: #3e3426; }
.zoom-levels button:hover::after { background: #7a5c30; }
html[data-map-zoom-level="0"] .zoom-levels [data-zoom-level="0"]::after,
html[data-map-zoom-level="1"] .zoom-levels [data-zoom-level="1"]::after,
html[data-map-zoom-level="2"] .zoom-levels [data-zoom-level="2"]::after,
html[data-map-zoom-level="3"] .zoom-levels [data-zoom-level="3"]::after,
html[data-map-zoom-level="4"] .zoom-levels [data-zoom-level="4"]::after,
html[data-map-zoom-level="5"] .zoom-levels [data-zoom-level="5"]::after {
    background: #e0a53a; box-shadow: 0 0 4px #c8862a; }

.hex { transition: opacity 300ms; }
.hex.far { opacity: .45; }
.hex .top { stroke: rgba(0, 0, 0, .55); stroke-width: 1; transition: filter 120ms; }
.hex .side { stroke: rgba(0, 0, 0, .7); stroke-width: 1; }
.hex .deco { fill: none; stroke-width: 1.4; stroke-linecap: round; stroke-linejoin: round; pointer-events: none; }
/* Нарисованная плитка (art/terrain, см. HexMap): картинка сама по себе мышь не ловит — деревья
   выступают на соседей сверху, — а ловит прозрачный верх гекса поверх неё; на нём же и рамки.
   Правило стоит до рамок «ты здесь», «можно идти»: они его перекрывают. */
.hex.art image { pointer-events: none; }
.hex.art .terrain-sprite { pointer-events: none; }
.hex.art .top { fill: transparent; stroke: none; }
/* Сетки на рисованной карте нет (автор, 22.10.2026): земля сплошная, контура в плитках тоже нет
   (tools/strip-outline.py). Гекс видно по рамкам — «ты здесь», «можно идти», под курсором. */
/* Гекс под курсором светлеет накладкой на его верх, а не фильтром по картинке (04.10.2026): фильтр
   на время перехода уводил плитку в отдельный слой — она на миг становилась резче соседних, а
   потом снова чуть расплывалась. Накладка рисуется тем же путём, что и всё остальное. */
.hex.art.reachable:hover .top { fill: rgba(255, 232, 180, .13); }
/* Живая вода (см. HexMap.Swell): соседний вариант воды поверх плитки проявляется и гаснет. */
.hex .swell { opacity: 0; animation: swell 6s ease-in-out infinite alternate; }
@keyframes swell { from { opacity: 0; } to { opacity: .85; } }
@media (prefers-reduced-motion: reduce) { .hex .swell { animation: none; } }
/* Дорога линией (см. HexMap.RoadLinePath): на простой карте и там, где рисунка под форму нет. */
.hex .road-line { fill: none; stroke: #b39a6c; stroke-width: 3.2; stroke-linecap: round; stroke-linejoin: round; opacity: .8; pointer-events: none; }
/* Река линией (21.10.2026): на простой карте и где рисунка русла под форму нет. */
.hex .river-line { fill: none; stroke: #4f86ab; stroke-width: 4.5; stroke-linecap: round; stroke-linejoin: round; opacity: .85; pointer-events: none; }
/* Дымка к краям обзора (см. HexMap): только картинка — клики и подсказки проходят к гексам под ней. */
.map-haze { pointer-events: none; }
/* Земля наделов (docs/HOMESTEAD.md, шаг 4, автор): светится сама земля — слой над плитками, под деревьями и стелами.
   Внутри едва видно, гуще к краю: три полосы вдоль кромки, обрезанные по земле; поверх — кайма. */
.land { --land: 160, 225, 130; }
.land.guest { --land: 125, 185, 235; }
.land.foe { --land: 200, 95, 82; }
.land-fill { fill: rgba(var(--land), .05); stroke: none; }
.land-glow { fill: none; stroke: rgb(var(--land)); stroke-linecap: round; stroke-linejoin: round; }
.land-glow.wide { stroke-width: 18px; opacity: .06; }
.land-glow.mid { stroke-width: 10px; opacity: .08; }
.land-glow.near { stroke-width: 4px; opacity: .12; }
.land-edge { fill: none; stroke: rgba(var(--land), .8); stroke-width: 1.3px; stroke-linecap: round; }
/* «Ты здесь» (автор, 24.10.2026): едва видный свет на земле, без рамки; на простой карте — тонкая тусклая кайма. */
.hex.art.current .top { fill: url(#here-glow); }
.hex:not(.art).current .top { stroke: rgba(201, 164, 104, .45); stroke-width: 1; }
.hex.reachable { cursor: pointer; }
/* Дальний гекс, до которого есть путь (Paths): без рамки, но кликабелен и подсвечивается под мышью. */
.hex.walkable { cursor: pointer; }
/* Гекс, на который смотрят издали (правый клик): голубая рамка, как у дружеских меток. */
.hex.watched .top { stroke: #6ec6ff; stroke-width: 2; }
.hex.walkable:hover .top { stroke: rgba(201, 164, 104, .55); stroke-width: 1.5; }
.hex.art.walkable:hover .top { fill: rgba(255, 232, 180, .09); }
.hex:not(.art).walkable:hover .top { filter: brightness(1.2); }
.hex.reachable:hover .top { stroke: var(--gold-hover); stroke-width: 2; }
/* Прицел перелёта грифона (FlightRules): куда можно сесть — золотая рамка, под мышью ярче. */
.hex.fly-target { cursor: crosshair; }
.hex.fly-target .top { stroke: rgba(201, 164, 104, .7); stroke-width: 1.5; }
.hex.fly-target:hover .top { stroke: var(--gold-hover); stroke-width: 2.5; filter: brightness(1.25); }
.hex:not(.art).reachable:hover .top { filter: brightness(1.35); }
/* Рамки можно погасить кнопками на карте. «Ты здесь» и гекс под курсором остаются: без них
   не понять, где стоишь и куда шагнёшь. Погасив «можно идти», на безопасных гексах возвращаем
   рамку зоны — если её саму не погасили. */
.hex.blocked { cursor: not-allowed; }

.t-city :is(.top, .terrain-base) { fill: #6b5a44; } .t-city .side { fill: #3a3024; } .t-city .deco { stroke: #d6b169; fill: rgba(40, 30, 18, .8); }
.t-field :is(.top, .terrain-base) { fill: #4f5a2e; } .t-field .side { fill: #2c3418; }
.t-desert :is(.top, .terrain-base) { fill: #8a7658; } .t-desert .side { fill: #4a3f2e; }
.t-forest :is(.top, .terrain-base) { fill: #2f4526; } .t-forest .side { fill: #1a2716; } .t-forest .deco { fill: #182615; stroke: #0f190d; stroke-width: 1; }
.t-hills :is(.top, .terrain-base) { fill: #6a6150; } .t-hills .side { fill: #3e382d; } .t-hills .deco { stroke: #8f846c; }
.t-swamp :is(.top, .terrain-base) { fill: #3b4838; } .t-swamp .side { fill: #222a21; } .t-swamp .deco { stroke: #6f7e52; }
.t-water :is(.top, .terrain-base) { fill: #1f3a4f; } .t-water .side { fill: #12222e; } .t-water .deco { stroke: #4d7a96; }

/* Путь (автор, 24.10.2026, вариант А): бусины с тёмной подложкой — читаются и на светлой траве; огонёк бежит к цели.
   У цели — два круга на земле, третий расходится, и точка. */
.route-bead { fill: var(--gold-hover); animation: route-bead 2.4s linear infinite; }
.route-bead-shadow { fill: rgba(10, 6, 2, .6); }
@keyframes route-bead { 0% { opacity: .6; } 12% { opacity: 1; fill: #ffe2a0; } 30%, 100% { opacity: .6; } }
.route-goal-halo { fill: none; stroke: rgba(18, 12, 4, .55); stroke-width: 2; }
.route-goal-ring { fill: none; stroke: #e8c27a; stroke-width: .9; }
.route-goal-ripple { fill: none; stroke: var(--gold-hover); stroke-width: .9; transform-box: fill-box; transform-origin: center; animation: route-ripple 2.6s ease-out infinite; }
@keyframes route-ripple { from { transform: scale(.25); opacity: .95; } to { transform: scale(1); opacity: 0; } }
.route-goal-dot { fill: #ffe2a0; }
@media (prefers-reduced-motion: reduce) { .route-bead, .route-goal-ripple { animation: none; } }

/* Чутьё (25.10.2026, «как в „Ведьмаке“»): светящиеся отпечатки к зверю — тёплые у мирного, красные у нападающего;
   отсвет бежит по следу от коня. */
.trail use { fill: #ffcf8a; filter: drop-shadow(0 0 1.2px rgba(255, 170, 70, .9)); animation: trail-glow 2.6s ease-in-out infinite; }
.trail.hostile use { fill: #ff6a52; filter: drop-shadow(0 0 1.4px rgba(255, 60, 40, .95)); }
@keyframes trail-glow { 0%, 100% { fill-opacity: .55; } 18% { fill-opacity: 1; } 40% { fill-opacity: .55; } }
@media (prefers-reduced-motion: reduce) { .trail use { animation: none; } }

/* Метки зверей: силуэт вида без подложки, обводка прямо по контуру. Толщины — в точках
   силуэта (64 на высоту), а не карты: путь масштабируется вместе с ним. Обводка под заливкой
   (paint-order), поэтому видна только её внешняя половина и тонкие рога не заплывают. */
.mark use { fill: var(--mark, #4a4540); stroke: #e8dcc0; stroke-width: 9; stroke-linejoin: round; paint-order: stroke; }
.mark text { fill: #f3e8d0; font: 700 7px var(--font-body); text-anchor: middle; letter-spacing: 0.2px;
    paint-order: stroke; stroke: #141210; stroke-width: 2px; }
/* Заливка — цвет вида, тот же, что был у плашки: цвет узнаётся раньше силуэта. Тёмные
   виды на тёмной земле держит светлая обводка. */
.mark.k-dog { --mark: #6b6258; }
.mark.k-boar { --mark: #5e4228; }
.mark.k-bear { --mark: #3d2a1c; }
.mark.k-deer { --mark: #9a6a2e; }
.mark.k-traveller { --mark: #3f5a6e; }
.mark.k-bandit { --mark: #5a3d5e; }
/* Другие игроки — тот же силуэт: друг голубой с голубой же обводкой, посторонний — светлый с
   тёмной обводкой, наоборот против зверей (тёмная заливка, светлая кайма): с волком не спутать.
   Заливка постороннего — его размер против тебя: оранжевый может тебя проглотить, зелёного
   можешь проглотить ты (автор, 05.10.2026). Красный и золотой заняты: злой зверь, ты и босс. */
.mark.k-horse { --mark: #77736c; }
.mark.k-horse.stranger { --mark: #d9d2c2; }
.mark.k-horse.stranger use { stroke: #1c1814; stroke-width: 7; }
.mark.k-horse.stranger.threat { --mark: #f08a3c; }
.mark.k-horse.stranger.prey { --mark: #3fb894; }
.mark.k-horse.friend { --mark: #3f8ac0; }
.mark.k-horse.friend use { stroke: #c9e8fa; }
/* Разыскиваемый (docs/PVP.md) — красной обводкой, как зверь, что нападает сам; поверх обводки постороннего. */
.mark.k-horse.wanted use { stroke: #e0483a; stroke-width: 12; }
/* Идёт за тобой с нападением на уме (намерение, 20.10.2026): красная обводка, как у зверя, что нападает сам. */
.mark.k-horse.hunting use { stroke: #e0483a; stroke-width: 12; }
/* Карма от 50 (docs/PVP.md, 20.10.2026) — голубой обводкой; розыск её перебивает. */
.mark.k-horse.blessed use { stroke: #6ec6ff; stroke-width: 12; }
/* Свой конь: заливка — масть персонажа, обводка золотая, как была у кружка: по ней себя и находят. */
.mark.k-horse.self use { stroke: var(--gold); stroke-width: 10; }
/* Идёт влево — разворачивается влево: силуэт снят с портрета, а там конь смотрит вправо. Зеркалим
   обёртку вокруг начала координат — середины гекса; зеркало самого <use> «от своей рамки» уводило
   коня вбок (05.10.2026). */
.mark.k-horse.self .turn { transition: transform .25s ease; }
.mark.k-horse.self.left .turn { transform: scaleX(-1); }
/* Красная обводка — нападает сам. Предупреждение до того, как шагнёшь. */
.mark.hostile use { stroke: #e0483a; stroke-width: 12; }
/* Босс: золотая обводка потолще — его ищут нарочно, и видно его раньше прочих. */
.mark.boss use { stroke: var(--gold); stroke-width: 16; }
/* Красная ленточка у корня хвоста чужого коня (docs/PVP.md): ищет поединка. Малиновая, а не огненная, как «!» и
   обводка розыска; тёмная кайма — иначе на заливке силуэта ленточку не видно. */
.mark .ribbon-mark path { fill: none; stroke: #e0344a; stroke-width: 3.2; stroke-linecap: round; stroke-linejoin: round; }
.mark .ribbon-mark { filter: drop-shadow(0 0 1px #141210) drop-shadow(0 0 1px #141210); }
.mark .karma-flag { font: 800 10px var(--font-body); text-anchor: middle; paint-order: stroke; stroke: #141210; stroke-width: 2.4px; }
.mark .karma-flag.pale { fill: #e6a59c; }
.mark .karma-flag.red, .mark .karma-flag.enemy { fill: #ff4a3d; filter: drop-shadow(0 0 2px rgba(255, 60, 40, .95)); }
/* Звери из отслеживаемого задания — золотой «!» в верхнем левом углу силуэта. Тёмная обводка
   под знаком держит его на любой земле и на светлой обводке силуэта. */
.mark .quest-flag { fill: #f0b440; font: 800 9px var(--font-body); text-anchor: middle; stroke: #141210; stroke-width: 2.4px; }

/* Отслеживаемое чудовище: кольцо на логове в обзоре, стрелка к нему у края. */
/* В обзоре стрелка привязана к земле, у края её положение и размер не зависят от зума.
   map-motion.js смешивает оба положения в одной системе координат. */
.map-pointer .pin-position { transform: translate(
    calc(var(--pin-x, 0px) + var(--pin-edge-x, 0px) / var(--pin-zoom)),
    calc(var(--pin-y, 0px) + var(--pin-edge-y, 0px) / var(--pin-zoom))); }
.map-pointer .pin-size { transform: scale(calc(1 - var(--pin-edge, 0) + var(--pin-edge, 0) / var(--pin-zoom))); }
.map-pointer polygon { transform: rotate(var(--pin-angle, 90deg)); }
.map-pointer .bob { animation: map-pointer 1.6s ease-in-out infinite; }
@keyframes map-pointer { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-2.5px); } }
@media (prefers-reduced-motion: reduce) { .map-pointer .bob { animation: none; } }
/* Клад ничей — рисунок логова (и центр разбитого лагеря) в золотом отсвете. */
.site-art.open { filter: drop-shadow(0 0 2.5px rgba(224, 180, 100, .95)); }
/* Кластер леса на дальнем кольце приглушён, как и его плитка (.hex.far). */
.site-art.far { opacity: .45; }
/* Каркас надела: заложить материалы — окно во всю ширину, строка на материал. */
.homestead-need { display: flex; align-items: center; gap: 10px; padding: 6px 0; }
.homestead-need .entity-info { flex: 1 1 auto; }
.homestead-need.done .text-0 { color: var(--text-2); }
/* Лоток (TrayBox, 30.10.2026): материалы каркаса и монеты подношения — как кормушка, ящик своей высоты: ниже другие окна. */
.storage-box.tray-box { margin: 8px 0; flex: 0 0 auto; }
.storage-box.tray-box .box-content { flex: 0 0 auto; min-height: 96px; max-height: 220px; overflow-y: auto; }
.tray-box .empty-note { display: flex; align-items: center; justify-content: center; text-align: center; min-height: 84px; margin: 6px; border: 1px dashed rgba(201, 164, 104, .4); border-radius: 6px; }
.tray-box.drop-target .empty-note { border-color: var(--gold); }
.tray-box .homestead-needs { padding: 4px 10px; }
/* Ромбы на концах полосы стоят на 15 px за её краем (.forge-bar::before/after): при 10 px они вылезали на рамку окна. */
.tray-box .forge-bar { margin: 8px 24px 0; }
.tray-box .pay-line { margin: 6px 10px 2px; color: var(--heading); font-variant-numeric: tabular-nums; }
.tray-box > .building-actions { padding: 0 10px 10px; box-sizing: border-box; }
/* Права надела (шаг 2): строка — кому, столбцы — строить, собирать, подбирать. */
.land-rights { width: 100%; border-collapse: collapse; }
.land-rights th { font-weight: 400; font-size: .85em; color: var(--text-2); padding: 2px 6px; }
.land-rights td { padding: 4px 6px; text-align: center; border-top: 1px solid rgba(255, 255, 255, .06); }
.land-rights td:first-child { text-align: left; }
/* Постройки под строкой (31.10.2026): нажать на имя — раскрыть; своё у постройки — золотой чертой слева. */
.land-rights-toggle { all: unset; cursor: pointer; display: inline-flex; gap: 4px; align-items: baseline; }
.land-rights-toggle:hover, .land-rights-toggle:focus-visible { color: var(--heading); }
.land-rights-arrow { display: inline-block; width: 1em; color: var(--text-2); }
.land-rights tr.land-building td { border-top: none; padding-top: 2px; padding-bottom: 2px; font-size: .92em; }
.land-rights tr.land-building td:first-child { padding-left: 24px; }
.land-rights tr.land-building.own td:first-child { box-shadow: inset 2px 0 0 #c9a468; }
/* «Построить» на наделе (шаг 5): строений много — окно прокручивается, раздел строений под чертой. */
.modal.build { max-height: 88vh; overflow-y: auto; }
.build-section { margin: 12px 0 6px; padding-top: 8px; border-top: 1px solid rgba(201, 164, 104, .18); font-size: 15px; color: var(--heading); }
/* Друг на карте — голубым: кольцо на гексе с именем над ним, за краем обзора — стрелка с именем. */
.friend-label, .friend-arrow-name { fill: #bfe3f7; font: 700 8px var(--font-body); text-anchor: middle; paint-order: stroke; stroke: #101418; stroke-width: 2.5px; }
.lair-arrow.friend-arrow polygon { fill: #6ab4e8; stroke: #0c2334; filter: drop-shadow(0 0 3px #3a82b8); }
.lair-arrow polygon { fill: #e0a53a; stroke: #3a2508; stroke-width: 1; filter: drop-shadow(0 0 3px #c8862a); }
/* Отслеживаемая метка с карты мира (31.10.2026) — сиреневым, как её флажок там. */
.lair-arrow.mark-arrow polygon { fill: #b994f0; stroke: #1c1228; filter: drop-shadow(0 0 3px #8a64c8); }
.lair-arrow.mark-arrow text.friend-arrow-name { fill: #e2d3fa; }
/* Имя друга мельче числа под стрелкой: имён над одной стрелкой бывает несколько, столбиком. */
.lair-arrow text.friend-arrow-name { font-size: 6.5px; stroke-width: 2px; }
.lair-arrow text { fill: #f3e8d0; font: 700 9px var(--font-body); text-anchor: middle; paint-order: stroke; stroke: #141210; stroke-width: 2.5px; font-variant-numeric: tabular-nums; }

/* Плашка отслеживаемого в углу карты: имя слева, прогресс справа, ширина не скачет. */
.quest-tracker { position: absolute; top: 8px; left: 8px; z-index: 2; display: flex; flex-direction: column; gap: 3px; min-width: 170px; max-width: 280px;
    padding: 6px 8px; border: 1px solid #5a3a10; border-radius: 6px; background: rgba(12, 10, 8, .78); font-size: 12px; pointer-events: none; }
.quest-tracker > div { display: flex; justify-content: space-between; gap: 10px; }
.quest-tracker span { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: #d9c8a4; }
.quest-tracker strong { flex-shrink: 0; font-variant-numeric: tabular-nums; color: #f3e8d0; }
.quest-tracker .ready strong { color: #e0a53a; }
/* Туша: пунктир и приглушённый цвет — её разделывают, а не боятся. */
.mark.dead use { fill-opacity: 0.45; stroke: #b8a88a; stroke-width: 8; stroke-dasharray: 10 7; }
.mark.dead text { fill: #cfc2a6; }
/* Логово на карте: зев пещеры. Красная кайма — чудовище дома, золотая — клад ничей. */
.lair-mark path { fill: #2a201a; stroke: #b0402e; stroke-width: 1.4; stroke-linejoin: round; }
.lair-mark .hole { fill: #050403; stroke: none; }
/* Костёр на карте: играющий отсвет вокруг огня и дым над ним (автор, 04.10.2026). */
.fire-glow { mix-blend-mode: screen; transform-box: fill-box; transform-origin: center; animation: fire-flicker 7s ease-in-out infinite; }
.fire-glow.woods { animation-name: fire-flicker-woods; animation-duration: 9s; }
.fire-glow.still { animation: none; opacity: .85; }
.fire-glow.woods.still { opacity: .55; }
/* Отсвет дышит медленно и неглубоко: частое мигание резало глаз (автор, 04.10.2026). */
@keyframes fire-flicker {
    0%, 100% { opacity: .8; transform: scale(1); }
    35% { opacity: 1; transform: scale(1.05); }
    70% { opacity: .72; transform: scale(.98); }
}
@keyframes fire-flicker-woods {
    0%, 100% { opacity: .52; transform: scale(1); }
    45% { opacity: .66; transform: scale(1.04); }
}
/* Рисунок костра чуть глуше плиток вокруг: полный контраст выбивался из гекса (автор, 04.10.2026). */
.campfire-art { filter: contrast(.82) saturate(.85) brightness(.95); }
/* Пламя: рисунок разрезан по кромке поленьев — низ стоит, верх колышется вокруг линии разреза. */
.campfire-art .hearth { clip-path: inset(57% 0 0 0); }
.campfire-art .flame { clip-path: inset(0 0 43% 0); transform-box: fill-box; transform-origin: 44% 57%; animation: flame-dance 1.7s ease-in-out infinite; }
@keyframes flame-dance {
    0%, 100% { transform: skewX(0deg) scaleY(1); }
    17% { transform: skewX(-4deg) scaleY(1.07); }
    34% { transform: skewX(2.5deg) scaleY(.96); }
    52% { transform: skewX(-2deg) scaleY(1.1); }
    71% { transform: skewX(4.5deg) scaleY(1.02); }
    86% { transform: skewX(-1.5deg) scaleY(.94); }
}
.fire-smoke circle { fill: #b9b4aa; opacity: 0; filter: blur(1.4px); transform-box: fill-box; transform-origin: center; animation: smoke-rise 6.5s linear infinite; }
.fire-smoke.thin circle { fill: #9a968e; animation-name: smoke-rise-thin; animation-duration: 8s; }
.fire-smoke.still circle { animation: none; opacity: .38; }
.fire-smoke.thin.still circle { opacity: .24; }
@keyframes smoke-rise {
    0% { opacity: 0; transform: translate(0, 0) scale(.5); }
    12% { opacity: .6; }
    60% { opacity: .35; }
    100% { opacity: 0; transform: translate(8px, -30px) scale(2.8); }
}
@keyframes smoke-rise-thin {
    0% { opacity: 0; transform: translate(0, 0) scale(.35); }
    15% { opacity: .38; }
    100% { opacity: 0; transform: translate(5px, -26px) scale(1.5); }
}
/* Простая карта без рисунков: язычок пламени. */
.fire-mark { fill: #f08a24; stroke: #3a1505; stroke-width: 1.2; stroke-linejoin: round; filter: drop-shadow(0 0 3px rgba(240, 138, 36, .8)); }
.lair-mark.open path:not(.hole) { stroke: var(--gold); filter: drop-shadow(0 0 2px rgba(201, 164, 104, .8)); }
/* Числа на карте — тёмной пилюлей, как у значков эффектов (CountPill, 10.10.2026): над стопкой и в мешочке. */
.count-pill rect { fill: rgba(0, 0, 0, .7); }
.count-pill text { fill: #e8e2d6; font: 500 5.6px var(--font-body); text-anchor: middle; font-variant-numeric: tabular-nums; }

/* Кучка на земле: мешочек в нижнем углу гекса, пилюля с числом заполняет его тело (10.10.2026). */
.loot .pack { fill: rgba(28, 22, 15, .55); stroke: rgba(240, 215, 154, .85); stroke-width: 1.8; stroke-linejoin: round; }

/* Печь в поле: её место выводится из координат, сервер о ней не рассказывает. */
.station { fill: none; stroke: #b8935a; stroke-width: 1.5; stroke-linecap: round; stroke-linejoin: round; pointer-events: none; }

/* ---------- Внутри хищника ---------- */
.view-body:has(.inside-screen) { align-items: stretch; }
.inside-screen {
    position: relative;
    flex: 1;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    text-align: center;
    overflow: hidden;
    border-radius: 6px;
    padding: 10px 12px 14px;
    gap: 2px;
    background: radial-gradient(ellipse at center, #3a1414 0%, #1c0808 55%, #0b0404 100%);
}
.inside-glow { position: absolute; inset: 0; background: radial-gradient(circle at 50% 60%, rgba(160, 40, 40, .25), transparent 60%); animation: inside-pulse 2.4s ease-in-out infinite; }
@keyframes inside-pulse { 50% { opacity: .4; } }
@media (prefers-reduced-motion: reduce) { .inside-glow { animation: none; } }
/* Хищник занимает всю свободную высоту: подписи и полосы берут своё, остальное — ему. */
.inside-predator { flex: 1; min-height: 0; width: 100%; display: flex; align-items: stretch; justify-content: center; opacity: .85; position: relative; }
.inside-predator .horse-portrait { height: 100%; width: auto; max-width: 100%; }
.inside-text { max-width: 420px; color: #d9b8a8; font-size: 14px; position: relative; }
.inside-health { width: 340px; max-width: 100%; position: relative; display: grid; grid-template-columns: auto 1fr; gap: 4px 10px; align-items: center; }
.inside-label { text-align: right; font-size: 11px; color: #c9a898; white-space: nowrap; }
/* Сколько массы сбережёшь (или уже отбил) — тёплым золотом, рядом с полосой переваривания. */
.inside-saved { position: relative; font-size: 12px; color: var(--gold); text-shadow: 0 0 2px #000; }
p.inside-saved { margin: 4px 0 0; }
/* Портрет уступает место тексту, но не схлопывается совсем; на маленьком экране — прокрутка. */
.inside-predator { min-height: 56px; }
.inside-screen { overflow-y: auto; justify-content: safe center; }
.inside-screen .figure-name, .inside-screen .text-1 { position: relative; }
/* Канат (docs/VORE-TUG.md): строка той же сетки, что «Переварено» и «Выдержка», в той же рамке
   .bar — чтобы края полос стояли ровно. Внутри кованый желоб, как полоса заставки (app.css
   .splash-bar): золото тянется к хищнику ровно на столько, сколько он заберёт, метка стоит на
   канате, имена сторон — мелко по краям. Оба видят одну и ту же полосу. */
.bar-tug > .tug-track {
    display: block;
    position: relative;
    height: 10px; /* как заливка соседних полос с числом: строки одной высоты */
    border-radius: 2px;
    overflow: visible;
    background: linear-gradient(#050505, #121110);
    box-shadow: inset 0 1px 3px rgba(0, 0, 0, .9);
}
.tug-fill {
    position: absolute;
    top: 0;
    left: 0;
    height: 100%;
    border-radius: 2px 0 0 2px;
    background: linear-gradient(90deg, #4a361e, #8a6a3e 55%, var(--gold-hover));
    box-shadow: 0 0 10px rgba(201, 164, 104, .35);
    transition: width 220ms ease-out;
}
.tug-mark {
    position: absolute;
    top: 50%;
    width: 7px;
    height: 14px;
    border: 1px solid var(--gold-hover);
    border-radius: 2px;
    background: linear-gradient(#f0d9a8, #8a6a3e);
    transform: translate(-50%, -50%);
    box-shadow: 0 0 6px rgba(0, 0, 0, .9), 0 0 8px rgba(201, 164, 104, .5);
    transition: left 220ms ease-out;
}
/* Имена сторон — по краям, тем же шрифтом, что подписи заставки; середина свободна для метки. */
.bar-tug > em {
    display: flex;
    justify-content: space-between;
    padding: 0 8px;
    font: normal 9px/14px var(--font-title);
    letter-spacing: .1em;
    text-transform: lowercase;
    color: #e8cdb8;
    text-shadow: 0 0 3px #000, 0 0 3px #000;
    pointer-events: none;
}
.bar-tug > em > span { max-width: 42%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* На краях: у жертвы отбито всё, что можно, — метка гаснет; хищник дотянул — полоса горит красным. */
.tug-won .tug-mark { background: linear-gradient(#bfae8c, #5a4a30); box-shadow: 0 0 6px rgba(0, 0, 0, .9); }
.tug-lost .tug-fill { background: linear-gradient(90deg, #4a1010, #a02020 55%, #ec3b3b); box-shadow: 0 0 10px rgba(236, 59, 59, .4); }
@media (prefers-reduced-motion: reduce) { .tug-fill, .tug-mark { transition: none; } }
/* Подсказка под полосами — не шире текста над ними. */
.inside-hint { max-width: 420px; position: relative; }

/* Взгляд хищника на свою добычу: та же подложка, другой смысл. */
.prey-screen .inside-predator { opacity: .95; }
.prey-screen .inside-text span { display: block; color: #e8cdb8; margin-bottom: 4px; }
.prey-item { height: 60%; aspect-ratio: 1; align-self: center; background: var(--art) center / contain no-repeat; }

/* Окно смерти: та же багровая подсветка, что внутри хищника, — продолжение той же сцены. */
.death-backdrop { background: radial-gradient(circle at 50% 45%, rgba(90, 16, 14, .55), rgba(0, 0, 0, .88) 70%); }
.death-modal { text-align: center; border-color: var(--danger); }
.death-modal h2 { color: var(--danger-text); }
.death-modal .death-by { font-size: 16px; color: #e8cdb8; }
.death-modal .actions { justify-content: center; }
/* Пока погибший не возродился, обзора и действий нет: прежняя сцена с убийцей — уже не его. */
.game-col.center.dead > .view-panel,
.game-col.center.dead > .action-surface { visibility: hidden; }

.self-marker { position: absolute; left: 50%; top: calc(50% + (var(--vboy) - var(--self-elevation)) * var(--u)); }
.self-marker .shadow { fill: rgba(0, 0, 0, .6); }
.self-gait { width: var(--u); height: var(--u); }
.self-climb.walking:not([data-gait]) .self-gait,
.self-climb[data-gait="walking"] .self-gait { animation: self-gait .55s ease-in-out infinite; }
@keyframes self-gait { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(calc(-2 * var(--u))); } }
.self-marker .pulse { fill: none; stroke: var(--gold); stroke-width: 1.5; opacity: .6; animation: map-pulse 1.8s ease-out infinite; transform-box: fill-box; transform-origin: center; }
@keyframes map-pulse { from { transform: scale(.7); opacity: .8; } to { transform: scale(1.6); opacity: 0; } }

/* Переход между гексами: карта едет под конём, конь взбирается на новую плиту и покачивается.
   Имена «a» и «b» через шаг: на маршруте класс walking между шагами не снимается, а одноимённую
   анимацию браузер не перезапускает (как lunge-a / lunge-b у сцены боя). Сдвиг — в единицах карты (--dx, --dy),
   в пиксели его переводит --u: так слой едет transform'ом HTML, который ведёт компоновщик. Начинает с --sx, --sy —
   недоеханного прошлым шагом маршрута (HexMap.OnParametersSet), а не с нуля: иначе остаток проскакивал бы разом. */
:is(.map-ground, .map-things, .pin-slide):is(.walking, .settling).a { animation: map-walk-a var(--dur) linear both; }
:is(.map-ground, .map-things, .pin-slide):is(.walking, .settling).b { animation: map-walk-b var(--dur) linear both; }
@keyframes map-walk-a { from { transform: translate(calc(var(--camera-from-x, var(--sx, 0)) * var(--u)), calc(var(--camera-from-y, var(--sy, 0)) * var(--u))); } to { transform: translate(calc(var(--dx) * var(--u)), calc(var(--dy) * var(--u))); } }
@keyframes map-walk-b { from { transform: translate(calc(var(--camera-from-x, var(--sx, 0)) * var(--u)), calc(var(--camera-from-y, var(--sy, 0)) * var(--u))); } to { transform: translate(calc(var(--dx) * var(--u)), calc(var(--dy) * var(--u))); } }

.self-climb:is(.walking, .settling).a { animation: self-climb-a var(--dur) linear both; }
.self-climb:is(.walking, .settling).b { animation: self-climb-b var(--dur) linear both; }
@keyframes self-climb-a { from { transform: translate(0, calc(var(--camera-from-climb, var(--sh, 0)) * var(--u))); } to { transform: translate(0, calc(var(--dh) * var(--u))); } }
@keyframes self-climb-b { from { transform: translate(0, calc(var(--camera-from-climb, var(--sh, 0)) * var(--u))); } to { transform: translate(0, calc(var(--dh) * var(--u))); } }

.self-bob.walking { animation: self-bob .55s ease-in-out infinite; }
@keyframes self-bob { 0%, 100% { transform: translate(0, 0); } 50% { transform: translate(0, -2px); } }

.map-movers { position: absolute; inset: 0; }
/* Чужой конь в пути едет с гекса на гекс той же прямой, что карта под своим, и замирает на месте до новой карты.
   Свой HTML-элемент нулевого размера в точке, откуда идёт (--x, --y от коня, в единицах карты); силуэт — SVG, где
   единица карты — 1 (viewBox 0 0 1 1, рисунок вылезает за рамку). */
.hex-map .mover { position: absolute; left: 50%; top: calc(50% + var(--vboy) * var(--u)); width: 0; height: 0;
    translate: calc(var(--x) * var(--u)) calc(var(--y) * var(--u)); }
.hex-map :is(.mover-art, .self-art) { position: absolute; left: 0; top: 0; width: var(--u); height: var(--u); overflow: visible; }
.mover.walking { animation: mover-walk var(--dur) linear forwards; }
@keyframes mover-walk { from { transform: translate(0, 0); } to { transform: translate(calc(var(--dx) * var(--u)), calc(var(--dy) * var(--u))); } }

@media (prefers-reduced-motion: reduce) {
    .self-marker .pulse,
    .self-climb.walking:not([data-gait]) .self-gait,
    .self-climb[data-gait="walking"] .self-gait,
    :is(.map-ground, .map-things, .pin-slide, .self-climb):is(.walking, .settling):is(.a, .b),
    .self-bob.walking,
    .mover.walking { animation: none; }
}

.movement-progress { display: flex; align-items: center; gap: 10px; width: min(420px, 100%); }
.movement-progress .bar { flex: 1; }
.moving-note { margin-top: 4px; color: #c9b48a; }

/* ---------- Действия ---------- */
.action-surface { flex-shrink: 0; }
/* Ряд сохраняет отступы, когда под ним появляется полоса прокрутки. */
.action-panel { padding: 0; overflow-x: auto; overflow-y: hidden; scrollbar-width: thin; scrollbar-color: #6a6056 transparent; }
.action-row { display: flex; align-items: center; gap: 4px; width: max-content; min-width: 100%; min-height: 60px; padding: 0 8px; }
.action-row > button { flex-shrink: 0; }
/* Группы действий (автор, 09.10.2026): между непустыми — черта, пустые не видны. «Своё» (встать, поднять
   отменить) — слева, сразу за щитом (автор, 11.10.2026; до того у правого края); «Поднять хвост» — у правого края. */
.action-group { display: flex; align-items: center; gap: 4px; flex-shrink: 0; }
.action-group:not(:has(*)) { display: none; }
.action-group:has(*) ~ .action-group:has(*) { margin-left: 6px; padding-left: 9px; border-left: 1px solid #3a3632; align-self: stretch; }
.action-worn-name { max-width: 220px; margin-inline: 4px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--heading); font-size: 13px; }
/* «Поднять хвост» у правого края — правило после черты между группами: та же сила, и позднее побеждает;
   стоя раньше, оно проигрывало отступу 6px, и кнопка прыгала влево, едва перед ней появлялась группа (автор, 11.10.2026). */
.action-group.action-tail { margin-left: auto; }
.action-box.danger:not(:disabled) { border-color: #6e2a24; }
.action-box.danger:not(:disabled):hover { border-color: #c0473c; }
.action-box.on:not(:disabled) { border-color: var(--gold); }
.action-box {
    position: relative;
    min-width: 54px;
    height: 50px;
    padding: 0 6px;
    background: rgba(100, 100, 100, .2);
    border: 2px solid #2e2e2e;
    border-radius: 5px;
    color: #b0b0b0;
    cursor: pointer;
}
.action-box:hover:not(:disabled) { border-color: #4b4b4b; }
.action-box:disabled { cursor: not-allowed; opacity: .55; }
.action-name { position: absolute; left: 3px; bottom: 2px; font-size: 10px; text-shadow: 0 0 2px #000; z-index: 1; }
/* Подписи по центру и до двух строк (09.10.2026): «Наполнить», «Выбросить» обрезались слева направо. */
.action-box:not(.art) { width: 58px; min-width: 58px; padding: 0 2px; }
.action-box:not(.art) .action-name { left: 2px; right: 2px; bottom: 3px; text-align: center; line-height: 1.1; white-space: normal; overflow-wrap: anywhere; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.action-box { overflow: hidden; }
.action-box:not(:disabled) { border-color: #5a4a30; background: rgba(120, 96, 60, .22); color: #d6c7a8; }
.action-box:not(:disabled):hover { border-color: var(--gold); }
/* Перезарядка: кнопка гаснет, как любая нерабочая, а заливка снизу убывает вместе с остатком.
   Полупрозрачная — под ней должно оставаться видно саму кнопку. */
.cooldown-layer { position: absolute; inset: 0; background: rgba(0, 0, 0, .5); clip-path: inset(var(--cooldown-clear) 0 0); transition: clip-path 200ms linear; }
.cooldown-text { position: absolute; top: 2px; left: 4px; font-size: 11px; color: #e0d4bb; z-index: 1; text-shadow: 0 0 2px #000; }

/* ---------- Иконки действий и навыков (art/actions, см. ActionArt) ----------
   Картинка во всю кнопку, поверх — одна на всех кованая рамка actions/border. Рамка режется на девять
   кусков (border-image): уголки держат свой размер в пикселях при любом размере кнопки, а не ужимаются
   вместе с ней. Уголок в исходнике — 46 из 256 px, то есть 18%. «Золото» — та же рамка под фильтром. */
.action-box.art, .tree-skill.art { --frame-gold: sepia(1) saturate(2.6) hue-rotate(-12deg) brightness(1.2); }
.action-box.art::after, .tree-skill.art::before {
    content: ""; position: absolute; inset: 0; z-index: 1; pointer-events: none;
    border: 12px solid transparent;
    border-image: var(--art-action-frame) 18% / 12px stretch;
}
.action-art, .tree-art { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; pointer-events: none; }
.tree-skill.art::before, .tree-art { transition: filter 150ms; }

/* Кнопка: квадрат 52. Имя мелко внизу — полное в подсказке; перезарядка — числом посередине. */
.action-box.art, .action-box.art:not(:disabled) { width: 56px; min-width: 56px; height: 56px; padding: 0; border: 0; border-radius: 6px; background: #0e0d0c; }
.action-art { border-radius: 6px; }
.action-box.art .action-name { left: 0; right: 0; bottom: 3px; z-index: 2; padding: 0 1px; text-align: center; font-size: 9.5px; letter-spacing: -.1px; line-height: 1.1;
    color: #eadfc6; white-space: normal; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; text-shadow: 0 0 3px #000, 0 1px 2px #000; }
/* У рисунка подпись только под мышью (автор, 10.10.2026): низ рисунка занят, у копыт действие как раз
   внизу. У пиктограммы знак выше середины, низ свободен — подпись видна всегда. */
.action-box.art.drawn .action-name { opacity: 0; }
.action-box.art.drawn:hover .action-name, .action-box.art.drawn:focus-visible .action-name { opacity: 1; }
/* Оттенок рамки по роду действия (автор, 10.10.2026; ActionArt.Tint): бой — красный, заклинание — цвета
   маны, доброе к другому — синий. Слегка: рамка остаётся кованой. Под мышью — золото у всех. */
.action-box.art { --frame-tint: brightness(1); }
.action-box.art.tint-fight { --frame-tint: sepia(.5) saturate(2.2) hue-rotate(-40deg); }
.action-box.art.tint-spell { --frame-tint: sepia(.5) saturate(2.2) hue-rotate(215deg); }
.action-box.art.tint-kind { --frame-tint: sepia(.5) saturate(2) hue-rotate(170deg); }
.action-box.art.tint-guard { --frame-tint: saturate(.2) brightness(1.5); }
.action-box.art::after { filter: var(--frame-tint); }
.action-box.art:not(:disabled):hover::after { filter: var(--frame-gold); }
.action-box.art:not(:disabled):hover .action-art { filter: brightness(1.12); }
.action-box.art:disabled { opacity: 1; }
.action-box.art:disabled .action-art { filter: grayscale(1) brightness(.42); }
.action-box.art:disabled::after { filter: var(--frame-tint) brightness(.55); }
.action-box.art:disabled .action-name { color: #8c857c; }
.action-box.art.cooling .action-art { filter: grayscale(.6) brightness(.45); }
.action-box.art .cooldown-layer { z-index: 0; border-radius: 0 0 6px 6px; }
.action-box.art .cooldown-text { inset: 0; top: 0; left: 0; z-index: 2; display: flex; align-items: center; justify-content: center; padding-bottom: 6px;
    font: 600 20px var(--font-title); color: #f0e2c4; text-shadow: 0 0 4px #000, 0 1px 2px #000; }

/* ---------- Чат ---------- */
/* Высоту чата тянут за верхнюю кромку (chat-resize.js пишет --chat-height на <html>). Чат сжимаемый,
   карта над ним — нет (min-height у .view-panel): уменьшили окно — уступает чат, а не карта. */
/* Сам чат уступает карте только до 175px (вкладки целы); ниже — лишь если игрок сам так потянул. */
.chat-panel { position: relative; height: var(--chat-height, 260px); min-height: min(175px, var(--chat-height, 175px)); flex-shrink: 1; display: flex; flex-direction: column; background: rgba(22, 22, 22, .85); border-radius: 6px; }
.chat-grip { position: absolute; left: 0; right: 0; top: -4px; height: 9px; z-index: 3; cursor: ns-resize; touch-action: none; }
.chat-grip::after { content: ""; position: absolute; left: 50%; top: 3px; width: 46px; height: 3px; margin-left: -23px; border-radius: 2px; background: #3e3426; transition: background 120ms; }
.chat-grip:hover::after, .chat-resizing .chat-grip::after { background: var(--gold); }
/* Пока тянут — текст под курсором не выделяется, и курсор не мигает над чужими элементами. */
.chat-resizing, .chat-resizing * { cursor: ns-resize !important; user-select: none; }
/* Лента и столбик вкладок справа от неё: чату нужнее высота, чем ширина (автор, 03.10.2026). */
.chat-main { flex: 1; min-height: 0; display: flex; gap: 4px; }
/* Столбик не выше ленты: что не влезло (чат сжали, собеседников много) — прокручивается, а не
   наезжает на поле ввода. */
/* Ширина постоянная: имя собеседника любой длины её не раздвигает (автор: «чтоб ширина не скакала»). */
.chat-tabs { flex: none; width: 104px; box-sizing: border-box; min-height: 0; overflow: hidden auto; scrollbar-width: none; display: flex; flex-direction: column; gap: 2px; padding-left: 4px; border-left: 1px solid #2e2e2e; }
.chat-tabs > button { flex-shrink: 0; }
.chat-tabs button { background: transparent; border: 1px solid transparent; border-radius: 4px; color: var(--text-muted);
    font: 500 12px var(--font-body); padding: 4px 8px; text-align: left; white-space: nowrap; cursor: pointer; }
.chat-tabs button:hover { color: var(--heading); }
.chat-tabs button.active { color: var(--heading); border-color: #2e2e2e; background: rgba(255, 255, 255, .03); }
/* Свои вкладки (29.10.2026): имя и карандаш настройки — как у переписки имя и крестик. «+» — новая вкладка. */
.chat-tab-row { flex-shrink: 0; display: flex; align-items: center; height: 24px; border: 1px solid transparent; border-radius: 4px; }
.chat-tab-row.active, .chat-tab-row.editing { border-color: #2e2e2e; background: rgba(255, 255, 255, .03); }
.chat-tabs .chat-tab { flex: 1; min-width: 0; display: flex; align-items: center; border: 0; padding: 2px 2px 2px 8px; color: #8f877b; }
.chat-tab-row.active .chat-tab, .chat-tabs .chat-tab:hover { color: var(--heading); }
.chat-tabs .chat-tab-edit { flex: none; width: 18px; height: 18px; padding: 0; border: 0; border-radius: 3px; font-size: 11px; line-height: 1; text-align: center; color: #6a5d48; opacity: 0; }
.chat-tab-row:hover .chat-tab-edit, .chat-tab-row.active .chat-tab-edit, .chat-tab-row.editing .chat-tab-edit, .chat-tab-edit:focus-visible { opacity: 1; }
.chat-tabs .chat-tab-edit:hover { color: var(--heading); background: rgba(255, 255, 255, .06); }
.chat-tabs .chat-tab-add { align-self: flex-start; padding: 0 8px; font-size: 15px; line-height: 20px; color: #5a5249; }
/* Настройка вкладки — на месте ленты: имя, две строки галочек (сказанное, игра), кнопки. */
.chat-tab-editor { display: flex; flex-direction: column; align-items: flex-start; gap: 8px; padding: 4px; }
.chat-tab-editor .chat-tab-name { width: 200px; max-width: 100%; height: 28px; }
.chat-tab-feeds { display: flex; flex-wrap: wrap; gap: 4px 14px; font-size: 13px; color: #b0b0b0; }
.chat-tab-feeds label { display: inline-flex; align-items: center; gap: 5px; cursor: pointer; }
/* Цвет канала (01.11.2026, ChatTints): пометка канала — тоном, слова — светлее; имя остаётся своим (поддержка, своё).
   Кружок за галочкой открывает набор под строкой; «без цвета» — перечёркнутый серый. */
.tint-gold { --tint: #c9a35b; --tint-text: #dccfae; }
.tint-amber { --tint: #d08a52; --tint-text: #e2b994; }
.tint-red { --tint: #c4645c; --tint-text: #dda59e; }
.tint-pink { --tint: #c97aa8; --tint-text: #e2b3cf; }
.tint-violet { --tint: #a583b8; --tint-text: #c9a8d8; }
.tint-blue { --tint: #6a9fd0; --tint-text: #a9c9e8; }
.tint-teal { --tint: #4fb3a3; --tint-text: #9fd6cc; }
.tint-green { --tint: #78ad6c; --tint-text: #b0d3a6; }
.chat-line.tinted .chat-channel { color: var(--tint); }
.chat-line.tinted .chat-text { color: var(--tint-text); }
.chat-tint-dot { width: 14px; height: 14px; padding: 0; border-radius: 50%; border: 1px solid rgba(255, 255, 255, .25);
    background: var(--tint, #8a8a8a); cursor: pointer; flex: none; }
.chat-tint-dot.plain { background: linear-gradient(135deg, #6a6a6a 45%, #a34d42 45% 55%, #6a6a6a 55%); }
.chat-tint-dot:hover, .chat-tint-dot.active { border-color: #f3e8d0; box-shadow: 0 0 0 1px #f3e8d0; }
.chat-tint-pick { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; }
.chat-tint-pick .chat-tint-dot { width: 18px; height: 18px; }
.chat-tint-pick .btn { height: 22px; padding: 0 8px; font-size: 12px; }
.chat-tab-buttons { display: flex; gap: 6px; }
.chat-tab-buttons .btn { height: 26px; padding: 0 10px; font-size: 12px; }
/* Личные переписки — под тонкой чертой, по вкладке на собеседника. Их может быть сколько угодно,
   поэтому у списка своя прокрутка: три вкладки над ним стоят на месте. Длинное имя обрезается,
   целиком оно в подсказке. Справа — крестик «закрыть»; он спрашивает подтверждение на месте строки. */
.chat-peers { flex: 0 1 auto; min-height: 24px; overflow: hidden auto; display: flex; flex-direction: column; gap: 1px;
    margin-top: 2px; padding-top: 3px; border-top: 1px solid #2e2e2e; scrollbar-width: none; }
.chat-peer-row { flex-shrink: 0; display: flex; align-items: center; height: 22px; border: 1px solid transparent; border-radius: 4px; }
.chat-peer-row.active { border-color: #2e2e2e; background: rgba(255, 255, 255, .03); }
.chat-tabs .chat-peer { flex: 1; min-width: 0; display: flex; align-items: center; border: 0; padding: 2px 2px 2px 8px; color: #a583b8; }
.chat-peer-row.active .chat-peer, .chat-tabs .chat-peer:hover { color: #d9c2e6; }
.chat-peer-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.chat-tabs .chat-peer-close, .chat-tabs .chat-peer-yes, .chat-tabs .chat-peer-no {
    flex: none; width: 18px; height: 18px; padding: 0; border: 0; border-radius: 3px; font-size: 13px; line-height: 1; text-align: center; color: #6a5d48; }
.chat-tabs .chat-peer-close { opacity: 0; }
.chat-peer-row:hover .chat-peer-close, .chat-peer-row.active .chat-peer-close, .chat-peer-close:focus-visible { opacity: 1; }
.chat-tabs .chat-peer-close:hover, .chat-tabs .chat-peer-no:hover { color: var(--heading); background: rgba(255, 255, 255, .06); }
.chat-peer-row.asking { border-color: #5a3a10; }
.chat-peer-ask { flex: 1; min-width: 0; padding-left: 6px; font-size: 11px; color: #d9c8a4; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.chat-tabs .chat-peer-yes { color: var(--danger-text); }
.chat-tabs .chat-peer-yes:hover { background: rgba(160, 40, 30, .25); }
/* «Не слышу: N» — внизу столбика, отдельно от вкладок: это не вид ленты, а список заглушённых. */
.chat-tabs .chat-deaf { margin-top: auto; font-size: 11px; color: #8a6f5a; }
.chat-ignored { display: flex; flex-direction: column; gap: 4px; padding: 4px; }
.chat-ignored-row { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.chat-ignored-row .btn { height: 24px; padding: 0 8px; font-size: 12px; }
/* Меню имени — прямо в строке, за именем: всплывающее окно обрезалось бы краем ленты. */
.chat-menu { display: inline-flex; gap: 2px; margin: 0 4px; vertical-align: baseline; }
.chat-menu button { border: 1px solid #5a3a10; border-radius: 4px; background: rgba(12, 10, 8, .78); color: #d9c8a4;
    font: 500 12px var(--font-body); padding: 0 6px; cursor: pointer; }
.chat-menu button:hover { color: #f3e8d0; border-color: var(--gold); }
/* Личное — своего цвета: в общей ленте его не должно быть можно пропустить. */
.chat-whisper .chat-text { color: #c9a8d8; }
.chat-whisper .chat-channel { color: #a583b8; }
/* От друга — голубым, от остальных — сиреневым (автор, 04.10.2026): и строки, и вкладка переписки. */
.chat-whisper.friend .chat-text { color: #8cc8ea; }
.chat-whisper.friend .chat-channel { color: #6aa6c8; }
.chat-tabs .chat-peer-row.friend .chat-peer { color: #8cc8ea; }
.chat-tabs .chat-peer-row.friend.active .chat-peer, .chat-tabs .chat-peer-row.friend .chat-peer:hover { color: #bfe3f7; }
.chat-unread { display: inline-block; width: 6px; height: 6px; margin-left: 5px; border-radius: 50%; background: var(--gold); vertical-align: middle; }
.chat-body { flex: 1; min-height: 0; overflow-y: auto; padding: 2px 4px; overflow-wrap: anywhere; scrollbar-width: thin; scrollbar-color: #3a3633 transparent; }
.chat-line { font-size: 14px; line-height: 1.35; color: #b0b0b0; }
.chat-line time { color: #57524d; font-size: 11px; margin-right: 6px; }
.chat-name { color: #a4a4a4; font-weight: 500; }
.chat-name.own { color: #6c7da3; }
.chat-name.addressable { cursor: pointer; }
.chat-name.addressable:hover { color: #d8d0c4; text-decoration: underline; }

/* Цвет имени за поддержку (SupportRules, 29.10.2026): лестница самоцветов. Оттенки разведены с занятыми: обычное имя
   серое, своё сине-серое, друзья голубые, админ и урон кирпичные, карма алая. Рубин светится (на карточке тлеет),
   алмаз везде переливается бликом — как в показе автору (29.10.2026: «переливался слегка цветами»). Своё имя и имя
   под мышью — тоже цветом поддержки. У алмаза цвет самих букв прозрачный: видна переливающаяся подложка. */
.name-silver { --name-tint: #d5dae0; }
.name-gold { --name-tint: #dfb64e; }
.name-emerald { --name-tint: #3fc47e; }
.name-ruby { --name-tint: #e2406f; text-shadow: 0 0 6px rgba(226, 64, 111, .45); }
.name-diamond {
    --name-tint: transparent;
    /* Тень букв (у карточки персонажа — тёмная обводка) легла бы поверх подложки и погасила её. */
    text-shadow: none;
    background: linear-gradient(100deg, #dfe6ee 35%, #ffb8e2 45%, #fff 50%, #a6e6ff 55%, #dfe6ee 65%) 100% 0 / 300% 100%;
    -webkit-background-clip: text; background-clip: text;
    animation: name-glint 4s linear infinite;
}
.name-silver, .name-gold, .name-emerald, .name-ruby, .name-diamond,
.chat-name.own:is(.name-silver, .name-gold, .name-emerald, .name-ruby, .name-diamond),
.chat-name.addressable:hover:is(.name-silver, .name-gold, .name-emerald, .name-ruby, .name-diamond),
.admin-badge:is(.name-silver, .name-gold, .name-emerald, .name-ruby, .name-diamond) { color: var(--name-tint); }
.card-name .name-ruby { animation: name-smolder 3.2s ease-in-out infinite alternate; }
@keyframes name-smolder {
    from { text-shadow: 0 0 3px rgba(226, 64, 111, .3); }
    to { text-shadow: 0 0 9px rgba(226, 64, 111, .75); }
}
@keyframes name-glint {
    from { background-position: 100% 0; }
    to { background-position: -200% 0; }
}
/* Поддержка в настройках (01.11.2026): цвета имени — плашками в два ряда, каждая надпись своим цветом; выбранная —
   в золотой рамке с галочкой, как выбор в «Графике». */
.gm-opt-tints { align-items: flex-start; }
.gm-tints { display: grid; grid-template-columns: repeat(3, minmax(112px, auto)); gap: 8px; flex: none; }
.gm-tints button { display: inline-flex; align-items: center; justify-content: center; gap: 8px; height: 40px; padding: 0 14px;
    border: 1px solid #4b4540; border-radius: 6px; background: rgba(0, 0, 0, .25); color: #cfc5b2; font: 600 15px var(--font-body); cursor: pointer; }
.gm-tints button:hover:not(.on):not(:disabled) { border-color: var(--gold); }
.gm-tints button.on { border-color: #c9a468; background: linear-gradient(rgba(107, 82, 49, .55), rgba(63, 47, 27, .55)); box-shadow: inset 0 0 0 1px #a07e4a; cursor: default; }
.gm-tints button:disabled:not(.on) { opacity: .6; cursor: wait; }
.gm-tints .menu-icon { width: 16px; height: 16px; color: #f0e2c4; }
.gm-opt-text a { color: var(--gold-hover); }
.gm-opt-text a:hover { color: #e2c48c; }
.gm-settings .gm-opt .btn { flex: none; height: 40px; text-transform: none; }
/* Пароль в меню: поля столбиком в половину окна, кнопка под ними. Страница «Свой пароль» — как была. */
.gm-password form { display: flex; flex-direction: column; max-width: 360px; padding-top: 8px; }
.gm-password .field { margin-bottom: 10px; }
.gm-password .field > span { color: var(--text-2); }
.gm-password .field input { width: 100%; height: 38px; }
.gm-password .hint { margin: 0 0 10px; color: #8fc27a; }
.gm-password .btn { align-self: flex-start; height: 40px; margin-top: 4px; text-transform: none; }
@media (prefers-reduced-motion: reduce) {
    .card-name .name-ruby, .name-diamond { animation: none; }
}
.chat-mention { color: #c9a35b; }
/* «*Садится на Амарию.*» — действие, а не речь: курсивом, как ремарка. */
.chat-action { font-style: italic; color: #cbbfa8; }
/* Позвали меня: строку должно быть видно в потоке, даже пробегая чат глазами. */
.chat-line.mentions-me { background: rgba(201, 163, 91, .12); box-shadow: inset 2px 0 0 #c9a35b; padding-left: 4px; border-radius: 2px; }
.chat-channel { color: #a0874f; font-size: 12px; margin-right: 4px; }
/* Счётчик одинаковых строк игры подряд: «×4» бледной пилюлей в конце строки. */
.chat-count { margin-left: 6px; padding: 0 5px; border-radius: 8px; background: rgba(255, 255, 255, .07); color: #8a8378; font-size: 11px; font-style: normal; vertical-align: 1px; }
/* Проглоченный: «изнутри такого-то» — тише имени; мычание вместо слов — курсивом, как всё невнятное. */
.chat-inside { color: #8a6f5a; font-size: 12px; }
.chat-colon { color: #a4a4a4; font-weight: 500; margin-right: 6px; }
.chat-muffled { color: #8d8478; font-style: italic; }
.chat-system .chat-text { color: #787878; font-style: italic; font-size: 13px; }
/* Объявление админа (14.10.2026): во всех лентах; как обычный разговор — тот же размер, без жирного, — только красным
   (как шёпот своим цветом). Приглушённым: яркое «режет глаза». */
.chat-admin .chat-channel { color: #a34d42; }
.chat-admin .chat-text { color: #c65f52; }

/* Куски строки игры (ChatTone, автор 14.10.2026): вещь, количество, кто — чуть ярче и прямо; урон красным —
   сознание ярким, жизнь тёмным. Сама строка остаётся тихой, серой. */
/* Приглушено (автор, 14.10.2026): без жирного; сознание всё так же ярче жизни. */
.chat-system .tone-value { color: #968e81; font-style: normal; }
.chat-system .tone-mind { color: #c9655a; font-style: normal; }
.chat-system .tone-life { color: #9c4038; font-style: normal; }
/* Рассказ (тексты Elana): длинная проза от второго лица — тёплым пергаментным цветом и с чертой слева,
   чтобы читалась отдельно от сообщений игры и разговора. */
.chat-line.chat-story { margin: 4px 0; padding-left: 7px; box-shadow: inset 2px 0 0 #6b4f2a; }
.chat-story .chat-text { color: #c8ad80; font-style: italic; }
/* Титры: кто помог игре. */
.credits { display: flex; flex-direction: column; align-items: center; gap: 2px; padding: 8px 0 4px; text-align: center; }
.credits h3 { margin: 12px 0 4px; font: 600 13px var(--font-body); letter-spacing: .06em; text-transform: uppercase; color: var(--text-muted); }
.credits h3:first-child { margin-top: 0; }
.credits p { margin: 0; font-size: 16px; color: var(--heading); }
.chat-input { display: flex; gap: 4px; padding-top: 4px; border-top: 1px solid #2e2e2e; }
.chat-input select { width: 110px; height: 30px; flex-shrink: 0; }
.chat-input .chat-to { width: 130px; flex: none; }
.chat-input input { height: 30px; }
.chat-input .btn { height: 30px; padding: 0 12px; }
/* Отказ чата — строкой в ленте, цветом ошибки: виден на любой вкладке. */
.chat-refusal .chat-text { color: var(--danger-text); font-style: normal; }

/* ---------- Клетка ---------- */
.cell-resource { display: flex; align-items: center; justify-content: space-between; padding: 4px 8px; }
/* Шапка гекса не сжимается: колонка — гибкий столбец, и при толпе на гексе (у лавки — жители) строка координат обрезалась. */
.cell-header { text-align: left; padding: 0; overflow: hidden; flex-shrink: 0; }
/* Вид местности (автор, 23.10.2026; art/scenes): картина справа, слева тёмное под название — как на наброске
   автора. Нет картины — та же плашка, тёмная. */
.cell-scene {
    position: relative; min-height: 92px; padding: 12px 14px 10px;
    display: flex; flex-direction: column; justify-content: center; gap: 2px;
    background:
        linear-gradient(90deg, rgba(14, 13, 12, .94) 0%, rgba(14, 13, 12, .78) 34%, rgba(14, 13, 12, .18) 72%, rgba(14, 13, 12, 0) 100%),
        linear-gradient(0deg, rgba(14, 13, 12, .55), rgba(14, 13, 12, 0) 45%),
        var(--scene, linear-gradient(160deg, #26221d, #15130f)) right center / cover no-repeat;
}
.cell-title { font-family: var(--font-title); font-size: 22px; line-height: 1.1; color: var(--heading); text-shadow: 0 1px 3px #000, 0 0 8px rgba(0, 0, 0, .8); white-space: normal; }
.cell-sub { font-size: 12px; color: #cfc4ae; text-shadow: 0 1px 2px #000; }
.cell-marks { display: flex; flex-wrap: wrap; gap: 4px 6px; padding: 0 10px; }
.cell-marks:not(:empty) { padding: 7px 10px; border-top: 1px solid rgba(201, 164, 104, .14); }
.cell-marks .safe-badge { margin-top: 0; }
.cell-chip { display: inline-block; padding: 2px 9px; font-size: 11px; color: #c9bda5; border: 1px solid #4a4339; border-radius: 10px; background: rgba(74, 67, 57, .22); }
.cell-chip.water { color: #9fc3d8; border-color: #34505f; background: rgba(52, 80, 95, .22); }
.cell-header > .text-1 { padding: 0 12px 7px; }
.watch-header .btn { margin-top: 6px; }
.watch-item { display: flex; align-items: center; gap: 8px; padding: 3px 0; }
.watch-item .text-1 { flex: 1; }
.watch-error { margin-top: 6px; color: #d98a6a; }
.cell-header .text-0 { white-space: normal; }
/* ---------- Качество ---------- */
/* Качество (1–100) — цветом рамки иконки и названия, числом — в подсказке. Рамка карточки не
   трогается: золотая у неё значит «выбрано». Полосы шкалы: до 30 — ржаво-бурое, 30–60 — серое
   (как у всех вещей), 60–90 — синее, 90–99 — золото с тёплым свечением, 100 — белое. */
:is(.item-card, .counter-line).q-rusty .entity-icon { border-color: #7a5a3c; background-color: rgba(122, 90, 60, .12); }
:is(.item-card, .counter-line).q-rusty .text-0 { color: #9c8a78; }
:is(.item-card, .counter-line).q-blue .entity-icon { border-color: #4a86b8; background-color: rgba(74, 134, 184, .12); }
:is(.item-card, .counter-line).q-blue .text-0 { color: #8cc0ea; }
:is(.item-card, .counter-line).q-gold .entity-icon { border-color: #d0a03a; background-color: rgba(217, 164, 65, .12); box-shadow: 0 0 6px rgba(217, 164, 65, .45); }
:is(.item-card, .counter-line).q-gold .text-0 { color: #f2c457; }
:is(.item-card, .counter-line).q-white .entity-icon { border-color: #f4f1ea; background-color: rgba(244, 241, 234, .14); box-shadow: 0 0 8px rgba(255, 255, 255, .6); }
:is(.item-card, .counter-line).q-white .text-0 { color: #ffffff; text-shadow: 0 0 6px rgba(255, 255, 255, .45); }
/* Сумы: мелкие кнопки-отборы по роду вещи — значками, в ряд под заголовком. Рисунок автора —
   белый, поэтому он идёт маской, а цвет даёт заливка (автор, 06.10.2026: цвета — под интерфейс):
   включённая — тёплое золото на тёмном, выключенная — серая, как погашенный текст. */
.inv-filters { display: flex; flex-wrap: wrap; justify-content: center; gap: 4px; padding: 4px 8px 6px; border-bottom: 1px solid rgba(255, 255, 255, .06); }
/* Плитки отборов — мельче и тише прежних (автор, 09.10.2026): контурный знак, включённый — приглушённым
   золотом, а не светлым; ярче только под курсором. И всё вдвое тусклее — opacity во всех состояниях. */
.inv-filter { opacity: .5; position: relative; display: flex; align-items: center; justify-content: center; width: 21px; height: 21px; padding: 0; background: #141414; border: 1px solid #2e2c2a; border-radius: 3px; cursor: pointer; color: #4a4640; }
.inv-filter svg { width: 14px; height: 14px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
.inv-filter:hover { border-color: #5a5247; color: #8a8072; }
.inv-filter.on { border-color: rgba(176, 141, 87, .55); background: #18150f; color: #a8936d; }
.inv-filter.on:hover { border-color: var(--gold); color: var(--heading); }
/* Черта между отборами и порядком (31.10.2026): тонкая, высотой в плитку. */
.inv-filters-sep { flex: none; width: 1px; margin: 2px 3px; background: #2e2c2a; }
/* Пояс и кошель над инвентарём (09.10.2026): свободные места — одна полоса с бледными знаками. */
.holder-free { display: flex; align-items: center; gap: 8px; margin: 3px; padding: 5px 8px; border: 1px dashed #3a3632; border-radius: 5px; font-size: 11px; color: #5a5650; }
.holder-glyphs { display: flex; gap: 2px; flex: none; }
.holder-glyphs svg { width: 18px; height: 18px; fill: none; stroke: #3d3934; stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round; }
.holder-box.drop-target .holder-free { border-color: var(--gold); color: var(--text); }
.holder-box.drop-target .holder-glyphs svg { stroke: var(--gold); }
/* Окно «Изучить»: строка на каждое, что есть на гексе; число — цветом своей полосы. */
.survey-line { display: flex; justify-content: space-between; gap: 16px; padding: 6px 0; border-bottom: 1px solid rgba(255, 255, 255, .06); }
.survey-line .survey-quality { font-weight: 600; }
/* Живность (19.10.2026): вместо качества — слова в несколько строк; имя строки не ужимается в «Жи…». */
.survey-line > .text-0 { flex-shrink: 0; }
.survey-line .survey-note { text-align: right; color: var(--text); }
.survey-line.q-rusty .survey-quality { color: #9c8a78; }
.survey-line.q-blue .survey-quality { color: #8cc0ea; }
.survey-line.q-gold .survey-quality { color: #f2c457; }
.survey-line.q-white .survey-quality { color: #ffffff; text-shadow: 0 0 6px rgba(255, 255, 255, .45); }
/* Качество постройки надела (27.10.2026): одна строка в окне строения, число — цветом своей полосы. */
.structure-quality { margin: 6px 0; font-weight: 600; color: #c8c2b8; }
.structure-quality.q-rusty { color: #9c8a78; } .structure-quality.q-blue { color: #8cc0ea; } .structure-quality.q-gold { color: #f2c457; }
.structure-quality.q-white { color: #ffffff; text-shadow: 0 0 6px rgba(255, 255, 255, .45); }
/* Редкость — у склянок облика, своими цветами, чтобы не путать с качеством: обычная — серая
   (как у всех вещей), редкая — зелёная, уникальная — фиолетовая. */
:is(.item-card, .counter-line).r-rare .entity-icon { border-color: #4f9a56; background-color: rgba(79, 154, 86, .12); }
:is(.item-card, .counter-line).r-rare .text-0 { color: #8fd296; }
:is(.item-card, .counter-line).r-unique .entity-icon { border-color: #8a5cc4; background-color: rgba(138, 92, 196, .14); box-shadow: 0 0 6px rgba(138, 92, 196, .5); }
:is(.item-card, .counter-line).r-unique .text-0 { color: #c3a0f0; }
.item-card.selected { border-color: var(--gold); }

/* ---------- Печь ---------- */
/* Окно загрузки не тянется и не сжимается, как у склада: экран печи прокручивается целиком,
   а длинная загрузка листается внутри своего окна. */
.furnace .furnace-load { flex: 0 0 auto; }
.furnace .furnace-load .box-content { min-height: 120px; max-height: 280px; }
.furnace-heat { width: 100%; display: flex; flex-direction: column; gap: 4px; }
/* Полоса жара: чем жарче, тем краснее и ярче — цвет и свечение задаёт --fire. */
.furnace-temp { position: relative; height: 22px; background: #0e0e0e; border: 1px solid #323232; border-radius: 4px; overflow: hidden; }
.furnace-temp > span { position: absolute; inset: 0 auto 0 0; width: var(--fill); background: linear-gradient(90deg, #2a120a, var(--fire)); box-shadow: 0 0 12px var(--fire); transition: width 400ms, background 400ms; }
.furnace-temp > em { position: absolute; inset: 0; font: normal 12px/22px var(--font-body); text-align: center;
    color: #f3e6d6; text-shadow: -1px 0 #160c08, 1px 0 #160c08, 0 -1px #160c08, 0 1px #160c08, 0 0 3px #000;
    font-variant-numeric: tabular-nums; }
/* Строка о топливе — на две строки заранее, чтобы кнопки под ней не прыгали. */
.furnace-status { min-height: 2.8em; }
.furnace-message { min-height: 1.4em; }
/* Нагрев кучки — тем же огнём, что жар печи, только ниже: карточка ровно в высоту иконки. */
.heat-bar { position: relative; height: 10px; margin: 2px; background: #0e0e0e; border: 1px solid #323232; border-radius: 4px; overflow: hidden; }
.heat-bar > span { position: absolute; inset: 0 auto 0 0; width: var(--fill); background: linear-gradient(90deg, #2a0806, var(--fire)); box-shadow: 0 0 8px var(--fire); transition: width 400ms, background 400ms; }
.heat-bar > em { position: absolute; inset: 0; font: normal 10px/14px var(--font-body); text-align: center; color: #f3e6d6; text-shadow: 0 0 3px #000, 0 0 3px #000; font-variant-numeric: tabular-nums; }

/* Гекс выбран целью — для действий по местности: рамка золотом, как у выбранной карточки. */
/* И под курсором тоже: общее правило наведения (button.box:hover) весомее и красило рамку в серый —
   выделение пропадало ровно тогда, когда на плашку смотрят. */
.cell-header.area-target, button.cell-header.area-target:hover { border-color: var(--gold); }
/* Свой персонаж выбран (открыт его экран) — та же золотая рамка, что у выбранной цели и гекса:
   раньше была лишь чуть более светлая серая, и выделения не было видно (автор, 04.10.2026). */
.character-box.selected, button.character-box.selected:hover { border-color: var(--gold); }
/* Карточка своего персонажа (автор, 09.10.2026): без светлого отлива под курсором и в выделении —
   его показывает рамка; оба нижних угла прямые; значки эффектов внизу, слева, как и текст. */
button.character-box:hover, .character-box.selected { background: linear-gradient(to bottom, #161616 0%, #1c1c1c 7%, #131313 100%); }
.character-box { border-radius: 6px 6px 0 0; }
.character-stats { display: grid; margin: 4px 2px 0; }
.character-survival {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 14px;
    margin: 4px 2px 0;
    padding-top: 3px;
    border-top: 1px solid #2b2824;
}
.character-stat {
    display: grid;
    grid-template-columns: 15px minmax(0, 1fr);
    align-items: center;
    gap: 6px;
    min-height: 14px;
}
.character-stat-glyph {
    flex: none;
    width: 14px;
    height: 14px;
    fill: none;
    stroke: currentColor;
    stroke-width: 1.9;
    stroke-linecap: round;
    stroke-linejoin: round;
}
.character-stat > .bar { margin: 0; min-width: 0; }
.character-stat-glyph-mind, .character-stat-glyph-health { color: #e08478; }
.character-stat-glyph-stamina { color: #d9a95f; }
.character-stat-glyph-mana { color: #b99cdd; }
.character-stat-glyph-hunger { color: #b6c66a; }
.character-stat-glyph-thirst { color: #83bed7; }
.character-stat:has(> .bar.warn) > .character-stat-glyph { color: #e07a6e; }
.character-box > .effect-badges { margin: 4px 2px 0; justify-content: flex-start; }
.cell-coords { font-size: 11px; color: #a89f8d; text-shadow: 0 1px 2px #000; margin-top: 2px; }
.safe-badge {
    display: inline-block;
    margin-top: 6px;
    padding: 2px 10px;
    font-size: 11px;
    color: #9cc587;
    border: 1px solid #3f5a33;
    border-radius: 10px;
    background: rgba(63, 90, 51, .2);
}

/* ---------- Экран строения (BuildingScreen) ----------
   Общий каркас: одна колонка по центру, прижатая к верху, шириной не больше списка.
   Внутри — строительные блоки: плитки с цифрами, ползунок, ряд кнопок, список строк. */
.view-body:has(.building-screen) { align-items: stretch; }
/* Экран во всю ширину — чтобы полоса прокрутки была у края панели, а не посреди неё;
   колонку в 560px делают поля по бокам. */
.building-screen { width: 100%; min-height: 0; overflow-y: auto; scrollbar-gutter: stable; display: flex; flex-direction: column; align-items: stretch; gap: 8px; padding: 10px max(10px, calc(50% - 280px)) 12px; text-align: center; }
/* Экран прокручивается целиком, поэтому блоки не сжимаются под его высоту. */
.building-screen > * { flex-shrink: 0; }
.building-screen > p, .building-screen .field-error { margin: 0; }
.phaser-window-content { display: flex; flex-direction: column; gap: 8px; }
.phaser-window-content > p { margin: 0; }
/* Шапка в одну строку: картинка слева, название с описанием справа от неё (автор, 05.10.2026:
   так влезает и картинка, и название). Столбиком она съедала 154px из 258 на ноутбуке. Одна на
   любом экране — ничего не прыгает. */
.building-head { display: flex; align-items: center; justify-content: center; gap: 12px; margin-bottom: 4px; text-align: left; }
.building-head .entity-icon { width: 56px; height: 56px; margin: 0; font-size: 28px; }
.building-head .figure-name, .building-screen h3.figure-name { margin: 0; }
.building-head p { margin: 0; }
.building-title { display: flex; flex-direction: column; align-items: flex-start; gap: 2px; min-width: 0; }
/* Внутри действия (торговля, разговор) шапка в одну строку: «← Назад» и название. */
.building-head.compact { position: relative; min-height: 34px; margin-bottom: 0; text-align: center; }
.building-back { position: absolute; left: 0; top: 0; }
/* «Снести» строения надела — в правом верхнем углу шапки (автор, 26.10.2026): подальше от кнопок работы. Шапка
   оставляет ему место с обеих сторон, чтобы название стояло по центру и не залезало под кнопку. */
.building-screen { position: relative; }
.building-head.demolishable { padding: 0 84px; }
.building-demolish { position: absolute; top: 10px; right: max(10px, calc(50% - 280px)); }
.building-demolish.active { border-color: var(--danger); }
.building-demolish-confirm { display: flex; flex-direction: column; gap: 6px; padding: 8px; border: 1px solid var(--danger); border-radius: 6px; }
.building-demolish-confirm p { margin: 0; }
/* Тёплый металл на кромках, тёмное поле под содержимым. Заголовок Phaser лежит поверх этой рамки. */
.building-themed {
    --building-edge: #4a3b2d;
    --building-brass: #c5a477;
    background: radial-gradient(ellipse at 50% 0, #30251b 0%, #1c1916 40%, #151414 78%);
}
.building-themed > .building-head {
    position: relative;
    border: 1px solid var(--building-edge);
    border-radius: 7px;
    background:
        radial-gradient(ellipse at 50% 0, #7e593329, transparent 72%),
        repeating-linear-gradient(135deg, #d8a87208 0 1px, transparent 1px 5px),
        linear-gradient(#2c241c, #1e1a17);
    box-shadow: inset 0 1px #d5ac7624, 0 4px 12px #0005;
}
.building-themed > .building-head:not(.compact) { padding-block: 6px; }
.building-themed > .building-head:not(.compact, .demolishable) { padding-inline: 12px; }
.building-themed > .building-head::after {
    content: "";
    position: absolute;
    inset: auto 12px -1px;
    height: 1px;
    background: linear-gradient(90deg, transparent, #94764b, transparent);
    pointer-events: none;
}
.building-themed .building-head > .entity-icon { filter: drop-shadow(0 2px 4px #0009); }
.building-themed .building-head .figure-name { color: #ecd3a8; }
.building-themed .building-title > p { color: #c2b3a2; }
.building-themed > .storage-box:not(.drop-target) {
    border-color: var(--building-edge);
    background: linear-gradient(#28211b, #1b1815 36px, #171615);
    box-shadow: inset 0 1px #d5ac761c, 0 4px 12px #0006;
}
.building-themed > .storage-box > .box-header {
    border-bottom-color: var(--building-edge);
    box-shadow: none;
}
.building-themed > .storage-box > .box-header > :first-child { color: #dfc6a3; }
.building-themed > .storage-box > .box-header > .text-2 { color: var(--building-brass); font-variant-numeric: tabular-nums; }
.building-themed .building-actions > .btn:not(.primary, .danger, .ghost) {
    background: linear-gradient(#302821, #211c18);
    border-color: var(--building-edge);
}
.building-themed .building-actions > .btn:not(.primary, .danger, .ghost):hover:not(:disabled) { border-color: var(--building-brass); }
.building-themed .building-actions > .btn.primary {
    background: linear-gradient(#614729, #3a2c1e);
    box-shadow: inset 0 1px #e8c58c33;
}
.building-themed.furnace .furnace-heat {
    padding: 8px 10px 6px;
    border: 1px solid var(--building-edge);
    border-radius: 6px;
    background: linear-gradient(#241d17, #191715);
    box-shadow: inset 0 1px #d5ac761c;
}
.building-themed.furnace .furnace-status { color: #cbbba8; }
.building-themed .building-actions > .btn.primary:hover:not(:disabled) { background: linear-gradient(#785b36, #483624); }
/* Широкий экран — для торговли в две половины. */
.building-screen.wide { padding-left: max(10px, calc(50% - 460px)); padding-right: max(10px, calc(50% - 460px)); }
/* Низкое окно: списки внутри экрана своей прокрутки не держат — прокручивается один экран,
   а не экран и список в нём. */
@media (max-height: 900px) {
    .building-screen { padding-top: 6px; padding-bottom: 8px; }
    .building-demolish { top: 6px; }
    .building-head:not(.compact) { margin-bottom: 0; }
    .building-screen .post-letters,
    .building-screen.furnace .furnace-load .box-content { max-height: none; }
}

/* Ряд плиток с цифрами — и в строениях, и в «Съесть». Плитки поровну делят ширину,
   сколько бы их ни было, и одной высоты: цифры меняются — ряд стоит на месте. */
.stat-tiles { display: flex; align-items: stretch; gap: 12px; width: 100%; }
.stat-tiles > div:not(.stat-arrow) { flex: 1 1 0; }
.stat-tiles > div:not(.stat-arrow) { display: flex; flex-direction: column; justify-content: center; min-width: 0; padding: 8px 12px; border: 1px solid #2e2c2a; border-radius: 6px; background: rgba(0, 0, 0, .35); }
.stat-tiles strong { font: 500 22px var(--font-title); color: var(--heading); }
.stat-tiles strong.unspent { color: #c9a468; }
/* Длинное слово в плитке (имя хозяина надела): шрифт по ширине плитки и числу букв (--chars), не крупнее 22px.
   Буква Cinzel — 0,64–0,69 em (замер 27.10.2026), с запасом 0,72; совсем широкое (сплошные M) обрежется многоточием. */
.stat-tiles > .fit-tile { container-type: inline-size; }
.stat-tiles strong.fit-text { font-size: clamp(10px, calc(100cqi / (var(--chars) * .72)), 22px); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.stat-arrow { flex: none; align-self: center; font-size: 22px; color: var(--gold); }
.modal .stat-tiles { margin: 6px 0; }
/* Окно со списком (Смастерить, Починка): шире обычного, список прокручивается внутри. */
.modal.modal-list { max-width: 560px; width: 100%; }
.modal-list .recipe-list { max-height: 60vh; overflow-y: auto; }

/* Ползунок «сколько» и число справа постоянной ширины — чтобы ползунок не дёргался. */
.range-row { display: flex; gap: 10px; align-items: center; width: 100%; }
.range-row input[type=range] { flex: 1; height: auto; padding: 0; accent-color: var(--gold); background: transparent; border: 0; }
.range-row input[type=number] { width: 90px; height: 32px; }
.range-value { width: 90px; flex-shrink: 0; text-align: right; color: var(--heading); font-variant-numeric: tabular-nums; }
/* Число рядом с ползунком — вписать точно (автор, 09.10.2026): перелив, набор воды, деление кучки. */
.range-row input.range-number { width: 80px; height: 30px; flex-shrink: 0; text-align: right; font-variant-numeric: tabular-nums; }
.range-unit { flex-shrink: 0; min-width: 22px; color: var(--text-muted); }
.pour-number label { flex: 1; text-align: right; }

/* Кнопки действия — поровну во всю ширину колонки. */
.building-actions { display: flex; gap: 10px; width: 100%; }
.building-actions > .btn { flex: 1 1 0; }
/* Старшина обоза у амбара и в лавке (охрана обоза): реплика и запись. */
.escort-talk { width: 100%; display: flex; flex-direction: column; gap: 6px; padding: 10px 12px; border: 1px solid #2e2c2a; border-radius: 6px; background: rgba(0, 0, 0, .35); }

/* Список строк: товар, рецепт, монета, задание. Прокручивается весь экран, а не список. */
.recipe-list { width: 100%; display: flex; flex-direction: column; gap: 6px; }
.recipe { display: flex; align-items: center; gap: 8px; padding: 6px; text-align: left; border: 1px solid #2e2c2a; border-radius: 6px; background: rgba(0, 0, 0, .35); }
.recipe.lacking { border-color: #49332c; }
.recipe .entity-icon { flex-shrink: 0; }
.recipe .entity-info { flex: 1; min-width: 0; }
.recipe .btn { flex-shrink: 0; }
.recipe-cost { display: flex; flex-wrap: wrap; gap: 4px 12px; font-size: 11px; color: #9a9188; margin-top: 2px; }
.recipe .text-0 > .text-2, .recipe-cost .text-2 { color: #aca399; }
.recipe-cost .lacking { color: #d78976; }
.recipe-cost > span > .text-2 { overflow: visible; color: #c6bbae; }

/* Строка, под которую место держат всегда: появилась или нет — кнопки не прыгают. */
.building-note { min-height: 2.8em; font-size: 12px; color: var(--text-2, #9a9188); }
.building-note.lacking { color: #b4614f; }
.building-note.hidden { visibility: hidden; }
.stat-tiles strong.lacking { color: #b4614f; }

/* ---------- Святилище роста ---------- */
.shrine-section { display: grid; gap: 8px; padding: 12px 14px; border: 1px solid #3a3632; border-radius: 8px; background: rgba(0, 0, 0, .26); text-align: left; }
.shrine-section-head { display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: 4px 12px; }
.shrine-section h3 { margin: 0; color: var(--heading); font: 500 17px var(--font-title); }
.shrine-section p { margin: 0; }
.shrine-section .building-actions { flex-wrap: wrap; gap: 6px; }
.shrine-section .building-actions > .btn, .shrine-step-head > .btn { flex: 0 1 auto; min-height: 30px; height: auto; max-width: 100%; padding: 4px 10px; font-size: 13px; white-space: normal; }
.shrine-training { border-color: #594735; background: linear-gradient(145deg, rgba(88, 65, 39, .22), transparent 68%), rgba(0, 0, 0, .3); }
.shrine-step { display: grid; gap: 5px; }
.shrine-step + .shrine-step { padding-top: 8px; border-top: 1px solid #41352b; }
.shrine-step-head, .shrine-growth { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 14px; }
.shrine-step-head > .shrine-facts, .shrine-growth > .shrine-facts { flex: 1 1 220px; }
.shrine-status { color: #e1ad80; font-size: 12px; }
.shrine-status.running { color: var(--gold-hover); }
.shrine-facts { display: flex; flex-wrap: wrap; gap: 6px 14px; margin: 0; text-align: left; }
.shrine-facts > div { flex: 1 1 108px; min-width: 0; }
.shrine-facts dt { color: #a69d93; font-size: 12px; }
.shrine-facts dd { margin: 2px 0 0; color: var(--heading); font-weight: 600; font-variant-numeric: tabular-nums; }
.shrine-shortage { color: #e2a292; font-size: 12px; }
.shrine-speed-control { display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: center; gap: 10px; }
.shrine-speed-control .range-value { width: 40px; }
.shrine-speed-control .range-row input { min-width: 0; }
.shrine-speed-control > .btn { min-height: 30px; padding: 4px 10px; font-size: 13px; }
.storage-box.tray-box.shrine-coins { margin: 2px 0; }
.storage-box.tray-box.shrine-coins .box-content { min-height: 58px; max-height: 160px; }
.shrine-coins .empty-note { min-height: 46px; padding: 4px 8px; }
.shrine-growth { padding: 4px 14px; }
.shrine-penance { border-color: #332c25; }
.sanctuary-note { font-size: 12px; }
/* Почему кнопки «Поднять» и «Открыть» живые или серые. В Святилище — золотом, вдали — приглушённо. */
.sanctuary-notice { display: flex; align-items: center; gap: 8px; padding: 7px 12px; border: 1px solid #3a3632; border-radius: 6px;
    background: rgba(12, 11, 10, .9); color: #9a9188; font-size: 12px; line-height: 1.35; text-align: left; }
.sanctuary-notice-mark { flex: none; font-size: 14px; color: #6b645c; }
.sanctuary-notice.here { border-color: var(--gold); color: var(--heading); box-shadow: 0 0 12px rgba(201, 164, 104, .25); }
.sanctuary-notice.here .sanctuary-notice-mark { color: var(--gold); }
.self-sheet > .sanctuary-notice { margin-top: 2px; }
/* Две строки под предупреждение — есть оно или нет. */
.sanctuary-lack { min-height: 2.8em; font-size: 12px; color: #b4614f; }
.death-attackers-title { margin: 10px 0 4px; }
.death-attackers { display: flex; flex-direction: column; gap: 4px; margin-bottom: 8px; } /* кто нападал — в противники (docs/PVP.md) */
.death-respawn { display: flex; flex-direction: column; gap: 6px; margin-bottom: 8px; } /* где очнуться (RespawnRules, 30.10.2026) */
.death-respawn label { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; text-align: left; cursor: pointer; }
.death-respawn input { width: auto; height: auto; flex: none; margin: 0; } /* общий стиль полей модалки растягивал кружок на всю ширину */
.sanctuary-lack.hidden { visibility: hidden; }
/* Части тела: крупный номер уровня слева, кнопка «Поднять» одной ширины. */
.sanctuary-body .body-level { width: 36px; flex-shrink: 0; text-align: center; font: 500 22px var(--font-title); color: var(--heading); font-variant-numeric: tabular-nums; }

/* ---------- Полоса уровня (docs/MASS-POOL.md) ----------
   Кованая полоса — те же приёмы, что у заставки (автор: «божественно красиво»): тёмный желоб в
   рамке, ромбы-заклёпки по концам, тлеющее золото, по живой полосе бежит блик. */
.forge-bar { position: relative; height: 10px; margin: 0 14px; border: 1px solid var(--border-hover); border-radius: 2px;
    background: linear-gradient(#050505, #121110); box-shadow: inset 0 1px 3px rgba(0, 0, 0, .9), 0 0 0 1px rgba(0, 0, 0, .8); }
.forge-bar.tall { height: 18px; }
.forge-bar.slim { height: 7px; margin: 6px 12px 2px 0; }
.forge-bar::before, .forge-bar::after { content: ""; position: absolute; top: 50%; width: 9px; height: 9px; border: 1px solid var(--gold);
    background: linear-gradient(135deg, #7d6039, #2a2014); transform: translateY(-50%) rotate(45deg); box-shadow: 0 0 6px rgba(0, 0, 0, .9); }
.forge-bar::before { left: -15px; }
.forge-bar::after { right: -15px; }
.forge-bar.slim::before, .forge-bar.slim::after { width: 6px; height: 6px; }
.forge-bar.slim::before { left: -11px; }
.forge-bar.slim::after { right: -11px; }
/* Ромбы с подписями: нынешний уровень слева, следующий справа. Заклёпки при этом прячем. */
.forge-bar.labeled { margin: 6px 24px; }
.forge-bar.labeled::before, .forge-bar.labeled::after { display: none; }
.forge-end { position: absolute; top: 50%; z-index: 1; width: 26px; height: 26px; border: 1px solid var(--gold); border-radius: 3px;
    background: linear-gradient(135deg, #7d6039, #2a2014); box-shadow: 0 0 8px rgba(0, 0, 0, .9), inset 0 0 6px rgba(201, 164, 104, .2);
    transform: translateY(-50%) rotate(45deg); display: grid; place-items: center; }
.forge-end.left { left: -22px; }
.forge-end.right { right: -22px; }
.forge-end > b { transform: rotate(-45deg); font: 500 12px var(--font-title); color: var(--heading); font-variant-numeric: tabular-nums; }
.forge-end.right > b { color: var(--gold-hover); }
.forge-fill { position: relative; height: 100%; overflow: hidden; background: linear-gradient(90deg, #4a361e, #8a6a3e 55%, var(--gold-hover));
    box-shadow: 0 0 10px rgba(201, 164, 104, .35); transition: width 600ms ease-out; }
.forge-bar.live .forge-fill::after { content: ""; position: absolute; inset: 0; width: 60px;
    background: linear-gradient(90deg, transparent, rgba(255, 236, 196, .35), transparent); animation: forge-sheen 1.8s ease-in-out infinite; }
@keyframes forge-sheen { from { transform: translateX(-60px); } to { transform: translateX(560px); } }
.forge-text { position: absolute; inset: 0; font: 500 11px/18px var(--font-title); letter-spacing: .12em; text-align: center; color: #f0e2c4;
    text-shadow: 0 0 3px #000, 0 1px 0 #000; pointer-events: none; }
.forge-bar.slim .forge-text { font-size: 9px; line-height: 7px; letter-spacing: .08em; }
@media (prefers-reduced-motion: reduce) { .forge-bar.live .forge-fill::after { animation: none; } }

/* Травмы (docs/INJURIES.md): красноватая карточка над частями тела, строка на травму. */
.injury-card { width: 100%; display: flex; flex-direction: column; gap: 6px; margin-bottom: 10px; padding: 10px 14px; border: 1px solid #6e2a24;
    border-radius: 8px; background: rgba(110, 42, 36, .15); text-align: left; }
.injury-row { display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 10px; font-size: 13px; }
.injury-row strong { color: #d98a7a; }
.injury-card p { margin: 0; }

/* Медальон уровня: кованый ромб с числом. */
/* Число — поверх ромба: ромб уходит под текст (z-index −1 внутри своего контекста), иначе он его закрывал
   (автор, 11.10.2026: «почему ромбы пустые?»). */
.part-medal { position: relative; isolation: isolate; flex: none; width: 40px; height: 40px; display: grid; place-items: center;
    font: 500 18px var(--font-title); color: var(--heading); font-variant-numeric: tabular-nums; text-shadow: 0 1px 0 #000; }
.part-medal::before { content: ""; position: absolute; inset: 5px; z-index: -1; border: 1px solid var(--gold); border-radius: 3px; transform: rotate(45deg);
    background: linear-gradient(135deg, #3a2c18, #15110b); box-shadow: 0 0 8px rgba(0, 0, 0, .9), inset 0 0 6px rgba(201, 164, 104, .15); }

/* Развитие персонажа: общий залог, следующий шаг и компактный выбор части. */
.body-development {
    display: grid;
    gap: 7px;
    padding: 10px 14px;
    border: 1px solid #504331;
    border-radius: 8px;
    background: linear-gradient(140deg, rgba(79, 58, 34, .22), rgba(9, 8, 7, .46) 65%);
    text-align: left;
}

.body-development-head, .body-training-heading, .body-development-next {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 6px 12px;
}

.body-development-head h2 {
    margin: 0;
    color: var(--heading);
    font: 500 20px var(--font-title);
}

.body-training-heading, .body-training-state, .body-development-hint {
    color: var(--text);
    font-size: 13px;
}

.body-training-heading strong { color: var(--text); }
.body-training-state.running { color: var(--gold-hover); }
.body-training-state.waiting { color: #e1ad80; }

.body-development-resources {
    display: grid;
    gap: 5px;
    margin: 0;
    padding: 5px 0;
    border-block: 1px solid #37312a;
    font-size: 13px;
}

.body-development-resources > div, .body-development-resources dd {
    display: flex;
    align-items: baseline;
    flex-wrap: wrap;
    gap: 2px 10px;
}

.body-development-resources > div { justify-content: space-between; }
.body-development-resources dt { color: var(--text); }
.body-development-resources dd { justify-content: flex-end; margin: 0; text-align: right; }
.body-development-resources strong { color: var(--heading); font-variant-numeric: tabular-nums; }
.body-development-resources small { color: #a69d93; font-size: 12px; }
.body-development-hint { display: grid; gap: 3px; flex: 1 1 215px; }
.body-development-hint:empty, .body-development-actions:empty, .body-development-next:not(:has(span, button)) { display: none; }
.body-development-actions { display: flex; flex-wrap: wrap; gap: 6px; }
.body-development-actions .btn { height: auto; min-height: 32px; padding: 4px 10px; font-size: 13px; }
.body-development .field-error { margin: 0; }

.body-parts-list { display: grid; gap: 3px; }
.body-parts-list h3 { margin: 2px 0; color: var(--heading); font: 500 15px var(--font-title); }
.body-part-entry { min-width: 0; border: 1px solid #35312c; border-radius: 6px; background: rgba(0, 0, 0, .29); }
.body-part-entry:has(.body-part-row:not(:disabled)):hover { border-color: var(--border-hover); }
.body-part-entry.selected { border-color: #8b6c44; background: linear-gradient(115deg, rgba(86, 61, 32, .32), rgba(0, 0, 0, .26) 80%); }
.body-part-entry.requirement-focus { outline: 1px solid var(--heading); outline-offset: 2px; }

.body-part-row {
    display: grid;
    grid-template-columns: auto minmax(5.25rem, .34fr) minmax(0, 1fr) auto auto;
    align-items: center;
    gap: 4px 9px;
    width: 100%;
    min-height: 34px;
    padding: 2px 10px 2px 6px;
    border: 0;
    border-radius: inherit;
    background: transparent;
    color: var(--text);
    font: inherit;
    text-align: left;
    cursor: pointer;
}

.body-part-row:focus-visible { outline: 2px solid var(--gold-hover); outline-offset: -2px; }
.body-part-row:disabled { cursor: default; }
.body-part-row .part-medal { width: 28px; height: 28px; font-size: 15px; }
.body-part-row .part-medal::before { inset: 5px; }
.body-part-name { font-size: 14px; font-weight: 600; }
.body-part-effect { color: var(--text); font-size: 13px; }
.body-part-state { color: var(--gold-hover); font-size: 12px; }
.body-part-chevron {
    grid-column: -2;
    width: 7px;
    height: 7px;
    margin: 0 3px 0 0;
    border: solid #a69d93;
    border-width: 0 1px 1px 0;
    transform: rotate(-45deg);
}
.body-part-rising {
    position: relative;
    grid-column: -2;
    width: 70px;
    height: 28px;
    overflow: hidden;
    pointer-events: none;
}

.body-part-rising i {
    position: absolute;
    bottom: -12px;
    width: 13px;
    height: 13px;
    border-top: 2px solid var(--gold-hover);
    border-left: 2px solid var(--gold-hover);
    filter: drop-shadow(0 0 4px rgba(201, 164, 104, .55));
    animation: body-part-rise 2.4s linear infinite;
}

.body-part-rising i:nth-child(1) { left: 8px; }
.body-part-rising i:nth-child(2) { left: 29px; animation-delay: -.8s; }
.body-part-rising i:nth-child(3) { left: 50px; animation-delay: -1.6s; }

@keyframes body-part-rise {
    0% { transform: translateY(0) rotate(45deg); opacity: 0; }
    28% { opacity: .9; }
    72% { opacity: .55; }
    100% { transform: translateY(-43px) rotate(45deg); opacity: 0; }
}

@media (prefers-reduced-motion: reduce) {
    .body-part-rising i { bottom: 5px; transform: rotate(45deg); animation: none; }
}

.body-pledge-dialog { display: grid; gap: 10px; width: min(100%, 420px); }
.body-pledge-dialog h2, .body-pledge-dialog p { margin: 0; }
.body-pledge-level { color: var(--heading); font-size: 13px; }
.body-part-gains { display: flex; flex-wrap: wrap; gap: 4px 12px; color: var(--text); font-size: 12px; }
.body-part-gains strong { margin-left: 2px; color: var(--gold-hover); white-space: nowrap; }

/* ---------- Экипировка: кукла ---------- */
/* Сцена — контейнер по обоим размерам: кукла вписывается в неё целиком, сохраняя пропорции
   холста 1000 × 600 (см. GearDoll.razor). Внутри куклы всё в долях её ширины (cqw) —
   гнёзда, подписи, нити, — поэтому она масштабируется целиком и нити не промахиваются. */
.gear-stage { flex: 1; min-width: 0; min-height: 0; container-type: size; display: flex; align-items: center; justify-content: center; }
.gear-doll { position: relative; width: min(100cqw, 100cqh * 5 / 3, 1150px); aspect-ratio: 5 / 3; container-type: inline-size; }
/* Крайние подписи и гнёзда могут выходить за границу холста куклы. */
.gear-doll > .phaser-ui-layer { inset-block: -24px; }
/* Под копытами — тёплое пятно света: конь стоит на месте, а не висит в пустоте. */
.gear-doll::before {
    content: "";
    position: absolute;
    left: 22%;
    right: 22%;
    bottom: 1%;
    height: 22%;
    background: radial-gradient(ellipse at center, rgba(201, 164, 104, .10), transparent 70%);
    pointer-events: none;
}
.gear-horse { position: absolute; left: 17%; top: 7.5%; width: 66%; }

.gear-threads { position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible; pointer-events: none; }
.thread polyline { fill: none; stroke: rgba(176, 141, 87, .25); stroke-width: 1; stroke-dasharray: 3 4; vector-effect: non-scaling-stroke; transition: stroke 150ms; }
.thread circle { fill: #2a2217; stroke: rgba(176, 141, 87, .45); stroke-width: 1; vector-effect: non-scaling-stroke; }
.thread.worn polyline { stroke: rgba(201, 164, 104, .5); stroke-dasharray: none; }
.thread.worn circle { fill: var(--gold); stroke: none; filter: drop-shadow(0 0 3px rgba(201, 164, 104, .9)); }
.thread:is(.lit, .compatible) polyline { stroke: var(--gold-hover); stroke-dasharray: none; }
.thread:is(.lit, .compatible) circle { fill: var(--gold-hover); stroke: none; }
.thread.accepts polyline { stroke: var(--gold-hover); stroke-dasharray: 4 4; animation: gear-march .7s linear infinite; }
@keyframes gear-march { to { stroke-dashoffset: -8; } }

/* Гнездо: кованая ячейка, как желоб полосы на заставке, — тёмная, в рамке, с тенью внутрь.
   По пять гнёзд с каждой стороны: гнездо с подписью — 7,6 + ~3,5 доли ширины, шаг — 12. */
.gear-slot { position: absolute; width: 18cqw; transform: translate(-50%, -3.8cqw); display: flex; flex-direction: column; align-items: center; }
.gear-socket {
    width: 7.6cqw;
    aspect-ratio: 1;
    border: 1px solid var(--border-hover);
    border-radius: 8px;
    background: var(--art, none) center / 78% no-repeat, radial-gradient(circle at 50% 35%, #26221d, #0b0a09 75%);
    box-shadow: inset 0 1px 4px rgba(0, 0, 0, .9), 0 0 0 1px rgba(0, 0, 0, .8), 0 6px 14px rgba(0, 0, 0, .6);
    display: flex;
    align-items: center;
    justify-content: center;
    color: #4d4843;
    font-size: 2.4cqw;
    transition: border-color 150ms, box-shadow 150ms;
}
.gear-socket.empty { border-style: dashed; border-color: #3a3632; }
.gear-socket.filled { border-color: rgba(176, 141, 87, .7); cursor: grab; }
.gear-slot.lit .gear-socket.filled, .gear-socket.filled:focus-visible {
    border-color: var(--gold-hover);
    box-shadow: inset 0 1px 4px rgba(0, 0, 0, .9), 0 0 0 1px rgba(0, 0, 0, .8), 0 0 14px rgba(201, 164, 104, .4);
    outline: none;
}
.gear-socket.selected { outline: 2px solid var(--gold); outline-offset: 2px; }
/* Вещь в руке подходит сюда — гнездо тлеет, пока её не бросят. Свечение — отдельный слой,
   и дышит у него только прозрачность: анимация самой тени перерисовывала бы куклу каждый кадр. */
.gear-socket { position: relative; }
.gear-slot:is(.accepts, .compatible) .gear-socket { border: 1px solid var(--gold-hover); color: var(--gold); }
.gear-slot.accepts .gear-socket::after { content: ""; position: absolute; inset: -1px; border-radius: 8px; pointer-events: none;
    box-shadow: 0 0 18px rgba(201, 164, 104, .7); opacity: .3; will-change: opacity; animation: smoulder 1.1s ease-in-out infinite alternate; }

/* Подпись — над гнездом и вне потока: гнездо по-прежнему стоит центром на своей высоте (автор, 09.10.2026). */
.gear-caption { position: absolute; bottom: 100%; left: 0; right: 0; margin-bottom: .25cqw; display: flex; flex-direction: column; align-items: center; text-align: center; }
.gear-slot:is(.accepts, .compatible) .gear-slot-name { color: var(--gold-hover); }
.gear-slot-name { font: 500 clamp(9px, 1.45cqw, 13px)/1 var(--font-body); color: var(--text); }

@media (prefers-reduced-motion: reduce) {
    .thread.accepts polyline, .gear-slot.accepts .gear-socket::after { animation: none; }
}

.subtabs { display: flex; gap: 6px; justify-content: center; width: 100%; }
.subtabs button { flex: 0 0 auto; min-width: 96px; height: 30px; padding: 0 14px; border: 1px solid #2e2c2a; border-radius: 15px; background: rgba(0, 0, 0, .35); color: var(--text-2, #9a9188); cursor: pointer; }
.subtabs button.active { border-color: var(--gold); color: var(--heading); }
.subtabs.overview-tabs { justify-content: flex-start; flex-wrap: wrap; gap: 4px; }
.subtabs.overview-tabs button { min-width: 0; height: 26px; padding: 0 10px; font-size: 12px; }
/* «Персонаж»: вкладки страниц прибиты к верху, под ними прокручивается страница. */
.view-body:has(.self-screen) { align-items: stretch; overflow: hidden; }
.self-screen { flex: 1; min-width: 0; min-height: 0; display: flex; flex-direction: column; }
.self-tabs { flex: none; flex-wrap: wrap; padding: 8px 0; }
.self-page { flex: 1; min-height: 0; display: flex; overflow-y: auto; }
.self-page > .self-view { height: auto; }
.self-sheet { width: 100%; max-width: 640px; margin: 0 auto; display: flex; flex-direction: column; gap: 8px; padding: 0 10px 10px; }
/* «Осмотреть» игрока (02.11.2026): слева он во весь рост, справа медали или счёт съеденного. Узко — одно под другим. */
.view-body:has(.inspect-screen) { align-items: stretch; overflow: hidden; }
.inspect-screen { display: flex; flex-direction: column; gap: 8px; width: 100%; height: 100%; min-height: 0; container-type: inline-size; }
.inspect-body { flex: 1 1 auto; min-height: 0; display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); grid-template-rows: minmax(0, 1fr); gap: 12px 24px; overflow-y: auto; }
@container (max-width: 640px) { .inspect-body { grid-template-columns: minmax(0, 1fr); grid-template-rows: 300px auto; } }
.inspect-figure { display: flex; align-items: flex-end; justify-content: center; min-height: 0; container-type: size; }
.inspect-figure .figure { width: 100%; max-width: min(600px, (100cqh - 60px) * 660 / 510); display: flex; flex-direction: column; justify-content: flex-end; }
.inspect-figure .figure .horse-portrait, .inspect-figure .figure .creature-portrait { flex: 1 1 auto; min-height: 140px; width: 100%; height: auto; }
.inspect-side { display: flex; flex-direction: column; gap: 12px; min-width: 0; }
.medal-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(84px, 1fr)); gap: 12px 8px; }
.medal { display: flex; flex-direction: column; align-items: center; gap: 4px; text-align: center; cursor: default; }
.medal img { width: 64px; height: 64px; object-fit: contain; filter: drop-shadow(0 2px 3px rgba(0, 0, 0, .5)); user-select: none; }
.medal-title { font-size: 12px; line-height: 1.2; color: var(--heading); }
/* Летопись (FeatsStats, 02.11.2026): плитки с крупными числами, ниже звери по видам и последние жертвы. */
.feats { display: flex; flex-direction: column; gap: 12px; width: 100%; min-width: 0; }
/* Перенос — гибкой лентой: неполный ряд растягивается во всю ширину, края рядов совпадают. Подпись — всегда в две строки
   высотой, число прижато книзу: числа стоят на одной линии, сколько бы строк ни заняла подпись. */
.feat-tiles { display: flex; flex-wrap: wrap; gap: 8px; }
.feat-tile { flex: 1 1 110px; display: flex; flex-direction: column; align-items: center; justify-content: space-between; gap: 2px; padding: 8px 6px;
    border: 1px solid var(--border); border-radius: 6px; background: rgba(0, 0, 0, .18); text-align: center; }
.feat-tile span { font-size: 12px; line-height: 1.2; min-height: 2.4em; display: flex; align-items: center; color: var(--text-muted); }
.feat-tile b { font: 500 26px var(--font-title); color: #e2c48c; }
.feat-block { border: 1px solid var(--border); border-radius: 6px; padding: 8px 12px; background: rgba(0, 0, 0, .12); }
.feat-block > header { display: flex; align-items: baseline; gap: 10px; margin-bottom: 6px; }
.feat-block h3 { margin: 0; font: 500 18px var(--font-title); color: var(--heading); }
.feat-block > footer { display: flex; align-items: center; gap: 10px; margin-top: 6px; font-size: 12px; }
.feat-table { width: 100%; border-collapse: collapse; font-size: 13px; }
.feat-table th { text-align: left; font-weight: 500; color: var(--text-muted); padding: 4px 6px; border-bottom: 1px solid var(--border); background: rgba(255, 255, 255, .03); }
.feat-table td { padding: 3px 6px; border-bottom: 1px solid rgba(255, 255, 255, .04); color: var(--heading); vertical-align: middle; }
.feat-table .num { text-align: right; width: 1%; white-space: nowrap; }
.feat-icon { width: 34px; }
.feat-icon .creature-portrait { width: 28px; height: 28px; display: block; }
.feat-badge { display: inline-block; margin-left: 6px; padding: 0 8px; border: 1px solid #6b6258; border-radius: 10px; font-size: 11px; line-height: 18px; color: var(--heading); white-space: nowrap; }
.feat-badge.gold { border-color: var(--gold); color: #e2c48c; }

/* «Персонаж → Квесты». */
.quest-book { align-self: flex-start; padding-top: 10px; }
.quest-book .craft-skills-title { margin: 6px 0 0; }
.quest-empty { margin: 0; }
.quest-card.tracked { border-color: #5a3a10; box-shadow: inset 0 0 0 1px rgba(224, 165, 58, .25); }
.quest-bar { display: block; margin: 5px 0 2px; }
.quest-track { flex-shrink: 0; width: 92px; }
.quest-track.on { border-color: #e0a53a; color: #e0a53a; }
.quest-actions { flex-shrink: 0; display: flex; flex-direction: column; gap: 4px; }

/* «Персонаж → Карта мира» (27.10.2026): холст на всю страницу вкладки, как дерево навыков; тянут мышью (world-map.js).
   Справа — список мест и свои метки (31.10.2026); на узком экране — под картой. */
.world-map-page { flex: 1; min-width: 0; display: flex; gap: 10px; margin: 0 10px 10px; }
.world-map { position: relative; flex: 1; min-width: 0; min-height: 320px; overflow: hidden; border: 1px solid #2e2c2a;
    border-radius: 6px; background: #0d0c0b; }
.world-map canvas { position: absolute; inset: 0; width: 100%; height: 100%; display: block; cursor: grab; touch-action: none; }
.world-map canvas.dragging { cursor: grabbing; }
.phaser-world-map { position: absolute; inset: 0; pointer-events: none; visibility: hidden; }
.phaser-world-map[data-ui-renderer="phaser"] { visibility: inherit; }
.phaser-world-map[data-ui-renderer="phaser"] + canvas { opacity: 0; }
.world-map-buttons { position: absolute; right: 8px; bottom: 8px; z-index: 2; display: flex; flex-direction: column; gap: 3px; }
.world-map-buttons button { width: 26px; height: 26px; padding: 0; border: 1px solid #5a3a10; border-radius: 5px; background: rgba(12, 10, 8, .78);
    color: #d9c8a4; font: 600 17px/1 var(--font-body); cursor: pointer; }
.world-map-buttons button:hover { color: #f3e8d0; border-color: var(--gold); }
/* Подсказка гекса — у курсора; скрыта видимостью, а не display: так у неё есть размер, и она не вылезает за край. */
.world-map-tip { position: absolute; left: 0; top: 0; z-index: 3; pointer-events: none; visibility: hidden; white-space: nowrap; padding: 6px 9px;
    border: 1px solid #5a3a10; border-radius: 5px; background: rgba(12, 10, 8, .92); font-size: 13px; line-height: 1.35; color: #b8ad9c; }
.world-map-tip.shown { visibility: visible; }
.world-map-tip-title { color: #f3e8d0; font-weight: 600; }
/* Свои координаты и куда растут q и r — в левом нижнем углу карты (01.11.2026). */
.world-map-coords { position: absolute; left: 8px; bottom: 8px; z-index: 2; display: flex; flex-direction: column; padding: 4px 8px;
    border: 1px solid #5a3a10; border-radius: 5px; background: rgba(12, 10, 8, .78); font-size: 12px; color: #d9c8a4; font-variant-numeric: tabular-nums; }
.world-map-axes { color: var(--text-2); }
.world-map-goto { display: flex; flex-direction: column; gap: 4px; padding-bottom: 8px; border-bottom: 1px solid rgba(201, 164, 104, .18); }
.world-map-goto-row { display: flex; align-items: center; gap: 6px; }
.world-map-goto-row label { display: flex; align-items: center; gap: 3px; }
.world-map-goto-row input { width: 52px; box-sizing: border-box; }
.world-map-goto-error { color: #e08a6a; }
.world-map-wait { position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%); margin: 0; }
.world-map-places { flex: 0 0 250px; min-height: 0; overflow-y: auto; display: flex; flex-direction: column; gap: 4px; padding: 8px;
    border: 1px solid #2e2c2a; border-radius: 6px; background: rgba(12, 10, 8, .5); font-size: 13px; }
.world-map-places-title { margin: 8px 0 2px; color: var(--heading); font-family: var(--font-title); font-size: 14px; letter-spacing: .03em; }
.world-map-places-title:first-child { margin-top: 0; }
.world-map-hint, .world-map-empty { margin: 0; }
.world-map-new { display: flex; flex-direction: column; gap: 5px; padding-bottom: 8px; border-bottom: 1px solid rgba(201, 164, 104, .18); }
.world-map-new input { width: 100%; box-sizing: border-box; }
.world-map-new-buttons { display: flex; gap: 6px; }
.world-place { display: flex; align-items: center; gap: 4px; }
.world-place-go { all: unset; flex: 1; min-width: 0; display: flex; align-items: center; gap: 6px; padding: 3px 4px; border-radius: 4px; cursor: pointer; }
.world-place-go:hover, .world-place-go:focus-visible { background: rgba(201, 164, 104, .12); color: var(--heading); }
.world-place-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.world-place-far { flex: none; color: var(--text-2); font-variant-numeric: tabular-nums; }
.world-place .btn.small { flex: none; padding: 2px 7px; }
.world-place-track.on { border-color: #b994f0; color: #d6c4f2; }
.world-place.tracked .world-place-name { color: #e2d3fa; }
/* Значки в списке — те же цвета, что метки на холсте (world-map.js). */
.world-place-icon { flex: none; width: 9px; height: 9px; border: 1px solid #2a1e12; }
.world-place-icon.k-city { background: #dc3228; border-color: #fff0c8; }
.world-place-icon.k-village { background: #f0d278; }
.world-place-icon.k-home { background: #8fd296; transform: rotate(45deg) scale(.85); }
.world-place-icon.k-portal { border: 2px solid #c9a468; border-radius: 50%; background: rgba(159, 208, 245, .85); }
.world-place-icon.k-quest { border: 2px solid #f05a46; border-radius: 50%; background: none; }
.world-place-icon.k-pin { background: #a582d8; clip-path: polygon(0 0, 100% 35%, 0 70%); border: none; }
.world-place.tracked .k-pin { background: #c9a0ff; }
@media (max-width: 760px) {
    .world-map-page { flex-direction: column; }
    .world-map-places { flex: none; max-height: 40vh; }
}

/* ---------- Дерево навыков ---------- */
/* Окно в поле 2200 × 1500 (см. SkillTree.razor): поле больше окна, его тянут мышью или пальцем —
   двигает его js/skilltree.js через transform. Поэтому всё, что на поле дышит и светится,
   анимирует только прозрачность своего слоя: анимация тени перерисовывала бы всё поле
   каждый кадр, и перетаскивание тормозило (замер 28.09: 60 к/с рывками против ровных 72). */
.skills-layout {
    display: grid;
    grid-template-rows: auto minmax(270px, 1fr) auto;
    flex: 1;
    min-width: 0;
    min-height: 0;
    gap: 8px;
    margin: 0 10px 10px;
    container: skills / inline-size;
}
.tree-view { position: relative; min-width: 0; overflow: hidden; border: 1px solid #2e2c2a; border-radius: 6px; cursor: grab; touch-action: none; outline: none;
    background: radial-gradient(ellipse at 50% 45%, #1a1714, #0d0c0b 70%); }
.tree-view.dragging { cursor: grabbing; }
.tree-view:focus-visible { box-shadow: inset 0 0 0 1px var(--gold); }
.tree-world { position: absolute; left: 0; top: 0; width: 2200px; height: 1500px; will-change: transform; }
.tree-world.glide { transition: transform 380ms cubic-bezier(.2, .7, .2, 1); }
/* Еле заметная точечная гравировка: видно, что поле едет, даже когда в кадре пусто. */
.tree-world::before { content: ""; position: absolute; inset: 0; pointer-events: none;
    background-image: radial-gradient(circle, rgba(176, 141, 87, .10) 1px, transparent 1.5px); background-size: 40px 40px; }
/* Дымка по краям: за краем окна дерево продолжается. */
.tree-fog { position: absolute; inset: 0; z-index: 1; pointer-events: none; border-radius: 6px; box-shadow: inset 0 0 30px 10px #0b0a09; }

.tree-horse { position: absolute; pointer-events: none; }
.tree-horse::after { content: ""; position: absolute; left: 10%; right: 10%; bottom: -30px; height: 90px;
    background: radial-gradient(ellipse at center, rgba(201, 164, 104, .08), transparent 70%); }

.tree-links { position: absolute; left: 0; top: 0; overflow: visible; pointer-events: none; }
.tree-link path { fill: none; stroke: var(--c); stroke-width: 1.8; stroke-dasharray: 5 6; opacity: .62; transition: opacity 150ms; }
.tree-link.met path { stroke-dasharray: none; opacity: .85; filter: drop-shadow(0 0 3px var(--c)); }
.tree-link.lit path { opacity: 1; stroke-width: 2.2; }
.tree-link circle { fill: #141210; stroke: var(--c); stroke-width: 1.5; }
.tree-link text { fill: var(--c); font: 600 16px var(--font-body); text-anchor: middle; dominant-baseline: central; }
.tree-link.met circle { fill: var(--c); filter: drop-shadow(0 0 4px var(--c)); }
.tree-link.met text { fill: #141210; }

/* Медальон части тела — кованый, уровень внутри, кольцо цвета части. */
.tree-medal { position: absolute; width: 0; height: 0; }
.tree-medal b { position: absolute; left: 0; top: 0; transform: translate(-50%, -50%); width: 36px; height: 36px; border-radius: 50%;
    display: flex; align-items: center; justify-content: center; font: 600 15px var(--font-title); color: var(--heading); font-variant-numeric: tabular-nums;
    background: radial-gradient(circle at 50% 35%, #2c2620, #0b0a09 75%);
    border: 2px solid var(--c); box-shadow: 0 0 0 1px #000, 0 0 10px color-mix(in srgb, var(--c) 55%, transparent), inset 0 1px 3px rgba(0, 0, 0, .9); }
.tree-medal span { position: absolute; top: 0; white-space: nowrap; font: 600 16px var(--font-body);
    color: var(--c); text-shadow: 0 0 3px #000, 0 0 3px #000, 0 0 6px #000; }
.tree-medal.right span { left: 24px; transform: translateY(-50%); }
.tree-medal.left span { right: 24px; transform: translateY(-50%); }
.tree-medal.up span { left: 0; top: -30px; transform: translate(-50%, -50%); }
.tree-medal.down span { left: 0; top: 30px; transform: translate(-50%, -50%); }

/* Гнездо навыка — квадратное, как кнопки на панели действий: название внутри, снизу;
   в углах ряд (I, II) и ✦ у активных — они появятся на панели действий. */
.tree-skill { position: absolute; transform: translate(-50%, -50%); width: 108px; height: 108px; padding: 6px; border-radius: 8px;
    border: 1px dashed #665744; color: #e1d4c0; font: 600 18px/1.08 var(--font-body); text-align: center; cursor: pointer;
    display: flex; align-items: flex-end; justify-content: center;
    background: radial-gradient(circle at 50% 35%, #26221d, #0b0a09 75%);
    box-shadow: inset 0 1px 4px rgba(0, 0, 0, .9), 0 0 0 1px rgba(0, 0, 0, .8), 0 6px 14px rgba(0, 0, 0, .6);
    transition: border-color 150ms, color 150ms; }
.tree-skill .tier { position: absolute; left: 7px; top: 5px; font: 600 15px var(--font-title); font-style: normal; color: #b3a48c; }
.tree-skill .kind { position: absolute; right: 7px; top: 4px; font-style: normal; font-size: 15px; color: #b3a48c; }
.tree-skill:hover, .tree-skill:focus-visible { border-color: var(--border-hover); color: var(--text); outline: none; }
/* Можно открыть — гнездо тлеет, как под подходящей вещью в «Экипировке». Свечение — отдельный слой. */
.tree-skill.ready { border: 1px solid var(--gold-hover); color: var(--heading); }
.tree-skill.ready::after { content: ""; position: absolute; inset: -1px; border-radius: 8px; pointer-events: none;
    box-shadow: 0 0 18px rgba(201, 164, 104, .7); opacity: .3; will-change: opacity; animation: smoulder 1.2s ease-in-out infinite alternate; }
.tree-skill.ready .tier, .tree-skill.ready .kind { color: var(--gold); }
/* Открыт — горит золотом. */
.tree-skill.owned { border: 1px solid var(--gold); color: #f0e2c4;
    background: radial-gradient(circle at 50% 35%, #4a3a22, #1a140c 80%);
    box-shadow: inset 0 1px 4px rgba(0, 0, 0, .6), 0 0 0 1px #000, 0 0 16px rgba(201, 164, 104, .55); }
.tree-skill.owned .tier, .tree-skill.owned .kind { color: var(--gold-hover); }
.tree-skill.selected { outline: 2px solid var(--gold); outline-offset: 3px; }
@keyframes smoulder { from { opacity: .25; } to { opacity: 1; } }
/* Нарисованная иконка навыка (см. ActionArt и «Иконки действий» выше): рамка кованая, а не пунктир.
   Закрыт — тёмный и серый; можно открыть — железо и тление; открыт — рамка в золоте и свечение. */
.tree-skill.art, .tree-skill.art.ready, .tree-skill.art.owned { padding: 0; border: 0; background: #0e0d0c; }
.tree-skill.art::before { border-width: 17px; border-image-width: 17px; }
.tree-art { border-radius: 8px; }
.tree-skill.art .tier { left: 19px; top: 7px; z-index: 2; text-shadow: 0 0 3px #000, 0 1px 2px #000; }
.tree-skill.art .kind { right: 19px; top: 6px; z-index: 2; text-shadow: 0 0 3px #000, 0 1px 2px #000; }
.tree-skill.art > span { position: relative; z-index: 2; width: 100%; padding: 13px 3px 8px; border-radius: 0 0 8px 8px;
    background: linear-gradient(transparent, rgba(0, 0, 0, .94) 28%); text-shadow: 0 0 3px #000, 0 1px 2px #000; overflow-wrap: anywhere; }
.tree-skill.art.locked .tree-art { filter: grayscale(.85) brightness(.65); }
.tree-skill.art.locked::before { filter: brightness(.74); }
.tree-skill.art.ready .tree-art { filter: brightness(.85); }
.tree-skill.art.owned::before { filter: var(--frame-gold); }
.tree-skill.art.owned { box-shadow: 0 0 0 1px #000, 0 0 16px rgba(201, 164, 104, .55); }
.tree-skill.art:hover::before, .tree-skill.art:focus-visible::before { filter: var(--frame-gold) brightness(1.1); }
.tree-skill.art:hover .tree-art { filter: none; }

/* Управление камерой вынесено за поле, чтобы не закрывать узлы. */
.tree-toolbar { display: flex; align-items: center; gap: 8px; padding: 0 2px; }
.tree-home { height: 28px; padding: 0 10px; border-radius: 6px;
    border: 1px solid var(--border-hover); background: rgba(10, 9, 8, .85); color: var(--text); font: 13px var(--font-body); cursor: pointer; }
.tree-home:hover { border-color: var(--gold); color: var(--heading); }
/* Кнопки масштаба образуют одну группу. */
.tree-zoom { display: flex; }
.tree-zoom button { width: 31px; height: 28px; padding: 0; border: 1px solid var(--border-hover); background: rgba(10, 9, 8, .85);
    color: var(--text); font: 16px/1 var(--font-body); cursor: pointer; }
.tree-zoom button:first-child { border-radius: 6px 0 0 6px; border-right-width: 0; }
.tree-zoom button:last-child { border-radius: 0 6px 6px 0; }
.tree-zoom button:hover { border-color: var(--gold); color: var(--heading); }
.tree-hint { margin-inline-end: auto; color: #a69d93; font-size: 12px; }

.tree-detail {
    display: grid;
    gap: 4px;
    padding: 10px 14px;
    border: 1px solid #504331;
    border-radius: 8px;
    background: linear-gradient(140deg, rgba(79, 58, 34, .2), rgba(12, 11, 10, .55) 65%);
    text-align: left;
}
.tree-detail-head { display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: 4px 12px; }
.tree-detail-head h3 { margin: 0; color: var(--heading); font: 500 18px var(--font-title); }
.tree-detail-idea { margin: 0; color: var(--text); font-size: 13px; }
.tree-detail-columns { display: grid; grid-template-columns: minmax(0, 1.25fr) minmax(0, .75fr) minmax(0, 1fr); gap: 8px 14px; margin-top: 4px; }
.tree-detail h4 { margin: 0 0 4px; color: var(--heading); font: 600 13px var(--font-body); }
.tree-requirements dl { display: grid; gap: 3px; margin: 0; font-size: 13px; }
.tree-requirements dl > div { display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: 2px 8px; }
.tree-requirements dd { margin: 0; font-variant-numeric: tabular-nums; }
.tree-price, .tree-next { padding-inline-start: 14px; border-inline-start: 1px solid #40372e; }
.tree-price > span, .tree-price strong, .tree-next > span { display: block; font-size: 13px; }
.tree-price strong { color: var(--heading); font-size: 15px; }
.tree-price strong span { color: var(--text); font-size: 13px; font-weight: 400; }
.tree-next { display: grid; align-content: center; justify-items: start; gap: 4px; }
.tree-next .btn { min-height: 32px; height: auto; padding: 4px 10px; white-space: normal; font-size: 13px; }
.tree-next > span { color: #a69d93; }
.tree-detail .met { color: #b1cc8c; }
.tree-detail .lacking { color: #e2a292; }
.tree-detail .field-error { margin: 2px 0 0; }
.tree-detail-owned { color: var(--gold-hover); font-size: 13px; }
.tree-detail-empty { margin: 0; padding: 4px 2px; color: #a69d93; font-size: 13px; }
@container skills (max-width: 610px) {
    .tree-detail-columns { grid-template-columns: 1.2fr 1fr; }
    .tree-next { grid-column: 1 / -1; grid-template-columns: auto 1fr; align-items: center; padding: 0; border: 0; }
}
@media (prefers-reduced-motion: reduce) {
    .tree-skill.ready::after { animation: none; opacity: .6; }
    .tree-world.glide { transition: none; }
}

/* ---------- Городской склад ---------- */
.storage-box { flex: 1 1 auto; min-height: 0; width: 100%; margin: 0; display: flex; flex-direction: column; }
.storage-box .box-content { flex: 1; min-height: 0; overflow-y: auto; text-align: left; }
/* Склады, сундуки, рамы и чаны прокручиваются вместе с экраном, чтобы список не сжался до нуля. */
.building-screen.storage > .storage-box,
.building-screen > .tannery-box,
.building-screen.storage > .storage-box > .box-content,
.tannery-box > .tannery-slots { flex: 1 0 auto; }
.building-screen.storage > .storage-box > .box-content,
.tannery-box > .tannery-slots { overflow: visible; }
.storage-hint { margin: 0; padding: 5px 8px; }
.storage-hint .btn { margin-inline-start: 4px; }
/* Склад надела: три секции вкладками над ящиком (27.10.2026); на вкладку можно бросить кучку. */
.store-tabs { display: flex; gap: 4px; }
.store-tabs .btn { flex: 1 1 0; min-width: 0; height: 30px; padding: 0 8px; white-space: nowrap; }
.store-tabs .btn .count { margin-left: 5px; font-size: 12px; opacity: .7; font-variant-numeric: tabular-nums; }

/* ---------- Торговля ---------- */
/* Две половины одной высоты: прилавок слева, товар торговца справа. Прокручиваются сами —
   поэтому сам экран не прокручивается (иначе полос две), а половины берут всю высоту, что
   осталась над кнопками: кнопки всегда на виду. */
.building-screen:has(> .trade-halves) { overflow: hidden; }
.trade-halves { display: grid; grid-template-columns: 1fr 1fr; grid-template-rows: minmax(0, 1fr); gap: 12px; flex: 1 1 0; min-height: 0; }
/* На низком экране место под строку-предупреждение не держим, а пояснение уходит в подсказку кнопки. */
@media (max-height: 900px) {
    .building-screen:has(> .trade-halves) .building-note.hidden,
    .building-screen:has(> .trade-halves) .trade-hint,
    .building-screen:has(> .trade-halves) .field-error:empty { display: none; }
}
.trade-box { min-height: 0; margin: 0; display: flex; flex-direction: column; }
.trade-box .box-content { flex: 1; min-height: 0; overflow-y: auto; text-align: left; }
.counter-line { display: flex; align-items: center; gap: 8px; padding: 4px; border-bottom: 1px solid #2e2c2a; }
.counter-line .entity-info { flex: 1; min-width: 0; }
.counter-line .range-value { width: 70px; }
.counter-remove { flex-shrink: 0; width: 30px; padding: 0; }
/* Прилавок делится на «Продаёшь» и «Покупаешь». */
.counter-group { padding: 6px 4px 2px; text-transform: uppercase; letter-spacing: .05em; font-size: 11px; }
/* Витрина: товар тащат на прилавок; уже выложенное приглушено, как в инвентаре. */
.stock-line { cursor: grab; }
.stock-line:active { cursor: grabbing; }
.stock-line.on-counter { opacity: 0.65; filter: grayscale(0.6); }
.stock-line .counter-note { color: var(--gold); }
/* Обмен с игроком (01.11.2026): карточки сум на столе, у своих — ползунок «сколько» и ×; согласие — золотом. */
.exchange .trade-box .market-offer { padding: 2px 4px; }
.exchange-amount { flex: 0 0 80px; width: 80px; }
.exchange-agreed { color: var(--gold); font-weight: 600; }
@media (max-width: 700px) {
    .building-screen:has(> .trade-halves) { overflow-y: auto; }
    .trade-halves { grid-template-columns: 1fr; grid-template-rows: none; flex: none; }
    .trade-box { height: 260px; }
}

/* ---------- Разговор ---------- */
/* Реплика собеседника с его портретом, под ней — нумерованные ответы игрока. */
.dialog { display: flex; flex-direction: column; gap: 8px; text-align: left; }
.dialog-reply { margin: 0; color: #9a9188; font-style: italic; }
.dialog-say { display: flex; gap: 10px; align-items: flex-start; padding: 10px; border: 1px solid #2e2c2a; border-radius: 6px; background: rgba(0, 0, 0, .35); }
.dialog-say .entity-icon { width: 56px; height: 56px; margin: 0; }
.dialog-say p { margin: 2px 0 0; color: var(--heading); line-height: 1.45; }
.dialog-speaker { font-family: var(--font-title); color: var(--gold); }
.dialog-options { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 4px; }
.dialog-option { width: 100%; text-align: left; padding: 7px 10px; border: 1px solid #2e2c2a; border-radius: 6px; background: rgba(0, 0, 0, .2); color: var(--text-1, #d8cfc2); cursor: pointer; }
.dialog-option:hover:not(:disabled) { border-color: var(--gold); background: rgba(201, 164, 104, .08); }
.dialog-option:disabled { opacity: .5; cursor: default; }
.dialog-number { color: var(--gold); margin-right: 4px; }

/* ---------- Банк ---------- */
/* Поле «сколько снять» стоит в строке и ширины не занимает: иначе оно раздавило бы название. */
.withdraw { display: flex; gap: 6px; align-items: center; flex-shrink: 0; }
.withdraw input[type=number] { width: 72px; height: 32px; }

/* ---------- Эффекты ---------- */
.effect-badges { display: flex; flex-wrap: wrap; gap: 3px; margin: 2px 0; justify-content: inherit; }
.stage-caption .effect-badges { justify-content: center; }
/* Плитка со знаком, сила числом в углу (автор, 09.10.2026, вид «Б»). Рамка по роду: помогает, мешает, просто так.
   Число вылезает за край на 5 px — отсюда отступ справа и снизу у ряда. */
.effect-badges { padding: 0 5px 5px 0; gap: 7px; }
.effect-badge { position: relative; flex: none; width: 22px; height: 22px; display: flex; align-items: center; justify-content: center; background: #121110; border: 1px solid var(--tone-line); border-radius: 4px; color: var(--tone); }
.effect-badge svg { width: 15px; height: 15px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
/* Число — на тёмной полупрозрачной подложке-пилюле (автор, 09.10.2026): тень букв читалась хуже. */
.effect-badge em { position: absolute; right: -5px; bottom: -5px; background: rgba(0, 0, 0, .7); border-radius: 6px; padding: 1px 3px; font: normal 10px/10px var(--font-body); color: #e8e2d6; font-variant-numeric: tabular-nums; pointer-events: none; }
.effect-good { --tone: #d9b878; --tone-line: #8a6a3e; }
.effect-bad { --tone: #e07a6e; --tone-line: #8a2a24; }
.effect-plain { --tone: #b8b2aa; --tone-line: #4b4744; }
.effect-water { --tone: #8fc4d6; --tone-line: #2f5b6b; }
.effect-safe { --tone: #9cc587; --tone-line: #3f5a33; } /* зелёный, как плашка «Безопасная зона» в клетке */
.effect-ribbon { --tone: #e0344a; --tone-line: #7a1c28; } /* красная ленточка (docs/PVP.md) — красным, как она сама */

/* ---------- Страница /dev/icons: все картинки игры разом ---------- */
.icons-page .panel { max-width: 1100px; }
.icons-group { margin: 26px 0; }
.icons-group h2 { margin: 0 0 2px; font-size: 16px; }
.icons-group > p { margin: 0 0 10px; }
.icons-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(124px, 1fr)); gap: 10px; }
.icon-cell { margin: 0; display: flex; flex-direction: column; align-items: center; gap: 4px; }
/* Клетчатая подложка: на ней видно, где у картинки прозрачность, а где залитое поле.
   Сама картинка — отдельным <img>, иначе фон карточки её бы перекрыл. */
.icon-art {
    width: 100%;
    aspect-ratio: 1;
    border: 1px solid #2e2c2a;
    border-radius: 6px;
    display: flex;
    align-items: center;
    justify-content: center;
    overflow: hidden;
    background-color: #171513;
    background-image:
        linear-gradient(45deg, #23211f 25%, transparent 25%, transparent 75%, #23211f 75%),
        linear-gradient(45deg, #23211f 25%, transparent 25%, transparent 75%, #23211f 75%);
    background-size: 16px 16px, 16px 16px;
    background-position: 0 0, 8px 8px;
}
.icon-art img { width: 82%; height: 82%; object-fit: contain; font-size: 10px; color: #b4614f; }
.icon-cell figcaption { text-align: center; line-height: 1.25; }
.icon-name { display: block; font-size: 12px; color: var(--heading); }
.icon-cell code { font-size: 10px; color: #8a8178; word-break: break-all; }

/* «Осмотреть» вещь (09.10.2026): крупная картинка и строки подсказки (TipView) на странице. */
.item-screen { width: 100%; max-width: 460px; margin: 0 auto; padding: 10px 12px 16px; align-self: flex-start; }
.item-screen-head { display: flex; flex-direction: column; align-items: center; gap: 6px; margin-bottom: 14px; }
/* Свет под вещью — тот же тёплый, что за бойцами на сцене «против», но вдвое слабее и без пятна-пола. */
.item-screen-art { width: 100%; height: 224px; display: flex; align-items: center; justify-content: center;
    background: radial-gradient(ellipse 34% 46% at 50% 50%, rgba(67, 42, 21, .55) 0%, rgba(46, 28, 14, .3) 45%, transparent 78%); }
.item-screen-art img { max-width: 200px; max-height: 200px; filter: drop-shadow(0 6px 10px rgba(0, 0, 0, .55)); }
.item-screen-art .entity-icon { width: 120px; height: 120px; }
.item-screen-head h2 { margin: 0; font-size: 20px; color: var(--heading); text-align: center; }
.item-screen .tip-view { align-items: center; text-align: center; }
.item-screen-head.q-blue h2 { color: #8cc0ea; } .item-screen-head.q-gold h2 { color: #f2c457; } .item-screen-head.q-white h2 { color: #fff; }
.item-screen-head.q-rusty h2 { color: #9c8a78; } .item-screen-head.r-rare h2 { color: #8fd296; } .item-screen-head.r-unique h2 { color: #c3a0f0; }
.tip-view { display: flex; flex-direction: column; gap: 5px; font-size: 13px; color: #9a9188; }
.tip-view .tip-key::after { content: ": "; }
.tip-view .tip-val { color: #d6cfc4; font-weight: 500; }
.tip-view .tip-val.up { color: #8fbf4a; } .tip-view .tip-val.down { color: #e07a6e; }
.tip-view .tip-val.q-rusty { color: #b09a84; } .tip-view .tip-val.q-grey { color: #c8c2b8; } .tip-view .tip-val.q-blue { color: #8cc0ea; }
.tip-view .tip-val.q-gold { color: #f2c457; } .tip-view .tip-val.q-white { color: #fff; }
.tip-view .tip-val.r-rare { color: #8fd296; } .tip-view .tip-val.r-unique { color: #c3a0f0; }
.tip-view .tip-hint { font-size: 11px; color: #6b645c; }

/* Арена (docs/ARENA.md): часы схватки — отсчёт красным крупно, «Бой!» вспышкой, время боя тихо.
   Стоят в панели действий первым элементом и на сцене «против» под словом «против». */
.arena-clock { font-family: var(--font-title); letter-spacing: .08em; font-size: 15px; color: #c9b887; align-self: center; white-space: nowrap; }
.arena-clock.countdown { color: #e0533f; font-size: 22px; }
.arena-clock.betting { color: #c9b887; font-size: 18px; }
.arena-bets { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; margin-top: 6px; }
.arena-bets input { width: 90px; }
.arena-clock.fight { color: #f0d070; font-size: 26px; animation: arena-fight 1s ease-out; }
@keyframes arena-fight { from { transform: scale(1.6); opacity: .2; } to { transform: scale(1); opacity: 1; } }
/* Карточка боя на экране арены: стороны рядом, «против» между ними. */
.arena-sides { display: flex; align-items: center; gap: 16px; }
.arena-side { flex: 1; }
.arena-sides .versus-label { font-size: 12px; }

/* Режим админа в игре (14.10.2026): окно «Игроки / Выдать / Звери / Арена / Гекс» и кнопки над игроком. */
.game-admin { color: #c65f52; border-color: #6e2a24; margin-right: 8px; }
.modal.admin-panel { max-width: 720px; width: 100%; max-height: 90vh; display: flex; flex-direction: column; }
.admin-panel .view-tabs { margin-bottom: 10px; }
.admin-panel .admin-search { margin-bottom: 8px; }
.admin-panel .admin-list { max-height: 50vh; overflow-y: auto; border-top: 1px solid var(--border); }
.admin-panel .admin-row { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 6px; padding: 5px 2px; border-bottom: 1px solid var(--border); }
.admin-panel .admin-row-actions { display: flex; flex-wrap: wrap; gap: 4px; }
.admin-panel .admin-sub { background: var(--panel-inner); padding: 6px 8px; justify-content: flex-start; }
.admin-panel .admin-sub input { width: 110px; }
.admin-panel .admin-online { color: #7d9a4f; margin-right: 4px; }
.admin-panel .admin-line { margin: 4px 0 8px; display: flex; align-items: center; gap: 8px; }
.admin-panel .admin-items { display: grid; grid-template-columns: repeat(auto-fill, minmax(92px, 1fr)); gap: 6px; max-height: 34vh; overflow-y: auto; padding: 2px; }
.admin-panel .admin-item { display: flex; flex-direction: column; align-items: center; gap: 4px; padding: 6px 4px; background: var(--panel-inner); border: 1px solid var(--border); color: var(--text); cursor: pointer; font: inherit; font-size: 12px; }
.admin-panel .admin-item:hover { border-color: var(--border-hover); }
.admin-panel .admin-item.on { border-color: var(--gold); color: var(--heading); }
.admin-panel .admin-item .entity-icon, .admin-panel .admin-item .creature-portrait { width: 40px; height: 40px; }
.admin-panel .admin-item-name { text-align: center; line-height: 1.2; overflow-wrap: anywhere; }
.admin-panel .admin-form { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 8px 12px; align-items: end; margin-top: 10px; }
.admin-panel .admin-form label { display: flex; flex-direction: column; gap: 4px; font-size: 13px; color: var(--text-muted); }
.admin-panel .admin-form .admin-check { flex-direction: row; align-items: center; }
.admin-panel .admin-form .admin-check input { width: auto; height: auto; }
.admin-panel .admin-buttons { display: flex; flex-wrap: wrap; gap: 8px; }
.admin-panel .admin-done { color: #9fb86f; }
.action-box.admin-action { border-color: #6e2a24; }

/* ---------- Король Оленей (docs/DEER-KING.md) ---------- */
/* Прибавка амулета на полосе — той же штриховкой, что срезанное ранами (.bar > i.cut), но цвета своей полосы
   (автор, 16.10.2026: не красной рамкой). */
.bar > i.bonus {
    position: absolute;
    top: 2px;
    bottom: 2px;
    right: 2px;
    background: rgba(214, 207, 196, .5);
    -webkit-mask: var(--art-fullness-mask) repeat;
    mask: var(--art-fullness-mask) repeat;
    pointer-events: none;
}
.bar-mind > i.bonus { background: rgba(239, 59, 59, .7); }
.bar-mana > i.bonus { background: rgba(140, 100, 255, .75); }
/* Похищение (автор, 16.10.2026): Король слегка светится синим, частицы втягиваются от цели к нему. */
.stage-glow.drain { animation: drain-glow 1.1s ease-in-out infinite; }
@keyframes drain-glow { 0%, 100% { filter: drop-shadow(0 0 5px rgba(120, 180, 255, .7)); }
    50% { filter: drop-shadow(0 0 14px rgba(110, 170, 255, .95)) drop-shadow(0 0 28px rgba(90, 150, 255, .45)) brightness(1.08); } }
.versus-stage > .drain-stream { position: absolute; inset: 18% 8% 34%; z-index: 2; pointer-events: none; }
.drain-stream i { position: absolute; top: calc(50% + var(--y)); width: 6px; height: 6px; margin: -3px; border-radius: 50%;
    background: #bfe0ff; box-shadow: 0 0 7px 2px #5aa2ff; opacity: 0; animation: drain-right 1s cubic-bezier(.55, 0, .9, .45) var(--d) infinite; }
.drain-stream.to-left i { animation-name: drain-left; }
/* Втягиваются: медленно отрываются от цели и всё быстрее летят к Королю, сходясь к нему и тая. */
@keyframes drain-right { 0% { left: 16%; opacity: 0; transform: translateY(0) scale(1); } 20% { opacity: .95; }
    85% { opacity: .9; } 100% { left: 80%; opacity: 0; transform: translateY(calc(var(--y) * -1)) scale(.4); } }
@keyframes drain-left { 0% { left: 84%; opacity: 0; transform: translateY(0) scale(1); } 20% { opacity: .95; }
    85% { opacity: .9; } 100% { left: 20%; opacity: 0; transform: translateY(calc(var(--y) * -1)) scale(.4); } }
.dmg-float.drain { color: #9cc8ff; }
.entity-card.card-drain-a .entity-icon { animation: card-drain-a 1.6s ease-in-out; }
.entity-card.card-drain-b .entity-icon { animation: card-drain-b 1.6s ease-in-out; }
@keyframes card-drain-a { 0%, 100% { filter: none; } 30%, 70% { filter: drop-shadow(0 0 6px #6aa8ff) brightness(1.1); } }
@keyframes card-drain-b { 0%, 100% { filter: none; } 30%, 70% { filter: drop-shadow(0 0 6px #6aa8ff) brightness(1.1); } }
/* Экран алтаря: четыре выемки под черепа — те же ячейки, что в инвентаре; пустая чуть темнее. */
.altar-hollows { display: flex; gap: 8px; justify-content: center; margin: 4px 0 8px; }
.altar-hollows .entity-icon.empty { background-color: rgba(0, 0, 0, .25); }
@media (prefers-reduced-motion: reduce) { .stage-glow.drain, .drain-stream i { animation: none; } }

/* Вихрь Короля Оленей (16.10.2026): один ролик на всю серию — из позы замаха пять быстрых тычков вперёд и
   возврат. Длина — серия (KingRules.WhirlMs, 1,5 с) и возврат. Жертве на каждый удар — короткий толчок (jolt):
   полное вздрагивание на каждый удар серии дёргалось. */
.stage-scale.whirl-a, .stage-scale.whirl-b { animation: whirl-a 1.7s linear var(--whirl-offset); }
.stage-scale.whirl-b { animation-name: whirl-b; }
@keyframes whirl-a {
    0% { transform: translateX(-20%) scale(1.02, .98); filter: var(--trail-none); animation-timing-function: cubic-bezier(.9, 0, 1, .6); }
    9% { transform: translateX(20%); filter: var(--trail-fwd); animation-timing-function: ease-in-out; }
    17% { transform: translateX(4%); filter: var(--trail-none); animation-timing-function: cubic-bezier(.6, 0, 1, .7); }
    25% { transform: translateX(22%); filter: var(--trail-fwd); animation-timing-function: ease-in-out; }
    33% { transform: translateX(4%); filter: var(--trail-none); animation-timing-function: cubic-bezier(.6, 0, 1, .7); }
    41% { transform: translateX(22%); filter: var(--trail-fwd); animation-timing-function: ease-in-out; }
    49% { transform: translateX(4%); filter: var(--trail-none); animation-timing-function: cubic-bezier(.6, 0, 1, .7); }
    57% { transform: translateX(22%); filter: var(--trail-fwd); animation-timing-function: ease-in-out; }
    65% { transform: translateX(4%); filter: var(--trail-none); animation-timing-function: cubic-bezier(.6, 0, 1, .7); }
    74% { transform: translateX(20%); filter: var(--trail-fwd); animation-timing-function: ease-in-out; }
    100% { transform: translateX(0); filter: var(--trail-none); } }
@keyframes whirl-b {
    0% { transform: translateX(-20%) scale(1.02, .98); filter: var(--trail-none); animation-timing-function: cubic-bezier(.9, 0, 1, .6); }
    9% { transform: translateX(20%); filter: var(--trail-fwd); animation-timing-function: ease-in-out; }
    17% { transform: translateX(4%); filter: var(--trail-none); animation-timing-function: cubic-bezier(.6, 0, 1, .7); }
    25% { transform: translateX(22%); filter: var(--trail-fwd); animation-timing-function: ease-in-out; }
    33% { transform: translateX(4%); filter: var(--trail-none); animation-timing-function: cubic-bezier(.6, 0, 1, .7); }
    41% { transform: translateX(22%); filter: var(--trail-fwd); animation-timing-function: ease-in-out; }
    49% { transform: translateX(4%); filter: var(--trail-none); animation-timing-function: cubic-bezier(.6, 0, 1, .7); }
    57% { transform: translateX(22%); filter: var(--trail-fwd); animation-timing-function: ease-in-out; }
    65% { transform: translateX(4%); filter: var(--trail-none); animation-timing-function: cubic-bezier(.6, 0, 1, .7); }
    74% { transform: translateX(20%); filter: var(--trail-fwd); animation-timing-function: ease-in-out; }
    100% { transform: translateX(0); filter: var(--trail-none); } }
.stage-scale.storm-a, .stage-scale.storm-b {
    animation: storm-a var(--storm-step) linear var(--whirl-offset) var(--storm-hits);
}
.stage-scale.storm-b { animation-name: storm-b; }
@keyframes storm-a {
    0%, 100% { transform: translateX(0); filter: var(--trail-none); }
    60% { transform: translateX(22%); filter: var(--trail-fwd); }
}
@keyframes storm-b {
    0%, 100% { transform: translateX(0); filter: var(--trail-none); }
    60% { transform: translateX(22%); filter: var(--trail-fwd); }
}
.stage-scale.jolt-a { animation: jolt-a .25s linear; }
.stage-scale.jolt-b { animation: jolt-b .25s linear; }
@keyframes jolt-a { 0%, 52%, 100% { transform: none; } 60%, 76% { transform: translateX(var(--jolt-recoil)) scale(.99, 1.01); } }
@keyframes jolt-b { 0%, 52%, 100% { transform: none; } 60%, 76% { transform: translateX(var(--jolt-recoil)) scale(.99, 1.01); } }
@media (prefers-reduced-motion: reduce) { .stage-scale:is([class*="whirl-"], [class*="storm-"], [class*="jolt-"]) { animation: none; } }

/* ---------- Портал (docs/PORTALS.md) ----------
   Знак — белый силуэт (art/ui/portal), кладётся маской и красится здесь: тусклая бронза в ячейке, голубой огонь, когда
   загорелся, красная вспышка, если адреса нет. Строка под ячейками держит место — ничего не прыгает. */
.portal-dial { width: 100%; margin: 0; padding: 10px 8px 6px; }
.portal-cells { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); gap: 6px; justify-items: center; }
.portal-cell { display: flex; flex-direction: column; align-items: center; gap: 4px; width: 100%; max-width: 56px; padding: 2px 0; border: 1px solid var(--border); border-radius: 6px; background: var(--panel-inner); }
.portal-arrow { width: 100%; height: 20px; padding: 0; border: 0; background: transparent; color: var(--text-muted); font-size: 11px; line-height: 20px; cursor: pointer; }
.portal-arrow:hover:not(:disabled) { color: var(--heading); }
.portal-arrow:disabled { opacity: .35; cursor: default; }
.portal-glyph, .portal-mini { display: inline-block; background-color: #96764e; -webkit-mask: var(--glyph) center / contain no-repeat; mask: var(--glyph) center / contain no-repeat; }
.portal-glyph { width: 36px; height: 36px; transition: background-color .2s, filter .2s; }
.portal-cell.lit .portal-glyph { background-color: #c8ecff; filter: drop-shadow(0 0 5px #5aaaff); }
.portal-cells.open .portal-cell { border-color: #3f6f9a; }
.portal-cells.failed .portal-glyph { background-color: #ffaa96; filter: drop-shadow(0 0 5px #e6281e); animation: portal-flash .7s ease-out 1; }
@keyframes portal-flash { 0% { filter: drop-shadow(0 0 14px #ff3020); } 100% { filter: drop-shadow(0 0 5px #e6281e); } }
.portal-note { min-height: 1.4em; margin: 4px 0 0; font-size: 13px; }
.portal-note.error { color: var(--danger-text); }
.portal-rings, .portal-book { width: 100%; margin: 0; text-align: left; }
.portal-ring, .portal-entry { display: flex; align-items: center; gap: 8px; padding: 6px 8px; border-top: 1px solid var(--border); }
.portal-ring-name { flex: none; min-width: 84px; color: var(--heading); }
.portal-ring.chosen .portal-ring-name { color: var(--gold-hover); }
.portal-ring-state { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.portal-ring.linked .portal-ring-state { color: #9fd0f5; }
.portal-ring .btn, .portal-entry .btn { flex: none; height: 28px; padding: 0 10px; }
.portal-entry-info { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.portal-entry-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--heading); }
.portal-entry.here .portal-entry-name { color: var(--gold-hover); }
.portal-entry-address { display: flex; align-items: center; gap: 2px; }
.portal-mini { width: 14px; height: 14px; }
.portal-entry .btn.portal-icon-btn { width: 30px; padding: 0; }
.portal-rename { flex: 1; min-width: 0; height: 28px; }
.chat-portal { color: #9fd0f5; text-decoration: underline dotted; cursor: pointer; }
.chat-portal:hover { color: #c8ecff; }
@media (prefers-reduced-motion: reduce) {
    .portal-cells.failed .portal-glyph { animation: none; }
    .portal-glyph { transition: none; }
}
