

/* ============================================================
   TOKENS
   ============================================================ */
/* ==========================================================================
   KAPIKA CORE: токены, контейнер, база, утилиты
   Desktop-first. Лестница брейкпоинтов (max-width): 1240 · 1024 · 900 · 768 · 560 · 400
   База (без @media) = 1240–1920, контейнер капается на 1680.
   Шрифт: Nunito 400;500;600;700;800 (Google Fonts). Иконки: Tabler icons webfont.
   ========================================================================== */

/* ---------- ТОКЕНЫ ---------- */
:root{
  /* синий (акцент/бренд) */
  --blue:#005EB8; --blue-d:#00498f; --blue-t:#E6F1FB;
  /* зелёный (действие/корзина) */
  --green:#A4D65E; --green-d:#9DCB5C; --green-t:#EAF3DE;
  /* ЧЕМ ПИШУТ НА ЛАЙМЕ. Белый текст на --green давал 1.70:1 (на ховерном
     --green-d 1.89:1) при пороге AA 4.5:1 для текста и 3:1 для иконок.
     Провал на всех 20 экранах, а не особенность одной страницы.
     --green-ink: 8.26:1 на --green и 7.44:1 на --green-d. Значение приехало
     с главной (там тёмный ink на лайме жил с самого начала, см. .button--loud)
     и теперь одно на проект: .button--primary, .cartbtn, кнопка поиска шапки,
     активная пилюля «В наличии». */
  --green-ink:#173208;
  /* ГЛУБОКИЙ ЗЕЛЁНЫЙ: ЛАЙМ, КОТОРЫЙ ВИДНО НА БЕЛОМ. Первым потребителем стало
     промо мега-меню (.header__promo--green): карточка по построению белый
     текст на цвете, а на прежнем #7fb53f это 2.45:1 (иконка 54px и заголовок
     19px не проходят даже порог 3:1). Тот же тон, опущенная светлота →
     5.05:1 на белом; синее промо рядом (--blue) даёт 6.38:1.
     ВТОРОЙ ПОТРЕБИТЕЛЬ (ЭТОТ РАУНД): ГРАФИКА НА БЕЛОМ. Сам --green на белом
     даёт 1.70:1, а WCAG 1.4.11 требует 3:1 от любой фигуры, которая НЕСЁТ
     СМЫСЛ. Лаймовая точка пройденного шага в таймлайне заказа и лаймовая
     линия между шагами и есть ровно такая фигура: ими показано, докуда дошёл
     заказ. Правило: лайм остаётся ЗАЛИВКОЙ (на нём пишут --green-ink), а
     ГРАНИЦУ фигуры и ТОНКУЮ ЛИНИЮ на белом красит --green-deep. */
  --green-deep:#55792A;
  /* текст */
  --ink:#2E3138;   /* заголовки, сильный текст */
  --ink2:#41454B;  /* основной body-текст */
  --muted:#676C74; /* вторичный/мета, AA-контраст (см. notes) */
  /* поверхности и линии */
  --bg2:#F3F6FA;   /* бледная панель/зебра */
  --page:#EDF1F7;  /* фон страницы (body) */
  --line:#E7EDF6;  /* мягкая граница карточек */
  --line2:#DCE2EC; /* контрастная граница/контролы */
  /* --control: ГРАНИЦА КОНТРОЛА, КОТОРАЯ САМА И ЕСТЬ СОСТОЯНИЕ.
     Не «ещё один серый»: у него ровно один повод существовать. WCAG 1.4.11
     требует 3:1 от фигуры, несущей смысл, а --line2 на белом даёт 1.30:1.
     Для рамки поля ввода или карточки этого хватает (у них есть и высота, и
     заливка, и подпись, а граница там лишь очерчивает), но у НЕОТМЕЧЕННОГО
     чекбокса рамка остаётся ЕДИНСТВЕННЫМ носителем: заливка приходит только с
     :checked, галочка внутри погашена, и в пустом состоянии контраста не
     оставалось вовсе (замер: 1.30:1). --control даёт 3.58:1 на белом,
     3.30:1 на --bg2, 3.16:1 на --page.
     ЗАКОН: этим цветом красят ТОЛЬКО такие рамки, а именно чекбокс .check__box и
     чекбокс-опцию фасета .filters__opt-box. Всё прочее (карточки, поля,
     чипы, селекты) остаётся на --line2: подменить его целиком значило бы
     перекрасить каждый контрол на двадцати экранах. */
  --control:#7F8896;
  /* --faint: ТОЛЬКО ДЕКОРАЦИЯ, И ЭТО ПРАВИЛО, А НЕ ОПИСАНИЕ.
     На белом он даёт 1.65:1, на --bg2 всего 1.50:1. Значит им можно красить лишь
     то, чего не жалко не увидеть: иконку-заглушку внутри подложки (рядом с
     ней всегда есть текст), тонкую разделительную мелочь.
     ⚠️ ПРОЧЕРК «—» ЭТИМ ЦВЕТОМ НЕ ПИШУТ. Прочерк несёт смысл «данных нет»
     (остатка/пола/ростовки у этой позиции взять неоткуда). Это ТЕКСТ, порог
     AA 4.5:1, и красить его нужно --muted (5.28:1 на белом, 4.88:1 на --bg2).
     Второго имени для «нет данных» не заводим: --muted и есть «вторичный
     текст», а второе имя тому же смыслу лишь начало расхождения (та же логика,
     что у серого на тинтах, см. семью ink-on-tint ниже).
     ⚠️ ИКОНКА-УПРАВЛЕНИЕ (кнопка, ссылка) тоже не --faint: у графики
     интерфейса порог 3:1 (WCAG 1.4.11). И ЛЮБАЯ ФИГУРА, НЕСУЩАЯ СОСТОЯНИЕ:
     шаг таймлайна, иконка недоступного документа, для них те же 3:1 (для зелёной
     графики на светлом есть --green-deep). ТОЧКИ НАЛИЧИЯ (.stock__dot) оставлены
     согласованным исключением: их скин зафиксирован макетом, а уровень там
     дублируют форма и подпись; не перекрашивайте их «под порог», см. STOCK.
     Прежняя формулировка токена разрешала им «прочерк сумма 0», из-за неё
     прочерки поехали по четырём файлам.
     ⚠️ ЗАКРЫТЫЕ СПИСКИ «ЗАПРЕЩЕНО / РАЗРЕШЕНО» И САМОПРОВЕРКА ГРЕПОМ:
     assets/_core-notes.md, ## tokens, первый блок («ПРАВИЛО --faint: закон
     проекта»). Замеры точек наличия там же, ## core-b4fix §1. */
  --faint:#C3CAD6;
  /* ---------- ПОЛОСА ПРОКРУТКИ: ОДНА НА ПРОЕКТ ----------
     ЗНАЧЕНИЯ ИЗ ОРИГИНАЛА (kapika-*.html:163-167, восемь одинаковых копий
     правила aside{…}), а не «похожие» токены: при миграции ползунок отдали
     --line2 (#DCE2EC, светлее оригинала), а ховер отдали --muted (#676C74, это
     цвет ТЕКСТА: полоса под курсором становилась тёмной плашкой).
     Замер: #CDD5E1 на белом 1.46:1 (оригинал), #DCE2EC 1.30:1 (было),
     #676C74 5.28:1 (было на ховере). Полоса входит в согласованный вид,
     контраст ей не «чинят».
     --sb-face задаёт цвет панели-хозяина: из него рисуется поле вокруг ползунка
     (border у ::-webkit-scrollbar-thumb) и «шторки» подсказки (см. секцию
     SCROLLER). Хозяин на другом фоне перебивает переменные У СЕБЯ. */
  --sb-w:10px;
  --sb-pad:3px;
  --sb-thumb:#CDD5E1;
  --sb-thumb-hover:#B4BDCD;
  --sb-face:#fff;
  /* ПУСТОЙ СЛОТ ТОЧКИ НАЛИЧИЯ (.stock__dot). Значение из согласованного
     макета (#dbe0e8 в исходных kapika-*.html), а не «ближайший» --line2:
     --line2 задаёт цвет ГРАНИЦ, и подмена уводила кружок в другой оттенок.
     Закон тот же, что у полосы прокрутки выше: контраст согласованному
     виду не пересчитывают. Почему именно так, см. секцию STOCK. */
  --stock-slot:#DBE0E8;
  /* акценты статусов */
  --sale:#E5332A;  /* цена/скидка/ошибка: ФОН и ГРАНИЦА, не текст (см. --ink-alert) */
  --pink:#FBEAF0;  /* мягкая розовая подложка (девочки/промо) */
  /* ЭКСКЛЮЗИВ: ТЁПЛОЕ ЗОЛОТО, И ЭТО НЕ УКРАШЕНИЕ. На розовом бейдж сливался
     с пилюлей «Девочки» (--pink с тем же цветом текста #A23A63): в строке
     выдачи они стоят рядом и читались как одна метка. Свободных смыслов в
     палитре не осталось: зелёный занят наличием, синий предзаказом,
     красный распродажей, поэтому вводим пару. Контраст 6.07:1. */
  --gold-t:#FBEFD6;
  --gold-ink:#7A5200;

  /* ---------- ЧЕМ ПИШУТ НА ТИНТАХ (семья «ink-on-tint») ----------
     ДОЛГ, ЗАКРЫТЫЙ В БАТЧЕ Б3. Тон-в-тон текст-цвета бейджей были литералами
     в семи местах (#5a7d22 / #4a6b1e / #6b7280 / #A23A63 + var(--sale) как
     текст) и подбирались «на глаз»: при кегле 10–12px замер дал 3.75–5.57:1
     при пороге AA 4.5:1. Не проходили ТРИ пилюли статуса и два тега:
       .status--ship  #5a7d22 на --green-t   4.18:1
       .status--pack  #6b7280 на --bg2       4.46:1
       .status--cancel --sale  на --pink     3.75:1
       .tag--tint-green #5a7d22 на --green-t 4.18:1
       .tag--sale      #fff на --sale        4.34:1
     Чинится СЕМЬЁЙ, а не по одному бейджу: у «текста на тинте» теперь два
     имени на весь проект, и любой новый тинтовый бейдж берёт их, а не
     подбирает свой оттенок.
       --ink-ok:     зелёный текст на зелёных тинтах (--green-t, #E4F0D3) и на
                     белом: 5.38 / 5.19 / 6.15:1. Заменил ОБА прежних зелёных
                     (#5a7d22 и #4a6b1e): двух оттенков «того же зелёного» на
                     одном экране (пилюли «Отгружен» и «Выполнен» стоят рядом
                     в одной таблице) быть не должно.
       --ink-alert:  красный текст на --pink и на белом: 5.39 / 6.25:1.
                     var(--sale) как ЦВЕТ ТЕКСТА больше не используется.
                     Он остался фоном, границей и цветом иконок (порог 3:1).
       --sale-d:     тёмный красный ФОН под белым текстом (.tag--sale): на
                     самом --sale белый давал 4.34:1, здесь 5.54:1.
     Серый текст на --bg2 своего имени не получил: там уже есть --muted
     (4.88:1 на --bg2), второе имя тому же смыслу не заводим. */
  --ink-ok:#4a6b1e;
  --ink-alert:#B82820;
  --sale-d:#C9271E;
  /* радиусы */
  --r:20px;        /* крупный (карточки/меню) */
  --r2:12px;       /* средний (плитки/поля) */

  /* ---------- ШКАЛА Z-INDEX (единая точка правды) ----------
     Порядок слоёв снизу вверх:
       контент → хром над контентом → липкая панель → шапка → дропдауны/мега →
       затемнение (scrim) → элемент поверх затемнения → плавающая шапка → лайтбокс.
     Токенов РОВНО 10, и это полный набор. Правила: новые слои берут ТОЛЬКО
     эти токены; «магических» 999/9999 и вообще голых чисел в z-index
     в проекте нет (проверяется грепом `z-index:[[:space:]]*[0-9]`,
     должно быть пусто). Значения с запасом в 10 нужны, чтобы точечно вклиниться
     между слоями, не переписывая шкалу.
     Первые ДВА токена живут ВНУТРИ собственного стекинг-контекста блока
     (карточка, лайтбокс): там важен лишь порядок между своими детьми. */
  --z-content:1;        /* бейджи/оверлеи внутри своей карточки          */
  /* Второй «этаж» того же внутреннего контекста: хром, который обязан лежать
     ПОВЕРХ такого оверлея. Сегодня такой потребитель один, лайтбокс:
     .lightbox__dialog стоит на --z-content, а панель/крестик/стрелки в разметке
     идут ДО диалога, и без своего слоя диалог закрашивал бы их собой. Менять
     порядок узлов нельзя: подпись и счётчик обязаны читаться (и получать фокус)
     раньше картинки. Поэтому токен узаконен как 10-й, а не выкинут. */
  --z-content-top:3;    /* хром поверх такого оверлея (панель/кнопки лайтбокса) */
  --z-suggest:30;       /* подсказки поля поиска в потоке страницы       */
  --z-sticky:50;        /* липкие панели контента (.orderbar), НИЖЕ шапки */
  --z-header:60;        /* шапка (.header, sticky)                       */
  --z-dropdown:70;      /* дропдауны/селекты/мега шапки, над шапкой      */
  --z-scrim:80;         /* затемнение экрана (поиск, выезд фильтров)     */
  --z-above-scrim:95;   /* то, ради чего затемнили (шапка с поиском, drawer) */
  --z-float:120;        /* плавающая шапка главной над hero-каруселью (быв. --z-header-float) */
  --z-toast:220;        /* сообщение об отказе: поверх всего, включая просмотрщик */
  --z-lightbox:200;     /* модальный просмотрщик фото: поверх всего      */
}

/* ---------- RESET / БАЗА ---------- */
*{box-sizing:border-box}

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

body{
  margin:0;
  font-family:'Nunito',system-ui,-apple-system,'Segoe UI',sans-serif;
  font-size:14px;
  color:var(--ink2);
  background:var(--page);
  -webkit-font-smoothing:antialiased;
}

button{font-family:inherit}
input,select,textarea{font-family:inherit}
img{max-width:100%}

[hidden]{display:none!important}

/* переход по якорю: шапка приклеена, без отступа заголовок раздела уезжает
   под неё, и человек попадает на середину текста. Высоту шапки не зашиваем:
   она разная на страницах с полками каталога и без них; нижнюю кромку уже
   считает и публикует kapika.js (--header-bottom). */
:target{scroll-margin-top:calc(var(--header-bottom, 160px) + 16px)}

/* видимый фокус для клавиатуры (в шапке/подвале нативный outline сброшен, возвращаем глобально) */
:focus-visible{outline:2px solid var(--blue);outline-offset:2px;border-radius:4px}
a:focus-visible,button:focus-visible{outline:2px solid var(--blue);outline-offset:2px}

@media (prefers-reduced-motion:reduce){
  *,*::before,*::after{
    animation-duration:.01ms!important;
    animation-iteration-count:1!important;
    transition-duration:.01ms!important;
    scroll-behavior:auto!important;
  }
}

/* ---------- ССЫЛКА «К СОДЕРЖИМОМУ» ----------
   В шапке проекта логотип, две пилюли раздела, поиск, три дропдауна и
   корзина: с клавиатуры до содержимого приходилось проходить их на КАЖДОЙ
   странице, и обхода не было ни на одной из 21. Ссылка стоит ПЕРВОЙ в
   документе (partials/header.html), не видна, пока не получит фокус, и
   ведёт на ландмарк #main.
   Слой --z-float: она обязана быть видна и над плавающей шапкой главной. */
.skip-link{
  position:fixed; top:12px; left:12px; z-index:var(--z-float);
  padding:11px 18px; border-radius:var(--r2);
  background:#fff; border:1px solid var(--line2);
  box-shadow:0 8px 24px -12px rgba(20,38,90,.4);
  color:var(--blue); font-size:14px; font-weight:800; line-height:1.2; text-decoration:none;
  /* строка не переносится: без этого ссылка шириной по содержимому вставала
     в две строки (замер: 135×62 вместо 197×40) */
  white-space:nowrap;
  transform:translateY(calc(-100% - 20px));
}
.skip-link:focus{transform:none}
/* Цель ссылки фокусируется ПРОГРАММНО (в табуляцию она не входит, у неё
   tabindex="-1"), а кольцо вокруг всей области содержимого ничего не
   сообщает: переход виден прокруткой и следующим Tab. Кольцо самой
   skip-ссылки при этом на месте: она обычная <a> (см. :focus-visible). */
main[tabindex="-1"]:focus{outline:none}

/* ---------- КОНТЕЙНЕР ---------- */
/* Центрирующая обёртка (бывш. .page). Горизонтальный padding задан clamp вместо
   лесенки 32→24→18. padding-block несёт вертикальный ритм: сверху 22px,
   снизу 104px, это зазор под мобильную залипающую панель заказа/корзины. */
.wrap{
  width:100%;
  max-width:1680px;
  margin-inline:auto;
  padding-inline:clamp(16px,4vw,40px);
  padding-block:22px 104px;
}
/* инлайн-контейнер внутри залипающего тулбара (бывш. .tbin), без верхнего ритма */
.wrap--flush{padding-block:0 16px}
/* контентные/легал-страницы без залипающей панели: уменьшенный низ */
.wrap--calm{padding-bottom:44px}
/* ---------- РАЗРЕЗ КОНТЕЙНЕРА ПОД ЛАНДМАРК ----------
   Главная остаётся единственной страницей, где содержимое НАЧИНАЕТСЯ вне .wrap
   (hero во всю ширину вьюпорта), а подвал живёт ВНУТРИ .wrap. Ландмарк
   <main> обязан накрыть hero вместе с сеткой блоков и не накрыть подвал,
   значит контейнер разрезан надвое: первый закрывается внутри <main>,
   второй несёт подвал. Вертикальный ритм обязан остаться прежним, поэтому
   у первого нет низа, у второго нет верха: сумма отступов между сеткой и
   подвалом та же (margin-top подвала), низ страницы тот же. */
.wrap--cut-b{padding-bottom:0}
.wrap--cut-t{padding-top:0}
/* На широком экране kapika.js измеряет залипающую .orderbar и
   пишет --orderbar-h в :root, чтобы панель не перекрывала низ страницы. */
.wrap--orderbar{padding-bottom:calc(var(--orderbar-h,104px) + 22px)}
/* На телефоне .orderbar скрыт: не оставляем под него пустое место. */
@media(max-width:768px){ .wrap--orderbar{padding-bottom:22px} }

/* ---------- УТИЛИТЫ: ЗАГОЛОВКИ ---------- */
/* Заголовок страницы H1 (бывш. .h1) */
/* ⚠️ ДЛИННОЕ СЛОВО ЛОМАЕТ СТРАНИЦУ, А НЕ ПЕРЕНОСИТСЯ. «Политика
   конфиденциальности» при 32px/800 занимает 350px, а бокс на 320px виден
   288px, и документ получал горизонтальную прокрутку (замер: scrollWidth 351
   при innerWidth 320). Заголовок не имеет права быть шире своей колонки,
   поэтому перенос внутри слова разрешён ЗДЕСЬ, у самого компонента, а не
   правилом страницы. break-word, а не anywhere: он срабатывает ТОЛЬКО когда
   слово не влезает целиком, и на всех ступенях выше ничего не меняет
   (проверено: переносов на 360+ не появилось). */
.page-title{
  margin:0;
  font-size:26px;
  font-weight:800;
  color:var(--ink);
  overflow-wrap:break-word;
}
/* приписка-мета рядом с H1 (бывш. .h1 em) */
.page-title__meta{
  margin-left:8px;
  font-size:14px;
  font-weight:500;
  font-style:normal;
  color:var(--muted);
}
/* --lg: КРУПНЫЙ ПЛОСКИЙ H1 КОНТЕНТНОЙ СТРАНИЦЫ (бывш. .ab-h1).
   Второй размер заголовка страницы, а не второй блок: рабочие экраны
   (каталог, корзина, кабинет) носят 26px и живут рядом с таблицей, а
   у редакционных страниц («О компании», «Контакты», легал) заголовок занимает
   первый экран целиком, и он набран крупно и без надзаголовка.
   max-width 20ch задаёт МЕРУ СТРОКИ, а не «случайно так вышло»: при 50px
   без неё «Детская обувь, одежда и аксессуары» растягивается на всю
   ширину колонки в одну строку и заголовок перестаёт быть блоком.
   Короткие названия («Контакты») ограничение не замечают. */
.page-title--lg{
  font-size:clamp(32px,4vw,50px);
  line-height:1.06;
  letter-spacing:-.025em;
  max-width:20ch;
  text-wrap:balance;
}

/* ЛИД: абзац-врезка под заголовком страницы (бывш. .ab-hero-lead
   инфо-страниц и .lead экранов входа). Он не элемент заголовка:
   на экранах входа между H1 и лидом ничего нет, а на «О компании»
   лид стоит внутри своей зоны с водяным знаком.
   --narrow: где под лидом идёт полоса статов, мера уже (56ch),
   иначе лид перекрывает по ширине сетку под ним. */
.page-lead{
  max-width:62ch;
  margin:18px 0 0;
  font-size:16.5px;line-height:1.62;font-weight:500;
  color:var(--ink2);text-wrap:pretty;
}
.page-lead--narrow{max-width:56ch}
.page-lead a{color:var(--blue);font-weight:700;text-decoration:none}
.page-lead a:hover{text-decoration:underline}

/* Заголовок секции (бывш. .blk-head / .blk-head h2) */
.sec-head{margin:0 0 16px}
/* --split: заголовок и ссылка «Все новинки»/«Вся история» ОДНОЙ строкой.
   Выравнивание по baseline: 27px заголовка и 15px ссылки садятся на общую
   линию шрифта, а не по центру блока. flex-wrap нужен, чтобы на узком ссылка
   уходила под заголовок, а не резалась. (Витрины главной; в теле сайта
   .sec-head пока встречается без ссылки, там модификатор не ставится.) */
.sec-head--split{
  display:flex;
  align-items:baseline;
  justify-content:space-between;
  gap:8px 20px;
  flex-wrap:wrap;
}
.sec-head__title{
  margin:0;
  font-size:clamp(22px,2.4vw,27px);
  font-weight:800;
  letter-spacing:-.02em;
  color:var(--ink);
  text-wrap:balance;
}
/* --lg: заголовок секции на РЕДАКЦИОННОЙ странице («Материалы»,
   «Производство», «Способы доставки»). Там секции разделяет воздух,
   а не карточка, и заголовок держит эту роль на три пункта крупнее.
   Второго имени тому же заголовку не заводим: это его размер. */
.sec-head--lg .sec-head__title{font-size:clamp(23px,2.5vw,30px);line-height:1.14}

/* ---------- УТИЛИТЫ: ПРОЧЕЕ ---------- */
/* доступное скрытие (для лейблов/подписей скринридеру) */
.visually-hidden{
  position:absolute!important;
  width:1px;height:1px;
  padding:0;margin:-1px;
  overflow:hidden;
  clip:rect(0 0 0 0);clip-path:inset(50%);
  white-space:nowrap;border:0;
}

/* ==========================================================================
   АДАПТИВ (только для core; остальные брейкпоинты лестницы живут в компонентах)
   ========================================================================== */
@media (max-width:1240px){
  /* :not(--lg) ЗДЕСЬ ОБЯЗАТЕЛЕН. У модификатора та же одноклассовая
     специфичность (0,1,0), а это правило стоит в файле ниже, поэтому без
     оговорки оно перебивало бы crumb-размер редакционного H1: на планшете
     и телефоне clamp(32…50px) молча превращался в 22px, и «крупный плоский
     заголовок» инфо-страниц оставался только на десктопе. Свою ступень
     --lg держит сам (clamp по вьюпорту). */
  .page-title:not(.page-title--lg){font-size:22px}
}


/* ============================================================
   BUTTONS
   ============================================================ */
/* =========================================================
   BUTTONS: core.css
   Blocks: .button  .iconbtn  .cartbtn  .textlink
   Tokens used: --green/--green-d  --blue/--blue-d/--blue-t
                --ink2  --line2  --muted
   Desktop-first. Ladder used here: 1240 / 1024 / 768
   ========================================================= */

/* ---------- .button: pill text / CTA button ---------- */
.button{
  display:inline-flex;
  align-items:center;
  justify-content:center;
  gap:9px;
  min-height:44px;
  padding:13px 24px;
  border:0;
  border-radius:999px;
  background:#fff;
  color:var(--ink2);
  font-family:inherit;
  font-size:14.5px;
  font-weight:800;
  line-height:1;
  white-space:nowrap;
  text-decoration:none;
  cursor:pointer;
  /* box-shadow В ПЕРЕХОДЕ БОЛЬШЕ НЕТ. Своей тени у базовой кнопки нет (её
     заводит только примесь --loud, и у той свой transition), а вот КОЛЬЦО
     ФОКУСА рисуется именно box-shadow, и с переходом оно проявлялось 140 мс
     после нажатия Tab. Реакция на клавиатуру обязана быть мгновенной. */
  transition:background .14s, border-color .14s, color .14s;
}
.button i{font-size:18px}
/* ---------- ДВУХСЛОЙНОЕ КОЛЬЦО ФОКУСА ----------
   Кольцо кнопки обязано читаться на ЛЮБОЙ подложке: кнопки проекта стоят и на
   белой карточке формы, и на тинте, и поверх фотографии первого экрана. Одним
   цветом это не решается, и попытка решить стоила проекта: у .button--accent
   тут стояло outline-color:#fff, и БЕЛОЕ кольцо на БЕЛОЙ карточке входа не
   меняло ни одного пикселя (замер: 1.76:1 при пороге 3:1 для носителя
   состояния), и единственным следом фокуса оставался бледный ореол.
     • box-shadow белым заполняет ЗАЗОР outline-offset вплотную к кнопке
       (spread 2px = offset 2px, без размытия);
     • outline --blue-d идёт снаружи этой белой полосы.
   Тёмная линия против белой полосы даёт 8.94:1 независимо от того, что под
   кнопкой; на белой карточке работает внешняя тёмная линия (8.94:1 к белому),
   на тёмном фото работает внутренняя белая. Своего кольца скинам больше не нужно. */
.button:focus-visible{
  outline:2px solid var(--blue-d); outline-offset:2px;
  box-shadow:0 0 0 2px #fff;
}
/* недоступность задаётся ТОЛЬКО атрибутом: кнопка обязана выпадать и из клавиатуры.
   УДАЛЕНО (четвёртая приёмка): класс-дубль .button.is-disabled, generic-имя
   вне БЭМ, ни одна страница его не ставила. */
.button:disabled,
.button[disabled]{opacity:.5; cursor:default; pointer-events:none}

/* primary: green CTA (в заказ, оформить, оплатить).
   ТЁМНЫЙ ИНК ЗАДАЁТ --green-ink (8.26:1; на ховере 7.44:1). Белый текст, стоявший
   здесь раньше, давал 1.70:1: канон-кнопка была нечитаемой на всех экранах,
   где она есть (панель заказа, карточка товара, главная, корзина). */
.button--primary{background:var(--green); color:var(--green-ink)}
.button--primary:hover{background:var(--green-d)}
/* УДАЛЕНО (четвёртая приёмка): .is-ok / .is-warn, generic-имена вне БЭМ,
   ни одного потребителя в 20 страницах и партиалах (единственная кнопка с
   таким поведением, kapika-order-excel.html:753, красится своим страничным
   классом .ok и ещё не мигрирована). Когда мигрирует: подписи-состояния
   объявляются модификаторами БЛОКА (.button--ok / .button--warn) и живут
   здесь же, рядом с --primary. */

/* ghost: white, outlined, neutral secondary */
.button--ghost{
  background:#fff;
  color:var(--ink2);
  border:1.5px solid var(--line2);
  font-weight:700;
}
.button--ghost:hover{border-color:var(--blue); color:var(--blue)}

/* ghost-accent: white outlined but brand-blue label (менеджер, ещё, bulk-step) */
.button--ghost-accent{
  background:#fff;
  color:var(--blue);
  border:1.5px solid var(--line2);
  font-weight:800;
}
.button--ghost-accent:hover{border-color:var(--blue); background:var(--blue-t)}

/* accent: solid blue */
.button--accent{background:var(--blue); color:#fff}
.button--accent:hover{background:var(--blue-d)}
/* СВОЕГО КОЛЬЦА ФОКУСА У --accent НЕТ. Здесь стояло outline-color:#fff плюс
   ореол rgba(0,94,184,.35). На белой карточке входа (все три экрана входа)
   белое кольцо было невидимо, а ореол давал 1.76:1. Двухслойное кольцо
   базовой .button работает и на белом, и на цвете, см. комментарий выше. */


/* ---------- loud: «громкая» кнопка витрины (hero и дашборд главной) ----------
   ПРИМЕСЬ К СКИНУ, а не самостоятельный скин: цвет ЦЕЛИКОМ даёт --primary/
   --accent, --loud добавляет РОВНО ОДНО, а именно ОБЪЁМ (тень + подъём на ховере).
   Плоский канон тела сайта не меняется: у --loud ровно два потребителя, это
   первый экран главной и её карточка «Продолжить заказ», то есть места, где
   кнопка обязана читаться поверх фотографии и градиента.

   СВОЕГО ЦВЕТА ТЕКСТА У --loud БОЛЬШЕ НЕТ. Раньше здесь стояло правило
   .button--primary.button--loud{color:#173208}, потому что базовый --primary
   был белым по лайму (1.70:1) и «громкая» кнопка главной чинила контраст
   ТОЛЬКО у себя. Теперь тёмный ink живёт в самом --primary (--green-ink,
   8.26:1), одинаково на всех 20 экранах, и примеси нечего переопределять:
   у неё осталась одна ответственность: громкость.

   ТЕНЬ ОДНА НА ДВА КОНТЕКСТА. В источнике их было две: чёрная 0 14px 34px -14px
   rgba(0,0,0,.45) у hero (кнопка на фотографии) и зелёное свечение
   0 10px 22px -12px rgba(120,170,50,.9) у дашборда (кнопка на белой карточке).
   Свели к теневой семье ядра rgba(20,38,90,…), той же, что у .tile/.header;
   на тёмном фото тень и так не читается, а на белой карточке даёт тот же объём.
   Ховер-подсветка фона тоже одна: --green-d (у дашборда был свой #b0e06a). */
.button--loud{
  box-shadow:0 12px 26px -14px rgba(20,38,90,.45);
  transition:background .14s, border-color .14s, color .14s, box-shadow .18s, transform .16s;
}
.button--loud:hover{
  transform:translateY(-2px);
  box-shadow:0 18px 34px -16px rgba(20,38,90,.5);
}
/* ФОКУС НЕ ГАСИТ ОБЪЁМ. Белую полосу кольца базовая .button:focus-visible
   печатает тем же свойством box-shadow, которым --loud держит тень, и одно
   молча стирало бы другое: кнопка первого экрана «сплющивалась» бы под
   клавиатурой. Здесь оба слоя названы вместе: полоса кольца и тень объёма.
   БЕЗ box-shadow В ПЕРЕХОДЕ: у --loud он свой (.18s, ради подъёма на ховере),
   и кольцо проявлялось бы 180 мс. Именно у этой кнопки ждать нельзя, ведь она
   стоит на фотографии, где ЧИТАЕТСЯ ИМЕННО БЕЛАЯ ПОЛОСА. Подъём и цвет
   по-прежнему с переходом. */
.button--loud:focus-visible{
  box-shadow:0 0 0 2px #fff, 0 12px 26px -14px rgba(20,38,90,.45);
  transition:background .14s, border-color .14s, color .14s, transform .16s;
}

/* size modifiers */
.button--lg{min-height:52px; padding:0 24px; font-size:16px}
.button--lg i{font-size:21px}
.button--block{width:100%}

/* ---------- .iconbtn: round icon button ---------- */
.iconbtn{
  position:relative;
  flex-shrink:0;
  width:46px; height:46px;
  display:inline-flex;
  align-items:center;
  justify-content:center;
  border:1.5px solid var(--line2);
  border-radius:50%;
  background:#fff;
  color:var(--ink2);
  cursor:pointer;
  transition:border-color .14s, color .14s, background .14s;
}
.iconbtn i{font-size:21px}
.iconbtn:hover{border-color:var(--blue); color:var(--blue); background:var(--blue-t)}
.iconbtn:focus-visible{outline:2px solid var(--blue); outline-offset:2px}

/* burger / menu: blue-tint filled */
.iconbtn--menu{background:var(--blue-t); border-color:transparent; color:var(--blue)}
/* второй селектор ловит РЕАЛЬНОЕ состояние «дропдаун бургера открыт» (хук
   .header__menu-toggle из партиала). Раньше здесь стоял generic-класс
   .is-open, которого нет ни в одной разметке, и правило было мёртвым. */
.iconbtn--menu:hover,
.header__menu--open .header__menu-toggle{background:#d3e5fa; border-color:transparent; color:var(--blue-d)}

/* ---------- .cartbtn: green cart with counter ----------
   Носителем служит <a href="kapika-cart.html"> (переход в заказ = навигация; см.
   partials/header.html). Кнопкой блок был раньше, поэтому в объявлении
   остаются border/font-family (обнуляют дефолты <button>, если компонент
   когда-нибудь понадобится кнопкой) плюс text-decoration/color для ссылки. */
.cartbtn{
  position:relative;
  flex-shrink:0;
  display:inline-flex;
  align-items:center;
  gap:9px;
  height:46px;
  padding:0 18px 0 15px;
  border:0;
  border-radius:999px;
  background:var(--green);
  /* ⚠️ БЕЛЫЙ ЗДЕСЬ СОГЛАСОВАННЫЙ ВИД, А НЕ НЕДОСМОТР (возврат, приёмка «вид»).
     Здесь стоял тёмный var(--green-ink): при миграции лайм с белой подписью
     сочли дефектом контраста (1.70:1) и перекрасили текст. Кнопка заказа остаётся
     самым заметным элементом шапки, и такая перекраска меняет УТВЕРЖДЁННЫЙ
     макет; решение о контрасте принимает заказчик, а не вёрстка.
     Оригинал: .cartbtn{background:var(--green);color:#fff} (без рамки).
     Замер после возврата: #fff на --green 1.70:1, на ховерном --green-d
     1.89:1, это ниже AA 4.5:1, вынесено отдельной строкой в отчёт. */
  color:#fff;
  font-family:inherit;
  font-size:14px;
  text-decoration:none;
  white-space:nowrap;
  cursor:pointer;
  transition:background .14s;
}
.cartbtn:hover,
.cartbtn:visited{color:#fff}   /* ссылка не синеет и не «фиолетовеет» */
.cartbtn:hover{background:var(--green-d)}
.cartbtn:focus-visible{outline:2px solid var(--blue); outline-offset:2px}
.cartbtn i{font-size:21px}
.cartbtn__icon{display:inline-flex; align-items:center}   /* имя элемента = разметка партиала (было .cartbtn__ico, опечатка) */
.cartbtn__count{font-size:14px; font-weight:700}
.cartbtn__dot{opacity:.55; font-weight:700}
.cartbtn__sum{font-size:15px; font-weight:800}
/* ДЛИННЫЙ БЕЙДЖ РАБОТАЕТ НА ЛЮБОЙ ШИРИНЕ (модификатор ставит KCart по длине суммы).
   Ширина бейджа зависит от ЗАКАЗА, а не от вьюпорта: смешанный заказ печатает
   «4 позиции · 40 680 ₽ + $632.00» (286px) и распирал шапку на 1241–1260,
   в сливере между ступенью ≤1240 и шириной, на которой .wrap уже подрос
   (найдено развёрткой по 1px внутри самой шапки). Слова «N позиций» уходят,
   число читается кружком над иконкой, полная фраза остаётся доступным именем
   кнопки. Валюты по-прежнему раздельны: сокращать сумму нельзя. */
.cartbtn--long .cartbtn__count,
.cartbtn--long .cartbtn__dot{display:none}
.cartbtn--long .cartbtn__badge{display:flex}
.cartbtn__badge{
  display:none;
  position:absolute; top:-7px; right:-7px;
  min-width:19px; height:19px; padding:0 5px;
  align-items:center; justify-content:center;
  border-radius:999px;
  background:var(--blue); color:#fff;
  font-size:12px; font-weight:800;
  border:2px solid #fff;
}

/* УДАЛЕНО (третья приёмка): .cartbtn--compact, форс-модификатор «свернуть в кружок».
   Ни одна страница/партиал его не ставила, а то же самое компонент делает сам
   в @media(max-width:1024). Понадобится принудительное сворачивание, тогда вернуть
   модификатор вместе с разметкой, которая его ставит, а не «про запас». */

/* ---------- .textlink: inline arrow link-button («Открыть →», «Подробнее», «Изменить») ---------- */
.textlink{
  display:inline-flex;
  align-items:center;
  gap:6px;
  padding:0;
  border:0;
  background:none;
  color:var(--blue);
  font-family:inherit;
  font-size:15px;
  font-weight:700;
  white-space:nowrap;
  text-decoration:none;
  cursor:pointer;
  transition:color .16s;
}
.textlink i{font-size:17px}
.textlink:hover{color:var(--blue-d)}
.textlink:focus-visible{outline:2px solid var(--blue); outline-offset:2px; border-radius:4px}
.textlink--sm{font-size:13.5px}
.textlink--sm i{font-size:16px}
.textlink--muted{color:var(--muted); font-weight:600; text-decoration:underline}
.textlink--muted:hover{color:var(--blue)}
/* on-dark: та же ссылка на ФОТО/градиенте (вторичное действие hero главной).
   Синий на тёмной подложке нечитаем, поэтому белый + пунктирное подчёркивание
   (аффорданс ссылки без цвета).
   ⚠️ ЗДЕСЬ СТОЯЛО «контраст #fff на затемнении hero заведомо выше 4.5:1».
   Это допущение без замера, и неверное: 15px/700 прямо на фотографии давали
   1.08–1.10 на узких ступенях (композит по боксам глифов). Носителем контраста
   стала подложка, а не затемнение: на главной ссылка идёт с примесью
   .hero__link (тёмная пилюля, home.css). Голым белый текст на фотографии не
   оставляем, правило раунда записано в assets/_core-notes.md.
   Фокус перекрашиваем: базовая обводка синяя и на тёмном не видна. */
.textlink--on-dark{color:#fff; border-bottom:1.5px dashed rgba(255,255,255,.55); padding-bottom:3px}
.textlink--on-dark:hover{color:#fff; border-bottom-color:#fff}
.textlink--on-dark:focus-visible{outline-color:#fff}


/* =========================================================
   Responsive: ladder 1240 / 1024 / 768
   ========================================================= */
@media (max-width:1240px){
  .iconbtn{width:44px; height:44px}
  .cartbtn{height:44px; padding:0 16px 0 14px; gap:8px}
  /* ⚠️ ПРАВКА ПРИЁМКИ Б2: ПЕРЕПОЛНЕНИЕ ШАПКИ НА 1025–1034.
     Самый длинный и самый изменчивый узел шапки, бейдж заказа. При
     смешанном заказе он печатает «2 позиции · 22 800 ₽ + $632.00» и
     занимает 286px, отчего строка шапки распирала страницу (замер: при
     окне 1025 документ 1035). Ширина бейджа зависит от СОДЕРЖИМОГО ЗАКАЗА,
     поэтому подрезать соседей было бы лечением симптома: следующий заказ
     подлиннее вернул бы дефект.
     Решение: на тесных ступенях число позиций уходит в кружок над иконкой
     (он для этого и есть), а в пилюле остаётся СУММА, ради неё бейдж и
     читают. Валюты остаются раздельными («₽ + $»), полная фраза уходит в
     доступное имя кнопки (aria-label ставит KCart). Экономия 86px.
     Пустое состояние («В заказе пусто») не трогаем: там подпись и есть весь
     смысл кнопки, а места она занимает мало. */
  .cartbtn--filled .cartbtn__count,
  .cartbtn--filled .cartbtn__dot{display:none}
  .cartbtn--filled .cartbtn__badge{display:flex}
  /* Последний рубеж: заказ на семизначные суммы в двух валютах ещё длиннее.
     Пилюле разрешено сжиматься, а сумме обрезаться многоточием: обрезанная
     сумма читается плохо, но горизонтальная прокрутка ВСЕЙ страницы хуже,
     а полный текст остаётся доступным именем кнопки. */
  .cartbtn{flex-shrink:1; min-width:44px}
  .cartbtn__sum{min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap}
}
@media (max-width:1024px){
  /* tight header: cart auto-collapses to a circle (source bp was 980) */
  .cartbtn{width:44px; height:44px; padding:0; gap:0; border-radius:50%; justify-content:center}
  .cartbtn__count,.cartbtn__dot,.cartbtn__sum{display:none}
  .cartbtn__badge{display:flex}
}
@media (max-width:768px){
  .iconbtn{width:42px; height:42px}
  /* ⚠️ min-width СБРАСЫВАЕМ ЯВНО. Ступень ≤1240 ставит кнопке min-width:44px
     (там она ещё с суммой и обязана не схлопнуться), и на телефоне эта
     минималка перебивала width:42px: в ряду из пяти кружков корзина была
     44×42, единственный эллипс среди кругов. */
  .cartbtn{width:42px; height:42px; min-width:0}
}


/* ============================================================
   BADGES
   ============================================================ */
/* ============================================================
   СТАТУСЫ И ТЕГИ  (core.css, компонент)
   Опирается на глобальную палитру :root:
   --blue --blue-d --blue-t --green --green-t --bg2 --line
   --line2 --pink --sale --muted --ink2
   ТЕКСТ НА ТИНТЕ ЗАДАЁТСЯ ТОКЕНАМИ (--ink-ok / --ink-alert / --muted, см. :root):
   прежние литералы #5a7d22 / #4a6b1e / #6b7280 и var(--sale)-как-текст
   не проходили порог 4.5:1 при кегле 10–12px. Литералами остались только
   ГРАНИЦЫ контурных чипов (#cfe3b5 / #cfe0f5) и цвета на сплошной заливке
   (#294510 на лайме 6.33:1, #A23A63 на --pink 5.46:1), они порог проходят.
   Брейкпоинты по канон-лестнице: используется только 560.
   ============================================================ */

/* -------- .status: пилюля статуса заказа (тинт + иконка) ---- */
/* Жизненный цикл заказа: ожидает → подтверждён → в сборке → отгружен →
   выполнен, в стороне стоит отменён. Модификатор на каждое состояние словаря
   (window.KStatus): подписи, иконки и порядок там же, пилюлю печатает он.
   Иконка <i> опциональна.
   СЕМЬИ ЦВЕТА (чтобы шесть пилюль читались как путь, а не как радуга):
     синий:   заказ у менеджера (ожидает, подтверждён),
     серый:   заказ на складе (в сборке),
     зелёный: заказ у партнёра (отгружен, выполнен),
     розовый: пути нет (отменён).
   Внутри синей семьи пилюли различает ИКОНКА И ПОДПИСЬ, а не оттенок:
   у «Ожидает» часы, у «Подтверждён» галочка и более тёмный --blue-d.
   КОНТРАСТ (замер до/после, порог AA 4.5:1 при 12px/700):
     --wait   var(--blue)   на --blue-t   5.57 → 5.57  (не менялся)
     --ok     var(--blue-d) на --blue-t   —    → 7.81  (новый)
     --pack   #6b7280→--muted на --bg2    4.46 → 4.88
     --ship   #5a7d22→--ink-ok на --green-t 4.18 → 5.38
     --done   #4a6b1e→--ink-ok на #E4F0D3 5.19 → 5.19  (тот же тон, теперь имя)
     --cancel --sale→--ink-alert на --pink 3.75 → 5.39                        */
.status{
  display:inline-flex;align-items:center;gap:6px;
  font-size:12px;font-weight:700;line-height:1;
  padding:6px 12px;border-radius:999px;white-space:nowrap;
}
.status i{font-size:13px}

.status--wait  {background:var(--blue-t);  color:var(--blue)}      /* Ожидает   */
.status--ok    {background:var(--blue-t);  color:var(--blue-d)}    /* Подтверждён */
.status--pack  {background:var(--bg2);     color:var(--muted)}     /* В сборке  */
.status--ship  {background:var(--green-t); color:var(--ink-ok)}    /* Отгружен  */
.status--done  {background:#E4F0D3;        color:var(--ink-ok)}    /* Выполнен  */
.status--cancel{background:var(--pink);    color:var(--ink-alert)} /* Отменён   */

/* -------- .tag: маркер товара (флэт, uppercase) ------------- */
/* Дефолт = зелёный «снова в наличии». Инлайн в таблице/имени.   */
.tag{
  display:inline-flex;align-items:center;
  font-size:10px;font-weight:800;line-height:1;
  letter-spacing:.07em;text-transform:uppercase;
  padding:5px 9px;border-radius:7px;white-space:nowrap;
  background:var(--green);color:#294510;
}
.tag--new {background:var(--blue);  color:#fff}      /* Новинка (синяя)     */
.tag--exc {background:var(--gold-t);color:var(--gold-ink)} /* Эксклюзив (золото) */
.tag--sale{background:var(--sale-d);color:#fff}      /* Распродажа / −% (5.54:1; на --sale было 4.34) */

/* Наложение поверх фото карточки товара (левый-верхний угол). */
.tag--pin{position:absolute;top:12px;left:12px;z-index:var(--z-content)}

/* Пилюльный крой тега (бейджи над названием на карточке товара, бывш.
   .pd-pre/.pd-exc): скруглённая пилюля, чуть крупнее и просторнее плоской
   плашки. Скин (цвет) задаётся отдельным модификатором. */
.tag--pill{
  padding:4px 12px;border-radius:999px;
  font-size:10.5px;letter-spacing:.04em;
}
/* Тинтовые скины пилюли (мягкая заливка вместо сплошной).
   Зелёный = доступность («Предзаказ» / «В наличии»),
   синий  = свойство товара («Эксклюзив» / сезон).                   */
.tag--tint-green{background:var(--green-t);color:var(--ink-ok)}
.tag--tint-blue {background:var(--blue-t); color:var(--blue-d)}

/* -------- .chip, токен-чип: фильтр (съёмный) / наличие ------ */
/* База = съёмный чип активного фильтра: белый, обводка, ✕.      */
.chip{
  display:inline-flex;align-items:center;gap:7px;
  background:#fff;border:1px solid var(--line2);border-radius:999px;
  padding:6px 13px;font-size:12.5px;font-weight:700;
  color:var(--ink2);white-space:nowrap;
}

/* Чип наличия (компактный, статичный, без ✕). */
.chip--sm{padding:4px 11px;font-size:12px}
.chip--av {background:var(--green-t);border-color:transparent;color:var(--ink-ok)} /* Со склада */
.chip--pre{background:var(--blue-t); border-color:transparent;color:var(--blue)} /* Предзаказ  */

/* ============================================================
   Адаптив
   ============================================================ */
@media (max-width:560px){
  /* Длинные подписи наличия переносятся, а не режутся. */
  .chip--av,.chip--pre{white-space:normal}
}


/* ============================================================
   CARDS
   ============================================================ */
/* ============================================================
   TILE: универсальная белая карточка
   surface: #fff / border --line / radius --r / тень покоя
   Наведение опционально (--hover / --shadow / --feature).
   Тени-лестница:
     base   0 1px 2px       rgba(20,38,90,.04)
     md     0 8px 24px -16px rgba(20,38,90,.16) → hover .24
     lg     0 18px 40px -22px rgba(20,38,90,.3)
   ============================================================ */
.tile{
  --tile-pad: 24px 26px;
  background:#fff;
  border:1px solid var(--line);
  border-radius:var(--r);
  padding:var(--tile-pad);
  box-shadow:0 1px 2px rgba(20,38,90,.04);
}

/* ---------- модификаторы поверхности ---------- */
.tile--flat{ box-shadow:none; }                       /* только border, без тени (fx-card) */
.tile--plain{ border:0; box-shadow:none; }            /* панель в тонированном сайдбаре (sum-card) */

.tile--shadow{                                        /* средняя + подъём на hover (pmcard) */
  box-shadow:0 8px 24px -16px rgba(20,38,90,.16);
  transition:transform .16s ease, box-shadow .16s ease;
}
.tile--shadow:hover{
  transform:translateY(-3px);
  box-shadow:0 16px 34px -18px rgba(20,38,90,.24);
}

.tile--shadow-h{                                      /* тяжёлая плавающая (тулбар, модалка) */
  box-shadow:0 18px 40px -22px rgba(20,38,90,.3);
}

/* ---------- раскладка/наполнение ---------- */
.tile--clip{ overflow:hidden; padding:0; }            /* оборачивает таблицу/список (qo-card, oh-card) */
.tile--inset{                                         /* вложенная группа внутри карточки (pd-ordercard) */
  --tile-pad:15px 16px;                               /* через токен: микс может обнулить (--tile-pad:0) */
  background:transparent;
  border-color:var(--line2);
  border-radius:14px;
  box-shadow:none;
}
.tile--sm{ border-radius:16px; --tile-pad:20px; }     /* мелкая плитка-иконка (ab-mat-tile, fx-card) */

/* ---------- подъём для любой статичной плитки ---------- */
.tile--hover{ transition:transform .16s ease, box-shadow .16s ease; }
.tile--hover:hover{
  transform:translateY(-3px);
  box-shadow:0 16px 34px -18px rgba(20,38,90,.24);
}

/* ---------- элементы ---------- */
.tile__head{                                          /* строка-шапка карточки (dc-card-h) */
  display:flex; align-items:center; gap:10px;
  flex-wrap:wrap; margin:0 0 18px;
}
.tile__icon{                                           /* акцентная иконка-слот (pmcard-ic) */
  display:block; width:34px; height:34px; color:var(--blue);
}
.tile__icon svg{ display:block; width:100%; height:100%; }

/* ============================================================
   PRODUCT-CARD: карточка товара в сетке
   фото-панель (object-fit:contain нормализует разный формат)
   + информация ОТДЕЛЬНО под панелью
   ============================================================ */
.product-card{ display:flex; flex-direction:column; text-decoration:none; }
.product-card:focus-visible{
  outline:2px solid var(--blue); outline-offset:3px; border-radius:20px;
}

.product-card__photo{
  position:relative;
  aspect-ratio:4/5;
  display:flex; align-items:center; justify-content:center;
  border:1px solid var(--line);
  border-radius:18px;
  overflow:hidden;
  transition:box-shadow .18s, transform .18s, border-color .18s;
}
/* Кадр в край, без белой подложки и внутреннего поля (решение заказчика).
   object-position center 60%: товар снят в нижней половине кадра, поэтому центр
   обрезки смещён вниз, иначе у вертикальных снимков срезалась бы подошва.
   mix-blend-mode здесь бесполезен (фон съёмки серый, а не белый) и снят. */
.product-card__photo img{
  width:100%; height:100%;
  object-fit:cover; object-position:center 60%;
  display:block;
}
/* Кадра у товара нет: в выгрузке 1С фотографий пока нет ни одной, и на месте
   снимка стоит та же иконка-заглушка, что в таблице каталога. Пустой белый
   прямоугольник читался бы как «картинка не загрузилась». */
.product-card__noimg{ font-size:46px; color:var(--faint); }

.product-card:hover .product-card__photo{
  transform:translateY(-4px);
  border-color:#cfdaee;
  box-shadow:0 24px 48px -26px rgba(20,38,90,.44);
}

/* слот для тега «новинка/снова»: визуал берётся из компонента .tag/.tag--new,
   здесь только позиционирование внутри фото-панели */
.product-card__badge{ position:absolute; top:12px; left:12px; z-index:var(--z-content); }

.product-card__info{ padding:15px 4px 0;
  /* Колонка во всю оставшуюся высоту: подпись с ценой прижимается к низу,
     поэтому у карточек с названием в одну и в две строки цена на одной линии. */
  display:flex; flex-direction:column; flex:1;
}
.product-card__name{
  margin:0 0 4px;
  font-size:14.5px; font-weight:800; line-height:1.3; color:var(--ink);
}
.product-card__meta{
  margin:0 0 11px;
  font-size:12px; font-weight:600; color:var(--muted);
}
.product-card__foot{
  margin-top:auto;
  display:flex; align-items:flex-end; justify-content:space-between; gap:10px;
}
.product-card__price b{ font-size:16px; font-weight:800; color:var(--ink); }
.product-card__price span{ font-size:12px; font-weight:600; color:var(--muted); }
.product-card__stock{
  font-size:12px; font-weight:700; color:var(--ink-ok);
  text-align:right; line-height:1.35;
}

/* сетка-хелпер под товарные карточки */
.product-grid{
  display:grid;
  grid-template-columns:repeat(5,1fr);
  gap:20px;
}

/* ============================================================
   АДАПТИВ (desktop-first, лестница 1240/1024/900/768/560/400)
   ============================================================ */
@media(max-width:1240px){
  .product-grid{ grid-template-columns:repeat(4,1fr); }
}
@media(max-width:1024px){
  .product-grid{ grid-template-columns:repeat(3,1fr); }
}
@media(max-width:560px){
  .product-grid{ grid-template-columns:repeat(2,1fr); }
  .tile{ --tile-pad:20px 18px; }
  /* ⚠️ ЯВНЫЙ МОДИФИКАТОР ПОЛЕЙ СИЛЬНЕЕ УЗКОЙ СТУПЕНИ. Обе строки пишут одну
     и ту же переменную на одном узле, и правило ступени (оно ниже по файлу)
     побеждало: у «Заказа принятого» карточка на телефоне вдруг ужималась до
     20/18, хотя в согласованном макете .ok-card держал 28/30 на любой
     ширине. Плитка, которой поля назначили руками, ужиматься не должна. */
  .tile--pad-lg{ --tile-pad:28px 30px; }
}


/* ============================================================
   FORMS
   ============================================================ */
/* ==========================================================================
   FORMS: канонический компонент (core.css)
   Зависит от токенов :root (компонент tokens): --blue --blue-d --blue-t
   --green --green-d --ink --ink2 --muted --bg2 --line --line2 --sale
   --r2(12px). Шрифт Nunito наследуется от body.
   Desktop-first, брейкпоинты по лестнице (1240/1024/900/768/560/400).
   ========================================================================== */

/* ---------- 1. Раскладка формы (замена per-field margin у auth и .dc-grid) ---------- */
.form{display:flex;flex-direction:column;gap:16px}
.field-grid{display:grid;grid-template-columns:1fr 1fr;gap:16px 24px}

/* ---------- 2. Поле: обёртка / лейбл / подсказка / значение-only ---------- */
.field{display:flex;flex-direction:column;gap:6px;min-width:0}
.field--full{grid-column:1/-1}
.field__label{font-size:13px;font-weight:700;color:var(--ink)}
.field__label--muted{font-size:12.5px;font-weight:700;color:var(--muted)} /* компактные/данные (кабинет) */
.field__hint{font-size:12px;font-weight:600;color:var(--muted)}

/* ---------- 3. Контрол: input / textarea / native select ---------- */
.field__control{position:relative;display:flex;align-items:center} /* когда есть иконка/кнопка внутри */

.field__input{width:100%;height:48px;padding:0 14px;border:1.5px solid var(--line2);border-radius:var(--r2);
  font-family:inherit;font-size:15px;color:var(--ink);background:#fff;outline:0;
  transition:border-color .14s,box-shadow .14s}
/* ПОДСКАЗКА В ПУСТОМ ПОЛЕ ЭТО ТЕКСТ, а не декорация: пока поле пусто, она
   единственная говорит, что туда писать (ИНН, артикул, поиск по каталогу).
   Литерал #9aa0a8 давал 2.64:1 на белом при пороге AA 4.5:1, и он же
   был скопирован в поле поиска шапки, то есть сборкой разъезжался по всем
   12 страницам. Своего имени «серому в поле» не заводим: у проекта уже
   есть --muted («вторичный текст», 5.28:1 на белом), см. закон токенов. */
.field__input::placeholder{color:var(--muted)}
.field__input:focus{border-color:var(--blue);box-shadow:0 0 0 3px var(--blue-t)}
.field__input:disabled{background:var(--bg2);color:var(--muted);cursor:not-allowed}
/* ПОЛЕ, КОТОРОЕ НЕ ПРИМЕНИМО. Не гасим его disabled: отключённое поле браузер
   не отправляет, а нам нужно, чтобы пустое значение дошло до сервера и затёрло
   прежнее (см. linkKpp в js/auth.js). Вид берём тот же, что у выключенного:
   серая подложка, приглушённый текст, курсор-запрет. */
.field--off .field__input{background:var(--bg2);color:var(--muted);cursor:not-allowed}
.field--off .field__label,.field--off .field__hint{color:var(--muted)}

textarea.field__input{height:auto;min-height:64px;padding:11px 14px;line-height:1.45;resize:vertical}

/* native <select> оформляем как поле + свой шеврон (оригиналы полагались на OS) */
select.field__input{appearance:none;-webkit-appearance:none;padding-right:40px;cursor:pointer;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%238D8F93' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
  background-repeat:no-repeat;background-position:right 14px center}

/* компактная плотность (кабинет: 44px) */
.field__input--sm{height:44px;font-size:14px}
/* место под замыкающую кнопку (глаз пароля) */
.field__input--addon{padding-right:48px}

/* показать/скрыть пароль (было .eye), аффорданс поля */
.field__toggle{position:absolute;right:6px;width:38px;height:38px;border:0;background:none;color:var(--muted);
  cursor:pointer;display:flex;align-items:center;justify-content:center;border-radius:9px;font-size:19px}
.field__toggle:hover{color:var(--blue);background:var(--bg2)}

/* ---------- 4. Тона строки сообщения (было .field.err) ----------
   У строки под полем ТРИ тона, и каждый обязан читаться отдельно: нейтральная
   подсказка, отказ и подтверждение. Иконку в строку ставит скрипт, значит
   отступ иконки тоже принадлежит ядру, а не той странице, которая додумалась
   первой (раньше и отступ, и зелёный тон стояли в order.css правилами по
   .field__hint, page-CSS красил компонент ядра). */
.field__hint .ti{margin-right:5px;font-size:14px;vertical-align:-2px}

.field--error .field__input{border-color:var(--sale)}
.field--error .field__input:focus{box-shadow:0 0 0 3px rgba(229,51,42,.14)}
.field--error .field__hint{color:var(--ink-alert)}
/* ПОДТВЕРЖДЕНИЕ («Добавлено: …»). Без своего тона оно выглядело ровно как
   исходная подсказка, и добавление ничем себя не выдавало. Цвета токенами:
   текст --green-ink (12.4:1 на белом и на фоне страницы), галочка
   --green-deep (5.05:1 при пороге 3:1). Тон подкреплён ИКОНКОЙ, а не только
   цветом. Рамку поля этот тон НЕ трогает: «принято» относится к сообщению,
   а не к ошибке ввода. */
.field--ok .field__hint{color:var(--green-ink);font-weight:700}
.field--ok .field__hint .ti{color:var(--green-deep)}

/* ---------- 5. Служебная строка формы (было .row-between: «запомнить»/«забыли») ---------- */
.form-row{display:flex;align-items:center;justify-content:space-between;gap:12px;flex-wrap:wrap;margin:4px 0 22px}

/* ---------- 6. Чекбокс / радио (кастомные, было .check + .box) ---------- */
.check{display:inline-flex;align-items:center;gap:9px;font-size:13.5px;font-weight:600;color:var(--ink2);
  cursor:pointer;user-select:none}
.check__input{position:absolute;opacity:0;width:0;height:0}
/* РАМКА ЗДЕСЬ ЕДИНСТВЕННЫЙ НОСИТЕЛЬ ПУСТОГО СОСТОЯНИЯ, отсюда --control, а не
   --line2 (см. закон токена): заливка приходит только с :checked, галочка до
   этого погашена, и на --line2 неотмеченный чекбокс держался на 1.30:1. */
.check__box{width:19px;height:19px;flex-shrink:0;border:1.5px solid var(--control);border-radius:6px;color:#fff;
  display:flex;align-items:center;justify-content:center;transition:background .14s,border-color .14s}
.check__box i{font-size:13px;opacity:0;transition:opacity .1s}
.check__input:checked+.check__box{background:var(--blue);border-color:var(--blue)}
.check__input:checked+.check__box i{opacity:1}
/* ФОКУС. Сам input 0×0 и невидим, поэтому кольцо на коробочке остаётся ЕДИНСТВЕННЫМ
   признаком того, где стоит клавиатура (а на регистрации чекбокс согласия ещё и
   обязательный). Здесь стоял ореол var(--blue-t), а на белой карточке формы
   это 1.15:1, то есть признака не было. Кольцо теперь РОВНО ТО ЖЕ, что у
   кнопки: белая полоса в зазоре + тёмная линия снаружи, 8.94:1 на любой
   подложке (см. .button:focus-visible). */
.check__input:focus-visible+.check__box{
  outline:2px solid var(--blue-d); outline-offset:2px;
  box-shadow:0 0 0 2px #fff;
}
/* радио-вариант (в текущей вёрстке не встречается, добавлен каноном) */
.check--radio .check__box{border-radius:50%}
.check--radio .check__box i{display:none}
.check--radio .check__input:checked+.check__box{background:#fff;border-color:var(--blue)}
.check--radio .check__input:checked+.check__box::after{content:"";width:9px;height:9px;border-radius:50%;background:var(--blue)}

/* МНОГОСТРОЧНАЯ ПОДПИСЬ (согласие на регистрации, три строки со ссылками).
   Базовый .check центрирует коробочку по ВСЕЙ подписи: у одной строки это
   и есть выравнивание, а у трёх коробочка уезжает на середину абзаца и
   перестаёт читаться как отметка к его началу. Модификатор ставит её по
   ПЕРВОЙ строке: верх коробочки совпадает с верхом текста. Отдельным
   модификатором, а не сменой базы: в форме входа подпись однострочная
   («Запомнить меня»), там центрирование верное. */
.check--top{align-items:flex-start}

/* ССЫЛКИ В ПОДПИСИ (оферта, политика конфиденциальности). Без своего правила
   это голый <a>: браузерный #0000EE с подчёркиванием, единственное синее
   пятно на экране, не принадлежащее палитре. Приём берём тот же, что у ссылок
   внутри текста (.prose a): цвет --blue, подчёркивание приходит по наведению.
   Вес 800 при подписи 600 даёт ту же разницу в 200, что у .prose a (700 при 500):
   ссылка выделяется насыщенностью, а не только цветом.
   Правило живёт здесь, а не в auth.css: .check это компонент ядра, и красить
   его со страницы значит развести один компонент по нескольким файлам. */
.check a{color:var(--blue);font-weight:800;text-decoration:none}
.check a:hover{color:var(--blue-d);text-decoration:underline}
.check a:focus-visible{outline:2px solid var(--blue-d);outline-offset:2px;border-radius:4px}

/* ---------- 7. Кастомный селект (было .cs) ---------- */
.select{position:relative;display:inline-flex}
.select__btn{display:inline-flex;align-items:center;gap:10px;height:40px;padding:0 14px 0 16px;
  border:1.5px solid var(--line2);border-radius:999px;background:#fff;
  font-family:inherit;font-size:13px;font-weight:700;color:var(--ink2);cursor:pointer}
.select__btn:hover,.select--open .select__btn{border-color:var(--blue)}
.select__btn i{color:var(--muted);font-size:16px;transition:transform .15s}
.select--open .select__btn i{transform:rotate(180deg)}
.select__menu{position:absolute;top:calc(100% + 6px);right:0;min-width:100%;padding:6px;z-index:var(--z-dropdown);
  background:#fff;border:1px solid var(--line);border-radius:var(--r2);
  box-shadow:0 18px 40px -18px rgba(20,38,90,.3);display:none}
.select--up .select__menu{top:auto;bottom:calc(100% + 6px)}
.select--open .select__menu{display:block}
.select__opt{padding:9px 14px;border-radius:9px;font-size:13px;font-weight:600;color:var(--ink2);
  white-space:nowrap;cursor:pointer}
.select__opt:hover{background:var(--bg2)}
.select__opt--active{background:var(--blue-t);color:var(--blue)} /* было .cs-opt.on */
/* размеры/раскладка по контексту (шапка-поиск 48px, тулбар заказов 44px, моб. на всю ширину) */
.select--lg .select__btn{height:48px}
.select--md .select__btn{height:44px}

/* ---------- 8. Строка поиска в шапке ----------
   УДАЛЕНО (фаза багов ядра). Здесь жил ранний вариант имён поля поиска
   (.header__search / __search-input / __search-submit, свод бывш. .search).
   Разметка партиала говорит другими именами: строка = .header__search,
   а САМО поле = .header__searchbox > .header__searchbox-input +
   .header__searchbox-go, и всё это объявлено в секции HEADER.
   Совпадение имени блока .header__search давало КОЛЛИЗИЮ: правила поля
   (height:48px, border, border-radius:999px, background:#fff, flex:1)
   применялись к СТРОКЕ поиска целиком, и строка получала лишнюю обводку и
   фиксированную высоту 48px при контенте 48px + padding 26px (переполнение).
   Имена __search-input/__search-submit в разметке не встречались вовсе. */

/* УДАЛЕНО (четвёртая приёмка): .searchfield + 11 элементов/модификаторов
   (__icon __input __suggest --open __option __thumb __text __price __empty).
   Это был ВТОРОЙ, мёртвый поиск: в проекте живёт .header__searchbox
   (partials/header.html, стили в секции HEADER), а .searchfield не встречался
   ни в одной из 20 страниц и ни в одном партиале. Понадобится автоподсказка, и
   она станет элементом ЖИВОГО блока (.header__searchbox-suggest и т.д.),
   а не вторым блоком с тем же смыслом. */

/* ==========================================================================
   Адаптив (лестница). Формам нужен один брейк: свод двухколоночной сетки
   и защита от iOS-зума на телефонах. (Исходники: кабинет .dc-grid@600,
   auth @480, qo @560 → канон 560.)
   ========================================================================== */
@media(max-width:560px){
  .field-grid{grid-template-columns:1fr}
  .field__input{font-size:16px} /* iOS не зумит при >=16px */
}


/* ============================================================
   CATALOG
   ============================================================ */
/* ============================================================
   CATALOG TABLE  (.catalog), core.css
   Grid-«таблица»: одна DOM (Bitrix foreach → .catalog__row),
   на узком экране строки перестраиваются в карточки.
   Зависит от глобальных токенов :root:
   --blue --blue-d --blue-t --green-d --ink --ink2 --muted
   --line --line2 --bg2 --pink --sale --r2

   ЭТО ОБЩИЙ БЛОК ПЕРЕЧНЯ ТОВАРОВ, А НЕ «БЛОК СТРАНИЦЫ КАТАЛОГА».
   Грамматика строки одна и та же: Товар · Пол · Размеры · Доступно ·
   Цена · Заказ · Итого · (8-я узкая). Она одна у обоих каталогов И у корзины
   (в рукописном прототипе корзины над таблицей так и написано:
   «корзина = грамматика каталога»). Корзина берёт .catalog целиком и
   меняет только содержимое 8-й ячейки (там не раскрытие, а удаление
   позиции) и надстройку над таблицей (группы «В наличии»/«Предзаказ»,
   это уже вёрстка страницы). Второго рельса на 8 колонок в проекте
   быть не должно.
   ============================================================ */

/* ширины колонок (8 треков): единая точка правды, меняется в @media
   Товар · Пол · Размеры · Доступно · Цена · Заказ · Итого · (раскрытие)
   «Товару» +3fr (30→33) за счёт «Цены» (15→13) и «Итого» (13→12): на 1440
   строке названия доставалось 195px, из них 72 съедала плашка-бейдж, и на
   первую строку оставалось 123px при 127, нужных для «Ботинки кожаные», и
   имя рвалось после первого же слова. Стало 217/145. Слабина у соседей
   замерена: «Цена» держит «2 280 ₽/пара» (≈90px при 146 чистых),
   «Итого» держит «136 800 ₽» (≈67px при 134). */
.catalog{
  --catalog-cols:minmax(0,33fr) minmax(0,9fr) minmax(0,11fr) minmax(0,10fr) minmax(0,12fr) minmax(0,13fr) minmax(0,12fr) minmax(0,3fr);
}

/* ---------- шапка таблицы ---------- */
.catalog__head{display:grid;grid-template-columns:var(--catalog-cols);align-items:end;border-bottom:1px solid var(--line)}
.catalog__head-cell{padding:16px 10px;font-size:12px;font-weight:700;color:var(--muted);text-transform:uppercase;letter-spacing:.04em;text-align:center}
.catalog__head-cell:first-child{text-align:left}
.catalog__head-cell:last-child{padding-left:0;padding-right:6px}
/* иконка-подсказка в шапке таблицы (ⓘ у «Размеры», «Заказ, кор.»).
   Текст подсказки задаётся атрибутом data-tip на самой иконке, механизм
   показа общий, см. секцию TIP + core.js (модуль 8). Иконка без data-tip
   ничего не показывает, поэтому и не должна ставиться. */
/* ОДНА ДЕКЛАРАЦИЯ НА ОБА РЕЛЬСА: та же иконка стоит в шапке перечня заказов
   (.orders__hint, секция ORDERS). Появится третий рельс, заводим общий .hint,
   а не третью копию. Цвет берём --muted (5.28:1): иконка носит подсказку, то есть
   это графика управления, порог 3:1; прежний #b9c0cc давал 1.83:1. */
.catalog__hint,
.orders__hint{margin-left:4px;font-size:13px;color:var(--muted);vertical-align:-2px}
.catalog__hint:hover,
.orders__hint:hover{color:var(--blue)}

/* ---------- строки и ячейки ---------- */
.catalog__row{display:grid;grid-template-columns:var(--catalog-cols);align-items:center;border-top:1px solid var(--line);transition:background .12s}
.catalog__body>.catalog__row:first-child{border-top:0}
.catalog__body>.catalog__row:not(.catalog__row--exp):hover{background:#FAFBFE}
.catalog__row--exp{background:#F6F9FD}
.catalog__cell{padding:16px 10px;min-width:0;text-align:center}
.catalog__cell--prod{text-align:left}
.catalog__cell--chev{padding-left:0;padding-right:6px}

/* ---------- ячейка «Товар» ---------- */
.catalog__product{display:flex;align-items:center;gap:13px;min-width:0}
.catalog__product-body{min-width:0}
.catalog__thumb{position:relative;width:64px;height:82px;flex-shrink:0;border-radius:12px;background:var(--bg2);display:flex;align-items:center;justify-content:center;color:var(--faint);overflow:hidden;cursor:zoom-in}
/* заглушка без фото: открывать нечего → не курсор-лупа, не фокус-цель
   (модификатор ставит core.js при «оживлении» миниатюр, см. notes) */
.catalog__thumb--empty{cursor:default}
/* иконка-заглушка «фото нет» внутри миниатюры (в т.ч. из onerror-фолбэка):
   размер задаёт КОМПОНЕНТ, а не страница (было style="font-size:24px",
   потом page-level в catalog.css) */
.catalog__thumb>i{font-size:24px}
.catalog__thumb img{width:100%;height:100%;object-fit:contain;display:block}
.catalog__thumb:focus-visible{outline:2px solid var(--blue);outline-offset:2px}
.catalog__thumb-zoom i{font-size:12px}
.catalog__thumb-zoom{position:absolute;right:4px;top:4px;width:20px;height:20px;border-radius:50%;background:rgba(255,255,255,.95);display:flex;align-items:center;justify-content:center;color:var(--blue);box-shadow:0 1px 4px rgba(20,38,90,.25)}
/* СТРОКА НАЗВАНИЯ ОБТЕКАЕТ БЕЙДЖ, А НЕ ДЕЛИТ С НИМ ШИРИНУ.
   Было два подхода, и оба заказчик забраковал:
     1) бейдж инлайном внутри названия: на длинном имени пилюля не влезала
        в строку и сваливалась ПОД название;
     2) бейдж соседом по флексу: пилюля встала справа, но отняла у имени
        треть ширины НАВСЕГДА (замер 1440: строка 195px, из них имени 125),
        и «Ботинки утеплённые с мембраной «Зимний пух»» вытянулись в 4 строки
        столбиком ПОД пилюлей, и читалось ровно как «лейбл сверху, текст снизу».
   Обтекание снимает обе беды: бейдж стоит справа от ПЕРВОЙ строки, вторая и
   следующие берут всю ширину колонки, и то же имя укладывается в две строки.
   Поэтому бейдж печатается ПЕРВЫМ узлом (float обтекается только тем, что
   идёт после него); для чтения с экрана это тоже верный порядок: сначала
   пометка, потом название. */
.catalog__titleline{display:block}
/* ДЛИННОЕ СЛОВО ЛОМАЕТСЯ, А НЕ ВЫЛЕЗАЕТ В СОСЕДНЮЮ ЯЧЕЙКУ. min-width:0
   держит в границах КОРОБКУ ссылки, но не её текст: на 769–804 колонка
   «Товар» ужимается до 75px, а слово «утеплённые» требует 82, и буквы
   ложились поверх колонки «Пол» (приёмка Б2). Перенос внутри слова это
   единственное, что здесь остаётся: ужимать колонку дальше некуда
   (замеры ступени лежат в catalog.css, блок ≤900). */
/* line-height 1.45 (20.3px) на волос выше плашки-бейджа (20px), см. правило
   ниже: строка обязана быть выше плавающей коробки, иначе плашка укорачивает
   и вторую строку тоже. */
.catalog__name{font-size:14px;font-weight:700;line-height:1.45;color:inherit;text-decoration:none;overflow-wrap:anywhere}
/* БЕЙДЖ СТРОКИ ЭТО ПЛАВАЮЩАЯ ПЛАШКА СПРАВА (оригинал: .bdg рядом с .pname).
   Крой взят у плоской плашки оригинала: обычный регистр, без трекинга. Пилюля
   999px/uppercase (.tag--pill) в оригинале принадлежит КАРТОЧКЕ товара
   (.pd-pre/.pd-exc) и остаётся там.
   ⚠️ ВЫСОТА ТА ЖЕ 20px, НО РАЗЛОЖЕНА ИНАЧЕ: у базового .tag line-height:1 и
   поля 5px, и заглавные буквы садились в коробке выше середины: сверху
   оставалось 3px, снизу 3.5px, и перекос было видно. 14px строка + 3px поля
   дают те же 20px, но глюфы центрует уже сам шрифт. */
/* ⚠️ ВЫСОТА ПЛАШКИ ОБЯЗАНА БЫТЬ НИЖЕ СТРОКИ НАЗВАНИЯ (20px против 20.3px).
   Плавающая коробка укорачивает КАЖДУЮ строку, которую задевает хотя бы на
   пиксель: с полями 1px сверху и 2px снизу плашка занимала 23px и залезала
   на вторую строку, обе получали по 123px вместо 195, в них влезало по
   одному слову, и трёхсловное имя вытягивалось в три строки. Поля сняты:
   теперь укорочена ровно первая строка, остальные берут всю ширину. */
.catalog__titleline>.tag{
  float:right;margin:0 0 0 10px;
  line-height:1.4;padding-block:3px;
  text-transform:none;letter-spacing:normal;
}
/* ЦВЕТА ПЛАШКИ СТРОКИ ОБЩИЕ (.tag--new / --sale / --exc), СВОЕГО СКИНА НЕТ.
   РЕШЕНИЕ ВЛАДЕЛЬЦА 29.07, не забывать при следующих сверках с оригиналом.
   В согласованном макете строка каталога и витрина главной красили «Новинку»
   ПО-РАЗНОМУ: в строке (.bdg.new) она была ЗЕЛЁНАЯ (--green + белый), на
   карточке витрины (.pcard-badge.new) синяя. Я вернул строке зелёный и
   показал владельцу замер: белый на --green даёт 1.70:1 при пороге 4.5, и
   на телефоне слово превращается в светлое пятно. Владелец выбрал СИНИЙ,
   то есть сознательно отказался от макетного цвета ради читаемости.
   Поэтому здесь НЕТ узкого скина: строка берёт общий синий .tag--new, а
   «Распродажа» берёт общий затемнённый --sale-d (5.54:1 против 4.34:1 у
   исходного --sale) по тому же основанию.
   ⚠️ НЕ «ЧИНИТЬ» ОБРАТНО НА ЗЕЛЁНЫЙ ПРИ СЛЕДУЮЩЕЙ СВЕРКЕ С ОРИГИНАЛОМ:
   расхождение с макетом здесь намеренное и согласованное. */
/* подпись под названием начинается ПОД плашкой, а не обтекает её:
   короткое имя не должно утаскивать артикул и цвет на первую строку */
.catalog__meta{clear:both;margin-top:5px;font-size:12px;color:var(--muted);font-weight:600}

/* ---------- содержимое ячеек «Размеры / Цена / Итого» ---------- */
.catalog__range{font-size:14px;font-weight:700}
.catalog__subnote{margin-top:2px;font-size:12px;color:var(--muted);font-weight:600}
.catalog__price b{font-size:15px;font-weight:800}
/* единица измерения не разрывается: без nowrap на 950–1150 «/кор.» рвалось
   после слэша («12 600 ₽ /» + «кор.») */
.catalog__price-unit{font-size:12px;font-weight:600;color:var(--muted);white-space:nowrap}
.catalog__price-sub{margin-top:2px;font-size:12px;color:var(--muted);font-weight:600}
.catalog__sum{font-size:16px;font-weight:800}

/* ---------- «ДАННЫХ НЕТ»: ПРОЧЕРК (.nodata) ----------
   Прочерк в ячейке («пола/ростовки/остатка у этой позиции взять неоткуда»)
   печатался ДВУМЯ страничными классами (.cart__nodata и .order__nodata),
   и они уже разошлись в главном: в цвете. У кабинета --muted (5.28:1), у
   корзины --faint (1.65:1, нечитаемо). Прочерк несёт СМЫСЛ, значит это
   ТЕКСТ с порогом AA 4.5:1, и цвет ему обязано задавать ЯДРО, а не страница
   (то же правило, что у бейджа и панели). Разметку печатает KTable.nodata.
   Второго имени у «нет данных» в проекте не будет: страничные .cart__nodata
   и .order__nodata из cart.css и cabinet.css удаляются (см. хендофф). */
.nodata{font-size:13px;font-weight:700;color:var(--muted)}

/* ---------- кнопка раскрытия строки ----------
   ⚠️ Цвет здесь --muted, а не прежний #b9c0cc: это ЕДИНСТВЕННАЯ разметка кнопки
   (только иконка, ни подписи, ни рамки), а у графики управления порог 3:1
   (WCAG 1.4.11). Было 1.83:1 на белом, стало 5.28:1. */
.catalog__toggle{display:inline-flex;padding:0;border:0;background:none;color:var(--muted);cursor:pointer}
.catalog__toggle i{font-size:20px}

/* ---------- раскрытие характеристик ---------- */
.catalog__spec{display:none;background:#F6F9FD}
.catalog__spec--open{display:block}
.catalog__specbox{padding:2px 2px 16px 87px}/* левый отступ = миниатюра(64)+gap(13)+паддинг ячейки(10) → ровно под названием */
.catalog__spec-title{margin:0 0 12px;font-size:12px;font-weight:700;color:var(--muted);text-transform:uppercase;letter-spacing:.07em}
.catalog__specsheet{display:grid;grid-template-columns:1fr 1fr;gap:0 56px;max-width:720px}
.catalog__specrow{display:flex;align-items:baseline;justify-content:space-between;gap:16px;padding:9px 2px;border-top:1px solid var(--line)}
.catalog__specsheet .catalog__specrow:nth-child(1),
.catalog__specsheet .catalog__specrow:nth-child(2){border-top:0}
.catalog__specrow-key{color:var(--muted);font-size:12.5px;font-weight:600}
.catalog__specrow-val{color:var(--ink);font-size:12.5px;font-weight:700;text-align:right}

/* ---------- пол-лейбл (.gender) ---------- */
.gender{display:inline-block;padding:3px 11px;border-radius:999px;font-size:12px;font-weight:700;white-space:nowrap}
.gender--girl{background:#FBEAF0;color:#A23A63}
.gender--boy{background:var(--blue-t);color:var(--blue)}
.gender--uni{background:#EAF3DE;color:var(--ink-ok)}

/* ---------- точки наличия (.stock) ----------
   ТРИ СЛОТА, ЗАЛИТО k, и это уровень остатка: 0 нет · 1 мало · 2 достаточно ·
   3 много. Разметку печатает View::stockHtml, здесь только скин; индикатор
   один на оба каталога, карточку товара, корзину и кабинет.

   ⚠️ ЗНАЧЕНИЯ ВЗЯТЫ ИЗ СОГЛАСОВАННОГО МАКЕТА, А НЕ ИЗ ПЕРЕСЧЁТА КОНТРАСТА.
   Здесь стояла «починка» под WCAG 1.4.11: точка 10px с обводкой 1.5px
   --muted у обеих точек и заливкой --green-deep у залитой. Формально
   контрастнее, но на экране это ДРУГОЙ компонент: обведённые кольца
   вместо плоских кружков и тёмно-оливковый вместо брендового лайма;
   заказчик именно это и увидел («цвета и обводки не как в макете»).
   Возвращён макет: пустой слот плоский --stock-slot, залитый --green-d,
   диаметр 9px, обводки нет вовсе. Случай и решение те же, что у полосы
   прокрутки (токены --sb-*): согласованный вид не «чинят».

   ЧЕМ ЗАКРЫТ СМЫСЛ ДЛЯ ТЕХ, КТО ОТТЕНКИ НЕ РАЗЛИЧАЕТ. Уровень никогда не
   держится на одном цвете: его несёт ЧИСЛО ЗАЛИТЫХ ТОЧЕК (форма, а не тон),
   подпись View::stockLabel в aria-label самого .stock («Мало,
   заканчивается (3 коробки)») и видимый текст рядом, в строке остатка
   карточки товара и в матрице размеров. Убирать подпись из разметки нельзя:
   без неё уровень остаётся только в цвете. Цвет ТЕКСТА .stock задан --muted
   (5.28:1 на белом), он к макету претензий не имел и не меняется.
   Приглушённая строка (.sizes__row--out, opacity .5) это «неактивный
   компонент»: там уровень продублирован словом «нет» на месте степпера. */
.stock{display:inline-flex;align-items:center;gap:8px;font-size:13px;font-weight:700;color:var(--muted);cursor:default}
.stock__dots{display:inline-flex;gap:4px}
.stock__dot{display:block;width:9px;height:9px;border-radius:50%;background:var(--stock-slot)}
.stock__dot--full{background:var(--green-d)}

/* ---------- степпер количества (.stepper) ---------- */
.stepper{display:inline-flex;align-items:center;height:38px;border:1px solid var(--line2);border-radius:10px;background:#fff}
.stepper__btn{display:flex;align-items:center;justify-content:center;width:32px;height:36px;border:0;border-radius:9px;background:none;color:var(--blue);font-size:19px;line-height:1;cursor:pointer}
.stepper__btn:first-child{border-radius:9px 0 0 9px}
.stepper__btn:last-child{border-radius:0 9px 9px 0}
.stepper__btn:hover{background:var(--bg2)}
/* предел достигнут (0 шт / весь остаток): кнопка гаснет и перестаёт реагировать.
   Совпадает с трактовкой disabled у .catalog__runbtn и .button.
   ⚠️ Состояние задаётся ТОЛЬКО атрибутом disabled (кнопка обязана быть недоступна и с
   клавиатуры). Класс-дубль .stepper__btn--disabled удалён в третью приёмку:
   ни одна страница его не ставила, а класс без атрибута оставлял кнопку кликабельной. */
.stepper__btn:disabled,
.stepper__btn[disabled]{opacity:.4;cursor:default}
.stepper__btn:disabled:hover,
.stepper__btn[disabled]:hover{background:none}
/* ⚠️ outline:0 ЗДЕСЬ БОЛЬШЕ НЕТ, И ЭТО НЕ ОПЕЧАТКА. Он гасил глобальное
   кольцо ядра, а весь признак фокуса держала строка ниже: background:#fafcff
   на белом степпере, а это 1.02:1. Это не «слабое кольцо», это ОТСУТСТВИЕ фокуса:
   поле количества это главный контрол каталога, корзины и карточки товара
   (сотни таких полей на страницу), и с клавиатуры было не видно, в котором
   из них ты стоишь.
   КОЛЬЦО УХОДИТ ВНУТРЬ (outline-offset:-2px), а не наружу: поле зажато
   между «−» и «+» вплотную, кольцо со смещением наружу легло бы на соседние
   кнопки и вылезло за скруглённую рамку .stepper. Внутри поля фон белый →
   --blue даёт 6.38:1. Тот же приём и по той же причине, что у
   .artorder__option (order.css). Подсветка #fafcff остаётся мягким
   довеском, носителем состояния она не является. */
.stepper__input{width:40px;height:36px;border:0;border-left:1px solid var(--line);border-right:1px solid var(--line);background:none;font-family:inherit;font-size:15px;font-weight:800;text-align:center;-moz-appearance:textfield}
.stepper__input:focus{background:#fafcff}
.stepper__input:focus-visible{outline:2px solid var(--blue);outline-offset:-2px}
.stepper__input::-webkit-outer-spin-button,
.stepper__input::-webkit-inner-spin-button{-webkit-appearance:none;margin:0}
.stepper--sm{height:32px}
.stepper--sm .stepper__btn{width:26px;height:30px;font-size:17px}
.stepper--sm .stepper__input{width:30px;height:30px;font-size:13px}

/* ---------- тумблер вида (одежда): «Размерным рядом / По размерам» ---------- */
.catalog__viewbar{display:flex;align-items:center;gap:12px;margin:0 0 16px}
.catalog__view-label{font-size:13px;color:var(--muted);font-weight:600}
.catalog__view{display:inline-flex;gap:2px;padding:3px;border-radius:999px;background:var(--bg2)}
.catalog__view-btn{display:inline-flex;align-items:center;gap:7px;padding:8px 16px;border:0;border-radius:999px;background:none;font-family:inherit;font-size:13px;font-weight:700;color:#5b6068;cursor:pointer}
.catalog__view-btn i{font-size:16px}
.catalog__view-btn--on{background:#fff;color:var(--blue);box-shadow:0 1px 4px rgba(20,38,90,.14)}

/* ---------- размерная матрица (режим «По размерам») ---------- */
/* строка-матрица: cell--prod/--gender/--chev тянутся на всю высоту,
   подстроки .sizes__row--grid берут колонки у родителя через subgrid.

   НОМЕР СТРОКИ ЗАДАЁТСЯ ПЕРЕМЕННОЙ --row, А НЕ ИНЛАЙНОВЫМ grid-row.
   Он считается только в JS (зависит от длины размерной линейки), поэтому
   остаётся в атрибуте style, но как ЗНАЧЕНИЕ переменной:
       style="--row:3"        обычная подстрока
       style="--row:1/span 9" ячейка на всю высоту строки
   Применяет его ОДНО правило ниже. Раньше страница писала grid-row инлайном,
   а мобильная ступень гасила его единственным в проекте `!important`,
   а с переменной перебивать нечего, и !important снят. */
.catalog__row--matrix>*{grid-row:var(--row,auto)}
/* САМИ подстроки описаны НЕ здесь, а в независимом компоненте .sizes ниже:
   тот же «размер · доступно · цена · степпер · сумма» нужен и карточке товара,
   и корзине. Прежние .catalog__szrow/__szcell/__subt/__szna удалены. */
/* РАЗДЕЛИТЕЛЬ ПЕРЕД РАСКРЫТОЙ ПОЗИЦИЕЙ ТАКОЙ ЖЕ, КАК МЕЖДУ ВСЕМИ ОСТАЛЬНЫМИ
   СТРОКАМИ: 1px --line.
   ⚠️ В СОГЛАСОВАННОМ ОРИГИНАЛЕ ЗДЕСЬ БЫЛО 2px var(--line2) (.krow.szx),
   намеренно более заметная линия между товарами. Заказчик разобрал результат
   и попросил единообразия: «два пикселя, а везде один, тоже бросается».
   Это ОСОЗНАННАЯ СМЕНА ПО ЕГО ПРОСЬБЕ, а не возврат и не недосмотр:
   «чинить» обратно на 2px нельзя. */
.catalog__row--matrix{align-items:stretch;border-top:1px solid var(--line)}
.catalog__row--matrix>.catalog__cell--prod,
.catalog__row--matrix>.catalog__cell--gender,
.catalog__row--matrix>.catalog__cell--chev{align-self:start;padding-top:16px}
.catalog__row--matrix>.catalog__szfoot{display:flex;align-items:center;justify-content:flex-start;padding:11px 10px 12px;border-top:1px solid var(--line)}
.catalog__row--matrix>.catalog__runfoot{display:flex;align-items:center;justify-content:center;gap:6px;padding:9px 8px 10px;border-top:1px solid var(--line)}
.catalog__row--matrix>.catalog__sztotal{display:flex;align-items:center;justify-content:center;padding:11px 10px 12px;border-top:1px solid var(--line);font-size:16px;font-weight:800;color:var(--ink)}

/* кнопки матрицы */
.catalog__runbtn{display:inline-flex;align-items:center;gap:4px;padding:7px 11px;border:1.5px solid var(--line2);border-radius:999px;background:none;font-family:inherit;font-size:12.5px;font-weight:700;color:var(--blue);white-space:nowrap;cursor:pointer}
.catalog__runbtn:hover{border-color:var(--blue);background:var(--blue-t)}
.catalog__runbtn[disabled]{opacity:.4;cursor:default}
.catalog__runbtn[disabled]:hover{border-color:var(--line2);background:none}
.catalog__bysize{display:inline-flex;align-items:center;gap:6px;padding:8px 13px;border:1.5px solid var(--line2);border-radius:999px;background:none;font-family:inherit;font-size:12.5px;font-weight:700;color:var(--blue);white-space:nowrap;cursor:pointer}
.catalog__bysize:hover{border-color:var(--blue);background:var(--blue-t)}
.catalog__collapse{display:inline-flex;align-items:center;gap:5px;padding:6px 4px;border:0;background:none;font-family:inherit;font-size:12.5px;font-weight:700;color:var(--blue);cursor:pointer}
.catalog__collapse:hover{text-decoration:underline}
.catalog__collapse i{font-size:14px}   /* было style="font-size:14px" в разметке */
/* УДАЛЕНО (третья приёмка): .catalog__collapse--lite, приглушённый вариант «Свернуть».
   В разметке каталогов и карточек товара не встречается (проверено грепом по всем
   20 страницам + партиалам). */

/* ============================================================
   АДАПТИВ (desktop-first, лестница 1240/1024/900/768/560/400)
   К таблице относятся: 1240 (шире «Товар»), 768 (таблица→карточки),
   560/400 (доводка карточек). 1024/900 таблицу не трогают.
   ============================================================ */
@media(max-width:1240px){
  /* На 1001–1150 ячейка «Цена» ломалась на три строки, «12 600 ₽» и «/кор.»
     уезжали на разные строки. Правится ОДНОЙ точкой правды --catalog-cols.
     ⚠️ ЗДЕСЬ ЖИЛИ ДВА РАЗНЫХ НАБОРА на одну и ту же ступень: ядро ставило
     30·8·10·9·15·15·10·3 на .catalog, а catalog.css ставил 27·10·10·9·14·15·12·3
     на .listing__thead/.listing__tbody, то есть на ДЕТЕЙ того же блока, и
     значение на самом узле всегда побеждало унаследованное: правило ядра
     было мёртвым, а мерили доли по страничному файлу.
     ОСТАВЛЕН ОДИН НАБОР, ЭТОТ, со ЗАМЕРЕННЫМИ страницей значениями: «Пол» 8→10fr
     (пилюля «Мальчики» 76px + 20px полей ячейки перестаёт ложиться на глифы
     колонки «Размеры»), «Итого» 10→12fr (длинная сумма с шевроном) за счёт
     «Товара» (30→27) и «Цены» (15→14). Ступени ≤1024 и ≤900 остаются у
     catalog.css: там доли перераспределяются вместе со СТРАНИЧНЫМ
     содержимым (сокращение пола, мельче миниатюра), это не дело ядра. */
  .catalog{--catalog-cols:minmax(0,27fr) minmax(0,10fr) minmax(0,10fr) minmax(0,9fr) minmax(0,14fr) minmax(0,15fr) minmax(0,12fr) minmax(0,3fr);}
}
@media(max-width:900px){
  /* БЕЙДЖ СПРАВА ОТ НАЗВАНИЯ. Плашка не переносится и не сжимается, поэтому
     на 769–900 ужимается САМА ПЛАШКА (поля 9→7px), а название сжимается и
     переносится под собой. Селектор был `.catalog__name .tag`, и после
     переноса бейджа в соседи названия он не совпадал бы ни с чем и правило
     умерло бы молча. На карточке товара и на витрине бейдж прежний. */
  .catalog__titleline>.tag{padding-left:7px;padding-right:7px}
}

/* ≤768: та же DOM перестраивается в КАРТОЧКИ */
@media(max-width:768px){
  .catalog__head{display:none}
  .catalog__body>.catalog__row{grid-template-columns:1fr 1fr;align-items:center;gap:10px 14px;margin-bottom:12px;padding:12px 14px 14px;border:1px solid var(--line2);border-radius:14px}
  .catalog__body>.catalog__row:first-child{border-top:1px solid var(--line2)}
  /* контур раскрытой карточки задаётся с родителем: одноклассовый селектор проигрывал
     по специфичности `.catalog__body>.catalog__row` и правило было мёртвым */
  .catalog__body>.catalog__row--exp{border-color:var(--blue-t)}
  .catalog__cell{padding:0;text-align:left}
  /* Шапка таблицы скрыта → значение без подписи повисает в пустоте («Девочки»,
     три точки, степпер, голое «12 600 ₽»). Подпись даёт САМА страница атрибутом
     data-label на ячейке (тексты разные у обуви и одежды: «Заказ, кор.» / «Заказ»),
     а КАК она рисуется, решает правило компонента (переехало из catalog.css). */
  .catalog__cell[data-label]{display:flex;align-items:center;justify-content:space-between;
    gap:10px;min-height:38px;justify-self:stretch;text-align:left}
  .catalog__cell[data-label]::before{content:attr(data-label);flex:0 0 auto;
    font-size:12px;font-weight:700;color:var(--muted);text-transform:uppercase;letter-spacing:.04em}
  /* тач-таргеты кнопок компонента: «− ряд / + ряд» было 58×34, тумблер вида 172×34 */
  .catalog__runbtn{min-height:36px}
  .catalog__view-btn{min-height:36px}
  .catalog__cell--prod{grid-column:1 / -1;padding-bottom:10px;border-bottom:1px solid var(--line)}
  /* строка стала карточкой, тесноты нет: бейджу возвращаются поля оригинала
     (ужимал их шаг ≤900, он же накрывал и эту ступень) */
  .catalog__titleline>.tag{padding-left:9px;padding-right:9px}
  .catalog__cell--price,.catalog__cell--total{text-align:right}
  /* «ЗАКАЗ» И «ИТОГО» ИДУТ ВО ВСЮ ШИРИНУ КАРТОЧКИ, А НЕ В ПОЛОВИНУ.
     Карточка раскладывает пары «подпись/значение» в две колонки, и «Заказ»
     попадал в ЛЕВУЮ: степпер прижимался к её правому краю, то есть к
     середине карточки (замер на 700: степпер 221..343 при ширине карточки
     58..642). А развёрнутая по размерам позиция кладёт свои степперы во всю
     ширину, это 520..642. Один и тот же орган управления стоял в двух местах,
     и это заказчик и увидел. Во всю ширину обе строки встают на одну
     вертикаль: правый край 642 у любого степпера.
     «Итого» тянем следом, иначе сумма осталась бы в половине строки и
     разошлась бы с итогом развёрнутой позиции. */
  .catalog__cell--order,
  .catalog__cell--total{grid-column:1 / -1;justify-self:stretch}
  /* ШЕВРОН ЗДЕСЬ ЕДИНСТВЕННЫЙ ТРИГГЕР «Характеристик» (обработчик слушает
     .catalog__toggle), поэтому на карточке его НЕ прячем, а превращаем в
     полноширинную строку-кнопку с подписью. Подпись даёт CSS (как «Итого»
     у .catalog__sztotal), разметка страниц не меняется. */
  .catalog__cell--chev{grid-column:1 / -1;justify-self:stretch;padding:8px 0 0;margin-top:2px;border-top:1px solid var(--line)}
  .catalog__toggle{width:100%;min-height:36px;display:flex;align-items:center;justify-content:space-between;gap:8px;
    font-family:inherit;font-size:12.5px;font-weight:700;color:var(--blue)}
  .catalog__toggle::before{content:"Характеристики"}
  .catalog__toggle i{font-size:18px}
  .catalog__row--exp .catalog__toggle{color:var(--blue-d)}
  /* раскрытая панель ПРИШИТА к своей карточке: общий контур и скругление
     снизу, без зазора (раньше висела отдельным прямоугольником) */
  .catalog__body>.catalog__row--exp{margin-bottom:0;border-bottom-left-radius:0;border-bottom-right-radius:0}
  .catalog__spec--open{
    margin:0 0 12px;background:#F6F9FD;
    border:1px solid var(--blue-t);border-top:0;
    border-radius:0 0 14px 14px;
  }
  .catalog__specbox{padding:10px 14px 14px}
  /* подпись «Характеристики» уже стоит на кнопке-триггере прямо над панелью */
  .catalog__spec-title{display:none}
  .catalog__specsheet{grid-template-columns:1fr;max-width:none}
  .catalog__specsheet .catalog__specrow:nth-child(2){border-top:1px solid var(--line)}
  /* размерная матрица: строка становится карточкой (display:block) и
     линеаризуется. Grid-позиции гасим обычным правилом, БЕЗ !important:
     номер строки приходит переменной --row (см. базовую ступень), инлайнового
     grid-row в разметке больше нет и перебивать нечего. Это был последний
     !important в ядре. Линеаризацию самих подстрок (.sizes__row--grid)
     делает компонент .sizes, см. его секцию. */
  .catalog__row--matrix{display:block;margin-bottom:12px;padding:12px 14px 14px;border:1px solid var(--line2);border-radius:14px}
  .catalog__row--matrix>*{grid-column:auto;grid-row:auto;align-self:auto;padding:0}
  .catalog__row--matrix>.catalog__cell--prod{padding-bottom:10px;border-bottom:1px solid var(--line)}
  .catalog__row--matrix>.catalog__cell--gender{padding-top:10px}
  /* подвал матрицы. «Свернуть» и сумма это разные смыслы по краям (space-between),
     а «− ряд / + ряд» это ПАРА одной группы: на карточке их разносило к краям
     (на 390px между ними ~330px и они читались как две несвязанные кнопки) →
     держим рядом слева, как на десктопе. */
  .catalog__row--matrix>.catalog__szfoot,
  .catalog__row--matrix>.catalog__sztotal{justify-content:space-between;padding:10px 0}
  .catalog__row--matrix>.catalog__runfoot{justify-content:flex-start;gap:6px;padding:10px 0}
  /* ячейка «Свернуть» пуста в глобальном режиме «По размерам», и пустая полоса
     с линией-разделителем ни к чему. На десктопе НЕ прячем: там эта ячейка
     держит линию-разделитель по колонкам. */
  .catalog__row--matrix>.catalog__szfoot:empty{display:none}
  /* шапка таблицы на карточке скрыта → итог позиции получает подпись
     (типографика как у .catalog__head-cell) */
  .catalog__row--matrix>.catalog__sztotal::before{
    content:"Итого";
    font-size:12px;font-weight:700;color:var(--muted);
    text-transform:uppercase;letter-spacing:.04em;
  }
  /* строка-кнопка «Характеристики» в матричной карточке (идёт последним
     узлом строки, поэтому садится в самый низ карточки) */
  .catalog__row--matrix>.catalog__cell--chev{padding:8px 0 0;margin-top:2px;border-top:1px solid var(--line)}
  /* Матричные степперы (каталог одежды + матрица размеров на карточке
     товара): 26×30 это не тач-таргет. На мобильном укрупняем до ≥36px.
     Правило общее для компонента .stepper (не скоуплено на каталог),
     десктоп не затронут, оно живёт внутри @768. */
  /* БАЗОВЫЙ степпер получает тот же тач-размер, что и --sm: кнопка была 32×36 против
     38×36 у --sm, и в одной строке компаньоны стояли разной ширины. Высота
     кнопки 36px (≥ порога), общая высота блока остаётся базовые 38px. */
  .stepper__btn{width:38px;height:36px}
  .stepper__input{width:44px;height:36px}
  .stepper--sm{height:38px}
  .stepper--sm .stepper__btn{width:38px;height:36px;font-size:19px}
  .stepper--sm .stepper__input{width:44px;height:36px;font-size:14px}
}

/* ≤560: карточка в один столбец */
@media(max-width:560px){
  .catalog__body>.catalog__row{grid-template-columns:1fr}
  .catalog__cell--price,.catalog__cell--total{text-align:left}
  /* ТУМБЛЕР ВИДА ПЕРЕЕХАЛ СЮДА С ≤400, И ЭТО НЕ КОСМЕТИКА.
     Замер: подпись «Вид каталога:» 92px + зазор 12 + пилюля из двух кнопок
     315px = 419px нужных, а внутри .listing доступно (ширина экрана − ~70px).
     419 набирается только с ~497px, поэтому весь диапазон 401–496 (то есть
     402·412·414·428·430, все «большие телефоны») жал пилюлю до min-content:
     «Размерным рядом» ломалось на две строки, кнопка росла 36→51px, подпись
     рвалась на «Вид / каталога:». Лечение то же, что уже было объявлено
     на ≤400: подпись на свою строку, пилюля во всю ширину, кнопки делят её
     поровну, текст не переносится. Иконки на этой ступени ОСТАЮТСЯ: пилюля
     с ними требует 315px, а во всю ширину доступно 331px уже на 401. */
  .catalog__viewbar{flex-wrap:wrap;gap:8px}
  .catalog__view,.catalog__view-btn{flex:1;justify-content:center}
  .catalog__view-btn{white-space:nowrap;padding:8px 10px}
}

/* ≤400: плотнее */
@media(max-width:400px){
  .catalog__body>.catalog__row{padding:12px 12px 14px}
  /* на 390 внутри .listing остаётся 320px, а пилюля с иконками требует 315,
     запаса нет ни на пиксель: иконки убираем, поля кнопки 10→8px */
  .catalog__view-btn{padding:9px 8px;gap:0}
  .catalog__view-btn i{display:none}
}


/* ==========================================================================
   SIZES: размерная матрица. ОДИН компонент на два контекста
   ==========================================================================
   «размер · доступно · цена · степпер · сумма» жил двумя реализациями:
   .catalog__szrow/__szcell/__subt/__szna (каталог одежды, режим «По размерам»)
   и .sizes__row/__num/__avail/__qty/__sub (карточка товара-одежды). Одна
   сущность, одно имя; в батче Б2 та же матрица нужна корзине.

   БАЗА = колонка на карточке товара (собственная сетка 4 колонки).
   МОДИФИКАТОР .sizes__row--grid = строка внутри строки-носителя: ложится в
   subgrid родителя, число колонок диктует родитель, позицию (grid-column/
   grid-row) страница по-прежнему ставит инлайном из JS.

   ТРЕТИЙ КОНТЕКСТ, КОРЗИНА (Б2), НОВЫХ МОДИФИКАТОРОВ НЕ ТРЕБУЕТ.
   Раскрытие позиции «По размерам» в корзине это ровно строка-носитель с
   подстроками, то есть тот же --grid: он описан как «subgrid родителя», а
   не «subgrid каталога», и родителем может быть любая строка на том же
   рельсе. Строка корзины и есть та же строка: у корзины ДОСЛОВНО грамматика
   каталога (Товар · Пол · Размеры · Доступно · Цена · Заказ · Итого · ✕),
   поэтому её таблица берёт блок .catalog (секция CATALOG выше) со всеми его
   раскрытиями: .catalog__row--matrix, .catalog__szfoot, .catalog__sztotal,
   .catalog__runbtn, тумблер .catalog__view*. Своего .cart__szrow/__szfoot
   заводить НЕ НАДО: это была бы четвёртая реализация одной матрицы.
   Единственное отличие корзины это 8-я колонка: у каталога там раскрытие
   (.catalog__cell--chev), у корзины удаление позиции; ширина трека (3fr)
   и паддинги те же, меняется только содержимое ячейки.

   Элементы: __head __row __size __stock __price __qty __sum __na
   Модификаторы: --grid (subgrid родительской строки: каталог, корзина) ·
                 --sep (линия сверху) · --out (размера нет) ·
                 --zero (сумма по строке = 0)
   Зависимости-компоненты: .stepper (кол-во) и .stock (точки наличия).
   ========================================================================== */
/* --sizes-pad: БОКОВОЙ ОТСТУП ТАБЛИЦЫ РАЗМЕРОВ, И ОН ОБЩИЙ.
   Его держат и строки, и подвал с кнопками «± ряд» (.product__runs в карточке).
   Числом он был бы записан дважды и разъехался бы на первой же ступени
   адаптива: их тут пять, и отступ на каждой свой. */
.sizes{display:flex;flex-direction:column;--sizes-pad:18px}
/* тянется ПОСЛЕДНЯЯ колонка («Сумма»): деньги всегда прижаты к правому краю,
   размер·наличие·кол-во стоят рядом слева, и это читается как таблица на любой ширине */
.sizes__head,
.sizes__row{
  display:grid;
  grid-template-columns:54px 88px 116px minmax(0,1fr);
  gap:12px;align-items:center;min-height:50px;padding:0 var(--sizes-pad);
}
.sizes__head{font-size:12px;font-weight:700;color:var(--muted);border-bottom:1px solid var(--line)}
.sizes__head span:last-child{text-align:right}
.sizes__row{font-size:14px}
.sizes__row:not(:last-child){border-bottom:1px solid var(--line)}
.sizes__row--out{opacity:.5}                   /* размера нет в наличии */
.sizes__size{font-weight:800;color:var(--ink)}
.sizes__stock{display:flex;align-items:center;min-width:0}
.sizes__price{font-weight:700;color:var(--ink2)}
.sizes__qty{display:flex;align-items:center}   /* степпер по ширине контента */
/* «нет» стоит на месте степпера (в колонке «Наличие» у такой строки стоят пустые
   точки .stock) */
.sizes__na{font-size:12px;font-weight:700;color:var(--ink-alert)}
.sizes__sum{font-weight:800;color:var(--ink);white-space:nowrap;text-align:right}
/* Сумма по строке = 0 (размер не выбран или его нет): приглушённый прочерк.
   ⚠️ Приглушённый, но ЧИТАЕМЫЙ: это значение колонки, а не украшение. Был
   --faint с 1.65:1 на белом (текст, порог 4.5:1), стал --muted с 5.28:1. */
.sizes__sum--zero{font-weight:600;color:var(--muted)}

/* ---------- адаптив КАРТОЧКИ ТОВАРА (лестница 1024 / 900 / 768 / 560 / 400) ----
   Все эти ступени про СВОЮ сетку компонента; каталожный --grid берёт колонки
   у родителя, и ниже, ПОСЛЕ всех ступеней, отдельным блоком снимает с себя
   всё, что здесь ему не адресовано. */
@media(max-width:1024px){
  /* колонка покупки на карточке товара сузилась до 400px, матрица ужимается,
     чтобы «Сумма» не выезжала за скруглённую рамку */
  .sizes__head,
  .sizes__row{grid-template-columns:44px 76px 104px minmax(0,1fr);gap:10px}
  .sizes{--sizes-pad:14px}
}
@media(max-width:900px){
  /* блок покупки снова во всю ширину колонки, возвращаем просторные колонки */
  .sizes__head,
  .sizes__row{grid-template-columns:54px 88px 116px minmax(0,1fr);gap:12px}
  .sizes{--sizes-pad:18px}
}
@media(max-width:768px){
  /* с этой ступени .stepper--sm вырастает до тач-размера (кнопки 38px, степпер
     целиком 122px), и колонка «Кол-во» обязана его вмещать, иначе степпер
     вылезает в зазор и подпирает «Сумму» */
  .sizes__head,
  .sizes__row{grid-template-columns:54px 88px 124px minmax(0,1fr)}
}
@media(max-width:560px){
  /* Колонка «Кол-во» теперь 124px: на телефоне .stepper--sm тач-размера (38px
     кнопки, 122px в сумме) в прежние 104px не влезал и наезжал на «Сумму». */
  .sizes__head,
  .sizes__row{grid-template-columns:44px 48px 124px minmax(0,1fr);gap:8px}
  .sizes{--sizes-pad:12px}
  /* шапка матрицы не мельче 11px (было 9px, на грани читаемости);
     под неё расширены колонки «Размер» и «Наличие», чтобы подписи не переносились */
  .sizes__head{font-size:12px;letter-spacing:.01em}
}
@media(max-width:400px){
  /* ❗ЧЕТЫРЁМ КОЛОНКАМ ЗДЕСЬ УЖЕ НЕ ХВАТАЕТ ШИРИНЫ, И СЖИМАТЬ БОЛЬШЕ НЕЧЕГО.
     Замер на 375: строка 271px, а «Размер» 44 + «Наличие» 50 + «Пар» 124
     (столько занимает тач-степпер, меньше нельзя) плюс три зазора съедают
     236, «Сумме» остаётся 15px при нужных 54, и она обрезалась прямо на
     цифрах: на экране стояло «13 380» без знака рубля. На 320 колонка
     схлопывалась в ноль совсем.
     Поэтому на этой ступени сумма уходит ВТОРОЙ СТРОКОЙ во всю ширину и
     прижимается вправо: это единственное, что не требует отнимать место
     ни у степпера, ни у наличия. Шапка ту же ячейку не печатает: подпись
     «Сумма» над пустым местом читалась бы как потерянная колонка. */
  .sizes__head,
  .sizes__row{grid-template-columns:44px 50px minmax(0,1fr);gap:6px}
  .sizes__head > :last-child{display:none}
  .sizes__row{row-gap:2px}
  .sizes__sum{grid-column:1/-1;font-size:13px;text-align:right}
  .sizes{--sizes-pad:10px}
}

/* ---------- --grid: та же матрица внутри строки-носителя (subgrid) ----------
   Носителем служит .catalog__row--matrix: каталог одежды в режиме «По размерам» и
   раскрытая позиция корзины (одна и та же строка на одном рельсе).
   ⚠️ БЛОК СТОИТ ПОСЛЕ ВСЕХ СТУПЕНЕЙ КАРТОЧКИ, И ЭТО ОБЯЗАТЕЛЬНО.
   Медиазапрос специфичности не добавляет: при равной специфичности решает
   ПОРЯДОК В ФАЙЛЕ. Пока --grid стоял выше, на ≤560/≤400 к каталожной строке
   прилетали card-значения (gap 6px и padding 0 10px вместо 8/12 и 10px 0,
   сумма мельчала до 13px), это замерено. Отсюда и вниз ничего card-специфичного
   быть не должно.
   Колонки берём у родителя, поэтому свою сетку и свои поля гасим:
   gap:normal возвращает subgrid к зазорам родителя. */
.sizes__row--grid{
  grid-template-columns:subgrid;
  gap:normal;align-items:stretch;min-height:0;padding:0;
}
.sizes__row--grid:not(:last-child){border-bottom:0}
.sizes__row--grid:hover{background:var(--bg2)}
.sizes__row--sep{border-top:1px solid var(--line)}
/* в каталоге ячейку центрирует и «раздаёт» высоту сама строка (на карточке
   товара ячейки это обычный текст в клетках своей сетки) */
.sizes__row--grid>*{display:flex;align-items:center;justify-content:center;min-height:48px;padding:4px 10px}
/* сумма в каталоге не мельчает вместе с карточкой товара (двухклассовый
   селектор бьёт .sizes__sum{font-size:13px} из ступени ≤400 по специфичности) */
.sizes__row--grid .sizes__sum{font-size:inherit}

@media(max-width:768px){
  /* Каталог на этой ступени это карточки, а не таблица: subgrid родителя
     больше нет. РАСКЛАДКА ПОДСТРОКИ ОБЪЯВЛЕНА, А НЕ ОТДАНА ПЕРЕНОСУ.
     Было flex-wrap, и одна и та же матрица собиралась по-разному на каждой
     ширине: на 430 всё вставало в одну строку, на 390 вниз уезжала только
     «сумма» и висела одна у правого края, на 360/320 вниз уезжал степпер.
     Внутри ОДНОЙ матрицы результат тоже плавал: «122» шире «92» на ~8px при
     запасе 4px, а строка размера без остатка (вместо степпера короткое «нет»)
     оставалась однострочной, и матрица шла лесенкой из строк разной высоты.
     Теперь всегда две строки, одинаковые у всех размеров:
         размер · наличие ------------- цена
         степпер --------------------- сумма
     Третья колонка прижата вправо: деньги стоят одним столбцом, как в
     таблице, и сумма читается под ценой, а не рядом с ней.
     ⚠️ СРЕДНЯЯ КОЛОНКА ЗАДАНА minmax(0,1fr), А НЕ auto, И ЭТО НЕ ВКУСОВЩИНА.
     Степпер занимает колонки 1-2; если обе они auto, его ширина (122px)
     раздаётся в эти треки, и колонка «наличие» встаёт на разный x в строке
     со степпером и в строке с коротким «нет» (замер: 34 против 59px),
     та же лесенка, только по горизонтали. Гибкий трек забирает разницу на
     себя, и левый столбец у всех строк матрицы одной ширины.
     Ячейка «наличие» есть не у всех потребителей матрицы (в детальной
     заказа её нет), и пустой гибкий трек просто растягивается. */
  /* 26px в первой колонке это ширина трёхзначного размера (замер: «122» 25,2px,
     «92» 16,8px). Без этой полки колонка «наличие» вставала на разный x у
     соседних строк ОДНОЙ матрицы (75 против 83 на 390), та же рваность,
     из-за которой всё и переделывается. Верх auto: длиннее «122» размеров
     нет, но появятся, и трек вырастет сам. */
  .sizes__row--grid{
    display:grid;
    grid-template-columns:minmax(26px,auto) minmax(0,1fr) auto;
    gap:6px 10px;align-items:center;
    padding:10px 0;border-top:1px solid var(--line);
  }
  .sizes__row--grid>*{min-height:0;padding:0;justify-content:flex-start}
  .sizes__row--grid>.sizes__size {grid-column:1;grid-row:1}
  .sizes__row--grid>.sizes__stock{grid-column:2;grid-row:1}
  .sizes__row--grid>.sizes__price{grid-column:3;grid-row:1;justify-content:flex-end}
  .sizes__row--grid>.sizes__qty  {grid-column:1/3;grid-row:2}
  .sizes__row--grid>.sizes__sum  {grid-column:3;grid-row:2;justify-content:flex-end}
  /* размер без остатка: на месте степпера короткое «нет». Своей высоты у него
     нет, и строка выходила на 19px ниже соседних (замер на 390: 65 против 84),
     матрица опять шла лесенкой. Держим за ним место степпера. */
  .sizes__row--grid>.sizes__qty>.sizes__na{display:inline-flex;align-items:center;min-height:38px}
}


/* ============================================================
   BREADCRUMBS
   ============================================================ */
/* ============================================================
   .breadcrumbs, хлебные крошки. ОБЩИЙ блок: каталог, карточка
   товара, корзина, заказ, кабинет, контентные страницы.
   Разметка это плоская строка ссылок с иконками-шевронами между
   ними; текущая страница это <b> (не ссылка).
   Токены: --muted / --blue. Брейк один, 560 (см. --wrap).
   ============================================================ */
/* Верхняя отбивка обязательна: без неё цепочка прилипает к нижней кромке
   шапки и читается как её продолжение. Низ меньше верха: крошки
   принадлежат заголовку под ними, а не шапке над ними. */
.breadcrumbs{display:flex;gap:8px;align-items:center;font-size:13px;color:var(--muted);margin:18px 0 12px}
/* размер шеврона-разделителя задан здесь, а не инлайном в разметке
   (в страницах он был зашит как style="font-size:13px" на каждом <i>) */
.breadcrumbs i{font-size:13px;flex-shrink:0}
/* КОНВЕНЦИЯ (канон, см. _core-notes → ## core-bugfix-2 §9 «КРОШКИ»;
   отдельной секции ## breadcrumbs в заметках нет): каждый предок цепочки это
   <a href="…"> на РЕАЛЬНЫЙ прототип, а текущая страница это <b> БЕЗ ссылки.
   Курсор/цвет ссылки даём только там, где href есть: <a> без href больше не
   притворяется кликабельным (был синим с cursor:pointer и не кликался). */
.breadcrumbs a[href]{color:var(--blue);text-decoration:none;cursor:pointer}
.breadcrumbs a[href]:hover{text-decoration:underline}
/* НЕссылочный элемент цепочки это текущая страница: и <b>, и «забытый» <a>
   без href рисуются одинаково нейтральным текстом */
.breadcrumbs b,
.breadcrumbs a:not([href]){color:#5b6068;font-weight:600;text-decoration:none;cursor:default}
/* длинная цепочка (карточка товара): переносится, снизу больше воздуха */
.breadcrumbs--wrap{flex-wrap:wrap;margin-bottom:22px}

@media(max-width:560px){
  /* БЫЛО: цепочка превращалась в горизонтальный скроллер со СКРЫТОЙ полосой
     (scrollbar-width:none + ::-webkit-scrollbar{display:none}). На телефоне
     это худший из вариантов: последние звенья (а именно они говорят, где ты
     находишься) уезжали за правый край, и ничто не намекало, что там ещё
     что-то есть (на iOS/macOS системная полоса всплывает только во время
     прокрутки, то есть уже после того, как пользователь догадался).
     СТАЛО: перенос. Ничего не спрятано, подсказка не нужна вовсе, а цена вопроса
     всего одна-две лишние строки в 13px. Блок и называется --wrap: на мобильном он
     наконец делает то, что обещает именем. Крошки при этом остаются вторым
     способом навигации: первый это кнопка «назад» и рельс каталога. */
  .breadcrumbs,
  .breadcrumbs--wrap{flex-wrap:wrap;row-gap:2px}
  /* ПРЕДОК переносится целиком: рвать «Обувь для девочек» посередине хуже,
     чем перенести звено. */
  .breadcrumbs a{white-space:nowrap}

  /* ТЕКУЩУЮ СТРАНИЦУ НА ТЕЛЕФОНЕ НЕ ПЕЧАТАЕМ ВОВСЕ.
     Последнее звено это не ссылка, а имя того, что человек и так видит:
     прямо под крошками стоит тот же текст заголовком h1. На карточке товара
     это «731308с-4 Обувь для активного отдыха (красный) 33-38 (6 пар)»,
     то есть две строки дословного повтора, из-за которых цепочка занимала три строки
     вместо одной. Убираем звено и его шеврон; путь наверх, ради которого
     крошки и нужны, остаётся целиком.
     Шеврон гасим через :has(), ведь он стоит ПЕРЕД звеном, а обратного соседа
     в CSS иначе не достать. */
  .breadcrumbs b{display:none}
  .breadcrumbs i:has(+ b){display:none}
}


/* ============================================================
   ORDERBAR
   ============================================================ */
/* ============================================================
   .orderbar, залипающая нижняя панель заказа. ОБЩИЙ блок
   order-family: каталоги (обувь/одежда), карточка товара,
   заказ по артикулам, заказ из Excel.
   Поверхность даёт общий .tile.tile--shadow-h, внутренний
   контейнер даёт .wrap.wrap--flush; зазор страницы под панель
   даёт модификатор .wrap--orderbar (см. секцию КОНТЕЙНЕР).
   Кнопка внутри это общий .button.

   ⚠️ НА СТРАНИЦЕ КОРЗИНЫ ПАНЕЛИ НЕТ (было записано наоборот,
   исправлено в батче Б2). .orderbar отвечает за ОДИН смысл:
   «сколько набрано на этой странице → перейти в заказ». В корзине
   заказ уже открыт: там нужна не строка-итог, а сводка (позиций,
   наличие, предзаказ, доставка, к оплате) с двумя действиями,
   и она стоит в потоке страницы, а не поверх него. Поставить туда
   ещё и .orderbar значит показать две суммы и два CTA на одном
   экране. Сводка это вёрстка страницы корзины (единственный
   потребитель на 20 страниц): поверхность .tile, кнопки .button,
   деньги KMoney/KOrder.totalText, липкость через var(--header-bottom).
   Если когда-нибудь понадобится липкая кнопка оформления на
   мобильном, это .orderbar с другой подписью кнопки, и подпись
   параметризуется в KOrderbar (одно место), а не в странице.
   ============================================================ */
/* слой --z-sticky (50) идёт СТРОГО НИЖЕ шапки (--z-header 60): раньше обе были
   60, и панель, идущая в DOM позже, накрывала шапку и открытое мега-меню */
.orderbar{position:fixed;left:0;right:0;bottom:0;z-index:var(--z-sticky)}
.orderbar__card{display:flex;align-items:center;gap:22px;padding:14px 22px;flex-wrap:wrap}
.orderbar__summary{display:flex;align-items:center;gap:13px}
.orderbar__icon{width:46px;height:46px;border-radius:50%;background:var(--blue-t);color:var(--blue);display:flex;align-items:center;justify-content:center;flex-shrink:0}
.orderbar__icon i{font-size:22px}   /* было инлайном в разметке страниц */
.orderbar__summary b{font-size:15px;font-weight:800;display:block;line-height:1.25}
.orderbar__summary small{display:block;font-size:13px;color:var(--muted);font-weight:600;line-height:1.45}
.orderbar__total{margin-left:auto;text-align:left}
.orderbar__total small{font-size:12px;color:var(--muted);font-weight:600}
.orderbar__total b{display:block;font-size:22px;font-weight:800;color:var(--ink);line-height:1.1}

/* На мобильном вся панель дублирует корзину в шапке и забирает
   значительную часть экрана. На планшете/десктопе остаётся без изменений. */
@media(max-width:768px){
  .orderbar{display:none}
}

/* ============================================================
   ORDERS
   ============================================================ */
/* ============================================================
   .orders, ПЕРЕЧЕНЬ ЗАКАЗОВ (истории). Общий блок.

   ПОЧЕМУ ПЕРЕЕХАЛ В ЯДРО (правило проекта: второй потребитель означает переезд).
   Строка заказа была написана ДВАЖДЫ и под ОДНИМИ И ТЕМИ ЖЕ именами с
   несовместимым смыслом:
     home.css     .orders:  список последних заказов ВНУТРИ плитки,
                  .order:   СТРОКА заказа (сетка, .order__num/__date/
                  __status/__sum/__acts);
     cabinet.css  .orders:  ВСЯ СТРАНИЦА истории (flex-колонка с поиском,
                  чипами и пагинацией), .order: КАРТОЧКА ОДНОГО ЗАКАЗА
                  (детальная: таймлайн, сводка, состав).
   Два разных блока под одним именем это прямое нарушение канона: страница,
   подключившая оба файла, получила бы чужую сетку в своей плитке.
   РЕШЕНИЕ: «перечень заказов» это ОДИН компонент, и он здесь. Страничные
   обёртки переименованы (home → .home__orders, cabinet → .history), имя
   .order осталось детальной странице заказа, где оно единственное.

   ПОЧЕМУ .orders, А НЕ .orderlist. Рядом в ядре живёт .orderlist, перечень
   СТРОК ОДНОГО заказа (товар · количество · сумма). Здесь перечень САМИХ
   ЗАКАЗОВ: номер · состав · статус · сумма · действия. Разные предметы,
   разные имена; множественное число и говорит, чего в перечне много.

   ДВА ВИДА ОДНОГО КОМПОНЕНТА (модификатор обязателен, он несёт колонки):
     .orders--table    полный: пять колонок + шапка (страница истории),
                       ПРИПАРКОВАН в cabinet-orders.css, страницы ещё нет;
     .orders--compact  короткий: без состава и без шапки, живёт внутри
                       плитки в узкой колонке дашборда (главная).
   Колонки задаёт одна переменная --orders-cols (та же техника, что
   --catalog-cols и --orderlist-cols): ступень меняет ОДНУ строку.
   ⚠️ Ступени --table считают ширину ОКНА, потому что рядом стоит меню
   кабинета известной ширины. У --compact ширина зависит от колонки
   дашборда, а не от окна, поэтому оконные ступени ему НЕ адресованы: он
   держит свои четыре колонки до 560 и только там раскладывается 2×2.

   Разметку печатает ядро (window.KOrders), иначе две страницы напишут
   одну и ту же строку двумя способами, с чего вся эта история и началась.
   ============================================================ */
/* ❗minmax(0,1fr), а не 1fr: у 1fr минимальный размер = min-content, и в узкой
   колонке дашборда (2 колонки, ~630px на ширинах 1241…1330) строка не сжималась
   и вылезала за экран на 28px. */
.orders--compact{--orders-cols:126px minmax(0,1fr) auto auto;--tile-pad:4px 8px}
/* Пустое состояние: плитке нужны обычные поля (сжатые 4px/8px придуманы под
   строки заказов). Состояние центрируется в плитке ПО ОБЕИМ ОСЯМ, иначе на
   row-flex блок .state прижимался к левому краю широкой плитки. Ставится
   только когда заказов нет. */
.orders--empty{--tile-pad:24px 26px;display:grid;place-items:center}
/* иконка пустого состояния заказов красится в наш фирменный синий, под пару к синей
   карточке заказа рядом (общий .state оставляем нейтральным) */
.orders--empty .state__icon{background:var(--blue-t);color:var(--blue)}

.orders__row{
  display:grid;grid-template-columns:var(--orders-cols,minmax(0,1fr));
  gap:18px;align-items:center;
  padding:15px 22px;
}

.orders__row{padding:16px 22px;border-top:1px solid var(--line);transition:background .12s}
.orders__row:first-child{border-top:0}
.orders__row:hover{background:#FAFBFE}   /* та же подсветка строки, что у рельса каталога */
.orders__cell{min-width:0}

.orders__num b{display:block;font-size:15px;font-weight:800;color:var(--ink);white-space:nowrap}
.orders__num span{display:block;margin-top:3px;font-size:12.5px;font-weight:600;color:var(--muted)}

/* колонка статуса: пилюля исполнения (.status) + подпись «в сборке с 15 июля»
   + чип оплаты (.chip) это ВТОРАЯ ОСЬ, поэтому и разметка другая: два разных
   факта не должны выглядеть одинаковыми пилюлями (решение KStatus) */
.orders__since{display:block;margin-top:6px;font-size:12px;font-weight:600;color:var(--muted)}

/* tabular-nums: суммы стоят колонкой и не должны «плясать» при пересчёте */
.orders__sum b{
  display:block;font-size:15px;font-weight:800;color:var(--ink);
  white-space:nowrap;font-variant-numeric:tabular-nums;
}
/* предзаказ в $ идёт ОТДЕЛЬНОЙ строкой, с рублями он не складывается
   (бизнес-правило проекта) */
.orders__sum span{display:block;margin-top:3px;font-size:12px;font-weight:600;color:var(--muted)}

.orders__acts{display:flex;align-items:center;justify-content:flex-end;gap:8px;flex-wrap:wrap}

/* ---------- короткий вид (плитка дашборда) ---------- */
.orders--compact .orders__row{padding:12px 16px;gap:14px;border-radius:12px}
.orders--compact .orders__acts{gap:7px}
/* Пилюля статуса и чип оплаты идут СТОЛБИКОМ и всегда одинаково. Строкой они
   ложились по потоку: у короткой подписи («Ожидает») влезали рядом, у
   длинной («Подтверждён») переносились, и соседние строки перечня получали
   разную высоту без всякой причины. */
.orders--compact .orders__state{display:flex;flex-direction:column;align-items:flex-start;gap:6px}
/* в плитке номер и дата стоят теснее: строк три, а не шесть */
.orders--compact .orders__num span{margin-top:1px}

@media(max-width:768px){
  /* тач-размеры: кнопки строки не мельче 44px */
  .orders__acts .button{min-height:44px}
}
@media(max-width:560px){
  /* КОРОТКИЙ ВИД 2×2: номер и статус сверху, сумма и кнопки снизу */
  .orders--compact .orders__row{
    grid-template-columns:minmax(0,1fr) auto;
    grid-template-areas:"num state" "sum acts";
    gap:10px 12px;
  }
  .orders--compact .orders__num{grid-area:num}
  .orders--compact .orders__state{grid-area:state;align-items:flex-end;text-align:right}
  .orders--compact .orders__sum{grid-area:sum}
  .orders--compact .orders__acts{grid-area:acts;justify-content:flex-end}
}
@media(max-width:400px){
  .orders__acts{flex-wrap:wrap;justify-content:flex-end}
}


/* ============================================================
   HEADER
   ============================================================ */
/* =====================================================================
   .header, канонический компонент шапки Kapika (desktop-first)
   База (без @media) = 1240–1920, ширину капает контейнер .wrap (1680).
   Токены (--blue/--green/--line/--line2/--ink/--ink2/--muted/--bg2/
   --blue-t/--green-t/--blue-d/--green-d/--r/--r2) берутся из :root core.css.
   Внешние компоненты (тут НЕ переопределяются): .iconbtn, .iconbtn--menu,
   .cartbtn, .select, .button.
   ===================================================================== */

/* ---------- каркас ---------- */
.header{
  position:sticky;top:0;z-index:var(--z-header);
  background:#fff;border:1px solid var(--line);border-radius:var(--r);
  transition:box-shadow .18s;
}
.header--scrolled{box-shadow:0 14px 34px -22px rgba(20,38,90,.4)}
.header--mopen{
  border-bottom-left-radius:0;border-bottom-right-radius:0;
  /* Мега-меню живёт внутри stacking context шапки. Сама панель не может
     подняться выше соседнего scrim, если родитель остаётся на --z-header. */
  z-index:var(--z-above-scrim);
}
.header--searchopen{z-index:var(--z-above-scrim)}   /* поверх собственного затемнения */

/* верхняя строка */
.header__nav{display:flex;align-items:center;gap:18px;padding:13px 18px 13px 24px}
/* активный поиск заменяет верхнюю строку целиком */
.header--searchopen .header__nav{display:none}

/* ---------- логотип ---------- */
.header__logo{display:flex;align-items:center;gap:10px;flex-shrink:0;text-decoration:none;color:inherit}
.header__logo-img{display:block;width:auto;height:50px;flex-shrink:0}
.header__logo-sub{font-size:13px;color:var(--muted);font-weight:600}

/* ---------- пилюли каталога ---------- */
.header__cat{display:flex;align-items:center;gap:8px;flex-shrink:0}
/* Носителем на бою служит <a href> на страницу полки: полка это настоящая страница,
   и переход обязан уметь Ctrl+клик и «открыть в новой вкладке» (в прототипе
   плашка была <button>, потому что страницы полки там нет, и подчёркивания у
   кнопки не бывает по определению). Ссылке нужно ровно то же, что уже дописала
   себе .cartbtn при таком же переезде, то есть снять UA-подчёркивание. Цвет трогать
   не надо: color в самом компоненте сильнее UA-правил :link/:visited. */
.header__catpill{
  display:inline-flex;align-items:center;gap:9px;height:46px;padding:0 20px;
  border-radius:999px;border:1.5px solid var(--line2);background:#fff;
  font-family:inherit;font-weight:800;font-size:15px;color:var(--ink2);
  text-decoration:none;
  cursor:pointer;white-space:nowrap;transition:background .14s,border-color .14s,color .14s;
}
.header__catpill-chevron{font-size:16px;color:var(--muted);transition:transform .16s,color .14s}
/* ⚠️ СЧЁТЧИК РАЗДЕЛА НЕ ПОКАЗЫВАЕТСЯ, ЭТО РЕШЕНИЕ МАКЕТА (возврат, приёмка «вид»).
   Дословно из оригинала (kapika-home.html, дореформенная версия):
     /* счётчики количества товаров скрыты: при тысячах SKU точное число
        не информативно *​/
     .catpill .num,.catlist .cat .n,.opt .cnt{display:none}
   При миграции число ожило: ядро стало печатать его из общих данных, и на
   каталоге обуви пилюля «В наличии» показывала 68, а «Предзаказ» ничего.
   Асимметричная пара с цифрой это не тот вид, который согласовывали.
   МЕХАНИЗМ ОСТАВЛЕН ЖИВЫМ (window.KHeader.count / .counts пишет в этот узел):
   понадобится счётчик, снимается одна строка display:none, разметку и ядро
   трогать не нужно. Скин ниже тоже из оригинала, он ждёт своего часа. */
.header__catpill-num{display:none;font-weight:700;font-size:12.5px;color:var(--muted);background:var(--bg2);border-radius:999px;padding:1px 9px}
.header__catpill--on .header__catpill-num{background:rgba(255,255,255,.24);color:#fff}
.header__catpill:not(.header__catpill--on):hover{border-color:var(--blue);color:var(--blue)}
.header__catpill:not(.header__catpill--on):hover .header__catpill-chevron{color:var(--blue)}
.header__catpill--on{border-color:transparent;color:#fff}
.header__catpill--on .header__catpill-chevron{color:#fff;transform:rotate(180deg)}
/* ⚠️ ВОЗВРАТ (приёмка «вид»): У АКТИВНОЙ ПИЛЮЛИ БЕЛАЯ ПОДПИСЬ НА ОБОИХ ФОНАХ.
   Здесь стояли три правила .header__catpill--stock.header__catpill--on
   (подпись/шеврон --green-ink, подложка счётчика rgba(255,255,255,.75)):
   белое по лайму давало 1.70:1 и при миграции ink «пошёл за фоном». Но
   согласованный макет строит пару пилюль как ОДИН элемент управления:
   активная всегда белая, отличается только заливка (лайм / синий):
     .catpill.on{border-color:transparent;color:#fff}
     .catpill.st.on{background:var(--green)}   .catpill.pz.on{background:var(--blue)}
     .catpill.on .num{background:rgba(255,255,255,.24);color:#fff}
   Разный цвет подписи у соседних пилюль читался как разный тип кнопки.
   Замер после возврата: #fff на --green 1.70:1 (на --blue 6.38:1), вынесено в отчёт. */
.header__catpill--stock.header__catpill--on{background:var(--green)}  /* В наличии: доступно сейчас */
.header__catpill--pre.header__catpill--on{background:var(--blue)}     /* Предзаказ: новая коллекция */

.header__spacer{flex:1;min-width:8px}

/* ---------- кластер инструментов ---------- */
.header__tools{display:flex;align-items:center;gap:8px;flex-shrink:0}
.header__manager,.header__account,.header__menu{position:relative;flex-shrink:0}
.header__manager{margin-left:6px}                 /* мягкий зазор: поиск | аккаунт */
.header__menu{margin-left:18px}                   /* бургер отделён от остальных иконок */

/* контекстные размеры иконок-переключателей (визуал от .iconbtn) */
.header__search-close{width:42px;height:42px;flex-shrink:0}
.header__search-close i{font-size:19px}

/* ---------- дропдауны шапки (менеджер / кабинет / бургер) ---------- */
.header__pop{
  position:absolute;top:calc(100% + 8px);right:0;display:none;z-index:var(--z-dropdown);
  /* background-COLOR, а не шорткат: узел несёт примесь .scroller--fade, и
     шорткат обнулил бы её background-image (шторки подсказки) */
  background-color:#fff;border:1px solid var(--line);border-radius:var(--r2);
  box-shadow:0 20px 44px -18px rgba(20,38,90,.32);
}
.header__manager--open .header__pop,
.header__account--open .header__pop,
.header__menu--open .header__pop{display:block}
.header__pop--manager{width:286px;padding:16px}
.header__pop--menu{width:256px;padding:7px}
.header__pop--wide{width:268px}

/* открытый дропдаун подсвечивает СВОЙ триггер, хуки .header__*-toggle
   из партиала (у бургера подсветка живёт в компоненте .iconbtn--menu) */
.header__manager--open .header__manager-toggle,
.header__account--open .header__account-toggle{border-color:var(--blue);color:var(--blue);background:var(--blue-t)}

.header__pop-link{
  display:flex;align-items:center;gap:11px;padding:10px 12px;border-radius:9px;
  font-size:14px;color:var(--ink2);text-decoration:none;cursor:pointer;font-weight:600;
}
.header__pop-link:hover{background:var(--bg2);color:var(--blue)}
.header__pop-link i{font-size:18px;color:inherit}

/* ПУНКТ, ВЕДУЩИЙ НА ТЕКУЩУЮ СТРАНИЦУ, ЭТО СОСТОЯНИЕ, А НЕ ССЫЛКА (core.js,
   модуль 16: клик по нему гасится, чтобы перезагрузка не уносила набранное).
   Состояние читается атрибутом aria-current, второго имени (модификатора)
   тому же состоянию не заводим: в подвале модификатор появился раньше и
   остаётся ради своего скина, здесь нового не нужно. */
.header__pop-link[aria-current="page"],
.header__mega-link[aria-current="page"]{background:var(--bg2); color:var(--ink2); font-weight:700; cursor:default}
.header__pop-link[aria-current="page"]:hover,
.header__mega-link[aria-current="page"]:hover{background:var(--bg2); color:var(--ink2)}

.header__menu-label{font-size:12px;font-weight:800;color:var(--muted);text-transform:uppercase;letter-spacing:.06em;padding:8px 12px 4px}
.header__menu-sep{height:1px;background:var(--line);margin:6px 8px}

/* карточка менеджера внутри дропдауна */
.header__manager-head{display:flex;align-items:center;gap:11px;padding-bottom:13px;margin-bottom:6px;border-bottom:1px solid var(--line)}
/* ⚠️ ВОЗВРАТ (приёмка «вид»): У АВАТАРА МЕНЕДЖЕРА ИСХОДНЫЙ #6f9e2e.
   Стоял --ink-ok (#4a6b1e): #6f9e2e на --green-t даёт 2.78:1 при пороге 3:1
   для графики. Тон подобран макетом под лаймовую семью шапки, это ЛИЦО
   карточки менеджера, и потемнение меняло вид дропдауна на всех страницах.
   Оригинал: .mgrhead .mav{background:var(--green-t);color:#6f9e2e}.
   Замер после возврата: 2.78:1, вынесено в отчёт. */
.header__manager-avatar{width:42px;height:42px;border-radius:50%;background:var(--green-t);color:#6f9e2e;display:flex;align-items:center;justify-content:center;font-size:20px;flex-shrink:0}
.header__manager-head b{font-size:15px;font-weight:800;display:block}
.header__manager-head small{font-size:12px;color:var(--muted)}
.header__manager-row{display:flex;align-items:center;gap:11px;padding:9px 6px;border-radius:9px;font-size:14px;color:var(--ink2);text-decoration:none;cursor:pointer;font-weight:600}
.header__manager-row:hover{background:var(--bg2);color:var(--blue)}
.header__manager-row i{font-size:18px;color:var(--blue)}
.header__manager-hours{display:flex;align-items:center;gap:11px;padding:9px 6px;font-size:13px;color:var(--muted);font-weight:600}
.header__manager-hours i{font-size:18px}

/* ---------- сворачиваемый поиск ---------- */
.header__search{display:none;align-items:center;gap:12px;padding:13px 18px 13px 24px}
.header--searchopen .header__search{display:flex}
.header__search .select__btn{height:48px}         /* выровнять высоту селекта под поиск */
.header__searchbox{flex:1;min-width:0;display:flex;align-items:center;gap:10px;height:48px;padding:0 6px 0 20px;border:1.5px solid var(--line2);border-radius:999px}
.header__searchbox:focus-within{border-color:var(--blue)}
/* min-width:0 обязателен: без него flex-инпут не сжимается ниже своей
   дефолтной ширины (~161px) и на 390/430 вылезает из контейнера */
.header__searchbox-input{flex:1;min-width:0;border:0;outline:0;font-family:inherit;font-size:14px;color:var(--ink2);background:none}
/* тот же токен, что у .field__input::placeholder, литерала «серого в поле»
   в проекте больше нет ни одного */
.header__searchbox-input::placeholder{color:var(--muted)}
/* ⚠️ ВОЗВРАТ (приёмка «вид»): ЛУПА В ЛАЙМОВОМ КРУЖКЕ БЕЛАЯ.
   Стоял --green-ink (у графики порог 3:1, белая лупа даёт 1.70:1). Кружок
   поиска входит в ту же лаймовую семью, что корзина: перекрашивать его
   в одиночку значило рассыпать семью. Оригинал: .search .go{background:
   var(--green);color:#fff}. Замер после возврата: 1.70:1, вынесено в отчёт. */
.header__searchbox-go{width:38px;height:38px;border-radius:50%;background:var(--green);color:#fff;border:0;display:flex;align-items:center;justify-content:center;cursor:pointer;flex-shrink:0}
.header__searchbox-go:hover{background:var(--green-d)}
.header__searchbox-go i{font-size:18px}

/* затемнение под активный поиск (сиблинг шапки) */
.header__scrim{display:none;position:fixed;inset:0;background:rgba(18,28,56,.42);z-index:var(--z-scrim)}
.header--searchopen ~ .header__scrim{display:block;animation:header-scrim-fade .18s ease}
@keyframes header-scrim-fade{from{opacity:0}to{opacity:1}}

/* ---------- мега-меню каталога ---------- */
/* Панель абсолютная внутри .header, а шапка липкая (top:0) → прокрутка
   страницы её не двигает. Поэтому панель ОГРАНИЧЕНА вьюпортом и скроллится
   внутри себя: высота = экран − нижняя кромка шапки − воздух снизу.
   --header-bottom пишет core.js (модуль 1) при открытии/ресайзе/скролле;
   фолбэк 96px на случай, если JS не отработал. На десктопе контент помещается, и правило
   ничего не меняет визуально, оно включается только когда список длиннее экрана. */
.header__mega{
  position:absolute;top:100%;left:-1px;right:-1px;margin-top:-1px;display:none;z-index:var(--z-dropdown);
  /* background-COLOR, а не шорткат: узел несёт примесь .scroller--fade, и
     шорткат обнулил бы её background-image (шторки подсказки) */
  background-color:#fff;border:1px solid var(--line);border-top:0;border-radius:0 0 var(--r) var(--r);
  box-shadow:0 28px 50px -26px rgba(20,38,90,.32);padding:22px 28px 26px 24px;
  max-height:calc(100vh - var(--header-bottom,96px) - 16px);
  max-height:calc(100dvh - var(--header-bottom,96px) - 16px);
  overflow-y:auto;overscroll-behavior:contain;-webkit-overflow-scrolling:touch;
}
.header--mopen .header__mega{display:block}

.header__mega-genders{display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin-bottom:18px}
.header__mega-genders-label{font-size:12px;color:var(--muted);margin-right:2px}
.header__gendertab{background:#fff;border:1.5px solid var(--line2);border-radius:999px;padding:7px 18px;font-size:13px;font-weight:700;cursor:pointer;color:#5b6068;font-family:inherit}
/* :not(--on) здесь как у .header__catpill: иначе ховер (0,2,0) перебивал
   белый текст активного таба (0,1,0) и подпись пропадала на синем фоне */
.header__gendertab:not(.header__gendertab--on):hover{border-color:var(--blue);color:var(--blue)}
.header__gendertab--on{background:var(--blue);border-color:transparent;color:#fff}
.header__gendertab--on:hover{background:var(--blue-d)}

/* Панель полки это три колонки по типам товара. Правого промо-блока с иконкой
   больше нет (решение заказчика): без него пунктам достаётся вся ширина, и
   длинные группы больше не переносятся лишней строкой. */
.header__mega-cols{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:10px 40px;align-content:start}
/* ЗАГОЛОВОК КОЛОНКИ МЕГА-МЕНЮ ЧИТАЕТСЯ РАНЬШЕ СВОИХ ПУНКТОВ.
   Было: 12px серым #525a66 над пунктами 15px цветом --ink2, то есть заголовок
   оказывался тише и мельче того, что он подписывает, и «Одежду» с
   «Аксессуарами» люди просто не замечали (замечание заказчика 21.09.2026).
   Стало: тот же капслок, но самым тёмным цветом текста, на пункт крупнее и с
   более заметной чертой под ним. Капслок и разрядку не трогаем, это рисунок
   утверждённого макета. */
.header__mega-title{display:flex;align-items:center;justify-content:space-between;gap:8px;font-size:14px;font-weight:800;color:var(--ink);margin:0 0 10px;padding:0 0 9px;text-transform:uppercase;letter-spacing:.05em;border-bottom:2px solid var(--line2);text-decoration:none;transition:color .15s ease}
.header__mega-title:hover{color:var(--brand)}
.header__mega-title .ti{font-size:16px;line-height:1;flex:0 0 auto}
.header__mega-links{display:grid;grid-template-columns:1fr;column-gap:18px}
.header__mega-link{display:block;font-size:15px;color:var(--ink2);text-decoration:none;padding:10px;margin:1px -10px;border-radius:10px;cursor:pointer;font-weight:500}
.header__mega-link:hover{background:var(--bg2)}
/* группа колонки мега-меню (печатает core.js: .header__mega-group > __mega-title +
   __mega-links). Базовое правило обязано быть: это грид-ячейка, а у грид-элемента
   min-width по умолчанию auto, и длинный пункт («Резиновые сапоги») распирал трек. */
.header__mega-group{min-width:0}
/* Длинная группа («Обувь») раскладывает свои пункты в два подстолбца внутри
   своей колонки, иначе она тянулась бы одним столбцом заметно ниже соседей. */
.header__mega-group--split .header__mega-links{grid-template-columns:1fr 1fr;column-gap:24px}

/* Ссылка «вся витрина» внутри панели: нужна только там, где плашка перестала
   быть ссылкой, то есть на телефоне. На мыши её печатать незачем: она
   повторяла бы саму плашку. */
.header__mega-all{
  display:none;align-items:center;gap:10px;
  margin:0 0 14px;padding:12px 14px;border-radius:12px;
  background:var(--blue-t);color:var(--blue);
  font-size:15px;font-weight:700;text-decoration:none;
}
.header__mega-all span{display:flex;flex-direction:column;min-width:0}
.header__mega-all small{font-size:12px;font-weight:600;color:var(--muted)}
.header__mega-all-go{margin-left:auto;font-size:16px}

/* =====================================================================
   .header--float, плавающая прозрачная шапка над hero (главная)
   Прозрачная → белеет при: скролле (.header--solid), раскрытии каталога
   (.header--mopen), активном поиске (.header--searchopen).
   Кнопки без обводки на прозрачной, с обводкой (дефолт компонентов) на белой.
   Объявлено ПОСЛЕ базы: при равной специфичности переопределяет .header--scrolled.
   ===================================================================== */
.header--float{
  position:fixed;top:16px;left:32px;right:32px;max-width:1616px;margin-inline:auto;z-index:var(--z-float);
  background:rgba(255,255,255,.6);
  -webkit-backdrop-filter:blur(16px) saturate(1.35);backdrop-filter:blur(16px) saturate(1.35);
  border:1px solid rgba(255,255,255,.5);
  box-shadow:0 12px 44px -18px rgba(20,38,90,.34);
  transition:background .3s,box-shadow .3s,border-color .3s;
}
.header--float.header--solid,
.header--float.header--mopen,
.header--float.header--searchopen{
  background:#fff;border-color:var(--line);box-shadow:0 6px 22px -12px rgba(20,38,90,.22);
  /* В непрозрачном состоянии размытие уже ничего не рисует, но продолжает
     создавать containing block для position:fixed. На главной из-за этого
     мобильная .header__mega считала top/left/right/bottom от самой шапки,
     сжималась в узкую полосу и уходила под hero. Без фильтра её геометрия,
     как и на внутренних страницах, снова считается от окна браузера. */
  -webkit-backdrop-filter:none;backdrop-filter:none;
}
/* ⚠️ ВОЗВРАТ (приёмка «вид»): НА СТЕКЛЕ КНОПКИ БЕЗ ОБВОДКИ, ЭТО РЕШЕНИЕ МАКЕТА.
   Здесь стояло border-color:var(--ink2): при миграции тонкую границу белой
   пилюли на стекле сочли дефектом (2.14:1 при пороге 3:1 по WCAG 1.4.11) и
   поставили тёмную линию цвета подписи. Первый экран главной от этого менялся
   заметнее всего: вместо чистых белых «таблеток» на кадре героя появлялась
   обведённая карандашом строка.
   Оригинал (kapika-home.html, дореформенная версия):
     .page--home .hdr:not(.solid):not(.mopen):not(.searchopen) .catpill:not(.on),
     .page--home .hdr:not(.solid):not(.mopen):not(.searchopen) .iconbtn:not(.menu)
       {border-color:transparent;box-shadow:none}
   Замер после возврата: белая заливка против стекла 2.14:1 (1440–1024),
   1.82–1.86 (900–320), 1.53 в альбомной 844×390, вынесено в отчёт.
   ОБА СЕЛЕКТОРА, А НЕ ТОЛЬКО ПИЛЮЛЯ: у круглых .iconbtn заливка та же белая и
   то же стекло вокруг, и в оригинале снималась обводка и у них.
   Правило действует только пока шапка прозрачна: побелела (скролл/меню/поиск), и
   вернулся мягкий --line2 самого компонента. */
.header--float:not(.header--solid):not(.header--mopen):not(.header--searchopen) .header__catpill:not(.header__catpill--on),
.header--float:not(.header--solid):not(.header--mopen):not(.header--searchopen) .iconbtn:not(.iconbtn--menu){
  border-color:transparent;box-shadow:none;
}
.header--float .header__catpill:not(.header__catpill--on){background:#fff;color:var(--ink)}
.header--float .header__logo-sub{color:var(--ink);font-weight:700}  /* «B2B кабинет» читабельно на прозрачной */
/* КОЛЬЦО ФОКУСА НА ПРОЗРАЧНОЙ ШАПКЕ ВИДНО ТОЛЬКО С КЛАВИАТУРЫ И ОСТАЁТСЯ.
   Кольцо ядра (2px --blue, offset 2px) лежит НЕ на кнопке, а в зазоре
   вокруг неё, то есть на стекле rgba(255,255,255,.6) над первым экраном.
   Замер по фактическим слоям: стекло над тёмно-синей подложкой героя
   (.hero, #062A6B) даёт светлоту 0.393, и пара с --blue даёт 2.69:1; на живом
   кадре hero-girls самый тёмный столбец шапки даёт 3.21:1, самый светлый 4.96.
   Порог для кольца фокуса равен 3:1, значит на служебных слайдах (их два из
   трёх, там кадра нет и видна сама подложка) кольцо не проходит.
   --blue-d держит 3.67:1 на самом тёмном замеренном стекле (0.376 по
   замерам границ пилюль) и 7.50:1 на самом светлом, а с белой заливкой
   самой кнопки даёт 9.04:1.
   ⚠️ ЭТО ПРАВИЛО ПРИ ВОЗВРАТЕ ВИДА НЕ СНИМАЕТСЯ, в отличие от соседних:
   кольцо фокуса рисуется ТОЛЬКО при работе с клавиатуры (:focus-visible),
   мышью и на скриншоте макета его не видно вовсе, согласованный вид оно
   не меняет.
   Правило гаснет вместе с прозрачностью (скролл/меню/поиск): на белой
   шапке кольцо снова --blue и даёт свои 6.38:1. */
.header--float:not(.header--solid):not(.header--mopen):not(.header--searchopen) :focus-visible{
  outline-color:var(--blue-d);
}

/* ⚠️ УДАЛЕНО (приёмка «вид»): ПРИГЛУШЁННАЯ ПИЛЮЛЯ РАЗДЕЛА «СКОРО».
   Здесь жили три правила .header__catpill.soon (белая заливка, серая
   подпись, cursor:help), скин выдуманного состояния: раздел «Предзаказ»
   при миграции перестал быть кнопкой и получил пометку «скоро», потому что
   в демо-данных под ним не было объявлено ни одной категории. В
   согласованном макете «Предзаказ» это ПОЛНОЦЕННАЯ ВТОРАЯ ПИЛЮЛЯ, она
   раскрывает такую же панель, как «В наличии» (см. data.js → SECTIONS.cats
   и core.js, модуль 1). Продуктовых решений вроде «скоро» вёрстка не
   принимает.
   Примесь .soon как таковая ОСТАЁТСЯ (секция SOON): ею помечены пункты без
   своей страницы в подвале, в рельсе каталога и в колонке мега-меню,
   это решения прежних раундов, к пилюлям разделов отношения не имеющие.
   Вместе с этим блоком снят и @media(max-width:359px), который прятал
   пометку на самых узких телефонах: прятать больше нечего. */

/* =====================================================================
   Адаптив: канонич. лестница 1240 / 1024 / 900 / 768 / 560 / 400
   (размеры/сворачивание .iconbtn и .cartbtn живут в их собственных компонентах)
   ===================================================================== */
@media(max-width:1240px){
  .header__nav{gap:12px;padding:12px 16px}
  .header__logo-img{height:46px}
  .header__logo-sub{display:none}
  .header__catpill{height:42px;padding:0 15px;font-size:14px}
  .header__search{padding:12px 16px 14px}
}
@media(max-width:1024px){
  .header__mega-cols{grid-template-columns:repeat(2,minmax(0,1fr))}
  /* небольшой запас строке: зазор 12→10, поля 16→14. Ступень широкая
     (901–1024), содержимому здесь и так просторно, но запас нужен под
     счётчик раздела, который растёт вместе с каталогом. */
  .header__nav{gap:10px;padding:12px 14px}
  .header__manager{margin-left:2px}
  .header__menu{margin-left:10px}
}
@media(max-width:900px){
  .header--float{left:14px;right:14px}
  /* ⚠️ ПРАВКА ПРИЁМКИ Б2: ПЕРЕПОЛНЕНИЕ ШАПКИ НА 769–783.
     Диапазон 769–1024 был единственным местом лестницы, где строка шапки не
     пересобиралась ВООБЩЕ: пилюли каталога ещё стоят в одну строку с
     логотипом и кластером (перенос был только с ≤768), а корзина уже
     свёрнута в кружок (с ≤1024). На нижней кромке места переставало
     хватать: при окне 769 документ 783, и от содержимого бейджа это НЕ
     зависело: не помещался сам каркас (замер на 769: логотип 70 + пилюли
     346 + распорка 8 + кластер 276 + три зазора по 12 + поля 2×16 = 768
     при 705 доступных внутри шапки).
     Подрезать поля и кегли можно было ровно на эти 13–14px, и упереться
     в них снова, как только счётчик раздела станет трёхзначным. Поэтому
     берём МЕХАНИЗМ, который у шапки уже есть: строка пилюль переезжает
     вниз на ступень раньше, не с 768, а с 900. На 769 в верхней строке
     остаются логотип и кластер (334px из 705), запас двукратный, и он
     не зависит ни от длины подписей, ни от величины счётчиков.
     ⚠️ МЕРИТЬ НАДО ВНУТРИ ШАПКИ (правый край .header__tools против правого
     края .header минус поле), а не по прокрутке документа: шапка стоит
     внутри .wrap с боковыми полями, и кластер успевает вылезти ЗА КРАЙ
     БЕЛОЙ КАРТОЧКИ, пока страница ещё не прокручивается (бургер торчал из
     скруглённого угла на 11px при «нулевом» переполнении документа). */
  .header__nav{flex-wrap:wrap;row-gap:10px}
  .header__cat{order:6;width:100%;gap:8px}
  .header__catpill{flex:1;justify-content:center}
}
@media(max-width:768px){
  /* Навигация уходит в бургер. Перенос строки пилюль объявлен ступенью выше
     (≤900, см. правку переполнения), здесь остаётся только то, что этой
     ступени СВОЁ: плотнее поля и зазоры, мельче логотип, поля пилюль. */
  .header__nav{gap:10px;padding:11px 14px}
  .header__logo-img{height:44px}
  .header__catpill{height:42px;padding:0 10px}
  .header__tools{gap:6px}
  .header__manager{margin-left:2px}
  .header__menu{margin-left:8px}
  .header__search{padding:10px 14px 14px;gap:8px}
  /* На телефоне панель продолжает шапку, а не живёт отдельным bottom-sheet.
     Прежняя привязка к bottom:0 и max-height:82dvh делала верхнюю кромку зависимой от
     высоты экрана: на одних телефонах оставался зазор, на других лист залезал
     под шапку. --header-bottom содержит её живую нижнюю границу; минус пиксель склеивает
     границы без шва. Боковой clamp точно повторяет padding .wrap, внутри которого
     стоит сама шапка, и совпадает с ней на всех мобильных ширинах, а не только на 390px. */
  .header__mega{
    position:fixed;top:calc(var(--header-bottom,96px) - 1px);left:clamp(16px,4vw,40px);right:clamp(16px,4vw,40px);bottom:0;margin:0;
    border:1px solid var(--line);border-top:0;border-radius:0;
    box-shadow:0 24px 44px -20px rgba(20,38,90,.4);
    padding:14px 16px calc(18px + env(safe-area-inset-bottom));
    max-height:none;
    z-index:var(--z-above-scrim);
  }
  /* На главной плавающая шапка на этой ступени отступает от окна ровно на
     14px, тогда как обычная шапка живёт в .wrap с полями 16px. Панель должна
     повторять ширину своего варианта шапки, а не общие поля контейнера. */
  .header--float .header__mega{left:14px;right:14px}
  /* Затемнение под листом: без него список висит над страницей ни на чём. */
  .header--mopen ~ .header__scrim{display:block;animation:header-scrim-fade .18s ease}
  .header__mega-cols{grid-template-columns:1fr}
  .header__mega-all{display:flex}
  /* тач-таргет вкладок пола: 7px+13px+7px = 33px по высоте, ниже порога 36 */
  .header__gendertab{min-height:38px;padding:8px 18px}
}
@media(max-width:560px){
  .header__nav{padding:10px 12px;gap:8px}
  .header__tools{gap:5px}
  /* У ДРОПДАУНОВ ШАПКИ НА ТЕЛЕФОНЕ ЯКОРЬ ПО ПРАВОМУ КРАЮ ШАПКИ, А НЕ ИКОНКИ.
     Панель шириной 288 висела на right:0 своей обёртки-иконки; иконка менеджера
     стоит в середине кластера, поэтому на 390 панель уезжала за левый край
     экрана (left:-76, контакты нечитаемы), у кабинета наполовину.
     Гасим позиционирование обёрток: containing block становится сама .header
     (sticky/fixed = позиционированный предок), панель равняется по правому краю
     шапки и на 320–1919 гарантированно внутри вьюпорта. top:100% отсчитывается
     теперь от НИЗА шапки (включая перенесённую строку пилюль), как у мега-меню.
     Позиция только классами, без inline-стилей. */
  .header__manager,
  .header__account,
  .header__menu{position:static}
  .header__pop{
    right:12px;left:auto;
    /* высокий список (бургер) не упирается в низ экрана, а скроллится внутри
       себя; --header-bottom (нижняя кромка шапки) пишет core.js, фолбэк 96px */
    max-height:calc(100vh - var(--header-bottom,96px) - 24px);
    max-height:calc(100dvh - var(--header-bottom,96px) - 24px);
    overflow-y:auto;overscroll-behavior:contain;-webkit-overflow-scrolling:touch;
  }
  /* дропдауны не выходят за экран */
  .header__pop--manager,
  .header__pop--menu,
  .header__pop--wide{width:min(288px,calc(100vw - 24px))}
  /* селект категорий съедал 2/3 строки поиска (поле ужималось до ~43px):
     на телефоне ищем по всему каталогу, поле получает всю ширину */
  .header__search .select{display:none}
  .header__mega{padding:16px 12px 18px}
}
@media(max-width:400px){
  .header__logo-img{height:42px}
  .header__catpill{font-size:13px;gap:6px;padding:0 8px}
  .header__gendertab{padding:6px 14px}
}


/* ============================================================
   FOOTER
   ============================================================ */
/* ============================================================
   FOOTER (.footer): канонический общий подвал
   Токены из :root ядра: --line, --line2, --r, --r2,
   --muted, --ink, --ink2, --blue.
   Внешний контейнер (.wrap) даёт боковые отступы, а сам подвал
   это карточка со своим паддингом и не задаёт ширину.
   ============================================================ */
.footer{
  background:#fff;
  border:1px solid var(--line);
  border-radius:var(--r);
  padding:30px 32px 22px;
  margin-top:48px;
}

.footer__top{
  display:grid;
  grid-template-columns:1.6fr 1fr 1.2fr 1fr;
  gap:30px;
}

/* колонки грида подвала: те же min-width:0, что и у групп мега-меню, иначе
   длинный e-mail/пункт меню распирает трек и ломает сетку 4 колонок */
.footer__brand,
.footer__col{min-width:0}

/* бренд-колонка */
/* В подвале фирменный знак стоит над подписью кабинета. */
.footer__logo{
  display:inline-flex;
  flex-direction:column;
  gap:3px;
  margin-bottom:12px;
  text-decoration:none;
  color:inherit;
}
.footer__logo-img{display:block;width:auto;height:66px}
.footer__logo-sub{
  font-size:13px;
  font-weight:600;
  line-height:1.2;
  color:var(--muted);
}
.footer__about{
  font-size:13px;
  color:var(--muted);
  line-height:1.6;
  margin:0 0 14px;
  max-width:280px;
  font-weight:500;
}
.footer__contacts{
  display:flex;
  flex-direction:column;
  gap:9px;
}
.footer__contact{
  display:flex;
  align-items:center;
  gap:9px;
  font-size:14px;
  color:var(--ink2);
  text-decoration:none;
  font-weight:700;
}
.footer__contact i{color:var(--blue);font-size:18px}

/* колонки ссылок */
.footer__heading{
  font-size:13px;
  font-weight:800;
  color:var(--ink);
  margin:0 0 12px;
}
.footer__link{
  display:block;
  font-size:14px;
  color:#5b6068;
  text-decoration:none;
  padding:5px 0;
  font-weight:500;
}
.footer__link:hover{color:var(--blue)}
/* ЛОГОТИПЫ ЭТО ТА ЖЕ САМОССЫЛКА, ЧТО И ПУНКТ СПИСКА. На главной оба логотипа
   (шапки и подвала) ведут на неё же: клик гасит core.js, здесь остаётся только
   курсор, чтобы узел не обещал перехода. Своего модификатора им не заводим:
   состояние уже названо атрибутом aria-current (см. .header__*-link). */
.footer__logo[aria-current="page"],
.header__logo[aria-current="page"]{cursor:default}

/* нижняя строка */
.footer__bottom{
  display:flex;
  align-items:center;
  justify-content:space-between;
  gap:16px;
  flex-wrap:wrap;
  margin-top:24px;
  padding-top:18px;
  border-top:1px solid var(--line);
  font-size:12.5px;
  color:var(--muted);
  font-weight:600;
}
.footer__legal{display:flex;gap:18px;flex-wrap:wrap}
.footer__legal-link{color:var(--muted);text-decoration:none}
.footer__legal-link:hover{color:var(--blue)}

/* ---------- 1240: четыре колонки → две ---------- */
@media(max-width:1240px){
  .footer__top{grid-template-columns:1fr 1fr;gap:24px}
}

/* ---------- 768: компактнее паддинг карточки ---------- */
@media(max-width:768px){
  .footer{padding:26px 22px 18px;margin-top:40px}
}

/* ---------- 560: полный стек ---------- */
@media(max-width:560px){
  .footer{padding:22px 18px 16px}
  .footer__top{grid-template-columns:1fr;gap:22px}
  .footer__bottom{flex-direction:column;align-items:flex-start;gap:10px}
}


/* ============================================================
   CABNAV
   ============================================================ */
/* ============================================================
   .cabnav, БОКОВОЕ МЕНЮ КАБИНЕТА (история заказов · заказ ·
   данные компании). Третий общий блок навигации после .header
   и .footer, и живёт по тем же правилам:

     • РАЗМЕТКА: partials/cabnav.html, приезжает сборкой
       (build.py, маркер CABNAV). Руками между маркерами не правят.
     • СПИСОК ПУНКТОВ: данные (KData.cabinet()), печатает ядро
       (core.js, модуль 17). В партиале только оболочка: пустой
       [data-cabnav], как [data-footer-cats] в подвале.
     • АКТИВНЫЙ ПУНКТ объявляет САМА страница: <body data-cab="orders">.
       Ключ берётся из того же перечня. Зашитого .cabnav__item--on в
       разметке нет (тот же запрет, что у пилюли раздела в шапке).
     • Раздел без своей страницы получает примесь .soon, вести его на
       чужую страницу запрещено.

   ЛАЙАУТ СТРАНИЦЫ (две колонки «меню + содержимое») ЖИВЁТ НЕ ЗДЕСЬ:
   это вёрстка кабинета, она в assets/css/cabinet.css. Ядро отвечает
   за сам блок меню, страница отвечает за то, где он стоит.

   Ступень 1024 взята по канон-лестнице: боковая колонка на планшете и
   телефоне сворачивается в кнопку-«текущий раздел» с выпадающим
   списком (иначе меню съедает первый экран).
   ============================================================ */
.cabnav{
  background:#fff;border:1px solid var(--line);border-radius:var(--r);
  /* липнет ПОД шапкой: её нижнюю кромку пишет core.js (--header-bottom),
     фолбэк 96px тот же, что у дропдаунов шапки */
  padding:8px;position:sticky;top:calc(var(--header-bottom,96px) + 12px);
}
/* кнопка-сворачиватель: до 1024 её нет */
.cabnav__toggle{display:none}

.cabnav__head{display:flex;align-items:center;gap:12px;padding:12px 12px 14px}
.cabnav__avatar{
  width:42px;height:42px;border-radius:12px;flex-shrink:0;
  background:var(--blue-t);color:var(--blue);
  display:flex;align-items:center;justify-content:center;
  font-size:16px;font-weight:800;
}
.cabnav__org{min-width:0}
.cabnav__org b{display:block;font-size:14px;font-weight:800;color:var(--ink);line-height:1.2}
.cabnav__org span{font-size:12px;font-weight:600;color:var(--muted)}

.cabnav__list{
  display:flex;flex-direction:column;gap:2px;
  padding:6px 4px 4px;border-top:1px solid var(--line);
}
.cabnav__item{
  display:flex;align-items:center;gap:12px;
  padding:11px 12px;border-radius:11px;
  font-size:14px;font-weight:700;color:var(--ink2);text-decoration:none;
}
.cabnav__item i{font-size:19px;color:var(--muted);flex-shrink:0}
.cabnav__item:hover{background:var(--bg2);color:var(--blue)}
.cabnav__item:hover i{color:var(--blue)}
/* Активный пункт отличается РОВНО тем же, чем в согласованном дизайне:
   заливкой и цветом (оригинал: .cn-item.on{background:var(--blue-t);color:var(--blue)}).
   ⚠️ БЫЛО СЛОМАНО: батч Б3 «ради WCAG 1.4.1» дорисовывал метку-полоску
   3×18px (.cabnav__item--on::before) и поднимал начертание до 800, а обоих
   признаков в утверждённом виде не было, и обоснование «метка вторит другим
   меню проекта» было ложным: такая метка в проекте была ровно одна, эта.
   Не возвращать: нецветовой признак «вы здесь» остаётся ПРОГРАММНЫМ, ведь
   aria-current="page" печатает сам модуль (core.js, KCabnav.itemHTML), а на
   ≤1024 текущий раздел прямо назван подписью кнопки .cabnav__current. */
.cabnav__item--on{background:var(--blue-t);color:var(--blue)}
.cabnav__item--on i{color:var(--blue)}
/* «Выйти» тише остальных, а на наведении красный (действие не рядовое) */
.cabnav__item--out{color:var(--muted)}
.cabnav__item--out:hover{color:var(--ink-alert);background:var(--pink)}
.cabnav__item--out:hover i{color:var(--ink-alert)}
/* счётчик у пункта (сколько заказов в истории) */
.cabnav__badge{
  margin-left:auto;border-radius:999px;padding:1px 8px;
  font-size:12px;font-weight:800;background:var(--blue);color:#fff;
}
.cabnav__sep{height:1px;background:var(--line);margin:6px 10px}
/* пункт-обещание: цвет и курсор даёт примесь .soon (секция SOON) */
.cabnav__item.soon:hover{background:none}

@media(max-width:1024px){
  /* На узких ступенях меню это НЕ белая карточка: видимый контрол здесь сама
     кнопка раздела, обёртка только якорит выпадающий список.
     ⚠️ БЫЛО СЛОМАНО: обёртка оставалась белой плиткой с рамкой и радиусом,
     а кнопка внутри оставалась прозрачной и безрамочной; под белой липкой шапкой это
     читалось как её продолжение, и у раскрытия не было своего вида.
     z-index 50 (--z-sticky) взят как в оригинале (там стояло ровно 50): раскрытый
     список обязан уходить ПОД шапку (--z-header 60), а не поверх неё, потому
     что на скролле он не закрывается. */
  .cabnav{
    position:relative;top:auto;
    background:none;border:0;border-radius:0;padding:0;
    z-index:var(--z-sticky);
  }
  .cabnav__head{display:none}
  /* контур + фон страницы вместо белой плашки: --control (не --line2), потому
     что рамка тут единственная форма контрола, а --line2 на --page даёт 1.16:1 */
  .cabnav__toggle{
    display:flex;align-items:center;gap:11px;width:100%;min-height:44px;
    background:none;border:1px solid var(--control);border-radius:var(--r2);
    padding:11px 14px;
    font-family:inherit;font-size:14.5px;font-weight:800;color:var(--ink);
    cursor:pointer;-webkit-tap-highlight-color:transparent;
    transition:background .14s,border-color .14s,color .14s;
  }
  .cabnav__toggle-icon{font-size:20px;color:var(--blue);flex-shrink:0}
  .cabnav__current{flex:1;text-align:left}
  .cabnav__chevron{font-size:19px;color:var(--muted);flex-shrink:0;transition:transform .18s,color .14s}
  /* наведение/нажатие: тот же приём, что у .filters__open и .select__btn */
  .cabnav__toggle:hover{border-color:var(--blue);color:var(--blue)}
  .cabnav__toggle:hover .cabnav__chevron{color:var(--blue)}
  .cabnav__toggle:active{background:var(--bg2)}
  /* раскрыто, и вид свой: тот же тинт, что у активного пункта на десктопе */
  .cabnav--open .cabnav__toggle{background:var(--blue-t);border-color:var(--blue);color:var(--blue)}
  .cabnav--open .cabnav__chevron{color:var(--blue);transform:rotate(180deg)}
  /* выпадающий список берёт слой дропдауна той же шкалы, что у шапки.
     top/left/right без прежних 4px/-1px: те числа компенсировали padding и
     рамку белой обёртки, которой больше нет, и список встаёт ровно по кнопке
     с зазором 6px (шаг дропдаунов проекта, ср. .select__menu) */
  .cabnav__list{
    display:none;position:absolute;top:calc(100% + 6px);left:0;right:0;
    z-index:var(--z-dropdown);
    background:#fff;border:1px solid var(--line);border-radius:14px;
    box-shadow:0 22px 46px -16px rgba(20,38,90,.34);padding:8px;
  }
  .cabnav--open .cabnav__list{display:flex}
  .cabnav__item{-webkit-tap-highlight-color:transparent;min-height:44px}
  /* в свёрнутом меню текущий раздел уже назван кнопкой, подсветка лишняя */
  .cabnav__item--on{background:none;color:var(--ink2)}
  .cabnav__item--on i{color:var(--muted)}
}


/* ============================================================
   TIP
   ============================================================ */
/* ============================================================
   .tip, общая подсказка по атрибуту [data-tip]
   ОДИН механизм на весь проект: разметка страницы несёт только
   data-tip="текст" на любом элементе (иконка ⓘ в шапке таблицы,
   подпись, бейдж), всё остальное делает core.js (модуль 8): роль,
   фокус, показ по ховеру/тапу/фокусу, закрытие через
   registerDismiss (клик снаружи · Esc · скролл), клампинг по
   вьюпорту. Сам узел .tip один на документ, инжектится в <body>.
   Координаты приходят переменными --tip-x/--tip-y (как --orderbar-h),
   видимость задаётся модификатором --open, не inline-стилем.
   ============================================================ */
/* носитель подсказки: курсор-вопрос + видимый фокус (это фокус-цель) */
[data-tip]{cursor:help}
[data-tip]:focus-visible{outline:2px solid var(--blue);outline-offset:2px;border-radius:4px}

.tip{
  position:fixed;top:var(--tip-y,0);left:var(--tip-x,0);z-index:var(--z-dropdown);
  display:none;max-width:min(260px,calc(100vw - 24px));
  padding:9px 12px;border-radius:10px;
  background:var(--ink);color:#fff;
  font-size:12.5px;font-weight:600;line-height:1.45;
  box-shadow:0 16px 34px -16px rgba(20,38,90,.55);
  pointer-events:none;   /* подсказка не перехватывает клик/ховер */
}
.tip--open{display:block}


/* ============================================================
   SIDEPAGE
   ============================================================ */
/* ============================================================
   .sidepage, СТРАНИЦА РАЗДЕЛА: боковое меню + содержимое.
   Второй общий лайаут проекта после .wrap.

   ПОЧЕМУ В ЯДРЕ. Раскладка «меню слева, содержимое справа» жила
   в assets/css/cabinet.css под именем .cabinet (три страницы
   кабинета) и ОТДЕЛЬНОЙ рукописной копией .cab в каждой из пяти
   инфо-страниц, с той же колонкой, тем же gap и тем же сводом
   на 1024. Второй потребитель = переезд компонента (закон проекта),
   а тут их девять.

   Ширина колонки меню взята не «на глаз»: 256px это контейнер под
   пункт «История заказов» с бейджем без переноса (тот же расчёт,
   что был записан в cabinet.css).
   На ≤1024 меню сворачивается в кнопку (скин в секции CABNAV),
   поэтому колонок больше нет: содержимое занимает всю ширину.

   ⚠️ ДОЛГ: .cabinet/.cabinet__main в assets/css/cabinet.css
   это ТА ЖЕ раскладка, оставшаяся у принятых страниц кабинета.
   Схлопнуть её в .sidepage должен владелец cabinet.css отдельным
   проходом; ядро трогать принятые страницы не станет.
   ============================================================ */
.sidepage{
  display:grid;
  /* minmax(0,1fr), а не 1fr: у дорожки 1fr минимальный размер = min-content,
     и широкая таблица растягивала бы сетку, а не ужималась (дефект, который
     в проекте ловили трижды) */
  grid-template-columns:256px minmax(0,1fr);
  gap:20px;
  align-items:start;
  /* ⚠️ БЫЛО СЛОМАНО: при переезде лайаута из страниц в ядро потеряли верхний
     отступ (в оригинале .cab нёс margin-top:22px, кабинет 20px). Шапка
     лежит ВНУТРИ .wrap, её padding-block:22px стоит НАД шапкой, поэтому без
     этой строки между шапкой и меню оставался ровно 0px, то есть «прилипло».
     Значение одно на все ступени (20px, шаг соседей: .catalog-layout, .product). */
  margin-top:20px;
}
.sidepage__main{min-width:0}

@media(max-width:1024px){
  .sidepage{grid-template-columns:minmax(0,1fr);gap:14px}
  /* Крошки на узких ступенях скрыты, как в оригинале (.cab-crumbs{display:none}
     на всех инфо-страницах): наверху и так тесно, а путь дублирует кнопку
     раздела. У инфо-страниц крошки без своего класса, поэтому адресуем их
     прямым потомком колонки содержимого; каталог и карточка вне .sidepage. */
  .sidepage__main>.breadcrumbs{display:none}
}


/* ============================================================
   PROSE
   ============================================================ */
/* ============================================================
   .prose, ТИПОГРАФИКА ДЛИННОГО ТЕКСТА (редакционная колонка).
   Абзацы, подзаголовки, списки и ссылки внутри одного контейнера.

   ЗАЧЕМ БЛОК, А НЕ КЛАСС НА КАЖДОМ АБЗАЦЕ. Инфо-страницы и легал
   держат сотни абзацев подряд; класс на каждом <p> это не БЭМ,
   а шум, и первый же вставленный абзац останется без стиля.
   Здесь ровно тот случай, для которого существует стилизация по
   потомкам, и в проекте она уже принята (.cabnav__item i,
   .tile>*:last-child). Наружу блок отдаёт СВОИ элементы только
   там, где узлу нужен отдельный смысл (__note).

   ДЛИНА СТРОКИ ЭТО ЧАСТЬ КОМПОНЕНТА (68ch ≈ 90 знаков): длиннее
   строка теряет обратный ход глаза. Ширину задаёт переменная
   --prose-measure, и страница может сузить колонку, не переписывая
   правила (page-CSS не адресует элементы ядра, он выставляет
   объявленный вход, см. закон --soon-mark).
   ============================================================ */
.prose{
  --prose-measure:68ch;
  font-size:15px;
  line-height:1.7;
  font-weight:500;
  color:var(--ink2);
}
.prose p,
.prose ul,
.prose ol{max-width:var(--prose-measure);margin:0 0 12px;text-wrap:pretty}
.prose>*:last-child{margin-bottom:0}

.prose h2{
  margin:26px 0 10px;
  font-size:clamp(18px,1.7vw,20px);font-weight:800;letter-spacing:-.01em;
  color:var(--ink);text-wrap:balance;
}
.prose h3{margin:22px 0 8px;font-size:15px;font-weight:800;color:var(--ink);text-wrap:balance}
.prose h2:first-child,
.prose h3:first-child{margin-top:0}

/* СИЛЬНЫЙ ТЕКСТ ВНУТРИ ПРОЗЫ ТЕМНЕЕ, а не только жирнее: на --ink2
   разница 500→800 при 15px читается слабо. */
.prose b,.prose strong{font-weight:800;color:var(--ink)}
.prose a{color:var(--blue);font-weight:700;text-decoration:none}
.prose a:hover{text-decoration:underline}

/* СПИСКИ. Маркер это своя точка (::marker неровно ложится в Safari при
   line-height 1.6+), поэтому list-style снят, а отступ держит padding-left. */
.prose ul,.prose ol{padding-left:0;list-style:none;display:flex;flex-direction:column;gap:9px}
.prose li{position:relative;padding-left:20px;font-size:14.5px;line-height:1.6}
/* МАРКЕР ДЕКОРАТИВЕН, И ЭТО ЗАКОННЫЙ СЛУЧАЙ --faint (закон токенов):
   он ничего не сообщает (что это список, видно по отступу и по строкам),
   рядом всегда есть текст, и состояния он не несёт. Исходная вёрстка красила
   его --line2 (1.15:1 на фоне страницы), тогда как --faint (1.65:1) заметнее, а
   роль та же. НУМЕРАЦИЯ ниже, наоборот, ТЕКСТ (номер пункта договора
   называют вслух), поэтому она --muted и проходит порог 4.5:1. */
.prose ul>li::before{
  content:"";position:absolute;left:3px;top:.62em;
  width:6px;height:6px;border-radius:50%;background:var(--faint);
}
/* нумерованный список: счётчик вместо точки, тем же кеглем */
.prose ol{counter-reset:prose}
.prose ol>li{counter-increment:prose;padding-left:26px}
.prose ol>li::before{
  content:counter(prose) ".";position:absolute;left:0;top:0;
  font-weight:800;color:var(--muted);
}

/* СНОСКА/ПРИМЕЧАНИЕ мельче основного текста (бывш. .ab-mat-cap) */
.prose__note{
  max-width:82ch;margin:16px 0 0;
  font-size:13.5px;line-height:1.6;font-weight:600;color:var(--muted);
}
.prose__note b{color:var(--ink2)}

@media(max-width:560px){
  .prose{font-size:14.5px}
}


/* ============================================================
   DOC
   ============================================================ */
/* ============================================================
   .doc, ЮРИДИЧЕСКИЙ ДОКУМЕНТ: нумерованные разделы (оферта,
   политика конфиденциальности, условия возврата).

   Строение раздела: номер в тинтовой плитке + заголовок в одну
   строку, дальше текст с отступом ПОД заголовком (41px = плитка
   27px + gap 13px + 1px), так номер остаётся единственным, что
   торчит в левое поле, и глаз идёт по нумерации.
   Типографику текста внутри даёт .prose (ставится на тот же
   контейнер), здесь остаётся только структура документа.

   ⚠️ ТЕКСТ ДОКУМЕНТА ЮРИДИЧЕСКИЙ. Ни компонент, ни страница
   не сокращают, не переписывают и не «улучшают» его формулировки,
   и реквизиты в тексте НЕ подставляются из KData (см. data.js,
   SHOP.legal): в документе они часть документа.
   ============================================================ */
.doc__section{padding:19px 0;border-top:1px solid var(--line)}
.doc__section:first-child{border-top:0;padding-top:0}
.doc__section>*:last-child{margin-bottom:0}

/* ⚠️ СЕЛЕКТОР С ПРЕДКОМ СТОИТ НАМЕРЕННО, А НЕ ПО НЕБРЕЖНОСТИ.
   .doc и .prose по канону стоят на ОДНОМ контейнере (типографику текста даёт
   проза), а заголовок раздела это h2 или h3 внутри неё. Поэтому голый
   .doc__title (0,1,0) проигрывал прозе .prose h2 / .prose h3 (0,1,1):
   селектор с тегом специфичнее, и порядок в файле не спасал: заголовки
   разделов оферты, политики и возврата молча набирались 20px вместо 16px и
   получали чужой верхний отступ 26px. Компенсирующий слой той же формы жил
   в info.css (страничный CSS чинил компонент ядра), теперь лечение стоит
   там, где объявлен сам компонент, а слой из info.css удалён.
   Ровно та же ловушка, что ядро уже ловило у .page-title--lg, только там
   специфичность добирал медиазапрос, а здесь тег в селекторе прозы. */
.doc .doc__title{
  display:flex;align-items:center;gap:13px;
  margin:0 0 9px;
  font-size:16px;font-weight:800;letter-spacing:normal;color:var(--ink);
}
.doc__num{
  flex-shrink:0;
  display:inline-flex;align-items:center;justify-content:center;
  width:27px;height:27px;border-radius:9px;
  background:var(--blue-t);color:var(--blue);
  font-size:13px;font-weight:800;
}
/* тело раздела встаёт под ЗАГОЛОВОК, а не под номер */
.doc__section p,
.doc__section ul,
.doc__section ol{margin-left:41px}
/* у документа строка длиннее, чем у редакционной прозы: сплошной
   юридический текст с отступом 41px иначе рвётся слишком узко */
.doc{--prose-measure:82ch}

@media(max-width:560px){
  /* на телефоне отступ в 41px съедает пятую часть ширины: номер
     остаётся, вынос текста снимается */
  .doc__section p,
  .doc__section ul,
  .doc__section ol{margin-left:0}
}


/* ============================================================
   DEFLIST
   ============================================================ */
/* ============================================================
   .deflist, СТРОКИ «НАЗВАНИЕ → ЗНАЧЕНИЕ» НА ВОЛОСЯНЫХ ЛИНИЯХ.
   Реквизиты организации, факты производства, параметры раздела.

   РАЗМЕТКА это настоящий список определений (валидный HTML5:
   группу <dt>+<dd> разрешено обернуть в <div>):
     <dl class="deflist">
       <div class="deflist__row">
         <dt class="deflist__key">ИНН</dt>
         <dd class="deflist__value">7721260590</dd>
       </div>
     </dl>
   <div>/<span> тоже работают, правила не завязаны на теги, но
   у реквизитов семантика списка определений настоящая.

   ДВА АКЦЕНТА, ОДИН СКЕЛЕТ:
     базовый:   ключ приглушён, значение сильное (реквизиты:
                спрашивают «ИНН?» и читают цифры);
     --fact:    наоборот, сильная левая колонка (редакционные
                факты «5 стран · Италия, Молдова…»: слева факт,
                справа пояснение).
   Ширина левой колонки задаётся входом --deflist-key.
   ============================================================ */
.deflist{--deflist-key:200px;margin:0}
.deflist__row{
  display:grid;
  grid-template-columns:var(--deflist-key) minmax(0,1fr);
  gap:4px 20px;align-items:baseline;
  padding:13px 0;border-top:1px solid var(--line2);
}
.deflist__row:first-child{border-top:0;padding-top:0}
.deflist__key{margin:0;font-size:14px;font-weight:600;color:var(--muted)}
.deflist__value{margin:0;font-size:15px;font-weight:700;color:var(--ink);line-height:1.45}
.deflist__value a{color:var(--blue);text-decoration:none}
.deflist__value a:hover{text-decoration:underline}

/* --fact: факт слева, пояснение справа */
.deflist--fact{--deflist-key:138px}
.deflist--fact .deflist__key{font-size:15px;font-weight:800;color:var(--ink)}
.deflist--fact .deflist__value{font-size:13.5px;font-weight:500;color:var(--ink2);line-height:1.55}
/* РАМКА ТАБЛИЦЫ ФАКТОВ СДЕЛАНА КАК В ПРОТОТИПЕ (.ab-kv, kapika-terms.html:696).
   Замер обеих страниц: у прототипа линия и НАД таблицей, и под каждой строкой,
   первая строка держит свои 13px сверху. Общий .deflist первую линию и отступ
   снимает намеренно (там таблица идёт сразу за подписью), но у редакционных
   фактов от этого пропадала верхняя грань: таблица теряла рамку и «съезжала»
   к заголовку. Возвращаем только этой ветке: обычный .deflist не трогаем. */
.deflist--fact{border-top:1px solid var(--line2)}
.deflist--fact .deflist__row:first-child{border-top:0;padding-top:13px}
/* широкая подпись («Юридический адрес», «Отгрузочный склад») */
.deflist--wide{--deflist-key:230px}
/* На /terms/ подписи короче («Оплата», «Отгрузка», «Доставка»), и колонка в
   230px разгоняла значения вправо, а в прототипе там 190px (.ab-kv--wide). */
.deflist--fact.deflist--wide{--deflist-key:190px}

@media(max-width:560px){
  /* две колонки в 320px не живут: подпись встаёт над значением */
  .deflist__row{grid-template-columns:minmax(0,1fr);gap:2px;padding:11px 0}
}


/* ============================================================
   STATE
   ============================================================ */
/* ============================================================
   .state, КРУПНОЕ СОСТОЯНИЕ ЭКРАНА: «заявка отправлена»,
   «проверьте почту», «здесь пока пусто». Иконка в круге,
   заголовок, объяснение, необязательная плашка-примечание и
   действие.

   ЗАЧЕМ В ЯДРЕ. Ровно эта фигура уже нарисована в проекте
   ТРИЖДЫ и тремя разными именами: .cabinet__empty (пустая
   история заказов), .orderempty (пустая корзина), .orderok
   (заказ принят, там же вариант «в строку»). Четвёртой копией
   она приходила бы с экранами входа (регистрация и
   восстановление пароля). Канон один: компонент здесь.

   ⚠️ ДОЛГ: три перечисленные копии живут в cabinet.css / cart.css
   / order.css, файлах принятых страниц. Схлопывать их в .state
   должен владелец этих файлов отдельным проходом.

   ТОН ИКОНКИ. По умолчанию нейтральный (--bg2 / --muted:
   4.88:1 при пороге 3:1 для графики). Модификатор --ok означает успех: --green-t
   и --ink-ok (5.38:1). Литералов вроде #5a7d22 здесь нет:
   «текст и графика на тинте» это семья токенов, см. закон токенов.
   ============================================================ */
.state{text-align:center;padding:8px 0 4px}
.state__icon{
  display:flex;align-items:center;justify-content:center;
  width:72px;height:72px;margin:0 auto 22px;
  border-radius:50%;background:var(--bg2);color:var(--muted);
}
.state__icon i{font-size:34px}
.state--ok .state__icon{background:var(--green-t);color:var(--ink-ok)}

.state__title{margin:0 0 10px;font-size:24px;font-weight:800;color:var(--ink);text-wrap:balance}
.state__text{
  max-width:44ch;margin:0 auto 14px;
  font-size:15px;line-height:1.55;font-weight:500;color:var(--ink2);text-wrap:pretty;
}
.state__text b{font-weight:800;color:var(--ink)}
/* примечание: тише текста, на подложке, это «мелкий шрифт» состояния */
.state__note{
  margin:18px 0 24px;padding:13px 16px;border-radius:var(--r2);
  background:var(--bg2);
  font-size:13px;line-height:1.5;font-weight:600;color:var(--muted);text-align:left;
}
.state__note b{color:var(--ink2)}
.state__actions{display:flex;flex-direction:column;align-items:stretch;gap:10px}

@media(max-width:560px){
  .state__icon{width:64px;height:64px;margin-bottom:18px}
  .state__icon i{font-size:30px}
  .state__title{font-size:21px}
}


/* ============================================================
   SCROLLER
   ============================================================ */
/* .scroller, ПРИМЕСЬ «ЭТОТ УЗЕЛ ПРОКРУЧИВАЕТСЯ». Одна полоса на проект:
   до неё каждый скроллер красился сам (10px в catalog.css, 6px в лайтбоксе,
   ничего в мега-панели и в подсказках артикулов).
   Класс НЕ включает прокрутку: overflow объявляет блок-хозяин там, где он
   ему нужен (у мега-панели всегда, у .header__pop только на ≤560, у рельса
   фильтров только в drawer). Примесь отвечает ровно за ВИД полосы.
   Значения берутся из переменных --sb-* в :root; хозяин на другом фоне перебивает
   их У СЕБЯ (сегодня таких хозяев нет).
   ⚠️ ХОЗЯИН НЕ ИМЕЕТ ПРАВА ПИСАТЬ ШОРТКАТ background ПОСЛЕ ЭТОЙ СЕКЦИИ:
   он обнулит background-image модификатора --fade. Пишем background-color. */
.scroller{
  scrollbar-width:thin;
  scrollbar-color:var(--sb-thumb) transparent;
}
.scroller::-webkit-scrollbar{width:var(--sb-w);height:var(--sb-w)}
.scroller::-webkit-scrollbar-track{background:transparent}
.scroller::-webkit-scrollbar-thumb{background:var(--sb-thumb);border-radius:8px;border:var(--sb-pad) solid var(--sb-face)}
.scroller::-webkit-scrollbar-thumb:hover{background:var(--sb-thumb-hover)}

/* .scroller--fade, ПОДСКАЗКА «ЕСТЬ ЕЩЁ КОНТЕНТ», БЕЗ СКРИПТА.
   Две «шторки» цвета панели прокручиваются ВМЕСТЕ с содержимым
   (background-attachment:local), две тени прибиты к рамке (scroll) и
   выглядывают из-под шторки, как только та уехала. Пока прокручивать
   нечего, шторки стоят ровно на тенях и подсказки не видно вовсе,
   на десктопе согласованный вид не меняется НИ НА ПИКСЕЛЬ.
   Почему не JS: то же самое пришлось бы пересчитывать на каждом рендере
   мега-панели (смена раздела/пола) и на каждом пересчёте фасетов.
   Почему это важнее полосы: на iOS полоса это overlay, её при неподвижном
   списке НЕТ ни при каком CSS, а шторка видна всегда.
   Размеры менять ПАРОЙ: шторка непрозрачна первые 14.4px (60% от 24px) и
   обязана перекрывать тень (10px), иначе подсказка видна в покое. */
.scroller--fade{
  background-image:
    linear-gradient(var(--sb-face) 60%, rgba(255,255,255,0)),
    linear-gradient(rgba(255,255,255,0), var(--sb-face) 60%),
    radial-gradient(farthest-side at 50% 0,    rgba(20,38,90,.16), rgba(20,38,90,0)),
    radial-gradient(farthest-side at 50% 100%, rgba(20,38,90,.16), rgba(20,38,90,0));
  background-position:0 0, 0 100%, 0 0, 0 100%;
  background-size:100% 24px, 100% 24px, 100% 10px, 100% 10px;
  background-repeat:no-repeat;
  background-attachment:local, local, scroll, scroll;
}


/* ============================================================
   SOON
   ============================================================ */
/* .soon, ПУНКТ НАВИГАЦИИ, СТРАНИЦЫ ДЛЯ КОТОРОГО ЕЩЁ НЕТ.
   Решение оркестратора: вести такой пункт на чужую страницу запрещено,
   честнее показать его несобранным. Класс-ПРИМЕСЬ: садится на узел любого
   блока (.footer__link, .filters__cat, пункт меню) и не требует своих
   размеров/отступов, их даёт блок-хозяин.

   ⚠️ СЕКЦИЯ ДЕРЖИТСЯ ПОСЛЕДНЕЙ В ФАЙЛЕ И НЕ ПОДНИМАЕТСЯ ВЫШЕ.
   Примесь обязана перебивать цвет и курсор блока-хозяина, а её селектор
   такой же одноклассовый (0,1,0), поэтому решает порядок в файле. Стоя в секции
   BUTTONS, .soon проигрывала .footer__link, и «скоро» выглядел как живая
   ссылка (замер: цвет пункта был #5b6068, как у обычной ссылки).
   Стили страничных файлов (catalog.css и т.п.) грузятся ПОСЛЕ ядра и
   перебивают примесь, там приглушение остаётся на своём модификаторе
   (.filters__cat--soon).

   РАЗМЕТКА (обязательно <span>, а не <a>: «<a> без href не бывает», это канон):
     <span class="footer__link soon" data-tip="…почему пусто…">
       Предзаказ <span class="soon__mark">скоро</span></span>
   data-tip объясняет причину (механизм подсказок в модуле 8 core.js;
   он же делает узел фокус-целью, так что пункт доступен и с клавиатуры).

   .soon__mark: margin-left:auto прижимает пометку к правому краю во
   ФЛЕКС-хозяине (рельс категорий), а в строчном/блочном (подвал) auto=0, и
   там зазор даёт padding-left. Одно правило на оба контекста. */
.soon{color:var(--muted); cursor:default; text-decoration:none}
.soon:hover{color:var(--muted); background:none; text-decoration:none}
.soon[data-tip]{cursor:help}          /* есть пояснение, значит курсор подсказки */
/* ЦВЕТ ПОМЕТКИ КРАСИТ ЯДРО, а хозяину оставлен ОДИН ОБЪЯВЛЕННЫЙ ВХОД,
   переменная --soon-mark. Нужен он там, где примесь садится на ТЁМНУЮ
   поверхность (сценарий главной, карточка по фото): без входа страница
   писала правило по .soon__mark, то есть красила элемент чужого компонента
   (home.css). Правило проекта: page-CSS не адресует элементы ядра, он
   выставляет объявленную переменную на СВОЁМ блоке. */
.soon__mark{
  margin-left:auto; padding-left:6px;
  font-size:12px; font-weight:700; color:var(--soon-mark,var(--muted));
  text-transform:uppercase; letter-spacing:.04em;
}

/* ==========================================================================
   ПОДСКАЗКИ ПОИСКА это элементы живого блока .header__searchbox.
   Отдельного блока намеренно НЕ заводим: при четвёртой приёмке .searchfield
   удалили с прямым указанием: автоподсказка должна прирасти к живому поиску,
   а не стать вторым блоком с тем же смыслом.
   Вид панели повторяет .header__pop (тот же фон/граница/тень/радиус), чтобы
   выпадашка поиска не выглядела чужой рядом с меню шапки.
   ========================================================================== */
.header__searchbox{position:relative}

.header__searchbox-suggest{
  position:absolute;top:calc(100% + 10px);left:0;right:0;z-index:var(--z-dropdown);
  padding:6px;background-color:#fff;border:1px solid var(--line);border-radius:var(--r2);
  box-shadow:0 20px 44px -18px rgba(20,38,90,.32);
  max-height:min(62vh,520px);overflow:auto;overscroll-behavior:contain;
}

/* Строка результата. Ссылка занимает её целиком: попасть можно в любое место строки. */
.header__searchbox-option{
  display:flex;align-items:center;gap:12px;padding:8px 10px;
  border-radius:11px;text-decoration:none;color:inherit;
}
 /* --on ведёт клавиатура (стрелки): наведение мышью и выбор с клавиш дают одно состояние. */
.header__searchbox-option:hover,.header__searchbox-option--on{background:var(--bg2)}

.header__searchbox-thumb{
  width:44px;height:44px;flex-shrink:0;object-fit:cover;
  border-radius:9px;background:var(--bg2);
}
.header__searchbox-text{min-width:0;flex:1;display:flex;flex-direction:column;gap:2px}
/* Название и мета идут в одну строку с многоточием: высота строки не скачет. */
.header__searchbox-name{
  font-size:13.5px;font-weight:700;color:var(--ink2);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
}
.header__searchbox-meta{
  font-size:12px;font-weight:600;color:var(--muted);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
}
/* Совпавший кусок запроса. Только насыщенность, ведь подсветку фоном на строке,
   которая и так выделяется наведением, читать тяжело. */
.header__searchbox-mark{color:var(--blue);font-weight:800}

.header__searchbox-price{font-size:13.5px;font-weight:800;color:var(--ink);white-space:nowrap}

/* Метка области. Цвета те же, что у плашек каталога в шапке:
   «В наличии» зелёная, «Предзаказ» синяя. */
.header__searchbox-tag{
  flex-shrink:0;padding:3px 9px;border-radius:999px;
  font-size:12px;font-weight:700;line-height:1.5;
}
.header__searchbox-tag--stock{background:var(--green-t);color:var(--green-deep)}
.header__searchbox-tag--pre{background:var(--blue-t);color:var(--blue)}

/* Хвост списка: переход на страницу результатов. */
.header__searchbox-all{
  display:flex;align-items:center;justify-content:center;gap:8px;
  margin-top:4px;padding:12px;border-radius:11px;
  background:var(--blue-t);color:var(--blue);
  font-size:13px;font-weight:700;text-decoration:none;
}
.header__searchbox-all:hover{background:var(--blue);color:#fff}
.header__searchbox-all i{font-size:16px}

.header__searchbox-empty{
  padding:20px 14px;text-align:center;
  font-size:13px;font-weight:600;color:var(--muted);
}
.header__searchbox-empty b{display:block;margin-bottom:4px;color:var(--ink2)}


@media(max-width:560px){
  .header__searchbox-thumb{width:38px;height:38px}
  .header__searchbox-price{display:none}   /* на телефоне важнее название */
}

/* Постраничность результатов поиска. В каталоге страниц нет, там «Показать
   ещё» (.pager в catalog.css), поэтому это отдельный маленький блок. */
.search-pager{display:flex;align-items:center;justify-content:center;gap:16px;flex-wrap:wrap;padding:28px 4px 4px}
.search-pager__pages{display:flex;align-items:center;gap:12px;font-size:14px;font-weight:700}
/* Подсветка совпадения в выдаче поиска. Тег <b> ставит сам движок
   (TITLE_FORMATED), а имя карточки и без него полужирное, поэтому красим цветом,
   тем же, что у подсказок в шапке (.header__searchbox-mark). */
.product-card__name b{color:var(--blue)}


/* ============================================================
   ORDERLIST
   ============================================================ */
/* ============================================================
   .orderlist, ПЕРЕЧЕНЬ СТРОК ЗАКАЗА (ТОЛЬКО ЧТЕНИЕ). Общий блок.

   ПЕРЕЕХАЛ ИЗ order.css В ЯДРО (батч Б3, фундамент кабинета). Потребителей
   стало ТРИ, и они уже в двух разных css-файлах:
     1) результат разбора файла Excel (со столбцом «Статус»)  в order.css
     2) состав принятого заказа («Заказ принят»)              в order.css
     3) СОСТАВ ЗАКАЗА ИЗ ИСТОРИИ (kapika-cabinet-order)       в cabinet.css
   Третий потребитель не мог бы взять компонент из чужого страничного файла
   и завёл бы четвёртую таблицу «Товар · Количество · Сумма» (в исходной
   разметке кабинета это .od-item). Место общего блока в ядре.
   ⚠️ order.css ОБЯЗАН УДАЛИТЬ свою копию .orderlist* (база + ступени
   768/560/400), значения перенесены сюда один-в-один, вид не изменится.
   См. handoff в assets/_core-notes.md.

   ПОЧЕМУ ЭТО НЕ .catalog. Блок .catalog это перечень товаров, КОТОРЫЙ НАБИРАЮТ:
   у него восьмиколоночный рельс с наличием, ценой единицы, степпером и
   раскрытием. Здесь ничего этого нет и быть не может: строки уже набраны
   (или уже отклонены разбором), править их тут нельзя. Общий рельс на такую
   таблицу натягивать нельзя: половина колонок осталась бы пустой.
   Черновик по артикулам, наоборот, набирается, и берёт именно .catalog.

   Колонки задаёт одна переменная (единая точка правды, как --catalog-cols).
   ============================================================ */
.orderlist{--orderlist-cols:minmax(0,15fr) minmax(0,38fr) minmax(0,16fr) minmax(0,17fr)}
.orderlist--status{--orderlist-cols:minmax(0,14fr) minmax(0,31fr) minmax(0,14fr) minmax(0,15fr) minmax(0,19fr)}
/* Строка С МИНИАТЮРОЙ («фото + название» · количество · сумма) это состав
   ПРИНЯТОГО заказа на чеке «Заказ принят». Артикул отдельной колонкой здесь
   не нужен: он в подписи под названием, как в строке каталога. */
.orderlist--photo{--orderlist-cols:minmax(0,1fr) auto auto}
.orderlist__head,
.orderlist__row{display:grid;grid-template-columns:var(--orderlist-cols);gap:0 14px;align-items:center}
.orderlist__head{padding:12px 20px;background:#FAFCFE;border-bottom:1px solid var(--line)}
.orderlist__row{padding:13px 20px;border-bottom:1px solid var(--line)}
.orderlist__row:last-child{border-bottom:0}
/* ОДНА БАЗОВАЯ ЛИНИЯ. Ячейка «Товар» двухстрочная (название + .orderlist__sub),
   а «Количество» и «Сумма» однострочные: при align-items:center число вставало
   МЕЖДУ названием и подписью и ни к чему не было привязано («по правому краю
   так себе», приёмка). Базовая линия блочной ячейки это её первая строка,
   поэтому число садится ровно на строку названия. Исключения: строка с фото
   (высоту задаёт миниатюра, а не текст, там остаётся центр, как в оригинале)
   и колонка статуса (пилюля со своей высотой). */
.orderlist__row{align-items:baseline}
.orderlist--photo .orderlist__row{align-items:center}
.orderlist--status .orderlist__cell--status{align-self:center}
/* строка, которая в заказ не пойдёт (нет в наличии, артикул не найден) */
.orderlist__row--off{color:var(--muted)}
.orderlist__cell{min-width:0}
/* ячейка ШАПКИ это тот же элемент в другом состоянии, а не второе имя */
.orderlist__cell--head{
  font-size:12px;font-weight:800;color:var(--muted);
  text-transform:uppercase;letter-spacing:.03em;
}
.orderlist__cell--art{font-weight:800;color:var(--ink);word-break:break-word}
.orderlist__row--off .orderlist__cell--art{color:var(--muted)}
.orderlist__cell--name{font-weight:600;color:var(--ink2)}
.orderlist__row--off .orderlist__cell--name{color:var(--muted)}
.orderlist__sub{margin-top:3px;font-size:12px;font-weight:600;color:var(--muted)}
/* tabular-nums: количество и суммы стоят колонкой и не должны «плясать»
   (то же правило, что у .orders__sum b и .dashcard__amount-value). Nunito
   рисует пропорциональные цифры, и при выключке вправо разряды соседних
   строк не совпадали. */
.orderlist__cell--qty,
.orderlist__cell--sum{text-align:right;white-space:nowrap;font-variant-numeric:tabular-nums}
.orderlist__cell--sum{font-weight:800;color:var(--ink)}
.orderlist__row--off .orderlist__cell--sum{font-weight:600;color:var(--muted)}
.orderlist__cell--status{display:flex;justify-content:flex-end}
/* ⚠️ СВОИХ .orderlist__product/__thumb ЗДЕСЬ БОЛЬШЕ НЕТ. Они были пятой
   декларацией ячейки «Товар» и стояли без единого потребителя: строку с
   миниатюрой печатает ОБЩАЯ ячейка ядра (KTable.prod → .catalog__product /
   .catalog__thumb), тем же вызовом, что корзина и кабинет. Размер
   миниатюры под чек (52×66 вместо каталожных 64×82) правит страница своей
   примесью .orderok__thumb, см. order.css. */
/* количество с уточнением («2 коробки» / «12 пар») печатает KTable.qtyCell */
.orderlist__cell--qty b{display:block;font-size:13.5px;font-weight:700;color:var(--ink2)}
.orderlist__cell--qty span{display:block;margin-top:2px;font-size:12px;font-weight:600;color:var(--muted)}
/* сумма держит собственный минимум (оригинал .od-item .sum{min-width:78px}),
   иначе в смешанном заказе колонка прыгает между «$396» и «44 900 ₽» */
.orderlist--photo .orderlist__cell--sum{font-size:14.5px;min-width:78px}
/* подвал перечня: слева пояснение, справа итог (состав заказа) */
.orderlist__foot{
  display:flex;align-items:center;justify-content:space-between;gap:12px;flex-wrap:wrap;
  padding:15px 20px;background:#FAFCFE;border-top:1px solid var(--line);
}
.orderlist__foot-label{font-size:13.5px;font-weight:700;color:var(--ink2)}
.orderlist__foot-sum{font-size:19px;font-weight:800;color:var(--ink);font-variant-numeric:tabular-nums}

@media(max-width:768px){
  /* Перечень строк заказа: таблица → карточки. Внутреннего горизонтального
     скроллера не заводим: на телефоне он прячет половину строки. */
  .orderlist__head{display:none}
  .orderlist__row{
    grid-template-columns:minmax(0,1fr);gap:6px;
    margin-bottom:10px;padding:14px 16px;
    border:1px solid var(--line2);border-bottom:1px solid var(--line2);border-radius:14px;
  }
  .orderlist__row:last-child{margin-bottom:0}
  .orderlist__cell{display:flex;align-items:baseline;justify-content:space-between;gap:12px;text-align:left}
  .orderlist__cell[data-label]::before{
    content:attr(data-label);flex:0 0 auto;
    font-size:12px;font-weight:700;color:var(--muted);
    text-transform:uppercase;letter-spacing:.04em;
  }
  .orderlist__cell--qty,
  .orderlist__cell--sum{text-align:right}
  /* Количество из двух строк («2 коробки» + «12 пар») и на карточке остаётся
     столбиком у правого края: ячейка здесь flex «подпись/значение», и без
     переноса <b> и <span> встали бы третьим и четвёртым элементом одной
     строки, то есть «КОЛИЧЕСТВО · 2 коробки · 12 пар» враздрай. */
  .orderlist__cell--qty{flex-wrap:wrap;row-gap:0}
  .orderlist__cell--qty span{flex:1 0 100%;text-align:right}
  .orderlist__cell--status{justify-content:space-between}
  .orderlist__sub{margin-top:0}
  /* ячейка товара с фото остаётся строкой «фото + текст», а не «подпись/значение» */
  .orderlist__cell--product{display:block}
}

@media(max-width:560px){
  .orderlist__foot{gap:6px}
}

@media(max-width:400px){
  .orderlist__row{padding:12px 13px}
}


/* ============================================================
   LIGHTBOX
   ============================================================ */
/* ============================================================
   .lightbox, общий просмотрщик фото товара (каталог + карточки)
   Разметку инжектит core.js (модуль KLightbox) в конец <body>,
   страницы её НЕ несут. Публичный API:
     window.KLightbox.open(photos, start, title, url) / .close()
   Открытие/навигация/счётчик «N / M»/закрытие (крестик · Esc ·
   клик по фону/стрелки/лента). Токены :root: --blue/--blue-d.
   Брейк по канон-лестнице, доводка на 560.
   ============================================================ */
.lightbox{position:fixed;inset:0;z-index:var(--z-lightbox);display:none;align-items:center;justify-content:center}
.lightbox--open{display:flex;animation:lightbox-fade .16s ease}
@keyframes lightbox-fade{from{opacity:0}to{opacity:1}}

/* ЗАГРУЗКА КАДРА. Файл выгрузки весит сотни килобайт, и переключение выглядело
   поломкой: нажал стрелку, и несколько секунд пустоты. Носитель состояния это
   aria-busy на самом окне (ставит js/lightbox.js), второго не заводим.
   Кольцо лежит ПОВЕРХ кадра и ничего не двигает: прежний кадр остаётся на
   месте, пока грузится новый, и так видно, что именно меняется. */
.lightbox[aria-busy="true"] .lightbox__fig::after{
  content:"";position:absolute;left:50%;top:50%;width:38px;height:38px;margin:-19px 0 0 -19px;
  border:3px solid rgba(255,255,255,.35);border-top-color:#fff;border-radius:50%;
  animation:lightbox-spin .8s linear infinite;
}
.lightbox[aria-busy="true"] .lightbox__img{opacity:.45;transition:opacity .15s}
@keyframes lightbox-spin{to{transform:rotate(360deg)}}
@media (prefers-reduced-motion:reduce){
  .lightbox[aria-busy="true"] .lightbox__fig::after{animation:none;border-top-color:rgba(255,255,255,.35)}
}
/* Подложка: .88 просвечивала, на 390 сквозь неё читались шапка и заголовок
   страницы, фото не выглядело единственным объектом внимания. Плотность
   поднята до .96 + лёгкое размытие подложки (где поддерживается).
   rgba() тёмного бренд-синего по правилу токенов допустим с комментарием. */
.lightbox__scrim{position:absolute;inset:0;background:rgba(10,16,34,.96);
  -webkit-backdrop-filter:blur(3px);backdrop-filter:blur(3px)}
.lightbox__bar{position:absolute;top:16px;left:20px;z-index:var(--z-content-top);display:flex;align-items:center;gap:14px;color:#fff;max-width:calc(100% - 150px)}
.lightbox__title{min-width:0;font-weight:800;font-size:15px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.lightbox__counter{font-size:13px;color:rgba(255,255,255,.72);font-weight:700;flex-shrink:0}
/* ЕСЛИ КАДР ОДИН, СЧЁТЧИКА НЕТ. «1 / 1» не сообщал ничего, а пустой узел иначе
   съел бы gap строки. Пустым его оставляет core.js (render), как и счётчик
   раздела в шапке (.header__catpill-num:empty), один приём на проект. */
.lightbox__counter:empty{display:none}
.lightbox__open{flex-shrink:0;display:inline-flex;align-items:center;gap:6px;height:32px;padding:0 13px;border-radius:999px;background:var(--blue);color:#fff;font:inherit;font-size:12.5px;font-weight:800;border:0;cursor:pointer;text-decoration:none}
.lightbox__open:hover{background:var(--blue-d)}
.lightbox__open i{font-size:15px}
/* УДАЛЕНО (четвёртая приёмка): .lightbox__open--hidden. Ссылки-цели нет
   (KLightbox.open без url) → узла .lightbox__open в DOM просто НЕТ: <a> без
   href канон запрещает, а скрытый-но-живой <a href="#"> ловил фокус и вёл
   в никуда. Создаёт и удаляет узел core.js (setOpenLink). */
.lightbox__close{position:absolute;top:14px;right:16px;z-index:var(--z-content-top);width:44px;height:44px;border-radius:50%;border:0;background:rgba(255,255,255,.14);color:#fff;font-size:22px;cursor:pointer;display:flex;align-items:center;justify-content:center;transition:background .14s}
.lightbox__close:hover{background:rgba(255,255,255,.3)}
.lightbox__nav{position:absolute;top:50%;transform:translateY(-50%);z-index:var(--z-content-top);width:52px;height:52px;border-radius:50%;border:0;background:rgba(255,255,255,.14);color:#fff;font-size:26px;cursor:pointer;display:flex;align-items:center;justify-content:center;transition:background .14s}
.lightbox__nav:hover{background:rgba(255,255,255,.3)}
.lightbox__nav--prev{left:18px}
.lightbox__nav--next{right:18px}
.lightbox__nav--hidden{display:none}
.lightbox__dialog{position:relative;z-index:var(--z-content);display:flex;flex-direction:column;align-items:center;justify-content:center;width:100%;height:100%;padding:64px 84px 18px}
.lightbox__fig{position:relative;margin:0;flex:1;min-height:0;display:flex;align-items:center;justify-content:center;width:100%}
.lightbox__img{max-width:100%;max-height:100%;object-fit:contain;border-radius:14px;background:#fff;box-shadow:0 30px 80px -20px rgba(0,0,0,.6);cursor:zoom-in;transition:transform .2s}
.lightbox__img--zoomed{cursor:zoom-out;transform:scale(1.7)}
.lightbox__strip{display:flex;gap:8px;padding:14px 4px 2px;overflow-x:auto;max-width:100%;flex-shrink:0}
/* если фото одно, ленты нет. Модификатором, а не inline display: инлайн перебивал
   бы медиазапросы компонента */
.lightbox__strip--hidden{display:none}
/* лента лайтбокса это единственный скроллер проекта на ТЁМНОМ: полоса тоньше
   и без поля вокруг ползунка. Значения взяты из оригинала (kapika-product.html:530-531),
   ховера у неё там не было, поэтому --sb-thumb-hover равен --sb-thumb.
   Сам вид полосы рисует примесь .scroller (см. секцию SCROLLER), а узел
   получает её в разметке лайтбокса (core.js, модуль 13). */
.lightbox__strip{--sb-w:6px;--sb-pad:0;--sb-thumb:rgba(255,255,255,.3);--sb-thumb-hover:rgba(255,255,255,.3);--sb-face:transparent}
.lightbox__thumb{width:62px;height:62px;flex-shrink:0;border-radius:10px;overflow:hidden;border:2px solid transparent;background:#fff;cursor:pointer;padding:0;opacity:.55;transition:opacity .14s,border-color .14s}
.lightbox__thumb img{width:100%;height:100%;object-fit:contain;display:block;padding:4px}
.lightbox__thumb--active{opacity:1;border-color:#fff}
.lightbox__thumb:hover{opacity:1}
.lightbox__thumb:focus-visible,.lightbox__nav:focus-visible,.lightbox__close:focus-visible,.lightbox__open:focus-visible{outline:2px solid #fff;outline-offset:2px}

/* доводка на телефоне (свод бывш. 640 → 560) */
@media(max-width:560px){
  .lightbox__dialog{padding:56px 10px 12px}
  .lightbox__nav{width:42px;height:42px;font-size:22px}
  .lightbox__nav--prev{left:6px}
  .lightbox__nav--next{right:6px}
  /* «Открыть карточку» больше не заезжает под крестик: полосу ограничиваем
     справа реальным местом кнопки-крестика (44px + отступы), а не процентом */
  .lightbox__bar{left:14px;right:70px;max-width:none;gap:10px}
  .lightbox__open{height:30px;padding:0 11px;font-size:12px}
  .lightbox__open i{font-size:14px}
  .lightbox__thumb{width:48px;height:48px}
}



/* ============================================================
   ЗОНА НАЖАТИЯ НА ТАЧЕ БОЛЬШЕ, ЧЕМ ВИДНО
   Замер на стенде: кнопка степпера 38×36, точка слайдера 10×10. Пальцем в
   такое попадают через раз, а степперов на странице каталога до 446 штук.
   Утверждённый размер при этом менять нельзя: он и есть дизайн.
   Поэтому растим НЕВИДИМУЮ зону псевдоэлементом: вид остаётся прежним,
   промахов меньше. ::after, а не padding: padding сдвинул бы соседей.
   ============================================================ */
@media(hover:none),(max-width:768px){
  .stepper__btn,.hero__dot,.home__dot,.field__toggle,
  .filters__facet-toggle,.filters__reset,.filters__showall,.filters__preset{position:relative}
  .stepper__btn::after,.hero__dot::after,.home__dot::after,.field__toggle::after,
  .filters__facet-toggle::after,.filters__reset::after,.filters__showall::after,
  .filters__preset::after{
    content:"";position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);
    width:max(100%,44px);height:max(100%,44px);
  }
  /* Точки слайдера стоят в 9px друг от друга: зона в 44px их бы перекрыла и
     нажатие уходило соседней. Здесь берём ровно зазор, 20px. */
  .hero__dot::after,.home__dot::after{width:20px;height:20px}
}

/* ============================================================
   ПОЛОСА ПРО COOKIE (include/cookie-note.php)
   Гостю, один раз. Живёт внизу экрана поверх страницы, как липкая панель
   заказа, но у гостя её не бывает, поэтому за место они не спорят.
   Ширину ограничиваем контейнером страницы: на широком экране полоса во всю
   ширину читается как баннер, а не как сообщение.
   ============================================================ */
/* ШИРИНА ИДЁТ ПО КОНТЕНТУ СТРАНИЦЫ, а не своя. Полоса повторяет контейнер
   .wrap (максимум 1680 и те же боковые поля clamp), поэтому её края стоят
   ровно под краями шапки, подвала и таблицы каталога. Раньше стояло своё
   1240, из-за чего полоса была уже контента и висела не по сетке. */
.cookiebar{
  position:fixed;left:0;right:0;bottom:0;z-index:var(--z-sticky);
  padding:0 clamp(16px,4vw,40px) 18px;pointer-events:none;
}
.cookiebar__card{
  max-width:1680px;margin:0 auto;pointer-events:auto;
  display:flex;align-items:center;gap:22px;flex-wrap:wrap;
  padding:18px 24px;
  /* Тень глубже, чем у обычной плитки: полоса лежит ПОВЕРХ страницы, и ей
     нужно от неё оторваться. Слоя два: короткий плотный даёт край, а длинный
     мягкий даёт объём. */
  box-shadow:0 2px 6px rgba(20,38,90,.08), 0 12px 32px rgba(20,38,90,.14);
  border-color:var(--line2);
}
.cookiebar__body{flex:1 1 420px;min-width:0}
.cookiebar__title{margin:0 0 4px;font-size:15px;font-weight:800;color:var(--ink);line-height:1.3}
.cookiebar__text{margin:0;font-size:13.5px;line-height:1.5;color:var(--ink2);font-weight:500}
/* Кнопка не сжимается: «Понятно» в две строки читается как ошибка вёрстки. */
.cookiebar__ok{flex:0 0 auto}

@media(max-width:560px){
  .cookiebar{padding:0 12px 12px}
  .cookiebar__card{padding:15px 17px;gap:14px}
  .cookiebar__body{flex-basis:100%}
  .cookiebar__title{font-size:14.5px}
  .cookiebar__text{font-size:13px}
  /* На узком экране кнопка во всю ширину, иначе она прижимается к краю и в
     неё трудно попасть большим пальцем. */
  .cookiebar__ok{width:100%;justify-content:center}
}

/* ============================================================
   СООБЩЕНИЕ ОБ ОТКАЗЕ (window.kapikaToast)
   Вместо системного alert(): он выглядит чужим на любом сайте, блокирует
   страницу и не даёт ни стиля, ни времени жизни. Появляется снизу по центру,
   над липкой панелью заказа, и уходит сам.
   ============================================================ */
.toast{
  position:fixed;left:50%;transform:translateX(-50%);
  bottom:calc(var(--orderbar-h, 0px) + var(--cookiebar-h, 0px) + 20px);
  z-index:var(--z-toast);
  max-width:min(560px, calc(100vw - 32px));
  display:flex;align-items:flex-start;gap:11px;
  padding:14px 18px;border-radius:var(--r);
  background:var(--ink);color:#fff;
  font-size:14px;line-height:1.45;font-weight:600;
  box-shadow:0 8px 28px rgba(20,38,90,.28);
  opacity:0;transition:opacity .18s ease-out, transform .18s ease-out;
}
.toast--in{opacity:1}
.toast i{font-size:19px;line-height:1.2;flex-shrink:0}
.toast--warn i{color:#FFC46B}
.toast--ok i{color:var(--green)}
@media(prefers-reduced-motion:reduce){ .toast{transition:none} }
