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

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

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

   ШРИФТ. 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%; }

/* СТРАНИЦА НЕ ЕЗДИТ ВБОК.
   Крупная буква за первым экраном намеренно уходит за правый край — но
   вместе с ней за край уезжала и вся страница: документ становился на
   четыре десятка пикселей шире окна, и любое касание таскало макет
   вправо-влево. Ощущалось это как «сайт болтается в руках».

   `clip`, а не `hidden`: `hidden` делает корень прокручиваемым
   контейнером и ломает `position: sticky` у панелей внутри. `clip` режет
   и ничего больше не меняет. */
html, body { overflow-x: clip; }

/* ФОН СТОИТ И СВЕТИТСЯ.
   Свечение — отдельным слоем на псевдоэлементе, а не в фоне тела: тело
   прокручивается вместе с лентой, и свет уезжал бы вниз. Здесь он
   закреплён и остаётся там, где ему положено. */
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); }

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

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

   Теперь высоту задаёт содержимое, а воздух — отступами. Полная высота
   осталась там, где она и есть содержание: обложка с именем салона и
   утверждение во весь экран. Эти два и держат ритм, ради которого версия
   делалась, а остальные больше не изображают его пустотой. */
/* ОТСТУПЫ СОСЕДЕЙ СКЛАДЫВАЮТСЯ.
   Было 11vh сверху и столько же снизу — на телефоне до 104 пикселей с
   каждой стороны, а между двумя разделами они встречаются: двести восемь
   пикселей, на которых нет ничего. Пролистывая, человек видит экран
   пустоты и решает, что сайт кончился.

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

   Поэтому режем до семидесяти между разделами: вместе с воздухом
   заголовка это и читается той сотней, на которую рассчитывали. */
.spread {
  position: relative;
  padding: clamp(26px, 4.4vh, 46px) var(--pad);
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 14px;
}

/* Обложка — во весь экран и прижата к низу: имя салона там и есть всё
   содержание, и воздух над ним работает. */
.spread.cover {
  min-height: 100svh;
  padding-top: calc(var(--pad) + env(safe-area-inset-top, 0px));
  /* Запас под волну: последняя строка обложки не должна упираться в её
     гребень, даже когда он поднимается на прокрутке. */
  padding-bottom: calc(env(safe-area-inset-bottom, 0px) + clamp(64px, 12vh, 128px));
}

/* УТВЕРЖДЕНИЕ — ПО СОДЕРЖАНИЮ, А НЕ ПО ЭКРАНУ.
   Высоту этой полосы дважды задавали долей экрана: сперва 100svh, потом
   46svh. Оба раза с одной ошибкой — экран не знает, сколько здесь текста.

   Замерено на живой витрине: содержания 124 пикселя, полоса 385. Двести
   шестьдесят один пиксель пустоты, две трети раздела, — и заголовок
   висел посреди неё. Доля экрана делает воздух тем больше, чем длиннее
   строка помещается: на широком экране текст короче, а пустота больше.
   Ровно наоборот тому, что нужно.

   Теперь высоту задаёт сам текст, а воздух — своё поле, заметно шире
   обычного раздела: полоса по-прежнему читается отдельным вдохом, но не
   занимает экран, на котором ничего нет. */
.spread.claim {
  padding-block: clamp(40px, 6.4vh, 72px);
  padding-top: calc(clamp(40px, 6.4vh, 72px) + env(safe-area-inset-top, 0px));
}
/* Обложка режет собственное украшение: буква обязана уходить за край
   кадра, а не за край документа. */
.spread.cover { justify-content: flex-end; overflow-x: clip; }

/* ── ПЕРЕХОД ОТ ОБЛОЖКИ К ЛЕНТЕ ───────────────────────────────────────
   Стык встык читается как «страница кончилась и началась другая»: ровная
   линия поперёк экрана — самая заметная граница из возможных.

   Волна снимает её и объясняет, что дальше то же самое, только светлее.
   Она ТЯНЕТСЯ, пока обложка уходит вверх: гребень поднимается, и переход
   происходит на глазах, а не случается за кадром. Считает это браузер по
   положению прокрутки — ни одного слушателя. */
.cover-wave {
  position: absolute;
  left: -2%;
  right: -2%;
  bottom: -1px;
  width: 104%;
  height: clamp(56px, 11vh, 116px);
  display: block;
  pointer-events: none;
  /* ПОД ТЕКСТОМ, НАД СНИМКОМ.
     На z-index: 2 волна легла поверх часов работы и срезала строку —
     человек видел «Еж» и обрыв. Снимок обложки лежит на -1; ставим волну
     туда же, но она идёт ПОЗЖЕ в разметке и потому рисуется над ним, а
     текст, у которого своего слоя нет, остаётся выше обоих. */
  z-index: -1;
  transform-origin: 50% 100%;
}
.cover-wave path { fill: var(--paper); }

@supports (animation-timeline: view()) {
  .cover-wave {
    animation: wave-rise linear both;
    animation-timeline: view();
    animation-range: exit 0% exit 100%;
  }
}
@keyframes wave-rise {
  from { transform: scaleY(1) translateY(0); }
  to   { transform: scaleY(2.1) translateY(-4%); }
}

/* На своём фото волна светлее самой бумаги: под ней снимок, и ровный
   цвет бумаги выглядел бы заплаткой. */
body.own .cover-wave path { fill: color-mix(in srgb, var(--paper) 82%, transparent); }

@media (prefers-reduced-motion: reduce) {
  .cover-wave { animation: none; }
}

/* Хвост уступает место якорю: кнопка висит поверх ленты, и без запаса
   она накрыла бы последнюю строку. */
.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 .cover-early { color: rgb(247 247 249 / .82); }

/* ── ТИПОГРАФИКА ──────────────────────────────────────────────────────
   Размер не «крупный», а предельный: имя салона обязано занять экран,
   иначе разворот превращается в обычную страницу с большим заголовком. */
/* ИМЯ ОБЯЗАНО ПОМЕЩАТЬСЯ.
   Размер задан в долях ширины экрана, а длина слова от неё не зависит:
   «COSMETOLOGY» на 15vw шире экрана, и его срезало по краю — салон видел
   на своей витрине «COSMETOLO». Ниже скрипт ужимает кегль ровно
   настолько, чтобы влезло самое длинное слово; переменная — точка, за
   которую он берётся. */
.huge {
  margin: 0;
  font-family: var(--face);
  font-weight: 700;
  font-size: clamp(44px, calc(15vw * var(--fit, 1)), 132px);
  line-height: .92;
  letter-spacing: -.045em;
  text-wrap: balance;
}
/* ПЕРЕНОС ПО БУКВАМ — ПОСЛЕДНЯЯ МЕРА, А НЕ ПРАВИЛО.
   Включённый всегда, он позволял браузеру разорвать слово на грани: имя
   формально помещалось, а на обложке оставалась одинокая «Y». Класс
   ставит подгонка и только тогда, когда слово не влезает даже на самом
   маленьком кегле — там обрезанное имя было бы хуже переноса. */
.huge.split-word { overflow-wrap: anywhere; }

.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); }
.cover-early { margin: 2px 0 0; max-width: 34ch; font-size: 13px;
  line-height: 1.4; color: var(--soft); }
.fine { margin: 4px 0 0; font-size: 13px; color: var(--soft); }
/* ПУСТАЯ ПОДПИСЬ НЕ ЗАНИМАЕТ СТРОКУ.
   Под свободными окнами подпись появляется, только когда окон больше,
   чем помещается. Всё остальное время она пустая — и всё равно держала
   строку в тридцать четыре пикселя, из-за чего у раздела снизу было
   больше воздуха, чем у соседей. Пустой абзац виден не текстом, а
   сбитым ритмом. */
.fine:empty { display: none; }

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

   Строки декоративны и помечены 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.

   `contain: paint` держит рисование внутри рамки: это то, ради чего
   подсказку и ставили, только без вечного слоя. */
.marquee { contain: paint; }
/* Контурная пара убрана из разметки: две строки разом читались как два
   ряда обрывков. Правило осталось бы висеть без хозяина. */

/* Тот же прайс списком: он и есть настоящий, а строки — его лицо. */
.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: flex-start;
  gap: 12px;
  cursor: pointer;
}
.plain .nm { flex: 1; font-size: 16px; }
/* Что входит. Строка теперь двухэтажная, поэтому цена равняется по
   ВЕРХУ, а не по базовой линии: иначе она уезжала бы к описанию. */
.plain .what {
  display: block;
  margin-top: 3px;
  font-size: 13px;
  line-height: 1.35;
  color: var(--soft);
}
/* Цена и длительность стоят В ОДНУ ЛИНИЮ.
   Строка выровнена по ВЕРХУ (иначе цена уезжала бы к описанию услуги), а
   шрифты у них разного размера — и при равнении по верху «2 ч» повисало
   над ценой. Одинаковая высота строки ставит обе на место, не трогая
   выравнивание самой строки. */
.plain .pr,
.plain .du { line-height: 22px; white-space: nowrap; }
.plain .pr { font-family: var(--face); font-weight: 700; font-size: 16px; }
.plain .du { font-size: 13px; color: var(--soft); }

/* ЗАГОЛОВОК РУБРИКИ.
   Прайс шёл сплошным списком: «Наращивание» и «Педикюр» вперемешку, и
   двадцать строк читались как одна куча. Набран мелко и в разрядку —
   это указатель, а не второй заголовок раздела. */
.plain .cat {
  padding: 22px 0 6px;
  font-size: 12px;
  font-weight: 600;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--soft);
}
.plain .cat:first-child { padding-top: 4px; }

/* ── ЦВЕТНАЯ ПАНЕЛЬ ───────────────────────────────────────────────────
   Смена цвета во весь экран — это смена главы. Заголовок мелким шрифтом
   такого не делает: его пролистывают, не заметив. */
/* УТВЕРЖДЕНИЕ НЕ КРАСИТСЯ ВОВСЕ. РЕШЕНИЕ РАЗВЁРНУТО.

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

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

   Утверждение остаётся крупным и лежит на той же бумаге, что и всё
   остальное. Главу делает размер и воздух, а не заливка.

   Акцент при этом никуда не делся, но красит он теперь ровно два места:
   карточки мастеров и услуг (через --card) и отметку платного окна. Там
   он работает как указание — «вот это», — а не как фон целого экрана. */
.claim {
  background: none;
  color: var(--ink);
  justify-content: center;
}
/* Раскрывающаяся подложка была нужна только заливке. Без неё анимировать
   нечего, и она бы мигала прозрачным прямоугольником. */
.claim::before { display: none; }
.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(78vw, 360px);
  scroll-snap-align: center;
  display: grid;
  /* СОДЕРЖИМОЕ НЕ ШИРЕ СВОЕЙ КАРТОЧКИ.
     Жалоба владелицы lami-by-kami, 18.09.2026: «что с фотками работ
     произошло???» — две работы стояли внахлёст, снимок наезжал на
     соседний, и подписи слипались.
     Причина не в снимках. Колонка `auto` у вложенной сетки берёт ширину
     по самому длинному неразрывному куску содержимого, а в подписи это
     «Долговременная» плюс цена, которую нельзя переносить: 192 пикселя
     при колонке в 162. Снимок честно занимал 100% этой колонки — то есть
     вылезал за карточку на соседнюю.
     `minmax(0, 1fr)` запрещает колонке быть шире карточки, а `min-width`
     — самой карточке быть шире своей доли в сетке работ. */
  grid-template-columns: minmax(0, 1fr);
  min-width: 0;
  gap: 12px;
  /* Соседняя карточка ОТСТУПАЕТ, а не спорит за взгляд. Полоса читалась
     как четыре равнозначные картинки подряд: глазу не за что зацепиться,
     и она выглядела «обычной». Разница мелкая — доли процента размера и
     немного прозрачности, — но именно она отличает ленту, по которой
     ведут пальцем, от ряда плиток. */
  transition: opacity .34s var(--ease), transform .34s var(--ease);
  opacity: .58;
  transform: scale(.965);
}
.strip .shot.near { opacity: 1; transform: none; }
/* Один-два снимка — сетка, а не лента: листать нечего, и приглушённый
   сосед читался бы недогруженной картинкой. */
.strip.few { display: grid; overflow: visible; scroll-snap-type: none;
  margin: 0; padding: 0; grid-template-columns: minmax(0, min(100%, 360px)); }
/* Две работы стоят парой, а не лентой из двух. Просвет между ними шире
   ленточного: в ленте соседа отделяет приглушение и прокрутка, а здесь
   обе карточки равны и стоят вплотную — 10 пикселей читались как одна
   разрезанная фотография. */
.strip.few.pair { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 16px; }
.strip.few .shot { width: auto; opacity: 1; transform: none; }
.strip .shot button {
  appearance: none; border: 0; padding: 0; background: none;
  cursor: pointer; display: block; overflow: hidden;
  /* Радиус крупнее и тень мягче: край снимка перестаёт быть чертой и
     становится краем предмета. Тень двойная — ближняя держит контур,
     дальняя даёт вес; одна такого не даёт. */
  border-radius: 26px;
  box-shadow:
    0 1px 2px rgb(0 0 0 / .05),
    0 18px 34px -18px rgb(0 0 0 / .22);
  transition: transform .18s var(--ease), box-shadow .34s 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);
  /* СНИМОК ПОЯВЛЯЕТСЯ, А НЕ ВЫСКАКИВАЕТ. Картинка вставала мгновенно и
     рывком — на медленной сети это выглядит как подёргивание страницы.
     Появление привязано к самой загрузке, а не к таймеру: класс ставит
     обработчик load. */
  opacity: 0;
  transform: scale(1.03);
  transition: opacity .5s var(--ease), transform .7s var(--ease);
}
.strip img.here { opacity: 1; transform: none; }
@media (prefers-reduced-motion: reduce) {
  .strip .shot { opacity: 1; transform: none; transition: none; }
  .strip img { opacity: 1; transform: none; transition: none; }
}
/* ДЕМО ГОЛОСОМ. Проигрыватель системный намеренно: свой рисовать значит
   переучивать человека нажимать «играть», а выигрыш — только внешний. Он
   лишь встаёт в ширину полосы и в её цвет. */
.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));
}

/* ── ЗНАК САЛОНА ──────────────────────────────────────────────────────
   Логотип и имя вместе — лого-блок, с которого начинается обложка.
   Высота задана, ширина свободна: знаки бывают и круглые, и вытянутые, и
   вписывать чужой рисунок в наш квадрат значит его обрезать. */
.covermark {
  height: clamp(38px, 9vw, 64px);
  width: auto; max-width: 62vw;
  margin: 0 0 14px;
  object-fit: contain; object-position: left center;
  align-self: flex-start;
}
/* КТО РАБОТАЕТ — С ЛИЦАМИ И НАЖАТИЕМ.
   Здесь стояли неподвижные плитки: имя, должность и всё. К мастеру идут
   не за должностью — на него смотрят, а потом записываются именно к
   нему; плитка не давала ни того, ни другого. */
.tile.face{
  display:flex; flex-direction:column; align-items:flex-start; gap:4px;
  width:100%; text-align:left; border:0; font:inherit; color:inherit;
  cursor:pointer; -webkit-appearance:none; appearance:none;
  transition:transform .15s ease;
}
.tile.face:active{transform:scale(.99)}
.tile.face .pic{
  display:block; width:100%; aspect-ratio:4/3; overflow:hidden;
  border-radius:14px; margin-bottom:8px;
}
.tile.face .pic img{width:100%; height:100%; object-fit:cover; display:block}

/* Карточка мастера в листе: лицо крупно, потом слова. */
.face-big{
  width:100%; aspect-ratio:1/1; overflow:hidden; border-radius:18px;
  margin-bottom:14px;
}
.face-big img{width:100%; height:100%; object-fit:cover; display:block}
.facelist{display:grid; gap:6px; margin:14px 0 16px}

.covermark[hidden] { display: none; }

/* ── БЛИЖАЙШЕЕ ОКНО НА ОБЛОЖКЕ ────────────────────────────────────────
   Строка, ради которой витрину и открывают: есть ли время. Она же
   кнопка — от «время есть» до выбора часа не должно быть ни одного
   лишнего движения.

   Индикатора рядом нет намеренно: зелёный кружок — знак из системного
   языка, а обложка набирается в цвете салона. Чужой зелёный на песочном
   или тёмно-вишнёвом фоне читается посторонним элементом, и слова
   «Свободно сегодня в 15:00» говорят всё сами. */
.coverfree {
  display: inline-flex; align-items: center; gap: 10px;
  align-self: flex-start; margin: 14px 0 0;
  padding: 11px 16px; border: 0; border-radius: 999px;
  background: color-mix(in srgb, var(--ink) 10%, transparent);
  -webkit-backdrop-filter: blur(14px); backdrop-filter: blur(14px);
  color: inherit; font: inherit; font-size: 15px; cursor: pointer;
  transition: transform .3s var(--ease), background .3s var(--ease);
}
.coverfree:hover { background: color-mix(in srgb, var(--ink) 16%, transparent); }
.coverfree:active { transform: scale(.97); }
.coverfree .txt { font-weight: 600; }
.coverfree .cnt { color: var(--soft); }
.coverfree[hidden] { display: none; }

/* ── ОЦЕНКА И ВИДЖЕТ ОТЗЫВОВ ──────────────────────────────────────────
   Общую оценку бизнес заработал, а витрина показывала только звёздочки
   под каждой отдельной фразой. Цифра крупная — её и ищут глазами. */
.mark {
  display: flex; align-items: baseline; gap: 10px; margin: 0 0 18px;
}
.mark .num {
  font-family: var(--face); font-weight: 800;
  font-size: clamp(34px, 9vw, 56px); letter-spacing: -.03em; line-height: 1;
}
.mark .of { font-size: 14px; color: var(--soft); }
/* Виджет Яндекса владелец вставляет сам, и до сих пор его видели только
   клиенты первой версии. Рамка своя: чужая страница внутри нашей должна
   выглядеть частью витрины, а не вклеенным окном. */
.maps { margin-top: 18px; }
.maps-reviews {
  width: 100%; height: min(420px, 64vh); border: 0; border-radius: 18px;
  background: color-mix(in srgb, var(--ink) 5%, transparent);
}

/* ── О НАС ────────────────────────────────────────────────────────────
   Текст, который владелец написал сам. От него оставалась одна строка в
   цветной полосе: остальное обрезалось многоточием и не показывалось
   нигде. У ZOV_NAILS там пятьсот тридцать восемь знаков — ровно то, что
   читает человек, который ещё выбирает мастера.

   Ширина в строку, а не во всю полосу: длинная строка на широком экране
   теряется глазом на переносе. Абзацы владельца сохранены как есть. */
.storytext { max-width: 34em; }
.storytext p {
  margin: 0 0 14px;
  font-size: clamp(16px, 3.9vw, 20px);
  line-height: 1.5;
  color: color-mix(in srgb, var(--ink) 82%, transparent);
}
.storytext p:last-child { margin-bottom: 0; }
/* Перечисление владельца. Маркер рисуем сами и вешаем в поле: тире,
   набранное им внутри строки, при переносе оставляло вторую строку под
   собой, а не под первой буквой. */
.storytext ul { margin: 0 0 14px; padding: 0; list-style: none; }
.storytext ul:last-child { margin-bottom: 0; }
.storytext li {
  position: relative; padding-left: 1.1em; margin: 0 0 6px;
  font-size: clamp(16px, 3.9vw, 20px);
  line-height: 1.5;
  color: color-mix(in srgb, var(--ink) 82%, transparent);
}
.storytext li::before {
  content: ""; position: absolute; left: 0; top: .68em;
  width: .5em; height: 1px;
  background: color-mix(in srgb, var(--ink) 42%, transparent);
}

/* ── МЫ В СЕТИ ────────────────────────────────────────────────────────
   Ссылки лежали строками в списке контактов: «Instagram — Открыть»
   между телефоном и часами работы. Лента мастера часто и есть его
   главная витрина, а мы держали её в справочнике мелким шрифтом. */
.linkdeck {
  display: grid; gap: 8px;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 240px), 1fr));
}
.linkcard {
  display: flex; align-items: center; justify-content: space-between;
  gap: 14px; padding: 18px 20px; border-radius: 18px;
  background: color-mix(in srgb, var(--ink) 6%, transparent);
  color: inherit; text-decoration: none;
  /* Наклон и подъём под пальцем — как у карточек мастеров. Движение
     маленькое: карточка отвечает на касание, а не прыгает. */
  transition: transform .4s var(--ease), background .4s var(--ease);
}
.linkcard:hover { background: color-mix(in srgb, var(--ink) 11%, transparent); }
.linkcard:active { transform: scale(.985); }
.linkcard .nm { font-size: clamp(17px, 4vw, 21px); font-weight: 600; }
.linkcard .go {
  flex: none; width: 34px; height: 34px; border-radius: 50%;
  display: grid; place-items: center;
  background: color-mix(in srgb, var(--ink) 10%, transparent);
  transition: transform .4s var(--ease);
}
.linkcard:hover .go { transform: translate3d(2px, -2px, 0); }
.linkcard .go svg {
  width: 15px; height: 15px; fill: none; stroke: currentColor;
  stroke-width: 2; stroke-linecap: round; stroke-linejoin: round;
}


/* ── НАВЕРХ ───────────────────────────────────────────────────────────
   Стоит слева от записи, кружком: это второе действие, а не второе
   главное. Размер — сорок восемь, чтобы попадать пальцем не глядя. */
.anchor-up {
  flex: none;
  width: 48px; height: 48px;
  display: grid; place-items: center;
  border: 0; border-radius: 50%;
  background: color-mix(in srgb, var(--ink) 10%, var(--paper));
  color: var(--ink);
  cursor: pointer;
  -webkit-backdrop-filter: blur(14px);
  backdrop-filter: blur(14px);
  transition: transform .2s var(--ease), opacity .2s var(--ease);
}
.anchor-up[hidden] { display: none; }
.anchor-up:active { transform: scale(.94); }
.anchor-up svg {
  width: 20px; height: 20px; fill: none; stroke: currentColor;
  stroke-width: 2; stroke-linecap: round; stroke-linejoin: round;
}

/* ПОДПИСЬ. Без неё это просто красивые картинки: человек смотрит и не
   понимает, что перед ним и сколько стоит. */
.strip figcaption {
  display: flex; align-items: baseline; gap: 12px;
  padding: 0 4px;
  /* Цена уходит под название, когда в строку они не помещаются. В две
     колонки на телефоне «Долговременная укладка» и «1300 ₽» рядом не
     встают никогда, а сжимать имя до многоточия нельзя: человек выбирает
     работу по названию. */
  flex-wrap: wrap;
  row-gap: 2px;
}
/* Имя и цена — не два равных слова в строке. Имя ведёт, цена отвечает:
   она чуть тише по цвету и набрана табличными цифрами, поэтому в ряду
   карточек цены стоят ровным столбцом, а не пляшут по ширине знаков. */
.strip figcaption .nm {
  flex: 1 1 auto; min-width: 0; font-size: 15.5px; line-height: 1.25;
  font-weight: 500; letter-spacing: -.012em;
  /* Слово длиннее колонки переносится, а не раздвигает карточку. */
  overflow-wrap: anywhere;
}
.strip figcaption .pr {
  font-family: var(--face); font-weight: 600; font-size: 15px;
  white-space: nowrap; font-variant-numeric: tabular-nums;
  letter-spacing: -.01em;
  color: color-mix(in srgb, var(--ink) 72%, transparent);
}

/* ── ФАКТЫ ────────────────────────────────────────────────────────────
   Адрес и телефон текстом, а не за иконкой: их читают, а не нажимают. */
.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; }

/* «Приду» — ответ, которого ждёт мастер. Заполненный рядом с тихой
   «Отменить»; второго вопроса не задаём: промах ничего не стоит. */
.mineline .yes {
  appearance: none; border: 0; cursor: pointer; font: inherit;
  flex: none; font-size: 13.5px; padding: 7px 13px; border-radius: 999px;
  margin-left: auto;
  background: var(--ink); color: var(--paper);
  transition: opacity .16s var(--ease);
}
.mineline .yes:disabled { opacity: .45; 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;
  /* Кнопка записи тянется, кружок «наверх» — нет: главное действие
     обязано оставаться самым широким. */
  display: flex; align-items: center; gap: 10px;
  transform: translate3d(0, 120%, 0);
  transition: transform .4s var(--ease);
}
.anchor .pill.wide { flex: 1; min-width: 0; }
.anchor.on { transform: none; }
/* НАРОЧНОЕ ИСКЛЮЧЕНИЕ из общего `[hidden] { display: none }` в конце
   файла: якорь не исчезает, а уезжает вниз — появление и уход должны быть
   движением, а не миганием. Переживает общее правило по весу, а не по
   порядку: два селектора против одного.

   FLEX, А НЕ BLOCK. Атрибут `hidden` с якоря никто не снимает: видимость
   переключает класс `on`, а исключение действует всегда. Значит `block`
   действовал ВСЕГДА — и ряд якоря разваливался в столбик: кружок
   «наверх» вставал над кнопкой записи, а не рядом, и наезжал на
   заголовок раздела, мимо которого проплывал. Раскладка здесь не имеет
   отношения к видимости, и менять её вместе с ней нельзя. */
.anchor[hidden] { display: flex; }

/* ── ЛИСТ ─────────────────────────────────────────────────────────────
   Всё, что требует ответа, живёт поверх ленты. Лента не теряется:
   человек видит, куда вернётся. */
.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; }
/* ПОДЪЁМ С ПРИБЛИЖЕНИЕМ, А НЕ ПРОСТО СНИЗУ ВВЕРХ.
   Сдвиг на двадцать шесть пикселей читается как «список дорисовался».
   Едва заметное приближение — 0.965 → 1 — читается как «предмет вышел
   вперёд»: он не появился на месте, он подошёл. Разница в три с
   половиной процента, и именно она отличает движение от анимации.

   Задержка `--in` приходит из скрипта и делает очередь: соседи трогаются
   не разом, а один за другим. Разом — это вспышка, по очереди — приход. */
.rise.armed {
  opacity: 0;
  transform: translate3d(0, 30px, 0) scale(.965);
}
.rise.in {
  opacity: 1; transform: none;
  /* БЫСТРЕЕ, ЧЕМ ПАЛЕЦ. Было .7s и .9s — почти секунда, и на телефоне
     блок не успевал проявиться, пока человек на него смотрел: лента
     листается быстрее, чем идёт такой показ. Движение осталось тем же,
     только успевает случиться. */
  transition:
    opacity .42s var(--ease) var(--in, 0ms),
    transform .52s var(--ease) var(--in, 0ms);
}
.rise.settled, .tile.rise.settled { opacity: 1; transform: none; transition: none; }

/* ЗАГОЛОВОК ВЫЕЗЖАЕТ ИЗ-ПОД КРАЯ.
   У раздела он объявляет тему, и обычное проявление ставит его в один
   ряд с содержимым. Маска даёт ему собственный жест: буквы выходят
   из-под невидимой линии, как из-за листа бумаги. Клип, а не overflow:
   строчные хвосты у «Д» и «ц» не должны срезаться. */
.spread .head, .spread .stat {
  --peek: 0;
  clip-path: inset(-.24em -.5em calc(var(--peek) * 1em) -.5em);
  transform: translate3d(0, calc(var(--peek) * .9em), 0);
}
/* Классы ставит тот же наблюдатель, что и остальному: раздел уже был под
   присмотром, но его собственные `armed`/`in` не значили ничего — правил
   для них не существовало. Теперь значат. */
.spread.armed .head, .spread.armed .stat { --peek: 1; }
.spread.in .head, .spread.in .stat {
  --peek: 0;
  transition: clip-path .95s var(--ease), transform .95s var(--ease);
}
.spread.settled .head, .spread.settled .stat { 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; }
  .spread.armed .head, .spread.armed .stat { --peek: 0; 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;
  /* Заливка и рамка ОТ ЦВЕТА ТЕКСТА, а не жёстким чёрным: на тёмном
     наборе чёрное по чёрному не видно вовсе. И заметнее, чем было: поле
     на 5% заливки с рамкой в 16% на светлой бумаге не читалось — форму
     заявки человек видел как пустой экран с одной кнопкой. */
  background: color-mix(in srgb, var(--ink) 7%, transparent);
  box-shadow: inset 0 0 0 1.5px var(--line);
}
/* Подсказка внутри поля — это подсказка, а не текст: светлее набранного,
   но всё ещё читаемая. Браузерная по умолчанию бледнее некуда. */
.fld input::placeholder, .fld textarea::placeholder {
  color: color-mix(in srgb, var(--ink) 55%, transparent);
  opacity: 1;
}
.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 выше:
   во весь экран приглушённый акцент читается плитой, а не главой), и
   шторке осталось двигать прозрачный прямоугольник.

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

   Классы armed и settled при этом остаются: это общий механизм появления
   для ВСЕХ разделов ленты, и полоса пользуется им как остальные — текст
   выезжает, подложки просто больше нет. */
.claim { position: relative; }

/* БОЛЬШОЙ БУКВЫ ПОЗАДИ БОЛЬШЕ НЕТ.

   Замысел был из ролика: имя лежит за объектом и перекрывается им. На
   деле выходило другое. Буква рисовалась шрифтом обложки — а он
   декоративный, и нужного знака в нём может не быть вовсе. Вместо буквы
   браузер ставит пустой прямоугольник, и на 58vh это серый квадрат в
   треть экрана, который владелец справедливо принял за поломку.

   Чинить это подбором запасного шрифта значит подпирать украшение,
   которое и так ничего не объясняет. Убрано целиком — вместе с
   параллаксом, который его двигал. */

/* Строка фактов раскрывается вместе с линией: сначала чертится
   разделитель, следом приходит текст. Мелочь, которую замечают не
   глазами, а ощущением «сделано». */
.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;
  }
}


/* ═══════════════════════════════════════════════════════════════════════
   ДЛИНА И ЦЕНА

   Мастера рисуют эту линейку от руки и кидают в переписку. Здесь она —
   часть витрины.

   НОГОТЬ ЗДЕСЬ ПРЕДМЕТ, А НЕ СХЕМА. У чертежа нет ни света, ни толщины,
   и рядом с крупной типографикой «Разворота» он выглядел бы вставленной
   картинкой из другого документа. Форма, мягкий блик и тень делают его
   вещью, на которую смотрят.

   Всё остальное говорит типографика — как и везде в этой версии: цена
   крупно, подпись тонко, ничего не спрятано за нажатием.
   ═══════════════════════════════════════════════════════════════════ */
.spread.nails { gap: 20px; }
.spread.nails[hidden] { display: none; }

.nailwrap {
  display: grid;
  grid-template-columns: minmax(96px, 132px) 1fr;
  gap: clamp(18px, 6vw, 44px);
  align-items: center;
}

/* Палец и пластина: один объект, собранный из трёх слоёв — так тень
   ложится между ними, а не поверх. */
.nailform {
  position: relative;
  aspect-ratio: 1 / 2.1;
  align-self: center;
}
.nailform i { position: absolute; display: block; }

.nail-finger {
  left: 6%; right: 6%; bottom: 0; top: 34%;
  border-radius: 44% 44% 38% 38% / 16% 16% 10% 10%;
  background:
    linear-gradient(100deg,
      color-mix(in srgb, var(--ink) 6%, transparent) 0 22%,
      transparent 60%),
    color-mix(in srgb, var(--ink) 11%, var(--paper));
  box-shadow: inset 0 1px 0 color-mix(in srgb, var(--paper) 70%, transparent);
}

/* Пластина — сама ногтевая поверхность. Блик узкий и смещённый: широкий
   выглядит пластиком. */
.nail-plate {
  left: 16%; right: 16%; top: 30%; height: 26%;
  /* Верхние углы РОВНЫЕ. Были скруглены на 40 %, и вместе со скруглённым
     низом свободного края они образовывали две дуги, смотрящие друг на
     друга: между пластиной и краем зиял просвет, ноготь распадался на два
     белых пятна, и длина в нём не читалась вовсе. */
  border-radius: 0 0 40% 40% / 0 0 26% 26%;
  background:
    linear-gradient(112deg,
      color-mix(in srgb, var(--paper) 92%, #fff) 0 30%,
      color-mix(in srgb, var(--ink) 7%, var(--paper)) 62%);
  box-shadow:
    inset 0 1px 2px color-mix(in srgb, #fff 60%, transparent),
    0 2px 6px color-mix(in srgb, var(--ink) 10%, transparent);
}

/* Свободный край — он и растёт. Высоту задаёт скрипт переменной, а не
   класс на каждую ступень: ступеней пять, а состояний между ними много,
   и на классах движение получилось бы ступенчатым. */
.nail-tip {
  left: 16%; right: 16%;
  bottom: calc(70% + 0px);
  height: calc(var(--tip, .12) * 30%);
  /* Низ РОВНЫЙ по той же причине: край продолжает пластину, а не висит
     над ней. Скругление осталось только сверху — это и есть форма
     отросшего ногтя. */
  border-radius: 46% 46% 0 0 / 62% 62% 0 0;
  background:
    linear-gradient(112deg,
      color-mix(in srgb, #fff 78%, var(--paper)) 0 34%,
      color-mix(in srgb, var(--paper) 88%, #fff) 70%);
  box-shadow:
    inset 0 1px 0 color-mix(in srgb, #fff 80%, transparent),
    0 -1px 4px color-mix(in srgb, var(--ink) 8%, transparent);
  transition: height .45s var(--ease);
}

/* ШКАЛА — СПИСОК, А НЕ ПОДПИСИ К ЧЕРТЕЖУ.
   Её читают глазами сверху вниз, и цена в ней — главное. */
.nailscale { list-style: none; margin: 0; padding: 0; display: grid; gap: 2px; }
.nailscale li {
  display: flex; align-items: baseline; gap: 12px;
  padding: 11px 0;
  border-top: 1px solid var(--line);
  cursor: pointer;
  transition: opacity .25s var(--ease);
}
.nailscale li:first-child { border-top: 0; }
.nailscale .code {
  font-family: var(--face); font-weight: 700;
  font-size: 19px; letter-spacing: -.02em;
  min-width: 2.6em;
}
.nailscale .word { flex: 1; font-size: 13px; color: var(--soft); }
.nailscale .pr {
  font-family: var(--face); font-weight: 700; font-size: 19px;
  white-space: nowrap;
}
/* Выбранная ступень — единственная в полную силу. Остальные притушены:
   так видно, к какой длине относится ноготь рядом. */
.nailscale li { opacity: .42; }
.nailscale li.on { opacity: 1; }

@media (prefers-reduced-motion: reduce) {
  .nail-tip { transition: none; }
}

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

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

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

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

   Правило общее, а не по классу на каждый: тем же болели полоса товаров
   (.strip) и её список (.plain), и следующий такой элемент заболел бы
   молча. Якорь — нарочное исключение: он весит два селектора и потому
   сильнее этого правила независимо от порядка. */
/* ── Закон и подпись ──────────────────────────────────────────────────
   Ссылки об обработке данных на этой витрине не было вообще: только
   внутри формы записи, рядом с галочкой согласия. Человек, который ещё
   не начал записываться, узнать о ней не мог никак.
   Набор мелкий и тихий — это не раздел, а сноска, — но нажимаемый. */
.tail-legal {
  margin: 18px 0 0;
  font-size: 12.5px;
  line-height: 1.5;
  color: var(--soft);
}
.tail-legal a { color: inherit; text-decoration: underline; text-underline-offset: 3px; }
/* Разделитель — точка: пункты не перечисление, они просто соседи. */
.tail-legal i {
  display: inline-block;
  width: 3px; height: 3px;
  margin: 0 9px 3px;
  border-radius: 50%;
  background: currentColor;
  opacity: .5;
}

/* ══ ПОЛКА ═══════════════════════════════════════════════════════════════
   Свои снимки владельца с подписями: линейка длин ногтей, вид кабинета,
   схема проезда.

   СЕТКА, А НЕ ЛЕНТА — при том что соседние блоки здесь листаются вбок.
   Ленту листают только те, кто понял, что она листается: у полки нет ни
   повторяющегося ритма, ни счёта, по которым это видно, и половина
   клиентов увидела бы один снимок из шести.

   ОДНА КОЛОНКА НА ТЕЛЕФОНЕ. Схему проезда и линейку длин РАССМАТРИВАЮТ, а
   в половину экрана рассмотреть нельзя.

   СТОИТ ВЫШЕ ОБЩЕГО [hidden] НАМЕРЕННО. Правило браузера весит ровно один
   селектор — столько же, сколько `.shelf-grid`, — и при равном весе
   побеждает объявленное ниже. Скрытая полка иначе осталась бы на месте
   пустым блоком, а ровно эту пустоту владелец и назвал «слишком много
   пустоты» на живой витрине. */
.shelf-grid{display:grid;gap:14px;grid-template-columns:1fr}
@media (min-width:640px){
  .shelf-grid{grid-template-columns:repeat(2,1fr);gap:18px}
}
@media (min-width:1000px){
  .shelf-grid{grid-template-columns:repeat(3,1fr)}
}
.shelf-card{margin:0;display:flex;flex-direction:column;gap:10px}
/* Высота зарезервирована до загрузки: иначе страница прыгает под пальцем,
   когда доезжает шестой снимок, и человек нажимает не туда, куда целился.
   Пропорция 4:3, а не квадрат: линейка длин и схема проезда почти всегда
   горизонтальные. */
.shelf-card img{width:100%;aspect-ratio:4/3;object-fit:cover;
  /* 4:3 — только резерв места до загрузки: настоящие пропорции
     ставит скрипт, когда снимок пришёл. Жёсткая рамка резала
     широким кадрам края, а узким добавляла пустоты. */display:block;
  border-radius:14px;background:rgb(0 0 0 / .05)}
.shelf-card figcaption{display:flex;flex-direction:column;gap:3px}
.shelf-card figcaption b{font-size:15px;font-weight:600;line-height:1.25}
.shelf-card figcaption span{font-size:13px;line-height:1.4;opacity:.7}

/* ── НАПОМИНАНИЕ В КВИТАНЦИИ ──────────────────────────────────────────
   Таблетка-ссылка обязана выглядеть как таблетка-кнопка рядом: у <a> свой
   браузерный вид — строчный, подчёркнутый, — и без сброса он проступал
   ровно там, где кнопка должна быть главной. Раньше это лечилось стилем
   прямо в разметке; одно правило здесь дешевле копий по всем листам. */
a.pill {
  display: flex; align-items: center; justify-content: center;
  text-align: center; text-decoration: none; box-sizing: border-box;
}
.remind-slot { display: grid; gap: 8px; margin: 16px 0 14px; }
.remind-slot:empty { display: none; }
.remind-slot .fine { margin: 0; }
.remind-slot .quietlink { justify-self: start; margin: 0; }
.remind-slot #remindMailBox { display: grid; gap: 8px; }
.remind-slot #remindMailBox[hidden] { display: none; }

/* ВХОД ЧЕРЕЗ TELEGRAM — первой строкой листа «Ваши записи». Промежутки
   сеткой, чтобы подпись не липла к кнопке и к заголовку ниже. */
.tglogin { display: grid; gap: 8px; margin-bottom: 18px; }
.tglogin .fine { margin: 0; }

[hidden] { display: none; }

/* ── НЕСКОЛЬКО УСЛУГ В ОДНОЙ ЗАПИСИ ──────────────────────────────────
   Выбранная услуга — та же плашка, что и везде в листе, только с
   крестиком. Отдельный вид завёл бы в листе второй язык кнопок. */
.pill.sm.picked {
  display: inline-flex; align-items: center; gap: 8px;
  cursor: default;
}
.pill.sm.picked .pickx {
  border: none; background: none; padding: 2px; margin: -2px -4px -2px 0;
  font: inherit; font-size: 18px; line-height: 1; cursor: pointer;
  color: inherit; opacity: .55;
}
.pill.sm.picked .pickx:hover { opacity: 1; }
/* Сумма стоит между выбранными услугами и вопросом «когда»: без
   собственного поля она липнет к обоим и читается подписью к чужому
   разделу. */
.fine.total { margin: 10px 0 18px; font-weight: 600; }

/* СВОИ ЗАПИСИ И АБОНЕМЕНТЫ В ХВОСТЕ.
   Строка была одна — дата и кнопка отмены. Теперь рядом стоит, подтвердили
   запись или ещё нет, сколько осталось по абонементу и куда платить; это
   единственное место, где вернувшийся человек всё это узнаёт. */
.mineline .mark{
  display:block; font-style:normal;
  font-size:13px; line-height:1.4;
  color:color-mix(in srgb, var(--ink) 58%, transparent);
}
.mineline .mark:empty{display:none}

/* ══ ПЛАТНОЕ ОКНО ════════════════════════════════════════════════════════
   Цена времени стоит в подписи, цвет — акцент владельца. Подпись при этом
   остаётся чернилами: акцент бывает любым, включая почти белый, и на
   BARBERHØOD такой цвет уже сделал кнопку невидимой. Обводка и подложка
   работают с любым выбранным цветом.

   Выше общего [hidden] стоять не обязано: правило ничего не задаёт
   display. */
.pill.ghost.paid{
  box-shadow: inset 0 0 0 1.5px var(--accent);
  background: color-mix(in srgb, var(--accent) 10%, transparent);
}
body.no-accent .pill.ghost.paid{
  box-shadow: inset 0 0 0 1.5px var(--ink);
  background: color-mix(in srgb, var(--ink) 8%, transparent);
}

/* «УЗНАТЬ МЕНЯ В TELEGRAM» — ДВЕРЬ, А НЕ СНОСКА.

   Это единственный вход постоянного клиента: по нему он получает свои
   записи на любом устройстве и напоминание накануне. Подчёркнутой
   ссылкой внутри абзаца он и выглядел как сноска — мелко, серо, с
   переносом на вторую строку посреди слова «Telegram».

   Теперь это отдельная строка-таблетка: то же место, тот же текст, но
   форму глаз узнаёт как действие. Цвет берёт у своей строки — на обложке
   она светлая, в шапке тёмная, — поэтому рамка и фон заданы через
   currentColor, а не своим цветом. */
.early-link{
  display:inline-flex;align-items:center;gap:6px;
  margin-top:8px;padding:9px 15px;
  border-radius:999px;
  color:inherit;text-decoration:none;
  font-size:13.5px;font-weight:600;letter-spacing:-.01em;
  white-space:nowrap;
  box-shadow:inset 0 0 0 1px color-mix(in srgb, currentColor 34%, transparent);
  background:color-mix(in srgb, currentColor 8%, transparent);
  transition:transform .16s var(--ease);
}
.early-link:active{transform:scale(.97)}
/* Значок Telegram опознаётся быстрее любой подписи и говорит, куда ведёт
   нажатие. Рисуем маской: цвет тогда берётся у текста, а разметка ссылки
   остаётся собранной из элементов — адрес в ней пришёл по сети. */
.early-link::before{
  content:"";width:15px;height:15px;flex:none;
  background:currentColor;
  -webkit-mask:var(--tg-mark) center/contain no-repeat;
  mask:var(--tg-mark) center/contain no-repeat;
}
:root{
  --tg-mark:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M21.9 4.3 18.9 19c-.2 1-.8 1.3-1.7.8l-4.6-3.4-2.2 2.1c-.3.3-.5.5-1 .5l.4-5 9.1-8.2c.4-.4-.1-.6-.6-.2L7.1 12.8 2.6 11.4c-1-.3-1-1 .2-1.5l17.7-6.8c.8-.3 1.5.2 1.4 1.2'/%3E%3C/svg%3E");
}
/* Строка над кнопкой — своя, и переносится она нормально. */
.cover-early{display:flex;flex-direction:column;align-items:flex-start}
