/* Палитра снята пипеткой с шести скриншотов игры из Steam.
   Янтарь #F2A53B — один и тот же цвет у Upgrade Ship, SHOP и Attack.
   Красного цвета ошибки нет: перерасход показывается диагональной
   штриховкой, как игра показывает недоступное. */
:root {
  --bg: #0D0D0D;
  --line: #FFFFFF;
  --action: #F2A53B;
  --stat-now: #6DFBFF;
  --stat-gain: #F04EFF;
  --budget: #FFD548;
  --muted: rgba(255, 255, 255, 0.6);
  /* --faint красит мелкий текст: подвал 11px, подписи вкладок 9px,
     имена блоков в списке. На 0.28 он давал 2.44:1 поверх --bg при
     норме AA 4.5:1. Меряться одним лишь фоном страницы нельзя: тот же
     текст лежит на подсветках строк и вкладок, а они светлее фона, и
     0.45 давал на них 4.43-4.49:1 — снова ниже нормы. 0.5 держит
     5.15:1 на самой светлой одиночной подложке сайта и 5.34:1 на чистом
     --bg. Сторож считает подложки однослойными: вложенные подсветки он
     не складывает, а они бывают глубже — подпись вкладки под курсором
     лежит на 0.045 поверх 0.025, и там 5.13:1. Запас на такие слои и
     есть разница между 0.45 и 0.5.
     Хайрлайнам, пунктирам и рельсам шкал контраст к тексту не нужен —
     им оставлена прежняя прозрачность в --faint-line. Нового цвета не
     заводим: белый тот же, разной остаётся только прозрачность. */
  --faint: rgba(255, 255, 255, 0.5);
  --faint-line: rgba(255, 255, 255, 0.28);
  --notch: 10px;
  /* Поля страницы — токен, а не число: их вычитает --page-head, считая
     высоту шапки над колонками, и их же повторяет оверлей карточки.
     Порознь эти числа уже разъезжались: ниже 620px поля падали вдвое,
     а константа шапки оставалась прежней. Все три места читают токен,
     и переопределён он один раз — разъехаться им больше нечем. */
  --body-pad: 24px;
  --mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}

html { color-scheme: dark; }
body {
  margin: 0;
  padding: var(--body-pad);
  background: var(--bg);
  color: var(--line);
  font: 13px/1.45 var(--mono);
  font-variant-numeric: tabular-nums;
}
/* Узкому экрану поля вдвое меньше. Стоит рядом с базовым значением, а не
   среди прочих правил узкого экрана: разъехались они именно потому, что
   лежали в разных концах файла. */
@media (max-width: 620px) { :root { --body-pad: 12px; } }

/* Панели не карточки: прозрачные поверх фона, хайрлайн и срезанный угол,
   как HUD игры висит поверх космоса. Заливок и теней нет. */
.panel {
  border: 1px solid var(--line);
  background: transparent;
  padding: 16px;
  margin-bottom: 16px;
  clip-path: polygon(0 0, calc(100% - var(--notch)) 0, 100% var(--notch), 100% 100%, var(--notch) 100%, 0 calc(100% - var(--notch)));
}

.projection { color: var(--line); }

.plan-mode-tabs {
  position: relative; display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
  max-width: 620px; margin: 0 0 16px; padding: 3px;
  border: 1px solid var(--faint-line); background: rgba(255, 255, 255, 0.025);
  clip-path: polygon(0 0, calc(100% - 8px) 0, 100% 8px, 100% 100%, 8px 100%, 0 calc(100% - 8px));
}
/* Чип FOCUS на /crew — настоящая ссылка с адресом, на /training — кнопка,
   и кнопкой её делать неправильно. Одинаково выглядеть они обязаны, а
   глобального сброса подчёркивания у ссылок в файле нет: без этой строки
   один и тот же чип на двух страницах был подчёркнут и не подчёркнут. */
.mode-tab, .course-filter, .course-kind, .course-time, .run-control button {
  border: 1px solid var(--faint-line); background: transparent; color: var(--muted);
  font: inherit; text-transform: uppercase; text-decoration: none; cursor: pointer;
}
.mode-tab {
  min-height: 52px; padding: 8px 13px; border: 0;
  display: grid; grid-template-columns: 18px minmax(0, 1fr); align-items: center; gap: 9px;
  text-align: left; letter-spacing: 0.08em; transition: color 140ms ease, background 140ms ease;
}
.mode-tab.no-icon { grid-template-columns: minmax(0, 1fr); padding-left: 15px; }
.mode-tab + .mode-tab { border-left: 1px solid var(--faint-line); }
.mode-tab strong, .mode-tab small { display: block; }
.mode-tab strong { color: inherit; font-size: 11px; letter-spacing: 0.12em; }
.mode-tab small { margin-top: 2px; color: var(--faint); font-size: 9px; letter-spacing: 0; text-transform: none; }
.mode-tab:hover { color: var(--line); background: rgba(255, 255, 255, 0.045); }
.mode-tab.is-active {
  color: var(--bg); background: var(--action);
  box-shadow: inset 0 -2px 0 rgba(0, 0, 0, 0.32);
}
.mode-tab.is-active small { color: rgba(13, 13, 13, 0.7); }
.course-filter.is-active, .course-kind.is-active, .course-time.is-active {
  border-color: var(--action); color: var(--action);
}
.section-heading { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.section-heading h2 { margin-bottom: 0; }
.builder-actions { display: flex; align-items: center; gap: 12px; }
.course-picker { display: grid; gap: 8px; margin-top: 16px; }
.course-picker > label { color: var(--muted); font-size: 11px; letter-spacing: 0.16em; }
.course-picker input[type="search"] {
  grid-column: 1 / -1;
  width: 100%; border: 1px solid var(--faint-line); background: var(--bg); color: var(--line);
  padding: 8px; font: inherit;
}
.course-filters { grid-column: 1 / -1; display: flex; flex-wrap: wrap; gap: 4px; }
.course-filters.secondary { margin-top: -2px; }
.course-filter, .course-kind, .course-time { padding: 4px 7px; font-size: 10px; }
/* grid-auto-rows обязателен: список выше своей max-height, и строки auto
   Chrome сжимает до минимума карточки, то есть до её min-height. На узком
   экране карточке нужно две строки, и кнопка ADD вылезала за нижнюю рамку. */
.course-card-list { display: grid; grid-auto-rows: min-content; gap: 6px; max-height: 300px; overflow-y: auto; padding-right: 4px; }
.course-card {
  position: relative;
  display: grid; grid-template-columns: 120px minmax(0, 1fr) auto; align-items: center; gap: 10px;
  min-height: 86px; padding: 8px; border: 1px solid var(--faint-line); background: rgba(255, 255, 255, 0.025);
}
.course-card.is-selected { border-color: transparent; padding-bottom: 15px; }
.course-card.is-selected::after {
  content: ""; position: absolute; inset: -1px; z-index: 2; pointer-events: none;
  border: 1px solid var(--action); box-shadow: inset 3px 0 0 var(--action);
}
.course-card[hidden] { display: none; }
.course-card-media { align-self: start; display: grid; grid-template-rows: 68px 18px; background: #050505; }
.course-card-media img { display: block; width: 120px; height: 68px; object-fit: contain; image-rendering: pixelated; }
.course-card-time { display: grid; place-items: center; color: var(--line); font-size: 10px; letter-spacing: .08em; background: #050505; }
.course-card-time::before { content: "◷"; margin-right: 4px; color: var(--muted); }
.course-card-body { min-width: 0; display: grid; gap: 4px; align-content: center; }
.course-card-body strong { color: var(--line); font-size: 12px; letter-spacing: .04em; }
.course-card-description { color: var(--muted); font-size: 10px; line-height: 1.35; }
.course-card-meta { color: var(--stat-now); font-size: 10px; line-height: 1.35; }
.course-effects { display: flex; flex-wrap: wrap; gap: 4px; }
.course-effects > span { display: inline-flex; align-items: center; gap: 3px; padding: 1px 5px 1px 2px; background: rgba(255, 255, 255, .065); color: #50ef86; font-size: 10px; }
.course-effects img { display: block; width: 17px; height: 17px; object-fit: contain; image-rendering: pixelated; }
.course-effects b { color: #50ef86; font-size: 10px; letter-spacing: 0; }
/* Без извлечённых спрайтов на месте иконки стоит его короткое имя:
   тогда плашке нужен левый отступ, съеденный ради иконки. */
.course-effects > span:not(:has(img)) { padding-left: 5px; }
.course-card-impact { display: block; margin-top: 2px; color: var(--action); font-size: 10px; line-height: 1.4; text-decoration: none; }
.course-card-impact[hidden] { display: none; }
.stat-label { display: inline-flex; align-items: center; gap: 5px; }
.stat-label img { display: block; width: 16px; height: 16px; flex: 0 0 16px; object-fit: contain; image-rendering: pixelated; }
.tp-cost { display: inline-flex; align-items: center; justify-content: flex-end; gap: 4px; white-space: nowrap; }
.tp-cost img { display: block; width: 14px; height: 14px; object-fit: contain; image-rendering: pixelated; }
.custom-add-run {
  min-width: 72px; min-height: 34px; padding: 7px 12px;
  border: 1px solid var(--faint-line); background: rgba(255, 255, 255, 0.025);
  color: var(--muted); font: inherit; font-size: 10px; font-weight: 700;
  letter-spacing: .08em; cursor: pointer;
}
.custom-add-run:hover:not(:disabled) { border-color: var(--action); background: var(--action); color: var(--bg); }
.custom-add-run:disabled { opacity: .35; cursor: default; }
.plan-course { display: inline-flex; align-items: center; gap: 7px; min-width: 0; }
.plan-course img { display: block; width: 44px; height: 25px; flex: 0 0 44px; object-fit: contain; image-rendering: pixelated; background: #050505; }
.run-control { display: inline-grid; grid-template-columns: 24px 42px 24px; align-items: center; gap: 3px; white-space: nowrap; }
.run-control button { width: 24px; height: 24px; padding: 0; }
.run-control input { box-sizing: border-box; width: 42px; min-width: 42px; padding: 3px; text-align: center; }
.error-inline { color: var(--action); }
.empty-custom { color: var(--muted); margin: 0; }
.custom-plan-workspace + .custom-plan-workspace { margin-top: 28px; padding-top: 28px; border-top: 1px solid var(--faint-line); }
.add-custom-plan, .remove-custom-plan {
  min-height: 34px; padding: 7px 12px; border: 1px solid var(--faint-line);
  background: rgba(255, 255, 255, 0.025); color: var(--muted); font: inherit;
  font-size: 10px; font-weight: 700; letter-spacing: .08em; cursor: pointer;
}
.add-custom-plan { display: block; width: fit-content; margin: 0 0 16px auto; }
.remove-custom-plan { width: 29px; padding: 0; color: var(--faint); font-size: 18px; line-height: 1; }
.add-custom-plan:hover, .remove-custom-plan:hover { border-color: var(--action); background: var(--action); color: var(--bg); }
.custom-comparison-table th:not(:first-child), .custom-comparison-table td:not(:first-child) { text-align: right; white-space: nowrap; }
.custom-comparison-table th:last-child, .custom-comparison-table td:last-child { padding-right: 0; }
@media (max-width: 680px) {
  .plan-mode-tabs { grid-template-columns: minmax(0, 1fr); }
  .mode-tab + .mode-tab { border-left: 0; border-top: 1px solid var(--faint-line); }
  .section-heading { align-items: flex-start; flex-direction: column; }
  .builder-actions { flex-wrap: wrap; }
  .custom-plan { overflow-x: auto; }
  .course-card { grid-template-columns: 96px minmax(0, 1fr); align-items: start; }
  .course-card-media { grid-template-rows: 54px 18px; }
  .course-card-media img { width: 96px; height: 54px; }
  .custom-add-run { grid-column: 2; justify-self: end; min-height: 30px; }
}

.calculate {
  border: 1px solid var(--action);
  color: var(--bg);
  background: repeating-linear-gradient(45deg, var(--action) 0 6px, #d99333 6px 12px);
  font: inherit;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  padding: 10px 20px;
  cursor: pointer;
}

.is-capped td { color: var(--action); }
.is-zero { color: var(--faint); }
.error { border-color: var(--action); color: var(--action); }
.empty { color: var(--muted); }

.columns { display: grid; grid-template-columns: minmax(250px, 290px) minmax(0, 1fr); align-items: start; gap: 16px; margin-top: 20px; }
@media (max-width: 860px) { .columns { grid-template-columns: minmax(0, 1fr); } }

:focus-visible { outline: 2px solid var(--stat-now); outline-offset: 2px; }
@media (prefers-reduced-motion: reduce) { * { transition: none !important; animation: none !important; } }

.masthead {
  display: flex; align-items: center; gap: 20px;
  border-bottom: 1px solid var(--faint-line); padding-bottom: 12px; margin-bottom: 20px;
}
.wordmark {
  display: inline-flex; align-items: center; gap: 8px;
  color: var(--action); text-decoration: none; letter-spacing: 0.2em;
}
.wordmark img { display: block; width: 40px; height: 40px; image-rendering: pixelated; }
.wordmark:hover img { filter: brightness(1.15); }
.masthead nav a { color: var(--line); text-decoration: none; }
.masthead nav a:hover { color: var(--action); }
.masthead nav ul { list-style: none; margin: 0; padding: 0; }

h1 { font-size: 18px; letter-spacing: 0.16em; margin: 0 0 4px; text-transform: uppercase; }
h2 { font-size: 12px; letter-spacing: 0.18em; text-transform: uppercase; margin: 0 0 12px; }
h3 { font-size: 11px; letter-spacing: 0.16em; text-transform: uppercase;
     color: var(--muted); font-weight: 700; margin: 20px 0 8px; }
.crew-name { letter-spacing: normal; text-transform: none; }
.rarity-common { color: #969696; }
.rarity-elite { color: #FFFFFF; }
.rarity-unique { color: #58A6FF; }
.rarity-epic { color: #C56BFF; }
.rarity-hero { color: #C99B14; }
.rarity-special { color: #FFE78A; }
.rarity-legendary { color: #F28C28; }
.tagline { color: var(--muted); margin: 0 0 16px; }

.field { display: block; font-size: 11px; letter-spacing: 0.16em;
         text-transform: uppercase; color: var(--muted); font-weight: 700; margin-bottom: 4px; }
.training { color: var(--muted); font-size: 11px; letter-spacing: 0.16em; margin: 8px 0 0; }
.training strong { color: var(--action); font-size: 15px; font-weight: 700; letter-spacing: 0; }
.selected-crew { display: grid; grid-template-columns: 72px minmax(0, 1fr); align-items: center;
                 gap: 12px; margin-top: 12px; padding: 8px; border: 1px solid var(--faint-line);
                 background: rgba(255, 255, 255, 0.025); }
.crew-portrait { display: block; width: 72px; height: 72px; object-fit: contain; image-rendering: pixelated; }
.crew-portrait-missing { box-sizing: border-box; display: grid; place-items: center;
                         border: 1px dashed var(--faint-line); color: var(--faint); font-size: 9px; }
.selected-crew-info { min-width: 0; }
.selected-crew-info > .crew-name { display: block; overflow-wrap: anywhere; font-weight: 700; }
/* Имя ведёт в карточку крю. Цвет здесь занят редкостью, поэтому состояние
   показывает подчёркивание, а не смена цвета: своего значения у цвета
   ссылки в этом блоке нет. */
.selected-crew-info > a.crew-name { text-decoration: underline; text-decoration-color: var(--faint-line); text-underline-offset: 3px; }
.selected-crew-info > a.crew-name:hover { text-decoration-color: currentColor; }
.selected-crew-info .training { margin-top: 6px; }

.stat-row, .prio-row {
  display: grid;
  justify-content: start; align-items: center; gap: 6px; margin-bottom: 4px;
}
.stat-row { grid-template-columns: 62px 24px 60px 24px; }
/* Ряд с селектом: подпись и поле. Колонок под степперы здесь нет,
   поэтому и сетка своя — иначе селект попадает в 24-пиксельную колонку
   и держится в ней только заданной шириной. */
.stat-row.select-row { grid-template-columns: 62px 170px; }
.stat-row.select-row select { width: 100%; margin-top: 0; }
.weapon-mark { color: var(--muted); font-size: 0.85em; }
.prio-row { grid-template-columns: 62px 24px 60px 24px 24px; }
.short { color: var(--muted); }
.step {
  border: 1px solid var(--faint-line); background: transparent; color: var(--line);
  font: inherit; line-height: 1; padding: 3px 0; cursor: pointer;
}
.step:hover:not(:disabled) { border-color: var(--action); color: var(--action); }
.step:disabled { opacity: 0.3; cursor: default; }
.priority-max svg { display: block; margin: auto; }
input[type=number], input[type=search], select {
  border: 1px solid var(--faint-line); background: transparent; color: var(--line);
  font: inherit; padding: 3px 6px;
}
select { width: 100%; margin-top: 4px; }
input[type=search] { width: 100%; box-sizing: border-box; }
input[type=number] {
  appearance: textfield; -moz-appearance: textfield; text-align: center;
}
input[type=number]::-webkit-inner-spin-button,
input[type=number]::-webkit-outer-spin-button { appearance: none; margin: 0; }
.crew-picker { position: relative; }
.crew-results {
  position: absolute; z-index: 1; top: calc(100% + 4px); left: 0; right: 0;
  max-height: 248px; overflow-y: auto; border: 1px solid var(--action);
  background: var(--bg);
}
.crew-result {
  display: block; width: 100%; border: 0; border-bottom: 1px solid var(--faint-line);
  background: transparent; color: var(--line); font: inherit; padding: 6px;
  text-align: left; cursor: pointer;
}
.crew-result:last-child { border-bottom: 0; }
/* Стрелками список ходит без переноса фокуса, поэтому активный пункт
   красится тем же янтарём, что и пункт под курсором: для читателя это
   одно и то же «вот сюда сейчас нажмут». */
.crew-result:hover, .crew-result:focus-visible,
.crew-result.is-active { background: var(--action); color: var(--bg); }
.hint, .note { color: var(--faint); font-size: 11px; }
/* Подпись в панели настройки прижата к своему контролу: сверху её держит
   отступ заголовка, снизу остаётся щель до поля. Абзацных отступов брать
   неоткуда — в панели они шире, чем зазор между самими рядами. */
.setup .hint { margin: 0 0 4px; }
/* Сокращение с расшифровкой помечено пунктиром снизу, а не цветом: цвет
   на сайте принадлежит данным. Пунктир тот же хайрлайн, что у рамок,
   поэтому он и берёт --faint-line, а не заводит новый цвет. */
abbr[title] { text-decoration: underline dotted var(--faint-line); text-underline-offset: 3px; cursor: help; }
.priority-heading { display: flex; align-items: center; gap: 6px; }
.remaining, .remaining output { color: var(--action); font-size: 11px; font-weight: 700; }
.remaining { margin-left: auto; white-space: nowrap; }
.priority-clear { display: inline-grid; place-items: center; width: 29px; height: 27px; padding: 0;
                  border: 1px solid transparent; background: transparent; color: var(--faint); cursor: pointer; }
.priority-clear svg { display: block; transition: transform 120ms ease; }
.priority-clear:hover { border-color: var(--action); background: transparent; color: var(--action); }
.priority-clear:hover svg { transform: translateY(-1px); }
.linklike {
  border: 0; background: transparent; color: var(--budget); font: inherit;
  font-size: 11px; text-decoration: underline; cursor: pointer; padding: 0; margin-left: 6px;
}

table { border-collapse: collapse; width: 100%; }
th { text-align: left; font-weight: normal; color: var(--muted); padding: 5px 8px 5px 0; }
td { padding: 4px 8px 4px 0; }
thead tr { border-bottom: 1px solid var(--faint-line); }
tfoot tr { border-top: 1px solid var(--faint-line); }
tfoot th { color: var(--line); }
.plan h2, .projection h2, .custom-plan h2, .custom-projection h2 { color: var(--line); }
.training-plan-table, .projection-table { color: var(--line); font-size: 12px; line-height: 1.4; }
.training-plan-table thead th, .projection-table thead th { color: var(--muted); font-size: 11px; }
.training-plan-table tbody td, .projection-table tbody td { color: var(--line); font-size: 12px; }
.training-plan-table th:not(:first-child), .training-plan-table td:not(:first-child) { white-space: nowrap; }
.training-plan-table th:nth-child(n+3), .training-plan-table td:nth-child(n+3) { text-align: right; }
.training-plan-table th:last-child, .training-plan-table td:last-child { padding-right: 0; }
.projection-table { table-layout: fixed; }
.projection-table th { width: 20%; }
.projection-table th:not(:first-child), .projection-table td:not(:first-child) { text-align: right; white-space: nowrap; }
.projection-table th:last-child, .projection-table td:last-child { padding-right: 0; }
.projection-table tr.is-priority td { background: rgba(109, 251, 255, 0.07); }
.projection-table tr.group-break td { border-top: 1px solid var(--faint-line); padding-top: 10px; }
.muted { color: var(--faint); }

.training-spent { display: grid; grid-template-columns: 52px 28px; align-items: center; gap: 8px;
                  width: fit-content; margin-left: auto; }
.training-spent .bar { height: 8px; overflow: hidden; background: var(--faint-line); }
.training-spent .bar::before {
  content: ""; display: block; height: 100%; width: var(--fill);
  background: var(--stat-now); transform: scaleX(var(--bar-scale, 1));
  transform-origin: left; transition: transform 320ms ease-out;
}

.strategy-switch { border: 0; padding: 0; margin: 16px 0; min-width: 0; }
.strategy-switch legend { color: var(--muted); font-size: 11px; letter-spacing: 0.16em;
                          text-transform: uppercase; font-weight: 700; margin-bottom: 7px; }
.strategy-track { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); width: 100%;
                  border: 1px solid var(--faint-line); padding: 2px;
                  background: rgba(255, 255, 255, 0.025);
                  clip-path: polygon(0 0, calc(100% - 8px) 0, 100% 8px, 100% 100%, 8px 100%, 0 calc(100% - 8px)); }
.strategy-track label { cursor: pointer; min-width: 0; }
.strategy-track input { position: absolute; opacity: 0; pointer-events: none; }
.strategy-option { display: flex; align-items: center; justify-content: center; box-sizing: border-box;
                   min-height: 34px; padding: 6px 4px; color: var(--muted);
                   text-align: center; font-size: 9px; font-weight: 700; line-height: 1.15; white-space: normal;
                   letter-spacing: .04em; transition: color 140ms ease, background 140ms ease; }
.strategy-track label + label .strategy-option { border-left: 1px solid var(--faint-line); }
.strategy-track label:first-child .strategy-option { border-left: 0; }
.strategy-track label:hover .strategy-option { color: var(--line); background: rgba(255, 255, 255, .045); }
.strategy-track input:checked + .strategy-option { color: var(--bg); background: var(--action); box-shadow: inset 0 -2px 0 rgba(0, 0, 0, .32); }
.strategy-track input:focus-visible + .strategy-option { outline: 2px solid var(--stat-now); outline-offset: -2px; }

/* Четыре режима VALUES — это не список, а таблица истинности двух
   признаков: строки различаются снаряжением, колонки тренировкой.
   Сетка 2x2 это показывает, полоса из четырёх плиток прятала бы. Порядок
   в разметке тот же, что в колонках таблицы, и по сетке он читается
   слева направо и сверху вниз без единой подписи осей.
   Ширина панели 262px: по 131px на плитку, и «TRAINED + GEAR» встаёт
   в строку. В полосе из четырёх плитке досталось бы 65px, и одна
   подпись из четырёх шла бы в две строки, а три в одну.
   Границы переставлены под сетку: вертикальная у правого столбца,
   горизонтальная у нижнего ряда. Правило label + label осталось бы
   у третьей плитки, где ряд начинается заново. */
.strategy-track.quad { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.strategy-track.quad label + label .strategy-option { border-left: 0; }
.strategy-track.quad label:nth-child(even) .strategy-option { border-left: 1px solid var(--faint-line); }
.strategy-track.quad label:nth-child(n+3) .strategy-option { border-top: 1px solid var(--faint-line); }
.howitworks { width: 100%; max-width: none; margin: 32px 0 0; border-top: 1px solid var(--faint-line); }
.howitworks > summary { cursor: pointer; display: flex; align-items: baseline; justify-content: space-between;
                       gap: 16px; padding: 18px 0; color: var(--line); list-style: none; }
.howitworks > summary::-webkit-details-marker { display: none; }
.howitworks > summary::after { content: "+"; color: var(--action); font-size: 20px; line-height: 1; }
.howitworks[open] > summary::after { content: "−"; }
.howitworks > summary span { font-size: 11px; text-transform: uppercase; letter-spacing: .12em; }
.howitworks > summary small { margin-left: auto; color: var(--faint); font-size: 10px; font-weight: normal; }
.howitworks-body { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); column-gap: 40px; row-gap: 6px; padding-top: 8px;
                  color: var(--muted); font-family: var(--mono); font-size: 13px; font-weight: 400; line-height: 1.45;
                  letter-spacing: normal; text-transform: none; }
.howitworks .n { color: var(--action); margin-right: 8px; }
.howitworks article { margin-bottom: 24px; }
.howitworks p, .howitworks li { color: inherit; }

/* Подвал двухъярусный: верхний ярус — выходные данные по краям строки,
   нижний — правовые оговорки в две колонки. Права на игру слева, наши
   собственные правила справа: два разных обязательства, и колонка —
   самая дешёвая граница между ними. Читаются как сноска, поэтому тот
   же --faint и никакой рамки — отделяет их только отступ. */
.footer { color: var(--faint); border-top: 1px solid var(--faint-line);
          margin-top: 32px; padding-top: 12px; font-size: 11px; }
.footer-meta { display: flex; align-items: center; justify-content: space-between; gap: 16px; flex-wrap: wrap; }
.footer-legal { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
                gap: 6px 32px; margin-top: 10px; }
.footer-legal p { margin: 0; }
.footer a { color: var(--muted); }
.footer a:hover { color: var(--action); }
.index-nav { list-style: none; padding: 0; }
/* Калькуляторы перечисляет вложенный список, а не строка описания:
   на втором калькуляторе описание бы устарело, а список — нет. */
.index-nav ul { list-style: none; padding-left: 18px; margin: 6px 0 0; }

/* Список ссылок на странице 404 — тот же приём, что у .index-nav:
   плоский список без маркеров, заголовок раздела ссылкой. */
.notfound-links { list-style: none; padding: 0; }

/* Таблицы плана шире мобильного экрана — пусть скроллится таблица,
   а не вся страница. */
@media (max-width: 620px) {
  /* Таблица справочника скроллится своей обёрткой .crew-scroll:
     display:block превратил бы её строки в блоки и убил бы липкие
     колонки, ради которых обёртка и заведена.
     Отдельная страница карточки панелью не обёрнута — там таблицу статов
     называет её собственная обёртка. Без этого одна и та же карточка вела
     себя по-разному: в оверлее поверх списка она лежит в .panel и таблица
     скроллилась сама, а на своей странице за край уезжала вся страница. */
  .panel table:not(.crew-table), .crew-card-page table { display: block; overflow-x: auto; }
  .howitworks-body { grid-template-columns: minmax(0, 1fr); }
  .howitworks > summary small { display: none; }
  .footer-meta { display: grid; justify-content: start; gap: 5px; }
  .footer-legal { grid-template-columns: minmax(0, 1fr); }
}

/* Справочник крю. Ни одного нового цвета и ни одного литерального:
   только токены из :root. Затемнение оверлея делается прозрачностью
   поверх --bg, а не собственным rgba, иначе палитра раздваивается.

   cap-track разделяет три понятия тремя отрезками — базу, прирост
   тренировки и снаряжение, в порядке колонок строки. Смешение
   снаряжения с тренировкой свело бы вместе ровно то, что раздел
   терминов спеки разводит. */
.crew-table td, .crew-table th { white-space: nowrap; }
.crew-table td.metric, .crew-table th.metric { text-align: right; }
.crew-table td.is-capped { color: var(--action); }
/* Ячейка портрета: 32px картинки плюс 6px отступа — те самые 38px,
   на которых стоит липкая колонка имени. Правила только для tbody:
   в шапке ячейка пустая, и высота 32px растянула бы там ряд.
   Заглушка меряется по внешнему краю, иначе её пунктир добавляет
   к ширине свои два пикселя, и имя наезжает на портрет. */
.crew-table td.col-portrait { width: 32px; padding: 2px 6px 2px 0; }
.crew-table td.col-portrait img, .crew-table td.col-portrait .portrait-blank {
  display: block; box-sizing: border-box; width: 32px; height: 32px;
}

/* Портреты — пиксель-арт 200×200, сжатый до 32 и 64. Сглаживание
   размывает их в кашу, поэтому pixelated обязателен. Заглушка держит
   ровно тот же размер: иначе строка без файла короче остальных. */
.crew-portrait, .crew-table td.col-portrait img { image-rendering: pixelated; }
.portrait-blank { border: 1px dashed var(--faint-line); background: transparent; }
/* В списке у заглушки нет подписи: пунктирный квадрат один сообщает, что
   портрета нет. Это отметка, а не хайрлайн, — единственная граница на
   сайте, которой контраст нужен, и красится она текстовым --faint.
   На сцене карточки и в панели /training рядом стоит «NO IMAGE», там
   значение несёт подпись, а рамка вокруг неё остаётся хайрлайном. */
.crew-table td.col-portrait .portrait-blank { border-color: var(--faint); }
/* Карточка. По умолчанию колонок нет: блоки идут в порядке разметки,
   он же порядок чтения — сцена, статы, способность, коллекция. Правило
   order отсюда убрано: оно расходилось с обходом по клавиатуре.

   Шире 860px — две колонки, и каждая свой поток. COLLECTION начинается
   сразу под сценой: таблица статов вдвое длиннее сцены, и на общем ряду
   он ждал бы её конца, оставляя в правой колонке дыру в полтораста
   пикселей. Держит это второй ряд в 1fr — элемент, пересекающий гибкий
   ряд, рядов с auto не растит, поэтому высоту первого ряда задаёт одна
   сцена, а весь остаток таблицы уходит во второй. */
@media (min-width: 861px) {
  .card-grid { display: grid; grid-template-columns: minmax(0, 1fr) 360px; grid-template-rows: auto 1fr;
               gap: 0 24px; align-items: start; }
  .card-stage { grid-column: 2; grid-row: 1; }
  .card-main { grid-column: 1; grid-row: 1 / span 2; }
  .card-collection { grid-column: 2; grid-row: 2; }
}
.card-close { position: absolute; top: 12px; right: 12px; display: inline-grid; place-items: center; width: 29px; height: 27px;
              border: 1px solid transparent; color: var(--faint); }
.card-close:hover { border-color: var(--action); color: var(--action); }
.card-overlay > .panel, .crew-card-page { position: relative; }

/* Сцена: спрайт в нативных 200×200, платформа двумя хайрлайнами, слоты
   квадратами со срезанным углом по бокам — статовые справа, остальные слева. */
.stage { display: grid; grid-template-columns: 44px minmax(0, 1fr) 44px; align-items: center; gap: 12px; padding: 20px 0 8px; }
.stage-sprite { position: relative; display: grid; place-items: center; }
.stage-sprite .crew-portrait { width: min(200px, 100%); height: auto; aspect-ratio: 1; }
.stage-sprite .portrait-blank { display: grid; place-items: center; box-sizing: border-box; width: min(200px, 100%); height: auto; aspect-ratio: 1; font-size: 10px; color: var(--faint); }
.stage-floor { display: block; width: min(160px, 100%); height: 0; border-top: 1px solid var(--muted); margin-top: 6px; }
.stage-floor.faint { width: min(110px, 100%); border-color: var(--faint-line); margin-top: 3px; }
.stage-left, .stage-right { display: grid; gap: 8px; align-content: center; }

/* Четыре состояния слота: есть статовый — сплошная яркая рамка, нет
   статового — сплошная бледная, есть мёртвый — сплошная приглушённая,
   нет мёртвого — пунктир. */
.slot-box { display: grid; place-items: center; gap: 1px; width: 40px; height: 40px; box-sizing: border-box; padding: 3px 0 2px;
            border: 1px dashed var(--faint-line); color: var(--faint);
            clip-path: polygon(0 0, calc(100% - 6px) 0, 100% 6px, 100% 100%, 6px 100%, 0 calc(100% - 6px)); }
.slot-box b { font-size: 8px; letter-spacing: .08em; font-weight: 700; }
.slot-box:not(.dead) { border-style: solid; }
.slot-box.owned { border-color: var(--line); color: var(--line); }
.slot-box.owned.dead { border-style: solid; border-color: var(--muted); color: var(--muted); }
/* Имя крю выглядит одинаково на отдельной странице и в оверлее, а тег
   у него разный: на странице h1, во фрагменте h2 (там h1 уже занят
   заголовком списка). Разметке уровень заголовка нужен верный, глазу —
   один и тот же вид, поэтому правило выравнивает всё, что у базовых h1
   и h2 разное: отступ, размер и разрядку. Разрядка взята заголовочная,
   0.16em: h2 со своими 0.18em тут единственный на весь сайт, кто выбит
   из общего ряда. */
.stage-id h1, .stage-id h2 { margin: 0; font-size: 18px; letter-spacing: 0.16em; }
/* Место под крестик закрытия: он висит в правом верхнем углу карточки,
   а имя теперь стоит там же и уезжало под него. */
.stage-id { padding-right: 44px; } /* 31px кнопки + 12px её отступа */
.stage-notes { margin-top: 8px; }
.stage-notes p:last-child { margin-bottom: 0; }
.card-ref { margin-top: 20px; padding-top: 12px; border-top: 1px solid var(--faint-line); }
.card-ref h2 { display: flex; align-items: center; gap: 8px; min-height: 24px; }
.card-ref img { image-rendering: pixelated; }
.empty-ref { color: var(--muted); }
/* Левая колонка вдвое шире правой, и строка в ней уезжала за сотню
   знаков. Мера — в ch, а не в пикселях: шрифт моноширинный. */
.card-ability p { max-width: 68ch; }

/* Баннер коллекции — единственная заливка на всём сайте: это картинка
   игры, а не украшение интерфейса. Стоит ПОД всем текстом блока и
   закрывает его снизу: текст на неё не кладётся вовсе, у семнадцати
   коллекций фоны разной яркости, и любая подпись поверх потерялась бы
   на половине из них. Пропорция 1024×124 при ширине колонки даёт ровно
   44px, так что cover почти ничего не режет; срезанный угол тот же, что
   у панелей. Сглаживание не снимается: это не пиксель-арт, а рисованные
   текстуры. */
.collection-banner {
  display: block; width: 100%; height: 44px; margin: 14px 0 0;
  object-fit: cover; object-position: center; image-rendering: auto;
  clip-path: polygon(0 0, calc(100% - var(--notch)) 0, 100% var(--notch), 100% 100%, var(--notch) 100%, 0 calc(100% - var(--notch)));
}

/* Ширины колонок у карточки свои. Класс .projection-table она берёт ради
   шрифта, выравнивания и подсветки строк, но его th{width:20%} посчитаны
   на пять колонок проекции, а здесь их семь: 7×20% = 140%, браузер делит
   поровну, и трёхбуквенное имя стата получает ровно столько же места,
   сколько «POOL COST».
   Названы три колонки, ширина которых от ширины таблицы не зависит.
   Считаны они по содержимому, а к ячейке её 8px отступа справа браузер
   добавляет сам: имя стата — иконка 16 плюс отступ 6 плюс трёхбуквенное
   имя; дорожка — ровно её собственный min-width, те же 90px, из которых
   сложен и порог контейнерного запроса ниже; колонка plan — слово plan,
   и отступа справа у неё нет вовсе. Ширина висит на имени колонки, а не
   на её позиции: у карточки, открытой с планировщика, колонки plan нет
   вовсе, и правило на :last-child село бы на шкалу CAP, срезав её
   с девяноста до сорока.
   Остаток поровну делят четыре числовые колонки. Им задана доля, а не
   auto: шестая колонка уезжает по контейнерному запросу, и место в
   таблице за ней остаётся. Долю браузер пересчитывает на оставшиеся
   колонки, auto же оставлял под спрятанной колонкой дыру в её ширину,
   а числа жались в остаток и обрезались.
   Блок целиком отдан широкому экрану: ниже 680px таблица считает
   колонки сама (table-layout: auto), и доли ей только мешают. */
@media (min-width: 681px) {
  .card-table th:first-child { width: 56px; }
  .card-table th:nth-child(n+2):nth-child(-n+5) { width: 25%; }
  .card-table th:nth-child(6) { width: 90px; }
}

/* Статы группами: заголовок группы — строка-разделитель приглушённым капсом. */
.card-table .group-row th { padding-top: 12px; color: var(--faint); font-size: 10px; letter-spacing: .16em; text-align: left; border-bottom: 1px solid var(--faint-line); }
.card-table .total-row td { border-top: 1px solid var(--faint-line); padding-top: 8px; color: var(--muted); }
/* Нетренируемый стат приглушён: слов «not trainable» в строке больше нет,
   и разницу между «нечего показывать» и «показать нечем» держит цвет. */
.card-table tr.not-trainable td { color: var(--muted); }
.flag-text { color: var(--action); font-size: 10px; letter-spacing: .08em; text-transform: uppercase; }

/* Число в колонке TRAINED — ссылка в планировщик; отдельной колонки со
   словом plan нет. Цвет числа остаётся цветом данных: янтарь на сайте
   значит «упёрлось в потолок», и красить им кликабельность значило бы
   сказать неправду. Кликабельность несёт тонкое подчёркивание — тем же
   приёмом, что имя крю на /training, и оно наливается под курсором.
   color: inherit обязателен: базового цвета ссылок в файле нет, и без
   него браузер выкрасил бы колонку чисел своим синим. */
.card-table a.plan-link { color: inherit; text-decoration: underline; text-decoration-color: var(--faint-line); text-underline-offset: 3px; }
.card-table a.plan-link:hover { text-decoration-color: currentColor; }
/* Своё имя, а не .hint из /training: там подпись — отдельная
   строка над селектом (11px, читается как подпись к полю формы),
   здесь — вторая строка внутри тесной ячейки таблицы статов (10px,
   как и остальные подписи в этой колонке). Общее имя раньше держало
   обе роли одним правилом, и то, что писалось позже в файле, тихо
   перебивало кегль другой странице. */
.card-weapon-note { color: var(--muted); font-size: 10px; }
/* Числа в колонках не переносятся, а подписи под ними — обязаны:
   заметка про оружие это фраза в 24 знака, и доля таблицы такой ширины
   не даёт ни на одном экране. Без переноса заметка ложилась на соседнюю
   колонку поверх её чисел. */
.card-table tbody td .card-weapon-note { white-space: normal; }
/* Флаг ложился туда же и по той же причине, но переносом его не спасти:
   разрыв пришёлся бы на пробел перед ним, а тот стоит в самой ячейке,
   и ячейка числа не переносит. Поэтому флаг встаёт под число отдельной
   строкой — там же, где стоит заметка про оружие, и читается так же:
   подпись к числу, а не часть его. */
.card-table tbody td .flag-text { display: block; }
/* Перенос перед заметкой ставит разметка, и после блочного флага он
   лишний: строку заметке открывает сам блок, а <br> добавляет к ней
   пустую — 31px воздуха в ряду. Убирается он, а не блок: без <br>
   заметка слиплась бы с числом там, где флага нет. */
.card-table tbody td .flag-text + br { display: none; }

/* Сплошная шкала: три отрезка база / снаряжение / тренировка, штриховка —
   выброшенный пул. Ширина ячейки = CAP стата, поэтому капнутый стат заполнен целиком. */
/* Рельса под отрезками — фон, а не отметка: значение несут отрезки,
   и рельса остаётся на прежней прозрачности. Штриховка перерасхода,
   наоборот, единственная отметка своего понятия и красится --faint. */
.cap-track { display: flex; height: 6px; min-width: 90px; background: color-mix(in srgb, var(--faint-line) 35%, transparent); }
.cap-track i { display: block; height: 100%; }
.cap-track .base { background: var(--stat-now); }
.cap-track .gear { background: var(--budget); }
.cap-track .trained { background: var(--stat-gain); }
.cap-track .wasted { background: repeating-linear-gradient(45deg, var(--faint) 0 3px, transparent 3px 6px); }

/* Оверлей лежит поверх всей страницы и повторяет её поля — тем же токеном,
   а не той же парой чисел: третий экземпляр 24/12 на том же брейкпойнте
   разъезжался бы с первыми двумя ровно так же, как разъехались поля
   страницы с --page-head. */
.card-overlay {
  position: fixed; inset: 0; z-index: 10; overflow-y: auto; padding: var(--body-pad);
  background: var(--bg); opacity: .97;
}
.card-overlay > .panel { max-width: 900px; margin: 0 auto; }

/* Семь колонок не всегда помещаются, и ячейки налезали друг на друга.
   Шкала уходит первой: она повторяет числа, которые остаются рядом,
   а «plan» — единственное действие в строке и остаться обязано.

   Условие — ширина колонки, а не ширина окна: на ≤860px сетка карточки
   схлопывается в один столбец и таблица получает всю ширину, а тесно ей
   как раз на 861-1165px, где сетка в две колонки оставляет левой 448px.
   Порог считается прямо: семь колонок по 90px дорожки и 8px отступа —
   686px на таблицу, а таблица занимает всю ширину блока.

   Порог остаётся общим и для карточки без колонки plan: там таблице
   хватает места на сорок пикселей дольше, и шкала уезжает чуть раньше,
   чем ей перестало хватать. Второй порог ради этих сорока пикселей
   развёл бы два правила, которые обязаны срабатывать вместе. */
.card-stats { container-type: inline-size; container-name: card-stats; }
@container card-stats (max-width: 686px) {
  .card-table thead th:nth-child(6), .card-table tbody td:nth-child(6) { display: none; }
  /* Имя пятой колонки — из двух слов, и в строку оно шире всех своих
     чисел: колонка раздувается до имени и утаскивает таблицу за край.
     Перенос по пробелу ставит имя в две строки шапки и делает колонку
     вдвое уже. Числа под ним не переносятся: у них пробелов нет.
     Условие тут то же самое, что и у пропажи шестой колонки, и это не
     совпадение: тесно таблице от ширины блока, а не окна. Порознь эти
     два правила разъезжались бы на узком блоке внутри широкого окна —
     карточка в двухколоночной сетке на 861-1165px. */
  .card-table thead th:nth-child(5) { white-space: normal; }
}

/* На узком экране доли ширины сходятся в ничто: колонки считает таблица. */
@media (max-width: 680px) {
  .card-table { table-layout: auto; }
  .card-table th:first-child { width: auto; }
  /* Имя стата не переносится: иконка и подпись — одна единица. */
  .card-table th:first-child, .card-table tbody td:first-child { white-space: nowrap; }
  /* Здесь ширину колонки задаёт её содержимое, и колонка растёт под
     заметку сама. Перенос дробил бы её на строки в шесть знаков. */
  .card-table tbody td .card-weapon-note { white-space: nowrap; }
}

/* Заметка про оружие в одну строку требует 177px, и на пятую колонку их
   уходит почти половина таблицы. Пока пятой была POOL COST со своими
   тремя знаками, остатка хватало; с приходом полного режима колонок стало
   на 17px больше, и «plan» — единственное действие строки — вылезал за
   край экрана. Замерено: при nowrap колонкам нужно 387px, то есть окно
   от 411px. Ниже заметка переносится; трёх строк она стоит только у DMG
   и только у крю со слотом оружия. */
@media (max-width: 410px) {
  .card-table tbody td .card-weapon-note { white-space: normal; }
}


/* В навигации стало три раздела, и разделять их пробелом из разметки
   больше нельзя: «Training Crew Calculators» читается как одна строка.
   Правило дописано сюда, а не к .masthead выше, чтобы блок раздела
   оставался цельным. Список плоский на вид — маркеры и отступы сняты
   правилом выше. */
.masthead nav .nav-links { display: flex; flex-wrap: wrap; gap: 18px; }

/* У раздела с подменю тот же знак раскрытия, что у секций формы и
   у GEAR: ▸ закрыто, ▾ раскрыто, цвет --faint. Свой значок здесь
   означал бы, что на сайте два разных способа сказать одно и то же. */
.masthead nav .has-menu { position: relative; }
.masthead nav .has-menu > a::before { content: "▸ "; color: var(--faint); }
.masthead nav .has-menu:hover > a::before,
.masthead nav .has-menu:focus-within > a::before { content: "▾ "; }

/* Подменю висит поверх страницы, а не раздвигает шапку. Раскрывает
   наведение и таб: :focus-within даёт клавиатуре тот же путь, что мыши.
   На тач-устройствах ни того, ни другого нет — там ссылка ведёт на хаб
   /calc, и он перечисляет то же самое. Это маленькая панель HUD: тот же
   срезанный угол, что у .panel, но хайрлайн приглушён — меню
   вспомогательное и не спорит яркостью с содержимым страницы. Фон
   непрозрачный, иначе сквозь меню читается страница; цвет тот же --bg,
   нового в палитре не заводим. */
.masthead nav .menu {
  position: absolute; left: 0; top: 100%; z-index: 10;
  display: none;
  padding: 10px 14px;
  background: var(--bg); border: 1px solid var(--faint-line);
  clip-path: polygon(0 0, calc(100% - var(--notch)) 0, 100% var(--notch), 100% 100%, var(--notch) 100%, 0 calc(100% - var(--notch)));
}
.masthead nav .has-menu:hover .menu,
.masthead nav .has-menu:focus-within .menu { display: grid; gap: 8px; }
/* Ширину меню задаёт самое длинное название, а не круглое число: колонка
   грида иначе схлопывается и «Crew level cost» переносится по словам. */
.masthead nav .menu li { white-space: nowrap; }
.masthead nav .menu a { color: var(--muted); }
.masthead nav .menu a:hover { color: var(--action); }

/* Бургер нужен только узкому экрану; на широком навигация стоит строкой,
   и кнопки нет вовсе. Переключатель — скрытый чекбокс, а не <details>:
   `:checked ~` описан спецификацией и ведёт себя одинаково везде, тогда
   как показ содержимого закрытого <details> на широком экране — поведение
   неспецифицированное, и проверить его в мобильном Safari нечем. */
.burger { display: none; }

/* Вместе с подписью с широкого экрана уходит и сам чекбокс. visually-hidden
   прячет его от глаз, но не от Tab: без этого правила обход шёл с логотипа
   на невидимый переключатель — кольцо фокуса рисовалось на display:none
   подписи, а пробел щёлкал состоянием, которое выше 620px ни на что
   не влияет. */
.nav-toggle { display: none; }

/* Узкий экран: строка ссылок сворачивается в кнопку у правого края шапки.
   Раскрытое меню висит поверх страницы той же панелью, что и подменю
   калькуляторов, — фон --bg, хайрлайн --faint-line, срезанный угол. Само
   подменю внутри бургера перестаёт быть выпадашкой: наведения на телефоне
   нет, поэтому калькуляторы просто стоят вложенным списком и видны сразу.
   Свой медиа-блок, а не общий выше: правила равны базовым по
   специфичности и обязаны стоять после них. */
@media (max-width: 620px) {
  .masthead nav { position: relative; margin-left: auto; }
  /* Здесь переключатель снова участвует в обходе: он и есть кнопка меню
     для клавиатуры. Видимым его это не делает — visually-hidden оставляет
     от него точку в один пиксель. */
  .nav-toggle { display: inline-block; }
  .burger { display: inline-flex; align-items: center; cursor: pointer; color: var(--line); }
  .burger:hover { color: var(--action); }
  .burger-close { display: none; }
  .nav-toggle:checked ~ .burger { color: var(--action); }
  .nav-toggle:checked ~ .burger .burger-open { display: none; }
  .nav-toggle:checked ~ .burger .burger-close { display: block; }
  /* Обводка фокуса переносится со скрытого чекбокса на его кнопку, иначе
     клавиатура ведёт к невидимой цели. */
  .nav-toggle:focus-visible ~ .burger { outline: 2px solid var(--stat-now); outline-offset: 2px; }

  .masthead nav .nav-links { display: none; }
  .nav-toggle:checked ~ .nav-links {
    display: grid; gap: 10px;
    position: absolute; right: 0; top: 100%; z-index: 10;
    padding: 12px 16px;
    background: var(--bg); border: 1px solid var(--faint-line);
    clip-path: polygon(0 0, calc(100% - var(--notch)) 0, 100% var(--notch), 100% 100%, var(--notch) 100%, 0 calc(100% - var(--notch)));
  }
  .masthead nav .menu {
    position: static; display: grid; gap: 8px;
    padding: 8px 0 0 14px;
    background: none; border: 0; clip-path: none;
  }
  /* Раскрывать нечего — знак раскрытия убран. */
  .masthead nav .has-menu > a::before { content: none; }
}

/* GEAR свёрнут, пока пуст: семь степперов нужны редко, а места занимают
   больше, чем все прочие секции панели вместе. Заголовок — тот же h3,
   что у остальных секций, поэтому и маркер тот же (ниже). */
/* Все три подписи блока — один класс и один тон: они делают одно и то же,
   стоят над своим контролом и прижаты к нему, сверху зазор между группами.
   Первую держит на месте отступ заголовка, свой верхний ей не нужен. */
.gear-details .hint { margin: 10px 0 4px; }
.gear-details > .hint:first-of-type { margin-top: 0; }

/* UI-константа: высота всего, что стоит над колонками, — поля страницы,
   masthead и h1. Не из дампа. Нужна только на узком экране, где панели
   идут друг под другом и таблице не от чего взять высоту.
   Поля не переписаны числом, а взяты тем же токеном, которым живёт body:
   ниже 620px они падают с 24px до 12px, и записанная сюда сумма от них
   отставала. 119px — всё остальное: masthead с его отступом, h1 и отступ
   ряда колонок. */
:root { --page-head: calc(var(--body-pad) + 119px); }
/* Высоту ряда задают фильтры, а таблица под неё подстраивается: список
   ниже фильтров выглядел обрезанным. Фильтры при этом не скроллятся
   внутри себя нигде — они целиком видны, какой бы длины ни были.
   contain: size вычёркивает список из подсчёта высоты ряда: без него
   ряд считался бы по 194 строкам таблицы, а не по панели слева. */
.crew-columns { align-items: stretch; }
.crew-columns > .crew-list { contain: size; }
/* min-height:0 — иначе флекс-элемент не даёт себя сжать ниже содержимого,
   и обёртка таблицы вырастает за пределы панели вместо скролла. */
.crew-columns > .crew-list { display: flex; flex-direction: column; min-height: 0; }
/* Колонка грида не должна раздвигаться таблицей: без min-width:0 её
   минимум равен ширине таблицы, и горизонтально скроллится страница,
   а не обёртка, — липкие имя с портретом тогда не работают. */
.columns > .crew-list { min-width: 0; }
.crew-scroll { flex: 1; min-height: 0; overflow: auto; }
/* Одна колонка: панели идут друг под другом, общего ряда у них больше нет,
   и брать высоту списку неоткуда — с contain: size он схлопнулся бы в ноль.
   Таблица держит себя в пределах экрана сама, ради липкой шапки. Блок стоит
   здесь, а не среди прочих правил узкого экрана: те выше по файлу
   и проиграли бы по порядку. */
@media (max-width: 860px) {
  .crew-columns > .crew-list { contain: none; }
  .crew-scroll { flex: none; max-height: calc(100vh - var(--page-head)); }
}
/* Липнет шапка целиком, а не каждая ячейка сама по себе. По ячейкам
   нижнему ярусу приходилось задавать отступ числом — высотой верхнего, —
   а она зависит от шрифта и равна 21.5px, не 22. В эти полпикселя и
   просвечивали уезжающие строки. */
.crew-table thead { position: sticky; top: 0; z-index: 3; }
.crew-table thead th {
  background: var(--bg);
  /* border-collapse съедает границу липкой ячейки, тень — нет. */
  box-shadow: inset 0 -1px 0 var(--faint-line);
}
/* Верхняя кромка области прокрутки приходится на дробную позицию: всё,
   что стоит над ней на странице, складывается в 160.09px. Safari режет
   содержимое по целому физическому пикселю, а фон липкой шапки рисует
   с дробной, — и в оставшуюся половину пикселя просвечивает строка,
   уехавшая под шапку. Внешняя тень цветом фона продолжает шапку вверх
   за её собственную границу и эту щель закрывает; за пределами обёртки
   она обрезана и потому невидима. Только первому ярусу: у второго она
   легла бы поверх имён блоков. */
.crew-table thead tr:first-child th {
  box-shadow: inset 0 -1px 0 var(--faint-line), 0 -8px 0 var(--bg);
}
.crew-table thead tr { border-bottom: 0; }
/* Липким ячейкам нужен непрозрачный фон — панели прозрачные, и без
   заливки под ними просвечивают уезжающие колонки. */
.crew-table .col-portrait, .crew-table .col-name { position: sticky; z-index: 1; background: var(--bg); }
.crew-table .col-portrait { left: 0; }
.crew-table .col-name { left: 38px; }
/* Шапка закреплённых колонок едет вместе с ними: иначе над именем крю
   оказывается заголовок той колонки, которая как раз уехала. */
.crew-table thead .col-portrait, .crew-table thead .col-name { z-index: 4; }

/* Двухъярусная шапка: имена блоков приглушённым капсом, у первой колонки
   блока — хайрлайн слева. Шапка не подкрашивается: цвет у данных. */
.crew-table .block-head { color: var(--faint); font-size: 10px; letter-spacing: .16em; text-align: left; padding-bottom: 2px; }

/* Заголовок целиком — цель клика, а не подчёркнутое слово внутри неё:
   в колонке шириной 31px промахнуться мимо трёх букв слишком легко. */
.crew-table thead th a {
  display: block; color: var(--muted); text-decoration: none;
  padding: 5px 0 5px 8px; margin: -5px 0 -5px -8px;
}
.crew-table thead th.metric a { padding: 5px 0 5px 0; margin: -5px 0; }
.crew-table thead th a:hover { color: var(--line); }
.crew-table thead th[aria-sort] a, .crew-table thead th.is-active a { color: var(--action); }
.caret { font-size: 8px; font-style: normal; }
.crew-table thead th:not(.metric) .caret { margin-left: 4px; }
.crew-table thead th.metric .caret { display: block; line-height: 1; margin-top: -2px; }

/* Иконка стата вместо подписи — в шапке списка и в колонке STAT карточки.
   Исходники неквадратные (от 63×87 до 96×79), поэтому не растягиваются,
   а вписываются в кегль строки; пиксель-арт масштабируется без сглаживания.
   Активную колонку по-прежнему называет каретка: иконку цветом не назовёшь. */
.stat-icon { display: inline-block; width: 16px; height: 16px; object-fit: contain; image-rendering: pixelated; vertical-align: middle; }
/* Активную колонку текстовые заголовки называют янтарём, иконка цветом
   не назовётся — вместо этого приглушённая иконка проясняется, как текст
   светлеет при наведении. */
.crew-table thead th .stat-icon { opacity: .72; }
.crew-table thead th.is-active .stat-icon, .crew-table thead th a:hover .stat-icon { opacity: 1; }

/* В карточке иконка стоит рядом с именем стата: колонка не зажата, как
   в списке, и имя читается быстрее иконки. В списке имени нет — четырнадцать
   колонок по 31px с подписью и иконкой уводят Σ PTS за край экрана. */
.card-table .stat-icon { margin-right: 6px; }

/* Наведение отвечает на движение мыши и помогает вести взгляд вдоль
   строки в двадцать одну ячейку. Колонка, по которой список
   отсортирован, ничем не заливается: заливка на всю высоту таблицы
   спорит с самими числами, а сказать, что и куда отсортировано,
   довольно янтарной подписи и стрелки в шапке. */
.crew-table tbody tr:hover td { background: color-mix(in srgb, var(--line) 5%, transparent); }
/* Липким ячейкам подсветка строки нужна непрозрачная: полупрозрачная
   заменяет собой их заливку, и сквозь имя с портретом просвечивают
   колонки, уехавшие под них при горизонтальной прокрутке. Оттенок тот же —
   он и так ложится на var(--bg), только здесь смешан заранее. */
.crew-table tbody tr:hover .col-portrait,
.crew-table tbody tr:hover .col-name { background: color-mix(in srgb, var(--line) 5%, var(--bg)); }

.crew-table .col-name { padding-right: 14px; max-width: 170px; overflow: hidden; text-overflow: ellipsis; }

/* Имена способностей и коллекций бывают длиннее любой разумной колонки.
   Обрезаются многоточием, полное имя — в title. */
.crew-table .col-text { max-width: 105px; overflow: hidden; text-overflow: ellipsis; }
/* Только в теле: тот же селектор без tbody перебивал цвет заголовка —
   он специфичнее правила шапки, — и ABILITY с COLL светились ярче соседей. */
.crew-table tbody .col-text a { color: var(--line); }
.crew-table tbody .col-text a:hover { color: var(--action); }
.crew-table .none { color: var(--faint); }

/* Слоты в строке — те же четыре состояния, что в полосе карточки,
   но без рамок: 194 строки по три рамки превращают таблицу в сетку. */
.crew-table .col-equip { letter-spacing: 0.04em; }
.crew-table .slot { margin-right: 4px; font-size: 10px; }
.crew-table .slot.dead { color: var(--faint); }

/* Граница блока: первая колонка каждого блока отбита хайрлайном.
   Это не украшение: блоки — то же деление, что в окне персонажа,
   и смешение CORE со SKILL читалось бы как один длинный ряд. */
.crew-table th.col-first, .crew-table td.col-first { border-left: 1px solid var(--faint-line); padding-left: 8px; }

/* Шкала — доля от лучшего значения в игре по этой колонке. Цвет один
   на всю таблицу: у pixyship полоски разноцветные, но там цвет кодирует
   величину, а величина уже написана числом над полоской. */
/* Рисуется только заполненная часть: пустая рельса под каждым числом —
   это четыре тысячи одинаковых серых полосок, которые сами по себе
   не значат ничего. Короткая полоска и так читается как «мало». */
.crew-table .bar { display: block; height: 2px; margin-top: 2px; }
.crew-table .bar b { display: block; height: 100%; background: var(--stat-now); }
.crew-table.with-bars td.metric { vertical-align: top; padding-bottom: 6px; }

/* Секции панели: те же h3, что на /training, маркер <details> скрыт
   на широком экране и виден на узком, где секции сворачивает crew.js.
   GEAR — такая же секция, отличается только тем, что свёрнут и на широком. */
.setup-section > summary, .gear-details > summary { list-style: none; cursor: pointer; }
.setup-section > summary::-webkit-details-marker,
.gear-details > summary::-webkit-details-marker { display: none; }
.setup-section > summary h3, .gear-details > summary h3 { display: block; margin-top: 20px; }
.setup-section > summary h3::before, .gear-details > summary h3::before { content: "▸ "; color: var(--faint); }
.setup-section[open] > summary h3::before, .gear-details[open] > summary h3::before { content: "▾ "; }
.setup-section[aria-disabled="true"] { opacity: .45; }
/* Причина, по которой две секции выше погашены и их контролы выключены.
   Подсказка принадлежит им, а не следующему заголовку, поэтому прижата
   к ним сверху; снизу шаг задаёт margin-top самого заголовка. Отступ тот
   же, что у подсказок GEAR. */
.setup .focus-hint { margin: 6px 0 0; }
.setup-section .check { display: flex; align-items: center; gap: 6px; margin-bottom: 4px; }
/* Отметка, живущая вне секций: ритм панели задают заголовки секций своим
   margin-top, и одиночный переключатель между ними встаёт в тот же шаг. */
.setup > .check { display: flex; align-items: center; gap: 6px; margin-top: 20px; }

.visually-hidden { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }

/* Калькуляторы. Та же двухколонная раскладка, что у планировщика: форма
   слева, результат справа. Ни одного нового цвета и ни одного нового
   контрола — степперы, сегментированный переключатель и таблица взяты
   у /training. */
.calc-page .setup > :first-child { margin-top: 0; }
.calc-page .setup .stat-row { grid-template-columns: 46px 24px 60px 24px; }
.calc-page .setup .calculate { margin-top: 20px; }
.strategy-track.pair { grid-template-columns: repeat(2, minmax(0, 1fr)); }

/* Результат называет условие, при котором получен: картинка исследования из
   игры (ассет 19384, один на все семь ступеней) плюс ступень и скидка. Стоит
   во фрагменте, а не в форме, — иначе устаревала бы под самим списком.
   Хайрлайн без заливки: панели сайта тоже прозрачные, фон сделал бы карточку. */
.calc-applied { display: grid; grid-template-columns: 64px minmax(0, 1fr); align-items: center;
                gap: 12px; margin: 0 0 16px; padding: 8px; border: 1px solid var(--faint-line); }
.calc-applied img { display: block; width: 64px; height: 52px; object-fit: contain; image-rendering: pixelated; }
.calc-applied strong { display: block; font-weight: 700; }
.calc-applied p { margin: 5px 0 0; color: var(--muted); font-size: 11px; }
.calc-applied b { color: var(--action); font-size: 15px; font-weight: 700; }

/* Уровни разложены по колонкам сверху вниз. Каждая тройка «уровень / газ /
   XP» — своя группа: номер уровня прижат влево, числа вправо, а группы
   разделены вертикальным волоском, чтобы взгляд не съезжал в соседнюю. */
.calc-table { color: var(--line); font-size: 12px; line-height: 1.4; }
.calc-table thead th { color: var(--muted); font-size: 11px; }
.calc-table th, .calc-table td { text-align: right; white-space: nowrap; }
.calc-table th:nth-child(3n+1), .calc-table td:nth-child(3n+1) { text-align: left; color: var(--muted); }
.calc-table th:nth-child(3n+1):not(:first-child), .calc-table td:nth-child(3n+1):not(:first-child) {
  border-left: 1px solid var(--faint-line); padding-left: 18px;
}
.calc-table th:last-child, .calc-table td:last-child { padding-right: 0; }
/* Итог — единственное амберное пятно результата, и стоит там же, где итоги
   всех таблиц сайта: в подвале. */
.calc-table tfoot th { color: var(--action); }
.calc-table tfoot th:nth-child(3n+1) { color: var(--muted); }

/* Темп — второй вопрос к тому же переходу: сколько он займёт при известной
   добыче или сколько добывать, чтобы уложиться в срок. Ответ живёт под
   таблицей, а не отдельной панелью: считается он от её же итога. */
/* Легенда несёт корзину: та же кнопка и тот же жест, что чистят PRIORITY
   на /training, — прижата вправо, чтобы не спорить с названием блока. */
.pace-switch { margin-bottom: 0; }
.pace-switch legend { display: flex; align-items: center; width: 100%; }
.pace-switch legend .priority-clear { margin-left: auto; }
.pace-group { display: grid; gap: 4px; margin-top: 10px; }
.pace-row { display: grid; grid-template-columns: 84px 92px; gap: 6px; align-items: center; }
.pace-row input[type=number] { box-sizing: border-box; width: 100%; text-align: right; }
/* Неактивный режим убран целиком: два набора полей сразу читались бы как
   один вопрос с четырьмя ответами. Там, где :has не поддержан, видны оба —
   сервер всё равно считает по выбранной радиокнопке. */
.setup:has(input[name="pace"][value="deadline"]:checked) .pace-group[data-mode="income"],
.setup:has(input[name="pace"][value="income"]:checked) .pace-group[data-mode="deadline"] { display: none; }

.calc-pace { margin-top: 16px; padding-top: 14px; border-top: 1px solid var(--faint-line); }
.calc-pace h3 { margin: 0 0 8px; color: var(--muted); font-size: 11px;
                letter-spacing: 0.16em; font-weight: 700; }
/* Таблица жмётся к содержимому, а не растягивается на панель: строка
   «столько всего / на столько в день / столько дней» должна читаться
   слева направо одним движением, без пустых пролётов между числами. */
.pace-table { width: auto; border-collapse: collapse; font-size: 12px; line-height: 1.4; }
.pace-table th { text-align: left; font-weight: 400; }
.pace-table td { text-align: right; white-space: nowrap; padding-left: 14px; }
.pace-table tbody th, .pace-src, .pace-unit { color: var(--muted); }
.pace-table .pace-unit { text-align: left; padding-left: 6px; }
.pace-table tfoot th, .pace-table tfoot td { padding-top: 6px; }
/* Ответ — единственное янтарное пятно блока: при известной добыче это
   итоговый срок, при заданном сроке — обе нормы добычи. */
.pace-table tfoot .pace-value, .needed .pace-value { color: var(--action); font-weight: 700; }
.pace-note { margin: 8px 0 0; color: var(--muted); font-size: 11px; }
