/* ===== src/css/tokens.css ===== */
/* ==========================================================================
   tokens.css — ЕДИНСТВЕННОЕ МЕСТО, ГДЕ ЗАДАНЫ ЦВЕТА, РАЗМЕРЫ И ОТСТУПЫ
   ==========================================================================
   Правило проекта: ни один другой CSS-файл не содержит цветовых литералов и
   «магических» пикселей. Хочешь другой акцент — правишь одну строку здесь.
   ========================================================================== */

:root {
  /* --- фоны и панели ---------------------------------------------------- */
  /*  Палитра взята из макета заставки: глубокая синева вместо нейтрально-чёрной
     и ДВЕ линии рамки. --line — рамка окна и карточки, --line2 — рамка того, что
     лежит ВНУТРИ окна (поле, сегмент выбора, клавиша): так вложенность видна без
     лишних теней. */
  --bg0: #03060d;              /* самый глубокий фон */
  --bg1: #080f1a;
  --line: #17263a;
  --line2: #223650;
  --panel-b: #17263a;          /* рамка стеклянных окон */

  /*  Окна намеренно НЕплотные. Кнопки в игре полупрозрачные (--cyan-15 и
     родственные), и окна сделаны такими же: сквозь ангар, панель базы, радар и
     подсказки должна угадываться игра. Иначе окно читается как глухая плита
     поверх мира, и «кабина корабля» превращается в набор наклеек.
     Значение — ВЕСЬ фон панели, то есть градиент, а не один цвет: в макете
     верхняя кромка окна светлее нижней, и объём читается без второй рамки.
     Каждое правило пишет `background: var(--panel-glass)`, поэтому смена
     градиента здесь меняет все окна сразу.
     ПОЧЕМУ ТАКОЙ СВЕТЛЫЙ. Раньше здесь стояло 0.72 от тёмно-синего, и на
     почти чёрном полотне окно сливалось с фоном: владелец видел контур и
     пустоту внутри — «поля пустые». Теперь окно заметно светлее фона, а поле
     внутри него (--panel-inset) — заметно темнее окна. Пара «светлое окно,
     тёмное поле» и есть та вложенность, что в макете. */
  --panel-glass: linear-gradient(180deg, rgba(20, 33, 52, 0.84), rgba(11, 18, 31, 0.86));
  /*  Чуть плотнее — там, где под стеклом мелкий текст или прозрачный канвас
     радара: игровое свечение не должно съедать подписи. */
  --panel-glass-strong: linear-gradient(180deg, rgba(16, 27, 44, 0.93), rgba(8, 14, 24, 0.95));

  /*  Вставные поля: позывной на заставке, сегмент сложности, ячейки телепорта,
     строки радара, вкладки, карточки ангара. Они ТЕМНЕЕ окна — «углубление»
     должно читаться как углубление, а не как ещё одна панель. */
  --panel-inset: rgba(2, 4, 9, 0.88);
  /*  Клавиша светлее окна: она лежит НА панели и должна читаться как предмет,
     который нажимают, а не как вырез в ней. */
  --panel-key: #16263a;

  /*  Плотная заливка осталась ровно одна и только у мелкого элемента прямо на
     полотне: у полосы выгрузки трюма нет запаса по площади, и прозрачность
     стоила бы читаемости белых цифр прогресса. */
  --panel-solid: rgba(6, 11, 20, 0.96);
  /*  Затемнение под модальными экранами. Оно плотнее стекла, потому что на
     экранах лежит мелкий серый --ink-dim, но игра сквозь него всё равно
     угадывается — иначе экран читался бы как отдельное приложение.
     --scrim-light — те же два мягких пятна, что в макете: фиолетовое сверху и
     бирюзово-розовые по нижним углам. Это свет, а не текстура: он не спорит ни
     с текстом, ни с размытием полотна. */
  --scrim: rgba(3, 6, 13, 0.72);
  --scrim-light:
    radial-gradient(60% 40% at 50% 0%, rgba(169, 139, 255, 0.16), transparent 70%),
    radial-gradient(40% 30% at 15% 90%, rgba(94, 242, 220, 0.08), transparent 70%),
    radial-gradient(40% 30% at 85% 85%, rgba(255, 95, 134, 0.08), transparent 70%);

  /*  Один рецепт матовости на все окна, чтобы они не различались силой
     размытия от файла к файлу. Экранам нужно сильнее: на них лежит мелкий
     серый текст, а тач-кнопкам слабее — они и так мелкие. */
  --blur-glass: 14px;
  --blur-screen: 20px;
  --blur-touch: 8px;

  /* --- акценты ----------------------------------------------------------- */
  /*  Пять цветов макета. --blue существует только как середина градиента: в
     самом интерфейсе он не встречается отдельно, иначе цветов стало бы шесть. */
  --cyan: #5ef2dc;
  --blue: #6aa8ff;
  --violet: #a98bff;
  --amber: #ffb14e;
  --rose: #ff5f86;
  --lime: #8fe37a;
  --gold: #f4cf6a;

  /*  Фирменный градиент заголовков и главной кнопки — четыре цвета в одном
     порядке, как в макете. Он же используется для текста (background-clip), для
     заливки кнопки «новый забег» и для рамок-акцентов. */
  --grad-brand: linear-gradient(90deg,
    var(--cyan) 0%, var(--blue) 30%, var(--violet) 55%, var(--rose) 100%);

  /* --- текст ------------------------------------------------------------- */
  --ink: #e8f1fb;
  --ink-dim: #7f93ab;
  /*  Тёмный текст на акцентной заливке: в макете это почти чёрный с зеленцой,
     он читается и на циане, и на фиолете, и на лайме. */
  --ink-on-accent: #04120f;

  /* --- производные (полупрозрачные заливки и свечения) ------------------- */
  --cyan-15: rgba(94, 242, 220, 0.15);
  --cyan-30: rgba(94, 242, 220, 0.3);
  --cyan-45: rgba(94, 242, 220, 0.45);
  --violet-10: rgba(169, 139, 255, 0.1);
  --violet-15: rgba(169, 139, 255, 0.15);
  --violet-30: rgba(169, 139, 255, 0.3);
  --amber-15: rgba(255, 177, 78, 0.15);
  --amber-30: rgba(255, 177, 78, 0.3);
  /*  Рамка «гибнет станция»: тот же сигнал, что при опасности для корабля, но
      другим цветом — по цвету и видно, ЧТО именно в опасности. */
  --amber-50: rgba(255, 177, 78, 0.5);
  --rose-12: rgba(255, 95, 134, 0.12);
  --rose-20: rgba(255, 95, 134, 0.2);
  --rose-30: rgba(255, 95, 134, 0.3);
  --rose-50: rgba(255, 95, 134, 0.5);
  --lime-12: rgba(143, 227, 122, 0.12);
  --lime-30: rgba(143, 227, 122, 0.3);
  --white-05: rgba(255, 255, 255, 0.05);
  --white-08: rgba(255, 255, 255, 0.08);
  --white-10: rgba(255, 255, 255, 0.1);
  --white-30: rgba(255, 255, 255, 0.3);
  --black-30: rgba(0, 0, 0, 0.3);
  --black-40: rgba(0, 0, 0, 0.4);

  /* --- тени и свечения --------------------------------------------------- */
  /*  Свечение — часть языка макета: главная кнопка и активный сегмент светятся
     своим цветом, окна отбрасывают тень. Всё это здесь, чтобы отклик на
     наведение не собирался из литералов в шести файлах. */
  --glow-cyan: 0 0 18px rgba(94, 242, 220, 0.45);
  --glow-violet: 0 0 22px rgba(169, 139, 255, 0.35);
  --glow-rose: 0 8px 32px rgba(255, 95, 134, 0.35);
  /*  Тень главной кнопки — нейтральная. Было цветное свечение, и вокруг кнопки
     появлялась светлая кайма, которую владелец принял за «белую полосу» у края. */
  --shadow-hero: 0 6px 18px rgba(0, 0, 0, 0.45);
  --shadow-panel: 0 8px 32px rgba(0, 0, 0, 0.45);
  --shadow-screen: 0 24px 60px rgba(0, 0, 0, 0.55);
  --shadow-key: 0 1px 0 rgba(0, 0, 0, 0.45);

  /* --- типографика ------------------------------------------------------- */
  --fs: 'Jost', system-ui, -apple-system, 'Segoe UI', sans-serif;
  --fm: 'JetBrains Mono', ui-monospace, 'Consolas', monospace;
  /*  Дисплейный шрифт заголовков — из макета: широкий, тяжёлый, с запасом по
     кеглю. Он идёт ПЕРВЫМ, а за ним стоит обычный --fs: без сети (или пока
     шрифт грузится) заголовок останется заголовком, а не превратится в
     нечитаемую плашку. */
  --fd: 'Unbounded', var(--fs);
  /* Плавные размеры: на узком экране текст мельче, на широком крупнее,
     без отдельных медиазапросов на каждый размер. */
  --t-xs: clamp(10px, 0.62rem + 0.15vw, 12px);
  --t-sm: clamp(11px, 0.68rem + 0.2vw, 13px);
  --t-md: clamp(13px, 0.78rem + 0.25vw, 15px);
  --t-lg: clamp(16px, 0.9rem + 0.5vw, 20px);
  --t-xl: clamp(22px, 1.2rem + 1.2vw, 34px);
  --t-hero: clamp(30px, 1.6rem + 3.2vw, 58px);

  /* --- геометрия --------------------------------------------------------- */
  --gap-xs: 4px;
  --gap-sm: 8px;
  --gap: 12px;
  --gap-lg: 20px;
  /*  Шаг для разрежённых списков (справка по управлению). Раньше он
     запрашивался в screens.css, но здесь объявлен не был: переменная не
     находилась, объявление gap отбрасывалось целиком, и строки справки
     слипались в один блок. */
  --gap-xl: 28px;
  --pad-sm: 8px;
  --pad: 12px;
  --pad-lg: 20px;
  --pad-xl: 28px;
  /*  Скругления вернулись вместе с макетом и теперь заданы здесь одним
     набором: 6 — вложенная мелочь (клавиша, чип, разделитель), 9 — поле ввода,
     12 — кнопка, сегмент и вкладка, 14 — окно. Раньше здесь стоял ноль, потому
     что интерфейс читался как приборная панель; макет показал «кабину» с
     мягкими формами, и форма окна снова задаётся в ОДНОМ месте — компонент,
     которому нужен другой радиус, ссылается на токен, а не на число. */
  --r-sm: 6px;
  --r: 9px;
  --r-lg: 12px;
  --r-xl: 14px;
  --r-pill: 999px;

  /* Верхний и нижний отступы с учётом «чёлки» и жестовой полосы телефона. */
  --safe-top: env(safe-area-inset-top, 0px);
  --safe-bottom: env(safe-area-inset-bottom, 0px);
  --safe-left: env(safe-area-inset-left, 0px);
  --safe-right: env(safe-area-inset-right, 0px);

  /* Панели HUD: базовые размеры, которые переопределяет layout.css. */
  --topbar-inset: 14px;
  --hud-edge: 16px;
  --radar-width: 340px;
  /*  Насколько поднять переключатели звука над левым нижним краем на узком экране:
      там лежат тач-кнопки (--touch-size), и ряд иначе ложится прямо на них. */
  --sys-lift: 168px;
  /*  Высота строки ресурсов. Нужна, чтобы левая панель начиналась ПОД ней и при
      этом тянулась до низа экрана: без общего числа панель либо наезжала на
      ресурсы, либо оставляла дыру. */
  --topbar-h: 46px;
  --touch-size: 64px;

  /* --- слои ------------------------------------------------------------- */
  --z-world: 0;
  --z-hud: 10;
  --z-toast: 20;
  --z-screen: 30;
  /* Карточки ангара: нужны выше HUD, иначе при наведении карточка поднимается
     и её верхний край срезается соседним слоем. */
  --z-card: 5;

  --dur-fast: 0.15s;
  --dur: 0.2s;
  --ease: cubic-bezier(0.16, 1, 0.3, 1);

  /* --- акцент активного семейства (переопределяется в hangar.css) --------- */
  --accent: var(--violet);
  --accent-15: var(--violet-15);
  --accent-30: var(--violet-30);
  --accent-glow: var(--violet-30);
}

/* ===== src/css/base.css ===== */
/* ==========================================================================
   base.css — сброс, типографика, общие элементы (панель, кнопка, полоса)
   ========================================================================== */

*, *::before, *::after { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
  height: 100%;
  background: var(--bg0);
  color: var(--ink);
  font-family: var(--fs);
  font-size: var(--t-md);
  overflow: hidden;
}

/* ВАЖНО: touch-action:none стоит только на игровом полотне и зонах
   управления. В монолите оно висело на html/body, из-за чего на телефоне
   нельзя было прокрутить ангар — а он по высоте в экран не влезает. */
body {
  position: relative;
  /* Сначала vh, потом dvh — порядок и есть совместимость. Браузер без dvh
     (Chrome < 108, Firefox < 101, Safari < 15.4) отбросит второе объявление и
     возьмёт первое; современный возьмёт последнее.
     Зачем dvh: мобильный браузер прячет адресную строку, и 100vh даёт лишнюю
     высоту — низ интерфейса уезжает под экран. */
  height: 100vh;
  min-height: 100vh;
  height: 100dvh;
  min-height: 100dvh;
  user-select: none;
  -webkit-user-select: none;
  -webkit-tap-highlight-color: transparent;
}

h1, h2, h3, p { margin: 0; }
button { font-family: inherit; color: inherit; background: none; border: none; }
canvas { display: block; }

/* Полосы прокрутки: тонкие и в тон интерфейсу. */
* { scrollbar-width: thin; scrollbar-color: var(--violet-30) transparent; }
::-webkit-scrollbar { width: 6px; height: 6px; }
::-webkit-scrollbar-track { background: rgba(0, 0, 0, 0.2); border-radius: var(--r-sm); }
::-webkit-scrollbar-thumb { background: var(--violet-30); border-radius: var(--r-sm); }
::-webkit-scrollbar-thumb:hover { background: var(--violet); }

/* Текст */
/*  Заголовок и название экрана набраны дисплейным шрифтом макета. Именно он
   делает заставку «своей», а внутренние экраны — её продолжением. Текст
   интерфейса остался на Jost: Unbounded на 12–15px читается плохо, он для
   крупных надписей. */
.t-hero      { font-family: var(--fd); font-size: var(--t-hero); font-weight: 900; letter-spacing: -0.01em; line-height: 1; }
.t-title     { font-family: var(--fd); font-size: var(--t-xl); font-weight: 800; letter-spacing: 0.01em; line-height: 1.15; }
/*  Название экрана («Ангар», «Пауза», «Управление») набрано тем же градиентом,
    что и заголовок заставки: экраны — продолжение макета, а не отдельные окна.
    Цвет текста прозрачный, его рисует градиент под буквами. */
.t-title {
  background-image: var(--grad-brand);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}
.t-lg        { font-size: var(--t-lg); font-weight: 700; }
.t-md        { font-size: var(--t-md); }
.t-sm        { font-size: var(--t-sm); }
.t-xs        { font-size: var(--t-xs); }
.t-mono      { font-family: var(--fm); font-variant-numeric: tabular-nums; }
.t-dim       { color: var(--ink-dim); }
.t-center    { text-align: center; }
.t-right     { text-align: right; }
.t-strong    { font-weight: 700; }
.t-upper     { text-transform: uppercase; letter-spacing: 0.08em; }
.t-cyan { color: var(--cyan); } .t-violet { color: var(--violet); }
.t-amber { color: var(--amber); } .t-rose { color: var(--rose); }
.t-lime { color: var(--lime); } .t-gold { color: var(--gold); }
.t-white { color: #fff; } .t-black { color: var(--ink-on-accent); }

/*  Градиент заголовка — фирменный, из четырёх цветов макета, и с мягким
    свечением под буквами. Свечение задано фильтром, а не text-shadow: цвет
    текста здесь прозрачный (его рисует градиент), и тень от буквы браузер не
    построил бы. */
.t-gradient {
  background-image: var(--grad-brand);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  filter: drop-shadow(0 0 22px var(--violet-30));
}

/* --- общие блоки ---------------------------------------------------------- */
/*  Стекло одно на кнопки и на окна: одна заливка, один радиус размытия.
   Раньше окно брало плотный --panel, и оно отличалось от кнопок настолько, что
   выглядело наклейкой поверх игры.
   Форма окна — из макета: скругление и одна линия рамки. Раньше здесь стоял
   ноль, потому что интерфейс читался как приборная панель; макет задал мягкие
   формы, и токен --r-xl теперь равен 14px (см. tokens.css). */
.glass-panel {
  background: var(--panel-glass);
  backdrop-filter: blur(var(--blur-glass));
  -webkit-backdrop-filter: blur(var(--blur-glass));
  border: 1px solid var(--line);
  border-radius: var(--r-xl);
  box-shadow: var(--shadow-panel);
}

/*  Карточка — стекло с той же градиентной заливкой, что окно в макете, и
   второй линией рамки (--line2): вложенность видна без второй тени. Раньше
   здесь стояла та же заливка, что у полей внутри (--panel-inset), и поля
   сливались с карточкой — заливки было не видно. Теперь карточка светлее
   (стекло), а поле внутри темнее. */
.card {
  background: var(--panel-glass);
  border: 1px solid var(--line2);
  border-radius: var(--r-xl);
  padding: var(--pad);
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  transition: border-color var(--dur) var(--ease), background-color var(--dur) var(--ease),
              box-shadow var(--dur) var(--ease);
  position: relative;
  /* Карточка поднимается при наведении за счёт цвета и тени, а не сдвига:
     сдвиг обрезался рамкой прокручиваемой области. */
  z-index: var(--z-card);
}
.card:hover {
  border-color: var(--accent);
  box-shadow: 0 0 0 1px var(--accent-30), var(--shadow-panel);
}

.divider-v { width: 1px; background: var(--white-10); align-self: stretch; }
.divider-h { height: 1px; background: var(--white-10); }

/* --- кнопки --------------------------------------------------------------- */
/*  Кнопка по умолчанию — вторичная: заливка «вставного» поля, одна линия рамки
   и скругление 12px, как в макете. Раньше у базовой .btn не было ни фона, ни
   рамки, и кнопки без варианта («ЗАГРУЗИТЬ», «В МЕНЮ», «ЗАКРЫТЬ») выглядели
   просто текстом — по ним не видно, что по ним можно нажать. */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--gap-xs);
  /* 44px — минимальная удобная цель для пальца (гайдлайны Apple/Google). */
  min-height: 44px;
  padding: 10px var(--pad-lg);
  background: var(--panel-glass);
  border: 1px solid var(--line2);
  border-radius: var(--r-lg);
  color: var(--ink);
  font-size: var(--t-sm);
  font-weight: 800;
  letter-spacing: 0.04em;
  text-align: center;
  cursor: pointer;
  outline: none;
  transition: transform var(--dur) var(--ease), background-color var(--dur), border-color var(--dur), box-shadow var(--dur);
  touch-action: manipulation;
}
/* Наведение: кнопка остаётся на месте. Раньше она поднималась на 2px, и когда
   это происходило у верхней кромки панели, верхний край обрезался. Ощущение
   отклика даёт свечение и подсветка фона — без сдвига геометрии.
   Внутреннюю рамку (inset 0 0 0 1px) здесь НЕ ставим: она рисовала вторую
   линию вплотную к границе, и кнопка выглядела обведённой дважды — владелец
   принял это за баг. Отклик даёт сама граница и мягкая тень. */
.btn:hover {
  border-color: var(--accent-30);
  box-shadow: var(--shadow-panel);
}
.btn:active { transform: scale(0.98); }
/*  Клавиатурная рамка — цветом акцента, как в макете: без неё ход по кнопкам
   с Tab виден только по едва заметной тени. */
.btn:focus-visible,
.diff-btn:focus-visible,
.tab:focus-visible {
  outline: 2px solid var(--violet);
  outline-offset: 2px;
}

.btn-hangar   { background: var(--violet-15); border-color: var(--violet-30); color: var(--violet); }
.btn-hangar:hover { background: var(--violet-30); border-color: var(--violet); }
.btn-home     { background: var(--cyan-15); border-color: var(--cyan-30); color: var(--cyan); }
.btn-home:hover { background: var(--cyan-30); border-color: var(--cyan); }
.btn-toggle-base { background: var(--amber-15); border-color: var(--amber-30); color: var(--amber); }
.btn-toggle-base:hover { background: var(--amber-30); border-color: var(--amber); }

/*  Сплошные кнопки: заливка акцентом и ТЁМНЫЙ текст на ней. Светлее при
   наведении они становятся фильтром, а не вторым цветом в токенах: иначе на
   каждый акцент пришлось бы держать ещё и его «яркий» оттенок. */
.btn-primary { background: var(--cyan); border-color: var(--cyan); color: var(--ink-on-accent); }
.btn-violet  { background: var(--violet); border-color: var(--violet); color: var(--ink-on-accent); }
.btn-lime    { background: var(--lime); border-color: var(--lime); color: var(--ink-on-accent); }
.btn-amber   { background: var(--amber); border-color: var(--amber); color: var(--ink-on-accent); }
.btn-primary:hover,
.btn-violet:hover,
.btn-lime:hover,
.btn-amber:hover { filter: brightness(1.12); }
.btn-danger-soft { background: var(--rose-20); border-color: var(--rose-50); color: var(--rose); }
.btn-danger-soft:hover { background: var(--rose-50); border-color: var(--rose); }

.btn-block { width: 100%; }

/* Кнопка-переключатель: у неё есть состояние, и его видно.
   Раньше состояние (открыт радар или нет) жило только в памяти кода, а кнопка
   выглядела одинаково — понять, что происходит, было нельзя. */
.btn-toggle { justify-content: space-between; text-align: left; }
.btn-toggle .toggle-state {
  font-family: var(--fm);
  font-size: var(--t-xs);
  letter-spacing: 0.06em;
  opacity: 0.75;
  flex-shrink: 0;
}
.btn-toggle[aria-expanded="true"] {
  background: var(--accent-30);
  border-color: var(--accent);
  color: var(--ink);
}
.btn-toggle[aria-expanded="true"] .toggle-state { opacity: 1; }

/*  Недоступное действие: кнопка гаснет целиком, но остаётся на месте и не
   исчезает — иначе карточки в ряду прыгали бы по высоте. */
.btn-upgrade-disabled {
  background-color: var(--black-40) !important;
  color: var(--ink-dim) !important;
  border-color: var(--line2) !important;
  opacity: 0.45 !important;
  cursor: not-allowed !important;
  pointer-events: none !important;
  box-shadow: none !important;
}

/* --- полосы прогресса ----------------------------------------------------- */
/*  Дорожка и заливка — «таблетки»: радиус берётся из токена --r-pill, а он
   теперь не ноль. В макете все мелкие элементы скруглены, и полоса,
   оставленная прямоугольной, читалась бы чужой. Отдельных radius здесь не
   пишем, иначе форма полосы разъехалась бы с формой окон. */
.bar {
  height: 7px;
  background: var(--white-08);
  border-radius: var(--r-pill);
  overflow: hidden;
  margin-top: 6px;
  position: relative;
}
.bar-fill {
  height: 100%;
  width: 0%;
  border-radius: var(--r-pill);
  transition: width 0.1s linear;
  position: relative;
  overflow: hidden;
}
/*  Заливка светится своим цветом, как активный сегмент в макете: полоса — это
   показатель, и её должно быть видно боковым зрением. */
.bar-fill.tech    { background: var(--lime);   box-shadow: 0 0 10px var(--lime-30); }
.bar-fill.upgrade { background: var(--violet); box-shadow: 0 0 10px var(--violet-30); }
/* Улучшения базы: у них свой цвет, чтобы полоса на карточке базы не путалась
   с полосами корабля. */
.bar-fill.base    { background: var(--cyan);   box-shadow: 0 0 10px var(--cyan-30); }

/* Бегущий блик на активной полосе. */
@keyframes scanline {
  0% { transform: translateX(-100%); }
  100% { transform: translateX(100%); }
}
.bar.active::after,
.bar.upgrade-active::after,
.bar.base-active::after {
  content: '';
  position: absolute;
  /*  Длинные свойства перед inset: сам inset — Chrome 87+, Safari 14.1+, и на
      браузере постарше объявление просто отбрасывается. */
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  inset: 0;
  animation: scanline 1s infinite linear;
  pointer-events: none;
}
.bar.active::after { background: linear-gradient(90deg, transparent, var(--lime), transparent); }
.bar.upgrade-active::after { background: linear-gradient(90deg, transparent, var(--violet), transparent); }
.bar.base-active::after { background: linear-gradient(90deg, transparent, var(--cyan), transparent); }

/* --- анимации ------------------------------------------------------------- */
@keyframes blinker { 0%, 100% { opacity: 1; } 50% { opacity: 0.25; } }
.animate-blink { animation: blinker 0.8s ease-in-out infinite; }

@keyframes edgePulse {
  0%, 100% { opacity: 0.15; }
  50% { opacity: 0.5; }
}

/* Полоса-подсветка по краям экрана при критическом состоянии. Углов у неё нет
   по построению — это градиент, а не рамка, поэтому обнуление радиусов её не
   касается; цвет берётся из токена, как и везде. */
#edge-danger {
  position: fixed;
  /*  inset без запасных longhands оставил бы полосу-предупреждение в углу. */
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  inset: 0;
  pointer-events: none;
  z-index: var(--z-hud);
  opacity: 0;
  transition: opacity var(--dur);
  background:
    radial-gradient(120% 90% at 50% 50%, transparent 55%, var(--rose-50) 100%);
}
/*  Критическое состояние: пульс чаще и заметнее — предупреждение должно
    перебивать внимание, но не мигать как гирлянда. */
#edge-danger.on { animation: edgePulse 0.75s ease-in-out infinite; opacity: 0.5; }

/*  В опасности СТАНЦИЯ, а не корабль: тот же сигнал, но янтарный — понять, что
    именно гибнет, можно не читая надписей. */
#edge-danger.is-base {
  background:
    radial-gradient(120% 90% at 50% 50%, transparent 55%, var(--amber-50) 100%);
}

/* --- прокручиваемая область ---------------------------------------------- */
.scroll-y {
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior: contain;
  touch-action: pan-y;   /* пальцем можно скроллить, канвас это не касается */
}

.hidden { display: none !important; }

/* ===== src/css/layout.css ===== */
/* ==========================================================================
   layout.css — каркас экрана: мир, шапка, панель управления, тач-кнопки
   ==========================================================================
   Подход mobile-first: базовые правила описывают телефон, а более широкие
   экраны получают добавки. Никаких «десктопных» размеров, которые потом
   приходится отменять медиазапросами.
   ========================================================================== */

#viewport {
  position: relative;
  display: flex;
  flex-direction: column;
  width: 100%;
  /*  vh перед dvh: без запасного объявления браузер без dvh оставил бы высоту
      «по содержимому», а у #stage она флексовая (flex: 1 1 auto) — то есть
      полотно игры схлопнулось бы в ноль. */
  height: 100vh;
  height: 100dvh;
  overflow: hidden;
}

/* Пока открыт ангар, интерфейс полёта не нужен: на телефоне он просвечивает
   сквозь панель и мешает нажимать. */
#viewport.in-hangar #topbar,
#viewport.in-hangar #controls,
#viewport.in-hangar #touch-controls { visibility: hidden; }

/* --- игровой мир ---------------------------------------------------------- */
#stage {
  position: relative;
  flex: 1 1 auto;
  min-height: 0;
  overflow: hidden;
}

#gameCanvas {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  inset: 0;
  width: 100%;
  height: 100%;
  /* Единственное место, где запрещены жесты браузера: по полотну мы водим
     кораблём. Всё остальное (ангар, радар) скроллится как обычно. */
  touch-action: none;
  /*  Клик по полотну — это приказ «лети в точку», поэтому курсор прицельный:
     иначе мышь читается как украшение поверх игры. */
  cursor: crosshair;
}

/* --- шапка --------------------------------------------------------------- */
/*  Строка ресурсов и значок сектора. Пока места хватает, они стоят в одну
   строку по краям; на узком окне шапка переносится — сектор уезжает на вторую
   строку, и ничего не наезжает друг на друга. */
#topbar {
  position: absolute;
  top: calc(var(--topbar-inset) + var(--safe-top));
  left: calc(var(--hud-edge) + var(--safe-left));
  right: calc(var(--hud-edge) + var(--safe-right));
  display: flex;
  flex-wrap: wrap;
  gap: var(--gap-xs) var(--gap-sm);
  align-items: flex-start;
  justify-content: space-between;
  align-content: flex-start;
  z-index: var(--z-hud);
  pointer-events: none;
}
#topbar > * { pointer-events: auto; }

/* Ресурсы (уровень, опыт, склад, кредиты) — ОДНОЙ строкой.
   Подпись и значение стоят рядом, как в компактной приборной панели.

   Чего здесь больше НЕТ и почему. Раньше у блока стояло свойство
   «container-type: inline-size», а правила «@container stats (max-width: …)»
   убирали подписи и жали отступы. Для флекс-элемента с шириной «по
   содержимому» это замкнутый круг: браузеру нужна ширина блока, чтобы выбрать
   вариант по контейнерному запросу, а ширина блока зависит от того, какие
   варианты применились. Chrome отдаёт контейнерному запросу ширину ноль —
   срабатывает самая узкая ветка, текст «0 EXP0 ⬡0 $» сбивается в кучу и
   наезжает друг на друга (это и было видно на скриншоте). Отзывчивость строки
   теперь задана медиазапросами по ширине окна в responsive.css.
   `flex-wrap: wrap` — страховка: перенос всегда лучше наложения. */.stat-cluster {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--gap-xs) var(--gap);
  flex: 0 1 auto;
  border-radius: var(--r-lg);
  padding: 8px var(--pad);
  max-width: 100%;
  min-width: 0;
  font-size: var(--t-sm);
}

/*  Пара «подпись + значение». min-width здесь НЕ обнуляется намеренно: у
   флекс-элемента с overflow: visible автоматический минимум равен ширине
   содержимого, и именно он не даёт паре сжаться до наложения текста. */
.stat-item {
  display: flex;
  align-items: baseline;
  gap: 6px;
  white-space: nowrap;
}
/* Между смысловыми парами — тонкий разделитель, как на приборной панели. */
.stat-item + .stat-item { padding-left: var(--gap); border-left: 1px solid var(--line2); }
.stat-item .cap {
  color: var(--ink-dim);
  font-weight: 700;
  font-size: var(--t-xs);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
.stat-item .val { font-family: var(--fm); font-weight: 700; letter-spacing: 0.02em; }

.tier-cluster {
  display: flex;
  align-items: center;
  gap: var(--gap-sm);
  padding: 8px var(--pad);
  border-radius: var(--r-lg);
  background: var(--violet-10);
  border: 1px solid var(--violet-30);
  flex-shrink: 0;
}
.tier-dot {
  width: 10px; height: 10px; border-radius: 50%;
  background: var(--violet);
  box-shadow: 0 0 12px var(--violet);
  flex-shrink: 0;
}

/* --- память о месте гибели ------------------------------------------------ */
/*  Строка «✕ КРУШЕНИЕ 4 шт · 1870 м»: видно, что под крестиком есть лут и
   сколько до него лететь. Красный акцент — потому что это про потерю.
   Стоит ПО ЦЕНТРУ ЭКРАНА (владелец: «убери слева отметку о месте гибели —
   помести это в центр»), чуть выше середины: читается в бою и не закрывает
   корабль. */
.wreck-center {
  position: absolute;
  left: 50%;
  top: 42%;
  transform: translate(-50%, -50%);
  z-index: var(--z-hud);
  pointer-events: none;
  white-space: nowrap;
}
.wreck-center .wreck-center-cap {
  font-size: var(--t-xs);
  font-weight: 700;
  letter-spacing: 0.08em;
  color: var(--ink-dim);
}
.wreck-cluster {
  display: flex;
  align-items: center;
  gap: var(--gap-sm);
  padding: 8px var(--pad);
  border-radius: var(--r-lg);
  background: var(--rose-12);
  border: 1px solid var(--rose-30);
  font-size: var(--t-sm);
  white-space: nowrap;
  flex-shrink: 0;
}
.wreck-cluster .wreck-cross {
  color: var(--rose);
  font-weight: 800;
  line-height: 1;
}
.wreck-cluster .wreck-dist {
  font-family: var(--fm);
  font-size: var(--t-xs);
  color: var(--ink-dim);
  font-weight: 700;
}
/* Узкий экран: подпись «Крушение» уходит, остаются крестик, число и дистанция. */
@media (max-width: 560px) {
  .wreck-cluster .cap { display: none; }
}

/* --- панель управления: ресурсы, телеметрия, карта, кнопки ------------------ */
/*  Порядок блоков задан владельцем: 1. уровень и ресурсы (шириной во всю панель),
    2. состояние корабля и базы, 3. добыча, 4. статусы, 5. оппоненты, 6. карта,
    7. кнопки. Панель стоит СПРАВА. Статусы улучшений вынесены в левую часть
    экрана (см. .job-indicator ниже). */
#controls {
  position: absolute;
  right: calc(var(--hud-edge) + var(--safe-right));
  bottom: calc(var(--hud-edge) + var(--safe-bottom));
  left: calc(var(--hud-edge) + var(--safe-left));
  z-index: var(--z-hud);
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: var(--gap-sm);
  pointer-events: none;
  /*  ПРОКРУТКИ В ПАНЕЛИ НЕТ НИКОГДА. Владелец: «иногда появляется скролл в левой
      панели — не нужно, уменьшай за счёт карты. скрол не нужен ни при каких
      обстоятельствах». Поэтому overflow скрыт: если содержимое выше окна, место
      забирает КАРТА (она сжимается, см. #minimap ниже), а не появляется полоса. */
  max-height: calc(100vh - 120px);
  max-height: calc(100dvh - 120px);
  overflow: hidden;
  /* Ни один ребёнок не может вылезти за отведённую ширину. */
  min-width: 0;
}
#controls > * {
  pointer-events: auto;
  /* Свечение и тень кнопок рисуются внутри отведённого места, поэтому
     обрезки у кромки экрана не бывает. */
  max-width: 100%;
  min-width: 0;
}
/*  Ресурсы — первая строка панели, во всю её ширину. Раньше они жили в шапке
    экрана, и панель начиналась с телеметрии: «сколько у меня есть» и «что со
    мной» читались в разных углах. */
#controls .stat-cluster {
  flex: 0 0 auto;
  width: 100%;
  /*  Панель узкая (340px), поэтому зазор между парами меньше, чем в шапке: с
      подписями «Склад» и «Кредиты» строка переносилась на вторую, и владелец
      видел разорванную шапку. Подписи убраны, а зазор уменьшен как страховка. */
  gap: var(--gap-xs) var(--gap-sm);
}

/* --- звук и музыка — В ЛЕВОМ НИЗУ ---------------------------------------- */
/*  Владелец: «управление звуком перенеси в левый низ». На широком экране слева
    внизу пусто (панель управления стоит столбцом справа), поэтому ряд просто лежит
    у края. На узком экране там тач-кнопки, и ряд поднимается на --sys-lift: иначе
    он накрывал бы «Тягу» и «Магнит».
    pointer-events: none у обёртки и auto у кнопок — как у остальных слоёв HUD: ряд не
    должен перехватывать нажатия по миру между кнопками. */
.sys-controls {
  position: absolute;
  left: calc(var(--hud-edge) + var(--safe-left));
  bottom: calc(var(--hud-edge) + var(--safe-bottom));
  z-index: var(--z-hud);
  pointer-events: none;
}

.sys-controls .hud-toggle { pointer-events: auto; }

@media (max-width: 899px) {
  .sys-controls {
    bottom: calc(var(--hud-edge) + var(--safe-bottom) + var(--sys-lift));
  }
}

/* --- статусы улучшений — в ЛЕВОЙ части экрана ----------------------------- */
/*  Владелец: «а статусы улучшений выведи в левую часть экрана». Раньше блок жил
    внутри панели управления и прижимался к её низу; теперь это самостоятельная
    плашка у левого верхнего края — там, где игрок ничего не делает руками, и её
    видно, пока идут работы.
    Строка на каждую идущую работу, строки стоят одна под другой. Когда работ нет,
    блок схлопывается в ноль и ничего не сдвигает.
    Заливка — стекло: плотный фон на живой игре выглядел бы заплатой. */
.job-indicator {
  position: absolute;
  left: calc(var(--hud-edge) + var(--safe-left));
  top: calc(var(--topbar-inset) + var(--safe-top));
  width: min(300px, 42vw);
  z-index: var(--z-hud);
  display: flex;
  flex-direction: column;
  gap: 4px;
  max-height: 0;
  opacity: 0;
  overflow: hidden;
  pointer-events: none;
  padding: 0 var(--gap-sm);
  border-radius: var(--r-sm);
  background: var(--panel-glass-strong);
  backdrop-filter: blur(var(--blur-glass));
  -webkit-backdrop-filter: blur(var(--blur-glass));
  border: 1px solid transparent;
  transition: max-height var(--dur) var(--ease), opacity var(--dur) var(--ease),
              padding var(--dur) var(--ease), border-color var(--dur) var(--ease);
}
.job-indicator.active {
  /*  Работы идут параллельно: до четырёх улучшений базы, восьми модернизаций и
     четырёх исследований. Запас по высоте — на восемь строк, иначе длинный
     список обрежется. */
  max-height: 240px;
  opacity: 1;
  padding: 5px var(--gap-sm);
  border-color: var(--line2);
}

/* Строка: полоса слева, подпись справа. Сетка не даёт подписи выдавить полосу. */
.job-row {
  display: grid;
  grid-template-columns: 52px minmax(0, 1fr);
  align-items: center;
  gap: var(--gap-sm);
  min-width: 0;
  font-size: var(--t-xs);
}

/*  Полоса работы: дорожка и заливка — «таблетки». Радиус берётся из токена
   --r-pill, а он теперь не ноль: в макете все мелкие элементы скруглены. */
.job-bar {
  position: relative;
  height: 5px;
  border-radius: var(--r-pill);
  background: var(--white-08);
  overflow: hidden;
}
.job-fill {
  position: absolute;
  top: 0;
  right: auto;
  bottom: 0;
  left: 0;
  inset: 0 auto 0 0;
  width: 0%;
  border-radius: var(--r-pill);
  transition: width 0.15s linear;
}
/* Цвет — по виду работы: исследования зелёные, модернизации фиолетовые,
   работы базы голубые (тем же цветом, что и щит базы). */
.job-row.research .job-fill { background: var(--lime); box-shadow: 0 0 8px var(--lime); }
.job-row.upgrade .job-fill { background: var(--violet); box-shadow: 0 0 8px var(--violet); }
.job-row.base .job-fill { background: var(--cyan); box-shadow: 0 0 8px var(--cyan); }

.job-label {
  font-family: var(--fm);
  font-weight: 700;
  color: var(--ink-dim);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  min-width: 0;
}

.btn-row {
  position: relative;
  display: flex;
  gap: var(--gap-sm);
  min-width: 0;
}
.btn-row .btn { flex: 1 1 0; min-width: 0; white-space: nowrap; padding: 10px var(--gap-sm); }

/* Кнопка сворачивания радара: на телефоне нужна, на большом экране лишняя.
   Ширина 100% — панель радара всегда той же ширины, что и ряд кнопок, поэтому
   на телефоне ничего не выступает за край экрана. */
#btn-radar-toggle {
  display: inline-flex;
  width: 100%;
  box-sizing: border-box;
}

/* --- тач-управление ------------------------------------------------------- */
#touch-controls {
  position: absolute;
  left: calc(var(--hud-edge) + var(--safe-left));
  bottom: calc(var(--hud-edge) + var(--safe-bottom));
  z-index: var(--z-hud);
  display: flex;
  gap: var(--gap);
  align-items: flex-end;
  pointer-events: none;
}
#touch-controls > * { pointer-events: auto; }

.touch-col { display: flex; flex-direction: column; gap: var(--gap-sm); }
.touch-row { display: flex; gap: var(--gap-sm); align-items: flex-end; }

.touch-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--r-lg);
  font-weight: 700;
  backdrop-filter: blur(var(--blur-touch));
  -webkit-backdrop-filter: blur(var(--blur-touch));
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.35);
  /* Тач-кнопки не должны вызывать скролл или зум по двойному тапу. */
  touch-action: none;
  user-select: none;
  -webkit-user-select: none;
  cursor: pointer;
}
.touch-btn:active { filter: brightness(1.6); }

.touch-thrust { width: var(--touch-size); height: var(--touch-size); font-size: var(--t-xl); }
.touch-side   { width: calc(var(--touch-size) * 0.85); height: calc(var(--touch-size) * 0.85); font-size: var(--t-lg); }
.touch-fire   { width: var(--touch-size); height: var(--touch-size); font-size: var(--t-sm); }
.touch-magnet { width: var(--touch-size); height: calc(var(--touch-size) * 0.7); font-size: var(--t-xs); }
.touch-bomb   { width: calc(var(--touch-size) * 0.8); height: calc(var(--touch-size) * 0.8); font-size: var(--t-xs); }

.touch-cyan   { background: var(--cyan-15); border: 1px solid var(--cyan-30); color: var(--cyan); }
.touch-violet { background: var(--violet-15); border: 1px solid var(--violet-30); color: var(--violet); }
.touch-rose   { background: var(--rose-20); border: 1px solid var(--rose-50); color: var(--rose); }
.touch-amber  { background: var(--amber-15); border: 1px solid var(--amber-30); color: var(--amber); }

/* На устройствах с мышью и на широких экранах тач-кнопки не нужны. */
@media (hover: hover) and (pointer: fine) {
  #touch-controls { display: none; }
}

/* --- широкие экраны ------------------------------------------------------- */
/*  ПРАВАЯ ПАНЕЛЬ ВО ВСЮ ВЫСОТУ. Владелец: «блок индикации перенеси вправо.
    пункты идут в таком порядке — 1. уровень и ресурсы (по ширине сделай как весь
    блок). 2 состояние корабля и базы. 3. добыча 4. статусы 5. оппоненты 6. карта
    7. кнопки ангар и так далее».

    Устройство столбца (сверху вниз): ресурсы → телеметрия (состояние, добыча,
    статусы, оппоненты) → карта → кнопки. Телеметрия забирает остаток высоты и
    прокручивается внутри себя, поэтому карта и кнопки всегда остаются на месте,
    а панель доходит до низа экрана при любой высоте окна. */
@media (min-width: 900px) {
  #controls {
    left: auto;
    right: calc(var(--hud-edge) + var(--safe-right));
    top: calc(var(--topbar-inset) + var(--safe-top));
    bottom: calc(var(--hud-edge) + var(--safe-bottom));
    width: var(--radar-width);
    max-width: calc(100vw - var(--hud-edge) * 2);
    max-height: none;
    /*  Без прокрутки (владелец: «скрол не нужен ни при каких обстоятельствах»):
        лишнее место отнимает карта, а не полоса прокрутки. */
    overflow: hidden;
    padding-right: 0;
  }
  /*  Телеметрия — по содержимому и без прокрутки: она сообщает состояние корабля,
      станции и целей, сжимать её нельзя. Место уступает карта. */
  #controls .radar-stats {
    flex: 0 0 auto;
    /*  Снимаем мобильный предел: на широком экране телеметрия занимает свою высоту. */
    max-height: none;
    overflow: hidden;
  }
  /*  КАРТА — распорка столбца: она забирает остаток высоты и сжимается, когда строк
      телеметрии стало больше. Кнопки не сжимаются: они закреплены у низа панели. */
  #controls #minimap {
    flex: 1 1 auto;
    min-height: 120px;
    min-width: 0;
  }
  /*  Место карты: контейнер занимает остаток высоты панели, а КВАДРАТ держит сам
      канвас (radar.css, #minimapCanvas). Раньше квадрат считался здесь, и на высокой
      панели ширина упиралась в max-width, высота оставалась 100% — карта
      растягивалась по вертикали (владелец: «в веб-версии растянута карта»). */
  #controls .mm-body {
    height: 100%;
    width: 100%;
    max-width: 100%;
    margin: 0 auto;
  }
  #controls .btn-row {
    flex: 0 0 auto;
  }
  #btn-radar-toggle { display: none; }
}

/* ===== src/css/hud.css ===== */
/* ==========================================================================
   hud.css — полоса извлечения трюма, уведомления и служебные подсказки
   ========================================================================== */

/* --- выгрузка трюма на базе ---------------------------------------------- */
/*  Единственное окно, которое осталось плотным. Оно висит по центру полотна,
   под ним вспыхивают выстрелы и взрывы, а внутри — белая цифра прогресса;
   стекло здесь съело бы читаемость. Форма при этом прямоугольная, как у всех:
   радиус берётся из нулевого токена. */
#unload-progress-bar {
  position: absolute;
  left: 50%;
  top: calc(50% - 40px);
  transform: translateX(-50%);
  width: min(180px, 46vw);
  height: 18px;
  background: var(--panel-solid);
  border: 1px solid var(--violet);
  border-radius: var(--r-pill);
  padding: 2px;
  display: none;
  z-index: var(--z-hud);
  box-shadow: var(--glow-violet);
}
#unload-progress-fill {
  height: 100%;
  width: 0%;
  background: linear-gradient(90deg, var(--violet), var(--cyan));
  border-radius: var(--r-pill);
  transition: width 0.05s linear;
}
.unload-bar-text {
  position: absolute;
  /*  Длинные свойства перед inset: сам inset — Chrome 87+, Safari 14.1+. */
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--fm);
  font-size: var(--t-xs);
  font-weight: 800;
  color: var(--ink);
  text-shadow: 0 1px 2px var(--black-40);
}

/* --- уведомления ---------------------------------------------------------- */
/*  Уведомление висит по центру сверху и НЕ принадлежит панели кнопок: раньше
   оно стояло внутри ряда кнопок и его обрезала рамка панели управления, так что
   сообщение о завершённой технологии было не прочитать.

   `fixed` + центрирование: элемент не зависит ни от прокрутки панели, ни от
   `overflow` родителей. Переносится по строкам, потому что названия работ
   длинные («Скорость & Маневренность»).

   Уведомление — тоже стекло: оно всплывает прямо над игрой, и глухая плашка
   закрывала бы бой. Белому тексту здесь хватает контраста, потому что под
   уведомлением всегда полотно и размытие, а не яркая панель. */
#toast-notif {
  position: fixed;
  top: calc(env(safe-area-inset-top, 0px) + var(--gap-lg));
  left: 50%;
  transform: translate(-50%, -12px);
  z-index: var(--z-toast);

  max-width: min(520px, calc(100vw - var(--gap-lg) * 2));
  padding: 10px var(--pad-lg);
  background: var(--panel-glass-strong);
  backdrop-filter: blur(var(--blur-glass));
  -webkit-backdrop-filter: blur(var(--blur-glass));
  border: 1px solid var(--lime);
  border-radius: var(--r-lg);
  box-shadow: 0 4px 20px var(--lime-30), 0 0 0 1px var(--black-40);

  text-align: center;
  font-size: var(--t-sm);
  font-weight: 700;
  line-height: 1.35;
  color: var(--ink);
  /* Длинное название работы переносится, а не вылезает за рамку. */
  white-space: normal;
  overflow-wrap: anywhere;

  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition: opacity var(--dur) var(--ease), transform var(--dur) var(--ease), visibility var(--dur);
}
#toast-notif.show {
  opacity: 1;
  visibility: visible;
  transform: translate(-50%, 0);
}

/* --- предупреждение о критическом состоянии ------------------------------- */
/* Полоса по краям экрана: включается, когда ХП корабля или базы ниже 20%. */
#edge-danger { z-index: calc(var(--z-hud) - 1); }

/* --- быстрые переходы между станциями ------------------------------------- */
/*  Ряд маленьких кнопок под блоком кнопок панели: по одной на каждую свою
   станцию. Владелец: «добавь кнопки быстрых перемещений — 3-4-5 между базами».
   Кнопки повторяют клавиши 1…5, поэтому подписаны просто номером станции: цифра
   на кнопке и цифра на клавиатуре — одна и та же. */
.quick-jumps {
  display: flex;
  gap: var(--gap-xs);
  flex-wrap: wrap;
  min-width: 0;
}

.quick-jumps.hidden { display: none; }

/* --- звук и музыка --------------------------------------------------------- */
/*  Владелец: «управление звуком перенеси в левый низ». Ряд стоит отдельным блоком у
    левого нижнего края (позицию задаёт layout.css), кнопки компактные, подписи
    меняют состояние. Выключенное состояние различается не цветом текста, а рамкой и
    приглушённым фоном — чтобы читалось и на телефоне. */
.sys-controls {
  display: flex;
  gap: var(--gap-xs);
  flex-wrap: wrap;
  min-width: 0;
}

.hud-toggle {
  padding: 5px var(--gap-sm);
  font-family: var(--fm);
  font-size: var(--t-xs);
  font-weight: 800;
  letter-spacing: 0.04em;
  line-height: 1.2;
  color: var(--ink);
  background: var(--cyan-15);
  border: 1px solid var(--cyan-45);
  border-radius: var(--r-sm);
  cursor: pointer;
  transition: background 0.15s, border-color 0.15s, color 0.15s;
}

.hud-toggle:hover {
  background: var(--cyan-30);
  border-color: var(--cyan);
}

/* Выключено: глухая рамка и приглушённый текст. Не «невидимая», а именно «выкл». */
.hud-toggle.off {
  color: var(--ink-dim);
  background: var(--white-05);
  border-color: var(--line2);
}

.btn-quick-jump {
  /*  Квадратная кнопка-цифра: места в панели мало, а «перейти» — действие на один
      палец. Размер не меньше 34 — в него попадает и мышь, и палец на телефоне. */
  min-width: 34px;
  padding: 6px 0;
  font-size: var(--t-xs);
  font-weight: 800;
  color: var(--ink);
}

.btn-quick-jump.is-main { color: var(--cyan); }

/*  Свёрнутая станция: кнопка не «переход», а «развернуть здесь» — поэтому серая и
    пунктирная, чтобы её не нажимали, думая, что уже стоят на станции. */
.btn-quick-jump.is-packed {
  color: var(--ink-dim);
  border-style: dashed;
}

/* --- строка трюма ---------------------------------------------------------- */
/*  «В трюме 2» плюс плашки с номерами станций: те же номера, что у клавиш и кнопок
   переходов. Пустой трюм строку прячет целиком — место в панели дорогое. */
.rs-hold-chips {
  display: inline-flex;
  gap: 4px;
  margin-left: auto;
}

.rs-hold-chip {
  min-width: 18px;
  padding: 1px 4px;
  border: 1px solid var(--line2);
  border-radius: var(--r-sm);
  color: var(--cyan);
  font-size: var(--t-xs);
  font-weight: 800;
  line-height: 1.4;
  text-align: center;
}

/* ===== src/css/radar.css ===== */
/* ==========================================================================
   radar.css — радар телеметрии
   ==========================================================================
   На широком экране радар — постоянная панель 340px. На телефоне он по
   умолчанию свёрнут в одну кнопку: полоса состояния и квадратный радар
   съедали бы половину экрана, а играют на телефоне пальцами по краям.

   Размеры здесь намеренно «защитные»: панель не может стать шире отведённого
   места и не может вытолкнуть себя за край экрана. Причина — сетка строк
   состояния: подпись с запретом переноса задаёт минимальную ширину колонки, и
   без minmax(0, 1fr) панель распирало шире экрана, а правый край уезжал за
   границу (это и было видно на телефоне).
   ========================================================================== */

/*  Панель радара — стекло, как кнопка, которая её раскрывает: радар занимает
   заметный угол экрана, и плотная плита на этом месте закрывала бы половину
   обзора. Радиус берётся из нулевого токена, поэтому углы прямые. */
#minimap {
  display: flex;
  flex-direction: column;
  width: 100%;
  max-width: 100%;
  min-width: 0;
  box-sizing: border-box;
  border-radius: var(--r-lg);
  overflow: hidden;
}

.mm-head {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: var(--gap-sm);
  padding: 8px var(--pad);
  border-bottom: 1px solid var(--line2);
  /* Тонировка шапки — вставное поле, как сегмент на заставке: тёмная полоса
     над картой читается как «углубление», а не как ещё одна панель. */
  background: var(--panel-inset);
  min-width: 0;
}
.mm-head span {
  font-family: var(--fm);
  font-size: var(--t-xs);
  letter-spacing: 0.12em;
  color: var(--ink-dim);
  font-weight: 600;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  min-width: 0;
}

/*  СЕКТОР в шапке карты (владелец: «Секор выводи над картой»). Номер — тем же
    моношрифтом, что и остальные значения, но крупнее и акцентным цветом: это
    ответ на вопрос «где я», и он должен читаться первым.
    Селекторы с `.mm-head` не для красоты: правило `.mm-head span` выше по
    специфичности, и без родителя цвет и размер не применились бы. */
.mm-head .mm-sector {
  display: inline-flex;
  align-items: baseline;
  gap: 6px;
  min-width: 0;
}
.mm-head .mm-sector-cap {
  color: var(--violet);
  font-weight: 700;
  letter-spacing: 0.12em;
}
.mm-head .mm-sector-val {
  font-family: var(--fm);
  font-size: var(--t-sm);
  font-weight: 800;
  color: var(--violet);
}
/* Заголовок ужимается первым, подпись купола остаётся целиком. */
.mm-head span:last-child { flex: 0 0 auto; }

/* --- полосы состояния ----------------------------------------------------- */
/*  Полосы лежат на стекле радара, но подписи у них мелкие и серые, поэтому
   подложка здесь плотнее оконной: иначе игровое свечение съедало бы «Щит
   корабля» и «Трюм (скрап)». Прозрачность при этом остаётся — под панелью
   угадывается мир. */
/*  Телеметрия — ОТДЕЛЬНЫЙ блок панели (раньше лежала внутри карты). Теперь она
    забирает остаток высоты левого столбца и прокручивается внутри, поэтому
    панель доходит до низа экрана. Радиус — как у остальных панелей столбца. */
.radar-stats {
  padding: var(--pad-sm) var(--pad);
  background: var(--panel-glass-strong);
  border: 1px solid var(--line2);
  border-radius: var(--r-lg);
  display: grid;
  gap: 6px;
  min-width: 0;
  align-content: start;
  /*  На телефоне столбец не тянется на всю высоту: там панель снизу, и
      телеметрия не должна съедать обзор. Прокрутки при этом НЕТ (владелец: «скрол
      не нужен ни при каких обстоятельствах»): остаток высоты ограничивает блок, а
      лишние строки не прокручиваются — на широком экране вместо этого сжимается
      карта (см. layout.css). */
  max-height: 42vh;
  max-height: 42dvh;
  overflow: hidden;
}

.rs-row {
  display: grid;
  /* minmax(0, 1fr) вместо 1fr: колонка может сжиматься уже своего содержимого.
     Иначе длинная подпись распирала панель шире экрана. */
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 2px 8px;
  align-items: center;
  font-size: var(--t-xs);
  min-width: 0;
}

/* Строки, которые код показывает и прячет (ХП базы, таймер отсутствия),
   используют ЭТУ же раскладку по колонкам и по-прежнему две строки по высоте.
   Раньше код ставил им display: flex, и композиция рассыпалась: подпись
   обрезалась до «ХП…», а полоса вставала рядом со значением, а не под ним. */
.rs-row.row-inline {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
}
.rs-row.row-inline.hidden { display: none; }

/*  Группы телеметрии: это подпись системы, а не строка со значением. Порядок и
    цвет строк теперь означают систему (корпус — роза, щит — циан, расходники —
    янтарь, груз — фиолет, станция — лайм), а не «как получилось». */
.rs-group {
  margin-top: var(--gap-sm);
  font-size: var(--t-xs);
  letter-spacing: 0.08em;
  color: var(--ink-dim);
  opacity: 0.7;
}
.rs-group:first-child { margin-top: 0; }

/* --- оппоненты: четыре значка -------------------------------------------- */
/*  Вместо строки «Оппонент» — сетка 2×2: имя и номер сектора. Значок читается
    боковым зрением (номер + имя), а не требует вчитываться в одну длинную строку.
    Выбывший гаснет: «ВЫБЫЛ» вместо сектора, и весь значок притушен. */
.opp-board {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--gap-xs);
  margin-top: var(--gap-xs);
}
.opp-chip {
  display: grid;
  grid-template-columns: auto 1fr;
  grid-template-rows: auto auto;
  column-gap: var(--gap-xs);
  padding: 3px 5px;
  border: 1px solid var(--rose-30);
  border-radius: var(--r-sm);
  background: var(--panel-inset);
  min-width: 0;
}
.opp-chip-mark {
  grid-row: 1 / span 2;
  align-self: center;
  font-family: var(--fm);
  font-size: var(--t-xs);
  font-weight: 700;
  color: var(--rose);
}
.opp-chip-name {
  font-size: var(--t-xs);
  font-weight: 700;
  color: var(--ink);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.opp-chip-where {
  font-family: var(--fm);
  font-size: var(--t-xs);
  color: var(--ink-dim);
}
/*  Ближайший живой — акцентная рамка: с четырьмя целями игроку нужен ответ
    «к кому лететь», а не только «кто где». */
.opp-chip.is-nearest { border-color: var(--rose); }
.opp-chip.is-out { opacity: 0.4; border-style: dashed; }
.opp-chip.is-out .opp-chip-where { color: var(--ink-dim); }
.opp-board.is-clear .opp-chip { border-color: var(--lime-30); }

/*  Недоступная система не исчезает со своего места: строка гаснет, а не прячется.
    Раньше она скрывалась, и при покупке щита весь столбец прыгал вверх-вниз. */
.rs-row.is-locked { opacity: 0.35; }
.rs-row.is-locked .rs-bar-fill { width: 0 !important; }
.rs-label {
  color: var(--ink-dim);
  font-weight: 600;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  min-width: 0;
}
.rs-val {
  font-family: var(--fm);
  font-weight: 700;
  white-space: nowrap;
  justify-self: end;
}
.rs-bar-track {
  grid-column: 1 / -1;
  width: 100%;
  max-width: 100%;
  height: 6px;
  background: var(--white-08);
  border-radius: var(--r-pill);
  overflow: hidden;
  position: relative;
  min-width: 0;
}
/* Цвет заливки задаётся явно по id (см. ниже): наследование currentColor
   легко теряется, и полоса становится невидимой. */
.rs-bar-fill {
  position: absolute;
  top: 0;
  right: auto;
  bottom: 0;
  left: 0;
  inset: 0 auto 0 0;
  width: 100%;
  border-radius: var(--r-pill);
  transition: width 0.3s ease;
}

/* --- явные цвета полос ---------------------------------------------------- */
#rs-hp-fill       { background: var(--lime);   box-shadow: 0 0 10px var(--lime); }
#rs-base-hp-fill  { background: var(--lime);   box-shadow: 0 0 10px var(--lime); }
#rs-ammo-fill     { background: var(--amber);  box-shadow: 0 0 10px var(--amber); }
#rs-bomb-fill     { background: var(--amber);  box-shadow: 0 0 10px var(--amber); }
#rs-cargo-fill    { background: var(--violet); box-shadow: 0 0 10px var(--violet); }
#rs-cargo-fill.full { background: var(--rose); box-shadow: 0 0 10px var(--rose); }
/*  Энергощит корабля — тот же циан, что у щита базы и у кольца вокруг корабля:
    одна система одного цвета, как бы она себя ни показывала. */
#rs-shield-fill   { background: var(--cyan);   box-shadow: 0 0 10px var(--cyan); }
/*  Цель забега — роза: тот же цвет, что у корабля и станции оппонента в мире. */
#rs-opp-fill      { background: var(--rose);   box-shadow: 0 0 10px var(--rose-50); }

/* --- карта ---------------------------------------------------------------- */
/*  Под канвасом карты фон плотнее оконного (--panel-glass) и это осознанно:
   канвас рисуется на прозрачном фоне (clearRect), сетка у него почти
   невидимая, и яркая вспышка под панелью утопила бы точки кораблей. Мир сквозь
   карту всё равно виден — просто приглушённым. */
.mm-body {
  position: relative;
  width: 100%;
  max-width: 100%;
  /* Квадрат без магической высоты: браузер сам считает сторону. */
  aspect-ratio: 1 / 1;
  overflow: hidden;
  background: var(--panel-glass-strong);
}

/*  Браузер без aspect-ratio (Chrome < 88, Safari < 15) оставил бы у карты
    высоту по содержимому, а содержимое — абсолютно позиционированный канвас,
    то есть ноль: радара не было бы видно вовсе. Классический «квадрат через
    padding» включается ТОЛЬКО там, где aspect-ratio нет. */
@supports not (aspect-ratio: 1 / 1) {
  .mm-body { height: 0; padding-bottom: 100%; }
  /*  Канвас-квадрат держится на aspect-ratio, а его здесь нет: возвращаем простое
      заполнение контейнера. Контейнер уже квадратный (padding-bottom выше), поэтому
      искажений не возникает. */
  #minimapCanvas { width: 100%; height: 100%; margin: 0; }
}
#minimapCanvas {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  inset: 0;
  /*  КВАДРАТ ЖИВЁТ НА КАНВАСЕ, А НЕ НА КОНТЕЙНЕРЕ. Владелец: «в веб-версии смотри
      что — растянута карта». Причина: контейнеру .mm-body задавался aspect-ratio, но
      на высокой панели `height: 100%` и `max-width: 100%` сжимали ШИРИНУ, а высота
      оставалась прежней — коробка становилась выше, чем шире, и канвас (буфер
      квадратный, 340×340) растягивался по вертикали. Теперь контейнер просто
      занимает место, а канвас сам держит квадрат по МЕНЬШЕЙ стороне коробки:
      auto-размеры + aspect-ratio + max-width/max-height 100% + margin auto.
      Работает и когда панель выше, и когда она шире. */
  width: auto;
  height: auto;
  aspect-ratio: 1 / 1;
  max-width: 100%;
  max-height: 100%;
  margin: auto;
  display: block;
  touch-action: none;
}

.mm-foot {
  display: flex;
  justify-content: space-between;
  gap: var(--gap-sm);
  padding: 8px var(--pad);
  border-top: 1px solid var(--line2);
  font-family: var(--fm);
  font-size: var(--t-xs);
  color: var(--ink-dim);
  background: var(--panel-inset);
  min-width: 0;
}
.mm-foot b { color: var(--cyan); font-weight: 700; }
/* Первой ужимается надпись «до базы», координаты корабля остаются целиком. */
.mm-foot span:first-child { flex: 1 1 auto; }
.mm-foot span:last-child { flex: 0 0 auto; }
.mm-foot span {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  min-width: 0;
}

/* --- свёрнутое состояние на телефоне -------------------------------------- */
@media (max-width: 899px) {
  /* Закрытый радар: показываем только строку кнопки. */
  #minimap .mm-head,
  #minimap .radar-stats,
  #minimap .mm-body,
  #minimap .mm-foot { display: none; }

  #minimap.open .mm-head,
  #minimap.open .radar-stats,
  #minimap.open .mm-foot { display: flex; }
  #minimap.open .radar-stats { display: grid; }
  #minimap.open .mm-body { display: block; }

  /* Открытый радар ограничен по высоте, чтобы карта не выдавила кнопки. Прокрутки
     нет и здесь: владелец просил не показывать полосу ни при каких обстоятельствах —
     лишнее просто обрезается пределом высоты. */
  #minimap.open .mm-body { max-height: min(46vh, 320px); max-height: min(46dvh, 320px); }
  #minimap.open { max-height: 62vh; max-height: 62dvh; overflow: hidden; }
}

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

#hangar-screen {
  position: absolute;
  /*  Longhands перед inset: на браузере без inset ангар не накрыл бы экран. */
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: calc(var(--pad-sm) + var(--safe-top)) calc(var(--pad-sm) + var(--safe-right))
           calc(var(--pad-sm) + var(--safe-bottom)) calc(var(--pad-sm) + var(--safe-left));
  z-index: var(--z-screen);
  background: var(--scrim);
  backdrop-filter: blur(var(--blur-screen));
  -webkit-backdrop-filter: blur(var(--blur-screen));
  /* Скролл ангара — обычный, поэтому жесты разрешены. */
  touch-action: pan-y;
}

/*  Панель ангара — то же стекло, что у кнопки, которая её открывает. Раньше
   заливка была почти непрозрачной (--panel), и ангар читался как отдельное
   приложение поверх игры. Теперь сквозь панель угадывается мир: ангар
   открывают у базы, и видеть, что вокруг неё происходит, полезно.
   Толщину стекла держит не заливка, а размытие: под панелью мир, и без blur
   звёзды и трассеры спорили бы с текстом карточек. */
.hangar-panel {
  position: relative;
  display: flex;
  flex-direction: column;
  width: 100%;
  max-width: 1100px;
  /* Высота ФИКСИРОВАННАЯ, а не «по содержимому». Вкладки при переключении
     различаются числом карточек, и раньше панель подстраивалась под каждую —
     шапка и ряд вкладок прыгали вверх-вниз, из-за чего терялась связь с тем,
     куда нажимаешь. Теперь двигается только содержимое внутри. */
  height: min(92vh, 900px);
  height: min(92dvh, 900px);
  min-height: 0;
  padding: var(--pad);
  border-radius: var(--r-xl);
  border: 1px solid var(--accent-30);
  box-shadow: var(--shadow-screen);
  background: var(--panel-glass);
  backdrop-filter: blur(var(--blur-glass));
  -webkit-backdrop-filter: blur(var(--blur-glass));
  /* Акцентное семейство по умолчанию — модернизации. */
  --accent: var(--violet);
  --accent-15: var(--violet-15);
  --accent-30: var(--violet-30);
  --accent-glow: var(--violet-30);
}

/* Каждая вкладка — своё цветовое семейство. Акцент меняется здесь, а карточки,
   кнопки, полосы и чипы берут цвет из --accent, поэтому внутри вкладки всё
   одного цвета. Раньше цвета были перемешаны: фиолетовая карточка, зелёный
   заголовок, голубая кнопка — «базар», а не система. */
.hangar-panel:has(#content-upgrades:not(.hidden)) {
  --accent: var(--violet);
  --accent-15: var(--violet-15);
  --accent-30: var(--violet-30);
  --accent-glow: var(--violet-30);
}
.hangar-panel:has(#content-tech:not(.hidden)) {
  --accent: var(--lime);
  --accent-15: var(--lime-12);
  --accent-30: var(--lime-30);
  --accent-glow: var(--lime-30);
}
.hangar-panel:has(#content-base:not(.hidden)) {
  --accent: var(--cyan);
  --accent-15: var(--cyan-15);
  --accent-30: var(--cyan-30);
  --accent-glow: var(--cyan-30);
}
/* Запасной путь, если :has() не поддерживается: те же акценты ставит JS
   классами is-active-* на самой панели (см. showHangarTab в ui/hangar.js). */
.hangar-panel.is-active-violet {
  --accent: var(--violet);
  --accent-15: var(--violet-15);
  --accent-30: var(--violet-30);
}
.hangar-panel.is-active-lime {
  --accent: var(--lime);
  --accent-15: var(--lime-12);
  --accent-30: var(--lime-30);
}
.hangar-panel.is-active-cyan {
  --accent: var(--cyan);
  --accent-15: var(--cyan-15);
  --accent-30: var(--cyan-30);
}

/* --- шапка ангара --------------------------------------------------------- */
.hangar-head {
  display: flex;
  flex-wrap: wrap;
  gap: var(--gap);
  align-items: center;
  justify-content: space-between;
  padding-bottom: var(--pad);
  border-bottom: 1px solid var(--line);
  flex-shrink: 0;
}
/*  Заголовок ангара получает градиент из .t-title (см. base.css) — здесь только
    свечение под буквами в цвет вкладки, чтобы шапка не спорила с карточками. */
.hangar-head h2 { filter: drop-shadow(0 0 18px var(--accent-glow)); }

.hangar-wallet {
  display: flex;
  align-items: center;
  gap: var(--gap);
  background: var(--panel-inset);
  border: 1px solid var(--line2);
  border-radius: var(--r-lg);
  padding: 6px var(--pad);
  font-family: var(--fm);
  font-weight: 700;
  font-size: var(--t-sm);
}

/* --- вкладки -------------------------------------------------------------- */
.hangar-tabs {
  display: flex;
  gap: var(--gap-sm);
  padding: var(--pad) 0;
  border-bottom: 1px solid var(--line);
  flex-shrink: 0;
  /* На узком экране вкладки прокручиваются вбок, а не ломают вёрстку. */
  overflow-x: auto;
  scrollbar-width: none;
}
.hangar-tabs::-webkit-scrollbar { display: none; }

.tab {
  flex: 0 0 auto;
  padding: 10px var(--pad-lg);
  border-radius: var(--r-lg);
  font-size: var(--t-sm);
  font-weight: 800;
  letter-spacing: 0.04em;
  white-space: nowrap;
  cursor: pointer;
  transition: background-color var(--dur), color var(--dur), border-color var(--dur);
  min-height: 44px;
  /* Неактивная вкладка — спокойное «вставное» поле; активная получает свой
     акцент, как выбранная ячейка сегмента на заставке. */
  background: var(--panel-inset);
  color: var(--ink-dim);
  border: 1px solid var(--line2);
}
.tab:hover { color: var(--ink); }

/* Активная вкладка: у каждой свой цвет, но форма одна. Рамка — того же цвета,
   что и заливка, иначе на цветном фоне остаётся тёмный контур. */
.tab.is-active { color: var(--ink-on-accent); font-weight: 800; }
.tab.is-active-violet { background: var(--violet); border-color: var(--violet); box-shadow: 0 0 20px var(--violet-30); }
.tab.is-active-lime   { background: var(--lime);   border-color: var(--lime);   box-shadow: 0 0 20px var(--lime-30); }
.tab.is-active-cyan   { background: var(--cyan);   border-color: var(--cyan);   box-shadow: 0 0 20px var(--cyan-30); }

/* --- содержимое вкладок --------------------------------------------------- */
/* Содержимое занимает всё оставшееся место. Поэтому шапка и ряд вкладок всегда
   на одной и той же высоте, какая бы вкладка ни была открыта. */
.hangar-body {
  display: grid;
  gap: var(--gap);
  align-content: start;
  padding: var(--pad-sm) 2px var(--pad-sm) 0;
  margin-top: var(--gap-sm);
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior: contain;
  touch-action: pan-y;
  /* flex: 1 вместо min-height: 0 — вкладка растягивается на свободное место. */
  flex: 1 1 auto;
  min-height: 0;
}

/*  Модернизации. Раньше здесь стояло `overflow-y: visible` с объяснением «восемь
    карточек обязаны помещаться без прокрутки». Карточек стало двенадцать
    (стволы, пробой, ракеты, дрон), и это правило превратилось в поломку: список
    перестал прокручиваться и уезжал за край панели — владелец так и написал:
    «там просто уходит интерфейс за край экрана». Теперь вкладка прокручивается
    как все остальные, а карточки компактнее. */
#content-upgrades {
  gap: var(--gap-sm);
}
#content-upgrades .card { padding: 10px; }
#content-upgrades .card-note { margin-bottom: 2px; }

/*  Карточка не может быть шире своей колонки. Без min-width: 0 сетка-контейнер
    считает минимальной шириной содержимое (длинное название и подпись в кнопке),
    и панель выезжала за край окна — владелец видел именно это: «уходит интерфейс
    за край экрана». */
/*  В ангаре карточка — элемент СПИСКА внутри окна, а не бланк с полями: под ней
   нет полей ввода, только текст, чипы и полоса. Поэтому здесь она темнее окна
   (вставная), а на заставке — стеклянная: заливка карточки выбирается по тому,
   что лежит ВНУТРИ неё.
   ПОЧЕМУ #hangar-screen, а не просто .card: раньше правило стояло на голом
   .card, а этот файл подключается после base.css. Из-за этого заливка «карточка
   ангара» доставалась ВСЕМ карточкам игры, включая заставку: карточка на
   заставке становилась такой же почти чёрной, как поле внутри неё, и поля
   выглядели пустыми — без заливки. Теперь правило действует только внутри
   ангара. */
#hangar-screen .card { min-width: 0; background: var(--panel-inset); }

/* Управление базой: всё сверху, без растягивания по высоте. Раньше блоки
   разъезжались по вертикали (justify-content: space-between), и элементы
   приходилось искать глазами по всему экрану. */
#content-base {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  justify-content: flex-start;
  gap: var(--gap);
  /*  Вложенный блок тоже полупрозрачный: он лежит на стекле панели, и плотная
     заливка вернула бы ту самую «плиту», от которой уходим. Отдельного blur
     здесь нет намеренно — панель под ним уже размыта, второй слой размытия
     стоил бы кадров на телефоне. */
  background: var(--black-30);
  border: 1px solid var(--accent-30);
  border-radius: var(--r-lg);
  padding: var(--pad);
  overflow-y: auto;
  min-height: 0;
}
/* Кнопка сворачивания базы — сразу под содержимым, а не у нижнего края. */
#content-base #btn-toggle-base { margin-top: 0; }

/* Разделитель разделов внутри сетки карточек. */
.section-header {
  grid-column: 1 / -1;
  display: flex;
  align-items: center;
  gap: var(--gap-sm);
  padding: 6px var(--pad);
  border-radius: var(--r-sm);
  font-weight: 800;
  font-size: var(--t-xs);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  margin-top: 6px;
}
.section-combat  { background: var(--rose-12); border: 1px solid var(--rose-30); color: var(--rose); }
.section-utility { background: var(--cyan-15); border: 1px solid var(--cyan-30); color: var(--cyan); }

/* --- карточка ------------------------------------------------------------- */
.card-head {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: var(--gap-sm);
  margin-bottom: 4px;
}
/* Цвета внутри карточки берутся из акцента вкладки: вся вкладка получается
   одного семейства. */
.card-title { font-weight: 700; font-size: var(--t-sm); color: var(--accent); min-width: 0; }
.card-title .tier { color: var(--accent); opacity: 0.8; }
.card-chip {
  flex-shrink: 0;
  font-family: var(--fm);
  font-size: var(--t-xs);
  padding: 4px 10px;
  background: var(--accent-15);
  border: 1px solid var(--accent-30);
  border-radius: var(--r-sm);
  color: var(--accent);
  white-space: nowrap;
}
.card-note { font-size: var(--t-xs); color: var(--ink-dim); margin-bottom: 4px; }
/*  Конкретные цифры технологии: что она даёт сейчас и что даст после
   исследования. Строки без переноса — цифры должны читаться столбиком. */
.card-stats {
  display: flex;
  flex-direction: column;
  gap: 2px;
  margin: 6px 0;
  padding: 6px 8px;
  background: var(--white-05);
  border-radius: var(--r-sm);
}
.card-stat {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: var(--gap-sm);
  font-size: var(--t-xs);
  color: var(--ink-dim);
  min-width: 0;
}
.card-stat > span:first-child {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.card-stat-val {
  flex-shrink: 0;
  font-family: var(--fm);
  font-weight: 700;
  color: var(--accent);
}
/* Уточнение под цифрами: какую именно модернизацию усиливает технология. */
.card-subnote {
  font-size: var(--t-xs);
  color: var(--ink-dim);
  opacity: 0.8;
  margin-bottom: 6px;
}
.card-req { font-size: var(--t-xs); color: var(--amber); margin-bottom: 6px; }
.card-action { margin-top: var(--gap); }

/*  Кнопка в карточке — ОДНА строка: «Улучшить за 45 ⬡». Владелец присылал скриншот,
    где подпись и цена разъехались на две строки и кнопка выросла вдвое, а текст
    обрезался краем карточки. Причина — перенос внутри кнопки: у неё три элемента
    (подпись, цена, знак склада), и на узкой карточке они вставали столбиком.
    Теперь кнопка не переносится, а подсказка (.card-hint) остаётся отдельной
    строкой — у неё свой white-space: normal. */
.card .btn { flex-wrap: nowrap; white-space: nowrap; }

/* --- подсказка «почему кнопка неактивна» ---------------------------------- */
/*  Строка живёт ВНУТРИ кнопки и занимает свою строку ВСЕГДА: пустая она не видна,
    но и не даёт кнопке менять высоту. Владелец прислал скриншот, где кнопки в
    одном ряду были РАЗНОЙ высоты, а под серой кнопкой висела пустая полоса: линия
    подсказки появлялась только у заблокированных, но её текст был тёмным на тёмном
    фоне и не читался. Теперь высота у всех карточек одинаковая, а причина отказа
    видна словами. */
.card-hint {
  display: block;
  min-height: 1.25em;
  margin-top: 2px;
  font-size: var(--t-xs);
  font-weight: 600;
  color: var(--ink-on-accent);
  opacity: 0.85;
  white-space: normal;
  line-height: 1.25;
  text-align: center;
}
/*  Старое правило оставлено: разметку и код никто не должен переучивать, а
    .show теперь ничего не включает — строка и так блочная. */
.card-hint.show { display: block; }
/*  Цвет подсказки следует за причиной, но остаётся тёмным: она лежит на ЦВЕТНОЙ
    кнопке (доступной). */
/*  Подсказка «почему нельзя» лежит на ЦВЕТНОЙ кнопке, поэтому текст тёмный —
    тот же --ink-on-accent, что и у подписи кнопки. Раньше здесь стояли три
    разных почти-чёрных литерала, и они расходились с цветом текста кнопки. */
.card-hint.is-money { color: var(--ink-on-accent); }
.card-hint.is-level { color: var(--ink-on-accent); }
.card-hint.is-job { color: var(--ink-on-accent); }
.card-hint.is-tier { color: var(--ink-on-accent); }
.card-hint.is-max { color: var(--ink-on-accent); }
/*  У ЗАБЛОКИРОВАННОЙ кнопки фон серый, а не акцентный — тёмный текст на нём не
    виден вовсе. Именно из-за этого подсказка «почему нельзя» пропадала, и ангар
    выглядел сломанным: кнопки серые, объяснения нет. Здесь цвета переопределены
    для серого фона. */
.btn-upgrade-disabled .card-hint,
.btn-upgrade-disabled .card-hint.is-money,
.btn-upgrade-disabled .card-hint.is-level,
.btn-upgrade-disabled .card-hint.is-job,
.btn-upgrade-disabled .card-hint.is-tier,
.btn-upgrade-disabled .card-hint.is-max {
  color: var(--ink);
  opacity: 0.9;
}

/* Кнопка действия и полоса прогресса внутри карточки — тоже акцент вкладки. */
.card .btn-primary,
.card .btn-lime,
.card .btn-violet { background: var(--accent); color: var(--ink-on-accent); }
.card .btn-primary:hover,
.card .btn-lime:hover,
.card .btn-violet:hover { filter: brightness(1.12); }
.card .bar-fill.upgrade,
.card .bar-fill.tech { background: var(--accent); }

/* Карточки технологий MK больше не «зелёные среди фиолетовых»: пока открыта
   эта вкладка, акцент всей панели зелёный, поэтому отдельное оформление
   достаточно только для рамки. */
.card.tech { border-color: var(--accent-30); }

/* --- блок «управление базой» --------------------------------------------- */
.base-block {
  display: flex;
  flex-wrap: wrap;
  gap: var(--gap);
  justify-content: space-between;
  align-items: flex-start;
  padding-bottom: var(--pad);
  border-bottom: 1px solid var(--line);
}
.base-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--gap);
}
.base-card {
  background: var(--panel-inset);
  border: 1px solid var(--accent-30);
  border-radius: var(--r-lg);
  padding: var(--pad);
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  gap: var(--gap-sm);
}
.base-card .row {
  display: flex;
  justify-content: space-between;
  gap: var(--gap-sm);
  font-size: var(--t-xs);
  font-weight: 700;
  color: var(--accent);
}

/* --- адаптация ------------------------------------------------------------ */
@media (min-width: 720px) {
  #hangar-screen { padding: calc(var(--pad) + var(--safe-top)); }
  .hangar-panel { padding: var(--pad-lg); }
  .hangar-body { grid-template-columns: 1fr 1fr; }
  .base-grid { grid-template-columns: 1fr 1fr; }
}

/* Низкий экран (ландшафт телефона): панель занимает почти всё окно, иначе
   фиксированная высота оставила бы содержимому слишком мало места. */
@media (max-height: 520px) {
  .hangar-panel { height: 96vh; height: 96dvh; }
}

/* ===== src/css/screens.css ===== */
/* ==========================================================================
   screens.css — главное меню, экран провала и подсказки по управлению
   ========================================================================== */

/* --- таблица рекордов ------------------------------------------------------ */
/*  Три колонки: позывной, счёт, время. Одна сетка на шапку и на строки, иначе
   колонки разъезжаются и таблица читается как список. */
.records-inner { max-width: 560px; }

.records-card {
  width: 100%;
  padding: var(--pad-lg);
  display: grid;
  gap: var(--gap-xs);
}

.records-tabs { justify-content: center; }

.records-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto auto;
  align-items: baseline;
  gap: var(--gap);
  width: 100%;
  text-align: left;
  font-size: var(--t-sm);
  color: var(--ink-dim);
}

.records-head {
  padding-bottom: var(--gap-sm);
  border-bottom: 1px solid var(--line);
}

.records-body { display: grid; gap: var(--gap-xs); }

.records-name {
  color: var(--ink);
  font-weight: 700;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.records-score { color: var(--gold); font-weight: 700; }
.records-time { flex: none; }

/* --- слоты сохранений ------------------------------------------------------- */
/*  Строка слота: имя и возраст слева, «ЗАГРУЗИТЬ» и «×» справа. Кнопки стоят в
   одну строку с подписью, поэтому на телефоне имя ужимается первым — оно
   единственное, что можно обрезать без потери смысла. */
.slots-inner { max-width: 560px; }

.slots-form {
  width: 100%;
  padding: var(--gap-sm);
  display: grid;
  gap: var(--gap-sm);
  text-align: left;
}

.slots-body {
  width: 100%;
  padding: var(--gap-sm);
  display: grid;
  gap: var(--gap-xs);
}

.slot-row {
  display: flex;
  align-items: center;
  gap: var(--gap-sm);
  min-width: 0;
}

.slot-label {
  display: grid;
  gap: 2px;
  min-width: 0;
  flex: 1 1 auto;
  text-align: left;
}

.slot-name {
  color: var(--ink);
  font-weight: 700;
  font-size: var(--t-sm);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.slot-del { flex: 0 0 auto; min-width: 44px; padding: 10px 0; }

.screen {
  position: absolute;
  /*  Без запасных longhands экран меню на старом браузере не растянулся бы на
      окно и просто повис бы в углу. */
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  /*  Выравнивание по верху, а центрирование делает margin:auto у .screen-inner.
     Причина: у «justify-content: center» и прокручиваемого контейнера есть
     известная поломка — когда содержимое выше окна, верхняя часть уезжает за
     край и до неё нельзя домотать. С margin:auto центрирование работает только
     при запасе места, а при переполнении просто отступа нет. */
  justify-content: flex-start;
  gap: var(--gap);
  padding: calc(var(--pad-lg) + var(--safe-top)) calc(var(--pad-lg) + var(--safe-right))
           calc(var(--pad-lg) + var(--safe-bottom)) calc(var(--pad-lg) + var(--safe-left));
  z-index: var(--z-screen);
  /*  Затемнение, а не плита: сквозь экран должно угадываться, что игра ещё
     жива. Плотность выбрана по самому слабому тексту на экране — серому
     --ink-dim: он мельче и тусклее заголовков, и именно ему нужен запас
     контраста.
     Поверх затемнения — два мягких пятна света из макета заставки: без них
     экран читается как серая плёнка, а с ними — как та же «кабина», что и меню. */
  background: var(--scrim-light), var(--scrim);
  backdrop-filter: blur(var(--blur-screen));
  -webkit-backdrop-filter: blur(var(--blur-screen));
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  touch-action: pan-y;
  text-align: center;
}

.screen-inner {
  width: 100%;
  max-width: 560px;
  /*  margin: auto по вертикали центрирует экран, когда места хватает, и
     оставляет его у верха, когда содержимое выше окна (см. комментарий у
     .screen). Горизонтальный зазор тоже auto — на случай узкого окна. */
  margin: auto 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--gap);
}

.screen-lead {
  color: var(--ink-dim);
  font-size: var(--t-md);
  line-height: 1.6;
  max-width: 34ch;
}

/* ========================= ГЛАВНОЕ МЕНЮ ================================= */
/*  Заставка — единственный экран, который игрок видит до игры, и по макету она
    устроена как одна колонка 640px: заголовок, версия, пояснение, поле
    позывного, сегмент сложности, две кнопки и карточка клавиш. Размеры взяты
    из макета, поэтому заставка и внутренние окна выглядят роднёй, а не двумя
    разными приложениями. */
.menu-inner {
  max-width: 640px;
  gap: var(--gap-lg);
}

.menu-head {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 14px;
  text-align: center;
}

/*  Заголовок «дышит» свечением — в макете это анимация hum: цвет тени
    перетекает от бирюзы к розе. Двигается ТОЛЬКО тень, размеры и положение
    букв не меняются, поэтому вёрстка не дёргается.
    Верхний регистр — из макета: название набрано капителью. В разметке текст
    обычный, заглавные делает оформление, поэтому имя игры в файле остаётся
    тем, что задумано, а не «ASTRUM IMPETUS» в каждом поиске.
    Две строки — по просьбе владельца: «ASTRUM» и «IMPETUS» одна над другой
    (в разметке <br>). Поэтому кегль можно взять крупнее: самое длинное слово из
    семи букв занимает около 5em, и на 76px это 380px — колонка 640px его
    держит. Межстрочный меньше единицы: строки должны читаться как один блок. */
.menu-title {
  font-size: clamp(40px, 9vw, 76px);
  line-height: 0.95;
  text-transform: uppercase;
  animation: menu-hum 5s ease-in-out infinite;
}
@keyframes menu-hum {
  0%, 100% { filter: drop-shadow(0 0 18px var(--cyan-30)); }
  50% { filter: drop-shadow(0 0 30px var(--rose-30)); }
}

/*  Версия и название редакции — верхним регистром, как плашка в макете. В
    разметке текст остаётся строчным: именно его ищут проверки сборки, и
    регистр — дело оформления, а не содержания. */
.menu-meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: var(--gap);
  margin: 0;
  font-size: var(--t-sm);
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.menu-meta .version-num {
  font-weight: 600;
  color: var(--cyan);
  padding: 3px 10px;
  border: 1px solid var(--cyan-45);
  border-radius: var(--r-sm);
  background: var(--cyan-15);
}

.menu-meta .version-name {
  color: var(--ink-dim);
  font-weight: 500;
  letter-spacing: 0.02em;
}

/* --- позывной игрока -------------------------------------------------------- */
/*  Зачем поле: корабль игрока и корабль оппонента выглядят ОДИНАКОВО, и
    единственное, что их различает, — подпись над кораблём. Поле стоит на
    заставке, в той же панели, что и справка по управлению. */
.name-row {
  display: flex;
  /*  Карточка (.card) задаёт flex-direction: column — здесь это НЕ то, что
     нужно: подпись и поле стоят в одну строку, как в макете. */
  flex-direction: row;
  align-items: center;
  gap: 14px;
  width: 100%;
  padding: 6px 6px 6px var(--pad-lg);
  text-align: left;
}

.name-label {
  color: var(--ink-dim);
  font-size: var(--t-sm);
  white-space: nowrap;
}

/*  Форма поля из макета: «углубление» темнее карточки, значение прижато вправо —
    так видно, что это бланк, а не заголовок. */
.name-input {
  flex: 1 1 auto;
  min-width: 0;
  padding: 10px 14px;
  background: var(--panel-inset);
  border: 1px solid var(--line2);
  border-radius: var(--r);
  color: var(--ink);
  font-family: var(--fs);
  font-size: var(--t-lg);
  font-weight: 600;
  text-align: right;
  text-transform: uppercase;
  outline: none;
}
/*  Рамка фокуса — цветом акцента, а не системная: на тёмном стекле системная
    почти не видна, и непонятно, куда вводится позывной. */
.name-input:focus {
  border-color: transparent;
  outline: 2px solid var(--cyan);
  outline-offset: 1px;
}

/* --- версия на заставке --------------------------------------------------- */
/*  Версия — часть лица игры, а не мелкая сноска: номер заметный, название
   редакции рядом. Подпись читается на телефоне и не спорит с заголовком. */
.screen-version {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: center;
  gap: var(--gap-xs) var(--gap-sm);
  margin-top: calc(var(--gap-sm) * -1);
  font-size: var(--t-xs);
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.screen-version .version-num {
  color: var(--ink-dim);
  font-weight: 600;
}

.screen-version .version-name {
  color: var(--ink-dim);
  font-weight: 500;
}

/* На экране провала версия — просто подпись под кнопкой. */
.result-card + .screen-version,
.screen-version.t-dim {
  border: 0;
  background: none;
  box-shadow: none;
  color: var(--ink-dim);
  font-weight: 600;
}

/* --- финал забега: счёт ---------------------------------------------------- */
/*  Счёт — главная цифра экрана, поэтому он крупнее заголовка карточки: игрок
    должен увидеть «сколько я прошёл», а не искать число в списке строк. */
.victory-score {
  font-family: var(--fm);
  font-size: var(--t-hero);
  font-weight: 800;
  color: var(--gold);
  line-height: 1.1;
}

.victory-best {
  font-size: var(--t-xs);
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

/*  Строка итогов: подпись слева, значение справа. Сетка, а не flex: значения
    выравниваются по правому краю одной колонкой, как в приборной панели. */
.result-row {
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: baseline;
  gap: var(--gap-sm);
  width: 100%;
  font-size: var(--t-sm);
  color: var(--ink-dim);
}

/* --- меню: кнопки «новый забег» и «загрузить» ------------------------------ */
/*  Две кнопки в ряд 1.25 : 1, как в макете: главная шире, потому что она и есть
    основное действие. На узком экране они встают друг под друга — подпись
    «ЗАГРУЗИТЬ · 12 мин назад» длинная.
    Высота задана ЯВНО у обеих (см. .btn-hero и min-height ниже): при выравнивании
    по растяжению браузер иногда оставлял «загрузить» на её собственных 44px, и
    кнопки стояли разной высоты — это и было видно как «баг справа». */
.menu-actions {
  display: grid;
  /*  Заставка: четыре действия в один столбец. Владелец: «логотип и кнопки — новая
     игра, обучение, загрузить, зал славы»: в две колонки подписи вида
     «ЗАГРУЗИТЬ · СЛОТОВ 3» уже не влезали, а рядом с «ОБУЧЕНИЕМ» колонка читалась
     как разные по важности кнопки. Один столбец = один размер у всех. */
  grid-template-columns: 1fr;
  align-items: stretch;
  gap: var(--gap);
  width: 100%;
}
.menu-actions > .btn { min-height: 56px; }

/*  Второй шаг (имя и сложность): там действий два, и они логично встают парой. */
.setup-inner { max-width: 640px; }
.setup-actions { grid-template-columns: 1fr 1fr; }

/*  Номер версии на внутренних экранах (пауза, справка F1). Владелец: «здесь пиши
    пожалуйста номер версии — это f1»: на заставке версия есть, но в бою её не видно,
    а именно оттуда игрок обычно и сообщает, какая у него сборка. Тихо и по центру:
    это справка, а не действие. */
.screen-version {
  margin-top: var(--gap-sm);
  text-align: center;
  letter-spacing: 0.04em;
}

/*  Сноска под кнопками (в макете — «новый забег перезаписывает сохранение»).
    Отрицательный отступ подтягивает её к кнопкам: это часть той же группы. */
.menu-note {
  margin: calc(var(--gap-xs) * -1) 0 0;
  text-align: center;
}

.btn-load {
  border: 1px solid var(--line2);
  background: var(--panel-glass);
  color: var(--ink);
  padding: 10px var(--pad);
}
.btn-load:hover { border-color: var(--cyan); color: var(--ink); }

/*  Загружать нечего: кнопка выключена и говорит об этом словами. Это не
    «спрятать» — игрок должен видеть, что функция есть, и понимать, почему она
    сейчас недоступна. */
.btn-load-off {
  opacity: 0.45;
  cursor: not-allowed;
  pointer-events: none;
}

/* --- сложность на заставке ------------------------------------------------ */
/*  Четыре кнопки в ряд на широком экране и в две строки на телефоне: слово
    «НЕВОЗМОЖНО» длинное, и в один ряд на узком экране оно бы обрезалось. */
.difficulty-row {
  display: grid;
  /*  Одна колонка на всю ширину карточки: без minmax(0, 1fr) колонка могла
     оказаться уже карточки (по содержимому), и сегмент сложности не доходил до
     её правого края. */
  grid-template-columns: minmax(0, 1fr);
  gap: var(--gap-sm);
  justify-items: stretch;
  width: 100%;
  padding: var(--pad-lg);
  text-align: center;
}

.difficulty-picker {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 6px;
  width: 100%;
  padding: 5px;
  background: var(--panel-inset);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
}

/*  Кнопка сложности мельче обычной: это выбор настроения, а не главное действие.
    Высота всё равно не меньше удобной цели для пальца. */
.diff-btn {
  min-height: 40px;
  padding: 6px 4px;
  font-size: var(--t-xs);
  background: transparent;
  border: 1px solid transparent;
  border-radius: var(--r);
  color: var(--ink-dim);
  transition: color var(--dur), background-color var(--dur), box-shadow var(--dur);
}
.diff-btn:hover { color: var(--ink); border-color: transparent; box-shadow: none; }

/*  Выбранная ячейка сегмента. Класс .is-chosen ставит код, aria-pressed — тот
    же выбор словами; стилизуем оба, чтобы вид не зависел от того, что успело
    обновиться. */
.diff-btn.is-chosen,
.diff-btn[aria-pressed="true"] {
  background: var(--cyan);
  border-color: var(--cyan);
  color: var(--ink-on-accent);
  font-weight: 800;
  box-shadow: var(--glow-cyan);
}

/*  Подпись сложности занимает ДВЕ строки всегда. Иначе при переключении
    сложности карточка меняла высоту: у «НОРМАЛЬНО» подпись короче и укладывалась
    в одну строку, у остальных — в две, и поле на глазах сжималось.
    Две строки — не украшение, а запас по самому длинному тексту из
    config/difficulty.js («…копит уровни до 12, уходит за рудой, к рейдам готов»). */
.difficulty-hint {
  margin: 0;
  min-height: 2.8em;
  max-width: 46ch;
  justify-self: center;
  text-align: center;
  line-height: 1.4;
}

/* --- подсказки по управлению --------------------------------------------- */
/*  Блок подсказок — окно на заставке, поэтому он тоже стекло, а не панель: под
   экраном остаётся игровое полотно со звёздами и миром, и глухая заливка
   превратила бы его в чёрный лист. Размытие обязательно — строки подсказок
   мелкие, и без blur движение за окном спорило бы с текстом. */
.controls-help {
  margin-top: var(--gap-sm);
  padding: var(--pad-lg);
  border-radius: var(--r-xl);
  border: 1px solid var(--line);
  background: var(--panel-glass);
  backdrop-filter: blur(var(--blur-glass));
  -webkit-backdrop-filter: blur(var(--blur-glass));
  font-size: var(--t-xs);
  color: var(--ink-dim);
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--gap) var(--gap-xl);
  text-align: left;
  width: 100%;
}
.controls-help b { color: var(--ink); }

/*  Строка подсказки: клавиши слева, действие справа. min-width: 0 — иначе
    длинная подпись распирает колонку и сетка выезжает за карточку. */
.key-row {
  display: flex;
  align-items: center;
  gap: 10px;
  min-width: 0;
}
.key-row > span:last-child { min-width: 0; }
.key-row .k { display: flex; gap: var(--gap-xs); flex: none; }
.key-row.wide { grid-column: 1 / -1; }
.key-row.foot {
  grid-column: 1 / -1;
  margin: 6px 0 0;
  padding-top: 14px;
  border-top: 1px solid var(--line);
  line-height: 1.5;
}

/*  Клавиша — «кнопка» из макета: тёмная заливка, нижняя грань толще (клавиша
    нажимается), радиус 6. Она должна читаться как физическая клавиша, поэтому у
    неё своя форма, а не жирный текст в строке. */
kbd {
  font-family: var(--fs);
  font-size: var(--t-xs);
  font-weight: 600;
  color: var(--ink);
  min-width: 26px;
  text-align: center;
  padding: 3px 8px;
  background: var(--panel-key);
  border: 1px solid var(--line2);
  border-bottom-width: 2px;
  border-radius: var(--r-sm);
  box-shadow: var(--shadow-key);
  white-space: nowrap;
}

/* --- справка по управлению (F1) ------------------------------------------ */
/*  Две колонки на широком экране и одна на узком: список длинный, и в столбик
   он не влезает на телефон по высоте. */
.help-inner { max-width: 720px; }

.help-grid {
  display: grid;
  gap: var(--gap-lg) var(--gap-xl);
  grid-template-columns: 1fr 1fr;
  width: 100%;
  text-align: left;
}

.help-col { display: grid; gap: 7px; align-content: start; }

.help-head {
  margin: 0;
  font-size: var(--t-xs);
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--cyan);
}

.help-row {
  display: grid;
  grid-template-columns: minmax(84px, auto) 1fr;
  align-items: baseline;
  gap: var(--gap-sm);
  font-size: var(--t-sm);
  color: var(--ink-dim);
}

.help-keys { display: inline-flex; flex-wrap: wrap; gap: 4px; }

/*  Клавиша в справке оформлена общим правилом kbd (см. выше, в подсказках на
    заставке). Здесь только плотнее отступ: строк в справке много, и на телефон
    они должны уместиться целиком. */
.help-row kbd {
  padding: 2px 7px;
}

@media (max-width: 640px) {
  .help-grid { grid-template-columns: 1fr; }
  .help-row { grid-template-columns: minmax(76px, auto) 1fr; }
}

/* --- подсказка на экране провала ----------------------------------------- */
/*  Не украшение: она объясняет, что крестик на радаре — это место, куда стоит
   вернуться, и где смотреть число контейнеров. Без неё игрок видит крестик и
   не понимает, есть ли за ним лут. */
.screen-hint {
  max-width: 44ch;
  font-size: var(--t-sm);
  line-height: 1.55;
  color: var(--rose);
  border: 1px solid var(--rose-20);
  background: var(--rose-12);
  border-radius: var(--r-lg);
  padding: var(--gap-sm) var(--pad-lg);
}

/* --- экран провала -------------------------------------------------------- */
/*  Карточка итогов была только рамкой: цифры висели прямо на полотне и спорили
   с ним. Теперь под ними стекло — итоги читаются, а мир за ними продолжает
   угадываться. */
.result-card {
  width: 100%;
  padding: var(--pad-lg) var(--pad-xl);
  border-radius: var(--r-xl);
  border: 1px solid var(--rose-30);
  background: var(--panel-glass);
  backdrop-filter: blur(var(--blur-glass));
  -webkit-backdrop-filter: blur(var(--blur-glass));
  box-shadow: var(--shadow-panel);
  display: grid;
  gap: var(--gap-xs);
}
.result-card .label {
  font-size: var(--t-xs);
  color: var(--ink-dim);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.08em;
}
.result-scrap { color: var(--violet); }
.result-money { color: var(--lime); }

/*  Большая кнопка главного действия — заливка фирменным градиентом, как
    «Новый забег» в макете: это самое заметное действие экрана, и его должно
    быть видно раньше остальных кнопок. Тёмный текст на градиенте читается на
    всех четырёх цветах.
    Тень НЕЙТРАЛЬНАЯ, без цветного ореола: цветное свечение вокруг кнопки
    читалось как светлая полоса у её края (владелец так и сказал: «убери белую
    полосу справа»), а не как свет.
    В :hover заливка и рамка заданы ЯВНО. Причина: `.btn-home:hover` из base.css
    специфичнее `.btn-hero` и перекрашивал градиент в циановое стекло при
    наведении — главная кнопка меняла цвет на глазах. */
.btn-hero {
  min-height: 56px;
  padding: 0 var(--pad-xl);
  font-size: var(--t-md);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  border-radius: var(--r-lg);
  background: var(--grad-brand);
  border-color: transparent;
  color: var(--ink-on-accent);
  box-shadow: var(--shadow-hero);
}
.btn-hero:hover {
  background: var(--grad-brand);
  border-color: transparent;
  /*  БЕЗ filter: brightness. Владелец дважды видел «белую линию справа» у
     градиентной кнопки: это след фильтра на скруглённом градиенте — браузер
     рисует кнопку отдельным слоем, и по её краю выступает светлая кромка.
     Отклик даёт внутренняя подсветка, а не фильтр. `filter: none` обязателен:
     базовое правило .btn-primary:hover тоже ставит brightness, и без явного
     сброса линия вернулась бы у кнопки «ПРОДОЛЖИТЬ». */
  filter: none;
  box-shadow: var(--shadow-hero), inset 0 0 0 2px var(--white-10);
}
.btn-hero.danger { box-shadow: 0 0 30px var(--rose-30); }

/* --- телепорт по секторам (T) -------------------------------------------- */
/*  Лист секторов: 35 клеток одной сеткой 7 × 5, как страница календаря.
    Сетка, а не список: номер сектора игрок ищет глазами по положению, и
    колонки читаются быстрее любой строки текста. */
.teleport-inner { max-width: 520px; }

.teleport-summary {
  font-size: var(--t-sm);
  letter-spacing: 0.12em;
  color: var(--cyan);
}

.tp-grid {
  display: grid;
  /*  Ровно 7 столбцов — ширина календарной страницы. 35 секторов мира
      раскладываются в 5 ровных рядов без остатка.
      minmax(0, 1fr), а не 1fr: иначе длинная подпись клетки распирала бы
      всю сетку шире панели. */
  grid-template-columns: repeat(7, minmax(0, 1fr));
  gap: var(--gap-xs);
  width: 100%;
  padding: var(--pad);
  border: 1px solid var(--line);
  border-radius: var(--r-xl);
  background: var(--panel-glass);
  backdrop-filter: blur(var(--blur-glass));
  -webkit-backdrop-filter: blur(var(--blur-glass));
}

/*  Клетка — кнопка: по ней бьют пальцем, значит у неё минимальная удобная
    высота, а не «по размеру цифры». */
.tp-cell {
  min-height: 36px;
  padding: var(--gap-xs) 0;
  border: 1px solid var(--line2);
  border-radius: var(--r-sm);
  background: var(--panel-inset);
  color: var(--ink);
  font-family: var(--fm);
  font-size: var(--t-sm);
  font-weight: 700;
  cursor: pointer;
  transition: border-color var(--dur-fast) var(--ease),
              background-color var(--dur-fast) var(--ease),
              color var(--dur-fast) var(--ease);
}

/*  Где игрок был: клетка живая — её видно рамкой и цветом. Наведение меняет
    цвет и фон, а не положение: сдвиг обрезал бы клетку у кромки листа. */
.tp-cell.is-visited { border-color: var(--cyan-30); color: var(--cyan); }
.tp-cell.is-visited:hover { border-color: var(--cyan); background: var(--cyan-15); }

/*  Где игрок сейчас: это подпись положения, а не «выбранная» клетка — выбор
    происходит нажатием, и после него лист уже закрыт. */
.tp-cell.is-current { background: var(--cyan-15); border-color: var(--cyan); }

/*  Куда прилететь нельзя: пунктир вместо рамки и тусклая цифра. Клетка остаётся
    на месте — так лист и выглядит календарём, где просто не все дни открыты. */
.tp-cell.is-unknown {
  border-style: dashed;
  color: var(--ink-dim);
  cursor: default;
}

/*  В секторе есть НЕнайденный маяк: маленькая искра в углу клетки. Это
    подсказка «сюда стоит слетать», а не украшение — поэтому она видна и у
    закрытого сектора, где игрок ещё не был. */
.tp-cell.has-beacon { position: relative; }
.tp-cell.has-beacon::after {
  content: '✦';
  position: absolute;
  top: 1px;
  right: 3px;
  font-size: var(--t-xs);
  color: var(--gold);
  pointer-events: none;
}

/* --- именованные точки прилёта -------------------------------------------- */
/*  Базы, обломки флота и найденные маяки. Кнопки, а не список: по ним бьют
    пальцем, и каждая — точка прилёта. */
.tp-anchors-block {
  display: grid;
  gap: var(--gap-xs);
  width: 100%;
  justify-items: stretch;
}

.tp-anchors-head {
  font-size: var(--t-xs);
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-dim);
}

.tp-anchors {
  display: grid;
  gap: var(--gap-xs);
  width: 100%;
}

.tp-anchor {
  min-height: 34px;
  padding: 6px var(--gap-sm);
  border: 1px solid var(--cyan-30);
  border-radius: var(--r-sm);
  background: var(--cyan-15);
  color: var(--cyan);
  font-family: var(--fm);
  font-size: var(--t-xs);
  font-weight: 700;
  letter-spacing: 0.04em;
  text-align: left;
  cursor: pointer;
  transition: border-color var(--dur-fast) var(--ease), background-color var(--dur-fast) var(--ease);
}
.tp-anchor:hover { border-color: var(--cyan); background: var(--cyan-30); }

/* --- пауза и сохранение (Esc, P) ------------------------------------------ */
/*  Одна панель на паузу и на сохранение: игрок нажал Esc или P, игра встала, и
    здесь же лежит кнопка записи. Кнопки идут столбиком во всю ширину: это
    решения, которые нажимают не глядя, и промахнуться по ним нельзя. */
.pause-inner {
  display: grid;
  gap: var(--gap-sm);
  width: min(420px, 92vw);
  justify-items: stretch;
}

.pause-actions {
  display: grid;
  gap: var(--gap-xs);
  width: 100%;
  /*  ВСЕ КНОПКИ ПАУЗЫ ОДНОГО РАЗМЕРА. Владелец: «сделай все кнопки одного
     размера». Раньше главная кнопка была выше остальных (min-height 56 против 44
     у базовой .btn), и столбик выглядел ступеньками. */
}
.pause-actions > .btn { min-height: 56px; }

/* ===== src/css/responsive.css ===== */
/* ==========================================================================
   responsive.css — ЕДИНОЕ место для развилок по размеру экрана
   ==========================================================================
   Размеры, которые зависят от ширины/высоты окна, заданы токенами в
   tokens.css. Здесь меняются только сами токены — компоненты о медиазапросах
   не знают. Так «на телефоне радар уже» правится одной строкой, а не поиском
   по семи файлам.

   Что здесь НЕ живёт: раскладка отдельных блоков по доступному им месту —
   она описана в layout.css рядом с самими блоками.
   ========================================================================== */

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

   Раскладка по шагам:
     1. шире 470px — всё в одну строку: «Ур. 1 | 13 EXP | Склад 4 | Кредиты 0 $»
        слева, «Сектор 1» справа;
     2. 470px и уже — уходят подписи («Ур.», «Склад», «Кредиты»): значения
        остаются читаемыми, а строка — одной. Сжимать текст хуже, чем убрать
        расшифровку;
     3. 410px и уже — значок сектора уступает место строке ресурсов целиком.
        Сектор на телефоне — подсказка о глубине, а ресурсы нужны каждую
        секунду. Если места не хватило и здесь, шапка переносит сектор на
        вторую строку (flex-wrap в layout.css) — но не сжимает значения;
     4. уже 300px — блок ресурсов тоже имеет право перенестись построчно. */

/* Совсем мало: остаются значения, подписи уходят — сжимать текст хуже, чем
   убрать расшифровку. Разделители между парами остаются на месте. */
@media (max-width: 470px) {
  .stat-item .cap { display: none; }
  .stat-item + .stat-item { padding-left: var(--gap-sm); }
  .stat-cluster { gap: var(--gap-xs) var(--gap-sm); padding: 6px var(--gap-sm); }
}

/* Телефон: ресурсы важнее подсказки о секторе. Сектор уходит целиком, строка
   ресурсов остаётся одной строкой. */
@media (max-width: 410px) {
  .tier-cluster { display: none; }
}

/* --- узкий телефон -------------------------------------------------------- */
@media (max-width: 480px) {
  :root {
    --hud-edge: 10px;
    --topbar-inset: 8px;
    --radar-width: 100%;
    --touch-size: 58px;
    --pad-lg: 14px;
    --pad-xl: 18px;
  }
  .hangar-wallet { gap: var(--gap-sm); padding: 4px var(--pad-sm); }
}

/* --- заставка: ширина колонки и сегмент сложности ------------------------- */
/*  На телефоне кнопки встают друг под друга, а сегмент сложности раскладывается
    в 2 × 2: слово «НЕВОЗМОЖНО» в четыре узкие ячейки не влезает, и подпись
    обрезалась бы. Развилки здесь, потому что это размеры окна, а не раскладка
    конкретного блока. */
@media (max-width: 520px) {
  .menu-actions { grid-template-columns: 1fr; }
  .difficulty-picker { grid-template-columns: 1fr 1fr; }
  .controls-help { grid-template-columns: 1fr; }
  .menu-inner { gap: var(--gap); }
}

/* --- очень узкий экран (iPhone SE и подобные) ----------------------------- */
@media (max-width: 360px) {
  :root {
    --touch-size: 52px;
    --t-sm: 11px;
    --t-xs: 10px;
  }
  #touch-controls { gap: var(--gap-sm); }
  /* Строка ресурсов ужимается, чтобы не выдавливать кнопки и не обрезаться. */
  .stat-cluster { padding: 4px var(--gap-xs); gap: var(--gap-xs); }
  .stat-item + .stat-item { padding-left: var(--gap-xs); }
  .tier-cluster { padding: 4px var(--gap-sm); }
  .btn-row { gap: var(--gap-xs); }
  .btn-row .btn { padding: 10px 6px; }
}

/* --- низкий экран: ландшафт телефона ------------------------------------- */
@media (max-height: 520px) {
  :root {
    --touch-size: 48px;
    --topbar-inset: 6px;
    --hud-edge: 8px;
  }
  .stat-cluster { padding: 4px var(--gap-sm); }
  .tier-cluster { padding: 4px 10px; }
  .screen { padding: var(--pad) var(--pad); gap: var(--gap-sm); }
  .controls-help { display: none; }   /* в ландшафте подсказки съедают экран */
  .hangar-panel { max-height: 96vh; max-height: 96dvh; }
}

/* --- большой экран -------------------------------------------------------- */
@media (min-width: 1400px) {
  :root {
    --radar-width: 380px;
    --touch-size: 72px;
  }
}

/* --- уважение к системным настройкам ------------------------------------- */
/* Кто просил меньше движения — получает меньше движения. Игра от этого не
   ломается: все эффекты чисто декоративные. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
  }
}

/* --- тёмная тема ---------------------------------------------------------- */
/* Игра и так тёмная, но если система просит светлую — стекло становится
   плотнее, чтобы текст не тонул. Полностью непрозрачным его делать нельзя:
   тогда окна снова превратятся в плиты и перестанут отличаться от фона. */
@media (prefers-color-scheme: light) {
  :root {
    --bg0: #03060d;
    --panel-glass: linear-gradient(180deg, rgba(12, 20, 34, 0.82), rgba(7, 13, 23, 0.82));
    --panel-glass-strong: linear-gradient(180deg, rgba(10, 17, 29, 0.9), rgba(5, 10, 18, 0.92));
    --ink-dim: #93a5bd;
  }
}