/* Оформление взято из Moto Route Generator — те же токены, те же панели и
   кнопки. Разница только в раскладке: там окно на компьютере, здесь телефон в
   тире, поэтому навигация внизу под большой палец. */
:root {
  color-scheme: dark;
  font-family: Inter, "Segoe UI", Arial, sans-serif;
  --bg: #0e1014;
  --panel: #171a20;
  --panel-2: #1e222a;
  --line: #2a303a;
  --text: #f6f7f9;
  --muted: #99a2b1;
  --accent: #ff5a36;
  --accent-2: #ff855f;
  /* Тёмный конец акцента для градиентов и цвет надписи на самом акценте. Оба
     задаются из журнала под выбранный цвет; здесь значения на первую отрисовку,
     до того как настройки прочитаны. */
  --accent-deep: #d1441f;
  --accent-ink: #ffffff;
  --green: #32d583;
  --red: #f04438;
  --tabbar-height: 62px;
}

/* Светлая тема. Переопределяются токены и та горсть цветов, что задана
   впрямую и занимает много места на экране: кнопки, границы, схема площадки.
   Всё остальное — акценты и предупреждения — одинаково читается на любом фоне,
   поэтому не трогается вовсе. */
:root[data-theme="light"] {
  color-scheme: light;
  --bg: #f2f4f7;
  --panel: #ffffff;
  --panel-2: #eceff4;
  --line: #d7dce4;
  --text: #14171c;
  --muted: #5c6675;
}

:root[data-theme="light"] .button {
  background: #e4e8ef;
}

:root[data-theme="light"] .button.ghost {
  color: #3a4250;
  border-color: #ccd3dd;
}

:root[data-theme="light"] .list-item:hover {
  border-color: #c3ccd8;
}

:root[data-theme="light"] .badge {
  color: var(--muted);
  background: rgba(0, 0, 0, 0.06);
}

:root[data-theme="light"] .tabbar {
  background: rgba(242, 244, 247, 0.96);
}

:root[data-theme="light"] .lock-key {
  background: #e4e8ef;
}

:root[data-theme="light"] .lock-screen {
  background: rgba(242, 244, 247, 0.94);
}

:root[data-theme="light"] .sheet-backdrop {
  background: rgba(20, 23, 28, 0.32);
}

:root[data-theme="light"] .stage-ground,
:root[data-theme="light"] .face3d.ground {
  fill: #e9edf3;
  stroke: #cfd6e0;
}

:root[data-theme="light"] .stage-grid {
  stroke: rgba(20, 23, 28, 0.08);
}

:root[data-theme="light"] .stage-grid.major {
  stroke: rgba(20, 23, 28, 0.18);
}

:root[data-theme="light"] .face3d.gridline {
  fill: rgba(20, 23, 28, 0.08);
}

:root[data-theme="light"] .face3d.gridline.major {
  /* Каждые десять метров — белая полоса поверх светлого пола, с тонкой
     обводкой: иначе на белом её не видно вовсе. По ней и отмеряют дистанцию. */
  fill: #ffffff;
  stroke: rgba(20, 23, 28, 0.35);
  stroke-width: 0.4;
}

:root[data-theme="light"] .stage-axis {
  fill: #66707f;
}

* {
  box-sizing: border-box;
}

/* Атрибут hidden обязан скрывать: любой класс с display в этом файле побеждает
   правило браузера [hidden] { display: none }. Один раз для всего приложения. */
[hidden] {
  display: none !important;
}

html,
body {
  width: 100%;
  min-height: 100%;
  margin: 0;
  background: var(--bg);
  color: var(--text);
  -webkit-text-size-adjust: 100%;
}

/* Размер интерфейса целиком, а не только шрифта: в приложении всё задано в
   точках, и увеличение одного шрифта разъехало бы кнопки и счётчики зон.
   zoom делает то же, что колесо с Ctrl в браузере, но только для приложения. */
body {
  zoom: var(--ui-scale, 1);
}

body {
  overscroll-behavior-y: contain;
}

button,
select,
input,
textarea {
  font: inherit;
  color: inherit;
}

button:focus-visible,
select:focus-visible,
input:focus-visible,
textarea:focus-visible {
  outline: 2px solid var(--accent-2);
  outline-offset: 2px;
}

/* Высота экрана делится на масштаб — и это не красивость, а условие того, что
 * нижняя панель стоит внизу. Размер интерфейса задан через zoom на body: внутри
 * него 100dvh — это по-прежнему высота окна в точках, но нарисована она будет
 * уменьшенной. При 80% оболочка выходила ростом в 0,8 экрана, и панель, стоящая
 * последней, оказывалась на пятой части экрана выше низа — ровно то, что видно
 * на телефоне. Делим на масштаб — и оболочка снова ровно в экран.
 */
.app-shell {
  display: flex;
  flex-direction: column;
  min-height: calc(100vh / var(--ui-scale, 1));
  min-height: calc(100dvh / var(--ui-scale, 1));
}

/* ── Верхняя полоса ─────────────────────────────────────────────── */

.topbar {
  position: sticky;
  z-index: 6;
  top: 0;
  order: 1;
  display: flex;
  align-items: center;
  gap: 12px;
  padding: calc(env(safe-area-inset-top, 0px) + 12px) 16px 12px;
  background:
    radial-gradient(circle at 8% -30%, rgba(255, 90, 54, 0.16), transparent 34%),
    var(--bg);
  border-bottom: 1px solid var(--line);
}

.brand-icon {
  display: grid;
  width: 42px;
  height: 42px;
  flex: 0 0 auto;
  place-items: center;
  border-radius: 13px;
  color: var(--accent);
  background: rgba(255, 90, 54, 0.12);
  border: 1px solid rgba(255, 90, 54, 0.22);
}

/* Значок журнала — он же кнопка помощи. Внешне он остался значком: место,
   куда тычут, когда не понимают, что делать, должно выглядеть привычно. */
.brand-icon {
  padding: 0;
  cursor: pointer;
}

.brand-icon:active {
  transform: scale(0.96);
}

/* Первая строка главы помощи — на виду, остальное по нажатию. */
.fold .help-lead {
  margin: 0 0 4px;
  padding: 0 2px 8px;
}

/* Эмблема клуба в списках: показываем как она встанет в шапку листа —
   полоской, а не квадратом во весь блок. */
.club-logo {
  display: block;
  max-width: 100%;
  max-height: 64px;
  margin: 0 0 8px;
  border-radius: 8px;
  background: #fff;
}

/* Эмблем у клуба несколько, и каждая живёт своей карточкой: без рамки картинки,
   бегунки и кнопки сливаются в одну кучу, и непонятно, что к чему относится. */
.logo-row {
  margin: 0 0 10px;
  padding: 10px;
  border: 1px solid var(--line);
  border-radius: 12px;
  background: var(--panel-2);
}

.logo-row .field-label {
  margin: 0 0 6px;
}

.brand-icon svg {
  width: 26px;
  height: 26px;
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
}

.brand-text {
  min-width: 0;
  flex: 1 1 auto;
}

.brand-text h1 {
  margin: 0;
  overflow: hidden;
  font-size: 17px;
  letter-spacing: 0.01em;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.brand-text p {
  margin: 2px 0 0;
  overflow: hidden;
  color: var(--muted);
  font-size: 11.5px;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Размер букв рядом с языком: это то же «как мне удобнее смотреть». Вид как у
   переключателя языка, чтобы шапка не рассыпалась на разные кнопки. */
.text-size {
  display: flex;
  align-items: center;
  flex: 0 0 auto;
  gap: 2px;
  padding: 3px;
  border-radius: 10px;
  background: var(--panel-2);
}

.text-size button {
  min-width: 30px;
  padding: 7px 6px;
  border: 0;
  border-radius: 7px;
  color: var(--muted);
  background: transparent;
  font-family: inherit;
  font-size: 11.5px;
  font-weight: 750;
  cursor: pointer;
}

.text-size button:hover:not(:disabled) {
  color: var(--ink);
  background: var(--panel);
}

.text-size button:disabled {
  opacity: 0.35;
  cursor: default;
}

.text-size .text-size-value {
  min-width: 40px;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
}

/* На узком экране в шапке тесно. Ужимаем всё, что можно ужать, а не выкидываем
   кнопки: увеличить буквы нужнее всего именно на телефоне. Число уходит
   (вернуть 100% можно бегунком в «Ещё»), подпись под названием тоже — без неё
   название журнала остаётся на экране, а с ней его вытесняло в ноль. */
@media (max-width: 430px) {
  .text-size .text-size-value {
    display: none;
  }

  .text-size button {
    min-width: 26px;
    padding: 6px 4px;
  }

  .language-switch select {
    min-width: 46px;
    padding: 6px 4px;
  }

  .brand-text p {
    display: none;
  }

  .topbar {
    gap: 8px;
    padding-right: 12px;
    padding-left: 12px;
  }
}

.language-switch {
  display: flex;
  flex: 0 0 auto;
  padding: 3px;
  border-radius: 10px;
  background: var(--panel-2);
}

.language-switch select {
  min-width: 58px;
  padding: 7px 6px;
  border: 0;
  border-radius: 7px;
  color: var(--muted);
  background: transparent;
  font-size: 11.5px;
  font-weight: 750;
  cursor: pointer;
}

/* ── Кто вошёл ───────────────────────────────────────────────────── */

/* Чип с человеком, который сейчас в приложении. Нужен затем, чтобы тренер
   никогда не записывал результаты в чужой журнал, думая, что он в своём. */
.account-chip {
  display: flex;
  align-items: center;
  gap: 9px;
  min-height: 42px;
  padding: 5px 9px;
  border: 1px solid var(--line);
  border-radius: 12px;
  background: var(--panel-2);
  cursor: pointer;
  text-align: start;
}

.account-chip:hover {
  border-color: var(--accent-2);
}

.account-chip-face {
  display: grid;
  overflow: hidden;
  width: 28px;
  height: 28px;
  flex: 0 0 auto;
  place-items: center;
  border-radius: 50%;
  color: var(--accent-ink);
  background: linear-gradient(135deg, var(--accent), var(--accent-deep));
  font-size: 11px;
  font-weight: 800;
}

.account-chip-face img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.account-chip-text {
  display: flex;
  min-width: 0;
  flex-direction: column;
}

.account-chip-name {
  overflow: hidden;
  font-size: 12px;
  font-weight: 700;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.account-chip-role {
  overflow: hidden;
  color: var(--muted);
  font-size: 10.5px;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* На телефоне чип живёт в верхней полосе, на компьютере — внизу боковой,
   там, где его и ищут глазами. */
.topbar-account {
  display: flex;
  flex: 0 0 auto;
}

.topbar-account .account-chip-text {
  display: none;
}

.tabbar-account {
  display: none;
}

/* Поле со ссылкой: её всегда можно взять руками, если «Поделиться» и буфер
   не сработали — на телефоне это бывает. */
.link-field {
  width: 100%;
  min-height: 44px;
  padding: 10px 12px;
  border: 1px solid var(--line);
  border-radius: 12px;
  background: var(--panel-2);
  font-size: 12px;
}

/* Размер интерфейса: бегунок от половины до двойного и число рядом. Ползунок
   крупный — его тянут пальцем, часто в перчатке, а не мышью. Число само
   кнопка: нажатие возвращает 100%. */
.range-row {
  display: flex;
  align-items: center;
  gap: 12px;
}

.range {
  flex: 1;
  min-width: 0;
  height: 28px;
  margin: 0;
  padding: 0;
  background: transparent;
  -webkit-appearance: none;
  appearance: none;
  touch-action: pan-y;
}

.range::-webkit-slider-runnable-track {
  height: 6px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--panel-2);
}

.range::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 26px;
  height: 26px;
  margin-top: -11px;
  border: none;
  border-radius: 50%;
  background: var(--accent);
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.45);
  cursor: pointer;
}

.range::-moz-range-track {
  height: 6px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--panel-2);
}

.range::-moz-range-thumb {
  width: 26px;
  height: 26px;
  border: none;
  border-radius: 50%;
  background: var(--accent);
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.45);
  cursor: pointer;
}

.range-value {
  min-width: 62px;
  padding: 8px 10px;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: var(--panel-2);
  color: var(--text);
  font-size: 13px;
  font-weight: 600;
  text-align: center;
  cursor: pointer;
}

/* Шаги установки приходят одной строкой с переводами строк: нумерация меняется
   от устройства к устройству, и собирать её из отдельных ключей значило бы
   держать в семи языках по четыре обрывка вместо одной понятной фразы. */
.install-steps {
  margin: 0 0 12px;
  white-space: pre-line;
  color: var(--text);
  font-size: 13px;
  line-height: 1.7;
}

/* Цвет акцента: пять точек, выбранная обведена. */
/* Девять готовых цветов плюс своя палитра: в один ряд они на телефоне не
   встают, поэтому ряд переносится. */
.accent-row {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
}

.accent-dot {
  width: 32px;
  height: 32px;
  padding: 0;
  border: 2px solid transparent;
  border-radius: 50%;
  cursor: pointer;
  box-shadow: 0 0 0 1px var(--line) inset;
}

/* Свой цвет — обычное поле выбора цвета, но кружком, как остальные: палитру
   рисует само устройство, и это единственный способ дать её и на телефоне.
   Селектор длиннее соседей нарочно: правило `.field input` в этом же файле
   задаёт полям ширину во всю строку, и кружок расплывался в полосу через весь
   экран. Обводку выбранного приходится повторить здесь же — иначе прозрачная
   рамка отсюда перебивает `.accent-dot.active`. */
.accent-row input.accent-custom {
  width: 32px;
  height: 32px;
  min-height: 0;
  flex: 0 0 auto;
  padding: 0;
  border: 2px solid transparent;
  border-radius: 50%;
  overflow: hidden;
  background: transparent;
  -webkit-appearance: none;
  appearance: none;
}

.accent-row input.accent-custom.active {
  border-color: var(--text);
}

.accent-custom::-webkit-color-swatch-wrapper {
  padding: 0;
}

.accent-custom::-webkit-color-swatch {
  border: none;
  border-radius: 50%;
}

.accent-custom::-moz-color-swatch {
  border: none;
  border-radius: 50%;
}

.accent-dot.active {
  border-color: var(--text);
}

/* ── Экран ──────────────────────────────────────────────────────── */

.screen-host {
  display: flex;
  width: 100%;
  max-width: 760px;
  flex: 1 1 auto;
  order: 2;
  flex-direction: column;
  gap: 14px;
  padding: 14px 14px calc(var(--tabbar-height) + env(safe-area-inset-bottom, 0px) + 18px);
  margin: 0 auto;
}

.panel {
  padding: 16px;
  border: 1px solid var(--line);
  border-radius: 18px;
  background: linear-gradient(150deg, rgba(255, 255, 255, 0.025), transparent 55%), var(--panel);
  box-shadow: 0 14px 34px rgba(0, 0, 0, 0.16);
}

.panel h2 {
  margin: 5px 0 7px;
  font-size: 17px;
}

.panel p.panel-note {
  margin: 0 0 12px;
  color: var(--muted);
  font-size: 12.5px;
  line-height: 1.5;
}

/* Отказ базы или неверный пароль — это не подсказка, и выглядеть как
   подсказка не должно. */
.panel p.panel-note.error {
  color: var(--red);
}

.section-kicker {
  color: var(--accent-2);
  font-size: 10px;
  font-weight: 800;
  letter-spacing: 0.13em;
  text-transform: uppercase;
}

.panel-head {
  display: flex;
  align-items: flex-start;
  gap: 10px;
}

.panel-head > div {
  min-width: 0;
  flex: 1 1 auto;
}

/* Кнопка шапки не ужимается под заголовок: подпись у неё на восьми языках
   разной длины, и сжатая она переносилась бы по буквам. Заголовку места
   хватает — он переносится по словам, кнопка нет. */
.panel-head > .button {
  flex: 0 0 auto;
  white-space: nowrap;
}

/* Два управления справа: своё действие панели и кнопка складки. Ряд не
   ужимается по той же причине, что и одиночная кнопка, и переносится целиком,
   а не по буквам внутри подписей. */
.panel-head-row {
  display: flex;
  flex: 0 0 auto;
  flex-wrap: wrap;
  gap: 8px;
  justify-content: flex-end;
}

/* ── Свёрнутая панель ───────────────────────────────────────────────
   Внутри неё нет ничего, кроме шапки, — ни пояснения, ни полей. Поэтому и
   отступы меньше: панель в полный рост с одним заголовком внутри читается как
   пустая, а не как свёрнутая. Двадцать таких подряд — это список названий, и
   выглядеть он должен списком. */
.panel.panel-folded {
  padding-top: 14px;
  padding-bottom: 14px;
}

.panel.panel-folded .panel-head {
  align-items: center;
}

/* Заголовок свёрнутой панели — та же строка, что и раскрытой, но без отступа
   снизу: под ним ничего нет. */
.panel.panel-folded h2 {
  margin-bottom: 0;
}

/* ── Кнопки ─────────────────────────────────────────────────────── */

.button {
  min-height: 44px;
  padding: 11px 14px;
  border: 0;
  border-radius: 12px;
  color: var(--text);
  background: #292f39;
  font-size: 13px;
  font-weight: 750;
  cursor: pointer;
  transition: transform 140ms ease, filter 140ms ease, background 140ms ease;
}

.button:hover:not(:disabled) {
  filter: brightness(1.09);
  transform: translateY(-1px);
}

.button:active:not(:disabled) {
  transform: translateY(0);
}

.button:disabled {
  cursor: not-allowed;
  opacity: 0.38;
}

.button.primary {
  color: var(--accent-ink);
  background: linear-gradient(135deg, var(--accent), var(--accent-deep));
}

.button.ghost {
  color: #c2c8d2;
  background: transparent;
  border: 1px solid #343b47;
}

.button.danger {
  color: #ffd9d5;
  background: transparent;
  border: 1px solid rgba(240, 68, 56, 0.5);
}

.button.full {
  width: 100%;
}

/* Кнопка-ссылка: «открыть бота» — это переход, а не действие в журнале, и
   делать его надо настоящей ссылкой. Без этих трёх строк она выглядела бы
   подчёркнутой строкой текста, а не кнопкой. */
a.button {
  display: block;
  text-align: center;
  text-decoration: none;
}

.button.small {
  min-height: 36px;
  padding: 8px 11px;
  border-radius: 10px;
  font-size: 12px;
}

.button-row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 9px;
  margin-top: 12px;
}

.button-row.single {
  grid-template-columns: 1fr;
}

/* Стрелки порядка: две маленькие кнопки в строке списка. */
.order-buttons {
  display: flex;
  gap: 6px;
}

.button.tiny {
  min-height: 34px;
  min-width: 38px;
  padding: 4px 8px;
  font-size: 15px;
}

/* ── Поля ───────────────────────────────────────────────────────── */

/* Поля входа стоят в настоящей форме: без неё связка ключей телефона не
   предлагает сохранить пароль. Своих отступов у формы быть не должно. */
/* Подвал закрытого экрана: номер сборки и аварийное обновление. Мелко и тихо —
   это не то, чем пользуются каждый день, но добраться до него надо всегда. */
.gate-foot {
  margin-top: 18px;
  padding-top: 12px;
  border-top: 1px solid #2a303a;
  text-align: center;
}

.gate-foot .lock-note {
  margin-bottom: 8px;
  font-size: 11.5px;
  opacity: 0.75;
}

.gate-form {
  margin: 0;
}

.field {
  display: block;
  margin-top: 12px;
}

.field > span {
  display: block;
  margin-bottom: 5px;
  color: var(--muted);
  font-size: 11.5px;
  font-weight: 700;
  letter-spacing: 0.02em;
}

.field input,
.field select,
.field textarea {
  width: 100%;
  min-height: 44px;
  padding: 10px 12px;
  border: 1px solid var(--line);
  border-radius: 12px;
  background: var(--panel-2);
  font-size: 15px;
}

.field textarea {
  min-height: 76px;
  resize: vertical;
}

/* Поиск над выпадающим списком: без зазора поле ввода и список слипаются в
   одну деталь, и непонятно, где пишут, а где выбирают. */
.field .search-input {
  margin-bottom: 6px;
}

.field-hint {
  margin: 5px 0 0;
  color: var(--muted);
  font-size: 11px;
  line-height: 1.45;
}

/* Сырой текст, приехавший со сканера. Смотреть на него будут ровно затем,
 * чтобы увидеть, что доехало, — значит показывать как есть: своим начертанием,
 * без переносов по словам и без письма справа налево. Иврит в этой строке стоит
 * рядом с цифрами, и двунаправленный разбор переставил бы их местами: строка
 * читалась бы иначе, чем приехала, а проверяют здесь именно приехавшее.
 *
 * Строка длинная и без пробелов — рвём по знакам, иначе она вылезает за
 * карточку и уносит с собой весь экран. */
.scan-raw {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 11px;
  direction: ltr;
  unicode-bidi: plaintext;
  text-align: left;
  overflow-wrap: anywhere;
  white-space: pre-wrap;
  padding: 6px 8px;
  border-radius: 8px;
  background: var(--panel-2);
  border: 1px solid var(--line);
  color: var(--text);
}

.field-row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 10px;
}

/* Поле с кнопкой: поле берёт всё место, кнопка — по надписи. Поровну здесь
   плохо, потому что в поле пишут название, а на кнопке одно слово. */
.field-row.with-button {
  grid-template-columns: 1fr auto;
  align-items: start;
}

.check-field {
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: 42px;
  color: var(--text);
  font-size: 13.5px;
}

.check-field input {
  width: 20px;
  height: 20px;
  flex: 0 0 auto;
  accent-color: var(--accent);
}

.search-input {
  width: 100%;
  min-height: 44px;
  padding: 10px 12px;
  border: 1px solid var(--line);
  border-radius: 12px;
  background: var(--panel-2);
  font-size: 15px;
}

/* ── Списки ─────────────────────────────────────────────────────── */

.list {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 0;
  margin: 12px 0 0;
  list-style: none;
}

/* Строка списка с делом рядом. Кнопка «Записать подход» раньше висела над всем
   занятием, и нажать её можно было не выбрав ни упражнения, ни стрелка; теперь
   она стоит у своего упражнения и молчит, пока некого записывать. */
/* Ствол под именем участника: человек в занятии остаётся одной записью, а
   патроны у каждого ствола свои — одно поле на все врало бы. */
.participant-row.gun-row {
  padding-inline-start: 30px;
  margin-top: 4px;
}

/* Подпись ствола тянется на всю строку: тогда его число встаёт в тот же
   столбец, что число человека над ним. Без этого поле садилось сразу за
   названием ствола, и цифры шли лестницей — каждая по длине названия. */
.gun-row-name {
  flex: 1 1 auto;
  min-width: 0;
  color: var(--muted);
  font-size: 12px;
  line-height: 1.35;
}

/* Отметка «принести» у ствола в списке участников. Это просьба, а не свойство
   ствола, — поэтому она стоит рядом с названием, а не вместо него. */
.bring-mark {
  margin-inline-start: 8px;
  padding: 1px 7px;
  border-radius: 999px;
  background: rgba(255, 90, 54, 0.16);
  color: var(--accent);
  font-size: 10.5px;
  font-weight: 700;
  white-space: nowrap;
}

/* То же название, но нажимаемое: с него инструктор меняет ствол. Выглядит
   подписью, потому что подпись и есть; что её можно нажать, говорит
   подчёркивание под курсором. Свойства шрифта перечислены поимённо: короткое
   font: inherit перебило бы размер из правила выше. */
button.gun-row-name {
  padding: 0;
  border: 0;
  background: none;
  font-family: inherit;
  font-size: 12px;
  line-height: 1.35;
  text-align: start;
  cursor: pointer;
}

button.gun-row-name:hover > span,
button.gun-row-name:focus-visible > span {
  text-decoration: underline;
}

/* Между людьми — воздух: список стволов иначе слипается со следующим именем и
   читается как его стволы. */
.participant-row.gun-row + .participant-row:not(.gun-row) {
  margin-top: 10px;
}

/* День недели в расписании: сам день и его инструкторы под ним. */
/* Подпись над списком внутри блока: без неё имена с галочками читались как
   «состав дня», хотя это те, кто этот день ведёт. */
.field-label {
  margin: 8px 0 2px;
  color: var(--muted);
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

.schedule-day {
  margin-top: 10px;
  padding: 10px 12px;
  border: 1px solid #2a303a;
  border-radius: 12px;
  background: var(--panel-2);
}

.schedule-day .check-field:first-child {
  font-weight: 700;
}

/* Подпись пальцем. touch-action забирает жест холсту: без этого палец листает
   страницу вместо того, чтобы рисовать. */
.sign-wrap {
  position: relative;
  margin-top: 10px;
  border: 1px solid #2a303a;
  border-radius: 12px;
  background: #ffffff;
  overflow: hidden;
}

.sign-pad {
  display: block;
  width: 100%;
  height: 200px;
  touch-action: none;
  cursor: crosshair;
}

.sign-line {
  position: absolute;
  inset-inline: 18px;
  bottom: 42px;
  height: 1px;
  background: #c9d0da;
  pointer-events: none;
}

.sign-shot {
  display: flex;
  min-height: 64px;
  align-items: center;
  justify-content: center;
  margin-top: 8px;
  padding: 6px;
  border: 1px solid #2a303a;
  border-radius: 12px;
  background: #ffffff;
}

.sign-shot img {
  max-width: 100%;
  max-height: 90px;
}

.list-row {
  display: flex;
  align-items: stretch;
  gap: 8px;
}

.list-row > .list-item {
  width: auto;
  flex: 1 1 auto;
}

/* Ряд «поле ввода плюс кнопка»: ввод занимает всё, что осталось. Без этого
   поле сжималось до собственной ширины и рядом с кнопкой выглядело обрубком. */
.list-row > input {
  flex: 1 1 auto;
  min-width: 0;
}

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

   На узком экране кнопка уходит на свою строку целиком: там она читается, а
   зажатая в угол «Удалить» рядом с тремя полями — нет. */
.age-band-row {
  display: grid;
  grid-template-columns: 4.5rem 4.5rem minmax(0, 1fr) auto;
  gap: 8px;
  align-items: stretch;
}

.age-band-row > input,
.age-band-row > select {
  min-width: 0;
}

@media (max-width: 430px) {
  .age-band-row {
    grid-template-columns: 4.5rem 4.5rem minmax(0, 1fr);
  }

  .age-band-row > .list-row-action {
    grid-column: 1 / -1;
  }
}

.list-row-action {
  display: flex;
  flex: 0 0 auto;
  align-items: center;
  justify-content: center;
  min-width: 46px;
  padding: 0 12px;
  border: 1px solid var(--accent-deep);
  border-radius: 14px;
  background: var(--accent);
  color: var(--accent-ink);
  font-size: 13px;
  font-weight: 700;
  line-height: 1.15;
  cursor: pointer;
}

.list-row-action:disabled {
  border-color: #3a4250;
  background: var(--panel-2);
  color: var(--muted);
  cursor: default;
}

.list-item {
  display: flex;
  width: 100%;
  align-items: center;
  gap: 11px;
  padding: 11px 12px;
  border: 1px solid transparent;
  border-radius: 14px;
  background: var(--panel-2);
  text-align: start;
  cursor: pointer;
}

.list-item:hover {
  border-color: #3a4250;
}

.list-item-body {
  min-width: 0;
  flex: 1 1 auto;
}

.list-item-title {
  display: block;
  overflow: hidden;
  font-size: 14px;
  font-weight: 700;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.list-item-sub {
  display: block;
  margin-top: 3px;
  color: var(--muted);
  font-size: 11.5px;
  line-height: 1.4;
}

.list-item-value {
  flex: 0 0 auto;
  color: var(--accent-2);
  font-size: 13px;
  font-weight: 800;
  font-variant-numeric: tabular-nums;
}

.avatar {
  display: grid;
  overflow: hidden;
  width: 38px;
  height: 38px;
  flex: 0 0 auto;
  place-items: center;
  border-radius: 12px;
  color: #fff;
  background: linear-gradient(135deg, #3a4250, #2a303a);
  font-size: 14px;
  font-weight: 800;
}

/* Кружок с фотографией: картинка заполняет его целиком, инициалы остаются
   запасным вариантом. */
/* С фотографией кружок перестаёт быть сеткой и становится простым блоком.
   В сетке с place-items: center высота области равна содержимому, и height:
   100% у картинки не имел от чего считаться: высота бралась из пропорций
   снимка, и вертикальный кадр вылезал из кружка вниз, на кнопку. У блока
   высота задана, и картинка честно заполняет его. */
.avatar.has-photo {
  display: block;
  padding: 0;
  background: none;
}

.account-chip-face.has-photo {
  display: block;
}

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

.row-actions {
  display: flex;
  gap: 7px;
  margin-top: 10px;
}

.empty-state {
  padding: 18px 12px;
  border: 1px dashed #343b47;
  border-radius: 14px;
  color: var(--muted);
  font-size: 12.5px;
  line-height: 1.5;
  text-align: center;
}

.badge {
  display: inline-block;
  padding: 3px 8px;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.07);
  color: var(--muted);
  font-size: 10.5px;
  font-weight: 750;
  letter-spacing: 0.02em;
}

.badge.live {
  color: var(--accent-ink);
  background: var(--accent);
}

.badge.done {
  color: #0c1a12;
  background: var(--green);
}

/* Занятие прошло, а закрыть его забыли. Не тревога и не успех — дело, которое
   ждёт: поэтому янтарный, а не красный и не зелёный. */
.badge.stale {
  color: #23180a;
  background: #e0a33c;
}

/* Отменённое занятие: серым, а не красным. Красный на списке читается как
   «ошибка», а отменённое занятие — не ошибка, а просто не состоится. */
.badge.off {
  color: #e6e9ef;
  background: rgba(255, 255, 255, 0.14);
}

/* Кнопка WhatsApp: зелёным, как сам мессенджер. Рядом с ней стоит кнопка
   оповещения, и из двух одинаковых кнопок не видно, что это разные каналы. */
.button.wa {
  border: 1px solid rgba(37, 211, 102, 0.4);
  background: rgba(37, 211, 102, 0.16);
  color: #6ee7a0;
}

/* Оповещения: до кого связь есть, до кого нет и кому уже открыли WhatsApp. */
.badge.wa {
  color: #6ee7a0;
  background: rgba(37, 211, 102, 0.16);
}

.badge.sent {
  color: #23180a;
  background: #6ee7a0;
}

.badge.none {
  color: #ff9b93;
  background: rgba(240, 68, 56, 0.14);
}

/* Предпросмотр сообщения: пузырь, а не поле ввода. Видно ровно то, что
   уйдёт, — а поле ввода читается как «здесь ещё можно поправить». */
.wa-bubble {
  padding: 11px 13px;
  border-radius: 14px 14px 14px 4px;
  background: #075e54;
  color: #eafff4;
  font-size: 13.5px;
  line-height: 1.5;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}

/* Строка с галочкой: подпись рядом, а не над — иначе не понять, к чему
   галочка относится. */
.check-row {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 10px 0 0;
  font-size: 13px;
}

.check-row input[type="checkbox"] {
  width: 20px;
  min-height: 20px;
  height: 20px;
  flex: 0 0 auto;
  accent-color: var(--accent);
}

/* ── Плитки со статистикой ──────────────────────────────────────── */

.stat-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(88px, 1fr));
  gap: 8px;
  margin-top: 12px;
}

.stat {
  padding: 10px 11px;
  border: 1px solid var(--line);
  border-radius: 13px;
  background: rgba(255, 255, 255, 0.03);
}

.stat b {
  display: block;
  font-size: 18px;
  font-variant-numeric: tabular-nums;
}

.stat span {
  display: block;
  margin-top: 2px;
  color: var(--muted);
  font-size: 10.5px;
}

/* ── Сегменты (переключатель) ───────────────────────────────────── */

.segmented {
  display: flex;
  gap: 4px;
  padding: 4px;
  border-radius: 13px;
  background: var(--panel-2);
  overflow-x: auto;
}

/* Больше трёх кнопок в ряд не влезает, а прокручивать ряд нельзя: за краем
   прячется то, чего человек не ищет, потому что не знает о нём. */
.segmented.wrap {
  flex-wrap: wrap;
  overflow-x: visible;
}

.segmented.wrap button {
  flex: 1 0 40%;
  white-space: normal;
}

.segmented button {
  flex: 1 0 auto;
  min-height: 38px;
  padding: 8px 12px;
  border: 0;
  border-radius: 10px;
  color: var(--muted);
  background: transparent;
  font-size: 12.5px;
  font-weight: 750;
  cursor: pointer;
  white-space: nowrap;
}

.segmented button.active {
  color: var(--accent-ink);
  background: var(--accent);
}

/* ── Счётчики зон: под палец в перчатке ─────────────────────────── */

.zone-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(74px, 1fr));
  gap: 9px;
  margin-top: 12px;
}

.zone {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-start;
  gap: 6px;
  padding: 9px 6px;
  border: 1px solid var(--line);
  border-radius: 14px;
  background: rgba(255, 255, 255, 0.03);
}

.zone-label {
  color: var(--muted);
  font-size: 11px;
  font-weight: 800;
  letter-spacing: 0.04em;
  /* Подписи у клеток разной длины: «A» в один знак и «Не обстрел.» в две
     строки. Без общей высоты длинная подпись отталкивала число и кнопки вниз,
     и в ряду одна клетка стояла ниже прочих — плюс с минусом вылезали за её
     рамку. Держим место под ДВЕ строки у всех: тогда ряд ровный при любой
     подписи и на любом языке, а иврит с арабским длиннее русского. */
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 2.4em;
  line-height: 1.2;
  text-align: center;
}

.zone.miss .zone-label,
.zone.penalty .zone-label {
  color: #ffb4ab;
}

.zone-value {
  font-size: 24px;
  font-weight: 800;
  font-variant-numeric: tabular-nums;
  line-height: 1;
}

.zone-buttons {
  display: flex;
  gap: 6px;
}

.zone-buttons button {
  width: 38px;
  height: 38px;
  border: 0;
  border-radius: 11px;
  color: var(--text);
  background: #2c333e;
  font-size: 19px;
  font-weight: 700;
  line-height: 1;
  cursor: pointer;
}

.zone-buttons button:active {
  background: var(--accent);
  color: var(--accent-ink);
}

.result-strip {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
  margin-top: 14px;
  padding: 12px 14px;
  border: 1px solid rgba(255, 90, 54, 0.3);
  border-radius: 14px;
  background: rgba(255, 90, 54, 0.09);
}

.result-strip span {
  color: var(--muted);
  font-size: 11.5px;
  font-weight: 750;
  letter-spacing: 0.05em;
  text-transform: uppercase;
}

.result-strip b {
  font-size: 24px;
  font-variant-numeric: tabular-nums;
}

/* ── График ─────────────────────────────────────────────────────── */

.chart-host {
  margin-top: 12px;
}

.chart {
  display: block;
  width: 100%;
  height: auto;
}

.chart-grid {
  stroke: #2a303a;
  stroke-width: 1;
}

.chart-axis {
  fill: #7d8797;
  font-size: 8px;
}

.chart-line {
  fill: none;
  stroke: var(--accent);
  stroke-width: 2;
  stroke-linejoin: round;
}

.chart-area {
  fill: rgba(255, 90, 54, 0.14);
  stroke: none;
}

.chart-dot {
  fill: var(--accent-2);
}

.chart-dot.best {
  fill: var(--green);
}

/* Второй стрелок при сравнении: другой цвет и пунктир — чтобы линии
   различались и на чёрно-белой печати ведомости. */
.chart-line.second {
  stroke: #4ea1ff;
  stroke-dasharray: 5 3;
}

.chart-dot.second {
  fill: #4ea1ff;
}

.series-dot {
  display: inline-block;
  width: 9px;
  height: 9px;
  margin-inline-end: 6px;
  border-radius: 50%;
  background: var(--accent);
  vertical-align: baseline;
}

.series-dot.second {
  background: #4ea1ff;
}

.trend {
  margin-top: 10px;
  color: var(--muted);
  font-size: 12px;
  line-height: 1.5;
}

.trend b.up {
  color: var(--green);
}

.trend b.down {
  color: #ffb4ab;
}

/* ── Нижняя навигация ───────────────────────────────────────────── */

/* Панель липнет к низу, а не приколочена к экрану.
 *
 * Раньше здесь было position: fixed — и на iPhone панель уезжала на середину
 * экрана и там застревала до следующей прокрутки. Так ведёт себя фиксированный
 * элемент, когда всплывает и уходит клавиатура: Safari сдвигает видимую часть
 * окна, а «приколоченное к низу» остаётся привязанным к прежнему низу.
 *
 * Липкая панель этим не страдает: она стоит последней в колонке, прижата к низу
 * своим margin-top: auto, а при прокрутке остаётся у нижнего края. Порядок в
 * разметке другой (панель идёт до экрана — так её видит чтение с экрана), и его
 * поправляет order.
 */
.tabbar {
  position: sticky;
  z-index: 7;
  bottom: 0;
  order: 3;
  margin-top: auto;
  /* Раньше здесь была сетка из пяти колонок, и шестая вкладка — «Люди» у
     администратора — уезжала во второй ряд. Поровну делим ширину между теми
     кнопками, что видны, сколько бы их ни было. */
  display: flex;
  gap: 2px;
  padding: 6px 6px calc(env(safe-area-inset-bottom, 0px) + 6px);
  background: rgba(14, 16, 20, 0.96);
  border-top: 1px solid var(--line);
  backdrop-filter: blur(8px);
}

.tabbar > button {
  flex: 1 1 0;
  min-width: 0;
}

.tabbar button {
  display: flex;
  min-height: 50px;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 3px;
  padding: 4px 2px;
  border: 0;
  border-radius: 12px;
  color: var(--muted);
  background: transparent;
  font-size: 10px;
  font-weight: 750;
  cursor: pointer;
}

.tabbar button svg {
  width: 21px;
  height: 21px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.9;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.tabbar button.active {
  color: var(--accent);
  background: rgba(255, 90, 54, 0.12);
}

/* ── Модальный лист ─────────────────────────────────────────────── */

.sheet-backdrop {
  position: fixed;
  /* Выше ворот (40). Окно открывается и из закрытого экрана — «Заполни свою
     карточку» именно оттуда и открывается, — а на слое 20 оно уходило за
     непрозрачный экран ворот: кнопка работает, форма есть, и её не видно. */
  z-index: 50;
  inset: 0;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  padding: 0;
  background: rgba(6, 7, 10, 0.66);
}

.sheet {
  width: 100%;
  max-width: 640px;
  /* Высота делится на масштаб интерфейса: dvh про zoom не знает, и на 90%
     окно занимало 83% экрана вместо 92 — из-за этого казалось обрезанным. */
  max-height: calc(92dvh / var(--ui-scale, 1));
  overflow-y: auto;
  overscroll-behavior: contain;
  scrollbar-width: thin;
  padding: 16px 16px calc(env(safe-area-inset-bottom, 0px) + 18px);
  border: 1px solid var(--line);
  border-radius: 22px 22px 0 0;
  background: var(--panel);
}

/* Полоса прокрутки видна: без неё длинная форма выглядит обрезанной, а не
   прокручиваемой. */
.sheet::-webkit-scrollbar {
  width: 10px;
}

.sheet::-webkit-scrollbar-thumb {
  border: 3px solid transparent;
  border-radius: 8px;
  background: #4a5361;
  background-clip: content-box;
}

.sheet::-webkit-scrollbar-thumb:hover {
  background: #5d6877;
  background-clip: content-box;
}

/* Окно можно отодвинуть: тянуть за заголовок. Нужно на компьютере, где окно
   упирается в нижний край. */
.sheet-head {
  cursor: move;
  touch-action: none;
}

.sheet-head h2 {
  user-select: none;
}

.sheet-head {
  position: sticky;
  z-index: 2;
  top: -16px;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 6px 0 12px;
  margin: -6px 0 0;
  background: var(--panel);
}

.sheet-head h2 {
  margin: 0;
  flex: 1 1 auto;
  font-size: 17px;
}

.sheet-close {
  width: 36px;
  height: 36px;
  flex: 0 0 auto;
  border: 0;
  border-radius: 11px;
  color: var(--muted);
  background: var(--panel-2);
  font-size: 17px;
  cursor: pointer;
}

/* Подвал окна: кнопка сохранения всегда на виду. Голова окна липкая сверху,
   подвал — снизу, и длинная форма прокручивается между ними. */
.sheet-foot {
  position: sticky;
  z-index: 2;
  bottom: 0;
  padding: 12px 0 6px;
  margin-top: 14px;
  background: var(--panel);
  box-shadow: 0 -12px 16px -12px rgba(0, 0, 0, 0.85);
}

/* Участник занятия: галочка «был» и число патронов рядом. Патроны заносит сам
   стрелок при записи, инструктор правит здесь. */
.participant-row {
  display: flex;
  align-items: center;
  gap: 10px;
}

.participant-row .check-field {
  flex: 1 1 auto;
  min-width: 0;
}

/* Длинный список под кнопкой. Свёрнутость — классом на обёртке: правило с
   display в этом файле побеждает атрибут hidden, и «скрытый» блок остался бы
   на виду. */
.fold {
  margin-top: 10px;
  border: 1px solid var(--line);
  border-radius: 12px;
  background: var(--panel-2);
  overflow: hidden;
}

.fold-head {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  min-height: 46px;
  padding: 10px 12px;
  border: 0;
  background: none;
  color: var(--ink);
  font-family: inherit;
  font-size: 14px;
  font-weight: 700;
  text-align: start;
  cursor: pointer;
}

.fold-title {
  flex: 1 1 auto;
  min-width: 0;
}

/* Число отмеченного — единственное, что видно у свёрнутого блока. */
.fold-count:not(:empty) {
  padding: 2px 9px;
  border-radius: 999px;
  background: var(--accent);
  color: var(--accent-ink);
  font-size: 11.5px;
  font-variant-numeric: tabular-nums;
}

.fold-sign {
  color: var(--muted);
  font-size: 12px;
}

.fold-body {
  padding: 0 12px 12px;
}

.fold.folded .fold-body {
  display: none;
}

.participant-row {
  margin-top: 6px;
}

.rounds-input {
  width: 84px;
  min-height: 38px;
  flex: 0 0 auto;
  padding: 6px 8px;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: var(--panel-2);
  font-size: 13px;
  text-align: center;
}

/* Сумма по стволам. Это не поле: её не правят, её складывают, — поэтому рамка
   пунктиром и цвет приглушён. Габарит ровно как у поля: числа обязаны стоять
   одним столбцом, иначе взгляд ищет их заново в каждой строке. */
.rounds-total {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 84px;
  min-height: 38px;
  flex: 0 0 auto;
  padding: 6px 8px;
  border: 1px dashed var(--line);
  border-radius: 10px;
  color: var(--muted);
  font-size: 13px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

/* Ведомость: на экране — предпросмотр листа, при печати — сам лист вместо
   всего приложения. Держим её в отдельном узле, а не в окне: печатать окно
   поверх приложения значит печатать и приложение. */
/* Лист для печати держится в разметке, но не на виду: display: none лишил бы
   его размеров, а без них не измерить строку и не подогнать графу. Прозрачный и
   зафиксированный узел не тянет за собой прокрутку. */
#print-host {
  position: fixed;
  z-index: -1;
  top: 0;
  left: 0;
  width: 1200px;
  height: 850px;
  overflow: hidden;
  opacity: 0;
  pointer-events: none;
}

#print-host:empty {
  display: none;
}

/* Лист, который сейчас превращают в картинку: он должен быть нарисован, но не
   виден — display: none сделал бы измерение строк невозможным. */
.roster-stage {
  position: absolute;
  z-index: -1;
  top: 0;
  left: -20000px;
  width: 1200px;
  opacity: 0;
  pointer-events: none;
}

.roster-preview {
  overflow-x: auto;
  margin-top: 12px;
  border: 1px solid var(--line);
  border-radius: 12px;
  background: #ffffff;
}

.roster-preview .roster-sheet {
  display: block;
  width: 100%;
  height: auto;
}

.roster-preview .roster-sheet + .roster-sheet {
  border-top: 1px solid #cccccc;
}

/* Предпросмотр листа схемы — тот же приём, что и у ведомости: белая бумага,
   лист во всю ширину окна, листы друг под другом с чертой между ними. */
.sheet-preview {
  overflow-x: auto;
  margin-top: 12px;
  border: 1px solid var(--line);
  border-radius: 12px;
  background: #ffffff;
}

.sheet-preview .drill-sheet {
  display: block;
  width: 100%;
  height: auto;
}

.sheet-preview .drill-sheet + .drill-sheet {
  border-top: 1px solid #cccccc;
}

/* Иконка в шапке карточки: открывает удостоверение. Прозрачная, чтобы не
   спорить с градиентом шапки. */
.card-badge-button {
  display: grid;
  width: 30px;
  height: 30px;
  flex: 0 0 auto;
  place-items: center;
  padding: 0;
  border: 1px solid rgba(255, 255, 255, 0.45);
  border-radius: 9px;
  color: inherit;
  background: rgba(255, 255, 255, 0.16);
  cursor: pointer;
}

/* Удостоверений столько, сколько клубов, и стоят они в шапке в ряд.
   Сжимается и переносит: на телефоне трём кнопкам в одну строку не встать, а
   ушедшая за край кнопка — это кнопка, по которой не нажать. */
.shooter-card-badges {
  display: flex;
  min-width: 0;
  flex: 0 1 auto;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: 6px;
}

/* Когда клубов несколько, рядом с иконкой стоит название клуба: две одинаковые
   иконки рядом не различить. Ширина при этом перестаёт быть квадратной.
   minmax(0, auto) — чтобы столбцу с названием было чем сжиматься: иначе его
   наименьшая ширина равна всей строке, и резать её нечем. */
.card-badge-button.named {
  /* Ряд, а не сетка. Сетке для ряда нужен явный перечень столбцов, и стоит
     этому одному свойству не доехать — иконка с подписью встают друг под
     друга, вылезая из шапки. У ряда порядок «в строку» и есть поведение по
     умолчанию, ломать нечего. */
  display: flex;
  width: auto;
  max-width: 100%;
  align-items: center;
  padding: 0 8px;
  gap: 6px;
}

.card-badge-button.named svg {
  flex: 0 0 auto;
}

.card-badge-button.named span {
  overflow: hidden;
  min-width: 0;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Список клубов слева при нескольких удостоверениях: на экране его называют
   сами кнопки, и повторять названия дважды значит отнимать место у обоих. На
   бумаге кнопок нет — там список и остаётся. */
.shooter-card-top b.only-print {
  display: none;
}

.card-badge-button svg {
  width: 18px;
  height: 18px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.7;
  stroke-linecap: round;
}

/* Членская карточка клуба: рисунок, а не разметка. Своих цветов у неё нет —
   они внутри рисунка: он уходит в картинку отдельным документом, и внешние
   стили в него не попадают. Здесь только размер на экране.

   Пропорция задана самим рисунком (viewBox), поэтому высоту не назначаем: она
   считается из ширины сама, и на телефоне карточка не обрезается. */
.idcard-deck {
  display: flex;
  flex-direction: column;
  gap: 14px;
}

.idcard-svg {
  display: block;
  width: 100%;
  max-width: 520px;
  height: auto;
  border-radius: 12px;
}

/* Карточка и столбик кнопок к ней — в одной строке, кнопки справа.

   Карточке разрешено сжиматься (min-width: 0), иначе строка не сожмётся вовсе
   и кнопки уедут за край экрана: это уже случалось в журнале с рядом кнопок,
   у которого нечему было уменьшаться.

   На узком экране строка переносится, и столбик становится рядом кнопок под
   карточкой: на телефоне в 360 точек карточка и два столбика подписей рядом не
   стоят. */
.idcard-row {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  align-items: flex-start;
}

.idcard-row > .idcard-svg {
  flex: 1 1 240px;
  min-width: 0;
}

.idcard-tools {
  display: flex;
  flex: 0 0 auto;
  flex-direction: column;
  gap: 8px;
  width: 148px;
  max-width: 100%;
}

@media (max-width: 430px) {
  /* Перенеслось — значит кнопки стоят под карточкой, и столбик им уже не нужен:
     двумя в ряд они читаются лучше, чем двумя друг под другом на всю ширину. */
  .idcard-tools {
    flex-direction: row;
    width: 100%;
  }

  .idcard-tools > * {
    flex: 1 1 0;
  }
}

/* Виртуальное удостоверение: пропорции банковской карты, читается и в тёмной
   теме — это своя карточка журнала, а не копия чужого документа. */
.idcard {
  display: flex;
  overflow: hidden;
  max-width: 520px;
  flex-direction: column;
  border: 1px solid var(--line);
  border-radius: 16px;
  background: var(--panel-2);
}

.idcard-top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 10px 14px;
  background: linear-gradient(120deg, var(--accent), var(--accent-deep));
  color: var(--accent-ink);
  font-size: 12px;
  font-weight: 800;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

.idcard-body {
  display: flex;
  gap: 14px;
  padding: 14px;
}

.idcard-photo {
  display: grid;
  overflow: hidden;
  width: 96px;
  height: 120px;
  flex: 0 0 auto;
  place-items: center;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: var(--panel);
  color: var(--muted);
  font-size: 26px;
  font-weight: 800;
}

.idcard-photo.has-photo {
  display: block;
}

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

.idcard-data {
  min-width: 0;
  flex: 1 1 auto;
}

.idcard-name {
  margin: 0 0 8px;
  font-size: 17px;
  font-weight: 800;
}

.idcard-rows {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 3px 10px;
  font-size: 12.5px;
}

.idcard-rows span:nth-child(odd) {
  color: var(--muted);
}

.idcard-rows span:nth-child(even) {
  font-weight: 600;
}

.idcard-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 8px 14px 12px;
  color: var(--muted);
  font-size: 11.5px;
}

/* ── Тост ───────────────────────────────────────────────────────── */

/* Сообщение видно всегда: и поверх окна, и поверх ворот. Иначе отказ,
   случившийся в окне, остался бы за ним. */
.toast {
  position: fixed;
  z-index: 60;
  right: 12px;
  bottom: calc(var(--tabbar-height) + env(safe-area-inset-bottom, 0px) + 14px);
  left: 12px;
  max-width: 560px;
  padding: 12px 14px;
  border: 1px solid var(--line);
  border-radius: 14px;
  margin: 0 auto;
  background: #22272f;
  box-shadow: 0 16px 40px rgba(0, 0, 0, 0.4);
  color: var(--text);
  font-size: 13px;
  text-align: center;
}

/* ── Широкий экран: панель слева, как в Moto ────────────────────── */

@media (min-width: 900px) {
  /* На компьютере окно стоит по центру и целиком в рамке: прижатое к нижнему
     краю, оно читается как обрезанное, даже когда прокручивается. */
  .sheet-backdrop {
    align-items: center;
    padding: 24px;
  }

  .sheet {
    border-radius: 20px;
  }

  .topbar-account {
    display: none;
  }

  .tabbar-account {
    display: block;
    margin-top: auto;
    padding-top: 10px;
  }

  .tabbar-account .account-chip {
    width: 100%;
  }

  .app-shell {
    display: grid;
    grid-template-columns: 232px minmax(0, 1fr);
    grid-template-rows: auto 1fr;
    grid-template-areas:
      "brand top"
      "nav main";
    min-height: 100vh;
  }

  .topbar {
    grid-area: top;
  }

  .tabbar {
    position: sticky;
    top: 0;
    right: auto;
    bottom: auto;
    left: auto;
    /* На широком экране панель — боковая колонка сетки: прижимать её к низу
       нечем и незачем. */
    margin-top: 0;
    display: flex;
    height: 100vh;
    flex-direction: column;
    gap: 6px;
    grid-area: nav;
    padding: 16px 12px;
    border-top: 0;
    border-right: 1px solid var(--line);
    grid-row: 1 / span 2;
  }

  .tabbar button {
    min-height: 46px;
    flex-direction: row;
    justify-content: flex-start;
    gap: 11px;
    padding: 10px 12px;
    font-size: 13px;
  }

  .screen-host {
    grid-area: main;
    padding-bottom: 40px;
  }

  .toast {
    bottom: 22px;
  }
}

/* ── Иврит: справа налево ───────────────────────────────────────── */

[dir="rtl"] .list-item,
[dir="rtl"] .field > span {
  text-align: right;
}

[dir="rtl"] .chart-axis {
  direction: ltr;
}

/* ── Фото в форме ученика ───────────────────────────────────────── */

.photo-row {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  margin-top: 4px;
}

.photo-box {
  width: 88px;
  height: 110px;
  flex: 0 0 auto;
  overflow: hidden;
  border: 1px solid var(--line);
  border-radius: 12px;
  background: var(--panel-2);
}

.photo-empty {
  display: grid;
  width: 100%;
  height: 100%;
  place-items: center;
  color: var(--muted);
  font-size: 26px;
  font-weight: 800;
}

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

.photo-actions {
  display: flex;
  min-width: 0;
  flex: 1 1 auto;
  flex-direction: column;
  gap: 7px;
}

/* ── Карточка стрелка ───────────────────────────────────────────── */

.shooter-card {
  overflow: hidden;
  border: 1px solid #3a4250;
  border-radius: 16px;
  background: var(--panel-2);
}

.shooter-card-top {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
  padding: 9px 13px;
  background: linear-gradient(120deg, var(--accent), var(--accent-deep));
  color: var(--accent-ink);
}

.shooter-card-top span {
  font-size: 10.5px;
  font-weight: 800;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  opacity: 0.9;
}

.shooter-card-top b {
  overflow: hidden;
  font-size: 12px;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.shooter-card-body {
  display: flex;
  align-items: flex-start;
  gap: 13px;
  padding: 13px;
}

.shooter-card-photo {
  width: 84px;
  height: 105px;
  flex: 0 0 auto;
  overflow: hidden;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: var(--panel);
}

.card-photo {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.card-photo.empty {
  display: grid;
  place-items: center;
  color: var(--muted);
  font-size: 25px;
  font-weight: 800;
}

.shooter-card-data {
  min-width: 0;
  flex: 1 1 auto;
}

.shooter-card-name {
  display: block;
  font-size: 16px;
  line-height: 1.25;
}

.shooter-card-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 5px;
  margin-top: 7px;
}

/* Мелкая пометка рядом с именем в списке: срок разрешения виден до того, как
   карточку открыли. */
.badge.tiny {
  padding: 1px 6px;
  margin-inline-start: 6px;
  font-size: 9.5px;
  letter-spacing: 0.03em;
  vertical-align: middle;
}

.field-hint.warn,
.list-item-sub.warn {
  color: #f5c451;
}

.field-hint.bad,
.list-item-sub.bad {
  color: var(--red);
}

/* Телефон и почта на карточке — ссылки: звонок и письмо в одно касание. */
.card-link {
  color: var(--accent-2);
  text-decoration: none;
}

.card-link:hover {
  text-decoration: underline;
}

.badge.warn {
  color: #1d1403;
  background: #f5c451;
}

.badge.bad {
  color: #fff;
  background: var(--red);
}

.shooter-card-rows {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 3px 10px;
  margin: 10px 0 0;
}

.shooter-card-rows dt {
  color: var(--muted);
  font-size: 11px;
}

.shooter-card-rows dd {
  margin: 0;
  font-size: 12.5px;
  font-variant-numeric: tabular-nums;
  overflow-wrap: anywhere;
}

.gun-editor {
  padding: 11px 12px;
  border: 1px solid var(--line);
  border-radius: 14px;
  margin-top: 10px;
  background: rgba(255, 255, 255, 0.03);
}

/* ── Схема площадки ─────────────────────────────────────────────── */

.stage-host {
  margin-top: 10px;
  padding: 8px;
  border: 1px solid var(--line);
  border-radius: 14px;
  background: #101318;
}

.stage-svg {
  display: block;
  width: 100%;
  height: 46vh;
  max-height: 420px;
}

/* Снятая схема: та же рамка, что и у чертежа, — на экране это одно и то же,
   «как выглядит площадка». Ограничение по высоте такое же, как у чертежа:
   вертикальный снимок иначе занимал бы весь экран, и до кнопок под ним пришлось
   бы листать. */
.drill-photo {
  display: block;
  width: 100%;
  max-height: 46vh;
  object-fit: contain;
  border-radius: 10px;
}

/* Снимок в окне занятия, пока у него ещё спрашивают название: маленький, но
   такой, чтобы отличить одну схему от другой. С телефона легко прислать не тот
   кадр, и увидеть это надо до того, как он попадёт в занятие. */
.drill-photo-preview {
  display: block;
  width: 100%;
  max-height: 220px;
  object-fit: contain;
  margin-top: 8px;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: #101318;
}

/* Жест забирает себе не весь план, а только предмет под пальцем. Забрав
   весь холст, редактор отнимал бы и прокрутку страницы: план занимает пол-
   экрана, и пролистать его пальцем становилось нечем — а теперь тянуть можно
   на каждой вкладке, и цена этого была бы на каждой. */
.stage-svg.editable {
  touch-action: pan-y;
}

.stage-svg.editable [data-id],
.stage-svg.editable .stage-handle {
  touch-action: none;
}

.stage-ground {
  fill: #14181e;
  stroke: #2a303a;
}

.stage-grid {
  stroke: rgba(255, 255, 255, 0.05);
  stroke-width: 1;
}

.stage-grid.major {
  stroke: rgba(255, 255, 255, 0.13);
}

.stage-axis {
  fill: #6f7a89;
  font-size: 9px;
}

.stage-firing-line {
  stroke: var(--accent-2);
  stroke-width: 2;
  stroke-dasharray: 7 5;
}

.stage-face {
  fill: var(--accent);
  stroke: #ffb9a6;
  stroke-width: 1;
}

.stage-noshoot .stage-face {
  fill: #d8dee8;
  stroke: #8b93a1;
}

.stage-cross {
  stroke: #7a808c;
  stroke-width: 1.2;
}

.stage-facing {
  stroke: rgba(255, 185, 166, 0.75);
  stroke-width: 1.4;
}

.stage-prop {
  fill: #3b434f;
  stroke: #55606f;
  stroke-width: 1;
}

.stage-fault {
  stroke: #f5c451;
  /* Толще и реже, чем было: мелкий пунктир в два пикселя сливался в сплошную
     черту и был неотличим от любой другой линии на чертеже. Здесь нужен именно
     вид ленты, выложенной кусками. */
  stroke-width: 4;
  stroke-dasharray: 9 7;
  stroke-linecap: round;
}

.stage-box {
  /* Границы бокса должны читаться сразу: это место, из которого стреляют, и
     первое, что ищут на схеме. Тонкий пунктир на бумаге пропадал вовсе, а
     светло-зелёная рамка терялась среди клеток. */
  fill: rgba(192, 57, 43, 0.12);
  stroke: #c0392b;
  stroke-width: 2.6;
}

/* Засечки по углам — так бокс размечают лентой в тире, и по ним границы видно
   даже там, где сама линия тонкая. */
.stage-box-corner {
  stroke: #c0392b;
  stroke-width: 3.4;
  stroke-linecap: square;
}

.stage-shooter {
  fill: var(--green);
}

.stage-shooter-dir {
  fill: var(--green);
}

.stage-touch {
  fill: transparent;
  /* И БЕЗ ОБВОДКИ.
     
     Обводка в SVG наследуется. У штрафной линии на самой группе стоит класс
     «stage-fault» — он назван по типу предмета, — а тот задаёт жёлтый пунктир.
     Площадка касания внутри этой группы наследовала его и обводилась жёлтым
     пунктирным КВАДРАТОМ вокруг линии: восемьдесят на восемьдесят пикселей
     ниоткуда. Площадка касания на то и площадка, что её не видно; краски она
     не берёт ни от кого. */
  stroke: none;
}

/* Полоса касания вдоль линии замера: сама линия тонкая, пальцем в неё не
   попасть, а убрать свободную линейку больше нечем — она не висит ни на одном
   предмете, и в чужой панели её нет. */
.stage-touch-line {
  stroke: transparent;
  stroke-width: 16;
}

.stage-item.selected .stage-face,
.stage-item.selected .stage-prop,
.stage-item.selected .stage-box {
  stroke: #fff;
  stroke-width: 2.2;
}

/* ── Схема: новые элементы ──────────────────────────────────────── */

.stage-face.noshoot {
  fill: #d8dee8;
  stroke: #8b93a1;
}

.stage-face.steel {
  fill: #c9d2e0;
  stroke: #7f8a9a;
}

.stage-face.mover {
  fill: #f5c451;
  stroke: #fbe3a6;
}

.stage-face.array {
  fill: rgba(255, 90, 54, 0.35);
  stroke: var(--accent);
}

.stage-steel-leg {
  fill: #6c7686;
  stroke: none;
}

.stage-steel-leg.noshoot {
  fill: #8b93a1;
}

.stage-port-marker {
  fill: var(--accent-2);
}

/* Проём в стене на виде сверху. Стена цела — окно прорезано по высоте, — и
   поэтому окно рисуется НАКЛАДКОЙ на стену: пустой внутри, чтобы через него
   читалась дырка, и обведённым, чтобы было видно, где он вдоль стены. */
.stage-port {
  fill: var(--bg);
  stroke: var(--accent-2);
  stroke-width: 1.5;
}

/* Створка: куда её толкать. Линия от окна поперёк стены и стрелка на конце. */
.stage-port-door {
  stroke: var(--accent-2);
  stroke-width: 1.5;
}

/* На какой высоте окно — знаком над ним. Сверху высоту не видно вовсе, а
   стрелять стоя или с колена — это разные упражнения. */
/* Знак высоты проёма. Размер здесь НЕ задаётся: его ставит рисовальщик
   строкой стиля, потому что он зависит от размера площадки — как и у всех
   прочих подписей плана. Жёсткие девять пикселей и были причиной того, что на
   большом поле знак превращался в точку. */
.stage-port-level {
  fill: var(--accent-2);
  font-weight: 700;
}

/* Сколько метров в клетке плана. Без подписи сетку читают как что угодно —
   от полуметра до двух. */
.stage-grid-note {
  fill: var(--muted);
  font-size: 9px;
}

/* Ряд глубины: линия поперёк поля и число у её края. По схеме этап размечают
   рулеткой от переднего края, и это первое, что на ней ищут. */
.stage-range {
  stroke: #c0392b;
  stroke-width: 1.2;
  stroke-dasharray: 5 3;
  opacity: 0.75;
}

/* Своя линия замера: её провели руками, значит она важнее рядов. Сплошная и
   заметнее — пунктир рядов рядом с ней читается как разметка, а это ответ на
   заданный вопрос. */
.stage-measure {
  stroke: #1f6feb;
  stroke-width: 1.6;
  opacity: 0.9;
}

.stage-range-label.measure {
  fill: #1f6feb;
}

.stage-range-label {
  fill: #c0392b;
  font-size: 10px;
  font-weight: 700;
}

.stage-code {
  fill: #aab3c0;
  font-size: 9px;
  font-weight: 700;
}

/* Свой узор из мишеней: по строке на каждую. Списком, а не пальцем по
   картинке — мишени в сборке нарочно лежат друг на друге, и попасть пальцем в
   нижнюю нельзя: её не видно. */
.array-rows {
  display: flex;
  flex-direction: column;
  gap: 4px;
  margin: 6px 0 10px;
}

.array-line {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 4px;
}

.array-no {
  font-family: ui-monospace, monospace;
  font-size: 12px;
  color: var(--dim, #6f665c);
  min-width: 14px;
}

.array-at {
  font-family: ui-monospace, monospace;
  font-size: 11px;
  color: var(--dim, #6f665c);
  font-variant-numeric: tabular-nums;
}

.array-line .button.small.active {
  border-color: var(--accent, #2f4858);
  border-width: 2px;
}

/* Выбор расстановки сборки: восемь маленьких видов спереди. Словами их не
   различить — «три в ряд» и «три внахлёст» на слух одно и то же. */
.array-row {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}

.array-pick {
  border: 1px solid var(--line, #d8d0c4);
  background: var(--card, #fff);
  border-radius: 6px;
  padding: 3px;
  cursor: pointer;
  line-height: 0;
}

.array-pick.active {
  border-color: var(--accent, #2f4858);
  border-width: 2px;
  padding: 2px;
}

svg.array-icon {
  width: 74px;
  height: 52px;
  display: block;
}

/* Выбор перекрытия: девять маленьких мишеней в ряд. Картинкой, а не словом —
   «верх», «низ» и «угол сверху слева» на слух одинаковы. */
.cover-row {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}

.cover-pick {
  border: 1px solid var(--line, #d8d0c4);
  background: var(--card, #fff);
  border-radius: 6px;
  padding: 3px;
  cursor: pointer;
  line-height: 0;
}

.cover-pick.active {
  border-color: var(--accent, #2f4858);
  border-width: 2px;
  padding: 2px;
}

.cover-icon {
  width: 26px;
  height: 38px;
  display: block;
}

/* Точка перекрытия на плане: «мишень чем-то закрыта, смотри спереди».
   Сверху видно только перекрытие по ширине — верх, низ и угол уходят по
   высоте, а высоты на плане нет. */
.stage-cover-dot {
  stroke-width: 1;
}

/* Подпись «mini»: мельче и спокойнее номера — она поясняет, а не нумерует.
   Латиницей нарочно: слово одинаково на всех восьми языках. */
.stage-code.mini {
  font-size: 8px;
  font-weight: 600;
  letter-spacing: 0.06em;
  opacity: 0.85;
}

.stage-motion {
  fill: none;
  stroke: #f5c451;
  stroke-width: 1.4;
  stroke-dasharray: 3 3;
}

.stage-activator {
  fill: rgba(50, 213, 131, 0.25);
  stroke: var(--green);
  stroke-width: 1.4;
}

.stage-slot-mark {
  fill: var(--accent);
}

.stage-slot-mark.noshoot {
  fill: #d8dee8;
}

.stage-prop.dashed {
  stroke-dasharray: 4 3;
}

/* Следы подошв в боксе стрелка: место человека, а не ещё одна железка. */
.stage-shoe rect {
  fill: #e8e4dd;
  stroke: #8d8579;
  stroke-width: 0.8;
}

/* Косые отметки ножек: стол сверху — столешница и четыре ноги. */
.stage-leg-mark {
  stroke: #8a6a4a;
  stroke-width: 3;
  stroke-linecap: round;
}

/* Подпорка зрительного барьера: ею он и отличается от стены. */
.stage-brace {
  stroke: #7d8796;
  stroke-width: 3;
  stroke-linecap: round;
}

/* ── движение в объёме ───────────────────────────────────────────
   В покое качель, боббер и макс-трэп — это мишень на стойке, и объём честно
   показывает мишень на стойке. Отличает их не вид, а ход. Поэтому в объёме
   рисуется второе положение призраком и стрелка между ними. */

.face3d.ghost3d {
  fill: none;
  stroke: #9aa4b2;
  stroke-width: 1.4;
  stroke-dasharray: 5 4;
}

.face3d.move3d {
  fill: var(--accent);
  stroke: none;
  opacity: 0.92;
}

/* Табличка со знаком механизма: тот же знак, что на виде сверху. */
.face3d.sign3d {
  fill: #1c2027;
  stroke: #f5c451;
  stroke-width: 1.2;
}

.face3d.sign3d-ink {
  fill: #f5c451;
  stroke: none;
}

/* Куда смотрит стрелок: клин на земле перед следами. По одним следам не
   понять, лицом человек к валу или спиной, — а в El Presidente он как раз
   спиной, и это половина замысла упражнения. */
.face3d.shooter-dir {
  fill: rgba(50, 213, 131, 0.45);
  stroke: none;
}

/* Следы стрелка на земле — те же, что на плане. */
.face3d.shoe {
  fill: #e8e4dd;
  stroke: #8d8579;
  stroke-width: 1;
}

/* Знак механизма на виде сверху. Заменил буквенные коды «S», «DT», «CS», «MT»:
   сверху все подвижные одинаковы — полоска, — и код был единственным, чем они
   различались, то есть не различались никак.

   Знак стоит НАД следом мишени и в её размер не входит: сам след — это то, что
   мишень занимает на земле, по нему меряют рулеткой, и раздувать его картинкой
   нельзя. */
.stage-glyph-line {
  fill: none;
  stroke: #f5c451;
  stroke-width: 1.6;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.stage-glyph-dot {
  fill: #f5c451;
  stroke: none;
}

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

.mech-ground {
  stroke: #6f7a89;
  stroke-width: 1.4;
  stroke-dasharray: 5 4;
}

.mech-base {
  fill: #6c7686;
  stroke: none;
}

.mech-post {
  fill: #8a94a4;
  stroke: none;
}

.mech-paper {
  fill: #c98a52;
  stroke: #e0a878;
  stroke-width: 1.4;
}

.mech-steel {
  fill: #dfe3ea;
  stroke: #8a94a4;
  stroke-width: 1.4;
}

.mech-pedal {
  fill: #b8763a;
  stroke: #d99a5f;
  stroke-width: 1.4;
}

.mech-cable {
  fill: none;
  stroke: #8a94a4;
  stroke-width: 2;
  stroke-linecap: round;
}

/* Стрелка движения — единственное оранжевое пятно на схемке. Оранжевый здесь
   значит «вот это и едет»: если им покрасить что-то ещё, взгляд перестанет
   находить движение с первого раза. */
.mech-arrow {
  fill: none;
  stroke: var(--accent);
  stroke-width: 2.6;
  stroke-linecap: round;
}

.mech-arrow-head {
  fill: var(--accent);
  stroke: none;
}

/* Где предмет был или где окажется. Заливку снимаем совсем: призрак с
   заливкой спорит с настоящим предметом за внимание. */
.mech-ghost {
  fill: none;
  stroke: #7a828f;
  stroke-width: 1.4;
  stroke-dasharray: 4 3;
}

/* Объёмный значок в карточке: та же рамка, что у вида сверху. Холст объёма в
   приложении растянут на пол-экрана — здесь он должен быть значком, и высоту
   ему задаём тут. */
.parts-icon.space {
  display: block;
  width: 100%;
  height: 132px;
  max-height: 132px;
  border-radius: 10px;
  background: #101318;
}

/* Вид сборки спереди в карточке: та же рамка, что у прочих значков. */
.parts-array {
  width: 100%;
}

.parts-array .stage-front {
  display: block;
  width: 100%;
  height: 150px;
  max-height: 150px;
  border-radius: 10px;
  background: #101318;
}

.parts-mech {
  display: block;
  width: 100%;
  height: auto;
  border-radius: 10px;
  background: #101318;
}

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

   Раньше она была жёлтой, и на это была причина: две пунктирные вещи одного
   цвета означали разное. Но причина оказалась не та. На площадке это ОДНА И ТА
   ЖЕ лента, и два разных цвета на схеме придумывали различие, которого в тире
   нет — человек искал у них разный смысл и не находил.

   Различает их ФОРМА: у зоны замкнутый контур в одну черту, у линии две строчки
   пилюль со сдвигом, так что красное стоит против белого. Это читается как
   лента, а не как две линии рядом, и спутать одно с другим нельзя.

   Цвета — те же самые числа, что у зоны (stage-area-path и stage-area-tape).
   Разойдись они, на одном чертеже оказались бы две «одинаковые» ленты чуть
   разного оттенка — хуже, чем два честно разных цвета. */
.stage-fault-mark {
  fill: #d8392a;
  stroke: none;
}

.stage-fault-mark.white {
  fill: #f2efe9;
}

/* Пятка ножки барьера: без неё ножка читается как стена под прямым углом. */
.stage-brace.foot {
  stroke-width: 4;
}

/* Тренировочные железки: лучи звезды, балка рэка, стойка дерева. */
.stage-arm {
  stroke: #8a94a4;
  stroke-width: 2.4;
  stroke-linecap: round;
}

.stage-arm.beam {
  stroke-width: 4;
}

.stage-plate {
  fill: #dfe3ea;
  stroke: #8a94a4;
  stroke-width: 1.2;
}

/* Размерная линия: засечки на концах и деления по метрам. */
.stage-measure-cap {
  stroke: #1f6feb;
  stroke-width: 2.2;
  stroke-linecap: round;
}

.stage-measure-tick {
  stroke: #1f6feb;
  stroke-width: 1.2;
  opacity: 0.75;
}

.stage-prop.custom {
  stroke: #9aa4b2;
  stroke-width: 1;
}

.stage-link {
  stroke: var(--green);
  stroke-width: 1.2;
  stroke-dasharray: 4 4;
}

.stage-handle-line {
  stroke: #fff;
  stroke-width: 1;
  stroke-dasharray: 3 3;
}

.stage-handle-dot {
  fill: #fff;
  stroke: var(--accent);
  stroke-width: 2;
}

/* Линия выравнивания и подпись размеров живут только под пальцем. */
.stage-guide {
  stroke: var(--accent-2);
  stroke-width: 1;
  stroke-dasharray: 5 4;
  opacity: 0.75;
}

.stage-measure {
  fill: #fff;
  font-size: 13px;
  font-weight: 700;
  text-anchor: middle;
  /* Обводка тем же тёмным полем: над мишенью белые цифры иначе теряются. */
  stroke: rgba(8, 10, 14, 0.85);
  stroke-width: 4;
  paint-order: stroke;
}

/* Зона — красно-белой лентой ограждения.
   
   Так её и обозначают на площадке: чередующимися красными и белыми кусками,
   и это сразу читается как «сюда нельзя» — в отличие от рамки любого другого
   цвета, о значении которой надо догадываться.
   
   Двумя слоями: белая сплошная подложка и красный пунктир поверх. Один
   пунктир двух цветов средствами обводки не задать, а рисовать ленту кусками
   значило бы завести свою геометрию там, где хватает двух линий. */
.stage-area-tape {
  fill: none;
  stroke: #f2efe9;
  stroke-width: 5;
  stroke-linejoin: round;
}

.stage-area-path {
  /* Красным, а не жёлтым: жёлтым нарисована штрафная линия, и две жёлтые
     пунктирные вещи на одном чертеже означали разное, а выглядели одинаково.

     Свечение — вторым широким полупрозрачным контуром под основным, а не
     фильтром: фильтры не переживают перенос схемы в картинку и на печать, и
     на бумаге от них остаётся пустое место. */
  fill: rgba(224, 74, 37, 0.07);
  stroke: #d8392a;
  stroke-width: 5;
  /* Куски ленты: красный поверх белой подложки — оттого лента и полосатая. */
  stroke-dasharray: 9 9;
  stroke-linejoin: round;
}

.stage-area-glow {
  fill: none;
  stroke: rgba(216, 57, 42, 0.22);
  stroke-width: 10;
  stroke-linejoin: round;
}

/* Ручки по углам: за них зону тянут. */
.stage-area-grip {
  fill: #fff;
  stroke: #d8392a;
  stroke-width: 2.4;
}

.stage-area-dot {
  fill: #e04a25;
}

/* ── QR-код ссылки ──────────────────────────────────────────────────

   Белым по чёрному код не читается: камеры ищут тёмные клетки на светлом фоне.
   Поэтому у него своя, всегда светлая подложка — независимо от того, что вокруг
   в приложении темно. */
.qr-host {
  display: flex;
  justify-content: center;
  margin: 12px 0 4px;
}

.qr {
  display: block;
  width: 100%;
  /* Предел по ширине: растянутый во весь экран код камере не помогает, а места
     занимает столько, что подпись под ним уходит за край. */
  max-width: 260px;
  height: auto;
  border-radius: 10px;
}

.qr-bg {
  fill: #ffffff;
}

.qr-dot {
  fill: #000000;
}

/* ── Схема: вид спереди ─────────────────────────────────────────── */

.stage-front-host {
  margin-top: 10px;
  padding: 8px;
  border: 1px solid var(--line);
  border-radius: 14px;
  background: #101318;
}

.stage-front {
  display: block;
  width: 100%;
  height: 34vh;
  max-height: 300px;
}

.stage-front.editable {
  touch-action: none;
}

.stage-azone {
  fill: rgba(255, 255, 255, 0.16);
  stroke: none;
}

.stage-front-slot.noshoot .stage-face {
  fill: #d8dee8;
  stroke: #8b93a1;
}

.stage-front-slot.selected .stage-face {
  stroke: #fff;
  stroke-width: 2.4;
}

.stage-limit {
  stroke: var(--red);
  stroke-width: 1.2;
  stroke-dasharray: 7 4;
}

.stage-limit.mini {
  stroke: #f5c451;
  stroke-dasharray: 4 5;
}

/* ── Схема: объёмный вид ────────────────────────────────────────── */

.stage-3d {
  display: block;
  width: 100%;
  height: 46vh;
  max-height: 420px;
  touch-action: none;
}

.face3d {
  stroke-width: 0.6;
  vector-effect: non-scaling-stroke;
}

.face3d.ground {
  fill: #14181e;
  stroke: #2a303a;
}

.face3d.gridline {
  fill: rgba(255, 255, 255, 0.06);
  stroke: none;
}

.face3d.gridline.major {
  fill: rgba(255, 255, 255, 0.16);
}

.face3d.target {
  fill: var(--accent);
  stroke: #ffb9a6;
}

.face3d.noshoot {
  fill: #d8dee8;
  stroke: #8b93a1;
}

.face3d.steel {
  fill: #c9d2e0;
  stroke: #7f8a9a;
}

.face3d.leg {
  fill: #6c7686;
  stroke: none;
}

/* Крест «не стрелять» в объёме. Тёмный: белая мишень бывает и зачётной, и
   штрафной, и различить их можно только по кресту — цвет тут не помощник. */
.face3d.cross {
  fill: #333a45;
  stroke: none;
}

.face3d.wall {
  fill: #3b434f;
  stroke: #55606f;
}

/* Свои линии замера и связь «активатор — мишень» в объёме: они лежат на полу
   площадки, и их видно с рубежа так же, как на плане. */
.face3d.measure {
  fill: var(--accent-2);
  stroke: none;
}

.face3d.link {
  fill: #7f8a9a;
  stroke: none;
}

/* Число замера в объёме — подпись к линии, а не часть площадки. */
.measure3d {
  fill: var(--accent-2);
  font-weight: 700;
}

/* Створка окна в объёме: светлее стены и с обводкой — её видно как отдельную
   доску, стоящую под углом, а не как часть стены. */
.face3d.door {
  fill: #59636f;
  stroke: #8b97a8;
}

.face3d.vision {
  fill: rgba(59, 67, 79, 0.55);
  stroke: #55606f;
}

.face3d.barrel {
  fill: #4a5361;
  stroke: #5d6877;
}

.face3d.barrel.top {
  fill: #5d6877;
}

.face3d.table {
  fill: #45505f;
  stroke: #5d6877;
}

.face3d.mover {
  fill: #f5c451;
  stroke: #fbe3a6;
}

.face3d.activator {
  fill: rgba(50, 213, 131, 0.6);
  stroke: var(--green);
}

.face3d.startbox {
  /* Тот же красный, что и на плане: бокс — одно и то же место в обоих видах,
     и разный цвет заставлял бы искать его заново. */
  fill: rgba(192, 57, 43, 0.22);
  stroke: #c0392b;
  /* Толще обычной грани: на плане бокс обведён жирно, и в объёме он должен
     весить столько же — бледный контур читается как тень, а не как границы. */
  stroke-width: 0.16;
}

.face3d.shooter {
  fill: rgba(50, 213, 131, 0.5);
  stroke: var(--green);
}

/* Та же лента в объёме: кусками, красный через белый. Цвета те же, что на
   плане и у зоны, — одна лента должна выглядеть одной с любой стороны. */
.face3d.fault {
  fill: #d8392a;
  stroke: none;
}

.face3d.fault.white {
  fill: #f2efe9;
}

/* Зона в объёме: лёгкая краска внутри и красно-белая лента по краю — та же,
   что у штрафной линии. Оливковым пятном с жёлтой рамкой она читалась рядом с
   красно-белой линией как другой предмет, хотя на площадке их огораживают одной
   и той же лентой. Обводки у пятна больше нет: её место заняла лента. */
.face3d.area {
  fill: rgba(216, 57, 42, 0.12);
  stroke: none;
}

.face3d.area-tape {
  fill: #d8392a;
  stroke: none;
}

.face3d.area-tape.white {
  fill: #f2efe9;
}

.face3d.prop {
  fill: #6d7686;
  stroke: #8a93a3;
}

/* ── Схема: проверка по правилам ────────────────────────────────── */

.stage-checks {
  display: flex;
  flex-direction: column;
  gap: 7px;
  padding: 0;
  margin: 8px 0 0;
  list-style: none;
}

.stage-checks li {
  padding: 9px 11px;
  border: 1px solid rgba(240, 68, 56, 0.4);
  border-radius: 12px;
  background: rgba(240, 68, 56, 0.08);
  color: #ffd9d5;
  font-size: 12px;
  line-height: 1.45;
}

.stage-checks li b {
  color: #fff;
  white-space: nowrap;
}

.field-hint.good {
  color: var(--green);
}

/* ── Экран блокировки ───────────────────────────────────────────── */

.lock-screen {
  position: fixed;
  z-index: 40;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 20px;
  background:
    radial-gradient(circle at 50% 0%, rgba(255, 90, 54, 0.16), transparent 45%),
    var(--bg);
}

.lock-card {
  display: flex;
  width: 100%;
  max-width: 320px;
  flex-direction: column;
  align-items: center;
  gap: 14px;
}

.lock-card h2 {
  margin: 0;
  font-size: 18px;
}

/* Экран входа — тот же экран-ворота, но в нём поля и кнопки, а не десять
   клавиш, поэтому он шире, и содержимое тянется во всю ширину. */
.lock-card.wide {
  max-width: 380px;
  align-items: stretch;
  text-align: start;
}

.lock-card.wide .brand-icon {
  align-self: center;
}

.lock-card.wide h2 {
  text-align: center;
}

.gate-lang {
  display: flex;
  justify-content: center;
}

.gate-lang select {
  max-width: 180px;
}

.lock-note {
  margin: 0;
  color: var(--muted);
  font-size: 11.5px;
  line-height: 1.5;
  opacity: 0.85;
}

/* Тот, кто подключился только что: пометка нужна ровно до того момента,
   пока ему не выдали права. */
.list-item-value.badge-new {
  padding: 2px 8px;
  border-radius: 999px;
  background: rgba(50, 213, 131, 0.16);
  color: var(--green);
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

.lock-dots {
  display: flex;
  gap: 12px;
  height: 16px;
}

.lock-dots span {
  width: 14px;
  height: 14px;
  border: 1px solid #4a5361;
  border-radius: 50%;
}

.lock-dots span.filled {
  background: var(--accent);
  border-color: var(--accent);
}

.lock-pad {
  display: grid;
  width: 100%;
  grid-template-columns: repeat(3, 1fr);
  gap: 10px;
}

.lock-key {
  min-height: 62px;
  border: 1px solid var(--line);
  border-radius: 16px;
  color: var(--text);
  background: var(--panel-2);
  font-size: 23px;
  font-weight: 700;
  cursor: pointer;
}

.lock-key:active {
  background: var(--accent);
  color: var(--accent-ink);
}

/* Ряд из «←» и «0»: стирание слева, ноль в середине — как на клавиатуре
   телефона, чтобы палец шёл на привычное место. */
.lock-key.wide {
  grid-column: 1;
}

.lock-message {
  margin: 0;
  min-height: 18px;
  color: var(--muted);
  font-size: 12px;
  line-height: 1.45;
  text-align: center;
}

.lock-message.bad {
  color: #ffb4ab;
}

.lock-pad.shake {
  animation: lock-shake 380ms ease;
}

@keyframes lock-shake {
  0%, 100% { transform: translateX(0); }
  25% { transform: translateX(-7px); }
  50% { transform: translateX(7px); }
  75% { transform: translateX(-4px); }
}

/* ── Конструктор площадки ───────────────────────────────────────── */

.builder-tabs {
  /* Вкладок больше, чем влезает в строку: переносим, а не режем. */
  flex-wrap: wrap;
}

.builder-variant {
  margin-top: 4px;
}

/* Увеличение — рамка со прокруткой: увеличенный план из неё не выпадает. */
.builder-zoom {
  overflow: auto;
  -webkit-overflow-scrolling: touch;
}

.builder-tools {
  margin-top: 10px;
}

/* Что сейчас взведено. Плашкой, а не серой строкой: это СОСТОЯНИЕ — пока оно
   держится, каждый тычок по площадке ставит предмет. Серой подсказкой мелким
   шрифтом оно было неотличимо от пояснений вокруг, и человек про взвод забывал,
   а потом удивлялся мишеням, которых не ставил. */
.builder-armed {
  margin: 8px 0 0;
  padding: 8px 10px;
  /* Подкраска — теми же rgba, что и остальные акцентные плашки журнала:
     color-mix здесь больше нигде не встречается, и вводить его ради одной
     плашки значило бы поставить вид этой плашки в зависимость от возраста
     браузера на чьём-то телефоне. */
  border: 1px solid rgba(255, 90, 54, 0.35);
  border-left: 3px solid var(--accent);
  border-radius: 10px;
  background: rgba(255, 90, 54, 0.12);
  color: var(--text);
  font-size: 0.86rem;
  line-height: 1.35;
}

/* Крестовина сдвига: кнопка под палец в перчатке, а не под курсор. */
.builder-pad {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 8px;
  margin: 8px 0;
}

.builder-pad .button {
  min-height: 46px;
  font-size: 18px;
  font-weight: 700;
}

.builder-tile-row {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-bottom: 10px;
}

.builder-tile-row .button {
  flex: 1 1 auto;
  min-width: 92px;
}

.builder-tags {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
  gap: 2px 12px;
}

.builder-brief h3 {
  margin: 0 0 4px;
  font-size: 17px;
}

.builder-brief-list {
  margin: 6px 0 0;
  padding-left: 18px;
  color: var(--muted);
  font-size: 13.5px;
  line-height: 1.5;
}

.builder-grid-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 13px;
}

.builder-grid-table th,
.builder-grid-table td {
  padding: 4px 6px;
  border-bottom: 1px solid var(--line);
  text-align: left;
}

.builder-grid-table input {
  width: 100%;
  min-width: 56px;
  min-height: 38px;
  padding: 4px 6px;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: var(--panel-2);
  color: var(--text);
  font: inherit;
}

.builder-row-active td {
  background: rgba(255, 90, 54, 0.1);
}

/* ── Печать ведомости ───────────────────────────────────────────── */

/* Ведомость — A4 лёжа: она такой и нарисована. */
@page {
  size: A4 landscape;
  margin: 8mm;
}

@media print {
  /* Лист ведомости печатается один, без приложения вокруг. */
  body.printing .app-shell,
  body.printing #sheet-host,
  body.printing #lock-host,
  body.printing #toast-host {
    display: none !important;
  }

  body.printing #print-host {
    position: static;
    display: block;
    width: auto;
    height: auto;
    overflow: visible;
    opacity: 1;
  }

  /* Лист занимает страницу целиком, но не больше неё — и высота здесь важнее
     ширины. Лист нарисован в пропорциях 1123 × 794; растянутый по ширине
     печатного поля (297 − 8 − 8 = 281 мм) он выходит 199 мм высотой, а поля
     этого всего 194 мм (210 − 8 − 8). Пяти миллиметров хватало, чтобы принтер
     выдал второй лист — белую полосу низа, на которой ничего нет.
     Поэтому задаём высоту, а вписаться в неё SVG умеет сам: viewBox и
     preserveAspectRatio по умолчанию оставляют пропорции и ставят лист по
     середине. 192 мм — те же 194 минус запас на округление у принтера. */
  body.printing #print-host .roster-sheet {
    display: block;
    width: 100%;
    height: 192mm;
  }

  /* Членская карточка печатается ОДИН К ОДНОМУ: 85,6 × 53,98 мм по ISO 7810,
     как банковская. Её вырезают и ламинируют, и «во всю страницу» тут значило
     бы выбросить карточку.

     Размер задаётся обеим сторонам, а не одной: у рисунка своя пропорция, и
     если она разойдётся с бумагой на сотые доли, лучше пусть это увидит глаз
     на готовой карточке, чем линейка на вырезанной.

     Страница остаётся A4 лёжа — она одна на весь журнал. Сколько карточек
     влезло, столько и печатается: у кого два пистолета, у того две карточки, и
     обе выходят на одном листе. */
  body.printing #print-host .idcard-svg {
    display: block;
    width: 85.6mm;
    height: 53.98mm;
    margin: 0 0 6mm;
    break-inside: avoid;
  }

  /* Лист схемы — та же бумага, что и ведомость, и высота здесь так же важнее
     ширины: растянутый по ширине печатного поля лист выходит выше поля, и
     принтер выдаёт вторым листом белую полосу низа. */
  body.printing #print-host .drill-sheet {
    display: block;
    width: 100%;
    height: 192mm;
  }

  body.printing #print-host .drill-sheet + .drill-sheet {
    break-before: page;
  }

  /* Перенос — только МЕЖДУ листами. Раньше он стоял на каждом листе, включая
     последний: Chrome такой перенос после последнего листа не выполняет, а
     Safari на телефоне добавляет пустую страницу. Правило «лист после листа»
     после последнего не срабатывает вообще. */
  body.printing #print-host .roster-sheet + .roster-sheet {
    break-before: page;
  }

  :root {
    color-scheme: light;
  }

  html,
  body {
    background: #fff;
    color: #000;
  }

  .topbar,
  .tabbar,
  .toast,
  .no-print {
    display: none !important;
  }

  /* А это — наоборот: на экране скрыто, на бумаге нужно. Кнопок удостоверений
     на печати нет, и без списка клуб человека на листе не назван нигде. */
  .shooter-card-top b.only-print {
    display: block;
  }

  .screen-host {
    max-width: none;
    padding: 0;
  }

  .panel {
    border: 1px solid #999;
    background: #fff;
    box-shadow: none;
    color: #000;
    break-inside: avoid;
  }

  .panel p.panel-note,
  .list-item-sub,
  .stat span,
  .section-kicker {
    color: #444;
  }

  .list-item,
  .stat {
    background: #fff;
    border: 1px solid #bbb;
  }

  .list-item-value {
    color: #000;
  }

  /* Схема на бумаге: тёмное поле плана съело бы весь картридж и читалось бы
     хуже, чем на экране. */
  .stage-host {
    border: 1px solid #999;
    background: #fff;
  }

  .stage-svg {
    height: auto;
    max-height: none;
  }

  .stage-ground {
    fill: #fff;
    stroke: #999;
  }

  .stage-grid {
    stroke: rgba(0, 0, 0, 0.12);
  }

  .stage-grid.major {
    stroke: rgba(0, 0, 0, 0.28);
  }

  .stage-axis {
    fill: #444;
  }
}

/* ── Схема: мишени своей формы ──────────────────────────────────
   Цвет мишени приходит стилем прямо из данных: правило отсюда его не задаёт,
   иначе оно перебило бы выбранный цвет и на глаз ничего бы не менялось. */
.stage-face.shaped {
  /* Запас на случай, если рисующий забыл посчитать толщину. Настоящая приходит
     стилем прямо из данных (Stage.lineWidth) и это правило перебивает: толщина
     обязана считаться от размера мишени, иначе на маленькой карточке линия
     съедает поля, а зоны слипаются в овал. */
  stroke-width: 1.2;
}

/* Зоны бумажной мишени: тонкой линией, чтобы читались, но не спорили с силуэтом. */
.stage-zone-line {
  /* Тоже запас: настоящая толщина приходит из Stage.zoneWidth. */
  stroke-width: 1;
  fill: none;
}

/* Стойки бумажной мишени: две ножки с основанием, как на площадке. */
.stage-stand-leg {
  fill: #8a6a4a;
  stroke: none;
}

/* Выбор цвета мишени: сами цвета, а не их названия — так их и выбирают. */
.stage-colors {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

.stage-swatch {
  width: 34px;
  height: 34px;
  padding: 0;
  border: 2px solid transparent;
  border-radius: 9px;
  cursor: pointer;
}

.stage-swatch.active {
  box-shadow: 0 0 0 2px var(--green);
}

/* ── Схема: вкладка «Что есть» ──────────────────────────────────
   Список того, из чего собирается схема. Карточки узкие: их тридцать с лишним,
   и смотрят их глазами, а не читают подряд. */
.parts-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  gap: 10px;
  margin: 8px 0 16px;
}

.parts-card {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 10px;
  border: 1px solid var(--line);
  border-radius: 12px;
  background: var(--panel-2);
}

.parts-card h4 {
  margin: 0;
  padding-right: 22px;
  font-size: 15px;
}

.parts-card .field-hint {
  margin: -4px 0 0;
  font-variant-numeric: tabular-nums;
}

.parts-num {
  position: absolute;
  top: 8px;
  right: 10px;
  font-size: 12px;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}

.parts-row {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
  align-items: flex-end;
}

.parts-shot {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 3px;
}

.parts-shot span {
  font-size: 11px;
  color: var(--muted);
}

.parts-icon {
  width: 62px;
  height: 62px;
  border-radius: 8px;
  background: var(--panel);
}

.parts-icon.face {
  width: 58px;
  height: 74px;
}

.parts-ground {
  stroke: var(--line);
  stroke-width: 1;
  stroke-dasharray: 4 3;
}

.parts-tint {
  display: flex;
  align-items: center;
  gap: 5px;
  flex-wrap: wrap;
}

.parts-tint span {
  font-size: 11px;
  color: var(--muted);
}

.parts-dot {
  display: block;
  width: 14px;
  height: 14px;
  border-radius: 4px;
  border: 1px solid;
}

.parts-plan {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: auto;
  padding-top: 8px;
  border-top: 1px dashed var(--line);
}

.parts-plan span {
  font-size: 11px;
  color: var(--muted);
}

/* Пустая ячейка в ряду цветов — «как обычно»: без неё выбранный цвет нечем
   снять, и занятие уже не вернуть к обычным цветам. */
.stage-swatch.plain {
  background: transparent;
  border-style: dashed;
  border-color: var(--line);
  color: var(--muted);
  font-size: 16px;
  line-height: 1;
}

/* Щелчок мимо окна его не закрывает — окно вздрагивает: видно, что нажатие
   услышано, и видно, что закрывают крестиком. */
.sheet.nudge {
  animation: sheet-nudge 220ms ease;
}

@keyframes sheet-nudge {
  0% { transform: translateX(0); }
  25% { transform: translateX(-5px); }
  55% { transform: translateX(5px); }
  100% { transform: translateX(0); }
}

@media (prefers-reduced-motion: reduce) {
  .sheet.nudge {
    animation: none;
  }
}

/* Строка в списке «не закончили регистрацию»: имя, язык, готовый текст и две
   кнопки. Не строка-кнопка, а блок: тут не выбирают, а делают. */
.list-row-block {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 12px;
  margin-bottom: 10px;
  border: 1px solid var(--line);
  border-radius: 12px;
  background: var(--panel-2);
}

.list-row-block .panel-note {
  margin: 0;
  white-space: pre-wrap;
}

.list-row-block .field-hint {
  margin: -4px 0 0;
}

/* Поле кода правил в списке причин снятия. Узкое нарочно: код короткий, а
   рядом стоит текст причины, которому место и нужно. Не сжимается, иначе на
   узком экране «10.5.1» переносится по цифрам. */
.dq-code {
  flex: 0 0 5.5em;
  min-width: 5.5em;
  font-variant-numeric: tabular-nums;
  text-align: center;
}


/* ── Схема на весь экран ─────────────────────────────────────────
 *
 * За компьютером схему правят мышью, а план занимает меньше половины окна:
 * полметра на экране — это два пикселя, и мишень мышью не поймать. Здесь
 * чертёж занимает всё окно, и двигать его мышью можно точно.
 *
 * Слой 30: выше журнала (6–7), но ниже ворот (40) и окна (50). Ворота, если
 * они появятся, обязаны накрыть и это — иначе человек останется в схеме, из
 * которой некуда выйти.
 */
.stage-full {
  position: fixed;
  z-index: 30;
  inset: 0;
  display: flex;
  flex-direction: column;
  gap: 10px;
  /* Отступы с оглядкой на вырез и строку состояния: на телефоне кнопка
     «Свернуть» стояла ровно под часами, и нажать её было нечем. */
  padding: max(12px, env(safe-area-inset-top, 0px)) max(12px, env(safe-area-inset-right, 0px))
    max(12px, env(safe-area-inset-bottom, 0px)) max(12px, env(safe-area-inset-left, 0px));
  background: var(--bg);
}

/* Слой, открытый ИЗ ОКНА карточки, обязан быть выше окна.

   У окна z-index 50, у слоя 30: приложенный к тому же хозяину, слой уходил под
   окно, и кнопка «на весь экран» выглядела бы нажатой впустую — ничего не
   происходит. Выше окна, но ниже сообщений (60): сообщение о сохранении должно
   быть видно и в полном экране. */
.stage-full.over-sheet {
  z-index: 55;
}

/* На бумаге слоя нет: он перестаёт быть слоем и печатается тем, что в нём
   лежит, — чертежом. Оставленный слоем, он давал закрашенный лист во всю
   страницу с кнопкой «Свернуть» поверх. */
@media print {
  .stage-full {
    position: static;
    z-index: auto;
    inset: auto;
    padding: 0;
    background: none;
  }
}

/* Полоса с кнопкой и подсказкой — про экран, а не про схему: на бумаге её
   быть не должно. */
.stage-full-bar {
  display: flex;
  align-items: center;
  gap: 12px;
  flex: 0 0 auto;
}

.stage-full-bar .field-hint {
  margin: 0;
  min-width: 0;
}

/* На широком экране кнопки уходят в колонку сбоку, а схема занимает всё
   остальное. Столбиком сверху вниз они съедали высоту, и от схемы оставалась
   полоска: ради полного экрана его и открывают, чтобы видеть схему. */

/* Кнопки в слое — мельче: их там много, и каждая в полстроки отнимала место у
   схемы. */
.stage-full .button.small,
.stage-full .segmented button {
  min-height: 30px;
  padding: 5px 8px;
  font-size: 11px;
}

.stage-full .builder-pad .button {
  min-height: 32px;
  font-size: 14px;
}

.stage-full .button-row,
.stage-full .builder-pad {
  gap: 5px;
  margin-top: 6px;
}

.stage-full .field-hint {
  margin: 2px 0;
}

/* Слева чертёж, справа палитра и правка — двумя свёртками. Раскладывать их
   по колонкам поодиночке, как было, значит отдать строки на откуп
   автораскладке: она заводила чертежу свою строку в пол-окна, а правке
   выбранного — следующую, ниже нижнего края экрана.

   На УЗКОМ экране свёртков как бы нет: display: contents отдаёт их детей прямо
   в гибкую колонку слоя, и телефон раскладывается ровно так, как раскладывался
   до свёртков. Ни одного правила ниже переписывать под них не пришлось. */
.stage-full-main,
.stage-full-side {
  display: contents;
}

/* На весь экран уезжает не только чертёж: там теперь и ставят, и красят, и
   убирают. Значит расти обязан ЧЕРТЁЖ, а палитра и кнопки правки — остаться
   при своих размерах и прокручиваться у себя, если не влезли. Растянутые
   наравне с холстом, они сжимали схему в полоску.

   Потомком, а не ребёнком: со свёртком колонки они уже не дети слоя, а внуки,
   и правило по «>» перестало бы их находить — молча, без единой ошибки. */
.stage-full details.advanced-block,
.stage-full .builder-tools {
  flex: 0 0 auto;
  max-height: 32vh;
  overflow-y: auto;
}

/* А растёт тот холст, который сейчас видно: в объёме — свёрток вида, в котором
   он лежит, на плане — сам чертёж. Растянув оба, слой отдавал половину окна
   спрятанному. */
.stage-full:not(.stage-full-3d) .builder-viewbox {
  flex: 0 0 auto;
}

/* Холст занимает всё, что осталось: ради этого полный экран и нужен. Обычные
   46vh с потолком в 420 пикселей оставили бы половину окна пустой. */
.stage-full .stage-host,
.stage-full .builder-viewbox {
  display: flex;
  flex-direction: column;
  flex: 1 1 auto;
  min-height: 0;
  height: auto;
  max-height: none;
}

.stage-full .stage-svg,
.stage-full .stage-3d {
  flex: 1 1 auto;
  min-height: 0;
  height: auto;
  max-height: none;
}

/* Переключатель видов внутри полного экрана — строкой, а не растянутый на
   всю ширину: рядом с ним больше ничего нет, и растянутый он читался бы как
   заголовок. */
/* Три кнопки одной строкой: «Сверху», «Объём» и «На весь экран», в этом
   порядке и одинаковые на вид. Порознь они стояли лесенкой — широкий
   переключатель и маленькая кнопка под ним, — и на разных вкладках по-разному.
   Ряд теперь один и тот же везде: и в конструкторе, и в мастере. */
.builder-viewrow {
  display: flex;
  align-items: stretch;
  gap: 6px;
}

.builder-viewrow > .segmented {
  flex: 2 1 0;
  min-width: 0;
  margin: 0;
}

/* Переключатель занимает две доли, «на весь экран» одну: кнопок в них две и
   одна, значит каждая выходит ровно в треть ряда. */
.builder-viewrow > .segmented + .segmented {
  flex: 1 1 0;
}

.stage-full .builder-viewrow {
  flex: 0 0 auto;
}

/* Широкий экран: кнопки уходят в колонку сбоку. Правила ниже идут ПОСЛЕ
   гибкой раскладки нарочно — они её и заменяют, а стоя выше проигрывали бы ей
   при равной силе, и чертёж по-прежнему уезжал бы под нижний край.

   Сетка ровно из двух ячеек, и обе заняты свёртками. Раньше сюда попадали все
   узлы слоя по одному, а строки им заводила автораскладка — по порядку. Полоса
   выхода занимала первую, переключатель и палитра — вторую, строка взвода и
   чертёж — третью, правка выбранного — ЧЕТВЁРТУЮ. Чертёж высотой почти в окно
   растягивал третью строку, и четвёртая начиналась ниже нижнего края: отсюда и
   пустой провал под палитрой, и панель, которой не видно и не достать. */
@media (min-width: 900px) {
  .stage-full {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(260px, 26vw);
    /* Вторая строка — ровно то, что осталось от окна. Отсюда и берётся высота
       чертежа: не из вычитания выдуманного числа, а из самого окна. Прежние
       «100vh минус 190 пикселей» держались, пока полоса сверху была ровно
       такой высоты, и переставали держаться от каждой лишней строки в ней. */
    grid-template-rows: auto minmax(0, 1fr);
    column-gap: 14px;
  }

  /* Полоса выхода — во всю ширину, над обоими столбцами. */
  .stage-full > .stage-full-bar {
    grid-column: 1 / -1;
    grid-row: 1;
  }

  /* Слева — чертёж и свёрток вида: в объёме холст лежит внутри свёртка, и
     разлучать их значило бы отправить объём в колонку кнопок. */
  .stage-full-main {
    grid-column: 1;
    grid-row: 2;
    display: flex;
    flex-direction: column;
    gap: 10px;
    /* Без этого колонка не даст чертежу сжаться: гибкий элемент по умолчанию не
       становится меньше своего содержимого, и рисунок, выросший от увеличения,
       вытолкнул бы низ колонки за край окна. */
    min-height: 0;
  }

  /* Справа — палитра, строка взвода, правка выбранного. Колонка прокручивается
     ЦЕЛИКОМ и у себя: полоса прокрутки доходит до последней кнопки, а не
     обрывается там, где кончилось окно. */
  .stage-full-side {
    grid-column: 2;
    grid-row: 2;
    display: flex;
    flex-direction: column;
    gap: 10px;
    min-height: 0;
    overflow-y: auto;
    /* Докрутив колонку до конца, не утягиваем за собой страницу под слоем. */
    overscroll-behavior: contain;
  }

  /* Внутри колонки высоту никому не режем: режет её сама колонка. Потолок в
     треть окна был нужен, пока палитра и правка стояли в общей гибкой стопке
     рядом с чертежом; здесь он дал бы вторую полосу прокрутки внутри первой. */
  .stage-full-side details.advanced-block,
  .stage-full-side .builder-tools {
    max-height: none;
    overflow: visible;
  }

  .stage-full .stage-svg,
  .stage-full .stage-3d {
    height: 100%;
    max-height: 100%;
    width: 100%;
  }
}

/* Ручка поворота в объёме. Свои правила, а не правила плана: у плана холст
   размечен в пикселях, и его «stroke-width: 2» здесь означает два МЕТРА —
   ручка выходила оранжевым кольцом на треть площадки и закрывала схему.
   Толщину и радиус здесь задаёт сам рисовальщик, в единицах своего холста. */
.stage-handle3d-line {
  stroke: #fff;
}

.stage-handle3d-dot {
  fill: #fff;
  stroke: var(--accent);
}
