/* ============================================================
   LIBRE RESALE — интернет-магазин. Чистый премиальный e-commerce
   в духе TSUM Collect: белый фон, sans-serif, минимализм.
   ============================================================ */

/* Палитра строго ахроматическая: ни одного цветного канала, все оттенки —
   r == g == b. Раньше здесь был тёплый набор с золотым акцентом; на вещах
   разных цветов он спорил с фотографией и удешевлял её. Чёрно-белый фон
   не спорит ни с чем, а единственным цветом на странице остаётся товар.
   Имена переменных сохранены: --accent и --sale теперь просто чёрный,
   поэтому восемьсот строк ниже и inline-стили в HTML продолжают работать. */
:root {
    --bg:      #ffffff;
    --ink:     #101010;
    --muted:   #767676;
    --muted-2: #a8a8a8;
    --line:    #e6e6e6;
    --soft:    #f6f6f6;
    --soft-2:  #ededed;
    --accent:  #101010;
    --sale:    #101010;
    /* Красная цена со скидкой — как у ЦУМ Collect (решение владельца). */
    --sale-red: #d81e28;
    /* Бейдж скидки ТЕМ ЖЕ красным, что и цена: рыжий #f0461c («как у
       Ламоды») дрался с #d81e28 на одной карточке — два акцента, детектор
       Impeccable «two accents fighting». Один красный на всё (владелец
       утвердил, пункт 1 референсов). */
    --sale-badge: #d81e28;
    /* Заголовочный голос: Tenor Sans — люкс-гротеск с кириллицей. Заголовки
       и имена брендов; текст и кнопки остаются Inter, логотип — Cabin. */
    --font-display: 'Tenor Sans', 'Inter', -apple-system, sans-serif;
    --maxw:    1440px;
    --pad:     clamp(16px, 4vw, 56px);
    --ease:    cubic-bezier(.22,.61,.36,1);
}

* { margin: 0; padding: 0; box-sizing: border-box; }
html { scroll-behavior: smooth; }
body {
    font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
    color: var(--ink); background: var(--bg); line-height: 1.5;
    font-size: 15px; -webkit-font-smoothing: antialiased; text-rendering: optimizeLegibility;
}
a { color: inherit; text-decoration: none; }
img { display: block; max-width: 100%; }
button { font-family: inherit; cursor: pointer; }

/* ===== Плавные переходы между страницами ===== */
/* Только прозрачность, без сдвига. Пока в анимации был transform, main на
   время её проигрывания становился системой координат для всех потомков с
   position: fixed — и шторка фильтров, и алфавитный указатель брендов
   позиционировались не по экрану, а по внутренностям страницы длиной в
   девятнадцать метров. Ловушка срабатывала дважды и каждый раз выглядела
   как «элемент уехал в никуда»; двенадцать пикселей подъезда того не стоят. */
main { animation: pageIn .4s ease; }
@keyframes pageIn { from { opacity: 0; } to { opacity: 1; } }
/* Фон корня — белый явно: резинка прокрутки iOS за краями страницы
   красится цветом html, и без правила он отличался от белого таббара. */
html { background: #fff; }

/* Полноэкранная карусель фото товара: тап по снимку в галерее. */
.lightbox { position: fixed; inset: 0; z-index: 300; background: #fff; display: none; }
.lightbox.show { display: block; }
.lb-close {
    position: absolute; top: calc(env(safe-area-inset-top, 0px) + 10px); right: 10px;
    z-index: 2; width: 40px; height: 40px; border: none; border-radius: 50%;
    background: rgba(16,16,16,.06); color: var(--ink); font-size: 24px; line-height: 1;
    display: grid; place-items: center; cursor: pointer;
}
.lb-track {
    display: flex; height: 100%; overflow-x: auto;
    scroll-snap-type: x mandatory; scrollbar-width: none;
}
.lb-track::-webkit-scrollbar { display: none; }
.lb-slide {
    flex: 0 0 100%; scroll-snap-align: center;
    display: grid; place-items: center; padding: 30px 0;
}
.lb-slide img { max-width: 100%; max-height: 100%; object-fit: contain; }
.lb-dots {
    position: absolute; bottom: calc(env(safe-area-inset-bottom, 0px) + 20px);
    left: 0; right: 0; display: flex; justify-content: center; gap: 6px;
}
.lb-dots span { width: 6px; height: 6px; border-radius: 50%; background: var(--muted-2); }
.lb-dots span.on { background: var(--ink); }
body.is-leaving { opacity: 0; transition: opacity .18s ease; }
@media (prefers-reduced-motion: reduce) {
    main { animation: none; }
    body.is-leaving { transition: none; opacity: 1; }
}

/* ===== Смена пола: подмена содержимого без рывка ===== */
/* Товары и обложки категорий гасим сразу по нажатию и возвращаем, когда новые
   уже в разметке. Без этого экран моргает: женская сумка стоит на месте ровно
   до мгновения, когда её скачком подменяет мужская, и глаз читает это как сбой,
   а не как результат своего же нажатия. Только прозрачность, без transform, —
   ловушка с position: fixed описана абзацем выше. */
.lr-swap-out { opacity: 0; transition: opacity .15s ease; }
.lr-swap-in  { animation: lrSwapIn .28s ease both; }
@keyframes lrSwapIn { from { opacity: 0; } to { opacity: 1; } }

/* ===== Первое появление каталога: лесенка (пункт 8 референсов) =====
   Класс вешает catalog.js ТОЛЬКО на первый рендер списка; задержка
   каждой карточки — в inline --d (номер × 25 мс, потолок внутри JS).
   Фильтры, пол и «показать ещё» класса не получают — там мгновенно. */
.pcard.enter { opacity: 0; animation: cardIn .25s ease-out forwards; animation-delay: var(--d, 0ms); }
@keyframes cardIn { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: translateY(0); } }

/* ===== Бейдж корзины: пружинка при добавлении (пункт 7 референсов) ===== */
@keyframes badgePop { 0% { transform: scale(1); } 50% { transform: scale(1.35); } 100% { transform: scale(1); } }
.badge-pop { animation: badgePop .3s cubic-bezier(.34, 1.56, .64, 1); }

@media (prefers-reduced-motion: reduce) {
    .lr-swap-out { transition: none; opacity: 1; }
    .lr-swap-in { animation: none; }
    .pcard.enter { animation: none; opacity: 1; }
    .badge-pop { animation: none; }
    .sheet { animation-duration: .01s; }
}
.wrap { width: 100%; max-width: var(--maxw); margin-inline: auto; padding-inline: var(--pad); }

/* ============================ ШАПКА ============================ */
/* Тёмной полосы над шапкой больше нет. Она занимала 38px в самом дорогом
   месте страницы и несла три ссылки, которые и так есть в меню категорий,
   в бургере и в подвале, плюс одну строку про подлинность. Строка осталась —
   переехала в правый край меню категорий, где не требует отдельного этажа. */

/* контейнер не должен быть containing block — иначе sticky-шапка отлипает через ~160px скролла */
#app-header { display: contents; }
/* Фон сплошной белый. Полупрозрачный с блюром просвечивал уезжающими под
   шапку карточками, и она оказывалась чуть темнее панели категорий под ней —
   сверху страницы было видно два разных белых. */
.header { position: sticky; top: -1px; z-index: 90; background: #fff; }
.header .wrap { display: grid; grid-template-columns: auto 1fr auto; align-items: center; gap: 30px; height: 76px; }
/* Логотип набран Cabin — той же гарнитурой, что и приложение. Раньше сайт
   рисовал его Inter, приложение — двумя другими шрифтами, и человек за один
   сеанс видел три разных написания одного названия. Разрядка .18em обязательна:
   у прописных нет выносных элементов, и без неё они слипаются в серое пятно. */
.logo { font-family: 'Cabin', 'Inter', -apple-system, sans-serif; font-weight: 700; font-size: 22px; letter-spacing: .18em; white-space: nowrap; }
/* Точка-разделитель убрана: --accent теперь чёрный, и она не подсвечивала
   ничего, а лишь мешала. Логотип должен переживать набор обычным текстом —
   в подписи под иконкой, в посте, в чеке точку не поставить. */

.search { position: relative; max-width: 560px; width: 100%; justify-self: center; }
/* Поле поиска по дизайн-пакету: прямоугольник 42px с радиусом 8, а не
   пилюля. Пилюли из редизайна ушли совсем — остались только у счётчиков. */
.search input {
    width: 100%; height: 42px; border: 1px solid var(--line); background: var(--soft);
    border-radius: 8px; padding: 0 20px 0 44px; font-size: 14px; color: var(--ink);
    outline: none; transition: border-color .2s, background .2s;
}
.search input:focus { border-color: var(--accent); background: #fff; }
.search .ic { position: absolute; left: 16px; top: 50%; transform: translateY(-50%); color: var(--muted-2); pointer-events: none; }
.search .ic svg { display: block; }
.search-close { display: none; }

/* Панель живых подсказок поиска */
.search-panel { position: absolute; top: calc(100% + 10px); left: 0; right: 0; background: #fff; border: 1px solid var(--line); border-radius: 8px; box-shadow: 0 18px 50px rgba(0,0,0,.14); padding: 6px; z-index: 95; display: none; max-height: 70vh; overflow-y: auto; }
.search-panel.show { display: block; }
.sug { display: flex; align-items: center; gap: 14px; padding: 8px 10px; border-radius: 10px; transition: background .15s; }
.sug:hover { background: var(--soft); }
.sug-ph { width: 46px; height: 58px; border-radius: 6px; background: var(--soft-2); overflow: hidden; flex: none; display: grid; place-items: center; color: var(--muted-2); }
.sug-ph img { width: 100%; height: 100%; object-fit: cover; }
.sug-txt { flex: 1; min-width: 0; }
.sug-brand { font-size: 13px; font-weight: 700; }
.sug-name { font-size: 13px; color: var(--muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.sug-price { font-size: 14px; font-weight: 700; white-space: nowrap; }
.sug-all { display: block; padding: 12px 10px; font-size: 13px; font-weight: 600; color: var(--accent); border-top: 1px solid var(--line); margin-top: 4px; }
.sug-empty { padding: 22px 12px; text-align: center; color: var(--muted); font-size: 14px; }

.h-actions { display: flex; align-items: center; gap: 8px; }
.only-mobile { display: none; }
/* Рамки у кнопки больше нет, «вы вошли» показывает одна точка в углу. */
.acct-btn.authed::after { content: ''; position: absolute; top: 4px; right: 4px; width: 8px; height: 8px; border-radius: 50%; background: var(--ink); border: 2px solid #fff; }
/* Переключатель пола по дизайн-пакету: серая плашка с радиусом 8, выбранный
   пол выделен цветом текста, а не чёрной заливкой — заливка спорила бы с
   кнопками, которые в редизайне единственные чёрные плашки шапки. */
.seg { display: inline-flex; align-items: center; height: 36px; padding: 0 2px; border-radius: 8px; background: var(--soft); }
/* Прежний стиль вернулся по слову владельца: капс «как у ЦУМа» не
   зашёл — «мне нравился шрифт, который был до этого». */
.seg button { border: none; background: transparent; height: 32px; padding: 0 14px; border-radius: 6px; font-size: 13px; font-weight: 600; color: var(--muted); transition: color .2s; }
.seg button.on { color: var(--ink); }
/* Иконки шапки без кругов и рамок — просто контур в зоне нажатия 38px,
   как в дизайн-пакете. Круглая рамка вокруг каждой иконки давала шапке
   три лишних кольца, к которым взгляд цеплялся раньше, чем к товару. */
.icon-btn { position: relative; width: 38px; height: 38px; border: none; border-radius: 8px; background: transparent; display: grid; place-items: center; color: var(--ink); transition: background .2s; }
.icon-btn:hover { background: var(--soft); }
.icon-btn svg { display: block; }
.burger { display: none; background: none; border: none; color: var(--ink); }
.burger svg { display: block; }

/* Навигация категорий. Прилипает под шапкой, а не уезжает вместе со
   страницей: логотип, поиск и категории читаются как один блок, и линейка
   у этого блока ровно одна — снизу. */
.catnav { position: sticky; top: 75px; z-index: 89; background: #fff; border-bottom: 1px solid var(--line); }
/* Только собственная строка меню, а не любой .wrap внутри: у выпадающей
   панели брендов свой контейнер, и высота в 48px его бы расплющила. */
.catnav > .wrap { display: flex; align-items: center; height: 48px; }
.catnav-links { display: flex; align-items: center; gap: 30px; min-width: 0; flex: 1; overflow-x: auto; scrollbar-width: none; }
.catnav-links::-webkit-scrollbar { display: none; }
.catnav a { font-size: 13px; letter-spacing: .05em; text-transform: uppercase; color: var(--ink); font-weight: 500; white-space: nowrap; position: relative; padding: 4px 0; transition: opacity .2s; }
.catnav a:hover { opacity: .55; }
.catnav a.sale { font-weight: 700; }

/* Выпадающая штора меню — как в ЦУМ и Farfetch: наведение на «Бренды»
   показывает марки с поиском и избранным, наведение на раздел — его
   подкатегории. Панель — потомок самой .catnav (она sticky, то есть
   позиционированная), поэтому висит ровно под линейкой меню на любой
   странице, а курсор переезжает в неё, не теряя наведения. */
.catnav-links a[data-mega] { display: inline-flex; align-items: center; gap: 6px; }
.catnav-links a[data-mega]::after { content: ""; width: 5px; height: 5px; border-right: 1.4px solid currentColor; border-bottom: 1.4px solid currentColor; transform: translateY(-2px) rotate(45deg); transition: transform .2s; }
.catnav-links a.on { opacity: .55; }
.catnav-links a.on[data-mega]::after { transform: translateY(1px) rotate(-135deg); }
.mega { position: absolute; left: 0; right: 0; top: 100%; background: #fff; border-bottom: 1px solid var(--line); box-shadow: 0 20px 30px -24px rgba(0,0,0,.4); padding: 26px 0 28px; opacity: 0; visibility: hidden; transform: translateY(-6px); transition: opacity .18s var(--ease), transform .18s var(--ease), visibility .18s; }
.catnav.mega-open .mega { opacity: 1; visibility: visible; transform: none; }
.mega-cols { columns: 5; column-gap: 30px; }
.mega-cols > a { display: block; padding: 7px 0; font-size: 14px; font-weight: 500; letter-spacing: 0; text-transform: none; break-inside: avoid; }
.mega-all { display: inline-block; margin-top: 18px; font-weight: 700; border-bottom: 1px solid var(--ink); padding-bottom: 2px; }
.mega-sub { font-size: 12px; font-weight: 700; letter-spacing: .12em; text-transform: uppercase; color: var(--muted); margin-bottom: 12px; }
.mega-empty { color: var(--muted); padding: 12px 0; }
/* Поиск внутри шторы: марок под триста, и листать пять колонок ради одной
   нужной — дольше, чем набрать три буквы. */
.mega-search { position: relative; max-width: 420px; margin-bottom: 20px; }
.mega-search .ic { position: absolute; left: 16px; top: 50%; transform: translateY(-50%); color: var(--muted); font-size: 16px; }
.mega-search input { width: 100%; height: 42px; border: 1px solid var(--line); border-radius: 8px; padding: 0 18px 0 40px; font: inherit; font-size: 14px; background: var(--soft); }
.mega-search input:focus { outline: none; border-color: var(--ink); background: #fff; }
/* Строка марки со звездой. Звезда — не ссылка, поэтому она button: клик по
   ней добавляет в избранное и никуда не уводит. */
.mb-row { display: flex; align-items: center; gap: 9px; padding: 6px 0; break-inside: avoid; }
.mb-row a { font-size: 14px; font-weight: 500; letter-spacing: 0; text-transform: none; }
.mb-star { background: none; border: none; padding: 0; font-size: 15px; line-height: 1; color: var(--muted-2); transition: color .2s, transform .2s; }
.mb-star:hover { color: var(--ink); transform: scale(1.15); }
.mb-star.on { color: var(--ink); }
.mega-fav { margin-bottom: 20px; padding-bottom: 18px; border-bottom: 1px solid var(--line); }
.mega-fav .mega-cols { columns: 5; }
@media (max-width: 1100px) { .mega-cols, .mega-fav .mega-cols { columns: 3; } }

/* ============================ КНОПКИ ============================ */
.btn { display: inline-flex; align-items: center; justify-content: center; gap: 10px; height: 52px; padding: 0 30px; border-radius: 10px; font-size: 15px; font-weight: 600; border: 1px solid transparent; transition: transform .25s var(--ease), background .25s, color .25s; }
.btn-dark { background: var(--ink); color: #fff; }
.btn-dark:hover { transform: translateY(-2px); }
.btn-ghost { background: transparent; color: var(--ink); border-color: var(--ink); }
.btn-ghost:hover { background: var(--ink); color: #fff; }
.btn-block { width: 100%; }

/* Старый блок .hero (баннер-картинка с текстом поверх и .hero-cap) удалён:
   разметка под него исчезла ещё раньше, а правила остались и задавали
   фиксированную высоту с overflow: hidden новой витрине. Живой стиль
   витрины — в разделе «ВИТРИНА (ГЛАВНАЯ)» ниже. */
.eyebrow { font-size: 12px; letter-spacing: .26em; text-transform: uppercase; color: var(--accent); font-weight: 600; }

/* ============================ СЕКЦИИ ============================ */
/* только вертикальный отступ — иначе перебивает боковой padding у .section.wrap */
.section { padding-block: clamp(48px, 7vw, 88px); }
.section-head { display: flex; align-items: flex-end; justify-content: space-between; margin-bottom: 30px; gap: 20px; }
/* Заголовки секций — заголовочным голосом (пункт 2 референсов). */
.section-head h2 { font-family: var(--font-display); font-size: clamp(22px, 2.8vw, 34px); font-weight: 400; letter-spacing: .02em; }
.section-head .link { font-size: 14px; font-weight: 600; color: var(--muted); white-space: nowrap; transition: color .2s; }
.section-head .link:hover { color: var(--accent); }

/* ============================ СЕТКА ТОВАРОВ ============================ */
/* Просветы шире: воздух между карточками — «галерея», не «склад»
   (пункт 4 референсов, дайл DENSITY 3 по Taste). */
.grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: clamp(20px, 2.2vw, 34px) clamp(14px, 1.6vw, 24px); }
.grid.g5 { grid-template-columns: repeat(5, 1fr); }

.pcard { display: block; position: relative; }
/* Радиус 8 — общий для фото и полей по дизайн-пакету. */
.pcard .ph { position: relative; aspect-ratio: 3/4; background: var(--soft); overflow: hidden; border-radius: 8px; }
/* .35s, а не .7s: зум по наведению — самый частый жест каталога, долгий
   отклик «тянет» (правило Эмиля: ховеры 200–400 мс; пункт 5 референсов). */
.pcard .ph img { width: 100%; height: 100%; object-fit: cover; transition: transform .35s var(--ease); }
.pcard:hover .ph img { transform: scale(1.045); }
.pcard .ph .noimg { position: absolute; inset: 0; display: grid; place-items: center; color: var(--muted-2); }
/* Универсальные плитки категорий: иконка на мягком фоне вместо фото товара */
.cat-card .cat-ph { display: grid; place-items: center; background: linear-gradient(135deg, #f4f4f4 0%, #e9e9e9 100%); }
.cat-card .cat-ph svg { width: 42%; height: 42%; color: #6b6b6b; transition: transform .5s var(--ease), color .3s; }
.cat-card:hover .cat-ph svg { transform: scale(1.08); color: var(--ink); }
/* Фото категории поверх иконки; если файла нет — img.onerror удаляет её и видна иконка */
.cat-card .cat-ph .cat-img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; transition: transform .7s var(--ease); }
.cat-card:hover .cat-ph .cat-img { transform: scale(1.05); }
/* Сетка категорий в одну строку на любом десктопе. Раньше минимум был 150px:
   на ноутбуке в ряд помещалось пять плиток, шестая («Ювелирные украшения»)
   уезжала на вторую строку и висела там одна рядом с четырьмя пустыми
   местами — ровно то, что читается как сломанная вёрстка. 128px хватает,
   чтобы шесть разделов встали в строку начиная с 901px ширины.
   ID перебивает .grid/.grid.g5 и медиазапросы, поэтому картинки не разъезжаются. */
#catGrid { grid-template-columns: repeat(auto-fit, minmax(128px, 1fr)); }
/* Длинное название переносится на вторую строку, а не обрезается многоточием:
   «Ювелирные укра…» не говорит человеку ничего. */
.cat-card .p-brand {
    display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
    overflow: hidden; line-height: 1.25;
}
/* Бейдж скидки — как у Ламоды: рыжий, ВПРИТЫК в нижний левый угол фото
   (владелец сверил по их карточке). Скругление короба его подрежет само —
   у .ph overflow: hidden; свой радиус только у верхнего правого угла. */
.pcard .badge { position: absolute; bottom: 0; left: 0; background: var(--sale-badge); color: #fff; font-size: 12px; font-weight: 700; padding: 3px 7px; border-radius: 0 6px 0 0; letter-spacing: .02em; }
/* Сердечко видно всегда, а не по наведению. Пряталось оно ради чистой сетки,
   но с мышкой у нас заходит меньшинство, а на телефоне наведения не бывает
   вовсе: для всех остальных избранного на сайте просто не существовало.
   Контраст держим кружком: фотографии у нас на белом, и один голый контур
   на светлой обуви теряется. */
/* Сердце без подложки — решение владельца: кружок за иконкой выделял
   кнопку сильнее самой вещи. Зона нажатия остаётся прежней, просто
   прозрачная; фотографии на белом фоне, контур читается и так. */
.pcard .fav { position: absolute; top: 8px; right: 8px; width: 34px; height: 34px; background: none; display: grid; place-items: center; color: var(--ink); cursor: pointer; transition: color .2s, transform .2s; }
.pcard .fav svg { display: block; filter: drop-shadow(0 1px 2px rgba(255,255,255,.9)); }
.pcard .fav:hover { transform: scale(1.12); }
/* Сохранённый товар — то же сердце, но залитое. Иконка одна, состояние
   рисует CSS: у контура появляется заливка цветом линии. */
.pcard .fav.on { color: var(--sale); }
.pcard .fav.on svg path { fill: currentColor; }
/* Марка капслоком по дизайн-пакету: «CHANEL», а не «Chanel». */
/* Бренд — заголовочным голосом, разреженным капсом; цена обычным весом:
   фото решает, текст не кричит (пункты 2 и 4 референсов, стиль ЦУМа). */
.pcard .p-brand { font-family: var(--font-display); font-size: 12px; font-weight: 400; margin: 14px 0 3px; letter-spacing: .12em; text-transform: uppercase; }
.pcard .p-name { font-size: 13px; color: var(--muted); line-height: 1.35; height: 2.7em; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.pcard .p-price { display: flex; align-items: baseline; gap: 8px; margin-top: 8px; }
.pcard .p-price .now { font-size: 15px; font-weight: 600; }
.pcard .p-price .old { font-size: 13px; color: var(--muted-2); text-decoration: line-through; }
/* Строчными, как в дизайн-пакете: «отличное», а не «Отличное». */
.pcard .p-cond { font-size: 12px; color: var(--muted); margin-top: 4px; text-transform: lowercase; }
/* Кто продавец. Кегль тот же, что у состояния, но начертание плотнее и
   регистр не сбит: это не характеристика вещи, а предупреждение — от него
   зависит, можно ли будет вернуть покупку. Кричать не нужно, потеряться
   тоже нельзя. Те же 12 и то же расстояние 3–4 точки в iOS и Android. */
/* Категория, размер и красный процент — строки карточки из приложения.
   На десктопе их место занимают название и состояние, поэтому здесь они
   спрятаны, а включаются в мобильном блоке ниже. */
.pcard .p-cat, .pcard .p-size { display: none; }
/* Цены: старая зачёркнута перед новой, новая при скидке красная. */
.pcard .p-price .now.sale { color: var(--sale-red); }

/* Скелетоны */
.skeleton .ph::after { content: ''; position: absolute; inset: 0; background: linear-gradient(90deg, transparent, rgba(255,255,255,.6), transparent); transform: translateX(-100%); animation: shimmer 1.4s infinite; }
@keyframes shimmer { 100% { transform: translateX(100%); } }
.skeleton .l { height: 12px; background: var(--soft-2); border-radius: 5px; margin-top: 12px; }
.skeleton .l.s { width: 45%; }

.grid-empty { grid-column: 1/-1; text-align: center; color: var(--muted); padding: 60px 0; }

/* Избранные марки — плитками над сеткой товаров. Марок обычно единицы, и
   отдельная сетка ради них выглядела бы пустой; плитки же переносятся по
   ширине и одинаково смотрятся и при двух марках, и при двадцати. */
.fav-brands { margin-bottom: 34px; }
.fav-brands .section-head { margin-bottom: 16px; }
.fav-brands h3 { font-size: 18px; font-weight: 700; letter-spacing: -.01em; }
.fb-chips { display: flex; flex-wrap: wrap; gap: 10px; }
.fb-chip {
    display: inline-flex; align-items: center; gap: 8px;
    padding: 9px 12px 9px 16px; border: 1px solid var(--line); border-radius: 999px;
    font-size: 14px; font-weight: 600; background: #fff;
}
.fb-chip a { color: var(--ink); }
.fb-chip a:hover { opacity: .55; }
/* Крестик — своя цель под палец: 44 px по высоте у самой плитки, поэтому
   кнопке хватает и 24, но нажимать её нужно точно, а не «где-то рядом». */
.fb-x {
    width: 24px; height: 24px; flex: none; border-radius: 50%;
    display: inline-flex; align-items: center; justify-content: center;
    font-size: 11px; color: var(--muted); background: var(--soft-2);
    transition: background .15s, color .15s;
}
.fb-x:hover { background: var(--ink); color: #fff; }

/* ============================ ВИТРИНА (ГЛАВНАЯ) ============================ */
/* Две колонки: слева обещание, справа две настоящие вещи из наличия.
   Стоковой картинки нет намеренно — на витрине ресейла честнее показывать
   то, что действительно можно купить прямо сейчас. */
/* Витрина живёт в той же колонке, что и все секции ниже, и заканчивается
   вместе с ними. Раньше она была во всю ширину окна и на широком мониторе
   расползалась: заголовок улетал к левому краю, фотографии — к правому,
   между ними оставалась пустая середина, и страница переставала выглядеть
   как один макет. Ограничение по --maxw убирает и то, и другое. */
.hero { background: #fff; }

/* ---------- Витрина-мозаика ----------
   Слева одна большая плитка, справа две поменьше в столбик. Пропорция
   1.45:1 не декоративная: главная плитка должна читаться как обложка
   (крупный заголовок в две строки + кнопка), а боковые — как подписи
   к разделам, и при равной ширине эта разница пропадает.

   Высота задана плиткам, а не картинкам: фотографии приезжают позже
   разметки, и если бы ряд держался на них, первый экран прыгал бы на
   каждой загрузке. */
.mosaic {
    display: grid; grid-template-columns: 1.45fr 1fr;
    gap: clamp(10px, 1.1vw, 16px);
    /* Нижнее поле больше верхнего: раньше витрину от «Категорий» отделяла
       строка обещаний, теперь обещания уехали внутрь чёрной плитки, и без
       этого отступа мозаика склеивалась бы со следующим разделом. */
    padding-block: clamp(18px, 2.2vw, 28px) clamp(22px, 3vw, 40px);
}
.mos-big, .mos-tile {
    position: relative; overflow: hidden; border-radius: 4px;
    display: flex; flex-direction: column; isolation: isolate;
}
/* Текст стоит сверху, кнопка — по нижнему краю (margin-top: auto у .mos-btn).
   Раньше низ плитки занимали три строки обещаний, а кнопка стояла сразу под
   заголовком; обещания уехали в полку сервиса под витриной, и место у нижнего
   края освободилось. Кнопка там уместнее: взгляд идёт «что за магазин →
   что за вещь → куда нажать», а не возвращается наверх.

   padding-right держит колонку текста левее снимка: снимок лежит абсолютным
   слоем в правом верхнем углу, обычный padding его не подвинет, а заголовок
   в две строки шире половины плитки и заехал бы прямо под него. */
.mos-big {
    grid-row: span 2; background: var(--ink); color: #fff;
    justify-content: flex-start;
    padding: clamp(26px, 3vw, 44px);
    padding-right: calc(36% + clamp(26px, 3vw, 44px));
    min-height: clamp(320px, 34vw, 460px);
}
.mos-eyebrow { font-size: 11px; letter-spacing: .24em; text-transform: uppercase; opacity: .6; }
/* 42, а не 36: обещания ушли из плитки, и заголовок остался единственным
   текстом в ней — в прежнем кегле он повис в верхней трети чёрного поля.
   max-width не нужен — ширину задаёт padding-right плитки. */
.mos-h { font-size: clamp(22px, 2.9vw, 42px); font-weight: 700; line-height: 1.08; letter-spacing: -.025em; margin: 14px 0 0; }
.mos-btn {
    align-self: flex-start; margin-top: clamp(22px, 2.4vw, 34px);
    background: #fff; color: var(--ink);
    padding: 13px 26px; border-radius: 10px; font-size: 13px; font-weight: 600;
    transition: transform .25s var(--ease);
}
/* Кнопку к нижнему краю прижимает auto, но только когда над ней есть что
   растягивать: на планшете плитка ниже, и auto прилепил бы кнопку к подписи
   со снимком. Поэтому auto — с широкого экрана. */
@media (min-width: 901px) { .mos-btn { margin-top: auto; } }
.mos-big:hover .mos-btn { transform: translateY(-2px); }

.mos-tile { background: var(--soft); padding: clamp(20px, 2.2vw, 30px); min-height: clamp(150px, 16vw, 222px); }
.mos-tile.mos-warm { background: #eee9e2; }
.mos-txt b { display: block; font-size: clamp(17px, 1.6vw, 22px); font-weight: 700; letter-spacing: -.02em; }
.mos-txt i { display: block; font-style: normal; font-size: 13px; color: var(--muted); margin-top: 6px; max-width: 54%; }

/* Снимок и подпись к нему лежат в одном слое поверх фона плитки, но под её
   текстом (z-index: -1 внутри своего stacking-контекста — isolation выше не
   даёт слою уехать за фон всей страницы).

   Снимок прижат к ВЕРХНЕМУ краю, а низ плитки отдан подписи. Раньше фотография
   стояла в нижнем углу, и вся верхняя половина плитки пустовала; теперь плитка
   читается как витринная карточка: вещь сверху, что это за вещь и сколько она
   стоит — снизу.

   Белый фон под снимком оставлен намеренно: фотографии приезжают с вырезанным
   фоном, и на тёмной плитке это выглядит как карточка товара, лежащая на
   витрине, а не как дырка в макете.

   inset задан числом, а не нулём: абсолютный потомок считается от padding-box,
   то есть при inset: 0 слой лёг бы прямо на поля плитки и подпись прижалась бы
   к самому краю. Значения повторяют padding плиток. */
.mos-ph { position: absolute; z-index: -1; pointer-events: none; }
/* Слой сквозной для мыши, а кликабельны только снимок и подпись: иначе пустая
   середина плитки перехватывала бы клик, предназначенный ссылке в каталог. */
.mos-ph img, .amos-ph img, .mos-cap { pointer-events: auto; }
.mos-ph:empty { display: none; }
.mos-big .mos-ph { inset: clamp(26px, 3vw, 44px); }
.mos-tile .mos-ph { inset: clamp(20px, 2.2vw, 30px); }
/* 3/4 — родное соотношение карточек товара (900x1200). В квадрате они бы
   легли с белыми полями сверху и снизу, а на тёмной плитке такая полоса
   читается как брак вёрстки. */
.mos-ph img {
    position: absolute; top: 0; right: 0;
    width: 36%; max-width: 250px; aspect-ratio: 3 / 4;
    object-fit: contain; background: #fff; border-radius: 3px;
    transition: transform .5s var(--ease);
}
/* На боковых плитках снимок меряем не по ширине, а по высоте: плитка ниже,
   чем шире, и от ширины он вышел бы либо крошечным, либо вылез за край.
   Подпись там стоит слева внизу, поэтому снимок может занимать всю высоту. */
.mos-tile .mos-ph img { width: auto; max-width: 46%; height: 100%; }
/* А на большой плитке снимок и подпись стоят в одном, правом углу: снимок
   сверху, марка с ценой под ним. Пока высота считалась только от ширины,
   фотография доставала до подписи и накрывала её своим белым фоном — а буквы
   там белые, и марка пропадала: от «CHANEL» оставалась нижняя половина.
   Поэтому высоту здесь задаём явно, оставляя внизу полосу под подпись. */
.mos-big .mos-ph img { height: calc(100% - 62px); width: auto; }
/* Увеличение по наведению убрано у большой плитки: снимок там прижат к
   правому краю и к подписи, и лишние 5% выезжали за плитку и снова
   наползали на марку. На боковых плитках места по бокам хватает. */
.mos-tile:hover .mos-ph img { transform: scale(1.05); }

/* Подпись — это не украшение, а ответ на вопрос «что там внутри». Марка и цена
   настоящие: они из того же ответа, что и лента новинок, поэтому витрина не
   врёт и обновляется сама. Строка с ценой заодно снимает главный страх
   комиссионного каталога — «а вдруг тут всё по цене бутика». */
.mos-cap {
    position: absolute; left: 0; bottom: 0; max-width: 58%;
    display: flex; flex-direction: column; gap: 3px;
    transition: transform .3s var(--ease);
}
.mos-cap em, .mos-cap b, .mos-cap i {
    display: block; font-style: normal;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.mos-cap em { font-size: 10px; letter-spacing: .2em; text-transform: uppercase; color: var(--muted); }
.mos-cap b { font-size: 12px; font-weight: 600; letter-spacing: .12em; text-transform: uppercase; }
.mos-cap i { font-size: 13px; color: var(--muted); }
/* На главной плитке слева уже стоят заголовок и кнопка, поэтому подпись уходит
   в правый нижний угол — под снимок, на одну линию с кнопкой. 44%, а не 36%:
   при 36% длинные марки обрезались до «MAISON MARGIE…», а справа от них
   оставалось пустое чёрное поле — колонка под снимок и так шире. */
.mos-big .mos-cap { left: auto; right: 0; max-width: 44%; text-align: right; }
.mos-big .mos-cap em { color: rgba(255, 255, 255, .5); }
.mos-big .mos-cap b { color: #fff; }
.mos-big .mos-cap i { color: rgba(255, 255, 255, .62); }
/* На узкой плитке трёх строк не помещается — жертвуем пояснением. */
.mos-tile .mos-cap em { display: none; }
.mos-big:hover .mos-cap, .mos-tile:hover .mos-cap { transform: translateY(-2px); }

/* ============================ ПОЛКА СЕРВИСА ============================
   Четыре обещания магазина сразу под витриной. Здесь они, а не в чёрной
   плитке, по трём причинам: серый 12px по чёрному почти не читался; внутри
   плитки-ссылки на каталог их нельзя было сделать ссылками на разбор в /help;
   и четвёртого обещания — про экспертизу — там не было места вовсе.

   Ни рамок, ни цветных полосок: полка должна прочитаться как строка магазина,
   а не как четыре баннера. Отделяет карточки только линейка сверху всей полки
   и просвет между ними. */
.svc {
    display: grid; grid-template-columns: repeat(4, 1fr);
    gap: clamp(18px, 2.4vw, 40px);
    padding-block: clamp(24px, 3vw, 38px);
    border-top: 1px solid var(--line);
}
/* align-content: start — не косметика. Карточки в полке растягиваются по
   самой высокой (описания у них в две и в три строки), а сетка из двух
   строк auto лишнюю высоту по умолчанию раздаёт строкам. У карточек с
   коротким описанием заголовок из-за этого опускался на 5px ниже соседей,
   и вся полка выглядела кривой. */
.svc-i {
    display: grid; grid-template-columns: auto 1fr;
    grid-template-areas: "ic t" ". d"; gap: 4px 14px;
    align-items: start; align-content: start;
}
/* Пункт-кнопка (самовывоз открывает окно): сбрасываем кнопочный вид,
   чтобы она стояла в полке неотличимо от соседних ссылок. */
button.svc-i { background: none; border: none; padding: 0; font: inherit; text-align: left; cursor: pointer; color: inherit; }
button.svc-i u { text-underline-offset: 3px; }
/* Иконка в мягком кружке: на белом фоне тонкая линия сама по себе теряется,
   а кружок задаёт ей вес, не превращая карточку в баннер. */
.svc-ic {
    grid-area: ic; display: grid; place-items: center; flex: none;
    width: 38px; height: 38px; border-radius: 50%;
    background: var(--soft); color: var(--ink);
    transition: background .2s, color .2s;
}
.svc-ic svg { width: 20px; height: 20px; }
.svc-i:hover .svc-ic { background: var(--ink); color: #fff; }
.svc-i b { grid-area: t; align-self: center; font-size: 14px; font-weight: 700; letter-spacing: -.01em; }
.svc-i:hover b { text-decoration: underline; text-underline-offset: 3px; }
.svc-i i { grid-area: d; font-style: normal; font-size: 12.5px; line-height: 1.5; color: var(--muted); }
/* На среднем экране четыре колонки сжимаются до двух слов в строке — складываем
   в две. Полка при этом остаётся полкой, а не столбиком из четырёх строк. */
@media (max-width: 1100px) { .svc { grid-template-columns: 1fr 1fr; } }

/* ============================ ГОРИЗОНТАЛЬНАЯ ЛЕНТА ============================ */
/* Карточки не переносятся, а уезжают вправо: обрезанная у края лента обещает
   продолжение, ровный край сетки читается как «это весь ассортимент». */
.rail { position: relative; }
.rail-track { display: grid; grid-auto-flow: column; grid-auto-columns: clamp(180px, 21vw, 250px); gap: clamp(12px, 1.4vw, 22px); overflow-x: auto; overscroll-behavior-x: contain; scroll-snap-type: x proximity; scrollbar-width: none; -ms-overflow-style: none; }
.rail-track::-webkit-scrollbar { display: none; }
.rail-track > * { scroll-snap-align: start; }

/* ===== Журнал — карусель первого экрана (в духе Oskelly, референс
   владельца). Фото-коллаж из наших вещей слева, заголовок Tenor с
   подзаголовком и «Подробнее» справа; на телефоне — колонкой. Листается
   руками: стрелки и точки, автопрокрутки нет сознательно. ===== */
.jr-hero { padding: 10px 0 6px; }
.jr-track { display: grid; grid-auto-flow: column; grid-auto-columns: 100%; overflow-x: auto; scroll-snap-type: x mandatory; scrollbar-width: none; -ms-overflow-style: none; }
.jr-track::-webkit-scrollbar { display: none; }
.jr-slide { scroll-snap-align: start; display: grid; grid-template-columns: 1fr 1fr; background: var(--soft); border-radius: 16px; overflow: hidden; min-height: 380px; }
.jr-ph { display: block; overflow: hidden; }
.jr-ph img { width: 100%; height: 100%; object-fit: cover; display: block; }
.jr-body { display: flex; flex-direction: column; align-items: center; justify-content: center; text-align: center; gap: 12px; padding: 30px clamp(20px, 4vw, 64px); }
.jr-body b { font-family: var(--font-display); font-weight: 400; font-size: clamp(24px, 2.6vw, 34px); line-height: 1.25; letter-spacing: .02em; }
.jr-body i { font-style: normal; font-size: 14px; color: var(--muted); line-height: 1.6; max-width: 42ch; }
.jr-btn { display: inline-flex; align-items: center; justify-content: center; border: 1px solid var(--ink); padding: 11px 26px; font-size: 13px; font-weight: 600; color: var(--ink); transition: background .2s, color .2s; }
.jr-slide:hover .jr-btn { background: var(--ink); color: #fff; }
.jr-nav { display: flex; align-items: center; justify-content: center; gap: 14px; padding-top: 12px; }
.jr-arr { width: 34px; height: 34px; border: none; background: none; font-size: 22px; line-height: 1; color: var(--muted); cursor: pointer; transition: color .2s; }
.jr-arr:hover { color: var(--ink); }
.jr-dots { display: flex; gap: 6px; }
.jr-dot { width: 6px; height: 6px; border-radius: 50%; background: var(--muted-2); transition: background .2s, transform .2s; }
.jr-dot.on { background: var(--ink); transform: scale(1.25); }

/* Читалка статьи (/journal.html). Узкая колонка, заголовочный голос. */
.art { max-width: 760px; margin: 0 auto; padding: 26px var(--pad) 60px; }
.art-cover { width: 100%; border-radius: 14px; display: block; margin-bottom: 26px; }
.art h1 { font-family: var(--font-display); font-weight: 400; font-size: clamp(26px, 4vw, 38px); line-height: 1.25; text-align: center; letter-spacing: .02em; margin-bottom: 10px; }
.art .art-sub { text-align: center; color: var(--muted); font-size: 15px; line-height: 1.6; margin-bottom: 30px; }
.art h2 { font-family: var(--font-display); font-weight: 400; font-size: 22px; letter-spacing: .03em; margin: 32px 0 10px; text-align: center; }
.art p { font-size: 15.5px; line-height: 1.75; color: var(--ink); margin-bottom: 14px; }
.art img { width: 100%; border-radius: 12px; margin: 18px 0; display: block; }
.art .art-cta { display: block; margin: 36px auto 0; max-width: 320px; }

@media (max-width: 719.98px) {
    .jr-slide { grid-template-columns: 1fr; min-height: 0; }
    .jr-ph { aspect-ratio: 16/10; }
    .jr-body { padding: 18px 18px 22px; gap: 8px; }
    .jr-body b { font-size: 21px; }
    .jr-body i { font-size: 13px; }
    .jr-hero { padding: 6px var(--pad) 0; }
    /* Два бренда рядом на телефон не влезают: у снимка минимум 120px,
       пара таких шире половины экрана — секция вылезала за край и резалась
       overflow-клипом. В столбик, как полки. */
    .fav-brands { grid-template-columns: 1fr; gap: 26px; }
}

/* ===== Кураторские полки в духе ЦУМ Collect (референс владельца) =====
   Вещь на воздухе, бренд заголовочным капсом ПО ЦЕНТРУ, цена мелко под
   ним. Заголовок секции тоже по центру (.sh-center). */
.sh-center { justify-content: center; text-align: center; }
.trail { grid-auto-columns: clamp(170px, 19vw, 230px); align-items: start; }
.tcard { display: flex; flex-direction: column; align-items: center; gap: 4px; }
.tcard-ph { width: 100%; aspect-ratio: 3/4; display: flex; align-items: center; justify-content: center; overflow: hidden; }
.tcard-ph img { max-width: 92%; max-height: 92%; object-fit: contain; transition: transform .35s var(--ease); }
.tcard:hover .tcard-ph img { transform: scale(1.04); }
.tcard-brand { font-family: var(--font-display); font-size: 17px; letter-spacing: .1em; text-transform: uppercase; color: var(--ink); margin-top: 6px; text-align: center; }
.tcard-price { font-size: 13px; color: var(--muted); }
/* Плитка «Смотреть все» в конце ленты сумок — квадратик с рамкой,
   вертикально по центру ряда, как у ЦУМа. */
.tcard-all { justify-content: center; }
.tcard-all .tcard-allbox { border: 1px solid var(--ink); padding: 26px 22px; font-size: 12px; letter-spacing: .14em; text-transform: uppercase; text-align: center; line-height: 1.7; color: var(--ink); transition: background .2s, color .2s; }
.tcard-all:hover .tcard-allbox { background: var(--ink); color: #fff; }

/* «Любимые бренды»: пара снимков и имя — два бренда в ряд. */
.fav-brands { display: grid; grid-template-columns: 1fr 1fr; gap: clamp(16px, 3vw, 48px); }
.fb-col { display: flex; flex-direction: column; align-items: center; gap: 12px; }
.fb-shots { display: flex; gap: clamp(10px, 1.6vw, 22px); justify-content: center; width: 100%; }
.fb-ph { width: clamp(120px, 16vw, 210px); aspect-ratio: 3/4; display: flex; align-items: center; justify-content: center; overflow: hidden; }
.fb-ph img { max-width: 94%; max-height: 94%; object-fit: contain; transition: transform .35s var(--ease); }
.fb-ph:hover img { transform: scale(1.04); }
.fb-name { font-family: var(--font-display); font-size: clamp(20px, 2.2vw, 26px); letter-spacing: .08em; text-transform: uppercase; color: var(--ink); }
.fb-name:hover { text-decoration: underline; text-underline-offset: 6px; }

/* ===== Окно самовывоза («тап подробнее», карточка магазина как у ЦУМа).
   Место под фото зала (pickup-ph) уже свёрстано — снимки владельца
   встанут каруселью без переделки. ===== */
.pickup-back { position: fixed; inset: 0; background: rgba(15,12,9,.5); backdrop-filter: blur(2px); z-index: 300; }
.pickup-modal { position: fixed; z-index: 310; top: 50%; left: 50%; transform: translate(-50%, -50%); width: min(92vw, 560px); max-height: 90vh; overflow-y: auto; background: #fff; border-radius: 18px; padding: 26px 28px 24px; box-shadow: 0 30px 80px rgba(0,0,0,.3); }
.pickup-x { position: absolute; top: 14px; right: 16px; background: none; border: none; font-size: 24px; line-height: 1; color: var(--muted); cursor: pointer; }
.pickup-x:hover { color: var(--ink); }
.pickup-t { font-family: var(--font-display); font-size: 20px; letter-spacing: .04em; text-align: center; margin-bottom: 16px; }
.pickup-ph { aspect-ratio: 16/9; background: var(--soft); border-radius: 12px; display: grid; place-items: center; color: var(--muted-2); font-size: 13px; margin-bottom: 16px; }
.pickup-addr { font-size: 15px; font-weight: 600; line-height: 1.5; margin-bottom: 14px; }
.pickup-row { display: flex; gap: 18px; margin-bottom: 18px; }
.pickup-cell { flex: 1; }
.pickup-cell b { display: block; font-size: 13px; margin-bottom: 3px; }
.pickup-cell i { font-style: normal; font-size: 13px; color: var(--muted); line-height: 1.5; }
.rail-track .grid-empty { grid-column: 1; }
/* Кнопки висят на уровне фотографий, а не по центру карточки: под снимком
   ещё три строки текста, и «центр» оказался бы под ними. */
.rail-nav { position: absolute; top: 36%; transform: translateY(-50%); z-index: 2; width: 42px; height: 42px; display: grid; place-items: center; border: 1px solid var(--line); border-radius: 50%; background: rgba(255,255,255,.94); backdrop-filter: blur(6px); box-shadow: 0 2px 12px rgba(0,0,0,.10); font-size: 22px; line-height: 1; color: var(--ink); cursor: pointer; transition: background .2s, border-color .2s; }
.rail-nav:hover { background: #fff; border-color: var(--ink); }
.rail-nav.prev { left: -18px; }
.rail-nav.next { right: -18px; }
/* На тач-экранах ленту листают пальцем, стрелки там только мешают. */
@media (hover: none) { .rail-nav { display: none; } }

/* ============================ ПОДБОРКИ ПО БЮДЖЕТУ ============================ */
.picks { display: grid; grid-template-columns: repeat(3, 1fr); gap: clamp(12px, 1.4vw, 22px); }
.pick { display: flex; flex-direction: column; gap: 8px; padding: clamp(20px, 2.4vw, 32px); border: 1px solid var(--line); border-radius: 8px; transition: border-color .2s, transform .2s var(--ease); }
.pick:hover { border-color: var(--ink); transform: translateY(-2px); }
.pick-k { font-size: clamp(17px, 1.7vw, 21px); font-weight: 700; letter-spacing: -.01em; }
.pick-v { font-size: 13px; color: var(--muted); line-height: 1.5; }

/* ============================ КАК ЭТО РАБОТАЕТ ============================ */
.steps { display: grid; grid-template-columns: repeat(3, 1fr); gap: clamp(20px, 3vw, 44px); list-style: none; counter-reset: step; padding: 0; }
.steps li { counter-increment: step; border-top: 2px solid var(--ink); padding-top: 18px; }
.steps li::before { content: "0" counter(step); display: block; font-size: 12px; font-weight: 700; letter-spacing: .14em; color: var(--muted-2); margin-bottom: 12px; }
.steps b { display: block; font-size: 16px; font-weight: 700; margin-bottom: 8px; }
.steps p { font-size: 13px; color: var(--muted); line-height: 1.65; }
.steps a { text-decoration: underline; text-underline-offset: 2px; }

/* ============================ БРЕНДЫ ============================ */
.brand-rail { display: flex; flex-wrap: wrap; gap: 12px; }
.brand-rail a { padding: 12px 22px; border: 1px solid var(--line); border-radius: 8px; font-size: 14px; font-weight: 500; transition: border-color .2s, background .2s; }
.brand-rail a:hover { border-color: var(--ink); background: var(--ink); color: #fff; }

/* Абзац под заголовком страницы бренда. Пустой он схлопывается, чтобы
   не оставлять дыру, пока не пришло имя бренда. */
.brand-intro { max-width: 62ch; font-size: 14px; line-height: 1.65; color: var(--muted); margin: 4px 0 26px; }
.brand-intro:empty { display: none; }

/* ============================ КАТАЛОГ ============================ */
.catalog-layout { display: grid; grid-template-columns: 260px 1fr; gap: 40px; align-items: start; padding-top: 32px; }
.filters { position: sticky; top: 140px; }
.filter-group { border-bottom: 1px solid var(--line); padding: 18px 0; }
.filter-group:first-child { padding-top: 0; }
/* Подзаголовок группы по дизайн-пакету: обычный регистр, без разрядки.
   Капс с разрядкой остался только у надзаголовков-«бровок» витрины. */
.filter-group h4 { font-size: 13px; margin-bottom: 12px; font-weight: 700; }

/* ---------- Чекбокс-списки фильтров (дизайн-пакет) ----------
   Квадрат 18×18 с радиусом 4; выбранный заливается чёрным, галка белая.
   Счётчик наличия — серое число после названия, без скобок. */
.check-list { display: flex; flex-direction: column; gap: 9px; max-height: 260px; overflow-y: auto; }
.check-item { display: flex; align-items: center; gap: 10px; text-align: left; background: none; border: none; padding: 0; font-size: 14px; color: var(--ink); cursor: pointer; }
.check-box { width: 18px; height: 18px; flex: none; border-radius: 4px; border: 1.5px solid var(--line); background: transparent; display: grid; place-items: center; transition: background .15s, border-color .15s; }
.check-box svg { width: 12px; height: 12px; opacity: 0; transition: opacity .15s; }
.check-item:hover .check-box { border-color: var(--muted-2); }
.check-item.on .check-box { background: var(--ink); border-color: var(--ink); }
.check-item.on .check-box svg { opacity: 1; }
.check-item .fcount { font-style: normal; font-size: 12px; color: var(--muted-2); }
.check-lb.top { font-weight: 600; }

/* Поле «Например, Chanel» над списком брендов */
.f-search { position: relative; margin-bottom: 12px; }
.f-search svg { position: absolute; left: 12px; top: 50%; transform: translateY(-50%); width: 14px; height: 14px; color: var(--muted-2); pointer-events: none; }
.f-search input { width: 100%; height: 40px; border: 1px solid var(--line); border-radius: 8px; padding: 0 12px 0 34px; font: inherit; font-size: 13px; outline: none; background: #fff; }
.f-search input:focus { border-color: var(--ink); }
.f-empty { font-size: 13px; color: var(--muted-2); padding: 4px 0; }

/* Chip-фильтры (бренд, цвет, размер, материал) — мультивыбор как в приложении */
.chips-filter { display: flex; flex-wrap: wrap; gap: 8px; max-height: 210px; overflow-y: auto; }
.chips-filter button { display: inline-flex; align-items: center; gap: 7px; border: 1px solid var(--line); background: #fff; border-radius: 8px; padding: 7px 14px; font-size: 13px; color: var(--ink); transition: border-color .15s, background .15s, color .15s; }
.chips-filter button:hover { border-color: var(--ink); }
.chips-filter button.on { background: var(--ink); color: #fff; border-color: var(--ink); }
.chips-filter .sw { width: 13px; height: 13px; border-radius: 50%; border: 1px solid rgba(0,0,0,.18); flex: none; }
/* Количество товаров у варианта фильтра — приходит из фасетов бэкенда */
.chips-filter .fcount { font-style: normal; font-size: 11px; color: var(--muted); }
.chips-filter button.on .fcount { color: rgba(255,255,255,.7); }

/* Шапка и кнопка «Показать» — видны только в мобильном drawer */
.filters-head { display: none; align-items: center; justify-content: space-between; margin-bottom: 16px; }
.filters-head span { font-size: 18px; font-weight: 700; }
.filters-close { background: none; border: none; font-size: 22px; color: var(--muted); line-height: 1; padding: 4px; }
.filters-done { display: none; width: 100%; height: 50px; margin-top: 18px; border: none; border-radius: 12px; background: var(--ink); color: #fff; font-size: 15px; font-weight: 700; }
.price-row { display: flex; gap: 8px; align-items: center; }
.price-row input { width: 100%; height: 40px; border: 1px solid var(--line); border-radius: 8px; padding: 0 10px; font-size: 13px; outline: none; font-family: inherit; }
.price-row input:focus { border-color: var(--accent); }
/* Заполненное поле цены выделяется само — чёрной рамкой и жирной цифрой,
   как в дизайн-пакете. Кнопки «Применить» больше нет: цена применяется
   по вводу, и рамка отвечает на вопрос «а сработало ли?». */
.price-row input:not(:placeholder-shown) { border-color: var(--ink); font-weight: 600; }
/* «Сбросить» живёт в ряду активных чипов — подчёркнутым словом, как в
   дизайн-пакете. Появляется, когда есть что сбрасывать (два фильтра и больше). */
.chips-reset { background: none; border: none; color: var(--muted); font-size: 13px; text-decoration: underline; text-underline-offset: 3px; cursor: pointer; padding: 6px 4px; }
.chips-reset:hover { color: var(--ink); }

.toolbar { display: flex; align-items: center; justify-content: space-between; margin-bottom: 22px; gap: 16px; flex-wrap: wrap; }
.toolbar h1 { font-family: var(--font-display); font-size: clamp(22px, 3vw, 32px); font-weight: 400; letter-spacing: .02em; }
.toolbar .count { color: var(--muted); font-size: 14px; margin-left: 12px; font-weight: 400; white-space: nowrap; }
.sortsel { height: 42px; border: 1px solid var(--line); border-radius: 8px; padding: 0 34px 0 14px; font-size: 14px; color: var(--ink); outline: none; appearance: none; -webkit-appearance: none; -moz-appearance: none; background: #fff url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath d='M2.5 4.5l3.5 3.5 3.5-3.5' fill='none' stroke='%238a847c' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") no-repeat right 13px center; cursor: pointer; }
.filter-toggle { display: none; }
/* Язычок шторки фильтров существует только в мобильной раскладке. */
.filters-grab { display: none; }
.load-more { text-align: center; margin-top: 44px; }

.breadcrumb { display: flex; gap: 8px; font-size: 13px; color: var(--muted); margin: 22px 0 6px; flex-wrap: wrap; }
.breadcrumb a:hover { color: var(--ink); }
.breadcrumb span { color: var(--muted-2); }

/* Активные фильтры-чипсы */
.active-chips { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 18px; }
.active-chips .c { display: inline-flex; align-items: center; gap: 6px; background: var(--soft-2); border-radius: 8px; padding: 6px 12px; font-size: 13px; }
.active-chips .c b { font-weight: 600; }
.active-chips .c button { background: none; border: none; color: var(--muted); font-size: 14px; line-height: 1; }

/* ============================ КАРТОЧКА ТОВАРА ============================ */
/* Сетка страницы по дизайн-пакету: галерея фиксированной ширины 520,
   информация занимает остальное. Миниатюры — ряд под главным фото. */
.product { display: grid; grid-template-columns: 520px 1fr; gap: 64px; padding-top: 20px; align-items: start; }
.gallery { display: flex; flex-direction: column; gap: 10px; position: sticky; top: 140px; }
.thumbs { display: flex; flex-direction: row; gap: 10px; }
.thumbs button { width: 76px; aspect-ratio: 3/4; border: 1.5px solid transparent; border-radius: 6px; overflow: hidden; background: var(--soft); padding: 0; }
.thumbs button.on { border-color: var(--ink); }
.thumbs img { width: 100%; height: 100%; object-fit: cover; }
.gallery-main { position: relative; aspect-ratio: 3/4; background: var(--soft); border-radius: 8px; overflow: hidden; }
.gallery-main img { width: 100%; height: 100%; object-fit: cover; }
/* Лента кадров: снап по горизонтали, полосу прокрутки прячем — она бы
   перечеркнула фото, а листание и так подсказано точками. */
.gal-track { display: flex; height: 100%; overflow-x: auto; scroll-snap-type: x mandatory; scrollbar-width: none; -ms-overflow-style: none; }
.gal-track::-webkit-scrollbar { display: none; }
.gal-slide { flex: 0 0 100%; height: 100%; scroll-snap-align: center; }
/* Точки и кнопки «поделиться / в избранное» — телефонная история: на десктопе
   кадр выбирают миниатюрами, а делятся адресной строкой. */
.gal-bar { display: none; }
.gal-dots { display: none; position: absolute; left: 0; right: 0; bottom: 12px; justify-content: center; gap: 6px; }
.gal-dots span { width: 6px; height: 6px; border-radius: 100px; background: rgba(16,16,16,.2); transition: width .2s var(--ease), background .2s; }
.gal-dots span.on { width: 18px; background: var(--ink); }

/* Типографика карточки из дизайн-пакета: крупная марка, название вторым
   планом серым. Раньше было наоборот — марка мелким капсом, название
   огромное, — но в ресейле выбирают сначала марку. */
.pd-brand { font-family: var(--font-display); font-size: clamp(22px, 2vw, 26px); font-weight: 400; letter-spacing: .06em; text-transform: uppercase; }
.pd-brand a { border-bottom: 1px solid transparent; transition: border-color .2s; }
.pd-brand a:hover { border-color: var(--ink); }
.pd-name { font-size: clamp(15px, 1.4vw, 17px); font-weight: 400; color: var(--muted); margin: 6px 0 20px; line-height: 1.35; }
.pd-price { display: flex; align-items: baseline; gap: 14px; margin-bottom: 8px; }
.pd-price .now { font-size: clamp(24px, 2.2vw, 28px); font-weight: 700; }
.pd-price .old { font-size: 18px; color: var(--muted-2); text-decoration: line-through; }
.pd-price .disc { background: var(--sale); color: #fff; font-size: 13px; font-weight: 700; padding: 3px 8px; border-radius: 5px; }
.pd-cond { color: var(--muted); font-size: 14px; margin-bottom: 22px; }
.pd-cond b { color: var(--ink); }
/* Кнопки в ряд: «Купить» шире, «В корзину» обводкой 1.5 — дизайн-пакет. */
.pd-cta { display: flex; gap: 10px; margin-bottom: 24px; }
.pd-cta #btnBuy { flex: 2; }
.pd-cta #btnCart { flex: 1.2; border-width: 1.5px; white-space: nowrap; }
/* Иконка держится первой строки текста, а не центра блока: на узком экране
   строка переносится, и центрирование уводило иконку вниз. */
/* Сервисный блок — серая плашка вместо трёх строк с линейками сверху:
   так гарантия и доставка читаются одним предметом, а не тремя сносками. */
.pd-serv { background: var(--soft); border-radius: 8px; padding: 20px 22px; display: flex; flex-direction: column; gap: 16px; margin-bottom: 26px; }
.pd-serv-row { display: grid; grid-template-columns: 20px 1fr; gap: 12px; align-items: start; }
.pd-serv-row svg { width: 20px; height: 20px; margin-top: 1px; color: var(--ink); }
.pd-serv-row b { display: block; font-size: 14px; font-weight: 700; }
.pd-serv-row span { display: block; font-size: 13px; color: var(--muted); line-height: 1.5; margin-top: 3px; }
/* Рамка «Характеристик» — четвёртая линия в той же стопке, что и три линии
   гарантий. Собственный отступ сверху ей не нужен: последняя гарантия уже
   отдаёт свои 13px снизу. С margin-top последний просвет выходил на 24px
   шире трёх предыдущих — ровные линии с одним кривым интервалом. */
.pd-specs { border-top: 1px solid var(--line); padding-top: 22px; }
.pd-specs h3 { font-size: 15px; font-weight: 700; margin-bottom: 14px; }
.pd-specs dl { display: grid; grid-template-columns: 130px 1fr; gap: 10px 16px; font-size: 14px; }
.pd-specs dt { color: var(--muted); }
.pd-desc { margin-top: 24px; font-size: 14.5px; color: #333333; line-height: 1.7; white-space: pre-line; }

/* ============================ ПОДВАЛ ============================ */
footer { background: var(--ink); color: #c9c9c9; padding: 64px 0 32px; margin-top: 40px; }
.foot-grid { display: grid; grid-template-columns: 1.6fr 1fr 1fr 1fr; gap: 40px; }
footer .logo { color: #fff; margin-bottom: 14px; display: inline-block; }
footer .about { font-size: 13.5px; line-height: 1.7; color: #a8a8a8; max-width: 320px; }
.foot-req { font-size: 12px; color: #8a8a8a; line-height: 1.8; margin-top: 16px; }
.foot-col h4 { color: #fff; font-size: 13px; letter-spacing: .08em; text-transform: uppercase; margin-bottom: 16px; }
.foot-col a { display: block; font-size: 13.5px; margin-bottom: 11px; color: #c9c9c9; transition: color .2s; }
/* На чёрном фоне подсветка ссылки — белым: --accent теперь чёрный и здесь
   был бы невидим. */
.foot-col a:hover { color: #fff; }
.store-badges { display: flex; gap: 10px; margin-top: 6px; flex-wrap: wrap; }
.store-badge { display: inline-flex; align-items: center; gap: 8px; background: rgba(255,255,255,.07); border: 1px solid rgba(255,255,255,.16); padding: 10px 14px; border-radius: 10px; font-size: 13px; }
.store-badge small { display: block; font-size: 10px; color: #909090; }
.foot-bottom { border-top: 1px solid rgba(255,255,255,.1); margin-top: 44px; padding-top: 22px; display: flex; justify-content: space-between; flex-wrap: wrap; gap: 12px; font-size: 12.5px; color: #8a8a8a; }

/* ============================ ЮРИДИЧЕСКИЕ ============================ */
.legal { padding: 46px 0 70px; }
.legal .inner { max-width: 820px; }
/* Без цвета ссылку в тексте отличает только подчёркивание. */
.legal .back { font-size: 14px; color: var(--ink); font-weight: 600; margin-bottom: 22px; display: inline-block; text-decoration: underline; text-underline-offset: 3px; }
.legal h1 { font-size: clamp(28px, 4vw, 42px); font-weight: 700; margin-bottom: 8px; }
.legal .updated { color: var(--muted); font-size: 14px; margin-bottom: 34px; }
.legal h2 { font-size: 20px; font-weight: 700; margin: 34px 0 12px; }
.legal p { color: #333333; margin-bottom: 14px; white-space: pre-line; }
.legal .card-box { background: var(--soft); border: 1px solid var(--line); border-radius: 12px; padding: 24px 26px; margin-bottom: 28px; }
.legal h3 { font-size: 15px; font-weight: 700; margin: 18px 0 6px; }
.legal h3:first-child { margin-top: 0; }

/* ===== СТРАНИЦА НЕ НАЙДЕНА ===== */
/* Тупик без выхода раздражает сильнее самой ошибки, поэтому под текстом
   стоят кнопки и ряд разделов: с 404 человек должен уходить в каталог,
   а не закрывать вкладку. */
.notfound { padding: 80px 0 100px; text-align: center; }
.notfound .inner { max-width: 620px; margin: 0 auto; }
.notfound .nf-code { font-family: "Cabin", sans-serif; font-size: clamp(72px, 14vw, 132px); line-height: 1; color: var(--line); letter-spacing: 2px; }
.notfound h1 { font-size: clamp(24px, 3.4vw, 34px); font-weight: 700; margin: 10px 0 12px; }
.notfound p { color: var(--muted); font-size: 15px; margin-bottom: 30px; }
.nf-actions { display: flex; flex-wrap: wrap; gap: 12px; justify-content: center; margin-bottom: 34px; }
.nf-links { display: flex; flex-wrap: wrap; gap: 6px 22px; justify-content: center; font-size: 14px; }
.nf-links a { color: var(--muted); text-decoration: underline; text-underline-offset: 3px; }
.nf-links a:hover { color: var(--ink); }

/* ===== ПОМОЩЬ ===== */
/* Ряд якорей вверху страницы: четыре темы видны сразу, без прокрутки. */
.help-nav { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 30px; }
.help-nav a { border: 1px solid var(--line); border-radius: 999px; padding: 8px 15px; font-size: 13px; font-weight: 600; transition: border-color .2s, background .2s; }
.help-nav a:hover { border-color: var(--ink); background: var(--soft); }

/* Частые вопросы на нативных details/summary: ответ лежит в HTML и виден
   поисковику, даже пока свёрнут. Стрелку рисуем сами — браузерный маркер
   в разных движках выглядит по-разному. */
.faq details { border-bottom: 1px solid var(--line); }
.faq details:first-child { border-top: 1px solid var(--line); }
.faq summary { list-style: none; cursor: pointer; padding: 17px 34px 17px 0; font-size: 15px; font-weight: 600; position: relative; }
.faq summary::-webkit-details-marker { display: none; }
.faq summary::after { content: ""; position: absolute; right: 6px; top: 50%; width: 8px; height: 8px; border-right: 1.6px solid var(--ink); border-bottom: 1.6px solid var(--ink); transform: translateY(-70%) rotate(45deg); transition: transform .2s var(--ease); }
.faq details[open] summary::after { transform: translateY(-30%) rotate(-135deg); }
.faq summary:hover { color: var(--muted); }
/* Кнопка копирования трека: цвет меняется плавно в обе стороны — резкий
   возврат после «Скопировано» заметил владелец в приложении. */
.track-copy { border: none; background: none; padding: 2px 6px; font-size: 12px;
    color: var(--muted); text-decoration: underline; text-underline-offset: 3px;
    cursor: pointer; transition: color .25s var(--ease); }
.track-copy.done { color: #1a9a4e; text-decoration: none; }

/* Чёрным, не серым: ответ читают как основной текст, а не как сноску
   (владелец). Так же в iOS (FAQRow) и Android (LegalTexts). */
.faq details p { font-size: 14px; line-height: 1.65; color: var(--ink); margin: 0; padding: 0 34px 20px 0; }

/* ============================ АДАПТИВ ============================ */
@media (max-width: 1080px) {
    .grid, .grid.g5 { grid-template-columns: repeat(3, 1fr); }
    .product { grid-template-columns: 1fr; gap: 30px; }
    .gallery { position: static; }
    .catalog-layout { grid-template-columns: 1fr; }
    .filters { position: fixed; inset: 0 0 0 auto; width: min(90vw, 360px); background: #fff; z-index: 120; padding: 22px; overflow-y: auto; transform: translateX(100%); transition: transform .3s var(--ease); box-shadow: -10px 0 40px rgba(0,0,0,.15); }
    .filters.open { transform: none; }
    /* В мобильном drawer крупнее пункты — чтобы легко попадать пальцем */
    .filters .check-item { font-size: 15px; padding: 5px 0; }
    .filters .check-box { width: 20px; height: 20px; }
    .filters-head { display: flex; }
    .filters-done { display: block; }
    /* Цвет надписи задаём явно. Без color кнопку красит сам браузер: на
       iPhone <button> без цвета уезжает в системный синий, и на ахроматической
       странице «Фильтры» оказывались единственным цветным словом.
       -webkit-appearance: none — чтобы Safari не подмешивал свой фон и рамку. */
    .filter-toggle {
        display: inline-flex; align-items: center; gap: 7px; height: 42px; padding: 0 16px;
        border: 1px solid var(--line); border-radius: 8px; background: #fff;
        color: var(--ink); font-size: 14px; font-weight: 600;
        -webkit-appearance: none; appearance: none;
    }
    .filter-toggle svg { flex: none; }
    .filter-toggle:active { background: var(--soft); }
    .filters-backdrop { display: none; position: fixed; inset: 0; background: rgba(0,0,0,.28); z-index: 110; }
    .filters-backdrop.show { display: block; }
}
@media (max-width: 900px) {
    .header .wrap { grid-template-columns: auto 1fr auto; height: 64px; gap: 10px; }
    .search { display: none; }
    .header.search-open .search { display: flex; align-items: center; gap: 12px; position: absolute; left: 0; right: 0; top: 100%; padding: 12px var(--pad); background: #fff; border-bottom: 1px solid var(--line); max-width: none; justify-self: stretch; }
    .header.search-open .search input { flex: 1; }
    .header.search-open .search .ic { left: calc(var(--pad) + 18px); }
    .header.search-open .search-close { display: inline-block; background: none; border: none; color: var(--muted); font-size: 14px; white-space: nowrap; }
    .header.search-open .search-panel { left: var(--pad); right: var(--pad); }
    .seg { display: none; }
    .only-mobile { display: grid; }
    .foot-grid { grid-template-columns: 1fr 1fr; }
    .burger { display: block; }
    .logo { font-size: 18px; letter-spacing: .12em; }
    /* Мозаика на планшете и телефоне складывается в столбик: главная плитка
       во всю ширину, две маленькие под ней в ряд. Три плитки в один столбец
       заняли бы весь первый экран и увели товар за сгиб. */
    .mosaic { grid-template-columns: 1fr 1fr; padding-block: 14px 22px; }
    /* padding-right возвращаем обычный: на узком экране заголовок и так
       ограничен max-width, а зарезервированная под снимок треть колонки
       сжала бы текст до двух слов в строке. */
    .mos-big { grid-row: auto; grid-column: 1 / -1; min-height: 260px; padding-right: clamp(26px, 3vw, 44px); }
    .mos-h { font-size: clamp(24px, 6.4vw, 34px); max-width: 62%; }
    /* 150, а не 132: в плитке теперь две строки подписи под снимком, и на
       132 они прижимались к нему вплотную. */
    .mos-tile { min-height: 150px; }
    .mos-txt i { display: none; }
    .mos-ph img { width: 42%; }
    .mos-tile .mos-ph img { width: auto; }
    .mos-cap { max-width: 54%; }
    .picks, .steps { grid-template-columns: 1fr; }
    /* Поиск, избранное и профиль переехали в нижний бар — в шапке они дублируют
       его и превращают её в частокол из пяти кружков. Оставляем логотип,
       гамбургер (в нём выбор пола и категории) и корзину. */
    #mSearch, #favBtn, #acctBtn { display: none; }
}
@media (max-width: 620px) {
    /* На узких экранах поднимаем нижнюю границу бокового отступа,
       чтобы карточки товара не липли к краям (шапка тоже использует --pad). */
    :root { --pad: clamp(20px, 4vw, 56px); }
    .grid, .grid.g5 { grid-template-columns: repeat(2, 1fr); gap: 20px 12px; }
    .foot-grid { grid-template-columns: 1fr; gap: 26px; }
    .pd-specs dl { grid-template-columns: 110px 1fr; }
}

/* ============================ НИЖНИЙ ТАБ-БАР ============================
   На телефоне сайт должен ощущаться приложением, а не страницей: навигация
   уезжает вниз, под большой палец, вместо гамбургера в дальнем верхнем углу.
   На десктопе бар не нужен — там курсор одинаково легко достаёт любой угол. */
.tabbar { display: none; }
@media (max-width: 900px) {
    /* Жёсткая фиксация по горизонтали: страницу нельзя таскать вбок,
       что бы ни вылезло за край. clip, а не hidden — не создаёт
       собственного скролл-контейнера. */
    html, body { overflow-x: clip; }
    /* Поля ввода не мельче 16px: на меньший кегль iOS Safari отвечает
       автоматическим зумом при фокусе, после которого вся страница
       становилась шире экрана и «плавала» влево-вправо. */
    input:not([type=checkbox]):not([type=radio]), select, textarea { font-size: 16px; }
    .f-search input, .price-row input { font-size: 16px; }

    .tabbar {
        display: grid; grid-template-columns: repeat(5, 1fr);
        position: fixed; left: 0; right: 0; bottom: 0; z-index: 130;
        /* Непрозрачный белый: полупрозрачный фон с блюром на длинных страницах
           «просвечивал» карточками товара, и бар переставал читаться как
           отдельный слой. В приложении он тоже сплошной. */
        background: #fff;
        border-top: 1px solid var(--line);
        /* Полоса-индикатор дома на iPhone: контент бара поднимаем над ней. */
        padding-bottom: env(safe-area-inset-bottom, 0px);
    }
    .tab {
        display: flex; flex-direction: column; align-items: center; justify-content: center;
        gap: 3px; height: 54px; color: var(--muted-2); text-decoration: none;
        -webkit-tap-highlight-color: transparent;
    }
    .tab.on { color: var(--ink); }
    .tab:active { opacity: .55; }
    .tab-ic { position: relative; display: block; width: 23px; height: 23px; }
    .tab-ic svg { width: 100%; height: 100%; display: block; }
    .tab-lb { font-size: 10.5px; letter-spacing: .01em; line-height: 1; }
    .tab-badge {
        position: absolute; top: -5px; right: -7px; min-width: 15px; height: 15px;
        padding: 0 4px; border-radius: 100px; background: var(--accent); color: #fff;
        font-size: 9.5px; font-weight: 700; line-height: 15px; text-align: center;
    }
    /* Футер не должен уезжать под бар — освобождаем ему высоту в конце страницы. */
    body.has-tabbar { padding-bottom: calc(54px + env(safe-area-inset-bottom, 0px)); }

    /* --- Карточка товара по-приложенчески --- */
    /* Фото во всю ширину экрана: поля --pad гасим отрицательным margin.
       Миниатюр в приложении нет — фото листают пальцем, а положение показывают
       точки под кадром. Ряд миниатюр на телефоне только съедал бы высоту и
       дублировал бы то, что и так делает свайп. */
    .gallery { grid-template-columns: 1fr; gap: 0; margin-inline: calc(var(--pad) * -1); }
    .thumbs { display: none; }
    /* Фото вписывается целиком на белом фоне, а не обрезается по 3:4: в
       приложении .scaledToFit, и обувь с сумками там видно с полями по бокам,
       а не подрезанными сверху и снизу. */
    .gallery-main { border-radius: 0; aspect-ratio: auto; height: 420px; background: #fff; }
    .gallery-main img { object-fit: contain; }
    .gal-bar { display: flex; align-items: center; gap: 10px; height: 44px; padding: 0 var(--pad); }
    .gal-dots { display: flex; position: static; justify-content: flex-start; flex: 1; }
    .gal-acts { display: flex; gap: 4px; margin-left: auto; }
    .gal-act {
        display: grid; place-items: center; width: 36px; height: 36px; padding: 0;
        border: none; border-radius: 100px; background: rgba(16,16,16,.06);
        color: var(--ink); font-size: 17px; line-height: 1;
    }
    .gal-act svg { width: 17px; height: 17px; }
    .gal-act.fav.on { color: var(--sale); }
    .gal-act.fav.on svg path { fill: currentColor; }
    .gal-act.done { background: rgba(16,16,16,.16); }

    /* «Купить» всегда под пальцем, а не в середине длинной страницы. */
    /* Без линейки над кнопками — решение владельца: белой панели и тени
       кнопок достаточно, дивайдер только дробил экран. */
    body.has-pdcta .pd-cta {
        position: fixed; z-index: 125; left: 0; right: 0;
        bottom: calc(54px + env(safe-area-inset-bottom, 0px));
        flex-direction: row; gap: 10px; margin: 0;
        padding: 10px var(--pad); background: #fff;
    }
    body.has-pdcta .pd-cta .btn { flex: 1; }
    /* Порядок кнопок как в приложении: «В корзину» слева, «Купить» справа —
       под большим пальцем оказывается действие, которое ведёт к оплате. */
    body.has-pdcta .pd-cta #btnCart { order: 1; }
    body.has-pdcta .pd-cta #btnBuy  { order: 2; }
    body.has-pdcta { padding-bottom: calc(54px + 63px + env(safe-area-inset-bottom, 0px)); }

    /* --- Фильтры: шторка снизу вместо ящика сбоку --- */
    /* Боковой ящик — жест десктопа. На телефоне то же содержимое естественнее
       поднимается снизу: движение совпадает с направлением большого пальца,
       и шторка не закрывает экран целиком — видно, что под ней каталог. */
    .filters {
        inset: auto 0 0 0; width: auto; max-height: 86vh; z-index: 140;
        border-radius: 18px 18px 0 0; transform: translateY(100%);
        padding: 18px var(--pad) calc(18px + env(safe-area-inset-bottom, 0px));
        box-shadow: 0 -12px 40px rgba(0,0,0,.18);
    }
    .filters.open { transform: none; }
    /* Прокрутка списка фильтров не должна утягивать за собой страницу под шторкой. */
    .filters { overscroll-behavior: contain; }
    .filters-backdrop { z-index: 135; }
    /* Язычок. Полоска узкая, а зона касания — во всю ширину шторки и с запасом
       по высоте: пальцем в четыре пикселя не попадают. Отрицательные поля
       компенсируют внутренние отступы шторки, чтобы язычок дотягивался до краёв.
       touch-action: none — иначе первое же движение вниз браузер отдаёт
       прокрутке и до нашего обработчика жест не доходит. */
    .filters-grab {
        display: block; margin: -18px calc(var(--pad) * -1) 4px;
        padding: 12px 0 10px; touch-action: none; cursor: grab;
        position: sticky; top: -18px; z-index: 3; background: #fff;
    }
    .filters-grab span {
        display: block; width: 38px; height: 4px; margin: 0 auto;
        border-radius: 100px; background: var(--soft-2);
    }
    .filters-done { position: sticky; bottom: 0; }
}

/* ============================ ОБОЛОЧКА ПРИЛОЖЕНИЯ (ТЕЛЕФОН) ============================
   Ниже — не «адаптив десктопа», а повторение экранов iOS-приложения: та же
   шапка, тот же серый поиск, те же вкладки пола, та же анатомия карточки.
   Человек, который сначала зашёл с телефона, а потом поставил приложение,
   должен попасть в знакомый интерфейс, а не учить его заново.

   Правила намеренно стоят после общего мобильного блока: то, что там
   настраивалось для «сайта на телефоне» (шапка с логотипом, футер, витрина),
   здесь отключается целиком, а не подпирается !important. */
.appbar, .ab-tabs { display: none; }
@media (max-width: 900px) {
    /* Десктопная шапка, панель категорий и подвал в приложении не существуют.
       Реквизиты и правовые документы из подвала переехали на экран «Профиль». */
    .catnav, footer { display: none; }
    .header { display: none; }
    /* Единственное, ради чего шапка остаётся в разметке, — оверлей поиска.
       Он всплывает поверх экрана, как search в приложении. */
    .header.search-open {
        display: block; position: fixed; inset: 0 0 auto 0; z-index: 160;
        background: #fff; border-bottom: 1px solid var(--line);
        padding-top: env(safe-area-inset-top, 0px);
    }
    .header.search-open .wrap { display: block; height: auto; padding: 10px var(--pad); }
    .header.search-open .burger,
    .header.search-open .logo,
    .header.search-open .h-actions { display: none; }
    .header.search-open .search {
        position: relative; top: auto; left: auto; right: auto;
        display: flex; align-items: center; gap: 12px; padding: 0;
        border: none; max-width: none;
    }
    .header.search-open .search .ic { left: 16px; }
    .header.search-open .search input { flex: 1; height: 42px; border-radius: 8px; padding-left: 42px; }

    /* Шапка без нижней линейки. Разделитель под ней только дробил экран:
       заголовок, поиск и вкладки пола и так читаются как один блок, а линия
       под ними на каждом экране складывалась в «бесконечные девайдеры».
       В приложении шапка тоже сливается с фоном. */
    .appbar {
        display: block; position: fixed; top: 0; left: 0; right: 0; z-index: 90;
        background: #fff;
        padding: env(safe-area-inset-top, 0px) var(--pad) 0;
    }
    /* Шапка вынута из потока, поэтому место под неё держит контейнер. Высоту
       ему проставляет initAppbarAutoHide() по факту — здесь только запасное
       значение на случай, если скрипт не дошёл: 150 = ряд заголовка 52 плюс
       строка поиска 52 плюс слот сегментов 46. Из потока шапка вынута ради
       сворачивания: в потоке оно дёргало бы страницу вверх на свою высоту.

       display: block обязателен: на широком экране контейнер объявлен
       display: contents (иначе он становится containing block и десктопная
       шапка отлипает при прокрутке), а у такого элемента нет собственной
       коробки — min-height ему нечему задать. */
    #app-header { display: block; min-height: var(--appbar-h, 150px); }
    /* Шапка постоянна: логотип с корзиной, поиск и сегменты висят при любой
       прокрутке — сворачивание по жесту убрано по решению владельца.
       Линейки под шапкой нет тоже по его решению: белый слой отделяют
       только сами сегменты. */
    /* min-height, а не height: длинному названию раздела нужна вторая строка,
       и шапка должна под неё подрасти, а не обрезать текст. */
    .ab-row { display: grid; grid-template-columns: 30px 1fr 30px; align-items: center; min-height: 44px; padding: 4px 0; }
    .ab-ic {
        position: relative; display: grid; place-items: center;
        width: 30px; height: 30px; color: var(--ink);
        background: none; border: none; -webkit-tap-highlight-color: transparent;
    }
    .ab-ic svg { width: 21px; height: 21px; display: block; }
    .ab-ic:active { opacity: .5; }
    .ab-badge {
        position: absolute; top: -1px; right: -3px; min-width: 15px; height: 15px;
        padding: 0 4px; border-radius: 100px; background: var(--accent); color: #fff;
        font-size: 9.5px; font-weight: 700; line-height: 15px; text-align: center;
    }
    /* Название раздела показываем целиком. Многоточие («Ювелирные укр…»)
       экономило один ряд пикселей и отнимало смысл: человек не понимает,
       где он находится. Длинное название переносится на вторую строку. */
    .ab-title {
        text-align: center; font-size: 16px; font-weight: 600; letter-spacing: -.01em;
        line-height: 1.2; overflow-wrap: anywhere;
        display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
    }
    /* Название магазина на главной — крупнее, вместо логотипа. */
    .ab-title.ab-brand { font-size: 19px; font-weight: 700; letter-spacing: .09em; }

    .ab-search {
        display: flex; align-items: center; gap: 8px; width: 100%; height: 42px;
        margin-bottom: 10px; padding: 0 12px; border: none; border-radius: 8px;
        background: var(--soft); color: var(--muted); font-size: 15px; text-align: left;
    }
    .ab-search-ic svg { width: 17px; height: 17px; display: block; }
    .ab-search:active { background: var(--soft-2); }
    /* На «Брендах» то же серое поле — но живое: фильтрует уже загруженный
       список, поэтому внутри настоящий input, а не кнопка-заглушка. */
    .ab-filter input {
        flex: 1; min-width: 0; height: 100%; padding: 0;
        border: none; background: none; outline: none;
        /* 16px, не наследованные 15: меньший кегль включает автозум iOS. */
        font: inherit; font-size: 16px; color: var(--ink);
    }
    .ab-filter input::placeholder { color: var(--muted); }

    /* Слот «Женское | Мужское» — отдельный закреплённый слой ПОД шапкой и
       НИЖЕ липкого сегмента ленты по z: доехав сюда, «Новое | Скидки»
       прилипает ровно на месте пола и закрывает его собой. */
    .ab-tabs {
        display: block;
        position: fixed; left: 0; right: 0; top: var(--bar-h, 104px);
        height: 46px; z-index: 40; background: #fff;
    }
    .ab-tabs .ab-gender {
        position: absolute; top: 0; bottom: 0;
        left: var(--pad); right: var(--pad);
    }

    .ab-gender { display: flex; }
    /* Стиль ЦУМа по референсу владельца: разреженный капс, ОБА пола одним
       чёрным («второй гендер тоже такого же цвета»), выбранный отличается
       только чертой снизу. Та же вёрстка в iOS и Android. */
    .ab-gender button {
        flex: 1; background: none; border: none; padding: 0;
        font-size: 13px; color: var(--ink); position: relative;
        text-transform: uppercase; letter-spacing: .14em; font-weight: 400;
    }
    /* Подчёркивание активной вкладки — ровно как в приложении: 2px, во всю
       ширину вкладки, а не «пилюля» на сером фоне. */
    .ab-gender button.on { color: var(--ink); }
    .ab-gender button.on::after {
        content: ""; position: absolute; left: 0; right: 0; bottom: 0;
        height: 2px; background: var(--ink);
    }

    /* --- Главный экран --- */
    .app-home { padding-top: 14px; }
    .app-home .grid.g2 { grid-template-columns: repeat(2, 1fr); gap: 20px 12px; padding: 0 var(--pad); }
    .app-more { min-height: 40px; padding: 18px 0 4px; text-align: center; font-size: 13px; color: var(--muted-2); }

    /* Заголовок раздела. Ссылка справа мелкая и серая — она подсказка, а не
       вторая кнопка: основное действие на телефоне это сам список под ней. */
    .ah-head { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; padding: 0 var(--pad); margin: 26px 0 12px; }
    .ah-head h2 { font-size: 17px; font-weight: 700; letter-spacing: -.01em; }
    .ah-head a { font-size: 13px; color: var(--muted); }

    /* Переключатель ленты «Новое | Скидки» — как у ЦУМ Collect: серая
       подложка, выбранный режим — белая плашка. Живёт в слоте шапки
       (.ab-tabs) поверх «Женское | Мужское» — оформление слоя задано там. */
    .feed-tabs {
        display: flex; gap: 4px; padding: 4px;
        background: var(--soft-2); border-radius: 10px;
    }
    .feed-tabs button {
        flex: 1; height: 38px; border: none; border-radius: 8px;
        background: transparent; font-size: 14px; font-weight: 600;
        color: var(--muted); transition: background .2s, color .2s;
    }
    .feed-tabs button.on { background: #fff; color: var(--ink); box-shadow: 0 1px 3px rgba(0,0,0,.08); }
    /* Сегмент ленты: стоит перед каталогом, виден сразу, а доехав до
       шапки — прилипает ровно на место «Женское | Мужское» (top слота) и
       закрывает его собой: z-index выше слота (40), ниже шапки (90).
       Чистая вёрстка, без наблюдателей — подмена скриптом опаздывала за
       инерционной прокруткой iPhone. */
    .feed-tabs-flow {
        position: sticky; top: var(--bar-h, 104px); z-index: 50;
        margin: 20px var(--pad) 12px;
    }
    /* Белое паспарту вокруг прилипшего сегмента: без него из-под его
       скруглённых углов выглядывало подчёркивание «Женское | Мужское» —
       снизу и по бокам. В потоке подложка невидима: белое на белом. */
    .feed-tabs-flow::before {
        content: ""; position: absolute; inset: -8px calc(var(--pad) * -1);
        background: #fff; z-index: -1;
    }

    /* Полноэкранный поиск — как в приложении: поле с «Отменить» и живые
       подсказки. Открывается кнопкой «Поиск по каталогу» из шапки. */
    .msearch {
        position: fixed; inset: 0; z-index: 200; background: #fff;
        padding: calc(env(safe-area-inset-top, 0px) + 10px) var(--pad) 0;
        display: none;
    }
    .msearch.show { display: block; }
    .ms-bar { display: flex; align-items: center; gap: 12px; }
    .ms-field {
        flex: 1; display: flex; align-items: center; gap: 8px; height: 42px;
        padding: 0 12px; border-radius: 8px; background: var(--soft);
    }
    .ms-ic svg { width: 17px; height: 17px; display: block; color: var(--muted); }
    .ms-field input {
        flex: 1; min-width: 0; border: none; background: none; outline: none;
        /* 16px: меньший кегль включает автозум iOS при фокусе. */
        font: inherit; font-size: 16px; color: var(--ink);
    }
    .ms-cancel { border: none; background: none; padding: 0; font-size: 15px; color: var(--ink); }
    .ms-list { padding-top: 10px; overflow-y: auto; max-height: calc(100% - 70px); }
    .ms-hint { padding: 28px 4px; text-align: center; font-size: 13px; color: var(--muted); }
    .ms-item {
        display: flex; align-items: center; gap: 12px; width: 100%;
        padding: 12px 4px; border: none; background: none; text-align: left;
    }
    .ms-item:active { opacity: .6; }
    .ms-item .ms-txt { display: flex; flex-direction: column; gap: 1px; font-size: 15px; color: var(--ink); }
    .ms-item small { font-size: 12px; color: var(--muted); }
    .ah-head-grid { margin-bottom: 14px; }

    /* Кружки категорий. Ряд уезжает за правый край экрана — обрезанный
       шестой кружок сам показывает, что список листается. */
    .ah-cats { display: flex; gap: 14px; overflow-x: auto; scrollbar-width: none; padding: 4px var(--pad) 2px; margin-bottom: 4px; }
    /* Кураторские полки на телефоне: заголовок Tenor по центру, рельса с
       боковым кантом страницы, карточки поуже десктопных. */
    .ah-sec-t { font-family: var(--font-display); font-size: 18px; letter-spacing: .04em; text-align: center; margin: 20px 0 10px; }
    /* Ровно ДВЕ карточки на экран с чётким снапом: полторы карточки с
       подписью «в дырке» читались как криво свёрстанные (владелец). */
    .app-trail { grid-auto-columns: calc(50vw - var(--pad) - 7px); gap: 14px; padding: 0 var(--pad) 4px; scroll-snap-type: x mandatory; }
    .app-trail .tcard-brand { font-size: 14px; }
    .app-trail .tcard-price { font-size: 12px; }
    .app-trail .tcard-allbox { padding: 20px 16px; font-size: 11px; }
    .ah-cats::-webkit-scrollbar { display: none; }
    /* Категории — ПРЯМОУГОЛЬНЫЕ плитки, не кружки: «в кружках не по
       стилю, ещё и криво с фото» (владелец; референс — категории ЦУМа).
       Предметка 3/4 на нейтральном поле, подпись разреженным капсом ПОД
       плиткой. Те же плитки в iOS и Android. */
    .ah-cat { flex: none; width: 104px; display: flex; flex-direction: column; align-items: center; gap: 7px; }
    .ah-cat-ph { position: relative; display: grid; place-items: center; width: 104px; height: 132px; border-radius: 10px; background: var(--soft); overflow: hidden; color: var(--muted); }
    .ah-cat-ph svg { width: 26px; height: 26px; }
    /* Фотография ложится поверх иконки. Не загрузилась — под ней уже лежит
       иконка, и плитка не остаётся пустой. */
    .ah-cat-ph img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
    .ah-cat span { font-family: var(--font-display); font-size: 10px; letter-spacing: .1em; text-transform: uppercase; line-height: 1.3; text-align: center; color: var(--ink); overflow-wrap: anywhere; }
    .ah-cat:active .ah-cat-ph { opacity: .6; }

    /* Ряд «пилюль»: бюджет, бренды. Один и тот же элемент — чтобы два ряда
       на одном экране не выглядели как два разных интерфейса. */
    .ah-row { display: flex; gap: 8px; overflow-x: auto; scrollbar-width: none; padding: 2px var(--pad); }
    .ah-row::-webkit-scrollbar { display: none; }
    .ah-chip { flex: none; padding: 9px 15px; border: 1px solid var(--line); border-radius: 8px; font-size: 13.5px; color: var(--ink); white-space: nowrap; }
    .ah-chip:active { background: var(--soft); }
    .ah-sec { margin-top: 6px; }
    .ah-sec .ah-head { margin-bottom: 10px; }

    /* --- Витрина-мозаика на телефоне ---
       Тот же приём, что на десктопе, сложенный под палец: широкая тёмная
       плитка и две узкие под ней. Три повода зайти в каталог вместо одного,
       и все три помещаются в первый экран вместе с шапкой.

       Скругление 14px — как у баннеров и карточек товара, чтобы витрина
       не выглядела вставкой из другого интерфейса. */
    .amos { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; padding: 0 var(--pad); }

    /* Плитки собраны сеткой, а не абсолютным слоем поверх флекса.
       Разница не косметическая — она снимает целый класс багов.

       Раньше снимок с подписью «марка + цена» лежал отдельным слоем
       (position: absolute, z-index: -1) поверх фона плитки, и высоту плитке
       приходилось считать руками: 244 = 18 поля + 110 снимок + 32 подпись +
       82 нижний блок. Любое изменение — марка длиннее, цена в шесть знаков,
       заголовок в три строки, другой системный шрифт — ломало эту сумму, и
       подпись налезала на кнопку или на пояснение раздела. Так и всплыл баг
       «везде, где марка и цена».

       Теперь снимок и подпись — обычная правая колонка в потоке. Плитка
       считает свою высоту сама, подпись физически не может ни на что налезть,
       а пиксельных бюджетов в этом блоке больше нет. */
    .amos-big, .amos-tile {
        position: relative; overflow: hidden; border-radius: 14px;
        display: grid; grid-template-columns: 1fr auto;
        gap: 4px 12px; align-content: start;
    }
    /* Заголовок сверху, кнопка по низу левой колонки, снимок с подписью — во
       всю высоту правой (grid-row: 1 / -1). Обещания магазина из плитки уехали
       в полку .svc под витриной: серый 10.5px по чёрному почти не читался, а
       ссылкой на разбор в /help внутри плитки-ссылки его было не сделать. */
    .amos-big {
        grid-column: 1 / -1; background: var(--ink); color: #fff;
        grid-template-areas: "eyebrow photo" "title photo" "btn photo";
        grid-template-rows: auto auto 1fr;
        padding: 18px; min-height: 190px;
    }
    .amos-eyebrow { grid-area: eyebrow; font-size: 10px; letter-spacing: .22em; text-transform: uppercase; opacity: .6; }
    /* Кегль плавающий, а не фиксированный: при фиксированных 22px строка
       «Подлинные вещи» (192px в Inter) не влезала в колонку на экране 320pt и
       заголовок ломался на три строки посередине словосочетания. Ширину
       колонки теперь держит сама сетка, max-width не нужен.
       Именно > b: внутри плитки есть ещё один <b> — марка в подписи к
       снимку, и без прямого потомка она получала бы отступы заголовка. */
    .amos-big > b { grid-area: title; font-size: clamp(18px, 5.6vw, 24px); font-weight: 700; line-height: 1.14; letter-spacing: -.02em; margin: 8px 0 14px; }
    /* align-self: end прижимает кнопку к низу своей строки, а 1fr у этой
       строки отдаёт ей весь запас высоты плитки: заголовок остаётся наверху,
       кнопка — у нижнего края, между ними работает фотография. */
    .amos-btn { grid-area: btn; align-self: end; justify-self: start; background: #fff; color: var(--ink); padding: 10px 18px; border-radius: 10px; font-size: 13px; font-weight: 600; }
    .amos-big:active { opacity: .9; }

    /* Узкая плитка — один столбец, а не два: при ширине плитки около 170px на
       левую колонку рядом со снимком оставалось 70px, и «До 30 000 ₽» (88px в
       Inter Bold) туда не влезало ни при какой вёрстке — именно это и рвало
       заголовок на «До» и «30 000 ₽». Теперь заголовку отдана вся ширина, а
       снимок стоит под ним в правом нижнем углу.

       min-height 150 — не бюджет, а заглушка от прыжка: пока снимок не
       приехал, .amos-ph:empty схлопнут, и без неё плитка сначала была бы
       вдвое ниже. 150 = 37 текст + 4 просвет + 83 снимок + 26 поля. */
    .amos-tile {
        background: var(--soft); padding: 13px; min-height: 150px;
        grid-template-columns: 1fr; grid-template-rows: auto 1fr;
    }
    .amos-tile.amos-warm { background: #eee9e2; }
    .amos-tile:active { opacity: .8; }
    .amos-txt b { display: block; font-size: 15px; font-weight: 700; letter-spacing: -.01em; }
    .amos-txt i { display: block; font-style: normal; font-size: 11.5px; line-height: 1.3; color: var(--muted); margin-top: 4px; }

    /* Правая колонка: снимок, под ним марка и цена.
       Фон у карточки белый намеренно: фотографии приезжают с вырезанным фоном,
       и так это читается как вещь на витрине, а не как дырка в макете. */
    .amos-ph { display: flex; flex-direction: column; gap: 5px; }
    .amos-ph:empty { display: none; }
    /* Снимок сверху, марка с ценой — по низу колонки, вровень с кнопкой
       «Смотреть каталог» слева (правка владельца: раньше подпись висела
       сразу под снимком и была выше кнопки). */
    .amos-big .amos-ph {
        grid-area: photo; width: 104px;
        height: 100%; justify-content: space-between;
    }
    /* Снимки прижаты к правому нижнему углу плитки: сверху над ними —
       заголовок раздела во всю ширину. Снимков два (с одним слева оставалось
       пустое поле): ряд собирает .amos-phrow, пустой слот схлопнут через
       .amos-ph:empty, и плитка с одним снимком выглядит как раньше. */
    /* Снимки разведены по краям (просьба владельца: одинаковые поля слева
       и справа), а не собраны в правом углу. */
    /* Снимки разведены по краям плитки, но не дальше 160px: на телефоне
       в альбомной ориентации плитка вдвое шире, и «по краям» превращалось
       в две картинки на разных концах с дырой посередине. */
    .amos-phrow {
        display: flex; justify-content: space-between; align-self: end;
        width: 100%; max-width: 160px; margin-left: auto;
    }
    .amos-tile .amos-ph { width: 66px; }

    /* Квадрат, а не 3:4, и у большой плитки тоже — решение владельца. */
    .amos-ph img {
        width: 100%; aspect-ratio: 1 / 1; object-fit: contain;
        background: #fff; border-radius: 8px;
    }
    /* В большой плитке снимок тянется на всю высоту колонки, а не стоит
       квадратом: иначе между ним и подписью зияла дыра, ведь подпись
       прижата к низу, вровень с кнопкой (правка владельца). */
    .amos-big .amos-ph img { flex: 1; aspect-ratio: auto; min-height: 0; }
    /* Подпись в потоке под снимком: ни position, ни max-width, ни пиксельного
       запаса — ширину задаёт колонка, и обрезать текст многоточием приходится
       только если марка длиннее самого снимка. */
    .amos-ph .mos-cap { position: static; max-width: none; gap: 2px; text-align: right; }
    .amos-ph .mos-cap em { display: none; }
    .amos-ph .mos-cap b { font-size: 10.5px; letter-spacing: .1em; }
    .amos-ph .mos-cap i { font-size: 12px; }
    .amos-big .mos-cap b { color: #fff; }
    .amos-big .mos-cap i { color: rgba(255, 255, 255, .62); }
    /* На узких плитках подписи нет вовсе — и это не экономия места.
       Плитка ведёт в подборку («Новинки», «До 30 000 ₽»), а не в тот товар,
       чью марку и цену показывала подпись: человек читал «UGG 18 000 ₽»,
       нажимал и попадал в список из сорока вещей. Фотография как образец
       раздела работает, конкретная цена под ней — обманывает. Живая марка с
       ценой осталась там, где нажатие действительно ведёт в этот товар, —
       на большой плитке. */
    .amos-tile .mos-cap { display: none; }
    /* Раз подписи нет, снимок на узкой плитке перестаёт быть отдельной
       ссылкой: вся плитка — одно нажатие в подборку. */
    .amos-tile .amos-ph img { pointer-events: none; }

    /* --- Полка сервиса на телефоне ---
       Та же полка, что на широком экране, но лентой: четыре карточки в столбик
       заняли бы весь второй экран, а обрезанная у правого края лента сама
       показывает, что её листают. Приём тот же, что у кружков категорий и
       «пилюль» бюджета ниже, — экран не должен выглядеть как склейка из
       четырёх разных интерфейсов. */
    .svc {
        display: flex; gap: 8px; overflow-x: auto; scrollbar-width: none;
        grid-template-columns: none; border-top: none;
        padding: 12px var(--pad) 2px;
    }
    .svc::-webkit-scrollbar { display: none; }
    .svc-i {
        flex: none; width: 62%; max-width: 260px;
        background: var(--soft); border-radius: 14px; padding: 13px;
        grid-template-columns: 1fr; grid-template-areas: "ic" "t" "d"; gap: 8px 0;
    }
    .svc-i:active { opacity: .8; }
    .svc-ic { width: 32px; height: 32px; background: #fff; }
    .svc-ic svg { width: 18px; height: 18px; }
    .svc-i b { font-size: 13.5px; }
    .svc-i i { font-size: 11.5px; line-height: 1.4; }

    /* Баннеров может не быть вовсе — пустой блок не должен оставлять отступ. */
    .banners:empty { display: none; }

    /* Карусель баннеров: снап-лента, точки-«капсулы» под ней. */
    .banners { position: relative; margin-bottom: 18px; }
    .bn-track { display: flex; overflow-x: auto; scroll-snap-type: x mandatory; scrollbar-width: none; }
    .bn-track::-webkit-scrollbar { display: none; }
    .bn-slide {
        position: relative; flex: 0 0 100%; scroll-snap-align: center;
        height: 170px; border-radius: 14px; overflow: hidden; background: var(--soft);
    }
    /* Внутренние поля ленты делаем отступом самого слайда, иначе снап
       «прилипает» не к краю картинки, а к краю экрана. */
    .bn-slide { margin: 0 var(--pad); flex-basis: calc(100% - var(--pad) * 2); }
    .bn-slide img { width: 100%; height: 100%; object-fit: cover; }
    .bn-txt {
        position: absolute; inset: auto 0 0 0; padding: 16px;
        background: linear-gradient(to top, rgba(0,0,0,.55), transparent);
        color: #fff; display: block;
    }
    .bn-txt b { display: block; font-size: 18px; font-weight: 600; }
    .bn-txt i { display: block; font-size: 13px; font-style: normal; opacity: .9; margin-top: 2px; }
    .bn-dots { display: flex; justify-content: center; gap: 6px; margin-top: 10px; }
    .bn-dots span { width: 6px; height: 6px; border-radius: 100px; background: var(--soft-2); transition: width .2s var(--ease), background .2s; }
    .bn-dots span.on { width: 18px; background: var(--ink); }

    /* --- Карточка товара как в приложении --- */
    /* БРЕНД → категория → размер → цена. Название товара и состояние
       (десктопные строки) уступают им место: в две колонки на 375 точек
       читается только то, по чему выбирают. */
    .pcard .ph { border-radius: 10px; aspect-ratio: 165 / 240; }

    /* На телефоне кружок поменьше: карточка вдвое уже, и 34 точки съедали
       заметный кусок фотографии. */
    .pcard .fav { width: 30px; height: 30px; top: 6px; right: 6px; font-size: 15px; }
    /* Карточка как на десктопе и в приложении: марка, название (ровно две
       строки места — карточки в ряду стоят вровень при любой длине),
       цена, состояние. Раньше здесь вместо названия была категория без
       ограничения высоты, и соседние карточки разъезжались по вертикали. */
    .pcard .p-brand { font-size: 13px; font-weight: 700; text-transform: uppercase; margin: 8px 0 2px; }
    .pcard .p-price { gap: 6px; margin-top: 4px; flex-wrap: wrap; }
    .pcard .p-price .now { font-size: 16px; font-weight: 700; }
    .pcard .p-price .old { font-size: 13px; }


    /* Хлебные крошки и большие заголовки страниц — приём сайта. В приложении
       за «где я нахожусь» отвечает шапка экрана, дублировать её незачем. */
    .desk-only, .breadcrumb { display: none; }

    /* --- Бренды: боковой указатель букв --- */
    .bl-index {
        display: flex; flex-direction: column; align-items: center;
        position: fixed; right: 2px; z-index: 80;
        top: 50%; transform: translateY(-50%);
    }
    .bl-index span {
        width: 22px; height: 17px; line-height: 17px; text-align: center;
        font-size: 10.5px; color: var(--soft-2);
    }
    .bl-index span.on { color: var(--ink); font-weight: 600; }
    .brand-list { padding-right: 24px; }
    /* Буква прилипает под шапку экрана, а не под верх окна. */
    .bl-letter { top: 44px; }

    /* --- Профиль --- */
    .pr-guest { padding-top: 18px; }

    /* --- Каталог: плитки разделов и список подкатегорий --- */
    /* Название страницы уже стоит в шапке экрана — второй раз крупным
       заголовком его не повторяем, как и в приложении. */
    .toolbar h1 { display: none; }
    /* Панель над выдачей должна занимать одну-две компактные строки: каждый
       лишний пиксель здесь — это пиксель, на который товар уехал под сгиб. */
    .toolbar { padding-top: 10px; margin-bottom: 12px; gap: 8px; }
    .toolbar .count { font-size: 12.5px; }
    .filter-toggle, .sortsel { height: 38px; font-size: 13px; }
    .active-chips { margin-bottom: 12px; }

    /* Разделы каталога — лента широких плиток в духе ЦУМ Collect (выбор
       владельца, «каталог А»): фото во всю плитку одна под другой, подпись
       капслоком с широкой разрядкой у левого нижнего края. Читаемость
       подписи на любом снимке даёт нижний градиент, а не плашка. */
    .app-cats { display: flex; flex-direction: column; gap: 10px; padding: 16px 0; }
    .ac-card { position: relative; display: block; height: 150px; border-radius: 10px; overflow: hidden; background: var(--soft); }
    .ac-ph { position: absolute; inset: 0; display: grid; place-items: center; color: #6b6b6b;
             background: linear-gradient(135deg, #f4f4f4 0%, #e9e9e9 100%); }
    .ac-ph svg { width: 26%; height: 26%; }
    .ac-ph .cat-img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
    /* Градиент — над фотографией, под подписью. Без него белый капс тонет
       на светлых снимках, а именно светлого фона у нас большинство. */
    .ac-card::after {
        content: ""; position: absolute; inset: 0; z-index: 1; pointer-events: none;
        background: linear-gradient(to top, rgba(0,0,0,.55), transparent 52%);
    }
    /* Название показываем целиком: «Ювелирные укр…» — это не раздел, а
       загадка. Длинному названию сжимаем кегль и разрядку (класс .long
       ставит JS), чтобы строка не ломалась и плитки оставались ровными. */
    .ac-name {
        position: absolute; left: 14px; right: 14px; bottom: 12px; z-index: 2;
        font-size: 13px; font-weight: 600; color: #fff;
        text-transform: uppercase; letter-spacing: .24em;
        line-height: 1.3; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
        text-shadow: 0 1px 10px rgba(0, 0, 0, .55);
    }
    .ac-name.long { font-size: 11px; letter-spacing: .16em; }
    .app-rows { padding-top: 6px; }
    .ar-row {
        display: flex; align-items: center; justify-content: space-between;
        min-height: 52px; border-bottom: 1px solid var(--line);
        font-size: 15px; color: var(--ink);
    }
    .ar-row:first-child { font-weight: 600; }
    .ar-chev { color: var(--muted-2); font-size: 20px; line-height: 1; }
}
@media (min-width: 901px) {
    /* На десктопе алфавит виден целиком, поэтому список брендов раскладываем
       в колонки, а не тянем одной лентой на четыре экрана. */
    .brand-list { columns: 3; column-gap: 40px; }
    .bl-group { break-inside: avoid; }
    /* Боковой указатель букв — приём телефона: на десктопе он не нужен,
       весь алфавит и так на экране. */
    .bl-index { display: none; }
}

/* ============================ БРЕНДЫ (СПИСОК ПО АЛФАВИТУ) ============================
   Один список на оба макета: на телефоне он занимает всю ширину и получает
   боковой указатель букв, на десктопе раскладывается в несколько колонок. */
.brand-list { position: relative; }
.bl-letter {
    position: sticky; top: 0; z-index: 2; background: #fff;
    padding: 12px 0 8px; font-size: 15px; font-weight: 700;
}
.bl-row {
    display: flex; align-items: center; gap: 10px;
    min-height: 44px; padding: 6px 0; border-bottom: 1px solid var(--line);
    font-size: 15px; color: var(--ink);
}
/* Ссылка растянута на всю строку: на телефоне по названию марки попадают
   пальцем, а не курсором — узкая цель здесь дорого стоит. */
.bl-row a { flex: 1; display: flex; align-items: center; min-height: 44px; color: inherit; }
.bl-row a:hover { opacity: .55; }
.bl-row .mb-star { flex: none; font-size: 17px; }
.bl-chev { color: var(--muted-2); font-size: 20px; line-height: 1; }

/* ============================ ПРОФИЛЬ ============================ */
.pr-head { display: flex; align-items: center; gap: 14px; padding: 18px 0 22px; }
.pr-avatar {
    width: 52px; height: 52px; border-radius: 50%; background: var(--soft);
    display: grid; place-items: center; color: var(--muted); flex: none;
}
.pr-avatar svg { width: 26px; height: 26px; }
.pr-phone { font-size: 17px; font-weight: 700; }
.pr-sub { font-size: 13px; color: var(--muted); margin-top: 2px; }

/* Без верхней линейки: строки и так разделены снизу, а линия над первой
   строкой ложилась вплотную под шапку и читалась вторым разделителем. */
.pr-list { margin-bottom: 22px; }
.pr-row {
    display: flex; align-items: center; gap: 14px; min-height: 56px;
    padding: 8px 0; border-bottom: 1px solid var(--line); color: var(--ink);
}
.pr-ic { flex: none; width: 22px; height: 22px; color: var(--muted); }
.pr-ic svg { width: 100%; height: 100%; display: block; }
.pr-txt { flex: 1; font-size: 15px; }
.pr-txt i { display: block; font-style: normal; font-size: 12.5px; color: var(--muted); margin-top: 2px; }
.pr-chev { color: var(--muted-2); font-size: 20px; line-height: 1; }
.pr-row:hover .pr-chev { color: var(--ink); }

.pr-guest { padding: 26px 0 24px; }
.pr-guest-t { font-size: 20px; font-weight: 700; margin-bottom: 8px; }
.pr-guest p { font-size: 14px; color: var(--muted); line-height: 1.6; margin-bottom: 18px; max-width: 420px; }
.pr-guest .btn { max-width: 420px; }
.pr-req { font-size: 12px; color: var(--muted-2); line-height: 1.7; padding: 22px 0 8px; }

/* ============================ МОДАЛКА ВХОДА ============================ */
.modal-backdrop { display: none; position: fixed; inset: 0; background: rgba(15,12,9,.5); backdrop-filter: blur(2px); z-index: 200; }
.modal-backdrop.show { display: block; }
/* --kb — высота выехавшей клавиатуры (её меряет initModalKeyboard в
   common.js по visualViewport). Окно от клавиатуры не уменьшается, поэтому
   без поправки нижняя часть модалки оказывается под ней: браузер доводит
   поле до нижнего края модалки, а край этот уже спрятан — поле встаёт
   впритык или на пару пикселей ниже видимого. Ужимаем модалку на высоту
   клавиатуры и приподнимаем на половину — она целиком остаётся над
   клавиатурой, и прокрутке внутри есть куда доводить поле.
   Без клавиатуры --kb равна нулю, и правило совпадает с прежним. */
.modal { display: none; position: fixed; z-index: 210; top: calc(50% - var(--kb, 0px) / 2); left: 50%; transform: translate(-50%, -48%); width: min(92vw, 400px); max-height: calc(92vh - var(--kb, 0px)); overflow-y: auto; background: #fff; border-radius: 18px; padding: 34px 30px 28px; box-shadow: 0 30px 80px rgba(0,0,0,.3); }
/* Запас, с которым браузер сам доводит поле при фокусе: доводка скриптом
   отрабатывает после анимации клавиатуры, а это — первый, мгновенный кадр. */
.modal .auth-input, .modal .auth-pick, .modal .phone-row, .modal .code-cell { scroll-margin: 16px 0; }
.modal.show { display: block; animation: modalIn .25s var(--ease); }
@keyframes modalIn { from { opacity: 0; transform: translate(-50%, -44%); } to { opacity: 1; transform: translate(-50%, -50%); } }
.modal-close { position: absolute; top: 16px; right: 16px; background: none; border: none; font-size: 20px; color: var(--muted); line-height: 1; }
.auth-title { font-size: 22px; font-weight: 700; margin-bottom: 8px; }
.auth-sub { font-size: 14px; color: var(--muted); margin-bottom: 20px; line-height: 1.5; }
.auth-sub b { color: var(--ink); }
/* font-family задан явно: поля ввода шрифт страницы НЕ наследуют, и без
   этой строки .auth-input рисовался шрифтом браузера. Пока все поля формы
   были одинаковыми, это лишь выглядело чужеродно. После того как город и
   адрес стали кнопками-шторками (.auth-pick, там font: inherit), в одной
   колонке пошла чересполосица: имя и телефон — шрифтом браузера, город и
   адрес — шрифтом сайта, индекс — снова браузера. В приложениях эти же
   поля набраны одним шрифтом. */
.auth-input { width: 100%; height: 52px; border: 1px solid var(--line); border-radius: 12px; padding: 0 16px; font-family: inherit; font-size: 16px; outline: none; transition: border-color .2s; background: var(--soft); }
.auth-input:focus { border-color: var(--accent); background: #fff; }
.code-cells { display: flex; gap: 10px; justify-content: center; margin: 4px 0 2px; }
.code-cell { width: 56px; height: 60px; border: 1px solid var(--line); border-radius: 12px; background: var(--soft); text-align: center; font-size: 26px; font-weight: 700; color: var(--ink); outline: none; transition: border-color .2s, background .2s; }
.code-cell:focus { border-color: var(--accent); background: #fff; }
.auth-err { color: var(--sale); font-size: 13px; min-height: 18px; margin: 8px 2px; }
.modal .btn { margin-top: 4px; }
.auth-back { display: block; width: 100%; text-align: center; background: none; border: none; color: var(--muted); font-size: 14px; margin-top: 14px; }
.auth-note { font-size: 12px; color: var(--muted-2); line-height: 1.6; margin-top: 16px; text-align: center; }
.auth-note a { color: var(--muted); text-decoration: underline; }
.auth-fallback { color: var(--ink); text-decoration: underline; }
.auth-back[hidden] { display: none; }

/* ---------- Вход: телефон с кодом страны и остальные способы ----------
   Код страны — отдельная кнопка слева от поля, а не часть маски: с маской
   «+7 » номер из другой страны просто нельзя было ввести. */
/* Флаг, код страны и номер — одно поле, а не три коробки в ряд: раньше
   кнопка страны и ввод были обведены каждый своей рамкой, и номер читался
   как что-то отдельное от своего кода. Рамка теперь на всей строке и
   подсвечивается, когда курсор внутри. */
.phone-row { display: flex; align-items: center; height: 52px; border: 1px solid var(--line);
             border-radius: 12px; background: var(--soft); transition: border-color .2s; }
.phone-row:focus-within { border-color: var(--accent); background: #fff; }
.cc-btn { display: flex; align-items: center; gap: 7px; height: 100%; padding: 0 10px 0 12px;
          flex: 0 0 auto; border: none; background: none; font-size: 16px; color: var(--ink); }
.phone-dial { font-size: 16px; color: var(--ink); padding-right: 8px; flex: 0 0 auto; }
/* Ширина фиксированная: Windows не рисует флаг-эмодзи и подставляет вместо
   него пару букв («RS», «CY») — они разной ширины, и названия стран в списке
   разъезжаются лесенкой. */
.cc-flag { font-size: 20px; line-height: 1; flex: 0 0 30px; text-align: center; white-space: nowrap; display: inline-flex; align-items: center; justify-content: center; }
/* Флаг-картинка (Twemoji): лёгкое скругление, чтобы прямоугольник флага
   не выглядел маркой, наклеенной на форму. */
.cc-img { display: block; width: 20px; height: 20px; border-radius: 3px; }
.cc-caret { font-size: 11px; color: var(--muted-2); flex: 0 0 auto; }
.phone-row .auth-input { min-width: 0; flex: 1; height: 100%; border: none; background: none;
                         padding: 0; border-radius: 0; }
.phone-row .auth-input:focus { background: none; }
.cc-list { max-height: 46vh; overflow-y: auto; margin: 12px -6px 0; }
.cc-row { display: flex; align-items: center; gap: 12px; width: 100%; padding: 11px 6px; background: none;
          border: none; border-bottom: 1px solid var(--line); font-size: 15px; color: var(--ink); text-align: left; }
.cc-row:hover { background: var(--soft); }
.cc-name { flex: 1; }
.cc-dial { color: var(--muted); }

/* Мобильное меню */
.mmenu { position: fixed; inset: 0 auto 0 0; width: min(86vw, 330px); background: #fff; z-index: 130; transform: translateX(-100%); transition: transform .3s var(--ease); padding: 22px; overflow-y: auto; }
.mmenu #mAuth { color: var(--accent); font-weight: 700; }
/* Тень только у ОТКРЫТОГО меню.

   Панель всегда висит слева за краем экрана (translateX(-100%)), а тень
   рисуется от её правой кромки на 10 вправо с размытием 40 — и падала на
   страницу даже при закрытом меню. В портрете это терялось на белом
   содержимом во всю ширину, а в альбомном, где лента сужена и по бокам
   поля, слева проступала серая полоса, будто фон затемнён (владелец:
   «слева начинается с черноты, справа белое»). */
.mmenu.open { transform: none; box-shadow: 10px 0 40px rgba(0, 0, 0, .15); }
.mmenu .m-close { background: none; border: none; font-size: 24px; margin-bottom: 20px; }
.mmenu a { display: block; padding: 13px 0; font-size: 16px; border-bottom: 1px solid var(--line); }
.mbackdrop { display: none; position: fixed; inset: 0; background: rgba(0,0,0,.4); z-index: 125; }
.mbackdrop.show { display: block; }

/* ---------- Кнопка корзины в шапке ---------- */
.cart-btn { position: relative; text-decoration: none; }
/* Счётчик по дизайн-пакету: 16px, в углу зоны нажатия. Единственное место,
   где скругление-пилюля осталось законным. */
.cart-count { position: absolute; top: 1px; right: 1px; min-width: 16px; height: 16px; padding: 0 4px; border-radius: 100px; background: var(--accent); color: #fff; font-size: 10px; font-weight: 700; display: grid; place-items: center; line-height: 1; }
.cart-count[hidden] { display: none; }

/* ---------- Переключатель способа доставки в оформлении заказа ---------- */
.dm-seg { display: flex; gap: 6px; background: var(--soft); border: 1px solid var(--line); border-radius: 12px; padding: 4px; margin: 4px 0 8px; }
.dm-seg button { flex: 1; height: 40px; border: none; background: transparent; border-radius: 9px; font-size: 14px; font-weight: 600; color: var(--muted); transition: background .2s, color .2s; }
.dm-seg button.on { background: var(--ink); color: #fff; }

/* ---------- Доставка СДЭК ----------
   Раньше город и улица спрашивались текстом, и человек не видел ни цены
   доставки, ни куда он её получит. Теперь город выбирается из подсказки
   (СДЭК работает по коду города, а не по названию), пункт выдачи — из списка. */

/* Вторая полоса переключателей, под «Доставка / Самовывоз»: вложенная,
   поэтому чуть мельче и без верхнего отступа. */
.dw-seg { margin-top: 0; }
.dw-seg button { height: 36px; font-size: 13px; }

/* Пунктов выдачи в Москве под тысячу — список со своей прокруткой,
   иначе кнопка оплаты уезжает за пределы экрана. Список живёт в шторке
   (см. раздел «Шторки»), и там ограничение по высоте снимается. */
.dp-list { max-height: 232px; overflow-y: auto; margin-top: 8px;
           display: flex; flex-direction: column; gap: 6px; }
.dp-list:empty { display: none; }
.dp { display: flex; align-items: flex-start; gap: 10px; width: 100%; text-align: left;
      padding: 9px 12px; border: 1px solid var(--line); border-radius: 10px;
      background: var(--soft); cursor: pointer; transition: border-color .2s, background .2s; }
.dp:hover { border-color: var(--muted-2); }
.dp.on { border-color: var(--ink); background: #fff; }
.dp-b { flex: none; margin-top: 1px; font-size: 10px; font-weight: 700; letter-spacing: .3px;
        text-transform: uppercase; color: var(--muted); }
.dp-t { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.dp-t b { font-size: 13px; font-weight: 600; color: var(--ink); }
.dp-t i { font-style: normal; font-size: 11px; color: var(--muted); }
.dp-empty { padding: 12px 4px; font-size: 13px; color: var(--muted); }

/* Метро вынесено из общей серой приписки в собственную плашку. У части пунктов
   СДЭК метро не присылает вовсе, и раньше поиск по нему молча не находил —
   человек считал, что сломан поиск. Теперь видно, у каких пунктов это поле
   заполнено, а у каких искать по метро нечего. */
.dp-m { align-self: flex-start; max-width: 100%; padding: 1px 8px; border-radius: 999px;
        border: 1px solid var(--line); background: #fff; font-size: 11px; font-weight: 600;
        color: var(--ink); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.dp.on .dp-m { background: var(--soft); }

/* Пока в поиске пусто, списка нет вовсе: первые сорок адресов из тысячи ничем
   не лучше остальных и читаются как список, который не догрузился. Вместо них
   говорим, что делать и сколько пунктов в городе. */
.dp-hint { display: flex; flex-direction: column; gap: 3px; padding: 14px 4px 10px; }
.dp-hint b { font-size: 13px; font-weight: 600; color: var(--ink); }
.dp-hint i { font-style: normal; font-size: 12px; color: var(--muted); }

/* Строка расчёта. Если СДЭК не ответил, её просто нет: цену согласует
   менеджер, а оформление заказа из-за чужого API не останавливаем. */
.dcalc { display: flex; align-items: baseline; gap: 8px; margin-top: 12px;
         padding: 10px 14px; border-radius: 10px; background: var(--soft); }
.dcalc b { font-size: 15px; font-weight: 700; color: var(--ink); }
.dcalc span { font-size: 12px; color: var(--muted); }
.dcalc.wait { font-size: 13px; color: var(--muted); }
/* Бесплатная доставка — единственное зелёное пятно в форме. Это подарок,
   и он должен прочитаться раньше, чем человек дочитает строку до конца. */
.dcalc.free b { color: #1a7f37; }

/* Итог к оплате: товары плюс доставка. До порога человек видел в карточке
   одну цену, а списывалось бы другое число — эта строка убирает сюрприз. */
/* Подпись у левого края, сумма у правого — как в сводке корзины и в
   приложениях: прижатое к сумме «Итого» владелец счёл ошибкой вёрстки. */
.dtotal { display: flex; align-items: baseline; flex-wrap: wrap; gap: 8px;
          justify-content: space-between;
          margin-top: 14px; padding: 12px 14px; border-radius: 12px;
          border: 1px solid var(--line); }
.dtotal span { font-size: 13px; color: var(--muted); }
.dtotal b { font-size: 18px; font-weight: 800; color: var(--ink); }
/* Подсказка «не хватает N ₽» занимает всю ширину и встаёт над итогом. */
.dtotal i { flex-basis: 100%; order: -1; font-style: normal; font-size: 12px;
            color: var(--muted); }

/* display: flex сильнее браузерного [hidden] { display: none } — без этих
   двух строк спрятанные блоки остаются видимыми пустыми полосами. */
.dcalc[hidden], .dm-seg[hidden], .dtotal[hidden] { display: none; }

/* ---------- Просьба к заказу ----------
   Поле необязательное, и выглядеть оно должно необязательным: подпись под ним
   перечисляет примеры, а не обещает, что доставка сдвинется сама.
   Высоту задаём отдельно от .auth-input: у той высота 52px и нулевые поля
   сверху-снизу — в многострочном поле текст лёг бы одной строкой впритык
   к рамке. Растягивать разрешаем только по вертикали: по ширине поле тянется
   за модалкой, и ручное расширение вылезало бы за её край. */
.oc-head { font-size: 13px; font-weight: 600; color: var(--muted); margin: 18px 2px 8px; }
.oc-area { display: block; height: 92px; min-height: 92px; padding: 12px 16px;
           line-height: 1.45; font-family: inherit; color: var(--ink); resize: vertical; }
.oc-hint { font-size: 12px; color: var(--muted); line-height: 1.5; margin: 6px 2px 0; }

/* ---------- Выбор способа оплаты ----------
   Порядок способов на странице ЮKassa задаёт сама ЮKassa, и переставить его
   в кабинете нельзя: СБП там шестой, спрятан под «Ещё способы». Поэтому выбор
   мы делаем здесь, где СБП стоит первым и выбран заранее, а на ЮKassa уходим
   уже с готовым ответом. Банковские кнопки при этом никуда не деваются. */
.pm-head { font-size: 13px; font-weight: 600; color: var(--muted); margin: 18px 2px 8px; }
.pm-list { display: flex; flex-direction: column; gap: 8px; }
.pm { display: flex; align-items: center; gap: 12px; width: 100%; text-align: left;
      padding: 11px 14px; border: 1px solid var(--line); border-radius: 12px;
      background: var(--soft); cursor: pointer; transition: border-color .2s, background .2s; }
.pm:hover { border-color: var(--muted-2); }
.pm.on { border-color: var(--ink); background: #fff; }
.pm-ic { flex: none; width: 34px; height: 34px; border-radius: 10px; display: grid; place-items: center;
         font-size: 11px; font-weight: 800; color: #fff; letter-spacing: .2px; }
.pm-ic.sbp { background: linear-gradient(135deg, #1ea7de, #7a3fd6 45%, #f5a623 75%, #e64b3c); }
.pm-ic.sber { background: #21a038; }
.pm-ic.tpay { background: #ffdd2d; color: #1b1b1b; }
.pm-ic.card { background: var(--ink); }
.pm-ic.more { background: var(--muted-2); }
.pm-t { display: flex; flex-direction: column; gap: 1px; min-width: 0; padding-right: 8px; }
.pm-t b { font-size: 15px; font-weight: 600; color: var(--ink); }
.pm-t i { font-style: normal; font-size: 12px; color: var(--muted); }
/* Галочка появляется только у выбранного — на месте она стоит всегда,
   иначе строка дёргалась бы по ширине при переключении. */
.pm-mark { margin-left: auto; flex: none; width: 18px; text-align: center; color: var(--ink); opacity: 0; }
.pm.on .pm-mark { opacity: 1; }

/* ---------- Кнопка «В корзину» на карточке товара ---------- */
.btn.added { border-color: var(--accent); color: var(--accent); }

/* ---------- Кнопка избранного в шапке ---------- */
.fav-btn { position: relative; text-decoration: none; }

/* ---------- Страница «Избранное» ---------- */
.fav-card { position: relative; display: flex; flex-direction: column; }
.fav-card .p-brand { display: block; text-decoration: none; color: inherit; }
.fav-card .p-name { text-decoration: none; color: var(--muted); }
.fav-remove { position: absolute; top: 8px; right: 8px; width: 32px; height: 32px; border-radius: 50%; border: 1px solid var(--line); background: rgba(255,255,255,.92); color: var(--muted); font-size: 14px; line-height: 1; display: grid; place-items: center; cursor: pointer; z-index: 2; transition: background .2s, color .2s, border-color .2s; }
.fav-remove:hover { background: #fff; color: var(--ink); border-color: var(--muted-2); }
.fav-add { margin-top: 12px; height: 42px; font-size: 14px; }

/* ---------- Страница корзины ---------- */
.cart-layout { display: grid; grid-template-columns: 1fr 340px; gap: 48px; align-items: start; margin: 20px 0 60px; }
.cart-list { display: flex; flex-direction: column; gap: 14px; }
/* Строки корзины по дизайн-пакету: без рамок-карточек, просто разделитель.
   Чекбокс выбора — наше дополнение к пакету: он позволяет оплатить часть
   корзины, и убирать его было бы шагом назад. */
.cart-item { display: grid; grid-template-columns: 22px 84px 1fr auto; gap: 20px; align-items: center; border-bottom: 1px solid var(--line); padding: 18px 0; }
.cart-check { display: grid; place-items: center; }
.cart-check input, #cartSelAll { width: 18px; height: 18px; accent-color: var(--ink); cursor: pointer; }
.cart-selall { display: inline-flex; align-items: center; gap: 8px; font-size: 14px; color: var(--muted); margin: 0 2px; cursor: pointer; user-select: none; }
.btn:disabled { opacity: .45; pointer-events: none; }

/* --- «Какие подборки вам показывать?» — первый заход на сайт ---
   Тот же экран, что пятым в онбординге приложений. НУАР по выбору
   владельца («Город грехов»): чёрный фон, чёрно-белые снимки, белый капс
   с разрядкой. Выбор теперь в два шага: тап по плитке рисует обводку и
   зажигает кнопку «Продолжить», применяет — кнопка. Снимки те же файлы,
   что показывают приложения (/cat/intro-*.jpg). */
.gintro {
    position: fixed; inset: 0; z-index: 200; background: #0d0d0e;
    display: flex; flex-direction: column; align-items: center;
    /* Поля плиток по бокам — 32, как у ЦУМ Collect и ровно как в iOS
       (OnboardingView) и Android (GenderIntro.kt). На экране 375px плитке
       остаётся 375 − 2×32 = 311px; шире 484px её ограничивает уже не поле,
       а потолок 420px (.gintro-cards). */
    justify-content: center; gap: 20px; padding: 24px 32px;
}
.gintro-brand {
    font-family: Cabin, Inter, sans-serif; font-weight: 700;
    letter-spacing: .18em; font-size: 13px; color: #fff;
}
.gintro-title {
    font-size: 19px; font-weight: 600; color: #fff; text-align: center;
    text-transform: uppercase; letter-spacing: .06em; text-wrap: balance;
}
.gintro-cards { display: flex; flex-direction: column; gap: 16px; width: min(100%, 420px); }
.gintro-card {
    position: relative; overflow: hidden;
    /* 230 вместо прежних 170 — «карточки с фото сделать побольше»
       (владелец); место дала убранная кнопка «Покажу всё». Та же высота
       у плиток в iOS и Android. */
    height: 230px; border: 0; border-radius: 14px; cursor: pointer;
    font: inherit; font-size: 14px; font-weight: 600; letter-spacing: .22em;
    color: #fff; display: flex; align-items: flex-end; padding: 18px;
}
/* Подложки плиток тёмно-серые: до приезда снимков экран уже нуар,
   а не цветные пятна на чёрном. */
.gintro-w { background: #1b1b1d; }
.gintro-m { background: #232325; }
/* Выбранная плитка отвечает обводкой — по референсу владельца. */
.gintro-card.sel { outline: 2px solid #fff; outline-offset: 3px; }
.gintro-btn {
    width: min(100%, 420px); padding: 15px 0; border: 0; border-radius: 10px;
    background: #f2f0ec; color: #0b0b0c; font: inherit; font-size: 13px;
    font-weight: 700; letter-spacing: .22em; text-transform: uppercase;
    cursor: pointer; transition: opacity .25s var(--ease);
}
/* Кнопка «спит», пока выбор не сделан, — и «загорается» после тапа. */
.gintro-btn:disabled { opacity: .35; pointer-events: none; }
/* Снимок проявляется, а не встаёт в кадр готовым: подставленный разом он
   переключает плитку с цвета на фотографию одним кадром — это и есть тот
   рывок, о котором говорил владелец.
   Класс .ready вешается на весь экран, а не на каждый снимок отдельно:
   ждём обе картинки и проявляем их одним движением (gintroReveal в home.js).
   Проявлять по отдельности — то самое «полудёргано и несинхронно».
   Фирменный фон плитки остаётся подложкой: пока снимки едут (или если их
   на сервере нет), экран выглядит ровно так, как выглядел до фотографий. */
.gintro-ph {
    position: absolute; inset: 0; width: 100%; height: 100%;
    object-fit: cover; opacity: 0; transition: opacity .45s var(--ease);
    /* Нуар: снимки обесцвечиваются на месте — файлы остаются цветными,
       их же в цвете показывает витрина. */
    filter: grayscale(1) contrast(1.12) brightness(.92);
}
.gintro.ready .gintro-ph { opacity: 1; }
/* Затемнение только у нижнего края — там, где подпись; вуаль на весь
   снимок портила бы саму фотографию. Тот же приём в приложениях
   (GenderIntro.kt, OnboardingView.swift). */
.gintro-cap {
    position: relative; z-index: 1; color: #fff;
    text-shadow: 0 1px 12px rgba(0, 0, 0, .55);
}
.gintro-card::after {
    content: ""; position: absolute; inset: 0; z-index: 0; pointer-events: none;
    background: linear-gradient(to bottom, transparent 50%, rgba(0, 0, 0, .45));
}
/* Пока снимка нет, затемнять нечего; подложка теперь тёмная, так что
   белая подпись читается и без градиента. */
.gintro-card:not(:has(.gintro-ph))::after { display: none; }
.cart-ph { width: 84px; aspect-ratio: 3/4; border-radius: 6px; overflow: hidden; background: var(--soft); display: grid; place-items: center; }
.cart-ph img { width: 100%; height: 100%; object-fit: cover; }
.cart-ph .noimg { color: var(--muted-2); display: grid; place-items: center; }
/* min-width: 0 здесь не украшение, а условие работы всей строки. Колонка
   1fr в grid по умолчанию не сжимается уже своего min-content, а min-content
   этого блока — самое длинное слово названия или бренда. Без обнуления
   минимума строка на телефоне становилась шире экрана, а html там обрезан
   по горизонтали (overflow-x: clip) — цена просто уезжала под обрез. */
.cart-info { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
/* Бренды пишутся одним словом («BALENCIAGA», «SCHIAPARELLI») и в 13px
   прописными занимают около сотни точек — больше, чем остаётся колонке на
   узком экране. Разрешаем ломать слово: перенос честнее обрезки. */
.cart-brand { font-size: 13px; font-weight: 700; color: var(--ink); text-transform: uppercase; overflow-wrap: break-word; }
.cart-name { font-size: 13px; color: var(--muted); margin-top: 2px; overflow-wrap: break-word; }
.cart-price { font-size: 15px; font-weight: 700; white-space: nowrap; }
/* «Убрать» — слово с подчёркиванием, как в дизайн-пакете, а не круглая
   кнопка с крестиком: слово не спутать с «закрыть» и по нему проще попасть. */
.cart-remove { display: inline-block; background: none; border: none; padding: 0; font-size: 13px; color: var(--muted); text-decoration: underline; text-underline-offset: 3px; margin-top: 8px; cursor: pointer; transition: color .2s; }
.cart-remove:hover { color: var(--ink); }
.cart-summary { border-radius: 10px; padding: 24px; background: var(--soft); position: sticky; top: 90px; display: flex; flex-direction: column; gap: 14px; }

/* Итог: подпись и сумма в одной строке, посаженные на общую базовую линию.
   Главное здесь сумма — ей вес и размер; «Итого 2 вещи» рядом остаётся
   пояснением и потому мельче и серее. Разделитель между «Итого» и числом
   вещей убран по замечанию владельца: точка посреди короткой фразы читалась
   как обрыв, а не как связка. Тот же порядок и те же слова в приложениях
   (CartView.swift) — итог сверху, доставка под ним. */
.cart-total { display: flex; justify-content: space-between; align-items: baseline; gap: 12px; }
.cart-total-cap { font-size: 14px; color: var(--muted); }
/* Сумма не переносится и не жмётся: сжатие сработало бы раньше, чем перенос
   подписи, и цена — то единственное, ради чего смотрят на этот блок, —
   уехала бы в две строки. */
.cart-total-sum { font-size: 22px; font-weight: 700; letter-spacing: -.01em; white-space: nowrap; flex: 0 0 auto; }
.cart-ship { display: flex; justify-content: space-between; align-items: baseline; gap: 12px; font-size: 13px; padding-bottom: 14px; border-bottom: 1px solid var(--line); }
.cart-ship span { color: var(--muted); }
.cart-ship b { font-weight: 600; white-space: nowrap; }

/* Промокод: строка-кнопка, по нажатию открывается шторка с полем. Поле
   раскрывалось прямо здесь, но в корзине оно занимало место постоянного
   шага оформления, которым не является: кодов пока нет вовсе. В шторке
   оно появляется только тогда, когда человек сам за ним пришёл.
   Вид ссылки, а не рамки с полем, — по той же причине. */
.cart-promo { display: flex; flex-direction: column; gap: 10px; }
/* Кнопка во фрейме «как оформить заказ», текст по центру, — просьба
   владельца; та же рамка в iOS (CartView.promoBlock) и Android
   (PromoBlock). Подчёркнутая строка была незаметна и не читалась кнопкой. */
.cart-promo-open { width: 100%; height: 52px; display: flex; align-items: center; justify-content: center; background: none; border: 1px solid var(--ink); border-radius: 10px; padding: 0 16px; font: inherit; font-size: 15px; font-weight: 600; color: var(--ink); cursor: pointer; transition: background .2s; }
.cart-promo-open:hover { background: rgba(0, 0, 0, .04); }
/* Применённый код: слева — что применилось, справа — как это отменить.
   «Убрать» рядом со строкой, а не под ней: отмена должна читаться как
   часть той же строки, иначе её ищут глазами по всей сводке. */
.cart-promo-on { display: flex; align-items: baseline; gap: 10px; }
.cart-promo-ok { flex: 1; min-width: 0; display: flex; flex-wrap: wrap; gap: 2px 6px; font-size: 13px; color: var(--ink); }
.cart-promo-ok i { font-style: normal; font-weight: 600; text-transform: uppercase; }
.cart-promo-off { flex: none; background: none; border: none; padding: 0; font: inherit; font-size: 13px; color: var(--muted); text-decoration: underline; text-underline-offset: 3px; cursor: pointer; transition: color .2s; }
.cart-promo-off:hover { color: var(--ink); }

/* По левому краю, а не по центру: строка переносится, и у центрированной
   вторая строка вставала посередине — «лесенкой», без опоры на левый край
   (замечание владельца про перенос в корзине). Соседняя строка согласия
   выровнена так же: два серых абзаца подряд с разным выравниванием читаются
   как вёрстка, собранная из кусков. */
.cart-note { font-size: 12px; color: var(--muted); line-height: 1.5; text-align: left; }

/* Согласие с правилами — под кнопкой и в корзине, и в форме оформления. */
/* Цвет --muted, а не совсем светлый --muted-2: строка мелкая, но это
   согласие с правилами — его должно быть видно, а не угадывать. */
.rules-note { font-size: 12px; color: var(--muted); line-height: 1.5; text-align: left; }
.rules-note a { color: var(--ink); text-decoration: underline; text-underline-offset: 2px; }
/* В модалке оформления строка стоит между кнопкой оплаты и «Отмена» —
   без отступа она прилипает к обеим. */
.modal .rules-note { margin-top: 12px; }
/* Низ оформления: кнопка оплаты и согласие приклеены к нижнему краю окна
   (владелец) — оплата всегда под пальцем, сколько бы полей ни было выше.
   Отрицательные поля перекрывают паддинг модалки (34px 30px 28px), фон
   прячет проезжающие под футером поля. bottom: -28px — sticky липнет к
   границе контента, а видимый низ окна на 28px ниже (нижний паддинг).
   Те же приклеенные футеры в iOS (checkoutFooter) и Android. */
.ck-foot { position: sticky; bottom: -28px; z-index: 5; margin: 14px -30px -28px; padding: 10px 30px 14px; background: #fff; border-radius: 0 0 18px 18px; }
.ck-foot .rules-note { margin-top: 10px; margin-bottom: 0; }
/* Условие бесплатного тарифа DaData: логотип в списке подсказок адреса.
   Тот же вид в iOS и Android. */
.dd-note { display: flex; align-items: center; justify-content: flex-end; gap: 4px; padding: 6px 4px 0; font-size: 11px; color: var(--muted-2); }
.dd-badge { width: 13px; height: 13px; border-radius: 3px; background: #fd9800; color: #212121; font-size: 9px; font-weight: 600; display: inline-flex; align-items: center; justify-content: center; margin-left: 2px; }

/* Экраны-состояния по дизайн-пакету — одна форма на пустую корзину,
   «войдите», пустую выдачу и ошибку: иконка, заголовок, подсказка, выход. */
.cart-empty, .page-state { display: flex; flex-direction: column; align-items: center; gap: 16px; padding: 72px 0; text-align: center; }
.page-state { grid-column: 1 / -1; }
.cart-empty svg, .page-state > svg { color: var(--muted-2); }
.cart-empty b, .page-state b { font-size: 17px; font-weight: 700; }
.cart-empty span, .page-state span { font-size: 14px; color: var(--muted); max-width: 340px; line-height: 1.55; }
.cart-empty .btn, .page-state .btn { margin-top: 6px; }

/* «Продана» — состояние без цвета: плашка поверх обесцвеченного фото.
   Так сохранённая вещь в избранном не притворяется доступной. */
.ph.is-gone img, .gallery.is-gone .gal-slide img, .gallery.is-gone .thumbs img { filter: grayscale(1); opacity: .55; }
.sold-tag { position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%); z-index: 2; background: rgba(16, 16, 16, .85); color: #fff; font-size: 12px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; padding: 7px 14px; border-radius: 6px; white-space: nowrap; }

/* ---------- Страница «Мои заказы» ---------- */
.orders-list { display: flex; flex-direction: column; gap: 14px; margin: 20px 0 24px; max-width: 640px; }
.order-card { border: 1px solid var(--line); border-radius: 14px; padding: 18px 20px; }
.order-head { display: flex; justify-content: space-between; align-items: flex-start; gap: 12px; }
.order-id { font-size: 15px; font-weight: 700; }
.order-date { font-size: 13px; color: var(--muted); margin-top: 2px; }
.order-status { font-size: 12px; font-weight: 700; padding: 5px 12px; border-radius: 100px; background: var(--soft-2); color: var(--muted); white-space: nowrap; }
/* Без цвета «оплачено» и «отменено» различаются заливкой: успех — чёрная
   плашка, неудача — контурная. Зелёный и красный тут были единственными
   цветными пятнами на всей странице заказов. */
.order-status.ok { background: var(--ink); color: #fff; }
.order-status.err { background: #fff; color: var(--ink); box-shadow: inset 0 0 0 1px var(--ink); }
.order-foot { display: flex; justify-content: space-between; align-items: baseline; gap: 12px; margin-top: 14px; padding-top: 14px; border-top: 1px solid var(--line); flex-wrap: wrap; }
.order-sum { font-size: 17px; font-weight: 700; }
.order-track { font-size: 13px; color: var(--ink); }
.order-track.muted { color: var(--muted-2); }

/* ---------- Профиль: ссылки ---------- */
.profile-links { display: flex; flex-direction: column; gap: 2px; margin: 6px 0 14px; }
.profile-links a { display: flex; align-items: center; justify-content: space-between; padding: 13px 4px; font-size: 15px; color: var(--ink); border-bottom: 1px solid var(--line); }
.profile-links a:hover { opacity: .55; }
.profile-links a::after { content: "›"; color: var(--muted-2); font-size: 18px; padding-left: 12px; }
/* Пояснение под строкой — у «Привязать телефон»: одного названия мало, надо
   сказать, зачем номер вообще нужен. */
.profile-links a i { display: block; margin-top: 3px; font-size: 12px; font-style: normal; color: var(--muted); line-height: 1.4; }

/* ---------- Профиль: «Мои данные» ---------- */
.md-field { margin-bottom: 14px; }
.md-field label { display: block; font-size: 12px; letter-spacing: .06em; text-transform: uppercase; color: var(--muted); margin-bottom: 6px; }
.md-field .hint { font-size: 12px; color: var(--muted); margin-top: 6px; line-height: 1.45; }
/* Красный здесь — единственный цвет на всём сайте, и он заслужен: это не
   украшение, а знак необратимого действия. Кнопка нарочно не сплошная, чтобы
   не выглядела как главное действие экрана. */
.btn-danger {
    background: #fff; color: #b00020; border: 1px solid #e6c4ca;
}
.btn-danger:hover { background: #fff5f6; border-color: #b00020; }
.md-danger { margin-top: 26px; padding-top: 18px; border-top: 1px solid var(--line); }
.md-danger p { font-size: 12.5px; color: var(--muted); line-height: 1.5; margin-bottom: 12px; }
.md-list { font-size: 13.5px; color: var(--ink); line-height: 1.6; margin: 0 0 16px; padding-left: 18px; }
.md-list li { margin-bottom: 4px; }

/* ---------- Страница результата оплаты ---------- */
.pay-result { max-width: 520px; margin: 80px auto; text-align: center; padding: 0 20px; }
.pay-icon { width: 76px; height: 76px; border-radius: 50%; background: var(--soft); display: grid; place-items: center; font-size: 34px; color: var(--muted); margin: 0 auto 24px; }
.pay-icon.ok { background: var(--ink); color: #fff; }
.pay-icon.err { background: #fff; color: var(--ink); box-shadow: inset 0 0 0 1px var(--ink); }
.pay-result h1 { font-size: 26px; font-weight: 700; margin-bottom: 12px; }
.pay-result p { font-size: 15px; color: var(--muted); line-height: 1.6; }
.pay-actions { display: flex; gap: 12px; justify-content: center; flex-wrap: wrap; margin-top: 28px; }

@media (max-width: 820px) {
    .cart-layout { grid-template-columns: 1fr; }
    .cart-summary { position: static; }
}

/* Строка корзины на телефоне.
   Считаем на экране в 360 точек: боковые поля --pad забирают 40, чекбокс
   22, фото 84 и три промежутка по 20 — ещё 166. Названию и цене вместе
   остаётся 154, а одна цена вроде «1 250 000 ₽» занимает под сотню — на
   слово «BALENCIAGA» не хватает. Поэтому на узких экранах ужимаем всё,
   что задано в точках: чекбокс, фото и промежутки. Порог 480, а не 620:
   выше него места хватает и трогать разметку незачем. */
@media (max-width: 480px) {
    /* Фото НЕ уменьшаем: в корзине человек сверяется с картинкой, а не
       читает её. Место находим за счёт промежутков и чекбокса — их урезать
       не жалко. При 360 точках: 40 на поля, 20 чекбокс, 84 фото, три
       промежутка по 10 — на название и цену остаётся 186, и они влезают. */
    .cart-item { grid-template-columns: 20px 84px minmax(0, 1fr) auto; gap: 10px; padding: 14px 0; }
    .cart-price { font-size: 14px; }
}

/* ============================ ШТОРКИ ============================
   Полноэкранное окно с полем сверху и списком под ним. Появилось из-за
   вполне конкретной беды: подсказки городов и пунктов выдачи висели прямо
   в форме, под полем, и на телефоне выехавшая клавиатура закрывала их
   целиком — человек печатал вслепую и не видел, из чего выбирать.
   В шторке поле всегда сверху, а список занимает всю оставшуюся высоту.

   Нижний край поднят на высоту клавиатуры (--kb её меряет initModalKeyboard
   в common.js): окно от клавиатуры не уменьшается, и без поправки нижняя
   часть списка и кнопка снова оказались бы под ней. */
.sheet-wrap { position: fixed; inset: 0; z-index: 300; }
.sheet-back { display: none; }
.sheet {
    position: absolute; top: 0; left: 0; right: 0; bottom: var(--kb, 0px);
    display: flex; flex-direction: column; background: #fff;
    padding: calc(env(safe-area-inset-top, 0px) + 14px) var(--pad)
             calc(env(safe-area-inset-bottom, 0px) + 14px);
    animation: sheetIn .2s var(--ease);
}
/* Анимируем только прозрачность: на широких экранах шторка выровнена
   по центру через transform, и сдвиг в анимации подрался бы с ним.
   На телефоне (снизу, без transform) — подъём 12px, см. медиа-блок ниже. */
@keyframes sheetIn { from { opacity: 0; } to { opacity: 1; } }
@keyframes sheetInLift { from { opacity: 0; transform: translateY(12px); } to { opacity: 1; transform: translateY(0); } }
.sheet-bar { display: flex; align-items: center; gap: 12px; margin-bottom: 14px; }
.sheet-title { flex: 1; min-width: 0; font-size: 17px; font-weight: 700; color: var(--ink); }
.sheet-x { flex: none; width: 34px; height: 34px; margin-right: -6px; border: none;
           background: none; font-size: 24px; line-height: 1; color: var(--muted); cursor: pointer; }
.sheet-x:hover { color: var(--ink); }
.sheet-input {
    flex: none; width: 100%; height: 52px; border: 1px solid var(--line); border-radius: 12px;
    padding: 0 16px; font: inherit;
    /* 16px: меньший кегль включает автозум iOS при фокусе. */
    font-size: 16px; color: var(--ink); background: var(--soft); outline: none;
    transition: border-color .2s, background .2s;
}
.sheet-input:focus { border-color: var(--accent); background: #fff; }
/* Стрелка «моё местоположение» в поле шторки — как у ЦУМа. Кнопка лежит
   ПОВЕРХ поля, поэтому у поля справа запас под неё, иначе длинный набранный
   текст заезжал бы под стрелку. */
.sheet-inrow { position: relative; }
.sheet-inrow .sheet-input { padding-right: 44px; }
.sheet-geo { position: absolute; right: 6px; top: 50%; transform: translateY(-50%);
    width: 36px; height: 36px; display: flex; align-items: center; justify-content: center;
    background: none; border: none; color: var(--ink); }
.sheet-geo:disabled { opacity: .4; }
.sheet-caps { text-transform: uppercase; }
.sheet-hint { flex: none; margin-top: 10px; font-size: 12px; color: var(--muted); line-height: 1.5; }
/* Место под ответ держим только когда он есть: пустая строка в шторке
   отодвигала бы список от поля без причины. */
.sheet-msg { flex: none; font-size: 12px; line-height: 1.4; }
.sheet-msg:not(:empty) { margin-top: 10px; }
.sheet-msg.bad { color: var(--sale); }
.sheet-msg.ok { color: var(--ink); }
.sheet-list { flex: 1; min-height: 0; overflow-y: auto; margin-top: 10px; -webkit-overflow-scrolling: touch; }
/* Список пунктов выдачи в шторке занимает всю высоту: ограничение в 232
   точки было нужно в форме, чтобы кнопка оплаты не уехала за экран. */
.sheet-list.dp-list { max-height: none; }
/* Черты между строками убраны — просьба владельца («убери дивайдеры»),
   так же в iOS и Android: строки различимы и без них, по высоте и отступам. */
.sheet-row {
    display: flex; align-items: center; width: 100%; min-height: 48px; padding: 12px 4px;
    border: none; background: none; text-align: left;
    font: inherit; font-size: 15px; color: var(--ink); cursor: pointer;
}
.sheet-row:hover { background: var(--soft); }
.sheet-row:active { opacity: .6; }
.sheet-empty { padding: 26px 4px; text-align: center; font-size: 13px; color: var(--muted); line-height: 1.5; }
/* Кнопка внизу, над клавиатурой: до неё не нужно прокручивать список. */
.sheet-go { flex: none; margin-top: 14px; }

/* Компактная шторка (промокод): не во весь экран, а невысокой картой у
   нижнего края — в ней одно поле и кнопка, и разворачиваться ей незачем.
   Затемнение сверху показываем и на телефоне: карта не закрывает страницу,
   и без вуали читалась бы как её часть. То же решение в iOS — одна ступень
   260 вместо [.medium, .large] (PromoCodeSheet.swift). */
.sheet-wrap.compact .sheet-back { display: block; position: absolute; inset: 0;
                                  background: rgba(15,12,9,.5); backdrop-filter: blur(2px); }
@media (max-width: 719.98px) {
    .sheet-wrap.compact .sheet {
        top: auto; bottom: var(--kb, 0px);
        border-radius: 18px 18px 0 0;
        padding-top: 14px;
    }
    /* Подъём вместо голого проявления: на телефоне шторка не центрируется
       transform-ом, сдвигу драться не с чем (пункт 6 референсов, Эмиль). */
    .sheet { animation: sheetInLift .25s cubic-bezier(.16, 1, .3, 1); }
}

/* На широком экране во весь экран разворачивать нечего — там шторка
   становится окном по центру, как модалка оформления. */
@media (min-width: 720px) {
    .sheet-back { display: block; position: absolute; inset: 0;
                  background: rgba(15,12,9,.5); backdrop-filter: blur(2px); }
    .sheet {
        top: 50%; left: 50%; right: auto; bottom: auto; transform: translate(-50%, -50%);
        width: min(92vw, 460px); height: min(80vh, 620px);
        border-radius: 18px; padding: 22px 24px 20px;
        box-shadow: 0 30px 80px rgba(0,0,0,.3);
    }
    /* Компактной карте фиксированная высота окна не нужна: её высота — это
       поле, кнопка и ответ «не найден», не больше. */
    .sheet-wrap.compact .sheet { height: auto; min-height: 0; }
}

/* Поле-кнопка в форме оформления: город, адрес и пункт выдачи больше не
   вводятся на месте, а открывают шторку. Выглядит как поле ввода — иначе
   человек не поймёт, что по нему надо нажать. */
.auth-pick {
    /* Не фиксированная высота, а минимальная: значение ниже до двух строк,
       и длинный адрес пункта растит поле, вместо того чтобы резаться.
       Однострочные значения выглядят как прежде. Так же в iOS и Android. */
    display: flex; align-items: center; gap: 10px; width: 100%; min-height: 52px;
    border: 1px solid var(--line); border-radius: 12px; padding: 12px 16px;
    background: var(--soft); font: inherit; font-size: 16px; color: var(--ink);
    text-align: left; cursor: pointer; transition: border-color .2s;
}
.auth-pick:hover { border-color: var(--muted-2); }
/* Пустое поле — цвет подсказки, как у placeholder: заполненное от пустого
   должно отличаться с одного взгляда. */
.auth-pick.empty { color: var(--muted); }
/* До двух строк, а не одна: адреса пунктов вроде «Волгоградский проспект,
   96, корп. 1» в одну строку не помещались, и человек не видел собственного
   адреса (жалоба владельца «адрес не видно»). */
.auth-pick-v {
    flex: 1; min-width: 0; overflow: hidden;
    display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2;
}
.auth-pick-c { flex: none; color: var(--muted-2); font-size: 18px; line-height: 1; }

/* ============================ ПОЛОСА ПРО COOKIE ============================
   Внизу, а не поверх экрана: модальное окно на входе закрывает товар и
   ломает первое впечатление, а требования закона выполняет ровно так же. */
.ckbar {
    position: fixed; left: 0; right: 0; bottom: 0; z-index: 140;
    display: flex; align-items: center; justify-content: center; gap: 18px;
    padding: 14px var(--pad); padding-bottom: calc(14px + env(safe-area-inset-bottom, 0px));
    background: var(--ink); color: #fff;
    transform: translateY(100%); transition: transform .3s var(--ease);
}
.ckbar.show { transform: none; }
.ckbar p { max-width: 760px; font-size: 13px; line-height: 1.5; margin: 0; }
.ckbar a { text-decoration: underline; text-underline-offset: 2px; }
.ckbar-ok {
    flex: none; height: 38px; padding: 0 22px; border: none; border-radius: 10px;
    background: #fff; color: var(--ink); font-size: 13px; font-weight: 600;
    -webkit-appearance: none; appearance: none;
}
.ckbar-ok:active { opacity: .7; }
@media (max-width: 900px) {
    /* Не залезаем на нижний таб-бар: он выше по z-index не окажется, но
       перекрыть «Каталог» и «Корзину» полосой про cookie — так себе идея. */
    .ckbar {
        flex-direction: column; align-items: stretch; gap: 12px; text-align: left;
        bottom: calc(54px + env(safe-area-inset-bottom, 0px)); padding-bottom: 14px;
    }
    .ckbar-ok { width: 100%; height: 42px; }
}

/* ================================== ТОСТ ==================================
   Замена alert(). Системное окно в вебвью приложения подписано «libresale.ru
   сообщает», блокирует всю страницу и уводит фокус — для сообщения «не удалось
   добавить в корзину» это чересчур. Тост всплывает снизу и уходит сам.
   z-index 150: выше таб-бара (130), кнопки товара (125) и полосы про cookie
   (140), но ниже поискового оверлея (160) — из поиска тостов не бывает. */
.toast {
    position: fixed; z-index: 150;
    left: 50%; bottom: calc(24px + env(safe-area-inset-bottom, 0px));
    max-width: min(420px, calc(100vw - 2 * var(--pad)));
    padding: 13px 22px; border-radius: 10px;
    background: var(--ink); color: #fff;
    font-size: 13.5px; line-height: 1.4; text-align: center;
    box-shadow: 0 10px 34px rgba(0, 0, 0, .3);
    opacity: 0; pointer-events: none;
    transform: translate(-50%, 14px);
    transition: opacity .2s var(--ease), transform .2s var(--ease);
}
.toast.show { opacity: 1; transform: translate(-50%, 0); }

@media (max-width: 900px) {
    /* Держимся над таб-баром, а на карточке товара — ещё и над полосой
       «В корзину / Купить» (54 таб-бар + 63 полоса + 12 просвет). */
    .toast { bottom: calc(66px + env(safe-area-inset-bottom, 0px)); }
    body.has-pdcta .toast { bottom: calc(129px + env(safe-area-inset-bottom, 0px)); }
}

/* --- Телефон в альбомной ориентации ---
   Мобильная оболочка включается по ширине до 900px, и в альбомной
   ориентации те же экраны растягивались на 800+ точек: кружки разделов,
   подборки по цене и витрина расползались, между снимками зияли дыры.
   Приложения мы просто заперли в портрет, а сайт крутить не запрещаем —
   поэтому здесь держим содержимое в телефонной ширине и центрируем.
   Полоса вкладок остаётся во всю ширину: она часть оболочки. */
@media (max-width: 900px) and (orientation: landscape) {
    .app-home,
    .app-cats,
    .app-rows,
    main > .wrap,
    .grid.g2 { max-width: 560px; margin-left: auto; margin-right: auto; }

    /* Шапка, поиск и ряд «Женское | Мужское» тоже по 560, а не во всю
       ширину экрана.

       Было так: лента и витрина сужены до 560 и стоят по центру, а шапка
       со строкой поиска и рядом пола растянуты на все 830 — между ними
       получалась ступенька в сотню точек с каждой стороны. Хуже того,
       сегмент «Новое | Скидки», прилипая к шапке, закрывает собой ряд
       пола белым паспарту по своей ширине — а ряд был шире, и его чёрное
       подчёркивание торчало из-под сегмента слева и справа двумя палками
       (владелец: «кнопки разные... торчат тёмные палки»).

       Ширина считается так же, как у содержимого ленты: 560 минус поля,
       поэтому шапка встаёт ровно над витриной, а паспарту сегмента
       накрывает ряд пола целиком. */
    .appbar .ab-row,
    .appbar .ab-search,
    .ab-tabs .ab-gender {
        max-width: calc(560px - var(--pad) * 2);
        margin-left: auto;
        margin-right: auto;
    }
}
