/* ==========================================================================
   Poker Shop — реализация knowledge/14-design-system.md
   Все токены и правила взяты оттуда дословно. Своих решений здесь нет.
   ========================================================================== */

:root {
  /* 2.1 Красный как материал — сукно */
  --cloth-800: #421318;
  --cloth-700: #571C24;
  --cloth-600: #6E2730;
  --spot-500:  #8E2B2F;

  /* 2.1 Чёрный с примесью красного */
  --ink-950: #160F11;
  --ink-900: #241619;
  --ink-700: #3D2A2E;

  /* 2.1 Керамика — поверхность чтения */
  --clay-050: #F1EDEA;
  --clay-100: #E9E4E1;
  --clay-200: #D8D2CE;

  /* 2.1 Металл */
  --metal-900: #4A423B;
  --metal-700: #6A6058;
  --metal-500: #8C8279;
  --metal-300: #B6ADA3;
  --metal-100: #D5CEC6;
  --brass-500: #8A7140;

  /* 2.2 Белый — только числа */
  --inlay: #FFFFFF;

  /* 2.3 Радиусы кодируют предмет */
  --r-edge: 0;
  --r-plaque: 2px;
  --r-case: 6px;
  --r-chip: 50%;

  /* 2.4 Одна тень на систему */
  --lift: 0 18px 40px -12px rgba(36, 22, 25, .45);

  /* 2.5 Ритм — Фибоначчи ×4 */
  --s-1: 4px;  --s-2: 8px;  --s-3: 12px; --s-4: 20px;
  --s-5: 32px; --s-6: 52px; --s-7: 84px; --s-8: 136px;

  /* 2.6 Линии */
  --hair: 1px solid var(--metal-300);
  --hair-dark: 1px solid var(--ink-700);
  --rule: 2px solid var(--ink-900);

  /* 3.2 Гарнитуры: граница по кеглю, а не по месту в разметке */
  --f-text: 'Golos Text', 'PT Root UI', 'Helvetica Neue', Arial, sans-serif;
  --f-display: 'Cuprum', 'PT Sans Narrow', 'Golos Text', sans-serif;

  /* 4. Сетка — база 10 колонок по числу мест за турнирным столом */
  --gutter: 40px;
  --gap: 24px;
  --cols: 10;
  --tile-span: 2;
  --maxw: 1360px;
}

* { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--clay-100);
  color: var(--ink-900);
  font-family: var(--f-text);
  font-size: 17px;
  line-height: 1.6;
  font-variant-numeric: tabular-nums;
  text-align: left;
}

img { max-width: 100%; display: block; }

a { color: inherit; }

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

/* --------------------------------------------------------------------------
   3.3 Шкала кеглей
   -------------------------------------------------------------------------- */
.t-caption { font-size: 13px; line-height: 1.45; }
.t-small   { font-size: 15px; line-height: 1.5; }
.t-body    { font-size: 17px; line-height: 1.6; }
.t-lead    { font-size: 21px; line-height: 1.45; }
.t-h3      { font-family: var(--f-display); font-size: 27px; line-height: 1.25; font-weight: 700; }
.t-h2      { font-family: var(--f-display); font-size: 34px; line-height: 1.15; font-weight: 700; }
.t-h1      { font-family: var(--f-display); font-size: 54px; line-height: 1.05; font-weight: 700; }
.t-num     { font-family: var(--f-display); font-size: 88px; line-height: 1;    font-weight: 700; }
.t-hero    { font-family: var(--f-display); font-size: 140px; line-height: .95; font-weight: 700; }

h1, h2, h3, h4 { margin: 0; font-weight: 700; }
p { margin: 0 0 var(--s-3); }
p:last-child { margin-bottom: 0; }

/* Длина строки — не больше 68 знаков */
.prose { max-width: 660px; }

/* --------------------------------------------------------------------------
   Сетка и плоскости
   -------------------------------------------------------------------------- */
.wrap {
  max-width: var(--maxw);
  margin: 0 auto;
  padding: 0 var(--gutter);
}

.grid {
  display: grid;
  grid-template-columns: repeat(var(--cols), minmax(0, 1fr));
  gap: var(--gap);
}

/* Плоскости выходят за поле сетки и режутся краем экрана */
.plane { width: 100%; }
.plane-cloth { background: var(--cloth-700); color: var(--clay-100); }
.plane-ink   { background: var(--ink-950);   color: var(--clay-100); }
.plane-metal { background: var(--metal-100); color: var(--ink-900); }
.plane-clay  { background: var(--clay-100);  color: var(--ink-900); }

.sec { padding: var(--s-7) 0; }
.sec-tight { padding: var(--s-6) 0; }

.stack-2 > * + * { margin-top: var(--s-2); }
.stack-3 > * + * { margin-top: var(--s-3); }
.stack-4 > * + * { margin-top: var(--s-4); }
.stack-5 > * + * { margin-top: var(--s-5); }
.stack-6 > * + * { margin-top: var(--s-6); }

.muted { color: var(--metal-700); }
.muted-dark { color: var(--metal-300); }

/* Отбивка раздела — только под заголовком уровня 2 */
.rule { border: 0; border-top: var(--rule); margin: var(--s-3) 0 var(--s-5); }
.hair { border: 0; border-top: var(--hair); margin: var(--s-4) 0; }
.hair-dark { border: 0; border-top: var(--hair-dark); margin: var(--s-4) 0; }

/* --------------------------------------------------------------------------
   6. Фокус — двойное кольцо
   -------------------------------------------------------------------------- */
a:focus-visible, button:focus-visible, input:focus-visible,
select:focus-visible, [tabindex]:focus-visible {
  outline: 2px solid var(--ink-900);
  outline-offset: 2px;
  box-shadow: 0 0 0 4px var(--clay-050);
  border-radius: var(--r-plaque);
}

/* --------------------------------------------------------------------------
   5.5 Кнопка. Первичная — чёрная, не красная.
   -------------------------------------------------------------------------- */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 44px;
  padding: 0 var(--s-4);
  border: 1px solid transparent;
  border-radius: var(--r-plaque);
  background: var(--ink-900);
  color: var(--clay-050);
  font-size: 15px;
  font-weight: 600;
  text-decoration: none;
  cursor: pointer;
  transition: background 120ms linear;
}
.btn:hover { background: var(--ink-950); }
.btn:active { background: var(--ink-950); }

.btn-inv { background: var(--clay-100); color: var(--ink-900); }
.btn-inv:hover { background: var(--clay-050); }

.btn-sec {
  background: transparent;
  border-color: var(--metal-500);
  color: inherit;
}
.btn-sec:hover { background: var(--clay-200); }
.plane-cloth .btn-sec:hover, .plane-ink .btn-sec:hover { background: var(--cloth-600); }

.btn-txt {
  background: none;
  border: 0;
  padding: 0;
  min-height: 0;
  color: inherit;
  text-decoration: none;
  border-bottom: 1px solid var(--spot-500);
  cursor: pointer;
  font-weight: 600;
}

.btn[disabled] { background: var(--metal-500); color: var(--metal-100); cursor: default; }

.btn-row { display: flex; flex-wrap: wrap; gap: var(--s-3); }

/* --------------------------------------------------------------------------
   5.3 Инлей — главный узел системы. Белое поле, число внутри.
   -------------------------------------------------------------------------- */
.inlay {
  display: inline-flex;
  align-items: baseline;
  justify-content: center;
  gap: var(--s-2);
  background: var(--inlay);
  color: var(--ink-900);
  border-radius: var(--r-plaque);
  padding: var(--s-2) var(--s-3);
  font-family: var(--f-display);
  font-size: 27px;
  font-weight: 700;
  line-height: 1.1;
  white-space: nowrap;
}
.inlay .u { font-family: var(--f-text); font-size: 13px; font-weight: 400; color: var(--metal-700); }
.inlay-lg { font-size: 54px; padding: var(--s-3) var(--s-4); }
.inlay-sm { font-size: 21px; }

/* Цена «по запросу» — инлея нет, вместо числа кнопка */
.price-request { display: inline-flex; }

.price-note { color: var(--metal-700); font-size: 13px; line-height: 1.45; }
.plane-cloth .price-note, .plane-ink .price-note { color: var(--metal-300); }

/* --------------------------------------------------------------------------
   5.4 Компонент номинала. Знак валюты в него подставить нельзя.
   -------------------------------------------------------------------------- */
.denom {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 40px; height: 40px;
  border-radius: var(--r-chip);
  background: var(--ink-900);
  color: var(--inlay);
  font-family: var(--f-display);
  font-size: 15px;
  font-weight: 700;
  flex: 0 0 auto;
}
.denom::before {
  content: "";
  position: absolute; inset: 0;
  border-radius: var(--r-chip);
  background: repeating-conic-gradient(
    var(--spot-500) 0deg 9deg, transparent 9deg 60deg);
  -webkit-mask: radial-gradient(circle closest-side, transparent 0 68%, #000 68% 100%);
          mask: radial-gradient(circle closest-side, transparent 0 68%, #000 68% 100%);
}
.denom-lg { width: 64px; height: 64px; font-size: 21px; }
.denom-row { display: flex; flex-wrap: wrap; gap: var(--s-2); }

/* --------------------------------------------------------------------------
   5.1 Плитка товара. Не карточка: ни рамки, ни фона, ни тени.
   -------------------------------------------------------------------------- */
.tiles {
  display: grid;
  grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: var(--gap) var(--gap);
}
.tiles-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
.tiles-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.tiles-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }

.tile {
  display: block;
  text-decoration: none;
  color: inherit;
  background: none;
  border: 0;
  padding: 0;
  text-align: left;
  cursor: pointer;
}
.tile__scene { margin-bottom: 0; }
.tile__line { border-top: var(--hair); }
.tile__name {
  font-size: 21px; line-height: 1.45;
  margin-top: var(--s-3);
  /* Три строки, а не две: публичные названия из 08a §6 несут материал, вес и
     число фишек, и обрезка на второй строке съедала ровно то число, по
     которому выбирают. Название важнее ровной высоты плитки. */
  display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical;
  overflow: hidden;
}
/* Ряд коротких фактов через волосяную вертикаль. §9 запрещает мета-ряд через
   средние точки, и запрет закрывается здесь один раз на весь сайт: тот же
   класс носит строка под заголовком карточки и строка позиции в корзине.
   Две реализации одного правила разошлись бы на первой же правке. */
.tile__sub,
.meta { display: flex; gap: var(--s-3); flex-wrap: wrap; }
.tile__sub { margin-top: var(--s-2); color: var(--metal-700); font-size: 15px; }
.tile__sub span,
.meta span { position: relative; }
.tile__sub span + span::before,
.meta span + span::before {
  content: ""; position: absolute; left: calc(var(--s-3) * -1 + 5px); top: 2px; bottom: 2px;
  border-left: 1px solid var(--metal-300);
}
.tile__price { margin-top: var(--s-3); }
.tile__supply { margin-top: var(--s-2); font-size: 13px; line-height: 1.45; color: var(--metal-700); }

.tile__out { margin-top: var(--s-3); font-size: 15px; color: var(--metal-700); }

/* --------------------------------------------------------------------------
   Фотосцена. Фотографий товара нет — сцена собрана из материала средствами CSS.
   Это временная замена съёмке по knowledge/15-visual-content-plan.md.
   -------------------------------------------------------------------------- */
/* Поле сцены — не плоская заливка, а съёмочный фон: свет сверху, углы уходят в
   тень. Плоский чёрный квадрат делал из предмета наклейку, и сорок одинаковых
   чёрных квадратов подряд читались как пустые места, где не подгрузились
   фотографии. Разница в два тона стоит ничего и возвращает предмету объём. */
.scene {
  position: relative;
  aspect-ratio: 4 / 5;
  background:
    radial-gradient(ellipse 150% 105% at 50% 4%, var(--ink-900), var(--ink-950) 72%);
  border-radius: var(--r-edge);
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
}
.scene--wide { aspect-ratio: 16 / 10; }
.scene--hero { aspect-ratio: auto; height: 100%; }
/* Подпись сцены. Сцена с подписью отдаёт ей нижнюю полосу, а предмет
   центруется в оставшемся поле: раньше подпись ложилась поверх предмета
   и на широкой сцене конструктора не читалась ни на фишке, ни на фоне. */
.scene:has(.scene__note) { padding-bottom: var(--s-6); }
.scene__note {
  position: absolute; left: var(--s-3); right: var(--s-3); bottom: var(--s-4);
  font-size: 13px; line-height: 1.45; color: var(--metal-500);
}

/* фишка */
.o-chip {
  position: relative;
  width: 58%; aspect-ratio: 1;
  border-radius: var(--r-chip);
  background:
    radial-gradient(circle at 38% 32%, rgba(255,255,255,.10), transparent 58%),
    var(--chip-body, #7E2830);
  box-shadow: inset 0 0 0 2px rgba(0,0,0,.25);
  display: flex; align-items: center; justify-content: center;
}
.o-chip::before {
  content: ""; position: absolute; inset: 0; border-radius: var(--r-chip);
  background: repeating-conic-gradient(var(--chip-spot, #E9E4E1) 0deg 11deg, transparent 11deg 45deg);
  -webkit-mask: radial-gradient(circle closest-side, transparent 0 74%, #000 74% 92%, transparent 92%);
          mask: radial-gradient(circle closest-side, transparent 0 74%, #000 74% 92%, transparent 92%);
}
.o-chip::after {
  content: ""; position: absolute; inset: 22%;
  border-radius: var(--r-chip);
  background: var(--clay-050);
  box-shadow: inset 0 0 0 1px rgba(0,0,0,.18);
}
.o-chip__n {
  position: relative; z-index: 2;
  font-family: var(--f-display); font-weight: 700;
  color: var(--ink-900); font-size: 2.4vw;
}
.tiles .o-chip__n { font-size: 28px; }

/* стопка фишек.

   Раньше каждая фишка была самостоятельным эллипсом, и между эллипсами по бокам
   светился фон: восемь колец одно над другим, а не столбик. Теперь тело столбика
   одно — цилиндр, — а восемь элементов рисуют по нему только швы и вставки.
   Это и есть устройство настоящей стопки: снаружи видно не восемь предметов,
   а один столб с восемью стыками. */
.o-stack { position: relative; width: 38%; aspect-ratio: 1 / 1.5; }
/* тело: цилиндр. Свет по горизонтали — слева и справа бок уходит от зрителя. */
.o-stack::before {
  content: ""; position: absolute; left: 0; right: 0; top: 5%; bottom: 0;
  border-radius: 0 0 50% 50% / 0 0 13% 13%;
  background:
    linear-gradient(90deg,
      rgba(0,0,0,.55) 0, rgba(0,0,0,.08) 20%,
      rgba(255,255,255,.09) 42%, rgba(255,255,255,0) 62%,
      rgba(0,0,0,.12) 78%, rgba(0,0,0,.55) 100%),
    var(--chip-body, #7E2830);
}
/* верхняя фишка лежит лицом вверх: видно инлей. */
.o-stack::after {
  content: ""; position: absolute; left: -2%; right: -2%; top: 0; height: 13%;
  border-radius: 50%;
  background:
    radial-gradient(ellipse closest-side at 50% 50%,
      var(--clay-100) 0 46%, transparent 47%),
    var(--chip-body, #7E2830);
  box-shadow: inset 0 0 0 1px rgba(0,0,0,.35), 0 1px 0 rgba(0,0,0,.4);
}
/* шов между фишками: дуга, повторяющая низ цилиндра. Тело под ней сплошное,
   поэтому по бокам, где дуга уходит вверх, фон не проглядывает. */
.o-stack i {
  position: absolute; left: 0; right: 0; height: 11%;
  border-radius: 0 0 50% 50% / 0 0 26% 26%;
  box-shadow: inset 0 -1px 0 rgba(0,0,0,.55), inset 0 -3px 0 rgba(255,255,255,.07);
}
/* вставка на боку фишки. Шаг по горизонтали разный у соседей: в настоящей стопке
   фишки лежат под случайным поворотом, и ровная колонка вставок выдаёт рисунок. */
/* Вставка занимает боковину почти на всю высоту, как на кромке в герое: короткий
   светлый квадратик посередине читался горошиной, и восемь горошин подряд давали
   не стопку, а шашечное поле. По бокам вставки — тот же шов, что в герое. */
.o-stack i::after {
  content: ""; position: absolute; top: 9%; height: 82%;
  width: 11%; left: 40%;
  background: var(--chip-spot, #E9E4E1); opacity: .62;
  box-shadow: inset 1px 0 0 rgba(0,0,0,.3), inset -1px 0 0 rgba(0,0,0,.3);
}
/* Разброс касается только фишек, у которых вставка есть. У керамики её нет,
   и отрицание в селекторе стоит здесь именно поэтому, а не для специфичности. */
.o-stack:not(.o-stack--print) i:nth-child(4n+1)::after { left: 30%; }
.o-stack:not(.o-stack--print) i:nth-child(4n+2)::after { left: 50%; }
.o-stack:not(.o-stack--print) i:nth-child(4n+3)::after { left: 36%; }
.o-stack:not(.o-stack--print) i:nth-child(4n)::after   { left: 56%; }

/* Керамика: печать идёт по всей поверхности фишки, отдельной вставки в борту
   не существует. Вместо светлого столбика вставок — сплошная печатная полоса
   по каждой фишке, и бок стопки читается как печать, а не как горошек. */
.o-stack--print i::after {
  left: 11%; width: 78%; opacity: .22;
  box-shadow: none;
}
/* Глина: тело матовое, вставка глубже утоплена, а на лицевой фишке видна
   накатка — та самая фактура, которую требует макро-кадр §3.1. */
.o-stack--clay i::after { opacity: .48; }
.o-stack--clay::after {
  background:
    repeating-conic-gradient(from 0deg at 50% 50%, rgba(0,0,0,.16) 0deg 4deg, transparent 4deg 9deg),
    radial-gradient(ellipse closest-side at 50% 50%, var(--clay-100) 0 46%, transparent 47%),
    var(--chip-body, #7E2830);
}

/* карты.

   Был один и тот же веер из двух рубашек на все двенадцать позиций раздела.
   В блоке «Докупить» четыре разные колоды стояли рядом четырьмя одинаковыми
   картинками, и выбирать между ними приходилось по обрезанному заголовку.
   Карты берут по трём вещам, и все три уже записаны в характеристиках:
   сколько колод в упаковке, из чего они сделаны и какой у них индекс.
   Индекс печатается на лице, поэтому поверх рубашек в кадре лежит лицевая
   карта: по рубашке размер индекса не виден вовсе, а приходят в этот раздел
   именно за ним.

   Чего в кадре нет намеренно — рисунка рубашки конкретного производителя.
   Три пластиковые колоды с крупным индексом данные не различают ничем, кроме
   имени завода, и сцена не различает их тоже. Нарисовать им разные рубашки
   значило бы сказать о товаре то, чего мы не проверяли.

   Пропорция кадра — тоже признак. Квадратная плашка не читается картой ни при
   каком узоре: покерная карта 63 × 88, и стоит она вертикально. */
.o-cards { position: relative; width: 46%; aspect-ratio: 1 / 1.34; }
.o-cards i {
  position: absolute; inset: 0;
  border-radius: var(--r-case);
  background:
    radial-gradient(ellipse 120% 80% at 28% 12%, rgba(255,255,255,.10), transparent 58%),
    repeating-linear-gradient(45deg, rgba(0,0,0,.18) 0 3px, transparent 3px 7px),
    var(--cloth-700);
  box-shadow: inset 0 0 0 4px var(--clay-050), var(--lift);
}
.o-cards i:first-of-type { transform: rotate(-14deg) translate(-22%, 3%); }
.o-cards i:nth-of-type(2) { transform: rotate(-7deg) translate(-11%, 1%); }

/* Лицевая карта. Белой её взять нельзя: по §9 п. 14 белое отдано числам, а
   лицо карты — плоскость в треть кадра. Взят off-white из глиняного ряда —
   тот самый, которым план и предлагает заменять белое везде, кроме чисел. */
.o-cards b {
  position: absolute; inset: 0;
  border-radius: var(--r-case);
  background: var(--clay-050);
  box-shadow: var(--lift);
  transform: rotate(8deg) translate(26%, -4%);
}
/* Индекс. На этом размере ни буква ранга, ни очертание масти не читаются, а
   два пятна верного размера и цвета читаются как индекс сразу. Красное здесь
   — цвет печати масти, то есть материал, а не акцент. */
.o-cards b::before, .o-cards b::after,
.o-cards b u::before, .o-cards b u::after {
  content: ""; position: absolute; left: 10%;
  background: var(--ink-900);
}
.o-cards b::before, .o-cards b u::before { top: 8%; width: 12%; height: 14%; }
.o-cards b::after, .o-cards b u::after { top: 25%; width: 9%; height: 9%; background: var(--spot-500); }
.o-cards--jumbo b::before, .o-cards--jumbo b u::before { width: 20%; height: 23%; }
.o-cards--jumbo b::after, .o-cards--jumbo b u::after { top: 35%; width: 15%; height: 15%; }
/* «Крупный, 2 угла» — так индекс записан в характеристиках у двух колод из
   двенадцати, и это единственное, чем они отличаются от третьей такой же
   пластиковой колоды с крупным индексом. Второй индекс повёрнут: его читает
   тот, кто держит карту с другой стороны, ради него второй угол и печатают. */
.o-cards b u { position: absolute; inset: 0; transform: rotate(180deg); }

/* Бумага и полупластик — матовые. Блик на рубашке даёт пластик, и это второе,
   за чем смотрят в характеристики: бумажная колода живёт один вечер. */
.o-cards--paper i,
.o-cards--semi i { background-image: repeating-linear-gradient(45deg, rgba(0,0,0,.14) 0 3px, transparent 3px 7px); }
.o-cards--paper b { background: var(--clay-200); }
.o-cards--semi b { background: var(--clay-100); }

/* Спецсерия: в характеристиках она названа золотой, и от обычной колоды
   отличается кантом. Латунь в палитре есть, и берётся она отсюда же. */
.o-cards--gold i { box-shadow: inset 0 0 0 4px var(--brass-500), var(--lift); }

/* Блок из двенадцати колод — не веер, а кирпич: он и продаётся, и хранится
   целиком, и на полке у него видна толщина, а не рубашка. Два цвета рубашек
   заявлены в характеристиках, поэтому пачки в кадре разные. */
.o-cards--block { width: 70%; aspect-ratio: 1 / .92; }
.o-cards--block i {
  inset: 2% auto 26% 3%; width: 44%;
  border-radius: var(--r-plaque);
  transform: none;
  box-shadow: inset 0 0 0 3px var(--clay-050), var(--lift);
}
.o-cards--block i:nth-of-type(2) {
  inset: 2% 3% 26% auto;
  background:
    repeating-linear-gradient(45deg, rgba(0,0,0,.26) 0 3px, transparent 3px 7px),
    var(--ink-700);
}
/* Срез пачки: под каждой рубашкой видно, что колод в ней шесть, а не одна.
   Толщина здесь и есть товар — блок берут тогда, когда колоды кончаются
   быстрее, чем успеваешь их докупать. */
.o-cards--block i::after {
  content: ""; position: absolute; left: -1%; right: -1%; top: 100%; height: 34%;
  border-radius: 0 0 var(--r-plaque) var(--r-plaque);
  background: repeating-linear-gradient(0deg, var(--ink-950) 0 2px, var(--clay-200) 2px 4px);
  box-shadow: var(--lift);
}

/* Карта-отсечка. Это не колода: одна непрозрачная пластина без лица, без
   индекса и без рубашки, и кладут её под низ, чтобы не было видно нижней
   карты. Веер здесь соврал бы про предмет. */
.o-cards--cut { width: 40%; }
.o-cards--cut i {
  background: var(--cloth-600);
  box-shadow: inset 0 0 0 1px rgba(0,0,0,.45), var(--lift);
  transform: rotate(-5deg);
}

/* Карты с макетом заказчика. Рубашки у них ещё нет — её приносит покупатель,
   и на её месте стоит та же пунктирная рамка, что и на месте имени магазина
   в шапке. Нарисовать здесь узор значило бы показать чужой макет. */
.o-cards--custom i {
  background: var(--ink-900);
  box-shadow: inset 0 0 0 4px var(--clay-050), var(--lift);
}
.o-cards--custom i::after {
  content: ""; position: absolute; inset: 11%;
  border: 1px dashed rgba(214,206,198,.45);
  border-radius: var(--r-plaque);
}

/* сукно.

   Был ровный красный прямоугольник: ни фактуры на таком размере, ни толщины,
   ни признака, что это ткань, а не плашка. Полотно узнают по трём вещам, и все
   три здесь: переплетение, строчка по краю и отогнутый угол, за которым видна
   изнанка. Угол важнее остального — он единственный показывает, что у предмета
   есть толщина и что он мягкий. */
.o-cloth {
  position: relative;
  width: 78%; aspect-ratio: 4 / 3;
  border-radius: var(--r-plaque);
  background:
    repeating-linear-gradient(90deg, rgba(255,255,255,.055) 0 1px, transparent 1px 3px),
    repeating-linear-gradient(0deg, rgba(0,0,0,.22) 0 1px, transparent 1px 3px),
    radial-gradient(ellipse 130% 95% at 32% 16%, rgba(255,255,255,.11), transparent 62%),
    var(--cloth-700);
  box-shadow: inset 0 0 44px rgba(0,0,0,.5);
}
.o-cloth::before {
  content: ""; position: absolute; inset: 7%;
  border: 1px dashed rgba(233,228,225,.20);
  border-radius: var(--r-plaque);
}
.o-cloth::after {
  content: ""; position: absolute; right: 0; bottom: 0; width: 24%; aspect-ratio: 1;
  background: linear-gradient(315deg,
    var(--ink-900) 0 44%,
    rgba(214,206,198,.30) 44% 45.4%,  /* сгиб ловит свет — без этой линии угол
                                         читается дырой, а не отворотом */
    var(--cloth-800) 45.4%);
  clip-path: polygon(100% 0, 100% 100%, 0 100%);
  border-radius: 0 0 var(--r-plaque) 0;
}

/* стол.

   Борт стал бортом: у него есть толщина, свет по верхней кромке и тень под ней.
   Плоское кольцо одного тона читалось как обводка фигуры, а не как обитый край,
   и стол получался иконкой стола. Внутри — строчка по сукну: на настоящем столе
   она есть всегда, и именно она отличает игровой стол от овальной столешницы. */
.o-table { width: 84%; aspect-ratio: 16 / 9; position: relative; }
.o-table i {
  position: absolute; inset: 0;
  border-radius: 50% / 50%;
  background: linear-gradient(180deg, var(--ink-700) 0, var(--ink-900) 62%, var(--ink-950) 100%);
  box-shadow:
    inset 0 2px 0 rgba(214,206,198,.16),
    inset 0 0 0 1px rgba(0,0,0,.5);
}
.o-table i::before {
  content: ""; position: absolute; inset: 10%;
  border-radius: 50% / 50%;
  background:
    repeating-linear-gradient(90deg, rgba(255,255,255,.045) 0 1px, transparent 1px 3px),
    repeating-linear-gradient(0deg, rgba(0,0,0,.18) 0 1px, transparent 1px 3px),
    var(--cloth-700);
  box-shadow: inset 0 4px 14px rgba(0,0,0,.6);
}
.o-table i::after {
  content: ""; position: absolute; inset: 22%;
  border-radius: 50% / 50%;
  border: 1px solid rgba(233,228,225,.16);
}
/* Рейсинг — утопленная дорожка между бортом и полотном, куда ставят фишки и
   стакан. Без неё клубный стол и профессиональный отличались бы на виде
   сверху только подписью: у обоих десять мест и дилер-бокс. Кольцо не
   дорисовано отдельным элементом, а получено сдвигом двух уже существующих
   границ: полотно уходит внутрь, хайрлайн выходит наружу, и между ними
   остаётся полоса самого борта — то есть ровно то, чем рейсинг и является. */
.o-table--race i::before { inset: 19%; }
.o-table--race i::after {
  inset: 12%;
  border-color: rgba(233,228,225,.26);
  box-shadow: inset 0 0 0 1px rgba(0,0,0,.6);
}

/* Разметка боксов. Восемь мест, девять и десять — это разные предметы, и по
   §3.3 плана съёмки в этой категории решение принимается именно по габаритам.
   Пока метки были нарисованы в CSS одним числом, девять столов показывали
   один овал, и главный признак категории на сцену не попадал. */
.o-table span {
  position: absolute; width: 7%; aspect-ratio: 1.6;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  border: 1px solid rgba(233,228,225,.34);
  background: rgba(0,0,0,.22);
}
/* Складывается пополам — по столу идёт линия стыка, и это главный признак
   накладки: она не стоит, а кладётся и увозится в двух половинах. `z-index`
   здесь обязателен — полотно нарисовано дочерним элементом и без него
   закрывает шов целиком, то есть признак был в разметке, но не в кадре. */
.o-table--fold::before {
  content: ""; position: absolute; left: 4%; right: 4%; top: 50%; height: 2px;
  z-index: 1;
  background: rgba(0,0,0,.6);
  box-shadow: 0 1px 0 rgba(214,206,198,.2);
}
/* Складные ножки: стол стоит на них, и из-под борта видны две стойки. Взяты
   металлическим, а не `--ink-700`: на сцене `--ink-950` тёмная стойка не
   отличима от фона, и признак, ради которого модификатор заведён, на кадр
   не попадал вовсе. */
.o-table--legs::after {
  content: ""; position: absolute; left: 22%; right: 22%; bottom: -11%; height: 11%;
  border: 2px solid var(--metal-700);
  border-top: 0;
  border-radius: 0 0 var(--r-plaque) var(--r-plaque);
}
/* Дилер-бокс — трей в вырезе борта, по середине длинной стороны. Единственное,
   чем клубный стол отличается от турнирного на виде сверху. */
.o-table b {
  position: absolute; left: 40%; right: 40%; bottom: 3%; height: 11%;
  border-radius: var(--r-plaque);
  background: var(--metal-700);
  box-shadow: inset 0 0 0 1px var(--metal-300), inset 0 3px 6px rgba(0,0,0,.5);
}

/* кофр / кейс.

   Четыре корпуса вместо одного. Разделение не косметическое: обязательный
   кадр §3.1 плана съёмки требует показать «материал корпуса, замки, ручка»,
   а одинаковый серый ящик на двенадцати позициях этот кадр не выполняет.
   Класс ставится по характеристикам позиции — см. `caseMod` в app.js. */
.o-case {
  width: 74%; aspect-ratio: 3 / 2; position: relative;
  border-radius: var(--r-case);
  background: linear-gradient(160deg, var(--metal-300), var(--metal-700) 55%, var(--metal-900));
  box-shadow: var(--lift);
}
.o-case::before {
  content: ""; position: absolute; left: 0; right: 0; top: 46%; height: 8%;
  background: rgba(0,0,0,.38);
}
.o-case::after {
  content: ""; position: absolute; left: 44%; right: 44%; top: 40%; height: 20%;
  border-radius: var(--r-plaque);
  background: var(--metal-100);
  box-shadow: inset 0 0 0 1px rgba(0,0,0,.3);
}

/* Дерево: тёмный лак по волокну, латунь только в окантовке и замке. Латунь
   здесь не украшение — она в палитре §2.1 ровно от этой фурнитуры и от
   карт-гарда, но корпус из неё делать нельзя: латунный ящик читается оливковым
   и уходит из материального ряда. Волокно набрано двумя решётками с разным
   шагом и углом: одна даёт ровную полоску, а ровная полоска — это ткань. */
.o-case--wood {
  background:
    repeating-linear-gradient(93deg, rgba(0,0,0,.24) 0 1px, transparent 1px 6px),
    repeating-linear-gradient(97deg, rgba(138,113,64,.15) 0 2px, transparent 2px 13px),
    linear-gradient(160deg, var(--metal-900), var(--ink-900) 64%, var(--ink-950));
  box-shadow: inset 0 0 0 2px var(--brass-500), var(--lift);
}
.o-case--wood::after { background: var(--brass-500); }
/* Шов корпуса на тёмном лаке не читался вовсе: чёрная полоса по чёрному телу
   пропадала, и от закрытого кейса оставался один латунный квадрат посреди
   доски — без причины, по которой он там есть. По §3.1 на обязательном кадре
   закрытого кейса читаются корпус, замок и ручка; замок без шва не замок. */
.o-case--wood::before {
  background: var(--ink-950);
  box-shadow: 0 -1px 0 rgba(138,113,64,.42), 0 1px 0 rgba(138,113,64,.42);
}

/* Кожа и подарочная коробка. Нижний край градиента раньше упирался в
   `--ink-950` — тот же цвет, что у сцены, и предмет там терял край: снизу он
   просто растворялся в фоне, а сверху оставалась плоская бордовая плашка.
   Теперь тело держится в пределах бордового ряда и ниже сцены не опускается. */
.o-case--soft {
  aspect-ratio: 3 / 2.2;
  background: linear-gradient(160deg, var(--cloth-700), var(--cloth-800) 54%, var(--ink-900));
  box-shadow: inset 0 0 0 1px rgba(214,206,198,.18), var(--lift);
}
.o-case--soft::before {
  left: 6%; right: 6%; top: 7%; height: 86%;
  background: none;
  border: 1px dashed rgba(233,228,225,.26);
  border-radius: var(--r-plaque);
}
/* Ремень поперёк — единственная деталь, по которой кожаный футляр узнаётся
   в закрытом виде. Светлым его брать нельзя: по §9 п. 14 белое на сайте
   отдано числам, а светлая полоса на тёмном теле читается именно белой — и
   тогда футляр на сто фишек кричит громче кофра на тысячу, что неправда и
   про предмет, и про иерархию листа. Ремень взят металлическим и узким:
   он деталь, а не композиция. */
.o-case--soft::after {
  left: 46.5%; right: 46.5%; top: 0; height: 100%;
  border-radius: 0;
  background: var(--metal-700);
  box-shadow: inset 1px 0 0 rgba(0,0,0,.4), inset -1px 0 0 rgba(0,0,0,.4);
}

/* Транспортный кофр: усиленный угол, борт выше, шов ниже середины. */
.o-case--roll {
  aspect-ratio: 3 / 2.3;
  background: linear-gradient(160deg, var(--metal-500), var(--metal-900) 60%, var(--ink-950));
  box-shadow: inset 0 0 0 2px var(--metal-700), var(--lift);
}
.o-case--roll::before { top: 58%; height: 6%; }
.o-case--roll::after { top: 52%; height: 18%; }
/* Второй ярус гнёзд: от тысячи фишек корпус глубже, и по боку идёт второй шов.
   Это единственное, чем набор на 1000 отличается от набора на 500 в закрытом
   виде, — и ровно то, что покупатель хочет знать до заказа. */
.o-case__tier {
  position: absolute; left: 0; right: 0; top: 34%; height: 4%;
  background: rgba(0,0,0,.3);
}
/* Ручка для переноски. §3.1 плана съёмки требует, чтобы на обязательном кадре
   закрытого кейса читались «материал корпуса, замки, ручка». Корпус и замок
   были, ручки не было ни у одного кейса, кроме кофра на колёсах, — а кейс,
   который не за что взять, это ящик. Кожаному футляру ручка не ставится: его
   носят за перевязь, и она в кадре уже есть. */
.o-case__bar {
  position: absolute; left: 38%; right: 38%; top: -8%; height: 8%;
  border: 2px solid var(--metal-700);
  border-bottom: 0;
  border-radius: var(--r-plaque) var(--r-plaque) 0 0;
}
.o-case--wood .o-case__bar { border-color: var(--brass-500); }
/* Выдвижная ручка и колёса рисуются только по заявленным характеристикам. */
.o-case__grip {
  position: absolute; left: 34%; right: 34%; top: -13%; height: 13%;
  border: 2px solid var(--metal-500);
  border-bottom: 0;
  border-radius: var(--r-plaque) var(--r-plaque) 0 0;
}
.o-case__wheels { position: absolute; left: 0; right: 0; bottom: -7%; height: 7%; }
.o-case__wheels::before,
.o-case__wheels::after {
  content: ""; position: absolute; bottom: 0; width: 13%; aspect-ratio: 1;
  border-radius: var(--r-chip);
  background: var(--ink-950);
  box-shadow: inset 0 0 0 1px var(--metal-500);
}
.o-case__wheels::before { left: 9%; }
.o-case__wheels::after  { right: 9%; }

/* Пара кофров: клубный набор приезжает в двух, и это видно на закрытом кадре.
   Второй корпус стоит позади и виден углом — предмета два, и в разметке их
   тоже два, а не один с приписанной тенью. */
.o-pair { position: relative; width: 80%; }
.o-pair .o-case { width: 84%; }
.o-pair .o-case:first-child { position: absolute; right: 0; top: 0; opacity: .66; }
.o-pair .o-case:last-child { margin-top: 15%; }

/* рэк и дилерский трей.

   Ряды больше не фиксированы: рэк на 100 фишек — пять рядов по двадцать,
   трей на 300 — шесть, на 500 — десять. Число берётся из характеристик
   позиции, поэтому решётка на сцене и строка «Рядов» в таблице не могут
   разойтись. Одна и та же решётка на четыре позиции скрывала ровно то,
   за что их различают. */
.o-rack {
  width: 68%; aspect-ratio: 5 / 2; position: relative;
  border-radius: var(--r-plaque);
  background: var(--metal-700);
  display: grid; grid-template-columns: repeat(var(--rack-n, 5), 1fr); gap: 4px; padding: 6px;
}
.o-rack--wide { width: 82%; aspect-ratio: 3 / 1; gap: 3px; }
.o-rack i { background: var(--ink-950); border-radius: 2px; box-shadow: inset 0 0 0 1px var(--metal-500); }
/* Крышка с замком: у трея она есть, у рэка нет, и в названии обеих позиций
   это стоит первым. Петля по верхнему краю и плашка замка по нижнему. */
.o-rack--lid { box-shadow: inset 0 2px 0 var(--metal-300); }
.o-rack--lid::after {
  content: ""; position: absolute; left: 44%; right: 44%; bottom: -5%; height: 10%;
  border-radius: var(--r-plaque);
  background: var(--metal-300);
  box-shadow: inset 0 0 0 1px rgba(0,0,0,.35);
}

/* диск: баттон, хранитель карт, монета */
.o-disc {
  width: 46%; aspect-ratio: 1; border-radius: var(--r-chip);
  background: radial-gradient(circle at 36% 30%, rgba(255,255,255,.22), transparent 60%), var(--disc-body, var(--clay-050));
  color: var(--ink-900);
  display: flex; align-items: center; justify-content: center;
  font-family: var(--f-display); font-weight: 700; font-size: 15px;
  letter-spacing: .04em;
  box-shadow: inset 0 0 0 2px rgba(0,0,0,.2), var(--lift);
  text-align: center; padding: var(--s-2);
}

/* плака */
.o-plaque {
  width: 40%; aspect-ratio: 1 / 2.1;
  border-radius: var(--r-plaque);
  background: linear-gradient(150deg, #8E2B2F, #421318);
  box-shadow: inset 0 0 0 2px rgba(255,255,255,.14), var(--lift);
  display: flex; align-items: center; justify-content: center;
}
.o-plaque span {
  width: 64%; aspect-ratio: 1 / 1.6; background: var(--clay-050);
  display: flex; align-items: center; justify-content: center;
  font-family: var(--f-display); font-weight: 700; color: var(--ink-900);
}

/* кубик */
.o-dice { display: flex; gap: var(--s-2); }
.o-dice i {
  width: 44px; height: 44px; border-radius: var(--r-plaque);
  background: var(--clay-050);
  box-shadow: inset 0 -6px 12px rgba(0,0,0,.14), var(--lift);
  position: relative;
}
.o-dice i::after {
  content: ""; position: absolute; left: 50%; top: 50%; width: 8px; height: 8px;
  margin: -4px 0 0 -4px; border-radius: var(--r-chip); background: var(--ink-900);
}

/* таймер */
.o-timer {
  width: 74%; aspect-ratio: 5 / 3;
  border-radius: var(--r-case);
  background: var(--ink-700);
  box-shadow: inset 0 0 0 3px var(--metal-900), var(--lift);
  display: flex; align-items: center; justify-content: center;
}
.o-timer span { font-family: var(--f-display); font-size: 40px; font-weight: 700; color: var(--inlay); }

/* документ / сертификат */
.o-doc {
  width: 62%; aspect-ratio: 3 / 2;
  background: var(--clay-050);
  border-radius: var(--r-plaque);
  box-shadow: var(--lift);
  display: flex; align-items: center; justify-content: center;
  color: var(--ink-900);
}

/* мат */
.o-mat {
  width: 76%; aspect-ratio: 4 / 3;
  border-radius: 40px / 30px;
  background:
    repeating-linear-gradient(90deg, rgba(255,255,255,.04) 0 1px, transparent 1px 3px),
    var(--cloth-600);
  box-shadow: inset 0 0 0 3px var(--ink-950);
}

/* --------------------------------------------------------------------------
   Шапка. Переключателя сегмента нет. Липкости с размытием нет.
   -------------------------------------------------------------------------- */
.hdr { background: var(--clay-100); border-bottom: var(--hair); }
.hdr__in { display: flex; align-items: center; gap: var(--s-5); padding: var(--s-4) 0; }
.hdr__mark { display: flex; align-items: center; gap: var(--s-3); text-decoration: none; color: inherit; }
.hdr__glyph {
  width: 32px; height: 32px; border-radius: var(--r-chip);
  background: var(--ink-900); position: relative; flex: 0 0 auto;
}
.hdr__glyph::before {
  content: ""; position: absolute; inset: 0; border-radius: var(--r-chip);
  background: repeating-conic-gradient(var(--clay-100) 0deg 12deg, transparent 12deg 60deg);
  -webkit-mask: radial-gradient(circle closest-side, transparent 0 64%, #000 64% 100%);
          mask: radial-gradient(circle closest-side, transparent 0 64%, #000 64% 100%);
}
.hdr__glyph::after {
  content: ""; position: absolute; inset: 30%; border-radius: var(--r-chip); background: var(--clay-100);
}
/* Пустое место под имя — это объект, а не отсутствие объекта. Рамка была
   инлайновым span с width и height, то есть шириной в два пикселя: подпись
   вылезала из-под неё и садилась на знак, и место под имя читалось как сломанная
   вёрстка, а не как открытый вопрос владельца (§9 п. 7 системы). Подпись
   переехала внутрь рамки: снаружи она объясняла соседний знак, внутри —
   собственную пустоту. */
.hdr__slot {
  display: inline-flex; align-items: center;
  height: 32px; padding: 0 var(--s-3);
  border: 1px dashed var(--metal-300);
  border-radius: var(--r-plaque);
}
.hdr__slotcap { font-size: 12px; line-height: 1; color: var(--metal-700); white-space: nowrap; }
/* Обёртка меню и правых ссылок. На широком экране она ничего не меняет — просто
   растягивается на свободное место; на телефоне становится прокручиваемой строкой. */
.hdr__scroll { flex: 1 1 auto; display: flex; align-items: center; gap: var(--s-5); }
.hdr__nav { display: flex; gap: var(--s-4); flex-wrap: wrap; }
.hdr__nav a { text-decoration: none; font-size: 15px; }
.hdr__nav a:hover { border-bottom: 1px solid var(--spot-500); }
.hdr__nav a[aria-current="page"] { border-bottom: 1px solid var(--ink-900); }
.hdr__right { margin-left: auto; display: flex; align-items: center; gap: var(--s-4); }

/* Хлебные крошки */
.crumbs { font-size: 13px; line-height: 1.45; color: var(--metal-700); display: flex; gap: var(--s-2); flex-wrap: wrap; }
.crumbs a { text-decoration: none; }
.crumbs a:hover { border-bottom: 1px solid var(--spot-500); }

/* --------------------------------------------------------------------------
   Подвал
   -------------------------------------------------------------------------- */
.ftr { background: var(--cloth-800); color: var(--clay-100); padding: var(--s-7) 0 var(--s-6); }
.ftr a { text-decoration: none; }
.ftr a:hover { border-bottom: 1px solid var(--clay-200); }
.ftr__cols { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--gap); }
.ftr__h { font-size: 15px; font-weight: 600; margin-bottom: var(--s-3); }
.ftr__list { list-style: none; margin: 0; padding: 0; font-size: 15px; }
.ftr__list li + li { margin-top: var(--s-2); }

/* --------------------------------------------------------------------------
   7. Смелость в одном месте — герой главной
   -------------------------------------------------------------------------- */
/* Высоту герою задаёт содержимое, а не доля экрана. Было 84vh с текстом по
   центру и кромкой, приклеенной к низу абсолютом: на настоящем экране в 800 px
   это давало две трети пустого чёрного поля сверху и полоску материала в 60 px
   под срезом — то есть ровно обратное тому, чего требует §7. Кромка встала в
   поток отдельной полосой: она всегда одной высоты, её видно при любом окне,
   и пустоте неоткуда взяться. */
.hero {
  --edge-h: 340px;
  background: var(--ink-950); color: var(--clay-100);
  position: relative; overflow: hidden;
}
.hero__in {
  position: relative; z-index: 2;
  padding: var(--s-7) 0 var(--s-5);
}
/* Число стоит НА материале, а не над чёрным полем: §7 требует «поверх кадра —
   одно число t-hero белым», и кадр здесь — кромка, а не пустота над ней. Белое
   на глине — это и есть инлей, смелость системы по §7; белое на чёрном было бы
   просто крупным шрифтом.

   Отвергнуто столбиком, как буквально написано в §7 («заголовок под числом»):
   в настоящем окне 1280×800 число в 112 px плюс подпись съедали 150 px до
   заголовка, и полоса материала уходила под срез целиком. §7 в том же абзаце
   требует кадр во весь экран; сохранить оба требования буквально нельзя, и
   выбрано то, ради которого раздел написан, — виден материал. Порядок чтения
   меняется, инлей на материале остаётся. */
.hero__stamp {
  position: absolute; z-index: 3; left: 0; right: 0; bottom: 0;
  height: var(--edge-h);
  display: flex; align-items: center;
  pointer-events: none;
}

/* Макро-срез кромки. §7 требует кадр, в котором фишка НЕ читается целиком:
   читается материал. Сверху вниз по кадру: край лицевой стороны, меловой срез
   инлея, перегиб, кромка со слоем edge-spot и накаткой, уход в тень.

   Все границы лежат на одной дуге — окружности, центр которой втрое ниже кадра.
   Кривизна на такой дистанции почти не видна, и ровно поэтому она нужна: прямая
   линия во всю ширину читается как линейка вёрстки, а чуть провисающая — как
   край предмета, который больше кадра. Геометрия дуги вынесена в --arc, потому
   что по ней идут все четыре слоя: разъехавшись хоть на процент, они перестают
   быть одной фишкой.

   Две отвергнутые версии: целая фишка в углу — предмет на подложке, что §7
   запрещает прямо; дуга во весь низ — читалась как колесо, то есть телл §8.1
   п. 11 и сигнальная категория из CLAUDE.md, инвариант 3. */
.hero__edge {
  --arc: ellipse 240% 342% at 50% 289%;
  position: relative; z-index: 1; height: var(--edge-h);
  /* Стопы идут снизу вверх: у радиальной дуги меньший процент — ближе к центру,
     то есть ниже по кадру. Полоса кадра лежит между 55,3 % (низ) и 84,5 % (верх),
     и почти вся она отдана материалу: свет по губе, кромка, обрез кадром снизу.

     Прошлая раскладка отдавала материалу треть полосы, а две трети — чёрному телу
     и меловому срезу инлея. На макро-кадре кромки лицевой стороны не видно вообще,
     поэтому срез инлея был выдумкой, и читался он как забытая направляющая: тонкая
     светлая линия во всю ширину, оторванная от красного чёрным провалом. Убран.
     Осталось то, что на таком увеличении действительно попадает в кадр. */
  background: radial-gradient(var(--arc),
    var(--cloth-800) 0,
    var(--cloth-800) 57%,    /* низ: кромка уходит от света и обрезается кадром */
    var(--cloth-700) 63%,
    var(--cloth-600) 72%,    /* самая освещённая часть кромки */
    var(--cloth-700) 79%,
    var(--cloth-800) 82%,
    var(--metal-100) 82.15%, /* губа: фаска ловит свет тонкой линией */
    var(--metal-100) 82.3%,
    var(--ink-950)   82.4%,  /* выше губы предмета нет — только поле героя */
    var(--ink-950)   100%);
}
/* слой edge-spot. Вставка проходит кромку насквозь, от губы до обреза кадра, а не
   сидит полосой посередине: у глиняной фишки это клин другого теста, запрессованный
   в тело. Полоса посередине вместе с накаткой давала жалюзи.

   По краям вставки — тёмный шов в два пикселя. Без шва вставка вставала встык с
   кромкой и читалась как серая панель: чередование красных и серых полей во всю
   высоту полосы давало не фишку, а гофрированный лист. Шов — это стык двух
   запрессованных тестов, и он единственный отличает вставку от заплатки.
   Ширина уменьшена с 86 до 72 пикселей, шаг увеличен с 400 до 430 по той же
   причине: вставка должна быть деталью кромки, а не её половиной.

   Тесто вставки тёмное, а не меловое. Светлая вставка спорила с числом героя за
   право быть самым светлым пятном кадра, а по §2.2 белое в этой системе носят
   только числа. Плюс три меловых поля во всю высоту полосы читались панелями
   независимо от ширины. Тёмная вставка на бордовой кромке — сочетание такое же
   настоящее, и на ней виден шов, а не панель. */
.hero__edge::before {
  content: ""; position: absolute; inset: 0;
  background: repeating-linear-gradient(90deg,
    rgba(214,206,198,.16) 0 1px,
    var(--ink-900)        1px 73px,
    rgba(214,206,198,.16) 73px 74px,
    transparent           74px 430px);
  -webkit-mask: radial-gradient(var(--arc), #000 0 81.5%, transparent 82.05%);
          mask: radial-gradient(var(--arc), #000 0 81.5%, transparent 82.05%);
}
/* накатка: насечка по кромке. Идёт поверх вставок, потому что её прокатывают по
   готовой кромке. Шаг мелкий и контраст низкий намеренно: на таком увеличении
   это рубчик, который виден на ощупь, а не решётка.

   Контраст снижен с .13/.035 до .085/.025 и шаг растянут с 5 до 6 пикселей: на
   светлом тесте вставки прежняя насечка читалась вчетверо сильнее, чем на тёмном
   сукне кромки, и превращала вставку в шлифованный металл. Насечка одна на всю
   кромку, значит её контраст задаётся по светлому участку, а не по тёмному. */
.hero__edge::after {
  content: ""; position: absolute; inset: 0;
  background: repeating-linear-gradient(90deg,
    rgba(0,0,0,.085) 0 2px, rgba(255,255,255,.025) 2px 3px, transparent 3px 6px);
  -webkit-mask: radial-gradient(var(--arc), #000 0 81.7%, transparent 82.1%);
          mask: radial-gradient(var(--arc), #000 0 81.7%, transparent 82.1%);
}
/* свет на цилиндре — верхний слой, и он же сажает вставки в тело: без него
   edge-spot светится ровной заливкой и читается как наклейка, а не как вставка.
   Свет тоже идёт по --arc, иначе блик пересекает кромку под своим углом.

   Третий слой — падение к левому и правому краю кадра. Кромка круглая, и на краях
   она уходит от зрителя так же, как уходит вниз. Без этого падения полоса ровной
   яркости во всю ширину читается как лента, а не как бок предмета. */
.hero__edge i {
  position: absolute; inset: 0;
  background:
    linear-gradient(90deg,
      rgba(22,15,17,.5) 0, rgba(22,15,17,0) 20%,
      rgba(22,15,17,0) 80%, rgba(22,15,17,.5) 100%),
    radial-gradient(var(--arc),
      rgba(255,255,255,0) 0 71%, rgba(255,255,255,.065) 77%, rgba(255,255,255,0) 81.4%),
    radial-gradient(var(--arc),
      rgba(22,15,17,.72) 0 52%,
      rgba(22,15,17,.34) 60%,
      rgba(22,15,17,0)   69%);
}
/* тень кадра: верх полосы растворяется в чёрном поле героя, а не режется линией.
   Ложится поверх стыка, поэтому привязан к той же высоте полосы. */
.hero__shade {
  position: absolute; z-index: 2; left: 0; right: 0;
  bottom: var(--edge-h); height: 96px;
  background: linear-gradient(180deg, rgba(22,15,17,0), var(--ink-950));
  pointer-events: none;
}
.hero__stamp .wrap { width: 100%; display: flex; justify-content: flex-end; }
.hero__num { display: flex; align-items: baseline; gap: var(--s-3); }
.hero__num b { color: var(--inlay); font-family: var(--f-display); font-size: 140px; line-height: .95; font-weight: 700; }
/* Подпись стоит на глине, а не на чёрном, поэтому серый металл ей не годится:
   --metal-300 на --cloth-700 читается хуже, чем на поле героя. */
.hero__num i { font-style: normal; color: var(--clay-200); font-size: 13px; line-height: 1.45; }
/* 740, а не 620: на 620 строка «…перестаёт быть лёгкой» переносила последнее
   слово в отдельную строку, и заголовок читался как три обрывка. */
.hero__h { max-width: 740px; }
.hero__p { max-width: 520px; margin-top: var(--s-4); color: var(--metal-300); }
.hero__cta { margin-top: var(--s-5); }

@media (prefers-reduced-motion: no-preference) {
  .hero__settle { animation: settle 240ms cubic-bezier(.2,.7,.3,1) both; }
  @keyframes settle { from { transform: translateY(-8px); } to { transform: none; } }
}

/* --------------------------------------------------------------------------
   Полоса измерений. Компонента в 14-design-system.md нет, и добавлен он по
   §2.2: белое отдано числам, а четыре числа здесь — это и есть то, из-за чего
   выбирают между материалами. Отдельной плоскости не заводит, продолжает
   чёрное поле героя и отделяется от него волосяной линией.
   -------------------------------------------------------------------------- */
.facts { border-top: var(--hair-dark); padding: var(--s-6) 0; }
.facts__row { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 0; }
.facts__c { padding-right: var(--s-5); }
.facts__c + .facts__c { border-left: var(--hair-dark); padding-left: var(--s-5); }
.facts__v {
  font-family: var(--f-display); font-size: 54px; line-height: 1; font-weight: 700;
  color: var(--inlay); display: flex; align-items: baseline; gap: 6px;
}
.facts__v span { font-family: var(--f-text); font-size: 15px; font-weight: 400; color: var(--metal-300); }
.facts__k { margin-top: var(--s-3); font-size: 15px; line-height: 1.5; color: var(--metal-300); }

/* --------------------------------------------------------------------------
   Полоса отгрузки перед подвалом. У справочных страниц высокая ценность и
   нулевая заметность: без такой полосы «14–30 дней» покупатель узнаёт уже
   из корзины. Повторяет ритм .facts и тоже стоит на чёрном — но без белого:
   сроки здесь не числа-аргументы, а условия, и §2.2 белое им не отдаёт.
   -------------------------------------------------------------------------- */
.ship__row { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 0; }
.ship__c { padding-right: var(--s-5); }
.ship__c + .ship__c { border-left: var(--hair-dark); padding-left: var(--s-5); }
.ship__h { font-size: 15px; font-weight: 600; color: var(--clay-100); }
.ship__p { margin: var(--s-2) 0 0; font-size: 15px; line-height: 1.5; color: var(--metal-300); }

/* --------------------------------------------------------------------------
   Лестница тиража в конструкторе. Ступень — не карточка: фон и рамка у неё
   появляются только у выбранной, остальные держит одна линия слева.
   -------------------------------------------------------------------------- */
.ladder { display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); gap: 0; }
.ladder__s {
  padding: var(--s-3) var(--s-3) var(--s-4); border: 0; border-left: var(--hair);
  background: none; color: inherit; text-align: left; cursor: pointer; font: inherit;
}
.ladder__s:last-child { border-right: var(--hair); }
.ladder__n { font-family: var(--f-display); font-size: 34px; line-height: 1.15; font-weight: 700; }
.ladder__n span { font-family: var(--f-text); font-size: 13px; font-weight: 400; color: var(--metal-700); margin-left: 4px; }
.ladder__k { margin-top: var(--s-2); font-size: 13px; line-height: 1.45; color: var(--metal-700); }
.ladder__s--on { background: var(--ink-900); color: var(--clay-050); border-left-color: var(--ink-900); }
.ladder__s--on .ladder__n span, .ladder__s--on .ladder__k { color: var(--metal-300); }

/* Применение кастома: плитка без рамки, с одной линией сверху */
.usecase { border-top: var(--rule); padding-top: var(--s-3); }
.usecase__h { font-size: 17px; font-weight: 600; }
.usecase__p { margin: var(--s-2) 0 0; font-size: 15px; line-height: 1.5; color: var(--metal-700); }

/* --------------------------------------------------------------------------
   Подбор комплекта — решение боли, а не строчка в сайдбаре
   -------------------------------------------------------------------------- */
.pick { background: var(--clay-050); border-radius: var(--r-case); padding: var(--s-5); box-shadow: var(--lift); }
.pick--flat { box-shadow: none; border: var(--hair); }
.pick__q + .pick__q { margin-top: var(--s-4); padding-top: var(--s-4); border-top: var(--hair); }
.pick__lbl { font-size: 15px; font-weight: 600; margin-bottom: var(--s-3); }
.opts { display: flex; flex-wrap: wrap; gap: var(--s-2); }
.opt {
  border: 1px solid var(--metal-500);
  background: transparent;
  border-radius: var(--r-plaque);
  padding: 8px var(--s-3);
  font-size: 15px;
  cursor: pointer;
}
.opt:hover { background: var(--clay-200); }
.opt[aria-pressed="true"] { background: var(--ink-900); color: var(--clay-050); border-color: var(--ink-900); }
.pick__out { margin-top: var(--s-5); padding-top: var(--s-4); border-top: var(--rule); }

/* --------------------------------------------------------------------------
   5.6 Фасеты
   -------------------------------------------------------------------------- */
.layout-cat { display: grid; grid-template-columns: 2fr 8fr; gap: var(--gap); }
.facets__g + .facets__g { margin-top: var(--s-4); padding-top: var(--s-4); border-top: var(--hair); }
.facets__h { font-size: 15px; color: var(--metal-900); font-weight: 600; margin-bottom: var(--s-3); }
.facet {
  display: flex; align-items: center; gap: var(--s-2);
  width: 100%; background: none; border: 0; padding: var(--s-1) 0;
  cursor: pointer; text-align: left; font-size: 15px;
}
.facet__dot { width: 8px; height: 8px; border-radius: var(--r-chip); background: transparent; flex: 0 0 auto; }
.facet[aria-pressed="true"] .facet__dot { background: var(--spot-500); }
.facet__n { margin-left: auto; font-size: 13px; color: var(--metal-700); }
.facet--empty { color: var(--metal-500); cursor: default; }
.chips-sel { display: flex; flex-wrap: wrap; gap: var(--s-2); margin-bottom: var(--s-4); }
.chip-sel {
  display: inline-flex; align-items: center; gap: var(--s-2);
  border: 1px solid var(--metal-500); border-radius: var(--r-plaque);
  padding: 4px var(--s-2); font-size: 13px; background: none; cursor: pointer;
}

.cat-head { display: flex; align-items: flex-end; justify-content: space-between; gap: var(--s-4); flex-wrap: wrap; }
.sortsel { border: 1px solid var(--metal-500); border-radius: var(--r-plaque); background: var(--clay-050); padding: 10px var(--s-3); font-size: 15px; }

/* --------------------------------------------------------------------------
   Карточка товара
   -------------------------------------------------------------------------- */
/* Все три колонки — minmax(0, …), а не голый fr. Голая доля значит
   minmax(auto, Nfr), и нижняя граница такого трека растёт под содержимое.
   В галерее лежит сцена с aspect-ratio: её ширина и высота выводятся друг из
   друга, и трек разъезжался до 536 px внутри поля в 358 — карточка товара
   уезжала за правый край телефона целиком. Ноль в нижней границе разрывает
   этот круг: трек считается от поля, а не от предмета внутри. */
.pdp { display: grid; grid-template-columns: minmax(0, 5fr) minmax(0, 5fr); gap: var(--s-6); }
.pdp__gal { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--s-2); }
.pdp__thumbs { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--s-2); }
/* Гнёзда четырёх обязательных кадров. Три из четырёх пустые, и пустое гнездо
   обязано отличаться от сломанного: у него пунктирная рамка и имя кадра —
   тот же язык, которым в шапке обозначено место под неназванный бренд. */
.pdp__thumb {
  position: relative; aspect-ratio: 1;
  background: var(--ink-950); border: 0; padding: 0; cursor: pointer;
  border-radius: var(--r-edge);
  overflow: hidden;
}
.pdp__thumb[aria-pressed="true"] { box-shadow: inset 0 0 0 2px var(--clay-050); }
.pdp__thumb--empty {
  background: none;
  box-shadow: inset 0 0 0 1px var(--metal-300);
  cursor: default;
}
/* Пунктир рисуется вставкой, а не border: рамка на самом элементе съела бы
   один пиксель под подпись и увела бы гнёзда из ряда по высоте. */
.pdp__thumb--empty::before {
  content: ""; position: absolute; inset: var(--s-2);
  border: 1px dashed var(--metal-300);
  border-radius: var(--r-plaque);
}
/* Живое гнездо: тот же предмет, что на большой сцене, только квадратом.
   Собственной сцены здесь нет — фон даёт сама кнопка. */
.pdp__thumbin {
  position: absolute; inset: 0;
  display: flex; align-items: center; justify-content: center;
}
.pdp__thumbin .o-chip__n { font-size: 13px; }
.pdp__thumbcap {
  position: absolute; left: var(--s-2); right: var(--s-2); bottom: var(--s-2);
  font-size: 11px; line-height: 1.2; color: var(--metal-500);
  text-align: left;
}
.pdp__thumb--live .pdp__thumbcap { color: var(--clay-200); }

.keyspecs { display: grid; grid-template-columns: repeat(3, minmax(0,1fr)); gap: var(--s-4); }
.keyspec__v { font-family: var(--f-display); font-size: 27px; font-weight: 700; line-height: 1.25; }
.keyspec__k { font-size: 13px; line-height: 1.45; color: var(--metal-700); }

.spectable { width: 100%; border-collapse: collapse; font-size: 15px; }
.spectable td { padding: var(--s-2) var(--s-3); border-bottom: var(--hair); vertical-align: top; }
.spectable tr:nth-child(even) { background: var(--clay-200); }
.spectable td:first-child { color: var(--metal-700); width: 42%; }

.important { border-top: var(--hair); padding-top: var(--s-3); font-size: 15px; color: var(--metal-900); }
.important dl { margin: 0; display: grid; grid-template-columns: max-content 1fr; gap: var(--s-2) var(--s-4); }
.important dt { color: var(--metal-700); }
.important dd { margin: 0; }

.kit { list-style: none; margin: 0; padding: 0; font-size: 15px; }
.kit li { padding: var(--s-2) 0; border-bottom: var(--hair); display: flex; justify-content: space-between; gap: var(--s-4); }

/* Нумерованный разбор в материале. Номер — число, поэтому он белый: §2.2
   отдаёт белое числам и ничему больше. Кружок под ним — та же фишка (--r-chip),
   что и в остальных компонентах, а не декоративный маркер. */
.numlist { list-style: none; margin: var(--s-4) 0 0; padding: 0; counter-reset: n; font-size: 17px; }
.numlist li { counter-increment: n; position: relative; padding: 0 0 var(--s-4) calc(var(--s-5) + var(--s-3)); line-height: 1.55; }
.numlist li::before {
  content: counter(n); position: absolute; left: 0; top: 2px;
  width: var(--s-5); height: var(--s-5); border-radius: var(--r-chip);
  background: var(--cloth-700); color: var(--inlay);
  font-family: var(--f-text); font-size: 13px; font-weight: 600;
  display: flex; align-items: center; justify-content: center;
}

/* Таблица шире экрана прокручивается сама, а не растягивает страницу.
   Пять колонок структуры уровней на телефоне иначе дают горизонтальный
   скролл всему документу — а он ломает вёрстку, а не одну таблицу. */
.tablewrap { overflow-x: auto; }
.tablewrap table { min-width: 100%; }
/* Подпись о прокрутке нужна только на телефоне: на широком экране таблица
   помещается целиком, и фраза про боковое движение была бы ложью. */
.tablehint { display: none; }

/* Таблица характеристик в три и более колонок. В 14-design-system.md компонент
   описан для пары «свойство — значение»; сравнение трёх материалов в эту пару
   не складывается. Добавлены шапка и сброс фиксированной ширины первой колонки,
   ритм строк и чередование остаются прежними. */
.spectable--grid td:first-child { width: auto; color: var(--ink-900); }
.spectable--grid thead tr { background: none; }
.spectable--grid th {
  padding: var(--s-2) var(--s-3); border-bottom: var(--rule); text-align: left;
  font-family: var(--f-text); font-size: 13px; font-weight: 600; color: var(--metal-700);
}

.optgrp { border: var(--hair); border-radius: var(--r-plaque); padding: var(--s-3); }
.optgrp label { display: flex; align-items: center; gap: var(--s-3); padding: var(--s-2) 0; font-size: 15px; cursor: pointer; }
.optgrp label + label { border-top: var(--hair); }
.optgrp .pr { margin-left: auto; color: var(--metal-700); font-size: 13px; }

/* 5.8 Печать «проверено на турнирах». Без числа блок не рендерится вовсе. */
.stamp {
  width: 104px; height: 104px; border-radius: var(--r-chip);
  border: 2px solid var(--brass-500); color: var(--brass-500);
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  text-align: center; font-size: 13px; line-height: 1.2;
}

/* Блок перетока на /clubs */
.toclub { background: var(--cloth-700); color: var(--clay-100); padding: var(--s-5); }
/* Тот же красный план, но другой повод: позиция снята с публикации. Раньше
   сообщение носило класс `toclub` — компонент §5.10, «переток на /clubs».
   Плоскость у них общая, смысл разный, и имя класса врало о втором. */
.notice { background: var(--cloth-700); color: var(--clay-100); padding: var(--s-5); }

/* 5.7 Карточка б/у — единственный компонент на металле */
.used { background: var(--metal-100); padding: var(--s-5); border-radius: var(--r-edge); }

/* Карточка услуги — живёт на красной плоскости, фотосцены нет */
.svc { background: var(--cloth-700); color: var(--clay-100); padding: var(--s-5); display: flex; flex-direction: column; gap: var(--s-4); }
.svc__defs { display: grid; grid-template-columns: repeat(2, minmax(0,1fr)); gap: var(--s-3) var(--s-4); font-size: 15px; }
.svc__defs dt { color: var(--metal-300); font-size: 13px; }
.svc__defs dd { margin: 0 0 var(--s-3); }

/* Конструктор */
.steps { display: flex; gap: var(--s-2); flex-wrap: wrap; }
.step {
  border: 1px solid var(--metal-500); background: none; border-radius: var(--r-plaque);
  padding: 8px var(--s-3); font-size: 13px; cursor: pointer; display: flex; gap: var(--s-2); align-items: center;
}
.step[aria-current="step"] { background: var(--ink-900); color: var(--clay-050); border-color: var(--ink-900); }
.step__i { width: 20px; height: 20px; border-radius: var(--r-chip); background: var(--metal-300); color: var(--ink-900);
  display: flex; align-items: center; justify-content: center; font-size: 13px; font-weight: 700; }
.step[aria-current="step"] .step__i { background: var(--clay-050); }

.field { display: block; }
.field__l { font-size: 13px; color: var(--metal-700); margin-bottom: var(--s-2); }
.inp {
  width: 100%; background: var(--clay-050); border: 1px solid var(--metal-500);
  border-radius: var(--r-plaque); padding: 10px var(--s-3); font-size: 17px;
}
.denomrow { display: grid; grid-template-columns: 90px 1fr 120px 40px; gap: var(--s-3); align-items: center; }
.denomrow + .denomrow { margin-top: var(--s-2); }

.spec { background: var(--clay-050); border: var(--hair); padding: var(--s-4); }
.spec dl { margin: 0; display: grid; grid-template-columns: max-content 1fr; gap: var(--s-2) var(--s-4); font-size: 15px; }
.spec dt { color: var(--metal-700); }
.spec dd { margin: 0; }

/* Пометка прототипа — не часть сайта */
.proto {
  background: var(--metal-100); color: var(--ink-900);
  border-bottom: 1px solid var(--metal-500);
  font-size: 13px; line-height: 1.45;
}
.proto__in { padding: var(--s-2) var(--gutter); max-width: var(--maxw); margin: 0 auto; display: flex; gap: var(--s-4); flex-wrap: wrap; }
.proto b { font-weight: 600; }

/* Служебное */
.modal-back { position: fixed; inset: 0; background: rgba(22,15,17,.55); display: flex; align-items: center; justify-content: center; padding: var(--s-4); z-index: 50; }
.modal { background: var(--clay-050); border-radius: var(--r-case); box-shadow: var(--lift); max-width: 560px; width: 100%; padding: var(--s-5); }

.sticky-cart {
  position: fixed; right: var(--s-4); bottom: var(--s-4); z-index: 40;
  background: var(--clay-050); border-radius: var(--r-case); box-shadow: var(--lift);
  padding: var(--s-3) var(--s-4); display: flex; gap: var(--s-3); align-items: center; font-size: 15px;
}

.two { display: grid; grid-template-columns: repeat(2, minmax(0,1fr)); gap: var(--gap); }
.three { display: grid; grid-template-columns: repeat(3, minmax(0,1fr)); gap: var(--gap); }
.four { display: grid; grid-template-columns: repeat(4, minmax(0,1fr)); gap: var(--gap); }

.secheads { display: flex; align-items: baseline; justify-content: space-between; gap: var(--s-4); flex-wrap: wrap; }

.linklist { list-style: none; margin: 0; padding: 0; }
.linklist li { border-bottom: var(--hair); }
.linklist a { display: block; padding: var(--s-3) 0; text-decoration: none; font-size: 17px; }
.linklist a:hover { color: var(--cloth-700); }

/* --------------------------------------------------------------------------
   4. Отклик сетки
   -------------------------------------------------------------------------- */
@media (max-width: 1439px) {
  :root { --cols: 8; --gutter: 32px; }
  .tiles { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  .t-hero, .hero__num b { font-size: 112px; }
}
@media (max-width: 1023px) {
  :root { --cols: 6; --gutter: 24px; --gap: 20px; }
  .tiles, .tiles-4 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .layout-cat { grid-template-columns: 1fr; }
  .pdp { grid-template-columns: minmax(0, 1fr); gap: var(--s-5); }
  .ftr__cols { grid-template-columns: repeat(2, minmax(0,1fr)); }
  .t-hero, .hero__num b { font-size: 96px; }
  .t-h1 { font-size: 42px; }
  .hero { --edge-h: 280px; }
  .four { grid-template-columns: repeat(2, minmax(0,1fr)); }
  .ladder { grid-template-columns: repeat(3, minmax(0,1fr)); }
  .ladder__s { border-top: var(--hair); }
}
@media (max-width: 767px) {
  :root { --cols: 4; --gutter: 20px; --gap: 16px; }
  .tiles, .tiles-4, .tiles-3 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .three, .two { grid-template-columns: 1fr; }
  .keyspecs { grid-template-columns: repeat(2, minmax(0,1fr)); }
  /* Шапка на телефоне занимала 193 px: пять пунктов меню переносились в три ряда,
     корзина уходила в четвёртый, и вместе с полосой прототипа до героя оставалось
     306 px из 780. Материал первого экрана в такой высоте не помещается вовсе —
     кромка начиналась на 771-м пикселе, то есть за срезом.

     Меню встало в одну прокручиваемую строку. Это размен: правые пункты за краем
     экрана видно не сразу. Взамен видно товар, ради которого страница открыта, а
     полоска обрезанного пункта у правого края сама говорит, что строка едет. */
  .hdr__in { flex-wrap: wrap; gap: var(--s-2); padding: var(--s-3) 0; }
  .hdr__mark { width: 100%; }
  .hdr__scroll {
    width: 100%; margin-left: 0;
    display: flex; align-items: center; gap: var(--s-4);
    overflow-x: auto; scrollbar-width: none;
    /* край строки растворяется — это и есть подсказка о прокрутке */
    -webkit-mask: linear-gradient(90deg, #000 0 88%, transparent 100%);
            mask: linear-gradient(90deg, #000 0 88%, transparent 100%);
  }
  .hdr__scroll::-webkit-scrollbar { display: none; }
  .hdr__nav { flex-wrap: nowrap; }
  .hdr__nav a, .hdr__right a { white-space: nowrap; }
  .hdr__right { margin-left: 0; }
  /* Полоса прототипа — две длинные фразы в четыре строки на 390 px. Мельче и
     плотнее: сказать она обязана всё, занимать первый экран — нет. */
  .proto { font-size: 12px; line-height: 1.35; }
  .proto__in { gap: var(--s-2); padding: 6px var(--gutter); }
  .t-h2 { font-size: 27px; }
  .t-h1 { font-size: 34px; }
  .t-hero, .hero__num b { font-size: 74px; }
  .hero { --edge-h: 220px; }
  .hero__in { padding: var(--s-5) 0 var(--s-4); }
  /* Ручной перенос в заголовке рассчитан на широкую строку. На 390 px он даёт
     третью строку из одного слова — перенос отдаётся браузеру. Перед самим
     переносом в разметке стоит пробел: без него скрытый br склеивал соседние
     слова в одно, и заголовок читался как опечатка. На широком экране этот
     пробел стоит в конце строки и схлопывается. */
  .hero__h br { display: none; }
  /* На телефоне число садится на левый край, под заголовок: правый край здесь
     не уравновешивает ничего, он просто край. */
  .hero__stamp .wrap { justify-content: flex-start; }
  .svc__defs { grid-template-columns: 1fr; }
  .denomrow { grid-template-columns: 1fr 1fr; }
  /* Многоколоночная таблица на телефоне не сжимается до нечитаемого: она
     прокручивается, а колонки держат собственную ширину. */
  .spectable--grid { min-width: 560px; }
  .tablehint {
    display: block; margin: var(--s-2) 0 0;
    font-size: 13px; line-height: 1.3; color: var(--metal-700);
  }
  .numlist li { padding-left: calc(var(--s-5) + var(--s-2)); }
  /* Вертикальная линия между измерениями на телефоне превращается в
     горизонтальную: четыре колонки по 80 px не читаются ни в каком кегле. */
  .facts__row, .ship__row { grid-template-columns: repeat(2, minmax(0,1fr)); gap: var(--s-4) 0; }
  .facts__c, .ship__c { padding-right: var(--s-4); }
  .facts__c + .facts__c { padding-left: var(--s-4); }
  .facts__c:nth-child(odd) { border-left: 0; padding-left: 0; }
  .ship__c + .ship__c { padding-left: var(--s-4); }
  .ship__c:nth-child(odd) { border-left: 0; padding-left: 0; }
  .facts__v { font-size: 42px; }
  /* Шаг вставок и накатки задан в пикселях, а не в долях ширины: доля дала бы
     на телефоне тот же рисунок, что на мониторе, то есть уменьшила бы масштаб
     кадра — и вместо макро получилась бы вся фишка. Шаг уменьшается вдвое,
     дуга остаётся прежней. */
  .hero__edge::before { background: repeating-linear-gradient(90deg,
    rgba(214,206,198,.16) 0 1px,
    var(--ink-900)        1px 44px,
    rgba(214,206,198,.16) 44px 45px,
    transparent           45px 250px); }
  .hero__edge::after { background: repeating-linear-gradient(90deg,
    rgba(0,0,0,.11) 0 3px, rgba(255,255,255,.03) 3px 4px, transparent 4px 10px); }
  .ladder { grid-template-columns: repeat(2, minmax(0,1fr)); }
}
@media (max-width: 479px) {
  :root { --gutter: 16px; }
  /* Ниже 480 плитка перестраивается в горизонтальную: сцена слева на 40 %.
     Условие на сцену обязательно: тем же классом .tile набраны текстовые
     карточки материалов и подразделов — заголовок плюс подпись, без предмета.
     Без условия заголовок уезжал в узкую колонку на 40 %, подпись в остаток,
     и на 390 px остаток был уже самого длинного слова: трек 1fr раздвигался
     под min-content и выносил строку на три пикселя за экран. Горизонтальной
     плитку делает сцена, а не класс. */
  .tiles, .tiles-4, .tiles-3, .tiles-2 { grid-template-columns: 1fr; }
  /* Текстовые карточки — по одной в ряд. На двух в ряд им остаётся по 169 px,
     и подпись рассыпается на слова по строке. */
  .four { grid-template-columns: 1fr; }
  .tile:has(.tile__scene) {
    display: grid; grid-template-columns: 40% 1fr;
    gap: var(--s-2) var(--s-3); align-content: start;
  }
  /* Сцена занимает первую колонку целиком, весь текст идёт во вторую. Без этих
     двух строк пять детей плитки раскладывались автопотоком по две штуки в ряд:
     сцена и название, потом характеристики и цена, потом срок — то есть цена
     оказывалась в колонке сцены, под предметом. Число строк названо явно, потому
     что разметку плитки печатает одна функция с постоянным составом детей. */
  .tile:has(.tile__scene) > .tile__scene { grid-column: 1; grid-row: 1 / span 4; }
  /* Текстовая колонка должна уметь стать уже своего длинного слова, иначе
     она распирает плитку за край экрана. */
  .tile:has(.tile__scene) > :not(.tile__scene) { grid-column: 2; min-width: 0; }
  .tile__line { display: none; }
  .tile__name { margin-top: 0; font-size: 17px; }
  .scene { aspect-ratio: 4 / 5; }
  .pdp__thumbs { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  .ftr__cols { grid-template-columns: 1fr; }
}

@media (prefers-reduced-motion: reduce) {
  * { animation-duration: 0ms !important; transition-duration: 0ms !important; }
}
