/* Only3D — design tokens lifted from the Figma "Only3D Tokens" collection.
   Keep these in sync with the Figma variables; they are the same design system. */

:root {
  --bg-base:      #F7F7F5;
  --bg-surface:   #FFFFFF;
  --bg-muted:     #EEEEEA;

  --text-primary:   #1A1A1A;
  --text-secondary: #5C5C58;
  --text-inverse:   #FFFFFF;

  --border: #E2E2DC;

  --accent:      #FF5A1F;
  --accent-dark: #D8460F;
  --teal:        #0FA3A3;

  --success: #1E9E5A;
  --danger:  #D64545;

  --r-sm: 8px;
  --r-md: 12px;
  --r-lg: 16px;
  --r-xl: 24px;

  --s-1: 4px;  --s-2: 8px;  --s-3: 12px; --s-4: 16px;
  --s-5: 24px; --s-6: 32px; --s-7: 48px; --s-8: 64px;

  --max-w: 1440px;
  --gutter: 48px;
}

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

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

body {
  margin: 0;
  font-family: Inter, -apple-system, "Segoe UI", Roboto, Arial, sans-serif;
  font-size: 16px;
  line-height: 1.5;
  color: var(--text-primary);
  background: var(--bg-base);
}

img { max-width: 100%; display: block; }
a { color: inherit; text-decoration: none; }

/* Header brand block: wordmark, drawn logo, slogan. Wraps to two lines on a
   narrow screen instead of squeezing the nav out of the bar. */
/* flex-shrink: 0 — иначе .brand как обычный сжимаемый flex-элемент в строке
   topbar сжимался первым при недостатке места и переносил логотип+слоган в
   два ряда сам по себе; из-за выросшей высоты шапки логотип визуально уезжал
   вверх относительно центрированных nav/кнопок, хотя формально всё было
   отцентрировано по общей (выросшей) высоте строки. */
.brand { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; flex-shrink: 0; }
/* Хвост «y» в файле логотипа тянется заметно ниже базовой линии остальных
   букв, поэтому центрирование по границам картинки визуально задирает «O»
   выше кнопок рядом. Сдвиг вниз — не хак с рандомным числом: в файле буква
   «O» кончается на 26 из 132px высоты канвы выше её нижнего края (из-за
   хвоста «y»), тот же процент площади сдвинут здесь на отображаемой высоте. */
.brand__mark { height: 40px; width: auto; display: block; transform: translateY(3px); }
.brand__slogan {
  font-size: 20px; font-style: italic; color: var(--text-secondary);
  white-space: nowrap;
}
@media (max-width: 900px) { .brand__slogan { display: none; } }
@media (max-width: 560px) { .brand__mark { height: 30px; transform: translateY(2px); } }

/* One-time credential display: monospace so O/0 and l/1 are distinguishable,
   and selectable as a whole word for copying. */
.cred {
  font-family: ui-monospace, "SF Mono", Consolas, monospace;
  font-size: 14px; letter-spacing: .3px;
  background: var(--bg-muted); border: 1px solid var(--border);
  border-radius: var(--r-sm); padding: 4px 10px;
  user-select: all; word-break: break-all;
}

.wrap { max-width: var(--max-w); margin: 0 auto; padding-inline: var(--gutter); }

/* ---------- typography ---------- */
h1 { font-size: 32px; line-height: 40px; font-weight: 700; letter-spacing: -.3px; margin: 0; }
h2 { font-size: 24px; line-height: 32px; font-weight: 600; letter-spacing: -.2px; margin: 0; }
h3 { font-size: 18px; line-height: 26px; font-weight: 600; margin: 0; }
.display { font-size: 44px; line-height: 52px; font-weight: 700; letter-spacing: -.5px; margin: 0; }
.muted { color: var(--text-secondary); }
.small { font-size: 14px; }
.caption { font-size: 12px; color: var(--text-secondary); }

/* ---------- header ---------- */
/* Grid на 3 колонки, а не flex + position:absolute на меню: абсолютный
   left:42% был подогнан под один экран (1440px) и на более узких/широких
   desktop-ширинах либо наезжал на кнопки, либо на лого — процент не знает
   о реальной ширине соседних блоков. auto/1fr/auto вместо этого отдаёт
   лого и кнопкам ровно ту ширину, что им нужна, а меню центрируется в
   оставшемся месте между ними — оно физически не может наложиться на
   соседей, какой бы ни была ширина окна. */
.topbar {
  display: grid; grid-template-columns: auto 1fr auto; align-items: center;
  gap: var(--s-5);
  padding: 18px var(--gutter);
  background: var(--bg-surface);
  border-bottom: 1px solid var(--border);
  position: sticky; top: 0; z-index: 20;
}
.logo { font-size: 22px; font-weight: 700; }
.topbar nav {
  display: flex; gap: 28px;
  justify-self: center;
}
.topbar nav a { font-size: 20px; font-weight: 500; color: var(--text-secondary); text-align: center; }
.topbar nav a:hover, .topbar nav a[aria-current] { color: var(--text-primary); }
.topbar .actions { display: flex; align-items: center; gap: var(--s-4); }

/* ---------- buttons ---------- */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--s-2);
  padding: 12px 20px; border-radius: var(--r-md);
  font: inherit; font-size: 15px; font-weight: 600;
  border: 1px solid transparent; cursor: pointer;
  transition: background .12s ease, border-color .12s ease;
}
.btn--primary { background: var(--accent); color: var(--text-inverse); }
.btn--primary:hover { background: var(--accent-dark); }
.btn--secondary { background: var(--bg-surface); border-color: var(--border); }
.btn--secondary:hover { background: var(--bg-muted); }
.btn--ghost { background: transparent; color: var(--text-secondary); }
.btn--sm { padding: 8px 12px; font-size: 13px; border-radius: 10px; }
.btn--block { width: 100%; }
.btn[disabled] { opacity: .5; cursor: not-allowed; }

/* ---------- badges & pills ---------- */
.badge {
  display: inline-flex; align-items: center;
  padding: 5px 10px; border-radius: 999px;
  background: var(--bg-muted); color: var(--text-secondary);
  font-size: 12px; font-weight: 500;
}
.badge--success { color: var(--success); }
.badge--danger  { color: var(--danger); }
.badge--accent  { background: var(--accent); color: var(--text-inverse); }

/* ---------- cards & grid ---------- */
.grid { display: grid; gap: 20px; }
.grid--4 { grid-template-columns: repeat(4, 1fr); }
.grid--3 { grid-template-columns: repeat(3, 1fr); }

.card {
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  overflow: hidden;
  display: flex; flex-direction: column;
}
.card__media {
  /* Вертикальные 3:4, а не горизонтальные: каталог чаще всего смотрят с телефона,
     где ширина зажата, а высота бесплатна. Портретная плитка показывает предмет
     крупнее при той же ширине колонки. Тот же формат просим загружать — см.
     подсказку в админке. */
  aspect-ratio: 3 / 4; background: var(--bg-muted);
  display: grid; place-items: center; color: var(--text-secondary); font-size: 12px;
  /* Без overflow и min-*: 0 ниже картинка прорывает рамку: как грид-элемент она
     получает min-height: auto, то есть не может стать ниже своей собственной
     высоты, и контейнер растёт вслед за ней — aspect-ratio молча перестаёт
     работать. Именно поэтому портретные фото делали плитки каталога разной
     высоты. Замерено: 514 / 285 / 214 px до правки, 214 у всех после. */
  overflow: hidden;
}
.card__media img {
  width: 100%; height: 100%; object-fit: cover;
  min-width: 0; min-height: 0;
}
.card__body { padding: 14px 16px 16px; display: flex; flex-direction: column; gap: var(--s-2); }
.card__title { font-size: 16px; font-weight: 600; }
.card__foot {
  display: flex; align-items: center; justify-content: space-between;
  flex-wrap: wrap; gap: var(--s-2); margin-top: auto;
}
.price { font-size: 18px; font-weight: 700; }
.price--old { font-size: 14px; font-weight: 400; color: var(--text-secondary); text-decoration: line-through; }

/* ---------- forms ---------- */
.field { display: flex; flex-direction: column; gap: 6px; }
.field > label { font-size: 12px; font-weight: 500; color: var(--text-secondary); }
.input, .textarea, .select {
  width: 100%; padding: 10px 12px;
  font: inherit; font-size: 14px;
  background: var(--bg-surface); color: var(--text-primary);
  border: 1px solid var(--border); border-radius: 10px;
}
.input:focus, .textarea:focus, .select:focus {
  outline: 2px solid var(--accent); outline-offset: -1px; border-color: var(--accent);
}
.textarea { min-height: 84px; resize: vertical; }

/* Option chips (material / colour pickers) — a styled radio group. */
.chips { display: flex; flex-wrap: wrap; gap: var(--s-2); }
.chip input { position: absolute; opacity: 0; width: 0; height: 0; }
.chip > span {
  display: inline-block; padding: 8px 14px; border-radius: 10px;
  border: 1px solid var(--border); background: var(--bg-surface);
  font-size: 13px; font-weight: 500; cursor: pointer;
}
.chip input:checked + span { background: var(--text-primary); color: var(--text-inverse); border-color: var(--text-primary); }
.chip input:focus-visible + span { outline: 2px solid var(--accent); outline-offset: 2px; }

/* ---------- layout helpers ---------- */
.stack   { display: flex; flex-direction: column; gap: var(--s-4); }
.row     { display: flex; align-items: center; gap: var(--s-3); }
.between { display: flex; align-items: center; justify-content: space-between; gap: var(--s-3); }
.section { padding-block: var(--s-7); }
.panel {
  background: var(--bg-surface); border: 1px solid var(--border);
  border-radius: var(--r-lg); padding: 20px;
}

/* ---------- breadcrumbs ---------- */
.breadcrumbs {
  display: flex; flex-wrap: wrap; align-items: center; gap: 6px;
  margin-bottom: var(--s-5);
  font-size: 13px; color: var(--text-secondary);
}
.breadcrumbs a { color: var(--text-secondary); text-decoration: none; }
.breadcrumbs a:hover { color: var(--accent); text-decoration: underline; }
.breadcrumbs span[aria-current] { color: var(--text-primary); }
.breadcrumbs__sep { color: var(--border); }

/* ---------- hero ---------- */
/* align-items: start, не center — иначе более высокая колонка (фото) съедает
   лишнюю высоту поровну вверх и вниз, и кадры обрезаются сверху, выше
   заголовка. Так фото стартуют вровень с "Вещи..." Левая колонка чуть шире
   правой (6fr/5fr, не 1fr/1fr) — с укрупнённой витриной текстовый блок иначе
   выглядит слишком узким рядом с ней. */
.hero { display: grid; grid-template-columns: 6fr 5fr; gap: var(--s-7); align-items: start; padding-block: var(--s-8); }
/* Больше воздуха между заголовком, текстом и кнопками — так текстовый блок
   тянется вниз ближе к высоте укрупнённой витрины справа, сами кнопки не
   меняются в размере. */
.hero > .stack { gap: var(--s-6); }
.hero__media { aspect-ratio: 3 / 2; background: var(--bg-muted); border-radius: var(--r-xl); }

/* ---------- category strip ---------- */
.cats { display: grid; grid-template-columns: repeat(6, 1fr); gap: var(--s-4); }
.cat {
  display: flex; flex-direction: column; align-items: center; gap: 10px;
  padding: 20px; border-radius: var(--r-lg);
  background: var(--bg-surface); border: 1px solid var(--border);
  font-size: 13px; font-weight: 600; text-align: center;
}
.cat:hover { border-color: var(--accent); }
.cat__dot { width: 28px; height: 28px; border-radius: 50%; background: var(--teal); }

/* ---------- cart ---------- */
.qty { display: inline-flex; align-items: center; gap: 14px; padding: 6px 12px; background: var(--bg-muted); border-radius: 10px; }
.qty button { border: 0; background: none; font: inherit; font-weight: 700; color: var(--text-secondary); cursor: pointer; padding: 0 2px; }
.cart-line { display: flex; align-items: center; gap: var(--s-4); }
.cart-line__remove { white-space: nowrap; color: var(--text-secondary); }
.cart-line__remove:hover { color: var(--danger); }
/* Below ~560px the line wraps; the remove control then sits on its own row and
   must not stretch across the whole card. */
@media (max-width: 560px) {
  .cart-line { flex-wrap: wrap; }
  .cart-line__remove { margin-left: auto; }
}
.cart-line__thumb { width: 88px; height: 88px; border-radius: var(--r-md); background: var(--bg-muted); flex: none; object-fit: cover; }

/* ---------- upload dropzone ---------- */
.dropzone {
  display: flex; flex-direction: column; align-items: center; gap: 6px;
  padding: 40px 24px; text-align: center; cursor: pointer;
  background: var(--bg-base);
  border: 1px dashed var(--accent); border-radius: var(--r-lg);
}
.dropzone:hover { background: var(--bg-muted); }
/* The input stays in the layout (not display:none) so it remains keyboard
   focusable and still accepts a dropped file. */
.dropzone input[type=file] { position: absolute; width: 1px; height: 1px; opacity: 0; }
.dropzone:focus-within { outline: 2px solid var(--accent); outline-offset: 2px; }
.dropzone__icon { width: 40px; height: 40px; border-radius: 50%; background: var(--accent); }
.dropzone__title { font-size: 15px; font-weight: 600; }

.steps { margin: 0; padding-left: 20px; display: flex; flex-direction: column; gap: 8px; font-size: 14px; color: var(--text-secondary); }
.steps li::marker { color: var(--accent); font-weight: 700; }

/* ---------- checkout ---------- */
.checkout-grid { display: grid; grid-template-columns: 1fr 360px; gap: var(--s-6); align-items: start; }
.checkout-summary { gap: 10px; position: sticky; top: 96px; }
@media (max-width: 900px) {
  .checkout-grid { grid-template-columns: 1fr; }
  .checkout-summary { position: static; }
}

/* ---------- footer ----------
   Один столбец сверху вниз: строка «бренд + написать нам», затем отдельной
   строкой копирайт на всю ширину — а не бренд слева / копирайт справа на
   одной линии, как было раньше. Со space-between копирайт вечно оказывался
   на другой видимой "линии", чем кнопка письма, и выглядело как два разных
   блока, а не один подвал. */
.footer {
  margin-top: var(--s-8); padding: 40px var(--gutter);
  background: var(--text-primary); color: var(--text-inverse);
  display: flex; flex-direction: column; gap: 16px;
  font-size: 13px;
}
.footer__top { display: flex; align-items: center; gap: var(--s-4); flex-wrap: wrap; }

/* The footer sits on the dark surface, so its links need an explicit accent —
   inheriting white would make the mail address indistinguishable from prose. */
.footer a { text-decoration: underline; text-underline-offset: 2px; }

/* Email is the shop's main contact channel, so it gets a solid accent block
   rather than an underlined link lost in the footer text. */
.footer__mail {
  display: inline-flex; align-items: center; gap: 12px;
  padding: 10px 16px; border-radius: var(--r-md);
  background: var(--accent); color: var(--text-inverse);
  text-decoration: none; width: fit-content;
  transition: background .12s ease;
}
.footer__mail:hover { background: var(--accent-dark); }
.footer__mail-icon { font-size: 18px; line-height: 1; }
.footer__mail-text { font-size: 13px; font-weight: 600; white-space: nowrap; }
/* Адрес — только десктопная часть строки; на узком экране остаётся короткая
   подпись, а ссылка mailto: всё равно рабочая без него. */
.footer__mail-addr { font-weight: 700; }

/* Same address, called out inside a page body. */
.mail-callout {
  display: flex; flex-direction: column; gap: 4px;
  padding: 14px 16px; border-radius: var(--r-md);
  background: var(--bg-muted); border-left: 3px solid var(--accent);
}
.mail-callout__addr { font-size: 16px; font-weight: 700; color: var(--accent); }
.mail-inline { color: var(--accent); font-weight: 600; text-decoration: underline; }

/* ---------- misc ---------- */
.empty { padding: var(--s-8) 0; text-align: center; color: var(--text-secondary); }
.htmx-request { opacity: .6; transition: opacity .1s; }

/* ---------- responsive ---------- */
@media (max-width: 1100px) {
  :root { --gutter: 24px; }
  .grid--4 { grid-template-columns: repeat(3, 1fr); }
  .cats { grid-template-columns: repeat(3, 1fr); }
}
@media (max-width: 760px) {
  :root { --gutter: 20px; }
  .grid--4, .grid--3 { grid-template-columns: repeat(2, 1fr); }
  .hero { grid-template-columns: 1fr; padding-block: var(--s-6); }
  .display { font-size: 32px; line-height: 40px; }
  .topbar nav { display: none; }
  .cats { grid-template-columns: repeat(2, 1fr); }
  /* По центру — иначе бренд, кнопка и копирайт повисают у левого края. */
  .footer { align-items: center; text-align: center; }
  .footer__top { justify-content: center; }
  /* Адрес в самой кнопке — на мобильном лишний: сама ссылка mailto: уже ведёт
     на нужный адрес, а короткая подпись в одну строку читается быстрее, чем
     полная строка с адресом. */
  .footer__mail-addr { display: none; }
  .footer__mail-text { font-size: 14px; }
}

/* Terms pages: a readable measure and generous rhythm — these are long documents
   people actually have to get through. */
.legal { max-width: 760px; }
.legal h2 { font-size: 19px; margin: 32px 0 10px; }
.legal p { margin: 0 0 12px; line-height: 1.65; }
.legal-list { margin: 0 0 14px; padding-left: 20px; line-height: 1.65; }
.legal-list li { margin-bottom: 6px; }
.consent { padding: 4px 0; }

/* Inline explainer attached to a choice the customer just made — softer than an
   error, more visible than a caption. */
.notice {
  padding: 12px 14px; border-radius: var(--r-md);
  background: var(--bg-muted); border-left: 3px solid var(--teal);
  font-size: 14px;
}

/* Site-wide notice strip. Sits under the sticky header, not over it, so it never
   covers navigation. */
.topbanner {
  display: flex; flex-wrap: wrap; align-items: center; gap: 8px;
  padding: 12px var(--gutter);
  background: var(--accent); color: var(--text-inverse);
  font-size: 14px;
}
.topbanner a { text-decoration: underline; font-weight: 600; margin-left: auto; }

/* Pickup-point picker on the checkout. A scrolling list rather than a map: the
   page must work with JavaScript off, and a big city returns a couple of hundred
   points that would otherwise push the payment block off the screen. */
.pvz-list {
  max-height: 320px;
  overflow-y: auto;
  border: 1px solid var(--border);
  border-radius: 10px;
}
.pvz-item {
  display: flex;
  gap: 10px;
  align-items: flex-start;
  padding: 10px 12px;
  cursor: pointer;
  border-bottom: 1px solid var(--border);
}
.pvz-item:last-child { border-bottom: none; }
.pvz-item:hover { background: var(--surface-2, rgba(0, 0, 0, .03)); }
/* Список может быть длиной в тысячи строк со своей прокруткой — после выбора
   точки (особенно с карты) строка автоматически проскраливается в видимую
   область (см. pvz-map.js), а подсветка должна быть заметна с первого взгляда,
   а не угадываться по чуть более тёмному фону. */
.pvz-item.is-selected {
  background: var(--surface-2, rgba(0, 0, 0, .05));
  border-left: 3px solid var(--accent);
  padding-left: 9px;
}
.pvz-item input { margin-top: 3px; flex: 0 0 auto; }
/* Автор всегда побеждает встроенный стиль браузера для [hidden] — `display: flex`
   выше побеждает `display: none`, даже без разницы в специфичности. Без этой
   строки JS-фильтр (pvz-map.js) снимает и ставит атрибут `hidden`, ничего не
   меняя на экране: строка остаётся видимой ровно как была. */
.pvz-item[hidden] { display: none; }


/* ---------- сетки каталога и карточки товара ----------
   Раньше обе жили инлайном с колонками в пикселях: 280px под фильтры и 620px под
   фото. На телефоне это выдавливало товары за край экрана — фиксированная колонка
   не умеет ужиматься. Теперь колонки гибкие, а на узком экране просто становятся
   друг под друга. */
.catalog-layout {
  display: grid; grid-template-columns: 280px minmax(0, 1fr);
  gap: 32px; align-items: start;
}
.product-layout {
  /* Колонка фото уже прежних 620px: кадр стал вертикальным, и при той ширине он
     вырастал до 827 px — кнопка «в корзину» уезжала за нижний край экрана. */
  display: grid; grid-template-columns: minmax(0, 480px) minmax(0, 1fr);
  gap: 48px; align-items: start;
}
@media (max-width: 900px) {
  .catalog-layout, .product-layout { grid-template-columns: 1fr; gap: 24px; }
}

/* ---------- лента обложек на главной ----------
   Список выводится дважды, анимация сдвигает дорожку ровно на половину её ширины —
   шов приходится на точную копию, поэтому прокрутка выглядит бесконечной. Это
   CSS-анимация, а не скрипт: витрина обязана жить и с выключенным JS. */
.roll {
  position: relative;
  overflow: hidden;
  border-radius: var(--r-xl);
  background: var(--bg-muted);
  padding: 16px 0;
  /* Края растворяются, иначе обложки обрубаются по живому. */
  -webkit-mask-image: linear-gradient(to right, transparent, #000 6%, #000 94%, transparent);
  mask-image: linear-gradient(to right, transparent, #000 6%, #000 94%, transparent);
}
.roll__track {
  display: flex;
  gap: 16px;
  width: max-content;
  padding-inline: 16px;
  /* Скорость от числа обложек, а не фиксированная: шесть карточек и двадцать
     должны ехать с одинаковой скоростью, а не за одинаковое время. */
  animation: roll-scroll calc(var(--roll-count, 6) * 3.5s) linear infinite;
}
/* Навёл курсор или дошёл табом — лента останавливается: иначе по ссылке не
   попасть. */
.roll:hover .roll__track,
.roll:focus-within .roll__track { animation-play-state: paused; }

/* Крупнее, чем раньше (было 180×240) — тот же формат 3:4, что у карточек
   товаров ниже, но заметно больше, чтобы витрина первого экрана не терялась
   рядом с заголовком и весомее перекликалась по масштабу с фото каталога. */
.roll__item { flex: 0 0 auto; width: 260px; }
.roll__item img {
  display: block; width: 260px; height: 347px; object-fit: cover;
  border-radius: var(--r-lg); background: var(--bg-surface);
}
/* Второй режим витрины: по одной обложке во всю ширину, плавно сменяя друг друга.
   Кадры лежат стопкой в одной ячейке грида; сами доли цикла считает шаблон, здесь
   только геометрия. */
.fader {
  display: grid;
  /* Портрет, как и везде. Ограничиваем по ширине, а не по высоте: потолок высоты
     оставлял ширину прежней, и кадр снова становился горизонтальным. */
  aspect-ratio: 3 / 4;
  max-width: 345px;
  margin-inline: auto;
  border-radius: var(--r-xl);
  overflow: hidden;
  background: var(--bg-muted);
}
.fader__shot {
  grid-area: 1 / 1;
  display: block; width: 100%; height: 100%;
  animation-name: showcase-fade;
  animation-timing-function: ease-in-out;
  animation-iteration-count: infinite;
}
.fader__shot img {
  display: block; width: 100%; height: 100%; object-fit: cover;
  min-width: 0; min-height: 0;
}
/* Навели курсор — смена останавливается: иначе по ссылке не попасть. */
.fader:hover .fader__shot,
.fader:focus-within .fader__shot { animation-play-state: paused; }

@media (prefers-reduced-motion: reduce) {
  /* Анимация мешает — показываем только первый кадр, без мельтешения. */
  .fader__shot { animation: none; opacity: 0; }
  .fader__shot:first-of-type { opacity: 1; }
}

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

/* Кому анимация мешает — тому просто лента с прокруткой пальцем. */
@media (prefers-reduced-motion: reduce) {
  .roll__track { animation: none; }
  .roll { overflow-x: auto; }
}

/* ---------- галерея товара ----------
   Переключение без единой строки JS: радиокнопки помнят выбор, соседние
   селекторы показывают нужный кадр. С выключенным скриптом работает так же. */
.gallery { display: flex; flex-direction: column; gap: 12px; }
.gallery__pick { position: absolute; opacity: 0; width: 0; height: 0; }
.gallery__stage {
  /* Тот же 3:4, что и в каталоге: иначе одно фото обрезается в двух местах
     по-разному, и товар на странице выглядит не тем, на который нажимали. */
  aspect-ratio: 3 / 4; background: var(--bg-muted);
  border-radius: var(--r-xl); overflow: hidden;
  display: grid; place-items: center; color: var(--text-secondary); font-size: 12px;
}
.gallery__slide {
  display: none;
  width: 100%; height: 100%; object-fit: cover; min-width: 0; min-height: 0;
  grid-area: 1 / 1;
}
/* Grid на 3 колонки, а не flex с фиксированной шириной миниатюры: сумма
   105px-блоков не обязана совпадать с шириной фото над ними при любой
   ширине экрана, и правый край рядов плавал то ближе, то дальше от края
   кадра. Grid всегда растягивает 3 колонки ровно на ширину контейнера, то
   есть на ширину .gallery__stage — совпадение гарантировано, а не подогнано
   под один экран. */
.gallery__thumbs { display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px; }
.gallery__thumb {
  /* Тот же вертикальный формат, что и у большого кадра: миниатюра — уменьшенная
     копия того, что откроется, а не другой кроп того же снимка. */
  aspect-ratio: 3 / 4; border-radius: var(--r-md); overflow: hidden;
  background: var(--bg-muted); cursor: pointer;
  /* Рамка без перехода намеренно: плавное перекрашивание на 150 мс успевает
     показать выбранной ещё предыдущую миниатюру, а выбор должен читаться сразу. */
  border: 2px solid transparent;
}
.gallery__thumb img {
  display: block; width: 100%; height: 100%; object-fit: cover;
  min-width: 0; min-height: 0;
}
.gallery__thumb:hover { border-color: var(--border); }

/* По одной паре правил на кадр: какой радиокнопке поставили галочку, тот кадр и
   показан, та миниатюра и подсвечена. Картинок у товара не больше десяти —
   см. MAX_PER_PRODUCT в services/images.py. */
.gallery__pick:nth-of-type(1):checked ~ .gallery__stage .gallery__slide:nth-of-type(1),
.gallery__pick:nth-of-type(2):checked ~ .gallery__stage .gallery__slide:nth-of-type(2),
.gallery__pick:nth-of-type(3):checked ~ .gallery__stage .gallery__slide:nth-of-type(3),
.gallery__pick:nth-of-type(4):checked ~ .gallery__stage .gallery__slide:nth-of-type(4),
.gallery__pick:nth-of-type(5):checked ~ .gallery__stage .gallery__slide:nth-of-type(5),
.gallery__pick:nth-of-type(6):checked ~ .gallery__stage .gallery__slide:nth-of-type(6),
.gallery__pick:nth-of-type(7):checked ~ .gallery__stage .gallery__slide:nth-of-type(7),
.gallery__pick:nth-of-type(8):checked ~ .gallery__stage .gallery__slide:nth-of-type(8),
.gallery__pick:nth-of-type(9):checked ~ .gallery__stage .gallery__slide:nth-of-type(9),
.gallery__pick:nth-of-type(10):checked ~ .gallery__stage .gallery__slide:nth-of-type(10) {
  display: block;
}
.gallery__pick:nth-of-type(1):checked ~ .gallery__thumbs .gallery__thumb:nth-of-type(1),
.gallery__pick:nth-of-type(2):checked ~ .gallery__thumbs .gallery__thumb:nth-of-type(2),
.gallery__pick:nth-of-type(3):checked ~ .gallery__thumbs .gallery__thumb:nth-of-type(3),
.gallery__pick:nth-of-type(4):checked ~ .gallery__thumbs .gallery__thumb:nth-of-type(4),
.gallery__pick:nth-of-type(5):checked ~ .gallery__thumbs .gallery__thumb:nth-of-type(5),
.gallery__pick:nth-of-type(6):checked ~ .gallery__thumbs .gallery__thumb:nth-of-type(6),
.gallery__pick:nth-of-type(7):checked ~ .gallery__thumbs .gallery__thumb:nth-of-type(7),
.gallery__pick:nth-of-type(8):checked ~ .gallery__thumbs .gallery__thumb:nth-of-type(8),
.gallery__pick:nth-of-type(9):checked ~ .gallery__thumbs .gallery__thumb:nth-of-type(9),
.gallery__pick:nth-of-type(10):checked ~ .gallery__thumbs .gallery__thumb:nth-of-type(10) {
  border-color: var(--accent);
}
/* Дошли табом до переключателя — обводка появляется на его собственной
   миниатюре. Раньше правило не называло номер и подсвечивало сразу все. */
.gallery__pick:nth-of-type(1):focus-visible ~ .gallery__thumbs .gallery__thumb:nth-of-type(1),
.gallery__pick:nth-of-type(2):focus-visible ~ .gallery__thumbs .gallery__thumb:nth-of-type(2),
.gallery__pick:nth-of-type(3):focus-visible ~ .gallery__thumbs .gallery__thumb:nth-of-type(3),
.gallery__pick:nth-of-type(4):focus-visible ~ .gallery__thumbs .gallery__thumb:nth-of-type(4),
.gallery__pick:nth-of-type(5):focus-visible ~ .gallery__thumbs .gallery__thumb:nth-of-type(5),
.gallery__pick:nth-of-type(6):focus-visible ~ .gallery__thumbs .gallery__thumb:nth-of-type(6),
.gallery__pick:nth-of-type(7):focus-visible ~ .gallery__thumbs .gallery__thumb:nth-of-type(7),
.gallery__pick:nth-of-type(8):focus-visible ~ .gallery__thumbs .gallery__thumb:nth-of-type(8),
.gallery__pick:nth-of-type(9):focus-visible ~ .gallery__thumbs .gallery__thumb:nth-of-type(9),
.gallery__pick:nth-of-type(10):focus-visible ~ .gallery__thumbs .gallery__thumb:nth-of-type(10) {
  outline: 2px solid var(--accent); outline-offset: 2px;
}

@media (max-width: 900px) {
  .roll__item, .roll__item img { width: 140px; }
  .roll__item img { height: 187px; }
}

/* --- номер заказа и постоянная ссылка ------------------------------------- */
/* Номер набирают руками в переписке и диктуют по телефону, поэтому он подан
   как отдельный объект, а не как часть фразы: крупно, моноширинно и с
   воздухом вокруг. Моноширинный шрифт здесь не украшение — он не даёт
   спутать «1» с «l» при чтении вслух. */
.order-key {
  background: var(--bg-muted);
  border: 1px solid var(--border);
  border-radius: 14px;
  padding: 18px 20px;
  margin-top: 20px;
}
.order-key__label {
  font-size: 12px;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--text-secondary);
}
.order-key__row {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
  margin-top: 4px;
}
.order-key__value {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 26px;
  line-height: 1.2;
  /* Номер выделяется двойным щелчком целиком, вместе с решёткой. */
  user-select: all;
}
/* Ссылка длинная и на узком экране обязана переноситься внутри себя, иначе
   растягивает страницу и появляется горизонтальная прокрутка. */
.order-link {
  font-size: 13px;
  color: var(--text-secondary);
  word-break: break-all;
  min-width: 0;
}

/* Кнопка «Проверить заказ» в шапке: то же оформление, что у корзины, только
   подпись на узком экране короче. Переключение подписи — через два span, а не
   через JS: разметка приходит с сервера уже полной, и на медленном соединении
   надпись не прыгает после загрузки. */
.order-check__short { display: none; }
@media (max-width: 560px) {
  .order-check__full { display: none; }
  .order-check__short { display: inline; }
}

/* --- карта ПВЗ на оформлении заказа ---------------------------------------
   Ни у Ozon, ни у Яндекс Доставки нет готового виджета для встраивания на
   сторонний сайт — оба лишь просят метки фирменного цвета. Карта своя, на
   Яндекс.Картах, поверх уже существующего списка: список остаётся первым и
   работает без JS и без ключа API, карта — по кнопке. */
.pvz-toggle { margin: 8px 0; }
.pvz-map {
  height: 320px;
  border: 1px solid var(--border);
  border-radius: 10px;
  margin-bottom: 10px;
  overflow: hidden;
}
/* Кнопка «Выбрать этот пункт» внутри балуна Яндекс.Карт — балун рисует свой
   HTML напрямую, поэтому стиль задаётся классом, а не через .btn. */
.pvz-pick {
  margin-top: 6px;
  font: inherit; font-size: 13px; font-weight: 600;
  color: var(--accent); background: none; border: none; padding: 0; cursor: pointer;
}
.pvz-pick:hover { text-decoration: underline; }
