/* Эмодзи набором Discord, а не системным. У каждой платформы свой рисунок:
   один и тот же значок на iPhone, Android и Windows выглядит по-разному, и
   отзыв, написанный с телефона, на чужом экране читался иначе. Discord рисует
   Twemoji — берём тот же шрифт, чтобы сайт и бот говорили одними значками.

   Формат COLR/CPAL — векторный и лёгкий: 481 КБ на весь набор против десятка
   мегабайт у растровых. unicode-range сужен до самих эмодзи, поэтому файл
   скачивается только когда они на странице есть, и обычный текст его не ждёт. */
@font-face {
  font-family: "Twemoji";
  src: url("/assets/fonts/twemoji.woff2") format("woff2");
  font-display: swap;
  unicode-range:
    /* U+200D — невидимый соединитель. Без него составные эмодзи вроде семьи
       (человек + соединитель + человек + …) распадаются на отдельные лица:
       соединитель берётся из текстового шрифта, и лигатура не собирается.
       U+FE0E просит текстовый вид значка — его отдаём системе, не себе. */
    U+200D,
    U+00A9, U+00AE, U+203C, U+2049, U+20E3, U+2122, U+2139,
    U+2194-21AA, U+231A-231B, U+2328, U+23CF-23FA, U+24C2,
    U+25AA-25FE, U+2600-27BF, U+2934-2935, U+2B00-2BFF,
    U+3030, U+303D, U+3297, U+3299, U+FE0F,
    U+1F000-1F02F, U+1F0A0-1F0FF, U+1F100-1F1FF,
    U+1F200-1F2FF, U+1F300-1F5FF, U+1F600-1F64F,
    U+1F650-1F67F, U+1F680-1F6FF, U+1F700-1F77F,
    U+1F780-1F7FF, U+1F800-1F8FF, U+1F900-1F9FF,
    U+1FA00-1FAFF, U+E0020-E007F;
}

/* Заголовочный шрифт. В файле ровно одно начертание — Black, поэтому лицо
   объявлено весом 900. Просить у него 700 или 800 бессмысленно: браузер всё
   равно возьмёт единственное имеющееся, а с font-synthesis: none подделки не
   будет. Диапазон весов здесь указывать нельзя — это статический файл. */
@font-face {
  font-family: "BDAB Display";
  src: url("/assets/fonts/nunito-black.ttf") format("truetype");
  font-style: normal;
  font-weight: 900;
  font-display: swap;
}

/* Основной текстовый шрифт — переменный, ось насыщенности 250…900.
   Одно лицо честно закрывает все веса, которыми пользуется интерфейс. */
@font-face {
  font-family: "BDAB Sans";
  src: url("/assets/fonts/tilda-sans-vf.ttf") format("truetype-variations");
  font-style: normal;
  font-weight: 250 900;
  font-display: swap;
}

:root {
  /* Design tokens: change these first before editing component rules. */
  color-scheme: dark;
  --bg: #0d0a0b;
  --bg-soft: #17100f;
  --safe-bg: #331013;
  /* Свечение фона живёт отдельно от --accent: кнопкам нужен яркий сигнальный
     красный, подложке — глубокий вишнёвый из логотипа. Одним цветом обе роли
     не закрыть: на фоне #ef4444 отдаёт малиной. */
  --bg-glow: #db2e2e;
  --glow-1: rgba(219, 46, 46, 0.2);
  --glow-2: rgba(219, 46, 46, 0.09);
  --glow-3: rgba(255, 138, 76, 0.06);
  --surface: rgba(28, 28, 34, 0.86);
  --surface-strong: rgba(35, 35, 42, 0.92);
  --surface-plain: #19191f;
  --text: #f8f4f4;
  --muted: #a9a1a4;
  --line: rgba(255, 255, 255, 0.13);
  --layer-bg: rgba(255, 255, 255, 0.052);
  --layer-bg-strong: rgba(255, 255, 255, 0.078);
  --layer-line: rgba(255, 255, 255, 0.17);
  --layer-shadow: 0 8px 18px rgba(0, 0, 0, 0.055);
  --accent: #ef4444;
  --accent-deep: #991b1b;
  --accent-soft: rgba(239, 68, 68, 0.15);
  --discord-blurple: #5865f2;
  /* Каноничная палитра БДаБика из utils/components.py. */
  --bot-success: #57f287;
  --bot-error: #ed4245;
  --bot-gold: #f1c40f;
  --bot-mushroom: #c0392b;
  --bot-warning: #fee75c;
  --bot-neutral: #95a5a6;
  --green: var(--bot-success);
  --amber: var(--bot-gold);
  --shadow: 0 10px 24px rgba(0, 0, 0, 0.18);
  --radius: 22px;
  --radius-control: 16px;
  --radius-compact: 12px;
  --icon-radius: var(--radius-control);
  --radius-pill: 999px;
  --space-1: 6px;
  --space-2: 10px;
  --space-3: 14px;
  --space-4: 18px;
  --space-5: 24px;
  --top-icon-size: 48px;
  --top-edge-icon-size: 56px;
  --top-theme-size: 42px;
  --top-nav-gap: 12px;
  --top-actions-gap: 12px;
  --top-actions-width: calc(var(--top-theme-size) * 2 + var(--top-edge-icon-size) + var(--top-actions-gap) * 2);
  --content-width: 1180px;
  --topbar-width: 1320px;
  /* Twemoji идёт сразу за нашим шрифтом, до системных запасных. Иначе знаки
     вроде ♀ и ♂ находятся в Trebuchet MS, и составной эмодзи разрывается на
     половине: человек из одного шрифта, знак пола из другого, лигатура не
     собирается. Буквам это не мешает — у Twemoji в диапазоне только эмодзи. */
  --font-ui: "BDAB Sans", "Twemoji", "Avenir Next", "Trebuchet MS", sans-serif;
  --font-display: "BDAB Display", "BDAB Sans", "Twemoji", "Arial Rounded MT Bold", sans-serif;
  --control-height: 44px;
  /* Движение: короткое, прерываемое и одинаковое для родственных элементов. */
  --motion-quick: 150ms;
  --motion-control: 220ms;
  --motion-content: 300ms;
  --motion-bar: 420ms;
  --motion-game: 780ms;
  --motion-fluid: cubic-bezier(0.22, 1, 0.36, 1);
  --motion-standard: cubic-bezier(0.4, 0, 0.2, 1);
  --motion-emphasized: cubic-bezier(0.2, 0.8, 0.2, 1);
  --safe-top: env(safe-area-inset-top, 0px);
  --safe-right: env(safe-area-inset-right, 0px);
  --safe-bottom: env(safe-area-inset-bottom, 0px);
  --safe-left: env(safe-area-inset-left, 0px);
  --topbar-offset: 14px;
  font-family: var(--font-ui);
  font-size: 15px;
  line-height: 1.45;
}

:root[data-theme="light"] {
  /* Light theme mirrors the same token names to keep components theme-agnostic. */
  color-scheme: light;
  /* Белая тема должна быть белой: цвет приносит розовое свечение сверху, а не
     подложка и не рамки. Раньше и фон, и все слои были подкрашены тёплым
     коричнево-красным (74, 38, 38) — вместе они давали грязновато-розовый
     оттенок на всей странице вместо чистого белого. Нейтральные слои
     (28, 25, 34) оставляют свечению всю работу с цветом.

     При этом на белом всё нужно заметно контрастнее, чем на тёмном: те же
     рамки и тот же акцент, что уверенно читались на чёрном, здесь выцветали
     до пастели, и панели сливались с фоном. Поэтому линии, свечение и
     акцентные цвета тут глубже своих тёмных двойников. */
  --bg: #ffffff;
  --bg-soft: #fdf6f6;
  --safe-bg: #f1d8d5;
  --bg-glow: #db2e2e;
  --glow-1: rgba(211, 26, 26, 0.32);
  --glow-2: rgba(211, 26, 26, 0.17);
  --glow-3: rgba(255, 124, 48, 0.24);
  --surface: rgba(255, 255, 255, 0.76);
  --surface-strong: rgba(255, 255, 255, 0.88);
  --surface-plain: #ffffff;
  --text: #17151a;
  --muted: #4f4b59;
  --line: rgba(28, 25, 34, 0.30);
  /* На светлой теме внутренние слои должны быть темнее подложки, иначе белое
     на белом сливается и панели внутри карточек исчезают. */
  --layer-bg: rgba(28, 25, 34, 0.062);
  --layer-bg-strong: rgba(28, 25, 34, 0.10);
  --layer-line: rgba(28, 25, 34, 0.26);
  --layer-shadow: 0 8px 18px rgba(24, 20, 30, 0.10);
  --accent: #bf1a1a;
  --accent-deep: #7f1d1d;
  --accent-soft: rgba(191, 26, 26, 0.16);
  --green: #166b3d;
  --amber: #7d5c00;
  --shadow: 0 10px 26px rgba(24, 20, 30, 0.16);
}

* {
  box-sizing: border-box;
}

/* На слабом устройстве сохраняем цвета и иерархию, но убираем прозрачные
   слои. Режим выбирается один раз при загрузке и не дёргает вёрстку позже. */
:root[data-performance="lite"] {
  --surface: #1c1c22;
  --surface-strong: #23232a;
  --layer-bg: #29292f;
  --layer-bg-strong: #303037;
  --layer-shadow: none;
  --shadow: 0 6px 14px rgb(0 0 0 / 16%);
}

:root[data-theme="light"][data-performance="lite"] {
  --surface: #ffffff;
  --surface-strong: #ffffff;
  --layer-bg: #f4f2f5;
  --layer-bg-strong: #eceaef;
  --layer-shadow: none;
  --shadow: 0 6px 14px rgb(24 20 30 / 8%);
}

:root[data-performance="lite"] *,
:root[data-performance="lite"] *::before,
:root[data-performance="lite"] *::after {
  backdrop-filter: none !important;
  -webkit-backdrop-filter: none !important;
}

/* Горизонтальное переполнение режется на html, а не на body: на body оно
   превращало его в контейнер прокрутки, и липкая шапка прилипала к нему —
   то есть уезжала вместе со страницей вместо того, чтобы остаться на экране. */
html {
  min-height: 100%;
  overflow-x: hidden;
  overflow-y: scroll;
  scrollbar-gutter: stable;
  /* Сплошной цвет под градиентами: им закрашиваются зона выреза и пружинящий
     край прокрутки на телефоне. Без него там видно серый фон самого Safari. */
  background-color: var(--safe-bg);
  background-image:
    radial-gradient(ellipse 130rem 38rem at 50% -13rem, var(--glow-1), transparent 68%),
    radial-gradient(ellipse 55rem 60rem at 104% 22%, var(--glow-2), transparent 70%),
    radial-gradient(ellipse 70rem 42rem at -8% 104%, var(--glow-3), transparent 68%),
    linear-gradient(172deg, var(--bg) 0%, var(--bg-soft) 58%, var(--bg) 100%);
  background-position: center top;
  background-repeat: no-repeat;
  background-attachment: fixed;
}

body {
  position: relative;
  min-height: 100vh;
  margin: 0;
  color: var(--text);
  background: transparent;
  font-family: var(--font-ui);
  font-weight: 400;
  font-synthesis: none;
  letter-spacing: 0.005em;
  text-rendering: optimizeLegibility;
}

button,
input,
select {
  color: inherit;
  font-family: var(--font-ui);
  font-synthesis: none;
  appearance: none;
}

h1,
h2,
h3,
.brand strong,
.nav-item strong,
.project-card strong,
button {
  font-family: var(--font-display);
}

button {
  min-height: var(--control-height);
}

.app-shell {
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: column;
  min-height: 100vh;
  min-height: 100dvh;
}

/* Движением на сайте управляет кнопка в шапке. Системная настройка задаёт
   только начальное положение переключателя — дальше решает человек, поэтому
   гасит анимации именно атрибут, а не медиазапрос. */
:root[data-motion="off"] .page {
  animation: none;
}

:root[data-motion="off"] *,
:root[data-motion="off"] *::before,
:root[data-motion="off"] *::after {
  transition: none !important;
  animation: none !important;
  scroll-behavior: auto !important;
}

.topbar {
  /* Top navigation keeps brand/actions independent from centered project nav. */
  position: sticky;
  top: var(--topbar-offset);
  z-index: 300;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 18px;
  width: min(var(--topbar-width), calc(100% - 28px));
  min-height: 84px;
  margin: 14px auto 0;
  padding: 10px;
  border: 1.5px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface-strong);
  box-shadow: var(--shadow);
}

.glass-panel {
  position: relative;
  overflow: hidden;
}

.glass-panel::before {
  display: none;
  content: none;
}

.glass-panel > *,
.topbar > * {
  position: relative;
  z-index: 1;
}

/* Каждый блок панели лежит в своём слое, поэтому тот, что ниже в разметке,
   рисуется поверх соседей — и всплывающий список фонов уезжал под кнопку
   «Покинуть семью». Поднимаем блок, внутри которого список сейчас открыт. */
.glass-panel > *:has(.choice-menu details[open]) {
  z-index: 5;
}

.brand {
  display: flex;
  align-items: center;
  gap: 12px;
  flex: 0 0 auto;
  min-width: 184px;
  padding: 6px 8px;
  border-radius: var(--radius-control);
  color: inherit;
  text-decoration: none;
}

.brand-mark {
  position: relative;
  display: grid;
  flex: 0 0 var(--top-edge-icon-size);
  place-items: center;
  overflow: hidden;
  width: var(--top-edge-icon-size);
  min-width: var(--top-edge-icon-size);
  max-width: var(--top-edge-icon-size);
  height: var(--top-edge-icon-size);
  min-height: var(--top-edge-icon-size);
  max-height: var(--top-edge-icon-size);
  border-radius: var(--radius-control);
  background: var(--accent);
  color: white;
  font-weight: 900;
  letter-spacing: 0;
  box-shadow: 0 7px 16px rgba(239, 68, 68, 0.2);
}

/* Лого сайта — картинкой фона, а не тегом img: тема переключается кнопкой
   без перерисовки разметки, и подменять src было бы нечем. Версии с
   подложкой: тайл и так квадратный со скруглением, лого им и становится. */
.site-logo {
  background-image: url("/assets/logo-dark.svg");
  background-position: center;
  background-repeat: no-repeat;
  background-size: cover;
}

:root[data-theme="light"] .site-logo {
  background-image: url("/assets/logo-light.svg");
}

.home-logo {
  display: block;
  flex: 0 0 22px;
  width: 22px;
  height: 22px;
  border-radius: 7px;
}

.brand-mark img {
  display: block;
  width: 100%;
  max-width: 100%;
  height: 100%;
  max-height: 100%;
  border-radius: inherit;
  object-fit: cover;
}

.brand strong,
.brand small,
.nav-item strong,
.nav-item small {
  display: block;
  line-height: 1.12;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.brand > span:not(.brand-mark),
.nav-item > span:not(.nav-icon) {
  display: grid;
  align-content: center;
  gap: 3px;
  min-width: 0;
}

.nav-item > span:not(.nav-icon) {
  min-height: var(--top-icon-size);
}

.brand small,
.nav-item small,
.metric span,
.command-row small,
.timeline small,
p {
  color: var(--muted);
}

.nav-list {
  position: absolute;
  left: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--top-nav-gap);
  max-width: calc(100% - 420px);
  transform: translateX(-50%);
}

.nav-item {
  position: relative;
  overflow: hidden;
  display: flex;
  align-items: center;
  gap: 12px;
  box-sizing: border-box;
  width: 194px;
  min-width: 194px;
  max-width: 194px;
  height: 64px;
  min-height: 64px;
  max-height: 64px;
  /* Слева на полпикселя больше, чем сверху и снизу: рядом с закруглённым
     углом ровно равный отступ читается как более тесный. Ровно 8 выглядели
     поджато, прежние 9,5 — уже с перекосом. */
  padding: 0 14px 0 7px;
  border: 1.5px solid transparent;
  border-radius: var(--radius-control);
  background: transparent;
  color: inherit;
  outline: none;
  text-align: left;
  text-decoration: none;
  -webkit-tap-highlight-color: transparent;
  cursor: pointer;
  transition:
    transform var(--motion-control) var(--motion-fluid),
    background var(--motion-control) var(--motion-standard),
    border-color var(--motion-control) var(--motion-standard);
}

.nav-item:focus-visible {
  border-color: color-mix(in srgb, var(--project-color, var(--accent)) 28%, var(--line));
  outline: none;
}

.nav-item:hover {
  transform: translateY(-1px);
  border-color: color-mix(in srgb, var(--project-color, var(--accent)) 18%, var(--line));
  background: color-mix(in srgb, var(--project-color, var(--accent)) 5%, var(--surface));
}

.nav-item.active {
  border-color: color-mix(in srgb, var(--project-color, var(--accent)) 30%, var(--line));
  background: color-mix(in srgb, var(--project-color, var(--accent)) 7%, var(--surface-strong));
  box-shadow: 0 8px 18px rgba(0, 0, 0, 0.06);
}

/* Содержимое пункта лежит над подсветкой, а размеры значка — только у значка:
   когда эти два правила слиплись, подписи в меню растянуло под размер иконки. */
.nav-item > * {
  position: relative;
  z-index: 1;
}

.nav-icon,
.action-icon {
  display: grid;
  flex: 0 0 var(--top-icon-size);
  place-items: center;
  width: var(--top-icon-size);
  min-width: var(--top-icon-size);
  max-width: var(--top-icon-size);
  height: var(--top-icon-size);
  min-height: var(--top-icon-size);
  max-height: var(--top-icon-size);
  border-radius: var(--icon-radius);
  color: var(--project-color, var(--accent));
  font-size: 13px;
  font-weight: 900;
  line-height: 1;
}

.nav-icon {
  position: relative;
  overflow: hidden;
  box-sizing: border-box;
  border: 0;
  /* Скругление подобрано на глаз: дуга значка продолжает дугу кнопки, а не
     спорит с ней. Строгая формула «радиус кнопки минус поле» давала слишком
     острые углы. */
  border-radius: 10px;
  background: transparent;
  box-shadow: none;
}

.action-icon {
  border: 1.5px solid var(--layer-line);
  background: var(--layer-bg);
  box-shadow: var(--layer-shadow);
}

.action-icon {
  width: 34px;
  min-width: 34px;
  max-width: 34px;
  height: 34px;
  min-height: 34px;
  max-height: 34px;
}

.nav-icon img {
  display: block;
  flex: 0 0 auto;
  width: 100%;
  max-width: 100%;
  height: 100%;
  max-height: 100%;
  aspect-ratio: 1 / 1;
  border-radius: inherit;
  object-fit: cover;
}

.nav-item[data-section="discord"] .nav-icon img {
  width: 100%;
  max-width: 100%;
  height: 100%;
  max-height: 100%;
  object-fit: cover;
}

.top-actions {
  display: flex;
  align-items: center;
  gap: var(--top-actions-gap);
  flex-shrink: 0;
  min-width: max-content;
  z-index: 2;
}

.profile-menu-wrap {
  position: relative;
  display: grid;
  flex: 0 0 var(--top-edge-icon-size);
  place-items: center;
}

.search-button,
.theme-button,
.motion-button,
.discord-login-button {
  position: relative;
  overflow: hidden;
  display: grid;
  place-items: center;
  padding: 0;
  border-radius: var(--radius-control);
  cursor: pointer;
  line-height: 0;
  transition:
    background var(--motion-control) var(--motion-standard),
    color var(--motion-control) var(--motion-standard),
    border-color var(--motion-control) var(--motion-standard),
    scale var(--motion-control) var(--motion-fluid);
}

.search-button,
.theme-button,
.motion-button {
  flex: 0 0 var(--top-theme-size);
  width: var(--top-theme-size);
  min-width: var(--top-theme-size);
  max-width: var(--top-theme-size);
  height: var(--top-theme-size);
  min-height: var(--top-theme-size);
  max-height: var(--top-theme-size);
  border: 0;
  background: transparent;
  box-shadow: none;
  color: var(--text);
}

.search-button svg,
.theme-button span,
.theme-button svg,
.motion-button svg {
  display: block;
}

.theme-button span {
  display: grid;
  place-items: center;
  width: 100%;
  height: 100%;
}

.search-button svg,
.theme-button svg {
  width: 22px;
  height: 22px;
}

@media (hover: hover) and (pointer: fine) {
  .search-button:hover,
  .theme-button:hover,
  .motion-button:hover {
  background: color-mix(in srgb, var(--text) 6%, transparent);
  color: color-mix(in srgb, var(--text) 82%, var(--accent));
}
}

.search-button:active,
.theme-button:active {
  background: color-mix(in srgb, var(--text) 9%, transparent);
}

.discord-login-button {
  flex: 0 0 var(--top-edge-icon-size);
  width: var(--top-edge-icon-size);
  min-width: var(--top-edge-icon-size);
  max-width: var(--top-edge-icon-size);
  height: var(--top-edge-icon-size);
  min-height: var(--top-edge-icon-size);
  max-height: var(--top-edge-icon-size);
  border: 0 !important;
  background: color-mix(in srgb, var(--discord-blurple) 12%, transparent);
  box-shadow: none;
}

.discord-login-button:hover {
  border-color: transparent !important;
  background: color-mix(in srgb, var(--discord-blurple) 18%, transparent);
}

.discord-login-button.authenticated {
  background: var(--layer-bg);
}

.discord-login-button .login-door-icon {
  display: block;
  width: 29px;
  height: 29px;
  color: var(--text);
  /* Контур занимает x=3…19 внутри viewBox 24×24, поэтому математический
     центр рисунка на пиксель левее центра кнопки. */
  transform: translateX(1px);
}

.discord-login-button img {
  display: block;
  width: 100%;
  min-width: 100%;
  max-width: 100%;
  height: 100%;
  min-height: 100%;
  max-height: 100%;
  aspect-ratio: 1 / 1;
  flex: 0 0 100%;
  border-radius: inherit;
  object-fit: cover;
}

.discord-login-button .avatar-letter {
  display: grid;
  place-items: center;
  width: 100%;
  height: 100%;
  border-radius: inherit;
  background: color-mix(in srgb, var(--discord-blurple) 84%, white);
  color: white;
  font-size: 24px;
  font-weight: 900;
  line-height: 1;
}

.profile-menu {
  position: absolute;
  top: calc(100% + 14px);
  right: 0;
  z-index: 40;
  width: min(340px, calc(100vw - 28px));
  padding: 12px;
  border: 1.5px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface-strong);
  box-shadow: var(--shadow);
  opacity: 0;
  transform: translate3d(0, -8px, 0) scale(0.97);
  transform-origin: top right;
  pointer-events: none;
  transition:
    opacity var(--motion-control) var(--motion-standard),
    transform var(--motion-content) var(--motion-fluid);
}

.profile-menu[hidden] {
  display: none;
}

.profile-menu.is-open {
  opacity: 1;
  transform: translate3d(0, 0, 0) scale(1);
  pointer-events: auto;
}

.profile-menu::before {
  content: "";
  position: absolute;
  right: 24px;
  top: -6px;
  width: 12px;
  height: 12px;
  border-top: 1.5px solid var(--line);
  border-left: 1.5px solid var(--line);
  background: inherit;
  transform: rotate(45deg);
}

.profile-menu-head {
  position: relative;
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 8px;
}

.profile-menu-avatar {
  display: grid;
  flex: 0 0 48px;
  place-items: center;
  overflow: hidden;
  width: 48px;
  height: 48px;
  border-radius: var(--icon-radius);
  background: color-mix(in srgb, var(--discord-blurple) 18%, transparent);
  color: white;
  font-weight: 900;
}

.profile-menu-avatar img,
.profile-menu-avatar .avatar-letter {
  display: grid;
  place-items: center;
  width: 100%;
  height: 100%;
  border-radius: inherit;
  object-fit: cover;
}

.profile-menu-head strong,
.profile-menu-head small {
  display: block;
  line-height: 1.2;
}

.profile-menu-head small {
  margin-top: 5px;
  color: var(--muted);
}

.profile-menu-list {
  display: grid;
  gap: 8px;
  margin: 10px 0;
}

.profile-menu-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  min-height: 44px;
  padding: 10px 12px;
  border: 1.5px solid var(--layer-line);
  border-radius: var(--radius-control);
  background: var(--layer-bg);
  box-shadow: var(--layer-shadow);
}

.profile-menu-row span {
  color: var(--muted);
}

.profile-menu-row strong {
  text-align: right;
}

.profile-menu-logout {
  display: grid;
  place-items: center;
  width: 100%;
  min-height: 46px;
  margin-top: 10px;
  border: 1.5px solid color-mix(in srgb, var(--accent) 34%, var(--layer-line));
  border-radius: var(--radius-control);
  background: color-mix(in srgb, var(--accent) 12%, var(--layer-bg));
  color: var(--text);
  font: inherit;
  font-weight: 900;
  cursor: pointer;
  transition:
    background var(--motion-control) var(--motion-standard),
    border-color var(--motion-control) var(--motion-standard),
    transform var(--motion-control) var(--motion-fluid),
    scale var(--motion-control) var(--motion-fluid);
}

.profile-menu-logout:hover {
  border-color: color-mix(in srgb, var(--accent) 52%, var(--layer-line));
  background: color-mix(in srgb, var(--accent) 18%, var(--layer-bg));
}

.profile-menu-logout:active {
  transform: translateY(1px);
}

.theme-fade {
  position: fixed;
  inset: 0;
  z-index: 9999;
  pointer-events: none;
  opacity: 0;
  transition: opacity var(--motion-control) var(--motion-standard);
  will-change: opacity;
}

:root.theme-switching *:not(.theme-fade),
:root.theme-switching *:not(.theme-fade)::before,
:root.theme-switching *:not(.theme-fade)::after {
  transition: none !important;
}

.theme-fade.to-light {
  background:
    radial-gradient(ellipse 95rem 48rem at 18% -12rem, rgba(220, 38, 38, 0.14), transparent 72%),
    radial-gradient(ellipse 78rem 44rem at 86% -10rem, rgba(184, 107, 0, 0.1), transparent 74%),
    linear-gradient(145deg, #f6f2f0, #efe6e3);
}

.theme-fade.to-dark {
  background:
    radial-gradient(ellipse 95rem 48rem at 18% -12rem, rgba(239, 68, 68, 0.2), transparent 72%),
    radial-gradient(ellipse 78rem 44rem at 86% -10rem, rgba(245, 158, 11, 0.12), transparent 74%),
    linear-gradient(145deg, #0c0c0f, #161116);
}

.theme-fade.active {
  opacity: 0.92;
}

.theme-fade.done {
  opacity: 0;
}

.workspace {
  flex: 1 0 auto;
  width: min(var(--content-width), calc(100% - 28px));
  margin: 0 auto;
}

.page-stage {
  padding: 26px 0 54px;
  overflow-anchor: none;
}

/* Подвал повторяет верхнюю панель: та же ширина, рамка, скругление и фон —
   страница получается зажатой между двумя одинаковыми плашками. */
.site-footer {
  position: static;
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 18px;
  width: min(var(--topbar-width), calc(100% - 28px));
  min-height: 84px;
  /* auto сверху прижимает подвал к низу экрана на коротких страницах. */
  margin: auto auto 20px;
  padding: 10px;
  border: 1.5px solid color-mix(in srgb, var(--line) 30%, transparent);
  border-radius: var(--radius);
  /* Подвал заметно прозрачнее меню: он служебный и не должен спорить с шапкой. */
  background: color-mix(in srgb, var(--surface-strong) 18%, transparent);
  transform: none;
}

.site-footer-inner {
  display: flex;
  flex: 1;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
  min-width: 0;
}

.site-footer-links {
  display: flex;
  flex: 1;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
  min-width: 0;
}

.site-footer-links a {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  min-height: var(--control-height);
  padding: 8px 14px;
  border: 1.5px solid color-mix(in srgb, var(--layer-line) 45%, transparent);
  border-radius: var(--radius-compact);
  background: color-mix(in srgb, var(--layer-bg-strong) 35%, transparent);
  color: var(--muted);
  font-size: 13px;
  font-weight: 800;
  line-height: 1;
  text-decoration: none;
  transition: color var(--motion-quick) var(--motion-standard),
              border-color var(--motion-quick) var(--motion-standard);
}

.site-footer-links a:hover {
  border-color: var(--accent);
  color: var(--text);
}

/* Значок и подпись стоят на одной оси: у svg своя базовая линия, и без
   выключенного baseline он съезжает вниз на пару пикселей. */
.site-footer-links svg {
  flex: none;
  display: block;
}

@media (max-width: 720px) {
  .site-footer {
    min-height: 0;
    padding: var(--space-3);
  }

  /* На телефоне подвал — одна строка значков без подписей. */
  .site-footer-links {
    flex-wrap: nowrap;
    justify-content: space-between;
    width: 100%;
    gap: var(--space-1);
  }

  .site-footer-links a {
    flex: 1 1 0;
    justify-content: center;
    padding: 10px 0;
  }

  .site-footer-links a span {
    display: none;
  }

  .site-footer-inner {
    justify-content: center;
  }

  .site-footer-links {
    justify-content: center;
  }
}

/* minmax(0, 1fr), а не просто одна колонка: у автоматической колонки нижняя
   граница равна самому широкому содержимому страницы, и одна широкая панель
   растягивала всю сетку — на планшете страница уезжала вбок целиком. */
.page {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 18px;
}

.page.page-enter {
  animation: page-in var(--motion-content) var(--motion-fluid) both;
}

@keyframes page-in {
  from {
    opacity: 0;
    transform: translate3d(0, 7px, 0) scale(0.995);
  }
}

.hero {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 18px;
}

.hero-discord .hero-copy {
  min-height: 210px;
}

.hero-discord h1 {
  max-width: none;
  font-size: clamp(36px, 4.3vw, 58px);
}

.hero-copy,
.panel,
.metric,
.command-row {
  /* Общий слой панели. Вложенные слои используют токены --layer-*. */
  border: 1.5px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface);
  box-shadow: var(--shadow);
}

.hero-copy {
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  min-height: 338px;
  padding: clamp(24px, 4vw, 38px);
}

.eyebrow {
  display: inline-flex;
  width: fit-content;
  align-items: center;
  gap: 8px;
  margin-bottom: 20px;
  padding: 8px 12px;
  border: 1.5px solid var(--layer-line);
  border-radius: var(--radius-pill);
  background: var(--layer-bg);
  box-shadow: var(--layer-shadow);
  color: var(--muted);
  font-size: 13px;
}

.pulse {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--green);
  box-shadow: 0 0 0 5px color-mix(in srgb, var(--green) 14%, transparent);
}

h1,
h2,
h3,
p {
  margin: 0;
}

h1 {
  max-width: 760px;
  font-size: clamp(35px, 5vw, 64px);
  line-height: 0.98;
  letter-spacing: 0;
}

h2 {
  font-size: 22px;
  letter-spacing: 0;
}

h3 {
  font-size: 16px;
}

p {
  line-height: 1.5;
}

.hero-copy p {
  max-width: 700px;
  margin-top: 18px;
  font-size: 16px;
}

.hero-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  margin-top: 30px;
}

.primary-button,
.secondary-button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 46px;
  padding: 0 16px;
  border-radius: var(--radius-control);
  border: 1.5px solid var(--layer-line);
  background: var(--layer-bg);
  box-shadow: var(--layer-shadow);
  text-decoration: none;
  cursor: pointer;
  transition:
    transform var(--motion-control) var(--motion-fluid),
    background var(--motion-control) var(--motion-standard),
    border-color var(--motion-control) var(--motion-standard),
    scale var(--motion-control) var(--motion-fluid);
}

.primary-button {
  border-color: color-mix(in srgb, var(--accent) 32%, var(--line));
  color: color-mix(in srgb, var(--accent) 22%, var(--text));
  font-weight: 800;
}

.secondary-button {
  color: var(--text);
}

.primary-button:hover,
.secondary-button:hover {
  transform: translateY(-2px);
  border-color: color-mix(in srgb, var(--accent) 42%, var(--line));
  background: color-mix(in srgb, var(--accent) 9%, var(--surface-strong));
}

.grid-4,
.grid-3,
.grid-2 {
  display: grid;
  gap: 14px;
}

.grid-3 {
  grid-template-columns: repeat(3, minmax(0, 1fr));
}

.grid-4 {
  grid-template-columns: repeat(4, minmax(0, 1fr));
}

.grid-2 {
  grid-template-columns: repeat(2, minmax(0, 1fr));
  align-items: start;
}

.project-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 14px;
}

.project-card {
  display: grid;
  grid-template-columns: 48px minmax(0, 1fr) auto;
  align-items: center;
  gap: 14px;
  min-height: 92px;
  padding: 16px;
  border: 1.5px solid var(--layer-line);
  border-radius: var(--radius);
  background: var(--layer-bg);
  box-shadow: var(--layer-shadow);
  color: inherit;
  text-align: left;
  line-height: 1.2;
  cursor: pointer;
  transition:
    transform var(--motion-control) var(--motion-fluid),
    border-color var(--motion-control) var(--motion-standard),
    background var(--motion-control) var(--motion-standard),
    scale var(--motion-control) var(--motion-fluid);
}

.project-card:hover {
  transform: translateY(-2px);
  border-color: color-mix(in srgb, var(--project-color, var(--accent)) 38%, var(--line));
  background: color-mix(in srgb, var(--project-color, var(--accent)) 8%, var(--surface));
}

.project-card-icon {
  display: grid;
  place-items: center;
  overflow: hidden;
  width: 48px;
  min-width: 48px;
  max-width: 48px;
  height: 48px;
  min-height: 48px;
  max-height: 48px;
  border-radius: var(--icon-radius);
  border: 0;
  background: transparent;
  box-shadow: none;
  color: var(--project-color, var(--accent));
  font-weight: 900;
}

.project-card-icon img {
  display: block;
  width: 100%;
  max-width: 100%;
  height: 100%;
  max-height: 100%;
  border-radius: inherit;
  object-fit: cover;
}

.project-card[data-jump-section="discord"] .project-card-icon img {
  width: 100%;
  max-width: 100%;
  height: 100%;
  max-height: 100%;
  object-fit: cover;
}

.project-card strong,
.project-card small {
  display: block;
  line-height: 1.18;
}

.project-card small {
  color: var(--muted);
  margin-top: 3px;
}

.project-card-arrow {
  color: var(--muted);
  font-size: 24px;
}

.games-showcase {
  --project-color: var(--amber);
}

.game-grid {
  display: grid;
  grid-template-columns: minmax(0, 1.3fr) repeat(2, minmax(0, 1fr));
  gap: 12px;
}

.game-card {
  display: grid;
  align-content: space-between;
  gap: 28px;
  min-height: 210px;
  padding: 16px;
  border: 1.5px solid var(--layer-line);
  border-radius: var(--radius);
  background: var(--layer-bg);
  color: inherit;
  text-align: left;
  box-shadow: var(--layer-shadow);
  transition:
    transform var(--motion-control) var(--motion-fluid),
    border-color var(--motion-control) var(--motion-standard),
    background var(--motion-control) var(--motion-standard),
    scale var(--motion-control) var(--motion-fluid);
}

.game-card.featured {
  min-height: 250px;
  background: var(--layer-bg-strong);
}

.game-card:hover {
  transform: translateY(-2px);
  border-color: color-mix(in srgb, var(--project-color, var(--accent)) 42%, var(--line));
}

.game-card-top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}

.game-icon {
  display: grid;
  place-items: center;
  line-height: 1;
  width: 46px;
  height: 46px;
  border-radius: var(--radius-control);
  border: 1.5px solid var(--layer-line);
  background: var(--layer-bg-strong);
  color: var(--project-color, var(--accent));
  font-weight: 900;
}

.game-status {
  padding: 6px 9px;
  border: 1.5px solid var(--layer-line);
  border-radius: var(--radius-pill);
  background: var(--layer-bg);
  color: color-mix(in srgb, var(--project-color, var(--accent)) 82%, var(--text));
  font-size: 12px;
  font-weight: 800;
}

.game-card strong,
.game-card small {
  display: block;
  line-height: 1.18;
}

.game-card strong {
  font-size: 22px;
}

.game-card small {
  max-width: 42ch;
  margin-top: 8px;
  color: var(--muted);
  line-height: 1.55;
}

.alias-preview {
  display: grid;
  grid-template-columns: minmax(150px, 0.46fr) minmax(0, 1fr);
  gap: 12px;
}

.alias-word,
.alias-rules,
.reward-list {
  border: 1.5px solid var(--layer-line);
  border-radius: var(--radius-control);
  background: var(--layer-bg);
  box-shadow: var(--layer-shadow);
}

.alias-word {
  display: grid;
  place-items: center;
  min-height: 150px;
  padding: 16px;
  text-align: center;
}

.alias-word small {
  color: var(--muted);
  font-weight: 700;
}

.alias-word strong {
  font-size: 32px;
}

.alias-rules {
  display: grid;
  align-content: center;
  gap: 10px;
  padding: 12px;
}

.layer-panel {
  border-color: var(--layer-line);
  box-shadow: var(--layer-shadow);
}

.layer-chip {
  border: 1.5px solid var(--layer-line);
  box-shadow: var(--layer-shadow);
}

.alias-rules .layer-chip,
.reward-list .layer-chip {
  min-height: 42px;
  padding: 10px 12px;
  border-radius: var(--radius-compact);
  background: var(--layer-bg-strong);
}

.alias-rules .layer-chip {
  font-weight: 750;
}

.reward-list {
  display: grid;
  gap: 8px;
  padding: 10px;
}

.reward-list .layer-chip {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}

.reward-list span {
  color: var(--muted);
  text-align: right;
}

.panel,
.metric {
  --panel-padding: 18px;
  padding: var(--panel-padding);
}

.metric {
  display: grid;
  align-content: center;
  gap: 8px;
  min-height: 92px;
}

.metric strong {
  font-size: 26px;
  line-height: 1.08;
}

.panel-header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 18px;
  margin-bottom: 18px;
}

.panel-header p {
  max-width: 64ch;
}

.tag {
  display: none;
  align-items: center;
  justify-content: center;
  width: fit-content;
  min-height: 30px;
  padding: 6px 10px;
  border: 1.5px solid var(--layer-line);
  border-radius: var(--radius-pill);
  background: var(--layer-bg);
  box-shadow: var(--layer-shadow);
  color: color-mix(in srgb, var(--accent) 84%, var(--text));
  font-size: 12px;
  font-weight: 800;
  line-height: 1;
  white-space: nowrap;
}

.command-list {
  display: grid;
  gap: 10px;
}

.account-panel {
  --project-color: var(--discord-blurple);
}

.birthday-control {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: center;
  gap: 12px;
  margin-top: 12px;
  padding: 12px;
  border: 1.5px solid var(--layer-line);
  border-radius: var(--radius-control);
  background: var(--layer-bg);
  box-shadow: var(--layer-shadow);
}

.birthday-icon {
  /* Эмодзи сидит на базовой линии, и строка внутри квадрата выше самого
     глифа — из-за этого значок прижимался к верху рамки, хотя сама
     коробка отцентрована. line-height: 1 ужимает строку до кегля. */
  line-height: 1;
  display: grid;
  place-items: center;
  width: 42px;
  height: 42px;
  border-radius: var(--radius-compact);
  background: color-mix(in srgb, #f472b6 15%, var(--layer-bg-strong));
  font-size: 27px;
}

.birthday-control > span:nth-child(2) {
  min-width: 0;
}

.birthday-control > span:nth-child(2) > strong,
.birthday-status {
  display: block;
}

.birthday-status {
  margin-top: 3px;
  color: var(--muted);
  font-size: 12px;
  font-weight: 700;
}

/* Кнопка без подписи — ровный квадрат: высота у компактных кнопок 40, значит
   и ширина 40. Раньше каждая такая кнопка ставила свою ширину и получался
   прямоугольник. */
.icon-button {
  display: grid;
  place-items: center;
  flex: 0 0 auto;
  width: 40px;
  min-width: 40px;
  height: 40px;
  min-height: 40px;
  padding: 0;
}

.compact-button,
.inventory-toggle {
  min-height: 40px;
  padding: 9px 12px;
  border: 1.5px solid var(--layer-line);
  border-radius: var(--radius-compact);
  background: var(--layer-bg-strong);
  color: var(--text);
  font-weight: 800;
  cursor: pointer;
  transition:
    transform var(--motion-control) var(--motion-fluid),
    border-color var(--motion-control) var(--motion-standard),
    background var(--motion-control) var(--motion-standard),
    scale var(--motion-control) var(--motion-fluid);
}

.compact-button:hover,
.inventory-toggle:hover {
  transform: translateY(-1px);
  border-color: color-mix(in srgb, var(--discord-blurple) 38%, var(--layer-line));
  background: color-mix(in srgb, var(--discord-blurple) 11%, var(--layer-bg-strong));
}

.birthday-form {
  grid-column: 2 / -1;
  display: grid;
  align-items: end;
  gap: 10px;
}

.birthday-form label {
  display: grid;
  gap: 6px;
}

.birthday-form label > span {
  color: var(--muted);
  font-size: 11px;
  font-weight: 800;
}

.birthday-form input,
.command-search input {
  width: 100%;
  min-height: 44px;
  padding: 0 13px;
  border: 1.5px solid var(--layer-line);
  border-radius: var(--radius-compact);
  outline: none;
  background: var(--layer-bg-strong);
  color: var(--text);
  font: inherit;
}

.birthday-form input:focus,
.command-search input:focus {
  border-color: color-mix(in srgb, var(--discord-blurple) 58%, var(--layer-line));
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--discord-blurple) 13%, transparent);
}

.command-catalog {
  --project-color: var(--discord-blurple);
}

.command-search {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  align-items: center;
  gap: 12px;
  margin-bottom: 12px;
  padding: 10px 12px;
  border: 1.5px solid var(--layer-line);
  border-radius: var(--radius-control);
  background: var(--layer-bg);
}

.command-search > span {
  color: var(--muted);
  font-size: 12px;
  font-weight: 850;
}

.command-categories {
  display: grid;
  gap: 9px;
}

.command-category {
  overflow: hidden;
  border: 1.5px solid var(--layer-line);
  border-radius: var(--radius-control);
  background: var(--layer-bg);
  box-shadow: var(--layer-shadow);
}

.command-category[hidden],
.catalog-command[hidden] {
  display: none;
}

.command-category summary {
  justify-content: space-between;
  min-height: 48px;
  padding: 10px 13px;
  list-style: none;
  cursor: pointer;
}

.command-category summary::-webkit-details-marker {
  display: none;
}

.command-category summary::after {
  content: "↓";
}


.command-category summary small {
  order: 2;
  min-width: 29px;
  margin-left: 10px;
  color: var(--muted);
  text-align: right;
}

.command-catalog-list {
  display: grid;
  gap: 1px;
  padding: 0 8px 8px;
}

.catalog-command {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(290px, auto);
  align-items: center;
  gap: 14px;
  min-height: 66px;
  padding: 10px;
  border-top: 1px solid var(--layer-line);
}

.catalog-command p {
  margin-top: 4px;
  font-size: 12px;
  line-height: 1.4;
}

.command-forms {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: 5px;
}

.command-forms code {
  padding: 6px 8px;
  border: 1px solid color-mix(in srgb, var(--discord-blurple) 24%, var(--layer-line));
  border-radius: 8px;
  background: color-mix(in srgb, var(--discord-blurple) 8%, var(--layer-bg-strong));
  color: color-mix(in srgb, var(--discord-blurple) 46%, var(--text));
  font-family: ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace;
  font-size: 11px;
  white-space: nowrap;
}

.command-empty {
  margin-top: 12px;
  color: var(--muted);
  font-weight: 750;
}

.discord-server-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  align-items: stretch;
  gap: 14px;
}

.news-panel,
.news-panel {
  display: grid;
  grid-template-rows: auto minmax(0, 1fr);
}

.news-list {
  display: grid;
  align-content: start;
  gap: 10px;
  min-height: 0;
  overflow: hidden auto;
  padding: 2px 8px 8px 2px;
  scrollbar-gutter: stable;
  touch-action: pan-y;
}

.news-item {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 12px;
  padding: 13px;
  border: 1.5px solid var(--layer-line);
  border-radius: var(--radius-control);
  background: var(--layer-bg);
  box-shadow: var(--layer-shadow);
}

.news-item.featured:has(.news-image) {
  grid-template-columns: minmax(170px, 0.65fr) minmax(0, 1.35fr);
}

.news-image {
  display: block;
  overflow: hidden;
  min-height: 160px;
  border-radius: var(--radius-compact);
  background: var(--layer-bg-strong);
}

.news-image img {
  display: block;
  width: 100%;
  height: 100%;
  max-height: 240px;
  object-fit: cover;
}

.news-date {
  color: var(--muted);
  font-size: 11px;
  font-weight: 750;
}

.news-item h3 {
  margin-top: 5px;
  font-size: 18px;
}

.news-item p {
  display: -webkit-box;
  overflow: hidden;
  margin-top: 7px;
  line-height: 1.5;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 5;
}

.text-link {
  display: inline-flex;
  margin-top: 9px;
  color: color-mix(in srgb, var(--discord-blurple) 62%, var(--text));
  font-size: 12px;
  font-weight: 800;
  text-decoration: none;
}

.role-cloud {
  display: flex;
  flex-wrap: wrap;
  align-content: flex-start;
  gap: 8px;
}

.role-chip {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  min-height: 34px;
  padding: 7px 10px;
  border: 1px solid color-mix(in srgb, var(--role-color) 38%, var(--layer-line));
  border-radius: var(--radius-compact);
  background: color-mix(in srgb, var(--role-color) 9%, var(--layer-bg));
  font-size: 12px;
  font-weight: 780;
}

.role-chip i {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--role-color);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--role-color) 14%, transparent);
}

.account-card {
  display: grid;
  grid-template-columns: auto minmax(140px, 0.36fr) minmax(0, 1.64fr);
  align-items: center;
  gap: 12px;
  min-height: 72px;
  padding: 12px;
  border: 1.5px solid var(--layer-line);
  border-radius: var(--radius-control);
  background: var(--layer-bg);
  box-shadow: var(--layer-shadow);
}

.account-identity {
  min-width: 0;
}

.account-economy {
  /* Семь клеток ровно в один ряд. Разваливался он раньше не от количества,
     а от того, что каждой было задано 124 пикселя минимум. */
  display: grid;
  grid-template-columns: repeat(7, minmax(0, 1fr));
  gap: 5px;
  align-content: center;
  min-width: 0;
  width: 100%;
  margin-left: 0;
}

.account-economy > span {
  display: grid;
  gap: 2px;
  min-width: 0;
  padding: 6px 7px;
  border: 1.5px solid var(--layer-line);
  border-radius: var(--radius-compact);
  background: var(--layer-bg-strong);
  text-align: right;
}

.account-economy small {
  font-size: 11px;
}

.account-economy .money-figure,
.account-economy > span > strong {
  font-size: 13px;
}

/* Итог — то, что ищут глазами первым, поэтому рамка теплее остальных. */
.account-economy > .account-total {
  border-color: color-mix(in srgb, var(--accent) 40%, var(--layer-line));
}

.account-economy small,
.account-economy-status {
  color: var(--muted);
}

.account-economy-status {
  margin-left: auto;
  font-size: 13px;
  font-weight: 800;
}

.account-loading {
  display: flex;
  align-items: center;
}

.profile-stat-grid {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 14px;
}

.profile-stat {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 16px;
  border: 1.5px solid color-mix(in srgb, var(--stat-color) 34%, var(--layer-line));
  border-radius: var(--radius);
  background: color-mix(in srgb, var(--stat-color) 6%, var(--surface));
  box-shadow: var(--shadow);
}

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

   Положение фиксированное, а не по месту в карточке: у карточки обрезаются
   края (overflow: hidden), и подсказка под ней просто не показывалась бы.
   Точку считает скрипт один раз, когда указатель заходит на карточку. */
.profile-stat-tip {
  position: fixed;
  z-index: 120;
  left: var(--profile-tip-x, 0);
  top: var(--profile-tip-y, 0);
  width: var(--profile-tip-w, 240px);
  padding: 9px 11px;
  border: 1px solid var(--layer-line);
  border-radius: var(--radius-compact);
  background: var(--surface-strong);
  box-shadow: var(--shadow);
  color: var(--text);
  font-size: 13px;
  font-weight: 650;
  line-height: 1.35;
  opacity: 0;
  pointer-events: none;
  transform: translateY(-4px);
  transition: opacity 120ms ease, transform 120ms ease;
}

/* Только наведение и только клавиатурный фокус. От :focus-within подсказка
   оставалась висеть после обычного щелчка мышью, хотя курсор уже ушёл. */
.profile-stat:hover .profile-stat-tip,
.profile-stat:focus-visible .profile-stat-tip {
  opacity: 1;
  transform: translateY(0);
}

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

   Поэтому на касании это полоса внизу экрана во всю ширину: место под неё
   всегда одно и то же, читать удобно, и промахнуться мимо неё нечем. */
@media (hover: none) {
  .profile-stat-tip {
    left: 12px;
    right: 12px;
    top: auto;
    bottom: calc(12px + env(safe-area-inset-bottom, 0px));
    width: auto;
    padding: 12px 14px;
    border-radius: var(--radius-control);
    box-shadow: 0 12px 32px rgb(0 0 0 / 45%);
    transform: translateY(8px);
    /* Полоса висит поверх содержимого, а обычная подложка панелей прозрачна
       на восемь процентов — сквозь неё просвечивал текст под ней. Кладём
       непрозрачный фон страницы вниз, а поверх — ту же подложку. */
    background:
      linear-gradient(var(--surface-strong), var(--surface-strong)),
      var(--bg);
  }

  .profile-stat:focus-within .profile-stat-tip {
    opacity: 1;
    transform: translateY(0);
  }
}

.profile-stat-level {
  --stat-color: var(--bot-gold);
}

.profile-stat-streak {
  --stat-color: var(--bot-mushroom);
}

.profile-stat-family {
  --stat-color: var(--bot-success);
}

.profile-stat-inventory {
  --stat-color: var(--bot-neutral);
}

.profile-stat.is-burned {
  --stat-color: var(--bot-error);
}

.profile-stat.is-unlit {
  --stat-color: var(--bot-warning);
}

/* Шапка приклеена к верху карточки, остальное — к низу. Одинаковый первый ряд
   у всех карточек не зависит от того, сколько строк идёт ниже. */
.profile-stat-head {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  align-items: center;
  gap: 11px;
  margin-bottom: auto;
}

.profile-stat-icon {
  line-height: 1;
  display: grid;
  place-items: center;
  width: 42px;
  height: 42px;
  border: 1px solid color-mix(in srgb, var(--stat-color) 45%, var(--layer-line));
  border-radius: 14px;
  background: color-mix(in srgb, var(--stat-color) 16%, var(--layer-bg));
  font-size: 28px;
}

.profile-stat small,
.profile-stat-note {
  color: var(--muted);
  font-size: 13px;
  font-weight: 750;
}

.profile-stat strong {
  display: block;
  overflow: hidden;
  font-size: 21px;
  line-height: 1.12;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.profile-stat-note {
  overflow: hidden;
  margin-top: 7px;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Нижняя строка: XP и место в рейтинге в одной линии. */
.profile-stat-foot {
  display: flex;
  gap: 6px;
  align-items: baseline;
  min-width: 0;
}

.profile-stat-foot span {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.profile-stat-foot .profile-stat-rank {
  flex: none;
  color: var(--stat-color);
}

.profile-progress {
  overflow: hidden;
  height: 7px;
  margin-top: 4px;
  border-radius: var(--radius-pill);
  background: var(--layer-bg-strong);
}

.profile-progress i {
  display: block;
  height: 100%;
  border-radius: inherit;
  background: var(--stat-color);
  transition: width var(--motion-content) var(--motion-fluid);
}

.profile-detail-grid {
  display: grid;
  grid-template-columns: minmax(0, 1.45fr) minmax(300px, 0.85fr);
  gap: 14px;
  align-items: start;
}

.current-activity-panel {
  --project-color: var(--discord-blurple);
}

.current-activity-grid {
  display: grid;
}

.current-activity-card {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  align-content: start;
  min-width: 0;
  border: 1.5px solid var(--layer-line);
  border-radius: var(--radius-control);
  background: var(--layer-bg);
  box-shadow: var(--layer-shadow);
}

.current-activity-card > span:nth-child(2) {
  min-width: 0;
}

.current-activity-card small,
.activity-line small {
  display: block;
  color: var(--muted);
  font-size: 11px;
  font-weight: 750;
}

.current-activity-card strong {
  display: block;
  overflow: hidden;
  margin-top: 3px;
  font-size: 18px;
  line-height: 1.15;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.current-activity-card > p,
.current-activity-list {
  grid-column: 1 / -1;
  width: 100%;
}

/* Всё, что лежит в карточке во всю ширину, тянется до её краёв: кнопка,
   заметка и списки. Иначе кнопка «Получить задание» стояла по центру, а
   плашки соседей не доходили до рамки. */
.current-activity-card > .activity-action,
.current-activity-card > p,
.current-activity-card > .current-activity-list {
  justify-self: stretch;
  width: 100%;
  margin-right: 0;
}

.current-activity-card > p {
  color: var(--muted);
  font-size: 13px;
  font-weight: 700;
}

.current-activity-icon {
  line-height: 1;
  display: grid;
  place-items: center;
  width: 40px;
  height: 40px;
  border-radius: var(--radius-compact);
  background: color-mix(in srgb, var(--discord-blurple) 15%, var(--layer-bg));
  font-size: 26px;
}

.current-activity-list {
  display: grid;
  gap: 7px;
}

.activity-line {
  display: grid;
  align-items: center;
  min-width: 0;
  border-radius: var(--radius-compact);
  background: var(--layer-bg-strong);
  color: inherit;
  text-decoration: none;
}

.activity-line b {
  display: block;
  overflow: hidden;
  margin-top: 2px;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.activity-line i,
.activity-line em {
  color: var(--muted);
  font-size: 11px;
  font-style: normal;
  font-weight: 800;
  white-space: nowrap;
}

.activity-line[href]:hover {
  background: color-mix(in srgb, var(--discord-blurple) 13%, var(--layer-bg-strong));
}

.operations-panel,
.inventory-panel {
  min-width: 0;
}

.operation-summary {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 10px;
}

.operation-summary > span {
  display: grid;
  gap: 5px;
  padding: 11px 13px;
  border: 1.5px solid var(--layer-line);
  border-radius: var(--radius-compact);
  background: var(--layer-bg);
}

.operation-summary small {
  color: var(--muted);
  font-size: 12px;
  font-weight: 750;
}

.amount-positive {
  color: var(--green);
}

.amount-negative {
  color: var(--accent);
}

.operation-list,
.inventory-list {
  display: flex;
  flex-direction: column;
  gap: 7px;
  margin-top: 12px;
}

.operation-row,
.inventory-row {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: center;
  gap: 11px;
  min-height: 54px;
  padding: 9px 10px;
  border: 1px solid var(--layer-line);
  border-radius: var(--radius-compact);
  background: var(--layer-bg);
}

.operation-row > span:nth-child(2),
.inventory-row > span:nth-child(2) {
  /* Явная сетка, а не голый inline-span: внутри лежат два блочных <small>
     (описание и строка с редкостью), и без этого их высота не попадает в
     высоту строки — подпись вылезает за рамку карточки. */
  display: grid;
  gap: 2px;
  align-content: center;
  min-width: 0;
}

.operation-row strong,
.inventory-row strong {
  display: block;
  overflow: hidden;
  line-height: 1.2;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.operation-row small,
.inventory-row small {
  display: block;
  overflow: hidden;
  margin-top: 3px;
  color: var(--muted);
  font-size: 11px;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.operation-sign,
.inventory-emoji {
  display: grid;
  place-items: center;
  width: 34px;
  height: 34px;
  border-radius: 11px;
  background: var(--layer-bg-strong);
  font-weight: 900;
}

/* Стрелка одна на оба знака: раньше это были глифы ↗ и ↘, и у них разный
   рост в шрифте — расход выглядел мельче прихода. */
.operation-sign svg {
  display: block;
}

.operation-sign.negative svg {
  rotate: 90deg;
}

.operation-sign.positive {
  color: var(--green);
}

.operation-sign.negative {
  color: var(--accent);
}

.inventory-emoji {
  overflow: hidden;
  font-size: 20px;
  line-height: 1;
  font-family: "Twemoji", "Apple Color Emoji", "Segoe UI Emoji", sans-serif;
}

.inventory-emoji img,
.inventory-emoji .unicode-emoji {
  display: block;
  width: 24px;
  height: 24px;
  transform: none;
}

.inventory-emoji img {
  object-fit: contain;
}

.inventory-emoji .unicode-emoji {
  width: auto;
  height: auto;
  margin: 0;
  line-height: 1;
  text-align: center;
}

.inventory-toggle {
  width: 100%;
  margin-top: 10px;
}

.inventory-more,
.empty-note {
  margin-top: 12px;
  color: var(--muted);
  font-size: 13px;
  font-weight: 700;
  line-height: 1.45;
}

/* Пустое или загружаемое состояние занимает предсказуемую строку панели и
   центрируется по высоте. Так текст не прилипает к верхнему левому углу, а
   панель не меняет размер рывком после ответа сервера. */
.panel > .empty-note:only-child,
.panel > .panel-header + .empty-note:last-child {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 72px;
  margin: 0;
  padding: var(--space-2) 0;
  font-size: 14px;
  text-align: center;
}

@media (max-width: 980px) {
  .profile-stat-grid {
  grid-template-columns: repeat(2, minmax(0, 1fr));
}

  .profile-detail-grid {
  grid-template-columns: 1fr;
}
}

.account-avatar {
  display: grid;
  flex: 0 0 48px;
  place-items: center;
  overflow: hidden;
  width: 48px;
  height: 48px;
  border-radius: var(--icon-radius);
  background: color-mix(in srgb, var(--discord-blurple) 18%, transparent);
  color: white;
  font-weight: 900;
}

.account-avatar img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.account-avatar .avatar-letter {
  display: grid;
  place-items: center;
  width: 100%;
  height: 100%;
  border-radius: inherit;
}

.account-card strong,
.account-card small {
  display: block;
  line-height: 1.2;
}

/* Отступ нужен только под именем: там вторая строка идёт под первой. В
   клетках с деньгами подпись стоит рядом с суммой, и этот же отступ ронял её
   на четыре пикселя ниже — строка переставала читаться как одна. */
.account-identity small {
  margin-top: 4px;
}

.account-card small {
  color: var(--muted);
}

@media (max-width: 760px) {
  .account-card {
  display: flex;
  align-items: flex-start;
  flex-wrap: wrap;
}

  .account-economy {
  width: 100%;
  margin-left: 0;
}

  /* На узком экране шесть клеток в ряд не встают: раскладываем по две, а
     подпись и сумму разводим по краям — так строка читается слева направо. */
  .account-economy {
  grid-template-columns: repeat(2, minmax(0, 1fr));
}

  .account-economy > span {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  text-align: left;
}

  .account-economy-status {
  width: 100%;
  margin-left: 0;
}

  .profile-stat-grid {
  grid-template-columns: 1fr;
}

  .current-activity-grid {
  grid-template-columns: 1fr;
}

  .profile-stat {
  min-height: 118px;
}

}


/* Unified dashboard rhythm: independent stacks prevent empty grid rows. */
.dashboard-columns {
  display: grid;
  gap: var(--space-4);
}


.current-activity-grid {
  gap: var(--space-3);
}


.character-choice-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
}


/* Choice lists float over the page and scroll inside their own fixed viewport. */


.economy-hub {
  display: grid;
  gap: var(--space-3);
}

.economy-tabs {
  gap: var(--space-2);
}

.economy-pane {
  min-width: 0;
}

.economy-balance-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
  gap: var(--space-3);
  margin-bottom: var(--space-3);
}

.economy-balance-grid > span,
.economy-action-card,
.economy-list-row {
  border: 1.5px solid var(--layer-line);
  border-radius: var(--radius-control);
  background: var(--layer-bg);
}

.economy-balance-grid > span {
  display: grid;
  gap: var(--space-1);
  padding: var(--space-3);
}

.economy-balance-grid small,
.economy-action-card small,
.economy-row-copy small,
.economy-note,
.pixel-tools small {
  color: var(--muted);
}


.economy-actions-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--space-3);
}

.economy-action-card {
  display: grid;
  gap: var(--space-2);
  padding: var(--space-3);
}

.economy-action-card > span {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: var(--space-2);
}

.economy-action-card input,
.stock-actions input,
.pyramid-actions input {
  min-width: 0;
  min-height: var(--control-height);
  padding: 0 12px;
  border: 1.5px solid var(--layer-line);
  border-radius: var(--radius-compact);
  background: var(--layer-bg-strong);
  color: var(--text);
  font: inherit;
}

.economy-note {
  margin: var(--space-3) 0 0;
}

.economy-scroll {
  display: grid;
  gap: var(--space-2);
  max-height: min(520px, 58vh);
  overflow-y: auto;
  padding: 0;
  overscroll-behavior: contain;
  scrollbar-gutter: stable;
  scrollbar-width: thin;
}

.economy-list-row {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-3);
}

.economy-row-icon {
  display: grid;
  place-items: center;
  line-height: 1;
  font-size: 26px;
  width: 42px;
  height: 42px;
  border-radius: var(--radius-compact);
  background: var(--layer-bg-strong);
  font-family: "Twemoji", "Apple Color Emoji", "Segoe UI Emoji", sans-serif;
}

.economy-row-copy {
  display: grid;
  gap: 3px;
  align-content: center;
  min-width: 0;
}

.economy-row-copy strong,
.economy-row-copy small {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.stock-actions {
  display: grid;
  grid-template-columns: 72px auto auto;
  gap: var(--space-2);
}

/* Контора: четыре числа слева, взнос и выход справа. Строк текста здесь
   больше, чем у соседей, поэтому колонки выравниваем по верху. */
.pyramid-row {
  align-items: start;
}

.pyramid-row .economy-row-copy {
  gap: 4px;
}

.pyramid-inside {
  color: var(--text);
  font-weight: 800;
}

.pyramid-actions {
  display: grid;
  gap: var(--space-2);
  justify-items: end;
}

.pyramid-actions form {
  display: grid;
  grid-template-columns: 96px auto;
  gap: var(--space-2);
}

.pyramid-actions .pyramid-leave {
  grid-template-columns: auto;
  justify-content: end;
}

@media (max-width: 720px) {
  /* На узком экране кнопки под текстом, а не сбоку: иначе поле суммы
     сжимается до пары символов. */
  .pyramid-row {
    grid-template-columns: auto minmax(0, 1fr);
  }

  .pyramid-actions {
    grid-column: 1 / -1;
    justify-items: stretch;
  }

  .pyramid-actions form {
    grid-template-columns: minmax(0, 1fr) auto;
  }
}


.slot-preview.is-spinning {
  animation: slot-spin var(--motion-quick) var(--motion-standard) infinite alternate;
}


/* Карта — картинка из колоды. Пропорции берём у самого файла (2:3), рамку и
   фон рисовать не надо: они уже нарисованы. */
.playing-card {
  display: block;
  width: clamp(122px, 26vw, 168px);
  aspect-ratio: 2 / 3;
  border-radius: 10px;
  object-fit: contain;
  box-shadow: 0 6px 12px rgba(0, 0, 0, 0.2);
}

/* Стрелка между картами: она читается наравне с самими картами, а не как
   мелкий значок между ними. */
.card-result > i {
  color: var(--muted);
  font-size: clamp(26px, 5vw, 38px);
  font-style: normal;
  line-height: 1;
}

.card-result,
.card-choice-buttons {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-4);
}

.pixel-panel {
  margin-top: var(--space-4);
}

/* Полотно во всю ширину, инструменты полосой под ним: в колонке на сто
   девяносто пикселей палитра из семнадцати плашек не помещалась никак. */
.pixel-layout {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  align-items: start;
  gap: var(--space-3);
}

.pixel-canvas-wrap {
  overflow: hidden;
  border: 1.5px solid var(--layer-line);
  border-radius: var(--radius-control);
  background: #fff;
  box-shadow: var(--layer-shadow);
}

.pixel-canvas-wrap canvas {
  display: block;
  width: 100%;
  height: 100%;
  cursor: crosshair;
  /* Порядок важен: crisp-edges стоял последним и перебивал pixelated, а в
     части браузеров он равнозначен обычному сглаживанию — отсюда мыло на
     телефоне, где холст растянут вшестеро. */
  image-rendering: crisp-edges;
  image-rendering: pixelated;
}

.pixel-tools {
  display: grid;
  gap: var(--space-3);
}

.pixel-palette {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: var(--space-2);
}

.pixel-palette button {
  min-height: 36px;
  border: 2px solid var(--layer-line);
  border-radius: var(--radius-compact);
  background: var(--pixel-color);
  cursor: pointer;
}

.pixel-palette button.active {
  border-color: var(--text);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--discord-blurple) 32%, transparent);
}

.pixel-instant {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  cursor: pointer;
}

@media (max-width: 760px) {
  .dashboard-columns,
  .pixel-layout {
  grid-template-columns: 1fr;
}

  .current-activity-grid {
  grid-template-columns: repeat(2, minmax(0, 1fr));
}

  .pixel-tools {
  grid-template-columns: minmax(0, 1fr) auto;
}
}

@media (max-width: 760px) {
  .current-activity-grid,
  .economy-balance-grid,
  .economy-actions-grid {
  grid-template-columns: 1fr;
}

  .economy-list-row {
  grid-template-columns: auto minmax(0, 1fr);
}

  .economy-list-row > button,
  .stock-actions {
  grid-column: 1 / -1;
}

  .stock-actions {
  grid-template-columns: minmax(0, 1fr) auto auto;
}

  .pixel-tools {
  grid-template-columns: 1fr;
}

  .card-result,
  .card-choice-buttons {
  gap: var(--space-2);
}
}

.command-row {
  display: grid;
  grid-template-columns: 42px minmax(0, 1fr) auto;
  align-items: center;
  gap: 12px;
  padding: 10px;
  border-color: var(--layer-line);
  background: var(--layer-bg);
  box-shadow: var(--layer-shadow);
  text-align: left;
  line-height: 1.25;
  cursor: pointer;
  transition:
    transform var(--motion-control) var(--motion-fluid),
    border-color var(--motion-control) var(--motion-standard),
    background var(--motion-control) var(--motion-standard),
    scale var(--motion-control) var(--motion-fluid);
}

.command-row strong,
.command-row small {
  line-height: 1.22;
}

.command-row:hover {
  transform: translateX(3px);
  border-color: color-mix(in srgb, var(--accent) 40%, var(--line));
  background: color-mix(in srgb, var(--accent) 7%, var(--surface));
}

.command-row > small:last-child {
  justify-self: end;
  font-family: ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace;
}

.timeline {
  display: grid;
  gap: 12px;
}

.timeline-item {
  display: grid;
  grid-template-columns: 10px minmax(0, 1fr);
  align-items: start;
  gap: 12px;
  min-height: 58px;
  padding: 12px;
  border: 1.5px solid var(--layer-line);
  border-radius: var(--radius-control);
  background: var(--layer-bg);
  box-shadow: var(--layer-shadow);
}

.timeline-item::before {
  width: 10px;
  height: 10px;
  margin-top: 7px;
  border-radius: 50%;
  background: var(--accent);
  content: "";
}

@keyframes tab-exit-down {
  to {
    opacity: 0;
    transform: translate3d(0, 7px, 0) scale(0.99);
  }
}

@keyframes tab-enter-up {
  from {
    opacity: 0;
    transform: translate3d(0, 9px, 0) scale(0.99);
  }
}

@media (max-width: 1200px) {
  :root {
  --top-icon-size: 48px;
  --top-nav-gap: 8px;
  --top-actions-gap: 8px;
}

  .topbar,
  .workspace {
  width: min(100% - 20px, var(--content-width));
}

  .topbar {
  position: sticky;
  justify-content: space-between;
  align-items: center;
  gap: 8px;
}

  .brand {
  flex: 0 0 var(--top-edge-icon-size);
  min-width: 0;
  padding: 0;
}

  .brand > span:not(.brand-mark),
  .nav-item > span:not(.nav-icon),
  .nav-item strong,
  .nav-item small {
  display: none;
}

  .brand-mark,
  .nav-list > .nav-item,
  .nav-list > .nav-item > .nav-icon,
  .theme-button,
  .discord-login-button {
  aspect-ratio: 1 / 1 !important;
  box-sizing: border-box;
}

  .nav-list {
  position: absolute;
  left: 50%;
  flex: 0 0 auto;
  justify-content: center;
  gap: var(--top-nav-gap);
  max-width: none;
  overflow: visible;
  padding-bottom: 0;
  transform: translateX(-50%);
}

  .top-actions {
  gap: var(--top-actions-gap);
}

  .nav-item {
  display: flex;
  flex: 0 0 var(--top-icon-size);
  place-items: center;
  align-items: center;
  justify-content: center;
  width: var(--top-icon-size);
  min-width: var(--top-icon-size);
  max-width: var(--top-icon-size);
  height: var(--top-icon-size);
  min-height: var(--top-icon-size);
  max-height: var(--top-icon-size);
  padding: 0;
  border-color: transparent;
  border-radius: var(--radius-control);
  background: transparent;
  box-shadow: none;
}

  .nav-list > .nav-item:hover,
  .nav-list > .nav-item.active {
  border-color: transparent;
  background: transparent;
  box-shadow: none;
}

  .nav-list > .nav-item > .nav-icon,
  .nav-list > .nav-item > .nav-icon > img {
  width: 100% !important;
  min-width: 100% !important;
  max-width: 100% !important;
  height: 100% !important;
  min-height: 100% !important;
  max-height: 100% !important;
  inline-size: 100% !important;
  min-inline-size: 100% !important;
  max-inline-size: 100% !important;
  block-size: 100% !important;
  min-block-size: 100% !important;
  max-block-size: 100% !important;
  aspect-ratio: 1 / 1 !important;
}

  .nav-list > .nav-item > .nav-icon {
  border-radius: inherit;
  border: 0;
  background: transparent;
  box-shadow: none;
  overflow: hidden;
}

  .nav-list > .nav-item > .nav-icon > img {
  display: block;
  object-fit: cover !important;
}

  .hero,
  .grid-2,
  .grid-3,
  .project-grid,
  .game-grid {
    grid-template-columns: 1fr;
  }

  /* Четыре цифры о сервере на планшете — попарно: в один столбик они
     отодвинули бы панели на экран вниз. */
  .grid-4 {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

@media (max-width: 620px) {
  :root {
  --top-icon-size: 48px;
  --top-edge-icon-size: clamp(52px, 13.4vw, 56px);
  --top-theme-size: 42px;
  --top-nav-gap: 8px;
  --top-actions-gap: 8px;
  --top-actions-width: calc(var(--top-theme-size) + var(--top-edge-icon-size) + var(--top-actions-gap));
  --radius: 18px;
  --radius-control: 16px;
  --radius-compact: 10px;
}

  .topbar,
  .workspace {
  width: min(100% - 8px, var(--content-width));
}

  .topbar {
  top: 10px;
  display: grid;
  grid-template-columns: var(--top-edge-icon-size) auto minmax(var(--top-actions-width), 1fr);
  align-items: center;
  justify-content: initial;
  gap: 8px;
  min-height: 74px;
  margin-top: 10px;
  padding: 7px;
}

  .brand {
  flex: 0 0 var(--top-edge-icon-size);
  min-width: 0;
  padding: 0;
}

  .brand > span:not(.brand-mark),
  .nav-item > span:not(.nav-icon),
  .nav-item strong,
  .nav-item small {
  display: none;
}

  .brand-mark {
  width: var(--top-edge-icon-size);
  min-width: var(--top-edge-icon-size);
  max-width: var(--top-edge-icon-size);
  height: var(--top-edge-icon-size);
  min-height: var(--top-edge-icon-size);
  max-height: var(--top-edge-icon-size);
  aspect-ratio: 1;
  border-radius: var(--radius-control);
  font-size: 15px;
}

  .nav-list {
  position: static;
  flex: 0 0 auto;
  justify-self: start;
  justify-content: flex-start;
  gap: var(--top-nav-gap);
  max-width: none;
  overflow: visible;
  padding-bottom: 0;
  transform: none;
}

  .nav-item {
  display: flex;
  flex: 0 0 var(--top-icon-size);
  place-items: center;
  align-items: center;
  justify-content: center;
  width: var(--top-icon-size);
  min-width: var(--top-icon-size);
  max-width: var(--top-icon-size);
  height: var(--top-icon-size);
  min-height: var(--top-icon-size);
  max-height: var(--top-icon-size);
  aspect-ratio: 1;
  padding: 0;
  border-color: transparent;
  border-radius: var(--radius-control);
  background: transparent;
  box-shadow: none;
}

  .nav-list > .nav-item:hover,
  .nav-list > .nav-item.active {
  border-color: transparent;
  background: transparent;
  box-shadow: none;
}

  .nav-icon {
  width: 100%;
  min-width: 100%;
  max-width: 100%;
  height: 100%;
  min-height: 100%;
  max-height: 100%;
  aspect-ratio: 1;
  border-radius: inherit;
  border: 0;
  background: transparent;
  box-shadow: none;
  font-size: 16px;
  overflow: hidden;
}

  .nav-icon img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

  .theme-button {
  width: var(--top-theme-size);
  min-width: var(--top-theme-size);
  max-width: var(--top-theme-size);
  height: var(--top-theme-size);
  min-height: var(--top-theme-size);
  max-height: var(--top-theme-size);
  aspect-ratio: 1;
  border-radius: var(--radius-control);
}

  .discord-login-button {
  width: var(--top-edge-icon-size);
  min-width: var(--top-edge-icon-size);
  max-width: var(--top-edge-icon-size);
  height: var(--top-edge-icon-size);
  min-height: var(--top-edge-icon-size);
  max-height: var(--top-edge-icon-size);
  aspect-ratio: 1;
  border-radius: var(--radius-control);
}

  .top-actions {
  justify-self: end;
}

  .brand-mark,
  .discord-login-button {
  width: var(--top-edge-icon-size) !important;
  min-width: var(--top-edge-icon-size) !important;
  max-width: var(--top-edge-icon-size) !important;
  height: var(--top-edge-icon-size) !important;
  min-height: var(--top-edge-icon-size) !important;
  max-height: var(--top-edge-icon-size) !important;
  inline-size: var(--top-edge-icon-size) !important;
  min-inline-size: var(--top-edge-icon-size) !important;
  max-inline-size: var(--top-edge-icon-size) !important;
  block-size: var(--top-edge-icon-size) !important;
  min-block-size: var(--top-edge-icon-size) !important;
  max-block-size: var(--top-edge-icon-size) !important;
  flex: 0 0 var(--top-edge-icon-size) !important;
}

  .nav-list > .nav-item,
  .nav-list > .nav-item > .nav-icon {
  width: var(--top-icon-size) !important;
  min-width: var(--top-icon-size) !important;
  max-width: var(--top-icon-size) !important;
  height: var(--top-icon-size) !important;
  min-height: var(--top-icon-size) !important;
  max-height: var(--top-icon-size) !important;
  inline-size: var(--top-icon-size) !important;
  min-inline-size: var(--top-icon-size) !important;
  max-inline-size: var(--top-icon-size) !important;
  block-size: var(--top-icon-size) !important;
  min-block-size: var(--top-icon-size) !important;
  max-block-size: var(--top-icon-size) !important;
  flex: 0 0 var(--top-icon-size) !important;
  aspect-ratio: 1 / 1 !important;
  box-sizing: border-box;
}

  .theme-button {
  width: var(--top-theme-size) !important;
  min-width: var(--top-theme-size) !important;
  max-width: var(--top-theme-size) !important;
  height: var(--top-theme-size) !important;
  min-height: var(--top-theme-size) !important;
  max-height: var(--top-theme-size) !important;
  inline-size: var(--top-theme-size) !important;
  min-inline-size: var(--top-theme-size) !important;
  max-inline-size: var(--top-theme-size) !important;
  block-size: var(--top-theme-size) !important;
  min-block-size: var(--top-theme-size) !important;
  max-block-size: var(--top-theme-size) !important;
  flex: 0 0 var(--top-theme-size) !important;
  aspect-ratio: 1 / 1 !important;
  box-sizing: border-box;
}

  .nav-list > .nav-item > .nav-icon,
  .nav-list > .nav-item > .nav-icon > img {
  inline-size: 100% !important;
  min-inline-size: 100% !important;
  max-inline-size: 100% !important;
  block-size: 100% !important;
  min-block-size: 100% !important;
  max-block-size: 100% !important;
  aspect-ratio: 1 / 1 !important;
}

  .nav-list > .nav-item > .nav-icon > img {
  display: block;
  object-fit: cover !important;
}

  .page-stage {
  padding-top: 18px;
}

  .hero-copy,
      .command-row {
  grid-template-columns: 38px minmax(0, 1fr);
}

  .command-row > small {
  grid-column: 2;
}

  .alias-preview {
  grid-template-columns: 1fr;
}

  .game-card,
  .game-card.featured {
  min-height: 180px;
}

  .reward-list .layer-chip {
  display: grid;
  align-items: start;
}

  .reward-list span {
  text-align: left;
}

}

@media (max-width: 760px) {
  .discord-server-grid {
  grid-template-columns: 1fr;
}

  .news-panel {
  height: auto;
  min-height: 0;
}

  .catalog-command {
  grid-template-columns: 1fr;
}

  .command-forms {
  justify-content: flex-start;
}
}

@media (max-width: 760px) {
  .birthday-control {
  grid-template-columns: auto minmax(0, 1fr);
}

  .birthday-control > .compact-button,
  .birthday-form {
  grid-column: 1 / -1;
}

  .birthday-form {
  grid-template-columns: 1fr;
}

  .birthday-form .primary-button {
  width: 100%;
}

  .command-search {
  grid-template-columns: 1fr;
  gap: 6px;
}

  .command-forms {
  display: grid;
  grid-template-columns: 1fr;
}

  .command-forms code {
  overflow: hidden;
  text-overflow: ellipsis;
}

  .news-item.featured:has(.news-image) {
  grid-template-columns: 1fr;
}

  .news-image {
  min-height: 150px;
}

}

.discord-tools-grid {
  display: grid;
  grid-template-columns: minmax(0, 1.2fr) minmax(320px, 0.8fr);
  align-items: start;
  gap: 18px;
}

.discord-community-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 18px;
}

.character-editor,
.family-panel,
.leaderboard-panel,
.people-finder,
.casino-panel {
  min-width: 0;
}

.character-editor-layout {
  display: grid;
  grid-template-columns: minmax(220px, 0.8fr) minmax(280px, 1.2fr);
  gap: 18px;
  align-items: start;
}

/* Ровно тот цвет, который выбран, и ничего сверху. Раньше поверх лежал белый
   блик: сама картинка приходит от бота с плотной заливкой и закрывала его в
   середине, а выше неё блик оставался виден — сверху фон казался светлее.
   В Discord тот же образ заливается сплошным цветом, так что и здесь никакой
   подсветки быть не должно. */
.character-preview {
  display: grid;
  place-items: center;
  overflow: hidden;
  width: 100%;
  min-height: 0;
  aspect-ratio: 1033 / 1523;
  border: 1.5px solid var(--layer-line);
  border-radius: var(--radius-control);
  background: transparent;
}

.character-preview img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.character-controls,
.character-controls fieldset {
  display: grid;
  gap: 12px;
  min-width: 0;
}

.character-controls fieldset {
  margin: 0;
  padding: 0;
  border: 0;
}

.character-controls legend,
.color-control > span,
.people-role-select > span,
.casino-bet > span {
  margin-bottom: 7px;
  color: var(--muted);
  font-size: 12px;
  font-weight: 850;
}

.choice-chips,
.segmented-control {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
}

.choice-chips label {
  cursor: pointer;
}

.choice-chips input {
  position: absolute;
  opacity: 0;
  pointer-events: none;
}

.choice-chips span,
.segmented-control button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: var(--control-height);
  padding: 9px 14px;
  border: 1.5px solid var(--layer-line);
  border-radius: var(--radius-compact);
  background: var(--layer-bg);
  color: var(--muted);
  font: inherit;
  font-size: 12px;
  font-weight: 820;
}

.segmented-control button {
  cursor: pointer;
}

.choice-chips input:checked + span,
.segmented-control button.active {
  border-color: color-mix(in srgb, var(--discord-blurple) 48%, var(--layer-line));
  background: color-mix(in srgb, var(--discord-blurple) 18%, var(--layer-bg-strong));
  color: var(--text);
}

/* Галочка и «Настроить» стоят в одну строку: места хватает, пока у чипа нет
   эмодзи, а длинная подпись при нехватке ужимается многоточием. */
.avatar-face-choice {
  align-items: center;
  flex-wrap: nowrap;
}

.avatar-face-choice label {
  min-width: 0;
}

.avatar-face-choice label span {
  display: flex;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}

.avatar-face-choice .compact-button {
  flex: none;
  /* Чип рядом ростом с --control-height, у кнопок собственный минимум 40px. */
  min-height: var(--control-height);
}

/* По левому краю, а не по центру: рядом стоят чипы «Грибочел» и список
   одежды, они прижаты влево, и отцентрованный блок между ними читался как
   съехавший — ни к чему не привязанный. */
.avatar-fit {
  display: grid;
  gap: var(--space-2);
  justify-items: start;
}

/* Сцена повторяет пропорции холста грибочела, поэтому вырез лица можно
   ставить в долях — ровно те же, по которым бот потом обрежет аватарку. */
.avatar-fit-stage {
  position: relative;
  width: min(100%, 240px);
  aspect-ratio: 1033 / 1523;
  border-radius: var(--radius-compact);
  background: var(--character-bg, var(--layer-bg));
  overflow: hidden;
}

.avatar-fit-hole {
  position: absolute;
  left: var(--face-x);
  top: var(--face-y);
  width: var(--face-w);
  height: var(--face-h);
  overflow: hidden;
  cursor: grab;
  touch-action: none;
}

.avatar-fit-hole.is-dragging {
  cursor: grabbing;
}

.avatar-fit-hole img {
  position: absolute;
  left: calc(-100% * (var(--zoom) - 1) * var(--x));
  top: calc(-100% * (var(--zoom) - 1) * var(--y));
  width: calc(100% * var(--zoom));
  height: calc(100% * var(--zoom));
  object-fit: cover;
  object-position: calc(var(--x) * 100%) calc(var(--y) * 100%);
  user-select: none;
}

.avatar-fit-frame {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  user-select: none;
}

.avatar-fit-hint {
  color: var(--muted);
  font-size: 12px;
}

.avatar-fit-actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
}

.color-control {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: center;
  gap: 10px;
}

.color-control > span {
  grid-column: 1 / -1;
  margin: 0;
}

.color-control input[type="color"] {
  width: 52px;
  /* Ростом с кнопки одежды рядом — иначе ряд выглядит кривым. */
  height: var(--control-height);
  padding: 3px;
  border: 1.5px solid var(--layer-line);
  border-radius: var(--radius-compact);
  background: var(--layer-bg);
}

/* Цвет фона живёт под превью: там он не отнимает ширину у списка одежды. */
.character-side {
  display: grid;
  grid-template-rows: auto 1fr;
  gap: var(--space-3);
  min-width: 0;
  /* У сетки редактора align-items: start, поэтому колонку тянем сами —
     иначе цвет не встанет вровень с нижней строкой одежды. */
  align-self: stretch;
}

.character-side .color-control {
  grid-template-columns: auto minmax(0, 1fr);
  align-content: end;
}



/* Список одежды листается вниз: вещей бывает много, а вширь их некуда
   класть — справа от цвета фона колонка и так узкая. */
.clothing-strip {
  display: grid;
  grid-auto-rows: max-content;
  gap: var(--space-2);
  /* Ровно три строки: список остаётся компактным, остальное листается. */
  max-height: calc(var(--control-height) * 3 + var(--space-2) * 2);
  overflow-y: auto;
  padding-right: 4px;
  scrollbar-width: thin;
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
  mask-image: linear-gradient(to bottom,
    transparent 0, #000 var(--fade-top, 0px),
    #000 calc(100% - var(--fade-bottom, 0px)), transparent 100%);
}

.clothing-strip label {
  position: relative;
  min-width: 0;
  cursor: pointer;
}

.clothing-strip input {
  position: absolute;
  opacity: 0;
  pointer-events: none;
}

.clothing-strip label > span {
  display: flex;
  align-items: center;
  gap: 8px;
  min-height: var(--control-height);
  padding: 9px 11px;
  border: 1.5px solid var(--layer-line);
  border-radius: var(--radius-compact);
  background: var(--layer-bg);
  font-size: 12px;
  font-weight: 780;
}

.clothing-strip input:checked + span {
  border-color: color-mix(in srgb, var(--discord-blurple) 48%, var(--layer-line));
  background: color-mix(in srgb, var(--discord-blurple) 17%, var(--layer-bg));
}

.clothing-strip span img,
.clothing-strip .unicode-emoji {
  display: block;
  flex: 0 0 22px;
  width: 22px;
  height: 22px;
  margin: 0;
  object-fit: contain;
  line-height: 1;
  transform: none;
  font-family: "Twemoji", "Apple Color Emoji", "Segoe UI Emoji", sans-serif;
}

.clothing-strip .unicode-emoji {
  width: auto;
  height: auto;
  min-width: 22px;
  text-align: center;
}

.family-roster,
.leaderboard-list,
.people-list {
  display: grid;
  gap: 8px;
}

.operation-list,
.inventory-list,
.family-roster,
.leaderboard-list,
.people-list,
.news-list,
.role-cloud-scroll,
.permission-grid {
  overscroll-behavior: contain;
  scrollbar-width: thin;
}

.operation-list,
.inventory-list {
  padding: 0;
  border-radius: var(--radius-compact);
  scrollbar-gutter: stable;
  mask-image: linear-gradient(to bottom,
    transparent 0, #000 var(--fade-top, 0px),
    #000 calc(100% - var(--fade-bottom, 0px)), transparent 100%);
}

.family-roster,
.leaderboard-list,
.people-list {
  max-height: min(520px, 58vh);
  overflow-y: auto;
  padding: 0;
  border-radius: var(--radius-compact);
  scrollbar-gutter: stable;
  mask-image: linear-gradient(to bottom,
    transparent 0, #000 var(--fade-top, 0px),
    #000 calc(100% - var(--fade-bottom, 0px)), transparent 100%);
}

.family-member,
.leaderboard-row,
.person-card {
  display: grid;
  align-items: center;
  gap: 10px;
  padding: 10px;
  border: 1px solid var(--layer-line);
  border-radius: var(--radius-compact);
  background: var(--layer-bg);
}


.family-member-actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: 12px;
}

.family-preview {
  overflow: hidden;
  margin: 0 0 18px;
  border: 1px solid var(--layer-line);
  border-radius: var(--radius-control);
  background: transparent;
}

.family-preview img {
  display: block;
  width: 100%;
  aspect-ratio: 3099 / 1523;
  object-fit: cover;
}

/* Подпись и кнопка смены холста делят одну строку: уровень слева, переход к
   родителям справа. На узком экране кнопка съезжает под подпись. */
.family-preview figcaption {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
  padding: 10px 12px;
  color: var(--muted);
  font-size: 13px;
}

.family-avatar,
.leader-avatar,
.person-avatar {
  display: grid;
  place-items: center;
  overflow: hidden;
  width: 42px;
  height: 42px;
  border-radius: 13px;
  background: var(--layer-bg-strong);
}

.family-avatar img,
.leader-avatar img,
.person-avatar img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.family-member small,
.family-member strong,
.person-card small,
.person-card strong {
  display: block;
}

.family-member small,
.person-card small,
.person-card p {
  color: var(--muted);
  font-size: 11px;
}

.family-settings {
  display: grid;
  gap: 12px;
  margin-top: 18px;
  padding-top: 18px;
  border-top: 1px solid var(--layer-line);
}

.family-background {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 10px;
  align-items: end;
}

.family-background label,
.family-background-select {
  display: grid;
  gap: 8px;
  color: var(--muted);
  font-size: 12px;
}

.family-background input:not([type="color"]),
.family-background-select select,
.people-role-select select,
.casino-bet input {
  width: 100%;
  min-height: 42px;
  padding: 0 12px;
  border: 1.5px solid var(--layer-line);
  border-radius: var(--radius-compact);
  background: var(--layer-bg);
  color: var(--text);
  font: inherit;
  font-weight: 750;
}

.choice-menu {
  position: relative;
  display: grid;
  gap: 8px;
  min-width: 0;
}

.choice-menu > span {
  color: var(--muted);
  font-size: 12px;
  font-weight: 800;
}

.choice-menu details {
  position: relative;
  min-width: 0;
}


.choice-menu summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  min-height: var(--control-height);
  padding: 9px 12px;
  border: 1.5px solid var(--layer-line);
  border-radius: var(--radius-control);
  outline: none;
  background: var(--layer-bg-strong);
  color: var(--text);
  cursor: pointer;
  list-style: none;
}

.choice-menu summary::-webkit-details-marker {
  display: none;
}

.choice-menu summary:focus-visible,
.choice-menu details[open] summary {
  border-color: color-mix(in srgb, var(--discord-blurple) 52%, var(--layer-line));
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--discord-blurple) 12%, transparent);
}

.choice-menu summary > span {
  display: flex;
  align-items: center;
  gap: 9px;
  min-width: 0;
}

.choice-menu summary strong {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.choice-menu summary i {
  display: grid;
  flex: 0 0 26px;
  place-items: center;
  width: 26px;
  height: 26px;
  color: var(--muted);
  font-style: normal;
  line-height: 1;
}

.choice-menu summary i::before {
  line-height: 1;
}

.choice-menu details[open] summary i::before {
  content: "↑";
}

.family-panel:has(.choice-menu details[open]),
.people-finder:has(.choice-menu details[open]) {
  z-index: 4;
  overflow: visible;
}

.choice-menu-options {
  display: grid;
  gap: 6px;
  padding: 8px;
  border: 1.5px solid var(--layer-line);
  border-radius: var(--radius-control);
  scrollbar-gutter: stable;
}

.choice-menu details:not([open]) > .choice-menu-options {
  display: none;
}

.choice-menu-options button {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: center;
  gap: 10px;
  min-width: 0;
  min-height: var(--control-height);
  padding: 9px 11px;
  border: 1px solid transparent;
  border-radius: var(--radius-compact);
  color: var(--text);
  text-align: left;
  cursor: pointer;
}

.choice-menu-options button:hover,
.choice-menu-options button.active {
  border-color: color-mix(in srgb, var(--discord-blurple) 34%, var(--layer-line));
  background: color-mix(in srgb, var(--discord-blurple) 14%, var(--layer-bg-strong));
}

.choice-menu-options button > span,
.choice-menu summary .unicode-emoji,
.choice-menu-options .unicode-emoji {
  display: grid;
  place-items: center;
  min-width: 24px;
  min-height: 24px;
  font-family: "Twemoji", "Apple Color Emoji", "Segoe UI Emoji", sans-serif;
  line-height: 1;
}

.choice-menu-options button > strong {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.choice-menu-options button > small {
  color: var(--muted);
  white-space: nowrap;
}

select option {
  background: var(--surface-plain);
  color: var(--text);
}

.family-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  margin-top: 14px;
}

.danger-button,
.icon-button {
  min-height: 40px;
  padding: 8px 12px;
  border: 1.5px solid color-mix(in srgb, var(--accent) 40%, var(--layer-line));
  border-radius: var(--radius-compact);
  background: color-mix(in srgb, var(--accent) 11%, var(--layer-bg));
  color: var(--text);
  font: inherit;
  font-weight: 820;
  cursor: pointer;
}

.icon-button {
  min-width: 40px;
  padding: 6px;
  border-color: var(--layer-line);
  background: var(--layer-bg-strong);
}

.leaderboard-tabs,
.people-tabs,
.operation-filters {
  margin-bottom: 12px;
}

.leaderboard-row {
  grid-template-columns: 34px auto minmax(0, 1fr) auto;
}

.leaderboard-row.is-you {
  border-color: color-mix(in srgb, var(--discord-blurple) 46%, var(--layer-line));
  background: color-mix(in srgb, var(--discord-blurple) 10%, var(--layer-bg));
}

.leaderboard-row > strong:first-child {
  color: var(--muted);
  text-align: center;
}

.leaderboard-row > b {
  white-space: nowrap;
}

.people-role-select {
  display: grid;
  gap: 6px;
  margin-bottom: 12px;
}

.people-role-select > span {
  margin: 0;
}

.people-list {
  grid-template-columns: repeat(2, minmax(0, 1fr));
}

.person-card {
  grid-template-columns: auto minmax(0, 1fr);
}

.person-card p {
  margin-top: 4px;
  line-height: 1.45;
}

.activity-action {
  width: 100%;
  margin-top: 2px;
}

.inline-status {
  color: var(--muted);
  font-size: 12px;
  font-weight: 750;
}

.games-page {
  width: min(100%, 1040px);
  margin: 0 auto;
}

/* Поле у панели игр было своё, до тридцати пикселей, и она заметно отличалась
   от соседей. Оставляем общее панельное. */
.games-shell {
  position: relative;
}

/* Баланс и кнопка входа стоят в строке заголовка панели: раньше они висели
   поверх неё absolute, и широкая надпись наезжала на список игр. */
.panel-header-aside {
  display: flex;
  flex: 0 0 auto;
  align-items: center;
}

.games-balance {
  color: var(--muted);
  font-size: 17px;
  white-space: nowrap;
}

.games-balance-login {
  min-height: 34px;
  padding: 0 14px;
  border: 1.5px solid var(--layer-line);
  border-radius: var(--radius-compact);
  background: var(--layer-bg);
  color: var(--muted);
  font-family: inherit;
  font-size: 13px;
  font-weight: 700;
  white-space: nowrap;
  cursor: pointer;
}

.games-balance-login:hover {
  border-color: var(--accent);
  color: var(--text);
}

.casino-tabs {
  display: grid;
  margin-bottom: 18px;
}

.casino-tabs button {
  display: grid;
  place-items: center;
  gap: 5px;
  min-width: 0;
  min-height: 86px;
  padding: 12px 9px;
  border: 1px solid var(--layer-line);
  border-radius: var(--radius-compact);
  background: var(--layer-bg);
  color: var(--muted);
  cursor: pointer;
  transition:
    border-color var(--motion-control) var(--motion-standard),
    background var(--motion-control) var(--motion-standard),
    color var(--motion-control) var(--motion-standard),
    transform var(--motion-control) var(--motion-fluid),
    scale var(--motion-control) var(--motion-fluid);
}

.casino-tabs button:hover {
  transform: translateY(-1px);
  border-color: color-mix(in srgb, var(--amber) 38%, var(--layer-line));
  color: var(--text);
}

.casino-tabs button.active {
  border-color: color-mix(in srgb, var(--amber) 62%, var(--layer-line));
  background: color-mix(in srgb, var(--amber) 13%, var(--layer-bg));
  color: var(--text);
}

.casino-tabs button > span {
  font-size: 28px;
  font-family: "Twemoji", "Apple Color Emoji", "Segoe UI Emoji", sans-serif;
  line-height: 1;
}

.casino-tabs button > strong {
  overflow: hidden;
  max-width: 100%;
  font-size: 14px;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.casino-board {
  display: grid;
  gap: 16px;
}

.casino-stage {
  display: grid;
  gap: var(--space-2);
  min-height: 0;
  padding: 0;
  text-align: center;
}

.casino-stage > strong {
  font-size: clamp(34px, 6vw, 68px);
  letter-spacing: 0.06em;
}

.casino-stage > span {
  margin-top: 12px;
  font-weight: 850;
}

.casino-stage > small {
  margin-top: 5px;
  color: var(--muted);
}

.coin-preview {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: clamp(12px, 4vw, 30px);
}

.coin-preview i {
  color: var(--muted);
  font-size: 17px;
  font-style: normal;
}

.coin-side {
  display: grid;
  gap: 9px;
  place-items: center;
  width: clamp(110px, 18vw, 154px);
  color: var(--text);
}

.coin-side img {
  display: block;
  width: 100%;
  aspect-ratio: 1;
  border-radius: 50%;
  object-fit: contain;
  filter: drop-shadow(0 4px 7px rgba(0, 0, 0, 0.2));
}

.coin-side.result {
  width: clamp(142px, 23vw, 188px);
}

.coin-side strong {
  font-size: 16px;
  line-height: 1;
}

/* Значок внутри фишки выбора: без отступа он вплотную упирался в подпись. */
.choice-coin,
.choice-dice {
  display: block;
  width: 32px;
  height: 32px;
  margin-right: 9px;
  object-fit: contain;
}

.choice-coin {
  border-radius: 50%;
}

.race-preview {
  font-family: "Twemoji", "Apple Color Emoji", "Segoe UI Emoji", sans-serif;
  letter-spacing: 0.04em;
}

/* Кости: до броска показываем все шесть граней, после — выпавшую крупно. */
.dice-preview {
  display: flex;
  flex-wrap: wrap;
  gap: clamp(8px, 2vw, 14px);
  align-items: center;
  justify-content: center;
}

.dice-preview img {
  display: block;
  width: clamp(48px, 9vw, 70px);
  aspect-ratio: 1;
  filter: drop-shadow(0 3px 5px rgba(0, 0, 0, 0.18));
}

.dice-face {
  display: block;
  width: clamp(120px, 25vw, 176px);
  aspect-ratio: 1;
  filter: drop-shadow(0 5px 8px rgba(0, 0, 0, 0.2));
}

.cockfight-preview {
  position: relative;
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  gap: clamp(16px, 5vw, 54px);
  width: min(100%, 480px);
  min-height: 150px;
  padding: 22px clamp(18px, 5vw, 42px);
  border: 1px solid color-mix(in srgb, var(--amber) 30%, var(--layer-line));
  border-radius: var(--radius-control);
}

.cockfight-preview > span {
  font-family: "Twemoji", "Apple Color Emoji", "Segoe UI Emoji", sans-serif;
  font-size: clamp(54px, 9vw, 84px);
  line-height: 1;
  filter: drop-shadow(0 10px 10px rgba(0, 0, 0, 0.2));
}

.cockfight-preview > span:first-child {
  transform: scaleX(-1);
}

.cockfight-preview > i {
  align-self: center;
  color: var(--accent);
  font-size: 18px;
  font-style: normal;
  font-weight: 900;
}

.cockfight-result {
  display: grid;
  gap: 10px;
  place-items: center;
}

.cockfight-result > span {
  font-family: "Twemoji", "Apple Color Emoji", "Segoe UI Emoji", sans-serif;
  font-size: clamp(76px, 13vw, 116px);
  line-height: 1;
}

.cockfight-result.won > span {
  filter: drop-shadow(0 12px 18px color-mix(in srgb, var(--green) 30%, transparent));
}

.cockfight-result.lost > span {
  filter: grayscale(0.2) drop-shadow(0 12px 18px color-mix(in srgb, var(--accent) 26%, transparent));
}

.cockfight-result > strong {
  font-size: 18px;
}

.slot-preview {
  color: var(--amber);
}

/* Колесо, указатель и шарик лежат друг на друге: крутится только колесо,
   указатель обязан стоять, иначе по нему нечего читать. Само колесо —
   готовый файл из tools/roulette.py, поэтому рисовать тут нечего. */
/* Стол рулетки: слева колесо, справа лента чисел. Разметка одна и та же до
   броска и после — колесо никуда не переезжает. */
.roulette-stage {
  display: flex;
  flex-wrap: wrap;
  gap: clamp(16px, 4vw, 34px);
  align-items: center;
  justify-content: center;
  /* Родитель — grid с центровкой, и без этого стол сжимался по содержимому,
     а лента заворачивалась под колесо. */
  width: 100%;
}

.roulette-table {
  position: relative;
  display: block;
  flex: 0 0 auto;
  width: clamp(170px, 30vw, 236px);
  aspect-ratio: 1;
}

.roulette-wheel-art {
  display: block;
  width: 100%;
  height: 100%;
  border-radius: 50%;
  /* Тень едва заметная: колесо лежит на столе, а не висит над ним. */
  filter: drop-shadow(0 4px 8px rgba(0, 0, 0, 0.22));
  will-change: transform;
}

.roulette-pointer {
  position: absolute;
  top: -3%;
  left: 50%;
  width: 9%;
  transform: translateX(-50%);
}

.roulette-ball {
  position: absolute;
  top: 7.5%;
  left: 50%;
  width: 5.4%;
  transform: translateX(-50%);
}

/* Лента: сверху вниз — числа так, как они идут по колесу. Пока колесо
   крутится, строки меняются вместе с ним. */
.roulette-strip {
  display: grid;
  gap: 6px;
  flex: 0 1 132px;
  min-width: 108px;
}

.roulette-strip-row {
  display: grid;
  place-items: center;
  min-height: 38px;
  padding: 0 12px;
  border: 1.5px solid transparent;
  border-radius: var(--radius-compact);
  color: var(--muted);
  font-size: 17px;
  font-variant-numeric: tabular-nums;
  opacity: 0.5;
}

.roulette-strip-row.red { background: color-mix(in srgb, #ac1d18 55%, transparent); }
.roulette-strip-row.black { background: color-mix(in srgb, #1d1d1b 65%, transparent); }
.roulette-strip-row.green { background: color-mix(in srgb, #15703f 55%, transparent); }

/* Под указателем — то, что сейчас выпадает: крупнее и в полную силу. */
.roulette-strip-row.is-current {
  min-height: 52px;
  border-color: var(--layer-line);
  color: #fff;
  font-size: 28px;
  font-weight: 800;
  opacity: 1;
}

.casino-bet {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 10px;
}

.casino-bet > span {
  grid-column: 1 / -1;
  margin: 0;
}

.casino-choice {
  justify-content: center;
}

.roulette-choice {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
}

.roulette-choice label,
.roulette-choice span {
  width: 100%;
}

.race-choice {
  display: grid;
  grid-template-columns: repeat(5, minmax(0, 1fr));
}

.race-choice label,
.race-choice span {
  width: 100%;
}

.casino-play {
  width: 100%;
}

@media (max-width: 760px) {
  .discord-tools-grid,
  .discord-community-grid,
  .character-editor-layout {
  grid-template-columns: 1fr;
}

  .character-preview {
  min-height: 0;
}

  .casino-tabs {
  grid-template-columns: repeat(3, minmax(0, 1fr));
}
}

@media (max-width: 760px) {
  .people-list {
  grid-template-columns: 1fr;
}

  .family-background {
  grid-template-columns: 1fr;
}

  .family-member {
  grid-template-columns: auto minmax(0, 1fr);
}

  .family-member-actions {
  grid-column: 1 / -1;
  justify-content: stretch;
}

  .family-member-actions button {
  flex: 1 1 170px;
}

  .leaderboard-row {
  grid-template-columns: 28px auto minmax(0, 1fr);
}

  .leaderboard-row > b {
  grid-column: 3;
}

  .games-balance {
  font-size: 15px;
}

  

  .roulette-choice,
  .race-choice {
  grid-template-columns: repeat(2, minmax(0, 1fr));
}

  .casino-stage {
  min-height: 0;
  padding: 0;
}

  .coin-preview {
  gap: 10px;
}
}

/* Final component overrides must stay after the legacy component block. */
.choice-menu-options {
  position: absolute;
  top: calc(100% + var(--space-1));
  right: 0;
  left: 0;
  max-height: min(300px, 48vh);
  margin-top: 0;
  overflow-y: auto;
}

.choice-chips.character-choice-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-2);
}

.character-choice-grid label,
.character-choice-grid span {
  width: 100%;
  min-width: 0;
}

.casino-tabs {
  grid-template-columns: repeat(auto-fit, minmax(92px, 1fr));
  gap: var(--space-2);
}

.cockfight-preview {
  align-items: center;
  background: var(--layer-bg);
}

@media (max-width: 760px) {
  .casino-tabs {
  grid-template-columns: repeat(2, minmax(0, 1fr));
}
}

/* Shared interaction layout: keep menus stable, opaque and independently scrollable. */
.search-button {
  flex: 0 0 var(--top-theme-size);
  width: var(--top-theme-size);
  min-width: var(--top-theme-size);
  max-width: var(--top-theme-size);
  height: var(--top-theme-size);
  min-height: var(--top-theme-size);
  max-height: var(--top-theme-size);
  padding: 0;
  border: 0;
  border-radius: var(--radius-control);
  background: transparent;
  color: var(--text);
}

body.search-open {
  overflow: hidden;
}

.site-search-backdrop {
  position: fixed;
  z-index: 500;
  inset: 0;
  display: grid;
  place-items: start center;
  padding: min(15vh, 140px) var(--space-4) var(--space-4);
  background: rgba(5, 5, 8, 0.68);
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--motion-control) var(--motion-standard);
}

.site-search-backdrop[hidden] {
  display: none;
}

.site-search-dialog {
  width: min(620px, 100%);
  max-height: min(680px, 76vh);
  padding: var(--space-4);
  border: 1.5px solid var(--layer-line);
  border-radius: var(--radius);
  background: var(--surface-plain);
  box-shadow: 0 28px 80px rgba(0, 0, 0, 0.42);
  opacity: 0;
  transform: translate3d(0, -10px, 0) scale(0.97);
  transform-origin: top center;
  transition:
    opacity var(--motion-control) var(--motion-standard),
    transform var(--motion-content) var(--motion-fluid);
}

.site-search-backdrop.is-open {
  opacity: 1;
  pointer-events: auto;
}

.site-search-backdrop.is-open .site-search-dialog {
  opacity: 1;
  transform: translate3d(0, 0, 0) scale(1);
}

.site-search-heading,
.site-search-input,
.site-search-results button {
  display: flex;
  align-items: center;
}

.site-search-heading {
  justify-content: space-between;
  gap: var(--space-3);
  margin-bottom: var(--space-3);
}

.site-search-heading strong,
.site-search-heading small {
  display: block;
}

.site-search-heading strong {
  font-family: var(--font-display);
  font-size: 24px;
}

.site-search-heading small,
.site-search-results small {
  color: var(--muted);
}

.site-search-input {
  gap: var(--space-2);
  min-height: 52px;
  padding: 0 var(--space-3);
  border: 1.5px solid var(--layer-line);
  border-radius: var(--radius-control);
  background: var(--layer-bg-strong);
}

.site-search-input:focus-within {
  border-color: color-mix(in srgb, var(--discord-blurple) 56%, var(--layer-line));
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--discord-blurple) 12%, transparent);
}

.site-search-input input {
  width: 100%;
  min-width: 0;
  border: 0;
  outline: 0;
  background: transparent;
  color: var(--text);
  font: inherit;
}

.site-search-results {
  display: grid;
  gap: var(--space-2);
  max-height: min(500px, 58vh);
  margin-top: var(--space-3);
  overflow-y: auto;
  scrollbar-gutter: stable;
}

.site-search-results button {
  gap: var(--space-2);
  width: 100%;
  min-height: 50px;
  padding: var(--space-2) var(--space-3);
  border: 1px solid var(--layer-line);
  border-radius: var(--radius-compact);
  background: var(--layer-bg);
  color: var(--text);
  text-align: left;
  cursor: pointer;
}

.site-search-results button:hover {
  border-color: color-mix(in srgb, var(--discord-blurple) 42%, var(--layer-line));
  background: color-mix(in srgb, var(--discord-blurple) 11%, var(--layer-bg));
}

.site-search-results button span {
  flex: 1;
  min-width: 0;
  font-weight: 800;
}

.site-search-results .search-arrow {
  flex: none;
  display: block;
  color: var(--muted);
}

.birthday-form {
  grid-template-columns: minmax(0, 1fr);
}

.birthday-form-actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: var(--space-2);
}

.birthday-status.is-error,
.inline-status.is-error {
  color: var(--accent);
}

/* Отступ под заголовком уже задан самим заголовком: собственный gap добавлял
   его второй раз, и у прав сверху получалась полоса вдвое шире, чем у всех
   остальных панелей. */
.permission-section {
  display: grid;
  gap: 0;
}

.permission-section h3 {
  font-size: 17px;
}

.role-cloud-scroll {
  max-height: 220px;
  overflow-y: auto;
  padding-right: var(--space-1);
  scrollbar-gutter: stable;
}

.permission-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-2);
}

.permission-chip {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  min-height: 40px;
  padding: 8px 10px;
  border: 1px solid var(--layer-line);
  border-radius: var(--radius-compact);
  background: var(--layer-bg);
  color: var(--muted);
  font-size: 12px;
  font-weight: 800;
}

.permission-chip i {
  display: grid;
  place-items: center;
  width: 22px;
  height: 22px;
  border-radius: 8px;
  background: var(--layer-bg-strong);
  font-style: normal;
}

.permission-chip.is-granted {
  border-color: color-mix(in srgb, var(--green) 38%, var(--layer-line));
  color: var(--text);
}

.permission-chip.is-granted i {
  background: color-mix(in srgb, var(--green) 18%, var(--layer-bg-strong));
  color: var(--green);
}

.current-activity-grid {
  grid-template-columns: repeat(2, minmax(0, 1fr));
}


.current-activity-card strong,
.activity-line b {
  overflow: visible;
  text-overflow: clip;
  white-space: normal;
  overflow-wrap: anywhere;
}

/* Заголовок карточки — одна строка. Так шапки одинаковой высоты у всех
   четырёх, содержимое начинается на одном уровне, а панель не растёт вдвое
   ради одного длинного названия: хвост уходит в многоточие. */
.current-activity-card > span:nth-child(2) {
  display: grid;
  align-content: start;
}

.current-activity-card > span:nth-child(2) strong {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.activity-line {
  padding: var(--space-2) var(--space-3);
}

.activity-action {
  align-self: end;
  min-height: 44px;
  white-space: normal;
}

.discord-community-grid {
  align-items: stretch;
}

.leaderboard-panel,
.people-finder {
  z-index: 1;
  display: flex;
  flex-direction: column;
  height: min(680px, 78vh);
  min-height: 560px;
}

.leaderboard-list,
.people-list {
  flex: 1;
  min-height: 0;
  max-height: none;
}

.choice-menu details {
  isolation: auto;
}

.choice-menu details[open] {
  z-index: 42;
}

.choice-menu:has(details[open]) {
  z-index: 44;
}

.panel:has(.choice-menu details[open]) {
  z-index: 40;
  overflow: visible;
}

/* Список висит поверх страницы, и под ним обычно стоят кнопки — в семье это
   красная «Покинуть семью». Без явной границы всплывающий список читался как
   продолжение панели, и кнопка выглядела его частью. Отделяем тремя вещами
   сразу: светлым кольцом по контуру, глубокой тенью и зазором от поля. */
.choice-menu-options {
  z-index: 43;
  overflow: hidden auto;
  padding: var(--space-1);
  border: 0;
  border-radius: var(--radius-control);
  background: var(--surface-plain);
  opacity: 1;
  box-shadow:
    0 0 0 1.5px color-mix(in srgb, var(--discord-blurple) 34%, var(--layer-line)),
    0 0 0 6px color-mix(in srgb, var(--bg) 62%, transparent),
    0 22px 48px rgba(0, 0, 0, 0.5);
}

.choice-menu-options button {
  background: var(--surface-plain);
}

.operations-panel,
.inventory-panel {
  display: flex;
  flex-direction: column;
  height: min(610px, 72vh);
  min-height: 500px;
}


.pixel-countdown {
  min-height: 18px;
  font-size: 12px;
  font-weight: 800;
  font-variant-numeric: tabular-nums;
}

@media (max-width: 980px) {
  .leaderboard-panel,
  .people-finder,
  .operations-panel,
  .inventory-panel {
  height: auto;
  min-height: 0;
}

  .leaderboard-list,
  .people-list,
  .operation-list,
  .inventory-list {
  max-height: min(540px, 64vh);
}
}

@media (max-width: 760px) {
  .current-activity-grid,
  .permission-grid {
  grid-template-columns: 1fr;
}

  .birthday-form-actions {
  display: grid;
  grid-template-columns: 1fr;
}

  .site-search-backdrop {
  padding: var(--space-3);
}

  .site-search-dialog {
  max-height: calc(100vh - var(--space-5));
}
}

/* Dashboard invariants: live updates must not shift surrounding panels. */
.site-search-backdrop {
  backdrop-filter: none;
}

/* Сообщение переносится по словам: обрезка многоточием прятала как раз ту
   часть, ради которой сообщение и показывают. Место под две строки
   зарезервировано заранее, поэтому панель от него не прыгает. */
.inline-status {
  display: block;
  min-height: 18px;
  margin: 0;
  line-height: 18px;
  overflow-wrap: anywhere;
}

/* Пустая строка состояния схлопывается: место под неё держалось всегда, и внизу
   панели оставалась полоса, которой там быть не должно. Сообщения появляются
   редко и короткие — сдвиг при их показе незаметнее, чем постоянный воздух. */
.inline-status.is-empty {
  display: none;
}

.current-activity-card {
  grid-template-rows: auto auto;
}

/* Последняя строка забирает остаток высоты — иначе растягивать кнопку не во
   что: ряды сжимаются по содержимому. */
.current-activity-card.is-empty {
  grid-template-rows: auto 1fr;
}

.current-activity-card.is-empty > span:nth-child(2) {
  align-self: center;
}

.current-activity-card > .activity-action {
  grid-column: 1 / -1;
}

.dashboard-columns {
  grid-template-columns: minmax(0, 1.2fr) minmax(320px, 0.8fr);
  align-items: start;
}

.dashboard-stack {
  display: grid;
  align-content: start;
  gap: var(--space-4);
  min-width: 0;
}

.character-editor,
.family-panel,
.operations-panel,
.inventory-panel {
  display: flex;
  flex-direction: column;
  min-width: 0;
  height: auto;
}

.character-editor-layout {
  min-height: 0;
}

.operation-list,
.inventory-list {
  /* Колонка на flex, а не на grid: у строки задан min-height, и в сетке ряд
     считался по нему — 54 пикселя, — а всё, что выше, вылезало на соседнюю
     карточку. Во flex-колонке строка занимает столько, сколько ей нужно. */
  display: flex;
  flex-direction: column;
  align-items: stretch;
  flex: 0 1 auto;
  min-height: 0;
  max-height: min(680px, 72vh);
  overflow-y: auto;
}

.operation-row,
.inventory-row {
  flex: 0 0 auto;
}

.character-controls {
  min-height: 0;
}

.character-controls > .primary-button {
  align-self: end;
}

.family-preview-media {
  position: relative;
}

.family-scene-hotspots {
  position: absolute;
  inset: 0;
  pointer-events: none;
}

.family-scene-hotspot {
  position: absolute;
  top: 0;
  bottom: 0;
  pointer-events: auto;
}

.family-scene-hotspot i {
  z-index: 2;
  overflow: hidden;
  width: max-content;
  padding: 7px 9px;
  border: 1px solid var(--layer-line);
  border-radius: var(--radius-compact);
  background: var(--surface-plain);
  box-shadow: var(--layer-shadow);
  color: var(--text);
  font-style: normal;
  line-height: 1.3;
  text-overflow: ellipsis;
  white-space: nowrap;
  opacity: 0;
  transition:
    opacity var(--motion-quick) var(--motion-standard),
    transform var(--motion-control) var(--motion-fluid);
  pointer-events: none;
}

.family-scene-hotspot:hover i {
  opacity: 1;
}

.family-scene-hotspots.layout-trio .slot-left {
  left: 2.5%;
  width: 33.3%;
}

.family-scene-hotspots.layout-trio .slot-center {
  left: 35.8%;
  width: 28.4%;
}

.family-scene-hotspots.layout-trio .slot-right {
  left: 64.2%;
  width: 33.3%;
}

.family-scene-hotspots.layout-pair .slot-left {
  left: 16.7%;
  width: 33.3%;
}

.family-scene-hotspots.layout-pair .slot-right {
  left: 50%;
  width: 33.3%;
}

.family-scene-hotspots.layout-adult-child .slot-left {
  left: 19.2%;
  width: 33.3%;
}

.family-scene-hotspots.layout-adult-child .slot-center {
  left: 52.5%;
  width: 28.4%;
}

.family-roster {
  min-height: 0;
  overflow: hidden auto;
  overflow-x: hidden;
}

.family-member {
  grid-template-columns: auto minmax(0, 1fr);
  min-width: 0;
  overflow: hidden;
}

.family-member-copy,
.person-card > span:last-child,
.profile-menu-head > span:last-child,
.leaderboard-row > span:nth-child(3) {
  min-width: 0;
  overflow: hidden;
}

.family-member-copy strong,
.person-card strong,
.person-card small,
.leaderboard-row > span:nth-child(3),
.account-identity strong,
.account-identity small,
.profile-menu-head strong,
.profile-menu-head small {
  overflow: hidden;
  max-width: 100%;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.family-member.has-actions .family-member-actions {
  grid-column: 1 / -1;
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  width: 100%;
  min-width: 0;
  margin-top: var(--space-1);
}

.family-member.has-actions .family-member-actions button {
  min-width: 0;
  white-space: normal;
}

.people-list,
.person-card,
.person-card > span:last-child,
.person-card p {
  box-sizing: border-box;
  min-width: 0;
  max-width: 100%;
}

.people-list {
  width: 100%;
  overflow-x: hidden;
}

.person-card p {
  overflow: hidden;
  overflow-wrap: anywhere;
}

.account-card,
.account-economy,
.profile-stat,
.leaderboard-row,
.person-card {
  min-width: 0;
}

.account-identity {
  flex: 1 1 180px;
  overflow: hidden;
}

.account-economy {
  flex: 0 1 auto;
}

.profile-stat strong,
.profile-stat-note {
  overflow: hidden;
  max-width: 100%;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Обычные панели: заголовок и список под ним. Раньше здесь была сетка
   «заголовок + остаток высоты» под фиксированную обёртку — из-за неё список
   растягивался на всю панель, и при пяти ролях внутри висел воздух. */
.role-section,
.permission-section {
  min-width: 0;
  min-height: 0;
  margin: 0;
}

.permission-section {
  border: 1.5px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface);
  box-shadow: var(--shadow);
}

.roles-login-panel {
  grid-row: 1 / -1;
}

/* Список занимает столько, сколько нужно содержимому, и прокручивается,
   когда его много, — как все остальные списки на сайте. */
.role-cloud-scroll,
.permission-grid {
  align-content: start;
  width: 100%;
  height: auto;
  min-height: 0;
  max-height: min(320px, 44vh);
  overflow-y: auto;
}

/* Роли — облако плашек по ширине названия, а не столбик во всю панель:
   столбик остался от времён, когда список был сеткой в одну колонку. */
.role-cloud-scroll {
  display: flex;
  flex-flow: row wrap;
}

.role-cloud-scroll .role-chip {
  flex: 0 1 auto;
  width: auto;
}

.role-chip,
.permission-chip {
  width: 100%;
  min-width: 0;
  min-height: 42px;
  justify-content: flex-start;
  overflow: hidden;
  text-align: left;
}

.role-chip {
  white-space: normal;
  overflow-wrap: anywhere;
}

@media (max-width: 760px) {
  .dashboard-columns {
  grid-template-columns: 1fr;
}

  .family-roster {
  max-height: min(360px, 48vh);
}
}

@media (max-width: 760px) {
  .role-cloud-scroll,
  .permission-grid,
  .family-member.has-actions .family-member-actions {
  grid-template-columns: 1fr;
}
}

/* Discord dashboard: paired geometry and consistent live cards. */

.current-activity-card {
  /* Высота по содержимому: принудительный минимум и растягивающийся ряд
     оставляли под коротким текстом пустую половину карточки. */
  grid-template-rows: auto auto;
}

.current-activity-card > span:nth-child(2) small {
  font-size: 12px;
  line-height: 1.25;
}

.current-activity-card > span:nth-child(2) strong {
  margin-top: var(--space-1);
  line-height: 1.18;
}

.current-activity-list {
  align-self: start;
}

.current-activity-list > p,
.activity-line {
  box-sizing: border-box;
  margin: 0;
}

.current-activity-list > p {
  display: flex;
  align-items: center;
  border-radius: var(--radius-compact);
  background: var(--layer-bg-strong);
  color: var(--muted);
  font-weight: 750;
}

.activity-line {
  grid-template-columns: minmax(0, 1fr) auto;
  gap: var(--space-3);
}

.activity-line > span {
  min-width: 0;
}


.activity-line em {
  font-size: 12px;
}

.activity-first {
  overflow: hidden;
  margin-top: var(--space-2);
  font-size: 12px;
  line-height: 1.25;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Строка ленты и значок «в календарь» стоят в ряд: сама строка бывает
   ссылкой, а вкладывать кнопку в ссылку нельзя — нажатие уходило бы по ней. */
.activity-row {
  display: flex;
  align-items: stretch;
  gap: 6px;
  min-width: 0;
}

.activity-row > .activity-line {
  flex: 1 1 auto;
  min-width: 0;
}

/* Кнопка со значком — квадрат ровно по высоте своей строки. Строки в ленте
   разной высоты (у события есть время, у дня рождения нет), поэтому сторону
   не задаём числом: высоту берём растяжением по строке, а ширину из неё же
   через aspect-ratio. min-height сбрасываем — глобальное правило для button
   иначе не даёт кнопке стать ниже высоты обычного элемента управления. */
.activity-calendar {
  display: grid;
  flex: 0 0 auto;
  align-self: stretch;
  aspect-ratio: 1;
  place-items: center;
  width: auto;
  height: auto;
  min-height: 0;
  padding: 0;
  border: 1.5px solid var(--layer-line);
  border-radius: var(--radius-compact);
  background: var(--layer-bg-strong);
  color: var(--muted);
  cursor: pointer;
}

/* Значок — доля от кнопки, а не число: кнопка растёт вместе со строкой, и
   при жёстком размере он выглядел мельче в высокой строке события, чем в
   низкой строке дня рождения. Доля держит пропорцию одинаковой. */
.activity-calendar svg {
  width: 62%;
  height: 62%;
}

.activity-calendar:hover {
  border-color: color-mix(in srgb, var(--discord-blurple) 40%, var(--layer-line));
  color: var(--text);
}

.activity-line-leading {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  align-items: center;
  gap: var(--space-2);
}

.operations-panel {
  min-height: 560px;
}

/* Список занимает всю свободную высоту панели и прокручивается внутри себя.
   Снятый предел растягивал панель на всю историю: с двумя тысячами записей она
   выходила в двадцать семь тысяч пикселей. Все операции при этом на месте —
   просто до дальних надо доскроллить. */
.operations-panel .operation-list {
  flex: 1 1 auto;
  min-height: 0;
  max-height: none;
}

.operations-panel:not(.is-collapsed) {
  height: min(610px, 72vh);
}

.family-roster {
  display: grid;
  grid-auto-rows: max-content;
  align-content: start;
  flex: 0 1 auto;
  max-height: min(280px, 36vh);
  padding-right: var(--space-2);
  padding-bottom: var(--space-3);
  touch-action: pan-y;
  scrollbar-gutter: stable;
  mask-image: none;
}

.family-roster::-webkit-scrollbar,
.operation-list::-webkit-scrollbar,
.inventory-list::-webkit-scrollbar,
.news-list::-webkit-scrollbar,
.role-cloud-scroll::-webkit-scrollbar,
.permission-grid::-webkit-scrollbar {
  width: 8px;
}

.family-roster::-webkit-scrollbar-thumb,
.operation-list::-webkit-scrollbar-thumb,
.inventory-list::-webkit-scrollbar-thumb,
.news-list::-webkit-scrollbar-thumb,
.role-cloud-scroll::-webkit-scrollbar-thumb,
.permission-grid::-webkit-scrollbar-thumb {
  border-radius: 999px;
  background: color-mix(in srgb, var(--text) 26%, transparent);
}

.family-scene-hotspot i {
  position: fixed;
  right: auto;
  bottom: auto;
  left: 0;
  top: 0;
  max-width: 220px;
  font-size: 12px;
  transform: none;
}

.family-scene-hotspot:hover i {
  transform: none;
}

@media (max-width: 980px) {
  .operations-panel {
  min-height: 0;
}

  .account-card {
  grid-template-columns: auto minmax(0, 1fr);
}

  .account-economy {
  grid-column: 1 / -1;
}
}

@media (max-width: 760px) {
  .account-economy {
  grid-auto-flow: row;
  grid-auto-columns: auto;
  grid-template-columns: 1fr;
}

  .current-activity-card {
  min-height: 0;
}

  .family-roster {
  max-height: min(380px, 52vh);
}
}

/* ── Уведомления ─────────────────────────────────────────────────────────── */

.notifications-panel {
  --project-color: var(--discord-blurple);
}

.notification-list {
  display: grid;
  gap: var(--space-1);
  margin-top: var(--space-1);
  max-height: min(560px, 68vh);
  overflow-y: auto;
  padding: 0;
  scrollbar-gutter: stable;
  scrollbar-width: thin;
}

.notification-list::-webkit-scrollbar {
  width: 8px;
}

.notification-list::-webkit-scrollbar-thumb {
  border-radius: 999px;
  background: color-mix(in srgb, var(--text) 26%, transparent);
}

.notification-row {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-2) var(--space-3);
  border: 1px solid var(--layer-line);
  border-radius: var(--radius-compact);
  background: var(--layer-bg);
}

.notification-row-master {
  margin-top: var(--space-2);
  border-color: color-mix(in srgb, var(--discord-blurple) 40%, var(--layer-line));
  background: color-mix(in srgb, var(--discord-blurple) 10%, var(--layer-bg));
}

.notification-icon {
  line-height: 1;
  display: grid;
  place-items: center;
  width: 36px;
  height: 36px;
  border-radius: var(--radius-compact);
  background: var(--layer-bg-strong);
  font-size: 24px;
}

.notification-copy {
  display: grid;
  gap: 2px;
  min-width: 0;
}

.notification-copy strong {
  overflow: hidden;
  font-size: 14px;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.notification-copy small {
  color: var(--muted);
  font-size: 12px;
  line-height: 1.35;
}

.notification-toggle {
  flex: none;
  min-width: 116px;
  min-height: 44px;
  padding: 0 var(--space-3);
  border: 1px solid transparent;
  border-radius: var(--radius-control);
  font: inherit;
  font-size: 13px;
  cursor: pointer;
  transition:
    background var(--motion-control) var(--motion-standard),
    border-color var(--motion-control) var(--motion-standard),
    color var(--motion-control) var(--motion-standard);
}

.notification-toggle.is-on {
  border-color: color-mix(in srgb, var(--green) 45%, var(--layer-line));
  background: color-mix(in srgb, var(--green) 18%, var(--layer-bg));
  color: var(--text);
}

.notification-toggle.is-off {
  border-color: var(--layer-line);
  background: var(--layer-bg-strong);
  color: var(--muted);
}

.notification-toggle:disabled {
  cursor: default;
  opacity: 0.55;
}

.notification-toggle:not(:disabled):hover {
  border-color: color-mix(in srgb, var(--discord-blurple) 55%, var(--layer-line));
}

@media (max-width: 620px) {
  .notification-row {
  grid-template-columns: auto minmax(0, 1fr);
  row-gap: var(--space-2);
}

  .notification-toggle {
  grid-column: 1 / -1;
  width: 100%;
}
}

/* ── Единый вид прокручиваемых списков и подписей ────────────────────────── */

/* Полоса появляется только когда есть что листать. Раньше у семьи стоял
   принудительный `scroll` и на двух участниках висел мёртвый жёлоб. */
.family-roster {
  overflow-y: auto;
}

/* Мягкий уход в прозрачность с обоих концов — одинаково у всех списков внутри
   панелей. Выпадающие меню и диалог поиска сюда намеренно не входят. */
.news-list,
.role-cloud-scroll,
.permission-grid,
.notification-list,
.economy-scroll,
.family-roster {
  mask-image: linear-gradient(
    to bottom,
    transparent 0,
    #000 var(--fade-top, 0px),
    #000 calc(100% - var(--fade-bottom, 0px)),
    transparent 100%
  );
}

/* Короткие подписи — та же гарнитура, что у заголовков. Иначе внутри одной
   панели соседствуют две разные гарнитуры, и текст «скачет». */
.color-control span,
.profile-stat small,
.current-activity-card small,
.notification-copy strong,
.panel-header h2 {
  font-family: var(--font-display);
}

/* Стрелка выпадающего меню: треугольник как у бегунка прокрутки, только
   крупнее. Рисуем бордерами — не зависит от наличия глифа в шрифте. */
.choice-menu summary i::before,
.command-category summary::after {
  content: "";
  display: block;
  width: 0;
  height: 0;
  border-right: 7px solid transparent;
  border-left: 7px solid transparent;
  border-top: 9px solid currentColor;
  border-bottom: 0;
}

.choice-menu details[open] summary i::before,
.command-category[open] summary::after {
  border-top: 0;
  border-bottom: 9px solid currentColor;
}

/* Канон: стрелка меняется без вращения и смещения. Поворот на 180° убран. */
.command-category summary::after {
  color: var(--muted);
  transition: none;
}

.command-category[open] summary::after {
  transform: none;
}

.activity-avatar {
  flex: none;
  width: 22px;
  height: 22px;
  border-radius: var(--radius-pill);
  object-fit: cover;
}

.command-short {
  border-style: dashed;
  opacity: 0.85;
}

/* ── Админ-панель ────────────────────────────────────────────────────────── */

.nav-item-admin {
  --project-color: var(--accent);
}

.nav-icon-glyph {
  display: grid;
  place-items: center;
  font-size: 20px;
}

.admin-queue {
  display: grid;
  gap: var(--space-3);
  margin-top: var(--space-2);
  max-height: min(760px, 74vh);
  overflow-y: auto;
  padding: 0;
  scrollbar-gutter: stable;
  scrollbar-width: thin;
  mask-image: linear-gradient(
    to bottom,
    transparent 0,
    #000 var(--fade-top, 0px),
    #000 calc(100% - var(--fade-bottom, 0px)),
    transparent 100%
  );
}

.admin-queue-item {
  display: grid;
  gap: var(--space-2);
  padding: var(--space-3);
  border: 1px solid var(--layer-line);
  border-radius: var(--radius-control);
  background: var(--layer-bg);
}

.admin-queue-head {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  align-items: center;
  gap: var(--space-3);
}

.admin-queue-facts {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-1) var(--space-4);
  margin: 0;
}

.admin-queue-facts div {
  display: grid;
  gap: 2px;
}

.admin-queue-facts dt {
  color: var(--muted);
  font-size: 11px;
}

.admin-queue-facts dd {
  margin: 0;
  font-size: 14px;
}

.admin-queue-use {
  margin: 0;
  color: var(--muted);
  font-size: 13px;
}

.admin-queue-gif {
  max-width: min(320px, 100%);
  border-radius: var(--radius-compact);
}

.admin-queue-actions,
.admin-reject-form {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  align-items: center;
}

.admin-reject-form input {
  flex: 1 1 240px;
  min-width: 0;
  min-height: var(--control-height);
  padding: 0 var(--space-3);
  border: 1px solid var(--layer-line);
  border-radius: var(--radius-control);
  background: var(--layer-bg-strong);
}

.admin-setting {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
  align-items: center;
  justify-content: space-between;
  margin-top: var(--space-2);
  padding: var(--space-3);
  border: 1px solid var(--layer-line);
  border-radius: var(--radius-control);
  background: var(--layer-bg);
}

.admin-cooldown-form {
  display: flex;
  gap: var(--space-2);
  align-items: center;
}

.admin-cooldown-form input {
  width: 110px;
  text-align: center;
}

.compact-button.is-success {
  border-color: color-mix(in srgb, var(--green) 45%, var(--layer-line));
  background: color-mix(in srgb, var(--green) 18%, var(--layer-bg));
}

.compact-button.is-danger {
  border-color: color-mix(in srgb, var(--accent) 45%, var(--layer-line));
  background: color-mix(in srgb, var(--accent) 16%, var(--layer-bg));
}

/* Кнопка личного задания занимает такую же рамку, как блоки соседних карточек:
   та же высота, отступы, фон и радиус, без тонкой обводки. */
.current-activity-card > .activity-action {
  border-color: transparent;
  border-radius: var(--radius-compact);
  background: var(--layer-bg-strong);
}

/* Подсветка — только у кнопки: рядом той же меркой сверстана подпись
   «Новые розыгрыши появятся здесь», и нажимать там нечего. */
.current-activity-card > button.activity-action:hover {
  border-color: color-mix(in srgb, var(--discord-blurple) 38%, var(--layer-line));
}

.activity-line-famous i {
  line-height: 1;
  display: grid;
  place-items: center;
  width: 22px;
  height: 22px;
  font-size: 15px;
}

/* Поля ввода во всех формах админки выглядят одинаково — как в остальных
   формах сайта. Раньше каждая форма описывала их заново и чуть иначе. */
.admin-moderation-form input,
.admin-cooldown-form input,
.admin-famous-form input,
.admin-access-row input {
  min-height: var(--control-height);
  padding: 0 var(--space-3);
  border: 1px solid var(--layer-line);
  border-radius: var(--radius-control);
  background: var(--layer-bg-strong);
  color: var(--text);
  font: inherit;
}

.admin-subhead {
  margin: var(--space-3) 0 var(--space-2);
  color: var(--muted);
  font-family: var(--font-display);
  font-size: 14px;
  font-weight: 900;
}

.admin-subhead:first-of-type {
  margin-top: var(--space-2);
}

.admin-famous-form {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  align-items: center;
  margin-top: var(--space-2);
}



.admin-famous-form input[name="name"] {
  flex: 1 1 220px;
  min-width: 0;
}

.admin-famous-form input[name="url"] {
  flex: 2 1 300px;
  min-width: 180px;
}

.admin-famous-form input[name="date"] {
  width: 100px;
  text-align: center;
}

.admin-birthday-columns {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-4);
  align-items: start;
  margin-top: var(--space-4);
}

.admin-birthday-list {
  display: grid;
  gap: var(--space-1);
  max-height: min(420px, 52vh);
  overflow-y: auto;
  padding: 0;
  scrollbar-gutter: stable;
  scrollbar-width: thin;
  mask-image: linear-gradient(
    to bottom,
    transparent 0,
    #000 var(--fade-top, 0px),
    #000 calc(100% - var(--fade-bottom, 0px)),
    transparent 100%
  );
}

.admin-birthday-row {
  display: grid;
  grid-template-columns: auto minmax(120px, 0.8fr) minmax(180px, 1.2fr) auto auto;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-2) var(--space-3);
  border: 1px solid var(--layer-line);
  border-radius: var(--radius-compact);
  background: var(--layer-bg);
}

.admin-birthday-link {
  width: 100%;
  min-width: 0;
  min-height: var(--control-height);
  padding: 0 var(--space-3);
  border: 1px solid var(--layer-line);
  border-radius: var(--radius-control);
  background: var(--layer-bg-strong);
  color: var(--text);
}

@media (max-width: 760px) {
  .admin-birthday-row {
    grid-template-columns: auto minmax(0, 1fr) auto auto;
  }

  .admin-birthday-link {
    grid-column: 1 / -1;
    grid-row: 2;
  }
}

.admin-birthday-date {
  color: var(--muted);
  font-size: 12px;
  font-variant-numeric: tabular-nums;
}

.admin-birthday-name {
  overflow: hidden;
  font-size: 14px;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Один отступ между блоками на всю админку: раньше каждый раздел задавал его
   сам, и форма розыгрыша упиралась в список без единого пикселя. */
.panel[id^="admin-"] > * {
  margin-block: 0;
}

/* Селектор с [id] специфичнее общего `.panel > .panel-header + *`, который
   обнуляет отступ у первого блока. */
.panel[id^="admin-"] > .panel-header + *,
.panel[id^="admin-"] > * + * {
  margin-top: var(--space-3);
}

@media (max-width: 860px) {
  .admin-birthday-columns {
  grid-template-columns: 1fr;
}
}

/* ── Создание предмета с сайта ───────────────────────────────────────────── */

.create-item-form {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-2) var(--space-3);
  margin-top: var(--space-3);
}

.create-field {
  display: grid;
  gap: var(--space-1);
  min-width: 0;
}

.create-field-wide {
  grid-column: 1 / -1;
}

.create-field-emoji input {
  text-align: center;
}

.create-field span {
  color: var(--muted);
  font-size: 12px;
  font-weight: 750;
}

.create-field input,
.create-field select {
  min-width: 0;
  min-height: var(--control-height);
  padding: 0 var(--space-3);
  border: 1px solid var(--layer-line);
  border-radius: var(--radius-control);
  background: var(--layer-bg-strong);
  color: var(--text);
  font: inherit;
}

.create-field input[type="file"] {
  padding: 9px var(--space-3);
}

.create-field textarea {
  min-width: 0;
  padding: var(--space-2) var(--space-3);
  border: 1px solid var(--layer-line);
  border-radius: var(--radius-control);
  background: var(--layer-bg-strong);
  color: var(--text);
  font: inherit;
  font-size: 13px;
  line-height: 1.55;
  resize: vertical;
}

.rules-actions {
  grid-column: 1 / -1;
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
}

.create-submit {
  grid-column: 1 / -1;
  margin-top: var(--space-1);
}

@media (max-width: 620px) {
  .create-item-form {
  grid-template-columns: 1fr;
}
}

.admin-moderation-form {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-2) var(--space-3);
  margin-top: var(--space-3);
}

.admin-moderation-actions {
  grid-column: 1 / -1;
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin-top: var(--space-1);
}

@media (max-width: 620px) {
  .admin-moderation-form {
  grid-template-columns: 1fr;
}
}

/* Полное число участников вместе с ботами — приглушённая приписка,
   выровненная по базовой линии основного числа, а не свисающая под ним. */
.metric-extra {
  margin-left: 6px;
  color: var(--muted);
  font-size: 0.5em;
  font-weight: 750;
  letter-spacing: 0;
  vertical-align: baseline;
}

/* Главный переключатель ЛС выделяется цветом, но не размером: одинаковая
   высота строки со всеми остальными пунктами. */


.leaderboard-privacy {
  margin-top: var(--space-3);
}

/* Строка появляется, только когда заданы цена и тираж. Место под неё держим
   всегда, иначе панель подпрыгивает прямо во время набора цены. */
.create-fee {
  min-height: 16px;
  margin: var(--space-3) 0 0;
  color: var(--muted);
  font-size: 13px;
  font-weight: 750;
  line-height: 16px;
}

.hero-admin-button {
  --project-color: var(--accent);
}

.activity-note {
  color: var(--muted);
  font-size: 12px;
  font-weight: 750;
}

/* Подпись категории в справочнике команд. */
.command-category summary small.category-hint {
  overflow: hidden;
  color: var(--muted);
  font-size: 12px;
  font-weight: 700;
  text-align: left;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Текст кнопки личного задания совпадает по кеглю с содержимым соседних
   блоков — крупная рамка не должна нести мелкую подпись. */
.current-activity-card > .activity-action {
  font-weight: 800;
}

/* Панель создания идёт во всю ширину под колонками: сбоку от неё пусто быть
   не должно. */
.create-item-panel {
  --project-color: var(--discord-blurple);
}


.create-item-panel .create-submit {
  grid-column: 1 / -1;
  justify-self: start;
}

@media (max-width: 980px) {
  .create-item-panel .create-item-form {
  grid-template-columns: repeat(2, minmax(0, 1fr));
}
}

@media (max-width: 620px) {
  .create-item-panel .create-item-form,
  .create-item-panel .create-field-wide {
  grid-template-columns: 1fr;
  grid-column: 1 / -1;
}
}

/* ── Единый вид раскрывающихся заголовков ────────────────────────────────── */

/* Порядок в строке: название, описание, стрелка справа. Никаких перестановок
   через order — иначе стрелка уезжает в середину строки. */
.command-category summary {
  display: flex;
  align-items: center;
  gap: var(--space-3);
}

.command-category summary small.category-hint {
  order: 0;
  flex: 1 1 auto;
  margin-left: 0;
}

.command-category summary::after {
  order: 1;
  flex: 0 0 auto;
  margin-left: 0;
}

/* Ровно одна стрелка справа, меняется сменой стороны бордера. */
.choice-menu summary i {
  font-size: 0;
}

.choice-menu summary i::before {
  content: "";
  font-size: 0;
}

/* ── Пропорции: крупная рамка не несёт мелкий текст ──────────────────────── */


.current-activity-list > p,
.activity-line {
  padding: var(--space-2) var(--space-3);
}

.current-activity-card > .activity-action {
  /* Ровно на месте плашки задания из соседней карточки: та же высота и тот
     же верхний край. Отступ сверху сдвигал кнопку на два пикселя вниз. */
  min-height: 56px;
  align-self: stretch;
  margin-top: 0;
  width: 100%;
  padding: var(--space-2) var(--space-4);
  font-size: 15px;
  letter-spacing: 0;
}

.profile-stat {
  min-height: 118px;
}

/* Обязательные поля помечаются звёздочкой, необязательные — ничем. */
.create-field span .req {
  margin-left: 4px;
  color: var(--accent);
  font-style: normal;
}

.create-templates {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  align-items: center;
  margin: 0;
  color: var(--muted);
  font-size: 12px;
  font-weight: 750;
}

.create-templates a {
  padding: 4px var(--space-2);
  border: 1px solid var(--layer-line);
  border-radius: var(--radius-compact);
  color: var(--text);
  text-decoration: none;
}

.create-templates a:hover {
  border-color: color-mix(in srgb, var(--discord-blurple) 45%, var(--layer-line));
}

/* Приватность: подпись занимает всю свободную ширину до кнопки. */
.leaderboard-privacy .notification-copy {
  align-self: center;
}

.leaderboard-privacy .notification-copy strong {
  font-size: 14px;
}

/* Главный переключатель ЛС — только цвет, размеры общие. */
.notification-row-master {
  padding: var(--space-2) var(--space-3);
}

.notification-row-master .notification-copy small {
  font-size: 12px;
}

/* Панель создания и уведомления делят строку поровну. */
.create-item-panel .create-item-form {
  grid-template-columns: repeat(2, minmax(0, 1fr));
}

.create-item-panel .create-field-wide {
  grid-column: 1 / -1;
}

.create-item-panel .create-submit {
  min-width: 0;
  width: 100%;
}

.treasury-amount {
  margin: var(--space-2) 0 var(--space-3);
  font-family: var(--font-display);
  font-size: 26px;
}

.admin-birthday-date.is-positive {
  color: var(--green);
}

.admin-birthday-date.is-negative {
  color: var(--accent);
}

/* Уведомления и создание предмета делят строку: обе панели тянутся на общую
   высоту, а внутренние списки прокручиваются, а не растягивают карточку. */
.notifications-panel,
.create-item-panel {
  display: flex;
  flex-direction: column;
}

.notifications-panel .notification-list {
  flex: 1 1 auto;
  min-height: 0;
  max-height: none;
}

.create-item-panel .create-item-form {
  flex: 1 1 auto;
  align-content: start;
}


/* ── Гарнитуры ───────────────────────────────────────────────────────────── */

/* У заголовочного шрифта единственное начертание. Просим ровно его, чтобы
   правило веса совпадало с тем, что реально лежит в файле. */
h1,
h2,
h3,
.brand strong,
.nav-item strong,
.project-card strong,
.panel-header h2,
.admin-subhead,
.treasury-amount,
.current-activity-card > span:nth-child(2) strong,
.profile-stat strong,
button {
  font-weight: 900;
}

/* Короткие подписи остаются на заголовочной гарнитуре — иначе внутри одной
   панели снова соседствуют две разные и текст читается как скачущий. */
.color-control span,
.profile-stat small,
.current-activity-card small,
.notification-copy strong {
  font-family: var(--font-display);
  font-weight: 900;
}

/* Галочка рисуется бордерами: символа ✓ нет ни в одной из наших гарнитур,
   и он подтягивал системный шрифт отдельным глифом. */
.permission-chip .check-mark {
  flex: none;
  width: 9px;
  height: 5px;
  margin-right: 2px;
  border-bottom: 2px solid currentColor;
  border-left: 2px solid currentColor;
  transform: rotate(-45deg);
}

/* ── Соразмерность текста и панелей ──────────────────────────────────────── */

/* Пустые состояния занимают строку, а не половину карточки. */
.current-activity-list > p {
  min-height: 0;
  padding: var(--space-2) var(--space-3);
  font-size: 13px;
}

/* Карточка «Сейчас на сервере»: заголовок, содержимое, ничего лишнего. */
.current-activity-card {
  gap: var(--space-2);
  padding: var(--space-3);
}

.current-activity-card > span:nth-child(2) strong {
  font-size: 16px;
}

.current-activity-card small {
  font-size: 11px;
}

.activity-line b {
  font-size: 13px;
  line-height: 1.35;
}

/* Ряд карточек не выравнивается по самой высокой. */
.current-activity-grid {
  align-items: start;
}

/* ── Экономика: одинаковый отступ у всех вкладок ─────────────────────────── */

/* Вкладки открывались на разной высоте: у «Банка» первый элемент шёл без
   отступа, а у «Работы» подпись несла собственный верхний. Теперь расстояние
   от кнопок вкладок задаёт сама область, а не содержимое конкретной вкладки. */
.economy-pane {
  margin-top: var(--space-2);
}

.economy-pane > *:first-child {
  margin-top: 0;
}

.economy-note {
  margin: var(--space-3) 0 0;
}

.economy-balance-grid {
  margin-top: 0;
  margin-bottom: var(--space-3);
}

/* ── Портфель ────────────────────────────────────────────────────────────── */

.portfolio-position {
  display: grid;
  gap: 2px;
  justify-items: end;
  text-align: right;
  white-space: nowrap;
}

.portfolio-position strong {
  font-size: 15px;
}

.portfolio-position small {
  font-size: 12px;
  font-weight: 750;
}

/* ── Парные карточки «Сейчас на сервере» ─────────────────────────────────── */

/* Соседние карточки в ряду выравниваются по высоте, но содержимое остаётся
   вверху: карточка тянется, элементы внутри — нет. */
.current-activity-grid {
  align-items: stretch;
}

/* ── Единый вид полос прокрутки ──────────────────────────────────────────── */

/* Раньше часть списков красила бегунок в синий, часть оставляла системным —
   отсюда разнобой. Один нейтральный серый на все прокручиваемые области. */
* {
  scrollbar-color: color-mix(in srgb, var(--text) 26%, transparent) transparent;
  scrollbar-width: thin;
}

*::-webkit-scrollbar {
  width: 8px;
  height: 8px;
}

*::-webkit-scrollbar-track {
  background: transparent;
}

*::-webkit-scrollbar-thumb {
  border-radius: 999px;
  background: color-mix(in srgb, var(--text) 26%, transparent);
}

*::-webkit-scrollbar-thumb:hover {
  background: color-mix(in srgb, var(--text) 38%, transparent);
}

/* ── Поиск по акциям ─────────────────────────────────────────────────────── */

.stock-search {
  margin-bottom: var(--space-2);
}

/* Превышение длины подсвечивается прямо во время ввода. */
.create-field input.is-over-limit {
  border-color: var(--accent);
  background: color-mix(in srgb, var(--accent) 10%, var(--layer-bg-strong));
}

.field-limit {
  margin-top: 2px;
  color: var(--accent);
  font-size: 12px;
  font-weight: 750;
}

/* ── Числовые поля ───────────────────────────────────────────────────────── */

/* Общий `appearance: none` для input снимал и нативные стрелки счётчика —
   нажимать было не на что. Возвращаем их именно числовым полям. */
input[type="number"] {
  appearance: auto;
  -webkit-appearance: auto;
}

input[type="number"]::-webkit-inner-spin-button,
input[type="number"]::-webkit-outer-spin-button {
  opacity: 1;
  height: 100%;
}

/* ── Выбор цвета ─────────────────────────────────────────────────────────── */

/* Цвет заливает всю кнопку, а не сидит скруглённым квадратом внутри рамки. */
.color-control input[type="color"] {
  overflow: hidden;
  padding: 0;
  cursor: pointer;
}

.color-control input[type="color"]::-webkit-color-swatch-wrapper {
  padding: 0;
}

.color-control input[type="color"]::-webkit-color-swatch {
  border: none;
  border-radius: 0;
}

.color-control input[type="color"]::-moz-color-swatch {
  border: none;
  border-radius: 0;
}

/* ── Ошибки не растягивают панель ────────────────────────────────────────── */

/* Строка ошибки под полем занимает заранее отведённое место: панель не должна
   прыгать в высоте от того, что человек ошибся. */
.create-field {
  position: relative;
  padding-bottom: 18px;
}

.field-limit {
  position: absolute;
  top: 100%;
  left: 0;
  right: 0;
  margin-top: -16px;
  line-height: 1.2;
  white-space: nowrap;
}

/* ── Списки на телефоне короче ───────────────────────────────────────────── */

@media (max-width: 760px) {
  .operation-list,
  .inventory-list,
  .economy-scroll,
  .leaderboard-list,
  .people-list,
  .notification-list,
  .admin-queue,
  .admin-birthday-list {
    max-height: 40vh;
  }

  /* На большом экране список операций тянется во всю высоту соседней колонки,
     ради этого с него снят предел. На телефоне колонок нет, и без этой строки
     он разворачивался целиком — прокрутка страницы уходила в никуда. */
  .operations-panel .operation-list {
    max-height: 40vh;
  }

  .family-roster {
    max-height: 30vh;
  }
}

/* ── Сворачивание панелей ────────────────────────────────────────────────── */

.panel-toggle {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  width: 100%;
  min-height: 0;
  padding: 0;
  border: 0;
  background: none;
  text-align: left;
  cursor: pointer;
}

.panel-toggle h2 {
  margin: 0;
}

.panel.is-collapsed > *:not(.panel-header) {
  display: none;
}

.panel.is-collapsed .panel-header {
  margin-bottom: 0;
}

/* Свёрнутая панель занимает высоту своего заголовка и ни пикселем больше.
   Соседка по строке сетки тянет ряд вверх, и без этого правила на месте
   свёрнутой оставалась пустая область в её рост. */
.panel.is-collapsed {
  align-self: start;
  min-height: 0;
  height: auto;
}

/* ── Шапка на телефоне ───────────────────────────────────────────────────── */

.profile-menu-tools {
  display: flex;
  gap: var(--space-2);
  justify-content: center;
  margin-top: var(--space-3);
}

/* Те же значки и тот же размер, что в шапке на большом экране: человек узнаёт
   кнопку, а не читает подпись заново. */
.menu-icon-button {
  display: grid;
  place-items: center;
  width: var(--top-theme-size);
  height: var(--top-theme-size);
  padding: 0;
  border: 1.5px solid var(--layer-line);
  border-radius: var(--radius-control);
  background: var(--layer-bg);
  color: var(--text);
  line-height: 0;
  cursor: pointer;
}

.menu-icon-button:hover {
  border-color: var(--discord-blurple);
}

.menu-icon-button[aria-pressed="false"] {
  color: var(--muted);
}

/* На узком экране поиск и тема живут внутри меню профиля: три отдельные
   кнопки в шапке занимали место, которого там нет. */
@media (max-width: 760px) {
  .search-button,
  .theme-button,
  .motion-button {
    display: none;
  }
}

@media (min-width: 761px) {
  .profile-menu-tools {
    display: none;
  }
}

/* ── Казино: фигура по центру, подпись отдельно ──────────────────────────── */

/* Раньше подпись о выигрыше делила сетку с фигурой и сдвигала её вверх.
   Теперь фигура занимает всю свободную высоту и центрируется в ней, а подпись
   живёт в отдельной строке заранее известной высоты — стол не прыгает. */
.casino-visual-card,
.casino-result-card {
  border: 1.5px solid var(--layer-line);
  border-radius: var(--radius-control);
  background: var(--layer-bg);
}

.casino-visual-card {
  display: grid;
  place-items: center;
  min-height: 180px;
  padding: 22px;
}

.casino-figure {
  display: grid;
  place-items: center;
  gap: 10px;
  width: 100%;
  min-height: 0;
}

.casino-result-card {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  gap: var(--space-3);
  padding: 14px 16px;
  text-align: left;
}

.casino-result-card small {
  color: var(--muted);
  font-weight: 750;
}

.casino-result-card strong {
  font-size: 21px;
  text-align: right;
}

.casino-figure > strong {
  font-size: clamp(34px, 6vw, 68px);
  letter-spacing: 0.06em;
}

.casino-visual-card .cockfight-preview {
  min-height: 0;
  padding: 0;
  border: 0;
  background: transparent;
}

/* ── Свой цвет в пиксельбатле — та же кнопка, что и везде ────────────────── */

/* ── Пиксельбатл: сетка и выбранная клетка ───────────────────────────────── */

.pixel-canvas-wrap {
  position: relative;
}
.pixel-confirm.is-hidden {
  display: none;
}

/* ── Плавность ───────────────────────────────────────────────────────────── */

/* Анимация барабанов трогала filter: blur — его браузер пересчитывает на
   каждом кадре и не может отдать композитору. Оставляем только сдвиг и
   масштаб: их кадры собираются на видеокарте. */
@keyframes slot-spin {
  from {
    transform: translateY(-5px) scale(0.98);
  }
  to {
    transform: translateY(5px) scale(1.02);
  }
}

.slot-preview.is-spinning {
  will-change: transform;
}

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

/* ── Значок валюты ───────────────────────────────────────────────────────── */

/* Та же картинка, что кастом-эмодзи БДаБика в Discord. Размер привязан к
   строке, поэтому значок растёт вместе с суммой рядом и не сбивает базовую
   линию: без этого числа в таблицах прыгали бы по вертикали. */
.coin {
  display: inline-block;
  width: 1.08em;
  height: 1.08em;
  vertical-align: -0.18em;
  object-fit: contain;
}

/* ── Суммы подстраивают кегль под свою длину ─────────────────────────────── */

/* Карточка объявляет себя контейнером, чтобы сумма внутри считала кегль от её
   собственной ширины, а не от размера окна: одно и то же число стоит и в
   широкой панели банка, и в узкой полоске профиля. */
.economy-balance-grid > span,
.account-economy > span,
.operation-summary > span {
  container-type: inline-size;
}

/* Задуманный размер — потолок. Если цифр столько, что строка в ширину не
   лезет, кегль опускается ровно настолько, чтобы она поместилась: 0.62em —
   средняя ширина знака в наших цифрах с разделителями. */
.money-figure {
  display: block;
  min-width: 0;
  font-size: min(var(--figure-size, 20px), calc(100cqi / (var(--len, 10) * 0.62)));
  line-height: 1.2;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.economy-balance-grid { --figure-size: 20px; }
.account-economy { --figure-size: 15px; }
.games-balance { --figure-size: 17px; }
.operation-summary { --figure-size: 19px; }

/* ── Банк: перевод и вклад ───────────────────────────────────────────────── */

/* Три карточки одного роста и одного строения: подпись, поле, кнопка. Раньше
   высоту им задавала длина подсказки, и ряд выходил рваным. */
.economy-actions-grid {
  align-items: stretch;
}

.economy-action-card {
  align-content: start;
  gap: var(--space-2);
  padding: var(--space-3);
}

.economy-action-card small {
  line-height: 1.25;
}

.economy-action-card > span {
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  gap: var(--space-2);
}

/* Кнопка ровно под своё слово плюс воздух по бокам, а не во всю карточку. */
.economy-action-card .compact-button,
.economy-action-card .danger-button {
  min-width: max-content;
  padding-inline: 14px;
}

/* Карточка снятого вклада живёт без поля ввода — кнопка занимает его место,
   чтобы ряд не разъезжался. */
.economy-action-static {
  align-content: space-between;
}

.economy-action-static .danger-button {
  width: 100%;
  min-height: var(--control-height);
}

/* ── Одинаковый отступ от заголовка панели до первого элемента ───────────── */

/* Раньше каждый список добавлял к отступу заголовка свой собственный, и
   расстояние до первой кнопки в разных панелях отличалось на глаз. */
.panel-header {
  margin-bottom: 14px;
}

.panel-toggle h2 {
  line-height: 1.2;
}

.panel > .panel-header + * {
  margin-top: 0;
}

/* У свёрнутой панели поля сверху и снизу одинаковые — иначе заголовок стоит
   выше середины полоски. Отдельное нижнее поле в 18px спорило с полем самой
   панели, которое на телефоне равно 13px. */
.panel.is-collapsed {
  padding-top: var(--panel-padding, 18px);
  padding-bottom: var(--panel-padding, 18px);
}

/* ── Скачки: дорожки ─────────────────────────────────────────────────────── */

/* Пять дорожек, по одной на зверя. Позиция бегущего — доля ширины дорожки,
   поэтому шаг анимации меняет только одно число и не трогает вёрстку. */
.race-track {
  display: grid;
  gap: 10px;
  width: min(100%, 460px);
}

.race-lane {
  position: relative;
  display: block;
  height: 34px;
  border: 1.5px dashed var(--layer-line);
  border-radius: 999px;
  background: var(--layer-bg);
}

.race-runner {
  position: absolute;
  top: 50%;
  left: var(--race-progress, 0%);
  font-size: 22px;
  font-style: normal;
  line-height: 1;
  transform: translate(-50%, -50%);
  transition: left 90ms linear;
}

.race-runner.is-winner {
  filter: drop-shadow(0 0 6px var(--amber));
}

/* ── Петушиный бой ───────────────────────────────────────────────────────── */

/* Побеждает тот, кого назвал сервер, — он и налетает на соперника. Летит один
   петух, второй стоит: так сразу видно, чья взяла. */
.cockfight-fight {
  display: grid;
  grid-template-columns: auto auto;
  gap: 90px;
  align-items: center;
  font-size: 46px;
}

.fighter {
  line-height: 1;
  transition: transform var(--motion-game) var(--motion-emphasized);
}

.mine {
  transform: scaleX(-1);
}

.cockfight-fight.mine-wins.is-charging .mine {
  transform: scaleX(-1) translateX(-96px) rotate(-14deg);
}

.cockfight-fight.theirs-wins.is-charging .theirs {
  transform: translateX(-96px) rotate(-14deg);
}

/* При выключенных анимациях бойцы просто стоят. */
.cockfight-fight.is-calm .fighter {
  transition: none;
}

/* ── Панели заполняют освободившееся место ──────────────────────────────── */


/* ── Тесты ───────────────────────────────────────────────────────────────── */

.quiz-tabs,
.quiz-search {
  margin-bottom: var(--space-3);
}

.quiz-search-row {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin-bottom: var(--space-3);
}

.quiz-search-row .quiz-search {
  flex: 1;
  min-width: 0;
  margin-bottom: 0;
}

.quiz-list {
  max-height: min(594px, 66vh);
}

.quiz-row-actions {
  display: flex;
  flex: none;
  gap: var(--space-2);
  align-items: center;
}

.quiz-import {
  margin-bottom: var(--space-3);
}

.quiz-hint,
.quiz-subhead {
  grid-column: 1 / -1;
  margin: 0;
  color: var(--muted);
  font-size: 12px;
  font-weight: 750;
}

/* Конструктор собран из тех же кирпичей, что и форма создания предмета:
   .create-field для полей, панельные плашки для блоков. Своей вёрстки у него
   нет — иначе он выглядит как чужая страница внутри сайта. */
.quiz-editor {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-3);
  align-items: start;
}

.quiz-editor > .segmented-control {
  grid-column: 1 / -1;
}

.quiz-editor input[type="text"],
.quiz-editor select,
.quiz-option-row input,
.quiz-result-row input {
  min-width: 0;
  min-height: var(--control-height);
  padding: 0 12px;
  border: 1.5px solid var(--layer-line);
  border-radius: var(--radius-compact);
  background: var(--layer-bg-strong);
  color: var(--text);
  font: inherit;
  font-weight: 750;
}

.quiz-editor input[type="text"]:focus-visible,
.quiz-editor select:focus-visible,
.quiz-option-row input:focus-visible,
.quiz-result-row input:focus-visible {
  border-color: var(--discord-blurple);
  outline: none;
}

.quiz-questions,
.quiz-results {
  display: grid;
  gap: var(--space-3);
}

.quiz-question,
.quiz-results {
  display: grid;
  gap: var(--space-2);
  margin: 0;
  padding: var(--space-3);
  border: 1.5px solid var(--layer-line);
  border-radius: var(--radius-control);
  background: var(--layer-bg);
}

.quiz-question legend,
.quiz-subhead {
  padding: 0;
  color: var(--muted);
  font-family: var(--font-display);
  font-size: 12px;
  font-weight: 900;
  letter-spacing: 0.02em;
  text-transform: uppercase;
}

.quiz-question legend {
  padding: 0 6px;
}

/* Ответ, его роль и кнопка удаления стоят одной строкой: так видно, какой
   вариант к чему относится, без чтения подписей. */
.quiz-option-row,
.quiz-result-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto auto;
  gap: var(--space-2);
  align-items: center;
}

.quiz-result-row {
  grid-template-columns: minmax(0, 0.8fr) minmax(0, 1.2fr) auto;
}

.quiz-correct {
  display: flex;
  gap: 6px;
  align-items: center;
  color: var(--muted);
  font-size: 12px;
  font-weight: 750;
  white-space: nowrap;
}

.quiz-question-actions,
.quiz-editor-actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
}

/* ── Прохождение теста ───────────────────────────────────────────────────── */

.quiz-play {
  display: grid;
  gap: var(--space-3);
  justify-items: center;
  text-align: center;
}

.quiz-play-head {
  display: grid;
  gap: 2px;
}

.quiz-play-head small {
  color: var(--muted);
  font-weight: 750;
}

.quiz-progress {
  width: 100%;
  height: 6px;
  overflow: hidden;
  border-radius: 999px;
  background: var(--layer-bg);
}

.quiz-progress i {
  display: block;
  width: var(--quiz-progress, 0%);
  height: 100%;
  border-radius: 999px;
  background: var(--discord-blurple);
  transition: width var(--motion-content) var(--motion-fluid);
}

.quiz-question-text {
  margin: 0;
  font-size: 18px;
  font-weight: 850;
}

.quiz-image {
  max-width: min(100%, 360px);
  max-height: 220px;
  border-radius: var(--radius-control);
  object-fit: contain;
}

.quiz-options {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: var(--space-2);
  width: 100%;
}

.quiz-option {
  display: grid;
  grid-auto-rows: min-content;
  gap: 8px;
  place-content: center;
  justify-items: center;
  min-height: var(--control-height);
  padding: 12px 14px;
  text-align: center;
  border: 1.5px solid var(--layer-line);
  border-radius: var(--radius-control);
  background: var(--layer-bg);
  color: var(--text);
  font: inherit;
  font-weight: 800;
  cursor: pointer;
}

.quiz-option img {
  max-width: 100%;
  max-height: 130px;
  border-radius: var(--radius-compact);
  object-fit: contain;
}

.quiz-option:hover:not(:disabled) {
  border-color: var(--discord-blurple);
}

/* Верный вариант зелёный, выбранный неверный — красный. Оба видны разом,
   поэтому и понятно, где была ошибка. */
.quiz-option.is-correct {
  border-color: var(--success, #22c55e);
  background: color-mix(in srgb, var(--success, #22c55e) 18%, var(--layer-bg));
}

.quiz-option.is-wrong {
  border-color: var(--accent);
  background: color-mix(in srgb, var(--accent) 18%, var(--layer-bg));
}

.quiz-score {
  font-size: 26px;
}

/* ── Каталоги в админке ──────────────────────────────────────────────────── */

.admin-catalog {
  max-height: min(460px, 52vh);
}

.admin-catalog-row {
  align-items: center;
}

.admin-catalog-edit {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-3);
  padding: var(--space-3);
  border: 1.5px solid var(--discord-blurple);
  border-radius: var(--radius-control);
  background: var(--layer-bg);
}

@media (max-width: 760px) {
  .quiz-editor,
  .admin-catalog-edit {
    grid-template-columns: 1fr;
  }

  .quiz-option-row,
  .quiz-result-row {
    grid-template-columns: minmax(0, 1fr) auto;
  }

  .quiz-list {
    max-height: 48vh;
  }
}

/* ── Кнопка анимаций ─────────────────────────────────────────────────────── */

/* Значок — кардиограмма: линия скачет, когда движение включено, и перечёркнута,
   когда выключено. Кнопка стоит слева от темы и повторяет её размеры. */
.motion-button {
  color: var(--text);
}

.motion-slash {
  opacity: 0;
  transition: opacity var(--motion-quick) var(--motion-standard);
}

.motion-button[aria-pressed="false"] {
  color: var(--muted);
}

.motion-button[aria-pressed="false"] .motion-slash {
  opacity: 1;
}

/* ── Уведомления и создание предмета одной длины ─────────────────────────── */

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


/* Панели в стопке заполняют пустоту над собой сами: свернул верхнюю — нижняя
   поднялась. Ряд из двух панелей выравнивается по верху, поэтому свёрнутая
   не растягивается под рост соседки. */
.dashboard-columns {
  align-items: start;
}

/* Пара «Уведомления»/«Создать предмет» читается как одно целое и заканчивается
   на одной высоте — но только пока обе развёрнуты. */
.dashboard-pair {
  align-items: stretch;
}

.dashboard-pair .panel.is-collapsed {
  align-self: start;
}

/* ── Конструктор: понятные подписи вместо значков ────────────────────────── */

/* Радиокнопка «верный» подписана словами и меняет вид, когда выбрана: голый
   кружок рядом с полем ввода читался как непонятная деталь. */
.quiz-correct,
.quiz-score-pick {
  display: flex;
  gap: 8px;
  align-items: center;
  justify-content: center;
  box-sizing: border-box;
  min-width: 150px;
  height: var(--control-height);
  padding: 0 12px;
  border: 1.5px solid var(--layer-line);
  border-radius: var(--radius-compact);
  background: var(--layer-bg);
  color: var(--muted);
  font-size: 12px;
  font-weight: 800;
  white-space: nowrap;
  cursor: pointer;
}

/* Родная радиокнопка рисовалась браузером как вытянутый овал и ломала строку.
   Кружок рисуем сами, а сам input оставляем невидимым для мыши и живым для
   клавиатуры. */
.quiz-correct input[type="radio"] {
  position: absolute;
  width: 1px;
  height: 1px;
  opacity: 0;
  pointer-events: none;
}

.quiz-correct::before {
  content: "";
  flex: none;
  box-sizing: border-box;
  width: 16px;
  height: 16px;
  border: 2px solid var(--layer-line);
  border-radius: 50%;
}

.quiz-correct.is-picked {
  border-color: var(--success, #22c55e);
  color: var(--text);
}

.quiz-correct.is-picked::before {
  border-color: var(--success, #22c55e);
  background: var(--success, #22c55e);
  box-shadow: inset 0 0 0 3px var(--layer-bg);
}

.quiz-score-pick select {
  min-height: 30px;
  padding: 0 8px;
}

/* Кнопки удаления отличаются цветом: их легко нажать по ошибке. */
.compact-button.is-remove {
  border-color: color-mix(in srgb, var(--accent) 45%, var(--layer-line));
  color: var(--accent);
}

.quiz-option-row > *,
.quiz-result-row > * {
  height: var(--control-height);
}

.quiz-option-row {
  grid-template-columns: minmax(0, 1fr) auto auto;
}

.quiz-result-row {
  grid-template-columns: minmax(0, 0.8fr) minmax(0, 1.2fr) auto;
}

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


/* ── Пиксельбатл: масштаб и панорама ─────────────────────────────────────── */

/* Поле живёт в окне со своими границами: приближённый холст выходит за них и
   обрезается, как на карте, а не растягивает панель. */
.pixel-viewport {
  position: relative;
  overflow: hidden;
  border-radius: var(--radius-control);
  touch-action: none;
}

/* Обёртка больше ничего не масштабирует: приближение и сдвиг живут внутри
   холста, поэтому линии и клетки при любом увеличении остаются чёткими. */
.pixel-canvas-wrap {
  position: relative;
  width: 100%;
  height: 100%;
}

/* Приближённое поле таскают мышью — курсор об этом и говорит. */
.pixel-viewport:has(.pixel-canvas-wrap[style*="--pixel-zoom: 1;"]) canvas {
  cursor: crosshair;
}

.pixel-zoom-controls {
  position: absolute;
  right: 10px;
  bottom: 10px;
  display: grid;
  gap: 6px;
}

.pixel-zoom-controls .compact-button {
  width: 34px;
  min-width: 34px;
  height: 34px;
  padding: 0;
  background: color-mix(in srgb, var(--surface-plain) 88%, transparent);
  font-size: 16px;
}

/* Табличка в углу поля, как на пиксельбатле реддита: координаты клетки под
   курсором и её автор. Стоит на месте и видна всегда — так за ней не надо
   следить глазами по всему полю. */
.pixel-hint {
  position: absolute;
  top: 10px;
  left: 10px;
  z-index: 2;
  display: grid;
  gap: 1px;
  padding: 5px 10px;
  border: 1.5px solid var(--layer-line);
  border-radius: var(--radius-compact);
  background: color-mix(in srgb, var(--surface-plain) 92%, transparent);
  color: var(--text);
  font-size: 12px;
  font-weight: 800;
  font-variant-numeric: tabular-nums;
  pointer-events: none;
}

.pixel-hint strong {
  font-size: 13px;
}

.pixel-hint small {
  color: var(--muted);
  font-size: 11px;
  font-weight: 750;
}

/* ── Пиксели одного участника в админке ──────────────────────────────────── */

.pixel-author-row {
  align-items: center;
}

/* ── Поток панелей: пустота заполняется тем, что идёт ниже ───────────────── */

/* Панели текут колонками, а не стоят в строках сетки. Порядок в разметке тот
   же, что был, поэтому расположение не меняется: первая колонка набирается
   сверху вниз, потом вторая. Разница в том, что свёрнутая панель отдаёт своё
   место следующей, а не оставляет дыру до конца строки. */
/* В потоке высоту задаёт содержимое, а не строка сетки. Прежние правила пары
   растягивали панели под соседку и мешали заполнять пустоту. */
.panel-flow .panel {
  height: auto;
}

.panel-flow .operations-panel:not(.is-collapsed) {
  height: min(610px, 72vh);
}

.panel-flow .inventory-panel:not(.is-collapsed) {
  max-height: min(610px, 72vh);
}

/* ── Рулетка: ставка на своё число ───────────────────────────────────────── */

/* Отдельная строка во всю ширину под фишками: число от нуля до тридцати шести
   вводится руками, а не выбирается из списка на тридцать семь кнопок. */
/* Число стоит в том же ряду, что и остальные ставки, и выглядит такой же
   плашкой — просто в неё можно писать. Переключатель спрятан: его роль
   выполняет само введённое число. */
.roulette-number-bet {
  display: flex;
  width: 92px;
}

.roulette-number-bet input[type="radio"] {
  position: absolute;
  width: 1px;
  height: 1px;
  opacity: 0;
  pointer-events: none;
}

/* Общее правило ряда прячет любой input внутри — оно рассчитано на скрытые
   переключатели. Полю ввода видимость возвращаем обратно. */
.choice-chips .roulette-number-bet input[type="number"] {
  position: static;
  opacity: 1;
  pointer-events: auto;
  width: 100%;
  min-width: 0;
  min-height: var(--control-height);
  padding: 0 10px;
  border: 1.5px solid var(--layer-line);
  border-radius: var(--radius-compact);
  background: var(--layer-bg);
  color: var(--text);
  font: inherit;
  font-size: 13px;
  font-weight: 800;
  text-align: center;
}

.roulette-number-bet input[type="radio"]:checked + input[type="number"] {
  border-color: var(--discord-blurple);
}

/* Кнопка «Пройти» стоит последней и одинаковой ширины во всех строках —
   так они выстраиваются в ровный столбец у правого края. */
.quiz-row-actions {
  margin-left: auto;
}

.quiz-row-actions .quiz-start {
  min-width: 104px;
  justify-content: center;
}

/* Подпись о клетке под курсором, продублированная строкой в панели: плавающая
   подсказка может уехать за край поля, а эта видна всегда. */
.pixel-hover-line {
  min-height: 18px;
  color: var(--muted);
  font-size: 12px;
  font-weight: 800;
  font-variant-numeric: tabular-nums;
  overflow-wrap: anywhere;
}

/* ── Роли и права: колонка ростом с «Новости» ────────────────────────────── */

/* ── Две колонки личного кабинета ────────────────────────────────────────── */

/* Половины живут отдельно: панель закреплена за своей колонкой и не уходит к
   соседке, когда что-то свернули. Раньше панели текли общим потоком, и
   свёрнутые «Новости» утаскивали сохранёнки на другую сторону.
   На узком экране колонка одна, панели идут подряд. */
#dashboard-panels {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  align-items: start;
  gap: var(--space-4);
}

#dashboard-panels > .panel-column {
  display: grid;
  align-content: start;
  gap: var(--space-4);
  min-width: 0;
}

@media (max-width: 900px) {
  #dashboard-panels {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* ── Новости, роли и права: одинаковая невысокая мера ────────────────────── */

/* Три панели одного ряда читаются как набор, поэтому и списки внутри них
   ограничены одинаково: длинное содержимое прокручивается, а не растит панель. */
.news-list,
.role-cloud-scroll,
.permission-grid {
  max-height: 360px;
  overflow-y: auto;
  overscroll-behavior: contain;
  scrollbar-width: thin;
}

/* ── Полотно на весь экран ───────────────────────────────────────────────── */

/* Панель занимает треть страницы, и разглядывать чужие рисунки в ней неудобно:
   увеличение упирается в её размеры. Развёрнутое полотно ложится поверх всего
   и получает столько места, сколько есть у экрана. */
/* Анимация появления страницы оставляет на ней transform, а он делает страницу
   точкой отсчёта для position: fixed — развёрнутое полотно раскрывалось не на
   экран, а на страницу. Пока полотно открыто, transform странице не нужен. */
/* ── Страница полотна ────────────────────────────────────────────────────── */

/* Полотно живёт отдельной страницей, а не поверх кабинета: внутри панели его
   перекрывала шапка, и поднять его слоем было нельзя — шапка рисуется в корне
   страницы, а полотно внутри неё. */
.pixel-page {
  display: grid;
  gap: var(--space-3);
  align-content: start;
}

/* Ширину ограничиваем по доступной высоте, а не режем max-height: при
   обрезке высоты ширина оставалась стопроцентной, соотношение ломалось — и
   внутри холста по бокам появлялись пустые полосы, где клик ни во что не
   попадал. Размер поля берём из данных: зашитые 96/64 разошлись с
   настоящими 192×128 и врали бы при любой смене размера. */
.pixel-viewport.is-page {
  --pixel-room: calc(100vh - 180px);
  width: min(100%, calc(var(--pixel-room) * var(--поле-ш, 96) / var(--поле-в, 64)));
  aspect-ratio: var(--поле-ш, 96) / var(--поле-в, 64);
  height: auto;
  max-height: none;
  margin-inline: auto;
  border: 1.5px solid var(--line);
}

/* Панель инструментов идёт одной полосой во всю ширину: разбивка на колонки
   сжимала ряд с палитрой до трети экрана, и семнадцать плашек не помещались
   в строку даже на большом мониторе. */
.pixel-page .pixel-tools,
.pixel-panel .pixel-tools {
  grid-template-columns: minmax(0, 1fr);
}

/* Кнопки на полотне: на телефоне мельче, чтобы не закрывать сам рисунок. */
@media (max-width: 760px) {
  .pixel-zoom-controls {
    right: 6px;
    bottom: 6px;
    gap: 4px;
  }

  .pixel-zoom-controls .compact-button {
    width: 28px;
    min-width: 28px;
    height: 28px;
    font-size: 13px;
  }

  .pixel-hint {
    top: 6px;
    left: 6px;
    padding: 3px 7px;
  }

  .pixel-hint strong {
    font-size: 12px;
  }
}

/* ── Края гаснут только там, где есть что прокручивать ───────────────────── */

/* Оба края ведут себя одинаково: верхний появляется, когда список уже сдвинут,
   нижний — пока внизу что-то осталось. Домотал до конца — угасания снизу нет,
   как и сверху в самом начале. У списка, который помещается целиком, краёв не
   появляется вовсе. Переходов нет: это не анимация, а состояние прокрутки. */
.is-scrolled-top {
  --fade-top: 14px;
}

.is-scrolled-bottom {
  --fade-bottom: 18px;
}

.is-scrolled-left {
  --fade-left: 18px;
}

.is-scrolled-right {
  --fade-right: 18px;
}

/* Стрелки у полосы прокрутки уезжали под то же угасание и выглядели обрубками.
   Полоса и без них понятна. Чтобы правило подействовало, у полосы должен быть
   задан и собственный размер: иначе браузер оставляет системный вид целиком. */
*::-webkit-scrollbar {
  width: 8px;
  height: 8px;
}

*::-webkit-scrollbar-button,
*::-webkit-scrollbar-button:start,
*::-webkit-scrollbar-button:end,
*::-webkit-scrollbar-corner {
  display: none;
  width: 0;
  height: 0;
}

*::-webkit-scrollbar-track {
  background: transparent;
}


/* ── Управление полотном одной строкой ───────────────────────────────────── */

/* Палитра, свой цвет и тумблеры стоят в одном ряду под холстом: раньше цвета
   занимали сетку, а тумблеры уезжали ниже — на телефоне до них надо было
   долистывать. */
.pixel-controls {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
  align-items: center;
  justify-content: space-between;
}

/* Палитра — ряд квадратов. Раньше она была сеткой в четыре колонки, и на
   узком экране плашки растягивались в прямоугольники. */
.pixel-controls .pixel-palette {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  align-items: center;
  justify-content: flex-start;
}

.pixel-controls .pixel-palette button {
  flex: 0 0 auto;
  box-sizing: border-box;
  width: var(--swatch, 28px);
  height: var(--swatch, 28px);
  min-width: 0;
  min-height: 0;
  padding: 0;
  aspect-ratio: 1;
}

/* Свой цвет подписан и стоит отдельной плашкой: без подписи было непонятно,
   что этот квадрат вообще делает. */
.pixel-own-color {
  display: flex;
  flex: 0 0 auto;
  gap: 8px;
  align-items: center;
  color: var(--muted);
  font-size: 12px;
  font-weight: 800;
  white-space: nowrap;
  cursor: pointer;
}

.pixel-own-color i {
  display: block;
  flex: 0 0 auto;
  width: var(--swatch, 28px);
  height: var(--swatch, 28px);
  overflow: hidden;
  border: 1.5px dashed var(--layer-line);
  border-radius: var(--radius-compact);
}

.pixel-own-color input[type="color"] {
  display: block;
  width: 200%;
  height: 200%;
  margin: -25%;
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
}

.pixel-switches {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
  align-items: center;
}

.pixel-status {
  display: flex;
  gap: var(--space-3);
  align-items: center;
  min-height: 26px;
}

/* Сообщение стоит в одной строке со счётчиком: отдельной строкой под ним оно
   при появлении сдвигало всё полотно вверх-вниз. Длинный текст ужимается. */
.pixel-status .inline-status {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.pixel-status .inline-status.is-empty {
  display: none;
}

/* Размер плашки подбирается под ширину: на большом экране все семнадцать
   помещаются одной строкой, на телефоне переносятся в две-три. */
.pixel-controls {
  --swatch: 28px;
}

@media (max-width: 980px) {
  .pixel-controls {
    --swatch: 26px;
  }
}

@media (max-width: 560px) {
  .pixel-controls {
    --swatch: 24px;
  }
}

/* Кнопки масштаба тоже квадратные: без явной ширины они растягивались по
   содержимому и выходили прямоугольниками. */
.pixel-zoom-controls .compact-button {
  flex: 0 0 auto;
  box-sizing: border-box;
  width: 32px;
  min-width: 32px;
  height: 32px;
  min-height: 0;
  padding: 0;
  aspect-ratio: 1;
}

/* ── Одинаковые поля у всех панелей ──────────────────────────────────────── */

/* Расстояние от края панели до содержимого задаёт только сама панель. Списки
   раньше добавляли своё несимметричное поле (слева два пикселя, справа восемь),
   и в соседних панелях содержимое стояло по-разному.

   Место под полосу прокрутки резервируется отдельно: в macOS полоса рисуется
   поверх содержимого и наезжала на строки, а в Windows занимала место — из-за
   этого один и тот же список выглядел на двух системах по-разному. */
.news-list,
.role-cloud-scroll,
.permission-grid,
.notification-list,
.economy-scroll,
.family-roster,
.leaderboard-list,
.people-list,
.operation-list,
.inventory-list,
.admin-queue,
.admin-birthday-list,
.quiz-list,
.admin-catalog {
  padding: 0;
  scrollbar-gutter: stable;
}

/* Полоса всегда своя и всегда занимает место, а не всплывает поверх строк. */
.news-list::-webkit-scrollbar,
.role-cloud-scroll::-webkit-scrollbar,
.permission-grid::-webkit-scrollbar,
.notification-list::-webkit-scrollbar,
.economy-scroll::-webkit-scrollbar,
.family-roster::-webkit-scrollbar,
.leaderboard-list::-webkit-scrollbar,
.people-list::-webkit-scrollbar,
.operation-list::-webkit-scrollbar,
.inventory-list::-webkit-scrollbar,
.admin-queue::-webkit-scrollbar,
.admin-birthday-list::-webkit-scrollbar,
.quiz-list::-webkit-scrollbar,
.admin-catalog::-webkit-scrollbar {
  width: 8px;
  background: transparent;
}

/* Стрелка выпадающего меню стоит по центру своей строки. Раньше её рисовал
   пустой элемент без выравнивания, и треугольник прижимался к верху. */
.choice-menu summary i {
  display: flex;
  flex: 0 0 auto;
  align-items: center;
  justify-content: center;
  width: 16px;
  height: 100%;
  min-height: 20px;
  color: var(--muted);
}

/* ── Окно полотна ────────────────────────────────────────────────────────── */

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

   Ширина ограничена так, чтобы поле влезало и по высоте — в горизонтальном
   положении телефона оно не уходит за экран. */
.pixel-viewport {
  --pixel-room: min(68vh, 640px);
  width: min(100%, calc(var(--pixel-room) * var(--поле-ш, 96) / var(--поле-в, 64)));
  aspect-ratio: var(--поле-ш, 96) / var(--поле-в, 64);
  height: auto;
  margin-inline: auto;
}

.pixel-viewport.is-page {
  --pixel-room: calc(100vh - 190px);
}

@media (max-width: 980px) {
  .pixel-viewport {
    --pixel-room: min(56vh, 520px);
  }

  .pixel-viewport.is-page {
    --pixel-room: calc(100vh - 210px);
  }
}

/* Горизонтальное положение телефона: экран низкий, под полотно отдаётся почти
   всё, а управление ужимается. */
@media (max-height: 560px) and (orientation: landscape) {
  .pixel-viewport,
  .pixel-viewport.is-page {
    --pixel-room: calc(100vh - 140px);
  }

  .pixel-controls {
    --swatch: 22px;
    gap: var(--space-2);
  }
}

/* ── Смахивание шапки ────────────────────────────────────────────────────── */

/* Шапка убирается уверенной прокруткой вниз и возвращается движением вверх.
   Visibility выключает отрисовку только после затухания, не меняя геометрию
   страницы. Display здесь использовать нельзя: его переключение вместе с
   opacity давало Safari и слабым устройствам промежуточный мигающий кадр. */
.topbar {
  opacity: 1;
  visibility: visible;
  transition:
    opacity var(--motion-bar) var(--motion-fluid),
    visibility 0s linear 0s;
}

:root.topbar-hidden .topbar {
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition:
    opacity var(--motion-bar) var(--motion-fluid),
    visibility 0s linear var(--motion-bar);
}

:root[data-motion="off"] .topbar,
:root[data-motion="off"].topbar-hidden .topbar {
  transition: none;
}

:root.bare-page-active .topbar {
  display: none;
}

/* ── Одинаковые отступы у всех панелей ───────────────────────────────────── */

/* Панель-сетка добавляет свой промежуток к отступу заголовка, и под ним
   выходит вдвое больше, чем у соседей. Отступ задаёт только заголовок. */
.economy-hub {
  gap: 0;
}

.economy-hub > .panel-header {
  margin-bottom: 14px;
}

/* Инвентарь держал пятьсот пикселей высоты независимо от содержимого: при
   коротком списке снизу оставалась пустая полоса в четыре раза шире, чем поле
   у остальных панелей. Высоту задаёт список, предел остаётся. */
.inventory-panel,
.operations-panel {
  min-height: 0;
}

.panel-flow .operations-panel:not(.is-collapsed) {
  height: auto;
}

/* Список операций прокручивается внутри панели, а не растит её на всю историю:
   без предела панель вытягивалась на все полторы тысячи записей. */
.panel-flow .operations-panel:not(.is-collapsed) .operation-list,
.operations-panel .operation-list {
  max-height: min(560px, 64vh);
}

/* Именинники прокручиваются: карточка держит высоту двух строк, сколько бы их
   ни было, и соседние карточки ряда не разъезжаются. Края гаснут по тем же
   правилам, что у остальных списков: сверху — когда список сдвинут, снизу —
   пока внизу кто-то ещё остался. */
.current-activity-scroll {
  max-height: 104px;
  overflow-y: auto;
  /* Без scrollbar-gutter: он резервировал место справа, и содержимое списков
     не доходило до края, а кнопки соседних карточек доходили. Полоса рисуется
     поверх — ряды в карточках теперь одной ширины. */
  scrollbar-width: thin;
  mask-image: linear-gradient(
    to bottom,
    transparent 0,
    #000 var(--fade-top, 0px),
    #000 calc(100% - var(--fade-bottom, 0px)),
    transparent 100%
  );
}

.current-activity-scroll::-webkit-scrollbar {
  width: 8px;
  background: transparent;
}

/* ── Низ панелей с кнопкой в конце ───────────────────────────────────────── */

/* Поле под строку ошибки нужно только тем полям, у которых эта строка есть.
   Последний ряд формы получал его просто так, и под кнопкой отправки
   оставалась лишняя полоса. */
.create-field {
  padding-bottom: 0;
}

.create-field:has(.field-limit) {
  padding-bottom: 18px;
}

/* Строка со стоимостью появляется только вместе с ценой и тиражом: место под
   неё держать незачем, кнопка и так последняя. */
.create-fee:empty {
  display: none;
}

/* У панели игр снизу оставался отступ формы поверх поля самой панели. */
.casino-board {
  margin-bottom: 0;
}

.casino-board > .casino-play {
  margin-bottom: 0;
}

/* ── Магазин и рынок ─────────────────────────────────────────────────────── */

.economy-subtabs {
  margin-bottom: var(--space-3);
}

.shop-actions {
  grid-template-columns: 72px auto;
}

.market-actions {
  grid-template-columns: auto;
}

/* Форма лота: предмет тянется, цена и количество узкие, кнопка по краю. */
.market-sell {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) 84px auto;
  align-items: end;
  gap: var(--space-2);
  margin-bottom: var(--space-3);
  padding: var(--space-3);
  border: 1.5px solid var(--layer-line);
  border-radius: var(--radius-control);
  background: var(--layer-bg);
}

.market-sell label {
  display: grid;
  gap: var(--space-1);
  min-width: 0;
}

.market-sell label > span {
  color: var(--muted);
  font-size: 12px;
  font-weight: 800;
}

.market-sell input,
.market-sell select {
  min-width: 0;
  min-height: var(--control-height);
  padding: 0 12px;
  border: 1.5px solid var(--layer-line);
  border-radius: var(--radius-compact);
  background: var(--layer-bg-strong);
  color: var(--text);
  font: inherit;
}

@media (max-width: 720px) {
  .market-sell {
    grid-template-columns: minmax(0, 1fr) minmax(74px, 0.62fr) auto;
  }

  .market-sell-item {
    grid-column: 1 / -1;
  }

  .market-sell button {
    grid-column: auto;
    min-height: var(--control-height);
    align-self: end;
  }
}

/* ── Реакции под новостью ────────────────────────────────────────────────── */

.news-reactions {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-top: 9px;
}

.news-reaction {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 3px 8px;
  border: 1.5px solid var(--layer-line);
  border-radius: 999px;
  background: var(--layer-bg-strong);
  font-family: "Twemoji", "Apple Color Emoji", "Segoe UI Emoji", sans-serif;
  font-size: 13px;
  line-height: 1.2;
}

.news-reaction img {
  display: block;
  width: 18px;
  height: 18px;
}

/* Счётчик набирается нашим шрифтом, а не эмодзийным: в Apple Color Emoji у
   цифр своя ширина и свои просветы, и «12» читалось как «1 2». */
.news-reaction b {
  color: var(--muted);
  font-family: var(--font-ui);
  font-size: 12px;
  font-weight: 800;
  font-variant-numeric: tabular-nums;
}

/* ── Настройки ───────────────────────────────────────────────────────────── */

.settings-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: var(--space-3);
}

.settings-card {
  display: grid;
  align-content: start;
  gap: var(--space-2);
  min-width: 0;
}

.settings-card h3 {
  font-size: 14px;
}

/* Внутри карточки рамка уже своя — вложенная выглядела бы коробкой в коробке. */
.settings-card .birthday-control,
.settings-card .notification-row {
  margin-top: 0;
}

/* ── Сохранёнки ──────────────────────────────────────────────────────────── */

.saved-list {
  display: grid;
  gap: var(--space-2);
  margin-top: var(--space-3);
  max-height: min(560px, 64vh);
  overflow-y: auto;
  overscroll-behavior: contain;
  scrollbar-gutter: stable;
  scrollbar-width: thin;
  mask-image: linear-gradient(
    to bottom,
    transparent 0,
    #000 var(--fade-top, 0px),
    #000 calc(100% - var(--fade-bottom, 0px)),
    transparent 100%
  );
}

.saved-item {
  display: grid;
  gap: var(--space-2);
  padding: var(--space-3);
  border: 1.5px solid var(--layer-line);
  border-radius: var(--radius-control);
  background: var(--layer-bg);
}

.saved-head {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: center;
  gap: var(--space-2);
}

.saved-avatar {
  width: 28px;
  height: 28px;
  border-radius: 50%;
}

.saved-meta {
  display: grid;
  gap: 2px;
  min-width: 0;
}

.saved-meta strong,
.saved-meta small {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.saved-meta small {
  color: var(--muted);
  font-size: 12px;
  font-weight: 700;
}

.saved-text {
  display: -webkit-box;
  overflow: hidden;
  line-height: 1.5;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 6;
}

.saved-image img,
.saved-image video {
  display: block;
  width: 100%;
  max-height: 220px;
  border-radius: var(--radius-compact);
  object-fit: cover;
}

.saved-remove {
  color: var(--muted);
}

/* ── Кнопки говорят одним шрифтом ────────────────────────────────────────── */

/* Часть кнопок наследовала текстовый шрифт через `font: inherit`, и рядом с
   заголовочным он читался как чужой — особенно «Виден» рядом с «Изменить».
   Здесь и семейство, и кегль сведены к одному виду. */
.compact-button,
.inventory-toggle,
.primary-button,
.secondary-button,
.danger-button,
.icon-button,
.notification-toggle,
.profile-menu-logout,
.segmented-control button,
.choice-chips span {
  font-family: var(--font-display);
  font-size: 14px;
  font-weight: 800;
}

/* ── Настройки: день рождения, приватность, уведомления ──────────────────── */

.settings-notifications {
  margin-top: var(--space-3);
}

.settings-notifications h3 {
  margin-bottom: var(--space-2);
  font-size: 14px;
}

/* Панель во всю ширину, поэтому категории идут в две колонки: одной строкой
   на всю ширину получалась бы дорожка из пустоты между текстом и кнопкой. */
.settings-notification-list {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
  gap: var(--space-2);
  max-height: none;
  overflow: visible;
  mask-image: none;
}

/* Кнопка даты — такая же узкая, как переключатель рейтингов. */
.birthday-control .notification-toggle {
  min-width: 116px;
}

.settings-card .birthday-status {
  margin-top: 0;
}

/* ── Строка эмодзи и названия в создании предмета ────────────────────────── */

/* Эмодзи ровно одно — широкое поле под него выглядело ошибкой, а названию
   места не хватало. Заодно поля перестали растягиваться по высоте соседа:
   у названия под ним живёт строка ошибки, и она тянула эмодзи вниз. */
.create-row-head {
  display: grid;
  grid-template-columns: 92px minmax(0, 1fr);
  gap: var(--space-3);
  align-items: start;
}

.create-field {
  align-content: start;
}

/* ── Строка дня рождения ─────────────────────────────────────────────────── */

/* Карточка живёт рядом со строкой уведомлений и должна выглядеть так же:
   раньше у неё были своя рамка потолще и закругление покрупнее. */
.settings-card .birthday-control,
.birthday-nudge {
  padding: var(--space-2) var(--space-3);
  border-width: 1px;
  border-radius: var(--radius-compact);
  box-shadow: none;
}

/* Ввод даты: слева поле, справа кнопки — строка не растёт вниз. */
.birthday-control.is-editing {
  grid-template-columns: auto minmax(0, 1fr);
}

.birthday-form {
  grid-column: auto;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
}

.birthday-form input {
  flex: 1 1 120px;
  min-width: 0;
}

.birthday-form .notification-toggle {
  min-width: 104px;
}

.birthday-delete {
  color: color-mix(in srgb, var(--accent) 70%, var(--text));
}

/* ── Кастом-эмодзи в тексте ──────────────────────────────────────────────── */

/* Картинка эмодзи приходит размером 64px — без размера по кеглю она была
   больше самой строки. */
.discord-emoji {
  width: 1.15em;
  height: 1.15em;
  vertical-align: -0.18em;
  object-fit: contain;
}

/* ── Пустая карточка розыгрышей ──────────────────────────────────────────── */

/* Рядом стоит кнопка «Получить личное задание», и подпись должна попадать
   с ней в одну линию — иначе карточки выглядят разной высоты. */
.activity-note {
  display: grid;
  place-items: center;
  padding: 0 18px;
  border: 1.5px solid transparent;
  border-radius: var(--radius-compact);
  background: var(--layer-bg-strong);
  color: var(--muted);
  font-size: 14px;
  font-weight: 800;
  text-align: center;
}

/* На телефоне поле занимает строку целиком, а кнопки делят следующую поровну:
   в одну строку они там не помещаются и разъезжались по одной. */
@media (max-width: 620px) {
  .birthday-form {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .birthday-form input {
    grid-column: 1 / -1;
  }

  /* Переключатели уведомлений на телефоне занимают строку целиком — здесь
     это кнопки формы, и растягиваться им не нужно. */
  .birthday-form .notification-toggle {
    min-width: 0;
    grid-column: auto;
  }

  .birthday-form .birthday-delete {
    grid-column: 1 / -1;
  }
}

/* ── Вкладки поиска людей ────────────────────────────────────────────────── */

/* Пять кнопок не помещались в ряд буквально на несколько пикселей и
   сваливались во вторую строку. Теперь они делят ширину поровну: пока
   колонка шире сотни пикселей, ряд остаётся одним, а на узкой панели
   аккуратно распадается на две строки одинаковых кнопок — вместо обрезанных
   подписей. */
.people-tabs {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(100px, 1fr));
  gap: 8px;
}

.people-tabs button {
  min-width: 0;
  padding: 9px 10px;
  font-size: 13px;
  white-space: nowrap;
}

/* Кнопка сохранения образа стоит под обеими колонками редактора: раньше она
   заканчивала правый столбец и оказывалась выше нижнего края превью. */
.character-actions {
  grid-column: 1 / -1;
  display: grid;
  gap: var(--space-2);
}

/* ── Админка: сводка, аналитика сайта, логи ──────────────────────────────── */

.admin-metrics {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
  gap: var(--space-2);
  margin-top: var(--space-3);
}

.admin-metric {
  display: grid;
  align-content: start;
  gap: 3px;
  padding: var(--space-3);
  border: 1.5px solid var(--layer-line);
  border-radius: var(--radius-control);
  background: var(--layer-bg);
}

.admin-metric small,
.admin-metric em {
  color: var(--muted);
  font-size: 11px;
  font-style: normal;
  font-weight: 750;
}

/* Кегль задаёт .money-figure — он считает его от ширины карточки. Прежний
   фиксированный размер перебивал этот расчёт и число вылезало за рамку. */
.admin-metric strong {
  line-height: 1.2;
}

/* Полоски долей: сравнение на глаз важнее точных чисел, поэтому длина
   считается от самой посещаемой строки. */
.admin-bars {
  display: grid;
  gap: var(--space-2);
  margin-top: var(--space-2);
}

.admin-bar {
  display: grid;
  gap: 5px;
}

.admin-bar-copy {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-2);
  min-width: 0;
}

.admin-bar-copy strong {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.admin-bar-copy small {
  flex: none;
  color: var(--muted);
  font-size: 12px;
  font-weight: 750;
}

.admin-bar-track {
  display: block;
  overflow: hidden;
  height: 8px;
  border-radius: 999px;
  background: var(--layer-bg-strong);
}

.admin-bar-track i {
  display: block;
  height: 100%;
  border-radius: 999px;
  background: color-mix(in srgb, var(--discord-blurple) 62%, var(--layer-line));
}

.admin-logs-head {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  gap: var(--space-2);
  margin-top: var(--space-3);
}

.admin-logs {
  display: grid;
  gap: var(--space-2);
  max-height: min(620px, 70vh);
  margin-top: var(--space-3);
  overflow-y: auto;
  overscroll-behavior: contain;
  scrollbar-gutter: stable;
  scrollbar-width: thin;
  mask-image: linear-gradient(
    to bottom,
    transparent 0,
    #000 var(--fade-top, 0px),
    #000 calc(100% - var(--fade-bottom, 0px)),
    transparent 100%
  );
}

.admin-log {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: var(--space-2);
  padding: var(--space-3);
  border: 1.5px solid var(--layer-line);
  border-radius: var(--radius-control);
  background: var(--layer-bg);
}

.admin-log-avatar {
  width: 26px;
  height: 26px;
  border-radius: 50%;
}

.admin-log-body {
  display: grid;
  gap: 5px;
  min-width: 0;
}

.admin-log-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--space-2);
}

.admin-log-head small {
  color: var(--muted);
  font-size: 12px;
  font-weight: 750;
}

.admin-log-head .text-link {
  margin: 0 0 0 auto;
}

.admin-log-bot {
  padding: 1px 6px;
  border-radius: 999px;
  background: color-mix(in srgb, var(--discord-blurple) 22%, var(--layer-bg-strong));
  color: var(--muted);
  font-size: 10px;
  font-style: normal;
  font-weight: 800;
  text-transform: uppercase;
}

.admin-log-body p {
  overflow-wrap: anywhere;
  line-height: 1.45;
}

/* Эмбед лога повторяет полоску Discord слева — по ней видно, где кончается
   один лог и начинается другой. */
.admin-log-embed {
  display: grid;
  gap: 4px;
  padding: var(--space-2) var(--space-3);
  border-left: 3px solid var(--embed-color, var(--layer-line));
  border-radius: var(--radius-compact);
  background: var(--layer-bg-strong);
  font-size: 13px;
}

.admin-log-embed img {
  max-width: 100%;
  border-radius: var(--radius-compact);
}

.admin-log-embed small {
  color: var(--muted);
  font-size: 11px;
  font-weight: 750;
}

.admin-log-field b {
  color: var(--muted);
}

@media (max-width: 620px) {
  .admin-logs-head {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* Числа в админских карточках считают кегль от ширины самой карточки: казна
   в миллиардах вылезала за рамку. */
.admin-metric {
  container-type: inline-size;
  --figure-size: 20px;
}

/* ── Досье участников ────────────────────────────────────────────────────── */

.admin-members-layout {
  display: grid;
  grid-template-columns: minmax(240px, 320px) minmax(0, 1fr);
  gap: var(--space-3);
  margin-top: var(--space-3);
  align-items: start;
}

.admin-members-side {
  display: grid;
  gap: var(--space-2);
  min-width: 0;
}

.admin-member-list {
  display: grid;
  gap: 6px;
  max-height: min(560px, 62vh);
  overflow-y: auto;
  overscroll-behavior: contain;
  scrollbar-gutter: stable;
  scrollbar-width: thin;
  mask-image: linear-gradient(
    to bottom,
    transparent 0,
    #000 var(--fade-top, 0px),
    #000 calc(100% - var(--fade-bottom, 0px)),
    transparent 100%
  );
}

.admin-member-row {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  align-items: center;
  gap: var(--space-2);
  min-height: 48px;
  padding: 7px 10px;
  border: 1.5px solid var(--layer-line);
  border-radius: var(--radius-compact);
  background: var(--layer-bg);
  text-align: left;
  cursor: pointer;
}

.admin-member-row.is-active {
  border-color: color-mix(in srgb, var(--discord-blurple) 50%, var(--layer-line));
  background: color-mix(in srgb, var(--discord-blurple) 12%, var(--layer-bg));
}

/* Размер задан стилем, а не атрибутами width/height в разметке: атрибуты —
   всего лишь подсказка с самым низким приоритетом, и стоило чему-нибудь
   тронуть ширину картинок, как аватарки раздувались до натуральных 256px и
   разносили весь список. У соседних списков размер задан так же.
   Только прямой потомок: в подписи под именем стоит значок грибочков, и общее
   правило раздувало его с кегля строки до тех же тридцати пикселей — строка
   вырастала в полтора раза, а текст с аватаркой разъезжались. */
.admin-member-row > img {
  width: 30px;
  height: 30px;
  min-width: 30px;
  border-radius: 50%;
  object-fit: cover;
}

.admin-member-row span {
  display: grid;
  gap: 1px;
  min-width: 0;
}

.admin-member-row strong,
.admin-member-row small {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.admin-member-row small {
  display: flex;
  align-items: center;
  gap: 4px;
  color: var(--muted);
  font-size: 11px;
  font-weight: 750;
}

.admin-member-card {
  min-width: 0;
}

.admin-dossier-head {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  align-items: center;
  gap: var(--space-3);
  margin-bottom: var(--space-2);
}

.admin-dossier-head img {
  width: 52px;
  height: 52px;
  min-width: 52px;
  border-radius: 50%;
  object-fit: cover;
}

.admin-dossier-head span {
  display: grid;
  gap: 2px;
  min-width: 0;
}

.admin-dossier-head small {
  color: var(--muted);
  font-size: 12px;
  font-weight: 750;
}

.admin-dossier-block {
  display: grid;
  gap: var(--space-2);
  margin-top: var(--space-3);
  padding: var(--space-3);
  border: 1.5px solid var(--layer-line);
  border-radius: var(--radius-control);
  background: var(--layer-bg);
}

.admin-dossier-block h3 {
  font-size: 14px;
}

.admin-dossier-block .admin-metrics {
  margin-top: 0;
}

/* Строка правки: поля тянутся, кнопка встаёт следом. Сетка здесь не годится —
   repeat(auto-fit) не уживается с колонкой auto, и правило просто отбрасывалось,
   а форма расползалась в столбик. */
.admin-dossier-form {
  display: flex;
  flex-wrap: wrap;
  align-items: end;
  gap: var(--space-2);
}

.admin-dossier-form .create-field {
  flex: 1 1 170px;
  min-width: 0;
}

.admin-dossier-form > button {
  flex: 0 0 auto;
}

.admin-dossier-form .create-field {
  padding-bottom: 0;
}

.admin-toggles {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}

.admin-toggles .notification-toggle {
  min-width: 0;
  min-height: 38px;
}

.admin-member-items {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}

.admin-member-item {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 5px 8px;
  border: 1.5px solid var(--layer-line);
  border-radius: 999px;
  background: var(--layer-bg-strong);
  font-size: 13px;
  font-weight: 750;
}

.admin-member-item .icon-button {
  min-width: 26px;
  min-height: 26px;
  padding: 0;
  border-radius: 50%;
}

.admin-rights-form {
  align-items: center;
  gap: var(--space-3);
}

.admin-right {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 13px;
  font-weight: 750;
}

.admin-member-cases {
  display: grid;
  gap: 6px;
}

.admin-member-case {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-3);
  border: 1px solid var(--layer-line);
  border-radius: var(--radius-compact);
  background: var(--layer-bg-strong);
}

.admin-member-case.is-revoked {
  opacity: 0.6;
}

.admin-member-case span {
  display: grid;
  gap: 2px;
  min-width: 0;
}

.admin-member-case small {
  overflow: hidden;
  color: var(--muted);
  font-size: 12px;
  font-weight: 700;
  text-overflow: ellipsis;
  white-space: nowrap;
}

@media (max-width: 900px) {
  .admin-members-layout {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* В панели ролей чипы идут списком во всю ширину — в досье они должны стоять
   в строку, как обычные метки. */
.admin-member-card .role-chip {
  flex: 0 1 auto;
  width: auto;
  min-height: 32px;
}

/* ── Нейросети в админке ─────────────────────────────────────────────────── */

.admin-ai-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
  gap: var(--space-2);
  margin: var(--space-3) 0;
}

.admin-ai-card {
  display: grid;
  align-content: start;
  gap: 6px;
  padding: var(--space-3);
  border: 1.5px solid var(--layer-line);
  border-radius: var(--radius-control);
  background: var(--layer-bg);
}

.admin-ai-card.is-primary {
  border-color: color-mix(in srgb, var(--discord-blurple) 50%, var(--layer-line));
  background: color-mix(in srgb, var(--discord-blurple) 10%, var(--layer-bg));
}

.admin-ai-head {
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
}

.admin-ai-head i {
  color: var(--muted);
  font-size: 11px;
  font-style: normal;
  font-weight: 800;
  text-transform: uppercase;
}

.admin-ai-card small {
  color: var(--muted);
  font-size: 12px;
  font-weight: 750;
}

.admin-ai-key {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 6px;
  margin-top: 2px;
}

.admin-ai-key input {
  min-width: 0;
  min-height: var(--control-height);
  padding: 0 12px;
  border: 1.5px solid var(--layer-line);
  border-radius: var(--radius-compact);
  background: var(--layer-bg-strong);
  color: var(--text);
  font-family: var(--font-ui);
  font-size: 13px;
}

/* ── Доступ к админ-панели ───────────────────────────────────────────────── */

.admin-access-list {
  display: grid;
  gap: var(--space-2);
  margin-top: var(--space-3);
}

.admin-access-row {
  display: grid;
  grid-template-columns: auto minmax(160px, 220px) minmax(0, 1fr) auto;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-3);
  border: 1.5px solid var(--layer-line);
  border-radius: var(--radius-control);
  background: var(--layer-bg);
}

.admin-access-row.is-owner {
  grid-template-columns: auto minmax(0, 1fr);
  border-color: color-mix(in srgb, var(--discord-blurple) 45%, var(--layer-line));
}

.admin-access-row img {
  width: 30px;
  height: 30px;
  min-width: 30px;
  border-radius: 50%;
  object-fit: cover;
}

.admin-access-name {
  display: grid;
  gap: 2px;
  min-width: 0;
}

.admin-access-name small {
  color: var(--muted);
  font-size: 12px;
  font-weight: 750;
}

.admin-access-checks {
  display: flex;
  flex-wrap: wrap;
  gap: 6px var(--space-3);
  min-width: 0;
}

.admin-access-actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
}

@media (max-width: 900px) {
  .admin-access-row {
    grid-template-columns: auto minmax(0, 1fr);
  }

  .admin-access-checks,
  .admin-access-actions {
    grid-column: 1 / -1;
  }
}

/* ── Галочки ─────────────────────────────────────────────────────────────── */

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

   Именно `appearance: none` с полной заменой вида: голый `none` без своей
   отрисовки когда-то оставил подписи без квадратиков вовсе. */
input[type="checkbox"] {
  appearance: none;
  -webkit-appearance: none;
  position: relative;
  flex: 0 0 auto;
  width: 18px;
  height: 18px;
  margin: 0;
  border: 1.5px solid var(--layer-line);
  border-radius: 6px;
  background: var(--layer-bg);
  cursor: pointer;
  transition:
    border-color var(--motion-control) var(--motion-standard),
    background var(--motion-control) var(--motion-standard);
}

input[type="checkbox"]::before {
  content: "";
  position: absolute;
  inset: 4px;
  border-radius: 2px;
  background: var(--discord-blurple);
  opacity: 0;
  transition:
    inset 120ms var(--motion-fluid),
    opacity 120ms var(--motion-standard);
}

input[type="checkbox"]:hover {
  border-color: color-mix(in srgb, var(--discord-blurple) 45%, var(--layer-line));
}

input[type="checkbox"]:checked {
  border-color: var(--discord-blurple);
}

input[type="checkbox"]:checked::before {
  inset: 0;
  border-radius: 4px;
  opacity: 1;
}

/* Нажатие показывает, каким станет: квадрат наливается ещё до отпускания. */
input[type="checkbox"]:active::before {
  inset: 0;
  border-radius: 4px;
  opacity: 0.55;
}

input[type="checkbox"]:disabled {
  cursor: default;
  opacity: 0.45;
}

/* Там, где квадратик спрятан намеренно (одежда, чипы выбора), он и так лежит
   с opacity: 0 — своя отрисовка ему только мешает.
   position повторяем обязательно: общее правило выше объявляет relative, и по
   весу селекторов оно равно здешнему, но стоит позже — из-за этого спрятанные
   галочки переставали быть absolute, начинали занимать место в строке и
   разносили вёрстку грибочела и админки. */
.clothing-strip input[type="checkbox"],
.choice-chips input[type="checkbox"] {
  position: absolute;
  width: auto;
  height: auto;
  margin: 0;
  border: 0;
  background: none;
}

.clothing-strip input[type="checkbox"]::before,
.choice-chips input[type="checkbox"]::before {
  content: none;
}

/* ── Строки без иконки ───────────────────────────────────────────────────── */

/* .economy-list-row рассчитана на три части: значок, текст и действие. Там,
   где значка нет, текст попадал в колонку auto и переставал сжиматься —
   строка распирала список, а кнопка уезжала за экран. На телефоне это было
   особенно заметно в тестах. */
.economy-list-row:not(:has(> .economy-row-icon)) {
  grid-template-columns: minmax(0, 1fr) auto;
}

/* На узком экране кнопки встают под текстом, а не выдавливают его. */
@media (max-width: 520px) {
  .economy-list-row:not(:has(> .economy-row-icon)) {
    grid-template-columns: minmax(0, 1fr);
  }

  .quiz-row-actions {
    margin-left: 0;
    flex-wrap: wrap;
  }

  .quiz-row-actions .quiz-start {
    flex: 1 1 auto;
  }
}

/* ── Телефон: своя мера воздуха ──────────────────────────────────────────── */

/* Настольная вёрстка на узком экране разъезжалась: отступы съедали ширину,
   поля сжимались в точку, а ряды кнопок ломались вида «три и одна». Здесь
   собраны правила для телефона — размеры мельче, ряды ровные, ничего не
   уезжает за край. */
@media (max-width: 620px) {
  :root {
    --space-1: 4px;
    --space-2: 8px;
    --space-3: 10px;
    --space-4: 12px;
    --control-height: 40px;
    --radius: 18px;
    --radius-control: 13px;
    --radius-compact: 10px;
  }

  /* Ничто не должно уводить страницу вбок: клип не создаёт прокрутки и не
     ломает прилипание шапки, в отличие от overflow: hidden. */
  .app-shell {
    overflow-x: clip;
  }

  .page-stage {
    padding: 14px 0 32px;
  }

  .page {
    gap: 12px;
  }

  .panel,
  .metric {
    --panel-padding: 13px;
    padding: var(--panel-padding);
  }

  .metric {
    min-height: 74px;
  }

  .metric strong {
    font-size: 21px;
  }

  .panel-header h2 {
    font-size: 19px;
  }

  /* Кнопки: меньше воздуха внутри, но палец всё ещё попадает. */
  .compact-button,
  .inventory-toggle,
  .primary-button,
  .secondary-button,
  .danger-button,
  .notification-toggle,
  .segmented-control button,
  .choice-chips span {
    min-height: 38px;
    padding: 7px 10px;
    font-size: 13px;
  }

  /* Вкладки — лентой в одну строку с прокруткой. Перенос всё равно выходил
     рваным («три сверху, одна снизу»), а лента читается как в приложении. */
  .segmented-control {
    display: flex;
    flex-wrap: nowrap;
    gap: 6px;
    overflow-x: auto;
    padding-bottom: 2px;
    scrollbar-width: none;
  }

  .segmented-control::-webkit-scrollbar {
    display: none;
  }

  .segmented-control button {
    flex: 0 0 auto;
  }

  /* Ряды действий делят строку поровну — так они не встают лесенкой. */
  .choice-chips,
  .admin-moderation-actions,
  .quiz-question-actions,
  .quiz-editor-actions,
  .birthday-form-actions {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(92px, 1fr));
    gap: 6px;
  }

  /* Поля ввода не должны ужиматься в точку рядом с кнопками: на телефоне
     строка формы разворачивается в столбик. */
  .quiz-option-row,
  .quiz-result-row,
  .quiz-editor,
  .create-item-form,
  .admin-moderation-form,
  .market-sell,
  .admin-dossier-form,
  .admin-ai-key {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
  }

  /* У ответа поле занимает строку целиком, а его кнопки делят следующую. */
  .quiz-option-row {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .quiz-option-row > input[type="text"],
  .quiz-result-row > *,
  .market-sell > *,
  .admin-dossier-form > * {
    grid-column: 1 / -1;
  }

  .stock-actions {
    grid-template-columns: 68px repeat(auto-fit, minmax(0, 1fr));
  }

  /* Экономика и балансы: три колонки на телефоне превращались в мелкое сито. */
  .economy-balance-grid,
  .economy-actions-grid,
  .admin-metrics {
    grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
  }

  .profile-stat-grid,
  .current-activity-grid,
  .discord-community-grid,
  .settings-grid {
    grid-template-columns: minmax(0, 1fr);
  }

  /* Нечётный остаток в ряду из двух колонок занимает строку целиком — так
     это выглядит решением, а не обрывком. */
  .economy-actions-grid > :last-child:nth-child(odd),
  .economy-balance-grid > :last-child:nth-child(odd),
  .choice-chips > :last-child:nth-child(odd),
  .admin-metrics > :last-child:nth-child(odd),
  .games-grid > :last-child:nth-child(odd) {
    grid-column: 1 / -1;
  }

  /* Длинные подписи в списках переносятся, а не растягивают строку. */
  .economy-row-copy small,
  .admin-member-row small,
  .saved-meta small {
    white-space: normal;
  }

  .economy-row-icon,
  .notification-icon,
  .birthday-icon,
  .giveaway-icon {
    width: 34px;
    height: 34px;
  }
}

/* ── Телефон: мельче кнопки, меньше переносов ────────────────────────────── */

@media (max-width: 620px) {
  /* Кнопки-вкладки заметно мельче: так ряд из четырёх помещается в строку
     целиком, как на компьютере, и ленту не приходится листать. */
  .segmented-control button,
  .people-tabs button,
  .leaderboard-tabs button {
    padding: 6px 8px;
    font-size: 12px;
    min-height: 34px;
    gap: 4px;
  }

  .segmented-control {
    gap: 4px;
  }

  /* Строка рейтинга снова в одну линию: имя ужимается, а сумма стоит рядом,
     а не уезжает под него. */
  .leaderboard-row {
    grid-template-columns: 22px 30px minmax(0, 1fr) auto;
    gap: 8px;
    padding: 8px 10px;
  }

  .leaderboard-row > b {
    grid-column: auto;
    font-size: 13px;
    white-space: nowrap;
  }

  .leaderboard-row > span:nth-child(3) {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  .leader-avatar,
  .leader-avatar img {
    width: 30px;
    height: 30px;
  }

  .leaderboard-row > strong:first-child {
    font-size: 12px;
  }

  /* Длинные перечисления интересов не растягивают карточку на пол-экрана. */
  .person-card p {
    display: -webkit-box;
    overflow: hidden;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 2;
  }
}

/* ── Строки настроек и справочник: одна линия вместо двух ────────────────── */

/* Переключатель стоит в той же строке, что название: раньше на планшете и
   телефоне он падал вниз во всю ширину, и восемь настроек занимали два экрана
   вместо одного. */
@media (max-width: 760px) {
  .notification-row,
  .birthday-control {
    grid-template-columns: auto minmax(0, 1fr) auto;
  }

  .notification-toggle,
  .birthday-control .notification-toggle {
    grid-column: auto;
    width: auto;
    min-width: 0;
  }
}

/* Справочник команд: название слева, подпись прижата к стрелке справа. Раньше
   подпись начиналась там, где кончалось название, — у каждой строки в своём
   месте, а длинные названия ещё и переносились на вторую строку. */
.command-category summary > span {
  flex: 0 0 auto;
  white-space: nowrap;
}

.command-category summary small.category-hint {
  min-width: 0;
  text-align: right;
}

@media (max-width: 620px) {
  /* На телефоне подписи для этого места нет: строка остаётся одной линией с
     названием и стрелкой, а что внутри — видно после раскрытия. */
  .command-category summary small.category-hint {
    display: none;
  }

  .command-category summary {
    min-height: 42px;
    padding: 8px 11px;
  }

  /* Ширина у всех переключателей одна: правый край строк выстраивается в
     ровную колонку, а не пляшет по длине слова. */
  .notification-toggle {
    min-width: 90px;
    min-height: 36px;
    padding: 0 10px;
    font-size: 12px;
  }

  /* Название настройки на узком экране переносится, а не обрывается
     многоточием: «Скрыть меня из рейтингов» дороже одной сэкономленной
     строки. */
  .notification-copy strong {
    display: -webkit-box;
    overflow: hidden;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 2;
    white-space: normal;
    line-height: 1.2;
  }

  /* Пояснение к настройке не растягивает строку на пол-экрана. */
  .notification-copy small {
    display: -webkit-box;
    overflow: hidden;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 2;
  }

  .operation-row,
  .inventory-row {
    gap: 8px;
    min-height: 46px;
    padding: 7px 9px;
  }

  .operation-row > strong:last-child {
    font-size: 13px;
  }
}

/* ── Казино: подпись к выбору и компактные плитки игр ────────────────────── */

/* Ряд фишек сам по себе читался как случайные кнопки под полем ставки. Теперь
   у него есть подпись — такая же, как у «Ставки», — а сам ряд стоит по центру
   поля, а не жмётся к левому краю. */
.casino-choice-block {
  display: grid;
  gap: 7px;
}

.casino-choice-title {
  color: var(--muted);
  font-size: 12px;
  font-weight: 850;
}

.casino-board .casino-choice {
  justify-content: center;
}

/* Выбранная фишка отличается не только оттенком: тонкая рамка на телефоне
   почти не читалась. */
.casino-choice input:checked + span {
  border-width: 2px;
  padding: 8px 13px;
}

@media (max-width: 620px) {
  /* Семь игр в два ровных ряда: четыре сверху, три снизу. Раньше плитки шли по
     две в ряд и занимали пол-экрана до самой игры. */
  .casino-tabs {
    grid-template-columns: repeat(12, minmax(0, 1fr));
    gap: 6px;
    margin-bottom: var(--space-3);
  }

  .casino-tabs button {
    min-height: 52px;
    padding: 7px 4px;
    gap: 3px;
  }

  .casino-tabs button:nth-child(-n + 4) {
    grid-column: span 3;
  }

  .casino-tabs button:nth-child(n + 5) {
    grid-column: span 4;
  }

  .casino-tabs button > span {
    font-size: 17px;
  }

  .casino-tabs button > strong {
    font-size: 11px;
  }

  /* Поле игры и монеты в нём — под ширину телефона, а не под ширину стола. */
  .casino-stage {
    min-height: 0;
    padding: 0;
  }

  .casino-visual-card {
    min-height: 138px;
    padding: 14px;
  }

  /* Фишка занимает всю свою ячейку: раньше она жалась к левому краю, и ряд
     выглядел парой случайных кнопок, а не выбором из двух. */
  .choice-chips label {
    display: grid;
  }

  .choice-chips span {
    width: 100%;
  }

  .coin-side {
    width: clamp(72px, 21vw, 110px);
    gap: 6px;
  }

  .coin-side.result {
    width: clamp(96px, 28vw, 142px);
  }

  .coin-side strong {
    font-size: 12px;
  }
}

/* ── Телефон: ровные ряды кнопок и компактная сводка сервера ─────────────── */

@media (max-width: 620px) {
  /* Кнопки шапки делят строку поровну: раньше они шли по ширине надписи и
     ряд получался лесенкой — «Открыть команды» с «Новостями», под ними
     «Настройки» с «Администрацией», и все четыре разной длины. */
  .hero-actions {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 8px;
    margin-top: var(--space-4);
  }

  .hero-actions > :last-child:nth-child(odd) {
    grid-column: 1 / -1;
  }

  /* Три числа о сервере — одной строкой. Тремя карточками во всю ширину они
     занимали треть экрана до первой полезной панели. */
  .grid-3 {
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 8px;
  }

  .grid-4 {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 8px;
  }

  .grid-4 .metric,
  .grid-3 .metric {
    min-height: 0;
    padding: 10px;
    gap: 4px;
  }

  .grid-4 .metric strong,
  .grid-3 .metric strong {
    font-size: 19px;
  }

  .grid-4 .metric span,
  .grid-3 .metric span {
    font-size: 11px;
    line-height: 1.25;
  }
}

/* ── Заголовки внутри текста Discord ─────────────────────────────────────── */

/* Строки с решётками приходят из объявлений сервера. Раньше они выводились
   как есть, вместе с «###», — теперь это обычные подзаголовки. */
.discord-heading {
  display: block;
  margin-top: 4px;
  color: var(--text);
  font-weight: 850;
  line-height: 1.25;
}

.discord-heading.h1 { font-size: 1.18em; }
.discord-heading.h2 { font-size: 1.08em; }
.discord-heading.h3 { font-size: 1em; }

.discord-subtext {
  display: block;
  color: var(--muted);
  font-size: 0.85em;
  font-style: normal;
}

.discord-line {
  display: block;
  min-width: 0;
}

.discord-empty {
  min-height: 0.65em;
}

.discord-quote {
  margin: 2px 0;
  padding-left: 10px;
  border-left: 3px solid var(--layer-line);
  color: color-mix(in srgb, var(--text) 88%, var(--muted));
}

.discord-bullet {
  position: relative;
  padding-left: 15px;
}

.discord-bullet::before {
  content: "•";
  position: absolute;
  left: 2px;
  color: var(--muted);
}

.discord-inline-code {
  padding: 0.08em 0.32em;
  border-radius: 5px;
  background: var(--layer-bg-strong);
  font-family: ui-monospace, "SFMono-Regular", Menlo, monospace;
  font-size: 0.9em;
}

.discord-spoiler {
  padding: 0 0.2em;
  border-radius: 4px;
  background: var(--text);
  color: transparent;
}

.discord-spoiler:hover,
.discord-spoiler:focus {
  color: var(--bg);
}

/* ── Телефон: перевод денег во всю ширину ────────────────────────────────── */

/* Половина строки на карточку перевода означала поле ввода в четыре знака —
   в нём не помещалась даже подсказка («Суммс», «Су»). Три карточки идут одна
   под другой, как вклад: поле широкое, кнопка рядом. На компьютере остаётся
   ряд из трёх — там места хватает. */
@media (max-width: 620px) {
  .economy-tabs > button {
    min-width: 124px;
    justify-content: center;
  }

  .market-sell {
    grid-template-columns: minmax(0, 1fr) minmax(74px, 0.62fr) auto;
  }

  .market-sell > *,
  .market-sell .market-sell-price,
  .market-sell .market-sell-quantity,
  .market-sell > button {
    grid-column: auto;
  }

  .market-sell .market-sell-item {
    grid-column: 1 / -1;
  }

  .economy-actions-grid {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* На сенсорном экране нажатие обрабатывается сразу, без ожидания жеста
   двойного увеличения. Прокрутка страницы и горизонтальных лент сохраняется. */
button,
a,
summary,
[role="button"] {
  touch-action: manipulation;
}

/* ── Вкладки: всегда одна строка с прокруткой ────────────────────────────── */

/* Перенос вкладок на вторую строку всегда выходит рваным: то три сверху и
   одна снизу, то две одиноких кнопки под пятью. Лента листается вбок, как в
   приложениях, и на широком экране выглядит ровно так же — пока всё влезает,
   прокрутки просто нет. */
.segmented-control,
.people-tabs {
  display: flex;
  flex-wrap: nowrap;
  overflow-x: auto;
  padding-bottom: 2px;
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
}

.segmented-control::-webkit-scrollbar,
.people-tabs::-webkit-scrollbar {
  display: none;
}

.segmented-control > *,
.people-tabs > * {
  flex: 0 0 auto;
}

/* ── Экономика на телефоне: карточки в один столбец ──────────────────────── */

/* Кошелёк и банк стояли парой в полстроки, а вклад — во всю ширину: разнобой
   на ровном месте. Все карточки идут одна под другой, как на широком экране. */
@media (max-width: 620px) {
  .economy-balance-grid {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* ── Предел ужимания: ниже 360px страница листается вбок ─────────────────── */

/* Самый узкий настоящий телефон — 360px, под него вёрстка и проверена. Дальше
   ужимать нечего: панели рассыпаются на полосы в одно-два слова, кнопки
   налезают друг на друга. Поэтому ширина упирается в пол, а на совсем узком
   экране человек просто листает страницу ещё и вбок. */
body {
  min-width: 360px;
}

@media (max-width: 359px) {
  /* Обычно горизонтальная прокрутка запрещена — иначе случайный широкий блок
     уводил бы страницу вбок. Здесь она, наоборот, единственный способ добраться
     до правого края. */
  html {
    overflow-x: auto;
  }
}

/* ── Выбор цвета: квадрат, читаемый код и обычная рамка ──────────────────── */

/* Образец цвета — квадрат в высоту соседней кнопки. Раньше он был вытянут
   вширь и рядом с кнопкой смотрелся случайным прямоугольником. */
.color-control input[type="color"] {
  width: var(--control-height);
  height: var(--control-height);
  aspect-ratio: 1;
}

/* Код цвета — то, ради чего сюда смотрят во второй раз: его переписывают в
   другие места. Мелкой серой подписи для этого мало. */
.color-control code,
.pixel-own-color code {
  color: var(--text);
  font-size: 15px;
  font-weight: 800;
  letter-spacing: 0.02em;
  font-variant-numeric: tabular-nums;
}

/* Рамка образца — такая же, как у цветов палитры. Пунктир выделял «Свой цвет»
   среди них без причины. */
.pixel-own-color i {
  border: 2px solid var(--layer-line);
}

/* ── Мобильная шапка: без пересчёта Safari при движении адресной строки ── */

@media (max-width: 760px), (pointer: coarse) {
  :root {
    --topbar-offset: max(8px, calc(var(--safe-top) + 6px));
  }

  html {
    /* Системные области Safari получают сплошной safe-bg. Сам градиент
       рисуется отдельным фиксированным слоем ниже и совпадает с этим цветом
       на обоих краях, поэтому сверху и снизу не остаётся рамок. */
    background-image: none;
  }

  body::before {
    content: "";
    position: fixed;
    z-index: 0;
    top: calc(-1 * var(--safe-top));
    right: calc(-1 * var(--safe-right));
    bottom: calc(-1 * var(--safe-bottom));
    left: calc(-1 * var(--safe-left));
    pointer-events: none;
    background: linear-gradient(
      to bottom,
      var(--safe-bg) 0%,
      color-mix(in srgb, var(--safe-bg) 74%, var(--bg-glow) 26%) 17%,
      var(--bg) 43%,
      var(--bg) 70%,
      color-mix(in srgb, var(--bg-soft) 58%, var(--safe-bg) 42%) 89%,
      var(--safe-bg) 100%
    );
  }

  .app-shell {
    /* svh не меняется при появлении и скрытии панели браузера. Подвал
       остаётся в одной точке документа, а не ездит вслед за каждым свайпом. */
    min-height: 100vh;
    min-height: 100svh;
    padding-top: calc(92px + var(--safe-top));
  }

  :root.bare-page-active .app-shell {
    padding-top: max(8px, var(--safe-top));
  }

  .topbar {
    position: fixed;
    top: var(--topbar-offset);
    right: max(4px, var(--safe-right));
    left: max(4px, var(--safe-left));
    width: auto;
    max-width: none;
    margin: 0;
  }

  /* Подвал держится тех же полей, что и фиксированная шапка. */
  .site-footer {
    width: auto;
    margin-right: max(4px, var(--safe-right));
    margin-left: max(4px, var(--safe-left));
  }
}

/* Safari удерживает hover после касания. Убираем только зависающие подъёмы;
   короткое сжатие кнопки остаётся непосредственной обратной связью. */
@media (hover: none), (pointer: coarse) {
  .nav-item:hover,
  .primary-button:hover,
  .secondary-button:hover,
  .project-card:hover,
  .game-card:hover,
  .compact-button:hover,
  .inventory-toggle:hover,
  .command-row:hover {
    transform: none;
  }

  button:not(:disabled):active {
    scale: 0.97;
  }
}

/* ── Поддержка проекта ───────────────────────────────────────────────────── */

/* Три широкие кнопки в столбик: на странице больше ничего нет, и мельчить
   незачем. У каждой свой цвет службы — он единственное, чем они отличаются. */
.page-back-bar {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  min-width: 0;
  margin-bottom: var(--space-3);
}

/* Одна кнопка на всю навигацию: значок сайта и его название ведут на главную.
   Отдельные «Назад» и «Главная» рядом только путали. */
.home-button {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding-left: 8px;
  flex: 0 0 auto;
  white-space: nowrap;
}

.home-button img {
  width: 22px;
  height: 22px;
  border-radius: 7px;
  object-fit: cover;
}

.page-back-title {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  font-family: var(--font-display);
  font-size: 18px;
  font-weight: 900;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.page-back-bar.is-title-hidden .page-back-title {
  display: none;
}

@media (max-width: 520px) {
  .page-back-bar {
    gap: var(--space-2);
  }

  .page-back-tools {
    gap: var(--space-1);
  }
}

/* ── Алиас ───────────────────────────────────────────────────────────────── */

/* Игровому экрану нужен другой характер, чем остальному сайту: крупные пятна
   цвета, читаемое издалека слово и две большие кнопки под большой палец.
   Цвет команды задаётся одним числом и красит всё разом — плашку хода,
   рамку слова и строку счёта. */
.alias-page {
  --alias-accent: var(--discord-blurple);
}

[data-alias-team-color="0"] { --alias-accent: #ef4444; }
[data-alias-team-color="1"] { --alias-accent: #3b82f6; }
[data-alias-team-color="2"] { --alias-accent: #22c55e; }
[data-alias-team-color="3"] { --alias-accent: #f59e0b; }
[data-alias-team-color="4"] { --alias-accent: #a855f7; }
[data-alias-team-color="5"] { --alias-accent: #14b8a6; }

.alias-page .panel {
  width: min(100%, 760px);
  margin-inline: auto;
}

.alias-page .page-back-bar {
  width: min(100%, 760px);
  margin-inline: auto;
}

.alias-stage {
  display: grid;
  justify-items: center;
  gap: var(--space-3);
  padding: var(--space-4) 0 var(--space-2);
  text-align: center;
}

.alias-ready {
  gap: var(--space-2);
  padding: var(--space-5) 0;
  border-radius: var(--radius);
  background:
    radial-gradient(120% 100% at 50% 0%,
      color-mix(in srgb, var(--alias-accent) 26%, transparent), transparent 70%);
}

.alias-badge {
  padding: 4px 12px;
  border-radius: var(--radius-pill);
  background: color-mix(in srgb, var(--alias-accent) 22%, var(--layer-bg));
  color: color-mix(in srgb, var(--alias-accent) 70%, var(--text));
  font-size: 12px;
  font-weight: 850;
  text-transform: uppercase;
  letter-spacing: 0.08em;
}

.alias-team-name {
  font-family: var(--font-display);
  font-size: clamp(26px, 6vw, 38px);
  font-weight: 900;
  line-height: 1.1;
}

.alias-timebar {
  width: min(100%, 640px);
  height: 8px;
  border-radius: var(--radius-pill);
  background: var(--layer-bg);
  overflow: hidden;
}

.alias-timebar span {
  display: block;
  width: var(--доля, 100%);
  height: 100%;
  border-radius: inherit;
  background: var(--alias-accent);
  transition: width 1s linear;
}

.alias-clock {
  font-family: var(--font-display);
  font-size: clamp(34px, 7vw, 48px);
  font-weight: 900;
  font-variant-numeric: tabular-nums;
  line-height: 1;
}

.alias-word {
  display: grid;
  place-items: center;
  width: min(100%, 640px);
  min-height: clamp(140px, 26vh, 220px);
  padding: var(--space-4);
  border: 2px solid color-mix(in srgb, var(--alias-accent) 45%, var(--layer-line));
  border-radius: var(--radius);
  background:
    linear-gradient(180deg,
      color-mix(in srgb, var(--alias-accent) 14%, var(--layer-bg)),
      var(--layer-bg));
  font-family: var(--font-display);
  font-size: clamp(30px, 8vw, 54px);
  font-weight: 900;
  overflow-wrap: anywhere;
}

.alias-answers {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--space-2);
}

.alias-answers-big {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  width: min(100%, 640px);
  gap: var(--space-2);
}

.alias-hit,
.alias-miss {
  min-height: 64px;
  padding: 0 var(--space-4);
  border: 2px solid transparent;
  border-radius: var(--radius);
  font-family: var(--font-display);
  font-size: 18px;
  font-weight: 900;
  cursor: pointer;
  transition: transform var(--motion-control) var(--motion-fluid);
}

.alias-hit {
  border-color: color-mix(in srgb, var(--green) 55%, transparent);
  background: color-mix(in srgb, var(--green) 24%, var(--layer-bg));
  color: var(--text);
}

.alias-miss {
  border-color: color-mix(in srgb, var(--accent) 45%, transparent);
  background: color-mix(in srgb, var(--accent) 16%, var(--layer-bg));
  color: var(--text);
}

.alias-hit:active,
.alias-miss:active {
  transform: scale(0.98);
}

.alias-go {
  width: min(100%, 320px);
  margin-top: var(--space-2);
}

.alias-round-foot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: var(--space-2) var(--space-3);
}

.alias-turn {
  font-size: 16px;
}

.alias-progress,
.alias-recap-word {
  color: var(--muted);
  font-size: 13px;
}

.alias-recap {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--space-1);
}

.alias-recap-word {
  padding: 4px 10px;
  border: 1px solid var(--layer-line);
  border-radius: var(--radius-pill);
}

.alias-recap-word.is-hit {
  border-color: color-mix(in srgb, var(--green) 55%, var(--layer-line));
  color: var(--green);
}

.alias-recap-word.is-miss {
  text-decoration: line-through;
}

.alias-score {
  display: grid;
  gap: var(--space-1);
  margin-top: var(--space-3);
}

.alias-score-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  padding: var(--space-2) var(--space-3);
  border: 1px solid color-mix(in srgb, var(--alias-accent) 30%, var(--layer-line));
  border-left: 4px solid var(--alias-accent);
  border-radius: var(--radius-compact);
  /* Заливка есть у всех строк: раньше цвет проявлялся только у той, чей ход,
     и остальные выглядели забытыми. */
  background: color-mix(in srgb, var(--alias-accent) 9%, var(--layer-bg));
  font-weight: 800;
}

.alias-score-row.is-active {
  border-color: color-mix(in srgb, var(--alias-accent) 60%, var(--layer-line));
  background: color-mix(in srgb, var(--alias-accent) 22%, var(--layer-bg));
}

.alias-score-team {
  display: grid;
  gap: 2px;
  min-width: 0;
}

.alias-score-team strong {
  font-size: 15px;
}

.alias-score-team small {
  overflow: hidden;
  color: var(--muted);
  font-size: 12px;
  font-weight: 700;
  text-overflow: ellipsis;
}

/* Объясняющий в составе выделен: в игре это главный вопрос к экрану. */
.alias-score-team small b {
  color: var(--text);
}

.alias-score-points {
  font-family: var(--font-display);
  font-size: 20px;
  font-variant-numeric: tabular-nums;
}

.alias-roles {
  color: var(--muted);
  font-size: 14px;
  font-weight: 700;
}

.alias-roles strong {
  color: var(--text);
}

.alias-switch {
  align-items: center;
}

.alias-create select,
.alias-settings select {
  min-height: var(--control-height);
  padding: 0 var(--space-3);
  border: 1px solid var(--layer-line);
  border-radius: var(--radius-control);
  background: var(--layer-bg-strong);
  color: var(--text);
  font: inherit;
}

.alias-rules ol {
  display: grid;
  gap: var(--space-2);
  margin: 0;
  padding-left: 22px;
  color: var(--muted);
  line-height: 1.5;
}

.alias-rules li::marker {
  color: var(--text);
  font-weight: 900;
}

/* ── Алиас по коду ───────────────────────────────────────────────────────── */

.alias-entry {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: var(--space-3);
  margin-top: var(--space-3);
}

.alias-entry .alias-name {
  grid-column: 1 / -1;
}

/* Две карточки рядом: полей в них разное число, поэтому кнопки прижаты к низу
   — иначе «Войти» висела бы выше «Создать». */
.alias-card {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  padding: var(--space-3);
  border: 1px solid var(--layer-line);
  border-radius: var(--radius);
  background: var(--layer-bg);
}

.alias-card > strong {
  font-family: var(--font-display);
  font-size: 16px;
  font-weight: 900;
}

.alias-card-row {
  display: grid;
  grid-template-columns: minmax(0, 2fr) minmax(0, 1fr);
  gap: var(--space-2);
}

.alias-card .create-submit {
  margin: auto 0 0;
}

.alias-card .empty-note {
  margin: 0;
  line-height: 1.4;
}

.create-field .coin {
  vertical-align: -3px;
}

.alias-code-input {
  font-family: var(--font-display);
  font-size: 22px;
  font-weight: 900;
  letter-spacing: 0.18em;
  text-transform: uppercase;
}

.alias-room-head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
  padding-bottom: var(--space-2);
  border-bottom: 1px solid var(--layer-line);
}

.alias-code {
  display: grid;
  gap: 2px;
  padding: 6px 14px;
  border: 1px solid color-mix(in srgb, var(--accent) 40%, var(--layer-line));
  border-radius: var(--radius-compact);
  background: color-mix(in srgb, var(--accent) 12%, var(--layer-bg));
  color: var(--text);
  text-align: left;
  cursor: pointer;
  transition: background var(--fast), border-color var(--fast);
}

.alias-code:hover {
  border-color: color-mix(in srgb, var(--accent) 70%, var(--layer-line));
  background: color-mix(in srgb, var(--accent) 20%, var(--layer-bg));
}

.alias-code small {
  color: var(--muted);
  font-size: 11px;
}

.alias-code strong {
  font-family: var(--font-display);
  font-size: 24px;
  font-weight: 900;
  letter-spacing: 0.2em;
  line-height: 1.1;
}

/* Настройки правятся на месте: выбрал — сохранилось, и это видят все в
   комнате. Кнопки «сохранить» тут нет специально. */
.alias-settings {
  display: grid;
  gap: var(--space-2);
  padding-top: var(--space-3);
  border-top: 1px solid var(--layer-line);
}

.alias-settings-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: var(--space-2);
}

.alias-settings-label {
  color: var(--muted);
  font-size: 13px;
  font-weight: 700;
}

@media (max-width: 620px) {
  /* Узкий экран: код занимает строку целиком, ставка и выход — вторую. */
  .alias-room-head {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    align-items: center;
  }

  .alias-code {
    grid-column: 1 / -1;
  }
}

.alias-toggle-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  padding: var(--space-2) var(--space-3);
  border: 1px solid var(--layer-line);
  border-radius: var(--radius-compact);
  background: var(--layer-bg);
}

.alias-words {
  display: grid;
  gap: var(--space-1);
}

.alias-room-bet {
  flex: 1;
  color: var(--muted);
  font-weight: 700;
}

.alias-lobby {
  display: grid;
  gap: var(--space-3);
  padding-top: var(--space-3);
}

.alias-lobby-teams {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: var(--space-2);
}

.alias-lobby-team {
  display: grid;
  gap: var(--space-2);
  padding: var(--space-3);
  border: 1px solid color-mix(in srgb, var(--alias-accent) 30%, var(--layer-line));
  border-left: 4px solid var(--alias-accent);
  border-radius: var(--radius-compact);
  background: color-mix(in srgb, var(--alias-accent) 9%, var(--layer-bg));
}

.alias-lobby-team-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
}

.alias-lobby-team-head strong {
  font-family: var(--font-display);
  font-size: 17px;
  font-weight: 900;
}

.alias-lobby-players {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}

.alias-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 10px;
  border-radius: var(--radius-pill);
  background: var(--layer-bg-strong);
  font-size: 13px;
  font-weight: 700;
}

 /* Крестик — знак внутри плашки, а не кнопка в полный рост: общее правило
    button задаёт высоту в 44 пикселя, и плашка с ним раздувалась в овал. */
/* Крест внутри чипа — тот же рисованный, только мельче: кнопка размером в
   строку текста, полноразмерная иконка в неё не помещается. */
.alias-chip-kick svg {
  width: 12px;
  height: 12px;
}

.alias-chip-kick {
  display: grid;
  place-items: center;
  width: 18px;
  height: 18px;
  min-height: 0;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: none;
  color: var(--muted);
  font-size: 12px;
  line-height: 1;
  cursor: pointer;
}

.alias-chip-kick:hover {
  color: var(--danger, #ef4444);
}

.alias-word-hidden {
  color: var(--muted);
  font-size: clamp(18px, 4vw, 26px);
}

.game-teaser {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
}

.game-teaser h2 {
  font-family: var(--font-display);
  font-size: 20px;
  font-weight: 900;
}

.game-teaser .primary-button {
  flex: none;
}

/* Карточка с полотном: шапка строкой, под ней сам рисунок. */
.game-teaser.has-preview {
  flex-direction: column;
  align-items: stretch;
}

.game-teaser-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
}

/* Кисть админа: подпись над ползунком, оба во всю ширину переключателей. */
.pixel-brush {
  display: grid;
  gap: 6px;
  min-width: 150px;
  font-size: 13px;
  font-weight: 750;
  color: var(--muted);
}

.pixel-brush input[type="range"] {
  width: 100%;
  accent-color: var(--accent);
}

.pixel-preview {
  display: block;
  width: 100%;
  min-height: 0;
  padding: 0;
  border: 1.5px solid var(--layer-line);
  border-radius: var(--radius-control);
  background: var(--layer-bg-strong);
  cursor: pointer;
  overflow: hidden;
}

.pixel-preview:hover {
  border-color: color-mix(in srgb, var(--accent) 55%, var(--layer-line));
}

.pixel-preview canvas {
  display: block;
  width: 100%;
  height: 190px;
  /* Клетки должны остаться клетками: сглаживание превращает их в кашу. */
  image-rendering: pixelated;
}

@media (max-width: 620px) {
  .pixel-preview canvas {
    height: 150px;
  }
}

@media (max-width: 620px) {
  .alias-answers-big {
    grid-template-columns: minmax(0, 1fr);
  }
}

.debug-overrides select {
  min-height: var(--control-height);
  padding: 0 var(--space-3);
  border: 1px solid var(--layer-line);
  border-radius: var(--radius-control);
  background: var(--layer-bg-strong);
  color: var(--text);
  font: inherit;
}

.debug-switch {
  align-items: center;
}

.debug-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
}

.debug-summary {
  color: var(--muted);
  font-size: 12px;
  font-weight: 800;
}

.debug-list {
  display: grid;
  gap: var(--space-1);
  max-height: min(460px, 56vh);
  overflow-y: auto;
  scrollbar-width: thin;
}

.debug-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto auto;
  align-items: center;
  gap: 4px var(--space-2);
  padding: var(--space-2) var(--space-3);
  border: 1px solid var(--layer-line);
  border-radius: var(--radius-compact);
  background: var(--layer-bg);
}

.debug-row small {
  grid-column: 1 / -1;
  color: var(--muted);
  font-size: 12px;
}

.debug-name {
  overflow: hidden;
  font-weight: 800;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.debug-code,
.debug-time {
  color: var(--muted);
  font-size: 12px;
  font-variant-numeric: tabular-nums;
}

.debug-row.is-ok .debug-code {
  color: var(--green);
}

.debug-row.is-bad {
  border-color: color-mix(in srgb, var(--accent) 55%, var(--layer-line));
}

.debug-row.is-bad .debug-code {
  color: var(--accent);
}

.donate-lead {
  margin-bottom: var(--space-3);
  font-size: 15px;
  line-height: 1.5;
}

.donate-links {
  display: grid;
  gap: var(--space-3);
}

.donate-link {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 84px;
  padding: var(--space-4);
  border: 1.5px solid var(--layer-line);
  border-radius: var(--radius);
  background: var(--layer-bg);
  color: var(--text);
  font-family: var(--font-display);
  font-size: 20px;
  font-weight: 900;
  text-align: center;
  text-decoration: none;
  transition:
    transform var(--motion-control) var(--motion-standard),
    border-color var(--motion-control) var(--motion-standard);
}

.donate-link:hover {
  transform: translateY(-2px);
  border-color: var(--accent);
}

.donate-link:active {
  transform: translateY(0);
}

/* Кнопка без адреса: видно, что служба будет, но нажимать пока нечего. */
.donate-link.is-waiting {
  border-style: dashed;
  cursor: default;
  opacity: 0.65;
}

.donate-link.is-waiting:hover {
  transform: none;
  border-color: var(--layer-line);
}

/* Если буфер обмена недоступен, на кнопке остаётся сам адрес — его хотя бы
   можно выделить руками, поэтому длинная строка обязана переноситься. */
.donate-link {
  overflow-wrap: anywhere;
}

.donate-link.is-copied {
  border-color: var(--green);
  color: var(--green);
}

@media (max-width: 620px) {
  .donate-link {
    min-height: 68px;
    padding: var(--space-3);
    font-size: 17px;
  }
}

/* ── Главная: рассказ про сервер ─────────────────────────────────────────── */

.home-hero h1 {
  max-width: 18ch;
  font-family: var(--font-display);
  font-size: clamp(30px, 4.6vw, 52px);
  font-weight: 900;
  line-height: 1.05;
}

.home-hero p {
  max-width: 62ch;
  margin-top: var(--space-3);
  color: var(--muted);
  font-size: 16px;
  line-height: 1.55;
}

.home-hero .hero-actions {
  margin-top: var(--space-4);
}

.home-hero .hero-actions .primary-button,
.home-hero .hero-actions .secondary-button,
.home-outro-actions .primary-button,
.home-outro-actions .secondary-button {
  box-sizing: border-box;
  min-height: 46px;
  padding-block: 0;
  font-family: var(--font-display);
  font-weight: 800;
  line-height: 1;
  text-align: center;
  text-decoration: none;
}

.home-hero .hero-actions a > span,
.home-outro-actions a > span {
  display: block;
  line-height: 1.1;
}

.home-doing {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
  gap: var(--space-2);
}

.home-doing-card {
  display: grid;
  grid-template-rows: auto minmax(0, 1fr);
  gap: var(--space-2);
  min-height: 220px;
  padding: var(--space-3);
  border: 1px solid var(--layer-line);
  border-radius: var(--radius);
  background: var(--layer-bg);
  container-type: inline-size;
  text-align: center;
}

.home-doing-card strong {
  align-self: start;
  justify-self: center;
  font-family: var(--font-display);
  font-size: 17px;
  font-weight: 900;
}

.home-doing-card p {
  align-self: center;
  margin: 0;
  color: var(--muted);
  line-height: 1.4;
  text-wrap: balance;
}

.home-doing-card-short p {
  font-size: clamp(21px, 10.5cqi, 29px);
  line-height: 1.28;
}

.home-doing-card-medium p {
  font-size: clamp(17px, 7.5cqi, 21px);
  line-height: 1.35;
}

.home-doing-card-long p {
  font-size: clamp(15px, 6.4cqi, 18px);
}

/* История: столбик точек, соединённых линией. */
.home-history {
  display: grid;
  gap: var(--space-3);
  margin: 0;
  padding: 0;
  list-style: none;
}

.home-history li {
  position: relative;
  display: grid;
  grid-template-columns: minmax(96px, auto) minmax(0, 1fr);
  gap: var(--space-3);
  align-items: baseline;
  padding-left: var(--space-3);
}

.home-history li::before {
  content: "";
  position: absolute;
  top: 8px;
  left: 0;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: color-mix(in srgb, var(--accent) 70%, var(--layer-line));
}

.home-history li:not(:last-child)::after {
  content: "";
  position: absolute;
  top: 18px;
  bottom: calc(var(--space-3) * -1 + 4px);
  left: 3.5px;
  width: 1px;
  background: var(--layer-line);
}

.home-history-when {
  font-family: var(--font-display);
  font-size: 16px;
  font-weight: 900;
}

.home-history-what {
  color: var(--muted);
  line-height: 1.5;
}

/* Цитаты. */
.quote-grid {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: minmax(240px, calc((100% - var(--space-2) * 2) / 3));
  gap: var(--space-2);
  margin-top: var(--space-3);
  overflow-x: auto;
  overscroll-behavior-inline: contain;
  scroll-snap-type: inline proximity;
  scrollbar-width: thin;
}

.quote-grid.is-scrolled-left:not(.is-scrolled-right) {
  mask-image: linear-gradient(to right, transparent, #000 18px);
}

.quote-grid.is-scrolled-right:not(.is-scrolled-left) {
  mask-image: linear-gradient(to right, #000 calc(100% - 18px), transparent);
}

.quote-grid.is-scrolled-left.is-scrolled-right {
  mask-image: linear-gradient(to right, transparent, #000 18px,
                              #000 calc(100% - 18px), transparent);
}

.quote-card {
  display: grid;
  grid-template-rows: 1fr auto;
  gap: var(--space-2);
  margin: 0;
  min-height: 150px;
  padding: var(--space-3);
  text-align: center;
  border: 1px solid var(--layer-line);
  border-left: 3px solid color-mix(in srgb, var(--accent) 60%, var(--layer-line));
  border-radius: var(--radius);
  background: var(--layer-bg);
  scroll-snap-align: start;
}

/* Блок с текстом — обычный абзац: у сетки эмодзи становится отдельной
   ячейкой и уезжает на свою строку. */
.quote-card blockquote {
  align-self: center;
  max-width: 30ch;
  margin: 0 auto;
  font-size: var(--кегль, 16px);
  font-weight: 700;
  line-height: 1.4;
  text-wrap: balance;
}

.quote-card figcaption {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  color: var(--muted);
  font-size: 13px;
  font-weight: 700;
}

.quote-avatar {
  line-height: 1;
  display: grid;
  place-items: center;
  width: 26px;
  height: 26px;
  overflow: hidden;
  border-radius: 50%;
  background: var(--layer-bg-strong);
  font-size: 15px;
}

.quote-avatar img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.home-quotes-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
  margin-top: var(--space-3);
}

.quote-own-actions > span {
  color: var(--muted);
  font-weight: 750;
}

.home-outro-actions {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-2);
  width: min(100%, 520px);
}

.home-outro-actions .primary-button,
.home-outro-actions .secondary-button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  text-decoration: none;
}

@media (max-width: 440px) {
  .home-outro-actions {
    grid-template-columns: 1fr;
  }
}

@media (max-width: 760px) {
  .quote-grid {
    grid-auto-columns: minmax(240px, 86%);
  }
}

.quote-form-actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  grid-column: 1 / -1;
}

.home-stats {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: var(--space-2);
}

.home-stat {
  display: grid;
  gap: 4px;
  padding: var(--space-3);
  border: 1px solid var(--layer-line);
  border-radius: var(--radius);
  background: var(--layer-bg);
}

.home-stat strong {
  font-family: var(--font-display);
  font-size: clamp(24px, 3vw, 32px);
  font-weight: 900;
  font-variant-numeric: tabular-nums;
}

.home-stat span {
  color: var(--muted);
  line-height: 1.4;
}

.home-outro {
  display: grid;
  justify-items: start;
  gap: var(--space-3);
}

.home-outro h2 {
  font-family: var(--font-display);
  font-size: clamp(24px, 3.4vw, 36px);
  font-weight: 900;
}

.home-outro p {
  max-width: 60ch;
  color: var(--muted);
  line-height: 1.55;
}

.home-outro .primary-button {
  text-decoration: none;
}

.home-outro-note {
  color: var(--muted);
  font-size: 12px;
}

.project-card-icon-svg {
  display: grid;
  place-items: center;
  color: var(--muted);
  font-family: var(--font-display);
  font-weight: 900;
}

.project-card-next {
  cursor: default;
  opacity: 0.75;
}

/* ── Эффект-мейкер ───────────────────────────────────────────────────────── */

/* Инструменту тесно в колонке под текст: даём ему ширину и высоту экрана. */
.utilities-page .panel,
.utilities-page .page-back-bar {
  width: min(100%, 1180px);
}

.effect-layout {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(280px, 360px);
  gap: var(--space-3);
  margin-top: var(--space-3);
  min-height: calc(100vh - 240px);
}

.effect-stage {
  display: grid;
  gap: var(--space-2);
  align-content: start;
  /* Полоса кнопок ужимается по ширине этой колонки, а не окна: колонка вдвое
     уже страницы, и по ширине окна подписи оставались полными как раз там,
     где места уже не хватало, — кнопки уезжали на второй ряд. */
  container-type: inline-size;
}

/* Ужимаем в два приёма, по ширине колонки. Сначала сегменты: они дают больше
   всего ширины, а короче от этого не становятся. Подписи кнопок трогаем
   позже — «Скачать PNG» понятнее, чем «PNG», и терять их надо последними. */
@container (max-width: 780px) {
  .effect-bar .effect-group button {
    padding: 9px 8px;
  }
}

@container (max-width: 730px) {
  .effect-bar .effect-label-full {
    display: none;
  }

  .effect-bar .effect-label-short {
    display: inline;
  }

  .effect-bar button {
    white-space: nowrap;
  }
}

.effect-drop {
  display: grid;
  place-items: center;
  min-height: min(60vh, 460px);
  padding: var(--space-3);
  border: 1px dashed color-mix(in srgb, var(--accent) 40%, var(--layer-line));
  border-radius: var(--radius);
  background: var(--layer-bg);
  color: var(--muted);
  text-align: center;
  cursor: pointer;
}

/* Картинка занимает всё, что ей отдали: ширину ограничивает контейнер, высоту
   — экран, а пропорция держится через соотношение сторон самой картинки. */
.effect-drop canvas {
  width: min(100%, calc(var(--соотношение, 1) * var(--высота-холста)));
  height: auto;
  max-width: 100%;
  border-radius: var(--radius-compact);
  image-rendering: auto;
}

/* С картинкой область исчезает: остаётся сам холст. Рамка и отступы вокруг
   узкого изображения читались как пустые поля неправильного размера. */
.effect-drop.is-loaded {
  --высота-холста: min(68vh, 620px);

  min-height: 0;
  padding: 0;
  border: none;
  background: transparent;
}

.effect-drop canvas.is-empty {
  display: none;
}

/* Клетка под холстом в режиме выреза. Прозрачную дырку иначе не отличить от
   белого облака: на светлой теме и то и другое выглядит белым пятном. */
.effect-drop canvas.is-cut {
  background-image:
    linear-gradient(45deg, var(--layer-bg-strong) 25%, transparent 25%),
    linear-gradient(-45deg, var(--layer-bg-strong) 25%, transparent 25%),
    linear-gradient(45deg, transparent 75%, var(--layer-bg-strong) 75%),
    linear-gradient(-45deg, transparent 75%, var(--layer-bg-strong) 75%);
  background-position: 0 0, 0 8px, 8px -8px, -8px 0;
  background-size: 16px 16px;
}

.effect-frame {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  align-items: center;
  color: var(--muted);
  font-size: 12px;
  font-weight: 700;
}

.effect-frame input[type="range"] {
  flex: 1 1 160px;
}

.effect-frame-label {
  flex: none;
}

/* Кнопки держатся парой: переносится вся пара, а не «Отмена» в одиночку. */
.effect-frame-actions {
  display: flex;
  flex: none;
  gap: var(--space-2);
}

.effect-frame output {
  color: var(--text);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/* Ползунок кадра — тот же, что у эффектов. */
.effect-frame input[type="range"] {
  width: 100%;
  height: 22px;
  margin: 0;
  padding: 0;
  background: none;
  cursor: pointer;
}

.effect-frame input[type="range"]::-webkit-slider-runnable-track {
  height: 8px;
  border-radius: var(--radius-pill);
  background:
    linear-gradient(to right,
      color-mix(in srgb, var(--accent) 65%, transparent) var(--доля, 0%),
      color-mix(in srgb, var(--text) 12%, transparent) var(--доля, 0%));
}

.effect-frame input[type="range"]::-webkit-slider-thumb {
  appearance: none;
  width: 18px;
  height: 18px;
  margin-top: -5px;
  border: 2px solid color-mix(in srgb, var(--layer-bg) 80%, transparent);
  border-radius: 50%;
  background: color-mix(in srgb, var(--accent) 55%, #fff);
  box-shadow: 0 2px 8px rgb(0 0 0 / 45%);
}

.effect-frame input[type="range"]::-moz-range-thumb {
  width: 18px;
  height: 18px;
  border: 2px solid color-mix(in srgb, var(--layer-bg) 80%, transparent);
  border-radius: 50%;
  background: color-mix(in srgb, var(--accent) 55%, #fff);
}



/* Одна полоса под картинкой: слева настройки инструмента, справа — действия.
   Отдельный ряд только под кнопки отнимал высоту у самой картинки. */
.effect-bar {
  display: flex;
  flex-wrap: wrap;
  /* Группы держат свою высоту: иначе разделительная линия перед короткой
     группой тянулась на всю высоту полосы и висела в пустоте. */
  align-items: flex-start;
  /* Не center, а stretch: при центрировании каждая кнопка вставала по своему
     содержимому, и «Скачать PNG» с другим кеглем оказывалась на полтора
     пикселя выше «Мема». Высоту задаёт строка, одну на всех. */
  align-items: stretch;
  gap: var(--space-2);
}

/* В полосе живёт ровно один вид кнопки. Раньше выбор набора рисовался
   сегментами: они на четыре пикселя выше обычной кнопки, с другим фоном и
   приглушённым текстом — рядом это читалось как случайный разнобой.
   Ничего не прокручивается: спрятанную за краем кнопку просто не находят. */
.effect-group {
  display: flex;
  flex-wrap: wrap;
  align-items: stretch;
  gap: 6px;
  min-width: 0;
}

/* Формы облачка стоят сеткой три на два. В одну строку шесть кнопок распирали
   левую колонку, и панель с ползунками уезжала за край панели. */
.bubble-shapes {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
}

/* Инструменты облачков стоят два на два — столько же строк, сколько у форм
   слева. Одной лентой они растягивали полосу, а действия уезжали вниз. */
.bubble-tools {
  display: grid;
  grid-auto-flow: column;
  grid-template-rows: repeat(2, auto);
  gap: var(--space-2);
}

/* Высота задаётся явно, а не содержимым: у выравнивания по строке она
   считается для каждой строки отдельно, и при переносе нижний ряд кнопок
   оказывался выше верхнего. Подписи здесь однострочные, так что фиксировать
   высоту безопасно. */
.effect-bar button {
  height: 40px;
  min-height: 0;
  padding-block: 0;
}

@media (max-width: 620px) {
  .effect-bar button {
    height: 38px;
  }
}

.effect-bar > .effect-group,
.effect-bar > button {
  flex: 0 0 auto;
}

/* Инструменты стоят слева, сохранение и сброс — у правого края: во всех
   утилитах одинаково, чтобы рука искала кнопки в одном месте. Блок ужимается,
   а не уезжает вниз: перенос происходит внутри него. */
.effect-bar > .effect-actions {
  /* Забирает остаток строки, а не свою ширину по содержимому: иначе флекс
     переносил блок целиком вниз, вместо того чтобы сложить кнопки внутри. */
  flex: 1 1 0;
  min-width: 0;
}



/* Иконка идёт перед подписью: кнопку узнают по значку раньше, чем прочитают. */
.effect-download,
.effect-reset,
.effect-clear-action {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
}

.effect-download svg,
.effect-reset svg,
.effect-clear-action svg {
  flex: 0 0 auto;
}

/* Сброс и очистка стирают сделанное, поэтому чуть теплее остальных. Именно
   чуть: яркая красная кнопка перетягивала на себя весь взгляд, хотя рядом
   стоят вещи поважнее. */
.effect-reset,
.effect-clear-action {
  color: color-mix(in srgb, var(--accent) 30%, var(--text));
}

.effect-reset:hover:not(:disabled),
.effect-clear-action:hover:not(:disabled) {
  color: color-mix(in srgb, var(--accent) 55%, var(--text));
  border-color: color-mix(in srgb, var(--accent) 34%, var(--layer-line));
}

/* Группа держит свою высоту, а не тянется до самой высокой в полосе. Иначе
   блок сохранений (всегда два ряда) растягивал и однорядные группы, и
   разделительная черта выходила двухрядной там, где инструменты стоят в одну
   строку. Кнопки внутри и так заданы по высоте, поэтому вид не меняется. */
.effect-bar > .effect-group {
  align-self: start;
}

/* Соседние группы разделяет волосяная линия, а не зазор: одного зазора в
   ряду одинаковых кнопок глаз не замечает. */
.effect-bar > .effect-group + .effect-group {
  /* Разделитель — короткая черта в одну кнопку, а не рамка во всю высоту:
     рядом с группой из одного ряда линия на два ряда висела в пустоте. */
  position: relative;
  padding-left: var(--space-3);
}

.effect-bar > .effect-group + .effect-group::before {
  content: "";
  position: absolute;
  /* Высота — от отступа до отступа, а не число в пикселях: у группы в
     одну кнопку и у группы в два ряда высота своя, и фиксированная черта
     рядом с двухрядной смотрелась огрызком. */
  top: 8px;
  bottom: 8px;
  /* Ровно посередине промежутка: слева от группы ещё зазор полосы, и без
     поправки на него черта косила вправо. */
  left: calc((var(--space-3) - var(--space-2)) / 2 - 1px);
  width: 2px;
  border-radius: 1px;
  background: var(--layer-line);
}

/* Выбранное отмечаем тем же способом, что и везде на сайте: рамка и подложка
   акцентом. Высота, шрифт и радиус при этом не меняются — иначе выбор
   «прыгает» относительно соседей. */
.compact-button.is-active {
  border-color: color-mix(in srgb, var(--discord-blurple) 55%, var(--layer-line));
  background: color-mix(in srgb, var(--discord-blurple) 20%, var(--layer-bg-strong));
  color: var(--text);
}

/* Фон демотиватора — это цвет, а не слово: образец показывает его сразу и
   занимает вчетверо меньше места, чем «Классический» и «Светлый».
   Селектор из двух классов не случаен: у .compact-button.is-active фон задан
   сокращённой записью, а она обнуляет background-image. */
/* Образец рисуем отдельным слоем, а не фоном кнопки. Фоном он держался на
   background-clip: content-box, но сокращённая запись background в правилах
   наведения и нажатия сбрасывает clip на border-box — цвет разливался на всю
   кнопку и оставался так, пока курсор рядом. Отдельный слой этому не
   подвержен вовсе: ни одно правило кнопки до него не дотягивается. */
.compact-button.effect-swatch {
  position: relative;
  width: auto;
  aspect-ratio: 1;
  padding: 0;
  overflow: hidden;
}

.compact-button.effect-swatch::before {
  content: "";
  position: absolute;
  inset: 5px;
  border-radius: calc(var(--radius-compact) - 3px);
  background-color: var(--образец);
  transition:
    inset var(--motion-control) var(--motion-fluid),
    border-radius var(--motion-control) var(--motion-fluid);
}

/* Выбранный образец залит во всю кнопку — это и есть пометка выбора.
   Цветную плашку видно издалека, в отличие от тонкой рамки, и не надо гадать,
   какой фон сейчас стоит.
   Нажатие заливает так же, но временно: пока держишь незабранный образец,
   он показывает, каким станет. Отпустил, не выбрав — вернулся в рамку. */
.compact-button.effect-swatch.is-active::before,
.compact-button.effect-swatch:active::before,
.compact-button.effect-swatch.is-pressed::before {
  inset: 0;
  border-radius: inherit;
}

.compact-button.effect-swatch:active::before,
.compact-button.effect-swatch.is-pressed::before {
  transition-duration: 80ms;
}

/* Прозрачность показываем шашкой: сплошным цветом «ничего» не нарисуешь.
   Одна плитка conic-gradient — это и есть клетка два на два; пара градиентов
   под 45° давала бы ромбы. Плитка 14px делит поле образца ровно надвое,
   поэтому по краям нет обрезанных клеток. */
/* У выбранного прозрачного подложку кнопки оставляем обычной: акцент
   просвечивал бы сквозь прозрачные клетки и красил шашку в фиолетовый.
   Что образец выбран, и так видно — он залит во всю кнопку. */
.compact-button.effect-swatch.is-clear.is-active {
  background-color: var(--layer-bg-strong);
}

.compact-button.effect-swatch.is-clear::before {
  --шашка: color-mix(in srgb, var(--text) 22%, transparent);
  background-color: var(--layer-bg-strong);
  background-image: conic-gradient(
    var(--шашка) 0 25%, transparent 0 50%,
    var(--шашка) 0 75%, transparent 0);
  background-size: 14px 14px;
}

.compact-button.is-active:hover {
  background: color-mix(in srgb, var(--discord-blurple) 26%, var(--layer-bg-strong));
}

.effect-actions {
  /* Блок действий никогда не съезжает отдельной строкой под инструменты: он
     стоит в той же строке и, если места мало, сам складывается в два ряда
     по две кнопки. Пары («скачать» и «стереть») переносятся целиком: разрывать
     их посередине нельзя, иначе кнопки перемешиваются между строками. */
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  align-content: center;
  gap: var(--space-2);
  width: max-content;
  max-width: 100%;
  /* Прижимаем к правому краю: действия во всех утилитах стоят под правым
     нижним углом картинки, и искать их каждый раз заново не приходится. */
  margin-left: auto;
}

.effect-action-row {
  display: flex;
  justify-content: flex-end;
  gap: var(--space-2);
}

.effect-actions > button {
  /* Все четыре одной ширины: «Сбросить» рядом со «Скачать PNG» смотрелось
     мельче и читалось как менее важное. */
  flex: 0 0 auto;
  min-width: 148px;
}

.effect-action-row > button {
  min-width: 148px;
}

.effect-actions > .effect-clear {
  width: 40px;
  justify-self: end;
}

.effect-controls {
  display: grid;
  gap: var(--space-2);
  align-content: start;
  max-height: calc(100vh - 260px);
  overflow-y: auto;
  padding-right: 4px;
  scrollbar-width: thin;
  overscroll-behavior: contain;
  mask-image: linear-gradient(to bottom,
    transparent 0, #000 var(--fade-top, 0px),
    #000 calc(100% - var(--fade-bottom, 0px)), transparent 100%);
}

.effect-item {
  display: grid;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-3);
  border: 1px solid var(--layer-line);
  border-radius: var(--radius-compact);
  background: var(--layer-bg);
}

.effect-item.is-on {
  border-color: color-mix(in srgb, var(--accent) 50%, var(--layer-line));
  background: color-mix(in srgb, var(--accent) 10%, var(--layer-bg));
}

.effect-toggle {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
  min-height: 0;
  padding: 0;
  border: 0;
  background: none;
  color: var(--text);
  text-align: left;
  cursor: pointer;
}

.effect-toggle strong {
  display: block;
  font-family: var(--font-display);
  font-size: 15px;
  font-weight: 900;
}

.effect-toggle small {
  color: var(--muted);
  font-size: 12px;
}

.effect-switch {
  flex: none;
  padding: 3px 10px;
  border-radius: var(--radius-pill);
  background: var(--layer-bg-strong);
  font-size: 11px;
  font-weight: 800;
  text-transform: uppercase;
}

.effect-item.is-on .effect-switch {
  background: color-mix(in srgb, var(--green) 25%, var(--layer-bg));
}

.effect-param {
  display: grid;
  grid-template-columns: minmax(64px, auto) minmax(0, 1fr) 34px;
  gap: var(--space-2);
  align-items: center;
  color: var(--muted);
  font-size: 12px;
  font-weight: 700;
}

.effect-param output {
  color: var(--text);
  font-variant-numeric: tabular-nums;
  text-align: right;
}

/* Свой ползунок: нативный в тёмной теме выглядит как деталь от чужого сайта.
   Дорожка полупрозрачная, закрашенная часть считается в --доля. */
.effect-param input[type="range"] {
  width: 100%;
  height: 22px;
  margin: 0;
  padding: 0;
  background: none;
  cursor: pointer;
}

.effect-param input[type="range"]::-webkit-slider-runnable-track {
  height: 8px;
  border-radius: var(--radius-pill);
  background:
    linear-gradient(to right,
      color-mix(in srgb, var(--accent) 65%, transparent) var(--доля, 0%),
      color-mix(in srgb, var(--text) 12%, transparent) var(--доля, 0%));
}

.effect-param input[type="range"]::-moz-range-track {
  height: 8px;
  border-radius: var(--radius-pill);
  background: color-mix(in srgb, var(--text) 12%, transparent);
}

.effect-param input[type="range"]::-moz-range-progress {
  height: 8px;
  border-radius: var(--radius-pill);
  background: color-mix(in srgb, var(--accent) 65%, transparent);
}

.effect-param input[type="range"]::-webkit-slider-thumb {
  appearance: none;
  width: 18px;
  height: 18px;
  margin-top: -5px;
  border: 2px solid color-mix(in srgb, var(--layer-bg) 80%, transparent);
  border-radius: 50%;
  background: color-mix(in srgb, var(--accent) 55%, #fff);
  box-shadow: 0 2px 8px rgb(0 0 0 / 45%);
  transition: transform var(--fast), box-shadow var(--fast);
}

.effect-param input[type="range"]::-moz-range-thumb {
  width: 18px;
  height: 18px;
  border: 2px solid color-mix(in srgb, var(--layer-bg) 80%, transparent);
  border-radius: 50%;
  background: color-mix(in srgb, var(--accent) 55%, #fff);
  box-shadow: 0 2px 8px rgb(0 0 0 / 45%);
}

.effect-param input[type="range"]:hover::-webkit-slider-thumb,
.effect-param input[type="range"]:focus-visible::-webkit-slider-thumb {
  transform: scale(1.12);
  box-shadow: 0 0 0 6px color-mix(in srgb, var(--accent) 18%, transparent);
}

.effect-param input[type="range"]:active::-webkit-slider-thumb {
  transform: scale(0.96);
}

.effect-param input[type="range"]:focus-visible {
  outline: none;
}

/* Узкий ПК: колонка настроек уступает место полосе под картинкой, чтобы
   кнопки влезали в один ряд. Ползункам этой ширины хватает, а вот кнопки
   на второй строке — ровно то, от чего мы уходили. */
@media (min-width: 981px) and (max-width: 1100px) {
  .effect-layout {
    grid-template-columns: minmax(0, 1fr) minmax(240px, 280px);
  }
}

/* Одна колонка раньше, чем кажется нужным: в двух колонках ниже этой ширины
   полоса под картинкой уже не влезает в строку, а в одной — влезает с
   запасом, потому что картинка занимает всю ширину страницы. */
@media (max-width: 980px) {
  .effect-layout {
    grid-template-columns: minmax(0, 1fr);
    min-height: 0;
  }

  /* В одну колонку кнопки стоят выше списка, поэтому своя прокрутка списку
     больше не нужна — а прокрутка внутри прокрутки только мешает: до нужного
     ползунка не добраться, страница уезжает вместо списка. Список течёт
     вместе со страницей, как все длинные списки на сайте. */
  .effect-controls {
    max-height: none;
    overflow: visible;
    padding-right: 0;
    mask-image: none;
  }

  /* Пустое поле не держит высоту: пока картинки нет, хватает места «нажми
     или перетащи», а с картинкой рамка обнимает саму картинку. */
  .effect-drop {
    min-height: 180px;
  }

  .effect-stage [data-demotivator-drop],
  .effect-stage [data-deepfry-drop] {
    min-height: 180px;
  }

  .effect-drop.is-loaded {
    min-height: 0;
  }

  /* Действия не прижимаются к правому краю: на узкой полосе между ними и
     настройками зияла дыра в пол-экрана. */
  .effect-bar > .effect-actions {
    margin-left: 0;
  }

  .effect-drop.is-loaded {
    --высота-холста: 46vh;
  }

  /* На узком экране сохраняем ту же сетку два на два, меняются только
     короткие подписи и высота кнопок. */
  .effect-actions {
    display: flex;
    flex-wrap: wrap;
    justify-content: flex-end;
    gap: 6px;
  }

  .effect-actions button {
    min-width: 0;
    padding: 0 8px;
    font-size: 12px;
  }

  .effect-clear {
    width: 40px;
  }

  .utilities-page .panel {
    padding: var(--space-3);
  }
}


.quote-row .economy-row-copy strong {
  white-space: normal;
}

/* Опубликованные отзывы таскают за ручку — им нужна колонка под неё,
   заявкам на модерации не нужна. */
.quote-row {
  grid-template-columns: minmax(0, 1fr) auto;
}

.quote-row:has(.sort-handle) {
  grid-template-columns: auto minmax(0, 1fr) auto;
}

/* Слева — кто мы, справа — куда идти. Разделы стоят сеткой два на два и
   заодно закрывают пустоту, которая оставалась от мокапа. */
.home-hero {
  display: grid;
  grid-template-columns: minmax(0, 1.05fr) minmax(280px, 0.95fr);
  gap: var(--space-4);
  align-items: center;
}

.home-hero-copy {
  display: grid;
  gap: var(--space-3);
  align-content: start;
}

.home-hero-side {
  display: grid;
  gap: var(--space-2);
  align-content: start;
}

.home-projects {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-2);
}

/* В шапке карточка раздела короткая: иконка, название, подпись. Стрелка тут
   лишняя — по карточке и так понятно, что она ведёт дальше. */
.home-projects .project-card-arrow {
  display: none;
}

/* На узком экране эти карточки стоят по две в ряд, и на текст остаётся всего
   полсотни точек — «Minecraft» переставал влезать и вылезал за рамку. Поджимаем
   иконку и отступы: название важнее, чем размер значка. */
@media (max-width: 430px) {
  .home-projects .project-card {
    grid-template-columns: 38px minmax(0, 1fr);
    gap: 10px;
    padding: 13px;
  }

  .home-projects .project-card-icon {
    width: 38px;
    min-width: 38px;
    max-width: 38px;
    height: 38px;
    min-height: 38px;
    max-height: 38px;
  }
}

@media (max-width: 900px) {
  .home-hero {
    grid-template-columns: minmax(0, 1fr);
  }
}

.home-tagline {
  color: var(--muted);
  font-family: var(--font-display);
  font-size: 15px;
  font-weight: 800;
}

.home-hero p {
  margin: 0;
}

.inline-link {
  padding: 0;
  min-height: 0;
  border: 0;
  background: none;
  color: color-mix(in srgb, var(--accent) 45%, var(--text));
  font: inherit;
  font-weight: 800;
  text-decoration: none;
  cursor: pointer;
}

.inline-link:hover {
  color: var(--text);
}

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

.server-rules {
  display: grid;
  gap: var(--space-3);
  line-height: 1.55;
}

.server-rules p {
  margin: 0;
  color: var(--muted);
}

.server-rules .rule-head {
  display: block;
  margin-bottom: 2px;
  color: var(--text);
  font-family: var(--font-display);
  font-size: 16px;
  font-weight: 900;
}

/* Эмодзи растёт вместе с текстом и сидит на строке, а не под ней. */
.chat-emoji {
  width: 1.25em;
  height: 1.25em;
  vertical-align: -0.28em;
  object-fit: contain;
}

.emoji-strip {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  max-height: 132px;
  overflow-y: auto;
}

.emoji-chip {
  display: grid;
  place-items: center;
  line-height: 1;
  font-size: 20px;
  width: 34px;
  height: 34px;
  min-height: 0;
  padding: 0;
  border: 1px solid transparent;
  border-radius: var(--radius-compact);
  background: none;
  cursor: pointer;
}

.emoji-chip:hover {
  border-color: var(--layer-line);
  background: var(--layer-bg-strong);
}

.emoji-chip img {
  width: 24px;
  height: 24px;
  object-fit: contain;
}

/* Канал или роль в тексте — как в Discord: подсвеченная плашка, а не номер. */
.chat-mention {
  padding: 1px 5px;
  border-radius: 5px;
  background: color-mix(in srgb, var(--discord-blurple) 26%, transparent);
  color: color-mix(in srgb, var(--discord-blurple) 25%, var(--text));
  font-weight: 800;
  white-space: nowrap;
}

/* ── Окно поверх страницы ────────────────────────────────────────────────── */

.modal-backdrop {
  position: fixed;
  inset: 0;
  z-index: 60;
  display: grid;
  place-items: center;
  padding: var(--space-4);
  background: rgb(0 0 0 / 52%);
  -webkit-backdrop-filter: blur(12px) saturate(82%);
  backdrop-filter: blur(12px) saturate(82%);
}

.modal-card {
  position: relative;
  display: grid;
  justify-items: center;
  gap: var(--space-3);
  width: min(100%, 380px);
  padding: var(--space-4);
  border: 1.5px solid var(--layer-line);
  border-radius: var(--radius);
  background: var(--surface-strong);
  box-shadow: 0 24px 60px rgb(0 0 0 / 45%);
  text-align: center;
}

.modal-card strong {
  font-family: var(--font-display);
  font-size: 20px;
  font-weight: 900;
}

.modal-card p {
  color: var(--muted);
  line-height: 1.5;
}

/* Страница, которая без входа не работает вовсе: одна кнопка «Войти» по
   центру панели, вместо своего текста на каждой такой странице. */
.auth-gate {
  display: grid;
  justify-items: center;
  padding: var(--space-5) var(--space-4);
}

.modal-close {
  position: absolute;
  top: 10px;
  right: 10px;
  display: grid;
  place-items: center;
  width: 32px;
  height: 32px;
  min-height: 0;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: none;
  color: var(--muted);
  cursor: pointer;
}

.modal-close:hover {
  background: var(--layer-bg);
  color: var(--text);
}

html.modal-open body {
  overflow: hidden;
}

/* Простые списки внутри панели: пункты через отступ, без маркерной каши. */
.plain-list {
  display: grid;
  gap: var(--space-2);
  margin: 0;
  padding-left: 20px;
  color: var(--muted);
  line-height: 1.5;
}

.plain-list li::marker {
  color: color-mix(in srgb, var(--accent) 60%, var(--muted));
}

/* Подписи кнопок инструмента: на широком экране полные, на телефоне короткие
   — иначе пять кнопок в строке начинают переносить слова. */
.effect-label-short {
  display: none;
}

@media (max-width: 860px) {
  .effect-label-full {
    display: none;
  }

  .effect-label-short {
    display: inline;
  }

  .effect-actions button {
    white-space: nowrap;
  }
}

/* Тема и движение на страницах без верхней панели: те же переключатели, что
   в шапке сайта, прижаты к правому краю. */
.page-back-tools {
  display: flex;
  flex: 0 0 auto;
  align-items: center;
  gap: var(--space-2);
  margin-left: auto;
}

.page-back-tools .icon-button {
  display: grid;
  place-items: center;
  width: 40px;
  min-width: 40px;
  height: 40px;
  min-height: 40px;
  padding: 0;
  border-radius: var(--radius-control);
  line-height: 0;
}

.page-back-tools svg {
  display: block;
  width: 22px;
  height: 22px;
}

/* Майнкрафт: пока сервера нет, на странице одно слово во весь экран. */
.soon-screen {
  display: grid;
  place-items: center;
  min-height: min(62vh, 520px);
}

.soon-screen strong {
  font-family: var(--font-display);
  font-size: clamp(72px, 22vw, 260px);
  font-weight: 900;
  line-height: 1;
  letter-spacing: -0.02em;
  background: linear-gradient(180deg, var(--text), color-mix(in srgb, var(--text) 35%, transparent));
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

/* Заглушка техработ: то же крупное слово, что и «Скоро», плюс пояснение. */
.maintenance-screen {
  gap: var(--space-3);
  text-align: center;
}

.maintenance-screen strong {
  font-size: clamp(38px, 9vw, 92px);
}

.maintenance-screen p {
  color: var(--muted);
  font-size: 17px;
}

/* Тумблер техработ стоит первым в панели сайта — это рубильник, а не настройка. */
.admin-maintenance {
  margin-bottom: var(--space-3);
}

/* ── «Сейчас на сервере»: карточки просторнее ────────────────────────────────
   В ленту ивентов теперь попадают четыре разных источника, и трёх строк на
   104 пикселя стало мало. Высоту держим общей для всех карточек ряда — иначе
   они разъезжаются, — а вместе с ней подрастает и кегль: в прежнем размере
   текст тонул бы в появившемся воздухе. */
.current-activity-scroll {
  max-height: 168px;
}

.current-activity-card {
  gap: var(--space-3);
  padding: var(--space-4);
}

.current-activity-card > span:nth-child(2) strong {
  font-size: 19px;
}

.current-activity-card > span:nth-child(2) small,
.current-activity-card small {
  font-size: 12.5px;
}

.current-activity-list {
  gap: 8px;
}

.activity-line {
  padding: 9px 12px;
}

.activity-line b {
  font-size: 14px;
}

.activity-line small {
  font-size: 12px;
}

.activity-line em {
  font-size: 12px;
}

@media (max-width: 760px) {
  .current-activity-scroll {
    max-height: 200px;
  }
}

/* Верхний ряд — задания: там одна строка текста, и тянуть его под высоту
   списков незачем. Нижний ряд — ивенты и именинники, им нужен размах.
   Внутри ряда высоты выравниваются сами. */
.current-activity-grid > .current-activity-card:nth-child(-n + 2)
  .current-activity-list {
  max-height: none;
}

.current-activity-grid > .current-activity-card:nth-child(n + 3)
  .current-activity-scroll {
  max-height: 152px;
}

/* Справа в ленте — когда всё закончится. Слово подписью сверху, чтобы дата
   не читалась как непонятно чей штамп. */
.activity-line em i {
  display: block;
  color: var(--muted);
  font-size: 11px;
  font-style: normal;
  font-weight: 750;
  line-height: 1;
  margin-bottom: 2px;
}

.activity-line em {
  text-align: right;
  line-height: 1.2;
}

/* ── Утилиты с картинкой ─────────────────────────────────────────────────────
   Демотиватор и шакализатор живут на той же разметке, что эффект-мейкер:
   одна ячейка под картинку, один ряд кнопок, одни ползунки. Три инструмента
   рядом должны выглядеть роднёй, а не тремя разными сайтами. */
/* Высоту держим, только пока картинки нет: с картинкой рамка обнимает её,
   иначе вокруг остаётся пустое поле в пол-экрана. */
.effect-stage [data-demotivator-drop]:not(.is-loaded),
.effect-stage [data-deepfry-drop]:not(.is-loaded) {
  min-height: min(48vh, 400px);
}

.effect-stage [data-demotivator-drop],
.effect-stage [data-deepfry-drop] {
  width: 100%;
}

.effect-stage canvas.is-empty {
  display: none;
}

.effect-drop.is-over {
  border-color: var(--accent);
  background: var(--layer-bg-strong);
}

/* Поля демотиватора — обычные поля формы, как везде на сайте. В колонке
   настроек им нужна только полная ширина. */
.effect-controls > .create-field {
  width: 100%;
}

/* ── Плашка о выполненном задании ────────────────────────────────────────── */

.quest-toasts {
  position: fixed;
  right: clamp(12px, 3vw, 26px);
  bottom: clamp(12px, 3vw, 26px);
  z-index: 70;
  display: grid;
  gap: 10px;
  justify-items: end;
  pointer-events: none;
}

.quest-toast {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  gap: var(--space-3);
  align-items: center;
  width: min(92vw, 360px);
  padding: 14px 16px;
  border: 1.5px solid var(--layer-line);
  border-radius: var(--radius);
  background: var(--surface-strong);
  box-shadow: 0 18px 44px rgb(0 0 0 / 42%);
  animation: quest-toast-in 520ms cubic-bezier(0.16, 1, 0.3, 1) both;
}

.quest-toast.is-leaving {
  animation: quest-toast-out 380ms ease-in forwards;
}

/* Галочка обводится по кругу: линия рисуется, а не появляется целиком. */
.quest-toast-mark {
  display: grid;
  place-items: center;
  line-height: 1;
  font-size: 22px;
  width: 42px;
  height: 42px;
  border-radius: 50%;
  background: color-mix(in srgb, var(--accent) 22%, transparent);
  color: var(--accent);
}

.quest-toast.is-daily .quest-toast-mark {
  background: color-mix(in srgb, var(--amber, #f59e0b) 22%, transparent);
  color: var(--amber, #f59e0b);
}

.quest-toast.is-shroom .quest-toast-mark {
  background: color-mix(in srgb, var(--positive, #22c55e) 22%, transparent);
  color: var(--positive, #22c55e);
}

.quest-toast.is-shroom-miss .quest-toast-mark {
  background: var(--layer-bg-strong);
  color: var(--muted);
}

.quest-toast-mark path {
  stroke-dasharray: 26;
  stroke-dashoffset: 26;
  animation: quest-toast-check 460ms 180ms ease-out forwards;
}

.quest-toast-copy {
  min-width: 0;
}

.quest-toast-copy small {
  display: flex;
  align-items: center;
  gap: 8px;
  color: var(--muted);
  font-size: 11.5px;
  font-weight: 750;
}

.quest-toast-first {
  padding: 1px 7px;
  border-radius: 999px;
  background: color-mix(in srgb, var(--amber, #f59e0b) 26%, transparent);
  color: var(--amber, #f59e0b);
  font-size: 10.5px;
  font-style: normal;
  font-weight: 800;
}

.quest-toast-copy strong {
  display: block;
  overflow: hidden;
  margin-top: 3px;
  font-size: 16px;
  line-height: 1.2;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.quest-toast-reward {
  color: var(--positive, #22c55e);
  font-size: 19px;
  font-variant-numeric: tabular-nums;
  animation: quest-toast-reward 620ms 260ms cubic-bezier(0.16, 1, 0.3, 1) both;
}

@keyframes quest-toast-in {
  from { opacity: 0; transform: translate3d(0, 22px, 0) scale(0.94); }
  to   { opacity: 1; transform: none; }
}

@keyframes quest-toast-out {
  to { opacity: 0; transform: translate3d(0, 12px, 0) scale(0.97); }
}

@keyframes quest-toast-check {
  to { stroke-dashoffset: 0; }
}

@keyframes quest-toast-reward {
  from { opacity: 0; transform: translate3d(0, 8px, 0) scale(0.8); }
  to   { opacity: 1; transform: none; }
}

/* При выключенных анимациях плашка просто появляется и просто исчезает. */
html[data-motion="off"] .quest-toast,
html[data-motion="off"] .quest-toast-mark path,
html[data-motion="off"] .quest-toast-reward {
  animation: none;
}

html[data-motion="off"] .quest-toast-mark path {
  stroke-dashoffset: 0;
}

/* ── Гриб, всплывающий на сайте сам по себе ──────────────────────────────── */

.shroom-drop {
  position: fixed;
  z-index: 45;
  width: 46px;
  height: 46px;
  padding: 0;
  border: none;
  background: none;
  cursor: grab;
  /* Своя логика перетаскивания — жест не должен скроллить страницу под ним. */
  touch-action: none;
  -webkit-tap-highlight-color: transparent;
  filter: drop-shadow(0 8px 18px rgb(0 0 0 / 40%));
  animation:
    shroom-in 420ms cubic-bezier(0.16, 1, 0.3, 1) both,
    shroom-bob 2400ms ease-in-out 420ms infinite;
}

.shroom-drop.is-dragging {
  cursor: grabbing;
  /* Пока держат — без болтанки от bob-анимации, положение целиком в руках. */
  animation-play-state: paused;
}

.shroom-drop img {
  display: block;
  width: 100%;
  height: 100%;
  pointer-events: none;
}

.shroom-drop.is-leaving {
  animation: shroom-out 320ms ease-in forwards;
}

/* Поворот у каждого гриба свой (--поворот, ставится в JS) и должен
   держаться через все три анимации разом — иначе следующий кадр анимации
   переписывает transform целиком и поворот пропадает. */
@keyframes shroom-in {
  from { opacity: 0; transform: scale(0.4) rotate(var(--поворот, 0deg)); }
  to   { opacity: 1; transform: scale(1) rotate(var(--поворот, 0deg)); }
}

@keyframes shroom-out {
  to { opacity: 0; transform: scale(0.5) rotate(var(--поворот, 0deg)); }
}

@keyframes shroom-bob {
  0%, 100% { transform: translateY(0) rotate(var(--поворот, 0deg)); }
  50%      { transform: translateY(-8px) rotate(var(--поворот, 0deg)); }
}

html[data-motion="off"] .shroom-drop {
  animation: none;
  transform: rotate(var(--поворот, 0deg));
}

/* ── Сброс данных ────────────────────────────────────────────────────────── */

/* Панель выглядит опаснее соседних намеренно: сюда не заходят «посмотреть». */
.admin-wipe {
  border-color: color-mix(in srgb, var(--accent) 40%, var(--layer-line));
}

.admin-wipe-full {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-3);
  border: 1px solid color-mix(in srgb, var(--accent) 45%, var(--layer-line));
  border-radius: var(--radius-compact);
  background: color-mix(in srgb, var(--accent) 8%, var(--layer-bg));
  cursor: pointer;
}

.admin-wipe-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: var(--space-2);
  margin-top: var(--space-3);
}

.admin-wipe-item {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-3);
  border: 1px solid var(--layer-line);
  border-radius: var(--radius-compact);
  background: var(--layer-bg);
  cursor: pointer;
}

.admin-wipe-item.is-on {
  border-color: color-mix(in srgb, var(--accent) 50%, var(--layer-line));
  background: color-mix(in srgb, var(--accent) 10%, var(--layer-bg));
}

.admin-wipe-item span,
.admin-wipe-full span {
  display: grid;
  gap: 2px;
  min-width: 0;
}

.admin-wipe-item small,
.admin-wipe-full small {
  color: var(--muted);
  font-size: 11px;
  font-weight: 700;
}

.admin-wipe-item input:disabled + span {
  opacity: 0.45;
}

.admin-wipe-numbers {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: var(--space-2);
  margin-top: var(--space-3);
}

.admin-wipe-numbers small {
  color: var(--muted);
  font-size: 11px;
  font-weight: 700;
}

.admin-wipe-preview {
  margin-top: var(--space-3);
}

.admin-wipe-list {
  display: grid;
  gap: 6px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.admin-wipe-list li {
  display: flex;
  justify-content: space-between;
  gap: var(--space-3);
  padding: 6px 10px;
  border-radius: var(--radius-compact);
  background: var(--layer-bg);
  font-size: 12px;
  font-weight: 750;
}

.admin-wipe-list span {
  color: var(--muted);
}

.admin-wipe-confirm {
  display: flex;
  flex-wrap: wrap;
  align-items: end;
  gap: var(--space-2);
  margin-top: var(--space-3);
}

.admin-wipe-confirm .create-field {
  flex: 1 1 200px;
}

.admin-wipe-go:not(:disabled) {
  border-color: var(--accent);
  background: color-mix(in srgb, var(--accent) 22%, var(--layer-bg-strong));
  color: var(--text);
}

/* ── Угасание у прокручиваемых списков ───────────────────────────────────── */

/* Канон: всё, что листается, гаснет по краям — иначе обрезанный элемент
   читается как сломанная вёрстка, а не как «дальше есть ещё».
   Классы уже перечислены в FADED_LISTS: скрипт проставляет is-scrolled-*,
   но без маски это ничего не меняло — у девяти списков её просто не было. */
.quiz-list,
.admin-catalog,
.site-search-results,
.choice-popover,
.choice-menu-options,
.debug-list,
.emoji-strip {
  mask-image: linear-gradient(to bottom,
    transparent 0, #000 var(--fade-top, 0px),
    #000 calc(100% - var(--fade-bottom, 0px)), transparent 100%);
}

/* Ленты листаются вбок, поэтому и гаснут по бокам. */
.segmented-control,
.people-tabs {
  mask-image: linear-gradient(to right,
    transparent 0, #000 var(--fade-left, 0px),
    #000 calc(100% - var(--fade-right, 0px)), transparent 100%);
}

/* В полосе утилит сегменты не прокручиваются вовсе — гасить там нечего. */
.effect-bar > .segmented-control {
  mask-image: none;
}

/* ── Полоса утилит на телефоне ───────────────────────────────────────────── */

@media (max-width: 620px) {
  /* Ряды во всю ширину и кнопки поровну: иначе строка обрывается рваным
     краем справа, а ширины скачут от «Рандом» к «×». */
  .effect-bar > .effect-group,
  .effect-bar > .effect-actions {
    flex: 1 1 auto;
  }

  .effect-bar button {
    flex: 1 1 0;
    min-width: 0;
  }

  .effect-bar .effect-clear {
    flex: 0 0 44px;
  }

  /* Образец фона — квадрат: ширину задаём не числом, а по высоте кнопки,
     иначе на телефоне выходил прямоугольник 36×38. */
  .effect-bar .effect-swatch {
    flex: 0 0 auto;
    width: auto;
    aspect-ratio: 1;
  }

  /* Режимы и образцы фона стоят одной строкой: это один выбор — каким будет
     кадр, — и разносить его по двум рядам незачем. Подписи ужимаются, но
     остаются целыми словами. */
  .effect-bar > .demo-modes {
    flex: 1 1 auto;
    min-width: 0;
  }

  .effect-bar > .demo-swatches {
    flex: 0 0 auto;
    justify-content: flex-start;
    gap: 6px;
  }

  .demo-modes button {
    flex: 0 1 auto;
    min-width: 0;
    padding: 0 8px;
    font-size: 12px;
  }

  /* Разделитель между группами при переносе оказывается в начале строки и
     читается как случайная палка. Группы и так стоят разными рядами. */
  .effect-bar > .effect-group + .effect-group {
    padding-left: 0;
    border-left: 0;
  }

  /* Пустое поле под картинку — по месту, а не в пол-экрана: до кнопок под
     ним иначе надо докручивать. */
  .effect-stage [data-demotivator-drop]:not(.is-loaded),
  .effect-stage [data-deepfry-drop]:not(.is-loaded),
  .effect-stage [data-bubbles-drop]:not(.is-loaded),
  .effect-drop:not(.is-loaded) {
    min-height: 168px;
  }
}

/* ── Значок грибочков не подчиняется чужим правилам для картинок ─────────── */

/* Значок стоит внутри числа, а числа мы вставляем куда угодно — в том числе в
   списки, где у картинок задан размер аватарки. Такое правило (например
   `.admin-member-row img`) специфичнее одного класса и раздувало значок с
   кегля строки до тридцати пикселей: строка вырастала в полтора раза, текст
   с аватаркой разъезжались. Здесь специфичность та же, но правило идёт
   последним — и размер по кеглю выигрывает у любого списка. */
img.coin {
  width: 1.08em;
  height: 1.08em;
  min-width: 0;
  min-height: 0;
  max-width: none;
  max-height: none;
  border-radius: 0;
}


/* Кнопка обмена под списком инвентаря: панель одна, содержимое меняется. */
.inventory-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: flex-end;
  gap: var(--space-2);
  margin-top: var(--space-2);
}

/* В узкой колонке дашборда сводка и две кнопки в одну строку не помещаются:
   сводка уходит на свою строку целиком, а кнопки остаются справа и не рвут
   надписи посередине. */
.inventory-actions .compact-button {
  white-space: nowrap;
}

/* Сводка забирает свободное место слева, кнопки жмутся к правому краю. */
.inventory-summary {
  flex: 1 1 200px;
  min-width: 0;
  /* Перечисление ступеней переносится на новые строки, а не обрывается
     многоточием: места под ним достаточно, а обрезанная сводка бесполезна. */
  color: var(--muted);
  font-size: 11px;
  line-height: 1.5;
}

/* Слоты ставки — цель перетаскивания. Их всегда пять, и они не съезжают. */
.swap-slots {
  display: grid;
  /* Квадраты по 62 пикселя: растянутые во всю ширину слоты выглядели как
     пустые плиты и съедали пол-экрана. Стоят по центру — там же появляется
     выпавшая вещь, и взгляду некуда прыгать. */
  grid-template-columns: repeat(5, minmax(0, 62px));
  justify-content: center;
  gap: 7px;
  margin-top: 12px;
  padding: 8px;
  border: 1px dashed var(--layer-line);
  border-radius: var(--radius-compact);
  transition: border-color 0.15s ease, background 0.15s ease;
}

.swap-slots.is-target {
  border-color: var(--accent, #c0392b);
  background: color-mix(in srgb, var(--accent, #c0392b) 12%, transparent);
}

.swap-slot {
  line-height: 1;
  display: grid;
  place-items: center;
  width: 100%;
  max-width: 62px;
  height: 62px;
  padding: 0;
  border: 1px solid var(--layer-line);
  border-radius: var(--radius-compact);
  background: var(--layer-bg);
  cursor: default;
  font-size: 32px;
}

.swap-slot.is-filled {
  border-style: solid;
  border-color: var(--accent, #c0392b);
  cursor: pointer;
}

.swap-hint {
  margin-top: 8px;
  text-align: center;
}

/* Слоты сгорают, на их месте вырастает выпавшая вещь. */
.swap-slots.is-prize {
  display: grid;
  grid-template-columns: 1fr;
  place-items: center;
  border-style: solid;
  border-color: var(--accent, #c0392b);
  animation: swap-flash 0.45s ease-out;
}

.swap-prize {
  display: grid;
  justify-items: center;
  gap: 2px;
  padding: 4px 0;
  animation: swap-rise 0.45s ease-out;
}

.swap-prize-emoji {
  font-size: 34px;
  line-height: 1;
}

.swap-prize-emoji img,
.swap-prize-emoji .unicode-emoji {
  display: block;
  width: 38px;
  height: 38px;
}

.swap-prize small {
  color: var(--muted);
  font-size: 11px;
}

@keyframes swap-flash {
  from { background: color-mix(in srgb, var(--accent, #c0392b) 45%, transparent); }
  to { background: transparent; }
}

@keyframes swap-rise {
  from { opacity: 0; transform: scale(0.6) translateY(8px); }
  to { opacity: 1; transform: none; }
}

/* Слот, из которого вещь ушла в обмен, коротко вспыхивает. */
@media (prefers-reduced-motion: reduce) {
  .swap-slots.is-prize,
  .swap-prize {
    animation: none;
  }
}

/* Слоты и подсказка отнимают высоту у списка, чтобы панель обмена была
   ровно такой же, как инвентарь, и ничего под ней не прыгало. */
.swap-source-list {
  max-height: max(180px, min(680px, 72vh) - 150px);
}

/* Список-источник той же высоты, что инвентарь: панель не прыгает. */
.swap-source {
  cursor: grab;
  touch-action: pan-y;
  user-select: none;
}

.swap-source.is-dim {
  cursor: default;
  opacity: 0.4;
}

/* Части подписи стоят через отступ, без точек-разделителей: на сайте есть
   вёрстка, и пунктуация там, где хватает пустого места, только шумит. */
.meta {
  /* Именно inline, а не flex: flex-контейнер не сжимается ниже содержимого и
     раздувает ячейку сетки — панель начинает вылезать за свои границы.
     Обычный текст переносится и обрезается ровно так же, как раньше. */
  display: inline;
}

.meta > span {
  margin-inline-end: 14px;
  /* Часть подписи переносится целиком: «Обычный ×994» и «Цена: 1 400» —
     каждая одно целое, и разрывать их посреди пробела нельзя. */
  white-space: nowrap;
}

.meta > span:last-child {
  margin-inline-end: 0;
}

.meta > span:empty {
  display: none;
}

/* Строка с редкостью и ценой: она короткая и должна быть видна целиком,
   поэтому переносится, а не обрезается многоточием следом за описанием.
   Селектор с классом строки — иначе правило `.economy-row-copy small`
   с его nowrap перебивает по весу. */
.meta-row,
.economy-row-copy .meta-row,
.inventory-row .meta-row {
  overflow: visible;
  white-space: normal;
}

/* Второстепенная часть внутри заголовка — приглушена, а не отделена точкой. */
.meta-inline {
  color: var(--muted);
  font-weight: 500;
}

/* Ручка переноса строки: браться за неё, а не за весь блок, — иначе нельзя
   ни выделить текст, ни прокрутить список пальцем. */
.sort-handle {
  align-self: center;
  padding: 0 4px;
  color: var(--muted);
  cursor: grab;
  font-size: 15px;
  line-height: 1;
  touch-action: none;
  user-select: none;
}

[data-sortable] .economy-list-row.is-dragging {
  opacity: 0.65;
  border-color: var(--accent, #c0392b);
  cursor: grabbing;
}

[data-sortable].is-sorting {
  cursor: grabbing;
}

[data-sortable].is-sorting .economy-list-row:not(.is-dragging) {
  transition: transform 0.12s ease;
}

.bunker-category-tabs {
  margin-bottom: var(--space-4);
  overflow-x: auto;
  scrollbar-width: thin;
}

.bunker-category-tabs > button {
  flex: 0 0 auto;
}

.bunker-admin-layout {
  display: grid;
  grid-template-columns: minmax(0, 1.2fr) minmax(18rem, 0.8fr);
  gap: var(--space-4);
  align-items: start;
}

.bunker-entry-list {
  max-height: 34rem;
  overflow-y: auto;
}

.bunker-entry-list .economy-list-row.is-muted {
  opacity: 0.58;
}

.bunker-editor {
  position: sticky;
  top: calc(var(--header-height, 0px) + var(--space-3));
}

.bunker-import {
  margin-top: var(--space-4);
}

.bunker-import > summary {
  cursor: pointer;
  font-weight: 700;
}

.bunker-import .create-item-form {
  margin-top: var(--space-3);
}

@media (max-width: 760px) {
  .bunker-admin-layout {
    grid-template-columns: minmax(0, 1fr);
  }

  .bunker-editor {
    position: static;
  }
}

/* Летит за пальцем и ничего не перехватывает. */
.swap-ghost {
  line-height: 1;
  position: fixed;
  top: 0;
  left: 0;
  z-index: 9000;
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  border: 1px solid var(--layer-line);
  border-radius: var(--radius-compact);
  background: var(--layer-bg);
  font-size: 22px;
  pointer-events: none;
}

/* Серверный каркас публичной страницы остаётся полезным без JavaScript и
   даёт поисковику тот же смысл, который после загрузки показывает интерфейс. */
.seo-fallback-panel {
  display: grid;
  gap: var(--space-4);
  max-width: 860px;
  margin-inline: auto;
}

.seo-fallback-panel h1,
.privacy-heading h1,
.privacy-copy h2 {
  margin: 0;
  font-family: var(--font-display);
  font-weight: 900;
}

.seo-fallback-panel h1,
.privacy-heading h1 {
  font-size: clamp(28px, 5vw, 44px);
  line-height: 1.08;
}

.seo-fallback-panel p,
.privacy-copy p {
  margin: 0;
  color: var(--muted);
  line-height: 1.6;
}

.seo-fallback-panel nav {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
}

.seo-fallback-panel nav a {
  min-height: var(--control-height);
  padding: 10px 14px;
  border: 1.5px solid var(--layer-line);
  border-radius: var(--radius-compact);
  color: var(--text);
  font-weight: 800;
  text-decoration: none;
}

.privacy-panel {
  display: grid;
  gap: var(--space-5);
}

.privacy-heading {
  display: grid;
  gap: var(--space-2);
}

.privacy-heading p {
  margin: 0;
  color: var(--muted);
}

.privacy-copy {
  display: grid;
  gap: var(--space-5);
}

.privacy-copy section {
  display: grid;
  gap: var(--space-2);
  scroll-margin-top: calc(var(--header-height, 0px) + var(--space-4));
}

.privacy-copy h2 {
  font-size: 19px;
}

.privacy-delete-button {
  width: fit-content;
  text-decoration: none;
}

.privacy-actions {
  display: grid;
  justify-items: start;
  gap: var(--space-2);
  margin-top: var(--space-2);
}

.privacy-actions .inline-status {
  max-width: min(100%, 620px);
}

@media (max-width: 620px) {
  .privacy-actions {
    justify-items: stretch;
  }

  .privacy-delete-button {
    width: 100%;
  }
}

/* ── Единая мобильная доводка списков и утилит ─────────────────────────── */

.news-stickers {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin-top: var(--space-2);
}

.news-stickers a {
  display: grid;
  place-items: center;
  width: 132px;
  height: 132px;
  overflow: hidden;
  border-radius: var(--radius-compact);
  background: var(--layer-bg-strong);
}

.news-stickers img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
}

.inventory-action-buttons {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: var(--space-2);
}

.swap-hint-touch {
  display: none;
}

/* Обычный и серверный эмодзи занимают одну оптическую область. Иначе
   Twemoji сидит ниже, а картинка Discord прилипает к левому краю плитки. */
.inventory-emoji img,
.inventory-emoji .unicode-emoji {
  margin: auto;
  vertical-align: middle;
}

.inventory-emoji .unicode-emoji {
  display: grid;
  place-items: center;
  width: 24px;
  height: 24px;
  transform: translateY(-1px);
}

.swap-slot img.discord-emoji,
.swap-slot .unicode-emoji {
  display: grid;
  place-items: center;
  width: 34px;
  height: 34px;
  margin: auto;
  font-size: 31px;
  line-height: 1;
  object-fit: contain;
  vertical-align: middle;
}

/* Вставка строк в длинный список не должна менять точку привязки страницы.
   Позицию дополнительно сохраняет JS, а это отключает автокоррекцию браузера. */
.page-stage,
.news-list,
.role-cloud-scroll,
.permission-grid,
.notification-list,
.economy-scroll,
.family-roster,
.leaderboard-list,
.people-list,
.operation-list,
.inventory-list,
.admin-queue,
.current-activity-scroll,
.saved-list,
.quiz-list,
.admin-catalog,
.admin-birthday-list,
.admin-logs,
.admin-member-list,
.clothing-strip,
.effect-controls,
.quote-grid,
.site-search-results,
.segmented-control,
.people-tabs,
.choice-popover,
.choice-menu-options {
  overflow-anchor: none;
}

.debug-list,
.emoji-strip,
[data-keep-scroll] {
  overflow-anchor: none;
}

@media (max-width: 620px) {
  /* На дробном CSS-пикселе Safari оставлял справа и снизу тонкую полосу
     подложки. Небольшой overscan остаётся внутри уже обрезанной иконки. */
  .brand-mark.site-logo,
  .home-logo.site-logo {
    background-size: calc(100% + 2px) calc(100% + 2px);
  }

  .news-stickers a {
    width: 112px;
    height: 112px;
  }

  .swap-hint-pointer {
    display: none;
  }

  .swap-hint-touch {
    display: inline;
  }

  /* Сводка всегда одной строкой над действиями. Кнопки не пытаются занять
     остаток этой же строки и потому не ломают подписи редкостей. */
  .inventory-actions {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    align-items: stretch;
  }

  .inventory-summary,
  .inventory-action-buttons {
    width: 100%;
  }

  .inventory-summary {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 2px 16px;
  }

  .inventory-action-buttons {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .inventory-action-buttons .compact-button {
    width: 100%;
    min-width: 0;
  }

  .inventory-action-buttons .compact-button:only-child {
    grid-column: 1 / -1;
  }

  /* На телефоне у каждого инструмента одна и та же компактная схема:
     настройки занимают свои ряды, а четыре системных действия стоят ровной
     полосой. Цветом отделяем получение файла от очистки результата. */
  .effect-bar {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    align-items: stretch;
    gap: var(--space-2);
  }

  .effect-bar > .effect-group {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(82px, 1fr));
    width: 100%;
    gap: 6px;
  }

  /* Режимы и образцы фона — одна строка: обёртка раскладывает их сама,
     общая сетка полосы её не трогает. */
  .effect-bar > .demo-choice {
    display: flex;
    flex-wrap: nowrap;
    align-items: stretch;
    gap: 6px;
    width: 100%;
  }

  .demo-choice .demo-modes {
    display: flex;
    flex: 1 1 auto;
    flex-wrap: nowrap;
    min-width: 0;
    width: auto;
  }

  .demo-choice .demo-swatches {
    display: flex;
    flex: 0 0 auto;
    flex-wrap: nowrap;
    width: auto;
  }

  .demo-modes button {
    flex: 1 1 0;
    min-width: 0;
    padding: 0 4px;
    font-size: 11px;
    white-space: nowrap;
  }

  .effect-bar > .effect-group + .effect-group {
    padding: 0;
    border: 0;
  }

  .effect-bar > .effect-actions {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    width: 100%;
    margin: 0;
    gap: 6px;
  }

  .effect-action-row {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 6px;
  }

  /* Разделитель между инструментами и действиями на узком экране упирался
     в левый край и висел отдельной чертой — убираем. */
  .effect-bar > .effect-group + .effect-actions,
  .effect-bar > .effect-group + .effect-group {
    padding-left: 0;
  }

  .effect-bar > .effect-group + .effect-actions::before,
  .effect-bar > .effect-group + .effect-group::before {
    display: none;
  }



  .effect-action-row > button {
    width: 100%;
    min-width: 0;
    height: 40px;
    padding: 0 6px;
    justify-self: stretch;
  }

  .effect-download,
  .effect-reset,
  .effect-clear-action {
    /* Синим на сайте помечен выбор, а не важность: подсвеченная им кнопка
       скачивания читалась как «выбрано». Все действия выглядят одинаково,
       различают их подписи. */
    border-color: var(--layer-line);
    background: var(--layer-bg-strong);
  }


}
