/* Шрифт игры (02.10, Влад: «вшить как там, где ты проверяешь») — Roboto из Android (переменный, насыщенность 100–900),
   урезанный до латиницы, кириллицы и турецких букв: assets/fonts/roboto.woff2, лицензия OFL 1.1. Без него каждый телефон
   рисовал своим системным шрифтом (Samsung, Xiaomi, Huawei — разные), а на компьютере — шрифтом системы.
   display: block — без мигания системным шрифтом до загрузки (урок Словотеки); файл лежит рядом, ждать почти нечего. */
@font-face {
  font-family: "Nardy Roboto";
  src: url("../assets/fonts/roboto.woff2") format("woff2");
  font-weight: 100 900;
  font-style: normal;
  font-display: block;
}

/* Нарды. Вертикальная раскладка на оригинальной графике проекта.
   Доска — картинка одного из шести скинов; пункты, шашки, кости и кнопка ставятся
   поверх неё по замеренной геометрии (см. boardArt.json и board.js). */

:root {
  /* Безопасные отступы (06.10): вырез камеры из приложения (--cut-*) или env() браузера — что больше. */
  --safe-top: max(env(safe-area-inset-top, 0px), var(--cut-top, 0px));
  --safe-bottom: max(env(safe-area-inset-bottom, 0px), var(--cut-bottom, 0px));
  --safe-left: max(env(safe-area-inset-left, 0px), var(--cut-left, 0px));
  --safe-right: max(env(safe-area-inset-right, 0px), var(--cut-right, 0px));
  --font: "Nardy Roboto", Roboto, system-ui, sans-serif;
  color-scheme: dark;

  /*
    Единица размера всего, что стоит ВОКРУГ доски: доля её ширины.
    Мерить от экрана (vmin/vw/vmax) нельзя — доска подбирает свой размер сама,
    и на узком высоком экране она мелкая, а подписи рядом оставались «экранными»
    и не совпадали с ней по масштабу. Ширину доски сообщает скрипт (ResizeObserver).
  */
  --bw: var(--board-width, 340px);

  /*
    Пропорции сняты с оригинала (там аватар 121 px при доске 464), но с поправкой:
    у них доска занимала 72% ширины экрана, у нас 95%. Поэтому коэффициенты примерно
    на четверть меньше «юнитевских» — иначе элементы вокруг доски разносит.
  */

  /*
    Полоса снизу под рекламный баннер. Сам баннер рисует не игра, но место под него
    занимать нельзя. Значение из оригинала: SafeArea резервирует Screen.height * 0.0781
    даже когда баннера нет (см. SafeArea.GetBannerSize).
  */
  --banner: max(7.81dvh, var(--ad-safe-h, 0px));   /* полоса под баннер (натив отдаёт его высоту в --ad-safe-h) */

  /*
    Угловые кнопки (назад, настройки, правила, статистика...) одинаковые в меню и в игре:
    один размер и один отступ от края. Раньше в игре они считались от доски, в меню —
    от экрана, и при переходе кнопки прыгали и меняли размер. Размер — меньший из двух
    прежних вариантов.
  */
  --corner-btn: clamp(44px, 5vmax, 64px);
  --edge-x: clamp(10px, 2vmin, 20px);
  --edge-y: clamp(6px, 1.2vmin, 14px);

  --ink: #eaf1fb;
  --ink-dim: #93a6c2;
  --accent: #f0c04a;
  --accent-soft: rgba(240, 192, 74, .3);

  --blue-1: #4a90d9;
  --blue-2: #2f68a8;
  --panel: rgba(9, 20, 38, .62);
  --panel-line: rgba(160, 196, 245, .18);

  --gap: 8px;
}

* { box-sizing: border-box; }

html, body {
  height: 100%;
  margin: 0;
  overflow: hidden;
  color: var(--ink);
  font: 400 14px/1.35 var(--font);
  background: #16233c url("../assets/bg/bg_0.png") center / cover no-repeat;
  -webkit-text-size-adjust: 100%;
  -webkit-tap-highlight-color: transparent;
  /* Долгое нажатие не выделяет текст и не открывает системное меню «Копировать / Поделиться» (Android, 02.10). */
  -webkit-user-select: none;
  user-select: none;
  -webkit-touch-callout: none;
}
/* Где текст вводят или копируют — выделение остаётся: имя игрока, запись партии .mat. */
input, textarea { -webkit-user-select: text; user-select: text; -webkit-touch-callout: default; }
/* Кнопки и поля сами шрифт не наследуют — без этого они рисовались системным шрифтом телефона, а не вшитым. */
button, input, textarea, select { font-family: var(--font); }

img { max-width: 100%; }
[hidden] { display: none !important; }

/* ───────────────────────────────────────────────── каркас экрана */

/*
  Строки: панель, игроки, верхняя рельса, доска, нижняя рельса, подсказка.
  Доска получает всю оставшуюся высоту (1fr) и сама ограничивает себя пропорцией —
  поэтому ничего не надо пересчитывать руками при смене устройства.
*/
#app {
  height: 100%;
  display: grid;
  gap: 6px;
  grid-template-rows: auto minmax(0, 1fr) auto;
  grid-template-areas:
    "top"
    "board"
    "hint";
  padding: calc(var(--edge-y) + var(--safe-top)) calc(var(--edge-x) + var(--safe-right))
           calc(6px + var(--banner) + var(--safe-bottom)) calc(var(--edge-x) + var(--safe-left));
  /* Игра только вертикальная (30.09): на широком окне (планшет, ПК) колонка по центру,
     а не карточки игроков по краям экрана. */
  max-width: 620px;
  margin-inline: auto;
}

/* ───────────────────────────────────────────────── верхняя панель */

.topbar {
  grid-area: top;
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  gap: 10px;
}

.icon-btn {
  appearance: none;
  width: var(--corner-btn);
  aspect-ratio: 1;
  border: none;
  border-radius: 14px;
  background: linear-gradient(180deg, var(--blue-1), var(--blue-2));
  box-shadow: 0 3px 0 #21486f, 0 6px 14px -6px #000;
  cursor: pointer;
  display: grid;
  place-items: center;
}
/* Картинку ставит скрипт на дочерний элемент, а не через переменную: относительный
   путь внутри custom property считается от файла стилей, а не от страницы. */
.icon-btn > i {
  width: 58%;
  height: 58%;
  background: center / contain no-repeat;
}
.icon-btn:active { transform: translateY(2px); box-shadow: 0 1px 0 #21486f; }

.screen-title {
  margin: 0;
  text-align: center;
  /* Поменьше и спокойнее (Влад, 02.10: «не нужен такой большой элемент, и так всё понятно»); высота шапки — от кнопок. */
  font-size: calc(var(--bw) * .045);
  font-weight: 700;
  letter-spacing: .5px;
  color: var(--ink-dim);
  text-shadow: 0 2px 4px rgba(0, 0, 0, .5);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* ───────────────────────────────────────────────── строка игроков */

/*
  Аватар, счёт и имя — как в оригинале: овал с рамкой, счёт «выезжает» из-под него,
  имя снизу. Счётчик лежит ПОД аватаром по z, поэтому и смотрится вложенным.
*/
.players {
  /* Отступ от верхней панели: без него на коротких телефонах аватарки вставали
     вплотную под кнопки «назад» и «настройки». */
  margin-top: calc(var(--bw) * .05);
  margin-bottom: calc(var(--bw) * .03);
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: start;
  gap: 8px;
}

.player {
  display: grid;
  grid-template-columns: auto auto;
  grid-template-areas: "ava score" "name name";
  align-items: center;
  justify-items: start;
}
.player.right {
  grid-template-columns: auto auto;
  grid-template-areas: "score ava" "name name";
  justify-items: end;
}

.ava-wrap {
  grid-area: ava;
  position: relative;
  width: calc(var(--bw) * .2);
  min-width: 44px;
  aspect-ratio: 121 / 122;
  display: grid;
  place-items: center;
  z-index: 2;
}
/* Порядок как в оригинале: цветная подложка, поверх силуэт, сверху золотая рамка. */
.ava-wrap::before,
.ava-wrap::after {
  content: "";
  position: absolute;
  inset: 0;
  background: center / contain no-repeat;
}
/* Дырка в ободке начинается на 6.1% от края (замер по альфе frame_0), и цвет должен
   заполнять её целиком — иначе между цветом и золотом видна тёмная щель. */
/* Картинка цвета — 168×172, а сам круг в ней только 164×166 у левого верхнего угла
   (замер по альфе): справа и снизу пустые поля. Растянутая целиком, она сдвигала круг
   вверх-влево, и снизу-справа под золотым ободком оставалась щель (Влад, 27.09).
   Поэтому масштаб — по кругу (168/164, 172/166), привязка к левому верхнему углу,
   а отступ чуть меньше дырки ободка (4.3–4.9%), чтобы край уходил под золото. */
.ava-wrap::before {
  background-image: var(--ava-back, none);
  background-size: 102.44% 103.61%;
  background-position: 0 0;
  inset: 4%;
  border-radius: 50%;
}
.ava-wrap::after { background-image: url("../assets/frame/frame_0.png"); }
.ava-img {
  position: relative;
  z-index: 1;
  width: 62%;
  height: 76%;
  object-fit: contain;
}
.ava-chip {
  position: absolute;
  /* Поверх ободка: без этого золотая рамка рисовалась сверху и срезала фишку. */
  z-index: 3;
  left: -6%;
  bottom: -14%;
  width: 40%;
  aspect-ratio: 1;
  background: center / contain no-repeat;
  filter: drop-shadow(0 1px 2px rgba(0, 0, 0, .6));
}
.player.right .ava-chip { left: auto; right: -6%; }

.player-score {
  grid-area: score;
  min-width: calc(2ch + var(--bw) * .08 + 2px);   /* место под две цифры сразу (2ch + поля + рамка: box-sizing border-box) (Влад, 21.10: при 10+ плашка разъезжалась) */
  margin-inline-start: calc(var(--bw) * -.035);
  padding: calc(var(--bw) * .018) calc(var(--bw) * .03) calc(var(--bw) * .018) calc(var(--bw) * .05);
  border-radius: 9px;
  font-size: calc(var(--bw) * .05);
  background: rgba(4, 11, 24, .8);
  border: 1px solid var(--panel-line);
  font-weight: 800;
  font-variant-numeric: tabular-nums;
  text-align: center;
}
.player.right .player-score {
  margin-inline: 0 calc(var(--bw) * -.035);
  padding: calc(var(--bw) * .018) calc(var(--bw) * .05) calc(var(--bw) * .018) calc(var(--bw) * .03);
}

.player-name {
  grid-area: name;
  max-width: calc(var(--bw) * .42);
  margin-top: calc(var(--bw) * .018);
  font-size: calc(var(--bw) * .04);
  color: var(--ink-dim);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.player.right .player-name { justify-self: end; }

.goal {
  align-self: center;
  text-align: center;
  font-weight: 700;
  font-size: calc(var(--bw) * .036);
  line-height: 1.25;
  letter-spacing: .4px;
  text-transform: uppercase;
  color: var(--ink-dim);
}

/* Чей ход — подсвечивается рамка аватара. */
.player.active .ava-wrap { filter: drop-shadow(0 0 8px var(--accent)); }

/* ───────────────────────────── рельсы вокруг доски: пипы и лоток */

.rail {
  /* Рельса шириной ровно с доску: пипы у её левого края, лоток у правого, как
     в оригинале. Ширину доски сообщает скрипт (следит за её размером). */
  width: min(100%, var(--board-width, 100%));
  margin-inline: auto;
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: center;
  gap: 8px;
  min-height: 32px;
}
/* Ничто в рельсе не имеет права раздуть её шире доски. */
.rail > * { min-width: 0; }

/*
  Лоток снятых шашек: тёмная ниша справа от пипов. Шашки лежат с сильным нахлёстом —
  иначе пятнадцать штук не поместятся ни на одном экране.
*/
/*
  Лоток снятых шашек. Длина ровно такая, чтобы встали все пятнадцать боком:
  шашка в лотке — отдельный спрайт 24×64, то есть шашка, поставленная на ребро.
  Поэтому ширина считается от высоты лотка, а не подбирается на глаз.
*/
/* Порядок в рельсе задаётся стороной выброса: лоток уезжает к своему краю доски. */
.rail[data-bearoff="left"] .pips { order: 3; justify-self: end; }
.rail[data-bearoff="left"] .tray { order: 1; justify-self: start; }
.rail[data-bearoff="left"] .rail-mid { order: 2; }

/*
  Лоток снятых шашек. Размер задан правилом, а не подобран: шашка лежит в нём НА РЕБРЕ
  (спрайт chip_collected 24×64), высота лотка равна её длинной стороне, а ширины хватает
  ровно на пятнадцать штук с небольшим зазором. Оба лотка поэтому одинаковые —
  они считаются от одной величины, ширины доски.
*/
.tray {
  --tray-h: calc(var(--bw) * .08);
  justify-self: end;
  height: var(--tray-h);
  /* Шашка встаёт во ВНУТРЕННЮЮ высоту (минус рамка 2px), поэтому и ширина считается от неё:
     от полной высоты лоток выходил длиннее ровно на одну шашку — «лишняя дырка». */
  /* Шашки — вплотную к левому краю и друг к другу (Влад, 03.10: «слева зазор, шашки не вплотную»): без внутреннего
     отступа, соседние заходят друг на друга на 1 px — дробная ширина иначе давала щели сглаживания. */
  /* 15 ячеек ровно на всю внутреннюю ширину, между ними 1 px (Влад, 03.10: «слева и справа свободное место»): сетка, а не
     ряд с отрицательными полями — та давала 14–15 px пустоты справа (замер 412/360/768). */
  width: calc((var(--tray-h) - 4px) * 24 / 64 * 15 + 14px + 4px);
  padding: 0;
  border-radius: calc(var(--tray-h) * .18);
  background: rgba(4, 11, 24, .74);
  border: 2px solid var(--panel-line);
  display: grid;
  grid-template-columns: repeat(15, minmax(0, 1fr));
  column-gap: 1px;
  align-items: stretch;
  overflow: hidden;
}
/* Лоток как цель выброса. Тонкой рамки было мало: цель на доске — целая шашка
   с ореолом, а лоток рядом читался как «просто подсвеченная рамка». Теперь золотая
   рамка, свечение наружу и мягкая пульсация — видно боковым зрением. */
.tray { position: relative; }
.tray.droppable {
  border-color: var(--accent);
  /* Золото ПОВЕРХ тёмной подложки, а не вместо неё: иначе лоток становился
     полупрозрачным и сквозь него был виден фон (Влад, 28.09). */
  background: linear-gradient(rgba(240, 192, 74, .16), rgba(240, 192, 74, .16)), rgba(4, 11, 24, .74);
  box-shadow: 0 0 8px 1px rgba(240, 192, 74, .35);   /* 03.10: без второй (внутренней) обводки — тоньше, спокойнее */
  cursor: pointer;
  overflow: visible;
}
/* Пульсация — прозрачностью отдельного слоя со свечением, а не анимацией box-shadow:
   тень перерисовывается каждый кадр (батарея, 28.09), прозрачность двигает видеокарта. */
.tray.droppable::after {
  content: '';
  position: absolute;
  inset: -4px;
  border-radius: inherit;
  box-shadow: 0 0 12px 2px rgba(240, 192, 74, .45);
  pointer-events: none;
  /* Без пульсации — ровное свечение (Влад, 03.10: «слишком ярко», «пульсацию уберём»). */
}
@keyframes tray-pulse { from { opacity: .2; } to { opacity: 1; } }
@media (prefers-reduced-motion: reduce) { .tray.droppable::after { animation: none; } }

.off-chip {
  width: 100%;
  height: 100%;
  background: var(--img) center / 100% 100% no-repeat;
}
/*
  Рельсы и доска — один блок, выровненный по центру свободной высоты. Иначе рельсы
  прилипали к краям экрана, а доска висела посередине с пустотой вокруг.
*/
.board-block {
  grid-area: board;
  min-height: 0;
  display: grid;
  /* Карточки, рельсы и доска — одна группа, выровненная по центру свободной высоты.
     Раньше карточки стояли отдельной строкой сверху, и вся пустота собиралась между
     ними и доской: на высоком экране профили улетали к самому верху. */
  /* Доска — единственная тянущаяся строка: ей достаётся ровно то, что осталось
     от карточек и двух рельс. Раньше её высота считалась от --rails-height, которую
     никто не выставлял, и запасные 80px не сходились с настоящими рельсами:
     на телефоне доска наезжала на нижнюю рельсу, на планшете уходила за экран. */
  grid-template-rows: auto auto minmax(0, 1fr) auto;
  gap: 6px;
  container-type: size;
}

.pips {
  display: flex;
  align-items: center;
  gap: 6px;
  font-weight: 700;
  font-size: calc(var(--bw) * .05);
  font-variant-numeric: tabular-nums;
  /* Строка обрезана по высоте цифр: иначе поля шрифта над и под цифрами разные (и у
     каждого шрифта свои), и счётчик над доской казался дальше, чем под ней (Влад, 28.09). */
  line-height: 1;
  text-box: trim-both cap alphabetic;
}
/* У картинки доски внизу прозрачная полоса тени (~1.6% ширины): видимое дерево кончается
   раньше рамки узла. Нижняя рельса поднимается на неё, чтобы отступы сверху и снизу совпали. */
#rail-player { margin-top: calc(var(--bw) * var(--rail-shift, -.0132)); }   /* сдвиг свой у скина: styles.json railShift */   /* 30.09: по пикселям видимого дерева зазоры сверху и снизу — 8.2 и 5.6 px, выровнено */
/* Число пипов — всегда место под три цифры (02.10, предрелизная проверка): колонка пипов меняла ширину 167 → 98 → 7,
   и середина рельсы — флажок «Сдаться» и «Ускорить» — съезжала на 5–11 px. Цифры моноширинные (tabular-nums). */
.pips > span { min-width: 3ch; text-align: right; }   /* к кубу прижато, место под 3 цифры — слева (03.10) */
.pips .chip-icon {
  width: calc(var(--bw) * .055);
  aspect-ratio: 1;
  background: center / contain no-repeat;
}

/* Автобой: в оригинале это переключатель «Ускорить» под доской. */
.autoplay {
  appearance: none;
  justify-self: center;
  display: flex;
  align-items: center;
  gap: calc(var(--bw) * .015);
  padding: calc(var(--bw) * .012) calc(var(--bw) * .035);
  border: 1px solid var(--panel-line);
  border-radius: 999px;
  background: rgba(9, 20, 38, .55);
  color: var(--ink-dim);
  font-size: calc(var(--bw) * .034);
  cursor: pointer;
  /* Переключение цветом, а не рывком: без перехода включение автобоя выглядело
     как подмена картинки. */
  transition: background .16s ease-out, color .16s ease-out, border-color .16s ease-out;
}
.autoplay:disabled {
  opacity: .38;
  cursor: default;
}

.autoplay[aria-pressed="true"] {
  color: #10305a;
  background: linear-gradient(180deg, #7cc0f5, #4a90d9);
  border-color: transparent;
}
.autoplay-icon {
  width: calc(var(--bw) * .045);
  aspect-ratio: 1;
  /* steps_3 — часы со стрелками вверх («ускорить»); steps_7 те же со стрелками вниз. */
  background: center / contain no-repeat url("../assets/steps/steps_3.png");
}
.autoplay[aria-pressed="true"] .autoplay-icon { background-image: url("../assets/steps/steps_7.png"); }

/* «Ускорить» и «Замедлить» лежат в одной ячейке, видна одна: ширину кнопки задаёт
   более длинная надпись, и при нажатии кнопка не прыгает (как в оригинале, где
   надпись меняется). */
.autoplay-text { display: grid; white-space: nowrap; }
.autoplay-text > span { grid-area: 1 / 1; text-align: center; }
/* Жирная всегда, даже скрытая: иначе ширина, занятая под неё, меньше нажатой. */
.autoplay-text > .on { font-weight: 700; }
.autoplay-text > .on,
.autoplay[aria-pressed="true"] .autoplay-text > .off { visibility: hidden; }
.autoplay[aria-pressed="true"] .autoplay-text > .on { visibility: visible; }

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

.board-wrap {
  min-width: 0;
  min-height: 0;
  display: grid;
  place-items: center;
  /* Свободное место меряет сама раскладка — доска читает его как 100cqh/100cqw. */
  container-type: size;
}

.board {
  position: relative;
  /* Доска занимает всю свободную высоту строки и столько ширины, сколько позволяет
     пропорция. Считать «сколько осталось» больше не нужно: строка доски в сетке
     объявлена как 1fr, а min-height: 0 у обёртки не даёт ей распирать экран. */
  /* Меньшее из двух: сколько позволяет свободная высота и сколько ширина.
     Контейнерные единицы считаются от .board-wrap — это ровно то, что осталось. */
  height: min(100cqh, 100cqw / var(--board-aspect, 0.893));
  aspect-ratio: var(--board-aspect, 0.893);
  margin-inline: auto;
  container-type: inline-size;
  filter: drop-shadow(0 12px 24px rgba(0, 0, 0, .55));
  user-select: none;
  touch-action: manipulation;
}

.board-surface {
  position: absolute;
  inset: 0;
  background: center / 100% 100% no-repeat;
}

/* Пункт: прозрачный прямоугольник поверх нарисованного треугольника. */
.point {
  position: absolute;
  cursor: default;
}
/* Обучение: пункт, о котором говорит урок, — мягкая жёлтая подсветка столбика от края доски (src/tutorial). */
.point.lesson-mark::before { content: ''; position: absolute; inset: 0 4%; pointer-events: none; border-radius: 8px;
  background: linear-gradient(to bottom, rgba(255, 214, 90, .5), rgba(255, 214, 90, .08) 96%);
  box-shadow: inset 0 0 0 2px rgba(255, 214, 90, .75), 0 0 10px rgba(255, 200, 60, .45);
  animation: lesson-mark-in .5s ease-out both; }
.point.bottom.lesson-mark::before { background: linear-gradient(to top, rgba(255, 214, 90, .5), rgba(255, 214, 90, .08) 96%); }
@keyframes lesson-mark-in { from { opacity: 0; } }
/* Под полной «головой» столбика не видно (Влад, 05.10) — светятся сами шашки: жёлтый ореол по контуру стопки.
   Только по явной отметке 'glow' (голова, стопки в марсе): в остальных местах ореолы накладывались — «шашки не подсвечивать». */
.point.lesson-mark.mark-glow .stack { animation: lesson-stack-glow 2.4s ease-in-out infinite; }
@keyframes lesson-stack-glow {
  0%, 100% { filter: drop-shadow(0 0 2px rgba(255, 214, 90, .9)) drop-shadow(0 0 5px rgba(255, 200, 60, .6)); }
  50% { filter: drop-shadow(0 0 3px rgba(255, 220, 100, 1)) drop-shadow(0 0 10px rgba(255, 200, 60, .95)); } }
/* Зелёная — «сюда», алая — «сюда нельзя» (Влад, 05.10: «куда ходить — зелёным, куда нельзя — алым»). */
.point.lesson-mark.mark-green::before { background: linear-gradient(to bottom, rgba(80, 220, 110, .5), rgba(80, 220, 110, .08) 96%);
  box-shadow: inset 0 0 0 2px rgba(80, 220, 110, .85), 0 0 10px rgba(60, 200, 90, .45); }
.point.bottom.lesson-mark.mark-green::before { background: linear-gradient(to top, rgba(80, 220, 110, .5), rgba(80, 220, 110, .08) 96%); }
.point.lesson-mark.mark-red::before { background: linear-gradient(to bottom, rgba(235, 60, 60, .5), rgba(235, 60, 60, .08) 96%);
  box-shadow: inset 0 0 0 2px rgba(240, 70, 70, .9), 0 0 10px rgba(230, 50, 50, .5); }
.point.bottom.lesson-mark.mark-red::before { background: linear-gradient(to top, rgba(235, 60, 60, .5), rgba(235, 60, 60, .08) 96%); }

.stack {
  position: absolute;
  inset: 0;
}

/* Шашки ставит скрипт: ширина в долях ДОСКИ (а не пункта), смещение — от внешнего
   края ряда. Раскладку flex'ом делать нельзя: шаг и нахлёст сняты с оригинала,
   и их надо задавать числом, а не «распредели как получится». */
.chip {
  position: absolute;
  width: var(--chip-width, 7.9cqw);
  aspect-ratio: 1;
  transform: translateX(-50%);
  background: center / contain no-repeat;
  filter: drop-shadow(0 1px 2px rgba(0, 0, 0, .5));
}

.chip .count {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  font-size: min(2.6cqw, 14px);
  font-weight: 800;
  color: #12202f;
  text-shadow: 0 1px 0 rgba(255, 255, 255, .5);
}
.chip.black .count { color: #f2f6fb; text-shadow: 0 1px 2px #000; }

/*
  Шашка реагирует на выбор ростом — в оригинале это ScaleDuration 0.1 с.
  Увеличение задаётся ВНУТРИ transform, а не отдельным свойством scale: отдельное
  свойство применяется к уже сдвинутой шашке и уводило её вбок на половину ширины.
*/
/* top/bottom — шаг в стопке. Когда шашка уходит из спрессованной стопки (или приходит
   в неё), остальные плавно встают на новый шаг, пока она летит, а не прыгают разом
   (Влад, 27.09). Новые узлы стартуют без перехода: у них нет прежнего значения. */
.chip { transition: transform .1s ease-out, top .3s ease-out, bottom .3s ease-out; }
/* Новая расстановка — без перехода (board.js update: больше трёх мест поменялось разом). */
.board.snap .chip { transition: none; }
.point.selected .stack .chip:last-of-type,
.bar-slot.selected .chip:last-of-type { transform: translateX(-50%) scale(1.14); }

/* Планка посередине: в коротких там стоят сбитые шашки. */
.bar-slot {
  position: absolute;
  cursor: default;
}

/* Половина доски в зазоре между рядами: там лежат кости и кнопки. */
.board-half {
  --ctl-h: 11cqw;
  position: absolute;
  transform: translateY(-50%);
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 5%;
  pointer-events: none;
}
.board-half > * { pointer-events: auto; }

/*
  Пустой лоток костей раньше всё равно занимал 62% половины, и «Бросить» вместе
  с костью начального броска уезжали от центра. Пустой — значит его нет.
*/
.board-dice {
  flex: none;            /* лоток не сжимается рядом с кнопкой: иначе пара/сетка складывалась в столбик (03.10) */
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: calc(var(--bw) * .02);
  width: calc(var(--bw) * .25);
  pointer-events: none;
}
.board-dice:empty { display: none; }
/* Свои слои у лотков костей и кнопок на доске (трасса 04.10): иначе их появление и исчезновение в момент броска
   («Бросить» прячется, «Готово/Отмена» появляются) перерисовывало плитки большого слоя доски. */
.board-dice, .board-half > .board-btn { will-change: transform; }
/* Бросок, подготовленный заранее (dice.js prepare): лоток вне раскладки, в середине половины, кости почти прозрачные —
   «Бросить» рядом не сдвигается. В момент броска класс снимается, и лоток встаёт на обычное место. */
.board-dice.staged { position: absolute; left: 50%; top: 50%; translate: -50% -50%; pointer-events: none; }

/* На дубле костей четыре — встают в ряд, того же размера, что пара (Влад, 04.10: «кости дубля летят мельче — некорректно»).
   Сетка 2×2 из обычных костей (0.1 доски) выше зазора до шестой шашки (0.175 доски), поэтому раньше кости дубля были
   мельче (0.083). Ряд 4 × 0.1 + 3 × 0.004 = 0.412 — ровно ширина зоны половины; плашка «Нет ходов» на дубль не ложится. */
.board-dice.four { width: calc(var(--bw) * .412); gap: calc(var(--bw) * .004); }

/* Размер от доски, а не от половины: так игровые кости и кости начального
   броска одинаковы, и на узком экране они не съёживаются. */
.die {
  width: calc(var(--bw) * .1);
  aspect-ratio: 1;
  background: center / contain no-repeat;
  filter: drop-shadow(0 2px 5px rgba(0, 0, 0, .5));
  transition: transform .18s ease-out;
}
/*
  Подброс. Кость взлетает, крутится в воздухе, падает и отскакивает — в оригинале
  бросок читается именно как бросок, а не как подмена картинки. Раньше здесь было
  лёгкое покачивание, и подброса не читалось вовсе.
*/
/* Сам бросок — в src/ui/toss.js: у каждой кости своя случайная траектория, CSS так не умеет. */

/*
  3D-кость (H1c, 03.10; src/ui/die3d.js). Место кости (.die) — то же, что у спрайтовой: 0.1 доски (и на дубле).
  Все размеры куба — от --dw (размер места), поэтому заготовки кадров годятся для любого размера доски.
  Анимируются только transform и opacity; фона-спрайта, filter и перехода у места нет.
*/
.die.d3host { --dw: calc(var(--bw) * .1); background: none !important; filter: none; transition: none; position: relative; overflow: visible; }
.die3 { position: absolute; inset: 0; perspective: calc(var(--dw) * 5.93); pointer-events: none; }
.d3-shadow { position: absolute; left: 50%; top: 50%; width: calc(var(--dw) * .855); height: calc(var(--dw) * .855);
  margin: calc(var(--dw) * -.4275) 0 0 calc(var(--dw) * -.4275); border-radius: 26%; opacity: .85;
  background: radial-gradient(closest-side, rgba(0, 0, 0, .55), rgba(0, 0, 0, .32) 62%, rgba(0, 0, 0, 0) 100%);
  transform: translate(3.7%, 6.5%); will-change: transform, opacity; }
.d3-mover { position: absolute; inset: 0; transform-style: preserve-3d; will-change: transform, opacity; }
.d3-cube { position: absolute; left: 50%; top: 50%; width: calc(var(--dw) * .791); height: calc(var(--dw) * .791);
  margin: calc(var(--dw) * -.3955) 0 0 calc(var(--dw) * -.3955); transform-style: preserve-3d; will-change: transform; }
.d3-face, .d3-body { position: absolute; left: 0; top: 0; }
/* Текстура грани — с прозрачной каймой 4/192 (die3d.js TEX_PAD): плоскость больше грани на кайму с каждой стороны. */
.d3-face { left: -2.0833%; top: -2.0833%; width: 104.1667%; height: 104.1667%; backface-visibility: hidden; background: center / 100% 100% no-repeat; }
.d3-inner { position: absolute; inset: 0; transform-style: preserve-3d; }
/* Затенение — у шести мест вокруг куба, поверх граней (die3d.js template); размер и кайма — как у грани. */
.d3-shade { position: absolute; left: -2.0833%; top: -2.0833%; width: 104.1667%; height: 104.1667%; backface-visibility: hidden;
  background: center / 100% 100% no-repeat; opacity: 0; }
.d3-body { display: block; backface-visibility: visible; }
/* Масштаб отрисовки граней, затенений и тела — фиксированный (трасса 04.10): без will-change видеокарта перерисовывала
   их в момент старта броска и по ходу полёта — при каждом заметном изменении поворота/высоты кости. */
.d3-face, .d3-shade, .d3-body { will-change: transform; }
/* В покое тело не нужно (грань сверху закрывает всё): стоящие ребром срезы давали бы тонкие линии поверх точек. */
.die3.rest .d3-body { display: none; }

/* Третья и четвёртая кости дубля «разъезжаются» — появляются чуть иначе. */
.die.extra { animation: die-split .3s ease-out; }
/* Конечное состояние не задано: кость приходит к своему наклону (inline transform). */
@keyframes die-split {
  from { opacity: 0; transform: scale(.4); }
  to   { opacity: 1; }
}

/*
  Кнопки на доске. Все ОДНОЙ ВЫСОТЫ — 11% ширины доски: «Бросить» широкая с кубиком,
  приём и отмена квадратные. Раньше высота зависела от содержимого, и «Бросить»
  прыгала по размеру, а на планшете становилась мелкой.
*/
.board-btn {
  appearance: none;
  border: none;
  border-radius: calc(var(--ctl-h) * .24);
  background: linear-gradient(180deg, var(--blue-1), var(--blue-2));
  box-shadow: 0 3px 0 #21486f, 0 6px 16px -6px #000;
  color: #fff;
  font: 700 calc(var(--ctl-h) * .42)/1 var(--font);
  cursor: pointer;
  white-space: nowrap;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: calc(var(--ctl-h) * .22);
  height: var(--ctl-h);
  width: var(--ctl-h);
  padding: 0;
  flex: 0 0 auto;
}
.board-btn.wide {
  width: auto;
  padding: 0 calc(var(--ctl-h) * .38);
}
.board-btn > i {
  width: calc(var(--ctl-h) * .54);
  aspect-ratio: 1;
  background: center / contain no-repeat;
  filter: drop-shadow(0 1px 1px rgba(0, 0, 0, .4));
}
.board-btn:active { transform: translateY(2px); box-shadow: 0 1px 0 #21486f; }
/* Неактивная кнопка — НЕПРОЗРАЧНАЯ (Влад, 02.10: полупрозрачные накладывались на логотип доски): свой приглушённый
   серо-синий цвет и бледный значок — «пока нельзя» читается цветом, а не прозрачностью. */
.board-btn:disabled {
  cursor: default;
  background: linear-gradient(180deg, #66789a, #53637f);
  box-shadow: 0 2px 0 #36425a;
  color: rgba(255, 255, 255, .55);
}
.board-btn:disabled > i { opacity: .55; filter: none; }
.board-btn:disabled:active { transform: none; }

/* Счётчик на верхней шашке, когда стопка не поместилась целиком. */
.chip .count {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  font-size: min(2.6cqw, 14px);
  font-weight: 800;
  color: #12202f;
  text-shadow: 0 1px 0 rgba(255, 255, 255, .5);
}
.chip.black .count { color: #f2f6fb; text-shadow: 0 1px 2px #000; }

/*
  Подсветка ходов. Пунктирных рамок нет намеренно (правка Влада 26.09): в оригинале
  доступный пункт помечается СТАТИЧНЫМ призраком шашки, а пункт, где стоит одинокая
  шашка соперника и её можно сбить, — красным (GameplayChips 1_3 против 1_2,
  BoardController.HighlightPlacements: Blot, если у соперника ровно одна шашка).
*/
.point.selected .stack .chip:last-of-type,
.bar-slot.selected .chip:last-of-type {
  outline: 3px solid var(--accent);
  outline-offset: -2px;
  border-radius: 50%;
  filter: drop-shadow(0 0 10px var(--accent));
}

/* Куда встанет шашка — призрак ровно в той позиции стопки, без мигания и рамок.
   Полупрозрачна только КАРТИНКА (слой ::before), сам узел непрозрачный: так ореол
   сбиваемой шашки не бледнеет вместе с ней (Влад, 28.09). */
.chip.ghost {
  background-image: none !important;
  filter: none;
}
.chip.ghost::before {
  content: '';
  position: absolute;
  inset: 0;
  background: var(--chip-img) center / contain no-repeat;
  opacity: .5;
}
/* В самой игре (не в разборе) — белый контур по краю призрака изнутри, без заливки (Влад, 03.10; было жёлтое пятно):
   на тёмных треугольниках полупрозрачная шашка иначе терялась. */
.chip.ghost:not(.trail)::after {
  content: '';
  position: absolute;
  inset: 5%;
  border-radius: 50%;
  box-shadow: inset 0 0 0 2px rgba(255, 255, 255, .9);
  pointer-events: none;
}
.replay-board .chip.ghost::after { content: none; }
.chip.ghost.blot {
  /* Сбиваемая шашка: ореол как у выбранной (рамка + свечение), только красный. */
  outline: 3px solid #ff4d3a;
  outline-offset: -2px;
  border-radius: 50%;
  filter: drop-shadow(0 0 10px #ff4d3a);
}
/* Свечение — поверх соседних пунктов: пункт с выбранной шашкой или с призраком-боем
   поднимается над остальными, иначе ореол уходил под шашку соседнего столбца. */
.point.selected, .bar-slot.selected, .point:has(.chip.ghost) { z-index: 3; }   /* призрак цели — поверх костей (30.09) */

/* ───────────────────────────────────────────────── куб удвоения */

/* Знак «x2» на карточке владельца куба — оригинальная подложка DoublingBack. */
.cube-badge[hidden] { display: none; }
.cube-badge {
  margin-left: 8px;
  min-width: 42px;
  padding: 3px 10px;
  background: center / 100% 100% no-repeat url("../assets/DoublingBack/DoublingBack_0.png");
  font: 800 15px/1.25 var(--font);
  text-align: center;
  color: #fff;
  display: inline-block;
  text-shadow: 0 1px 2px rgba(0, 0, 0, .55);
}

.offer { display: grid; gap: 12px; justify-items: center; }
.offer-cube {
  min-width: 78px;
  padding: 10px 16px;
  background: center / 100% 100% no-repeat url("../assets/DoublingBack/DoublingBack_0.png");
  font: 900 26px/1 var(--font);
  color: #fff;
  text-shadow: 0 2px 4px rgba(0, 0, 0, .5);
  text-align: center;
}
.offer-text { margin: 0; text-align: center; white-space: pre-line; min-height: 4.2em; display: grid; place-items: center; }
.offer-text.offer-answer { font-weight: 700; animation: offer-answer .25s ease-out; }
.offer-text.is-yes { color: #7ee08a; }
.offer-text.is-no { color: #ff8a7a; }
@keyframes offer-answer { from { opacity: 0; } to { opacity: 1; } }

/*
  Сообщение на время. В оригинале это то же окно уведомления, что и остальные,
  только без кнопок — поэтому и здесь оно выглядит как окно, а не как тёмная
  плашка с текстом: та читалась как отладочный вывод поверх игры.
*/
.toast {
  position: fixed;
  left: 50%;
  /* Место задаёт скрипт (placeToast): по центру доски, низом — над рядом костей. */
  top: 32%;
  transform: translate(-50%, calc(-100% + 10px));
  max-width: none;
  box-sizing: border-box;
  padding: clamp(9px, 1.6vmin, 14px) clamp(12px, 2.4vmin, 20px);   /* ширину задаёт скрипт (placeToast) — одна на все сообщения */
  border-radius: 18px;
  background: linear-gradient(180deg, #233b62, #16294a);
  border: 1px solid rgba(160, 196, 245, .28);
  box-shadow: 0 18px 44px -16px #000, inset 0 1px 0 rgba(255, 255, 255, .1);
  color: var(--ink);
  font-size: clamp(15px, 2.4vmin, 21px);
  font-weight: 700;
  line-height: 1.35;
  text-align: center;
  text-shadow: 0 1px 2px rgba(0, 0, 0, .5);
  white-space: nowrap;
  overflow: hidden;
  opacity: 0;
  pointer-events: none;
  transition: opacity .2s ease, transform .2s ease;
  z-index: 60;
}
.toast.show { opacity: 1; transform: translate(-50%, -100%); }
/* Плашка — сообщение доски; поверх окон (настройки, итог, удвоение) ей не место (Влад, 04.10: «Ходов нет» вылезала
   поверх настроек, открытых прямо перед надписью). Пока окно открыто — не видна; закрыли раньше таймера — дочитывается. */
body.modal-open .toast { visibility: hidden; }

/* ───────────────────────────────────────────────── подсказка и итог */

/* Строка-подсказка выключена (см. main.js render): место отдано доске. */
.hint { display: none; }
.hint {
  grid-area: hint;
  text-align: center;
  min-height: 1.3em;
  font-size: 13px;
  color: var(--ink-dim);
}
.hint.strong { color: var(--accent); font-weight: 700; }

.bottom-bar {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
}

.btn {
  appearance: none;
  border: 1px solid var(--panel-line);
  background: var(--panel);
  color: var(--ink);
  padding: 7px 14px;
  border-radius: 10px;
  font: inherit;
  font-weight: 600;
  cursor: pointer;
}
.btn:hover:not(:disabled) { border-color: var(--accent); }
.btn:disabled { opacity: .35; cursor: default; }

/* ───────────────────────────────────────────────── окна поверх игры */

/*
  Появление окон. В оригинале окно итога выезжает снизу (DOAnchorPosY 0.85 с, OutCubic)
  и проявляется (0.65 с), а остальные окна просто проявляются (Fade, 0.5 с). Здесь то же,
  только короче: 0.85 с на телефоне читается как задержка, а не как движение.
*/
@keyframes overlay-in { from { opacity: 0; } }
@keyframes window-in {
  from { opacity: 0; transform: translateY(10px) scale(.98); }
}
/* Окно, которое сейчас под другим окном (не верхнее и не экран-основа). visibility,
   а не display: так при возврате к нему анимация появления не перезапускается. */
.overlay.buried { visibility: hidden; }
/* Окно итога ждёт первой отрисовки на первом кадре анимаций (screens.js open, 04.10): иначе начало проявления
   пропадало, пока видеокарта рисовала окно, и оно «выскакивало». */
.overlay.prep, .overlay.prep * { animation-play-state: paused !important; }

.overlay {
  position: fixed;
  inset: 0;
  z-index: 50;
  animation: overlay-in .18s ease-out;
  /* Flex, а не grid: у grid свойство place-items доходит до содержимого окна и
     сжимает шапку, тело и подвал до ширины текста. У flex такого свойства нет. */
  display: flex;
  align-items: center;
  justify-content: center;
  padding: calc(12px + var(--safe-top)) 12px
           calc(12px + var(--banner) + var(--safe-bottom));
  /* Без размытия фона (батарея, 28.09): на слабых видеокартах backdrop-filter дорог.
     Затемнение чуть плотнее вместо него. */
  background: rgba(4, 10, 22, .76);
}

/* Без затемнения: меню — это целый экран, а не окно поверх партии. */
/* До открытия меню при запуске игровой экран не показывается — иначе он мелькал. */
body:not(.ready) #app { visibility: hidden; }

.overlay.bare {
  /* Меню появляется сразу, без проявления: угловые кнопки меню и игры стоят в одних
     точках, и при переходе «игра → меню» они не должны гаснуть и загораться (мигание). */
  animation: none;
  background: none;
  backdrop-filter: none;
  align-items: stretch;
  /* Меню — целый экран: края те же, что у игрового экрана, чтобы угловые кнопки
     стояли в тех же точках. */
  padding-top: calc(var(--edge-y) + var(--safe-top));
  padding-inline: calc(var(--edge-x) + var(--safe-left)) calc(var(--edge-x) + var(--safe-right));
}

.window {
  width: min(460px, 100%);
  max-height: 100%;
  animation: window-in .32s cubic-bezier(.2, .9, .3, 1);
  /*
    Правила, статистика и оформление — одной высоты. Иначе окно прыгает при каждом
    переключении страницы правил и при смене режима в статистике: содержимое там
    разной длины, а рамка от этого меняться не должна.
  */
  display: grid;
  grid-template-rows: auto 1fr auto;
  border-radius: 18px;
  background: linear-gradient(180deg, #1d3152, #132340);
  border: 1px solid rgba(160, 196, 245, .22);
  box-shadow: 0 24px 60px -20px #000, inset 0 1px 0 rgba(255, 255, 255, .08);
  overflow: hidden;
}

.window-head {
  display: grid;
  grid-template-columns: 44px 1fr 44px;
  align-items: center;
  padding: 10px 10px 4px;
}

.window-title {
  margin: 0;
  text-align: center;
  font-size: 17px;
  font-weight: 800;
  letter-spacing: .3px;
}

.window-back {
  appearance: none;
  width: 40px; height: 40px;
  border: none;
  border-radius: 12px;
  background: linear-gradient(180deg, var(--blue-1), var(--blue-2));
  box-shadow: 0 2px 0 #21486f;
  cursor: pointer;
  display: grid;
  place-items: center;
}
.window-back::after {
  content: "";
  width: 46%;
  height: 46%;
  background: center / contain no-repeat url("../assets/settings/settings_2.png");
}
.window-back:active { transform: translateY(2px); box-shadow: none; }

.window-body {
  min-width: 0;
  padding: 8px 16px 14px;
  overflow-y: auto;
  overflow-x: hidden;
  overscroll-behavior: contain;
}

.window-foot {
  padding: 10px 16px calc(14px + var(--safe-bottom));
  border-top: 1px solid rgba(160, 196, 245, .14);
  display: grid;
  gap: 8px;
}

body.modal-open #app { filter: saturate(.7) brightness(.8); }
/* Под меню доска партии не видна: у меню своя декорация, и две доски сразу — каша. */
body.modal-open:has(.window--menu) #app { visibility: hidden; }

/* ───────────────────────────────────────────────── главное меню */

/*
  Композиция оригинала: иконки по углам, логотип по центру, крупные кнопки внизу.
  Окно без рамки — фон экрана и есть фон меню.
*/
/* У окна-экрана единственный ребёнок, и он должен занять всю высоту, а не строку
   по содержимому: иначе меню считает свой рост само и кнопки уходят за край. */
.window--menu { grid-template-rows: minmax(0, 1fr); }

.window--about,
.window--rules,
.window--statistic,
.window--style,
.window--external,   /* разбор партий (.mat) — такой же экран, как история (Влад, 02.10) */
.window--history {   /* история: при смене фильтра (длинные/короткие) список разной длины — окно не прыгает (30.09) */
  height: min(86dvh, 780px);
}

/* Картинка забирает свободную высоту, текст стоит на месте — страницы правил
   разной длины, но рамка и стрелки от этого не должны ездить. */
.window--rules .rules {
  height: 100%;
  width: 100%;
  min-width: 0;
  display: grid;
  /* Три строки — переключатель, картинка, текст. Было две при трёх детях: текст уходил
     в неявную строку, а картинка с пропорцией 1.3 не слушалась своей строки и на
     низких экранах (Redmi, SE) наезжала на текст. */
  grid-template-rows: auto minmax(0, 1fr) auto;
  grid-template-columns: minmax(0, 1fr);
  gap: 10px;
  overflow-y: auto;     /* страховка: длинная страница на низком экране прокручивается */
}
/* Картинка ОДНОГО размера на всех страницах (Влад, 28.09: «картинка постоянно меняет
   размер в зависимости от текста»). Высота — доля экрана, а не остаток после текста;
   длинный текст прокручивается под ней, а не сжимает её. */
.window--rules .rules { grid-template-rows: auto clamp(80px, 17dvh, 170px) minmax(0, 1fr); }
.window--rules .rules-text { overflow-y: auto; min-height: 0; align-self: stretch; }
.window--rules .rules-text { font-size: clamp(12px, 3.4vw, 13.5px); line-height: 1.38; }
.window--rules .rules > * { min-width: 0; }
.window--rules .rules-picture {
  min-height: 0;
  aspect-ratio: auto;   /* высоту даёт строка сетки, а не ширина */
  height: 100%;
  background-position: center;
  background-size: contain;
  background-repeat: no-repeat;
}
.window--rules .rules-text { align-self: start; }

/* Окно итога выезжает заметнее — это момент, ради которого игра и шла. */
.window--result { animation-duration: .55s; }

/*
  Меню занимает ВЕСЬ экран, а не карточку посередине: навигационные иконки должны
  стоять в углах экрана, а не в углах узкой колонки — на планшете это и выглядело
  «странно расположенным». Ширину ограничивает только блок действий.
*/
.window.chromeless {
  /* Меню — экран, а не окно: без выезда и без проявления (см. .overlay.bare). */
  animation: none;
  position: relative;
  width: 100%;
  max-width: none;
  height: 100%;
  background: none;
  border: none;
  box-shadow: none;
  display: block;
  overflow: visible;
}
/* В меню «назад» — это «вернуться в партию». Ставим её отдельной строкой сверху,
   чтобы она не налезала на иконки по углам. */
.window-back.floating {
  position: static;
  margin: 0 14px 6px auto;
}

/*
  Фон меню: доски из углов и рассыпанные шашки. Это НЕ живая доска партии — в оригинале
  у меню своя декорация, и сквозь неё не должно быть видно текущую позицию.
*/
.menu-decor {
  /* Доски и шашки с фона убраны (решение Влада 26.09): остаётся деревянный фон. */
  display: none;
  position: fixed;
  inset: 0;
  overflow: hidden;
  pointer-events: none;
  z-index: 0;
}
/* Свойства фона по отдельности, а не короткой записью: `background` сбрасывает
   background-image в none, и по специфичности перебивал картинку у .decor-board. */
.menu-decor i {
  position: absolute;
  background-position: center;
  background-size: contain;
  background-repeat: no-repeat;
  opacity: .85;
  filter: saturate(.9) brightness(.85);
}

/* Содержимое меню поверх декорации. */
.menu > :not(.menu-decor) { position: relative; z-index: 1; }
.decor-board { background-image: url("../assets/gboards/board_wood_ru.png"); }
.decor-chip { background-image: url("../assets/GameplayChips/GameplayChips%201_1.png"); }
.decor-chip:nth-of-type(odd) { background-image: url("../assets/GameplayChips/GameplayChips%201_0.png"); }

.menu {
  position: relative;
  /* Именно height, а не min-height: у сетки с высотой «по содержимому» строка 1fr
     считается от содержимого, и логотип перестаёт сжиматься. */
  height: 100%;
  display: grid;
  /* minmax(0,1fr), а не 1fr: иначе логотип не даёт строке сжаться и в горизонтали
     кнопки уезжают за нижний край экрана. */
  grid-template-rows: auto minmax(0, 1fr) auto;
  gap: clamp(12px, 3vmin, 28px);
  --menu-pad: clamp(14px, 2.5vmax, 40px);
  padding: 0 var(--menu-pad);
}

.menu-corners {
  display: flex;
  justify-content: space-between;
  gap: 10px;
  /* Угловые кнопки — к самому краю экрана, как в игре; отступ меню только для содержимого. */
  margin-inline: calc(-1 * var(--menu-pad));
}
.menu-corner { display: flex; gap: 10px; }

/* Кнопки-иконки растут по БОЛЬШЕЙ стороне: на планшете vmin даёт телефонный размер. */
.menu-icon {
  appearance: none;
  width: var(--corner-btn);
  aspect-ratio: 1;
  border: none;
  border-radius: 14px;
  background: linear-gradient(180deg, var(--blue-1), var(--blue-2));
  box-shadow: 0 3px 0 #21486f, 0 6px 14px -6px #000;
  cursor: pointer;
  display: grid;
  place-items: center;
}
.menu-icon > i {
  width: 64%;
  height: 64%;
  background: center / contain no-repeat;
}
.menu-icon:active { transform: translateY(2px); box-shadow: 0 1px 0 #21486f; }

.menu-logo {
  align-self: center;
  justify-self: center;
  width: min(84%, 38rem);
  /* Своя строка может оказаться ниже, чем просит пропорция (горизонталь телефона):
     фон рисуется contain, поэтому логотип просто станет меньше и останется по центру. */
  max-height: 100%;
  aspect-ratio: 334 / 224;   /* логотип «Нарды Прайм» */
  background: center / contain no-repeat;
  /* Чуть приглушён по яркости (Влад, 30.09; насыщенность вернули) — картинка не тронута. */
  filter: brightness(.93) drop-shadow(0 10px 22px rgba(0, 0, 0, .5));
}

/* Блок действий — колонка комфортной ширины по центру: во всю ширину планшета
   кнопка в полметра читается как ошибка вёрстки. */
.menu-actions {
  display: grid;
  gap: clamp(10px, 1.4vmax, 20px);
  width: min(100%, 34rem);
  margin-inline: auto;
}

/* ───────────────────────────────────────────────── строки и поля */

.fields { display: grid; gap: 8px; grid-template-columns: minmax(0, 1fr); }   /* строки не шире окна: на 320 px «Режим игры» + переключатель распирали колонку до 272 px при 251 (05.10) */

.field-row {
  display: grid;
  /* minmax(0, 1fr): подпись сжимается и переносится, а не распирает строку — на 320 px
     строки «Новой игры» и настроек вылезали за окно на 14–19 px (замер 27.09). */
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: center;
  gap: 10px;
  min-height: 46px;
  padding: 6px 12px;
  border-radius: 12px;
  background: rgba(6, 15, 30, .55);
  border: 1px solid rgba(160, 196, 245, .12);
}
.field-row > .field-label { grid-column: 2; }
.field-row > .field-icon { grid-column: 1; }
.field-row > :not(.field-label):not(.field-icon) { grid-column: 3; }

.field-icon {
  width: 22px; height: 22px;
  background: center / contain no-repeat;
  opacity: .85;
}

.field-label { font-size: 14px; overflow-wrap: break-word; hyphens: auto; }
/* Строка с переключателем — всегда в одну линию (Влад 27.09: два яруса на Redmi/SE
   «разъезжались» рядом с однострочными). Чтобы влезало на узком экране, переключатель
   занимает фиксированную долю строки, а длинное значение («Друг (по очереди)»)
   переносится внутри него, а не распирает строку. Стрелки при этом стоят на месте:
   ширина колонки не зависит от текста. */
/* Одинаковая доля строки у всех переключателей на ВСЕХ экранах: иначе на планшете
   ширина шла от самого длинного значения каждого, и стрелки строк стояли вразнобой. */
.field-row:has(.selector) { grid-template-columns: auto minmax(0, 1fr) minmax(0, 1.15fr); }
.field-row:has(.selector) .selector { grid-template-columns: 32px minmax(0, 1fr) 32px; }
.field-row:has(.selector) .selector-value { white-space: normal; line-height: 1.15; }
@media (max-width: 420px) {
  .field-row:has(.selector) { gap: 6px; padding-inline: 10px; }
  .field-row:has(.selector) .selector { grid-template-columns: 26px minmax(0, 1fr) 26px; gap: 2px; }
  .field-row:has(.selector) .selector-value { font-size: 13px; }
  .field-row:has(.selector) .field-label { font-size: 13px; }
  /* Строки без значка с переключателем (Куб удвоения в «Новой игре») — те же отступ и шрифт подписи, что у строк со
     стрелками: подпись стояла на 6 px правее остальных (Влад, 03.10). */
  .field-row:not(:has(.selector)):not(:has(.field-icon)) { gap: 6px; padding-inline: 10px; }
  .field-row:not(:has(.selector)):not(:has(.field-icon)) .field-label { font-size: 13px; }
}

.field-block { display: grid; gap: 8px; padding: 10px 2px; }

.text-input {
  width: 150px;
  padding: 7px 10px;
  border-radius: 9px;
  border: 1px solid rgba(160, 196, 245, .25);
  background: rgba(3, 9, 20, .7);
  color: var(--ink);
  font: inherit;
  text-align: right;
}

/* Переключатель «да/нет». */
.switch {
  appearance: none;
  width: 54px; height: 30px;
  border-radius: 999px;
  border: 1px solid rgba(160, 196, 245, .25);
  background: rgba(3, 9, 20, .8);
  position: relative;
  cursor: pointer;
  transition: background .18s;
}
.switch .knob {
  position: absolute;
  top: 3px; left: 3px;
  width: 22px; height: 22px;
  border-radius: 50%;
  background: #8ea7c8;
  transition: transform .18s, background .18s;
}
.switch.on { background: linear-gradient(180deg, #3f8f4a, #2c6c37); border-color: #64c074; }
.switch.on .knob { transform: translateX(24px); background: #eafbe9; }

/* Выбор стрелками. */
.selector {
  display: grid;
  grid-template-columns: 32px minmax(92px, auto) 32px;
  align-items: center;
  gap: 6px;
}
.selector-cell { display: grid; }
.selector-cell > .selector-value { grid-area: 1 / 1; align-self: center; min-width: 0; }
/* Длинное слово («Горизонтальная») переносится по слогам, а не вылезает за стрелку:
   иначе ячейка раздувалась по нему, и короткое значение («Авто») стояло не по центру
   между стрелками (Влад, 28.09). */
.selector-cell { min-width: 0; }
.selector-value { hyphens: auto; overflow-wrap: break-word; }
.selector-value.sizer { visibility: hidden; }
.selector-value {
  text-align: center;
  font-weight: 700;
  font-size: 14px;
  white-space: nowrap;
}
.arrow {
  appearance: none;
  border: none;
  width: 30px; height: 30px;
  background: center / 62% no-repeat;
  cursor: pointer;
  opacity: .9;
}
.arrow:active { transform: scale(.92); }

/* Крупные кнопки меню. */
.big-btn {
  appearance: none;
  border: none;
  width: 100%;
  /* Растёт по БОЛЬШЕЙ стороне экрана: на планшете 7vmin давало телефонные 56 px. */
  min-height: clamp(52px, 5.4vmax, 92px);
  border-radius: 14px;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  color: #fff;
  font: 800 clamp(16px, 1.9vmax, 28px)/1 var(--font);
  cursor: pointer;
  background: linear-gradient(180deg, var(--blue-1), var(--blue-2));
  box-shadow: 0 4px 0 #21486f, 0 10px 22px -10px #000;
}
.big-btn.green { background: linear-gradient(180deg, #57b862, #3b8544); box-shadow: 0 4px 0 #2b6733, 0 10px 22px -10px #000; }
.big-btn.orange { background: linear-gradient(180deg, #f0973a, #c56d1c); box-shadow: 0 4px 0 #94510f, 0 10px 22px -10px #000; }
.big-btn:active { transform: translateY(3px); box-shadow: 0 1px 0 #21486f; }
.big-btn-icon { width: 22px; height: 22px; background: center / contain no-repeat; }

/* ───────────────────────────────────────────────── оформление */

/* 10 досок (21.10): по 4 в ряд, крупно (Влад: «по 5 — мелко»); неполный последний ряд — по центру. */
.board-grid { display: flex; flex-wrap: wrap; justify-content: center; gap: 8px; }
.board-grid > * { flex: 0 0 calc((100% - 24px) / 4); }
.board-pick {
  appearance: none;
  border: 2px solid transparent;
  border-radius: 10px;
  aspect-ratio: .9;
  background: center / contain no-repeat;
  cursor: pointer;
}
.board-pick.picked { border-color: var(--accent); box-shadow: 0 0 0 2px var(--accent-soft); }

.avatar-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 8px; }
.avatar-pick {
  appearance: none;
  border: 2px solid transparent;
  border-radius: 50%;
  aspect-ratio: 1;
  overflow: hidden;
  background: linear-gradient(160deg, #7fb2e8, #3d6da8);
  cursor: pointer;
  padding: 0;
}
.avatar-pick img { width: 100%; display: block; margin-top: 8%; }

/*
  Цвета фона — это не аватары: спрайт сам по себе круглый и занимает всю кнопку.
  Синяя подложка и сдвиг вниз, нужные силуэту аватара, здесь только портили вид.
*/
.avatar-grid.colors .avatar-pick { background: none; }
/* Тот же сдвиг круга, что и под ободком: картинка больше круга справа и снизу. */
.avatar-grid.colors .avatar-pick img { margin-top: 0; width: 102.44%; height: 103.61%; max-width: none; object-fit: fill; }
.avatar-pick.picked { border-color: var(--accent); box-shadow: 0 0 0 2px var(--accent-soft); }

/* ───────────────────────────────────────────────── статистика */

.stats { display: grid; gap: 2px; margin-top: 6px; }
.stat-section { margin: 8px 2px 2px; font-size: 12px; font-weight: 700; color: var(--ink-dim); text-transform: uppercase; letter-spacing: .04em; }
.stats > .stat-group:first-child > .stat-section { margin-top: 0; }
/* Группы разделов статистики, ветки обучения, части «Об игре» (21.10) — в вертикали прозрачны: раскладка как была. */
.stat-group, .tut-group, .about-side, .about-text { display: contents; }
.dice-stats { display: grid; gap: 2px; }
.dice-row { display: grid; grid-template-columns: 1fr 64px 64px; gap: 6px; padding: 7px 12px; border-radius: 9px;
  background: rgba(6, 15, 30, .45); font-size: 13px; }
.dice-row > span:not(.dice-label) { text-align: right; font-weight: 700; }
.dice-head { background: none; padding-top: 0; padding-bottom: 0; font-size: 11px; color: var(--ink-dim); }
.dice-head > span { font-weight: 400 !important; }
.dice-label { color: var(--ink-dim); }
.dice-faces { display: grid; grid-template-columns: repeat(6, 1fr); gap: 4px; margin-top: 2px; }
.dice-face { display: grid; justify-items: center; gap: 1px; padding: 6px 0; border-radius: 9px; background: rgba(6, 15, 30, .45); font-size: 12px; }
.dice-face b { font-size: 14px; }
.dice-face .dim { color: var(--ink-dim); }
.dice-note { font-size: 11px; line-height: 1.35; color: var(--ink-dim); padding: 2px 4px; }
.stat-row {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 10px;
  padding: 8px 12px;
  border-radius: 9px;
  background: rgba(6, 15, 30, .45);
  font-size: 13px;
}
.stat-name { color: var(--ink-dim); }
.stat-value { font-variant-numeric: tabular-nums; }

/* ───────────────────────────────────────────────── правила */

.rules { display: grid; gap: 10px; }
.rules-picture {
  aspect-ratio: 1.3;
  border-radius: 12px;
  background: center / contain no-repeat;
}
.rules-text { font-size: 13.5px; line-height: 1.5; white-space: pre-line; color: #dbe6f5; }
.rules-nav { display: grid; grid-template-columns: 40px 1fr 40px; align-items: center; }
.rules-counter { text-align: center; color: var(--ink-dim); font-variant-numeric: tabular-nums; }

/* ───────────────────────────────────────────────── итог партии */

/* overflow: hidden — вращающееся сияние за флагом шире окна; без обрезки оно раздувало
   прокручиваемую область, и в окне победы появлялась непонятная прокрутка. */
.result { display: grid; justify-items: center; gap: 10px; padding: 6px 0 2px; }
/* Сияние обрезает сама карточка окна (у .window overflow: hidden и скруглённые углы),
   а не блок содержимого — иначе у сияния была видна жёсткая горизонтальная граница.
   Тело окна итога не прокручивается: содержимое помещается во всех раскладках (замер). */
.window--result .window-body { overflow: visible; }
.result-banner {
  position: relative;
  display: grid;
  place-items: center;
  width: 100%;
}

@media (prefers-reduced-motion: reduce) {
  .overlay, .window { animation: none; }
  .chip { transition: none; }
}

/*
  Пропорция — ровно как у спрайта (722×190), чтобы картинка не вписывалась с полями.
  Надпись центрируется по полосе ленты: полоса занимает верхние ~73% картинки
  (замер по альфе: центр на 36.5% высоты), ниже — загнутые концы. Поэтому строки 73/27.
*/
.result-flag {
  position: relative;
  z-index: 1;
  width: 100%;
  max-width: 320px;
  aspect-ratio: 722 / 190;
  background: center / contain no-repeat;
  display: grid;
  grid-template-rows: 73fr 27fr;
  place-items: center;
}
.result-flag span {
  font-weight: 900;
  font-size: 18px;
  letter-spacing: .5px;
  text-shadow: 0 2px 4px rgba(0, 0, 0, .55);
}
.result-winner { display: grid; justify-items: center; gap: 2px; text-align: center; }
.result-winner span {
  font-size: 12px; font-weight: 700; letter-spacing: .8px; text-transform: uppercase;
  color: var(--ink-dim);
}
.result-winner b { font-size: 24px; font-weight: 900; line-height: 1.15; overflow-wrap: anywhere; }
.result-points {
  font-size: 17px; font-weight: 800; color: var(--accent);
  /* Надпись с очками — вторым шагом, после окна; счёт на табло меняется третьим. */
  animation: result-points-in .45s .7s cubic-bezier(.2, .8, .3, 1.3) both;
}
@keyframes result-points-in { from { opacity: 0; transform: scale(.6); } to { opacity: 1; transform: none; } }
.result-num.bump { animation: result-bump .5s cubic-bezier(.2, .8, .3, 1.4); color: var(--accent); }
@keyframes result-bump { 0% { transform: scale(1); } 40% { transform: scale(1.45); } 100% { transform: scale(1); } }

/* Табло: слева игрок, справа соперник, победитель раунда подсвечен и получает «+N». */
.result-board {
  width: 100%;
  margin-top: 4px;
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
  align-items: center;
  gap: 6px;
  padding: 12px 10px 10px;
  border-radius: 14px;
  background: rgba(4, 11, 24, .45);
  border: 1px solid var(--panel-line);
}
.result-side { position: relative; display: grid; justify-items: center; gap: 4px; min-width: 0; opacity: .62; }
.result-side.won { opacity: 1; }
.result-ava .ava-wrap { width: 58px; min-width: 0; }
.result-side.won .result-ava .ava-wrap { filter: drop-shadow(0 0 8px var(--accent)); }
.result-name {
  max-width: 100%; font-size: 13px; color: var(--ink-dim);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.result-side.won .result-name { color: var(--accent); font-weight: 700; }
.result-num { font-size: 30px; font-weight: 900; line-height: 1; font-variant-numeric: tabular-nums; }
.result-plus {
  position: absolute; top: -4px; right: calc(50% - 46px);
  padding: 2px 7px; border-radius: 999px;
  background: var(--accent); color: #3a2600;
  font-style: normal; font-weight: 900; font-size: 13px;
  box-shadow: 0 2px 6px rgba(0, 0, 0, .45);
}
.result-colon { font-size: 26px; font-weight: 900; color: var(--ink-dim); align-self: end; padding-bottom: 2px; }
.result-actions { display: grid; gap: 8px; }

.confirm p { margin: 8px 0 4px; text-align: center; white-space: pre-line; }

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

.debug-note {
  margin: 6px 0 0;
  text-align: center;
  font-size: 12px;
  color: var(--ink-dim);
}

/* Кнопка отладки (только с ?dev=1): в полосе под баннер, слева — там игра ничего не рисует. */
.debug-fab {
  position: fixed;
  left: calc(var(--edge-x) + var(--safe-left));
  bottom: calc(6px + var(--safe-bottom));
  z-index: 40;
  padding: 6px 12px;
  border-radius: 999px;
  border: 1px dashed rgba(240, 192, 74, .7);
  background: rgba(9, 20, 38, .8);
  color: var(--accent);
  font: 700 13px/1 var(--font);
  cursor: pointer;
}
body.modal-open:has(.window--menu) .debug-fab { display: none; }

/* Невидимая кнопка, держащая место в меню (см. screens.js). */
.big-btn.menu-slot { visibility: hidden; pointer-events: none; }

/* Конфетти (confetti.js): поверх всего, но нажатия проходят к кнопкам окна. */
.confetti { position: fixed; inset: 0; width: 100%; height: 100%; z-index: 90; pointer-events: none; overflow: hidden; contain: strict; }
/* Бумажка (ui/confetti.js): внешний элемент летит по траектории, внутренний — сам листок, переворачивается. */
.confetti > i { position: absolute; left: 0; top: 0; opacity: 0; }
.confetti > i > b { display: block; }

/* ───────────────────────────────────────────────── история партий */

.window--history { height: min(86dvh, 780px); }
.history-list { display: grid; gap: 8px; }
.history-empty { margin: 18px 0; text-align: center; color: var(--ink-dim); }
.history-row {
  appearance: none;
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  gap: 10px;
  padding: 10px 12px;
  border-radius: 12px;
  border: 1px solid var(--panel-line);
  border-left: 4px solid var(--ink-dim);
  background: rgba(4, 11, 24, .45);
  color: var(--ink);
  font: inherit;
  text-align: left;
  cursor: pointer;
}
.history-row.won { border-left-color: #57b862; }
.history-row.lost { border-left-color: #e8404c; }
.history-row.open { border-left-color: var(--accent); }
.history-row:active { transform: translateY(1px); }
.history-main { display: grid; gap: 2px; min-width: 0; }
.history-when { font-weight: 700; }
.history-what { font-size: 13px; color: var(--ink-dim); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.history-side { display: grid; justify-items: end; gap: 2px; }
.history-score { font-size: 18px; font-variant-numeric: tabular-nums; }
.history-status { font-size: 12px; font-weight: 700; }
.history-row.won .history-status { color: #7fd48a; }
.history-row.lost .history-status { color: #ff8a8a; }
.history-row.open .history-status { color: var(--accent); }

/* Просмотр партии: своя доска в окне. Контейнер доски — с явной высотой, доска
   вписывается в него сама (container-type у .board-wrap). */
.window--replay { width: min(520px, 100%); }
.replay { display: grid; gap: 8px; }
.replay-board { width: 100%; height: min(56dvh, 560px); }
.replay-caption { text-align: center; font-weight: 700; }
.replay-nav { display: flex; justify-content: center; gap: 10px; }
.replay-btn {
  appearance: none;
  min-width: 52px;
  padding: 8px 12px;
  border: none;
  border-radius: 12px;
  background: linear-gradient(180deg, var(--blue-1), var(--blue-2));
  box-shadow: 0 3px 0 #21486f;
  color: #fff;
  font-size: 18px;
  cursor: pointer;
}
.replay-btn:disabled { opacity: .35; cursor: default; }
.replay-btn:active:not(:disabled) { transform: translateY(2px); box-shadow: 0 1px 0 #21486f; }
.replay-note { text-align: center; font-size: 13px; color: #ff8a7a; min-height: 1em; }

.replay-info { text-align: center; font-size: 13px; color: var(--ink-dim); }

.replay-copy {
  appearance: none; justify-self: center; display: inline-flex; align-items: center; gap: 8px; padding: 10px 20px; border-radius: 999px;
  border: 0; background: linear-gradient(180deg, #4fae62, #3b8c4c); box-shadow: 0 3px 0 #255c31; color: #fff;
  font: 700 15px/1.2 var(--font); cursor: pointer;
}
.replay-copy:active { transform: translateY(2px); box-shadow: 0 1px 0 #255c31; }
/* Значок «скачать»: стрелка вниз над чертой. */
.replay-copy-ico { width: 18px; height: 18px; background: no-repeat center / contain url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='white' stroke-width='2.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 3v12'/%3E%3Cpath d='M6 10l6 6 6-6'/%3E%3Cpath d='M4 21h16'/%3E%3C/svg%3E"); }
.replay-mat { width: 100%; height: 120px; font: 11px/1.3 ui-monospace, monospace; background: rgba(3, 9, 20, .7); color: var(--ink); border-radius: 8px; }

/*
  Окна на планшете. Ширина окна задана в пикселях (до 460), и на экране 800–1024 они
  выглядели «необоснованно мелкими» (Влад, 27.09). На больших экранах окно увеличивается
  целиком — шрифты, кнопки, отступы разом, без переписывания каждого размера.
  Высота делится на тот же коэффициент, чтобы увеличенное окно всё равно помещалось.
*/
@media (min-width: 700px) and (min-height: 700px) {
  .window:not(.chromeless) { zoom: 1.35; --wz: 1.35; max-height: calc(100% / 1.35); }
  /* Кнопки окна и так растут от экрана (vmax) — вместе с увеличением окна выходили грузными. */
  .window:not(.chromeless) .big-btn { min-height: 52px; font-size: 18px; }
  .window--about, .window--rules, .window--statistic, .window--style, .window--history, .window--external { height: min(calc(86dvh / 1.35), 780px); }
}
@media (min-width: 1000px) and (min-height: 1000px) {
  .window:not(.chromeless) { zoom: 1.55; --wz: 1.55; max-height: calc(100% / 1.55); }
  .window--about, .window--rules, .window--statistic, .window--style, .window--history, .window--external { height: min(calc(86dvh / 1.55), 780px); }
}

/*
  Кости розыгрыша первого хода лежат НАД средней линией половины, а не в одном ряду
  с кнопками: иначе при появлении «Бросить» они делили место, и кость сжималась
  (Влад, 27.09). Кнопки и обычные кости остаются на линии — ничего не двигается.
*/
.board-half .opening-die {
  position: absolute;
  left: 50%;
  /* 03.10 (зона костей): по высоте — на линии костей, как обычный бросок, а не над ней: над серединой кость заходила
     в зону роста стопок (в коротких там стоят пятёрки). Кнопок в половине во время розыгрыша нет — делить место не с чем. */
  top: 50%;
  translate: -50% -50%;
  flex: none;
}
/* Пара после розыгрыша (03.10): кость победителя и переехавшая кость проигравшего — рядом, по центру половины. */
.board-half .opening-die.pair.first { translate: calc(-100% - 10%) -50%; }
.board-half .opening-die.pair.second { translate: 10% -50%; }

/*
  Свет победы: веер лучей (repeating-conic-gradient) за лентой, медленно и РАВНОМЕРНО
  вращается (один оборот за 40 с — не «бешено», без качания), края растворяются маской;
  в центре тёплое сияние дышит. Центр — на полосе ленты (36.5% её высоты).
  Обрезается карточкой окна (overflow: hidden), как и прежде.
*/
.result-rays {
  position: absolute;
  left: 50%;
  top: 36.5%;
  width: min(140%, 520px);
  aspect-ratio: 1;
  translate: -50% -50%;
  z-index: 0;
  pointer-events: none;
  animation: rays-in .9s cubic-bezier(.2, .8, .2, 1) both;
}
.result-rays .rays,
.result-rays .glow { position: absolute; inset: 0; border-radius: 50%; }
/* 01.10 (Влад: «дурацкие бесконечные лучи»): лучей нет. Победа — тёплое сияние за лентой: вспыхивает один раз
   при появлении окна и остаётся мягким и неподвижным. */
.result-rays .rays { display: none; }
.result-rays .glow {
  /* Сияние — за лентой, вытянутое по ней (Влад, 01.10: свечение должно быть под ленточкой, сверху). */
  inset: 33% 0%;
  border-radius: 50%;
  background: radial-gradient(closest-side, rgba(255, 222, 130, .42), rgba(255, 190, 80, .14) 55%, transparent 78%);
  animation: glow-flash 1.1s cubic-bezier(.2, .8, .2, 1) both;
}
.result-rays.lose { animation: none; }
.result-rays.lose .glow {
  background: radial-gradient(closest-side, rgba(150, 140, 255, .55), rgba(110, 120, 230, .20) 55%, transparent 80%);
  animation: glow-dim 1.8s ease-out both;
}
@keyframes glow-dim { 0% { opacity: 0; transform: scale(.92); } 100% { opacity: .9; transform: scale(1); } }
@keyframes glow-flash { 0% { opacity: 0; transform: scale(.6); } 45% { opacity: 1; transform: scale(1.12); } 100% { opacity: .8; transform: scale(1); } }
.result-flag { position: relative; z-index: 1; }
@keyframes rays-in { from { opacity: 0; transform: scale(.6) rotate(-25deg); } }
@keyframes rays-spin { to { transform: rotate(360deg); } }
@keyframes glow-breathe { from { opacity: .7; transform: scale(.94); } to { opacity: 1; transform: scale(1.06); } }
@media (prefers-reduced-motion: reduce) { .result-rays, .result-rays .rays, .result-rays .glow { animation: none; } }

/* ── Разбор партии в просмотре (src/analysis.js) ─────────────────────────── */
.review-summary { display: grid; grid-template-columns: 1fr 1fr; gap: 6px; }
.review-summary:has(.review-wait) { grid-template-columns: 1fr; }
.replay:has(.review-side) .replay-board { height: min(38dvh, 420px); }
.review-wait { text-align: center; font-size: 13px; color: var(--ink-dim); padding: 6px 0; }
.review-side {
  display: grid; grid-template-columns: auto 1fr; gap: 8px; align-items: center;
  padding: 6px 8px; min-width: 0; border-radius: 10px; background: rgba(3, 9, 20, .45);
}
.review-pr {
  min-width: 48px; padding: 4px 6px; border-radius: 8px; display: grid; justify-items: center; line-height: 1.05;
  color: #fff; background: rgba(94, 170, 255, .16); border: 1px solid rgba(124, 192, 245, .35);
}
.review-pr b { font-size: 19px; font-variant-numeric: tabular-nums; }
.review-pr small { font-size: 10px; letter-spacing: .08em; color: var(--ink-dim); }
.review-who { min-width: 0; }
.review-name { font-weight: 700; font-size: 14px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.review-luck { font-size: 11px; color: var(--ink-dim); }
.review-counts { display: flex; gap: 8px; font-size: 12px; font-weight: 700; }
.cnt { display: inline-flex; align-items: center; gap: 4px; white-space: nowrap; }
.cnt::before { content: ''; width: 8px; height: 8px; border-radius: 50%; background: currentColor; }
.cnt.inaccuracy { color: #f2d15c; } .cnt.mistake { color: #f59a3c; } .cnt.blunder { color: #ff6b5e; }
.review-legend { display: inline-flex; gap: 8px; font-weight: 400; }
.review-scale { font-size: 11px; line-height: 1.3; color: var(--ink-dim); text-align: center; }
.replay-verdict { text-align: center; font-size: 14px; line-height: 1.3; min-height: 2.6em; color: var(--ink-dim); display: grid; align-items: center; }
.replay-verdict.inaccuracy { color: #f2d15c; }
.replay-verdict.mistake { color: #f59a3c; }
.replay-verdict.blunder { color: #ff6b5e; font-weight: 700; }
.replay-best {
  justify-self: center; padding: 6px 14px; border-radius: 999px; border: 0; font: inherit; font-size: 13px;
  color: #fff; background: #3f7fc4; box-shadow: 0 2px 0 #21486f; cursor: pointer;
}
.replay-best[hidden] { display: none; }
.review-graph:empty { display: none; }
.review-graph-label { display: flex; justify-content: space-between; align-items: baseline; gap: 6px; flex-wrap: wrap; font-size: 11px; color: var(--ink-dim); margin-bottom: 3px; }
.review-graph-label > span:first-child { font-size: 13px; font-weight: 600; color: var(--ink); }   /* «Ваши шансы на победу» крупнее (Влад, 03.10) */
.review-graph svg { display: block; width: 100%; height: 76px;   /* было 64 — чуть выше (Влад, 03.10) */
  background: rgba(3, 9, 20, .45); border-radius: 8px; cursor: pointer; }
.review-graph .mid { stroke: rgba(255, 255, 255, .55); stroke-width: 1; vector-effect: non-scaling-stroke; stroke-dasharray: 3 3; }   /* 50% — ярче (Влад, 03.10) */
.review-graph .area { fill: rgba(94, 170, 255, .18); }
.review-graph .curve { fill: none; stroke: #7cc0f5; stroke-width: 1.6; vector-effect: non-scaling-stroke; }
.review-graph .cursor { stroke: #fff; stroke-width: 1; vector-effect: non-scaling-stroke; }
.review-graph .dot { stroke: rgba(0, 0, 0, .5); stroke-width: .8; vector-effect: non-scaling-stroke; }
.review-graph .dot.inaccuracy { fill: #f2d15c; }
.review-graph .dot.mistake { fill: #f59a3c; }
.review-graph .dot.blunder { fill: #ff6b5e; }
.review-graph .dot.theirs { fill-opacity: .35; }

/* Шашка, летящая в лоток (main.js, flyToTray): круглый спрайт на ходу сменяется боковым. */
.tray-fly { position: fixed; z-index: 50; pointer-events: none; transform-origin: center; }
.tray-fly i { position: absolute; inset: 0; background: center / contain no-repeat; }
.tray-fly .edge { background-size: 100% 100%; opacity: 0; }

/* След хода в просмотре партии: бледная шашка там, откуда ушли, кольцо на пришедшей. */
.chip.ghost.trail::before { opacity: .3; }
/* Просмотр партии: откуда ушла шашка — заметный призрак с пунктирным кольцом, куда пришла — светлое кольцо. */
.replay-board .chip.ghost.trail::before { opacity: .45; }
.replay-board .chip.ghost.trail { outline: 2px dashed rgba(255, 255, 255, .75); outline-offset: -3px; border-radius: 50%; }
.replay-board .chip.moved { outline: 3px solid rgba(124, 192, 245, 1); box-shadow: 0 0 10px rgba(124, 192, 245, .8); }
.chip.moved { outline: 2px solid rgba(124, 192, 245, .95); outline-offset: -2px; border-radius: 50%; }
/* Синяя обводка пришедшей шашки — поверх всего (Влад, 03.10): над соседними шашками стопки, соседними пунктами и костями. */
.chip.moved { z-index: 6; }
.point:has(.chip.moved), .bar-slot:has(.chip.moved) { z-index: 4; }
/* Верх правой колонки (пипы соперника) не под кнопкой «Настройки» в углу. */

/* Неактивная строка окна (сложность при игре с другом): видна, но погашена — окно не прыгает. */
.field-row.disabled { opacity: .4; pointer-events: none; }

/* ── Разбор партии, компоновка 30.09 (Влад: чётко, без воды, PR вместо балла) ─────────────── */
/* Портрет: колонка итог → доска → строка хода → вердикт → навигация → варианты → куб → график → низ. */
.replay-side { display: contents; }
.replay > .replay-info { order: 0; }
.replay .review-summary { order: 1; }
.replay .replay-side > .field-row { order: 2; }
.replay > .replay-board { order: 3; }
.replay .replay-caption { order: 4; font-size: 14px; }
.replay .replay-verdict { order: 5; min-height: 1.4em; font-size: 14px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; display: block; }
.replay .replay-nav { order: 6; flex-wrap: nowrap; gap: 8px; }
.replay .review-cands { order: 7; }
.replay .review-cube { order: 8; }
.replay .review-graph { order: 9; }
.replay .replay-foot { order: 10; display: grid; gap: 6px; }
.replay .replay-btn { min-width: 0; flex: 1 1 0; padding: 8px 6px; font-size: 16px; }
.replay .replay-btn.err { flex: 1.6 1 0; font-size: 14px; font-weight: 700; white-space: nowrap; background: linear-gradient(180deg, #d9534a, #a93a33); box-shadow: 0 3px 0 #6e231f; }
.replay .replay-btn.err:disabled { opacity: .3; }
.review-cands:empty, .review-cube:empty { display: none; }
.review-cands, .review-cube { display: grid; gap: 2px; padding: 6px; border-radius: 10px; background: rgba(3, 9, 20, .45); }
.cand-cap { font-size: 11px; color: var(--ink-dim); padding: 0 4px 2px; }
.cand {
  display: grid; grid-template-columns: minmax(0, 1fr) 4.4em 3.8em; gap: 6px; align-items: center;
  padding: 5px 6px; border: 0; border-radius: 7px; background: transparent; color: var(--ink);
  font: 13px/1.2 var(--font); text-align: right; font-variant-numeric: tabular-nums; cursor: pointer;
}
.cand > span:first-child { text-align: left; }
/* Запись хода не обрезается: четыре разных хода на дубле переносятся на вторую строку (30.09). */
.cand > .mv { text-align: left; font-weight: 600; white-space: normal; line-height: 1.15; word-spacing: .1em; letter-spacing: -.01em; }
.cand.head { cursor: default; font-size: 10px; color: var(--ink-dim); padding-top: 0; padding-bottom: 0; }
.cand.played { box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .35); }
.cand.on { background: rgba(94, 170, 255, .22); }
.cand .dl { color: #ffb4a8; }
.cube-line { font-size: 13px; padding: 2px 4px; }
.cube-line.inaccuracy { color: #f2d15c; } .cube-line.mistake { color: #f59a3c; } .cube-line.blunder { color: #ff6b5e; font-weight: 700; }
.cube-cells { display: grid; grid-template-columns: repeat(3, 1fr); gap: 4px; }
.cube-cell { display: grid; justify-items: center; padding: 4px; border-radius: 6px; background: rgba(255, 255, 255, .05); }
.cube-cell small { font-size: 10px; color: var(--ink-dim); } .cube-cell b { font-size: 13px; font-variant-numeric: tabular-nums; }
.review-summary .review-side { padding: 5px 7px; }
.review-luck { white-space: nowrap; }
/* Маленькие экраны: доска ниже, всё остальное плотнее. */
@media (max-height: 700px) {
  .replay:has(.review-side) .replay-board { height: min(34dvh, 300px); }
  .replay .replay-btn.err { font-size: 13px; padding: 8px 4px; }
  .replay { gap: 6px; }
}

/* ── PR в статистике (30.09) ─────────────────────────────────────────────── */
.pr-line { display: grid; grid-template-columns: 1fr; gap: 6px; align-items: center; }
.pr-main {
  display: flex; align-items: baseline; gap: 10px; padding: 10px 12px; border: 1px solid rgba(124, 192, 245, .35);
  border-radius: 12px; background: rgba(94, 170, 255, .12); color: var(--ink); font: inherit; cursor: pointer; text-align: left;
}
.pr-val { font-size: 26px; font-variant-numeric: tabular-nums; }
.pr-lbl { font-size: 13px; color: var(--ink-dim); flex: 1; }
.pr-trend { font-size: 13px; font-weight: 700; } .pr-trend.up { color: #7ed98a; } .pr-trend.down { color: #ff8a7a; }
.pr-go { font-size: 22px; color: var(--ink-dim); }
.pr-none { font-size: 13px; color: var(--ink-dim); padding: 8px 4px; }
.pr-detail { display: grid; gap: 10px; }
.pr-cells { display: grid; grid-template-columns: repeat(5, 1fr); gap: 6px; }
.pr-cell { display: grid; justify-items: center; gap: 2px; padding: 8px 4px; border-radius: 10px; background: rgba(3, 9, 20, .45); }
.pr-cell small { font-size: 11px; color: var(--ink-dim); } .pr-cell b { font-size: 18px; font-variant-numeric: tabular-nums; }
.pr-graph svg { display: block; width: 100%; height: auto; background: rgba(3, 9, 20, .45); border-radius: 8px; }
.pr-dot { stroke: rgba(3, 9, 20, .8); stroke-width: 1; } .pr-avg { fill: none; stroke: #f2d15c; stroke-width: 2.4; stroke-linejoin: round; opacity: .9; }
.pr-guide { stroke: rgba(255, 255, 255, .12); stroke-dasharray: 3 3; vector-effect: non-scaling-stroke; } .pr-guide-t { fill: rgba(255, 255, 255, .5); font-size: 9px; }

/* ── Куб удвоения (арт Влада, 30.09) ───────────────────────────────────── */
.goal-col { display: grid; justify-items: center; align-self: center; gap: 4px; min-width: 0; }
.cube-tile { display: inline-block; width: 34px; height: 34px; margin-left: 8px; vertical-align: middle; background: center / contain no-repeat; }
.cube-tile[hidden] { display: none; }
.cube-tile.center { width: 38px; height: 38px; margin: 0; }
.cube-tile.dim { opacity: .35; filter: grayscale(.6); }
.offer-cube-tile { display: block; width: 84px; height: 84px; background: center / contain no-repeat; }

/* Кости соперника с прошлого хода — убраны из раскладки, пока в их половине кнопка «Удвоить x2». */
.stale-hidden { display: none !important; }

/* Бьющая шашка соперника в момент боя (30.09): красный ободок и свечение на 0.9 с. */
.chip.hit-flash { border-radius: 50%; z-index: 4; animation: hit-glow .8s ease-out forwards; }
/* Удар (Влад, 03.10): не чёткая обводка, а размытое красное свечение вокруг бьющей шашки — вспыхнуло и погасло. */
@keyframes hit-glow {
  0% { box-shadow: 0 0 0 0 rgba(255, 60, 40, 0); }
  18% { box-shadow: 0 0 14px 6px rgba(255, 60, 40, .9); }
  100% { box-shadow: 0 0 22px 10px rgba(255, 60, 40, 0); }
}

/* Призрак цели на полной стопке (лежит внахлёст на верхней шашке) — ярким ободком, иначе его не видно (30.09). */
.chip.ghost.over { border-radius: 50%; outline: 3px solid rgba(255, 214, 90, .95); outline-offset: -2px; filter: drop-shadow(0 0 8px rgba(255, 214, 90, .8)); z-index: 3; }
.chip.ghost.over::before { opacity: .8; }

/* Варианты: с MWC — четыре колонки; раскрытая строка — полный расклад победы/поражения (30.09). */
.cand.mwc { grid-template-columns: minmax(0, 1fr) 4.4em 3.8em 4.2em; }
.cand-detail { display: grid; gap: 6px; margin: 2px 4px 6px; padding: 8px 10px; border-radius: 8px; background: rgba(255, 255, 255, .04); }
.cand-grid { display: grid; grid-template-columns: 1fr repeat(2, 4.2em); gap: 3px 6px; align-items: baseline; font-size: 13px; font-variant-numeric: tabular-nums; text-align: right; }
.cand-grid.kox { grid-template-columns: 1fr repeat(3, 4.2em); }
.cand-grid small { font-size: 10px; color: var(--ink-dim); }
.cand-grid b { text-align: left; font-weight: 600; }
.cand-grid .win { color: #7ed98a; } .cand-grid .loss { color: #ff8a7a; }
.cand-bar { display: flex; height: 8px; border-radius: 4px; overflow: hidden; background: rgba(255, 255, 255, .08); }
.cand-bar i { display: block; min-width: 0; }
.cand-bar .w1 { background: #4fae62; } .cand-bar .w2 { background: #7ed98a; } .cand-bar .w3 { background: #c8f5cf; }
.cand-bar .l1 { background: #b8483d; } .cand-bar .l2 { background: #ff8a7a; } .cand-bar .l3 { background: #ffc9c1; }

/* ── Разбор на весь экран (Влад, 30.09) ──────────────────────────────────────
   Окно не центрируется по высоте содержимого: его высота — весь экран без полосы баннера,
   поэтому доска стоит на месте, как бы ни менялось то, что под ней. Полоса прокрутки
   зарезервирована заранее (scrollbar-gutter) — её появление не сужает доску. */
.overlay.full {
  padding: var(--safe-top) var(--safe-right)
           calc(var(--banner) + var(--safe-bottom)) var(--safe-left);
  background: #16233c url("../assets/bg/bg_0.png") center / cover no-repeat;
  animation: none;
}
.overlay.full .window {
  width: min(620px, 100%); height: 100%; max-height: none;
  border-radius: 0; border: none; box-shadow: none; background: rgba(13, 25, 48, .55);
  animation: none;
}
.overlay.full .window-body { scrollbar-gutter: stable both-edges; padding-inline: 10px; }
.replay .replay-board, .replay:has(.review-side) .replay-board { height: min(42dvh, 460px); }
@media (max-height: 700px) { .replay .replay-board, .replay:has(.review-side) .replay-board { height: min(36dvh, 300px); } }
/* Место под итог (карточки PR) занято с самого начала: пока идёт разбор, там строка «Анализ…»,
   и доска не съезжает, когда карточки появляются. */
.replay .review-summary { min-height: 60px; align-content: center; }

/* Пипсы под доской: слева «моя» сторона, справа соперник. */
.replay > .replay-pips { order: 3; display: flex; justify-content: space-between; gap: 8px; margin-top: -4px; font-size: 13px; }
.replay-pip { display: flex; align-items: center; gap: 6px; min-width: 0; color: var(--ink-dim); }
.replay-pip span { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.replay-pip b { color: var(--ink); font-size: 15px; font-variant-numeric: tabular-nums; }
.replay-pip.theirs { flex-direction: row-reverse; }
.replay-pip .chip-icon { width: 16px; height: 16px; flex: none; background: center / contain no-repeat; }

/* Сыгранный ход в вариантах — золотой чертой слева (сам ход написан в строке хода). */
.cand.played { box-shadow: inset 3px 0 0 #f2c14e; }
/* Строка хода с записью хода: одна строка всегда (иначе доска и всё ниже прыгали бы). */
.replay .replay-caption { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.replay .replay-caption.long { font-size: 12.5px; }

/* ── Статистика: цвет PR по шкале, полоса, строка истории (Влад, 30.09) ── */
.pr-main { flex-wrap: wrap; row-gap: 8px; }
.pr-bar { position: relative; display: flex; flex-basis: 100%; height: 6px; border-radius: 3px; overflow: visible; gap: 2px; }
.pr-bar i { flex: 1; border-radius: 3px; opacity: .55; }
.pr-bar em { position: absolute; top: -4px; width: 4px; height: 14px; margin-left: -2px; border-radius: 2px; background: #fff; box-shadow: 0 0 0 2px rgba(10, 20, 40, .8); }
.history-link {
  /* Заметная, но спокойная (Влад, 01.10: «важная, но не ярко»): как строка панели, чуть светлее, с рамкой. */
  display: flex; align-items: center; gap: 12px; padding: 13px 14px; border: 1px solid rgba(124, 192, 245, .35); border-radius: 12px;
  background: rgba(94, 170, 255, .10); color: var(--ink);
  font: inherit; font-size: 16px; font-weight: 600; cursor: pointer; text-align: left;
}
.history-link:active { transform: scale(.98); }
.history-link span:not(.pr-go) { flex: 1; min-width: 0; white-space: normal; line-height: 1.2; overflow-wrap: break-word; }   /* на 320 px «Разобрать партию (.mat)» налезала на счётчик — переносится (05.10) */
/* Строки статистики — всегда в одну строку (Влад, 02.10: на SE «Разобрать партию (.mat)» переносилась): на узком экране
   шрифт чуть меньше, а не перенос. */
.history-link { font-size: clamp(13px, 4.1vw, 16px); }
.history-link b { min-width: 26px; padding: 2px 8px; border-radius: 10px; background: rgba(3, 9, 20, .45); color: var(--ink-dim); font-size: 13px; text-align: center; font-variant-numeric: tabular-nums; }
/* Значок истории: часы со стрелкой назад. */
.history-ico { flex: none; width: 22px; height: 22px; background: no-repeat center / contain url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%237cc0f5' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3 12a9 9 0 1 0 3-6.7'/%3E%3Cpath d='M3 4v5h5'/%3E%3Cpath d='M12 7v5l3 3'/%3E%3C/svg%3E"); }
.history-ico.import { background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%237cc0f5' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M14 3H7a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V8z'/%3E%3Cpath d='M14 3v5h5'/%3E%3Cpath d='M9 13h6M9 17h4'/%3E%3C/svg%3E"); }
.pr-explain { flex-basis: 100%; font-size: 13px; line-height: 1.4; color: #dbe6f5; }   /* было бледно (Влад) */
.pr-scale { display: grid; gap: 5px; flex-basis: 100%; }
.pr-ends { display: flex; justify-content: space-between; font-size: 11px; color: var(--ink-dim); }

/* Разбор идёт — экран закрыт (Влад, 30.09): плашка с полосой, шапка с «Назад» открыта. */
.review-block {
  position: fixed; left: 0; right: 0; bottom: 0; top: calc(var(--safe-top) + 60px); z-index: 5;
  display: grid; place-items: center; background: rgba(8, 16, 32, .72); padding: 16px;
}
.review-block-card { display: grid; gap: 10px; justify-items: center; width: min(320px, 100%); padding: 18px 16px;
  border-radius: 16px; background: #182a48; border: 1px solid rgba(160, 196, 245, .22); }
.review-block-title { font-weight: 700; font-size: 16px; }
.review-block-bar { width: 100%; height: 10px; border-radius: 5px; background: rgba(255, 255, 255, .1); overflow: hidden; }
.review-block-bar i { display: block; height: 100%; width: 0; border-radius: 5px; background: linear-gradient(90deg, #5cb8e6, #8fdca0); transition: width .2s linear; }
.review-block-card b { font-variant-numeric: tabular-nums; font-size: 15px; color: var(--ink-dim); }
.review-block[hidden] { display: none; }

/* ── Разбор: уровень, «Проверить глубже», список ходов (Влад, 30.09: аккуратно, без засорения) ── */
.replay .review-level { order: 1; display: flex; justify-content: center; align-items: center; gap: 6px; min-height: 22px;
  margin-top: -2px; font-size: 13px; color: var(--ink-dim); }
.replay .review-level[hidden] { display: none; }
.review-level .sep { opacity: .6; }
.review-level .done { color: #8fdca0; font-weight: 600; }
.review-level-go { appearance: none; border: none; background: none; padding: 2px 2px; color: #8cc8f5; font: inherit; font-weight: 700;
  cursor: pointer; text-decoration: underline; text-decoration-color: rgba(140, 200, 245, .45); text-underline-offset: 3px; }
.review-level-go:active { opacity: .7; }

.cand-check { appearance: none; justify-self: start; margin-top: 2px; padding: 6px 12px; border-radius: 999px;
  border: 1px solid rgba(124, 192, 245, .45); background: rgba(94, 170, 255, .1); color: #9fd0f7; font: 600 12px/1.2 inherit; cursor: pointer; }
.cand-check.done { border-color: transparent; background: none; padding-left: 0; color: #8fdca0; }

.replay .review-moves { order: 9; display: grid; gap: 6px; }
.review-moves-toggle { appearance: none; display: flex; align-items: center; justify-content: space-between; width: 100%;
  padding: 10px 12px; border: 1px solid var(--panel-line); border-radius: 12px; background: rgba(9, 20, 38, .45);
  color: var(--ink); font: 600 14px/1.2 inherit; cursor: pointer; }
.review-moves-toggle i { font-style: normal; color: var(--ink-dim); }
.review-moves-segs { display: flex; gap: 6px; }
.review-moves-seg { appearance: none; flex: 1; padding: 6px 10px; border-radius: 999px; border: 1px solid var(--panel-line);
  background: none; color: var(--ink-dim); font: 600 12px/1.2 inherit; cursor: pointer; }
.review-moves-seg.on { background: rgba(94, 170, 255, .22); border-color: rgba(124, 192, 245, .55); color: var(--ink); }
.review-moves-list { display: grid; gap: 2px; }
.review-move { appearance: none; display: grid; grid-template-columns: 2.1em 14px 2em minmax(0, 1fr) 3.4em; gap: 8px; align-items: center;
  padding: 7px 10px; border: none; border-radius: 8px; background: rgba(255, 255, 255, .03); color: var(--ink);
  font: 13px/1.2 inherit; font-variant-numeric: tabular-nums; text-align: left; cursor: pointer; }
.review-move.on { background: rgba(94, 170, 255, .22); }
.review-move .n { color: var(--ink-dim); text-align: right; }
.review-move .chip-icon { width: 14px; height: 14px; background: center / contain no-repeat; }
.review-move .d { color: var(--ink-dim); }
.review-move .mv { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; font-weight: 600; }
.review-move .l { text-align: right; font-weight: 700; }
.review-move .l.inaccuracy { color: #f2d15c; } .review-move .l.mistake { color: #f59a3c; } .review-move .l.blunder { color: #ff6b5e; }
.review-moves-empty { text-align: center; font-size: 13px; color: var(--ink-dim); padding: 6px 0; }

/* Проверка одного хода: время заранее неизвестно — бегущая полоса вместо процентов. */
.review-block.busy .review-block-card b { display: none; }
.review-block.busy .review-block-bar i { width: 35% !important; transition: none; animation: review-busy 1.1s ease-in-out infinite; }
@keyframes review-busy { from { transform: translateX(-100%); } to { transform: translateX(290%); } }
.history-pr { font-weight: 700; }
/* 01.10 (проба tools/probe_explore.py, 320 px): карточки итога в разборе не умещались, счётчики ошибок тянули окно
   вбок (гориз. прокрутка). Карточки сжимаются, имя — многоточием, счётчики при нехватке места переносятся. */
.review-summary { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
.review-summary .review-side, .review-who { min-width: 0; }
.review-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.review-counts { flex-wrap: wrap; row-gap: 2px; }

/* Шашка в полёте — с тенью. (След хода в игре убран 01.10 — он только в разборе, .replay-board.) */
/* Тень «приподнятой» шашки в полёте — плавно, в такт перелёту (Влад, 03.10: при ходе одной шашкой на 2–4 пункта тень
   на каждой посадке резко появлялась и пропадала). Анимация длиной ровно в перелёт: растёт на взлёте и к посадке
   возвращается к обычной тени — снятие класса после приземления уже ничего не меняет. */
.chip.flying { animation: chip-lift var(--fly-ms, 460ms) ease-in-out both; }
@keyframes chip-lift {
  0% { filter: drop-shadow(0 1px 2px rgba(0, 0, 0, .5)); }
  40% { filter: drop-shadow(0 6px 6px rgba(0, 0, 0, .55)); }
  100% { filter: drop-shadow(0 1px 2px rgba(0, 0, 0, .5)); }
}

/* Строка хода в разборе — колонками фиксированной ширины, высота строки постоянная (01.10: надпись прыгала). */
.replay .replay-caption { display: grid; grid-template-columns: max-content max-content max-content minmax(0, 1fr); column-gap: 10px;
  grid-auto-rows: auto; align-items: center; height: 22px; line-height: 22px; font-size: 14px; text-align: left; white-space: nowrap; overflow: hidden; }
.replay .replay-caption > .sz { height: 0; line-height: 0; visibility: hidden; }
@media (max-width: 360px) { .replay .replay-caption { font-size: 13px; column-gap: 8px; } }
.replay .replay-caption > span { overflow: hidden; text-overflow: ellipsis; }
.replay .replay-caption .n { color: var(--ink-dim); font-weight: 600; font-variant-numeric: tabular-nums; }
.replay .replay-caption .d { font-variant-numeric: tabular-nums; }
.replay .replay-caption .mv.long { font-size: 12.5px; }
.replay .replay-caption .start { grid-column: 1 / -1; text-align: center; }
.replay .replay-verdict { height: 20px; line-height: 20px; min-height: 0; }
.replay-verdict.best { color: #7ed98a; }
/* Варианты: колонки в px — у шапки шрифт мельче, и em-колонки съезжали относительно строк (01.10, MWC). */
.cand { grid-template-columns: minmax(0, 1fr) 58px 50px; }
.cand.mwc { grid-template-columns: minmax(0, 1fr) 58px 50px 56px; }
/* Список ходов: шапка колонок. */
.review-move.head { cursor: default; padding-top: 2px; padding-bottom: 2px; background: none; font-size: 11px; color: var(--ink-dim); }
.review-move.head .mv, .review-move.head .l { font-weight: 400; color: var(--ink-dim); }
.review-move { grid-template-columns: 2.1em 14px 2.4em minmax(0, 1fr) 3.4em; }
.replay-delete { appearance: none; justify-self: center; margin-top: 6px; padding: 8px 14px; border: 0; background: none;
  color: #ff8a7a; font: inherit; font-size: 14px; text-decoration: underline; text-underline-offset: 3px; cursor: pointer; }

/* Главное меню: кнопки выше, подальше от рекламного баннера (Влад, 01.10). */
.menu-actions { margin-bottom: clamp(12px, 3dvh, 32px); }

/* «Ускорить» и белый флажок «Сдаться» — в середине нижней рельсы (01.10). Флажок виден на своём ходу до броска;
   место держит всегда, чтобы рельса не прыгала. */
.rail-mid { justify-self: center; display: flex; align-items: center; gap: calc(var(--bw) * .02); min-width: 0; padding-inline: calc(var(--bw) * .03); }   /* зазор до лотка (Влад, 02.10) */
.resign-btn { appearance: none; flex: none; width: calc(var(--bw) * .085); aspect-ratio: 1; padding: 0; border-radius: 50%;
  border: 1px solid rgba(124, 150, 190, .28); background: rgba(9, 20, 38, .45); cursor: pointer; display: grid; place-items: center; }
.resign-btn i { width: 54%; aspect-ratio: 1; background: no-repeat center / contain url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23b9c7da' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M7.5 21V4'/%3E%3Cpath d='M7.5 4h10l-2 4 2 4h-10' fill='%23b9c7da'/%3E%3C/svg%3E"); opacity: .7; }
.resign-btn:active { transform: scale(.94); }
.resign-btn:disabled { opacity: .35; cursor: default; }
.resign-btn:disabled:active { transform: none; }
/* Куб в рельсе — размером с флажок и по центру строки (Влад, 03.10: «куб ложится криво»). */
.rail .cube-tile { width: calc(var(--bw) * .085); height: calc(var(--bw) * .085); margin-left: calc(var(--bw) * .02); align-self: center; vertical-align: initial; }
/* «Ускорить» — круглая кнопка-значок размером с флажок (03.10): с надписью она не помещалась рядом с флажком и кубом и
   наезжала на них (на 360 px не хватало ~40 px, на планшете ~25). Надпись — подсказка (title) и для диктора. */
.rail .autoplay { width: calc(var(--bw) * .085); aspect-ratio: 1; padding: 0; justify-content: center; flex: none; }
.rail .autoplay .autoplay-text { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); }
.rail .autoplay .autoplay-icon { width: 60%; height: 60%; margin: 0; }

/* Подписи строк настроек переносятся ровно (Влад, 01.10: «позиции» висело одно на второй строке). */
.field-label { text-wrap: balance; }
/* Надпись на ленте итога — по высоте заглавных, без запаса строки под выносные: центр точный при любом шрифте. */
.result-flag span { line-height: 1; text-box: trim-both cap alphabetic; }

/* ── Правила, вёрстка 01.10 ── окно фиксированной высоты: переключатель режима, ниже страница (заголовок, картинка,
   текст) — прокручивается целиком, если не влезла. Картинка одного размера на всех страницах. */
.window--rules .rules { display: grid; grid-template-rows: minmax(0, 1fr); gap: 10px; overflow: hidden; }
.window--rules .rules-page { overflow-y: auto; min-height: 0; display: flex; flex-direction: column; gap: 12px; padding: 2px 4px 8px; }
.rules-title { margin: 2px 0 0; text-align: center; font-size: 18px; font-weight: 800; color: #f2d15c; letter-spacing: .2px; }
.window--rules .rules-picture {
  flex: none; align-self: center; height: clamp(150px, 31dvh, 300px); width: auto; aspect-ratio: 597 / 666;
  border-radius: 10px; background: center / contain no-repeat; filter: drop-shadow(0 6px 14px rgba(0, 0, 0, .45));
}
.window--rules .rules-text { display: grid; gap: 10px; font-size: 15px; line-height: 1.45; color: #dbe6f5; white-space: normal; overflow: visible; }
.window--rules .rules-text p { margin: 0; }
.rules-block { display: grid; gap: 6px; }
.rules-block ul { margin: 0; padding: 0; list-style: none; display: grid; gap: 4px; }
.rules-block li { position: relative; padding-left: 16px; }
.rules-block li::before { content: ''; position: absolute; left: 3px; top: .62em; width: 6px; height: 6px; border-radius: 50%; background: #f2d15c; }
.rules-counter { display: flex; justify-content: center; gap: 8px; }
.rules-counter i { width: 8px; height: 8px; border-radius: 50%; background: rgba(255, 255, 255, .25); }
.rules-counter i.on { background: #f2d15c; transform: scale(1.2); }
@media (max-height: 640px) { .window--rules .rules-text { font-size: 14px; } .rules-title { font-size: 16px; } }

/* Окно поверх окна: без своего затемнения и без проявления фона — под ним уже затемнено (01.10, без мигания). */
.overlay.stacked { background: rgba(4, 10, 22, .6); animation: none; }   /* затемняет нижнее окно, без проявления */
/* Полноэкранный экран (разбор партии) — свой плотный фон, нижнее окно сквозь него не видно (02.10). */
.overlay.stacked.full { background: #16233c url("../assets/bg/bg_0.png") center / cover no-repeat; }
/* «Сбросить статистику» — мелкая неяркая ссылка в самом низу. */
.stats-reset { appearance: none; justify-self: center; margin: 14px 0 4px; padding: 6px 10px; border: 0; background: none;
  color: var(--ink-dim); font: inherit; font-size: 13px; text-decoration: underline; text-underline-offset: 3px; cursor: pointer; opacity: .75; }

/* «Об игре» — маленькая ссылка под кнопками меню (01.10); отступ до баннера — её нижнее поле. */
.menu-about { appearance: none; justify-self: center; margin: -4px 0 0; padding: 6px 12px; border: 0; background: none;
  color: var(--ink-dim); font: inherit; font-size: 14px; text-decoration: underline; text-underline-offset: 3px; cursor: pointer; }
.about { display: grid; gap: 12px; font-size: 15px; line-height: 1.45; color: #dbe6f5; }
.about p { margin: 0; }
.about-logo { justify-self: center; width: min(62%, 240px); aspect-ratio: 334 / 224; background: center / contain no-repeat; }
.about-contacts { display: grid; gap: 6px; margin-top: 4px; padding: 12px 14px; border-radius: 12px; background: rgba(3, 9, 20, .45); }
.about-contacts div { display: flex; gap: 10px; align-items: baseline; }
.about-contacts span { color: var(--ink-dim); min-width: 64px; }
.about-link { color: #7cc0f5; font-weight: 600; text-decoration: none; }
.about-h { margin: 6px 0 -4px; font-size: 17px; font-weight: 800; color: #f2d15c; }
.pr-up { color: #8fdca0; font-size: 11px; } .pr-graph .review-graph-label { display: flex; justify-content: space-between; }
.pr-graph-hint { margin-top: 6px; font-size: 12px; color: var(--ink-dim); text-align: center; }
.pr-cells { grid-template-columns: repeat(auto-fit, minmax(54px, 1fr)); }
.about-rules-cap { margin-top: 0; font-size: 13px; font-weight: 700; color: var(--ink-dim); text-align: center; letter-spacing: .4px; text-transform: uppercase; }
.about-rules { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
/* Плашка правил — как блок контактов внизу окна: тёмная подложка, отступ от текста «Об игре». */
.about-rules-box { display: grid; gap: 8px; padding: 10px 12px 12px; margin-bottom: 8px; border-radius: 12px;
  background: rgba(3, 9, 20, .45); box-shadow: inset 0 0 0 1px rgba(124, 192, 245, .14); }
.about-rule-btn { appearance: none; min-width: 0; padding: 9px 8px; border: 0; border-radius: 10px; cursor: pointer;
  background: linear-gradient(180deg, #4b8fd6, #3672b5); box-shadow: 0 3px 0 #21486f; color: #fff; font: 700 15px/1.2 var(--font);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.about-rule-btn:active { transform: translateY(2px); box-shadow: 0 1px 0 #21486f; }
.about p b { color: #fff; }

/* Полоса прокрутки окон — тонкая и поверх содержимого не толкает его (Влад, 02.10: «гигантский скролл бар двигает картинку»). */
.window-body, .window--rules .rules-page { scrollbar-width: thin; scrollbar-gutter: stable; scrollbar-color: rgba(160, 196, 245, .35) transparent; }
.window-body::-webkit-scrollbar, .window--rules .rules-page::-webkit-scrollbar { width: 4px; }
.window-body::-webkit-scrollbar-thumb, .window--rules .rules-page::-webkit-scrollbar-thumb { background: rgba(160, 196, 245, .35); border-radius: 2px; }
/* Картинки страниц правил стопкой, видна одна. */
.window--rules .rules-picture { position: relative; background: none !important; }
.window--rules .rules-picture > i { position: absolute; inset: 0; background: center / contain no-repeat; opacity: 0; }
.window--rules .rules-picture > i.on { opacity: 1; }

/* «Разобрать партию (.mat)» — в истории, над фильтрами (02.10); загруженные — отдельным списком под кнопкой. */
.import-mat { appearance: none; display: flex; align-items: center; justify-content: center; gap: 10px; padding: 12px 14px; border-radius: 12px;
  border: 1px dashed rgba(124, 192, 245, .5); background: rgba(94, 170, 255, .08); color: var(--ink); font: 600 15px/1.2 var(--font); cursor: pointer; }
.import-mat-ico { width: 20px; height: 20px; background: no-repeat center / contain url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%237cc0f5' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 16V4'/%3E%3Cpath d='M6 9l6-6 6 6'/%3E%3Cpath d='M4 20h16'/%3E%3C/svg%3E"); }
.ext-list { margin-bottom: 6px; }
.history-row.ext { border-left: 3px solid rgba(124, 192, 245, .6); }

/* История и загруженные партии (02.10): строка — инфо и счёт, ниже справа «мусорка» и «Разобрать» со значком рекламы. */
.history-row { display: grid; grid-template-columns: minmax(0, 1fr) auto; }
.history-actions { grid-column: 1 / -1; display: flex; justify-content: flex-end; align-items: center; gap: 8px; margin-top: 6px; }
.trash-btn { appearance: none; width: 34px; height: 34px; border-radius: 9px; border: 1px solid rgba(160, 196, 245, .25); background: rgba(9, 20, 38, .5) no-repeat center / 18px
  url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23b9c7da' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 7h16'/%3E%3Cpath d='M9 7V4h6v3'/%3E%3Cpath d='M6 7l1 13h10l1-13'/%3E%3Cpath d='M10 11v6M14 11v6'/%3E%3C/svg%3E"); cursor: pointer; }
.analyze-btn { appearance: none; display: inline-flex; align-items: center; gap: 8px; padding: 8px 14px; border: 0; border-radius: 10px; cursor: pointer;
  background: linear-gradient(180deg, #4fae62, #3b8c4c); box-shadow: 0 3px 0 #255c31; color: #fff; font: 700 15px/1.2 var(--font); }
.analyze-btn:active { transform: translateY(2px); box-shadow: 0 1px 0 #255c31; }
.analyze-btn.big { width: 100%; justify-content: center; padding: 14px; font-size: 17px; }
/* Значок рекламы — плашка «видео» (как в Словотеке у кнопок за ревард). */
.ad-ico { width: 22px; height: 16px; border-radius: 4px; background: rgba(255, 255, 255, .92) no-repeat center / 9px
  url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 10 10'%3E%3Cpath d='M2 1l7 4-7 4z' fill='%233b8c4c'/%3E%3C/svg%3E"); flex: none; }
.import-file { font-size: 12px; color: var(--ink-dim); text-align: center; word-break: break-all; }

/* ── «Поделиться игрой» (02.10, по Словотеке): карточка-картинка, знаки площадок ── */
.share { display: grid; gap: 14px; justify-items: center; padding-bottom: 4px; }
.share-card { width: min(100%, 300px); aspect-ratio: 1; box-sizing: border-box; display: grid; align-content: center; justify-items: center;
  gap: 12px; padding: 9% 13%; border-radius: 16px; overflow: hidden; background: radial-gradient(120% 90% at 50% 35%, #1d4170 0%, #0b1d38 75%);
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .12); }
.share-card-logo { width: 90%; aspect-ratio: 334 / 224; background: center / contain no-repeat; }
.share-card-text { margin: 0; display: grid; gap: 4px; text-align: center; line-height: 1.25; text-wrap: balance; }
.share-card-text b { color: #fff; font-size: 15px; font-weight: 800; white-space: nowrap; }
.share-card-text span { color: #e7c46e; font-size: 13px; font-weight: 600; }
.share-card-text :is(b, span) { text-transform: uppercase; letter-spacing: .04em; }   /* капсом (Влад, 21.10) */
.sh-hint { margin: 0; text-align: center; color: var(--ink-dim); text-wrap: balance; }
.sh-apps { display: flex; justify-content: center; gap: 14px; width: 100%; min-height: 52px; }
.sh-app { width: 52px; height: 52px; flex: none; padding: 0; border: none; background: none; display: grid; place-items: center; border-radius: 12px; cursor: pointer; }
.sh-app:active { transform: scale(.94); }
.sh-logo { height: 100%; width: auto; display: block; }
.sh-app .ico { width: 100%; height: 100%; border-radius: 24%; display: grid; place-items: center; color: #fff; background: #45A8AE; }

/* ── Заставка (02.10): фон игры, золотой логотип, тонкая золотая полоса — без слогана и радуги ── */
.splash { position: fixed; inset: 0; z-index: 1000; display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 34px; background: #16233c url("../assets/bg/bg_0.png") center / cover no-repeat; transition: opacity .28s ease; }
.splash.gone { opacity: 0; pointer-events: none; }
.splash-logo { width: min(64vw, 300px); aspect-ratio: 334 / 224; object-fit: contain; transform-origin: center;
  filter: brightness(.93) drop-shadow(0 10px 22px rgba(0, 0, 0, .5)); }   /* как логотип меню — подмена незаметна */
/* До того как найдено место логотипа в меню — только фон; затем логотип и полоса проявляются уже на месте. */
.splash-logo, .splash-bar { opacity: 0; transition: opacity .2s ease; }
.splash.placed .splash-logo, .splash.placed .splash-bar { opacity: 1; }
.splash.leaving .splash-bar { opacity: 0; }
.splash-bar { width: min(52vw, 340px); height: clamp(6px, .9vmin, 12px); border-radius: 99px;   /* толще, особенно на планшете (Влад, 03.10) */
  background: rgba(255, 255, 255, .1); overflow: hidden; }
/* Золото — в тон логотипу; обёртка выезжает слева, градиент внутри едет навстречу — полоса «открывает» его (анимация в index.html, 06.10). */
.splash-bar span { display: block; height: 100%; width: 100%; overflow: hidden; border-radius: 99px; transform: translateX(-100%); will-change: transform; }
.splash-bar i { display: block; height: 100%; width: 100%; background: linear-gradient(90deg, #a8742a, #e7b955 55%, #f7dc8c); transform: translateX(100%); will-change: transform; }

/* ── Разобранный матч в списке (02.10): PR слева внизу, «Посмотреть» (синяя), вся плашка — открывает разбор ── */
.history-row.reviewed { cursor: pointer; }
.hist-pr { margin-right: auto; display: inline-flex; align-items: baseline; gap: 5px; font-size: 15px; font-variant-numeric: tabular-nums; }
/* PR — плашкой высотой с кнопку «Посмотреть» (Влад, 21.10: элементы ряда должны согласовываться). */
.hist-pr { box-sizing: border-box; height: 38px; align-items: center; padding: 0 12px; border-radius: 10px;
  background: rgba(3, 9, 20, .45); border: 1px solid var(--panel-line); }
body[data-orientation="landscape"] .hist-pr { height: 30px; padding: 0 10px; font-size: 14px; }
.hist-pr small { font-size: 11px; font-weight: 700; color: var(--ink-dim); letter-spacing: .3px; }
.hist-pr i { font-style: normal; color: var(--ink-dim); }
.view-btn { appearance: none; padding: 8px 14px; border: 0; border-radius: 10px; cursor: pointer; color: #fff; font: 700 15px/1.2 var(--font);
  background: linear-gradient(180deg, #4b8fd6, #3672b5); box-shadow: 0 3px 0 #21486f; }
.view-btn:active { transform: translateY(2px); box-shadow: 0 1px 0 #21486f; }

/* «Разобрать» (со значком рекламы и без) и «Посмотреть» — одного размера (Влад, 03.10): кнопки не прыгают, когда значок
   появляется / пропадает или разбор становится просмотром. */
.analyze-btn:not(.big), .view-btn { box-sizing: border-box; min-width: 138px; height: 38px; justify-content: center; display: inline-flex; align-items: center;
  padding: 0 12px; font: 700 15px/1 var(--font); }

/* ─────────────────────────────────────────────── Обучение (04.10, src/tutorial) */
.window--lesson { width: min(520px, 100%); }
.lesson { display: grid; gap: 10px; }
/* Доска урока — сколько останется: задание, отклик и кнопки всегда на экране (320×568 — «Дальше» уходил вниз). */
.lesson-board { height: max(150px, min(56dvh, 560px, calc(100dvh - 380px - var(--banner) - var(--safe-top) - var(--safe-bottom)))); }
/* Задание — плашкой над доской, как сообщения в игре; точки — шаги главы. */
.lesson-task { display: grid; gap: 8px; padding: 10px 14px 12px; border-radius: 16px;
  background: linear-gradient(180deg, #233b62, #16294a); border: 1px solid rgba(160, 196, 245, .28);
  box-shadow: 0 10px 28px -14px #000, inset 0 1px 0 rgba(255, 255, 255, .1); }
.lesson-dots { display: flex; justify-content: center; gap: 6px; }
.lesson-dots i { width: 7px; height: 7px; border-radius: 50%; background: rgba(160, 196, 245, .28); }
.lesson-dots i.done { background: var(--accent); opacity: .55; }
.lesson-dots i.now { background: var(--accent); box-shadow: 0 0 6px var(--accent); }
.lesson-text { text-align: center; font-size: 16px; font-weight: 700; line-height: 1.35; height: 5.4em; overflow: hidden;
  display: flex; align-items: center; justify-content: center; text-wrap: balance; }
/* Плашка одного размера (Влад, 05.10: «чтобы не двигало ничего»): фраза — 4 строки, отклик — 2. */
.lesson-feed { text-align: center; font-weight: 700; height: 2.6em; line-height: 1.3; font-size: 14px; color: var(--ink-dim);
  display: flex; align-items: center; justify-content: center; text-wrap: balance; }
.lesson-feed.ok { color: #7cd77a; }
.lesson-feed.bad { color: #ff8a7a; }
.lesson-links { display: flex; justify-content: center; gap: 22px; min-height: 22px; }
.lesson-link { appearance: none; border: 0; background: none; color: var(--ink-dim); font: inherit; font-size: 14px;
  font-weight: 700; text-decoration: underline; text-underline-offset: 3px; cursor: pointer; padding: 2px 4px; }
.lesson-link[hidden] { display: none; }
.lesson-btns { display: grid; gap: 8px; }
.lesson-choice { display: grid; grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); gap: 8px; }
.lesson-choice:empty { display: none; }
.lesson-btns .big-btn[hidden] { display: none; }

/* Список глав: ветка — заголовком, глава — строкой; пройденная — галочкой. */
.tut-menu-note { margin: 0 0 6px; text-align: center; color: var(--ink-dim); font-size: 14px; line-height: 1.4; }
.tut-list { display: grid; gap: 6px; }
.tut-branch { margin: 10px 2px 2px; font-size: 13px; font-weight: 700; color: var(--ink-dim); letter-spacing: .4px; text-transform: uppercase; }
.tut-ch { appearance: none; display: grid; grid-template-columns: 26px 1fr auto; align-items: center; gap: 10px;
  padding: 10px 12px; border: 0; border-radius: 12px; cursor: pointer; text-align: left; font: inherit; color: var(--ink);
  background: rgba(255, 255, 255, .06); }
.tut-ch:active { transform: translateY(1px); }
.tut-ch-n { width: 26px; height: 26px; border-radius: 50%; display: grid; place-items: center; font-size: 13px; font-weight: 700;
  background: rgba(160, 196, 245, .16); color: var(--ink-dim); }
.tut-ch-name { font-weight: 700; }
.tut-ch-mark { font-weight: 900; color: var(--ink-dim); }
.tut-ch.done .tut-ch-n { background: rgba(124, 215, 122, .22); color: #7cd77a; }
.tut-ch.done .tut-ch-mark { color: #7cd77a; }

/* Первый запуск: «Умеете играть?» */
.tut-ask { display: grid; gap: 14px; text-align: center; }
.tut-ask-text { margin: 4px 0 0; font-size: 18px; font-weight: 700; }
.tut-ask-btns { display: grid; gap: 8px; }
.tut-ask-note { margin: 0; font-size: 13px; color: var(--ink-dim); line-height: 1.4; }

/* (i): «Обучение» — во всю ширину под кнопками правил. */
.about-tut-btn { width: 100%; }

/* Обучение: фраза сцены сменилась — мягкое проявление. */
.lesson-text.flash { animation: lesson-say .45s ease-out; }
@keyframes lesson-say { from { opacity: .15; transform: translateY(3px); } }
/* Урок, 05.10: внизу «←» и «→», между ними — кнопки шага (ответы, «Выбросить», «Сыграть партию»). */
.lesson-nav { display: grid; grid-template-columns: 56px minmax(0, 1fr) 56px; align-items: center; gap: 10px; min-height: 56px; }
.lesson-center { display: grid; gap: 8px; }
.lesson-center .big-btn[hidden] { display: none; }
.lesson-nav-btn { appearance: none; width: 56px; height: 56px; border: 0; border-radius: 50%; cursor: pointer; font: inherit;
  font-size: 26px; font-weight: 900; line-height: 1; color: #fff; background: linear-gradient(180deg, var(--blue-1), var(--blue-2));
  box-shadow: 0 3px 0 #21486f; }
.lesson-nav-btn:disabled { opacity: .3; cursor: default; }
.lesson-nav-btn:active:not(:disabled) { transform: translateY(2px); box-shadow: 0 1px 0 #21486f; }
/* Стрелки хода поверх доски: зелёная — можно, красная с крестиком — нельзя. */
.lesson-arrows { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; z-index: 7; overflow: visible; }
.lesson-arrows .arrow-under { fill: none; stroke: rgba(20, 12, 4, .55); stroke-width: 8; stroke-linecap: round; }
.lesson-arrows .arrow { fill: none; stroke-width: 4.5; stroke-linecap: round; }
.lesson-arrows .arrow-head { stroke: rgba(20, 12, 4, .55); stroke-width: 1.5; }
.lesson-arrows .arrow.red { stroke-dasharray: 7 6; }
.lesson-arrows .arrow-x { stroke: #ff5a4f; stroke-width: 4.5; stroke-linecap: round; filter: drop-shadow(0 1px 2px rgba(0, 0, 0, .6)); }
/* Урок 05.10: куда нажать — белая мягкая пульсация шашки. */
.chip.tap-pulse::after { content: ''; position: absolute; inset: -12%; border-radius: 50%; pointer-events: none;
  box-shadow: 0 0 0 3px rgba(255, 255, 255, .95), 0 0 14px 4px rgba(255, 255, 255, .7); animation: tap-pulse 1.3s ease-in-out infinite; }
@keyframes tap-pulse { 0%, 100% { opacity: .15; transform: scale(.94); } 50% { opacity: 1; transform: scale(1.06); } }
/* Лоток вывода в уроке — под доской справа, как в игре. Пульсирует белым, когда на него надо нажать. */
.lesson-tray-row { display: flex; align-items: center; justify-content: flex-end; gap: 10px; min-height: calc(var(--bw) * .08); }
.lesson-tray-row[hidden] { display: none; }
.lesson-tray-cap { font-size: 13px; font-weight: 700; color: var(--ink-dim); }
.lesson-tray { justify-self: auto; }
.lesson-tray.droppable::before { content: ''; position: absolute; inset: -5px; border-radius: inherit; pointer-events: none;
  box-shadow: 0 0 0 3px rgba(255, 255, 255, .9), 0 0 14px 4px rgba(255, 255, 255, .6); animation: tap-pulse 1.3s ease-in-out infinite; }
/* Маршрут по кругу (как в правилах): белый — белых, тёмный — чёрных. */
.lesson-arrows .route-under { fill: none; stroke-width: 10; stroke-linecap: round; stroke-linejoin: round; opacity: .85; }
.lesson-arrows .route { fill: none; stroke-width: 5; stroke-linecap: round; stroke-linejoin: round; }
/* Учебная партия: «Подсказка» в шапке на месте «до N очков». */
.hint-top { min-height: 0; padding: 6px 16px; font-size: 15px; border-radius: 12px; }
.hint-top:disabled { opacity: .45; }
.hint-top[hidden] { display: none; }
/* После «Умею»: где найти обучение — кнопка (i) пульсирует, рядом пузырь «Обучение — здесь». */
.tut-point { position: relative; animation: tut-point 2.6s ease-in-out 3; }   /* медленнее (Влад, 06.10, дважды): три вдоха за 8 с пузыря */
@keyframes tut-point { 50% { box-shadow: 0 0 0 4px rgba(255, 255, 255, .9), 0 0 18px 6px rgba(255, 255, 255, .55); } }
.tut-bubble { position: fixed; z-index: 70; max-width: 220px; padding: 9px 12px; border-radius: 12px; pointer-events: none;
  background: #fff; color: #16294a; font-weight: 700; font-size: 14px; line-height: 1.3; box-shadow: 0 8px 22px -8px #000;
  animation: tut-bubble-in .65s ease-out both; }   /* уходит анимацией из tutorial.js (pointToTutorial) */
.tut-bubble::before { content: ''; position: absolute; left: 18px; top: -7px; border: 7px solid transparent; border-top: 0; border-bottom-color: #fff; }
@keyframes tut-bubble-in { from { opacity: 0; transform: translateY(-4px); } }

/* Урок, правки 05.10 (v5). Ссылки («Повторить», «Заново», «Подсказка») — между «←» и «→», отдельной строки нет. */
.lesson-center .lesson-links { gap: 16px; flex-wrap: wrap; }
/* Действие игрока в фразе задачи — цветом. */
.lesson-text .act { color: #ffd65a; }
/* Новые стрелки проявляются плавно. */
.lesson-arrows.fade { animation: lesson-arrows-in .9s ease-out both; }
@keyframes lesson-arrows-in { from { opacity: 0; } }
/* Последний шаг главы: «→» зелёная — дальше следующая глава. */
.lesson-nav-btn.next.finish { background: linear-gradient(180deg, #57b862, #3b8544); box-shadow: 0 3px 0 #2b6733; }
.lesson-nav-btn.next.finish:active { box-shadow: 0 1px 0 #2b6733; }
/* Ничего не ждём от игрока, следующий шаг — продолжение: «→» медленно дышит (больше-меньше). */
.lesson-nav-btn.next.breathe { animation: lesson-breathe 1.8s ease-in-out infinite; }
@keyframes lesson-breathe { 50% { transform: scale(1.12); } }
/* Невысокие экраны (iPhone SE 375×667 и ниже): доска крупнее, текст и кнопки компактнее (Влад, 05.10). */
@media (max-height: 760px) {
  .lesson { gap: 8px; }
  .lesson-task { gap: 4px; padding: 8px 12px 8px; }
  .lesson-text { font-size: 14px; line-height: 1.3; height: 5.2em; }
  .lesson-feed { font-size: 13px; height: 2.6em; }
  .lesson-nav { grid-template-columns: 48px minmax(0, 1fr) 48px; min-height: 48px; }
  .lesson-nav-btn { width: 48px; height: 48px; font-size: 22px; }
}

/* Высота доски урока — всё, что осталось после плашки и кнопок (флекс, без подсчёта «сколько занимает остальное»):
   доска во всю ширину, а если по высоте не помещается — ужимается; лишнее место остаётся внизу, не между блоками. */
.overlay.full .window--lesson { display: flex; flex-direction: column; }
.window--lesson .window-body { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; }
.window--lesson .lesson { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; }
.window--lesson .lesson > * { flex-shrink: 0; }
.window--lesson .lesson > .lesson-board { flex: 0 1 auto; min-height: 150px; height: calc((min(620px, 100vw) - 24px) / .893); max-height: 560px; }

/* v6 (05.10). «→» дышит быстрее. */
.lesson-nav-btn.next.breathe { animation-duration: 1.1s; }
/* Ссылок нет — строка не занимает места (иначе «Сыграть партию» уезжала вниз). */
.lesson-links:not(:has(.lesson-link:not([hidden]))) { display: none; }
/* Лоток чёрных в «Конце партии» — светится: сколько соперник успел вывести. */
.lesson-tray.lit { border-color: rgba(255, 214, 90, .95); box-shadow: 0 0 12px 3px rgba(255, 200, 60, .65); transition: box-shadow .5s, border-color .5s; }
/* Шаг-показ: отклика не бывает — его строка отдана фразе, плашка того же размера. */
.lesson-task.no-feed .lesson-feed { display: none; }
.lesson-task.no-feed .lesson-text { height: calc(5.4em + 8px + 2.6 * 14px); }
@media (max-height: 760px) { .lesson-task.no-feed .lesson-text { height: calc(5.2em + 4px + 2.6 * 13px); } }
/* Абзацы фразы; шаг партии — текст сверху плашки, без точки (Влад, 05.10). */
.lesson-text .para { display: block; }
.lesson-text .para + .para { margin-top: .55em; }
.lesson-dots:empty { display: none; }
.lesson-task.top .lesson-text { align-items: flex-start; padding-top: 4px; }
/* Шаг партии без точки: высота строки точек (7 px + зазор) отдана тексту — плашка того же размера. */
.lesson-task.no-feed.top .lesson-text { height: calc(5.4em + 8px + 2.6 * 14px + 15px); }
@media (max-height: 760px) { .lesson-task.no-feed.top .lesson-text { height: calc(5.2em + 4px + 2.6 * 13px + 11px); } }

/* Тренировка (05.10): переключатель в «Новой игре» — отдельно, в самом низу, с пояснением. */
.training-block { display: grid; margin-top: 4px; padding-top: 10px; border-top: 1px solid rgba(160, 196, 245, .14); }
.training-block { gap: 3px; }
.training-block .field-note { margin: 0 6px; }
.training-block.disabled { opacity: .4; pointer-events: none; }
.field-note { font-size: 12px; line-height: 1.3; font-weight: 400; color: var(--ink-dim); }
/* Низкие экраны (320×568): строки «Новой игры» плотнее — окно с тренировкой помещается без прокрутки. */
@media (max-height: 620px) {
  .window--newgame .fields { gap: 5px; }
  .window--newgame .field-row { min-height: 37px; padding-block: 2px; }
  .window--newgame .training-block { margin-top: 2px; padding-top: 5px; }
}
/* В тренировке «Подсказка» — под «до N очков», не вместо (Влад, 05.10); в учебной партии — на месте очков, как было. */
.hint-top.training { min-height: 0; height: calc(var(--bw) * .08); padding: 0 12px; font-size: clamp(12px, 3.4vw, 14px); border-radius: 10px; white-space: nowrap; }
.hint-top .ad-ico { margin-left: 6px; }
/* Тренировка (06.10, Влад): плашка — по центру доски, во всю её ширину, поверх кнопок и костей; тёмная, чуть прозрачная. */
.board-wrap { position: relative; }
.train-explain { position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%); z-index: 9;
  width: calc(var(--bw) * .82); box-sizing: border-box; padding: calc(var(--bw) * .03) calc(var(--bw) * .035); border-radius: calc(var(--bw) * .035);
  background: rgba(10, 22, 42, .9); border: 1px solid rgba(160, 196, 245, .3); box-shadow: 0 10px 26px -10px #000;
  color: #fff; font-size: max(12px, calc(var(--bw) * .04)); line-height: 1.3; font-weight: 700; text-align: center;
  animation: train-explain-in .2s ease-out both; }
@keyframes train-explain-in { from { opacity: 0; } }
.train-explain[hidden] { display: none; }
.train-explain h4 { margin: 0 0 2px; font-size: 1.12em; }
/* Плашка ошибки — ровно три строки: класс / шансы / «есть ход лучше» (Влад, 06.10); остальные плашки переносятся. */
.train-explain:is(.inaccuracy, .mistake, .blunder) :is(h4, p) { white-space: nowrap; }
.train-explain.inaccuracy h4 { color: #e8d36a; }
.train-explain.mistake h4 { color: #f0a050; }
.train-explain.blunder h4 { color: #ff7a6a; }
.train-explain p { margin: 0; }
.train-explain-btns { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; margin-top: 10px; }
.train-explain-btns .big-btn { min-height: 0; height: calc(var(--bw) * .11); font-size: max(12px, calc(var(--bw) * .04)); padding: 0 8px; }
.train-explain-btns .ad-ico { margin-left: 6px; flex: none; }

/* Разбор хода (06.10) — на месте профилей: шириной с доску, все размеры от её ширины (--bw), одна раскладка везде.
   Верх и низ ставит скрипт (main.js setReview) — от верхних кнопок до рельсы соперника. */
.players { position: relative; }
/* Шириной с доску; на узких экранах (доска уже экрана) — почти во всю ширину экрана, чтобы текст не сжимался. */
.train-review { position: absolute; z-index: 5; left: 50%; transform: translateX(-50%); top: 0; bottom: 0;
  width: clamp(var(--board-width, 100%), calc(100vw - 16px), max(var(--board-width, 100%), 420px));
  box-sizing: border-box; display: grid; grid-template-columns: minmax(0, 1fr) max-content; gap: calc(var(--bw) * .03);
  align-items: center; padding: calc(var(--bw) * .022) calc(var(--bw) * .03); border-radius: calc(var(--bw) * .035);
  background: rgba(10, 22, 42, .96); border: 1px solid rgba(160, 196, 245, .3); box-shadow: 0 10px 26px -10px #000; color: #fff;
  --tr-font: max(11px, calc(var(--bw) * .036 * var(--tr-scale, 1))); }
.train-review[hidden] { display: none; }
/* Встречное растворение (06.10): табличка опускается сверху и проявляется, шапка (профили, счёт) в это же время гаснет;
   обратно — так же. visibility снимается с задержкой — пока шапка гаснет, по ней не нажать. */
.train-review.in { animation: train-review-in .28s ease-out both; }
@keyframes train-review-in { from { opacity: 0; translate: 0 -10px; } }
.train-review.out { pointer-events: none; }   /* угасание — анимацией из main.js (setReview) */
.players > .player, .players > .goal-col, #rail-opponent { transition: opacity .28s ease, visibility 0s linear 0s; }
.players.reviewing > .player, .players.reviewing > .goal-col, #rail-opponent.tr-covered { opacity: 0; visibility: hidden; transition: opacity .28s ease, visibility 0s linear .28s; }
.tr-text { min-width: 0; font-size: var(--tr-font); line-height: 1.3; font-weight: 600; }
.tr-text h4 { margin: 0 0 .3em; font-size: 1.1em; font-weight: 800; }
.tr-text h4.inaccuracy { color: #e8d36a; } .tr-text h4.mistake { color: #f0a050; } .tr-text h4.blunder { color: #ff7a6a; }
.tr-text ul { margin: 0; padding: 0; list-style: none; display: grid; gap: .35em; }
.tr-text li { position: relative; padding-left: .95em; }
.tr-text li::before { content: ''; position: absolute; left: .15em; top: .5em; width: .38em; height: .38em; border-radius: 50%; background: #7cc0f5; }
.tr-moves { display: grid; gap: calc(var(--bw) * .015); align-content: center; }
.tr-moves small { text-align: center; font-size: max(10px, calc(var(--tr-font) * .85)); color: var(--ink-dim); font-weight: 700; }
.tr-move { appearance: none; display: flex; justify-content: space-between; align-items: center; gap: 6px;
  padding: calc(var(--bw) * .015) calc(var(--bw) * .025); border-radius: calc(var(--bw) * .025);
  border: 1.5px solid rgba(160, 196, 245, .25); background: rgba(255, 255, 255, .06); color: #fff; font: inherit;
  font-size: var(--tr-font); font-weight: 700; cursor: pointer; white-space: nowrap; }
.tr-move b { font-variant-numeric: tabular-nums; }
.tr-move.best b { color: #8fd18c; }
.tr-move.yours b { color: #ff9a8a; }
.tr-move.on.best { border-color: #6cc26a; background: rgba(108, 194, 106, .18); }
.tr-move.on.yours { border-color: #e67a6a; background: rgba(230, 122, 106, .16); }
/* Самые узкие (320): чуть плотнее строки и пункты — худший случай (три причины) помещается без прокрутки. */
@media (max-width: 340px) { .tr-text { line-height: 1.22; } .tr-text ul { gap: .2em; } .tr-text h4 { margin-bottom: .2em; } }
/* Кнопки плашки — в одну строку («Подсказка · 4» на 320 px переносилась); на самых узких плашка чуть шире (90%). */
.train-explain-btns .big-btn { white-space: nowrap; padding: 0 6px; }
@media (max-width: 340px) { .train-explain { width: calc(var(--bw) * .9); } }

/* Урок, 06.10: стрелки «← →» — рисунком (символ шрифта на Android тонкий). */
.lesson-nav-btn { display: grid; place-items: center; }
.lesson-nav-btn i { width: 46%; aspect-ratio: 1; background: no-repeat center / contain url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='white' stroke-width='3.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 12h15'/%3E%3Cpath d='M13 5.5 19.5 12 13 18.5'/%3E%3C/svg%3E"); }
.lesson-nav-btn.prev i { transform: scaleX(-1); }
/* Голова с ореолом: контурный столбик под стопкой не нужен — он торчал выше шашек; остаётся только свечение (Влад, 06.10). */
.point.lesson-mark.mark-glow::before { content: none; }

/* Тренировка, 06.10: плашка — колонкой (текст по центру, кнопки внизу) — сменившая её плашка той же высоты держит кнопки
   на прежнем месте; смена — без проявления. */
.train-explain { display: flex; flex-direction: column; }
.train-explain-body { flex: 1; display: flex; flex-direction: column; justify-content: center; }
.train-explain.swap { animation: none; }
/* «Добавить +5 ▶»: надпись и значок — одной группой по центру кнопки (были разнесены зазором кнопки 10 px + отступ). */
.train-explain-btns .big-btn { gap: 0; }
.train-explain-btns .big-btn .ad-ico { margin-left: .4em; }

/* Приложение: отступы только от выреза камеры (MainActivity), env() не участвует — он отдаёт и системные кнопки,
   пока они видны под шторкой, и интерфейс поднимался (Влад, 06.10; см. index.html). */
:root.native-insets { --safe-top: var(--cut-top, 0px); --safe-bottom: var(--cut-bottom, 0px); --safe-left: var(--cut-left, 0px); --safe-right: var(--cut-right, 0px); }

/* ── «Оцените игру» (16.10.2026, rate.js): узкое плотное окно, пять звёзд; 1–2 → «спасибо» на том же месте
   (обе части лежат в одной ячейке сетки — окно не меняет высоту при подмене) ── */
.window--rate { width: min(336px, 100%); }
.window--rate .window-body { padding: 2px 14px 16px; }
.rate { display: grid; text-align: center; }
.rate > * { grid-area: 1 / 1; align-self: center; }
.rate p { margin: 0 0 12px; font-size: 14.5px; line-height: 1.3; opacity: .9; text-wrap: balance; }
.rate-stars { display: flex; justify-content: center; gap: 2px; }
.rate-star { padding: 2px; background: none; border: none; line-height: 0; color: rgba(255, 255, 255, .35); cursor: pointer;
  transition: color .16s ease, transform .16s ease; }
.rate-star svg { width: 44px; height: 44px; display: block; }
.rate-star svg path { fill: none; }
.rate-star.on { color: var(--accent); animation: rateStar .32s cubic-bezier(.34, 1.56, .64, 1); }
.rate-star.on svg path { fill: currentColor; }
.rate-star:active { transform: scale(.92); }
@keyframes rateStar { 0% { transform: scale(.6); } 60% { transform: scale(1.18); } 100% { transform: scale(1); } }
.rate-thanks { visibility: hidden; display: grid; gap: 4px; line-height: 1.3; }
.rate-thanks b { font-size: 20px; }
.rate.done .rate-ask { visibility: hidden; }
.rate.done .rate-thanks { visibility: visible; }

/* ── Удвоение (20.10, Влад: профи решают по кубу, глядя на доску) — табличка на месте профилей, как разбор хода;
   доска открыта. Куб · заголовок, текст, счёт · кнопки колонкой. ── */
.offer-bar { position: absolute; z-index: 6; left: 50%; transform: translateX(-50%); top: 0; bottom: 0;
  width: clamp(var(--board-width, 100%), calc(100vw - 16px), max(var(--board-width, 100%), 420px));
  box-sizing: border-box; display: grid; grid-template-columns: max-content minmax(0, 1fr); gap: calc(var(--bw) * .03);
  align-items: center; padding: calc(var(--bw) * .022) calc(var(--bw) * .03); border-radius: calc(var(--bw) * .035);
  background: rgba(10, 22, 42, .96); border: 1px solid rgba(240, 200, 90, .45); box-shadow: 0 10px 26px -10px #000; color: #fff;
  --ob-font: max(11px, calc(var(--bw) * .036)); font-size: var(--ob-font); }
.offer-bar.ask { grid-template-columns: max-content minmax(0, 1fr) max-content; }
.offer-bar[hidden] { display: none; }
.offer-bar.in { animation: train-review-in .28s ease-out both; }
.players.offering > .player, .players.offering > .goal-col { opacity: 0; visibility: hidden; transition: opacity .28s ease, visibility 0s linear .28s; }
.ob-cube { display: block; width: calc(var(--bw) * .15); aspect-ratio: 1; background: center / contain no-repeat; }
.ob-mid { min-width: 0; line-height: 1.3; }
.ob-mid p { margin: 0; font-weight: 600; }
.ob-mid .ob-text { font-size: 1.12em; font-weight: 800; color: #f3d36b; text-wrap: balance; }
.ob-score { margin-top: .3em !important; color: var(--ink-dim); font-variant-numeric: tabular-nums; }
.ob-text.ob-answer { font-weight: 800; animation: offer-answer .25s ease-out; }
.ob-text.is-yes { color: #7ee08a; } .ob-text.is-no { color: #ff8a7a; }
.ob-btns { display: grid; gap: calc(var(--bw) * .02); }
.ob-btns .big-btn { min-height: 0; height: calc(var(--bw) * .1); padding: 0 calc(var(--bw) * .035); font-size: var(--ob-font); white-space: nowrap; }

/* Номера пунктов на рамке доски (20.10, разбор партий): по центру полосы рамки, мелко, светлым по дереву. */
.pt-num { position: absolute; transform: translate(-50%, -50%); font-style: normal; font-weight: 700; line-height: 1;
  font-size: max(8px, var(--pt-num-size, 2.5cqw)); color: rgba(255, 244, 222, .92); text-shadow: 0 1px 2px rgba(0, 0, 0, .8); pointer-events: none;
  font-variant-numeric: tabular-nums; }
.pt-num[hidden] { display: none; }
/* 23.10: цифра у внутреннего края рамки, растёт наружу (board.js layoutNumbers): верхний ряд — вверх, нижний — вниз. */
.pt-num { transform: translate(-50%, 0); font-size: max(8px, calc(var(--pt-num-base, 2cqw) * 1.3)); }
.pt-num.top { transform: translate(-50%, -100%); }
/* В горизонтали экрана — на 15% крупнее прежних 0.92 полосы (Влад, 23.10: «не на 40%, а на 15% всего»). */
body[data-orientation="landscape"] .pt-num, body[data-orientation="landscape"] .pt-num.top { font-size: max(8px, calc(var(--pt-num-base, 2cqw) * .92 * 1.15));
  --pt-y: var(--pt-mid); transform: translate(-50%, -50%); }
/* Строка «шансы те же, но риск марса X% вместо Y%» (20.10) длиннее «шансы −N%» — переносится, ровными строками. */
.train-explain.wrap p { white-space: normal; text-wrap: balance; }
/* Плашка тренировки открыта (ход или куб, 20.10) — кнопки доски под ней прячутся: «Удвоить» / «Бросить» торчали по краям. */
.board-wrap:has(> .train-explain:not([hidden])) .board-btn { visibility: hidden; }

/* ═══════════════════════ Горизонталь (21.10, Влад: «(в)» — авто по телефону + выбор в настройках) ═══════════════════════
   Игра (переверстка 21.10 по отзыву Влада): доска СТРОГО по центру экрана, от верха до полосы баннера, своя картинка
   1056×740. Лотки стоймя у её края со стороны выброса (белые — справа; чёрные в длинных — слева), шашка в лотке — того же
   размера, что на доске. Слева — игрок: «Назад» в углу, профиль, пипы, внизу «Ускорить» и «Сдаться»; справа — соперник:
   «Настройки» в углу, профиль, пипы, внизу «до N очков» и куб. Разбор хода и предложение удвоения встают на место
   колонки игрока. Всё вокруг доски меряется --bw, а он здесь — от ширины боковой колонки (не от доски). */
body[data-orientation="landscape"] {
  /* Высота под доску: экран минус верхний отступ и полоса баннера (те же, что поля #app). */
  --land-h: calc(100dvh - var(--safe-top) - 12px - var(--banner) - var(--safe-bottom));
  /* Ширина боковой колонки не меньше: 130 px / 14% экрана; на узких телефонах (≤ 720 px) — 96 px, доска крупнее. */
  --side-min: max(130px, 14vw);
  /* Доска во всю высоту, но каждой боковой колонке остаётся не меньше max(130 px, 14% экрана): на коротких телефонах и
     планшетах доска тогда ниже и по центру. 1.136 и 24 px — две колонки лотков (0.068 ширины доски + 12 px каждая). */
  --land-bw: min(calc(var(--land-h) * 1056 / 740),
    calc((100vw - 2 * var(--side-min) - 24px - 2 * var(--edge-x) - var(--safe-left) - var(--safe-right)) / 1.136));
  --land-bh: calc(var(--land-bw) * 740 / 1056);
  /* Лоток: шашка на ребре длиной ровно с шашку на доске (boardArt landscape chipWidth 0.068). */
  --tray-w: calc(var(--land-bw) * .068 + 4px);
  /* Пятнадцать шашек на ребре в полный рост не влезают в половину доски — толщина ужата, ширина лотка — как шашка. */
  --oc-h: calc((var(--land-bh) * .47 - 18px) / 15);
  --tray-col: calc(var(--tray-w) + 8px);
  --side-w: calc((100vw - var(--land-bw) - 2 * var(--tray-col) - 2 * var(--edge-x) - var(--safe-left) - var(--safe-right)) / 2);
}
.side-panel { display: none; }
body[data-orientation="landscape"] {
  --land-pad: calc((var(--land-h) - var(--land-bh)) / 2);   /* на планшете доска ниже экрана — поля сверху и снизу */
}
body[data-orientation="landscape"] #app {
  max-width: none;
  /* Сверху небольшой отступ (6 px), снизу — полоса баннера. */
  padding-top: calc(6px + var(--safe-top));
  /* игроки | пусто (ширина лотка) | доска | лотки | матч; колонки по бокам доски одной ширины — доска ровно по центру */
  grid-template-columns: minmax(0, 1fr) var(--tray-col) var(--land-bw) var(--tray-col) minmax(0, 1fr);
  /* поле | кнопки | соперник | его пипсы | пусто | вы | ваши пипсы | поле — всё по высоте доски */
  grid-template-rows: var(--land-pad) auto auto auto minmax(0, 1fr) auto auto var(--land-pad);
  grid-template-areas: none;
  gap: 0;
}
body[data-orientation="landscape"] :is(.board-block, .rail, .players, .topbar) { display: contents; }
body[data-orientation="landscape"] :is(.topbar, .players, .pips, .rail-mid, .goal-col, .player, .train-review, .offer-bar) {
  --bw: min(var(--board-width, 340px), calc(var(--side-w) * 2.4));
}
/* Слева сверху — «Назад» и «Настройки» (вне рамки); ниже рамка с игроками: соперник сверху, черта, вы снизу.
   Справа — рамка матча: режим, «до N очков», куб, внизу «Ускорить» и «Сдаться». */
body[data-orientation="landscape"] .side-panel {
  display: block; align-self: stretch; position: relative; z-index: 0;
  border-radius: 16px; border: 1px solid rgba(240, 192, 74, .28);
  background: linear-gradient(180deg, rgba(24, 40, 68, .82), rgba(12, 22, 42, .86));
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .07), 0 12px 26px -14px #000;
}
body[data-orientation="landscape"] .side-panel.left { grid-column: 1; grid-row: 3 / 8; margin: 8px 6px 0 0; }
body[data-orientation="landscape"] .side-panel.right { grid-column: 5; grid-row: 2 / 8; margin-left: 6px; }
body[data-orientation="landscape"] .side-panel.left::after {
  content: ''; position: absolute; left: 12%; right: 12%; top: 50%; height: 1px;
  background: linear-gradient(90deg, transparent, rgba(240, 192, 74, .45), transparent);
}
body[data-orientation="landscape"] :is(#btn-back, #btn-settings, .screen-title, .player, .pips, .goal-col, .rail-mid) { position: relative; z-index: 1; }
body[data-orientation="landscape"] :is(#btn-back, #btn-settings) { --corner-btn: clamp(42px, calc(var(--side-w) * .3), 60px); }
/* В левом верхнем углу экрана (на планшете доска ниже — кнопки всё равно в углу, а не у рамки игроков). */
body[data-orientation="landscape"] #btn-back { grid-column: 1; grid-row: 1 / 3; align-self: start; justify-self: start; }
body[data-orientation="landscape"] #btn-settings { grid-column: 1; grid-row: 1 / 3; align-self: start; justify-self: start; margin-left: calc(var(--corner-btn) + 10px); }
/* Режим матча («Короткие · Тренировка») — шапкой правой рамки. */
body[data-orientation="landscape"] .screen-title { display: block; grid-column: 5; grid-row: 2; justify-self: center; align-self: start;
  margin: 12px 8px 0 14px; font-size: clamp(11px, calc(var(--side-w) * .08), 16px); white-space: normal; text-align: center; line-height: 1.2; }
/* Профили: одинаковые (аватар, счёт справа, имя под ними), крупнее; шашка на аватаре не нужна — она в пипсах под ним. */
body[data-orientation="landscape"] .player,
body[data-orientation="landscape"] .player.right { grid-template-columns: auto auto; grid-template-areas: "ava score" "name name"; justify-items: center; justify-self: center; }
body[data-orientation="landscape"] .player.right .player-score { margin-inline: calc(var(--bw) * -.035) 0; padding: calc(var(--bw) * .018) calc(var(--bw) * .03) calc(var(--bw) * .018) calc(var(--bw) * .05); }
body[data-orientation="landscape"] .player .ava-wrap { width: calc(var(--bw) * .25); }
body[data-orientation="landscape"] .ava-chip { display: none; }
body[data-orientation="landscape"] .player-name { margin-top: 2px; justify-self: center; }
/* Пипсы — по центру под профилем, плашкой (шашка стороны и число). */
body[data-orientation="landscape"] .pips { align-self: center; padding: 3px 10px 3px 6px; border-radius: 999px; background: rgba(3, 9, 20, .5); border: 1px solid var(--panel-line); }
body[data-orientation="landscape"] .pips > span { min-width: 3ch; text-align: center; }   /* место под три цифры — плашка не меняет ширину */
body[data-orientation="landscape"] #card-opponent { grid-column: 1; grid-row: 3; margin: 14px 6px 0 0; }
body[data-orientation="landscape"] #rail-opponent .pips { grid-column: 1; grid-row: 4; justify-self: center; margin: 6px 6px 0 0; }
body[data-orientation="landscape"] #card-player { grid-column: 1; grid-row: 6; align-self: end; margin-right: 6px; }
body[data-orientation="landscape"] #rail-player .pips { grid-column: 1; grid-row: 7; justify-self: center; margin: 6px 6px 12px 0; }
/* Матч: «до N очков», куб в центре, «Подсказка» в тренировке — в середине правой рамки, крупно. */
body[data-orientation="landscape"] .goal-col { grid-column: 5; grid-row: 3 / 7; align-self: center; justify-self: center; padding: 0 8px 0 14px; gap: 8px; }
body[data-orientation="landscape"] .goal { font-size: calc(var(--bw) * .038); }
body[data-orientation="landscape"] .goal-col .cube-tile.center { width: calc(var(--bw) * .12); height: calc(var(--bw) * .12); }
/* «Ускорить» и «Сдаться» — внизу правой рамки. */
body[data-orientation="landscape"] .rail-mid { grid-column: 5; grid-row: 7; align-self: end; justify-self: center; padding: 0 0 0 6px; margin-bottom: 12px; gap: calc(var(--bw) * .05); }
body[data-orientation="landscape"] .rail-mid > :is(.autoplay, .resign-btn) { width: calc(var(--bw) * .13); }
body[data-orientation="landscape"] #rail-player { margin-top: 0; }

/* Доска: размер задан явно (у обёртки container-type: size — по содержимому она нулевая). */
body[data-orientation="landscape"] .board-wrap {
  grid-column: 3; grid-row: 2 / 8;
  width: var(--land-bw); height: var(--land-bh); align-self: center;
}
body[data-orientation="landscape"] .board { height: min(100cqh, 100cqw / var(--board-aspect, 1.427)); }
/* Кнопки на доске — от вертикального эквивалента доски, как кости (11cqw широкой доски выходили крупнее зазора рядов). */
body[data-orientation="landscape"] .board-half { --ctl-h: calc(var(--board-width, 340px) * .11); }

/* Лотки стоймя: соперник — у верхнего ряда, игрок — у нижнего; колонка — со стороны выброса. */
body[data-orientation="landscape"] #app .tray {   /* только игровой экран: лотки урока — лёжа, как в вертикали */
  --tray-h: var(--tray-w);
  grid-column: 4; grid-row: 2 / 8; justify-self: end;
  width: var(--tray-w);
  height: calc(var(--oc-h) * 15 + 14px + 4px);
  display: flex; flex-direction: column; gap: 1px;
  border-radius: calc(var(--tray-w) * .2);
}
/* Лотки в горизонтали — оба справа (Влад, 21.10: «лаконично»), и в коротких, и в длинных. */
body[data-orientation="landscape"] #tray-opponent { align-self: start; margin-top: 0; }   /* верх — на линии верхнего края доски */
body[data-orientation="landscape"] #tray-player { flex-direction: column-reverse; align-self: end; margin-bottom: 0; }   /* низ — на линии нижнего края доски */
body[data-orientation="landscape"] #app .off-chip {
  position: relative; flex: none; width: 100%; height: var(--oc-h); aspect-ratio: auto; background: none;
}
/* Спрайт шашки на ребре стоячий (24×64) — в стоячем лотке он лежит поперёк. */
body[data-orientation="landscape"] #app .off-chip::before {
  content: ''; position: absolute; left: 50%; top: 50%;
  width: var(--oc-h); height: calc(var(--tray-w) - 4px);
  transform: translate(-50%, -50%) rotate(90deg);
  background: var(--img) center / 100% 100% no-repeat;
}

/* Разбор хода и удвоение — на месте колонки игрока, от «Назад» до низа; профиль и пипы игрока под ними гаснут. */
body[data-orientation="landscape"] :is(.train-review, .offer-bar) {
  position: relative; grid-column: 1; grid-row: 2 / 8; align-self: stretch; z-index: 3;
  left: auto; top: auto !important; bottom: auto !important; width: auto; transform: none; margin-right: 4px;
  grid-template-columns: minmax(0, 1fr); align-content: center; overflow-y: auto;
}
body[data-orientation="landscape"] .offer-bar.ask { grid-template-columns: minmax(0, 1fr); justify-items: center; text-align: center; }
body[data-orientation="landscape"] .players.reviewing ~ .rail .pips { visibility: hidden; }
/* Удвоение в горизонтали — справа: игроки слева остаются видны. */
body[data-orientation="landscape"] .players.offering > .player { opacity: 1; visibility: inherit; }

/* ── Горизонталь: разбор партии (21.10, Влад: «доску как в горизонтальной версии, справа плотнее») ──
   Широкая доска слева во всю высоту, пипы под ней. Справа колонка (не уже 340 px): шапка окна (она лежит над этой
   колонкой), строка матча, PR сторон, строка хода, вердикт, навигация, варианты; куб, график, ходы, .mat — прокруткой. */
body[data-orientation="landscape"] .window {
  /* --wz — увеличение окна на планшете (zoom): экранные единицы внутри окна делятся на него */
  --rp-h: calc((100dvh - var(--banner) - var(--safe-top) - var(--safe-bottom)) / var(--wz, 1) - 44px);
  --rp-bw: min(calc(var(--rp-h) * 1056 / 740), calc((100vw - var(--safe-left) - var(--safe-right)) / var(--wz, 1) - 360px));
  --rp-bh: calc(var(--rp-bw) * 740 / 1056);
}
body[data-orientation="landscape"] .overlay.full .window:has(.replay) {
  width: 100%;
  grid-template-columns: calc(var(--rp-bw) + 18px) minmax(0, 1fr);
  grid-template-rows: auto minmax(0, 1fr);
}
body[data-orientation="landscape"] .window:has(.replay) > .window-head { grid-column: 2; grid-row: 1; z-index: 2; padding-top: 6px; }
body[data-orientation="landscape"] .window:has(.replay) > .window-body {
  grid-column: 1 / -1; grid-row: 1 / -1; overflow: hidden; padding: 8px 0 0 8px; scrollbar-gutter: auto;
}
body[data-orientation="landscape"] .replay {
  height: 100%;
  grid-template-columns: auto minmax(0, 1fr);
  grid-template-rows: auto minmax(0, 1fr) auto;
  grid-template-areas: "board info" "board side" "pips side";
  gap: 6px 14px; align-content: center;
}
body[data-orientation="landscape"] .replay > .replay-board,
body[data-orientation="landscape"] .replay:has(.review-side) > .replay-board {
  grid-area: board; height: var(--rp-bh); width: var(--rp-bw); align-self: center;
}
body[data-orientation="landscape"] .replay > .replay-pips { grid-area: pips; margin: 0; padding-bottom: 6px; }
body[data-orientation="landscape"] .replay > .replay-info { grid-area: info; padding-top: 52px; text-align: left; }
body[data-orientation="landscape"] .replay-side {
  grid-area: side; display: flex; flex-direction: column; gap: 6px; min-height: 0;
  overflow-y: auto; overscroll-behavior: contain; padding: 0 10px 12px 0;
}
body[data-orientation="landscape"] .replay-side > * { order: 0; flex: none; }
body[data-orientation="landscape"] .replay .replay-caption,
body[data-orientation="landscape"] .replay .replay-verdict { text-align: left; }
body[data-orientation="landscape"] .replay .cand { padding-block: 3px; }
/* Плашка «Анализ…» — на всё окно, кроме шапки с «Назад». */
/* Плашка «Разбираем партию» (22.10, Влад: «криво под горизонталку»): затемняет всю панель разбора (доску и колонку), карточка
   с прогрессом — по центру; «Назад» над ней остаётся доступной. */
body[data-orientation="landscape"] .window:has(.replay) { position: relative; }
body[data-orientation="landscape"] .review-block { position: absolute; inset: 0; border-radius: inherit; }
body[data-orientation="landscape"] .window:has(.replay) > .window-head { position: relative; z-index: 6; }

/* ── Горизонталь: урок обучения — как разбор: широкая доска с лотками слева, справа задание и стрелки под ним. ── */
body[data-orientation="landscape"] .window--lesson {
  --ls-h: calc((100dvh - var(--banner) - var(--safe-top) - var(--safe-bottom)) / var(--wz, 1) - 80px);
  --ls-bw: min(calc(var(--ls-h) * 1056 / 740), calc((100vw - var(--safe-left) - var(--safe-right)) / var(--wz, 1) - 380px));
}
body[data-orientation="landscape"] .overlay.full .window--lesson {
  width: 100%; display: grid;
  grid-template-columns: calc(var(--ls-bw) + 18px) minmax(0, 1fr); grid-template-rows: auto minmax(0, 1fr);
}
body[data-orientation="landscape"] .window--lesson > .window-head { grid-column: 2; grid-row: 1; z-index: 2; }
body[data-orientation="landscape"] .window--lesson > .window-body { grid-column: 1 / -1; grid-row: 1 / -1; display: block; overflow: hidden; padding: 6px 12px 0 8px; }
body[data-orientation="landscape"] .window--lesson .lesson {
  display: grid; height: 100%;
  grid-template-columns: auto minmax(0, 1fr);
  grid-template-rows: auto minmax(0, 1fr) auto auto minmax(0, 1fr) auto;
  grid-template-areas: "trayB ." "board ." "board task" "board nav" "board ." "tray .";
  gap: 6px 18px; align-content: center;
}
body[data-orientation="landscape"] .window--lesson .lesson > .lesson-tray-row:first-child { grid-area: trayB; }
body[data-orientation="landscape"] .window--lesson .lesson > .lesson-tray-row:not(:first-child) { grid-area: tray; }
body[data-orientation="landscape"] .window--lesson .lesson > .lesson-board {
  grid-area: board; width: var(--ls-bw); height: calc(var(--ls-bw) * 740 / 1056); max-height: none; min-height: 0; align-self: center;
}
/* Задание и стрелки — плотным блоком по центру колонки, не во всю её ширину. */
body[data-orientation="landscape"] .window--lesson .lesson > :is(.lesson-task, .lesson-nav) { justify-self: center; width: min(100%, 420px); }
body[data-orientation="landscape"] .window--lesson .lesson > .lesson-task { grid-area: task; }
body[data-orientation="landscape"] .window--lesson .lesson > .lesson-nav { grid-area: nav; }

/* ── Горизонталь: меню и окна (21.10, переверстка по отзыву Влада) ──
   Кнопки — комфортной ширины (~320 px) по центру, а не во всю ширину окна; строки — в две колонки; поля и фильтры —
   своей ширины слева; списки — карточками размера вертикали, их просто больше в ряд. */

/* 1. Главное меню: логотип крупнее (угловые кнопки поверх его строки), кнопки ~320 px ниже, без пустого места под третью. */
body[data-orientation="landscape"] .menu { grid-template-rows: minmax(0, 1fr) auto; gap: clamp(8px, 2.4vh, 18px); padding-bottom: 6px; }
body[data-orientation="landscape"] .menu-corners { position: absolute; inset: 0 0 auto 0; margin-inline: 0; z-index: 2; }
body[data-orientation="landscape"] .menu-logo { width: min(56%, 38rem); padding-top: 4px; }
body[data-orientation="landscape"] .menu-actions { width: min(100%, 34rem, 37vw); gap: 10px; }   /* доля экрана — как на планшете */
body[data-orientation="landscape"] .menu-slot { display: none; }

/* Крупные кнопки в окнах: не во всю ширину, надпись крупнее относительно кнопки. */
body[data-orientation="landscape"] .window:not(.chromeless) .big-btn { min-height: 48px; font-size: 18px; }
body[data-orientation="landscape"] .window-foot > .big-btn,
body[data-orientation="landscape"] .window .fields > .big-btn { justify-self: center; width: min(100%, 320px); }
body[data-orientation="landscape"] .window-foot { justify-items: center; }
body[data-orientation="landscape"] .window-foot > .result-actions { width: min(100%, 520px); }

/* Подсказка «листается дальше»: стрелка внизу окна, пока ниже есть содержимое (как в «Пауке»). */
body[data-orientation="landscape"] .window-body.more::after {
  content: ''; position: sticky; bottom: 0; display: block; height: 30px; margin-top: -30px; pointer-events: none;
  background: no-repeat center bottom 4px / 22px 14px url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 22 14'%3E%3Cpath d='M2 2l9 9 9-9' fill='none' stroke='%23f0c04a' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E"),
    none;   /* без тёмной полосы-градиента (Влад, 21.10) — только стрелка */
  animation: more-bob 1.4s ease-in-out infinite;
}
@keyframes more-bob { 50% { background-position: center bottom 0; } }

/* Окна-списки шире и почти во всю высоту. */
body[data-orientation="landscape"] .window:is(.window--newgame, .window--settings, .window--style, .window--statistic,
  .window--about, .window--tutorial, .window--history, .window--external, .window--rules) { width: min(860px, 100%); }
body[data-orientation="landscape"] :is(.window--about, .window--rules, .window--statistic, .window--style, .window--external,
  .window--history, .window--tutorial) { height: 100%; }

/* 2–3. Новая игра, настройки: строки в две колонки; тренировка — в левой колонке, пояснение под ней. */
body[data-orientation="landscape"] :is(.window--newgame, .window--settings, .window--statistic) .fields {
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); column-gap: 12px;
}
body[data-orientation="landscape"] :is(.window--newgame, .window--settings, .window--statistic) .fields > :not(.field-row, .history-link, .training-block) { grid-column: 1 / -1; }
/* Тренировка: переключатель — в левой колонке, пояснение — справа от него (в две строки, без прокрутки окна). */

/* 4. Оформление: имя — короткой строкой; доски по 5 в ряд и по центру (второй ряд тоже), помельче. */
body[data-orientation="landscape"] .window--style .fields > .field-row:first-child { width: min(100%, 420px); }
body[data-orientation="landscape"] .board-grid { display: flex; flex-wrap: wrap; justify-content: center; gap: 10px; }
body[data-orientation="landscape"] .board-grid > * { flex: 0 0 calc((100% - 40px) / 5); max-width: 130px; }
body[data-orientation="landscape"] .avatar-grid { display: flex; flex-wrap: wrap; justify-content: center; gap: 8px; }
body[data-orientation="landscape"] .avatar-grid > * { flex: 0 0 calc((100% - 56px) / 8); max-width: 86px; }

/* 5. Правила: картинка слева во всю высоту страницы (от заголовка), справа заголовок и текст колонкой плотнее. */
body[data-orientation="landscape"] .window--rules .rules-page {
  display: grid; grid-template-columns: auto minmax(0, 1fr); grid-template-rows: auto minmax(0, 1fr); gap: 6px 20px; overflow: hidden;
}
body[data-orientation="landscape"] .window--rules .rules-picture { grid-column: 1; grid-row: 1 / -1; align-self: stretch; height: 100%; width: auto; max-height: none; }
body[data-orientation="landscape"] .window--rules .rules-picture[hidden] { display: none; }
body[data-orientation="landscape"] .window--rules .rules-title { grid-column: 2; grid-row: 1; text-align: left; }
body[data-orientation="landscape"] .window--rules .rules-text { grid-column: 2; grid-row: 2; overflow-y: auto; min-height: 0; max-width: 46em; line-height: 1.3; font-size: 12.5px; }
body[data-orientation="landscape"] .window--rules .rules-picture[hidden] ~ :is(.rules-title, .rules-text) { grid-column: 1 / -1; }

/* 6. Об игре (Влад, 21.10): сверху логотип | плашка «Правила» (обучение, длинные, короткие); ниже текст в две колонки. */
body[data-orientation="landscape"] .about { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); column-gap: 26px; align-items: center; }
body[data-orientation="landscape"] .about-side { display: contents; }
body[data-orientation="landscape"] .about-side > .about-logo { grid-column: 1; grid-row: 1; width: min(70%, 230px); }
body[data-orientation="landscape"] .about-side > .about-rules-box { grid-column: 2; grid-row: 1; margin: 0; }
body[data-orientation="landscape"] .about-text { display: block; grid-column: 1 / -1; grid-row: 2; columns: 2; column-gap: 28px; }
body[data-orientation="landscape"] .about-text > * { break-inside: avoid; margin: 0 0 10px; }
body[data-orientation="landscape"] .about-text > .about-h { break-after: avoid; }

/* 7. Обучение: колонка длинных | колонка коротких; заголовки веток — белые и крупнее. */
body[data-orientation="landscape"] .tut-list { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); column-gap: 14px; align-items: start; }
body[data-orientation="landscape"] .tut-group { display: grid; gap: 6px; }
body[data-orientation="landscape"] .tut-branch { margin-top: 2px; font-size: 13px; font-weight: 800; color: #fff; }

/* 8. Статистика: разделы колонками (матчи | партии | как выиграны …), строки короткие — значение рядом с названием. */
body[data-orientation="landscape"] .stats { grid-template-columns: repeat(auto-fill, minmax(230px, 1fr)); gap: 12px 18px; align-items: start; }
body[data-orientation="landscape"] .stat-group { display: grid; gap: 2px; }
body[data-orientation="landscape"] .stat-group > .stat-section { margin-top: 0; }
body[data-orientation="landscape"] .stat-group.dice-group { grid-column: 1 / -1; }

/* 9. История (и разбор файлов): фильтры своей ширины слева в одну строку, карточки размера вертикали — по три в ряд. */
body[data-orientation="landscape"] :is(.window--history, .window--external) .fields { grid-template-columns: repeat(2, minmax(0, 340px)); column-gap: 12px; }
body[data-orientation="landscape"] :is(.window--history, .window--external) .fields > :not(.field-row) { grid-column: 1 / -1; }
body[data-orientation="landscape"] .history-list { grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); align-items: start; gap: 8px; }
/* Карточка партии — лаконичная (Влад, 21.10: «гигантские»): поля меньше, кнопки ниже и уже. */
body[data-orientation="landscape"] .history-row { padding: 7px 10px; gap: 2px 8px; }
body[data-orientation="landscape"] .history-what { font-size: 12px; }
body[data-orientation="landscape"] .history-score { font-size: 16px; }
body[data-orientation="landscape"] .history-actions { margin-top: 2px; gap: 6px; }
body[data-orientation="landscape"] :is(.history-row .analyze-btn:not(.big), .history-row .view-btn) { min-width: 0; height: 30px; padding: 0 12px; font-size: 13px; }
body[data-orientation="landscape"] .history-list > :not(.history-row) { grid-column: 1 / -1; }

/* Итог партии — как в вертикали, только помещается (Влад, 21.10: «профили крупнее, цифры и кнопки меньше»):
   у стороны табло аватар и счёт рядом, имя под ними; лента ниже; шапки нет (она пустая). */
body[data-orientation="landscape"] .window--result { width: min(400px, 100%); }
body[data-orientation="landscape"] .window--result > .window-head { display: none; }
body[data-orientation="landscape"] .window--result .window-body { padding-block: 10px 2px; }
body[data-orientation="landscape"] .window--result .result { gap: 4px; padding-top: 0; }
body[data-orientation="landscape"] .window--result .result-flag { width: min(66%, 240px); }
body[data-orientation="landscape"] .window--result .result-winner b { font-size: 22px; }
body[data-orientation="landscape"] .window--result .result-board { margin-top: 2px; }
body[data-orientation="landscape"] .window--result .result-side { grid-template-columns: auto auto; grid-template-areas: "ava num" "name name"; align-items: center; column-gap: 10px; }
body[data-orientation="landscape"] .window--result .result-side.side-opponent { grid-template-areas: "num ava" "name name"; }
body[data-orientation="landscape"] .window--result .result-ava { grid-area: ava; }
body[data-orientation="landscape"] .window--result .result-ava .ava-wrap { width: 72px; }
body[data-orientation="landscape"] .window--result .result-num { grid-area: num; font-size: 26px; }
body[data-orientation="landscape"] .window--result .result-name { grid-area: name; }
body[data-orientation="landscape"] .window--result .result-colon { align-self: center; font-size: 22px; padding: 0; }
body[data-orientation="landscape"] .window--result .result-actions { grid-template-columns: 1fr 1fr; }
body[data-orientation="landscape"] .window--result .window-foot { padding-block: 8px 10px; }
body[data-orientation="landscape"] .window--result .big-btn { min-height: 40px; font-size: 16px; }

/* Подтверждение — небольшое окно, кнопки в ряд и не огромные (пропорции как на планшете). */
body[data-orientation="landscape"] .window--confirm { width: min(330px, 100%); }
body[data-orientation="landscape"] .window--confirm .result-actions { grid-template-columns: 1fr 1fr; }
body[data-orientation="landscape"] .window--confirm .big-btn { min-height: 40px; font-size: 16px; }

/* 19. Оценка: текст в две строки, а не одной длинной. */
body[data-orientation="landscape"] .rate-ask p { max-width: 22ch; margin-inline: auto; text-wrap: balance; }
/* Новая игра в низком окне: строки плотнее — всё, включая пояснение к тренировке, без прокрутки. */
body[data-orientation="landscape"] .window--newgame .fields { row-gap: 5px; }
body[data-orientation="landscape"] .window--newgame .field-row { min-height: 36px; padding-block: 2px; }

/* Значения переключателей — всегда в одну строку («Друг (по очереди)», «Горизонтальная»): колонка значения по самому
   длинному варианту (скрытый образец в ячейке), строка не растёт в высоту; подпись берёт остаток. */
/* Колонка переключателя одной ширины во всех строках (Влад: «стрелки не на одном уровне») — по самому длинному значению
   («Друг (по очереди)»); подпись берёт остаток. */
body[data-orientation="landscape"] .field-row:has(.selector) { grid-template-columns: auto minmax(0, 1fr) 206px; }
body[data-orientation="landscape"] .field-row:has(.selector) .selector { grid-template-columns: 30px minmax(0, 1fr) 30px; gap: 4px; }
body[data-orientation="landscape"] /* Слово не рвётся по слогам (hyphens/overflow-wrap выключены); фраза из нескольких слов («Друг (по очереди)») переносится по пробелу. */
.field-row:has(.selector) .selector-value { hyphens: none; overflow-wrap: normal; }
body[data-orientation="landscape"] .field-row:has(.selector) .field-label { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* Разбор партии: перемотка ниже — пять вариантов хода видны без прокрутки. */
body[data-orientation="landscape"] .replay .replay-btn { padding: 4px 6px; font-size: 14px; min-height: 0; height: 32px; }
body[data-orientation="landscape"] .replay .replay-btn.err { font-size: 13px; }

/* Надписи на кнопках доски — по центру по высоте букв (без обрезки строка садилась выше середины). */
.board-btn { text-box: trim-both cap alphabetic; }

/* Удвоение и разбор хода в горизонтали — от левого края до доски; на планшете высотой с доску, по центру. */
/* Разбор хода в тренировке — тоже справа, поверх лотков и рамки матча (Влад: «широкая хорошая плашка под текст»). */
body[data-orientation="landscape"] .train-review { grid-column: 4 / 6; grid-row: 2 / 8; align-self: stretch; height: auto; margin: 0 0 0 6px; }
/* Удвоение — справа, поверх лотков и рамки матча, её размером. */
body[data-orientation="landscape"] .offer-bar { grid-column: 4 / 6; grid-row: 2 / 8; align-self: stretch; height: auto; margin: 0 0 0 6px; }
body[data-orientation="landscape"] .offer-bar .ob-btns .big-btn { height: calc(var(--bw) * .13); min-height: 0; width: 100%; font-size: calc(var(--bw) * .05); border-radius: 12px; }
body[data-orientation="landscape"] .offer-bar .ob-btns { width: 100%; display: grid; gap: 14px; margin-top: 4px; }

/* Правила: картинке — вся высота до черты над стрелками (поля окна и подвала тоньше). */
body[data-orientation="landscape"] .window--rules .window-head { padding-block: 6px 0; }
body[data-orientation="landscape"] .window--rules .window-body { padding-bottom: 4px; }
body[data-orientation="landscape"] .window--rules .rules-page { padding: 0 4px 0 0; }
body[data-orientation="landscape"] .window--rules .window-foot { padding-block: 4px 6px; }

/* Разбор партии: шапка окна ниже, строки плотнее — пять вариантов хода без прокрутки. */
body[data-orientation="landscape"] .window:has(.replay) > .window-head { padding-block: 4px 0; }
body[data-orientation="landscape"] .window:has(.replay) .window-back { width: 34px; height: 34px; }
body[data-orientation="landscape"] .replay > .replay-info { padding-top: 40px; }
body[data-orientation="landscape"] .replay .review-summary { min-height: 0; }
body[data-orientation="landscape"] .replay .review-side { padding: 3px 8px; }
body[data-orientation="landscape"] .replay .replay-verdict { min-height: 0; }
body[data-orientation="landscape"] .replay .cand { padding-block: 2px; }
body[data-orientation="landscape"] .replay-side { gap: 5px; }
/* Шансы сыгранного хода (раскрытая строка) — под списком вариантов, а не посреди него: пять вариантов видны сразу. */
body[data-orientation="landscape"] .review-cands > .cand-detail { order: 1; }
body[data-orientation="landscape"] .review-cands > .cand-cap { display: none; }   /* «Варианты» — и так видно по шапке «Ход · Δ · Победа» */
body[data-orientation="landscape"] .replay .replay-btn { height: 30px; }

/* Новая игра: тренировка — отдельным модулем между двумя чертами во всю ширину окна (сверху — своя, снизу — черта подвала):
   переключатель слева, пояснение рядом в две строки; по высоте — посередине между чертами. */
body[data-orientation="landscape"] .window--newgame .fields > .training-block {
  grid-column: 1 / -1; display: flex; align-items: center; gap: 16px;
  margin: 4px -16px -14px; padding: 10px 16px; border-top: 1px solid rgba(160, 196, 245, .14);
}
/* Переключатель — ровно шириной левой колонки строк выше; пояснение — две строки по предложениям (перенос в тексте). */
body[data-orientation="landscape"] .window--newgame .training-block { gap: 12px; }
body[data-orientation="landscape"] .window--newgame .training-block > .field-row { flex: 0 0 calc(50% - 6px); box-sizing: border-box; }
body[data-orientation="landscape"] .window--newgame .training-block > .field-note { margin: 0; white-space: pre-line; line-height: 1.35; }

/* Оформление: подписи разделов («Имя игрока», «Вид доски», «Аватарка», «Цвет фона») — по центру, как сами плитки. */
body[data-orientation="landscape"] .window--style .field-block > .field-label { text-align: center; }
body[data-orientation="landscape"] .window--style .fields > .field-row:first-child { justify-self: center; }

/* Правила: стрелки симметрично вокруг точек. */
body[data-orientation="landscape"] .window--rules .window-foot > .rules-nav { grid-template-columns: 40px auto 40px; column-gap: 18px; justify-content: center; }

/* Об игре: текст одной колонкой во всю ширину. */
body[data-orientation="landscape"] .about-text { columns: auto; }

/* Обучение: третья ветка («Продвинутое») — с отступом от глав над ней. */
body[data-orientation="landscape"] .tut-group:nth-child(n + 3) { margin-top: 14px; }

/* Разбор партии и урок — такой же панелью, как остальные окна (скругления, рамка, фон), а не прозрачным слоем. */
body[data-orientation="landscape"] .overlay.full .window:is(:has(.replay), .window--lesson) {
  background: linear-gradient(180deg, #1d3152, #132340); border: 1px solid rgba(160, 196, 245, .22); border-radius: 18px;
  margin: 6px 8px 4px; height: calc(100% - 10px);
}
body[data-orientation="landscape"] .replay-side.more::after {
  content: ''; position: sticky; bottom: 0; display: block; flex: none; height: 30px; margin-top: -30px; pointer-events: none;
  background: no-repeat center bottom 4px / 22px 14px url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 22 14'%3E%3Cpath d='M2 2l9 9 9-9' fill='none' stroke='%23f0c04a' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E"),
    none;
}

/* Итог партии на телефоне без прокрутки (Влад, 21.10): «Партия N» и «Победа в партии» крупнее, рамка табло тоньше,
   кнопки ниже. */
body[data-orientation="landscape"] .window--result .result-flag span { font-size: 22px; }
body[data-orientation="landscape"] .window--result .result-winner span { font-size: 14px; }
body[data-orientation="landscape"] .window--result .result-winner { gap: 0; }
body[data-orientation="landscape"] .window--result .result-board { padding: 4px 8px; border-radius: 12px; }
body[data-orientation="landscape"] .window--result .result-ava .ava-wrap { width: 64px; }
body[data-orientation="landscape"] .window--result .big-btn { min-height: 36px; font-size: 15px; }
body[data-orientation="landscape"] .window--result .window-foot { padding-block: 6px 8px; }

/* Кнопки на доске — крупнее и шире (Влад: «малюсенькие»). */
body[data-orientation="landscape"] .board-half { --ctl-h: calc(var(--board-width, 340px) * .13); }
body[data-orientation="landscape"] .board-btn.wide { padding: 0 calc(var(--ctl-h) * .55); }

/* Урок: ряды лотков держат место и когда пусты (не прыгает доска и плашка между шагами), плашка одной высоты. */
body[data-orientation="landscape"] .window--lesson .lesson > .lesson-tray-row[hidden] { display: flex; visibility: hidden; }
body[data-orientation="landscape"] .window--lesson .lesson > .lesson-task { min-height: 140px; align-content: center; }

/* Итог (Влад, 21.10): окно на ~15% меньше, имя — по центру под аватаром. */
body[data-orientation="landscape"] .window--result { width: min(340px, 100%); }
body[data-orientation="landscape"] .window--result .result-flag { width: min(66%, 210px); }
body[data-orientation="landscape"] .window--result .result-ava .ava-wrap { width: 58px; }
body[data-orientation="landscape"] .window--result .result-side { grid-template-columns: auto auto; justify-items: center; }
body[data-orientation="landscape"] .window--result .result-name { justify-self: stretch; text-align: center; }
body[data-orientation="landscape"] .window--result .result-side .result-ava { justify-self: center; }
body[data-orientation="landscape"] .window--result .result-side.side-player .result-name { grid-column: 1; }
body[data-orientation="landscape"] .window--result .result-side.side-opponent .result-name { grid-column: 2; }

/* История: колонки карточек не шире 340 px и по центру; PR — плашкой ровно с кнопку «Посмотреть». */
body[data-orientation="landscape"] .history-list { grid-template-columns: repeat(auto-fill, minmax(250px, 340px)); justify-content: center; column-gap: 12px; }
body[data-orientation="landscape"] :is(.history-row .hist-pr, .history-row .view-btn, .history-row .analyze-btn:not(.big)) { width: 104px; min-width: 0; justify-content: center; }
.history-row .hist-pr { min-width: 138px; justify-content: center; }

/* Разбор партии: значок «Назад» крупнее; доска прижата к пипсам (на планшете между ними был зазор). */
body[data-orientation="landscape"] .window:has(.replay) .window-back { width: 40px; height: 40px; }
body[data-orientation="landscape"] .window:has(.replay) .window-back::after { width: 58%; height: 58%; }
body[data-orientation="landscape"] .replay > .replay-board,
body[data-orientation="landscape"] .replay:has(.review-side) > .replay-board { align-self: end; }
body[data-orientation="landscape"] .replay { align-content: end; }

/* Лотки — оба ровно по центру своей колонки (у лотка чёрных в длинных срабатывало вертикальное «к краю»). */
body[data-orientation="landscape"] .rail .tray { justify-self: center; order: 0; }
/* В разборе хода игроки слева остаются видны (табличка справа). */
body[data-orientation="landscape"] .players.reviewing > .player { opacity: 1; visibility: inherit; }
body[data-orientation="landscape"] .players.reviewing ~ .rail .pips { visibility: inherit; }

/* История: PR, корзина и кнопка — одной высоты (горизонталь 30, вертикаль 38). */
.history-actions .trash-btn { flex: none; width: 38px; height: 38px; }
body[data-orientation="landscape"] .history-actions .trash-btn { width: 30px; height: 30px; background-size: 16px; }
/* История: фильтры — двумя колонками ровно над колонками карточек, по центру; небольшой промежуток до карточек. */
body[data-orientation="landscape"] :is(.window--history, .window--external) .fields { grid-template-columns: repeat(2, minmax(0, 340px)); justify-content: center; column-gap: 12px; row-gap: 12px; }

/* Итог: двоеточие — по середине цифр счёта (у сторон под аватаром ещё строка имени, и центр стороны ниже центра цифр). */
body[data-orientation="landscape"] .window--result .result-colon { align-self: start; margin-top: calc(29px - .6em); }

/* Разбор партии на телефоне: доска чуть ниже (не задевает скругление панели), стрелка «листается дальше» — маленькая в углу,
   не перекрывает пятый вариант. */
body[data-orientation="landscape"] .window:has(.replay) > .window-body { padding: 8px 0 0 10px; }
body[data-orientation="landscape"] .window { --rp-h: calc((100dvh - var(--banner) - var(--safe-top) - var(--safe-bottom)) / var(--wz, 1) - 52px); }
body[data-orientation="landscape"] .replay-side.more::after { height: 16px; margin-top: -16px; background: no-repeat right 6px bottom 2px / 16px 10px url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 22 14'%3E%3Cpath d='M2 2l9 9 9-9' fill='none' stroke='%23f0c04a' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E"); }
/* Планшет: в разборе — вертикальная доска (ниже и уже), правая колонка разбора — шире. */
@media (min-height: 600px) {
  body[data-orientation="landscape"] .window { --rp-bw: calc(var(--rp-h) * 766 / 858); --rp-bh: var(--rp-h); }
}

/* «Лучший ход / Ваш ход» и проценты — по центру овала по высоте букв (строка центрировалась вместе с местом под хвостики
   «у», «д» — текст сидел выше середины). Поля чуть больше — высота овала прежняя. */
.tr-move > * { text-box: trim-both cap alphabetic; }
.tr-move { padding-block: calc(var(--bw) * .015 + 3px); }

/* Горизонталь: режим матча — тем же шрифтом, что «до N очков». */
body[data-orientation="landscape"] .screen-title { font-size: calc(var(--bw) * .038); font-weight: 700; letter-spacing: .4px; text-transform: uppercase; color: var(--ink-dim); line-height: 1.25; }
/* Имя — по центру под аватаром (а не под аватаром со счётом вместе — уезжало вправо). */
body[data-orientation="landscape"] .player-name { grid-area: auto; grid-column: 1; grid-row: 2; justify-self: center; text-align: center; max-width: calc(var(--bw) * .34); }
/* Кнопки плашек — одной формы: «Подсказка / Пропустить» на доске и «Принять / Отказаться» в удвоении. */
body[data-orientation="landscape"] :is(.train-explain-btns, .ob-btns) .big-btn {
  height: clamp(36px, calc(var(--board-width, 340px) * .12), 54px); min-height: 0; border-radius: 12px;
  font-size: clamp(13px, calc(var(--board-width, 340px) * .045), 19px);
}
/* Вертикаль: кнопки удвоения — той же формы, что «Подсказка / Пропустить» (высота, шрифт, скругление). */
.ob-btns .big-btn { height: calc(var(--bw) * .11); font-size: max(12px, calc(var(--bw) * .04)); border-radius: 14px; }

/* ── Предупреждение о рекламе (21.10) ── */
.window--adnotice { width: min(360px, 100%); }
.adn { display: grid; gap: 10px; text-align: center; font-size: 15px; line-height: 1.4; color: #dbe6f5; padding-bottom: 2px; }
.adn p { margin: 0; }
.adn-close { text-wrap: pretty; }
.adn-thanks { font-weight: 800; color: var(--accent); }
.adn-who { font-size: 13px; color: var(--ink-dim); }
/* Значки — как кнопки закрытия в рекламе Яндекса: белый знак в тёмном полупрозрачном круге. */
.adn-ico { display: inline-block; width: 1.7em; height: 1.7em; margin: 0 .15em; vertical-align: -.48em; border-radius: 50%;
  background: rgba(0, 0, 0, .55) center / 52% no-repeat; box-shadow: 0 0 0 1px rgba(255, 255, 255, .28); }
/* Как в рекламе Яндекса (сняты с тестового объявления, 21.10): тонкий серый крестик и «пропустить» ▶| в тёмном круге. */
.adn-ico.x { background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M6 6l12 12M18 6L6 18' stroke='%23d9d9d9' stroke-width='2.4' stroke-linecap='round'/%3E%3C/svg%3E"); }
.adn-ico.arrow { background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M6 5.5v13l9.5-6.5z' fill='%23d9d9d9'/%3E%3Crect x='16.6' y='5.5' width='2.6' height='13' rx='1.1' fill='%23d9d9d9'/%3E%3C/svg%3E"); }
.window--adnotice .window-foot > .big-btn { justify-self: center; width: min(100%, 260px); }
body[data-orientation="landscape"] .window--adnotice { width: min(560px, 100%); }
body[data-orientation="landscape"] .window--adnotice .adn { gap: 6px; }
body[data-orientation="landscape"] .window--adnotice .window-foot { padding-block: 8px 10px; }
.adn-ico.arrow { background-size: 66%; }

/* Горизонталь: «Назад» и «Настройки» в игре — в стиле рамок игроков (тёмные, золотая обводка), не ярко-синие: меньше
   отвлекают от доски (Влад, 21.10). */
/* (22.10: и в вертикали — игровой экран, Влад) */
:is(#btn-back, #btn-settings) {
  background: linear-gradient(180deg, rgba(24, 40, 68, .92), rgba(12, 22, 42, .95));
  border: 1px solid rgba(240, 192, 74, .28); box-shadow: 0 6px 16px -10px #000;
}
:is(#btn-back, #btn-settings) > i { opacity: .85; }
:is(#btn-back, #btn-settings):active { transform: translateY(1px); box-shadow: none; }
.autoplay.gone { display: none; }
body[data-orientation="landscape"] .rail-mid > .autoplay.gone { display: flex; visibility: hidden; }

/* ── Горизонталь на узких телефонах (≤ 720 px: Redmi 640×360, SE 667×375; Влад, 21.10: «тексты обрезаются, поле мелкое»).
   Pixel, 844 и планшеты не меняются. ── */
@media (max-width: 720px) {
  body[data-orientation="landscape"] { --side-min: 96px; }
  body[data-orientation="landscape"] :is(.topbar, .players, .pips, .rail-mid, .goal-col, .player, .train-review, .offer-bar) {
    --bw: min(var(--board-width, 340px), calc(var(--side-w) * 2.2));
  }
  /* Строки с переключателем: колонка значения уже, стрелки и шрифт мельче — подписи целиком. */
  body[data-orientation="landscape"] .field-row:has(.selector) { grid-template-columns: auto minmax(0, 1fr) 168px; gap: 4px; padding-inline: 8px; }
  body[data-orientation="landscape"] .field-row:has(.selector) .selector { grid-template-columns: 24px minmax(0, 1fr) 24px; gap: 2px; }
  body[data-orientation="landscape"] .field-row:has(.selector) .selector-value { font-size: 12px; }
  /* подпись может уйти на вторую строку — в пределах высоты строки (две строки 12 px ниже её минимума) */
  body[data-orientation="landscape"] .field-row .field-label { font-size: 12px; line-height: 1.1; }
  body[data-orientation="landscape"] .field-row:has(.selector) .field-label { white-space: normal; overflow: visible; text-overflow: clip; }
  body[data-orientation="landscape"] .window--newgame .training-block > .field-note { font-size: 11px; }
  body[data-orientation="landscape"] .field-row:has(.selector) .arrow { width: 24px; height: 24px; }
  body[data-orientation="landscape"] .window-body { padding-inline: 10px; }
  body[data-orientation="landscape"] .window--newgame .fields > .training-block { margin-inline: -10px; padding-inline: 10px; }
}
@media (max-height: 400px) {
  body[data-orientation="landscape"] .window--newgame .field-row { min-height: 32px; }
  body[data-orientation="landscape"] .window-head { padding-block: 6px 2px; }
  body[data-orientation="landscape"] .window-foot { padding-block: 6px 8px; }
  body[data-orientation="landscape"] .window--newgame .fields > .training-block { padding-block: 6px; }
}

/* Статистика: блок PR («средняя цена ваших ошибок за ход») — во всю ширину окна, шкала тоже (23.10, Влад). */
body[data-orientation="landscape"] .window--statistic .fields > .pr-line { grid-column: 1 / -1; }
/* История: карточки в две колонки и на узких телефонах (Redmi, SE). */
/* (auto-fill считает число колонок по максимуму дорожки: при minmax(…, 340px) на 640 выходила одна — поэтому доля ширины) */
body[data-orientation="landscape"] .history-list { grid-template-columns: repeat(auto-fill, minmax(max(230px, 34%), 1fr)); }
/* Разбор: стрелка «листается дальше» — справа, в полосе прокрутки, не на таблице. */
body[data-orientation="landscape"] .replay-side { padding-right: 18px; }
body[data-orientation="landscape"] .replay-side.more::after { background-position: right 0 bottom 2px; margin-right: -18px; }
@media (max-width: 720px) {
  /* Пояснение к тренировке — ровно две строки (перенос в тексте), шрифт по ширине колонки. */
  body[data-orientation="landscape"] .window--newgame .training-block > .field-note { font-size: 10.5px; letter-spacing: -.1px; white-space: pre-line; }
}
@media (max-height: 400px) {
  /* Итог партии на низких телефонах помещается целиком (не листается — без стрелки). */
  body[data-orientation="landscape"] .window--result .result-flag { width: min(60%, 190px); }
  body[data-orientation="landscape"] .window--result .result-ava .ava-wrap { width: 50px; }
  body[data-orientation="landscape"] .window--result .result-winner b { font-size: 19px; }
  body[data-orientation="landscape"] .window--result .result-flag span { font-size: 19px; }
  body[data-orientation="landscape"] .window--result .window-body { padding-top: 6px; }
}
/* Урок в горизонтали: лоток — по шашке ДОСКИ УРОКА (0.068 её ширины), а не игровой доски — шашка на ребре в лотке равна
   шашке на поле, как в вертикали. */
body[data-orientation="landscape"] .window--lesson .lesson-tray { --tray-h: calc(var(--ls-bw) * .068 + 4px); }
body[data-orientation="landscape"] .window--lesson .lesson-tray-row { min-height: calc(var(--ls-bw) * .068 + 4px); }

/* Проверка вёрстки 21.10 (tools/stand/layout_check.py): */
/* 1. Горизонталь: имя под аватаром — во всю ширину рамки игрока, длинное («Друг (по очереди)», «Friend (same device)»)
   переносится на вторую строку, а не уходит за край. */
body[data-orientation="landscape"] .player-name { grid-column: 1 / -1; max-width: calc(var(--side-w) - 18px); white-space: normal;
  overflow-wrap: anywhere; line-height: 1.15; text-wrap: balance; text-overflow: clip; }
/* 2. История на узких вертикальных (360): плашка PR не уже своего текста. */
@media (max-width: 400px) { .history-row .hist-pr { min-width: 0; padding: 0 10px; } }
/* 3. Горизонталь: плашка ошибки в тренировке — от ширины доски (на 640×360 --bw мал, «Пропустить» не влезал). */
body[data-orientation="landscape"] .train-explain { width: min(calc(var(--land-bw) * .62), 440px); }
body[data-orientation="landscape"] .train-explain-btns .big-btn { padding: 0 10px; }

/* Xiaomi 10C (21.10): значения переключателей — в одну строку и в вертикали («Горизонтальная», «Вертикальная» переносились
   на 360 px); на узких — шрифт значения и подписи чуть меньше. */
/* Слово не рвётся по слогам (hyphens/overflow-wrap выключены); фраза из нескольких слов («Друг (по очереди)») переносится по пробелу. */
.field-row:has(.selector) .selector-value { hyphens: none; overflow-wrap: normal; }
@media (max-width: 400px) {
  body:not([data-orientation="landscape"]) .field-row:has(.selector) .selector-value { font-size: 12.5px; letter-spacing: -.1px; }
  body:not([data-orientation="landscape"]) .field-row:has(.selector) .field-label { font-size: 13px; }
}
/* Новая игра в горизонтали на высоте 360 с баннером — без прокрутки. */
@media (max-height: 380px) {
  body[data-orientation="landscape"] .window--newgame .fields { row-gap: 4px; }
  body[data-orientation="landscape"] .window--newgame .field-row { min-height: 30px; padding-block: 1px; }
  body[data-orientation="landscape"] .window--newgame .fields > .training-block { padding-block: 5px; margin-top: 2px; }
  body[data-orientation="landscape"] .window--newgame .window-head { padding-block: 4px 0; }
  body[data-orientation="landscape"] .window--newgame .window-body { padding-top: 4px; }
}
@media (max-width: 340px) {
  /* самые узкие (SE 1-го поколения, 320): длинному слову места нет — как раньше, перенос по слогам */
  body:not([data-orientation="landscape"]) .field-row:has(.selector) .selector-value { font-size: 12px; hyphens: auto; overflow-wrap: break-word; }
}

/* «Поделиться» (Xiaomi 10C, 21.10): подписи на карточке — от её ширины (капсом «ДЛИННЫЕ И КОРОТКИЕ НАРДЫ» не влезала и
   карточка «плыла»). Горизонталь: карточка слева высотой окна, подсказка и площадки справа — без прокрутки. */
.share-card { container-type: inline-size; }
.share-card-text b { font-size: 5.6cqw; letter-spacing: .03em; }
.share-card-text span { font-size: 4.6cqw; }
body[data-orientation="landscape"] .window--share { width: min(620px, 100%); }
body[data-orientation="landscape"] .share { grid-template-columns: auto minmax(0, 1fr); align-items: center; column-gap: 20px; }
body[data-orientation="landscape"] .share > .share-card { grid-row: 1 / span 3;
  width: min(300px, calc(100dvh - var(--banner) - var(--safe-top) - var(--safe-bottom) - 110px)); }
body[data-orientation="landscape"] .share > :not(.share-card) { grid-column: 2; }
body[data-orientation="landscape"] .sh-apps { flex-wrap: wrap; }

/* Горизонталь (Xiaomi 10C, 21.10): кости крупнее (на 22% — по доске той же высоты они выходили мельче, чем в вертикали);
   «Готово» и «Отмена» дальше друг от друга; «Бросить» и «Удвоить» на 10% длиннее, шрифт на пункт крупнее. */
body[data-orientation="landscape"] .board-half { --bw: calc(var(--board-width, 340px) * 1.22); gap: 9%; }
body[data-orientation="landscape"] .board-btn.wide { padding: 0 calc(var(--ctl-h) * .68); font-size: calc(var(--ctl-h) * .42 + 1px); }
/* Имя — ровно по центру под кружком аватара (а не под «аватар + счёт» — у «Прайм AI» уезжало вправо); длинное
   выходит за колонку поровну в обе стороны и переносится в пределах рамки. */
body[data-orientation="landscape"] .player-name { grid-column: 1; justify-self: unsafe center; width: max-content;
  max-width: calc(var(--side-w) - 18px); text-align: center; }
/* Рамка аватара и свечение хода — спокойнее (Влад, 21.10). */
body .ava-wrap::after { filter: brightness(.82) saturate(.8); }
.player.active .ava-wrap { filter: drop-shadow(0 0 5px rgba(240, 192, 74, .55)); }
/* Фоны аватарок — темнее и спокойнее: на тёмном интерфейсе яркие били по глазам (везде, где показаны). */
.ava-wrap::before { filter: brightness(.68) saturate(.78); }
.avatar-grid.colors .avatar-pick img { filter: brightness(.68) saturate(.78); }
/* Горизонталь: разбор хода в тренировке — шрифт на пункт крупнее (место есть). */
/* Порог шрифта тоже умножается на --tr-scale (23.10): с порогом 12.5 px уменьшение на SE / Redmi не работало — включалась
   прокрутка. Теперь подгонка (main.js placeReview) уменьшает весь текст, пока не влезет. */
body[data-orientation="landscape"] .train-review { --tr-font: calc(max(12.5px, calc(var(--bw) * .036 + 1.5px)) * var(--tr-scale, 1)); }
body[data-orientation="landscape"] .tr-moves small { font-size: calc(var(--tr-font) * .85); }
/* Поворот экрана: на время перестройки раскладки (main.js hideWhileTurning) — без «кривых» кадров. */
#app, .overlay { transition: opacity .2s ease-out; }
body.reorienting :is(#app, .overlay) { opacity: 0; transition: none; }
/* Горизонталь: таблички справа — непрозрачные (сквозь них просвечивали «ДЛИННЫЕ» и кнопки рамки матча). */
body[data-orientation="landscape"] :is(.train-review, .offer-bar) { background: #0b1830; }
/* «Партия из файла» в горизонтали — строки в две колонки, без прокрутки. */
body[data-orientation="landscape"] .window--import { width: min(620px, 100%); }
body[data-orientation="landscape"] .window--import .fields { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); column-gap: 12px; row-gap: 6px; }
body[data-orientation="landscape"] .window--import .fields > .import-file { grid-column: 1 / -1; }
body[data-orientation="landscape"] .window--import .window-foot > .analyze-btn { justify-self: center; width: min(100%, 320px); }
/* «PR подробно» в горизонтали: слева средние и лучший/худший, справа график высотой окна — без прокрутки. */
/* 23.10 (Влад: «нормальная вёрстка окна»): окно того же размера, что «Статистика» под ним, — не плавает поверх его шапки. */
body[data-orientation="landscape"] .window--pr-detail { width: min(860px, 100%); height: 100%; }
body[data-orientation="landscape"] .window--pr-detail .window-body { display: grid; align-content: center; }
body[data-orientation="landscape"] .pr-detail { grid-template-columns: minmax(0, .8fr) minmax(0, 1.2fr); grid-auto-flow: dense; align-items: start; column-gap: 16px; }
body[data-orientation="landscape"] .pr-detail > .pr-graph { grid-column: 2; grid-row: 1 / span 4; }
body[data-orientation="landscape"] .pr-detail > :not(.pr-graph) { grid-column: 1; }
body[data-orientation="landscape"] .pr-graph svg { max-height: calc(100dvh - var(--banner) - var(--safe-top) - var(--safe-bottom) - 120px); }
/* Итог в горизонтали: сияние за лентой выходило за тело окна — окно «прокручивалось» на 6–27 px (и могла появиться жёлтая
   стрелка), хотя содержимое помещается. Тело не прокручивается, сияние обрезается рамкой окна. */
body[data-orientation="landscape"] .window--result .window-body { overflow: hidden; }
/* Длинное имя («Friend (same device)») — в пределах ширины колонки аватара, переносится, а не уходит за край экрана. */
body[data-orientation="landscape"] .player-name { max-width: max(calc(var(--bw) * .36), 76px); }
body[data-orientation="landscape"] .player-name { justify-self: safe center; }   /* не за левый край экрана */
body[data-orientation="landscape"] :is(.player, .player.right) .player-name { justify-self: safe center;
  max-width: min(max(calc(var(--bw) * .36), 76px), calc(var(--side-w) - 34px)); }
/* Разбор: пустые строки таблицы вариантов держат место (23.10) — точка-заглушка едва видна, расшифровка-заглушка невидима. */
.cand.empty { cursor: default; color: transparent; }
.cand-detail.stub { visibility: hidden; }
.review-cands:empty { display: none; }

/* Разбор матча в вертикали (23.10): доска широкая — во всю ширину по своей пропорции 1056×740, ниже места больше. */
body:not([data-orientation="landscape"]) .replay > .replay-board,
body:not([data-orientation="landscape"]) .replay:has(.review-side) > .replay-board { height: auto; width: 100%; aspect-ratio: 1056 / 740; }
/* Разбор матча в горизонтали (23.10, Влад): под табло PR — переключатель партий, график, стрелки, «Ход N/M», вердикт,
   таблица вариантов; ниже — куб, уровень разбора, все ходы, .mat. */
body[data-orientation="landscape"] .replay-side > .review-summary { order: 1; }
body[data-orientation="landscape"] .replay-side > .field-row { order: 2; }
body[data-orientation="landscape"] .replay-side > .review-graph { order: 3; }
body[data-orientation="landscape"] .replay-side > .replay-nav { order: 4; }
body[data-orientation="landscape"] .replay-side > .replay-caption { order: 5; }
body[data-orientation="landscape"] .replay-side > .replay-verdict { order: 6; }
body[data-orientation="landscape"] .replay-side > .review-cands { order: 7; }
body[data-orientation="landscape"] .replay-side > .review-cube { order: 8; }
body[data-orientation="landscape"] .replay-side > .review-level { order: 9; }
body[data-orientation="landscape"] .replay-side > .review-moves { order: 10; }
body[data-orientation="landscape"] .replay-side > .replay-foot { order: 11; }

/* История: карточка матча ниже — меньше промежуток между текстом и кнопками (23.10). */
.history-row { row-gap: 0; }
.history-actions { margin-top: 2px; }
body[data-orientation="landscape"] .history-row { padding-block: 6px; }

/* ── Разбор матча, 23.10 (Влад, второй заход) ──────────────────────────────────────────────────────────────────────
   Сверху: итог матча → переключатель партий → итог партии (в обеих ориентациях). «Разобрать точнее» — внизу, над .mat. */
.replay .review-match, .replay .replay-side > .field-row, .replay .review-summary { order: 1; }
body[data-orientation="landscape"] .replay-side > :is(.review-match, .field-row, .review-summary) { order: 1; }
.review-summary.review-match[hidden] { display: none; }
.replay .review-summary.review-match { min-height: 0; }
.review-match-cap { grid-column: 1 / -1; font-size: 11px; font-weight: 600; letter-spacing: .02em; color: var(--ink-dim); padding: 0 2px; }
.review-match .review-wait { grid-column: 1 / -1; padding: 2px 0 4px; }
.replay-foot > .review-level { order: 0; }
/* Плотнее (Влад): строка «Разбор» под заголовком — вплотную к шапке; пипсы, «Ход N/M» и вердикт — ближе друг к другу. */
body:not([data-orientation="landscape"]) .replay > .replay-info { margin-top: -10px; margin-bottom: -2px; }
body:not([data-orientation="landscape"]) .replay > .replay-pips { margin-top: -6px; }
body:not([data-orientation="landscape"]) .replay .replay-caption { margin-top: -5px; }
body:not([data-orientation="landscape"]) .replay .replay-verdict { margin-top: -6px; min-height: 1.3em; }
/* Таблица вариантов: строки ниже. */
.replay .cand { padding-block: 3px; }
body[data-orientation="landscape"] .replay .cand { padding-block: 1px; }
.cand-detail { padding-block: 6px; margin-bottom: 4px; }
/* «Проверить глубже / Проверено глубже» — в левой верхней клетке расшифровки: высота строки та же, что у пустой клетки. */
.cand-grid > :first-child { height: 20px; display: flex; align-items: center; }
.cand-grid > .cand-check { justify-self: start; margin: 0; padding: 0 9px; font-size: 11px; line-height: 18px; text-align: left; white-space: nowrap; }
.cand-grid > .cand-check.done { padding-left: 0; }
/* «Показать все ходы»: стрелка крупнее. */
.review-moves-toggle i.chev { width: 18px; height: 12px; flex: none; background: no-repeat center / contain url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 22 14'%3E%3Cpath d='M2 2l9 9 9-9' fill='none' stroke='%23c9d6ea' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E"); }
.review-moves-toggle i.chev.up { transform: rotate(180deg); }
/* Горизонталь: с итогом матча колонка выросла — стрелки уходили под край на 915×412. Блоки сверху компактнее, график ниже. */
body[data-orientation="landscape"] .replay-side > .field-row { min-height: 0; padding-block: 3px; }
body[data-orientation="landscape"] .replay-side > .field-row .selector-arrow, body[data-orientation="landscape"] .replay-side > .field-row button { height: 26px; }
body[data-orientation="landscape"] .replay .review-summary .review-side { padding: 2px 8px; }
body[data-orientation="landscape"] .replay .review-pr b { font-size: 16px; }
body[data-orientation="landscape"] .replay .review-graph svg { height: 50px; }
body[data-orientation="landscape"] .review-match-cap { font-size: 10px; margin-bottom: -2px; }
/* SE / Redmi в горизонтали (23.10): счётчики ошибок переносились — плашки вырастали вдвое. В итоге матча (горизонталь) —
   PR и удача (так и просили: «итоги матча с PR и удачей»), счётчики — в итоге партии, одной строкой. */
/* 24.10 (Влад): счётчики в итоге матча в горизонтали вернули — были спрятаны 23.10 из-за переноса; теперь одной строкой. */
body[data-orientation="landscape"] .replay .review-counts { flex-wrap: nowrap; gap: 5px; font-size: 11px; }
body[data-orientation="landscape"] .replay .review-name { font-size: 13px; }
/* Низкие экраны в горизонтали (SE, Redmi: 360–375 px): стрелки — в первом экране. График ниже, легенда — в строку с подписью. */
@media (max-height: 400px) {
  body[data-orientation="landscape"] .replay .review-graph svg { height: 36px; }
  body[data-orientation="landscape"] .replay .review-graph-label { flex-wrap: nowrap; margin-bottom: 1px; }
  body[data-orientation="landscape"] .replay .review-graph-label > span:first-child { font-size: 11px; white-space: nowrap; }
  body[data-orientation="landscape"] .replay .review-legend { gap: 5px; font-size: 9px; white-space: nowrap; }
  body[data-orientation="landscape"] .replay-side { gap: 4px; }
  body[data-orientation="landscape"] .replay > .replay-info { font-size: 12px; }
}
/* Значок ролика на кнопках плашек — в долях шрифта кнопки (23.10, Влад: на планшете значок крохотный, на SE — крупный
   рядом с текстом): был 22 px на всех экранах при шрифте от 12 до 24 px. */
:is(.train-explain-btns, .ob-btns) .big-btn .ad-ico { width: 1.4em; height: 1em; border-radius: .26em; background-size: .56em; }
/* Выбранная шашка — чуть спокойнее (23.10, Влад: «слишком яркая, чуточку приглушить»): контур и ореол тише на ~20%. */
.point.selected .stack .chip:last-of-type,
.bar-slot.selected .chip:last-of-type {
  outline-color: color-mix(in srgb, var(--accent) 82%, transparent);
  filter: drop-shadow(0 0 8px color-mix(in srgb, var(--accent) 65%, transparent));
}
.cand-grid { grid-template-rows: 20px; }
.cand-grid > :first-child { box-sizing: border-box; margin: 0; align-self: center; }
/* «Итог матча» — по центру, светлым и жирным, как заголовок окна (23.10, Влад). Переключатель партий — ближе к табло партии. */
.review-match-cap { text-align: center; color: var(--ink); font-size: 13px; font-weight: 700; letter-spacing: 0; }
.replay .replay-side > .field-row { margin-top: 6px; margin-bottom: -4px; }
body[data-orientation="landscape"] .review-match-cap { font-size: 12px; margin-bottom: 0; }
body[data-orientation="landscape"] .replay .replay-side > .field-row { margin-top: 4px; margin-bottom: -3px; }
/* Строки «История матчей / Разобрать матч (.mat, .xg)» — в одну строку всегда (23.10, Влад: на SE переносилась, плашка росла):
   шрифт меньше, а не перенос. */
.history-link span:not(.pr-go) { white-space: nowrap; }
.history-link { font-size: clamp(12px, 3.9vw, 16px); gap: 10px; }
body[data-orientation="landscape"] .history-link { font-size: clamp(12px, 2.1vw, 16px); }
@media (max-width: 340px) { body:not([data-orientation="landscape"]) .history-link { font-size: 11.2px; gap: 8px; padding-inline: 12px; } }
/* Низкая горизонталь с баннером (SE 667×375, Redmi 640×360: под игру остаётся 310–325 px) — стрелки должны быть в первом экране:
   табло PR, переключатель и график ещё ниже. */
@media (max-height: 400px) {
  body[data-orientation="landscape"] .replay .review-pr { min-width: 40px; padding: 2px 5px; }
  body[data-orientation="landscape"] .replay .review-pr b { font-size: 14px; }
  body[data-orientation="landscape"] .replay .review-pr small { font-size: 8px; }
  body[data-orientation="landscape"] .replay .review-summary .review-side { padding: 1px 6px; gap: 6px; }
  body[data-orientation="landscape"] .replay .review-name { font-size: 12px; }
  body[data-orientation="landscape"] .replay .review-luck { font-size: 10px; }
  body[data-orientation="landscape"] .replay .replay-side > .field-row { padding-block: 0; min-height: 28px; font-size: 12px; }
  body[data-orientation="landscape"] .replay .replay-side > .field-row .selector-value { font-size: 12px; }
  body[data-orientation="landscape"] .replay .review-graph svg { height: 28px; }
  body[data-orientation="landscape"] .replay .replay-btn { height: 28px; }
  body[data-orientation="landscape"] .replay > .replay-info { padding-top: 34px; }
  body[data-orientation="landscape"] .window:has(.replay) .window-back { width: 32px; height: 32px; }
  body[data-orientation="landscape"] .review-match-cap { font-size: 11px; }
}
/* Подпись графика с легендой не шире колонки: раньше nowrap выталкивал «Грубая ошибка» за край и давал прокрутку вбок. */
body[data-orientation="landscape"] .replay .review-graph-label { flex-wrap: wrap; row-gap: 0; }
body[data-orientation="landscape"] .replay-side { overflow-x: hidden; }
/* Полоса прокрутки колонки разбора — тонкая (в preview.html на компьютере она была толстой и съедала ширину; на телефоне — поверх). */
body[data-orientation="landscape"] .replay-side, .overlay.full .window-body { scrollbar-width: thin; scrollbar-color: rgba(160, 196, 245, .35) transparent; }
/* Удвоение (23.10, Влад): ответ проявляется сам (main.js, double-decided) — своя анимация класса не нужна. */
.ob-text.ob-answer { animation: none; }
/* Горизонталь: куб, текст и счёт — по центру во всех трёх окнах («решает», ответ, предложение бота), и куб — на одном месте:
   у текста постоянная высота, а место кнопок «Принять / Отказаться» держится и там, где их нет. */
body[data-orientation="landscape"] .offer-bar { grid-template-columns: minmax(0, 1fr); justify-items: center; text-align: center; }
body[data-orientation="landscape"] .offer-bar .ob-mid { min-height: 7.4em; display: grid; align-content: center; }
body[data-orientation="landscape"] .offer-bar:not(.ask)::after { content: ''; display: block; height: calc(var(--bw) * .26 + 18px); }
/* «Новая игра» на низких телефонах с баннером (360×640, 320×568): окно прокручивалось на 8–12 px — строки чуть ниже. */
@media (max-height: 700px) and (orientation: portrait) {
  .window--newgame .fields .field-row { min-height: 40px; padding-block: 4px; }
  .window--newgame .fields { gap: 6px; }
}
@media (max-height: 600px) and (orientation: portrait) {
  .window--newgame .fields .field-row { min-height: 36px; padding-block: 2px; }
  .window--newgame .fields { gap: 5px; }
  .window--newgame .training-hint, .window--newgame .fields > p { font-size: 11px; }
}
@media (max-height: 600px) and (orientation: portrait) {
  .window--newgame .fields .field-row { min-height: 34px; }
  .window--newgame .fields { gap: 4px; }
}
/* «Уважаемый игрок» в горизонтали — без прокрутки на любом телефоне (23.10, Влад): окно шире, шрифт — от высоты экрана. */
body[data-orientation="landscape"] .window--adnotice { width: min(640px, 100%); }
body[data-orientation="landscape"] .window--adnotice .adn { font-size: clamp(12px, 4.4dvh, 15px); line-height: 1.3; gap: clamp(3px, 1.2dvh, 6px); }
body[data-orientation="landscape"] .window--adnotice .adn-who { font-size: .86em; }
body[data-orientation="landscape"] .window--adnotice .window-foot { padding-block: clamp(4px, 1.6dvh, 8px) clamp(6px, 2dvh, 10px); }
body[data-orientation="landscape"] .window--adnotice .window-foot > .big-btn { min-height: 0; height: clamp(38px, 12dvh, 52px); }
/* Совсем низкая горизонталь (SE 1: 568×320, с баннером под игру 270 px). */
@media (max-height: 340px) {
  body[data-orientation="landscape"] .train-explain { width: min(calc(var(--land-bw) * .8), 440px); }
  body[data-orientation="landscape"] :is(.train-explain-btns, .ob-btns) .big-btn { font-size: 12px; padding: 0 6px; }
  body[data-orientation="landscape"] .window--newgame .fields { row-gap: 4px; }
  body[data-orientation="landscape"] .window--newgame .fields .field-row { min-height: 32px; padding-block: 1px; }
  body[data-orientation="landscape"] .window--newgame .window-head { padding-block: 2px 0; }
  body[data-orientation="landscape"] .pr-cells .pr-cell { padding-block: 3px; }
  body[data-orientation="landscape"] .pr-detail { gap: 6px; }
  body[data-orientation="landscape"] .pr-detail .stat-row { padding-block: 4px; }
}
@media (max-height: 340px) {
  body[data-orientation="landscape"] .window--newgame .window-foot { padding-block: 4px 6px; }
  body[data-orientation="landscape"] .window--newgame .window-foot .big-btn { min-height: 0; height: 36px; font-size: 16px; }
  body[data-orientation="landscape"] .window--newgame .window-back { width: 32px; height: 32px; }
  body[data-orientation="landscape"] .window--newgame .window-title { font-size: 16px; }
  body[data-orientation="landscape"] .window--newgame .field-label { font-size: 12px; line-height: 1.1; }
}
@media (max-height: 340px) {
  body[data-orientation="landscape"] .window--newgame .fields .field-row { min-height: 30px; padding-block: 0; }
  body[data-orientation="landscape"] .window--newgame .training-block { padding-block: 2px; }
}
/* Кнопки плашки — строго равной ширины (23.10, Влад: «Назад» скукоживалась — колонки 1fr уступали месту под «Добавить +5 ▶»). */
.train-explain-btns { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
/* Низкая горизонталь (Redmi, SE): плашка на доске шире — кнопкам хватает места. */
@media (max-height: 400px) {
  body[data-orientation="landscape"] .train-explain { width: min(calc(var(--land-bw) * .8), 440px); }
}
/* Номера пунктов — бледнее (23.10, Влад: «не ярко-белые»). */
.pt-num { color: rgba(255, 244, 222, .68); }
/* ── Разбор матча, 23.10 (третий заход) ── */
/* Строка матча — в две строки: «Режим · игрок — игрок» и «дата · счёт». */
.replay-info .ri-who { color: var(--ink); font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.replay-info .ri-when { font-size: .92em; }
/* Табло-заготовка (пока считается): та же высота, без чисел. */
.review-side.pending .review-pr { opacity: .55; }
/* «Выбор партии» — одной строкой (в горизонтали переносилось по слогам). */
.replay .replay-side > .field-row .field-label { white-space: nowrap; }
body[data-orientation="landscape"] .replay .replay-side > .field-row { grid-template-columns: auto minmax(0, 1fr); }
/* Горизонталь: пипсы и имена под доской — не шире доски (23.10, Redmi: длинные имена раздвигали колонку доски и толкали
   разбор вправо — MWC упирался в край экрана). */
body[data-orientation="landscape"] .replay > .replay-pips { width: var(--rp-bw); min-width: 0; max-width: var(--rp-bw); }
body[data-orientation="landscape"] .replay { grid-template-columns: var(--rp-bw) minmax(0, 1fr); }
body[data-orientation="landscape"] .replay-pip { min-width: 0; flex: 1 1 0; }
/* История (23.10, Влад): PR двух сторон не помещался на Redmi, на Pixel упирался в края — блок PR занимает всё свободное
   место ряда, «Посмотреть / Разобрать» уже. */
body:not([data-orientation="landscape"]) .history-row .hist-pr { flex: 1 1 auto; min-width: 0; margin-right: 0; justify-content: center; padding: 0 10px; white-space: nowrap; }
body:not([data-orientation="landscape"]) :is(.history-row .view-btn, .history-row .analyze-btn:not(.big)) { min-width: 0; width: 118px; padding: 0 10px; flex: none; }
@media (max-width: 340px) { body:not([data-orientation="landscape"]) :is(.history-row .view-btn, .history-row .analyze-btn:not(.big)) { width: 100px; font-size: 14px; } }
@media (max-width: 400px) {
  body:not([data-orientation="landscape"]) .history-row .hist-pr { padding: 0 6px; gap: 3px; font-size: 14px; }
  body:not([data-orientation="landscape"]) :is(.history-row .view-btn, .history-row .analyze-btn:not(.big)) { width: 108px; font-size: 14px; }
  body:not([data-orientation="landscape"]) .history-actions { gap: 6px; }
}
@media (max-width: 340px) {
  body:not([data-orientation="landscape"]) .history-row .hist-pr { font-size: 13px; padding: 0 4px; }
  body:not([data-orientation="landscape"]) :is(.history-row .view-btn, .history-row .analyze-btn:not(.big)) { width: 92px; font-size: 13px; padding: 0 6px; }
}
/* Подсветка ходящего — чуть больше (23.10, Влад): ореол шире и заметнее, но тише исходного 8 px на акценте. */
.player.active .ava-wrap { filter: drop-shadow(0 0 7px rgba(240, 192, 74, .7)); }
/* Кубик у пипов — по видимому квадрату (23.10, Влад: «неровно стоит у 133»): в картинке cube_*.png снизу прозрачная полоса
   тени (видимое — верхние 114 из 138 px), по рамке кубик казался на 9% выше числа. */
.rail .pips .cube-tile { transform: translateY(9%); }
/* «Поделиться игрой» в окне (i) — под почтой, по центру, шириной как в настройках. */
.about-share { display: flex; justify-content: center; margin-top: 10px; }
.about-share .big-btn { width: min(100%, 300px); }
@media (max-height: 340px) {
  body[data-orientation="landscape"] .replay > .replay-pips { font-size: 11px; gap: 4px; }
  body[data-orientation="landscape"] .replay-pip { gap: 4px; }
  body[data-orientation="landscape"] .replay-pip b { font-size: 12px; }
  body[data-orientation="landscape"] .replay-pip .chip-icon { width: 12px; height: 12px; }
}
/* Дата, время и счёт матча в разборе — белым, как строка игроков (23.10, Влад). */
.replay-info .ri-when { color: var(--ink); }
/* Горизонталь: подсказка с выводом шашки — табличка слева, зеркально правой (23.10): лотки справа открыты. Закрывает
   рамку игроков и кнопки «Назад» / настройки — на время подсказки. */
body[data-orientation="landscape"] .train-review.tr-left { grid-column: 1 / 3; grid-row: 2 / 8; margin: 0 6px 0 0; z-index: 6; }
/* «за 10 матчей» — подпись ячейки PR одной строкой; не влезает (5 ячеек на узком экране) — переносится по словам, по центру. */
.pr-cell small { text-align: center; line-height: 1.15; white-space: normal; overflow-wrap: normal; }
.pr-cell { min-width: 0; padding-inline: 2px; }
.pr-cell small { font-size: 10.5px; }
/* Горизонталь: четыре ячейки PR — по две в ряд (23.10, Влад: не «3 + 1»); в вертикали — все в одну линию. */
body[data-orientation="landscape"] .pr-cells[data-n="4"] { grid-template-columns: repeat(2, minmax(0, 1fr)) !important; }
@media (max-width: 340px) { .pr-cell small { font-size: 9.5px; letter-spacing: -.01em; } }
/* 24.10 (Влад): разбор — кнопки не прыгают при листании ходов (screens.js holdTail): распорка — в самом конце колонки. */
.replay .replay-tail { order: 99; flex: none; pointer-events: none; }
/* Строка с именем: при нехватке места сжимается имя (с многоточием), значения — только после него. */
.named { display: inline-flex; max-width: 100%; min-width: 0; white-space: nowrap; vertical-align: bottom; }
.named > span { overflow: hidden; text-overflow: ellipsis; min-width: 0; flex: 0 1 auto; }
.named > .np { flex-shrink: 0; }
.named > .nm { flex-shrink: 1000; min-width: 2.6em; }
.replay .replay-verdict:has(> .named) { display: flex; justify-content: center; }
body[data-orientation="landscape"] .replay .replay-verdict:has(> .named) { justify-content: flex-start; }
.cube-line:has(> .named) { display: flex; }
.review-move .mv:has(> .named) { display: flex; }
/* Табло PR одной ширины при любом числе (24.10: «8.1» и «11.5» давали разную ширину окошка). */
.review-pr b { min-width: 3.6ch; text-align: center; }
/* Точки ошибок на графике — круглые при любой ширине (были кругами растянутого svg). */
.review-plot { position: relative; }
.review-dots { position: absolute; inset: 0; pointer-events: none; }
.review-dots .dot { position: absolute; width: 7px; height: 7px; margin: -3.5px 0 0 -3.5px; border-radius: 50%; box-shadow: 0 0 0 .8px rgba(0, 0, 0, .5); }
.review-dots .dot.blunder { width: 9px; height: 9px; margin: -4.5px 0 0 -4.5px; }
.review-dots .dot.inaccuracy { background: #f2d15c; }
.review-dots .dot.mistake { background: #f59a3c; }
.review-dots .dot.blunder { background: #ff6b5e; }
.review-dots .dot.theirs { opacity: .35; }
/* Строка хода: не хватает места — сжимается имя (с многоточием), запись хода — целиком (24.10, Влад: на Redmi
   «Андрей Полочкин» оставался целым, а ход обрезался). Имя — от 2.6em до полной ширины, ход — не уже своей записи. */
.replay .replay-caption { grid-template-columns: max-content minmax(2.6em, max-content) max-content minmax(max-content, 1fr); }
.replay .replay-caption .mv { overflow: visible; }
/* Колонка разбора — не шире экрана (24.10): длинная строка без переноса (куб, вердикт) расширяла её, вместе с ней росла
   доска (её высота — от ширины) и всё ниже прыгало на 10–30 px. */
.replay { grid-template-columns: minmax(0, 1fr); }
.review-cube { grid-template-columns: minmax(0, 1fr); }
.review-cube > *, .replay .replay-verdict > .named { min-width: 0; }
.named > .nk { flex-shrink: 0; white-space: pre; }
/* Стрелка «дальше есть ещё» в колонке разбора — в конце колонки (у всех блоков свой order, стрелка без него вставала первой
   и висела вверху справа). */
body[data-orientation="landscape"] .replay-side.more::after { order: 100; }
/* «Все ходы»: колонки в px, а не в em (24.10, Влад: «Кости», «Ход» стояли не над своими колонками — у шапки шрифт мельче,
   и её колонки в em выходили уже). */
.review-move { grid-template-columns: 30px 14px 36px minmax(0, 1fr) 48px; }
/* Призрак поверх полной стопки — тем же белым контуром, что и обычный (24.10, Влад: в горизонтали ряды короче, призрак чаще
   ложится на стопку и вспыхивал жёлтым). Остаётся заметнее за счёт непрозрачнее картинки. */
.chip.ghost.over:not(.blot) { outline: none; filter: none; }
/* Строка куба: слева «кто: что» (переносится, не режется), справа оценка — целиком и всегда в строку (24.10). */
.cube-line { display: grid; grid-template-columns: minmax(0, 1fr) auto; column-gap: 10px; align-items: baseline; }
.cube-line .cl-what { overflow-wrap: anywhere; text-wrap: balance; }
.cube-line .cl-verdict { white-space: nowrap; text-align: right; font-weight: 700; }
/* Ячейки куба: число — на одной линии во всех трёх, даже если подпись в одной ячейке перенеслась на две строки (24.10). */
.cube-cell { grid-template-rows: 1fr auto; align-items: center; text-align: center; }
/* Куб в разборе (24.10, Влад: «удвоение — отдельным шагом, куб на доске»): в строке пипсов — у владельца гранью ставки или
   в центре гранью 64, как в игре; на шаге «Куб» — предложенный куб крупно посреди доски. */
.replay-cube { display: inline-block; flex: none; width: 22px; height: 22px; background: center / contain no-repeat; }
.replay-cube[hidden] { display: none; }
.replay-pips > .replay-cube.mid { align-self: center; width: 24px; height: 24px; }
.replay .board-wrap { position: relative; }
.replay-cube-offer { position: absolute; left: 50%; top: 50%; width: 15%; aspect-ratio: 1; transform: translate(-50%, -50%);
  background: center / contain no-repeat; filter: drop-shadow(0 0 14px rgba(255, 214, 90, .75)) drop-shadow(0 4px 8px rgba(0, 0, 0, .6));
  pointer-events: none; z-index: 5; animation: cube-offer-in .35s ease-out; }
.replay-cube-offer[hidden] { display: none; }
@keyframes cube-offer-in { from { opacity: 0; transform: translate(-50%, -50%) scale(.6); } }
.replay .replay-caption > .replay-cube.d { width: 22px; height: 22px; align-self: center; }
.review-move .replay-cube.d { width: 20px; height: 20px; }
.review-move.cube { background: rgba(240, 192, 74, .08); }
.review-move.cube.on { background: rgba(94, 170, 255, .22); }
/* Таблица вариантов держит высоту (holdHeight) — содержимое прижато кверху, а не растянуто по ней (шаг «Куб»). */
.review-cands { align-content: start; }
/* Строка куба в «Все ходы» — переносится, а не режется (узкая колонка в горизонтали SE / Redmi). */
.review-move.cube .mv { white-space: normal; line-height: 1.2; text-overflow: clip; }
/* 24.10 (Влад): шаг «Куб» ↔ шаг хода — строка пипсов и строка хода одной высоты (куб «64» в центре был выше куба у
   владельца, значок в строке хода — выше текста: кнопки сдвигались на 2–3 px). */
.replay > .replay-pips { height: 24px; align-items: center; }
.replay-pips .replay-cube, .replay-pips > .replay-cube.mid { width: 20px; height: 20px; }
.replay .replay-caption > .replay-cube.d { width: 18px; height: 18px; }
/* Вердикт: имя ужимается сильнее, чем значения. */
.named > .nm { min-width: 1.6em; }
/* Горизонталь: без жёлтой стрелки «дальше» в колонке разбора — поля шире (Влад, 24.10). */
body[data-orientation="landscape"] .replay-side { padding-right: 6px; }
body[data-orientation="landscape"] .replay-side.more::after { content: none; }
/* Выбранная шашка — подсветка ВНУТРИ (24.10, Влад: «не по внешнему радиусу, а по внутреннему»): без контура и ореола
   снаружи; кольцо по внутреннему краю шашки и мягкое свечение внутрь. */
.point.selected .stack .chip:last-of-type,
.bar-slot.selected .chip:last-of-type { outline: none; filter: drop-shadow(0 1px 2px rgba(0, 0, 0, .5)); }
.point.selected .stack .chip:last-of-type::after,
.bar-slot.selected .chip:last-of-type::after {
  content: ''; position: absolute; inset: 6%; border-radius: 50%; pointer-events: none;
  box-shadow: inset 0 0 0 3px color-mix(in srgb, var(--accent) 88%, transparent),
    inset 0 0 10px 3px color-mix(in srgb, var(--accent) 55%, transparent);
}
/* Партии матча (25.10): «◀ Партия 1 · счёт 1:0 · до 5 ▶» во всю ширину, без подписи. */
.replay .round-row { grid-template-columns: minmax(0, 1fr); }
.replay .round-row > .selector { grid-column: 1; grid-template-columns: 32px minmax(0, 1fr) 32px; }
.replay .round-row .selector-value { white-space: nowrap; font-weight: 600; }
/* Шапка разбора: дата, время и счёт — ярко, как строка с режимом и именами. */
.replay-info .ri-when { color: var(--ink); font-weight: 600; }
/* Счёт партии в блоке «Куб». */
.cube-state { margin-top: 2px; padding: 4px 4px 0; border-top: 1px solid rgba(160, 196, 245, .12); font-size: 12px; color: var(--ink-dim); text-align: center; }
/* «Разобрать точнее ≈ N с» и пояснение ниже. */
.replay .review-level { flex-direction: column; gap: 0; }
.review-level-hint { font-size: 11px; color: var(--ink-dim); }
/* «Проверить глубже» куба (короткие, 25.10) — кнопка-ссылка в блоке «Куб». */
.cube-deeper { appearance: none; display: block; margin: 4px auto 0; border: none; background: none; padding: 4px 6px;
  color: #8cc8f5; font: inherit; font-size: 13px; font-weight: 700; text-decoration: underline; text-underline-offset: 3px; cursor: pointer; }
.cube-deeper:disabled { color: var(--ink-dim); text-decoration: none; cursor: default; }
.cube-deeper.done { color: #8fdca0; text-decoration: none; text-align: center; }
/* SE 1 / 320 px: «Партия 4 · счёт 2:4 · до 5 · Кроуфорд» не влезала на 14 px — шрифт строки партий чуть мельче. */
/* Строка партий — во всю ширину в обеих ориентациях; шрифт крупнее (25.10, Влад), на узких — мельче, чтобы «Кроуфорд» не резался. */
.replay .replay-side > .field-row.round-row { grid-template-columns: minmax(0, 1fr); }
.replay .field-row.round-row > .selector { width: 100%; justify-self: stretch; grid-template-columns: 32px minmax(0, 1fr) 32px; }
.replay .field-row.round-row .selector-cell { justify-items: center; }
body:not([data-orientation="landscape"]) .replay .field-row.round-row .selector-value { font-size: 14px; letter-spacing: 0; }
@media (max-width: 390px) { body:not([data-orientation="landscape"]) .replay .field-row.round-row .selector-value { font-size: 13px; } }
@media (max-width: 340px) { body:not([data-orientation="landscape"]) .replay .field-row.round-row .selector-value { font-size: 11px; letter-spacing: -.01em; } }
body[data-orientation="landscape"] .replay .replay-side > .field-row.round-row .selector-value { font-size: 13px; }
@media (max-height: 400px) { body[data-orientation="landscape"] .replay .replay-side > .field-row.round-row .selector-value { font-size: 12px; } }
body[data-orientation="landscape"] .replay .replay-side > .field-row.round-row { grid-template-columns: minmax(0, 1fr); }
body[data-orientation="landscape"] .replay .replay-side > .field-row.round-row > .selector { grid-column: 1 / -1; width: 100%; grid-template-columns: 30px minmax(0, 1fr) 30px; }
/* Счёт партии в блоке «Куб» — белым и размером как строки решений («взятие»), 25.10 Влад. */
.cube-state { color: var(--ink); font-size: 13px; }
/* Стрелки строки партий — ровно у краёв на низкой горизонтали (SE / Redmi): стрелка там 24 px, а колонка была 30 px —
   справа оставалось лишних 6 px (25.10, Влад). Колонки — по стрелке. */
body[data-orientation="landscape"] .replay .replay-side > .field-row.round-row > .selector { grid-template-columns: auto minmax(0, 1fr) auto; }
.replay .field-row.round-row > .selector { grid-template-columns: auto minmax(0, 1fr) auto; }


/* ───────────── Онлайн (01.11, online/client.js) */
.oclock { display: inline-flex; align-items: center; gap: 6px; padding: 3px 10px; border-radius: 999px; background: rgba(3, 9, 20, .55);
  border: 1px solid var(--panel-line); font-variant-numeric: tabular-nums; color: rgba(255, 255, 255, .6); font-weight: 700; }
.oclock .oc-main { display: inline-block; width: 5ch; text-align: right; font-size: 17px; }
.oclock .oc-delay { display: inline-block; width: 3ch; text-align: right; font-size: 13px; color: #8fd18f; }
.oclock.on { color: #fff; border-color: rgba(143, 209, 143, .7); }
.oclock.low.on .oc-main { color: #ff7a6b; }
body.online #autoplay { display: none; }
#rail-opponent .rail-mid { display: flex; justify-content: center; align-items: center; }
.online-lobby { display: flex; flex-direction: column; gap: 8px; }
.online-label { font-size: 13px; opacity: .7; margin-top: 6px; }
.online-name { font: inherit; font-size: 17px; padding: 10px 12px; border-radius: 12px; border: 1px solid var(--panel-line); background: rgba(3, 9, 20, .5); color: #fff; }
.online-name.bad { border-color: #ff7a6b; }
.online-msg { color: #ffcf6b; margin: 0; }
.online-seat { display: flex; justify-content: space-between; gap: 8px; padding: 10px 12px; border-radius: 12px; background: rgba(3, 9, 20, .4); }
.online-seat .os-name { font-weight: 700; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.online-seat .os-state { flex: none; width: 16ch; text-align: right; opacity: .75; }
.online-seat.away .os-state { color: #ff7a6b; }
.online-link { margin: 0; font-size: 13px; word-break: break-all; opacity: .8; user-select: all; }
.online-rules { display: flex; flex-direction: column; gap: 6px; }
.online-rule { display: flex; justify-content: space-between; gap: 8px; padding: 8px 12px; border-radius: 12px; background: rgba(3, 9, 20, .4); }
.online-rule b { text-align: right; }
.online-seed { word-break: break-all; }
.online-between { position: fixed; left: 50%; top: 12px; transform: translateX(-50%); z-index: 50; padding: 8px 14px; border-radius: 999px;
  background: rgba(3, 9, 20, .85); border: 1px solid var(--panel-line); color: #fff; font-size: 14px; }
body.online-offline::after { content: 'Нет связи — переподключаемся…'; position: fixed; left: 50%; bottom: 12px; transform: translateX(-50%); z-index: 60;
  padding: 8px 14px; border-radius: 999px; background: #8a2a20; color: #fff; font-size: 14px; }
body.online #btn-back { visibility: hidden; }   /* онлайн: меню с ботами нет — выход из матча только сдачей */
