:root {
    --bg: #14161a;
    --surface: #1c1f26;
    --border: #2b2f38;
    --text: #e7e9ee;
    --muted: #8b93a3;
    --accent: #d9483b;
    --danger: #c0392b;
    --ok: #3fa66a;
}

* { box-sizing: border-box; }

body {
    margin: 0;
    background: var(--bg);
    color: var(--text);
    font: 15px/1.5 -apple-system, "Segoe UI", Roboto, Ubuntu, sans-serif;
}

a { color: var(--text); text-decoration: none; }
a:hover { color: var(--accent); }

.topbar {
    display: flex;
    align-items: center;
    gap: 24px;
    padding: 12px 24px;
    background: var(--surface);
    border-bottom: 1px solid var(--border);
}

.brand { font-weight: 700; letter-spacing: .5px; color: var(--accent); }
/* Пункты меню переносятся целиком: у рута их столько, что на ноутбуке
   строка не помещается, и «Обратная связь» иначе рвалась бы пополам. */
.topbar nav { display: flex; flex-wrap: wrap; gap: 4px 18px; }
.topbar nav a { white-space: nowrap; }
.topbar nav a.active { color: var(--accent); }
.topbar .me { margin-left: auto; display: flex; align-items: center; gap: 12px; }
.topbar .email { color: var(--muted); font-size: 13px; }

/* Переключатель языка — в правом углу шапки, рядом с выходом. До входа
   шапки нет, и он висит там же сам по себе. */
.langs { display: inline-flex; gap: 6px; }
a.lang {
    color: var(--muted);
    font-size: 12px;
    letter-spacing: .5px;
    padding: 2px 7px;
    border: 1px solid var(--border);
    border-radius: 999px;
}
a.lang:hover { color: var(--accent); border-color: var(--accent); }
.langs.floating { position: fixed; top: 16px; right: 24px; }

main { padding: 24px; max-width: 1100px; margin: 0 auto; }
main.centered { display: flex; justify-content: center; align-items: center; min-height: 100vh; }

/* Вход и регистрация — на фоне картинки из самой игры. Сверху затемнение,
   чтобы форма читалась и на светлом небе; на узком экране — вертикальный
   вариант той же сцены, иначе от широкой остаётся одна середина. */
body.guest {
    background: linear-gradient(rgba(10, 12, 16, .55), rgba(10, 12, 16, .75)),
        url("/static/auth/bg-wide.jpg") center / cover no-repeat fixed, var(--bg);
}
body.guest .login { background: rgba(28, 31, 38, .88); backdrop-filter: blur(4px); box-shadow: 0 10px 40px rgba(0, 0, 0, .5); }
@media (max-aspect-ratio: 4/5) {
    /* fixed на телефонах либо не работает, либо растягивает картинку на всю
       высоту страницы — там фон просто прокручивается. */
    body.guest { background-image: linear-gradient(rgba(10, 12, 16, .55), rgba(10, 12, 16, .75)), url("/static/auth/bg-tall.jpg"); background-attachment: scroll; }
}

.card {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 10px;
    padding: 20px 24px;
    margin-bottom: 20px;
}

.card h1 { font-size: 18px; margin: 0 0 12px; }

.login { width: 340px; }
.login h1 { color: var(--accent); font-size: 22px; margin-bottom: 2px; }
.login label { display: block; margin: 14px 0; }
.login button { width: 100%; }
.login .hint { display: block; margin-top: 4px; font-size: 12px; }
.login .switch { margin: 14px 0 0; text-align: center; font-size: 13px; }
/* Поле-ловушка регистрации: убрано за экран, а не display:none —
   иные боты пропускают как раз невидимые по стилю поля. */
.login .trap { position: absolute; left: -10000px; width: 1px; height: 1px; overflow: hidden; }

label { font-size: 13px; color: var(--muted); }

input, select {
    display: block;
    width: 100%;
    margin-top: 4px;
    padding: 9px 11px;
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: 6px;
    color: var(--text);
    font-size: 14px;
}

input:focus, select:focus { outline: none; border-color: var(--accent); }
input:disabled { opacity: .5; }

button {
    padding: 9px 14px;
    background: var(--accent);
    border: none;
    border-radius: 6px;
    color: #fff;
    font-size: 13px;
    cursor: pointer;
}

button:hover { filter: brightness(1.1); }
button.danger { background: var(--danger); }
button.link { background: none; color: var(--muted); padding: 0; }
button.link:hover { color: var(--accent); }

.row-form { display: flex; gap: 14px; align-items: flex-end; flex-wrap: wrap; }
.row-form label { flex: 1; min-width: 180px; }

/* Широкая таблица не должна рвать карточку: она прокручивается внутри
   своей обёртки. Колонок в «Доступах» и «Играх» столько, что на ноутбуке
   они не помещаются и без всякого телефона. */
.table-wrap { overflow-x: auto; }
table { width: 100%; border-collapse: collapse; font-size: 14px; }
th { text-align: left; color: var(--muted); font-weight: 500; font-size: 12px; text-transform: uppercase; }
th, td { padding: 10px 8px; border-bottom: 1px solid var(--border); vertical-align: middle; }
tr.inactive { opacity: .45; }

td.actions, th.actions { text-align: right; }
td.actions form { display: inline-flex; gap: 4px; margin-left: 6px; vertical-align: middle; }
td.actions input { width: 150px; margin: 0; }

.badge {
    display: inline-block;
    padding: 2px 8px;
    border-radius: 999px;
    font-size: 12px;
    border: 1px solid var(--border);
}

.badge.role-root { border-color: var(--accent); color: var(--accent); }
.badge.role-admin { border-color: var(--ok); color: var(--ok); }
.badge.role-user { color: var(--muted); }

.muted { color: var(--muted); font-size: 13px; }
.nowrap { white-space: nowrap; }
.error {
    padding: 9px 12px;
    background: rgba(192, 57, 43, .15);
    border: 1px solid var(--danger);
    border-radius: 6px;
    color: #ffb3ab;
    font-size: 13px;
}

code { font-size: 12px; color: var(--text); }
.kv { display: inline-block; margin-right: 10px; }

/* --- игроки --- */

.card-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 16px; margin-bottom: 12px; }
.card-head h1 { margin: 0; }
.head-actions { display: flex; gap: 8px; align-items: center; }

a.button {
    display: inline-block;
    padding: 9px 14px;
    background: var(--accent);
    border-radius: 6px;
    color: #fff;
    font-size: 13px;
}

a.button:hover { color: #fff; filter: brightness(1.1); }
a.button.ghost { background: none; border: 1px solid var(--border); color: var(--muted); }

.players { display: flex; flex-direction: column; gap: 8px; }

.player-row {
    display: flex;
    gap: 14px;
    align-items: center;
    padding: 12px 16px;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 8px;
}

/* Ссылка на карточку занимает всю строку, кроме места под пометку «во враги»:
   кнопку внутрь <a> положить нельзя, поэтому колонки строки живут здесь. */
.player-link {
    flex: 1;
    min-width: 0;
    display: grid;
    grid-template-columns: minmax(180px, 1fr) 2fr auto;
    gap: 14px;
    align-items: center;
}

.player-row:hover { border-color: var(--accent); }
/* Подсвечивается рамка строки, а не текст: иначе ник и шкалы перекрашивались
   бы в акцент при наведении куда угодно в строке. */
.player-link:hover { color: var(--text); }
.player-main { display: flex; gap: 8px; align-items: baseline; flex-wrap: wrap; }
.nick { font-weight: 600; }
.nick-big { font-size: 22px; }
.clan { color: var(--muted); font-size: 13px; }
/* Статус клана: помеченные альянсы должны читаться боковым зрением, в списке
   из полусотни строк. */
a.clan:hover { color: var(--accent); }
.clan.clan-ally { color: #7fd3a1; }
.clan.clan-enemy { color: #e88b81; }
.badge.clan-ally { border-color: rgba(63, 166, 106, .5); color: #7fd3a1; }
.badge.clan-enemy { border-color: rgba(217, 72, 59, .5); color: #e88b81; }
.badge.clan-neutral { color: var(--muted); }

.filters { display: flex; gap: 10px; align-items: flex-start; }
.filters input[type=search] { flex: 1; }
.filters select { width: 210px; margin-top: 0; }
h1.section { font-size: 18px; margin: 0 0 12px; }
/* Игровой ID постоянен, поэтому печатаем его моноширинным — так его удобнее
   сверять с игрой и копировать. */
.game-id {
    color: var(--muted);
    font-size: 13px;
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
}
.player-traits { display: flex; gap: 6px; flex-wrap: wrap; }

.tag {
    display: inline-block;
    padding: 2px 8px;
    border-radius: 999px;
    font-size: 12px;
    border: 1px solid var(--border);
    color: var(--muted);
}

.tag.neg { border-color: rgba(217, 72, 59, .5); color: #e88b81; }
.tag.pos { border-color: rgba(63, 166, 106, .5); color: #7fd3a1; }

.trait-list { display: flex; gap: 8px; flex-wrap: wrap; margin: 16px 0; }

/* Кто отметил признак: строка на признак — метка со счётчиком, имена
   отметивших и кнопка снятия у всех. Видят её только админы. */
.trait-marks { list-style: none; padding: 0; margin: 12px 0 0; }
.trait-marks li { display: flex; gap: 10px; align-items: baseline; padding: 6px 0; border-top: 1px solid var(--border); font-size: 13px; }
.trait-marks form { margin-left: auto; }

/* Галочки признаков в форме на карточке игрока. */
.trait-checks { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 8px; margin: 12px 0 8px; }
/* В форме признаков галочки идут во всю её ширину. */
.note-form .trait-checks { margin: 10px 0 4px; }
.trait-checks .check { padding: 7px 10px; font-size: 13px; }

.check {
    display: flex;
    align-items: center;
    gap: 9px;
    padding: 10px 12px;
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: 8px;
    color: var(--text);
    font-size: 14px;
    cursor: pointer;
}

/* Цвет говорит, что за признак, ещё до того как его отметили: плохое
   красным, хорошее зелёным, нейтральное серым — те же цвета, что у меток
   в карточке. Отметка добавляет заливку и рамку в полную силу: цвет
   отвечает на «что это», а заливка — на «выбрано ли». */
.check.neg { border-color: rgba(217, 72, 59, .35); color: #e88b81; }
.check.pos { border-color: rgba(63, 166, 106, .35); color: #7fd3a1; }
.check:hover { filter: brightness(1.25); }
.check.neg:has(input:checked) { border-color: var(--danger); background: rgba(192, 57, 43, .14); }
.check.pos:has(input:checked) { border-color: var(--ok); background: rgba(63, 166, 106, .14); }
.check.zero:has(input:checked) { border-color: var(--muted); background: rgba(255, 255, 255, .06); }
.check input { width: auto; margin: 0; }

h2 { font-size: 14px; color: var(--muted); font-weight: 500; margin: 18px 0 0; }

textarea {
    display: block;
    width: 100%;
    margin-top: 4px;
    padding: 10px 12px;
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: 6px;
    color: var(--text);
    font: inherit;
    resize: vertical;
}

textarea:focus { outline: none; border-color: var(--accent); }

label.block { display: block; margin-top: 14px; }
.form-actions { display: flex; gap: 10px; align-items: center; margin-top: 20px; }
.note-form { margin-bottom: 18px; }
.note-form button { margin-top: 10px; }

.notes { list-style: none; padding: 0; margin: 0; }
.notes li { padding: 12px 0; border-top: 1px solid var(--border); }
.note-head { display: flex; gap: 12px; align-items: baseline; font-size: 13px; }
.note-head .author { color: var(--accent); }
.note-head form { margin-left: auto; }
.note-body { margin: 6px 0 0; white-space: pre-wrap; }

/* История банов: список коротких строк «когда — что стало — где заметили».
   Значки в нём те же, что и у статусов кланов, поэтому строке остаётся
   только выровнять их по тексту. */
.bans { list-style: none; padding: 0; margin: 0 0 10px; }
.bans li { display: flex; gap: 10px; align-items: baseline; padding: 4px 0; color: var(--muted); font-size: 13px; }

.empty { color: var(--muted); text-align: center; }
.center { text-align: center; }
.narrow { max-width: 110px; }
th.narrow, td.narrow { width: 90px; }
td.narrow input { width: 100%; }
td.actions button { margin-left: 6px; }
/* Кнопка доступа к играм стоит в обычной колонке и по левому краю, как её
   заголовок: форма сама по себе блочная и без этого роняла бы кнопку
   на свою строку. */
td.access form { display: inline-flex; }

/* --- справка --- */

/* Вопросы свёрнуты через <details>: список читается целиком, а ответ
   разворачивается без единой строки скрипта. */
.faq details { border-top: 1px solid var(--border); }
.faq details:first-of-type { border-top: none; }

.faq summary {
    padding: 12px 0;
    cursor: pointer;
    font-weight: 600;
    list-style: none;
    display: flex;
    align-items: baseline;
    gap: 10px;
}
.faq summary::-webkit-details-marker { display: none; }
.faq summary::before {
    content: "+";
    color: var(--accent);
    font-weight: 700;
    width: 12px;
}
.faq details[open] > summary::before { content: "–"; }
.faq summary:hover { color: var(--accent); }
.faq summary:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.faq-body { padding: 0 0 14px 22px; max-width: 70ch; }
.faq-body p { margin: 0 0 10px; }
.faq-body p:last-child { margin-bottom: 0; }
.faq-body table { margin-top: 4px; }
.faq-body td.yes { color: var(--ok); text-align: center; }
.faq-body td.no { color: var(--muted); text-align: center; }
.faq-body th.narrow { text-align: center; }

.htmx-indicator { opacity: 0; transition: opacity .2s; }
.htmx-request .htmx-indicator, .htmx-request.htmx-indicator { opacity: 1; }

/* Личный список врагов. Строка подбора — не ссылка, а форма, поэтому она не
   подсвечивается при наведении, как строки поиска. */
.player-row.static:hover { border-color: var(--border); }
.player-row.static .player-main { flex: 1; }

/* Пометки «в друзья» и «во враги» в строке поиска намеренно тихие: это личное
   дело того, кто их ставит, а не свойство игрока, и в списке из полусотни
   строк они не должны спорить со шкалами. Цвет появляется только у уже
   поставленной: красный у вражды, зелёный у дружбы. */
.mark {
    padding: 3px 10px;
    border-radius: 999px;
    border: 1px solid var(--border);
    background: none;
    color: var(--muted);
    font-size: 12px;
    white-space: nowrap;
}

button.mark.enemy:hover { border-color: rgba(217, 72, 59, .5); color: #e88b81; filter: none; }
a.mark.done.enemy, a.mark.done.enemy:hover { border-color: rgba(217, 72, 59, .5); color: #e88b81; }
button.mark.friend:hover { border-color: rgba(76, 175, 122, .5); color: #8fd4a8; filter: none; }
a.mark.done.friend, a.mark.done.friend:hover { border-color: rgba(76, 175, 122, .5); color: #8fd4a8; }
/* Две пометки в строке стоят рядом: сначала дружба, потом вражда. */
.marks { display: flex; align-items: center; gap: 6px; }
/* Раскрытая заметка встаёт на место своей пометки — столбиком, чтобы поле
   было шире подписи. Ширина ограничена: строка поиска не должна превращаться
   в форму во весь экран из-за одного нажатия. */
.mark-form {
    display: flex;
    flex-direction: column;
    gap: 4px;
    width: 260px;
    max-width: 100%;
}
.mark-form textarea { margin: 0; }
.mark-form .muted, .mark-form .error { margin: 0; font-size: 12px; }
.mark-actions { display: flex; align-items: center; gap: 10px; }
/* Значок «во врагах» и «в друзьях» на карточке: те же цвета, что у пометок
   в строке поиска. */
.badge.enemy { border-color: rgba(217, 72, 59, .5); color: #e88b81; }
.badge.friend { border-color: rgba(63, 166, 106, .5); color: #7fd3a1; }
.row-actions { display: flex; align-items: center; justify-content: flex-end; gap: 10px; }
.enemies .note-head { margin-bottom: 6px; }
.enemies .note-head .nick { color: var(--text); }
.enemies .note-head a.nick:hover { color: var(--accent); }
.enemy-comment { display: flex; gap: 10px; align-items: flex-end; }
.enemy-comment textarea { flex: 1; margin: 0; }
/* Индикатор стоит в одной строке с полем поиска, поэтому его надо опустить
   до базовой линии ввода. */
.filters .htmx-indicator { align-self: center; }

/* Очередь строительства: провинция заголовком, под ней пронумерованный
   список зданий. Стрелки и крестик стоят справа от названия — это формы,
   поэтому строка и выкладывается флексом. */
.build-province { margin: 14px 0 4px; font-size: 15px; }
.build-list { margin: 0; padding-left: 22px; }
.build-list li { display: flex; align-items: center; gap: 8px; padding: 2px 0; }
.build-list .label { flex: 1; }
/* Картинка здания или войска у записи очереди. Размер задан заранее,
   чтобы строка не прыгала, пока картинка грузится с сайта игры. */
.build-list .queue-img { width: 32px; height: 28px; object-fit: contain; flex: none; }

/* Выбор здания или войска плитками с картинками. Радиокнопка спрятана:
   выбранную плитку видно по рамке. Плитки занимают всю ширину формы. */
.pick-tiles {
    flex-basis: 100%;
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(96px, 1fr));
    gap: 6px;
    margin: 0;
    padding: 0;
    border: 0;
    max-height: 320px;
    overflow-y: auto;
}
.pick-tiles legend { padding: 0; margin-bottom: 6px; }
.pick-tiles .tile {
    display: flex; flex-direction: column; align-items: center; gap: 4px;
    min-width: 0; padding: 6px 4px;
    border: 1px solid var(--border); border-radius: 6px;
    font-size: 12px; text-align: center; cursor: pointer;
}
.pick-tiles .tile input { position: absolute; opacity: 0; pointer-events: none; }
.pick-tiles .tile img { width: 48px; height: 40px; object-fit: contain; }
.pick-tiles .tile:has(input:checked) { border-color: var(--accent); }
.pick-tiles .tile:has(input:focus-visible) { outline: 2px solid var(--accent); }

/* Карта партии: широкая и мелкая в деталях, поэтому во всю ширину карточки
   и с фоном под цвет моря — море отдельными фигурами не рисуется. */
.game-map {
    display: block;
    width: 100%;
    height: auto;
    margin: 4px 0 10px;
    border: 1px solid var(--border);
    border-radius: 6px;
    background: #16222c;
}
/* Тонкая граница есть у всех провинций: без неё карта в один цвет
   расползается в пятно. Заметная обводка означает свою страну и означает
   её во всех режимах: цвет в каждом свой, а «где я» — вопрос один и тот же. */
.game-map polygon { stroke: rgba(0, 0, 0, .35); stroke-width: 1; }
.game-map polygon.own { stroke: #e8b45f; stroke-width: 6; }

/* Заливка провинций живёт здесь, а не в атрибуте: карта выводится один раз,
   а красится по-разному в зависимости от режима. Серый по умолчанию — это
   «про этот режим о владельце ничего не известно»; ничейная земля темнее,
   чтобы свободную не принять за чужую. */
.game-map polygon { fill: #4a5b68; }
.game-map polygon.neutral { fill: #2b3a46; }
/* Режим «Кланы»: цвет приезжает переменной --clan из шаблона, потому что
   раздаётся он на партию и заранее в стилях его не пропишешь. */
#map-clans:checked ~ .game-map polygon.clan { fill: var(--clan); }
/* Режим «Мои списки»: своя заливка у врагов и друзей, всё прочее серое.
   Правило врага стоит ниже — если человек попал в оба списка, побеждает
   вражда: о ней предупредить нужнее. */
#map-sides:checked ~ .game-map polygon.side-friend { fill: #3f7d5a; }
#map-sides:checked ~ .game-map polygon.side-enemy { fill: #a2453c; }
/* Режим «Коалиции»: цвет тоже приезжает переменной, но берётся не из нашей
   палитры, а из самой игры — коалиция раскрашивает себя сама. */
#map-teams:checked ~ .game-map polygon.team { fill: var(--team); }
/* Режим «Игроки»: цвет говорит, кто владеет землёй, — живой человек, бот,
   игрок с подпиской или забаненный. Правила спорят за одну провинцию,
   и побеждает стоящее ниже, поэтому они и идут по возрастанию важности:
   подписка важнее «просто живого», бан важнее всего. Ничейной земле своего
   правила не досталось — она остаётся тёмно-серой, как и в других режимах. */
#map-players:checked ~ .game-map polygon.human { fill: #3c6e9c; }
#map-players:checked ~ .game-map polygon.ai { fill: #6f7b52; }
#map-players:checked ~ .game-map polygon.premium { fill: #7d63a8; }
/* Ставший ботом — после подписки: у брошенной страны она уже ничего
   не значит. Бан — последним, он важнее всего. */
#map-players:checked ~ .game-map polygon.gone { fill: #8a5a2b; }
#map-players:checked ~ .game-map polygon.banned { fill: #a2453c; }
/* Режим «Сила»: цвет говорит, насколько владелец опаснее смотрящего.
   Шкала идёт от красного к зелёному через жёлтый — так же, как читается
   светофор, и отдельного объяснения ей не нужно. Провинции без счёта
   правила не получают и остаются серыми: «не спрашивали» — это не
   «слабый», и путать их нельзя. */
#map-power:checked ~ .game-map polygon.power-much-up { fill: #8f2f26; }
#map-power:checked ~ .game-map polygon.power-up { fill: #c0564a; }
#map-power:checked ~ .game-map polygon.power-even { fill: #b08a2e; }
#map-power:checked ~ .game-map polygon.power-down { fill: #4f8f5f; }
#map-power:checked ~ .game-map polygon.power-much-down { fill: #2f6b45; }
/* Режим «Топ кланов»: цвет тоже переменной, но раздаётся он не по владениям,
   а по месту в рейтинге — первый номер должен быть одного цвета во всех
   партиях. */
#map-top:checked ~ .game-map polygon.top { fill: var(--top); }
/* Режим «Играли вместе»: цвет группы, собранной из архива коалиций. */
#map-together:checked ~ .game-map polygon.together { fill: var(--together); }
/* Режим «Активность»: светофор по последнему входу в партию. Боты и те,
   про кого игра не сказала, остаются серыми. */
.game-map text {
    font: 600 34px system-ui, -apple-system, "Segoe UI", sans-serif;
    fill: rgba(255, 255, 255, .92);
    /* Обводка под текстом: поверх пёстрой заливки иначе не прочитать. */
    paint-order: stroke;
    stroke: rgba(0, 0, 0, .65);
    stroke-width: 6;
}
/* Название страны красится и получает значок только в режиме личных
   списков: в режиме кланов подписи нейтральные, иначе на одной картинке
   опять оказались бы две разные разметки. */
#map-sides:checked ~ .game-map text.enemy { fill: #ffb3aa; }
#map-sides:checked ~ .game-map text.friend { fill: #a8e8c0; }
.game-map text .sign { display: none; }
#map-sides:checked ~ .game-map text .sign { display: inline; }
/* Меч чёрный — на приглушённой заливке он читается как значок, а не как
   часть названия. Светлая обводка вместо общей тёмной: иначе чёрное
   по чёрному сливается. Рукопожатие рисует шрифт эмодзи, fill его
   не берёт. Класс не «mark»: тот в проекте занят кнопками списков,
   и его font-size сжал бы значок до кнопочного. */
.game-map text .sign { fill: #000; stroke: rgba(255, 255, 255, .55); }
/* Ник второй строкой под страной: мельче и тише названия, но того же цвета —
   в режиме личных списков он должен краснеть и зеленеть вместе с ним. */
.game-map text .player { font-size: 26px; font-weight: 500; opacity: .8; }

/* Переключатель режимов карты. Радиокнопки настоящие и лежат первыми
   в контейнере: по ним css и понимает, что показывать, — сами они
   не видны, а нажимаются подписями-вкладками. Убирать их из потока
   display:none нельзя, иначе до них не добраться с клавиатуры. */
.map-view { position: relative; }
.map-view > .map-mode { position: absolute; width: 1px; height: 1px; opacity: 0; }
.map-tabs { display: flex; gap: 8px; margin: 4px 0 0; }
.map-tabs label {
    padding: 7px 12px;
    border: 1px solid var(--border);
    border-radius: 6px;
    color: var(--muted);
    font-size: 13px;
    cursor: pointer;
}
.map-tabs label:hover { color: var(--text); }
#map-clans:checked ~ .map-tabs label[for="map-clans"],
#map-sides:checked ~ .map-tabs label[for="map-sides"],
#map-teams:checked ~ .map-tabs label[for="map-teams"],
#map-players:checked ~ .map-tabs label[for="map-players"],
#map-power:checked ~ .map-tabs label[for="map-power"],
#map-top:checked ~ .map-tabs label[for="map-top"],
#map-together:checked ~ .map-tabs label[for="map-together"],
#map-paper:checked ~ .map-tabs label[for="map-paper"] {
    border-color: var(--accent);
    color: var(--text);
}
/* Клавиатуре тоже надо показывать, где она находится: сама кнопка не видна,
   поэтому рамку рисуем на её подписи. */
#map-clans:focus-visible ~ .map-tabs label[for="map-clans"],
#map-sides:focus-visible ~ .map-tabs label[for="map-sides"],
#map-teams:focus-visible ~ .map-tabs label[for="map-teams"],
#map-players:focus-visible ~ .map-tabs label[for="map-players"],
#map-power:focus-visible ~ .map-tabs label[for="map-power"],
#map-top:focus-visible ~ .map-tabs label[for="map-top"],
#map-together:focus-visible ~ .map-tabs label[for="map-together"],
#map-paper:focus-visible ~ .map-tabs label[for="map-paper"] { outline: 2px solid var(--accent); }

/* Легенда и пояснение — свои у каждого режима, показывается только пара
   выбранного. */
.map-pane { display: none; }
#map-clans:checked ~ .pane-clans,
#map-sides:checked ~ .pane-sides,
#map-teams:checked ~ .pane-teams,
#map-players:checked ~ .pane-players,
#map-power:checked ~ .pane-power,
#map-top:checked ~ .pane-top,
#map-together:checked ~ .pane-together,
#map-paper:checked ~ .pane-paper { display: block; }
/* Вкладка «Потери»: строка раскрывается списком противников. Пока идёт
   подсчёт, блок притушен — газета листается по дню за запрос. */
.losses summary { cursor: pointer; }
.losses .swatch { display: inline-block; vertical-align: middle; }
.losses-duels { margin: 6px 0 4px 18px; padding: 0; font-size: 13px; color: var(--muted); }
#paper-box.htmx-request { opacity: .5; pointer-events: none; }

/* Легенда карты: цвет раздаётся кланам на партию, поэтому она идёт сразу
   под картой и читается как её продолжение. Строки в ряд и с переносом —
   кланов обычно единицы, но на ширину телефона они всё равно не встают. */
.map-legend {
    display: flex;
    flex-wrap: wrap;
    gap: 6px 16px;
    margin: 0 0 10px;
    padding: 0;
    list-style: none;
}
.map-legend li { display: flex; align-items: center; gap: 6px; }
.map-legend .swatch,
.roster .swatch {
    width: 14px;
    height: 14px;
    border-radius: 3px;
    border: 1px solid rgba(0, 0, 0, .45);
}
/* В таблице образец стоит перед числом, а не отдельной строкой списка. */
.roster .swatch { display: inline-block; vertical-align: -2px; }

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

   Прячет чужие страницы правило, которое собирает шаблон: правил ровно
   столько, сколько страниц, а это число своё у каждой партии. Показывать
   выбранную нельзя — пришлось бы задавать display, а на телефоне строка
   таблицы становится блоком (table.stack), и вёрстка бы поехала. */
.paged .page-pick { position: absolute; width: 1px; height: 1px; opacity: 0; }
.pager { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; margin-top: 12px; color: var(--muted); }
.pager label {
    min-width: 28px;
    padding: 4px 8px;
    text-align: center;
    border: 1px solid var(--border);
    border-radius: 6px;
    cursor: pointer;
    color: var(--text);
}
.pager label:hover { border-color: var(--accent); }
/* Образцы режимов списков и игроков — те же цвета, что и заливка на карте.
   Полосы силы объявлены без места: тот же образец стоит и в легенде,
   и в колонке «Сила» таблицы состава, и значит там ровно то же. */
.map-legend .swatch.side-enemy { background: #a2453c; }
.map-legend .swatch.side-friend { background: #3f7d5a; }
.map-legend .swatch.human { background: #3c6e9c; }
.map-legend .swatch.ai { background: #6f7b52; }
.map-legend .swatch.gone { background: #8a5a2b; }
.map-legend .swatch.premium { background: #7d63a8; }
.map-legend .swatch.banned { background: #a2453c; }
/* Образцы режима силы — та же шкала от красного к зелёному. */
.swatch.power-much-up { background: #8f2f26; }
.swatch.power-up { background: #c0564a; }
.swatch.power-even { background: #b08a2e; }
.swatch.power-down { background: #4f8f5f; }
.swatch.power-much-down { background: #2f6b45; }

/* Ссылка, которой ещё рано: пока идёт счёт до следующего похода за картой,
   скрипт вешает этот класс, а по нулю снимает. Нажатие гасим и вида ради,
   и по делу — раньше срока оно вернёт тот же отказ. */
a.button.waiting, a.waiting { opacity: .45; pointer-events: none; }

/* Число проверок карты правится прямо в строке пользователя: поле узкое,
   счёт там двузначный. */
/* Слово о только что изменённой настройке: подтверждение или отказ. Стоит
   там же, где меняли, — у поля проверок или в конце строки. Мелкое и без
   рамки: это не сообщение об ошибке страницы, а подпись к одному нажатию. */
.row-said { display: inline-block; margin-left: 8px; font-size: 12px; white-space: nowrap; }
.row-said.done { color: var(--ok); }
.row-said.bad { color: var(--danger); }

.inline-checks { display: flex; gap: 6px; align-items: center; }
.inline-checks input { width: 68px; }

/* Выбор пакета. Отдельно от .inline-checks: там узкое поле под число,
   а здесь список, и сжиматься ему нельзя — во flex-строке он схлопывается
   до одной стрелки, и какой пакет выбран, становится не видно.

   Колонке нужна и своя ширина: таблица тянется на всю карточку, колонок
   в «Доступах» больше десятка, и без этого браузер ужимает ячейку так,
   что кнопка вылезает за неё и налезает на соседний столбец. Таблица
   от этого становится шире карточки — и прокручивается внутри неё,
   как и задумано у .table-wrap. */
.inline-plan { display: flex; gap: 6px; align-items: center; flex-wrap: wrap; }
/* Ни список, ни кнопка не ужимаются: иначе список схлопывается в одну
   стрелку, а кнопка — в полоску, и обе становятся нечитаемы. Если ячейке
   всё же не хватит ширины, они встанут в две строки, а не полезут
   на соседний столбец. */
.inline-plan > * { flex: none; }
.inline-plan select { width: 10rem; }
td.plan { min-width: 18rem; }

/* «Собираем данные о партии»: накрывает карту вместе с легендой на то время,
   которого не хватило странице до обычного времени сбора. Ставит и снимает
   его скрипт — без него карта видна сразу. */
.map-loading {
    position: absolute;
    inset: 0;
    z-index: 2;
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--surface);
    color: var(--muted);
    font-size: 14px;
}

/* То же «Собираем данные о партии», но на всю страницу: нажатое «Заглянуть
   в партию» уводит на новый адрес, а ответа сайт игры ждёт до минуты. Браузер
   всё это время показывает прежнюю страницу, поэтому накрываем её сами —
   иначе нажатие выглядит потерянным. Снимать не нужно: перекрытие уходит
   вместе со страницей, а при возврате назад его убирает скрипт. */
.page-loading {
    position: fixed;
    inset: 0;
    z-index: 20;
    display: flex;
    align-items: center;
    justify-content: center;
    background: rgba(20, 22, 26, .85);
    color: var(--text);
    font-size: 15px;
}

/* Счётчик проверок у формы «Проверить игру»: число крупнее подписи, потому
   что смотрят именно на него. Ноль краснеет — дальше формой не воспользуешься.
   Имя не «checks»: так зовётся сетка галочек в карточке игрока, и счётчик
   разъезжало её колонками. Строка короткая — переносить её незачем. */
.checks-left { color: var(--muted); font-size: 13px; white-space: nowrap; }
.checks-left b { color: var(--text); font-size: 16px; }
.checks-left.out, .checks-left.out b { color: var(--danger); }

/* Обратная связь. Открытое обращение подсвечено как «требует внимания»,
   закрытое — спокойным зелёным: то же деление, что у статусов кланов.
   Счётчик в шапке — то же кольцо, только с цифрой внутри. */
.badge.open { border-color: rgba(217, 72, 59, .5); color: #e88b81; }
.badge.done { border-color: rgba(63, 166, 106, .5); color: #7fd3a1; }
.badge.count { border-color: var(--accent); color: var(--accent); padding: 1px 7px; }
.card-head .filters { flex-wrap: wrap; }

/* --- узкие экраны ---

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

/* Планшет и узкое окно: отступы съедают слишком много ширины, а строки,
   рассчитанные на три колонки, перестают в неё влезать. */
@media (max-width: 900px) {
    main { padding: 16px; }
    .card { padding: 16px; }

    /* Шапка в две строки: меню прокручивается вбок отдельной лентой,
       а имя, роль, язык и выход встают под ним. Прятать меню за кнопкой
       не стали — разделов десяток, и лента нажимается тем же пальцем,
       что и кнопка, только без лишнего нажатия. */
    .topbar { flex-wrap: wrap; gap: 10px; padding: 10px 12px; }
    .topbar nav {
        order: 3;
        width: 100%;
        flex-wrap: nowrap;
        gap: 16px;
        overflow-x: auto;
        padding-bottom: 2px;
        /* Полоса прокрутки под меню съедала бы строку целиком. */
        scrollbar-width: none;
    }
    .topbar nav::-webkit-scrollbar { display: none; }
    .topbar nav a { white-space: nowrap; }
    .topbar .me { gap: 10px; }
    .topbar .email { max-width: 40vw; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }


    /* Строка поиска: ник, клан и пометки в три колонки требуют ширины,
       которой уже нет. Ник с кланом остаются вместе, пометки уходят вниз. */
    .player-link { grid-template-columns: minmax(0, 1fr); gap: 6px; }
    /* Раскрытая заметка на узком экране занимает отдельную строку: рядом
       с ником ей остаётся полоска в пару слов. */
    .player-row:has(.mark-form) { flex-wrap: wrap; }
    .player-row .mark-form { width: 100%; }
    .filters { flex-wrap: wrap; }
    /* Узкое поле формы («Знак», «Порядок», «Статус») на широком экране
       экономит место, а здесь только режет подписи. */
    .row-form .narrow, .filters .narrow { max-width: none; }
}

@media (max-width: 640px) {
    main { padding: 12px; }
    .card { padding: 14px; border-radius: 8px; margin-bottom: 14px; }

    .langs.floating { top: 10px; right: 12px; }
    .login { width: 100%; }

    /* Поля на телефоне: шрифт мельче 16px заставляет Safari приближать
       страницу при попадании в поле, и обратно она уже не отъезжает. */
    input, select, textarea { font-size: 16px; }

    /* Формы, сложенные в строку, на телефоне встают в колонку; кнопка
       под ними — во всю ширину, чтобы по ней было легко попасть. */
    /* Поиск и фильтр к нему встают друг под друга: рядом они помещаются
       только за счёт того, что оба становятся нечитаемо узкими. Проверка
       на поле поиска нужна затем, что тем же классом размечен ряд кнопок
       в обратной связи, а кнопкам в столбик стоять незачем. */
    .filters:has(input[type=search]) { flex-direction: column; }
    .filters input[type=search], .filters select { width: 100%; }
    .filters { flex-wrap: wrap; }

    .row-form { flex-direction: column; align-items: stretch; gap: 10px; }
    .row-form label { min-width: 0; }
    .row-form button, .form-actions button, .note-form button { width: 100%; }
    .form-actions { flex-direction: column; align-items: stretch; }
    .card-head { flex-direction: column; align-items: stretch; gap: 10px; }
    .head-actions { flex-wrap: wrap; }
    .enemy-comment { flex-direction: column; align-items: stretch; }
    .enemy-comment button { width: 100%; }

    /* Кнопка в строке таблицы остаётся кнопкой в строке: растягивать её
       во всю ширину незачем — их там по две-три в ряд. */
    td.actions button, td.actions a.button, .trait-marks button { width: auto; }

    /* Таблицы, не разложенные в карточки, прокручиваются вбок целиком:
       переносить в них строки некуда, столбцы станут по букве шириной. */
    .table-wrap { white-space: nowrap; }

    /* Переключатель режимов карты — такая же лента, как меню в шапке. */
    .map-tabs { overflow-x: auto; scrollbar-width: none; }
    .map-tabs::-webkit-scrollbar { display: none; }
    .map-tabs label { white-space: nowrap; }

    /* Строка отметок признака: кнопка «снять у всех» уходит под имена,
       иначе она отжимает их в две буквы. */
    .trait-marks li { flex-wrap: wrap; }
    .trait-marks form { margin-left: 0; width: 100%; }
    .note-head { flex-wrap: wrap; gap: 8px; }
}

/* Таблица, разложенная в карточки: заголовок столбца печатается перед
   значением из data-label. Так показываются те таблицы, где в строке живут
   поля и кнопки, — прокручивать их вбок и одновременно нажимать на них
   невозможно. Порог здесь выше телефонного: «Доступы» и «Игры» не влезают
   и в планшет, а тянуть форму из-за края экрана одинаково неудобно
   на любой ширине. */
@media (max-width: 900px) {
    /* Разложенной таблице прокрутка ни к чему: она и так по ширине экрана. */
    .table-wrap:has(table.stack) { overflow-x: visible; white-space: normal; }
    table.stack, table.stack tbody, table.stack tr, table.stack td { display: block; width: 100%; }
    table.stack { white-space: normal; }
    table.stack thead { display: none; }
    table.stack tr {
        border: 1px solid var(--border);
        border-radius: 8px;
        padding: 10px 12px;
        margin-bottom: 10px;
    }
    table.stack td {
        display: flex;
        gap: 12px;
        justify-content: space-between;
        align-items: baseline;
        padding: 5px 0;
        border: none;
        max-width: none;
        width: auto;
        text-align: left;
    }
    table.stack td::before {
        content: attr(data-label);
        flex: 0 0 auto;
        color: var(--muted);
        font-size: 11px;
        text-transform: uppercase;
        letter-spacing: .3px;
    }
    /* Пустая подпись означает «значение говорит за себя»: так помечены
       ячейки с кнопками. */
    table.stack td[data-label=""]::before { content: none; }
    table.stack td.actions { flex-wrap: wrap; justify-content: flex-start; }
    table.stack td.actions form { margin-left: 0; }
    table.stack td input { width: 100%; }
    table.stack td.narrow, table.stack th.narrow { width: auto; }
}

/* Герои игрока: строка на героя — портрет с именем слева, ряд ступеней
   справа. Ступеней у героев от десяти до двадцати, поэтому ряд не влезает
   в строку целиком и переносится. */
.hero-rows { list-style: none; padding: 0; margin: 0 0 12px; }
/* Две колонки, а не строка с переносом: у героев разное число ступеней,
   и при переносе ряд одного оказывался бы справа от имени, а другого —
   под ним. Ряд переносится внутри своей колонки, имя остаётся на месте. */
.hero-rows li {
    display: grid;
    grid-template-columns: 220px minmax(0, 1fr);
    gap: 14px;
    align-items: center;
    padding: 8px 0;
    border-top: 1px solid var(--border);
}
.hero-who { display: flex; gap: 10px; align-items: center; }
/* Портрет игра рисует под тёмный фон карточки, поэтому рамка ему не нужна. */
.hero-face { border-radius: 4px; }
.hero-who .muted { font-size: 12px; }

/* Слева направо: у героев разная длина ряда, и от общей левой вертикали
   они читаются как одна шкала, а не как рваный край. */
.hero-levels { display: flex; flex-wrap: wrap; gap: 4px; }
/* Ступень — та же кнопка-переключатель, что и галочка признака, только
   квадратная и с числом: их два десятка в ряд, и подписи длиннее числа
   там не помещаются. */
.hero-levels .lvl { cursor: pointer; }
.hero-levels .lvl input { position: absolute; opacity: 0; width: 1px; height: 1px; }
.hero-levels .lvl span {
    display: block;
    min-width: 30px;
    padding: 5px 0;
    text-align: center;
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: 6px;
    color: var(--muted);
    font-size: 13px;
}
.hero-levels .lvl:hover span { color: var(--text); }
.hero-levels .lvl input:checked + span { border-color: var(--accent); background: rgba(217, 72, 59, .16); color: var(--text); }
/* Ноль — это «героя нет»: он выбран у всех, кого не отмечали, и красить
   его в акцент незачем. */
.hero-levels .lvl.zero input:checked + span { border-color: var(--muted); background: rgba(255, 255, 255, .06); }
.hero-levels .lvl input:focus-visible + span { outline: 2px solid var(--accent); outline-offset: 1px; }

/* На телефоне герой и его ступени встают друг под друга: в две колонки ряд
   превращается в узкий столбик из трёх кнопок. Правило стоит после самих
   стилей героев — иначе оно проиграло бы им по порядку. */
@media (max-width: 640px) {
    .hero-rows li { grid-template-columns: minmax(0, 1fr); gap: 8px; }
}

/* Свёрнутый блок карточки: признаки и герои. Отмечают их изредка, а карточку
   читают каждый раз — развёрнутые списки отодвигали бы комментарии на второй
   экран. Знак «+» тот же, что в справке: одно движение, одно значение. */
details.fold > summary {
    display: flex;
    align-items: baseline;
    gap: 10px;
    cursor: pointer;
    list-style: none;
}
details.fold > summary::-webkit-details-marker { display: none; }
details.fold > summary::before {
    content: "+";
    color: var(--accent);
    font-weight: 700;
    width: 12px;
}
details.fold[open] > summary::before { content: "–"; }
details.fold > summary:hover .fold-title { color: var(--accent); }
details.fold > summary:focus-visible { outline: 2px solid var(--accent); outline-offset: 4px; }
.fold-title { font-size: 18px; font-weight: 700; }
/* Экономика игроков: отрицательный итог по ресурсу — тем же цветом,
   что и отрицательные метки. */
details.economy { margin-top: 16px; }
.eco-neg { color: #e88b81; }
/* Первому блоку после заголовка нужен тот же отступ, что был у h1. */
details.fold[open] > summary { margin-bottom: 14px; }

/* Журнал входов. Отказ подсвечен, а не притушен, как заблокированный
   пользователь: неудачные попытки — как раз то, за чем в журнал приходят.
   Строка браузера ужимается до начала: она длиной в полстраницы,
   а различают по ней телефон и настольный браузер — это видно и по началу.
   Целиком её отдаёт подсказка. */
tr.fail td { background: rgba(192, 57, 43, .07); }
.agent {
    display: inline-block;
    max-width: 260px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    vertical-align: bottom;
}

/* На телефоне запись раскладывается карточкой, и ужимать в ней нечего:
   ширину держит сам экран. Подписанные поля фильтра занимают строку
   целиком: в других фильтрах поля лежат в .filters напрямую, а здесь
   они обёрнуты подписями — и без этого правила ужимались бы до половины
   ширины, оставляя рядом пустоту. */
@media (max-width: 640px) {
    table.stack .agent { max-width: 100%; white-space: normal; }
    .filters label:not(.check) { width: 100%; }
}

/* --- регистрации --- */

/* Выбор отрезка — те же вкладки, что у карты партии, только ссылками. */
.seg { display: flex; gap: 8px; }
.seg a {
    padding: 7px 12px;
    border: 1px solid var(--border);
    border-radius: 6px;
    color: var(--muted);
    font-size: 13px;
    white-space: nowrap;
}
.seg a:hover { color: var(--text); }
.seg a.active { border-color: var(--accent); color: var(--text); }

.tiles { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 12px; margin-bottom: 12px; }
.tile { border: 1px solid var(--border); border-radius: 8px; padding: 12px 14px; }
.tile-label { color: var(--muted); font-size: 12px; text-transform: uppercase; margin-bottom: 6px; }
.tile-value { font-size: 30px; font-weight: 600; line-height: 1.1; margin-bottom: 4px; }

/* Направление изменения — стрелкой и словом, цвет только помогает. */
.change .change-value { font-size: 20px; font-weight: 600; }
.change.up .change-value, .change.up .change-pct { color: var(--ok); }
.change.down .change-value, .change.down .change-pct { color: #e88b81; }
.tile .change .muted { display: block; }
.tile .change .change-value { font-size: 30px; }
td .change .change-value { font-size: 14px; }

/* Столбики по дням. Зазор в 2px разделяет соседние, верх скруглён,
   низ стоит на оси. Столбец целиком ловит наведение: в тонкий столбик
   мышью не попасть. */
.bars {
    display: flex;
    align-items: stretch;
    gap: 2px;
    height: 140px;
    border-bottom: 1px solid var(--border);
}
.bar { flex: 1; display: flex; align-items: flex-end; min-width: 0; }
.bar span {
    display: block;
    width: 100%;
    min-height: 3px;
    background: var(--accent);
    border-radius: 4px 4px 0 0;
}
.bar:hover span { opacity: .8; }
.bar:hover { background: rgba(255, 255, 255, .04); }
.bars-axis { display: flex; justify-content: space-between; margin-top: 6px; }

.hbar {
    display: inline-block;
    width: 120px;
    height: 8px;
    margin-right: 8px;
    vertical-align: middle;
}
.hbar span { display: block; height: 100%; background: var(--accent); border-radius: 0 4px 4px 0; }

@media (max-width: 900px) {
    .tiles { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    .hbar { width: 80px; }
}
@media (max-width: 640px) {
    .card-head:has(.seg) { flex-direction: column; }
    .tile-value, .tile .change .change-value { font-size: 24px; }
}

/* Две строки полей одной формы (страница аккаунта): без зазора подпись
   второй строки липнет к полям первой. */
.row-form + .row-form { margin-top: 12px; }

/* --- страница аккаунта --- */

/* Ник в списке «Доступов» — ссылка на страницу человека, и видно это
   должно быть сразу: раньше она выглядела обычным текстом. */
a.user-link { color: var(--accent); font-weight: 600; }
a.user-link:hover { text-decoration: underline; }

.notice {
    padding: 9px 12px;
    background: rgba(63, 166, 106, .12);
    border: 1px solid var(--ok);
    border-radius: 6px;
    font-size: 13px;
}

/* Настройки строками: подпись слева, значение и кнопка справа. */
.settings { display: grid; gap: 0; margin-bottom: 8px; }
.setting {
    display: grid;
    grid-template-columns: 180px minmax(0, 1fr);
    align-items: center;
    gap: 12px;
    padding: 10px 0;
    border-bottom: 1px solid var(--border);
}
.setting:last-child { border-bottom: none; }
.setting-name { color: var(--muted); font-size: 13px; }
.setting-value { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; }
form.inline { display: inline-flex; flex-wrap: wrap; align-items: center; gap: 6px; }
form.inline select, form.inline input { margin: 0; width: auto; }
form.inline input[type=number] { width: 90px; }
form.inline input[type=password] { width: 200px; }
button.ghost { background: none; border: 1px solid var(--border); color: var(--text); }
button.ghost:hover { border-color: var(--accent); }

.facts { display: grid; grid-template-columns: 180px minmax(0, 1fr); gap: 6px 12px; margin: 10px 0 0; }
.facts dt { color: var(--muted); font-size: 13px; }
.facts dd { margin: 0; }

@media (max-width: 640px) {
    .setting, .facts { grid-template-columns: minmax(0, 1fr); gap: 6px; }
    form.inline input[type=password] { width: 100%; }
}

/* Поиск игроков в лобби: искомые строкой, крестик справа. */
.hunt-list { list-style: none; margin: 10px 0 0; padding: 0; display: flex; flex-wrap: wrap; gap: 6px 14px; }
.hunt-list li { display: flex; align-items: center; gap: 6px; }

/* Список планов размещения героев: строки без маркеров, кнопка «Убрать»
   в строке. */
ul.hero-plans { list-style: none; padding: 0; margin: 8px 0; }
ul.hero-plans li { padding: 4px 0; border-top: 1px solid var(--border, #ddd); }

/* Плитки героев: портрет выше, чем картинка здания, — он вертикальный. */
.hero-tiles .tile img { width: 48px; height: 62px; object-fit: cover; }

/* Рейтинги газеты: ресурсы, армии, устрашение. Цвета линий — восемь категорий
   в постоянном порядке, проверенные на фоне админки (#1c1f26): яркость,
   насыщенность, различимость при дальтонизме и контраст 3:1. Цвет
   закреплён за страной, а не за местом. */
/* Переключатель разделов газеты: выбранный — с рамкой акцента. */
.paper-switch { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 10px; }
.paper-switch button.active { border-color: var(--accent); color: var(--text); }
.rank-legend { display: grid; grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); gap: 4px 16px; }
.rank-legend .place { color: var(--muted); min-width: 1.6em; text-align: right; }
.rank-chart { margin: 12px 0 4px; }
.rank-chart svg { width: 100%; height: auto; display: block; }
.rank-chart .grid { stroke: var(--border); stroke-width: 1; }
.rank-chart .tick { fill: var(--muted); font-size: 11px; }
.rank-chart .end { fill: var(--text); font-size: 11px; }
.rank-chart path { fill: none; stroke-width: 2; stroke-linejoin: round; stroke-linecap: round; }
.rank-chart circle { stroke: var(--surface); stroke-width: 2; }
.rank-chart g:hover path { stroke-width: 3; }
.rank-table .swatch {
    display: inline-block; vertical-align: -2px;
    width: 14px; height: 14px; border-radius: 3px;
}
.series-1 { --series: #3987e5; }
.series-2 { --series: #d95926; }
.series-3 { --series: #199e70; }
.series-4 { --series: #c98500; }
.series-5 { --series: #d55181; }
.series-6 { --series: #008300; }
.series-7 { --series: #9085e9; }
.series-8 { --series: #e66767; }
.rank-chart [class^="series-"] path { stroke: var(--series); }
.rank-chart [class^="series-"] circle { fill: var(--series); }
.rank-table .swatch[class*="series-"] { background: var(--series); }
