/* ==========================================================================
   СЛОВНОВЧЕРА — design system
   Single stylesheet by design (Task 6 brief): grows across later tasks.
   ========================================================================== */

/* --------------------------------------------------------------------------
   Fonts (self-hosted, one @font-face per file/weight — see Task 6 note:
   some binaries carry internal name-table strings like "Manrope Medium" /
   "Unbounded SemiBold" rather than base-family-plus-weight, so we do not
   rely on that metadata; each block declares its own explicit font-weight
   under a shared font-family name).
   -------------------------------------------------------------------------- */

/* Onest (SIL OFL) — self-hosted woff2, one @font-face per weight. Used site-wide for both body
   and headings; heading rules just reach for the heavier weights (600–800). */
@font-face {
    font-family: 'Onest';
    src: url('/fonts/onest/onest-300-9d2ba87a66d7e48de78833eb714e6fe1.woff2') format('woff2');
    font-weight: 300;
    font-style: normal;
    font-display: optional;
}

@font-face {
    font-family: 'Onest';
    src: url('/fonts/onest/onest-400-f997ac56420f17c37861250396ddaaf8.woff2') format('woff2');
    font-weight: 400;
    font-style: normal;
    font-display: optional;
}

@font-face {
    font-family: 'Onest';
    src: url('/fonts/onest/onest-500-2fd2a2585428daf72e39dc568958d85f.woff2') format('woff2');
    font-weight: 500;
    font-style: normal;
    font-display: optional;
}

@font-face {
    font-family: 'Onest';
    src: url('/fonts/onest/onest-600-8c03bc070abb3b74caf3ad55f7e6a51a.woff2') format('woff2');
    font-weight: 600;
    font-style: normal;
    font-display: optional;
}

@font-face {
    font-family: 'Onest';
    src: url('/fonts/onest/onest-700-339b667d09243379bb13113022500ab0.woff2') format('woff2');
    font-weight: 700;
    font-style: normal;
    font-display: optional;
}

@font-face {
    font-family: 'Onest';
    src: url('/fonts/onest/onest-800-e0c4c784468c5f6b302e5ce9bc283b9f.woff2') format('woff2');
    font-weight: 800;
    font-style: normal;
    font-display: optional;
}

/* Metrics-matched fallback for Onest. Onest's own metrics (from the woff2): 1000 em, ascent 970,
   descent 305, x-height 527. Rendered in Arial while Onest is still loading, but with the SAME line
   box and character width — so when Onest swaps in there is NO reflow/jump, and (unlike swap alone)
   the text is visible immediately (no invisible flash). Used via the font stacks below. */
@font-face {
    font-family: 'Onest-fallback';
    src: local('Arial');
    /* size-adjust измерен ЭМПИРИЧЕСКИ в браузере (ширина одинакового текста в Onest vs Arial):
       Onest шире Arial всего на 3% → 103%. (Ранее стояло 131.2% по xAvgCharWidth — неверно, из-за чего
       кнопки/текст «скакали» при подмене шрифта.) ascent/descent — из реальных метрик Onest (970/305
       при upm 1000). При этих значениях запасной Arial занимает то же место, что Onest, и при подмене
       нет ни горизонтального, ни вертикального сдвига. */
    size-adjust: 103%;
    ascent-override: 97%;
    descent-override: 30.5%;
    line-gap-override: 0%;
}

/* --------------------------------------------------------------------------
   Brand tokens
   -------------------------------------------------------------------------- */

:root {
    /* Brand hex colors — exact values from Global Constraints, do not adjust. */
    --color-payne-gray: #62727F;
    --color-pastel-blue: #A8BAC6;
    --color-beige: #E5E3C5;
    --color-camel: #B19870;
    --color-old-burgundy: #442D2A;
    --color-eerie-black: #1E1E1E;
    --color-gainsboro: #D4DBE1;
    --color-alabaster: #F2F3EC;
    --color-white: #FFFFFF;

    /* Semantic aliases built on the tokens above — keep component CSS below
       readable without hard-coding hex values a second time.

       Retheme (revamp item 3): the storefront reads as brand blue + butter now, not
       burgundy/camel. --color-accent is the pastel blue, --color-accent-soft is the butter/beige
       tint for soft section backgrounds and badges, and --color-accent-dark stays a DEEP tone
       (payne-gray, not pale blue) specifically so primary buttons/prices keep white-text-legible
       contrast - pale blue with white text would be unreadable. --color-danger is unchanged
       (old-burgundy) and reserved for genuine error/destructive states (form errors, "danger"
       links), which are a different semantic than the brand accent. */
    --color-text: var(--color-eerie-black);
    --color-text-muted: var(--color-payne-gray);
    /* Тёплая бумага вместо чистого белого. Белый на экране холодный и «удешевляет» —
       владелица сформулировала это как «белый старит сайт», и она права: у брендов,
       на которые она ориентируется, страница всегда чуть тёплая (у Organic Zoo #F5F2EB).
       Значение подобрано между их бумагой и нашим прежним белым: заметно теплее белого,
       но светлее беж-зон (--color-beige #E5E3C5), чтобы зоны по-прежнему читались.

       ОТТЕНОК ВАЖНЕЕ ТЕПЛА, А КРАСНЫЙ ПЕРЕВЕС ВАЖНЕЕ ОТТЕНКА. Бумагу правили трижды, и
       владелица трижды повторила: «фон розовый». Первые две попытки чинили температуру и
       оттенок — 45°, потом 53° — но розовое никуда не делось, потому что причина была в
       другом. У тона #F6F5EE красного было на единицу больше зелёного, а разница с синим
       всего 8: слишком слабо, чтобы читаться жёлтым, и ровно столько, чтобы читаться
       подкрашенным серым с красным уклоном. Это и есть «грязно-розовый». Рядом насыщенная
       жёлтая шапка добивала эффект: на её фоне слабый тон уходит в противоположную сторону.

       Лечение не в подкрутке, а в двух правилах для всех светлых тонов сразу:
       R РОВНО РАВЕН G — красного перевеса нет как величины, оттенок ровно 60°, чистый жёлтый;
       разница с синим не меньше 11 — тон читается как ЦВЕТ, а не как испачканный серый.

       ПОТОМ ЖЕЛТИЗНУ СНЯЛИ НА 40%: с правилом R = G она стала не нужна. Насыщенность была
       защитой от розового — тон должен был звучать достаточно громко, чтобы читаться жёлтым,
       а не подкрашенным серым. Но громкость лечила следствие; причиной был красный перевес,
       и его больше нет. Без него тон можно опускать в тихий, почти нейтральный — розовым он
       не станет, потому что становиться нечем. Владелица попросила меньше жёлтого, и это
       ровно та правка, которую новая система позволяет сделать безопасно. */
    --color-bg: #F8F8EF;
    /* Самое светлое в системе: карточки, поля ввода, приподнятые панели. Не чистый белый —
       он единственным нейтральным пятном в кадре сбивает калибровку глаза, и тогда честно
       тёплая бумага рядом с ним снова читается розовой. --color-white остаётся чистым и
       живёт только там, где белый — это ТЕКСТ на тёмном. */
    --color-surface: #FDFDF7;
    /* Ступень между бумагой и панелью: подсветки при наведении, чередующиеся полосы. */
    --color-bg-soft: #F2F2E9;
    --color-bg-tint-blue: #EDF2F4;
    --color-border: var(--color-gainsboro);
    --color-accent: var(--color-pastel-blue);
    --color-accent-soft: var(--color-beige);
    --color-accent-dark: var(--color-payne-gray);
    --color-danger: var(--color-old-burgundy);
    /* Терракотовый красный магазина: предупреждения в корзине, маркер сноски про Meta,
       а с 12.10.2026 — сердце «Избранное» в шапке. Два имени на один цвет: --color-footnote
       оставлено, потому что на него ссылаются десятки правил, но значение теперь одно. */
    --color-terracotta: #B24A2E;
    --color-footnote: var(--color-terracotta);

    /* ЦВЕТА ЭМБЛЕМЫ С ПЕЧАТНОЙ ПАМЯТКИ: чернила и масло. На бумаге логотип набран именно
       ими, и счётчик в шапке теперь красится тем же, а не «похожим» — выбор владелицы
       12.10.2026 из семи вариантов оформления шапки. Заведены переменными, чтобы цвет
       нельзя было развести по двум местам. */
    /* Pantone 5435 C — фирменный голубой марки. */
    --color-brand-blue: #A5BAC9;
    --color-emblem-ink: #2E3D48;
    --color-emblem-cream: #F0EBC4;

    --font-heading: 'Onest', 'Onest-fallback', sans-serif;
    --font-body: 'Onest', 'Onest-fallback', sans-serif;

    --space-1: 0.25rem;
    --space-2: 0.5rem;
    --space-3: 0.75rem;
    --space-4: 1rem;
    --space-5: 1.5rem;
    --space-6: 2rem;
    --space-7: 3rem;
    --space-8: 4.5rem;

    --container-max: 1200px;
    --radius-sm: 6px;
    --radius-md: 12px;
    /*
      ВЫСОТА ШАПКИ 78 — 13.10.2026. Было 88: логотип висел на 60-й точке экрана, под ним
      26 точек пустоты, содержимое начиналось только на 122-й. Владелица показала ziggyzaza
      («там лого прям сверху экрана»); у них при окне 1440 шапка 58 точек, логотип 52–90.

      Сначала поставил 66 — по их мерке; она сказала, что так шапка слишком узкая.
      78 — середина: логотип на 55-й точке, содержимое с 112-й. Логотип поднялся, но шапка
      осталась просторной. Пробовали лесенкой 66 / 72 / 78 / 82 / 88, выбрано это.
    */
    --header-height: 78px;
}

/* --------------------------------------------------------------------------
   Reset
   -------------------------------------------------------------------------- */

*,
*::before,
*::after {
    box-sizing: border-box;
}

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

body,
h1, h2, h3, h4, p, ul, ol, figure {
    margin: 0;
}

ul, ol {
    padding: 0;
    list-style: none;
}

img {
    max-width: 100%;
    display: block;
}

a {
    color: inherit;
    text-decoration: none;
}

button {
    font: inherit;
    color: inherit;
    background: none;
    border: none;
    cursor: pointer;
    padding: 0;
}

/* select/textarea are in here with input for a reason: native form controls do NOT inherit type
   from their parent, they fall back to the browser's own UI font (Arial on Chrome/macOS), which
   is how the product page's variant <select> and the admin textareas ended up off-brand while
   every other element on the page was Onest. */
input,
select,
textarea {
    font: inherit;
    color: inherit;
}

/* Пол по фону для полей ввода. Браузер рисует незаданный <input> ЧИСТО БЕЛЫМ, и на тёплой
   бумаге это выдаёт ровно те холодные пятна, из-за которых сайт и выглядел разнобойно: поле
   промокода в шторке корзины и счётчик количества на странице корзины так и остались
   #FFFFFF, пока все остальные поля давно жили на тёплом тоне.

   Список исключений завёрнут в :where() СПЕЦИАЛЬНО. Обычный :not() наследует вес своего
   аргумента, и девять атрибутных селекторов подряд дали бы правилу вес выше любого
   одиночного класса — оно перебило бы строку поиска и все прочие поля со своим цветом
   (проверено: строка поиска действительно посветлела). У :where() вес нулевой, поэтому
   правило весит ровно как один тег и работает как пол, а не как диктат: любое поле со
   своим классом продолжает жить по-своему. Кнопки и переключатели исключены: у них свой вид. */
input:where(:not([type="checkbox"], [type="radio"], [type="range"], [type="color"], [type="submit"], [type="button"], [type="reset"], [type="image"], [type="file"])),
select,
textarea {
    background-color: var(--color-surface);
}

svg {
    display: block;
}

.visually-hidden {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}

/* --------------------------------------------------------------------------
   Base typography
   -------------------------------------------------------------------------- */

/* АТРИБУТ hidden ДОЛЖЕН ПРЯТАТЬ ВСЕГДА. Браузер прячет [hidden] правилом display:none из своей
   таблицы, но у неё низший приоритет: любое наше `.field { display: flex }` его перебивает, и
   элемент остаётся на экране с проставленным hidden. Именно так «Причина возврата или обмена»
   продолжала показываться до поиска заказа, хотя в разметке стояло hidden="hidden". */
[hidden] { display: none !important; }

body {
    font-family: var(--font-body);
    font-weight: 400;
    font-size: 1rem;
    line-height: 1.6;
    color: var(--color-text);
    background: var(--color-bg);

    /* ПОДВАЛ ПРИЖАТ К НИЗУ ЭКРАНА НА КОРОТКИХ СТРАНИЦАХ.
       Без этого страница вроде «Будем скучать», «Подписка подтверждена» или «Страница не найдена»
       кончалась раньше экрана, и под подвалом оставалась пустая полоса фона. Со стороны это
       читается не как «мало текста», а как сломанная вёрстка: подвал висит посреди экрана.
       min-height: 100svh, а не 100vh, — из-за мобильных браузеров: в vh входит адресная строка,
       которая при прокрутке уезжает, и страница получает лишние ~60 px пустоты внизу. */
    display: flex;
    flex-direction: column;
    min-height: 100svh;
}

/* Содержимое забирает весь свободный остаток, выталкивая подвал вниз. На длинных страницах
   ничего не меняется: остатка нет, подвал и так внизу. */
.site-main {
    flex: 1 0 auto;
}

h1, h2, h3,
.btn {
    font-family: var(--font-heading);
    font-weight: 700;
    line-height: 1.25;
    color: var(--color-text);
}

h1 {
    font-size: clamp(1.35rem, 1.1rem + 1vw, 1.9rem);
}

h2 {
    font-size: clamp(1.15rem, 1rem + 0.6vw, 1.5rem);
    margin-bottom: var(--space-4);
}

h3 {
    font-size: 1.1rem;
    font-weight: 600;
    margin-bottom: var(--space-2);
}

p {
    color: var(--color-text);
}

.container {
    max-width: var(--container-max);
    margin-inline: auto;
    padding-inline: var(--space-4);
}

section + section {
    margin-top: var(--space-8);
}

/* --------------------------------------------------------------------------
   Buttons
   -------------------------------------------------------------------------- */

.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-2);
    padding: var(--space-3) var(--space-6);
    border-radius: var(--radius-sm);
    font-size: 0.95rem;
    font-weight: 700;
    letter-spacing: 0.02em;
    transition: background-color 0.15s ease, color 0.15s ease, border-color 0.15s ease;
}

.btn--primary {
    background: var(--color-accent-dark);
    color: var(--color-white);
    border: 1px solid var(--color-accent-dark);
}

.btn--primary:hover {
    background: var(--color-eerie-black);
    border-color: var(--color-eerie-black);
}

.btn--secondary {
    background: transparent;
    color: var(--color-accent-dark);
    border: 1px solid var(--color-accent-dark);
}

.btn--secondary:hover {
    background: var(--color-accent-dark);
    color: var(--color-white);
}

.btn:disabled {
    opacity: 0.5;
    cursor: not-allowed;
}

.btn--primary:disabled:hover {
    background: var(--color-accent-dark);
    border-color: var(--color-accent-dark);
}

/* Looks inactive but stays clickable (so the click can nudge the shopper to pick a colour). */
.btn.is-blocked {
    opacity: 0.5;
    cursor: not-allowed;
}

.btn--primary.is-blocked:hover {
    background: var(--color-accent-dark);
    border-color: var(--color-accent-dark);
}

/* --------------------------------------------------------------------------
   Wordmark (shared by header, footer, hero)
   -------------------------------------------------------------------------- */

.wordmark {
    display: inline-flex;
    flex-direction: column;
    align-items: center;
    gap: var(--space-1);
    text-align: center;
}

/* logo.svg is viewBox 0 0 2000 1000 (2:1) with fill="currentColor" - as a plain <img> (not
   inlined) its currentColor resolves to the SVG's own default (black), which reads fine on the
   light header/footer. Width-driven sizing keeps the 2:1 aspect ratio via `height: auto`. */
.wordmark__img {
    width: 190px;
    height: auto;
    display: block;
}

.wordmark--footer {
    flex-direction: row;
    align-items: center;
    gap: var(--space-4);
}

.wordmark--footer .wordmark__img {
    width: 150px;
}

/* --------------------------------------------------------------------------
   Marquee (running line) — the very first element in the body, above the header, on every page
   (see layout/base.html). Pure CSS: the track holds several copies of the same text already
   duplicated in the markup, translated left by exactly 50% of its own width, so the loop is
   seamless (at the halfway point the second half of copies lines up pixel-for-pixel with where
   the first half started).
   -------------------------------------------------------------------------- */

.marquee {
    overflow: hidden;
    background: var(--color-old-burgundy);
    color: var(--color-alabaster);
}

.marquee__track {
    display: flex;
    width: max-content;
    animation: marquee-scroll 100s linear infinite;
}

.marquee__item {
    flex: 0 0 auto;
    padding: var(--space-2) var(--space-6);
    font-family: var(--font-heading);
    font-size: 0.7rem;
    font-weight: 600;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    white-space: nowrap;
    position: relative;
}

/* Small blue/butter accent dot between repeats, alternating so the strip doesn't read as flatly
   monochrome. */
.marquee__item::after {
    content: '';
    position: absolute;
    right: 0;
    top: 50%;
    width: 4px;
    height: 4px;
    border-radius: 50%;
    background: var(--color-pastel-blue);
    transform: translateY(-50%);
}

.marquee__item:nth-child(even)::after {
    background: var(--color-beige);
}

@keyframes marquee-scroll {
    from {
        transform: translateX(0);
    }
    to {
        transform: translateX(-50%);
    }
}

@media (prefers-reduced-motion: reduce) {
    .marquee__track {
        animation-play-state: paused;
    }
}

/* --------------------------------------------------------------------------
   Header
   -------------------------------------------------------------------------- */

/*
  ШАПКА БЕЗ СОБСТВЕННОГО ФОНА + ОТДЕЛЬНАЯ ПОДЛОЖКА — 12.10.2026, по ziggyzaza.

  Шапка приклеена к верху экрана и прозрачна всегда. Бежевую полосу за логотипом рисует
  .site-header__ground — обычный блок в разметке (layout/base.html), прямо перед шапкой.
  Он не приклеен, поэтому при прокрутке просто уезжает вверх вместе со страницей, а шапка
  остаётся. Полоса не гаснет и не появляется — она именно уезжает, как у ziggyzaza.

  Так было сделано после замечания 12.10.2026: «футер как будто появляется в моменте,
  скачок, у ziggyzaza его нет». До этого фон был у самой шапки и переключался классом
  .is-scrolled — отсюда и скачок.

  Цвет полосы прежний, #E5E3C5. Его не трогать.
*/
.site-header {
    position: sticky;
    top: 0;
    z-index: 40;
    background: transparent;
    border-bottom: none;
}

.site-header__ground {
    height: var(--header-height);
    /* в разметке блок ничего не занимает: шапка ложится ровно на него */
    margin-bottom: calc(-1 * var(--header-height));
    background: var(--color-beige);
    border-bottom: 1px solid var(--color-border);
}

/*
  ФОТОГРАФИЮ ПОД ШАПКУ НЕ ПОДТЯГИВАЕМ — 12.10.2026, по трём скриншотам ziggyzaza.

  Раньше здесь было `margin-top: -header-height`: фотография заезжала под шапку с первого
  же кадра, и логотип всегда лежал на ней. У ziggyzaza не так: наверху под логотипом видна
  светлая полоса — это просто фон страницы, фотография начинается ПОД шапкой. Полоса исчезает
  сама, когда человек прокручивает: фотография уезжает под приклеенную прозрачную шапку.

  Правило оставлено пустым намеренно, чтобы это не сделали заново «для воздуха».
*/

/* Home page only (.has-hero-overlay is set on <html> just for the home hero): drop the footer's
   usual top margin so the footer sits flush under the hero photo instead of leaving a white band.
   Other pages keep the normal breathing room above the footer. */
.has-hero-overlay .site-footer {
    margin-top: 0;
}

.site-header__bar {
    position: relative;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-3);
    min-height: var(--header-height);
    /* Deliberately NOT .container: this bar must span the full viewport width so the hamburger
       sits near the left edge and the utility icons near the right edge even on wide screens (a
       centered, max-width .container pulled both far in from the edges). It carries its own
       horizontal padding instead - small on the left (just enough breathing room for the
       hamburger's focus ring/tap target), larger on the right to match the utility icon cluster's
       usual .container-derived spacing. The logo stays centered via absolute positioning against
       this padding-box's full width, so it remains centered on the viewport either way. */
    padding-left: var(--space-3);
    padding-right: var(--space-5);
}

.hamburger {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 40px;
    height: 40px;
    color: var(--color-text);
}

.site-header .wordmark {
    position: absolute;
    left: 50%;
    top: 50%;
    transform: translate(-50%, -50%);
}

/* Responsive header logo: ~140px on narrow phones, up to 220px from the tablet breakpoint (see
   the 768px query below) - stays comfortably inside --header-height at every size. */
.site-header .wordmark__img {
    width: 140px;
}

@media (min-width: 420px) {
    .site-header .wordmark__img {
        width: 180px;
    }
}

/* Mobile header: a slimmer bar (60px). The logo stays CENTRED (the base .wordmark absolute-centre
   rule), the hamburger sits left and the search / favourites / cart icons on the right. The logo SVG
   was cropped to its letters, so at 128px wide its box is only ~15px tall and fits the thin bar
   without stretching it (which used to leave a white gap under the header on the home hero). Placed
   after the 420px rule above so it wins for phones in the 420-768px range. */
@media (max-width: 768px) {
    :root {
        --header-height: 60px;
    }

    /* Узкий телефон: 128 — предел. Логотип стоит по центру, а значки справа начинаются
       на 252-й точке экрана 360 px, поэтому его половина не может быть шире 72.
       Проверено измерением 12.10.2026, когда пробовали 150 и 170 — налезало. */
    .site-header .wordmark__img {
        width: 128px;
    }

    /* Совсем узкие экраны (320 px, старые iPhone SE): 128 там налезает на значки
       на 12 точек — это было и раньше, поправлено заодно 12.10.2026. */
    @media (max-width: 359px) {
        .site-header .wordmark__img {
            width: 90px;
        }
    }

    /* От 420 и от 560 места между бургером и значками хватает на больший логотип. */
    @media (min-width: 420px) {
        .site-header .wordmark__img {
            width: 190px;
        }
    }

    @media (min-width: 560px) {
        .site-header .wordmark__img {
            width: 230px;
        }
    }

    /* Tighter icon cluster (so the bigger centred logo has room and never overlaps the search icon).
       The icons sit close to the right edge (small right padding) at the owner's request. The
       `.site-header` prefix raises specificity above the base .header-utility rules that appear later
       in this file (equal specificity would otherwise let those win by source order). */
    .site-header__bar {
        padding-right: var(--space-4);
    }

    .site-header .header-utility {
        gap: var(--space-1);
    }

    .site-header .header-utility__icon {
        width: 28px;
        height: 28px;
    }
}

.header-utility {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    justify-self: end;
}

/* «RU» набран тем же цветом, что и значки: прежний приглушённый серо-синий выглядел
   выцветшим рядом с ними — владелица спросила об этом прямо. Кегль на пункт крупнее,
   чтобы две буквы не казались мельче соседних значков. */
.header-utility__pill {
    display: none;
    align-items: center;
    padding: var(--space-1) var(--space-3);
    /*
      РАМКА «RU» — ФИРМЕННЫЙ ГОЛУБОЙ 5435 C (#A5BAC9), 12.10.2026 по вопросу владелицы
      «у RU рамка голубой фирменный цвет?». До этого стояла общая серая рамка интерфейса
      #D4DBE1 — из той же холодной семьи, но бледнее и не из палитры марки.

      Меняется только у этой таблетки: --color-border держит все линии сайта,
      трогать его нельзя.
    */
    border: 1px solid var(--color-brand-blue);
    border-radius: 999px;
    font-size: 0.8rem;
    font-weight: 600;
    color: var(--color-emblem-ink);
}

/*
  ЗНАЧКИ В ШАПКЕ КРУПНЕЕ И ОДНОГО ЦВЕТА С ЭМБЛЕМОЙ — правка 12.10.2026 по замечанию
  владелицы: «мелковаты сами иконки, контур лупы и RU какого цвета?».

  Замеры подтвердили оба наблюдения. Значки были 22 px в кнопке 36 px — на её широком экране
  шапка просторная, и значки в ней терялись. И тёмных в шапке было ДВА: лупа с корзиной
  рисовались почти чёрным #1E1E1E, а счётчик — синеватыми чернилами эмблемы #2E3D48.
  Рядом это читается как «значки чужие, счётчик свой».

  Теперь значок 26 px в кнопке 40 px, а цвет один на всю шапку — чернила эмблемы.
*/
.header-utility__icon {
    position: relative;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 40px;
    height: 40px;
    color: var(--color-emblem-ink);
}

.header-utility__icon svg {
    width: 26px;
    height: 26px;
}

/*
  ОТКЛИК НА НАВЕДЕНИЕ. При полной проверке 12.10.2026 выяснилось, что значки в шапке
  не отзывались вообще никак: ни цветом, ни фоном. На компьютере это читается как
  «картинка, а не кнопка» — особенно теперь, когда два значка залиты цветом и на цвет
  реагировать уже нечем.

  Отклик — мягкий кружок позади значка, тот же приём, что у значков почты и Telegram
  в подвале. Сам значок при этом не меняется: заливка у сердца и корзины фирменная,
  и перекрашивать её на наведении значит показывать человеку цвет, которого в палитре нет.
*/
.header-utility__icon::before {
    content: '';
    position: absolute;
    inset: 2px;
    border-radius: 50%;
    /*
      ЧЕРНИЛА, ПРОЗРАЧНЫЕ НА 12 % — 12.10.2026, по замечанию владелицы «наводя на них они
      белые». Прежняя подложка была светлой (--color-bg-soft): на бежевой плашке она
      читалась как белая заплатка, а когда шапка стала прозрачной и значки поехали
      по фотографии — тем более.

      Полупрозрачные чернила ведут себя одинаково на любом фоне: на светлом это лёгкая
      тень, на фотографии — мягкое затемнение. Белого не появляется нигде.
    */
    background: rgba(46, 61, 72, 0.12);
    opacity: 0;
    transition: opacity 0.18s ease;
}

.header-utility__icon:hover::before,
.header-utility__icon:focus-visible::before {
    opacity: 1;
}

/* Сам значок и счётчик поверх кружка, иначе подложка перекроет их. */
.header-utility__icon svg,
.header-utility__badge {
    position: relative;
    z-index: 1;
}

.header-utility__badge {
    position: absolute;
}

.header-utility__pill {
    transition: border-color 0.18s ease, background-color 0.18s ease;
}

/* «RU» отзывается тем же, чем и значки, — иначе на наведении таблетка белела отдельно. */
.header-utility__pill:hover {
    background: rgba(46, 61, 72, 0.12);
    border-color: var(--color-emblem-ink);
}

/*
  КОНТУР КОРЗИНЫ — ЧЕРНИЛА ЭМБЛЕМЫ #2E3D48, 13.10.2026 по прямому указанию владелицы:
  «контур корзины и лупа и RU сделай одного фирменного тёмно-синего».

  Было --color-text (#1E1E1E, почти чёрный): в одном ряду с лупой и «RU», набранными
  чернилами, читались два разных тёмных. Теперь правый ряд звучит одним цветом.
  Логотип и бургер остаются чёрными — её же решение, не трогать.
*/
.header-utility__cart {
    color: var(--color-emblem-ink);
}

.header-utility__badge {
    position: absolute;
    /* Чуть выше и правее прежнего: кружок вырос с 16 до 18 px вместе с ободком. */
    top: -3px;
    right: -3px;
    min-width: 19px;
    height: 19px;
    padding: 0 4px;
    border-radius: 999px;
    /*
      СЧЁТЧИК ЦВЕТАМИ ЭМБЛЕМЫ — выбор владелицы 12.10.2026. Был бледно-голубой кружок
      с тёмной цифрой: он сливался с серо-голубой шапкой, и число приходилось искать.
      Теперь те же два цвета, что у логотипа на памятке: тёмные чернила и масляная цифра.
      Пятно читается сразу и при этом остаётся своим, а не «красной точкой как везде».

      Белый ободок обязателен: без него тёмный кружок налезает на значок корзины и оба
      превращаются в кляксу. Он же отделяет счётчик от любого фона, на который попадёт шапка.
    */
    background: var(--color-emblem-ink);
    color: var(--color-emblem-cream);
    box-shadow: 0 0 0 2px var(--color-surface);
    font-size: 0.66rem;
    font-weight: 700;
    line-height: 19px;
    text-align: center;
}

/* Hidden when the count is 0 (no "0" bubble poking above the icons - keeps the utility row clean). */
.header-utility__badge--hidden {
    display: none;
}

/* Highlights the search icon itself while the bar below is open (aria-expanded="true", set by
   static/js/search.js) - a plain color change, no layout shift. */
#search-toggle[aria-expanded="true"] {
    color: var(--color-accent-dark);
}

/* --------------------------------------------------------------------------
   Header search bar (fragments/header.html, static/js/search.js) - a slide-down bar just under
   the header's own row, toggled open/closed by #search-toggle. It lives inside .site-header
   (itself `position: sticky`), so it scrolls/sticks together with the rest of the header rather
   than floating independently, and never overlaps the marquee above or the mobile nav panel
   (`z-index: 50`, well above this bar's own stacking context).

   It hangs *below* the header out of flow (absolute, top: 100%) rather than growing it: the home
   hero is pulled up by exactly `-1 * --header-height`, so a bar that added its own height to the
   header would push the whole page down by that much the moment search was opened, tearing a
   strip open under the hero.

   This is the inner-page look (an opaque strip with a pill input). Over the home hero the bar
   sheds all of it and becomes a bare line on the photo - see the .has-hero-overlay rules below.
   -------------------------------------------------------------------------- */

.site-search-bar {
    position: absolute; /* out of flow: never changes .site-header's height */
    top: 100%;
    left: 0;
    right: 0;
    z-index: 45;
    /*
      БЕЛОЙ ПОЛОСЫ ВО ВСЮ ШИРИНУ НЕТ — владелица забраковала её 12.10.2026, и по делу:
      на главной лента рассекала фотографию пополам, а на остальных страницах добавляла
      ещё одну горизонтальную черту к шапке.

      Видимым остаётся только само поле: оно светлое, с мягкой тенью, и над снимком читается
      как карточка, а не как заплатка. Сама полоса прозрачна и нужна лишь для того, чтобы
      поле встало по центру и не меняло высоту шапки.
    */
    background: transparent;
    border-bottom: none;
    pointer-events: none;
    padding: var(--space-3) var(--space-5);
    animation: search-bar-slide-down 0.18s ease-out;
}

.site-search-bar[hidden] {
    display: none;
}

/*
  ПОЛЕ — ОДНА ШИРОКАЯ ПИЛЮЛЯ ВО ВСЮ СТРОКУ, лупа внутри слева, крестик внутри справа.
  Собрано по образцу ziggyzaza.com.au: там поиск занимает всю ширину шапки и выглядит
  одинаково на любой странице. Прежние 560 px по центру оставляли поле плавать в пустоте,
  а на главной оно и вовсе было другим.
*/
.site-search-bar__form {
    position: relative;
    /* Полоса-обёртка не ловит нажатия (pointer-events: none), поэтому поле возвращает себе
       эту способность — иначе по нему нельзя будет кликнуть. */
    pointer-events: auto;
    display: flex;
    align-items: center;
    gap: var(--space-2);
    max-width: 1200px;
    margin: 0 auto;
    padding: 0 var(--space-2);
    border-radius: 999px;
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    /* Тень делает поле карточкой над фотографией: без неё светлая пилюля на светлом небе
       растворяется. На обычных страницах она почти незаметна. */
    box-shadow: 0 10px 28px rgba(30, 30, 30, 0.16);
}

.site-search-bar__form:focus-within {
    border-color: var(--color-emblem-ink);
    background: var(--color-surface);
}

.site-search-bar__input {
    flex: 1;
    min-width: 0;
    padding: var(--space-3) var(--space-2);
    border: 0;
    border-radius: 999px;
    background: transparent;
    font-size: 1rem;
    color: var(--color-emblem-ink);
}

.site-search-bar__input:focus {
    outline: none;
}

.site-search-bar__input::placeholder {
    color: var(--color-text-muted);
}

/* Chrome draws its own clear-✕ inside `type="search"` fields, in its own blue - it lands right
   next to our own submit icon and reads as a second, broken-looking control. */
.site-search-bar__input::-webkit-search-cancel-button {
    -webkit-appearance: none;
    appearance: none;
}

/*
  ЛУПА СЛЕВА — ОНА ЖЕ КНОПКА «НАЙТИ». Тёмного кружка больше нет: на светлой пилюле он читался
  как главное действие строки, хотя главное здесь — набрать слово. Теперь лупа выглядит
  значком, а работает кнопкой, и Enter в поле делает то же самое.
*/
.site-search-bar__submit {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    order: -1;
    width: 36px;
    height: 36px;
    border-radius: 999px;
    background: transparent;
    color: var(--color-emblem-ink);
}

.site-search-bar__submit:hover {
    background: var(--color-surface);
}

/* Крестик — закрыть поиск, не уходя со страницы. Раньше закрыть можно было только повторным
   нажатием на лупу в шапке или клавишей Esc: и то и другое надо знать заранее. */
.site-search-bar__close {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    width: 36px;
    height: 36px;
    border: 0;
    border-radius: 999px;
    background: transparent;
    color: var(--color-text-muted);
    cursor: pointer;
}

.site-search-bar__close:hover {
    background: var(--color-surface);
    color: var(--color-emblem-ink);
}

@keyframes search-bar-slide-down {
    from {
        opacity: 0;
        transform: translateY(-8px);
    }
    to {
        opacity: 1;
        transform: translateY(0);
    }
}

@media (prefers-reduced-motion: reduce) {
    .site-search-bar {
        animation: none;
    }
}

/* --------------------------------------------------------------------------
   ПОИСК ОДИН НА ВСЕХ СТРАНИЦАХ.

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

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

   Оставлен один вид, собранный по образцу ziggyzaza.com.au, который владелица прислала:
   светлая полоса во всю ширину, внутри — поле-пилюля с лупой слева и крестиком справа.
   Весь прежний блок .has-hero-overlay … .site-search-bar удалён целиком.
   -------------------------------------------------------------------------- */

/* Подсказки под полем (static/js/search.js -> GET /search/suggest). Позиционируются от
   .site-search-bar, а ширина повторяет ширину самого поля — 1200 px с теми же боковыми
   отступами, что у строки поиска. Было 560: после того как поле растянулось во всю строку,
   подсказки висели бы узкой колонкой под широким полем.
   z-index 45 — выше липкой шапки (40), но ниже мобильного меню (50): одновременно они
   не открываются, но порядок слоёв пусть будет осмысленным. */
.site-search-suggestions {
    position: absolute;
    /* Отступ в полшага от поля: полосы-подложки больше нет, и подсказки висят отдельной
       карточкой — приклеенные вплотную, они читались бы как продолжение поля. */
    top: calc(100% + var(--space-2));
    left: var(--space-5);
    right: var(--space-5);
    max-width: 1200px;
    margin: 0 auto;
    pointer-events: auto;
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    box-shadow: 0 14px 34px rgba(30, 30, 30, 0.2);
    max-height: 360px;
    overflow-y: auto;
    z-index: 45;
}

/* Остаток от прежнего «поиска-типографики» на главной: подсказки висели отдельной узкой
   карточкой в 620 px. Убран 12.10.2026 вместе с самим тем вариантом — поиск теперь один
   на всех страницах, и подсказки везде повторяют ширину поля. */

.site-search-suggestions[hidden] {
    display: none;
}

.site-search-suggestions__item {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    padding: var(--space-2) var(--space-4);
    color: var(--color-text);
}

.site-search-suggestions__item:not(:last-child) {
    border-bottom: 1px solid var(--color-border);
}

.site-search-suggestions__item:hover,
.site-search-suggestions__item:focus-visible {
    background: var(--color-bg-soft);
}

.site-search-suggestions__thumb {
    width: 44px;
    height: 44px;
    flex-shrink: 0;
    object-fit: cover;
    border-radius: var(--radius-sm);
    background: var(--color-bg-soft);
}

.site-search-suggestions__info {
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-width: 0;
}

.site-search-suggestions__name {
    font-size: 0.9rem;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.site-search-suggestions__price {
    font-size: 0.85rem;
    color: var(--color-text-muted);
}

.site-search-suggestions__empty {
    padding: var(--space-4);
    color: var(--color-text-muted);
    font-size: 0.9rem;
    text-align: center;
}

/* --------------------------------------------------------------------------
   Mobile nav (slide-in panel, toggled by js/main.js)
   -------------------------------------------------------------------------- */

.mobile-nav {
    position: fixed;
    inset: 0;
    z-index: 50;
    visibility: hidden;
    pointer-events: none;
}

.mobile-nav.is-open {
    visibility: visible;
    pointer-events: auto;
}

.mobile-nav__backdrop {
    position: absolute;
    inset: 0;
    background: rgba(30, 30, 30, 0.45);
    opacity: 0;
    transition: opacity 0.2s ease;
}

.mobile-nav.is-open .mobile-nav__backdrop {
    opacity: 1;
}

.mobile-nav__panel {
    position: relative;
    z-index: 1;
    width: min(320px, 85vw);
    height: 100%;
    /* Тот же материал, что у шторки корзины: обе выезжают поверх затемнённой страницы, значит
       обе должны быть приподнятой поверхностью, а не просто куском той же бумаги. Раньше меню
       стояло на тоне страницы и без тени — оно читалось как продолжение фона, поехавшее вбок,
       а корзина рядом как настоящая панель. Тень зеркальная: меню приходит слева, корзина
       справа, поэтому смещение по X с обратным знаком. */
    background: var(--color-surface);
    box-shadow: 12px 0 40px rgba(30, 30, 30, 0.18);
    padding: var(--space-6) var(--space-5);
    transform: translateX(-100%);
    transition: transform 0.25s ease;
    display: flex;
    flex-direction: column;
    gap: var(--space-6);
}

.mobile-nav.is-open .mobile-nav__panel {
    transform: translateX(0);
}

.mobile-nav__close {
    align-self: flex-end;
    width: 32px;
    height: 32px;
}

.mobile-nav__list li + li {
    margin-top: var(--space-4);
}

.mobile-nav__list a {
    font-family: var(--font-heading);
    font-weight: 600;
    font-size: 1.05rem;
}

/* Accordion (revamp item 10): a category with children (e.g. "Дети 1–7 лет") renders its own
   link plus a separate chevron toggle; the sublist is collapsed (max-height: 0) by default and
   expands when static/js/main.js toggles .is-expanded on the parent <li>. max-height (rather than
   height: auto, which can't be transitioned) is set generously above any realistic sublist's
   real height so the slide animation never clips content. */
.mobile-nav__parent-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-2);
}

.mobile-nav__accordion-toggle {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 28px;
    height: 28px;
    color: var(--color-text-muted);
    transition: transform 0.2s ease;
}

.mobile-nav__item--accordion.is-expanded .mobile-nav__accordion-toggle {
    transform: rotate(180deg);
    color: var(--color-text);
}

.mobile-nav__sublist {
    margin-top: 0;
    max-height: 0;
    overflow: hidden;
    padding-left: var(--space-4);
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    transition: max-height 0.25s ease, margin-top 0.25s ease;
}

.mobile-nav__item--accordion.is-expanded .mobile-nav__sublist {
    margin-top: var(--space-3);
    max-height: 480px;
}

.mobile-nav__sublist a {
    font-family: var(--font-body);
    font-weight: 400;
    font-size: 0.9rem;
    color: var(--color-text-muted);
}

/* "Смотреть все" — the lead item of an expanded category submenu, a touch stronger than its
   sibling sub-category links. */
.mobile-nav__sublist-all {
    font-weight: 600;
    color: var(--color-text);
}

body.nav-open,
body.modal-open {
    overflow: hidden;
}

/* --------------------------------------------------------------------------
   Footer
   -------------------------------------------------------------------------- */

.site-footer {
    position: relative;
    isolation: isolate;
    margin-top: var(--space-8);
    /* Soft brand mesh gradient (сливочный Beige #E5E3C5 ↔ голубой Pastel Blue #A8BAC6), matching the
       branded bag. Only the two brand hexes over an alabaster base - no grey, so the blue stays true. */
    background-color: var(--color-alabaster);
    background-image:
        radial-gradient(95% 120% at 7% 16%, rgba(229, 227, 197, 0.98), rgba(229, 227, 197, 0) 55%),
        radial-gradient(90% 120% at 93% 84%, rgba(229, 227, 197, 0.85), rgba(229, 227, 197, 0) 52%),
        radial-gradient(115% 130% at 58% 46%, rgba(168, 186, 198, 0.95), rgba(168, 186, 198, 0) 60%),
        radial-gradient(70% 95% at 82% 22%, rgba(168, 186, 198, 0.72), rgba(168, 186, 198, 0) 55%);
    border-top: 1px solid var(--color-border);
    padding-top: var(--space-6);
}

.site-footer__grid {
    display: grid;
    grid-template-columns: 1fr;
    gap: var(--space-6);
}

.site-footer__brand {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
}

.site-footer__tagline {
    color: var(--color-text-muted);
    max-width: 21rem;
}

/* "Будьте на связи" + email form, sitting under the footer logo (moved here from the home page).
   Dropped a little lower under the logo, with the email field and button stacked at the same width
   so they line up. */
.site-footer__newsletter {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
}

/* Тот же заголовок колонки, что «Покупателям» и «Контакты»: размер и вес НЕ переопределяем —
   берём глобальный h3 (1.1rem / 600), иначе «Будем на связи» выбивался (был 1.05rem / 400).
   Отступ снизу задаёт .newsletter-block, чтобы поле ввода встало на уровень «О нас». */
.site-footer__newsletter-title {
    margin-bottom: 0;
}

/* Страница подтверждения подписки: заголовок и кнопка по центру. .legal-page по умолчанию
   выравнивает текст по ширине (justify) — для одной короткой строки это ни к чему. */
.newsletter-confirmed {
    text-align: center;
}

.newsletter-confirmed h1 {
    margin-bottom: var(--space-6);
}

.newsletter-confirmed .legal-page__note {
    text-align: center;
    margin-inline: auto;
}

/* Селектор из ДВУХ классов намеренно: у .legal-page есть `p { text-align: justify }` (класс + тег),
   и оно перебивает как выравнивание секции, так и одиночный класс — кнопка прижималась влево.
   По той же причине здесь перечислены ВСЕ абзацы этой страницы: кнопка «Скопировать промокод»
   и строка «промокод скопирован» ловили ту же justify-выключку и уезжали к левому краю. */
.newsletter-confirmed .newsletter-confirmed__action {
    text-align: center;
    margin: var(--space-3) 0 0;
}

/* Отбивка перед «Смотреть каталог» больше: это следующий шаг, а не ещё одна кнопка в ряду. */
.newsletter-confirmed .newsletter-confirmed__action:last-of-type {
    margin-top: var(--space-5);
}

/* И такая же отбивка ПОСЛЕ кнопки: подпись «Введите код в корзине…» стояла к ней вплотную и
   читалась как часть кнопки. Это отдельная мысль — ей нужен воздух. */
.newsletter-confirmed .newsletter-confirmed__action + .legal-page__note {
    margin-top: var(--space-5);
}

/* Обёртка «заголовок + форма подписки». Повторяет раскладку родителя, потому что заголовок теперь
   живёт внутри фрагмента (чтобы исчезать вместе с формой после подписки) — иначе он остаётся
   единственным соседом формы без отступа между ними. */
.newsletter-block {
    display: flex;
    flex-direction: column;
    /* space-4, а не space-3: у соседней колонки между «Покупателям» и «О нас» набегает 15 px
       (12 px отступа заголовка + собственная строка ссылки), и с 12 px поле ввода вставало на
       3 px выше. С 16 px первая строка всех колонок сходится в одну линию. */
    gap: var(--space-4);
}

/* Сообщение после подписки — на месте заголовка и формы. Не «плашка», а спокойный текст в тон
   подвала: это финал действия, а не предупреждение. */
.newsletter-done {
    /* pre-line: перевод строки из самого текста сообщения работает, а лишние пробелы/переносы в
       разметке — нет. «Спасибо за подписку!» стоит отдельной строкой. */
    white-space: pre-line;
    /* Первая строка сообщения встаёт на уровень «О нас», а не на уровень заголовков колонок:
       заголовок «Будем на связи» исчез вместе с формой, и без этого отступа текст поднимался в
       строку заголовков. Считаем ровно ту высоту, которую занимал заголовок (1.1rem × line-height
       1.25) плюс отступ до первой строки. */
    margin-top: calc(1.1rem * 1.25 + var(--space-4));
    max-width: 340px;
    font-family: var(--font-heading);
    font-size: 1.05rem;
    line-height: 1.5;
    color: var(--color-text);
}

/* Roomy stacked form: a comfortably wide email field with the button below it, both full-width of
   the newsletter column - reads as a proper sign-up, not a cramped little box. */
/* flex-wrap: nowrap ОБЯЗАТЕЛЕН, и это не перестраховка. Базовый .newsletter-form объявлен с
   flex-wrap: wrap ради строчной раскладки на главной, а здесь направление меняется на column — и в
   колоночном flex перенос работает не так, как ожидаешь: элементы переносятся не на новую строку, а
   в НОВЫЙ СТОЛБЕЦ справа. Высота при этом ограничена: подвал — грид, его ячейки растягиваются на
   высоту строки, так что у формы есть определённая высота, в которую надо влезть.

   Пока в форме было два элемента (поле и кнопка), они в эту высоту помещались и перенос не
   срабатывал. Третий — блок согласий — не поместился, и форма разложилась в три столбца поверх
   колонок «Покупателям» и «Контакты»: поле слева, галочки на ссылках, кнопка в контактах.
   Колонка из полей формы не должна переноситься никогда, поэтому запрет прямой. */
.site-footer__newsletter .newsletter-form {
    flex-direction: column;
    flex-wrap: nowrap;
    align-items: stretch;
    max-width: 340px;
    gap: var(--space-3);
}

/* Mobile: let the email field + "Подписаться" button fill the full column width, so their side
   margins match the rest of the footer (the 340px cap left extra space on the right). */
@media (max-width: 768px) {
    .site-footer__newsletter .newsletter-form {
        max-width: none;
    }
}

.site-footer__newsletter .newsletter-form__label {
    flex: none;
    width: 100%;
}

.site-footer__newsletter .newsletter-form__input {
    padding-block: var(--space-4);
}

.site-footer__newsletter .newsletter-form__submit {
    width: 100%;
}

/* flex: none — по той же причине, что и у .newsletter-form__label выше. В строчной раскладке
   (главная) блоку согласий нужен flex-basis: 100%, чтобы занять целую строку. Здесь направление
   колоночное, и та же сотня процентов означает уже 100% ВЫСОТЫ контейнера — вместе с
   flex-shrink: 1 это отдало бы согласиям всю высоту формы и сплющило поле ввода с кнопкой.
   Ширину задаём отдельно: колонка подвала шире формы, и без ограничения строки согласий вылезали
   бы за правый край поля ввода. */
.site-footer__newsletter .newsletter-form__consents {
    flex: none;
    max-width: 340px;
}

@media (max-width: 768px) {
    .site-footer__newsletter .newsletter-form__consents {
        max-width: none;
    }
}

.site-footer__social {
    display: flex;
    gap: var(--space-3);
}

.site-footer__social-link {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 34px;
    height: 34px;
    border-radius: 50%;
    border: 1px solid var(--color-border);
    font-size: 0.7rem;
    font-weight: 700;
}

.site-footer__col h3 {
    margin-bottom: var(--space-3);
}

.site-footer__col ul li + li {
    margin-top: var(--space-2);
}

.site-footer__col a {
    /* Darker than the muted payne-gray so links stay readable over the footer's colour gradient. */
    color: #363c42;
}

.site-footer__col a:hover {
    color: var(--color-eerie-black);
}

/* Контакты: a collapsible item at the end of the Покупателям list. The toggle
   button matches the sibling footer links; clicking it expands the panel with
   the social channels + contact info. */
.site-footer__contacts-toggle {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    color: var(--color-text-muted);
    font: inherit;
    cursor: pointer;
}

/* When Контакты is its own footer column, its toggle doubles as the column heading - match the
   sibling <h3> headings (font/size/weight/spacing). */
.site-footer__contacts .site-footer__contacts-toggle {
    margin-bottom: var(--space-3);
    font-family: var(--font-heading);
    font-size: 1.1rem;
    font-weight: 600;
    color: var(--color-text);
}

.site-footer__contacts-toggle:hover {
    color: var(--color-payne-gray);
}

.site-footer__contacts-chevron {
    transition: transform 0.2s ease;
}

.site-footer__contacts-toggle[aria-expanded="true"] .site-footer__contacts-chevron {
    transform: rotate(180deg);
}

/* Раскрытие: max-height анимируется до РЕАЛЬНОЙ высоты контента (её ставит main.js через scrollHeight),
   а не до фиксированного значения — поэтому нет «перелёта» и мёртвой паузы. Пиксельная анимация max-
   height надёжно работает во всех браузерах (в отличие от grid-fr, который не анимируется в Safari).
   Короткий ease-out делает раскрытие снаппи, без ощущения «залипания».

   На телефоне панель остаётся в потоке: колонка там одна, раздвигать подвал вниз ей некуда
   мешать. На широких экранах она вынимается из потока — см. брейкпоинт 768px ниже. */
.site-footer__contacts-panel {
    max-height: 0;
    overflow: hidden;
    transition: max-height 0.2s ease-out;
}

/* No top margin: the intro starts right under the "Контакты" heading, level with "О нас" in the
   neighbouring column. Compact so the expanded panel fits the other columns' height and the footer
   doesn't grow when Контакты is opened. */
.site-footer__contacts-intro {
    margin-top: 0;
    max-width: none;
    /* Same dark graphite as the "Покупателям" links so it reads over the gradient. */
    color: #363c42;
    font-size: 1rem;
    /* 1.4, а не 1.5: разбивка на два предложения добавила абзацу пятую строку, и панель стала
       заканчиваться ниже соседней колонки. Строки чуть плотнее — и низ снова сходится. */
    line-height: 1.4;
    /* Выключка по формату: колонка узкая, и рваный правый край рядом с ровной колонкой ссылок
       выглядел неопрятно. Переносы намеренно выключены — слова рвутся целиком; расплата за это
       чуть более широкие межсловные пробелы в отдельных строках. */
    text-align: justify;
}

.site-footer__contact-email {
    display: inline-block;
    color: #363c42;
    margin-top: var(--space-2);
    margin-bottom: 0;
    font-size: 0.92rem;
}

.site-footer__contact-email:hover {
    color: var(--color-eerie-black);
}

.site-footer__contacts-panel .site-footer__social {
    margin-bottom: 0;
    color: var(--color-text-muted);
}

/* E-mail text + channel icons on one row (keeps the expanded panel short enough that opening it
   doesn't push the footer taller). */
.site-footer__contacts-foot {
    display: flex;
    align-items: center;
    justify-content: flex-start;
    flex-wrap: wrap;
    gap: var(--space-3) var(--space-4);
    /* 4px вместо 8px — вместе со сноской ниже отыгрывает те 10 px, из-за которых последняя
       строка сноски висела ниже последней ссылки соседней колонки «Покупателям». */
    margin-top: 4px;
}

.site-footer__contacts-foot .site-footer__contact-email {
    margin: 0;
}

.site-footer__social-link:hover {
    color: var(--color-payne-gray);
    border-color: var(--color-payne-gray);
}

/* Маркер сноски, висящий в правом верхнем углу иконки Instagram (связывает её со сноской «*»
   внизу подвала). Раньше это была SVG-звезда в пять лучей — теперь ровно тот же типографский
   символ «*», что в сноске и в письмах; цвет и насыщенность приходят из .footnote-star.
   line-height: 1 обязателен: у строчного бокса иначе своя высота, и звёздочка уезжает вниз. */
.site-footer__social-link--ig {
    position: relative;
    overflow: visible;
}

.site-footer__social-star {
    position: absolute;
    top: -2px;
    right: -1px;
    font-size: 0.95rem;
    line-height: 1;
    pointer-events: none;
}

/* Маркер сноски про Meta. Красный, а не цвет текста: это единственная пометка на сайте,
   которую закон требует заметить, — сливаясь с абзацем, она свою работу не делает.
   Тон тот же, что у предупреждений в корзине (--color-footnote), чтобы не заводить
   в палитре второй красный. Звёздочка над иконкой Instagram в подвале
   (.site-footer__social-star) красится тем же цветом: это одна и та же сноска. */
.footnote-star {
    color: var(--color-footnote);
    font-weight: 600;
}

.site-footer__contacts ul li + li {
    margin-top: var(--space-2);
}

.site-footer__qr {
    display: inline-flex;
    flex-direction: column;
    align-items: center;
    gap: var(--space-2);
    margin-top: var(--space-4);
    color: var(--color-text-muted);
    font-size: 0.8rem;
}

.site-footer__qr img {
    width: 112px;
    height: 112px;
    background: var(--color-surface);
    padding: var(--space-2);
    border-radius: var(--radius-sm);
    border: 1px solid var(--color-border);
}

.site-footer__bottom {
    /* Полоса была вчетверо выше, чем ей положено: четыре строки в столбик, каждая с отбивкой,
       да ещё щедрые поля сверху и снизу. Это служебный низ, а не раздел — он должен читаться
       как подпись под документом, поэтому три коротких элемента сведены в одну строку
       (см. .site-footer__requisites), а отступы ужаты почти вдвое. */
    /* Отбивка больше остальных отступов подвала: под колонками должно оставаться место для
       раскрытой панели «Контакты» — она вынута из потока и на эту строку не давит, но и
       налезать на реквизиты ей нельзя. */
    margin-top: var(--space-7);
    padding-block: var(--space-3);
    border-top: 1px solid var(--color-border);
    font-size: 0.8rem;
    color: var(--color-text-muted);
}

/* Сноска про Meta живёт внутри раскрывающейся панели «Контакты», под строкой с почтой и
   иконками. Кегль ещё мельче остального в панели: это пометка при значке, а не текст, который
   читают. Нижнего отступа нет — она последняя в панели. */
.site-footer__disclaimer {
    /* Сноска прижата к строке с иконками намеренно: звёздочка, которую она объясняет, стоит
       прямо на иконке Instagram — чем ближе пояснение к своей звёздочке, тем оно понятнее.
       Заодно вторая (последняя) строка сноски встаёт ровно на уровень последней ссылки
       колонки «Покупателям», и низ подвала перестаёт быть рваным. */
    margin: 2px 0 0;
    font-size: 0.68rem;
    line-height: 1.4;
    color: var(--color-text-muted);
}

/* --- Фотографии брака в заявке на возврат -------------------------------------------------- */
/* Настоящий <input type=file> спрятан, а нажимается его подпись: браузерная кнопка «Выберите
   файл» выглядит чужой на любой странице и не переводится. */
.return-photos { display: flex; flex-direction: column; gap: var(--space-2); }
.return-photos[hidden] { display: none; }
.return-photos__button {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    align-self: flex-start;
    padding: 10px 18px;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm);
    background: var(--color-surface);
    font-size: 0.92rem;
    cursor: pointer;
}
.return-photos__button:hover { border-color: var(--color-payne-gray); }
/* Кнопку подсвечивает та же проверка, что и остальные поля формы: при браке фотография обязательна. */
.return-photos__button--invalid { border-color: var(--color-footnote); }
.return-photos__button input[type="file"] {
    position: absolute;
    width: 1px;
    height: 1px;
    opacity: 0;
    pointer-events: none;
}
/* Фокус с клавиатуры живёт на скрытом поле — рамку рисуем вокруг видимой подписи. */
.return-photos__button:focus-within {
    outline: 2px solid var(--color-payne-gray);
    outline-offset: 2px;
}
.return-photos__list {
    margin: 0;
    font-size: 0.85rem;
    color: var(--color-text-muted);
    word-break: break-word;
}
.return-photos__list[hidden] { display: none; }

/* Снимки в карточке заявки в админке. */
.admin-return-photos { display: flex; flex-wrap: wrap; gap: var(--space-2); margin-top: var(--space-2); }
.admin-return-photos a { display: block; line-height: 0; }
.admin-return-photos img {
    width: 120px;
    height: 150px;
    object-fit: cover;
    border-radius: var(--radius-sm);
    border: 1px solid var(--color-border);
    background: var(--color-bg-soft);
}

/* Подпись вместо снимка, который браузер не умеет показать (почти всегда HEIC с айфона).
   Обычно её нет вовсе: она появляется, только когда картинка не загрузилась (см. onerror
   в admin/returns-detail.html). Размер тот же, что у миниатюры, — ряд снимков не прыгает. */
.admin-return-photos__подпись { display: none; }

.admin-return-photos__неоткрылась {
    /* У ссылки стоит line-height: 0 — он нужен миниатюре, чтобы под ней не было щели.
       Здесь внутри текст, и с нулевым межстрочным он слипся бы в одну полосу. */
    line-height: normal;
    text-decoration: none;
}

.admin-return-photos__неоткрылась .admin-return-photos__подпись {
    display: flex;
    align-items: center;
    width: 120px;
    /* min-height, а не height: подпись переводится на свой язык и может стать длиннее.
       Жёсткая высота обрезала бы последнюю строку — ровно это и вышло при первой проверке. */
    min-height: 150px;
    padding: 10px;
    box-sizing: border-box;
    border: 1px dashed var(--color-border);
    border-radius: var(--radius-sm);
    background: var(--color-bg-soft);
    font-size: 0.68rem;
    line-height: 1.4;
    color: var(--color-text-muted);
    text-decoration: none;
}

/* Кнопка отзыва согласия на cookie. Выглядит как ссылка и стоит рядом с дисклеймером: это
   служебное действие, а не призыв — тому, кто её ищет, она нужна, остальным мешать не должна.
   Сбрасываем всё, что браузер рисует у <button> по умолчанию: фон, рамку, свои отступы и шрифт. */
.site-footer__cookie-link {
    display: inline-block;
    margin-bottom: 0;
    padding: 0;
    border: 0;
    background: none;
    font: inherit;
    font-size: 0.72rem;
    /* Чёрным, в отличие от приглушённой строки реквизитов рядом: это единственное действие в
       нижней полосе, и найти его должно быть легче, чем прочитать номер ОГРНИП. */
    color: var(--color-eerie-black);
    text-decoration: underline;
    text-underline-offset: 0.2em;
    cursor: pointer;
}

.site-footer__cookie-link:hover {
    color: var(--color-accent-dark);
}

/* Реквизиты продавца под подвалом. Колонкой, а не строкой: почта — то, ради чего сюда смотрят
   чаще всего, и она должна стоять отдельно, а не теряться в перечислении номеров. */
/* Одна строка на двоих: реквизиты слева, «Настройки cookie» — прижаты к правому краю.
   Выключка по базовой линии, потому что кегли у них разные; перенос разрешён, поэтому на
   телефоне кнопка сама уйдёт под реквизиты. Точки-разделителя между ними больше нет: она
   нужна была, пока три элемента шли подряд одной строкой, а разнесённым по краям она бы
   висела посреди пустоты. */
.site-footer__requisites {
    display: flex;
    flex-flow: row wrap;
    align-items: baseline;
    justify-content: space-between;
    column-gap: var(--space-4);
    row-gap: var(--space-1);
}

/* Почта — тем же приглушённым цветом, что и вся строка реквизитов: она часть перечисления, а не
   отдельный призыв, и выделяться из него не должна. Что по ней кликают, показывает подчёркивание
   (с отступом, чтобы хвосты «р» и «у» не перечёркивались линией), а темнеет она только под курсором. */
.site-footer__requisites-email {
    color: inherit;
    text-decoration: underline;
    text-underline-offset: 0.2em;
}

.site-footer__requisites-email:hover {
    color: var(--color-text);
}

/* Строка с ИНН и ОГРНИП — служебная: нужна для доверия и по закону, но читать её подряд никто не
   станет. Поэтому мельче и приглушённее почты. Разделители — узкие шпации в разметке, а не
   отступы, чтобы при переносе на узком экране номера не разъезжались по разным строкам с висящей
   точкой. */
.site-footer__requisites-legal {
    margin-bottom: 0;
    font-size: 0.72rem;
    line-height: 1.6;
    color: var(--color-text-muted);
}

/* --------------------------------------------------------------------------
   Newsletter form (shared by footer + home page newsletter section)
   -------------------------------------------------------------------------- */

.newsletter-form {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
    max-width: 420px;
}

.newsletter-form__label {
    flex: 1 1 220px;
}

.newsletter-form__input {
    width: 100%;
    padding: var(--space-3) var(--space-4);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm);
    background: var(--color-surface);
}

.newsletter-form__submit {
    flex: 0 0 auto;
}

/* Галочки согласий между полем и кнопкой. flex-basis: 100% обязателен: .newsletter-form —
   flex-контейнер с переносом, и без него блок встал бы В СТРОКУ с полем ввода, растолкав его и
   кнопку. Фрагмент подписки живёт и в подвале, и на главной, поэтому правило общее. */
.newsletter-form__consents {
    flex: 1 1 100%;
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    margin-top: var(--space-1);
}

/* align-items: flex-start, а не center: текст согласия в две строки переносится, и по центру
   квадратик уезжал бы на середину абзаца вместо первой строки. */
.newsletter-consent {
    display: flex;
    align-items: flex-start;
    gap: var(--space-2);
    font-size: 0.78rem;
    line-height: 1.45;
    color: var(--color-text-muted);
    cursor: pointer;
}

/* flex: none — иначе flex-контейнер сжимает квадратик по ширине, когда текст длинный, и он из
   квадрата превращается в узкую полоску. margin-top подгоняет его к базовой линии первой строки. */
.newsletter-consent input[type="checkbox"] {
    flex: none;
    margin-top: 0.15em;
    cursor: pointer;
}

.newsletter-consent a {
    color: inherit;
    text-decoration: underline;
}

/* Подсветка при попытке отправить без согласия. Красным — только рамка квадратика: красить весь
   текст значило бы кричать на человека там, где достаточно показать, чего не хватает. */
.newsletter-consent.is-invalid input[type="checkbox"] {
    outline: 2px solid #E0393B;   /* тот же красный, что у .checkout-checkbox.is-invalid */
    outline-offset: 2px;
}

/* --------------------------------------------------------------------------
   Cards (collections grid, generic reuse for later tasks)
   -------------------------------------------------------------------------- */

.card,
.collection-card {
    display: block;
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    overflow: hidden;
    transition: box-shadow 0.15s ease, transform 0.15s ease;
}

.collection-card:hover {
    box-shadow: 0 8px 24px rgba(30, 30, 30, 0.08);
    transform: translateY(-2px);
}

/* Deliberately NO lift or shadow on hover any more: a frameless card has no edge to lift, and a
   photo drifting 3px up out of a 2px seam just breaks the grid's alignment. The hover now happens
   inside the photo instead - the second shot fades in and the size/colour panel rises. */

.collection-card__media {
    aspect-ratio: 4 / 5;
    background: var(--color-bg-soft);
    overflow: hidden;
}

.collection-card__media img {
    width: 100%;
    height: 100%;
    object-fit: cover;
}

.collection-card h3,
.collection-card p {
    padding-inline: var(--space-4);
}

.collection-card h3 {
    margin-top: var(--space-4);
    margin-bottom: var(--space-1);
}

.collection-card p {
    padding-bottom: var(--space-4);
    color: var(--color-text-muted);
    font-size: 0.9rem;
}

/* --------------------------------------------------------------------------
   Home page: hero
   -------------------------------------------------------------------------- */

/* Landscape hero photo (1280x1024). The hero takes the photo's own aspect ratio so the whole frame
   shows full-bleed at a normal banner height, with no crop and no side bars (leokid-style banner). */
.hero {
    position: relative;
    isolation: isolate;
    aspect-ratio: 1280 / 1024;
    display: flex;
    align-items: center;
    overflow: hidden;
    color: var(--color-white);
    background: var(--color-alabaster);
}

.hero__image {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
    object-position: center;
    z-index: 1;
}

.hero::after {
    content: '';
    position: absolute;
    inset: 0;
    z-index: 2;
    background: linear-gradient(180deg, rgba(30, 30, 30, 0) 45%, rgba(30, 30, 30, 0.6) 100%);
}

.hero__overlay {
    position: relative;
    z-index: 3;
    width: 100%;
    /* Bottom padding lifts the lede + CTA up off the very edge, sitting them over the sand above
       the child rather than crammed against the bottom. */
    padding: var(--space-5) var(--space-4);
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--space-4);
    text-align: center;
}

.hero__lede {
    max-width: 30.5rem;
    color: var(--color-white);
    text-shadow: 0 1px 14px rgba(30, 30, 30, 0.35);
}

/* Small brand-emblem watermark accent (emblem task, item 4, optional): a faint corner mark on
   the hero photo, the same idea as the little figure-8 mark tucked into the corner of the
   brand's own lifestyle photography. Inline SVG (not an <img>) so `stroke="currentColor"` picks
   up the `color` set here rather than defaulting to black - reused below on the About page photo
   with a different color/opacity for its lighter background. Purely decorative: aria-hidden. */
.brand-watermark {
    position: absolute;
    z-index: 1;
    width: 44px;
    height: 55px;
    pointer-events: none;
}

.brand-watermark--hero {
    right: var(--space-4);
    bottom: var(--space-4);
    color: var(--color-white);
    opacity: 0.35;
    filter: drop-shadow(0 1px 2px rgba(30, 30, 30, 0.35));
}

/* --------------------------------------------------------------------------
   Home page: brand story / USP
   -------------------------------------------------------------------------- */

.brand-story {
    max-width: 45rem;
}

.usp__grid {
    display: grid;
    grid-template-columns: 1fr;
    gap: var(--space-6);
}

.usp__item p {
    color: var(--color-text-muted);
}

.collections__grid {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: var(--space-4);
}

.newsletter-section {
    /* Butter section tint (retheme item 3), instead of the neutral alabaster used elsewhere. */
    position: relative;
    isolation: isolate;
    background: var(--color-accent-soft);
    border-radius: var(--radius-md);
    padding: var(--space-6);
}

/* --------------------------------------------------------------------------
   Cookie consent (Task 14): fixed bottom bar, shown once by static/js/cookie-consent.js.
   -------------------------------------------------------------------------- */

.cookie-consent[hidden] {
    display: none;
}

.cookie-consent {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 60;
    background: var(--color-eerie-black);
    box-shadow: 0 -4px 16px rgba(30, 30, 30, 0.18);
}

/* Плашка закреплена у нижнего края экрана, поэтому она перекрывает низ страницы, и доскроллить
   туда нельзя в принципе: даже в самом низу под плашкой оставался конец подвала — в том числе
   раскрытые «Контакты». Резервируем под неё место; реальную высоту проставляет
   static/js/cookie-consent.js, а после закрытия плашки она обнуляется.

   Запас живёт в ПОДВАЛЕ, а не на body: на body зарезервированная полоса заливалась фоном
   СТРАНИЦЫ, то есть под подвалом появлялась чужая по цвету полоса, которая ещё и меняла длину
   страницы, когда плашку закрывали. В подвале та же полоса залита его собственным градиентом и
   не читается как пустота. */
.site-footer {
    padding-bottom: var(--cookie-bar-space, 0px);
}

.cookie-consent__inner {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: var(--space-4);
    padding-block: var(--space-5);
}

.cookie-consent__text {
    color: var(--color-white);
    font-size: 0.9rem;
    max-width: 42rem;
}

.cookie-consent__text a {
    color: var(--color-pastel-blue);
    text-decoration: underline;
}

.cookie-consent__actions {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-3);
}

/* --------------------------------------------------------------------------
   Catalog (Task 7): filter sidebar, sort/toolbar, product grid, pagination.
   Product-card visuals (.card, .product-card__media aspect-ratio) deliberately mirror
   .collection-card above rather than inventing a second card style.
   -------------------------------------------------------------------------- */

.catalog__header {
    margin-bottom: var(--space-3); /* вкладки/«Найдено» ближе к «Каталогу» — уже расстояние */
}

/* Лёгкий заголовок (вес 400) — спокойнее и современнее, чем базовый жирный 700. То же и у «Избранного». */
.catalog__header h1 {
    font-weight: 400;
    letter-spacing: -0.01em;
}

.catalog__description {
    color: var(--color-text-muted);
    max-width: 40rem;
}

/* Sub-category tab strip (e.g. "Все"/Футболки/Толстовки/... under "Дети 1-8 лет"): mirrors the
   .pagination__link is-active treatment above rather than inventing a third "active state" style. */
.subcategory-tabs {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2) var(--space-5);
    margin-bottom: var(--space-6);
}

/* Plain text tabs (no pill/border boxes): inactive tabs are muted, the active one is dark with an
   underline. The transparent bottom border reserves the underline's space so switching tabs never
   nudges the row. */
.subcategory-tabs__link {
    display: inline-flex;
    align-items: center;
    padding: var(--space-1) 0;
    border-bottom: 2px solid transparent;
    font-family: var(--font-heading);
    font-weight: 600;
    font-size: 0.9rem;
    color: var(--color-text-muted);
    background: none;
    transition: color 0.15s ease, border-color 0.15s ease;
}

.subcategory-tabs__link:hover {
    color: var(--color-text);
}

.subcategory-tabs__link.is-active {
    color: var(--color-eerie-black);
    border-bottom-color: var(--color-eerie-black);
}

/* The catalogue spans the full viewport width (edge to edge, like loobmbrand.ru/newitems) rather
   than sitting inside the 1200px .container - only a small responsive gutter keeps the outer
   cards off the very edge. The heading, breadcrumbs and tabs share the same left gutter so they
   line up with the first product column. */
.catalog {
    padding-inline: clamp(var(--space-4), 2.5vw, var(--space-7));
    padding-block: var(--space-6) var(--space-5);
}

/* No big white void between the catalogue grid and the footer - let the footer sit right under the
   products (its own border + padding provide the separation). */
.site-main:has(.catalog) ~ .site-footer,
.site-main:has(.catalog) + .site-footer {
    margin-top: 0;
}

.catalog__layout {
    display: grid;
    grid-template-columns: 1fr;
    gap: var(--space-6);
    align-items: start;
}

.catalog__filters {
    background: var(--color-bg-soft);
    border-radius: var(--radius-md);
    padding: var(--space-5);
}

.filter-form {
    display: flex;
    flex-direction: column;
    gap: var(--space-5);
}

.filter-form__group {
    border: none;
    padding: 0;
    margin: 0;
}

.filter-form__group legend {
    font-family: var(--font-heading);
    font-weight: 600;
    font-size: 0.95rem;
    margin-bottom: var(--space-3);
    padding: 0;
}

.filter-form__checkbox {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    font-size: 0.9rem;
}

.filter-form__checkbox + .filter-form__checkbox {
    margin-top: var(--space-2);
}

.filter-form__group--swatches {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
}

.filter-form__swatch {
    position: relative;
    display: inline-flex;
}

.filter-form__swatch input {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    opacity: 0;
    cursor: pointer;
}

.filter-form__swatch-dot {
    width: 28px;
    height: 28px;
    border-radius: 50%;
    border: 2px solid var(--color-border);
    display: inline-block;
}

.filter-form__swatch input:checked + .filter-form__swatch-dot {
    border-color: var(--color-accent-dark);
    box-shadow: 0 0 0 2px var(--color-white), 0 0 0 3px var(--color-accent-dark);
}

.filter-form__price-range {
    display: flex;
    gap: var(--space-2);
}

.filter-form__price-range input {
    width: 100%;
    padding: var(--space-2) var(--space-3);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm);
    background: var(--color-surface);
}

.filter-form__submit {
    width: 100%;
}

.catalog__toolbar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-4);
    margin-bottom: var(--space-5);
    flex-wrap: wrap;
}

.catalog__count {
    color: var(--color-text-muted);
    font-size: 0.9rem;
}

.catalog__empty {
    color: var(--color-text-muted);
    padding: var(--space-7) 0;
    text-align: center;
}

/* The photos sit almost flush - a 2px seam between cards, a slightly taller one between rows so a
   name/price band never reads as belonging to the photo underneath it. The cards themselves carry
   no frame (see .product-card below), so the gutter is the ONLY thing separating one shot from the
   next, and keeping it hairline-thin is what makes the grid read as one continuous wall of
   photography rather than a tray of boxed thumbnails. */
.product-grid {
    display: grid;
    grid-template-columns: repeat(2, 1fr);   /* phones: two per row */
    gap: var(--space-5) 2px;
}

@media (min-width: 640px) {
    .product-grid {
        grid-template-columns: repeat(3, 1fr);
    }
}

@media (min-width: 1024px) {
    .product-grid {
        grid-template-columns: repeat(4, 1fr);
    }
}

/* Equal-height polaroid frames across a catalogue row: the grid stretches each cell, and this
   makes the wrapped card fill its cell so a one-line name and a three-line name still produce
   frames of the same height (the extra goes to white frame below the caption). Scoped to the
   catalogue grid, whose children are wrapper <div>s - the favorites grid appends bare
   .product-card articles straight into .product-grid, so a blanket rule would flex those cards. */
.catalog__results .product-grid > * {
    display: flex;
}

.catalog__results .product-grid > * > .product-card {
    width: 100%;
}

/* No frame at all: the photographs ARE the grid. The old polaroid matte (white fill + border +
   padding) put a visible box around every shot and forced a gutter wide enough to justify it; with
   the frame gone the cards sit almost flush (see the 2px gutters above) and the page reads as one
   continuous wall of photography, the way current fashion grids do. */
.product-card {
    background: transparent;
    border: none;
    border-radius: 0;
    padding: 0;
    box-shadow: none;
    transition: transform 0.2s ease;
}

.product-card__media {
    position: relative;
    aspect-ratio: 4 / 5;
    background: var(--color-bg-soft);
    overflow: hidden;
    border-radius: 0;
}

.product-card__media img {
    width: 100%;
    height: 100%;
    object-fit: cover;
}

/* Hover image-swap (like the reference shop): the primary photo stays in flow; the secondary
   is stacked on top of it and fades in when the card is hovered, with a gentle zoom on both.
   Falls back gracefully - a card with only one image simply has no secondary layer. */
.product-card__img--primary {
    transition: opacity 0.5s ease, transform 0.6s ease;
}

.product-card__img--secondary {
    position: absolute;
    inset: 0;
    opacity: 0;
    transition: opacity 0.5s ease, transform 0.6s ease;
}

.product-card:hover .product-card__img--secondary {
    opacity: 1;
}

.product-card:hover .product-card__img--primary,
.product-card:hover .product-card__img--secondary {
    transform: scale(1.04);
}

@media (hover: none) {
    /* No hover on touch devices - never hide the primary behind an invisible layer. */
    .product-card__img--secondary {
        display: none;
    }
}

/* --------------------------------------------------------------------------
   Favorites heart (revamp item 9) — overlaid top-right on the product-card image, and reused
   (bigger, inline with the heading) on the product detail page. Purely a client-side toggle
   (static/js/favorites.js); включённое состояние залито терракотом — тем же, что и сердце
   в шапке (решение владелицы 12.10.2026: «везде одним цветом»).
   -------------------------------------------------------------------------- */

.favorite-heart {
    position: absolute;
    top: var(--space-2);
    right: var(--space-2);
    z-index: 1;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 32px;
    height: 32px;
    border-radius: 50%;
    background: rgba(255, 255, 255, 0.85);
    color: var(--color-eerie-black);
    transition: background-color 0.15s ease, color 0.15s ease, transform 0.15s ease;
}

.favorite-heart:hover {
    transform: scale(1.08);
}

/*
  СЕРДЦЕ В ИЗБРАННОМ — ТЕРРАКОТОВОЕ, как и сердце в шапке. Решение владелицы 12.10.2026:
  «везде одним цветом». До этого включённое сердце на карточке заливалось пастельно-голубым,
  а в шапке горело терракотом — одно и то же действие было показано двумя разными цветами,
  и связь между «нажал сердце на карточке» и «значок в шапке загорелся» не читалась.

  Контур у выключенного сердца при этом остаётся тёмным: цветом отмечено именно включённое
  состояние, иначе нечем будет отличить одно от другого.
*/
.favorite-heart.is-active {
    color: var(--color-terracotta);
}

.favorite-heart.is-active svg path {
    fill: var(--color-terracotta);
    stroke: var(--color-terracotta);
}

.favorite-heart--detail {
    position: static;
    width: 40px;
    height: 40px;
    background: var(--color-bg-soft);
    flex: 0 0 auto;
}

/* Favorites heart overlaid on the product photo (top-right), above the image + zoom hint. */
.product-gallery__main .favorite-heart {
    z-index: 2;
}

/* Hover panel inside the photo (fragments/product-card.html): sizes on the left, colour dots on the
   right, rising from the bottom edge. Frosted rather than solid so the photo still shows through -
   the panel is an annotation on the picture, not a drawer covering it.

   translateY(101%), not 100%: a fractional layout height can leave a hairline of the panel visible
   at exactly 100%, and that sliver reads as a rendering bug along the bottom of every card. */
.product-card__quick {
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 2;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-3);
    padding: var(--space-3) var(--space-4);
    background: rgba(255, 255, 255, 0.92);
    -webkit-backdrop-filter: blur(8px);
    backdrop-filter: blur(8px);
    transform: translateY(101%);
    transition: transform 0.28s cubic-bezier(0.2, 0.7, 0.3, 1);
}

.product-card:hover .product-card__quick,
.product-card:focus-within .product-card__quick {
    transform: translateY(0);
}

/* No hover on a phone, and pinning this permanently over the bottom of every photo would cover the
   very thing people scroll a catalogue to look at. The product page carries all of it anyway. */
@media (max-width: 900px), (hover: none) {
    .product-card__quick {
        display: none;
    }
}

.product-card__sizes {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-3);
}

.product-card__size {
    font-size: 0.78rem;
    letter-spacing: 0.01em;
    color: var(--color-text);
    white-space: nowrap;
}

.product-card__size--out {
    color: var(--color-text-muted);
    opacity: 0.55;
    text-decoration: line-through;
}

/* Deliberately tiny (10px) and unringed: at this size the dots are a hint that the product comes in
   several colourways, not a control - the real, tappable swatches live on the product page. A dark
   border would turn each one into a bullseye and shout louder than the photo behind it, so they get
   only a hairline, and only to keep near-white colourways from vanishing into the panel. */
.product-card__dots {
    display: flex;
    flex-shrink: 0;
    gap: 5px;
}

.product-card__dot {
    width: 10px;
    height: 10px;
    border-radius: 999px;
    border: 1px solid rgba(30, 30, 30, 0.12);
}

/* Caption: name on the left, price on the right, one quiet line under the photo. A fixed two-line
   box for the name is what keeps every price in a row on the same baseline - with the frames gone,
   a long name (the longsleeve) would otherwise push its price a line below its neighbours', and the
   misalignment is glaring across a flush grid. */
/* The horizontal padding is what stops one card's price from running straight into the next card's
   name: with a 2px seam between photos, captions set flush to the card edge read as a single
   sentence across the row ("1 690 ₽ Махровые полосатые шорты"). The photos stay flush; only the
   text is inset. */
.product-card__body {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--space-3);
    padding: var(--space-3) var(--space-2) var(--space-4);
    text-align: left;
}

.product-card__name {
    font-family: var(--font-body);
    font-weight: 400;
    font-size: 0.9rem;
    line-height: 1.35;
    letter-spacing: 0;
    color: var(--color-text);
    margin-bottom: 0;
}

.product-card__price {
    color: var(--color-text);
    font-weight: 400;
    font-size: 0.9rem;
    letter-spacing: 0.01em;
    white-space: nowrap;
}

.pagination {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-2);
    margin-top: var(--space-7);
}

.pagination__link {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 36px;
    height: 36px;
    padding-inline: var(--space-2);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm);
    font-size: 0.9rem;
}

.pagination__link.is-active {
    background: var(--color-accent);
    border-color: var(--color-accent);
    color: var(--color-eerie-black);
}

/* --------------------------------------------------------------------------
   Product detail page (Task 8): image gallery, variant selector chips, quantity stepper.
   Reuses .btn / .product-grid (related products row uses the exact same fragment/grid as the
   catalog page) rather than inventing new primitives for those.
   -------------------------------------------------------------------------- */

.product-detail__layout {
    display: grid;
    grid-template-columns: 1fr;
    gap: var(--space-7);
    margin-bottom: var(--space-8);
}

.product-gallery__main {
    position: relative;
    aspect-ratio: 4 / 5;
    background: var(--color-bg-soft);
    border-radius: 0;
    overflow: hidden;
}

.product-gallery__main img {
    width: 100%;
    height: 100%;
    object-fit: cover;
}

/* Zoom affordance — only once gallery.js has wired the click-to-open-fullscreen behaviour. */
.product-gallery__main.is-zoomable {
    cursor: zoom-in;
}

.product-gallery__zoom-hint {
    position: absolute;
    right: var(--space-3);
    bottom: var(--space-3);
    display: none;
    align-items: center;
    justify-content: center;
    width: 40px;
    height: 40px;
    border-radius: 50%;
    background: rgba(255, 255, 255, 0.9);
    color: var(--color-eerie-black);
    box-shadow: 0 4px 14px rgba(30, 30, 30, 0.18);
    opacity: 0;
    transition: opacity 0.2s ease, transform 0.2s ease;
    pointer-events: none;
}

.product-gallery__main.is-zoomable .product-gallery__zoom-hint {
    display: inline-flex;
    opacity: 0.85;                 /* always visible once zoom is enabled, so it's an obvious cue */
}

.product-gallery__main.is-zoomable:hover .product-gallery__zoom-hint,
.product-gallery__main.is-zoomable:focus-visible .product-gallery__zoom-hint {
    opacity: 1;
    transform: scale(1.08);
}

/* --------------------------------------------------------------------------
   Full-screen image lightbox (gallery.js) — dark backdrop, large centred image, left/right nav.
   -------------------------------------------------------------------------- */
.lightbox {
    position: fixed;
    inset: 0;
    z-index: 200;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-2);
    padding: clamp(var(--space-3), 4vw, var(--space-6));
    background: rgba(20, 20, 20, 0.92);
}

.lightbox[hidden] {
    display: none;
}

.lightbox__stage {
    margin: 0;
    flex: 1 1 auto;
    height: 100%;
    display: flex;
    align-items: center;
    justify-content: center;
    min-width: 0;
}

.lightbox__image {
    max-width: 100%;
    max-height: 100%;
    object-fit: contain;
    border-radius: var(--radius-sm);
    box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5);
}

.lightbox__nav,
.lightbox__close {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    color: var(--color-white);
    background: rgba(255, 255, 255, 0.12);
    border-radius: 50%;
    transition: background-color 0.15s ease;
}

.lightbox__nav:hover,
.lightbox__close:hover {
    background: rgba(255, 255, 255, 0.26);
}

.lightbox__nav {
    width: clamp(44px, 6vw, 60px);
    height: clamp(44px, 6vw, 60px);
    font-size: 2rem;
    line-height: 1;
}

.lightbox__close {
    position: absolute;
    top: var(--space-4);
    right: var(--space-4);
    width: 44px;
    height: 44px;
    font-size: 1.6rem;
    line-height: 1;
}

.lightbox__counter {
    position: absolute;
    bottom: var(--space-4);
    left: 50%;
    transform: translateX(-50%);
    color: rgba(255, 255, 255, 0.85);
    font-size: 0.9rem;
    letter-spacing: 0.05em;
}

/* On phones the on-screen arrows overlap the image edges rather than eating layout width. */
@media (max-width: 599px) {
    .lightbox__nav {
        position: absolute;
        top: 50%;
        transform: translateY(-50%);
    }
    .lightbox__nav--prev { left: var(--space-2); }
    .lightbox__nav--next { right: var(--space-2); }
}

.product-gallery__thumbs {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
    margin-top: var(--space-3);
}

.product-gallery__thumb {
    width: 64px;
    height: 80px;
    padding: 0;
    border: 2px solid transparent;
    border-radius: 0;
    background: var(--color-bg-soft);
    overflow: hidden;
    cursor: pointer;
}

.product-gallery__thumb img {
    width: 100%;
    height: 100%;
    object-fit: cover;
}

.product-gallery__thumb.is-active {
    border-color: var(--color-accent-dark);
}

.product-info__heading {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: var(--space-3);
}

.product-info__heading h1 {
    font-size: 1.35rem;
    margin-bottom: var(--space-1);
}

/* Thin rule between the name/price block and the spec (Состав/Цвет/Размер) block. */
.product-info__divider {
    height: 1px;
    border: 0;
    background: var(--color-border);
    margin-block: var(--space-3);
}

.product-info__sku {
    font-size: 0.85rem;
    color: var(--color-text-muted);
    margin-top: var(--space-1);
    margin-bottom: 0;
}

.product-info__price {
    font-family: var(--font-heading);
    font-size: 1.3rem;
    font-weight: 600;
    color: var(--color-text);
    margin-top: var(--space-3);
    margin-bottom: 0;
}

.product-info__meta {
    font-size: 0.9rem;
    color: var(--color-text-muted);
    margin-bottom: var(--space-1);
}

.product-info__meta span {
    color: var(--color-text);
}

/* Size table (revamp item 7): a small reference chart, size range -> age, always showing the
   storefront's whole four-range grid. */

/* Wider modal so all four size columns are visible at once (the default 480px panel cut the last
   "116–122" column off). */
#size-guide-modal .modal__panel {
    width: min(780px, 100%);
}

.product-size-table__scroll {
    margin-top: var(--space-5);
    overflow-x: auto;
}

.product-size-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 0.85rem;
}

.product-size-table th,
.product-size-table td {
    padding: var(--space-3) var(--space-2);
    border: 1px solid var(--color-border);
    text-align: center;
    white-space: nowrap;
}

.product-size-table thead th {
    background: var(--color-bg-soft);
    font-family: var(--font-heading);
    font-weight: 600;
}

.product-size-table tbody th {
    text-align: left;
    color: var(--color-text-muted);
    font-weight: 600;
}

.product-size-table__note {
    margin-top: var(--space-3);
    font-size: 0.8rem;
    color: var(--color-text-muted);
}

/* Характеристики (revamp item 8): a plain definition list, term/value pairs stacked on mobile. */
.product-specs {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
}

.product-specs__row {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2) var(--space-3);
    padding-bottom: var(--space-2);
    border-bottom: 1px solid var(--color-border);
    /*
      ЗНАЧЕНИЕ ПО ЦЕНТРУ ОТНОСИТЕЛЬНО ПОДПИСИ. Подписи «Страна производства» и «Декларация
      о соответствии» не помещаются в 140 px и переносятся на две строки, а значение рядом
      прилипало к верхней — «Россия» висела над пустотой и читалась как надпись не на своём
      месте. Выравнивание по центру ставит её напротив середины подписи. Замечено владелицей
      на карточке товара.
    */
    align-items: center;
}

/*
  Исключение — «Уход»: там справа не значение в строку, а целый список с абзацем-вступлением
  и пятью пунктами. Центрировать его относительно короткой подписи значит утопить подпись
  в середину списка; она должна стоять у его начала.
*/
.product-specs__row--care {
    align-items: flex-start;
}

/* The last row (Уход, whose own care-list already reads as a clean stopping point) must not carry
   the divider used BETWEEN rows - without this, every row (including the last) got a
   border-bottom, so a stray horizontal line rendered right after the care-instruction list,
   inside the accordion's own bottom padding. */
.product-specs__row:last-child {
    padding-bottom: 0;
    border-bottom: none;
}

.product-specs__row dt {
    flex: 0 0 140px;
    font-weight: 600;
    color: var(--color-text-muted);
}

.product-specs__row dd {
    flex: 1 1 200px;
    margin: 0;
}

/* Уход (product-detail item 3): the "Материал/уход" row's replacement - an intro line plus a
   small icon + text row per care instruction, instead of a plain bullet list. */
.product-specs__row--care dd {
    flex-basis: 100%;
}

/*
  ВВОДНАЯ СТРОКА УХОДА — ОСНОВНЫМ ЦВЕТОМ, а не приглушённым. Она стояла серой заодно с
  подписями характеристик, но это не подпись: это обращение к покупателю с просьбой, и
  читается оно наравне с остальным текстом страницы. Приглушённый цвет делал её похожей
  на техническую сноску, которую взгляд пропускает. Замечено владелицей.
*/
.care-list__intro {
    margin: 0 0 var(--space-3);
    color: var(--color-text);
}

.care-list {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    list-style: none;
    padding-left: 0;
    margin: 0;
}

.care-list__item {
    display: flex;
    align-items: center;
    gap: var(--space-3);
}

.care-list__icon {
    flex: 0 0 auto;
    color: var(--color-accent-dark);
}

/* Доставка и оплата / Возврат (revamp item 8). */
.product-shipping-info ul {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
}

.product-shipping-info li {
    position: relative;
    padding-left: var(--space-4);
    color: var(--color-text-muted);
}

.product-shipping-info li::before {
    content: '';
    position: absolute;
    left: 0;
    top: 0.6em;
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background: var(--color-pastel-blue);
}


/* Colour options as photo thumbnails (Ziggy-Zaza style, fragments product/detail.html): a row of
   small garment photos, one per colourway product, the selected one ringed. Each is a link to that
   colour's own product page. */
.product-colorways__row {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-3);
    margin-top: var(--space-2);
}

.product-colorway {
    display: block;
    width: 58px;
    height: 72px;
    border-radius: var(--radius-sm);
    overflow: hidden;
    box-shadow: 0 0 0 1px var(--color-border);
    transition: box-shadow 0.15s ease, transform 0.15s ease;
}

.product-colorway img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
}

.product-colorway:not(.product-colorway--active):hover {
    transform: translateY(-1px);
    box-shadow: 0 0 0 1px var(--color-text-muted);
}

/* The current colour: a dark ring set off from the photo by a gap of page background, and not a
   link (it's the page you're on). */
.product-colorway--active {
    box-shadow: 0 0 0 2px var(--color-eerie-black);
    cursor: default;
}

.product-colorway:focus-visible {
    outline: 2px solid var(--color-accent-dark);
    outline-offset: 2px;
}

/* The vertical rhythm here is deliberately tight (space-4 gaps, not space-5) so the info column of
   the longsleeve - the one product with an extra "Цвет швов" selector - still fits inside the main
   photo's height. That is what lets its `margin-top: auto`-pinned "О модели"/"Детали" accordions
   end level with the bottom of the first photo instead of overflowing past it. Shorter products
   (no seam-colour row) simply gain a little more auto-margin above the accordions; their alignment
   is unaffected. */
.add-to-cart-form {
    display: flex;
    flex-direction: column;
    gap: var(--space-4);
    max-width: 360px;
    margin-top: var(--space-4);
}

/* Soft, rounded add-to-cart button in the brand's muted tone - sentence case, pill shape, gentle
   weight (the old solid-black UPPERCASE spec button read as harsh against the pastel palette). */
.add-to-cart-form__submit {
    /*
      ЕДИНЫЙ ВИД С «ПЕРЕЙТИ К ОПЛАТЕ» — решение владелицы. Та кнопка скруглена на --radius-sm
      и залита фирменным синим; эта остаётся светлой (главное действие на странице товара
      не должно кричать раньше времени), но берёт то же скругление и тот же синий — в рамке,
      в тексте и в заливке при наведении. Чёрный, стоявший здесь с 9 июля, был единственным
      местом на витрине с этим цветом и выбивался из ряда.
    */
    background: transparent;
    border: 1px solid var(--color-accent-dark);
    color: var(--color-accent-dark);
    padding-block: var(--space-4);
    border-radius: var(--radius-sm);
    text-transform: none;
    letter-spacing: 0.02em;
    font-weight: 500;
    font-size: 0.95rem;
}

.add-to-cart-form__submit:hover {
    background: var(--color-accent-dark);
    border-color: var(--color-accent-dark);
    color: var(--color-white);
}

/* Colour: informational only (revamp follow-up) - a round swatch + label, never a selector
   (see js/variant-selector.js's header comment). */
.product-color-static {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: var(--space-2);
}

/* "Цвет" label, same quiet soft style as the "Размер" label above it. */
.product-color-static__title {
    font-family: var(--font-heading);
    font-weight: 600;
    font-size: 0.85rem;
    text-transform: none;
    letter-spacing: 0;
    color: var(--color-text-muted);
}

/* Colour: a round swatch + name inline, no bordered box (the boxed paint-chip read as clunky). */
.product-color-static__chip {
    display: inline-flex;
    align-items: center;
    gap: var(--space-3);
    padding: 0;
    border: none;
    background: transparent;
    font-size: 0.95rem;
}

/* Round now, but deliberately still the 40px it always was: the info column's total height is what
   lines its bottom edge up with the bottom of the gallery next to it, and shrinking this swatch to
   match the 26px seam dots below pulled that edge 14px short. It is also the one colour here that
   is a crop of the real product photo rather than a flat fill, so it earns being the larger mark -
   at 26px a print or a melange just turns to mush. */
.product-color-static__swatch {
    width: 40px;
    height: 40px;
    border-radius: 999px;
    border: 1px solid rgba(30, 30, 30, 0.12);
    flex: 0 0 auto;
    background-size: cover;
    background-position: center;
}

.product-color-static__name {
    font-weight: 500;
}

/* Size guide: opens #size-guide-modal (static/js/modal.js) instead of an inline table. */
.size-guide-trigger {
    align-self: flex-start;
    font-size: 0.85rem;
    text-decoration: underline;
    color: var(--color-text-muted);
}

.size-guide-trigger:hover {
    color: var(--color-text);
}

.variant-selector__group {
    margin-bottom: var(--space-3);
}

.variant-selector__group[hidden] {
    display: none;
}

.variant-selector__label {
    display: block;
    font-family: var(--font-heading);
    font-weight: 600;
    font-size: 0.85rem;
    text-transform: none;
    letter-spacing: 0;
    color: var(--color-text-muted);
    margin-bottom: var(--space-2);
}

.variant-selector__chips {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2) var(--space-5);
}

/* Sizes as a clean row of text (no boxes): the chosen one is underlined, loobmbrand-style. */
.variant-chip {
    padding: var(--space-1) 0;
    /* Фиксированная минимальная ширина: иначе чип подгоняется под ширину текста, а цифры у Onest и
       запасного шрифта разной ширины — при подмене шрифта чипы «скакали». Теперь ширина не зависит
       от шрифта (размеры вида «116–122» помещаются), поэтому подмена ничего не двигает. */
    min-width: 4.25rem;
    border: none;
    border-radius: 0;
    background: transparent;
    font-size: 0.95rem;
    font-weight: 500;
    text-align: center;
    color: var(--color-text-muted);
    border-bottom: 1.5px solid transparent;
    cursor: pointer;
    transition: color 0.15s ease, border-color 0.15s ease;
}
.variant-chip:not(.is-active):not(.is-disabled):hover {
    color: var(--color-text);
    border-bottom-color: var(--color-border);
}

.variant-chip.is-active {
    color: var(--color-text);
    border-bottom-color: var(--color-eerie-black);
    font-weight: 600;
}

.variant-chip:disabled,
.variant-chip.is-disabled {
    opacity: 0.4;
    text-decoration: line-through;
    cursor: not-allowed;
}

/* Seam-colour selector (longsleeve). The dropdown itself carries the styling, so the wrapping
   group is just a plain block that lets the dropdown span the full column width. */
.variant-selector__group--color {
    padding: 0;
    border: none;
    background: transparent;
}

.variant-selector__chips--swatches {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-3);
    align-items: center;
}

/* The colour NAME, printed in the label itself ("Цвет швов — коричневый", js/variant-selector.js).
   The swatch alone can only show a shopper what a colour looks like, never what it is called - and
   the name is what they need when they come back to reorder or ask about it. It follows the cursor
   across the row (hover preview) and settles on the selected colour. */
.variant-selector__value {
    font-weight: 400;
    color: var(--color-text);
}

.variant-selector__value::before {
    content: ' — ';
    color: var(--color-text-muted);
}

/* Colour choice as round swatches: a filled circle per colourway, sized to be tapped, with the
   selected one carrying a thin dark ring set off from the dot by a gap of page background. The ring
   (rather than a heavy border or a fill change) is what keeps the colour itself undistorted - a
   border eats into a small dot and shifts how the colour reads. Unavailable colourways are struck
   through with a diagonal bar, the same "shown but not offered" language as the sizes. */
.color-swatch {
    position: relative;
    display: inline-flex;
    width: 28px;
    height: 28px;
    padding: 0;
    border-radius: 999px;
    background: transparent;
    border: none;
    box-shadow: 0 0 0 1.5px transparent;
    cursor: pointer;
    transition: box-shadow 0.15s ease, transform 0.15s ease;
}

/* The dot is inset inside the button so the ring on .is-active lands in that gap instead of on the
   colour. The hairline border only exists to keep near-white colourways (молочный, айвори) from
   dissolving into the page. */
.color-swatch__dot {
    width: 18px;
    height: 18px;
    margin: auto;
    border-radius: 999px;
    border: 1px solid rgba(30, 30, 30, 0.12);
}

.color-swatch:not(.is-active):not(.is-disabled):hover {
    box-shadow: 0 0 0 1.5px var(--color-border);
    transform: translateY(-1px);
}

.color-swatch:focus-visible {
    outline: 2px solid var(--color-accent-dark);
    outline-offset: 2px;
}

.color-swatch.is-active {
    box-shadow: 0 0 0 1.5px var(--color-eerie-black);
}

.color-swatch.is-disabled {
    opacity: 0.4;
    cursor: not-allowed;
}

/* Diagonal strike across an unavailable colourway - opacity alone reads as "loading", not as
   "sold out". */
.color-swatch.is-disabled::after {
    content: '';
    position: absolute;
    inset: 4px;
    background: linear-gradient(to top right,
                                transparent calc(50% - 1px),
                                var(--color-text) calc(50% - 1px),
                                var(--color-text) calc(50% + 1px),
                                transparent calc(50% + 1px));
    border-radius: 999px;
}

/* When the shopper clicks the blocked add-to-cart button, the colour selector "signals itself":
   a quick shake plus an emphasised outline. */
@keyframes attention-shake {
    0%, 100% { transform: translateX(0); }
    20%, 60% { transform: translateX(-5px); }
    40%, 80% { transform: translateX(5px); }
}

.needs-attention {
    animation: attention-shake 0.4s ease;
}

@media (prefers-reduced-motion: reduce) {
    .needs-attention {
        animation: none;
    }
}

.variant-selector__native {
    display: block;
    font-size: 0.85rem;
    color: var(--color-text-muted);
}

.variant-selector__native select {
    display: block;
    width: 100%;
    margin-top: var(--space-2);
    padding: var(--space-2) var(--space-3);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm);
    background: var(--color-surface);
}

.quantity-stepper {
    display: inline-flex;
    align-items: center;
    width: fit-content;
    border: 1px solid var(--color-border);
    border-radius: 0;
    overflow: hidden;
}

.quantity-stepper__btn {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 40px;
    height: 40px;
    font-size: 1.1rem;
    color: var(--color-text-muted);
    transition: color 0.15s ease, background 0.15s ease;
}

.quantity-stepper__btn:not(:disabled):hover {
    /* РАМКА, А НЕ ЗАЛИВКА. Заливка фоном закрашивала ячейку в другой цвет — то самое пятно,
       из-за которого бросалось в глаза белое поле ввода, и разница была всего в шесть единиц
       (#F8F8EF против #F2F2E9): мало, чтобы прочитаться как отклик, достаточно, чтобы
       заметить неровность. Одно потемнение знака оказалось незаметным совсем.

       Рамка рисуется внутренней тенью, а не border: border сдвинул бы содержимое ячейки
       на два пикселя, и знак подпрыгивал бы при наведении. Кнопки «Добавить в корзину»
       не касается — там инверсия в тёмное сделана намеренно, это главное действие. */
    color: var(--color-text);
    box-shadow: inset 0 0 0 2px var(--color-text);
}

.quantity-stepper input {
    width: 48px;
    height: 40px;
    border: none;
    border-left: 1px solid var(--color-border);
    border-right: 1px solid var(--color-border);
    text-align: center;
    /* ПРОЗРАЧНЫЙ, А НЕ БЕЛЫЙ. Своей заливки у поля не было, и браузер подставлял свою —
       чистый #FFFFFF. Рядом «−» и «+» заливки не имеют вовсе, и кнопка «Добавить в корзину»
       тоже прозрачная: сквозь них просвечивает молочный фон страницы. Из-за этого середина
       счётчика выглядела белой вставкой в молочной рамке.

       Прозрачный, а не конкретный цвет: тот же счётчик стоит и в корзине, где фон другой.
       Жёсткий цвет починил бы одну страницу и сломал вторую. */
    background: transparent;
    color: inherit;
}

/* Hide the native number-input spinner (up/down) arrows on every quantity stepper (PDP + cart
   lines) - only the −/+ buttons and the typed value should be visible. Scoped to
   .quantity-stepper input so other, unrelated number inputs elsewhere are unaffected. */
.quantity-stepper input[type=number]::-webkit-inner-spin-button,
.quantity-stepper input[type=number]::-webkit-outer-spin-button {
    -webkit-appearance: none;
    margin: 0;
}

.quantity-stepper input[type=number] {
    -moz-appearance: textfield;
    appearance: textfield;
}

.add-to-cart-form__stock-hint {
    min-height: 1.2em;
    font-size: 0.85rem;
    color: var(--color-text-muted);
}

/* "Уже в корзине" control on the product page: a live +/- stepper for the selected size's cart
   line, shown in place of the "Добавить в корзину" button once that size is in the cart. */
.pdp-in-cart {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: var(--space-3) var(--space-4);
}

/* These carry their own `display`, so the plain [hidden] attribute (UA display:none) would lose to
   it - re-assert display:none when hidden, at higher specificity, so variant-selector.js can toggle
   between the add button/stepper and the in-cart stepper by setting `hidden`. */
.pdp-in-cart[hidden],
.add-to-cart-form__submit[hidden],
.quantity-stepper[hidden] {
    display: none;
}

.pdp-in-cart__label {
    font-weight: 600;
}

.pdp-in-cart__stepper .quantity-stepper__btn:disabled {
    opacity: 0.4;
    cursor: not-allowed;
}

/* «+» ПРИ ИСЧЕРПАННОМ ОСТАТКЕ ОСТАЁТСЯ ЖИВЫМ. Раньше он выключался, и курсор показывал
   перечёркнутый кружок — молчаливый отказ, из-за которого владелица решила, что страница
   сломалась. Теперь кнопка нажимается и отвечает дрожанием: видно, что услышали. */
.pdp-in-cart__stepper .quantity-stepper__btn[data-at-max="1"] {
    cursor: pointer;
}

/* Число в блоке «В корзине» — не поле ввода, а подпись, поэтому дрожит вместе с кнопкой:
   иначе двигается «+», а причина в соседнем числе. */
.pdp-in-cart__qty.sv-nope {
    display: inline-block;
}

.pdp-in-cart__qty {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 48px;
    height: 36px;
    padding-inline: var(--space-2);
    border-left: 1px solid var(--color-border);
    border-right: 1px solid var(--color-border);
    font-weight: 600;
}

.pdp-in-cart__link {
    color: var(--color-text-muted);
    text-decoration: underline;
    text-underline-offset: 2px;
}

.pdp-in-cart__link:hover {
    color: var(--color-text);
}

.product-tabs {
    display: flex;
    flex-direction: column;
    max-width: 45rem;
    margin-top: var(--space-6);
    border-top: 1px solid var(--color-border);
}

/* --------------------------------------------------------------------------
   Accordions (Описание / Характеристики / Доставка и возврат on the product
   page) - collapsed by default, no JS height-measuring: `.accordion__panel`
   animates via a `max-height` 0->large transition rather than the `grid-
   template-rows: 0fr->1fr` trick, which still leaks a line of the collapsed
   panel's content: a stretched CSS Grid item's used height is computed as
   (available track space − its own padding), clamped at 0 rather than going
   negative, so `.accordion__panel-inner`'s padding-bottom alone renders as a
   visible sliver even at a genuine `0fr` track and `overflow: hidden`.
   `max-height` on `.accordion__panel` itself has no such gotcha: it caps the
   panel's own generated box (padding included) directly, so `overflow:
   hidden` clips everything - content and padding - at true zero height.
   -------------------------------------------------------------------------- */

.accordion {
    border-bottom: 1px solid var(--color-border);
}

/* Overrides the global `section + section { margin-top: var(--space-8) }` rule (see near the top
   of this file) specifically for the PDP accordions - Описание/Характеристики/Доставка и возврат
   are each their own <section class="accordion">, so without this override they'd inherit that
   generic big inter-section gap and no longer read as one tight, FAQ-style stacked list separated
   only by .accordion's own border-bottom. */
.product-tabs .accordion + .accordion {
    margin-top: 0;
}

.accordion__header-h {
    margin: 0;
    font-size: inherit;
}

.accordion__header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    width: 100%;
    padding: var(--space-4) 0;
    font-family: var(--font-heading);
    font-weight: 700;
    font-size: 1.1rem;
    text-align: left;
}

.accordion__chevron {
    flex: 0 0 auto;
    color: var(--color-text-muted);
    transition: transform 0.2s ease;
}

.accordion.is-open .accordion__chevron {
    transform: rotate(180deg);
}

.accordion__panel {
    max-height: 0;
    overflow: hidden;
    transition: max-height 0.3s ease;
}

.accordion.is-open .accordion__panel {
    /* Comfortably above the tallest panel's real content (Характеристики' six-row <dl> plus
       margins) so the transition never visibly clips an open panel; overflow stays hidden on
       the way there but the panel's actual (auto) content height is what's ultimately shown. */
    max-height: 700px;
}

.accordion__panel-inner {
    overflow: hidden;
}

.accordion__panel-inner > *:first-child {
    margin-top: 0;
}

.accordion__panel-inner p {
    color: var(--color-text-muted);
    white-space: pre-line;
}

.accordion__panel-inner {
    padding-bottom: var(--space-4);
}

.accordion__panel-inner h3 {
    margin-top: var(--space-5);
}

/* --------------------------------------------------------------------------
   Modal (size guide, static/js/modal.js) - a centered overlay, closed by
   default (`hidden`); the backdrop and the × button both carry
   `data-modal-close`.
   -------------------------------------------------------------------------- */

.modal {
    position: fixed;
    inset: 0;
    /* Высота — по ВИДИМОЙ части экрана. На телефоне (iOS Safari) `inset: 0` растягивает
       фиксированный слой на полный «большой» экран, включая область под панелью браузера, поэтому
       окно, отцентрированное по такому слою, визуально уезжало ВНИЗ. `dvh` учитывает панель, и
       окно встаёт ровно по центру видимой области. Браузеры без поддержки dvh просто игнорируют
       строку и ведут себя как раньше. */
    height: 100dvh;
    z-index: 60;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: var(--space-4);
}

.modal[hidden] {
    display: none;
}

.modal__backdrop {
    position: absolute;
    inset: 0;
    background: rgba(30, 30, 30, 0.5);
}

.modal__panel {
    position: relative;
    z-index: 1;
    width: min(480px, 100%);
    max-height: 85vh;
    overflow-y: auto;
    background: var(--color-surface);
    border-radius: var(--radius-md);
    padding: var(--space-6);
}

.modal__panel h2 {
    margin-bottom: var(--space-4);
    padding-right: var(--space-6);
}

.modal__close {
    position: absolute;
    top: var(--space-3);
    right: var(--space-3);
    width: 32px;
    height: 32px;
    font-size: 1.4rem;
    line-height: 1;
    color: var(--color-text-muted);
}

.modal__close:hover {
    color: var(--color-text);
}

/* --------------------------------------------------------------------------
   "Товар добавлен в корзину" modal (product/detail.html, static/js/cart.js). Reuses
   .modal/.modal__backdrop/.modal__panel/.modal__close for the overlay shell, same as the
   size-guide and welcome modals.
   -------------------------------------------------------------------------- */

.cart-added-modal__product {
    display: flex;
    align-items: center;
    gap: var(--space-4);
    margin-bottom: var(--space-5);
}

.cart-added-modal__image {
    width: 88px;
    height: 88px;
    flex-shrink: 0;
    object-fit: cover;
    border-radius: var(--radius-sm);
    background: var(--color-bg-tint-blue);
}

.cart-added-modal__name {
    margin: 0 0 var(--space-1);
    font-weight: 600;
}

.cart-added-modal__qty {
    margin: 0 0 var(--space-1);
    color: var(--color-text-muted);
}

.cart-added-modal__price {
    margin: 0;
    font-weight: 700;
}

.cart-added-modal__actions {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
}

.cart-added-modal__actions .btn {
    width: 100%;
}

/* --------------------------------------------------------------------------
   Favorites-page size picker (templates/favorites.html, static/js/favorites.js). Reuses
   .modal/.modal__backdrop/.modal__panel/.modal__close for the overlay shell, and .variant-chip
   (product/detail.html's own size chips) for each size pill, so a disabled/out-of-stock size
   looks and behaves identically in both places.
   -------------------------------------------------------------------------- */

.size-picker-modal__product-name {
    margin: 0 0 var(--space-4);
    font-weight: 600;
}

.size-picker-modal__sizes {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
    margin-bottom: var(--space-3);
}

.size-picker-modal__message {
    margin: 0;
    color: var(--color-text-muted);
}

/* --------------------------------------------------------------------------
   Welcome promo modal (fragments/welcome-modal.html, static/js/welcome.js) -
   shown once per browser on first visit. Reuses .modal/.modal__backdrop/
   .modal__panel/.modal__close for the overlay shell; everything below is its
   own branded content, a soft blue-to-butter gradient card rather than the
   plain white size-guide modal panel.
   -------------------------------------------------------------------------- */

/* Editorial split card: photo (left) + gift offer (right). */
.welcome-modal__panel {
    position: relative;
    isolation: isolate;
    width: min(760px, 100%);
    padding: 0;
    overflow: hidden;
    border: none;
    border-radius: var(--radius-md);
    text-align: left;
    display: grid;
    grid-template-columns: 0.9fr 1.1fr;
    background: var(--color-surface);
}

/* Left: warm lifestyle photo with the slogan over its darkened bottom. */
.welcome-modal__media {
    position: relative;
    min-height: 430px;
    background-size: cover;
    background-position: center;
}

.welcome-modal__media::after {
    content: '';
    position: absolute;
    inset: 0;
    background: linear-gradient(180deg, rgba(30, 30, 30, 0) 50%, rgba(30, 30, 30, 0.55) 100%);
}

.welcome-modal__slogan {
    position: absolute;
    left: var(--space-4);
    bottom: var(--space-4);
    z-index: 1;
    color: var(--color-white);
    font-family: var(--font-heading);
    font-size: 0.92rem;
    line-height: 1.35;
    text-shadow: 0 1px 12px rgba(30, 30, 30, 0.45);
}

/* Right: soft brand gradient (сливочный Beige ↔ голубой Pastel Blue), like the bag. */
.welcome-modal__body {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: var(--space-3);
    padding: var(--space-6);
    background-color: var(--color-alabaster);
    background-image:
        radial-gradient(95% 80% at 8% 6%, rgba(229, 227, 197, 0.92), rgba(229, 227, 197, 0) 60%),
        radial-gradient(95% 95% at 96% 100%, rgba(168, 186, 198, 0.8), rgba(168, 186, 198, 0) 62%);
}

/* Эмблема — на самом фото, в правом нижнем углу, напротив слогана. Мельче прежнего (было 44px
   в текстовой части), белая с мягкой тенью — как слоган, чтобы читалась на любом кадре. */
.welcome-modal__media .brand-emblem {
    position: absolute;
    right: var(--space-4);
    bottom: var(--space-4);
    z-index: 1;
    width: 30px;
    color: var(--color-white);
    opacity: 0.92;
    filter: drop-shadow(0 1px 8px rgba(30, 30, 30, 0.45));
}

.welcome-modal__eyebrow {
    margin: 0;
    text-transform: uppercase;
    letter-spacing: 0.14em;
    font-size: 0.7rem;
    color: var(--color-text-muted);
}

.welcome-modal__title {
    margin: 0;
    font-size: 1.6rem;
    line-height: 1.12;
    color: var(--color-eerie-black);
}

.welcome-modal__text {
    margin: 0;
    font-size: 0.92rem;
    line-height: 1.5;
    color: var(--color-text);
}

.welcome-modal__text strong {
    font-weight: 700;
    color: var(--color-eerie-black);
}

.welcome-modal__promo {
    width: 100%;
    margin-top: var(--space-1);
}

.welcome-modal__code {
    display: block;
    width: 100%;
    padding: var(--space-3);
    border: 1px dashed var(--color-camel);
    background: var(--color-surface);
    text-align: center;
    font-family: var(--font-heading);
    font-weight: 700;
    font-size: 1.05rem;
    letter-spacing: 0.1em;
    color: var(--color-eerie-black);
}

.welcome-modal__copy {
    width: 100%;
    padding: var(--space-3) var(--space-5);
    background: var(--color-eerie-black);
    color: var(--color-white);
    font-weight: 600;
    letter-spacing: 0.02em;
    cursor: pointer;
    transition: background 0.15s ease;
}

.welcome-modal__copy:hover {
    background: #000;
}

.welcome-modal__copied {
    margin: 0;
    font-size: 0.85rem;
    color: var(--color-accent-dark);
}

@media (max-width: 640px) {
    .welcome-modal__panel {
        grid-template-columns: 1fr;
        width: min(400px, 100%);
    }

    .welcome-modal__media {
        min-height: 170px;
    }

    .welcome-modal__body {
        padding: var(--space-5);
    }
}

/* --------------------------------------------------------------------------
   Breadcrumbs + "Вернуться назад" (fragments/breadcrumbs.html, product/detail.html).
   -------------------------------------------------------------------------- */

/* "← Вернуться назад" link, kept on the info pages (Доставка / Контакты / Возврат / Соглашение /
   О нас) at the top-left. Its styling was dropped when the link was removed from the catalog/product
   pages; restored here so it sits neatly at the top instead of falling to the default inline flow. */
.product-detail__back {
    display: inline-block;
    margin-bottom: var(--space-4);
    color: var(--color-text-muted);
    font-size: 0.9rem;
}

.product-detail__back:hover {
    color: var(--color-text);
}

.breadcrumbs {
    margin-top: var(--space-4);
    margin-bottom: var(--space-5);
}

/* leokid-style: small uppercase crumbs sitting at the very top of the product page. */
.breadcrumbs__list {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-1) var(--space-3);
    list-style: none;
    padding: 0;
    margin: 0;
    font-size: 0.72rem;
    text-transform: uppercase;
    letter-spacing: 0.06em;
}

.breadcrumbs__item {
    display: flex;
    align-items: center;
    gap: var(--space-3);
}

.breadcrumbs__item:not(:last-child)::after {
    content: '/';
    color: var(--color-border);
}

.breadcrumbs__link {
    color: var(--color-text-muted);
}

.breadcrumbs__link:hover {
    color: var(--color-accent-dark);
}

.breadcrumbs__current {
    color: var(--color-text);
}

.related-products {
    margin-top: var(--space-8);
}

.related-products h2 {
    margin-bottom: var(--space-5);
}

/* --------------------------------------------------------------------------
   Cart page (Task 9). Reuses .btn / .quantity-stepper from the product detail page rather than
   redefining the stepper a second time.
   -------------------------------------------------------------------------- */

.cart-page__title {
    margin-top: var(--space-5); /* чуть опускаем от шапки, чтобы не липло к верхней границе */
    margin-bottom: var(--space-6);
}

.cart-flash {
    padding: var(--space-3) var(--space-4);
    border-radius: var(--radius-sm);
    margin-bottom: var(--space-5);
}

.cart-flash--success {
    background: var(--color-beige);
    color: var(--color-eerie-black);
}

.cart-flash--error {
    background: var(--color-danger);
    color: var(--color-white);
}

.cart-empty {
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
    gap: var(--space-4);
    padding: var(--space-8) 0;
    color: var(--color-text-muted);
}

/* Empty-state copy (cart + favorites): a tidy centred column that stays within the width of
   the "Смотреть каталог" button below it rather than sprawling wider than the CTA. */
.empty-state__text {
    max-width: 20rem;
    margin: 0 auto;
    line-height: 1.5;
}

/* --------------------------------------------------------------------------
   Brand emblem accents (fragments/brand.html) + faint pattern watermark.
   -------------------------------------------------------------------------- */

.brand-emblem {
    display: block;
    width: 40px;
    height: auto;
}

/* Empty states (cart + favorites): centred column, copy then the catalog button. The emblem that
   used to sit above the copy was dropped at the owner's request. */
.empty-state {
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
    gap: var(--space-4);
    padding: var(--space-8) 0;
    color: var(--color-text-muted);
}

/* Эмблема выводится ровно в двух местах — на главной и в окне с промокодом, оба раза в ПРАВОМ
   нижнем углу. Правило для страниц ошибок (404/500) удалено: разметки под него давно нет,
   в шаблонах эмблема там не выводится. */

/* Brand emblem (horizontal, mono white) overlaid on the hero photo, bottom-right corner. */
.hero__emblem {
    position: absolute;
    right: clamp(var(--space-6), 7vw, var(--space-8));
    bottom: var(--space-5);
    z-index: 3;
    pointer-events: none;
}

.hero__emblem .brand-emblem {
    width: 52px;
    color: var(--color-white);
}

/* Smaller hero emblem on mobile (it read as oversized on a phone). */
@media (max-width: 768px) {
    .hero__emblem .brand-emblem {
        width: 26px;
    }
}

.cart-page__layout {
    display: flex;
    flex-direction: column;
    gap: var(--space-6);
}

.cart-lines {
    display: flex;
    flex-direction: column;
    gap: var(--space-5);
}

/* Each line is a single self-contained flex row (photo + body), never a multi-area grid - the
   old 4-area grid ("media info/form/total/remove") is what used to visually collapse when
   content overflowed. min-width: 0 on the body lets long product names/SKUs wrap or truncate
   instead of pushing the row wider than its container; the remove button is absolutely
   positioned but only relative to *this* row (position: relative below), so it can never bleed
   past it into the footer. */
.cart-line {
    position: relative;
    display: flex;
    gap: var(--space-4);
    padding: var(--space-5) 0;
    border-bottom: 1px solid var(--color-border);
}

.cart-line__media {
    flex: 0 0 96px;
    width: 96px;
    aspect-ratio: 4 / 5;
    background: var(--color-bg-soft);
    overflow: hidden;
    border-radius: var(--radius-sm);
}

.cart-line__media img {
    width: 100%;
    height: 100%;
    object-fit: cover;
}

.cart-line__body {
    flex: 1 1 auto;
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
    padding-right: var(--space-7);
}

.cart-line__name {
    display: block;
    font-family: var(--font-heading);
    font-weight: 600;
}

.cart-line__unit-price,
.cart-line__variant,
.cart-line__sku {
    color: var(--color-text-muted);
    font-size: 0.85rem;
}

.cart-line__footer {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-3);
    margin-top: var(--space-2);
}

.cart-line__total {
    font-weight: 700;
    color: var(--color-accent-dark);
}

.cart-remove-form {
    position: absolute;
    top: var(--space-4);
    right: 0;
}

.cart-line__remove {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 28px;
    height: 28px;
    border-radius: 50%;
    color: var(--color-text-muted);
    font-size: 1rem;
    line-height: 1;
}

.cart-line__remove:hover {
    color: var(--color-danger);
    background: var(--color-bg-soft);
}

/* Панель «Ваш заказ» (и та же панель в корзине). Раньше красилась общим мягким фоном, и
   после перевода страницы на тёплую бумагу разница между ними осталась в пять единиц —
   владелица справедливо сказала, что панель «сливается практически». Общий токен трогать
   нельзя: им же залиты подложки фотографий товара и состояния наведения, там усиление тона
   выглядело бы грязью. Поэтому панель получает собственный, чуть более бежевый тон и
   волосяную рамку: разница с бумагой становится втрое заметнее, а тон остаётся из того же
   семейства (оттенок 52° против 53° у страницы). */
.cart-summary {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: var(--space-4);
    padding: var(--space-5);
    /* Панель ТЕМНЕЕ страницы — ровно та же логика, что работала на белом фоне: там панель
       была на 13 пунктов темнее белого, здесь на 12 пунктов темнее бумаги. Светлее страницы
       пробовали — разницу в полтона глаз почти не ловит, и панель приходилось держать рамкой.

       РАЗДЕЛЯЕМ ГЛУБИНОЙ, А НЕ ЦВЕТОМ. Тон этой панели перебирали пять раз, и каждый провал
       был поучительным: беж совпадал с шапкой; светлее бумаги на полтона — не читалось;
       темнее и теплее — владелица назвала розовым; холодный серо-голубой — «ужасно», и это
       справедливо: замер её же скриншота показал панель на R−B −7 посреди страницы на +12.
       Два разных света в одном кадре, ровно то, от чего дешевеет любая вёрстка.

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

       Тень тёплая (коричнево-серая, не чёрная) — чёрная на кремовом даёт грязный ореол. */
    /* ФИРМЕННАЯ МЕШЕВАЯ ЗАЛИВКА — та же, что в подвале, теми же двумя цветами марки:
       сливочный беж #E5E3C5 и голубой #A8BAC6, четырьмя мягкими пятнами по углам.

       Отличие одно — плотность. В подвале пятна стоят на 0.72–0.98, там нет мелкого текста
       и это можно. Здесь мелкого текста много (артикул, «80–86 · голубой», условие
       бесплатной доставки), а контраст меряется по САМОЙ ТЁМНОЙ точке заливки, не по
       средней. Плотность подобрана замером реальных пикселей панели: при этих значениях
       самая тёмная точка держит норму 4.5 для приглушённого текста.

       Прямого перехода «беж → голубой» тут нет, и это важно. Линейный градиент между ними
       прошёл бы через точку, где красный и синий меняются местами, — грязная нейтральная
       середина, из-за которой началась вся история с розовым. Мешевая раскладка ставит
       цвета в РАЗНЫЕ углы и гасит каждый в прозрачность, поэтому середина остаётся светлой
       основой, а не смесью двух противоположных температур. */
    background-color: var(--color-surface);
    background-image:
        radial-gradient(90% 110% at 6% 8%, rgba(229, 227, 197, 0.55), rgba(229, 227, 197, 0) 58%),
        radial-gradient(85% 110% at 94% 92%, rgba(229, 227, 197, 0.45), rgba(229, 227, 197, 0) 55%),
        radial-gradient(110% 120% at 62% 40%, rgba(168, 186, 198, 0.30), rgba(168, 186, 198, 0) 62%),
        radial-gradient(70% 90% at 88% 14%, rgba(168, 186, 198, 0.22), rgba(168, 186, 198, 0) 58%);
    border: 1px solid #EAE9DE;
    border-radius: var(--radius-md);
    box-shadow: 0 1px 2px rgba(92, 88, 62, 0.05), 0 14px 32px -18px rgba(92, 88, 62, 0.28);
    /* Поля внутри панели НИЧЕМ не отличаются от полей на остальном сайте — иначе получается
       ровно то, что владелица поймала: я топила поле промокода на полтона, чтобы оно читалось
       на светлой панели, и завела единственное серое поле на весь магазин. ФИО, телефон,
       почта светлые, а промокод серый — он и бросался в глаза, хотя задача была обратная.
       Теперь его держит рамка, как и все прочие поля. Переопределена только подсветка «−/+»:
       на панели она должна быть чуть заметнее, чем была бы на бумаге. */
    --color-bg-soft: #EDEDE2;
    /* Мелкий текст темнее обычного — это и есть плата за видимый градиент. На общем #62727F
       низ панели пришлось бы держать на #F6F6EC, где перепада не видно; на #556370 низ
       уходит до #E7E7DE и градиент читается, а контраст даже растёт: 4.96 против 4.56. */
    --color-text-muted: #556370;
}

.cart-summary__promo {
    width: 100%;
    padding-bottom: var(--space-4);
    border-bottom: 1px solid var(--color-border);
}

.cart-promo-form__label {
    display: block;
    margin-bottom: var(--space-2);
    font-size: 0.85rem;
    color: var(--color-text-muted);
}

.cart-promo-form__row {
    display: flex;
}

.cart-promo-form__input {
    flex: 1 1 auto;
    min-width: 0;
    padding: var(--space-3) var(--space-4);
    border: 1px solid var(--color-border);
    border-right: none;
    border-radius: var(--radius-sm) 0 0 var(--radius-sm);
    background: var(--color-surface);
}

.cart-promo-form__submit {
    flex: 0 0 44px;
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--color-eerie-black);
    color: var(--color-white);
    font-size: 1.3rem;
    line-height: 1;
    border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
    transition: background-color 0.15s ease;
}

.cart-promo-form__submit:hover {
    background: var(--color-accent-dark);
}

/* Applied-promo state (cart-summary__promo swaps the input form for this once a code is
   accepted) - a soft blue pill echoing the header badge accent, plus a plain-text "remove" link
   styled as a button so it never looks like a second primary action next to "Оформить заказ". */
.cart-promo-applied {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-3);
    padding: var(--space-3) var(--space-4);
    background: var(--color-bg-tint-blue);
    border-radius: var(--radius-sm);
}

.cart-promo-applied__text {
    font-size: 0.9rem;
}

.cart-promo-applied__remove {
    font-size: 0.8rem;
    text-decoration: underline;
    color: var(--color-text-muted);
    white-space: nowrap;
}

.cart-promo-applied__remove:hover {
    color: var(--color-danger);
}

.cart-summary__subtotal {
    font-family: var(--font-heading);
    font-size: 1.2rem;
    font-weight: 600;
}

.cart-summary__checkout {
    width: 100%;
    margin-top: var(--space-2);
}

.cart-summary__continue {
    align-self: center;
    font-size: 0.9rem;
    color: var(--color-text-muted);
    text-decoration: underline;
}

.cart-summary__continue:hover {
    color: var(--color-text);
}

/* --------------------------------------------------------------------------
   Checkout (Task 10): contact/address/delivery/payment form + order summary
   sidebar, and the post-order confirmation page. Reuses .cart-summary/
   .cart-flash for the sidebar/flash-message look rather than redefining them.
   -------------------------------------------------------------------------- */

.checkout-page__title {
    /* Тот же лёгкий заголовок, что на «Избранном» и в «Каталоге» (там font-weight 400 + лёгкая
       отрицательная разрядка) — жирное начертание по умолчанию выбивалось из остальных страниц. */
    font-weight: 400;
    letter-spacing: -0.01em;
    margin-top: var(--space-5); /* чуть опускаем от шапки (веб и моб), чтобы не липло к верхней границе */
    margin-bottom: var(--space-7); /* «Контактные данные» ниже — больше воздуха под заголовком */
}

.checkout-page__layout {
    display: flex;
    flex-direction: column;
    gap: var(--space-6);
    align-items: start;
}

.checkout-form {
    display: flex;
    flex-direction: column;
    gap: var(--space-6);
}

.checkout-form__section {
    border: none;
    padding: 0;
    margin: 0;
    display: flex;
    flex-direction: column;
    gap: var(--space-4);
}

.checkout-form__section legend {
    font-family: var(--font-heading);
    font-weight: 600;
    font-size: 1.05rem;
    margin-bottom: var(--space-2);
    padding: 0;
}

.checkout-form__row {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-4);
}

.checkout-form__row .field {
    flex: 1 1 140px;
}

.field {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
}

/* Подписи полей стояли на том же приглушённом тоне, что и пояснения под ними, и сливались с
   ними в одну серую массу — владелица заметила это на форме возврата, но касается всех форм
   сайта. Теперь у подписи свой вес: она темнее пояснения (контраст 6.83 против 4.65) и чуть
   плотнее начертанием. Пояснения намеренно остаются прежними — иначе исчезнет сама разница
   между «что заполнить» и «как заполнить». */
.field__label {
    font-size: 0.9rem;
    font-weight: 500;
    color: #4A5866;
}

.field__input {
    width: 100%;
    padding: var(--space-3);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm);
    background: var(--color-surface);
    font-family: var(--font-body);
}

.field__input:focus {
    outline: 2px solid var(--color-accent-dark);
    outline-offset: 1px;
}

/* ОШИБКА ФОРМЫ — ТЕРРАКОТОВАЯ, А НЕ БОРДОВАЯ. --color-danger это #442D2A, тёмно-коричневый: на
   кремовом фоне он читается как обычный чёрный текст, и сообщение об ошибке ничем не отличалось
   от подписи к полю. Владелица заметила это на заявке о возврате: «не красного цвета».
   #B24A2E уже есть в палитре (сноска про Meta) — он заметен, но не кричит, как чистый красный. */
.field__error {
    color: var(--color-footnote);
    font-size: 0.85rem;
}

/* City prefix autocomplete (custom, replaces the native datalist which matched substrings). */
.city-autocomplete {
    position: relative;
}

.city-autocomplete__list {
    position: absolute;
    top: calc(100% + 2px);
    left: 0;
    right: 0;
    z-index: 25;
    margin: 0;
    padding: var(--space-1);
    list-style: none;
    max-height: 260px;
    overflow-y: auto;
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm);
    box-shadow: 0 14px 34px rgba(30, 30, 30, 0.14);
}

.city-autocomplete__list[hidden] {
    display: none;
}

.city-autocomplete__item {
    display: block;
    width: 100%;
    padding: var(--space-2) var(--space-3);
    text-align: left;
    background: transparent;
    border-radius: var(--radius-sm);
    font-size: 0.95rem;
    color: var(--color-text);
    cursor: pointer;
}

.city-autocomplete__item:hover {
    background: var(--color-bg-soft);
}

/* Client-side "required field is empty" highlight on submit (checkout.js) - a clear red. */
.field__input--invalid {
    border-color: #E0393B;
    box-shadow: 0 0 0 1px #E0393B;
    background: #FDF3F3;
}

.field__input--invalid:focus {
    outline-color: #E0393B;
}

.checkout-radio-group.is-invalid,
.checkout-checkbox.is-invalid,
.checkout-pickup.is-invalid {
    outline: 2px solid #E0393B;
    outline-offset: 4px;
    border-radius: var(--radius-sm);
}

.checkout-radio-group {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
}

/* Способ доставки как кликабельная карточка-кнопка (вместо радио-кружка): вся плашка выделяется. */
.checkout-radio {
    display: block;
    position: relative;
    padding: var(--space-3) var(--space-4);
    border: 1.5px solid var(--color-border);
    border-radius: var(--radius-md, 12px);
    background: var(--color-surface);
    cursor: pointer;
    font-size: 0.95rem;
    transition: border-color 0.15s ease, background-color 0.15s ease, box-shadow 0.15s ease;
}

/* Спрятать нативный кружок — выбор показываем самой карточкой. */
.checkout-radio input[type="radio"] {
    position: absolute;
    opacity: 0;
    width: 0;
    height: 0;
    margin: 0;
}

.checkout-radio:hover {
    border-color: var(--color-accent-dark, #b06a4f);
}

/* Выбранная карточка. :has() поддерживается всеми актуальными браузерами; если нет —
   карточка всё равно кликается (label), просто без подсветки. */
.checkout-radio:has(input[type="radio"]:checked) {
    border-color: var(--color-accent-dark, #b06a4f);
    background: var(--color-bg-muted, #f7f4f0);
    box-shadow: inset 0 0 0 1px var(--color-accent-dark, #b06a4f);
}

/* Галочка в углу выбранной карточки. */
.checkout-radio:has(input[type="radio"]:checked)::after {
    content: "✓";
    position: absolute;
    top: var(--space-3);
    right: var(--space-4);
    color: var(--color-accent-dark, #b06a4f);
    font-weight: 700;
}

.checkout-radio:has(input[type="radio"]:focus-visible) {
    outline: 2px solid var(--color-accent-dark, #b06a4f);
    outline-offset: 2px;
}

.checkout-radio__demo-note {
    display: block;
    font-style: normal;
    color: var(--color-text-muted);
    font-size: 0.8rem;
}

/* Small grey caption under the "Яндекс Доставка" option (what its ПВЗ network covers). */
.checkout-radio__hint {
    display: block;
    color: var(--color-text-muted);
    font-size: 0.8rem;
    margin-top: 2px;
}

/* Courier address block (shown only for "СДЭК — курьер до двери"). */
.checkout-courier {
    display: flex;
    flex-direction: column;
    gap: var(--space-4);
}

/* A class-selector `display:flex` outranks the bare `[hidden]` UA rule, so without these the
   pickup/courier blocks would stay visible even when checkout.js sets their `hidden` attribute
   (they're mutually exclusive per the chosen delivery option). Attribute+class wins it back. */
.checkout-pickup[hidden],
.checkout-courier[hidden] {
    display: none;
}

.checkout-courier__row {
    display: grid;
    grid-template-columns: 2fr 1fr 1.4fr;
    gap: var(--space-3);
}

/* Адрес курьера в две строки:
   1) улица (шире) + дом + корпус/строение + подъезд;
   2) кв./офис + домофон + этаж + индекс (одинаковые). */
.checkout-courier__line {
    display: grid;
    gap: var(--space-3);
}

.checkout-courier__line--1 {
    grid-template-columns: 2fr 1fr 1.3fr 1fr;
}

.checkout-courier__line--2 {
    grid-template-columns: 1fr 1fr 1fr 1fr;
}

@media (max-width: 767px) {
    .checkout-courier__row {
        grid-template-columns: 1fr;
    }
    /* На телефоне — по 2 ячейки в ряд, чтобы всё поместилось. */
    .checkout-courier__line--1,
    .checkout-courier__line--2 {
        grid-template-columns: 1fr 1fr;
    }
}

/* Wider modal + sized container for the СДЭК pickup-point map widget (its docs require an explicit
   height, else the map collapses to 0). */
.modal__panel--wide {
    max-width: 760px;
    width: min(92vw, 760px);
}

.checkout-pickup-map {
    position: relative;
    width: 100%;
    height: 48vh;
    min-height: 320px;
    margin-top: var(--space-3);
    margin-bottom: var(--space-3);
    border-radius: var(--radius-md, 12px);
    overflow: hidden;
    background: var(--color-bg-muted, #f2efe9);
}

/* Balloon «Выбрать этот пункт» button (ymaps renders balloon HTML inside the map container). */
.checkout-pickup-map__pick {
    /* Во всю ширину карточки: это главное действие в балуне, и на телефоне узкая кнопка рядом с
       текстом читалась как ссылка, а не как «нажми сюда». */
    display: block;
    width: 100%;
    margin: 8px 0 4px;
    padding: 9px 12px;
    border: none;
    border-radius: var(--radius-sm, 8px);
    background: var(--color-accent-dark, #b06a4f);
    color: #fff;
    font-family: var(--font-body);
    font-size: 0.9rem;
    font-weight: 600;
    cursor: pointer;
}

/* Loading overlay with a delivery truck driving across while points load. */
.checkout-pickup-map__loader {
    position: absolute;
    inset: 0;
    z-index: 2;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: var(--space-4);
    background: var(--color-bg-muted, #f2efe9);
}

.checkout-pickup-map__loader[hidden] {
    display: none;
}

.checkout-pickup-map__loader-text {
    font-size: 0.9rem;
    color: var(--color-text-muted);
}

/* The truck: a full-colour SVG box van (injected by checkout.js, liveried per carrier) that
   drives left→right across the overlay on a loop. */
.delivery-truck {
    width: 130px;
    height: 78px;
    filter: drop-shadow(0 3px 4px rgba(0, 0, 0, 0.12));
    animation: truck-drive 1.7s ease-in-out infinite;
}

.delivery-truck svg { display: block; width: 100%; height: 100%; }

@keyframes truck-drive {
    0%   { transform: translateX(-46px); }
    50%  { transform: translateX(46px); }
    100% { transform: translateX(-46px); }
}

@media (prefers-reduced-motion: reduce) {
    .delivery-truck { animation: none; }
}

/* Pickup-point balloon (ymaps renders this HTML inside the map container). */
.pickup-balloon__addr {
    font-weight: 600;
    margin-bottom: 6px;
}

.pickup-balloon__row {
    font-size: 0.85rem;
    margin-top: 3px;
}

.pickup-balloon__row--ok { color: #1a7f37; }
.pickup-balloon__row--muted { color: var(--color-text-muted); }

/* Комментарий пункта («как пройти») бывает на целый абзац — обрезаем до трёх строк, иначе карточка
   перестаёт помещаться на экран телефона и выглядит сломанной. */
.pickup-balloon__row--clamp {
    display: -webkit-box;
    -webkit-line-clamp: 3;
    -webkit-box-orient: vertical;
    overflow: hidden;
}
.pickup-balloon__op { font-weight: 600; }
.pickup-balloon__tel {
    color: var(--color-accent-dark, #b06a4f);
    text-decoration: underline;
    white-space: nowrap;
}

/* "Сначала укажите город" hint + the delivery radios locked until a city is entered. */
.checkout-city-required {
    font-size: 0.9rem;
    color: #B24A2E;
    margin-bottom: var(--space-2);
}

.checkout-radio-group--locked {
    opacity: 0.5;
    pointer-events: none;
}

/* Live delivery cost + total in the checkout order summary. */
.checkout-summary__total-row span {
    white-space: nowrap;
}

/* Значение доставки может быть длинным сообщением («Не удалось рассчитать стоимость…») —
   разрешаем перенос и прижимаем вправо, чтобы текст не вылезал за край сводки. Короткая цена
   («238 ₽») всё равно останется одной строкой. */
#summary-delivery {
    text-align: right;
    min-width: 0;
    white-space: normal;
    overflow-wrap: anywhere;
}

.checkout-summary__total-row {
    display: flex;
    justify-content: space-between;
    gap: var(--space-4);
    margin-top: var(--space-2);
    padding-top: var(--space-2);
    border-top: 1px solid var(--color-border);
    font-family: var(--font-heading);
    font-size: 1.1rem;
    font-weight: 600;
}

.checkout-summary__delivery {
    display: flex;
    justify-content: space-between;
    gap: var(--space-4);
}

/* Small helper text under an admin field (e.g. weight hint). */
/* --------------------------------------------------------------------------
   Приветственное окно: форма подписки за промокод
   -------------------------------------------------------------------------- */

/* Выключка по формату в окне: колонка узкая, и рваный правый край рядом с ровными полем ввода
   и кнопкой выглядел неопрятно. Переносы намеренно выключены — слова рвутся целиком; расплата
   за это чуть более широкие межсловные пробелы в отдельных строках. Ровно то же решение, что
   у вступления в колонке «Контакты» подвала. */
.welcome-modal__text {
    text-align: justify;
}

.welcome-modal__form {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
    margin-top: var(--space-4);
}

/* display: flex перебивает атрибут hidden — без этого правила форма оставалась на экране рядом
   с сообщением «спасибо за подписку», и человек не понимал, отправилось у него или нет. */
.welcome-modal__form[hidden] {
    display: none;
}

.welcome-modal__label {
    display: block;
}

.welcome-modal__input {
    width: 100%;
}

/* Галочки мельче основного текста: это условие, а не содержание предложения. Разводим их
   между собой, чтобы на телефоне не слипались в одну строку под палец. */
.welcome-modal__consents {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
}

.welcome-modal__consent {
    font-size: 0.78rem;
    line-height: 1.45;
}

.welcome-modal__submit {
    width: 100%;
}

.welcome-modal__error {
    margin: 0;
    font-size: 0.82rem;
    line-height: 1.45;
    color: var(--color-footnote);
}

/* Сообщение об успехе занимает место формы — размер тот же, чтобы окно не прыгало. */
.welcome-modal__done {
    margin: var(--space-4) 0 0;
    font-size: 0.95rem;
    line-height: 1.6;
    color: var(--color-text);
    white-space: pre-line;
}

/* Промокод на странице подтверждения подписки: его копируют глазами и руками, поэтому крупно,
   вразрядку и в рамке-купоне — как в письме. */
/* Скретч-карта с промокодом. Код лежит текстом под холстом: холст только закрашивает его сверху,
   поэтому читалки экрана и поиск по странице видят код всегда. */
.scratch {
    position: relative;
    display: inline-block;
    margin: var(--space-4) 0 var(--space-3);
    border-radius: var(--radius-md);
    /* Скругление обрезает и холст: без этого покрытие торчало бы прямоугольником из-под рамки. */
    overflow: hidden;
}

.scratch .newsletter-confirmed__code {
    margin: 0;
    /* Место справа под значок копирования — иначе он лёг бы на последние буквы кода. */
    padding-right: var(--space-8);
}

/* Значок копирования в правом нижнем углу рамки с кодом. Прозрачный, без собственной рамки:
   он подсказка, а не вторая кнопка рядом с «Смотреть каталог». */
.scratch__copy {
    position: absolute;
    right: 8px;
    bottom: 6px;
    z-index: 1;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 6px;
    border: 0;
    border-radius: var(--radius-sm);
    background: none;
    color: var(--color-text-muted);
    cursor: pointer;
    line-height: 0;
}
.scratch__copy:hover { color: var(--color-payne-gray); background: var(--color-bg-soft); }
.scratch__copy:focus-visible { outline: 2px solid var(--color-payne-gray); outline-offset: 2px; }

.scratch__cover {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    /* grab/grabbing: курсор объясняет, что покрытие можно тереть, без единого слова подписи. */
    cursor: grab;
    /* Без этого страница на телефоне прокручивается вместо стирания. */
    touch-action: none;
    transition: opacity 0.35s ease;
}

.scratch__cover:active {
    cursor: grabbing;
}

/* Стёрли достаточно — остаток снимаем плавно, чтобы не оставлять лохмотья. */
.scratch--revealed .scratch__cover {
    opacity: 0;
    pointer-events: none;
}

.newsletter-confirmed__hint {
    font-size: 0.88rem;
    color: var(--color-text-muted);
}

@media (prefers-reduced-motion: reduce) {
    .scratch__cover {
        transition: none;
    }
}

.newsletter-confirmed__copied {
    margin: var(--space-2) 0 0;
    font-size: 0.85rem;
    color: var(--color-text-muted);
}

.newsletter-confirmed__code {
    display: inline-block;
    margin: var(--space-4) 0 var(--space-3);
    padding: var(--space-3) var(--space-5);
    border: 1px dashed var(--color-border);
    border-radius: var(--radius-md);
    font-size: 1.35rem;
    font-weight: 700;
    letter-spacing: 0.08em;
    color: var(--color-text);
}

/* Пояснение под вещью, которую можно вернуть, но не обменять/* Пояснение под вещью, которую можно вернуть, но не обменять (бельё, чулочно-носочные). */
/*
  Красная часть строки «заказ найден»: сообщение о том, что срок прошёл. Цвет не общий
  --color-danger: он у нас глубокий бордовый, на светлом фоне читается как обычный тёмный текст
  и не тревожит. Здесь нужен именно сигнал, поэтому взят кирпичный #B24A2E — тот же, которым
  светится «Промокод не найден» и метка брака в админке.
*/
.return-status__alert {
    color: #B24A2E;
    font-weight: 500;
}

/* Выбор из нескольких заказов на одни и те же контакты. */
.return-choices { display: flex; flex-wrap: wrap; gap: var(--space-2); margin-bottom: var(--space-3); }
.return-choices[hidden] { display: none; }
.return-choice {
    flex: 1 1 14rem;
    /* Номер и дата по центру карточки: в ней всего две короткие строки, и прижатые к левому краю
       они выглядели остатком текста, а не кнопкой выбора. */
    text-align: center;
    padding: var(--space-3);
    font: inherit;
    font-size: 0.9rem;
    line-height: 1.45;
    color: var(--color-text);
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    cursor: pointer;
}
.return-choice:hover { border-color: var(--color-accent-dark); background: var(--color-bg-soft); }
.return-choice strong { font-weight: 600; }

.return-item__note {
    margin: var(--space-1) 0 0;
    font-size: 0.82rem;
    line-height: 1.5;
    color: var(--color-text-muted);
}

/* Режим брака на заявке: выбор «вернуть деньги / обменять» скрыт — решение принимается после
   осмотра, и спрашивать о нём заранее значит обещать, что выбор учтён. Скрываем целиком, а не
   гасим: погашенный, но видимый выбор человек всё равно пытается нажать. */
.return-items--defect .return-item__choice {
    display: none;
}


/* Подсказка под полем промокода в корзине: объясняет, почему «первый заказ» проверяется позже. */
.cart-promo-form__hint {
    margin: var(--space-2) 0 0;
    font-size: 0.78rem;
    line-height: 1.5;
    color: var(--color-text-muted);
}

.field__hint {
    display: block;
    margin-top: 4px;
    font-size: 0.8rem;
    color: var(--color-text-muted);
}
/* Собственный `display` перебивает браузерное правило для атрибута hidden, и подсказка,
   спрятанная скриптом, оставалась на экране. Так на заявке возврата абзац про брак («пришлите
   фотографии в ответ на письмо») висел под любой причиной — даже под «Не подошёл размер».
   Правило нужно каждому классу, который задаёт свой display и при этом прячется атрибутом. */
.field__hint[hidden] { display: none; }

/* Срок доставки — под выбором пункта/способа. */
.checkout-delivery-term {
    margin-top: var(--space-2);
    font-size: 0.9rem;
    font-weight: 500;
}

.checkout-delivery-term[hidden] { display: none; }

/* Ввод промокода в блоке заказа на странице оформления. */
.checkout-promo {
    margin-top: var(--space-3);
    padding-top: var(--space-3);
    border-top: 1px solid var(--color-border);
}

.checkout-promo__entry {
    display: flex;
    gap: var(--space-2);
}

.checkout-promo__entry .field__input { flex: 1 1 auto; min-width: 0; }

/* Подпись над полем купона (как у Lumnits). */
.checkout-promo__label {
    display: block;
    font-size: 0.85rem;
    color: var(--color-text-muted);
    margin-bottom: var(--space-2);
}

/* «Осталось добрать до бесплатной доставки» под итогами. */
.checkout-summary__freeship {
    margin-top: var(--space-1);
    font-size: 0.85rem;
    line-height: 1.45;
    color: var(--color-text-muted);
}

.checkout-summary__freeship strong {
    color: var(--color-accent-dark, #b06a4f);
    white-space: nowrap;
}

/* Срок доставки в сводке «Ваш заказ» — мелкой строкой справа под ценой доставки. */
.checkout-summary__term {
    margin-top: -4px;
    text-align: right;
    font-size: 0.8rem;
    color: var(--color-text-muted);
}

.checkout-summary__term[hidden] { display: none; }

/* Кирпичная подсказка «заполните поле» под городом (клиентская валидация). */
.checkout-city-error {
    color: #B24A2E;
    font-size: 0.85rem;
    margin-top: 2px;
}

.checkout-city-error[hidden] { display: none; }

/* Телефон с выбором кода страны (флаг + код слева, номер справа). */
.phone-input {
    position: relative;
    display: flex;
    align-items: stretch;
}
.phone-input__country {
    display: flex;
    align-items: center;
    gap: 5px;
    padding: 0 10px;
    border: 1px solid var(--color-border);
    border-right: none;
    background: var(--color-bg-soft);
    cursor: pointer;
    white-space: nowrap;
}
.phone-input__number {
    flex: 1;
    min-width: 0;
}
.phone-input__flag { font-size: 1.15rem; line-height: 1; }
.phone-input__code { font-size: 0.9rem; }
.phone-input__caret { font-size: 0.65rem; color: var(--color-text-muted); }
.phone-input__list {
    position: absolute;
    top: 100%;
    left: 0;
    z-index: 40;
    margin: 4px 0 0;
    padding: 4px;
    list-style: none;
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm);
    box-shadow: 0 8px 24px rgba(30, 30, 30, 0.14);
    max-height: 264px;
    overflow-y: auto;
    min-width: 240px;
    max-width: 88vw;
}
.phone-input__list[hidden] { display: none; }
/* Список стран живёт и на «Покупателям» (заявка на возврат), где у .legal-page__section ul стоит
   `list-style: disc` — а это селектор класса С ТЕГОМ, он весомее одного класса, и у каждой страны
   появлялась точка-маркер. Два класса перебивают его обратно. */
.phone-input .phone-input__list { list-style: none; padding-left: 4px; }
.phone-input__item {
    display: flex;
    align-items: center;
    gap: 8px;
    width: 100%;
    padding: 8px 10px;
    text-align: left;
    cursor: pointer;
    font-size: 0.9rem;
    background: none;
    border: none;
}
.phone-input__item:hover { background: var(--color-bg-soft); }
.phone-input__cname { flex: 1; }
.phone-input__ccode { color: var(--color-text-muted); }

/* Раскупленные позиции: подсветка строки корзины/сводки, кирпичный бейдж «Нет в наличии» и
   ссылка «выбрать другой размер». Товар мог раскупиться, пока лежал в корзине — наличие берётся
   свежим при каждой загрузке, тут мы его показываем. */
.cart-line--unavailable,
.checkout-summary__line--unavailable {
    opacity: 0.85;
}
.cart-line__stock-badge,
.checkout-summary__stock-badge {
    display: inline-block;
    color: #B24A2E;
    font-weight: 600;
    font-size: 0.85rem;
    margin: 4px 0 2px;
}
.cart-line__pick-size,
.checkout-summary__pick-size {
    display: inline-block;
    color: #B24A2E;
    font-size: 0.85rem;
    text-decoration: underline;
    text-underline-offset: 2px;
}
.cart-line__pick-size:hover,
.checkout-summary__pick-size:hover { text-decoration: none; }
.cart-summary__stock-note {
    color: #B24A2E;
    font-size: 0.85rem;
    margin-top: 8px;
    line-height: 1.4;
}
.cart-summary__stock-note a { color: #B24A2E; text-decoration: underline; }
.cart-summary__checkout[disabled],
.checkout-summary__submit[disabled] {
    opacity: 0.5;
    cursor: not-allowed;
}

/* «Промокод применён» — аккуратная зелёная плашка с галочкой, а не голый текст со ссылкой.
   Переносится и сжимается, чтобы длинный код не вылезал за рамку узкой сводки. */
.checkout-promo__applied {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: var(--space-2) var(--space-3);
    font-size: 0.85rem;
    padding: var(--space-3);
    background: #F1F7F1;
    border: 1px solid #CFE6CF;
    border-radius: var(--radius-md, 12px);
    color: #1a7f37;
}

.checkout-promo__applied strong { color: inherit; word-break: break-word; }

.checkout-promo__applied-text {
    display: inline-flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 6px 8px;
    flex: 1 1 auto;
    min-width: 0;
}

.checkout-promo__check {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 18px;
    height: 18px;
    flex: 0 0 auto;
    border-radius: 50%;
    background: #1a7f37;
    color: #fff;
    font-size: 0.7rem;
    line-height: 1;
}

.checkout-promo__remove {
    background: none;
    border: none;
    color: var(--color-text-muted);
    text-decoration: underline;
    cursor: pointer;
    font-size: 0.8rem;
    padding: 0;
    margin-left: auto;
    white-space: nowrap;
}

.checkout-promo__remove:hover { color: var(--color-text); }

.checkout-promo__msg {
    font-size: 0.85rem;
    margin-bottom: var(--space-2);
}

.checkout-promo__msg--ok { color: #1a7f37; }
.checkout-promo__msg--err { color: #b42318; }

.checkout-pickup-modal__search {
    margin-bottom: var(--space-3);
}

.checkout-confirmation__payment {
    margin-top: var(--space-2);
    font-weight: 600;
}

.checkout-confirmation__payment--paid {
    color: #1a7f37;
}

.checkout-confirmation__payment--failed {
    color: #b42318;
    font-weight: 500;
}

.checkout-confirmation__payment--pending {
    color: #8a6d00;
    font-weight: 500;
}

.checkout-confirmation__pay-again {
    display: inline-block;
    margin-top: var(--space-3);
}

/* СБП payment page (checkout/sbp.html): a searchable bank list (tap → bank app) + a QR for desktop. */
.checkout-sbp {
    padding-block: var(--space-7);
    max-width: 620px;
}

.checkout-sbp__lead {
    margin-bottom: var(--space-2);
}

.checkout-sbp__status {
    margin-bottom: var(--space-5);
    font-weight: 600;
    color: var(--color-accent-dark);
}

.checkout-sbp__banks-label {
    margin-bottom: var(--space-3);
}

.checkout-sbp__search {
    margin-bottom: var(--space-3);
}

.checkout-sbp__banks {
    list-style: none;
    margin: 0;
    padding: 0;
    max-height: 58vh;
    overflow-y: auto;
    border: 1px solid var(--color-border);
    border-radius: 12px;
}

.checkout-sbp__bank + .checkout-sbp__bank {
    border-top: 1px solid var(--color-border);
}

.checkout-sbp__bank-link {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    padding: var(--space-3) var(--space-4);
    color: var(--color-text);
    text-decoration: none;
}

.checkout-sbp__bank-link:hover {
    background: var(--color-bg-soft);
}

.checkout-sbp__bank-logo {
    width: 32px;
    height: 32px;
    flex: 0 0 auto;
    object-fit: contain;
    border-radius: 6px;
}

.checkout-sbp__qr-block {
    margin-top: var(--space-5);
}

.checkout-sbp__qr-block summary {
    cursor: pointer;
    font-weight: 600;
    color: var(--color-accent-dark);
}

.checkout-sbp__qr {
    width: 240px;
    margin-top: var(--space-3);
}

.checkout-sbp__qr svg {
    display: block;
    width: 100%;
    height: auto;
}

/* Payment-method page (checkout/pay-methods.html): СБП big at top, card small at the bottom. */
.checkout-pay {
    padding-block: var(--space-7);
    max-width: 560px;
}

.checkout-pay__lead {
    margin-bottom: var(--space-5);
}

.checkout-pay__methods {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
}

.checkout-pay__method {
    display: flex;
    flex-direction: column;
    gap: 3px;
    padding: var(--space-4) var(--space-5);
    border: 1px solid var(--color-border);
    border-radius: 14px;
    text-decoration: none;
    color: var(--color-text);
    transition: border-color 0.15s ease, background 0.15s ease;
}

.checkout-pay__method:hover {
    border-color: var(--color-accent-dark);
}

.checkout-pay__method-title {
    font-weight: 600;
    font-size: 1.1rem;
}

.checkout-pay__method-note {
    color: var(--color-text-muted);
    font-size: 0.9rem;
}

.checkout-pay__method--sbp {
    padding: var(--space-5);
    background: var(--color-accent-dark);
    border-color: var(--color-accent-dark);
    color: var(--color-white);
}

.checkout-pay__method--sbp .checkout-pay__method-title {
    font-size: 1.45rem;
}

.checkout-pay__method--sbp .checkout-pay__method-note {
    color: rgba(255, 255, 255, 0.85);
}

.checkout-pay__method--card {
    opacity: 0.85;
}

.checkout-pickup-map[hidden] {
    display: none;
}

/* --------------------------------------------------------------------------
   "Адрес и способ доставки" pickup/courier sub-sections + the mock
   pickup-point picker (checkout/checkout.html, static/js/checkout.js).
   -------------------------------------------------------------------------- */

.checkout-pickup {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: var(--space-2);
}

/* Выбранный пункт в рамке-чипе с крестиком «сбросить» справа. */
.checkout-pickup__chosen-row {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    align-self: stretch;
    padding: var(--space-2) var(--space-3);
    background: var(--color-bg-muted, #f7f4f0);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md, 12px);
}

.checkout-pickup__chosen-row[hidden] { display: none; }

.checkout-pickup__chosen {
    flex: 1 1 auto;
    min-width: 0;
    margin: 0;
    font-size: 0.9rem;
    color: var(--color-text);
}

.checkout-pickup__clear {
    flex: 0 0 auto;
    width: 26px;
    height: 26px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border: none;
    border-radius: 50%;
    background: transparent;
    color: var(--color-text-muted);
    font-size: 1.35rem;
    line-height: 1;
    cursor: pointer;
    transition: background-color 0.15s ease, color 0.15s ease;
}

.checkout-pickup__clear:hover {
    background: rgba(0, 0, 0, 0.07);
    color: var(--color-text);
}

/* Ссылка «Выбрать/Изменить пункт выдачи» — не большая кнопка, а скромная ссылка: карта
   и так открывается сразу при выборе ПВЗ, эта ссылка нужна лишь чтобы переоткрыть её. */
.checkout-pickup__reopen {
    align-self: flex-start;
    background: none;
    border: none;
    padding: 0;
    color: var(--color-accent-dark, #b06a4f);
    font-family: var(--font-body);
    font-size: 0.9rem;
    text-decoration: underline;
    text-underline-offset: 3px;
    cursor: pointer;
}

.checkout-pickup__reopen:hover {
    opacity: 0.8;
}

.checkout-pickup__reopen:disabled {
    color: var(--color-text-muted);
    text-decoration: none;
    cursor: default;
}

.checkout-courier-fields {
    display: flex;
    flex-direction: column;
    gap: var(--space-4);
}

.checkout-pickup-modal__note {
    font-size: 0.85rem;
    color: var(--color-text-muted);
    margin-bottom: var(--space-4);
}

.checkout-pickup-modal__list {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
}

.checkout-pickup-modal__point {
    display: flex;
    flex-direction: column;
    gap: 3px;
    width: 100%;
    text-align: left;
    padding: var(--space-3);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm);
    background: var(--color-surface);
    font-family: var(--font-body);
    font-size: 0.9rem;
    cursor: pointer;
}

.checkout-pickup-modal__point:hover,
.checkout-pickup-modal__point:focus-visible {
    border-color: var(--color-accent-dark);
    background: var(--color-bg-muted, #f7f4f0);
}

.checkout-pickup-modal__point-addr {
    font-weight: 600;
    color: var(--color-text);
}

.checkout-pickup-modal__point-op {
    font-size: 0.8rem;
    color: var(--color-accent-dark, #b06a4f);
}

.checkout-pickup-modal__point-row {
    font-size: 0.82rem;
    color: var(--color-text-muted);
}

.checkout-pickup-modal__point-row--ok {
    color: #1a7f37;
}

/* --- Мобильная версия выбора ПВЗ ------------------------------------------------------------
   Раньше на телефоне карта занимала пол-экрана, а карточки пунктов уходили далеко вниз, и всю
   модалку приходилось листать. Теперь модалка — почти на весь экран: сверху компактная карта и
   поиск, а СПИСОК пунктов прокручивается сам по себе (как у loomknits), с полной инфой в каждой
   карточке (адрес, часы, примерочная, срок) — не нужно ловить балун на карте. */
@media (max-width: 600px) {
    /* ТОЛЬКО «шторка» выбора ПВЗ (.modal--sheet на #pickup-point-modal). Раньше это правило
       висело на всех .modal сразу, и на телефоне ко дну прижимались ВСЕ окна — в том числе
       приветственное с промокодом, которое должно стоять по центру. */
    .modal--sheet {
        padding: var(--space-2);
        align-items: flex-end; /* модалка-«шторка» снизу — удобнее большим пальцем */
    }

    .modal__panel--wide {
        width: 100%;
        max-width: 100%;
        max-height: 94vh;
        max-height: 94dvh;
        padding: var(--space-4);
        display: flex;
        flex-direction: column;
        overflow: hidden;
    }

    .modal__panel--wide h2 {
        margin-bottom: var(--space-2);
        font-size: 1.1rem;
    }

    .checkout-pickup-map {
        flex: 0 0 auto;
        height: 30vh;
        min-height: 160px;
        margin-top: var(--space-2);
        margin-bottom: var(--space-2);
    }

    .checkout-pickup-modal__search {
        flex: 0 0 auto;
    }

    /* Единственная прокручиваемая часть — список пунктов. */
    .checkout-pickup-modal__list {
        flex: 1 1 auto;
        min-height: 0;
        overflow-y: auto;
        -webkit-overflow-scrolling: touch;
    }

    /* На телефоне «Контактные данные» тоже с воздухом под заголовком «Оформление заказа». */
    .checkout-page__title {
        margin-bottom: var(--space-6);
    }
}

/* Согласия checkboxes (acceptedTerms/marketingConsent) below the order comment. */
.checkout-checkbox {
    display: flex;
    align-items: flex-start;
    gap: var(--space-3);
    font-size: 0.9rem;
}

.checkout-checkbox input[type="checkbox"] {
    margin-top: 0.2em;
    flex: none;
}

.checkout-checkbox a {
    text-decoration: underline;
}

.checkout-summary__title {
    font-family: var(--font-heading);
    font-size: 1.1rem;
    font-weight: 600;
}

.checkout-summary__lines {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    width: 100%;
}

/* Строка товара: иконка слева, текст начинается с ВЕРХА иконки (как в корзине loomknits),
   ниже — степпер количества и цена; крестик «убрать» справа сверху. */
.checkout-summary__line {
    display: flex;
    align-items: flex-start;
    gap: var(--space-3);
    font-size: 0.9rem;
}

.checkout-summary__thumb {
    width: 84px;
    height: 108px;
    flex: 0 0 auto;
    object-fit: cover;
    border-radius: 8px;
}

.checkout-summary__line-sku {
    font-size: 0.72rem;
    color: var(--color-text-muted);
}

.checkout-summary__line-main {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    flex: 1 1 auto;
    min-width: 0;
}

.checkout-summary__line-info {
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-width: 0;
}

.checkout-summary__line-name {
    min-width: 0;
    line-height: 1.25;
}

.checkout-summary__line-variant {
    font-size: 0.8rem;
    color: var(--color-text-muted);
}

.checkout-summary__line-controls {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-2);
}

.checkout-summary__qty {
    display: inline-flex;
    align-items: center;
    border: 1px solid var(--color-border);
    border-radius: 999px;
    overflow: hidden;
}

.checkout-summary__qty-btn {
    width: 26px;
    height: 26px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border: none;
    background: transparent;
    color: var(--color-text);
    font-size: 0.95rem;
    line-height: 1;
    cursor: pointer;
    transition: background-color 0.15s ease;
}

/*
  ЗДЕСЬ СЧЁТЧИК ОВАЛЬНЫЙ, И РАМКА ЕМУ НЕ ГОДИТСЯ. Я перенёс сюда подсветку рамкой со страницы
  товара, где счётчик прямоугольный, — и внутри овала она нарисовалась прямым углом, торчащим
  из скруглённого края. Выглядело как поломка, и владелица справедливо так это и прочла.

  Для овала работает заливка: контейнер обрезает её по своей форме, и ячейка под курсором
  становится аккуратной скруглённой половинкой. Заливка тёмная, не бледная: прежний #f7f4f0
  отличался от фона на несколько единиц и читался пятном, а не откликом.
*/
.checkout-summary__qty-btn:not(:disabled):hover {
    background: var(--color-text);
    color: var(--color-white);
}
.checkout-summary__qty-btn:disabled { color: var(--color-text-muted); cursor: default; }

.checkout-summary__qty-val {
    min-width: 22px;
    text-align: center;
    font-variant-numeric: tabular-nums;
}

.checkout-summary__line-price {
    flex: 0 0 auto;
    white-space: nowrap;
    font-weight: 600;
}

.checkout-summary__line-remove {
    flex: 0 0 auto;
    width: 24px;
    height: 24px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border: none;
    background: transparent;
    color: var(--color-text-muted);
    font-size: 0.9rem;
    line-height: 1;
    border-radius: 50%;
    cursor: pointer;
    transition: background-color 0.15s ease, color 0.15s ease;
}

.checkout-summary__line-remove:hover { background: rgba(0, 0, 0, 0.06); color: var(--color-text); }

/* Сводка заказа на телефоне: иконка чуть меньше, элементы управления не вылезают за край. */
@media (max-width: 600px) {
    .checkout-summary__thumb {
        width: 56px;
        height: 70px;
    }
    .checkout-summary__line-controls {
        flex-wrap: wrap;
        row-gap: var(--space-1);
    }
    .checkout-summary__qty-btn {
        width: 32px;
        height: 32px;
    }
}

.checkout-summary__note {
    font-size: 0.8rem;
    color: var(--color-text-muted);
}

.checkout-summary__submit,
.checkout-summary__total {
    width: 100%;
}

.checkout-summary__total {
    font-size: 1.3rem;
}

/* Order-summary rows on the confirmation page: label left, amount right, amount never wraps so a
   large total always fits the box. */
.checkout-summary__totals {
    width: 100%;
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
}

.checkout-summary__row {
    display: flex;
    justify-content: space-between;
    gap: var(--space-4);
    width: 100%;
    margin: 0;
    font-family: var(--font-heading);
    font-size: 1.02rem;
    font-weight: 500;
}

.checkout-summary__row > span:last-child {
    white-space: nowrap;
}

.checkout-summary__row.checkout-summary__total {
    font-size: 1.3rem;
    font-weight: 700;
    border-top: 1px solid var(--color-border);
    padding-top: var(--space-3);
    margin-top: var(--space-1);
}

.checkout-summary__line > span:first-child {
    min-width: 0;
}

.checkout-summary__line > span:last-child {
    white-space: nowrap;
}

.checkout-confirmation__label {
    font-weight: 600;
    margin-top: var(--space-3);
}

.checkout-confirmation {
    position: relative;
    isolation: isolate;
    padding-block: var(--space-6);
}

.checkout-confirmation__title {
    margin-bottom: var(--space-3);
}

.checkout-confirmation__lede {
    font-size: 1.15rem;
    color: var(--color-text-muted);
    margin-bottom: var(--space-5);
    max-width: 35rem;
}

.checkout-confirmation__number,
.checkout-confirmation__next {
    margin-bottom: var(--space-4);
    max-width: 40rem;
}

/* Короткая страница «спасибо»: без блоков с деталями она получается совсем невысокой, поэтому
   даём ей воздух сверху/снизу, чтобы заголовок не липнул к шапке и к подвалу.

   ВЫКЛЮЧКА ПО ЦЕНТРУ — решение владелицы. Слева страница выглядела обрывком большой вёрстки:
   три коротких строки и две кнопки жались к левому краю, а справа зияла пустота во весь экран.
   Центр собирает их в одно сообщение — это единственное, ради чего страница существует. */
.checkout-confirmation--short {
    /* Только существующие ступени шкалы: --space-9 в ней нет, и одна такая переменная делает
       ВСЁ объявление невалидным — padding-block молча схлопывался в ноль, заголовок прижимался
       к шапке. */
    padding-block: var(--space-8);
    text-align: center;
}

/* max-width у строк остаётся (длинная строка нечитаема), но при выключке по центру его надо
   центрировать самим — иначе блок текста прижмётся влево внутри центрированной секции. */
.checkout-confirmation--short .checkout-confirmation__lede,
.checkout-confirmation--short .checkout-confirmation__number,
.checkout-confirmation--short .checkout-confirmation__next {
    margin-inline: auto;
    /* Строки делятся поровну, а не «как получится»: без этого вторая строка обрывалась одним
       словом («почту») под длинной первой — на центрированном тексте это особенно заметно. */
    text-wrap: balance;
}

.checkout-confirmation--short .checkout-confirmation__actions {
    justify-content: center;
}

.checkout-confirmation__actions {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-3);
    margin-top: var(--space-6);
}

/* Столбик вместо ряда: главное действие сверху и крупное, отход — под ним и мелкий.
   Две одинаковые кнопки в ряд заставляли выбирать, и рука шла к правой — а справа был уход. */
.checkout-confirmation__actions--stacked {
    flex-direction: column;
    align-items: center;
    gap: var(--space-3);
}

.checkout-confirmation__pay {
    min-width: 16rem;
    padding-top: 0.95rem;
    padding-bottom: 0.95rem;
    font-size: 1.05rem;
}

/* Ссылка, а не кнопка: уход с оплаты не должен выглядеть равным оплате. */
.checkout-confirmation__back {
    font-size: 0.92rem;
    color: var(--color-text-muted);
    text-decoration: underline;
    text-underline-offset: 3px;
}

.checkout-confirmation__back:hover,
.checkout-confirmation__back:focus-visible {
    color: var(--color-text);
}

.checkout-confirmation__layout {
    display: flex;
    flex-direction: column;
    gap: var(--space-6);
    margin-bottom: var(--space-6);
}

.checkout-confirmation__details {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
}

.checkout-confirmation__details h2 {
    font-family: var(--font-heading);
    font-size: 1.05rem;
    font-weight: 600;
    margin-top: var(--space-4);
}

/* --------------------------------------------------------------------------
   Login / registration (Task 11). A single centered card, reusing .field/
   .field__input/.field__error/.btn from the checkout form rather than
   redefining input styling a third time.
   -------------------------------------------------------------------------- */

.auth-page {
    display: flex;
    justify-content: center;
    padding: var(--space-8) var(--space-4);
}

.auth-card {
    width: 100%;
    max-width: 420px;
    display: flex;
    flex-direction: column;
    gap: var(--space-5);
    padding: var(--space-6);
    background: var(--color-bg-soft);
    border-radius: var(--radius-md);
}

.auth-card__title {
    margin: 0;
}

.auth-form {
    display: flex;
    flex-direction: column;
    gap: var(--space-4);
}

.auth-form__submit {
    margin-top: var(--space-2);
}

.auth-card__footer {
    margin: 0;
    font-size: 0.9rem;
    color: var(--color-text-muted);
    text-align: center;
}

/* --------------------------------------------------------------------------
   Account area (Task 12): dashboard, order history/detail, saved addresses.
   Reuses .card / .btn / .field / .field__input / .field__error / .cart-flash /
   .cart-summary / .checkout-confirmation__* from earlier tasks rather than
   redefining any of them a second time.
   -------------------------------------------------------------------------- */

.account-page {
    padding-block: var(--space-7);
    display: flex;
    flex-direction: column;
    gap: var(--space-6);
}

.account-page__back {
    font-size: 0.9rem;
}

.account-page__back a {
    color: var(--color-text-muted);
}

.account-page__title {
    margin-bottom: 0;
    /* Same calmer treatment as the catalogue title (.catalog__header h1): Unbounded at its regular
       weight, not the heavy bold 700 the global h1 uses, so it isn't so big/bold. */
    font-weight: 400;
    letter-spacing: -0.01em;
}

.account-page__email {
    color: var(--color-text-muted);
    margin-top: calc(-1 * var(--space-4));
}

.account-page__logout {
    align-self: flex-start;
}

.account-dashboard__grid {
    display: grid;
    grid-template-columns: 1fr;
    gap: var(--space-5);
}

.account-dashboard__card {
    padding: var(--space-6) var(--space-5);
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
}

.account-dashboard__card h2 {
    font-family: var(--font-heading);
    font-size: 1.1rem;
    font-weight: 600;
}

.account-dashboard__card p {
    color: var(--color-text-muted);
}

.account-orders-empty {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: var(--space-4);
    padding: var(--space-8) 0;
    color: var(--color-text-muted);
}

.account-order-list {
    display: flex;
    flex-direction: column;
    gap: var(--space-4);
}

.account-order-list__item {
    padding: 0;
}

.account-order-list__link {
    display: flex;
    flex-wrap: wrap;
    justify-content: space-between;
    align-items: center;
    gap: var(--space-3);
    padding: var(--space-4) var(--space-5);
    color: var(--color-text);
}

.account-order-list__number {
    font-family: var(--font-heading);
    font-weight: 600;
}

.account-order-list__status {
    color: var(--color-text-muted);
    font-size: 0.9rem;
}

.account-order-list__total {
    font-weight: 700;
    color: var(--color-accent-dark);
}

.address-list {
    display: grid;
    grid-template-columns: 1fr;
    gap: var(--space-5);
}

.address-card {
    padding: var(--space-5);
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    position: relative;
}

.address-card__label {
    font-family: var(--font-heading);
    font-weight: 600;
}

.address-card__badge {
    position: absolute;
    top: var(--space-4);
    right: var(--space-4);
    background: var(--color-beige);
    color: var(--color-eerie-black);
    font-size: 0.75rem;
    padding: var(--space-1) var(--space-2);
    border-radius: var(--radius-sm);
}

.address-card__phone,
.address-card__lines,
.address-card__comment {
    color: var(--color-text-muted);
    font-size: 0.9rem;
}

.address-card__actions {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-4);
    margin-top: var(--space-2);
}

.address-card__inline-form {
    display: contents;
}

.address-card__action {
    font-size: 0.85rem;
    color: var(--color-accent-dark);
    text-decoration: underline;
    background: none;
    border: none;
    cursor: pointer;
    padding: 0;
    font-family: var(--font-body);
}

.address-card__action--danger {
    color: var(--color-danger);
}

.account-form {
    margin-top: var(--space-2);
    padding: var(--space-6) var(--space-5);
    background: var(--color-bg-soft);
    border-radius: var(--radius-md);
    display: flex;
    flex-direction: column;
    gap: var(--space-4);
}

.account-form__title {
    margin: 0;
}

.account-form__row {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-4);
}

.account-form__row .field {
    flex: 1 1 140px;
}

.account-form__actions {
    display: flex;
    align-items: center;
    gap: var(--space-4);
}

/* --------------------------------------------------------------------------
   Admin area (Task 13): dashboard, orders, products, categories. Reuses
   .card / .btn / .field / .field__input / .field__error / .account-form* /
   .checkout-form__section / .cart-summary__subtotal from earlier tasks rather
   than redefining input/card styling a third time - only table/nav/stat-grid
   layout is genuinely new here.
   -------------------------------------------------------------------------- */

.admin-shell {
    padding-block: var(--space-7);
    display: flex;
    flex-direction: column;
    gap: var(--space-6);
}

.admin-nav {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-4);
    padding-bottom: var(--space-4);
    border-bottom: 1px solid var(--color-border);
}

.admin-nav__link {
    font-family: var(--font-heading);
    font-weight: 600;
    font-size: 0.95rem;
    color: var(--color-text);
}

.admin-nav__link--back {
    margin-left: auto;
    font-weight: 400;
    color: var(--color-text-muted);
}

.admin-shell__body {
    display: flex;
    flex-direction: column;
    gap: var(--space-6);
}

.admin-page__title {
    margin-bottom: 0;
}

.admin-page__header {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-4);
}

.admin-flash {
    background: var(--color-bg-soft);
    border-radius: var(--radius-sm);
    padding: var(--space-3) var(--space-4);
    color: var(--color-accent-dark);
}

.admin-stats {
    display: grid;
    grid-template-columns: 1fr;
    gap: var(--space-4);
}

.admin-stats__card {
    padding: var(--space-5);
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
}

.admin-stats__card h2 {
    font-family: var(--font-heading);
    font-size: 0.95rem;
    font-weight: 600;
    color: var(--color-text-muted);
}

.admin-stats__value {
    font-size: 2rem;
    font-weight: 700;
    color: var(--color-accent-dark);
}

/* Подпись под цифрой: по скольким заказам она набежала. Мельче и приглушённо — число заказов
   уточняет сумму, а не спорит с ней за внимание. */
.admin-stats__note {
    margin: 0.15rem 0 0;
    font-size: 0.82rem;
    color: var(--color-text-muted);
}

.admin-panel {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
}

.admin-panel h2 {
    font-family: var(--font-heading);
    font-size: 1.05rem;
    font-weight: 600;
}

.admin-filter {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    gap: var(--space-4);
}

.admin-filter__field {
    min-width: 200px;
}

/* Подписчики рассылки (admin/newsletter-list.html): пояснение над списком и подзаголовки секций.
   Классы .admin-table/.admin-page__title переиспользуем как есть — свой вид таблицы не изобретаем. */
.admin-note {
    max-width: 46rem;
    margin-bottom: var(--space-4);
    color: var(--color-text-muted);
    font-size: 0.92rem;
    line-height: 1.6;
}

.admin-section__title {
    margin-top: var(--space-6);
    margin-bottom: var(--space-3);
    font-size: 1.05rem;
}

.admin-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 0.9rem;
}

/* ВСЁ В ТАБЛИЦАХ АДМИНКИ — ПО ЦЕНТРУ СТОЛБЦА, и заголовок, и данные под ним. Решение владелицы:
   разнобой «заголовок над одним краем, значение под другим» заставлял глаз каждый раз искать,
   к какому столбцу относится ячейка. */
.admin-table th,
.admin-table td {
    text-align: center;
    padding: var(--space-3);
    border-bottom: 1px solid var(--color-border);
    vertical-align: middle;
}

/* Исключение — многострочные куски текста: состав заявки, контакты покупателя, заметки. Абзац
   по центру читается рвано, потому что у каждой строки свой левый край. Значения (числа, статусы,
   даты, суммы) остаются по центру, тексты — по левому. */
.admin-table td.admin-cell-text,
.admin-table th.admin-cell-text {
    text-align: left;
}

.admin-table th {
    color: var(--color-text-muted);
    font-weight: 600;
}

.admin-table--form input,
.admin-table--form select {
    min-width: 90px;
}

/*
  ТЕКСТ ВНУТРИ ПОЛЕЙ — ТОЖЕ ПО ЦЕНТРУ. Правило выше центрирует ЯЧЕЙКУ, но поле ввода занимает
  её целиком и печатает свой текст по левому краю. Выходил ровно тот разнобой, ради которого
  центрирование и вводилось: «Размер» по центру столбца, а «80–86» под ним — у левой кромки.
  Владелица увидела это на снимке формы и спросила, почему данные не по центру.

  Исключение — поле выбора цвета: там текст стоит рядом с квадратиком-образцом и читается
  как подпись к нему, а не как значение столбца.
*/
.admin-table--form input,
.admin-table--form select {
    text-align: center;
}
.admin-table--form .color-field__hex {
    text-align: left;
}

/*
  ШТРИХКОД — ШИРИНА ПОД 13 ЦИФР. При общем минимуме в 90 px код обрезался на середине
  («46404854302…»), и проверить его глазами было нельзя. Моноширинные цифры того же кегля
  занимают около 10 rem, плюс поля ввода — отсюда 11,5.
*/
.admin-col--barcode {
    min-width: 13.5rem;
}
/* Поле должно занимать колонку целиком: общий минимум в 90 px оставлял его уже столбца,
   и тринадцать цифр упирались в правый край рамки. */
.admin-table--form .admin-col--barcode input {
    width: 100%;
    min-width: 0;
}
.admin-table--form .admin-col--barcode input {
    font-variant-numeric: tabular-nums;
    letter-spacing: 0.02em;
}

/*
  Столбец действий — НЕ flex. display:flex на <td> вынимает ячейку из табличной раскладки: она
  перестаёт тянуться по высоте строки, и весь столбец уезжает относительно остальных — линии
  под «Редактировать» не совпадают с линиями строк. Ровно это было видно в списке товаров.
  Ссылка и кнопка выстраиваются строчными элементами, а по центру их ставит общее правило
  выравнивания таблицы.
*/
.admin-table td.admin-table__actions { white-space: nowrap; }
.admin-table td.admin-table__actions > * { display: inline-block; vertical-align: middle; margin: 0; }
.admin-table td.admin-table__actions > * + * { margin-left: var(--space-3); }

.admin-link-btn {
    font-size: 0.85rem;
    color: var(--color-accent-dark);
    text-decoration: underline;
    background: none;
    border: none;
    cursor: pointer;
    padding: 0;
    font-family: var(--font-body);
}

.admin-link-btn--danger {
    color: var(--color-danger);
}

.admin-form {
    display: flex;
    flex-direction: column;
    gap: var(--space-6);
}

.admin-form__hint {
    color: var(--color-text-muted);
    font-size: 0.85rem;
    max-width: 39.5rem;
}

/* Variant colour field: a native colour picker (the visible swatch) next to the hex text input,
   kept in sync by static/js/admin-product-form.js. Clicking the swatch opens the OS colour picker. */
.color-field {
    display: flex;
    align-items: center;
    gap: var(--space-2);
}

.color-field__picker {
    flex: 0 0 auto;
    width: 34px;
    height: 34px;
    padding: 0;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm);
    background: none;
    cursor: pointer;
}

/* Trim the browser's default swatch chrome so the control reads as a clean colour chip. */
.color-field__picker::-webkit-color-swatch-wrapper {
    padding: 2px;
}

.color-field__picker::-webkit-color-swatch {
    border: none;
    border-radius: 3px;
}

.color-field__hex {
    min-width: 0;
}

/* ---- Admin product-form image slots (drag-and-drop upload, static/js/admin-product-form.js) ---- */
.image-slots {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
    gap: var(--space-4);
}

.image-slot {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
    padding: var(--space-3);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    background: var(--color-surface);
}

/* Dropzone: a dashed frame that shows the preview (or a hint when empty), highlighted while a file
   is dragged over it. It's a <label> wrapping the hidden file input, so a click opens the picker. */
.image-slot__drop {
    position: relative;
    display: flex;
    align-items: center;
    justify-content: center;
    aspect-ratio: 4 / 5;
    border: 1.5px dashed var(--color-border);
    border-radius: var(--radius-sm);
    background: var(--color-bg-soft);
    cursor: pointer;
    overflow: hidden;
    text-align: center;
}

.image-slot__drop.is-dragover {
    border-color: var(--color-accent-dark);
    background: var(--color-bg-tint-blue);
}

.image-slot__drop:focus-visible {
    outline: 2px solid var(--color-accent-dark);
    outline-offset: 2px;
}

.image-slot__preview {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
}

.image-slot__preview.is-empty {
    display: none;
}

.image-slot__hint {
    padding: var(--space-3);
    color: var(--color-text-muted);
    font-size: 0.82rem;
    line-height: 1.4;
}

/* When a photo is present its preview covers the frame, so the hint underneath is hidden. */
.image-slot__preview:not(.is-empty) ~ .image-slot__hint {
    display: none;
}

.image-slot__uploading {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    background: rgba(255, 255, 255, 0.82);
    font-size: 0.85rem;
    color: var(--color-text);
}

/* The flex display above would otherwise override the `hidden` attribute's display:none. */
.image-slot__uploading[hidden] {
    display: none;
}

.image-slot__fields {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
}

.image-slot__meta {
    display: grid;
    grid-template-columns: 1fr 90px;
    gap: var(--space-2);
}

.image-slot__clear {
    align-self: flex-start;
    font-size: 0.8rem;
    color: var(--color-text-muted);
    text-decoration: underline;
    text-underline-offset: 2px;
    cursor: pointer;
}

.image-slot__clear:hover {
    color: var(--color-danger);
}

.admin-order-detail {
    display: flex;
    flex-direction: column;
    gap: var(--space-6);
}

.admin-status-form {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-4);
    align-items: center;
}

/* Wraps every <table class="admin-table"> so wide tables scroll horizontally instead of
   overflowing the viewport on narrow (~375px) screens, rather than shrinking columns unreadably. */
.table-responsive {
    width: 100%;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    min-width: 0;
}

/* The admin form is a flex column; its fieldsets default to min-width:auto, so a wide variant/price
   table would stretch a fieldset past the viewport (horizontal page scroll on phones) instead of
   scrolling inside its .table-responsive wrapper. min-width:0 lets the fieldset shrink to the column
   width so the table scrolls within it. */
.admin-form .checkout-form__section {
    min-width: 0;
}

/* --------------------------------------------------------------------------
   About page (Task 14): editorial brand-story layout for /about. Reuses .usp__grid/.usp__item
   from the home page for the closing "what we believe" section rather than redefining it.
   -------------------------------------------------------------------------- */

/* Creative "family album" layout: a full-bleed photo hero with parallax, a scattered collage of
   tilted polaroid photos + overlapping text notes (chaotic on desktop, clean stack on mobile), a
   big tilted closing quote, and scroll-reveal animations (static/js/about.js). */
.about {
    /* No bottom padding: the page now ends on the full-bleed production-photo band, which sits flush
       against the footer (paired with `.about ~ .site-footer { margin-top: 0 }`). */
    padding-bottom: 0;
    /* rotated / offset collage pieces must never trigger horizontal scroll */
    overflow-x: clip;
}

/* Scroll reveal (site-wide, static/js/reveal.js): elements fade/rise in when they enter the
   viewport. Hidden ONLY when JS is active (html.js-reveal); with no JS everything is visible. */
.js-reveal .reveal {
    opacity: 0;
    transform: translateY(26px);
}

.reveal {
    transition: opacity 0.7s ease, transform 0.7s ease;
}

.reveal.is-visible {
    opacity: 1;
    transform: none;
}

@media (prefers-reduced-motion: reduce) {
    .js-reveal .reveal { opacity: 1; transform: none; }
    .reveal { transition: none; }
}

.about-eyebrow {
    font-family: var(--font-heading);
    font-weight: 700;
    letter-spacing: 0.2em;
    text-transform: uppercase;
    font-size: 0.75rem;
    color: var(--color-text-muted);
}

/* ---- Full-bleed hero ---- */
.about-hero {
    position: relative;
    min-height: min(62vh, 540px);     /* shorter hero so the story + photos sit higher up the page */
    display: flex;
    align-items: flex-end;            /* sit the intro in the lower part of the photo, off the face */
    justify-content: center;
    overflow: hidden;
    color: var(--color-white);
    text-align: center;
}

.about-hero__bg {
    position: absolute;
    inset: -10% 0;                 /* taller than the frame so the parallax shift has room */
    background-size: cover;
    background-position: center;
    will-change: transform;
}

.about-hero__scrim {
    position: absolute;
    inset: 0;
    /* Darken enough that the white intro text stays readable over a bright, sunlit photo, while
       keeping the moody atmosphere: a uniform wash + a soft centred pool behind the text. */
    background:
        radial-gradient(120% 80% at 50% 45%, rgba(30, 30, 30, 0.5) 0%, rgba(30, 30, 30, 0.28) 55%, rgba(30, 30, 30, 0.2) 100%),
        linear-gradient(180deg, rgba(30, 30, 30, 0.35) 0%, rgba(30, 30, 30, 0.3) 45%, rgba(30, 30, 30, 0.55) 100%);
}

.about-hero__back {
    position: absolute;
    top: var(--space-4);
    left: var(--space-4);
    z-index: 2;
    color: rgba(255, 255, 255, 0.9);
    font-size: 0.9rem;
    text-shadow: 0 1px 8px rgba(30, 30, 30, 0.45);
}

.about-hero__back:hover {
    color: var(--color-white);
}

.about-hero__content {
    position: relative;
    z-index: 1;
    max-width: 26.5rem;
    padding: var(--space-6);
    padding-bottom: clamp(var(--space-7), 9vh, var(--space-8));  /* lift clear of the scroll hint */
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--space-4);
}

.about-hero .about-eyebrow {
    color: rgba(255, 255, 255, 0.85);
}

.about-hero__lede {
    /* explicit white: the base `p { color }` rule otherwise overrides the inherited white */
    color: var(--color-white);
    /* body font, normal weight, modest size: the heavy display face read as too big and bold
       over the photo - this keeps the intro calm and easy to read. */
    font-family: var(--font-body);
    font-weight: 400;
    font-size: clamp(1.05rem, 0.9rem + 0.8vw, 1.45rem);
    line-height: 1.5;
    text-shadow: 0 1px 12px rgba(30, 30, 30, 0.4);
}

.about-hero__sub {
    font-size: 1rem;
    color: rgba(255, 255, 255, 0.92);
    text-shadow: 0 1px 8px rgba(30, 30, 30, 0.4);
}

.about-hero__scrollhint {
    position: absolute;
    left: 50%;
    bottom: var(--space-5);
    transform: translateX(-50%);
    z-index: 1;
    line-height: 0;                       /* a bouncing down-arrow (SVG), not the old "листайте" text */
    color: rgba(255, 255, 255, 0.85);
    animation: about-bob 2.4s ease-in-out infinite;
}

@keyframes about-bob {
    0%, 100% { transform: translate(-50%, 0); }
    50% { transform: translate(-50%, 7px); }
}

@media (prefers-reduced-motion: reduce) {
    .about-hero__scrollhint { animation: none; }
}

/* ---- "О нас" story ----
   One family photo filling the left column edge-to-edge (full-bleed - it runs to the very left edge
   with no white gap around it), the story in a comfortable reading column on the right. Mobile
   default: the photo on top, the story below. */
.about-story {
    background: var(--color-bg);
    padding: 0;
}

.about-story__grid {
    display: flex;
    flex-direction: column;
}

/* Mobile order: photo first, then the "О нас" label, then the story. */
.about-story__media {
    order: 1;
}

.about-story__eyebrow {
    order: 2;
}

.about-story__text {
    order: 3;
}

.about-story__media {
    margin: 0;
    line-height: 0;
    /* Тёплая подложка в зарезервированной рамке: пока фото грузится, видно ровный цвет, а не белую
       вспышку или «проявляющийся» блюр. Рамка уже зарезервирована (width/height + aspect-ratio),
       поэтому сдвига нет, фото просто появляется поверх подложки. */
    background-color: #e7dfd2;
}

.about-story__media img {
    display: block;
    width: 100%;
    height: auto;
    object-fit: cover;
    filter: saturate(0.96) contrast(0.98);
}

.about-story__text {
    display: flex;
    flex-direction: column;
    gap: var(--space-4);
    /* Justified (по ширине) on all sizes at the owner's request. */
    text-align: justify;
    padding: var(--space-6) var(--space-4);
}

.about-story__eyebrow {
    display: block;
    margin-bottom: var(--space-1);
    text-align: left;
    /* Horizontal padding so the "О нас" label isn't flush against the screen edge on mobile, plus a
       top gap so it sits below the photo instead of right against it (desktop overrides both with its
       own grid placement). */
    padding-inline: var(--space-4);
    margin-top: var(--space-5);
}

.about-story__lede,
.about-story__text p {
    color: var(--color-text-muted);
    line-height: 1.7;
}

.about-story__lede {
    color: var(--color-text);
}

/* Desktop: two columns inside a centred max-width (so there's a white margin on the sides, as before).
   The photo column is stretched to the text column's height and its image is `object-fit: cover`, so
   the photo fills the whole column height - no white gap below it before "Как мы работаем". The text
   column is the wider of the two and its copy is justified. */
@media (min-width: 900px) {
    .about-story__grid {
        display: grid;
        grid-template-columns: minmax(0, 0.85fr) minmax(0, 1.15fr);
        /* Row 1 = the "О нас" label (auto height) over the text column; row 2 = photo + story. This
           is what drops the photo down so it starts on the first story line, with "О нас" sitting
           above it rather than beside its top corner. */
        grid-template-rows: auto 1fr;
        align-items: stretch;
        max-width: 1280px;
        margin-inline: auto;
    }

    /* Breathing room lives on the section (top only), so the columns themselves have NO vertical
       padding. That way row 2's height equals the story CONTENT exactly, and the photo - absolutely
       positioned to fill it - starts level with the first line and ends level with the last one,
       never overhanging it. */
    .about-story {
        /* Equal white space above ("О нас" ↔ header) and below (text ↔ "Как мы работаем"). */
        padding-top: clamp(var(--space-6), 4vw, var(--space-8));
        padding-bottom: clamp(var(--space-6), 4vw, var(--space-8));
    }

    .about-story__eyebrow {
        grid-column: 2;
        grid-row: 1;
        margin: 0 0 var(--space-4);
        padding-inline: clamp(var(--space-6), 5vw, var(--space-8));
    }

    /* Photo spans BOTH rows (the "О нас" label row + the story row), so its top starts level with
       "О нас" and its bottom ends level with the last line of the story. */
    .about-story__media {
        grid-column: 1;
        grid-row: 1 / 3;
        position: relative;
        height: 100%;
    }

    .about-story__media img {
        position: absolute;
        inset: 0;
        width: 100%;
        height: 100%;
    }

    .about-story__text {
        grid-column: 2;
        grid-row: 2;
        justify-content: center;
        padding-block: 0;
        padding-inline: clamp(var(--space-6), 5vw, var(--space-8));
        /* Justify only on the wide desktop column, where it reads cleanly. */
        text-align: justify;
    }
}

/* Full-bleed warm band (edge to edge). Was a cool blue tint (--color-bg-tint-blue) with bold
   centred headings, which read cold and spec-sheet-dry next to the warm editorial "О нас" collage
   above it. Now it is a soft cream ground that continues the collage's warmth: an eyebrow, a lighter
   heading, and three airy columns separated only by hairline rules - the same quiet, unboxed
   editorial voice as the story, rather than a panel bolted on below it. */
/* Warm butter band (--color-beige, echoing the header/marquee) with the "Как мы работаем" copy:
   an eyebrow, a lighter heading, and three airy columns separated only by hairline rules. */
.about-values {
    /* No top margin: the white gap above this block is now the "О нас" section's own padding-bottom,
       kept equal to its padding-top so the spacing matches top and bottom. */
    margin-top: 0;
    text-align: center;
    background: var(--color-beige);
    /* Боковой отступ полосы: карточка внутри должна уходить вправо и влево, а не жаться к
       середине, но и не упираться в самые края экрана. Значение подобрано по просьбе владелицы
       в два захода — сначала карточку раздвинули до краёв, потом чуть сузили снаружи. */
    padding: var(--space-6) clamp(var(--space-3), 1.8vw, var(--space-5));
}

.about-values__eyebrow {
    display: block;
    margin-bottom: var(--space-2);
}

.about-values h2 {
    font-weight: 500;
    letter-spacing: -0.01em;
    margin-bottom: var(--space-7);
}

.about-values h2,
.about-values .usp__grid {
    max-width: 1640px;
    margin-inline: auto;
}

.about-values .usp__grid {
    gap: 0;
    /* Equal-width columns (owner's request). Kept as wide as the band allows (see the reduced
       section padding + wider max-width) so the longest paragraph still lands on four lines. */
    grid-template-columns: repeat(3, 1fr);
}

/* Горизонтальные отступы держит КОЛОНКА, а не карточка (см. padding карточки ниже: по бокам он
   обнулён). Иначе крайний текст отступал от края карточки на сумму двух отступов, а от
   разделительной черты — только на свой, и просветы читались разными. Теперь и от края, и от
   черты одно и то же расстояние. */
.about-values .usp__item {
    text-align: left;
    padding-inline: var(--space-5);
}

.about-values .usp__item + .usp__item {
    border-left: 1px solid rgba(30, 30, 30, 0.12);
}

/* Heading centred over its own (left-aligned) paragraph. */
.about-values .usp__item h3 {
    /* 600, а не 500: на широкой светлой карточке заголовки колонок читались как обычный текст —
       владелица описала это как «слетела жирность». Отличать их от абзаца должен вес, а не размер:
       поднимать кегль здесь нельзя, три заголовка встанут в разную ширину строки. */
    font-weight: 600;
    font-size: 1.05rem;
    margin-bottom: var(--space-3);
    text-align: center;
}

.about-values .usp__item p {
    line-height: 1.7; /* same line spacing as the "О нас" story text */
    color: var(--color-text-muted);
    text-align: justify;
}

@media (max-width: 767px) {
    /* Stack to ONE column on phones. Without this the block kept its 3-column grid, squeezing each
       paragraph into a ~120px column where justified text stretched and broke apart.
       The three points also sit inside ONE light card (rounded + soft shadow), lifted off the beige
       band, so the "Что лежит в основе каждой вещи" heading above reads as a separate intro and no
       longer blends into "Собственная разработка". */
    .about-values .usp__grid {
        grid-template-columns: 1fr;
        background: var(--color-surface);
        border-radius: 16px;
        margin: var(--space-6) var(--space-3) 0;
        padding: var(--space-4) var(--space-5);
        box-shadow: 0 6px 24px rgba(30, 30, 30, 0.08);
    }

    /* Heading is the section intro on the plain beige; the gap down to the card (grid margin-top)
       does the separating now, so drop the old divider line under it. */
    .about-values h2 {
        margin-bottom: 0;
        padding-bottom: 0;
        border-bottom: none;
    }

    .about-values .usp__item:first-child {
        padding-top: var(--space-4);
    }

    /* On mobile the headings stay centred and the paragraphs justified (по ширине), same as desktop,
       at the owner's request - only the column layout and dividers change for the narrow screen. */
    .about-values .usp__item {
        padding-inline: 0;
        padding-block: var(--space-5);
    }

    .about-values .usp__item + .usp__item {
        border-left: none;
        border-top: 1px solid rgba(30, 30, 30, 0.12);
    }
}

/* Узкий планшет (768–899): колонок по-прежнему три, но светлой карточки ещё нет, и ширины на
   текст остаётся около 220 px. С отступом в 24 px выключка по ширине начинает растягивать строки
   пробелами. Возвращаем здесь прежние 12 px: равенство отступов «от края карточки» и «от черты»
   тут не нарушается, потому что карточки — и её собственных отступов — на этой ширине нет. */
@media (min-width: 768px) and (max-width: 899px) {
    .about-values .usp__item {
        padding-inline: var(--space-3);
    }
}

/* Desktop: lift the three points into a light card (same treatment as mobile) so the heading
   "Что лежит в основе каждой вещи" reads as an intro, clearly set off from the points below. The
   vertical hairlines between columns come from the base .usp__item+.usp__item rule. */
@media (min-width: 900px) {
    .about-values h2 {
        margin-bottom: 0;
    }

    .about-values .usp__grid {
        /* Был #FBFAF6 — тон из белой эпохи: красного на единицу больше зелёного, разница с
           синим всего 5. Ровно тот рецепт, который на тёплой бумаге читается розоватым.
           Теперь общий тон поверхностей: красного и зелёного поровну, синего на 11 меньше. */
        background: var(--color-surface);
        border-radius: 18px;
        margin-top: var(--space-7);
        /* По бокам нуля: отступы дают сами колонки, чтобы расстояние от текста до края карточки
           совпадало с расстоянием от текста до разделительной черты. */
        padding: var(--space-6) 0;
        box-shadow: 0 8px 30px rgba(30, 30, 30, 0.08);
    }
}

/* Atelier triptych directly under the copy: three production photos edge to edge across the full
   width, no gaps between them and no margins around them. Pre-graded to one warm film tone; this
   light filter holds the band consistent; a shared aspect-ratio keeps all three the same height. */
.about-work {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 0;
    /* На всю ширину, без белых краёв (edge-to-edge). */
    margin: 0;
    line-height: 0;
    /* start — фото держат заданную пропорцию и НЕ растягиваются по высоте. */
    align-items: start;
}

.about-work img {
    width: 100%;
    height: auto;        /* высоту задаёт aspect-ratio (не растягиваем) */
    /* Кадр стал ниже по просьбе владелицы: 6/5 вместо 14/15. Уменьшаем именно ПРОПОРЦИЮ, а не
       ширину — полоса из трёх фото должна остаться во всю ширину экрана, без белых полей по бокам,
       иначе развалится вся композиция низа страницы. */
    aspect-ratio: 6 / 5;
    object-fit: cover;
    display: block;
    filter: saturate(0.94) contrast(0.98);
    background-color: #e7dfd2; /* тёплая подложка, пока фото грузится — без белой вспышки/сдвига */
}

/* The About page ends on the full-bleed photo band, so drop the footer's usual top margin and let
   the footer sit flush under the photos (owner's request - no white gap between the production
   photos and "Покупателям"). `.about` is only on this page's <main>, so other pages keep the gap. */
.about ~ .site-footer {
    margin-top: 0;
}

/* Мобилка: фото производства делаем компактнее и с зазором, чтобы три снимка читались как отдельные,
   а не сливались вплотную («друг на друге»). Десктоп остаётся прежним (сплошная полоса без зазоров). */
@media (max-width: 600px) {
    /* Без зазоров и полей — сплошная полоса, как было раньше (белые рамки убраны).
       align-items:start и height:auto уже заданы в базовом правиле выше, поэтому фото не
       растягиваются. На телефоне колонка узкая — берём вертикальный кадр. */
    .about-work img {
        /* На телефоне фото стоят в один столбец и оттого кажутся выше — пропорцию держим ближе к
           квадрату, чтобы полоса не растянулась на три экрана прокрутки. */
        aspect-ratio: 1 / 1;
    }
}

/* --------------------------------------------------------------------------
   Legal pages (Task 14): one shared template for /legal/privacy, /legal/terms, /legal/returns.
   -------------------------------------------------------------------------- */

.legal-page {
    /* Одинаковый воздух сверху и снизу: столько же, сколько от шапки до «← Вернуться назад».
       Широкую белую полосу (было 48 + 72 = 120 px) убирает не этот отступ, а обнулённый
       margin подвала ниже — сам по себе отступ страницы остаётся симметричным. */
    padding-block: var(--space-7);
    max-width: 46.5rem;
}

/* Подвал вплотную к тексту «Покупателям» (Доставка, Возврат, Политика, Соглашение, Контакты).
   Тот же приём, что уже применён к каталогу чуть выше: у этих страниц контент заканчивается
   обычным абзацем, и 72 px отступа подвала читались как «страница оборвалась». */
.site-main:has(.legal-page) ~ .site-footer,
.site-main:has(.legal-page) + .site-footer {
    margin-top: 0;
}

/* Body copy on the "Покупателям" pages (Доставка, Возврат, Политика, Соглашение — all use
   .legal-page) is justified (по ширине), the same as the "О нас" story, so every page reads evenly
   on mobile. Justify only — no word hyphenation (owner asked for alignment only). Headings and the
   short courier-method lines are left as-is. */
.legal-page__lead,
.legal-page p,
.legal-page li {
    text-align: justify;
}

.legal-page__lead {
    font-size: 1.05rem;
    line-height: 1.6;
    margin-bottom: var(--space-6);
}

.legal-page__title {
    margin-bottom: var(--space-4);
}

.legal-page__note {
    color: var(--color-text-muted);
    font-size: 0.9rem;
    margin-bottom: var(--space-6);
}

/* A closing note that follows the sections (not a lead-in) needs breathing room above it. */
.legal-page__section + .legal-page__note {
    margin-top: var(--space-6);
}

/* Ссылка-действие в конце правовой страницы («Заполнить заявку на возврат»).
   Отдельный вариант, а не правка .legal-page__note: тем же классом набраны сноски на «Контактах»,
   в размерной сетке и на страницах подписки, и там укрупнять нечего.

   Крупнее сноски, потому что это единственное действие на странице: человек, дочитавший условия,
   идёт именно сюда. Дата редакции прижата к ней вплотную и набрана мельче — это подпись под
   документом, а не следующий шаг, и разделять их воздухом было незачем. */
.legal-page__note--action {
    font-size: 1.05rem;
    margin-bottom: var(--space-1);
}

.legal-page__note--action + .legal-page__revision {
    margin-top: 0;
    font-size: 0.78rem;
}

/* Дата редакции документа. Служебная строка: нужна в споре и при проверке, но читателю не адресована,
   поэтому — мельче основного текста и приглушённым цветом, как сноска. */
.legal-page__revision {
    color: var(--color-text-muted);
    font-size: 0.85rem;
    margin-bottom: var(--space-6);
}

/* Дата, стоящая ПОСЛЕ разделов (страница возврата), — это подпись под документом, а не подзаголовок
   под названием: сверху ей нужен воздух и отделяющая линия. */
.legal-page__section + .legal-page__revision {
    margin-top: var(--space-6);
    padding-top: var(--space-4);
    border-top: 1px solid var(--color-border);
}

.legal-page__section + .legal-page__section {
    margin-top: var(--space-6);
}

.legal-page__section h2 {
    font-size: 1.1rem;
}

.legal-page__section h3 {
    font-size: 1rem;
    margin-bottom: var(--space-2);
}

/* Inline "свяжитесь с нами…" contact chooser: an underlined trigger that reveals the contact
   methods (same as the footer) on hover/focus. */
/* Выбор действия под каждой вещью в заявке: появляется по галочке, поэтому отбит слева,
   чтобы визуально принадлежать вещи, а не форме целиком. */
/* Выбор — второй строкой, с отступом ровно под название: миниатюра 56 px, галочка и два зазора.
   Так вторая строка начинается там же, где название, а не от края карточки. */
.return-item__choice {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    margin: 0 0 0 calc(56px + 1rem + var(--space-3) * 2);
}

/* На телефоне отступ съедает половину ширины — там выбор идёт от края. */
@media (max-width: 700px) {
    .return-item__choice { margin-left: 0; }
}

.return-item__action {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    font-size: 0.95rem;
}

.contact-dropdown {
    position: relative;
}

.contact-dropdown__trigger {
    color: inherit;
    text-decoration: underline;
    text-underline-offset: 2px;
    cursor: pointer;
}

.contact-dropdown__trigger:hover {
    color: var(--color-payne-gray);
}

.contact-dropdown__menu {
    position: absolute;
    top: calc(100% + 6px);
    left: 0;
    z-index: 30;
    min-width: 190px;
    display: none;
    flex-direction: column;
    padding: var(--space-2);
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm);
    box-shadow: 0 14px 34px rgba(30, 30, 30, 0.14);
}

.contact-dropdown:hover .contact-dropdown__menu,
.contact-dropdown:focus-within .contact-dropdown__menu {
    display: flex;
}

.contact-dropdown__item {
    padding: var(--space-2) var(--space-3);
    border-radius: var(--radius-sm);
    color: var(--color-text);
    font-size: 0.95rem;
}

.contact-dropdown__item:hover {
    background: var(--color-bg-soft);
}

/* Delivery page: the "Доставка заказа" page title is sized to match the section-level
   "Оплата заказа" heading (rather than the larger default legal-page h1). */
.legal-page__heading--section {
    font-size: 1.1rem;
    margin-bottom: var(--space-3);
}

/* Consistent vertical rhythm between every block in a section (paragraphs, lists, etc.),
   with readable line spacing. */
.legal-page__section p {
    line-height: 1.6;
}

.legal-page__section > * + * {
    margin-top: var(--space-3);
}

.legal-page__section ul {
    list-style: disc;
    padding-left: 1.4em;
}

.legal-page__section li + li {
    margin-top: var(--space-1);
}

/* Free-shipping perk: a quietly emphasised line. */
.legal-page__highlight {
    font-weight: 600;
    color: var(--color-text);
}

/* Delivery methods (delivery.html): each courier as an icon + name + one-line description, instead
   of a plain bullet. Not the disc-bullet list above - reset the list styling and lay each row out. */
.delivery-methods {
    list-style: none;
    padding-left: 0;
    display: flex;
    flex-direction: column;
    gap: var(--space-4);
    margin-block: var(--space-2);
}

.delivery-method {
    display: grid;
    grid-template-columns: 96px 1fr;
    align-items: center;
    gap: var(--space-4);
}

.delivery-method + .delivery-method {
    margin-top: 0; /* overrides the generic `li + li` spacing; the grid gap handles it */
}

/* Courier logos as coloured wordmarks in each brand's colour, rendered in SVG so both are the exact
   same height and share one visual style (the raw icons ship in different styles/sizes). Fixed 24px
   SVG height + a fixed-width column keeps the two aligned and the descriptions flush. */
.courier-logo {
    display: inline-flex;
    align-items: center;
}

.courier-logo svg {
    height: 24px;
    width: auto;
}

.courier-logo__word {
    font-family: var(--font-heading);
    font-weight: 800;
    font-size: 30px;
    letter-spacing: 0.01em;
}

.courier-logo--cdek .courier-logo__word {
    fill: #1ab248; /* СДЭК green */
}

.courier-logo--5post .courier-logo__word {
    fill: #1d1d1b; /* 5post dark */
}

.courier-logo--5post .courier-logo__five {
    fill: #e5006d; /* 5post magenta */
}

.courier-logo--yandex .courier-logo__word {
    fill: #fc3f1d; /* Яндекс red */
}

.delivery-method__desc {
    color: var(--color-text);
}

/* --------------------------------------------------------------------------
   Brand-emblem intro splash (emblem task, item 2): layout/base.html, static/js/intro.js.
   A fixed overlay, `hidden` by default in the markup (so no-JS visitors/crawlers never see it and
   the real page underneath is never delayed), that intro.js reveals once per visitor and draws
   the two emblem paths via stroke-dashoffset before fading itself out. See intro.js for the
   localStorage gate + prefers-reduced-motion handling; the media query below is a CSS-only
   backstop in case that JS check is ever bypassed.
   -------------------------------------------------------------------------- */

.brand-intro {
    position: fixed;
    inset: 0;
    z-index: 200;
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--color-alabaster);
    transition: opacity 0.4s ease;
}

.brand-intro[hidden] {
    display: none;
}

.brand-intro.is-fading {
    opacity: 0;
}

.brand-intro__emblem {
    width: min(36vw, 168px);
    height: auto;
    color: var(--color-payne-gray);
}

.brand-intro__emblem path {
    transition: stroke-dashoffset 0.9s ease;
}

@media (prefers-reduced-motion: reduce) {
    /* Belt-and-braces: intro.js already skips showing this overlay at all when reduced motion is
       requested, but if script execution is ever delayed/interrupted after the element is
       unhidden, this guarantees no animated content is ever forced on a reduced-motion visitor. */
    .brand-intro {
        display: none !important;
    }
}

/* --------------------------------------------------------------------------
   Error pages (Task 14): templates/error/404.html, templates/error/500.html.
   -------------------------------------------------------------------------- */

.error-page {
    padding-block: var(--space-8);
    min-height: 40vh;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    text-align: center;
    gap: var(--space-4);
}

.error-page__code {
    font-family: var(--font-heading);
    font-weight: 800;
    font-size: clamp(3rem, 2rem + 6vw, 6rem);
    /* Deep tone, not the pale blue accent, for legibility at this size against a white page. */
    color: var(--color-accent-dark);
    line-height: 1;
}

.error-page__text {
    color: var(--color-text-muted);
    max-width: 33rem;
}

/* --------------------------------------------------------------------------
   Favorites page (revamp item 9): /favorites is populated entirely client-side by
   static/js/favorites.js from localStorage - #favorites-grid reuses .product-grid/.card exactly
   like the catalog, #favorites-empty is the "nothing saved yet" state.
   -------------------------------------------------------------------------- */

.favorites-page {
    padding-block: var(--space-7);
}

.favorites-page h1 {
    font-weight: 400;          /* лёгкий, как «Каталог» */
    letter-spacing: -0.01em;
    margin-bottom: var(--space-6);
}

.favorites-empty {
    position: relative;
    isolation: isolate;
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
    gap: var(--space-4);
    padding: var(--space-8) 0;
    color: var(--color-text-muted);
}

/* Both rules above (`.favorites-empty { display: flex }` and `.product-grid { display: grid }`,
   see line ~1125) are AUTHOR styles with the same specificity as the browser's own UA default
   `[hidden] { display: none }` - author styles always win a same-specificity tie against UA
   defaults, so plain `element.hidden = true/false` (favorites.js) had no visual effect at all
   without these overrides: a class+attribute selector's higher specificity is what actually hides
   either element once its `hidden` attribute is set. */
.favorites-empty[hidden] {
    display: none;
}

#favorites-grid[hidden] {
    display: none;
}

/* "Добавить в корзину" (static/js/favorites.js's favoriteCardMarkup) sits as a sibling AFTER
   .product-card__link, not inside it (so clicking it never navigates to the product page) - its
   horizontal margin mirrors .product-card__body's own padding so it lines up with the name/price
   text above it, and its bottom margin gives it the same breathing room as that padding does at
   the top. */
.favorite-card__add-btn {
    display: flex;
    width: calc(100% - var(--space-4) * 2);
    margin: 0 var(--space-4) var(--space-4);
}


/* --------------------------------------------------------------------------
   Breakpoints
   -------------------------------------------------------------------------- */

@media (min-width: 768px) {
    /* 320 px вместо 220: при окне 1440 буквы выходят ростом 38 px — ровно как у ziggyzaza
       (меряно пипеткой по их странице: 292×38, у нас было 220×26). 12.10.2026. */
    .site-header .wordmark__img {
        width: 320px;
    }

    .collections__grid {
        grid-template-columns: repeat(4, 1fr);
    }

    .usp__grid {
        grid-template-columns: repeat(3, 1fr);
    }

    .site-footer__grid {
        grid-template-columns: 1.1fr 0.9fr 1.4fr;
        gap: var(--space-7);
    }

    /* Колонки встали в ряд — и с этой ширины раскрытая панель «Контакты» вынимается из потока.
       Пока она была в потоке, её высота входила в высоту строки сетки: на одних ширинах панель
       умещалась, на других (около 1180px) оказывалась выше соседних колонок и толкала нижнюю
       полосу с реквизитами вниз — подвал подпрыгивал на каждом нажатии «Контакты».

       Вертикаль не задана намеренно: без top абсолютный элемент встаёт ровно туда, где стоял бы
       в потоке (static position), то есть сразу под кнопкой «Контакты». left/right: 0 задают ему
       ширину колонки. Место под него колонке резервирует main.js через min-height — иначе
       раскрытая панель налезала бы на реквизиты на узких экранах этого диапазона. */
    .site-footer__contacts {
        position: relative;
    }

    .site-footer__contacts-panel {
        position: absolute;
        left: 0;
        right: 0;
    }

    .product-detail__layout {
        grid-template-columns: 1fr 1fr;
        align-items: start;
    }

    /* The info column is forced to the MAIN photo's height (both columns are the same width and the
       photo is 4:5, so this aspect-ratio reproduces its height exactly), and `margin-top: auto` on
       .product-tabs then pushes the "О модели" / "Детали" accordions to the column's bottom edge -
       so the bottom of "Детали" ends on the same line as the bottom of the first photo, which is
       the layout the owner wants. This only holds while the info content fits inside the photo's
       height; the seam-colour swatches were shrunk (see .color-swatch) partly to keep the
       longsleeve's taller info column within it so the accordions align rather than overflow below. */
    .product-info {
        display: flex;
        flex-direction: column;
        aspect-ratio: 4 / 5;
    }

    .product-tabs {
        margin-top: auto;
    }

    .cart-page__layout {
        display: grid;
        grid-template-columns: 1fr 320px;
        align-items: start;
        gap: var(--space-7);
    }

    .cart-line__media {
        flex-basis: 120px;
        width: 120px;
    }

    .checkout-page__layout {
        display: grid;
        grid-template-columns: minmax(0, 1fr) 440px;
        gap: var(--space-7);
        align-items: start;
    }

    /* «Ваш заказ»: держится на уровне формы при прокрутке (sticky) и растянут по высоте —
       содержимое сверху, кнопка «Перейти к оплате» прижата к низу карточки. */
    .checkout-summary {
        position: sticky;
        top: 96px;
        align-self: start;
        display: flex;
        flex-direction: column;
        min-height: 540px;
    }

    .checkout-summary .checkout-summary__submit {
        margin-top: auto;
    }

    .checkout-confirmation__layout {
        display: grid;
        grid-template-columns: 1fr 380px;
        gap: var(--space-7);
    }

    .account-dashboard__grid,
    .address-list {
        grid-template-columns: repeat(2, 1fr);
    }

    .admin-stats {
        grid-template-columns: repeat(4, 1fr);
    }

    .cookie-consent__inner {
        flex-direction: row;
        align-items: center;
        justify-content: space-between;
    }
}

@media (min-width: 1024px) {
    .header-utility__pill {
        display: inline-flex;
    }

    /*
      Одинаковые просветы между колонками подвала. Раньше «Будем на связи» и «Покупателям» стояли
      заметно ближе друг к другу, чем «Покупателям» и «Контакты», хотя gap у грида один на все
      стыки. Дело не в gap, а в том, что колонки шире своего содержимого: форма подписки ограничена
      340px внутри дорожки 1.1fr, список ссылок — своей длины внутри 0.9fr. Пустой остаток дорожки
      прибавляется к gap, и каждый стык получает свой, отличный от соседнего, просвет.

      Лечится не подбором долей, а тем, что первые две дорожки делаются РОВНО по содержимому:
      340px — та самая ширина формы, max-content — длина самой длинной ссылки. Тогда после каждой
      колонки остаётся только сам gap, один и тот же. Остаток забирают «Контакты» — им это кстати,
      там разворачивается абзац с текстом.

      Только с 1024px: на планшете 340px + ссылки + два отступа уже не оставляют «Контактам»
      осмысленной ширины, поэтому там прежние доли (см. брейкпоинт 768px) остаются как есть.
    */
    .site-footer__grid {
        /* Первая дорожка была жёсткие 340px — ширина формы, а не ширина её содержимого. Подписи
           под галочками в неё не помещались и переносились на вторую строку, из-за чего кнопка
           «Подписаться» опускалась ниже последней ссылки соседней колонки. Теперь дорожка сама
           берёт ширину по самой длинной строке согласия (см. nowrap ниже), и колонки снова
           заканчиваются на одном уровне. */
        grid-template-columns: max-content max-content 1fr;
        /* Шире, чем --space-7 на брейкпоинте 768px: там доли дорожек сами добавляли к стыку свой
           остаток, и просветы казались больше, чем заданный отступ. Теперь дорожки ровно по
           содержимому, отступ виден как есть — и трёх рем стало мало, колонки читались как одна
           сплошная простыня. */
        gap: var(--space-8);
    }

    /* Обе подписи — в одну строку каждая. Ограничения ширины, которые держали форму в 340px,
       здесь сняты: дорожка теперь сама подстраивается под содержимое, и поле ввода с кнопкой
       растягиваются на ту же ширину, что и строки согласий.

       Только с 1024px: ниже дорожки заданы долями (1.1fr / 0.9fr / 1.4fr), и неразрывная строка
       в 390px там не поместилась бы в свою колонку — вылезла бы за край и растянула страницу
       горизонтальной прокруткой. На узких экранах перенос на вторую строку — правильное поведение. */
    .site-footer__newsletter .newsletter-form,
    .site-footer__newsletter .newsletter-form__consents {
        max-width: none;
    }

    .site-footer__newsletter .newsletter-consent {
        white-space: nowrap;
    }

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

       Поэтому кнопка прижимается к НИЗУ своей колонки: ячейки грида по умолчанию
       растягиваются на высоту самой высокой в строке, а margin-top: auto отдаёт кнопке
       весь остаток. Цепочка height/flex ниже нужна, чтобы эта высота дошла от ячейки
       грида до самой формы — без неё промежуточные обёртки схлопываются по содержимому
       и прижимать кнопку становится не к чему.

       Побочный эффект: просвет между галочками и кнопкой становится на несколько
       пикселей больше. Это осознанный размен — выровненный низ колонок виден сразу,
       а лишние пять пикселей внутри одной колонки не видит никто. */
    .site-footer__brand,
    .site-footer__newsletter {
        height: 100%;
    }

    .site-footer__newsletter .newsletter-block,
    .site-footer__newsletter .newsletter-form {
        flex: 1;
    }

    .site-footer__newsletter .newsletter-form__submit {
        margin-top: auto;
    }
}

/* --------------------------------------------------------------------------
   Free-shipping nudge toast (cart page): pops up near the bottom of the viewport when the
   cart subtotal is close to the 7 000 ₽ free-delivery threshold. Slides/fades in via the
   .is-visible class that cart.js toggles; `hidden` keeps it fully out until then.
   -------------------------------------------------------------------------- */
.free-ship-toast {
    position: fixed;
    left: 50%;
    bottom: var(--space-5);
    z-index: 60;
    display: flex;
    align-items: center;
    gap: var(--space-3);
    max-width: min(92vw, 440px);
    padding: var(--space-3) var(--space-3) var(--space-3) var(--space-5);
    background: var(--color-old-burgundy, #442D2A);
    color: var(--color-white);
    border-radius: 999px;
    box-shadow: 0 12px 32px rgba(30, 30, 30, 0.28);
    transform: translate(-50%, calc(100% + var(--space-6)));
    opacity: 0;
    transition: transform 0.45s cubic-bezier(0.2, 0.7, 0.3, 1), opacity 0.45s ease;
}

/* An author `display:flex` beats the UA `[hidden]{display:none}` on a same-specificity tie,
   so `hidden` alone would NOT hide the toast (same gotcha as .favorites-empty[hidden]). */
.free-ship-toast[hidden] {
    display: none;
}

.free-ship-toast.is-visible {
    transform: translate(-50%, 0);
    opacity: 1;
}

.free-ship-toast__text {
    font-size: 0.95rem;
    line-height: 1.35;
}

.free-ship-toast__close {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 28px;
    height: 28px;
    border-radius: 50%;
    font-size: 1.1rem;
    line-height: 1;
    color: var(--color-white);
    background: rgba(255, 255, 255, 0.16);
    transition: background-color 0.15s ease;
}

.free-ship-toast__close:hover {
    background: rgba(255, 255, 255, 0.28);
}

@media (prefers-reduced-motion: reduce) {
    .free-ship-toast {
        transition: opacity 0.2s ease;
        transform: translate(-50%, 0);
    }
}

/* ============================================================================
   Moments wall (/moments) — a curated pinboard of customer photos, styled as
   scattered polaroids in a masonry column layout.
   ============================================================================ */
.moments-intro {
    text-align: center;
    max-width: 640px;
    padding-top: var(--space-8);
    padding-bottom: var(--space-6);
}

.moments-intro__title {
    font-weight: 400;
    font-size: clamp(2rem, 1.4rem + 3vw, 3.4rem);
    letter-spacing: -0.02em;
    margin: var(--space-2) 0 var(--space-3);
}

.moments-intro__lede {
    color: var(--color-text-muted);
}

.moments-wall-wrap {
    padding-inline: clamp(var(--space-4), 2.5vw, var(--space-7));
    padding-bottom: var(--space-8);
}

.moments-wall {
    column-count: 2;
    column-gap: var(--space-5);
    max-width: 1200px;
    margin-inline: auto;
}

@media (min-width: 640px) { .moments-wall { column-count: 3; } }
@media (min-width: 980px) { .moments-wall { column-count: 4; } }

/* Each moment is a polaroid pinned to the board: white frame, caption in the thicker bottom band,
   a small tilt that straightens on hover. */
.moment {
    break-inside: avoid;
    margin: 0 0 var(--space-6);
    background: var(--color-surface);
    padding: var(--space-2) var(--space-2) var(--space-3);
    border-radius: 3px;
    box-shadow: 0 10px 26px rgba(30, 30, 30, 0.14);
    transform: rotate(var(--tilt, 0deg));
    transition: transform 0.3s ease, box-shadow 0.3s ease;
}

.moment:nth-child(4n+1) { --tilt: -2.5deg; }
.moment:nth-child(4n+2) { --tilt: 1.8deg; }
.moment:nth-child(4n+3) { --tilt: -1.2deg; }
.moment:nth-child(4n)   { --tilt: 2.6deg; }

.moment:hover {
    transform: rotate(0deg) scale(1.03);
    box-shadow: 0 18px 40px rgba(30, 30, 30, 0.2);
    position: relative;
    z-index: 2;
}

.moment__photo {
    overflow: hidden;
    border-radius: 2px;
    background: var(--color-bg-soft);
}

.moment__photo img {
    display: block;
    width: 100%;
    height: auto;
}

.moment__caption {
    display: flex;
    flex-direction: column;
    gap: 2px;
    padding: var(--space-2) var(--space-1) var(--space-1);
    text-align: center;
}

.moment__text {
    font-family: var(--font-heading);
    font-weight: 400;
    font-size: 0.9rem;
}

.moment__author {
    font-size: 0.78rem;
    color: var(--color-text-muted);
    letter-spacing: 0.02em;
}

.moments-cta {
    text-align: center;
    max-width: 620px;
    margin-block: var(--space-7) var(--space-8);
    background: var(--color-bg-tint-blue);
    border-radius: var(--radius-md);
    padding: var(--space-7) var(--space-6);
}

.moments-cta__title {
    font-weight: 400;
    margin-bottom: var(--space-3);
}

.moments-cta p {
    color: var(--color-text-muted);
    margin-bottom: var(--space-5);
}

/* ============================================================================
   Slide-in cart drawer (fragments/cart-drawer.html, static/js/cart-drawer.js).
   ============================================================================ */
/* NB: no `visibility: hidden` toggle here on purpose — WebKit won't run/apply the panel's transform
   transition while an ancestor is visibility:hidden, which left the panel stuck off-screen. Instead
   the container stays rendered but clips the off-screen panel (overflow:hidden, so no h-scroll) and
   ignores pointer events until open; the backdrop (opacity 0) and off-screen panel stay invisible. */
.cart-drawer {
    position: fixed;
    inset: 0;
    z-index: 1200;
    overflow: hidden;
    pointer-events: none;
}

.cart-drawer.is-open {
    pointer-events: auto;
}

.cart-drawer__backdrop {
    position: absolute;
    inset: 0;
    background: rgba(30, 30, 30, 0.45);
    opacity: 0;
    transition: opacity 0.3s ease;
}

.cart-drawer.is-open .cart-drawer__backdrop {
    opacity: 1;
}

.cart-drawer__panel {
    position: absolute;
    /* Панель начинается там же, где шапка: под лентой «бесплатная доставка», а после
       прокрутки — от самого верха. Число считает cart-drawer.js при открытии. */
    top: var(--шторка-сверху, 0px);
    right: 0;
    bottom: 0;
    width: min(440px, 94vw);
    display: flex;
    flex-direction: column;
    background: var(--color-surface);
    box-shadow: -12px 0 40px rgba(30, 30, 30, 0.18);
    transform: translateX(100%);
    transition: transform 0.32s cubic-bezier(0.4, 0, 0.2, 1);
}

.cart-drawer.is-open .cart-drawer__panel {
    transform: translateX(0);
}

/*
  ВЕРХНЯЯ ПЛАШКА ШТОРКИ КОНЧАЕТСЯ ТАМ ЖЕ, ГДЕ ПЛАШКА ШАПКИ.

  Владелица навела курсор ровно на этот стык 12.10.2026. Цвет у них и был один — тот же
  бежевый, — а высота разная: у шапки 89 px (а с лентой «бесплатная доставка» над ней низ
  уезжает ещё ниже), у шторки 57. Шторка открывается поверх страницы, и рядом оказывались
  две бежевые полосы, кончающиеся на разной высоте, — глаз цепляется сразу.

  Высоту считает скрипт в момент открытия: он знает, где на самом деле кончается шапка
  (с лентой или без, прокручена страница или нет), и кладёт это число в --высота-шапки.
  Запасное значение — обычная высота шапки, если скрипт почему-то не отработал.
*/
.cart-drawer__head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    box-sizing: border-box;
    min-height: var(--высота-шапки, var(--header-height));
    padding: var(--space-4) var(--space-5);
    border-bottom: 1px solid var(--color-border);
    background: var(--color-beige);
}

.cart-drawer__title {
    font-weight: 400;
    font-size: 1.2rem;
    margin: 0;          /* убираем унаследованный от h2 нижний отступ — иначе заголовок уезжает вверх */
    line-height: 1;     /* выравниваем по центру с крестиком закрытия */
}

.cart-drawer__close {
    background: none;
    border: none;
    cursor: pointer;
    font-size: 1.5rem;
    line-height: 1;
    color: var(--color-text-muted);
}

.cart-drawer__close:hover {
    color: var(--color-text);
}

.cart-drawer__body {
    flex: 1;
    overflow-y: auto;
}

.cart-drawer__empty {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--space-4);
    padding: var(--space-8) var(--space-5);
    text-align: center;
    color: var(--color-text-muted);
}

.cart-drawer__list {
    list-style: none;
    margin: 0;
    padding: 0;
}

.cart-drawer__item {
    display: grid;
    grid-template-columns: 88px 1fr auto;
    gap: var(--space-4);
    align-items: start;
    padding: var(--space-5);
    border-bottom: 1px solid var(--color-border);
}

.cart-drawer__thumb img {
    width: 88px;
    height: 112px;
    object-fit: cover;
    border-radius: var(--radius-sm);
    background: var(--color-bg-soft);
}

.cart-drawer__info {
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 2px;
}

.cart-drawer__name {
    font-family: var(--font-heading);
    font-weight: 400;
    font-size: 1.02rem;
    line-height: 1.35;
}

.cart-drawer__variant {
    font-size: 0.85rem;
    color: var(--color-text-muted);
}

.cart-drawer__line {
    display: flex;
    gap: var(--space-3);
    align-items: baseline;
    margin-top: var(--space-1);
    font-size: 0.85rem;
}

.cart-drawer__price {
    font-weight: 600;
    color: var(--color-accent-dark);
}

.cart-drawer__remove {
    background: none;
    border: none;
    cursor: pointer;
    padding: var(--space-1);
    font-size: 0.9rem;
    color: var(--color-text-muted);
}

.cart-drawer__remove:hover {
    color: var(--color-text);
}

.cart-drawer__summary {
    /* Раньше было position:sticky; bottom:0 — на невысоком экране (телефон) сводка прилипала к низу
       и НАЕЗЖАЛА на товар/промокод («съезжало друг на друга»). Теперь обычный поток: всё
       прокручивается в теле шторки единым столбцом, без наложений. */
    padding: var(--space-5);
    border-top: 1px solid var(--color-border);
    background: var(--color-surface);
}

.cart-drawer__total {
    display: flex;
    justify-content: space-between;
    margin-bottom: var(--space-2);
    font-family: var(--font-heading);
    font-size: 1.05rem;
}

.cart-drawer__note {
    margin-bottom: var(--space-4);
    font-size: 0.8rem;
    color: var(--color-text-muted);
}

.cart-drawer__checkout {
    width: 100%;
    margin-bottom: var(--space-3);
}

.cart-drawer__full-link {
    display: block;
    text-align: center;
    font-size: 0.85rem;
    color: var(--color-text-muted);
}

/* --- Полноценная корзина в шторке: единичная цена, степпер, промокод, наличие, итоги --- */
.cart-drawer__unit {
    font-size: 0.72rem;
    color: var(--color-text-muted);
}
.cart-drawer__controls {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-3);
    margin-top: var(--space-3);
}
.quantity-stepper--sm .quantity-stepper__btn {
    width: 28px;
    height: 28px;
    font-size: 0.95rem;
}
.quantity-stepper--sm .quantity-stepper__val {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 30px;
    height: 28px;
    border-left: 1px solid var(--color-border);
    border-right: 1px solid var(--color-border);
    font-size: 0.85rem;
}
.cart-drawer__price--struck {
    margin-top: var(--space-2);
}
.cart-drawer__item--unavailable { opacity: 0.9; }
.cart-drawer__stock-badge {
    color: #B24A2E;
    font-weight: 600;
    font-size: 0.8rem;
    margin-top: 4px;
}
.cart-drawer__pick-size {
    color: #B24A2E;
    font-size: 0.8rem;
    text-decoration: underline;
    text-underline-offset: 2px;
}
.cart-drawer__pick-size:hover { text-decoration: none; }

.cart-drawer__promo {
    /* Одинаковый отступ сверху и снизу — поле промокода ровно по центру между двумя линиями. */
    padding: var(--space-5) var(--space-5);
}
/* Сетка, а не flex: сообщение об ошибке ложится во вторую строку ПЕРВОГО столбца, то есть точно
   под поле ввода. Во flex оно было соседом всего блока и центрировалось вместе с кнопкой —
   визуально уезжало вправо. */
.cart-drawer__promo-form {
    display: grid;
    grid-template-columns: 1fr auto;
    gap: var(--space-2);
    align-items: start;
}
.cart-drawer__promo-input {
    flex: 1;
    min-width: 0;
    padding: 10px 12px;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm);
    font-size: 0.9rem;
}
.cart-drawer__promo-apply { white-space: nowrap; }
.cart-drawer__promo-applied {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-3);
    padding: 8px 12px;
    border-radius: var(--radius-sm);
    background: #eaf3ea;
    color: #2e6b3e;
    font-size: 0.85rem;
    flex-wrap: wrap;
}
.cart-drawer__promo-remove {
    background: none;
    border: none;
    cursor: pointer;
    color: #2e6b3e;
    text-decoration: underline;
    font-size: 0.8rem;
}
.cart-drawer__promo-error {
    grid-column: 1;
    margin-top: 0;
    color: #B24A2E;
    font-size: 0.8rem;
    /* По центру относительно поля с промокодом: короткая строка, прижатая к левому краю широкого
       блока, выглядит как случайно оставшийся текст, а не как ответ на нажатие «Применить». */
    text-align: center;
}
.cart-drawer__promo-error[hidden] { display: none; }

.cart-drawer__row {
    display: flex;
    justify-content: space-between;
    font-size: 0.9rem;
    margin-bottom: 4px;
    color: var(--color-text-muted);
}
.cart-drawer__row--total {
    margin: var(--space-2) 0;
    font-family: var(--font-heading);
    font-size: 1.05rem;
    color: var(--color-text);
}
.cart-drawer__stock-note {
    margin-bottom: var(--space-3);
    color: #B24A2E;
    font-size: 0.8rem;
    line-height: 1.4;
}
.cart-drawer__checkout[disabled] {
    opacity: 0.5;
    cursor: not-allowed;
}

/* На мобильном шторка = полноценная корзина во весь экран. */
@media (max-width: 600px) {
    .cart-drawer__panel { width: 100vw; }
    .cart-drawer__item { padding: var(--space-3) var(--space-4); }
    .cart-drawer__promo { padding: var(--space-3) var(--space-4) 0; }
    .cart-drawer__summary { padding: var(--space-4); }
}

/* ============================================================================
   Moments strip (home page): a loomknits-style horizontal, arrow-scrollable
   row of the same customer polaroids shown in full on /moments.
   ============================================================================ */
/* Full-bleed strip: spans the whole viewport width (edge to edge), only a small responsive gutter
   on the heading and on the first/last photo keeps them off the very edge. */
.moments-strip {
    /* margin-top overrides the generic `section + section` gap; kept small so the top gap (hero →
       heading) roughly matches the bottom gap (photos → footer). */
    margin-top: var(--space-5);
    padding-block: var(--space-4) var(--space-5);
}

.moments-strip__head {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
    margin-bottom: var(--space-6);
    padding-inline: clamp(var(--space-4), 2.5vw, var(--space-7));
}

.moments-strip__head h2 {
    font-weight: 600;
    font-size: clamp(1.6rem, 1.2rem + 1.4vw, 2.4rem);
    letter-spacing: -0.01em;
}

.moments-strip__subtitle {
    /* Wide enough that each <br/>-separated sentence stays on one line (two lines total). */
    max-width: 64rem;
    color: var(--color-text-muted);
    line-height: 1.55;
}

.moments-strip__all {
    color: var(--color-text-muted);
    font-size: 0.9rem;
    white-space: nowrap;
}

.moments-strip__all:hover {
    color: var(--color-text);
}

.moments-strip__viewport {
    position: relative;
}

.moments-strip__track {
    display: flex;
    gap: var(--space-4);
    list-style: none;
    margin: 0;
    padding: var(--space-2) clamp(var(--space-4), 2.5vw, var(--space-7)) var(--space-3);
    overflow-x: auto;
    scroll-behavior: smooth;
    scroll-snap-type: x mandatory;
    scrollbar-width: none;
    -ms-overflow-style: none;
}

.moments-strip__track::-webkit-scrollbar {
    display: none;
}

.moments-strip__item {
    flex: 0 0 auto;
    width: clamp(200px, 44vw, 280px);
    scroll-snap-align: start;
}

/* Upright polaroids in the strip - the wall's random tilt would look messy in a neat row. */
.moments-strip .moment {
    margin: 0;
    transform: none;
}

/* Uniform card height: crop every photo to the same 4:5 frame so the row lines up neatly. */
.moments-strip .moment__photo {
    aspect-ratio: 4 / 5;
}

.moments-strip .moment__photo img {
    width: 100%;
    height: 100%;
    object-fit: cover;
}

.moments-strip .moment:hover {
    transform: translateY(-4px);
}

.moments-strip__arrow {
    position: absolute;
    top: 42%;
    transform: translateY(-50%);
    z-index: 2;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 44px;
    height: 44px;
    border-radius: 50%;
    background: var(--color-surface);
    color: var(--color-text);
    border: 1px solid var(--color-border);
    box-shadow: 0 6px 18px rgba(30, 30, 30, 0.12);
    cursor: pointer;
    transition: background 0.15s ease, opacity 0.15s ease;
}

.moments-strip__arrow:hover {
    background: var(--color-bg-soft);
}

.moments-strip__arrow--prev {
    left: var(--space-2);
}

.moments-strip__arrow--next {
    right: var(--space-2);
}

.moments-strip__arrow:disabled {
    opacity: 0.35;
    cursor: default;
}

@media (hover: none) {
    .moments-strip__arrow {
        display: none;   /* touch devices swipe the row instead */
    }
}

/* ============================================================================
   Admin dashboard extras: status breakdown chips + status badges + table link.
   ============================================================================ */
.admin-status-grid {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-3);
}

.admin-status-chip {
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-width: 120px;
    padding: var(--space-3) var(--space-4);
    border: 1px solid var(--color-border);
    border-left: 3px solid var(--color-border);
    border-radius: var(--radius-sm);
    background: var(--color-surface);
    transition: background 0.15s ease;
}

.admin-status-chip:hover {
    background: var(--color-bg-soft);
}

.admin-status-chip__label {
    font-size: 0.85rem;
    color: var(--color-text-muted);
}

.admin-status-chip__count {
    font-family: var(--font-heading);
    font-size: 1.4rem;
}

.admin-status-chip--new       { border-left-color: var(--color-pastel-blue); }
.admin-status-chip--confirmed { border-left-color: var(--color-camel); }
.admin-status-chip--shipped   { border-left-color: var(--color-payne-gray); }
.admin-status-chip--delivered { border-left-color: #6b8f71; }
.admin-status-chip--cancelled { border-left-color: #c0504d; }

.admin-status-badge {
    display: inline-block;
    padding: 2px 10px;
    border-radius: 999px;
    font-size: 0.8rem;
    background: var(--color-bg-soft);
    border: 1px solid var(--color-border);
}

/* Раньше все метки были одинаково серыми, и «Новый» ничем не отличался от «Отправлен» — цвет
   несла только полоса слева. Теперь метка повторяет цвет этапа: взгляд ловит её и в столбце
   «Статус», и когда список отфильтрован и полос почти не видно. */
/* СТАТУС В СТРОКЕ — ЗНАЧОК, КОТОРЫЙ МОЖНО ПЕРЕКЛЮЧИТЬ. Выглядит ровно как прежний значок
   (цвета берутся его же классами .admin-status-badge--*), но это настоящий выпадающий список:
   раньше статус меняли либо открыв заказ, либо галочкой и кнопкой наверху, а меняют его часто
   и по одному. Двух элементов рядом быть не должно — значок и список повторяли одно слово. */
.admin-row-status {
    max-width: 10.5rem;
    padding: 2px 10px;
    font: inherit;
    font-size: 0.8rem;
    line-height: 1.5;
    cursor: pointer;
    background: var(--color-bg-soft);
    border: 1px solid var(--color-border);
    border-radius: 999px;
}
.admin-row-status:hover { filter: brightness(0.97); }
.admin-row-status:focus-visible { outline: 2px solid var(--color-accent-dark); outline-offset: 2px; }

/* Цветные классы .admin-status-badge--* идут НИЖЕ этого блока намеренно: у них та же
   вескость, и побеждает тот, кто записан позже. Если перенести этот блок в конец файла,
   все статусы станут одного цвета. */

.admin-status-badge--new       { background: rgba(26, 127, 55, 0.16);  border-color: rgba(26, 127, 55, 0.35);  color: #14622b; }
.admin-status-badge--confirmed { background: rgba(70, 104, 90, 0.16);  border-color: rgba(70, 104, 90, 0.35);  color: #33513f; }
.admin-status-badge--shipped   { background: rgba(98, 114, 127, 0.16); border-color: rgba(98, 114, 127, 0.35); color: #47545f; }
.admin-status-badge--delivered { background: rgba(177, 152, 112, 0.20); border-color: rgba(177, 152, 112, 0.45); color: #7a6338; }
.admin-status-badge--cancelled { background: rgba(192, 80, 77, 0.14);  border-color: rgba(192, 80, 77, 0.35);  color: #a3403d; }

.admin-table__link {
    color: var(--color-accent-dark);
    font-weight: 500;
}

/* Admin order-list filter: search field grows, export button sits to the right. */
.admin-filter {
    flex-wrap: wrap;
    align-items: flex-end;
}

.admin-filter__field--search {
    flex: 1 1 240px;
}

.admin-filter__export {
    margin-left: auto;
}

/* Orders-per-day bar chart (dashboard) - pure CSS bars, no external chart lib. */
.admin-chart {
    display: flex;
    align-items: flex-end;
    gap: var(--space-1);
    padding-top: var(--space-5);
}

.admin-chart__col {
    flex: 1;
    min-width: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 6px;
}

.admin-chart__area {
    height: 150px;
    width: 100%;
    display: flex;
    align-items: flex-end;
    justify-content: center;
}

.admin-chart__bar {
    position: relative;
    width: 70%;
    max-width: 26px;
    min-height: 3px;
    background: var(--color-pastel-blue);
    border-radius: 3px 3px 0 0;
}

.admin-chart__count {
    position: absolute;
    top: -16px;
    left: 50%;
    transform: translateX(-50%);
    font-size: 0.7rem;
    color: var(--color-text-muted);
}

.admin-chart__label {
    font-size: 0.66rem;
    color: var(--color-text-muted);
    white-space: nowrap;
}

/* ============================================================================
   Admin: bulk-status bar, order-detail head, and print styles.
   ============================================================================ */
.admin-bulk-bar {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: var(--space-3);
    margin-bottom: var(--space-4);
    padding: var(--space-3) var(--space-4);
    background: var(--color-bg-soft);
    border-radius: var(--radius-sm);
    font-size: 0.9rem;
}

.admin-bulk-bar__select {
    min-width: 160px;
}

.admin-table__check {
    width: 40px;
    text-align: center;
}

.admin-order-detail__head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-4);
    flex-wrap: wrap;
}

/* Print an order cleanly: drop all site/admin chrome and anything marked .no-print. */
@media print {
    .marquee,
    .site-header,
    .site-footer,
    .cookie-consent,
    .welcome-modal,
    .brand-intro,
    .admin-nav,
    .free-ship-toast,
    .cart-drawer,
    .no-print {
        display: none !important;
    }

    body {
        background: var(--color-surface);
        color: #000;
    }

    .admin-shell,
    .admin-shell__body,
    .container {
        max-width: none;
        margin: 0;
        padding: 0;
    }

    .admin-panel {
        border: none;
        box-shadow: none;
        padding: 0 0 10px;
        break-inside: avoid;
    }

    a {
        color: #000;
        text-decoration: none;
    }
}

/* ============================================================================
   Sticky mobile buy bar on the product page (appears when the main add button
   scrolls out of view).
   ============================================================================ */
.pdp-sticky {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 100;
    display: none;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-3);
    padding: var(--space-3) var(--space-4);
    background: var(--color-surface);
    border-top: 1px solid var(--color-border);
    box-shadow: 0 -6px 20px rgba(30, 30, 30, 0.08);
}

.pdp-sticky[hidden] {
    display: none;
}

@media (max-width: 740px) {
    .pdp-sticky:not([hidden]) {
        display: flex;
    }
}

.pdp-sticky__info {
    display: flex;
    flex-direction: column;
    min-width: 0;
}

.pdp-sticky__name {
    font-size: 0.82rem;
    color: var(--color-text-muted);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.pdp-sticky__price {
    font-family: var(--font-heading);
    font-weight: 600;
}

.pdp-sticky__btn {
    flex: 0 0 auto;
}

/* --------------------------------------------------------------------------
   Админка: что появилось с прошлого захода.

   Зелёная полоса слева, а не заливка всей строки: заливка спорит с колонкой статуса заказа
   (там свои цветные метки) и через десяток строк превращает таблицу в светофор. Полоса читается
   так же быстро, но не мешает читать сами данные. Подсветка держится до следующего открытия
   раздела — отметка о просмотре ставится в момент, когда страница открылась.
   -------------------------------------------------------------------------- */

.admin-row--new > td:first-child {
    box-shadow: inset 5px 0 0 0 #1a7f37;
}

.admin-row--new > td {
    background: rgba(26, 127, 55, 0.13);
}

/* --- ЦВЕТ СТРОКИ ЗАКАЗА = ЧТО ОН ЖДЁТ ОТ ВЛАДЕЛИЦЫ ------------------------------------------
   Не «какой статус», а «чем заняться». Полоса слева и очень слабая заливка: цвет должен
   подсказывать, а не кричать — в списке из тридцати строк яркая заливка мешает читать.

   янтарный  — денег нет, заказ трогать нельзя;
   зелёный   — оплачен, надо собрать и отправить (единственное, что требует рук);
   синий     — уехал, ждём перевозчика, делать нечего;
   песочный  — вручён, но идёт срок возврата: заказ ещё может вернуться;
   без цвета — вручён больше семи дней назад, всё закончилось (решение владелицы: сделанное
               не должно мозолить глаза);
   серый     — отменён.
   Класс ставится по AdminOrderController.stage(): логика в коде, а не в разметке. */
.admin-row--unpaid > td:first-child   { box-shadow: inset 5px 0 0 0 #8a6d00; }
.admin-row--unpaid > td               { background: rgba(138, 109, 0, 0.15); }
.admin-row--pack > td:first-child     { box-shadow: inset 5px 0 0 0 #46685a; }
.admin-row--pack > td                 { background: rgba(70, 104, 90, 0.14); }
.admin-row--transit > td:first-child  { box-shadow: inset 5px 0 0 0 #62727f; }
.admin-row--transit > td              { background: rgba(98, 114, 127, 0.13); }
.admin-row--window > td:first-child   { box-shadow: inset 5px 0 0 0 #b19870; }
.admin-row--window > td               { background: rgba(177, 152, 112, 0.18); }
.admin-row--cancelled > td            { color: var(--color-text-muted); }
.admin-row--cancelled > td:first-child { box-shadow: inset 5px 0 0 0 #c9c9bd; }
/* Заказ, который и новый, и ждёт оплаты: зелёная пометка «новый» не должна перебивать янтарную
   полосу «денег нет» — иначе к нему потянутся собирать. Побеждает то, что важнее. */
.admin-row--unpaid.admin-row--new > td:first-child { box-shadow: inset 5px 0 0 0 #8a6d00; }
.admin-row--unpaid.admin-row--new > td { background: rgba(138, 109, 0, 0.15); }

/* Метки в ячейках: оплата, заявка на возврат. */
.admin-chip {
    display: inline-block;
    padding: 1px 8px;
    border-radius: 999px;
    font-size: 0.74rem;
    font-weight: 600;
    white-space: nowrap;
}
.admin-chip--paid   { background: rgba(70, 104, 90, 0.14); color: #33513f; }
.admin-chip--unpaid { background: rgba(138, 109, 0, 0.16); color: #6b5400; }
.admin-chip--return { background: rgba(178, 74, 46, 0.12); color: #8c3a24; text-decoration: none; }
.admin-chip--return:hover { background: rgba(178, 74, 46, 0.2); }
.admin-muted { color: var(--color-text-muted); }

/* Номер заказа — в одну строку и по центру столбца. Раньше «SV-2026-100001» переносился
   пополам и читался как два разных числа.

   СЕЛЕКТОР ИЗ ДВУХ ЧАСТЕЙ ОБЯЗАТЕЛЕН: у `.admin-table th, .admin-table td` стоит
   `text-align: left`, а это класс ВМЕСТЕ С ТЕГОМ — он весомее одного класса, и центрирование
   по `.admin-col-number` молча не срабатывало. Ровно та же ловушка, что была со скрытой
   подсказкой про брак. */
.admin-table .admin-col-number {
    white-space: nowrap;
}
.admin-table td.admin-col-number { min-width: 9.5rem; }

/* Дата и время — тоже одной строкой: «07.09.2026» и «12:40» на разных строках читались
   как два разных поля, и строка таблицы становилась выше без всякой пользы. */
.admin-table .admin-col-date { white-space: nowrap; }

/* «согласие есть,» и «не подтверждена» — двумя строками, а не одной длинной. */
/* Сумма не переносится: «3 000» и «₽» разъезжались по строкам, знак рубля уезжал вниз. */
.admin-table .admin-col-money { white-space: nowrap; }
.admin-table td.admin-col-money { min-width: 7rem; }

.admin-table .admin-col-newsletter { white-space: nowrap; }
.admin-table .admin-col-newsletter span { display: block; }

/* Остаток по размерам в списке товаров. Столбиком, а не строкой через точку: размеров бывает
   шесть-восемь, в строку они переносятся и перестают читаться как таблица. Цифра выровнена
   вправо — так взгляд сравнивает количества между собой, а не ищет их в разных местах. */
/* ОСТАТОК — ДВА СТОЛБЦА, А НЕ ТРИ РАЗНЫХ ВЫРАВНИВАНИЯ. Подписи (расцветка, размер, «Всего»)
   по левому краю, числа по правому, и всё это в блоке ограниченной ширины: враспор на всю
   клетку между подписью и числом зияла пустота в полколонки, а глазу нужно, чтобы число стояло
   рядом со своей подписью. Клетка выровнена влево — центрирование тут только мешало. */
.admin-col-stock { white-space: nowrap; text-align: left; }
.admin-col-stock > * { max-width: 15rem; }

.admin-stock-total {
    display: flex;
    justify-content: space-between;
    gap: 0.9rem;
    align-items: baseline;
}
.admin-stock-total span { color: var(--color-text-muted); font-size: 0.82rem; }
.admin-stock-total strong { font-variant-numeric: tabular-nums; }
.admin-sizes { list-style: none; margin: 0.35rem 0 0; padding: 0; font-size: 0.82rem; }
.admin-sizes li {
    display: flex;
    justify-content: space-between;
    gap: 0.9rem;
    color: var(--color-text-muted);
    line-height: 1.5;
}
.admin-sizes b { font-variant-numeric: tabular-nums; font-weight: 500; color: var(--color-text); }
/* Закончившийся размер — не «нет такого», а «допечатать». Поэтому он заметен, а не спрятан. */
/* Экран «Заявка принята» — по центру, как все короткие сообщения на сайте: подтверждение заказа,
   отписка от рассылки, 404. Человек пришёл сюда за одним ответом, и читать его в колонку слева,
   как статью, незачем. */
/* Селектор из двух классов намеренно: у .legal-page есть `p { text-align: justify }` — класс плюс
   тег, и одиночный .return-done его не перебивал. Блок стоял по центру, а текст внутри растягивался
   по ширине, и экран не выглядел центрированным. Владелица это и увидела. */
.legal-page .return-done,
.legal-page .return-done p,
.legal-page .return-done h2 {
    text-align: center;
}

.return-done { max-width: 34rem; margin-left: auto; margin-right: auto; }
.return-done p { margin-left: auto; margin-right: auto; }
.return-done__action { margin-top: var(--space-5); }

/* Плашка о новом заказе. Снизу справа, а не сверху: сверху она перекрыла бы навигацию админки,
   а внизу справа — пустое место на любом экране. Фиксированная, чтобы не уезжала при прокрутке
   длинного списка заказов. */
.admin-notify {
    position: fixed;
    right: 1rem;
    bottom: 1rem;
    z-index: 60;
    display: grid;
    gap: 0.5rem;
    max-width: min(22rem, calc(100vw - 2rem));
    padding: 0.85rem 2.2rem 0.85rem 1rem;
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: 12px;
    box-shadow: 0 6px 24px rgba(46, 61, 72, 0.16);
}

.admin-notify__item {
    display: grid;
    gap: 0.1rem;
    text-decoration: none;
    color: var(--color-text);
    font-size: 0.95rem;
}

.admin-notify__item span { font-size: 0.85rem; color: var(--color-text-muted); }
.admin-notify__item:hover b { text-decoration: underline; }

.admin-notify__close {
    position: absolute;
    top: 0.3rem;
    right: 0.45rem;
    border: none;
    background: none;
    font-size: 1.2rem;
    line-height: 1;
    color: var(--color-text-muted);
    cursor: pointer;
    padding: 0.2rem 0.35rem;
}

/* Журнал заказа: время — моноширинным и фиксированной ширины, чтобы события выстроились
   столбиком и взгляд шёл по одной вертикали, а не искал начало каждой строки заново. */
.admin-journal { list-style: none; margin: 0; padding: 0; }
.admin-journal li {
    display: grid;
    grid-template-columns: 9.5rem 1fr auto;
    gap: 0.9rem;
    padding: 0.45rem 0;
    border-bottom: 1px solid var(--color-border);
    font-size: 0.92rem;
}
.admin-journal li:last-child { border-bottom: none; }
.admin-journal time {
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: 0.85rem;
    color: var(--color-text-muted);
    white-space: nowrap;
}
/* Кто сделал — приглушённо и справа: в большинстве строк это одно и то же, и спорить за внимание
   с самим событием ему незачем. */
.admin-journal em {
    font-style: normal;
    font-size: 0.82rem;
    color: var(--color-text-muted);
    white-space: nowrap;
}
@media (max-width: 640px) {
    .admin-journal li { grid-template-columns: 1fr; gap: 0.15rem; }
}

/* Резерв — приглушённо и мельче: это уточнение к цифре остатка, а не вторая цифра. */
.admin-sizes__res {
    margin-left: 0.4rem;
    font-size: 0.85em;
    font-weight: 400;
    color: var(--color-camel);
    white-space: nowrap;
}

.admin-sizes__out { color: var(--color-danger); }
.admin-sizes__out b { color: var(--color-danger); }

/* IP согласия: моноширинный, чтобы адреса читались столбиком, и помельче — это служебная
   колонка, она не должна перетягивать внимание с почты. */
.admin-table .admin-col-ip {
    white-space: nowrap;
    font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
    font-size: 0.82em;
    color: var(--color-text-muted, #6b6b6b);
}

/* СТОЛБЕЦ ДЕЙСТВИЙ В ЗАЯВКАХ. Кнопки лежали отдельными формами подряд, и у форм свои отступы:
   визуально «Обмен отправила» и «Не в товарном виде» наезжали друг на друга, а ширина у них
   была разная — по длине надписи. Теперь это колонка с зазором, и кнопки одной ширины: глаз
   читает их как один список действий, а не как случайно упавшие рядом элементы. */
.admin-actions-cell {
    min-width: 13rem;
}
/* ЯЧЕЙКУ НЕ ДЕЛАЕМ FLEX-КОНТЕЙНЕРОМ: `display:flex` на <td> отменяет табличное поведение —
   ячейка перестаёт растягиваться на всю высоту строки, и справа появляется белая проплешина.
   Поэтому раскладку задаём содержимому: формы блоками с отступом, кнопки во всю ширину. */
.admin-table td.admin-actions-cell form { margin: 0 0 8px; }
.admin-table td.admin-actions-cell form:last-child { margin-bottom: 0; }
.admin-table td.admin-actions-cell .btn {
    display: block;
    width: 100%;
    white-space: normal;
}

/* Заголовок-ссылка = сортировка по столбцу. */
.admin-sort {
    color: inherit;
    text-decoration: none;
    border-bottom: 1px dashed var(--color-border);
    white-space: nowrap;
}
.admin-sort:hover { border-bottom-color: var(--color-payne-gray); }
.admin-sort.is-on { border-bottom-style: solid; border-bottom-color: var(--color-payne-gray); }

/* Быстрые фильтры «чего заказ ждёт»: те же цвета, что у полосы слева у строки. */
.admin-stages {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    margin: 0 0 var(--space-4);
}
.admin-stage {
    display: inline-flex;
    align-items: baseline;
    gap: 6px;
    padding: 6px 12px;
    border: 1px solid var(--color-border);
    border-radius: 999px;
    background: var(--color-surface);
    color: var(--color-text);
    font-size: 0.85rem;
    text-decoration: none;
    white-space: nowrap;
}
.admin-stage span {
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    color: var(--color-text-muted);
}
.admin-stage:hover { border-color: var(--color-payne-gray); }
.admin-stage.is-on { border-width: 2px; padding: 5px 11px; }
.admin-stage--unpaid.is-on  { border-color: #8a6d00; background: rgba(138, 109, 0, 0.08); }
.admin-stage--pack.is-on    { border-color: #46685a; background: rgba(70, 104, 90, 0.08); }
.admin-stage--transit.is-on { border-color: #62727f; background: rgba(98, 114, 127, 0.08); }
.admin-stage--window.is-on  { border-color: #b19870; background: rgba(177, 152, 112, 0.10); }
.admin-stage--all.is-on     { border-color: var(--color-text); }
/* У отменённого заказа метка «не оплачен» — уже не новость: приглушаем, чтобы она не тянула
   взгляд на строку, с которой делать нечего. */
.admin-row--cancelled .admin-chip { opacity: 0.5; }

/* Заявки на возврат: цвет строки — по типу заявки (брак / обмен / деньги). */
.admin-row--defect > td:first-child   { box-shadow: inset 5px 0 0 0 #b24a2e; }
.admin-row--defect > td               { background: rgba(178, 74, 46, 0.14); }
.admin-row--exchange > td:first-child { box-shadow: inset 5px 0 0 0 #8a6d00; }
.admin-row--exchange > td             { background: rgba(138, 109, 0, 0.15); }
.admin-row--money > td:first-child    { box-shadow: inset 5px 0 0 0 #b19870; }
.admin-row--money > td                { background: rgba(177, 152, 112, 0.18); }
.admin-chip--defect { background: rgba(178, 74, 46, 0.16); color: #8c3a24; }
.admin-chip--photos { background: rgba(98, 114, 127, 0.14); color: #4a5763; }

/* Миниатюра товара в списке. Фиксированный размер, чтобы строки не прыгали по высоте. */
.admin-thumb { width: 56px; }
.admin-thumb img {
    width: 48px;
    height: 60px;
    object-fit: cover;
    border-radius: var(--radius-sm);
    border: 1px solid var(--color-border);
    background: var(--color-bg-soft);
    display: block;
    margin: 0 auto;            /* картинка — блок, и без этого она липнет к левому краю ячейки */
}
/* Суммы и даты не переносим: «3 550 ₽», разорванное на две строки, читается как две цифры. */
.admin-table td:nth-last-child(2) { white-space: nowrap; }

.admin-new-badge {
    display: inline-block;
    margin-left: 6px;
    padding: 1px 7px;
    border-radius: 999px;
    background: #1a7f37;
    color: #fff;
    font-size: 0.7rem;
    font-weight: 600;
    letter-spacing: 0.02em;
    vertical-align: middle;
}

/* Кнопка необратимого действия в админке (удаление заказов). Красная не для красоты: рядом стоит
   «Применить», и обе нажимаются одним движением — цвет здесь единственное, что успевает
   предупредить руку. */
.btn--danger {
    background: #b3261e;
    color: #fff;
    border-color: #b3261e;
}

.btn--danger:hover {
    background: #8c1d18;
    border-color: #8c1d18;
}

.admin-bulk-bar__delete {
    margin-left: auto;
}

/* --------------------------------------------------------------------------
   Таблица вариантов в карточке товара: колонка артикула шире остальных.
   Артикул вида SV-DTM-001-104110 — 18 символов, а колонка делила ширину поровну
   с «Размером» и обрезала его на середине; владелице приходилось щёлкать в поле
   и прокручивать текст, чтобы прочитать целиком.
   -------------------------------------------------------------------------- */

.admin-col--sku {
    min-width: 13rem;
}

/* Подпись под заголовком колонки — поясняет необязательное поле, не занимая ширины. */
.admin-th-hint {
    display: block;
    font-size: 0.72rem;
    font-weight: 400;
    color: var(--color-text-muted);
    letter-spacing: 0;
    text-transform: none;
}

/* --------------------------------------------------------------------------
   Выравнивание в админке.
   -------------------------------------------------------------------------- */

/* Заголовки таблицы вариантов — по ВЕРХНЕМУ краю. У «Своей цены» под названием есть пояснение,
   ячейка из-за него выше остальных, и при выравнивании по центру (общее правило .admin-table)
   все прочие названия провисали вниз относительно неё. */
.admin-table--form th {
    vertical-align: top;
}

/* Фильтр заказов: подписи «Статус» и «Поиск» — на одной линии, поля под ними — тоже.
   Расходились не блоки, а сами контролы: <select> и <input type="search"> считают
   собственную высоту по-разному, и этой разницы в пару пикселей хватало, чтобы подпись
   над более высоким полем уехала вверх. Выравнивание по нижнему краю тут не спасает — оно
   как раз и переносит всю разницу наверх, к подписям. Задаём обоим полям одну высоту —
   ту же, что у кнопок рядом, — и линия становится общей для всей строки фильтра. */
.admin-filter .field__input {
    height: 2.8rem;
}

/* Подпись — всегда в одну строку: перенос опустил бы поле под ней ниже соседнего. */
.admin-filter__field .field__label {
    white-space: nowrap;
}


/* --------------------------------------------------------------------------
   Заявка на возврат (/legal/return-request)
   -------------------------------------------------------------------------- */

/* Форму намеренно собираем из уже существующих .field / .field__input / .btn — она должна
   выглядеть ровно как оформление заказа, а не как отдельный бланк на чужом сайте. Здесь только
   то, чего в общих классах нет. */
.return-request-form {
    display: flex;
    flex-direction: column;
    gap: var(--space-4);
    max-width: 620px;
    margin-top: var(--space-5);
}

/* Ловушка для роботов. Не display:none и не hidden: часть автоматов такие поля пропускает
   осознанно. Поле остаётся в потоке, но уводится за пределы экрана — для человека его нет,
   для наивного бота оно обычное. */
.return-request-form__trap {
    position: absolute;
    left: -9999px;
    width: 1px;
    height: 1px;
    overflow: hidden;
}

.return-request-form__choice {
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    padding: var(--space-4);
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
}

.return-request-form__choice legend {
    padding: 0 var(--space-2);
}

.return-request-form__choice label {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    cursor: pointer;
}

.return-request-form__details {
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    padding: var(--space-3) var(--space-4);
}

.return-request-form__details summary {
    cursor: pointer;
    font-weight: 500;
}

.return-request-form__details[open] summary {
    margin-bottom: var(--space-3);
}

.return-request-form__consent {
    display: flex;
    align-items: flex-start;
    gap: var(--space-2);
    line-height: 1.6;
}

.return-request-form__consent input {
    margin-top: 0.25em;
    flex: none;
}

/* Подсказка в свёрнутом блоке реквизитов прижималась вплотную к первому полю — читалась как его
   подпись, а не как условие для всего блока. */
.return-request-form__details-hint {
    margin-bottom: var(--space-4);
}

/* Поля внутри details идут той же лесенкой, что и в основной форме. */
.return-request-form__details .field + .field {
    margin-top: var(--space-4);
}

/* Подпись над отключённым списком размеров гаснет вместе с ним: активная строка над серым полем
   читается как сбой вёрстки, а не как «здесь пока нечего выбирать». */
.field__label.is-disabled {
    opacity: 0.5;
}

/* Одной прозрачности мало: поле со своим фоном и рамкой всё равно читалось как рабочее. Гасим
   тремя признаками сразу — приглушённый текст, притопленный фон и пунктирная рамка. Пунктир здесь
   несёт смысл: сплошная рамка у соседних полей означает «сюда можно писать». */
.return-request-form__choice select:disabled {
    color: var(--color-text-muted);
    background-color: var(--color-bg-soft);
    border-style: dashed;
    opacity: 0.75;
    cursor: not-allowed;
}

/* Позиции заказа в заявке на возврат: человек узнаёт свою вещь по фото, а не вспоминает название. */
.return-items { display: flex; flex-direction: column; gap: var(--space-2); }

/*
  ДВЕ СТРОКИ, А НЕ ДВА СТОЛБЦА. Сначала было flex в одну линию — и колонка выбора занимала то,
  что осталось от названия: у «Футболки» переключатели стояли в одном месте, у «Лонгслива
  с контрастными швами» уезжали вправо и ломались на две строки. Фиксированная колонка это
  выровняла, но зажимала и название, и размеры.

  Теперь вещь читается сверху вниз, как её и рассказывают: первая строка — фотография, название
  и размер; вторая — что с ней сделать, во всю ширину. Одинаково у всех позиций независимо
  от длины названия, и размеры для обмена умещаются в один ряд.
*/
.return-item {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    padding: var(--space-3);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    cursor: pointer;
}

/* Галочка, миниатюра и название — одной строкой. */
.return-item > label {
    display: flex;
    align-items: center;
    gap: var(--space-3);
}

.return-item:has(input:checked) {
    border-color: var(--color-accent-dark);
    background: var(--color-bg-soft);
}

.return-item__img {
    width: 56px;
    height: 70px;
    object-fit: cover;
    border-radius: var(--radius-sm);
    flex: none;
}

.return-item__text { line-height: 1.5; }

/* Размеры для обмена. Отсутствующий размер не прячем, а показываем зачёркнутым: человек должен
   видеть, что размер существует, но кончился, — иначе он решит, что ошибся сайтом. */
.return-sizes { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); margin-top: var(--space-3); }
/* Подпись начинается там же, где первый размер: 12 px — боковое поле «таблетки», то есть её
   прямая часть, а не скруглённый край. Иначе строка висела левее ряда и читалась отдельно
   от него. */
.return-sizes .field__label { flex-basis: 100%; margin: 0 0 var(--space-1) 12px; }

.return-size {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 6px 12px;
    border: 1px solid var(--color-border);
    border-radius: 999px;
    cursor: pointer;
}

.return-size:has(input:checked) {
    border-color: var(--color-accent-dark);
    background: var(--color-bg-soft);
}

.return-size--out {
    opacity: 0.55;
    cursor: not-allowed;
    text-decoration: line-through;
    border-style: dashed;
}

/* РАЗМЕР, КОТОРЫЙ У ЧЕЛОВЕКА УЖЕ ЕСТЬ. Виден в ряду, но не выбирается: по нему человек
   опознаёт свой ряд размеров. Не зачёркнут — он не кончился, он просто свой; отличается
   приглушённостью и подписью «Ваш». На нажатие отвечает движением (класс sv-nope из
   общего правила «так нельзя») и красной строкой под рядом. */
.return-size--current {
    opacity: 0.7;
    cursor: not-allowed;
    border-style: dashed;
    position: relative;
}

.return-size--current::after {
    content: 'Ваш';
    position: absolute;
    top: -0.55rem;
    right: -0.35rem;
    padding: 0 0.3rem;
    border-radius: 999px;
    background: var(--color-bg-soft);
    border: 1px solid var(--color-border);
    font-size: 0.6rem;
    letter-spacing: 0.02em;
    color: var(--color-text-muted);
}

.return-sizes__error {
    flex-basis: 100%;
    margin: var(--space-2) 0 0;
}

/* Атрибут hidden обязан побеждать display из классов. Классическая ловушка: .return-sizes задаёт
   display:flex, .btn — inline-flex, и элемент с hidden всё равно остаётся на экране. Скрипт формы
   возврата полагается именно на hidden, поэтому здесь он усилен точечно. */
.return-sizes[hidden],
#return-find[hidden] {
    display: none;
}

/* Пояснение в скобках прямо в подписи: «Номер заказа (он есть в письме о заказе)». Размер и тон
   у него прежние, подписи не принадлежит — поэтому вес сбрасываем обратно. */
.field__label .field__hint--inline {
    display: inline;
    margin: 0;
    font-weight: 400;
    font-size: 0.8rem;
    color: var(--color-text-muted);
}

/* Подпись внутри свёрнутого блока реквизитов — того же веса, что и остальные подписи. */
.return-request-form__details-hint {
    color: #4A5866;
}

/* ССЫЛКИ В ТЕКСТЕ ПОДЧЁРКНУТЫ. Глобально у ссылок на сайте `text-decoration: none` и цвет
   наследуется — в меню и в подвале это правильно, там ссылка опознаётся по месту. Но в абзаце
   она становится невидимой: владелица не заметила, что «Заполните заявку на возврат» — ссылка.

   Подчёркивание здесь не украшение, а требование доступности: отличать ссылку одним цветом
   нельзя, дальтоник его не увидит. Линия работает всегда.

   Подчёркивание отодвинуто от текста и сделано тонким, чтобы не спорить с кириллическими
   выносными элементами (у «р», «у», «д» они уходят вниз и упираются в линию). */
.legal-page__section p a,
.legal-page__section li a,
.legal-page__note a,
.field__hint a {
    text-decoration: underline;
    text-decoration-thickness: 1px;
    text-underline-offset: 0.22em;
    text-decoration-color: rgba(31, 41, 51, 0.35);
}

.legal-page__section p a:hover,
.legal-page__section li a:hover,
.legal-page__note a:hover,
.field__hint a:hover {
    text-decoration-color: currentColor;
}

/* ==========================================================================
   Общий поиск по админке + период в списке заказов
   ========================================================================== */

/* Поле поиска стоит в шапке, справа от ссылки «На сайт»: оно нужно на каждой странице,
   поэтому едет вместе с навигацией, а не с содержимым раздела. На узком экране переносится
   на свою строку целиком — иначе кнопка «Найти» отрывалась от поля. */
.admin-nav__search {
    display: flex;
    align-items: center;
    gap: 6px;
    flex: 0 1 320px;
    margin-left: var(--space-4);
}

/* Подпись нужна тем, кто читает страницу голосом, но на экране её место занимает подсказка
   в самом поле — две подписи подряд выглядели бы ошибкой вёрстки. */
.admin-nav__search-label {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
}

.admin-nav__search-input {
    flex: 1 1 auto;
    min-width: 0;
    padding: 7px 12px;
    border: 1px solid var(--color-border);
    border-radius: 999px;
    background: var(--color-surface);
    color: var(--color-text);
    font-family: inherit;
    font-size: 0.9rem;
}

.admin-nav__search-input:focus-visible {
    outline: 2px solid var(--color-payne-gray);
    outline-offset: 1px;
}

.admin-nav__search-btn {
    padding: 7px 14px;
    border: 1px solid var(--color-border);
    border-radius: 999px;
    background: var(--color-surface);
    color: var(--color-text);
    font-family: inherit;
    font-size: 0.85rem;
    cursor: pointer;
}

.admin-nav__search-btn:hover {
    border-color: var(--color-payne-gray);
}

@media (max-width: 720px) {
    .admin-nav__search {
        flex: 1 1 100%;
        margin-left: 0;
    }
}

/* Страница результатов */
.admin-search-page__query {
    margin: 0;
    color: var(--color-text-muted);
}

.admin-search-page__empty {
    margin: 0;
    color: var(--color-text-muted);
    max-width: 60ch;
}

.admin-search-group__title {
    display: flex;
    align-items: baseline;
    gap: 8px;
    margin: 0 0 var(--space-3);
    font-size: 1.05rem;
}

.admin-search-group__count {
    font-size: 0.8rem;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    color: var(--color-text-muted);
}

.admin-search-hits {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 2px;
}

/* Вся строка — ссылка, а не только заголовок: попасть мышью в две строки проще, чем в одну,
   и это единственное действие, которое со строкой вообще можно сделать. */
.admin-search-hit__link {
    display: flex;
    flex-direction: column;
    gap: 2px;
    padding: 10px 12px;
    border: 1px solid transparent;
    border-radius: var(--radius-sm, 6px);
    background: var(--color-surface);
    text-decoration: none;
    color: var(--color-text);
}

.admin-search-hit__link:hover {
    border-color: var(--color-pastel-blue);
}

.admin-search-hit__title {
    font-weight: 600;
}

.admin-search-hit__sub {
    font-size: 0.85rem;
    color: var(--color-text-muted);
}

.admin-search-group__more {
    margin: var(--space-2) 0 0;
    font-size: 0.85rem;
}

/* Период в списке заказов */
.admin-filter__field--date {
    flex: 0 0 auto;
}

.admin-periods {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 12px;
    margin: 0 0 var(--space-4);
    font-size: 0.85rem;
}

.admin-periods__label {
    color: var(--color-text-muted);
}

/* Сумма уезжает вправо и набрана моноширинными цифрами: её читают, а не сравнивают
   с соседними ссылками. */
.admin-periods__sum {
    margin-left: auto;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
}

/* Вещь заказа, который ещё в пути: показываем, но не даём распоряжаться. Без галочки слева
   строка провисала бы в пустоте — поэтому своя раскладка, а не погашенная обычная. */
.return-item--readonly {
    opacity: 0.85;
}

.return-item--readonly .return-item__head {
    display: flex;
    align-items: center;
    gap: 12px;
}

.admin-search-results {
    display: flex;
    flex-direction: column;
    gap: var(--space-5);
}

/* --------------------------------------------------------------------------
   Ошибка под строкой с галочкой
   -------------------------------------------------------------------------- */

/* ОШИБКА НИКОГДА НЕ ВЫРАВНИВАЕТСЯ ПО ШИРИНЕ. На правовых страницах абзацы набраны по ширине
   (.legal-page p), и красная строка ошибки попадала под то же правило: короткая фраза
   растягивалась на всю ширину колонки, между словами появлялись дыры, и на телефоне это читалось
   как сломанная вёрстка. Ошибку читают быстро и целиком — ей нужен ровный левый край. */
/* Селектор из двух классов — иначе правило проигрывает `.legal-page p` по силе и красная
   строка на правовых страницах остаётся растянутой по ширине. */
.field__error,
.legal-page .field__error,
.legal-page p.field__error {
    text-align: left;
}

/* Ошибка под согласием встаёт под ТЕКСТОМ, а не под самой галочкой. Раньше она начиналась
   у левого края блока — то есть левее квадратика галочки, на несколько пикселей выступая
   за него, — и выглядела вывалившейся из строки, к которой относится. */
.return-request-form__consent + .field__error {
    padding-left: calc(1em + var(--space-2));
}

.checkout-checkbox + .field__error {
    padding-left: calc(1em + var(--space-3));
}

/* Подсказка «что отмечать» над таблицей возврата. Не предупреждение и не ошибка — правило,
   по которому уже расставлены галочки, поэтому спокойный фон и обычный текст. */
.admin-hint {
    border: 1px solid var(--color-border);
    border-left: 3px solid var(--color-pastel-blue);
    border-radius: var(--radius-sm, 6px);
    background: var(--color-surface);
    padding: var(--space-3) var(--space-4);
    margin-bottom: var(--space-4);
    font-size: 0.9rem;
}

.admin-hint__title {
    margin: 0 0 6px;
    font-weight: 700;
}

.admin-hint ul {
    margin: 0;
    padding-left: 18px;
    display: flex;
    flex-direction: column;
    gap: 4px;
}

/* ==========================================================================
   Админка на телефоне: таблицы становятся карточками
   ==========================================================================

   ПОЧЕМУ ЭТО ВАЖНО. Владелица работает с телефона, а список заказов шире экрана на 700 с лишним
   пикселей: статус, сумма и «Открыть» уезжали за правый край, и добраться до них можно было
   только горизонтальной прокруткой — вслепую, потому что шапка таблицы при этом уже уехала
   и непонятно, какой столбец читаешь.

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

@media (max-width: 760px) {

    .admin-table,
    .admin-table tbody,
    .admin-table tr,
    .admin-table td {
        display: block;
        width: auto;
    }

    /* Шапка не нужна: подписи теперь в самих ячейках. Убираем её доступно для скринридеров —
       через clip, а не display:none. */
    .admin-table thead {
        position: absolute;
        width: 1px;
        height: 1px;
        overflow: hidden;
        clip-path: inset(50%);
    }

    .admin-table tr {
        border: 1px solid var(--color-border);
        border-radius: var(--radius-sm, 8px);
        background: var(--color-surface);
        padding: 10px 12px;
        margin-bottom: 10px;
    }

    /* Цветная полоса «чего заказ ждёт» остаётся — на телефоне она и подсказывает, что делать. */
    .admin-table tr[class*="admin-row--"] {
        border-left-width: 4px;
    }

    .admin-table td {
        display: flex;
        justify-content: space-between;
        align-items: baseline;
        gap: 12px;
        padding: 4px 0;
        border: 0;
        text-align: right;
    }

    .admin-table td::before {
        content: attr(data-label);
        flex: 0 0 auto;
        color: var(--color-text-muted);
        font-size: 0.8rem;
        text-align: left;
    }

    /* Ячейки без подписи — галочка выбора, столбец действий — занимают всю ширину:
       у них нет пары «подпись — значение». */
    .admin-table td:not([data-label]) {
        justify-content: flex-start;
        text-align: left;
    }

    .admin-table__action,
    .admin-table__actions {
        margin-top: 6px;
        padding-top: 8px;
        border-top: 1px dashed var(--color-border);
    }

    /* Номер заказа — заголовок карточки: крупнее остального и без подписи слева. */
    .admin-table td.admin-col-number {
        font-weight: 700;
        font-size: 1.05rem;
    }

    .admin-table td.admin-col-number::before {
        content: "";
    }

    .admin-thumb img {
        max-width: 64px;
        height: auto;
    }

    /* Навигация по разделам: на телефоне ссылки стояли в одну строку мелким кеглем и переносились
       как попало — промахнуться пальцем было проще, чем попасть. Теперь это сетка крупных целей. */
    .admin-nav {
        display: grid;
        grid-template-columns: repeat(2, 1fr);
        gap: 6px;
    }

    .admin-nav__link {
        padding: 10px 12px;
        border: 1px solid var(--color-border);
        border-radius: var(--radius-sm, 8px);
        background: var(--color-surface);
        text-align: center;
        font-size: 0.9rem;
    }

    .admin-nav__link--back {
        margin-left: 0;
    }

    .admin-nav__search {
        grid-column: 1 / -1;
        margin-left: 0;
    }

    /* Фильтры в столбик: поле поиска и даты на телефоне ужимались до нечитаемых. */
    .admin-filter__field,
    .admin-filter__field--search,
    .admin-filter__field--date {
        flex: 1 1 100%;
    }

    .admin-filter__export {
        margin-left: 0;
    }

    .admin-periods__sum {
        margin-left: 0;
        flex: 1 1 100%;
    }
}

@media (max-width: 760px) {
    /* ЦЕЛИ ПОД ПАЛЕЦ. Галочка выбора была 13 пикселей, ссылки периодов — 22: в такие попадают
       через раз, а промах по галочке в списке заказов означает массовое действие не над тем
       заказом. Apple советует не меньше 44 пунктов, здесь берём разумный минимум. */
    .admin-table input[type="checkbox"],
    .admin-bulk-bar input[type="checkbox"] {
        width: 22px;
        height: 22px;
    }

    .admin-periods a {
        display: inline-block;
        padding: 8px 12px;
        border: 1px solid var(--color-border);
        border-radius: 999px;
        background: var(--color-surface);
        text-decoration: none;
    }

    .admin-table__link,
    .admin-table__actions a,
    .admin-table__actions button {
        display: inline-block;
        padding: 8px 0;
    }

    /* Шапка таблицы на телефоне не нужна совсем: подписи теперь внутри ячеек. */
    .admin-table thead {
        display: none;
    }
}

/* ==========================================================================
   «Требует внимания» — список дел на дашборде
   ========================================================================== */

.admin-attention {
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm, 8px);
    background: var(--color-surface);
    padding: var(--space-4);
}

.admin-attention__title {
    margin: 0 0 var(--space-3);
    font-size: 1.1rem;
}

.admin-attention__calm {
    margin: 0;
    color: var(--color-text-muted);
}

.admin-attention__list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 8px;
}

/* Полоса слева — степень срочности. Цвет тот же, что у полос в списке заказов, чтобы «срочное»
   выглядело одинаково во всей админке. */
.admin-attention__item {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    padding: 10px 12px;
    border: 1px solid var(--color-border);
    border-left: 4px solid var(--color-pastel-blue);
    border-radius: var(--radius-sm, 8px);
    background: var(--color-bg);
}

.admin-attention__item--urgent { border-left-color: #B24A2E; }
.admin-attention__item--soon   { border-left-color: #8a6d00; }
.admin-attention__item--calm   { border-left-color: var(--color-camel); }

.admin-attention__count {
    flex: 0 0 auto;
    min-width: 2.2em;
    text-align: center;
    font-size: 1.3rem;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
}

.admin-attention__text {
    display: flex;
    flex-direction: column;
    gap: 2px;
    flex: 1 1 auto;
}

.admin-attention__text span {
    font-size: 0.85rem;
    color: var(--color-text-muted);
}

.admin-attention__go {
    flex: 0 0 auto;
    padding: 8px 14px;
    border: 1px solid var(--color-border);
    border-radius: 999px;
    background: var(--color-surface);
    color: var(--color-text);
    text-decoration: none;
    font-size: 0.85rem;
    white-space: nowrap;
}

.admin-attention__go:hover {
    border-color: var(--color-payne-gray);
}

@media (max-width: 760px) {
    /* На телефоне кнопка уезжает под текст, а счётчик остаётся слева: так строка читается
       в два касания взгляда, а не в четыре. */
    .admin-attention__item {
        flex-wrap: wrap;
    }

    .admin-attention__go {
        width: 100%;
        text-align: center;
    }
}

.admin-settings-form {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
    max-width: 420px;
}

.admin-settings-todo {
    margin: 0;
    padding-left: 18px;
    display: flex;
    flex-direction: column;
    gap: 6px;
    color: var(--color-text-muted);
    font-size: 0.9rem;
}

/* Остаток по расцветкам в списке товаров: название расцветки — подпись над своей строкой
   размеров, общая цифра по расцветке уходит вправо. */
.admin-colors + .admin-colors {
    margin-top: 8px;
    padding-top: 8px;
    border-top: 1px dashed var(--color-border);
}

.admin-colors__name {
    display: flex;
    justify-content: space-between;
    gap: 10px;
    font-size: 0.8rem;
    color: var(--color-text-muted);
    margin-bottom: 3px;
}

.admin-colors__total {
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

/* Карточка найденного заказа в форме возврата: подтверждение «это ваш заказ», без выбора. */
.return-order-card {
    display: flex;
    flex-direction: column;
    gap: 2px;
    padding: 12px 14px;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm, 8px);
    background: var(--color-surface);
}

.return-order-card__number {
    font-weight: 700;
}

.return-order-card__meta {
    font-size: 0.85rem;
    color: var(--color-text-muted);
}

/* Форма отметки вручения: поле даты и кнопка в одну колонку, не во всю ширину панели. */
.admin-delivered-form {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    align-items: flex-start;
    max-width: 260px;
    margin-top: var(--space-3);
}

/* Правка состава заказа: три действия в одной ячейке — размер, количество, «убрать». */
/* КЛЕТКА ОСТАЁТСЯ КЛЕТКОЙ. Здесь стоял display:flex прямо на <td> — и клетка выпадала из
   раскладки таблицы: её высота переставала совпадать с соседями, а нижняя граница рисовалась
   отдельной чёрточкой на пару пикселей ниже общей линии. Строка выглядела съехавшей.
   Столбиком выстраивается обёртка внутри клетки, а сама клетка ведёт себя как все. */
.admin-item-edit {
    min-width: 150px;
}

.admin-item-edit__stack {
    display: flex;
    flex-direction: column;
    gap: 6px;
    align-items: flex-start;
}

.admin-item-edit select,
.admin-item-edit input[type="number"] {
    padding: 5px 8px;
    font-size: 13px;
    width: auto;
    min-width: 92px;
}

.admin-item-edit__qty {
    display: flex;
    align-items: center;
    gap: 6px;
}

.admin-page__head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-4);
    flex-wrap: wrap;
}

/* Редактор текстов: поле пошире и кнопка рядом с подсказкой. */
.admin-text-area {
    min-height: 140px;
    line-height: 1.6;
    font-family: inherit;
    resize: vertical;
}

.admin-text-actions {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    flex-wrap: wrap;
}

/* Подсказка про необязательные поля адреса: между строками полей, спокойным тоном. */
.checkout-courier__hint {
    margin: 2px 0 10px;
    max-width: 52ch;
}


/*
  ИТОГ В ФОРМЕ ЗАКАЗА ЗА ПОКУПАТЕЛЯ. Сумму называют вслух покупателю у прилавка, поэтому она
  набрана крупно и не сливается с полями формы: цифру ищут глазами за долю секунды.
*/
.admin-total {
    display: flex;
    align-items: baseline;
    flex-wrap: wrap;
    gap: var(--space-2) var(--space-3);
    /* По ширине списка выбранных вещей, который стоит прямо над ней: узкая строка итога
       под широким списком читалась как обрывок чужого блока. */
    width: min(62rem, calc(100vw - 2 * var(--space-5)));
    margin: var(--space-4) 0 var(--space-5);
    padding: var(--space-3) var(--space-4);
    background: var(--color-surface-alt, rgba(0, 0, 0, 0.03));
    border-radius: 8px;
}
.admin-total__label {
    color: var(--color-text-muted);
    font-size: 0.9rem;
}
.admin-total__sum {
    font-size: 1.5rem;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
}
.admin-total__parts {
    color: var(--color-text-muted);
    font-size: 0.88rem;
    font-variant-numeric: tabular-nums;
}

/*
  ССЫЛКА НА ОПЛАТУ. Поле, а не текст: ссылку копируют и отправляют покупателю в мессенджер,
  а выделять длинный адрес мышью по буквам неудобно — по нажатию выделяется целиком.
*/
.admin-paylink {
    margin-top: var(--space-3);
    padding: var(--space-3) var(--space-4);
    border: 1px solid var(--color-border);
    border-radius: 8px;
}
.admin-paylink__title {
    margin: 0 0 var(--space-2);
    font-weight: 600;
}
.admin-paylink__input {
    font-size: 0.9rem;
}
.admin-paylink__hint {
    margin: var(--space-2) 0 0;
    color: var(--color-text-muted);
    font-size: 0.85rem;
}

/* QR на карточке заказа: экран телефона держат перед покупателем, поэтому код крупный
   и на белом — цветная подложка мешает камере читать. */
.admin-paylink__qr { margin-top: var(--space-4); }
.admin-paylink__qr-img {
    width: 220px;
    max-width: 100%;
    padding: 10px;
    background: #fff;
    border-radius: 6px;
}
.admin-paylink__qr-img svg { display: block; width: 100%; height: auto; }

/* Запасной код — приглушённой строкой: он не должен выглядеть равноправным с основной
   кнопкой оплаты, иначе им начнут пользоваться по привычке, теряя автоматическую отметку. */
.admin-paylink__fallback {
    margin: var(--space-2) 0 0;
    font-size: 0.85rem;
    color: var(--color-text-muted);
}


/*
  ПОДПИСЬ ПОЛЯ — НА ОДНОЙ ЛИНИИ СО ЗНАЧЕНИЕМ. Подпись стояла у самой рамки, а текст внутри поля
  отодвинут внутренним отступом на 13 px — и «Имя» оказывалось левее, чем «Покупатель на ярмарке»
  под ним. Две левые линии вместо одной: владелица увидела это на форме заказа за покупателя
  и была права — подпись выглядела вылезшей за ячейку.

  Сдвигаем подпись на ту же величину, и край у подписи со значением становится общим. Столько же
  получают пояснение под полем и сообщение об ошибке — иначе они начнут спорить с подписью.

  Только админка: на сайте формы покупателя выровнены как были, и трогать их ради этого нельзя —
  там своя вёрстка и свои проверенные экраны.
*/
/* ТОЛЬКО ВНУТРИ ПОЛЯ. Отступ нужен, чтобы подпись встала по тексту внутри рамки. Но теми же
   классами набраны и самостоятельные пояснения — под таблицей, под кнопкой, — у которых никакой
   рамки рядом нет: им этот отступ давал сдвиг вправо, и соседние абзацы в одной панели шли
   по разным вертикалям. */
.admin-shell .field .field__label,
.admin-shell .field .field__hint,
.admin-shell .field .field__error {
    /* Ровно внутренний отступ поля плюс его рамка — если однажды поменяется --space-3,
       подпись уедет вместе с текстом, а не останется на старом месте. */
    padding-left: calc(var(--space-3) + 1px);
}

/* Галочки и переключатели живут не в .field: у них текст идёт сразу за квадратиком, и сдвиг
   оторвал бы подпись от него. */
.admin-shell .return-request-form__consent .field__label,
.admin-shell .checkout-radio .field__label {
    padding-left: 0;
}


/*
  ===== КАТАЛОГ В ФОРМЕ ЗАКАЗА ЗА ПОКУПАТЕЛЯ =====

  Упрощённый каталог: карточка с фотографией, под ней размеры кнопками — как на странице
  товара у покупателя. Сделан для продажи на ярмарке, где выбирать из выпадающего списка
  долго, а вещь у продавца перед глазами и узнаётся по виду.

  Сетка заполняется по ширине: при узком экране телефона — две колонки, на ноутбуке — четыре.
  Высоту задаёт содержимое, поэтому карточки с разным числом размеров не растягивают соседей.
*/
/*
  КАТАЛОГ ШИРЕ ФОРМЫ. Сама форма нарочно узкая — 520 px: поля ввода длиннее читать неудобно,
  а глазу нужна короткая левая линия. Но каталог в эту ширину не влезал: три тесные карточки,
  и размер «104–110» ломался пополам на двух строках.

  Поэтому каталог выходит за колонку формы вправо, до края страницы. Влево не выходит —
  иначе он оторвался бы от общей левой линии, по которой стоят все подписи полей.
*/
.picker {
    margin-bottom: var(--space-5);
    width: min(62rem, calc(100vw - 2 * var(--space-5)));
}

/* Поиск остаётся по ширине формы: он такое же поле ввода, как «Имя» и «Телефон»,
   и растянутым на всю страницу выбивался бы из ряда. */
.picker__search { max-width: 100%; }

.picker__search { margin-bottom: var(--space-4); }

.picker__grid {
    list-style: none;
    margin: 0;
    padding: 0;
    display: grid;
    /* 12.5rem, а не 11.5: при 11.5 колонка размера выходила уже, чем «104–110», и подпись
       переносилась на вторую строку — кнопка становилась двухэтажной. */
    grid-template-columns: repeat(auto-fill, minmax(12.5rem, 1fr));
    gap: var(--space-4);
    /* Строка ровняется по самой высокой карточке: иначе соседи с названием в две строки
       и в четыре стоят «лесенкой», и ряд кнопок размера у каждой на своей высоте. */
    align-items: stretch;
}

.picker__card {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    padding: var(--space-3);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm);
    background: var(--color-surface);
}

/* Пропорции 4:5 — те же, что у карточки товара в каталоге: фотографии сняты под них,
   и при другом соотношении вещь обрезалась бы не так, как привыкли. */
.picker__img {
    display: block;
    width: 100%;
    aspect-ratio: 4 / 5;
    object-fit: cover;
    border-radius: var(--radius-sm);
    background: var(--color-surface-alt, rgba(0, 0, 0, .04));
}
.picker__img--empty { border: 1px dashed var(--color-border); }

/* Название — не больше двух строк: длинные названия растягивали карточку и ломали ряд.
   Что не поместилось, обрывается многоточием; полное видно в строке «Выбрано» ниже. */
.picker__name {
    margin: 0;
    font-size: 0.9rem;
    line-height: 1.35;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

/* Расцветка — отдельной строкой и мельче названия: приклеенная к нему, она удваивала
   высоту карточки и повторяла слово из названия. */
.picker__color {
    margin: 0;
    font-size: 0.8rem;
    line-height: 1.3;
    color: var(--color-text-muted);
    display: -webkit-box;
    -webkit-line-clamp: 1;
    -webkit-box-orient: vertical;
    overflow: hidden;
}
.picker__price {
    margin: 0;
    font-size: 0.85rem;
    color: var(--color-text-muted);
    font-variant-numeric: tabular-nums;
}

/* Размеры сеткой по две в ряд, а не потоком: у «80–86» и «104–110» разная ширина, и в потоке
   они вставали вразнобой — то по одной в строке, то по две. В сетке колонки равны, и ряды
   кнопок у соседних карточек совпадают по высоте. */
.picker__sizes {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: var(--space-2);
    margin-top: auto;
    padding-top: var(--space-2);
}

/* Кнопка размера — не меньше 44 px по высоте: на ярмарке в неё попадают пальцем,
   часто на ходу и не глядя. */
.picker__size {
    min-height: 2.75rem;
    padding: 0 var(--space-1);
    font: inherit;
    font-size: 0.85rem;
    font-variant-numeric: tabular-nums;
    /* Размер не разрывается: «104–110» на двух строках читается как два разных числа. */
    white-space: nowrap;
    cursor: pointer;
    color: var(--color-text);
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm);
}
.picker__size:hover { border-color: var(--color-accent-dark); }
.picker__size:focus-visible {
    outline: 2px solid var(--color-accent-dark);
    outline-offset: 1px;
}
/* Выбранный размер — заливкой, а не только рамкой: рамку на солнце у прилавка не видно. */
.picker__size--on {
    background: var(--color-accent-dark);
    border-color: var(--color-accent-dark);
    color: #fff;
}

/* --- Выбранные вещи в заказе за покупателя ---------------------------------------------- */
/* Список, а не одна строка: на ярмарке берут несколько вещей, и все они идут в один заказ.
   Каждая строка несёт свои скрытые поля variantId + quantity — форма уходит обычным POST. */
.chosen {
    list-style: none;
    /* Та же ширина, что у каталога над ним: список — продолжение выбора, и в узкой колонке
       формы названия вроде «Штаны с лампасами · зелёный · 104–110» ломались на две строки. */
    width: min(62rem, calc(100vw - 2 * var(--space-5)));
    margin: var(--space-4) 0 0;
    padding: 0;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm);
    overflow: hidden;
}
.chosen[hidden] { display: none; }

/* Название тянется, цена и счётчик прижаты вправо и стоят друг под другом у соседних строк:
   так сумму по заказу видно столбиком, не вчитываясь в названия. */
.chosen__row {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    padding: var(--space-2) var(--space-3);
    background: var(--color-surface);
}
.chosen__row + .chosen__row { border-top: 1px solid var(--color-border); }

.chosen__name { flex: 1 1 auto; font-size: 0.9rem; line-height: 1.35; }
.chosen__price {
    flex: 0 0 auto;
    font-size: 0.9rem;
    color: var(--color-text-muted);
    font-variant-numeric: tabular-nums;
}

.chosen__qty { flex: 0 0 auto; display: flex; align-items: center; gap: var(--space-1); }
.chosen__n {
    min-width: 1.5rem;
    text-align: center;
    font-variant-numeric: tabular-nums;
}

/* Кнопки «−» и «+» не меньше 36 px: на ярмарке по ним попадают пальцем, часто на ходу. */
.chosen__btn {
    min-width: 2.25rem;
    min-height: 2.25rem;
    padding: 0 var(--space-2);
    font: inherit;
    line-height: 1;
    cursor: pointer;
    color: var(--color-text);
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm);
}
.chosen__btn:hover { border-color: var(--color-accent-dark); }

/* «Убрать» — не кнопка-коробка, а подпись: действие редкое и не должно спорить со счётчиком. */
.chosen__remove {
    flex: 0 0 auto;
    min-width: 0;
    min-height: 0;
    padding: var(--space-1) 0;
    border: 0;
    background: none;
    font-size: 0.82rem;
    color: var(--color-text-muted);
    text-decoration: underline;
}
.chosen__remove:hover { color: var(--color-text); }

.picker__empty {
    margin: var(--space-4) 0 0;
    color: var(--color-text-muted);
}

/* Запасной список свёрнут: он нужен редко, а развёрнутым спорил бы с каталогом за внимание. */
.picker__fallback { margin-bottom: var(--space-4); }
.picker__fallback summary {
    cursor: pointer;
    font-size: 0.9rem;
    color: var(--color-text-muted);
    padding: var(--space-2) 0;
}
.picker__fallback[open] summary { margin-bottom: var(--space-2); }

/* --- Отчёт по ярмарке (templates/admin/fair-report.html) ----------------------------------- */
/* Плиток в отчёте пять, а общая раскладка рассчитана на четыре — пятая («Средний чек»)
   оставалась одна во втором ряду и читалась как случайно забытая. Здесь колонки подстраиваются
   под число плиток: их бывает и четыре, когда неоплаченных заказов нет. */
.admin-stats--fair {
    grid-template-columns: repeat(auto-fit, minmax(10.5rem, 1fr));
    margin-bottom: var(--space-5);
}
@media (min-width: 900px) {
    .admin-stats--fair { grid-template-columns: repeat(auto-fit, minmax(10.5rem, 1fr)); }
}

/* Отбивка между разделами отчёта: без неё заголовок «Что разобрали» стоял вплотную к плиткам
   и читался как их подпись. */
.fair-report__cols { margin-bottom: var(--space-5); }
.fair-report__note { margin: var(--space-4) 0 var(--space-5); }

/* Две таблицы рядом, пока хватает ширины: «что разобрали» и «размеры» читают вместе — сколько
   взяли конкретной вещи и каких размеров не хватило. На узком экране встают друг под друга. */
.fair-report__cols {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(22rem, 1fr));
    gap: var(--space-5);
    align-items: start;
}

/* Числа в столбик и одной шириной цифры: суммы сравнивают глазами сверху вниз. */
.fair-report__num {
    text-align: right;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

.fair-report__table td:first-child { line-height: 1.35; }

/* Размеры полосками: доля видна взглядом, точное число стоит рядом — считать в уме не нужно. */
.fair-sizes {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
}
.fair-sizes__row {
    display: grid;
    grid-template-columns: 4.5rem 1fr 4rem 3rem;
    align-items: center;
    gap: var(--space-3);
}
.fair-sizes__name { font-variant-numeric: tabular-nums; white-space: nowrap; }
.fair-sizes__bar {
    height: 0.6rem;
    border-radius: 999px;
    background: var(--color-border);
    overflow: hidden;
}
.fair-sizes__fill {
    display: block;
    height: 100%;
    border-radius: 999px;
    background: var(--color-payne-gray, #4F6272);
}
.fair-sizes__n,
.fair-sizes__pct {
    font-variant-numeric: tabular-nums;
    text-align: right;
    white-space: nowrap;
}
.fair-sizes__pct { color: var(--color-text-muted); font-size: 0.85rem; }

/* Выбранная ярмарка в строке ссылок — как активный период выше по странице. */
.admin-periods a.is-on { font-weight: 600; text-decoration: none; }

@media (max-width: 700px) {
    .fair-sizes__row { grid-template-columns: 4.5rem 1fr 4rem; }
    .fair-sizes__pct { display: none; }
}

/* --- Клиенты: покупатели и подписчики одним списком ---------------------------------------- */
/* Две выгрузки стоят рядом, и подпись объясняет разницу: одна — то, что на экране, вторая
   уходит в сервис рассылок и потому содержит только разрешённые адреса. Перепутать их дороже,
   чем кажется, поэтому подпись здесь, а не в подсказке при наведении. */
.admin-people__exports {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-3);
    margin: var(--space-4) 0 var(--space-5);
}
.admin-people__exports .field__hint { margin: 0; flex: 1 1 16rem; min-width: 12rem; }

.admin-people__name { font-weight: 500; }

/* Состояние переписки — теми же чипами, что и статусы заказов: зелёный значит «можно»,
   серый «ещё нельзя», красный «нельзя и не будет». */
.admin-mail--ok           { background: rgba(26, 127, 55, 0.16);  border-color: rgba(26, 127, 55, 0.35);  color: #14622b; }
.admin-mail--pending      { background: rgba(177, 152, 112, 0.20); border-color: rgba(177, 152, 112, 0.45); color: #7a6338; }
.admin-mail--unsubscribed { background: rgba(192, 80, 77, 0.14);  border-color: rgba(192, 80, 77, 0.35);  color: #a3403d; }
.admin-mail--none         { background: var(--color-bg-soft); border-color: var(--color-border); color: var(--color-text-muted); }

/* Два ряда срезов с подписью слева: подпись называет вопрос, кнопки — ответы. Без подписи
   два ряда кнопок подряд читались бы как один длинный список, ради разделения которого всё
   и затевалось. */
.admin-people__filter {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-3);
    margin-bottom: var(--space-3);
}
.admin-people__filter-label {
    flex: 0 0 4.5rem;
    color: var(--color-text-muted);
    font-size: 0.9rem;
}
.admin-people__filter .admin-stages { margin: 0; }

/* --- Подсказки под поиском в админке ------------------------------------------------------- */
/* Выпадает прямо под полем, как на самом сайте. Форма поиска становится точкой отсчёта,
   поэтому список прилипает к её левому краю и повторяет её ширину, а не расползается
   по всей шапке. */
.admin-nav__search { position: relative; }

.admin-suggest {
    position: absolute;
    top: calc(100% + 6px);
    left: 0;
    right: 0;
    z-index: 40;
    max-height: 60vh;
    overflow-y: auto;
    padding: var(--space-2) 0;
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm);
    box-shadow: 0 12px 28px rgba(0, 0, 0, 0.12);
}
.admin-suggest[hidden] { display: none; }

/* Заголовок раздела — не кликается и потому приглушён: он объясняет, что за строки под ним. */
.admin-suggest__section {
    margin: var(--space-2) 0 2px;
    padding: 0 var(--space-3);
    font-size: 0.72rem;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--color-text-muted);
}
.admin-suggest__section:first-child { margin-top: 0; }

.admin-suggest__item,
.admin-suggest__all {
    display: block;
    padding: var(--space-2) var(--space-3);
    color: var(--color-text);
    text-decoration: none;
    line-height: 1.35;
}
.admin-suggest__item small {
    display: block;
    color: var(--color-text-muted);
    font-size: 0.8rem;
}
.admin-suggest__item:hover,
.admin-suggest__item.is-on,
.admin-suggest__all:hover,
.admin-suggest__all.is-on { background: var(--color-bg-soft); }

.admin-suggest__all {
    margin-top: var(--space-2);
    border-top: 1px solid var(--color-border);
    color: var(--color-accent-dark);
    font-size: 0.85rem;
}
.admin-suggest__empty {
    margin: 0;
    padding: var(--space-2) var(--space-3);
    color: var(--color-text-muted);
    font-size: 0.9rem;
}

/* --- Предупреждение о повторной заявке на возврат ------------------------------------------ */
/* Жёлтое, а не красное: это не ошибка, а вопрос. Красный читался бы как «так нельзя»,
   а повторная заявка по заказу часто совершенно законна. */
.return-duplicate {
    margin: var(--space-4) 0;
    padding: var(--space-3) var(--space-4);
    background: rgba(177, 152, 112, 0.14);
    border: 1px solid rgba(177, 152, 112, 0.45);
    border-radius: var(--radius-sm);
}
.return-duplicate__title { margin: 0 0 var(--space-2); font-weight: 600; }
.return-duplicate__list { margin: 0 0 var(--space-2); padding-left: 1.1rem; }
.return-duplicate__list li { margin-bottom: 0.2rem; }
.return-duplicate__hint {
    margin: 0 0 var(--space-3);
    font-size: 0.9rem;
    color: var(--color-text-muted);
}


/* ============================================================================
   «ТАК НЕЛЬЗЯ» — кнопка отвечает движением, а не текстом.

   Решение владелицы: если в поле количества стоит число, которого нет в наличии, кнопка
   «Добавить в корзину» на нажатие не срабатывает, а дрожит. Выключенная кнопка здесь была бы
   хуже: она молчит, и человек не понимает, из-за чего она серая.

   Поле подсвечивается в тот же момент: иначе дрожит кнопка, а причина в соседнем поле.

   Амплитуда падает к краям (5 → 3 → 2 px), поэтому движение читается как «мотнул головой»,
   а не как сбой отрисовки.
   ============================================================================ */
@keyframes sv-nope {
    10%, 90% { transform: translateX(-2px); }
    20%, 80% { transform: translateX(3px); }
    30%, 50%, 70% { transform: translateX(-5px); }
    40%, 60% { transform: translateX(5px); }
}

.sv-nope {
    /* ТОЛЬКО ДВИЖЕНИЕ. Красную рамку отсюда убрал: у поля количества общей границы нет
       (только левая и правая), и border-color на нём не виден вовсе — получалось, что
       кнопка краснеет, а поле нет. Дрожат оба, и этого достаточно: движение показывает
       и что услышали, и где искать причину. */
    animation: sv-nope 0.45s cubic-bezier(0.36, 0.07, 0.19, 0.97) both;
}

/* Движение выключено у тех, кто просил систему его не показывать (укачивание, вестибулярные
   нарушения). Сигнал остаётся: красная рамка держится, пока класс не снимут. */
/* Три колонки подвала заканчиваются на одном уровне.

   Крайние это уже умеют: кнопка «Подписаться» прижата к низу своей колонки (margin-top: auto
   у .newsletter-form__submit), а панель «Контакты» со сноской про Meta выведена из потока и
   привязана к низу строки. Средняя колонка — список «Покупателям» — просто кончалась там, где
   кончался текст, и её последняя ссылка «Согласие на обработку данных» висела на 13 пикселей
   выше соседей; низ подвала читался рваным.

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

   Только с 1180px. Ниже начинается планшетная полоса, где подписи согласий в форме переносятся
   и левая колонка выше средней на сотню-другую пикселей: там этот остаток размазался бы по
   списку просветами в палец шириной. Выше 1180px он всегда ровно 13 пикселей.

   Селектор с тегом nav намеренно: у соседней колонки «Контакты» тот же класс, и flex на ней
   сбил бы точку отсчёта для её панели, выведенной из потока, — панель уехала бы вверх,
   под заголовок. */
@media (min-width: 1180px) {
    nav.site-footer__col {
        display: flex;
        flex-direction: column;
    }

    nav.site-footer__col > ul {
        flex: 1 1 auto;
        display: flex;
        flex-direction: column;
        justify-content: space-between;
        /*
          ОДИН ПИКСЕЛЬ СВЕРХУ — чтобы буквы встали вровень с буквами, а не коробки с коробками.

          Коробки уже совпадали: и список, и панель «Контакты» кончались на 692. Но внутри
          последней строки остаётся свой просвет под буквами, и он зависит от кегля: у ссылки
          он на пиксель больше, чем у мелкой сноски, поэтому «Согласие на обработку данных»
          сидело на 689, а последняя строка сноски — на 690. Владелица это увидела.

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

          Низ кнопки «Подписаться» при этом на 692, и это нормально: у кнопки залитый
          прямоугольник, его край по определению ниже букв внутри. Равнять букву по краю
          заливки — значит поднять кнопку выше текста.
        */
        position: relative;
        top: 1px;
    }
}

@media (prefers-reduced-motion: reduce) {
    .sv-nope {
        animation: none;
        outline: 2px solid var(--color-danger);
        outline-offset: 1px;
    }
}


/* ============================================================================
   «В КОРЗИНЕ» — ЗАЛИТАЯ КНОПКА, «ДОБАВИТЬ В КОРЗИНУ» — С РАМКОЙ.

   Образец выбрала владелица: loomknits.com. Там кнопка никогда не исчезает, а меняет вид:
   пока товара нет в корзине — она контурная и зовёт нажать; как только он в корзине —
   заливается и сообщает состояние. Одно место на странице, два смысла, и ничего не пропадает.

   Базовый цвет кнопки НЕ меняется: заливка берётся из того же --color-eerie-black, в который
   она и так красится при наведении с 9 июля. Меняется только то, в каком состоянии эта
   заливка показывается.
   ============================================================================ */
.add-to-cart-form__submit[data-go-to-cart="1"] {
    background: var(--color-accent-dark);
    border-color: var(--color-accent-dark);
    color: var(--color-white);
}


/* ============================================================================
   ПАЛЕЦ БОЛЬШЕ КУРСОРА. На телефоне часть кнопок — 24–32 px: значки в шапке, «−»/«+»
   счётчиков, крестики «убрать позицию», закрытие шторки. Мышью в них попадают, пальцем — через
   раз, и промах в корзине стоит дороже всего: человек метит в «+», попадает в крестик.

   Apple рекомендует 44×44 pt, Google 48×48 dp. Увеличиваем ОБЛАСТЬ НАЖАТИЯ, а не сами кнопки:
   прозрачный слой поверх растягивает цель до 44 px, вид и вёрстка не меняются ни на пиксель.

   Только для сенсорного ввода: @media (pointer: coarse) не трогает ни ноутбук, ни десктоп.
   У .header-utility__icon position уже relative — её не переопределяем, остальным ставим.
   ============================================================================ */
@media (pointer: coarse) {
    .quantity-stepper__btn,
    .checkout-summary__qty-btn,
    .cart-line__remove,
    .checkout-summary__line-remove,
    .cart-drawer__remove,
    .cart-drawer__close,
    .free-ship-toast__close,
    .favorite-heart {
        position: relative;
    }

    .header-utility__icon::after,
    .quantity-stepper__btn::after,
    .checkout-summary__qty-btn::after,
    .cart-line__remove::after,
    .checkout-summary__line-remove::after,
    .cart-drawer__remove::after,
    .cart-drawer__close::after,
    .free-ship-toast__close::after,
    .favorite-heart::after {
        content: "";
        position: absolute;
        top: 50%;
        left: 50%;
        transform: translate(-50%, -50%);
        width: max(100%, 44px);
        height: max(100%, 44px);
        /* Слой невидим и не ловит ничего, кроме нажатий: ни фона, ни рамки. */
        background: transparent;
    }
}

/* ============================================================================
   НАПОМИНАНИЕ О ЗАБЫТОЙ КОРЗИНЕ — окно по центру экрана.

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

   Раскладку и фон даёт общий .modal — тот же, что у окна «Спасибо, что заглянули». Здесь
   только внутренности панели: она уже основной (480px широковато для трёх строк) и собрана
   столбиком по центру.
   ============================================================================ */
.cart-reminder__panel {
    width: min(27rem, 100%);
    text-align: center;
    padding: var(--space-6) var(--space-5) var(--space-5);
}

.cart-reminder__eyebrow {
    margin: 0;
    font-size: .75rem;
    letter-spacing: .06em;
    text-transform: uppercase;
    color: var(--color-accent-dark, #62727F);
}

/* «Ура! Вы вернулись!» — приветствие над вопросом. Мельче заголовка и приглушённее:
   это интонация, а не сообщение. */
.cart-reminder__hello {
    margin: 0;
    font-size: .95rem;
    color: var(--color-accent-dark, #62727F);
}

.cart-reminder__title {
    margin: var(--space-2) 0 0;
    font-size: 1.25rem;
    line-height: 1.25;
}

/* Карточка вещи — вся ссылка целиком: по фотографии нажимают охотнее, чем по кнопке. */
.cart-reminder__item {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    margin-top: var(--space-4);
    padding: var(--space-3);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md, 12px);
    background: var(--color-bg-soft);
    text-align: left;
    color: inherit;
    text-decoration: none;
}

.cart-reminder__item:hover { border-color: var(--color-accent-dark, #62727F); }

/* 4:5 — та же пропорция, что у карточек каталога и миниатюр в письмах о заказе. */
.cart-reminder__photo {
    width: 68px;
    height: 85px;
    flex: none;
    object-fit: cover;
    border-radius: var(--radius-sm, 8px);
    background: var(--color-surface);
}

.cart-reminder__text { display: flex; flex-direction: column; gap: 2px; min-width: 0; }

.cart-reminder__name { font-weight: 600; line-height: 1.3; overflow-wrap: anywhere; }

.cart-reminder__size { font-size: .9rem; color: var(--color-text-muted); }

.cart-reminder__price { margin-top: 2px; font-weight: 600; }

.cart-reminder__more {
    margin: var(--space-3) 0 0;
    font-size: .9rem;
    /* Приглушённый, а не --color-footnote: тот в палитре предупреждающе-красный (#B24A2E),
       и строка «И ещё 1 позиция» читалась как ошибка. Поймано на снимке 11.10.2026. */
    color: var(--color-text-muted);
}

/* Число с «позициями» — ссылка в корзину. Подчёркивание обязательно: серая строка
   без него не читается как кликабельная, а именно её и просят нажать. */
.cart-reminder__more-link {
    color: inherit;
    text-decoration: underline;
    text-underline-offset: 3px;
}

.cart-reminder__more-link:hover { color: var(--color-text); }

.cart-reminder__go {
    display: block;
    margin-top: var(--space-4);
    text-align: center;
}

/* «Пока посмотрю дальше» — ссылка, а не вторая кнопка: уйти должно быть легко, но отказ
   не обязан выглядеть таким же весомым решением, как переход в корзину. */
.cart-reminder__later {
    display: inline-block;
    margin-top: var(--space-3);
    border: 0;
    background: none;
    cursor: pointer;
    font: inherit;
    font-size: .9rem;
    color: var(--color-text-muted);
    text-decoration: underline;
    text-underline-offset: 3px;
}

.cart-reminder__later:hover { color: var(--color-text); }
