@charset "utf-8";
/* Оформление элементов — файл СОБРАН, править его бесполезно.

   Правки вносятся в web/styles/<папка>/<файл>.css, затем:
       python tools/build_styles.py

   Значения (палитра, кегли, скругления) лежат в static/токены.css и
   подключаются перед этим файлом. Ни одного размера числом: всё через
   токены.

   Элементы приехали из панели Entergalactic VPN. Свои правила пишем
   отдельными файлами, чужие не трогаем: их затрёт следующий перенос. */

  /* ── 01-лицо.css ─────────────────────────────────────── */
/* Свод признаков, по которым панель, кабинет, бот и лендинг читаются как
   один сервис. Правил здесь немного — это скорее договор, который держат
   остальные файлы. Нарушение любого пункта делает экран «чужим», даже когда
   отдельно он выглядит прилично.

   1. АКЦЕНТ ОДИН. Фирменный красный и ничего больше. Зелёный, жёлтый и
      красный-тревожный — только состояния (работает, требует внимания, не
      работает), их нельзя брать для украшения. Второго «фирменного» цвета
      у нас нет намеренно: он сразу удваивает число решений.

   2. ЗНАК — КВАДРАТ СО СКРУГЛЕНИЕМ, НЕ КРУГ. Логотип нарисован квадратом,
      поэтому аватарки, значки разделов и плитки тоже квадратные. Круг
      появляется ровно в двух местах: точка состояния и полоса-таблетка.

   3. МЕЛКАЯ ПОДПИСЬ — КАПСЛОКОМ С РАЗРЯДКОЙ. Заголовки групп, подписи
      плиток, шапки таблиц. Это прямая отсылка к знаку, где надпись набрана
      заглавными. Основной текст капслоком не набираем никогда.

   4. ГРАНИЦЫ ВОЛОСЯНЫЕ, ТЕНИ МЯГКИЕ. Карточку выделяет тень и фон, а не
      жирная рамка. Рамка в один пиксель — у полей и кнопок, где границу
      надо видеть.

   5. ЦИФРЫ КРУПНО И МОНОШИРИННО. Деньги, трафик, счётчики — табличными
      цифрами, чтобы столбцы не дрожали при обновлении.

   6. ВСЁ УПРАВЛЕНИЕ ОДНОГО РОСТА. Кнопка, поле, выпадающий список — 32
      точки. Мелкий ряд — 28. Третьей высоты не заводим.

   Классы ниже — общие мелочи, на которые опираются остальные темы. */

  /* Подпись группы: капслок с разрядкой — см. пункт 3 */
  .cap{font-size:var(--fs-2xs);text-transform:uppercase;letter-spacing:.07em;
       font-weight:650;color:var(--faint);}

  /* Разделитель между блоками. Волосяной — см. пункт 4 */
  .hr{height:1px;background:var(--border);border:none;margin:var(--sp-3) 0;}
  .hr-text{display:flex;align-items:center;gap:var(--sp-3);color:var(--faint);
           font-size:var(--fs-xs);margin:var(--sp-3) 0;}
  .hr-text::before,.hr-text::after{content:"";flex:1;height:1px;background:var(--border);}

  /* Точка состояния — один из двух разрешённых кругов */
  .dot-state{width:7px;height:7px;border-radius:50%;flex:none;background:var(--muted);}
  .dot-state.ok{background:var(--good);}
  .dot-state.warn{background:var(--warn);}
  .dot-state.bad{background:var(--bad);}

  /* Крупное число: деньги, трафик, счётчики — см. пункт 5 */
  .big{font-size:var(--fs-2xl);font-weight:700;letter-spacing:-.02em;
       font-variant-numeric:tabular-nums;line-height:1.15;}

  /* ── аватарка.css ─────────────────────────────────────── */
/* ── Аватарка. Квадрат со скруглением, как знак: круглых у нас только
     точка состояния и таблетка. Буква — когда картинки нет. ── */
  .ava{display:inline-grid;place-items:center;flex:none;overflow:hidden;
       width:var(--box);height:var(--box);border-radius:var(--box-r);
       background:var(--accent-soft);color:var(--accent);
       font-weight:700;font-size:var(--fs-sm);line-height:1;}
  .ava img{width:100%;height:100%;object-fit:cover;display:block;}
  .ava.sm{width:var(--box-sm);height:var(--box-sm);border-radius:var(--box-sm-r);
       font-size:var(--fs-xs);}
  .ava.lg{width:var(--box-lg);height:var(--box-lg);border-radius:var(--box-lg-r);
       font-size:var(--fs-lg);}
  .ava.xl{width:var(--box-xl);height:var(--box-xl);border-radius:var(--box-xl-r);
       font-size:var(--fs-xl);}
  /* Ряд аватарок внахлёст: участники переписки, устройства клиента */
  .ava-row{display:inline-flex;}
  .ava-row .ava{border:2px solid var(--surface);}
  .ava-row .ava + .ava{margin-left:calc(-1 * var(--sp-2));}
  .ava-row .more{background:var(--surface-3);color:var(--muted);}

  

  /* ── вкладки.css ─────────────────────────────────────── */
/* Вкладка переключает НАБОР одних и тех же данных: все оплаты, только
   рублёвые, только звёздные. Если за переключателем прячется другое
   содержимое — это уже раздел, и ему место в меню, а не во вкладках.

   Активная помечается заливкой, а не подчёркиванием: подчёркивание тонкое,
   на телефоне его не видно, и человек не понимает, где находится. */

  .tabs{display:flex;align-items:center;gap:var(--sp-2);padding:var(--sp-2);
        background:var(--surface-2);border-radius:var(--radius-sm);
        overflow-x:auto;scrollbar-width:none;}
  .tabs::-webkit-scrollbar{display:none;}
  .tabs > button{height:var(--ctl-sm);padding:0 var(--sp-3);border:none;background:none;
        border-radius:var(--radius-xs);color:var(--text-2);font:inherit;
        font-size:var(--fs-sm);font-weight:500;cursor:pointer;white-space:nowrap;
        display:inline-flex;align-items:center;gap:var(--sp-2);transition:background .13s,color .13s;}
  .tabs > button:hover{color:var(--text);}
  .tabs > button.on{background:var(--surface);color:var(--text);font-weight:600;
        box-shadow:var(--shadow-sm);}
  /* Счётчик рядом с названием: «Все 24», «Рублями 6» */
  .tabs > button .n{color:var(--muted);font-variant-numeric:tabular-nums;font-weight:500;}
  .tabs > button.on .n{color:var(--text-2);}

  /* Строка над таблицей целиком: вкладки слева, действия справа */
  .tabs-row{display:flex;align-items:center;gap:var(--sp-3);padding:var(--sp-3);
        border-bottom:1px solid var(--border);flex-wrap:wrap;}
  .tabs-row .tabs{flex:1;min-width:0;}
  .tabs-row .act{flex:none;display:flex;align-items:center;gap:var(--sp-2);}
  @media (max-width:720px){
    /* Действия под вкладками: в строку они не помещаются, а прокрутка вбок
       прячет кнопку «Настроить столбцы» за краем экрана. */
    .tabs-row{flex-direction:column;align-items:stretch;}
    .tabs-row .act > *{flex:1;}
  }

  /* ── вложение.css ─────────────────────────────────────── */
/* ── Вложение: файл в переписке или в обращении. ── */
  .attach{display:inline-flex;align-items:center;gap:var(--sp-2);max-width:100%;
       padding:var(--sp-2) var(--sp-3);border:1px solid var(--border);
       border-radius:var(--radius-sm);background:var(--surface);text-decoration:none;
       color:var(--text);font-size:var(--fs-sm);}
  .attach:hover{background:var(--surface-2);}
  .attach .ic{width:var(--box-sm);height:var(--box-sm);border-radius:var(--box-sm-r);flex:none;
       background:var(--accent-soft);color:var(--accent);display:grid;place-items:center;}
  .attach .ic svg{width:var(--ic-sm);height:var(--ic-sm);stroke:currentColor;fill:none;stroke-width:1.8;}
  .attach .txt{min-width:0;}
  .attach b{display:block;font-weight:550;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
  .attach small{color:var(--muted);font-size:var(--fs-xs);}

  /* ── всплывающие.css ─────────────────────────────────────── */
/* Четыре разных всплывающих, и путать их нельзя:

     .menu   меню действий — список того, что можно сделать: «Открыть»,
             «Перевыпустить», «Удалить». Открывается по кнопке.
     .pop    карточка у элемента — показывает данные, а не действия:
             сводка по клиенту, история платежа.
     .tip    подсказка — короткая строка, поясняющая значок. Появляется
             по наведению и не требует нажатия.
     .menubar строка меню — верхний ряд разделов с выпадающими списками.

   Общее у них одно: они рисуются поверх и обязаны закрываться нажатием
   мимо. Тень крупнее обычной — иначе всплывающее сливается с карточкой
   под ним, и непонятно, что уже открыто. */

  .menu,.pop{position:absolute;z-index:60;background:var(--surface);
       border:1px solid var(--border);border-radius:var(--radius-sm);
       box-shadow:var(--shadow-lg);padding:var(--sp-1);min-width:180px;width:max-content;max-width:min(320px,90vw);
       opacity:0;visibility:hidden;transform:translateY(-4px);
       transition:opacity .12s,transform .12s,visibility .12s;}
  .menu.open,.pop.open{opacity:1;visibility:visible;transform:none;}
  .pop{padding:var(--sp-3);min-width:240px;}

  /* Пункт меню действий: рост как у мелкой кнопки */
  .menu button,.menu a{display:flex;align-items:center;gap:var(--sp-2);width:100%;
       height:var(--ctl-sm);padding:0 var(--sp-2);border:none;background:none;
       border-radius:var(--radius-xs);color:var(--text);font:inherit;font-size:var(--fs-sm);
       text-align:left;text-decoration:none;cursor:pointer;white-space:nowrap;}
  .menu button:hover,.menu a:hover{background:var(--surface-2);}
  .menu svg{width:var(--ic-sm);height:var(--ic-sm);flex:none;stroke:currentColor;fill:none;
       stroke-width:1.8;opacity:.8;}
  .menu .sep{height:1px;background:var(--border);margin:var(--sp-1) 0;}
  .menu .danger{color:var(--bad);}
  .menu .danger:hover{background:var(--bad-bg);}
  .menu .kbd{margin-left:auto;color:var(--faint);font-size:var(--fs-2xs);
       font-family:var(--mono);}

  /* Подсказка: одна строка, появляется по наведению. Длинный текст сюда не
     кладём — для него есть кружок «?» и пояснение под полем. */
  .tip-host{position:relative;display:inline-flex;}
  .tip{position:absolute;z-index:70;bottom:calc(100% + var(--sp-1));left:50%;
       transform:translateX(-50%) translateY(2px);background:var(--text);color:var(--surface);
       font-size:var(--fs-xs);line-height:1.35;padding:var(--sp-1) var(--sp-2);
       border-radius:var(--radius-xs);white-space:nowrap;pointer-events:none;
       opacity:0;transition:opacity .12s,transform .12s;box-shadow:var(--shadow-md);}
  .tip-host:hover .tip,.tip-host:focus-within .tip{opacity:1;transform:translateX(-50%);}

  /* Строка меню: разделы сверху, у каждого свой список. Для витрины и для
     страниц, где боковое меню занимает слишком много места. */
  .menubar{display:flex;align-items:center;gap:2px;height:var(--ctl);
       border-bottom:1px solid var(--border);padding:0 var(--sp-2);background:var(--surface);}
  .menubar > .item{position:relative;}
  .menubar > .item > button{height:var(--ctl-sm);padding:0 var(--sp-3);border:none;
       background:none;border-radius:var(--radius-xs);color:var(--text-2);font:inherit;
       font-size:var(--fs-sm);font-weight:500;cursor:pointer;white-space:nowrap;}
  .menubar > .item > button:hover,.menubar > .item.open > button{
       background:var(--surface-2);color:var(--text);}
  .menubar .menu{top:calc(100% + var(--sp-1));left:0;}

  /* ── выделение.css ─────────────────────────────────────── */
/* ── Выделение в тексте: найденное слово в результатах поиска, изменённое
     значение в журнале. Не жёлтым маркером, а мягкой заливкой акцента —
     жёлтый в нашем наборе означает «требует внимания». ── */
  mark,.marker{background:var(--accent-soft);color:var(--accent);border-radius:var(--radius-xs);
       padding:0 2px;font-weight:600;}
  .marker.plain{background:var(--surface-3);color:var(--text);font-weight:500;}

  

  /* ── график.css ─────────────────────────────────────── */
/* ── Оформление графика.
     Сам график рисуется обычным SVG прямо в разметке — библиотека для двух
     линий не нужна. Здесь только обвязка: подписи, легенда, сетка. ── */
  .chart{position:relative;}
  .chart svg{display:block;width:100%;}
  .chart .grid line{stroke:var(--border);stroke-width:1;}
  .chart .line{fill:none;stroke:var(--accent);stroke-width:2;
       stroke-linecap:round;stroke-linejoin:round;}
  .chart .line.second{stroke:var(--muted);stroke-width:1.6;stroke-dasharray:4 5;}
  .chart .fill{fill:var(--accent-soft);}
  .chart .dot{fill:var(--accent);}
  .chart-legend{display:flex;gap:var(--sp-4);margin-top:var(--sp-2);
       color:var(--muted);font-size:var(--fs-xs);flex-wrap:wrap;}
  .chart-legend span{display:inline-flex;align-items:center;gap:var(--sp-1);}
  .chart-legend i{width:14px;height:2px;border-radius:var(--pill);background:var(--accent);}
  .chart-legend i.second{background:var(--muted);}
  .chart-legend .end{margin-left:auto;}
  .chart-axis{display:flex;justify-content:space-between;color:var(--faint);
       font-size:var(--fs-2xs);margin-top:var(--sp-1);font-variant-numeric:tabular-nums;}

  

  /* ── группа-кнопок.css ─────────────────────────────────────── */
/* ── Группа кнопок: слитый ряд. Для переключения вида списка, шага
     страницы. Не путать с сегментами: там выбор, здесь действия. ── */
  .btn-group{display:inline-flex;}
  .btn-group > .btn{border-radius:0;margin-left:-1px;min-width:0;}
  .btn-group > .btn:first-child{border-top-left-radius:var(--radius-sm);
       border-bottom-left-radius:var(--radius-sm);margin-left:0;}
  .btn-group > .btn:last-child{border-top-right-radius:var(--radius-sm);
       border-bottom-right-radius:var(--radius-sm);}
  .btn-group > .btn:hover,.btn-group > .btn.on{z-index:1;}

  

  /* ── загрузка-файлов.css ─────────────────────────────────────── */
/* Область загрузки файлов.
   Часть набора элементов. Собирается сборщиком:
   python tools/build_styles.py */

/* Снимки товара, накладная, картинка в статью. Обычное поле выбора файла
   выглядит чужеродно на любой странице и по-разному в каждом браузере,
   поэтому его прячут и рисуют своё.

   Перетаскивание — не единственный способ: по области можно и просто
   нажать. Тем, кто про перетаскивание не знает, иначе некуда деться, а на
   телефоне его нет вовсе.

   Разметка: label с классом «кладь», внутри — скрытое поле выбора файла и
   подпись. Пример целиком лежит в каталоге, в разделе «Интерфейс».

   Подсветку при наведении файла даёт поведение набора (kit.js). Не подключили
   — область остаётся рабочей, просто без подсветки. */

  .кладь{display:flex;flex-direction:column;align-items:center;
        justify-content:center;gap:var(--sp-2);
        min-height:calc(var(--лицо) * 1.6);padding:var(--sp-5);
        border:1px dashed var(--control-line);border-radius:var(--radius-lg);
        background:var(--surface-2);color:var(--muted);text-align:center;
        cursor:pointer;transition:border-color .12s, background .12s;}
  .кладь:hover{border-color:var(--accent);color:var(--text-2);}
  .кладь:focus-within{outline:2px solid var(--accent);outline-offset:2px;}
  /* Файл занесли над областью: подсвечиваем — иначе непонятно, отпустят его
     здесь или мимо. */
  .кладь.несут{border-color:var(--accent);border-style:solid;
        background:var(--accent-soft);color:var(--text);}
  .кладь svg{width:var(--box-lg);height:var(--box-lg);stroke:currentColor;
        fill:none;stroke-width:1.4;stroke-linecap:round;stroke-linejoin:round;
        color:var(--faint);}
  .кладь b{font-size:var(--fs-md);color:var(--text);}
  .кладь span{font-size:var(--fs-sm);}
  /* Что можно класть и сколько весит — мелко и рядом: спрашивают об этом
     только после отказа, а отказ лучше предупредить. */
  .кладь .правила{font-size:var(--fs-xs);color:var(--faint);}

  /* Уже положенные файлы. Показываем списком под областью: сетка миниатюр
     красивее, но по ней не видно имён, а удаляют файлы по имени. */
  .кладь-список{display:flex;flex-direction:column;gap:var(--sp-2);
        margin-top:var(--sp-3);}
  .кладь-список .файл{display:flex;align-items:center;gap:var(--sp-3);
        padding:var(--sp-2) var(--sp-3);background:var(--surface);
        border:1px solid var(--border);border-radius:var(--radius-sm);}
  .кладь-список .файл .лик{width:var(--box);height:var(--box);flex:none;
        border-radius:var(--radius-xs);background:var(--surface-2);
        overflow:hidden;display:grid;place-items:center;color:var(--faint);
        font-size:var(--fs-2xs);}
  .кладь-список .файл .лик img{width:100%;height:100%;object-fit:cover;
        display:block;}
  .кладь-список .файл .имя{flex:1;min-width:0;font-size:var(--fs-sm);
        overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
  .кладь-список .файл .вес{color:var(--faint);font-size:var(--fs-xs);
        font-variant-numeric:tabular-nums;}
  .кладь-список .файл button{width:var(--ctl-sm);height:var(--ctl-sm);flex:none;
        border:none;background:transparent;color:var(--muted);cursor:pointer;
        border-radius:var(--radius-sm);display:grid;place-items:center;}
  .кладь-список .файл button:hover{background:var(--surface-2);color:var(--bad);}
  .кладь-список .файл button svg{width:var(--ic-sm);height:var(--ic-sm);
        stroke:currentColor;fill:none;stroke-width:2;}
  /* Полоса загрузки прямо в строке файла: отдельная полоса под списком не
     показывает, какой именно файл ещё едет. */
  .кладь-список .файл .ход{width:100%;height:3px;border-radius:var(--pill);
        background:var(--surface-3);overflow:hidden;margin-top:var(--sp-1);}
  .кладь-список .файл .ход i{display:block;height:100%;background:var(--accent);}

  /* ── загрузка.css ─────────────────────────────────────── */
/* ── Кольцо загрузки ──
     Ставится там, где ждать меньше секунды: кнопка отправки, обновление
     списка. Для долгого ожидания есть заглушки — они показывают форму
     будущих данных, и ожидание не выглядит поломкой. */
  .spin{width:16px;height:16px;flex:none;border-radius:50%;
       border:2px solid color-mix(in oklab, currentColor 25%, transparent);
       border-top-color:currentColor;animation:spin .7s linear infinite;}
  .spin.lg{width:24px;height:24px;border-width:2.5px;}
  @keyframes spin{to{transform:rotate(360deg);}}
  @media (prefers-reduced-motion:reduce){.spin{animation-duration:2s;}}
  /* Кнопка в работе: кольцо вместо значка, ширина не скачет */
  .btn.busy{pointer-events:none;opacity:.75;}

  /* Полоса загрузки страницы — тонкая, наверху, без цифр: сколько осталось,
     мы всё равно не знаем, а честная неопределённость лучше выдуманных
     процентов. */
  .loading-bar{position:fixed;top:0;left:0;right:0;height:2px;z-index:90;
       background:transparent;overflow:hidden;}
  .loading-bar i{display:block;height:100%;width:40%;background:var(--accent);
       animation:loading 1.1s ease-in-out infinite;}
  @keyframes loading{0%{transform:translateX(-100%);}100%{transform:translateX(320%);}}

  /* ── затемнение.css ─────────────────────────────────────── */
/* Три способа показать что-то поверх страницы, и выбор между ними не вкусовой:

     окно     — короткое действие: подтвердить, ввести одно поле;
     боковая  — работа рядом с содержимым: карточка клиента, фильтры.
                Страница остаётся видна, и понятно, к чему это относится;
     нижняя   — то же, но на телефоне: снизу до неё дотягивается большой палец,
                а окно по центру на узком экране перекрывает всё.

   Общее: закрываются нажатием мимо и клавишей Esc, и у каждой есть заголовок.
   Шторка без заголовка выглядит выехавшим куском страницы. */

  .veil{position:fixed;inset:0;z-index:70;background:rgba(0,0,0,.45);
       opacity:0;visibility:hidden;transition:opacity .15s,visibility .15s;}
  .veil.open{opacity:1;visibility:visible;}

  

  /* ── значок.css ─────────────────────────────────────── */
/* ── Значок-метка. Отличается от .pill тем, что не про состояние, а про
     свойство: «Год», «Рубли», «Промо». ── */
  .badge{display:inline-flex;align-items:center;gap:var(--sp-1);height:20px;
       padding:0 var(--sp-2);border-radius:var(--radius-xs);background:var(--surface-3);
       color:var(--text-2);font-size:var(--fs-2xs);font-weight:600;white-space:nowrap;}
  .badge.accent{background:var(--accent-soft);color:var(--accent);}
  .badge.line{background:transparent;border:1px solid var(--border);}

  

  /* ── инструменты-таблицы.css ─────────────────────────────────────── */
/* ── Инструменты над таблицей: поиск, фильтры, выбор столбцов, счётчик
     выбранного. Раньше каждая таблица собирала их по-своему, и в одной
     поиск был слева, в другой справа. ── */
  .tbl-tools{display:flex;align-items:center;gap:var(--sp-2);flex-wrap:wrap;
       padding:var(--sp-3);border-bottom:1px solid var(--border);}
  .tbl-tools .grow{flex:1;min-width:180px;}
  .tbl-tools .picked{display:flex;align-items:center;gap:var(--sp-2);
       padding:0 var(--sp-3);height:var(--ctl);border-radius:var(--radius-sm);
       background:var(--accent-soft);color:var(--accent);font-size:var(--fs-sm);
       font-weight:600;}
  .tbl-tools .picked .x{border:none;background:none;color:inherit;cursor:pointer;
       font-size:var(--fs-base);line-height:1;padding:0;}
  /* Выбор столбцов: список с флажками в меню */
  .cols{padding:var(--sp-1);}
  .cols label{display:flex;align-items:center;gap:var(--sp-2);height:var(--ctl-sm);
       padding:0 var(--sp-2);border-radius:var(--radius-xs);font-size:var(--fs-sm);
       cursor:pointer;}
  .cols label:hover{background:var(--surface-2);}

  

  /* ── календарь.css ─────────────────────────────────────── */
/* ── Календарь: выбор даты окончания подписки, период отчёта.
     Клетка квадратная и ростом с мелкую кнопку: в календарь тычут пальцем
     чаще, чем куда-либо ещё. ── */
  .cal{display:inline-block;background:var(--surface);border:1px solid var(--border);
       border-radius:var(--radius-sm);padding:var(--sp-3);}
  .cal-head{display:flex;align-items:center;gap:var(--sp-2);margin-bottom:var(--sp-2);}
  .cal-head b{flex:1;text-align:center;font-size:var(--fs-sm);font-weight:600;}
  .cal-head button{width:var(--ctl-sm);height:var(--ctl-sm);border:1px solid var(--control-line);
       background:var(--surface);border-radius:var(--radius-xs);color:var(--text-2);
       cursor:pointer;display:grid;place-items:center;flex:none;}
  .cal-head button:hover{background:var(--surface-2);color:var(--text);}
  .cal-grid{display:grid;grid-template-columns:repeat(7,var(--ctl-sm));gap:2px;}
  .cal-grid .dow{height:var(--ctl-sm);display:grid;place-items:center;color:var(--muted);
       font-size:var(--fs-2xs);font-weight:650;text-transform:uppercase;letter-spacing:.06em;}
  .cal-day{height:var(--ctl-sm);border:none;background:none;border-radius:var(--radius-xs);
       color:var(--text);font:inherit;font-size:var(--fs-sm);cursor:pointer;
       font-variant-numeric:tabular-nums;display:grid;place-items:center;}
  .cal-day:hover{background:var(--surface-2);}
  .cal-day.other{color:var(--faint);}
  .cal-day.today{box-shadow:inset 0 0 0 1px var(--accent-line);font-weight:650;}
  .cal-day.on{background:var(--accent);color:var(--on-accent);font-weight:650;}
  /* Промежуток между двумя датами: концы залиты, середина мягкая */
  .cal-day.in{background:var(--accent-soft);color:var(--accent);border-radius:0;}
  .cal-day.in.start{border-radius:var(--radius-xs) 0 0 var(--radius-xs);}
  .cal-day.in.end{border-radius:0 var(--radius-xs) var(--radius-xs) 0;}
  .cal-day:disabled{opacity:.35;cursor:default;}
  @media (max-width:400px){
    /* Семь клеток по 30 точек с отступами не помещаются на узкий экран:
       ужимаем клетку, а не ломаем неделю на две строки. */
    .cal{padding:var(--sp-2);}
    .cal-grid{grid-template-columns:repeat(7,minmax(0,1fr));}
    .cal-day,.cal-grid .dow{height:var(--ctl-sm);font-size:var(--fs-xs);}
  }

  

  /* ── карточка-по-наведению.css ─────────────────────────────────────── */
/* ── Карточка по наведению.
     Отличается от подсказки тем, что в ней данные, а не одна строка, и она
     не исчезает, пока курсор внутри: из неё можно копировать. ── */
  .hover-card{position:relative;display:inline-flex;}
  .hover-card > .card-pop{position:absolute;z-index:65;top:calc(100% + var(--sp-1));left:0;
       min-width:260px;background:var(--surface);border:1px solid var(--border);
       border-radius:var(--radius-sm);box-shadow:var(--shadow-lg);padding:var(--sp-3);
       opacity:0;visibility:hidden;transform:translateY(-4px);
       transition:opacity .14s,transform .14s,visibility .14s;}
  .hover-card:hover > .card-pop,.hover-card:focus-within > .card-pop{
       opacity:1;visibility:visible;transform:none;}
  .hover-card .row{display:flex;gap:var(--sp-2);align-items:center;}
  .hover-card .row + .row{margin-top:var(--sp-2);}
  .hover-card small{color:var(--muted);font-size:var(--fs-xs);}

  

  /* ── карточки.css ─────────────────────────────────────── */
/* ═══ КАРТОЧКИ / СТРОКИ ════════════════════════════════════════════ */
  .card{background:var(--surface);border:1px solid var(--border);border-radius:var(--radius-lg);margin-bottom:var(--sp-3);overflow:hidden;}
  /* Единая строка-блок: одна высота и геометрия у шапки карточки, строки настроек
     и строки ленты — чтобы одиночные карточки выглядели одинаково везде. */
  .card-head{padding:var(--sp-3) var(--sp-4) var(--sp-1);}
  .card-head.ic{padding:var(--sp-3) var(--sp-4);min-height:58px;}
  .card-head.ic+.card-body{padding-top:var(--sp-1);} .card-head h2{margin:0;font-size:var(--fs-md);font-weight:640;} .card-head p{margin:3px 0 0;color:var(--muted);font-size:var(--fs-sm);}
  .card-body{padding:var(--sp-2) var(--sp-4) var(--sp-4);display:flex;flex-direction:column;gap:0;}
  /* Строка настройки — единый кирпич: одна высота, метка слева, управление справа.
     Кнопки в строках одного размера, чтобы правый край везде совпадал. */
  .row{display:flex;align-items:center;gap:var(--sp-3);padding:var(--sp-2) 0;border-top:1px solid var(--border-soft);min-height:58px;}
  /* подвал карточки с кнопкой — та же высота и то же место, что у обычной строки */
  .row-act{justify-content:flex-end;gap:var(--sp-2);}
  .modal .row-act{border-top:1px solid var(--border-soft);margin-top:var(--sp-1);}
  .row-act>.btn{margin-left:0;}
  .note+.row-act{border-top:none;margin-top:0;}
  .row>*:not(.r-label):not(:first-child){margin-left:auto;}
  .row .btn,.card-head.ic .btn{height:32px;min-width:116px;justify-content:center;}
  .row .ctl{display:flex;align-items:center;gap:var(--sp-2);margin-left:auto;flex-wrap:wrap;justify-content:flex-end;}
  .row input.field,.row select.field{height:32px;}
  input[type=file].field{height:32px;padding:0;font-size:var(--fs-sm);width:258px;max-width:258px;display:flex;align-items:center;overflow:hidden;color:var(--muted);}
  input[type=file].field::file-selector-button{height:30px;min-width:116px;margin:0 var(--sp-2) 0 0;padding:0 var(--sp-3);border:none;border-right:1px solid var(--border);background:var(--surface-2);color:var(--text);font:inherit;font-size:var(--fs-sm);font-weight:550;cursor:pointer;transition:background .13s;}
  input[type=file].field::file-selector-button:hover{background:var(--border-soft);}
  .row:first-child{border-top:none;}
  .row .r-label{flex:1;min-width:0;font-size:var(--fs-base);font-weight:550;}
  .row .r-label small{display:block;color:var(--muted);font-size:var(--fs-xs);font-weight:400;margin-top:2px;}
  /* подсказка-«?»: описание не занимает строку, а показывается по наведению/тапу */
  /* Кружок мелкий нарочно — он не должен спорить с подписью. Цель касания
     добираем невидимым полем вокруг: псевдоэлемент в раскладке не участвует,
     соседи не сдвигаются, вид не меняется ни на точку. */
  .q::after{content:"";position:absolute;inset:-4px;border-radius:50%;}
  .q{position:relative;width:16px;height:16px;flex:none;display:inline-grid;place-items:center;margin-left:var(--sp-1);padding:0;
    border:none;border-radius:50%;background:var(--surface-3);color:var(--muted);
    font:inherit;font-size:var(--fs-2xs);font-weight:700;line-height:1;cursor:help;vertical-align:1px;}
  .q:hover,.q.on{background:var(--accent-soft);color:var(--accent);}
  .q .tip{display:none;}                    /* текст живёт в разметке — чтобы переводился */
  .row .r-label.has-q{display:flex;align-items:center;gap:0;flex-wrap:wrap;}
  /* в форме пояснение стояло отдельной строкой и держало отступ до поля —
     ушло под «?», значит отступ теперь на самой подписи */
  .fld > label.has-q{margin-bottom:var(--sp-1);}
  #tipbox{position:fixed;z-index:80;max-width:280px;background:var(--surface);color:var(--text);
    border:1px solid var(--border);border-radius:var(--radius-sm);box-shadow:var(--shadow-lg);
    padding:var(--sp-2) var(--sp-2);font-size:var(--fs-sm);line-height:1.45;display:none;}
  #tipbox.show{display:block;}
  .note{color:var(--muted);font-size:var(--fs-sm);margin:var(--sp-2) 0 0;line-height:1.45;}
  .note ol{margin:var(--sp-1) 0 0;padding-left:var(--sp-4);}
  .note li{margin:3px 0;}
  .note b{color:var(--text);}
  .note+div,.note+.row{margin-top:var(--sp-3);}   /* кнопка под примечанием не липнет к тексту */
  .gl{color:var(--muted);font-size:var(--fs-sm);}
  /* Внутри строки настройки пояснение идёт отдельной строкой — как <small>.
     Без этого «Обзор» и «Состояние сервера…» слипались в одно слово:
     <small> тут занят под подсказку «?», поэтому текст кладут в .gl. */
  .row .r-label > .gl{display:block;margin-top:2px;font-size:var(--fs-xs);font-weight:400;}
  .empty{color:var(--muted);font-size:var(--fs-base);text-align:center;padding:var(--sp-6);}
  .empty b{display:block;font-size:var(--fs-base);font-weight:600;color:var(--text-2);}
  .empty i{display:block;margin-top:3px;font-size:var(--fs-sm);font-style:normal;color:var(--faint);}
  .empty svg{width:26px;height:26px;stroke:currentColor;fill:none;stroke-width:1.6;stroke-linecap:round;stroke-linejoin:round;opacity:.5;display:block;margin:0 auto var(--sp-2);}
  .vsep{width:1px;height:22px;background:var(--border);}
  /* шапка карточки с иконкой — единый вид на всех страницах */
  .card-head.ic{display:flex;align-items:center;gap:var(--sp-2);}
  .card-head.ic .dt-ic{width:var(--box-sm);height:var(--box-sm);border-radius:var(--box-sm-r);}
  .card-head.ic .dt-ic svg{width:15px;height:15px;}
  .card-head.ic .ht{flex:1;min-width:0;}
  .card-head.ic .ht h2{margin:0;} .card-head.ic .ht p{margin:2px 0 0;}
  /* подписи над колонками (тарифы и подобные таблицы) */
  .thead{display:grid;gap:var(--sp-2);padding:0 0 var(--sp-2);font-size:var(--fs-2xs);text-transform:uppercase;letter-spacing:.09em;color:var(--faint);font-weight:650;border-bottom:1px solid var(--border-soft);}
  .thead span:not(:first-child){text-align:center;}
  /* поле с единицей («дн.», ★, ₽) должно жить внутри своей колонки, а не вылезать */
  /* форма в одну линию с подписями (промокоды и т.п.) */
  .formgrid{display:grid;grid-template-columns:repeat(auto-fit,minmax(140px,1fr));gap:var(--sp-3);align-items:end;}
  .formgrid .fld{margin-bottom:0;}
  .formgrid .fld.act{display:flex;align-items:flex-end;} .formgrid .fld.act .btn{width:100%;}
  /* плейсхолдеры-подсказки для текстов бота */
  .ph{display:flex;flex-wrap:wrap;gap:var(--sp-1);align-items:center;margin-bottom:var(--sp-1);}
  .ph b{font-size:var(--fs-2xs);text-transform:uppercase;letter-spacing:.09em;color:var(--faint);font-weight:650;margin-right:2px;}
  code,.ph code{background:var(--surface-3);color:var(--text-2);border-radius:5px;padding:1px var(--sp-1);font-size:var(--fs-xs);font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;}
  .ph code{cursor:pointer;transition:background .12s,color .12s;}
  .ph code:hover{background:var(--accent-soft);color:var(--accent);}
  /* порядковый номер в списках-рейтингах */
  .rank{width:22px;height:22px;border-radius:var(--radius-xs);flex:none;display:grid;place-items:center;font-size:var(--fs-xs);font-weight:700;background:var(--surface-3);color:var(--muted);font-variant-numeric:tabular-nums;}
  .rank.top{background:var(--warn-bg);color:var(--warn);}
  .av-sm{width:var(--box-sm);height:var(--box-sm);border-radius:var(--box-sm-r);flex:none;display:grid;place-items:center;font-size:var(--fs-sm);font-weight:700;color:var(--on-accent);background:var(--accent);}
  /* пронумерованные шаги (настройка 2FA) */
  
  .step:first-child{border-top:none;padding-top:var(--sp-1);}
  .step .n{width:22px;height:22px;border-radius:50%;flex:none;display:grid;place-items:center;font-size:var(--fs-xs);font-weight:700;background:var(--accent-soft);color:var(--accent);}
  .step b{font-size:var(--fs-base);font-weight:600;display:block;}
  /* тонкая полоска-доля (статистика по тарифам, рефералы) */
  
  .bar i{display:block;height:100%;border-radius:3px;background:var(--accent);}
  .bar.good i{background:var(--good);} .bar.violet i{background:var(--violet);}
  .seg button{flex:none;white-space:nowrap;}
  .seg button{border:none;border-left:1px solid var(--control-line);background:transparent;color:var(--text);height:var(--ctl);padding:0 var(--sp-3);font:inherit;font-size:var(--fs-sm);cursor:pointer;font-variant-numeric:tabular-nums;}
  .seg button:first-child{border-left:none;} .seg button:hover{background:var(--surface-2);}
  .seg button.active{background:var(--accent-soft);color:var(--accent);font-weight:650;}

  .pill{display:inline-flex;align-items:center;gap:var(--sp-1);padding:3px var(--sp-2);border-radius:var(--pill);font-size:var(--fs-xs);font-weight:620;white-space:nowrap;}
  .pill svg{width:var(--ic-sm);height:var(--ic-sm);stroke:currentColor;fill:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;flex:none;}
  .pill svg.fill{fill:currentColor;stroke:none;}
  /* Состояние пишется ВТОРЫМ классом через пробел: .pill.good, .pill.warn.
     Так же, как у сообщения (.alert.good) и у наличия в корзине — один
     словарь на весь набор.

     Раньше у плашки состояния писались через дефис (.pill-good), и это
     стоило дороже, чем кажется: разметку пишут по памяти, «.pill good»
     выглядит правильно и молча даёт плашку без цвета. Ловится только
     глазами и только если знаешь, что искать.

     Прежние имена оставлены рядом: ими размечена вся панель, и переписывать
     сотню мест ради красоты — плохой размен. Новое пишем через пробел. */
  .pill.good, .pill-good{background:var(--good-bg);color:var(--good);}
  .pill.off,  .pill-mute{background:var(--surface-3);color:var(--muted);}
  .pill.bad,  .pill-bad{background:var(--bad-bg);color:var(--bad);}
  .pill.warn, .pill-warn{background:var(--warn-bg);color:var(--warn);}
  .pill.info, .pill-info{background:var(--info-bg);color:var(--info);}
  /* «Включено» — то же зелёное состояние: слово из панели, где им помечают
     действующую подписку. */
  .pill.on{background:var(--good-bg);color:var(--good);}
  .tag{font-size:var(--fs-2xs);font-weight:650;padding:1px var(--sp-1);border-radius:4px;flex:none;background:var(--surface-3);color:var(--muted);letter-spacing:.02em;text-transform:uppercase;}
  .tag.c-mute{background:var(--surface-3);color:var(--muted);}
  .tag.c-accent{background:var(--accent-soft);color:var(--accent);}
  .tag.c-good{background:var(--good-bg);color:var(--good);}
  .tag.c-warn{background:var(--warn-bg);color:var(--warn);}
  .tag.c-info{background:var(--info-bg);color:var(--info);}
  .tag.c-violet{background:var(--violet-bg);color:var(--violet);}
  /* свой выбор тега: цвет виден сразу, вид как у остальных полей */
  .tagpick{position:relative;}
  .tagpick-btn{display:inline-flex;align-items:center;gap:var(--sp-2);height:32px;padding:0 var(--sp-2);min-width:150px;
    background:var(--surface);border:1px solid var(--control-line);border-radius:var(--radius-xs);color:var(--text);
    font:inherit;font-size:var(--fs-base);cursor:pointer;transition:border-color .14s;}
  .tagpick-btn:hover{border-color:var(--accent-line);}
  .tagpick-btn .tp-val{flex:1;display:flex;align-items:center;gap:var(--sp-1);min-width:0;}
  .tagpick-btn svg{width:var(--ic-sm);height:var(--ic-sm);stroke:currentColor;fill:none;stroke-width:2;stroke-linecap:round;
    stroke-linejoin:round;color:var(--muted);transform:rotate(90deg);flex:none;}
  /* меню тега — fixed, иначе его обрезает прокрутка карточки клиента */
  .tagpick-menu{display:none;position:fixed;z-index:60;min-width:200px;max-height:260px;
    overflow:auto;background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);
    box-shadow:var(--shadow-lg);padding:var(--sp-1);}
  .tagpick-menu.show{display:block;}
  .tagpick-item{display:flex;align-items:center;gap:var(--sp-2);width:100%;text-align:left;background:none;border:none;
    color:var(--text);font:inherit;font-size:var(--fs-base);padding:var(--sp-1) var(--sp-2);border-radius:var(--radius-xs);cursor:pointer;}
  .tagpick-item:hover{background:var(--surface-2);}
  .tagpick-item.on{background:var(--accent-soft);}
  /* Выбор цвета тега — кружки вместо системного списка.
     Имя .tag-dot, а не .tag-dot: последним названы квадраты выбора темы в
     профиле, и два разных кружка с одним именем брали размер друг у друга —
     восемнадцать точек здесь против тридцати четырёх там. */
  .swatches{display:flex;gap:var(--sp-1);flex:none;}
  .tag-dot{width:18px;height:18px;border-radius:50%;border:2px solid transparent;cursor:pointer;padding:0;
    transition:transform .12s,border-color .12s;}
  .tag-dot:hover{transform:scale(1.15);}
  .tag-dot.on{border-color:var(--text);}
  .tag-dot.c-mute{background:var(--surface-3);}
  .tag-dot.c-accent{background:var(--accent);}
  .tag-dot.c-good{background:var(--good);}
  .tag-dot.c-warn{background:var(--warn);}
  .tag-dot.c-info{background:var(--info);}
  .tag-dot.c-violet{background:var(--violet);}

  .dot{width:7px;height:7px;border-radius:50%;flex-shrink:0;}
  .dot.on,.dot.up{background:var(--good);box-shadow:0 0 0 3px var(--good-bg);}
  .dot.off,.dot.down{background:var(--off);}
  .dot.stop{background:var(--accent);}

  /* ── клавиша.css ─────────────────────────────────────── */
/* ── Клавиша. Для подсказок «Ctrl K», «Esc». ── */
  .kbd{display:inline-flex;align-items:center;justify-content:center;min-width:18px;height:18px;
       padding:0 var(--sp-1);border:1px solid var(--border);border-bottom-width:2px;
       border-radius:var(--radius-xs);background:var(--surface);color:var(--muted);
       font-family:var(--mono);font-size:var(--fs-2xs);line-height:1;}

  

  /* ── кнопки.css ─────────────────────────────────────── */
/* ═══ КНОПКИ / ПОЛЯ ════════════════════════════════════════════════ */
  /* Кнопкой бывает и ссылка — например «В панель» в каталоге элементов.
     Без сброса подчёркивания и цвета она вылезает обычной синей ссылкой. */
  a.btn,a.btn:hover{text-decoration:none;color:var(--text);}
  a.btn.btn-primary,a.btn.btn-primary:hover{color:var(--on-accent);}
  .btn{display:inline-flex;align-items:center;gap:var(--sp-2);justify-content:center;height:var(--ctl);padding:0 var(--sp-4);border-radius:var(--radius-sm);border:1px solid transparent;font:inherit;font-size:var(--fs-base);font-weight:500;cursor:pointer;white-space:nowrap;transition:background .13s,border-color .13s,color .13s;min-width:116px;white-space:nowrap;}
  .btn.btn-narrow,.bulk .btn,.toolbar .btn.btn-narrow{min-width:0;}
  .btn-primary{background:var(--accent);color:var(--on-accent);border-color:var(--accent);}
  .btn-primary:hover{background:var(--accent-hover);border-color:var(--accent-hover);}
  .btn-ghost{background:transparent;color:var(--text-2);border-color:var(--control-line);}
  .btn-ghost:hover{background:var(--surface-2);color:var(--text);}
  .btn-sm{height:var(--ctl);padding:0 var(--sp-3);font-size:var(--fs-sm);}   /* размер тот же — класс оставлен для совместимости */
  .btn svg{width:var(--ic);height:var(--ic);stroke:currentColor;stroke-width:2;fill:none;stroke-linecap:round;stroke-linejoin:round;flex:none;}
  .menu-item{display:flex;align-items:center;gap:var(--sp-2);width:100%;text-align:left;background:none;border:none;color:var(--text);font:inherit;font-size:var(--fs-base);padding:var(--sp-2) var(--sp-2);border-radius:var(--radius-xs);cursor:pointer;}
  .menu-item:hover{background:var(--surface-2);}
  .menu-item svg{width:var(--ic);height:var(--ic);stroke:currentColor;fill:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;flex:none;}

  input.field,select.field,textarea.field{background:var(--surface);color:var(--text);border:1px solid var(--control-line);border-radius:var(--radius-xs);font:inherit;font-size:var(--fs-base);width:100%;transition:border-color .14s;}
  input.field,select.field{height:32px;padding:0 var(--sp-2);}
  textarea.field{padding:var(--sp-2) var(--sp-3);}
  input.field:focus,select.field:focus,textarea.field:focus{outline:none;border-color:var(--accent);}
  select.field:disabled,input.field:disabled{opacity:.55;cursor:not-allowed;}
  input.field::placeholder,textarea.field::placeholder{color:var(--faint);}
  /* Все поля текста одинаковой высоты по умолчанию и тянутся ТОЛЬКО вниз:
     вбок нельзя, меньше исходного — тоже. Атрибут rows игнорируем намеренно. */
  textarea.field{resize:vertical;width:100%;max-width:100%;min-width:100%;
    height:132px;min-height:132px;line-height:1.5;}
  textarea.ta{height:132px;min-height:132px;line-height:1.55;}
  .w-price{width:110px;} .w-num{width:84px;}
  .suffix,.price-box{display:inline-flex;align-items:center;gap:var(--sp-1);}
  .suffix .u,.price-box .u{color:var(--muted);font-size:var(--fs-sm);} .suffix .u{min-width:32px;} .fld:last-child{margin-bottom:0;}
  .fld label{display:block;font-size:var(--fs-base);font-weight:600;margin-bottom:2px;}
  .fld .hint{font-size:var(--fs-xs);color:var(--muted);margin-bottom:var(--sp-1);}
  .fld-ic{position:relative;}
  .fld-ic .im{position:absolute;left:11px;top:50%;transform:translateY(-50%);opacity:.6;pointer-events:none;}
  .fld-ic .im svg{width:15px;height:15px;stroke:currentColor;fill:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;}
  .fld-ic input.field{padding-left:var(--sp-6);}
  .fld .suffix{display:flex;width:100%;} .fld .suffix input.field{flex:1;} .fld .suffix .u{display:flex;align-items:center;}

  .iconbtn{width:var(--ctl-sm);height:var(--ctl-sm);border-radius:var(--radius-xs);flex:none;display:grid;place-items:center;background:transparent;border:1px solid transparent;color:var(--muted);cursor:pointer;text-decoration:none;transition:background .12s,color .12s,border-color .12s;}
  .iconbtn:hover{background:var(--surface-2);color:var(--text);border-color:var(--control-line);}
  .iconbtn.danger:hover{background:var(--accent-soft);color:var(--accent);border-color:var(--accent-line);}
  .iconbtn svg{width:var(--ic-sm);height:var(--ic-sm);stroke:currentColor;stroke-width:1.9;fill:none;stroke-linecap:round;stroke-linejoin:round;}
  .del{width:var(--ctl-sm);height:var(--ctl-sm);border-radius:var(--radius-xs);border:1px solid var(--control-line);background:transparent;color:var(--muted);cursor:pointer;flex:none;transition:.13s;display:grid;place-items:center;}
  .del svg{width:var(--ic-sm);height:var(--ic-sm);stroke:currentColor;fill:none;stroke-width:1.9;stroke-linecap:round;stroke-linejoin:round;}
  .del:hover{color:var(--accent);border-color:var(--accent-line);background:var(--accent-soft);}

  /* Цель касания добираем невидимым полем вокруг: сам переключатель ниже
     нормы нарочно — крупный тумблер в строке настроек выглядит грубо. */
  .sw::after{content:"";position:absolute;inset:-3px 0;}
  .sw{position:relative;width:34px;height:19px;border-radius:var(--pill);background:var(--surface-3);border:none;cursor:pointer;padding:0;flex-shrink:0;transition:background .15s;}
  .sw .knob{position:absolute;top:2px;left:2px;width:15px;height:15px;border-radius:50%;background:var(--surface);box-shadow:0 1px 2px rgba(0,0,0,.25);transition:left .15s;}
  .sw.on{background:var(--good);} .sw.on .knob{left:17px;}

  /* ── код-из-цифр.css ─────────────────────────────────────── */
/* ── Код из цифр: подтверждение входа, двухфакторная проверка.
     Клетки крупные и раздельные — так видно, сколько цифр введено и сколько
     осталось. Одним полем на шесть знаков человек теряет счёт. ── */
  .otp{display:flex;gap:var(--sp-2);}
  .otp input{width:var(--ctl);height:calc(var(--ctl) + var(--sp-2));text-align:center;
       border:1px solid var(--control-line);border-radius:var(--radius-sm);
       background:var(--surface);color:var(--text);font:inherit;
       font-size:var(--fs-xl);font-weight:600;font-variant-numeric:tabular-nums;
       box-shadow:var(--shadow-sm);transition:border-color .15s,box-shadow .15s;}
  .otp input:focus-visible{outline:none;border-color:var(--accent);
       box-shadow:0 0 0 3px color-mix(in oklab, var(--accent) 22%, transparent);}
  .otp .sep{align-self:center;color:var(--faint);}
  @media (max-width:420px){
    /* На узком экране шесть клеток по 36 не помещаются вместе с отступами */
    .otp{gap:var(--sp-1);}
    .otp input{width:var(--ctl-sm);font-size:var(--fs-lg);}
  }

  

  /* ── лента.css ─────────────────────────────────────── */
/* ── Лента: несколько карточек в ряд с прокруткой.
     Полосу прокрутки прячем, но саму прокрутку оставляем: на телефоне
     листают пальцем, на настольном — колесом с Shift. ── */
  .strip{display:flex;gap:var(--sp-3);overflow-x:auto;scroll-snap-type:x mandatory;
       scrollbar-width:none;padding-bottom:var(--sp-1);}
  .strip::-webkit-scrollbar{display:none;}
  .strip > *{flex:0 0 auto;scroll-snap-align:start;}
  .strip-dots{display:flex;gap:var(--sp-1);justify-content:center;margin-top:var(--sp-2);}
  .strip-dots i{width:6px;height:6px;border-radius:50%;background:var(--control-line);}
  .strip-dots i.on{background:var(--accent);width:18px;border-radius:var(--pill);}

  

  /* ── медиа-объект.css ─────────────────────────────────────── */
/* Медиа-объект, разделители и сетка карточек.
   Часть набора элементов. Собирается сборщиком:
   python tools/build_styles.py */

/* Мелочи, из которых собирается половина экранов, и до сих пор каждый экран
   рисовал их сам:

     .медиа     — картинка слева, текст справа: товар в списке, человек в
                  переписке, файл во вложениях;
     .разделы-сетка — карточки одинаковой ширины рядами, само переносится;
     .разделитель   — линия с подписью посреди содержимого. */

/* ── Медиа-объект ────────────────────────────────────────────────── */
  .медиа{display:flex;gap:var(--sp-3);align-items:flex-start;}
  .медиа > .лик{flex:none;width:var(--box-lg);height:var(--box-lg);
        border-radius:var(--radius);overflow:hidden;background:var(--surface-2);
        display:grid;place-items:center;color:var(--faint);
        font-size:var(--fs-xs);}
  .медиа > .лик img{width:100%;height:100%;object-fit:cover;display:block;}
  .медиа > .тело{flex:1;min-width:0;display:flex;flex-direction:column;
        gap:var(--sp-1);}
  .медиа b{font-size:var(--fs-md);line-height:1.35;}
  .медиа p{color:var(--muted);font-size:var(--fs-sm);line-height:1.55;margin:0;}
  .медиа .справа{flex:none;margin-left:auto;text-align:right;
        display:flex;flex-direction:column;gap:var(--sp-1);align-items:flex-end;}

  /* Крупный вид: картинка вдвое больше — для карточки товара в списке, где
     по снимку и выбирают. */
  .медиа.крупно > .лик{width:var(--лицо);height:var(--лицо);}

  @media (max-width:560px){
    /* Правый столбец уходит вниз: втроём в строку они не помещаются, и цена
       налезает на название. */
    .медиа{flex-wrap:wrap;}
    .медиа .справа{margin-left:calc(var(--box-lg) + var(--sp-3));
        align-items:flex-start;text-align:left;flex-direction:row;
        gap:var(--sp-3);}
  }

/* ── Сетка карточек ──────────────────────────────────────────────── */
  /* Одинаковой ширины, переносится сама. Число колонок не задаём: оно
     считается от места, и одна и та же сетка годится и в широкой странице,
     и в узкой колонке рядом с фильтрами. */
  .разделы-сетка{display:grid;gap:var(--sp-4);
        grid-template-columns:repeat(auto-fill, minmax(var(--сетка-мин), 1fr));}
  .разделы-сетка.плотно{gap:var(--sp-2);}
  .разделы-сетка.крупно{grid-template-columns:repeat(auto-fill, minmax(calc(var(--сетка-мин) * 1.5), 1fr));}

  /* Разноразмерная сетка: первая плитка вдвое шире и выше остальных. Годится
     для главной, где один раздел важнее прочих. */
  .разделы-сетка.разно > *:first-child{grid-column:span 2;grid-row:span 2;}
  @media (max-width:700px){
    .разделы-сетка.разно > *:first-child{grid-column:span 1;grid-row:span 1;}
  }

/* ── Разделитель с подписью ──────────────────────────────────────── */
  .разделитель-текст{display:flex;align-items:center;gap:var(--sp-3);
        color:var(--faint);font-size:var(--fs-xs);text-transform:uppercase;
        letter-spacing:.08em;}
  .разделитель-текст::before, .разделитель-текст::after{content:'';
        flex:1;height:1px;background:var(--border);}

  /* ── меню-боковое.css ─────────────────────────────────────── */
/* Меню сделано так, чтобы годиться не только этой панели: в админке магазина
   разделы будут свои, на витрине — короткий список. Поэтому оно умеет четыре
   вещи, и все они нужны разным проектам:

     • группы с подписями — когда разделов больше семи;
     • счётчик у пункта — непрочитанные обращения, новые заказы;
     • свёрнутый вид одними значками — когда экран дорог;
     • горизонтальный вид — на телефоне и в компактной раскладке.

   Ширина, отступы и цвета берутся из набора, поэтому меню переносится в
   другой проект вместе с темой и выглядит там своим. */

  /* на время переключения темы гасим все анимации — иначе элементы
     перекрашиваются вразнобой и цвет «проскакивает» через промежуточный */
  html.theme-switching *,html.theme-switching *::before,html.theme-switching *::after{transition:none!important;animation:none!important;}
  /* до перевода страницу не показываем, чтобы русский текст не мелькал */
  html.i18n-pending body{visibility:hidden;}
  html.i18n-pending{overflow:hidden;}

  .sidebar{width:var(--side-w);flex-shrink:0;background:var(--side);color:var(--side-text);
    display:flex;flex-direction:column;padding:var(--sp-2) var(--sp-2);gap:1px;overflow:auto;
    transition:margin-left .2s ease,width .16s ease;}
  /* прячем меню только в боковой раскладке — в компактной оно горизонтальное */
  :root:not([data-layout="compact"]) .shell.nav-hidden .sidebar{margin-left:calc(-1 * var(--side-w));}

  /* ── Шапка панели: кто вошёл и чем сейчас управляет ──────────────
     Строка с почтой и ссылкой смены стоит НАД переключателем, а не под ним:
     сперва человек убеждается, что он в нужной учётной записи, и только
     потом переключает раздел. Наоборот — и переключение уходит не туда,
     куда думал.

     Ссылка «Изменить» прижата вправо и приглушена: её ищут редко, но она
     обязана быть на виду, иначе смену учётной записи ищут в настройках. */
  .nav-head{display:flex;flex-direction:column;gap:var(--sp-2);
    padding:var(--sp-2) var(--sp-2) var(--sp-3);
    border-bottom:1px solid var(--border);margin-bottom:var(--sp-2);}
  .nav-head .кто{display:flex;align-items:center;justify-content:space-between;
    gap:var(--sp-2);min-height:var(--цель);}
  .nav-head .кто b{font-size:var(--fs-sm);font-weight:600;color:var(--side-text);
    overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
  .nav-head .кто a{display:inline-flex;align-items:center;min-height:var(--цель);
    color:var(--muted);font-size:var(--fs-xs);text-decoration:none;flex:none;}
  .nav-head .кто a:hover{color:var(--accent);}

  .nav-group{color:var(--side-muted);font-size:var(--fs-2xs);text-transform:uppercase;
    letter-spacing:.09em;padding:var(--sp-3) var(--sp-2) var(--sp-1);font-weight:650;white-space:nowrap;}
  .nav-group:first-child{padding-top:2px;}

  /* Пункт: рост как у всего управления, скругление общее. Активный —
     мягкая заливка акцентом и текст акцентом: полоску сбоку убрали, она
     спорила с заливкой и на свёрнутом виде обрезалась. */
  .nav{display:flex;align-items:center;gap:var(--sp-2);width:100%;text-align:left;background:none;
    border:none;color:var(--side-text);font:inherit;font-size:var(--fs-base);font-weight:500;
    height:var(--ctl);padding:0 var(--sp-2);border-radius:var(--radius-sm);cursor:pointer;
    transition:background .12s,color .12s;}
  .nav svg{width:var(--ic);height:var(--ic);flex:none;stroke:currentColor;fill:none;
    stroke-width:1.7;stroke-linecap:round;stroke-linejoin:round;opacity:.8;}
  .nav:hover{background:var(--side-active);color:var(--side-on-active);}
  .nav:hover svg{opacity:1;}
  .nav.active{background:var(--accent-soft);color:var(--accent);font-weight:600;}
  .nav.active svg{opacity:1;}

  /* Второй вид подсветки: заливка и полоска слева. Полоска сильнее держит
     взгляд, когда пунктов много и они идут сплошным столбцом, — глаз
     цепляется за край, а не пересчитывает заливки. Включается на всей
     панели, а не на пункте: вид подсветки должен быть один на меню. */
  .sidebar.полоска .nav{border-radius:0 var(--radius-sm) var(--radius-sm) 0;
    border-left:3px solid transparent;}
  .sidebar.полоска .nav.active{border-left-color:var(--accent);}
  .nav-badge{display:inline-block;margin-left:auto;background:var(--accent);color:var(--on-accent);
    border-radius:var(--pill);font-size:var(--fs-2xs);line-height:1;padding:3px var(--sp-1);font-weight:700;
    font-variant-numeric:tabular-nums;}

  /* Подпись внизу: кто вошёл. Отделена чертой и прижата к низу — так её
     находят, не читая всё меню. */
  .nav-foot{margin-top:auto;padding-top:var(--sp-2);border-top:1px solid var(--border);}
  .nav-user{display:flex;align-items:center;gap:var(--sp-2);padding:var(--sp-1) var(--sp-2);border-radius:var(--radius-sm);
    color:var(--side-text);text-decoration:none;}
  .nav-user:hover{background:var(--side-active);}
  .nav-user .ava{width:var(--box-sm);height:var(--box-sm);border-radius:var(--box-sm-r);flex:none;
    background:var(--accent-soft);color:var(--accent);display:grid;place-items:center;
    font-weight:700;font-size:var(--fs-xs);overflow:hidden;}
  .nav-user .ava img{width:100%;height:100%;object-fit:cover;display:block;}
  .nav-user b{font-size:var(--fs-sm);font-weight:600;display:block;line-height:1.25;}
  .nav-user small{display:block;color:var(--side-muted);font-size:var(--fs-2xs);line-height:1.25;}
  .nav-user .txt{min-width:0;}
  .nav-user .txt b,.nav-user .txt small{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}

  /* ── Свёрнутый вид: одни значки ──────────────────────────────────────
     Нужен там, где экран дорог: широкая таблица, разбор переписки. Ширина
     считается от роста пункта, поэтому при смене набора ничего не разъедется. */
  .sidebar.mini{width:calc(var(--ctl) + var(--sp-4));}
  .sidebar.mini .nav{justify-content:center;padding:0;gap:0;}
  .sidebar.mini .nav .lbl,.sidebar.mini .nav-group,
  .sidebar.mini .nav-user .txt{display:none;}
  .sidebar.mini .nav-badge{position:absolute;margin:0;transform:translate(11px,-11px);
    padding:2px var(--sp-1);}
  .sidebar.mini .nav{position:relative;}
  .sidebar.mini .nav-user{justify-content:center;padding:var(--sp-1) 0;}

  /* ── Горизонтальный вид: телефон и компактная раскладка ── */
  :root[data-layout="compact"] .body{flex-direction:column;}
  :root[data-layout="compact"] .sidebar{width:auto;flex-direction:row;align-items:center;
    overflow-x:auto;overflow-y:hidden;overscroll-behavior:contain;touch-action:pan-x;
    padding:var(--sp-1) var(--sp-2);gap:2px;border-bottom:1px solid var(--border);}
  :root[data-layout="compact"] .sidebar .nav-group,
  :root[data-layout="compact"] .sidebar .nav-foot{display:none;}
  :root[data-layout="compact"] .nav{white-space:nowrap;padding:0 var(--sp-3);flex:0 0 auto;width:auto;
    height:var(--ctl-sm);}
  :root[data-layout="compact"] .nav.active{background:var(--accent-soft);
    box-shadow:inset 0 -2px 0 var(--accent);}

  .main{flex:1;min-width:0;min-height:0;overflow:hidden;display:flex;flex-direction:column;}
  .view{display:none;min-height:0;flex:1;flex-direction:column;overflow:hidden;}
  .view.active{display:flex;animation:egfade .16s ease;}
  @media (prefers-reduced-motion:reduce){.view.active{animation:none;}}
  @keyframes egfade{from{opacity:0;transform:translateY(3px);}to{opacity:1;transform:none;}}
  /* обычные страницы прокручиваются целиком; страница клиентов — своими колонками */
  .view>.wrap{overflow-y:auto;flex:1;min-height:0;}
  .wrap{padding:var(--sp-5) var(--sp-5) var(--sp-6);}

  .page-head{display:flex;align-items:center;justify-content:space-between;gap:var(--sp-3);flex-wrap:wrap;margin-bottom:var(--sp-4);}
  .page-head h1{margin:0;font-size:var(--fs-2xl);font-weight:660;letter-spacing:-.02em;}
  .page-head p{margin:1px 0 0;color:var(--muted);font-size:var(--fs-sm);}

  /* ── меню-верхнее.css ─────────────────────────────────────── */
/* Боковое меню годится, когда разделов много и они равноправны — как в
   панели. Горизонтальное нужно там, где разделов пять-шесть, а по бокам
   дорога каждая точка: витрина, лендинг, кабинет клиента на широком экране.

   На телефоне горизонтальное меню не режем в «гамбургер» автоматически:
   пять пунктов помещаются в строку с прокруткой, и это честнее — видно, что
   разделы вообще есть. */

  .navbar{display:flex;align-items:center;gap:var(--sp-1);height:var(--ctl);
          overflow-x:auto;scrollbar-width:none;}
  .navbar::-webkit-scrollbar{display:none;}
  .navbar a{display:inline-flex;align-items:center;gap:var(--sp-2);height:var(--ctl);
            padding:0 var(--sp-3);border-radius:var(--radius-sm);white-space:nowrap;
            color:var(--text-2);text-decoration:none;font-size:var(--fs-sm);font-weight:600;
            transition:background .13s,color .13s;}
  .navbar a:hover{background:var(--surface-2);color:var(--text);}
  .navbar a.on{background:var(--accent-soft);color:var(--accent);}
  .navbar a .ic{width:var(--ic-sm);height:var(--ic-sm);flex:none;}
  .navbar .cnt{background:var(--bad);color:#fff;border-radius:var(--pill);
               font-size:var(--fs-2xs);font-weight:700;padding:1px var(--sp-1);
               font-variant-numeric:tabular-nums;}

  /* Подчёркивание вместо заливки — для витрины, где фон цветной */
  .navbar.line a{border-radius:0;border-bottom:2px solid transparent;height:calc(var(--ctl) + 6px);}
  .navbar.line a:hover{background:transparent;border-bottom-color:var(--control-line);}
  .navbar.line a.on{background:transparent;border-bottom-color:var(--accent);}

  /* Путь по разделам: где человек находится и как вернуться назад */
  /* Список без маркеров и номеров задан здесь, а не оставлен общему сбросу:
     набор переносится в другие проекты по файлам, и сброс туда может не
     поехать — в магазине он исключён нарочно, у него свой. Путь размечают
     тегом <ol> ради поисковика, и без этой строки перед разделами
     появляются номера: «1. Главная 2. Бинокли». */
  .crumbs{display:flex;align-items:center;gap:var(--sp-1);font-size:var(--fs-xs);color:var(--muted);
          flex-wrap:wrap;list-style:none;margin:0;padding:0;}
  .crumbs li{display:inline-flex;align-items:center;gap:var(--sp-1);}
  /* Ссылка пути — цель касания. Ряд ссылок подряд, а не текст в предложении:
     по ним попадают пальцем, и высоты по кеглю мало. */
  .crumbs a{display:inline-flex;align-items:center;min-height:var(--цель);
        color:var(--muted);text-decoration:none;}
  .crumbs a:hover{color:var(--accent);}
  .crumbs .sep{opacity:.5;}
  .crumbs b{color:var(--text);font-weight:600;}
  /* В магазине путь длиннее, чем в панели: каталог, раздел, подраздел, товар.
     На узком экране он не переносится на три строки, а листается вбок —
     перенос съедает пол-экрана ради того, что читают мельком. */
  @media (max-width:640px){
    .crumbs{flex-wrap:nowrap;overflow-x:auto;scrollbar-width:none;}
    .crumbs::-webkit-scrollbar{display:none;}
    .crumbs a, .crumbs b{white-space:nowrap;}
  }

  /* ── меню-правой-кнопкой.css ─────────────────────────────────────── */
/* ── Меню по правой кнопке.
     Появляется там, где нажали, поэтому положение задаёт скрипт, а не стили.
     Внешне это то же меню действий: заводить второй вид меню незачем. ── */
  .ctx{position:fixed;z-index:75;min-width:172px;}
  .ctx .hint{padding:var(--sp-1) var(--sp-2);color:var(--faint);font-size:var(--fs-2xs);}

  

  /* ── окна.css ─────────────────────────────────────── */
/* ═══ МОДАЛКИ / САВБАР / ТОСТ ══════════════════════════════════════ */
  .modal{position:fixed;inset:0;background:rgba(10,8,7,.55);backdrop-filter:blur(3px);display:none;align-items:center;justify-content:center;z-index:60;padding:var(--sp-5);}
  .modal.show{display:flex;animation:egfade .14s ease;}
  #confirm-modal,#qr-modal{z-index:80;}
  /* ЕДИНЫЙ размер всплывающих окон: ширина, высота, отступы и скругление
     одинаковые везде. Длинное содержимое прокручивается ВНУТРИ окна. */
  .modal .box{background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);
    padding:var(--sp-5);text-align:left;width:min(820px,94vw);max-width:none;max-height:88vh;
    display:flex;flex-direction:column;overflow:hidden;box-shadow:var(--shadow-lg);}
  .modal .box>*{flex:none;}
  /* всё, кроме заголовка и нижней полосы кнопок, живёт в прокручиваемой части */
  .modal .box>.m-body{flex:1;min-height:0;overflow:auto;}
  /* Короткое подтверждение отличается только выключкой по центру: ширина,
     отступы и скругление у всех окон одни. Раньше здесь стояла своя ширина
     в 420px, и окна панели были двух разных размеров. */
  .modal .box.ask{text-align:center;}
  .modal .box h3{margin:0 0 var(--sp-3);font-size:var(--fs-lg);font-weight:640;}
  .modal img{width:240px;height:240px;background:#fff;border-radius:var(--radius-sm);padding:var(--sp-2);}
  .modal .close{margin-top:var(--sp-4);}
  /* подвал модалки — кнопки всегда справа, всегда на одном расстоянии */
  .m-foot{display:flex;gap:var(--sp-2);justify-content:flex-end;align-items:center;margin-top:var(--sp-4);padding-top:var(--sp-3);border-top:1px solid var(--border-soft);}
  .m-foot.spread{justify-content:space-between;flex-wrap:wrap;row-gap:var(--sp-2);}
  .m-foot .acts,.m-foot>span{display:flex;gap:var(--sp-2);align-items:center;flex-wrap:wrap;}
  .modal .box>.m-foot:last-child{margin-bottom:0;}
  .savebar{position:sticky;bottom:0;z-index:5;display:none;justify-content:flex-end;padding:var(--sp-3) var(--sp-5);padding-bottom:calc(11px + env(safe-area-inset-bottom));background:var(--surface);border-top:1px solid var(--border);flex:none;margin-top:auto;}
  /* последняя карточка не должна уезжать под полосу: класс ставим сами,
     без :has() — он есть не везде и тормозит на больших страницах */
  /* полоса показывается, только когда есть что сохранять */
  .view.dirty>.savebar{display:flex;animation:egfade .14s ease;}
  .view.with-savebar>.wrap{padding-bottom:var(--sp-2);}
  .savebar .inner{width:100%;display:flex;justify-content:flex-end;gap:var(--sp-2);}
  .toast{position:fixed;left:50%;bottom:22px;transform:translate(-50%,14px);background:var(--text);color:var(--bg);font-weight:550;font-size:var(--fs-sm);padding:var(--sp-2) var(--sp-4);border-radius:var(--pill);opacity:0;pointer-events:none;transition:opacity .18s,transform .18s;z-index:99;box-shadow:var(--shadow-lg);}
  .toast.show{opacity:1;transform:translate(-50%,0);}
  .toast.err{background:var(--accent);color:var(--on-accent);}

  /* ── окно-вопроса.css ─────────────────────────────────────── */
/* ── Окно с вопросом: «Удалить клиента?» ──
     Отличается от обычного окна тем, что у него нет содержимого, кроме
     вопроса, и главная кнопка — опасная. Поэтому она справа и красная, а
     «Отмена» слева: так труднее нажать разрушительное по привычке. */
  .ask{width:min(400px, 92vw);}
  .ask .body{padding:var(--sp-4);}
  .ask .body h2{font-size:var(--fs-lg);font-weight:600;margin-bottom:var(--sp-1);}
  .ask .body p{color:var(--text-2);font-size:var(--fs-sm);line-height:1.55;}
  .ask .body .what{margin-top:var(--sp-3);padding:var(--sp-2) var(--sp-3);
       background:var(--surface-2);border-radius:var(--radius-sm);
       font-size:var(--fs-sm);color:var(--text);}
  .ask .foot{display:flex;gap:var(--sp-2);justify-content:flex-end;
       padding:var(--sp-3) var(--sp-4);border-top:1px solid var(--border);}

  /* ── опросник.css ─────────────────────────────────────── */
/* ── Опросник: вопрос и варианты ответа.
     Нужен для отзыва после закрытия обращения и для причины отказа от
     подписки. Вариант — целая строка, а не крошечный кружок: пальцем в
     кружок не попасть, и человек бросает опрос. ── */
  .quiz{display:flex;flex-direction:column;gap:var(--sp-4);}
  .quiz-q{font-size:var(--fs-md);font-weight:600;}
  .quiz-q small{display:block;color:var(--muted);font-size:var(--fs-xs);
       font-weight:400;margin-top:2px;}
  .quiz-opts{display:flex;flex-direction:column;gap:var(--sp-2);}
  .quiz-opt{display:flex;align-items:center;gap:var(--sp-3);padding:var(--sp-3);
       border:1px solid var(--control-line);border-radius:var(--radius-sm);
       background:var(--surface);cursor:pointer;transition:border-color .13s,background .13s;}
  .quiz-opt:hover{background:var(--surface-2);}
  .quiz-opt.on{border-color:var(--accent);background:var(--accent-soft);}
  .quiz-opt .mark{width:18px;height:18px;flex:none;border-radius:50%;
       border:1.6px solid var(--control-line);display:grid;place-items:center;}
  .quiz-opt.on .mark{border-color:var(--accent);background:var(--accent);}
  .quiz-opt.on .mark::after{content:"";width:7px;height:7px;border-radius:50%;
       background:var(--on-accent);}
  .quiz-opt .txt{min-width:0;}
  .quiz-opt b{display:block;font-weight:550;font-size:var(--fs-base);}
  .quiz-opt small{color:var(--muted);font-size:var(--fs-xs);}
  /* Оценка звёздами — для отзыва о поддержке */
  .stars{display:inline-flex;gap:var(--sp-1);}
  .stars button{border:none;background:none;padding:var(--sp-1);cursor:pointer;
       color:var(--control-line);line-height:0;}
  .stars button svg{width:var(--box-sm);height:var(--box-sm);fill:currentColor;}
  .stars button.on,.stars button:hover{color:var(--warn);}

  /* ── переписка.css ─────────────────────────────────────── */
/* ── Переписка ──
   Наши сообщения справа, клиента — слева, как в любом мессенджере: спорить
   с этой привычкой незачем. Время и признак прочтения — внутри пузыря, иначе
   они рвут ленту на части. */

  .chat{display:flex;flex-direction:column;gap:var(--sp-2);}
  .bubble{max-width:min(560px, 84%);padding:var(--sp-2) var(--sp-3);
       border-radius:var(--radius-sm);background:var(--surface-2);color:var(--text);
       font-size:var(--fs-sm);line-height:1.5;align-self:flex-start;
       word-break:break-word;}
  .bubble.mine{align-self:flex-end;background:var(--accent-soft);color:var(--text);}
  .bubble .who{display:block;font-size:var(--fs-2xs);font-weight:650;color:var(--muted);
       margin-bottom:2px;}
  .bubble .meta{display:flex;align-items:center;gap:var(--sp-1);margin-top:var(--sp-1);
       color:var(--faint);font-size:var(--fs-2xs);justify-content:flex-end;}
  .bubble .meta .seen{color:var(--good);}
  /* Служебная строка посреди ленты: дата, «Обращение закрыто» */
  .chat-mark{align-self:center;color:var(--muted);font-size:var(--fs-2xs);
       background:var(--surface-2);border-radius:var(--pill);padding:2px var(--sp-3);}
  /* Лента прокручивается сама к последнему сообщению */
  .chat-scroll{flex:1;min-height:0;overflow-y:auto;padding:var(--sp-3);
       display:flex;flex-direction:column;gap:var(--sp-2);}
  /* Строка ответа снизу: поле растёт до трёх строк, потом прокручивается */
  .chat-send{flex:none;display:flex;gap:var(--sp-2);align-items:flex-end;
       padding:var(--sp-3);border-top:1px solid var(--border);background:var(--surface);}
  .chat-send textarea{flex:1;min-width:0;min-height:var(--ctl);max-height:96px;
       resize:none;padding:var(--sp-2) var(--sp-3);border:1px solid var(--control-line);
       border-radius:var(--radius-sm);background:var(--surface);color:var(--text);
       font:inherit;font-size:var(--fs-base);line-height:1.45;}
  .chat-send textarea:focus-visible{outline:none;border-color:var(--accent);
       box-shadow:0 0 0 3px color-mix(in oklab, var(--accent) 22%, transparent);}

  /* ── плашка.css ─────────────────────────────────────── */
/* Плашка сообщает о состоянии дел, а не о результате действия. Результат
   действия — это всплывающее уведомление внизу («Сохранено»), оно исчезает
   само. Плашка остаётся, пока причина не устранена: «Домен не подтверждён»,
   «Оплата не настроена».

   Цвет по смыслу: серая — просто к сведению, жёлтая — стоит поправить,
   красная — не работает, зелёная — получилось. Красную бережём: если ею
   красить всё подряд, её перестают замечать. */

  .alert{display:flex;gap:var(--sp-3);padding:var(--sp-3);border:1px solid var(--border);
       border-radius:var(--radius-sm);background:var(--surface-2);color:var(--text);
       font-size:var(--fs-sm);line-height:1.5;}
  .alert > svg{width:var(--ic);height:var(--ic);flex:none;margin-top:1px;
       stroke:currentColor;fill:none;stroke-width:1.8;stroke-linecap:round;stroke-linejoin:round;}
  .alert b,.alert .body b{display:block;font-weight:600;margin-bottom:2px;}
  .alert .body p{display:block;}
  /* Имя .body занято начинкой карточки, где оно раскладывает содержимое в
     строку. Внутри плашки нам нужен столбец: заголовок, под ним пояснение. */
  .alert .body{min-width:0;display:block;}
  .alert .act{margin-left:auto;flex:none;align-self:center;}
  .alert.info{border-color:var(--accent-line);background:var(--accent-soft);color:var(--accent);}
  .alert.warn{border-color:color-mix(in oklab, var(--warn) 30%, transparent);
       background:var(--warn-bg);color:var(--warn);}
  .alert.bad{border-color:color-mix(in oklab, var(--bad) 30%, transparent);
       background:var(--bad-bg);color:var(--bad);}
  .alert.good{border-color:color-mix(in oklab, var(--good) 30%, transparent);
       background:var(--good-bg);color:var(--good);}
  /* Внутри цветной плашки текст остаётся читаемым: цвет берёт заголовок и
     значок, а пояснение — обычный цвет текста, иначе абзац сливается. */
  .alert.info .body p,.alert.warn .body p,.alert.bad .body p,.alert.good .body p{
       color:var(--text-2);margin-top:2px;}

  

  /* ── плитка-показателя.css ─────────────────────────────────────── */
/* Плитка отвечает на три вопроса сразу, и в этом порядке:

     что это         мелкой приглушённой строкой сверху
     сколько         крупным числом, моноширинными цифрами
     куда движется   значком с процентом в углу и пояснением внизу

   Наши прежние плитки отвечали только на первые два: «Доход за 30 дней —
   4 470». Само по себе это число ничего не говорит: много или мало, растёт
   или падает — непонятно, пока не вспомнишь прошлый месяц.

   Цифры моноширинные нарочно: сводка обновляется каждую минуту, и от
   прыгающей ширины цифр плитки дёргаются. */

  .kpi{position:relative;display:flex;flex-direction:column;gap:var(--sp-4);
       padding:var(--sp-4);background:var(--surface);border:1px solid var(--border);
       border-radius:var(--radius-lg);box-shadow:var(--shadow-sm);}
  /* Едва заметная подсветка снизу вверх — чтобы плитка отделялась от фона
     мягче, чем рамкой. У акцента берём считанные проценты: иначе сводка
     превращается в красное поле. */
  .kpi::before{content:"";position:absolute;inset:0;border-radius:inherit;
       background:linear-gradient(to top,
         color-mix(in oklab, var(--accent) 4%, transparent), transparent 60%);
       pointer-events:none;}
  .kpi > *{position:relative;}

  .kpi .назв{color:var(--muted);font-size:var(--fs-sm);line-height:1.3;}
  .kpi .знч{font-size:var(--fs-2xl);font-weight:600;letter-spacing:-.02em;
       font-variant-numeric:tabular-nums;line-height:1.15;}
  .kpi .знч small{font-size:var(--fs-md);font-weight:500;color:var(--muted);}

  /* Значок в углу: рост или падение за период */
  .kpi .рост{position:absolute;top:var(--sp-4);right:var(--sp-4);
       display:inline-flex;align-items:center;gap:var(--sp-1);height:22px;
       padding:0 var(--sp-2);border:1px solid var(--border);border-radius:var(--radius-xs);
       font-size:var(--fs-xs);font-weight:550;color:var(--text-2);
       font-variant-numeric:tabular-nums;background:var(--surface);}
  .kpi .рост svg{width:13px;height:13px;stroke:currentColor;fill:none;stroke-width:2;
       stroke-linecap:round;stroke-linejoin:round;}
  .kpi .рост.вверх{color:var(--good);border-color:color-mix(in oklab, var(--good) 30%, transparent);}
  .kpi .рост.вниз{color:var(--bad);border-color:color-mix(in oklab, var(--bad) 30%, transparent);}

  /* Подвал: куда движется и за какой срок */
  .kpi .низ{display:flex;flex-direction:column;gap:var(--sp-1);font-size:var(--fs-sm);}
  .kpi .низ b{display:flex;align-items:center;gap:var(--sp-2);font-weight:550;color:var(--text);}
  .kpi .низ b svg{width:var(--ic-sm);height:var(--ic-sm);stroke:currentColor;fill:none;
       stroke-width:2;flex:none;}
  .kpi .низ span{color:var(--muted);}

  /* Ряд плиток: четыре в строку на широком, две на среднем, одна на телефоне */
  .kpi-row{display:grid;gap:var(--sp-3);grid-template-columns:repeat(4,minmax(0,1fr));}
  @media (max-width:1100px){ .kpi-row{grid-template-columns:repeat(2,minmax(0,1fr));} }
  @media (max-width:620px){ .kpi-row{grid-template-columns:1fr;} }

  /* ── поиск.css ─────────────────────────────────────── */
/* Открывается с клавиатуры (Ctrl+K) и кнопкой в шапке. Ищет сразу везде:
   клиентов, разделы, действия. Смысл в том, чтобы не помнить, где что
   лежит: набрал «промо» — получил и раздел промокодов, и действие
   «Создать промокод».

   Устройство и размеры повторяют образец, на котором мы сошлись: поле
   ростом 36 с чертой под ним, список высотой в десять строк, заголовки
   групп мелкие и приглушённые, у пункта — значок слева и клавиша справа.

   Выбранный пункт подсвечивается мягкой заливкой, а не рамкой: рамка
   прыгает при движении стрелками и рябит. */

  /* Затемнение позади: закрывает панель, чтобы список читался */
  .find-back{position:fixed;inset:0;z-index:80;background:rgba(0,0,0,.45);
       backdrop-filter:blur(2px);opacity:0;visibility:hidden;transition:opacity .13s,visibility .13s;}
  .find-back.open{opacity:1;visibility:visible;}

  /* Окно: не по центру, а ближе к верху — так список не прыгает, когда
     результатов становится больше. */
  .find{position:fixed;z-index:81;left:50%;top:12vh;transform:translateX(-50%) translateY(-6px);
       width:min(560px, calc(100vw - var(--sp-6)));background:var(--surface);
       border:1px solid var(--border);border-radius:var(--radius);box-shadow:var(--shadow-lg);
       overflow:hidden;display:flex;flex-direction:column;
       opacity:0;visibility:hidden;transition:opacity .13s,transform .13s,visibility .13s;}
  .find.open{opacity:1;visibility:visible;transform:translateX(-50%);}

  /* Поле: та же высота, что у всего управления. Своей рамки нет — её роль
     играет черта под полем, иначе в окне получается рамка в рамке. */
  .find-input{display:flex;align-items:center;gap:var(--sp-2);height:var(--ctl);
       padding:0 var(--sp-3);border-bottom:1px solid var(--border);flex:none;}
  .find-input svg{width:var(--ic);height:var(--ic);flex:none;opacity:.5;
       stroke:currentColor;fill:none;stroke-width:1.9;}
  .find-input input{flex:1;min-width:0;height:100%;border:none;background:none;
       font:inherit;font-size:var(--fs-base);color:var(--text);outline:none;}
  .find-input input::placeholder{color:var(--muted);}
  .find-input .esc{flex:none;color:var(--faint);font-size:var(--fs-2xs);
       font-family:var(--mono);border:1px solid var(--border);border-radius:var(--radius-xs);
       padding:1px var(--sp-1);}

  /* Список: высотой в десять строк. Больше — прокрутка внутри окна, само
     окно при этом не растёт и не прыгает. */
  .find-list{max-height:calc(var(--ctl-sm) * 10);overflow-y:auto;padding:var(--sp-1);}
  .find-group{padding:var(--sp-1) 0;}
  .find-group > .ttl{padding:var(--sp-1) var(--sp-2);color:var(--muted);
       font-size:var(--fs-xs);font-weight:600;}
  .find-item{display:flex;align-items:center;gap:var(--sp-2);width:100%;
       height:var(--ctl-sm);padding:0 var(--sp-2);border:none;background:none;
       border-radius:var(--radius-xs);color:var(--text);font:inherit;font-size:var(--fs-sm);
       text-align:left;cursor:pointer;}
  .find-item svg{width:var(--ic-sm);height:var(--ic-sm);flex:none;color:var(--muted);
       stroke:currentColor;fill:none;stroke-width:1.8;}
  .find-item .sub{color:var(--muted);font-size:var(--fs-xs);}
  .find-item .kbd{margin-left:auto;color:var(--faint);font-size:var(--fs-2xs);
       font-family:var(--mono);letter-spacing:.08em;}
  .find-item:hover,.find-item.on{background:var(--surface-2);}
  .find-item.on{color:var(--text);}
  .find-sep{height:1px;background:var(--border);margin:var(--sp-1) 0;}

  /* Ничего не нашлось: короткая фраза и подсказка, что делать */
  .find-empty{padding:var(--sp-5) var(--sp-3);text-align:center;color:var(--muted);
       font-size:var(--fs-sm);}
  .find-empty b{display:block;color:var(--text);font-weight:600;margin-bottom:2px;}

  /* Подвал окна: подсказки по клавишам. На телефоне не нужен — там нет
     стрелок, и строка только занимает место. */
  .find-foot{display:flex;align-items:center;gap:var(--sp-3);padding:var(--sp-2) var(--sp-3);
       border-top:1px solid var(--border);color:var(--faint);font-size:var(--fs-2xs);flex:none;}
  .find-foot span{display:inline-flex;align-items:center;gap:var(--sp-1);}
  .find-foot .kbd{font-family:var(--mono);border:1px solid var(--border);
       border-radius:var(--radius-xs);padding:1px var(--sp-1);color:var(--muted);}
  @media (max-width:640px){
    .find{top:var(--sp-3);width:calc(100vw - var(--sp-5));}
    .find-foot{display:none;}
  }

  /* ── поле-поиска.css ─────────────────────────────────────── */
/* Строка поиска: поле с лупой внутри и очисткой.
   Часть набора элементов. Собирается сборщиком:
   python tools/build_styles.py */

/* Не путать с окном поиска по сервису (.find): то открывается с клавиатуры и
   ищет везде, а это — обычное поле на странице. Строка поиска нужна и
   магазину в шапке, и админке над таблицей, поэтому лежит в общих элементах.

   Лупа внутри поля, а не кнопкой рядом: кнопка рядом заставляет целиться,
   тогда как ввод начинают прямо с клавиатуры. Нажимать на лупу вообще не
   обязательно — Enter делает то же.

   Крестик появляется, только когда есть что стирать. Постоянный крестик в
   пустом поле читается как «закрыть» и по нему нажимают, чтобы убрать поиск.

   Высота — общая высота управления. Своей здесь нет и быть не может: рядом
   стоят кнопки, и разница даже в две точки видна на глаз. */

  .search{position:relative;display:flex;align-items:center;width:100%;}
  /* Размер значка задан здесь, а не оставлен общему сбросу: набор переносится
     в другие проекты по файлам, и сброс туда может не поехать вовсе — в
     магазине он исключён из сборки нарочно, у него свой. Значок без размера
     берёт двадцать четыре точки из viewBox и растягивается на пол-шапки. */
  .search > svg{position:absolute;left:var(--sp-3);width:var(--ic);height:var(--ic);
        color:var(--muted);stroke:currentColor;fill:none;stroke-width:1.9;
        pointer-events:none;}
  /* Отступ слева освобождает место под лупу: считается от неё, а не
     подобран числом, — поменяется размер значка, отступ поедет следом. */
  .search input{width:100%;height:var(--ctl);
        padding-left:calc(var(--sp-3) * 2 + var(--ic));
        padding-right:calc(var(--sp-3) * 2 + var(--ic));
        border:1px solid var(--control-line);border-radius:var(--radius-sm);
        background:var(--surface);color:var(--text);font:inherit;
        font-size:var(--fs-sm);outline:none;}
  .search input::placeholder{color:var(--muted);}
  .search input:focus{border-color:var(--accent);
        box-shadow:0 0 0 3px var(--accent-soft);}
  .search input::-webkit-search-cancel-button{display:none;}

  .search .стереть{position:absolute;right:var(--sp-2);border:none;background:none;
        color:var(--faint);cursor:pointer;padding:var(--sp-1);line-height:1;
        border-radius:var(--radius-xs);display:none;
        min-width:var(--цель);min-height:var(--цель);place-items:center;}
  .search .стереть svg{width:var(--ic-sm);height:var(--ic-sm);
        stroke:currentColor;fill:none;stroke-width:2;}
  .search .стереть:hover{color:var(--text);background:var(--surface-2);}
  /* Показываем крестик, только когда в поле что-то есть. Признак ставит
     разметка (класс «есть»), потому что состояние поля знает страница. */
  .search.есть .стереть{display:grid;}

  /* Кнопка «Найти» справа, слитно с полем. Нужна там, где поиск —
     отдельное действие: на странице результатов, в мастере. */
  .search.с-кнопкой input{border-top-right-radius:0;border-bottom-right-radius:0;}
  .search.с-кнопкой .btn{border-top-left-radius:0;border-bottom-left-radius:0;
        margin-left:-1px;flex:none;}
  .search.с-кнопкой .стереть{right:calc(var(--sp-2) + var(--поиск-кн));}

  /* Мелкая: в шапке таблицы и в боковой колонке, где рост управления давит */
  .search.мелкая input{height:var(--ctl-sm);font-size:var(--fs-xs);}
  .search.мелкая > svg{width:var(--ic-sm);height:var(--ic-sm);}

  /* ── поле-с-календарём.css ─────────────────────────────────────── */
/* ── Поле с календарём.
     Само поле обычное, календарь всплывает под ним и повторяет его ширину.
     Дату можно и напечатать: выбирать мышью декабрь позапрошлого года долго. ── */
  .datefield{position:relative;}
  .datefield .field{padding-right:var(--sp-6);}
  .datefield .open{position:absolute;right:var(--sp-1);top:50%;transform:translateY(-50%);
       width:var(--ctl-sm);height:var(--ctl-sm);border:none;background:none;
       border-radius:var(--radius-xs);color:var(--muted);cursor:pointer;
       display:grid;place-items:center;}
  .datefield .open:hover{background:var(--surface-2);color:var(--text);}
  .datefield .drop{position:absolute;z-index:60;top:calc(100% + var(--sp-1));left:0;
       opacity:0;visibility:hidden;transform:translateY(-4px);
       transition:opacity .12s,transform .12s,visibility .12s;}
  .datefield.open-drop .drop{opacity:1;visibility:visible;transform:none;}

  

  /* ── поле-с-подсказками.css ─────────────────────────────────────── */
/* ── Поле с подсказками: выбор тарифа, поиск клиента в форме.
     Список появляется под полем и повторяет его ширину — тогда понятно, что
     он относится к нему, а не к странице. ── */
  .combo{position:relative;}
  .combo .list{position:absolute;z-index:60;top:calc(100% + var(--sp-1));left:0;right:0;
       background:var(--surface);border:1px solid var(--border);border-radius:var(--radius-sm);
       box-shadow:var(--shadow-lg);padding:var(--sp-1);max-height:calc(var(--ctl-sm) * 7);
       overflow-y:auto;opacity:0;visibility:hidden;transform:translateY(-4px);
       transition:opacity .12s,transform .12s,visibility .12s;}
  .combo.open .list{opacity:1;visibility:visible;transform:none;}
  .combo .list button{display:flex;align-items:center;gap:var(--sp-2);width:100%;
       height:var(--ctl-sm);padding:0 var(--sp-2);border:none;background:none;
       border-radius:var(--radius-xs);color:var(--text);font:inherit;font-size:var(--fs-sm);
       text-align:left;cursor:pointer;}
  .combo .list button:hover,.combo .list button.on{background:var(--surface-2);}
  .combo .list .sub{margin-left:auto;color:var(--muted);font-size:var(--fs-xs);}
  .combo .list .none{padding:var(--sp-3);text-align:center;color:var(--muted);
       font-size:var(--fs-sm);}

  

  /* ── поле-с-приставкой.css ─────────────────────────────────────── */
/* ── Поле с приставками: кнопка рядом, единица, адрес.
     Приставки слиты с полем в один блок: разрыв между полем и кнопкой
     читается как два разных управления. ── */
  .igroup{display:flex;align-items:stretch;}
  .igroup > *{border-radius:0;margin-left:-1px;}
  .igroup > *:first-child{border-top-left-radius:var(--radius-sm);
       border-bottom-left-radius:var(--radius-sm);margin-left:0;}
  .igroup > *:last-child{border-top-right-radius:var(--radius-sm);
       border-bottom-right-radius:var(--radius-sm);}
  .igroup .field{flex:1;min-width:0;}
  .igroup .field:focus-visible{z-index:2;}
  .igroup .fix{display:inline-flex;align-items:center;flex:none;height:var(--ctl);
       padding:0 var(--sp-3);border:1px solid var(--control-line);background:var(--surface-2);
       color:var(--muted);font-size:var(--fs-sm);white-space:nowrap;}
  .igroup .btn{flex:none;}

  

  /* ── поле-телефона.css ─────────────────────────────────────── */
/* Поле телефона: приставка +7 и запись цифр по-русски.
   Часть набора элементов. Собирается сборщиком:
   python tools/build_styles.py */

/* Номер вводят чаще любого другого поля: заказ, звонок, вход по коду. И
   ошибаются в нём чаще всего — потому что каждый пишет по-своему: 8, +7,
   через скобки, без них, с пробелами.

   Поэтому «+7» показано приставкой внутри поля и не стирается: человеку
   остаётся ввести десять цифр, и вопрос «с восьмёрки или с семёрки» просто
   не возникает. Цифры сами расставляются как «900 000-00-00» — так номер
   читается и сверяется взглядом, а не по одной цифре.

   Приставка — часть поля, а не отдельный кусок слева: иначе она отрывается
   при переносе на телефоне, и остаётся поле, начинающееся ниоткуда.

   Поведение включает kit.js по признаку data-телефон: без скрипта поле
   остаётся обычным и работает как раньше — просто без подстановки. */

  .tel{position:relative;display:flex;align-items:center;width:100%;}

  /* Приставка. Не кнопка и не подпись: по ней не нажимают, её читают. */
  .tel .код{position:absolute;left:var(--sp-3);color:var(--text);
        font-size:var(--fs-base);font-variant-numeric:tabular-nums;
        pointer-events:none;user-select:none;}

  /* Отступ слева считается от ширины приставки, а не подобран числом:
     сменится кегль — поле поедет следом. */
  .tel input{width:100%;height:var(--ctl);
        padding-left:calc(var(--sp-3) * 2 + 2.1em);
        padding-right:var(--sp-3);
        border:1px solid var(--control-line);border-radius:var(--radius-sm);
        background:var(--surface);color:var(--text);font:inherit;
        font-size:var(--fs-base);font-variant-numeric:tabular-nums;
        letter-spacing:.02em;outline:none;}
  .tel input::placeholder{color:var(--muted);letter-spacing:normal;}
  .tel input:focus{border-color:var(--accent);
        box-shadow:0 0 0 3px var(--accent-soft);}
  .tel input:disabled{background:var(--surface-2);color:var(--muted);
        cursor:not-allowed;}

  /* Незаполненный номер: пока цифр меньше десяти, поле не ругается — человек
     ещё печатает. Признак ставит kit.js, когда поле теряет фокус. */
  .tel.мало input{border-color:var(--bad);}
  .tel .беда{display:none;color:var(--bad);font-size:var(--fs-xs);
        margin-top:var(--sp-1);}
  .tel.мало .беда{display:block;}

  /* Мелкое: в строке таблицы, в шапке фильтра */
  .tel.мелкое input{height:var(--ctl-sm);font-size:var(--fs-sm);}

  /* На телефоне кегль поля не опускаем ниже шестнадцати точек: мобильный
     Safari увеличивает страницу при касании поля с текстом мельче, и вернуть
     масштаб можно только руками. Это требование платформы, не наш выбор. */
  @media (max-width:620px){
    .tel input, .tel.мелкое input{font-size:var(--fs-touch);}
  }

  /* ── ползунок.css ─────────────────────────────────────── */
/* ── Ползунок ── */
  .rng{width:100%;height:20px;appearance:none;background:transparent;cursor:pointer;}
  .rng::-webkit-slider-runnable-track{height:5px;border-radius:var(--pill);background:var(--surface-3);}
  .rng::-moz-range-track{height:5px;border-radius:var(--pill);background:var(--surface-3);}
  /* Бегунок центрируем по дорожке расчётом, а не подобранным числом: поменяется
     высота дорожки или размер бегунка — смещение пересчитается само. */
  .rng::-webkit-slider-thumb{appearance:none;width:16px;height:16px;border-radius:50%;
       background:var(--accent);border:2px solid var(--surface);box-shadow:var(--shadow-sm);
       margin-top:calc((5px - 16px) / 2);}
  .rng::-moz-range-thumb{width:16px;height:16px;border-radius:50%;background:var(--accent);
       border:2px solid var(--surface);box-shadow:var(--shadow-sm);}
  .rng:focus-visible::-webkit-slider-thumb{box-shadow:0 0 0 3px color-mix(in oklab, var(--accent) 25%, transparent);}
  .rng-row{display:flex;align-items:center;gap:var(--sp-3);}
  .rng-row .val{min-width:56px;text-align:right;font-variant-numeric:tabular-nums;
       font-size:var(--fs-sm);color:var(--text-2);}

  /* ── полосы.css ─────────────────────────────────────── */
/* Полоса заполнения отвечает на вопрос «сколько уже», а не «сколько всего»:
   устройства 2 из 3, трафик 18 ГБ из 50, место на диске. Число рядом с ней
   обязательно — сама по себе полоса не говорит, много это или мало. */

  .bar{height:5px;border-radius:var(--pill);background:var(--surface-3);
       overflow:hidden;min-width:70px;}
  .bar > i{display:block;height:100%;border-radius:var(--pill);background:var(--accent);
           transition:width .25s;}
  .bar.warn > i{background:var(--warn);}
  .bar.bad > i{background:var(--bad);}
  .bar.good > i{background:var(--good);}
  .bar.tall{height:8px;}

  /* Ход дела с подписью: рассылка, импорт, выгрузка */
  .prog{display:flex;flex-direction:column;gap:var(--sp-1);}
  .prog .top{display:flex;align-items:baseline;gap:var(--sp-2);font-size:var(--fs-sm);}
  .prog .top b{font-weight:600;}
  .prog .top span{margin-left:auto;color:var(--muted);font-size:var(--fs-xs);
                  font-variant-numeric:tabular-nums;}

  /* Заглушки на время загрузки.
     Раньше пустой экран с крутилкой читался как «ничего нет»: человек уходил
     раньше, чем приходили данные. Заглушка показывает будущую форму списка,
     и ожидание перестаёт выглядеть поломкой. */
  .skel{background:linear-gradient(90deg,
        var(--surface-2) 25%, var(--surface-3) 37%, var(--surface-2) 63%);
        background-size:400% 100%;border-radius:var(--radius-xs);
        animation:skel 1.3s ease-in-out infinite;}
  .skel-line{height:11px;margin:var(--sp-1) 0;}
  .skel-line.w40{width:40%;} .skel-line.w60{width:60%;} .skel-line.w80{width:80%;}
  .skel-box{height:var(--box-sm);width:var(--box-sm);border-radius:var(--box-sm-r);flex:none;}
  .skel-row{display:flex;align-items:center;gap:var(--sp-3);padding:var(--sp-3) var(--sp-3);
            border-bottom:1px solid var(--border);}
  .skel-row > div{flex:1;min-width:0;}
  @keyframes skel{0%{background-position:100% 50%;}100%{background-position:0 50%;}}
  /* Тем, кто просил меньше движения, мигание ни к чему */
  @media (prefers-reduced-motion:reduce){.skel{animation:none;}}

  /* ── поля.css ─────────────────────────────────────── */
/* Раньше поле было только полем: рамка и текст внутри. Всё остальное —
   метку, пояснение, сообщение об ошибке — каждый экран рисовал по-своему,
   и они разъезжались по отступам и кеглю.

   Здесь поле собрано целиком, одной сущностью:

     .fld           обёртка: метка, поле, пояснение под ним
     .fld > label   название; ссылка справа встаёт сама (например «Забыл пароль?»)
     .fld .hint     пояснение — что писать и почему
     .fld .err      ошибка; поле при этом краснеет рамкой

   Ошибку показываем ПОД полем, а не всплывающей подсказкой: всплывающая
   исчезает при первом же касании, и человек снова не знает, что не так. */

  .fld{display:flex;flex-direction:column;gap:var(--sp-2);}
  .fld > label{display:flex;align-items:center;gap:var(--sp-2);font-size:var(--fs-sm);
               font-weight:550;line-height:1;color:var(--text);}
  .fld > label .req{color:var(--bad);font-weight:700;}
  .fld > label a,.fld > label .rt{margin-left:auto;font-weight:400;font-size:var(--fs-xs);
                                  color:var(--muted);text-decoration:none;}
  .fld > label a:hover{color:var(--accent);}
  .fld .hint{color:var(--muted);font-size:var(--fs-xs);line-height:1.45;}
  .fld .err{color:var(--bad);font-size:var(--fs-xs);line-height:1.45;
            display:flex;align-items:flex-start;gap:var(--sp-1);}
  .fld .err svg{width:var(--ic-sm);height:var(--ic-sm);flex:none;margin-top:1px;
                stroke:currentColor;fill:none;stroke-width:1.9;}

  /* Поле с ошибкой: краснеет рамка, а не весь блок. Красный фон под текстом
     ввода мешает читать то, что человек печатает. */
  .fld.bad .field,.field.bad{border-color:var(--bad);}
  .fld.bad .field:focus-visible,.field.bad:focus-visible{
      box-shadow:0 0 0 3px color-mix(in oklab, var(--bad) 30%, transparent);}

  /* Поле со значком или единицей измерения: «₽», «ГБ», «дней» */
  .fld .with{position:relative;display:flex;align-items:center;}
  .fld .with .field{padding-right:var(--sp-6);}
  .fld .with .unit{position:absolute;right:var(--sp-3);color:var(--muted);
                   font-size:var(--fs-sm);pointer-events:none;}
  .fld .with .lead{position:absolute;left:var(--sp-3);color:var(--muted);
                   font-size:var(--fs-sm);pointer-events:none;}
  .fld .with .lead ~ .field{padding-left:var(--sp-6);}

  /* Ряд полей: «с … по …», «имя и фамилия». На узком экране складывается. */
  .fld-row{display:flex;gap:var(--sp-3);}
  .fld-row > .fld{flex:1;min-width:0;}
  @media (max-width:560px){ .fld-row{flex-direction:column;} }

  /* Группа полей внутри карточки: отступ между полями один на всю панель */
  .fld-group{display:flex;flex-direction:column;gap:var(--sp-5);}

  /* ── Поле пароля: точки и кнопка показа ──────────────────────────── */
  /* Пароль скрыт по умолчанию — за спиной бывают люди. Но вслепую его
     набирают с ошибкой, и человек трижды получает «не подошёл», не понимая,
     где промахнулся: в раскладке, в регистре или в лишнем пробеле. Особенно
     на телефоне, где длинный пароль набирают руками.

     Кнопка стоит внутри поля, а не рядом: стоящая рядом читается как
     отдельное действие формы, и её нажимают вместо «Войти».

     Саму кнопку рисует поведение (static/kit.js) по признаку у обёртки. Не
     подключили поведение — остаётся обычное поле с точками: форма работает,
     просто без показа. Поэтому обёртку можно ставить в любую форму — и на
     входе, и в смене пароля, и у ключей оплаты в админке. */
  .пароль{position:relative;display:block;width:100%;}
  /* Место под кнопку: без отступа длинный пароль уезжает под неё, и конец
     строки не виден как раз тогда, когда его и хотели посмотреть. */
  /* box-sizing здесь обязателен: без него отступ под кнопку прибавляется к
     ширине, поле становится шире своей обёртки и вылезает за край карточки.
     Общий сброс есть не во всяком проекте — набор не должен на него
     рассчитывать. */
  .пароль > .field{box-sizing:border-box;width:100%;
        padding-right:calc(var(--ctl-sm) + var(--sp-2));}
  .пароль > .глазок{position:absolute;top:50%;right:var(--sp-1);
        transform:translateY(-50%);
        /* Мельче поля, чтобы сидеть внутри рамки, но не мельче нормы цели:
           в кнопку тоньше этого не попадают пальцем. */
        width:var(--ctl-sm);height:var(--ctl-sm);flex:none;padding:0;
        display:grid;place-items:center;border:none;background:transparent;
        color:var(--muted);border-radius:var(--radius-sm);cursor:pointer;}
  .пароль > .глазок:hover{background:var(--surface-2);color:var(--text);}
  .пароль > .глазок:focus-visible{outline:2px solid var(--accent);
        outline-offset:-2px;}
  .пароль > .глазок svg{width:var(--ic);height:var(--ic);stroke:currentColor;
        fill:none;stroke-width:1.8;stroke-linecap:round;stroke-linejoin:round;}
  /* Открытый пароль помечаем цветом: иначе человек уходит со страницы, не
     заметив, что оставил его на виду. */
  .пароль.видно > .глазок{color:var(--accent);}

  /* ── разделяемые-полосы.css ─────────────────────────────────────── */
/* ── Разделяемые полосы: список слева, карточка справа.
     Ручку делаем шириной в четыре точки, а хватать позволяем за восемь:
     тонкую линию мышью не поймать. ── */
  .split{display:flex;min-height:0;flex:1;}
  .split > .pane{min-width:0;overflow:auto;}
  .split > .grip{flex:none;width:4px;background:var(--border);cursor:col-resize;
       position:relative;}
  .split > .grip::after{content:"";position:absolute;inset:0 -4px;}
  .split > .grip:hover,.split > .grip.on{background:var(--accent);}
  @media (max-width:900px){
    /* На узком экране полосы встают друг под друга, ручка не нужна */
    .split{flex-direction:column;}
    .split > .grip{display:none;}
  }

  /* ── раскрывашка.css ─────────────────────────────────────── */
/* Раскрывашка прячет то, что нужно не всем и не всегда: редкие настройки,
   подробности платежа, ответы на вопросы. Прятать за ней главное нельзя —
   человек не найдёт.

   Собрана на обычных details и summary: браузер сам умеет открывать и
   закрывать, и это работает даже когда скрипты не загрузились. */

  .acc{border:1px solid var(--border);border-radius:var(--radius-sm);background:var(--surface);
       overflow:hidden;}
  .acc + .acc{margin-top:var(--sp-2);}
  .acc > summary{display:flex;align-items:center;gap:var(--sp-2);padding:var(--sp-3);
       cursor:pointer;font-size:var(--fs-base);font-weight:550;list-style:none;
       transition:background .12s;}
  .acc > summary::-webkit-details-marker{display:none;}
  .acc > summary:hover{background:var(--surface-2);}
  .acc > summary .sub{margin-left:auto;color:var(--muted);font-size:var(--fs-xs);font-weight:400;}
  /* Уголок поворачивается — единственная подсказка, что блок раскрывается */
  .acc > summary::after{content:"";width:8px;height:8px;flex:none;margin-left:var(--sp-1);
       border-right:1.6px solid var(--muted);border-bottom:1.6px solid var(--muted);
       transform:translateY(-2px) rotate(45deg);transition:transform .15s;}
  .acc[open] > summary::after{transform:translateY(2px) rotate(-135deg);}
  .acc .body{padding:0 var(--sp-3) var(--sp-3);color:var(--text-2);font-size:var(--fs-sm);
       line-height:1.55;}
  .acc[open] > summary{border-bottom:1px solid var(--border);}

  

  /* ── свойства.css ─────────────────────────────────────── */
/* Список «свойство — значение», лента событий, карточка с действием.
   Часть набора элементов. Собирается сборщиком:
   python tools/build_styles.py */

/* Три вещи, без которых не обходится ни одна страница разбора: карточка
   заказа, страница клиента, свойства товара. До сих пор каждый экран рисовал
   их по-своему, отсюда и разнобой в админке.

     .свойства  — что и чему равно: телефон, адрес, способ оплаты;
     .история   — что происходило и когда: заказ принят, собран, выдан;
     .действие  — карточка с одним делом: выгрузить, удалить, перевыпустить. */

/* ── Свойство и значение ─────────────────────────────────────────── */
  .свойства{display:grid;gap:var(--sp-1) var(--sp-4);
        grid-template-columns:max-content minmax(0, 1fr);align-items:baseline;}
  .свойства dt, .свойства .имя{color:var(--muted);font-size:var(--fs-sm);}
  .свойства dd, .свойства .знач{margin:0;color:var(--text);font-size:var(--fs-sm);
        overflow-wrap:anywhere;}
  /* Числа и суммы — табличными цифрами: в столбце они должны стоять ровно,
     иначе взгляд не сравнивает, а спотыкается. */
  .свойства .знач.num{font-variant-numeric:tabular-nums;}
  .свойства a{color:var(--accent);text-decoration:none;}
  .свойства a:hover{text-decoration:underline;}

  /* Широкий вид: значение под свойством. Нужен в узкой колонке — там пара в
     строку сжимается так, что адрес переносится по букве. */
  .свойства.столбиком{grid-template-columns:1fr;gap:var(--sp-3);}
  .свойства.столбиком dt, .свойства.столбиком .имя{font-size:var(--fs-xs);}

  @media (max-width:560px){
    .свойства{grid-template-columns:1fr;gap:var(--sp-3);}
    .свойства dt, .свойства .имя{font-size:var(--fs-xs);}
  }

/* ── Лента событий ───────────────────────────────────────────────── */
  /* Что произошло и когда. Линия слева связывает события в одну нить: без
     неё это просто список строк, и не видно, что одно следует за другим. */
  .история{display:flex;flex-direction:column;gap:var(--sp-4);
        position:relative;}
  .история .шаг{display:grid;grid-template-columns:var(--ctl-sm) minmax(0, 1fr);
        gap:var(--sp-3);position:relative;}
  .история .шаг .точка{width:var(--ctl-sm);height:var(--ctl-sm);flex:none;
        border-radius:var(--pill);background:var(--surface-2);
        color:var(--muted);display:grid;place-items:center;z-index:1;}
  .история .шаг .точка svg{width:var(--ic-sm);height:var(--ic-sm);
        stroke:currentColor;fill:none;stroke-width:2;
        stroke-linecap:round;stroke-linejoin:round;}
  /* Нить между точками. Рисуем на самом шаге, а не общей линией на ленте:
     тогда у последнего события хвост не свисает в пустоту. */
  .история .шаг:not(:last-child)::before{content:'';position:absolute;
        left:calc(var(--ctl-sm) / 2);top:var(--ctl-sm);
        bottom:calc(var(--sp-4) * -1);width:1px;background:var(--border);}
  .история .шаг b{display:block;font-size:var(--fs-sm);font-weight:600;}
  .история .шаг span{color:var(--muted);font-size:var(--fs-sm);line-height:1.5;}
  .история .шаг time{display:block;color:var(--faint);font-size:var(--fs-xs);
        margin-top:2px;font-variant-numeric:tabular-nums;}
  /* Случившееся — цветом «хорошо», случай беды — красным. Сейчас идущее
     помечаем акцентом: взгляд возвращается именно сюда. */
  .история .шаг.готово .точка{background:var(--good-bg);color:var(--good);}
  .история .шаг.сейчас .точка{background:var(--accent-soft);color:var(--accent);}
  .история .шаг.беда .точка{background:var(--bad-bg);color:var(--bad);}

/* ── Карточка с действием ────────────────────────────────────────── */
  /* Одно дело, объяснение и кнопка. Отдельной карточкой, а не строкой в
     настройках: действие, которое трудно отменить, обязано стоять особняком,
     иначе на него нажимают мимоходом. */
  .действие{display:flex;gap:var(--sp-4);align-items:flex-start;
        padding:var(--sp-4);background:var(--surface);
        border:1px solid var(--border);border-radius:var(--radius-lg);}
  .действие .тело{flex:1;min-width:0;display:flex;flex-direction:column;
        gap:var(--sp-1);}
  .действие b{font-size:var(--fs-md);}
  .действие p{color:var(--muted);font-size:var(--fs-sm);line-height:1.55;
        margin:0;}
  .действие .btn{flex:none;align-self:center;}
  /* Опасное действие: рамка и кнопка красные, но фон обычный. Красная
     заливка целой карточки кричит на человека каждый раз, когда он просто
     листает настройки. */
  .действие.опасно{border-color:var(--bad);}
  .действие.опасно b{color:var(--bad);}

  @media (max-width:640px){
    /* На телефоне кнопка уходит под текст и растягивается: сбоку она
       ужимается до пары букв. */
    .действие{flex-direction:column;}
    .действие .btn{width:100%;align-self:stretch;}
  }

  /* ── сегменты.css ─────────────────────────────────────── */
/* Тумблер отвечает на «включено или нет». Когда вариантов больше двух —
   период, режим, вид меню, — тумблеров ставят несколько, и человек читает
   их как независимые, хотя выбрать можно только один. Для такого случая
   сегменты: варианты стоят рядом в одной рамке, выбранный залит акцентом.

   Высота та же, что у всех кнопок, — третьей высоты в панели не бывает. */

  .seg{display:inline-flex;align-items:center;padding:2px;gap:2px;
       background:var(--surface-2);border:1px solid var(--control-line);
       border-radius:var(--radius-sm);}
  .seg > button{height:calc(var(--ctl) - 6px);min-width:74px;padding:0 var(--sp-3);border:none;
                background:transparent;color:var(--text-2);font:inherit;font-size:var(--fs-sm);
                font-weight:600;cursor:pointer;border-radius:var(--radius-xs);white-space:nowrap;
                transition:background .13s,color .13s;}
  .seg > button:hover{color:var(--text);}
  .seg > button.on{background:var(--accent);color:var(--on-accent);}

  /* Тихий вид: выбранный не заливается цветом, а поднимается белой плашкой.
     Нужен там, где переключатель стоит рядом с цветными кнопками и спорит
     с ними за внимание, — в шапке боковой панели, над списком разделов.
     Цветной выбор там читается как действие, хотя это всего лишь вид. */
  .seg.тихие{background:var(--surface-2);}
  .seg.тихие > button.on{background:var(--surface);color:var(--text);
       box-shadow:var(--shadow-sm);}
  /* Растянуть на всю ширину — например в окне на телефоне */
  .seg.wide{display:flex;width:100%;}
  .seg.wide > button{flex:1;min-width:0;}

  /* Мелкий ряд: фильтры над списком, где сегменты не должны спорить с данными */
  .seg.sm > button{height:calc(var(--ctl-sm) - 6px);min-width:0;padding:0 var(--sp-2);
                   font-size:var(--fs-xs);}

  /* Кнопка-выключатель: одиночная, залипающая. Годится для «Показать только
     активных» — когда состояние важнее названия действия. */
  .toggle{height:var(--ctl);padding:0 var(--sp-3);display:inline-flex;align-items:center;gap:var(--sp-2);
          border:1px solid var(--control-line);border-radius:var(--radius-sm);
          background:var(--surface);color:var(--text-2);font:inherit;font-size:var(--fs-sm);
          font-weight:600;cursor:pointer;white-space:nowrap;
          transition:background .13s,border-color .13s,color .13s;}
  .toggle:hover{background:var(--surface-2);color:var(--text);}
  .toggle.on{background:var(--accent-soft);border-color:var(--accent-line);color:var(--accent);}
  .toggle .dot-state{background:currentColor;}

  /* ── соотношение.css ─────────────────────────────────────── */
/* ── Соотношение сторон: под картинку товара, обложку, предпросмотр. ── */
  .ratio{position:relative;width:100%;overflow:hidden;border-radius:var(--radius-sm);
       background:var(--surface-2);}
  .ratio::before{content:"";display:block;padding-top:56.25%;}   /* 16 к 9 */
  .ratio.sq::before{padding-top:100%;}                            /* квадрат */
  .ratio.card::before{padding-top:75%;}                           /* 4 к 3 */
  .ratio > *{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;}

  

  /* ── список-выбора.css ─────────────────────────────────────── */
/* ── Выпадающий список ── */
  .sel{position:relative;display:inline-flex;align-items:center;width:100%;}
  .sel > select{appearance:none;width:100%;height:var(--ctl);
       border:1px solid var(--control-line);border-radius:var(--radius-sm);
       background:var(--surface);color:var(--text);font:inherit;font-size:var(--fs-base);
       padding:0 var(--sp-6) 0 var(--sp-3);cursor:pointer;box-shadow:var(--shadow-sm);
       transition:border-color .15s,box-shadow .15s;}
  .sel > select:focus-visible{outline:none;border-color:var(--accent);
       box-shadow:0 0 0 3px color-mix(in oklab, var(--accent) 22%, transparent);}
  /* Стрелку рисуем сами: системная у каждого браузера своя, и рядом с нашими
     полями она выглядит чужой. */
  .sel::after{content:"";position:absolute;right:var(--sp-3);width:9px;height:9px;
       border-right:1.6px solid var(--muted);border-bottom:1.6px solid var(--muted);
       transform:translateY(-2px) rotate(45deg);pointer-events:none;}
  .sel.sm > select{height:var(--ctl-sm);font-size:var(--fs-sm);}

  

  /* ── строка-списка.css ─────────────────────────────────────── */
/* ── Строка-элемент. Основа списков: значок или аватарка слева, название и
     пояснение, действие справа. На ней собраны списки клиентов, устройств,
     оплат — поэтому она одна на всё. ── */
  .item{display:flex;align-items:center;gap:var(--sp-3);padding:var(--sp-2) var(--sp-3);
       border-radius:var(--radius-sm);}
  .item + .item{border-top:1px solid var(--border);border-radius:0;}
  .item:hover{background:var(--surface-2);}
  .item .txt{min-width:0;flex:1;}
  .item .txt b{display:block;font-weight:600;font-size:var(--fs-base);
       overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
  .item .txt small{display:block;color:var(--muted);font-size:var(--fs-xs);
       overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
  .item .end{margin-left:auto;flex:none;display:flex;align-items:center;gap:var(--sp-2);}

/* ── Строка клиента: аватарка, имя, пояснение, метка справа ────────
     Раньше жила в разделе панели, а он в другие проекты не едет: каталог
     показывал строку, которой в магазине нет вовсе — имя, счётчик и метка
     слипались в одну строчку без отступов. Место такой строки здесь: списки
     людей нужны и панели, и магазину. */

/* ── Строка клиента: аватарка, имя, пояснение, метка справа ────────
     Раньше жила в разделе панели, а он в другие проекты не едет: каталог
     показывал строку, которой в магазине нет, — имя и метка слипались в
     одну строчку. Место такой строки здесь: списки людей нужны и панели,
     и магазину. */
.crow:hover .pinbtn,.crow .pinbtn.on,.crow .pinbtn:focus-visible{opacity:1;}
  .crow .pinbtn:hover{color:var(--text-2);background:var(--surface-3);}
  .crow .pinbtn.on{color:var(--accent);}
  .crow .pinbtn svg{width:14px;height:14px;fill:none;stroke:currentColor;stroke-width:1.7;stroke-linecap:round;stroke-linejoin:round;}
  .crow .pinbtn.on svg{fill:var(--accent-soft);}

  /* ── счётчик.css ─────────────────────────────────────── */
/* ── Счётчик: число устройств, дней, штук.
     Кнопки по краям, число в середине; печатать тоже можно — иначе с сотни
     до единицы придётся щёлкать девяносто девять раз. ── */
  .count{display:inline-flex;align-items:stretch;}
  .count button{width:var(--ctl);flex:none;border:1px solid var(--control-line);
       background:var(--surface);color:var(--text-2);font:inherit;font-size:var(--fs-lg);
       cursor:pointer;display:grid;place-items:center;}
  .count button:first-child{border-radius:var(--radius-sm) 0 0 var(--radius-sm);}
  .count button:last-child{border-radius:0 var(--radius-sm) var(--radius-sm) 0;}
  .count button:hover{background:var(--surface-2);color:var(--text);}
  .count button:disabled{opacity:.45;cursor:default;}
  .count input{width:calc(var(--ctl) * 1.6);text-align:center;
       border:1px solid var(--control-line);
       border-left:none;border-right:none;background:var(--surface);color:var(--text);
       font:inherit;font-size:var(--fs-base);font-variant-numeric:tabular-nums;
       min-width:0;height:var(--ctl);}
  .count input:focus-visible{outline:none;border-color:var(--accent);z-index:1;}
  /* Поле числа браузер рисует со своими стрелками-прокруткой. Рядом уже стоят
     наши «−» и «+», и вторая пара органов управления в одном поле выглядит
     поломкой: в корзине человек видел прокрутку вместо ровного счётчика.
     Прячем их, а разметке оставляем свободу писать type="number" — она нужна
     для проверки ввода и для клавиатуры на телефоне. */
  .count input[type="number"]{-moz-appearance:textfield;appearance:textfield;}
  .count input[type="number"]::-webkit-outer-spin-button,
  .count input[type="number"]::-webkit-inner-spin-button{
       -webkit-appearance:none;appearance:none;margin:0;}

  /* ── таблицы.css ─────────────────────────────────────── */
/* Таблиц в панели до сих пор не было: списки собирались строками-карточками.
   Для клиентов это верно, а вот оплаты, промокоды и журнал действий — это
   столбцы, которые хочется сравнивать глазами по вертикали.

   Главное правило здесь — телефон. Прокрутка вбок формально работает, но
   правый край с кнопкой действия уезжает за экран, и владелец её не находит.
   Поэтому на узком экране строки складываются в карточки, а подпись столбца
   берётся из data-подпись у ячейки. */

  .tbl{width:100%;border-collapse:collapse;font-size:var(--fs-sm);}
  .tbl th{text-align:left;padding:var(--sp-2) var(--sp-3);white-space:nowrap;
          background:var(--surface-2);border-bottom:1px solid var(--border);
          font-size:var(--fs-2xs);text-transform:uppercase;letter-spacing:.07em;
          font-weight:650;color:var(--faint);}
  .tbl td{padding:var(--sp-2) var(--sp-3);border-bottom:1px solid var(--border);vertical-align:middle;}
  .tbl tr:last-child td{border-bottom:none;}
  .tbl tbody tr:hover{background:var(--surface-2);}
  .tbl .num{text-align:right;font-variant-numeric:tabular-nums;}
  .tbl .act{text-align:right;white-space:nowrap;}

  /* Сортируемая шапка: стрелка появляется у столбца, по которому отсортировано */
  .tbl th.sortable{cursor:pointer;user-select:none;}
  .tbl th.sortable:hover{color:var(--text-2);}
  .tbl th.sortable::after{content:"";display:inline-block;width:0;height:0;margin-left:var(--sp-1);
                          vertical-align:1px;border-left:4px solid transparent;
                          border-right:4px solid transparent;opacity:0;}
  .tbl th.sorted-up::after{border-bottom:5px solid currentColor;opacity:.8;}
  .tbl th.sorted-down::after{border-top:5px solid currentColor;opacity:.8;}

  /* Плотный вид: когда строк много и важнее охват, а не воздух */
  .tbl.dense td{padding:var(--sp-1) var(--sp-3);}
  .tbl.dense th{padding:var(--sp-1) var(--sp-3);}

  /* Постраничность. Отдельно от таблицы: тем же набором пользуются списки. */
  .pager{display:flex;align-items:center;gap:var(--sp-1);padding:var(--sp-2) var(--sp-3);
         border-top:1px solid var(--border);}
  .pager .info{color:var(--muted);font-size:var(--fs-xs);margin-right:auto;}
  .pager button{height:var(--ctl-sm);min-width:var(--ctl-sm);padding:0 var(--sp-2);
                border:1px solid var(--control-line);border-radius:var(--radius-xs);
                background:var(--surface);color:var(--text-2);font:inherit;
                font-size:var(--fs-xs);font-weight:600;cursor:pointer;
                font-variant-numeric:tabular-nums;}
  .pager button:hover:not(:disabled){background:var(--surface-2);color:var(--text);}
  .pager button.on{background:var(--accent);border-color:var(--accent);color:var(--on-accent);}
  .pager button:disabled{opacity:.45;cursor:default;}

  @media (max-width:720px){
    /* Строка становится карточкой: подписи столбцов переезжают в ячейки */
    .tbl,.tbl tbody,.tbl tr,.tbl td{display:block;width:100%;}
    .tbl thead{display:none;}
    .tbl tr{border:1px solid var(--border);border-radius:var(--radius-sm);
            margin-bottom:var(--sp-2);background:var(--surface);overflow:hidden;}
    .tbl td{display:flex;align-items:center;gap:var(--sp-2);border-bottom:1px solid var(--border);
            padding:var(--sp-2) var(--sp-3);}
    .tbl td:last-child{border-bottom:none;}
    .tbl td::before{content:attr(data-подпись);flex:none;min-width:104px;
                    font-size:var(--fs-2xs);text-transform:uppercase;letter-spacing:.07em;
                    font-weight:650;color:var(--faint);}
    .tbl td:empty{display:none;}
    .tbl .num,.tbl .act{text-align:left;}
    .tbl .act .btn{width:100%;}
    .pager{flex-wrap:wrap;}
    .pager .info{width:100%;margin-bottom:var(--sp-1);}
  }

  /* ── текст.css ─────────────────────────────────────── */
/* ── Типографика ──
   Нужна там, где текст пишет человек, а не интерфейс: правовые документы,
   описание тарифа, ответ поддержки. Без неё каждый такой кусок набирается
   по-своему и выглядит вставкой из другого сервиса. */

  .text{color:var(--text-2);font-size:var(--fs-base);line-height:1.65;}
  .text > * + *{margin-top:var(--sp-3);}
  .text h1,.text h2,.text h3{color:var(--text);font-weight:650;letter-spacing:-.01em;
       line-height:1.3;}
  .text h1{font-size:var(--fs-2xl);}
  .text h2{font-size:var(--fs-lg);margin-top:var(--sp-5);}
  .text h3{font-size:var(--fs-md);margin-top:var(--sp-4);}
  .text ul,.text ol{padding-left:var(--sp-5);}
  .text li + li{margin-top:var(--sp-1);}
  .text a{color:var(--accent);text-decoration:underline;text-underline-offset:3px;}
  .text code{font-family:var(--mono);font-size:var(--fs-sm);background:var(--surface-2);
       border-radius:var(--radius-xs);padding:1px var(--sp-1);}
  .text blockquote{border-left:2px solid var(--accent-line);padding-left:var(--sp-3);
       color:var(--muted);}
  .text hr{height:1px;background:var(--border);border:none;margin:var(--sp-5) 0;}
  .text table{width:100%;border-collapse:collapse;font-size:var(--fs-sm);}
  .text th,.text td{text-align:left;padding:var(--sp-2);border-bottom:1px solid var(--border);}
  .text img{max-width:100%;border-radius:var(--radius-sm);}
  /* Строка ширины: длиннее восьмидесяти знаков глаз теряет начало строки */
  .text.narrow{max-width:72ch;}

  /* ── флажки.css ─────────────────────────────────────── */
/* ── Флажок и круглый переключатель ──
     Рисуем свои: системные не красятся в фирменный цвет и на телефоне
     оказываются вдвое мельче нормы для пальца. */
  .chk,.rad{display:inline-flex;align-items:flex-start;gap:var(--sp-2);cursor:pointer;
       font-size:var(--fs-base);line-height:1.45;position:relative;}
  .chk > input,.rad > input{position:absolute;opacity:0;width:0;height:0;}
  /* Размер квадрата — из ряда значков: восемнадцать точек были подобраны
     на глаз и не менялись вместе с кеглем, из-за чего на крупном тексте
     флажок выглядел мелким. */
  .chk .box,.rad .box{width:var(--ic);height:var(--ic);flex:none;margin-top:1px;
       border:1.6px solid var(--control-line);background:var(--surface);
       display:grid;place-items:center;transition:background .13s,border-color .13s;}
  .chk .box{border-radius:var(--radius-xs);}
  .rad .box{border-radius:50%;}
  .chk .box svg{width:var(--ic-sm);height:var(--ic-sm);stroke:var(--on-accent);fill:none;stroke-width:2.6;
       stroke-linecap:round;stroke-linejoin:round;opacity:0;transition:opacity .1s;}
  .rad .box i{width:8px;height:8px;border-radius:50%;background:var(--on-accent);
       transform:scale(0);transition:transform .12s;}
  .chk > input:checked ~ .box,.rad > input:checked ~ .box{background:var(--accent);border-color:var(--accent);}
  .chk > input:checked ~ .box svg{opacity:1;}
  .rad > input:checked ~ .box i{transform:scale(1);}
  .chk > input:focus-visible ~ .box,.rad > input:focus-visible ~ .box{
       box-shadow:0 0 0 3px color-mix(in oklab, var(--accent) 25%, transparent);}
  .chk > input:disabled ~ .box,.rad > input:disabled ~ .box{opacity:.5;}
  .chk .sub,.rad .sub{display:block;color:var(--muted);font-size:var(--fs-xs);margin-top:2px;}
  /* Цель для пальца: сам квадратик 18 точек, площадка добирает до нормы */
  .chk::after,.rad::after{content:"";position:absolute;left:0;top:0;width:24px;height:24px;
       transform:translate(-3px,-2px);}

  /* Столбец вариантов: отступ один, чтобы списки не разъезжались по экранам */
  .chk-list{display:flex;flex-direction:column;gap:var(--sp-3);}

  

  /* ── чат.css ─────────────────────────────────────── */
/* Чат с покупателем: кнопка в углу и окно переписки.
   Часть набора элементов. Собирается сборщиком:
   python tools/build_styles.py */

/* Имя .чат, а не .chat: латинское занято перепиской в панели — там это лента
   сообщений внутри страницы поддержки.

   Части названы с приставкой — .чат-окно, .чат-вызов, — а не просто «окно» и
   «кнопка». Короткие имена выглядят опрятнее, но они общие: «кнопка» и «окно»
   уже есть у магазина, и его правила перебивали наши. Окно чата открывалось
   нулевого размера, кнопка теряла форму — при том, что селекторы совпадали, а
   правила существовали. Приставка снимает вопрос навсегда.

   Нужен на любом сайте, где что-то продают: половина вопросов задаётся до
   покупки, и человек не пойдёт ради них звонить. Поэтому чат сделан один
   раз и встраивается, а не рисуется заново в каждом проекте.

   Кнопка в правом нижнем углу — там её ищут, и это единственное место, где
   она не закрывает содержимое. Счётчик на кнопке нужен потому, что человек
   закрывает окно и уходит читать товар: без счётчика он не узнает, что ему
   ответили.

   На компьютере окно небольшое и висит над страницей: человек читает товар
   и спрашивает попутно, не теряя страницу из виду.

   На телефоне — во весь экран. Окно в четверть экрана там бесполезно: в
   ленте видно две реплики, поле ввода закрывает клавиатура, а промах мимо
   крестика закрывает чат вместе с набранным вопросом. Разговор на телефоне
   всегда идёт целым экраном — так устроены все мессенджеры, и ждут того же.

   Ни затемнения, ни блокировки прокрутки на компьютере: чат не окно-вопрос,
   он не требует ответа немедленно. */

  .чат{position:fixed;right:var(--sp-4);bottom:var(--sp-4);z-index:70;
        display:flex;flex-direction:column;align-items:flex-end;gap:var(--sp-2);}

  /* ── Кнопка ────────────────────────────────────────────────────── */
  .чат-вызов{position:relative;width:var(--чат-кн);height:var(--чат-кн);
        border:none;border-radius:var(--pill);background:var(--accent);
        color:var(--on-accent);cursor:pointer;display:grid;place-items:center;
        box-shadow:var(--shadow-lg);}
  .чат-вызов svg{width:var(--ic);height:var(--ic);stroke:currentColor;
        fill:none;stroke-width:1.8;stroke-linecap:round;stroke-linejoin:round;}
  .чат-вызов:hover{background:var(--accent-hover);}

  /* Счётчик непрочитанных: человек закрыл окно и ушёл читать товар. */
  .чат-счёт{position:absolute;top:-2px;right:-2px;
        min-width:var(--счёт);height:var(--счёт);padding:0 var(--sp-1);
        border-radius:var(--pill);background:var(--bad);color:#fff;
        font-size:var(--fs-счёт);font-weight:700;display:grid;
        place-items:center;line-height:1;}

  /* ── Окно ──────────────────────────────────────────────────────── */
  .чат-окно{width:min(var(--чат-w), calc(100vw - var(--sp-5) * 2));
        height:min(var(--чат-h), calc(100dvh - var(--sp-6) * 4));
        background:var(--surface);border:1px solid var(--border);
        border-radius:var(--radius-lg);box-shadow:var(--shadow-lg);
        display:grid;grid-template-rows:auto 1fr auto;overflow:hidden;}
  /* Скрытое окно обязано быть скрыто: display сильнее атрибута hidden. */
  .чат-окно[hidden]{display:none;}

  .чат-шапка{display:flex;align-items:center;justify-content:space-between;
        gap:var(--sp-3);padding:var(--sp-3) var(--sp-4);
        border-bottom:1px solid var(--border);}
  .чат-шапка b{font-size:var(--fs-md);}
  .чат-шапка .подпись{color:var(--muted);font-size:var(--fs-xs);}
  .чат-закрыть{width:var(--цель);height:var(--цель);border:none;background:none;
        color:var(--muted);cursor:pointer;display:grid;place-items:center;
        border-radius:var(--radius-xs);flex:none;}
  .чат-закрыть:hover{background:var(--surface-2);color:var(--text);}
  .чат-закрыть svg{width:var(--ic-sm);height:var(--ic-sm);stroke:currentColor;
        fill:none;stroke-width:2;stroke-linecap:round;}

  /* Лента реплик */
  .чат-лента{overflow-y:auto;padding:var(--sp-4);display:grid;
        gap:var(--sp-2);align-content:start;}
  .чат-реплика{max-width:85%;padding:var(--sp-2) var(--sp-3);
        border-radius:var(--radius);font-size:var(--fs-sm);line-height:1.45;
        /* Длинная ссылка без пробелов иначе растягивает окно вбок. */
        overflow-wrap:anywhere;white-space:pre-wrap;}
  /* Ответ магазина слева и приглушённый, вопрос покупателя справа акцентом —
     как в любом мессенджере: своё справа, чужое слева. Переучивать человека
     ради оригинальности незачем. */
  .чат-реплика.магазин{justify-self:start;background:var(--surface-2);
        color:var(--text);}
  /* Реплика гостя — своё сообщение: справа и акцентом. Класс зовётся
     «гость», а не «свой»: с той стороны экрана сидит покупатель, и в
     переписке магазина он гость. */
  /* Мягкий акцент и обычный текст — ровно как своё сообщение в переписке
     панели (.bubble.mine). Раньше здесь была сплошная заливка акцентом с
     белым текстом: два блока про одно и то же — разговор — выглядели как из
     разных наборов, а на цветном фоне длинное сообщение читается хуже. */
  .чат-реплика.гость, .чат-реплика.свой{justify-self:end;
        background:var(--accent-soft);color:var(--text);}
  .чат-время{font-size:var(--fs-2xs);color:var(--faint);}
  .чат-реплика.гость + .время, .чат-реплика.свой + .время{justify-self:end;}

  /* Пустая лента: чат открыли и не знают, с чего начать */
  .чат-пусто{display:flex;flex-direction:column;gap:var(--sp-2);
        color:var(--muted);font-size:var(--fs-sm);text-align:center;
        padding:var(--sp-5) var(--sp-3);}

  /* Ввод */
  /* Кнопка держится низа поля: пока поле в одну строку, разницы нет, а когда
     разрастётся — «Отправить» обязана остаться у последней строки, а не
     уехать вверх и повиснуть над пустотой. */
  .чат-форма{display:grid;grid-template-columns:minmax(0, 1fr) auto;
        align-items:end;gap:var(--sp-2);padding:var(--sp-3);
        border-top:1px solid var(--border);}
  /* Поле начинается с одной строки и растёт по мере ввода — как в
     мессенджерах. Заранее высокое поле занимает пол-окна переписки ради
     сообщения из трёх слов, а именно такие тут и пишут.
     Рост даёт поведение набора (kit.js, признак data-рост). Не подключили —
     остаётся поле в одну строку с прокруткой внутри: писать можно. */
  /* box-sizing задаём сами: общий сброс есть не во всяком проекте, а без него
     отступы прибавляются к высоте — пустое поле выходит вдвое выше кнопки
     рядом, хотя строка в нём одна. */
  .чат-форма textarea{box-sizing:border-box;min-height:var(--ctl);
        max-height:calc(var(--ctl) * 4);resize:none;overflow-y:auto;
        /* Полосу прокрутки прячем: поле растёт само, и полоса появляется
           только у самых длинных сообщений — но там она перечёркивает поле
           поперёк и выглядит поломкой. Прокрутка при этом работает: колесом,
           пальцем и стрелками. */
        scrollbar-width:none;}
  .чат-форма textarea::-webkit-scrollbar{width:0;height:0;}

  /* ── Телефон: во весь экран ────────────────────────────────────── */
  @media (max-width:620px){
    .чат{right:0;bottom:0;left:0;align-items:flex-end;padding:var(--sp-3);
          padding-bottom:calc(var(--sp-3) + env(safe-area-inset-bottom));
          pointer-events:none;}
    .чат > *{pointer-events:auto;}

    /* Окно занимает экран целиком: разговор на телефоне идёт так везде.
       Скруглены только верхние углы — низ упирается в край экрана. */
    .чат-окно{position:fixed;inset:0;width:100%;height:100dvh;
          max-width:none;border:none;border-radius:0;
          padding-bottom:env(safe-area-inset-bottom);}
    .чат-шапка{padding-top:calc(var(--sp-3) + env(safe-area-inset-top));}
    /* Пока чат открыт, кнопка не нужна: она оказалась бы поверх переписки. */
    .чат:has(.чат-окно:not([hidden])) > .кнопка{display:none;}
  }

  /* ── шаги.css ─────────────────────────────────────── */
/* ── Шаги: мастер установки, подключение устройства ──
     Показывают, сколько всего и где человек сейчас. Без этого длинная
     установка ощущается бесконечной, и её бросают на середине. */
  .steps{display:flex;align-items:flex-start;gap:var(--sp-2);}
  .step{flex:1;min-width:0;display:flex;flex-direction:column;gap:var(--sp-2);}
  .step .top{display:flex;align-items:center;gap:var(--sp-2);}
  .step .num{width:24px;height:24px;border-radius:50%;flex:none;display:grid;place-items:center;
       font-size:var(--fs-xs);font-weight:700;background:var(--surface-3);color:var(--muted);
       font-variant-numeric:tabular-nums;}
  .step .line{flex:1;height:2px;border-radius:var(--pill);background:var(--surface-3);}
  .step .name{font-size:var(--fs-sm);font-weight:550;color:var(--muted);}
  .step .sub{font-size:var(--fs-xs);color:var(--faint);}
  /* Пройденный шаг: галочка и залитая линия */
  .step.done .num{background:var(--good);color:#fff;}
  .step.done .line{background:var(--good);}
  .step.done .name{color:var(--text-2);}
  /* Текущий: акцентом, чтобы взгляд возвращался именно сюда */
  .step.now .num{background:var(--accent);color:var(--on-accent);}
  .step.now .name{color:var(--text);font-weight:600;}
  @media (max-width:640px){
    /* На телефоне шаги встают столбиком: в строку они сжимаются до нечитаемого */
    .steps{flex-direction:column;gap:var(--sp-3);}
    .step .line{display:none;}
  }

  /* ── шапка-карточки.css ─────────────────────────────────────── */
/* Три вещи в одной строке: что это, пояснение и управление — переключатель
   периода, кнопка «Все клиенты», выбор столбцов. Раньше каждая карточка
   ставила их по-своему: где-то кнопка была под заголовком, где-то в углу,
   где-то отдельной строкой.

   На узком экране действие переезжает под заголовок, а не сжимает его: имя
   карточки важнее кнопки, и обрезать его нельзя. */

  .ch{display:flex;align-items:flex-start;gap:var(--sp-3);padding:var(--sp-4);
      border-bottom:1px solid var(--border);}
  .ch .txt{min-width:0;flex:1;}
  .ch h2{font-size:var(--fs-lg);font-weight:600;letter-spacing:-.01em;line-height:1.3;}
  .ch p{color:var(--muted);font-size:var(--fs-sm);margin-top:2px;}
  .ch .act{flex:none;display:flex;align-items:center;gap:var(--sp-2);}
  /* Без черты: когда за шапкой идёт не таблица, а свободное содержимое */
  .ch.plain{border-bottom:none;padding-bottom:0;}

  @media (max-width:720px){
    .ch{flex-direction:column;}
    .ch .act{width:100%;}
    .ch .act > *{flex:1;}
  }

  /* ── Переключатель периода ──
     На широком экране — ряд кнопок: все сроки видно сразу, выбор в одно
     нажатие. На узком тот же выбор становится списком: три кнопки по краю
     экрана либо сжимаются до нечитаемых, либо уезжают за край. */
  .period{display:inline-flex;}
  .period > select{display:none;}
  @media (max-width:900px){
    .period > .seg{display:none;}
    .period > select{display:block;height:var(--ctl-sm);border:1px solid var(--control-line);
      border-radius:var(--radius-sm);background:var(--surface);color:var(--text);
      font:inherit;font-size:var(--fs-sm);padding:0 var(--sp-3);width:100%;}
  }

  /* ── шапка.css ─────────────────────────────────────── */
/* ═══ ВЕРХНЯЯ ПАНЕЛЬ ════════════════════════════════════════════════ */
  .topbar{display:flex;align-items:center;gap:var(--sp-2);height:54px;flex-shrink:0;padding:0 var(--sp-3) 0 var(--sp-3);border-bottom:1px solid var(--border);background:var(--surface);}
  .burger{width:var(--ctl);height:var(--ctl);border-radius:var(--radius-sm);border:1px solid var(--control-line);background:transparent;color:var(--text-2);cursor:pointer;display:grid;place-items:center;transition:background .13s;}
  .burger:hover{background:var(--surface-2);}
  :root[data-layout="compact"] .burger{display:none;}  /* в компактной раскладке прятать нечего */
  .burger svg{width:var(--ic);height:var(--ic);stroke:currentColor;stroke-width:2;fill:none;stroke-linecap:round;}
  .brand{display:inline-flex;align-items:center;gap:var(--sp-2);font-weight:660;font-size:var(--fs-lg);
    letter-spacing:-.01em;flex:none;cursor:pointer;padding:2px var(--sp-3) 2px 2px;
    border:1px solid var(--control-line);border-radius:var(--radius-sm);background:transparent;
    transition:background .13s,border-color .13s;}
  .brand,.brand:hover{text-decoration:none;color:var(--text);}
  .brand:hover{background:var(--surface-2);}
  .brand .mark{width:var(--box-sm);height:var(--box-sm);border-radius:var(--box-sm-r);flex:none;background:var(--accent);color:var(--on-accent);display:grid;place-items:center;font-size:var(--fs-base);font-weight:800;overflow:hidden;}
  .brand .mark img{width:100%;height:100%;object-fit:cover;display:block;}


  /* Плотность: те же экраны, но воздуха больше или меньше. Меняем только
     отступы и высоту строк — расположение и размеры кнопок не трогаем. */
  :root[data-density="compact"]{--sp-3:9px; --sp-4:12px; --sp-5:15px; --sp-6:20px; --row-pad:6px;}
  :root[data-density="normal"]{--row-pad:9px;}
  :root[data-density="cozy"]{--sp-3:15px; --sp-4:20px; --sp-5:26px; --sp-6:34px; --row-pad:13px;}
  :root[data-density="compact"] .crow{padding:var(--sp-1) var(--sp-3) var(--sp-1) var(--sp-3);}
  :root[data-density="cozy"] .crow{padding:var(--sp-3) var(--sp-4) var(--sp-3) var(--sp-3);}
  :root[data-density="compact"] .card-body{padding-top:var(--sp-2);padding-bottom:var(--sp-2);}
  :root[data-density="cozy"] .card-body{padding-top:var(--sp-4);padding-bottom:var(--sp-4);}
  :root[data-density="compact"] .row{padding-top:var(--sp-2);padding-bottom:var(--sp-2);}
  :root[data-density="cozy"] .row{padding-top:var(--sp-3);padding-bottom:var(--sp-3);}

  /* Скругления: от строгих углов до телеграмных «капель» */
  :root[data-round="sharp"]{--radius:4px; --radius-sm:3px; --radius-xs:2px;}
  :root[data-round="round"]{--radius:18px; --radius-sm:14px; --radius-xs:11px;}

  /* Обои: узор Telegram за карточками. Рисуем сами, без картинок извне. */
  body{background-attachment:fixed;background-size:420px;}
  :root[data-paper="dots"] body{background-image:radial-gradient(currentColor 1.1px,transparent 1.2px);
    background-size:22px 22px;color:var(--wp);}
  :root[data-paper="rhomb"] body{background-image:
    repeating-linear-gradient(45deg,var(--wp) 0 1px,transparent 1px 26px),
    repeating-linear-gradient(-45deg,var(--wp) 0 1px,transparent 1px 26px);background-size:auto;}
  :root[data-paper="waves"] body{background-image:
    repeating-radial-gradient(circle at 0 0,transparent 0 18px,var(--wp) 18px 19px);background-size:auto;}
  :root{--wp:rgba(0,0,0,.05);}
  :root[data-theme="dark"],:root[data-theme="night"]{--wp:rgba(255,255,255,.045);}

  /* Аватарки в цветах Telegram: цвет берём по id, чтобы он не «прыгал» */
  .av.tgc,.av-sm.tgc,.dhead .av.tgc{color:#fff;background:var(--avc);}
  .crow.sel .av.tgc{background:var(--avc);color:#fff;}


  /* Экран блокировки: панель закрыта, но данные под ним остаются */
  .lockscr{position:fixed;inset:0;z-index:200;display:none;align-items:center;justify-content:center;
    background:var(--bg);}
  .lockscr.show{display:flex;}
  /* До того как скрипт разобрался: панель скрыта, замок показан. Признак
     ставится в самом начале страницы, поэтому промежутка «видно панель»
     не остаётся вовсе. */
  html[data-locked] .shell{visibility:hidden;}
  html[data-locked] .lockscr{display:flex;}
  .lockscr .in{width:min(320px,92vw);text-align:center;}
  .lockscr .mark{width:54px;height:54px;border-radius:16px;background:var(--accent);
    color:var(--on-accent);display:grid;place-items:center;font-size:var(--fs-3xl);font-weight:700;
    margin:0 auto var(--sp-3);}
  .lockscr h2{margin:0 0 var(--sp-1);font-size:var(--fs-xl);font-weight:640;}
  .lockscr p{margin:0 0 var(--sp-4);color:var(--muted);font-size:var(--fs-sm);}
  .lockscr input{width:100%;height:44px;text-align:center;letter-spacing:.4em;font-size:var(--fs-2xl);
    font-weight:600;border:1px solid var(--control-line);border-radius:var(--radius-sm);
    background:var(--surface);color:var(--text);outline:none;}
  .lockscr input:focus{border-color:var(--accent);}
  .lockscr .err{color:var(--accent);font-size:var(--fs-sm);margin-top:var(--sp-2);min-height:16px;}

  /* Колокольчик: счётчик непрочитанного прямо на кнопке */
  .bell{position:relative;}
  .bell .cnt{position:absolute;top:2px;right:2px;min-width:15px;height:15px;padding:0 var(--sp-1);
    border-radius:var(--pill);background:var(--accent);color:var(--on-accent);font-size:var(--fs-2xs);
    font-weight:700;display:none;align-items:center;justify-content:center;line-height:1;}
  .bell.has .cnt{display:flex;}

  /* Лента дел */
  .inbox{display:flex;flex-direction:column;gap:var(--sp-2);}
  .ibx{display:flex;gap:var(--sp-2);align-items:flex-start;padding:var(--sp-2) var(--sp-3);border:1px solid var(--border);
    border-radius:var(--radius-sm);background:var(--surface);cursor:pointer;text-align:left;
    width:100%;font:inherit;color:var(--text);transition:border-color .12s,background .12s;}
  .ibx:hover{background:var(--surface-2);border-color:var(--accent-line);}
  .ibx .t{flex:1;min-width:0;}
  .ibx b{display:block;font-size:var(--fs-base);font-weight:600;}
  .ibx small{display:block;color:var(--muted);font-size:var(--fs-sm);margin-top:2px;
    overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
  .ibx .when{flex:none;color:var(--faint);font-size:var(--fs-xs);white-space:nowrap;margin-top:2px;}

  /* Подвал: одна тихая строка под любым экраном — как на сайтах */
  .foot-bar{flex:none;display:flex;align-items:center;gap:var(--sp-2);flex-wrap:wrap;
    padding:var(--sp-2) var(--sp-5);border-top:1px solid var(--border);background:var(--surface);
    color:var(--faint);font-size:var(--fs-xs);}
  .foot-bar a,.foot-bar button{color:var(--muted);background:none;border:none;font:inherit;
    font-size:var(--fs-xs);text-decoration:none;cursor:pointer;padding:0;}
  .foot-bar a:hover,.foot-bar button:hover{color:var(--accent);}
  .foot-bar .sp{margin-left:auto;}

  /* ── Зона нажатия у мелких элементов ──────────────────────────────
     Кружок «?», тумблер и ссылки подвала нарисованы мелкими намеренно: они
     не должны спорить с содержимым. Но пальцем в цель меньше 24 точек
     попасть трудно (норма WCAG 2.5.8), а промах по «?» на телефоне выглядит
     как «подсказка не открывается».

     Поэтому растим не сам элемент, а невидимую площадку поверх него:
     абсолютный псевдоэлемент в раскладке не участвует, соседи не сдвигаются,
     вид не меняется ни на точку. Числа здесь — не размеры элементов, а
     добор до нормы, поэтому они и не вынесены в токены. */
  /* Кружок подсказки и переключатель добирают цель касания в своих файлах
     (карточки.css и кнопки.css): правило про элемент должно лежать там же,
     где сам элемент, иначе один класс оказывается заведён в двух местах и
     они начинают спорить размерами. */
  .foot-bar a,.foot-bar button{position:relative;}
  .foot-bar a::after,.foot-bar button::after{content:"";position:absolute;inset:-4px -2px;}
  .foot-bar .dot{width:6px;height:6px;border-radius:50%;background:var(--accent);display:none;}
  .foot-bar .led{width:7px;height:7px;border-radius:50%;background:var(--off,var(--muted));
    display:inline-block;margin-right:var(--sp-1);vertical-align:middle;}
  .foot-bar .led.on{background:var(--good);box-shadow:0 0 0 3px color-mix(in srgb,var(--good) 22%,transparent);}
  .foot-bar .led.bad{background:var(--accent);box-shadow:0 0 0 3px color-mix(in srgb,var(--accent) 22%,transparent);}
  .foot-bar.fresh .dot{display:inline-block;}
  /* Подвал на телефоне: оставляем то, ради чего в него смотрят — состояние
     туннеля и версию. Время сервера и нагрузка уезжают: они длинные и на
     узком экране просто рвут строку. */
  @media (max-width:720px){
    .foot-bar{padding:var(--sp-2) var(--sp-3);gap:var(--sp-2);font-size:var(--fs-xs);}
    .foot-bar #foot-srv,.foot-bar #foot-load{display:none;}
    .foot-bar .sp{margin-left:auto;}
  }
  @media (max-width:400px){
    .foot-bar{gap:var(--sp-2);}
    .foot-bar #foot-brand{display:none;}          /* название и так в шапке */
  }

  /* Версия панели внизу меню: тихая строка, с точкой — если есть непрочитанное */
  .ver{margin-top:auto;display:flex;align-items:center;gap:var(--sp-2);width:100%;background:none;border:none;
    cursor:pointer;color:var(--side-muted);font:inherit;font-size:var(--fs-xs);padding:var(--sp-2);
    border-radius:var(--radius-xs);transition:color .12s,background .12s;}
  .ver:hover{color:var(--side-text);background:var(--side-active);}
  .ver .dot{width:6px;height:6px;border-radius:50%;background:var(--accent);flex:none;display:none;}
  .ver.fresh .dot{display:block;}
  :root[data-layout="compact"] .ver{margin:0 0 0 auto;width:auto;}

  /* Список изменений */
  .rel{border-top:1px solid var(--border-soft);padding:var(--sp-3) 0 2px;}
  .rel:first-child{border-top:none;padding-top:0;}
  .rel h4{margin:0 0 2px;font-size:var(--fs-md);font-weight:640;display:flex;align-items:center;gap:var(--sp-2);}
  .rel .when{color:var(--faint);font-size:var(--fs-xs);font-weight:500;}
  .rel ul{margin:var(--sp-2) 0 0;padding:0;list-style:none;display:flex;flex-direction:column;gap:var(--sp-2);}
  .rel li{display:flex;gap:var(--sp-2);align-items:flex-start;font-size:var(--fs-base);line-height:1.45;}
  .rel li .tag{flex:none;margin-top:1px;}

  /* Второй уровень меню: разделы, которые раньше висели в сайдбаре.
     Держим их наверху страницы — так видно, где ты находишься. */
  .subnav{display:flex;gap:var(--sp-1);margin:0 0 var(--sp-4);border-bottom:1px solid var(--border);
    padding-bottom:0;overflow-x:auto;scrollbar-width:none;-webkit-overflow-scrolling:touch;
    overscroll-behavior:contain;touch-action:pan-x;overflow-y:hidden;
    scroll-snap-type:x proximity;}
  .subnav::-webkit-scrollbar{display:none;}
  .subnav button{flex:none;scroll-snap-align:start;white-space:nowrap;}
  .subnav button{appearance:none;background:none;border:none;font:inherit;font-size:var(--fs-md);
    font-weight:550;color:var(--muted);padding:var(--sp-2) var(--sp-3) var(--sp-2);cursor:pointer;position:relative;
    border-radius:var(--radius-xs) var(--radius-xs) 0 0;transition:color .12s,background .12s;}
  .subnav button:hover{color:var(--text);background:var(--surface-2);}
  .subnav button.on{color:var(--accent);}
  .subnav button.on::after{content:"";position:absolute;left:10px;right:10px;bottom:-1px;height:2px;
    background:var(--accent);border-radius:2px;}

  /* Состав кабинета: строки с порядком и тумблером */
  .ord-list{display:flex;flex-direction:column;gap:var(--sp-1);width:100%;max-width:420px;}
  .ord-row{display:flex;align-items:center;gap:var(--sp-2);padding:var(--sp-2) var(--sp-2);border:1px solid var(--border);
    border-radius:var(--radius-sm);background:var(--surface);}
  .ord-row.off{opacity:.5;}
  .ord-row .n{width:18px;text-align:center;color:var(--faint);font-size:var(--fs-xs);
    font-variant-numeric:tabular-nums;}
  .ord-row b{flex:1;font-size:var(--fs-base);font-weight:550;}
  /* Где блок окажется в кабинете. Без этой строки состав приходилось держать
     в голове: список плоский, а кабинет разложен по разделам. */
  .ord-row b small{display:block;color:var(--faint);font-size:var(--fs-xs);font-weight:400;}

  /* Оформление: выбор темы и цвета акцента */
  .sw-list{display:flex;flex-wrap:wrap;gap:var(--sp-2);}
  .sw-dot{width:34px;height:34px;border-radius:var(--radius-sm);border:2px solid transparent;
    cursor:pointer;padding:0;position:relative;transition:transform .12s,border-color .12s;}
  .sw-dot:hover{transform:scale(1.06);}
  .sw-dot.on{border-color:var(--text);}
  .sw-dot.on::after{content:'';position:absolute;inset:0;margin:auto;width:9px;height:9px;
    border-radius:50%;background:#fff;box-shadow:0 0 0 1px rgba(0,0,0,.18);}
  .th-list{display:flex;gap:var(--sp-2);flex-wrap:wrap;}
  .th-card{border:2px solid var(--border);border-radius:var(--radius);padding:0;cursor:pointer;
    background:transparent;overflow:hidden;width:132px;text-align:left;transition:border-color .12s;}
  .th-card.on{border-color:var(--accent);}
  .th-prev{height:64px;padding:var(--sp-2);display:flex;flex-direction:column;gap:var(--sp-1);justify-content:flex-end;}
  .th-prev i{display:block;height:7px;border-radius:4px;}
  .th-name{font-size:var(--fs-sm);font-weight:600;padding:var(--sp-2) var(--sp-2);border-top:1px solid var(--border-soft);
    color:var(--text);background:var(--surface);}
  /* куда бы ни встал логотип (шапка, аватарки, предпросмотр) — вписываем в рамку,
     иначе картинка идёт натуральным размером и растягивает страницу */
  /* логотип ещё грузится: место держим, содержимое прячем — так ничего не прыгает */
  .logo-wait{color:transparent!important;}
  .logo-wait>*{visibility:hidden;}
  .has-logo{overflow:hidden;padding:0;}
  /* картинка повторяет скругление контейнера и «прижимается» к пикселям —
     иначе в мобильном Safari по краю остаётся тонкий заусенец */
  .has-logo img{width:100%;height:100%;object-fit:cover;display:block;border-radius:inherit;
    transform:translateZ(0);backface-visibility:hidden;}
  /* предпросмотр логотипа в личном кабинете */
  .logo-prev{width:44px;height:44px;border-radius:var(--radius-sm);flex:none;overflow:hidden;display:grid;place-items:center;background:var(--accent);color:var(--on-accent);font-size:var(--fs-2xl);font-weight:800;}
  .logo-prev img{width:100%;height:100%;object-fit:cover;display:block;}
  .logo-prev .mark{width:100%;height:100%;border-radius:0;font-size:var(--fs-2xl);}
  .spacer{flex:1;}

  .gsearch{position:relative;flex:1;max-width:400px;margin-left:var(--sp-1);}
  .gsearch>svg{position:absolute;left:10px;top:50%;transform:translateY(-50%);width:var(--ic);height:var(--ic);stroke:var(--faint);stroke-width:2;fill:none;}
  .gsearch input{width:100%;height:var(--ctl);padding:0 var(--sp-6) 0 var(--sp-6);font:inherit;font-size:var(--fs-base);background:var(--surface-2);color:var(--text);border:1px solid transparent;border-radius:var(--radius-sm);transition:border-color .13s,background .13s;}
  .gsearch input::placeholder{color:var(--faint);}
  .gsearch input:focus{outline:none;background:var(--surface);border-color:var(--accent);}
  .gsearch kbd{position:absolute;right:8px;top:50%;transform:translateY(-50%);font:inherit;font-size:var(--fs-2xs);color:var(--faint);background:var(--surface-3);border-radius:5px;padding:2px var(--sp-1);pointer-events:none;}

  .tbtn{height:var(--ctl);padding:0 var(--sp-3);border-radius:var(--radius-sm);border:1px solid var(--control-line);background:transparent;color:var(--text-2);font:inherit;font-size:var(--fs-sm);cursor:pointer;text-decoration:none;display:inline-flex;align-items:center;gap:var(--sp-2);white-space:nowrap;transition:background .13s,color .13s,border-color .13s;}
  .tbtn:hover{background:var(--surface-2);color:var(--text);}
  .tbtn svg{width:var(--ic);height:var(--ic);stroke:currentColor;stroke-width:2;fill:none;stroke-linecap:round;stroke-linejoin:round;}
  /* кнопки-иконки в шапке: квадратные, одинаковые */
  .tbtn.ic-btn{width:32px;padding:0;justify-content:center;gap:var(--sp-1);}
  .tbtn.ic-btn#lang{width:auto;padding:0 var(--sp-2);}
  .tbtn.ic-btn .lb{font-size:var(--fs-xs);font-weight:700;letter-spacing:.04em;}
  .tbtn.ic-btn:hover{border-color:var(--accent-line);color:var(--text);}

  .body{display:flex;flex:1;min-height:0;}

  /* ── шторка-боковая.css ─────────────────────────────────────── */
/* ── Боковая шторка ── */
  .sheet{position:fixed;z-index:71;top:0;right:0;bottom:0;width:min(420px, 92vw);
       background:var(--surface);border-left:1px solid var(--border);
       box-shadow:var(--shadow-lg);display:flex;flex-direction:column;
       transform:translateX(100%);transition:transform .18s ease;}
  .sheet.open{transform:none;}
  .sheet.left{right:auto;left:0;border-left:none;border-right:1px solid var(--border);
       transform:translateX(-100%);}
  .sheet.left.open{transform:none;}

  

  /* ── шторка-нижняя.css ─────────────────────────────────────── */
/* ── Нижняя шторка: на телефоне ── */
  .drawer{position:fixed;z-index:71;left:0;right:0;bottom:0;max-height:88dvh;
       background:var(--surface);border-top:1px solid var(--border);
       border-radius:var(--radius) var(--radius) 0 0;box-shadow:var(--shadow-lg);
       display:flex;flex-direction:column;transform:translateY(100%);
       transition:transform .18s ease;
       padding-bottom:env(safe-area-inset-bottom);}
  .drawer.open{transform:none;}
  /* Полоска сверху: за неё тянут пальцем, и по ней видно, что окно съезжает вниз */
  .drawer::before{content:"";width:36px;height:4px;border-radius:var(--pill);
       background:var(--control-line);margin:var(--sp-2) auto 0;flex:none;}

  /* Общая начинка шторок */
  .sheet-head,.drawer-head{display:flex;align-items:center;gap:var(--sp-3);flex:none;
       padding:var(--sp-3) var(--sp-4);border-bottom:1px solid var(--border);}
  .sheet-head h2,.drawer-head h2{font-size:var(--fs-lg);font-weight:600;letter-spacing:-.01em;}
  .sheet-head p,.drawer-head p{color:var(--muted);font-size:var(--fs-xs);margin-top:2px;}
  .sheet-head .x,.drawer-head .x{margin-left:auto;width:var(--ctl-sm);height:var(--ctl-sm);
       border:none;background:none;border-radius:var(--radius-xs);color:var(--muted);
       cursor:pointer;display:grid;place-items:center;flex:none;}
  .sheet-head .x:hover,.drawer-head .x:hover{background:var(--surface-2);color:var(--text);}
  .sheet-body,.drawer-body{flex:1;min-height:0;overflow-y:auto;padding:var(--sp-4);}
  .sheet-foot,.drawer-foot{flex:none;display:flex;gap:var(--sp-2);justify-content:flex-end;
       padding:var(--sp-3) var(--sp-4);border-top:1px solid var(--border);
       background:var(--surface);}
  /* На телефоне кнопки в подвале шторки — во всю ширину и в один столбец:
     тянуться большим пальцем к правому нижнему углу неудобно. */
  @media (max-width:560px){
    .sheet-foot,.drawer-foot{flex-direction:column-reverse;}
    .sheet-foot .btn,.drawer-foot .btn{width:100%;}
  }

  

  /* ── блок-видео.css ─────────────────────────────────────── */
/* Секции «Видео», «Лента с прокруткой» и «Специальные предложения».
   Часть набора элементов. Собирается сборщиком:
   python tools/build_styles.py */

/* Три секции, которые ставят на главную чаще прочих.

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

/* ── Видео ───────────────────────────────────────────────────────── */
  .видео{position:relative;overflow:hidden;border-radius:var(--radius-lg);
        background:var(--surface-3);aspect-ratio:16 / 9;}
  .видео img, .видео iframe{position:absolute;inset:0;width:100%;height:100%;
        object-fit:cover;border:none;display:block;}
  /* Кнопка воспроизведения по центру. Крупная нарочно: по ней целятся
     пальцем на телефоне, а промах открывает ссылку под обложкой. */
  .видео .играть{position:absolute;inset:0;display:grid;place-items:center;
        border:none;background:transparent;cursor:pointer;padding:0;
        color:#fff;}
  .видео .играть .круг{width:var(--лицо);height:var(--лицо);
        border-radius:var(--pill);display:grid;place-items:center;
        background:rgba(0,0,0,.55);backdrop-filter:blur(2px);}
  .видео .играть svg{width:var(--box-lg);height:var(--box-lg);
        fill:currentColor;stroke:none;}
  .видео .играть:hover .круг{background:rgba(0,0,0,.72);}
  /* Подпись поверх обложки: о чём ролик и сколько идёт. */
  .видео .подпись{position:absolute;left:0;right:0;bottom:0;
        padding:var(--sp-4);color:#fff;
        background:linear-gradient(to top, rgba(0,0,0,.65), transparent);}
  .видео .подпись b{display:block;font-size:var(--fs-lg);}
  .видео .подпись span{font-size:var(--fs-sm);opacity:.85;}

/* ── Лента карточек с прокруткой ─────────────────────────────────── */
  .лента-карточек{display:flex;gap:var(--sp-4);overflow-x:auto;
        scroll-snap-type:x mandatory;padding-bottom:var(--sp-2);
        scrollbar-width:thin;}
  .лента-карточек > *{flex:none;width:var(--сетка-мин);scroll-snap-align:start;}
  /* Прокрутка пальцем — на телефоне, стрелками — на компьютере: там колесо
     листает страницу, а не ленту, и без стрелок ряд кажется обрезанным. */
  .лента-обёртка{position:relative;}
  .лента-обёртка .стрелка{position:absolute;top:calc(50% - var(--ctl));
        width:var(--ctl);height:var(--ctl);border-radius:var(--pill);
        border:1px solid var(--border);background:var(--surface);
        color:var(--text-2);display:grid;place-items:center;cursor:pointer;
        box-shadow:var(--shadow-md);z-index:2;}
  .лента-обёртка .стрелка svg{width:var(--ic);height:var(--ic);
        stroke:currentColor;fill:none;stroke-width:2;
        stroke-linecap:round;stroke-linejoin:round;}
  .лента-обёртка .стрелка.влево{left:calc(var(--sp-3) * -1);}
  .лента-обёртка .стрелка.вправо{right:calc(var(--sp-3) * -1);}
  .лента-обёртка .стрелка:hover{background:var(--surface-2);color:var(--text);}
  @media (max-width:760px){
    /* На телефоне листают пальцем, стрелки только занимают место. */
    .лента-обёртка .стрелка{display:none;}
  }

/* ── Специальные предложения ─────────────────────────────────────── */
  .спец{display:grid;gap:var(--sp-4);
        grid-template-columns:repeat(auto-fit, minmax(260px, 1fr));}
  .спец .предложение{position:relative;display:flex;flex-direction:column;
        gap:var(--sp-2);padding:var(--sp-5);border-radius:var(--radius-lg);
        background:var(--surface-2);border:1px solid var(--border);
        overflow:hidden;}
  .спец .предложение b{font-size:var(--fs-xl);letter-spacing:-.01em;
        line-height:1.25;}
  .спец .предложение p{color:var(--muted);font-size:var(--fs-sm);
        line-height:1.55;margin:0;}
  .спец .предложение .btn{align-self:flex-start;margin-top:var(--sp-2);}
  /* Срок — единственное, что заставляет решать сейчас. Показываем его
     отдельно и приглушённо: крикливый таймер читается как обман. */
  .спец .предложение .срок{color:var(--faint);font-size:var(--fs-xs);
        display:flex;align-items:center;gap:var(--sp-1);}
  .спец .предложение .срок svg{width:var(--ic-sm);height:var(--ic-sm);
        stroke:currentColor;fill:none;stroke-width:1.8;}
  /* Главное предложение — акцентной подложкой, на всю ширину ряда. */
  .спец .предложение.главное{grid-column:1 / -1;
        background:var(--accent-soft);border-color:var(--accent-line, var(--border));}
  .спец .предложение .метка{position:absolute;top:var(--sp-3);right:var(--sp-3);
        padding:0 var(--sp-2);min-height:var(--ctl-sm);display:inline-flex;
        align-items:center;border-radius:var(--pill);background:var(--accent);
        color:var(--on-accent);font-size:var(--fs-2xs);font-weight:700;}

  /* ── блок-возможности.css ─────────────────────────────────────── */
/* Три-четыре пункта, каждый — значок, название и одна фраза. Больше шести
   никто не читает: список превращается в стену, и человек пролистывает мимо.

   Пишем не свойства, а пользу: не «AES-256», а «Никто не видит, что ты
   открываешь». Свойство говорит инженеру, польза — покупателю. */

  .feat{padding:var(--sp-6) var(--sp-5);background:var(--surface);}
  .feat .in{max-width:var(--shop-w);margin:0 auto;}
  .feat .шапка{text-align:center;max-width:var(--чит-w);margin:0 auto var(--sp-6);
       display:flex;flex-direction:column;gap:var(--sp-2);}
  .feat h2{font-size:var(--fs-2xl);font-weight:650;letter-spacing:-.02em;}
  .feat .шапка p{color:var(--muted);font-size:var(--fs-md);line-height:1.6;}

  .feat .сетка{display:grid;gap:var(--sp-5);
       grid-template-columns:repeat(auto-fit,minmax(240px,1fr));}
  .feat .пункт{display:flex;flex-direction:column;gap:var(--sp-2);}
  .feat .пункт .зн{width:var(--box);height:var(--box);border-radius:var(--box-r);
       background:var(--accent-soft);color:var(--accent);display:grid;place-items:center;
       margin-bottom:var(--sp-1);}
  .feat .пункт .зн svg{width:var(--ic);height:var(--ic);stroke:currentColor;fill:none;
       stroke-width:1.8;stroke-linecap:round;stroke-linejoin:round;}
  .feat .пункт b{font-size:var(--fs-md);font-weight:600;}
  .feat .пункт p{color:var(--muted);font-size:var(--fs-sm);line-height:1.6;}

  /* Вид карточками: когда пунктов много и нужна граница между ними */
  .feat.cards .пункт{padding:var(--sp-4);border:1px solid var(--border);
       border-radius:var(--radius-lg);background:var(--surface);box-shadow:var(--shadow-sm);}

  /* Вид строками: значок слева, текст справа — для узких колонок */
  .feat.rows .сетка{grid-template-columns:1fr;gap:var(--sp-4);}
  .feat.rows .пункт{flex-direction:row;gap:var(--sp-3);align-items:flex-start;}
  .feat.rows .пункт .зн{margin-bottom:0;flex:none;}

  @media (max-width:620px){
    .feat{padding:var(--sp-5) var(--sp-4);}
    .feat h2{font-size:var(--fs-xl);}
  }

  /* Номер вместо значка: блок «как заказать», где порядок несёт смысл и
     человек читает пункты подряд. Тот же квадрат, только с цифрой — иначе
     шаги пришлось бы верстать отдельным блоком ради одной подмены. */
  .feat .пункт .зн.номер{background:var(--accent);color:var(--on-accent);
        font-size:var(--fs-md);font-weight:700;}

  /* ── блок-вопросы.css ─────────────────────────────────────── */
/* ── Вопросы и ответы ──
   Отвечают на то, из-за чего не покупают: «а если не подойдёт», «сколько
   идёт доставка», «а деньги вернут». Вопросы пишем словами покупателя, а не
   отдела: «Когда придёт заказ», а не «Сроки исполнения обязательств».

   Первый вопрос открыт: закрытый список читается как декорация, и его
   пролистывают, не поняв, что там ответы. */

  .faq{padding:var(--sp-6) var(--sp-5);background:var(--surface);}
  .faq .in{max-width:var(--узк-w);margin:0 auto;}
  .faq h2{font-size:var(--fs-2xl);font-weight:650;letter-spacing:-.02em;
       text-align:center;margin-bottom:var(--sp-2);}
  .faq .под{color:var(--muted);font-size:var(--fs-md);text-align:center;
       margin-bottom:var(--sp-6);}
  .faq .список{display:flex;flex-direction:column;gap:var(--sp-2);}
  /* Сам вопрос — это раскрывашка из набора: заводить второй такой же
     элемент незачем, разница была бы только в отступах. */
  .faq .acc > summary{font-size:var(--fs-md);padding:var(--sp-4);}
  .faq .acc .body{padding:0 var(--sp-4) var(--sp-4);font-size:var(--fs-base);}
  /* Строка под списком: что делать, если ответа нет */
  .faq .низ{margin-top:var(--sp-5);text-align:center;color:var(--muted);
       font-size:var(--fs-sm);}
  .faq .низ a{color:var(--accent);text-decoration:none;font-weight:550;}

/* ── Контакты ──
   Способ связи ставим первым, форму — второй: человеку, который уже
   разозлился, нужен телефон, а не поле «Тема обращения». */

  /* Выравнивание задано явно: блок ставят и в колонку с текстом по центру
     (первый экран, тарифы), и оттуда наследуется центрирование — способы
     связи выстраиваются в столбик по осевой и читаются как стихи. */
  .contacts{padding:var(--sp-6) var(--sp-5);background:var(--surface);
       text-align:left;}
  .contacts .in{max-width:var(--shop-w);margin:0 auto;display:grid;gap:var(--sp-6);
       grid-template-columns:1fr 1.2fr;align-items:start;}
  .contacts h2{font-size:var(--fs-2xl);font-weight:650;letter-spacing:-.02em;
       margin-bottom:var(--sp-2);}
  .contacts .лево > p{color:var(--muted);font-size:var(--fs-md);line-height:1.6;
       margin-bottom:var(--sp-5);}
  .contacts .способ{display:flex;align-items:center;gap:var(--sp-3);
       padding:var(--sp-3) 0;border-bottom:1px solid var(--border);}
  .contacts .способ:last-child{border-bottom:none;}
  .contacts .способ .зн{width:var(--box-sm);height:var(--box-sm);flex:none;
       border-radius:var(--box-sm-r);background:var(--accent-soft);color:var(--accent);
       display:grid;place-items:center;}
  .contacts .способ .зн svg{width:var(--ic-sm);height:var(--ic-sm);stroke:currentColor;
       fill:none;stroke-width:1.8;}
  .contacts .способ b{display:block;font-size:var(--fs-base);font-weight:550;}
  .contacts .способ small{display:block;color:var(--muted);font-size:var(--fs-xs);}
  .contacts .форма{padding:var(--sp-5);border:1px solid var(--border);
       border-radius:var(--radius-lg);background:var(--surface);box-shadow:var(--shadow-sm);
       display:flex;flex-direction:column;gap:var(--sp-4);}
  @media (max-width:860px){ .contacts .in{grid-template-columns:1fr;} }

/* ── Баннер ──
   Узкая полоса о том, что скоро кончится: скидка, срок, остаток. Закрывается
   крестиком и больше не показывается — баннер, который нельзя закрыть,
   раздражает сильнее, чем помогает. */

  .banner{display:flex;align-items:center;gap:var(--sp-3);padding:var(--sp-3) var(--sp-4);
       background:var(--accent);color:var(--on-accent);font-size:var(--fs-sm);}
  .banner b{font-weight:650;}
  .banner .act{margin-left:auto;display:flex;align-items:center;gap:var(--sp-2);}
  .banner .btn{background:var(--on-accent);color:var(--accent);border-color:transparent;}
  .banner .x{width:var(--ctl-sm);height:var(--ctl-sm);border:none;background:none;
       color:inherit;opacity:.75;cursor:pointer;border-radius:var(--radius-xs);
       display:grid;place-items:center;flex:none;}
  .banner .x:hover{opacity:1;background:rgba(255,255,255,.15);}
  /* Спокойный вид: для сообщений, которые не про деньги */
  .banner.calm{background:var(--surface-2);color:var(--text);
       border-bottom:1px solid var(--border);}
  .banner.calm .btn{background:var(--accent);color:var(--on-accent);}
  @media (max-width:620px){
    .banner{flex-wrap:wrap;}
    .banner .act{width:100%;margin-left:0;}
    .banner .act .btn{flex:1;}
  }

  /* ── блок-герой.css ─────────────────────────────────────── */
/* Первый экран отвечает на три вопроса за пять секунд: что это, кому и что
   нажать. Всё остальное — ниже. Поэтому здесь ровно один заголовок, одна
   мысль под ним и одно главное действие; вторая кнопка допустима, но только
   как «посмотреть», а не как второй равный выбор.

   Три вида под разные задачи:
     .hero          текст по центру — для сервиса, где показывать нечего;
     .hero.split    текст слева, картинка справа — для товара;
     .hero.cover    текст поверх картинки — для витрины с сильной съёмкой. */

  .hero{padding:var(--sp-6) var(--sp-5);text-align:center;background:var(--surface);}
  .hero .in{max-width:var(--узк-w);margin:0 auto;display:flex;flex-direction:column;
       align-items:center;gap:var(--sp-4);}
  .hero h1{font-size:var(--fs-3xl);font-weight:700;letter-spacing:-.03em;line-height:1.15;}
  .hero h1 em{font-style:normal;color:var(--accent);}
  .hero p{color:var(--muted);font-size:var(--fs-md);line-height:1.6;max-width:52ch;}
  .hero .кнопки{display:flex;gap:var(--sp-3);flex-wrap:wrap;justify-content:center;}
  .hero .кнопки .btn{min-width:160px;}
  /* Строка доверия под кнопками: «Уже 3 200 клиентов · Возврат 14 дней» */
  .hero .доверие{display:flex;gap:var(--sp-4);flex-wrap:wrap;justify-content:center;
       color:var(--faint);font-size:var(--fs-xs);}
  .hero .доверие span{display:inline-flex;align-items:center;gap:var(--sp-1);white-space:nowrap;}
  /* Значку обязательно задавать размер: без него он берёт свой из viewBox —
     двадцать четыре точки — и разносит строку. */
  .hero .доверие svg{width:var(--ic-sm);height:var(--ic-sm);flex:none;
       stroke:currentColor;fill:none;stroke-width:1.8;}

  /* ── Пополам: текст и картинка ── */
  .hero.split{text-align:left;}
  .hero.split .in{max-width:var(--shop-w);display:grid;grid-template-columns:1fr 1fr;
       align-items:center;gap:var(--sp-6);}
  .hero.split .кнопки,.hero.split .доверие{justify-content:flex-start;}
  .hero.split .текст{display:flex;flex-direction:column;gap:var(--sp-4);align-items:flex-start;}
  .hero.split .картинка{border-radius:var(--radius-lg);overflow:hidden;
       background:var(--surface-2);}
  .hero.split .картинка img{width:100%;height:100%;object-fit:cover;display:block;}
  @media (max-width:900px){
    /* Картинка уходит под текст: в две колонки на телефоне не помещается
       ни то, ни другое, а резать заголовок нельзя. */
    .hero.split .in{grid-template-columns:1fr;}
    .hero.split .картинка{order:-1;}
  }

  /* ── Поверх картинки ── */
  .hero.cover{position:relative;color:#fff;background:var(--text);overflow:hidden;
       padding:calc(var(--sp-6) * 2) var(--sp-5);}
  .hero.cover img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;
       opacity:.55;}
  .hero.cover .in{position:relative;}
  .hero.cover p{color:rgba(255,255,255,.85);}
  .hero.cover .доверие{color:rgba(255,255,255,.7);}

  @media (max-width:620px){
    .hero{padding:var(--sp-5) var(--sp-4);}
    .hero h1{font-size:var(--fs-2xl);}
    .hero .кнопки{width:100%;flex-direction:column;}
    .hero .кнопки .btn{width:100%;}
  }

  /* Коллаж вместо одной картинки: когда своего баннера нет, а пустое место
     рядом с текстом выглядит так, будто снимок не загрузился. Четыре товара
     квадратами — витрина показывает себя сама. */
  .hero.split .картинка.коллаж{display:grid;grid-template-columns:1fr 1fr;
       gap:var(--sp-2);background:none;}
  .hero.split .картинка.коллаж a{display:block;border-radius:var(--radius);
       overflow:hidden;background:var(--surface-2);aspect-ratio:1;}
  .hero.split .картинка.коллаж img{width:100%;height:100%;object-fit:cover;}

  /* ── блок-доставка.css ─────────────────────────────────────── */
/* Секция «Доставка и оплата».
   Часть набора элементов. Собирается сборщиком:
   python tools/build_styles.py */

/* Про доставку спрашивают до того, как положат товар в корзину, а не после.
   Пока ответа нет на странице, его спрашивают в чате — или уходят.

   Секция отвечает на три вопроса разом: как привезут, за сколько и как
   платить. Порядок именно такой: способ, срок, цена — в нём и думают.

   Два вида на выбор:
     .доставка            — карточки в ряд, для главной и страницы условий;
     .доставка.строкой    — сжатая полоса, для страницы товара под кнопкой.
   Оформление одно, разница в раскладке: на странице товара место дорого. */

  .доставка{display:grid;gap:var(--sp-4);
        grid-template-columns:repeat(auto-fit, minmax(240px, 1fr));}

  .доставка .способ{display:flex;flex-direction:column;gap:var(--sp-2);
        padding:var(--sp-4);background:var(--surface);
        border:1px solid var(--border);border-radius:var(--radius-lg);}

  /* Значок в кружке: способов обычно три-четыре, и глаз находит нужный по
     картинке быстрее, чем по заголовку. */
  .доставка .способ .знак{width:var(--box);height:var(--box);flex:none;
        border-radius:var(--box-r);background:var(--accent-soft);
        color:var(--accent);display:grid;place-items:center;}
  .доставка .способ .знак svg{width:var(--ic);height:var(--ic);
        stroke:currentColor;fill:none;stroke-width:1.8;
        stroke-linecap:round;stroke-linejoin:round;}

  .доставка .способ b{font-size:var(--fs-md);letter-spacing:-.01em;}
  .доставка .способ p{color:var(--muted);font-size:var(--fs-sm);line-height:1.55;
        margin:0;}

  /* Срок и цена — то, ради чего секцию читают. Держим их в самом низу
     карточки и одной строкой: так их сравнивают между способами, ведя
     взглядом по одной линии. */
  .доставка .способ .итог{margin-top:auto;padding-top:var(--sp-3);
        display:flex;align-items:baseline;gap:var(--sp-2);flex-wrap:wrap;
        border-top:1px solid var(--border-soft);}
  .доставка .способ .срок{font-size:var(--fs-sm);color:var(--text-2);}
  .доставка .способ .цена{margin-left:auto;font-size:var(--fs-lg);
        font-weight:700;color:var(--text);font-variant-numeric:tabular-nums;}
  /* Бесплатно — это довод, а не цена: показываем цветом «хорошо», иначе
     оно теряется среди рублей соседних способов. */
  .доставка .способ .цена.даром{color:var(--good);}

  /* Приписка под способом: «от 3 000 ₽», «кроме области» */
  .доставка .способ .мелко{color:var(--faint);font-size:var(--fs-xs);}

  /* ── Способы оплаты ──────────────────────────────────────────────── */
  /* Отдельной карточкой, а не пунктом в списке доставки: платят все, а
     доставку выбирают. */
  .доставка .оплата{display:flex;flex-wrap:wrap;gap:var(--sp-2);
        margin-top:var(--sp-2);}
  .доставка .оплата span{min-height:var(--ctl-sm);padding:0 var(--sp-3);
        display:inline-flex;align-items:center;
        border:1px solid var(--border);border-radius:var(--radius-xs);
        background:var(--surface-2);color:var(--text-2);
        font-size:var(--fs-xs);}

  /* ── Сжатый вид: под кнопкой на странице товара ──────────────────── */
  .доставка.строкой{display:flex;flex-direction:column;gap:0;
        border:1px solid var(--border);border-radius:var(--radius);
        overflow:hidden;background:var(--surface);}
  .доставка.строкой .способ{flex-direction:row;align-items:center;
        gap:var(--sp-3);padding:var(--sp-3) var(--sp-4);border:none;
        border-radius:0;border-bottom:1px solid var(--border-soft);}
  .доставка.строкой .способ:last-child{border-bottom:none;}
  .доставка.строкой .способ .знак{width:var(--ctl-sm);height:var(--ctl-sm);
        background:transparent;color:var(--muted);}
  .доставка.строкой .способ .знак svg{width:var(--ic-sm);height:var(--ic-sm);}
  .доставка.строкой .способ p,
  .доставка.строкой .способ .мелко{display:none;}
  .доставка.строкой .способ .итог{margin:0;padding:0;border:none;
        margin-left:auto;gap:var(--sp-3);}
  .доставка.строкой .способ .цена{font-size:var(--fs-sm);}

  @media (max-width:560px){
    /* На телефоне срок уходит под название: в строку они не помещаются, и
       цена наезжает на срок. */
    .доставка.строкой .способ{flex-wrap:wrap;}
    .доставка.строкой .способ .итог{width:100%;margin-left:0;
        padding-left:calc(var(--ctl-sm) + var(--sp-3));}
  }

  /* ── блок-категории.css ─────────────────────────────────────── */
/* Плитки разделов и подборок на главной.
   Часть набора элементов. Собирается сборщиком:
   python tools/build_styles.py */

/* Главная страница магазина отвечает на вопрос «что тут есть». Плитки
   разделов отвечают быстрее списка: картинка узнаётся раньше, чем читается
   слово.

   Плитки разного размера намеренно: первая крупная — раздел, который
   продаёт лучше всего. Ровная сетка одинаковых квадратов говорит «нам всё
   равно, куда ты пойдёшь».

   Число товаров в углу — не украшение: раздел с тремя товарами и раздел с
   тремя сотнями требуют разного настроя, и лучше сказать заранее. */

  .cats{display:grid;grid-template-columns:repeat(4,1fr);gap:var(--sp-3);}
  /* Первая плитка занимает две колонки и две строки: это витрина раздела,
     а не пункт списка. */
  .cats > .плитка.крупно{grid-column:span 2;grid-row:span 2;}

  .cats .плитка{position:relative;display:flex;flex-direction:column;
        justify-content:flex-end;gap:var(--sp-1);min-height:var(--плитка-h);
        padding:var(--sp-4);border-radius:var(--radius-lg);overflow:hidden;
        background:var(--surface-2);text-decoration:none;color:var(--text);
        border:1px solid var(--border);
        transition:transform .15s,box-shadow .15s;}
  .cats .плитка:hover{transform:translateY(-2px);box-shadow:var(--shadow-lg);}
  .cats .плитка img{position:absolute;inset:0;width:100%;height:100%;
        object-fit:cover;}
  /* Затемнение под текстом: без него подпись читается только на удачном фото,
     а на светлом снимке пропадает совсем. */
  .cats .плитка img + .тень{position:absolute;inset:0;
        background:linear-gradient(to top,
          rgba(0,0,0,.62) 0%, rgba(0,0,0,.15) 45%, transparent 70%);}
  .cats .плитка .назв, .cats .плитка .скок{position:relative;}
  .cats .плитка .назв{font-size:var(--fs-md);font-weight:650;
        letter-spacing:-.01em;}
  .cats .плитка.крупно .назв{font-size:var(--fs-xl);}
  .cats .плитка .скок{color:var(--muted);font-size:var(--fs-xs);}
  /* Поверх фотографии подписи белые: серый на снимке не читается */
  .cats .плитка:has(img) .назв{color:#fff;}
  .cats .плитка:has(img) .скок{color:rgba(255,255,255,.8);}

  /* Плитка без фотографии: значок вместо картинки, чтобы ряд не выглядел
     недоделанным, пока снимков нет. */
  .cats .плитка .зн{position:relative;width:var(--box-lg);height:var(--box-lg);
        border-radius:var(--box-lg-r);background:var(--accent-soft);
        color:var(--accent);display:grid;place-items:center;
        margin-bottom:auto;}
  .cats .плитка .зн svg{width:var(--ic);height:var(--ic);stroke:currentColor;
        fill:none;stroke-width:1.8;}

  @media (max-width:900px){
    .cats{grid-template-columns:repeat(2,1fr);}
    .cats > .плитка.крупно{grid-column:span 2;grid-row:auto;}
  }
  @media (max-width:520px){
    /* Две колонки остаются и на телефоне: одна колонка растягивает
       десять разделов на три экрана, и до нижних не долистывают. */
    .cats{gap:var(--sp-2);}
    .cats .плитка{min-height:var(--плитка-h-sm);padding:var(--sp-3);}
  }

  /* ── блок-команда.css ─────────────────────────────────────── */
/* Блок команды: карточки людей с ролью и связью.
   Часть набора элементов. Собирается сборщиком:
   python tools/build_styles.py */

/* Люди на странице работают там, где решение принимают с оглядкой на
   человека: услуги, доставка, поддержка. В магазине электроники — не очень,
   в мастерской или сервисе — очень.

   Роль пишем понятную покупателю, а не должность из штатного расписания:
   «Отвечает на вопросы о доставке» вместо «Менеджер отдела логистики».

   Ссылки на связь — только рабочие. Личная почта в таком блоке однажды
   утекает в рассылки, поэтому здесь общий ящик отдела. */

  .team{padding:var(--sp-6) var(--sp-4);}
  .team .in{max-width:var(--shop-w);margin:0 auto;}
  .team .шапка{text-align:center;max-width:var(--чит-w);margin:0 auto var(--sp-6);
        display:flex;flex-direction:column;gap:var(--sp-2);}
  .team .шапка h2{font-size:var(--fs-2xl);letter-spacing:-.02em;}
  .team .шапка p{color:var(--muted);font-size:var(--fs-md);line-height:1.6;}

  .team .сетка{display:grid;grid-template-columns:repeat(4,1fr);gap:var(--sp-4);}

  .team .чел{display:flex;flex-direction:column;align-items:center;
        text-align:center;gap:var(--sp-2);padding:var(--sp-4);
        background:var(--surface);border:1px solid var(--border);
        border-radius:var(--radius-lg);}
  /* Портрет кружком и одного размера у всех: прямоугольные снимки разной
     высоты рвут ряд, и блок выглядит собранным наспех. */
  .team .чел .лицо{width:var(--лицо);height:var(--лицо);border-radius:var(--pill);
        background:var(--surface-2);overflow:hidden;display:grid;place-items:center;
        color:var(--faint);font-size:var(--fs-lg);font-weight:600;flex:none;}
  .team .чел .лицо img{width:100%;height:100%;object-fit:cover;display:block;}
  .team .чел b{font-size:var(--fs-md);font-weight:650;}
  .team .чел .роль{color:var(--muted);font-size:var(--fs-sm);line-height:1.5;}
  .team .чел .связь{display:flex;gap:var(--sp-2);margin-top:var(--sp-1);}
  .team .чел .связь a{width:var(--box);height:var(--box);border-radius:var(--pill);
        border:1px solid var(--border);display:grid;place-items:center;
        color:var(--muted);text-decoration:none;}
  .team .чел .связь a:hover{border-color:var(--accent);color:var(--accent);}
  .team .чел .связь svg{width:var(--ic-sm);height:var(--ic-sm);
        stroke:currentColor;fill:none;stroke-width:1.8;}

  /* Ряд без карточек: когда людей много и рамки превращают блок в таблицу */
  .team.без-рамок .чел{background:none;border:none;padding:0;}

  @media (max-width:900px){
    .team .сетка{grid-template-columns:repeat(2,1fr);}
  }
  @media (max-width:520px){
    .team .сетка{grid-template-columns:1fr;}
    /* В столбик карточка разворачивается в строку: портрет слева, текст
       справа — иначе четыре человека занимают четыре экрана.

       Раскладка сеткой, а не строкой: строкой портрет, имя, роль и две
       кнопки связи не помещаются в триста девяносто точек — карточка
       вылезала за край, и страница листалась вбок. Здесь портрет держит
       левую колонку на две строки, текст занимает остаток, кнопки уходят
       под текст. Разметка при этом не меняется. */
    .team .чел{display:grid;grid-template-columns:auto minmax(0, 1fr);
          gap:var(--sp-1) var(--sp-3);align-items:center;text-align:left;}
    .team .чел .лицо{grid-row:span 2;width:var(--box-xl);height:var(--box-xl);
          font-size:var(--fs-sm);}
    .team .чел b{align-self:end;}
    .team .чел .роль{align-self:start;}
    .team .чел .связь{grid-column:2;margin-top:var(--sp-2);}
  }

  /* ── блок-логотипы.css ─────────────────────────────────────── */
/* Ряд логотипов: с кем работаем, где о нас писали.
   Часть набора элементов. Собирается сборщиком:
   python tools/build_styles.py */

/* Чужие логотипы на своей странице — довод доверия, но только если они не
   спорят с самой страницей. Поэтому ряд приглушён: логотипы серые и
   становятся цветными по наведению.

   Строка над рядом обязательна и должна быть честной: «Нас выбрали» и
   «О нас писали» — разные вещи, и подмена быстро выходит боком.

   Ряд не переносится в сетку: логотипы разной ширины, и в сетке они встают
   с рваными промежутками. Лучше пусть листается вбок. */

  .logos{padding:var(--sp-6) var(--sp-4);}
  .logos .in{max-width:var(--shop-w);margin:0 auto;display:flex;
        flex-direction:column;align-items:center;gap:var(--sp-4);}
  .logos .подпись{color:var(--faint);font-size:var(--fs-xs);
        text-transform:uppercase;letter-spacing:.08em;}

  .logos .ряд{display:flex;align-items:center;justify-content:center;
        gap:var(--sp-6);flex-wrap:wrap;width:100%;}
  .logos .ряд a, .logos .ряд span{display:grid;place-items:center;
        height:var(--box-lg);opacity:.55;filter:grayscale(1);
        transition:opacity .15s,filter .15s;color:var(--muted);
        font-weight:700;font-size:var(--fs-md);letter-spacing:-.02em;}
  .logos .ряд a:hover{opacity:1;filter:grayscale(0);}
  .logos .ряд img{max-height:100%;width:auto;display:block;}

  @media (max-width:760px){
    /* На телефоне ряд листается вбок: перенос по два логотипа делает из
       строки доверия столбик логотипов, который читают как список. */
    .logos .ряд{flex-wrap:nowrap;overflow-x:auto;justify-content:flex-start;
          gap:var(--sp-5);scrollbar-width:none;}
    .logos .ряд::-webkit-scrollbar{display:none;}
    .logos .ряд a, .logos .ряд span{flex:none;}
  }

  /* ── блок-отзывы.css ─────────────────────────────────────── */
/* Блок отзывов: оценка в целом и карточки людей.
   Часть набора элементов. Собирается сборщиком:
   python tools/build_styles.py */

/* Отзывы работают, когда им верят. Верят двум вещам: видно распределение
   оценок (а не только «4,8 из 5») и видно живого человека — имя, дату, что
   именно он купил.

   Поэтому слева сводка со шкалой по звёздам: сколько пятёрок, сколько
   единиц. Магазин, где сто отзывов и все пятёрки, выглядит хуже магазина,
   где есть три двойки с ответом продавца.

   Ответ магазина — вложенным блоком в карточке, а не отдельным отзывом:
   так видно, что на жалобу ответили, а не что жалоб две. */

  .reviews{display:flex;gap:var(--sp-5);align-items:flex-start;}

  /* ── Сводка ────────────────────────────────────────────────────── */
  .reviews .сводка{width:var(--cart-sum-w);flex:none;background:var(--surface);
        border:1px solid var(--border);border-radius:var(--radius-lg);
        padding:var(--sp-4);display:flex;flex-direction:column;gap:var(--sp-3);}
  .reviews .сводка .число{display:flex;align-items:baseline;gap:var(--sp-2);}
  .reviews .сводка .число b{font-size:var(--fs-3xl);font-weight:700;line-height:1;
        letter-spacing:-.03em;}
  .reviews .сводка .число span{color:var(--muted);font-size:var(--fs-sm);}
  /* Шкала: строка на каждую оценку. Полосу считаем в процентах — так она
     не зависит от ширины колонки и не ломается при переносе. */
  .reviews .шкала{display:flex;flex-direction:column;gap:var(--sp-1);}
  .reviews .шкала .ст{display:flex;align-items:center;gap:var(--sp-2);
        font-size:var(--fs-xs);color:var(--muted);}
  .reviews .шкала .ст .зв{width:var(--sp-4);text-align:right;flex:none;}
  .reviews .шкала .ст .полоса{flex:1;height:var(--sp-2);border-radius:var(--pill);
        background:var(--surface-3);overflow:hidden;}
  .reviews .шкала .ст .полоса i{display:block;height:100%;border-radius:var(--pill);
        background:var(--warn);}
  .reviews .шкала .ст .скок{width:var(--sp-6);text-align:right;flex:none;
        color:var(--faint);}

  /* ── Звёзды ────────────────────────────────────────────────────── */
  /* Имя не .stars: тем классом в наборе уже названы кнопки выбора оценки в
     опроснике. Здесь оценку не ставят, а показывают, и путать их нельзя.

     Один приём на весь магазин: серый ряд звёзд, поверх него жёлтый обрезан
     по ширине оценки. Половинки получаются сами, отдельной картинки не надо. */
  .rating{display:inline-flex;position:relative;flex:none;line-height:1;}
  .rating .низ, .rating .верх{display:flex;gap:2px;}
  .rating .низ{color:var(--surface-3);}
  .rating .верх{position:absolute;inset:0;overflow:hidden;color:var(--warn);
        white-space:nowrap;}
  .rating svg{width:var(--ic-sm);height:var(--ic-sm);fill:currentColor;flex:none;}
  .rating.крупно svg{width:var(--ic);height:var(--ic);}

  /* ── Карточка отзыва ───────────────────────────────────────────── */
  .reviews .список{flex:1;min-width:0;display:flex;flex-direction:column;
        gap:var(--sp-3);}
  .review{background:var(--surface);border:1px solid var(--border);
        border-radius:var(--radius-lg);padding:var(--sp-4);
        display:flex;flex-direction:column;gap:var(--sp-3);}
  .review .верх{display:flex;align-items:center;gap:var(--sp-3);}
  .review .кто{flex:1;min-width:0;display:flex;flex-direction:column;gap:2px;}
  .review .кто b{font-size:var(--fs-sm);font-weight:600;}
  .review .кто span{color:var(--faint);font-size:var(--fs-xs);}
  /* Отметка «купил здесь» — главный довод доверия, поэтому цветом, а не серым */
  .review .купил{display:inline-flex;align-items:center;gap:var(--sp-1);
        color:var(--good);font-size:var(--fs-xs);}
  .review .купил svg{width:var(--ic-sm);height:var(--ic-sm);stroke:currentColor;
        fill:none;stroke-width:2;}
  .review .текст{color:var(--text-2);font-size:var(--fs-sm);line-height:1.65;}
  .review .плюс-минус{display:flex;flex-direction:column;gap:var(--sp-1);
        font-size:var(--fs-sm);}
  .review .плюс-минус div{display:flex;gap:var(--sp-2);}
  .review .плюс-минус b{flex:none;color:var(--muted);font-weight:600;
        font-size:var(--fs-xs);width:var(--sp-6);}

  /* Фотографии от покупателя: ряд с прокруткой вбок */
  .review .фото-ряд{display:flex;gap:var(--sp-2);overflow-x:auto;
        scrollbar-width:none;}
  .review .фото-ряд::-webkit-scrollbar{display:none;}
  .review .фото-ряд img, .review .фото-ряд .нет{width:var(--cart-pic-sm);
        height:var(--cart-pic-sm);border-radius:var(--radius-sm);object-fit:cover;
        flex:none;background:var(--surface-2);display:grid;place-items:center;
        color:var(--faint);font-size:var(--fs-2xs);}

  /* Ответ магазина */
  .review .ответ{background:var(--surface-2);border-radius:var(--radius);
        padding:var(--sp-3);display:flex;flex-direction:column;gap:var(--sp-1);
        border-left:2px solid var(--accent);}
  .review .ответ b{font-size:var(--fs-xs);color:var(--accent);}
  .review .ответ p{font-size:var(--fs-sm);color:var(--text-2);line-height:1.6;}

  /* Полезен ли отзыв */
  .review .низ{display:flex;align-items:center;gap:var(--sp-2);
        font-size:var(--fs-xs);color:var(--muted);}
  .review .низ button{display:inline-flex;align-items:center;gap:var(--sp-1);
        height:var(--ctl-sm);padding:0 var(--sp-2);border:1px solid var(--border);
        border-radius:var(--radius-sm);background:var(--surface);cursor:pointer;
        color:var(--text-2);font-size:var(--fs-xs);}
  .review .низ button:hover{border-color:var(--control-line);}
  .review .низ svg{width:var(--ic-sm);height:var(--ic-sm);stroke:currentColor;
        fill:none;stroke-width:1.8;}

  @media (max-width:900px){
    .reviews{flex-direction:column;}
    .reviews .сводка{width:100%;}
  }

  /* ── блок-подписка.css ─────────────────────────────────────── */
/* Блок подписки на письма и полоса согласия.
   Часть набора элементов. Собирается сборщиком:
   python tools/build_styles.py */

/* Поле и кнопка в одну строку, подпись под ними. Это единственная форма на
   сайте, где допустимо одно поле без заголовка: что вводить, ясно из текста
   рядом, и лишняя подпись делает блок тяжелее, чем повод его заполнить.

   Обещание конкретное: что придёт и как часто. «Подпишитесь на новости»
   не работает, «Раз в неделю: новинки и скидки» — работает.

   Отписка упомянута сразу. Строка про «отписаться в один клик» снимает
   главное возражение — что письма будут идти вечно. */

  .subscribe{padding:var(--sp-6) var(--sp-4);background:var(--surface-2);
        border-radius:var(--radius-lg);}
  .subscribe .in{max-width:var(--узк-w);margin:0 auto;text-align:center;
        display:flex;flex-direction:column;align-items:center;gap:var(--sp-3);}
  .subscribe h2{font-size:var(--fs-xl);letter-spacing:-.02em;}
  .subscribe p{color:var(--muted);font-size:var(--fs-sm);line-height:1.6;
        max-width:52ch;}

  /* Строка ввода. Поле и кнопка одной высоты — она у набора общая, поэтому
     здесь ничего не задаём и не подгоняем руками. */
  .subscribe .строка{display:flex;gap:var(--sp-2);width:100%;
        max-width:calc(var(--чит-w) * 0.7);}
  .subscribe .строка .field{flex:1;min-width:0;}
  .subscribe .строка .btn{flex:none;}

  /* Ответ формы: подставляется на место строки ввода, а не рядом с ней —
     иначе человек снова жмёт кнопку, думая, что не сработало. */
  .subscribe .готово{display:flex;align-items:center;gap:var(--sp-2);
        color:var(--good);font-size:var(--fs-sm);
        background:var(--good-bg);padding:var(--sp-3) var(--sp-4);
        border-radius:var(--radius);}
  .subscribe .готово svg{width:var(--ic);height:var(--ic);stroke:currentColor;
        fill:none;stroke-width:2.2;flex:none;}
  .subscribe .ошибка{color:var(--bad);font-size:var(--fs-xs);}

  .subscribe .мелко{color:var(--faint);font-size:var(--fs-xs);line-height:1.5;}
  .subscribe .мелко a{color:var(--muted);}

  /* Тот же блок узкой полосой — для подвала и боковой колонки */
  .subscribe.полоса{padding:var(--sp-4);}
  .subscribe.полоса .in{flex-direction:row;text-align:left;max-width:var(--shop-w);
        align-items:center;gap:var(--sp-4);}
  .subscribe.полоса .текст{flex:1;min-width:0;}
  .subscribe.полоса h2{font-size:var(--fs-md);}
  .subscribe.полоса .строка{width:auto;flex:none;}

  @media (max-width:760px){
    .subscribe .строка{flex-direction:column;}
    .subscribe .строка .btn{width:100%;}
    .subscribe.полоса .in{flex-direction:column;text-align:center;}
    .subscribe.полоса .строка{width:100%;}
  }

  /* ── блок-показатели.css ─────────────────────────────────────── */
/* Блок показателей: числа, которыми магазин говорит о себе.
   Часть набора элементов. Собирается сборщиком:
   python tools/build_styles.py */

/* Четыре числа в ряд: сколько лет, сколько заказов, сколько городов, какая
   оценка. Работают только конкретные цифры — «более 10 000 клиентов» верят
   меньше, чем «10 480 заказов за 2025 год».

   Число крупное, подпись под ним мелкая. Наоборот не делаем: читают здесь
   именно цифру, подпись только уточняет.

   Разделители между числами — волосяные линии, а не карточки. Карточки
   превращают четыре факта в четыре объекта, между которыми глаз выбирает,
   хотя выбирать нечего: их читают подряд. */

  /* Имя не .stats: этот класс с самого начала занят плитками показателей
     на главной панели. Совпадение имён не ошибка сборки — просто блок молча
     получает чужую сетку и ужимается вчетверо; ловится только глазами. */
  .figures{padding:var(--sp-6) var(--sp-4);}
  .figures .in{max-width:var(--shop-w);margin:0 auto;}
  .figures .шапка{text-align:center;max-width:var(--чит-w);margin:0 auto var(--sp-6);
        display:flex;flex-direction:column;gap:var(--sp-2);}
  .figures .шапка h2{font-size:var(--fs-2xl);letter-spacing:-.02em;}
  .figures .шапка p{color:var(--muted);font-size:var(--fs-md);line-height:1.6;}

  .figures .ряд{display:grid;grid-template-columns:repeat(4,1fr);gap:var(--sp-5);}
  .figures .пункт{display:flex;flex-direction:column;gap:var(--sp-1);
        text-align:center;padding:0 var(--sp-3);}
  .figures .пункт + .пункт{border-left:1px solid var(--border);}
  .figures .пункт b{font-size:var(--fs-3xl);font-weight:700;line-height:1;
        letter-spacing:-.03em;}
  .figures .пункт span{color:var(--muted);font-size:var(--fs-sm);}
  /* Приставка к числу — знак валюты, плюс, процент. Мельче самого числа,
     иначе «₽» спорит с суммой за внимание. */
  .figures .пункт b i{font-size:var(--fs-xl);font-style:normal;color:var(--muted);
        font-weight:600;}

  /* Тёмный вид: тот же блок заливкой, когда стоит между светлыми разделами */
  .figures.тёмный{background:var(--text);border-radius:var(--radius-lg);}
  .figures.тёмный .шапка h2, .figures.тёмный .пункт b{color:var(--surface);}
  .figures.тёмный .шапка p, .figures.тёмный .пункт span{
        color:color-mix(in oklab, var(--surface) 70%, transparent);}
  .figures.тёмный .пункт + .пункт{
        border-color:color-mix(in oklab, var(--surface) 22%, transparent);}

  @media (max-width:900px){
    .figures .ряд{grid-template-columns:repeat(2,1fr);gap:var(--sp-4);}
    /* Линия между колонками остаётся только внутри пары: у первого пункта
       второй строки её быть не должно, иначе она висит в воздухе. */
    .figures .пункт + .пункт{border-left:none;}
    .figures .пункт:nth-child(even){border-left:1px solid var(--border);}
  }
  @media (max-width:520px){
    .figures .ряд{grid-template-columns:1fr;}
    .figures .пункт{padding:var(--sp-3) 0;}
    .figures .пункт:nth-child(even){border-left:none;}
    .figures .пункт + .пункт{border-top:1px solid var(--border);}
  }

  /* ── блок-призыв.css ─────────────────────────────────────── */
/* Блок призыва: заголовок, строка пояснения и кнопка.
   Часть набора элементов. Собирается сборщиком:
   python tools/build_styles.py */

/* Ставится в конце страницы и между разделами: человек дочитал, согласен —
   и ему некуда нажать. Пролистывать обратно наверх он не будет.

   Кнопка здесь ровно одна. Две равные кнопки в призыве — это уже не призыв,
   а выбор, и на нём останавливаются. Вторую ссылку, если нужна, ставим
   строкой под кнопкой и делаем её обычной ссылкой, а не кнопкой.

   Три вида, все три из тех же частей:
     обычный   на фоне страницы, между разделами
     цветной   заливка акцентом, в конце страницы
     в рамке   когда рядом уже есть цветной блок и второй перебьёт первый */

  .cta{padding:var(--sp-6) var(--sp-4);}
  .cta .in{max-width:var(--узк-w);margin:0 auto;text-align:center;
        display:flex;flex-direction:column;align-items:center;gap:var(--sp-3);}
  .cta h2{font-size:var(--fs-2xl);line-height:1.25;letter-spacing:-.02em;}
  .cta p{color:var(--muted);font-size:var(--fs-md);line-height:1.6;
        max-width:52ch;}
  .cta .кнопки{display:flex;gap:var(--sp-3);flex-wrap:wrap;
        justify-content:center;margin-top:var(--sp-1);}
  /* Мелкая строка под кнопкой снимает последнее возражение: «без карты»,
     «отмена в один клик». Работает лучше ещё одного абзаца выше. */
  .cta .мелко{color:var(--faint);font-size:var(--fs-xs);}

  /* Цветной: акцент во всю ширину */
  .cta.цветной{background:var(--accent);color:var(--on-accent);
        border-radius:var(--radius-lg);}
  .cta.цветной h2{color:var(--on-accent);}
  /* Пояснение на цветном фоне — не приглушённый серый, а свой же цвет с
     прозрачностью: серый на цветном выглядит грязным пятном. */
  .cta.цветной p{color:color-mix(in oklab, var(--on-accent) 82%, transparent);}
  .cta.цветной .мелко{color:color-mix(in oklab, var(--on-accent) 65%, transparent);}
  /* Кнопка на цветном становится светлой: закрашенная акцентом кнопка на
     акцентном фоне не видна вовсе. */
  .cta.цветной .btn-primary{background:var(--surface);color:var(--accent);
        border-color:var(--surface);}
  .cta.цветной .btn-primary:hover{background:var(--surface-2);
        border-color:var(--surface-2);}
  .cta.цветной .btn-ghost{color:var(--on-accent);
        border-color:color-mix(in oklab, var(--on-accent) 45%, transparent);}

  /* В рамке: тот же блок, но тише */
  .cta.в-рамке{background:var(--surface);border:1px solid var(--border);
        border-radius:var(--radius-lg);}

  @media (max-width:620px){
    .cta{padding:var(--sp-5) var(--sp-4);}
    .cta h2{font-size:var(--fs-xl);}
    /* Кнопки на телефоне во всю ширину и в столбик: рядом они сжимаются до
       узких прямоугольников, по которым тяжело попасть. */
    .cta .кнопки{flex-direction:column;align-self:stretch;}
    .cta .кнопки .btn{width:100%;}
  }

  /* Форма внутри призыва: три поля в ряд и кнопка. Заявку оставляют те, кто
     не нашёл нужного в каталоге, — уйти без покупки проще всего именно
     отсюда, поэтому форма стоит прямо в блоке, а не за ссылкой. */
  .cta .подбор-форма{display:grid;grid-template-columns:repeat(3, 1fr) auto;
        gap:var(--sp-2);align-items:end;width:100%;text-align:left;
        margin-top:var(--sp-2);}
  .cta .подбор-форма .btn{align-self:end;}
  @media (max-width:820px){
    .cta .подбор-форма{grid-template-columns:1fr;}
    .cta .подбор-форма .btn{width:100%;}
  }

  /* ── блок-связь.css ─────────────────────────────────────── */
/* Секция «Напишите нам» и «Как нас найти».
   Часть набора элементов. Собирается сборщиком:
   python tools/build_styles.py */

/* Две секции про одно: как до вас достучаться. Стоят обычно рядом, поэтому
   и живут в одном файле — правятся вместе.

   .связь-блок — форма: имя, способ связи, вопрос. Полей ровно три: каждое
   лишнее поле в форме обратной связи — минус десятая часть отправивших, а
   перезвонить можно и уточнить.

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

/* ── Форма связи ─────────────────────────────────────────────────── */
  .связь-блок{display:grid;gap:var(--sp-5);
        grid-template-columns:minmax(0, 1fr) minmax(0, 1fr);
        align-items:start;}
  .связь-блок > .про{display:flex;flex-direction:column;gap:var(--sp-3);}
  .связь-блок > .про h2{font-size:var(--fs-2xl);letter-spacing:-.02em;
        line-height:1.2;}
  .связь-блок > .про p{color:var(--muted);font-size:var(--fs-md);
        line-height:1.6;margin:0;}

  /* Способы связи списком: кому-то проще написать в мессенджер, чем ждать
     ответа на письмо. Показываем все и не заставляем выбирать форму. */
  .связь-блок .способы{display:flex;flex-direction:column;gap:var(--sp-2);
        margin-top:var(--sp-2);}
  .связь-блок .способы a{display:flex;align-items:center;gap:var(--sp-3);
        min-height:var(--ctl);color:var(--text);text-decoration:none;
        font-size:var(--fs-md);}
  .связь-блок .способы a:hover{color:var(--accent);}
  .связь-блок .способы svg{width:var(--ic);height:var(--ic);flex:none;
        stroke:currentColor;fill:none;stroke-width:1.8;
        stroke-linecap:round;stroke-linejoin:round;color:var(--muted);}
  .связь-блок .способы .мелко{color:var(--faint);font-size:var(--fs-xs);
        margin-left:auto;}

  .связь-блок form{display:flex;flex-direction:column;gap:var(--sp-3);
        padding:var(--sp-5);background:var(--surface);
        border:1px solid var(--border);border-radius:var(--radius-lg);}
  .связь-блок form .btn{width:100%;}
  /* Согласие под кнопкой, а не над: сперва действие, потом мелкий шрифт про
     обработку данных. Над кнопкой его читают как препятствие. */
  .связь-блок form .согласие{color:var(--faint);font-size:var(--fs-xs);
        line-height:1.5;}
  .связь-блок form .согласие a{color:var(--muted);}

/* ── Локация ─────────────────────────────────────────────────────── */
  .локация{display:grid;gap:var(--sp-5);
        grid-template-columns:minmax(0, 1fr) minmax(0, 1.4fr);
        align-items:stretch;}
  .локация .сведения{display:flex;flex-direction:column;gap:var(--sp-4);}
  .локация .сведения .строка{display:flex;gap:var(--sp-3);align-items:flex-start;}
  .локация .сведения .строка svg{width:var(--ic);height:var(--ic);flex:none;
        margin-top:2px;stroke:currentColor;fill:none;stroke-width:1.8;
        stroke-linecap:round;stroke-linejoin:round;color:var(--muted);}
  .локация .сведения b{display:block;font-size:var(--fs-md);}
  .локация .сведения span{color:var(--muted);font-size:var(--fs-sm);
        line-height:1.55;}

  /* Место под карту. Соотношение задано, чтобы страница не прыгала, когда
     врезка догрузится: без него содержимое ниже дёргается вверх-вниз. */
  .локация .карта{position:relative;overflow:hidden;min-height:var(--карта-h);
        border:1px solid var(--border);border-radius:var(--radius-lg);
        background:var(--surface-2);}
  .локация .карта iframe,
  .локация .карта img{position:absolute;inset:0;width:100%;height:100%;
        border:none;display:block;object-fit:cover;}
  /* Карта выключена: показываем адрес и ссылку, а не пустое серое поле. */
  .локация .карта .вместо{position:absolute;inset:0;display:flex;
        flex-direction:column;align-items:center;justify-content:center;
        gap:var(--sp-2);padding:var(--sp-4);text-align:center;
        color:var(--muted);font-size:var(--fs-sm);}
  .локация .карта .вместо svg{width:var(--box);height:var(--box);
        stroke:currentColor;fill:none;stroke-width:1.4;color:var(--faint);}

  @media (max-width:860px){
    /* На узком форма и сведения встают друг под друга: в две колонки поле
       ввода сжимается до трети экрана, и в него неудобно писать. */
    .связь-блок, .локация{grid-template-columns:1fr;}
  }

  /* ── блок-цены.css ─────────────────────────────────────── */
/* Блок тарифов: колонки с ценой и списком того, что входит.
   Часть набора элементов. Собирается сборщиком:
   python tools/build_styles.py */

/* Тарифы сравнивают глазами по строкам, поэтому списки в колонках должны
   совпадать построчно: одинаковый набор пунктов, а не «в дорогом написали
   больше». Иначе сравнение превращается в чтение трёх разных текстов.

   Средний тариф приподнят и обведён — не ради красоты: без подсказки люди
   выбирают самый дешёвый или уходят думать. Подписан честно («Берут чаще
   всего»), а не «Лучший выбор».

   Годовая цена показывается за месяц с пометкой о списании раз в год. Крупная
   сумма за год отпугивает, спрятанная — злит при оплате. */

  .price-block{display:flex;flex-direction:column;gap:var(--sp-5);align-items:center;}
  .price-block > .шапка{text-align:center;max-width:var(--чит-w);
        display:flex;flex-direction:column;gap:var(--sp-2);}
  .price-block > .шапка h2{font-size:var(--fs-2xl);letter-spacing:-.02em;}
  .price-block > .шапка p{color:var(--muted);font-size:var(--fs-base);line-height:1.6;}

  /* Переключатель «месяц / год» — сегменты из набора, ничего своего */
  .price-block .срок{display:flex;align-items:center;gap:var(--sp-2);}
  .price-block .срок .выгода{font-size:var(--fs-xs);color:var(--good);
        background:var(--good-bg);padding:2px var(--sp-2);border-radius:var(--pill);}

  .price-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:var(--sp-4);
        width:100%;align-items:stretch;}

  .plan{display:flex;flex-direction:column;gap:var(--sp-3);padding:var(--sp-5);
        background:var(--surface);border:1px solid var(--border);
        border-radius:var(--radius-lg);}
  .plan > .назв{font-size:var(--fs-md);font-weight:650;}
  .plan > .опис{color:var(--muted);font-size:var(--fs-sm);line-height:1.55;}
  .plan .цена{display:flex;align-items:baseline;gap:var(--sp-1);flex-wrap:wrap;}
  .plan .цена b{font-size:var(--fs-3xl);font-weight:700;line-height:1;
        letter-spacing:-.03em;}
  .plan .цена span{color:var(--muted);font-size:var(--fs-sm);}
  .plan .цена s{color:var(--faint);font-size:var(--fs-sm);}
  .plan .мелко{color:var(--faint);font-size:var(--fs-xs);}

  .plan .что{display:flex;flex-direction:column;gap:var(--sp-2);
        padding-top:var(--sp-3);border-top:1px solid var(--border-soft);
        font-size:var(--fs-sm);}
  .plan .что div{display:flex;gap:var(--sp-2);align-items:flex-start;
        color:var(--text-2);}
  .plan .что svg{width:var(--ic);height:var(--ic);flex:none;stroke:currentColor;
        fill:none;stroke-width:2.2;color:var(--good);margin-top:2px;}
  /* Чего в тарифе нет, показываем той же строкой серым: пустое место в
     колонке читается как «забыли написать». */
  .plan .что div.нет{color:var(--faint);}
  .plan .что div.нет svg{color:var(--faint);stroke-width:1.8;}

  /* Кнопка прижата к низу карточки: у тарифов разной длины списки, и без
     этого кнопки встают лесенкой. */
  .plan .btn{margin-top:auto;}

  /* Выбор по умолчанию */
  .plan.главный{border-color:var(--accent);box-shadow:var(--shadow-lg);
        position:relative;}
  .plan.главный .метка{position:absolute;top:0;left:50%;
        transform:translate(-50%,-50%);background:var(--accent);
        color:var(--on-accent);font-size:var(--fs-2xs);font-weight:700;
        padding:var(--sp-1) var(--sp-3);border-radius:var(--pill);
        white-space:nowrap;}

  @media (max-width:900px){
    .price-grid{grid-template-columns:1fr;}
    /* Приподнятая карточка на телефоне не приподнята: там всё в столбик,
       и тень читается как случайная. */
    .plan.главный{box-shadow:none;}
  }

  /* ── заготовки.css ─────────────────────────────────────── */
/* Здесь не отдельные элементы, а целые куски страницы, которые повторяются
   из проекта в проект: шапка сайта, подвал с колонками, карточка входа.
   Собраны из тех же кнопок и полей — ничего своего, только раскладка.

   Зачем отдельным файлом: панель ими не пользуется, ими пользуются витрина,
   лендинг и кабинет. Держать их вперемешку с внутренностями панели значит
   через месяц не знать, что можно трогать, а что сломает админку. */

  /* Подвал переехал в отдельный модуль (Витрина/подвал.css): здесь
     оставалась его первая, куцая версия, и два одинаковых имени спорили
     между собой — колонки подвала брали то одну раскладку, то другую.

  /* ── Шапка сайта: знак слева, меню в середине, действия справа ── */
  .site-head{display:flex;align-items:center;gap:var(--sp-3);height:56px;padding:0 var(--sp-4);
             background:var(--surface);border-bottom:1px solid var(--border);}
  .site-head .znak{width:var(--box-sm);height:var(--box-sm);border-radius:var(--box-sm-r);
                   background:var(--accent);color:var(--on-accent);display:grid;
                   place-items:center;font-weight:800;flex:none;overflow:hidden;}
  .site-head .znak img{width:100%;height:100%;object-fit:cover;display:block;}
  .site-head .name{font-weight:700;font-size:var(--fs-lg);letter-spacing:-.01em;
                   text-decoration:none;color:var(--text);}
  .site-head .mid{flex:1;min-width:0;display:flex;justify-content:center;}
  .site-head .acts{display:flex;align-items:center;gap:var(--sp-2);flex:none;}
  /* Узкая: меню уезжает под шапку отдельной строкой, а не прячется совсем */
  @media (max-width:760px){
    .site-head{flex-wrap:wrap;height:auto;padding:var(--sp-2) var(--sp-3);gap:var(--sp-2);}
    .site-head .mid{order:3;width:100%;justify-content:flex-start;}
    .site-head .acts{margin-left:auto;}
  }

  /* ── Карточка формы: вход, регистрация, восстановление пароля ── */

  /* ── кабинет.css ─────────────────────────────────────── */
/* Личный кабинет покупателя: меню, заказы, избранное, сравнение.
   Часть набора элементов. Собирается сборщиком:
   python tools/build_styles.py */

/* Кабинет открывают ради одного вопроса: где мой заказ. Всё остальное —
   адреса, карты, настройки — открывают в десять раз реже, и порядок пунктов
   меню должен это отражать.

   Заказ в списке показывает состояние словами и полосой шагов, а не только
   цветной точкой: «Собираем» и «Передан в доставку» — разные вещи, и по
   цвету их не различить.

   Товары внутри заказа показываем сразу, не пряча за «Подробнее»: человек
   узнаёт свой заказ по картинке, а не по номеру. */

  .lk{display:flex;gap:var(--sp-5);align-items:flex-start;}

  /* ── Меню кабинета ─────────────────────────────────────────────── */
  .lk-menu{width:var(--side-w);flex:none;display:flex;flex-direction:column;
        gap:var(--sp-1);}
  .lk-menu .кто{display:flex;align-items:center;gap:var(--sp-2);
        padding:var(--sp-3);background:var(--surface);border:1px solid var(--border);
        border-radius:var(--radius);margin-bottom:var(--sp-2);}
  .lk-menu .кто .лицо{width:var(--box);height:var(--box);flex:none;
        border-radius:var(--pill);background:var(--accent-soft);
        color:var(--accent);display:grid;place-items:center;font-weight:700;
        font-size:var(--fs-sm);overflow:hidden;}
  .lk-menu .кто .лицо img{width:100%;height:100%;object-fit:cover;display:block;}
  .lk-menu .кто .тело{flex:1;min-width:0;display:flex;flex-direction:column;}
  .lk-menu .кто b{font-size:var(--fs-sm);overflow:hidden;
        text-overflow:ellipsis;white-space:nowrap;}
  .lk-menu .кто span{color:var(--faint);font-size:var(--fs-xs);}

  .lk-menu a{display:flex;align-items:center;gap:var(--sp-2);
        min-height:var(--ctl);padding:0 var(--sp-3);border-radius:var(--radius-sm);
        color:var(--text-2);text-decoration:none;font-size:var(--fs-sm);}
  .lk-menu a:hover{background:var(--surface-2);color:var(--text);}
  .lk-menu a.on{background:var(--accent-soft);color:var(--accent);font-weight:600;}
  .lk-menu a svg{width:var(--ic);height:var(--ic);flex:none;stroke:currentColor;
        fill:none;stroke-width:1.8;}
  .lk-menu a .скок{margin-left:auto;color:var(--faint);font-size:var(--fs-xs);}
  /* Выход отделён линией и приглушён: это не пункт наравне с заказами */
  .lk-menu .выход{margin-top:var(--sp-2);padding-top:var(--sp-2);
        border-top:1px solid var(--border);}
  .lk-menu .выход a{color:var(--muted);}
  .lk-menu .выход a:hover{color:var(--bad);background:var(--bad-bg);}

  .lk > .тело{flex:1;min-width:0;display:flex;flex-direction:column;gap:var(--sp-3);}

  /* ── Заказ в списке ────────────────────────────────────────────── */
  .order{background:var(--surface);border:1px solid var(--border);
        border-radius:var(--radius-lg);overflow:hidden;}
  .order > .шапка{display:flex;align-items:center;gap:var(--sp-3);flex-wrap:wrap;
        padding:var(--sp-3) var(--sp-4);background:var(--surface-2);
        border-bottom:1px solid var(--border);}
  .order > .шапка .ном{font-family:var(--mono);font-size:var(--fs-sm);
        font-weight:700;}
  .order > .шапка .дата{color:var(--muted);font-size:var(--fs-xs);}
  .order > .шапка .сумма{margin-left:auto;font-size:var(--fs-md);font-weight:700;
        white-space:nowrap;}

  /* Полоса шагов заказа: принят → собираем → в пути → выдан */
  .order .шаги{display:flex;align-items:center;gap:var(--sp-2);
        padding:var(--sp-3) var(--sp-4);border-bottom:1px solid var(--border-soft);}
  .order .шаги .ш{flex:1;display:flex;flex-direction:column;gap:var(--sp-1);
        font-size:var(--fs-2xs);color:var(--faint);}
  .order .шаги .ш i{display:block;height:3px;border-radius:var(--pill);
        background:var(--surface-3);}
  .order .шаги .ш.пройден i{background:var(--good);}
  .order .шаги .ш.пройден{color:var(--good);}
  .order .шаги .ш.сейчас i{background:var(--accent);}
  .order .шаги .ш.сейчас{color:var(--accent);font-weight:600;}
  /* Отменённый заказ: вся полоса серая, шаги не подсвечиваем вовсе —
     зелёная половина у отменённого заказа выглядит издевательством. */
  .order.отменён .шаги .ш i{background:var(--surface-3);}
  .order.отменён .шаги .ш{color:var(--faint);}

  .order .товары{padding:var(--sp-3) var(--sp-4);display:flex;gap:var(--sp-2);
        flex-wrap:wrap;align-items:center;}
  .order .товары .фото{width:var(--box-xl);height:var(--box-xl);flex:none;
        border-radius:var(--radius-sm);background:var(--surface-2);overflow:hidden;
        display:grid;place-items:center;color:var(--faint);font-size:var(--fs-2xs);}
  .order .товары .фото img{width:100%;height:100%;object-fit:cover;display:block;}
  .order .товары .ещё{color:var(--muted);font-size:var(--fs-sm);}
  .order > .низ{display:flex;gap:var(--sp-2);padding:var(--sp-3) var(--sp-4);
        border-top:1px solid var(--border-soft);flex-wrap:wrap;}

  /* ── Сравнение товаров ─────────────────────────────────────────── */
  /* Таблица со столбцом свойств слева. Первый столбец липкий: без него при
     прокрутке вбок непонятно, какое свойство сравниваешь. */
  .compare{overflow-x:auto;}
  .compare table{border-collapse:collapse;width:100%;font-size:var(--fs-sm);}
  .compare th, .compare td{padding:var(--sp-3);text-align:left;
        border-bottom:1px solid var(--border-soft);vertical-align:top;
        min-width:var(--cart-sum-w);}
  .compare th:first-child, .compare td:first-child{position:sticky;left:0;
        background:var(--surface);color:var(--muted);min-width:var(--side-w);
        border-right:1px solid var(--border);}
  /* Строка, где значения различаются, подсвечена: ради неё и сравнивают */
  .compare tr.разное td{background:var(--warn-bg);}
  .compare tr.разное td:first-child{background:var(--warn-bg);}

  @media (max-width:900px){
    .lk{flex-direction:column;}
    .lk-menu{width:100%;flex-direction:row;overflow-x:auto;scrollbar-width:none;}
    .lk-menu::-webkit-scrollbar{display:none;}
    .lk-menu .кто{display:none;}
    .lk-menu a{flex:none;}
    .lk-menu .выход{border-top:none;margin-top:0;padding-top:0;}
    .order > .шапка .сумма{margin-left:0;width:100%;}
  }

  /* ── корзина-шторка.css ─────────────────────────────────────── */
/* Корзина в шторке: подтверждение, что товар добавлен.
   Часть набора элементов. Собирается сборщиком:
   python tools/build_styles.py */

/* Открывается сама после «В корзину». Задача одна — показать, что нажатие
   сработало, и не увести человека со страницы каталога: перебросишь его на
   отдельную страницу корзины — он перестанет выбирать дальше.

   Внизу две кнопки, и они не равны. «Оформить» закрашена, «Продолжить
   покупки» обычная: раз уж корзину открыли, чаще идут платить, но возврат
   к выбору обязан остаться на виду, а не за крестиком.

   Опирается на шторку из набора: своя раскладка здесь только внутри. */

  .cart-drawer{display:flex;flex-direction:column;height:100%;}
  .cart-drawer > .шапка{display:flex;align-items:center;gap:var(--sp-2);
        padding:var(--sp-4);border-bottom:1px solid var(--border);}
  .cart-drawer > .шапка b{font-size:var(--fs-md);}
  .cart-drawer > .шапка .скок{color:var(--muted);font-size:var(--fs-sm);}
  .cart-drawer > .шапка .x{margin-left:auto;}

  .cart-drawer > .список{flex:1;overflow:auto;padding:var(--sp-3);
        display:flex;flex-direction:column;gap:var(--sp-2);}

  /* Строка в шторке короче, чем на странице корзины: свойства товара здесь не
     показываем — их сверяют при оформлении, а не в момент добавления. */
  .cart-drawer .мини{display:flex;gap:var(--sp-3);align-items:center;}
  .cart-drawer .мини .фото{width:var(--box-xl);height:var(--box-xl);flex:none;
        border-radius:var(--radius);background:var(--surface-2);overflow:hidden;
        display:grid;place-items:center;color:var(--faint);font-size:var(--fs-2xs);}
  .cart-drawer .мини .фото img{width:100%;height:100%;object-fit:cover;display:block;}
  .cart-drawer .мини .тело{flex:1;min-width:0;display:flex;flex-direction:column;gap:2px;}
  .cart-drawer .мини .имя{font-size:var(--fs-sm);font-weight:600;color:var(--text);
        text-decoration:none;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
  .cart-drawer .мини .цена{font-size:var(--fs-sm);color:var(--muted);}
  .cart-drawer .мини .цена b{color:var(--text);}

  /* Низ шторки: сумма и кнопки. Не прокручивается вместе со списком — иначе
     при длинном списке «Оформить» уезжает, и её приходится искать. */
  .cart-drawer > .низ{border-top:1px solid var(--border);padding:var(--sp-4);
        display:flex;flex-direction:column;gap:var(--sp-3);background:var(--surface);}
  .cart-drawer > .низ .итог{display:flex;justify-content:space-between;
        align-items:baseline;}
  .cart-drawer > .низ .итог span{color:var(--muted);font-size:var(--fs-sm);}
  .cart-drawer > .низ .итог b{font-size:var(--fs-xl);font-weight:700;}
  .cart-drawer > .низ .кнопки{display:flex;gap:var(--sp-2);}
  .cart-drawer > .низ .кнопки .btn{flex:1;}

  /* Пустая корзина: не «ничего нет», а куда идти дальше */
  .cart-drawer .пусто{flex:1;display:flex;flex-direction:column;align-items:center;
        justify-content:center;gap:var(--sp-3);padding:var(--sp-6);text-align:center;}
  .cart-drawer .пусто svg{width:var(--box-xl);height:var(--box-xl);color:var(--faint);
        stroke:currentColor;fill:none;stroke-width:1.3;}
  .cart-drawer .пусто b{font-size:var(--fs-md);}
  .cart-drawer .пусто p{color:var(--muted);font-size:var(--fs-sm);}

  /* ── корзина.css ─────────────────────────────────────── */
/* Корзина: строки товаров и подсчёт к оплате.
   Часть набора элементов. Собирается сборщиком:
   python tools/build_styles.py */

/* Корзина — не список, а последняя проверка перед деньгами. Поэтому в строке
   ровно то, что человек хочет перепроверить: тот ли товар, сколько штук,
   сколько за них.

   Итог держится липким справа. Прокрутил длинный список — сумма всё равно
   перед глазами, и кнопка «Оформить» не уезжает вниз, куда никто не листает.
   На узком экране липнет к низу окна: там правой колонки нет вовсе.

   Удаление — крестиком, а не кнопкой «Удалить»: она читается как главное
   действие строки, хотя главное здесь — купить. Удалённая строка остаётся
   на месте с возвратом, потому что промах пальцем в корзине стоит дороже
   всего: человек не помнит, что смахнул, и уходит собирать заново. */

  .cart{display:flex;gap:var(--sp-5);align-items:flex-start;}
  .cart > .строки{flex:1;min-width:0;display:flex;flex-direction:column;gap:var(--sp-2);}

  /* ── Строка товара ─────────────────────────────────────────────── */
  .cart-row{display:flex;gap:var(--sp-3);padding:var(--sp-3);background:var(--surface);
        border:1px solid var(--border);border-radius:var(--radius-lg);align-items:flex-start;}
  .cart-row .фото{width:var(--cart-pic);height:var(--cart-pic);border-radius:var(--radius);
        background:var(--surface-2);flex:none;overflow:hidden;display:grid;
        place-items:center;color:var(--faint);font-size:var(--fs-2xs);text-align:center;}
  .cart-row .фото img{width:100%;height:100%;object-fit:cover;display:block;}

  .cart-row .тело{flex:1;min-width:0;display:flex;flex-direction:column;gap:var(--sp-1);}
  /* Название — ссылка на товар: по ней возвращаются посмотреть, что берут.
     Высота по кеглю мельче нормы касания. */
  .cart-row .имя{display:flex;align-items:center;min-height:var(--цель);
        font-size:var(--fs-sm);font-weight:600;color:var(--text);
        text-decoration:none;}
  .cart-row .имя:hover{color:var(--accent);}
  /* Выбранные свойства: цвет, размер, срок. Мельче названия — их сверяют
     глазами, а не читают. */
  .cart-row .свойства{color:var(--muted);font-size:var(--fs-xs);
        display:flex;flex-wrap:wrap;gap:var(--sp-2);}
  .cart-row .наличие{font-size:var(--fs-xs);color:var(--good);}
  .cart-row .наличие.мало{color:var(--warn);}
  .cart-row .наличие.нет{color:var(--bad);}

  /* Правый край строки: цена сверху, количество под ней */
  .cart-row .справа{display:flex;flex-direction:column;align-items:flex-end;
        gap:var(--sp-2);flex:none;}
  .cart-row .цена{font-size:var(--fs-md);font-weight:700;white-space:nowrap;}
  .cart-row .цена s{display:block;font-size:var(--fs-xs);font-weight:400;
        color:var(--faint);}
  .cart-row .x{border:none;background:none;color:var(--faint);cursor:pointer;
        padding:var(--sp-1);border-radius:var(--radius-xs);line-height:1;
        min-width:var(--цель);min-height:var(--цель);
        display:grid;place-items:center;}
  .cart-row .x:hover{color:var(--bad);background:var(--bad-bg);}

  /* Строка удалённого товара: держим место, пока человек не ушёл со страницы */
  .cart-row.убран{background:var(--surface-2);}
  .cart-row.убран .имя{color:var(--muted);text-decoration:line-through;}

  /* ── Итог ──────────────────────────────────────────────────────── */
  .cart-sum{width:var(--cart-sum-w);flex:none;position:sticky;top:var(--sp-5);
        background:var(--surface);border:1px solid var(--border);
        border-radius:var(--radius-lg);padding:var(--sp-4);
        display:flex;flex-direction:column;gap:var(--sp-3);}
  .cart-sum > b{font-size:var(--fs-md);}
  .cart-sum .ст{display:flex;justify-content:space-between;gap:var(--sp-3);
        font-size:var(--fs-sm);color:var(--muted);}
  .cart-sum .ст b{color:var(--text);font-weight:600;}
  .cart-sum .ст.скидка b{color:var(--good);}
  .cart-sum hr{border:none;border-top:1px solid var(--border-soft);margin:0;}
  .cart-sum .всего{display:flex;justify-content:space-between;align-items:baseline;
        gap:var(--sp-3);}
  .cart-sum .всего span{font-size:var(--fs-sm);color:var(--muted);}
  .cart-sum .всего b{font-size:var(--fs-2xl);font-weight:700;letter-spacing:-.02em;}
  .cart-sum .мелко{font-size:var(--fs-xs);color:var(--faint);text-align:center;}

  @media (max-width:900px){
    .cart{flex-direction:column;}
    /* Итог прилипает к низу окна: правой колонки нет, а сумма нужна на виду */
    .cart-sum{width:100%;position:sticky;top:auto;bottom:0;
          border-radius:var(--radius-lg) var(--radius-lg) 0 0;
          box-shadow:var(--shadow-lg);}
    .cart-row .фото{width:var(--cart-pic-sm);height:var(--cart-pic-sm);}
  }

  /* Промокод строкой под товарами: поле и кнопки в ряд. Отдельная форма,
     а не часть корзины, — применение кода не должно зависеть от того, что
     человек ввёл в поля количества. */
  .промо-строка{background:var(--surface);border:1px solid var(--border);
        border-radius:var(--radius-lg);padding:var(--sp-3);}
  .промо-строка .ряд{display:flex;gap:var(--sp-2);align-items:center;}
  .промо-строка .ряд .field{flex:1;min-width:0;}
  .промо-строка .подсказка{display:block;margin-top:var(--sp-1);
        font-size:var(--fs-xs);color:var(--muted);}
  .промо-строка .подсказка.плохо{color:var(--bad);}
  .промо-строка .подсказка.хорошо{color:var(--good);}

  /* Строка количества и крестик стоят в одну линию: форма внутри правой
     колонки строки, и без этого счётчик уезжает под цену отдельным блоком. */
  .cart-row .справа form{display:flex;align-items:center;gap:var(--sp-2);}

  @media (max-width:620px){
    .промо-строка .ряд{flex-wrap:wrap;}
    .промо-строка .ряд .field{flex-basis:100%;}
  }

  /* ── меню-каталога.css ─────────────────────────────────────── */
/* Меню каталога: список разделов и раскрытие во всю ширину.
   Часть набора элементов. Собирается сборщиком:
   python tools/build_styles.py */

/* Два вида одного и того же дерева разделов.

   Слева на странице каталога — обычный столбец: раздел, под ним подразделы,
   текущий подсвечен. Его читают, а не выбирают вслепую.

   Из шапки — раскрытие во всю ширину: слева разделы, справа содержимое
   наведённого. Так человек видит, что внутри, не нажимая. Выпадашка в две
   ступени с наведением работает плохо: курсор соскальзывает по дороге
   и меню закрывается на полпути — здесь такого нет, потому что уходить
   курсором никуда не надо.

   Раскрытие держится на скрытом флажке, а не на :hover: открытое по наведению
   меню закрывается, стоит увести мышь на пиксель, и человек не успевает
   дочитать. Нажал — открылось, нажал ещё — закрылось. */

  /* ── Столбец разделов сбоку ─────────────────────────────────────── */
  .cat-side{width:var(--side-w);flex:none;}
  .cat-side > b{display:block;font-size:var(--fs-md);font-weight:650;
        margin-bottom:var(--sp-3);}
  .cat-side a{display:flex;align-items:center;justify-content:space-between;gap:var(--sp-2);
        min-height:max(var(--ctl-sm), var(--цель));
        padding:var(--sp-1) var(--sp-2);border-radius:var(--radius-sm);
        color:var(--text-2);text-decoration:none;font-size:var(--fs-sm);}
  .cat-side a:hover{background:var(--surface-2);color:var(--text);}
  .cat-side a.on{background:var(--accent-soft);color:var(--accent);font-weight:600;}
  /* Число товаров в разделе. Приглушено нарочно: это справка, а не ссылка. */
  .cat-side a .скок{color:var(--faint);font-size:var(--fs-xs);}
  .cat-side .под{margin-left:var(--sp-3);border-left:1px solid var(--border);
        padding-left:var(--sp-2);}

  /* ── Раскрытие во всю ширину ────────────────────────────────────── */
  /* Кнопка с раскрытием не сжимается: рядом с ней в шапке стоит поиск,
     который берёт всё свободное место, и без этого кнопка ужимается в
     полоску шириной в полтора десятка точек. Блок обязан держать форму в
     любом соседстве — иначе его нельзя просто вставить. */
  /* Своей точки отсчёта у кнопки нет: раскрытие считает края по колонке
     шапки. Прижми его к кнопке — и на узком окне лист уходит за край: слева,
     если прижат правым краем, справа, если левым. */
  /* Точку отсчёта блок держит при себе. В шапке её даёт колонка содержимого
     (ниже мы туда и возвращаемся), но блок вставляют и отдельно: на странице
     раздела, в мастерской, на чужом сайте. Без позиционированного предка
     раскрытие считало края по всей странице и уезжало на высоту экрана вниз
     — между деревом разделов и листом зияла пустая полоса, и блок выглядел
     развалившимся. */
  .mega{position:relative;flex:none;min-width:var(--ctl);}
  .shop-head .in .mega{position:static;}
  .mega > input{position:absolute;opacity:0;pointer-events:none;}
  /* Раскрытие висит под кнопкой. Ширина ограничена окном, а не только
     собственным пределом: при окне около тысячи точек лист в 920 не
     помещался справа от кнопки — вылезал за край, и страница начинала
     листаться вбок. */
  .mega > .лист{display:none;position:absolute;top:calc(100% + var(--sp-2));
        left:0;width:920px;max-width:calc(100vw - var(--sp-4) * 2);
        background:var(--surface);
        border:1px solid var(--border);border-radius:var(--radius-lg);
        box-shadow:var(--shadow-lg);overflow:hidden;z-index:40;}
  .mega > input:checked ~ .лист{display:flex;}
  /* В шапке лист по-прежнему считает края по колонке содержимого, а не по
     кнопке: при узком окне прижатый к кнопке лист упирается в границу. */
  .shop-head .in .mega > .лист{left:var(--sp-4);
        max-width:calc(100% - var(--sp-4) * 2);}

  /* Левая колонка — разделы, правая — содержимое выбранного */
  .mega .лист > .разделы{width:var(--side-w);flex:none;padding:var(--sp-2);
        background:var(--surface-2);border-right:1px solid var(--border);}
  .mega .лист > .разделы button{display:flex;align-items:center;gap:var(--sp-2);width:100%;
        min-height:var(--ctl-sm);padding:0 var(--sp-2);border:none;background:none;
        border-radius:var(--radius-sm);color:var(--text-2);cursor:pointer;
        font:inherit;font-size:var(--fs-sm);text-align:left;}
  .mega .лист > .разделы button:hover,
  .mega .лист > .разделы button.on{background:var(--surface);color:var(--text);
        font-weight:600;}
  .mega .лист > .разделы button svg{width:var(--ic-sm);height:var(--ic-sm);
        stroke:currentColor;fill:none;stroke-width:1.8;flex:none;}

  .mega .лист > .внутри{flex:1;min-width:0;padding:var(--sp-4);
        display:grid;grid-template-columns:repeat(3,1fr);gap:var(--sp-4);
        align-content:start;}
  .mega .лист > .внутри .гр > b{display:block;font-size:var(--fs-sm);
        margin-bottom:var(--sp-2);}
  /* Ссылка раздела — цель касания: по ней жмут пальцем, а не наводят мышь.
     Высота была по кеглю, около восемнадцати точек — мельче нормы. */
  .mega .лист > .внутри .гр a{display:flex;align-items:center;
        min-height:var(--цель);color:var(--muted);
        text-decoration:none;font-size:var(--fs-sm);}
  .mega .лист > .внутри .гр a:hover{color:var(--accent);}

  /* На узком экране раскрытие превращается в обычный список: две колонки
     рядом на телефоне не помещаются, а горизонтальная прокрутка внутри
     меню — верный способ потерять человека.

     Ширину считаем от ШАПКИ, а не от кнопки: кнопка на телефоне квадратная,
     и «сто процентов» от неё — это сорок точек. Меню открывалось полоской,
     в которой не было видно ни одного раздела. Убираем у .mega собственную
     точку отсчёта — тогда лист считает края от липкой строки шапки, то есть
     растягивается на всю её ширину. */
  @media (max-width:820px){
    .mega > .лист{left:var(--sp-3);right:var(--sp-3);width:auto;max-width:none;
          flex-direction:column;max-height:70dvh;overflow:auto;}
    .mega .лист > .разделы{width:auto;border-right:none;
          border-bottom:1px solid var(--border);}
    .mega .лист > .внутри{grid-template-columns:1fr;}
  }

  /* ── Дерево разделов на телефоне ──────────────────────────────────
     Столбцом оно съедает пол-экрана до первого товара: человек листает
     мимо списка, который уже видел в шапке. Поэтому на узком экране
     разделы становятся лентой — та же навигация, одна строка высоты.

     Текущий раздел подсвечен, лента листается пальцем. Подразделы в ленту
     не выносим: они уточняют выбор, а уточняют его уже внутри раздела. */
  @media (max-width:900px){
    .cat-side{width:100%;flex-direction:row;gap:var(--sp-2);
          overflow-x:auto;scrollbar-width:none;padding-bottom:var(--sp-1);}
    .cat-side::-webkit-scrollbar{display:none;}
    .cat-side > b{display:none;}
    .cat-side a{flex:none;white-space:nowrap;border:1px solid var(--border);
          border-radius:var(--pill);padding:0 var(--sp-3);}
    .cat-side a.on{border-color:var(--accent);}
    /* Подразделы в ленте не нужны: они про выбор внутри раздела */
    .cat-side .под{display:none;}
  }

  /* ── оформление.css ─────────────────────────────────────── */
/* Оформление заказа: шаги, способы доставки и оплаты.
   Часть набора элементов. Собирается сборщиком:
   python tools/build_styles.py */

/* Оформление — место, где теряют больше всего покупателей. Поэтому здесь два
   правила важнее красоты.

   Первое: видно, сколько осталось. Шаги пронумерованы и показаны сверху —
   человек соглашается заполнять три поля, когда знает, что их три, и бросает,
   когда конца не видно.

   Второе: сумма не исчезает. Правая колонка с составом заказа висит липкой на
   всех шагах — иначе на шаге оплаты человек не помнит, за что платит, и идёт
   проверять назад. Каждый такой возврат — потерянный заказ.

   Способ доставки и оплаты — крупные плашки с выбором целиком, а не точки
   в столбик: по ним попадают пальцем, и видно, что выбрано, без прищура. */

  .checkout{display:flex;gap:var(--sp-5);align-items:flex-start;}
  .checkout > .шаг{flex:1;min-width:0;display:flex;flex-direction:column;gap:var(--sp-4);}

  /* Часть шага: заголовок с номером, под ним содержимое одной карточкой */
  .checkout .часть{background:var(--surface);border:1px solid var(--border);
        border-radius:var(--radius-lg);padding:var(--sp-4);
        display:flex;flex-direction:column;gap:var(--sp-3);}
  .checkout .часть > .назв{display:flex;align-items:center;gap:var(--sp-2);
        font-size:var(--fs-md);font-weight:650;}
  .checkout .часть > .назв .ном{width:var(--box-sm);height:var(--box-sm);flex:none;
        border-radius:var(--pill);background:var(--accent);color:var(--on-accent);
        display:grid;place-items:center;font-size:var(--fs-xs);font-weight:700;}
  /* «Изменить город» — ссылка в заголовке шага, но по ней жмут: значит цель
     касания, а не просто текст. Высота была по кеглю, мельче нормы. */
  .checkout .часть > .назв .прав{margin-left:auto;font-size:var(--fs-sm);
        font-weight:400;display:inline-flex;align-items:center;
        min-height:var(--цель);}

  /* ── Плашка выбора: доставка, оплата, пункт выдачи ─────────────── */
  .pick{display:flex;gap:var(--sp-3);align-items:flex-start;padding:var(--sp-3);
        border:1px solid var(--control-line);border-radius:var(--radius);
        cursor:pointer;background:var(--surface);
        transition:border-color .12s,background .12s;}
  .pick:hover{border-color:var(--accent-line);}
  .pick > input{margin:0;flex:none;accent-color:var(--accent);
        width:var(--ic);height:var(--ic);}
  .pick .тело{flex:1;min-width:0;display:flex;flex-direction:column;gap:2px;}
  /* Значок способа доставки: размер задан здесь, потому что набор уезжает в
     другие проекты по файлам, и наш сброс туда может не поехать — в магазине
     он исключён нарочно. Без размера значок берёт двадцать четыре точки из
     viewBox и раздувается во всю плашку. */
  .pick svg{width:var(--ic);height:var(--ic);flex:none;stroke:currentColor;
        fill:none;stroke-width:1.8;color:var(--muted);}
  .pick:has(> input:checked) svg{color:var(--accent);}
  .pick .тело b{font-size:var(--fs-sm);font-weight:600;}
  .pick .тело span{color:var(--muted);font-size:var(--fs-xs);}
  .pick .срок{text-align:right;flex:none;}
  .pick .срок b{display:block;font-size:var(--fs-sm);white-space:nowrap;}
  .pick .срок span{color:var(--muted);font-size:var(--fs-xs);white-space:nowrap;}
  /* Выбранное видно и цветом, и рамкой: одним цветом его не различают на
     плохом экране и при ярком свете. */
  .pick:has(> input:checked){border-color:var(--accent);background:var(--accent-soft);}
  .pick.нельзя{opacity:.55;cursor:not-allowed;}

  .picks{display:flex;flex-direction:column;gap:var(--sp-2);}

  /* ── Состав заказа справа ──────────────────────────────────────── */
  .order-sum{width:var(--cart-sum-w);flex:none;position:sticky;top:var(--sp-5);
        background:var(--surface);border:1px solid var(--border);
        border-radius:var(--radius-lg);padding:var(--sp-4);
        display:flex;flex-direction:column;gap:var(--sp-3);}
  .order-sum > b{font-size:var(--fs-md);}
  .order-sum .тов{display:flex;gap:var(--sp-2);align-items:center;}
  .order-sum .тов .фото{width:var(--box-lg);height:var(--box-lg);flex:none;
        border-radius:var(--radius-sm);background:var(--surface-2);overflow:hidden;}
  .order-sum .тов .фото img{width:100%;height:100%;object-fit:cover;display:block;}
  .order-sum .тов .имя{flex:1;min-width:0;font-size:var(--fs-xs);
        overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
  .order-sum .тов .цена{font-size:var(--fs-xs);font-weight:600;white-space:nowrap;}
  .order-sum .ст{display:flex;justify-content:space-between;gap:var(--sp-3);
        font-size:var(--fs-sm);color:var(--muted);}
  .order-sum .ст b{color:var(--text);font-weight:600;}
  .order-sum hr{border:none;border-top:1px solid var(--border-soft);margin:0;}
  .order-sum .всего{display:flex;justify-content:space-between;align-items:baseline;}
  .order-sum .всего span{color:var(--muted);font-size:var(--fs-sm);}
  .order-sum .всего b{font-size:var(--fs-2xl);font-weight:700;letter-spacing:-.02em;}
  /* Согласие с условиями — под кнопкой, а не над ней: сначала действие,
     потом мелкий шрифт. Прятать его нельзя, требовать галочку — можно. */
  .order-sum .согласие{font-size:var(--fs-2xs);color:var(--faint);text-align:center;
        line-height:1.5;}
  .order-sum .согласие a{color:var(--muted);}

  @media (max-width:900px){
    .checkout{flex-direction:column;}
    .order-sum{width:100%;position:static;}
  }

  /* ── подвал.css ─────────────────────────────────────── */
/* Подвал сайта: колонки ссылок, контакты, нижняя строка.
   Часть набора элементов. Собирается сборщиком:
   python tools/build_styles.py */

/* Подвал читают в двух случаях: ищут телефон с адресом и ищут то, чего не
   нашли в меню — доставку, возврат, договор. Поэтому здесь не «ещё немного
   ссылок», а справочная страницы.

   Колонок четыре и они не равны: первая — про магазин (знак, короткая строка,
   контакты), остальные — списки ссылок. Равные колонки читаются как таблица,
   и глаз не понимает, с чего начать.

   Нижняя строка отделена линией: там год, правовые ссылки и способы оплаты —
   то, что обязано быть, но что никто не ищет.

   На телефоне колонки становятся раскрывашками. Развёрнутый подвал на узком
   экране — это полтора экрана прокрутки мимо цели. */

  .site-foot{background:var(--surface-2);border-top:1px solid var(--border);
        margin-top:var(--sp-6);}
  .site-foot .in{max-width:var(--shop-w);margin:0 auto;width:100%;
        padding:var(--sp-6) var(--поля-сайта);}

  .site-foot .колонки{display:grid;
        grid-template-columns:1.4fr repeat(3, 1fr);gap:var(--sp-5);}

  /* Первая колонка: кто мы и как связаться */
  .site-foot .про{display:flex;flex-direction:column;gap:var(--sp-3);}
  .site-foot .про .знак{display:flex;align-items:center;gap:var(--sp-2);
        text-decoration:none;color:var(--text);}
  .site-foot .про .знак .кв{width:var(--box);height:var(--box);flex:none;
        border-radius:var(--box-r);background:var(--accent);color:var(--on-accent);
        display:grid;place-items:center;font-weight:800;overflow:hidden;}
  .site-foot .про .знак .кв img{width:100%;height:100%;object-fit:cover;display:block;}
  .site-foot .про .знак b{font-size:var(--fs-md);letter-spacing:-.01em;}
  .site-foot .про p{color:var(--muted);font-size:var(--fs-sm);line-height:1.6;}
  .site-foot .про .тел{font-size:var(--fs-lg);font-weight:700;color:var(--text);
        text-decoration:none;}
  .site-foot .про .часы{color:var(--faint);font-size:var(--fs-xs);}

  /* Кружки со ссылками на мессенджеры */
  .site-foot .связь{display:flex;gap:var(--sp-2);}
  .site-foot .связь a{width:var(--box);height:var(--box);border-radius:var(--pill);
        background:var(--surface);border:1px solid var(--border);color:var(--text-2);
        display:grid;place-items:center;text-decoration:none;}
  .site-foot .связь a:hover{border-color:var(--accent);color:var(--accent);}
  .site-foot .связь svg{width:var(--ic);height:var(--ic);stroke:currentColor;
        fill:none;stroke-width:1.8;}

  /* Колонка ссылок */
  .site-foot .гр > summary{display:flex;align-items:center;
        min-height:var(--цель);font-size:var(--fs-sm);font-weight:650;
        margin-bottom:var(--sp-3);}
  /* Ссылки подвала — цели касания: их читают и по ним жмут, часто с
     телефона в дороге. Высота по кеглю (около восемнадцати точек) мельче
     нормы, и в такую ссылку не попадают. */
  .site-foot .гр a{display:flex;align-items:center;min-height:var(--цель);
        color:var(--muted);text-decoration:none;font-size:var(--fs-sm);}
  .site-foot .гр a:hover{color:var(--accent);}
  /* Группа ссылок размечается тегом <details>: на телефоне она сворачивается
     САМА, без единой строки скрипта. Раньше раскрытие держалось на классе,
     который ставила страница, — блок нельзя было просто вставить в новый
     сайт: ссылки в подвале не открывались вовсе, пока не напишешь обработчик.
     Блок обязан работать сам по себе, иначе он не блок, а заготовка.

     На широком экране раскрытие отключается: там колонки видны целиком, и
     стрелка сворачивания врала бы. */
  .site-foot .гр > summary{list-style:none;}
  .site-foot .гр > summary::-webkit-details-marker{display:none;}
  .site-foot .гр > summary .стрелка{display:none;}
  .site-foot details.гр > .ссылки{display:block;}
  /* Одной строки выше уже мало. Раньше закрытый <details> прятал содержимое
     через display, и поставить потомку display:block значило раскрыть его.
     Chrome со 131-й версии прячет иначе — content-visibility на слоте
     ::details-content, — и приём перестал работать молча: на широком экране
     колонки ссылок пропали целиком, а место под них осталось занятым.
     Поэтому раскрываем сам слот. Строку выше не убираем: браузеры, где
     ::details-content ещё нет, держатся на ней. */
  .site-foot details.гр::details-content{content-visibility:visible;
        block-size:auto;}
  /* Заголовок на широком экране нажимать незачем: колонка и так открыта. */
  .site-foot .гр > summary{cursor:default;}

  /* ── Нижняя строка ─────────────────────────────────────────────── */
  .site-foot .низ{display:flex;align-items:center;gap:var(--sp-4);flex-wrap:wrap;
        margin-top:var(--sp-6);padding-top:var(--sp-4);
        border-top:1px solid var(--border);color:var(--faint);
        font-size:var(--fs-xs);}
  .site-foot .низ a{display:inline-flex;align-items:center;
        min-height:var(--цель);color:var(--faint);text-decoration:none;}
  .site-foot .низ a:hover{color:var(--text-2);text-decoration:underline;}
  .site-foot .низ .оплата{margin-left:auto;display:flex;gap:var(--sp-2);
        align-items:center;}
  /* Значки платёжных систем: рамка, а не заливка — цветные логотипы на сером
     фоне выглядят наклейками. */
  .site-foot .низ .оплата span{height:var(--ctl-sm);min-width:calc(var(--ctl-sm) * 1.6);
        padding:0 var(--sp-2);border:1px solid var(--border);border-radius:var(--radius-xs);
        background:var(--surface);display:grid;place-items:center;
        font-size:var(--fs-2xs);font-weight:600;color:var(--muted);}

  @media (max-width:900px){
    .site-foot .колонки{grid-template-columns:1fr;gap:var(--sp-4);}
    /* Колонки ссылок сворачиваются, первая колонка остаётся открытой:
       телефон прятать нельзя, за ним и приходят. */
    .site-foot .гр{border-bottom:1px solid var(--border);padding-bottom:var(--sp-3);}
    /* У последней группы черта лишняя: ниже своя линия у нижней строки, и две
       подряд читаются как промах вёрстки, а не как разделение. */
    .site-foot .гр:last-of-type{border-bottom:none;padding-bottom:0;}
    .site-foot .гр > summary{display:flex;align-items:center;justify-content:space-between;
          cursor:pointer;margin-bottom:0;min-height:var(--ctl);}
    .site-foot .гр > summary .стрелка{display:block;color:var(--faint);}
    .site-foot .гр > summary .стрелка svg{width:var(--ic);height:var(--ic);
          stroke:currentColor;fill:none;stroke-width:2;transition:transform .15s;}
    /* Свёрнуто по умолчанию, раскрывает сам браузер по нажатию на заголовок */
    .site-foot details.гр > .ссылки{display:none;padding-top:var(--sp-2);}
    .site-foot details.гр[open] > .ссылки{display:block;}
    /* Раскрытие слота, снятое выше ради широкого экрана, здесь возвращаем:
       на телефоне свёрнутая группа обязана быть свёрнутой и не занимать
       места. Открытая берёт раскрытие из правила выше. */
    .site-foot details.гр:not([open])::details-content{
          content-visibility:hidden;block-size:0;}
    .site-foot details.гр[open] > summary .стрелка svg{transform:rotate(180deg);}
    .site-foot .низ .оплата{margin-left:0;}
  }

  /* ── полоса-акции.css ─────────────────────────────────────── */
/* Верхняя полоса: акция, срок доставки, важное объявление.
   Часть набора элементов. Собирается сборщиком:
   python tools/build_styles.py */

/* Самая верхняя строка сайта, над шапкой. Годится ровно для одного
   сообщения: скидка, бесплатная доставка, перенос выходных. Два сообщения
   подряд перестают читать оба.

   Крестик обязателен. Полоса, которую нельзя закрыть, воспринимается как
   реклама и портит впечатление от всего остального; закрытая — запоминается
   лучше, потому что человек сам решил.

   Не липнет к верху. Липкая полоса съедает высоту экрана на телефоне и
   мешает читать то, ради чего пришли. */

  .promo-bar{display:flex;align-items:center;justify-content:center;
        gap:var(--sp-3);min-height:var(--ctl-sm);padding:var(--sp-1) var(--sp-4);
        background:var(--accent);color:var(--on-accent);
        font-size:var(--fs-sm);text-align:center;position:relative;}
  .promo-bar b{font-weight:650;}
  .promo-bar a{color:inherit;text-decoration:underline;text-underline-offset:2px;}
  /* Промокод — моноширинным: его переписывают вручную, и в обычном шрифте
     ноль путают с буквой О. */
  .promo-bar .код{font-family:var(--mono);font-weight:700;
        background:color-mix(in oklab, var(--on-accent) 18%, transparent);
        padding:2px var(--sp-2);border-radius:var(--radius-xs);
        letter-spacing:.04em;}
  .promo-bar .срок{opacity:.85;font-size:var(--fs-xs);}
  /* Крестик — цель касания, а не значок: по нему жмут пальцем на телефоне.
     Меньше нормы (24 на 24) в него не попадают и закрывают полосу со
     второго-третьего раза, злясь на каждый промах. */
  .promo-bar .x{position:absolute;right:var(--sp-3);border:none;background:none;
        color:inherit;opacity:.7;cursor:pointer;padding:var(--sp-1);
        line-height:1;border-radius:var(--radius-xs);
        min-width:var(--цель);min-height:var(--цель);
        display:grid;place-items:center;}
  .promo-bar .x:hover{opacity:1;
        background:color-mix(in oklab, var(--on-accent) 15%, transparent);}

  /* Тёмная: когда акцент уже занят шапкой и две цветные полосы подряд рябят */
  .promo-bar.тёмная{background:var(--text);color:var(--surface);}
  /* Тихая: служебное объявление, а не акция */
  .promo-bar.тихая{background:var(--surface-3);color:var(--text-2);}
  .promo-bar.тихая .код{background:var(--surface);}

  @media (max-width:640px){
    /* Срок акции уходит: на телефоне важнее сам повод, а дату уточнят
       на странице акции. Крестик остаётся — иначе полосу не закрыть. */
    .promo-bar .срок{display:none;}
    .promo-bar{padding-right:var(--sp-6);font-size:var(--fs-xs);}
  }

  /* ── сортировка.css ─────────────────────────────────────── */
/* Строка над каталогом: сортировка, вид сетки, число товаров.
   Часть набора элементов. Собирается сборщиком:
   python tools/build_styles.py */

/* Полоса между заголовком раздела и товарами. Отвечает на три вопроса сразу:
   сколько нашлось, в каком порядке показано, каким размером.

   Число товаров стоит слева и словами («1 248 товаров»), а не голой цифрой:
   цифра рядом с названием раздела читается как артикул.

   Сортировка — выпадающий список, а не ряд кнопок. Способов пять-шесть, ряд
   кнопок займёт всю строку и вытеснит остальное; из списка же видно текущий
   порядок, не раскрывая его.

   Переключатель вида (плиткой или списком) справа и без подписи: это
   единственное место, где значок понятен без слов. */

  .sort-bar{display:flex;align-items:center;gap:var(--sp-3);flex-wrap:wrap;
        padding:var(--sp-3) 0;}
  .sort-bar .скок{color:var(--muted);font-size:var(--fs-sm);}
  .sort-bar .скок b{color:var(--text);font-weight:600;}
  .sort-bar .право{margin-left:auto;display:flex;align-items:center;gap:var(--sp-2);}
  .sort-bar .подпись{color:var(--muted);font-size:var(--fs-sm);white-space:nowrap;}

  /* Кнопка «Фильтры» видна только на узком экране: на широком фильтры стоят
     столбцом слева, и кнопка вела бы в шторку с тем же самым. */
  .sort-bar .фильтры-кн{display:none;}
  .sort-bar .фильтры-кн .счёт{min-width:var(--счёт);height:var(--счёт);padding:0 var(--sp-1);
        border-radius:var(--pill);background:var(--accent);color:var(--on-accent);
        font-size:var(--fs-счёт);font-weight:700;display:grid;place-items:center;
        margin-left:var(--sp-1);}

  /* Вид сетки: две кнопки-значка, слипшиеся в один блок */
  .view-switch{display:inline-flex;border:1px solid var(--control-line);
        border-radius:var(--radius-sm);overflow:hidden;flex:none;}
  .view-switch button{width:var(--ctl);height:var(--ctl-sm);border:none;
        background:var(--surface);color:var(--muted);cursor:pointer;
        display:grid;place-items:center;}
  .view-switch button + button{border-left:1px solid var(--control-line);}
  .view-switch button:hover{color:var(--text);background:var(--surface-2);}
  .view-switch button.on{background:var(--accent-soft);color:var(--accent);}
  .view-switch svg{width:var(--ic-sm);height:var(--ic-sm);stroke:currentColor;
        fill:none;stroke-width:1.9;}

  @media (max-width:900px){
    .sort-bar .фильтры-кн{display:inline-flex;}
    /* Подпись «Сортировка:» уходит — сам список остаётся, и он говорит за себя */
    .sort-bar .подпись{display:none;}
    .view-switch{display:none;}
  }

  /* ── статья.css ─────────────────────────────────────── */
/* Блог: карточка статьи, страница статьи, автор, оглавление.
   Часть набора элементов. Собирается сборщиком:
   python tools/build_styles.py */

/* Статьи держат людей на сайте и приводят их из поиска, поэтому магазину
   без блога не обойтись. Здесь всё, что для него нужно.

   Главное правило страницы статьи — ширина строки. Текст в шестьдесят-
   семьдесят знаков читается, во всю ширину монитора — нет: глаз теряет
   начало следующей строки. Поэтому колонка узкая, а картинки и код могут
   выходить за её края.

   Оглавление сбоку и липкое: в длинной статье оно заменяет полосу
   прокрутки — видно, где ты и сколько осталось. */

  /* ── Карточка статьи в списке ──────────────────────────────────── */
  .post-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:var(--sp-4);}
  .post{display:flex;flex-direction:column;background:var(--surface);
        border:1px solid var(--border);border-radius:var(--radius-lg);
        overflow:hidden;text-decoration:none;color:inherit;
        transition:box-shadow .15s,transform .15s;}
  .post:hover{box-shadow:var(--shadow-lg);transform:translateY(-2px);}
  .post .фото{position:relative;background:var(--surface-2);}
  .post .фото::before{content:"";display:block;padding-top:56%;}
  .post .фото img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;}
  .post .фото .нет{position:absolute;inset:0;display:grid;place-items:center;
        color:var(--faint);font-size:var(--fs-xs);}
  .post .тело{padding:var(--sp-4);display:flex;flex-direction:column;
        gap:var(--sp-2);flex:1;}
  .post .тема{color:var(--accent);font-size:var(--fs-2xs);font-weight:700;
        text-transform:uppercase;letter-spacing:.06em;}
  .post h3{font-size:var(--fs-md);line-height:1.35;letter-spacing:-.01em;}
  .post p{color:var(--muted);font-size:var(--fs-sm);line-height:1.6;}
  /* Низ карточки прижат книзу: заголовки разной длины иначе разводят
     подписи по разной высоте, и ряд выглядит кривым. */
  .post .низ{margin-top:auto;display:flex;align-items:center;gap:var(--sp-2);
        color:var(--faint);font-size:var(--fs-xs);}
  .post .низ .автор{display:flex;align-items:center;gap:var(--sp-2);}
  .post .низ .автор .лицо{width:var(--box-sm);height:var(--box-sm);flex:none;
        border-radius:var(--pill);background:var(--surface-2);overflow:hidden;}
  .post .низ .автор .лицо img{width:100%;height:100%;object-fit:cover;display:block;}
  .post .низ .срок{margin-left:auto;white-space:nowrap;}

  /* Первая статья крупнее: карточка разворачивается в строку */
  .post.главная{grid-column:span 2;flex-direction:row;}
  .post.главная .фото{flex:1;min-width:0;}
  .post.главная .фото::before{padding-top:0;height:100%;}
  .post.главная .тело{flex:1;justify-content:center;gap:var(--sp-3);}
  .post.главная h3{font-size:var(--fs-xl);}

  /* ── Страница статьи ───────────────────────────────────────────── */
  .article{display:flex;gap:var(--sp-6);align-items:flex-start;
        max-width:var(--shop-w);margin:0 auto;padding:0 var(--sp-4);}
  .article > .текст{flex:1;min-width:0;max-width:var(--узк-w);}
  .article h1{font-size:var(--fs-2xl);line-height:1.2;letter-spacing:-.025em;
        margin-bottom:var(--sp-3);}
  .article .над{display:flex;align-items:center;gap:var(--sp-3);
        color:var(--muted);font-size:var(--fs-sm);margin-bottom:var(--sp-4);
        flex-wrap:wrap;}
  .article .обложка{width:100%;border-radius:var(--radius-lg);overflow:hidden;
        margin-bottom:var(--sp-5);background:var(--surface-2);}
  .article .обложка img{width:100%;display:block;}

  /* Сам текст. Кегль крупнее интерфейсного: читают подряд, а не выхватывают. */
  .article .тело{font-size:var(--fs-md);line-height:1.75;color:var(--text-2);}
  .article .тело > * + *{margin-top:var(--sp-4);}
  .article .тело h2{font-size:var(--fs-xl);color:var(--text);line-height:1.3;
        letter-spacing:-.02em;margin-top:var(--sp-6);}
  .article .тело h3{font-size:var(--fs-lg);color:var(--text);margin-top:var(--sp-5);}
  /* В тексте статьи маркеры возвращаем: сброс убирает их у всех списков,
     а перечисление без точек читается как сплошной абзац. */
  .article .тело ul{list-style:disc;padding-left:var(--sp-5);}
  .article .тело ol{list-style:decimal;padding-left:var(--sp-5);}
  .article .тело li + li{margin-top:var(--sp-2);}
  .article .тело a{color:var(--accent);}
  .article .тело img{width:100%;border-radius:var(--radius);display:block;}
  /* Цитата — линией слева, без кавычек-картинок: они спорят с текстом */
  .article .тело blockquote{border-left:2px solid var(--accent);
        padding-left:var(--sp-4);color:var(--text);font-size:var(--fs-lg);
        line-height:1.6;}
  .article .тело code{font-family:var(--mono);font-size:.92em;
        background:var(--surface-2);padding:2px var(--sp-1);
        border-radius:var(--radius-xs);}
  .article .тело pre{background:var(--surface-2);border:1px solid var(--border);
        border-radius:var(--radius);padding:var(--sp-4);overflow-x:auto;
        font-family:var(--mono);font-size:var(--fs-sm);line-height:1.6;}
  .article .тело pre code{background:none;padding:0;}

  /* ── Оглавление сбоку ──────────────────────────────────────────── */
  .toc{width:var(--side-w);flex:none;position:sticky;top:var(--sp-5);
        display:flex;flex-direction:column;gap:var(--sp-2);}
  .toc > b{font-size:var(--fs-xs);color:var(--faint);text-transform:uppercase;
        letter-spacing:.06em;}
  .toc a{display:block;padding:var(--sp-1) 0 var(--sp-1) var(--sp-3);
        border-left:2px solid var(--border);color:var(--muted);
        text-decoration:none;font-size:var(--fs-sm);line-height:1.45;}
  .toc a:hover{color:var(--text);border-color:var(--control-line);}
  .toc a.on{color:var(--accent);border-color:var(--accent);font-weight:600;}
  .toc a.под{padding-left:var(--sp-5);font-size:var(--fs-xs);}

  /* ── Автор под статьёй ─────────────────────────────────────────── */
  .author{display:flex;gap:var(--sp-4);align-items:flex-start;
        background:var(--surface-2);border-radius:var(--radius-lg);
        padding:var(--sp-4);margin-top:var(--sp-6);}
  .author .лицо{width:var(--box-xl);height:var(--box-xl);flex:none;
        border-radius:var(--pill);background:var(--surface-3);overflow:hidden;
        display:grid;place-items:center;font-weight:600;color:var(--muted);}
  .author .лицо img{width:100%;height:100%;object-fit:cover;display:block;}
  .author .тело{flex:1;min-width:0;display:flex;flex-direction:column;gap:var(--sp-1);}
  .author .тело b{font-size:var(--fs-sm);}
  .author .тело .роль{color:var(--faint);font-size:var(--fs-xs);}
  .author .тело p{color:var(--muted);font-size:var(--fs-sm);line-height:1.6;}

  @media (max-width:1080px){
    /* Оглавление уходит первым: колонка текста важнее, а по длинной статье
       на планшете листают прокруткой. */
    .toc{display:none;}
  }
  @media (max-width:760px){
    .post-grid{grid-template-columns:1fr;}
    .post.главная{grid-column:auto;flex-direction:column;}
    .post.главная .фото::before{padding-top:56%;height:auto;}
    .article .тело{font-size:var(--fs-base);}
  }

  /* ── страница.css ─────────────────────────────────────── */
/* Полотно страницы магазина: колонка содержимого и ритм между блоками.
   Часть набора элементов. Собирается сборщиком:
   python tools/build_styles.py */

/* Один блок ничего не решает — решает ритм между ними. Раньше каждая секция
   задавала свои поля, и страница читалась рваными кусками разной высоты:
   где-то блоки слипались, где-то между ними зиял экран пустоты.

   Здесь ровно две вещи: ширина колонки и расстояние между блоками. Ничего
   больше страница знать не должна — всё остальное приносят сами блоки.

   Блоки во всю ширину окна (первый экран с заливкой, подвал, полоса акции)
   ставятся ВНЕ этой колонки: они тянутся от края до края, а их содержимое
   выравнивается по той же ширине своими средствами. */

  .страница{max-width:var(--shop-w);margin:0 auto;width:100%;
        padding:var(--sp-5) var(--поля-сайта);
        display:flex;flex-direction:column;gap:var(--sp-6);}

  /* Блок, которому нужна вся ширина окна внутри колонки: уводим его за края
     ровно на то поле, что колонка задала. Так первый экран с заливкой не
     обрезается по краям, а сетка товаров рядом остаётся в колонке. */
  .страница > .во-всю-ширину{margin-left:calc(var(--поля-сайта) * -1);
        margin-right:calc(var(--поля-сайта) * -1);}

  /* Страница с боковой колонкой: фильтры или меню кабинета слева, содержимое
     справа. Ширину боковой задаёт сам блок — здесь только раскладка. */
  .страница.с-боковой{display:grid;grid-template-columns:auto 1fr;
        gap:var(--sp-5);align-items:start;}

  @media (max-width:900px){
    .страница.с-боковой{grid-template-columns:1fr;}
  }
  @media (max-width:620px){
    .страница{padding:var(--sp-4) var(--поля-сайта);gap:var(--sp-5);}
  }

  /* ── страницы-состояний.css ─────────────────────────────────────── */
/* Страницы состояний: ошибка, пусто, заказ принят, вход.
   Часть набора элементов. Собирается сборщиком:
   python tools/build_styles.py */

/* Четыре страницы, которые в магазине делают последними и зря: на них
   попадают в худшую минуту — когда что-то не нашлось, сломалось или когда
   человек только что отдал деньги.

   Устроены одинаково: крупный знак, короткий заголовок, строка объяснения,
   одно понятное действие. Разница только в тоне.

   На странице ошибки не пишем «Ошибка 404»: это код для нас, а человеку
   нужно знать, что делать дальше. Номер оставляем мелко внизу — по нему
   он опишет проблему в поддержке.

   На странице «Заказ принят» первым делом номер заказа, а не благодарность:
   его записывают или снимают на телефон. Спасибо — строкой ниже. */

  .state-page{min-height:60vh;display:flex;flex-direction:column;
        align-items:center;justify-content:center;text-align:center;
        gap:var(--sp-3);padding:var(--sp-6) var(--sp-4);}
  .state-page .зн{width:var(--лицо);height:var(--лицо);border-radius:var(--pill);
        background:var(--surface-2);color:var(--faint);display:grid;
        place-items:center;margin-bottom:var(--sp-2);}
  .state-page .зн svg{width:var(--box-lg);height:var(--box-lg);
        stroke:currentColor;fill:none;stroke-width:1.4;}
  .state-page h1{font-size:var(--fs-2xl);letter-spacing:-.02em;line-height:1.25;}
  .state-page p{color:var(--muted);font-size:var(--fs-md);line-height:1.6;
        max-width:52ch;}
  .state-page .кнопки{display:flex;gap:var(--sp-2);flex-wrap:wrap;
        justify-content:center;margin-top:var(--sp-2);}
  .state-page .код{color:var(--faint);font-size:var(--fs-xs);
        font-family:var(--mono);margin-top:var(--sp-4);}

  /* Успех: знак зелёный, а не серый — это единственное состояние, где цвет
     несёт смысл сам по себе. */
  .state-page.успех .зн{background:var(--good-bg);color:var(--good);}
  .state-page.успех .зн svg{stroke-width:2;}
  /* Номер заказа крупно и моноширинным: его переписывают. */
  .state-page .номер{font-family:var(--mono);font-size:var(--fs-2xl);
        font-weight:700;letter-spacing:.02em;background:var(--surface-2);
        padding:var(--sp-2) var(--sp-4);border-radius:var(--radius);}

  /* Беда: знак красный. Используем только для настоящих поломок, а не для
     «ничего не найдено» — иначе к красному привыкают и перестают замечать. */
  .state-page.беда .зн{background:var(--bad-bg);color:var(--bad);}

  /* Пусто: то же самое, но встроенное в страницу, а не на весь экран */
  .state-page.внутри{min-height:0;padding:var(--sp-6) var(--sp-4);}
  .state-page.внутри h1{font-size:var(--fs-lg);}
  .state-page.внутри p{font-size:var(--fs-sm);}

  /* Подсказки под пустым результатом поиска: что попробовать. Список
     конкретных действий работает лучше, чем «попробуйте изменить запрос». */
  .state-page .советы{display:flex;flex-direction:column;gap:var(--sp-1);
        color:var(--muted);font-size:var(--fs-sm);text-align:left;
        background:var(--surface-2);border-radius:var(--radius);
        padding:var(--sp-3) var(--sp-4);margin-top:var(--sp-2);}
  .state-page .советы b{color:var(--text);font-size:var(--fs-xs);
        margin-bottom:var(--sp-1);}

  /* ── Вход и регистрация ────────────────────────────────────────── */
  /* Карточка по центру экрана. Ширину не растягиваем: форма из двух полей
     на всю ширину монитора выглядит потерянной. */
  .auth-page{min-height:100vh;display:grid;place-items:center;
        padding:var(--sp-5) var(--sp-4);background:var(--bg);}
  /* Та же карточка внутри обычной страницы — с шапкой сверху и подвалом
     снизу. Высота в целый экран там лишняя: страница и так длиннее, а из-за
     неё под карточкой зияло пустое поле в рост монитора. Раньше это гасили
     стилем прямо в разметке каждой страницы; теперь это вид блока. */
  .auth-page.в-странице{min-height:0;padding:0;background:transparent;}
  .auth-card{width:100%;max-width:calc(var(--чит-w) * 0.62);
        background:var(--surface);border:1px solid var(--border);
        border-radius:var(--radius-lg);padding:var(--sp-5);
        display:flex;flex-direction:column;gap:var(--sp-4);
        box-shadow:var(--shadow-md);}
  .auth-card .знак{display:flex;flex-direction:column;align-items:center;
        gap:var(--sp-2);text-align:center;}
  .auth-card .знак .кв{width:var(--box-lg);height:var(--box-lg);
        border-radius:var(--box-lg-r);background:var(--accent);
        color:var(--on-accent);display:grid;place-items:center;
        font-weight:800;overflow:hidden;}
  .auth-card .знак .кв img{width:100%;height:100%;object-fit:cover;display:block;}
  .auth-card .знак h1{font-size:var(--fs-lg);}
  .auth-card .знак p{color:var(--muted);font-size:var(--fs-sm);}
  .auth-card form{display:flex;flex-direction:column;gap:var(--sp-3);}
  /* Строка «Забыли пароль» — рядом с подписью поля, а не под кнопкой: под
     кнопкой её принимают за продолжение согласия и не замечают. */
  .auth-card .строка-поля{display:flex;align-items:baseline;
        justify-content:space-between;gap:var(--sp-2);}
  .auth-card .строка-поля a{font-size:var(--fs-xs);color:var(--accent);
        text-decoration:none;display:inline-flex;align-items:center;
        min-height:var(--цель);}
  .auth-card .разделитель{display:flex;align-items:center;gap:var(--sp-3);
        color:var(--faint);font-size:var(--fs-xs);}
  .auth-card .разделитель::before, .auth-card .разделитель::after{
        content:"";flex:1;height:1px;background:var(--border);}
  /* Вход через чужую службу: Телеграм, Яндекс, ВКонтакте. Кнопки стоят
     столбиком и одинаковой ширины — их читают глазами сверху вниз, выбирая
     свою, а в ряд они сжимаются до нечитаемых огрызков.

     Добавить ещё один способ — дописать кнопку сюда, больше ничего: значок
     слева, подпись по центру строки, размеры общие. */
  .auth-card .другие{display:flex;flex-direction:column;gap:var(--sp-2);}
  .auth-card .другие .btn{width:100%;justify-content:center;gap:var(--sp-2);}
  .auth-card .другие .btn svg{width:var(--ic);height:var(--ic);flex:none;
        stroke:currentColor;fill:none;stroke-width:1.8;
        stroke-linecap:round;stroke-linejoin:round;}
  .auth-card .низ{text-align:center;color:var(--muted);font-size:var(--fs-sm);}
  .auth-card .низ a{color:var(--accent);text-decoration:none;font-weight:600;
        display:inline-flex;align-items:center;min-height:var(--цель);}
  /* Приписка под карточкой: «покупать можно и без входа», «данные не
     передаём». Не часть формы, поэтому мельче и приглушённее низа. Класс
     заведён здесь, а не выписан стилем в разметке страницы: инлайновый
     стиль мимо набора живёт своей жизнью и при смене темы остаётся прежним. */
  .auth-card .мелко{display:block;text-align:center;color:var(--faint);
        font-size:var(--fs-xs);line-height:1.5;}

  /* ── товар-карточка.css ─────────────────────────────────────── */
/* Карточка отвечает на четыре вопроса, и порядок здесь важнее красоты:
   что это (картинка), как называется, сколько стоит, можно ли купить сейчас.
   Всё остальное — размеры, состав, отзывы — на странице товара.

   Цену не прячем и не делаем мелкой: она вторая по важности после картинки,
   и человек ищет её глазами сразу. Старую цену ставим рядом зачёркнутой,
   а не вместо новой. */

  .prod{display:flex;flex-direction:column;background:var(--surface);
        border:1px solid var(--border);border-radius:var(--radius-lg);overflow:hidden;
        transition:box-shadow .15s,transform .15s;position:relative;}
  .prod:hover{box-shadow:var(--shadow-lg);transform:translateY(-2px);}
  .prod > a{display:flex;flex-direction:column;text-decoration:none;color:inherit;flex:1;}

  /* Картинка: квадрат, чтобы сетка не рвалась при разной высоте фотографий */
  .prod .фото{position:relative;background:var(--surface-2);overflow:hidden;}
  .prod .фото::before{content:"";display:block;padding-top:100%;}
  .prod .фото img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;}
  .prod .фото .нет{position:absolute;inset:0;display:grid;place-items:center;
        color:var(--faint);font-size:var(--fs-xs);}

  /* Метки поверх фото: скидка, новинка, «осталось два» */
  .prod .метки{position:absolute;top:var(--sp-2);left:var(--sp-2);display:flex;
        flex-direction:column;gap:var(--sp-1);align-items:flex-start;z-index:1;}
  .prod .метки .badge{background:var(--accent);color:var(--on-accent);}
  .prod .метки .badge.мало{background:var(--warn);color:#fff;}

  /* В избранное — в правом верхнем углу, поверх фото */
  .prod .сердце{position:absolute;top:var(--sp-2);right:var(--sp-2);z-index:1;
        width:var(--ctl-sm);height:var(--ctl-sm);border-radius:50%;border:none;
        background:rgba(255,255,255,.9);color:var(--muted);cursor:pointer;
        display:grid;place-items:center;box-shadow:var(--shadow-sm);}
  .prod .сердце:hover{color:var(--bad);}
  .prod .сердце.on{color:var(--bad);}
  .prod .сердце svg{width:var(--ic-sm);height:var(--ic-sm);stroke:currentColor;
        fill:none;stroke-width:1.8;}
  .prod .сердце.on svg{fill:currentColor;}

  .prod .тело{padding:var(--sp-3);display:flex;flex-direction:column;gap:var(--sp-2);flex:1;}
  .prod .имя{font-size:var(--fs-base);font-weight:550;line-height:1.4;
        display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;}
  .prod .оценка{display:flex;align-items:center;gap:var(--sp-1);color:var(--muted);
        font-size:var(--fs-xs);}
  /* Размер из ряда значков, а не числом: тринадцать точек были подобраны
     на глаз и разъезжались с соседней строкой при смене кегля. */
  .prod .оценка svg{width:var(--ic-sm);height:var(--ic-sm);fill:var(--warn);}
  .prod .цена{display:flex;align-items:baseline;gap:var(--sp-2);margin-top:auto;}
  .prod .цена b{font-size:var(--fs-lg);font-weight:650;font-variant-numeric:tabular-nums;}
  .prod .цена s{color:var(--faint);font-size:var(--fs-sm);}
  .prod .низ{padding:0 var(--sp-3) var(--sp-3);display:flex;gap:var(--sp-2);}
  .prod .низ .btn{flex:1;}
  /* Нет в наличии: карточка гаснет, но остаётся кликабельной — человек
     всё равно хочет посмотреть, что это было. */
  .prod.нет-в-наличии .фото img{filter:grayscale(1);opacity:.6;}
  .prod.нет-в-наличии .цена b{color:var(--muted);}

  /* ── Сетка каталога ── */
  .prod-grid{display:grid;gap:var(--sp-3);
        grid-template-columns:repeat(auto-fill,minmax(220px,1fr));}
  @media (max-width:620px){
    /* На телефоне две колонки, а не одна: одна колонка превращает каталог
       в бесконечную ленту, и до третьего товара никто не долистывает. */
    .prod-grid{grid-template-columns:repeat(2,minmax(0,1fr));gap:var(--sp-2);}
    .prod .тело{padding:var(--sp-2);}
    .prod .низ{padding:0 var(--sp-2) var(--sp-2);}
    .prod .имя{font-size:var(--fs-sm);}
  }

  /* ── Строкой: для избранного и «вы смотрели» ── */
  .prod.line{flex-direction:row;align-items:center;}
  .prod.line > a{flex-direction:row;align-items:center;gap:var(--sp-3);flex:1;}
  .prod.line .фото{width:var(--box-xl);flex:none;border-radius:var(--radius-sm);}
  .prod.line .фото::before{padding-top:100%;}
  .prod.line .тело{padding:var(--sp-3) 0;}
  .prod.line .низ{padding:var(--sp-3);}

  /* ── товар-страница.css ─────────────────────────────────────── */
/* Страница товара: галерея, варианты, покупка.
   Часть набора элементов. Собирается сборщиком:
   python tools/build_styles.py */

/* Страница товара решает один спор: купить сейчас или уйти думать. Всё, что
   отвечает на «а точно ли то самое», должно попасть на первый экран — фото,
   название, цена, наличие, кнопка. Описание, состав и отзывы — ниже.

   Галерея слева и липкая: человек листает описание и продолжает смотреть на
   товар. Отвязанная галерея заставляет прокручивать вверх-вниз, сверяя текст
   с картинкой.

   Блок покупки справа тоже липкий — по той же причине: кнопка «В корзину»
   не должна уезжать за край, когда человек дочитал состав и решился.

   Варианты (цвет, размер) — плашками, а не выпадающим списком. Из списка не
   видно, чего нет в наличии, а перебирать варианты по одному — работа. */

  /* Раскладка держится на переносе, а не только на ширине окна. Блок могут
     вставить в колонку уже самого окна — в каталог, в предпросмотр, в сетку
     из двух товаров. Медиа-запрос про это не знает: он меряет окно, а не
     место, которое блоку досталось. Поэтому колонкам заданы наименьшие
     ширины: не поместились — переносятся сами. */
  .prod-page{display:flex;gap:var(--sp-5);align-items:flex-start;flex-wrap:wrap;}

  /* ── Галерея ───────────────────────────────────────────────────── */
  .gallery{width:var(--gal-w);max-width:100%;flex:none;position:sticky;
        top:var(--sp-5);display:flex;gap:var(--sp-3);}
  /* Столбик мелких снимков слева от главного: под ним не прячется размер
     главного фото, и порядок кадров виден целиком. */
  .gallery .кадры{display:flex;flex-direction:column;gap:var(--sp-2);flex:none;}
  .gallery .кадры button{width:var(--box-xl);height:var(--box-xl);padding:0;
        border:1px solid var(--border);border-radius:var(--radius-sm);
        background:var(--surface-2);cursor:pointer;overflow:hidden;
        display:grid;place-items:center;color:var(--faint);font-size:var(--fs-2xs);}
  .gallery .кадры button img{width:100%;height:100%;object-fit:cover;display:block;}
  .gallery .кадры button.on{border-color:var(--accent);
        box-shadow:0 0 0 1px var(--accent);}

  .gallery .главное{flex:1;min-width:0;position:relative;background:var(--surface-2);
        border-radius:var(--radius-lg);overflow:hidden;}
  .gallery .главное::before{content:"";display:block;padding-top:100%;}
  .gallery .главное img{position:absolute;inset:0;width:100%;height:100%;
        object-fit:cover;}
  .gallery .главное .нет{position:absolute;inset:0;display:grid;place-items:center;
        color:var(--faint);font-size:var(--fs-sm);}
  .gallery .главное .метки{position:absolute;top:var(--sp-3);left:var(--sp-3);
        display:flex;flex-direction:column;gap:var(--sp-1);align-items:flex-start;}

  /* ── Середина: название, свойства, описание ────────────────────── */
  /* Наименьшая ширина середины — та, при которой название товара ещё читается
     строками, а не столбиком по слову. Уже неё колонка уходит на новую строку. */
  .prod-page > .середина{flex:1;min-width:var(--сред-мин);display:flex;
        flex-direction:column;gap:var(--sp-4);}
  .prod-page h1{font-size:var(--fs-2xl);line-height:1.25;letter-spacing:-.02em;}
  .prod-page .строка-оценки{display:flex;align-items:center;gap:var(--sp-3);
        flex-wrap:wrap;font-size:var(--fs-sm);color:var(--muted);}
  /* Ссылка «4,5 · 34 отзыва» ведёт к отзывам, по ней жмут — значит цель. */
  .prod-page .строка-оценки a{display:inline-flex;align-items:center;
        min-height:var(--цель);}
  .prod-page .строка-оценки .арт{color:var(--faint);}

  /* Варианты: цвет, размер, объём */
  .vars{display:flex;flex-direction:column;gap:var(--sp-3);}
  .vars .гр > b{display:block;font-size:var(--fs-sm);margin-bottom:var(--sp-2);}
  .vars .гр > b span{color:var(--muted);font-weight:400;}
  .vars .ряд{display:flex;flex-wrap:wrap;gap:var(--sp-2);}
  .vars .вар{min-width:var(--ctl);height:var(--ctl);padding:0 var(--sp-3);
        border:1px solid var(--control-line);border-radius:var(--radius-sm);
        background:var(--surface);color:var(--text-2);cursor:pointer;
        font-size:var(--fs-sm);display:inline-flex;align-items:center;
        justify-content:center;gap:var(--sp-1);}
  .vars .вар:hover{border-color:var(--accent-line);color:var(--text);}
  .vars .вар.on{border-color:var(--accent);background:var(--accent-soft);
        color:var(--accent);font-weight:600;}
  /* Нет в наличии: не прячем, а перечёркиваем — иначе человек решит, что
     размера не бывает вовсе, и уйдёт искать в другом месте. */
  .vars .вар.нет{opacity:.5;cursor:not-allowed;text-decoration:line-through;}
  /* Цвет показываем кружком, а не словом: «мокрый асфальт» ни о чём не говорит */
  .vars .вар.цвет{width:var(--ctl);padding:0;border-radius:var(--pill);}
  .vars .вар.цвет i{width:calc(var(--ctl) - var(--sp-3));
        height:calc(var(--ctl) - var(--sp-3));border-radius:var(--pill);
        border:1px solid var(--border);display:block;}

  /* Описание и свойства таблицей */
  .prod-page .опис{color:var(--text-2);font-size:var(--fs-base);line-height:1.65;}
  /* Строка обещаний магазина под описанием: те же правила, что в строке
     доверия на первом экране, — и тот же обязательный размер значка. */
  .prod-page .доверие{display:flex;gap:var(--sp-4);flex-wrap:wrap;
        color:var(--faint);font-size:var(--fs-xs);}
  .prod-page .доверие span{display:inline-flex;align-items:center;gap:var(--sp-1);}
  .prod-page .доверие svg{width:var(--ic-sm);height:var(--ic-sm);flex:none;
        stroke:currentColor;fill:none;stroke-width:2;}
  .prod-page .свойства-таб{width:100%;border-collapse:collapse;font-size:var(--fs-sm);}
  .prod-page .свойства-таб td{padding:var(--sp-2) 0;
        border-bottom:1px solid var(--border-soft);vertical-align:top;}
  .prod-page .свойства-таб td:first-child{color:var(--muted);width:40%;}

  /* ── Покупка справа ────────────────────────────────────────────── */
  .buy-box{width:var(--cart-sum-w);max-width:100%;flex:none;position:sticky;top:var(--sp-5);
        background:var(--surface);border:1px solid var(--border);
        border-radius:var(--radius-lg);padding:var(--sp-4);
        display:flex;flex-direction:column;gap:var(--sp-3);}
  .buy-box .цена{display:flex;align-items:baseline;gap:var(--sp-2);flex-wrap:wrap;}
  .buy-box .цена b{font-size:var(--fs-3xl);font-weight:700;letter-spacing:-.03em;
        line-height:1;}
  .buy-box .цена s{color:var(--faint);font-size:var(--fs-md);}
  .buy-box .цена .минус{background:var(--bad-bg);color:var(--bad);
        padding:2px var(--sp-2);border-radius:var(--radius-xs);
        font-size:var(--fs-xs);font-weight:700;}
  .buy-box .наличие{display:flex;align-items:center;gap:var(--sp-2);
        font-size:var(--fs-sm);color:var(--good);}
  /* Размер значка задаём в каждом правиле, где значок есть: набор уезжает в
     другие проекты по файлам, и наш сброс туда может не поехать — в магазине
     он исключён нарочно. Без размера значок берёт двадцать четыре точки из
     viewBox и раздувается в чёрное пятно на пол-экрана. */
  .buy-box .наличие svg{width:var(--ic);height:var(--ic);flex:none;
        stroke:currentColor;fill:none;stroke-width:2.2;}
  .buy-box .наличие.мало{color:var(--warn);}
  .buy-box .наличие.нет{color:var(--bad);}
  .buy-box .кнопки{display:flex;gap:var(--sp-2);}
  .buy-box .кнопки .btn-primary{flex:1;}
  /* Доставка прямо в блоке покупки: «когда привезут» — второй вопрос после
     цены, и ради него не должны уходить на отдельную страницу. */
  .buy-box .доставка{display:flex;flex-direction:column;gap:var(--sp-2);
        padding-top:var(--sp-3);border-top:1px solid var(--border-soft);
        font-size:var(--fs-sm);}
  .buy-box .доставка .ст{display:flex;gap:var(--sp-2);align-items:flex-start;
        color:var(--muted);}
  /* Заголовок строки отдельной строкой, а не в подбор к тексту: иначе
     «Оплата как удобно» слипается со следующим словом в «удобнокартой». */
  .buy-box .доставка .ст b{display:block;color:var(--text);font-weight:600;}
  .buy-box .доставка svg{width:var(--ic);height:var(--ic);flex:none;
        stroke:currentColor;fill:none;stroke-width:1.7;color:var(--faint);
        margin-top:2px;}

  @media (max-width:1080px){
    .prod-page{flex-wrap:wrap;}
    .gallery{position:static;width:100%;}
    .buy-box{position:static;width:100%;}
  }
  @media (max-width:640px){
    /* Кадры уходят под главное фото строкой: столбик слева съедает треть
       ширины экрана, а на ней и так тесно. */
    .gallery{flex-direction:column-reverse;}
    .gallery .кадры{flex-direction:row;overflow-x:auto;scrollbar-width:none;}
    .gallery .кадры::-webkit-scrollbar{display:none;}
  }

  /* Подзаголовок внутри колонки товара: «Характеристики», «Описание».
     Мельче заголовка страницы, но заметнее обычного текста. */
  .prod-page .назв-подраздела{font-size:var(--fs-md);font-weight:650;
        margin-bottom:var(--sp-2);}

  /* Вариант выбирается нажатием на всю плашку, а не на кружок радио: сам
     кружок спрятан, состояние показывает плашка. Так по варианту попадают
     пальцем, и видно, что выбрано, без прищура. */
  .vars .вар:has(> input:checked){border-color:var(--accent);
        background:var(--accent-soft);color:var(--accent);font-weight:600;}
  .vars .вар:has(> input:disabled){opacity:.5;cursor:not-allowed;
        text-decoration:line-through;}

  /* Форма в раскрывашке: быстрый заказ, отзыв. Поля в столбик — она стоит в
     узкой колонке, и в строку они сжимаются до нечитаемых. */
  .быстрый-заказ{display:flex;flex-direction:column;gap:var(--sp-3);
        margin-top:var(--sp-3);}

  /* ── фильтры.css ─────────────────────────────────────── */
/* Фильтры каталога: столбец слева и выбранное строкой сверху.
   Часть набора элементов. Собирается сборщиком:
   python tools/build_styles.py */

/* Фильтры работают, только когда видно две вещи: что можно выбрать и что уже
   выбрано. Второе забывают чаще — человек ставит галочку, уходит листать,
   возвращается и не понимает, почему товаров мало.

   Поэтому выбранное вынесено отдельной строкой над списком: каждый фильтр
   плашкой с крестиком и «Сбросить всё» в конце. Снять фильтр можно там же,
   где увидел, а не искать его обратно в столбце.

   Число товаров стоит у каждого значения. Без него человек ставит галочку и
   получает пустой каталог — а это уже похоже на поломку магазина.

   На узком экране столбец уезжает в шторку по кнопке «Фильтры»: место под
   товары важнее. Сама шторка берётся из набора, здесь только содержимое. */

  /* ── Столбец фильтров ──────────────────────────────────────────── */
  .filters{width:var(--side-w);flex:none;display:flex;flex-direction:column;
        gap:var(--sp-4);}
  .filters .гр > b{display:flex;align-items:center;justify-content:space-between;
        font-size:var(--fs-sm);font-weight:650;margin-bottom:var(--sp-2);}
  /* Свернуть группу — не украшение: фильтров бывает десяток, и без сворачивания
     до нижних не долистывают. */
  /* Кнопка сворачивания была одиннадцать на тринадцать точек: попасть в
     неё пальцем невозможно вовсе. Видимого размера не прибавляем — растёт
     только область нажатия. */
  .filters .гр > b button{border:none;background:none;color:var(--faint);
        cursor:pointer;padding:0;line-height:1;
        min-width:var(--цель);min-height:var(--цель);
        display:grid;place-items:center;}
  .filters .гр .знач{display:flex;flex-direction:column;gap:var(--sp-1);}
  .filters .гр label{display:flex;align-items:center;gap:var(--sp-2);
        min-height:var(--ctl-sm);cursor:pointer;font-size:var(--fs-sm);
        color:var(--text-2);}
  .filters .гр label:hover{color:var(--text);}
  .filters .гр label input{accent-color:var(--accent);width:var(--ic-sm);
        height:var(--ic-sm);margin:0;flex:none;}
  .filters .гр label .скок{margin-left:auto;color:var(--faint);
        font-size:var(--fs-xs);}
  /* Значение, которого нет ни у одного товара при текущем наборе фильтров */
  .filters .гр label.пусто{opacity:.45;cursor:not-allowed;}
  /* Длинный список значений: показываем первые, остальное по «Ещё». Высота
     задана в строках, а не в точках, — иначе при крупном кегле обрежет текст. */
  .filters .гр .знач.свёрнут{max-height:calc(var(--ctl-sm) * 5);overflow:hidden;}
  .filters .гр .ещё{border:none;background:none;color:var(--accent);cursor:pointer;
        padding:var(--sp-1) 0;font-size:var(--fs-xs);text-align:left;
        min-height:var(--цель);display:flex;align-items:center;}

  /* Цена от и до: два поля в строку */
  .filters .вилка{display:flex;align-items:center;gap:var(--sp-2);}
  .filters .вилка .in{flex:1;min-width:0;}
  .filters .вилка span{color:var(--faint);}

  /* ── Выбранное строкой над каталогом ───────────────────────────── */
  .picked{display:flex;flex-wrap:wrap;gap:var(--sp-2);align-items:center;}
  .picked .чип{display:inline-flex;align-items:center;gap:var(--sp-1);
        height:var(--ctl-sm);padding:0 var(--sp-2);border-radius:var(--pill);
        background:var(--accent-soft);color:var(--accent);font-size:var(--fs-xs);
        border:1px solid var(--accent-line);}
  .picked .чип button{border:none;background:none;color:inherit;cursor:pointer;
        padding:0;line-height:1;opacity:.7;
        min-width:var(--цель);min-height:var(--цель);
        display:grid;place-items:center;margin-right:calc(var(--sp-2) * -1);}
  .picked .чип button:hover{opacity:1;}
  .picked .сброс{border:none;background:none;color:var(--muted);cursor:pointer;
        font-size:var(--fs-xs);text-decoration:underline;padding:0 var(--sp-1);
        min-height:var(--цель);display:inline-flex;align-items:center;}
  .picked .сброс:hover{color:var(--bad);}

  @media (max-width:900px){
    .filters{width:100%;}
  }

  /* ── Отбор строкой, под полосой сортировки ────────────────────────
     Второй вид того же отбора: не столбцом слева, а строкой над товарами.
     Нужен там, где слева уже стоит дерево разделов, — две колонки подряд
     съедают половину ширины, и товары остаются на трети экрана. */
  .отбор{margin-bottom:var(--sp-4);}
  .отбор > summary{display:inline-flex;align-items:center;gap:var(--sp-2);
        list-style:none;cursor:pointer;}
  .отбор > summary::-webkit-details-marker{display:none;}
  .отбор[open] > summary{margin-bottom:var(--sp-3);}
  .filters-строкой{display:flex;flex-wrap:wrap;gap:var(--sp-4);
        align-items:flex-end;padding:var(--sp-4);background:var(--surface-2);
        border:1px solid var(--border);border-radius:var(--radius);}
  .filters-строкой .гр{display:flex;flex-direction:column;gap:var(--sp-2);}
  .filters-строкой .гр > b{font-size:var(--fs-sm);font-weight:650;}
  .filters-строкой .вилка{display:flex;align-items:center;gap:var(--sp-2);}
  .filters-строкой .вилка .field{width:var(--поиск-кн);}

  /* Полоса «Показать ещё»: кнопка по центру, отбитая от сетки товаров */
  .ещё-полоса{display:flex;justify-content:center;margin-top:var(--sp-5);}

  /* Полоса пути над страницей: тот же отступ, что у остального содержимого */
  .крошки-полоса{padding-top:var(--sp-3);padding-bottom:0;}

  /* ── шапка-блока.css ─────────────────────────────────────── */
/* Шапка блока на странице: заголовок, пояснение и действие справа.
   Часть набора элементов. Собирается сборщиком:
   python tools/build_styles.py */

/* Повторяется на каждой странице магазина: «Каталог», «Часто заказывают»,
   «Похожие товары», «Отзывы». Раньше в каждом блоке заголовок писался своей
   строкой, и они разъезжались: где-то пояснение под заголовком, где-то
   рядом, отступы у всех разные.

   Ссылка «Весь каталог» стоит справа и оформлена кнопкой, а не текстом:
   текстовая ссылка на одной строке с заголовком читается как его часть.

   На узком экране действие уходит под заголовок и растягивается на всю
   ширину — рядом с длинным заголовком ему места не остаётся. */

  .шапка-блока{display:flex;flex-direction:column;gap:var(--sp-1);
        margin-bottom:var(--sp-4);}
  .шапка-блока h2{font-size:var(--fs-2xl);letter-spacing:-.02em;line-height:1.25;}
  .шапка-блока p{color:var(--muted);font-size:var(--fs-sm);line-height:1.6;}

  /* С действием справа */
  .шапка-блока.с-ссылкой{flex-direction:row;align-items:flex-end;
        justify-content:space-between;gap:var(--sp-4);}
  .шапка-блока.с-ссылкой > div{display:flex;flex-direction:column;gap:var(--sp-1);
        min-width:0;}
  .шапка-блока.с-ссылкой .btn{flex:none;}

  /* По центру — когда под шапкой идёт ряд одинаковых карточек */
  .шапка-блока.по-центру{text-align:center;align-items:center;
        max-width:var(--чит-w);margin-left:auto;margin-right:auto;}

  @media (max-width:620px){
    .шапка-блока h2{font-size:var(--fs-xl);}
    .шапка-блока.с-ссылкой{flex-direction:column;align-items:stretch;}
    .шапка-блока.с-ссылкой .btn{width:100%;}
  }

  /* ── шапка-магазина.css ─────────────────────────────────────── */
/* Шапка интернет-магазина.
   Часть набора элементов. Собирается сборщиком:
   python tools/build_styles.py */

/* Шапка магазина отличается от шапки панели тем, что в ней работают, а не
   ориентируются. Четыре вещи, ради которых её открывают: найти товар, попасть
   в каталог, увидеть корзину, войти в свой кабинет. Всё остальное — лишнее.

   Строк две, и это не украшение. В верхней — город, телефон и служебные
   ссылки: их читают один раз и больше не ищут. В нижней — знак, каталог,
   поиск и действия: с ними работают постоянно, и они не должны уезжать вверх
   при листании. Поэтому липнет к верху именно нижняя строка.

   Поиск занимает всё свободное место намеренно. В магазине это главный
   инструмент: половина людей идёт не по каталогу, а вводит название. Узкое
   поле в углу читается как «поиск у нас на всякий случай». */

  .shop-head{background:var(--surface);border-bottom:1px solid var(--border);}

  /* ── Верхняя строка: служебное ─────────────────────────────────── */
  .shop-head .верх{border-bottom:1px solid var(--border-soft);}
  .shop-head .верх .in{display:flex;align-items:center;gap:var(--sp-4);
        min-height:var(--ctl-sm);font-size:var(--fs-xs);color:var(--muted);}
  /* Ссылки верхней строки — тоже цели касания, а не просто текст: по ним
     жмут пальцем. Высота у них была по кеглю, около восемнадцати точек, —
     мельче нормы (24 на 24, WCAG 2.5.8). Растягиваем на всю высоту строки:
     видимого ничего не меняется, а промахиваться перестают. */
  .shop-head .верх a, .shop-head .верх .город{display:inline-flex;
        align-items:center;min-height:var(--цель);color:var(--muted);
        text-decoration:none;}
  .shop-head .верх a:hover{color:var(--text);}
  .shop-head .верх .город{gap:var(--sp-1);background:none;border:none;
        padding:0;cursor:pointer;font:inherit;}
  .shop-head .верх .город svg{width:var(--ic-sm);height:var(--ic-sm);
        stroke:currentColor;fill:none;stroke-width:1.7;}
  .shop-head .верх .сп{margin-left:auto;display:flex;gap:var(--sp-4);align-items:center;}
  .shop-head .верх .тел{color:var(--text);font-weight:600;}

  /* ── Нижняя строка: рабочая ────────────────────────────────────── */
  .shop-head .низ{position:sticky;top:0;z-index:30;background:var(--surface);}
  .shop-head .низ .in{display:flex;align-items:center;gap:var(--sp-3);
        padding-top:var(--sp-3);padding-bottom:var(--sp-3);}

  /* Общая колонка содержимого: и верх, и низ выравниваются по одному краю,
     иначе телефон вверху не совпадает с кнопкой входа внизу. */
  .shop-head .in{max-width:var(--shop-w);margin:0 auto;
        padding-left:var(--поля-сайта);padding-right:var(--поля-сайта);width:100%;
        /* Точка отсчёта для раскрытия каталога: оно висит под кнопкой, но
           края считает по колонке содержимого — иначе при узком окне лист
           упирается в границу экрана или уезжает за неё. */
        position:relative;}

  /* Знак и название */
  .shop-head .znak{width:var(--box-lg);height:var(--box-lg);border-radius:var(--box-lg-r);
        background:var(--accent);color:var(--on-accent);display:grid;place-items:center;
        font-weight:800;flex:none;overflow:hidden;text-decoration:none;}
  .shop-head .znak img{width:100%;height:100%;object-fit:cover;display:block;}
  .shop-head .имя{font-weight:700;font-size:var(--fs-lg);letter-spacing:-.02em;
        text-decoration:none;color:var(--text);white-space:nowrap;}

  /* Кнопка каталога: единственная закрашенная в шапке. Она — вход во всё
     остальное, и человек ищет её глазами первой. */
  .shop-head .каталог{flex:none;}
  .shop-head .каталог svg{width:var(--ic);height:var(--ic);stroke:currentColor;
        fill:none;stroke-width:2;}

  /* Поиск занимает остаток строки */
  .shop-head .искать{flex:1;min-width:0;}

  /* Действия: корзина, избранное, вход. Подпись под значком прячется на
     узком экране — сам значок остаётся, потому что по нему и нажимают. */
  .shop-head .дела{display:flex;align-items:center;gap:var(--sp-1);flex:none;}
  /* Значок с подписью: ширина и высота не меньше нормы касания, иначе по
     нему промахиваются — а это корзина, вход и избранное. */
  .shop-head .дело{display:flex;flex-direction:column;align-items:center;gap:2px;
        min-width:var(--ctl);min-height:var(--цель);
        padding:var(--sp-1) var(--sp-2);border:none;background:none;
        color:var(--text-2);cursor:pointer;text-decoration:none;position:relative;
        border-radius:var(--radius-sm);font-size:var(--fs-2xs);}
  .shop-head .дело:hover{background:var(--surface-2);color:var(--text);}
  .shop-head .дело svg{width:var(--ic);height:var(--ic);stroke:currentColor;
        fill:none;stroke-width:1.8;}

  /* Число на корзине. Ставится на сам значок, а не рядом с подписью: считают
     его глазами по картинке, и при нуле оно просто не выводится. */
  .shop-head .дело .счёт{position:absolute;top:2px;right:var(--sp-1);
        min-width:var(--счёт);height:var(--счёт);padding:0 var(--sp-1);
        border-radius:var(--pill);background:var(--accent);
        color:var(--on-accent);font-size:var(--fs-счёт);
        font-weight:700;display:grid;place-items:center;line-height:1;}

  /* ── Узкий экран ───────────────────────────────────────────────────
     Верхняя строка уходит целиком: город и телефон на телефоне не читают.
     Поиск переносится на свою строку — в одну строку с логотипом и корзиной
     он сжимается до бесполезной ширины. */
  @media (max-width:820px){
    .shop-head .верх{display:none;}
    .shop-head .низ .in{flex-wrap:wrap;}
    .shop-head .имя{display:none;}
    /* Поиск — своей строкой: в одну строку со знаком и корзиной он сжимается
       до ширины, в которую не влезает даже слово «кофеварка». */
    .shop-head .искать{order:3;flex-basis:100%;}

    /* Подписи уходят, остаются значки. Кнопка каталога без подписи обязана
       стать квадратной: иначе она растягивается на полстроки и выглядит
       пустой кнопкой непонятно от чего. */
    .shop-head .каталог .подпись{display:none;}
    .shop-head .каталог > .btn, .shop-head .каталог.btn{
          width:var(--ctl);min-width:0;padding:0;flex:none;}
    .shop-head .дело .подпись{display:none;}

    /* Значки прижаты вправо, а не свалены в кучу слева: знак слева, действия
       справа — так шапка выглядит на любом сайте, и рука тянется туда же. */
    .shop-head .дела{margin-left:auto;}
  }

  /* ── Строка разделов под шапкой ────────────────────────────────────
     У магазина сотни позиций, и путь к разделу должен быть в один тык, а не
     через раскрытие каталога. Поэтому под шапкой идёт ровная строка ссылок.

     Края считает по той же колонке, что и шапка: раньше строка тянулась от
     края экрана, и на широком мониторе «Прицелы» стояли левее логотипа —
     сайт выглядел так, будто шапка съехала.

     На телефоне строка листается вбок, а не переносится: перенос на две-три
     строки съедает экран до первого товара. */
  .shop-head .разделы{border-top:1px solid var(--border-soft);
        background:var(--surface);}
  .shop-head .разделы .in{display:flex;align-items:center;gap:var(--sp-1);
        min-height:var(--ctl);overflow-x:auto;scrollbar-width:none;}
  /* Первая ссылка встаёт ровно под логотипом. Своё внутреннее поле ссылки
     сдвинуло бы её вправо на десяток точек, и строка выглядела бы съехавшей
     относительно шапки — мелочь, которую видно сразу и не понимаешь, что
     не так. Сдвигаем ряд ровно на это поле. */
  .shop-head .разделы .in > a:first-child{margin-left:0;}
  /* Поле ссылки съедает часть сдвига, поэтому считаем от поля колонки: они
     меняются вместе с шириной окна, и жёсткое число разъезжается. */
  .shop-head .разделы .in::-webkit-scrollbar{display:none;}
  .shop-head .разделы a{display:inline-flex;align-items:center;flex:none;
        min-height:var(--цель);padding:0 var(--sp-3);border-radius:var(--radius-sm);
        color:var(--text-2);text-decoration:none;font-size:var(--fs-sm);
        white-space:nowrap;}
  .shop-head .разделы a:hover{background:var(--surface-2);color:var(--text);}
  .shop-head .разделы a.on{color:var(--accent);font-weight:600;}

  /* На телефоне строка остаётся: кнопка каталога там квадратная и без
     подписи, а разделы — единственная видимая навигация. Поля колонки не
     трогаем: сдвиг первой ссылки считается от них, и своё поле здесь увело
     бы строку на несколько точек влево. */


  /* ── Виды шапки ──────────────────────────────────────────────────
     Разметка одна, вид выбирается признаком. Не три отдельных шапки: их
     пришлось бы править по три раза, и они бы разошлись — так и вышло с
     заготовками, которые лежали в проекте своей вёрсткой и отстали. */

  /* Витрина: знак по центру, разделы слева, дела справа. Тихая подача для
     магазина, который берёт видом, — одежда, посуда, интерьер. */
  /* Знак и название стоят по центру, разделы слева, дела справа. Считаем
     флексом, а не сеткой: в сетке знак и название попадали в разные ячейки и
     название уезжало во вторую строку — висело под знаком. Боковые части
     делят остаток поровну, поэтому середина оказывается ровно посередине. */
  .shop-head[data-вид="витрина"] .низ .in{display:flex;align-items:center;
        gap:var(--sp-2);}
  /* Навбар сжимается первым и прячет лишние разделы за краем: при нехватке
     места он налезал на знак — «Распродажа» ложилась поверх квадратика. */
  .shop-head[data-вид="витрина"] .навбар{flex:1 1 0;display:flex;
        gap:var(--sp-4);min-width:0;overflow:hidden;}
  .shop-head[data-вид="витрина"] .навбар a{color:var(--text-2);
        text-decoration:none;font-size:var(--fs-sm);
        min-height:var(--ctl);display:inline-flex;align-items:center;
        white-space:nowrap;}
  .shop-head[data-вид="витрина"] .навбар a:hover{color:var(--accent);}
  .shop-head[data-вид="витрина"] .дела{flex:1 1 0;justify-content:flex-end;}
  .shop-head[data-вид="витрина"]{border-bottom:1px solid var(--border-soft);}

  /* Плотный: без верхней служебной строки, поиск во всю ширину. Для магазина,
     где товаров много и ищут поиском, а не глазами. */
  .shop-head[data-вид="плотный"] .верх{display:none;}
  .shop-head[data-вид="плотный"] .низ .in{gap:var(--sp-3);}
  .shop-head[data-вид="плотный"] .искать{flex:1;}

  @media (max-width:760px){
    /* На телефоне вид один: знак слева, дела справа. Игры с раскладкой на
       узком экране кончаются наездом значков на название. */
    .shop-head[data-вид="витрина"] .низ .in{display:flex;}
    .shop-head[data-вид="витрина"] .навбар{display:none;}
    .shop-head[data-вид="витрина"] .znak,
    .shop-head[data-вид="витрина"] .имя,
    .shop-head[data-вид="витрина"] .дела{grid-column:auto;justify-self:auto;}
  }

  /* ── 00-основа.css ─────────────────────────────────────── */
/* Служебные классы набора — наша замена файлу 1-Основа/00-сброс.css.

   Тот файл в сборку не идёт: он задаёт правила на голые теги — body, html,
   svg, — а у магазина сброс уже свой, в shop.css. Приехав следом, чужой
   сброс менял бы фон, кегль и раскладку значков на всех страницах разом,
   включая витрину, которой набор не нужен вовсе.

   Поэтому здесь только то из него, что нужно самим элементам и ни на что
   не влияет, пока класс не поставлен руками. Полосы прокрутки, рамка
   фокуса и правила для svg намеренно не перенесены: они меняют вид того,
   что уже нарисовано.

   Файл наш, переносом из панели не затирается. */

  /* Табличные цифры: деньги и счётчики не должны дрожать при обновлении */
  .num,.mono{font-variant-numeric:tabular-nums;}
  .mono{font-family:var(--mono);font-size:var(--fs-sm);letter-spacing:-.01em;}

  /* Мелкая подпись капслоком с разрядкой */
  .eyebrow{font-size:var(--fs-2xs);text-transform:uppercase;letter-spacing:.09em;
           font-weight:650;color:var(--faint);}

  /* dvh, а не vh: в мобильном Safari 100vh считается без адресной строки, и
     нижняя полоса сохранения оказывалась за краем экрана — а прокрутки нет. */
  .shell{display:flex;flex-direction:column;height:100vh;height:100dvh;overflow:hidden;}

  @media (prefers-reduced-motion:reduce){*{animation:none!important;transition:none!important;}}

  /* ── 10-админка.css ─────────────────────────────────────── */
/* Правки набора под админку магазина.

   Здесь только то, что нужно из-за разницы между панелью VPN и магазином.
   Всё остальное берётся из набора как есть — иначе смысл переноса теряется.

   Файл наш, переносом из панели не затирается. */

  /* ── Заголовки: без засечек ─────────────────────────────────────────
     Витрина набирает h1–h3 антиквой — это её лицо, и на страницах магазина
     так и надо. Но shop.css подключён и в админке, и заголовки карточек
     управления оказывались с засечками, тогда как весь остальной набор —
     без. Рядом это читается как две разные программы. */
  .адм h1,.адм h2,.адм h3{ font-family:var(--font); letter-spacing:-.01em; }

  /* ── Кнопки в плотных списках ───────────────────────────────────────
     У кнопки набора есть наименьшая ширина в 116 точек: в строке настроек
     и в подвале карточки правый край так совпадает во всех разделах. Но
     магазинные списки — раздел сайта, товар, зона доставки — кладут кнопки
     в сетку рядом с текстом, и 116 точек съедают колонку с названием: на
     странице «Главная страница» имя секции сжималось до девяти точек.

     Внутри таких списков ширина кнопки — по содержимому. Это тот же приём,
     что в наборе у панели инструментов (.btn-narrow), только не по классу
     в разметке, а по месту: списки магазинные, и разметку они наследуют. */
  .секция .btn,
  .строка_товара .btn,
  .строка_заказа .btn,
  .строка_корзины .btn,
  .строка_способа .btn,
  .строка_вариант .btn,
  .строка_свойства .btn,
  .дерево .btn,
  .массовое .btn,
  .tbl .btn,
  .раздел_карта .btn,
  .блок_карта .btn{ min-width:0; }
