@font-face {
  font-family: 'Russo One';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url(data:font/woff2;base64,d09GMgABAAAAABGMAA8AAAAALIAAABE1AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGhYbjGYcGAZgAGwRCAq1QKggC4FIAAE2AiQDgwwEIAWDXgeJHgwHGxAkJeOYpbgdQFRRe/SICk645v8/JXAyRGhdsNPvhiCKAbaFxmMRguabgGHoeBSNpZcH3Xo//iyYIV0QTnmKLLziIZveq5+f79xi/+vR7e0FrDz90bVvxZQjJJn163Gmbz/cOLtfVrBIKLJ1rCQHuiLLLUtlALnAPLmmXyBOOd9yikAjdG5Gz4qd39+jU189GpQhxagJ9gVyMU5bNcZr//aU5bHBW3jjdpOx4FroFTK4BmUu23f1OCvjZ/zGi6iDS1HHGHQtk7WASDjYbb8STzEMAqbEe+fAPWSIj4xh2/YmqpVQOsVs/Pw8DpePw8UNC3bgmsAJTOA5TiAbHqF2bw3YhuAmkjqrMI7nMgKIp/aq1Ksy3B2L2dQAFYpU2tP57ITpUvEXSuURyxjgMBDYYMMkAKL4T9Oa3clCHv2qoxaHcKdQBmM2k/onO5e7nlwtOXqZpe4utTm6Ea00hbBI1FkkQhpsWatqYitOxBnOXreMrX/zVamHxeHeAKU29VdcAgFw0BTIlOHubwDGAhwIE591K4AOA5wVO1D+XTN5h+FTGC1olINV31zr2bAbPFZL0/ev2GWu9x7Kz/lg+CTXtk9yMI+XbyTJY3ugX2/aLrU975nqejV8d/6f30eZ83PpnD6HzjPmMJlFcaPuUj/6C9Pb9d1mNtwuemzg/3MIa6GgBw04eGjCx2RCImISUnIKaoCBkYmZhRVk5+Di5oH4+AUEhUSkZeUVFJWU1dQ1tbR19Q1NzSytbdCwVteyRqGUuspa9cay0LBmF9+1lBUzadDYZmAVcYKYqn3Zo61qBF/J6hnKjEw2N0pUak+s4PGdrtHFUulws3a6Vop8in/Thoa2UK51zeqYP6fIanFaEUdzM00Few+Xw1/EDHgcZgosdQwNgJEFXrh5WGXYWCIQRtqMFP3oqgn12Fpxc7w6Qu62l8awdKjhQ+fvCHAX7LFWkQhExcSTWNhAKmlVMpBNrpFXUFRSToVShZq6hqaWts6oW/oZKDPkjGBsMpr2SC2tbDbUNa/MrVI0QaU7uAyJdzNOLkKxKxZstwpev6T7QZuA4zUDVu0T+RBfpulxerFCWajgiHQh70ciS8R+FBYM2oflatzja4lhpAqCOcRn3h7CHnMg82PGoP1E02fNhG8iibKY+DzZDDHEdQAAQ3odDAHPIYZFpRDTxNoQ1i1sbkKwnvEWXbNwTx4IAAQCgQAAABcJEFAwzOqY4d+m7tvMeR0zUvgdQKt4drKAenBLwInCBfoA8r9TsNpWMf61BoDfxk5vUx2kgdfc6Iv/fwGWfnHmhOEcANTUfRCwECLQ0JmOQreHZCRtKv4oSvbzn3Nmx2u9jtv1qiM/eL5Kpml2vvbb+S/0V8vd3N27c9edj37UCHhrBNIXpuhneZoPzv2osTGMt1fLZHwGJmsQFCNrkKs6VWHLogpUJYEGKwjzXgmTq9NMZeBdZ3SEwGoYzzD/hknLki3qIg/px0xJSxi+p5TXaRSGKUO2LFqy8bLgxY5wjro2lK0xtnEkmT3D+j8UlWqcl/qBIKuY5iyfQ6EQ6vkFsL4M3W4ALEKrUhOa/BwSDo3TOn7T+nDwmsnjAo5/wE912UhS3UDUuZ7LejXFlskD454135IKEC6eK44l2KPm8k3SnbfEgysB6kNxXfXMy3trw//kU/gzqL8QqbqZoVx/i+jNyHMkGv0AJQNnLdpnoltzGZmOKLatZ94BH0iee/Yne3SYe/W0z/jtNCu2okRLS3pqn7U7j81jCaQKweW104MWt5d2xre8Fep7znnPcc0xW1qp9NbbvdhfEgmK6+RPmWLMYsU0g+KAI+FcxjjiWKLUwm4fblY9bMAz0LlaNzxPKjOJbAuWb//WgG9EceQC1nLLWwsOaQ4uXenc+FTlOcTmV9GccBqdfT0zvSVVd/lQ9CZq4m4kY2aSStjfkRVmJOLIZ51suiUV7/LgZtzRXR/gJTUmaxu2nOwTcDTnHksN9vM5DWykoYQiQCWJdYjDPUW+1tD7fngE5pvBIyHZEsXKFb2EF8eEv7UkpQ/BcKnnieF0+TJwh1lJRa4Q0Vwil6TFUcBpW8JHHviqrDZ5c1HFdVLFWgxPmS03G/vevT4nZ+Jo2gtpkZX9djVT4rDCjlKrIy9CvkqMPMRTkvwoyWPZ7M2NfCYOOH56l9HMPtE8yqo4q7oWKRRtTqwOxYuMfcZesfd2OsXMQkyWjbNw8Em02ELXD9d8cjEU4jnryfqK11e76/OziCQCVitiVQ4rb5LV64nmTAnY+xCSRxZN0xIQ6nArko4LjXDw70OF0+h8Zlpy2HvX4velhKl6aHxHfI1Uiy+Oy7mCFkfRdJjMOW6LDplFsK6VuIxyMU/5c/oGG0ujjHHhZspuaxMpWjD1hBdzUNcZkSM9QilNS+Ue3nI24wfxSKLmAEP5BSNlAAmhSJbtt/Ckpy1G62QFwhsx5tnnhydv0y0LtMl8OPUelZzgsPQ4nVHUeG7P8Ug0ptWW1nOjtlesed40Gr/Zd9VVMid0+ayAE1SuLIimz2I3tpKXD53aCfLl9LEPdjDCAJfbDAciC6LF6SF3y5DGzUID3xJ2P+CUS2n7W5hLZDPEnRILtsrTpHwFSxJjY24h2YzLSBa7Vvn6lY6Sxtck+9fmh1My1B1aMUI6eOKSS/WwSTL3WXqBOKSQR+1NnUqx3ow3cDmMICF1/oFjkK+XYDYdzK9eyw4Ytl4s3w7N8Cy/jwcXcHsfnEzLEO/L8vOMLOtLPtF23JInPnya7d2/JPH6L6r4G+Av5qUwGvfWUJ4OJovp0UjzsqzTEnNJuiheFcmKrsWmWOFV2Q0h4ksUrexI0QixjkAT9QO7p9ts7uafbm9/+imRR6hhuji9QyMtmm3saozlo+GhnXF0l0bSCL9fJebnS3SIeQg94AWBiszioVkuc/TjMc+pYMtTojAgiHVmv/6F/2ydZYfRbH7tmbAqxuRfVONwi7kqUJUaTfYyTwr7cpRumir0pSaB2oe70sfje59YgxAmGCF4fnQ2yV3XEDQQH4kJxkh/v7uj9Z/yf0I7v1zLeRk0F7f96/sTeMrDMWuE9nNvdsuHtYKO+AgrWC608NnwC5VZ5hF0Q2WaUvUV5Vz3vm4lvLi++v6W2b+43fPdCUwWM58poQndgJRrpt9OJ74vKVf/ZxYY96smlT6/wXHb0Fut5m+YOhkKmhhI2odElyGk7FcQgqLJnd3mZjPKKm+vnaNt6H0kjNGp0W8FTNxYl4yQJEoIJexfQd/krPQKWqJcWyiXRC9+XWuKW71vYJy+ZTg58hlw4nSQv/stFxPFFbrizJPrVO9VhDUM5+GphBw+u8VyoZglbLHrD2F3humLRtJwCIXE7xLcyItr7CzNLf9ATK9vNvwnC73k1NRJ2dftB38PTuF78j9PHXbBxZBnpt5LXlwj/ZP4Xtiz2XtL/CyxhfjkwGrryZMB6CW628A1b7ME7t1DSDI+fy9IgMXtNuDFGuiNTtwNRZMDY6KnXDukkNErCCnf/QtbZX82z/xLuJOkfyI2i5+QohFyFhiL7cWk2H6ZqwyXIAgJvBDle/fXgo8mlZ/afmtTvN++PQWppiXg+ZZFVnxTW8/7m4363Gzu1hO9RtKYVlHlxEfgxKrAJIPrAQ2cZY1lkyGmJVF/uiguHbUaaZ1693eZd/hoeGaSRBpdl8hERigxEEQvfAl25xV6qJE0HdPF6Q0I5Yrt4eX+Wn3/tywULpA3LDviKRumTMeFPTq8exjsJb97HL/Jrqnwmua6Yl2Vo7M8k68L0a6cJSzaxWQx0/nBfZvPs3uWGY3LthzZ9X+3Vp80EClt5fvcxk0WH51xGb4Y7WTLJpfpr6ivr/iueB5LaWeXiap7mPbm6OcY9m3gOOeBD016+MvE/RwHpxzq9qWm6u5jQoAvsZtqzCNMFrOrQW+l7luK1gpr3beEH+M8ES9Wg/ZWTMGAY5zJW7wVkSHKlk3ZhiKLXyxjEyHcy3F5Uh7iN/e+zh5bhRbSlKrxFit/qUWmQZb3qTIs9/RJnk0D+R9qOS7XmufH/rxc64/83Ka5+oFNniT9vQzLPpVsDWQqWMkE1iXT1isx3PydU/HIh9HO8qWm6oyeljkBvuQlo3HZ8OssbI0bV2P9SGJ3coheetiyE68YXHKgQsFq7K/Ntf54fWSOnpj+BmClkl92cw9p2Cd/UmOvSIejc0YjvNXitpyjsXr8sOZ983E1pmFvtR+rsUI+qwGq9jmb5/e27xTNcXbvK/fLwtI4NfaSUL5C89acJWxoP8eNM1nM4a0S9kVWSzGhq8E0aoLdMRLyHmqf+X+ccI6Gtq1e7B303PLAhXM5lZRbeOjpa+/9Ne6oe6puPPCgttpcC5f7YPEKCUsrLnM/GS45702OK44pjrNYOQ7FoGvBC3H+EdUpld/S9/b8hTcSFrT3wU+nLH64tyIyCLe0DT0hrMb9z1Bt61oKf8JkQaaErTW4Rw1v7BjaoI5lcYPvK4vF7RIOr+h5dy9VMSksE2sX1w/65ZVFjjEZsLxnTakDq3Fd9n1l6dpPUnvOs/ZztNDpyww6g9yrMeLM9bqx3QHJ1SkDXx5zn9KZER84pOO42oSwWlbwpwcb2gfGLad600pyTNVZvv7KfMTlpwheHz65B40Yb1xCuYv3kb3nrpWBp5ATsTVHap0v51BV2x7+yruldBfmOiMLuaAzmJnyWOd9h16PJL5UMvd/VO+O7xlnQSqNe5Mz77Aw614Y2yo85QEsYtBd3EJ1XdHMWmA519ZJud0Ho2isDVHsp8/vqqSvvoARtbymFu9a+ldddRk3eJD2p8ERPBLEaoyP4MGrRZGKlpDd4YlTG3D89qunCtw+vOWr96GxteUDfDj7/qO7zNxZPxFTtn7KBlNp6cRnoyz9ISiMo1qOBMPVaTX8l2zoMdQE5KlXBoivW3bH34jnkVBl2cUwKWn+zSrzw1P6vuaNuPb/13cw6JqKFf0nAMC7fSs/Ep4Q/j/8V8xEy+diQAMKZv2ps9K8vE7c5VOZb2OzMeACb15SHSKeOTDViPhOgHuxBebKZnamFzHNlv5aSkWYuqL/9na/MeHTvU4jxDm4Mw6MRr8KGwvTlXjOQZO/wdRHW5NpiGAbTLFgos6WVbR7Jrs36TEwXy91gvWykJp9aAEicZ4HvSp+nl0pdhjcBJpvob0dNzeHyWFqCeLdH2pY0l9g7WBNBlBKS9DcHlit0808o6jcV8gbodkSQzmaHlSzXpZMXoI2u0yeoN2ksTTdOEeizQxtxGsHNYY+OUHJjmg803QwR09uEHHQS1wkjgAgM0bD5ihJZLQ8sBEMWixuYPZGZhKAG704mE6Y6ne6QhM3TtfYwEO2UZpuMY/+9B4sxE9N6scw6u09+UmUe+lXOy8uA3prilxe6a6gxzGdBGu3FNV1vXt6paVbHhw4R3Qw9C6mjadh67+A6VGwNuDl4uEpqDqiDxE9vKNvdaF/X/Rv7EXvCrYEt3zaR+A8yMzWlCnXND75dsE/ubY6AO3gKK1eTryTOYAV5QO3HJ57VVne5cU7/Gn5TxdvzutmB+40fSQO9IsofAXUVEf3DmW2Yc2dhsfyvM038ioqt/w/zktNKKqmG6a07N+d/W+P6wmEIrFEKpMrlCq1RqsDeoPRZLZYIW6zO5wut8eLfP5AMBSORGPxRDKVBkAIRlAMJ0iKZliOF0RJVlRNN0zLdlzPl/9/+mkKmKHKggCEYATF4vRX0AGGsh5GDIADACCjXYAwgmJx+ivQACAEIygWp78CDwBCMIJicUqgEdSFoGj9TgFCMIJicforuAFACEZQLE5/BW8AEIIRVHFEh/6Mxi0AEIIRFIvTXwAjyqoLMsm7YaHwRzbDNg==) format('woff2');
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
/*
 * Токены, а не ad-hoc значения.
 *
 * Палитра — из мира «Депо» (`docs/02`): тёплый затёртый бетон, чугун
 * отбойника, сурик. Нейтральные цвета уведены в тёплую сторону намеренно:
 * серый «из коробки» рядом с песочным полом доски читается как грязь.
 *
 * Шрифт системный, и это решение, а не лень: стенд обязан открываться одним
 * файлом без единого запроса (`docs/01` §7), а веб-шрифт — это либо запрос,
 * либо 30-80 КБ в бюджете. Характер набирается кеглем, весом и разрядкой.
 */
:root {
  color-scheme: light dark;
  --bg: #f3efe6;
  --fg: #221e19;
  --muted: #6e6558;
  --line: #ded6c6;
  --wall: #2b2620;
  --panel: #fbf8f1;
  --accent: #b4552f;
  --good: #3f7d4e;
  /* Фон доски и цвет мела задаёт арт-пак; это значения на случай процедурного. */
  --board: #ffffff;
  --chalk: #221e19;
}

/* Системная тема — по умолчанию; явный выбор хозяина страницы сильнее её. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme='light']) {
    --bg: #14110e;
    --fg: #ede6d9;
    --muted: #9a9084;
    --line: #2c2620;
    --wall: #d8cfbf;
    --panel: #1d1913;
    --accent: #e0783f;
    --good: #6cbd82;
  }
}
:root[data-theme='dark'] {
  --bg: #14110e;
  --fg: #ede6d9;
  --muted: #9a9084;
  --line: #2c2620;
  --wall: #d8cfbf;
  --panel: #1d1913;
  --accent: #e0783f;
  --good: #6cbd82;
}

* {
  box-sizing: border-box;
}
/*
 * `hidden` обязан скрывать.
 *
 * Любое правило с `display` перебивает атрибут, и это ловилось уже дважды:
 * пузырь реплики оставался на экране после автоскрытия, панель тележек —
 * после конца раунда. Одно правило закрывает весь класс ошибок.
 */
[hidden] {
  display: none !important;
}
body {
  margin: 0;
  background: var(--bg);
  color: var(--fg);
  font:
    15px/1.55 system-ui,
    -apple-system,
    'Segoe UI',
    Roboto,
    sans-serif;
}

/* Один внешний отступ, заданный один раз — чтобы страница не уезжала вбок,
   как у ref-rr-ts, где она всегда прокручивалась на 12 px. */
.wrap {
  max-width: 760px;
  margin: 0 auto;
  padding-block: 20px 40px;
  padding-inline: 16px;
}

.screen {
  display: grid;
  gap: 18px;
}
.screen[hidden] {
  display: none;
}

/* ─── меню ─────────────────────────────────────────────────────────────── */

/* Заставка: настоящая доска, обрезанная полосой. Первый кадр обязан
   показывать, во что играют, а не только как это называется. */
.hero-frame {
  /* На коротком ноутбучном экране (800 px) заставка съедала ровно столько,
     что кнопка второго режима уходила за нижний край — замерено. */
  height: clamp(80px, 13vh, 170px);
  overflow: hidden;
  border-radius: 12px;
  border: 1px solid var(--line);
  background: var(--board);
  position: relative;
  box-shadow: inset 0 -40px 40px -40px rgb(0 0 0 / 45%);
}
#hero {
  display: block;
  width: 100%;
  /* Полоса гаснет по краям: срез тележек так читается как окно в большее
     пространство, а не как обрезанная картинка. */
  mask-image: linear-gradient(to bottom, transparent, #000 22%, #000 78%, transparent);
  /* Квадратная доска в горизонтальной полосе: показываем её середину. */
  aspect-ratio: 1;
  margin-top: -14%;
  pointer-events: none;
}

.brand {
  display: grid;
  gap: 8px;
}
.eyebrow {
  margin: 0;
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--accent);
}
/* Название и знак — одной строкой: знак объясняет правило, название его
   называет, и порознь они работают хуже. На узком экране знак ужимается
   вместе с кеглем (та же величина в `em`), поэтому строка не ломается. */
.brand-line {
  display: flex;
  align-items: center;
  gap: clamp(10px, 2.5vw, 18px);
  min-width: 0;
}
.brand-mark {
  flex: none;
  width: clamp(46px, 13vw, 78px);
  height: auto;
  border-radius: 22%;
  /* Знак нарисован на своём бетоне и имеет собственные углы: без тени он
     сливается с фоном страницы на тёмной теме. */
  box-shadow: 0 2px 10px rgb(0 0 0 / 35%);
}
h1 {
  margin: 0;
  /* Russo One — только на названии: это витринный шрифт, в тексте он
     нечитаем. Кириллическое подмножество приклеено к стилю сборкой
     (build.mjs), поэтому запросов за ним нет. Латиницы и цифр в файле нет
     вовсе, и `unicode-range` уводит их в системный шрифт. */
  font-family: 'Russo One', system-ui, sans-serif;
  font-size: clamp(27px, 8vw, 50px);
  line-height: 1;
  font-weight: 400;
  letter-spacing: -0.01em;
  text-wrap: balance;
}
/* «роботов» — вторым голосом: имя короткое, а полное название длинное, и без
   разницы в весе оно читается одним слипшимся словом. */
h1 span {
  display: block;
  color: var(--muted);
  font-size: 0.62em;
}
.lede {
  margin: 0;
  max-width: 52ch;
  color: var(--muted);
  font-size: 16px;
}

/* ─── два режима ───────────────────────────────────────────────────────── */

/* Развилка, а не список. Блоки разнесены линией и заголовком: раньше ступени
   лежали списком, а смена пряталась кнопкой среди разделов, и что режима два
   было попросту неоткуда узнать. */
.mode {
  display: grid;
  /* `minmax(0, 1fr)`, а не просто колонка по умолчанию: `auto` берёт ширину по
     максимальному содержимому, а внутри лежит листалка шириной в семь карточек.
     Из-за этого ВСЯ СТРАНИЦА ехала вбок на 589-1063 px — на телефоне, планшете
     и ноутбуке разом. Замерено, а не предположено. */
  grid-template-columns: minmax(0, 1fr);
  gap: 10px;
  padding-top: 14px;
  border-top: 1px solid var(--line);
}
.mode-head h2 {
  margin: 0;
  font-size: 13px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--accent);
}
.mode-note {
  margin: 4px 0 0;
  color: var(--muted);
  font-size: 13.5px;
  max-width: 56ch;
}
.mode-match .primary {
  justify-self: start;
  font: inherit;
  font-weight: 600;
  cursor: pointer;
  padding: 12px 20px;
  border-radius: 10px;
  border: 1px solid var(--accent);
  background: var(--accent);
  color: #fff;
}
.mode-match .primary:hover,
.mode-match .primary:focus-visible {
  filter: brightness(1.08);
}

/* ─── листалка ступеней ────────────────────────────────────────────────── */

/* Семь карточек столбиком — это два экрана прокрутки на телефоне до того, как
   игрок доберётся до второго режима. Поэтому они листаются вбок, с примагни-
   чиванием: палец отпускает — карточка встаёт по месту. */
.rail {
  display: flex;
  align-items: center;
  gap: 6px;
  min-width: 0;
}
.rail-arrow {
  flex: none;
  font: inherit;
  cursor: pointer;
  padding: 10px 8px;
  border-radius: 8px;
  border: 1px solid var(--line);
  background: var(--panel);
  color: var(--fg);
  line-height: 1;
}
.rail-arrow:hover,
.rail-arrow:focus-visible {
  border-color: var(--accent);
}
/* На телефоне стрелки не нужны: там листают пальцем, а две кнопки съедают
   ширину, которой и так нет. */
@media (max-width: 620px) {
  .rail-arrow {
    display: none;
  }
}

.levels {
  flex: 1;
  min-width: 0;
  display: grid;
  grid-auto-flow: column;
  /* Ширина фиксированная, а не `1fr`: доля ужимала семь карточек в любую
     ширину, и листалка переставала листать — а карточки становились тесными.
     Не помещаются — листаем; помещаются (очень широкий экран) — и хорошо. */
  grid-auto-columns: 240px;
  gap: 10px;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scroll-behavior: smooth;
  /* Полоса прокрутки под карточками — лишняя линия в макете; листание и так
     очевидно по обрезанной следующей карточке. */
  scrollbar-width: none;
  padding-bottom: 2px;
}
.levels::-webkit-scrollbar {
  display: none;
}
@media (max-width: 620px) {
  /* Одна карточка на экран плюс край следующей: край и есть подсказка, что
     тут листают. */
  .levels {
    grid-auto-columns: 82%;
  }
}
.level-card {
  scroll-snap-align: start;
}
.level-card {
  display: grid;
  gap: 3px;
  justify-items: start;
  text-align: left;
  font: inherit;
  cursor: pointer;
  padding: 14px 16px;
  border: 1px solid var(--line);
  border-left: 4px solid var(--accent);
  border-radius: 10px;
  background: var(--panel);
  color: var(--fg);
}
.level-card:hover,
.level-card:focus-visible {
  border-color: var(--accent);
}
.level-name {
  font-size: 19px;
  font-weight: 650;
  letter-spacing: -0.01em;
  text-transform: capitalize;
}
.level-span {
  font-variant-numeric: tabular-nums;
  color: var(--accent);
  font-weight: 600;
}
.level-meta {
  font-size: 12.5px;
  color: var(--muted);
}

.menu-links {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
}
.menu-links button,
.actions button,
.bar button {
  font: inherit;
  cursor: pointer;
  padding: 8px 14px;
  border-radius: 8px;
  border: 1px solid var(--line);
  background: var(--panel);
  color: var(--fg);
}
.menu-links button:hover,
.actions button:hover,
.bar button:hover {
  border-color: var(--accent);
}
.credit {
  margin: 0;
  color: var(--muted);
  font-size: 12.5px;
}

/* ─── меню в один экран ────────────────────────────────────────────────── */

/* Требование простое: на телефоне первый экран обязан показывать ВСЁ — обе
   развилки, ступени и нижние кнопки, — не прокручиваясь. Оно не выполнялось:
   на 360×600 меню кончалось на 902-м пикселе, то есть лишних было 302
   (`tools/fit-probe.mjs`). Это замер, а не впечатление, и он же сторожит
   правку от отката.

   Условие по ВЫСОТЕ, а не по ширине, и это не придирка: планшет лёжа —
   1024×640, широкий и низкий. По ширине он «большой экран» и получал бы
   просторную вёрстку, в которой меню не помещается. Не помещается оно из-за
   высоты, значит и спрашивать надо про высоту.

   Уменьшается всё сразу и понемногу, а не одно место сильно: отъесть 300
   пикселей у одного блока — значит сделать его нечитаемым. */

@media (max-height: 820px) {
  .wrap {
    padding-block: 12px 14px;
  }
  #s-menu {
    gap: 12px;
  }
  .hero-frame {
    height: clamp(56px, 9vh, 96px);
  }
  .lede {
    font-size: 14.5px;
  }
  .mode {
    padding-top: 10px;
    gap: 8px;
  }
  .level-card {
    padding: 10px 13px;
  }
  .level-name {
    font-size: 17px;
  }
  .mode-match .primary {
    padding: 9px 16px;
  }
}

@media (max-height: 700px) {
  .wrap {
    padding-block: 8px 10px;
  }
  #s-menu {
    gap: 9px;
  }
  .hero-frame {
    height: 52px;
  }
  .brand {
    gap: 4px;
  }
  .eyebrow {
    font-size: 11px;
  }
  .brand-mark {
    width: 42px;
  }
  h1 {
    font-size: clamp(23px, 6.4vw, 34px);
  }
  .lede {
    font-size: 13.5px;
    line-height: 1.4;
  }
  .mode-note {
    font-size: 12.5px;
  }
  .level-card {
    padding: 8px 12px;
    gap: 1px;
  }
  .level-name {
    font-size: 16px;
  }
  .level-meta {
    font-size: 11.5px;
  }
  .menu-links button {
    padding: 6px 11px;
    font-size: 13.5px;
  }
  .credit {
    font-size: 11px;
  }
}

/* Самые тесные: старый телефон книжкой и всё, что ниже него. Здесь отрезать
   уже нечего, кроме воздуха, — и отрезается именно он. */
@media (max-height: 640px) {
  .wrap {
    padding-block: 6px 6px;
  }
  #s-menu {
    gap: 7px;
  }
  .hero-frame {
    height: 44px;
  }
  .mode {
    padding-top: 8px;
  }
}

/* ─── служебные экраны ─────────────────────────────────────────────────── */

.bar {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
}
.bar h2 {
  margin: 0;
  font-size: 19px;
  font-weight: 650;
  letter-spacing: -0.01em;
}
.bar-right {
  margin-left: auto;
  display: flex;
  gap: 8px;
}
h3.sub {
  margin: 10px 0 0;
  font-size: 15px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
}
.note-line {
  margin: 0;
  color: var(--muted);
  max-width: 58ch;
}

.rules {
  display: grid;
  gap: 16px;
}
.rule {
  display: grid;
  gap: 12px 16px;
  grid-template-columns: minmax(160px, 220px) 1fr;
  align-items: center;
}
.rule-figure {
  width: 100%;
  height: auto;
  max-width: 100%;
  border: 1px solid var(--line);
  border-radius: 8px;
  display: block;
}
.rule-text h3 {
  margin: 0 0 4px;
  font-size: 16px;
  font-weight: 650;
}
.rule-text p {
  margin: 0;
  color: var(--muted);
}
@media (max-width: 520px) {
  .rule {
    grid-template-columns: 1fr;
  }
}

.note {
  border-top: 1px solid var(--line);
  padding-top: 14px;
  display: grid;
  gap: 8px;
}
.note h3 {
  margin: 6px 0 0;
  font-size: 13px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
}
.note p {
  margin: 0;
  max-width: 58ch;
}

.crew {
  display: grid;
  gap: 10px;
  grid-template-columns: repeat(auto-fit, minmax(215px, 1fr));
}
.crew-card {
  display: grid;
  gap: 5px;
  align-content: start;
  padding: 13px 15px;
  border: 1px solid var(--line);
  border-left: 4px solid var(--c, var(--line));
  border-radius: 10px;
  background: var(--panel);
}
.crew-head {
  display: flex;
  align-items: center;
  gap: 9px;
}
.crew-sprite {
  width: 34px;
  height: 34px;
  flex: none;
  image-rendering: auto;
}
.crew-card h3 {
  margin: 0;
  font-size: 17px;
  font-weight: 650;
}
.crew-who {
  margin: 0;
  font-size: 13px;
  color: var(--c, var(--muted));
  font-weight: 550;
}
.crew-essence {
  margin: 0;
  font-size: 13.5px;
  color: var(--muted);
}
.manner {
  margin: 4px 0 0;
  /* Три колонки, а не flex-wrap: иначе «удар» срывается на вторую строку
     и карточки перестают быть одной высоты. */
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 8px;
  font-size: 12px;
}
.manner div {
  display: grid;
  align-content: start;
}
.manner dt {
  color: var(--muted);
  letter-spacing: 0.05em;
  text-transform: uppercase;
  font-size: 10.5px;
}
.manner dd {
  margin: 0;
  font-weight: 600;
}
.crew-line {
  margin: 6px 0 0;
  font-size: 13.5px;
  font-style: italic;
  color: var(--fg);
}
.crew-quiet .crew-card {
  border-left-color: var(--line);
  opacity: 0.85;
}

.setting {
  display: grid;
  gap: 6px;
  justify-items: start;
}
.setting label {
  font-size: 13px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--muted);
}
.setting .hint {
  margin: 0;
  font-size: 13px;
  color: var(--muted);
  max-width: 54ch;
}
select {
  font: inherit;
  padding: 7px 10px;
  border-radius: 8px;
  border: 1px solid var(--line);
  background: var(--panel);
  color: var(--fg);
}

/* ─── доска ────────────────────────────────────────────────────────────── */

/* Квадрат через отношение сторон: доска квадратная на любой ширине.
   Ограничение по высоте нужно, чтобы на широком экране она не выпихивала
   кнопки за нижний край — там ограничивает высота окна, а не ширина. */
.board-frame {
  position: relative;
  margin: 0 auto;
  aspect-ratio: 1;
  /* На телефоне ограничивает ширина, на ноутбуке — высота окна: обвязка
     (панель, статус, кнопки) занимает примерно 380 px, и без этого вычета
     кнопки уезжают за нижний край на экране 1280x800. Замеряется в smoke.mjs.
     Число подобрано замером, а не выведено: когда в верхнюю строку добавился
     номер задачи, она стала на пару пикселей выше, и 412 перестало хватать. */
  width: min(100%, calc(100vh - 416px));
  background: var(--board);
  border: 1px solid var(--line);
  border-radius: 12px;
  /* Обрезка живёт на самой доске, а не на рамке: рамка обязана выпускать
     наружу пузырь реплики, иначе он режется на полслове у края. */
  touch-action: none;
  /* Доска лежит в кадре, а не наклеена на страницу: мягкая тень под рамкой
     и тонкая внутренняя кромка сверху. */
  box-shadow:
    0 10px 26px -12px rgb(0 0 0 / 45%),
    inset 0 1px 0 rgb(255 255 255 / 22%);
}
#board {
  display: block;
  width: 100%;
  height: 100%;
  border-radius: 11px;
  overflow: hidden;
}

/* Заливки стен и блока приходят атрибутом из арт-пака. Правила вида
   `.walls rect { fill: … }` здесь были и молча перебивали пак: CSS сильнее
   презентационного атрибута, и токен пака не доезжал до экрана. */
.lamps {
  mix-blend-mode: screen;
  pointer-events: none;
}
.robot {
  cursor: pointer;
}

/* ─── покой ────────────────────────────────────────────────────────────── */

/* Четвёртый параметр манеры (`docs/01` §4): что тележка делает, когда её не
   трогают. Проценты, а не единицы viewBox: с `fill-box` они считаются от
   самой фигуры, поэтому покой одинаков на доске 8×8 и 16×16. */
.robot .idle {
  transform-box: fill-box;
  transform-origin: 50% 50%;
}
/* Во время проезда покой молчит: иначе он дерётся с анимацией хода. */
.robot.moving .idle {
  animation: none;
}

.idle-settle {
  animation: idle-settle 7.5s ease-in-out infinite;
}
@keyframes idle-settle {
  0%,
  100% {
    transform: translateY(0);
  }
  45% {
    transform: translateY(2.4%);
  }
  /* короткий тик — остывающее железо */
  58% {
    transform: translateY(1%);
  }
  60% {
    transform: translateY(3%);
  }
  80% {
    transform: translateY(2%);
  }
}

.idle-jitter {
  animation: idle-jitter 1.5s steps(4, end) infinite;
}
@keyframes idle-jitter {
  0%,
  100% {
    transform: translate(0, 0);
  }
  25% {
    transform: translate(1.4%, -1%);
  }
  50% {
    transform: translate(-1.2%, 1%);
  }
  75% {
    transform: translate(1%, 1.4%);
  }
}

.idle-sweep {
  animation: idle-sweep 3.4s ease-in-out infinite;
}
@keyframes idle-sweep {
  0%,
  100% {
    transform: rotate(-3.5deg);
  }
  50% {
    transform: rotate(3.5deg);
  }
}

/* .idle-still намеренно без правила: неподвижность — это тоже характер. */

.idle-look {
  animation: idle-look 9s ease-in-out infinite;
}
@keyframes idle-look {
  0%,
  52% {
    transform: rotate(0deg);
  }
  62%,
  78% {
    transform: rotate(12deg);
  }
  88%,
  100% {
    transform: rotate(0deg);
  }
}

/* Пылинка масштабируется вокруг себя. Без этого SVG считает точкой отсчёта
   начало viewBox, и `scale` уносит частицу за пределы доски — так и было. */
.fx circle {
  transform-box: fill-box;
  transform-origin: 50% 50%;
}
.robot .ring {
  transition: opacity 120ms ease;
  transform-box: fill-box;
  transform-origin: 50% 50%;
  animation: ring-spin 9s linear infinite;
}
@keyframes ring-spin {
  to {
    transform: rotate(360deg);
  }
}

/* Цель дышит: на доске 256 клеток её надо находить взглядом сразу. */
.goal-glow {
  transform-box: fill-box;
  transform-origin: 50% 50%;
  animation: goal-breathe 2.8s ease-in-out infinite;
}
@keyframes goal-breathe {
  0%,
  100% {
    opacity: 0.7;
    transform: scale(0.9);
  }
  50% {
    opacity: 1;
    transform: scale(1.05);
  }
}

/* ─── смена ────────────────────────────────────────────────────────────── */

.menu-links .primary {
  border-color: var(--accent);
  background: var(--accent);
  color: #fff;
  font-weight: 600;
}
.score {
  margin-left: auto;
  color: var(--muted);
  font-size: 13px;
  font-variant-numeric: tabular-nums;
}
/* Кнопки заявок — числовой ряд, а не текст: торг читается одним взглядом. */
.bids button {
  min-width: 44px;
  font-variant-numeric: tabular-nums;
  font-weight: 600;
}
.bids button:disabled {
  opacity: 0.35;
  cursor: default;
}
.rivals .rival {
  border-left-color: var(--muted);
}
.rival-line {
  margin: 6px 0 0;
  min-height: 1.4em;
  font-size: 13.5px;
  font-style: italic;
  color: var(--fg);
}

/* ─── реплики ──────────────────────────────────────────────────────────── */

/* Пузырь висит над тележкой, не перекрывает доску и не ловит ввод
   (`docs/03` §2). Ввод он не ловит буквально: иначе им нельзя играть. */
.bark {
  position: absolute;
  z-index: 2;
  transform: translate(-50%, calc(-100% - 26px));
  max-width: min(62%, 260px);
  padding: 7px 11px;
  border-radius: 10px;
  border: 2px solid var(--c, var(--line));
  background: var(--panel);
  color: var(--fg);
  font-size: 13.5px;
  line-height: 1.35;
  pointer-events: none;
  box-shadow: 0 6px 18px -8px rgb(0 0 0 / 55%);
  animation: bark-in 160ms ease-out;
  display: flex;
  align-items: center;
  gap: 8px;
}
/* Детский режим: кегль такой, чтобы фразу было видно с дивана и можно было
   прочитать вслух. Длинных строк здесь не бывает — банк для него отдельный. */
.bark.kid {
  font-size: clamp(20px, 5.2vw, 30px);
  font-weight: 750;
  line-height: 1.15;
  letter-spacing: -0.01em;
  padding: 12px 16px;
  border-width: 3px;
  max-width: min(88%, 420px);
}
.bark-again {
  flex: none;
  pointer-events: auto;
  cursor: pointer;
  font-size: 17px;
  line-height: 1;
  padding: 6px 8px;
  border-radius: 8px;
  border: 1px solid var(--line);
  background: transparent;
}
.bark-again[hidden] {
  display: none;
}
.bark[data-side='below'] {
  transform: translate(-50%, 26px);
}
@keyframes bark-in {
  from {
    opacity: 0;
    transform: translate(-50%, calc(-100% - 18px));
  }
}

/* ─── обвязка игры ─────────────────────────────────────────────────────── */

.status {
  margin: 0;
  min-height: 1.5em;
  font-weight: 550;
}
.status[data-tone='good'] {
  color: var(--good);
}
.status[data-tone='ok'] {
  color: var(--accent);
}
.meta {
  margin: 0;
  color: var(--muted);
  font-size: 13px;
}

.robots-bar {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
}
.robots-bar button {
  font: inherit;
  cursor: pointer;
  padding: 7px 12px;
  border-radius: 999px;
  border: 2px solid var(--c);
  background: transparent;
  color: var(--fg);
}
.robots-bar button.active {
  background: var(--c);
  color: #fff;
}
.robots-bar button.is-goal::after {
  content: ' ◎';
}

/* Номер задачи. Сидит в общей строке с выбором смены, поэтому компактен:
   кнопки узкие, поле ровно под три знака. Отдельной строкой он стоил бы
   лишние 57 px высоты — на ноутбуке этого хватало, чтобы доска перестала
   помещаться в окно (замерено в smoke.mjs). */
.puzzle-nav {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  color: var(--muted);
  font-size: 13px;
}
.puzzle-nav button {
  font: inherit;
  cursor: pointer;
  padding: 6px 9px;
  border-radius: 7px;
  border: 1px solid var(--line);
  background: var(--panel);
  color: var(--fg);
  line-height: 1;
}
.puzzle-nav button:hover,
.puzzle-nav button:focus-visible {
  border-color: var(--accent);
}
#puzzle-no {
  font: inherit;
  width: 3.6em;
  padding: 5px 6px;
  border-radius: 7px;
  border: 1px solid var(--line);
  background: var(--panel);
  color: var(--fg);
  font-variant-numeric: tabular-nums;
  text-align: right;
}
#puzzle-of {
  font-variant-numeric: tabular-nums;
}

.counters {
  display: flex;
  gap: 20px;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}
.counters b {
  color: var(--fg);
  font-size: 17px;
}

.actions {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
}
kbd {
  font:
    11px/1 ui-monospace,
    monospace;
  padding: 2px 4px;
  margin-left: 4px;
  border: 1px solid var(--line);
  border-radius: 4px;
  color: var(--muted);
}

:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* Системная настройка сильнее любой красоты. */
@media (prefers-reduced-motion: reduce) {
  .robot .ring,
  .robot .idle,
  .goal-glow,
  .bark {
    animation: none;
  }
}

/* ─── смена с наводчиками ──────────────────────────────────────────────── */

/* Доска смены крупнее, чем в одиночной игре, и это не прихоть: во время торга
   игрок только на неё и смотрит, и на мелкой доске торг превращается в
   угадывание. Обвязка ради этого ужата — один ряд заявок и один ряд фишек,
   вместо карточек в пол-экрана. Вычет меньше, чем у игрового экрана (416):
   здесь нет ни панели роботов, ни счётчиков, ни четырёх кнопок. */
.match-board-frame {
  /* Вычет подобран замером, а не выведен: при 250 нижний ряд фишек уезжал за
     край на 4 px на планшете и на 50 на ноутбуке. При 310 всё помещается, и
     доска всё равно заметно крупнее прежней (на ноутбуке 500 против 384). */
  width: min(100%, calc(100vh - 310px));
}
/* На телефоне доска выходит за поля страницы: 32 px полей — это почти
   десятая часть доски, а читаемость клетки здесь дороже полей. */
@media (max-width: 520px) {
  .match-board-frame {
    width: calc(100% + 32px);
    margin-inline: -16px;
    border-radius: 0;
  }
}

.match-bar {
  gap: 8px;
}
.match-bar .score {
  margin-left: auto;
  font-size: 12.5px;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}

/* Таймер и своя заявка — крупно и справа, отдельно от текста фазы. */
.match-hud {
  display: flex;
  align-items: baseline;
  gap: 10px;
}
.match-phase {
  flex: 1;
  min-width: 0;
  font-weight: 550;
}
.match-nums {
  display: flex;
  align-items: baseline;
  gap: 10px;
  flex: none;
}
.match-mine {
  font-size: 13px;
  color: var(--muted);
  white-space: nowrap;
}
.match-clock {
  font-size: 22px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.02em;
  min-width: 3.2em;
  text-align: right;
}
/* Последние десять секунд видно боковым зрением: в торге это и есть решение. */
.match-clock.urgent {
  color: var(--accent);
}

/* Заявки — один ряд, который листается. Две строки по шесть кнопок съедали
   высоту, из-за которой доска и была мелкой. */
.bids {
  display: flex;
  gap: 6px;
  overflow-x: auto;
  scrollbar-width: none;
  padding-bottom: 2px;
}
.bids::-webkit-scrollbar {
  display: none;
}
.bids button {
  flex: none;
  font: inherit;
  cursor: pointer;
  min-width: 44px;
  padding: 9px 12px;
  border-radius: 8px;
  border: 1px solid var(--line);
  background: var(--panel);
  color: var(--fg);
  font-variant-numeric: tabular-nums;
}
.bids button:hover:not(:disabled),
.bids button:focus-visible:not(:disabled) {
  border-color: var(--accent);
}
.bids button:disabled {
  opacity: 0.35;
  cursor: default;
}

/* Фишка наводчика: лицо, имя, состояние. Вместо карточки с описанием — его
   читают один раз, а место оно занимает всю смену. */
.rivals {
  display: flex;
  gap: 8px;
  overflow-x: auto;
  scrollbar-width: none;
}
.rivals::-webkit-scrollbar {
  display: none;
}
.rival {
  flex: 1 1 0;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 7px 9px;
  border: 1px solid var(--line);
  border-left: 3px solid var(--c, var(--line));
  border-radius: 10px;
  background: var(--panel);
}
.rival-face {
  flex: none;
  width: 34px;
  height: 34px;
  display: block;
}
.rival-face svg {
  width: 100%;
  height: 100%;
  display: block;
  border-radius: 50%;
}
.rival-body {
  display: grid;
  min-width: 0;
}
.rival-name {
  font-size: 13.5px;
  font-weight: 600;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.rival-state {
  font-size: 12px;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}
.rival[data-state='bid'] .rival-state {
  color: var(--accent);
  font-weight: 600;
}
/* Думает — это видно: три точки, которые действительно идут. */
.rival[data-state='thinking'] .rival-state::after {
  content: '…';
  animation: thinking 1.1s steps(4, end) infinite;
}
@keyframes thinking {
  0% {
    content: '';
  }
  25% {
    content: '·';
  }
  50% {
    content: '··';
  }
  75% {
    content: '···';
  }
}
.rival-line {
  font-size: 12px;
  font-style: italic;
  color: var(--muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
@media (prefers-reduced-motion: reduce) {
  .rival[data-state='thinking'] .rival-state::after {
    animation: none;
    content: '…';
  }
}

/* Экран «как идёт смена». Шаги нумерованы: порядок здесь и есть содержание. */
.brief {
  display: grid;
  gap: 14px;
}
.brief-steps {
  margin: 0;
  padding-left: 22px;
  display: grid;
  gap: 9px;
  color: var(--muted);
  max-width: 60ch;
}
.brief-steps b {
  color: var(--fg);
}
/* На экране-объяснении фишки стоят столбиком: тут их читают, а не следят
   за ними краем глаза, и описание должно помещаться целиком. */
.brief-rivals {
  display: grid;
  gap: 8px;
  overflow: visible;
}
.brief-rivals .rival-who {
  font-size: 12.5px;
  color: var(--muted);
  white-space: normal;
}
.brief .primary {
  justify-self: start;
  font: inherit;
  font-weight: 600;
  cursor: pointer;
  padding: 12px 20px;
  border-radius: 10px;
  border: 1px solid var(--accent);
  background: var(--accent);
  color: #fff;
}
.ghost {
  font: inherit;
  cursor: pointer;
  width: 32px;
  height: 32px;
  border-radius: 50%;
  border: 1px solid var(--line);
  background: var(--panel);
  color: var(--muted);
}
.ghost:hover,
.ghost:focus-visible {
  border-color: var(--accent);
  color: var(--fg);
}
