/* ============================================================
   БЛОКИ ГЛАВНОЙ: плёнка, витрина, цены, комплект, форма заявки,
   окна (свой город, заведение, кадр макета, политика).

   Лежало инлайном в index.html. Вынесено, когда путь заказа
   разъехался по страницам: странице карты нужны отсюда окна и поля
   («мой город нашёлся не сразу» — это та же форма заявки), а держать
   их копию на второй странице значит завести вторую правду.

   База (токены, сетка, шапка, кнопки) — в assets/base.css.
   Конструктор — в assets/configurator.css.
   ============================================================ */


/* ---------- ТЁМНЫЕ СЦЕНЫ ----------
   Первые две секции — цифровой мир: тёмный экран, телефон, архив.
   Фон и цвета текста едут из film.js через переменные ниже, поэтому
   переход в бумагу происходит внутри одного экрана, а не стыком двух
   секций. Значения по умолчанию — на случай, если скрипт не поднялся. */
/* Акцент на тёмном берём светлый вариант: --route-ink рассчитан на
   контраст с бумагой и на чернилах уходит в нечитаемое. */


/* ---------- ТЕКСТ ВНУТРИ ПЛЁНОЧНЫХ СЕГМЕНТОВ ----------
   Колонка занимает левую половину сетки: объект в кадре стоит правее
   центра, и текст с ним не спорит. */
.section-sub{
  margin:10px 0 0; font-family:'JetBrains Mono',monospace; font-size:11px;
  text-transform:uppercase; letter-spacing:.08em; color:var(--ink-55);
}
/* Стилей секций 04–08 здесь больше нет.

   Это были .prose-col, .about-thesis, .starter-* , .looks-note,
   .tube-list и .gift-body — колонки текста и списки прежней вёрстки:
   «первые пять карточек» перечислением, «что придёт в тубусе»
   таблицей, тезис о подарке абзацем. Всё это рассказывает плёнка, и
   разметки под них на странице не осталось ни строчки. Стили пережили
   её на несколько правок и держались мёртвым грузом: две сотни строк,
   которые нечему применить. */

/* ---------- 08·а · ВИТРИНА: КАРТОЧКИ НА КАРТЕ ----------

   Три слоя друг на друге: кусок карты, карточки на нём, окно
   просмотра поверх страницы.

   РАЗМЕР КАРТОЧКИ СЧИТАЕТСЯ ОТ КАДРА, А НЕ ОТ ЭКРАНА. Карточка
   привязана к месту на карте, значит и величина у неё должна быть
   картовая: доля ширины кадра. Возьми мы vw — на широком мониторе
   карточки разъехались бы по карте пятнами, а на узком слиплись бы,
   хотя карта под ними всё та же. */
/* Снизу поле остаётся: под витриной идёт «Сколько стоит» — блок с
   заголовком и текстом, и вплотную к карте он читался бы подписью
   под ней. */
/* ПОЛЯ ПОД ВИТРИНОЙ БОЛЬШЕ НЕТ.

   Оно осталось от «Сколько стоит»: тот блок начинался заголовком, и
   вплотную к карте заголовок читался подписью под ней. Блока больше
   нет, за витриной сразу идёт заявка — со своим фоном и со своей
   картой. Поле между ними превращалось в пустую бумажную полосу, а
   карта, которая должна перетекать из блока в блок, на ней
   обрывалась. */
/* Секция витрины ОРАНЖЕВАЯ на любой ширине: и плёнка сверху, и заявка
   снизу кончаются акцентом, а карта в неё растворяется краями. */
#showcase{ padding-bottom:0; background:var(--route); }

.showcase-band{
  /* Кадр во всю ширину окна, мимо .grid. Прокрутка по горизонтали —
     для телефона: см. .showcase-stage.

     overscroll-behavior-x:contain, чтобы дотянув кадр до края, не
     уехать «назад» по истории — на трекпаде это происходит с одного
     лишнего движения. */
  overflow-x:auto; overscroll-behavior-x:contain;
  scrollbar-width:none;

  /* РАСТВОРЁННЫХ КРАЁВ БОЛЬШЕ НЕТ.

     На телефоне кадр шире экрана, и его левый и правый края гасились
     градиентом — знаком «дальше есть ещё». Пока под витриной была
     бумага, это читалось именно так. Теперь секция оранжевая, и
     гашение стало проявлять оранжевый прямо поверх карты: не подсказка
     о прокрутке, а два цветных пятна по краям кадра.

     Карта у кромки теперь просто обрезана — как и любой кадр карты. */
}
.showcase-band::-webkit-scrollbar{ display:none; }

.showcase-stage{
  position:relative; width:100%;
  /* Пропорция кадра — та же, что у самого куска карты (784,5 × 615,2),
     чтобы карта легла в него без обрезки и карточки встали ровно туда,
     куда их поставили в админке. */
  aspect-ratio:784.5 / 615.2;
  /* Ниже этой ширины карточки начинают наезжать друг на друга, как их
     ни расставляй: восьми штукам нужно место. Поэтому на телефоне кадр
     не сжимается, а уезжает под палец. */
  min-width:660px;
  /* РАМКИ У КАДРА БОЛЬШЕ НЕТ.

     Линии сверху и снизу очерчивали витрину, пока она была картинкой
     на бумаге. Теперь сверху к ней вплотную подходит оранжевое поле,
     которым кончается плёнка, а снизу — оранжевая заявка с
     продолжением той же карты. На обоих стыках серая линия читается
     швом: карта, которая должна перетекать из блока в блок,
     оказывается обведённой по краю. Кадр виден и без рамки — карта
     закрывает его целиком (object-fit:cover плюс зум). */
  /* Фона у кадра нет: под ним оранжевое поле секции, в которое карта и
     растворяется сверху и снизу (см. .showcase-map-frame). Бумажная
     подложка проступала бы в этих растворениях светлой полосой. */
  background:transparent;
  /* Отсюда считается размер карточек: доля ширины кадра. Взять vw
     нельзя — карточка привязана к месту на карте, и величина у неё
     должна быть картовая, иначе на широком мониторе карточки
     разъедутся по карте пятнами, а на узком слипнутся. */
  /* 13,5% от кадра — это 173 пикселя на мониторе 1280 и 189 на 1400.
     Число не подобрано на глаз: ровно столько занимает карточка в
     редакторе, где сетка набрана как repeat(auto-fill, minmax(170px,
     1fr)). Человек, пришедший сюда из редактора, видит карточку того
     же размера, к какому там привык. */
  --card-w:13.5%;
}
/* На телефоне кадр не сжимается (min-width), значит карточка на нём
   выходит вдвое мельче, чем на мониторе. Читать на ней уже нечего —
   добавляем доли, чтобы подпись оставалась подписью. Догнать
   редакторские 170 пикселей тут нельзя: восемь таких карточек сложатся
   в кучу и от карты под ними ничего не останется. */
@media (max-width:900px){
  .showcase-stage{ --card-w:16%; }

  /* ВЕРХНИЕ КАРТОЧКИ ВЫХОДЯТ ЗА КАДР — И ЭТО НЕ ОШИБКА РАССТАНОВКИ.

     Карточка висит НАД своей булавкой, а доля её выросла (см. выше):
     на телефоне она занимает 27% высоты кадра против 23% на мониторе.
     Булавка на четверти высоты — обычное место, и карточка над ней
     уходит выше верхней кромки. Кадр не резиновый: пропорция у него от
     карты, растянуть его по высоте нельзя.

     Поэтому на телефоне место карточке даёт ПОЛОСА над кадром.
     Плата: булавка карточки, поставленной в админке у самого нижнего
     края, обрежется кромкой — снизу поля нет вовсе, там сразу
     начинается заявка. В админке кадр тот же, и место видно при
     расстановке. */
  .showcase-band{
    /* ПОЛЕ СВЕРХУ — ОРАНЖЕВОЕ, А НЕ БУМАЖНОЕ.

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

       Цвет держит САМА СЕКЦИЯ, а не полоса: полоса прокручивается, и
       фон на ней пришлось бы держать шире экрана. */
    padding:28px 0 0;
  }
}

/* Кромки кадра РЕЗКИЕ. Растворять их не нужно: сверху и снизу к
   витрине вплотную подходят соседние полосы той же карты, и переход
   читается сменой исполнения — полноцветная карта, дальше её же улицы
   на оранжевом. Размывка на этом стыке только мешала бы: она делает
   вид, будто карта кончилась, тогда как она продолжается. */
.showcase-map-frame{ position:absolute; inset:0; overflow:hidden; }

/* КАРТА ПОКАЗАНА КРУПНЕЕ, ЧЕМ ВЛЕЗАЕТ В КАДР.

   На единичном масштабе в полосу попадал слишком большой кусок
   города: улицы сходились в паутину, и карточки стояли не «на
   квартале», а на сером фоне из линий.

   ВТРОЕ И СО СДВИГОМ — ЧТОБЫ ГОРОД НЕ ЧИТАЛСЯ. На полутора кратах в
   кадре лежал весь центр Твери: излучина Волги, стрелка, восьмиугольная
   площадь. Город узнавался с первого взгляда — а карта здесь не про
   Тверь, она про то, КАК ВЫГЛЯДИТ продукт: чужой узнаваемый город и
   обещает не то, и выдаёт, откуда взят образец. Сдвиг уводит кадр в
   жилую сетку юго-западнее центра: кварталы, ручей, парк — фактура
   города вообще, без единой приметы.

   Сдвиг задан в долях кадра, а не в юнитах карты: translate считает
   проценты от размера картинки, и одна и та же пара чисел держит кадр
   на любой ширине блока.

   ВСЕ ТРИ ЧИСЛА ОБЯЗАНЫ СОВПАДАТЬ С ДВУМЯ МЕСТАМИ. С админкой
   (backend/public/admin.html → .slot-map-stage): место карточки
   хранится в долях кадра, а не в координатах карты — раскладывают её
   там, показывают здесь, и стоит кадрам разойтись, как карточка уедет
   с той улицы, на которую её положили. И со сборщиком полос
   (tools/build-backgrounds.py → ЗУМ, СДВИГ): из них он считает окна
   соседних полос, которые стыкуются с витриной сверху и снизу. */
.showcase-map{
  position:absolute; left:50%; top:50%; width:100%; height:100%;
  object-fit:cover; user-select:none; pointer-events:none;
  --map-zoom:3;
  /* КАДР ВЫБРАН БЕЗ ВОДЫ, И ЭТО СЧИТАНО, А НЕ ПОДОБРАНО.

     Прежний кадр (-24.78%, -36.4%) стоял на излучине Волги: в окне
     витрины 2578 пикселей воды из 53 тысяч — река и пара прудов
     наискось через всю картинку. Под карточками это читалось не
     городом, а водоёмом.

     Новый кадр найден перебором по самому файлу карты: окно
     261 × 205 юнитов, шаг 2, условие — минимум воды при максимуме
     улиц. В нём 38 пикселей воды (тонкий ручей у края) и 3428 улиц
     против 2248 в прежнем: квартальная сетка плотнее.

     Числа связаны со сборщиком фонов: tools/build-backgrounds.py →
     СДВИГ = (-167.5, 176.9) юнитов. Проценты здесь — те же сдвиги,
     умноженные на зум: -dx / 784.6 × 300 и -dy / 615.3 × 300.
     Правишь одно — правь и второе, иначе улицы на стыке блоков
     разъедутся. */
  --map-pan-x:-26.96%;
  --map-pan-y:-94.05%;
  transform:translate(calc(-50% + var(--map-pan-x)), calc(-50% + var(--map-pan-y)))
            scale(var(--map-zoom));
}
.showcase-cards{ position:absolute; inset:0; }

/* ---------- КАРТОЧКА — ТА ЖЕ, ЧТО ВЫХОДИТ ИЗ РЕДАКТОРА ----------

   Не «фотография в рамочке», а ровно та карточка, которую человек
   соберёт в editor.html и получит напечатанной. Поэтому вся геометрия
   здесь — пересчёт печатной в доли, а не подобранные на глаз числа
   (исходник: assets/editor-print.css, формат small 15 × 20 мм):

     карточка   15 × 20 мм          → 3 : 4
     поле       0,7 мм              → 4,667% ширины
     фото       квадрат по ширине   → aspect-ratio 1 / 1
     полоса     весь остаток        → flex: 1

   Кегли там заданы в пунктах от карточки в 15 мм (42,52 pt), здесь —
   в долях ширины карточки через cqw: 3,5 pt → 8,23%, 2,5 pt → 5,88%,
   2,8 pt → 6,59%.

   ОТСЮДА ГЛАВНОЕ СВОЙСТВО: карточка выглядит одинаково при ЛЮБОЙ своей
   ширине. Она стоит и на карте размером с ноготь, и в окне просмотра
   размером в пол-экрана — и это один и тот же класс с одной и той же
   разметкой, без второй вёрстки «для крупного показа». Размер задаёт
   тот, кто карточку ставит; всё остальное считается от него. */
.sc-card{
  width:var(--card-w); aspect-ratio:3 / 4;
  /* Единица измерения для всего, что внутри: 1cqw = 1% ширины
     карточки. Без неё кегли пришлось бы задавать в vw, то есть
     привязать подпись на карточке к ширине окна, а не к ней самой. */
  container-type:inline-size;
  display:flex; flex-direction:column;
  /* Поле карточки задаётся НЕ здесь, а на её содержимом — в cqw.

     Проценты сюда напрашиваются (0,7 мм от 15 = 4,667%), но карточка
     на карте позиционирована абсолютно, и процентный padding
     считается от ширины ПОЗИЦИОНИРУЮЩЕГО предка, то есть от всего
     кадра карты: поле выходило в 37 пикселей вместо шести, фото
     сжималось втрое и карточка превращалась в марку посреди белого
     листа. cqw же — доля ширины самой карточки. На ней самой эта
     единица не работает (контейнер не спрашивает сам себя), поэтому
     поле и раздаёт содержимое. */
  padding:0; margin:0;
  background:#fff; border:0; border-radius:0;
  text-align:center;
}

/* Фото квадратное, но сжимаемое — как в печати: пусть лучше кадр
   потеряет сотую долю высоты, чем подпись уедет за край карточки. */
.sc-pic{
  flex:0 1 auto; min-height:0;
  margin:4.667cqw 4.667cqw 0;      /* поле карточки: 0,7 мм от 15 */
  aspect-ratio:1 / 1; overflow:hidden; background:#eee;
}
.sc-pic img{ width:100%; height:100%; object-fit:cover; }

/* Полоса забирает весь остаток высоты — и когда в ней есть текст, и
   когда его нет: пустая она читается местом под подпись от руки, а это
   ровно то, чем она и является. */
.sc-strip{
  flex:1 1 auto; min-height:0; padding:2.67cqw 4.667cqw 4.667cqw;
  display:flex; flex-direction:column; justify-content:center; align-items:center;
  gap:1.33cqw; overflow:hidden;
  font-family:'Inter','Helvetica Neue',Helvetica,Arial,sans-serif;
}
.sc-title{
  width:100%; font-weight:700; font-size:8.23cqw; line-height:1.1; color:#333;
  overflow:hidden; display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical;
}
.sc-comment{
  width:100%; font-size:5.88cqw; line-height:1.15; color:#555;
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
}
/* Дата и оценка разведены по краям строки — как на печатной карточке.
   Если заполнено что-то одно, оно встаёт по центру. */
.sc-bottom{
  width:100%; display:flex; align-items:flex-end; justify-content:space-between;
  gap:2cqw; overflow:hidden; flex-shrink:0;
}
.sc-bottom.is-single{ justify-content:center; }
.sc-date{
  font-family:'JetBrains Mono',ui-monospace,monospace;
  font-size:6.59cqw; letter-spacing:.08em; color:#555;
  min-width:0; overflow:hidden; white-space:nowrap;
}
/* Оценка ужиматься не должна: пропажу пятой точки заметить нечем,
   карточка просто станет другой. Ужимается дата — см. печать. */
.sc-dots{ display:block; flex-shrink:0; }

/* ---------- та же карточка, лежащая на карте ----------

   Здесь только про МЕСТО и поведение: сама карточка описана выше и
   ничего о карте не знает.

   Точка привязки — БУЛАВКА, а не угол карточки: булавка стоит на месте
   события, карточка висит на ней. Поэтому translate уводит карточку
   вверх и влево от точки, а не центрирует её. */
.showcase-card{
  position:absolute; left:var(--x); top:var(--y);
  transform:translate(-50%,calc(-100% - 6px)) rotate(var(--rot,0deg));
  transform-origin:50% 100%;
  box-shadow:0 6px 18px rgba(26,28,32,.28);
  cursor:pointer;
  transition:transform var(--t-menu) var(--ease-out), box-shadow var(--t-menu) var(--ease-out);
}
/* Булавка — та же оранжевая точка, что держит карточки на первом
   экране. Рисуется псевдоэлементом под карточкой: отдельным узлом она
   была бы ещё одной целью для нажатия, а нажимают тут по карточке. */
.showcase-card::after{
  content:''; position:absolute; left:50%; top:100%;
  width:9px; height:9px; margin:2px 0 0 -4.5px; border-radius:50%;
  background:var(--route); box-shadow:0 1px 3px rgba(26,28,32,.45);
}
@media (hover: hover) and (pointer: fine){
  .showcase-card:hover{
    transform:translate(-50%,calc(-100% - 12px)) rotate(0deg) scale(1.06);
    box-shadow:0 14px 30px rgba(26,28,32,.32);
    z-index:3;
  }
}
.showcase-card:focus-visible{ outline:2px solid var(--route-ink); outline-offset:3px; z-index:3; }

/* ---------- окно просмотра ----------

   У окна нет своей рамки, фона и полей: окно — это сама карточка,
   показанная крупно. Всё, что было вокруг неё (бумажная подложка,
   чернильная рамка, подпись сбоку), рисовало «карточку в интерфейсе»
   вместо карточки. */
.showcase-view{
  padding:0; border:0; background:none; overflow:visible;
  max-width:none; max-height:none;
}
.showcase-view::backdrop{ background:rgba(23,21,18,.72); }
/* Ширина — единственное, что задаётся снаружи: всё внутри карточки
   считается от неё (см. .sc-card). Три предела сразу: разумный
   потолок, ширина узкого экрана и высота — карточка втрое выше своей
   ширины на четверть, и 56vh как раз держит её в 75vh по высоте. */
.showcase-view-card{
  --card-w:min(420px, 78vw, 56vh);
  box-shadow:0 18px 60px rgba(0,0,0,.45);
}
/* Крестик — справа сверху, НА снимке.

   Снаружи карточки он стоял бы на затемнении, и его пришлось бы
   делать выворотным — а затемнение это единственный фон, про который
   нельзя сказать, светлый он или тёмный: под ним просвечивает то, что
   человек смотрел до открытия. На снимке фон известен: бумажная
   плашка с чернильной кромкой читается на любом кадре — тот же
   крестик, что у политики и подтверждённого макета (.privacy-close),
   только положенный на угол. */
.showcase-view-close{
  position:absolute; right:10px; top:10px; z-index:2;
  width:32px; height:32px; padding:0; cursor:pointer;
  display:flex; align-items:center; justify-content:center;
  background:var(--paper); border:1px solid var(--ink); color:var(--ink);
  font-size:14px; line-height:1;
  transition:background-color var(--t-tap) var(--ease-soft), color var(--t-tap) var(--ease-soft);
}
@media (hover: hover) and (pointer: fine){
  .showcase-view-close:hover{ background:var(--ink); color:var(--paper); }
}
.showcase-view-close:focus-visible{ outline:2px solid var(--route); outline-offset:2px; }

/* ---------- 09 · ЦЕНЫ: БЛОК СНЯТ ----------

   Здесь стояли три карточки наборов — «База», «Полный», «Подарок», —
   с каруселью на телефоне, точками под ней и кнопками по краям. Двести
   строк стилей.

   Выбор набора переехал в саму заявку (.order-kit-*): человек выбирает
   комплект там же, где называет город и оставляет контакт, а не
   этажом выше, отдельным решением, к которому потом надо возвращаться.
   Разметка ушла тогда же, стили остались и продолжали ездить на
   хостинг — четырнадцать килобайт правил, не совпадающих ни с одним
   элементом страницы.

   Перечёркнутая цена (.was) осталась: её показывает набор в заявке. */
.was{ font-weight:700; color:var(--ink-55); text-decoration:line-through; }
/* ---------- 12 · вопросы ---------- */
/* ---------- 12 · ВОПРОСЫ ----------
   ЗАГОЛОВОК НЕ ПЛАВАЕТ В ПУСТОТЕ. Отбивки складывались втроём: 96 px
   верхнего поля секции, 24 px собственного margin у h2 и ещё 28 px
   сверху у списка — плюс 24 px промежутка сетки между ними. Вокруг
   строки в 29 px высотой набиралось 110 сверху и 68 снизу.

   Своё нижнее поле у заголовка тут лишнее: на широком экране он стоит
   отдельной колонкой слева, и снизу от него ничего нет, а на узком
   расстояние до списка задаёт промежуток сетки. Верхнее поле секции
   ужато и привязано к ширине экрана. */
.faq-head{ grid-column:1/5; }
.faq-head h2{ margin-bottom:0; }
.faq-list{ grid-column:6/-1; }
@media (max-width:900px){ .faq-head, .faq-list{ grid-column:1/-1; } .faq-list{ margin-top:0; } }
.faq-list details{ border-top:1px solid var(--grid); }
.faq-list details:last-of-type{ border-bottom:1px solid var(--grid); }
.faq-list summary{
  list-style:none; cursor:pointer; padding:18px 40px 18px 0; position:relative;
  font-weight:700; font-size:16px; line-height:1.4; color:var(--ink);
}
.faq-list summary::-webkit-details-marker{ display:none; }
/* Знак состояния — плюс, у которого при открытии гаснет вертикаль.
   Никаких стрелок: страница живёт на прямых углах. */
.faq-list summary::before,
.faq-list summary::after{
  content:''; position:absolute; right:6px; top:calc(50% - 1px);
  width:15px; height:2px; background:var(--ink);
}
.faq-list summary::after{ transform:rotate(90deg); transition:transform var(--t-menu) var(--ease-out), opacity var(--t-menu) var(--ease-out); }
.faq-list details[open] summary::after{ transform:rotate(0deg); opacity:0; }
.faq-list summary:hover{ color:var(--route-ink); }
.faq-list summary:focus-visible{ outline:2px solid var(--ink); outline-offset:-2px; }
.faq-list details p{ margin:0 0 20px; font-size:15px; line-height:1.55; color:var(--ink-70); max-width:56ch; }
@media (prefers-reduced-motion: reduce){ .faq-list summary::after{ transition:none; } }

/* ---------- 11 · ЗАКАЗ (красная панель) ---------- */
.order-headline{ grid-column:1/8; }

/* ПОРЯДОК СТРОК В ЗАГОЛОВКЕ ЗАЯВКИ.

   Досье-полоса, заголовок и лид раскладываются по строкам сетки явно:
   иначе они встают в порядке разметки, а между ними вклиниваются
   ячейки соседей.

   Только на широком экране: на узком всё идёт одной лентой сверху
   вниз, и делить нечего.

   КОНСТРУКТОРА В ЭТОМ БЛОКЕ БОЛЬШЕ НЕТ. Здесь стояли правила для .cfg,
   .cfg-stage и .cfg-side — макет карты с настройками и правая колонка
   с составом комплекта: заявка когда-то собиралась прямо на главной.
   Теперь карта собирается на отдельной странице (map.html), а в заявке
   остались выбор набора и форма. Ни одного элемента с этими классами
   на странице нет — правила висели впустую вместе с описанием
   раскладки, которой не существует. */
@media (min-width:901px){
  #order .dossier-bar{ grid-row:1; }
  #order h2.order-headline{ grid-row:2; }
  #order p.order-headline{ grid-row:3; }
}

/* ПУНКТ КОМПЛЕКТА НЕ РАЗДВИГАЕТ КОЛОНКУ.

   Колонка сетки задана долей (1fr), а доля по умолчанию не даёт
   колонке стать уже своего содержимого: minmax(auto, 1fr). Значит,
   достаточно одному пункту комплекта оказаться шире — и колонка
   раздвигается, унося за собой соседнюю. Это и был тот случай, когда
   от выбора набора менялась ширина всего блока: у «Базы» пункты
   короткие, у «Подарка» есть «Комплект для визуализации маршрутов».

   min-width:0 снимает этот нижний предел, overflow-wrap разрешает
   длинному слову переноситься внутри строки. */
.order-kit-label{ min-width:0; overflow-wrap:anywhere; }
/* Снимка напротив текста здесь больше нет — см. комментарий в разметке
   секции заказа. Слот `order` в админке остался, но на страницу его
   теперь никто не выводит. */
/* Кегль не переопределяем: он общий для всех заголовков секций.

   Отбивки сняты. Сверху 48 стояли, чтобы заголовок разошёлся с
   досье-полосой; полосы хватает и 28. Снизу 24 отделяли заголовок от
   лида — а лид его ПРОДОЛЖАЕТ, это одна мысль в два предложения, и
   разрывать их полем в четверть строки незачем. */
#order h2.order-headline{ margin:28px 0 10px; }
/* АКЦЕНТ НА ОРАНЖЕВОМ — БУМАГА, А НЕ ТЁМНЫЙ АКЦЕНТ.

   --route-ink придуман для акцента НА БУМАГЕ и держит на ней 4,5:1. На
   самом акценте он даёт полтора — слово в заголовке просто пропадает,
   что и случилось с «своей картой» на странице карты. Правило было
   привязано к одному заголовку в #order; теперь оно про любой акцент
   внутри красной панели, где бы он ни стоял. */
.panel-red .accent{ color:var(--paper); }
#order h2.order-headline .dot-circle{ background:var(--paper); }
.order-side{ margin-top:0; }
/* СОСТАВ КОМПЛЕКТА — ОКНО ПОСТОЯННОГО РАЗМЕРА.

   Высота коробки задана и от набора не зависит. Раньше она равнялась
   содержимому: у «Базы» четыре пункта, у «Подарка» десять, — и
   переключение набора двигало всё, что стоит ниже и рядом. Съезжала
   форма заявки, менял высоту макет слева, прыгала вся секция. Выбор
   набора — обычное сравнение, человек щёлкает по трём вкладкам
   подряд, и страница не должна каждый раз пересобираться заново.

   Шапка, вкладки наборов и итог держатся на месте, прокручивается
   ТОЛЬКО список пунктов. Итог особенно: это то, ради чего сюда
   смотрят, и уезжать за нижнюю кромку он не должен никогда. */
/* ЭТО ПОТОЛОК, А НЕ ВЫСОТА.

   Саму высоту ставит скрипт по содержимому (index.html →
   подогнатьКомплект): набор из трёх пунктов не должен стоять коробкой
   на шесть. Здесь — предел, выше которого коробка не растёт, а список
   внутри начинает прокручиваться.

   560 вместо прежних 480: «База» с её тремя пунктами укладывалась в 493
   и упиралась в потолок, получая полосу прокрутки ради тринадцати
   пикселей. Прежнее число было подобрано под соседнюю колонку с макетом
   карты — той колонки на главной больше нет, сборка карты уехала на
   отдельную страницу, и рядом стоит форма заявки, которая всё равно
   вдвое выше.

   На узком экране потолок свой: там коробка соседствует не с колонкой,
   а с большим пальцем. */
.order-kit{
  border:1px solid var(--ink);
  display:flex; flex-direction:column;
  /* max-height, а не height: коробка встаёт по содержимому и упирается
     в потолок, только когда пунктов правда много. Список внутри —
     flex:1 1 auto с min-height:0, поэтому лишнее он забирает себе и
     прокручивается сам. */
  max-height:var(--kit-h,560px);
}
/* НА ТЕЛЕФОНЕ КОРОБКА ВЫШЕ, И ВОТ ПОЧЕМУ.

   Высота на десктопе подобрана под соседнюю колонку — состав и форма
   должны сойтись с макетом слева. На узком экране колонок нет, а
   мерить нечем: постоянные части коробки (шапка, вкладки наборов,
   полоса доп. опций и итог) занимают около трёхсот пикселей, и при
   четырёхстах на сам список оставалось сто двадцать — одна строка.
   Список из семи пунктов, показанный по одному, — это не список.

   Считаем от экрана, а не числом: 74% высоты окна оставляют коробке
   место и сверху, и снизу, так что видно и то, откуда пришли, и что
   дальше. Потолок в 600 — чтобы на планшете коробка не разъезжалась
   во весь экран. */
@media (max-width:900px){ .order-kit{ --kit-h:min(74vh,600px); } }
/* Какой это набор и почему он такой. Вместо кнопок-радио с ценами:
   выбирать нечего, набор следует из решения о карточках
   (index.html → наборПоСостоянию). */
.order-kit-now{ display:flex; align-items:baseline; gap:10px; flex-wrap:wrap; padding:10px 0 2px; }
.order-kit-now-name{
  font-family:'Inter','Helvetica Neue',Helvetica,Arial,sans-serif;
  font-weight:700; font-size:17px; letter-spacing:-.01em; color:var(--ink);
}
/* Строки «почему именно этот набор» больше нет (разбор в index.html,
   у renderKit) — осталось одно название. */

/* СОСТАВ — СТОЛБЦОМ, ПО ПУНКТУ НА СТРОКУ.

   История здесь такая. Сначала был нумерованный список с подзаголовком
   у каждого пункта — 238 px высоты, и в коробку, стоявшую по
   содержимому, он не влезал. Потом фраза через запятую — у «Полного»
   это четыре строки сплошного текста, в которых не видно, входит
   что-то или нет. Потом россыпь чипов в две строки: влезала, но на
   коробке в 560 px читалась не составом, а плывущей строкой, под
   которой оставалось пустое поле.

   Условие изменилось: коробка больше не стоит по содержимому, она
   тянется до низа кнопки «Отправить заявку». Высота теперь не дефицит,
   а то, что надо чем-то занять, — и столбец с галочками занимает её
   ровно тем, за чем в этот блок и смотрят: что входит в набор.
   Подзаголовков по-прежнему нет, из-за них список и раздувался. */
.order-kit-brief{
  display:grid; gap:7px;
  /* Поля по бокам те же, что у соседей по коробке (.order-kit-plans,
     .order-kit-total): без них первый пункт упирался прямо в рамку
     блока, и список читался приклеенным к ней. */
  margin:0; padding:10px 16px 2px; list-style:none;
}
.order-kit-brief li{
  display:flex; align-items:baseline; gap:6px;
  font-size:13px; line-height:1.45; color:var(--ink);
}
/* КНОПКА «ИЗМЕНИТЬ» — РЯДОМ С «КАРТОЙ ГОРОДА».

   Здесь стояла спецификация макета второй строкой пункта; она убрана
   (разбор — в index.html, у renderKit), а на её место пришёл путь в
   конструктор, где эти параметры и живут.

   Кнопка нарочно мелкая и обведённая, а не залитая: заливка в строке
   состава читалась бы главным действием чека, а главное в нём —
   «Отправить заявку» слева. Обводка говорит «сюда можно нажать» и на
   этом останавливается.

   ЦВЕТ НЕ --route-ink, И ЭТО ИЗ-ЗА КЕГЛЯ. Акцент коробки — #C4451F:
   им набраны галочки пунктов и сумма итога. На бумаге состава
   (#EDEAE3) он даёт 4,14 — сумме этого хватает с запасом (её кегль 30,
   норма 3), а надписи в десять пикселей нужна норма 4,5. Поэтому здесь
   тот же тёмный вариант акцента, что и у примечания про листы ниже:
   #A23D1F, 5,42 на той же бумаге. Глазом от акцента не отличается,
   порог проходит. */
.order-kit-brief .order-kit-edit{
  flex:none; margin-left:4px; padding:1px 7px 2px;
  font-family:'JetBrains Mono',monospace; font-size:10px; text-transform:uppercase;
  letter-spacing:.08em; line-height:1.5; text-decoration:none;
  color:#A23D1F; border:1px solid currentColor; border-radius:2px;
}
@media (hover:hover) and (pointer:fine){
  .order-kit-brief .order-kit-edit:hover{ color:var(--paper); background:#A23D1F; }
}
.order-kit-brief .order-kit-edit:focus-visible{ outline:2px solid var(--ink); outline-offset:2px; }

/* Галочки белым, а не зелёным: блок лежит на красной панели, и
   зелёный на ней — чужой цвет, который к тому же еле виден. Белый
   здесь и есть акцент.

   СТОЛБЕЦ ДЕРЖИТ ТЕКСТ, А НЕ ГАЛОЧКА.

   Всё в коробке выстроено по одной линии: название набора, примечание
   про листы, «Итого». Пункты состава из неё выпадали — галочка стояла
   на общей линии и отодвигала текст на тринадцать пикселей вправо,
   так что единственной несовпадающей колонкой оказывалось как раз то,
   что читают.

   Отрицательная отбивка вывешивает галочку влево: текст встаёт на
   общую линию, а знак уходит в поле коробки. У пунктов не первых в
   ряду он так же убирается в межколоночный промежуток — тот шире
   выноса, и на соседний текст знак не заедет. */
.order-kit-brief li::before{
  content:'✓'; font-size:10px; color:#fff; flex:none;
  width:7px; margin-left:-13px;
}
.order-kit-brief-note{
  margin:6px 0 0; padding:0 16px;
  font-size:12px; line-height:1.45; color:rgba(23,21,18,.66);
}
/* Зачёркнутая цена не переносится: «499» оставалось на одной строке,
   а «₽» уезжало на следующую — читалось как отдельное число. */
.order-kit-brief-note s{ opacity:.6; white-space:nowrap; }

.order-kit-title{
  flex:none;
  font-family:'JetBrains Mono',monospace; font-size:11px; font-weight:700; text-transform:uppercase;
  letter-spacing:.06em; padding:12px 16px; border-bottom:1px solid var(--ink);
  background:var(--paper); color:var(--ink);
}
/* Список — единственное, что прокручивается. overscroll-behavior не даёт
   докрутке уехать в страницу: дошёл до конца списка — остановился, а не
   потащил за собой всю секцию. */
/* scrollbar-gutter резервирует место под полосу прокрутки ВСЕГДА.

   Без него у «Базы» полосы нет (четыре пункта помещаются), а у
   «Подарка» есть — и строки внутри окна становятся на десяток
   пикселей уже. Со стороны это читается так, будто от набора меняется
   ширина всего блока: рамка стоит, а текст в ней дёргается. */
.order-kit-list{
  flex:1 1 auto; min-height:0; overflow-y:auto; overscroll-behavior:contain;
  scrollbar-width:thin; scrollbar-gutter:stable;
}
/* Растворённый нижний край — знак того, что список продолжается (класс
   ставит скрипт, см. хвостКомплекта). На мыши про это говорит полоса
   прокрутки, на телефоне её нет вовсе. */
.order-kit-list.has-more{
  -webkit-mask-image:linear-gradient(180deg, #000 calc(100% - 34px), transparent 100%);
  mask-image:linear-gradient(180deg, #000 calc(100% - 34px), transparent 100%);
}
.order-kit-list::-webkit-scrollbar{ width:10px; }
.order-kit-list::-webkit-scrollbar-thumb{
  background:rgba(26,28,32,.32); border:3px solid var(--paper);
}
.order-kit-list::-webkit-scrollbar-track{ background:var(--paper); }
/* Строка комплекта — сетка, а не флексбокс в строку: у пункта с
   загрузкой кнопка стоит напротив названия, а список файлов и ошибка
   ложатся ПОД ними во всю ширину. Флексом это требовало бы лишней
   обёртки на каждый пункт — включая четыре, которым она не нужна. */
.order-kit-row{
  display:grid; grid-template-columns:18px 1fr auto; align-items:center;
  column-gap:12px; padding:12px 16px; border-bottom:1px solid rgba(23,21,18,.3);
}
.order-kit-row:last-child{ border-bottom:none; }
.order-kit-num{ font-family:'JetBrains Mono',monospace; font-weight:700; font-size:14px; }
.order-kit-label{ font-weight:700; font-size:13px; line-height:1.3; }
/* Хвост под строкой — от колонки текста до правого края: под номером
   ему стоять незачем, он к номеру не относится. */
.order-kit-row > .kit-files{ grid-column:2 / -1; }
.order-kit-sub{ display:block; font-size:11px; font-weight:400; color:rgba(26,28,32,.72); margin-top:2px; }

/* ---- ДОПОЛНИТЕЛЬНАЯ ОПЦИЯ ----
   Стоит в том же списке, что и пункты набора, но отделена от них
   линией и знаком «+» вместо номера: пункты пронумерованы, потому что
   они уже в комплекте, а это — то, чего в нём нет, пока не нажали.
   Списком ниже её ставить нельзя: тогда «состав комплекта» перестал
   бы отвечать на вопрос, из чего он состоит. */
.order-kit-row.is-option{ border-top:1px solid var(--ink); }
.order-kit-row.is-option .order-kit-num{ opacity:.55; }
/* Полоса доп. опций. Не прокручивается вместе со списком и не растёт:
   сколько опций у набора, столько строк, и все они на виду. */
.order-kit-extras{ flex:none; }
/* На телефоне полоса стоит НАД итогом и делит высоту коробки со
   списком. Общее правило узкого экрана уводит кнопку под текст, и
   каждая опция вырастает до сотни пикселей: у «Базы» их две, и на
   список оставалось меньше, чем на предложения к нему. Здесь кнопка
   остаётся в строке — подпись у неё короткая, места хватает, — а
   пояснение ограничено двумя строками. */
@media (max-width:900px){
  .order-kit-extras .order-kit-row.is-option{ grid-template-columns:18px 1fr auto; }
  .order-kit-extras .order-kit-row.is-option > .kit-option-btn{
    grid-column:auto; margin-top:0; justify-self:end;
  }
  .order-kit-extras .order-kit-row .order-kit-sub{
    display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical; overflow:hidden;
  }
}
.order-kit-extras .order-kit-row:first-child{ border-top:1px solid var(--ink); }
/* Опция, которую пока нельзя взять: видно, что она есть, и написано,
   чего для неё не хватает. */
.order-kit-row.is-locked .order-kit-label{ color:var(--ink-55); }
.order-kit-row.is-locked .kit-option-btn{ opacity:.45; cursor:default; }
/* Цена добавленной опции — строкой под подзаголовком: подпись к тому,
   за что платят, а не кнопка. */
/* Цвет — БУМАГА, а не тёмная краска акцента.

   Коробка комплекта лежит на оранжевой панели, и --route-ink на ней
   почти неотличим от фона: строка про первый лист в наборе читалась
   как приглушённая подпись, хотя говорит она о деньгах — о том, что
   следующий лист платный. Выворотка на оранжевом — самый контрастный
   вариант, который есть в палитре, и цену видно сразу. */
.order-kit-price{
  display:block; margin-top:4px;
  font-family:'JetBrains Mono',monospace; font-size:11px; font-weight:700;
  letter-spacing:.04em; color:var(--paper);
}
/* Прежняя цена в той же строке — тише основного: зачёркнутое сообщает
   «столько же стоит отдельно», а не «столько платить». Тише той же
   бумагой, а не тёмной краской: иначе оно снова тонет в оранжевом. */
.order-kit-price s{ font-weight:400; color:rgba(237,234,227,.62); margin-left:2px; }
/* Крестик — единственное, что с добавленным пунктом ещё можно
   сделать. Размером с номер строки: действие редкое, тянуть на себя
   взгляд ему незачем, но и прятать его нельзя. */
/* Крестик стоит В УГЛУ СТРОКИ, а не в её ряду.

   Рядом с загрузчиком макетов он забирал себе колонку, и на текст
   оставалась треть ширины: «печатаем и вырезаем сами» ломалось на
   пять строк, а строка комплекта раздувалась вдвое против соседних.
   В углу он не отнимает у текста ничего и читается тем, чем является,
   — «убрать эту строку». */
.order-kit-row.is-added{ position:relative; padding-right:40px; }
/* ЗАГРУЗЧИК СТОИТ ПОД ТЕКСТОМ, А НЕ РЯДОМ.

   Раньше он занимал колонку справа — и это работало, пока состав жил
   во всю ширину экрана. В колонке шириной в треть окна кнопка съедала
   половину строки: на подпись оставалось сто пикселей, «печатаем и
   вырезаем сами» ломалось на пять строк, и пункт становился втрое
   выше соседних. Под текстом он не отнимает ширины ни у кого. */
.order-kit-row.has-upload{ grid-template-columns:18px 1fr; }
.order-kit-row.has-upload > .kit-upload-btn{
  grid-column:2; justify-self:start; margin-top:10px;
}
.kit-option-drop{
  position:absolute; top:8px; right:10px;
  width:22px; height:22px; padding:0; cursor:pointer;
  display:inline-flex; align-items:center; justify-content:center;
  background:none; border:1px solid rgba(23,21,18,.35); color:var(--ink);
  font-size:10px; line-height:1;
  transition:background-color .12s, color .12s, border-color .12s;
}
.kit-option-drop:hover{ background:var(--ink); border-color:var(--ink); color:var(--paper); }
.kit-option-btn{
  display:inline-flex; align-items:center; gap:7px; cursor:pointer; white-space:nowrap;
  background:var(--paper); border:1px solid var(--ink); color:var(--ink);
  padding:7px 12px; font-family:'JetBrains Mono',monospace;
  font-size:11px; font-weight:700; text-transform:uppercase; letter-spacing:.04em;
  transition:background-color var(--t-tap) var(--ease-soft), color var(--t-tap) var(--ease-soft);
}
/* Галочка появляется только у включённой опции — по ней состояние
   видно и без цвета, на случай если цвет человек не различает. */
.kit-option-btn::before{ content:'+'; font-weight:700; }
.kit-option-btn[aria-pressed="true"]{ background:var(--ink); color:var(--paper); }
.kit-option-btn[aria-pressed="true"]::before{ content:'✓'; }
@media (hover:hover) and (pointer:fine){
  .kit-option-btn:hover{ background:var(--ink); color:var(--paper); }
  .kit-option-btn[aria-pressed="true"]:hover{ background:var(--paper); color:var(--ink); }
}
.kit-option-btn:focus-visible{ outline:2px solid var(--ink); outline-offset:2px; }
@media (max-width:420px){
  .order-kit-row.is-option{ grid-template-columns:18px 1fr; }
  .order-kit-row.is-option > .kit-option-btn{ grid-column:2; margin-top:10px; justify-self:start; }
}

/* Кнопок-наборов здесь больше нет: выбирать нечего, набор следует из
   решения о карточках (index.html → наборПоСостоянию). Осталась
   коробка под строку с названием набора — рамка снизу отделяет её от
   состава, как отделяла ряд кнопок. */
/* Внутренние линейки тоньше по тону, чем рамка коробки.

   Все разделители были в полные чернила, и коробка читалась таблицей:
   пять одинаковых линий подряд, ни одна ничего не значит. Теперь
   иерархия: внешняя рамка и линия над итогом — чернила (это границы
   смысла: где коробка и где деньги), линия под названием набора —
   волосок. */
.order-kit-plans{ flex:none; border-bottom:1px solid var(--grid); padding:0 16px; }

/* Примечание про листы печати: нижний блок коробки вместе с итогом.
   Своей рамки у него нет намеренно — рамка отделила бы его от суммы,
   которую он объясняет. Пустой узел не занимает места: без печати в
   наборе примечания нет вовсе. */
/* Примечание выделено акцентом: это единственная строка в коробке,
   которая сообщает НЕОЧЕВИДНОЕ — что второй лист печати платный. Серым
   по бумаге она читалась сноской и пропускалась ровно теми, кому
   адресована.

   ЦВЕТ ТЕКСТА ПОДОБРАН ЗАМЕРОМ, А НЕ НА ГЛАЗ. Сначала стоял --route-ink
   (#C4451F) — тот самый «тёмный акцент», рассчитанный на чистую бумагу.
   На подложке в 9% оранжевого он даёт 3,75:1 при норме 4,5 для кегля
   12: строка-предупреждение оказалась бы хуже читаемой, чем обычный
   текст рядом. #A23D1F на той же подложке даёт 4,91 и остаётся
   акцентом, а не чернилами. */
.order-kit-hint{
  flex:none; padding:10px 16px;
  background:rgba(224,86,44,.09);
  border-top:1px solid rgba(224,86,44,.28);
}
.order-kit-hint:empty{ display:none; }
.order-kit-hint .order-kit-brief-note{ margin:0; padding:0; color:#A23D1F; }

/* ---- итог ---- */
.order-kit-total{
  flex:none;
  display:flex; flex-wrap:wrap; align-items:baseline; justify-content:space-between; gap:4px 12px;
  padding:14px 16px; border-top:1px solid var(--ink); color:var(--ink);
}
/* Итог — самая крупная строка коробки: за ней сюда и смотрят. Было
   11/24, стало 12/30. Подпись и сумма стоят на общей базовой линии
   (align-items:baseline выше), поэтому рост кегля их не разводит. */
.order-kit-total-label{
  font-family:'JetBrains Mono',monospace; font-size:12px; font-weight:700;
  text-transform:uppercase; letter-spacing:.06em;
}
.order-kit-total-sum{
  font-family:'Inter','Helvetica Neue',Helvetica,Arial,sans-serif;
  font-weight:700; font-size:30px; letter-spacing:-.02em; line-height:1.1;
  /* Сумма — единственное акцентное пятно в коробке. Крупному
     начертанию хватает и штатного --route-ink: на бумаге он даёт 4,5:1
     при норме 3:1 для кегля от 24. Подпись «Итого» рядом остаётся
     чернильной — иначе акцент перестаёт быть акцентом. */
  color:var(--route-ink);
}
.order-kit-total-note{ flex-basis:100%; margin:0; font-size:11px; line-height:1.35; color:rgba(26,28,32,.72); }

@media (max-width:900px){
  .order-headline{ grid-column:1/-1; }
}

/* ---------- ФОРМА ЗАЯВКИ ----------
   Живёт в ПРАВОЙ колонке конструктора, под «Составом комплекта» и
   итоговой суммой (раскладка — в assets/configurator.css, .cfg-controls).
   Слева под макетом она оставляла справа полтысячи пикселей пустоты. */
.slip{ margin-top:0; }
@media (max-width:900px){ .slip{ grid-column:1/-1; margin-top:8px; } }
.slip form{ display:grid; gap:22px; }
.field label{ display:block; font-family:'JetBrains Mono',monospace; font-size:11px; text-transform:uppercase; letter-spacing:.06em; color:var(--ink); margin-bottom:9px; }
.field select, .field input[type="text"]{
  width:100%; font-family:'Inter','Helvetica Neue',Helvetica,Arial,sans-serif; font-size:15px; color:var(--ink);
  background:var(--paper); border:1px solid var(--ink); padding:12px 12px;
}
.field select:focus-visible, .field input:focus-visible{ outline:2px solid var(--ink); outline-offset:1px; }
.field-error{ display:none; margin:9px 0 0; font-size:12px; font-weight:700; color:var(--ink); }
.field.has-error input{ border-color:var(--ink); border-width:2px; padding:11px; }
.field.has-error .field-error{ display:block; }
/* Подсказка внутри поля видна, пока поле не в работе: на фокусе она
   уходит, чтобы не спорить с набираемым текстом. */
.field input::placeholder{ color:rgba(26,28,32,.55); opacity:1; }
.field input:focus::placeholder{ color:transparent; }

.field-label{
  display:block; font-family:'JetBrains Mono',monospace; font-size:11px;
  text-transform:uppercase; letter-spacing:.06em; color:var(--ink); margin-bottom:9px;
}
/* Обёртка держит и кнопку, и поле, и выпадающее окно: окно позиционируется
   от неё, поэтому одинаково встаёт под обоими состояниями поля. */
.chan-box{ position:relative; }
.chan-pick{
  display:inline-flex; align-items:center; gap:10px; cursor:pointer;
  background:var(--paper); border:1px solid var(--ink); color:var(--ink);
  padding:12px 16px; font-family:'JetBrains Mono',monospace; font-size:11px;
  font-weight:700; text-transform:uppercase; letter-spacing:.06em;
}
.chan-pick[hidden]{ display:none; }
.chan-pick svg{ width:11px; height:11px; flex:none; transition:transform var(--t-menu) var(--ease-out); }
.chan-pick[aria-expanded="true"] svg{ transform:rotate(180deg); }
@media (hover:hover) and (pointer:fine){ .chan-pick:hover{ background:var(--ink); color:var(--paper); } }
.chan-pick:focus-visible{ outline:2px solid var(--ink); outline-offset:2px; }

/* Поле появляется только после выбора канала: до него непонятно,
   что вписывать — никнейм, номер или адрес почты. */
.chan-row{ display:flex; }
.chan-row[hidden]{ display:none; }
.chan-row input[type="text"]{ flex:1; min-width:0; }
.chan-badge{
  display:flex; align-items:center; justify-content:center; flex:none;
  width:46px; padding:0; cursor:pointer;
  border:1px solid var(--ink); border-left:none;
  background:var(--ink); color:var(--paper);
}
.chan-badge svg{ width:20px; height:20px; }
.chan-badge:focus-visible{ outline:2px solid var(--ink); outline-offset:2px; }
@media (hover:hover) and (pointer:fine){ .chan-badge:hover{ background:var(--route); color:var(--ink); } }

.chan-menu{
  position:absolute; top:calc(100% + 8px); left:0; right:0; z-index:30;
  background:var(--paper); border:1px solid var(--ink);
  opacity:0; visibility:hidden; transform:translateY(-6px);
  transition:opacity var(--t-menu) var(--ease-out), transform var(--t-menu) var(--ease-out), visibility 0s linear var(--t-menu);
}
.chan-menu.open{
  opacity:1; visibility:visible; transform:translateY(0);
  transition:opacity var(--t-menu) var(--ease-out), transform var(--t-menu) var(--ease-out);
}
@media (prefers-reduced-motion: reduce){
  .chan-menu{ transition:opacity var(--t-tap) var(--ease-soft), visibility 0s linear var(--t-tap); transform:none; }
  .chan-menu.open{ transition:opacity var(--t-tap) var(--ease-soft); }
  .chan-pick svg{ transition:none; }
}
.chan-slider{
  display:flex; gap:8px; padding:10px; overflow-x:auto;
  scroll-snap-type:x proximity; overscroll-behavior-x:contain;
  scrollbar-width:thin; scrollbar-color:var(--ink) transparent;
}
.chan-slider::-webkit-scrollbar{ height:6px; }
.chan-slider::-webkit-scrollbar-thumb{ background:var(--ink); }
.chan-option{
  flex:none; width:96px; scroll-snap-align:center; cursor:pointer;
  display:grid; gap:8px; justify-items:center; padding:14px 6px;
  background:var(--paper); border:1px solid var(--ink); color:var(--ink);
  font-family:'JetBrains Mono',monospace; font-size:10px; font-weight:700;
  text-transform:uppercase; letter-spacing:.04em; text-align:center;
}
.chan-option svg{ width:22px; height:22px; }
.chan-option[aria-checked="true"]{ background:var(--ink); color:var(--paper); }
@media (hover:hover) and (pointer:fine){
  .chan-option:not([aria-checked="true"]):hover{ background:rgba(23,21,18,.08); }
}
.chan-option:focus-visible{ outline:2px solid var(--route); outline-offset:-4px; }

/* Тот же выбор канала, но для окна «Карта для заведения»: ряд
   кнопок, который переносится по ширине окна, без прокрутки и без
   раскрывающегося меню. Лента с прокруткой оправдана в форме заказа,
   где рядом стоит длинная простыня полей; в окне на четыре поля она
   была бы механизмом сложнее самой задачи.

   Кнопки те же .chan-option — и вид, и разметка, и подпись общие:
   два разных вида одной и той же кнопки на одной странице пришлось
   бы держать согласованными вручную. */
.chan-simple{ display:flex; flex-wrap:wrap; gap:8px; }
/* Кнопки НЕ растягиваются по остатку строки: их пять, в строку они
   встают четвёрками, и растянутая пятая читалась бы не «ещё один
   канал», а «главный». Ширина от подписи, минимум — под самую
   короткую. */
.chan-simple .chan-option{ width:auto; min-width:76px; flex:0 0 auto; padding:10px 10px; gap:6px; }
.chan-simple .chan-option svg{ width:18px; height:18px; }

/* Селектор с .field label обязателен: общее правило `.field label{display:block}`
   перебивало одиночный `.consent` по специфичности — чекбокс схлопывался
   в инлайн-спан шириной 4px, и от него оставался только текст. */
.field label.consent{
  display:flex; align-items:flex-start; gap:12px; cursor:pointer;
  margin-bottom:0; font-family:'Inter','Helvetica Neue',Helvetica,Arial,sans-serif; font-size:13px;
  text-transform:none; letter-spacing:0; color:var(--ink);
}
.consent input{ position:absolute; opacity:0; width:1px; height:1px; }
.consent-box{
  flex:none; width:24px; height:24px; margin-top:-1px;
  border:2px solid var(--ink); background:var(--paper); position:relative;
}
.consent-box::after{
  content:''; position:absolute; inset:4px; background:var(--ink);
  opacity:0; transition:opacity var(--t-press) var(--ease-soft);
}
.consent:hover .consent-box{ background:rgba(23,21,18,.07); }
.consent input:checked + .consent-box::after{ opacity:1; }
.consent input:focus-visible + .consent-box{ outline:2px solid var(--ink); outline-offset:3px; }
.consent-text{ font-size:13px; font-weight:700; line-height:1.45; color:var(--ink); }
/* Кнопка живёт внутри <label>, поэтому клик по ней иначе просто
   переключал бы чекбокс — всплытие гасим в скрипте. */
/* Это <a>, а не кнопка (разбор — в index.html, у openPrivacy): кнопка
   не переносится по строкам, и длинное название политики уезжало на
   свою строку целиком. Сброс padding/border остался от кнопки и не
   мешает — правило одно на все три места, где ссылка встречается. */
.consent-link{
  display:inline; padding:0; border:none; background:none; cursor:pointer;
  font:inherit; color:var(--paper); font-weight:700;
  text-decoration:underline; text-underline-offset:3px; text-decoration-thickness:1px;
}
.consent-link:hover, .consent-link:focus-visible{ color:var(--ink); }
.consent-field.has-error .consent-box{ border-color:var(--ink); border-width:3px; }
.consent-field.has-error .field-error{ display:block; }
@media (prefers-reduced-motion: reduce){ .consent-box::after{ transition:none; } }

.slip-note{
  margin:0; font-family:'JetBrains Mono',monospace; font-size:11px; line-height:1.55;
  letter-spacing:.02em; color:var(--ink); max-width:52ch;
}
.slip-submit{ display:flex; align-items:center; gap:16px; flex-wrap:wrap; margin-top:4px; }
.slip-success{ font-size:13px; font-weight:700; display:none; }
.slip-success.in{ display:block; }

/* ---------- ПОДТВЕРЖДЁННЫЙ МАКЕТ В СОСТАВЕ КОМПЛЕКТА ----------

   Квадрат, а не полоска: в строке он стоит там же, где у «печати
   карточек» стоит кнопка загрузки, и держать эти два места в одной
   ширине нельзя — кадр карты в узкой полоске превратился бы в
   нечитаемую царапину. Квадрат 46×46 — размер, на котором свой район
   ещё узнаётся.

   Кадр внутри обрезается по центру (object-fit: cover): лист бывает
   и альбомный, и портретный, и квадратный, а кнопка обязана
   оставаться одной и той же — иначе строка состава прыгала бы по
   высоте при каждой смене формата. */
.kit-map-btn{
  position:relative;
  width:54px; height:54px; padding:0; flex:none; cursor:pointer;
  background:var(--paper); border:1px solid var(--ink);
  display:block; overflow:hidden;
  transition:box-shadow var(--t-tap) var(--ease-soft), transform var(--t-press) var(--ease-out);
}
.kit-map-btn img{ width:100%; height:100%; object-fit:cover; display:block; }
/* Галочка в углу кадра: с расстояния строки списка миниатюра карты и
   пустое место различаются плохо, а «прикреплено» — это первое, что
   человек проверяет глазами после нажатия. */
.kit-map-tick{
  position:absolute; right:0; bottom:0;
  display:flex; align-items:center; justify-content:center;
  width:16px; height:16px;
  background:var(--ok,#2F7A4F); color:#fff; font-size:10px; line-height:1;
}
/* Пока не подтверждён — на месте кадра стоит слово, а не пустота:
   иначе строка «Карта города» ничем не отличается от остальных, и
   непонятно, что от неё вообще чего-то ждут. */
.kit-map-empty{
  flex:none; align-self:center; max-width:96px;
  font-family:'JetBrains Mono',monospace; font-size:10px; line-height:1.3;
  text-transform:uppercase; letter-spacing:.04em; text-align:right;
  color:rgba(26,28,32,.5);
}
/* Кадр только что прикрепился — строка коротко подсвечивается: человек
   нажал кнопку на карте, а результат появился в другом месте экрана,
   и связать одно с другим должно движение, а не догадка. */
@keyframes kit-map-land{
  0%{ background:rgba(47,122,79,0); }
  30%{ background:rgba(47,122,79,.22); }
  100%{ background:rgba(47,122,79,0); }
}
.order-kit-row.is-landed{ animation:kit-map-land 1.2s ease 1; }
@media (prefers-reduced-motion:reduce){ .order-kit-row.is-landed{ animation:none; } }
@media (hover:hover) and (pointer:fine){
  .kit-map-btn:hover{ box-shadow:0 0 0 2px var(--ink); }
}
.kit-map-btn:focus-visible{ outline:2px solid var(--ink); outline-offset:2px; }
@media (max-width:420px){
  .order-kit-row > .kit-map-btn{ grid-column:2; margin-top:10px; justify-self:start; }
}

/* ---------- ОКНО «ПОДТВЕРЖДЁННЫЙ МАКЕТ» ----------
   Разметку и поведение делит с окном политики (.privacy-*): те же
   шапка с крестиком, то же затемнение, тот же способ закрытия. */
.map-shot-dialog{
  width:min(640px,92vw); max-height:88vh; padding:0; border:1px solid var(--ink);
  background:var(--paper); color:var(--ink);
}
.map-shot-dialog::backdrop{ background:rgba(23,21,18,.6); }
.map-shot-body{ padding:18px 22px 22px; display:flex; flex-direction:column; gap:12px; }
.map-shot-body img{ width:100%; height:auto; display:block; border:1px solid var(--ink); }
.map-shot-spec{
  margin:0; font-family:'JetBrains Mono',monospace; font-size:12px;
  line-height:1.5; color:var(--ink);
}
.map-shot-note{ margin:0; font-size:12px; color:rgba(26,28,32,.65); }
/* «Убрать» — ссылка, а не кнопка: это отмена уже сделанного, и вставать
   вровень с действиями ей незачем. */
.map-shot-drop{
  align-self:flex-start; padding:0; background:none; border:0; cursor:pointer;
  font-family:'JetBrains Mono',monospace; font-size:11px;
  color:rgba(26,28,32,.6); text-decoration:underline; text-underline-offset:3px;
}
@media (hover:hover) and (pointer:fine){ .map-shot-drop:hover{ color:var(--route); } }
.map-shot-drop:focus-visible{ outline:2px solid var(--ink); outline-offset:2px; }

/* ---------- ЗАГРУЗКА PDF В СОСТАВЕ КОМПЛЕКТА ----------
   Кнопка живёт третьей колонкой строки комплекта — напротив названия
   пункта, а не под ним (см. .order-kit-row). Своей обёртки у неё
   поэтому нет: обёртка ставила бы её обратно в колонку текста. */
.kit-upload-btn{
  display:inline-flex; align-items:center; gap:8px; cursor:pointer;
  background:var(--paper); border:1px solid var(--ink); color:var(--ink);
  padding:7px 12px; font-family:'JetBrains Mono',monospace;
  font-size:11px; font-weight:700; text-transform:uppercase; letter-spacing:.04em;
  white-space:nowrap;
}
/* Теперь это ССЫЛКА в редактор карточек, а не кнопка загрузки:
   собственных PDF мы больше не принимаем (см. index.html, разметка
   пункта «Печать и вырезка карточек»). Вид оставлен тот же — место на
   строке пункта и роль «действие рядом с пунктом» не изменились. */
.kit-upload-btn{ text-decoration:none; }
@media (hover:hover) and (pointer:fine){ .kit-upload-btn:hover{ background:var(--ink); color:var(--paper); } }
.kit-upload-btn:focus-visible{ outline:2px solid var(--ink); outline-offset:2px; }
/* Пусто — значит и места не занимает: :empty снимает верхний отступ,
   иначе пункт с загрузкой был бы выше остальных на пустой список. */
.kit-files{ list-style:none; margin:10px 0 0; padding:0; display:grid; gap:6px; }
.kit-files:empty{ display:none; }
/* Узкая колонка: кнопка не влезает третьим столбцом — уходит под
   текст, как раньше. Лучше высокая строка, чем перенос слов в кнопке
   по одной букве. */
@media (max-width:420px){
  .order-kit-row{ grid-template-columns:18px 1fr; }
  .order-kit-row > .kit-upload-btn{ grid-column:2; margin-top:10px; justify-self:start; }
}
.kit-file{
  display:flex; align-items:center; gap:8px;
  font-family:'JetBrains Mono',monospace; font-size:11px; color:var(--ink);
}
.kit-file-name{ overflow:hidden; text-overflow:ellipsis; white-space:nowrap; min-width:0; }
/* Размер уходит вправо, надбавка остаётся у имени: она про этот файл,
   а не про строку. Порядок в разметке — крестик, имя, надбавка,
   размер, — и auto-отступ стоит на размере, а не на крестике. */
.kit-file-size{ color:rgba(26,28,32,.72); flex:none; margin-left:auto; }
.kit-file-extra{
  flex:none; font-weight:700; color:var(--route-ink);
  border:1px solid var(--route); padding:1px 5px; white-space:nowrap;
}
/* Крестик «убрать файл» и строка ошибки загрузки удалены вместе с
   самой загрузкой: убирать нечего (макет один и приходит из
   редактора), а ошибаться при выборе файла больше негде. */

/* ---------- ДИАЛОГИ ---------- */
.city-dialog{
  width:min(460px, calc(100vw - 32px)); padding:0;
  border:1px solid var(--ink); background:var(--paper); color:var(--ink);
}
.city-dialog::backdrop{ background:rgba(23,21,18,.55); }
.city-dialog-inner{ padding:26px; display:grid; gap:18px; }
.city-dialog h3{
  margin:0; font-family:'Inter','Helvetica Neue',Helvetica,Arial,sans-serif; font-weight:700;
  font-size:24px; letter-spacing:-.02em; line-height:1.05;
}
.city-dialog-lede{ margin:0; font-size:14px; line-height:1.5; color:var(--ink-70); }
.city-dialog-actions{ display:flex; gap:10px; justify-content:flex-end; flex-wrap:wrap; }
.city-dialog-done{ display:none; margin:0; font-size:13px; font-weight:700; }
.city-dialog-done.in{ display:block; }

/* Колонка с прокруткой только в теле: при фиксированной высоте тела
   шапка и текст вместе перерастали окно, и диалог получал вторую
   полосу прокрутки поверх первой. */
.privacy-dialog{
  width:min(720px, calc(100vw - 32px)); max-height:min(82vh, 900px); padding:0;
  border:1px solid var(--ink); background:var(--paper); color:var(--ink);
  overflow:hidden;
}
.privacy-dialog[open]{ display:flex; flex-direction:column; }
.privacy-dialog::backdrop{ background:rgba(23,21,18,.55); }
.privacy-head{
  flex:none; background:var(--paper);
  display:flex; align-items:flex-start; gap:16px;
  padding:24px 26px 14px; border-bottom:1px solid var(--ink);
}
.privacy-head h3{
  margin:0; flex:1; font-family:'Inter','Helvetica Neue',Helvetica,Arial,sans-serif; font-weight:700;
  font-size:clamp(20px,2.4vw,26px); letter-spacing:-.02em; line-height:1.08;
}
.privacy-close{
  flex:none; width:34px; height:34px; cursor:pointer;
  background:var(--paper); border:1px solid var(--ink); color:var(--ink);
  font-size:15px; line-height:1;
}
@media (hover:hover) and (pointer:fine){ .privacy-close:hover{ background:var(--ink); color:var(--paper); } }
/* min-height:0 обязателен: без него flex-элемент не даёт себя сжать
   ниже содержимого и прокрутка уезжает на весь диалог. */
.privacy-body{ flex:1; min-height:0; padding:20px 26px 26px; overflow-y:auto; overscroll-behavior:contain; }
.privacy-body h4{
  margin:26px 0 8px; font-family:'JetBrains Mono',monospace; font-size:11px;
  font-weight:700; text-transform:uppercase; letter-spacing:.06em; color:var(--route-ink);
}
.privacy-body h4:first-of-type{ margin-top:0; }
.privacy-body p, .privacy-body li{ margin:0 0 10px; font-size:14px; line-height:1.55; color:var(--ink-70); }
.privacy-body ul{ margin:0 0 10px; padding-left:18px; }
.privacy-body a{ color:var(--route-ink); font-weight:700; }
.privacy-date{
  font-family:'JetBrains Mono',monospace; font-size:11px; text-transform:uppercase;
  letter-spacing:.06em; color:var(--ink-55);
}

/* ---------- 14 · ФУТЕР ---------- */
/* Honeypot: убираем из виду геометрией, а не display:none — часть ботов
   пропускает поля, скрытые через display/visibility.

   Ушло left:-9999px: поле уезжало за левый край секции, и держать его
   там могла только обрезка на самой секции. Обрезка мешала выпадающему
   окну выбора города, поэтому прятать поле надо там, где оно стоит:
   пиксель на пиксель, содержимое срезано clip-path. Для бота поле
   осталось обычным видимым input, что и требуется. */
.hp-field{ position:absolute; width:1px; height:1px; overflow:hidden; clip-path:inset(50%); pointer-events:none; }

footer{ border-top:4px solid var(--route); padding:36px 0 24px; }
.foot-logo{ grid-column:1/4; align-self:center; margin:0; }
/* Два повода написать — двумя равными колонками, а не двумя строками
   подряд.

   Строками они шли в четыре колонки сетки, и второй повод («карта для
   заведения») в эту ширину не вставал: подпись переносилась, оставляя
   на третьей строке одно слово. Хуже перенос был тем, что оба повода
   набраны одинаково и подряд — где кончается первый и начинается
   второй, приходилось разбирать по смыслу.

   Теперь у каждого своя колонка и свой мон-заголовок над адресом:
   повод читается заголовком, действие — ссылкой под ним, а переносить
   нечего, потому что переносить больше некуда. */
.foot-contacts{
  grid-column:5/10; align-self:center;
  display:grid; grid-template-columns:repeat(2,1fr); gap:12px 24px;
  font-size:14px; line-height:1.55; color:var(--ink-70);
}
.foot-contacts a{ font-weight:700; color:var(--ink); text-underline-offset:3px; }
.foot-contacts a:hover{ color:var(--route-ink); }
/* Заявка от заведения открывается окном, а не почтовым клиентом, — но
   стоит в строке рядом со ссылкой на почту и обязана выглядеть так же.
   Своих значений тут нет: всё повторяет правило для ссылки выше. */
.foot-contacts button{
  display:inline; padding:0; border:none; background:none; cursor:pointer;
  font:inherit; font-weight:700; color:var(--ink);
  text-decoration:underline; text-underline-offset:3px; text-decoration-thickness:1px;
}
.foot-contacts button:hover, .foot-contacts button:focus-visible{ color:var(--route-ink); }
.foot-contacts dt{
  margin:0 0 3px; font-family:'JetBrains Mono',monospace; font-size:11px;
  letter-spacing:.06em; text-transform:uppercase; color:var(--ink-55);
}
.foot-contacts dd{ margin:0; }
@media (max-width:560px){ .foot-contacts{ grid-template-columns:1fr; gap:16px; } }
.foot-social{
  grid-column:10/-1; display:flex; gap:10px;
  justify-content:flex-end; align-items:center; align-self:center;
}
.foot-social a{
  display:flex; align-items:center; justify-content:center;
  width:42px; height:42px; flex:none;
  border:1px solid var(--ink); color:var(--ink); text-decoration:none;
  transition:background-color var(--t-tap) var(--ease-soft), color var(--t-tap) var(--ease-soft);
}
.foot-social a svg{ width:22px; height:22px; }
.foot-social a:hover, .foot-social a:focus-visible{ background:var(--route); color:var(--paper); border-color:var(--route); }
.foot-bottom{ grid-column:1/-1; margin-top:28px; padding-top:16px; border-top:1px solid var(--grid); font-family:'JetBrains Mono',monospace; font-size:11px; color:var(--ink-55); }
.foot-bottom a{ color:inherit; text-decoration:underline; text-underline-offset:2px; }
.foot-bottom a:hover{ color:var(--ink); }
/* .foot-legal — строка с реквизитами продавца под копирайтом — удалена
   вместе с самой строкой: кто продаёт, названо в политике обработки
   персональных данных (см. комментарий в подвале index.html). */
@media (max-width:900px){
  .foot-logo, .foot-contacts, .foot-social{ grid-column:1/-1; }
  .foot-contacts{ margin-top:20px; }
  .foot-social{ justify-content:flex-start; margin-top:20px; }
}

/* Отклик на нажатие переехал в assets/motion.css — он теперь общий на
   три страницы, и там же к нему добавлена рамка: у контурных кнопок
   фон переезжал плавно, а рамка щёлкала. */


/* ============================================================
   ТРИ ШАГА ЗАКАЗА

   Список, а не три коробки: коробки на красном превращаются в
   аппликацию, а тут нужен порядок действий. Порядок и держат номер,
   волосок сверху и состояние справа — тот же язык, что у «Как это
   работает» на плёнке, только здесь по нему идут руками.
   ============================================================ */
.order-steps{
  grid-column:1/-1; margin:26px 0 0; padding:0; list-style:none;
  display:grid; gap:0;
}
.order-step{ border-top:1px solid rgba(23,21,18,.3); padding:22px 0 26px; }
.order-step:last-child{ border-bottom:1px solid rgba(23,21,18,.3); }

/* Кегль номера и ширина колонки под него — одна величина на двоих.
   Колонка была задана в em от ЗАГОЛОВКА шага и за кеглем номера не
   следовала: на широком экране номер доходил до 34 px, а колонка
   оставалась прежней, и «02» выезжало на заголовок. Множитель 1.5 —
   две моноширинные цифры плюс разрядка и запас: при 1.4 номер выходил
   ровно в ширину колонки, впритык. */
.order-step{ --step-num:clamp(24px,2.6vw,34px); --step-col:calc(var(--step-num) * 1.5); }
.order-step-head{ display:grid; grid-template-columns:var(--step-col) 1fr auto; align-items:baseline; gap:12px; }
/* НОМЕР ШАГА — КРУПНО.

   Стоял четырнадцатым кеглем, наравне со служебными подписями, и читался
   как сноска: три шага выглядели тремя одинаковыми блоками, а «на каком
   я» приходилось выяснять по заголовку. Номер — единственное, что
   отвечает на этот вопрос с одного взгляда, и размер ему положен
   соответствующий. Колонка под него расширена там же, где задана
   (.order-step-head), и вместе с отступом тела шага — иначе заголовок
   и текст под ним разъехались бы по левому краю. */
.order-step-num{
  font-family:'JetBrains Mono',monospace; font-weight:700;
  font-size:var(--step-num); line-height:1;
  letter-spacing:.04em; color:var(--ink);
}
/* Кегль заголовка вынесен в переменную: от него считается подъём
   состава и карты в третьем шаге (см. .order-final-kit ниже). Высота
   строки заголовка — это высота шапки шага, а шапку они и перекрывают. */
.order-steps{ --step-title-fs:clamp(20px,2vw,26px); }
.order-step-title{
  margin:0; font-family:'Inter','Helvetica Neue',Helvetica,Arial,sans-serif;
  font-weight:700; font-size:var(--step-title-fs); letter-spacing:-.02em; color:var(--ink);
}
/* Состояние шага — справа, моно, как рег-номер в досье-полосе: это
   служебная пометка, а не часть заголовка. */
.order-step-state{
  font-family:'JetBrains Mono',monospace; font-size:11px; text-transform:uppercase;
  letter-spacing:.06em; color:rgba(23,21,18,.62); white-space:nowrap;
}
.order-step.is-done .order-step-state{ color:var(--ink); font-weight:700; }
/* Пропущенный шаг гаснет весь, кроме галочки: видно, что он есть и что
   его сознательно закрыли. */
.order-step.is-skipped .order-step-note,
.order-step.is-skipped .order-step-row{ opacity:.45; }
.order-step.is-skipped .order-step-row{ pointer-events:none; }

/* ---------- ЗАКРЫТЫЙ ШАГ ----------

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

   Закрытый шаг приглушён и не нажимается, а вместо содержимого стоит
   строка с причиной и кнопкой, которая ведёт туда, где причину
   снимают. Не «спрятан»: человек должен видеть, что шаг есть и сколько
   их всего, иначе процесс становится непредсказуемым.

   Открывает шаги скрипт по состоянию черновика (index.html →
   обновитьЗамки). */
.order-step.is-locked > .order-step-body{ opacity:.38; pointer-events:none; user-select:none; }
.order-step.is-locked .order-step-state{ color:var(--ink); opacity:.6; }

/* ПЛАШКИ С ПРИЧИНОЙ ЗДЕСЬ БОЛЬШЕ НЕТ.

   Была рамка пунктиром с абзацем и кнопкой «Собрать карту», и
   вставлялась она в каждый закрытый шаг — то есть во второй и третий
   сразу. Вместе с кнопкой первого шага и нижними воротами одно и то же
   приглашение стояло на странице четыре раза, а рамка стоила по сто
   двадцать пикселей на шаг.

   Теперь закрытый шаг говорит подписью в заголовке: «ждёт карту».
   Причина — шагом выше, там же и кнопка (разбор в index.html,
   обновитьЗамки). */

.order-step-body{ margin-left:calc(var(--step-col) + 12px); margin-top:10px; }
@media (max-width:560px){ .order-step-body{ margin-left:0; } }
.order-step-note{ margin:0 0 16px; font-size:15px; line-height:1.45; color:var(--ink); max-width:52ch; }
.order-step-row{ display:flex; align-items:center; gap:18px; flex-wrap:wrap; }

/* Кадра подтверждённого макета в шагах больше нет: .order-shot,
   .order-spec и .order-step-files убраны вместе с разметкой (разбор —
   в index.html, в первом и втором шаге). Карта показывается один раз,
   в третьем шаге (.order-final-shot ниже). */

/* ГАЛОЧКА «ШАГ ЗАКРЫТ» — рядом с кнопкой шага.

   Зелёный на всю страницу один и означает ровно это (--ok): не
   «хорошо», а «сделано». Толщина обводки 3,5 при поле 24 даёт линию в
   пять пикселей на 34-пиксельном знаке — галочка обязана читаться
   краем глаза, иначе она не отвечает на свой единственный вопрос.

   Знак, а не текст: словами то же самое говорит подпись состояния в
   заголовке шага, и второй раз повторять её рядом с кнопкой нечем. */
.order-tick{ display:block; flex:none; width:34px; height:34px; color:var(--ok); }
.order-tick[hidden]{ display:none; }
.order-tick svg{ display:block; width:100%; height:100%; }

.order-skip{ display:flex; align-items:flex-start; gap:9px; margin-top:18px; cursor:pointer; }
.order-skip input{ margin:2px 0 0; accent-color:var(--ink); }
.order-skip span{ font-size:12px; line-height:1.4; color:rgba(23,21,18,.72); max-width:46ch; }

/* Правил свёрнутой сноски под галочкой больше нет — сама сноска
   убрана вместе с разговором о самостоятельной печати (разбор в
   index.html, у .order-skip). */

/* Состав комплекта и форма лежат в третьем шаге и ширину берут свою, а
   не колонку конструктора: его на этой странице больше нет. */
.order-step .cfg-controls{ grid-column:auto; grid-row:auto; }
.order-step-body > .cfg-controls{ display:flex; flex-direction:column; gap:24px; }

/* Шаг просит внимания: отправку остановили, потому что карта не
   собрана. Та же подсветка, что была у кнопки подтверждения в
   конструкторе, — вспышка рамкой, а не бесконечная анимация. */
.order-step.is-asking{ animation:orderStepAsk 1.2s var(--ease-out) 2; }
@keyframes orderStepAsk{
  0%,100%{ box-shadow:inset 0 0 0 0 rgba(23,21,18,0); }
  40%{ box-shadow:inset 0 0 0 2px var(--ink); }
}
@media (prefers-reduced-motion: reduce){
  .order-step.is-asking{ animation:none; box-shadow:inset 0 0 0 2px var(--ink); }
}


/* ---------- ТРЕТИЙ ШАГ: ЗАЯВКА И НАБОР РЯДОМ ----------

   Поля слева, набор справа. В разметке набор идёт первым — так сложилось
   с тех пор, когда состав стоял НАД формой. Порядок в колонках правит
   order, а разметку не трогаем: на телефоне колонки складываются, и там
   набор честно должен идти первым — сумму человек выбирает до того, как
   оставит контакты.

   Набор липкий: состав длиннее полей, и, дойдя до кнопки, человек всё
   ещё видит, за что платит. */
/* НАБОР В ЗАЯВКЕ СТОИТ НА СВОИХ НОГАХ.

   Эти три строки приехали сюда из assets/configurator.css: на div-е
   стоял ещё и класс .cfg-controls — правой колонки конструктора карты.
   Ради них главная тянула весь тот файл, сорок пять килобайт, из
   которых на ней работали ровно эти правила и ещё два обнуления
   отбивок ниже. Конструктор живёт на своей странице (map.html) и файл
   забирает там; здесь он больше не нужен.

   Что делает каждая: колонкой — чтобы состав и цена шли сверху вниз;
   align-self:start — чтобы набор держал высоту содержимого, а не
   растягивался по строке, оставляя под рамкой пустую коробку. */
/* align-self:start убран намеренно: колонка тянется на всю высоту
   строки, и коробка внутри неё — тоже (flex:1 у .order-kit ниже).
   Так низ состава совпадает с низом кнопки. */
.order-final-kit{ display:flex; flex-direction:column; }
/* Состав стоит первым в колонке, а цена — последней: собственные
   отбивки этих блоков здесь лишние. */
/* Обёртка тянется вместе с колонкой: коробка лежит ВНУТРИ неё, и без
   этого растягивалась колонка, а состав оставался по содержимому —
   висел в воздухе, не доезжая до кнопки на сотню пикселей. min-height:0
   обязателен: без него прокручиваемый список внутри распирает коробку
   по содержимому, вместо того чтобы забрать остаток высоты. */
.order-final-kit .order-side{
  margin-top:0;
  display:flex; flex-direction:column; flex:1 1 auto; min-height:0;
}
.order-final-kit .order-kit{
  margin-bottom:0;
  /* Потолок высоты снят: он был нужен, когда коробка вставала по
     содержимому. Теперь высоту задаёт строка сетки, а список внутри
     по-прежнему забирает остаток и прокручивается сам. */
  flex:1 1 auto; max-height:none; min-height:0;
}
/* ПОРЯДОК НА ТЕЛЕФОНЕ: ПОЛЯ → НАБОР → КАРТА → КНОПКА.

   Колонок на телефоне нет, и до этой правки порядок был порядком
   разметки: набор, карта, форма целиком, пояснение. То есть человек
   сперва читал чек на три тысячи, а кто он — писал последним.

   Теперь наоборот, и это верная последовательность разговора: сначала
   назовись (два поля), потом посмотри, что входит в набор и сколько
   стоит, потом убедись, что карта та самая, — и только после этого
   соглашайся и отправляй. Кнопка и согласие последними, потому что это
   и есть решение; всё, что выше, — основания для него.

   СОГЛАСИЕ ОСТАЛОСЬ ПЕРЕД КНОПКОЙ, А НЕ ПОСЛЕ. Галочка отпирает
   кнопку (см. обновитьВорота в index.html): поставленная ниже, она
   заставляла бы нажать сперва на запертую кнопку, потом искать, почему.

   КАК ЭТО СДЕЛАНО. Поля, согласие и кнопка лежат ВНУТРИ одной формы, а
   набор и карта — снаружи, рядом с ней. Чтобы расставить их вперемешку,
   коробки .slip и самой формы растворяются (display:contents): их дети
   становятся детьми колонки, и порядок можно назначить каждому.
   Разметка при этом не двигается — форма остаётся формой, отправка,
   FormData и скрытые поля работают как раньше. Своего оформления у
   этих двух коробок нет, кроме сетки формы с промежутком 22 — его
   берёт на себя gap колонки, ровно такой же.

   Ниже 900, а не 1280: в промежутке между ними колонок две, и там
   порядок задаёт сетка. */
@media (max-width: 900px){
  .order-final{
    display:flex; flex-direction:column; gap:22px;
    /* Отбивка от подписи шага: у формы она была своя (40 в разметке), а
       без коробки формы пропала вместе с ней. */
    padding-top:12px;
  }
  .order-final > .slip{ display:contents; }
  .order-final .slip > form{ display:contents; }
  /* Всё, что в форме, — первым; исключения ниже. */
  .order-final .slip > form > *{ order:1; }
  .order-final-kit{ order:2; margin-top:0; }
  .order-final > .order-final-shot{ order:3; margin:0; }
  .order-final .slip > form > .consent-field{ order:4; }
  .order-final .slip > form > .slip-submit{ order:5; }
  .order-final > .slip-note{ order:6; }
}

@media (min-width: 901px) {
  .order-final{
    display:grid;
    /* Две колонки: форма и состав. Карта — колонка третья, и включается
       она только с 1280 (разбор ниже, там же и порог). */
    grid-template-columns:minmax(0,460px) 44px minmax(300px,420px);
    column-gap:0; row-gap:24px;
    /* НЕ align-items:start. Набор обязан тянуться на всю высоту строки,
       то есть ровно до низа пояснения под кнопкой «Отправить заявку».
       Со start он вставал по содержимому и висел в воздухе, а под ним
       оставалась дыра. */
    align-items:stretch;
  }
  /* КОЛОНКИ РАССТАВЛЕНЫ ЯВНО, А НЕ ПОРЯДКОМ (order).

     Жёлоба — отдельными колонками сетки, а column-gap снят в ноль. Так
     сделано с тех пор, как между составом и картой промежутка не было
     вовсе (карта стояла к чеку вплотную), и одним column-gap разные
     стыки задать нельзя. Промежутки теперь равны, и column-gap подошёл
     бы снова, — но явные жёлоба оставлены: они не мешают, а нумерация
     колонок ниже уже на них опирается.

     Разметка идёт в порядке «состав, карта, форма» (состав первым — он
     нужен раньше на телефоне, где колонок нет), поэтому места
     назначаются номерами, а не порядком показа.

     СТРОКА УКАЗАНА ЯВНО, И БЕЗ НЕЁ СЕТКА РАЗЪЕЗЖАЛАСЬ: автораскладка,
     поставив состав в третью колонку, а карту в пятую, для формы первую
     колонку той же строки уже НЕ ищет — назад по строке она не
     возвращается. Форма уезжала во вторую строку: состав и карта стояли
     над ней, а не рядом. Номер строки снимает вопрос. */
  .order-final > .slip{ grid-column:1; grid-row:1; }
  /* СОСТАВ ЗАНИМАЕТ ОБЕ СТРОКИ, И ЭТО НЕ УКРАШЕНИЕ.

     При ширине 420 состав разворачивается в шесть строк без переносов,
     и его собственная высота (347) БОЛЬШЕ высоты формы при ширине 460
     (293 — широкой форме нужно меньше строк). Стоял бы он в одной
     строке с формой — задавал бы её высоту, и пояснение, лежащее
     строкой ниже, отъезжало бы от кнопки на разницу: замерено 78
     пикселей пустоты между «Отправить заявку» и текстом. Ровно та
     щель, которую мы убирали.

     Растянутый на обе строки, он получает высоту формы плюс промежуток
     плюс пояснение — 351 пиксель, чуть больше своих 347, — и не давит
     ни на одну из строк. Пояснение встаёт под кнопкой в обычные 24
     пикселя, а низ состава приходит на низ пояснения.

     С 1280 этого не нужно: там колонок три, форма ужимается до 345–372
     и становится выше состава сама (см. ниже). */
  .order-final-kit{ grid-column:3; grid-row:1 / 3; }
  .order-final > .order-final-shot{ grid-column:5; grid-row:1; }
  /* ПОЯСНЕНИЕ — ВТОРОЙ СТРОКОЙ, НО ПОКА В КОЛОНКЕ ФОРМЫ.

     Во всю ширину плашки оно уходит только с 1280 (см. ниже). Здесь,
     где колонок две, оно остаётся под формой — и вот почему.

     Состав при ширине 420 разворачивается в шесть строк без переносов,
     и его собственная высота (347) БОЛЬШЕ, чем высота формы при
     ширине 460 (293: широкой форме нужно меньше строк). Строку задаёт
     самый высокий, то есть состав, — и всё, что стоит второй строкой
     во всю ширину, отъезжает от кнопки на разницу: замерено 54 пикселя
     пустоты между «Отправить заявку» и пояснением. Ровно та щель,
     которую мы только что убирали.

     Поставленное в колонку формы, пояснение идёт сразу за кнопкой, в
     обычном промежутке строки. Состав при этом кончается ниже — но
     ниже ПОЯСНЕНИЯ, а не посреди пустоты, и разница уже 13 пикселей.

     С 1280 этой беды нет: там колонок три, форма ужимается до 345–372
     и становится выше состава — низ формы, состава и карты совпадает
     до пикселя, и пояснение честно ложится во всю ширину. */
  .order-final > .slip-note{ grid-column:1; grid-row:2; }
  /* 52ch — мера для КОЛОНКИ, а этот абзац меру задаёт себе сам: в своей
     строке он тянется во всю отведённую ширину. С потолком в 355
     пикселей он остался бы в три строки, и вынос из формы не дал бы
     ничего. */
  .order-final > .slip-note{ max-width:none; }

  /* Отбивка в 40px осталась от времён, когда над формой стоял состав. */
  .order-final .slip form{ margin-top:0 !important; }
}


/* ПРЕВЬЮ ПОДТВЕРЖДЁННОЙ КАРТЫ — ЕДИНСТВЕННОЕ МЕСТО, ГДЕ МАКЕТ ВИДНО.

   Первый шаг кадра больше не показывает (и скачать его нельзя), так
   что этот кадр — весь визуальный доступ человека к тому, что он
   заказывает. Отсюда три требования, и все три держатся геометрией,
   а не подбором на глаз.

   ПЕРВОЕ: КАРТУ ВИДНО ЦЕЛИКОМ. Никакого object-fit:cover — обрезать
   единственный вид на макет нельзя. Кадр кладётся по высоте, ширина
   идёт из его собственной пропорции.

   ВТОРОЕ: НИЗ КАДРА СОВПАДАЕТ С НИЗОМ СОСТАВА. height:100% при
   растянутой ячейке (align-items:stretch у .order-final) даёт ровно
   высоту строки, а состав тянется по той же строке — значит оба
   кончаются на одной линии, какой бы ни была высота формы.

   ТРЕТЬЕ, И ЭТО ГЛАВНОЕ: КАДР НЕ ЗАДАЁТ ВЫСОТУ СТРОКИ САМ.

   Отсюда position:absolute, и это не трюк. Растр приходит с длинной
   стороной 1100 px, и при обычном потоке браузер считает его вклад в
   высоту строки по натуральному размеру: строка раздувалась под
   картинку, состав тянулся за ней, а под кнопкой «Отправить заявку»
   оставалась пустая полоса высотой в разницу — то самое пустое место
   между кнопкой и пояснением под ней. Абсолютный кадр в расчёт высоты
   не входит вовсе: строку задаёт левая колонка (поля, кнопка и
   пояснение), а кадр подстраивается под готовую высоту.

   max-width страхует альбомные форматы: у них ширина при полной высоте
   выходит за колонку, и правило про замещаемые элементы уменьшает
   ВТОРУЮ сторону пропорционально — картинка становится ниже колонки,
   но не искажается и не обрезается. Лежит она при этом по верху. */
.order-final-shot{ position:relative; margin:0; min-height:0; }
.order-final-shot[hidden]{ display:none; }
/* КАДР ПРИЖАТ К НИЗУ, А НЕ К ВЕРХУ.

   Альбомный лист до низа состава не дотягивается (расчёт — у
   grid-template-columns выше), и остаток высоты куда-то деть надо.
   Прижатый к верху, кадр оставлял щель под собой — у самого низа
   плашки, на одной линии с «Итого» и с пояснением под кнопкой, то есть
   там, где взгляд ждёт края композиции. Прижатый к низу, он этот край
   и образует: низ карты, низ состава и низ пояснения ложатся на одну
   линию, а остаток уходит вверх, к заголовку состава, где рядом и без
   того воздух.

   Портретному кадру это безразлично: он занимает всю высоту, и верх с
   низом у него совпадают с составом. */
.order-final-shot img{
  position:absolute; left:0; bottom:0;
  border:1px solid rgba(237,234,227,.5); background:var(--paper);
}
/* ПО КАКОЙ СТОРОНЕ КЛАДЁТСЯ КАДР — РЕШАЕТ СКРИПТ, И ВОТ ПОЧЕМУ.

   Форматов три: A0 альбомный (он же по умолчанию), A0 портретный и
   квадрат. Портретный кадр при полной высоте строки в колонку
   помещается — его и кладём по высоте, тогда низ карты приходит ровно
   на низ состава. Альбомный при той же высоте потребовал бы 517
   пикселей ширины: столько есть только если отобрать их у формы, а там
   поля под имя и никнейм.

   Одним правилом CSS этот выбор не сделать. height:100% с
   max-width:100% выглядит решением и им не является: ширина
   подрезается по максимуму, а высота остаётся заданной — кадр
   РАСТЯГИВАЕТСЯ. Проверено: альбомный растр 1467×1037 встал как
   281×248, то есть с пропорцией 1,13 вместо 1,41. Обрезать нельзя
   (это единственный вид на макет), тянуть тем более.

   Поэтому сторону выбирает скрипт — одним сравнением: влезает ли кадр
   в колонку, если положить его по высоте (index.html, подогнатьКадр).
   Без скрипта не сработавший выбор оставит кадр по ширине — это
   безопасное состояние: целый, неискажённый, просто не дотянувшийся до
   низа состава. */
.order-final-shot img,
.order-final-shot.by-width img{ width:100%; height:auto; }
.order-final-shot.by-height img{ height:100%; width:auto; }
/* Стилей выкладки карточек (.order-final-pins, .pin-card и прочее)
   здесь больше нет: слой убран целиком. Разбор — в index.html, на
   месте, где он считался. Коротко: настоящая карточка занимает на
   кадре пять-десять пикселей, а любое увеличение врёт о размере
   товара. */

/* Карта только что прикрепилась — кадр коротко подсвечивается: человек
   нажал кнопку на ДРУГОЙ странице, а результат появился здесь, и
   связать одно с другим должно движение, а не догадка.

   Зелёным по рамке, а не фоном: фон у кадра закрыт непрозрачным
   растром, и анимация подложки была бы не видна вовсе. */
@keyframes shot-land{
  0%{ box-shadow:0 0 0 0 rgba(47,122,79,0); }
  30%{ box-shadow:0 0 0 5px rgba(47,122,79,.85); }
  100%{ box-shadow:0 0 0 0 rgba(47,122,79,0); }
}
.order-final-shot.is-landed img{ animation:shot-land 1.2s ease 1; }
@media (prefers-reduced-motion:reduce){ .order-final-shot.is-landed img{ animation:none; } }
/* КАДР НА УЗКОМ ЭКРАНЕ — В ОБЫЧНОМ ПОТОКЕ, ПОД СОСТАВОМ.

   Здесь он был просто спрятан: колонки под него нет, а на своё место в
   третьей колонке он рассчитан — картинка выведена из потока и висит по
   низу ячейки, которую растягивает строка сетки. В потоке без ячейки
   такая картинка обрушивает фигуру в нулевую высоту, и кадр пропадал.
   Пропадал он и на телефоне, где макет посмотреть больше негде вовсе:
   первый шаг кадра не показывает с тех пор, как мы убрали оттуда
   миниатюру.

   Поэтому на узком экране картинка возвращается в поток: обычная
   картинка по ширине колонки, фигура встаёт по ней. Слой карточек
   прижимается к верху, а не к низу, — по той же причине.

   Правила стоят ПОСЛЕ десктопных и с той же силой, поэтому внутри
   своего медиазапроса выигрывают порядком, без !important.

   ПОРОГ 900, А НЕ 1279, И ЭТО ИСПРАВЛЕНИЕ. Сперва кадр показывался со
   всего, что уже 1280, — и ломал двухколоночную раскладку: колонка ему
   назначена пятая (см. grid-column у .order-final-shot), а колонок там
   объявлено три. Сетка достраивала четвёртую и пятую сама, и форма
   сжималась до 175 пикселей вместо 460. Замерено на 1000.

   Ниже 900 сетки нет вовсе — там колонка в одну строку, и кадр стоит
   в ней обычным блоком. А между 901 и 1279 он снова спрятан: своей
   колонки у него нет, и ставить его там некуда (разбор — у правила
   ниже). */
@media (max-width:900px){
  .order-final-shot{ position:relative; margin:0 0 22px; }
  .order-final-shot img,
  .order-final-shot.by-width img,
  .order-final-shot.by-height img{
    position:static; width:100%; height:auto;
  }
}

/* Между 901 и 1279 кадра нет: колонок две, третьей под него не
   объявлено, а всовывать его в неявную колонку — это и была та
   поломка с формой в 175 пикселей. Полноширинной строкой под
   составом он тоже не встаёт: строка там уже занята пояснением, и
   кадр, вклинившись между чеком и пояснением, разорвал бы пару
   «кнопка — сноска к ней». */
@media (min-width:901px) and (max-width:1279px){
  .order-final-shot{ display:none; }
}

/* ---------- ШАГИ СО СЦЕНОЙ ----------

   Под первыми двумя шагами лежит то, что человека за ними ждёт: ночная
   карта под «Собери карту», рассыпанные карточки под «Сделай карточки».
   Картинки собраны из того же, из чего собран сайт (tools/build-backgrounds.py).

   ЗАТЕМНЕНИЕ ЛЕЖИТ ЗДЕСЬ, А НЕ В КАРТИНКЕ. Раньше карта запекалась уже
   притушенной — 35% чернил по всему кадру, ради текста поверх. Но текст
   занимает левую треть плашки, а гасла картинка целиком: справа, где
   ничего не лежит, карта зря уходила в серое.

   Градиент решает то же самое точнее: он гасит ровно ту часть, под
   которой строки, и отпускает остальное в полную силу. И он умеет то,
   чего запечённая вуаль не умеет вовсе, — меняться по ширине экрана:
   на широком текст стоит слева, значит гасим слева направо; на
   телефоне он идёт во всю плашку сверху, значит сверху вниз.

   Тёмная сцена посреди оранжевой секции — это не «ещё один цвет»: это
   тот же приём, что на первом экране, где текст лежит на ночной карте.
   Шаг перестаёт быть строкой списка и становится дверью. */
.order-step.is-scene{
  position:relative; isolation:isolate; overflow:hidden;
  padding:26px 26px 28px; margin-bottom:14px; border-top:0;
  color:var(--paper);

  /* Кадр сцены берётся по самой плашке: это не окно в общую карту, а
     своя картинка — ночная карта под «Собери карту» и рассыпанные
     карточки под «Сделай карточки». */
  background-color:var(--ink);
  background-repeat:no-repeat;
  background-size:cover;
  background-position:center;
}
/* Слой затемнения — отдельным элементом, а не вторым фоном: адреса
   картинок ниже перекрывают background-image целиком, и градиент,
   стоящий в том же свойстве, они бы стёрли. */
.order-step.is-scene::before{
  content:''; position:absolute; inset:0; z-index:-1; pointer-events:none;
  background:linear-gradient(180deg,
    rgba(26,28,32,.92) 0%, rgba(26,28,32,.86) 46%,
    rgba(26,28,32,.5) 74%, rgba(26,28,32,.12) 100%);
}
/* Светлая плашка «Сделай карточки» гасится бумагой, а не чернилами:
   текст на ней тёмный. */
.order-step.is-scene.is-light::before{
  background:linear-gradient(180deg,
    rgba(237,234,227,.9) 0%, rgba(237,234,227,.84) 46%,
    rgba(237,234,227,.5) 74%, rgba(237,234,227,.14) 100%);
}
@media (min-width:901px){
  /* На широком экране строки стоят слева, и правая половина плашки
     свободна — там карту видно в полную силу. */
  .order-step.is-scene::before{
    background:linear-gradient(90deg,
      rgba(26,28,32,.92) 0%, rgba(26,28,32,.88) 38%,
      rgba(26,28,32,.45) 66%, rgba(26,28,32,.08) 100%);
  }
  .order-step.is-scene.is-light::before{
    background:linear-gradient(90deg,
      rgba(237,234,227,.9) 0%, rgba(237,234,227,.86) 38%,
      rgba(237,234,227,.45) 66%, rgba(237,234,227,.1) 100%);
  }
}
/* Адреса картинок стоят ЗДЕСЬ, а не в разметке через переменную:
   относительный url() внутри custom property браузер разрешает от
   файла стилей, а не от страницы, и путь получался assets/assets/. */
/* ДВЕ СТРОКИ НА КАЖДУЮ КАРТИНКУ — ЭТО НЕ ОПЕЧАТКА.

   Полосы лежат в двух форматах (tools/build-backgrounds.py): webp втрое
   легче, но Safari до четырнадцатого его не открывает. Первая строка
   ставит jpeg, вторая перекрывает её на webp — а браузер, который не
   понимает image-set, вторую строку просто отбрасывает как
   недействительную и остаётся на первой. Никаких проверок в скриптах и
   ни одного лишнего запроса: качается ровно один файл из двух. */
/* .is-near ставит наблюдатель за 1200 px до блока (index.html): пока
   класса нет, адреса картинок нигде не упомянуты, и браузер их не
   качает. Плашка до этого момента стоит ровной заливкой — своей же,
   background-color выше, — так что пустого места не возникает. */
#order.is-near #orderStep1.is-scene{ background-image:url('step-map.jpg'); }
#order.is-near #orderStep1.is-scene{
  background-image:image-set(url('step-map.webp') type('image/webp'),
                             url('step-map.jpg') type('image/jpeg'));
}
#order.is-near #orderStep2.is-scene{ background-image:url('step-cards.jpg'); }
#order.is-near #orderStep2.is-scene{
  background-image:image-set(url('step-cards.webp') type('image/webp'),
                             url('step-cards.jpg') type('image/jpeg'));
}
/* ТРЕТИЙ ШАГ — ТА ЖЕ НОЧНАЯ КАРТА, ЕЁ НИЖНЯЯ ЧАСТЬ.

   Заявка стояла голой формой на оранжевом, после двух плашек-сцен —
   и читалась не третьим шагом, а хвостом секции. Теперь она такая же
   плашка, как «Собери карту», и на той же картинке — step-map.jpg.

   Кадрирование ведёт тот же скрипт, что и у первого шага (index.html →
   «Карта под первым шагом»): фон сдвигается на отступ плашки внутри
   секции, и карта в окне совпадает со следом улиц вокруг. Оба окна
   смотрят в ОДНУ карту, поэтому третье — продолжение первого, а не
   копия его кадра. Отдельной картинки не заводим — ни байта сверху.

   center bottom ниже — запасной вариант на случай, если скрипт не
   отработал: тогда плашка показывает низ полосы, первый шаг — её
   середину, и куски хотя бы не повторяют друг друга. */
#order.is-near #orderStep3.is-scene{ background-image:url('step-map.jpg'); }
#order.is-near #orderStep3.is-scene{
  background-image:image-set(url('step-map.webp') type('image/webp'),
                             url('step-map.jpg') type('image/jpeg'));
}
#orderStep3.is-scene{
  background-position:center bottom;
  /* overflow:hidden у сцены здесь нельзя, по двум причинам. Меню
     способов связи выпадает вниз и обрезалось бы кромкой плашки. И
     набор справа липкий (position:sticky): предок с overflow:hidden
     становится для него прокручиваемым контейнером, который никуда не
     прокручивается, — и липкость молча пропадала бы. Вуаль и фон
     лежат внутри плашки сами (inset:0), обрезать нечего. */
  overflow:visible;
  margin-bottom:0; border-bottom:0;
}

/* Форма на ночной карте. Поля остаются бумажными — это и есть бланк,
   лежащий на карте; меняются только подписи вокруг них. */
.is-form .field label,
.is-form .field-label,
.is-form .field label.consent,
.is-form .consent-text,
.is-form .slip-success{ color:var(--paper); }
.is-form .slip-note{ color:rgba(237,234,227,.72); }
/* Ошибку поля нельзя оставлять чернилами: на тёмном её просто не
   видно, и человек не узнал бы, почему заявка не ушла. */
.is-form .field-error{ color:var(--paper); }
.is-form .field.has-error input,
.is-form .consent-field.has-error .consent-box{ border-color:var(--route); }
.is-form .consent-box{ border-color:var(--paper); }
.is-form .consent:hover .consent-box{ background:rgba(237,234,227,.82); }
.is-form .consent input:focus-visible + .consent-box,
.is-form .field input:focus-visible{ outline-color:var(--paper); }
.is-form .consent-link:hover,
.is-form .consent-link:focus-visible{ color:var(--route); }
/* ВЫБОР СПОСОБА СВЯЗИ — АКЦЕНТОМ.

   Кнопка «Выбрать» была бумажной с чернильной рамкой — рисована под
   оранжевый фон. На ночной карте она читалась как ещё одно поле ввода:
   те же бумага и рамка, что у строки «Имя» рядом. А это не поле, это
   единственное действие в форме до самой отправки — без него заявку не
   отправить.

   Значок смены канала (.chan-badge) шёл чернилами на бумаге: на тёмном
   от него оставался светлый прямоугольник. Ему тот же акцент. */
.is-form .chan-pick{ background:var(--route); border-color:var(--route); color:var(--paper); }
.is-form .chan-pick:hover,
.is-form .chan-pick:focus-visible{ background:var(--paper); border-color:var(--paper); color:var(--ink); }
.is-form .chan-badge{ background:var(--route); border-color:var(--route); color:var(--paper); }
@media (hover:hover) and (pointer:fine){
  .is-form .chan-badge:hover{ background:var(--paper); color:var(--ink); }
}
/* Строка ввода рядом со значком остаётся бумажной — это поле. Но её
   рамка была чернильной, то есть невидимой на тёмном, и поле смыкалось
   со значком без границы. */
.is-form .chan-row input{ border-color:var(--route); }

/* Кнопка — как на других сценах: акцент на чернилах. Тёмная кнопка
   «на красном» здесь пропала бы в фоне. */
.is-form .btn.on-red{ background:var(--route); color:var(--paper); }
.is-form .btn.on-red:hover,
.is-form .btn.on-red:focus-visible{ background:var(--paper); color:var(--ink); }

/* Состав комплекта — бумажный чек, лежащий на карте. Прозрачная
   коробка с чернильной рамкой была нарисована под оранжевый фон; на
   ночной карте её строки пропали бы. С бумажным дном вся её
   внутренняя вёрстка (чернила, рамки, итог) работает как есть. */
.is-form .order-kit{ background:var(--paper); }
/* Галочки белые — под оранжевый. На бумаге они исчезали; ставим
   акцент, как у остальных «точек» на сайте. */
.is-form .order-kit-brief li::before{ color:var(--route-ink); }

/* СВЕТЛАЯ СЦЕНА — под карточками.

   Две тёмные плашки подряд читались одной длинной полосой, а карточки
   на них терялись: они и так под вуалью, а ведь именно они там главное.
   Светлая ставит их на бумагу, как в жизни, и заодно делит блок на два
   разных шага, а не на два куска одного. */
.order-step.is-scene.is-light{ background-color:var(--paper); color:var(--ink); }
.order-step.is-scene.is-light::after{
  background:linear-gradient(90deg, rgba(237,234,227,.9) 0%, rgba(237,234,227,.72) 55%, rgba(237,234,227,.5) 100%);
}
.order-step.is-scene.is-light .order-step-num,
.order-step.is-scene.is-light .order-step-title,
.order-step.is-scene.is-light .order-step-note{ color:var(--ink); }
.order-step.is-scene.is-light .order-step-state{ color:rgba(23,21,18,.62); }
.order-step.is-scene.is-light.is-done .order-step-state{ color:var(--ink); }
.order-step.is-scene.is-light .order-skip span{ color:rgba(23,21,18,.72); }
.order-step.is-scene.is-light .order-skip input{ accent-color:var(--ink); }
.order-step.is-scene.is-light.is-asking{ box-shadow:inset 0 0 0 2px var(--ink); }

.order-step.is-scene::after{
  /* Читаемость держит не сама картинка, а вуаль поверх неё: снимки на
     фоне карточек светлые, и белый текст на них пропадал. */
  content:''; position:absolute; inset:0; z-index:-1;
  background:linear-gradient(90deg, rgba(26,28,32,.86) 0%, rgba(26,28,32,.66) 55%, rgba(26,28,32,.5) 100%);
}
.order-step.is-scene + .order-step{ border-top:0; }
.order-step.is-scene .order-step-num,
.order-step.is-scene .order-step-title,
.order-step.is-scene .order-step-note{ color:var(--paper); }
.order-step.is-scene .order-step-state{ color:rgba(237,234,227,.7); }
.order-step.is-scene.is-done .order-step-state{ color:var(--paper); }
.order-step.is-scene .order-skip span{ color:rgba(237,234,227,.72); }
.order-step.is-scene .order-skip input{ accent-color:var(--paper); }
.order-step.is-scene.is-asking{ box-shadow:inset 0 0 0 2px var(--paper); }

/* Кнопка на тёмной сцене: бумага на чернилах, как на первом экране. */
.btn.on-scene{ background:var(--route); color:var(--paper); }
.btn.on-scene:hover, .btn.on-scene:focus-visible{ background:var(--paper); color:var(--ink); }

/* Блока «сделанное» (снимок + галочка в углу + «Редактировать») здесь
   больше нет: .order-done, .order-shot, .order-check и .order-edit
   удалены вместе с разметкой шагов. Вместо него — .order-tick выше. */
/* Галочка одна и та же на обеих сценах: зелёный держит контраст и на
   ночной плашке, и на бумажной, а перекрашивать знак состояния под фон
   значило бы заводить два зелёных вместо одного. */

@media (max-width:560px){
  .order-step.is-scene{ padding:20px 18px 22px; }
}


/* На узком экране состояние шага уходит под заголовок: в одну строку с
   ним оно отнимает у названия половину ширины, и «Собери карту»
   переносится на два слова. */
@media (max-width:560px){
  .order-step-head{ grid-template-columns:var(--step-col) 1fr; }
  .order-step-state{ grid-column:2; justify-self:start; margin-top:4px; }
}


/* ---------- ФОН БЛОКА ЗАЯВКИ: СЛЕД ТОЙ ЖЕ КАРТЫ ----------

   Под заявкой лежит ТОТ ЖЕ КУСОК ГОРОДА, что показан в витрине с
   карточками, — только дорогами, вполсилы. Середина страницы читается
   одной картой: витрина показывает её в полную силу, а выше и ниже от
   неё остаётся след.

   Раньше здесь лежал СЛЕДУЮЩИЙ кусок карты — тот, что идёт под
   витриной. Геометрически это было продолжение, но глазом читалось
   другой картой: у витрины центр города, а ниже начинались окраины с
   другим рисунком улиц.

   МАСШТАБ СЧИТАЕТСЯ ОТ ВИТРИНЫ. Она показывает 523 юнита карты во всю
   ширину блока, но не сжимается ниже 660 px (.showcase-stage
   min-width): на телефоне те же 523 юнита занимают 660 px, а не ширину
   экрана. Отсюда max(100%, 660px) — то же правило, что и у кадра
   витрины, иначе на узком экране масштабы разъедутся вдвое. */
#order{ position:relative; isolation:isolate; }
#order::before{
  content:''; position:absolute; inset:0; z-index:-1;
  background:top center/max(100%, 660px) auto no-repeat;
  /* СВЕРХУ ВСТЫК, СНИЗУ — В НОЛЬ.

     Верхняя кромка резкая: там полоса встречается с витриной, и улицы
     обязаны сойтись без размывки. А книзу след гаснет: заявка кончается
     на чистом акценте, и «Коротко о главном» начинается на чистой
     бумаге, без обрубленной сетки по границе.

     Гаснет он именно ЗДЕСЬ, а не в самой картинке, потому что здесь
     известна высота блока. Проценты держат растворение внутри него,
     каким бы длинным он ни вырос; vw считают от ширины окна, в которой
     живёт картинка (её высота — 141% ширины полосы), и не дают следу
     оборваться там, где картинка кончилась. min() берёт то, что
     наступит раньше. */
  -webkit-mask-image:linear-gradient(180deg,
    #000 0, #000 min(26%, 34vw), transparent min(100%, 140vw));
  mask-image:linear-gradient(180deg,
    #000 0, #000 min(26%, 34vw), transparent min(100%, 140vw));
}

/* Адрес контуров — под тем же классом подхода, что и плашки выше: это
   284 КБ (запасной jpeg) на четырёх пятых высоты страницы. */
#order.is-near::before{
  background-image:url('order-contours.jpg');
  background-image:image-set(url('order-contours.webp') type('image/webp'),
                             url('order-contours.jpg') type('image/jpeg'));
}

/* ---------- ЗАЯВКА СОБРАНА В КОМПОЗИЦИЮ ----------

   Поля во всю ширину колонки читались анкетой: строка ввода в семьсот
   пикселей просит длинного ответа, а там имя и никнейм. Поэтому мера
   ограничена: 460 на форму, 400 на набор.

   ВЫРАВНИВАНИЕ — ПО ЛЕВОМУ КРАЮ, А НЕ ПО ЦЕНТРУ.

   Здесь стояло justify-content:center: пара читалась как одна
   композиция, а не как две колонки, растянутые по краям. Замысел
   верный, следствие — нет. На 1440 колонки занимают 904 из 1257
   пикселей тела шага, и центрирование раскладывало остаток поровну:
   177 пикселей пустоты слева. Первый и второй шаги при этом начинаются
   вплотную к своему краю — и третий выглядел съехавшим вправо, один на
   всю страницу.

   Мера осталась прежней, ушло только центрирование: колонки стоят там
   же, где начинается всё остальное.

   НАБОР ШИРЕ ФОРМЫ И ПРИЖАТ К ПРАВОМУ КРАЮ.

   Было 460 + 400 подряд, слева: на 1280 колонки занимали 904 из 1086
   пикселей тела шага, и 182 простаивали справа — плашка кончалась,
   а композиция нет. Мера формы осталась (460: это поле под имя и
   никнейм, длиннее просить незачем), а набор вырос до 560 и встал
   к правому краю — space-between разводит колонки по краям плашки,
   и остаток ширины уходит в промежуток между ними, а не в пустоту
   за набором.

   560, а не «сколько останется»: на 1440 и шире набор растянулся бы
   до семисот с лишним, а это чек, а не полоса. Лишнее забирает
   промежуток. */
/* ПОРОГ КАРТЫ — 1280, А НЕ 901, И ЭТО ИЗМЕРЕНО.

   Третья колонка берёт остаток ширины, и остаток этот кончается быстро:
   при делении строки на три карте достаётся 372 пикселя на 1440, 333
   на 1280, 275 на 1100 и 226 на 940 — но чем уже окно, тем уже и форма
   с составом, а им нужнее: поле под имя и строки цен не сжимаются
   вслед за картинкой. На 1100 форма ужалась бы до 275 пикселей.

   Поэтому ниже 1280 колонок снова две, а меры им возвращаются прежние
   (460 и 420): там нет карты, ради которой их подрезали. */
@media (min-width: 1280px) {
  .order-final{
    /* ЛИШНЮЮ ШИРИНУ ЗАБИРАЕТ КОЛОНКА ФОРМЫ, А НЕ ПРОМЕЖУТОК.

       Стояло 460 + 560 с justify-content:space-between — и на широком
       экране весь остаток уходил в щель между колонками: на 1440 она
       раздувалась до 237 пикселей, форма и набор переставали читаться
       одной композицией.

       Теперь обе колонки стоят по мере и прижаты влево: форма 460,
       набор 420, между ними 44. Набор идёт ВПЛОТНУЮ ЗА ФОРМОЙ, а
       свободная ширина уходит вправо, в поле карты.

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

       Ширина набора 420, было 560: на 560 состав расплывался в две
       широкие строки, а со столбцом пунктов коробке нужна не ширина,
       а высота. Мера формы — 460: строка ввода в семьсот пикселей
       просит длинного ответа, а там имя и никнейм. */
    /* ТРИ РАВНЫЕ КОЛОНКИ И ДВА РАВНЫХ ПРОМЕЖУТКА.

       Раньше меры были жёсткими и разными: форма 400, состав 340, карта
       забирала остаток (421) и стояла к составу ВПЛОТНУЮ — без
       промежутка, как приложенный к чеку лист. Задумано это было ради
       высоты карты: чем шире ей колонка, тем ближе её низ к низу
       состава. Но пара «состав + карта» от этого читалась одним
       сросшимся блоком против одинокой формы, а сумма трёх колонок
       (1161 из 1205) всё равно не закрывала строку — справа оставался
       обрезок.

       Теперь строка делится ровно: 1fr − 44 − 1fr − 44 − 1fr. Остаток
       нулевой при любой ширине окна, промежутки одинаковые, и три
       блока стоят тремя блоками. На 1440 это 372 пикселя каждому —
       форме на 72 больше прежнего, составу на 32.

       Платой идёт высота карты: альбомному листу при 372 достаётся
       263 пикселя вместо 298. Дотянуться до низа состава ему всё равно
       было нечем (нужно 518 при высоте строки 366 — см. by-width
       ниже), так что платим тем, чего и не было. */
    grid-template-columns:minmax(0,1fr) 44px minmax(0,1fr) 44px minmax(0,1fr);
    column-gap:0; row-gap:24px;
  }

  /* СОСТАВ И КАРТА ПОДНЯТЫ К ЗАГОЛОВКУ ШАГА.

     Справа от «03 Оставь заявку» была пустая полоса во всю высоту
     шапки: заголовок занимает левую четверть строки, а состав с картой
     начинались только под ним. Блок от этого тянулся вниз без нужды.

     Верх приходит на КАПИТЕЛЬ заголовка, а не на коробку шапки: у
     заголовка сверху полуинтерлиньяж, у состава сразу рамка, и по
     коробкам они стояли бы вровень только в разметке.

     СЧИТАЕТСЯ ОТ КЕГЛЯ, А НЕ ВБИТО ЧИСЛОМ. Замерено при кегле 26:
     шапка 40, отбивка тела 10, капитель на 9 ниже верха шапки — итого
     подъём 41. Но кегль заголовка резиновый, clamp(20, 2vw, 26): на
     1280 это 25,6, и вбитое число задрало бы состав выше шапки.
     Поэтому в долях того же кегля: шапка 1,538 от него (40/26),
     капитель 0,346 (9/26), отбивка 10 неизменна.

     ПОДЪЁМ ЖИВЁТ ТОЛЬКО ЗДЕСЬ, С 1280, И ЭТО ИЗ-ЗА НИЗА.

     Поднятая коробка требует на 41 пиксель больше высоты, и если
     столько ей не даёт строка, она забирает их сама — строка растёт, а
     форма остаётся короткой. Ниже 1280 колонок две, форма там 460 и
     ровно поэтому НИЖЕ ростом (меньше переносов): на 1279 замерено
     рассогласование низа в 14 пикселей — состав уезжал под кнопку.
     В трёхколоночной раскладке форма уже (345–372) и, значит, выше
     состава: на 1320 и 1440 низ формы, состава и карты совпадает
     ровно, до пикселя.

     Заголовок не задевается: его текст кончается на 344 пикселе, а
     состав начинается на 565. */
  .order-final-kit,
  .order-final > .order-final-shot{
    margin-top:calc(-1 * (1.192 * var(--step-title-fs) + 10px));
  }

  /* Пояснение — во всю ширину плашки: три колонки, низ у них общий, и
     строка под ними принадлежит всем троим. Состав при этом
     возвращается в свою одну строку: растягивать его на две здесь
     незачем — форма выше него и задаёт строку сама. Разбор обоих
     решений — у тех же правил в блоке 901. */
  .order-final > .slip-note{ grid-column:1 / -1; }
  .order-final-kit{ grid-row:1; }
}


/* Стилей выкладки карточек (.cards-sheet) здесь больше нет: окно со
   стопкой листов убрано вместе с миниатюрой, которой оно открывалось. */

/* Число листов в строке приложенного макета. Стоит рядом с именем, до
   надбавки: сначала «сколько», потом «почём».

   Не приглушено: с тех пор как цена считается по листам, это число —
   причина суммы, а не примечание к имени файла. Серым оно читалось
   подписью, мимо которой глаз идёт. */
.kit-file-sheets {
    flex: none; font-weight: 700; color: var(--ink); white-space: nowrap;
}


/* Стилей плашки «сначала собери карту» здесь больше нет — сама
   плашка убрана (разбор в index.html, над кнопкой отправки). */

