/* blocks.core.css */
/*
    Core stylesheet for rendered blocks.

    Scope
    -----
    Everything here is prefixed `sb-` and nothing here styles a bare element outside `.sb-page`. That matters
    because the same stylesheet is loaded by two very different documents: a published page, where it is
    effectively the whole design, and the editor canvas, where it sits next to BootstrapBlazor's stylesheet and
    the editor's own. An unprefixed `button { }` rule would work perfectly on the published page and quietly
    restyle the editor's toolbar.

    Relationship to generated CSS
    -----------------------------
    This file holds the rules a block needs to be *correct*: its structure, its default proportions, its
    behaviour at narrow widths. Anything an author can change lives in the generated stylesheet as
    `[data-sb-id="…"] { … }`, which is emitted after this file and therefore wins on equal specificity. So every
    rule here is a single class selector wherever possible — raising specificity here would mean an author's
    padding silently failing to apply.
*/

/* ---------------------------------------------------------------------------------------------------
   Page root and resets, deliberately scoped
   --------------------------------------------------------------------------------------------------- */

.sb-page {
    box-sizing: border-box;
    -webkit-font-smoothing: antialiased;
    text-rendering: optimizeLegibility;

    /*
        Страница — контейнер, и от его ширины считаются все пороги конструктора.

        Ширинные запросы в этом файле, в остальных листах библиотеки и в генерируемом CSS — контейнерные, а
        не медиа: `@container sb-page (max-width: 767.98px)`, а не `@media`. Для посетителя это ничего не
        меняет, и не случайно: `.sb-page` — блочный элемент прямо в `<body>`, поля у которого сброшены (см.
        PageHtmlRenderer), поэтому его ширина равна ширине области просмотра — ровно тому, что меряет
        медиазапрос. Меняется холст редактора: там страница живёт в рамке заданной ширины внутри широкого
        окна, и медиазапрос честно отвечал про окно. «Телефон» показывал десктопную вёрстку, ужатую в полосу.

        Имя обязательно. Безымянный запрос отвечает от ближайшего предка с container-type, и первый же блок,
        объявивший контейнер для своих нужд, забрал бы себе запросы всего, что внутри него.

        inline-size, а не size: страница не должна перестать зависеть от содержимого по высоте — она только
        и делает, что растёт вниз. Из inline-size приходит ещё containment раскладки и стилей: страница
        становится содержащим блоком для position:fixed внутри себя (таких правил в библиотеке нет — кнопку
        «Редактировать сайт» рисует edit.js прямо в <body>, мимо страницы), новым контекстом наложения и
        новым контекстом форматирования. Прилипающая шапка при этом работает: containment раскладки не
        создаёт области прокрутки, а закраску (paint) inline-size не включает — иначе шапку обрезало бы.
    */
    container: sb-page / inline-size;

    /*
        Перенос длинного слова — один раз на весь сайт.

        Свойство наследуется, поэтому объявленное здесь оно действует в каждом блоке, включая те, которых ещё
        нет. Цена ошибки тут не «некрасиво», а «текст лежит поверх соседней карточки»: на телефоне колонка
        сужается до 150–200 пикселей, и «Восстановление», «info@студия-примеров.example.com» или ссылка без
        пробелов перестают помещаться в строку целиком. Без переноса браузер оставляет слово одной строкой и
        позволяет ей вылезти за границы блока — ровно то, на что жалуется владелец сайта.

        anywhere, а не break-word, — и разница между ними здесь решающая, хотя переносят они одинаково: слово
        рвётся только там, где иначе не поместилось бы. Отличается расчёт минимальной ширины содержимого.
        break-word её не уменьшает, поэтому заголовок в полосе по центру — а это флекс-элемент, чья ширина
        считается по содержимому, — получал ширину самого длинного слова целиком: 450 пикселей на экране в 360.
        Строка при этом переносилась, а коробка вокруг неё торчала за край экрана и давала странице
        горизонтальную прокрутку. anywhere учитывает возможность разрыва и в этом расчёте, и коробка сжимается
        вместе со строкой.
    */
    overflow-wrap: anywhere;

    /*
        Акцентный цвет, когда он краска, а не заливка.

        Это не то же самое, что --sb-primary, хотя обычно совпадает. Разница видна на залитой полосе: фон там уже
        основного цвета, и надзаголовок, рамка кнопки-контура и подпись роли, покрашенные в тот же основной,
        исчезают — цвет буквы совпадает с цветом под буквой. На такой полосе краска обязана стать контрастной, а
        заливка — остаться основной, иначе кнопка потеряет фон.

        Поэтому токена два: --sb-primary для заливок, --sb-ink-accent для всего, что рисуется поверх.
    */
    --sb-ink-accent: var(--sb-primary);

    /*
        Закон макета. Вся крупная геометрия страницы — ширины колонок, поля у краёв — берётся из этих имён,
        а не из собственных цифр в блоках. Цифра в блоке отвечает только за свой блок, и десять блоков дают
        десять «почти одинаковых» ширин: текст то начинается у борта окна, то нет, — ровно то, на что
        жалуется владелец. Имён намеренно два, а не десять: контейнер секции задаёт тема
        (--sb-container-width), узкая колонка — для читабельного текста, широкая — для витринных полос.
        «Во всю ширину» — это модификатор sb-width-full, отсутствие ограничения, а не третье значение.

        Узкая колонка — 760 пикселей: при базовом размере 16–17 это 70–80 знаков в строке, верхняя граница
        удобного чтения. Все прежние «свои» значения — 46rem у первого экрана без фотографии, 720 у шапки
        секции, 860 у вопросов, 620 у рассылки, 520 у запасной заявки — были попытками назвать эту же
        колонку, каждый раз заново.

        Поля страницы одни на всех ширинах: 24 пикселя от 375 — это 13% на воздух, и это работает. Поле,
        которое меняется от блока к блоку, и есть «где-то текст с краю, где-то нет».
    */
    --sb-gutter: var(--sb-container-padding, 24px);
    --sb-width-narrow: 760px;
    --sb-width-wide: 1440px;
}

.sb-page *,
.sb-page *::before,
.sb-page *::after {
    box-sizing: inherit;
}

/*
    hidden обязан прятать.

    Атрибутом hidden скрипт витрины переключает состояния: форму оформления и «Заказ принят», кабинет,
    приглашение войти и «вход не включён». Браузерное правило для него живёт в стилях агента и проигрывает
    любому display из авторского листа: `.sb-account { display: grid }` показывал разом кабинет с «Выйти»,
    кнопку «Войти» и строку про выключенный вход — три состояния, из которых верно одно (Д-4 из приёмки), а
    «Заказ принят» вставал мелкой строкой под так и не спрятанной формой (Д-10). !important здесь не спор о
    специфичности, а смысл атрибута: элемент со словом hidden не показывается, что бы ни говорил каскад.
*/
.sb-page [hidden] {
    display: none !important;
}

.sb-page p,
.sb-page ul,
.sb-page ol,
.sb-page figure,
.sb-page blockquote,
.sb-page pre {
    margin: 0;
}

.sb-page img,
.sb-page video,
.sb-page svg {
    display: block;
    max-width: 100%;
}

.sb-page a {
    text-decoration: none;
}

.sb-page a:hover {
    text-decoration: underline;
}

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

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

    Цвет — --sb-ink-accent, а не --sb-primary: на полосе, залитой основным цветом, рамка основного же цвета
    совпала бы с фоном и исчезла ровно там, где кнопка крупнее всего (см. blocks.layout.css).

    :focus-visible, а не :focus: рамка после щелчка мышью — это то, из-за чего её однажды и убирают совсем.
*/
.sb-page :is(a[href], button, input, select, textarea, summary, [tabindex]):focus-visible {
    outline: 2px solid var(--sb-ink-accent);
    outline-offset: 2px;
}

.sb-icon {
    flex: none;
    display: inline-block;
    vertical-align: middle;
}

/* ---------------------------------------------------------------------------------------------------
   Structure: section -> container -> content
   --------------------------------------------------------------------------------------------------- */

/*
    Класс несёт каждый блок-полоса, а не только layout.section: его ставит RootAttributes по каталогу
    (см. BlockComponentBase). Пока шаг секций был законом одного типа блока, все готовые полосы — герой,
    выгоды, тарифы, витрина — рисовались вовсе без вертикальных полей, и страница выглядела то слипшейся,
    то дырявой в зависимости от того, чьи внутренние отступы случайно оказались рядом.

    Шагов ритма два: полный — содержательной секции, половинный (sb-section-compact) — служебной полосе
    вроде панели фильтров над каталогом: она орган управления витриной, а не самостоятельная глава, и
    полный шаг отрывал бы её от товаров, которыми она управляет. Третьего размера нет намеренно.

    Модификатор выражен множителем, а не собственным padding-block: телефонные правила ниже уменьшают
    отступ тем же множителем, и компактная секция остаётся компактной на всех ширинах — правило с равной
    специфичностью, объявленное позже, иначе съедало бы модификатор на планшете.
*/
.sb-layout-section {
    position: relative;
    width: 100%;
    padding-block: calc(var(--sb-section-spacing) * var(--sb-rhythm, 1));
}

.sb-section-compact { --sb-rhythm: .5; }

/*
    The overlay is a pseudo-element rather than a second background layer so it can sit above a background
    image and below the text, which is the one arrangement a single element cannot express. It is armed by the
    `--sb-overlay` custom property that the `backgroundOverlay` style key writes, so a section without the
    property set paints nothing and costs nothing.
*/
.sb-layout-section::before {
    content: "";
    position: absolute;
    inset: 0;
    background: var(--sb-overlay, transparent);
    pointer-events: none;
}

.sb-layout-section > * {
    position: relative;
}

.sb-layout-container {
    width: 100%;
    max-width: var(--sb-container-width, 1180px);
    margin-inline: auto;
    padding-inline: var(--sb-gutter);
}

.sb-layout-container.sb-width-narrow {
    max-width: var(--sb-width-narrow);
}

.sb-layout-container.sb-width-wide {
    max-width: var(--sb-width-wide);
}

.sb-layout-container.sb-width-full {
    max-width: none;
}

/* ---------------------------------------------------------------------------------------------------
   Layout primitives
   --------------------------------------------------------------------------------------------------- */

.sb-layout-grid {
    display: grid;
    gap: var(--sb-space-lg);
    /*
        The column count is a plain number in the document (`gridColumns`), expanded here rather than in
        generated CSS: emitting `repeat()` from a stored value would mean generating a CSS function call from
        untrusted input, whereas a custom property can only ever be the number that passed validation.
    */
    grid-template-columns: repeat(var(--sb-grid-columns, 3), minmax(0, 1fr));
}

.sb-layout-columns {
    display: flex;
    flex-wrap: wrap;
    gap: var(--sb-space-lg);
    align-items: stretch;
}

.sb-layout-column {
    flex: 1 1 0;
    min-width: 0;
}

.sb-layout-column.sb-span-2 { flex-grow: 2; }
.sb-layout-column.sb-span-3 { flex-grow: 3; }
.sb-layout-column.sb-span-4 { flex-grow: 4; }
.sb-layout-column.sb-span-5 { flex-grow: 5; }

.sb-layout-stack {
    display: flex;
    flex-direction: column;
    gap: var(--sb-space-md);
    min-width: 0;
}

.sb-layout-stack.sb-dir-row {
    flex-direction: row;
    flex-wrap: wrap;
    align-items: center;
}

.sb-layout-spacer {
    width: 100%;
    height: var(--sb-space-xl);
    flex: none;
}

.sb-layout-divider {
    width: 100%;
    border: 0;
    border-top: 1px solid var(--sb-border);
    margin-block: var(--sb-space-md);
}

.sb-layout-divider.sb-style-dashed { border-top-style: dashed; }
.sb-layout-divider.sb-style-dotted { border-top-style: dotted; }
.sb-layout-divider.sb-style-thick { border-top-width: 3px; }

/*
    Карточка — предмет на странице, а не обведённый кусок фона.

    Одной рамкой в пиксель это не читается: ряд из трёх таких карточек выглядит как таблица без линий, и глазу
    не за что зацепиться. Отсюда тень по умолчанию — она короткая и почти невидимая по отдельности, но именно
    она отделяет карточку от полосы, на которой та лежит. Рамка при этом остаётся: тень одна, без рамки, плохо
    видна на втором фоне, а второй фон под карточками — обычная раскладка.
*/
.sb-layout-card {
    display: flex;
    flex-direction: column;
    /*
        Карточка в ряду колонок обязана уметь сжиматься. Флекс-элемент по умолчанию не становится уже своего
        содержимого (min-width: auto), и одна карточка с длинным словом внутри расталкивает весь ряд — а вместе
        с ним и страницу, у которой появляется горизонтальная прокрутка.
    */
    min-width: 0;
    gap: var(--sb-space-sm);
    padding: var(--sb-space-lg);
    background: var(--sb-surface);
    border: 1px solid var(--sb-border);
    border-radius: var(--sb-radius);
    box-shadow: var(--sb-shadow-sm);
}

.sb-layout-card.sb-elevated {
    border-color: transparent;
    box-shadow: var(--sb-shadow-md);
}

.sb-layout-card.sb-plain,
.sb-layout-card.sb-tinted {
    box-shadow: none;
}

/*
    Подъём — только у карточки-ссылки. Карточка, которая приподнимается под курсором, обещает, что по ней можно
    нажать; если нажать нельзя, это обещание ложное, и посетитель кликает в пустоту.

    `transition` на самом элементе, а не в :hover, — иначе возврат происходит мгновенно и движение выглядит
    дёрганым. `prefers-reduced-motion` отключает именно перемещение: тень остаётся, потому что мешает не она.
*/
.sb-layout-card.sb-linked {
    transition: transform .16s ease, box-shadow .2s ease, border-color .2s ease;
}

.sb-layout-card.sb-linked:hover {
    transform: translateY(-3px);
    box-shadow: var(--sb-shadow-lg);
    border-color: color-mix(in srgb, var(--sb-primary) 32%, var(--sb-border));
    text-decoration: none;
}

@media (prefers-reduced-motion: reduce) {
    .sb-layout-card.sb-linked { transition: box-shadow .2s ease, border-color .2s ease; }
    .sb-layout-card.sb-linked:hover { transform: none; }
}

.sb-layout-card.sb-tinted {
    background: var(--sb-surface-alt);
}

/* ---------------------------------------------------------------------------------------------------
   Shared content primitives used by many blocks
   --------------------------------------------------------------------------------------------------- */

.sb-eyebrow {
    font-size: .82em;
    font-weight: 600;
    letter-spacing: .08em;
    text-transform: uppercase;
    color: var(--sb-ink-accent);
}

.sb-lead {
    font-size: 1.15em;
    color: var(--sb-text-muted);
    line-height: 1.6;
}

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

/*
    A section's heading block: eyebrow, title, subtitle. Repeated by a dozen section blocks, so it is one class
    here instead of the same three-element arrangement copied into each of them.
*/
.sb-section-head {
    display: flex;
    flex-direction: column;
    gap: var(--sb-space-sm);
    margin-bottom: var(--sb-space-xl);
    /* Узкая колонка закона, а не своя цифра: заголовок с подзаголовком — текст, и мера строки у него та же,
       что у любого другого читабельного текста. */
    max-width: var(--sb-width-narrow);
}

/*
    Отступ шапки существует, чтобы отделить её от содержимого секции. Когда содержимого нет — герой без
    кнопок, — отступ складывался с нижним полем полосы, и секция выходила на 64 пикселя ниже снизу, чем
    сверху: тот самый «иногда расстояния есть, иногда нет». :where — чтобы авторский отступ из инспектора
    выигрывал при равной специфичности, как и у всех правил этого файла.
*/
.sb-section-head:where(:last-child) {
    margin-bottom: 0;
}

.sb-section-head.sb-align-center {
    align-items: center;
    text-align: center;
    margin-inline: auto;
}

.sb-section-head.sb-align-right {
    align-items: flex-end;
    text-align: right;
    margin-inline-start: auto;
}

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

.sb-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    /*
        Кнопка не шире того, во что её положили. Подпись «Записаться на бесплатную консультацию» в карточке
        шириной в 200 пикселей иначе рисуется одной строкой поверх соседей — inline-flex сам по себе не
        ограничен шириной родителя.
    */
    max-width: 100%;
    gap: .5em;
    padding: .75em 1.4em;
    border: 1px solid transparent;
    border-radius: var(--sb-radius-sm);
    font: inherit;
    font-weight: 600;
    line-height: 1.2;
    cursor: pointer;
    text-align: center;
    transition: transform .12s ease, box-shadow .18s ease, background-color .18s ease, color .18s ease;
}

.sb-btn:hover {
    text-decoration: none;
    transform: translateY(-1px);
}

.sb-btn:active {
    transform: translateY(0);
}

.sb-btn:focus-visible {
    outline: 2px solid var(--sb-ink-accent);
    outline-offset: 2px;
}

.sb-btn-primary {
    background: var(--sb-primary);
    color: var(--sb-primary-contrast);
    box-shadow: var(--sb-shadow-sm);
}

.sb-btn-primary:hover {
    box-shadow: var(--sb-shadow-md);
}

.sb-btn-secondary {
    background: var(--sb-secondary);
    color: var(--sb-primary-contrast);
}

.sb-btn-accent {
    background: var(--sb-accent);
    color: var(--sb-text);
}

.sb-btn-outline {
    background: transparent;
    border-color: var(--sb-ink-accent);
    color: var(--sb-ink-accent);
}

/* Наведение выворачивает кнопку наизнанку: заливка — тем, чем была рамка, буква — цветом полосы под ней. */
.sb-btn-outline:hover {
    background: var(--sb-ink-accent);
    color: var(--sb-surface);
}

.sb-tone-primary .sb-btn-outline:hover,
.sb-tone-dark .sb-btn-outline:hover {
    color: var(--sb-primary);
}

.sb-btn-subtle {
    background: var(--sb-surface-alt);
    color: var(--sb-text);
}

/*
    Основная кнопка на залитой полосе выворачивается.

    Полоса уже основного цвета: кнопка того же цвета на ней — это прямоугольник, который видно только по тени.
    Букву при этом читать можно, поэтому автоматическая проверка контраста молчит, а кнопка всё равно теряется —
    ровно тот случай, когда цифра проходит, а глазами плохо.
*/
.sb-tone-primary .sb-btn-primary,
.sb-tone-dark .sb-btn-primary {
    background: var(--sb-primary-contrast);
    color: var(--sb-primary);
}

/* Приглушённая кнопка на залитой полосе: второй фон там не отличается от первого. */
.sb-tone-primary .sb-btn-subtle,
.sb-tone-dark .sb-btn-subtle {
    background: var(--sb-primary-contrast);
    color: var(--sb-primary);
}

.sb-btn-ghost {
    background: transparent;
    color: var(--sb-ink-accent);
    padding-inline: .4em;
}

.sb-btn-sm { padding: .5em 1em; font-size: .9em; }
.sb-btn-lg { padding: .9em 1.8em; font-size: 1.08em; }
.sb-btn-block { width: 100%; }
.sb-btn-pill { border-radius: var(--sb-radius-pill); }

.sb-btn-group {
    display: flex;
    flex-wrap: wrap;
    gap: var(--sb-space-sm);
    align-items: center;
}

.sb-btn-group.sb-align-center { justify-content: center; }
.sb-btn-group.sb-align-right { justify-content: flex-end; }

/* ---------------------------------------------------------------------------------------------------
   Visibility
   --------------------------------------------------------------------------------------------------- */

/*
    !important here is deliberate and is the reason visibility is a class rather than a generated declaration.
    "Hidden on mobile" has to beat whatever `display` the author set for that same breakpoint, and the generated
    rule has identical specificity and comes later in the cascade — so without it, setting a mobile display value
    would silently un-hide the block.
*/
.sb-hidden { display: none !important; }

@container sb-page (min-width: 1400px) {
    .sb-hide-wide { display: none !important; }
}

@container sb-page (max-width: 991.98px) {
    .sb-hide-tablet { display: none !important; }
}

@container sb-page (max-width: 767.98px) {
    .sb-hide-mobile { display: none !important; }
}

/* ---------------------------------------------------------------------------------------------------
   Responsive collapse of the layout primitives

   These come last so they override the desktop rules above, and they are intentionally *not* per-node: an
   author who wants a two-column grid on a phone sets it explicitly and the generated rule, emitted after this
   file, wins.
   --------------------------------------------------------------------------------------------------- */

@container sb-page (max-width: 991.98px) {
    .sb-layout-section {
        padding-block: calc(var(--sb-section-spacing) * var(--sb-rhythm, 1) * .7);
    }

    .sb-layout-grid {
        grid-template-columns: repeat(min(var(--sb-grid-columns, 3), 2), minmax(0, 1fr));
    }
}

@container sb-page (max-width: 767.98px) {
    .sb-layout-section {
        padding-block: calc(var(--sb-section-spacing) * var(--sb-rhythm, 1) * .5);
    }

    .sb-layout-grid {
        grid-template-columns: minmax(0, 1fr);
    }

    .sb-layout-columns {
        flex-direction: column;
    }

    .sb-layout-stack.sb-dir-row {
        flex-direction: column;
        align-items: stretch;
    }

    .sb-section-head {
        margin-bottom: var(--sb-space-lg);
    }

    /*
        Внутренние поля карточки на телефоне.

        L — это пять шагов сетки, сорок пикселей. На десктопе, где карточка шириной в 360, они занимают чуть
        больше пятой части; на телефоне карточка сама шириной в 296, и те же сорок пикселей с каждой стороны
        съедают больше четверти, оставляя тексту 216. У тарифов было хуже: XL — это 64, и на строку оставалось
        168 пикселей, куда не помещается слово «Восстановление». Именно это и выглядит как «отступы страдают»:
        воздуха больше, чем содержимого, а содержимое из-за этого не помещается.

        M вместо L — это 24 пикселя: карточка остаётся карточкой, а не полосой в рамке, и строке достаётся
        почти вся её ширина.
    */
    .sb-layout-card {
        padding: var(--sb-space-md);
    }

    /*
        Крупная кнопка на телефоне остаётся крупной по высоте — в неё целятся пальцем, — но перестаёт быть
        широкой по бокам: 1.8em при увеличенном шрифте давали 33 пикселя с каждой стороны, то есть шестьдесят
        шесть из трёхсот на пустое место вокруг подписи.
    */
    .sb-btn-lg {
        padding: .9em 1.2em;
        font-size: 1em;
    }
}

/* ---------------------------------------------------------------------------------------------------
   Scroll reveal

   Content is visible by default and only hidden once blocks.js has confirmed it can drive the animation. A
   visitor with JavaScript disabled, or one whose connection dropped the script, therefore sees the page —
   the opposite arrangement is the single most common way a "tasteful fade-in" turns into a blank page.
   --------------------------------------------------------------------------------------------------- */

.sb-reveal-ready [data-sb-reveal] {
    opacity: 0;
    transition: opacity .55s ease, transform .55s ease;
    will-change: opacity, transform;
}

.sb-reveal-ready [data-sb-reveal="fade-up"] { transform: translateY(24px); }
.sb-reveal-ready [data-sb-reveal="fade-down"] { transform: translateY(-24px); }
.sb-reveal-ready [data-sb-reveal="fade-left"] { transform: translateX(24px); }
.sb-reveal-ready [data-sb-reveal="fade-right"] { transform: translateX(-24px); }
.sb-reveal-ready [data-sb-reveal="zoom"] { transform: scale(.94); }

.sb-reveal-ready [data-sb-reveal].sb-revealed {
    opacity: 1;
    transform: none;
}

@media (prefers-reduced-motion: reduce) {
    .sb-page *,
    .sb-page *::before,
    .sb-page *::after {
        animation-duration: .001ms !important;
        transition-duration: .001ms !important;
    }

    .sb-reveal-ready [data-sb-reveal] {
        opacity: 1;
        transform: none;
    }
}

/* ---------------------------------------------------------------------------------------------------
   Editor affordances

   Only rendered when the canvas is in editor mode, and never present in published output.
   --------------------------------------------------------------------------------------------------- */

.sb-empty-hint {
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 92px;
    padding: var(--sb-space-md);
    border: 1.5px dashed var(--sb-border);
    border-radius: var(--sb-radius-sm);
    color: var(--sb-text-muted);
    font-size: .92rem;
    text-align: center;
}

.sb-unknown {
    padding: var(--sb-space-md);
    border: 1.5px dashed var(--sb-warning);
    border-radius: var(--sb-radius-sm);
    background: color-mix(in srgb, var(--sb-warning) 8%, transparent);
    font-size: .9rem;
}

.sb-unknown code {
    display: inline-block;
    margin-inline-start: .4em;
    padding: .1em .4em;
    border-radius: 4px;
    background: color-mix(in srgb, var(--sb-warning) 18%, transparent);
}

/* blocks.layout.css */
/*
    Layout blocks.

    The structural rules — .sb-layout-section, .sb-layout-container, .sb-layout-grid and friends — live in
    blocks.core.css, because several section blocks reuse them directly. What is here is the modifiers those
    blocks add from authored properties.
*/

/* ---------------------------------------------------------------------------------------------------
   Section tone and height
   --------------------------------------------------------------------------------------------------- */

.sb-tone-alt { background-color: var(--sb-surface-alt); }

.sb-tone-primary {
    background-color: var(--sb-primary);
    color: var(--sb-primary-contrast);
}

/*
    A tinted section has to carry its text colour down to the headings and muted text inside it, otherwise a
    heading keeps --sb-text and disappears against the accent background. Re-pointing the tokens rather than
    setting `color` on descendants is what makes that work for blocks that have not been written yet.
*/
.sb-tone-primary,
.sb-tone-dark {
    --sb-text: var(--sb-primary-contrast);
    /*
        86, а не 78.

        Приглушённый текст на залитой полосе — это подзаголовок и примечание под кнопкой, то есть как раз то,
        ради чего человек читает эту полосу. На среднем по светлоте фирменном цвете 78% давали 4.13 — ниже нормы
        4.5, и замечал это только замер: глазами текст «вроде читается». Разница в оттенке между 78 и 86 почти
        незаметна, а разница между «проходит» и «не проходит» — полная.
    */
    --sb-text-muted: color-mix(in srgb, var(--sb-primary-contrast) 86%, transparent);
    --sb-border: color-mix(in srgb, var(--sb-primary-contrast) 24%, transparent);

    /* Краска — контрастная: см. --sb-ink-accent в blocks.core.css. Заливка остаётся основной, иначе кнопка
       на этой полосе потеряет фон. */
    --sb-ink-accent: var(--sb-primary-contrast);
}

.sb-tone-dark {
    background-color: #12141c;
    color: #f4f6fb;
}

.sb-minh-50,
.sb-minh-75,
.sb-minh-100 {
    display: flex;
    flex-direction: column;
}

.sb-minh-50 { min-height: 50vh; }
.sb-minh-75 { min-height: 75vh; }
.sb-minh-100 { min-height: 100vh; }

.sb-minh-50 > *,
.sb-minh-75 > *,
.sb-minh-100 > * {
    width: 100%;
}

.sb-valign-start { justify-content: flex-start; }
.sb-valign-center { justify-content: center; }
.sb-valign-end { justify-content: flex-end; }

/* ---------------------------------------------------------------------------------------------------
   Spacing modifiers, shared by every block with a `gap` property
   --------------------------------------------------------------------------------------------------- */

.sb-gap-xs { gap: var(--sb-space-xs); }
.sb-gap-sm { gap: var(--sb-space-sm); }
.sb-gap-md { gap: var(--sb-space-md); }
.sb-gap-lg { gap: var(--sb-space-lg); }
.sb-gap-xl { gap: var(--sb-space-xl); }
.sb-gap-2xl { gap: var(--sb-space-2xl); }

.sb-pad-xs { padding: var(--sb-space-xs); }
.sb-pad-sm { padding: var(--sb-space-sm); }
.sb-pad-md { padding: var(--sb-space-md); }
.sb-pad-lg { padding: var(--sb-space-lg); }
.sb-pad-xl { padding: var(--sb-space-xl); }
.sb-pad-2xl { padding: var(--sb-space-2xl); }

/*
    Крупные поля на телефоне спускаются на шаг вниз.

    Автор выбирает отступ, глядя на десктоп, где карточка шириной в треть экрана. На телефоне та же карточка
    шириной во весь экран минус поля страницы, и 2XL — девяносто шесть пикселей с каждой стороны — оставляет
    от трёхсот пятидесяти сто пятьдесят. Это не «просторно», это сломано.

    Мелкие шаги (XS, S, M) не трогаются: они и на телефоне читаются так же, как задуманы, а увеличивать
    отступ, который автор сознательно сделал маленьким, правило не вправе.

    Стоит после базовых объявлений и потому выигрывает у них при равной специфичности. Собственный отступ
    автора из инспектора идёт отдельным листом после этого и по-прежнему главнее.
*/
@container sb-page (max-width: 767.98px) {
    .sb-pad-lg { padding: var(--sb-space-md); }
    .sb-pad-xl { padding: var(--sb-space-lg); }
    .sb-pad-2xl { padding: var(--sb-space-lg); }
}

/* ---------------------------------------------------------------------------------------------------
   Grid
   --------------------------------------------------------------------------------------------------- */

/*
    Equal height is the default for a grid and is what `align-items: stretch` already gives; the class exists so
    turning it *off* is expressible, because a row of cards of differing length sometimes reads better ragged.
*/
.sb-layout-grid.sb-equal { align-items: stretch; }
.sb-layout-grid:not(.sb-equal) { align-items: start; }

/* ---------------------------------------------------------------------------------------------------
   Columns
   --------------------------------------------------------------------------------------------------- */

.sb-layout-columns.sb-valign-start { align-items: flex-start; }
.sb-layout-columns.sb-valign-center { align-items: center; }
.sb-layout-columns.sb-valign-end { align-items: flex-end; }
.sb-layout-columns.sb-valign-stretch { align-items: stretch; }

@container sb-page (max-width: 767.98px) {
    /*
        column-reverse rather than reordering the nodes: the document order stays the reading order, so a screen
        reader and a crawler still see heading-then-image while a phone shows image-then-heading.
    */
    .sb-layout-columns.sb-reverse-mobile {
        flex-direction: column-reverse;
    }
}

/* ---------------------------------------------------------------------------------------------------
   Stack
   --------------------------------------------------------------------------------------------------- */

.sb-layout-stack.sb-align-start { align-items: flex-start; }
.sb-layout-stack.sb-align-center { align-items: center; }
.sb-layout-stack.sb-align-end { align-items: flex-end; }
.sb-layout-stack.sb-align-stretch { align-items: stretch; }

.sb-layout-stack.sb-justify-start { justify-content: flex-start; }
.sb-layout-stack.sb-justify-center { justify-content: center; }
.sb-layout-stack.sb-justify-end { justify-content: flex-end; }
.sb-layout-stack.sb-justify-between { justify-content: space-between; }
.sb-layout-stack.sb-justify-around { justify-content: space-around; }

.sb-layout-stack.sb-nowrap { flex-wrap: nowrap; }

/* ---------------------------------------------------------------------------------------------------
   Spacer
   --------------------------------------------------------------------------------------------------- */

.sb-layout-spacer.sb-size-xs { height: var(--sb-space-xs); }
.sb-layout-spacer.sb-size-sm { height: var(--sb-space-sm); }
.sb-layout-spacer.sb-size-md { height: var(--sb-space-md); }
.sb-layout-spacer.sb-size-lg { height: var(--sb-space-lg); }
.sb-layout-spacer.sb-size-xl { height: var(--sb-space-xl); }
.sb-layout-spacer.sb-size-2xl { height: var(--sb-space-2xl); }

/* ---------------------------------------------------------------------------------------------------
   Divider
   --------------------------------------------------------------------------------------------------- */

.sb-layout-divider.sb-w-half { width: 50%; }
.sb-layout-divider.sb-w-quarter { width: 25%; }

.sb-layout-divider.sb-align-center { margin-inline: auto; }
.sb-layout-divider.sb-align-right { margin-inline-start: auto; }

/* ---------------------------------------------------------------------------------------------------
   Card
   --------------------------------------------------------------------------------------------------- */

.sb-layout-card.sb-plain {
    background: transparent;
    border-color: transparent;
    padding: 0;
}

/*
    A card that is a link keeps the surrounding text colour instead of turning blue, and lifts on hover so it
    reads as one target. The underline is suppressed on the card itself but deliberately left on its heading, so
    there is still a visible hover affordance for anyone who cannot perceive the shadow change.
*/
.sb-layout-card.sb-linked {
    color: inherit;
    text-decoration: none;
    transition: transform .15s ease, box-shadow .2s ease, border-color .2s ease;
}

.sb-layout-card.sb-linked:hover {
    text-decoration: none;
    transform: translateY(-2px);
    box-shadow: var(--sb-shadow-lg);
    border-color: color-mix(in srgb, var(--sb-primary) 35%, var(--sb-border));
}

.sb-layout-card.sb-linked:hover :is(h1, h2, h3, h4, h5, h6) {
    text-decoration: underline;
    text-underline-offset: 3px;
}

.sb-layout-card.sb-linked:focus-visible {
    outline: 2px solid var(--sb-ink-accent);
    outline-offset: 3px;
}

/* Shared symbol instance (editor shell; expanded away at publish). */
.sb-symbol {
    min-height: 3rem;
}

.sb-symbol-shell {
    border: 1.5px dashed color-mix(in srgb, var(--sb-accent, #4f46e5) 55%, transparent);
    background: color-mix(in srgb, var(--sb-accent, #4f46e5) 8%, transparent);
    border-radius: 10px;
    padding: 1rem 1.15rem;
    color: var(--sb-text, #171a22);
}

.sb-symbol-shell strong {
    display: block;
    font-size: 0.85rem;
    letter-spacing: 0.02em;
    text-transform: uppercase;
    opacity: 0.75;
}

.sb-symbol-id {
    display: inline-block;
    margin: 0.25rem 0 0.4rem;
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: 0.8rem;
    opacity: 0.85;
}

.sb-symbol-shell p {
    margin: 0;
    font-size: 0.9rem;
    opacity: 0.8;
}

/* blocks.content.css */
/*
    Content blocks: headings, text, images, buttons, badges, lists, quotes, code, tables.

    `.sb-prose` is the one place in this library where bare element selectors are allowed, because the markup
    inside it comes from a rich-text or Markdown property and there is nothing to hang a class on. It is scoped to
    the class, so it cannot reach the editor chrome or anything outside the block.
*/

/* ---------------------------------------------------------------------------------------------------
   Heading
   --------------------------------------------------------------------------------------------------- */

.sb-content-heading {
    display: flex;
    flex-direction: column;
    gap: var(--sb-space-xs);
}

.sb-content-heading.sb-align-center { align-items: center; text-align: center; }
.sb-content-heading.sb-align-right { align-items: flex-end; text-align: right; }

.sb-content-heading .sb-accent { color: var(--sb-ink-accent); }

/*
    Explicit sizes override the type scale the heading level would give. Set with a single class so the author's
    own font-size in the style inspector still wins — the generated rule has the same specificity and comes later.
*/
.sb-content-heading.sb-size-sm .sb-heading-text { font-size: var(--sb-font-size-h5); }
.sb-content-heading.sb-size-md .sb-heading-text { font-size: var(--sb-font-size-h4); }
.sb-content-heading.sb-size-lg .sb-heading-text { font-size: var(--sb-font-size-h2); }
.sb-content-heading.sb-size-xl .sb-heading-text { font-size: var(--sb-font-size-h1); }

/* Нижняя граница — как у .sb-section-title.sb-size-display, и по той же причине: она про телефон. */
.sb-content-heading.sb-size-display .sb-heading-text {
    font-size: clamp(1.95rem, 6vw, 4.4rem);
    line-height: 1.04;
    letter-spacing: -0.03em;
}

/* ---------------------------------------------------------------------------------------------------
   Text
   --------------------------------------------------------------------------------------------------- */

.sb-content-text {
    display: flex;
    flex-direction: column;
    gap: .85em;
}

.sb-content-text.sb-align-center { text-align: center; align-items: center; }
.sb-content-text.sb-align-right { text-align: right; align-items: flex-end; }

.sb-content-text.sb-size-sm { font-size: .9em; }
.sb-content-text.sb-size-lg { font-size: 1.12em; }

/*
    A comfortable measure is expressed in `ch` — a unit of the font's own character width — so it stays correct
    when the site's typeface or base size changes. A fixed pixel width would need retuning for every theme.
*/
.sb-measure { max-width: 68ch; }

.sb-content-text.sb-align-center.sb-measure,
.sb-content-richtext.sb-align-center.sb-measure { margin-inline: auto; }

/* ---------------------------------------------------------------------------------------------------
   Prose: rich text and Markdown output
   --------------------------------------------------------------------------------------------------- */

.sb-prose { line-height: 1.7; }

.sb-prose.sb-align-center { text-align: center; }
.sb-prose.sb-align-right { text-align: right; }

.sb-prose > * + * { margin-top: .9em; }

.sb-prose :is(h1, h2, h3, h4, h5, h6) {
    margin-top: 1.8em;
    margin-bottom: .6em;
}

.sb-prose > :is(h1, h2, h3, h4, h5, h6):first-child { margin-top: 0; }

.sb-prose :is(ul, ol) {
    padding-inline-start: 1.4em;
    display: flex;
    flex-direction: column;
    gap: .35em;
}

.sb-prose li > :is(ul, ol) { margin-top: .35em; }

.sb-prose a {
    color: var(--sb-ink-accent);
    text-decoration: underline;
    text-underline-offset: 2px;
}

.sb-prose strong { font-weight: 650; }

.sb-prose blockquote {
    padding-inline-start: 1.1em;
    border-inline-start: 3px solid var(--sb-primary);
    color: var(--sb-text-muted);
    font-style: italic;
}

.sb-prose :is(code, kbd, samp) {
    padding: .15em .4em;
    border-radius: 6px;
    background: var(--sb-surface-alt);
    font-family: var(--sb-font-mono);
    font-size: .9em;
}

.sb-prose pre {
    padding: var(--sb-space-md);
    border-radius: var(--sb-radius-sm);
    background: var(--sb-surface-alt);
    overflow-x: auto;
}

.sb-prose pre code {
    padding: 0;
    background: none;
}

.sb-prose img {
    border-radius: var(--sb-radius-sm);
}

/*
    Таблица из текстового или markdown-блока прокручивается сама.

    Обернуть её, как это делает блок «Таблица», негде: разметка приходит из свойства и обёртки в ней нет.
    Поэтому прокручивается сама таблица — `display: block` превращает её в обычный контейнер прокрутки, а
    `width: max-content` не даёт ей ужаться до нечитаемых столбцов. Без этого таблица из четырёх столбцов на
    телефоне растягивает страницу, и горизонтально едет не таблица, а весь сайт.
*/
.sb-prose table {
    display: block;
    width: max-content;
    max-width: 100%;
    overflow-x: auto;
    border-collapse: collapse;
}

.sb-prose :is(th, td) {
    padding: .55em .8em;
    border-bottom: 1px solid var(--sb-border);
    text-align: start;
}

.sb-prose hr {
    border: 0;
    border-top: 1px solid var(--sb-border);
}

/* Markdown that has not been through the server yet; see MarkdownBlock. */
.sb-markdown-pending {
    padding: var(--sb-space-md);
    border: 1.5px dashed var(--sb-border);
    border-radius: var(--sb-radius-sm);
    background: var(--sb-surface-alt);
    font-family: var(--sb-font-mono);
    font-size: .88em;
    white-space: pre-wrap;
}

/* ---------------------------------------------------------------------------------------------------
   Raw HTML block, editor framing only
   --------------------------------------------------------------------------------------------------- */

.sb-content-html.sb-html-editing {
    border: 1.5px dashed var(--sb-border);
    border-radius: var(--sb-radius-sm);
}

.sb-html-label {
    padding: .3em .7em;
    border-bottom: 1px dashed var(--sb-border);
    background: var(--sb-surface-alt);
    color: var(--sb-text-muted);
    font-size: .78rem;
    text-transform: uppercase;
    letter-spacing: .06em;
}

.sb-html-body { padding: var(--sb-space-sm); }

/* ---------------------------------------------------------------------------------------------------
   Image
   --------------------------------------------------------------------------------------------------- */

.sb-content-image { position: relative; }

.sb-image-media {
    width: 100%;
    height: auto;
}

.sb-content-image.sb-rounded :is(.sb-image-media, .sb-image-link) { border-radius: var(--sb-radius); }
.sb-content-image.sb-shadow .sb-image-media { box-shadow: var(--sb-shadow-lg); }

/*
    A fixed aspect ratio needs the image to fill the box, so height:100% and object-fit come together with the
    ratio classes rather than being applied always — an unconstrained image must keep its intrinsic proportions.
*/
[class*="sb-ratio-"] .sb-image-media {
    height: 100%;
    object-fit: cover;
}

.sb-content-image.sb-fit-contain .sb-image-media { object-fit: contain; }

.sb-ratio-1-1 { aspect-ratio: 1 / 1; }
.sb-ratio-4-3 { aspect-ratio: 4 / 3; }
.sb-ratio-3-2 { aspect-ratio: 3 / 2; }
.sb-ratio-16-9 { aspect-ratio: 16 / 9; }
.sb-ratio-21-9 { aspect-ratio: 21 / 9; }
.sb-ratio-3-4 { aspect-ratio: 3 / 4; }
.sb-ratio-9-16 { aspect-ratio: 9 / 16; }

.sb-image-link { display: block; height: 100%; }

.sb-image-caption {
    margin-top: .6em;
    color: var(--sb-text-muted);
    font-size: .88em;
    line-height: 1.5;
}

/*
    Пустой слот изображения.

    Серый прямоугольник с пунктиром читается как поломка: автор открывает шаблон и первым делом видит место, где
    чего-то не хватает. Поэтому подложка тонирована цветами темы — она выглядит как задуманный элемент, пока
    фотографии нет, и остаётся понятной подсказкой: пунктир и надпись на месте.
*/
.sb-image-placeholder {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: .5em;
    min-height: 180px;
    border: 1.5px dashed color-mix(in srgb, var(--sb-primary) 34%, var(--sb-border));
    border-radius: var(--sb-radius);
    background:
        linear-gradient(140deg,
            color-mix(in srgb, var(--sb-primary) 13%, var(--sb-surface)) 0%,
            color-mix(in srgb, var(--sb-secondary, var(--sb-primary)) 9%, var(--sb-surface)) 52%,
            color-mix(in srgb, var(--sb-accent, var(--sb-primary)) 11%, var(--sb-surface)) 100%);
    color: color-mix(in srgb, var(--sb-primary) 72%, var(--sb-text));
    font-size: .9rem;
}

/* ---------------------------------------------------------------------------------------------------
   Icon
   --------------------------------------------------------------------------------------------------- */

.sb-content-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
}

.sb-content-icon.sb-shape-circle,
.sb-content-icon.sb-shape-square {
    /*
        The plate is tinted from the icon's own colour with color-mix, so a single colour property drives both.
        `currentColor` inside color-mix resolves against the element's colour, which is what the block sets inline.

        Градиент, а не заливка: ровное пятно в 12% выглядит как невыкрашенное место, особенно рядом с кнопкой
        того же цвета. Наклонный переход от 18% к 8% даёт плитке объём при той же средней светлоте — контраст
        иконки к подложке от этого не падает.
    */
    padding: .6em;
    background:
        linear-gradient(135deg,
            color-mix(in srgb, currentColor 19%, transparent) 0%,
            color-mix(in srgb, currentColor 8%, transparent) 100%);
    box-shadow: inset 0 0 0 1px color-mix(in srgb, currentColor 12%, transparent);
}

.sb-content-icon.sb-shape-circle { border-radius: var(--sb-radius-pill); }
.sb-content-icon.sb-shape-square { border-radius: var(--sb-radius-sm); }

/* ---------------------------------------------------------------------------------------------------
   Badge
   --------------------------------------------------------------------------------------------------- */

/*
    Ширина плашки ограничена родителем, перенос разрешён.

    `white-space: nowrap` тут стоял ради коротких подписей вроде «В наличии», и на десктопе он безобиден. На
    телефоне плашка живёт в карточке шириной в двести пикселей, и подпись «Бесплатная доставка от 3000» рисуется
    одной строкой поверх соседнего текста. Перенос в две строки некрасив; плашка, лежащая поверх чужого
    содержимого, — сломана. Одно ограничение ширины не помогло бы: с nowrap строка просто вылезает из коробки,
    какой бы max-width ей ни задали.
*/
[class*="sb-badge-"] {
    display: inline-flex;
    align-items: center;
    gap: .4em;
    max-width: 100%;
    padding: .3em .7em;
    border-radius: var(--sb-radius-sm);
    font-size: .8em;
    font-weight: 600;
    line-height: 1.3;
}

.sb-content-badge.sb-pill { border-radius: var(--sb-radius-pill); }

.sb-badge-primary { background: color-mix(in srgb, var(--sb-primary) 14%, transparent); color: var(--sb-ink-accent); }
.sb-badge-accent { background: color-mix(in srgb, var(--sb-accent) 20%, transparent); color: var(--sb-text); }
.sb-badge-success { background: color-mix(in srgb, var(--sb-success) 14%, transparent); color: var(--sb-success); }
.sb-badge-warning { background: color-mix(in srgb, var(--sb-warning) 16%, transparent); color: var(--sb-warning); }
.sb-badge-danger { background: color-mix(in srgb, var(--sb-danger) 14%, transparent); color: var(--sb-danger); }
.sb-badge-neutral { background: var(--sb-surface-alt); color: var(--sb-text-muted); }

/* ---------------------------------------------------------------------------------------------------
   List
   --------------------------------------------------------------------------------------------------- */

.sb-content-list {
    display: grid;
    gap: .6em;
    margin: 0;
    padding: 0;
    list-style: none;
}

.sb-content-list.sb-cols-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.sb-content-list.sb-cols-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }

.sb-list-item {
    display: flex;
    align-items: flex-start;
    gap: .6em;
    line-height: 1.55;
    min-width: 0;
}

.sb-list-marker {
    margin-top: .18em;
    color: var(--sb-ink-accent);
}

/* The bullet and number variants keep the browser's own markers, so they need the default list layout back. */
.sb-content-list.sb-marker-bullet,
.sb-content-list.sb-marker-number {
    display: block;
    padding-inline-start: 1.4em;
    list-style: revert;
}

.sb-content-list.sb-marker-bullet.sb-cols-2,
.sb-content-list.sb-marker-number.sb-cols-2 { columns: 2; }

.sb-content-list.sb-marker-bullet.sb-cols-3,
.sb-content-list.sb-marker-number.sb-cols-3 { columns: 3; }

.sb-content-list.sb-marker-bullet .sb-list-item,
.sb-content-list.sb-marker-number .sb-list-item { display: list-item; }

@container sb-page (max-width: 767.98px) {
    .sb-content-list.sb-cols-2,
    .sb-content-list.sb-cols-3 { grid-template-columns: minmax(0, 1fr); columns: auto; }
}

/* ---------------------------------------------------------------------------------------------------
   Quote
   --------------------------------------------------------------------------------------------------- */

.sb-content-quote {
    display: flex;
    flex-direction: column;
    gap: var(--sb-space-sm);
}

.sb-quote-text {
    font-size: 1.1em;
    line-height: 1.6;
}

.sb-content-quote.sb-quote-bar {
    padding-inline-start: var(--sb-space-md);
    border-inline-start: 3px solid var(--sb-primary);
}

.sb-content-quote.sb-quote-card {
    padding: var(--sb-space-lg);
    border: 1px solid var(--sb-border);
    border-radius: var(--sb-radius);
    background: var(--sb-surface);
}

.sb-content-quote.sb-quote-large .sb-quote-text {
    font-size: 1.5em;
    font-weight: 500;
    line-height: 1.4;
}

.sb-content-quote.sb-align-center { text-align: center; align-items: center; }
.sb-content-quote.sb-align-right { text-align: right; align-items: flex-end; }

.sb-quote-author {
    display: flex;
    align-items: center;
    gap: .7em;
    font-size: .92em;
}

.sb-quote-avatar {
    width: 44px;
    height: 44px;
    border-radius: var(--sb-radius-pill);
    object-fit: cover;
}

.sb-quote-name {
    display: flex;
    flex-direction: column;
}

.sb-quote-name cite {
    font-style: normal;
    font-weight: 600;
}

.sb-quote-role { color: var(--sb-text-muted); font-size: .9em; }

/* ---------------------------------------------------------------------------------------------------
   Code
   --------------------------------------------------------------------------------------------------- */

.sb-content-code {
    border: 1px solid var(--sb-border);
    border-radius: var(--sb-radius-sm);
    background: var(--sb-surface-alt);
    overflow: hidden;
}

.sb-code-language {
    padding: .35em .8em;
    border-bottom: 1px solid var(--sb-border);
    color: var(--sb-text-muted);
    font-family: var(--sb-font-mono);
    font-size: .78rem;
    text-transform: uppercase;
    letter-spacing: .06em;
}

.sb-code-pre {
    margin: 0;
    padding: var(--sb-space-md);
    font-family: var(--sb-font-mono);
    font-size: .88rem;
    line-height: 1.6;
    overflow-x: auto;
}

.sb-content-code.sb-code-wrap .sb-code-pre {
    white-space: pre-wrap;
    word-break: break-word;
    overflow-x: visible;
}

/* ---------------------------------------------------------------------------------------------------
   Table
   --------------------------------------------------------------------------------------------------- */

/*
    The scroll container, not the table, is what may overflow. Giving the wrapper a tabindex makes it reachable by
    keyboard — a scrollable region that only a mouse can reach is unusable for anyone who does not use one.
*/
.sb-table-scroll {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
}

.sb-table-scroll:focus-visible {
    outline: 2px solid var(--sb-ink-accent);
    outline-offset: 2px;
}

.sb-table {
    width: 100%;
    min-width: 34rem;
    border-collapse: collapse;
    text-align: start;
}

.sb-table caption {
    padding-bottom: .6em;
    color: var(--sb-text-muted);
    font-size: .88em;
    text-align: start;
}

.sb-table :is(th, td) {
    padding: .7em .9em;
    border-bottom: 1px solid var(--sb-border);
    text-align: start;
    vertical-align: top;
}

.sb-table th {
    font-weight: 650;
    white-space: nowrap;
}

.sb-content-table.sb-striped .sb-table tbody tr:nth-child(even) { background: var(--sb-surface-alt); }
.sb-content-table.sb-bordered .sb-table :is(th, td) { border: 1px solid var(--sb-border); }
.sb-content-table.sb-compact .sb-table :is(th, td) { padding: .42em .6em; font-size: .92em; }

/* blocks.sections.css */
/*
    Ready-made sections.

    Everything grid-shaped here shares one pattern: `grid-template-columns: repeat(var(--sb-grid-columns), …)` with the
    count coming from the block as a custom property, plus the same two collapse breakpoints. That is deliberate
    repetition of a short rule rather than one shared class — a section's grid and the layout grid primitive have
    different gaps, different alignment and different collapse points, and folding them together would mean every
    change to one being checked against the other.
*/

/* ---------------------------------------------------------------------------------------------------
   Shared: section heading and action row
   --------------------------------------------------------------------------------------------------- */

.sb-section-title { margin: 0; }

.sb-section-title.sb-size-md { font-size: var(--sb-font-size-h3); }
.sb-section-title.sb-size-lg { font-size: var(--sb-font-size-h2); }
.sb-section-title.sb-size-xl { font-size: var(--sb-font-size-h1); }

/*
    Нижняя граница clamp опущена с 2.3rem до 1.9: она работает только уже 42rem, то есть на телефоне, где
    36 пикселей на заголовок первого экрана — это две-три буквы в строке. Верхняя граница и наклон не тронуты,
    поэтому на десктопе размер прежний.
*/
.sb-section-title.sb-size-display {
    font-size: clamp(1.9rem, 5.5vw, 4.2rem);
    line-height: 1.05;
    letter-spacing: -0.03em;
}

.sb-action-note {
    margin: .8em 0 0;
    color: var(--sb-text-muted);
    font-size: .88em;
}

.sb-action-note.sb-align-center { text-align: center; }
.sb-action-note.sb-align-right { text-align: right; }

/*
    Off-screen but still in the accessibility tree. display:none would remove it from that tree too, which is the
    opposite of what a visually hidden label is for.
*/
.sb-visually-hidden {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
    border: 0;
}

/* ---------------------------------------------------------------------------------------------------
   Hero
   --------------------------------------------------------------------------------------------------- */

.sb-hero { position: relative; }

.sb-hero.sb-minh-50,
.sb-hero.sb-minh-75,
.sb-hero.sb-minh-100 { justify-content: center; }

.sb-hero-bg {
    background-size: cover;
    background-position: center;
    background-repeat: no-repeat;
}

/*
    The overlay for a hero with a background photograph. Defaults to a dark wash rather than to the `--sb-overlay`
    property alone, because a hero whose author picked a background and left the overlay on must be readable without
    them having also picked a colour.
*/
.sb-hero-bg.sb-has-overlay {
    --sb-overlay: linear-gradient(rgba(9, 11, 18, .58), rgba(9, 11, 18, .58));

    /*
        Краска — светлая, как и текст ниже. Пока этого не было, рамка фокуса на кнопке внутри фотогероя
        рисовалась фирменным цветом поверх тёмной заливки: у тёмно-синего или почти чёрного бренда она
        пропадала целиком, и первый экран оказывался единственным местом сайта, где с клавиатуры не видно,
        где ты. Полосы tone="primary" и tone="dark" делают ровно то же (blocks.layout.css) — фотогерой
        просто забыли.
    */
    --sb-ink-accent: #fff;
}

.sb-hero-bg.sb-has-overlay,
.sb-hero-bg.sb-has-overlay .sb-section-title,
.sb-hero-bg.sb-has-overlay .sb-lead { color: #fff; }

.sb-hero-bg.sb-has-overlay .sb-eyebrow { color: #fff; opacity: .85; }

.sb-hero-media { margin-top: var(--sb-space-xl); }

.sb-hero-media img {
    width: 100%;
    border-radius: var(--sb-radius);
    box-shadow: var(--sb-shadow-xl);
}

/* ---------------------------------------------------------------------------------------------------
   Hero with media beside the text
   --------------------------------------------------------------------------------------------------- */

.sb-split {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--sb-space-2xl);
    align-items: center;
}

/*
    Опубликованный первый экран без фотографии не оставляет за собой пустую половину.

    В редакторе на этом месте подсказка, и колонка занята; у посетителя подсказки нет, и полстраницы уходит в
    никуда — текст жмётся к левому краю, справа воздух. Это тот случай, когда шаблон выглядит сломанным не по
    своей вине: автор просто ещё не дошёл до картинки. Пустая колонка убирается, текст занимает строку целиком,
    и мера строки не даёт ему растянуться в нечитаемую простыню.
*/
.sb-split:has(> .sb-split-media:empty) { grid-template-columns: minmax(0, 1fr); }
.sb-split > .sb-split-media:empty { display: none; }
.sb-split:has(> .sb-split-media:empty) .sb-split-text { max-width: var(--sb-width-narrow); }

/*
    The media side is a visual swap, not a reordering of the markup: the text stays first in the document so the reading
    order remains heading-then-image for a screen reader and a crawler.
*/
.sb-hero-split.sb-media-left .sb-split-text { order: 2; }
.sb-hero-split.sb-media-left .sb-split-media { order: 1; }

.sb-split-text .sb-section-head { margin-bottom: var(--sb-space-md); }

.sb-split-highlights {
    display: flex;
    flex-direction: column;
    gap: .6em;
    margin: 0 0 var(--sb-space-lg);
    padding: 0;
    list-style: none;
}

/*
    Галочка не сжимается, текст рядом с ней — обязан.

    Строка из иконки и подписи — это флекс-контейнер, а флекс-элемент по умолчанию не становится уже своего
    содержимого. На телефоне подпись из одного длинного слова поэтому не переносится, а выезжает за галочку и
    за край блока. То же самое повторяется в шагах, ленте времени, контактах и списке тарифа, поэтому
    `min-width: 0` стоит у каждого такого текстового соседа иконки.
*/
.sb-split-highlights li {
    display: flex;
    align-items: flex-start;
    gap: .6em;
    line-height: 1.5;
    min-width: 0;
}

.sb-split-highlights li > span { min-width: 0; }

.sb-split-check { margin-top: .15em; color: var(--sb-success); }

.sb-split-image {
    width: 100%;
    object-fit: cover;
}

.sb-split-image.sb-rounded { border-radius: var(--sb-radius); }
.sb-split-image.sb-shadow { box-shadow: var(--sb-shadow-xl); }

/* ---------------------------------------------------------------------------------------------------
   Features: grid
   --------------------------------------------------------------------------------------------------- */

.sb-feature-grid {
    display: grid;
    grid-template-columns: repeat(var(--sb-grid-columns, 3), minmax(0, 1fr));
    gap: var(--sb-space-lg);
    align-items: stretch;
}

.sb-feature {
    display: flex;
    flex-direction: column;
    gap: .7em;
    color: inherit;
    text-decoration: none;
}

.sb-feature-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 46px;
    height: 46px;
    color: var(--sb-ink-accent);
    flex: none;
}

.sb-feature-icon.sb-shape-circle,
.sb-feature-icon.sb-shape-square {
    background:
        linear-gradient(135deg,
            color-mix(in srgb, currentColor 19%, transparent) 0%,
            color-mix(in srgb, currentColor 8%, transparent) 100%);
    box-shadow: inset 0 0 0 1px color-mix(in srgb, currentColor 12%, transparent);
}

.sb-feature-icon.sb-shape-circle { border-radius: var(--sb-radius-pill); }
.sb-feature-icon.sb-shape-square { border-radius: var(--sb-radius-sm); }

.sb-feature-title { margin: 0; font-size: var(--sb-font-size-h4); }

.sb-feature-text {
    margin: 0;
    color: var(--sb-text-muted);
    line-height: 1.6;
}

.sb-feature.sb-linked { transition: transform .15s ease, box-shadow .2s ease, border-color .2s ease; }

.sb-feature.sb-linked:hover {
    text-decoration: none;
    transform: translateY(-2px);
    box-shadow: var(--sb-shadow-lg);
}

.sb-feature.sb-linked:hover .sb-feature-title {
    text-decoration: underline;
    text-underline-offset: 3px;
}

.sb-feature.sb-linked:focus-visible {
    outline: 2px solid var(--sb-ink-accent);
    outline-offset: 3px;
}

/* ---------------------------------------------------------------------------------------------------
   Features: rows
   --------------------------------------------------------------------------------------------------- */

.sb-feature-rows {
    display: flex;
    flex-direction: column;
    gap: var(--sb-space-2xl);
}

/*
    Список, в котором ни у одного ряда нет картинки, — это пункты с иконками, а не чередующиеся развороты.
    Шаг в 2XL выбран под ряды-развороты высотой в фотографию; между текстовыми абзацами высотой в сотню
    пикселей он оставляет больше пустоты, чем содержимого, — ровно «гигантские пустоты» policy-страниц.
    :where обнуляет добавку специфичности, чтобы авторский gap из инспектора выигрывал при равном селекторе.
    В редакторе на месте пустой картинки подсказка, и там шаг остаётся крупным — как у .sb-split выше.
*/
.sb-feature-rows:where(:not(:has(.sb-feature-row-media:not(:empty)))) { gap: var(--sb-space-xl); }

.sb-feature-row {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--sb-space-xl);
    align-items: center;
}

.sb-feature-row.sb-media-left .sb-feature-row-text { order: 2; }
.sb-feature-row.sb-media-left .sb-feature-row-media { order: 1; }

/* Строка без картинки — по той же причине, что и первый экран: см. .sb-split выше. */
.sb-feature-row:has(> .sb-feature-row-media:empty) { grid-template-columns: minmax(0, 1fr); }
.sb-feature-row > .sb-feature-row-media:empty { display: none; }

.sb-feature-row-text {
    display: flex;
    flex-direction: column;
    gap: .7em;
    align-items: flex-start;
    min-width: 0;
}

.sb-feature-image {
    width: 100%;
    object-fit: cover;
}

.sb-feature-image.sb-rounded { border-radius: var(--sb-radius); }

.sb-feature-link {
    display: inline-flex;
    align-items: center;
    gap: .4em;
    color: var(--sb-ink-accent);
    font-weight: 600;
}

.sb-feature-link:hover { text-decoration: none; gap: .6em; }

/* ---------------------------------------------------------------------------------------------------
   Steps
   --------------------------------------------------------------------------------------------------- */

.sb-step-list {
    display: grid;
    counter-reset: sb-step;
    gap: var(--sb-space-lg);
    margin: 0;
    padding: 0;
    list-style: none;
}

.sb-step-list.sb-horizontal { grid-template-columns: repeat(var(--sb-grid-columns, 4), minmax(0, 1fr)); }
.sb-step-list.sb-vertical { grid-template-columns: minmax(0, 1fr); }

.sb-step {
    position: relative;
    display: flex;
    gap: var(--sb-space-md);
    counter-increment: sb-step;
    min-width: 0;
}

.sb-step-body { min-width: 0; }

.sb-step-list.sb-horizontal .sb-step { flex-direction: column; }

.sb-step-marker {
    position: relative;
    z-index: 1;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 46px;
    height: 46px;
    flex: none;
    border-radius: var(--sb-radius-pill);
    background: var(--sb-surface);
    border: 2px solid var(--sb-primary);
    color: var(--sb-ink-accent);
    font-weight: 700;
}

/*
    The number comes from the list counter, not from the data: inserting a step in the middle renumbers the rest, which
    is the whole reason to use this section instead of four cards.
*/
.sb-step-list.sb-numbered .sb-step-marker::before { content: counter(sb-step); }
.sb-step-list.sb-numbered .sb-step-marker .sb-icon { display: none; }

/*
    The connector is drawn from the marker rather than as a line behind the whole row, so it never sticks out past the
    first or last step. `calc(100% ...)` spans the gap to the next marker.
*/
.sb-step-list.sb-connected.sb-horizontal .sb-step:not(:last-child) .sb-step-marker::after {
    content: "";
    position: absolute;
    top: 50%;
    left: 100%;
    width: calc(var(--sb-space-lg) + 2px);
    height: 2px;
    background: color-mix(in srgb, var(--sb-primary) 35%, transparent);
}

.sb-step-list.sb-connected.sb-vertical .sb-step:not(:last-child) .sb-step-marker::after {
    content: "";
    position: absolute;
    top: 100%;
    left: 50%;
    width: 2px;
    height: calc(var(--sb-space-lg) + 2px);
    transform: translateX(-50%);
    background: color-mix(in srgb, var(--sb-primary) 35%, transparent);
}

.sb-step-title { margin: 0 0 .3em; font-size: var(--sb-font-size-h4); }

.sb-step-text {
    margin: 0;
    color: var(--sb-text-muted);
    line-height: 1.6;
}

/* ---------------------------------------------------------------------------------------------------
   Stats
   --------------------------------------------------------------------------------------------------- */

.sb-stat-list {
    display: grid;
    grid-template-columns: repeat(var(--sb-grid-columns, 4), minmax(0, 1fr));
    gap: var(--sb-space-lg);
    margin: 0;
}

.sb-stat {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: .3em;
    text-align: center;
}

.sb-stat-list.sb-divided .sb-stat + .sb-stat {
    border-inline-start: 1px solid var(--sb-border);
}

.sb-stat-icon { color: var(--sb-ink-accent); margin-bottom: .2em; }

.sb-stat-value {
    margin: 0;
    font-family: var(--sb-font-heading);
    font-size: clamp(2rem, 4vw, 3rem);
    font-weight: 700;
    line-height: 1.05;
    letter-spacing: -0.02em;
}

.sb-stat-suffix { color: var(--sb-ink-accent); }

.sb-stat-label {
    margin: 0;
    color: var(--sb-text-muted);
    font-size: .92em;
    line-height: 1.4;
}

/* ---------------------------------------------------------------------------------------------------
   Logos
   --------------------------------------------------------------------------------------------------- */

.sb-logo-list {
    display: grid;
    grid-template-columns: repeat(var(--sb-grid-columns, 5), minmax(0, 1fr));
    gap: var(--sb-space-lg);
    align-items: center;
    margin: 0;
    padding: 0;
    list-style: none;
}

.sb-logo-link {
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 48px;
}

.sb-logo img {
    max-height: 42px;
    width: auto;
    object-fit: contain;
}

.sb-logo-name {
    color: var(--sb-text-muted);
    font-weight: 650;
    font-size: 1.05em;
    letter-spacing: -0.01em;
    text-align: center;
}

/*
    Grayscale plus reduced opacity, lifting on hover. A row of logos in their own brand colours reads as a strip of
    advertisements rather than as a list, and the list is the point.
*/
.sb-logos.sb-grayscale .sb-logo img {
    filter: grayscale(1);
    opacity: .62;
    transition: filter .2s ease, opacity .2s ease;
}

.sb-logos.sb-grayscale .sb-logo:hover img {
    filter: grayscale(0);
    opacity: 1;
}

/* ---------------------------------------------------------------------------------------------------
   Testimonials
   --------------------------------------------------------------------------------------------------- */

.sb-testimonial-grid {
    display: grid;
    grid-template-columns: repeat(var(--sb-grid-columns, 3), minmax(0, 1fr));
    gap: var(--sb-space-lg);
    align-items: start;
}

.sb-testimonial {
    display: flex;
    flex-direction: column;
    gap: var(--sb-space-sm);
    margin: 0;
    padding: var(--sb-space-lg);
    background: var(--sb-surface);
    border: 1px solid var(--sb-border);
    border-radius: var(--sb-radius);
    height: 100%;
}

.sb-testimonial-rating { display: flex; gap: 2px; }

.sb-star { color: var(--sb-border); }

/* Иконки набора рисуются контуром — на самом svg стоит атрибут fill="none". Правило листа сильнее атрибута
   представления, поэтому залитой звезде заливка включается отсюда; currentColor — чтобы цвет жил в одном
   объявлении и токен --sb-warning красил и контур, и заливку в обеих темах. */
.sb-star.sb-filled { color: var(--sb-warning); fill: currentColor; }

.sb-testimonial-quote {
    margin: 0;
    font-size: 1.02em;
    line-height: 1.65;
}

.sb-testimonial-author {
    display: flex;
    align-items: center;
    gap: .7em;
    margin-top: auto;
    padding-top: var(--sb-space-sm);
}

.sb-testimonial-avatar {
    width: 42px;
    height: 42px;
    flex: none;
    border-radius: var(--sb-radius-pill);
    object-fit: cover;
}

.sb-testimonial-who { display: flex; flex-direction: column; min-width: 0; }

.sb-testimonial-who cite { font-style: normal; font-weight: 650; }

.sb-testimonial-role {
    color: var(--sb-text-muted);
    font-size: .88em;
}

/* ---- Carousel -----------------------------------------------------------------------------------
   Scroll-snap does the moving; blocks.js only drives the buttons and the dots. That keeps touch swiping,
   keyboard scrolling and momentum working before the script runs — and if it never runs, this is still a
   scrollable strip rather than a stack of clipped slides.
   ------------------------------------------------------------------------------------------------- */

.sb-carousel { position: relative; }

.sb-carousel-track {
    display: flex;
    gap: var(--sb-space-lg);
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    scroll-behavior: smooth;
    scrollbar-width: none;
    padding-bottom: 4px;
}

.sb-carousel-track::-webkit-scrollbar { display: none; }

.sb-carousel-slide {
    flex: 0 0 calc((100% - var(--sb-space-lg) * 2) / 3);
    min-width: 260px;
    scroll-snap-align: start;
}

.sb-carousel-controls {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: var(--sb-space-md);
    margin-top: var(--sb-space-lg);
}

.sb-carousel-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 38px;
    height: 38px;
    border: 1px solid var(--sb-border);
    border-radius: var(--sb-radius-pill);
    background: var(--sb-surface);
    color: var(--sb-text);
    cursor: pointer;
}

.sb-carousel-btn:hover { border-color: var(--sb-ink-accent); color: var(--sb-ink-accent); }

.sb-carousel-dots { display: flex; gap: 6px; }

.sb-carousel-dot {
    width: 8px;
    height: 8px;
    padding: 0;
    border: 0;
    border-radius: var(--sb-radius-pill);
    background: var(--sb-border);
    cursor: pointer;
    transition: width .2s ease, background-color .2s ease;
}

.sb-carousel-dot.sb-active {
    width: 22px;
    background: var(--sb-primary);
}

/* ---------------------------------------------------------------------------------------------------
   FAQ
   --------------------------------------------------------------------------------------------------- */

.sb-faq-list {
    display: grid;
    grid-template-columns: repeat(var(--sb-grid-columns, 1), minmax(0, 1fr));
    gap: var(--sb-space-sm);
    align-items: start;
    /* Узкая колонка закона: вопрос-ответ читают, как любой другой текст. */
    max-width: var(--sb-width-narrow);
    margin-inline: auto;
}

.sb-faq-list.sb-cols-2 { max-width: none; gap: var(--sb-space-md) var(--sb-space-lg); }

.sb-faq-item {
    border: 1px solid var(--sb-border);
    border-radius: var(--sb-radius-sm);
    background: var(--sb-surface);
    overflow: hidden;
}

.sb-faq-question { margin: 0; font-size: 1em; }

.sb-faq-trigger {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--sb-space-sm);
    width: 100%;
    padding: var(--sb-space-md);
    border: 0;
    background: transparent;
    color: inherit;
    font: inherit;
    font-weight: 600;
    text-align: start;
    cursor: pointer;
}

.sb-faq-trigger:hover { color: var(--sb-ink-accent); }

.sb-faq-trigger:focus-visible {
    outline: 2px solid var(--sb-ink-accent);
    outline-offset: -2px;
}

.sb-faq-chevron {
    flex: none;
    color: var(--sb-text-muted);
    transition: transform .2s ease;
}

/*
    Panels are driven by data-sb-open, which the markup sets before any script runs. A closed panel is display:none
    rather than max-height:0 — animating an unknown height needs JavaScript to measure it, and a height guess that is
    too small clips the answer.
*/
.sb-faq-answer { display: none; }

.sb-faq-item[data-sb-open="1"] .sb-faq-answer { display: block; }

.sb-faq-item[data-sb-open="1"] .sb-faq-chevron { transform: rotate(180deg); }

.sb-faq-answer-body {
    padding: 0 var(--sb-space-md) var(--sb-space-md);
    color: var(--sb-text-muted);
}

/* ---------------------------------------------------------------------------------------------------
   Pricing
   --------------------------------------------------------------------------------------------------- */

.sb-pricing-grid {
    display: grid;
    grid-template-columns: repeat(var(--sb-grid-columns, 3), minmax(0, 1fr));
    gap: var(--sb-space-lg);
    align-items: stretch;
}

.sb-pricing-plan {
    position: relative;
    display: flex;
    flex-direction: column;
    gap: var(--sb-space-md);
    padding: var(--sb-space-xl);
}

/*
    The featured plan is lifted rather than merely coloured. A colour difference alone is invisible to a good share of
    visitors; the scale and the shadow are not.
*/
.sb-pricing-plan.sb-featured {
    border: 2px solid var(--sb-primary);
    transform: translateY(-6px);
}

/*
    Плашка над тарифом центрирована абсолютно, то есть выходит из потока и ничем не ограничена по ширине. С
    `white-space: nowrap` подпись длиннее карточки вылезала за неё с обеих сторон — а на телефоне заодно и за
    край экрана, добавляя странице горизонтальную прокрутку. Ограничение по ширине без разрешения переноса
    ничего не даёт: строка просто вылезает из ограниченной коробки. Поэтому переносить разрешено, а ширина
    привязана к карточке.
*/
.sb-pricing-badge {
    position: absolute;
    top: 0;
    left: 50%;
    transform: translate(-50%, -50%);
    max-width: calc(100% - var(--sb-space-md));
    padding: .25em .8em;
    border-radius: var(--sb-radius-pill);
    background: var(--sb-primary);
    color: var(--sb-primary-contrast);
    font-size: .76em;
    font-weight: 700;
    letter-spacing: .04em;
    text-align: center;
    text-transform: uppercase;
}

.sb-pricing-head { display: flex; flex-direction: column; gap: .4em; }

.sb-pricing-name {
    margin: 0;
    font-size: var(--sb-font-size-h4);
}

.sb-pricing-price {
    display: flex;
    align-items: baseline;
    flex-wrap: wrap;
    gap: .25em;
    margin: 0;
}

.sb-pricing-amount {
    font-family: var(--sb-font-heading);
    font-size: 2.4rem;
    font-weight: 700;
    line-height: 1;
    letter-spacing: -0.02em;
}

.sb-pricing-currency { font-size: 1.3rem; font-weight: 650; }

.sb-pricing-period { color: var(--sb-text-muted); font-size: .92em; }

.sb-pricing-description {
    margin: 0;
    color: var(--sb-text-muted);
    font-size: .94em;
    line-height: 1.55;
}

.sb-pricing-features {
    display: flex;
    flex-direction: column;
    gap: .55em;
    margin: 0;
    padding: var(--sb-space-sm) 0 0;
    border-top: 1px solid var(--sb-border);
    list-style: none;
}

.sb-pricing-features li {
    display: flex;
    align-items: flex-start;
    gap: .55em;
    line-height: 1.5;
    min-width: 0;
}

.sb-pricing-features li > span { min-width: 0; }

.sb-pricing-check { margin-top: .18em; color: var(--sb-success); }

.sb-pricing-cta { margin-top: auto; }

/* ---------------------------------------------------------------------------------------------------
   Team
   --------------------------------------------------------------------------------------------------- */

.sb-team-list {
    display: grid;
    grid-template-columns: repeat(var(--sb-grid-columns, 4), minmax(0, 1fr));
    gap: var(--sb-space-lg);
    margin: 0;
    padding: 0;
    list-style: none;
}

.sb-team-member {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: .35em;
    text-align: center;
}

.sb-team-photo {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 100%;
    max-width: 168px;
    aspect-ratio: 1;
    margin-bottom: .6em;
    overflow: hidden;
    background: var(--sb-surface-alt);
}

.sb-team-photo img { width: 100%; height: 100%; object-fit: cover; }

.sb-team-photo.sb-shape-circle { border-radius: var(--sb-radius-pill); }
.sb-team-photo.sb-shape-rounded { border-radius: var(--sb-radius); }
.sb-team-photo.sb-shape-square { border-radius: 0; }

.sb-team-initials {
    color: var(--sb-text-muted);
    font-family: var(--sb-font-heading);
    font-size: 2rem;
    font-weight: 700;
}

.sb-team-name { margin: 0; font-size: 1.05em; }

.sb-team-role { margin: 0; color: var(--sb-ink-accent); font-size: .9em; font-weight: 600; }

.sb-team-bio {
    margin: .3em 0 0;
    color: var(--sb-text-muted);
    font-size: .9em;
    line-height: 1.55;
}

.sb-team-contacts { display: flex; gap: .4em; margin-top: .6em; }

.sb-team-contact {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 32px;
    height: 32px;
    border: 1px solid var(--sb-border);
    border-radius: var(--sb-radius-pill);
    color: var(--sb-text-muted);
}

.sb-team-contact:hover {
    border-color: var(--sb-ink-accent);
    color: var(--sb-ink-accent);
    text-decoration: none;
}

/* ---------------------------------------------------------------------------------------------------
   Timeline
   --------------------------------------------------------------------------------------------------- */

.sb-timeline-list {
    margin: 0;
    padding: 0;
    list-style: none;
}

.sb-timeline-item {
    position: relative;
    display: flex;
    gap: var(--sb-space-md);
    padding-bottom: var(--sb-space-xl);
    min-width: 0;
}

.sb-timeline-item:last-child { padding-bottom: 0; }

.sb-timeline-marker {
    position: relative;
    z-index: 1;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 34px;
    height: 34px;
    flex: none;
    border-radius: var(--sb-radius-pill);
    background: var(--sb-surface);
    border: 2px solid var(--sb-primary);
    color: var(--sb-ink-accent);
}

/* The rail is drawn from each marker down to the next, so it stops at the last item instead of running past it. */
.sb-timeline-item:not(:last-child) .sb-timeline-marker::after {
    content: "";
    position: absolute;
    top: 100%;
    left: 50%;
    width: 2px;
    height: calc(var(--sb-space-xl) + 2px);
    transform: translateX(-50%);
    background: color-mix(in srgb, var(--sb-primary) 30%, transparent);
}

.sb-timeline-body { padding-top: .2em; min-width: 0; }

.sb-timeline-date {
    display: block;
    color: var(--sb-ink-accent);
    font-size: .86em;
    font-weight: 700;
    letter-spacing: .04em;
    text-transform: uppercase;
}

.sb-timeline-title { margin: .2em 0 .3em; font-size: var(--sb-font-size-h4); }

.sb-timeline-text {
    margin: 0;
    color: var(--sb-text-muted);
    line-height: 1.6;
}

/* ---------------------------------------------------------------------------------------------------
   Posts
   --------------------------------------------------------------------------------------------------- */

.sb-post-header {
    display: flex;
    align-items: flex-end;
    justify-content: space-between;
    gap: var(--sb-space-lg);
    flex-wrap: wrap;
}

.sb-post-header .sb-section-head { margin-bottom: var(--sb-space-xl); }

.sb-post-more {
    display: inline-flex;
    align-items: center;
    gap: .4em;
    margin-bottom: var(--sb-space-xl);
    color: var(--sb-ink-accent);
    font-weight: 600;
    /* Подпись пишет автор, и «Все публикации нашего блога» в узкой колонке рвать надо — со `nowrap` она
       выезжала за край. Стрелка при этом должна остаться на строке с последним словом. */
    text-wrap: balance;
}

.sb-post-more:hover { text-decoration: none; gap: .6em; }

.sb-post-list.sb-cards {
    display: grid;
    grid-template-columns: repeat(var(--sb-grid-columns, 3), minmax(0, 1fr));
    gap: var(--sb-space-lg);
    align-items: stretch;
}

.sb-post-list.sb-rows {
    display: flex;
    flex-direction: column;
    gap: var(--sb-space-md);
}

.sb-post {
    display: flex;
    flex-direction: column;
    overflow: hidden;
    border: 1px solid var(--sb-border);
    border-radius: var(--sb-radius);
    background: var(--sb-surface);
    color: inherit;
    text-decoration: none;
    height: 100%;
}

.sb-post-list.sb-rows .sb-post { flex-direction: row; align-items: stretch; }

.sb-post-media { flex: none; }

.sb-post-list.sb-cards .sb-post-media { aspect-ratio: 16 / 9; }
.sb-post-list.sb-rows .sb-post-media { width: 220px; }

.sb-post-media img { width: 100%; height: 100%; object-fit: cover; }

.sb-post-body {
    display: flex;
    flex-direction: column;
    gap: .45em;
    padding: var(--sb-space-md);
    /* В раскладке строками карточка — флекс-ряд «картинка + текст»; без этого текст не сжимается под картинку. */
    min-width: 0;
}

.sb-post-meta {
    color: var(--sb-text-muted);
    font-size: .82em;
    text-transform: uppercase;
    letter-spacing: .05em;
}

.sb-post-title { margin: 0; font-size: 1.12em; line-height: 1.35; }

.sb-post-excerpt {
    margin: 0;
    color: var(--sb-text-muted);
    font-size: .94em;
    line-height: 1.6;
}

.sb-post.sb-linked { transition: transform .15s ease, box-shadow .2s ease, border-color .2s ease; }

.sb-post.sb-linked:hover {
    text-decoration: none;
    transform: translateY(-2px);
    box-shadow: var(--sb-shadow-lg);
    border-color: color-mix(in srgb, var(--sb-primary) 35%, var(--sb-border));
}

.sb-post.sb-linked:hover .sb-post-title {
    text-decoration: underline;
    text-underline-offset: 3px;
}

.sb-post.sb-linked:focus-visible {
    outline: 2px solid var(--sb-ink-accent);
    outline-offset: 3px;
}

/* ---------------------------------------------------------------------------------------------------
   Call to action
   --------------------------------------------------------------------------------------------------- */

.sb-cta-inner .sb-section-head { margin-bottom: var(--sb-space-lg); }

.sb-cta-inner.sb-split {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--sb-space-xl);
    flex-wrap: wrap;
}

/* Ограничение — чтобы текст не выдавил кнопку из строки; ширина — узкая колонка закона, не своя цифра. */
.sb-cta-inner.sb-split .sb-section-head { margin-bottom: 0; max-width: var(--sb-width-narrow); }

/* ---------------------------------------------------------------------------------------------------
   Contact and forms
   --------------------------------------------------------------------------------------------------- */

.sb-contact-grid {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--sb-space-2xl);
    align-items: start;
}

.sb-contact-grid.sb-single { grid-template-columns: minmax(0, 1fr); }

.sb-contact-details {
    display: flex;
    flex-direction: column;
    gap: var(--sb-space-md);
    margin: 0;
    padding: 0;
    list-style: none;
}

.sb-contact-details li { display: flex; align-items: flex-start; gap: .8em; min-width: 0; }

.sb-contact-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 40px;
    height: 40px;
    flex: none;
    border-radius: var(--sb-radius-sm);
    background: color-mix(in srgb, var(--sb-primary) 10%, transparent);
    color: var(--sb-ink-accent);
}

/*
    Адрес и почта — самые длинные неразрывные строки на сайте. Без min-width колонка с ними не сжимается ниже
    своей самой длинной строки, и на телефоне почта уезжает за иконку и за край карточки.
*/
.sb-contact-text { display: flex; flex-direction: column; min-width: 0; }

.sb-contact-label {
    color: var(--sb-text-muted);
    font-size: .82em;
    text-transform: uppercase;
    letter-spacing: .05em;
}

/* Телефон и почта — ссылки, но читаются как значение: цвет берётся у текста, а не у ссылки. */
.sb-contact-value { color: inherit; text-decoration: none; }
.sb-contact-value:hover { text-decoration: underline; }

.sb-form {
    display: flex;
    flex-direction: column;
    gap: var(--sb-space-md);
    padding: var(--sb-space-xl);
    border: 1px solid var(--sb-border);
    border-radius: var(--sb-radius);
    background: var(--sb-surface);
}

.sb-form-row { display: flex; flex-direction: column; gap: .35em; }

.sb-form-row label {
    color: var(--sb-text-muted);
    font-size: .88em;
    font-weight: 600;
}

.sb-form-row :is(input, textarea) {
    width: 100%;
    padding: .7em .9em;
    border: 1px solid var(--sb-border);
    border-radius: var(--sb-radius-sm);
    background: var(--sb-surface);
    color: var(--sb-text);
    font: inherit;
}

.sb-form-row textarea { resize: vertical; }

.sb-form-row :is(input, textarea):focus-visible {
    outline: 2px solid var(--sb-ink-accent);
    outline-offset: -1px;
    border-color: var(--sb-ink-accent);
}

/*
    The honeypot. Positioned off-screen rather than display:none or type="hidden": a hidden input is the first thing a
    bot skips, and a text input it cannot see is one it fills in. aria-hidden and tabindex="-1" are on the markup, so a
    keyboard or a screen reader never reaches it either.
*/
.sb-form-honeypot {
    position: absolute;
    left: -9999px;
    width: 1px;
    height: 1px;
    overflow: hidden;
}

.sb-form-consent {
    margin: 0;
    color: var(--sb-text-muted);
    font-size: .82em;
    line-height: 1.5;
}

.sb-form-status {
    margin: 0;
    font-size: .9em;
    font-weight: 600;
}

.sb-form-status:empty { display: none; }

.sb-form-status.sb-sending { color: var(--sb-text-muted); }
.sb-form-status.sb-success { color: var(--sb-success); }
.sb-form-status.sb-error { color: var(--sb-danger); }

/* ---------------------------------------------------------------------------------------------------
   Newsletter
   --------------------------------------------------------------------------------------------------- */

.sb-newsletter-inner {
    max-width: var(--sb-width-narrow);
    margin-inline: auto;
}

.sb-newsletter-form { display: flex; flex-direction: column; gap: var(--sb-space-sm); }

.sb-newsletter-field { display: flex; gap: .6em; }

.sb-newsletter-field input {
    flex: 1;
    min-width: 0;
    padding: .8em 1em;
    border: 1px solid var(--sb-border);
    border-radius: var(--sb-radius-sm);
    background: var(--sb-surface);
    color: var(--sb-text);
    font: inherit;
}

.sb-newsletter-field input:focus-visible {
    outline: 2px solid var(--sb-ink-accent);
    outline-offset: -1px;
    border-color: var(--sb-ink-accent);
}

.sb-newsletter-form .sb-form-consent,
.sb-newsletter-form .sb-form-status { text-align: center; }

/* ---------------------------------------------------------------------------------------------------
   Responsive collapse

   Last in the file so it overrides the desktop rules above. Per-node overrides from the inspector are emitted after
   this stylesheet, so an author who sets an explicit column count for a breakpoint still wins.
   --------------------------------------------------------------------------------------------------- */

@container sb-page (max-width: 991.98px) {
    .sb-feature-grid,
    .sb-pricing-grid,
    .sb-team-list,
    .sb-testimonial-grid,
    .sb-post-list.sb-cards,
    .sb-step-list.sb-horizontal {
        grid-template-columns: repeat(min(var(--sb-grid-columns, 3), 2), minmax(0, 1fr));
    }

    .sb-logo-list { grid-template-columns: repeat(min(var(--sb-grid-columns, 5), 3), minmax(0, 1fr)); }

    .sb-stat-list { grid-template-columns: repeat(min(var(--sb-grid-columns, 4), 2), minmax(0, 1fr)); }

    /* The connector assumes a single row; once the grid wraps it points at nothing. */
    .sb-step-list.sb-connected.sb-horizontal .sb-step .sb-step-marker::after { display: none; }

    .sb-carousel-slide { flex-basis: calc((100% - var(--sb-space-lg)) / 2); }

    .sb-pricing-plan.sb-featured { transform: none; }

    .sb-faq-list.sb-cols-2 { grid-template-columns: minmax(0, 1fr); }
}

@container sb-page (max-width: 767.98px) {
    .sb-split,
    .sb-feature-row,
    .sb-contact-grid,
    .sb-feature-grid,
    .sb-pricing-grid,
    .sb-team-list,
    .sb-testimonial-grid,
    .sb-post-list.sb-cards,
    .sb-step-list.sb-horizontal,
    .sb-stat-list {
        grid-template-columns: minmax(0, 1fr);
    }

    .sb-logo-list { grid-template-columns: repeat(2, minmax(0, 1fr)); }

    /*
        On a phone the media goes below the text in both hero variants and in every feature row, whichever side the
        author chose: the heading has to be the first thing on screen.
    */
    .sb-hero-split.sb-media-left .sb-split-text,
    .sb-feature-row.sb-media-left .sb-feature-row-text { order: 1; }

    .sb-hero-split.sb-media-left .sb-split-media,
    .sb-feature-row.sb-media-left .sb-feature-row-media { order: 2; }

    .sb-step-list.sb-horizontal .sb-step { flex-direction: row; }

    .sb-stat-list.sb-divided .sb-stat + .sb-stat {
        border-inline-start: 0;
        border-top: 1px solid var(--sb-border);
        padding-top: var(--sb-space-md);
    }

    .sb-carousel-slide { flex-basis: 100%; min-width: 0; }

    .sb-post-list.sb-rows .sb-post { flex-direction: column; }
    .sb-post-list.sb-rows .sb-post-media { width: 100%; aspect-ratio: 16 / 9; }

    .sb-newsletter-field { flex-direction: column; }

    .sb-cta-inner.sb-split { flex-direction: column; align-items: stretch; }

    /*
        Внутренние поля на телефоне — по тем же соображениям, что и у карточки в blocks.core.css: у тарифа
        было 64 пикселя с каждой стороны при ширине карточки в 296, и название тарифа физически не помещалось
        в оставшиеся 168. Форма и отзыв шли следом с сорока.
    */
    .sb-pricing-plan,
    .sb-testimonial,
    .sb-form {
        padding: var(--sb-space-md);
    }

    /* Цена набрана крупно намеренно, но 2.4rem — это размер для колонки в треть экрана, а не во весь. */
    .sb-pricing-amount { font-size: 2rem; }
}

/* blocks.commerce.css */
/*
    Магазин: сетка товаров, карточка, корзина, оформление, заказы.

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

/* ---------------------------------------------------------------------------------------------------
   Сетка товаров
   --------------------------------------------------------------------------------------------------- */
.sb-products-grid {
    display: grid;
    grid-template-columns: repeat(var(--sb-products-cols, 4), minmax(0, 1fr));
    gap: var(--sb-products-gap, 20px);
}

.sb-products-cols-1 { --sb-products-cols: 1; }
.sb-products-cols-2 { --sb-products-cols: 2; }
.sb-products-cols-3 { --sb-products-cols: 3; }
.sb-products-cols-4 { --sb-products-cols: 4; }
.sb-products-cols-5 { --sb-products-cols: 5; }
.sb-products-cols-6 { --sb-products-cols: 6; }

.sb-products-gap-sm { --sb-products-gap: 12px; }
.sb-products-gap-md { --sb-products-gap: 20px; }
.sb-products-gap-lg { --sb-products-gap: 32px; }

.sb-product {
    display: flex;
    flex-direction: column;
    gap: 8px;
    padding: 12px;
    background: var(--sb-surface);
    border: 1px solid var(--sb-border);
    border-radius: var(--sb-radius);
    transition: border-color .15s ease, transform .15s ease;
}

.sb-product:hover { border-color: var(--sb-ink-accent); }

.sb-product-link { display: block; color: inherit; text-decoration: none; }

/*
    Соотношение сторон задано, а не оставлено содержимому: карточки, чья высота зависит от того, какая
    картинка успела загрузиться, дёргают всю сетку на каждой второй загрузке.
*/
.sb-product-media {
    aspect-ratio: 1 / 1;
    display: grid;
    place-items: center;
    overflow: hidden;
    background: var(--sb-surface-alt);
    border-radius: var(--sb-radius-sm);
}

.sb-product-media img { width: 100%; height: 100%; object-fit: contain; }

.sb-product-noimage {
    width: 44px;
    height: 44px;
    border-radius: 8px;
    background: var(--sb-border);
}

.sb-product-name {
    margin: 6px 0 0;
    font-size: 14.5px;
    font-weight: 550;
    line-height: 1.4;
}

.sb-product-code { color: var(--sb-text-muted); font-size: 12px; }

.sb-product-price { font-size: 17px; font-weight: 650; }

/* Не серым и не мелким: «цена по запросу» — это предложение действия, а не отсутствие данных. */
.sb-product-price-none { font-size: 14px; font-weight: 550; color: var(--sb-ink-accent); }

.sb-product-stock { font-size: 12.5px; }

/* Из темы, а не константами: тёмная палитра меняет фон, и вбитые тёмно-зелёный с тёмно-янтарным на нём
   переставали читаться — при том, что --sb-success и --sb-warning для этого и заведены. */
.sb-in-stock { color: var(--sb-success); }
.sb-out-stock { color: var(--sb-warning); }

.sb-product-add { margin-top: auto; }

/* Подтверждение «Добавлено ✓» — цветом успеха, а не основного действия: кнопка на пару секунд
   перестаёт быть кнопкой и становится ответом. Класс вешает скрипт и там же снимает. */
.sb-btn.sb-added {
    background: var(--sb-success);
    border-color: var(--sb-success);
    color: #fff;
}

.sb-products-empty,
.sb-categories-empty,
.sb-orders-empty {
    padding: 32px 0;
    color: var(--sb-text-muted);
    text-align: center;
}

.sb-products-all { margin-top: 24px; text-align: center; }

/* Каркас загрузки: место нужного размера, чтобы вёрстка не прыгала, когда придут данные. */
.sb-product-skeleton { pointer-events: none; }
.sb-product-skeleton .sb-product-media { background: var(--sb-surface-alt); }

.sb-skeleton-line {
    height: 12px;
    margin-top: 8px;
    border-radius: 6px;
    background: var(--sb-surface-alt);
}

.sb-skeleton-short { width: 55%; }

/* ---------------------------------------------------------------------------------------------------
   Карточка товара
   --------------------------------------------------------------------------------------------------- */
.sb-product-detail {
    display: grid;
    grid-template-columns: minmax(0, 5fr) minmax(0, 6fr);
    gap: 40px;
    align-items: start;
}

.sb-product-gallery-main {
    position: relative;
    aspect-ratio: 1 / 1;
    display: grid;
    place-items: center;
    overflow: hidden;
    background: var(--sb-surface-alt);
    border-radius: var(--sb-radius);

    /* Свайп по картинке не должен уводить страницу вбок; вертикальная прокрутка при этом остаётся,
       иначе с телефона нельзя пролистать карточку, начав жест с фотографии. */
    touch-action: pan-y;
}

.sb-product-gallery-main img { width: 100%; height: 100%; object-fit: contain; }

.sb-gallery-nav {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    display: grid;
    place-items: center;
    width: 40px;
    height: 40px;
    border: none;
    border-radius: 50%;
    background: rgba(255, 255, 255, 0.86);
    box-shadow: 0 1px 6px rgba(15, 23, 42, 0.18);
    color: #0f172a;
    font-size: 22px;
    line-height: 1;
    cursor: pointer;
}

.sb-gallery-nav:hover { background: #fff; }
.sb-gallery-prev { left: 10px; }
.sb-gallery-next { right: 10px; }

.sb-gallery-counter {
    position: absolute;
    right: 10px;
    bottom: 10px;
    padding: 3px 9px;
    border-radius: 20px;
    background: rgba(15, 23, 42, 0.62);
    color: #fff;
    font-size: 12px;
}

.sb-product-gallery-thumbs { display: flex; gap: 8px; margin-top: 10px; flex-wrap: wrap; }

/* Миниатюра — кнопка, а не картинка с обработчиком: карточку смотрят с клавиатуры чаще, чем принято
   думать, и «нажимаемая картинка» для клавиатуры не существует. */
.sb-product-thumb {
    width: 66px;
    height: 66px;
    padding: 0;
    overflow: hidden;
    border: 2px solid transparent;
    border-radius: var(--sb-radius-sm);
    background: var(--sb-surface-alt);
    cursor: pointer;
}

.sb-product-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.sb-product-thumb:hover { border-color: var(--sb-border); }
.sb-thumb-current { border-color: var(--sb-ink-accent); }

.sb-product-thumb:focus-visible,
.sb-gallery-nav:focus-visible,
.sb-variant-value:focus-visible {
    outline: 2px solid var(--sb-ink-accent);
    outline-offset: 2px;
}

/* ---- Выбор варианта ---- */
.sb-product-variants { display: grid; gap: 14px; margin: 18px 0; }

.sb-variant-label {
    margin-bottom: 6px;
    font-size: 13px;
    font-weight: 600;
    color: var(--sb-text-muted);
}

.sb-variant-values { display: flex; flex-wrap: wrap; gap: 8px; }

.sb-variant-value {
    min-width: 46px;
    padding: 8px 14px;
    border: 1px solid var(--sb-border);
    border-radius: var(--sb-radius-sm);
    /* Фон из темы: вбитый белый в тёмной палитре оставлял белую кнопку с почти белой надписью — текст-то
       наследует --sb-text, а он в тёмной теме светлеет. */
    background: var(--sb-surface);
    font-size: 14px;
    line-height: 1.2;
    cursor: pointer;
    transition: border-color 0.12s, background 0.12s;
}

.sb-variant-value:hover:not(:disabled) { border-color: var(--sb-ink-accent); }

.sb-variant-picked {
    border-color: var(--sb-ink-accent);
    background: var(--sb-ink-accent);
    color: #fff;
}

/* Перечёркнут, а не убран: спрятать размер — значит скрыть, что он вообще бывает, и человек уйдёт
   искать его туда, где о нём скажут. */
.sb-variant-gone {
    color: var(--sb-text-muted);
    background: var(--sb-surface-alt);
    text-decoration: line-through;
    cursor: not-allowed;
}

.sb-product-description {
    margin-top: 24px;
    line-height: 1.65;
    color: var(--sb-text);
}

.sb-product-description :is(h2, h3) { margin: 18px 0 8px; font-size: 17px; }
.sb-product-description p { margin: 0 0 10px; }
.sb-product-description :is(ul, ol) { margin: 0 0 10px; padding-left: 22px; }
.sb-product-description img { max-width: 100%; height: auto; border-radius: var(--sb-radius-sm); }

.sb-product-title { margin: 0 0 6px; font-size: 28px; line-height: 1.2; }

.sb-product-offer {
    display: flex;
    align-items: baseline;
    gap: 16px;
    margin: 18px 0;
    flex-wrap: wrap;
}

.sb-product-price-value { font-size: 30px; font-weight: 680; }

.sb-product-cart { display: flex; gap: 12px; align-items: center; margin-bottom: 24px; }

.sb-product-summary { color: var(--sb-text-muted); line-height: 1.6; }

/*
    minmax(0, max-content), а не max-content: голый max-content не даёт колонке ужаться, и характеристика с
    длинным названием («Максимальная нагрузка на ось») растягивает таблицу шире экрана телефона.
*/
.sb-product-specs {
    display: grid;
    grid-template-columns: minmax(0, max-content) minmax(0, 1fr);
    gap: 6px 20px;
    margin-top: 24px;
    padding-top: 20px;
    border-top: 1px solid var(--sb-border);
    font-size: 14px;
}

.sb-product-specs dt { color: var(--sb-text-muted); }
.sb-product-specs dd { margin: 0; }

/* ---------------------------------------------------------------------------------------------------
   Количество
   --------------------------------------------------------------------------------------------------- */
.sb-qty {
    display: inline-flex;
    align-items: stretch;
    border: 1px solid var(--sb-border);
    border-radius: var(--sb-radius-sm);
    /* Не overflow:hidden: он срезал рамку фокуса у кнопок «плюс» и «минус» внутри, и количество менялось с
       клавиатуры вслепую. `clip` со свободным полем скругляет углы так же, но рамке даёт выйти наружу. */
    overflow: clip;
    overflow-clip-margin: 6px;
}

.sb-qty-step {
    width: 36px;
    border: 0;
    background: var(--sb-surface-alt);
    font-size: 16px;
    cursor: pointer;
}

.sb-qty-step:hover { background: var(--sb-border); }

.sb-qty-input {
    width: 56px;
    border: 0;
    text-align: center;
    font: inherit;
    /* Стрелки браузера рядом с собственными кнопками — два способа сделать одно, и оба мелкие. */
    -moz-appearance: textfield;
}

.sb-qty-input::-webkit-outer-spin-button,
.sb-qty-input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }

/* ---------------------------------------------------------------------------------------------------
   Корзина
   --------------------------------------------------------------------------------------------------- */
.sb-cart { display: grid; grid-template-columns: minmax(0, 1fr) 300px; gap: 32px; align-items: start; }

.sb-cart-line {
    display: grid;
    grid-template-columns: 72px minmax(0, 1fr) auto auto auto;
    gap: 14px;
    align-items: center;
    padding: 14px 0;
    border-bottom: 1px solid var(--sb-border);
}

.sb-cart-line-media {
    width: 72px;
    height: 72px;
    display: grid;
    place-items: center;
    overflow: hidden;
    background: var(--sb-surface-alt);
    border-radius: var(--sb-radius-sm);
}

.sb-cart-line-media img { width: 100%; height: 100%; object-fit: contain; }

.sb-cart-line-name { font-weight: 550; }

/* Имя варианта — «21 см», «Синий, 42» — отдельной строкой под названием: без него две строки одного
   товара разных размеров неотличимы, и покупатель убирает не ту (Д-7 из приёмки). */
.sb-cart-line-variant { color: var(--sb-text-muted); font-size: 12.5px; }
.sb-cart-line-code { color: var(--sb-text-muted); font-size: 12.5px; }
.sb-cart-line-total { font-weight: 620; white-space: nowrap; }

.sb-cart-line-remove {
    width: 30px;
    height: 30px;
    border: 0;
    border-radius: 50%;
    background: transparent;
    color: var(--sb-text-muted);
    font-size: 20px;
    line-height: 1;
    cursor: pointer;
}

.sb-cart-line-remove:hover { background: var(--sb-surface-alt); color: var(--sb-danger); }

.sb-cart-summary {
    position: sticky;
    top: 24px;
    padding: 20px;
    background: var(--sb-surface-alt);
    border-radius: var(--sb-radius);
}

.sb-cart-row { display: flex; justify-content: space-between; gap: 16px; padding: 6px 0; }

.sb-cart-total {
    margin-top: 8px;
    padding-top: 12px;
    border-top: 1px solid var(--sb-border);
    font-size: 18px;
}

.sb-cart-checkout { display: block; width: 100%; margin-top: 16px; text-align: center; }
.sb-cart-clear { display: block; width: 100%; margin-top: 8px; }
.sb-cart-note { margin: 10px 0 0; color: var(--sb-text-muted); font-size: 12.5px; }
.sb-cart-loading { padding: 32px 0; color: var(--sb-text-muted); }
.sb-cart-empty { grid-column: 1 / -1; padding: 48px 0; text-align: center; }

/* ---------------------------------------------------------------------------------------------------
   Разделы и фильтры
   --------------------------------------------------------------------------------------------------- */
.sb-categories-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 4px; }
.sb-categories-list a { color: inherit; text-decoration: none; }
.sb-categories-list a:hover { color: var(--sb-ink-accent); }

/*
    Разделы в строку переносятся, а не уезжают за экран.

    `grid-auto-flow: column` кладёт все разделы в один ряд без переноса: восемь разделов на телефоне — это ряд
    шириной в полторы тысячи пикселей, который тянет за собой всю страницу. Флекс с переносом даёт ту же строку
    там, где она помещается, и две-три строки там, где нет.
*/
.sb-categories-inline .sb-categories-list {
    display: flex;
    flex-wrap: wrap;
    justify-content: start;
    gap: 8px 18px;
}

.sb-categories-tiles .sb-categories-list {
    grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
    gap: 12px;
}

.sb-categories-tiles .sb-categories-list li {
    padding: 16px;
    background: var(--sb-surface-alt);
    border-radius: var(--sb-radius-sm);
}

/* Сетка живёт на внутренней обёртке: корень блока — полоса с контейнером, как у остальных секций витрины. */
.sb-filters-inner { display: grid; gap: 16px; }
.sb-filters-head { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; }
.sb-filters-title { margin: 0; font-size: 16px; }

.sb-filters-reset {
    border: 0;
    background: transparent;
    color: var(--sb-ink-accent);
    font: inherit;
    cursor: pointer;
}

.sb-filters-search input {
    width: 100%;
    padding: 9px 12px;
    border: 1px solid var(--sb-border);
    border-radius: var(--sb-radius-sm);
    font: inherit;
}

.sb-filter-group { margin: 0; padding: 0; border: 0; }
.sb-filter-group legend { padding: 0; font-size: 13px; font-weight: 600; }
.sb-filter-value { display: flex; align-items: center; gap: 8px; padding: 3px 0; font-size: 14px; }
.sb-filter-count { margin-left: auto; color: var(--sb-text-muted); font-size: 12px; }
.sb-filters-empty { color: var(--sb-text-muted); font-size: 13px; }

/* ---------------------------------------------------------------------------------------------------
   Оформление и заказы
   --------------------------------------------------------------------------------------------------- */
.sb-checkout { display: grid; grid-template-columns: minmax(0, 1fr) 320px; gap: 32px; align-items: start; }
.sb-checkout-fields { display: grid; gap: 14px; }

.sb-field { display: grid; gap: 5px; }
.sb-field > span { font-size: 13px; font-weight: 550; }
/* Из темы, а не константой: в тёмной палитре вбитый красный на тёмном фоне уходил ниже нормы контраста. */
.sb-field em { color: var(--sb-danger); font-style: normal; margin-left: 2px; }

.sb-field input,
.sb-field select,
.sb-field textarea {
    padding: 9px 12px;
    border: 1px solid var(--sb-border);
    border-radius: var(--sb-radius-sm);
    font: inherit;
}

.sb-checkout-summary {
    position: sticky;
    top: 24px;
    padding: 20px;
    background: var(--sb-surface-alt);
    border-radius: var(--sb-radius);
}

.sb-checkout-summary h3 { margin: 0 0 12px; font-size: 15px; }
.sb-checkout-lines { list-style: none; margin: 0 0 12px; padding: 0; display: grid; gap: 6px; font-size: 14px; }
.sb-checkout-lines li { display: flex; justify-content: space-between; gap: 12px; }
.sb-checkout-submit { width: 100%; margin-top: 14px; }
.sb-checkout-note { margin: 10px 0 0; color: var(--sb-text-muted); font-size: 12.5px; }

.sb-checkout-changed {
    margin-top: 12px;
    padding: 10px 12px;
    background: #fffbeb;
    border: 1px solid #fde68a;
    border-radius: var(--sb-radius-sm);
    color: #92400e;
    font-size: 13px;
    line-height: 1.5;
}

.sb-checkout-done { padding: 48px 0; text-align: center; }

/* Номер — самое крупное на экране успеха: его записывают на бумажку и называют по телефону. */
.sb-checkout-number {
    margin: 10px 0 6px;
    font-size: 38px;
    font-weight: 700;
    letter-spacing: .01em;
}

/* Оформлять нечего — состояние, а не поломка: тон приглушённый, как у выключенного входа. */
.sb-checkout-empty {
    margin-top: 12px;
    color: var(--sb-text-muted);
    font-size: 13.5px;
    line-height: 1.5;
}

/* За экран, а не display:none — скрытое поле бот пропускает первым делом, а убранное с глаз заполняет. */
.sb-honeypot {
    position: absolute;
    left: -9999px;
    width: 1px;
    height: 1px;
    overflow: hidden;
}

/*
    Прокручивается обёртка, а не таблица.

    Таблица заказов и таблица сотрудников собираются скриптом, обернуть их некуда — но контейнер, в котором они
    лежат, есть в разметке. Без прокрутки таблица из пяти столбцов на телефоне не помещается и тянет вбок всю
    страницу: горизонтально едет не таблица, а сайт. `-webkit-overflow-scrolling` — ради инерции на iOS, как и
    у блока «Таблица».
*/
.sb-orders,
.sb-account-people {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
}

.sb-orders-table { width: 100%; min-width: 22rem; border-collapse: collapse; font-size: 14px; }

.sb-orders-table th {
    padding: 8px 10px;
    border-bottom: 1px solid var(--sb-border);
    color: var(--sb-text-muted);
    font-weight: 600;
    text-align: left;
}

.sb-orders-table td { padding: 10px; border-bottom: 1px solid var(--sb-surface-alt); }
.sb-orders-loading { padding: 24px 0; color: var(--sb-text-muted); }
.sb-orders-anonymous { padding: 32px 0; text-align: center; }

/* Выключенный вход — состояние магазина, а не поломка: тон приглушённый, без кнопки и без цвета тревоги. */
.sb-orders-off { padding: 32px 0; text-align: center; color: var(--sb-text-muted); }

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

/* ---------------------------------------------------------------------------------------------------
   Телефон
   --------------------------------------------------------------------------------------------------- */
@container sb-page (max-width: 900px) {
    .sb-product-detail,
    .sb-cart,
    .sb-checkout { grid-template-columns: minmax(0, 1fr); gap: 24px; }

    .sb-cart-summary,
    .sb-checkout-summary { position: static; }

    .sb-products-grid { grid-template-columns: repeat(min(var(--sb-products-cols, 4), 2), minmax(0, 1fr)); }
}

@container sb-page (max-width: 560px) {
    .sb-products-grid { grid-template-columns: minmax(0, 1fr); }

    /* Стрелки убираются: с телефона листают пальцем, а кнопка шириной с ноготь поверх фотографии только
       мешает в неё смотреть. Свайп и миниатюры остаются. */
    .sb-gallery-nav { display: none; }
    .sb-product-thumb { width: 54px; height: 54px; }

    /* Значения варианта — во всю ширину и покрупнее: сюда целятся пальцем. */
    .sb-variant-value { flex: 1 1 auto; padding: 11px 14px; }

    .sb-cart-line {
        grid-template-columns: 56px minmax(0, 1fr) auto;
        grid-template-areas:
            "media body remove"
            "media qty   total";
    }

    .sb-cart-line-media { grid-area: media; width: 56px; height: 56px; }
    .sb-cart-line-body { grid-area: body; }
    .sb-cart-line .sb-qty { grid-area: qty; }
    .sb-cart-line-total { grid-area: total; }
    .sb-cart-line-remove { grid-area: remove; }
}

/* ---------------------------------------------------------------------------------------------------
   Личный кабинет
   --------------------------------------------------------------------------------------------------- */
.sb-account { display: grid; gap: 22px; }

.sb-account-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    flex-wrap: wrap;
    padding-bottom: 16px;
    border-bottom: 1px solid var(--sb-border);
}

.sb-account-name { font-size: 18px; font-weight: 600; }
.sb-account-email { font-size: 14px; color: var(--sb-text-muted); }

.sb-account-label {
    margin-bottom: 8px;
    font-size: 13px;
    font-weight: 600;
    color: var(--sb-text-muted);
}

.sb-account-cabinet-list { display: flex; flex-wrap: wrap; gap: 10px; }

/* Выбор организации — кнопки, а не выпадающий список: их две-три, и от какой из них покупаешь, должно
   быть видно, не открывая ничего. */
.sb-account-cabinet {
    display: grid;
    gap: 2px;
    padding: 10px 16px;
    border: 1px solid var(--sb-border);
    border-radius: var(--sb-radius-sm);
    /* Фон из темы: вбитый белый в тёмной палитре давал белое на белом — надпись наследует --sb-text. */
    background: var(--sb-surface);
    text-align: left;
    cursor: pointer;
}

.sb-account-cabinet:hover { border-color: var(--sb-ink-accent); }

.sb-account-cabinet-current {
    border-color: var(--sb-ink-accent);
    box-shadow: 0 0 0 1px var(--sb-ink-accent) inset;
}

.sb-account-cabinet-name { font-weight: 550; }
.sb-account-cabinet-legal { font-size: 12.5px; color: var(--sb-text-muted); }

.sb-account-members { width: 100%; min-width: 20rem; border-collapse: collapse; font-size: 14px; }

.sb-account-members th {
    padding: 6px 10px;
    text-align: left;
    font-size: 12px;
    font-weight: 600;
    color: var(--sb-text-muted);
    border-bottom: 1px solid var(--sb-border);
}

.sb-account-members td { padding: 8px 10px; border-bottom: 1px solid var(--sb-border); }

.sb-account-anonymous { display: grid; gap: 12px; justify-items: start; }
.sb-account-off, .sb-account-loading { color: var(--sb-text-muted); }

/* Мои устройства: строка на сессию — что это, когда было, чем завершить. Текущая помечена цветом рамки,
   а не только словом: список из двух одинаковых строк «Chrome · Windows» различим иначе только датами. */
.sb-account-device-list {
    display: grid;
    gap: 10px;
    margin: 0;
    padding: 0;
    list-style: none;
}

.sb-account-device {
    display: grid;
    grid-template-columns: 1fr auto;
    align-items: center;
    gap: 2px 14px;
    padding: 10px 14px;
    border: 1px solid var(--sb-border);
    border-radius: 10px;
}

.sb-account-device-current { border-color: var(--sb-ink-accent); }

.sb-account-device-what { font-weight: 550; }
.sb-account-device-when { grid-column: 1; font-size: 12.5px; color: var(--sb-text-muted); }

.sb-account-device-revoke { grid-column: 2; grid-row: 1 / span 2; }
.sb-account-device-revoke-all { justify-self: start; }

@container sb-page (max-width: 560px) {
    .sb-account-cabinet { width: 100%; }

    .sb-account-device { grid-template-columns: 1fr; }
    .sb-account-device-revoke { grid-column: 1; grid-row: auto; justify-self: start; }
}

/* Поля на карточке, кроме названия: площадь у помещения, срок у услуги, пробег у автомобиля.
   Каталог товаров — частный случай, и карточка не обязана быть товарной. */
.sb-product-fields {
    display: grid;
    gap: 3px;
    margin: 6px 0 0;
    font-size: 12.5px;
}

.sb-product-field {
    display: flex;
    gap: 6px;
    align-items: baseline;
}

/* Имя поля приходит из сопоставления источника и бывает длинным («Гарантия производителя»). Без сжатия оно
   выталкивало значение за карточку; с ним — переносится само и уступает место цифре, ради которой поле и есть. */
.sb-product-field dt { color: var(--sb-text-muted); min-width: 0; }
.sb-product-field dd { margin: 0; overflow-wrap: anywhere; }

/* blocks.booking.css */
/*
    Онлайн-запись.

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

.sb-booking-block .sb-booking {
    margin-top: var(--sb-space-lg);
}

/*
    Узкая колонка закона для всего содержимого секции — календаря, итога и запасной заявки: три ряда кнопок и
    форма на всю ширину контейнера читались бы как таблица, а не как запись. Выравнивание следует шапке теми
    же классами, что у подзаголовка: под центрированной шапкой колонка стоит по центру полосы, иначе секция
    на 1280 стояла содержимым у левого борта, а правая половина пустовала (повторная приёмка, /zapis).
*/
.sb-booking,
.sb-booking-done,
.sb-booking-callback {
    max-width: var(--sb-width-narrow);
}

.sb-booking.sb-align-center,
.sb-booking-done.sb-align-center,
.sb-booking-callback.sb-align-center {
    margin-inline: auto;
}

.sb-booking.sb-align-right,
.sb-booking-done.sb-align-right,
.sb-booking-callback.sb-align-right {
    margin-inline-start: auto;
}

/*
    Подзаголовок секции — внутри выбора окон, а не в шапке: «выберите услугу, день и время» — обещание
    календаря и скрывается вместе с ним, когда услуг ещё нет или скрипта не случилось. Типографика — sb-lead,
    как у любого подзаголовка; здесь только его место в потоке.
*/
.sb-booking-lead {
    margin: 0 0 var(--sb-space-lg);
}

.sb-booking-lead.sb-align-center { text-align: center; }
.sb-booking-lead.sb-align-right { text-align: right; }

.sb-booking-steps {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: var(--sb-space-lg);
}

.sb-booking-step-title {
    margin: 0 0 var(--sb-space-sm);
    font-size: var(--sb-font-size-h5);
    font-family: var(--sb-font-heading);
    font-weight: var(--sb-heading-weight);
    color: var(--sb-text);
}

/* ---- Услуги ------------------------------------------------------------------------------------ */

.sb-booking-services {
    display: flex;
    flex-wrap: wrap;
    gap: var(--sb-space-sm);
}

.sb-booking-service {
    display: flex;
    flex-direction: column;
    gap: 2px;
    align-items: flex-start;
    padding: var(--sb-space-sm) var(--sb-space-md);
    min-width: 180px;
    border: 1px solid var(--sb-border);
    border-radius: var(--sb-radius);
    background: var(--sb-surface);
    color: var(--sb-text);
    font: inherit;
    text-align: left;
    cursor: pointer;
}

.sb-booking-service-name {
    font-weight: 600;
}

.sb-booking-service-meta {
    font-size: 0.86em;
    color: var(--sb-text-muted);
}

/* ---- Дни и окна -------------------------------------------------------------------------------- */

.sb-booking-days,
.sb-booking-slots {
    display: flex;
    flex-wrap: wrap;
    gap: var(--sb-space-xs);
}

.sb-booking-days {
    flex-wrap: nowrap;
    overflow-x: auto;
    padding-bottom: var(--sb-space-xs);
}

.sb-booking-day,
.sb-booking-slot {
    flex: 0 0 auto;
    padding: var(--sb-space-xs) var(--sb-space-sm);
    border: 1px solid var(--sb-border);
    border-radius: var(--sb-radius-sm);
    background: var(--sb-surface);
    color: var(--sb-text);
    font: inherit;
    white-space: nowrap;
    cursor: pointer;
}

.sb-booking-day-label::first-letter {
    text-transform: uppercase;
}

/*
    Занятое окно приглушено и не нажимается. Не «скрыто»: пропавшее время читается как «мастер не работает», а
    приглушённое — как «здесь уже кто-то есть», и это разные ответы на разные вопросы.
*/
.sb-booking-slot.sb-booking-taken {
    opacity: 0.45;
    cursor: not-allowed;
    text-decoration: line-through;
}

.sb-booking-service.sb-picked,
.sb-booking-day.sb-picked,
.sb-booking-slot.sb-picked {
    border-color: var(--sb-ink-accent);
    box-shadow: inset 0 0 0 1px var(--sb-ink-accent);
}

.sb-booking-empty {
    margin: 0;
    color: var(--sb-text-muted);
}

/* ---- Контакты ---------------------------------------------------------------------------------- */

/*
    «Контакт», а не «форма»: имя .sb-booking-form занято корнем секции — базовый класс выводится из типа
    блока, а тип здесь booking.form. Одноимённое правило на внутренней форме делало гридом всю полосу, и
    запись целиком вставала в её левую колонку.
*/
.sb-booking-contact {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--sb-space-sm) var(--sb-space-md);
    align-items: start;
}

.sb-booking-contact textarea {
    resize: vertical;
}

.sb-booking-submit {
    grid-column: 1 / -1;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--sb-space-sm);
}

/* Что именно выбрано — рядом с кнопкой, а не наверху: подтверждать читают там, где нажимают. */
.sb-booking-chosen {
    color: var(--sb-text-muted);
    font-size: 0.92em;
}

.sb-booking-note,
.sb-booking-error {
    grid-column: 1 / -1;
    margin: 0;
    font-size: 0.9em;
    color: var(--sb-text-muted);
}

.sb-booking-error {
    color: var(--sb-danger);
}

/* ---- Итог и запасная заявка -------------------------------------------------------------------- */

.sb-booking-done {
    margin-top: var(--sb-space-lg);
    padding: var(--sb-space-lg);
    border: 1px solid var(--sb-border);
    border-radius: var(--sb-radius);
    background: var(--sb-surface-alt);
}

.sb-booking-done h3 {
    margin: 0 0 var(--sb-space-xs);
    font-family: var(--sb-font-heading);
}

.sb-booking-done-what {
    margin: 0 0 var(--sb-space-xs);
    font-size: 1.05em;
}

.sb-booking-callback {
    margin-top: var(--sb-space-lg);
    /* Ширина — узкой колонкой закона вместе с календарём и итогом, см. правило в начале файла. */
}

.sb-booking-callback-title {
    margin: 0 0 var(--sb-space-xs);
    font-family: var(--sb-font-heading);
    font-weight: var(--sb-heading-weight);
}

.sb-booking-callback-text {
    margin: 0 0 var(--sb-space-md);
    color: var(--sb-text-muted);
}

/* Каркас загрузки нужного размера, а не «Загружаем…»: пустое место, которое потом заполняется, дёргает вёрстку. */
.sb-booking-skeleton {
    display: block;
    width: 180px;
    height: 58px;
    border-radius: var(--sb-radius);
    background: var(--sb-surface-alt);
}

@container sb-page (max-width: 700px) {
    .sb-booking-contact {
        grid-template-columns: minmax(0, 1fr);
    }
}

/* blocks.nav.css */
/*
    Навигация: шапка, подвал, списки ссылок.

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

/* ---------------------------------------------------------------------------------------------------
   Шапка
   --------------------------------------------------------------------------------------------------- */
.sb-navbar {
    position: relative;
    z-index: 30;
    background: var(--sb-surface);
    border-bottom: 1px solid var(--sb-border);
}

/* Прилипание — на самом элементе, а не на обёртке: обёртка может быть в потоке с overflow, и тогда
   position:sticky молча перестаёт работать, что выглядит как «иногда не прилипает». */
.sb-navbar[data-sb-nav="sticky"] {
    position: sticky;
    top: 0;
}

.sb-navbar.sb-scrolled {
    box-shadow: 0 1px 3px rgba(15, 23, 42, .08), 0 6px 20px rgba(15, 23, 42, .06);
}

.sb-navbar-inner {
    display: flex;
    align-items: center;
    gap: 24px;
    min-height: 64px;
}

/*
    Название сайта сжимается и переносится по словам.

    `flex-shrink: 0` держал название в одну строку любой ценой. Пока название короткое, это незаметно; «Студия
    ландшафтного дизайна „Вересковый сад“» при этом забирало себе всю шапку, а меню рядом сжималось до
    столбиков по одной букве — и то же название на телефоне выталкивало страницу за край экрана.

    Теперь название уступает первым и переносится — по словам, а не посреди слова: разрыв посреди слова
    страница разрешает всему подряд (см. blocks.core.css), и для коротких подписей это разрешение отзывается
    правилом ниже.
*/
.sb-navbar-brand {
    display: inline-flex;
    align-items: center;
    gap: 10px;
    color: inherit;
    text-decoration: none;
}

.sb-navbar-logo { max-height: 36px; width: auto; display: block; }
.sb-navbar-name { font-size: 17px; font-weight: 650; }

/*
    Пункт меню, ссылка подвала, название сайта — подписи, а не текст. «Кон-так-ты» в три строки по букве
    читается хуже, чем то же слово, перенесённое целиком на вторую строку списка.
*/
.sb-navbar-name,
.sb-navbar-link,
.sb-navbar-sublink,
.sb-navbar-cta,
.sb-menu-link,
.sb-footer-link,
.sb-footer-legal {
    overflow-wrap: normal;
}

.sb-navbar-menu { flex: 1; display: flex; }

/* Положение меню задаётся тем, куда уезжает свободное место, а не выравниванием текста: так пункты не
   расползаются между собой, когда их три, и не слипаются, когда их восемь. */
.sb-navbar-align-left .sb-navbar-menu { justify-content: flex-start; }
.sb-navbar-align-center .sb-navbar-menu { justify-content: center; }
.sb-navbar-align-right .sb-navbar-menu { justify-content: flex-end; }

/*
    Перенос на вторую строку, а не сжатие пунктов. Меню из семи разделов рядом с длинным названием сайта не
    помещается в строку ни при каком раскладе — и выбор тут между «часть пунктов ниже» и «все пункты, но по
    букве в столбик».
*/
.sb-navbar-links {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 4px;
    margin: 0;
    padding: 0;
    list-style: none;
}

.sb-navbar-links > li { position: relative; }

/*
    Высота не меньше 24 — нижняя граница WCAG 2.2 для цели нажатия.

    Отступ сверху и снизу давал строку в двадцать пикселей, и это цель, в которую промахиваются: попадание
    пальцем на телефоне и попадание мышью при треморе считаются одинаково. В шапке магазина промах стоит
    дорого — по ней уходят из каталога в корзину.
*/
.sb-navbar-link {
    display: inline-flex;
    align-items: center;
    min-height: 32px;
    padding: 6px 12px;
    border-radius: var(--sb-radius-sm);
    color: inherit;
    text-decoration: none;
    font-size: 15px;
    line-height: 1.3;
}

.sb-navbar-link:hover { background: var(--sb-surface-alt); }

/* Текущий раздел — подчёркиванием и весом, а не только цветом: цвет один и тот же для «здесь вы сейчас»
   и для «сюда можно нажать» плохо различают и в норме, а при дальтонизме не различают вовсе. */
.sb-navbar-link[aria-current="page"] {
    font-weight: 650;
    box-shadow: inset 0 -2px 0 var(--sb-ink-accent);
    border-radius: var(--sb-radius-sm) var(--sb-radius-sm) 0 0;
}

.sb-navbar-cta { margin-left: 8px; }

/*
    Счётчик позиций у ссылки «Корзина». Пока корзина пуста, его нет вовсе: ноль в шапке — это ответ на
    вопрос, которого посетитель не задавал. Заполняет его скрипт из ответа корзины (см. initCartBadge).
*/
.sb-navbar-cart-count {
    display: inline-grid;
    place-items: center;
    min-width: 20px;
    height: 20px;
    margin-left: 6px;
    padding: 0 6px;
    border-radius: 10px;
    background: var(--sb-ink-accent);
    color: var(--sb-primary-contrast);
    font-size: 12px;
    font-weight: 650;
    line-height: 1;
}

/* На залитой кнопке-CTA значок инвертируется: акцент на акценте неразличим. Краска — токеном краски,
   как того требует закон листов (BlockStylesheetTests): в шапке он равен основному. */
.sb-navbar-cta .sb-navbar-cart-count {
    background: var(--sb-primary-contrast);
    color: var(--sb-ink-accent);
}

.sb-navbar-empty {
    padding: 8px 12px;
    color: var(--sb-text-muted);
    font-size: 13px;
}

/* ---- Подменю ----
   Раскрывается наведением и фокусом. Только фокусом было бы недоступно мышью, только наведением —
   недоступно с клавиатуры и на телефоне. */
.sb-navbar-submenu {
    position: absolute;
    top: 100%;
    left: 0;
    z-index: 10;
    display: none;
    min-width: 200px;
    /* Раскрытое подменю у правого пункта меню уезжало за край страницы: `left: 0` привязывает его к пункту,
       а ширину ничто не ограничивало. Ограничение по ширине окна просмотра — единственная мера, которая
       работает и когда пункт стоит у самого края. */
    max-width: min(280px, calc(100cqw - 24px));
    margin: 0;
    padding: 6px;
    list-style: none;
    background: var(--sb-surface);
    border: 1px solid var(--sb-border);
    border-radius: var(--sb-radius-sm);
    box-shadow: 0 8px 24px rgba(15, 23, 42, .12);
}

.sb-has-children:hover > .sb-navbar-submenu,
.sb-has-children:focus-within > .sb-navbar-submenu { display: block; }

.sb-navbar-sublink {
    display: block;
    padding: 8px 10px;
    border-radius: 8px;
    color: inherit;
    text-decoration: none;
    font-size: 14.5px;
}

.sb-navbar-sublink:hover { background: var(--sb-surface-alt); }

/* ---- Гамбургер ---- */
.sb-navbar-toggle {
    display: none;
    order: 3;
    flex-direction: column;
    justify-content: center;
    gap: 5px;
    width: 40px;
    height: 40px;
    padding: 0 9px;
    border: 1px solid var(--sb-border);
    border-radius: var(--sb-radius-sm);
    background: transparent;
    cursor: pointer;
}

.sb-navbar-toggle span {
    display: block;
    height: 2px;
    border-radius: 2px;
    background: currentColor;
}

/* ---------------------------------------------------------------------------------------------------
   Подвал
   --------------------------------------------------------------------------------------------------- */
.sb-footer {
    background: var(--sb-surface-alt);
    border-top: 1px solid var(--sb-border);
    padding-top: 48px;
}

.sb-footer-inner {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 2fr);
    gap: 40px;
    padding-bottom: 32px;
}

.sb-footer-logo { max-height: 34px; width: auto; display: block; }
.sb-footer-name { font-size: 16px; font-weight: 650; }

.sb-footer-tagline {
    margin: 10px 0 0;
    max-width: 40ch;
    color: var(--sb-text-muted);
    font-size: 14px;
    line-height: 1.55;
}

.sb-footer-columns {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
    gap: 28px;
}

.sb-footer-column-title {
    margin-bottom: 10px;
    font-size: 13px;
    font-weight: 650;
    text-transform: uppercase;
    letter-spacing: .04em;
    color: var(--sb-text-muted);
}

.sb-footer-column ul { margin: 0; padding: 0; list-style: none; display: grid; gap: 2px; }

.sb-footer-link {
    color: inherit;
    text-decoration: none;
    font-size: 14.5px;
}

.sb-footer-link:hover { text-decoration: underline; }

.sb-footer-bottom {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    flex-wrap: wrap;
    padding: 18px 0;
    border-top: 1px solid var(--sb-border);
    color: var(--sb-text-muted);
    font-size: 13.5px;
}

/* Не меньше 24 по высоте — как и все остальные ссылки сайта. Правовая ссылка мелкая, но нажимают её
   не реже прочих, и промах по ней стоит ровно столько же. */
.sb-footer-legal {
    display: inline-flex;
    align-items: center;
    min-height: 26px;
    color: inherit;
}

/* ---------------------------------------------------------------------------------------------------
   Список ссылок и соцсети
   --------------------------------------------------------------------------------------------------- */
.sb-menu-links-title {
    margin-bottom: 10px;
    font-size: 13px;
    font-weight: 650;
    text-transform: uppercase;
    letter-spacing: .04em;
    color: var(--sb-text-muted);
}

.sb-menu-links-list { margin: 0; padding: 0; list-style: none; display: grid; gap: 8px; }
.sb-menu-links-horizontal .sb-menu-links-list { display: flex; flex-wrap: wrap; gap: 6px 18px; }

.sb-menu-link { color: inherit; text-decoration: none; }
.sb-menu-link:hover { text-decoration: underline; }
.sb-menu-link[aria-current="page"] { font-weight: 650; }

.sb-menu-links-empty { color: var(--sb-text-muted); font-size: 13px; }

.sb-social { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.sb-social-title { font-size: 13.5px; color: var(--sb-text-muted); }
.sb-social-list { display: flex; gap: 8px; margin: 0; padding: 0; list-style: none; }

.sb-social-link {
    display: inline-grid;
    place-items: center;
    width: 38px;
    height: 38px;
    border: 1px solid var(--sb-border);
    border-radius: 50%;
    color: inherit;
    text-decoration: none;
}

.sb-social-link:hover {
    border-color: var(--sb-ink-accent);
    color: var(--sb-ink-accent);
}

/* ---------------------------------------------------------------------------------------------------
   Телефон
   --------------------------------------------------------------------------------------------------- */
@container sb-page (max-width: 860px) {
    .sb-navbar-inner { flex-wrap: wrap; }

    .sb-navbar-toggle { display: flex; margin-left: auto; }

    /*
        Свёрнутое меню — display:none, а не нулевая высота.

        Высотой сворачивают ради анимации, но тогда ссылки остаются в порядке обхода с клавиатуры и их
        читает экранный диктор: посетитель уезжает фокусом в невидимый список и не понимает, где он.
    */
    .sb-navbar-menu {
        display: none;
        flex-basis: 100%;
        order: 4;
        padding-bottom: 12px;
    }

    .sb-navbar[data-sb-nav-open="1"] .sb-navbar-menu { display: block; }

    .sb-navbar-links { flex-direction: column; align-items: stretch; gap: 2px; }
    .sb-navbar-link { padding: 11px 12px; }

    /* На телефоне подменю просто раскрыто: наводить нечем, а прятать вложенное за вторым касанием —
       это два касания до раздела, о котором никто не догадывается. */
    .sb-navbar-submenu {
        position: static;
        display: block;
        border: none;
        box-shadow: none;
        padding: 0 0 0 14px;
        min-width: 0;
    }

    .sb-navbar-cta { margin: 8px 0 0; display: block; text-align: center; }

    .sb-footer-inner { grid-template-columns: minmax(0, 1fr); gap: 28px; }
}

/* Ссылки подвала и списков — тоже не меньше 24 по высоте: правило одно для всего сайта, а не для шапки. */
.sb-footer-link,
.sb-menu-link {
    display: inline-flex;
    align-items: center;
    min-height: 28px;
    padding-block: 2px;
}

/* blocks.decor.css */
/*
    Фоновое оформление секций.

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

    Всё здесь — градиенты на токенах темы. Ни одного файла, ни одного жёстко заданного цвета: автор меняет палитру,
    и сияние в шапке меняется вместе с кнопками. Оформление рисуется в ::after, потому что ::before уже занят
    затемнением фоновой картинки (см. blocks.core.css) — а секции нужны оба слоя сразу: и фотография с затемнением,
    и мягкий свет поверх.

    Правило дешевизны
    -----------------
    Ни одного анимированного свойства, ни одного blur(). Мягкость даёт сам радиальный градиент — он растушёван по
    построению. `filter: blur()` на слое во весь экран стоит перерисовки при каждой прокрутке, и на слабом ноутбуке
    это видно; растушёванный градиент не стоит ничего.
*/

/* ---------------------------------------------------------------------------------------------------
   Слой
   --------------------------------------------------------------------------------------------------- */

/*
    `clip`, а не `hidden`: `hidden` делает элемент контейнером прокрутки, и `position: sticky` у всего, что внутри,
    молча перестаёт работать. Секция с прилипающей боковой колонкой — обычное дело, и ломать её ради подрезки
    декоративного пятна нельзя.
*/
.sb-decor {
    position: relative;
    overflow: clip;

    --sb-decor-1: var(--sb-primary, #4f46e5);
    --sb-decor-2: var(--sb-secondary, var(--sb-primary, #0ea5e9));
    --sb-decor-3: var(--sb-accent, var(--sb-secondary, #f59e0b));
    --sb-decor-strength: 20%;
    --sb-decor-line: color-mix(in srgb, var(--sb-text, #13151d) 11%, transparent);
}

/*
    На залитой секции красить фон её же основным цветом бессмысленно — пятно не видно. Свет там даёт белый, а
    тёплое пятно берётся из акцента: он единственный в палитре гарантированно контрастирует с основным.
*/
.sb-tone-primary.sb-decor,
.sb-tone-dark.sb-decor {
    --sb-decor-1: #ffffff;
    --sb-decor-2: var(--sb-accent, #ffffff);
    --sb-decor-3: #ffffff;
    --sb-decor-strength: 13%;
    --sb-decor-line: color-mix(in srgb, #ffffff 15%, transparent);
}

.sb-decor::after {
    content: "";
    position: absolute;
    inset: 0;
    z-index: 0;
    pointer-events: none;
}

/* Содержимое — над слоем. У .sb-layout-section дети уже позиционированы, но без z-index, а ::after идёт последним
   и потому выигрывает порядок отрисовки. Явный порядок надёжнее рассуждений о том, кто где в разметке. */
.sb-decor > * {
    position: relative;
    z-index: 1;
}

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

.sb-decor-glow::after {
    background:
        radial-gradient(48% 42% at 12% 0%,
            color-mix(in srgb, var(--sb-decor-1) var(--sb-decor-strength), transparent) 0%, transparent 72%),
        radial-gradient(42% 38% at 90% 4%,
            color-mix(in srgb, var(--sb-decor-2) var(--sb-decor-strength), transparent) 0%, transparent 70%),
        radial-gradient(62% 48% at 64% 106%,
            color-mix(in srgb, var(--sb-decor-3) calc(var(--sb-decor-strength) * .65), transparent) 0%, transparent 74%);
}

/* ---------------------------------------------------------------------------------------------------
   Меш — плотнее и цветнее. Для одного экрана на странице, обычно первого.
   --------------------------------------------------------------------------------------------------- */

.sb-decor-mesh::after {
    background:
        radial-gradient(38% 44% at 8% 12%,
            color-mix(in srgb, var(--sb-decor-1) calc(var(--sb-decor-strength) * 1.5), transparent) 0%, transparent 66%),
        radial-gradient(34% 40% at 78% 0%,
            color-mix(in srgb, var(--sb-decor-2) calc(var(--sb-decor-strength) * 1.35), transparent) 0%, transparent 64%),
        radial-gradient(40% 46% at 100% 62%,
            color-mix(in srgb, var(--sb-decor-3) var(--sb-decor-strength), transparent) 0%, transparent 68%),
        radial-gradient(46% 40% at 28% 100%,
            color-mix(in srgb, var(--sb-decor-2) var(--sb-decor-strength), transparent) 0%, transparent 70%);
}

/* ---------------------------------------------------------------------------------------------------
   Пятно — один крупный ореол за содержимым. Самое спокойное из цветных: держит центр и не спорит с текстом.
   --------------------------------------------------------------------------------------------------- */

.sb-decor-spot::after {
    background: radial-gradient(58% 62% at 50% 34%,
        color-mix(in srgb, var(--sb-decor-1) calc(var(--sb-decor-strength) * 1.2), transparent) 0%, transparent 70%);
}

/* ---------------------------------------------------------------------------------------------------
   Точки и сетка — фактура, а не цвет. Обе гаснут к краям маской: не гаснущая сетка выглядит как миллиметровка
   и спорит с текстом ровно там, где его читают.
   --------------------------------------------------------------------------------------------------- */

.sb-decor-dots::after {
    background-image: radial-gradient(var(--sb-decor-line) 1.2px, transparent 1.3px);
    background-size: 22px 22px;
    -webkit-mask-image: radial-gradient(72% 68% at 50% 22%, #000 0%, transparent 78%);
    mask-image: radial-gradient(72% 68% at 50% 22%, #000 0%, transparent 78%);
}

.sb-decor-grid::after {
    background-image:
        linear-gradient(to right, var(--sb-decor-line) 1px, transparent 1px),
        linear-gradient(to bottom, var(--sb-decor-line) 1px, transparent 1px);
    background-size: 64px 64px;
    -webkit-mask-image: radial-gradient(74% 70% at 50% 26%, #000 0%, transparent 76%);
    mask-image: radial-gradient(74% 70% at 50% 26%, #000 0%, transparent 76%);
}

/* ---------------------------------------------------------------------------------------------------
   Лучи — свет сверху. Сильное оформление: хорошо ровно на одном экране, обычно на тёмном.
   --------------------------------------------------------------------------------------------------- */

.sb-decor-rays::after {
    background: repeating-conic-gradient(from 196deg at 50% -22%,
        color-mix(in srgb, var(--sb-decor-1) var(--sb-decor-strength), transparent) 0deg 7deg,
        transparent 7deg 20deg);
    -webkit-mask-image: linear-gradient(to bottom, #000 0%, transparent 78%);
    mask-image: linear-gradient(to bottom, #000 0%, transparent 78%);
}

/* ---------------------------------------------------------------------------------------------------
   Телефон
   --------------------------------------------------------------------------------------------------- */

/* Шаг фактуры на узком экране мельче: клетка в 64 пикселя на ширине в 360 даёт пять линий — это уже не фактура,
   а разлиновка. */
@container sb-page (max-width: 720px) {
    .sb-decor-grid::after { background-size: 40px 40px; }
    .sb-decor-dots::after { background-size: 16px 16px; }
}

/*
    Прозрачность оформления не трогается при `prefers-reduced-contrast`, а вот при печати слой убирается целиком:
    градиент во всю полосу — это страница, залитая краской, и ничего больше он на бумаге не даёт.
*/
@media print {
    .sb-decor::after { display: none; }
}

