/*
 * Motrax — разделы страницы.
 *
 * Мотив один на всю страницу: красная вертикаль у древка флага стала
 * конструктивной осью, вдоль которой выстроены моноширинные метки.
 * Спецификации набраны как в паспорте продукта, цифры табличные.
 */

/* --- Служебное ------------------------------------------------------- */

.mx-skip {
  position: absolute;
  inset-inline-start: -9999px;
  z-index: 100;
}
.mx-skip:focus {
  inset-inline-start: var(--gut);
  inset-block-start: var(--s2);
  padding: 12px 18px;
  background: var(--accent);
  color: var(--on-accent);
}

/* --- Шапка ----------------------------------------------------------- */

/*
 * Шапка лежит поверх страницы, а не в её потоке.
 *
 * Со sticky она занимала место, и сжатие при прокрутке вычитало
 * восемнадцать пикселей из высоты — вся страница дёргалась вверх
 * ровно в тот момент, когда человек её листает. Место под шапку
 * теперь держит отступ у main: он постоянный, и сжиматься шапка
 * может сколько угодно.
 */
.mx-header {
  position: fixed;
  inset-block-start: 0;
  inset-inline: 0;
  z-index: 50;
  background: color-mix(in srgb, var(--ground) 88%, transparent);
  backdrop-filter: blur(14px);
  border-block-end: 1px solid transparent;
  transition: border-color .3s var(--ease), background .3s var(--ease);
}
.mx-header.is-stuck { border-block-end-color: var(--line-soft); }

/*
 * Шапка сжимается на ходу. Второй порог, а не тот же, что у полоски:
 * полоска нужна сразу, как только под шапку что-то заехало, а сжатие
 * на восьми пикселях выглядело бы дрожанием.
 */
.mx-main { padding-block-start: var(--head); }

.mx-header__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s4);
  min-block-size: var(--head);
  transition: min-block-size .35s var(--ease);
}
.mx-header.is-small .mx-header__inner { min-block-size: var(--head-small); }
.mx-header.is-small .mx-logo__mark { block-size: 17px; }
.mx-header.is-small .mx-header__cta { padding-block: 10px; }

.mx-logo { display: inline-flex; align-items: center; gap: 10px; flex: none; }
.mx-logo__mark {
  inline-size: 14px;
  block-size: 22px;
  transition: block-size .35s var(--ease);
  background: var(--accent);
  border-radius: 1px;
  flex: none;
}
.mx-logo__word {
  font-family: var(--display);
  font-weight: 700;
  font-size: 1.15rem;
  letter-spacing: -.03em;
  font-variation-settings: 'wdth' var(--wide);
}
.mx-logo--lg .mx-logo__word { font-size: clamp(1.6rem, 1.2rem + 1.4vw, 2.4rem); }
.mx-logo--lg .mx-logo__mark { inline-size: 18px; block-size: 30px; }

.mx-nav { display: flex; gap: var(--s4); }
.mx-nav a {
  font-family: var(--mono);
  font-size: var(--t-mono);
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--dim);
  transition: color .2s var(--ease);
}
.mx-nav a:hover { color: var(--ink); }

/* Никаких автоотступов и соседских селекторов: display:none убирает
   меню с экрана, но не из дерева, и правило вида «.mx-nav + …» продолжало
   бы срабатывать. Раскладку держит space-between у родителя. */
.mx-header__side { display: flex; align-items: center; gap: var(--s3); }
.mx-header__cta { padding: 13px 20px; transition: padding-block .35s var(--ease); }

/* --- Переключатель схемы --------------------------------------------- */

.mx-toggle { display: inline-flex; align-items: center; gap: 10px; }
/*
 * Зелёный флага живёт в интерфейсе там, где он что-то значит. Здесь это
 * «включено и работает»: бегунок — единственный постоянно видимый элемент
 * управления на странице, и зелёный на нём читается как исправность,
 * а не как украшение.
 */
.mx-toggle__track {
  inline-size: 46px;
  block-size: 26px;
  border: 1px solid var(--line);
  border-radius: 999px;
  position: relative;
  flex: none;
  transition: border-color .25s var(--ease), background .25s var(--ease);
}
.mx-toggle__knob {
  position: absolute;
  inset-block-start: 2px;
  inset-inline-start: 2px;
  inline-size: 20px;
  block-size: 20px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  background: var(--ok);
  color: var(--ground);
  transition: transform .32s var(--ease), background .25s var(--ease);
}
.mx-toggle__knob svg { inline-size: 12px; block-size: 12px; }
.mx-toggle[data-state="light"] .mx-toggle__knob { transform: translateX(20px); }
.mx-toggle:hover .mx-toggle__track { border-color: var(--ok); background: color-mix(in srgb, var(--ok) 12%, transparent); }

/* Внутри бегунка всегда одна иконка — та, что описывает текущую схему. */
.mx-toggle__sun { display: none; }
.mx-toggle[data-state="light"] .mx-toggle__sun { display: block; }
.mx-toggle[data-state="light"] .mx-toggle__moon { display: none; }
.mx-toggle__text {
  font-family: var(--mono);
  font-size: var(--t-mono);
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--dim);
  min-inline-size: 4.5em;
  text-align: start;
}

/* --- Бургер и мобильное меню ------------------------------------------ */

/*
 * Кнопка меню.
 *
 * Сорок на сорок — это палец, а сам знак вдвое уже и сидит в середине.
 * Поэтому коробка кнопки, выровненная по полю обёртки, ставила знак
 * на десять пикселей глубже, чем логотип слева: справа поле выглядело
 * шире, хотя числа совпадали. Вытягиваем кнопку наружу ровно на этот
 * отступ — знак встаёт на одну вертикаль с логотипом, а палец своего
 * не теряет.
 *
 * Отступ живёт одним числом и тратится дважды: им же отбиты полоски
 * внутри кнопки. Разъехаться двум значениям негде.
 */
.mx-burger {
  --burger-inset: 10px;
  display: none;
  inline-size: 40px;
  block-size: 40px;
  position: relative;
  margin-inline-end: calc(-1 * var(--burger-inset));
}
.mx-burger span {
  position: absolute;
  inset-inline: var(--burger-inset);
  block-size: 1px;
  background: var(--ink);
  transition: transform .3s var(--ease), opacity .2s var(--ease);
}
.mx-burger span:first-child { inset-block-start: 16px; }
.mx-burger span:last-child { inset-block-start: 23px; }
.mx-burger[aria-expanded="true"] span:first-child { transform: translateY(3.5px) rotate(45deg); }
.mx-burger[aria-expanded="true"] span:last-child { transform: translateY(-3.5px) rotate(-45deg); }

.mx-menu { border-block-start: 1px solid var(--line-soft); background: var(--ground); }
.mx-menu nav { display: grid; gap: var(--s3); padding-block: var(--s4); }
.mx-menu a {
  font-family: var(--display);
  font-weight: 600;
  font-size: 1.4rem;
  letter-spacing: -.03em;
  font-variation-settings: 'wdth' var(--wide);
}

/* --- Первый экран ----------------------------------------------------- */

/* Верхнее поле первого экрана вдвое меньше нижнего: сверху уже стоит
   шапка, и её высота работает тем же воздухом. Формула та же, только
   с половинными числами — сокращение одинаково на всех ширинах. */
.mx-hero { position: relative; overflow: hidden; padding-block: clamp(36px, 4.5vw, 75px) var(--pad); }

/*
 * Красное свечение слева отсылает к полосе у древка флага,
 * не изображая её буквально.
 */
.mx-hero__field {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: radial-gradient(48vw 42vw at 8% 42%, color-mix(in srgb, var(--accent) 16%, transparent), transparent 62%);
}

/*
 * Точечная карта мира: компания работает по всему свету, и фон говорит
 * это без подписи. География настоящая — сетка разложена из Natural Earth
 * на сборке, а не нарисована на глаз.
 *
 * SVG подключён маской, а не картинкой: цвет точкам задаёт токен темы,
 * поэтому карта следует за светлой и тёмной схемой одним файлом вместо
 * двух. Прозрачность низкая намеренно — это фон, а не иллюстрация.
 */
.mx-hero__map {
  position: absolute;
  inset: 0;
  pointer-events: none;
  display: grid;
  place-items: center;
  overflow: hidden;
}

/* Пропорция ровно как у сетки точек: 130 столбцов на 48 строк. Только
   поэтому проценты координат попадают в нужные места карты. */
.mx-hero__mapinner {
  position: relative;
  inline-size: min(118%, 1600px);
  aspect-ratio: 130 / 48;
}
.mx-hero__mapinner::before {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--ink);
  -webkit-mask-image: url(../img/world-dots.svg);
  mask-image: url(../img/world-dots.svg);
  -webkit-mask-size: 100% 100%;
  mask-size: 100% 100%;
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  opacity: .085;
}
:root[data-theme="light"] .mx-hero__mapinner::before { opacity: .07; }

/*
 * Вспышки поставок. Порты настоящие, координаты посчитаны из долготы
 * и широты. Загораются по очереди: ушла партия — дошла. Это не украшение,
 * а то, чем компания занимается, показанное без единого слова.
 *
 * Размер точки равен ячейке сетки (сотая от ста тридцати), чтобы вспышка
 * села на карту, а не поверх неё.
 */
.mx-ping {
  position: absolute;
  inset-inline-start: var(--x);
  inset-block-start: var(--y);
  inline-size: 1.15%;
  aspect-ratio: 1;
  translate: -50% -50%;
  border-radius: 1px;
  background: var(--accent);
  opacity: 0;
  animation: mxPing 12s var(--d, 0s) infinite;
}

/* Порт отгрузки горит всегда: из него всё и уходит. */
.mx-ping--home {
  animation: mxHome 4s infinite;
  box-shadow: 0 0 0 1px color-mix(in srgb, var(--accent) 45%, transparent);
}

@keyframes mxPing {
  0%   { opacity: 0; transform: scale(.5); box-shadow: 0 0 0 0 transparent; }
  3%   { opacity: 1; transform: scale(1); box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent) 40%, transparent); }
  9%   { opacity: .9; transform: scale(1); box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 16%, transparent); }
  17%  { opacity: 0; transform: scale(2.4); box-shadow: 0 0 0 5px transparent; }
  100% { opacity: 0; transform: scale(.5); box-shadow: 0 0 0 0 transparent; }
}

@keyframes mxHome {
  0%, 100% { opacity: .95; transform: scale(1); }
  50%      { opacity: .55; transform: scale(.8); }
}

/* При отключённом движении вспышки просто стоят на местах: карта
   остаётся осмысленной, мигания нет. */
@media (prefers-reduced-motion: reduce) {
  .mx-ping { animation: none; opacity: .8; }
}

/* На телефоне карта целиком превращается в пятно. Показываем её
   середину крупнее: там же и большинство портов назначения. */
@media (max-width: 760px) {
  .mx-hero__mapinner { inline-size: 230%; }
  .mx-ping { inline-size: .8%; }
}

/* --- Первый экран: содержимое ---------------------------------------- */

.mx-hero__inner { position: relative; }

.mx-hero__eyebrow { margin-block-end: var(--s4); }

.mx-hero__title {
  font-size: var(--t-h1);
  max-inline-size: 16ch;
  margin-block-end: var(--s5);
}

.mx-hero__row {
  display: grid;
  grid-template-columns: minmax(0, 1.1fr) minmax(0, .9fr);
  gap: var(--s5) var(--gut);
  align-items: end;
  padding-block-end: var(--s6);
}
.mx-hero__lede { font-size: var(--t-lead); color: var(--ink-soft); max-inline-size: 46ch; }
.mx-hero__cta { display: flex; flex-wrap: wrap; gap: var(--s2); justify-self: start; }

.mx-figures {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(200px, 100%), 1fr));
  gap: var(--s4);
  padding-block-start: var(--s4);
  border-block-start: 1px solid var(--line);
}
.mx-figures dt {
  font-family: var(--display);
  font-weight: 500;
  font-size: var(--t-figure);
  line-height: 1;
  letter-spacing: -.045em;
  font-variation-settings: 'wdth' var(--wide);
}
.mx-figures dd { margin-block-start: var(--s1); }

/* --- Заставка первого захода ------------------------------------------
 *
 * Карта прорастает из Джебель-Али: круглая обрезка расходится от точки
 * отгрузки наружу, и за секунду видно, что это не декорация, а карта
 * поставок. Дальше карта отступает в фон, вперёд выходит знак, и весь
 * слой уходит — открывая первый экран, где лежит та же самая карта.
 *
 * Два занавеса вместо одного было бы ошибкой: если сильно затемнить,
 * а потом показать и без того тёмный сайт, глаз прочитает два разных
 * открытия. Поэтому затемнения как такового нет — карта гаснет, знак
 * разгорается, и это одно движение, а не два.
 *
 * Время целиком здесь: сценарий ждёт события о конце ухода, а не
 * считает миллисекунды у себя. Двум числам разойтись негде.
 */
.mx-intro {
  position: fixed;
  inset: 0;
  z-index: 200;
  background: var(--ground);
  display: grid;
  place-items: center;
  overflow: hidden;
  animation: mx-intro-out .7s var(--ease) 6.9s forwards;
}

/*
 * Пропорция ровно как у сетки точек — 130 столбцов на 48 строк — и
 * ширина та же, что у карты первого экрана. Только при этих двух
 * условиях проценты координат попадают в нужные места, а уход слоя
 * читается как продолжение, а не как смена картинки.
 */
.mx-intro__stage {
  position: absolute;
  inline-size: min(118%, 1600px);
  aspect-ratio: 130 / 48;
  animation: mx-intro-dim .7s var(--ease) 5.2s forwards;
}

/*
 * Точки загораются по одной, волной от Джебель-Али.
 *
 * Оживлять каждую нельзя: их тысяча девятьсот тридцать пять, и маска
 * отдельных точек не различает — она берёт из файла только альфа-канал.
 * Поэтому поверх маски с точками кладётся вторая, круговой градиент
 * с широким размытым краем, и слои пересекаются. Точка видна ровно
 * настолько, насколько до неё дошёл градиент, а поскольку каждая стоит
 * на своём расстоянии от Джебель-Али — загораются они друг за другом.
 * Волна вместо шторки, одним элементом и без единого лишнего узла.
 *
 * Композит задаётся верхнему слою, а не нижнему: он описывает, как слой
 * складывается с тем, что под ним. С обратным порядком получается
 * объединение, и внутри круга выходит сплошная заливка вместо точек.
 *
 * Радиус — зарегистрированная переменная: без @property обычная
 * переменная в анимации переключается скачком на середине. Там, где
 * регистрации нет, так и произойдёт: карта появится разом. Хуже,
 * но не сломано, и объявленное значение оставляет её видимой.
 */
.mx-intro__map {
  --mx-r: 95%;

  position: absolute;
  inset: 0;
  background: var(--ink);

  -webkit-mask-image:
    url(../img/world-dots.svg),
    radial-gradient(circle at 65.29% 39.55%, #000 var(--mx-r), transparent calc(var(--mx-r) + 26%));
  mask-image:
    url(../img/world-dots.svg),
    radial-gradient(circle at 65.29% 39.55%, #000 var(--mx-r), transparent calc(var(--mx-r) + 26%));
  -webkit-mask-size: 100% 100%, 100% 100%;
  mask-size: 100% 100%, 100% 100%;
  -webkit-mask-repeat: no-repeat, no-repeat;
  mask-repeat: no-repeat, no-repeat;
  -webkit-mask-composite: source-in, source-over;
  mask-composite: intersect, add;

  /* Ярче, чем в первом экране: там карта фон, здесь она сюжет. */
  opacity: .2;
  /* Почти равномерно и с мягким торможением в конце. Резкое замедление
     съедало бы всю волну в первой трети времени. Небольшая задержка —
     чтобы сперва прочиталась точка, из которой всё растёт. */
  animation: mx-intro-grow 5.2s cubic-bezier(.25, .25, .5, 1) .2s forwards;
}

/* Точка отгрузки. Она же начало координат для роста карты — проценты
   взяты из долготы и широты Джебель-Али, как и вспышки в первом экране. */
.mx-intro__ping {
  position: absolute;
  inset-inline-start: 65.29%;
  inset-block-start: 39.55%;
  inline-size: 8px;
  block-size: 8px;
  margin: -4px 0 0 -4px;
  background: var(--accent);
  box-shadow: 0 0 0 0 color-mix(in srgb, var(--accent) 60%, transparent);
  animation: mx-intro-ping 1.7s var(--ease) 3;
}

.mx-intro__mark {
  position: relative;
  display: flex;
  align-items: center;
  gap: 14px;
  opacity: 0;
  transform: translateY(12px);
  animation: mx-intro-mark .7s var(--ease) 5.2s forwards;
}
.mx-intro__bar {
  inline-size: 18px;
  block-size: 30px;
  background: var(--accent);
  border-radius: 1px;
}
.mx-intro__word {
  font-family: var(--display);
  font-weight: 700;
  font-size: clamp(2rem, 1.4rem + 1.6vw, 2.8rem);
  letter-spacing: -.03em;
  font-variation-settings: 'wdth' var(--wide);
  color: var(--ink);
}

/*
 * 95% — не запас, а расчёт: от Джебель-Али до дальнего угла сетки ровно
 * столько и есть. Начало отрицательное на ширину размытия, иначе первый
 * кадр показал бы половину волны уже зажжённой.
 */
@property --mx-r {
  syntax: '<percentage>';
  inherits: false;
  initial-value: -26%;
}
@keyframes mx-intro-grow {
  from { --mx-r: -26%; }
  to   { --mx-r: 95%; }
}
@keyframes mx-intro-dim { to { opacity: .3; } }
@keyframes mx-intro-mark { to { opacity: 1; transform: none; } }
@keyframes mx-intro-ping {
  0%   { box-shadow: 0 0 0 0 color-mix(in srgb, var(--accent) 60%, transparent); }
  70%  { box-shadow: 0 0 0 22px color-mix(in srgb, var(--accent) 0%, transparent); }
  100% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--accent) 0%, transparent); }
}
/* visibility в конце: слой перестаёт ловить нажатия ещё до того, как
   сценарий доберётся до его удаления. */
@keyframes mx-intro-out { to { opacity: 0; visibility: hidden; } }

/* --- Страница категории ------------------------------------------------
 *
 * Не первый экран главной в миниатюре. Там задача объяснить, кто мы;
 * здесь человек уже пришёл за конкретным, и ему нужен ответ на «что
 * именно у вас есть и по каким отраслям». Поэтому вместо картинки —
 * сводка числами и разрез по отраслям, а весь воздух отдан списку ниже.
 */
.mx-cathero {
  /* Сверху меньше: над секцией уже стоит шапка, и её высота работает
     тем же воздухом. Ровно как в первом экране главной. */
  padding-block-start: clamp(28px, 3.5vw, 56px);
  border-block-start: 0;
}
/* Без красной риски: на главной она отмечает начало раздела в длинной
   череде, здесь раздел один и отмечать нечего — вертикаль только
   отжимала текст вправо без причины. */
.mx-cathero__top { max-inline-size: 62ch; }

/*
 * Шар в правой половине первого экрана.
 *
 * Появляется только там, где для него есть место: на узком экране
 * правой половины нет вовсе, а двести тридцать семь килобайт плеера
 * ради картинки, которую не покажут, — плохая сделка. Порог тот же,
 * на котором страница вообще расходится на две колонки.
 *
 * Пока файл не разобран, узел пуст и ничего не занимает: подскок
 * раскладки на полсекунды заметнее, чем отсутствие картинки.
 */
/*
 * Шар на телефоне — фоном за текстом.
 *
 * Раньше его здесь просто не было, а плеер в двести тридцать семь
 * килобайт грузился всё равно: платили и не показывали. Теперь он
 * стоит по центру первого экрана, крупнее ширины окна и приглушённый,
 * — фактура, а не иллюстрация. Читаемость держит не затемнение поверх
 * него, а его же приглушённость: текст идёт слоем выше и ни во что
 * не упирается.
 *
 * Обрезка обязательна: шар шире окна, и без неё страница получила бы
 * горизонтальную прокрутку. clip, а не hidden — режет так же, области
 * прокрутки не создаёт.
 */
.mx-cathero { overflow: clip; }

/*
 * Угол, а не середина.
 *
 * Пробовали фоном по центру — и упёрлись в то, что анимация это
 * залитая сфера, а не линейный рисунок. Диск под текстом на тёмной
 * схеме тонет, на светлой становится серым кругом поверх лида,
 * и третьего положения у непрозрачной заливки нет.
 *
 * Поэтому шар уходит за правый нижний угол: на экран попадает дуга
 * с орбитами, а текст с ней нигде не пересекается. Того же рода
 * решение, что и на широком экране, — показать часть и обрезать
 * остальное.
 */
/*
 * Неподвижный кадр занимает то же место, что и плеер, и показывается
 * там, где плеера нет вовсе — на узком экране. Правила положения общие,
 * поэтому и живут одним списком: разъехаться двум шарам негде.
 */
.mx-cathero__art {
  position: absolute;
  inset-block-start: auto;
  inset-block-end: -34%;
  inset-inline-start: auto;
  inset-inline-end: -38%;
  inline-size: min(130%, 30rem);
  aspect-ratio: 1 / 1;
  pointer-events: none;
}

/*
 * Вложенный файл ведёт себя как обычный узел: растягиваем его на всю
 * коробку и обрезаем по ней. slice вместо meet — заполнить и срезать
 * лишнее, а не вписать с полями.
 */
.mx-cathero__art > svg {
  inline-size: 100%;
  block-size: 100%;
  display: block;
}
.mx-cathero__art { opacity: .55; }

/* Текст слоем выше шара. */
.mx-cathero__top { position: relative; z-index: 1; }

/*
 * Диск под шаром — только там, где шар главный объект.
 *
 * Он делает планету сплошной и тёмной в обеих схемах: внутри анимации
 * сфера собрана из слоёв darken и lighten, такой слой считается вместе
 * с тем, что под ним, и без подложки на песочном фоне выходил серый
 * шарик. Для главного объекта это обязательное лечение.
 *
 * Фоном за текстом он, наоборот, вредит: сплошная тёмная заливка либо
 * тонет на тёмной схеме, либо спорит с буквами на светлой, а третьего
 * не дано — она непрозрачная. Без него остаётся линейная графика:
 * материки, орбиты, метки. Они одного тона и читаются на обоих фонах
 * ровно так же, как весь остальной сайт.
 *
 * Размер и положение — с отрисовки: сфера занимает 52.5% ширины холста
 * и стоит в его середине. Берём с запасом на полпикселя, иначе по краю
 * остаётся светлый волосок.
 */
.mx-cathero__art > * { position: relative; }

.mx-cathero--figures { padding-block-start: 0; border-block-start: 0; }

@media (min-width: 901px) {
  /*
   * Шар вынут из потока и висит поверх.
   *
   * В колонке он был заперт её шириной, а главным здесь он и должен
   * быть: орбиты — сопровождение, и пусть заезжают под текст. Абсолютное
   * положение даёт ему расти независимо от того, сколько места осталось
   * от заголовка.
   *
   * overflow: clip, а не hidden: обрезает так же, но не создаёт
   * область прокрутки — иначе шар, вылезающий за правый край, добавил
   * бы странице горизонтальную полосу.
   *
   * pointer-events: none обязательны. Прозрачный холст поверх кнопок
   * перехватывал бы нажатия, и «Request a quote» переставала бы
   * работать в половине случаев — молча.
   */
  .mx-cathero { overflow: clip; }

  /*
   * Шар центрируется по середине текста слева, а не по середине раздела.
   * Разница видна сразу: при центрировании по разделу он уезжает вниз,
   * потому что раздел выше текста — высоту ему задаёт сам шар.
   *
   * Считает это раскладка, а не число в стилях. Сетка ставит текст
   * и пустой якорь в одну строку; align-content: start держит строку
   * в высоту текста у верхнего края, а align-items: center выравнивает
   * якорь по её середине. Высота текста зависит от ширины окна, длины
   * заголовка и числа строк лида — угадать её отступом нельзя.
   *
   * Высота разделу не задаётся. Пробовали задать её по размеру шара,
   * чтобы тот не обрезался снизу, — и получили под кнопками пустое
   * поле в треть экрана. Обрезанная орбита не потеря: заказчик прямо
   * разрешил сопровождению заезжать и выходить за край. Дыра в вёрстке
   * — потеря.
   */
  .mx-cathero__grid {
    position: relative;
    display: grid;
    grid-template-columns: minmax(0, 1fr) 0;
    align-content: start;
    align-items: center;
  }
  .mx-cathero__top { position: relative; z-index: 1; }

  /* Точка отсчёта: ни ширины, ни высоты — только положение. */
  .mx-cathero__anchor { position: relative; inline-size: 0; block-size: 0; }

  .mx-cathero__art {
    position: absolute;
    /*
     * Середина шара приходится на середину текста слева и на правый
     * край обёртки — то есть на третью колонку сводки внизу.
     *
     * Считает это раскладка: строка ровно в высоту текста, половина
     * её высоты и есть середина. Прежде ту же роль играла пустая точка
     * в сетке; с картинкой она не нужна — от сетки до края обёртки
     * отсчёт такой же.
     *
     * Вправо шар почти не сдвинут: раньше он уезжал на четверть себя
     * и на широком мониторе оказывался наполовину за экраном. Орбиты
     * при этом всё равно выходят за обрез, и это в порядке.
     */
    inset-block-start: 50%;
    inset-inline-end: 0;
    inline-size: min(54rem, 58vw);
    aspect-ratio: 1 / 1;
    transform: translate(-2%, -50%);
    pointer-events: none;
    isolation: auto;
  }
  /* На широком экране шар главный объект страницы, а не фактура. */
}

/*
 * Снимок первого экрана.
 *
 * Раздел ростом в четыре пятых экрана.
 *
 * Не во весь: полный экран не оставляет и полоски следующего раздела,
 * и страница выглядит так, будто дальше ничего нет. Четыре пятых
 * показывают край следующего — это и есть приглашение листать,
 * которое не приходится рисовать стрелкой.
 *
 * min, а не ровно: экран бывает низким, шрифт крупным, а заголовок
 * длинным, и жёсткая высота в этом случае режет кнопки. Нижней
 * границей она держит меру, а вырасти сверх неё разрешает.
 *
 * svh, а не vh: на телефоне адресная строка уезжает и приезжает,
 * и вместе с ней скакала бы высота раздела. svh считает по меньшему
 * из двух состояний и потому не дёргается.
 */
.mx-cathero--cat {
  position: relative;
  min-block-size: 80svh;
  display: grid;
  align-content: center;
  /* Шапка лежит поверх страницы: без запаса крошки уедут под неё. */
  padding-block-start: calc(var(--head) + var(--s5));
  /* Снизу отступа нет: раздел заканчивается там же, где числа,
     и снимок — фон раздела — обрывается ровно по их нижней строке. */
  padding-block-end: 0;
  overflow: clip;
}

/*
 * Текст по центру свободного места, сводка прижата к низу.
 *
 * Строка 1fr забирает весь остаток высоты, и заголовок центруется
 * в нём; строка auto держит сводку у нижнего края. Отступом это
 * не задаётся: остаток зависит от высоты окна, а она разная.
 */
.mx-cathero--cat .mx-wrap {
  display: grid;
  grid-template-rows: 1fr auto;
  gap: var(--s5);
}
.mx-cathero--cat .mx-cathero__top { align-self: center; }

@media (max-width: 900px) {
  /*
   * На узком экране текст прижат к верху, а не центрирован.
   *
   * Раздел держит ту же долю высоты окна, но текста в него влезает
   * заметно меньше. На обычном телефоне разницы нет — содержимое
   * и так выше раздела, — а на длинном окне центрирование отодвигало
   * заголовок от шапки пустотой в четверть экрана.
   */
  .mx-cathero--cat .mx-cathero__top { align-self: start; }

  /*
   * Запас под шапкой — такой же, как на «О компании».
   *
   * В формуле была ошибка: к отступу прибавлялась высота шапки, хотя
   * раздел и так начинается под ней — она его не перекрывает. Высота
   * считалась дважды, и между шапкой и крошками выходило девяносто
   * пикселей пустоты вместо двадцати восьми.
   *
   * Значение то же, что у базового правила: пять страниц начинаются
   * одинаково, и объяснять разницу нечем.
   */
  .mx-cathero--cat { padding-block-start: clamp(28px, 3.5vw, 56px); }
}

/*
 * Своей подложки у чисел нет: их держит нижняя заслонка снимка.
 * Плашка под ними была бы прямоугольником внутри прямоугольника,
 * а читаемость и без неё та же — заслонка к низу выходит на сплошной
 * цвет фона.
 */
.mx-cathero__figures { border-block-start-color: var(--line-soft); }

/*
 * Снимок — фон раздела: подо всем и на всю ширину окна.
 *
 * Вылет за контейнер считается от самой раскладки: контейнер по центру,
 * значит от его края до края окна ровно половина разницы их ширин.
 * 50% здесь — половина ширины раздела, 50vw — половина окна; их
 * разность и есть вылет, какой бы ширины ни было окно.
 */
.mx-cathero__shot {
  position: absolute;
  inset-block: 0;
  inset-inline: calc(50% - 50vw);
  margin: 0;
  z-index: 0;
}

@media (min-width: 901px) {
  /*
   * Коробка кадра равна самому кадру, а не разделу.
   *
   * Так пришлось сделать из-за шва. Пока кадр вписывался по высоте
   * внутрь коробки во весь раздел, слева от него оставалось пустое
   * место, и заслонка в этом месте была уже полупрозрачной: на тёмной
   * схеме разницы не видно, а на светлой левый край фотографии
   * читался вертикальной линией.
   *
   * Теперь коробка ровно по кадру и прижата к правому краю окна,
   * а заслонка лежит на ней — значит её сплошная зона всегда
   * приходится на левую часть самой фотографии, при любой ширине
   * окна. Слева от коробки просто фон страницы, и граничить ему
   * не с чем.
   *
   * Ширина выводится из высоты пропорцией: высоту задают inset-block,
   * дальше считает aspect-ratio.
   */
  .mx-cathero__shot {
    inset-inline-start: auto;
    inline-size: auto;
    aspect-ratio: 1600 / 1067;

    /*
     * Коробка выше раздела, и лишнее обрезается его же overflow.
     *
     * Иначе кадр выходит мелким: раздел широкий и низкий, снимок
     * вытянут иначе, поэтому вписанный целиком он упирается в высоту
     * и занимает едва половину полосы. Целиком и крупно одновременно
     * не получится, пока полоса шире кадра по пропорции, — и здесь
     * выбрано крупно.
     *
     * Прижат к низу: срезается верх с потолком и лампой, то есть
     * наименее осмысленная часть кадра. Пол и ряд бочек остаются.
     *
     * Число здесь одно и работает ручкой: 100% — кадр целиком,
     * 180% — почти как cover без полей.
     */
    inset-block-start: auto;
    block-size: 120%;
  }
}
.mx-cathero__shot img {
  inline-size: 100%;
  block-size: 100%;
  /*
   * Режим наложения: снимок считается вместе с фоном страницы,
   * а не лежит поверх него отдельной картинкой.
   *
   * luminosity берёт от снимка светлоту, а цвет — у фона, и кадр
   * уходит в фирменный тон сам. Оборотная сторона известна: такой
   * слой зависит от того, что под ним, а под ним фон, меняющийся
   * по тоглу, — значит и вид снимка в двух схемах будет разным.
   * Здесь это то, чего и добиваются.
   */
  mix-blend-mode: luminosity;
  /* На узком экране кадр работает фактурой на весь раздел, и обрезка
     по краям здесь уместнее пустых полей сверху и снизу. */
  object-fit: cover;
  object-position: 72% 50%;
}

@media (min-width: 901px) {
  /* Коробка уже равна пропорции кадра, поэтому обрезать нечего:
     cover здесь просто заполняет её без искажений. */
  .mx-cathero__shot img { object-position: center; }
}

/*
 * Заслонка под текстом.
 *
 * Не затемнение поверх фотографии, а сам фон страницы: слева он
 * закрывает снимок целиком и растворяется к середине. Цвет берётся
 * из токена, поэтому на светлой схеме заслонка песочная, на тёмной
 * почти чёрная, и подгонять под тогл нечего — в отличие от затемнения,
 * которое пришлось бы держать двумя разными.
 *
 * Три остановки, а не две: одна сплошная зона под колонкой текста,
 * и только потом растворение. С двумя градиент начинал редеть прямо
 * под заголовком, и по светлым бочкам буквы теряли край.
 *
 * Растворение задано в пикселях, а не в долях. Доля считается от ширины
 * коробки, а коробка теперь равна кадру и меняет размер вместе с высотой
 * окна: на широком мониторе тридцать процентов съедали треть склада.
 * Расстояние же нужно ровно одно — столько, сколько занимает колонка
 * текста, и оно от размеров окна не зависит.
 *
 * Заслонок две, слева и снизу, и нижняя работает так же. Она держит
 * числа: они стоят у нижнего края поверх кадра, и без неё им нужна
 * была бы собственная плашка — то есть прямоугольник внутри
 * прямоугольника. Заодно уходит нижняя граница снимка: раздел
 * не обрывается линией, а растворяется в странице.
 *
 * to right и to bottom, а не логические направления: у градиентов
 * их нет. Для зеркальной версии правило придётся продублировать
 * под [dir=rtl].
 */
.mx-cathero__shot::after {
  content: "";
  position: absolute;
  inset: 0;
  background:
    linear-gradient(to bottom,
      transparent 38%,
      color-mix(in srgb, var(--ground) 55%, transparent) 62%,
      color-mix(in srgb, var(--ground) 92%, transparent) 84%,
      var(--ground) 100%),
    linear-gradient(to right,
      var(--ground) 0,
      var(--ground) 140px,
      color-mix(in srgb, var(--ground) 62%, transparent) 320px,
      transparent 560px);
}

@media (max-width: 900px) {
  /*
   * На узком экране колонки нет — текст идёт поверх снимка целиком,
   * поэтому и заслонка сплошная почти по всей площади. Снимок
   * остаётся фактурой, а не иллюстрацией, и это честнее, чем читать
   * заголовок по светлым бочкам.
   */
  .mx-cathero__shot::after {
    background: linear-gradient(to bottom,
      var(--ground) 0%,
      color-mix(in srgb, var(--ground) 88%, transparent) 55%,
      color-mix(in srgb, var(--ground) 78%, transparent) 100%);
  }
}

/* Текст поверх снимка и заслонки. */
.mx-cathero--cat .mx-wrap { position: relative; z-index: 1; }

/*
 * Точечная лестница фасовок — запасной вариант, оставлен на случай,
 * если снимки не сойдутся. Ни одна категория её сейчас не просит,
 * поэтому и не рисуется ничего.
 */
.mx-cathero__packs { display: none; }

@media (min-width: 901px) {
  /*
   * Два слоя одной фигуры. Нижний — точечная фактура: сетку рисует
   * повторяющийся градиент, маска решает, где ей быть. Верхний —
   * контур, он держит силуэт, который фактура иначе съедает.
   *
   * Шаг сетки в пикселях, а не в долях: фактура обязана остаться
   * одинаковой на любом размере графики. Доли растянули бы точки
   * вместе с картинкой, и на широком экране вместо экрана из точек
   * вышли бы крупные кругляши.
   */
  /*
   * Размер берётся от высоты строки, а не от ширины окна.
   *
   * По ширине график перерастал первый экран и упирался в шапку —
   * бочке срезало верх, и это читалось как ошибка вёрстки, а не как
   * приём. Строка же ровно в высоту текста слева, поэтому график
   * встаёт от крошек до нижнего ряда кнопок и остаётся в этих
   * границах при любой длине заголовка.
   *
   * Ширина выводится из высоты пропорцией, поэтому задавать её
   * не нужно вовсе.
   */
  .mx-cathero__packs {
    display: block;
    position: absolute;
    inset-block: 0;
    inset-inline-end: 0;
    block-size: 100%;
    inline-size: auto;
    aspect-ratio: 128 / 90;
    pointer-events: none;
  }
  .mx-cathero__packs::before,
  .mx-cathero__packs::after {
    content: "";
    position: absolute;
    inset: 0;
    -webkit-mask-size: contain;
    mask-size: contain;
    -webkit-mask-repeat: no-repeat;
    mask-repeat: no-repeat;
    -webkit-mask-position: center;
    mask-position: center;
  }
  .mx-cathero__packs::before {
    color: var(--ink);
    background-image: radial-gradient(currentColor 30%, transparent 32%);
    background-size: 11px 11px;
    /* Фактура приглушена, контур нет: так читается рисунок, а не пятно. */
    opacity: .34;
    -webkit-mask-image: var(--fill);
    mask-image: var(--fill);
  }
  .mx-cathero__packs::after {
    background: var(--ink);
    opacity: .5;
    -webkit-mask-image: var(--line);
    mask-image: var(--line);
  }
}

/* Хлебные крошки строкой метки: путь короткий, отдельный блок под него
   был бы тяжелее самого пути. */
.mx-cathero__top > .mx-label { margin-block-end: var(--s3); }
.mx-cathero__top > .mx-label a { color: var(--dim); }
.mx-cathero__top > .mx-label a:hover { color: var(--ink); }

.mx-cathero__title {
  /* На ступень мельче заголовка главной: это раздел, а не сайт. */
  font-size: clamp(2.2rem, 1.5rem + 3vw, 4rem);
  line-height: 1.02;
  letter-spacing: -.03em;
  margin-block-end: var(--s4);
}
/*
 * Лид набирается обычным кеглем, а не увеличенным, как на главной.
 * Там он единственный текст на экране и держит паузу после заголовка;
 * здесь рядом стоит шар, и два крупных элемента спорят за внимание.
 * Проигрывает тот, что словами.
 */
.mx-cathero__lede {
  font-size: var(--t-body);
  color: var(--ink-soft);
  max-inline-size: 52ch;
}
.mx-cathero__cta {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s2);
  margin-block-start: var(--s5);
}
/*
 * Разрез по отраслям — отдельный раздел под первым экраном.
 *
 * Верхней линии у него нет: раздел выше заканчивается снимком во всю
 * ширину, и черта под фотографией читалась бы как её рамка.
 */
.mx-cathero__facts { border-block-start: 0; padding-block-end: 0; }
/* В этом разделе разрез идёт первым, и отступ ему даёт сам раздел. */
.mx-cathero__facts .mx-cathero__dirs { margin-block-start: 0; }

/*
 * Разрез по отраслям: подписи с числом, а не фильтр.
 *
 * Отступ сверху крупный, снизу почти никакого — и это не про красоту,
 * а про принадлежность. Блок описывает набор брендов, а не сводку
 * чисел над ним; при равных отступах он читался как подпись к числам,
 * потому что расстояние — единственное, чем вёрстка сообщает «это
 * вместе». Дальше от чисел, вплотную к стене — и вопрос снят.
 */
.mx-cathero__dirs {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-block-start: var(--s7);
}
.mx-cathero__dirs .mx-num {
  margin-inline-start: 8px;
  color: var(--accent-ink);
}
.mx-cathero__note {
  margin-block-start: var(--s3);
  color: var(--dim);
  max-inline-size: 62ch;
}

/*
 * Стена на странице категории показана целиком и без вкладок:
 * переключать не на что, мы уже внутри категории. Видимостью здесь
 * распоряжается режим, а не спрятанные радиокнопки, которых тут нет.
 */
.mx-brands--full .mx-brands__grid { display: grid; }
/* Шапка категории заканчивается разрезом по отраслям, а он принадлежит
   стене. Поэтому между ними ровно столько воздуха, сколько нужно, чтобы
   строки не слиплись, — и ни шагом больше. */
.mx-brands--full { border-block-start: 0; padding-block-start: var(--s4); }

/*
 * Поиск по стене.
 *
 * Поля нет в разметке — его ставит сценарий, поэтому и правила здесь
 * описывают то, чего без сценария не существует. Ничего не ломается:
 * нет узла — нет и оформления.
 */
.mx-wallfind {
  display: grid;
  gap: var(--s2);
  margin-block-end: var(--s4);
  max-inline-size: 52ch;
}
.mx-wallfind__label { color: var(--dim); }
.mx-wallfind__box {
  display: flex;
  align-items: stretch;
  gap: 1px;
  background: var(--line-soft);
  border: 1px solid var(--line-soft);
}
.mx-wallfind__input {
  flex: 1;
  min-inline-size: 0;
  background: var(--ground);
  border: 0;
  color: var(--ink);
  font: inherit;
  padding: 14px var(--s2);
}
/*
 * Подсказка внутри поля набрана тем же кеглем, что и ввод, но приглушена.
 * Уменьшать её нельзя: текст в поле подпрыгивал бы в размере с первой
 * набранной буквой.
 */
.mx-wallfind__input::placeholder { color: var(--dim); opacity: 1; }
/* Родную крестик-кнопку убираем: своя стоит рядом, две подряд — это
   вопрос «а они делают разное?», на который нечего ответить. */
.mx-wallfind__input::-webkit-search-cancel-button { display: none; }
.mx-wallfind__input:focus { outline: 0; }
/* Кольцо рисует коробка целиком, а не поле внутри неё: обводка по краю
   ввода оставляла бы кнопку за пределами подсвеченной области. */
.mx-wallfind__box:focus-within { border-color: var(--accent); }

.mx-wallfind__clear {
  background: var(--ground);
  border: 0;
  color: var(--dim);
  font-family: var(--mono);
  font-size: var(--t-mono);
  letter-spacing: .18em;
  text-transform: uppercase;
  padding-inline: var(--s3);
  cursor: pointer;
  transition: color .2s var(--ease), background .2s var(--ease);
}
.mx-wallfind__clear:hover,
.mx-wallfind__clear:focus-visible { color: var(--ink); background: var(--surface); }

/*
 * Лупа у правого края поля.
 *
 * Красная — это единственный акцент сайта, и здесь он на месте: знак
 * должен ловиться взглядом раньше подписи, иначе он не нужен вовсе.
 *
 * Своим фоном, как и кнопка рядом: коробка поля собрана из зазоров
 * в один пиксель на общей подложке, и прозрачная ячейка показала бы
 * линию насквозь.
 */
.mx-wallfind__icon {
  display: grid;
  place-items: center;
  padding-inline: var(--s3);
  background: var(--ground);
  color: var(--accent-ink);
}
.mx-wallfind__icon svg { inline-size: 20px; block-size: 20px; display: block; }

.mx-wallfind__count { color: var(--accent-ink); min-block-size: 1em; }

.mx-wallfind__empty {
  display: grid;
  justify-items: start;
  gap: var(--s3);
  margin-block-start: var(--s3);
}
.mx-wallfind__empty-text { color: var(--ink-soft); max-inline-size: 62ch; }

/*
 * Пока идёт отбор, сетка перестаёт быть сплошной таблицей.
 *
 * Линии между ячейками рисует не бордюр, а фон сетки, проступающий
 * в однопиксельных зазорах. Приём хорош ровно до тех пор, пока ячейки
 * заполняют ряд: при одной найденной марке тот же фон рисует светлый
 * прямоугольник на три четверти ширины — обводку вокруг пустоты.
 *
 * Поэтому на время отбора линию берёт на себя каждая ячейка, и берёт
 * тенью, а не бордюром: тень не занимает места, поэтому ряд не съезжает
 * на пиксель при первом же нажатии клавиши. Соседние тени ложатся в тот
 * же зазор и дают ту же однопиксельную линию, что и раньше.
 */
.mx-brands.is-finding .mx-brands__grid { background: transparent; border-color: transparent; }
.mx-brands.is-finding .mx-brand { box-shadow: 0 0 0 1px var(--line-soft); }

/* Обычная страница без категории: «О компании» и правовые тексты. */
.mx-page { max-inline-size: 72ch; }
.mx-page h2 { margin-block: var(--s5) var(--s2); }
.mx-page p { margin-block-end: var(--s3); color: var(--ink-soft); }

/* --- О компании -------------------------------------------------------
 *
 * Страница отвечает на вопрос «можно ли вам верить», и вёрстка здесь
 * работает на то же: никаких карточек с тенями и иконок — перечни,
 * числа и подписи. Доверие строится на проверяемом, а не на нарядном.
 */
/*
 * Верхнего поля нет ни у одного раздела страницы: между двумя соседними
 * складывались нижнее поле первого и верхнее второго, и на сто сорок
 * пикселей воздуха выходило двести восемьдесят. Расстояние держит одно
 * нижнее поле, и оно же работает перед следующим разделом.
 */
.mx-about { border-block-start: 0; padding-block-start: 0; }

/*
 * Четыре работы — намеренно не такая же сетка, как у направлений
 * и у брендов.
 *
 * На странице и без того две сетки одинаковых ячеек в волосяной клетке;
 * третья превращала страницу в каталог клеток. Здесь ячейки разделены
 * воздухом, а не линиями, стоят по две в ряд и набраны крупнее:
 * четыре утверждения, а не четыре карточки.
 */
.mx-about__list {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(380px, 100%), 1fr));
  gap: var(--s5) var(--gut);
}
.mx-about__item {
  display: grid;
  align-content: start;
  gap: var(--s2);
  padding-block-start: var(--s3);
  border-block-start: 1px solid var(--line);
}
.mx-about__n {
  font-family: var(--display);
  font-weight: 700;
  font-size: clamp(1.8rem, 1.3rem + 1.6vw, 2.6rem);
  line-height: .9;
  letter-spacing: -.04em;
  color: var(--accent);
}
.mx-about__title { font-size: var(--t-h3); }
.mx-about__item .mx-about__title { font-size: clamp(1.25rem, 1.05rem + .7vw, 1.7rem); }
.mx-about__item .mx-about__note { font-size: var(--t-body); max-inline-size: 46ch; }
.mx-about__title a { border-block-end: 1px solid var(--line); }
.mx-about__title a:hover { color: var(--accent-ink); border-block-end-color: var(--accent); }
.mx-about__note { color: var(--dim); font-size: var(--t-small); line-height: 1.6; }

/* Три линии товара: ссылка на страницу категории, а не пересказ её.
   Список живёт в одном месте, копия здесь однажды разошлась бы. */
.mx-about__lines {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(280px, 100%), 1fr));
  gap: var(--s4);
}
/*
 * Красная черта сверху работает, пока строки стоят рядом: три коротких
 * штриха читаются как отметки трёх колонок, а не как разделители.
 */
.mx-about__line {
  display: grid;
  align-content: start;
  gap: var(--s2);
  padding-block-start: var(--s3);
  border-block-start: 2px solid var(--accent);
}

@media (max-width: 600px) {
  /*
   * Стопкой те же черты становятся тремя красными линиями во всю
   * ширину экрана — и акцент, который должен быть один на страницу,
   * превращается в разлиновку. Оставляем волосяную линию нейтральным
   * цветом: разделять она разделяет, кричать перестаёт.
   */
  .mx-about__line { border-block-start-width: 1px; border-block-start-color: var(--line); }
}

/* Формальная часть: пары «поле — значение». Моноширинная подпись
   и обычный набор значения — тот же приём, что в контактах. */
.mx-about__facts {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(240px, 100%), 1fr));
  gap: var(--s4);
}
.mx-about__facts > div { display: grid; gap: 6px; align-content: start; }
.mx-about__facts dd { color: var(--ink-soft); }

/* --- Направления ------------------------------------------------------ */

.mx-dirs__list {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(290px, 100%), 1fr));
  gap: 1px;
  background: var(--line-soft);
  border: 1px solid var(--line-soft);
}
.mx-dir {
  --card-bg: var(--ground);
  /* Поля карточки — переменными: снимок выходит за них отрицательными
     отступами, и стоит задать их числами, как на другом брейкпойнте
     он вылезет наружу. Ровно это и случилось на телефоне: поля были
     24, а вычиталось 32. */
  --pad-x: var(--s4);
  --pad-y: var(--s4);
  background: var(--card-bg);
  padding: var(--pad-y) var(--pad-x);
  display: grid;
  align-content: start;
  gap: var(--s2);
  position: relative;
  transition: background .3s var(--ease);
}

/*
 * Снимок выходит за поля карточки к самым её краям — отрицательными
 * отступами, потому что поля заданы на самой карточке, а не на обёртке
 * содержимого. Низ кадра растворяется в фоне карточки: иначе фотография
 * лежала бы отдельной плашкой поверх, а должна быть её частью.
 */
.mx-dir__shot {
  position: relative;
  /* Снизу отступа нет: за него отвечает зазор сетки карточки. Пока
     номер стоял отдельной строкой, нужны были оба, и вместе они давали
     ту самую пустую полосу под кадром. */
  margin: calc(var(--pad-y) * -1) calc(var(--pad-x) * -1) 0;
  /* Кадр не режем: композиция снята под 3:2, и обрезка до полосы
     съедала бы небо у трактора и верх двигателя. Высоту отыгрываем
     иначе — номером на самом снимке, а не строкой под ним. */
  aspect-ratio: 16 / 9;
  overflow: hidden;
  background: var(--raised);
}
.mx-dir__shot img {
  inline-size: 100%;
  block-size: 100%;
  object-fit: cover;
  /* Снимки и так тёмные, но лёгкое обесцвечивание убирает последние
     тёплые оттенки, которые спорили бы с единственным акцентом. */
  filter: grayscale(.3) brightness(.82) contrast(1.06);
  transition: transform .6s var(--ease), filter .4s var(--ease);
}
/*
 * Растворение низа кадра в фоне карточки работает только на тёмной
 * схеме: там фон и фотография одной светлоты, и шов не виден. На светлом
 * фоне тёмный кадр с растяжкой в белый читается как дыра — поэтому там
 * у снимка честный край и волосяная линия под ним.
 */
.mx-dir__shot::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(to bottom, transparent 45%, var(--card-bg));
}
:root[data-theme="light"] .mx-dir__shot::after { background: none; }
:root[data-theme="light"] .mx-dir__shot { border-block-end: 1px solid var(--line); }
.mx-dir:hover .mx-dir__shot img { transform: scale(1.035); filter: grayscale(.12) brightness(1) contrast(1.06); }
:root[data-theme="light"] .mx-dir__shot img { filter: grayscale(.15) brightness(.97) contrast(1.02); }
:root[data-theme="light"] .mx-dir:hover .mx-dir__shot img { filter: grayscale(0) brightness(1.04) contrast(1.02); }
/* Красная риска появляется сверху карточки — тот же приём, что у секций. */
.mx-dir::before {
  content: "";
  position: absolute;
  inset-block-start: 0;
  inset-inline: 0;
  block-size: 2px;
  background: var(--accent);
  transform: scaleX(0);
  transform-origin: inline-start;
  transition: transform .35s var(--ease);
}
.mx-dir:hover { --card-bg: var(--surface); }
.mx-dir:hover::before { transform: scaleX(1); }

/*
 * Номер лежит на нижнем краю кадра, а не строкой под ним: снаружи он
 * занимал отдельную строку сетки и вместе с зазором давал пустую полосу.
 * Заодно номер теперь принадлежит снимку, а не висит между ним и текстом.
 *
 * Цвет не берётся из темы: под номером всегда тёмная фотография,
 * в какой бы схеме ни смотрели, поэтому красный всегда осветлённый.
 */
.mx-dir__code {
  position: absolute;
  inset-block-end: 12px;
  inset-inline-start: var(--pad-x);
  z-index: 1;
  color: #FF6072;
  text-shadow: 0 1px 8px rgba(0, 0, 0, .7);
}
/*
 * Пагинация ленты. На широком экране все четыре карточки перед глазами,
 * и указатель положения там врал бы: показывать нечего. Точки появляются
 * вместе с каруселью, в её же брейкпойнте.
 */
.mx-dirs__dots { display: none; }
.mx-dot {
  display: grid;
  place-items: center;
  /* Ширина фиксированная, а растягивается полоска внутри: иначе строка
     точек дёргалась бы при каждом перелистывании. */
  inline-size: 34px;
  block-size: 44px;
  border: 0;
}
.mx-dot__bar {
  block-size: 3px;
  inline-size: 8px;
  border-radius: 2px;
  /* Линия сетки для точки слишком бледна: её задача — разделять, а не
     показываться. Берём приглушённый текст в половину силы: неактивная
     точка видна как цель, но с активной не спорит. */
  background: color-mix(in srgb, var(--dim) 50%, transparent);
  transition: inline-size .45s var(--ease), background-color .3s var(--ease);
}
.mx-dot:hover .mx-dot__bar { background: var(--dim); }
.mx-dot[aria-current="true"] .mx-dot__bar {
  inline-size: 26px;
  background: var(--accent);
}

.mx-dir__title { font-size: var(--t-h3); }
.mx-dir__note { color: var(--dim); font-size: var(--t-small); line-height: 1.6; }
.mx-dir__specs { display: flex; flex-wrap: wrap; gap: 6px; margin-block-start: var(--s2); }

/* --- Бренды ----------------------------------------------------------- */

/*
 * Переключатель наборов.
 *
 * Радиокнопка убрана с экрана, но не из дерева: она остаётся настоящей,
 * с ней работают стрелки, таб и экранный диктор. Видимую часть рисует
 * подпись, а состояние приходит через `:checked ~` — родной механизм,
 * который не может отложить ни один плагин кэша.
 */
.mx-brands__radio { position: absolute; opacity: 0; pointer-events: none; }

/*
 * Переключатель — главный орган управления разделом, и весить он должен
 * соответственно. Моноширинным в одиннадцать пикселей, как подписи полей,
 * он терялся среди них: глаз не отличал кнопку от служебной надписи.
 *
 * Поэтому здесь наборный шрифт и обычный кегль, а моноширинным осталось
 * только число — оно и правда данные. Подчёркивание снизу у выбранной
 * вкладки: тот же приём, что у красной риски разделов, и той же толщины.
 */
.mx-brands__modes {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s2);
  margin-block-end: var(--s4);
}
.mx-brands__mode {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 10px;
  /* Не меньше пальца: подпись здесь и есть кнопка. */
  min-block-size: 56px;
  padding: 14px 22px;
  border: 1px solid var(--line);
  font-family: var(--display);
  font-weight: 600;
  font-size: clamp(.95rem, .88rem + .25vw, 1.12rem);
  letter-spacing: -.01em;
  color: var(--ink-soft);
  cursor: pointer;
  transition: color .25s var(--ease), border-color .25s var(--ease), background .25s var(--ease);
}
.mx-brands__mode:hover { color: var(--ink); border-color: var(--ink-soft); }

/* Счётчик набора: сразу видно, что за переключателем не пусто.
   Моноширинный — это число, и набирается оно как число. */
.mx-brands__count {
  font-family: var(--mono);
  font-size: .78em;
  color: var(--dim);
  transition: color .25s var(--ease);
}

/* Риска у выбранной — тот же знак, которым размечены разделы. */
.mx-brands__mode::after {
  content: "";
  position: absolute;
  inset-inline: -1px;
  inset-block-end: -1px;
  block-size: 2px;
  background: var(--accent);
  transform: scaleX(0);
  transform-origin: inline-start;
  transition: transform .3s var(--ease);
}

/* Списки спрятаны все, кроме выбранного. Первый показан и правилом без
   условий тоже — если бы `:checked` вдруг не сработал, раздел остался бы
   с содержимым, а не пустым. */
.mx-brands__grid { display: none; }
.mx-brands__grid--1 { display: grid; }

#mx-bs-1:checked ~ .mx-brands__sets .mx-brands__grid--1,
#mx-bs-2:checked ~ .mx-brands__sets .mx-brands__grid--2,
#mx-bs-3:checked ~ .mx-brands__sets .mx-brands__grid--3 { display: grid; }

#mx-bs-2:checked ~ .mx-brands__sets .mx-brands__grid--1,
#mx-bs-3:checked ~ .mx-brands__sets .mx-brands__grid--1 { display: none; }

/* Строка «показано столько из стольких» живёт вместе со своей сеткой. */
.mx-brands__more { display: none; }
#mx-bs-1:checked ~ .mx-brands__sets .mx-brands__more--1,
#mx-bs-2:checked ~ .mx-brands__sets .mx-brands__more--2,
#mx-bs-3:checked ~ .mx-brands__sets .mx-brands__more--3 {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s2) var(--s3);
  margin-block-start: var(--s3);
}
.mx-brands__more .mx-label { color: var(--dim); }

#mx-bs-1:checked ~ .mx-brands__modes label[for="mx-bs-1"],
#mx-bs-2:checked ~ .mx-brands__modes label[for="mx-bs-2"],
#mx-bs-3:checked ~ .mx-brands__modes label[for="mx-bs-3"] {
  color: var(--ink);
  border-color: var(--line);
  background: var(--surface);
}
#mx-bs-1:checked ~ .mx-brands__modes label[for="mx-bs-1"]::after,
#mx-bs-2:checked ~ .mx-brands__modes label[for="mx-bs-2"]::after,
#mx-bs-3:checked ~ .mx-brands__modes label[for="mx-bs-3"]::after { transform: scaleX(1); }

#mx-bs-1:checked ~ .mx-brands__modes label[for="mx-bs-1"] .mx-brands__count,
#mx-bs-2:checked ~ .mx-brands__modes label[for="mx-bs-2"] .mx-brands__count,
#mx-bs-3:checked ~ .mx-brands__modes label[for="mx-bs-3"] .mx-brands__count { color: var(--accent-ink); }

#mx-bs-1:focus-visible ~ .mx-brands__modes label[for="mx-bs-1"],
#mx-bs-2:focus-visible ~ .mx-brands__modes label[for="mx-bs-2"],
#mx-bs-3:focus-visible ~ .mx-brands__modes label[for="mx-bs-3"] {
  outline: 2px solid var(--accent-ink);
  outline-offset: 2px;
}

/*
 * Четыре колонки на три ряда, а не шесть на два. Auto-fit сам набирал
 * по шесть и делал ячейку 200 пикселей шириной: логотип в ней выходил
 * с ноготь, а «Liqui Moly» и «TotalEnergies» ломались на две строки.
 * Двенадцать брендов делятся и так, и так — берём тот делитель,
 * при котором марку видно.
 */
.mx-brands__grid {
  /* display здесь не назначается намеренно: им распоряжается
     переключатель наборов выше по файлу, а это правило стоит ниже
     и вернуло бы видимость всем трём сеткам разом. */
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 1px;
  background: var(--line-soft);
  border: 1px solid var(--line-soft);
}
.mx-brand {
  background: var(--ground);
  min-block-size: 168px;
  padding: var(--s3);
  display: grid;
  align-content: center;
  justify-items: center;
  gap: 6px;
  text-align: center;
  transition: background .3s var(--ease);
}
.mx-brand:hover, .mx-brand:focus-visible { background: var(--surface); }
/*
 * Задержка по диагонали: номер проставляет сценарий, потому что колонок
 * то четыре, то три, то две.
 *
 * Список переходов приходится выписать целиком. Общее правило появления
 * весит больше правила ячейки и затирает у неё переход фона — а фон
 * здесь работает при наведении. Одной задержкой на всё тоже не обойтись:
 * она досталась бы и наведению, и подсветка отставала бы на четверть
 * секунды у дальнего угла стены.
 */
.mx-js .mx-brand.mx-rise.is-in {
  transition:
    opacity .7s var(--ease),
    transform .7s var(--ease),
    background .3s var(--ease);
  transition-delay: calc(var(--i, 0) * 45ms), calc(var(--i, 0) * 45ms), 0s;
}
/*
 * Логотип подключён маской: форму берёт альфа-канал файла, цвет — токен
 * темы. Один файл работает и на светлом фоне, и на тёмном, и вся стена
 * читается как единое целое, а не как двенадцать разных фирменных палитр.
 */
.mx-brand__logo {
  inline-size: min(100%, 168px);
  block-size: 42px;
  background: var(--ink);
  -webkit-mask-image: var(--logo);
  mask-image: var(--logo);
  -webkit-mask-size: contain;
  mask-size: contain;
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  -webkit-mask-position: center;
  mask-position: center;
  opacity: .68;
  transition: opacity .3s var(--ease);
}
.mx-brand:hover .mx-brand__logo,
.mx-brand:focus-visible .mx-brand__logo { opacity: 1; }

/*
 * Надпись вместо знака — там, где файла логотипа нет. Набор тот же,
 * что у цифр и заголовков, и та же приглушённость, что у логотипов:
 * стена должна читаться как одна стена, а не как два сорта ячеек.
 */
.mx-brand__name {
  font-family: var(--display);
  font-weight: 600;
  /* Крупнее обычного текста: надпись должна весить на стене столько же,
     сколько логотип рядом, иначе ячейки без знака выглядят пустыми. */
  font-size: clamp(1.05rem, .9rem + .55vw, 1.45rem);
  letter-spacing: -.01em;
  line-height: 1.15;
  text-align: center;
  text-wrap: balance;
  color: var(--ink);
  opacity: .68;
  transition: opacity .3s var(--ease);
}
.mx-brand:hover .mx-brand__name,
.mx-brand:focus-visible .mx-brand__name { opacity: 1; }
/*
 * Категории проявляются при наведении. Это не эффект ради эффекта:
 * закупщику важно не то, что бренд «есть», а по каким направлениям.
 */
.mx-brand__cats {
  opacity: 0;
  transform: translateY(4px);
  transition: opacity .3s var(--ease), transform .3s var(--ease);
  font-size: 10px;
  letter-spacing: .1em;
}
.mx-brand:hover .mx-brand__cats,
.mx-brand:focus-visible .mx-brand__cats { opacity: 1; transform: none; }

.mx-brands__note { margin-block-start: var(--s4); }

/* --- Расчёт фасовки --------------------------------------------------- */

.mx-calc {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: var(--gut);
  border: 1px solid var(--line);
  background: var(--surface);
  padding: clamp(24px, 3vw, 44px);
}

.mx-calc__packs { border: 0; padding: 0; margin: 0 0 var(--s4); display: grid; gap: var(--s2); }
.mx-calc__packs legend { padding: 0; margin-block-end: var(--s2); }
.mx-calc__packs { grid-template-columns: repeat(auto-fit, minmax(min(92px, 100%), 1fr)); }
.mx-calc__packs legend { grid-column: 1 / -1; }

.mx-pack input { position: absolute; opacity: 0; pointer-events: none; }
.mx-pack__box {
  display: grid;
  gap: 2px;
  justify-items: center;
  padding: 14px 8px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  cursor: pointer;
  transition: border-color .25s var(--ease), background .25s var(--ease);
}
.mx-pack__box:hover { border-color: var(--ink-soft); }
.mx-pack input:checked + .mx-pack__box { border-color: var(--accent); background: color-mix(in srgb, var(--accent) 8%, transparent); }
.mx-pack input:focus-visible + .mx-pack__box { outline: 2px solid var(--accent-ink); outline-offset: 2px; }
.mx-pack__size { font-size: 1rem; font-weight: 500; }
.mx-pack__kind { font-size: 10px; }

.mx-calc__volume { display: grid; gap: var(--s2); }
.mx-calc__volume input[type="number"] {
  inline-size: 100%;
  padding: 14px 16px;
  background: transparent;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  font-size: 1.1rem;
}
.mx-calc__volume input[type="number"]:focus { outline: none; border-color: var(--accent); }
.mx-range { inline-size: 100%; accent-color: var(--accent); }

.mx-calc__grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(110px, 100%), 1fr));
  gap: var(--s3);
  padding-block-end: var(--s4);
  border-block-end: 1px solid var(--line-soft);
}
.mx-calc__grid b {
  display: block;
  margin-block-start: var(--s1);
  font-family: var(--display);
  font-weight: 500;
  font-size: clamp(1.6rem, 1.1rem + 1.8vw, 2.4rem);
  line-height: 1;
  letter-spacing: -.04em;
  font-variation-settings: 'wdth' var(--wide);
}

.mx-calc__bar { padding-block-start: var(--s4); }
.mx-calc__barhead { display: flex; justify-content: space-between; gap: var(--s2); margin-block-end: 10px; }
.mx-calc__track { block-size: 3px; background: var(--line); position: relative; overflow: hidden; }
.mx-calc__track span {
  position: absolute;
  inset-block: 0;
  inset-inline-start: 0;
  inline-size: var(--w, 0%);
  background: var(--accent);
  transition: inline-size .45s var(--ease);
}
.mx-calc__hint { margin-block-start: var(--s3); color: var(--dim); font-size: var(--t-small); }

/* Настоящие вместимости — справка, а не итог расчёта, поэтому тоном
   тише цифр над ней. Первая плашка выделена: это то самое число,
   по которому калькулятор и считает. */
.mx-supply__caps { display: flex; flex-wrap: wrap; gap: 8px; margin-block-start: var(--s3); }
.mx-supply__caps .mx-spec { color: var(--dim); }
.mx-supply__caps .mx-spec:first-child { border-color: var(--accent); color: var(--ink); }

.mx-supply__terms { display: flex; flex-wrap: wrap; gap: 8px; margin-block-start: var(--s4); }

/* --- Ход поставки ----------------------------------------------------- */

.mx-steps__track { position: relative; padding-inline-start: clamp(28px, 4vw, 64px); }
.mx-steps__track::before {
  content: "";
  position: absolute;
  inset-inline-start: 0;
  inset-block: 8px;
  inline-size: 1px;
  background: var(--line);
}
.mx-steps__fill {
  position: absolute;
  inset-inline-start: 0;
  inset-block-start: 8px;
  inline-size: 1px;
  background: var(--accent);
  block-size: var(--p, 100%);
}
.mx-js .mx-steps__fill { block-size: var(--p, 0%); transition: block-size .15s linear; }

.mx-step { position: relative; padding-block: clamp(24px, 3.2vw, 44px); display: grid; gap: var(--s1); }
.mx-step + .mx-step { border-block-start: 1px solid var(--line-soft); }
.mx-step::before {
  content: "";
  position: absolute;
  inset-inline-start: calc(clamp(28px, 4vw, 64px) * -1 - 4px);
  inset-block-start: calc(clamp(24px, 3.2vw, 44px) + 6px);
  inline-size: 9px;
  block-size: 9px;
  border-radius: 50%;
  background: var(--accent);
  border: 1px solid var(--accent);
  transition: background .4s var(--ease), border-color .4s var(--ease), transform .4s var(--ease);
}
.mx-js .mx-step::before { background: var(--ground); border-color: var(--line); }
/* Точка на дорожке загорается вместе с номером: это засечка, по которой
   видно, докуда дошла линия. */
.mx-js .mx-step.is-passed::before { background: var(--accent); border-color: var(--accent); }


/*
 * Шаг ------------------------------------------------------------------
 *
 * Номер набран наборным шрифтом, а не моноширинным. Моноширинным
 * набраны метки разделов — там он и уместен, это подпись. Здесь номер
 * работает заголовком, как числа первого экрана, и тонкие штрихи
 * мониша не дали бы контуру отличаться от заливки.
 */
.mx-step__n {
  font-family: var(--display);
  font-weight: 700;
  font-size: clamp(2.4rem, 1.4rem + 2.4vw, 3.4rem);
  line-height: .9;
  letter-spacing: -.04em;
  color: var(--accent);
  /* Оптическая правка: цифра сидит на своей верхней линии, а не
     на строке заголовка рядом. */
  margin-block-start: -.06em;
}
.mx-step__title { font-size: clamp(1.5rem, 1.1rem + 1.1vw, 2rem); }
.mx-step__note { color: var(--dim); }

/*
 * Итог шага: то, что закупщик получает на руки. На телефоне идёт
 * строкой под описанием, на широком экране — отдельной колонкой.
 * Прятать его на узком экране нельзя: это содержание, а не украшение.
 */
.mx-step__out {
  display: grid;
  gap: 6px;
  margin-block-start: var(--s2);
  padding-inline-start: var(--s3);
  border-inline-start: 1px solid var(--line-soft);
  color: var(--ink);
  font-size: var(--t-small);
  line-height: 1.5;
}
.mx-step__out .mx-label { color: var(--dim); }

/*
 * Пройденный шаг помечен не одной цифрой, а всей строкой. Три приметы
 * разного веса, все про одно:
 *
 * номер заливается — самая заметная;
 * заголовок набирает полный контраст — работает боковым зрением,
 *   даже если на номер не смотришь;
 * итог проявляется, и его волосяная линия загорается красным —
 *   потому что итог и правда становится правдой только после шага.
 *
 * Красный, а не зелёный: зелёный на сайте означает завершённое
 * состояние — галочка отправки, полный контейнер. Здесь же вся секция
 * это ход поставки, и несёт её красная линия. Номер загорается её
 * цветом: линия дошла и подожгла его.
 *
 * Итог гасится прозрачностью, а не убирается из потока: содержание
 * обязано остаться на странице, если сценарий не выполнился.
 */
.mx-js .mx-step__n {
  color: color-mix(in srgb, var(--dim) 55%, transparent);
  transition: color .45s var(--ease), -webkit-text-stroke-color .45s var(--ease);
}
/*
 * Обводка текста — свойство нестандартное, поэтому под условием:
 * без него номер просто останется приглушённой заливкой, а не исчезнет.
 * Прозрачную заливку в отрыве от обводки назначать нельзя.
 *
 * Толщина волосяная, как у всех линий на сайте: рамки, разделители
 * и сама дорожка нарисованы одним пикселем, и контур цифры,
 * набранный вдвое жирнее, выпадал из этого строя.
 */
@supports (-webkit-text-stroke: 1px currentColor) {
  .mx-js .mx-step__n {
    color: transparent;
    -webkit-text-stroke: 1px var(--dim);
  }
  .mx-js .mx-step.is-passed .mx-step__n { -webkit-text-stroke-color: var(--accent); }
}
.mx-js .mx-step.is-passed .mx-step__n { color: var(--accent); }

.mx-js .mx-step__title {
  color: var(--ink-soft);
  transition: color .45s var(--ease);
}
.mx-js .mx-step.is-passed .mx-step__title { color: var(--ink); }

.mx-js .mx-step__out {
  opacity: .34;
  transition: opacity .5s var(--ease), border-color .5s var(--ease);
}
.mx-js .mx-step.is-passed .mx-step__out {
  opacity: 1;
  border-inline-start-color: var(--accent);
}

/*
 * Широкий экран: номер слева, текст посередине, итог справа.
 *
 * До этого дорожка занимала 1208 пикселей, а текст в ней 604: ровно
 * половина ширины стояла пустой, и четыре коротких абзаца под большим
 * заголовком читались как сноска. Правую треть занял итог, левую —
 * номер, и ширина работает целиком.
 */
@media (min-width: 901px) {
  .mx-step {
    grid-template-columns: 5.5ch minmax(0, 1fr) minmax(0, 260px);
    column-gap: var(--s5);
    row-gap: var(--s2);
    align-items: start;
  }
  /* Номер занимает обе строки текста, чтобы стоять вровень с блоком,
     а не с одним заголовком. */
  .mx-step__n { grid-row: 1 / span 2; }
  .mx-step__title { grid-column: 2; }
  .mx-step__note { grid-column: 2; }
  .mx-step__out {
    grid-column: 3;
    grid-row: 1 / span 2;
    align-content: start;
    margin-block-start: 0;
  }
}

/* --- Контакты и форма -------------------------------------------------- */

.mx-contact__grid {
  display: grid;
  grid-template-columns: minmax(0, .9fr) minmax(0, 1.1fr);
  gap: var(--s6) var(--gut);
  align-items: start;
}
.mx-contact__title { font-size: var(--t-h2); margin-block: var(--s3); max-inline-size: 15ch; }
.mx-contact__note { color: var(--dim); max-inline-size: 44ch; }

.mx-contact__facts { display: grid; gap: var(--s3); margin-block-start: var(--s5); }
.mx-contact__facts li { display: grid; gap: 4px; }
.mx-contact__facts a:hover { color: var(--accent-ink); }
/* address по умолчанию курсивный — почтовому адресу это ни к чему. */
.mx-contact__addr, .mx-footer__addr { font-style: normal; }

.mx-form { display: grid; gap: var(--s3); }
.mx-form__row { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(220px, 100%), 1fr)); gap: var(--s3); }
.mx-field { display: grid; gap: 8px; }
.mx-field input, .mx-field textarea {
  inline-size: 100%;
  padding: 14px 16px;
  background: transparent;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  color: var(--ink);
  font-size: 1rem;
  resize: vertical;
}
.mx-field input:focus, .mx-field textarea:focus { outline: none; border-color: var(--accent); }
.mx-field input::placeholder, .mx-field textarea::placeholder { color: var(--dim); }

.mx-check { display: flex; gap: 12px; align-items: start; font-size: var(--t-small); color: var(--dim); }
.mx-check input { accent-color: var(--accent); margin-block-start: 3px; flex: none; }
.mx-form__submit { justify-self: start; }
.mx-form__hint { color: var(--dim); }

/* --- Подвал ------------------------------------------------------------ */

.mx-footer { border-block-start: 1px solid var(--line); padding-block: var(--s6) var(--s4); margin-block-start: var(--s4); }
.mx-footer__top {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s4);
  align-items: end;
  justify-content: space-between;
  padding-block-end: var(--s5);
}
.mx-footer__claim { color: var(--dim); max-inline-size: 40ch; }

.mx-footer__cols {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(200px, 100%), 1fr));
  gap: var(--s4);
  padding-block: var(--s5);
  border-block-start: 1px solid var(--line-soft);
}
.mx-footer__cols > div { display: grid; gap: var(--s1); align-content: start; }
.mx-footer__cols a { color: var(--dim); font-size: var(--t-small); transition: color .2s var(--ease); }
.mx-footer__cols a:hover { color: var(--ink); }
.mx-footer__text { color: var(--dim); font-size: var(--t-small); }
.mx-footer__strong { color: var(--ink) !important; font-size: 1rem !important; }

.mx-footer__bottom {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s3);
  justify-content: space-between;
  padding-block-start: var(--s4);
  border-block-start: 1px solid var(--line-soft);
}

/* --- Узкие экраны ------------------------------------------------------ */

@media (max-width: 1000px) {
  .mx-nav { display: none; }
  .mx-burger { display: block; }
  .mx-header__cta { display: none; }
  .mx-hero__row { grid-template-columns: 1fr; align-items: start; }
  .mx-calc { grid-template-columns: 1fr; }
  .mx-contact__grid { grid-template-columns: 1fr; }
}

@media (min-width: 1001px) {
  .mx-menu { display: none; }
}

@media (max-width: 560px) {
  .mx-toggle__text { display: none; }
}


/* --- Телефон -----------------------------------------------------------
 *
 * Правки высоты. На 390 пикселях страница вытягивалась на 9585 — девять
 * экранов на шесть разделов. Основной вклад давала стена брендов
 * в один столбец: двенадцать ячеек по 128 пикселей это полторы тысячи.
 */

@media (max-width: 560px) {
  :root { --pad: 56px; }

  /* Двенадцать брендов в две колонки вместо одной: минус тысяча пикселей
     прокрутки, и сетка при этом остаётся читаемой. */
  .mx-brands__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .mx-brand { min-block-size: 92px; padding: var(--s2); }
  .mx-brand__logo { block-size: 24px; inline-size: min(100%, 96px); }

  /*
   * Вкладки — одна строка с горизонтальной прокруткой.
   *
   * В столбик они занимали треть экрана и выглядели тремя разделами,
   * а не одним переключателем: рядом стоящие варианты сравниваются
   * взглядом, разнесённые по вертикали — по памяти.
   *
   * Ряд выходит за поля обёртки отрицательным отступом и возвращает
   * их своим внутренним: так крайняя вкладка доезжает до самого края
   * экрана, а следующая выглядывает из-за него — это и есть подсказка,
   * что ряд листается. scroll-padding возвращает прилипшую вкладку
   * на ту же вертикаль, где стоит текст.
   *
   * Полосу прокрутки прячем: подсказкой служит выглядывающая вкладка,
   * а полоса поверх подписей их перечёркивает.
   */
  .mx-brands__modes {
    flex-wrap: nowrap;
    overflow-x: auto;
    /* Докрутив ряд до края, не тянем за собой всю страницу. */
    overscroll-behavior-inline: contain;
    scroll-snap-type: x proximity;
    scroll-padding-inline-start: var(--gut);
    margin-inline: calc(-1 * var(--gut));
    padding-inline: var(--gut);
    /* Риска выбранной вкладки выходит на пиксель ниже её края —
       без запаса контейнер прокрутки её срежет. */
    padding-block-end: 2px;
    gap: 8px;
    scrollbar-width: none;
  }
  .mx-brands__modes::-webkit-scrollbar { display: none; }

  .mx-brands__mode {
    flex: 0 0 auto;
    scroll-snap-align: start;
    padding-inline: var(--s3);
  }
  /* На телефоне наведения нет — показываем направления сразу. */
  .mx-brand__cats { opacity: .75; transform: none; }

  .mx-dir { --pad-x: var(--s3); --pad-y: var(--s3); }
  .mx-head { margin-block-end: var(--s5); }

  /*
   * Цифры в столбик оставляли справа пустое поле шириной в пол-экрана.
   * Ставим подпись рядом с числом: получается строка сводки, как в
   * паспорте, и ширина работает целиком.
   */
  .mx-figures { gap: 0; }
  .mx-figures > div {
    display: grid;
    grid-template-columns: auto 1fr;
    align-items: baseline;
    gap: var(--s3);
    padding-block: var(--s3);
  }
  .mx-figures > div + div { border-block-start: 1px solid var(--line-soft); }
  .mx-figures dt { font-size: clamp(2rem, 1.2rem + 4vw, 2.8rem); min-inline-size: 4.2ch; }
  .mx-figures dd { margin-block-start: 0; text-align: end; }

  /* Фасовок четыре, и два столбца дают ровно две строки. С тремя
     последняя стояла бы одна. */
  .mx-calc__packs { grid-template-columns: repeat(2, minmax(0, 1fr)); }

  /* Две кнопки первого экрана были разной ширины и смотрелись
     как случайно сложенные. Тянем на всю ширину. */
  .mx-hero__cta { display: grid; gap: var(--s2); }
  .mx-hero__cta .mx-btn { justify-content: space-between; }

  .mx-calc { padding: var(--s3); }
  .mx-footer { padding-block: var(--s5) var(--s3); }
  .mx-footer__cols { padding-block: var(--s4); }
}

/* --- Пальцевые цели ----------------------------------------------------
 *
 * Условие — не ширина экрана, а способ ввода. Планшет с мышью лишней
 * высоты не получит, телефон в альбомной ориентации нормальные цели
 * получит. Сорок четыре пикселя — общепринятый минимум.
 */

@media (pointer: coarse) {
  .mx-toggle,
  .mx-logo,
  .mx-footer__cols a,
  .mx-contact__facts a,
  .mx-menu a {
    min-block-size: 44px;
    display: inline-flex;
    align-items: center;
  }
  .mx-footer__cols > div { gap: 0; }
  .mx-nav a { padding-block: 13px; }
}

/* --- Попапы -------------------------------------------------------------
 *
 * Родной dialog приходит с рамкой, полями и авто-полями браузера — всё
 * это снимается. Окно центрируется флексом, а не margin:auto, иначе
 * длинная форма на телефоне обрезается сверху и до неё не долистать.
 */

.mx-modal {
  inset: 0;
  inline-size: 100%;
  max-inline-size: none;
  block-size: 100%;
  max-block-size: none;
  margin: 0;
  padding: var(--s3);
  border: 0;
  background: transparent;
  color: var(--ink);
  overflow-y: auto;
  display: flex;
  align-items: center;
  justify-content: center;
}
.mx-modal:not([open]) { display: none; }

.mx-modal::backdrop {
  background: color-mix(in srgb, #000 72%, transparent);
  backdrop-filter: blur(6px);
  opacity: 0;
  transition: opacity .3s var(--ease);
}
.mx-modal.is-open::backdrop { opacity: 1; }

.mx-modal__box {
  position: relative;
  inline-size: min(100%, 640px);
  background: var(--surface);
  border: 1px solid var(--line);
  padding: clamp(24px, 4vw, 48px);
  display: grid;
  gap: var(--s3);
  box-shadow: var(--shadow);

  opacity: 0;
  transform: translateY(14px) scale(.985);
  transition: opacity .3s var(--ease), transform .35s var(--ease);
}
.mx-modal.is-open .mx-modal__box { opacity: 1; transform: none; }

.mx-modal__title { font-size: clamp(1.5rem, 1.1rem + 1.6vw, 2.1rem); max-inline-size: 20ch; }
.mx-modal__note { color: var(--dim); max-inline-size: 52ch; }
.mx-modal__note a { color: var(--accent-ink); }

.mx-modal__close {
  position: absolute;
  inset-block-start: 14px;
  inset-inline-end: 14px;
  inline-size: 40px;
  block-size: 40px;
  border-radius: 50%;
  transition: background .25s var(--ease);
}
.mx-modal__close:hover { background: var(--raised); }
.mx-modal__close span {
  position: absolute;
  inset-inline: 12px;
  inset-block-start: 19px;
  block-size: 1px;
  background: var(--ink);
}
.mx-modal__close span:first-child { transform: rotate(45deg); }
.mx-modal__close span:last-child { transform: rotate(-45deg); }

/* Благодарность: единственное место, где зелёный занимает крупную
   площадь. Здесь он означает состояние «получено», а не украшает. */
.mx-modal--done .mx-modal__box { text-align: center; justify-items: center; }
.mx-done__mark {
  inline-size: 64px;
  block-size: 64px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  color: var(--ok);
  background: color-mix(in srgb, var(--ok) 14%, transparent);
  border: 1px solid color-mix(in srgb, var(--ok) 45%, transparent);
}
.mx-done__mark svg { inline-size: 30px; block-size: 30px; }


/* Между планшетом и телефоном двенадцать брендов встают в три колонки:
   четыре к этой ширине уже сжимают ячейку до прежнего ногтя.

   Запрос с двумя границами, а не с одной. Мобильное правило про две
   колонки объявлено выше по файлу и при равном весе проиграло бы этому
   на трёхстах девяноста пикселях. Диапазон снимает зависимость
   от порядка строк. */
@media (min-width: 561px) and (max-width: 900px) {
  .mx-brands__grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .mx-brand { min-block-size: 148px; }
  .mx-brand__logo { inline-size: min(100%, 140px); block-size: 36px; }
}

/*
 * Направления лентой ----------------------------------------------------
 *
 * В один столбец четыре карточки давали три с половиной экрана прокрутки,
 * и стык в один пиксель слеплял их в сплошную простыню. Лентой они стоят
 * на одном экране, раздельно, и следующая выглядывает из-за края —
 * это и есть указание, что дальше есть ещё.
 *
 * Порог 632 не выдуман: ровно там сетка auto-fit с минимумом 290
 * перестаёт складывать две колонки. Возьми круглое число — и на полосе
 * между ним и настоящим порогом окажется то одна колонка без ленты,
 * то лента из двух карточек.
 */
@media (max-width: 632px) {
  .mx-dirs__list {
    /* Сколько соседней карточки торчит из-за края. Объявлено на ленте,
       а карточка наследует: пользовательские свойства наследуются, и
       двух копий одного числа быть не должно. */
    --peek: clamp(44px, 15vw, 76px);
    /* Лента идёт от края экрана до края, а не внутри полей обёртки:
       иначе соседняя карточка выглядывала бы из-за невидимой границы
       и приём читался бы как обрезка, а не как продолжение. */
    margin-inline: calc(var(--gut) * -1);
    /* Поле справа равно вылету соседки. Меньше — и последняя карточка
       не доедет до левого поля: прокрутки не хватит, а привязка
       mandatory будет тянуть её обратно. */
    padding-inline: var(--gut) var(--peek);
    display: flex;
    gap: var(--s2);
    background: none;
    border: 0;
    overflow-x: auto;
    /* Дойдя до края ленты, прокрутка не перебрасывается на страницу. */
    overscroll-behavior-inline: contain;
    scroll-snap-type: x mandatory;
    scroll-padding-inline-start: var(--gut);
    scrollbar-width: none;
  }
  .mx-dirs__list::-webkit-scrollbar { display: none; }

  .mx-dir {
    flex: 0 0 calc(100vw - var(--gut) - var(--peek));
    scroll-snap-align: start;
    /* Разделительных линий сетки больше нет — рамка теперь у карточки. */
    border: 1px solid var(--line-soft);
  }
  .mx-dirs__dots {
    display: flex;
    justify-content: center;
    gap: 2px;
    margin-block-start: var(--s3);
  }
}

@media (prefers-reduced-motion: reduce) {
  .mx-modal__box, .mx-modal::backdrop { transition: none; }
  .mx-modal__box { opacity: 1; transform: none; }
  .mx-dot__bar { transition: none; }
}

/* --- Мелочи формы -------------------------------------------------------- */

/* Ловушка для роботов: из потока убрана, но не display:none — часть
   заполнялок такие поля пропускает. */
.mx-trap {
  position: absolute;
  inline-size: 1px;
  block-size: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

.mx-form__error {
  color: var(--accent-ink);
  font-size: var(--t-small);
  border-inline-start: 2px solid var(--accent);
  padding-inline-start: var(--s2);
}
.mx-form__submit:disabled { opacity: .6; cursor: progress; }

.mx-menu__cta {
  font-family: var(--display);
  font-weight: 600;
  font-size: 1.4rem;
  letter-spacing: -.03em;
  font-variation-settings: 'wdth' var(--wide);
  color: var(--accent-ink);
  text-align: start;
}

/* --- Переход из расчёта в запрос ------------------------------------------ */

.mx-supply__act {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s2) var(--s3);
  margin-block-start: var(--s4);
}
.mx-supply__hint { color: var(--dim); }

/* Хорошо загруженный контейнер — зелёный: это выгодная отгрузка,
   а не просто заполненная полоса. Недогруз остаётся акцентным. */
.mx-calc.is-full .mx-calc__track span { background: var(--ok); }

/* --- Форма на телефоне ---------------------------------------------------
 *
 * В попапе форма занимала больше экрана, чем помещается, и до кнопки
 * приходилось долистывать. Убрано отдельное поле порта, а остальное
 * ужато по всем швам: подписи ближе к полям, поля ниже, зазоры меньше.
 * Кегль при этом не трогаем — набирать в мелких полях неудобно.
 */

@media (max-width: 560px) {
  .mx-modal { padding: var(--s2); align-items: flex-start; }
  .mx-modal__box { padding: var(--s3) var(--s3) var(--s4); gap: var(--s2); }
  .mx-modal__title { font-size: 1.45rem; max-inline-size: none; }
  .mx-modal__note { font-size: var(--t-small); }
  .mx-modal__close { inset-block-start: 8px; inset-inline-end: 8px; }

  .mx-form { gap: var(--s2); }
  .mx-form__row { gap: var(--s2); }
  .mx-field { gap: 5px; }
  .mx-field input, .mx-field textarea { padding: 11px 13px; font-size: 16px; }
  .mx-check { font-size: 13px; gap: 10px; }
  .mx-form__hint { display: none; }
}
