/* Toggle Coffee storefront — premium-minimal skin on the Milano layout system.
   Typeface: Mont (self-hosted, same as menu.toggle.coffee). */

/* ---------- fonts ---------- */

@font-face{font-family:"Mont";src:url("/assets/fonts/Mont-Regular.woff2?v=2") format("woff2"),url("/assets/fonts/Mont-Regular.woff?v=2") format("woff");font-weight:400;font-style:normal;font-display:swap}
@font-face{font-family:"Mont";src:url("/assets/fonts/Mont-SemiBold.woff2?v=2") format("woff2"),url("/assets/fonts/Mont-SemiBold.woff?v=2") format("woff");font-weight:500;font-style:normal;font-display:swap}
@font-face{font-family:"Mont";src:url("/assets/fonts/Mont-SemiBold.woff2?v=2") format("woff2"),url("/assets/fonts/Mont-SemiBold.woff?v=2") format("woff");font-weight:600;font-style:normal;font-display:swap}
@font-face{font-family:"Mont";src:url("/assets/fonts/Mont-Bold.woff2?v=2") format("woff2"),url("/assets/fonts/Mont-Bold.woff?v=2") format("woff");font-weight:700;font-style:normal;font-display:swap}
@font-face{font-family:"Mont";src:url("/assets/fonts/Mont-Black.woff2?v=2") format("woff2"),url("/assets/fonts/Mont-Black.woff?v=2") format("woff");font-weight:800;font-style:normal;font-display:swap}

/* ---------- design tokens ---------- */
:root{
  --header-bg:rgb(19,19,19); --header-ink:#fff;
  --font:"Mont",-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;

  --color-white:#ffffff;
  --color-gray:#f7f5f2;      /* warm paper */
  --color-gray-2:#efece7;
  --hair:#efedea;            /* lightest divider */
  --color-border:#e4e1db;

  --color-primary:#c14a3f;   /* terracotta, used sparingly */
  /* Терракота как ТЕКСТ на --color-primary-soft даёт 4.21:1 — провал WCAG 1.4.3 AA
     на кегле .9rem. Затемнённый тон того же оттенка даёт 5.55:1. Применяется только
     к тексту (ошибка формы, подпись остатка, метка изменённой строки), --color-primary
     не трогаем: на нём висят бейджи, счётчик корзины и фокусные кольца. */
  --color-primary-ink:#a63a30;
  --color-secondary:#14663f;
  --color-primary-soft:#f8ece9;
  --color-secondary-soft:#edf3ef;
  --color-heading:#141210;
  --color-text:#4a4642;
  --color-link:#141210;
  --color-link-hover:var(--color-primary);
  --color-background:#ffffff;

  --bg:var(--color-background);
  --ink:var(--color-text);
  /* Contrast pass: --muted is the lowest tone still allowed for text (~4.9:1 on paper),
     --faint is a NON-text tone (borders, dots, placeholder plates) at ~3.2:1.
     Plain hex first as a fallback for engines without color-mix(). */
  --muted:#726f6c;
  --muted:color-mix(in srgb,var(--color-text) 78%,transparent);
  --faint:#8f8c8a;
  --faint:color-mix(in srgb,var(--color-text) 62%,transparent);

  --btn-radius:999em;
  --btn-h:3rem;
  --btn-bg:#141210;
  --btn-bg-hover:#000000;
  --rounded:12px;
  --input-height:3rem;
  --tap:2.75rem;           /* 44px — minimum touch target */

  --lsp-display:-0.04em;   /* large headings */
  --lsp-head:-0.022em;     /* small headings */
  --ease:cubic-bezier(.2,.6,.2,1);
  --transition:all .5s var(--ease);

  --wrap:1180px;
  --wrap-narrow:660px;
  --pad:1.25rem;
  --section:64px;
  --hdr-h:5.5rem;          /* sticky header clearance for anchors */
}
@media (min-width:48rem){ :root{ --pad:2rem; --section:110px; } }
@media (min-width:1240px){ :root{ --pad:0rem; } }

/* Высота вьюпорта одним токеном. `svh` объявляем ТОЛЬКО через @supports: значение
   кастомного свойства парсером не проверяется, и неизвестная единица упала бы не здесь,
   а у потребителя (invalid at computed-value time) — вся высота стала бы пустой.
   Тот же приём, что у слайдера. */
:root{--sv:100vh}
@supports (height:1svh){ :root{--sv:100svh} }

/* Высота полосы про cookie ОДНИМ токеном: от неё зависят три соседа сразу —
   нижний отступ body, положение липких панелей покупки и подъём тоста.
   Величина фиксированная, чтобы раскладка была верна и БЕЗ JS; store.js
   уточняет её по факту (крупный системный шрифт, длинный перевод, 400% зум).
   Ниже 48rem текст ложится в две-три строки, а ниже 36rem кнопка уезжает на
   свою строку — отсюда запас.
   Третья ступень (36rem) добавлена по замеру: там к тексту прибавляются целая
   строка кнопки (44px) и отбивка (16px), и 6.5rem не хватало даже на короткую
   фразу — БЕЗ JS полоса накрыла бы липкую кнопку «К оформлению».
   Лучше переоценить: лишний запас — это узкая светлая полоска над плашкой,
   недооценка — недостижимая кнопка. */
:root{--ck-h:4rem}
@media (max-width:47.999rem){ :root{--ck-h:6.5rem} }
@media (max-width:36rem){ :root{--ck-h:8.5rem} }

/* ---------- base safeties ---------- */
*,*::before,*::after{box-sizing:border-box}
/* An author `display` beats the UA `[hidden]` rule (e.g. .cart{display:grid}),
   which is how an empty cart used to keep showing its totals panel. */
[hidden]{display:none!important}
/* scrollbar-gutter: место под полосу прокрутки резервируется всегда, поэтому дровер
   под lockScroll больше не дёргает страницу вбок на ширину полосы (~15px). */
html{-webkit-text-size-adjust:100%;scroll-padding-top:var(--hdr-h);scrollbar-gutter:stable}
body{margin:0;background:var(--bg);color:var(--ink);font-family:var(--font);
  font-size:15px;line-height:1.75;font-weight:400;-webkit-font-smoothing:antialiased;
  text-rendering:optimizeLegibility}
img{max-width:100%;height:auto;display:block}
a{color:var(--color-link);text-decoration:none;transition:color .3s var(--ease)}
a:hover{color:var(--color-link-hover)}
h1,h2,h3,h4{color:var(--color-heading);font-weight:700;line-height:1.12;
  letter-spacing:var(--lsp-head);margin:0 0 .5em}
h1{font-size:clamp(2.1rem,5vw,3.1rem);letter-spacing:var(--lsp-display)}
h2{font-size:clamp(1.4rem,3.4vw,1.9rem)}
h3{font-size:clamp(1.1rem,2.6vw,1.35rem)}
button{font:inherit}
::selection{background:var(--color-heading);color:#fff}
.wrap{max-width:var(--wrap);margin:0 auto;padding-inline:var(--pad)}
@media (max-width:1239px){ .wrap{padding-inline:var(--pad)} }

/* screen-reader-only text */
.vh{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;
  clip-path:inset(50%);white-space:nowrap;border:0}
/* wide tables scroll inside their own box instead of the page */
.tablewrap{overflow-x:auto;-webkit-overflow-scrolling:touch;max-width:100%}

/* ---------- focus + skip link ---------- */
:focus-visible{outline:2px solid var(--color-heading);outline-offset:2px}
.btn:focus-visible,.wchip.is-on:focus-visible,.fchip.is-on:focus-visible,
.wchip[aria-checked="true"]:focus-visible,.fchip[aria-current="true"]:focus-visible,
.pager a.is-on:focus-visible{outline-color:var(--color-primary)}
/* the bar and the drawer overlay are dark — the ink outline would vanish there */
.hdr__in :focus-visible,.drawer:focus-visible{outline-color:#fff}
.search__form input:focus-visible{outline-offset:4px}
.main:focus{outline:none}

.skip{position:absolute;left:-999px}
.skip:focus{left:1rem;top:1rem;z-index:60;background:var(--color-heading);color:#fff;
  padding:.7rem 1.25rem;border-radius:var(--btn-radius);font-size:.85rem;font-weight:600}

.eyebrow{display:block;font-size:.6875rem;font-weight:600;text-transform:uppercase;
  letter-spacing:.22em;color:var(--muted);margin-bottom:1.1rem}

/* ---------- buttons ---------- */
.btn{display:inline-flex;align-items:center;justify-content:center;gap:.4rem;
  height:var(--btn-h);padding:0 2rem;border:1px solid var(--btn-bg);border-radius:var(--btn-radius);
  background:var(--btn-bg);color:#fff;font-weight:600;font-size:.9rem;letter-spacing:-.01em;
  text-transform:none;cursor:pointer;transition:var(--transition);text-align:center;line-height:1}
.btn:hover{background:var(--btn-bg-hover);border-color:var(--btn-bg-hover);color:#fff}
.btn--sm{height:2.25rem;padding:0 1.15rem;font-size:.8rem}
.btn--lg{height:3.25rem;padding:0 2.5rem;width:100%;font-size:.95rem}
.btn--outline{background:transparent;color:var(--color-heading);border-color:var(--color-heading)}
.btn--outline:hover{background:var(--btn-bg-hover);color:#fff}
/* Белая пилюля для тёмной подложки (фото героя): чёрная кнопка на затемнённом
   кадре сливается со скримом и перестаёт читаться как кнопка. */
.btn--light{background:#fff;border-color:#fff;color:var(--color-heading)}
.btn--light:hover{background:var(--color-gray);border-color:var(--color-gray);
  color:var(--color-heading)}
/* disabled buttons still need to be findable by a screen reader, so no pointer-events
   trickery on aria-disabled — only the real attribute is inert */
.btn[disabled],.btn:disabled{opacity:.4;cursor:not-allowed;pointer-events:none}
.btn[aria-disabled="true"]{opacity:.4;cursor:not-allowed}

/* small neutral pill (sign-in method, misc labels) */
.chip{display:inline-block;font-size:.66rem;font-weight:600;text-transform:uppercase;
  letter-spacing:.1em;padding:.2rem .6rem;border-radius:999em;
  border:1px solid var(--color-border);color:var(--color-heading);line-height:1.6}

/* ---------- form controls ---------- */
.sel,
input[type=search],input[type=text],input[type=email],input[type=password],
input[type=tel],input[type=number],input[type=url]{
  width:100%;height:var(--input-height);padding:.55rem 1.25rem;
  border:1px solid var(--color-border);border-radius:var(--btn-radius);
  background:var(--color-background);color:var(--color-heading);font:inherit;font-size:.95rem;
  transition:border-color .3s var(--ease);appearance:none;-webkit-appearance:none}
textarea{width:100%;min-height:6rem;padding:.85rem 1.25rem;border:1px solid var(--color-border);
  border-radius:1.25rem;background:var(--color-background);color:var(--color-heading);font:inherit;
  font-size:.95rem;line-height:1.6;resize:vertical;transition:border-color .3s var(--ease)}
.sel{padding-right:2.75rem;cursor:pointer;
  background-image:url("data:image/svg+xml,%3Csvg width='10' height='6' viewBox='0 0 10 6' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M1 1l4 4 4-4' stroke='%23141210' stroke-width='1.4' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat:no-repeat;background-position:calc(100% - 20px) center}
/* NB: no `outline:none` here — the focus ring is the only cue for keyboard users. */
.sel:focus,input:focus,textarea:focus{border-color:var(--color-heading)}
.sel[disabled]{color:var(--color-heading);opacity:1;background-color:var(--color-gray);cursor:default}
::placeholder{color:var(--muted);opacity:1}
input[type=checkbox],input[type=radio]{width:1.15rem;height:1.15rem;flex:none;
  accent-color:var(--color-heading);cursor:pointer}
.field{display:block;margin:1.15rem 0}
.field__label{display:block;font-size:.7rem;letter-spacing:.14em;text-transform:uppercase;
  color:var(--muted);margin-bottom:.5rem}
.field__hint{display:block;font-size:.8rem;color:var(--muted);margin-top:.4rem;line-height:1.5}
.field input:disabled,.field input[readonly]{background:var(--color-gray);color:var(--color-text)}
/* Ошибка сервера стоит под своим полем, а не общей плашкой сверху: иначе непонятно,
   что именно исправлять. Тон затемнён ради контраста — см. --color-primary-ink. */
.field--err input,.field--err textarea{border-color:var(--color-primary-ink)}
.field__err{display:block;margin-top:.4rem;font-size:.8rem;line-height:1.5;color:var(--color-primary-ink)}
/* Выбор помола: подпись и компактный селект в одну строку. Полноширинная пилюля
   высотой 48px выглядела как главное поле страницы, хотя это вторичный выбор. */
.field--grind{display:flex;align-items:center;gap:.75rem;flex-wrap:wrap}
.field--grind .field__label{margin:0}
.field--grind .field__hint{flex-basis:100%;margin-top:0}
.sel--sm{width:auto;min-width:11rem;max-width:100%;height:2.5rem;font-size:.85rem;
  padding:0 2.5rem 0 1.05rem;background-color:var(--color-gray);border-color:transparent;
  background-position:calc(100% - 14px) center}
.sel--sm:hover{border-color:var(--color-border)}
.req{color:var(--color-primary)}

/* ---------- header (sticky, hairline) ---------- */
.hdr{background:var(--header-bg);border-bottom:1px solid transparent;
  position:sticky;top:0;z-index:30;transition:box-shadow .3s var(--ease),border-color .3s var(--ease)}
.hdr.is-stuck{box-shadow:0 1px 24px rgba(0,0,0,.35);border-color:transparent}
/* the bar is dark; the search panel below it keeps the light skin, so the
   inverted colours are scoped to .hdr__in rather than the whole header. */
.hdr__in{max-width:var(--wrap);margin:0 auto;padding-block:.9rem;padding-inline:var(--pad);
  display:flex;align-items:center;gap:1rem;color:var(--header-ink)}
@media (max-width:1239px){ .hdr__in{padding-inline:var(--pad)} }
.hdr__nav{display:none}
.hdr__brand{display:inline-flex;align-items:center;gap:.6875rem;flex:0 1 auto;min-width:0;
  color:var(--header-ink);transition:opacity .25s var(--ease)}
.hdr__brand:hover{opacity:.6;color:var(--header-ink)}
.hdr__badge{width:2.5rem;height:2.5rem;flex:none;border-radius:50%;display:block}
.hdr__name{font-weight:800;font-size:1.0625rem;letter-spacing:-.02em;line-height:1.1;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.hdr__actions{display:flex;align-items:center;gap:.65rem;margin-left:auto}
.ic{position:relative;background:none;border:0;color:var(--header-ink);padding:.3rem;cursor:pointer;
  display:grid;place-items:center;transition:opacity .25s var(--ease)}
.ic:hover{opacity:.55;color:var(--header-ink)}
.lang{display:flex;gap:.3rem;font-size:.75rem;letter-spacing:.04em;margin-left:.2rem}
/* Narrow screens: "Toggle Coffee" must read in full, as it does on menu.toggle.coffee,
   so the bar gives up RU/EN (it is in the drawer too) and tightens its gaps. */
@media (max-width:599px){
  .hdr .lang{display:none}
  .hdr__in{gap:.6rem}
  .hdr__actions{gap:.4rem}
  .hdr__burger{width:1.6rem}
}
.lang a{color:rgba(255,255,255,.72)}
.lang a:hover{color:var(--header-ink)}
.lang a.is-on{color:var(--header-ink);font-weight:600}
.hdr__burger{background:none;border:0;padding:.35rem;cursor:pointer;display:grid;gap:5px;width:1.9rem;
  position:relative}
.hdr__burger span{height:1.5px;background:var(--header-ink)}

.ic__count{position:absolute;top:-3px;right:-5px;min-width:15px;height:15px;padding:0 3px;
  border-radius:999px;background:var(--color-primary);color:#fff;font-size:.62rem;font-weight:700;
  line-height:15px;text-align:center}
.ic__dot{position:absolute;bottom:3px;right:3px;width:6px;height:6px;border-radius:50%;
  background:var(--color-secondary);border:1.5px solid var(--header-bg)}
.hdr__avatar{border-radius:50%;object-fit:cover}
.ic--cart.is-bump{animation:cartbump .45s var(--ease)}
@keyframes cartbump{30%{transform:scale(1.22)}60%{transform:scale(.94)}}

/* ---------- search ----------
   Панель лежит ВНУТРИ <header> соседом от .hdr__in. Инверсия цветов живёт на .hdr__in,
   а не на .hdr, — только поэтому панель остаётся светлой. НИ ОДНО правило этой секции
   не имеет права трогать .hdr / .hdr__in. */

/* Видимость. Без JS панель видна статически: это единственный работающий поиск в шапке,
   и обычная GET-форма на /shop/ обязана сабмититься. Блок подсказок при этом скрыт —
   без клиента он не переключается и превратился бы в мебель на каждой странице.
   Тень заменяет затемнение фона: панель читается как слой поверх, ничего не блокируя. */
.search{background:var(--color-gray);border-top:1px solid var(--hair);
  box-shadow:0 12px 28px rgba(20,18,16,.07)}
html.js .search{display:none}
html.js .search.is-open{display:block}
html:not(.js) .search__results{display:none}

/* Форма: белая пилюля с полем, очистка ВНУТРИ пилюли, «Закрыть» — снаружи. */
.search__form{max-width:var(--wrap);margin:0 auto;padding-block:.85rem;padding-inline:var(--pad);
  display:flex;align-items:center;gap:.7rem;color:var(--color-heading)}
.search__field{flex:1 1 auto;min-width:0;display:flex;align-items:center;gap:.6rem;
  height:var(--input-height);padding-inline:1rem;background:var(--color-background);
  border:1px solid var(--color-border);border-radius:var(--btn-radius);
  transition:border-color .3s var(--ease)}
.search__field:focus-within{border-color:var(--color-heading)}
.search__icon{flex:none;color:var(--muted)}
/* Глобальное правило полей (input[type=search]) задаёт padding .55rem 1.25rem, height,
   рамку и фон — внутри пилюли всё это нужно сбросить ЯВНО, иначе оно течёт внутрь.
   Ровно эта ловушка когда-то съела цифру в степпере количества.
   Кегль 16px безусловен: иначе iOS зумит в поле на 768px и обратно не отъезжает,
   а разница с прежними 1.05rem — четверть пикселя. Поле само по себе — цель 44px. */
.search__form input[type=search]{flex:1 1 auto;min-width:0;width:auto;
  height:auto;min-height:var(--tap);padding:0;border:0;background:none;border-radius:0;
  color:var(--color-heading);font-size:16px;line-height:1.25}
.search__form input[type=search]:focus{border:0}
/* Нативный крестик WebKit стоит вплотную к нашему — два «×» подряд читаются как ошибка. */
.search__form input[type=search]::-webkit-search-cancel-button{display:none}
.search__clear{flex:none;width:var(--tap);height:var(--tap);margin-right:-.65rem;
  display:grid;place-items:center;background:none;border:0;color:var(--muted);
  font-size:1.25rem;line-height:1;cursor:pointer}
.search__clear:hover{color:var(--color-heading)}
/* Словом промахнуться труднее, чем глифом, и «очистить» с «закрыть» больше не один
   элемент на два намерения. */
.search__close{flex:none;min-height:var(--tap);padding-inline:.9rem;background:none;border:0;
  color:var(--muted);font-size:.85rem;font-weight:600;cursor:pointer}
.search__close:hover{color:var(--color-heading)}

/* Выдача. Потолок высоты БЕЗУСЛОВЕН, а не мобильный: панель лежит внутри
   position:sticky шапки, и всё, что ушло ниже вьюпорта, недостижимо ничем — ни колесом,
   ни клавиатурой (на iPad и на ноутбуке 1280×720 туда уезжали хвостовые ссылки).
   Белая поверхность идёт во всю ширину, колонку держат внутренние обёртки: тёплая
   полоса остаётся только под полем, и выдача не растворяется в сетке карточек
   каталога — у .pc__media тот же тон --color-gray. */
.search__results{background:var(--color-background);border-top:1px solid var(--hair);
  padding-block:0 .75rem;
  max-height:min(60vh,26rem);overflow-y:auto;overscroll-behavior:contain}
@supports (height:1svh){ .search__results{max-height:min(60svh,26rem)} }
/* Только padding-block + отдельный padding-inline: шорткат обнулил бы боковые отступы. */
.sr__intro,.sr__out{max-width:var(--wrap);margin:0 auto;padding-inline:var(--pad)}

/* Блок по умолчанию (категории) и заголовки групп. */
.sr__intro{padding-block:.9rem 1rem}
.sr__g{margin:0 0 .6rem;font-size:.7rem;font-weight:600;text-transform:uppercase;
  letter-spacing:.14em;color:var(--muted)}
.sr__n{color:var(--faint);letter-spacing:.06em}
.sr__chips{display:flex;flex-wrap:wrap;gap:.5rem}
.sr__chips .chip{display:inline-flex;align-items:center;gap:.35rem;
  transition:border-color .25s var(--ease),color .25s var(--ease)}
.sr__chips .chip:hover{border-color:var(--color-heading)}
.sr__group{padding-block:.6rem .4rem;border-top:1px solid var(--hair)}
.sr__group:first-child{border-top:0}
.sr__note{margin:0;padding-block:.7rem;color:var(--muted);font-size:.85rem}

/* Строка результата. Средняя колонка minmax(0,1fr) обязательна: без неё грид-ячейка
   не сжимается ниже содержимого, и длинное название выдавливает цену за край. */
.sr__row{display:grid;grid-template-columns:44px minmax(0,1fr) auto;gap:.9rem;
  align-items:center;padding-block:.5rem;min-height:var(--tap);
  color:var(--color-text);border-radius:8px}
.sr__row.is-out{opacity:.55}
.sr__media{width:44px;height:44px;flex:none;border-radius:8px;overflow:hidden;
  background:var(--color-gray)}
/* Плейсхолдер вместо фото: приглушённый самолётик из фирменного круга не годится по весу,
   поэтому — две буквы фирменным начертанием, как на карточке каталога. */
.sr__media--ph::before{content:"TC";display:grid;place-items:center;width:100%;height:100%;
  font:800 .62rem/1 var(--font);letter-spacing:.06em;color:var(--faint)}
.sr__media--wide{height:30px}          /* обложка статьи, 3/2 — ширина колонки та же,
                                          иначе тексты двух групп не встанут по вертикали */
.sr__media img{width:100%;height:100%;object-fit:cover}
.sr__body{min-width:0}
.sr__t{display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;
  font-weight:600;letter-spacing:-.02em;color:var(--color-heading);line-height:1.35;
  overflow-wrap:break-word}
.sr__m{display:block;margin-top:.15rem;font-size:.7rem;text-transform:uppercase;
  letter-spacing:.14em;color:var(--muted);white-space:nowrap;overflow:hidden;
  text-overflow:ellipsis}
.sr__side{display:flex;flex-direction:column;align-items:flex-end;gap:.15rem;text-align:right}
.sr__price{font-weight:600;color:var(--color-heading);font-size:.9rem;white-space:nowrap}
.sr__stock{font-size:.68rem;text-transform:uppercase;letter-spacing:.1em;color:var(--color-primary-ink)}
.sr__off{font-size:.68rem;font-weight:700;color:var(--color-primary)}
/* Хвост группы: медиа нет, но текст обязан стоять на той же вертикали, что и названия. */
.sr__all{grid-template-columns:minmax(0,1fr);padding-left:calc(44px + .9rem)}
.sr__all .sr__t{color:var(--color-heading);font-size:.85rem}

/* Наведение и активная строка комбобокса. Активность помечена НЕ только цветом
   (фон + вертикальная черта), чтобы читалась и в монохроме. */
.sr__row:hover{color:var(--color-text);background:var(--color-gray)}
.sr__row[aria-selected="true"]{background:var(--color-gray);
  box-shadow:inset 2px 0 0 var(--color-heading)}
/* Терракота дозирована: подсвечиваем фоном, цвет текста не трогаем. */
.sr__hi{background:rgba(193,74,63,.12);color:inherit;border-radius:2px;padding:0 .12em}

/* Загрузка и пустое состояние. Скелетоны переиспользуют .sk/.sk--line/.sk--price —
   они уже уважают prefers-reduced-motion, новых анимаций не заводим. */
.search__results[aria-busy="true"]{opacity:.55;transition:opacity .2s var(--ease)}
.sr__row--sk{pointer-events:none}
.sr__sk2{width:55%;margin-top:.35rem}
.sr__empty{padding-block:.9rem 1rem}     /* ТОЛЬКО padding-block: padding-inline уже
                                            дал родитель .sr__out, иначе двойной отступ */
.sr__empty-t{margin:0;color:var(--color-heading);font-weight:600}
.sr__empty-x{margin:.35rem 0 .9rem;color:var(--muted);font-size:.85rem}
/* <mark> НЕ переопределяем: в forced-colors он сам подхватывает системные Mark/MarkText,
   а наш фон там всё равно отбрасывается. */
@media (forced-colors:active){
  .sr__row[aria-selected="true"]{background:Highlight;color:HighlightText;forced-color-adjust:none}
  .search__field{border-color:CanvasText}
}

/* ---------- drawer ---------- */
.drawer{position:fixed;inset:0;z-index:50;background:rgba(20,18,16,.4);backdrop-filter:blur(2px)}
.drawer__panel{position:absolute;inset:0 22% 0 0;max-width:23rem;background:#fff;color:var(--color-heading);
  padding:3.5rem 1.75rem 2rem;overflow:auto}
.drawer__close{position:absolute;top:.75rem;right:1rem;background:none;border:0;
  color:var(--color-heading);font-size:1.9rem;line-height:1;cursor:pointer}
.drawer__nav{display:flex;flex-direction:column}
.drawer__head{font-weight:600;font-size:1.05rem;letter-spacing:-.03em;padding:.85rem 0;
  border-bottom:1px solid var(--hair)}
.drawer__sub{padding:.55rem 0 .55rem .85rem;color:var(--muted);display:flex;justify-content:space-between;font-size:.95rem}
.drawer__sub:hover{color:var(--color-heading)}
.drawer__lang{margin-top:1.75rem;display:flex;gap:1.25rem;font-size:.9rem}
.drawer__lang a{color:var(--muted)}
.drawer__lang a.is-on{color:var(--color-heading);font-weight:600}
.n{color:var(--muted);font-size:.8em}

/* ---------- main / hero ---------- */
.main{min-height:60vh;padding-bottom:var(--section)}
.hero{padding-block:clamp(3rem,9vw,7rem) clamp(2.5rem,6vw,4rem)}
.hero__title{font-size:clamp(2.6rem,8vw,4.75rem);font-weight:800;letter-spacing:-.045em;
  line-height:1;margin:0 0 .5rem;color:var(--color-heading)}
.hero__lead{color:var(--muted);max-width:34rem;margin:0;font-size:1.05rem;line-height:1.65}
/* margin-block-start, never padding on .hero — a padding shorthand would wipe
   the .wrap padding-inline and glue the copy to the screen edge. */
.hero__cta{display:flex;flex-wrap:wrap;gap:.75rem;margin-block-start:2rem}
.page-head{padding-block:clamp(2.5rem,6vw,4.5rem) 1.25rem}
.page-head h1{margin:.1em 0}
.page-head__note{color:var(--muted);margin:.5rem 0 0;max-width:40rem}

/* ---------- home: hero slider ---------- */
/* Полноширинная полоса. .hslider — прямой ребёнок .main, а .main ширину не ограничивает,
   поэтому полная ширина берётся сама собой: ни 100vw (даёт горизонтальную прокрутку при
   видимом скроллбаре), ни отрицательных margin не нужно. Текст внутри держит меру .wrap.
   ВНИМАНИЕ: вертикальные отступы здесь и ниже — только padding-block; шорткат padding:X 0
   обнулил бы padding-inline у .wrap и приклеил копию к краю экрана на мобильном. */
/* Высота полосы — ОДНА непрерывная функция вьюпорта, без ступеней aspect-ratio.
   125vw            — портретная ветвь: ровно прежние 4/5 на телефонах до 442px;
   30rem + 16.5vw   — пологая ветвь планшета и десктопа;
   обе прямые пересекаются в W = 442.4px, поэтому обрыва нет ПО ПОСТРОЕНИЮ;
   76svh            — потолок по высоте вьюпорта (ландшафт телефона, низкое окно, зум);
   16rem            — абсолютный пол: при 400% зума полоса может быть выше вьюпорта,
                      вертикальная прокрутка по WCAG 1.4.10 разрешена, потеря текста — нет.
   Ни одного пикселя из изображения: CLS ноль по построению, как и раньше.
   svh, а не dvh: svh не меняется при сворачивании адресной строки, полоса не «дышит».

   svh задаётся ТОЛЬКО через @supports. Приём «правило-предшественник», которым в этом
   файле лечат max-height, для кастомного свойства НЕ работает: значение custom property
   не проверяется парсером, оно принимается как поток токенов и падает уже у потребителя
   (var() → invalid at computed-value time), поэтому предыдущее объявление не спасает. */
.hslider{position:relative;
  --hs-h:max(16rem, min(125vw, 30rem + 16.5vw, 76vh));
  --hs-pad:clamp(.5rem, var(--hs-h) * .028, 1.6rem);
  --hs-air:clamp(.45rem, var(--hs-h) * .022, .85rem);
  /* Дополнительный воздух под копией. Растёт от высоты полосы и обнуляется на
     короткой: при --hs-h ≤ 18rem (ландшафт телефона, 400% зума) запас под текстом
     и так всего десятки пикселей, и любой лишний отступ вернул бы срезание. */
  --hs-foot:clamp(0px, (var(--hs-h) - 18rem) * .18, 4rem)}
@supports (height:1svh){
  .hslider{--hs-h:max(16rem, min(125vw, 30rem + 16.5vw, 76svh))}
}

/* Высота приходит ОДНИМ значением --hs-h и считается только из вьюпорта — ни одного
   пикселя из изображения, поэтому сдвига вёрстки при загрузке фото нет по построению.
   Тёмная плита под слайдами делает белый текст читаемым ещё до прихода JPEG
   (и оставляет слайд осмысленным, если фото не пришло вовсе).
   min-height — только страховка на случай, если var(--hs-h) не разрешится (движок без
   min()/max(): var() тогда даёт не предыдущее объявление, а initial, и полоса
   схлопнулась бы в ноль). width не задаётся: без aspect-ratio у блочного div ширина
   auto = полная, переносить высоту в ширину больше нечему. */
.hslider__viewport{position:relative;overflow:hidden;
  height:var(--hs-h);min-height:15rem;
  background:var(--color-heading);touch-action:pan-y pinch-zoom}

/* Слои слайдов. Переход — только opacity: горизонтальные сдвиги провоцируют overflow
   и дёрганье на мобильном. visibility убирает неактивный слайд из порядка табуляции,
   поэтому переключается без задержки при показе и с задержкой при уходе. */
.hs__slide{position:absolute;inset:0;opacity:0;visibility:hidden;--hs-scrim:.75;
  transition:opacity .6s var(--ease),visibility 0s linear .6s}
.hs__slide.is-on{opacity:1;visibility:visible;z-index:2;
  transition:opacity .6s var(--ease),visibility 0s linear 0s}
.hs__slide--soft{--hs-scrim:.62}
.hs__slide--normal{--hs-scrim:.75}
.hs__slide--strong{--hs-scrim:.88}

.hs__media{position:absolute;inset:0}
/* Точка фокуса кадрирования приходит из разметки парой кастомных свойств --hs-fx/--hs-fy
   (шаблон печатает style="--hs-fx:X%;--hs-fy:Y%"), а не готовым object-position: так
   значение по умолчанию 50%/50% живёт здесь, в CSS, и слайд без фокуса кадрируется
   одинаково и до правки шаблона, и после. */
.hs__img{width:100%;height:100%;object-fit:cover;
  object-position:var(--hs-fx,50%) var(--hs-fy,50%);display:block}

/* Копия прижата к низу. Её нижний отступ перекрывает панель управления целиком:
   высота панели (44px) + её собственный нижний отступ + воздух. Считать «на глаз»
   нельзя — при восьми слайдах ряд точек становится шире колонки текста и заезжает
   под лид, а на мобильном панель вообще стоит по центру прямо под копией. */
.hs__body{position:absolute;inset:0;z-index:1;display:grid;
  /* без явной колонки неявный трек auto = min-content содержимого: одно длинное слово
     раздувало трек и выносило всю колонку за правый край полосы */
  grid-template-columns:minmax(0,1fr);
  /* align-content:end, а НЕ safe end: safe при переполнении роняет вниз кнопку CTA
     и кладёт панель поверх лида, то есть меняет отказ на худший. Переполнения по новой
     раскладке не бывает — максимум копии ограничен line-clamp у всех четырёх элементов
     и посчитан: запас неотрицателен во всей матрице. */
  align-content:end;
  /* Видимой навигации у слайдера нет, резервировать под неё высоту больше не нужно —
     прежние 44px тач-цели вернулись тексту. Остаётся собственный нижний отступ полосы
     плюс --hs-foot: дополнительный воздух, который на короткой полосе обнуляется сам. */
  padding-block-end:calc(var(--hs-pad) + var(--hs-air) + var(--hs-foot))}
/* .wrap несёт margin:0 auto. У грид-элемента авто-поля отменяют растяжение: коробка
   сжимается по содержимому и уезжает в центр — колонка текста переставала совпадать
   с вертикалью остальных секций. width:100% возвращает меру на место. */
.hs__body>.wrap{width:100%}
.hs__copy{position:relative;min-width:0;
  /* мера копии одной формулой: 100% на телефоне, 416px на 576, 497 на 768,
     607 на 1024, потолок 38rem — те же опорные точки, что были ступенями,
     но без скачков 535→416 на 576px и 416→608 на 1024px */
  max-width:min(100%, 38rem, max(26rem, 10.5rem + 42.9vw))}
/* Тень текста контраст не решает, но снимает «звон» белого на светлом пятне кадра.
   Кегли считаются из ВЫСОТЫ ПОЛОСЫ, а не только из ширины: min(ширинный член,
   высотный член). На нормальном вьюпорте правит ширинный (монотонная лестница
   без прежних инверсий 41.6→35.2 на 576 и 48→45.1 на 1024), а на короткой полосе
   управление молча забирает высотный, и копия сама садится под доступное место:
   на 812×375 заголовок уходит с 45.5 до 26.2px, потому что полоса 285px, — без
   единого медиазапроса. Ступенчатые правила кегля на 36/48/64rem поэтому удалены. */
.hs__eyebrow{color:rgba(255,255,255,.88);
  line-height:1.35;
  margin-bottom:clamp(.25rem, var(--hs-h) * .024, .9rem);
  /* потолок в две строки: без него надзаголовок был единственным элементом копии
     без ограничения, и «худший слайд» переставал быть конечной величиной */
  display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2;line-clamp:2;
  overflow:hidden;
  text-shadow:0 1px 22px rgba(0,0,0,.45)}
.hs__title{font-size:clamp(1.15rem, min(1rem + 2.75vw, var(--hs-h) * .083), 3.5rem);
  font-weight:800;letter-spacing:-.045em;line-height:1.05;
  margin:0 0 clamp(.25rem, var(--hs-h) * .022, .8rem);
  color:#fff;
  /* anywhere, а не break-word: только anywhere уменьшает min-content трека, поэтому
     только он реально переносит длинное слово, а не просто разрешает обрезку */
  overflow-wrap:anywhere;
  text-shadow:0 1px 22px rgba(0,0,0,.45);
  /* Иначе длинный заголовок срезается сверху молча, без многоточия. */
  display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:3;line-clamp:3;overflow:hidden}
.hs__lead{margin:0;color:rgba(255,255,255,.88);
  font-size:clamp(.8rem, min(.72rem + .55vw, var(--hs-h) * .032), 1.05rem);
  line-height:1.5;
  overflow-wrap:anywhere;
  text-shadow:0 1px 22px rgba(0,0,0,.45);
  display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:3;line-clamp:3;
  overflow:hidden}
/* Кнопка не растягивается на всю ширину: у нас пилюли, полноширинная выглядит чужеродно. */
.hs__cta{display:flex;flex-wrap:wrap;gap:.6rem;
  margin:clamp(.45rem, var(--hs-h) * .032, 1.5rem) 0 0}
/* .btn задаёт height:var(--btn-h)=48px — фиксированную, поэтому прежний min-height:var(--tap)
   (44px) не мог сработать ни при каких условиях, а длинная подпись кнопки вылезала из пилюли
   боком. Отпускаем высоту и разрешаем перенос: тач-цель тогда держит min-height, а не height. */
.hs__cta .btn{height:auto;min-height:var(--btn-h);padding-block:.55rem;
  line-height:1.25;max-width:100%;overflow-wrap:anywhere}

/* Панель управления лежит поверх всей полосы и сама кликов не ловит — иначе она
   перекрыла бы ссылку-кнопку слайда. Кликабельны только сами кнопки. */
.hs__ctrl{position:absolute;inset:0;z-index:3;pointer-events:none}
/* Панель — одной строкой у нижнего края, в мере .wrap: ‹ • • • › ▮▮ (порядок совпадает
   с порядком в разметке). Стрелки НЕ вынесены к краям полосы намеренно: ниже 1240px
   у .wrap боковое поле всего 32px, круглая стрелка 44px в него не помещается и наезжала
   на заголовок слайда. Держать текст в мере и одновременно ставить стрелки к краям
   на этих ширинах нельзя — выбран текст, он важнее.
   nowrap обязателен: при переносе flexbox сначала уводит кнопку паузы на вторую строку
   и только потом сжимает — панель вырастала вверх и наезжала на кнопку слайда. */
.hs__ctrl .wrap{height:100%;display:flex;align-items:flex-end;justify-content:flex-end;
  padding-block-end:var(--hs-pad)}



/* Пауза обязательна по WCAG 2.2.2: движение стартует само и длится дольше 5 с,
   наведение и фокус механизм остановки не заменяют.
   Фон .55 — по той же причине, что у стрелок: 4.38:1 с белым глифом на белом кадре. */
/* Пауза приходит из разметки с классом .vh — визуально её нет. Она обязана существовать
   по WCAG 2.2.2: движение стартует само и длится дольше 5 секунд, а наведение и фокус
   механизм остановки не заменяют. Проявляется только при переходе на неё с клавиатуры:
   .hs__play.vh:focus-visible это (0,3,0) против (0,1,0) у .vh, поэтому перебивает без
   !important. Мышью и пальцем её не достать — это и есть «навигации не видно». */
/* Скрытие обязано быть на ПАРЕ классов: .vh (0,1,0) объявлен выше по файлу, чем
   .hs__play (0,1,0), и при равной специфичности побеждает последний — кнопка оставалась
   видимой во все 44×44. .hs__play.vh это (0,2,0), оно перебивает без !important. */
.hs__play.vh{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;
  clip-path:inset(50%);white-space:nowrap;border:0}
.hs__play.vh:focus-visible{position:static;width:var(--tap);height:var(--tap);margin:0;
  overflow:visible;clip-path:none;white-space:normal;
  border:1px solid rgba(255,255,255,.5)}
.hs__play{flex:none;width:var(--tap);height:var(--tap);padding:0;
  border:1px solid rgba(255,255,255,.5);border-radius:50%;
  background:rgba(12,10,9,.55);color:#fff;font-size:.62rem;line-height:1;
  letter-spacing:.06em;cursor:pointer;pointer-events:auto;display:grid;place-items:center;
  transition:background-color .3s var(--ease),border-color .3s var(--ease)}
.hs__play:hover{background:rgba(12,10,9,.62);border-color:#fff;color:#fff}
.hs__play.is-paused{background:rgba(12,10,9,.62);border-color:rgba(255,255,255,.75);
  font-size:.72rem}

/* Кольцо фокуса на фотографии — двухслойное. Одного белого мало: оно невидимо и на
   белой пилюле .btn--light, и там, где панель оказалась на светлом кадре. Второй слой —
   тёмная тень; в forced-colors тени гасятся, там работу берёт на себя блок ниже. */
.hslider :focus-visible{outline:2px solid #fff;outline-offset:3px;
  box-shadow:0 0 0 5px rgba(12,10,9,.85)}

/* Единственная ступень сжатия — по ВЫСОТЕ: ландшафт телефона, низкое окно ноутбука,
   200–400% зума. Число строк — единственная величина, которую нельзя интерполировать,
   поэтому она и осталась медиазапросом, а кегли и отступы текут сами.
   Гейт без orientation: портретный 400%-зум (192×256, 320×192, 256×341) обязан сюда
   попадать, а orientation:landscape его пропускал. min-aspect-ratio:5/8 отделяет
   «полосу ограничивает высота» от «полосу ограничивает ширина»: полоса высото-ограничена
   при W/H > 0.608, а 5/8 = 0.625 — практически та же линия. Портретные телефоны матрицы
   (280×653 = 0.43 … 430×932 = 0.46) сюда не попадают, 568×320 = 1.78 и 192×256 = 0.75 —
   попадают.
   max-height в медиазапросе меряет БОЛЬШОЙ вьюпорт, а полоса живёт на svh — и это
   осознанно: на мобильном портрете правило не сработает, и не должно (запас там
   +42…+201px даже при полных 3+3); в ландшафте iOS показывает компактную панель,
   svh ≈ lvh; при зуме на десктопе панелей нет вовсе. Гейт надёжен ровно там, где нужен.
   Проверка непрерывности: 932×545 (гейт не сработал, 3+3) даёт запас +74.6,
   932×544 (сработал, 2+2) даёт +109 — скачок только вверх, контент не теряется. */
@media (max-height:34rem) and (min-aspect-ratio:5/8){
  .hs__title{-webkit-line-clamp:2;line-clamp:2}
  .hs__lead{-webkit-line-clamp:2;line-clamp:2}
}

/* Windows High Contrast: белый текст принудительно перекрашивается в CanvasText и
   ложится поверх фотографии, которую forced-colors не убирает. Фото декоративное
   (alt=""), поэтому его и убираем — оно единственная причина, по которой текст белый.
   На геометрию это не влияет: высоту держит --hs-h, а не изображение.
   ПРОВЕРИТЬ ВРУЧНУЮ в реальном Windows High Contrast перед деплоем: поведение
   background-image (градиентов скрима) в этом режиме различается между движками. */
@media (forced-colors:active){
  .hs__media{display:none}
  .hslider__viewport{background:Canvas;border-block:1px solid CanvasText}
  .hs__title,.hs__lead,.hs__eyebrow{color:CanvasText;text-shadow:none}
  .hs__play{background:Canvas;color:CanvasText}
}

/* Пользователь попросил больше контраста — герой обязан ответить: он весь построен
   на полупрозрачностях. Скрим поднимается до верхней ступени, лид и надзаголовок
   становятся чистым белым. Числа выше при этом только улучшаются (soft .62 → .88
   даёт 14.8:1 вместо 5.6:1), то есть заявленный контраст остаётся нижней границей. */
@media (prefers-contrast:more){
  .hs__slide{--hs-scrim:.88}
  .hs__eyebrow,.hs__lead{color:#fff}
}

/* Печать. В paged media единицы вьюпорта считаются от страничного бокса, поэтому на A4
   (794×1123) полоса выходила 611px — 54% первого листа под декоративное фото и кнопки.
   На бумаге от героя нужен только текст. */
@media print{
  .hslider__viewport{height:auto;min-height:0}
  .hs__media,.hs__ctrl{display:none}
  .hs__slide{position:static;opacity:1;visibility:visible}
  .hs__slide:not(.is-on){display:none}
  .hs__body{position:static;padding-block:0}
  .hs__title,.hs__lead,.hs__eyebrow{color:#000;text-shadow:none;
    -webkit-line-clamp:none;line-clamp:none;display:block}
}

/* ---------- юридические блоки витрины ----------
   Пять классов печатались шаблонами без единого правила: блоки выпадали из ритма,
   набираясь кеглем основного текста. Все — служебные примечания, поэтому один тон. */
.checkout__terms,.acct__rights,.auth__ydata,.order__returns,.order__legal{
  font-size:.85rem;line-height:1.6;color:var(--muted)}
.checkout__terms{margin:.9rem 0 0}
.checkout__terms a,.acct__rights a,.order__returns a,.order__legal a{color:var(--color-heading);
  text-decoration:underline;text-decoration-color:var(--color-border);
  text-underline-offset:.18em}
.checkout__terms a:hover,.acct__rights a:hover,.order__returns a:hover,.order__legal a:hover{
  text-decoration-color:var(--color-heading)}
/* Карточка «Ваши данные и права» в личном кабинете — та же плита, что у прочих карточек. */
.acct__rights{margin-top:var(--section);border-top:1px solid var(--hair);padding-top:1.25rem}
.acct__rights h2{font-size:.7rem;text-transform:uppercase;letter-spacing:.14em;
  color:var(--muted);margin:0 0 .6rem}
.auth__ydata{margin:.6rem 0 0}
.order__returns,.order__legal{margin:1.25rem 0 0}

/* ---------- sections ---------- */
.sect{padding-block:calc(var(--section) / 2)}
.sect__head{display:flex;align-items:end;justify-content:space-between;gap:1rem;
  border-bottom:1px solid var(--color-heading);padding-bottom:.6rem;margin-bottom:2rem}
.sect__head h2{font-size:clamp(1.15rem,2.6vw,1.6rem);letter-spacing:-.03em;margin:0}
.sect__all{font-size:.8rem;color:var(--muted);white-space:nowrap;padding-bottom:.1rem}
.sect__all:hover{color:var(--color-heading)}
.empty{color:var(--muted);padding:2.5rem 0}

/* ---------- home: where to start / why Toggle ---------- */
.starter{padding-block:calc(var(--section) / 2) 0}
.starter__grid{display:grid;gap:1rem;grid-template-columns:1fr;list-style:none;margin:0;padding:0}
.starter__i{display:block;border:1px solid var(--color-border);border-radius:var(--rounded);
  padding:1.25rem 1.4rem;color:inherit;height:100%;
  transition:border-color .3s var(--ease),background-color .3s var(--ease)}
.starter__i:hover{border-color:var(--color-heading);color:inherit}
.starter__t{display:block;font-weight:600;letter-spacing:-.02em;color:var(--color-heading)}
.starter__x{display:block;color:var(--muted);font-size:.85rem;margin-top:.25rem;line-height:1.55}
.why{display:grid;gap:1.75rem;grid-template-columns:1fr;list-style:none;margin:0;padding:0}
.why__i{border-top:1px solid var(--color-heading);padding-top:1rem}
.why__t{font-weight:700;letter-spacing:-.02em;color:var(--color-heading);display:block}
.why__t a{color:inherit;text-decoration:underline;text-decoration-color:var(--color-border);
  text-underline-offset:3px}
.why__t a:hover{text-decoration-color:var(--color-heading)}
.why__x{color:var(--muted);font-size:.9rem;margin:.35rem 0 0;line-height:1.6}

/* ---------- catalog grid + product card ---------- */
.grid{display:grid;gap:clamp(2rem,4vw,3rem) clamp(1rem,2.5vw,2rem);grid-template-columns:repeat(2,1fr);
  list-style:none;margin:0;padding:0}
.grid--3{grid-template-columns:repeat(2,1fr)}
.grid--list{grid-template-columns:1fr;gap:0}
.grid>li{display:flex;flex-direction:column;min-width:0}
.grid>li>.pc{flex:1 1 auto}
.pc{display:flex;flex-direction:column;position:relative;min-width:0}
.pc--out{opacity:.55}
/* The stretched link is anchored to the LINK box (media + text), not to the whole
   card, so the chips, meters and buy button below it stay easy to hit. */
.pc__link{position:relative;display:flex;flex-direction:column;color:inherit}
.pc__link::after{content:"";position:absolute;inset:0;z-index:1}
.pc__media{aspect-ratio:1/1;background:var(--color-gray);border-radius:var(--rounded);
  overflow:hidden;display:grid;place-items:center}
.pc__media img{width:100%;height:100%;object-fit:cover;transition:transform .7s var(--ease)}
.pc:hover .pc__media img{transform:scale(1.035)}
.pc__ph{font-weight:700;letter-spacing:.16em;color:var(--color-gray-2);font-size:.7rem;text-transform:uppercase}
.pc__body{flex:1 1 auto;padding-top:.9rem;display:flex;flex-direction:column;gap:.2rem}
.pc__body>*{overflow-wrap:break-word;min-width:0}
.pc__country{font-size:.66rem;text-transform:uppercase;letter-spacing:.16em;color:var(--muted)}
.pc__name{margin:0;font-size:.98rem;font-weight:600;line-height:1.35;letter-spacing:-.02em;
  color:var(--color-heading);display:inline-block;
  background-image:linear-gradient(currentColor,currentColor);
  background-size:0 1px;background-position:0 100%;background-repeat:no-repeat;transition:background-size .4s var(--ease)}
.pc:hover .pc__name{background-size:100% 1px}
.pc__proc{font-size:.72rem;color:var(--muted);text-transform:lowercase}
.pc__sub{color:var(--muted);font-size:.83rem;line-height:1.5;margin-top:.15rem;
  display:-webkit-box;-webkit-line-clamp:2;line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;
  min-height:2.5em}
.pc__oos{color:var(--muted);font-size:.78rem;text-transform:uppercase;letter-spacing:.1em}

/* merchandising badges */
/* не больше двух: третий бейдж закрывал товар на фото */
.pc__badges .pcb:nth-child(n+3){display:none}
.pc__badges{position:absolute;top:10px;left:10px;z-index:2;pointer-events:none;display:flex;flex-direction:column;
  align-items:flex-start;gap:.25rem}
.pdp__badges{display:flex;flex-wrap:wrap;gap:.35rem;margin-bottom:.85rem}
.pcb{font-size:.6rem;font-weight:600;text-transform:uppercase;letter-spacing:.1em;
  background:rgba(255,255,255,.94);color:var(--color-heading);border:1px solid var(--color-border);
  padding:.2rem .5rem;border-radius:999em;line-height:1.5}
.pcb--new,.pcb--week{border-color:var(--color-heading);color:var(--color-heading)}
.pcb--low{border-color:var(--color-primary);color:var(--color-primary)}
.pcb--microlot,.pcb--limited{border-color:var(--color-secondary);color:var(--color-secondary)}
.pcb--fresh{border-color:var(--color-secondary);color:var(--color-secondary)}
.pcb--sale{border-color:var(--color-primary);color:var(--color-primary)}

/* body / acidity meters */
.meters{display:flex;flex-direction:column;gap:.3rem;margin-top:.7rem;position:relative;z-index:2}
.meters--lg{margin:1rem 0 1.25rem;gap:.45rem;max-width:18rem}
.meter{display:flex;align-items:center;gap:.6rem}
.meter__l{font-size:.66rem;text-transform:uppercase;letter-spacing:.1em;color:var(--muted);
  min-width:6.2rem}
.meter__bar{display:flex;gap:3px}
.meter__bar i{width:14px;height:3px;border-radius:2px;background:var(--color-gray-2);display:block}
.meters--lg .meter__bar i{width:20px;height:4px}
.meter__bar i.on{background:var(--color-heading)}

/* weight chips + buy row on the card */
.pc__buy{position:relative;z-index:2;margin-top:auto;padding-top:.8rem;
  display:flex;flex-direction:column;gap:.6rem}
.wchips{display:flex;flex-wrap:wrap;gap:.35rem}
.wchip{background:none;border:1px solid var(--color-border);border-radius:999em;
  padding:.3rem .7rem;font-size:.78rem;color:var(--color-text);cursor:pointer;
  display:inline-flex;align-items:center;gap:.3rem;transition:var(--transition);line-height:1.4}
.wchip:hover{border-color:var(--color-heading);color:var(--color-heading)}
.wchip.is-on,.wchip[aria-checked="true"]{background:var(--btn-bg);border-color:var(--btn-bg);
  color:#fff;font-weight:600}
.wchip i{font-style:normal;font-size:.68rem;color:var(--color-primary);font-weight:600}
.wchip.is-on i,.wchip[aria-checked="true"] i{color:#fff;opacity:.9}
.wchips--lg .wchip{padding:.5rem 1.05rem;font-size:.85rem;flex-wrap:wrap;justify-content:center}
/* price per 100 g, printed inside the weight chip — its own line, so the
   discount marker stays next to the weight label */
.wchip__unit{display:block;flex-basis:100%;font-style:normal;font-size:.62rem;font-weight:400;
  letter-spacing:.02em;opacity:.75;margin-top:.1rem;color:inherit;text-align:center}
.wchip.is-on .wchip__unit,.wchip[aria-checked="true"] .wchip__unit{color:#fff;opacity:.8}
/* Компактный вариант для строки корзины: там чипы веса — вторичный контрол рядом
   с помолом, а не главный выбор страницы товара. */
.wchips--sm{gap:.35rem}
.wchips--sm .wchip{padding:.3rem .7rem;font-size:.75rem}
.pc__row{display:flex;align-items:center;justify-content:space-between;gap:.6rem}
.pc__price{font-weight:600;font-size:.95rem;color:var(--color-heading);display:flex;align-items:baseline;gap:.4rem}
.pc__old,.pdp__old{color:var(--muted);font-weight:400;font-size:.82em;text-decoration:line-through}
.pc__from{color:var(--muted);font-weight:400;font-size:.85em}
/* Помол по умолчанию назван прямо на карточке — иначе выбор за покупателя оказывается
   сюрпризом уже в корзине (поменять можно там же, в строке). */
.pc__grind{display:block;margin-top:.35rem;font-size:.75rem;color:var(--faint)}
.grid--list .pc{flex-direction:row;gap:1.5rem;align-items:center;
  border-bottom:1px solid var(--hair);padding:1.4rem 0}
.grid--list .pc__media{width:88px;flex:none;border-radius:8px}
.grid--list .pc__body{padding-top:0}
.grid--list .pc__sub{min-height:0}

/* catalogue filters */
.filters{border-top:1px solid var(--hair);border-bottom:1px solid var(--hair);
  padding:1.1rem 0;margin-bottom:2.25rem;display:flex;flex-direction:column;gap:.75rem}
.filters__adv{display:block;min-width:0}
.filters__adv>*+*{margin-top:.75rem}
.filters__adv>summary{list-style:none;cursor:pointer;display:inline-flex;align-items:center;gap:.4rem;
  border:1px solid var(--color-border);border-radius:999em;padding:.5rem 1rem;font-size:.8rem;
  color:var(--color-heading);font-weight:600;text-transform:uppercase;letter-spacing:.1em}
.filters__adv>summary::-webkit-details-marker{display:none}
.filters__adv>summary::after{content:"";width:.4rem;height:.4rem;border-right:1.4px solid currentColor;
  border-bottom:1.4px solid currentColor;transform:rotate(45deg) translate(-1px,-1px);
  transition:transform .25s var(--ease)}
.filters__adv[open]>summary::after{transform:rotate(225deg) translate(-1px,-1px)}
@media (min-width:48rem){.filters__adv>summary{display:none}}
.filters__row{display:flex;flex-wrap:wrap;align-items:baseline;gap:.5rem .75rem}
.filters__l{font-size:.66rem;text-transform:uppercase;letter-spacing:.14em;color:var(--muted);min-width:6rem}
.filters__chips{display:flex;flex-wrap:wrap;gap:.35rem}
.fchip{border:1px solid var(--color-border);border-radius:999em;padding:.3rem .75rem;
  font-size:.78rem;color:var(--color-text);transition:var(--transition);white-space:nowrap;
  display:inline-flex;align-items:center;gap:.3rem}
.fchip:hover{border-color:var(--color-heading);color:var(--color-heading)}
.fchip.is-on,.fchip[aria-current="true"]{background:var(--btn-bg);border-color:var(--btn-bg);
  color:#fff;font-weight:600}
.fchip i{font-style:normal;color:var(--muted);font-size:.9em;margin-left:.15rem}
.fchip.is-on i,.fchip[aria-current="true"] i{color:rgba(255,255,255,.75)}
.fchip.is-on:hover,.fchip[aria-current="true"]:hover{color:#fff;background:var(--btn-bg-hover)}
/* a facet that would give zero results: visible, but not a trap */
.fchip--0{opacity:.4;pointer-events:none}
.filters__bar{display:flex;flex-wrap:wrap;align-items:center;gap:.75rem 1.25rem;
  padding-top:.4rem;font-size:.78rem}
.filters__count{color:var(--muted);text-transform:uppercase;letter-spacing:.08em}
.filters__sort{display:flex;flex-wrap:wrap;gap:1rem;margin-left:auto}
.filters__sort a{color:var(--muted)}
.filters__sort a.is-on,.filters__sort a[aria-current="true"]{color:var(--color-heading);font-weight:600}
.filters__reset{color:var(--color-primary)}
.filters__reset:hover{color:var(--color-heading)}

/* jargon hint — plain <details>, no JS */
.hint{display:inline-block;margin-left:.35rem;vertical-align:middle}
.hint>summary{list-style:none;cursor:help;width:1.1rem;height:1.1rem;border-radius:50%;
  border:1px solid var(--color-border);color:var(--muted);font-size:.7rem;line-height:1;
  display:grid;place-items:center}
.hint>summary::-webkit-details-marker{display:none}
.hint>summary:hover{border-color:var(--color-heading);color:var(--color-heading)}
.hint p{margin:.5rem 0 0;font-size:.82rem;color:var(--muted);max-width:26rem;
  text-transform:none;letter-spacing:0;line-height:1.55}

/* search state inside the catalogue */
.searchbar{display:flex;flex-wrap:wrap;align-items:center;gap:.75rem;margin:.5rem 0 1.25rem}
.searchbar b{font-weight:600;color:var(--color-heading)}
.searchbar a{color:var(--color-primary);font-size:.85rem}
.searchbar a:hover{color:var(--color-heading)}
.searchbar form{display:flex;gap:.5rem;flex:1 1 16rem;min-width:0}
.searchbar form input{flex:1 1 auto;min-width:0;height:2.5rem}
#results{scroll-margin-top:var(--hdr-h)}

/* empty state (catalogue, search, rubric) */
.emptybox{border:1px solid var(--hair);border-radius:var(--rounded);
  padding:clamp(1.75rem,5vw,3rem);text-align:center;margin:2rem 0}
.emptybox h2{font-size:1.25rem;margin-bottom:.4rem}
.emptybox p{color:var(--muted);margin:0 0 1.5rem}
.emptybox__more{margin-top:3rem;text-align:left}
.emptybox__more h2,.emptybox__more h3{font-size:.8rem;text-transform:uppercase;letter-spacing:.14em;
  color:var(--muted);font-weight:600;margin-bottom:1.5rem}

.pager{display:flex;flex-wrap:wrap;gap:.4rem;justify-content:center;margin-top:3rem}
.pager a{min-width:2.2rem;height:2.2rem;display:grid;place-items:center;border:1px solid var(--color-border);
  border-radius:999em;font-size:.85rem;color:var(--color-text);padding-inline:.5rem}
.pager a:hover{border-color:var(--color-heading);color:var(--color-heading)}
.pager a.is-on,.pager a[aria-current="page"]{background:var(--btn-bg);border-color:var(--btn-bg);color:#fff}

/* ---------- shop sub-nav / breadcrumbs ---------- */
.shopnav{display:flex;flex-wrap:wrap;gap:.5rem;margin:1.75rem 0 2.5rem}
.shopnav a{border:1px solid var(--color-border);padding:.5rem 1.1rem;font-size:.82rem;
  border-radius:999em;color:var(--color-text);transition:var(--transition)}
.shopnav a:hover{border-color:var(--color-heading);color:var(--color-heading)}
.shopnav a.is-on,.shopnav a[aria-current="true"]{background:var(--btn-bg);color:#fff;border-color:var(--btn-bg)}
.shopnav a.is-on:focus-visible,.shopnav a[aria-current="true"]:focus-visible{outline-color:var(--color-primary)}
.crumbs{font-size:.78rem;color:var(--muted);margin-bottom:1rem;text-transform:uppercase;letter-spacing:.08em}
.crumbs a:hover{color:var(--color-heading)}

/* ---------- product page ---------- */
.pdp{padding-block:1.5rem var(--section)}
.pdp__grid{display:grid;gap:clamp(2rem,5vw,4rem);grid-template-columns:1fr}
.pdp__gallery,.pdp__info{min-width:0}
.pdp__media{background:var(--color-gray);aspect-ratio:1/1;overflow:hidden;border-radius:var(--rounded)}
.pdp__media img{width:100%;height:100%;object-fit:cover}
.pdp__ph{width:100%;height:100%;display:grid;place-items:center;color:var(--color-gray-2);
  font-weight:700;letter-spacing:.16em;font-size:.7rem;text-transform:uppercase}
/* variant photo strip */
.pdp__thumbs{display:flex;gap:.5rem;margin-top:.75rem;overflow-x:auto;padding-bottom:.25rem;
  -webkit-overflow-scrolling:touch;overscroll-behavior-inline:contain;scrollbar-width:thin}
.pdp__thumbs button{flex:none;width:4rem;height:4rem;padding:0;border:1px solid var(--color-border);
  border-radius:8px;overflow:hidden;background:var(--color-gray);cursor:pointer;
  transition:border-color .3s var(--ease)}
.pdp__thumbs button:hover{border-color:var(--color-heading)}
.pdp__thumbs button.is-on,.pdp__thumbs button[aria-current="true"]{border-color:var(--color-heading);
  box-shadow:0 0 0 1px var(--color-heading)}
.pdp__thumbs img{width:100%;height:100%;object-fit:cover}
.pdp__title{font-size:clamp(1.8rem,4.5vw,2.6rem);font-weight:700;letter-spacing:-.035em;margin:0 0 .3rem}
.pdp__sub{color:var(--muted);margin:0 0 1.5rem;font-size:1.05rem}
.pdp__country{display:block;font-size:.7rem;text-transform:uppercase;letter-spacing:.18em;
  color:var(--muted);margin-bottom:.5rem}
/* roast freshness + stock state */
.pdp__fresh{display:inline-flex;align-items:center;gap:.4rem;margin:.25rem 0 .75rem;
  font-size:.78rem;font-weight:600;letter-spacing:.02em;color:var(--color-secondary)}
.pdp__fresh--hot{background:var(--color-secondary-soft);border-radius:999em;padding:.3rem .8rem}
.pdp__stock{display:flex;align-items:center;gap:.45rem;font-size:.8rem;color:var(--muted);margin-top:.3rem}
.pdp__stock::before{content:"";width:7px;height:7px;border-radius:50%;flex:none;
  background:var(--color-secondary)}
.pdp__stock--low::before{background:var(--color-primary)}
.pdp__stock--out::before{background:var(--faint)}
.pdp__price{font-size:1.6rem;font-weight:700;letter-spacing:-.03em;margin:1.5rem 0 .25rem;
  display:flex;align-items:baseline;gap:.6rem;flex-wrap:wrap}
.pdp__unit{font-size:.8rem;color:var(--muted);margin:0 0 1.25rem}
.specs{border-collapse:collapse;width:100%;margin:1.5rem 0;font-size:.9rem}
.specs th,.specs td{text-align:left;vertical-align:top;padding:.7rem 0;border-bottom:1px solid var(--hair)}
.specs tr:first-child th,.specs tr:first-child td{border-top:1px solid var(--hair)}
.specs th{width:40%;color:var(--muted);font-weight:400;text-transform:uppercase;letter-spacing:.06em;font-size:.75rem}
.pdp__pay,.pdp__soon{color:var(--muted);font-size:.75rem;text-align:center;margin:.9rem 0 0;
  text-transform:uppercase;letter-spacing:.12em}
.pdp__desc{margin-top:3rem;border-top:1px solid var(--hair);padding-top:2rem}
.pdp__desc h2{font-size:1.1rem;text-transform:uppercase;letter-spacing:.1em}
.pdp__buy{display:flex;gap:.75rem;align-items:stretch;margin-top:.75rem}
.pdp__buy .btn--lg{flex:1;width:auto}
/* trust block under the buy button — every number comes from Settings */
.pdp__trust{margin-top:1.25rem;border-top:1px solid var(--hair);font-size:.85rem;color:var(--color-text)}
.pdp__trust ul{margin:0;padding:0}
.pdp__trust li{list-style:none;padding:.7rem 0;border-bottom:1px solid var(--hair);
  display:flex;gap:.6rem;align-items:baseline;line-height:1.5}
.pdp__trust b{font-weight:600;color:var(--color-heading)}
.pdp__trust a{text-decoration:underline;text-decoration-color:var(--color-border);text-underline-offset:3px}
.qty{display:inline-flex;align-items:center;height:3.25rem;box-sizing:border-box;
  border:1px solid var(--color-border);border-radius:999em;overflow:hidden;flex:none}
.qty button{width:2.4rem;height:100%;background:none;border:0;font-size:1.05rem;cursor:pointer;color:var(--color-heading)}
.qty button:hover{background:var(--color-gray)}
/* NB: padding MUST stay 0 here — the global input padding eats the whole content box
   of a ~38px field and the digit disappears. */
.qty input{width:2.4rem;min-width:0;height:100%;padding:0;border:0;border-radius:0;
  text-align:center;font:inherit;font-weight:600;color:var(--color-heading);background:none;
  -moz-appearance:textfield}
.qty input::-webkit-outer-spin-button,.qty input::-webkit-inner-spin-button{-webkit-appearance:none;margin:0}
/* .qty обрезает содержимое (overflow:hidden + скругление), поэтому кольцо со сдвигом
   наружу оказывается под обрезкой и кнопка выглядит нефокусируемой. */
.qty input:focus-visible,.qty button:focus-visible{outline-offset:-2px}
.btn.is-added{background:var(--color-secondary);border-color:var(--color-secondary)}
/* Главная кнопка страницы товара: заметный отклик на наведение и нажатие. */
.pdp__buy .btn--lg{transition:background-color .3s var(--ease),border-color .3s var(--ease),
  box-shadow .25s var(--ease),transform .12s var(--ease)}
.pdp__buy .btn--lg:hover{box-shadow:0 8px 22px rgba(20,18,16,.18)}
.pdp__buy .btn--lg:active{transform:translateY(1px);box-shadow:none}
.pdp__buy .btn--lg[disabled]{box-shadow:none;transform:none}
@media (prefers-reduced-motion:reduce){
  .pdp__buy .btn--lg{transition:background-color .3s var(--ease)}
  .pdp__buy .btn--lg:active{transform:none}
}

/* "Russian only for now" note */
.langnote{display:inline-block;font-size:.72rem;text-transform:uppercase;letter-spacing:.12em;
  color:var(--muted);border:1px solid var(--color-border);border-radius:999em;
  padding:.25rem .7rem;margin-bottom:1.25rem}

/* ---------- prose ---------- */
.prose{max-width:var(--wrap-narrow);color:var(--color-text);font-size:1.0625rem;line-height:1.8}
.prose>p:first-of-type{font-size:1.2rem;line-height:1.65;color:var(--color-heading)}
.prose p{margin:0 0 1.5em}
.prose h2{font-size:1.5rem;margin:1.8em 0 .5em}
.prose h3{font-size:1.2rem;margin:1.5em 0 .5em}
.prose ul{margin:0 0 1.5em;padding-left:1.2rem}
.prose li{margin:.3em 0}
.prose em{font-style:italic;color:var(--muted)}
.prose a{color:var(--color-heading);text-decoration:underline;text-decoration-color:var(--color-border);
  text-underline-offset:3px;text-decoration-thickness:1px}
.prose a:hover{text-decoration-color:var(--color-heading)}

/* ---------- prose blocks from mdlite: photo, photo row, quote, numbered list ----------
   Editor spec §7. Additions only — nothing above this comment is edited.
   The same four rulesets serve every .prose on the site: article, page, contact page
   and the product description. */

/* photo with a caption */
.prose .fig{margin:2.5rem 0}
/* mdlite emits no width/height (it would cost a disk read per picture per render),
   so the BOX reserves the space instead: a fixed ratio means the text below never
   jumps when a lazy photo arrives. `contain` over `cover` — an editorial photo must
   not be cropped by the engine; a portrait shot simply sits on a warm mat. */
.prose .fig img{display:block;width:100%;height:auto;aspect-ratio:3/2;object-fit:contain;
  background:var(--color-gray);border-radius:var(--rounded)}
.prose .fig figcaption{margin-top:.7rem;font-size:.85rem;line-height:1.5;color:var(--muted)}

/* a row of 2-3 photos: cropped to one height, otherwise mixed formats tear the bottom edge */
.prose .figrow{display:grid;gap:.75rem;margin:2.5rem 0}
.prose .figrow .fig{margin:0}
.prose .figrow .fig img{aspect-ratio:4/3;object-fit:cover}
.prose .figrow .fig figcaption{margin-top:.5rem;font-size:.8rem}
@media (min-width:40rem){
  .prose .figrow--2{grid-template-columns:1fr 1fr}
  .prose .figrow--3{grid-template-columns:repeat(3,1fr)}
}

/* quote with attribution — the only terracotta inside an article, and only a 2px hair.
   No fill and no italic: `.prose em` already owns italic for photo captions. */
.prose .quote{margin:2.5rem 0;padding-left:1.4rem;border-left:2px solid var(--color-primary)}
.prose .quote p{font-size:1.15rem;line-height:1.6;color:var(--color-heading);margin:0}
.prose .quote cite{display:block;margin-top:.9rem;font-style:normal;font-size:.78rem;
  text-transform:uppercase;letter-spacing:.08em;color:var(--muted)}
/* a quote that is nothing but an attribution (a caption under the block above it) */
.prose .quote cite::before{content:"— "}
.prose .quote cite:first-child{margin-top:0}
/* the parser eats the dash so the author need not remember which one to type */
.prose .quote cite::before{content:"— "}

/* numbered list — deeper than ul: the marker sits OUTSIDE the content box, so at the
   1.4rem of a bullet list a two-digit "10." hangs left of the text measure. 2rem holds
   three digits, which is more than any article here will ever number. */
.prose ol{margin:0 0 1.5em;padding-left:2rem}
.prose ol li{margin:.3em 0}
.prose ol li::marker{font-variant-numeric:tabular-nums;color:var(--muted)}

/* Wide screens only: a photo steps symmetrically outside the 660px measure, and its
   caption stays flush with the picture, not with the text. Only direct children of a
   narrow centred document — .pdp__desc and .cnote also carry .prose, and there
   a negative margin would shove the photo out of its column. Below 60rem there is no
   step-out at all: negative margins on a phone drag the picture under the screen edge
   (the same trap that bans `padding: X 0` on section wrappers). */
@media (min-width:60rem){
  .article>.prose>.fig,.article>.prose>.figrow,
  .doc>.prose>.fig,.doc>.prose>.figrow{margin-inline:-4rem}
}

/* phone: no step-out, less air, smaller captions */
@media (max-width:39.999rem){
  .prose .fig,.prose .figrow{margin-block:1.75rem}
  .prose .fig figcaption{font-size:.8rem}
  .prose .figrow{gap:.6rem}
  .prose .quote{margin-block:1.75rem;padding-left:1rem}
  .prose .quote p{font-size:1.05rem}
}

/* ---------- documents / articles ---------- */
.doc{padding-block:clamp(1.5rem,4vw,3rem) var(--section);max-width:var(--wrap-narrow)}
.doc--narrow{max-width:56rem}
.doc--center{text-align:center;max-width:34rem;margin-inline:auto}
.doc h1{margin-bottom:.6em}
/* Корзина и оформление — не «документ»: в 660px колонка списка сжимается до ~244px,
   и степпер налезает на цену. Ширину даёт .wrap, здесь только вертикальный ритм. */
.shopflow{padding-block:clamp(1.5rem,4vw,3rem) var(--section)}
.shopflow h1{margin-bottom:.6em}
/* closing CTA on plain CMS pages */
.pageend{margin-top:3rem;border-top:1px solid var(--hair);padding-top:2rem;
  display:flex;flex-wrap:wrap;gap:.75rem}
/* ---------- юридический документ (шаблон page_legal) ----------
   Ширину и типографику даёт .wrap + .doc + .prose — здесь только то, чего у обычной
   страницы нет: плашка черновика, строка редакции, оговорка о языке и ритм разделов.
   Вертикальные отступы — ТОЛЬКО padding-block: шорткат `padding: X 0` обнулил бы
   padding-inline у .wrap, и на телефоне текст прилип бы к краям экрана. */
.legal__draft{background:color-mix(in srgb,var(--color-primary) 8%,#fff);
  border-left:3px solid var(--color-primary);border-radius:.5rem;
  padding:.9rem 1.1rem;margin-block:1.25rem;font-size:.9rem;line-height:1.6;
  color:var(--color-text)}
.legal__draft p{margin:0}
.legal__draft p+p{margin-top:.4rem}
/* Строка редакции — та же мелкая капитель, что у eyebrow и меток на карточках. */
.legal__rev{font-size:.72rem;text-transform:uppercase;letter-spacing:.1em;
  color:var(--faint);margin-block:.75rem 1.5rem}
/* Оговорка о приоритете русской редакции: печатается только на EN, поэтому обрамление
   тонкими линиями — она обязана читаться как служебная, а не как первый абзац. */
.legal__note{font-size:.85rem;line-height:1.6;color:var(--muted);
  border-block:1px solid var(--hair);padding-block:.6rem;margin-block:1.25rem}
/* Документ длинный: разделы должны читаться как разделы, а не сливаться в полотно. */
.legal .prose h2{margin-top:2.25rem}
.legal .prose li{margin-block:.3rem}

.article{padding-block:clamp(1.5rem,4vw,3rem) var(--section);max-width:var(--wrap-narrow)}
.article__head{margin:1.5rem 0 2rem}
.article__k,.ac__k,.story__k{display:inline-block;font-size:.66rem;text-transform:uppercase;
  letter-spacing:.18em;color:var(--muted);margin-bottom:.9rem}
.article__k a,.ac__k a,.story__k a{color:inherit}
.article__k a:hover,.ac__k a:hover,.story__k a:hover{color:var(--color-heading)}
.article__head h1{font-size:clamp(1.9rem,5vw,3rem);margin:0 0 .5rem}
.article__meta{color:var(--muted);font-size:.8rem;text-transform:uppercase;letter-spacing:.06em}
/* fixed ratio: the cover no longer shoves the text down when it loads */
.article__nav{display:flex;justify-content:space-between;gap:1.5rem;margin-top:3.5rem;
  border-top:1px solid var(--hair);padding-top:1.5rem;font-size:.9rem}
.article__nav a:hover{color:var(--color-heading)}
.article__nav .article__k{margin-bottom:.25rem}
.article__next{margin-left:auto;text-align:right}

.ac{display:flex;flex-direction:column;color:inherit;height:100%}
.ac__media{aspect-ratio:3/2;background:var(--color-gray);overflow:hidden;margin-bottom:1rem;
  border-radius:var(--rounded);display:grid;place-items:center}
.ac__media img{width:100%;height:100%;object-fit:cover;transition:transform .7s var(--ease)}
.ac:hover .ac__media img{transform:scale(1.035)}
/* coverless card: a typographic plate instead of a fifth grey rectangle */
.ac__ph{color:var(--muted);font-weight:700;letter-spacing:.16em;font-size:.7rem;text-transform:uppercase;
  padding:1.25rem;text-align:center;align-self:center;max-width:100%}
.ac__ph b{display:block;margin-top:.6rem;font-size:1.05rem;font-weight:700;letter-spacing:-.025em;
  text-transform:none;line-height:1.3;color:var(--color-heading)}
.ac__t{font-size:1.05rem;font-weight:600;letter-spacing:-.025em;margin:.2rem 0;color:var(--color-heading)}
.ac__t a:not(:hover){color:inherit}
.ac__meta{color:var(--muted);font-size:.76rem;margin:0 0 .4rem;text-transform:uppercase;letter-spacing:.06em}
.ac__x{color:var(--muted);font-size:.9rem;margin:0;line-height:1.6}

.cards2{display:grid;gap:1.75rem;grid-template-columns:1fr;list-style:none;margin:0;padding:0}
.cards2>li{display:flex;flex-direction:column;min-width:0}
.story{display:flex;flex-direction:column;color:inherit}
.story img{aspect-ratio:16/10;object-fit:cover;background:var(--color-gray);
  border-radius:var(--rounded);margin-bottom:1rem;transition:transform .7s var(--ease)}
.story:hover img{transform:scale(1.03)}
.story__t{font-weight:600;font-size:1.05rem;letter-spacing:-.025em;color:var(--color-heading)}
.story__x{color:var(--muted);font-size:.88rem;margin-top:.35rem}

/* ---------- contact ----------
   Страница контактов — не «документ»: в 660px объявленные две колонки давали 253 и 316px,
   и ни один блок крупнее списка туда не влезал. Ширину даёт .wrap, здесь только
   вертикальный ритм. ТОЛЬКО padding-block: шорткат padding:X 0 обнулит padding-inline
   у .wrap и приклеит контент к краю экрана на мобильном.
   Брейкпоинтов у страницы нет: пороги деления следуют из ширины КОНТЕЙНЕРА через
   auto-fit + minmax(min(100%,Nrem),1fr). min(100%,…) обязателен — без него трек не
   схлопывается ниже N и на 320px появляется горизонтальная прокрутка. */
.contactpage{padding-block:clamp(1.5rem,4vw,3rem) var(--section)}

/* шапка-герой: герой сделан типографикой, а не фотографией, поэтому деградировать нечему */
.chead{position:relative;isolation:isolate;border-bottom:1px solid var(--hair);
  padding-bottom:clamp(1.5rem,4vw,2.5rem);margin-bottom:clamp(1rem,3vw,2rem)}
/* h1 на этой странице — служебное слово («Контакты»), а не содержание: героем идёт адрес.
   Глобальный h1 даёт clamp(2.1rem,5vw,3.1rem) и на 1440px выходил 49.6px против 46.4px
   у адреса — два дисплейных блока подряд конкурировали, и выигрывал ярлык. */
.chead h1{margin-bottom:.35em;font-size:clamp(1.15rem,2.2vw,1.5rem);font-weight:700;
  letter-spacing:var(--lsp-head);color:var(--muted)}
.chead__addr{margin:0 0 .9rem;font-weight:800;line-height:1.06;letter-spacing:-.035em;
  color:var(--color-heading);font-size:clamp(1.7rem,4.6vw,2.9rem)}
/* Две смысловые строки вместо жадного переноса: на 360px адрес одной строкой рвётся
   по случайному месту, а на 375px во второй строке остаётся одинокое «46». */
.chead__city,.chead__street{display:block}
/* город тише улицы: улица и есть то, что ищут глазами */
.chead__city{color:var(--muted);font-size:.62em;font-weight:700;letter-spacing:-.02em;margin-bottom:.15em}
.chead__hours{margin:0;font-size:.72rem;text-transform:uppercase;letter-spacing:.14em;
  color:var(--muted);display:flex;flex-wrap:wrap;align-items:center;gap:.5rem .9rem}
.cstatus{display:inline-flex;align-items:center;gap:.45rem;color:var(--muted)}
.cstatus--open{color:var(--color-heading)}
/* Точка — тёмно-зелёная, НЕ терракотовая: терракота на этой странице занята стрелкой
   карточки, и второй акцент рассыпал бы иерархию. */
.cstatus__dot{width:7px;height:7px;border-radius:50%;background:var(--faint);flex:none}
.cstatus--open .cstatus__dot{background:var(--color-secondary)}
.chead__note{margin:.7rem 0 0;font-size:.85rem;color:var(--muted)}
.chead__act{display:flex;flex-wrap:wrap;gap:.6rem;margin:clamp(1.25rem,3vw,1.75rem) 0 0}

.csect{padding-block:calc(var(--section) / 2) 0}
/* Порог деления следует из ширины КОНТЕЙНЕРА, а не окна: ландшафт телефона 568×320
   получает две колонки сам, без единого медиазапроса. */
.cfind{display:grid;gap:clamp(1.75rem,4vw,2.5rem);
  grid-template-columns:repeat(auto-fit,minmax(min(100%,20rem),1fr));align-items:start}
.cfacts{margin:0}
.cfacts dt{font-size:.7rem;text-transform:uppercase;letter-spacing:.14em;color:var(--muted);margin-top:1.5rem}
/* ОТДЕЛЬНЫМ правилом, не через :not() — :not() поднял бы специфичность до (0,2,0),
   и это уже ломало чекбоксы в админке. */
.cfacts dt:first-of-type{margin-top:0}
/* overflow-wrap по образцу .cl__name: адрес и почту правит владелец через админку,
   и одно длинное значение иначе даёт горизонтальную прокрутку на 320px. */
.cfacts dd{margin:.25rem 0 0;font-size:1.0625rem;color:var(--color-heading);overflow-wrap:break-word}
/* Тач-цель добираем ТОЛЬКО по вертикали (min-width в шапке уже обрезал «Toggle Coffee»),
   отрицательный margin-block возвращает прежний вертикальный ритм. */
.cfacts dd a{display:inline-flex;align-items:center;min-height:var(--tap);margin-block:-.45rem}
.cfacts dd a[href^="mailto:"]{overflow-wrap:anywhere}
.cfacts__city,.cfacts__street{display:block}

/* Плита с настоящим содержанием вместо серого плейсхолдера. Это же и зарезервированное
   место: появится снимок двери или встраиваемая карта — встанут сюда без правок раскладки. */
/* Колонка фактов и слот: без min-width:0 длинная строка распирает грид-трек. */
.cfind__facts,.cfind__slot{min-width:0}
.cplate{border:1px solid var(--color-border);border-radius:var(--rounded);background:var(--color-gray);
  padding:clamp(1.5rem,4vw,2rem);display:flex;flex-direction:column;justify-content:center;
  gap:1.1rem;min-height:13rem;height:100%}
.cplate__addr{margin:0;font-size:1.15rem;font-weight:700;letter-spacing:-.02em;
  color:var(--color-heading);line-height:1.3}
.cplate__act{margin:0;display:flex;flex-wrap:wrap;align-items:center;gap:.75rem 1.1rem}
/* Подчёркивание обязательно: ссылка, отличающаяся от текста только цветом,
   не читается при протанопии. */
.cplate__map{font-size:.85rem;color:var(--color-heading);text-decoration:underline;
  text-decoration-color:var(--color-border);text-underline-offset:3px;
  display:inline-flex;align-items:center;min-height:var(--tap)}
.cplate__map:hover,.cplate__map:focus-visible{text-decoration-color:var(--color-heading)}

/* карточки каналов: одна карточка = ОДНО действие, иначе растянутая ссылка съест соседние */
.ccards{display:grid;gap:1rem;grid-template-columns:repeat(auto-fit,minmax(min(100%,17rem),1fr));
  list-style:none;margin:0;padding:0}
.ccards>li{display:flex;min-width:0}
/* Паддинг 1.4rem сам даёт цель ≈76px по вертикали — --tap добирать не нужно. */
.ccard{position:relative;display:flex;flex-direction:column;gap:.25rem;width:100%;
  border:1px solid var(--color-border);border-radius:var(--rounded);padding:1.4rem 1.5rem;
  transition:border-color .3s var(--ease)}
.ccard:hover{border-color:var(--color-heading)}
/* Кольцо фокуса рисуется вокруг текста ссылки, а сама ссылка растянута на карточку —
   рамка добавляет второй, более заметный признак. */
.ccard:focus-within{border-color:var(--color-heading)}
.ccard__k{font-size:.7rem;text-transform:uppercase;letter-spacing:.14em;color:var(--muted)}
.ccard__v{font-size:1.15rem;font-weight:700;letter-spacing:-.025em;color:var(--color-heading);
  overflow-wrap:anywhere}
.ccard__v a{color:inherit}
.ccard__v a::after{content:"";position:absolute;inset:0;z-index:1}
.ccard__x{font-size:.85rem;color:var(--muted);line-height:1.55;margin-top:.15rem}
.ccard__go{position:absolute;top:1.15rem;right:1.25rem;color:var(--faint);font-size:.9rem;
  transition:color .3s var(--ease)}
/* единственное появление терракоты на всей странице */
.ccard:hover .ccard__go{color:var(--color-primary)}

/* Снимает лид-подачу .prose>p:first-of-type (1.2rem) именно здесь: правило рассчитано
   на меру 660px, а на контактах читают не прозу, а факты. .prose для статей не трогаем. */
.cnote>p:first-of-type{font-size:inherit;line-height:inherit;color:var(--color-text)}
.cpick__where{margin:.5rem 0 1.25rem;color:var(--muted);font-size:.9rem}

.creq{margin-top:var(--section);border-top:1px solid var(--hair);padding-top:1.5rem;
  font-size:.82rem;color:var(--muted)}
.creq__h{font-size:.7rem;text-transform:uppercase;letter-spacing:.14em;color:var(--muted);
  font-weight:600;margin:0 0 .9rem}
.creq dl{margin:0;display:grid;gap:.4rem 1.5rem;
  /* Ровно две колонки: подпись и значение. Прежний auto-fit давал на обычных ширинах
     нечётное число колонок, и <dt> оказывался над чужим <dd>. */
  grid-template-columns:auto 1fr;align-items:baseline}
.creq dt{color:var(--muted)}
.creq dd{margin:0;color:var(--color-text)}

/* Печать и контрастные режимы для страницы контактов стоят ЗДЕСЬ, а не в общих блоках
   @media print / forced-colors / prefers-contrast наверху файла: те блоки объявлены
   раньше (строки ~477–505), а специфичность у них та же (0,1,0). Дописка туда молча
   проигрывала бы по порядку правилам выше — .cfind осталась бы двухколоночной на бумаге,
   а рамка .ccard перекрасилась бы обратно шорткатом border. Поднимать специфичность
   ради переезда нельзя, поэтому блоки живут после своих базовых правил. */
@media print{
  /* На бумаге «Построить маршрут» не нажимается, а адрес и телефон уже напечатаны выше. */
  .cfind{grid-template-columns:1fr}
  .ccards{grid-template-columns:1fr}
  .contactpage .btn,.cplate__act,.ccard__go{display:none}
  .cfacts dd a{color:#000;text-decoration:none}
  .ccard{break-inside:avoid}
}
@media (forced-colors:active){
  .ccard,.cplate{border:1px solid}
  /* Второй селектор обязателен: .cstatus--open .cstatus__dot выше — это (0,2,0),
     одиночный класс ему проигрывает, и точка «открыто» осталась бы невидимой. */
  .cstatus__dot,.cstatus--open .cstatus__dot{background:CanvasText}
}
@media (prefers-contrast:more){
  .ccard,.cplate{border-color:var(--color-heading)}
}

.notice{border:1px solid var(--color-border);border-radius:var(--rounded);padding:1.75rem;margin-top:2rem;background:var(--color-gray)}

/* ---------- footer ---------- */
.foot{background:var(--color-gray);color:var(--color-text);margin-top:var(--section);
  border-top:1px solid var(--hair);font-size:.88rem}
.foot__in{max-width:var(--wrap);margin:0 auto;padding:clamp(2.5rem,6vw,4.5rem) var(--pad) 3rem;
  display:grid;gap:2.5rem;grid-template-columns:1fr}
@media (max-width:1239px){ .foot__in{padding-inline:var(--pad)} }
.foot__col{min-width:0}
.foot h4{color:var(--color-heading);font-size:.7rem;font-weight:600;text-transform:uppercase;
  letter-spacing:.16em;margin:0 0 1.1rem}
.foot ul{list-style:none;margin:0;padding:0;display:grid;gap:.5rem}
.foot a:hover{color:var(--color-heading)}
.foot__social{display:flex;flex-wrap:wrap;gap:1.25rem;margin-top:.6rem}
.foot__bar{border-top:1px solid var(--hair)}
.foot__bar-in{max-width:var(--wrap);margin:0 auto;padding-block:1.1rem;padding-inline:var(--pad);
  color:var(--muted);font-size:.76rem;display:flex;flex-wrap:wrap;gap:.6rem 1.5rem;
  justify-content:space-between;align-items:center;text-transform:uppercase;letter-spacing:.06em}
.foot__links{display:flex;flex-wrap:wrap;gap:1.25rem}
/* Реквизиты продавца (ЗоЗПП ст. 9, ПП РФ № 2463) — своей строкой под копирайтом и
   ссылками: .foot__bar-in уже перенослив (flex-wrap:wrap, row-gap .6rem), поэтому
   реквизиты не выталкивают ссылки за край. Капитель и трекинг ряда снимаем — это
   связный текст, а не метка. .8rem, а не .76rem ряда: на телефоне ряд и так
   поднимается до .8rem, ниже юридическую строку опускать нельзя. */
.foot__legal{flex-basis:100%;text-transform:none;letter-spacing:0;line-height:1.5;
  font-size:.8rem;color:var(--muted)}

/* =========================================================================
   Cart · checkout · account · orders
   ========================================================================= */
.cart{display:grid;gap:2.5rem;grid-template-columns:1fr}
.cart__lines{display:flex;flex-direction:column}
.cl{display:grid;grid-template-columns:64px 1fr auto;gap:1.25rem;align-items:center;
  padding:1.4rem 0;border-bottom:1px solid var(--hair);min-width:0}
.cl:first-child{border-top:1px solid var(--hair)}
.cl__media{width:64px;height:64px;border-radius:8px;overflow:hidden;background:var(--color-gray);
  display:grid;place-items:center;color:var(--color-gray-2);font-size:.6rem;flex:none}
.cl__media img{width:100%;height:100%;object-fit:cover}
.cl__body{min-width:0}
.cl__name{font-weight:600;letter-spacing:-.02em;color:var(--color-heading);overflow-wrap:break-word}
.cl__opt{color:var(--muted);font-size:.82rem}
/* Помол и фасовка правятся прямо в строке — иначе смена веса стоит пяти шагов
   (убрать → найти в каталоге → выбрать вес → выбрать помол → добавить). */
.cl__opts{display:flex;align-items:center;gap:.6rem;flex-wrap:wrap;margin-top:.45rem}
/* min-width сбрасываем: .sel--sm держит 11rem, и в узкой колонке (iPad портрет —
   тело строки ~104px) селект вылезал поверх колонки с ценой на 72px. */
.cl__opts .sel--sm{min-width:0;flex:0 1 12rem;max-width:12rem}
.cl__ctl{display:flex;align-items:center;gap:1.25rem;margin-top:.55rem}
/* Высоту задаёт и сама пилюля .qty (3.25rem), и её содержимое. Ужать только детей
   мало: у контейнера оставалась мёртвая зона ~9px сверху и снизу. */
.cl__ctl .qty{height:auto}
.cl__ctl .qty button,.cl__ctl .qty input{height:2.15rem;width:1.9rem}
/* «−» на единице выключена, а не удаляет молча — удаление только явной «Убрать». */
.cl__ctl .qty button[disabled]{opacity:.3;cursor:not-allowed}
.cl__rm{background:none;border:0;color:var(--muted);font-size:.78rem;cursor:pointer;
  text-transform:uppercase;letter-spacing:.08em;padding:0}
.cl__rm:hover{color:var(--color-primary)}
/* Объяснение стоит там, где произошло изменение, а не общей плашкой за экран выше. */
.cl__note{margin:.4rem 0 0;font-size:.75rem;color:var(--color-primary-ink)}
.cl.is-flagged{box-shadow:inset 2px 0 0 var(--color-primary-ink)}
.cl__price{font-weight:600;white-space:nowrap;color:var(--color-heading);
  display:flex;flex-direction:column;align-items:flex-end}
.cl__old{color:var(--muted);font-weight:400;font-size:.82rem;margin-right:.4rem}
.cl__unit{display:block;margin-top:.2rem;font-size:.78rem;color:var(--muted);font-weight:400}
/* Приглушённая цена честно значит «сервер ещё не подтвердил»: в JS не считается ничего. */
.cl.is-pending .cl__price{opacity:.4;transition:opacity .15s var(--ease)}
.cart__side{border:1px solid var(--color-border);border-radius:var(--rounded);padding:1.75rem;
  display:flex;flex-direction:column;gap:.85rem;height:max-content}
.cart__row{display:flex;justify-content:space-between;gap:1rem;font-size:.92rem}
.cart__row--total{font-size:1.2rem;font-weight:700;letter-spacing:-.02em;
  border-top:1px solid var(--hair);padding-top:.9rem;margin-top:.3rem}
.cart__cont{text-align:center;font-size:.85rem;color:var(--muted)}
.cart__empty{text-align:center;padding:4rem 0}
.cart__empty p{color:var(--muted);margin-bottom:1.5rem;font-size:1.05rem}
/* Пока сервер считает — суммы приглушены. Ни одна цифра тут не пересчитывается в JS,
   поэтому приглушённое старое значение и означает «ещё не подтверждено». */
.cart__side.is-pending .cart__row b,.cartbar.is-pending b{opacity:.4;transition:opacity .15s var(--ease)}
/* Липкая полоса с итогом — только на телефоне (правила ниже, в мобильном блоке):
   на десктопе её роль играет липкая .cart__side. */
.cartbar{display:none}
.cartbar__t{font-size:.95rem;color:var(--muted)}
/* JS вешает is-disabled на «К оформлению», когда оформлять нечего. Без правила класс был
   немым: приглушение держалось только на aria-disabled, которого CSS не видит. */
.btn.is-disabled{opacity:.45;pointer-events:none}
.cartbar__t b{color:var(--color-heading);font-weight:700;letter-spacing:-.02em}

/* server-side cart messages ("we adjusted your order", network failure) */
/* Корректировки копятся и держатся до крестика, поэтому блоку нужен и сам крестик,
   и запас прокрутки: JS уводит сюда фокус, а над ним стоит липкая шапка. */
.cartmsg{position:relative;margin-bottom:1.25rem;scroll-margin-top:calc(var(--hdr-h) + 1rem)}
.cartmsg ul{margin:.35rem 0 0;padding-left:1.1rem}
.cartmsg li{margin:.15rem 0}
.cartmsg .btn--sm{margin-top:.6rem}
.cartmsg__x{position:absolute;top:.35rem;right:.5rem;width:2rem;height:2rem;
  background:none;border:0;font-size:1.1rem;line-height:1;cursor:pointer;color:inherit}
/* Оба класса в селекторе не ради краткости: .formmsg задаёт padding шорткатом и стоит
   ниже по файлу, иначе первая строка сообщения уезжает под крестик. */
.formmsg.cartmsg{padding-right:2.6rem}
/* Сетевая ошибка — одна заменяемая строка над списком, она не накапливается. */
.cartmsg__net{display:block;margin-bottom:.5rem;font-weight:600}
.cartmsg:focus{outline:none}
.cartmsg:focus-visible{outline:2px solid var(--color-heading);outline-offset:2px}

/* progress towards free shipping */
.shipbar{margin:.9rem 0 .25rem;font-size:.8rem;color:var(--muted)}
.shipbar__t{display:block;margin-bottom:.4rem}
.shipbar__track{height:3px;border-radius:2px;background:var(--color-gray-2);overflow:hidden}
.shipbar__fill{display:block;height:100%;background:var(--color-heading);width:0;
  transition:width .4s var(--ease)}
.shipbar.is-free .shipbar__fill{background:var(--color-secondary)}
.shipbar.is-free .shipbar__t{color:var(--color-secondary);font-weight:600}

/* loading skeletons — shown until the first server quote lands */
.sk{display:block;border-radius:6px;background:var(--color-gray);position:relative;overflow:hidden;
  color:transparent!important}
.sk--line{height:.9em;width:100%}
.sk--price{height:1em;width:4.5rem}
@media (prefers-reduced-motion:no-preference){
  .sk::after{content:"";position:absolute;inset:0;transform:translateX(-100%);
    background:linear-gradient(90deg,transparent,rgba(255,255,255,.7),transparent);
    animation:skshine 1.2s infinite}
  @keyframes skshine{to{transform:translateX(100%)}}
}

.checkout{display:grid;gap:2.5rem;grid-template-columns:1fr}
.cform{border:0;min-width:0;padding:0;margin:0 0 1.75rem;display:flex;flex-direction:column;gap:.35rem}
.cform legend{padding:0;font-size:.8rem;text-transform:uppercase;letter-spacing:.14em;
  color:var(--muted);font-weight:600;margin-bottom:.5rem}
.cform .radio{display:flex;align-items:center;gap:.6rem;padding:.4rem 0;font-size:.95rem;
  cursor:pointer;width:100%}
/* Способ получения выбирается уже в корзине, поэтому блок одинаков на обеих страницах
   и отбит от остального линией. */
.cform--fx{margin-bottom:1.25rem;padding-bottom:1.1rem;border-bottom:1px solid var(--hair)}
.radio__price{margin-left:auto;color:var(--muted);font-size:.85rem;white-space:nowrap}
/* Единственный доступный способ печатается строкой: радиокнопка, которую нельзя
   переключить, только притворяется выбором. */
.fxline{display:flex;align-items:center;gap:.6rem;margin:.4rem 0;font-size:.95rem}
.fxline b{font-weight:600;color:var(--color-heading)}
.checkout__pickup{background:var(--color-gray);border-radius:10px;padding:1rem 1.15rem;font-size:.9rem;color:var(--color-text)}
.checkout__pay,.checkout__soon{color:var(--muted);font-size:.75rem;text-align:center;margin:.85rem 0 0;
  text-transform:uppercase;letter-spacing:.12em}
.checkout__hint,.checkout__req{color:var(--muted);font-size:.8rem;line-height:1.5}
.checkout__edit{font-size:.78rem}
.checkout__consent{color:var(--muted);font-size:.8rem;text-align:center;margin:.85rem 0 0;line-height:1.5}
.checkout__consent a{text-decoration:underline;text-decoration-color:var(--color-border);text-underline-offset:3px}
.checkout__summary{border:1px solid var(--color-border);border-radius:var(--rounded);
  padding:1.75rem;height:max-content}
.checkout__summary h2{font-size:.8rem;text-transform:uppercase;letter-spacing:.14em;color:var(--muted)}
.checkout__head{display:flex;align-items:baseline;justify-content:space-between;gap:1rem}
.checkout__head a{font-size:.78rem;color:var(--color-primary)}
.checkout__lines{display:flex;flex-direction:column;gap:.5rem;margin-bottom:1.25rem}
.sl{display:flex;justify-content:space-between;gap:1rem;font-size:.88rem}
.sl b{white-space:nowrap;color:var(--color-heading)}

.acct__head{display:flex;align-items:center;justify-content:space-between;gap:1rem;flex-wrap:wrap}
.acct{display:grid;gap:1.75rem;grid-template-columns:1fr;margin:2rem 0 3rem}
.acct__box{border:1px solid var(--color-border);border-radius:var(--rounded);padding:1.75rem;min-width:0}
.acct__box h2{font-size:.8rem;text-transform:uppercase;letter-spacing:.14em;color:var(--muted);margin-bottom:1.1rem}
.acct__why{margin:0;padding:0;list-style:none;display:grid;gap:.7rem;font-size:.9rem;color:var(--color-text)}
.acct__why li{display:flex;gap:.6rem;line-height:1.5}
.acct__why li::before{content:"—";color:var(--muted);flex:none}
.acct__orders-h{margin-top:2.5rem;font-size:.8rem;text-transform:uppercase;letter-spacing:.14em;color:var(--muted)}
.auth{display:grid;gap:3rem;grid-template-columns:1fr}
.auth__col{min-width:0}
.auth__col h1,.auth__col h2{font-size:1.4rem;text-transform:none;letter-spacing:var(--lsp-head);margin-bottom:1.25rem}
.auth__yandex{margin-top:.85rem;width:100%}
.auth__hint{font-size:.85rem;color:var(--muted);margin-top:1.1rem;line-height:1.55}
/* Согласие на обработку персональных данных: три точки сбора (регистрация,
   оформление заказа, вход через Яндекс) — один класс.
   flex-wrap обязателен: без него flex-basis:100% у подписи ошибки не переносит её
   на свою строку, а лишь сжимает текст согласия в узкую колонку.
   .85rem — минимум для юридической подписи, на телефоне НЕ мельчим. */
.consent{display:flex;flex-wrap:wrap;align-items:flex-start;gap:.6rem;font-size:.85rem;
  color:var(--color-text);line-height:1.5;margin:1.1rem 0;
  min-height:var(--tap);padding-block:.35rem}
/* Размер и flex:none чекбоксу уже даёт общее правило (см. «form controls»); здесь
   только вертикальная подгонка к первой строке текста. Проверено: глобальный
   padding:.55rem 1.25rem задан списком типов (search/text/email/password/tel/number/url)
   и на checkbox не течёт — сбрасывать нечего. */
.consent input[type=checkbox]{width:1.15rem;height:1.15rem;flex:none;margin-block:.15rem}
.consent a{text-decoration:underline;text-decoration-color:var(--color-border);text-underline-offset:3px}
/* .field--err красит рамку, но у нативного чекбокса рамки нет — ошибка была бы
   невидимой. Кольцо тем же тоном, что и подпись ошибки. */
.consent.field--err input[type=checkbox]{outline:2px solid var(--color-primary-ink);outline-offset:2px}
.consent .field__err{flex-basis:100%;margin-top:.15rem}

.otable{width:100%;border-collapse:collapse;font-size:.9rem;margin:1.25rem 0}
.otable th,.otable td{text-align:left;padding:.75rem .5rem;border-bottom:1px solid var(--hair);vertical-align:top}
.otable thead th{color:var(--muted);font-weight:500;font-size:.7rem;text-transform:uppercase;letter-spacing:.1em}
.otable__total td{font-weight:700;border-top:1px solid var(--color-heading);border-bottom:0}
.otable tfoot td{border-bottom:0;color:var(--muted)}
.otable__items{color:var(--muted);font-size:.82rem;line-height:1.5}
.order__status{margin:.6rem 0 2rem;text-transform:uppercase;letter-spacing:.08em;font-size:.82rem;color:var(--muted)}
.order__meta{color:var(--color-text);font-size:.9rem;margin:2rem 0}
.order__meta p{margin:.4rem 0}
.order__pay{color:var(--muted);text-transform:uppercase;letter-spacing:.1em;font-size:.75rem}
.order__link{overflow-wrap:anywhere;font-size:.85rem;color:var(--muted)}
.order__keep,.order__help{color:var(--muted);font-size:.85rem;line-height:1.6}
.ostatus{display:inline-block;font-size:.66rem;font-weight:600;text-transform:uppercase;
  letter-spacing:.1em;padding:.2rem .6rem;border-radius:999em;border:1px solid var(--color-border);
  background:transparent;color:var(--color-heading)}
.ostatus--new{border-color:var(--color-primary);color:var(--color-primary)}
.ostatus--processing{border-color:#b6b0a6;color:#6f6960}
.ostatus--shipped{border-color:var(--color-secondary);color:var(--color-secondary)}
.ostatus--done{border-color:var(--color-border);color:var(--muted)}
.ostatus--canceled{border-color:var(--color-border);color:var(--muted);text-decoration:line-through}

.formmsg{border-radius:10px;padding:.85rem 1.15rem;margin:0 0 1.5rem;font-size:.9rem;line-height:1.55}
.formmsg--ok{background:var(--color-secondary-soft);color:var(--color-secondary)}
.formmsg--err{background:var(--color-primary-soft);color:var(--color-primary-ink)}
.formmsg+.formmsg{margin-top:-.9rem}

/* toast: "added to cart · go to cart" */
/* Отступ снизу — через переменную: на телефоне тост обязан подняться над липкой
   панелью покупки, а .pdp .toast не сработает — тост лежит отдельным узлом в конце
   <body>, вне <main>. Класс вешает JS на корень (html.is-buybar). */
.toast{position:fixed;z-index:40;right:1.25rem;bottom:var(--toast-bottom,1.25rem);max-width:22rem;
  background:var(--color-heading);color:#fff;border-radius:14px;padding:.9rem 1.1rem;
  box-shadow:0 8px 32px rgba(0,0,0,.25);font-size:.9rem;line-height:1.4;
  display:flex;gap:.75rem;align-items:center;
  opacity:0;transform:translateY(8px);pointer-events:none;
  transition:opacity .25s var(--ease),transform .25s var(--ease)}
.toast.is-on{opacity:1;transform:none;pointer-events:auto}
.toast a{color:#fff;text-decoration:underline;white-space:nowrap;font-weight:600}
.toast a:hover{color:#fff;opacity:.8}
.toast :focus-visible{outline-color:#fff}
/* Текст живёт в live-области и должен ужиматься, а не выдавливать кнопки. */
.toast__msg{flex:1 1 auto;min-width:0}
.toast__undo,.toast__x{background:none;border:0;color:#fff;cursor:pointer;font-weight:600}
.toast__undo{text-decoration:underline;white-space:nowrap}
.toast__x{font-size:1.2rem;line-height:1;padding:.25rem;min-width:var(--tap);min-height:var(--tap);
  display:grid;place-items:center}

/* ---------- полоса про cookie ----------
   Это ИНФОРМИРОВАНИЕ, а не гейт согласия. Сторонней аналитики, рекламы и пикселей на
   сайте нет, а все куки (tc_lang, tc_shop, tc_uid, tc_preview, tc_shop_admin) строго
   необходимы: отключение ломает вход, корзину и выбор языка. Поэтому здесь ОДНА кнопка
   и нет кнопки «Отклонить» — она либо солгала бы (куки всё равно ставятся), либо
   сломала бы магазин; фальшивый выбор хуже его отсутствия. Отсюда и вид: узкая полоса
   без затемнения, без модальности и без перехвата фокуса.
   Фон тёмный (--color-heading), как шапка сайта: полоса читается как элемент
   интерфейса, а не как кусок контента, и не спорит с тёплой бумагой страницы.
   position:fixed, а не sticky: фиксированная полоса не участвует в потоке и не даёт
   ни пикселя CLS. Разметку печатает сервер и только при отсутствии куки tc_ck,
   поэтому мигания «показали — убрали» нет вовсе.
   z-index 45 — строго между тостом (40) и шторкой (50); «к содержимому» (60) выше всех. */
.ckbar{position:fixed;left:0;right:0;bottom:0;z-index:45;
  background:var(--color-heading);color:#fff}
.ckbar__in{max-width:var(--wrap);margin-inline:auto;padding-inline:var(--pad);
  display:flex;align-items:center;gap:1rem;min-height:var(--ck-h);
  padding-block:.75rem;padding-bottom:calc(.75rem + env(safe-area-inset-bottom))}
.ckbar__t{flex:1 1 auto;margin:0;font-size:.85rem;line-height:1.45}
.ckbar__t a{color:#fff;text-decoration:underline;
  text-decoration-color:rgba(255,255,255,.45);text-underline-offset:3px}
.ckbar__t a:hover{color:#fff;text-decoration-color:#fff}
/* Светлая пилюля: чёрная .btn на чёрной полосе не читается (тот же случай, что
   .btn--light на фото героя). Специфичность равна .btn (0,1,0), поэтому блок обязан
   стоять ПОСЛЕ секции кнопок — выигрывает порядком. */
.ckbar__ok{flex:none;background:#fff;border-color:#fff;color:var(--color-heading)}
.ckbar__ok:hover{background:var(--color-gray);border-color:var(--color-gray);
  color:var(--color-heading)}
/* Подложка тёмная — чернильное кольцо фокуса на ней не видно (приём из шапки). */
.ckbar :focus-visible{outline-color:#fff}

/* Полоса fixed и перекрывает низ страницы. Класс has-ck ставит СЕРВЕР ровно тогда,
   когда полоса напечатана, поэтому подъём соседей работает и без JS.
   padding у body — чтобы полоса не закрывала последнюю строку подвала. */
html.has-ck body{padding-bottom:var(--ck-h)}
html.has-ck{--toast-bottom:calc(var(--ck-h) + 1rem + env(safe-area-inset-bottom))}

/* На печати полосы нет — значит нет и резерва под неё, иначе внизу листа
   останется пустая полоса высотой в баннер. */
@media print{ .ckbar{display:none} html.has-ck body{padding-bottom:0} }
@media (forced-colors:active){
  .ckbar{background:Canvas;color:CanvasText;border-top:1px solid CanvasText}
  .ckbar__t a{color:LinkText}
  .ckbar__ok{background:ButtonFace;color:ButtonText;border-color:ButtonText}
}

/* ---------- responsive ---------- */
@media (min-width:48rem){
  .grid,.grid--3{grid-template-columns:repeat(3,1fr)}
  .starter__grid{grid-template-columns:repeat(3,1fr)}
  .why{grid-template-columns:repeat(3,1fr)}
  .hdr__nav{display:flex;gap:2.25rem;font-size:.82rem;text-transform:uppercase;letter-spacing:.1em;margin-left:1.5rem}
  .hdr__nav a{color:var(--header-ink)}
  .hdr__nav a:hover{color:var(--header-ink);opacity:.55}
  .hdr__burger{display:none}
  .pdp__grid{grid-template-columns:1.05fr 1fr}
  .cards2{grid-template-columns:1fr 1fr}
  /* auto-fit keeps the footer honest whether it has three columns or four */
  .foot__in{grid-template-columns:repeat(auto-fit,minmax(11rem,1fr))}
  /* Сводка фиксированной шириной 21rem на 768px оставляла списку 313px, из них телу
     строки 104px — в строке теперь не только имя, но и чипы веса, селект помола и
     степпер. Ширина сводки сжимается на планшете и приходит к прежним 21rem на десктопе;
     minmax(0,1fr) не даёт колонке списка распираться содержимым. */
  .cart{grid-template-columns:minmax(0,1fr) clamp(15rem,26vw,21rem)}
  /* Итог не должен уезжать за экран при длинном списке. align-self обязателен:
     в grid элемент по умолчанию растянут на всю строку, и липнуть ему нечем. */
  .cart__side{position:sticky;top:calc(var(--hdr-h) + 1rem);align-self:start}
  .checkout{grid-template-columns:1.5fr 1fr}
  /* Сводка стоит в DOM ПЕРЕД формой (порядок табуляции), визуальный порядок
     возвращаем сеткой, а не order — иначе он разойдётся с порядком чтения. */
  .checkout__form{grid-column:1;grid-row:1}
  .checkout__summary{grid-column:2;grid-row:1;position:sticky;top:calc(var(--hdr-h) + 1rem);align-self:start}
  .acct{grid-template-columns:1fr 1fr}
  .auth{grid-template-columns:1fr 1fr}
}
@media (min-width:64rem){
  .grid{grid-template-columns:repeat(4,1fr)}
  .grid--3{grid-template-columns:repeat(3,1fr)}
}

/* ---------- mobile (one block for the whole site) ---------- */
@media (max-width:47.999rem){
  /* iOS Safari zooms into fields smaller than 16px and never zooms back out */
  .sel,input[type=search],input[type=text],input[type=email],input[type=password],
  input[type=tel],input[type=number],input[type=url],textarea,.search__form input{font-size:16px}

  /* 44px touch targets. The header bar is already tight at 375px (the brand name must
     read in full), so its icons grow an invisible hit area instead of real width. */
  .ic::after,.hdr__burger::after{content:"";position:absolute;top:50%;left:50%;
    transform:translate(-50%,-50%);width:calc(100% + .4rem);height:var(--tap)}
  .fchip,.wchip{padding:.6rem 1rem;font-size:.85rem}
  .shopnav a{min-height:var(--tap);align-items:center}
  /* Тач-цель добираем ТОЛЬКО по вертикали: по горизонтали в шапке дефицит,
     и min-width обрезал надпись «Toggle Coffee» до «Toggle …» на 375px. */
  .ic{min-height:var(--tap)}
  .hdr__burger{min-height:var(--tap);align-content:center}
  .fchip,.wchip{min-height:var(--tap);display:inline-flex;align-items:center}
  .wchips--lg .wchip{padding:.6rem 1.1rem}
  .btn--sm{height:var(--tap);padding-inline:1.35rem}
  .pager a{min-width:var(--tap);height:var(--tap)}
  .cl__rm{min-height:var(--tap)}
  .filters__sort a{display:inline-block;padding-block:.55rem}
  .filters__adv>summary{min-height:var(--tap)}
  /* .search__close/.search__clear переехали в @media (pointer:coarse) в конце файла:
     44px им нужны и на iPad в портрете (ровно 768px — мимо этого порога). */
  .drawer__close{width:var(--tap);height:var(--tap);display:grid;place-items:center}
  .hint>summary{width:1.6rem;height:1.6rem;font-size:.8rem}
  .cform .radio{padding:.6rem 0;min-height:var(--tap)}
  .pdp__thumbs button{width:var(--tap);height:var(--tap)}
  /* WARNING: never touch the .qty input padding here — the digit disappears. */
  .cl__ctl .qty button,.cl__ctl .qty input{height:var(--tap);width:2.5rem}

  /* the smallest type gets a notch more air */
  .pc__country,.meter__l,.filters__l,.ac__k,.story__k,.article__k,
  .cfacts dt,.ccard__k,.creq__h{font-size:.75rem;letter-spacing:.1em}
  /* Контакты: пилюли шапки во всю ширину — палец не промахивается. Водяной знак
     самолёта на телефоне гасим: на 375px он ложится прямо под адрес. */
  .chead__act .btn{flex:1 1 100%}
  .pcb{font-size:.68rem}
  .pc__proc{font-size:.78rem}
  .pc__sub,.ac__x{font-size:.9rem}
  .pdp__pay,.checkout__pay,.order__pay,.foot__bar-in{font-size:.8rem}

  .grid .meters{display:none}
  .pc__row{flex-wrap:wrap}
  .pc__row .btn--sm{width:100%}
  /* buy button stays under the thumb at any scroll depth */
  .pdp__buy{position:sticky;bottom:0;z-index:3;background:var(--bg);
    padding-block:.75rem;padding-bottom:calc(.75rem + env(safe-area-inset-bottom));
    box-shadow:0 -1px 0 var(--hair)}
  /* Сводка и так идёт первой в DOM — order больше не нужен. */
  .checkout__summary{margin-bottom:.5rem}
  /* Итог и «К оформлению» — под большим пальцем, а не в конце длинной прокрутки.
     Приём тот же, что у .pdp__buy выше. */
  /* .cart — грид, а у грид-элемента контейнинг-блок это его собственная грид-область:
     она равна самому элементу, диапазона для липкости нет, и position:sticky не работал
     вовсе. У флекс-элемента контейнинг-блок — весь контейнер, поэтому на телефоне,
     где колонка всё равно одна, переводим .cart во флекс. Ровно поэтому .pdp__buy
     липнет исправно: его родитель — обычный блок. */
  .cart{display:flex;flex-direction:column}
  .cartbar{display:flex;align-items:center;gap:.9rem;position:sticky;bottom:0;z-index:3;
    background:var(--bg);padding-block:.75rem;
    padding-bottom:calc(.75rem + env(safe-area-inset-bottom));
    box-shadow:0 -1px 0 var(--hair)}
  .cartbar .btn{margin-left:auto;flex:1 1 auto;max-width:60%}
  /* Потолок выдачи считаем от ВИЗУАЛЬНОГО вьюпорта — единственного, что знает про
     экранную клавиатуру. На 375×667 с клавиатурой 300px доступно ~103px (две строки,
     обе видны), а прежние min(50vh,22rem) объявляли 333px, из которых была видна
     меньше трети. --vvh и --sbot пишет store.js; значения по умолчанию подобраны так,
     что формула верна и без JS. */
  .search__results{max-height:calc(var(--vvh,var(--sv)) - var(--sbot,9.4rem) - .5rem);min-height:0}
  /* Выдача поиска дотягивается до низа визуального вьюпорта, а полоса про cookie
     лежит поверх неё — последняя строка результатов оказывалась бы под плашкой.
     Резерв вычитается ровно тем же токеном, что поднимает липкие панели.
     Пол в 6rem обязателен: в ландшафте с открытой клавиатурой разность уходит
     в минус, а отрицательная max-height клампится в НОЛЬ — выдача исчезла бы
     целиком. Две строки под плашкой лучше, чем пустота. */
  html.has-ck .search__results{
    max-height:max(6rem,calc(var(--vvh,var(--sv)) - var(--sbot,9.4rem) - var(--ck-h) - .5rem))}
  .sr__row{min-height:var(--tap);align-items:center}
  .sr__chips .chip{min-height:var(--tap)}
  .sr__m{font-size:.75rem;letter-spacing:.1em}
  /* Ссылка, «Вернуть» и крестик неразрывны и на 375px съедали текст до колонки в 83px
     (тост вырастал до 130px в пять строк). Сообщение забирает первую строку целиком. */
  .toast{left:1rem;right:1rem;--toast-bottom:calc(1rem + env(safe-area-inset-bottom));
    max-width:none;flex-wrap:wrap;row-gap:.35rem}
  .toast__msg{flex-basis:100%}
  .toast__x{margin-left:auto}
  /* Липкая .pdp__buy лежит на z-index:3 и на 4 секунды съедала нажатия по кнопке
     покупки — тост встаёт над ней. На десктопе панель не липкая, правило не нужно. */
  html.is-buybar .toast{--toast-bottom:calc(5.5rem + env(safe-area-inset-bottom))}

  /* Полоса про cookie лежит поверх липких панелей: без подъёма покупатель на
     телефоне физически не дотянулся бы до «К оформлению» и «В корзину».
     У sticky нижний офсет работает так же, как у fixed: панель остановится
     ВЫШЕ полосы. Специфичность (0,2,1) перебивает исходные .cartbar/.pdp__buy. */
  html.has-ck .cartbar,
  html.has-ck .pdp__buy{bottom:var(--ck-h)}
  /* Тост: на телефоне --toast-bottom задан НА САМОМ .toast, и значение с html
     туда не дотянется — переменную приходится переопределять тоже на элементе.
     Комбинированный селектор (0,3,1) сильнее и .toast, и html.is-buybar .toast,
     поэтому порядок объявления роли не играет: на карточке товара с полосой
     тост встаёт над кнопкой покупки, а кнопка — над полосой. */
  html.has-ck .toast{--toast-bottom:calc(var(--ck-h) + 1rem + env(safe-area-inset-bottom))}
  html.has-ck.is-buybar .toast{--toast-bottom:calc(var(--ck-h) + 5.5rem + env(safe-area-inset-bottom))}
}
@media (max-width:36rem){
  .hero__cta .btn{flex:1 1 100%}
  .pageend .btn{flex:1 1 100%}
  /* Тот же приём, что у кнопок выше: в одну строку текст и пилюля здесь уже не
     помещаются, кнопка уезжает на свою строку во всю ширину. */
  .ckbar__in{flex-wrap:wrap}
  .ckbar__ok{width:100%}
  .cl{grid-template-columns:56px 1fr;gap:.9rem;align-items:start}
  .cl__media{width:56px;height:56px}
  /* Цена уходит под текст и прижата влево — значит и подпись «890 ₽ × 3» внутри неё
     равняется по левому краю, иначе колонка выглядит случайно отцентрованной. */
  .cl__price{grid-column:2;justify-self:start;margin-top:.5rem;align-items:flex-start}
  .cl__ctl{flex-wrap:wrap;gap:.75rem}
  .otable{font-size:.82rem}
  .otable th,.otable td{padding:.6rem .3rem}
  .otable td:nth-child(n+2){white-space:nowrap;text-align:right}
  .article__nav{flex-direction:column;gap:.9rem}
  .article__next{margin-left:0;text-align:left}
  .article__nav a{display:block;padding-block:.5rem}
  .grid--list .pc{gap:1rem}
  .grid--list .pc__media{width:72px}
}
@media (max-width:22rem){
  .grid,.grid--3{grid-template-columns:1fr}
  .grid{gap:2rem}
  /* Отвоёвывает ~15px: на 320px полю остаётся ~210px, и укороченного плейсхолдера
     «Сорт, страна, статья» (~166px) хватает — полный текст ушёл в aria-describedby. */
  .search__form{gap:.5rem;padding-inline:1rem}
  .search__field{padding-inline:.8rem}
}

/* Ландшафт телефона и 400% зум.
   812×375: шапка 72.8 + форма + клавиатура ~200px не оставляют под выдачу НИЧЕГО
   (посчитано: −19px; на 568×320 −64px). Гейт тот же, что у слайдера, и БЕЗ orientation —
   иначе портретный зум 400% (320×200) в него не попадёт. Прячем верхний ярус шапки
   только пока идёт поиск: возвращается ~76px, то есть две строки вместо нуля. */
@media (max-height:30rem) and (min-aspect-ratio:5/8){
  html.is-searching .hdr__in{display:none}
  .search__form{padding-block:.35rem}
  .sr__row{padding-block:.3rem;min-height:0;grid-template-columns:36px minmax(0,1fr) auto}
  .sr__media{width:36px;height:36px}
  .sr__media--wide{height:24px}
  .sr__all{grid-template-columns:minmax(0,1fr);padding-left:calc(36px + .9rem)}
  .sr__t{-webkit-line-clamp:1}
}

/* ---------- motion ---------- */
@media (prefers-reduced-motion:no-preference){
  .drawer__panel{animation:slidein .22s var(--ease)}
  @keyframes slidein{from{transform:translateX(-100%)}}
}
@media (prefers-reduced-motion:reduce){
  *{transition:none!important;animation:none!important;scroll-behavior:auto!important}
  .pc:hover .pc__media img,.ac:hover .ac__media img,.story:hover img{transform:none}
  /* Слайдер: автопрокрутки нет (её выключает store.js), значит останавливать нечего —
     кнопка паузы убирается. Смена слайда мгновенная: visibility не должна ждать
     несуществующего фейда, иначе уходящий слайд задержится на экране. */
  .hs__play{display:none}
  .hs__slide,.hs__slide.is-on{transition:none}
}

/* ---------- touch ----------
   Порог мобильного блока (47.999rem) отсекает iPad в портрете и сенсорный ноутбук:
   там «−»/«+» остаются 30×34px, а «Убрать» — около 22px. Гейт по вводу, а не по ширине.
   Специфичность равна исходным правилам корзины — выигрывает порядком, поэтому блок
   обязан стоять В КОНЦЕ файла. */
@media (pointer:coarse){
  .cl__rm{min-height:var(--tap)}
  /* Пилюля .qty задана высотой 3.25rem: без auto внутри неё останутся мёртвые 9px
     сверху и снизу, и нажатие мимо кнопки не сработает. */
  .cl__ctl .qty{height:auto}
  /* WARNING: не трогать padding у .qty input — цифра исчезнет. */
  .cl__ctl .qty button,.cl__ctl .qty input{height:var(--tap);width:2.5rem}
  /* Поиск: гейт по вводу, а не по ширине. iPad в портрете — ровно 768px и в мобильный
     порог не попадает, а крестик там раньше был примерно 11×22px. У «Закрыть» это
     min-width, а не width: внутри слово, и жёсткая коробка 44px его бы выдавила. */
  .search__clear{width:var(--tap);height:var(--tap);display:grid;place-items:center}
  .search__close{min-width:var(--tap);min-height:var(--tap);display:grid;place-items:center}
}
