/* Рукописные стили витрины ПОВЕРХ генерённого storefront.css (его пишет
 * конвейер 05, руками не трогать). Подключается из base.html ВТОРЫМ файлом.
 * Появился по аудиту 2026-08-20 (блок ВИЗУАЛ) и держит рукописные панели.
 *
 * Разделы:
 *   Г1  курсив в H1 обложки и буквица (значения сняты с демки-эталона
 *       11-mix-clay-editorial.html: .cover h1 i и .dek::first-letter)
 *   Г2  контейнер страницы .site-col: единственное правило ширины
 *       (колонка 1400 и поля 24, как на electron1ca.ru)
 *   Г2.1 большие фото слушаются aspect-ratio макета
 *   Г2.2 сетка каталога: защита от разъезда колонок
 *   Г2.3 страховка меню шапки: перенос вместо выезда за колонку
 *   Г3  мобильная сетка: колонка не выезжает за экран 390
 *   Г4  фон подложки страницы
 *   Г5  место под полосу прокрутки
 *   Б2  запасной display бейджа корзины в шапке
 *   Б3  панель мобильного меню (partials/menu_drawer.html)
 *   Б5  шторка мобильных фильтров (partials/filter_drawer.html) и бейдж
 *       числа применённых фильтров на кнопке «Фильтры»
 *   Б3/Б5 блокировка прокрутки страницы под открытой панелью
 *   Г6  снятое ограничение высоты фото обложки (история)
 *   Г7  сверка с демкой-эталоном: анимации, переходы и размеры
 *   Г8  первый экран главной вдвое компактнее (просьба Павла 28.08)
 *   Г9  бегущая строка с цифрами убрана (там же)
 *   Г10 иллюстрации в плитках рубрик на главной
 *   Г11 нижнее меню телефона (главная, категории, корзина, чат, избранное)
 *   Г12 страница чата (модуль 8)
 *   Г13 плитка каталога: без номера, читаемый заголовок, цена по низу
 *   Г14 карточка товара: фото целиком, читаемое описание, характеристики
 *   Г15 фотография во весь экран: окно, стрелки, счётчик
 *   Г16 корзина и оформление: тот же читаемый шрифт
 *   Г17 карточка товара на телефоне: шрифт и полное описание
 *   Г18 обложка главной: подпись полосой, а не блоком
 *   Г19 «часто берут» на главной: плашка с фотографией
 *   Г20 «добавить к заказу» в корзине: вертикальная плашка
 *   Ц1  cookie-баннер: артборд из макета -> полоса внизу экрана
 */

/* ══ ШИРИНА САЙТА: ЕДИНСТВЕННОЕ МЕСТО НАСТРОЙКИ ══════════════════════════
   Меняешь одно число - меняется ширина ВСЕХ страниц: и собранных конвейером
   из макета, и рукописных (корзина, избранное, вход, кабинет, заглушки).
   Величины сняты замером с electron1ca.ru 2026-08-28 (просьба Павла свести
   магазины к одной ширине): содержимое колонки 1400, боковые поля 24.
   У электроники вёрстка на content-box и поля лежат СНАРУЖИ max-width,
   у нас border-box из storefront.css и поля входят внутрь - поэтому полная
   ширина контейнера считается отдельной переменной, а не берётся на глаз.
   Правило проекта: ширина обязана совпадать на каждой странице до пикселя. */
:root {
    --site-width: 1400px;   /* содержимое колонки, как на electron1ca.ru */
    --site-pad: 24px;       /* боковые поля колонки, там же */
    /* контейнер целиком: содержимое плюс оба поля */
    --site-band: calc(var(--site-width) + var(--site-pad) * 2);
}


/* ── Г4: фон. В генерённом storefront.css у body стоит #FFF8FB, а обёртки
   страниц красятся в #FFEBF2 - на коротких страницах под подвалом оставалась
   белесая полоса. На демке-эталоне фон body и есть --bg:#FFEBF2; длинным
   страницам разницы нет (их обёртка того же цвета до самого низа). */
html, body { background: #FFEBF2; }

/* ── Г5: полоса прокрутки не должна дёргать вёрстку. Короткие страницы
   (поводы, заглушки, пустая корзина) помещаются в экран и полосы не имеют,
   длинные - имеют; при переходе между ними контент прыгал по горизонтали
   на ширину полосы. Место под полосу резервируем всегда.
   scrollbar-gutter - современный способ, overflow-y:scroll - запасной
   для старых браузеров (даёт ту же ширину, но рисует пустой жёлоб). */
html { scrollbar-gutter: stable both-edges; }
/* both-edges: место под полосу резервируется с ОБЕИХ сторон. Иначе колонка,
   отцентрованная по ширине окна, уезжает влево ровно на ширину полосы:
   замер давал поля 28 слева и 43 справа. */
@supports not (scrollbar-gutter: stable) {
    html { overflow-y: scroll; }
}

/* ── Г1: курсив и буквица обложки. Разметка (<em> в заголовке и
   <span class="dropcap">) хранится в настройке home_cover, а стили потерялись
   при экспорте артборда. Значения ровно с демки: цвет курсива --accent-d
   #C9436E, буквица 62px --accent #EE7598. Селектор p em нужен мобильному
   артборду: конвейер понижает там h1 до p с тем же оформлением. */
h1 em, p em {
  font-style: italic;
  font-weight: 500;
  color: #C9436E;
}
/* Буквица НЕ обтекается текстом. В макете она стояла в float и занимала
   высоту двух строк, поэтому вторая строка тоже отступала вправо (Павел
   30.08: «букву К сделать только на первой строчке»). Теперь это просто
   крупная первая буква внутри строки, вторая строка начинается от края.
   Тот же приём, что у буквицы описания на карточке (`DROPCAP_STYLE` в
   `storefront.py`) - там Павел попросил о том же 29.08. */
.dropcap {
  font-family: 'Playfair Display', Georgia, serif;
  font-size: 32px;
  line-height: 1;
  padding-right: 4px;
  color: #EE7598;
  font-weight: 900;
  vertical-align: -2px;
}

/* ── Г2: контейнер страницы. ЕДИНСТВЕННОЕ правило ширины на весь магазин.
   Класс .site-col стоит на рукописных страницах витрины и на каркасе
   кабинета. Артборды Design пишет конвейер, руками их не трогают, и они
   несут инлайновый max-width:1280px - поэтому подключены к ТОМУ ЖЕ правилу
   по атрибуту, а не отдельным числом. Третьего способа задать ширину
   страницы в проекте нет: сторожа - test_static_pages.ШиринаСайтаTest и
   проверка «одна ширина на всех страницах» в smoke_module1.py.
   Поля стоят на любой ширине, как у электроники: колонка сжимается вместе
   с экраном и к краю не прилипает. Мобильные артборды сюда не попадают -
   своего max-width у них нет, поля у них собственные. */
.site-col,
div[style*="max-width:1280px"] {
  max-width: var(--site-band) !important;
  margin-left: auto;
  margin-right: auto;
  padding-left: var(--site-pad);
  padding-right: var(--site-pad);
}

/* ── Г2.1: большие фото слушаются aspect-ratio макета.
   У картинок из экспорта проставлены атрибуты width и height - они нужны,
   чтобы вёрстка не прыгала при загрузке. Но атрибут height это
   презентационный хинт: когда ширина задана инлайновым `width:100%`, а
   высота приходит хинтом, `aspect-ratio` из того же инлайнового стиля уже
   не работает - фото встаёт ростом ровно в атрибут (800px у обложки)
   независимо от ширины колонки. До сужения это гасил ограничитель
   `max-height:600px` (снят, см. Г6); теперь лечим причину: возвращаем
   height:auto, и включается aspect-ratio макета - 3/4 у обложки главной и
   главного фото карточки, 4/5 у фото в «о нас», 4/3 на мобильной обложке.
   На демке у фото атрибутов нет, поэтому там aspect-ratio работал сразу.
   Плиток товара это не касается: там высоту задаёт родитель с aspect-ratio,
   а у самой картинки стоит инлайновый height:100%. */
img[style*="kenburns"] { height: auto; }

/* Высота первого экрана: при колонке 1280 фото вытягивает секцию до 920px,
   тогда как в концепте она 710. Ограничиваем высоту фото, кадрирует
   object-fit:cover из макета. Только десктоп: на мобиле кадр портретный. */
@media (min-width: 768.02px) {
  img[style*="kenburns"] { max-height: 600px; }
}

/* ── Г8: первый экран главной вдвое компактнее. Просьба Павла 28.08.2026:
   «сделать в два раза меньше, компактнее». Замер до правки: секция 720px
   (фото 600 плюс поля по 60), низ первого экрана на 918. Цель около 360.
   Трогаем ТОЛЬКО обложку главной: селектор цепляется за её сетку
   `1.12fr .88fr`, поэтому общий потолок 600px выше продолжает работать на
   фото карточки товара и страницы «о нас». Всё через !important: размеры
   приезжают инлайновым стилем артборда и иначе побеждают. */
@media (min-width: 768.02px) {
  .dc-desktop section[style*="1.12fr"] {
    padding: 34px !important;
    gap: 34px !important;
    border-radius: 32px !important;
  }

  /* высоту секции задаёт фото: 600 -> 290 */
  .dc-desktop section[style*="1.12fr"] img[style*="kenburns"] {
    max-height: 290px !important;
  }
  .dc-desktop section[style*="1.12fr"] figure {
    border-radius: 26px !important;
  }

  /* заголовок при 74px вставал в три строки, при 44px встаёт в две */
  .dc-desktop section[style*="1.12fr"] h1 {
    font-size: 44px !important;
  }
  .dc-desktop section[style*="1.12fr"] p {
    font-size: 16px !important;
    margin-top: 16px !important;
    max-width: 42ch !important;
  }

  /* ряд «Главная тема выпуска» и ряд кнопок: поджимаем отступы.
     Цепляться за `gap:14px` нельзя, он одинаковый у обоих рядов. */
  .dc-desktop section[style*="1.12fr"] div[style*="align-items:baseline"] {
    margin-bottom: 12px !important;
  }
  .dc-desktop section[style*="1.12fr"] div[style*="margin-top:34px"] {
    margin-top: 20px !important;
  }

  /* круги нарисованы под секцию 720px и в половинной высоте лезли бы на
     текст */
  .dc-desktop section[style*="1.12fr"] div[style*="width:230px"] {
    width: 150px !important;
    height: 150px !important;
  }
  .dc-desktop section[style*="1.12fr"] div[style*="width:120px"] {
    width: 80px !important;
    height: 80px !important;
  }
}

/* ── Г9: бегущая строка с цифрами убрана (Павел 28.08.2026, «убрать»).
   Разметку главной пишет конвейер, руками её не правят, поэтому прячем
   стилем. Селектор держится за три признака сразу: aria-hidden, запрет
   переноса и скругление 999px. В шаблонах такой блок ровно один, на
   десктопном и мобильном артбордах главной. Сами цифры никуда не делись,
   они кормят плитки ниже по странице. */
div[aria-hidden="true"][style*="white-space:nowrap"][style*="border-radius:999px"] {
  display: none !important;
}

/* ── Г2.2: сетка каталога. При колонке 1280 четыре плитки помещаются, как
   в макете; оставляем только защиту от разъезда (`1fr` = `minmax(auto,1fr)`
   растягивается нерастяжимым рядом цены). */
@media (min-width: 768.02px) {
  div[style*="grid-template-columns:280px 1fr"] {
    grid-template-columns: 280px minmax(0, 1fr) !important;
  }
  div[style*="grid-template-columns:280px 1fr"] div[style*="repeat(4,1fr)"] {
    grid-template-columns: repeat(4, minmax(0, 1fr)) !important;
  }
}

/* ── Г2.3: СТРАХОВКА на случай длинного меню. Само меню чинится ДАННЫМИ.
   В колонке 956 на меню остаётся 646px (956 минус логотип 154, две кнопки
   112 и два зазора по 22). У демки в меню 5 коротких пунктов, она занимает
   531px и влезает с запасом. У нас пунктов было шесть (пять рубрик из базы
   плюс «Подарок по поводу») и полными подписями они занимали 730px - на 84px
   больше, чем есть, а ссылки в разметке идут с `white-space:nowrap` и ужаться
   не могут. Кнопки избранного и корзины уезжали за колонку, а меню
   переносилось на второй ряд и растило шапку с демочных 167.5 до 211.
   Починено составом меню, а не вёрсткой: рубрика несёт `menu_label`
   («Адвент» вместо «Адвент-календари») и `show_in_menu` («Календарики» в
   шапку не идут, оставаясь в каталоге, фасете и панели бургера) - ревизия
   0003, значения ставит сид. Меню снова 531px, как на демке.
   Правило ниже остаётся страховкой: заказчица заведёт рубрики сама, и меню
   когда-нибудь опять окажется длиннее ряда. Тогда оно перенесётся и
   прижмётся к правому краю, а не подрежется краем экрана - ни один пункт не
   теряется. Ряды разъезжает тот же `gap:24px` из разметки макета. */
.dc-desktop header > div > nav {
  min-width: 0;
  flex-wrap: wrap;
  justify-content: flex-end;
}

/* ── Г3: мобильные сетки repeat(2,1fr) расползались - 1fr это minmax(auto,1fr),
   и плитка с нерастяжимым содержимым (цена + старая цена + кнопка «+»)
   раздувала правую колонку за край экрана 390 (подрез ~35px). minmax(0,1fr)
   разрешает колонке ужиматься; !important нужен против инлайнового стиля
   макета. Ряду цены внутри карточки разрешён перенос - иначе то же
   содержимое вылезало бы уже за карточку. */
@media (max-width: 768px) {
  .dc-mobile div[style*="repeat(2,1fr)"] {
    grid-template-columns: repeat(2, minmax(0, 1fr)) !important;
  }
  .dc-mobile article[data-slug] {
    max-width: 100%;
    min-width: 0;
  }
  .dc-mobile article[data-slug] > div:last-child {
    flex-wrap: wrap;
    row-gap: 6px;
  }
}

/* ── Б2: бейдж корзины в шапке. При нуле бейджа в макете нет, поэтому сервер
   печатает его скрытым, а storefront.js (paint) правит display в обе стороны.
   Показ - снятие инлайнового display, и вместе с ним со span уходит макетный
   display:grid: браузер держит в инлайновом стиле одно значение display, а не
   оба. Дублируем grid здесь, чтобы цифра осталась по центру кружка.
   place-items:center трогать не надо - он остаётся в разметке. */
[aria-label="Корзина"] span { display: grid; }

/* ── Б3: панель мобильного меню. Разметка в partials/menu_drawer.html,
   открытие - класс .open (storefront.js). Цвет текста задан явно. */
.menu-drawer { display: none; }
@media (max-width: 768px) {
  .menu-drawer {
    display: block;
    position: fixed;
    inset: 0;
    z-index: 90;
    pointer-events: none;
  }
  .menu-drawer-backdrop {
    position: absolute;
    inset: 0;
    background: rgba(46, 29, 37, .45);
    opacity: 0;
    transition: opacity .25s;
  }
  .menu-drawer-panel {
    position: absolute;
    top: 0;
    bottom: 0;
    left: 0;
    width: min(320px, 86vw);
    background: #FFF8FB;
    color: #2E1D25;
    box-shadow: 12px 0 34px rgba(178, 120, 146, .35);
    border-radius: 0 28px 28px 0;
    padding: 18px 22px 26px;
    transform: translateX(-104%);
    transition: transform .25s;
    overflow-y: auto;
    display: flex;
    flex-direction: column;
  }
  .menu-drawer.open { pointer-events: auto; }
  .menu-drawer.open .menu-drawer-backdrop { opacity: 1; }
  .menu-drawer.open .menu-drawer-panel { transform: translateX(0); }
  .menu-drawer-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 10px;
  }
  .menu-drawer-head b {
    font-family: 'Playfair Display', Georgia, serif;
    font-weight: 900;
    font-size: 22px;
  }
  .menu-drawer-x {
    width: 36px;
    height: 36px;
    border-radius: 50%;
    border: 0;
    background: #FFEBF2;
    color: #2E1D25;
    font-size: 14px;
    cursor: pointer;
    box-shadow: inset 2px 2px 6px rgba(206, 140, 166, .28);
  }
  .menu-drawer-link {
    display: block;
    padding: 12px 2px;
    font-family: 'PT Serif', Georgia, serif;
    font-size: 17px;
    color: #2E1D25;
    text-decoration: none;
    border-bottom: 1px solid #FFD6E4;
  }
  .menu-drawer-link.on { color: #C9436E; }
  .menu-drawer-split { height: 10px; }
  .menu-drawer-foot {
    display: flex;
    flex-wrap: wrap;
    gap: 12px 16px;
    padding-top: 4px;
  }
  .menu-drawer-foot a {
    font-family: 'PT Mono', monospace;
    font-size: 10px;
    letter-spacing: .12em;
    text-transform: uppercase;
    color: #8A7079;
    text-decoration: none;
  }

  /* Реквизиты продавца под ссылками меню (31.08). Мельче ссылок и без
     разрядки в заглавные: это сноска, а не пункт меню, и читать её приходят
     не глазами, а по делу - сверить, с кем имеешь дело. Разметка в
     partials/menu_drawer.html, там же и почему она вообще здесь. */
  .menu-drawer-legal {
    margin-top: 14px;
    font-family: 'PT Mono', monospace;
    font-size: 9px;
    line-height: 1.8;
    letter-spacing: .04em;
    color: #8A7079;
  }
}

/* ── Б5: бейдж числа применённых фильтров на кнопке «Фильтры». Число отдаёт
   сервер атрибутом data-count (слот в правилах конвейера), при нуле бейджа
   нет.

   03.09.2026 бейдж переехал ИЗ УГЛА В СТРОКУ кнопки. Было position:absolute с
   top:-4px и right:-4px, то есть значок торчал за пределы кнопки - ровно тот
   приём, что запрещён после трёх поломок 30.08: такое режет любой предок с
   overflow: hidden. Запас до реза был 238 сверху и 188 справа (замер сессии 2
   при 375), но запас исчезает сам собой, стоит кнопке уехать к краю: узкий
   экран, вторая кнопка в ряд, перенос блока. Гейт не отличает «пока не
   режет» от «не режет никогда», и правильно делает.

   Разметку тронуть нельзя: кнопка живёт в templates/generated, а слот
   конвейера разметку в значение не пускает, только текст. Поэтому бейдж
   остался псевдоэлементом, но встал третьим элементом ВНУТРЬ кнопки: она
   display:flex с gap:8px, значок сам сел справа от слова «Фильтры» и за
   границы больше не выходит. Резать нечего.

   position:relative у кнопки ставит слот конвейера; он теперь не нужен, но
   снимать его отсюда нельзя и вреда от него нет. */
[data-role="filters-open"][data-count]:not([data-count="0"])::after {
  content: attr(data-count);
  flex: 0 0 auto;
  min-width: 18px;
  height: 18px;
  padding: 0 4px;
  border-radius: 999px;
  background: #C9436E;
  color: #fff;
  font: 400 9px/18px 'PT Mono', monospace;
  letter-spacing: 0;
  text-align: center;
}

/* ── Б5: шторка мобильных фильтров. Разметка в partials/filter_drawer.html,
   состояние - storefront.js. Оформление в духе сайдбара десктопа. */
.filter-drawer { display: none; }
@media (max-width: 768px) {
  .filter-drawer {
    display: block;
    position: fixed;
    inset: 0;
    z-index: 90;
    pointer-events: none;
  }
  .filter-drawer-backdrop {
    position: absolute;
    inset: 0;
    background: rgba(46, 29, 37, .45);
    opacity: 0;
    transition: opacity .25s;
  }
  .filter-drawer-panel {
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    max-height: 84vh;
    background: #FFF8FB;
    color: #2E1D25;
    border-radius: 28px 28px 0 0;
    box-shadow: 0 -14px 34px rgba(178, 120, 146, .35);
    padding: 18px 20px 24px;
    transform: translateY(104%);
    transition: transform .25s;
    overflow-y: auto;
  }
  .filter-drawer.open { pointer-events: auto; }
  .filter-drawer.open .filter-drawer-backdrop { opacity: 1; }
  .filter-drawer.open .filter-drawer-panel { transform: translateY(0); }
  .filter-drawer-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 6px;
  }
  .filter-drawer-head b {
    font-family: 'Playfair Display', Georgia, serif;
    font-weight: 900;
    font-size: 22px;
  }
  .filter-drawer-x {
    width: 36px;
    height: 36px;
    border-radius: 50%;
    border: 0;
    background: #FFEBF2;
    color: #2E1D25;
    font-size: 14px;
    cursor: pointer;
    box-shadow: inset 2px 2px 6px rgba(206, 140, 166, .28);
  }
  .filter-drawer-label {
    font-family: 'PT Mono', monospace;
    font-size: 10px;
    letter-spacing: .2em;
    text-transform: uppercase;
    color: #8A7079;
    margin: 16px 0 10px;
  }
  .filter-drawer-rows {
    display: flex;
    flex-direction: column;
    gap: 4px;
  }
  .filter-drawer-row {
    display: flex;
    gap: 11px;
    align-items: center;
    border: 0;
    background: transparent;
    padding: 6px 0;
    cursor: pointer;
    font: 400 15px/1.5 'PT Serif', Georgia, serif;
    color: #2E1D25;
    text-align: left;
  }
  .filter-drawer-box {
    width: 22px;
    height: 22px;
    flex: 0 0 22px;
    border-radius: 8px;
    background: #FFEBF2;
    color: #fff;
    display: grid;
    place-items: center;
    font-size: 12px;
    box-shadow: inset 3px 3px 7px rgba(206, 140, 166, .30), inset -3px -3px 7px rgba(255, 255, 255, .95);
  }
  .filter-drawer-row[data-checked="1"] .filter-drawer-box {
    background: #EE7598;
    box-shadow: 3px 3px 8px rgba(238, 117, 152, .4);
  }
  .filter-drawer-name { flex: 1; min-width: 0; }
  .filter-drawer-count {
    font-family: 'PT Mono', monospace;
    font-size: 11px;
    color: #8A7079;
  }
  .filter-drawer-chips {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
  }
  .filter-drawer-chip {
    border: 0;
    background: #FFEBF2;
    color: #2E1D25;
    border-radius: 999px;
    padding: 9px 15px;
    font-family: 'PT Mono', monospace;
    font-size: 10px;
    letter-spacing: .06em;
    cursor: pointer;
    white-space: nowrap;
  }
  .filter-drawer-chip.on {
    background: #EE7598;
    color: #fff;
  }
  .filter-drawer-price {
    display: flex;
    gap: 10px;
    align-items: center;
    color: #8A7079;
  }
  .filter-drawer-price input {
    flex: 1;
    min-width: 0;
    border: 0;
    outline: none;
    color: #2E1D25;
    background: #FFEBF2;
    border-radius: 14px;
    padding: 11px 14px;
    font-family: 'PT Mono', monospace;
    font-size: 12px;
    box-shadow: inset 3px 3px 7px rgba(206, 140, 166, .30), inset -3px -3px 7px rgba(255, 255, 255, .95);
  }
  .filter-drawer-apply {
    width: 100%;
    margin-top: 18px;
    border: 0;
    cursor: pointer;
    border-radius: 999px;
    background: #EE7598;
    color: #fff;
    font-family: 'PT Mono', monospace;
    font-size: 11px;
    letter-spacing: .18em;
    text-transform: uppercase;
    padding: 16px 20px;
    box-shadow: 6px 6px 16px rgba(238, 117, 152, .45);
  }
}

/* ── Б3/Б5: пока открыта панель меню или шторка фильтров, страница под
   подложкой не прокручивается - палец должен тянуть панель, а не фон.
   Класс на <body> ставит и снимает storefront.js (syncScrollLock у обоих
   переключателей). Именно overflow, а не position:fixed: fixed сбрасывает
   позицию прокрутки, и при закрытии страница прыгала бы наверх.
   У html здесь overflow не задан (раздел Г5 ставит только scrollbar-gutter),
   поэтому overflow тела и есть overflow области просмотра.
   Без медиазапроса: панели живут на мобильном, но блокировка должна работать
   на любой ширине, где панель открыта. */
body.drawer-open { overflow: hidden; }


/* ── Г6: высота первого экрана. СНЯТО 2026-08-20 вместе с сужением колонки
   (Г2). Ограничение `img[style*="kenburns"]{max-height:600px}` было костылём
   под контейнер 1280: фото обложки тянулось до 802px и баннер выходил на
   920px вместо демочных 710. После сужения колонки до 1008 колонка фото
   становится 345px, при aspect-ratio 3/4 это 460px высоты и секция ровно
   710px - как на демке, без ограничителя. На экранах шире 1440 ограничитель
   теперь только мешал бы: демка там тоже растит фото. */


/* ═══ Г7: приведение к демке-эталону ══════════════════════════════════════
 *
 * Эталон - утверждённый концепт `Документация/Дизайн-концепции/
 * 11-mix-clay-editorial.html` (он же https://elektronik-studio.ru/marusya/
 * 11-mix-clay-editorial.html). Экспорт Claude Design принёс свой набор
 * анимаций (riseIn, riseInCT, kenburns, floaty, drift, marq, cutX,
 * heartBeat, pulseSoft), часть значений разъехалась с демкой. Все числа
 * ниже сняты с CSS демки, ничего не придумано; в скобках - строка демки.
 *
 * Ширину контейнера (1280 у нас против 1008 на демке) НЕ трогаем: это
 * решение Павла, см. отчёт по сверке.
 */

/* ── Г7.1: появление блоков при скролле.
   Демка (строки 159-167 + скрипт в конце файла): `.rv` = opacity 0 и
   translateY(26px), снимается классом `.in` по IntersectionObserver, переход
   `opacity .7s ease, transform .7s cubic-bezier(.2,.7,.3,1)`, лесенка задержек
   у плиток рубрик (.06/.12/.18/.24) и плиток товара (.08/.16/.24).
   У нас конвейер выдаёт CSS-анимации riseIn/riseInCT, которые играют СРАЗУ
   при загрузке: нижние блоки к моменту прокрутки уже стоят на месте, а четыре
   плитки товара всплывают разом, без лесенки. Возвращаем сценарий демки:
   атрибут data-reveal вешает storefront.js, оформление - здесь.
   `animation:none` глушит riseIn/riseInCT, иначе играли бы обе механики.
   `!important` обязателен: и transition, и animation стоят в инлайновом
   стиле разметки макета. */
[data-reveal] {
    transition: opacity .7s ease, transform .7s cubic-bezier(.2, .7, .3, 1) !important;
    animation: none !important;
}
[data-reveal="off"] {
    opacity: 0 !important;
    transform: translateY(26px) !important;
}
/* лесенка рубрик: демка `.cats .cat.rv:nth-child(2..5)` */
nav[aria-label="Рубрики"] > a[data-reveal]:nth-child(2) { transition-delay: .06s !important; }
nav[aria-label="Рубрики"] > a[data-reveal]:nth-child(3) { transition-delay: .12s !important; }
nav[aria-label="Рубрики"] > a[data-reveal]:nth-child(4) { transition-delay: .18s !important; }
nav[aria-label="Рубрики"] > a[data-reveal]:nth-child(5) { transition-delay: .24s !important; }
/* лесенка плиток товара: демка `.grid .card.rv:nth-child(2..4)`, пятая
   и дальше идут без задержки - на демке правил для них тоже нет */
article[data-slug][data-reveal]:nth-child(2) { transition-delay: .08s !important; }
article[data-slug][data-reveal]:nth-child(3) { transition-delay: .16s !important; }
article[data-slug][data-reveal]:nth-child(4) { transition-delay: .24s !important; }
/* Демка: `@media(prefers-reduced-motion:reduce){.rv{opacity:1;transform:none}}`.
   Скрипт при этой настройке не взводит блоки вовсе, правило - страховка на
   случай, когда настройку переключили уже после загрузки страницы. */
@media (prefers-reduced-motion: reduce) {
    [data-reveal] {
        opacity: 1 !important;
        transform: none !important;
        transition: none !important;
    }
}

/* ── Г7.2: типографика плитки товара.
   Демка: `.card h3{font-size:20px;line-height:1.24;min-height:50px}` и
   `.price{font-size:23px}`. У нас из экспорта 19px и 22px, а min-height
   потерялся - из-за этого строки «оценка» и «цена» в соседних плитках
   стоят на разной высоте, когда названия разной длины.
   Цену цепляем через ряд цены (последний div плитки), чтобы не задеть
   номер «01» в углу фото: у него тот же Playfair 900 22px. */
article[data-slug] h3 {
    font-size: 20px !important;
    line-height: 1.24 !important;
    min-height: 50px;
}
article[data-slug] > div:last-child > span[style*="font-weight:900"] {
    font-size: 23px !important;
}

/* ── Г7.3: фото в плитке приближается при наведении.
   Демка: `.card:hover .ph img{transform:scale(1.04)}`. У нас переход
   `transform .8s cubic-bezier(.2,.7,.3,1)` на картинке из макета есть,
   а правила, которое его запускает, в storefront.css нет - фото стоит. */
article[data-slug]:hover img { transform: scale(1.04); }

/* ── Г7.4: корзина в шапке бьётся как сердце.
   Демка: `.fav:hover,.pill:hover{animation:heartBeat .5s ease}` - обе
   круглые кнопки. У нас heartBeat достался только избранному. */
header a[aria-label="Корзина"]:hover { animation: heartBeat .5s ease; }

/* ── Г7.5: кнопка «Добавить обе».
   На демке это обычная `.btn-p`: padding 19px 32px, line-height 1.62 от
   `font:inherit`, пульсирующий ореол `pulseSoft 2.8s ease-out infinite`,
   при наведении пульс гасится. У нас кнопка приехала на 17px 28px, без
   пульса и с line-height:normal (у <button> он не наследуется).
   Только десктопный артборд: в мобильном кнопка своя, во всю ширину. */
.dc-desktop button[data-role="add-both"] {
    padding: 19px 32px !important;
    line-height: 1.62 !important;
    animation: pulseSoft 2.8s ease-out infinite;
}
.dc-desktop button[data-role="add-both"]:hover { animation: none; }

/* ── Г7.6: отбивка под блоком цифр.
   Демка: `.nums{margin-bottom:56px}`. */
.dc-desktop section[style*="repeat(4,1fr)"][style*="gap:18px"] { margin-bottom: 56px; }

/* ── Г7.7: лупа в поле поиска.
   На демке `.search span` размера не имеет и наследует 17px от body,
   в экспорте проставлено 15px. */
.dc-desktop header span[aria-hidden="true"] { font-size: 17px !important; }

/* ── Г7.8: заголовок обложки и цитата сжимаются на узком десктопе.
   Демка: `.cover h1{font-size:clamp(42px,5.4vw,74px)}` и
   `.pull p{font-size:clamp(22px,2.4vw,30px)}`. В экспорте clamp свернулся
   в верхнее значение, поэтому на ноутбучных 1280-1366 заголовок остаётся
   74px и выглядит крупнее, чем на демке. На 1440 и шире число совпадает. */
.dc-desktop h1[style*="font-size:74px"] { font-size: clamp(42px, 5.4vw, 74px) !important; }
.dc-desktop blockquote p[style*="font-size:30px"] { font-size: clamp(22px, 2.4vw, 30px) !important; }

/* ── Ц1: cookie-баннер (152-ФЗ). Разметка приезжает из макета
   (generated/cookie_banner.desktop.html), обёртку и якорь даёт рукописный
   partials/cookie_banner.html, поведение - storefront.js.

   ЧТО ЧИНИМ. В артборде плашка нарисована ВНУТРИ канваса: сам артборд это
   `position:relative;height:760px;background:#FFEBF2;overflow:hidden`, а
   плашка внутри него - `position:absolute;left:130px;bottom:28px;width:1180px`.
   Как есть это дало бы на каждой странице розовый блок высотой 760px под
   подвалом и плашку шириной 1180 независимо от экрана. Поэтому:
     * обёртка .cookie-banner сама становится fixed-полосой внизу экрана и
       выпадает из потока - высоты странице она не добавляет;
     * артборд внутри неё обнуляется: static, без высоты, без фона;
     * первый его ребёнок (контейнер шапки и плиток артборда, опустевший
       после правила remove в конвейере) прячется;
     * плашка - второй ребёнок - встаёт обычным блоком в колонку 1008, как
       весь остальной контент.

   z-index 80: НИЖЕ панели меню и шторки фильтров (у обеих 90). На 390
   открытая шторка накрывает баннер своим затемнением, а не спорит с ним.

   pointer-events: полоса-обёртка кликов не ловит (под ней обычная страница),
   сама плашка - ловит.

   `!important` ОБЯЗАТЕЛЕН на всём, что спорит с макетом: размеры и позиция у
   разметки конвейера стоят ИНЛАЙНОВЫМ стилем, а он побеждает обычное правило
   по классу (то же и в разделе Г2). Поймано скриншотом: без `!important`
   высота 760px и фон #FFEBF2 у артборда оставались, полоса-обёртка получала
   760px непрозрачного розового и накрывала им нижнюю часть КАЖДОЙ страницы.
   Кликам это не мешало (pointer-events:none), тесты и smoke этого не видели -
   видно было только глазами на снимке. */
.cookie-banner {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 80;
  pointer-events: none;
}
.cookie-banner[hidden] { display: none; }
.cookie-banner > div {
  position: static !important;
  height: auto !important;
  background: none !important;
  overflow: visible !important;
}
.cookie-banner > div > div:first-child { display: none; }
.cookie-banner > div > div:last-child {
  position: static !important;
  width: auto !important;
  max-width: 1008px;
  margin: 0 auto 20px !important;
  flex-wrap: wrap;
  pointer-events: auto;
}
/* На 390 плашка складывается в два ряда: текст, под ним кнопка во всю
   ширину. Артборда мобильного у этого экрана нет, размеры взяты те же, что
   у мобильных карточек витрины (padding 16-18, радиус из макета). */
@media (max-width: 768px) {
  .cookie-banner > div > div:last-child {
    margin: 0 12px 12px !important;
    padding: 16px 18px !important;
    gap: 12px !important;
  }
  .cookie-banner p { flex: 1 1 auto; font-size: 14px !important; }
  .cookie-banner > div > div:last-child > div {
    margin-left: 0 !important;
    width: 100%;
  }
  .cookie-banner button { width: 100%; }
}


/* ── Д1: видимый фокус на поле поиска. У поля из макета нет ни рамки, ни
   скругления: и то, и другое живёт на обёртке (999px, вдавленная тень).
   Прямая обводка самого поля рисовала прямоугольник поверх скруглённой
   плашки, поэтому подсвечиваем обёртку мягким кольцом акцента, а собственную
   обводку поля снимаем. Кольцо тенью, а не outline: тень повторяет
   скругление. `:has` поддерживают все современные браузеры; для старых
   остаётся системная обводка, это не хуже, чем было. */
input[type="search"]:focus-visible,
input[placeholder*="Карта желаний"]:focus-visible {
    outline: none !important;
}

div:has(> input[type="search"]:focus-visible),
div:has(> input[placeholder*="Карта желаний"]:focus-visible) {
    box-shadow:
        0 0 0 2px #EE7598,
        inset 5px 5px 12px rgba(206, 140, 166, .3),
        inset -5px -5px 12px rgba(255, 255, 255, .9) !important;
}

/* ══ М2-01. ПОЛЯ ВВОДА НА МОБИЛЕ НЕ МЕНЬШЕ 16px ══════════════════════════
 *
 * iOS Safari АВТОМАТИЧЕСКИ зумит страницу при фокусе на поле с font-size
 * меньше 16px, и вёрстку уводит вбок: человек тапает по полю телефона в
 * оформлении и видит съехавший экран с обрезанным левым краем. Точно эта
 * грабля уже стоила правки в электронике (реестр ошибок, 01.07.2026,
 * v=242) - там поля чекаута были 14px.
 *
 * Артборды из макета несут размеры инлайном (11-15px), поэтому перебить их
 * можно только `!important`. Десктоп не трогаем: там зума нет, а типографика
 * макета согласована.
 *
 * Чекбоксы и радио исключены: у них font-size ни на что не влияет, а
 * увеличение ломает выравнивание кружка в строке способа получения.
 */
@media (max-width: 900px) {
    input:not([type="checkbox"]):not([type="radio"]),
    textarea,
    select {
        font-size: 16px !important;
    }
}


/* ── Г10: иллюстрации в плитках рубрик (просьба Павла 28.08.2026).
   Плитку рисует конвейер, места под картинку в разметке нет, поэтому кладём
   её фоном. Инлайновый `background:#FFF8FB` это сокращённая запись, она
   обнуляет background-image, и перебить её можно только !important.
   Картинки с прозрачным фоном (заливка от краёв в scratchpad/plitka.py),
   поэтому цвет карточки виден насквозь и прямоугольника не видно.
   Цепляемся за адрес рубрики: он же лежит в плитке и на десктопе, и на
   мобильном артборде. */
/* Иконка «Карты желаний» заменена 31.08: прежняя (арка с тремя фотокарточками)
   читалась как рамка для фото, а товар это плакат-коллаж, куда наклеивают
   снимки своей мечты. Заметила заказчица.

   Имя файла с `-v2`, а не прежнее: статика отдаётся с
   `Cache-Control: public, immutable, max-age=2592000`, и под старым именем
   вернувшийся покупатель месяц видел бы прежнюю картинку. Адрес самой
   картинки версии не несёт - её несёт только css. */
nav[aria-label="Рубрики"] a[href="/catalog/karty-zhelaniy/"] {
  background-image: url("/static/design/img/rubrika-karty-zhelaniy-v2.png") !important;
}
nav[aria-label="Рубрики"] a[href="/catalog/otkrytki/"] {
  background-image: url("/static/design/img/rubrika-otkrytki.png") !important;
}
nav[aria-label="Рубрики"] a[href="/catalog/nakleyki/"] {
  background-image: url("/static/design/img/rubrika-nakleyki.png") !important;
}
nav[aria-label="Рубрики"] a[href="/catalog/advent-kalendari/"] {
  background-image: url("/static/design/img/rubrika-advent-kalendari.png") !important;
}
nav[aria-label="Рубрики"] a[href="/occasion/"] {
  background-image: url("/static/design/img/rubrika-podarki.png") !important;
}

/* Фон текст не обтекает, он ложится под него. Первая попытка ставила картинку
   справа и поджимала текст полем: заголовок «Адвент-календари» переносился в
   две строки, а рисунок читался кашей. Поэтому раскладка обычная для карточки
   рубрики: иллюстрация сверху по центру, текст под ней. Картинки сведены к
   общему квадратному холсту (scratchpad/plitki5.py), поэтому в ряду они одной
   оптической величины, хотя календарь вытянут вверх, а подарки вширь.
   Размер и место задаём на ОБЕИХ ширинах. Правило только для десктопа
   оставляло мобильной инлайновый `background:#FFF8FB`, то есть размер auto и
   угол 0% 0%: картинка в 320 точек ложилась в натуральную величину из левого
   верхнего угла плитки 173x125 и закрывала собой текст. */
nav[aria-label="Рубрики"] a[href^="/catalog/"],
nav[aria-label="Рубрики"] a[href="/occasion/"] {
  background-repeat: no-repeat !important;
  background-position: center 14px !important;
  background-size: auto 92px !important;
  padding-top: 118px !important;
  padding-bottom: 18px !important;
  text-align: center;
}

/* Просьба Павла 28.08: номер и счётчик товаров с плитки убрать, оставить
   картинку и название. Оба это span'ы внутри ссылки (номер первым, счётчик
   последним), название - b, поэтому прячем разом по тегу. Разметку пишет
   конвейер, руками её не трогают.
   Счётчик не потерян: он остаётся в фасете каталога, где по нему выбирают. */
nav[aria-label="Рубрики"] a > span { display: none !important; }

/* Отступы названия были рассчитаны на соседство с номером сверху и
   счётчиком снизу. Их больше нет. */
nav[aria-label="Рубрики"] a > b { margin: 0 !important; }

@media (min-width: 768.02px) {
  nav[aria-label="Рубрики"] a[href^="/catalog/"],
  nav[aria-label="Рубрики"] a[href="/occasion/"] {
    background-position: center 18px !important;
    background-size: auto 132px !important;
    padding-top: 162px !important;
    padding-bottom: 24px !important;
  }
}

/* Пятая плитка на мобиле («Подарки», добавлена по просьбе Павла 28.08) в
   сетке 2х2 оставалась бы половинкой в последнем ряду. Растягиваем её на обе
   колонки. Условие `:last-child:nth-child(odd)` считает само: заказчица
   добавит шестую рубрику, и ряды снова сойдутся без правки css. */
@media (max-width: 768px) {
  nav[aria-label="Рубрики"] a:last-child:nth-child(odd) {
    grid-column: 1 / -1;
  }
}


/* ── Г11: нижнее меню телефона. Просьба Павла 28.08.2026: «такое меню, как на
   Вайлдберриз, и твёрдо закрепить, чтобы оно никуда не девалось». Разметка -
   рукописный partials/bottom_nav.html из base.html, то есть на каждой
   странице витрины.

   Этажи по z-index на телефоне: панель меню и шторка фильтров 90 (накрывают
   всё), нижнее меню 85, cookie-баннер 80 и приподнят над меню своим bottom -
   иначе баннер и меню закрывали бы друг друга.

   Место под меню отдаёт body: без нижнего поля последняя строка страницы
   пряталась бы под панелью. */
.bnav { display: none; }

@media (max-width: 768px) {
  .bnav {
    display: grid;
    grid-template-columns: repeat(5, 1fr);
    /* Высота задана, а не набирается содержимым: под неё резервируется нижнее
       поле страницы (ниже) и по ней же поднимается cookie-баннер. Дай полосе
       расти от подписи - и конец страницы уедет под меню (нашло ревью codex
       29.08 на каркасе, сюда возвращено). */
    min-height: 62px;
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 85;
    background: #FFF8FB;
    border-top: 1px solid #FFD6E4;
    box-shadow: 0 -6px 18px rgba(206, 140, 166, .22);
    /* вырез под жесты айфона: без этого подписи лезут под системную полосу */
    padding-bottom: env(safe-area-inset-bottom, 0px);
  }

  .bnav__item {
    position: relative;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 4px;
    padding: 9px 2px 8px;
    min-width: 0;  /* иначе длинная подпись растянет колонку сетки */
    color: #8A7079;
    text-decoration: none;
    -webkit-tap-highlight-color: transparent;
  }

  .bnav__item--on { color: #C9436E; }

  .bnav__ico {
    display: block;
    width: 23px;
    height: 23px;
  }

  /* Подпись всегда в одну строку и внутри своей колонки: на 320 точках при
     пяти кнопках колонка около 64 точек, и «Избранное» иначе вылезает к
     соседу или поднимает всю полосу. */
  .bnav__lbl {
    font: 400 9px/1 'PT Mono', monospace;
    letter-spacing: .06em;
    text-transform: uppercase;
    max-width: 100%;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }

  /* Бейдж живёт по той же механике, что и в шапке (раздел Б2): сервер печатает
     его скрытым инлайновым display:none при нуле, storefront.js снимает
     инлайновый стиль, и тогда работает display:grid отсюда. */
  .bnav__badge {
    position: absolute;
    top: 4px;
    left: 50%;
    margin-left: 4px;
    min-width: 16px;
    height: 16px;
    padding: 0 4px;
    border-radius: 999px;
    background: #C9436E;
    color: #fff;
    font: 400 9px/1 'PT Mono', monospace;
    display: grid;
    place-items: center;
  }

  body { padding-bottom: calc(62px + env(safe-area-inset-bottom, 0px)); }

  .cookie-banner { bottom: calc(62px + env(safe-area-inset-bottom, 0px)); }
}

/* ── Г12: страница чата (модуль 8, артборд «19 Чат поддержки»).
   Размеры и цвета сняты с артборда: пузырь покупателя акцентный #EE7598 с
   белым текстом, пузырь магазина светлый #FFF8FB, скругление 22 с одним
   срезанным углом у своей стороны, подпись под пузырём PT Mono 10px.
   Лента прокручивается сама и держит высоту: без max-height страница уезжала
   бы вниз на каждом ответе, и поле ввода приходилось бы искать. */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}

.chat {
  background: #FFF8FB;
  border-radius: 28px;
  box-shadow: 8px 8px 20px rgba(206, 140, 166, .28), -8px -8px 20px rgba(255, 255, 255, .9);
  padding: 22px 24px 24px;
  max-width: 760px;
}

.chat__top {
  display: flex;
  align-items: center;
  gap: 12px;
  padding-bottom: 16px;
  border-bottom: 1px solid #FFD6E4;
}

.chat__avatar {
  width: 42px;
  height: 42px;
  border-radius: 50%;
  background: #EE7598;
  color: #fff;
  display: grid;
  place-items: center;
  font-family: 'Playfair Display', Georgia, serif;
  font-size: 20px;
}

.chat__who { display: flex; flex-direction: column; gap: 2px; }
.chat__who b { font-family: 'Playfair Display', Georgia, serif; font-size: 17px; font-weight: 500; }
.chat__who i { font-style: normal; font-size: 13px; color: #8A7079; }

.chat__feed {
  list-style: none;
  margin: 16px 0 0;
  padding: 0 2px;
  display: flex;
  flex-direction: column;
  gap: 12px;
  max-height: 46vh;
  overflow-y: auto;
  overscroll-behavior: contain;
}

.chat__msg { max-width: 84%; }
.chat__msg p {
  margin: 0;
  padding: 12px 16px;
  font-size: 16px;
  line-height: 1.5;
  border-radius: 22px;
  /* перенос длинного слова: без него ссылка в сообщении рвёт пузырь */
  overflow-wrap: anywhere;
  white-space: pre-wrap;
}
.chat__msg time {
  display: block;
  margin-top: 4px;
  font-family: 'PT Mono', monospace;
  font-size: 10px;
  letter-spacing: .08em;
  color: #8A7079;
}

.chat__msg--shop { align-self: flex-start; }
.chat__msg--shop p {
  background: #FFEBF2;
  color: #2E1D25;
  border-bottom-left-radius: 8px;
}

.chat__msg--mine { align-self: flex-end; text-align: right; }
.chat__msg--mine p {
  background: #EE7598;
  color: #fff;
  border-bottom-right-radius: 8px;
}

.chat__error {
  margin: 12px 0 0;
  padding: 10px 14px;
  border-radius: 16px;
  background: #FFD6E4;
  color: #C9436E;
  font-size: 14px;
}

.chat__prompts {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin: 16px 0 0;
}

.chat__prompt {
  border: 1px solid #FFD6E4;
  background: #FFF8FB;
  color: #C9436E;
  border-radius: 999px;
  padding: 8px 14px;
  font: 400 13px/1 'PT Serif', Georgia, serif;
  cursor: pointer;
}
.chat__prompt:hover { background: #FFEBF2; }

.chat__form { margin: 16px 0 0; }

.chat__whoami {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 12px;
  margin-bottom: 12px;
}

.chat__field { display: flex; flex-direction: column; gap: 6px; }
.chat__field > span {
  font-family: 'PT Mono', monospace;
  font-size: 10px;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: #8A7079;
}
.chat__field input,
.chat__field textarea {
  border: 1px solid #FFD6E4;
  border-radius: 18px;
  background: #fff;
  padding: 12px 16px;
  font: 400 16px/1.5 'PT Serif', Georgia, serif;
  color: #2E1D25;
  outline: none;
  resize: none;
}
.chat__field input:focus,
.chat__field textarea:focus { border-color: #EE7598; }

.chat__row { display: flex; gap: 12px; align-items: flex-end; }
.chat__field--wide { flex: 1 1 auto; min-width: 0; }

.chat__send {
  border: 0;
  border-radius: 999px;
  background: #EE7598;
  color: #fff;
  padding: 14px 26px;
  font-family: 'PT Mono', monospace;
  font-size: 12px;
  letter-spacing: .14em;
  text-transform: uppercase;
  cursor: pointer;
}
.chat__send:hover { background: #C9436E; }
.chat__send:disabled { opacity: .6; cursor: default; }

@media (max-width: 768px) {
  .chat {
    padding: 18px 16px 20px;
    border-radius: 24px;
  }
  .chat__feed { max-height: 52vh; }
  .chat__whoami { grid-template-columns: minmax(0, 1fr); }
  .chat__row { flex-direction: column; align-items: stretch; }
  .chat__send { width: 100%; }
}

/* ── Г13: плитка каталога. Правки Павла 29.08.2026 по скриншоту витрины:
   убрать номера 01-04, читаемый шрифт «примерно как на Вайлдберриз», цену
   выровнять по нижней границе, убрать лишние отступы, сделать понятной
   кнопку корзины.

   Разметку плитки пишет конвейер (`generated/`), руками её не трогаем.
   Поэтому цепляемся за то, что генератор ставит стабильно: `article` с
   атрибутом `data-slug` и инлайновые стили внутри него.

   Почему заголовок уезжает с Playfair Display на системный гротеск. Playfair
   это витринная антиква: она хороша в крупных заголовках страницы, но в
   плитке идёт мелко, с тонкими засечками, и длинное название маркетплейса
   («Карта желаний 2026-2027 подарочный набор подруге на день рождения,
   женщине подарок») читается тяжело. Системный гротеск ничего не весит,
   грузить нечего, и он ровно то, что стоит на карточках Вайлдберриз и Ozon.
   Крупные заголовки страниц остаются Playfair - там она на месте. */

article[data-slug] {
  /* Колонкой, чтобы строка цены могла прижаться к низу. Плитки в ряду
     растянуты сеткой на одну высоту, и без этого цена висела там, где
     кончился заголовок: у соседей разной длины - на разной высоте. */
  display: flex;
  flex-direction: column;
  padding: 12px !important;
}

/* Номер плитки. В макете он был украшением ряда из четырёх, на витрине из
   девятнадцати превратился в шум и мешал читать фото. */
article[data-slug] > div > span[style*="border-bottom-right-radius"] {
  display: none !important;
}

article[data-slug] > h3 {
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Arial, sans-serif !important;
  font-size: 15px !important;
  font-weight: 500 !important;
  line-height: 1.35 !important;
  letter-spacing: 0 !important;
  margin: 12px 2px 6px !important;
}

/* Строка «0 · 0 отзывов». Своих отзывов пока нет, поэтому она не должна
   тянуть на себя ни внимания, ни высоты.

   К низу плитки прижата ИМЕННО она, а не цена: отзывы, цена и кнопка это
   один блок, и уезжать вниз он должен целиком. Когда вниз уходила только
   цена, отзывы оставались висеть под заголовком, и между ними зияла дыра
   (правка Павла 29.08: «отзывы поставь над ценой»). */
article[data-slug] > div[style*="letter-spacing"] {
  margin: 0 2px !important;
  margin-top: auto !important;
  padding-top: 12px;
}

/* Строка цены: прижата к низу плитки и переносится, чтобы кнопке хватило
   ширины на собственную строку.

   Цена стоит по правому краю (Павел 30.08). Кнопка от этого не съезжает: у
   неё своя строка во всю ширину, `justify-content` на неё не действует. */
article[data-slug] > div[style*="display:flex"] {
  margin: 8px 2px 0 !important;
  flex-wrap: wrap;
  row-gap: 10px;
  justify-content: flex-end;
}

/* Цена тем же гротеском, что и заголовок: цифры в антикве узкие и на мелком
   кегле сливаются. Размер оставлен крупным - это главное число плитки. */
article[data-slug] > div[style*="display:flex"] > span:first-child {
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Arial, sans-serif !important;
  font-size: 21px !important;
  font-weight: 700 !important;
  letter-spacing: -.01em;
}

article[data-slug] > div[style*="display:flex"] > span:nth-child(2) {
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Arial, sans-serif !important;
  font-size: 13px !important;
}

/* Появление при скролле (раздел Г7.1) на плитках товара выключено. Павел
   29.08 прислал снимок: пока плитка выезжает, кнопка успевает отрисоваться в
   ещё не перенесённой строке цены и превращается в узкий вертикальный столбик
   с боковой надписью. Само по себе это доли секунды, но выглядит поломкой, а
   на длинном каталоге повторяется на каждой прокрутке.

   Глушим и переход, и начальное смещение: без второй строки плитки с
   `data-reveal="off"` остались бы прозрачными навсегда. На плитках рубрик
   главной появление остаётся - там оно играет один раз и не мешает. */
/* Под курсором плитка подскакивала на 8 точек И кренилась на 0.6 градуса
   (`.scp57cffe:hover` в генерённом storefront.css). Пока ведёшь мышью вдоль
   ряда, карточки прыгают одна за другой, а кнопка внизу скачет вместе с ними
   - Павел 29.08 дважды на это указал.

   Отклик оставляем, но тенью, а не движением: тень отвечает «кнопка живая»,
   ничего при этом не сдвигая. Сама тень взята из того же правила макета. */
article[data-slug]:hover {
  transform: none !important;
  box-shadow: 12px 14px 30px rgba(206, 140, 166, .36),
              -8px -8px 20px rgba(255, 255, 255, .9) !important;
}

article[data-slug][data-reveal],
article[data-slug][data-reveal="off"] {
  transition: none !important;
  animation: none !important;
  opacity: 1 !important;
  transform: none !important;
}

/* Кнопка корзины. Была розовым кружком 42x42 с плюсом в строке цены - Павел
   29.08 сказал «непонятно, что это добавление в корзину». Стала полосой во
   всю ширину плитки с подписью.

   Разметку не трогаем, поэтому плюс глушим `font-size: 0`, а подпись даём
   псевдоэлементом. Основу дал codex, поправлено двумя местами: у него фон
   кнопки совпадал с фоном плитки (#FFF8FB на #FFF8FB - кнопка растворялась),
   и селектор без ограничения задевал такую же кнопку на карточке товара, где
   она уже нормальная.

   ПОВЕДЕНИЕ снято с «flow button», который прислал Павел 29.08, и переложено
   в наши цвета. Там React и Tailwind, у нас Jinja и обычный CSS, поэтому
   компонент не переносится - переносится сам приём:
     - в покое кнопка светлая, с розовой обводкой и скруглением 100 (пилюля);
     - под курсором из середины расходится круг тёмного акцента и заливает
       кнопку, подпись и иконка становятся белыми, а скругление стягивается
       со 100 до 14 - пилюля превращается в плитку;
     - при нажатии кнопка чуть уменьшается.

   Круг сделан фоном-градиентом, а не отдельным элементом: у кнопки всего два
   псевдоэлемента, оба заняты иконкой и подписью, а разметку правит конвейер.
   Растёт `background-size` - получается тот же расходящийся круг.

   Иконка перекрашивается вместе с текстом, потому что нарисована МАСКОЙ, а не
   картинкой: цвет берётся из `currentColor`. Иначе белая сумка на светлой
   кнопке в покое была бы не видна. */
[data-slug] button[aria-label="В корзину"] {
  flex: 0 0 100%;
  width: 100% !important;
  height: auto !important;
  min-height: 46px;
  margin: 0 !important;
  /* Боковые поля маленькие нарочно. Кнопка и так во всю ширину, подпись в ней
     по центру, поэтому на вид они не влияют. Зато при узком контейнере поля
     больше самой плитки не дают кнопке ужаться: border-box меньше суммы своих
     полей не сжимается, и она вылезала наружу при любой их величине. */
  padding: 12px 0;
  /* Три строки от одной беды: пока страница грузится, мобильная копия блока
     «часто берут вместе» успевает разложиться узкой, и кнопка в ней
     вытягивалась в вертикальный столбик с боковой надписью поверх карточки
     (снимок Павла 29.08). Поля внутрь ширины, подпись в одну строку и обрез
     по своим границам - и при любой ширине контейнера кнопка остаётся
     кнопкой, а не полосой. */
  box-sizing: border-box;
  min-width: 0;
  overflow: hidden;
  /* Строкой кнопка выкладывает иконку и подпись: сам плюс из разметки
     остаётся безразмерным соседом и места не занимает.

     Промежуток между иконкой и подписью даёт отступ у самой иконки, а не
     `gap`. С `gap` промежутков выходило ДВА: плюс из разметки стоит между
     ними отдельным элементом, и хотя ширины у него нет, свой отступ он
     получает - пара уезжала от середины (заметил Павел 29.08). */
  display: flex;
  align-items: center;
  justify-content: center;
  border: 1.5px solid rgba(201, 67, 110, .45) !important;
  border-radius: 100px !important;
  background-color: transparent !important;
  /* Круг, который расходится из середины. В покое размера нет, под курсором
     разрастается шире кнопки. */
  /* Все четыре с `!important`: в разметке у кнопки инлайновый `background`
     сокращённой записью, и он сбрасывает размер фона в `auto`. Инлайновый
     стиль сильнее обычного правила, поэтому круга просто не было. */
  background-image: radial-gradient(circle, #C9436E 99%, rgba(201, 67, 110, 0) 100%) !important;
  background-repeat: no-repeat !important;
  background-position: center !important;
  background-size: 0 0 !important;
  color: #C9436E !important;
  box-shadow: none;
  font-size: 0 !important;  /* глушим плюс из разметки */
  line-height: 1;
  cursor: pointer;
  transition:
    background-size .8s cubic-bezier(.19, 1, .22, 1),
    border-radius .6s cubic-bezier(.23, 1, .32, 1),
    border-color .45s ease,
    color .45s ease,
    transform .12s ease;
  /* ⚠ Главная причина «кнопка крутится и встаёт вертикально». В макете у неё
     `transform: scale(1.15) rotate(90deg) !important` при наведении: для
     круглого плюсика поворот на 90 градусов был украшением, а полосу во всю
     ширину он разворачивает в вертикальный столбик поверх соседних блоков.
     Гасим в трёх состояниях и обязательно с `!important` - в макете оно тоже
     стоит, без него правило не перебить. */
  transform: none !important;
}

/* Иконка. Подарочная сумка с ручкой, а не корзина из проволоки: у магазина
   подарков она к месту, и на розовой полосе читается лучше - меньше мелких
   деталей, линия толще и ровнее. Картинкой в стиле, а не в разметке: разметку
   пишет конвейер.

   Иконку избранного (сердце) не трогаем: её состояние скрипт переключает
   подменой символа ♡ на ♥, и картинкой из стиля залитое сердце не показать. */
[data-slug] button[aria-label="В корзину"]::before {
  content: "";
  flex: none;
  width: 18px;
  height: 18px;
  margin-right: 9px;
  /* Маска, а не картинка: цвет берётся из `currentColor` и меняется вместе с
     подписью. Картинку пришлось бы держать в двух цветах и подменять. */
  background-color: currentColor;
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.9' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5.7 8.4h12.6l-1 11.1a1.7 1.7 0 0 1-1.7 1.5H8.4a1.7 1.7 0 0 1-1.7-1.5z'/%3E%3Cpath d='M9.4 8.4V6.7a2.6 2.6 0 0 1 5.2 0v1.7'/%3E%3C/svg%3E") center / contain no-repeat;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.9' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5.7 8.4h12.6l-1 11.1a1.7 1.7 0 0 1-1.7 1.5H8.4a1.7 1.7 0 0 1-1.7-1.5z'/%3E%3Cpath d='M9.4 8.4V6.7a2.6 2.6 0 0 1 5.2 0v1.7'/%3E%3C/svg%3E") center / contain no-repeat;
}

[data-slug] button[aria-label="В корзину"]::after {
  content: "В корзину";
  white-space: nowrap;
  font: 600 15px/1.2 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Arial, sans-serif;
  letter-spacing: .01em;
}

/* Наведение только там, где мышь действительно есть: на телефоне «залипший»
   ховер после касания выглядит как нажатая кнопка. */
@media (hover: hover) {
  [data-slug] button[aria-label="В корзину"]:hover {
    /* Круг разошёлся шире кнопки - она залита целиком. */
    background-size: 320% 320% !important;
    border-color: transparent !important;
    color: #fff !important;
    border-radius: 14px !important;
    transform: none !important;
  }
}

[data-slug] button[aria-label="В корзину"]:active {
  background-size: 320% 320% !important;
  border-color: transparent !important;
  color: #fff !important;
  border-radius: 14px !important;
  /* Единственное движение, которое кнопке оставлено: короткое нажатие. Оно
     внутри её собственных границ и ничего вокруг не двигает - в отличие от
     поворота из макета, который разворачивал полосу вертикально. */
  transform: scale(.96) !important;
}

/* На телефоне наведения нет, а отклик нужен: там кнопка залита сразу. Иначе
   покупатель увидел бы бледную пилюлю и не понял, что это главное действие.

   `!important` у размера фона обязателен, и это не перестраховка: в основном
   правиле выше стоит `background-size: 0 0 !important` (там оно нужно, чтобы
   перебить инлайновый `background` из разметки). Без своего `!important` этот
   ноль побеждал, круг не разрастался, и на телефоне оставалась белая подпись
   на светлой плитке - кнопки было просто не видно (поймано на проверке
   витрины при 375 точках 30.08). */
@media (hover: none) {
  [data-slug] button[aria-label="В корзину"] {
    background-size: 320% 320% !important;
    border-color: transparent !important;
    color: #fff !important;
    border-radius: 14px !important;
  }
}

/* Видимый фокус с клавиатуры: кнопка перестала быть кружком, и обводка по
   умолчанию на розовом почти не видна. */
[data-slug] button[aria-label="В корзину"]:focus-visible {
  outline: 3px solid rgba(201, 67, 110, .55);
  outline-offset: 3px;
}

/* На телефоне палец крупнее курсора: держим полосу нажатия не ниже 44. */
@media (max-width: 768px) {
  [data-slug] button[aria-label="В корзину"] { min-height: 44px; }
}

/* ── Г14: карточка товара. Правки Павла 29.08 по скриншотам: убрать номер,
   уместить фото целиком, «поменять карточку под вертикальный товар»,
   поправить описание и шрифт.

   Товар у магазина вертикальный: карты желаний, открытки, плакаты. Макет же
   резал фото по 3/4 с `object-fit: cover`, то есть отрезал верх и низ - на
   карточке «17 в 1» и половины изображения не было видно. */

/* Номер, как и в плитках каталога. На витрине из девятнадцати товаров он
   ничего не нумерует, а перекрывает угол фотографии. */
span[style*="border-bottom-right-radius"] {
  display: none !important;
}

/* Фото целиком, а не обрезанное, и РАМКА ПО РАЗМЕРУ ФОТО.

   Сперва я только вписал изображение в прежнюю рамку - и слева со справа
   остались светлые поля: рамка держала ширину колонки, а вертикальный кадр
   в неё не заполнял (Павел 29.08: «белые края убрать, сделать размер под
   картинки»). Теперь наоборот: размеры задаёт снимок, а рамка ужимается по
   нему.

   Высота ограничена экраном - без предела вертикальное фото уезжало вниз, и
   цена с кнопкой оказывались за краем. Ширина считается сама из пропорций
   снимка, поэтому пустых полей взяться неоткуда. */
img[data-role="gallery-main"] {
  aspect-ratio: auto !important;
  width: auto !important;
  height: auto !important;
  max-width: 100%;
  max-height: 68vh;
  object-fit: contain !important;
  /* Плавное «дыхание» убрано: оно медленно двигает картинку, и на
     вертикальном товаре это читается как ползание. */
  animation: none !important;
}

/* ── Пересборка карточки (Павел 29.08: «чтобы всё смотрелось»).

   Три беды на одном экране:
   1. Полоса миниатюр шла вниз по всей длине - у товара их 22, и лента
      уезжала на два экрана ниже самой фотографии.
   2. Правая колонка была уже левой части, хотя именно в ней цена, вариант и
      обе кнопки: главное на странице выглядело приложением к картинке.
   3. Заголовок набран витринной антиквой: у маркетплейсовых названий на
      четыре строки она читается тяжело, как и в плитках каталога. */

/* Сетка: миниатюры уже, правая колонка ощутимо шире. `minmax(0, 1fr)` в
   середине обязателен - без него длинное название распирает колонку и сетка
   разъезжается. */
div[style*="grid-template-columns:96px 1fr 452px"] {
  /* Средняя колонка ужимается по фотографии (`auto`), остаток забирает
     правая. Иначе в середине оставалось 240 точек пустоты вокруг снимка, а
     блок с ценой упирался в свои 520 - выглядело так, будто карточку не
     дособрали. */
  grid-template-columns: 84px auto minmax(520px, 1fr) !important;
  gap: 28px !important;
}

/* Лента миниатюр не длиннее самой фотографии: дальше прокрутка внутри ленты.
   Так карточка перестала тянуться вниз пустотой, а все 22 снимка остались
   доступны. */
div[style*="grid-template-columns:96px 1fr 452px"] > div:first-child {
  max-height: 68vh;
  overflow-y: auto;
  overscroll-behavior: contain;
  scrollbar-width: thin;
  scrollbar-color: rgba(201, 67, 110, .35) transparent;
  padding-right: 4px;
}

/* Миниатюрам запрещено ужиматься. Без этого лента не прокручивалась, а
   сплющивала все 22 снимка в отведённую высоту: колонка flex, и дети по
   умолчанию сжимаются. */
div[style*="grid-template-columns:96px 1fr 452px"] > div:first-child > * {
  flex: none;
}

div[style*="grid-template-columns:96px 1fr 452px"] > div:first-child::-webkit-scrollbar {
  width: 5px;
}
div[style*="grid-template-columns:96px 1fr 452px"] > div:first-child::-webkit-scrollbar-thumb {
  background: rgba(201, 67, 110, .35);
  border-radius: 999px;
}

/* Заголовок товара и заголовки блоков - тем же гротеском, что каталог и
   описание. Начертание и кегль оставлены от макета: меняется только рисунок
   шрифта, а не строй страницы. */
/* ⚠ Только карточка товара: страницу узнаём по её главному снимку. Без этого
   ограничения правило переписывало заголовок КАЖДОЙ страницы («Каталог»,
   «Корзина») и заодно урезало его с 56 до 34 - я это сам и поймал, сверяя
   корзину. */
body:has(img[data-role="gallery-main"]) h1[style*="Playfair"],
section[style*="grid-template-columns:1fr 1fr"] h2[style*="Playfair"] {
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Arial, sans-serif !important;
  letter-spacing: -.015em !important;
}

/* Цена - тем же гротеском. В антикве цифры узкие и с засечками: рядом с
   гротескным заголовком и кнопками они выглядели из другого набора, а цена на
   карточке главное число. Кегль чуть меньше: гротеск в тех же 44 смотрится
   тяжелее антиквы. */
span[data-role="price"] {
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Arial, sans-serif !important;
  font-size: 38px !important;
  font-weight: 700 !important;
  letter-spacing: -.02em !important;
}

body:has(img[data-role="gallery-main"]) h1[style*="Playfair"] {
  font-size: 34px !important;
  line-height: 1.18 !important;
  font-weight: 700 !important;
}

/* Рамка ужимается по снимку и встаёт по центру своей колонки. Без первого
   правила она осталась бы во всю ширину и вернула бы те самые поля, без
   второго - прижалась бы к левому краю.

   ⚠ Только карточка товара, как и правило заголовка выше. Без ограничения
   `width: fit-content` доставало и обложку ГЛАВНОЙ: там в такой же рамке
   лежит фотография с тёмной подписью внизу, и рамка ужималась не по снимку,
   а по самому длинному слову подписи - с 554 точек до 218. Снимок съезжал в
   узкий столбик, а подпись из полосы превращалась в тёмный блок во всю
   высоту (Павел прислал снимок 30.08). */
body:has(img[data-role="gallery-main"]) figure[style*="border-radius:34px"],
body:has(img[data-role="gallery-main"]) figure[style*="border-radius:26px"] {
  width: fit-content;
  max-width: 100%;
  margin-inline: auto !important;
  justify-self: center;
}

/* Описание и характеристики - тем же гротеском, что и плитки каталога.
   Playfair хороша в заголовке, а полотно описания из кабинета набирается ею
   тяжело: тонкие засечки, длинные строки. */
/* Характеристики переехали в блок с ценой (переносит `storefront.js`), а
   нижний блок убран целиком: строк в нём три, и рядом с длинным описанием он
   стоял почти пустым (Павел 29.08).

   Строк три потому, что остальные характеристики Ozon отдаёт НОМЕРАМИ, а
   справочник имён этому ключу закрыт: подписывать их догадками нельзя. */
section[style*="grid-template-columns:1fr 1fr"] {
  align-items: start !important;
}

/* Секция осталась с одним ребёнком - описание занимает всю ширину. */
section[data-one-column] {
  grid-template-columns: minmax(0, 1fr) !important;
}

/* Характеристики внутри блока с ценой: строкой ниже доставки, отделены
   тонкой чертой. Заголовок мельче - в панели он уже не главный. */
.specs-inline {
  margin-top: 18px;
  padding-top: 16px;
  border-top: 1px solid rgba(206, 140, 166, .28);
}

.specs-inline h2 {
  font-family: 'PT Mono', monospace !important;
  font-size: 10px !important;
  font-weight: 400 !important;
  letter-spacing: .2em !important;
  text-transform: uppercase;
  color: #8A7079 !important;
  margin: 0 0 12px !important;
}

section[style*="grid-template-columns:1fr 1fr"] p,
.dc-mobile div[style*="border-radius:26px"] > p,
section[style*="grid-template-columns:1fr 1fr"] div[style*="display:flex"] > span {
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Arial, sans-serif !important;
}

section[style*="grid-template-columns:1fr 1fr"] p,
.dc-mobile div[style*="border-radius:26px"] > p {
  font-size: 15px !important;
  line-height: 1.6 !important;
  /* Абзацы описания разделены переводом строки (см. `storefront.py`,
     `description_rest`): без этого свойства они склеились бы в простыню. */
  white-space: pre-line;
}

/* ── Свёрнутое описание. Тексты приехали из кабинета Ozon и написаны для
   маркетплейса: десять абзацев с повторами ключевых слов. Целиком они
   выдавливают характеристики и «часто берут вместе» далеко вниз, а покупателю
   там нужны первые три строки. Разворачивается кнопкой, как на электронике. */
[data-desc-collapsed] .desc__body {
  max-height: 260px;
  overflow: hidden;
  /* Текст не обрывается ножом, а тает: так видно, что дальше есть ещё. */
  -webkit-mask-image: linear-gradient(180deg, #000 62%, transparent 100%);
  mask-image: linear-gradient(180deg, #000 62%, transparent 100%);
}

.desc__more {
  margin-top: 14px;
  padding: 0;
  border: 0;
  background: transparent;
  color: #C9436E;
  font-family: 'PT Mono', monospace;
  font-size: 11px;
  letter-spacing: .16em;
  text-transform: uppercase;
  cursor: pointer;
  border-bottom: 1px solid rgba(201, 67, 110, .45);
  padding-bottom: 3px;
}

.desc__more:hover { color: #2E1D25; border-bottom-color: #2E1D25; }

.desc__more:focus-visible {
  outline: 3px solid rgba(201, 67, 110, .5);
  outline-offset: 3px;
}

/* ── Г15: фотография во весь экран (просьба Павла 29.08).

   Разметку окна строит `storefront.js`: конвейер такого экрана не рисует, а
   генерённые шаблоны руками не правим.

   Фон почти непрозрачный и тёплый, из палитры магазина: чёрная подложка на
   розовом сайте выглядит чужой. Прокрутку страницы под окном глушим тем же
   классом, что у мобильных панелей (раздел Б3/Б5). */
.lightbox {
  position: fixed;
  inset: 0;
  z-index: 100;  /* выше панели меню (90) и нижнего меню (85) */
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
  background: rgba(46, 29, 37, .93);
  cursor: zoom-out;
}

.lightbox[hidden] { display: none; }

.lightbox__img {
  /* Размер считается ОТ ОКНА за вычетом того, что вокруг: по бокам стрелки
     (48 + отступ), снизу счётчик, сверху крестик. С прежними 88vh высокий
     снимок упирался в счётчик и обрезался (Павел 29.08: «не помещается»). */
  max-width: min(1200px, calc(100vw - 160px));
  max-height: calc(100vh - 132px);
  width: auto;
  height: auto;
  display: block;
  border-radius: 18px;
  background: #FFF8FB;
  box-shadow: 0 30px 70px rgba(0, 0, 0, .45);
  cursor: default;  /* по самому снимку окно не закрывается */
  /* Горизонтальные движения оставляем себе (ими листают), вертикальные и
     щипок отдаём браузеру: фотографию должно быть можно приблизить. */
  touch-action: pan-y pinch-zoom;
}

.lightbox__counter {
  position: absolute;
  left: 50%;
  bottom: 20px;
  transform: translateX(-50%);
  font-family: 'PT Mono', monospace;
  font-size: 11px;
  letter-spacing: .16em;
  color: #FFF8FB;
  background: rgba(46, 29, 37, .55);
  border-radius: 999px;
  padding: 8px 16px;
}

.lightbox__close,
.lightbox__nav {
  position: absolute;
  width: 48px;
  height: 48px;
  border: 0;
  border-radius: 50%;
  background: rgba(255, 248, 251, .92);
  color: #2E1D25;
  cursor: pointer;
  font-size: 18px;
  line-height: 1;
  display: grid;
  place-items: center;
  transition: background-color .18s ease, transform .12s ease;
}

.lightbox__close { top: 18px; right: 18px; }
.lightbox__nav--prev { left: 18px; top: 50%; transform: translateY(-50%); }
.lightbox__nav--next { right: 18px; top: 50%; transform: translateY(-50%); }

/* Стрелки рисуем углом из двух границ: одна разметка на обе стороны, и не
   нужен ни шрифт значков, ни картинка. */
.lightbox__nav::before {
  content: "";
  width: 11px;
  height: 11px;
  border-top: 2px solid #2E1D25;
  border-right: 2px solid #2E1D25;
}
.lightbox__nav--prev::before { transform: rotate(-135deg); margin-left: 4px; }
.lightbox__nav--next::before { transform: rotate(45deg); margin-right: 4px; }

@media (hover: hover) {
  .lightbox__close:hover,
  .lightbox__nav:hover { background: #fff; }
}

.lightbox__close:focus-visible,
.lightbox__nav:focus-visible {
  outline: 3px solid #EE7598;
  outline-offset: 3px;
}

/* Снимок один - листать нечего, стрелки и счётчик прячем. */
.lightbox[data-one="1"] .lightbox__nav,
.lightbox[data-one="1"] .lightbox__counter { display: none; }

/* На телефоне кнопки крупнее и ближе к краям: их нажимают пальцем. */
@media (max-width: 768px) {
  .lightbox { padding: 12px; }
  /* На телефоне стрелки лежат поверх снимка, поэтому по ширине место под них
     не режем - листают всё равно пальцем. По высоте оставляем счётчик и
     крестик. */
  .lightbox__img {
    max-width: calc(100vw - 24px);
    max-height: calc(100vh - 116px);
  }
  .lightbox__close,
  .lightbox__nav { width: 44px; height: 44px; }
  .lightbox__nav--prev { left: 8px; }
  .lightbox__nav--next { right: 8px; }
  .lightbox__close { top: 10px; right: 10px; }
}

/* Стрелки на самой фотографии карточки. Миниатюры видны не всем: на телефоне
   их нет вовсе, а на десктопе лента сбоку и мелкая. Ставит `storefront.js`.

   Рисунок тот же, что у стрелок в окне просмотра, только мельче: это одна и
   та же вещь, и выглядеть она должна одинаково. */
.gal-nav {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  z-index: 3;
  width: 42px;
  height: 42px;
  border: 0;
  border-radius: 50%;
  background: rgba(255, 248, 251, .9);
  color: #2E1D25;
  cursor: pointer;
  display: grid;
  place-items: center;
  box-shadow: 4px 4px 12px rgba(206, 140, 166, .3);
  transition: background-color .18s ease;
}

.gal-nav--prev { left: 12px; }
.gal-nav--next { right: 12px; }

.gal-nav::before {
  content: "";
  width: 10px;
  height: 10px;
  border-top: 2px solid #2E1D25;
  border-right: 2px solid #2E1D25;
}
.gal-nav--prev::before { transform: rotate(-135deg); margin-left: 3px; }
.gal-nav--next::before { transform: rotate(45deg); margin-right: 3px; }

@media (hover: hover) {
  .gal-nav:hover { background: #fff; }
}

.gal-nav:focus-visible {
  outline: 3px solid #EE7598;
  outline-offset: 2px;
}

/* На телефоне листают пальцем, поэтому стрелки мельче и бледнее: они там
   подсказка, что снимок не один, а не главный способ листать. */
@media (max-width: 768px) {
  .gal-nav { width: 36px; height: 36px; background: rgba(255, 248, 251, .78); }
  .gal-nav--prev { left: 8px; }
  .gal-nav--next { right: 8px; }
}

/* ── Г16: корзина и оформление тем же гротеском (Павел 29.08, «шрифты тоже
   поменять»).

   Здесь гротеском ВСЁ, включая заголовок страницы: Павел показывал именно
   этот экран. Кегль заголовка не трогаем - меняется только рисунок шрифта.
   Названия товаров тут маркетплейсовые, на три строки, а
   суммы - главные числа страницы: и то и другое в антикве читается тяжело.

   Страницу узнаём по её собственному узлу: список корзины есть только здесь и
   на оформлении. Так правило не задевает остальной сайт. */
body:has([data-role="cart-list"]) [style*="Playfair"],
body:has([data-role="checkout-form"]) [style*="Playfair"] {
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Arial, sans-serif !important;
  letter-spacing: -.01em !important;
}

/* Итоговая сумма и цены строк: гротеск в том же кегле тяжелее антиквы,
   поэтому чуть мельче - иначе «К оплате» перевешивает саму кнопку. */
body:has([data-role="cart-list"]) [data-role="cart-total"] {
  font-size: 32px !important;
  font-weight: 700 !important;
}

/* Название товара в строке корзины: кегль ближе к плиткам каталога, чтобы
   длинное маркетплейсовое имя не занимало три строки. */
body:has([data-role="cart-list"]) [data-role="item-title"] {
  font-size: 16px !important;
  font-weight: 500 !important;
  line-height: 1.35 !important;
}

/* ── Г17: карточка товара на телефоне (проверка витрины при 375 точках,
   30.08).

   Правки Г14 прошли мимо мобильной копии, и это не случайность: телефонную
   разметку конвейер собирает отдельной страницей, опоры у неё другие.
   Название товара там не `h1`, а `p`; описание и отзывы лежат не в
   двухколоночной секции, а каждое в своей карточке со скруглением 26. Ни один
   селектор Г14 за них не цепляется - на телефоне так и остались витринная
   антиква в названии и «PT Serif» кеглем 14 в описании, ровно то, что Павел
   просил убрать.

   Правило узкое: только страница товара (её выдаёт главный снимок) и только
   мобильная копия. */
body:has(img[data-role="gallery-main"]) .dc-mobile p[style*="Playfair"] {
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Arial, sans-serif !important;
  font-size: 24px !important;
  font-weight: 700 !important;
  line-height: 1.2 !important;
  letter-spacing: -.015em !important;
}

/* Заголовок «Описание». Берём именно его, а не все три: блок узнаём по телу
   описания, которое складывает `storefront.js`. «Часто берут» и «Отзывы»
   остаются антиквой - на большом экране они её тоже сохранили. */
.dc-mobile div[style*="border-radius:26px"]:has(.desc__body) h2[style*="Playfair"] {
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Arial, sans-serif !important;
  font-size: 20px !important;
  letter-spacing: -.015em !important;
}

/* Абзацы описания. Прежнее правило цеплялось за ПРЯМОГО ребёнка карточки, а
   `storefront.js` завернул текст в `.desc__body` - абзацы стали внуками, и
   правило перестало работать: на телефоне описание осталось мелким, с
   засечками и без пустых строк между абзацами.

   Цепляемся за само тело описания: оно есть в обеих копиях, и одно правило
   покрывает и телефон, и большой экран. */
.desc__body p {
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Arial, sans-serif !important;
  font-size: 15px !important;
  line-height: 1.6 !important;
  /* Абзацы склеены переводом строки (`storefront.py`, `description_rest`) -
     без этого свойства они идут простынёй. */
  white-space: pre-line;
}

/* Хвост описания, который на телефоне дописывает скрипт: отбить от первого
   абзаца, иначе они склеиваются в один. */
.desc__tail { margin-top: 14px !important; }

/* ── Г18: обложка главной (снимок Павла 30.08).

   Подпись под фотографией сделана в макете под КОРОТКОЕ имя товара: тёмная
   полоса в одну строку, слева название, справа цена. У нас имена приехали из
   кабинета Ozon - «Карта желаний 2026-2027 подарочный набор подруге на день
   рождения, женщине подарок», 76 знаков. Полоса разрасталась на десять строк
   и закрывала снимок целиком.

   Обрезаем название двумя строками, а цену держим на своей строке: она
   короткая, и переносить её незачем. Разметку не трогаем - обложку печатает
   конвейер.

   На телефоне обложка своя, и подпись там разрасталась на четыре строки из
   224 точек высоты - почти половина снимка. Правило одно на обе разметки.

   Опоры разные, потому что разметки разные: на большом экране обложку выдаёт
   сетка колонок 1.12/0.88, на телефоне - рамка со скруглением 24, внутри
   которой лежит ССЫЛКА. У «о нас» рамка такая же, но подпись там
   `figcaption`, поэтому под правило она не попадает. */
section[style*="1.12fr"] figure a[style*="position:absolute"],
.dc-mobile figure[style*="border-radius:24px"] > a[style*="position:absolute"] {
  align-items: center !important;
}

section[style*="1.12fr"] figure a[style*="position:absolute"] > b,
.dc-mobile figure[style*="border-radius:24px"] > a[style*="position:absolute"] > b {
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  /* Без этого длинное слово распирает флекс-элемент, и обрезка не
     срабатывает: элемент шире полосы, а не наоборот. */
  min-width: 0;
  line-height: 1.3;
}

section[style*="1.12fr"] figure a[style*="position:absolute"] > span,
.dc-mobile figure[style*="border-radius:24px"] > a[style*="position:absolute"] > span {
  flex: none;
  white-space: nowrap;
}

/* ── Г19: «С картой желаний часто берут» - плашка с фотографией товара.

   Павел 30.08: «сюда добавить именно товар». Было две узкие пилюли, в них
   название и цена текстом: на витрине это читалось как ссылки, а не как
   товар. Снимок дописывает `storefront.js` (разметку блока печатает
   конвейер), а вид задаётся здесь.

   `white-space` в макете стоит инлайном - отсюда `!important`: без него
   длинное имя из кабинета Ozon держит пилюлю одной строкой, и она уезжает
   за край блока. */
a.cross-chip {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  white-space: normal !important;
  text-align: left;
  max-width: 340px;
  line-height: 1.35;
}

.cross-chip__img {
  flex: none;
  width: 42px;
  height: 42px;
  border-radius: 12px;
  object-fit: cover;
  display: block;
  background: #FFD6E4;
}

/* Имя товара в две строки: третья уже не помещается в высоту плашки, а
   обрывать текст на полуслове хуже, чем показать начало. */
.cross-chip__text {
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  min-width: 0;
}

/* На телефоне плашки идут столбцом во всю ширину: там ограничивать нечем. */
@media (max-width: 768px) {
  a.cross-chip { max-width: none; width: 100%; }
}

/* ── Г20: «Добавить к заказу» в корзине - вертикальная плашка.

   Павел 30.08: «кнопку в корзину сделать как везде и в вертикальном режиме».
   Сама разметка живёт в рукописном `pages/cart.html`, сюда вынесена только
   раскладка, и не для красоты: скрипт корзины прячет товар, который уже
   лежит в корзине, через `style.display`, а показывает обратно пустой
   строкой. Пустая строка стирает ЛЮБОЙ инлайновый `display`, поэтому
   `display:flex` в разметке жил ровно до первой перерисовки, и плашка
   разваливалась в обычный блок. В классе он это переживает. */
.cross-card {
  display: flex;
  flex-direction: column;
}

/* Название прижимает цену и кнопку вниз, чтобы у соседних плашек с именами
   разной длины кнопки стояли на одной линии. */
.cross-card > div { margin-bottom: auto; }

/* Кнопке возвращаем собственную высоту. В разделе Г13 у неё стоит
   `flex: 0 0 100%` - в плитке каталога это строка во всю ШИРИНУ, потому что
   там флекс идёт строкой. Здесь флекс идёт колонкой, и те же 100% читаются
   как высота: кнопка растягивалась на все 486 точек плашки. */
.cross-card button[aria-label="В корзину"] {
  flex: none !important;
  align-self: stretch;
}

/* ── Г21: живые данные вместо макетных. Обход витрины 30.08 при 1440 и 1280.

   Макет рисовали под короткие вымышленные названия. В базе имена из кабинета
   Ozon по 70-115 знаков, артикулы одним словом через подчёркивания и один
   товар без снимка. Три места, где от этого поехало; каждое с замером на
   боевом адресе. */

/* 1. Цена в плашке «часто берут» пропадала.

   Имя и цена лежали ОДНИМ текстом («Название · 1 330 ₽»), обрезанным по двум
   строкам. Замер при 1440: у первой плашки clientHeight 30 при scrollHeight
   45, у второй 30 при 59, то есть «· 1 330 ₽» и «· 1 000 ₽» уходили под
   обрез целиком. Теперь `storefront.js` кладёт имя и цену в разные узлы,
   обрезается только имя, цена стоит своей строкой и видна всегда.

   `min-height` в две строки держит соседние плашки одной высоты: у короткого
   имени строка была бы одна, и ряд шёл бы уступами. */
.cross-chip__body {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}

.cross-chip__text {
  min-height: calc(2 * 1.35em);
}

.cross-chip__price {
  color: #C9436E;
  white-space: nowrap;
}

/* 2. Артикул вылезал из своей колонки в корзине при 1280.

   Артикулы приходят одним словом: `карта_желаний_подарок_бежевая`. Подчёркивание
   переносом не считается, поэтому строка не разрывалась. Замер при 1280:
   колонка названия 196, строка артикула 218 у «Карта_желаний_подарок_романтик»
   (21.8 наружу), 211 у «карта_желаний_подарок_бежевая» (14.5) и 203 у
   «Открытки_НГ_скретч_ДМ_снегур» (7.3) - текст залезал в отступ до кнопки
   «−». При 1440 колонка 356 и всё помещалось, поэтому раньше не всплывало. */
[data-role="item-variant"] {
  overflow-wrap: anywhere;
}

/* 3. Товар без снимка показывал четверть логотипа.

   Вместо фотографии подставляется `logo.png` 760×246, а рамки товара
   вертикальные 3:4 с `object-fit: cover`. Замер на каталоге: рамка 224×298,
   от логотипа оставалось 24.3 % ширины - в плитке висел обрывок «ру».
   `contain` показывает логотип целиком, свободное место закрывает розовая
   подложка рамки, она уже задана в разметке.

   На логотип в шапке правило не влияет: там рамка 154×50 при пропорциях
   самого файла 760×246, вписывать нечего. */
img[src$="/design/img/logo.png"] {
  object-fit: contain !important;
}

/* ── Г22: сердечко «в избранное» в ЛЕВОМ верхнем углу снимка.

   Просьба Павла 30.08 по скриншоту. В макете кнопка стояла справа сверху.

   Цепляемся за `aria-label` и за инлайновый угол, а не за класс: имя класса
   печатает конвейер, и при следующем прогоне оно меняется.

   `right` и `bottom` заданы инлайном, поэтому перебить их можно только
   `!important`; `top` и `left` инлайна не имеют, им хватает обычной силы.

   Угол свободен: номер плитки («01», «02») там был, но его убрали в разделе
   Г13, а бейдж «Нет в наличии» стоит не на снимке, а в теле плитки вместо
   цены. Проверено замером - в рамке снимка у плитки каталога ровно два
   абсолютно позиционированных потомка: скрытый номер и эта кнопка.

   Мест в проекте ровно три, у каждого свой угол в разметке, поэтому и правил
   три. Отступ у каждого сохраняем прежний, меняем только сторону.

   1. Плитка товара, 36×36, `top:10px;right:10px`. Одна разметка на оба
      артборда. */
button[aria-label="В избранное"][style*="top:10px"] {
  right: auto !important;
  left: 10px;
}

/* 2. Снимок на карточке товара, ПК, 48×48, `bottom:18px;right:18px`. Гасим оба
      инлайновых угла, иначе кнопка осталась бы прижата к правому краю.

      Замер рамки снимка 459×612 до переноса: скидка «−57%» занимает 379-441 по
      горизонтали и 18-52 по вертикали, счётчик «1 / 5» стоит внизу слева,
      стрелки листания по центру, 285-327. Кнопка встаёт в 18-66 на 18-66 и не
      задевает ничего. */
button[aria-label="В избранное"][style*="bottom:18px"] {
  bottom: auto !important;
  right: auto !important;
  top: 18px;
  left: 18px;
}

/* 3. Тот же снимок на телефоне, 42×42, `bottom:14px;right:14px`.

      Правило намеренно БЕЗ `.dc-desktop`: Павел просил перенести везде, а
      сердечко - одна кнопка на весь магазин, и стоять она должна в одном углу.
      Мобильная половина файла ниже черты этим углом не занимается, там у
      кнопки только увеличение зоны нажатия (`::after`, inset -4). */
button[aria-label="В избранное"][style*="bottom:14px"] {
  bottom: auto !important;
  right: auto !important;
  top: 14px;
  left: 14px;
}

/* ── Г23: полоса прокрутки страницы в цвете магазина.

   Оставалась системной серой и на розовом фоне читалась как чужая деталь.
   Внутренние полосы в проекте покрашены давно: лента миниатюр на карточке
   товара (раздел выше), окно и текст отзыва (`reviews.css`), таблицы и чат
   панели (`admin-custom.css`). Палитра взята оттуда БЕЗ изменений -
   `rgba(201, 67, 110, .35)`, под курсором `.55`, жёлоб прозрачный, - иначе
   на одной странице оказались бы две разные полосы.

   Наборов правил два, потому что стандарта пока два: `scrollbar-*` понимают
   Firefox и свежие Chrome, `::-webkit-scrollbar` - Safari и Chrome. Кто не
   понял ни того ни другого, покажет системную полосу, и это не поломка.

   Полоса страницы шире внутренних (10 против 5): она одна на весь экран и по
   ней тянут мышью, а внутренние - короткие подсказки о том, что блок ещё
   прокручивается.

   ⚠ `scrollbar-gutter: stable both-edges` у `html` из раздела Г5 НЕ трогаем:
   на нём держится симметрия полей при переходах между страницами. `thin`
   сужает и полосу, и зарезервированный под неё жёлоб одновременно, поэтому
   симметрия сохраняется, меняется только ширина. Замер при окне 1440 до
   правки: поля колонки 39 слева и 24 справа плюс 15 точек полосы за краем,
   то есть по 39 с каждой стороны. */
html {
  scrollbar-width: thin;
  scrollbar-color: rgba(201, 67, 110, .35) transparent;
}

html::-webkit-scrollbar { width: 10px; }
html::-webkit-scrollbar-track { background: transparent; }
html::-webkit-scrollbar-thumb {
  background: rgba(201, 67, 110, .35);
  border-radius: 999px;
}
html::-webkit-scrollbar-thumb:hover { background: rgba(201, 67, 110, .55); }

/* ── Г24: мобильная полоса покупки на большом экране. ЗАПЛАТКА СНЯТА 31.08.

   Было: полоса строилась на любой ширине (модуль проверял наличие артборда
   `.dc-mobile` в разметке, а он есть всегда - его прячет css), дописывалась в
   `body` мимо артборда, а все её стили лежат внутри `@media (max-width: 768px)`.
   Выше 768 под подвалом карточки висел голый блок «1 330 ₽ 3 000 ₽ − 1 + ♡ В
   корзину» с рамками браузера: замер при 1440 давал 1420×117 и лишние 117
   точек длины страницы.

   Здесь стояло правило `@media (min-width: 768.02px) { .buybar { display: none } }`.
   Сессия телефона вылечила причину (коммит 5848bb8: полоса строится только
   при ПОКАЗАННОМ телефонном артборде), и правило стало мёртвым: замер 31.08
   при 1440 - полосы нет в разметке вовсе, у `body` нет класса `has-buybar`.
   Правило убрано, чтобы не путать следующего читателя.

   За тем, что блок не вернётся, следит прогон визуала: он падает, если полоса
   найдена и видима выше 768. */

/* ═══════════════════════════════════════════════════════════════════════════
 * МОБИЛЬНЫЕ ПРАВКИ. Сессия «Визуал телефона» пишет ТОЛЬКО ниже этой черты.
 *
 * Разделение по договорённости 30.08.2026: две сессии правят один файл, и
 * чтобы git сводил их сам, у каждой своя половина. Выше черты - десктоп,
 * ниже - телефон. Правила целиком: Документация/ПРАВИЛА-ПАРАЛЛЕЛЬНОЙ-РАБОТЫ.md
 * ═══════════════════════════════════════════════════════════════════════════ */

/* ─── М-Т01: цели под палец на телефоне ───────────────────────────────────
 *
 * Правило площадок: то, во что человек целится пальцем, не меньше 44 точек.
 * Обход витрины при 375 и 414 точках нашёл 19 целей мельче: крестик удаления
 * в корзине 26, плюс и минус 28, «Очистить корзину» высотой 13, стрелки
 * галереи 36, страницы каталога 40, подвал 16.
 *
 * ПРИЁМ: прозрачный слой `::after` поверх самой цели.
 *   - `position:absolute` вынимает слой из потока, поэтому соседи не сдвигаются
 *     ни на точку. Это же спасает цели с `display:grid` и `display:flex`:
 *     без `absolute` псевдоэлемент стал бы вторым элементом сетки и разъехал
 *     бы содержимое кнопки.
 *   - фона у слоя нет, поэтому кружки и плашки не разрастаются. Увеличить
 *     `padding` было нельзя: у большинства целей фон видимый, и подушка
 *     разнесла бы рисунок.
 *   - `position:relative` на самой цели нужен как точка отсчёта и на раскладку
 *     не влияет.
 *
 * Отступы у каждой цели СВОИ. Они не выдуманы, а посчитаны из замера
 * свободного места вокруг цели на живой витрине: слой доводится до 44 ровно
 * настолько, насколько можно, не залезая на соседнюю кнопку или ссылку.
 * Наложение двух зон нажатия хуже маленькой зоны: палец попадает в соседа.
 *
 * Где 44 не влезает физически, стоит максимум и подпись, сколько вышло.
 * Такие места собраны в отчёт как «нужна видимая правка».
 * ───────────────────────────────────────────────────────────────────────── */
@media (max-width: 768px) {

  /* ── Корзина ─────────────────────────────────────────────────────────── */

  /* Крестик удаления, 26x26. Слева в 8 точках ссылка на товар, на неё лезть
     нельзя: промах стоил бы человеку удалённой строки. Поэтому влево 6, а
     недостающее добираем вправо, там до края карточки 16 точек. */
  .dc-mobile [data-role="item-remove"] { position: relative; }
  .dc-mobile [data-role="item-remove"]::after {
    content: "";
    position: absolute;
    top: -9px; bottom: -9px; left: -6px; right: -12px;
  }

  /* Количество в строке заказа, 28x28. Между кнопками 28 точек, каждая берёт
     по 8, между зонами остаётся 12. Сверху и снизу свободно. */
  .dc-mobile [data-role="item-minus"],
  .dc-mobile [data-role="item-plus"] { position: relative; }
  .dc-mobile [data-role="item-minus"]::after,
  .dc-mobile [data-role="item-plus"]::after {
    content: "";
    position: absolute;
    inset: -8px;
  }

  /* «Вернуться в каталог» (16) и «Очистить корзину» (13) стоят в 14 точках
     друг от друга, и от кнопки «Оформить заказ» до низа карточки всего 79.
     На две цели по 44 нужно 88, поэтому до 44 здесь не дотянуться без
     раздвижки, а это уже видимая правка. Делим место без остатка: 37 и 38,
     между зонами 1 точка, мёртвых промежутков не остаётся. */
  .dc-mobile aside > a[href="/catalog/"] { position: relative; }
  .dc-mobile aside > a[href="/catalog/"]::after {
    content: "";
    position: absolute;
    top: -15px; bottom: -6px; left: 0; right: 0;
  }
  .dc-mobile [data-role="cart-clear"] { position: relative; }
  .dc-mobile [data-role="cart-clear"]::after {
    content: "";
    position: absolute;
    top: -7px; bottom: -18px; left: 0; right: 0;
  }

  /* «Вернуться в корзину» на оформлении, 346x16. Тот же вид ссылки, что в
     корзине, но соседей рядом нет: сверху 55, снизу 69. Поэтому здесь
     выходят полные 44, а не 37, как в корзине. */
  .dc-mobile aside > a[href="/cart/"] { position: relative; }
  .dc-mobile aside > a[href="/cart/"]::after {
    content: "";
    position: absolute;
    top: -14px; bottom: -14px; left: 0; right: 0;
  }

  /* ── Буквица в описании на телефоне ─────────────────────────────────────
   * Правка Павла 30.08 по скриншоту: «нет буквы большой, как на ПК».
   *
   * Причина не в стилях, а в РАЗНЫХ слотах у двух артбордов:
   *   ПК:       {{ product.description_first_html|safe }}
   *   телефон:  {{ product.description_first }}
   * Сервер кладёт буквицу в поле `_html` отдельным `<span>` (DROPCAP_STYLE в
   * services/storefront.py), а телефон печатает голое поле, без разметки.
   * Оба шаблона лежат в `generated/` и руками не правятся.
   *
   * Поэтому рисуем ту же букву средствами CSS через `::first-letter`. Значения
   * один в один из DROPCAP_STYLE, чтобы телефон и ПК не разъезжались.
   * `::first-letter` берёт и открывающую кавычку вместе с буквой, если абзац
   * начинается с неё, - это ровно то поведение, которое нужно.
   *
   * Опора на `.desc__body`: абзацы в него заворачивает storefront.js, и
   * цепляться надо за узел, который создаёт скрипт, а не за положение в
   * исходной разметке (грабля 30.08 с селектором через `>`). */
  .dc-mobile .desc__body p:first-of-type::first-letter {
    font-family: 'Playfair Display', Georgia, serif;
    font-size: 34px;
    line-height: 1;
    padding-right: 3px;
    color: #EE7598;
    font-weight: 900;
    vertical-align: -3px;
  }

  /* ── Лента миниатюр карточки ────────────────────────────────────────────
   * Просьба Павла 30.08: «миниатюры сделай актуальными для каждого товара».
   * Ленту собирает storefront.js из настоящих миниатюр (они лежат в скрытой
   * настольной копии страницы), а макетную полосу из пяти чужих снимков он
   * же прячет. Размеры, скругление и рамка активной взяты с той макетной
   * полосы один в один, чтобы вид не поехал.
   * Отличие одно и намеренное: лента прокручивается вбок. В макете стоял
   * `overflow:hidden`, и дальше пятого снимка было не добраться. */
  .gal-strip {
    display: flex;
    gap: 8px;
    margin: 10px 0 0;
    overflow-x: auto;
    overscroll-behavior-x: contain;
    scrollbar-width: none;
  }
  .gal-strip::-webkit-scrollbar { display: none; }

  .gal-strip__item {
    flex: 0 0 54px;
    height: 72px;
    padding: 0;
    border: 0;
    border-radius: 12px;
    overflow: hidden;
    background: #FFD6E4;
    cursor: pointer;
  }
  .gal-strip__item img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
  }
  .gal-strip__item[data-on] { box-shadow: 0 0 0 2px #EE7598; }

  /* ── Полоса покупки внизу карточки ──────────────────────────────────────
   * Просьба Павла 30.08: «на первом экране должна быть кнопка в корзину».
   * Замер на 390x664: кнопка начиналась на 954, влезть должна была в 619, а
   * всё кроме фотографии занимает 491. На снимок оставалось 116 точек, то
   * есть уменьшением картинки цель не бралась. Кнопка переехала в полосу
   * внизу экрана, как на Вайлдберриз и Озоне.
   *
   * Полоса стоит НАД нижним меню, а не вместо него. Сначала я меню прятал, и
   * вместе с ним с карточки уходили бейдж корзины и переход в неё: положил
   * товар и не видишь ни счётчика, ни куда идти (Павел 30.08 - «верни меню
   * нижнее»). Теперь на карточке два этажа, 62 плюс 62, и под них обе величины
   * ниже пересчитаны: нижнее поле страницы и подъём cookie-баннера.
   *
   * Вырез под жесты айфона остаётся на МЕНЮ: полоса больше не касается края
   * экрана, и второй раз его добавлять нельзя, иначе между полосой и меню
   * появится пустая щель. */
  .buybar {
    /* ⚠ Отступ снизу берётся ЗАМЕРОМ из `--bnav-h`, его ставит storefront.js.
       Формула `62px + env(safe-area-inset-bottom)` была НЕВЕРНОЙ: во всём
       проекте `box-sizing: border-box`, и у меню `min-height:62` считается
       ВМЕСТЕ с подушкой под вырез. Меню занимает не `62 + вырез`, а
       `max(62, содержимое + вырез)` - около 87 при вырезе 34, а полоса
       поднималась на 96. На айфоне между ними зияла щель со сквозным текстом
       страницы (Павел 30.08 - «блок с ценой и кнопкой отрывается»). На
       эмуляторе выреза нет, обе величины сходятся в 62, и щели не видно,
       поэтому и не поймал раньше. Запасное значение - на случай, если
       скрипт не успел. */
    display: flex;
    flex-direction: column;
    gap: 8px;
    position: fixed;
    left: 0;
    right: 0;
    bottom: var(--bnav-h, 62px);
    z-index: 85;
    padding: 9px 16px 10px;
    background: #FFF8FB;
    border-top: 1px solid #FFD6E4;
    /* Вторая тень - страховка от щели: сплошная полоса своего цвета на 24
       точки ВНИЗ. Если замер разойдётся с настоящей высотой меню хоть на
       пару точек, в просвете будет фон полосы, а не бегущий текст страницы.
       Меню стоит на том же этаже и позже в разметке, поэтому рисуется
       поверх и лишнего не видно. */
    box-shadow: 0 -6px 18px rgba(206, 140, 166, .22),
                0 24px 0 0 #FFF8FB;
  }

  /* Верхний ряд: цена и количество по краям. Нижний: избранное и корзина.
     В одну строку не собирается: на 375 точках кнопке остаётся около 90, а
     подпись «В КОРЗИНУ» прописными с разрядкой занимает больше сотни. */
  .buybar__top {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
  }
  .buybar__row {
    display: flex;
    align-items: center;
    gap: 10px;
  }

  /* Два этажа вместо одного: под меню место уже отдано правилом выше, здесь
     добавляем ещё столько же под полосу. Иначе конец страницы уезжает под неё. */
  /* Место под ОБА этажа, тоже замером. Иначе конец страницы уезжает под полосу. */
  /* Меню этажом выше полосы. Полоса дописана в конец разметки, поэтому при
     равном `z-index` рисовалась ПОВЕРХ меню, и её нижняя страховочная тень
     срезала значкам верхушки. Ниже шторки фильтров и панели меню (там 90),
     так что порядок этажей не ломается. */
  body.has-buybar .bnav { z-index: 86; }

  body.has-buybar { padding-bottom: calc(var(--bnav-h, 62px) + var(--buybar-h, 118px)); }
  body.has-buybar .cookie-banner { bottom: calc(var(--bnav-h, 62px) + var(--buybar-h, 118px)); }

  /* Цена. Начертание НЕ задаём: узел это `span[data-role="price"]`, и его
     уже переводит на читаемый гротеск общее правило выше по файлу. Свой у
     полосы только кегль, 38 туда не влезет.

     Правка Павла 30.08 «шрифт у цены поменяй как на пк версии»: раньше в
     полосе стоял `b`, общее правило написано под `span` и на `b` не
     действовало, поэтому цена оставалась старой антиквой из инлайнового
     стиля макета, а на всех остальных экранах она давно гротеск. */
  .buybar__price {
    display: flex;
    align-items: baseline;
    gap: 8px;
    min-width: 0;
  }
  .buybar__price [data-role="price"] {
    font-size: 24px !important;
    line-height: 1.1;
    color: #2E1D25;
    white-space: nowrap;
  }
  .buybar__price s {
    font-family: 'PT Mono', monospace;
    font-size: 11px;
    line-height: 1.3;
    color: #8A7079;
    white-space: nowrap;
  }
  /* У товара без скидки сервер присылает пустую строку, и пустой `s` всё
     равно занимал бы строку. */
  .buybar__price s:empty { display: none; }

  /* Избранное рядом с корзиной (Павел 30.08). Круг того же диаметра, что у
     сердечка на фото, и с тем же цветом. Заполненное состояние ♥ проставляет
     общая перерисовка сердец, она же ставит `data-fav`. */
  .buybar__fav {
    flex: 0 0 46px;
    width: 46px;
    height: 46px;
    border: 0;
    border-radius: 50%;
    background: #FFEBF2;
    color: #C9436E;
    font-size: 19px;
    line-height: 1;
    cursor: pointer;
    box-shadow: inset 2px 2px 6px rgba(206, 140, 166, .28);
  }
  .buybar__fav[data-fav] { background: #FFD6E4; }

  /* Количество прямо в полосе (Павел 30.08: «поменять кнопку в корзину так,
     чтоб можно было выбрать количество»). Вид и размеры сняты с такого же
     счётчика в панели покупки, чтобы человек узнавал элемент. Кружки 34, как
     в панели, а до 44 их доводит прозрачный слой: полоса и так занимает два
     этажа, растить её ещё и ради кнопок незачем. */
  .buybar__qty {
    display: flex;
    align-items: center;
    gap: 10px;
    flex: 0 0 auto;
    background: #FFEBF2;
    border-radius: 999px;
    padding: 5px 6px;
    box-shadow: inset 2px 2px 6px rgba(206, 140, 166, .28),
                inset -2px -2px 6px rgba(255, 255, 255, .95);
  }
  .buybar__qty button {
    position: relative;
    width: 34px;
    height: 34px;
    border: 0;
    border-radius: 50%;
    background: #FFF8FB;
    color: #2E1D25;
    font-size: 15px;
    line-height: 1;
    cursor: pointer;
  }
  .buybar__qty button::after {
    content: "";
    position: absolute;
    inset: -5px;
  }
  .buybar__qty span {
    font-family: 'PT Mono', monospace;
    font-size: 13px;
    min-width: 14px;
    text-align: center;
  }

  /* Цена и кнопка ушли с самой карточки: они повторяли полосу (Павел 30.08 -
     «убрать цену и кнопку в корзину с самой карточки, она дублирует нижнюю»).
     Прячем, а не удаляем: узлы остаются в разметке, и на них по-прежнему
     держатся общие механики. Скрытый счётчик количества хранит то же число,
     что в полосе (обе стороны обновляет `paintQty`), а перенос характеристик
     ищет панель покупки по кнопке и находит её.

     `!important` обязателен: у обоих блоков в разметке стоит инлайновый
     `display:flex`, а инлайн бьёт правило из файла при любом весе селектора.
     Без важности правило подходит по селектору, видно в стилях, и всё равно
     не действует. Третий раз за день эта же грабля. */
  .dc-mobile div:has(> [data-role="price"]),
  .dc-mobile div:has(> [data-role="add-to-cart"]) { display: none !important; }

  .buybar__btn {
    flex: 1;
    min-height: 46px;
    border: 0;
    border-radius: 999px;
    /* Тёмный розовый, а не светлый. Белым по #EE7598 контраст 2.74 - это ниже
       любого порога читаемости; по #C9436E выходит 4.65 и норма выполнена.
       Замер 03.09: эта кнопка оставалась ЕДИНСТВЕННОЙ в магазине на светлом
       розовом, плитки каталога и «Оформить заказ» уже были на тёмном.
       ⚠ Свечение НЕ трогаем: оно светло-розовое по всему магазину, у плиток
       ровно такое же (замер: заливка #C9436E, тень rgba(238,117,152,.4)).
       Тёмная заливка со светлым свечением - это приём витрины, а не оплошность. */
    background: #C9436E;
    color: #fff;
    /* Гарнитура главной кнопки магазина, а не моноширинная. Значения сняты
       замером с «Оформить заказ» на форме заказа: гротеск 15, вес 700,
       разрядка 0.9px, прописные. Кнопка полосы оставалась последней главной
       кнопкой на моноширинном 11 - том самом сочетании, которое 02.09
       вычищено из кабинета.
       ⚠ Разрядка в ПИКСЕЛЯХ, не в em. Прежние .16em при 11px давали 1.76px, а
       при 15px дали бы 2.4 - надпись расползлась бы. Пиксель не зависит от
       кегля и переживёт любой будущий переопределённый размер.
       Высоту это не меняет: у кнопки `min-height: 46px`, а строка 15x1.2 = 18
       в него укладывается с запасом, поэтому полоса остаётся 118 и посадка
       чата над ней не едет. */
    font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
    font-size: 15px;
    font-weight: 700;
    letter-spacing: .9px;
    text-transform: uppercase;
    cursor: pointer;
    box-shadow: 6px 6px 16px rgba(238, 117, 152, .45);
  }

  /* ── Карточка товара ─────────────────────────────────────────────────── */

  /* Стрелки галереи, 36x36. Уже `position:absolute`, своя точка отсчёта есть.
     `::before` у них занят уголком стрелки, поэтому слой вешаем на `::after`. */
  .gal-nav::after {
    content: "";
    position: absolute;
    inset: -4px;
  }

  /* Количество в карточке, 34x34. От кнопок корзины отличаются тем, что у них
     нет `data-role`: в корзине те же подписи, но там свои размеры и свои
     соседи, поэтому правила разведены, а не объединены по `aria-label`. */
  .dc-mobile button[aria-label="Меньше"]:not([data-role]),
  .dc-mobile button[aria-label="Больше"]:not([data-role]) { position: relative; }
  .dc-mobile button[aria-label="Меньше"]:not([data-role])::after,
  .dc-mobile button[aria-label="Больше"]:not([data-role])::after {
    content: "";
    position: absolute;
    inset: -5px;
  }

  /* «Показать полностью», высота 17. Ширина 150 уже достаточная, растим
     только вверх и вниз, там свободно. */
  .desc__more { position: relative; }
  .desc__more::after {
    content: "";
    position: absolute;
    top: -14px; bottom: -13px; left: 0; right: 0;
  }

  /* «Все» над блоком «Часто берут», 23x17. Прижата вправо `margin-left:auto`,
     слева до заголовка далеко, поэтому основной прирост влево. */
  .dc-mobile a[style*="margin-left:auto"][href="/catalog/"] { position: relative; }
  .dc-mobile a[style*="margin-left:auto"][href="/catalog/"]::after {
    content: "";
    position: absolute;
    top: -14px; bottom: -13px; left: -16px; right: -5px;
  }

  /* ── Каталог ─────────────────────────────────────────────────────────── */

  /* Страницы, 40x40, между ними 8. Берём по 2 с каждой стороны, между зонами
     остаётся 4. Опора - размеры из мобильного макета в самой разметке:
     класса у этих ссылок нет, а `partials/pagination.mobile.html` руками не
     трогаем, правка должна жить в стилях. */
  .dc-mobile a[style*="width:40px"][style*="border-radius:50%"] { position: relative; }
  .dc-mobile a[style*="width:40px"][style*="border-radius:50%"]::after {
    content: "";
    position: absolute;
    inset: -2px;
  }

  /* Строки рубрик в шторке фильтров, высота 35 при шаге 39. Здесь подушка
     уместна: фон у строки прозрачный, а отрицательное поле возвращает
     внешний размер, поэтому подписи остаются на прежних местах. Выходит 39,
     то есть весь просвет между строками становится нажимаемым и мёртвых
     полос не остаётся. До 44 мешает сам шаг списка. */
  .filter-drawer-row {
    padding-top: 8px;
    padding-bottom: 8px;
    margin-top: -2px;
    margin-bottom: -2px;
  }

  /* Чипы поводов. Слой убран: с правкой М-Т02 чип стал 44 своим размером,
     и накладывать сверху ещё 4 точки значит лезть в просвет к соседу. */
  .filter-drawer-chip::after { content: none; }

  /* Крестик шторки фильтров, 36x36. */
  .filter-drawer-x { position: relative; }
  .filter-drawer-x::after {
    content: "";
    position: absolute;
    inset: -4px;
  }

  /* ── Общее для всех страниц ──────────────────────────────────────────── */

  /* Бургер и кабинет в шапке, 42x42, не хватало ровно двух точек. */
  .dc-mobile [data-role="burger"],
  .dc-mobile a[aria-label="Кабинет"] { position: relative; }
  .dc-mobile [data-role="burger"]::after,
  .dc-mobile a[aria-label="Кабинет"]::after {
    content: "";
    position: absolute;
    inset: -1px;
  }

  /* Сердечко. В плитках каталога 36x36, в карточке товара 42x42, одно правило
     доводит первое до 44, второе до 50. Сердечко лежит поверх снимка, и слой
     отбирает у ссылки на товар 4 точки по краю: рядом с сердечком должно
     срабатывать сердечко. */
  .dc-mobile button[aria-label="В избранное"] { position: relative; }
  .dc-mobile button[aria-label="В избранное"]::after {
    content: "";
    position: absolute;
    inset: -4px;
  }

  /* Подвал страницы: ссылки в строку, между ними 14. Сверху и снизу свободно,
     поэтому по высоте выходит полные 44, по ширине берём по 7.

     ⚠ Ссылок стало ЧЕТЫРЕ (01.09 в подвал дописали «О нас»), и на узких
     экранах последняя переносится во второй ряд. Ряды стоят в 30 точках, сами
     ссылки 16, значит свободных между рядами ровно 14 - столько же, сколько
     берёт накладка, и зоны соседних рядов перекрываются на 14.
     ПРОВЕРЕНО ЗАМЕРОМ на 360, 375 и 414: ни одна ссылка не перехватывает углы
     чужой. На 414 подвал в одну строку; на 375 и 360 во второй ряд уходят
     «Контакты», и по горизонтали они не совпадают ни с одной ссылкой первого
     ряда, поэтому перекрытие приходится на пустое место.
     Уменьшать 14 нельзя: зона сразу станет ниже 44. Но если в подвал добавят
     ПЯТУЮ ссылку и второй ряд встанет под первым - перекрытие окажется на
     живых ссылках, и нажатие в полосе между рядами уйдёт в нижнюю. Тогда
     разносить ряды, а не резать накладку. */
  /* ⛔ ИСКЛЮЧЕНИЕ `.rvp__footer` ОБЯЗАТЕЛЬНО, не убирать.
     У страниц отзывов свой телефонный подвал, и лежит он ВНУТРИ артборда:
     `div.rvp > div.dc-mobile.rvp__foot > div.rvp__narrow > footer.rvp__footer`.
     То есть общий селектор его цепляет. Ссылки там 50 в высоту сами по себе
     (`reviews.css`), накладка им не нужна, а поверх она делала зону 62x78 и
     соседи начинали воровать друг у друга углы: замер на 375 показал ЧУЖУЮ
     ссылку в правых углах у «О нас» и «Оплаты».
     Поймано так: сначала я снял своё правило `.rvp footer > a`, а зоны
     остались раздутыми - цеплялось это, второе. Мораль: убирая накладку с
     чужого блока, проверять ВСЕ свои правила, которые до него дотягиваются. */
  .dc-mobile footer:not(.rvp__footer) > a { position: relative; }
  .dc-mobile footer:not(.rvp__footer) > a::after {
    content: "";
    position: absolute;
    top: -14px; bottom: -14px; left: -7px; right: -7px;
  }

  /* Крошки: ссылка «Главная» высотой 11. Ширина 52 уже больше 44, растим
     только по высоте. */
  div[style*="letter-spacing:.14em"] > a[href="/"] { position: relative; }
  div[style*="letter-spacing:.14em"] > a[href="/"]::after {
    content: "";
    position: absolute;
    top: -17px; bottom: -16px; left: 0; right: 0;
  }

  /* Крестик панели меню, 36x36. */
  .menu-drawer-x { position: relative; }
  .menu-drawer-x::after {
    content: "";
    position: absolute;
    inset: -4px;
  }

  /* Служебные ссылки внизу панели меню: три ряда с шагом 28, между рядами 12,
     между столбцами 16. Берём половину каждого просвета, выходит 28 в высоту.
     Больше нельзя, ряды начнут перехватывать нажатия друг у друга. */
  .menu-drawer-foot a { position: relative; }
  .menu-drawer-foot a::after {
    content: "";
    position: absolute;
    top: -6px; bottom: -6px; left: -8px; right: -8px;
  }

  /* Пока палец ведёт снимок, окно не должно прокручиваться и жестами
     браузера ничего делать: горизонталь наша. Щипок и вертикаль оставляем
     браузеру, иначе фотографию нельзя будет приблизить. */
  .lightbox { touch-action: pan-y pinch-zoom; }

  /* Соседний кадр во время свайпа в окне. Ставится абсолютом по ЗАМЕРУ
     текущего снимка: окно центрирует картинку флексом, у соседа свои
     пропорции, и попасть в то же место можно только по факту. `contain`,
     чтобы кадр другой формы не растягивало, пока он едет. Живёт только на
     время жеста, скрипт заводит и прячет его сам. */
  /* ── «Добавить к заказу» в корзине: ниже, и товар не обрезан ─────────────
   * Павел 30.08, третий заход. Первый (снимок квадратом) он забраковал не за
   * высоту, а за обрезку: у 3:4 снимка квадратная рамка срезает верх, и с
   * карточек уходили заголовок «КАРТА ЖЕЛАНИЙ» и сердце «для пар».
   * «Товар должен помещаться, а не быть обрезанным».
   *
   * Значит пропорцию рамки НЕ трогаем вовсе: она остаётся 3:4, как у самого
   * файла, и обрезать нечего в принципе. Высоту снимаем РАЗМЕРОМ снимка, а
   * не кадрированием. `contain` тут страховка на будущее: если приедет товар
   * со снимком другой формы, он ужмётся целиком, а не обрежется.
   *
   * `!important` не для веса: свойства заданы инлайном в разметке.
   */
  .dc-mobile .cross-card img {
    max-width: 96px;
    margin-left: auto;
    margin-right: auto;
    object-fit: contain !important;
    background: transparent !important;
  }

  /* Имя в две строки. Имена из кабинета Ozon по сто знаков занимали три,
     третья ничего не добавляет: полное имя человек видит на самой карточке,
     а здесь это подсказка «возьмите ещё вот это». */
  .dc-mobile .cross-card > div > a {
    display: -webkit-box !important;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
  }

  /* Снимок в отзыве открывается в окне, значит должен об этом говорить.
     Класс чужой (модуль отзывов), поэтому здесь только курсор и зона под
     палец, оформление снимка не трогаем. */
  .rv__photo { cursor: zoom-in; }

  .lightbox__ghost {
    position: absolute;
    object-fit: contain;
    border-radius: 18px;
    background: #FFF8FB;
    box-shadow: 0 30px 70px rgba(0, 0, 0, .45);
    pointer-events: none;
  }

  /* Крошки на телефонной карточке убраны (Павел 30.08 - «убрать»): строка
     повторяла название, которое стоит сразу под фотографией, занимала две
     строки и отодвигала первый экран. Опора `body:has(...)` это принятый в
     файле признак карточки товара: в каталоге крошки короткие и остаются.
     Разметка на месте, микроразметка хлебных крошек живёт отдельно в head и
     не задета. */
  body:has(img[data-role="gallery-main"]) .dc-mobile div[style*="letter-spacing:.14em"] {
    display: none;
  }

  /* Логотип в шапке: это ссылка на главную, высотой 34. Ширина 105 хватает,
     растим по высоте. До бургера и кабинета далеко, логотип стоит по центру
     через `margin:0 auto`. */
  .dc-mobile header a[href="/"] { position: relative; }
  .dc-mobile header a[href="/"]::after {
    content: "";
    position: absolute;
    top: -5px; bottom: -5px; left: 0; right: 0;
  }

  /* «Добавить обе» в блоке «Часто берут»: 43 в высоту, не хватало ровно
     одной точки. */
  .dc-mobile [data-role="add-both"] { position: relative; }
  .dc-mobile [data-role="add-both"]::after {
    content: "";
    position: absolute;
    top: -1px; bottom: -1px; left: 0; right: 0;
  }

  /* ── Кабинет ─────────────────────────────────────────────────────────── */
  /* Экраны кабинета оформлены отдельным файлом account.css, но правила про
     палец держим ЗДЕСЬ: своя половина custom.css это зона телефона, а
     account.css общий. Спор в каскаде не возникает: ни `position`, ни
     `::after` у этих правил в account.css не заданы, мы только дополняем. */

  /* Крошки кабинета, 52x11. Свой класс, поэтому под правило для крошек
     витрины (там опора на инлайновый стиль) они не попадали. */
  .acc-crumbs a { position: relative; }
  .acc-crumbs a::after {
    content: "";
    position: absolute;
    top: -17px; bottom: -16px; left: 0; right: 0;
  }

  /* Разделы кабинета (заказы, профиль, адреса, избранное), высота 36.
     Плашки переносятся на два ряда с просветом 8, берём по 4 сверху и снизу.
     По ширине они и так от 69 до 89, вширь не растим. */
  .acc-tab { position: relative; }
  .acc-tab::after {
    content: "";
    position: absolute;
    top: -4px; bottom: -4px; left: 0; right: 0;
  }

  /* ── Поле поиска в шапке ─────────────────────────────────────────────── */
  /* Полоса поиска 52 в высоту, а само поле внутри было 26: подушка в 13
     точек принадлежала ПОЛОСЕ, и тычок по её верхней части попадал в пустой
     div, курсор не вставал. Проверено `elementFromPoint`: возвращался
     родитель, а не поле.

     Единственная цель, где подушка уместнее слоя: у поля прозрачный фон, а
     `::after` внутрь `input` браузер не пускает вовсе. Подушку в 14 точек
     гасим отрицательным полем в 13, поэтому внешний размер поля прежний,
     полоса остаётся 52 и значок лупы не двигается, а нажимаемым становится
     весь прямоугольник полосы. */
  .dc-mobile input[aria-label="Поиск по каталогу"] {
    padding-top: 14px;
    padding-bottom: 14px;
    margin-top: -13px;
    margin-bottom: -13px;
  }

  /* ═════ М-Т02: видимые правки телефона (решение Павла 30.08) ═════════════
   * Разрешены отдельно от М-Т01: там вид не менялся, здесь меняется.
   * ══════════════════════════════════════════════════════════════════════ */

  /* ── 1. Кнопка «Показать N» прижата к низу шторки ────────────────────── */
  /* Было: кнопка последняя в прокручиваемой панели. При экране 667 точек
     панель занимает 107..667, а кнопка вставала на 637..682, то есть на 15
     точек ниже экрана. Человек выбирал повод и не видел, чем подтвердить.
     Нижнее меню шторку не перекрывает: у неё z-index 90, у меню 85.

     `bottom: 0`, а не 24 по нижней подушке панели: браузер отмеряет липкость
     от содержимого, а подушка остаётся за пределами отсчёта. При 24 кнопка
     вставала на 48 точек от низа, и под ней оставалась видимая полоса
     прокрутки. При 0 она стоит ровно там же, где стояла в конце списка,
     то есть в 24 точках от края, и при прокрутке до упора не прыгает.
     Замер: панель 107..667, кнопка 598..643 и вверху списка, и внизу. */
  .filter-drawer-apply {
    position: sticky;
    bottom: 0;
    z-index: 1;

    /* Полоса цвета панели вокруг кнопки: без неё список просвечивал бы возле
       пилюли и в подушке под ней.

       Сделана ТЕНЬЮ, а не слоем `::before`. Слой я сначала и написал, и он
       закрасил саму кнопку: `position: sticky` всегда заводит свой слой, а
       внутри слоя дети с отрицательным `z-index` рисуются ПОСЛЕ фона
       родителя, то есть поверх него. Розовая пилюля пропала, осталась белая
       надпись на светлом. Внешняя тень рисуется ЗА фоном элемента по
       определению, поэтому пилюля остаётся на месте.

       Первой в списке идёт РОДНАЯ тень кнопки из базового правила: список
       теней перекрывает свойство целиком, и без неё кнопка потеряла бы объём.
       Дальше ореол в 10 точек и полоса вниз на 24, до края панели. */
    box-shadow:
      6px 6px 16px rgba(238, 117, 152, .45),
      0 0 0 10px #FFF8FB,
      0 30px 0 24px #FFF8FB;
  }

  /* ── 2. Подсказки поиска не длиннее экрана ───────────────────────────── */
  /* Было: панель 1169 точек на экране 667, без своей прокрутки. С поднятой
     клавиатурой было видно два товара, до «Все N результатов» не добраться.
     Опора надёжная: скрипт вешает панель последним ребёнком полосы поиска,
     сразу за полем, поэтому берём соседа через `+`.
     `overscroll-behavior` не даёт прокрутке перескочить на страницу, когда
     список кончился. */
  input[aria-label="Поиск по каталогу"] + div > .dc-mobile {
    max-height: 58vh;
    overflow-y: auto;
    overscroll-behavior: contain;
  }

  /* ── 4. Согласие в оформлении читаемого размера ──────────────────────── */
  /* Было: 9px, самый мелкий текст на странице, при том что это согласие на
     обработку данных. Строка целиком 307x24.
     Подушка на тексте, а не на строке: строка это `label`, и подушка на ней
     сдвинула бы галочку. Текст фона не имеет, поэтому подушка не видна.

     `!important` обязателен: кегль задан ИНЛАЙНОВЫМ стилем прямо в разметке,
     а инлайн бьёт любое правило из файла независимо от веса селектора. Без
     важности правка молча не срабатывает: подушка применяется, кегль нет. */
  .dc-mobile input[name="consent"] + span {
    font-size: 12px !important;
    padding-top: 2px;
    padding-bottom: 2px;
  }

  /* ── 5. Поля цены в фильтрах 44 ──────────────────────────────────────── */
  /* Было 40. Внутрь `input` браузер псевдоэлемент не пускает, поэтому только
     подушка. Гасить её отрицательным полем нельзя: фон у поля видимый, и
     плашка должна вырасти вместе с зоной нажатия. */
  .filter-drawer-price input {
    padding-top: 13px;
    padding-bottom: 13px;
  }

  /* ── 6. Чипы поводов 44 и читаемый кегль ─────────────────────────────── */
  /* Было 29 в высоту при кегле 10px заглавными. Мелкой была не только зона
     нажатия, но и сама надпись. Раз шторку всё равно трогаем ради кнопки,
     делаем за один заход. */
  .filter-drawer-chip {
    font-size: 12px;
    padding: 15px 16px;
  }

  /* ── 3. Страницы отзывов ─────────────────────────────────────────────── */
  /* Эти страницы (`/reviews/my/`, `/reviews-rules/`, `/consent-reviews/`)
     собраны БЕЗ разделения на два артборда: своя разметка `.rvp` на оба
     экрана. Поэтому ни одно правило с опорой на `.dc-mobile` за них не
     цепляется, и цели там остались мелкими. Шапка у них всё же мобильная,
     её правила работают. */

  /* Крошки, 57x13. Ширина хватает, растим по высоте. */
  .rvp__crumbs a { position: relative; }
  .rvp__crumbs a::after {
    content: "";
    position: absolute;
    top: -16px; bottom: -15px; left: 0; right: 0;
  }

  /* ⛔ НАКЛАДКИ НА ПОДВАЛ ЭТИХ СТРАНИЦ БОЛЬШЕ НЕТ, и возвращать её не надо.
     Она стояла (`.rvp footer > a::after`, по 12 со всех сторон), пока на
     страницах отзывов был ОБЩИЙ сгенерированный подвал без мобильной версии:
     кегль 11, ссылки в три ряда, зоны по 42.
     01.09 сессия 5 дала этим страницам свой телефонный подвал
     `footer.rvp__footer` внутри `.rvp__narrow`, и зоны там уже 44 и больше,
     сделаны в `reviews.css`. Замер на 375 с моей накладкой поверх: коробки
     ссылок 48x50...70x50, зона с накладкой 74 по высоте, соседние зоны
     налезали друг на друга на 22. Нажатия это не путало, но оформление одного
     блока шло из двух файлов сразу.
     Сузить правило до настольного подвала, как предлагали, нельзя: весь этот
     раздел лежит внутри `@media (max-width: 768px)`, на ПК он не работает
     вовсе, и `.rvp .dc-desktop footer > a` был бы мёртвым кодом. */

  /* ⛔ Ссылкам в абзаце `.rvp__links` слой НЕ вешаем, и вот почему.

     Они строчные и переносятся на две строки. У строчного элемента прямоугольник
     это объединение всех его строк, и абсолютный слой накрывает ЦЕЛЫЙ этот
     прямоугольник, включая чужой текст между строками и соседнюю ссылку.
     Проверка тычком показала ровно это: центр «Согласия» возвращал абзац, а
     верх «Политики» возвращал соседнюю ссылку. То есть палец попадал не туда,
     куда целился, и это на двух юридических ссылках сразу.

     Выигрыш был 4 точки, цена - промахи между ссылками. Правило снято.
     До 44 такие ссылки доводятся только превращением абзаца в список блочных
     ссылок, а это разметка сессии 10. Вынесено в отчёт Павлу. */
}


/* ─── Полоса прокрутки страницы в цвет магазина ────────────────────────────
 *
 * Просьба соседней сессии 31.08: у отзывов, у окна правил и в панели полосы
 * уже покрашены, а у самой страницы оставалась системная серая.
 *
 * Цвет НЕ свой: `rgba(201, 67, 110, .35)` на прозрачном уже стоит в трёх
 * местах проекта - у ленты миниатюр карточки выше по этому файлу, в
 * `reviews.css` и в `admin-custom.css`. Заводить рядом вторую палитру нельзя:
 * на одной странице оказались бы две разные полосы.
 *
 * Наборов правил два, потому что стандарта пока два: `scrollbar-*` понимают
 * Firefox и свежие Chrome, `::-webkit-scrollbar` - Safari и Chrome.
 *
 * ⚠ `scrollbar-gutter: stable both-edges` у `html` стоит ВЫШЕ, в разделе Г5,
 * и здесь не трогается: на нём держится симметрия полей при переходах между
 * страницами. Полоса становится тоньше, значит и жёлоб под неё уже, но он
 * по-прежнему одинаков с обеих сторон и одинаков на всех страницах.
 *
 * ⚠ Правило ОБЩЕЕ для всей витрины, а не мобильное, хотя и лежит ниже черты
 * мобильных правок: эту половину файла отдали мне под него. Его законное
 * место - рядом с Г5. Если сессия 1 когда-нибудь напишет своё правило полосы
 * выше черты, победит это, потому что оно позже в файле; тогда одно из двух
 * надо убрать, а не держать оба.
 * ─────────────────────────────────────────────────────────────────────── */
html {
  scrollbar-width: thin;
  scrollbar-color: rgba(201, 67, 110, .35) transparent;
}

html::-webkit-scrollbar { width: 10px; }
html::-webkit-scrollbar-track { background: transparent; }
html::-webkit-scrollbar-thumb {
  background: rgba(201, 67, 110, .35);
  border-radius: 999px;
}


/* ─── Корзина на телефоне: лупа вместо полосы поиска ───────────────────────
 *
 * Павел 31.08. Полосу поиска прячем, в ряд шапки встаёт лупа, нажатие
 * раскрывает ту же полосу. Состояние ставит storefront.js в `data-search`,
 * он же служит признаком «это корзина»: на других страницах атрибута нет и
 * ни одно правило отсюда не срабатывает.
 * ─────────────────────────────────────────────────────────────────────── */

/* Свёрнутое состояние: полоса поиска это второй ребёнок шапки.

   `!important` обязателен: `display:flex` у полосы стоит ИНЛАЙНОМ, и его же
   переписывает скрипт подсказок поиска, когда вешает на полосу свой оверлей.
   Инлайн бьёт правило из файла при любом селекторе: без важности правило
   подходит, видно в стилях и молча не работает. */
.dc-mobile header[data-search="closed"] > div:nth-child(2) { display: none !important; }

/* Логотип центруем абсолютом. В ряду стало четыре элемента вместо трёх, и
   его `margin:0 auto` увёл бы логотип влево от середины. Бургер прижимаем
   к левому краю, лупа и кабинет уходят вправо сами. */
.dc-mobile header[data-search] > div:first-child { position: relative; }
.dc-mobile header[data-search] > div:first-child > a[href="/"] {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  margin: 0;
}
.dc-mobile header[data-search] > div:first-child > [data-role="burger"] {
  margin-right: auto;
}

/* Порядок в ряду задаём флексом, а НЕ местом вставки в разметку. Ссылку
   кабинета в шапку дописывает свой скрипт, и когда он это делает
   относительно нас - не наше дело: при вставке лупа оказалась за кабинетом,
   хотя код ставил её перед. `order` от этого не зависит вовсе. */
.dc-mobile header[data-search] .hdr-search { order: 1; }
.dc-mobile header[data-search] > div:first-child > a[aria-label="Кабинет"] { order: 2; }

/* Лупа тем же кружком, что бургер и кабинет: значения сняты с их инлайнового
   стиля в разметке, чтобы три кнопки в ряду были одинаковыми. */
.dc-mobile .hdr-search {
  position: relative;
  width: 42px;
  height: 42px;
  flex: 0 0 42px;
  border: 0;
  border-radius: 50%;
  background: #FFF8FB;
  box-shadow: 6px 6px 14px rgba(206, 140, 166, .28),
              -6px -6px 14px rgba(255, 255, 255, .9);
  display: grid;
  place-items: center;
  font-size: 16px;
  line-height: 1;
  cursor: pointer;
}

/* Зона под палец до 44, как у соседних кнопок ряда. */
.dc-mobile .hdr-search::after {
  content: "";
  position: absolute;
  inset: -1px;
}

/* Открытая лупа отмечена вдавленной тенью: понятно, что полоса раскрыта ею. */
.dc-mobile header[data-search="open"] .hdr-search {
  background: #FFEBF2;
  box-shadow: inset 2px 2px 6px rgba(206, 140, 166, .28);
}


/* ─── «Оформить заказ» на телефоне: читаемая подпись ───────────────────────
 *
 * Павел 31.08: «кнопку оформить заказ сделай ярче выраженной, шрифт какой-то
 * не контрастный». Это не вкусовщина, а считается: белый текст на #EE7598
 * даёт контраст 2,74 : 1 при норме 4,5 для обычного текста. Подпись в 11
 * точек с разрядкой .18em на светло-розовом читалась плохо.
 *
 * Фон меняем на #C9436E - тот же акцент, что уже стоит у кнопки «В корзину»
 * в плитках каталога, у бейджа корзины и у активного пункта нижнего меню.
 * Своего цвета не завожу. С ним контраст 4,65 : 1, норму проходит.
 *
 * Кегль 11 -> 14 и разрядка .18em -> .08em: широкая разрядка на мелком моно
 * растаскивает слово и мешает читать.
 *
 * Жирность НЕ трогаю: у PT Mono в проекте только начертание 400, и
 * `font-weight: 700` браузер подделал бы размазыванием.
 *
 * Целюсь по `pulseSoft` в ИНЛАЙНОВОМ стиле кнопки: класс `scp2087f3` носят
 * ещё «В каталог», «Мой заказ», «Показать» и «Добавить обе», а пульсация
 * стоит ровно у двух кнопок «Оформить заказ» - в корзине и на оформлении.
 *
 * `!important` потому, что фон, кегль и разрядка заданы инлайном.
 * ─────────────────────────────────────────────────────────────────────── */
@media (max-width: 768px) {
  .dc-mobile button.scp2087f3[style*="pulseSoft"] {
    background: #C9436E !important;
    /* Жирная подпись (Павел 31.08). Начертание меняем на тот же гротеск, что
       уже стоит у цены: у PT Mono в проекте есть ТОЛЬКО вес 400, и
       `font-weight: 700` на нём браузер подделал бы размазыванием - вышло бы
       грязнее, а не жирнее. У гротеска жирный настоящий. */
    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Arial, sans-serif !important;
    font-weight: 700 !important;
    font-size: 15px !important;
    letter-spacing: .06em !important;
    box-shadow: 6px 6px 16px rgba(201, 67, 110, .45);
  }
}


/* ─── Кнопка «В корзину» в плитке: без поворота на касании ─────────────────
 *
 * Павел 31.08: «проблемы с анимацией кнопки, такие же были на пк, надо убрать
 * это». На телефоне кнопка после нажатия разворачивалась боком и вырастала.
 *
 * Виновато правило из макета `.scpe170b6:hover { transform: scale(1.15)
 * rotate(90deg) !important }`: оно нарисовано для прежнего кружка с плюсом.
 * Сессия 1 его уже гасит, но ВНУТРИ `@media (hover: hover)` - на телефоне
 * такого ховера нет, значит и гашение не работает. А сам макетный `:hover`
 * никакой медиазапросом не прикрыт и прекрасно «залипает» после касания.
 *
 * Гасим ровно там, где настоящего наведения нет. На большом экране ничего не
 * меняется: там работает правило сессии 1.
 * ─────────────────────────────────────────────────────────────────────── */
@media (hover: none) {
  /* `:not(:active)` здесь ОБЯЗАТЕЛЬНО, и вот почему. Правило нажатия от
     сессии 1 (`:active { transform: scale(.96) }`) стоит в файле НАМНОГО
     раньше моего, а вес у селекторов одинаковый. Без `:not(:active)` во
     время нажатия совпадают оба, побеждает то, что позже, то есть моё - и
     кнопка переставала отзываться вовсе. Павел 31.08: «кнопка себя ведёт
     как-то странно, там должна быть анимация». */
  [data-slug] button[aria-label="В корзину"]:hover:not(:active),
  [data-slug] button[aria-label="В корзину"]:focus:not(:active) {
    transform: none !important;
  }

  /* Нажатие повторяем явно, чтобы порядок правил в файле больше ничего не
     решал: захочет кто-то дописать ниже - это правило всё равно рядом. */
  [data-slug] button[aria-label="В корзину"]:active {
    transform: scale(.96) !important;
  }
}

/* Отклик на добавление: короткий подскок кнопки.
 *
 * Он заменяет галочку, которую убрали 31.08: та ломала кнопку, потому что
 * подпись рисует псевдоэлемент, а галочка писалась в текст. Здесь ничего не
 * подменяется, двигается только сама кнопка.
 *
 * Метку `data-added` ставит storefront.js на полсекунды.
 *
 * ⚠ Двигаем ОТДЕЛЬНОЕ свойство `scale`, а не `transform`, и это не каприз.
 * Выше по кнопке стоит `transform: none !important` (им гасится поворот из
 * макета), а важное правило автора в каскаде СИЛЬНЕЕ анимации - обратный
 * порядок был бы верен только для инлайнового стиля, как это вышло с
 * kenburns в галерее. Проверено замером: подскок через `transform` не
 * двигался вовсе, `transform` так и оставался `none`.
 * `scale` - самостоятельное свойство, макетное правило его не трогает,
 * поэтому подскок идёт, а поворот остаётся погашенным. */
@keyframes cartPop {
  0%   { scale: .96; }
  45%  { scale: 1.06; }
  100% { scale: 1; }
}

[data-slug] button[aria-label="В корзину"][data-added] {
  animation: cartPop .42s ease-out;
}


/* ─── Количество на плитке каталога (телефон) ──────────────────────────────
 *
 * Павел 31.08: «сделай выбор количества до добавления, отдельной строкой».
 *
 * Счётчик виден всегда и стоит СВОЕЙ строкой над кнопкой «В корзину». Первый
 * вариант подменял кнопку счётчиком после добавления - забраковано: кнопка
 * пропадала под пальцем, и нажатие читалось как «не работает».
 *
 * Вид не придуман, а скопирован с эталонного счётчика карточки товара
 * (замер: коробка #FFEBF2, радиус 999, padding 5/6, две внутренние тени;
 * кнопки 34x34 кружком #FFF8FB, 15px; число 'PT Mono' 13px #2E1D25).
 * Розовой пилюлей его делать нельзя: рядом кнопка того же цвета, и человек
 * перестанет понимать, где кнопка покупки.
 *
 * Разметку строит storefront.js, здесь только вид.
 * ─────────────────────────────────────────────────────────────────────── */

.dc-mobile .tile-qty {
  /* Ряд с ценой у плитки это flex с переносом, и кнопка занимает в нём
     отдельную строку целиком. Счётчик встаёт туда же, строкой выше.
     Расстояние до кнопки даёт `gap` самого ряда, своего отступа не нужно. */
  flex: 1 0 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  padding: 5px 6px;
  border-radius: 999px;
  background: #FFEBF2;
  box-shadow: inset 2px 2px 6px rgba(206, 140, 166, .28),
              inset -2px -2px 6px rgba(255, 255, 255, .95);
}

.dc-mobile .tile-qty button {
  position: relative;
  flex: 0 0 34px;
  width: 34px;
  height: 34px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: #FFF8FB;
  color: #2E1D25;
  font: 400 15px/1 -apple-system, "Segoe UI", system-ui, sans-serif;
  cursor: pointer;
}

/* Зона под палец до 44 прозрачной накладкой: сама кнопка остаётся кружком
   34, как на карточке товара, а раскладка не едет - накладка вне потока. */
.dc-mobile .tile-qty button::after {
  content: "";
  position: absolute;
  top: -5px;
  bottom: -5px;
  left: -5px;
  right: -5px;
}

.dc-mobile .tile-qty button:active {
  background: #FFD6E4;
}

.dc-mobile .tile-qty span {
  /* Ширина фиксирована, иначе переход с «1» на «10» дёргает оба кружка. */
  min-width: 26px;
  text-align: center;
  font: 400 13px/1 "PT Mono", ui-monospace, "Courier New", monospace;
  color: #2E1D25;
}


/* ─── Зоны под палец: добор по итогам автотеста визуала ────────────────────
 *
 * Прогон 31.08 по 11 страницам на 375, плюс 360x640 и 414x896. Пять целей
 * оказались ниже 44 точек, все пять добираются здесь. Правки НЕВИДИМЫЕ:
 * ни одна не двигает раскладку и не меняет вид элемента.
 *
 * Что НЕ трогаем и почему:
 *   - «Вернуться в каталог» (37) и «Очистить корзину» (38) в корзине уже
 *     стоят на своём потолке, см. раздел выше: на две цели по 44 нужно 88, а
 *     места там 79, и раздвижка была бы видимой правкой;
 *   - «офертой» и «политикой обработки данных» в оформлении заказа - строчные
 *     ссылки ВНУТРИ предложения, и обе лежат в `label`, нажатие по которому
 *     ставит галочку согласия. Растянуть их до 44 значит накрыть эту самую
 *     галочку. Нажимаются обе, проверено замером;
 *   - названия товаров на плитках: ссылка идёт четырьмя строками, общая рамка
 *     123x81, нажимается целиком. Первый прогон теста считал их мелкими -
 *     ошибка была в тесте, а не в вёрстке.
 * ─────────────────────────────────────────────────────────────────────── */

/* Телефон в реквизитах на «Оплате»: 96x11, кегль 10, строка 19.
   Отступ СТРОЧНОМУ элементу высоту строки не меняет, но рамку нажатия
   растягивает - раскладка не двигается ни на точку. Отступ безопасен ещё и
   потому, что в этом абзаце ссылка одна: накрывать нечего, соседних целей
   рядом нет. */
.dc-mobile p a[href^="tel:"] {
  padding-top: 17px;
  padding-bottom: 16px;
}

/* Почта на «Контактах»: 307x29, блочная, запас сверху 112 и снизу 188.
   Отступ плюс такой же отрицательный внешний - высота нажатия 44, положение
   на экране прежнее. */
.dc-mobile a[href^="mailto:"] {
  padding-top: 8px;
  padding-bottom: 7px;
  margin-top: -8px;
  margin-bottom: -7px;
}

/* «Маршрут» на «Контактах»: 154x40, не хватает четырёх точек.
   Здесь именно накладка, а не отступ: у кнопки тёмная заливка и свой
   `padding:12px` в разметке, и добор отступом СДЕЛАЛ БЫ ПИЛЮЛЮ ВЫШЕ - это
   уже видимая правка, которой не просили. */
.dc-mobile a[href*="yandex.ru/maps"] { position: relative; }
.dc-mobile a[href*="yandex.ru/maps"]::after {
  content: "";
  position: absolute;
  top: -2px; bottom: -2px; left: 0; right: 0;
}

/* Поле промокода в корзине: 275x20, запас сверху 305, снизу 54.
   У поля `box-sizing: border-box`, но высота у него не задана, поэтому
   отступ высоту прибавляет; отрицательным внешним возвращаем строку на
   место. Заодно в поле становится легче попасть пальцем, а не только
   прочитать. */
.dc-mobile input[name="promo_code"] {
  padding-top: 13px;
  padding-bottom: 13px;
  margin-top: -13px;
  margin-bottom: -13px;
}

/* Название товара в блоке «часто берут вместе»: 129x34 внутри своей коробки
   в 54 точки, то есть по 10 свободных сверху и снизу. Добираем по 5.
   ⚠ ИМЕННО ОТСТУП, накладка здесь НЕ РАБОТАЕТ. На этой ссылке моё же правило
   обрезки в две строки ставит `overflow: hidden`, и оно режет собственный
   `::after` элемента: по стилям зона считалась 44, а замер на живой странице
   показал прежние 34 - точки над и под ссылкой отдавались родителю. Отступ
   лежит ВНУТРИ коробки элемента, обрезка его не трогает.
   Третью строку отступ не открывает: строк ровно две, их держит
   `-webkit-line-clamp`, а не высота коробки. Проверено замером после правки:
   ссылка 129x44, строк две, карточка 262 и блок 373 как были. */
.dc-mobile .cross-card > div > a {
  padding-top: 5px;
  padding-bottom: 5px;
  margin-top: -5px;
  margin-bottom: -5px;
}


/* ─── Кнопки карточки адреса в кабинете: зона под палец 44 ─────────────────
 *
 * `/account/addresses/`, замер 03.09 при 320: «Изменить» 101x32, «Удалить»
 * 91x32, «Сделать основным» 170x32 при норме 44.
 *
 * ПОЧЕМУ НЕ ОДНОЙ НАКЛАДКОЙ. Между рядами кнопок 6 точек, а ряды пересекаются
 * по горизонтали: зона в 44 отобрала бы нажатия у соседнего ряда. Проверено.
 *
 * ПОЧЕМУ НЕ `min-height`. У неосновного адреса кнопки стоят в ТРИ ряда, и
 * прибавка вышла бы 36 точек, а не 12: карточка длинного адреса 301 -> 337.
 *
 * ЧТО СДЕЛАНО. Разводим ряды до 12 и добираем накладкой по 6: зоны смыкаются
 * ВСТЫК, а не внахлёст, и никто ни у кого нажатия не отбирает. Замер после
 * правки: все пять кнопок дают 44, карточка основного адреса 175 не
 * изменилась вовсе (там один ряд), длинного 301 -> 313.
 *
 * ⚠ КЛАСС УДВОЕН НАМЕРЕННО, это не опечатка. `custom.css` подключён ДО
 * `account.css` (порядок: fonts, storefront, custom, account, chat), а там у
 * этого блока стоит `gap: 6px` СОКРАЩЁННЫМ свойством, то есть оно задаёт и
 * `row-gap`. Специфичность 0,1,0 у обоих, при равной выигрывает тот, кто ниже
 * по порядку подключения - `account.css`. Медиазапрос веса НЕ добавляет.
 * Удвоение даёт 0,2,0 и снимает зависимость от порядка файлов. Проверено
 * замером: без удвоения `row-gap` остаётся 6px. Поймала сессия 1 до выката.
 *
 * ⚠ `column-gap` остаётся 6: на нём держится разбивка кнопок по рядам
 * (101 / 170 / 91), тронешь - поедет перенос.
 *
 * Селектор из зоны сессии 1 (`account.css`), но правка чисто мобильная,
 * поэтому живёт здесь. Их файл не тронут.
 * ─────────────────────────────────────────────────────────────────────── */
@media (max-width: 768px) {
  .acc-address__tools.acc-address__tools {
    row-gap: 12px;
    column-gap: 6px;
  }

  .acc-address__tools .acc-btn { position: relative; }

  .acc-address__tools .acc-btn::after {
    content: "";
    position: absolute;
    top: -6px; bottom: -6px; left: 0; right: 0;
  }
}

/* ─── М-Т03: кабинет на телефоне читается обычным шрифтом ──────────────────
 *
 * Просьба Павла 03.09.2026 со скриншотами: «проверить профиль в моб версии,
 * шрифты не подходят, сделать обычные шрифты как на карточках товаров».
 *
 * ЗАМЕР ЭТАЛОНА (страница товара, 375 точек, 03.09):
 *   название товара   -apple-system 24/700
 *   заголовок раздела -apple-system 20/900
 *   абзац описания    -apple-system 15/400
 *   цена              -apple-system 21/700
 *   имя на плитке     -apple-system 15/500
 *   мелкие служебные подписи ОСТАЮТСЯ моноширинными: «Размеры, мм» PT Mono 11,
 *   скидка «−34%» PT Mono 9, звёзды PT Mono 10.
 * То есть на карточке товара засечных шрифтов в тексте нет вообще, а PT Mono
 * живёт только в подписях в пару слов.
 *
 * ЗАМЕР КАБИНЕТА ДО ПРАВКИ (те же 375 точек):
 *   .acc-title       Playfair Display 34/900
 *   .acc-card__title Playfair Display 21/700
 *   .acc-empty__title Playfair Display 24/900
 *   .acc-main и всё, что от неё наследует (примечания карточек, значения
 *   фактов, подсказки, переключатели, поля ввода) PT Serif 13...17
 * Соседняя страница «Мои отзывы» уже переведена на обычный шрифт
 * (.rvp__title -apple-system 34/900), поэтому два соседних экрана кабинета
 * набраны по-разному, что Павел и увидел.
 *
 * ЧТО ДЕЛАЕМ: гарнитуру меняем, кегль и насыщенность НЕ трогаем. Размеры
 * подобраны раньше и проверены замером, менять их никто не просил.
 *
 * ЧТО НЕ ТРОГАЕМ НАМЕРЕННО:
 *   - .acc-crumbs, .acc-tabs__label, .acc-tabs__who, .acc-field__label,
 *     .acc-fact__label - это те самые подписи в пару слов, у карточки товара
 *     такие тоже моноширинные. Убрать их значит уйти ОТ эталона, а не к нему;
 *   - .acc-avatar и .acc-menu__link - это кнопка кабинета в шапке, она стоит
 *     на ВСЕХ страницах, включая карточку товара, и уже одинакова везде;
 *   - .acc-eyebrow i - курсивный росчерк обложки, украшение, а не текст.
 *
 * ⚠ КЛАССЫ УДВОЕНЫ НАМЕРЕННО. `custom.css` подключён ДО `account.css`
 * (порядок: fonts, storefront, custom, account, chat), специфичность у
 * одиночного класса равная, и при равной выигрывает файл ниже по порядку.
 * Медиазапрос веса НЕ добавляет. Удвоение даёт 0,2,0 и снимает зависимость
 * от порядка подключения. Проверено вставкой пробного стиля НА МЕСТО
 * custom.css в head живой страницы, а не в конец.
 *
 * ⚠ .acc-input задан сокращённым свойством `font: 400 17px/1.4 <гарнитура>`.
 * Отдельное `font-family` ниже перебивает только гарнитуру, кегль остаётся
 * тот же. Проверено: 16px до и после.
 *
 * ЗАМЕР ПОСЛЕ (375 точек, прод):
 *   профиль:  карточка 773 -> 750, примечание 116 -> 93 (на строку меньше),
 *             подпись переключателя 45 -> 23, страница 3042 -> 2998;
 *   адреса:   карточки 175 и 245 БЕЗ изменений, все пять кнопок 101/91/101/
 *             170/91 при высоте 32, ряды 32 и 77 - разбивка по рядам и зоны
 *             под палец от 03.09 целы;
 *   заказы:   пустой блок 352, заголовок 55, абзац 47, страница 1013 - всё
 *             без изменений.
 *   Горизонтальной прокрутки нет ни на одной из трёх.
 *
 * Селекторы из зоны сессии 1 (`account.css`), но правка чисто мобильная,
 * поэтому живёт здесь. Их файл не тронут. На ПК кабинет остаётся засечным:
 * это их решение, Павлу сказано отдельно.
 * ─────────────────────────────────────────────────────────────────────── */
@media (max-width: 768px) {
  .acc-main.acc-main,
  .acc-switch.acc-switch,
  .acc-input.acc-input,
  .acc-title.acc-title,
  .acc-card__title.acc-card__title,
  .acc-empty__title.acc-empty__title,
  .acc-order__num.acc-order__num,
  .acc-order__total.acc-order__total,
  .acc-line__title.acc-line__title,
  .acc-line__sum.acc-line__sum {
    font-family: var(--acc-ui-font);
  }
}

/* ─── М-Т04: «Мои отзывы» встают в ту же сетку, что кабинет ────────────────
 *
 * Павел 03.09.2026, два скриншота подряд: «прыгает всё, такого никогда не
 * должно быть ни в одном проекте», «отступы у отзывов и остальных разные».
 *
 * ЧТО БЫЛО. Ряд вкладок кабинета ведёт на пять адресов. Четыре из них рисует
 * `pages/account.html` в сетке `.acc-*`, пятый - `reviews/my.html` в своей
 * сетке `.rvp__*`. Обе сетки нарисованы отдельно и НЕ СОВПАДАЮТ. Замер на
 * проде при 375, кабинет против отзывов:
 *
 *   боковое поле колонки      16   против  24   (ширина 343 против 327)
 *   нижнее поле колонки       30   против  40
 *   кегль крошек              10   против  11
 *   верхнее поле крошек        0   против  34
 *   крошки по вертикали      146   против 162
 *   пунктир под заголовком  скрыт  против  виден (высота 34 против 46)
 *   заголовок по вертикали   180   против 194
 *   ряд вкладок              232   против 270
 *   первая карточка          421   против 459, поля 34/20 против 38/28
 *
 * То есть при переходе «Избранное» -> «Отзывы» страница уезжала на 8 точек
 * вбок и на 38 вниз ЦЕЛИКОМ. Это и видно как прыжок: человек нажимает вкладку
 * в ряду, а ряд под пальцем перескакивает на новое место.
 *
 * Сами чипы НЕ виноваты: замер дал одинаковые 68/80/69/91/73 при 39 на обеих
 * страницах, активный не шире неактивного, перенос на второй ряд одинаковый.
 * Проверено отдельно, потому что первым делом подозрение падает на них.
 *
 * ЧТО ДЕЛАЕМ. Приводим отзывы К КАБИНЕТУ, а не наоборот: кабинет это четыре
 * страницы из пяти и хозяин самого ряда вкладок. Пять свойств, все про
 * геометрию. Кегли и цвета текста внутри карточки не трогаем: они не прыгают.
 *
 * ЗАМЕР ПОСЛЕ (375, прод), отзывы против кабинета:
 *   крошки     343x16 y146 x16  =  343x16 y146 x16
 *   заголовок  343x34 y180 x16  =      x34 y180 x16
 *   ряд        343x169 y232 x16 =  343x169 y232 x16
 *   чипы       68/80/69 y272, 91/73 y320  на обеих страницах
 *   карточка   343 y421 x16     =  343 y421 x16
 *   Горизонтальной прокрутки нет.
 *
 * ⚠ ОБЛАСТЬ ОГРАНИЧЕНА ПРЕДКОМ `.rvp` НАМЕРЕННО. `.rv__empty` живёт ещё и в
 * блоке отзывов НА КАРТОЧКЕ ТОВАРА: правка без предка переставила бы поля и
 * там, а на карточке товара блок стоит правильно и о нём не просили.
 * Заодно предок даёт специфичность 0,2,0 против 0,1,0 у `reviews.css`, и
 * правка перестаёт зависеть от порядка подключения. Порядок на этой странице
 * замерен: fonts, storefront, custom, account, reviews, chat-site, то есть
 * `reviews.css` подключён ПОЗЖЕ моего файла и при равном весе выигрывал бы.
 *
 * Селекторы из зоны сессии 5 (`reviews.css`), но правка чисто мобильная,
 * поэтому живёт здесь, их файл не тронут. Сессии 5 отправлено сообщение с
 * этими же цифрами: если они выровняют сетку у себя, блок станет пустым
 * повтором и его можно снять.
 *
 * ⚠ На ПК тот же разъезд остаётся: это `reviews.css` и `account.css`, зоны
 * соседей. Павлу сказано отдельно.
 * ─────────────────────────────────────────────────────────────────────── */
@media (max-width: 768px) {
  .rvp .rvp__main { padding: 0 16px 30px; }

  .rvp .rvp__crumbs {
    font-size: 10px;
    margin: 18px 0;
  }

  .rvp .rvp__title { margin-bottom: 18px; }

  /* Кабинет прячет свой пунктир под заголовком на телефоне
     (`.acc-title::after { display: none }` в мобильной ветке account.css).
     Здесь он оставался видимым и добавлял заголовку 12 точек высоты. */
  .rvp .rvp__title::after { display: none; }

  .rvp .rv__empty {
    padding: 34px 20px;
    margin-bottom: 22px;
  }
}
