/* ═══════════════════════════════════════════════════════════════════════
   «РАЗВОРОТ» — третья витрина.

   ЧЕМ ОТЛИЧАЕТСЯ. «Карточки» — сетка плиток, всё за нажатием. «История» —
   обложка и коллаж. Здесь интерфейсом работает ТИПОГРАФИКА: крупное имя во
   весь экран, цены бегут строкой, панели меняют цвет целиком. Похоже не на
   приложение, а на журнальный разворот, и в этом весь смысл: салон
   листают, а не «пользуются» им.

   ГЛАВНОЕ ПРАВИЛО. Ничего не спрятано за нажатием. Услуги с ценами,
   мастера, часы, адрес — всё лежит в ленте и видно листанием. Нажатие
   остаётся одно и по делу: записаться.

   ШРИФТ. Helvetica стоит первой: на технике Apple человек увидит именно
   её, как и просили. Лицензии на веб-файл у нас нет, поэтому всем
   остальным подставляется Onest — она уже в проекте, у неё честная
   кириллица и близкий гротескный характер. Замена на лицензионный файл —
   одна строка ниже.
   ═══════════════════════════════════════════════════════════════════════ */

:root {
  --face: "Helvetica Neue", Helvetica, "Onest", system-ui, -apple-system, sans-serif;
  --text: "Onest", "Helvetica Neue", system-ui, sans-serif;

  --paper: #efeee9;
  --ink: #111114;
  --soft: #6b6b74;
  --line: rgb(17 17 20 / .12);
  --accent: #111114;      /* перекрывается цветом бизнеса */
  --card: #1b1b20;        /* цвет карточек — настраивается владельцем */

  --pad: clamp(20px, 5.5vw, 64px);
  --ease: cubic-bezier(.16, 1, .3, 1);
}

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

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

/* ФОН СТОИТ И СВЕТИТСЯ.
   Свечение — отдельным слоем на псевдоэлементе, а не в фоне тела: тело
   прокручивается вместе с лентой, и свет уезжал бы вниз. Здесь он
   закреплён и остаётся там, где ему положено. */
body::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: -2;
  background:
    radial-gradient(120% 80% at 50% 108%,
      var(--glow, #fff) 0%, transparent 62%),
    var(--paper);
}

/* СВОЁ ФОТО. Поверх обязательно пелена: чужую фотографию мы не выбирали
   и её яркость предсказать нельзя, а текст на ней читать придётся. Без
   пелены светлый снимок съедает подписи целиком. */
body.own::before {
  background:
    linear-gradient(180deg, rgb(8 8 12 / .48), rgb(8 8 12 / .72)),
    var(--shot) center / cover no-repeat,
    var(--paper);
}

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--text);
  font-size: 16px;
  line-height: 1.45;
  -webkit-font-smoothing: antialiased;
  overscroll-behavior-y: none;
}

/* Пока не пришли данные, лента не мигает пустыми блоками. Показываем её
   одним движением, когда есть что показывать. */
body.boot .reel { opacity: 0; }
.reel { opacity: 1; transition: opacity .5s var(--ease); }

/* ── РАЗВОРОТ ─────────────────────────────────────────────────────────
   ВЫСОТА ПО СОДЕРЖИМОМУ, А НЕ ПО ЭКРАНУ.

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

   Теперь высоту задаёт содержимое, а воздух — отступами. Полная высота
   осталась там, где она и есть содержание: обложка с именем салона и
   утверждение во весь экран. Эти два и держат ритм, ради которого версия
   делалась, а остальные больше не изображают его пустотой. */
.spread {
  position: relative;
  padding: clamp(48px, 11vh, 104px) var(--pad);
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 14px;
}

/* Обложка и утверждение — во весь экран. Первая прижата к низу: имя
   салона там и есть всё содержание, и воздух над ним работает. */
.spread.cover,
.spread.claim {
  min-height: 100svh;
  padding-top: calc(var(--pad) + env(safe-area-inset-top, 0px));
}
.spread.cover { justify-content: flex-end; }

/* Хвост уступает место якорю: кнопка висит поверх ленты, и без запаса
   она накрыла бы последнюю строку. */
.spread.tail { padding-bottom: calc(var(--pad) + 96px); }

/* СНИМОК НА ОБЛОЖКЕ.
   Во всю полосу и под текстом. Пелена снизу вверх, а не ровная: имя стоит
   внизу, и затемнять ради него верх снимка значит портить сам снимок.
   Плотность подобрана под белый текст на светлой фотографии — под ней
   читается и она, и лицо на карточке. */
.spread.cover.shot { color: #F7F7F9; }
.covershot {
  position: absolute;
  inset: 0;
  z-index: -1;
  background: var(--cover) center / cover no-repeat;
}
.covershot::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg,
    rgb(8 8 12 / .20) 0%,
    rgb(8 8 12 / .38) 46%,
    rgb(8 8 12 / .78) 100%);
}
/* Подписи на снимке светлее обычного: на затемнённой фотографии серый
   цвета --soft уходит в фон. */
.spread.cover.shot .kicker,
.spread.cover.shot .lede,
.spread.cover.shot .hours,
.spread.cover.shot .hint { color: rgb(247 247 249 / .82); }

/* ── ТИПОГРАФИКА ──────────────────────────────────────────────────────
   Размер не «крупный», а предельный: имя салона обязано занять экран,
   иначе разворот превращается в обычную страницу с большим заголовком. */
.huge {
  margin: 0;
  font-family: var(--face);
  font-weight: 700;
  font-size: clamp(44px, 15vw, 132px);
  line-height: .92;
  letter-spacing: -.045em;
  text-wrap: balance;
}

.stat {
  margin: 0;
  font-family: var(--face);
  font-weight: 700;
  font-size: clamp(32px, 9.5vw, 88px);
  line-height: 1.02;
  letter-spacing: -.035em;
  text-wrap: balance;
}
.stat.small { font-size: clamp(30px, 8vw, 64px); }

.head {
  margin: 0 0 6px;
  font-family: var(--face);
  font-weight: 700;
  font-size: clamp(20px, 4.6vw, 30px);
  letter-spacing: -.02em;
}

.kicker {
  margin: 0;
  font-size: 12px;
  font-weight: 600;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--soft);
}

.lede { margin: 0; font-size: clamp(15px, 3.6vw, 19px); color: var(--soft); }
.hours { margin: 0; font-size: 14px; color: var(--soft); }
.fine { margin: 4px 0 0; font-size: 13px; color: var(--soft); }

/* Подсказка «листайте» — единственная инструкция на всей витрине.
   Уходит, как только человек тронул ленту: дальше она врёт. */
.hint {
  position: absolute;
  left: var(--pad);
  bottom: calc(var(--pad) + 40px);
  font-size: 12px;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--soft);
  transition: opacity .4s var(--ease);
}
.hint.gone { opacity: 0; }

/* ── БЕГУЩИЕ СТРОКИ С ЦЕНАМИ ──────────────────────────────────────────
   Две строки идут в разные стороны и с разной скоростью. Одинаковые
   поехали бы как один блок — весь смысл в расхождении.

   Строки декоративны и помечены aria-hidden: под ними лежит обычный
   список, по которому и ходят голосом и клавиатурой. */
.marquee {
  overflow: hidden;
  white-space: nowrap;
  font-family: var(--face);
  font-weight: 700;
  font-size: clamp(30px, 8vw, 72px);
  letter-spacing: -.03em;
  line-height: 1.12;
  mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent);
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent);
}
/* ДВИЖЕНИЕ СТРОКИ ВЕДЁТ СКРИПТ, А НЕ КЛЮЧЕВЫЕ КАДРЫ.
   Здесь стояла CSS-анимация, а скорость менялась через
   animation-duration на каждом событии прокрутки. Браузер при смене
   длительности пересчитывает анимацию с начала — строка дёргалась и
   разгонялась рывками. Ровно это и было видно.

   Скрипт двигает её сам, добавляя к спокойному ходу небольшую прибавку
   от пальца. Прибавка затухает, разрывов нет по устройству. */
.marquee span {
  display: inline-block;
  padding-right: .6em;
  will-change: transform;
}
.marquee.back { color: transparent; -webkit-text-stroke: 1px var(--line); }

/* Тот же прайс списком: он и есть настоящий, а строки — его лицо. */
.plain { list-style: none; margin: 18px 0 0; padding: 0; display: grid; gap: 2px; }
.plain li { border-top: 1px solid var(--line); }
.plain button {
  width: 100%;
  appearance: none;
  border: 0;
  background: none;
  font: inherit;
  color: inherit;
  text-align: left;
  padding: 13px 0;
  display: flex;
  align-items: baseline;
  gap: 12px;
  cursor: pointer;
}
.plain .nm { flex: 1; font-size: 16px; }
.plain .pr { font-family: var(--face); font-weight: 700; font-size: 16px; white-space: nowrap; }
.plain .du { font-size: 13px; color: var(--soft); white-space: nowrap; }

/* ── ЦВЕТНАЯ ПАНЕЛЬ ───────────────────────────────────────────────────
   Смена цвета во весь экран — это смена главы. Заголовок мелким шрифтом
   такого не делает: его пролистывают, не заметив. */
.claim {
  background: var(--accent);
  color: #fff;
  justify-content: center;
}
/* Цвет не выбран — подложки нет вовсе. Утверждение остаётся крупным и
   лежит на той же бумаге: чёрная плита посреди светлой ленты выглядит
   ошибкой, а не главой. */
body.no-accent .claim { background: none; color: var(--ink); }
body.no-accent .claim::before { display: none; }
/* На своём фото панель не закрашивает снимок целиком: человек выбрал
   фотографию, а мы бы её спрятали. Оставляем стекло. */
body.own .claim {
  background: color-mix(in srgb, var(--accent) 62%, transparent);
  backdrop-filter: blur(14px) saturate(1.4);
  -webkit-backdrop-filter: blur(14px) saturate(1.4);
}
.claim .stat { max-width: 16ch; }

/* ── КАРТОЧКИ ─────────────────────────────────────────────────────────
   Стекло с градиентом на тёмной подложке. Цвет задаёт владелец, поэтому
   всё построено на одной переменной. */
.deck { display: grid; gap: 12px; grid-template-columns: 1fr; }
@media (min-width: 640px) { .deck { grid-template-columns: 1fr 1fr; } }

.tile {
  position: relative;
  border-radius: 26px;
  padding: 18px;
  min-height: 132px;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  gap: 4px;
  color: #fff;
  overflow: hidden;
  background:
    radial-gradient(120% 100% at 20% 0%, rgb(255 255 255 / .22), transparent 60%),
    linear-gradient(160deg,
      color-mix(in srgb, var(--card) 82%, #fff 18%),
      var(--card));
  box-shadow: 0 18px 40px rgb(17 17 20 / .16), inset 0 1px 0 rgb(255 255 255 / .16);
}
.tile .nm { font-family: var(--face); font-weight: 700; font-size: 19px; letter-spacing: -.02em; }
.tile .role { font-size: 13px; color: rgb(255 255 255 / .72); }
.tile .ph {
  position: absolute; inset: 0; z-index: -1;
  background-size: cover; background-position: center;
  opacity: .55;
}

/* ── ЛЕНТА РАБОТ ──────────────────────────────────────────────────────
   Горизонтальная, с прилипанием: палец ведёт, снимок встаёт на место. */
.strip {
  display: flex;
  gap: 10px;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  margin: 0 calc(var(--pad) * -1);
  padding: 0 var(--pad);
  scrollbar-width: none;
}
.strip::-webkit-scrollbar { display: none; }
.strip .shot {
  flex: none;
  margin: 0;
  width: min(74vw, 320px);
  scroll-snap-align: center;
  display: grid;
  gap: 10px;
}
.strip .shot button {
  appearance: none; border: 0; padding: 0; background: none;
  cursor: pointer; display: block; border-radius: 22px; overflow: hidden;
  transition: transform .18s var(--ease);
}
.strip .shot button:active { transform: scale(.985); }
.strip img {
  width: 100%;
  aspect-ratio: 4 / 5;
  object-fit: cover;
  display: block;
  background: color-mix(in srgb, var(--ink) 6%, transparent);
}
/* ДЕМО ГОЛОСОМ. Проигрыватель системный намеренно: свой рисовать значит
   переучивать человека нажимать «играть», а выигрыш — только внешний. Он
   лишь встаёт в ширину полосы и в её цвет. */
.demobox { display: grid; gap: 12px; justify-items: start; }
.demobox audio { width: 100%; max-width: 460px; }

/* МЕСТО БЕЗ СНИМКА. У товара фотографии может не быть вовсе, и пустая
   дыра в полосе читается как поломка. Здесь стоит спокойная плашка в цвете
   витрины: видно, что это карточка, а не сбой. Пропорция та же, что у
   снимка: иначе соседние карточки в полосе встанут разной высоты. */
.strip .noshot {
  display: block;
  width: 100%;
  aspect-ratio: 4 / 5;
  background:
    linear-gradient(150deg,
      color-mix(in srgb, var(--ink) 12%, transparent),
      color-mix(in srgb, var(--ink) 4%, transparent));
}

/* ПОДПИСЬ. Без неё это просто красивые картинки: человек смотрит и не
   понимает, что перед ним и сколько стоит. */
.strip figcaption {
  display: flex; align-items: baseline; gap: 10px;
  padding: 0 2px;
}
.strip figcaption .nm { flex: 1; font-size: 15px; line-height: 1.25; }
.strip figcaption .pr {
  font-family: var(--face); font-weight: 700; font-size: 15px; white-space: nowrap;
}

/* ── ФАКТЫ ────────────────────────────────────────────────────────────
   Адрес и телефон текстом, а не за иконкой: их читают, а не нажимают. */
.factlist { display: grid; gap: 0; }
.factlist a, .factlist div {
  display: flex; align-items: baseline; gap: 12px;
  padding: 14px 0; border-top: 1px solid var(--line);
  color: inherit; text-decoration: none;
}
.factlist .k { width: 8.5em; flex: none; font-size: 13px; color: var(--soft); }
.factlist .v { font-size: 16px; }

/* ── КНОПКИ ───────────────────────────────────────────────────────────
   Форма одна на всю витрину: пилюля. Разница только в весе. */
.pill {
  appearance: none; border: 0; cursor: pointer;
  font: inherit; font-weight: 600; font-size: 16px;
  padding: 15px 22px; border-radius: 999px;
  background: var(--ink); color: var(--paper);
  min-height: 48px;
  transition: transform .15s var(--ease), opacity .15s var(--ease);
}
.pill:active { transform: scale(.975); }
.pill.wide { width: 100%; }
.pill.ghost { background: transparent; color: var(--ink); box-shadow: inset 0 0 0 1px var(--line); }
.tailrow { display: grid; gap: 8px; margin-top: 18px; }

/* СВОИ ЗАПИСИ В ХВОСТЕ. Строкой на визит, кнопка справа: одной фразой
   через точку было видно, что записан, и нельзя было ничего сделать. */
.mineline {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; padding: 11px 0;
  border-bottom: 1px solid var(--line);
}
.mineline b { font-weight: 600; font-size: 15px; }
.mineline .drop {
  appearance: none; border: 0; cursor: pointer; font: inherit;
  flex: none; font-size: 13.5px; padding: 7px 13px; border-radius: 999px;
  background: transparent; color: var(--soft);
  box-shadow: inset 0 0 0 1px var(--line);
  transition: color .16s var(--ease), box-shadow .16s var(--ease);
}
/* Опасность появляется на втором нажатии, вместе с вопросом: красная
   кнопка в списке своих визитов пугала бы каждый раз, когда человек
   просто зашёл проверить время. */
.mineline .drop[data-sure="1"] { color: #c2410c; box-shadow: inset 0 0 0 1.5px #c2410c; }
.mineline .drop:disabled { opacity: .5; cursor: default; }

/* ── ЯКОРЬ ────────────────────────────────────────────────────────────
   Одна кнопка, всегда на виду. Появляется, когда обложка ушла: на первом
   экране она спорила бы с именем салона. */
.anchor {
  position: fixed; left: 0; right: 0;
  bottom: calc(env(safe-area-inset-bottom, 0px) + 14px);
  padding: 0 var(--pad);
  z-index: 40;
  transform: translate3d(0, 120%, 0);
  transition: transform .4s var(--ease);
}
.anchor.on { transform: none; }
/* НАРОЧНОЕ ИСКЛЮЧЕНИЕ из общего `[hidden] { display: none }` в конце
   файла: якорь не исчезает, а уезжает вниз — появление и уход должны быть
   движением, а не миганием. Переживает общее правило по весу, а не по
   порядку: два селектора против одного. */
.anchor[hidden] { display: block; }

/* ── ЛИСТ ─────────────────────────────────────────────────────────────
   Всё, что требует ответа, живёт поверх ленты. Лента не теряется:
   человек видит, куда вернётся. */
.sheet { position: fixed; inset: 0; z-index: 60; }
.sheet[hidden] { display: none; }
.veil {
  position: absolute; inset: 0;
  background: rgb(17 17 20 / .42);
  backdrop-filter: blur(3px); -webkit-backdrop-filter: blur(3px);
  opacity: 0; transition: opacity .3s var(--ease);
}
.sheet.on .veil { opacity: 1; }
.sheet .card {
  position: absolute; left: 0; right: 0; bottom: 0;
  /* ШИРИНА ОГРАНИЧЕНА. Без этого на планшете и в браузере поля тянулись
     во всю страницу: строка ввода в 1700 пикселей под словом «Имя» — не
     форма, а полоса. Витрину открывают и с ноутбука. */
  max-width: 560px; margin-inline: auto;
  max-height: 88svh; overflow-y: auto;
  background: var(--paper);
  border-radius: 28px 28px 0 0;
  padding: 22px var(--pad) calc(var(--pad) + env(safe-area-inset-bottom, 0px));
  transform: translate3d(0, 100%, 0);
  transition: transform .38s var(--ease);
}
.sheet.on .card { transform: none; }
.sheet-x {
  position: absolute; right: 14px; top: 14px;
  width: 36px; height: 36px; border-radius: 50%;
  border: 0; background: rgb(17 17 20 / .06); cursor: pointer;
  display: grid; place-items: center;
}
.sheet-x svg { width: 18px; height: 18px; fill: none; stroke: var(--ink); stroke-width: 1.8; }

/* ── ПОЯВЛЕНИЕ ────────────────────────────────────────────────────────
   ВИДНО ПО УМОЛЧАНИЮ. Прячет только класс, который ставит скрипт, и
   только тому, что сейчас за пределами экрана: нет скрипта или встали
   кадры — витрина просто видна. Правило выучено на прошлой версии, где
   заголовок оставался прозрачным во всю фоновую вкладку. */
.rise { will-change: auto; }
.rise.armed { opacity: 0; transform: translate3d(0, 26px, 0); }
.rise.in {
  opacity: 1; transform: none;
  transition: opacity .55s var(--ease), transform .55s var(--ease);
}
.rise.settled, .tile.rise.settled { opacity: 1; transform: none; transition: none; }

/* ── ТОСТ ─────────────────────────────────────────────────────────── */
.toast {
  position: fixed; left: 50%; bottom: calc(env(safe-area-inset-bottom, 0px) + 84px);
  transform: translate(-50%, 12px);
  padding: 11px 18px; border-radius: 999px;
  background: var(--ink); color: var(--paper);
  font-size: 14px; opacity: 0; pointer-events: none; z-index: 70;
  transition: opacity .25s var(--ease), transform .25s var(--ease);
}
.toast.on { opacity: 1; transform: translate(-50%, 0); }

/* ── ПОКОЙ ────────────────────────────────────────────────────────────
   Уменьшенное движение — не выключатель. Убираем то, что ездит большими
   площадями; оставляем цвет и отклик на касание. */
@media (prefers-reduced-motion: reduce) {
  .marquee span { transform: none !important; }
  .rise.armed { opacity: 1; transform: none; }
  .sheet .card, .anchor { transition: none; }
}

/* Мелкие пилюли для выбора внутри листа: мастер, день, время. */
.chips { display: flex; flex-wrap: wrap; gap: 8px; margin: 6px 0 16px; }
.pill.sm { padding: 9px 14px; font-size: 15px; min-height: 40px; }

/* ВРЕМЯ ВЪЕЗЖАЕТ СТРОКОЙ.
   Здесь всё движение горизонтальное — цены бегут лентой, работы едут
   полосой, буква за ними идёт с отставанием. Окна подчиняются тому же
   языку: приезжают слева по очереди, а не проявляются на месте. Так
   момент выбора времени принадлежит этой версии, а не выглядит вставкой
   из соседней.

   Сдвиг маленький и одинаковый для всех: большой размах на восьми окнах
   читается как рябь, а не как движение. */
@keyframes sp-slot-in {
  from { opacity: 0; transform: translate3d(-14px, 0, 0); }
  to   { opacity: 1; transform: none; }
}
.chips [data-iso],
.chips [data-free] {
  animation: sp-slot-in .3s var(--ease) both;
  animation-delay: var(--in, 0ms);
}
@media (prefers-reduced-motion: reduce) {
  .chips [data-iso], .chips [data-free] { animation: none; }
}
.pill.sm.on { background: var(--ink); color: var(--paper); box-shadow: none; }

/* Поля листа. Подпись сверху, а не плейсхолдером: плейсхолдер исчезает
   ровно тогда, когда человек начал печатать и забыл, о чём его спросили. */
/* ═════════════════════════════════════════════════════════════════════
   БИЛЕТ НА ПОДТВЕРЖДЕНИИ

   Здесь стоял один заголовок «13:00». Человек собирался отдать своё имя и
   телефон, не видя, на что именно записывается: ни услуги, ни мастера, ни
   адреса, ни числа.

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

   Линия снизу, а не рамка вокруг: язык этой версии — журнальный, здесь
   правит типографика и линейки, а не коробки. ═════════════════════════ */
.ticket {
  margin: 2px 0 20px;
  padding-bottom: 16px;
  border-bottom: 1px solid var(--line);
}
.ticket-what {
  margin: 0 0 6px;
  font-size: 14px;
  color: var(--soft);
  letter-spacing: -.01em;
}
.ticket-when {
  margin: 0;
  font-family: var(--face);
  font-weight: 700;
  font-size: clamp(28px, 8vw, 40px);
  line-height: 1.02;
  letter-spacing: -.035em;
}

/* Условие предоплаты видно до выбора времени и остаётся с человеком после
   записи. Цвета берём из темы витрины: у каждого бизнеса своя палитра. */
.booking-note,
.booking-state {
  margin: 12px 0 18px;
  padding: 12px 14px;
  border-radius: 16px;
  background: color-mix(in srgb, var(--ink) 7%, transparent);
  color: var(--soft);
  font-size: 14px;
  line-height: 1.42;
}
.booking-note b,
.booking-state b { color: var(--ink); }
.booking-state {
  display: grid;
  gap: 6px;
}
.booking-state.is-payment {
  background: color-mix(in srgb, var(--ink) 11%, transparent);
}
.copy-payment {
  justify-self: start;
  padding: 0;
  border: 0;
  background: none;
  color: var(--ink);
  font: inherit;
  font-weight: 700;
  text-decoration: underline;
  text-underline-offset: 3px;
  cursor: pointer;
}
.copy-payment:focus-visible { outline: 2px solid var(--ink); outline-offset: 3px; }

/* ЗВЁЗДЫ ОЦЕНКИ. Пять кнопок, а не список: одно движение вместо трёх.
   Здесь они в цвете витрины, а не белые — фон у этой версии светлый, и
   белая звезда на нём пропала бы. */
.rate { display: flex; gap: 8px; margin: 2px 0 16px; }
.rate button {
  appearance: none; border: 0; cursor: pointer; padding: 0;
  width: 48px; height: 48px; border-radius: 15px;
  background: color-mix(in srgb, var(--ink) 6%, transparent);
  transition: transform .15s var(--ease), background-color .15s var(--ease);
}
.rate button::after {
  content: ""; display: block;
  width: 24px; height: 24px; margin: auto;
  background: color-mix(in srgb, var(--ink) 26%, transparent);
  clip-path: polygon(50% 0%, 61% 35%, 98% 35%, 68% 57%, 79% 91%, 50% 70%, 21% 91%, 32% 57%, 2% 35%, 39% 35%);
}
.rate button.on::after { background: #E8A800; }
.rate button:active { transform: scale(.92); }
@media (prefers-reduced-motion: reduce) { .rate button { transition: none; } }

/* ШАГИ ПОДКЛЮЧЕНИЯ. Цифра крупная и своя у каждого шага: сплошной абзац
   «откройте, нажмите, поделитесь» читают по диагонали и делают из трёх
   действий одно. Линейки между шагами — язык этой версии: здесь правят
   типографика и линии, а не коробки. */
.steps {
  list-style: none;
  counter-reset: step;
  margin: 4px 0 18px;
  padding: 0;
}
.steps li {
  counter-increment: step;
  display: grid;
  grid-template-columns: 30px 1fr;
  align-items: baseline;
  gap: 4px 12px;
  padding: 12px 0;
  border-top: 1px solid var(--line);
}
.steps li:last-child { border-bottom: 1px solid var(--line); }
.steps li::before {
  content: counter(step);
  grid-row: span 2;
  font-family: var(--face);
  font-weight: 700;
  font-size: 22px;
  line-height: 1;
  color: color-mix(in srgb, var(--ink) 40%, transparent);
}
.steps b { font-size: 16px; font-weight: 600; }
.steps span { font-size: 13.5px; color: var(--soft); }

/* ПОЧТА ПОД ТИХОЙ ССЫЛКОЙ. Полем во всю ширину она стояла наравне с
   телефоном и читалась как ещё одно обязательное. */
.quietlink {
  appearance: none; border: 0; background: none; cursor: pointer;
  font: inherit; font-size: 14px; color: var(--soft);
  padding: 4px 0; margin: 0 0 14px;
  text-decoration: underline; text-underline-offset: 3px;
}
.quietlink[hidden] { display: none; }

.fld { display: block; margin: 0 0 14px; }
.fld span { display: block; font-size: 13px; color: var(--soft); margin-bottom: 6px; }
.fld input, .fld textarea {
  width: 100%; font: inherit; font-size: 16px; color: var(--ink);
  padding: 13px 14px; border-radius: 14px; border: 0;
  background: rgb(17 17 20 / .05);
  box-shadow: inset 0 0 0 1px var(--line);
}
.fld textarea { resize: vertical; min-height: 96px; }
.fld input:focus, .fld textarea:focus { outline: none; box-shadow: inset 0 0 0 2px var(--ink); }

/* ГАЛОЧКА — СВОЯ, А НЕ СИСТЕМНАЯ.
   Браузерный квадрат приходит своим размером, своим радиусом и своим
   чёрным цветом: на светлой витрине это три жирных пятна посреди
   аккуратной формы. Здесь она в цвете витрины и размером с текст. */
.agree { display: flex; gap: 12px; align-items: flex-start; margin: 0 0 14px;
  font-size: 14px; line-height: 1.35; cursor: pointer; }
.agree input {
  position: absolute; opacity: 0; width: 0; height: 0; pointer-events: none;
}
.agree .box {
  flex: none; width: 22px; height: 22px; margin-top: 1px;
  border-radius: 7px; position: relative;
  box-shadow: inset 0 0 0 1.5px var(--line);
  transition: background .16s var(--ease), box-shadow .16s var(--ease);
}
.agree .box::after {
  content: ""; position: absolute; inset: 0;
  background: no-repeat center / 12px 9px;
  /* Галка нарисована в самом правиле: отдельный файл ради двенадцати
     пикселей — это лишний запрос на каждой витрине. */
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 9'%3E%3Cpath d='M1 4.4 4.3 7.8 11 1' fill='none' stroke='%23fff' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  opacity: 0; transform: scale(.7);
  transition: opacity .16s var(--ease), transform .16s var(--ease);
}
.agree input:checked ~ .box {
  background: var(--ink); box-shadow: inset 0 0 0 1.5px var(--ink);
}
.agree input:checked ~ .box::after { opacity: 1; transform: none; }
/* Клавиатура должна видеть, где она стоит: без этого галочка недоступна
   всем, кто не пользуется мышью. */
.agree input:focus-visible ~ .box { box-shadow: inset 0 0 0 2px var(--ink); }
/* Согласие — то, что держит кнопку. Отделяем его от удобств выше. */
.agree.need { margin-top: 18px; padding-top: 16px; border-top: 1px solid var(--line); }
.agree a { color: inherit; }

@media (prefers-reduced-motion: reduce) {
  .agree .box, .agree .box::after { transition: none; }
}

/* ── ОТЗЫВЫ ───────────────────────────────────────────────────────────
   Словами, а не звёздочками: средний балл 4,8 одинаков у всех и его
   пролистывают, а фразу читают. Поэтому цитата крупная, а имя мелким. */
.quotes { display: grid; gap: 14px; }
.quote {
  margin: 0; padding: 18px 20px; border-radius: 22px;
  background: color-mix(in srgb, var(--ink) 5%, transparent);
  box-shadow: inset 0 0 0 1px var(--line);
}
.quote p {
  margin: 0;
  font-family: var(--face);
  font-weight: 700;
  font-size: clamp(17px, 4.4vw, 24px);
  line-height: 1.22;
  letter-spacing: -.02em;
  text-wrap: pretty;
}
.quote cite { display: block; margin-top: 10px; font-size: 13px; font-style: normal; color: var(--soft); }

/* Партнёр — та же карточка, но кликабельная: ссылка не должна выглядеть
   иначе, чем соседний блок, иначе полоса рассыпается. */
a.tile { text-decoration: none; }

/* КНОПКА НА КАРТОЧКЕ АБОНЕМЕНТА.
   Обычная .pill здесь пропала бы: она красится в цвет текста страницы, а
   карточка своя, тёмная. Поэтому светлая на плитке, а не «как везде». */
.packgo {
  appearance: none; border: 0; cursor: pointer;
  align-self: start;
  margin-top: 10px;
  font: inherit; font-weight: 600; font-size: 15px;
  padding: 11px 18px; border-radius: 999px;
  min-height: 42px;
  background: rgb(255 255 255 / .94);
  color: #14141a;
  transition: transform .15s var(--ease), opacity .15s var(--ease);
}
.packgo:active { transform: scale(.975); }
.packgo:disabled { opacity: .6; cursor: default; }

/* ── КАК ДОЙТИ ────────────────────────────────────────────────────────
   Прямой файл проигрываем на месте, вертикально: снято это телефоном, и
   растягивать на всю ширину значит показать две чёрные полосы. */
.walkbox video {
  width: 100%;
  max-height: 62svh;
  border-radius: 22px;
  background: color-mix(in srgb, var(--ink) 8%, transparent);
  display: block;
}

/* ═══ РЕЖИССУРА ═══════════════════════════════════════════════════════
   Появление здесь не «блок стал видимым», а поставленное движение. Три
   приёма, каждый со своей работой.

   ПЕРВЫЙ: СТРОКА ВЫХОДИТ ИЗ-ПОД КРАЯ. Не проявляется, а выезжает
   из-за невидимой линии — так, как строку набирают, а не показывают.
   Маска обязательна: без неё это обычный сдвиг, который видели все.

   Каждая строка своя, и они идут по очереди — глаз читает сверху вниз,
   и появление обязано идти туда же. Одновременное чтением не является.  */
.reveal { display: block; overflow: hidden; }
.reveal > i {
  display: block;
  font-style: inherit;
  transform: translate3d(0, 105%, 0);
  transition: transform .82s var(--ease);
  transition-delay: calc(var(--n, 0) * 90ms);
}
.armed .reveal > i, .reveal.armed > i { transform: translate3d(0, 105%, 0); }
.in .reveal > i, .reveal.in > i { transform: none; }
.settled .reveal > i, .reveal.settled > i { transform: none; transition: none; }

/* ВТОРОЙ: КАРТОЧКИ ПРИЕЗЖАЮТ ПО ОЧЕРЕДИ И С РАЗНЫХ СТОРОН.
   Одинаковый вход у всех читается перерисовкой экрана; чередование
   сторон — приходом предметов. Поворот крошечный: полтора градуса
   чувствуются, три уже выглядят небрежностью. */
.deck > .rise.armed:nth-child(odd),
.quotes > .rise.armed:nth-child(odd) {
  transform: translate3d(-7%, 26px, 0) rotate(-1.2deg);
}
.deck > .rise.armed:nth-child(even),
.quotes > .rise.armed:nth-child(even) {
  transform: translate3d(7%, 26px, 0) rotate(1.2deg);
}
.deck > .rise.in, .quotes > .rise.in {
  transition-delay: calc(var(--n, 0) * 85ms);
}

/* ТРЕТИЙ: ПАНЕЛЬ НАЕЗЖАЕТ ШТОРКОЙ.
   Цвет не «включается», а закрывает экран снизу вверх. Это и делает
   смену главы событием, а не сменой фона. */
.claim { position: relative; overflow: hidden; }
.claim::before {
  content: "";
  position: absolute; inset: 0;
  background: inherit;
  transform: translate3d(0, 0, 0);
  transition: transform .9s var(--ease);
  z-index: -1;
}
.claim.armed::before { transform: translate3d(0, 100%, 0); }
.claim.settled::before { transform: none; transition: none; }

/* ЧЕТВЁРТЫЙ: ОГРОМНОЕ СЛОВО ПОЗАДИ.

   ИМЯ КЛАССА — bigmark, И ЭТО НЕ ПРИДИРКА. Сначала он назывался ghost, и
   правило немедленно захватило кнопки .pill.ghost, которые так назывались
   раньше: «Задать вопрос» и «Мои записи» стали буквами по 485 пикселей во
   весь конец ленты. Общее слово, добавленное позже, молча забирает себе
   всё, что так уже называлось.
   Взято из ролика, с которого версия началась: там имя лежит ЗА
   объектом и перекрывается им. Здесь тем же слоем идёт первая буква
   салона — она движется медленнее ленты, и от этого появляется глубина,
   которой не даёт ни одна из прежних версий. */
.bigmark {
  position: absolute;
  right: -.08em;
  bottom: 6%;
  margin: 0;
  font-family: var(--face);
  font-weight: 700;
  font-size: 58vh;
  line-height: .8;
  letter-spacing: -.06em;
  color: color-mix(in srgb, var(--ink) 7%, transparent);
  pointer-events: none;
  user-select: none;
  z-index: -1;
  will-change: transform;
}

/* Строка фактов раскрывается вместе с линией: сначала чертится
   разделитель, следом приходит текст. Мелочь, которую замечают не
   глазами, а ощущением «сделано». */
.factlist a, .factlist div { position: relative; border-top-color: transparent; }
.factlist a::before, .factlist div::before {
  content: "";
  position: absolute; left: 0; top: 0; height: 1px;
  width: 0; background: var(--line);
  transition: width .7s var(--ease);
  transition-delay: calc(var(--n, 0) * 90ms);
}
.facts.in .factlist a::before, .facts.in .factlist div::before { width: 100%; }
.facts.settled .factlist a::before, .facts.settled .factlist div::before {
  width: 100%; transition: none;
}

@media (prefers-reduced-motion: reduce) {
  .reveal > i, .claim::before, .factlist a::before, .factlist div::before {
    transform: none; width: 100%; transition: none;
  }
  .bigmark { display: none; }
}


/* ═══════════════════════════════════════════════════════════════════════
   СКРЫТОЕ ОБЯЗАНО ИСЧЕЗАТЬ — И ЭТО ПРАВИЛО ИДЁТ ПОСЛЕДНИМ

   `[hidden] { display: none }` живёт в таблице браузера и весит один
   селектор. Ровно столько же весит `.spread { display: flex }` — и при
   равном весе побеждает то, что объявлено ниже. Наше правило было выше,
   поэтому проигрывало всегда.

   Итог: полосы, которые код честно прятал — работы, мастера, абонементы,
   отзывы, партнёры, товары, дорога, демо, — оставались на месте пустыми,
   по 228px отступов каждая. У салона без них это 1824px пустоты посреди
   ленты: полтора экрана ни о чём, после которых человек решает, что
   витрина кончилась. Измерено на живой витрине bad-b-nails.

   ИМЕННО ЭТО и было «слишком много пустоты» — не высота полос.

   Правило общее, а не по классу на каждый: тем же болели полоса товаров
   (.strip) и её список (.plain), и следующий такой элемент заболел бы
   молча. Якорь — нарочное исключение: он весит два селектора и потому
   сильнее этого правила независимо от порядка. */
[hidden] { display: none; }
