/* ==========================================================================
   qoody.ru — дизайн-канон клиентского сайта.            27.08.2026, Дизайнер
   Префикс --s-* / .s-* : s = shop. Не путать с --q-* (веб-CRM) и --qm-* (мобайл
   CRM) — это ТРЕТЬЯ система, у неё другой пользователь. В CRM сидит оператор
   восемь часов подряд и ему нужна плотность; сюда человек заходит на четыре
   минуты купить лампы, и ему нужна крупность.

   ТЕМА ПО УМОЛЧАНИЮ — ТЁМНАЯ, и это не вкусовое решение. Замер фотоконтента
   27.08.2026 (первое фото всех 78 позиций каталога, медиана яркости рамки):
       тёмный фон   57 позиций — 73 %
       светлый фон  20 позиций — 26 %  (все 8 предохранителей + часть линз)
       пограничный   1 позиция —  1 %
   Светлая тема сделала бы 57 карточек тёмными плитами на белом. Она в каноне
   есть (`[data-theme="light"]`) и полностью посчитана — но как вариант к
   показу владельцу, а не как база.

   Ориентир «Самокат» взят СТРУКТУРОЙ, а не цветом: крупная карточка, кнопка
   покупки прямо в плитке, поиск на первом экране, checkout одним экраном.
   Светлость к простоте отношения не имеет.
   ========================================================================== */

/* ── ПОВЕРХНОСТИ ───────────────────────────────────────────────────────────
   На тёмной теме слой отделяет КРОМКА, а не заливка. Между холстом и
   карточкой физически не бывает большой разницы: #0A0B0D → #14161A это 1,12,
   и глазом это почти не отличимо (в мобильном каноне CRM тот же приём уже
   ловил меня — заливка card/card-2 как признак выбора не читалась, 1,09).
   Поэтому у карточки обязательна --s-edge, а не только заливка. */
:root{
  --s-bg:#0A0B0D;          /* холст страницы */
  --s-surface:#14161A;     /* карточка, 1,12 к холсту — работает вместе с кромкой */
  --s-surface-2:#1C1F24;   /* вложенный блок: поле поиска, плитка характеристики */
  --s-sunk:#101216;        /* утопленное: подложка галереи, полоса прогресса */
  --s-line:#23262B;        /* разделитель ВНУТРИ карточки */
  --s-edge:#2E323A;        /* кромка карточки к холсту — заметнее разделителя */
  --s-line-ctl:#606974;    /* рамка контрола: 3,19 к карточке — это форма, порог 3,0 */

  --s-ink:#F4F6F7;         /* 16,72 на карточке */
  --s-ink-mid:#A9B0B8;     /*  8,28 */
  --s-ink-soft:#7A828B;    /*  4,66 на карточке, 5,03 на холсте — нижняя граница букв */

  /* Акцент живёт в ТРЁХ ролях, и одним значением они не покрываются — в
     светлой теме циан проходит только заливкой. Роли и пороги:
       --s-accent      ЗАЛИВКА под тёмной буквой. Порог держит буква, не пятно.
       --s-accent-ink  буква НА заливке. 4,5.
       --s-accent-tx   акцентный ТЕКСТ на обычной подложке. 4,5.
       --s-accent-edge ФОРМА без букв: рамка фокуса, точка радио, полоса шага,
                       точка галереи, кромка primary. Порог 3,0.
     В тёмной теме edge и tx совпадают с акцентом; расходятся они в светлой. */
  --s-accent:#57DCFC;      /* фирменный циан бренда. 11,25 на карточке */
  --s-accent-tx:#57DCFC;   /* акцентный ТЕКСТ; в тёмной совпадает с акцентом */
  --s-accent-edge:#57DCFC; /* акцент ФОРМОЙ; 11,25 к карточке, 12,23 к холсту */
  --s-accent-ink:#00181F;  /* буквы НА акценте: 11,34 — тёмные, не белые */
  --s-accent-film:#1C2E35; /* акцент 12 % на карточке; текст акцентом по ней 8,75 */

  --s-pos:#3DDC84;  --s-pos-film:#192E27;   /* доставлен, оплачен, в наличии */
  --s-warn:#FFB020; --s-warn-film:#30281B;  /* ждёт оплаты, мало осталось */
  --s-neg:#FF6B6B;  --s-neg-film:#302024;   /* отменён, возврат, нет в наличии */

  /* Плёнка «товара нет»: гасит фото, не трогая подпись под ним. Значение
     своё в каждой теме — в светлой гасить надо белым, а не чёрным. */
  --s-veil:rgba(10,11,13,.55);

  /* ЧУЖИЕ БРЕНДЫ. Единственные цвета канона, которые НЕ меняются по теме:
     синий VK остаётся синим и на белом фоне, иначе кнопка перестаёт быть
     узнаваемой. Держим их токенами, а не литералами в правиле, чтобы
     значение лежало в одном месте — при смене фирменного цвета сервиса
     править придётся строку, а не искать по файлу. */
  --s-vk:#0070F7;    --s-vk-on:#FFFFFF;   /* 4,51 — фирменный #0077FF затемнён в LCh, ΔE 2,8 */
  --s-tg:#2AABEE;    --s-tg-on:#04283A;   /* 5,96 — цвет нетронут, изменена буква */

  /* Радиусы. Крупнее, чем в CRM (там 8–12): большая карточка с мелким
     радиусом выглядит казённой, а мы за «зашёл и купил». */
  --s-r-1:8px; --s-r-2:12px; --s-r-3:16px; --s-r-4:22px; --s-r-pill:999px;

  /* Тени. На тёмной теме тень почти не видна и служит не глубине, а отрыву
     липких панелей от контента. Больше нигде она не нужна — глубину здесь
     делает кромка. */
  --s-shadow-pop:0 10px 30px rgba(0,0,0,.55);
  --s-shadow-bar:0 -8px 24px rgba(0,0,0,.45);
  --s-shadow-lift:0 1px 2px rgba(0,0,0,.30);  /* отрыв выбранной вкладки сегмента */

  --s-font:'Inter',-apple-system,system-ui,'Segoe UI',sans-serif;
  --s-mono:'JetBrains Mono',ui-monospace,SFMono-Regular,monospace;
  --s-display:'Xolonium','Inter',sans-serif;   /* только wordmark и слоган бренда */

  /* Шкала отступов — та же логика, что в мобильном каноне CRM: величины
     раскладки берутся отсюда и ниоткуда больше. Компенсации (когда число
     вычисляется из высоты контрола) шкале не подчиняются и помечаются
     формулой в комментарии. */
  --s-sp-1:4px;  --s-sp-2:8px;  --s-sp-3:12px; --s-sp-4:16px;
  --s-sp-5:20px; --s-sp-6:24px; --s-sp-7:32px; --s-sp-8:48px; --s-sp-9:64px;

  /* Типографическая шкала. Крупнее CRM на две ступени: цена и название товара
     должны читаться с вытянутой руки в машине, а не с рабочего кресла. */
  --s-fs-1:12px;   /* мета, единицы, подпись под полем */
  --s-fs-2:13px;   /* вторичный текст, характеристики */
  --s-fs-3:15px;   /* основной текст, название товара в карточке */
  --s-fs-4:17px;   /* цена в карточке, заголовок блока в мобайле */
  --s-fs-5:22px;   /* заголовок раздела, цена в карточке товара */
  --s-fs-6:28px;   /* h1 мобайла */
  --s-fs-7:40px;   /* h1 десктопа */

  --s-h-ctl:48px;      /* высота контрола: поле, кнопка. 48, не 44 — палец */
  --s-h-ctl-sm:36px;   /* компактный контрол: чип, кнопка в строке корзины */
  --s-tap:44px;        /* минимальная тач-цель, ниже не опускаемся */
  --s-maxw:1280px;     /* было 1800 — на широком мониторе карточки расползались
                          в ленту по семь в ряд и товар терял вес */
}

/* ── СВЕТЛАЯ ТЕМА ──────────────────────────────────────────────────────────
   Полностью посчитана, но не база — см. шапку. Акцент здесь работает иначе:
   #57DCFC на белом даёт 1,61 и буквами быть не может. Поэтому в светлой теме
   циан живёт ТОЛЬКО заливкой (под тёмными буквами --s-accent-ink, 11,34),
   а там, где нужен акцентный ТЕКСТ, берётся затемнённый в CIELCh
   --s-accent-tx #007694. Затемнение в HSL здесь запрещено — оно уводит
   оттенок (проверено на MAX #FF5E3A в CRM).

   Все буквенные токены светлой темы посчитаны 27.08 заново, от ХУДШЕЙ своей
   подложки, а не от белого. Первая версия мерила всё на #FFFFFF и давала
   ровно 4,5x — а фактически буквы лежат на холсте #F4F5F7, на утопленном
   #EDEFF2 или на своей плёнке, и там те же цвета проваливались до 3,92–4,48.
   Порог считаем на самой тёмной подложке, на какой токен реально живёт:
   ink-soft — на sunk, статусные — на своей плёнке.

   Селектор БЕЗ :root намеренно. С `:root[data-theme="light"]` тема работала бы
   только на теге <html> — а её вешают и на кусок страницы: в витрине канона и
   в раскадровке макетов светлый вариант живёт контейнером рядом с тёмным.
   Поймано при сборке макетов 27.08: блок с data-theme на div молча оставался
   тёмным, потому что селектор не совпадал. */
[data-theme="light"]{
  --s-bg:#F4F5F7; --s-surface:#FFFFFF; --s-surface-2:#F4F5F7; --s-sunk:#EDEFF2;
  --s-line:#E4E7EB; --s-edge:#D8DCE2;
  --s-line-ctl:#838A94;     /* форма: 3,48 / 3,19 / 3,02 — порог 3,0.
                               Было #9AA2AC = 2,58 к белому: рамка поля и
                               кромка secondary-кнопки в светлой теме не
                               добирали до порога форм. Нашёл check_site.py
                               27.08 — в тёмной этот же токен даёт 3,19 и
                               проходит, поэтому глазом дыра не читалась. */
  --s-ink:#0E1013; --s-ink-mid:#565E66; --s-ink-soft:#656D76;  /* 19,05 / 6,59 / 4,55 */
  --s-accent:#57DCFC; --s-accent-ink:#00181F; --s-accent-film:#E7FAFF;
  --s-accent-tx:#007694;    /* 5,23 на карточке · 4,54 на утопленном */
  --s-accent-edge:#007694;  /* форма: 5,23 / 4,79 / 4,54 — порог 3,0 с запасом.
                               Циан формой здесь даёт 1,40–1,61: рамка фокуса
                               пропадала целиком, а это доступность клавиатуры. */
  --s-pos:#008234;  --s-pos-film:#E4FAEE;    /* 4,53 на своей плёнке */
  --s-warn:#A16200; --s-warn-film:#FFF4E0;   /* 4,52 */
  --s-neg:#C63840;  --s-neg-film:#FFEAEA;    /* 4,51 */
  --s-shadow-pop:0 10px 30px rgba(16,20,26,.14);
  --s-shadow-bar:0 -6px 20px rgba(16,20,26,.10);
  --s-shadow-lift:0 1px 2px rgba(16,20,26,.12);  /* на белом .30 чёрного читается грязью */
  --s-veil:rgba(244,245,247,.62);                /* гасим светлым: чёрная плёнка на белой теме даёт грязь */
}
/* --s-accent-tx объявлен в ОБЕИХ темах, каждый своим значением, и оба
   объявления стоят в своих блоках. Отдельной строкой `:root{...}` его писать
   нельзя: у :root и [data-theme=light] специфичность одна и та же (0,1,0), и
   строка ниже по файлу молча перебивает тему. Поймано 27.08 Куди на проде —
   в светлой теме акцентный текст остался цианом #57DCFC = 1,61:1. */

/* ── БАЗА ──────────────────────────────────────────────────────────────── */
.s,.s *,.s *::before,.s *::after{box-sizing:border-box}
/* container-type на корне канона — не украшение, а условие того, что канон
   вообще можно показать. Раскладочные пороги ниже написаны через @container,
   а не @media: медиазапрос спрашивает ширину ОКНА, и в раскадровке макетов,
   где «телефон» шириной 375 живёт внутри окна 1200, сетка внутри телефона
   раскрывалась в четыре колонки по 77 px и названия товаров схлопывались в
   «би ле…». Поймано Куди 27.08 на превью: скриптом это не видно, глазом —
   поломанная вёрстка мобайла. На живом сайте .s занимает всю ширину, и
   @container даёт ровно те же пороги, что давал @media.
   Здесь только sticky (шапка, таббар, полоса покупки) — containment его не
   трогает; fixed внутри .s ставить нельзя, он привяжется к контейнеру. */
.s{container-type:inline-size;container-name:s;
  font-family:var(--s-font);color:var(--s-ink);background:var(--s-bg);
  font-size:var(--s-fs-3);line-height:1.5;-webkit-font-smoothing:antialiased}
.s h1,.s h2,.s h3{margin:0;letter-spacing:-.02em;font-weight:800}
.s h1{font-size:var(--s-fs-6)} .s h2{font-size:var(--s-fs-5)} .s h3{font-size:var(--s-fs-4)}
@container s (min-width:900px){ .s h1{font-size:var(--s-fs-7)} }
.s p{margin:0}
.s a{color:inherit;text-decoration:none}
.s button{font:inherit;color:inherit;background:none;border:0;cursor:pointer}
.s-num{font-family:var(--s-mono);font-variant-numeric:tabular-nums;letter-spacing:-.02em}
/* Имя бренда ВСЕГДА строчными и никогда не uppercase — правило брендбука. */
.s-brand{font-family:var(--s-display);text-transform:lowercase;font-weight:700;
  letter-spacing:-.01em}
.s-sr{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}
.s-wrap{max-width:var(--s-maxw);margin:0 auto;padding:0 var(--s-sp-4)}
@container s (min-width:900px){ .s-wrap{padding:0 var(--s-sp-7)} }

/* ── КНОПКИ ────────────────────────────────────────────────────────────────
   Три веса и не больше: главное действие, соседнее, служебное. Четвёртый вес
   заводить нельзя — на экране с четырьмя равновесными кнопками человек не
   выбирает, а замирает.
   Primary — циановая заливка с ТЁМНЫМИ буквами. Белые буквы на #57DCFC дают
   1,43 и запрещены: связку «светлая заливка + белая буква» не спасти оттенком,
   менять надо букву. */
.s-btn{display:inline-flex;align-items:center;justify-content:center;gap:var(--s-sp-2);
  min-height:var(--s-h-ctl);padding:0 var(--s-sp-5);border-radius:var(--s-r-2);
  font-weight:700;font-size:var(--s-fs-3);border:1px solid transparent;
  transition:filter .14s ease,background .14s ease}
.s-btn--primary{background:var(--s-accent);color:var(--s-accent-ink);
  border-color:var(--s-accent-edge)}
.s-btn--primary:hover{filter:brightness(1.07)}
.s-btn--secondary{background:transparent;color:var(--s-ink);border-color:var(--s-line-ctl)}
.s-btn--secondary:hover{background:var(--s-surface-2)}
.s-btn--ghost{background:transparent;color:var(--s-ink-mid);padding:0 var(--s-sp-3)}
.s-btn--ghost:hover{color:var(--s-ink)}
.s-btn--block{width:100%}
.s-btn--sm{min-height:var(--s-h-ctl-sm);padding:0 var(--s-sp-4);font-size:var(--s-fs-2)}
/* Выключенная кнопка получает СВОЮ заливку и свой цвет букв. opacity гасит и
   заливку, и подпись разом — подпись уходит в нечитаемое. */
.s-btn:disabled,.s-btn[aria-disabled="true"]{background:var(--s-surface-2);
  border-color:transparent;color:var(--s-ink-soft);cursor:not-allowed;filter:none}
.s-iconbtn{display:inline-flex;align-items:center;justify-content:center;
  width:var(--s-tap);height:var(--s-tap);border-radius:var(--s-r-2);
  color:var(--s-ink-mid);border:1px solid transparent}
.s-iconbtn:hover{background:var(--s-surface-2);color:var(--s-ink)}
.s-iconbtn--framed{border-color:var(--s-line-ctl)}

/* ── ПОЛЯ ────────────────────────────────────────────────────────────────── */
.s-field{display:flex;align-items:center;gap:var(--s-sp-3);width:100%;
  min-height:var(--s-h-ctl);padding:0 var(--s-sp-4);
  background:var(--s-surface);border:1px solid var(--s-line-ctl);
  border-radius:var(--s-r-2);color:var(--s-ink)}
.s-field input,.s-field textarea{flex:1;min-width:0;border:0;outline:0;background:none;
  color:inherit;font:inherit}
.s-field input::placeholder{color:var(--s-ink-soft)}
.s-field:focus-within{border-color:var(--s-accent-edge)}
.s-field--err{border-color:var(--s-neg)}
.s-label{display:block;font-size:var(--s-fs-1);color:var(--s-ink-mid);
  margin-bottom:var(--s-sp-2);font-weight:600}
.s-hint{font-size:var(--s-fs-1);color:var(--s-ink-soft);margin-top:var(--s-sp-2)}
.s-hint--err{color:var(--s-neg)}

/* Поле одноразового кода. Четыре отдельные клетки, а не одно поле: человек
   видит, сколько цифр осталось, и промах пальца не стирает весь код.
   56×56 при шрифте 22 — цифра должна быть крупной, её диктуют вслух по
   телефону или переписывают из SMS. */
.s-code{display:flex;gap:var(--s-sp-3);justify-content:center}
.s-code__cell{width:56px;height:56px;display:flex;align-items:center;justify-content:center;
  background:var(--s-surface);border:1px solid var(--s-line-ctl);border-radius:var(--s-r-2);
  font:700 var(--s-fs-5)/1 var(--s-mono);color:var(--s-ink)}
.s-code__cell.is-filled{border-color:var(--s-ink-soft)}
.s-code__cell.is-cur{border-color:var(--s-accent-edge);box-shadow:0 0 0 3px var(--s-accent-film)}
.s-code__cell.is-err{border-color:var(--s-neg);color:var(--s-neg)}

/* ── КАРТОЧКА-КОНТЕЙНЕР ──────────────────────────────────────────────────── */
.s-card{background:var(--s-surface);border:1px solid var(--s-edge);
  border-radius:var(--s-r-3);padding:var(--s-sp-5)}
.s-card--flush{padding:0;overflow:hidden}

/* ── КАРТОЧКА ТОВАРА ───────────────────────────────────────────────────────
   ФОТО НА СВОЕЙ ПОДЛОЖКЕ. Каталог снят вразнобой: 73 % кадров на тёмном фоне,
   26 % на светлом (все предохранители). Общей заливки, которая подошла бы
   обеим группам, не существует — на тёмной плитке светлый кадр становится
   белым прямоугольником, на светлой тёмный становится плитой. Поэтому цвет
   подложки задаёт САМ ТОВАР через --s-photo-bg: медиана рамки его первого
   фото. Значение ПРИХОДИТ ИЗ API вместе с ценой и остатком — Логист считает
   его на сервере в момент загрузки или замены обложки (27.08). Мой файл
   ds/site/photo_bg.json (78 позиций, 35 уникальных значений) остаётся
   эталоном для сверки реализации, но источником истины не является: таблица
   на 78 строк умерла бы на 79-м товаре, а при замене фото через админку
   устарела бы молча.
   Фолбэк --s-sunk стоит на случай товара без замера — но он именно фолбэк,
   а не «обычное» значение. */
.s-prod{display:flex;flex-direction:column;background:var(--s-surface);
  border:1px solid var(--s-edge);border-radius:var(--s-r-3);overflow:hidden;
  position:relative}
.s-prod__ph{position:relative;aspect-ratio:1;background:var(--s-photo-bg,var(--s-sunk));
  display:flex;align-items:center;justify-content:center}
.s-prod__ph img{width:100%;height:100%;object-fit:contain}
/* Товара нет на складе. Гасим ФОТО, а не карточку целиком: название и цена
   нужны читаемыми — человек пришёл смотреть товар, а не отказ. Затемняющая
   плёнка, не opacity: прозрачность съела бы и подложку --s-photo-bg, ради
   которой всё и затевалось. */
.s-prod__ph--out::after{content:"";position:absolute;inset:0;background:var(--s-veil)}
.s-prod__body{padding:var(--s-sp-4);display:flex;flex-direction:column;gap:var(--s-sp-2);flex:1}
.s-prod__name{font-size:var(--s-fs-3);font-weight:600;line-height:1.35;
  display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
.s-prod__meta{font-size:var(--s-fs-1);color:var(--s-ink-soft)}
/* Подвал плитки: цена слева, действие справа. Работает, пока действие —
   ЗНАЧОК (в корзину). Как только там текст («Запросить», «Сообщить»),
   в строку 141 px оно не встаёт: цена ~110 + кнопка ~110 + зазор 12 = 232,
   и кнопка вылезала за край с обрезкой в «Запросит…». Поймал Логист на
   скринах, видно было и в мобайле, и на десктопе.
   Поэтому подвал знает два вида. Текстовое действие складывается в столбец
   и занимает всю ширину — это и честнее по смыслу: «Запросить» здесь
   главное действие карточки, а не довесок к цене. */
.s-prod__foot{margin-top:auto;padding-top:var(--s-sp-3);display:flex;align-items:flex-end;
  justify-content:space-between;gap:var(--s-sp-3);min-width:0}
.s-prod__foot>*{min-width:0}
.s-prod__foot--stack{flex-direction:column;align-items:stretch}
.s-prod__foot--stack .s-btn{width:100%}
.s-prod__foot--stack .s-price{margin-bottom:var(--s-sp-1)}

/* ── ДВЕ ЦЕНЫ: розничная и персональная ───────────────────────────────────
   Задача из брифа: у B2B-клиента рядом с розничной стоит его цена, и это
   НЕ должно читаться как распродажа. Разница между «скидкой» и «вашей ценой»
   держится на трёх вещах:
   1. зачёркнутая цена стоит СВЕРХУ и мелко, а не сбоку и крупно — у скидочных
      ценников наоборот, старая цена всегда рядом и заметна;
   2. персональная цена НЕ красная. Красный на ценнике = распродажа, это
      выученный рефлекс. Берём обычные чернила, а отличие несёт подпись;
   3. подпись обязательна, и в ней стоит НАЗВАНИЕ УРОВНЯ («Опт-1», «Дилер»),
      а не слово «скидка». Уровень — это статус клиента, который он получил;
      скидка — разовая акция магазина. Разница в том, вернётся ли он завтра.
   Сущностей ровно три (уточнено Логистом 27.08): розничная зачёркнутая,
   персональная главная, подпись уровня. Розничному клиенту — ОДНА цена, без
   зачёркивания и без подписи.
   Механика цены компонент не трогает: цена приходит одним запросом вместе с
   каталогом, а откуда она взялась — точечная на позицию, процент уровня или
   любое из пяти правил будущей иерархии — для карточки это всё равно одно
   число и одна подпись. */
.s-price{display:flex;flex-direction:column;gap:2px}
.s-price__old{font-size:var(--s-fs-1);color:var(--s-ink-soft);text-decoration:line-through}
.s-price__now{font:800 var(--s-fs-4)/1 var(--s-font);letter-spacing:-.02em;
  white-space:nowrap}   /* страховка к неразрывному пробелу в money() */
.s-price__unit{font-size:var(--s-fs-2);font-weight:600;color:var(--s-ink-mid);margin-left:2px}
/* Подпись цены — ДВА ВИДА, и различаются они формой, а не только словами.
   Причина в механике (Логист, 27.08): правила 1–3 дают клиенту постоянный
   уровень, а правило 4 — автоскидку за оборот, которая действует до конца
   следующего месяца и сгорает, если порог не добран. Одна и та же подпись на
   оба случая врёт в одну сторону: «Опт-1» под временной ценой означает
   «теперь навсегда», и человек узнает правду в тот день, когда цена молча
   вырастет.

   Постоянная — акцентом, без плашки: плашка рядом с ценой читается ярлыком
   акции. Цвет говорит «это про тебя», формы не несёт.
   Временная — янтарём, со знаком часов и СРОКОМ. Янтарь здесь тот же, что у
   статуса «ждёт вашего участия», и это не совпадение: добрать оборот до
   следующего порога — как раз участие клиента. Красный брать нельзя, он
   означает распродажу; акцент нельзя — он уже занят постоянным уровнем.
   Срок обязателен и в короткой форме тоже: подпись без даты не отличается от
   постоянной ничем, кроме цвета, а цвет в карточке 187 px легко проглядеть.
   Формулировки: в каталоге коротко — «−7 % до 31 мая», в карточке товара
   полно — «Скидка за оборот 7 % · действует до 31 мая».
   Розничному клиенту без скидок подписи нет вовсе — одна цена и ничего
   рядом. */
.s-price__tag{display:inline-flex;align-items:center;gap:5px;
  font-size:var(--s-fs-1);font-weight:700;color:var(--s-accent-tx)}
.s-price__tag svg{width:12px;height:12px;flex:none}
.s-price__tag--temp{color:var(--s-warn)}
.s-price--big .s-price__now{font-size:var(--s-fs-5)}
/* Цены нет — 15 позиций из 78 опубликованных. Сейчас витрина их прячет
   фильтром, но фильтр отключаемый, и состояние обязано существовать: пустое
   место под названием читается как «бесплатно» или как сломанная карточка. */
.s-price__ask{font-size:var(--s-fs-2);font-weight:700;color:var(--s-ink-mid)}

/* ── НАЛИЧИЕ ──────────────────────────────────────────────────────────────
   Точка + слово. Только точка не годится: цвет без подписи требует легенды,
   а её негде показать.
   Остаток — ЧИСЛО (shop_stock.qty, синк из МойСклад раз в час), но показываем
   его не всегда. Правило: «в наличии» без цифры, пока остаток > 3; при 1–3 —
   «осталось 2» янтарём; при нуле — серым, и кнопка покупки уступает место
   подписке на поступление. Причина простая: «в наличии 47 шт» не сообщает
   покупателю ничего, а «осталось 2» — сообщает. Цифра, которая никогда не
   меняет решение, это шум, и на её фоне не виден тот случай, когда она
   решение меняет. Порог 3 — рекомендация Логиста по фактическому остатку:
   из 36 позиций с данными 28 лежат на 11+, и только четыре в диапазоне 1–3. */
.s-stock{display:inline-flex;align-items:center;gap:var(--s-sp-2);
  font-size:var(--s-fs-1);font-weight:600;color:var(--s-ink-mid)}
.s-stock__dot{width:7px;height:7px;border-radius:50%;background:var(--s-pos);flex:none}
.s-stock--low .s-stock__dot{background:var(--s-warn)}
.s-stock--low{color:var(--s-warn)}
.s-stock--out .s-stock__dot{background:var(--s-ink-soft)}
.s-stock--out{color:var(--s-ink-soft)}

/* ── СЧЁТЧИК КОЛИЧЕСТВА ───────────────────────────────────────────────────
   Появляется НА МЕСТЕ кнопки «в корзину», а не рядом с ней: так плитка не
   меняет высоту при добавлении и сетка каталога не прыгает. Приём взят из
   продуктовых приложений, и он здесь ровно про то же — покупают по несколько
   штук, не отходя от полки. */
.s-qty{display:inline-flex;align-items:center;background:var(--s-surface-2);
  border-radius:var(--s-r-2);height:var(--s-h-ctl-sm)}
.s-qty__b{width:var(--s-h-ctl-sm);height:var(--s-h-ctl-sm);display:flex;align-items:center;
  justify-content:center;color:var(--s-ink);border-radius:var(--s-r-2)}
.s-qty__b:disabled{color:var(--s-ink-soft);cursor:not-allowed}
.s-qty__v{min-width:32px;text-align:center;font:700 var(--s-fs-3)/1 var(--s-mono);
  font-variant-numeric:tabular-nums}
.s-qty--lg{height:var(--s-h-ctl)}
.s-qty--lg .s-qty__b{width:var(--s-h-ctl);height:var(--s-h-ctl)}

/* ── ЧИП ФИЛЬТРА ─────────────────────────────────────────────────────────
   Выбранный чип — ИНВЕРСИЯ, а не мягкая подсветка: заливка --s-surface-2 к
   карточке даёт 1,12 и как признак выбора не читается (та же ошибка уже была
   в мобильном каноне CRM). */
.s-chip{display:inline-flex;align-items:center;gap:var(--s-sp-2);height:var(--s-h-ctl-sm);
  padding:0 var(--s-sp-4);border-radius:var(--s-r-pill);white-space:nowrap;
  font-size:var(--s-fs-2);font-weight:600;color:var(--s-ink-mid);
  background:var(--s-surface);border:1px solid var(--s-edge)}
.s-chip:hover{color:var(--s-ink);border-color:var(--s-line-ctl)}
.s-chip.is-on{background:var(--s-ink);color:var(--s-bg);border-color:var(--s-ink)}
.s-chip__n{font-family:var(--s-mono);font-size:var(--s-fs-1);opacity:.72}
.s-chips{display:flex;gap:var(--s-sp-2);overflow-x:auto;scrollbar-width:none;
  padding-bottom:2px}
.s-chips::-webkit-scrollbar{display:none}

/* ── СТАТУС ЗАКАЗА ───────────────────────────────────────────────────────
   В CRM у сотрудника девять статусов. КЛИЕНТУ показываем пять шагов и два
   терминальных состояния (согласовано с Логистом 27.08, финально утверждает
   владелец): «Новый» — внутренний, до оплаты клиенту его видеть незачем;
   «Готов к отправке» склеен с «Собирается» — для покупателя это одно и то же
   ожидание; «Передан перевозчику» на языке клиента — «В пути».
       ждёт меня    warn    Ожидает оплаты
       в работе     accent  Оплачен · Собирается · В пути
       готово       pos     Доставлен
       не сложилось neg     Отменён · Возврат
   Цветов ЧЕТЫРЕ на семь состояний, и это намеренно. Цвет отвечает ровно на
   один вопрос: нужно моё участие или всё идёт само. Какой именно шаг — говорят
   иконка и слово, поэтому иконка у каждого статуса СВОЯ и обязательна: она и
   есть носитель различия внутри группы. Семь оттенков человек не запомнит и
   начнёт читать подписи — и тогда цвет только мешает. */
.s-status{display:inline-flex;align-items:center;gap:var(--s-sp-2);
  height:28px;padding:0 10px;border-radius:var(--s-r-pill);
  font-size:var(--s-fs-1);font-weight:700;white-space:nowrap}
.s-status svg{width:14px;height:14px;flex:none}
.s-status--wait{background:var(--s-warn-film);color:var(--s-warn)}
.s-status--work{background:var(--s-accent-film);color:var(--s-accent-tx)}
.s-status--done{background:var(--s-pos-film);color:var(--s-pos)}
.s-status--fail{background:var(--s-neg-film);color:var(--s-neg)}

/* Прогресс заказа — те же пять шагов: Ожидает оплаты → Оплачен → Собирается →
   В пути → Доставлен. Отменённый и возврат прогрессом не рисуются вовсе: у
   них нет продолжения, полоса с оборванной серединой врёт про «сейчас
   продолжится». Их место — статусом в шапке заказа. */
.s-steps{display:flex;align-items:center;gap:0}
.s-step{flex:1;display:flex;flex-direction:column;gap:var(--s-sp-2);min-width:0}
.s-step__bar{height:4px;border-radius:var(--s-r-pill);background:var(--s-sunk)}
.s-step.is-done .s-step__bar{background:var(--s-accent-edge)}
.s-step.is-cur .s-step__bar{background:var(--s-accent-edge)}
.s-step__t{font-size:var(--s-fs-1);color:var(--s-ink-soft);overflow:hidden;
  text-overflow:ellipsis;white-space:nowrap}
.s-step.is-cur .s-step__t{color:var(--s-ink);font-weight:700}
.s-steps .s-step + .s-step{margin-left:var(--s-sp-2)}

/* ── О ТОВАРЕ: ИНФОГРАФИКА ПОД ГАЛЕРЕЕЙ ────────────────────────────────────
   Кадров у позиции пять, и предметный из них редко больше одного: остальные —
   баннеры продавца с WB («БОЛЕЕ ЯРКИЙ СВЕТ», «ТЕХНИЧЕСКИЕ ХАРАКТЕРИСТИКИ»,
   выноски с размерами). В галерею они не идут по трём причинам, и первая
   решающая: --s-photo-bg ОДНА на позицию, а слайды листаются на общей
   подложке. Кадры внутри позиции сняты на разных фонах — у 136703785 первый
   #1C0B0B, красноватый, а пятый нейтрально-тёмный, — и баннер на чужой
   подложке получает цветной шов по бокам, свой на каждом слайде.
   Вторая — масштаб. Галерея квадратная, кадр 3:4 внутри неё занимает 0,75
   ширины: на десктопе 525 px из исходных 900 (0,58), на мобильном 281 (0,31).
   Заголовок баннера при 0,58 ещё читается, таблица характеристик — уже нет,
   а на мобильном нечитаемо всё. Третья — миниатюра 72 px даёт кадр 54 px,
   0,06 от оригинала: лента из пяти, где четыре баннера, это четыре
   одинаковых серых квадрата, выбирать по ним нечего.
   Поэтому баннеры уходят СЮДА, в полную ширину колонки, где они и были
   нарисованы читаемыми. Подложка здесь тоже --s-photo-bg, но роль у неё
   другая: не «фон витрины», а поле кадра, и высота идёт от пропорции самого
   баннера, без обрезки — инфографику нельзя кадрировать, в ней текст. */
.s-about{display:flex;flex-direction:column;gap:var(--s-sp-4)}
.s-about__ph{border-radius:var(--s-r-3);overflow:hidden;
  background:var(--s-photo-bg,var(--s-sunk))}
.s-about__ph img{width:100%;height:auto;display:block}

/* ── СТРОКА КОРЗИНЫ ─────────────────────────────────────────────────────── */
.s-line{display:flex;gap:var(--s-sp-4);padding:var(--s-sp-4) 0;align-items:flex-start}
.s-line + .s-line{border-top:1px solid var(--s-line)}
.s-line__ph{width:72px;height:72px;flex:none;border-radius:var(--s-r-2);overflow:hidden;
  background:var(--s-photo-bg,var(--s-sunk));display:flex;align-items:center;justify-content:center}
.s-line__ph img{width:100%;height:100%;object-fit:contain}
.s-line__tx{flex:1;min-width:0;display:flex;flex-direction:column;gap:var(--s-sp-2)}
.s-line__name{font-size:var(--s-fs-2);font-weight:600;line-height:1.4;
  display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
.s-line__foot{display:flex;align-items:center;justify-content:space-between;gap:var(--s-sp-3);
  margin-top:auto}

/* ── ИТОГИ ───────────────────────────────────────────────────────────────── */
.s-sum{display:flex;flex-direction:column;gap:var(--s-sp-3)}
.s-sum__r{display:flex;align-items:baseline;justify-content:space-between;gap:var(--s-sp-4);
  font-size:var(--s-fs-2);color:var(--s-ink-mid)}
.s-sum__r b{color:var(--s-ink);font-weight:600}
.s-sum__r--save b{color:var(--s-pos)}
.s-sum__total{display:flex;align-items:baseline;justify-content:space-between;
  padding-top:var(--s-sp-3);border-top:1px solid var(--s-line);
  font-size:var(--s-fs-3);font-weight:700}
.s-sum__total b{font-size:var(--s-fs-5);letter-spacing:-.02em}

/* ── ЛИПКАЯ ПАНЕЛЬ ПОКУПКИ (мобайл) ──────────────────────────────────────
   Кнопка покупки не должна требовать прокрутки обратно вверх. Тень здесь
   легальна: панель — плавающий слой, а не карточка в потоке.
   env(safe-area-inset-bottom) обязателен, иначе на iPhone кнопка уезжает под
   индикатор жеста. */
.s-buybar{position:sticky;bottom:0;z-index:20;display:flex;align-items:center;
  gap:var(--s-sp-4);padding:var(--s-sp-3) var(--s-sp-4);
  padding-bottom:calc(var(--s-sp-3) + env(safe-area-inset-bottom,0px));
  background:var(--s-surface);border-top:1px solid var(--s-edge);
  box-shadow:var(--s-shadow-bar)}

/* ── ВЫБОР ИЗ СПИСКА: служба доставки, ПВЗ, способ оплаты ────────────────
   Один компонент на три задачи checkout'а. Выбранная строка помечена РАМКОЙ
   акцентом и точкой в кружке, а не заливкой: заливка --s-surface-2 к карточке
   1,12 и как признак выбора не видна, а акцентная заливка под текстом
   потребовала бы перекрашивать и текст.
   Радио-точка обязательна вместе с рамкой. Рамка одна работает, пока строка
   на экране одна; в списке из восьми ПВЗ глаз ищет отметку, а не сравнивает
   толщину границ. */
.s-pick{display:flex;flex-direction:column;gap:var(--s-sp-2)}
.s-opt{display:flex;align-items:flex-start;gap:var(--s-sp-3);width:100%;text-align:left;
  padding:var(--s-sp-4);border-radius:var(--s-r-2);
  background:var(--s-surface);border:1px solid var(--s-edge)}
.s-opt:hover{border-color:var(--s-line-ctl)}
.s-opt.is-on{border-color:var(--s-accent-edge);background:var(--s-accent-film)}
.s-opt__radio{width:20px;height:20px;flex:none;margin-top:1px;border-radius:50%;
  border:2px solid var(--s-line-ctl);display:flex;align-items:center;justify-content:center}
.s-opt.is-on .s-opt__radio{border-color:var(--s-accent-edge)}
.s-opt.is-on .s-opt__radio::after{content:"";width:10px;height:10px;border-radius:50%;
  background:var(--s-accent-edge)}
.s-opt__tx{flex:1;min-width:0;display:flex;flex-direction:column;gap:2px}
.s-opt__t{font-size:var(--s-fs-3);font-weight:600}
.s-opt__s{font-size:var(--s-fs-1);color:var(--s-ink-mid)}
/* Цена и срок доставки — справа, моно, чтобы столбец читался сверху вниз при
   сравнении служб. Цена ОБЯЗАНА быть видна до оплаты: это требование брифа и
   единственная причина, по которой выбор ПВЗ вообще стоит перед оплатой. */
.s-opt__price{text-align:right;flex:none;display:flex;flex-direction:column;gap:2px}
.s-opt__price b{font:700 var(--s-fs-3)/1 var(--s-mono);font-variant-numeric:tabular-nums}
.s-opt__price span{font-size:var(--s-fs-1);color:var(--s-ink-soft)}
.s-opt__logo{width:36px;height:36px;flex:none;border-radius:var(--s-r-1);
  background:var(--s-surface-2);display:flex;align-items:center;justify-content:center;
  font:700 10px/1 var(--s-mono);color:var(--s-ink-mid);letter-spacing:.02em}

/* Карта ПВЗ. ГРУЗИТСЯ ТОЛЬКО ПО КНОПКЕ — у ключа Яндекс.Карт лимит 500
   загрузок в сутки, дальше 390 ₽ за пакет и блокировка при систематическом
   превышении (Логист, 27.08). Автозагрузка карты на входе в checkout сожгла
   бы квоту на людях, которые до карты и не дошли: основной режим — список,
   карта дополняет его, а не наоборот.
   До нажатия на месте карты стоит не пустота и не спиннер, а заглушка с
   кнопкой: человек должен понимать, что карта здесь есть и почему она пока
   не показана. */
.s-mapbox{position:relative;border-radius:var(--s-r-3);overflow:hidden;
  border:1px solid var(--s-edge);background:var(--s-sunk);min-height:180px;
  display:flex;align-items:center;justify-content:center}
.s-mapbox__stub{display:flex;flex-direction:column;align-items:center;gap:var(--s-sp-3);
  padding:var(--s-sp-5);text-align:center;color:var(--s-ink-mid);font-size:var(--s-fs-2)}
/* Балун пина: адрес, часы, срок, цена — то же, что в строке списка, иначе
   выбор на карте и выбор в списке дают разные наборы фактов. */
.s-balloon{position:absolute;min-width:220px;padding:var(--s-sp-4);
  background:var(--s-surface);border:1px solid var(--s-edge);border-radius:var(--s-r-2);
  box-shadow:var(--s-shadow-pop);font-size:var(--s-fs-2)}
.s-balloon__t{font-size:var(--s-fs-3);font-weight:700;margin-bottom:var(--s-sp-1)}
.s-balloon__s{color:var(--s-ink-mid);font-size:var(--s-fs-1)}
.s-pin{width:28px;height:28px;border-radius:50% 50% 50% 4px;transform:rotate(-45deg);
  background:var(--s-ink-soft);border:2px solid var(--s-surface);position:absolute}
.s-pin.is-on{background:var(--s-accent-edge);z-index:2}

/* ── ШАГИ ОФОРМЛЕНИЯ ─────────────────────────────────────────────────────
   Нумерованные секции на ОДНОМ экране, а не мастер из четырёх страниц.
   Регистрации как отдельного шага нет — она растворена в шаге с телефоном.
   Номер шага нужен затем, чтобы длинная форма читалась как три коротких. */
.s-stage{display:flex;gap:var(--s-sp-4);padding:var(--s-sp-5) 0}
.s-stage + .s-stage{border-top:1px solid var(--s-line)}
.s-stage__n{width:28px;height:28px;flex:none;border-radius:50%;
  background:var(--s-surface-2);color:var(--s-ink-mid);
  display:flex;align-items:center;justify-content:center;
  font:700 var(--s-fs-2)/1 var(--s-mono)}
.s-stage.is-done .s-stage__n{background:var(--s-pos-film);color:var(--s-pos)}
.s-stage.is-cur .s-stage__n{background:var(--s-accent);color:var(--s-accent-ink)}
.s-stage__body{flex:1;min-width:0;display:flex;flex-direction:column;gap:var(--s-sp-3)}
.s-stage__t{font-size:var(--s-fs-4);font-weight:700}

/* ── ВХОД ЧЕРЕЗ VK И TELEGRAM ────────────────────────────────────────────
   Кнопки соцвхода в ФИРМЕННЫХ цветах сервисов — это тот случай, когда чужой
   бренд важнее своего: человек ищет знакомую синюю кнопку, а не нашу
   циановую. Но фирменный цвет не освобождает от порога, и белые буквы не
   держатся ни на одном из двух: VK #0077FF даёт 4,13, Telegram #2AABEE — 2,57.
   Чинится это по-разному, и разница показательна:
   · VK затемнён в CIELCh до #0070F7 — белый текст 4,51, а до фирменного
     ΔE 2,8, то есть глазом тот же синий;
   · Telegram затемнять пришлось бы до ΔE 17,5 — это уже другой цвет, и
     узнаваемость кнопки теряется. Поэтому у него меняем не заливку, а БУКВУ:
     тёмная #04283A на нетронутом фирменном даёт 5,96.
   Общее правило: связку «светлая заливка + белая буква» не спасти оттенком.
   Менять надо ту сторону, которая от этого не сломается. */
.s-social{display:flex;gap:var(--s-sp-3)}
.s-social__b{flex:1;display:inline-flex;align-items:center;justify-content:center;
  gap:var(--s-sp-2);min-height:var(--s-h-ctl);border-radius:var(--s-r-2);
  font-weight:700;font-size:var(--s-fs-2)}
.s-social__b--vk{background:var(--s-vk);color:var(--s-vk-on)}
.s-social__b--tg{background:var(--s-tg);color:var(--s-tg-on)}

/* ── ВКЛАДКИ / СЕГМЕНТ ──────────────────────────────────────────────────── */
.s-seg{display:flex;gap:var(--s-sp-1);padding:var(--s-sp-1);background:var(--s-surface-2);
  border-radius:var(--s-r-2)}
.s-seg__b{flex:1;height:var(--s-h-ctl-sm);border-radius:var(--s-r-1);
  font-size:var(--s-fs-2);font-weight:600;color:var(--s-ink-mid)}
.s-seg__b.is-on{background:var(--s-surface);color:var(--s-ink);
  box-shadow:var(--s-shadow-lift)}

/* ── ПЕРЕКЛЮЧАТЕЛЬ ТЕМЫ ───────────────────────────────────────────────────
   Владелец 27.08 решил: светлая тема равноправная, с выбором для человека.
   Значит нужен видимый контрол, а не «вариант к показу».

   Состояний ТРИ, поэтому это сегмент, а не тумблер: тумблер физически не
   умеет третьего положения, и «как в системе» в нём выразить нечем.

   Признак выбранного — НЕ заливка. Штатный .s-seg отделяет выбранную вкладку
   заливкой --s-surface по --s-surface-2, а это 1,10 в тёмной и 1,09 в
   светлой: глазом не видно ни там, ни там (та же грабля ловила меня в каноне
   мобайла CRM). Для вкладок каталога это терпимо — там ещё и содержимое
   меняется, — но у переключателя темы сменится вся страница, и промахнуться
   в выборе нельзя. Поэтому берём канонную связку выбранного варианта из
   .s-opt: кромка акцентом + акцентная плёнка + акцентный текст.
       кромка к рамке сегмента  10,27 тёмная · 4,79 светлая  (порог 3,0)
       текст на плёнке           8,75 тёмная · 4,86 светлая  (порог 4,5)
   Плёнка сама по себе признаком не является (1,17 / 1,01) — форму держит
   кромка, плёнка только поддерживает её цветом.

   Знак и подпись вместе, не по отдельности. Солнце и луна без подписи ещё
   узнаваемы, а монитор в роли «как в системе» — нет: его читают как
   «версия для компьютера». */
.s-theme{display:flex;gap:var(--s-sp-1);padding:var(--s-sp-1);
  background:var(--s-surface-2);border-radius:var(--s-r-2)}
.s-theme__b{flex:1;display:inline-flex;align-items:center;justify-content:center;
  gap:var(--s-sp-2);min-height:var(--s-tap);padding:0 var(--s-sp-3);
  border:1px solid transparent;border-radius:var(--s-r-1);
  font-size:var(--s-fs-2);font-weight:600;color:var(--s-ink-mid);
  white-space:nowrap}
.s-theme__b svg{flex:none}
.s-theme__b:hover{color:var(--s-ink)}
.s-theme__b.is-on{border-color:var(--s-accent-edge);background:var(--s-accent-film);
  color:var(--s-accent-tx)}

/* Компактный вид — для шапки десктопа, где рядом стоят поиск и корзина и
   подписи не помещаются. Тач-цель здесь опускаем до 36: в шапке десктопа
   работает мышь, а 44 px рвали бы высоту строки.

   Порог 1160, а не 900, как у остальной шапки. Посчитано, а не на глаз:
   логотип 90 + меню 340 + поиск 340 + переключатель 142 + два знака 104 и
   зазоры дают 1064 px, а на 900 внутри остаётся 836. Без порога переключатель
   выдавливал бы поиск — поле сжималось бы до min-content, и «Поиск по
   каталогу» превратился бы в обрубок. Ниже 1160 переключатель из шапки
   уходит совсем, и путь к нему остаётся один — строка «Оформление» в
   кабинете. Она есть и на мобайле, и на десктопе, поэтому потери доступа
   нет ни на одной ширине. */
.s-theme--icons{display:none}
@container s (min-width:1160px){ .s-theme--icons{display:flex} }
.s-theme--icons .s-theme__b{min-height:var(--s-h-ctl-sm);padding:0 var(--s-sp-3);flex:none}
.s-theme--icons .s-theme__b span{position:absolute;width:1px;height:1px;
  overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}

/* Вариант «знак НАД подписью» — для мобайла и вообще везде, где сегмент
   уже 340 px. Считать надо не сумму подписей, а ТРИ ШИРИНЫ САМОЙ ДЛИННОЙ:
   у кнопок flex:1, и все три равны широчайшей. Я это упустил и поставил на
   мобайл строчный вид — Логист намерил содержимое 383 при контейнере 316,
   третья кнопка уезжала за край телефона на 30 px и обрезалась им же.

   Замер по метрикам Inter (Bold — верхняя оценка, фактический вес 600 уже),
   ширина внутри сегмента = экран − отступы экрана и карточки − зазоры:
       строкой  «Как в системе»   439 px — не влезает даже на 390
       столбиком «Как в системе»  361 px — не влезает на 390
       столбиком «В системе»      281 px — не влезает на 320
       столбиком «Система» 12 px  226 px — влезает на 320, 360, 390
   Отсюда две правки разом: раскладка в столбец И подпись короче на ступень.
   Полное «Как в системе» осталось в aria-label — экран потерял слово,
   скринридер не потерял ничего.

   Тач-цель держим: знак 18 + зазор 4 + подпись 14 + отступы 12 = 48 ≥ 44. */
.s-theme--stack .s-theme__b{flex-direction:column;gap:var(--s-sp-1);
  padding:var(--s-sp-2) var(--s-sp-2) 6px;font-size:var(--s-fs-1);line-height:1.15}

/* Подпись над переключателем в подвале и в кабинете. Без неё три знака в
   ряд читаются как фильтр или как выбор языка. */
.s-theme-row{display:flex;flex-direction:column;gap:var(--s-sp-2)}
.s-theme-row__t{font-size:var(--s-fs-1);font-weight:700;color:var(--s-ink-mid);
  letter-spacing:.02em}

/* ── СЕТКИ ──────────────────────────────────────────────────────────────
   Мобайл — ДВЕ колонки. Одна колонка на 390 px даёт карточку 358 px: такая
   плитка занимает пол-экрана, и за один взгляд виден один товар. Две дают
   187 px — фото по-прежнему разборчиво, а за экран умещается четыре позиции.
   Это прямо тот приём, ради которого назван «Самокат». */
.s-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:var(--s-sp-3)}
@container s (min-width:640px){ .s-grid{grid-template-columns:repeat(3,1fr);gap:var(--s-sp-4)} }
@container s (min-width:1000px){ .s-grid{grid-template-columns:repeat(4,1fr)} }
@container s (min-width:1240px){ .s-grid{grid-template-columns:repeat(5,1fr)} }

/* ── ШАПКА ──────────────────────────────────────────────────────────────
   На мобильном в шапке ТРИ вещи: логотип, поиск, корзина. Каталог, кабинет и
   прочее уезжают в нижнюю панель — большим пальцем до верха экрана не
   дотянуться, а именно туда на телефоне чаще всего кладут навигацию.
   Поиск на первом экране, а не за иконкой лупы: 78 позиций с названиями
   вида «би лед линзы 2.5 в фары bi led 12v» листать бесполезно, их ищут. */
.s-hd{position:sticky;top:0;z-index:30;background:var(--s-bg);
  border-bottom:1px solid var(--s-line)}
.s-hd__in{display:flex;align-items:center;gap:var(--s-sp-3);
  height:56px;max-width:var(--s-maxw);margin:0 auto;padding:0 var(--s-sp-4)}
.s-hd__logo{font-size:20px;display:inline-flex;align-items:baseline;gap:5px}
.s-hd__dot{width:6px;height:6px;border-radius:50%;background:var(--s-accent-edge);flex:none}
.s-hd__nav{display:none;gap:var(--s-sp-5);margin-left:var(--s-sp-6)}
.s-hd__nav a{font-size:var(--s-fs-2);font-weight:600;color:var(--s-ink-mid)}
.s-hd__nav a:hover,.s-hd__nav a.is-on{color:var(--s-ink)}
@container s (min-width:900px){ .s-hd__in{height:68px;padding:0 var(--s-sp-7)} .s-hd__nav{display:flex} }
/* Счётчик корзины. Обводка цветом фона обязательна: без неё точка на кромке
   иконки теряется — та же история, что с сигналом просрочки в CRM. */
.s-cartbtn{position:relative}
.s-cartbtn__n{position:absolute;top:2px;right:2px;min-width:18px;height:18px;padding:0 5px;
  border-radius:var(--s-r-pill);background:var(--s-accent);color:var(--s-accent-ink);
  border:2px solid var(--s-bg);
  font:700 10px/14px var(--s-mono);display:flex;align-items:center;justify-content:center}

/* ── НИЖНЯЯ ПАНЕЛЬ (мобайл) ─────────────────────────────────────────────
   Четыре раздела: Главная · Каталог · Корзина · Кабинет. Пятый не заводить —
   на 390 px пятая цель уходит под 78 px ширины, и промахи по соседней
   становятся обычным делом. */
.s-tabs{position:sticky;bottom:0;z-index:30;display:flex;
  background:var(--s-surface);border-top:1px solid var(--s-edge);
  padding-bottom:env(safe-area-inset-bottom,0px)}
.s-tab{flex:1;display:flex;flex-direction:column;align-items:center;gap:3px;
  padding:var(--s-sp-2) 0 var(--s-sp-3);color:var(--s-ink-soft);
  font-size:10px;font-weight:600;min-height:var(--s-tap)}
.s-tab.is-on{color:var(--s-accent-tx)}
@container s (min-width:900px){ .s-tabs{display:none} }

/* ── КАБИНЕТ ────────────────────────────────────────────────────────────
   Карточка заказа: номер моно, дата, статус, состав свёрнут до строки
   «3 товара», прогресс. Разворачивать состав по умолчанию не надо — человек
   открывает заказы чтобы узнать «где посылка», а не пересчитать позиции. */
.s-order__head{display:flex;align-items:center;justify-content:space-between;
  gap:var(--s-sp-3);margin-bottom:var(--s-sp-3)}
.s-order__n{font:700 var(--s-fs-3)/1 var(--s-mono)}
.s-order__d{font-size:var(--s-fs-1);color:var(--s-ink-soft)}
.s-order__thumbs{display:flex;gap:var(--s-sp-2);margin-bottom:var(--s-sp-4)}
.s-order__th{width:44px;height:44px;border-radius:var(--s-r-1);overflow:hidden;flex:none;
  background:var(--s-photo-bg,var(--s-sunk))}
.s-order__th img{width:100%;height:100%;object-fit:contain}
.s-order__more{width:44px;height:44px;border-radius:var(--s-r-1);flex:none;
  background:var(--s-surface-2);color:var(--s-ink-mid);display:flex;align-items:center;
  justify-content:center;font:700 var(--s-fs-2)/1 var(--s-mono)}
/* Уровень клиента — единственное место, где статус показан как плашка: в
   кабинете это факт о человеке, а не украшение цены. */
.s-level{display:inline-flex;align-items:center;gap:var(--s-sp-2);
  padding:var(--s-sp-2) var(--s-sp-3);border-radius:var(--s-r-pill);
  background:var(--s-accent-film);color:var(--s-accent-tx);
  font-size:var(--s-fs-1);font-weight:700}
/* Строка документа и прайса. Скачивание — одна кнопка без экрана выбора:
   состав прайса определяет уровень клиента, выбирать нечего. */
.s-doc{display:flex;align-items:center;gap:var(--s-sp-3);padding:var(--s-sp-4) 0}
.s-doc + .s-doc{border-top:1px solid var(--s-line)}
.s-doc__ic{width:40px;height:40px;flex:none;border-radius:var(--s-r-2);
  background:var(--s-surface-2);color:var(--s-ink-mid);
  display:flex;align-items:center;justify-content:center}
.s-doc__tx{flex:1;min-width:0}
.s-doc__t{font-size:var(--s-fs-2);font-weight:600}
.s-doc__s{font-size:var(--s-fs-1);color:var(--s-ink-soft)}

/* ── ФУТЕР ──────────────────────────────────────────────────────────────── */
.s-ft{border-top:1px solid var(--s-line);background:var(--s-surface);
  padding:var(--s-sp-7) 0 var(--s-sp-6);margin-top:var(--s-sp-8)}
.s-ft__grid{display:grid;grid-template-columns:1fr 1fr;gap:var(--s-sp-6)}
@container s (min-width:900px){ .s-ft__grid{grid-template-columns:2fr 1fr 1fr 1fr} }
.s-ft__h{font-size:var(--s-fs-1);font-weight:700;color:var(--s-ink-soft);
  text-transform:uppercase;letter-spacing:.08em;margin-bottom:var(--s-sp-3)}
.s-ft a{display:block;font-size:var(--s-fs-2);color:var(--s-ink-mid);padding:var(--s-sp-2) 0}
.s-ft a:hover{color:var(--s-ink)}
.s-ft__bottom{margin-top:var(--s-sp-6);padding-top:var(--s-sp-4);
  border-top:1px solid var(--s-line);font-size:var(--s-fs-1);color:var(--s-ink-soft)}

/* ── ПУСТО · ЗАГРУЗКА · ОШИБКА ───────────────────────────────────────────
   У каждого экрана четыре состояния, и пустое обязано отвечать, куда идти
   дальше: «ничего не найдено» без кнопки — тупик. */
.s-empty{display:flex;flex-direction:column;align-items:center;gap:var(--s-sp-4);
  padding:var(--s-sp-8) var(--s-sp-5);text-align:center}
.s-empty__ic{width:56px;height:56px;border-radius:var(--s-r-3);display:flex;
  align-items:center;justify-content:center;background:var(--s-surface-2);color:var(--s-ink-soft)}
.s-empty__t{font-size:var(--s-fs-4);font-weight:700}
.s-empty__s{font-size:var(--s-fs-2);color:var(--s-ink-mid);max-width:280px}
.s-skel{display:block;background:var(--s-surface-2);border-radius:var(--s-r-1);
  animation:s-pulse 1.4s ease-in-out infinite}
.s-skel--ph{aspect-ratio:1;border-radius:var(--s-r-3)}
.s-skel--line{height:12px;margin-top:var(--s-sp-2)}
.s-skel--price{height:18px;width:60%;margin-top:var(--s-sp-3)}
@keyframes s-pulse{0%,100%{opacity:.5}50%{opacity:.9}}
@media (prefers-reduced-motion:reduce){ .s-skel{animation:none} }
.s-alert{display:flex;gap:var(--s-sp-3);padding:var(--s-sp-4);border-radius:var(--s-r-2);
  font-size:var(--s-fs-2);background:var(--s-neg-film);color:var(--s-neg)}
.s-alert svg{flex:none;width:18px;height:18px;margin-top:1px}
.s-alert--warn{background:var(--s-warn-film);color:var(--s-warn)}
