/* Шапка и блоки страницы. Стили самого конструктора приходят из constructor.html
   и здесь НЕ переопределяются — иначе выверенная панель поедет. */
/* Шапка выше обычных 60px: в неё идёт логотип ЦЕЛИКОМ, вместе с рукописным «Azukeri».
   Лого почти квадратное (1042x967), и на 60px слово схлопывалось в неразборчивое пятно —
   читался только цветок. 88px дают знаку 64px высоты, на которых слово уже видно. */
/* --head-h — сколько шапка занимает В ПОТОКЕ: это её высота ПЛЮС верхний отступ,
   на который она отодвинута от края. От него считаются высота первого экрана и
   отступ прокрутки у якорей, поэтому менять высоту капсулы, не тронув эту цифру,
   нельзя — конструктор сразу вылезет за экран. */
:root{ --head-h:74px; --head-gap:10px; --ctr-h-mobile:92dvh; }

html,body{margin:0;}
body{background:var(--bg);color:var(--ink);font-family:var(--f-ui);}
/* ⚠️ ВЫДЕЛЕНИЕ ТЕКСТА — НАШЕ, А НЕ СИНЕЕ ИЗ КОРОБКИ. Правила не было вовсе, и
   браузер красил выделенное своим синим: на странице, где цвета нет нигде, это
   была единственная цветная вспышка — и показывалась она каждый раз, когда
   человек копирует промокод, адрес почты или кличку из отзыва.
   Подложка ПОЛУПРОЗРАЧНАЯ и цвет букв не трогает (владелец выбрал «мягко» из
   трёх примерок 24.08.2026): плотная плашка цветом чернил на длинном абзаце
   выглядела тяжело.
   ⚠️ Цвет живёт в палитре (--sel в ⟦TOKENS⟧ конструктора), а не здесь: тема
   переключается атрибутом, и правило без переменной молча вернуло бы синий.
   Связку стережёт тест в sketch/site.test.js. */
::selection{background:var(--sel);}

/* ── СЕРЫЙ ПРЯМОУГОЛЬНИК ПРИ ТАПЕ ─────────────────────────────────────────
   Владелец с телефона 2026-08-17: «когда жмёшь на элементы, они обводятся в
   прямоугольник». Это подсветка тапа, которую браузер телефона рисует сам —
   заливка во всю площадь ссылки или кнопки. К нашим стилям она отношения не
   имеет, поэтому и не находилась в них.
   ⚠️ Кольцо фокуса (:focus-visible) при этом ОСТАЁТСЯ. Оно появляется только у
   того, кто идёт по странице клавиатурой, и убрать его — значит ослепить
   человека, который иначе не поймёт, где находится. Тап его не вызывает.
   ⚠️ Второй прямоугольник — синее выделение текста при быстром двойном тапе по
   кнопке: палец попадает по надписи, браузер решает, что её выделяют. Поэтому
   у нажимаемого текст не выделяется вовсе; у обычного — выделяется, как везде. */
html{-webkit-tap-highlight-color:transparent;}
a,button,summary,.rv-card,.work{-webkit-user-select:none;user-select:none;}
/* ⚠️ Те, что объявлены через all:unset, — поимённо, и класс НАРОЧНО удвоен.
   Сброс возвращает выделение, а живёт он ниже по файлу: при равном весе побеждал
   бы он, а не это правило. Удвоение делает вес больше, и порядок в файле
   перестаёт что-либо решать. Замер до правки: у .head-cart выходило auto. */
.head-cart.head-cart,.theme-btn.theme-btn,.rv-flip.rv-flip{
  -webkit-user-select:none;user-select:none;}

/* ── крупные мониторы: сайт увеличивается сам ──────────────────────────────
   Сайт нарисован под ширину около 1440. На мониторе 2560 при системном масштабе
   100% ему достаётся вдвое больше пикселей, и он выглядит мелким и пустым:
   владелец смотрел его с браузерным увеличением 200% и не подозревал об этом,
   пока не открыл ссылку на другом домене (Chrome помнит масштаб отдельно для
   каждого адреса).

   zoom, а не transform:scale — принципиально: zoom пересчитывает раскладку, текст
   рисуется в новом кегле и остаётся резким, тогда как transform растянул бы
   готовую картинку. И не увеличение шрифта: кегли поднялись бы, а панель, отступы
   и плитки остались прежними — раскладка поехала бы.

   ⚠️ Медиазапросы считают НАСТОЯЩУЮ ширину окна и увеличением не сбиваются
   (проверено: при zoom 1.5 на 2560 запрос ≥2300 продолжает совпадать). Иначе
   получилась бы петля: увеличили → запрос перестал совпадать → уменьшили.

   ⚠️ ГЛАВНОЕ ПРАВИЛО ПОРОГОВ. Увеличение не добавляет пикселей — оно ОТНИМАЕТ
   место: при zoom 1.5 странице остаётся ширина/1.5. Первому экрану нужно около
   1250 своих пикселей (панель занимает 370 при любой ширине, сцене нужно не
   меньше 800, иначе стена работ наезжает на панель — см. порог 1200 ниже по
   файлу). Отсюда и лестница: каждому экрану дают столько, сколько он выдержит,
   но не больше 1.5.

     окно 1400 → 1.12 → странице 1250
     окно 1560 → 1.25 → 1248
     окно 1750 → 1.40 → 1250
     окно 1900 → 1.50 → 1267
     окно 2560 → 1.50 → 1707

   Владелец просил «чтобы у всех было как на 150%». Буквально это невозможно: на
   ноутбуке 1440 при 1.5 странице осталось бы 960, а на телефоне 260. Поэтому
   лестница — это и есть «максимум, который экран выдерживает».

   Firefox старше 126 zoom не понимает и покажет как раньше. Это не поломка:
   сайт просто останется в прежнем размере.

   ⚠️ Множитель хранится ещё и переменной --zoom. Она нужна не для красоты: блоки
   во всю ширину (витрина, ход к работам) растянуты на 100vw, а vw — это ширина
   ОКНА, которая увеличением не меняется. После zoom 1.12 странице осталось 1272,
   а витрина осталась 1440 — и появилась боковая прокрутка на 94 пикселя. Поэтому
   ниже по файлу их ширина делится на --zoom.

     телефон  до 780   — 1.00, там свой макет, увеличивать нечего
     планшет  780–1400 — 1.00, включая iPad в обеих ориентациях (810…1180)
     ноутбук  1400     — 1.12
     ноутбук  1560     — 1.25
     ПК       1750     — 1.40
     ПК       1900+    — 1.50 */
:root{ --zoom:1; }
/* ⚠️ Лестница спрятана за @supports НЕ для красоты. На --zoom делятся ширины и
   высоты по всему проекту (полоса во всю ширину, окно заказа, сцена). Браузер,
   который zoom не понимает — Firefox старше 126, — увеличения не покажет, а
   переменную бы получил: деления сжали бы ему раскладку без всякого увеличения.
   Под @supports такой браузер оставляет --zoom равным единице, и всё делится на
   единицу, то есть не делится вовсе. */
@supports (zoom:1.5){
  @media (min-width:1400px){ :root{--zoom:1.12; zoom:var(--zoom);} }
  @media (min-width:1560px){ :root{--zoom:1.25; zoom:var(--zoom);} }
  @media (min-width:1750px){ :root{--zoom:1.40; zoom:var(--zoom);} }
  @media (min-width:1900px){ :root{--zoom:1.50; zoom:var(--zoom);} }
}

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

   Приём известен по продуктовым сайтам вроде Linear и Vercel, но здесь смягчён:
   мягкий радиус, тень почти без чернил. Резкая тень и сильное стекло спорят с
   рукодельным характером — сайт про лепку руками, а не про панель управления.

   Размытие требует своего слоя композитора, поэтому за ним следят два правила:
   высота капсулы фиксирована (не растёт от содержимого), а на телефоне размытие
   слабее — там под шапкой едет фотогалерея, и сильный фильтр на слабом железе
   заметно съедает кадры. */
.site-head{position:sticky;top:var(--head-gap);z-index:20;height:64px;
  margin:var(--head-gap) 12px 0;border-radius:18px;
  display:flex;align-items:center;gap:18px;padding:0 16px;
  border:1px solid var(--line);
  background:color-mix(in srgb,var(--panel) 72%,transparent);
  -webkit-backdrop-filter:blur(16px) saturate(1.5);
          backdrop-filter:blur(16px) saturate(1.5);
  box-shadow:0 8px 30px rgba(0,0,0,.07);}
/* Без размытия капсула станет полупрозрачной плашкой, сквозь которую читается
   текст под ней. Там, где фильтра нет, фон делаем плотным. */
@supports not ((backdrop-filter:blur(1px)) or (-webkit-backdrop-filter:blur(1px))){
  .site-head{background:var(--panel);}
}
/* min-height по той же норме 44, что и у якорей: замер давал 45×42 — до нормы не
   хватало двух пикселей по высоте. Логотип ведёт на верх страницы, промах по нему
   на телефоне попадает в пустую шапку и выглядит как «ссылка не работает». */
.site-head .brand{display:flex;align-items:center;text-decoration:none;color:var(--ink);
  min-height:44px;}
/* Логотип — CSS-маска, а не <img>: один файл красится currentColor и живёт в обеих
   темах сам, отдельный белый файл не нужен.
   В webp и втрое мельче оригинала: маске важна ТОЛЬКО альфа, цвет не нужен вовсе,
   а показывается знак в 64px — 192px хватает и на экран с тройной плотностью.
   35.9 КиБ PNG → 9.4 КиБ webp (пересобрать: см. tools/subset_ui_fonts.py рядом
   в коммите, логотип жмётся однострочником из тела коммита). */
/* ⚠️ 50px — не «на глаз», а потолок капсулы: 64 минус две кромки и по 6px воздуха
   сверху и снизу. Больше нельзя, меньше — беда: в знаке нарисовано слово, и на
   44px оно уже съезжает в пятно (на 60 это проверялось раньше и было признано
   пределом читаемости). Меняешь высоту капсулы — пересчитывай и это число. */
.site-head .mark{height:50px;aspect-ratio:277/256;flex:none;background:currentColor;
  -webkit-mask:url(/site/brand/lockup.webp) no-repeat center/contain;
          mask:url(/site/brand/lockup.webp) no-repeat center/contain;}
/* Название прячем от глаз, но НЕ display:none: иначе у единственной ссылки на главную
   не остаётся текста — скринридер прочитает её как «ссылка», а поисковик не увидит имя.
   В логотипе к тому же нарисовано только «Azukeri», без «Trace». */
.site-head .brand span{position:absolute;width:1px;height:1px;overflow:hidden;
  clip-path:inset(50%);white-space:nowrap;}
/* Подпись у логотипа с лапкой. Знак тот же, что на тумблере темы и на пройденном
   шаге в конструкторе (--paw), красится currentColor — значит живёт в обеих темах
   сам и всегда одного тона с текстом рядом.
   14px, а не мельче: пять пятен на меньшем кегле слипаются в кляксу — этим уже
   обжигались, когда лапка ставилась вместо галочки в панели. */
.site-head .promise{color:var(--muted);font-size:var(--t-meta);font-weight:400;margin:0;
  display:flex;align-items:center;gap:7px;}
/* Лапка одна и справа: слева владелец её убрал. Зеркалить нечего — пара имела
   смысл как рамка, одиночный знак ставим в его настоящем начертании, том же, что
   на тумблере темы и на пройденном шаге. */
.site-head .promise::after{content:'';width:14px;height:14px;flex:none;
  background:currentColor;
  -webkit-mask:var(--paw) no-repeat center/contain;
          mask:var(--paw) no-repeat center/contain;}
.site-head .anchors{margin-left:auto;display:flex;align-items:center;gap:16px;}
/* Слова живут в своей обёртке, отдельно от значка заказа и тумблера: на телефоне
   она одна и сжимается, а кнопки остаются на месте (см. правило в мобильном блоке). */
.site-head .anchor-links{display:flex;align-items:center;gap:16px;}
/* Два имени у одного пункта: полное для десктопа, короткое для телефона. Замер на
   390 (variants/head-390-lab.html): полными словами три пункта просят 260px при
   доступных 170, короткими укладываются без обрезки. Владелец 2026-08-19 выбрал
   именно этот вариант — «важно чтобы на телефон влезло тоже», ничего не пряча. */
.nav-short{display:none;}
/* min-height, а не только размер шрифта: сама надпись занимала 32px по высоте при
   норме тап-цели 44. В шапке 68px (мобильная) это не видно глазом, но пальцем
   попадать становится вдвое легче. */
.site-head .anchors a{color:var(--muted);text-decoration:none;font-size:var(--t-ui);
  display:flex;align-items:center;min-height:44px;}
@media (hover:hover){ .site-head .anchors a:hover{color:var(--ink);} }
/* ── заказ в шапке ─────────────────────────────────────────────────────────
   Маленький значок сумки со счётчиком — пример владельца: так сделано у
   palicadnik.ru. Голый значок, без пилюли и подложки: в шапке уже есть кнопка
   темы, и вторая капсула рядом читалась бы парой переключателей.
   ⚠️ Появляется, ТОЛЬКО когда в заказе что-то есть. Пустая корзина в шапке —
   украшение, которое отнимает место у пунктов: на 390 шапке доступно 298px.
   ⚠️ Размер значка 20 при тап-цели 44: цель добирается полем кнопки, как у
   ссылок рядом. Меньше 44 палец промахивается — мерено на телефоне 2026-08-14. */
.head-cart{all:unset;box-sizing:border-box;cursor:pointer;flex:none;position:relative;
  display:flex;align-items:center;justify-content:center;width:44px;min-height:44px;
  color:var(--muted);}
.head-cart[hidden]{display:none;}
/* Ответ пустой корзины — облачко под значком. Живёт в шапке (у неё position
   sticky и свой слой), поэтому прижимается к её низу и едет вместе с ней.
   ⚠️ pointer-events:none — облачко не должно перехватывать следующий тап:
   человек чаще всего сразу жмёт мимо, чтобы его убрать. */
.head-bubble{position:absolute;top:calc(100% + 8px);right:10px;z-index:2;
  pointer-events:none;max-width:min(260px,calc(100vw - 40px));
  padding:10px 14px;border-radius:14px;background:var(--panel);
  border:1px solid var(--line);box-shadow:0 10px 26px rgba(0,0,0,.12);
  animation:bubble .18s ease-out;}
.head-bubble[hidden]{display:none;}
.head-bubble b{display:block;font-size:var(--t-ui);font-weight:600;}
.head-bubble span{display:block;margin-top:2px;font-size:var(--t-meta);color:var(--muted);}
@keyframes bubble{from{opacity:0;transform:translateY(-4px);}to{opacity:1;transform:none;}}
@media (prefers-reduced-motion:reduce){ .head-bubble{animation:none;} }

/* Пустой заказ: значок на месте, но тише — он ничего не обещает и ничем не
   хвастается. Цифры при этом нет вовсе: кружок с нулём выглядел бы поломкой. */
.head-cart.empty{opacity:.45;}
@media (hover:hover){ .head-cart.empty:hover{opacity:.7;} }
.head-cart b[hidden]{display:none;}
.head-cart svg{fill:none;stroke:currentColor;stroke-width:1.5;
  stroke-linecap:round;stroke-linejoin:round;}
/* Лапка на бирке — заливкой, а не линией: в 20px контурный след превращается в
   кашу из шести колечек. Тот же рисунок, что и у --paw в остальных местах. */
.head-cart svg .paw{fill:currentColor;stroke:none;}
/* Счётчик — точкой у ручки сумки, как у всех: цифра поверх значка, а не рядом,
   иначе она читается отдельным словом и тянет на себя ширину шапки. */
/* ⚠️ Кружок мельче значка и отодвинут от ручки: на первой сборке он был 16px в
   самом углу, накрывал дужку сумки — и сумка читалась мусорной корзиной. */
.head-cart b{position:absolute;top:4px;right:3px;min-width:14px;height:14px;
  display:flex;align-items:center;justify-content:center;padding:0 3px;
  border-radius:999px;background:var(--accent);color:var(--on-accent);
  font-family:var(--f-badge);font-weight:700;font-size:10px;line-height:1;}
@media (hover:hover){ .head-cart:hover{color:var(--ink);} }
.head-cart:focus-visible{outline:2px solid var(--accent);outline-offset:2px;border-radius:12px;}
@media (max-width:780px){ .site-head .anchors{gap:10px;} }

/* конструктор — первый экран под шапкой, но НЕ весь документ */
/* ⚠️ Делим высоту окна на --zoom по той же причине, что и ширину витрины: dvh —
   это высота ОКНА, увеличением она не меняется, а странице после zoom достаётся
   меньше. Без деления первый экран становился в полтора раза выше окна, и чтобы
   увидеть обещание с ценой, приходилось крутить (владелец наткнулся сразу). */
.ctr-section{height:calc(100dvh / var(--zoom,1) - var(--head-h));display:flex;position:relative;}

.block{max-width:980px;margin:0 auto;padding:56px 18px;}
/* Шапка липкая, поэтому переход по якорю ставил заголовок секции ПОД неё:
   замер 2026-08-10 — верх заголовка на 56px, низ шапки на 88. Отступ прокрутки
   ровно в её высоту возвращает заголовок на глаза. */
.block,.ctr-section{scroll-margin-top:var(--head-h);}
/* --f-h1, а не --f-title: переменной с таким именем в конструкторе нет, и заголовки
   молча падали на шрифт по умолчанию вместо Playfair */
/* ⚠️ Курсив и вес названы ЯВНО, и это не украшение, а починка.
   Из Playfair у нас загружено ТОЛЬКО курсивное начертание (fonts/ui.css), обычного
   нет вовсе. Заголовок просил font-style:normal и по умолчанию имел вес 700 —
   браузер брал единственный доступный файл, курсивный, и наливал его до 700.
   Получался жирный курсив, которого никто не выбирал: владелец сравнил с образцом
   и заметил («не такой курсивный и не тонкий, а жирный»). 400 — тот вес, который
   он и одобрил в лаборатории. */
.block h2{font-family:var(--f-h1);font-style:italic;font-weight:400;
  font-size:28px;margin:0 0 12px;}
/* Короткая линия над заголовком — зачин раздела (выбор владельца 2026-08-15 из
   трёх способов, лаборатория variants/photos-block2.html).
   ⚠️ Линия принадлежит ЗАГОЛОВКУ, а не краю экрана: начинается там же, где текст,
   и потому читается началом раздела, а не рамкой вокруг него. Отдельной черты
   снизу нет и не нужно — низ раздела закрывает линия следующего.
   ⚠️ Правило общее для ВСЕХ разделов страницы: метка у одного из трёх читалась бы
   случайностью. Если понадобится только у «Какие фото прислать?» — сузить
   селектор до #photos h2. */
.block h2::before{content:"";display:block;width:56px;border-top:2px solid var(--ink);
  margin:0 0 18px;}
/* Заголовку страницы «Работы» зачин не нужен: он первый и единственный, отделять
   его не от чего. */
/* ── РАБОТЫ: витрина ──────────────────────────────────────────────────────
   Вернулась 2026-08-16 вместо убранного раздела — но одна, без сетки на виду и
   без полосы «Смотреть все работы». Гнуть карточки дугой она перестала: изгиб
   выключен в разметке (bend:0), сюда это не касается.
   ⚠️ СЕТКА ВИДНА, ПОКА ВИТРИНА НЕ ПОДНЯЛАСЬ. Раньше она пряталась всегда, и от
   этого было две беды. Первая: на 4G витрина молчит около секунды — только ogl.js
   весит 131 КБ, — и всё это время на её месте зияла пустота (владелец с телефона
   2026-08-16: «на телефоне не появляется сразу»). Вторая, хуже: у кого включено
   «поменьше движения» или нет WebGL, витрина не поднимается ВОВСЕ, и раздел
   «Работы» оставался пустым навсегда.
   Теперь место занимает сама сетка: кадры в ней — миниатюры по 2–7 КБ, они уже
   загружены страницей. Прячется она только тогда, когда витрина правда встала
   (класс .showcase-on вешает скрипт в site/page.html).
   Прячем так, чтобы содержимое осталось доступным: display:none скринридеры
   пропускают вовсе. На странице «Работы» сетка — единственный показ, поэтому
   селектор с #gallery. */
/* ⚠️ ЛЕНТА РАБОТ = ЛЕНТА ОТЗЫВОВ, ТОТ ЖЕ КОД. Работы листаются правилами .rv-row
   и .rv-card (ищи их ниже по файлу): владелец просил «сделай идентично отзывам»
   2026-08-16, а одинаковыми две ленты останутся, только пока правило одно на
   двоих. Заведёшь второе — они разъедутся в первый же вечер.
   Здесь остаётся ровно то, чем работы от отзывов ОТЛИЧАЮТСЯ: у отзыва под кадром
   текст, у работы нет ничего — карточка это и есть сама фотография.
   ⚠️ Здесь же стояли правила витрины-карусели на WebGL (.circular-gallery,
   #showcase, html{overflow-x:clip} ради её полной ширины). Витрина отменена
   вечером 2026-08-16 — «как-то плохо выглядит»; правила убраны целиком, чтобы не
   тянуть за собой мёртвый холст. Сам модуль site/showcase.js цел. */
#gallery .work img{display:block;width:100%;aspect-ratio:4/5;object-fit:cover;
  background:var(--chip);}

/* ── ТЁПЛАЯ ПОЛОСА РАЗДЕЛА ────────────────────────────────────────────────
   Ритм страницы: тёплое через раздел. Тёплые — «Какие фото прислать» и подвал,
   между ними обычные «Отзывы» и «Как проходит заказ» (владелец, 2026-08-17).
   Тёплым помечается разговор мастера с покупателем: просьба про фото и контакты.
   ⚠️ Полоса рисуется ПСЕВДОЭЛЕМЕНТОМ, а не фоном секции: у секции свои поля и
   максимальная ширина, и её фон покрасил бы колонку, а не полосу во всю ширину.
   ⚠️ Из 100vw вычитается --sbw: vw считается ВМЕСТЕ с полосой прокрутки, и без
   вычитания страница получает боковую прокрутку на её толщину. Эта ловушка в
   проекте уже ловила и витрину, и окно заказа. Переменную ставит скрипт в
   site/page.html и пересчитывает на изменение размера окна.
   ⚠️ z-index:-1 кладёт полосу ПОД содержимое, но над фоном страницы; поэтому у
   самой секции задан z-index:0 — без него полоса уехала бы под фон и пропала. */
.warm-band{position:relative;z-index:0;}
.warm-band::before{content:'';position:absolute;z-index:-1;inset:-28px 0;
  left:50%;transform:translateX(-50%);
  /* ⚠️⚠️ ДЕЛИТЬ НА --zoom ОБЯЗАТЕЛЬНО. vw — это ширина ОКНА, и увеличением сайта
     она не меняется: при zoom 1.5 странице остаётся 1270 пикселей, а полоса
     рисовалась на все 1905 и торчала за край. Снаружи это выглядело как пустая
     прокрутка вправо на треть экрана — владелец нашёл её 25.08.2026 на своём
     мониторе. Предупреждение про деление в этом файле было (см. лестницу --zoom),
     но именно эту полосу забыли. */
  width:calc((100vw - var(--sbw,0px)) / var(--zoom,1));background:var(--foot-bg);}

/* ── ПОДВАЛ ────────────────────────────────────────────────────────────────
   Был одной строкой в три ссылки. Стал блоком с колонками — раскладку владелец
   показал на palicadnik.ru (2026-08-16). Содержимое своё: у них внизу каталог
   разделов и страницы «покупателям», у нас одна страница и мастерская на одного
   человека, поэтому колонки — разделы этой же страницы, ходы написать и соцсети.
   Подвал отделён от страницы цветом карточки и кромкой сверху: иначе после
   светлой секции «Условия» он читается её продолжением, а не концом сайта. */
/* ⚠️ У подвала СВОИ цвета, а не общие. Сырость прежнего была не от нехватки
   украшений: он стоял на том же фоне, что страница, и глазу не за что было
   зацепиться — конец сайта читался продолжением «Условий». Тёплое поле решает это
   одним ходом и ничего не весит (владелец выбрал из четырёх, 2026-08-16).
   ⚠️ Именно переменными: цвет вшитый намертво превратил бы тёмную тему в песочное
   пятно посреди тёмной страницы. Тёмная половина не инверсия, а тот же тёплый тон,
   уведённый в глубину. */
:root{
  --foot-bg:#f1ece3; --foot-ink:#2b261f; --foot-muted:#6b6459; --foot-line:#e0d8cb;
}
:root[data-theme="dark"]{
  --foot-bg:#191714; --foot-ink:#ece6dc; --foot-muted:#948b7f; --foot-line:#2b2723;
}
.site-foot{background:var(--foot-bg);padding:34px 18px 30px;
  color:var(--foot-muted);font-size:var(--t-ui);}
/* Знак Azukeri в конце страницы — маской, как в шапке: один файл красится
   currentColor и живёт в обеих темах сам.
   ⚠️ display:block обязателен. Строчным элемент схлопывается в ноль по высоте, и
   маске нечего показывать — знак просто не появляется, без единой ошибки. */
.foot-mark{display:block;height:44px;aspect-ratio:277/256;background:currentColor;
  color:var(--foot-ink);margin:0 0 14px;
  -webkit-mask:url(/site/brand/lockup.webp) no-repeat left center/contain;
          mask:url(/site/brand/lockup.webp) no-repeat left center/contain;}
/* ⚠️ Колонок ЧЕТЫРЕ, а не три: 2026-08-19 добавились «Документы» — оферта и
   политика. При repeat(3) пятый блок срывался на вторую строку и вставал под
   обращением, читаясь его продолжением. Замер: 1040 − 4×24 = 944 на 5.5 долей,
   доля 171px — все колонки выше минимума 120. */
.foot-in{max-width:1040px;margin:0 auto;display:grid;gap:26px 24px;
  grid-template-columns:1.5fr repeat(4,minmax(120px,1fr));}
/* Обращение слева — единственное место в подвале, где говорят голосом, а не
   ярлыками. Кегль тот же, что у ссылок: подвал не спорит с разделами выше. */
.foot-say{margin:0;line-height:1.55;color:var(--foot-ink);}
.foot-col{display:flex;flex-direction:column;}
/* Ярлык колонки. ⚠️ НЕ h2, хотя выглядит заголовком: h2 на этой странице значит
   «раздел», их ровно четыре плюс окно заказа, и тест держит это число. Колонке
   подвала такой вес не нужен — имя группе даёт aria-label у самого <nav>. */
/* ⚠️ Ярлык колонки и ссылки под ним обязаны говорить РАЗНЫМ голосом: пока они были
   одного цвета и одного вида, владелец сразу поймал — «не понятно, кликабельно или
   нет». Курсив Playfair он отверг (2026-08-16), поэтому разводим тем, что осталось:
   ярлык — мелкий, вразрядку, прописными и приглушённый; ссылка — обычным кеглем и
   цветом чернил. Это два признака сразу, кегль и цвет: одного цвета мало, у того,
   кто различает оттенки хуже, список снова станет ровным. */
.foot-lbl{font-size:var(--t-meta);font-weight:600;letter-spacing:.06em;
  text-transform:uppercase;color:var(--foot-muted);opacity:.85;margin:0 0 6px;}
/* Ссылка подвала — 44px по высоте, как и все тап-цели: текстом она 18px, и пальцем
   в неё попадали через раз (замер при разборе телефонной версии 2026-08-14). */
/* Ссылка — цветом чернил, а не приглушённая: она единственное, что здесь можно
   нажать, и должна быть заметнее подписи над собой. */
/* ⚠️ ИНТЕРЛИНЬЯЖ ЗАДАН ЯВНО, и это не вкусовщина. У Comfortaa собственный
   line-height:normal даёт ~15px при кегле 14 — почти без пробела между строками.
   Ссылка, не влезшая в одну строку («Подарочный сертификат», «Политика обработки
   данных»), вставала двумя строками вплотную и читалась слипшейся с соседней.
   Замер 2026-08-19: коробка 30px на две строки. При 1.4 двухстрочная занимает 39,
   однострочная остаётся 30 — колонка не разъезжается.
   Вылезло, когда колонок стало четыре и они сузились со 215 до 171px, но заложено
   это было раньше: «Подарочный сертификат» слипался и до того. */
.site-foot a{color:var(--foot-ink);display:inline-flex;align-items:center;min-height:44px;
  line-height:1.4;text-decoration:none;}
/* ⚠️ Облачко «Почта скопирована!» — рядом со ссылкой, а не тостом внизу экрана:
   человек в этот момент смотрит в подвал, туда и отвечаем. Тем же приёмом и тем
   же видом, что облачко под значком заказа (.head-bubble), — двух разных ответов
   на одной странице быть не должно.
   ⚠️ Колонка подвала получает position:relative, иначе облачко считается от всей
   страницы и уезжает к её левому краю. */
.foot-col{position:relative;}
.foot-bubble{position:absolute;left:0;top:100%;z-index:3;pointer-events:none;
  white-space:nowrap;padding:8px 12px;border-radius:12px;background:var(--panel);
  border:1px solid var(--line);box-shadow:0 10px 26px rgba(0,0,0,.12);
  color:var(--ink);font-size:var(--t-meta);animation:bubble .18s ease-out;}
.foot-bubble[hidden]{display:none;}
.foot-col[hidden]{display:none;}
/* ⚠️ 44px нужны ПАЛЬЦУ. Там, где есть мышь, они делают колонку разреженной:
   четыре ссылки растягивались на 176px пустоты, и подвал выходил выше раздела
   «Условия». Курсору хватает 30 — попасть им можно и в строку текста. */
@media (hover:hover){
  .site-foot a{min-height:30px;}
  /* под мышью ссылка подчёркивается: цвет менять уже некуда — он и так чернильный */
  .site-foot a:hover{text-decoration:underline;text-underline-offset:4px;}
}
.site-foot a:focus-visible{outline:2px solid var(--accent);outline-offset:3px;border-radius:6px;}
.site-foot a[hidden]{display:none;}
/* Нижняя строка: подпись и одна честная оговорка про ручную работу. Отделена
   кромкой и набрана мельче — это уже не навигация, а титры. */
/* Титры: подпись слева, знак справа. ⚠️ Знак прижат к правому краю через auto —
   не отступом: отступ пришлось бы пересчитывать при каждой правке строки. */
.foot-legal{grid-column:1/-1;margin:0;padding-top:16px;border-top:1px solid var(--foot-line);
  display:flex;flex-wrap:wrap;align-items:center;gap:6px 22px;
  font-size:var(--t-meta);color:var(--foot-muted);}
.foot-legal .foot-mark{margin:0 0 0 auto;height:34px;}
@media (max-width:780px){
  /* На 390 четыре колонки не живут: замер даёт 354px на всё, то есть по 88 на
     колонку — «Какие фото прислать» разваливается в столбик по одному слову.
     Кладём обращение во всю ширину, а колонки по две в ряд. */
  .foot-in{grid-template-columns:repeat(2,minmax(0,1fr));gap:18px 18px;}
  .foot-say-wrap{grid-column:1/-1;}
  .site-foot{padding:28px 16px 24px;}
}
/* ⚠️ Тонкий подвал страницы «Работы». Там нет ни колонок, ни скрипта конструктора,
   который расставляет адреса по константам, — только знак, ссылка и подпись, и
   всё это ставится по центру. Отдельным классом, а не через :not(:has(...)):
   селектор :has() тут ничего не даёт, кроме зависимости от свежести браузера. */
.site-foot--thin{display:flex;flex-wrap:wrap;gap:6px 16px;
  justify-content:center;align-items:center;padding:26px 18px 24px;}
.site-foot--thin .foot-mark{height:34px;margin:0;}

/* ── обещание на пустой сцене ─────────────────────────────────────────────
   Разметка приезжает из site/promise.html внутрь placeholder конструктора, поэтому
   живёт и умирает вместе с ним: выбрал шнур — обещание ушло, сцена свободна.
   Правила пишем только для сайта; в самом конструкторе этого блока нет. */
.pl-promise{display:flex;align-items:center;justify-content:center;gap:56px;
  width:100%;max-width:900px;text-align:left;}
.pl-say{max-width:420px;}
/* Кегль обещания крупнее заголовков секций (28px): это первое, что читает человек,
   и оно обязано звучать громче, чем «Готовые работы» ниже. */
.pl-promise h1{font-family:var(--f-h1);font-style:italic;font-weight:600;font-size:46px;
  line-height:1.06;letter-spacing:-.015em;margin:0 0 16px;color:var(--ink);}
/* Два факта: цена и срок. Счётчик «27 сделано» и строка «Соберите свой справа»
   убраны владельцем 2026-08-13 — на первом экране должно остаться только то, что
   решает: сколько стоит и как быстро.
   Числа набраны шрифтом цены: все цифры интерфейса говорят одним голосом. */
.pl-facts{list-style:none;display:flex;gap:24px;margin:22px 0 0;padding:0;}
.pl-facts b{font-family:var(--f-price);font-size:30px;font-weight:400;display:block;
  line-height:1.1;letter-spacing:.01em;}
.pl-facts span{font-size:var(--t-meta);}
/* ── стена работ вместо одного фото ──────────────────────────────────────
   Двенадцать работ в двух колонках, едут вверх с разной скоростью. Одно фото
   не доказывало главную фразу обещания («двух одинаковых не бывает»), дюжина
   доказывает её раньше, чем человек дочитает строку.

   Едут ДОРОЖКИ, а не 48 картинок: два transform на всю стену. Такую анимацию
   браузер уводит на композитор, и она не отнимает кадров у конструктора, который
   рисует сцену рядом на главном потоке. JavaScript здесь не участвует вовсе. */
.pl-wall{width:400px;height:560px;flex:none;perspective:1200px;
  -webkit-mask-image:linear-gradient(to bottom,transparent,#000 15%,#000 85%,transparent);
          mask-image:linear-gradient(to bottom,transparent,#000 15%,#000 85%,transparent);}
/* Внутренний блок УЖЕ коробки. Маска режет ровно по её краю, а повёрнутая
   плоскость за край вылезает — без запаса правый столбец обрубается по живому. */
/* align-items:flex-start ОБЯЗАТЕЛЕН: по умолчанию флекс растягивает дорожку на
   высоту коробки, и тогда translateY(-50%) — половина ВИДИМОЙ высоты, а не половина
   содержимого. Стена ползёт вдесятеро медленнее (3.5 px/с вместо 35) и раз в круг
   прыгает на стыке. Проверено живьём, не теория. */
.pl-wall-in{display:flex;align-items:flex-start;gap:16px;height:100%;width:88%;
  margin-inline:auto;transform-style:preserve-3d;
  transform:rotateX(14deg) rotateY(-14deg) translateZ(-120px);}
/* Круг вдвое короче прежнего (был 82 с \ 119 с), потому что в дорожке теперь ШЕСТЬ
   кадров вместо двенадцати: у каждой свои шесть, общих работ нет. Скорость на глаз
   та же — половина пути за половину времени. Менять число, не пересчитав, нельзя:
   ход стены задаётся отношением «кадров в дорожке \ длительность». */
.pl-track{flex:1;min-width:0;will-change:transform;animation:pl-drift 41s linear infinite;}
/* Разный круг у соседки обязателен: с одинаковым стена едет одним полотном и
   перестаёт читаться как набор разных работ. */
.pl-track+.pl-track{animation-duration:60s;}
/* Отступ плиткам даёт margin, а не gap: с gap половина дорожки не совпадает с
   пробежкой на половину отступа, и стык уползает с каждым кругом. */
/* Фотография как есть, только со скруглёнными углами. Ни подложки, ни тени:
   решение владельца 2026-08-13 после того, как я перебрал обрезку кадра, выравнивание
   фона и вырезку обвеса — каждая правка тянула за собой новую кайму или ободок.
   Серый оттенок фона у части снимков остаётся: он в самих фотографиях, и лечится
   он пересъёмкой, а не кодом. */
.pl-track img{display:block;width:100%;height:auto;margin-bottom:16px;border-radius:14px;
  transition:opacity .3s,transform .3s;}
@keyframes pl-drift{to{transform:translateY(-50%);}}
/* Под курсором дорожка замирает, плитка выходит вперёд, соседние гаснут.
   ⚠️ ТОЛЬКО ПОД МЫШЬЮ. На тачскрине :hover прилипает к тому, чего коснулись, и
   держится до следующего касания: тап по стене останавливал бы её насовсем и гасил
   одиннадцать работ из двенадцати — первый экран выглядел бы сломанным. Та же
   ловушка, что поймал владелец 2026-08-16 в конструкторе («сброс в какой-то другой
   обводке»), только следствие крупнее. */
@media (hover:hover){
  .pl-wall:hover .pl-track{animation-play-state:paused;}
  .pl-wall:hover img{opacity:.45;}
  .pl-wall img:hover{opacity:1;transform:translateZ(64px);}
}
/* «Поменьше движения»: стена остаётся наклонённой сеткой и стоит. Не display:none —
   работы человек всё равно видит, просто они никуда не едут. */
@media (prefers-reduced-motion:reduce){
  .pl-track{animation:none;}
}

/* ── узкая стена: ноутбуки и телефоны ────────────────────────────────────
   Порог 1200, а не 780. Считается он не от окна, а от СЦЕНЫ: панель
   конструктора занимает свои 370px при любой ширине, и на окне 1024 сцене
   остаётся 599px, на 800 — всего 375. Стена в 400px там вылезала за край
   сцены и наезжала на панель (замер 2026-08-12: на 800px правый край стены
   554 при крае сцены 398).
   Ниже 780px эти же правила продолжают работать — там их дополняет мобильный
   блок, который перекладывает и текст. */
@media (max-width:1200px){
  /* Зазор и кегль здесь тоже меньше десктопных, и это не украшательство: на окне
     800 сцене остаётся 375px, а колонка текста не сжимается ниже своей
     минимальной ширины — при кегле 46 это 231px, и блок вылезал за сцену на 19px.
     Русские слова длинные, ужимается именно шрифт. */
  .pl-promise{gap:20px;}
  .pl-promise h1{font-size:34px;}
  /* Поворот вдвое слабее десктопного: коробка узкая, и на -14deg правый
     столбец вылезает за неё и обрубается маской. */
  .pl-wall{width:126px;height:318px;perspective:700px;}
  .pl-wall-in{width:94%;gap:8px;transform:rotateX(10deg) rotateY(-6deg);}
  /* Те же половины от прежних 54\76 — см. причину у десктопного правила. */
  .pl-track{animation-duration:27s;}
  .pl-track+.pl-track{animation-duration:38s;}
  .pl-track img{margin-bottom:8px;border-radius:8px;}
}
/* Сцена светлая в обеих темах (см. --stage в конструкторе), поэтому и текст обещания
   всегда тёмный — как подсказка и оговорка рядом с ним. */
.pl-promise h1,.pl-facts b{color:#1a1a1a;}
.pl-facts span{color:#6b6d73;}

/* ── работы: сетка ───────────────────────────────────────────────────────
   Сетка — основа блока, а не дубль витрины: её видят поиск, скринридеры и
   телефоны без WebGL. Ссылка вокруг картинки даёт открыть кадр целиком —
   ровно то, чего не умеет холст.
   Селекторы без #gallery: тот же фрагмент сетки стоит и на странице «Работы»,
   где секции с этим id нет. */
/* ⚠️ Сетки колонками больше нет. Она жила на странице «Работы» со всеми 27
   кадрами; страница убрана целиком 2026-08-17 — «оставим эту небольшую карусельку
   и всё». Осталась одна лента примеров на главной, а она едет правилами .rv-row
   (см. ниже): раскладку ей задавать здесь нельзя, иначе разъедется с отзывами. */
.works{list-style:none;margin:0;padding:0;}
.work a{display:block;border-radius:14px;overflow:hidden;}
.work img{display:block;width:100%;height:auto;background:var(--stage);}
.work a:focus-visible{outline:2px solid var(--accent);outline-offset:3px;}
/* ⚠️ Здесь пряталась сетка главной (#gallery .works): на главной её показывала
   витрина, и блок читался повтором. Раздел работ убран целиком 2026-08-15 —
   прятать стало нечего. Сама сетка жива на странице «Работы», правила выше общие. */

/* ⚠️ Здесь жил «Ход к остальным работам» (.works-more) — полоса во всю ширину
   с курсивной ссылкой и счётчиком. Раздел работ убран со страницы 2026-08-15,
   а ход к 27 работам перевешен пунктом в шапку. */

/* ⚠️ Здесь жили заголовок и подпись страницы «Работы» (main.block h1, .works-note).
   Страница убрана 2026-08-17, правила ушли вместе с ней. */


/* ── отзывы: лента карточек ──────────────────────────────────────────────
   Вид выбран владельцем из двух («лента карточек топ»). Листается пальцем,
   карточка прилипает к краю.
   ⚠️ 3D-витрину сюда не приспосабливать: она сделана под картинки, текст в ней
   не почитаешь, и она тянет 133 КиБ WebGL ради того, что делает scroll-snap. */
/* ⚠️ ЛЕНТА РАБОТ ЕДЕТ ЭТИМИ ЖЕ ПРАВИЛАМИ. Владелец 2026-08-16: «сделай идентично
   отзывам» — и это не «похоже», а буквально один код на две ленты. Разводить их
   нельзя: разъедутся в первый же вечер, когда кто-то поправит одну. */
.rv-row,#gallery .works{display:flex;gap:12px;overflow-x:auto;scroll-snap-type:x mandatory;
  -webkit-overflow-scrolling:touch;scrollbar-width:none;list-style:none;
  /* ⚠️ Лента идёт от края до края экрана, а поля берёт на себя прокрутка: иначе
     первая карточка прижата к полю блока, а последняя упирается в него же, и
     лента выглядит обрезанной. */
  margin:20px -18px 0;padding:2px 18px 4px;}
.rv-row::-webkit-scrollbar,#gallery .works::-webkit-scrollbar{display:none;}
/* 78%, а не вся ширина: соседняя карточка обязана выглядывать, иначе край экрана
   читается концом раздела и листать никто не пробует. */
.rv-card,#gallery .work{flex:0 0 78%;scroll-snap-align:start;
  background:var(--panel);border:1px solid var(--line);border-radius:18px;
  overflow:hidden;display:flex;flex-direction:column;}
/* ⚠️ Кадры вертикальные, карточка 4:5 — режется верх и низ. Замером по всем
   восьми: середина ловит и питомца, и обвес. Появится кадр, где не так, —
   ему свой object-position, а не общий сдвиг всем. */
.rv-card img{display:block;width:100%;aspect-ratio:4/5;object-fit:cover;background:var(--chip);}
.rv-text{padding:14px 16px 16px;display:flex;flex-direction:column;gap:10px;flex:1 1 auto;}
.rv-text p{margin:0;line-height:1.55;font-size:var(--t-ui);}
/* ⚠️ Лапка ПОСЛЕ клички (просьба владельца 2026-08-17): знак ставит точку в
   подписи, как печать под письмом. Рисуется маской из --paw — тем же файлом, что
   метит шаги заказа и лежит на тумблере тем; заведётся вторая лапка — они молча
   разойдутся. */
.rv-who{margin:0;color:var(--muted);font-size:var(--t-meta);
  display:flex;align-items:center;gap:7px;}
.rv-who::after{content:'';width:15px;height:15px;flex:none;
  background:currentColor;opacity:.55;
  -webkit-mask:var(--paw) no-repeat center/contain;
          mask:var(--paw) no-repeat center/contain;}
/* ⚠️ Лента фокусируется с клавиатуры — иначе до прокручиваемой области не
   добраться вовсе: браузер сам фокус ей не даёт. Кольцо рисуем своё, чтобы оно
   шло по краям ленты, а не по обрезанному краю экрана. */
.rv-row:focus-visible,#gallery .works:focus-visible{outline:2px solid var(--accent);outline-offset:-2px;border-radius:18px;}
/* ── ЛЕНТУ ТЯНУТ МЫШЬЮ ─────────────────────────────────────────────────────
   Здесь стояли две круглые кнопки со стрелками (владелец 2026-08-24: «мне не
   нравятся кнопочки переключения»), потом — свой курсор-лапка. Лапку отменил он
   же в тот же вечер: свой курсор ВСЕГДА отстаёт на кадр от настоящего, и на
   быстрых движениях это видно шлейфом. Осталась суть: ленту берут рукой и
   бросают, а курсор — системный.
   ⚠️ ТОЛЬКО там, где есть настоящее наведение и точный указатель. Пальцем лента
   листается сама, и подменять палец нечем.
   ⚠️ Класс `drag` на <html> ставит скрипт: без него ни протяжки, ни броска нет,
   и обещать рукой то, чего не будет, нельзя. */
@media (hover:hover) and (pointer:fine){
  /* ⚠️ Прилипание СНЯТО, и только для мыши (владелец: «чтобы оно не было резким…
     чтобы летело как бы каруселью»). Виновато было именно scroll-snap-type:x
     mandatory: на отпускании браузер сам дёргал ленту к ближайшей карточке и
     гасил бросок. На телефоне прилипание остаётся — без него свайп разболтается. */
  html.drag .rv-row,html.drag #gallery .works{scroll-snap-type:none;cursor:grab;}
  html.drag .rv-row.тянут,html.drag #gallery .works.тянут{cursor:grabbing;}

  /* ── СТРЕЛКИ ПО КРАЯМ ЛЕНТЫ ────────────────────────────────────────────
     Способов листать два, и они не спорят: кто тянет — тянет, кто ищет кнопку —
     находит. Тянуть догадывается не каждый, а у покупателя нет задачи
     разбираться в нашей ленте.
     ⚠️ Вид — «стекло» (выбор владельца 24.08.2026 из шести): полупрозрачная
     поверхность с размытием, как у шапки сайта. Кнопка лежит поверх ЧУЖИХ
     фотографий — светлых, тёмных, пёстрых; плотная плашка на них спорит, а
     стекло берёт цвет снимка под собой и остаётся читаемым на любом.
     ⚠️ Всплывают ТОЛЬКО под мышью: на телефоне заняли бы место и перекрыли кадр,
     а листается он пальцем. */
  .rv-wrap{position:relative;}
  .rv-flip{all:unset;box-sizing:border-box;position:absolute;z-index:3;
    top:calc(50% - 8px);transform:translateY(-50%);width:46px;height:46px;
    border-radius:50%;display:flex;align-items:center;justify-content:center;
    font-size:22px;line-height:1;color:var(--ink);cursor:pointer;
    background:color-mix(in srgb, var(--panel) 62%, transparent);
    border:1px solid color-mix(in srgb, var(--ink) 14%, transparent);
    -webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px);
    box-shadow:0 4px 18px rgba(0,0,0,.12);opacity:0;
    transition:opacity .18s ease,background .16s ease,transform .16s cubic-bezier(.2,.8,.2,1);}
  .rv-wrap:hover .rv-flip{opacity:1;}
  /* ⚠️ И по фокусу тоже: кто ходит табом, мышью не наводит, и кнопка, которую
     видно только под курсором, для него не существует. */
  .rv-flip:focus-visible{opacity:1;outline:2px solid var(--accent);outline-offset:3px;}
  .rv-flip:hover{background:color-mix(in srgb, var(--panel) 88%, transparent);}
  .rv-flip:active{transform:translateY(-50%) scale(.94);}
  /* Доехали до края — кнопка гаснет совсем: нажимать некуда, а серая кнопка
     поверх фотографии выглядит грязью. */
  .rv-flip[disabled]{opacity:0!important;cursor:default;}
  .rv-flip.rv-prev,.rv-flip.wk-prev{left:-10px;}
  .rv-flip.rv-next,.rv-flip.wk-next{right:-10px;}
}
@media (min-width:781px){ .rv-card,#gallery .work{flex-basis:300px;} }

/* ── ОТЗЫВЫ: стопка ──────────────────────────────────────────────────────
   ⚠️ Отзывы и примеры обвесов расходятся ровно в одном: у отзывов карточки чуть
   повёрнуты. Всё остальное — лента, карточка, прилипание — общее (см. .rv-row
   выше), и разводить это дальше нельзя.
   Поворот КРОШЕЧНЫЙ, около градуса: больше читается «коллажем из наклеек»,
   который в проекте уже отвергали. Ленте добавлен воздух сверху и снизу — иначе
   повёрнутые углы срезаются краем прокручиваемой области.
   ⚠️ Кладка в три колонки на компьютере ЗДЕСЬ БЫЛА И УБРАНА (владелец,
   2026-08-17): «слишком много места жрёт». Восемь отзывов разом занимали почти
   весь экран и отжимали вниз всё остальное. Лента и на компьютере остаётся
   лентой — со стрелками, как было. */
#reviews .rv-row{padding-top:10px;padding-bottom:16px;}
/* ⚠️ ТЕНИ ПОД КАРТОЧКАМИ НЕТ (владелец, 2026-08-17: «она как будто пульсирует»).
   Так и было: тень 20px под повёрнутой карточкой при листании то набегала на
   соседнюю, то отходила, и на глаз это читалось миганием. Поворот оставлен —
   стопку держит он, а не тень. Возвращать тень только вместе с проверкой на
   живом листании пальцем, а не на неподвижном снимке. */
#reviews .rv-card:nth-child(odd){transform:rotate(-1deg);}
#reviews .rv-card:nth-child(even){transform:rotate(1.2deg);}

/* ── какие фото прислать ─────────────────────────────────────────────────
   Раскладка выбрана владельцем из трёх (лаборатория variants/photos-block.html,
   2026-08-15). Прежняя — «фото слева, текст справа, четыре абзаца с одинаковым
   отступом» — читалась одной серой массой, о чём он и сказал.
   Здесь работают две вещи. Первая: ритм. Вступление тише текста, две просьбы
   разделены волосяной линией, вывод — курсивом с воздухом перед ним. Вторая:
   картинка показывает ровно то, о чём просит текст, — примету крупно.
   ⚠️ Считалось от ТЕЛЕФОНА: владелец смотрит сразу на нём. */
.ph-intro{margin:0;color:var(--muted);font-size:var(--t-ui);line-height:1.55;}
/* ⚠️ Замер на 390: без этого текст упирается в нижний край кадров, а подпись
   под ними сливается со вступлением — оба тихого серого цвета. */
.ph-media{margin-bottom:20px;}
.ph-asks{margin:22px 0 0;}
.ph-asks p{margin:0;line-height:1.6;}
.ph-asks p + p{margin-top:16px;padding-top:16px;border-top:1px solid var(--line);}
.ph-lead{font-family:var(--f-h1);font-style:italic;font-size:21px;line-height:1.35;margin:26px 0 0;
  display:flex;align-items:center;gap:12px;}
/* ⚠️ Лапка ПОСЛЕ вывода — выбор владельца 2026-08-18 из шести мест, которые
   стоят рядом в variants/photos-section-lab.html (раскладку он оставил нынешнюю).
   Тот же приём, что у подписи отзыва (.rv-who): печать под письмом, а не
   украшение. Рисунок общий, --paw; заведётся вторая лапка — они молча разойдутся.
   ⚠️ align-items:center, а не baseline: у пустой коробки с маской своей базовой
   линии нет, по baseline печать проваливается под строку. */
.ph-lead::after{content:'';width:26px;height:26px;flex:none;
  background:currentColor;opacity:.45;transform:rotate(14deg);
  -webkit-mask:var(--paw) no-repeat center/contain;
          mask:var(--paw) no-repeat center/contain;}
/* Кадр обвеса — целиком и небольшой.
   ⚠️ Пробовали и отменили (владелец, 2026-08-15): две квадратные рамки, наведённые
   вплотную на мордочки. Примету было видно отлично, но обрезанная в упор голова
   читается макросъёмкой, а не изделием — «фотки оч плохо смотрятся». Кроп сюда
   не возвращать; если понадобится показать примету крупнее, увеличиваем сам кадр,
   а не режем его.
   ⚠️ Ширину держит СТИЛЬ, а не атрибут width у картинки: 880px в разметке нужны
   браузеру, чтобы не прыгала верстка при загрузке. */
/* ⚠️ На телефоне кадр ОБТЕКАЕТСЯ текстом: вступление встаёт справа от него, дальше
   строки идут во всю ширину сами. Прежде кадр стоял отдельной строкой, и справа
   от него оставалась пустота в половину экрана — на неё владелец и указал.
   display:contents у обёрток нужен, чтобы кадр и абзацы оказались соседями в
   потоке: без этого обтекать нечего, они лежат в разных коробках. */
.ph-body,.ph-media{display:contents;}
.ph-shot{float:left;width:148px;margin:18px 16px 10px 0;}
.ph-shot img{display:block;width:100%;height:auto;border-radius:14px;}
/* ⚠️ Вывод обязан начинаться ПОД кадром: иначе курсив вклинивается сбоку узкой
   колонкой и перестаёт читаться выводом. */
.ph-lead{clear:both;}
/* На широком экране колонка текста не должна растягиваться на все 980px:
   строка длиннее ~70 знаков перестаёт читаться. */
@media (min-width:781px){
  /* На широком экране обтекание не нужно — там колонка кадра и колонка текста.
     Колонка кадра ровно по кадру, остальное тексту: при равных половинах
     небольшая фотография висела посреди пустой половины экрана. */
  .ph-body{display:grid;grid-template-columns:auto minmax(0,1fr);gap:34px;align-items:start;}
  .ph-media{display:block;margin-bottom:0;}
  .ph-shot{float:none;width:240px;margin:6px 0 0;}
  /* Предел ставим ВСЕЙ колонке, а не отдельным абзацам: пока он висел на
     вступлении и выводе, они рвались раньше просьб, и колонка выглядела рваной,
     а сами просьбы уезжали за 90 знаков в строке — читать тяжело. */
  .ph-body > :last-child{max-width:66ch;}
}

/* ── запись ──────────────────────────────────────────────────────────── */
/* ⚠️ КАРТОЧКА ЗАКАЗА ТЁПЛАЯ, и чередование от этого не ломается — ломалось бы,
   будь она полосой. Ритм держится ФОРМОЙ, а не только цветом:
       «Какие фото» — тёплая ПОЛОСА во всю ширину,
       «Как проходит заказ» — обычный фон секции и тёплая КАРТОЧКА с полями,
       подвал — снова тёплая полоса.
   Между полосой и карточкой остаётся фон страницы, между карточкой и подвалом —
   28px того же фона (замер). Именно эти просветы и разделяют тёплые пятна;
   сделаешь раздел заказа полосой — они слипнутся в сплошной беж, и керамика,
   единственное яркое на сайте, потеряется. */
.card-order{border:1px solid var(--foot-line);border-radius:18px;padding:22px;
  background:var(--foot-bg);color:var(--foot-ink);}
.card-order .card-note{color:var(--foot-muted);}
.card-order dt{color:var(--foot-muted);}
/* ── шаги заказа ──────────────────────────────────────────────────────────
   Раздел перестроен 2026-08-17: вместо перечня фактов, который на четыре пятых
   повторял первый экран и окно заказа, — путь от сборки до посылки.
   Номера рисуем сами, а не браузером: свой кружок читается меткой шага, а не
   пунктом списка, и совпадает с кружками шагов в конструкторе — там человек
   видел ровно такие же, пока собирал.
   ⚠️ Список остаётся <ol>: порядок шагов — это смысл, а не оформление, и
   скринридер обязан объявить «список из пяти». */
.steps{list-style:none;counter-reset:шаг;margin:0 0 20px;padding:0;
  display:flex;flex-direction:column;gap:14px;}
.steps li{counter-increment:шаг;position:relative;padding-left:34px;
  line-height:1.55;font-size:var(--t-ui);}
/* ⚠️ Знак шага — ФИРМЕННАЯ ЛАПКА, та же, что метит пройденный шаг в конструкторе
   и лежит на бегунке тумблера тем. Человек уже видел её, пока собирал обвес, и
   здесь она говорит «это то же место, тот же мастер». Номер в кружке, стоявший
   тут раньше, был служебным значком и ничего про мастерскую не сообщал.
   Список остаётся <ol>: порядок шагов — смысл, и скринридер обязан его объявить,
   даже когда цифры не нарисованы. */
.steps li::before{content:'';position:absolute;left:0;top:3px;
  width:22px;height:22px;background:currentColor;opacity:.5;
  -webkit-mask:var(--paw) no-repeat center/contain;
          mask:var(--paw) no-repeat center/contain;}
.steps b{font-weight:600;}
/* Приписка внутри шага: отдельным абзацем и тише самого шага. Это не продолжение
   действия, а запасной ход для того, у кого действие не вышло. */
.steps .step-aside{display:block;margin-top:8px;color:var(--foot-muted);}
.card-order .facts{display:flex;flex-wrap:wrap;gap:10px 32px;margin:0 0 16px;}
.card-order .facts div{display:flex;flex-direction:column;gap:2px;}
.card-order dt{color:var(--muted);font-size:var(--t-meta);}
.card-order dd{margin:0;font-size:18px;}
.card-note{color:var(--muted);margin:0 0 18px;line-height:1.5;}
/* Ход во ВКонтакте — тише главной кнопки, но это не сноска: у кого Telegram без
   VPN не открывается, тот должен увидеть, что заказать всё-таки есть чем. */
.vk-link{display:inline-flex;align-items:center;min-height:44px;margin-top:10px;
  color:var(--muted);font-size:var(--t-meta);text-decoration:none;
  text-decoration-line:underline;text-underline-offset:4px;}
@media (hover:hover){ .vk-link:hover{color:var(--ink);} }
.vk-link:focus-visible{outline:2px solid var(--accent);outline-offset:3px;border-radius:6px;}
.vk-link[hidden]{display:none;}
/* min-height 44, а не просто отступы: при кегле 14 и padding 12 кнопка выходила
   39px по высоте — ниже нормы тап-цели. Это главная кнопка страницы, и жать её
   будут пальцем на телефоне (замер 2026-08-14 при разборе телефонной версии). */
.tg-btn{display:inline-flex;align-items:center;justify-content:center;min-height:44px;
  padding:10px 22px;border-radius:999px;background:var(--accent);
  color:#fff;text-decoration:none;font-size:var(--t-ui);}
@media (hover:hover){ .tg-btn:hover{filter:brightness(1.08);} }
.tg-btn:focus-visible{outline:2px solid var(--ink);outline-offset:3px;}
/* В тёмной теме акцент светлеет, и белый текст на нём даёт 1.05 при норме 4.5 —
   надпись исчезает. Привязка к АТРИБУТУ, а не к системной теме: тумблер медиа-
   выборку не трогает, и на светлой системе с переключением в тёмную кнопка
   ломалась (замер 2026-08-07). Системная тема остаётся как значение по умолчанию.
   Красим текст в тёмный, а не гасим акцент: кнопка — единственное цветное пятно. */
/* Системной выборки здесь больше нет: с 2026-08-14 тема светлая по умолчанию для
   всех и меняется только тумблером, значит и починка кнопки нужна ровно там. */
:root[data-theme="dark"] .tg-btn{color:var(--bg);}

/* ── подарочный сертификат ────────────────────────────────────────────────
   Раздел заведён 2026-08-17 по просьбе владельца (пример — palicadnik.ru), но
   устроен иначе: у них номинал кладут в корзину и оплачивают на сайте, у нас
   выбор номинала собирает сообщение и уводит в чат — тем же ходом, что состав
   обвеса. Поэтому здесь нет ни корзины, ни формы, ни поля «своя сумма»: сумму,
   выходящую за три плитки, проще назвать словами в переписке.

   Снимок настоящего сертификата — главный довод раздела: человек видит, что
   именно получит его друг, а не читает обещание. Отсюда и раскладка: на
   компьютере лист слева и разговор справа, на телефоне лист сверху.
   ⚠️ Плитки номиналов — ТА ЖЕ клавиша, что в конструкторе: они дописаны прямо
   в общее правило .thumb,.shapechip,.subtab… внутри constructor.html, а не
   срисованы сюда похожими. Один предмет в восьми местах — и он остаётся одним,
   пока живёт от одного правила. Здесь только раскладка и кегль. */
.card-order.gift{display:grid;gap:22px 28px;position:relative;}
/* ⚠️ Следы лежат ПОД содержимым и не ловят нажатия: это фон, а не украшение
   поверх текста. Прозрачность 10% — на пределе заметности намеренно: дорожка
   должна находиться взглядом, а не спорить со снимком сертификата.
   ⚠️ Карточку не режем overflow:hidden — у снимка своя тень в 28px, её край
   карточки обрезал бы. Поэтому следы держатся внутри границ сами. */
.gift-paw{position:absolute;z-index:0;pointer-events:none;background:currentColor;
  opacity:.10;-webkit-mask:var(--paw) no-repeat center/contain;
  mask:var(--paw) no-repeat center/contain;}
.gift-shot,.gift-say{position:relative;z-index:1;}
.gift-paw.a{width:40px;height:40px;right:4%;top:9%;transform:rotate(24deg);}
.gift-paw.b{width:34px;height:34px;right:13%;top:31%;transform:rotate(30deg);}
.gift-paw.c{width:29px;height:29px;right:23%;top:52%;transform:rotate(36deg);}
@media (min-width:720px){
  .card-order.gift{grid-template-columns:300px minmax(0,1fr);align-items:start;}
}
.gift-shot{margin:0;}
/* Лист чуть повёрнут и с тенью — как карточки отзывов рядом: это снимок вещи,
   лежащей на столе, а не картинка, вклеенная в блок. Поворот мелкий: на большем
   углу тонкая рамка сертификата начинает выглядеть кривой печатью. */
.gift-shot img{display:block;width:100%;max-width:300px;height:auto;margin-inline:auto;
  border-radius:3px;box-shadow:0 10px 28px rgba(0,0,0,.14);
  transform:rotate(-1.4deg);}
:root[data-theme="dark"] .gift-shot img{box-shadow:0 10px 28px rgba(0,0,0,.5);}
@media (max-width:719px){ .gift-shot img{max-width:214px;} }
@media (prefers-reduced-motion:reduce){ .gift-shot img{transform:none;} }
.gift-say>p{line-height:1.55;}
.gift-say>p:first-child{margin-top:0;}
.gift-lbl{font-size:var(--t-meta);font-weight:600;letter-spacing:.06em;
  text-transform:uppercase;color:var(--foot-muted);margin:18px 0 8px;}
/* ⚠️ Раскладка и кегль плиток номинала переехали в constructor.html: такие же
   плитки завелись в окне заказа, а окно живёт и на отдельной странице
   конструктора, где этого файла нет. Здесь остался только отступ раздела —
   в окне он другой. Грань клавиши у обоих общая, из правила .thumb,.shapechip… */
#gift .gift-sums{margin:0 0 16px;}

/* ── кнопка темы: тумблер, у которого бегунок — лапка ──────────────────────
   Прежде здесь стоял кружок 32px: закрашенный в светлой теме, полый в тёмной.
   Владелец сказал «не очень выглядит», и это правда — знак ничего не обещал и
   не читался как переключатель. Теперь пилюля 46×26 с бегунком: он перебегает
   слева направо, и лапка на нём выворачивается вместе с темой.

   ⚠️ --paw объявлен ЗДЕСЬ, а не только в конструкторе. На site/works.html
   конструктора нет вовсе, а кнопка темы в шапке есть, и без переменной от
   бегунка осталась бы пустая заливка. Рисунок обязан СОВПАДАТЬ с тем, что в
   sketch/constructor.html (там он метит пройденный шаг и занятое место) — на
   это есть тест: двух разных лапок в проекте быть не должно. */
:root{
  --paw:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><ellipse cx='4.6' cy='9' rx='2.6' ry='3.3'/><ellipse cx='10' cy='5.4' rx='2.8' ry='3.6'/><ellipse cx='15.8' cy='5.9' rx='2.8' ry='3.6'/><ellipse cx='20.6' cy='9.6' rx='2.6' ry='3.3'/><path d='M12.2 13.2c3.7 0 6.7 2.8 6.7 5.6 0 2.2-1.9 3.4-3.8 3.4-1.2 0-1.9-.5-2.9-.5s-1.7.5-2.9.5c-1.9 0-3.8-1.2-3.8-3.4 0-2.8 3-5.6 6.7-5.6z'/></svg>");
}
.theme-btn{all:unset;box-sizing:border-box;cursor:pointer;flex:none;position:relative;
  width:46px;height:26px;border-radius:13px;border:1px solid var(--line);
  display:flex;align-items:center;justify-content:flex-start;padding:0 3px;
  transition:transform 140ms cubic-bezier(.23,1,.32,1),border-color 160ms ease;}
/* Пилюля 26px высотой — до нормы 44 растягиваем только область нажатия,
   прозрачным полем поверх. */
.theme-btn::after{content:'';position:absolute;top:50%;left:50%;width:46px;height:44px;
  transform:translate(-50%,-50%);border-radius:13px;}
@media (hover:hover){ .theme-btn:hover{border-color:var(--ink);} }
/* нажатие: кнопка чуть поджимается, иначе тумблер кажется картинкой */
.theme-btn:active{transform:scale(.94);}
.theme-btn:focus-visible{outline:2px solid var(--accent);outline-offset:3px;}
/* Бегунок: кружок чернилами, лапка выедена из него цветом шапки.
   ⚠️ Ход 18px, а не 20: внутри пилюли остаётся 46 − 2 (рамка) − 6 (поля) = 38,
   бегунок 20, значит проехать он может ровно 18. На 20 он наезжал на рамку.
   Кривая .32/.72/0/1 и 300 мс — выбор владельца из четырёх, показанных живьём
   (он заметил доводку в конце хода, посмотрел варианты без неё и оставил эту). */
.theme-btn i{width:20px;height:20px;border:none;border-radius:50%;background:var(--ink);
  position:relative;transition:transform 300ms cubic-bezier(.32,.72,0,1);}
.theme-btn i::after{content:'';position:absolute;inset:3.5px;background:var(--panel);
  -webkit-mask:var(--paw) center/contain no-repeat;
          mask:var(--paw) center/contain no-repeat;}
.theme-btn[aria-pressed="true"] i{transform:translateX(18px);}
/* «Поменьше движения»: бегунок оказывается на месте, но не едет туда. */
@media (prefers-reduced-motion:reduce){
  .theme-btn,.theme-btn i{transition-duration:1ms;}
}
/* ── подсказка «с чего начать»: первая плитка шнура ────────────────────────
   Спека: docs/superpowers/specs/2026-08-13-first-screen-start-hint-design.md
   Человек открывает сайт и видит движущуюся стену работ — она заметнее всего,
   а нажимать надо не на неё. Через 2,5 с первая плитка шнура всплывает и по её
   рамке пробегает светлый участок.

   ⚠️ Подсказка идёт БЕСКОНЕЧНО и гаснет только когда началась сборка. Сначала
   было три круга и отмена от любого действия — владелец сказал: «пересmаёт
   работать, когда я начинаю что-то делать, пусть живёт, пока не начну собирать».
   Он прав: прокрутка и клик по чему угодно ещё не значат, что человек понял, с
   чего начинать.

   Правила живут в site.css, а НЕ в конструкторе: конструктор уезжает в
   лабораторию и в артефакт, где подсказка не нужна (то же, что с обещанием).
   Класс вешает скрипт в site/page.html, он же его и снимает. */
@property --hint-a{syntax:'<angle>';inherits:false;initial-value:0turn}
.thumb.start-hint{animation:hint-lift 2600ms cubic-bezier(.4,0,.2,1) infinite;}
/* Под курсором подсказка отступает: у плитки есть свой подъём на :hover, и пока
   идёт анимация, он перебит — рука уже здесь, звать больше некуда. */
@media (hover:hover){
  .thumb.start-hint:hover,
  .thumb.start-hint:hover::before,
  .thumb.start-hint:hover::after{animation:none;}
  .thumb.start-hint:hover::before,.thumb.start-hint:hover::after{opacity:0;}
}
/* Бегущая обводка — конус минус середина. Угол крутится через @property:
   обычную переменную не анимировать, а поворачивать саму рамку нельзя — она не
   круглая, прямоугольник перекосит. */
.thumb.start-hint::before{content:'';position:absolute;inset:-2px;padding:2px;
  border-radius:inherit;pointer-events:none;
  background:conic-gradient(from var(--hint-a),transparent 0 52%,
    color-mix(in srgb,var(--accent) 75%,transparent) 70%,transparent 86%);
  -webkit-mask:linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);
          mask:linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);
  -webkit-mask-composite:xor;mask-composite:exclude;
  animation:hint-spin 2600ms linear infinite;}
/* Тень подъёма — за плиткой (z-index:-1), поэтому картинку шнура не трогает. */
.thumb.start-hint::after{content:'';position:absolute;inset:6px 4px -2px;
  border-radius:14px;z-index:-1;pointer-events:none;opacity:0;
  background:color-mix(in srgb,var(--ink) 30%,transparent);filter:blur(7px);
  animation:hint-shadow 2600ms cubic-bezier(.4,0,.2,1) infinite;}
@keyframes hint-lift{0%,72%,100%{transform:translateY(0)}36%{transform:translateY(-4px)}}
@keyframes hint-shadow{0%,72%,100%{opacity:0;transform:scale(.94)}
  36%{opacity:.5;transform:scale(1)}}
@keyframes hint-spin{to{--hint-a:1turn}}
/* «Поменьше движения»: подсказка остаётся, но стоит — обводка вместо пульсации. */
@media (prefers-reduced-motion:reduce){
  .thumb.start-hint{animation:none;}
  .thumb.start-hint::before{animation:none;background:none;
    border:2px solid var(--accent);-webkit-mask:none;mask:none;padding:0;}
  .thumb.start-hint::after{animation:none;display:none;}
}

/* ── принт «АЗУКЕРИ» на лотке сборки ───────────────────────────────────────
   Рисунок НЕ набран шрифтом: это картинка владельца (его витринная антиква,
   похожей в свободных шрифтах нет), из которой вынута альфа — белые буквы стали
   непрозрачными, розовый фон прозрачным. Дальше работает маской: цвет и
   прозрачность наши, начертание его.

   ⚠️ NO-REPEAT, и это не украшательство. Сначала я пустил картинку плиткой — она
   НЕ бесшовная, и на стыке слова наезжали друг на друга (владелец увидел сразу,
   я проверил склейкой 2×2: «АЗУК» ложится поперёк «АЗУКЕРИ»). Одна копия на
   cover шва не даёт вовсе.

   ⚠️ Маска ВЕКТОРНАЯ, и это тоже не эстетство. Растровая версия при cover
   растягивалась на ширину лотка и на экране двойной плотности мылила: тысяча
   точек превращалась в две тысячи. Буквы обведены по контуру (137 контуров,
   marching squares по альфе исходника, допуск 0.7px — на глаз не отличить от
   оригинала) и теперь резкие при любом размере и любой плотности. Заодно
   дешевле: 53 КиБ текстом против 90 КиБ картинкой той же чёткости, а на сервере
   с gzip — 19 КиБ. Пересобрать можно скриптом из тела коммита.

   ⚠️ Цвет ЗАШИТ, а не взят из палитры. Лоток светлый в обеих темах (см. --stage
   в конструкторе), и в тёмной теме --ink светлый — принт был бы белым по белому.
   На этом я потерял вечер показов: владелец говорил «плохо видно», а дело было
   не в проценте. Ровно поэтому же надписи на сцене имеют зашитый #6b6d73 — см.
   .placeholder,.approx в конструкторе.

   Красный — решение владельца 2026-08-13 и единственная краска на всём сайте:
   палитра выбрана бесцветной (акцент равен цвету текста). Взят красный сердечка
   из каталога (#d5231f, bead_round_red), а не случайный: он уже есть в кадрах
   работ. Прозрачность подбиралась глазом: 60 → 30 → 16 → 9 → 24. На девяти
   владелец сравнил два кадра и сказал «мыльно и цвет светлее» — и это не про
   резкость: у тонких штрихов при разнице в три-восемь пунктов от фона сглаженные
   края сливаются с ним, и знак читается размытым, каким бы чётким ни был контур.
   Отсюда правило: ниже примерно 15% этот принт перестаёт выглядеть чётким
   независимо от качества маски.

   Стоит 5% — решение владельца, принятое ПОСЛЕ того, как он увидел знак на
   девяти и услышал про эту границу: ему важнее, чтобы буквы не тянули внимание,
   чем чтобы держали край. Не «чинить» это число, приняв за недосмотр.

   На пяти процентах знак даёт #f4ebeb по лотку #f6f6f6 — разница в два пункта по
   красному и одиннадцать по остальным. Это у самой границы различимого: на
   хорошей матрице тёплый оттиск виден, на телефоне с автояркостью или на дешёвой
   TN-панели может пропасть совсем. Если однажды покажется, что принт «исчез» —
   дело не в коде.

   Принт под обвесом сделан ПОДЪЁМОМ СОДЕРЖИМОГО, а не отрицательным индексом у
   самого принта: так он не зависит от isolation на лотке и от того, кто ещё
   заведёт себе слой композитора.

   ⚠️ Ловушка была не в стилях, а в этом самом комментарии: лишний закрывающий
   знак в его середине оборвал блок раньше времени, остаток текста стал мусором
   для парсера, и тот съел вместе с ним ПРАВИЛО НИЖЕ. Со стороны это выглядело
   как «принт не рисуется на 30%, а на 100% рисуется», и я ушёл искать
   несуществующую беду с z-index. Наступал дважды: второй раз — когда описывал
   первый, набрав опасную пару символов дословно. Писать её здесь нельзя.
   Проверять такое надо не глазами, а списком: document.styleSheets → cssRules;
   правила в списке нет — значит сломан текст выше, а не сам стиль.

   Класс .mono вешает скрипт в site/page.html — только когда началась сборка.
   Заодно это ленивая загрузка: пока правило не совпало, браузер маску не тянет. */
.stage.mono::before{content:'';position:absolute;inset:0;pointer-events:none;
  background:#d5231f;opacity:var(--mono-op,.05);
  -webkit-mask:url(/site/brand/mono-print.svg) no-repeat center / cover;
          mask:url(/site/brand/mono-print.svg) no-repeat center / cover;}
/* Обвес, подпись и обещание — над принтом. #scale статичный и держится на
   transform, поэтому ему нужен ещё и position, иначе z-index к нему не пристанет. */
.stage.mono > #scale{position:relative;}
.stage.mono > #scale,.stage.mono > .approx,.stage.mono > .placeholder{z-index:1;}

.visually-hidden{position:absolute;width:1px;height:1px;overflow:hidden;
                 clip-path:inset(50%);white-space:nowrap;}

@media (max-width:780px){
  /* Воздух между разделами. Было 56 снизу и 56 сверху — 112px пустоты между
     последней строкой раздела и заголовком следующего, седьмая часть экрана
     телефона (замер на 390; владелец: «почему такой большой отступ перед
     условиями?»).
     Сперва убавил неровно (40 сверху, 28 снизу) — по правилу «над заголовком
     воздуха больше, чем под ним». Владелец попросил поровну, стало 40/40.
     ⚠️ И всё равно смотрелось плохо, но не там, где я мерил. Дело не в разрыве
     между разделами, а в ЗАЧИН-ЛИНИИ: под ней до заголовка 18px, а над ней
     оставалось 60 — линия висела в пустоте вместо того, чтобы принадлежать
     заголовку. Владелец на это и указал: «полоска далеко слишком от этого абзаца».
     28/28 дали над линией 36 при 18 под ней — и этого ОПЯТЬ не хватило.
     ⚠️ Замер по коробкам врал. Настоящую пустоту видно только по чернилам (замер
     по снимку): над линией 62px, под ней 29. Разницу в 26px даёт невидимый запас
     строки — у курсивного вывода 21px с межстрочным 1.35 под последней строкой
     остаётся полпустой строки, которой нет ни в одном getBoundingClientRect.
     Поэтому поля стыка урезаны отдельно: сумма полей между разделами даёт
     12px коробки ≈ 38px чернил сверху против 29 снизу — линия читается зачином
     заголовка, а не чертой посреди пустоты.
     ⚠️ Правило только для СТЫКА разделов (.block + .block). Верхнее поле первого
     раздела и нижнее у последнего трогать нельзя: сверху к нему примыкает
     конструктор, снизу подвал, и там 28px нужны. */
  .block{padding:28px 18px;}
  .block + .block{padding-top:4px;}
  /* ⚠️ Тёплая полоса на телефоне НЕ вылезает за свой раздел. Нарисована она с
     запасом (inset:-28px), чтобы на компьютере захватывать воздух вокруг: там у
     разделов поле 56px, и запас ложится в пустоту. А здесь поле стыка 4px, и
     полоса накрывала первые 28px СЛЕДУЮЩЕГО раздела вместе с его зачин-линией —
     линия рисуется у заголовка и просто пропадала. Владелец увидел это на
     телефоне 2026-08-17: «над подарочным сертификатом нет полосы».
     ⚠️ Дело не в порядке слоёв: у раздела с полосой свой контекст наложения
     (z-index:0), и он целиком рисуется ПОВЕРХ статичного содержимого соседа
     ниже. Спорить z-индексами тут нечего — полосе просто незачем заходить на
     чужую землю. Воздух ей даёт собственное поле, оно же возвращает 28px
     стыка, которые съедало правило выше. */
  .warm-band{padding-block:28px;}
  /* ⚠️ Только верх и низ. Полное inset:0 перебивает left:50%, на котором держится
     центровка полосы во всю ширину, и полоса уезжает влево на пол-экрана —
     проверено снимком. */
  .warm-band::before{top:0;bottom:0;}
  /* обещание — h1 страницы: display:none выкинул бы его из поиска и из скринридера
     вместе с пикселями. Прячем тем же приёмом, что и название в логотипе: из потока
     вон (шапка остаётся в одну строку), из документа — нет */
  .site-head .promise{position:absolute;width:1px;height:1px;overflow:hidden;
    clip-path:inset(50%);white-space:nowrap;}
  /* на телефоне капсула ниже и прижата к краям: 74px съедали бы первый экран, ради
     которого всё и затевалось. 56 + 8 отступа хватает, чтобы знак остался
     разборчивым. Размытие вдвое слабее десктопного — под шапкой едут фотографии
     работ, и сильный фильтр на телефоне обходится дороже, чем выглядит. */
  :root{--head-h:64px; --head-gap:8px;}
  .site-head{height:56px;margin:var(--head-gap) 8px 0;border-radius:16px;
    padding:0 12px;gap:8px;
    -webkit-backdrop-filter:blur(9px) saturate(1.3);
            backdrop-filter:blur(9px) saturate(1.3);}
  .site-head .mark{height:42px;}
  .site-head .anchors{gap:10px;}
  /* 11px, а не 12: с тремя пунктами двенадцатый кегль не влезает даже короткими
     словами (замер на 390). Тап-цель держит min-height:44px, она не меняется. */
  .site-head .anchors a{font-size:11px;}
  /* «Как делается» ломается на две строки внутри своей ссылки, и шапка становится
     двухэтажной. Запрещаем перенос ВНУТРИ ссылок, ужимаем отступы, а остаток
     уводим в боковую прокрутку — так все три раздела остаются достижимы. */
  .site-head .anchors a{white-space:nowrap;}
  /* ⚠️ Прокручиваются ТОЛЬКО слова, а корзина и тумблер закреплены. С третьим пунктом
     («Сертификат», просьба владельца 2026-08-19) замер на 390: капсуле достаётся
     280px, из них 90 забирают две кнопки и 40 зазоры — на три слова остаётся 150 при
     нужных 228. Уместить их укорачиванием нельзя, поэтому остаток уходит в боковую
     прокрутку. ⚠️ Прокручивать всю капсулу НЕЛЬЗЯ: первым за край уезжает значок
     заказа, а он обязан висеть всегда (см. разметку). */
  /* ⚠️ min-width:0 нужен ОБОИМ: и капсуле, и обёртке слов. У флекс-элемента
     min-width по умолчанию auto, то есть «не уже своего содержимого», и без этого
     сжиматься отказывается не обёртка, а сама .anchors — а вместе с ней уезжает
     за экран и всё, что в ней лежит. Замерено на 390: без этой строки страница
     всё так же едет вбок (457px при 375). */
  .site-head .anchors{min-width:0;}
  .site-head .anchor-links{min-width:0;overflow-x:auto;scrollbar-width:none;}
  /* ⚠️ На телефоне слова КОРОТКИЕ, и только так три пункта помещаются: замер на 390
     (variants/head-390-lab.html) — капсуле достаётся 280px, две кнопки забирают 90,
     зазоры 20, словам остаётся 170. Полные просят 260, короткие с шрифтом 11px
     укладываются без обрезки. Прятать пункт молчаливой обрезкой нельзя: он уезжает
     целиком, и кажется, что его нет вовсе. Прокрутка выше остаётся страховкой. */
  .site-head .anchor-links .nav-long{display:none}
  .site-head .anchor-links .nav-short{display:inline}
  .site-head .anchor-links{gap:8px;}
  .site-head .anchor-links::-webkit-scrollbar{display:none;}
  .site-head .head-cart, .site-head .theme-btn{flex:none;}
  @media (max-width:360px){
    .site-head{padding:0 10px;gap:6px;}
    .site-head .anchors{gap:8px;}
    /* ⚠️ 6px, а не 8: на 360 трём коротким словам не хватало ровно двух точек
       (замер живьём). Ниже 6 не опускать — слова начинают слипаться. */
    .site-head .anchor-links{gap:6px;}
    .site-head .anchors a{font-size:11px;}
  }
  /* 92% высоты, а не 100: снизу виден край следующего блока и понятно, что сайт длиннее.
     min-height:0 обязателен: в мобильном стиле конструктора (перенесённом на .ctr-section)
     есть min-height:100vh — это отдельное от height свойство, и порядок подключения
     стилей его не перебивает, только явный сброс здесь. */
  /* 36px в вычитании — тот самый просвет снизу, ради которого высота и занижена:
     без него шапка 68px + 92dvh секции дают ровно 100dvh, и «край следующего блока»
     исчезает. Раньше шапка была 60px и просвет случайно оставался в 8px. */
  /* ⚠️ ВЫСОТА ПО СОДЕРЖИМОМУ, и секция больше НЕ отдельное окно прокрутки. Так было
     до 2026-08-14: секция имела фиксированные 92dvh и собственный overflow-y:auto,
     чтобы флекс не сжимал превью. Побочный эффект оказался хуже болезни — палец на
     телефоне крутил внутренности секции, а страница стояла на месте: владелец
     первым делом на это и наткнулся («главная рамка с каруселью стоит»).
     Теперь прокрутка одна, страничная. Превью не сжимается, потому что сжимать
     нечего: высота секции равна содержимому. */
  .ctr-section{height:auto;min-height:0;overflow:visible;}
  /* мобильная раскладка конструктора (перенесённая с body) рассчитана на то, что скроллится
     сама страница: там body{height:auto} и высота ничем не ограничена, поэтому 44vh превью +
     вся панель просто растягивают документ. На сайте у .ctr-section ФИКСИРОВАННАЯ высота
     (92dvh), контент в неё не помещается, а .stage — флекс-элемент со flex-shrink:1 из
     базового правила конструктора (мобильный блок его не сбрасывает) — единственный, кто
     сжимается, и превью давит до пары десятков пикселей. Чиним тем же приёмом, что и на
     десктопе: секция сама становится скролл-контейнером, превью не сжимается и держится
     сверху через свой sticky — теперь он наконец получил секцию как границу. */
  .ctr-section .stage{flex-shrink:0;}
  /* Пока на сцене обещание — она листается вместе со страницей: прилипшая пустая
     сцена и была тем самым «стоит на месте». Как только собран обвес, сцена
     прилипает под шапкой: выбирая бусину, человек видит, что получается.
     Метку building ставит render() конструктора. */
  /* ⚠️ relative, а НЕ static: внутри сцены обещание лежит абсолютом (inset:0), и
     сцена обязана оставаться для него точкой отсчёта. Со static обещание с ценой
     и стеной оторвалось и легло поверх панели — владелец увидел это сразу
     («всё поехало»). relative в потоке не липнет, но систему координат держит. */
  .ctr-section .stage{position:relative;}
  /* ══ КОНСТРУКТОР ЗАНИМАЕТ РОВНО ОДИН ЭКРАН, ПОКА ИДЁТ СБОРКА ═════════════════
     Требование владельца 2026-08-14: «чтобы покупатель, куда бы ни опускался, всегда
     видел сцену с обвесом и одновременно — этап, который он выбирает». В одной
     страничной прокрутке это недостижимо: сцена 371px и панель 523–677px в экран 844
     не помещаются (нехватка по шагам 198/127/281/85), и кто-то обязательно
     закрывает другого. Выполняется только так: сцена сверху неподвижна, панель
     листается в своей части.

     ⚠️ Эту схему уже отвергали (85e9f1c) — но за побочный эффект: секция забирала
     весь жест, и СТРАНИЦА не двигалась вовсе, «главная рамка с каруселью стоит на
     месте». Здесь этого нет, и вот почему:
       · overscroll-behavior НЕ ставим: панель домотала своё — жест уходит странице;
       · сцена не прокручиваемая, палец по ней листает сразу страницу (замер: 0 → 900);
       · панель часто помещается целиком, и тогда жест сразу страничный (замер: 2092).

     ⚠️ Только при body.building. До выбора шнура в сцене лежит обещание, оно считано
     под свою высоту (318px содержимого) и живёт в потоке — первый экран не трогаем. */
  body.building .ctr-section{
    /* ⚠️ svh, а НЕ dvh. dvh — «динамическая» высота окна: на телефоне браузер прячет
       и показывает адресную строку прямо во время прокрутки, окно то растёт, то
       сжимается — и вместе с ним пересчитывалась коробка конструктора. Владелец
       увидел это сразу: «конструктор ужимается», «окно то открывается, то
       закрывается». На компьютере этого не видно вовсе, адресная строка не ездит.
       svh — высота при ПОКАЗАННОЙ обвязке браузера, она постоянна: коробка стоит
       намертво, а когда обвязка уезжает, снизу просто открывается чуть больше
       следующего блока.
       Делим на --zoom по той же причине, что и всё остальное: это высота ОКНА,
       увеличением она не меняется, а странице после zoom достаётся меньше.
       ⚠️ Вычитаем 8, а не 24. Двадцать четыре оставлялись «краем следующего блока»,
       чтобы человек видел: страница длиннее. В этой раскладке они не нужны: снизу и
       так есть стрелка «↓», а на телефоне при уехавшей адресной строке открывается
       ещё десятков шесть пикселей следующего блока (в этом и смысл svh). Разбор
       высоты на 390×844 до сбора запаса: окно 844 → шапка 64, секция 756, пусто 24;
       внутри секции поля 24, сцена 380, зазоры 24, стрелка со своим зазором 31 —
       панели оставалось 309. */
    height:calc(100svh / var(--zoom,1) - var(--head-h) - 8px);min-height:0;overflow:hidden;
    /* поля и зазоры ужаты с 12 до 8: на телефоне это не читается как теснота, но
       возвращает панели 16px, а она тут самое дефицитное место */
    padding:8px;gap:8px;}
  /* ⚠️ top:auto обязателен: правило выше делает сцену sticky с отступом шапки, а у
     НЕлипкой сцены забытый top не прижимает, а СДВИГАЕТ — сцена уезжала вниз и
     наезжала на панель (замер: сцена 40–340 при панели с 280). */
  /* 380 — ВЫБОР ВЛАДЕЛЬЦА из пяти, примеренных пальцем. Обмен прямой: чем выше
     полоса, тем крупнее обвес и тем меньше остаётся палитре. Замеры на 390×844:
       260 → обвес 204px, панели видно 427   ·   300 → 244/387
       340 → 284/347                          ·   380 → 324/307  ← взято
     ⚠️ Но НЕ жёстким числом: 380 — это ПОТОЛОК, а не константа. Жёстко заданная
     полоса на высоком экране занимает пятую часть, а на коротком треть — и панели
     не остаётся ничего: «Далее» уходила под край, а шторка выбора детали налезала на
     сцену тем сильнее, чем короче экран (замер: 12% сцены на 844, 31% на 700).
     45svh при 844 и есть ровно 380, поэтому на высоком телефоне ничего не меняется,
     а на коротком сцена уступает панели. */
  :root{ --scene-h:min(380px, 45svh); }
  body.building .ctr-section .stage{position:relative;top:auto;flex:0 0 auto;
    height:var(--scene-h);}
  /* ⚠️ Шторка теперь ЗНАЕТ ПРО СЦЕНУ. Раньше она была просто 52% высоты окна и про
     сцену не знала — оттого и наезжала. Её место — ровно то, что ниже сцены, и
     налезать больше нечем (замер: было 68px закрытой сцены, стало 0). */
  body.building .sheet{max-height:calc(100svh / var(--zoom,1) - var(--head-h) - 24px - var(--scene-h));}
  /* min-height:0 — иначе флекс не даёт элементу стать НИЖЕ содержимого, и внутренняя
     прокрутка не включается вовсе: панель просто вылезает за секцию */
  body.building .ctr-section .panel{flex:1 1 auto;min-height:0;overflow-y:auto;}
  /* ⚠️ ЛИПКОСТИ СЦЕНЫ БОЛЬШЕ НЕТ, и возвращать её нельзя. Она была нужна, пока
     конструктор тянулся страничной прокруткой: сцена прилипала под шапкой, чтобы не
     уехать. Теперь секция — ровно один экран, сцена и так всё время на месте.
     Правило стояло НИЖЕ нового и перебивало его (та же сила селектора, позже в файле):
     сцена оставалась sticky с отступом 72px и висела на 72px ниже верха секции —
     под шапкой зияла пустая полоса. Заодно из-за неё панель когда-то уезжала под
     сцену: непрозрачный лоток закрывал «Далее» и половину палитры. */
  /* Панель обязана быть позиционированной, иначе она не спорит с липкой сценой:
     непозиционированный элемент рисуется ниже любого позиционированного, чем бы
     он ни был. relative без z-index — ровно то, что нужно: панель встаёт выше
     сцены по порядку в разметке и НЕ заводит свой слой.
     ⚠️ Не давать сюда z-index. С ним панель становится контекстом наложения, и её
     строка цены (z-index:22) перестаёт перебивать шторку (z-index:20), лежащую
     снаружи панели: проверено живьём — цена уезжала за шторку и пропадала. */
  .ctr-section .panel{position:relative;}
  /* ⚠️ СТРЕЛКИ «↓» БОЛЬШЕ НЕТ, и возвращать не надо. Она говорила «страница длиннее»,
     пока конструктор тянулся страничной прокруткой и край следующего блока было не
     видно. Теперь при сборке конструктор — ровно один экран, а на телефоне снизу и так
     выглядывает следующий блок, когда уезжает адресная строка. Владелец: «надо бы
     убрать эту маленькую некрасивую стрелочку». */
  /* строка цены на sketch/constructor.html — position:fixed к вьюпорту (там body и есть
     единственный скролл); сборщик превращает это в position:absolute к низу секции (см.
     tools/build_site.py), что верно, пока секция сама не скроллится. Теперь секция —
     скролл-контейнер, и низ секции при частичной прокрутке уезжает за экран вместе с
     absolute-элементом. sticky держит строку у низа ВИДИМОЙ части секции, пока листаешь
     палитру, и всё равно не может вылезти за границы секции — подвал сайта не перекрывает. */
  .ctr-section .pricebar{position:sticky;bottom:0;}
  /* ══ СТРОКА ЦЕНЫ — НИЖНЯЯ ПОЛОСА ЭКРАНА ═════════════════════════════════════
     Владелец: «может можно плашку закрепить прямо снизу телефона красиво, чтобы оно
     было прям внизу телефона, а не как плашка поверх». Она и правда читалась
     отдельным предметом, лежащим на палитре: своя карточка, свои скругления, своя
     тень, и прилипала она к низу ПАНЕЛИ, то есть посреди экрана.
     Теперь это полоса самого окна: во всю ширину, вплотную к нижнему краю, без
     скруглений и тени, отделена одной линией. Так же ведёт себя строка при открытой
     шторке — раньше это был особый случай, теперь общее правило, и особый случай
     исчез вместе с расхождением.
     ⚠️ Панели нужен запас снизу: полоса перекрывает её нижний край, и без запаса
     последний ряд палитры до конца не долистать. */
  body.building.haspay .ctr-section .pricebar,
  body.sheet-open .ctr-section .pricebar{
    position:fixed;left:0;right:0;bottom:0;margin:0;z-index:22;
    /* ⚠️ Ни рамки, ни линии сверху. Сперва полоса отделялась линией в один пиксель —
       владелец показал снимком и попросил убрать: на светлом фоне она читается швом
       поперёк экрана, а полоса и так отделена тем, что стоит вплотную к краю. */
    border-radius:0;box-shadow:none;border:0;
    padding:8px 16px calc(10px + env(safe-area-inset-bottom));}
  body.building.haspay .ctr-section .panel{padding-bottom:96px;}
  /* ⚠️ Ушли ниже конструктора — строка цены уходит с ней вместе. Иначе на экране два
     напоминания об одном: внизу цена с «Готово», слева карточка с обвесом. Метку
     ctr-away ставит miniCheck() по тому же условию, по которому показывает карточку. */
  body.ctr-away .ctr-section .pricebar{display:none;}
  /* 104px внизу секции конструктор резервировал под ту самую строку цены, вынутую из потока.
     Строка вернулась в поток — резерв стал пустотой, а sticky отсчитывает низ от ВНУТРЕННЕЙ
     границы контента, и цена зависала на 104px выше края, посреди панели. */
  .ctr-section{padding-bottom:12px;}
  /* Обещание на телефоне: столбцом, фото первым — вещь показывается до того, как
     человек дочитает. Всё считано под ВЫСОТУ сцены: на 390×844 ей достаётся 369px,
     и первая сборка (кегль 28, фото 150) вылезала на 159px — заголовок с фактами
     просто обрезало. Отступ самого placeholder тоже режем: конструктор задаёт ему
     40px с каждой стороны, из-за чего строка фактов ужималась до 254px и «уже
     сделано» рвалось пополам. */
  .ctr-section .placeholder{padding:14px;}
  /* Стена оставила раскладку десктопной: текст слева, колонки справа — выбор
     владельца 2026-08-12 из трёх показанных живьём (одна лента поперёк / две
     полосы навстречу / колонки как на ПК). Цена выбора принята им же: колонка
     текста ужимается до 168px, заголовок идёт в четыре строки, факты встают
     столбиком. Столбцом сверху вниз блок больше не строится. */
  .pl-promise{gap:12px;text-align:left;max-width:none;}
  .pl-promise h1{font-size:24px;margin:0;}
  .pl-say{flex:1;min-width:0;max-width:none;}
  /* Колонка текста здесь 168px — два факта в строку не встают, ставим столбиком
     и кладём подпись рядом с числом, а не под ним. */
  .pl-facts{flex-direction:column;gap:6px;margin:10px 0 0;}
  .pl-facts li{display:flex;align-items:baseline;gap:6px;}
  /* nowrap обязателен: колонка текста здесь 168px, и «от 1900 ₽» ломалось после
     числа — рубль уезжал на вторую строку один. Переносится пусть подпись. */
  .pl-facts b{display:inline;font-size:18px;white-space:nowrap;}
  .pl-facts span{font-size:10px;}
  /* Сама стена здесь уже узкая — правила выше, в блоке 1200px */
}
