/*
 * Общий стиль витрины TopBro.
 *
 * Ни одного собственного значения цвета, шрифта или размера: всё берётся
 * переменными из tokens.css (правило 4). Там, где прототип использует значение,
 * которого в дизайн-системе нет (полупрозрачные подложки под фото, промежуточные
 * кегли вроде 34px и 12.5px), взята ближайшая роль дизайн-системы, а не подобрано
 * похожее число — расхождения перечислены в конце файла.
 *
 * Порядок разделов повторяет порядок блоков лендинга.
 *
 * Брейкпоинты 600px и 1024px записаны числами: `@media` не умеет читать
 * пользовательские свойства. Значения — те же, что в --breakpoint-tablet и
 * --breakpoint-desktop, других в файле нет.
 */

/* ==========================================================================
   ОСНОВА
   ========================================================================== */

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

html {
  scroll-behavior: smooth;
  /* Липкая шапка не должна накрывать заголовок блока при переходе по якорю. */
  scroll-padding-top: var(--space-96);
}

body {
  margin: 0;
  background: var(--color-bg-page);
  color: var(--color-text-primary);
  font-family: var(--font-text);
  font-size: var(--text-body-m-mobile);
  font-weight: var(--font-weight-regular);
  line-height: var(--text-body-m-line-height);
}

img {
  max-width: 100%;
}

a {
  color: inherit;
  text-decoration: none;
}

h1,
h2,
h3,
p {
  margin: 0;
}

/* Кольцо фокуса обязательно у каждого интерактивного элемента (Guidelines). */
a:focus-visible,
button:focus-visible,
select:focus-visible,
input:focus-visible,
summary:focus-visible {
  outline: none;
  box-shadow: var(--focus-ring);
}

@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }
}

/* ==========================================================================
   ТИПОГРАФИКА
   Шкала Typography: мобильный кегль по умолчанию, десктопный — с 1024px.
   ========================================================================== */

.display,
.h1,
.h2,
.h3,
.h4 {
  font-family: var(--font-headline);
}

.display {
  font-size: var(--text-display-mobile);
  font-weight: var(--text-display-weight);
  line-height: var(--text-display-line-height);
  letter-spacing: var(--text-display-tracking);
  text-transform: uppercase;
}

.h1 {
  font-size: var(--text-h1-mobile);
  font-weight: var(--text-h1-weight);
  line-height: var(--text-h1-line-height);
  letter-spacing: var(--text-h1-tracking);
  text-transform: uppercase;
}

.h2 {
  font-size: var(--text-h2-mobile);
  font-weight: var(--text-h2-weight);
  line-height: var(--text-h2-line-height);
  letter-spacing: var(--text-h2-tracking);
  text-transform: uppercase;
}

.h3 {
  font-size: var(--text-h3-mobile);
  font-weight: var(--text-h3-weight);
  line-height: var(--text-h3-line-height);
  letter-spacing: var(--text-h3-tracking);
}

.h4 {
  font-size: var(--text-h4-mobile);
  font-weight: var(--text-h4-weight);
  line-height: var(--text-h4-line-height);
  letter-spacing: var(--text-h4-tracking);
}

.body-l {
  font-size: var(--text-body-l-mobile);
  line-height: var(--text-body-l-line-height);
  color: var(--color-text-secondary);
}

.body-m {
  font-size: var(--text-body-m-mobile);
  line-height: var(--text-body-m-line-height);
  color: var(--color-text-secondary);
}

.body-s {
  font-size: var(--text-body-s);
  line-height: var(--text-body-s-line-height);
  color: var(--color-text-secondary);
}

.caption {
  font-size: var(--text-caption);
  font-weight: var(--text-caption-weight);
  line-height: var(--text-caption-line-height);
  letter-spacing: var(--text-caption-tracking);
  color: var(--color-text-muted);
}

.label {
  font-size: var(--text-label);
  font-weight: var(--text-label-weight);
  line-height: var(--text-label-line-height);
  letter-spacing: var(--text-label-tracking);
  text-transform: uppercase;
  color: var(--color-text-muted);
}

/* Надсловие витрины. Parisienne не поддерживает кириллицу в кнопках, полях и
   таблицах — здесь это ровно акцентная фраза, для которой роль и заведена. */
.script {
  font-family: var(--font-decorative);
  font-size: var(--text-h3-mobile);
  line-height: var(--text-h3-line-height);
  color: var(--color-accent);
  margin-bottom: var(--space-8);
}

@media (min-width: 1024px) {
  body {
    font-size: var(--text-body-m);
  }

  .display {
    font-size: var(--text-display);
  }

  .h1 {
    font-size: var(--text-h1);
  }

  .h2 {
    font-size: var(--text-h2);
  }

  .h3 {
    font-size: var(--text-h3);
  }

  .h4 {
    font-size: var(--text-h4);
  }

  .body-l {
    font-size: var(--text-body-l);
  }

  .body-m {
    font-size: var(--text-body-m);
  }

  .script {
    font-size: var(--text-h3);
  }
}

/* ==========================================================================
   СЕТКА И КОНТЕЙНЕР
   ========================================================================== */

.container {
  width: 100%;
  max-width: var(--container-max);
  margin-inline: auto;
  padding-inline: var(--container-gutter-mobile);
}

.section {
  padding-block: var(--space-48);
}

.section--page {
  background: var(--color-bg-page);
}

/*
 * Переходы между секциями лендинга. Фон страницы и фон секции — соседние тона
 * (`--color-bg-page` и `--color-bg-surface`), и стык между ними читался резкой
 * линией. Теперь светлая секция выходит из фона страницы и так же в него
 * возвращается; фото-блоки и CTA на своих краях тоже сходятся к фону страницы.
 * `--section-fade` равен вертикальному отступу секции: на такую длину сходятся к
 * фону края фото и CTA, и текст на них не заходит.
 *
 * Тон светлой секции меняется вдвое длиннее (`--section-blend`) и с разгоном:
 * короткая прямая рампа читалась полосой — глаз цепляется за точку, где она
 * начинается. Промежуточные тона стоят по плавной кривой (0 → 16 → 50 → 84 →
 * 100%), и у рампы нет ни начала, ни конца. На невысокой секции переход не длиннее
 * её половины, иначе верхний и нижний наложились бы. Тона соседние и тёмные: текст
 * на рампе читается так же, как на ровном фоне.
 */
:root {
  --section-fade: var(--space-48);
  --section-blend: min(calc(var(--section-fade) * 2), 50%);
  --blend-1: color-mix(in srgb, var(--color-bg-surface) 16%, var(--color-bg-page));
  --blend-2: color-mix(in srgb, var(--color-bg-surface) 50%, var(--color-bg-page));
  --blend-3: color-mix(in srgb, var(--color-bg-surface) 84%, var(--color-bg-page));
}

@media (min-width: 1024px) {
  :root {
    --section-fade: var(--space-96);
  }
}

.section--surface {
  background: linear-gradient(
    180deg,
    var(--color-bg-page) 0,
    var(--color-bg-surface) var(--section-fade),
    var(--color-bg-surface) calc(100% - var(--section-fade)),
    var(--color-bg-page) 100%
  );
  background: linear-gradient(
    180deg,
    var(--color-bg-page) 0,
    var(--blend-1) calc(var(--section-blend) * 0.25),
    var(--blend-2) calc(var(--section-blend) * 0.5),
    var(--blend-3) calc(var(--section-blend) * 0.75),
    var(--color-bg-surface) var(--section-blend),
    var(--color-bg-surface) calc(100% - var(--section-blend)),
    var(--blend-3) calc(100% - var(--section-blend) * 0.75),
    var(--blend-2) calc(100% - var(--section-blend) * 0.5),
    var(--blend-1) calc(100% - var(--section-blend) * 0.25),
    var(--color-bg-page) 100%
  );
}

/*
 * Сетка на тёмных блоках лендинга: секциях и подвале. Фото-блоки и розовый CTA
 * без неё. Сетка лежит отдельным слоем под содержимым (`::before`, `z-index: -1`
 * внутри `isolation: isolate`).
 *
 * Между тёмными блоками сетка сплошная: линии каждого блока сдвинуты так, чтобы
 * лечь на общую для всей страницы клетку (`--grid-shift` считает js/landing.js по
 * положению блока). Без сдвига у каждой секции клетка начиналась бы со своего
 * верха, и на стыке линии не совпадали. Вертикальные линии от центра, поэтому по
 * горизонтали сетка общая и без расчёта.
 *
 * Затухает сетка только там, где тёмный блок встречается с фото или CTA: там она
 * иначе обрывалась бы линией.
 */
:root {
  /* Проба: розовая сетка — акцент #F2569E. Прежняя белая: rgba(255, 255, 255, 0.07). */
  --grid-line: rgba(242, 86, 158, 0.14);
  --grid-cell: var(--space-48);
}

@media (min-width: 1024px) {
  :root {
    --grid-cell: var(--space-64);
  }
}

.section--page,
.section--surface,
.footer {
  position: relative;
  isolation: isolate;
}

.section--page::before,
.section--surface::before,
.footer::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background-image:
    linear-gradient(90deg, var(--grid-line) 1px, transparent 1px),
    linear-gradient(180deg, var(--grid-line) 1px, transparent 1px);
  background-size: var(--grid-cell) var(--grid-cell);
  background-position: center var(--grid-shift, 0px);
}

/* Вход из фото: сетка проявляется сверху. */
.hero + .section::before,
.promo + .section::before {
  -webkit-mask-image: linear-gradient(180deg, transparent 0, #000 var(--section-fade));
  mask-image: linear-gradient(180deg, transparent 0, #000 var(--section-fade));
}

/* Выход к фото: сетка гаснет снизу. */
.section:has(+ .promo)::before {
  -webkit-mask-image: linear-gradient(0deg, transparent 0, #000 var(--section-fade));
  mask-image: linear-gradient(0deg, transparent 0, #000 var(--section-fade));
}

/*
 * У розового CTA сетки не должно быть видно вовсе, в том числе в его свечении.
 * Свечение лежит в отступах соседей (см. `.cta::before` и `::after`) и на своём
 * дальнем краю почти прозрачное — линии просвечивали сквозь него. Поэтому сетка
 * здесь гаснет целиком ДО зоны свечения: над CTA — выше его `--section-fade`,
 * в подвале — проявляется только ниже его 48px.
 */
.section:has(+ .cta)::before {
  --grid-mask: linear-gradient(
    0deg,
    transparent 0,
    transparent var(--section-fade),
    #000 calc(var(--section-fade) * 2)
  );
  -webkit-mask-image: var(--grid-mask);
  mask-image: var(--grid-mask);
}

.footer::before {
  --grid-mask: linear-gradient(
    180deg,
    transparent 0,
    transparent var(--space-48),
    #000 calc(var(--space-48) + var(--section-fade))
  );
  -webkit-mask-image: var(--grid-mask);
  mask-image: var(--grid-mask);
}

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

@media (min-width: 600px) {
  .container {
    padding-inline: var(--container-gutter-tablet);
  }
}

@media (min-width: 1024px) {
  .container {
    padding-inline: var(--container-gutter-desktop);
  }

  .section {
    padding-block: var(--space-96);
  }

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

/* ==========================================================================
   ФОТО-ЗАГЛУШКИ
   В прототипе на месте фотографий диагональная штриховка двух оттенков. Оба
   оттенка дизайн-система не даёт; берутся две существующие поверхности —
   карточка и секция. Обе штатно держат на себе основной текст.
   ========================================================================== */

.photo {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  background: repeating-linear-gradient(
    45deg,
    var(--color-bg-card) 0,
    var(--color-bg-card) var(--space-12),
    var(--color-bg-surface) var(--space-12),
    var(--color-bg-surface) var(--space-24)
  );
}

/* Пришло настоящее фото — оно занимает всё место заглушки. */
.photo img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.photo__note {
  font-size: var(--text-caption);
  font-weight: var(--text-caption-weight);
  letter-spacing: var(--text-caption-tracking);
  color: var(--color-text-muted);
}

/* ==========================================================================
   КНОПКИ
   ========================================================================== */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-8);
  min-height: var(--touch-target-min);
  border: none;
  border-radius: var(--radius-none);
  font-family: var(--font-text);
  font-weight: var(--text-button-weight);
  line-height: var(--text-button-line-height);
  letter-spacing: var(--text-button-tracking);
  text-transform: uppercase;
  cursor: pointer;
}

.btn--l {
  min-height: var(--button-height-l);
  padding: var(--button-padding-l);
  font-size: var(--button-text-l);
}

.btn--m {
  min-height: var(--button-height-m);
  padding: var(--button-padding-m);
  font-size: var(--button-text-m);
}

.btn--s {
  min-height: var(--button-height-s);
  padding: var(--button-padding-s);
  font-size: var(--button-text-s);
}

.btn--primary {
  background: var(--button-primary-bg);
  color: var(--button-primary-text);
}

.btn--primary:hover {
  background: var(--button-primary-bg-hover);
}

.btn--primary:active {
  background: var(--color-accent-pressed);
  color: var(--color-text-on-accent-pressed);
}

.btn--primary:disabled {
  background: var(--button-primary-bg-disabled);
  color: var(--button-primary-text-disabled);
  cursor: default;
}

.btn--secondary {
  background: var(--button-secondary-bg);
  color: var(--button-secondary-text);
  border: 1px solid var(--button-secondary-border);
}

.btn--secondary:hover {
  background: var(--button-secondary-bg-hover);
  border-color: var(--button-secondary-border-hover);
}

.btn--ghost {
  background: var(--button-ghost-bg);
  color: var(--button-ghost-text);
}

.btn--ghost:hover {
  background: var(--button-ghost-bg-hover);
  color: var(--button-ghost-text-hover);
}

/* Кнопка на розовой заливке финального CTA: заливка — фон страницы. */
.btn--inverse {
  background: var(--color-bg-page);
  color: var(--color-text-primary);
}

.btn--block {
  width: 100%;
}

.icon-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--touch-target-min);
  height: var(--touch-target-min);
  background: transparent;
  border: 1px solid var(--color-border-control);
  border-radius: var(--radius-none);
  color: var(--color-text-primary);
  cursor: pointer;
}

.icon-btn:hover {
  background: var(--button-secondary-bg-hover);
}

.icon-btn:disabled {
  border-color: var(--color-border-disabled);
  color: var(--color-text-disabled);
  cursor: default;
}

.icon {
  width: var(--icon-size-20);
  height: var(--icon-size-20);
  fill: none;
  stroke: currentColor;
  stroke-width: var(--icon-stroke-width);
  stroke-linecap: round;
  stroke-linejoin: round;
  flex: none;
}

.icon--16 {
  width: var(--icon-size-16);
  height: var(--icon-size-16);
}

.icon--24 {
  width: var(--icon-size-24);
  height: var(--icon-size-24);
}

/* ==========================================================================
   ПОЛЯ ВВОДА
   ========================================================================== */

.field {
  width: 100%;
  min-height: var(--touch-target-min);
  padding: var(--field-padding);
  background: var(--field-bg);
  border: 1px solid var(--field-border);
  border-radius: var(--field-radius);
  color: var(--field-text);
  font-family: var(--font-text);
  font-size: var(--field-text-size);
}

.field:focus-visible {
  border-color: var(--field-border-focus);
  box-shadow: var(--field-focus-ring);
}

.field:disabled {
  background: var(--field-bg-disabled);
  border-color: var(--field-border-disabled);
  color: var(--field-text-disabled);
}

/* ==========================================================================
   ЗАГЛУШКИ ЗАГРУЗКИ И ОШИБКИ
   Пока данные не пришли, на их месте стоит блок того же размера, а не пустота.
   Мерцания нет намеренно: токенов длительности и кривой анимации в
   дизайн-системе нет вовсе.
   ========================================================================== */

.skeleton {
  background: var(--color-bg-card);
  border-radius: var(--radius-sm);
  color: transparent;
  user-select: none;
}

.skeleton--line {
  display: block;
  height: var(--space-12);
  margin-block: var(--space-8);
}

.skeleton--short {
  max-width: var(--space-96);
}

.skeleton--text {
  display: inline-block;
  min-width: var(--space-64);
  height: 1em;
  vertical-align: middle;
}

/* Ошибка API попадает на экран текстом, а не только в консоль (правило 7). */
.load-error {
  grid-column: 1 / -1; /* сетка услуг */
  flex: 1 1 100%;      /* лента мастеров */
  padding: var(--space-16);
  background: var(--color-bg-card);
  border-left: var(--space-4) solid var(--color-danger);
  border-radius: var(--radius-sm);
  color: var(--color-danger-text);
  font-size: var(--text-body-m);
  line-height: var(--text-body-m-line-height);
}

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* ==========================================================================
   ШАПКА
   ========================================================================== */

.header {
  position: sticky;
  top: 0;
  z-index: 20;
  background: var(--color-bg-page);
  border-bottom: 1px solid var(--color-border-divider);
}

/* Высота шапки: кнопка 44px, отступы полосы и линия снизу. Нужна hero, который
   на лендинге заходит под прозрачную шапку и отступает на эту высоту. */
:root {
  --header-height: 69px;
}

@media (min-width: 1024px) {
  :root {
    --header-height: 77px;
  }
}

/*
 * Шапка лендинга: лежит поверх фото hero и сливается с ним. Прозрачной она
 * остаётся только наверху страницы. Стоит прокрутить — под ней поедут карточки и
 * заголовки, и пункты меню легли бы прямо на текст, поэтому появляется
 * полупрозрачная подложка с размытием (`data-scrolled` ставит js/landing.js).
 * Открытое мобильное меню получает подложку сплошную: оно длинное, и сквозь
 * полупрозрачную под пунктами проступали кнопки hero.
 */
.header--overlay {
  position: fixed;
  inset-inline: 0;
  background: transparent;
  border-bottom-color: transparent;
  transition:
    background-color 0.25s ease,
    border-color 0.25s ease,
    backdrop-filter 0.25s ease;
}

.header--overlay[data-scrolled='true'] {
  background: var(--color-bg-page);
  background: color-mix(in srgb, var(--color-bg-page) 78%, transparent);
  border-bottom-color: var(--color-border-divider);
  backdrop-filter: blur(16px);
}

.header--overlay:has(.header__menu[data-open='true']) {
  background: var(--color-bg-page);
  border-bottom-color: var(--color-border-divider);
}

.header--overlay .header__menu {
  border-top-color: transparent;
}

.header__bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-16);
  padding-block: var(--space-12);
}

.header__logo {
  flex: none;
  font-family: var(--font-headline);
  font-size: var(--text-h4-mobile);
  font-weight: var(--font-weight-extrabold);
  letter-spacing: var(--text-caption-tracking);
  text-transform: uppercase;
  white-space: nowrap;
}

.header__nav {
  display: none;
  gap: var(--space-32);
}

.header__nav a {
  font-size: var(--text-body-s);
  font-weight: var(--font-weight-semibold);
  color: var(--color-text-secondary);
}

.header__nav a:hover {
  color: var(--color-accent-text);
}

.header__actions {
  display: flex;
  align-items: center;
  gap: var(--space-12);
}

.header__socials {
  display: none;
  gap: var(--space-16);
  color: var(--color-text-secondary);
}

.header__login {
  display: none;
}

.header__burger {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--touch-target-min);
  height: var(--touch-target-min);
  background: transparent;
  border: none;
  color: var(--color-text-primary);
  cursor: pointer;
}

/* Мобильное меню: тех же пять пунктов, что и в навигации десктопа. */
.header__menu {
  display: none;
  flex-direction: column;
  gap: var(--space-4);
  padding-bottom: var(--space-16);
  border-top: 1px solid var(--color-border-divider);
}

/* Меню раскрывается внутри липкой шапки. На невысоком экране пять пунктов и
   кнопка входа уезжают за нижний край, а доскроллить до них нельзя: шапка
   приколота к верху. Ограничиваем меню высотой экрана и прокручиваем его само. */
.header__menu[data-open='true'] {
  display: flex;
  max-height: calc(100vh - var(--space-64));
  overflow-y: auto;
  overscroll-behavior: contain;
}

@supports (max-height: 100dvh) {
  .header__menu[data-open='true'] {
    max-height: calc(100dvh - var(--space-64));
  }
}

.header__menu a {
  padding-block: var(--space-12);
  font-size: var(--text-body-m);
  font-weight: var(--font-weight-semibold);
  color: var(--color-text-secondary);
}

.header__menu .btn {
  margin-top: var(--space-8);
}

/* Планшет: разделы всё ещё под бургером, но для «Войти» место в строке уже есть,
   и прятать вход туда, где он помещается, незачем. */
@media (min-width: 600px) {
  .header__login {
    display: inline-flex;
  }
}

@media (min-width: 1024px) {
  .header__bar {
    padding-block: var(--space-16);
  }

  .header__logo {
    font-size: var(--text-h4);
  }

  .header__nav,
  .header__socials,
  .header__login {
    display: flex;
  }

  .header__actions {
    gap: var(--space-24);
  }

  .header__burger,
  .header__menu {
    display: none;
  }
}

/* ==========================================================================
   HERO
   ========================================================================== */

.hero {
  position: relative;
  /* Сверху ещё и высота шапки: она прозрачная и лежит поверх фото. */
  padding-block: calc(var(--space-48) + var(--header-height)) var(--space-48);
  /* Фото салона — то самое «фото: салон, работа мастера» из прототипа; на его
     месте до сих пор стояла штриховка-заглушка. Цвет держит вид, пока файл не
     загрузился, и подстраховывает, если снимка вдруг нет. */
  background-color: var(--color-bg-card);
  background-image: url('img/photo_1.jpg');
  background-position: center;
  background-size: cover;
  background-repeat: no-repeat;
}

/*
 * Затемнение над фото. На узком экране текст лежит поверх снимка целиком,
 * поэтому тень набирает силу к низу; на широком уходит влево, оставляя правую
 * часть кадра видимой под карточкой быстрого подбора. Обе заливки — из
 * прототипа: снимок светлый, и без них заголовок на нём не читается.
 */
.hero::before {
  content: '';
  position: absolute;
  inset: 0;
  /* Верхний слой — плавный уход фото в фон страницы у нижнего края. */
  background:
    linear-gradient(
      180deg,
      transparent calc(100% - var(--section-fade)),
      var(--color-bg-page) 100%
    ),
    linear-gradient(
      180deg,
      rgba(34, 30, 40, 0.5) 0%,
      rgba(34, 30, 40, 0.96) 70%
    );
}

.hero__grid {
  /* Над затемнением: иначе ::before накрыл бы текст и карточку. */
  position: relative;
  display: grid;
  gap: var(--space-32);
  align-items: center;
}

.hero__text {
  max-width: 600px;
}

.hero__title {
  margin-bottom: var(--space-16);
}

.hero__lead {
  margin-bottom: var(--space-24);
  max-width: 480px;
}

.hero__actions {
  display: flex;
  flex-direction: column;
  gap: var(--space-12);
}

.hero__card {
  padding: var(--space-24);
  background: var(--color-bg-card);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow-modal);
}

.hero__card-title {
  margin-bottom: var(--space-16);
  font-size: var(--text-h4-mobile);
  font-weight: var(--font-weight-bold);
}

.hero__form {
  display: flex;
  flex-direction: column;
  gap: var(--space-12);
}

.hero__dots {
  display: flex;
  gap: var(--space-8);
  margin-top: var(--space-24);
}

.hero__dot {
  width: var(--space-24);
  height: var(--space-4);
  border-radius: var(--radius-full);
  background: var(--color-border-control);
}

.hero__dot[data-active='true'] {
  background: var(--color-accent);
}

@media (min-width: 600px) {
  .hero__actions {
    flex-direction: row;
  }
}

@media (min-width: 1024px) {
  .hero {
    padding-block: calc(var(--space-96) + var(--header-height)) var(--space-96);
  }

  .hero::before {
    background:
      linear-gradient(
        180deg,
        transparent calc(100% - var(--section-fade)),
        var(--color-bg-page) 100%
      ),
      linear-gradient(
        90deg,
        rgba(34, 30, 40, 0.96) 0%,
        rgba(34, 30, 40, 0.82) 42%,
        rgba(34, 30, 40, 0.35) 75%,
        rgba(34, 30, 40, 0.55) 100%
      );
  }

  .hero__grid {
    grid-template-columns: 1fr auto;
    gap: var(--space-64);
  }

  .hero__title {
    margin-bottom: var(--space-24);
  }

  .hero__lead {
    margin-bottom: var(--space-32);
  }

  .hero__actions {
    gap: var(--space-16);
  }

  .hero__card {
    width: 380px;
    padding: var(--space-32);
  }

  .hero__card-title {
    font-size: var(--text-h4);
  }
}

/* ==========================================================================
   СЧЁТЧИКИ САЛОНА
   ========================================================================== */

.stats {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: var(--space-24);
  text-align: center;
}

.stats__value {
  font-family: var(--font-headline);
  font-size: var(--text-h1-mobile);
  font-weight: var(--text-h1-weight);
  line-height: var(--text-h1-line-height);
  color: var(--color-accent);
}

.stats__label {
  margin-top: var(--space-4);
  font-size: var(--text-body-s);
  color: var(--color-text-secondary);
}

@media (min-width: 1024px) {
  .stats {
    grid-template-columns: repeat(4, 1fr);
  }

  .stats__value {
    font-size: var(--text-h1);
  }
}

/* ==========================================================================
   УСЛУГИ
   ========================================================================== */

.services {
  display: grid;
  gap: var(--space-16);
}

.service {
  display: flex;
  gap: var(--space-16);
  align-items: center;
  padding: var(--space-16);
  background: var(--color-bg-surface);
  border-radius: var(--radius-sm);
}

.section--surface .service {
  background: var(--color-bg-page);
}

.service__photo {
  width: var(--space-64);
  height: var(--space-64);
  flex: none;
  border-radius: var(--radius-sm);
}

.service__photo .photo__note {
  display: none;
}

.service__body {
  flex: 1;
  min-width: 0;
}

.service__name {
  font-size: var(--text-h4-mobile);
  font-weight: var(--font-weight-bold);
}

.service__meta {
  margin-bottom: var(--space-8);
  font-size: var(--text-body-s);
  color: var(--color-text-muted);
}

@media (min-width: 600px) {
  .services {
    grid-template-columns: repeat(2, 1fr);
  }
}

@media (min-width: 1024px) {
  .services {
    grid-template-columns: 1.1fr 1fr 1fr 1fr;
    gap: var(--space-24);
    align-items: start;
  }

  /* Ступенчатая раскладка прототипа: вторая и четвёртая карточки ниже. */
  .services > :nth-child(2),
  .services > :nth-child(4) {
    margin-top: var(--space-64);
  }

  .services > :nth-child(3) {
    margin-top: var(--space-24);
  }

  .service {
    flex-direction: column;
    align-items: stretch;
    gap: var(--space-16);
    padding: var(--space-24);
  }

  .service__photo {
    width: auto;
    height: auto;
    aspect-ratio: 4 / 3;
  }

  .service__photo .photo__note {
    display: block;
  }

  .service__name {
    font-size: var(--text-h4);
  }

  .service__action {
    align-self: flex-start;
  }

  /* Первая карточка — крупное фото во всю высоту, подпись снизу на подложке. */
  .service--featured {
    padding: 0;
    overflow: hidden;
    gap: 0;
  }

  .service--featured .service__photo {
    aspect-ratio: 3 / 4;
    border-radius: var(--radius-none);
  }

  .service--featured .service__body {
    padding: var(--space-24);
  }
}

/* ==========================================================================
   КАК ЭТО РАБОТАЕТ
   ========================================================================== */

.steps {
  display: grid;
  gap: var(--space-24);
}

.step {
  display: flex;
  gap: var(--space-16);
  align-items: flex-start;
}

.step__number {
  display: flex;
  align-items: center;
  justify-content: center;
  width: var(--touch-target-min);
  height: var(--touch-target-min);
  flex: none;
  border: 1px solid var(--color-accent);
  border-radius: var(--radius-full);
  background: var(--color-bg-page);
  color: var(--color-accent);
  font-family: var(--font-headline);
  font-size: var(--text-h4-mobile);
  font-weight: var(--font-weight-extrabold);
}

.step__title {
  margin-bottom: var(--space-4);
  font-size: var(--text-h4-mobile);
  font-weight: var(--font-weight-bold);
}

.step__text {
  font-size: var(--text-body-s);
  line-height: var(--text-body-s-line-height);
  color: var(--color-text-muted);
}

@media (min-width: 1024px) {
  .steps {
    grid-template-columns: repeat(4, 1fr);
    gap: var(--space-24);
  }

  .step {
    flex-direction: column;
    align-items: center;
    text-align: center;
  }

  .step__number {
    margin-bottom: var(--space-8);
  }

  .step__title {
    font-size: var(--text-h4);
  }
}

/* ==========================================================================
   ВИДЖЕТ СВОБОДНОГО ВРЕМЕНИ (иллюстрация, не живые данные)
   ========================================================================== */

.calendar-block {
  display: grid;
  gap: var(--space-32);
  align-items: center;
}

.calendar-block__lead {
  margin-top: var(--space-16);
  max-width: 420px;
}

.calendar {
  padding: var(--space-24);
  background: var(--color-bg-surface);
  border-radius: var(--radius-sm);
}

.section--surface .calendar {
  background: var(--color-bg-page);
}

.calendar__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-16);
  margin-bottom: var(--space-16);
}

.calendar__tabs {
  display: flex;
  gap: var(--space-4);
  padding: var(--space-4);
  background: var(--color-bg-page);
  border-radius: var(--radius-sm);
}

.calendar__tab {
  padding: var(--space-8) var(--space-12);
  font-size: var(--text-caption);
  font-weight: var(--font-weight-bold);
  color: var(--color-text-muted);
}

.calendar__tab[data-active='true'] {
  background: var(--color-accent);
  color: var(--color-text-on-accent);
  border-radius: var(--radius-button-components);
}

.calendar__date {
  font-size: var(--text-body-s);
  font-weight: var(--font-weight-bold);
}

.calendar__slots {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-8);
  margin-bottom: var(--space-16);
}

.slot {
  padding: var(--space-12) var(--space-4);
  text-align: center;
  border-radius: var(--radius-sm);
  font-size: var(--text-body-s);
  font-weight: var(--font-weight-semibold);
}

.slot--available {
  background: var(--slot-available-bg);
  border: 1px solid var(--slot-available-border);
  color: var(--slot-available-text);
}

.slot--booked {
  background: var(--slot-booked-bg);
  border: 1px solid var(--slot-booked-border);
  color: var(--slot-booked-text);
  font-weight: var(--font-weight-regular);
}

.slot--selected {
  background: var(--slot-selected-bg);
  color: var(--slot-selected-text);
  font-weight: var(--font-weight-bold);
  box-shadow: var(--slot-selected-shadow);
}

.slot--past {
  background: var(--slot-past-bg);
  border: var(--slot-past-border);
  color: var(--slot-past-text);
  font-weight: var(--font-weight-regular);
  text-decoration: line-through;
}

.notice {
  display: flex;
  align-items: center;
  gap: var(--space-8);
  padding: var(--space-12) var(--space-16);
  background: var(--color-bg-card);
  border-left: var(--space-4) solid var(--color-info);
  border-radius: var(--radius-sm);
  font-size: var(--text-caption);
  color: var(--color-text-secondary);
}

.notice .icon {
  color: var(--color-info);
}

/* `display: flex` выше перебивает атрибут hidden — возвращаем ему силу. */
.notice[hidden] {
  display: none;
}

.calendar__summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-16);
  margin-top: var(--space-16);
  padding: var(--space-16);
  background: var(--color-bg-card);
  border-radius: var(--radius-sm);
}

.calendar__summary-title {
  font-size: var(--text-body-s);
  font-weight: var(--font-weight-bold);
}

@media (min-width: 1024px) {
  .calendar-block {
    grid-template-columns: 1fr 1fr;
    gap: var(--space-48);
  }

  .calendar__slots {
    grid-template-columns: repeat(6, 1fr);
  }
}

/* ==========================================================================
   МАСТЕРА
   Горизонтальная лента со стрелками — как в прототипе, с планшета и шире.
   На телефоне ленты нет: там карточки идут столбцом, во всю ширину экрана.
   ========================================================================== */

.masters-head {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--space-16);
  margin-bottom: var(--space-24);
}

.masters-nav {
  display: none;
  gap: var(--space-8);
  flex: none;
}

/* Узкий экран: карточки идут друг под другом во всю ширину. Лентой они здесь
   не листаются — соседняя карточка обрывалась у края экрана и читалась как
   «блок не поместился». Лента с прокруткой начинается с планшета, где в неё
   входят две целые карточки. */
.masters {
  display: flex;
  flex-direction: column;
  gap: var(--space-16);
}

.master {
  scroll-snap-align: start;
  background: var(--color-bg-page);
  border-radius: var(--radius-sm);
  overflow: hidden;
}

.section--page .master {
  background: var(--color-bg-surface);
}

.master__photo {
  aspect-ratio: 4 / 3;
}

.master__body {
  padding: var(--space-16);
}

.master__name {
  font-size: var(--text-h4-mobile);
  font-weight: var(--font-weight-bold);
}

.master__spec {
  margin-bottom: var(--space-4);
  font-size: var(--text-caption);
  color: var(--color-text-muted);
}

.master__rating {
  margin-bottom: var(--space-8);
  font-size: var(--text-caption);
  color: var(--color-accent);
}

.master__chips {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-4);
  margin-bottom: var(--space-12);
  min-height: var(--space-24);
}

.chip {
  padding: var(--space-4) var(--space-8);
  background: var(--color-accent-subtle);
  border-radius: var(--radius-full);
  color: var(--color-accent);
  font-size: var(--text-caption);
  font-weight: var(--font-weight-semibold);
}

@media (min-width: 600px) {
  /* С планшета — та самая горизонтальная лента: две целые карточки в кадре,
     остальные листаются свайпом, а с 1024px ещё и стрелками. */
  .masters {
    flex-direction: row;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    padding-bottom: var(--space-8);
  }

  .master {
    flex: 0 0 calc((100% - var(--space-16)) / 2);
  }
}

@media (min-width: 1024px) {
  .masters-head {
    margin-bottom: var(--space-48);
  }

  .masters-nav {
    display: flex;
  }

  .masters {
    gap: var(--space-24);
  }

  .master {
    flex: 0 0 calc((100% - var(--space-24) * 3) / 4);
  }

  .master__body {
    padding: var(--space-16);
  }

  .master__name {
    font-size: var(--text-h4);
  }
}

/* ==========================================================================
   ПРОМО-БАННЕР
   ========================================================================== */

.promo {
  position: relative;
  padding-block: var(--space-64);
  text-align: center;
  /* Интерьер салона — «фото: интерьер салона» прототипа; штриховка стояла на
     его месте заглушкой. Цвет держит вид, пока файл не загрузился. */
  background-color: var(--color-bg-card);
  background-image: url('img/photo_2.jpg');
  background-position: center;
  background-size: cover;
  background-repeat: no-repeat;
}

/*
 * Затемнение над фото. Здесь оно ровное, а не градиентом, как в hero: текст
 * стоит по центру и занимает всю ширину баннера — светлого угла, куда его
 * можно увести, тут нет. Плотность из прототипа.
 */
.promo::before {
  content: '';
  position: absolute;
  inset: 0;
  /* Верхний слой — плавный вход фото из фона страницы и уход обратно в него. */
  background:
    linear-gradient(
      180deg,
      var(--color-bg-page) 0,
      transparent var(--section-fade),
      transparent calc(100% - var(--section-fade)),
      var(--color-bg-page) 100%
    ),
    rgba(21, 19, 24, 0.8);
}

.promo .container {
  /* Над затемнением: иначе ::before накрыл бы заголовок и кнопку. */
  position: relative;
}

.promo__title {
  margin-bottom: var(--space-16);
}

.promo__lead {
  margin-bottom: var(--space-32);
}

@media (min-width: 1024px) {
  .promo {
    padding-block: var(--space-96);
  }

  .promo::before {
    background:
      linear-gradient(
        180deg,
        var(--color-bg-page) 0,
        transparent var(--section-fade),
        transparent calc(100% - var(--section-fade)),
        var(--color-bg-page) 100%
      ),
      rgba(21, 19, 24, 0.78);
  }
}

/* ==========================================================================
   КЛИЕНТУ / САЛОНУ
   ========================================================================== */

.benefits {
  display: grid;
  gap: var(--space-32);
}

.benefits__title {
  margin-bottom: var(--space-16);
}

.benefits__list {
  display: flex;
  flex-direction: column;
  gap: var(--space-16);
  margin: 0;
  padding: 0;
  list-style: none;
}

.benefits__item {
  display: flex;
  gap: var(--space-12);
  align-items: flex-start;
  font-size: var(--text-body-m);
  line-height: var(--text-body-m-line-height);
  color: var(--color-text-secondary);
}

.benefits__item .icon {
  color: var(--color-accent);
  margin-top: var(--space-4);
}

@media (min-width: 1024px) {
  .benefits {
    grid-template-columns: 1fr 1fr;
    gap: var(--space-48);
  }

  .benefits__title {
    margin-bottom: var(--space-24);
  }

  .benefits__list {
    gap: var(--space-24);
  }
}

/* ==========================================================================
   ГАЛЕРЕЯ РАБОТ
   Высота строки 160px десктопа и 120px мобильного собрана из шагов шкалы
   отступов: 96 + 64 и 96 + 24. Отдельных токенов высоты в системе нет.
   ========================================================================== */

.gallery {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  grid-auto-rows: calc(var(--space-96) + var(--space-24));
  gap: var(--space-8);
}

.gallery__item {
  border-radius: var(--radius-sm);
  border: 1px solid transparent;
}

.gallery__item:hover {
  border-color: var(--color-accent);
}

@media (min-width: 1024px) {
  .gallery {
    grid-template-columns: repeat(4, 1fr);
    grid-auto-rows: calc(var(--space-96) + var(--space-64));
    gap: var(--space-12);
  }

  /* В прототипе крупная плитка на два ряда, но шесть плиток занимают десять
     клеток при восьми в двух рядах: широкая последняя уезжала в третий ряд, и
     рядом с ней оставалась дыра. На три ряда 4 + 2×2 + 2 = 12 — сетка без
     пустот, крупное первое и широкое последнее сохранены. */
  .gallery__item:first-child {
    grid-column: span 2;
    grid-row: span 3;
  }

  .gallery__item:last-child {
    grid-column: span 2;
  }
}

/* ==========================================================================
   ОТЗЫВЫ
   ========================================================================== */

.reviews {
  display: grid;
  gap: var(--space-16);
}

.review {
  padding: var(--space-24);
  background: var(--color-bg-surface);
  border-radius: var(--radius-sm);
  border: 1px solid transparent;
}

.review--featured {
  border-color: var(--color-accent);
}

.review__head {
  display: flex;
  align-items: center;
  gap: var(--space-12);
  margin-bottom: var(--space-12);
}

/* До ответа сервера — заглушка высотой с карточку отзыва, а не полоска. */
.review.skeleton {
  min-height: calc(var(--space-96) + var(--space-64));
}

/* Инициалы автора — как кружки в кабинете и админке. Фото автора у отзыва нет:
   клиент его не загружает, а чужое лицо рядом с именем было бы неправдой. */
.review__avatar {
  display: flex;
  align-items: center;
  justify-content: center;
  width: var(--touch-target-min);
  height: var(--touch-target-min);
  flex: none;
  border-radius: var(--radius-full);
  background: var(--color-accent-subtle);
  color: var(--color-accent-text);
  font-size: var(--text-caption);
  font-weight: var(--font-weight-bold);
}

/* Отзывов пока нет — одна строка на всю ширину сетки. */
.reviews__empty {
  grid-column: 1 / -1;
}

.review__name {
  font-size: var(--text-body-m);
  font-weight: var(--font-weight-bold);
}

.review__service {
  font-size: var(--text-caption);
  color: var(--color-text-muted);
}

.review__stars {
  margin-bottom: var(--space-8);
  font-size: var(--text-body-s);
  color: var(--color-accent);
}

@media (min-width: 1024px) {
  .reviews {
    grid-template-columns: repeat(3, 1fr);
    gap: var(--space-24);
  }
}

/* ==========================================================================
   FAQ
   ========================================================================== */

.faq {
  display: flex;
  flex-direction: column;
  gap: var(--space-8);
  max-width: 760px;
}

.faq__item {
  padding: var(--space-16) var(--space-24);
  background: var(--color-bg-page);
  border-radius: var(--radius-sm);
}

.section--page .faq__item {
  background: var(--color-bg-surface);
}

.faq__question {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-16);
  min-height: var(--touch-target-min);
  font-size: var(--text-body-m);
  font-weight: var(--font-weight-semibold);
  cursor: pointer;
  list-style: none;
}

.faq__question::-webkit-details-marker {
  display: none;
}

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

.faq__item[open] .faq__question .icon {
  transform: rotate(180deg);
}

.faq__answer {
  margin-top: var(--space-12);
  font-size: var(--text-body-s);
  line-height: var(--text-body-s-line-height);
  color: var(--color-text-secondary);
}

/* ==========================================================================
   ФИНАЛЬНЫЙ CTA
   ========================================================================== */

.cta {
  position: relative;
  /* Выше соседей: подвал тоже позиционирован (слой сетки) и иначе закрыл бы
     своим фоном нижнее свечение. */
  z-index: 1;
  padding-block: var(--space-48);
  background: var(--color-accent);
  text-align: center;
}

/*
 * Плавный вход в розовый блок и выход из него. Внутрь CTA переход не заходит:
 * тёмный текст блока на тёмном краю не читался бы. Поэтому свечение лежит снаружи,
 * в пустых отступах соседей: сверху — в нижнем отступе FAQ (он равен
 * `--section-fade`), снизу — в верхнем отступе подвала (не меньше 48px).
 */
.cta::before,
.cta::after {
  content: '';
  position: absolute;
  inset-inline: 0;
  pointer-events: none;
}

.cta::before {
  bottom: 100%;
  height: var(--section-fade);
  background: linear-gradient(180deg, transparent, var(--color-accent));
}

.cta::after {
  top: 100%;
  height: var(--space-48);
  background: linear-gradient(180deg, var(--color-accent), transparent);
}

.cta__title {
  margin-bottom: var(--space-24);
  color: var(--color-text-on-accent);
}

.cta__form {
  display: flex;
  flex-direction: column;
  gap: var(--space-12);
  margin-bottom: var(--space-12);
}

.cta__field {
  border-radius: var(--radius-none);
  background: var(--color-bg-page);
  border-color: var(--color-border-control);
}

.cta__consent {
  display: flex;
  align-items: flex-start;
  justify-content: center;
  gap: var(--space-8);
  font-size: var(--text-caption);
  color: var(--color-text-on-accent);
  text-align: left;
}

.cta__consent a {
  text-decoration: underline;
}

@media (min-width: 1024px) {
  .cta {
    padding-block: var(--space-64);
  }

  .cta__form {
    flex-direction: row;
    justify-content: center;
  }

  .cta__field {
    width: 320px;
  }
}

/* ==========================================================================
   ПОДВАЛ
   ========================================================================== */

.footer {
  padding-top: var(--space-48);
  padding-bottom: var(--space-24);
  background: var(--color-bg-page);
}

.footer__grid {
  display: grid;
  gap: var(--space-32);
}

.footer__logo {
  margin-bottom: var(--space-12);
  font-family: var(--font-headline);
  font-size: var(--text-h4-mobile);
  font-weight: var(--font-weight-extrabold);
  text-transform: uppercase;
}

.footer__about {
  max-width: 280px;
  font-size: var(--text-body-s);
  line-height: var(--text-body-s-line-height);
  color: var(--color-text-muted);
}

.footer__socials {
  display: flex;
  gap: var(--space-16);
  margin-top: var(--space-16);
  color: var(--color-text-secondary);
}

.footer__col-title {
  margin-bottom: var(--space-16);
}

.footer__links {
  display: flex;
  flex-direction: column;
  gap: var(--space-8);
  font-size: var(--text-body-s);
  color: var(--color-text-secondary);
}

.footer__links a:hover {
  color: var(--color-accent-text);
}

.footer__bottom {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: var(--space-12);
  margin-top: var(--space-24);
  padding-top: var(--space-16);
  border-top: 1px solid var(--color-border-divider);
  font-size: var(--text-caption);
  color: var(--color-text-disabled);
}

.footer__bottom a {
  text-decoration: underline;
}

@media (min-width: 600px) {
  .footer__grid {
    grid-template-columns: repeat(2, 1fr);
  }
}

@media (min-width: 1024px) {
  .footer {
    padding-top: var(--space-64);
    padding-bottom: var(--space-32);
  }

  .footer__grid {
    grid-template-columns: 1.4fr 1fr 1fr 1fr;
    gap: var(--space-48);
  }

  .footer__logo {
    font-size: var(--text-h4);
  }
}

/* ==========================================================================
   ШАПКА КАБИНЕТА
   Собирается в js/header.js и одинакова на всех страницах Account-*.
   Остаётся выше содержимого: липкая, поверх страницы.

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

.account-header {
  position: sticky;
  top: 0;
  z-index: 20;
  background: var(--palette-dark-base);
  color: var(--palette-text-dark-primary);
  border-bottom: 1px solid var(--color-border-divider);
}

/* Место под шапку занято до того, как модуль её соберёт: страница не прыгает. */
[data-account-header] {
  min-height: var(--space-64);
}

.account-header__bar {
  display: flex;
  align-items: center;
  gap: var(--space-16);
  width: 100%;
  max-width: var(--container-max);
  margin-inline: auto;
  padding: var(--space-12) var(--container-gutter-mobile);
}

.account-header__logo {
  font-family: var(--font-headline);
  font-size: var(--text-h4-mobile);
  font-weight: var(--font-weight-extrabold);
  letter-spacing: var(--text-caption-tracking);
  text-transform: uppercase;
  color: var(--palette-text-dark-primary);
}

.account-header__burger {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--touch-target-min);
  height: var(--touch-target-min);
  margin-left: calc(var(--space-12) * -1);
  background: transparent;
  border: none;
  color: var(--palette-text-dark-primary);
  cursor: pointer;
}

/* На мобильном пункты уезжают под шапку и раскрываются бургером. */
.account-header__nav {
  display: none;
  position: absolute;
  left: 0;
  right: 0;
  top: 100%;
  flex-direction: column;
  padding: var(--space-8) var(--container-gutter-mobile) var(--space-16);
  background: var(--palette-dark-base);
  border-bottom: 1px solid var(--color-border-divider);
}

.account-header__nav--open {
  display: flex;
}

.account-header__link {
  padding-block: var(--space-12);
  font-size: var(--text-body-m);
  font-weight: var(--font-weight-semibold);
  color: var(--palette-text-dark-secondary);
}

.account-header__link:hover {
  color: var(--palette-text-dark-primary);
}

.account-header__link--active {
  color: var(--color-accent);
  font-weight: var(--font-weight-bold);
}

.account-header__side {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  margin-left: auto;
}

.account-header__guest {
  display: flex;
  align-items: center;
  gap: var(--space-8);
}

/* Меню аватара. Отдельного фрейма в прототипе нет — состав пунктов взят из
   листа «Глобальная навигация» карты связей. */
.account-menu {
  position: relative;
}

.account-menu__button {
  display: flex;
  align-items: center;
  gap: var(--space-8);
  min-height: var(--touch-target-min);
  padding-inline: var(--space-8);
  background: transparent;
  border: none;
  color: var(--palette-text-dark-primary);
  font-family: var(--font-text);
  font-size: var(--text-body-s);
  font-weight: var(--font-weight-semibold);
  cursor: pointer;
}

.account-menu__avatar {
  display: flex;
  align-items: center;
  justify-content: center;
  width: var(--space-32);
  height: var(--space-32);
  flex: none;
  border-radius: var(--radius-full);
  background: var(--palette-dark-card);
  color: var(--palette-text-dark-primary);
  font-size: var(--text-caption);
  font-weight: var(--font-weight-bold);
}

.account-menu__name {
  display: none;
}

.account-menu__list {
  position: absolute;
  top: 100%;
  right: 0;
  z-index: 1;
  display: flex;
  flex-direction: column;
  min-width: 220px;
  padding: var(--space-8);
  background: var(--palette-dark-card);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow-modal);
}

.account-menu__list[hidden] {
  display: none;
}

.account-menu__item {
  padding: var(--space-12);
  background: transparent;
  border: none;
  border-radius: var(--radius-sm);
  color: var(--palette-text-dark-secondary);
  font-family: var(--font-text);
  font-size: var(--text-body-s);
  font-weight: var(--font-weight-medium);
  text-align: left;
  cursor: pointer;
}

.account-menu__item:hover {
  background: var(--button-secondary-bg-hover);
  color: var(--palette-text-dark-primary);
}

/* Колокольчик. Живёт в той же тёмной шапке, что и меню аватара, и повторяет его
   оформление: своей палитры у всплывающей панели нет, есть общая «тёмный островок
   в светлой теме». */
/* На узком экране панель уведомлений отсчитывается не от кнопки, а от самой
   шапки. От колокольчика до правого края экрана меньше, чем ширина панели: будь
   она прижата к кнопке, левый край уехал бы за экран вместе с текстом. Шапка
   липкая, то есть уже позиционирована, — отдельной привязки ей не нужно, хватает
   `static` у обёртки. */
.account-bell {
  position: static;
}

.account-bell__button {
  display: inline-flex;
  position: relative;
  align-items: center;
  justify-content: center;
  width: var(--touch-target-min);
  height: var(--touch-target-min);
  background: transparent;
  border: none;
  color: var(--palette-text-dark-primary);
  cursor: pointer;
}

/* Кружок с числом непрочитанных. Смещён на угол иконки, а не приписан сбоку:
   иначе ширина кнопки менялась бы от появления новостей и шапка дёргалась. */
.account-bell__badge {
  position: absolute;
  top: var(--space-4);
  right: var(--space-4);
  min-width: var(--space-16);
  height: var(--space-16);
  padding-inline: var(--space-4);
  border-radius: var(--radius-full);
  background: var(--color-accent);
  color: var(--color-text-on-accent);
  font-size: var(--text-caption);
  font-weight: var(--font-weight-bold);
  line-height: var(--space-16);
  text-align: center;
}

.account-bell__badge[hidden] {
  display: none;
}

.account-bell__list {
  position: absolute;
  top: 100%;
  right: var(--container-gutter-mobile);
  z-index: 1;
  /* Ширина по содержимому, но не шире экрана: на телефоне панель прижата к
     правому краю шапки и должна оставаться в его пределах. */
  width: min(320px, calc(100vw - var(--space-32)));
  /* Лента длинная — прокручивается сама, а не растягивает страницу под шапкой. */
  max-height: 60vh;
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: var(--space-8);
  background: var(--palette-dark-card);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow-modal);
}

.account-bell__list[hidden] {
  display: none;
}

.account-bell__head {
  padding: var(--space-8) var(--space-12);
  font-size: var(--text-label);
  font-weight: var(--text-label-weight);
  letter-spacing: var(--text-label-tracking);
  text-transform: uppercase;
  color: var(--palette-text-dark-secondary);
}

/* Заголовков в панели два — «Скоро» и «Уведомления». Второй отделяется от
   предыдущего раздела, первый прижат к верху панели. */
.account-bell__head:not(:first-child) {
  margin-top: var(--space-12);
}

.account-bell__item {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
  padding: var(--space-12);
  border-radius: var(--radius-sm);
  color: var(--palette-text-dark-secondary);
}

.account-bell__item:hover {
  background: var(--button-secondary-bg-hover);
}

.account-bell__title {
  font-size: var(--text-body-s);
  font-weight: var(--font-weight-semibold);
  color: var(--palette-text-dark-primary);
}

/* Непрочитанное помечено точкой, а не только цветом: пометка должна читаться и
   там, где цвет не различают. Точка после заголовка, а не полоса слева, — иначе
   текст прочитанных и непрочитанных стоял бы на разной вертикали. */
.account-bell__item--unread .account-bell__title::after {
  content: '';
  display: inline-block;
  width: var(--space-8);
  height: var(--space-8);
  margin-left: var(--space-8);
  border-radius: var(--radius-full);
  background: var(--color-accent);
  vertical-align: middle;
}

.account-bell__detail,
.account-bell__meta,
.account-bell__when {
  font-size: var(--text-caption);
  line-height: var(--text-caption-line-height);
}

.account-bell__detail {
  color: var(--palette-text-dark-primary);
}

/* Роли темы (--color-text-muted) здесь не годятся: страницы кабинета светлые,
   и приглушённый текст светлой темы на тёмной панели почти не читается. Берём
   тот же тёмный ряд палитры, что и вся шапка. */
.account-bell__when {
  color: var(--palette-text-dark-muted);
}

.account-bell__empty {
  margin: 0;
  padding: var(--space-16) var(--space-12) var(--space-24);
  font-size: var(--text-body-s);
  color: var(--palette-text-dark-secondary);
  text-align: center;
}

/* Заглушка на время загрузки — размером с настоящий пункт, чтобы панель не
   меняла высоту, когда приедут данные. Фон свой: у `.skeleton` он взят из роли
   темы, а страницы кабинета светлые — внутри тёмной панели вышли бы белые
   бруски. */
.account-bell__skeleton {
  height: var(--space-64);
  margin: var(--space-4) 0;
  border-radius: var(--radius-sm);
  background: var(--palette-dark-surface);
}

@media (min-width: 600px) {
  .account-header__bar {
    padding-inline: var(--container-gutter-tablet);
  }

  .account-menu__name {
    display: inline;
  }

  /* С планшета панель помещается под самой кнопкой — привязываем к ней, как и
     меню аватара рядом. */
  .account-bell {
    position: relative;
  }

  .account-bell__list {
    right: 0;
  }
}

@media (min-width: 1024px) {
  .account-header__bar {
    gap: var(--space-32);
    padding: var(--space-16) var(--container-gutter-desktop);
  }

  .account-header__burger {
    display: none;
  }

  .account-header__nav {
    display: flex;
    position: static;
    flex-direction: row;
    gap: var(--space-24);
    padding: 0;
    background: transparent;
    border-bottom: none;
  }

  .account-header__link {
    padding-block: 0;
    font-size: var(--text-body-s);
  }
}

/* ==========================================================================
   ЭКРАН ВХОДА И РЕГИСТРАЦИИ
   Карточка на пустой странице; фреймы прототипа переключаются по хешу адреса.
   ========================================================================== */

.auth-page {
  min-height: 100vh;
  display: flex;
}

.auth {
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--space-24) var(--container-gutter-mobile);
}

.auth-card {
  width: 100%;
  max-width: 440px;
  padding: var(--space-24);
  /* В прототипе карточка светлее полотна страницы. Полотна такого тона в
     дизайн-системе нет (#151318 — фон холста прототипа), поэтому взята пара
     «страница + поднятая карточка»: роль карточки ровно эта. */
  background: var(--color-bg-card);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow-card);
}

.auth-card[hidden] {
  display: none;
}

.auth-card--centered {
  text-align: center;
}

.auth-card__head {
  margin-bottom: var(--space-24);
  text-align: center;
}

.auth-card__logo {
  display: block;
  margin-bottom: var(--space-8);
  font-family: var(--font-headline);
  font-size: var(--text-h4-mobile);
  font-weight: var(--font-weight-extrabold);
  letter-spacing: var(--text-caption-tracking);
  text-transform: uppercase;
}

.auth-card__back {
  font-size: var(--text-body-s);
  color: var(--color-text-muted);
}

.auth-card__back:hover {
  color: var(--color-text-secondary);
}

.auth-card__title {
  margin-bottom: var(--space-24);
  font-family: var(--font-headline);
  font-size: var(--text-h3-mobile);
  font-weight: var(--text-h3-weight);
  line-height: var(--text-h3-line-height);
  text-align: center;
}

.auth-card__lead {
  margin-top: calc(var(--space-24) * -1 + var(--space-8));
  margin-bottom: var(--space-24);
  font-size: var(--text-body-s);
  line-height: var(--text-body-s-line-height);
  color: var(--color-text-muted);
  text-align: center;
}

.auth-card__foot {
  margin-top: var(--space-24);
  font-size: var(--text-body-s);
  color: var(--color-text-muted);
  text-align: center;
}

/* Кружок с иконкой у служебных фреймов восстановления. */
.auth-mark {
  display: flex;
  align-items: center;
  justify-content: center;
  width: var(--space-64);
  height: var(--space-64);
  margin: 0 auto var(--space-16);
  border-radius: var(--radius-full);
  background: var(--color-bg-page);
}

.auth-mark--info {
  color: var(--color-info);
}

.auth-mark--warning {
  color: var(--color-warning);
}

@media (min-width: 600px) {
  .auth-card {
    padding: var(--space-32);
  }
}

/* Вход через внешний сервис — блок под формой на «Входе» и «Регистрации».
   Собственного стиля у него нет: разделитель нарисован токеном границы, кнопка
   взята вторичной из библиотеки компонентов. Отдельного фирменного цвета
   Яндекса здесь нет намеренно — второй акцент в продукте не вводится
   (tokens.css, «Акцент»). */
.auth-alt {
  margin-top: var(--space-16);
}

.auth-alt__divider {
  display: flex;
  align-items: center;
  gap: var(--space-12);
  margin-bottom: var(--space-16);
  font-size: var(--text-caption);
  font-weight: var(--text-caption-weight);
  letter-spacing: var(--text-caption-tracking);
  color: var(--color-text-muted);
}

/* Линии по обе стороны от слова «или»: псевдоэлементы, чтобы разделитель
   оставался одним элементом разметки. */
.auth-alt__divider::before,
.auth-alt__divider::after {
  content: '';
  flex: 1;
  height: 1px;
  background: var(--color-border-divider);
}

.auth-alt__note {
  margin-top: var(--space-8);
  font-size: var(--text-body-s);
  line-height: var(--text-body-s-line-height);
  color: var(--color-text-muted);
  text-align: center;
}

.auth-alt__note[hidden] {
  display: none;
}

/* Отключённая вторичная кнопка. Значения под неё в дизайн-системе есть
   (--button-secondary-*-disabled), а правила не было: до сих пор вторичную
   кнопку нигде не гасили. */
.btn--secondary:disabled {
  color: var(--button-secondary-text-disabled);
  border-color: var(--button-secondary-border-disabled);
  background: var(--button-secondary-bg);
  cursor: default;
}


/* ==========================================================================
   ФОРМЫ
   ========================================================================== */

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

/* Спутник к `display: flex` (см. .form-field[hidden]): окно «Доступ» прячет форму
   привязки, когда доступ уже выдан. */
.form[hidden] {
  display: none;
}

.form-field {
  display: flex;
  flex-direction: column;
  gap: var(--space-8);
}

/* Спутник к `display: flex` выше. Правило браузера `[hidden] { display: none }`
   лежит в его собственной таблице стилей, а значит слабее любого авторского, —
   без этой строки поле с атрибутом `hidden` остаётся на экране. Так же оговорено
   у .form-message, .form-field__error и остальных, кого прячут атрибутом. */
.form-field[hidden] {
  display: none;
}

.form-field__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-8);
}

.form-label {
  font-size: var(--text-label);
  font-weight: var(--text-label-weight);
  line-height: var(--text-label-line-height);
  letter-spacing: var(--text-label-tracking);
  text-transform: uppercase;
  color: var(--color-text-secondary);
}

.form-link {
  color: var(--color-accent-text);
  font-weight: var(--font-weight-semibold);
}

.form-link:hover {
  color: var(--color-accent-hover);
}

.form-field__control {
  position: relative;
  display: flex;
}

.field--with-action {
  padding-right: var(--space-48);
}

/* Показать или скрыть пароль. Зона нажатия — полная, значок внутри мельче. */
.field-action {
  position: absolute;
  top: 0;
  right: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  width: var(--touch-target-min);
  height: 100%;
  background: transparent;
  border: none;
  color: var(--color-text-muted);
  cursor: pointer;
}

.field-action:hover,
.field-action--active {
  color: var(--color-text-secondary);
}

/* Поле, на которое указал сервер или проверка на форме. */
.field--invalid {
  border-color: var(--field-border-error);
}

.field--invalid:focus-visible {
  border-color: var(--field-border-error);
  box-shadow: var(--focus-ring-danger);
}

.form-field__error {
  margin: 0;
  font-size: var(--text-caption);
  line-height: var(--text-body-s-line-height);
  color: var(--field-text-error);
}

.form-field__error[hidden] {
  display: none;
}

/* Сообщение обо всей форме: 429, отказ сети, предупреждение о неподключённом фрейме. */
.form-message {
  margin-bottom: var(--space-16);
  padding: var(--space-12) var(--space-16);
  background: var(--color-bg-page);
  border-left: var(--space-4) solid var(--color-info);
  border-radius: var(--radius-sm);
  font-size: var(--text-body-s);
  line-height: var(--text-body-s-line-height);
  color: var(--color-text-secondary);
}

.form-message[hidden] {
  display: none;
}

.form-message--danger {
  border-left-color: var(--color-danger);
  color: var(--color-danger-text);
}

.form-message--warning {
  border-left-color: var(--color-warning);
}

.form-consent {
  display: flex;
  align-items: flex-start;
  gap: var(--space-12);
  font-size: var(--text-body-s);
  line-height: var(--text-body-s-line-height);
  color: var(--color-text-secondary);
}

.form__submit {
  margin-top: var(--space-8);
}

/* Индикатор надёжности пароля: три деления. */
.strength {
  display: flex;
  gap: var(--space-4);
}

.strength__bar {
  flex: 1;
  height: var(--space-4);
  border-radius: var(--radius-full);
  background: var(--color-border-control);
}

.strength__bar--on {
  background: var(--color-accent);
}

.strength__text {
  margin: 0;
  font-size: var(--text-caption);
  color: var(--color-text-muted);
}

.strength__text:empty {
  display: none;
}

/* Кнопка в состоянии отправки. Кольцо не вращается: токенов длительности и
   кривой анимации в дизайн-системе нет, а сочинять их вёрстка не должна. */
.btn--busy {
  gap: var(--space-8);
}

.spinner {
  width: var(--space-12);
  height: var(--space-12);
  border-radius: var(--radius-full);
  border: 2px solid var(--button-primary-text-disabled);
  border-top-color: var(--button-primary-text);
}

/* ==========================================================================
   СВЕТЛЫЕ РАБОЧИЕ ЭКРАНЫ
   Дизайн-система показывает поля и кнопки только на тёмном фоне (tokens.css,
   «чего нет», пункт 2). Ниже — тот же компонент в светлой теме, собранный из
   ролей: своих значений не появилось, поменялись только роли.
   ========================================================================== */

[data-theme='light'] .field {
  background: var(--color-bg-card);
  border-color: var(--color-border-field);
  color: var(--color-text-primary);
}

[data-theme='light'] .field:disabled,
[data-theme='light'] .field[readonly] {
  background: var(--color-bg-surface);
  color: var(--color-text-secondary);
}

[data-theme='light'] .btn--secondary {
  color: var(--color-text-primary);
  border-color: var(--color-border-control);
}

[data-theme='light'] .btn--secondary:hover {
  background: var(--color-bg-surface);
  border-color: var(--color-border-control);
}

/* Ghost-кнопка на светлом фоне. Её цвет в токенах задан значением тёмной темы
   (--button-ghost-text: #C9C3D0), и на светлой рабочей области надпись почти не
   видна. Дизайн-система этот случай не описывает — tokens.css, «Чего нет», п. 2:
   «как выглядит вторичная, ghost- и деструктивная кнопка на светлых рабочих
   экранах — не определено». Берём роли приглушённого и основного текста, как это
   уже сделано выше для вторичной кнопки. */
[data-theme='light'] .btn--ghost {
  color: var(--color-text-secondary);
}

[data-theme='light'] .btn--ghost:hover {
  background: var(--color-bg-surface);
  color: var(--color-text-primary);
}

[data-theme='light'] .form-message {
  background: var(--color-bg-card);
  color: var(--color-text-secondary);
}

/* ==========================================================================
   ПРОФИЛЬ КЛИЕНТА
   ========================================================================== */

.profile {
  display: grid;
  gap: var(--space-24);
  padding-block: var(--space-24);
}

.profile__menu {
  display: flex;
  gap: var(--space-4);
  overflow-x: auto;
  padding: var(--space-8);
  background: var(--color-bg-surface);
  border-radius: var(--radius-sm);
}

.profile__menu-item {
  padding: var(--space-12) var(--space-16);
  border-radius: var(--radius-sm);
  color: var(--color-text-secondary);
  font-size: var(--text-body-s);
  white-space: nowrap;
}

.profile__menu-item--active {
  background: var(--color-bg-card);
  color: var(--color-text-primary);
  font-weight: var(--font-weight-bold);
  box-shadow: var(--shadow-card);
}

.profile__section[hidden] {
  display: none;
}

.profile__title {
  margin-bottom: var(--space-24);
}

.profile__avatar-row {
  display: flex;
  align-items: center;
  gap: var(--space-16);
  margin-bottom: var(--space-24);
}

.profile__avatar {
  display: flex;
  align-items: center;
  justify-content: center;
  width: var(--space-64);
  height: var(--space-64);
  flex: none;
  border-radius: var(--radius-full);
  background: var(--palette-dark-card);
  color: var(--palette-text-dark-primary);
  font-size: var(--text-h4);
  font-weight: var(--font-weight-bold);
}

.profile__grid {
  display: grid;
  gap: var(--space-16);
}

.profile__actions {
  display: flex;
  gap: var(--space-8);
}

.profile__narrow {
  max-width: 380px;
}

.profile__hint {
  margin: 0;
  color: var(--color-text-secondary);
}

/* Кнопка, которая читается как ссылка: «Отправить письмо повторно». */
.link-button {
  padding: 0;
  background: none;
  border: none;
  color: var(--color-accent-text);
  font-family: var(--font-text);
  font-size: var(--text-body-s);
  font-weight: var(--font-weight-semibold);
  text-decoration: underline;
  cursor: pointer;
}

.field--with-badge {
  padding-right: var(--space-96);
}

/* Спутник к `display` ниже: правило браузера `[hidden] { display: none }` лежит
   в его собственной таблице стилей и слабее любого авторского. Такой же спутник
   есть у всех компонентов, которые прячут атрибутом, — .form-message,
   .form-field, .modal и остальных. */
.badge[hidden] {
  display: none;
}

.badge {
  display: inline-block;
  padding: var(--space-4) var(--space-8);
  border-radius: var(--radius-full);
  font-size: var(--text-caption);
  font-weight: var(--font-weight-bold);
  white-space: nowrap;
}

/* Бейдж внутри поля ввода — только там, где он лежит поверх текста поля. */
.badge--in-field {
  position: absolute;
  top: 50%;
  right: var(--space-8);
  transform: translateY(-50%);
}

.badge--success {
  background: var(--color-bg-surface);
  color: var(--color-success-text);
}

.badge--warning {
  background: var(--color-bg-surface);
  color: var(--color-warning-text);
}

/* Опасная зона. Отдельная рамка цветом опасности — так в прототипе. */
.danger-zone {
  max-width: 520px;
  margin-top: var(--space-32);
  padding: var(--space-24);
  border: 1px solid var(--color-danger);
  border-radius: var(--radius-sm);
}

.danger-zone__title {
  margin-bottom: var(--space-8);
  font-size: var(--text-h4);
  font-weight: var(--font-weight-bold);
  color: var(--color-danger-text);
}

.danger-zone__text {
  margin-bottom: var(--space-16);
  font-size: var(--text-body-s);
  line-height: var(--text-body-s-line-height);
  color: var(--color-text-secondary);
}

.btn--danger {
  background: var(--button-danger-bg);
  color: var(--color-danger-text);
  border: 1px solid var(--button-danger-border);
}

.btn--danger:hover {
  background: var(--button-danger-bg-hover);
  color: var(--button-danger-text-hover);
}

/* Заливная деструктивная кнопка — подтверждение в модалке. */
.btn--danger-solid {
  background: var(--color-danger);
  color: var(--color-text-inverse);
}

.btn--danger-solid:hover {
  background: var(--palette-danger);
}

/* Переключатели уведомлений. Размеров тумблера дизайн-система не задаёт —
   дорожка и бегунок собраны из шагов шкалы отступов. */
.switches {
  display: flex;
  flex-direction: column;
  gap: var(--space-24);
  max-width: 460px;
}

.switch {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--space-16);
  cursor: pointer;
}

.switch__text {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}

.switch__title {
  font-size: var(--text-body-m);
  font-weight: var(--font-weight-semibold);
  color: var(--color-text-primary);
}

.switch__note {
  font-size: var(--text-body-s);
  color: var(--color-text-muted);
}

.switch__input {
  position: absolute;
  opacity: 0;
  width: 0;
  height: 0;
}

.switch__track {
  position: relative;
  width: var(--space-48);
  height: var(--space-24);
  flex: none;
  border-radius: var(--radius-full);
  background: var(--color-border-control);
}

.switch__track::after {
  content: '';
  position: absolute;
  top: var(--space-4);
  left: var(--space-4);
  width: var(--space-16);
  height: var(--space-16);
  border-radius: var(--radius-full);
  background: var(--color-bg-card);
}

.switch__input:checked + .switch__track {
  background: var(--color-accent);
}

.switch__input:checked + .switch__track::after {
  left: auto;
  right: var(--space-4);
}

.switch__input:focus-visible + .switch__track {
  box-shadow: var(--focus-ring);
}

.switch__input:disabled + .switch__track {
  background: var(--color-border-disabled);
}

/* ==========================================================================
   ВИЗАРД ЗАПИСИ
   ========================================================================== */

.wizard {
  display: flex;
  flex-direction: column;
  min-height: 100vh;
  max-width: 900px;
  margin-inline: auto;
  background: var(--color-bg-page);
}

/* На телефоне 100vh считается по экрану без адресной строки: визард оказывается
   выше видимой части, и липкая полоса с «Далее» встаёт под нижней панелью
   браузера. 100dvh — та же высота, но по действительно видимому окну. */
@supports (min-height: 100dvh) {
  .wizard {
    min-height: 100dvh;
  }
}

.wizard__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: var(--space-16) var(--container-gutter-mobile);
  border-bottom: 1px solid var(--color-border-divider);
}

.wizard__logo {
  font-family: var(--font-headline);
  font-size: var(--text-h4-mobile);
  font-weight: var(--font-weight-extrabold);
  letter-spacing: var(--text-caption-tracking);
  text-transform: uppercase;
}

.wizard__exit {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--touch-target-min);
  height: var(--touch-target-min);
  background: transparent;
  border: none;
  color: var(--color-text-secondary);
  cursor: pointer;
}

.wizard__exit:hover {
  color: var(--color-text-primary);
}

.wizard__body {
  flex: 1;
  padding: var(--space-24) var(--container-gutter-mobile) var(--space-32);
}

.wizard__title {
  margin-bottom: var(--space-24);
  text-transform: uppercase;
}

/* Липкая полоса с итогом и кнопкой «Далее». */
.wizard__bar {
  position: sticky;
  bottom: 0;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-16);
  flex-wrap: wrap;
  padding: var(--space-16) var(--container-gutter-mobile);
  background: var(--color-bg-surface);
  border-top: 1px solid var(--color-border-divider);
}

/* Узкий экран: итог и кнопки в одну строку не помещаются, и полоса всё равно
   переносится — но ширина кнопок при этом зависит от длины итога. Раскладываем
   явно в два ряда: итог сверху, «Назад» и «Далее» под ним во всю ширину. */
.wizard__summary {
  display: flex;
  flex: 1 1 100%;
  flex-direction: column;
  gap: var(--space-4);
  font-size: var(--text-body-s);
  color: var(--color-text-primary);
}

.wizard__hint {
  font-size: var(--text-caption);
  color: var(--color-text-muted);
}

.wizard__hint:empty {
  display: none;
}

.wizard__buttons {
  display: flex;
  flex: 1 1 100%;
  gap: var(--space-8);
}

/* :not(:only-child) — чтобы одинокая кнопка «Назад» на шаге 4 осталась
   по своей ширине, а не растянулась во всю полосу. */
.wizard__buttons .btn:not(:only-child) {
  flex: 1;
}

/* Ссылка-кнопка в отключённом состоянии: у ссылки нет `disabled`. */
.btn--disabled {
  background: var(--button-primary-bg-disabled);
  color: var(--button-primary-text-disabled);
  cursor: default;
  pointer-events: none;
}

/* --- Индикатор шагов ------------------------------------------------------- */

/* Селектор привязан к контейнеру индикатора намеренно. Класс `steps` занят
   дважды: им же размечен блок «Как это работает» на витрине (см. раздел выше).
   Без привязки правило индикатора выигрывало по порядку и разворачивало тот
   блок в один ряд из четырёх шагов без переноса — на узком экране два последних
   уезжали за край страницы, и доскроллить до них было нельзя. */
[data-wizard-steps] .steps {
  display: flex;
  align-items: flex-start;
  gap: var(--space-8);
  margin: 0 0 var(--space-24);
  padding: 0;
  list-style: none;
}

/* min-width: 0 обязателен. Без него колонка шага не может стать уже самого
   длинного слова подписи, и на узком экране «Подтверждение» распирает строку:
   подписи наезжают друг на друга и вылезают за край. */
.steps__item {
  flex: 1;
  min-width: 0;
  text-align: center;
}

.steps__link {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-8);
  color: inherit;
}

.steps__mark {
  display: flex;
  align-items: center;
  justify-content: center;
  width: var(--space-32);
  height: var(--space-32);
  border-radius: var(--radius-full);
  background: var(--color-bg-card);
  color: var(--color-text-disabled);
  font-family: var(--font-headline);
  font-size: var(--text-caption);
  font-weight: var(--font-weight-extrabold);
}

/* Подпись переносится по слогам, а если слог не помещается — по буквам.
   На десктопе колонка шире слова, и ни то ни другое не срабатывает. */
.steps__label {
  font-size: var(--text-caption);
  font-weight: var(--font-weight-semibold);
  color: var(--color-text-disabled);
  hyphens: auto;
  overflow-wrap: anywhere;
}

/* Пройденный шаг — кликабелен, текущий — акцентом, будущий — приглушён. */
.steps__item[data-state='done'] .steps__mark {
  background: var(--color-accent-subtle);
  color: var(--color-accent);
}

.steps__item[data-state='done'] .steps__label {
  color: var(--color-text-secondary);
}

.steps__item[data-state='done'] .steps__link:hover .steps__label {
  color: var(--color-text-primary);
}

.steps__item[data-state='current'] .steps__mark {
  background: var(--color-accent);
  color: var(--color-text-on-accent);
}

.steps__item[data-state='current'] .steps__label {
  color: var(--color-text-primary);
  font-weight: var(--font-weight-bold);
}

/* --- Табы категорий -------------------------------------------------------- */

.tabs {
  display: flex;
  gap: var(--space-8);
  overflow-x: auto;
  margin-bottom: var(--space-24);
}

.tabs__tab {
  padding: var(--space-12) var(--space-16);
  background: transparent;
  border: none;
  border-radius: var(--radius-none);
  color: var(--color-text-secondary);
  font-family: var(--font-text);
  font-size: var(--text-body-s);
  font-weight: var(--font-weight-semibold);
  white-space: nowrap;
  cursor: pointer;
}

.tabs__tab--active {
  background: var(--color-accent);
  color: var(--color-text-on-accent);
  font-weight: var(--font-weight-bold);
}

/* --- Карточка услуги с выбором --------------------------------------------- */

.service-list {
  display: grid;
  gap: var(--space-12);
}

.service-pick {
  position: relative;
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--space-12);
  min-height: var(--space-96);
  padding: var(--space-16);
  background: var(--color-bg-surface);
  border: 1px solid var(--color-border-divider);
  border-radius: var(--radius-sm);
  cursor: pointer;
}

.service-pick--on {
  background: var(--color-bg-card);
  border-color: var(--color-accent);
}

.service-pick__body {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}

.service-pick__name {
  font-size: var(--text-h4-mobile);
  font-weight: var(--font-weight-bold);
}

.service-pick__meta {
  font-size: var(--text-caption);
  color: var(--color-text-muted);
}

.service-pick__text {
  font-size: var(--text-body-s);
  line-height: var(--text-body-s-line-height);
  color: var(--color-text-secondary);
}

.service-pick__input,
.master-pick__input {
  position: absolute;
  opacity: 0;
  width: 0;
  height: 0;
}

.service-pick__mark {
  display: flex;
  align-items: center;
  justify-content: center;
  width: var(--space-24);
  height: var(--space-24);
  flex: none;
  border: 1px solid var(--color-border-control);
  border-radius: var(--radius-button-components);
  color: transparent;
}

.service-pick--on .service-pick__mark {
  background: var(--color-accent);
  border-color: var(--color-accent);
  color: var(--color-text-on-accent);
}

.service-pick__input:focus-visible ~ .service-pick__mark,
.master-pick__input:focus-visible ~ .master-pick__body {
  box-shadow: var(--focus-ring);
}

/* --- Плашка выбранного визита и карточки мастеров --------------------------- */

.picked {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-16);
  margin-bottom: var(--space-24);
  padding: var(--space-12) var(--space-16);
  background: var(--color-bg-surface);
  border-radius: var(--radius-sm);
  font-size: var(--text-body-s);
}

/* Спутник к `display: flex`: на странице переноса плашку прячут, когда запись
   не открылась. */
.picked[hidden] {
  display: none;
}

.picked__note {
  margin: var(--space-8) 0 0;
  font-size: var(--text-body-s);
}

.picked__actions {
  display: flex;
  gap: var(--space-8);
  flex-wrap: wrap;
  margin-top: var(--space-12);
}

.master-list {
  display: grid;
  gap: var(--space-12);
}

.master-pick {
  position: relative;
  display: flex;
  gap: var(--space-16);
  min-height: var(--space-96);
  padding: var(--space-16);
  background: var(--color-bg-surface);
  border: 1px solid var(--color-border-divider);
  border-radius: var(--radius-sm);
  cursor: pointer;
}

.master-pick--on {
  background: var(--color-bg-card);
  border-color: var(--color-accent);
}

/* Мастер, который не делает весь набор: виден, но не выбирается. */
.master-pick--off {
  cursor: default;
  opacity: 0.6;
}

.master-pick--off .master-pick__name {
  color: var(--color-text-secondary);
}

.master-pick__photo {
  width: var(--space-64);
  height: var(--space-64);
  flex: none;
  border-radius: var(--radius-sm);
}

.master-pick__photo .photo__note {
  display: none;
}

.master-pick__body {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
  border-radius: var(--radius-sm);
}

.master-pick__name {
  font-size: var(--text-h4-mobile);
  font-weight: var(--font-weight-bold);
}

.master-pick__spec,
.master-pick__nearest {
  font-size: var(--text-caption);
  color: var(--color-text-muted);
}

.master-pick__rating {
  font-size: var(--text-caption);
  color: var(--color-accent);
}

.master-pick__chips {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-4);
}

.master-pick__why {
  font-size: var(--text-caption);
  color: var(--color-warning-text);
}

.master-pick__why:empty {
  display: none;
}

.master-pick--any {
  align-items: center;
}

@media (min-width: 600px) {
  .wizard__header,
  .wizard__body,
  .wizard__bar {
    padding-inline: var(--container-gutter-tablet);
  }

  /* С планшета итог и кнопки снова помещаются в одну строку. */
  .wizard__summary,
  .wizard__buttons {
    flex: 0 1 auto;
  }

  .wizard__buttons .btn:not(:only-child) {
    flex: none;
  }

  .service-list {
    grid-template-columns: 1fr 1fr;
    gap: var(--space-16);
  }

  .master-list {
    grid-template-columns: 1fr 1fr;
    gap: var(--space-16);
  }
}

@media (min-width: 1024px) {
  .wizard__header,
  .wizard__body,
  .wizard__bar {
    padding-inline: var(--space-48);
  }

  .wizard__body {
    padding-top: var(--space-32);
  }

  .master-list {
    grid-template-columns: repeat(3, 1fr);
  }
}

/* ==========================================================================
   ШАГ 3: КАЛЕНДАРЬ И СЛОТЫ
   ========================================================================== */

.datetime {
  display: grid;
  gap: var(--space-32);
}

/* Спутник к `display: grid`: календарь переноса прячут, когда запись не открылась. */
.datetime[hidden] {
  display: none;
}

.calendar-month__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-8);
  margin-bottom: var(--space-16);
}

.calendar-month__title {
  font-size: var(--text-h4);
  font-weight: var(--font-weight-bold);
}

.calendar-month__weekdays,
.calendar-month__grid {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  gap: var(--space-4);
}

.calendar-month__weekdays {
  margin-bottom: var(--space-8);
  font-size: var(--text-caption);
  color: var(--color-text-muted);
  text-align: center;
}

/* Клетка дня. Состояние — в data-state, чтобы оно читалось в разметке. */
.day {
  aspect-ratio: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--color-bg-surface);
  border: 1px solid var(--color-border-divider);
  border-radius: var(--radius-sm);
  color: var(--color-text-primary);
  font-family: var(--font-text);
  font-size: var(--text-body-s);
  font-weight: var(--font-weight-semibold);
  cursor: pointer;
}

.day[data-state='free'] {
  background: var(--slot-available-bg);
  border-color: var(--slot-available-border);
}

/* Много свободного времени — как в прототипе, отдельной пометкой. */
.day[data-density='many'] {
  border-color: var(--color-accent);
}

.day[data-state='busy'] {
  background: var(--slot-booked-bg);
  border-color: var(--slot-booked-border);
  color: var(--slot-booked-text);
  cursor: default;
}

.day[data-state='past'] {
  background: var(--slot-past-bg);
  border: var(--slot-past-border);
  color: var(--slot-past-text);
  cursor: default;
}

.day[data-state='loading'] {
  background: var(--color-bg-card);
  color: transparent;
  cursor: default;
}

/* Двойной класс намеренно: одиночный проигрывает по весу правилам
   `.day[data-state=…]` выше, и выбранный день оставался бы неотличимым. */
.day.day--today {
  box-shadow: inset 0 0 0 1px var(--color-info);
}

.day.day--selected {
  background: var(--slot-selected-bg);
  border-color: var(--slot-selected-bg);
  color: var(--slot-selected-text);
  font-weight: var(--font-weight-bold);
}

.legend {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-8) var(--space-16);
  margin: var(--space-16) 0 0;
  padding: 0;
  list-style: none;
  font-size: var(--text-caption);
  color: var(--color-text-muted);
}

.legend__item {
  display: flex;
  align-items: center;
  gap: var(--space-8);
}

.legend__mark {
  width: var(--space-12);
  height: var(--space-12);
  flex: none;
  border-radius: var(--radius-button-components);
  border: 1px solid var(--color-border-divider);
}

.legend__mark--selected { background: var(--slot-selected-bg); border-color: var(--slot-selected-bg); }
.legend__mark--today { box-shadow: inset 0 0 0 1px var(--color-info); }
.legend__mark--free { background: var(--slot-available-bg); border-color: var(--color-accent); }
.legend__mark--busy { background: var(--slot-booked-bg); border-color: var(--slot-booked-border); }
.legend__mark--past { background: var(--slot-past-bg); border: var(--slot-past-border); }

/* --- Слоты дня ------------------------------------------------------------- */

.slots__day {
  margin-bottom: var(--space-16);
  font-size: var(--text-h4);
  font-weight: var(--font-weight-bold);
}

.slots__loading {
  display: flex;
  align-items: center;
  gap: var(--space-8);
  padding: var(--space-24);
  background: var(--color-bg-surface);
  border-radius: var(--radius-sm);
  font-size: var(--text-body-s);
  color: var(--color-text-secondary);
}

.slots__loading[hidden] {
  display: none;
}

.spinner--muted {
  border-color: var(--color-border-control);
  border-top-color: var(--color-accent);
}

.slot-group {
  margin-bottom: var(--space-16);
}

.slot-group__title {
  margin-bottom: var(--space-8);
  font-size: var(--text-label);
  font-weight: var(--text-label-weight);
  letter-spacing: var(--text-label-tracking);
  text-transform: uppercase;
  color: var(--color-text-muted);
}

/* Узкий экран: четыре колонки дают кнопку в ~60px — попасть пальцем тяжело,
   да и «17:30» в ней впритык. Две колонки, с планшета — снова четыре. */
.slot-group__grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: var(--space-8);
}

.slot-pick {
  padding: var(--space-12) var(--space-4);
  text-align: center;
  border-radius: var(--radius-sm);
  font-family: var(--font-text);
  font-size: var(--text-body-s);
  font-weight: var(--font-weight-semibold);
  cursor: pointer;
}

.slot-pick[data-state='available'] {
  background: var(--slot-available-bg);
  border: 1px solid var(--slot-available-border);
  color: var(--slot-available-text);
}

/* Занятое время видно и неактивно — не спрятано. */
.slot-pick[data-state='busy'] {
  background: var(--slot-booked-bg);
  border: 1px solid var(--slot-booked-border);
  color: var(--slot-booked-text);
  font-weight: var(--font-weight-regular);
  cursor: default;
}

.slot-pick[data-state='selected'] {
  background: var(--slot-selected-bg);
  border: 1px solid var(--slot-selected-bg);
  color: var(--slot-selected-text);
  font-weight: var(--font-weight-bold);
  box-shadow: var(--slot-selected-shadow);
}

.slots__end {
  margin: var(--space-16) 0;
  font-size: var(--text-body-m);
  font-weight: var(--font-weight-semibold);
  color: var(--color-text-primary);
}

.slots__end[hidden] {
  display: none;
}

/* Пустой день: состояние той же страницы. */
.slots__empty {
  padding: var(--space-32) var(--space-16);
  text-align: center;
  background: var(--color-bg-surface);
  border-radius: var(--radius-sm);
}

.slots__empty[hidden] {
  display: none;
}

.slots__empty-icon {
  color: var(--color-text-disabled);
  margin-bottom: var(--space-12);
}

.slots__empty-title {
  margin: 0 0 var(--space-8);
  font-size: var(--text-body-m);
  font-weight: var(--font-weight-semibold);
}

.slots__empty-note {
  margin: 0 0 var(--space-16);
  font-size: var(--text-body-s);
  color: var(--color-text-muted);
}

.slots__empty-dates {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-8);
  justify-content: center;
}

.slots__empty-hints {
  display: flex;
  flex-direction: column;
  gap: var(--space-8);
  margin: var(--space-16) 0 0;
  padding: 0;
  list-style: none;
  font-size: var(--text-body-s);
}

@media (min-width: 600px) {
  .slot-group__grid {
    grid-template-columns: repeat(4, 1fr);
  }
}

@media (min-width: 1024px) {
  .datetime {
    grid-template-columns: 1fr 1fr;
    gap: var(--space-48);
  }
}

/* ==========================================================================
   ШАГ 4: ПОДТВЕРЖДЕНИЕ И ЭКРАН УСПЕХА
   ========================================================================== */

/* Таймер удержания в шапке. Меньше минуты — другой вид, как в прототипе. */
.hold-timer {
  display: flex;
  align-items: center;
  gap: var(--space-8);
  margin-left: auto;
  margin-right: var(--space-16);
  padding: var(--space-8) var(--space-12);
  border-radius: var(--radius-sm);
  font-size: var(--text-body-s);
  font-weight: var(--font-weight-semibold);
  color: var(--color-text-muted);
}

.hold-timer[hidden] {
  display: none;
}

.hold-timer[data-state='urgent'] {
  background: var(--color-bg-card);
  color: var(--color-danger-text);
}

.hold-timer[data-state='urgent'] .icon {
  color: var(--color-danger);
}

/* Заметное сообщение об отказе: «слот заняли», «бронь истекла», «дубль». */
.alert {
  margin-bottom: var(--space-24);
  padding: var(--space-16);
  background: var(--color-bg-card);
  border-left: var(--space-4) solid var(--color-danger);
  border-radius: var(--radius-sm);
  color: var(--color-danger-text);
}

.alert[hidden] {
  display: none;
}

.alert__head {
  display: flex;
  align-items: flex-start;
  gap: var(--space-8);
  font-size: var(--text-body-m);
  line-height: var(--text-body-m-line-height);
}

.alert__suggestions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-8);
  margin-top: var(--space-12);
}

.alert__suggestions:empty {
  display: none;
}

.alert__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-16);
  margin-top: var(--space-12);
  font-size: var(--text-body-s);
}

.alert__actions:empty {
  display: none;
}

.confirm {
  display: grid;
  gap: var(--space-32);
}

.confirm__subtitle {
  margin-bottom: var(--space-16);
  font-size: var(--text-h4);
  font-weight: var(--font-weight-bold);
}

.confirm__client {
  margin: 0 0 var(--space-24);
}

.confirm__row {
  display: flex;
  justify-content: space-between;
  gap: var(--space-16);
  padding-block: var(--space-8);
  border-bottom: 1px solid var(--color-border-divider);
  font-size: var(--text-body-s);
}

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

.confirm__row dd {
  margin: 0;
  text-align: right;
}

.confirm__note {
  margin: var(--space-12) 0 0;
  font-size: var(--text-caption);
  color: var(--color-text-muted);
}

/* Сводка визита. */
.summary {
  padding: var(--space-24);
  background: var(--color-bg-surface);
  border-radius: var(--radius-sm);
  align-self: start;
}

.summary__block {
  margin-bottom: var(--space-24);
}

.summary__label {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-8);
  margin-bottom: var(--space-8);
  font-size: var(--text-label);
  font-weight: var(--text-label-weight);
  letter-spacing: var(--text-label-tracking);
  text-transform: uppercase;
  color: var(--color-text-muted);
}

.summary__services {
  display: flex;
  flex-direction: column;
  gap: var(--space-8);
  margin: 0;
  padding: 0;
  list-style: none;
}

.summary__service {
  display: flex;
  justify-content: space-between;
  gap: var(--space-16);
  font-size: var(--text-body-s);
}

.summary__address {
  margin-top: var(--space-4);
  font-size: var(--text-caption);
  color: var(--color-text-muted);
}

.summary__total {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-16);
  padding-top: var(--space-16);
  border-top: 1px solid var(--color-border-divider);
  font-size: var(--text-body-m);
}

.summary__total strong {
  font-size: var(--text-h4);
}

/* --- Экран успеха ---------------------------------------------------------- */

.success {
  max-width: 480px;
  margin-inline: auto;
  text-align: center;
}

.success__mark {
  display: flex;
  align-items: center;
  justify-content: center;
  width: var(--space-64);
  height: var(--space-64);
  margin: 0 auto var(--space-16);
  border-radius: var(--radius-full);
  background: var(--color-accent-subtle);
  color: var(--color-success);
}

.success__title {
  margin-bottom: var(--space-8);
}

.success__lead {
  margin-bottom: var(--space-24);
  font-size: var(--text-body-s);
  color: var(--color-text-muted);
}

.success__card {
  padding: var(--space-24);
  margin-bottom: var(--space-24);
  background: var(--color-bg-surface);
  border-radius: var(--radius-sm);
  text-align: left;
}

.success__reminders {
  margin-bottom: var(--space-24);
  text-align: left;
}

.success__actions {
  display: flex;
  flex-direction: column;
  gap: var(--space-8);
}

.success__note {
  margin-top: var(--space-16);
  font-size: var(--text-caption);
  color: var(--color-text-muted);
}

@media (min-width: 1024px) {
  .confirm {
    grid-template-columns: 1fr 380px;
    gap: var(--space-48);
    align-items: start;
  }

  .success__actions {
    flex-direction: row;
    justify-content: center;
  }
}

/* ==========================================================================
   КАБИНЕТ: СПИСОК ЗАПИСЕЙ, ДЕТАЛИ, ПЕРЕНОС
   ========================================================================== */

.page-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-16);
  flex-wrap: wrap;
  margin-bottom: var(--space-24);
}

/* Спутник к `display: flex`: заголовок карточки записи прячут вместе с ней,
   когда запись не открылась. */
.page-head[hidden] {
  display: none;
}

.breadcrumbs {
  display: flex;
  align-items: center;
  gap: var(--space-8);
  margin-bottom: var(--space-16);
  font-size: var(--text-body-s);
  color: var(--color-text-muted);
}

/* Табы «Предстоящие» / «История». */
.tabs--underline {
  gap: var(--space-24);
  margin-bottom: var(--space-24);
  border-bottom: 1px solid var(--color-border-divider);
}

.tabs--underline .tabs__tab {
  padding: 0 0 var(--space-12);
  background: transparent;
  color: var(--color-text-secondary);
  border-bottom: 2px solid transparent;
}

.tabs--underline .tabs__tab--active {
  background: transparent;
  color: var(--color-text-primary);
  border-bottom-color: var(--color-accent);
}

.booking-list {
  display: flex;
  flex-direction: column;
  gap: var(--space-12);
}

/* Три колонки — дата, состав визита, статус с кнопками — это строка таблицы.
   На узком экране на них остаётся ~230px: состав переносится по одному слову,
   а кнопки наезжают на него. Поэтому здесь строка разворачивается в карточку —
   статус и кнопки уходят на отдельный ряд под составом. С планшета всё
   возвращается в исходную строку. */
.booking-card {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-16);
  align-items: flex-start;
  min-height: var(--space-96);
  padding: var(--space-16);
  background: var(--color-bg-card);
  border: 1px solid var(--color-border-divider);
  border-radius: var(--radius-sm);
}

/* Ближайший визит выделен: до него человеку есть дело прямо сейчас. */
.booking-card--featured {
  border-color: var(--color-accent);
  box-shadow: var(--shadow-card);
}

.booking-card__date {
  display: flex;
  flex-direction: column;
  align-items: center;
  width: var(--space-48);
  flex: none;
  color: inherit;
}

.booking-card__day {
  font-family: var(--font-headline);
  font-size: var(--text-h3-mobile);
  font-weight: var(--font-weight-extrabold);
  line-height: 1;
}

.booking-card__month {
  font-size: var(--text-caption);
  text-transform: uppercase;
  color: var(--color-text-muted);
}

.booking-card__body {
  flex: 1;
  min-width: 0;
}

.booking-card__services {
  display: block;
  font-size: var(--text-body-m);
  font-weight: var(--font-weight-bold);
  color: inherit;
}

.booking-card__meta {
  margin-top: var(--space-4);
  font-size: var(--text-body-s);
  color: var(--color-text-secondary);
}

.booking-card__note {
  margin-top: var(--space-8);
  font-size: var(--text-caption);
  color: var(--color-warning-text);
}

.booking-card__note:empty {
  display: none;
}

.booking-card__side {
  display: flex;
  flex: 1 0 100%;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-8) var(--space-12);
}

.booking-card__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-8);
}

@media (min-width: 600px) {
  .booking-card {
    flex-wrap: nowrap;
  }

  .booking-card__side {
    flex: 0 1 auto;
    flex-direction: column;
    align-items: flex-end;
    gap: var(--space-12);
  }

  .booking-card__actions {
    justify-content: flex-end;
  }
}

.badge--muted {
  background: var(--color-bg-surface);
  color: var(--color-text-muted);
}

.badge--danger {
  background: var(--color-bg-surface);
  color: var(--color-danger-text);
}

/* Пустой кабинет и пустая история — состояния списка. */
.empty-state {
  padding: var(--space-48) var(--space-16);
  text-align: center;
  background: var(--color-bg-surface);
  border-radius: var(--radius-sm);
}

.empty-state[hidden] {
  display: none;
}

.empty-state__icon {
  color: var(--color-text-muted);
  margin-bottom: var(--space-12);
}

.empty-state__title {
  margin: 0 0 var(--space-8);
  font-size: var(--text-h4);
  font-weight: var(--font-weight-bold);
}

.empty-state__note {
  margin: 0 0 var(--space-24);
  font-size: var(--text-body-s);
  color: var(--color-text-muted);
}

/* --- Детали записи --------------------------------------------------------- */

.detail {
  display: grid;
  gap: var(--space-24);
}

/* Спутник к `display: grid` выше (см. .form-field[hidden]). Карточку прячут,
   когда запись не открылась — чужая или не найдена, — и без этой строки под
   текстом отказа оставался пустой шаблон «Состав визита» с прочерками. */
.detail[hidden] {
  display: none;
}

.detail__main {
  padding: var(--space-24);
  background: var(--color-bg-card);
  border-radius: var(--radius-sm);
}

.detail__side {
  align-self: start;
}

.detail__actions {
  display: flex;
  flex-direction: column;
  gap: var(--space-8);
}

.detail__comment {
  margin-bottom: var(--space-16);
  padding: var(--space-16);
  background: var(--color-bg-surface);
  border-radius: var(--radius-sm);
  font-size: var(--text-body-s);
}

.detail__comment p {
  margin: 0;
}

/* --- Перенос --------------------------------------------------------------- */

.reschedule-bar {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-8) var(--space-24);
  margin-bottom: var(--space-24);
  padding: var(--space-12) var(--space-16);
  background: var(--color-bg-surface);
  border-radius: var(--radius-sm);
  font-size: var(--text-body-s);
}

/* Спутники к `display` — форму переноса прячут, когда запись не открылась. */
.reschedule-bar[hidden] {
  display: none;
}

.reschedule-actions {
  display: flex;
  gap: var(--space-8);
  margin-top: var(--space-24);
}

.reschedule-actions[hidden] {
  display: none;
}

/* Текущее время записи: обведено, но не выбирается. */
.slot-pick[data-state='current'] {
  background: var(--slot-my-booking-bg);
  border: 1px solid var(--slot-my-booking-border);
  color: var(--slot-my-booking-text);
  cursor: default;
}

.day.day--current {
  box-shadow: inset 0 0 0 1px var(--color-accent);
}

.legend__mark--current {
  background: var(--slot-my-booking-bg);
  border-color: var(--slot-my-booking-border);
}

@media (min-width: 1024px) {
  .detail {
    grid-template-columns: 1fr 280px;
    gap: var(--space-48);
  }
}

/* ==========================================================================
   МОДАЛКА И ТОСТ
   ========================================================================== */

.modal {
  position: fixed;
  inset: 0;
  z-index: 30;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--space-16);
}

.modal[hidden] {
  display: none;
}

/* Затемнение под модалкой. Токена полупрозрачной подложки в системе нет —
   взят фон страницы тёмной темы с прозрачностью. */
.modal__backdrop {
  position: absolute;
  inset: 0;
  background: var(--palette-dark-base);
  opacity: 0.6;
}

/* Окно не выходит за экран: всё, что выше него, прокручивается внутри окна.
   Нижний отступ отдан кнопкам (см. .modal__actions) — иначе при прокрутке они
   упирались бы в самый край. На большом экране высота до предела не доходит,
   и прокрутки не возникает. */
.modal__window {
  position: relative;
  width: 100%;
  max-width: 380px;
  max-height: calc(100vh - var(--space-32));
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: var(--space-24) var(--space-24) 0;
  /* Модалка тёмная и на светлом экране — так в прототипе. */
  background: var(--palette-dark-card);
  color: var(--palette-text-dark-primary);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow-modal);
}

@supports (max-height: 100dvh) {
  .modal__window {
    max-height: calc(100dvh - var(--space-32));
  }
}

.modal__title {
  margin-bottom: var(--space-12);
  font-size: var(--text-h4);
  font-weight: var(--font-weight-bold);
}

.modal__text {
  margin-bottom: var(--space-16);
  font-size: var(--text-body-s);
  line-height: var(--text-body-s-line-height);
  color: var(--palette-text-dark-secondary);
}

/* Кнопки прилипают к низу окна и остаются видны, сколько бы текста ни было
   выше: содержимое уезжает под них. Нижний отступ здесь — тот самый, что снят
   с .modal__window, поэтому без прокрутки вид не меняется. */
.modal__actions {
  position: sticky;
  bottom: 0;
  display: flex;
  gap: var(--space-8);
  /* Отступ сверху отыгран отрицательным полем: без прокрутки расстояние до
     кнопок прежнее, а при прокрутке над ними остаётся непрозрачная полоска
     и текст не подъезжает вплотную. */
  margin-top: calc(var(--space-8) * -1);
  padding-top: var(--space-8);
  padding-bottom: var(--space-24);
  background: var(--palette-dark-card);
}

.modal__actions .btn {
  flex: 1;
}

/* Внутри тёмной модалки поля и вторичная кнопка остаются тёмными, даже когда
   страница светлая: перекрываем светлые правила выше. */
[data-theme='light'] .modal__window .field {
  background: var(--field-bg);
  border-color: var(--field-border);
  color: var(--field-text);
}

[data-theme='light'] .modal__window .btn--secondary {
  color: var(--button-secondary-text);
  border-color: var(--button-secondary-border);
}

[data-theme='light'] .modal__window .btn--secondary:hover {
  background: var(--button-secondary-bg-hover);
}

[data-theme='light'] .modal__window .form-message {
  background: var(--palette-dark-surface);
  color: var(--palette-text-dark-secondary);
}

[data-theme='light'] .modal__window .form-label {
  color: var(--palette-text-dark-secondary);
}

.toast {
  position: fixed;
  left: 50%;
  bottom: var(--space-24);
  transform: translateX(-50%);
  z-index: 40;
  display: flex;
  align-items: center;
  gap: var(--space-8);
  padding: var(--space-12) var(--space-16);
  background: var(--palette-dark-card);
  color: var(--palette-text-dark-primary);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow-modal);
  font-size: var(--text-body-s);
}

.toast[hidden] {
  display: none;
}

.toast .icon {
  color: var(--color-success);
}

@media (min-width: 1024px) {
  .profile {
    grid-template-columns: 220px 1fr;
    gap: var(--space-48);
    padding-block: var(--space-32);
  }

  .profile__menu {
    flex-direction: column;
    align-self: start;
    overflow: visible;
  }

  .profile__grid {
    grid-template-columns: 1fr 1fr;
    gap: var(--space-24);
  }

  .profile__actions {
    grid-column: 1 / -1;
  }
}

/* ==========================================================================
   РАЗДЕЛ АДМИНИСТРАТОРА
   Каркас страниц /admin/*: шапка, боковое меню, рабочая область.
   Собирается в js/admin-shell.js.

   ЦВЕТА. В прототипе админские экраны тёмные: полотно #221E28, сайдбар #1C1922.
   Второго значения в дизайн-системе нет вовсе, а подбирать похожее нельзя
   (правило 4). При этом tokens.css сам называет светлую тему темой «рабочих
   экранов: кабинет клиента и админка», а роль --palette-light-surface подписана
   «строки таблиц, боковая навигация» — то есть боковое меню в светлой теме
   системой предусмотрено. Поэтому раздел собран как кабинет: светлое полотно,
   тёмная липкая шапка, меню на --color-bg-surface. Структура — из прототипа,
   цвета — из токенов.

   ШАПКА берёт классы .account-header: это тот же компонент, а не похожий.
   Отличий два — широкая полоса без ограничения по --container-max (рабочая
   область админки шире витрины) и подпись роли рядом с логотипом.
   ========================================================================== */

/* Место под шапку занято до того, как модуль её соберёт: страница не прыгает. */
[data-admin-shell] {
  min-height: var(--space-64);
}

/* Рабочая область админки не сужается до витринного контейнера: таблице записей
   на 8 колонок ширины --container-max не хватит. */
.account-header__bar--wide {
  max-width: none;
}

.admin-role {
  padding: var(--space-4) var(--space-8);
  border-radius: var(--radius-full);
  background: var(--color-accent-subtle);
  color: var(--color-accent);
  font-size: var(--text-caption);
  font-weight: var(--font-weight-bold);
  letter-spacing: var(--text-caption-tracking);
}

/* На телефоне колонок нет: меню уезжает под шапку и раскрывается бургером. */
.admin-layout {
  display: block;
}

.admin-nav {
  display: none;
  flex-direction: column;
  gap: var(--space-4);
  padding: var(--space-12) var(--container-gutter-mobile);
  background: var(--color-bg-surface);
  border-bottom: 1px solid var(--color-border-divider);
}

.admin-nav--open {
  display: flex;
}

.admin-nav__link {
  display: flex;
  align-items: center;
  gap: var(--space-12);
  min-height: var(--touch-target-min);
  padding: var(--space-8) var(--space-12);
  border-radius: var(--radius-sm);
  color: var(--color-text-secondary);
  font-size: var(--text-body-m);
  font-weight: var(--font-weight-semibold);
}

.admin-nav__link:hover {
  background: var(--color-bg-card);
  color: var(--color-text-primary);
}

/* Активный пункт — заливка и цвет, а не только цвет: различие не должно
   держаться на одном оттенке (Guidelines). */
.admin-nav__link--active,
.admin-nav__link--active:hover {
  background: var(--color-accent-selected);
  color: var(--color-accent-text);
  font-weight: var(--font-weight-bold);
}

/* Число нерешённых запросов у пункта «Запросы» — тот же кружок, что у
   колокольчика кабинета, но в строке меню, прижатый вправо. */
.admin-nav__count {
  margin-left: auto;
  min-width: var(--space-24);
  height: var(--space-24);
  padding-inline: var(--space-4);
  border-radius: var(--radius-full);
  background: var(--color-accent);
  color: var(--color-text-on-accent);
  font-size: var(--text-caption);
  font-weight: var(--font-weight-bold);
  line-height: var(--space-24);
  text-align: center;
}

.admin-nav__count[hidden] {
  display: none;
}

.admin-main {
  padding: var(--space-24) var(--container-gutter-mobile);
}

/* Место будущего содержимого. Не «заглушка загрузки»: страница не ждёт ответа
   сервера, ей пока нечего показывать, и честнее сказать это словами. */
.admin-placeholder {
  padding: var(--space-32);
  border: 1px dashed var(--color-border-field);
  border-radius: var(--radius-sm);
  background: var(--color-bg-card);
  color: var(--color-text-muted);
  font-size: var(--text-body-m);
  text-align: center;
}

/* С планшета меню становится колонкой и раскрывать его больше не нужно. */
@media (min-width: 600px) {
  /* Бургер убираем здесь, а не на 1024px, как в шапке кабинета: там он до
     самого десктопа раскрывает пункты, спрятанные в шапке, а здесь пункты уже
     стоят колонкой слева, и кнопке нечего открывать. */
  .account-header__bar--wide .account-header__burger {
    display: none;
  }

  .admin-layout {
    display: grid;
    /* Ширина колонки — из шкалы отступов: 96 + 96 + 48. Отдельного токена
       ширины боковой панели в дизайн-системе нет. */
    grid-template-columns: calc(var(--space-96) * 2 + var(--space-48)) 1fr;
    align-items: start;
  }

  .admin-nav {
    display: flex;
    position: sticky;
    /* Шапка липкая, и меню встаёт под неё, а не под верх окна. */
    top: var(--space-64);
    padding: var(--space-16) var(--space-12);
    border-bottom: none;
    border-right: 1px solid var(--color-border-divider);
    /* Меню занимает всю высоту от шапки до низа окна — как сайдбар прототипа. */
    min-height: calc(100vh - var(--space-64));
  }

  .admin-main {
    padding: var(--space-32) var(--container-gutter-tablet);
  }
}

@media (min-width: 1024px) {
  .admin-main {
    padding: var(--space-32) var(--container-gutter-desktop);
  }
}

/* ==========================================================================
   АДМИНКА: СПИСКИ УСЛУГ И МАСТЕРОВ
   Экраны Admin-Services и Admin-Masters. Строка списка у них одна и та же:
   слева название с пометками и подробностями, справа три действия.

   ПОМЕТКА ОТКЛЮЧЁННОГО. Приглушённая строка плюс слово «Отключена» бейджем.
   Одного цвета мало: различие статуса не должно держаться на оттенке
   (Guidelines, «Доступность»). Прозрачность взята не из воздуха — это тот же
   приём, что у занятого слота в тёмной теме (--slot-booked-*), но токенов под
   светлую рабочую область дизайн-система не даёт, поэтому здесь роль
   приглушённого текста и обычная поверхность.
   ========================================================================== */

.admin-actions {
  display: flex;
  align-items: center;
  gap: var(--space-8);
  flex-wrap: wrap;
}

/* Строка над списком: что именно администратор здесь видит и чего не видит
   клиент. Без неё «Отключена» читается как поломка, а не как состояние. */
.admin-hint {
  margin-bottom: var(--space-16);
  font-size: var(--text-body-s);
  line-height: var(--text-body-s-line-height);
  color: var(--color-text-muted);
}

.admin-card {
  margin-bottom: var(--space-16);
  padding: var(--space-16);
  background: var(--color-bg-card);
  border: 1px solid var(--color-border-divider);
  border-radius: var(--radius-sm);
}

/* Заглушка загрузки того же размера, что и будущая карточка: список не прыгает,
   когда приезжают данные. Мерцания нет — токенов длительности в системе нет. */
.admin-card.skeleton {
  min-height: var(--space-96);
  border-color: transparent;
}

.admin-card__head {
  display: flex;
  align-items: baseline;
  gap: var(--space-12);
  margin-bottom: var(--space-12);
}

.admin-card__count {
  font-size: var(--text-caption);
  color: var(--color-text-muted);
}

.admin-rows {
  display: flex;
  flex-direction: column;
  gap: var(--space-8);
}

.admin-row {
  display: flex;
  flex-direction: column;
  gap: var(--space-12);
  padding: var(--space-12);
  background: var(--color-bg-surface);
  border-radius: var(--radius-sm);
}

/* Карточка мастера — та же строка, но сама себе блок: у неё нет объемлющей
   категории, как у услуги. */
.admin-row.admin-card {
  background: var(--color-bg-card);
}

/* Отключённая строка приглушена целиком, кроме кнопок: убрать её из работы —
   не то же самое, что убрать с глаз, и вернуть в работу надо чем-то нажимать. */
.admin-row--off .admin-row__main {
  opacity: 0.6;
}

.admin-row__main {
  min-width: 0;
}

.admin-row__title {
  display: flex;
  align-items: center;
  gap: var(--space-8);
  flex-wrap: wrap;
  font-size: var(--text-h4-mobile);
  font-weight: var(--font-weight-semibold);
  color: var(--color-text-primary);
}

.admin-row__meta {
  margin-top: var(--space-4);
  font-size: var(--text-body-s);
  color: var(--color-text-secondary);
}

.admin-row__note {
  margin-top: var(--space-8);
  font-size: var(--text-body-s);
  line-height: var(--text-body-s-line-height);
  color: var(--color-text-muted);
}

/* «Никто не оказывает» — не ошибка запроса, а положение дел, которое стоит
   заметить: такая услуга включена, но записаться на неё не к кому. */
.admin-row__warning {
  margin-top: var(--space-4);
  font-size: var(--text-body-s);
  color: var(--color-warning-text);
}

.admin-row__actions {
  display: flex;
  gap: var(--space-8);
  flex-wrap: wrap;
}

/* «Было / Стало» у запроса на новый график (`Admin-Requests`): два столбца
   строк «Ср: 12:00–18:00», на телефоне — друг под другом. */
.request-diff {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(calc(var(--space-96) * 2), 1fr));
  gap: var(--space-12);
  margin-top: var(--space-12);
  font-size: var(--text-body-s);
}

.request-diff__title {
  margin-bottom: var(--space-4);
  font-weight: var(--font-weight-semibold);
  color: var(--color-text-secondary);
}

.request-diff__list {
  margin: 0;
  padding: 0;
  list-style: none;
  color: var(--color-text-primary);
}

.admin-avatar {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--space-32);
  height: var(--space-32);
  flex: none;
  border-radius: var(--radius-full);
  background: var(--color-accent-subtle);
  color: var(--color-accent-text);
  font-size: var(--text-caption);
  font-weight: var(--font-weight-bold);
  position: relative;
  overflow: hidden;
}

/* Фото поверх инициалов: не загрузилось — скрипт убирает картинку, и под ней
   остаются инициалы (js/admin-masters.js, avatarOf). */
.admin-avatar img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Услуги мастера чипами — тем же элементом, каким они подписаны на витрине. */
.admin-chips {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-4);
  margin-top: var(--space-8);
}

.chip--static {
  cursor: default;
}

/* Мастер услугу умеет, а услуга выключена: связь есть, записаться нельзя. */
.chip--off {
  background: var(--color-bg-surface);
  color: var(--color-text-muted);
}

/* --- Формы в модалках ------------------------------------------------------ */

.field--textarea {
  min-height: var(--space-64);
  /* Тянется только вниз: по ширине поле и так во всю форму, а горизонтальная
     полоса прокрутки у переносимого текста — артефакт, а не прокрутка. */
  resize: vertical;
  overflow-x: hidden;
}

/* Модалка услуги и мастера шире обычной: в ней форма, а не один вопрос. */
.modal__window--wide {
  max-width: 560px;
}

.admin-form-row {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-12);
}

/* Фото услуги: превью слева, адрес справа. Превью 4:3 — как карточка услуги на
   витрине, чтобы обрезка была видна такой же, какой её увидит клиент. */
.photo-field {
  display: flex;
  align-items: center;
  gap: var(--space-12);
}

/* Оценка в строке раздела «Отзывы». Акцент текстом — на светлом фоне сам акцент
   по контрасту не проходит, tokens.css даёт для этого --color-accent-text. */
.admin-row__stars {
  color: var(--color-accent-text);
}

.photo-field .field {
  flex: 1;
  min-width: 0;
}

.photo-field__preview {
  display: flex;
  flex: none;
  align-items: center;
  justify-content: center;
  width: var(--space-96);
  aspect-ratio: 4 / 3;
  overflow: hidden;
  /* Цвета поля ввода, а не темы страницы: превью стоит в одной строке с полем, а
     модалка тёмная и на светлых экранах админки. */
  background: var(--field-bg);
  border: 1px solid var(--field-border);
  border-radius: var(--field-radius);
  font-size: var(--text-caption);
  line-height: var(--text-caption-line-height);
  color: var(--color-text-muted); /* как у подсказки под полем */
  text-align: center;
}

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

/* Не открылось — клиент тоже не увидит; сказано словом, не только цветом. */
.photo-field__preview--broken {
  border-color: var(--field-border-error);
  color: var(--field-text-error);
}

.admin-fieldset {
  margin-bottom: var(--space-16);
  padding: 0;
  border: none;
}

.admin-fieldset__note {
  margin-bottom: var(--space-8);
  font-size: var(--text-body-s);
  color: var(--palette-text-dark-muted);
}

.admin-checks {
  display: flex;
  flex-direction: column;
  gap: var(--space-12);
  max-height: var(--space-96);
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: var(--space-12);
  background: var(--palette-dark-surface);
  border-radius: var(--radius-sm);
}

.admin-checks__group {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}

.admin-checks__title {
  font-size: var(--text-label);
  font-weight: var(--text-label-weight);
  letter-spacing: var(--text-label-tracking);
  text-transform: uppercase;
  color: var(--palette-text-dark-muted);
}

/* Отметка услуги. Зона нажатия — вся строка целиком, а не квадратик 18px:
   Guidelines требуют минимум 44px, и попадать надо в название, а не в значок. */
.admin-check {
  display: flex;
  align-items: center;
  gap: var(--space-8);
  min-height: var(--touch-target-min);
  cursor: pointer;
}

.admin-check__input {
  position: absolute;
  width: 1px;
  height: 1px;
  opacity: 0;
}

.admin-check__box {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--icon-size-20);
  height: var(--icon-size-20);
  flex: none;
  border: 1px solid var(--color-border-control);
  border-radius: var(--radius-sm);
  background: var(--field-bg);
}

/* Галочка нарисована двумя гранями квадрата, повёрнутого на 45°: своей иконки
   у чекбокса в дизайн-системе нет, а тумблер здесь не подходит — отметок много
   и они про выбор из списка, а не про включение одной настройки. */
.admin-check__input:checked + .admin-check__box {
  background: var(--color-accent);
  border-color: var(--color-accent);
}

.admin-check__input:checked + .admin-check__box::after {
  content: '';
  width: var(--space-4);
  height: var(--space-8);
  margin-top: calc(var(--space-4) * -1);
  border: solid var(--color-text-on-accent);
  border-width: 0 2px 2px 0;
  transform: rotate(45deg);
}

.admin-check__input:focus-visible + .admin-check__box {
  box-shadow: var(--focus-ring);
}

/*
   Подпись отметки. Роль обычного текста, а не тёмной темы: список отметок
   появился в модалке, но живёт теперь и на светлой странице — рабочие часы у
   мастера. Вариант для модалки — правилом ниже, рядом с остальными её
   поправками.
*/
.admin-check__text {
  display: flex;
  align-items: center;
  gap: var(--space-8);
  font-size: var(--text-body-s);
  color: var(--color-text-primary);
}

/* Бейдж «отключена» внутри тёмной модалки: светлая поверхность здесь не читается. */
.modal__window .badge--muted {
  background: var(--palette-dark-base);
  color: var(--palette-text-dark-muted);
}

/* Переключатель вида блокировки внутри тёмной модалки. Роль вторичного текста в
   светлой теме — тёмно-серый, и на подложке модалки неактивный таб почти сливается
   с ней. Тот же случай и то же решение, что у подписи тумблера ниже. */
[data-theme='light'] .modal__window .tabs__tab {
  color: var(--palette-text-dark-secondary);
}

/* Подпись тумблера внутри тёмной модалки. Роль --color-text-primary в светлой
   теме — почти чёрный, и на подложке модалки он не читается вовсе. Тот же
   случай, что и у бейджа выше, и решается тем же: ролью текста тёмной темы.
   Заметно на всех модалках раздела — «Активна» у услуги, «Уведомить клиента»
   у записи. */
[data-theme='light'] .modal__window .switch__title {
  color: var(--palette-text-dark-primary);
}

/* Подпись отметки внутри тёмной модалки: на её подложке роль обычного текста
   светлой темы — почти чёрный, и он там не читается. */
[data-theme='light'] .modal__window .admin-check__text {
  color: var(--palette-text-dark-primary);
}

/* Рамка отметки внутри тёмной модалки. На светлой странице --color-border-control
   это почти чёрная полупрозрачность, и на тёмной подложке её не видно вовсе.
   Берём ту же границу, что уже стоит у полей внутри модалки. */
[data-theme='light'] .modal__window .admin-check__box {
  border-color: var(--field-border);
}

/* Редактор графика внутри тёмной модалки — окно «График» в «Мастерах». Сам
   редактор написан под светлую страницу мастера, и в модалке его подписи и
   разделители брали роли светлой темы: тёмно-серый текст на тёмной подложке.
   Решение то же, что у отметок выше, — роли текста тёмной темы. */
[data-theme='light'] .modal__window .break-row__day {
  color: var(--palette-text-dark-secondary);
}

[data-theme='light'] .modal__window .week-hours__off,
[data-theme='light'] .modal__window .week-hours__dash {
  color: var(--palette-text-dark-muted);
}

[data-theme='light'] .modal__window .week-hours__row,
[data-theme='light'] .modal__window .break-row {
  border-bottom-color: var(--field-border-disabled);
}

/* Список внутри плашки сообщения — записи, оказавшиеся вне нового графика. */
.form-message__list {
  margin: var(--space-8) 0 0;
  padding-left: var(--space-16);
}

@media (min-width: 600px) {
  /* С планшета строка раскладывается в две колонки: слева содержимое, справа
     действия. На телефоне они идут друг под другом — три кнопки в ряд с
     названием не помещаются. */
  .admin-row {
    flex-direction: row;
    align-items: flex-start;
    justify-content: space-between;
    gap: var(--space-16);
    padding: var(--space-16);
  }

  .admin-row__actions {
    flex: none;
    flex-wrap: nowrap;
  }

  .admin-form-row {
    grid-template-columns: repeat(3, 1fr);
  }

  .admin-checks {
    max-height: calc(var(--space-96) * 2);
  }
}

/* ==========================================================================
   АДМИНКА: ЗАПИСИ
   Экран Admin-Bookings-Table. Список записей выбранного дня, переключение дат,
   фильтр по мастеру и заблокированное время.

   СТРОКА ЗАПИСИ — ТА ЖЕ .admin-row, что у услуг и мастеров: слева содержимое,
   справа действия. Отличий два, и оба про состояние записи, а не про оформление.

   ОТМЕНЁННАЯ ПРИГЛУШЕНА, НО НЕ СПРЯТАНА. Отмена — это статус, а не удаление:
   строка остаётся на своём месте, бейджем сказано, кто отменил, и подстрокой —
   почему. Тот же приём, что у отключённой услуги (.admin-row--off): цвет плюс
   слово, потому что различие состояния не должно держаться на одном оттенке
   (Guidelines, «Доступность»).

   НАЛОЖЕНИЕ ОТМЕЧЕНО ПОЛОСОЙ СЛЕВА. Две записи на одно время у одного мастера —
   не поломка, а осознанное решение администратора, и в списке это должно быть
   видно сразу, до чтения строк. Полоса взята цветом предупреждения; своего
   токена под «строку с пометкой» в системе нет.
   ========================================================================== */

/* --- День и фильтр --------------------------------------------------------- */

.day-bar {
  display: flex;
  align-items: center;
  gap: var(--space-12);
  flex-wrap: wrap;
  margin-bottom: var(--space-8);
}

.day-bar__dates {
  display: flex;
  align-items: center;
  gap: var(--space-8);
}

/* Стрелка «день назад» и «день вперёд»: квадратная кнопка под один знак. */
.day-bar__step {
  min-width: var(--touch-target-min);
  padding-left: var(--space-8);
  padding-right: var(--space-8);
}

/* Поле даты не тянется на всю строку: рядом с ним стоят стрелки и «Сегодня». */
.day-bar__date {
  width: auto;
  min-width: var(--space-96);
}

/* На телефоне фильтр встаёт отдельной строкой во всю ширину: прижатый вправо
   выпадающий список рядом со стрелками читается как их продолжение. */
/* Переключатель периода — тот же компонент табов, что и в модалках. Собственный
   отступ ему здесь не нужен: расстояния задаёт сама строка. */
.day-bar__period {
  margin-bottom: 0;
}

.day-bar__filter {
  width: 100%;
}

/* Дата словами и итоги дня. Часовой пояс назван здесь же: время в списке
   салонное, и администратор в другом городе должен это видеть, а не выяснять. */
.day-bar__caption {
  margin-bottom: var(--space-16);
  font-size: var(--text-body-s);
  line-height: var(--text-body-s-line-height);
  color: var(--color-text-secondary);
}

/* --- День внутри периода --------------------------------------------------- */

/*
   Заголовок дня в списке длиннее одного дня. Без него «10:00» из среды и «10:00»
   из пятницы стоят рядом и неразличимы: у строки записи своей даты нет — только
   время, потому что в дневном режиме дата и так в шапке.
*/
.day-group__title {
  display: flex;
  align-items: baseline;
  gap: var(--space-8);
  margin: var(--space-24) 0 var(--space-8);
  font-size: var(--text-h4-mobile);
  font-weight: var(--font-weight-bold);
  color: var(--color-text-primary);
}

/* Первый заголовок стоит сразу под подсказкой — второй отступ подряд не нужен. */
.day-group__title:first-child {
  margin-top: 0;
}

.day-group__count {
  font-size: var(--text-caption);
  font-weight: var(--font-weight-regular);
  color: var(--color-text-muted);
}

/* --- Строка записи --------------------------------------------------------- */

/* Время визита — первое, что читают в строке, поэтому оно крупнее имени. */
.admin-row__time {
  font-size: var(--text-h4-mobile);
  font-weight: var(--font-weight-bold);
  color: var(--color-text-primary);
}

/* Отменённая: приглушена целиком, кроме действий, — как отключённая услуга. */
.admin-row--cancelled .admin-row__main {
  opacity: 0.6;
}

/* Два визита на одно время у одного мастера. */
.admin-row--overlap {
  border-left: var(--space-4) solid var(--color-warning);
}

/* Заблокированное время. Не запись: ни клиента, ни услуг у неё нет, и строка
   должна отличаться от записи с первого взгляда. */
.admin-row--blocked {
  border-left: var(--space-4) solid var(--color-info);
}

/* --- Подсказка под полем --------------------------------------------------- */

.form-field__hint {
  margin: 0;
  font-size: var(--text-caption);
  line-height: var(--text-body-s-line-height);
  color: var(--color-text-muted);
}

.form-field__hint[hidden] {
  display: none;
}

/* --- Подбор клиента -------------------------------------------------------- */

/*
   Выпадающий список совпадений под полем поиска. Не `position: absolute`:
   модалка прокручивается, и всплывающий слой внутри неё уехал бы от поля. Здесь
   он просто стоит следующим блоком и ограничен по высоте.
*/
.suggest {
  display: flex;
  flex-direction: column;
  max-height: var(--space-96);
  overflow-y: auto;
  overscroll-behavior: contain;
  background: var(--palette-dark-surface);
  border-radius: var(--radius-sm);
}

.suggest[hidden] {
  display: none;
}

.suggest__item {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
  min-height: var(--touch-target-min);
  padding: var(--space-8) var(--space-12);
  background: transparent;
  border: none;
  border-radius: var(--radius-none);
  color: var(--palette-text-dark-primary);
  font-family: var(--font-text);
  font-size: var(--text-body-s);
  text-align: left;
  cursor: pointer;
}

.suggest__item:hover,
.suggest__item:focus-visible {
  background: var(--palette-dark-card);
}

.suggest__item span {
  color: var(--palette-text-dark-muted);
  font-size: var(--text-caption);
}

/* «Никого не нашли» — тот же список, но нажимать не на что. */
.suggest__empty {
  padding: var(--space-8) var(--space-12);
  color: var(--palette-text-dark-muted);
  font-size: var(--text-body-s);
}

@media (min-width: 600px) {
  /* С планшета строка записи раскладывается в две колонки — как строки услуг
     и мастеров: правило для .admin-row ниже общее, здесь добавлять нечего. */
  .day-bar__date {
    min-width: auto;
  }

  .day-bar__filter {
    width: auto;
    margin-left: auto;
  }
}

/* ==========================================================================
   РАЗДЕЛ МАСТЕРА
   Экраны Master-Today и Master-Visit-Detail.

   БЕЗ БОКОВОГО МЕНЮ. У администратора шесть разделов и сайдбар; у мастера пока
   один экран и карточка визита, и колонка навигации была бы пустой рамкой вокруг
   содержимого. Поэтому раскладка одноколоночная, с ограниченной шириной: день
   мастера читается сверху вниз, а не разбирается по колонкам, — так он и
   нарисован в прототипе, где основной фрейм мобильный.
   ========================================================================== */

.master-main {
  max-width: var(--container-max);
  margin: 0 auto;
  padding: var(--space-24) var(--container-gutter-mobile);
}

/* Переключатель «Мастер / Администратор» в шапке: не навигация, а смена контура,
   поэтому пара кнопок с отмеченной текущей, а не пункт в общем ряду. */
.master-header__switch {
  margin-bottom: 0;
}

/* --- Карточка визита в ленте дня ------------------------------------------- */

/* Строка списка, но кликается целиком: у мастера действий в ленте нет, есть
   переход в карточку. Поэтому ссылка, а не блок с кнопкой внутри. */
.visit-card {
  display: block;
  color: inherit;
}

.visit-card:hover {
  border-color: var(--color-border-control);
}

/*
   Визит, который идёт прямо сейчас. Выделен рамкой и заливкой акцента, как в
   прототипе: это единственная карточка дня, до которой мастеру есть дело в
   данную минуту, и находиться она должна взглядом, а не чтением времени.
*/
.visit-card--now {
  border-color: var(--color-accent);
  background: var(--color-accent-subtle);
}

/* --- Карточка визита целиком ------------------------------------------------ */

/*
   Блоки внутри карточки визита. `.detail__main` уже даёт поверхность и отступы,
   поэтому внутренние блоки — не карточки, а разделённые линией части: карточка
   в карточке добавила бы вторую рамку вокруг каждого абзаца.
*/
.visit-detail {
  display: flex;
  flex-direction: column;
  gap: var(--space-24);
}

.visit-block + .visit-block {
  padding-top: var(--space-24);
  border-top: 1px solid var(--color-border-divider);
}

/* Телефон и кнопка «Показать» в одну строку: кнопка относится к номеру рядом,
   а не к блоку клиента целиком. */
.visit-phone {
  display: flex;
  align-items: center;
  gap: var(--space-12);
  flex-wrap: wrap;
  margin-top: var(--space-12);
  margin-bottom: var(--space-4);
  font-size: var(--text-body-m);
  color: var(--color-text-primary);
}

/* Действия столбцом и во всю ширину: их два-три, и каждое — решение, которое
   принимают отдельно. Ряд одинаковых кнопок подталкивал бы нажать ближайшую. */
.visit-actions {
  display: flex;
  flex-direction: column;
  gap: var(--space-8);
}

.visit-actions:empty {
  display: none;
}

@media (min-width: 600px) {
  .master-main {
    padding: var(--space-32) var(--container-gutter-tablet);
  }
}

@media (min-width: 1024px) {
  .master-main {
    padding: var(--space-32) var(--container-gutter-desktop);
  }
}

/* --- Полоса недели (Master-Schedule) ---------------------------------------- */

/*
   Семь дней в ряд с выбором. Не календарь: у мастера неделя — это навигация по
   дню, а не сетка визитов. Своего компонента под такую полосу в дизайн-системе
   нет, поэтому она собрана из тех же ролей, что и остальные кнопки выбора.
*/
.week-strip {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  gap: var(--space-4);
  margin-bottom: var(--space-16);
}

.week-day {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-4);
  min-height: var(--touch-target-min);
  padding: var(--space-8) var(--space-4);
  background: var(--color-bg-card);
  border: 1px solid var(--color-border-divider);
  border-radius: var(--radius-sm);
  color: var(--color-text-secondary);
  font-family: var(--font-text);
  cursor: pointer;
}

/* Сегодня отмечено рамкой, выбранный день — заливкой. Это разные вещи, и
   совпадают они не всегда: мастер листает недели вперёд. */
.week-day--today {
  border-color: var(--color-accent);
}

.week-day--active,
.week-day--active.week-day--today {
  background: var(--color-accent);
  border-color: var(--color-accent);
  color: var(--color-text-on-accent);
}

.week-day__name {
  font-size: var(--text-caption);
  text-transform: uppercase;
  letter-spacing: var(--text-caption-tracking);
}

.week-day__number {
  font-size: var(--text-body-m);
  font-weight: var(--font-weight-bold);
}

/* Точка под числом: в этот день есть заблокированное время. Только она —
   визиты пометить нечем, их лента приходит по одному дню за запрос. */
.week-day__mark {
  width: var(--space-4);
  height: var(--space-4);
  border-radius: var(--radius-full);
  background: currentColor;
}

/* --- Рабочие часы и перерывы (Master-Schedule-Profile) ---------------------- */

.week-hours {
  display: flex;
  flex-direction: column;
  gap: var(--space-8);
}

.week-hours__row {
  display: flex;
  align-items: center;
  gap: var(--space-12);
  flex-wrap: wrap;
  padding: var(--space-8) 0;
  border-bottom: 1px solid var(--color-border-divider);
}

.week-hours__row:last-child {
  border-bottom: none;
}

/* Название дня занимает всю свободную ширину, часы прижаты вправо: так семь
   строк читаются столбцом, а не лесенкой. */
.week-hours__row .admin-check {
  flex: 1;
  min-width: var(--space-96);
}

.week-hours__times {
  display: flex;
  align-items: center;
  gap: var(--space-8);
}

.week-hours__times[hidden] {
  display: none;
}

.week-hours__time {
  width: auto;
}

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

.week-hours__off {
  color: var(--color-text-muted);
  font-size: var(--text-body-s);
}

.week-hours__off[hidden] {
  display: none;
}

.break-row {
  display: flex;
  align-items: center;
  gap: var(--space-12);
  flex-wrap: wrap;
  padding: var(--space-12) 0;
  border-bottom: 1px solid var(--color-border-divider);
}

.break-row__days {
  display: flex;
  gap: var(--space-8);
  flex-wrap: wrap;
}

/* Отметка дня недели у перерыва: квадратик и две буквы. Зона нажатия — вся
   пара, как и у отметки услуги. */
.break-row__day {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  min-height: var(--touch-target-min);
  font-size: var(--text-body-s);
  color: var(--color-text-secondary);
  cursor: pointer;
}

/* --- Галерея работ ----------------------------------------------------------

   Своё имя, а не `.gallery`: так называется галерея на лендинге, и одинаковые
   классы перебивали друг друга — сетка лендинга становилась десятком плиток по
   96px, а здесь первая работа растягивалась на 2×2. */

.portfolio-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(var(--space-96), 1fr));
  gap: var(--space-12);
  margin-bottom: var(--space-16);
}

.portfolio-grid__item {
  display: flex;
  flex-direction: column;
  gap: var(--space-8);
  margin: 0;
}

.portfolio-grid__image {
  width: 100%;
  aspect-ratio: 1;
  object-fit: cover;
  border-radius: var(--radius-sm);
  background: var(--color-bg-surface);
}

.portfolio-grid__actions {
  display: flex;
  gap: var(--space-4);
  flex-wrap: wrap;
}

/* ==========================================================================
   ОЦЕНКА ЗВЁЗДАМИ
   Окно «Отзыв о визите» в «Моих записях». Пять радиокнопок, спрятанных для
   глаз, но не для клавиатуры и экранного диктора; видны подписи-звёзды.
   Стрелки переключают оценку, как у любой группы радиокнопок.
   ========================================================================== */

.rating-input {
  margin: 0;
  padding: 0;
  border: none;
}

.rating-input__stars {
  display: flex;
  gap: var(--space-4);
}

.rating-input__star {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--touch-target-min);
  height: var(--touch-target-min);
  border-radius: var(--radius-sm);
  font-size: var(--text-h3);
  line-height: 1;
  color: var(--color-text-disabled);
  cursor: pointer;
}

.rating-input__star--on {
  color: var(--color-accent);
}

/* Фокус на спрятанной радиокнопке виден на её звезде. */
.rating-input input:focus-visible + .rating-input__star {
  box-shadow: var(--focus-ring);
}

/* ==========================================================================
   ПОЛИТИКА ОБРАБОТКИ ДАННЫХ
   Экран System-Screens, фрейм «Политика». Оглавление слева, текст справа, как в
   прототипе. На телефоне оглавление стоит над текстом и не прилипает: рядом с
   текстом ему нет места, а приколотое сверху оно закрыло бы полэкрана.
   ========================================================================== */

.policy {
  display: grid;
  gap: var(--space-32);
  padding-block: var(--space-32) var(--space-64);
}

.policy__toc {
  display: flex;
  flex-direction: column;
  padding-bottom: var(--space-16);
  border-bottom: 1px solid var(--color-border-divider);
}

.policy__toc a {
  padding-block: var(--space-8);
  font-size: var(--text-body-s);
  color: var(--color-text-secondary);
}

.policy__toc a:hover {
  color: var(--color-text-primary);
}

/* Раздел, до которого дочитали, — его отмечает js/privacy-policy.js. */
.policy__toc a[aria-current='location'] {
  color: var(--color-accent-text);
  font-weight: var(--font-weight-bold);
}

.policy__text {
  min-width: 0;
  max-width: 600px;
}

.policy__title {
  margin-bottom: var(--space-8);
}

.policy__updated {
  margin-bottom: var(--space-32);
}

.policy__section + .policy__section {
  margin-top: var(--space-32);
}

.policy__heading {
  margin-bottom: var(--space-12);
}

.policy__section > * + * {
  margin-top: var(--space-12);
}

.policy__section > .policy__heading + * {
  margin-top: 0;
}

.policy__list {
  display: flex;
  flex-direction: column;
  gap: var(--space-8);
  margin-bottom: 0;
  padding-left: var(--space-24);
}

.policy__list li::marker {
  color: var(--color-accent);
}

.policy__contacts {
  display: grid;
  gap: var(--space-16);
  margin-inline: 0;
  margin-bottom: 0;
  padding: var(--space-16);
  background: var(--color-bg-surface);
  border-radius: var(--radius-sm);
}

.policy__contact {
  display: grid;
  gap: var(--space-4);
}

.policy__contact dd {
  margin: 0;
  color: var(--color-text-primary);
}

@media (min-width: 1024px) {
  .policy {
    grid-template-columns: 220px minmax(0, 1fr);
    gap: var(--space-48);
    padding-block: var(--space-48) var(--space-96);
  }

  /* Прилипает под шапкой — на той же высоте, куда переход по якорю ставит
     заголовок раздела (scroll-padding-top у html). */
  .policy__toc {
    position: sticky;
    top: var(--space-96);
    align-self: start;
    padding-bottom: 0;
    border-bottom: none;
  }
}

/* ==========================================================================
   ЧЕМ ЭТО РАСХОДИТСЯ С ПРОТОТИПОМ И ПОЧЕМУ
   (мимо tokens.css в файле только затемнения над фото, см. п. 1)
   ==========================================================================

   1. Полупрозрачные подложки. В прототипе шапка, затемнение над фото в hero и
      баннере, подложка синей плашки — это rgba поверх фона. Токенов
      прозрачности дизайн-система не даёт, поэтому взяты сплошные поверхности:
      шапка — --color-bg-page, плашка — --color-bg-card с полосой --color-info.
      Исключение — затемнения в hero и промо-баннере: под ними лежат
      фотографии, и сплошной поверхностью их не накрыть, не спрятав совсем.
      Там стоят ровно те rgba, что в прототипе (#221E28 разной плотности в
      hero, #151318 в баннере). Появится токен прозрачности — заменить на него;
      это единственные два места, где значение взято не из tokens.css.
   2. Фото. Штриховка на месте снимков была заглушкой прототипа («фото: салон,
      работа мастера», «фото: интерьер салона») и повторяла её же: собиралась
      из --color-bg-card и --color-bg-surface, потому что второго оттенка
      прототипа (#332E3B) в системе нет. Теперь в hero и промо-баннере стоят
      настоящие фотографии (web/img/photo_1.jpg, photo_2.jpg), а штриховка
      убрана; в остальных блоках заглушка осталась.
   3. #948C9D — самый частый серый прототипа. tokens.css прямо говорит, что это
      оформление страниц дизайн-системы, а не токен продукта; роль приглушённого
      текста закреплена за --color-text-muted, он и стоит везде.
   4. Кегли. 58, 34, 19, 16, 14.5, 13.5, 12.5, 11.5, 10.5px в шкале Typography
      отсутствуют. Каждый заменён ролью: заголовок hero — display, заголовки
      секций — h2, названия карточек — h4, тексты — body-m/body-s, подписи —
      caption. Кегли кнопок — из --button-text-*.
   5. Отступы. 20, 28, 40, 56, 80, 120px не из шкалы 8px. Взяты соседние шаги
      шкалы: 24, 32, 48, 64, 96. Высота строки галереи собрана сложением шагов.
   6. Радиусы. Кнопки — --radius-none (Grid); в библиотеке компонентов у них
      2px, это расхождение внутри самой системы, отмеченное в tokens.css.
      --radius-button-components использован один раз — под таб «Неделя», где в
      прототипе стоит ровно 2px.
   7. Тени. Карточке быстрого подбора нужна заметная тень (в прототипе
      0 20px 50px): из двух имеющихся ближе --shadow-modal.
   8. Зона нажатия. Кнопки прототипа ниже 44px (например, 9px 14px у мелких).
      Guidelines требуют минимум 44px, поэтому у .btn стоит
      min-height: var(--touch-target-min) — визуально кнопка чуть выше.
   9. Заглушки загрузки не мерцают: токенов длительности и кривой анимации в
      дизайн-системе нет вовсе.
  10. Раздел администратора. В прототипе его экраны тёмные (полотно #221E28,
      сайдбар #1C1922); второго значения в системе нет. Взята светлая тема —
      tokens.css сам называет её темой «кабинета клиента и админки», а роль
      боковой навигации закреплена за --palette-light-surface. Структура
      (шапка, сайдбар, рабочая область) — из прототипа, цвета — из токенов.
  11. Списки услуг и мастеров. Прототип показывает их таблицей и карточками с
      наведением; ни того, ни другого дизайн-система не описывает. Собрано из
      имеющегося: поверхность --color-bg-card под блок, --color-bg-surface под
      строку, бейджи статусов из .badge--success / --warning / --muted.
      Отключённая строка приглушена прозрачностью 0.6 — токена под «выключенное
      состояние» в светлой теме нет (в тёмной есть --slot-booked-*), поэтому
      рядом с ней всегда стоит слово, а не только цвет.
  12. Ghost-кнопка на светлом фоне («Отключить» / «Включить» в списках админки).
      Её цвет в токенах — значение тёмной темы, на светлом не читается; сама
      система признаёт этот случай неописанным. Взяты роли текста, как уже
      сделано для вторичной кнопки.
  13. Чекбокс. В библиотеке компонентов показаны тумблер и радио, отдельного
      чекбокса со списком нет. Галочка нарисована двумя гранями повёрнутого
      квадрата — 2px обводки, толщина линии, а не радиус и не отступ.
  14. Числами записаны только то, чего дизайн-система не описывает в принципе:
      толщина границы 1px (в системе есть цвета границ, но не толщина),
      брейкпоинты в @media и предельные ширины отдельных блоков, перенесённые
      из прототипа как есть, — карточка быстрого подбора 380px, колонка текста
      600px и 480px, столбец FAQ 760px, поле финального CTA 320px, карточка
      входа 440px, широкая модалка админки 560px, оглавление политики 220px.
      Ширина основного контейнера взята из --container-max.
      Отдельно — 2px у кольца кнопки в состоянии отправки: это толщина обводки,
      а не радиус и не отступ, роли под неё в системе тоже нет.
  15. Экран «Записи»: полоса слева у строки с наложением и у заблокированного
      времени. Роли «строка с пометкой» в системе нет — взяты цвета
      предупреждения и информации, толщина из шкалы отступов (--space-4), как
      уже сделано у .form-message.
  16. Выпадающий список совпадений при подборе клиента. Своего компонента
      «автодополнение» в системе нет; взяты поверхности тёмной модалки, внутри
      которой этот список и живёт.
*/
