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

:root {
  --paper: #f8f5fb;
  --surface: #ffffff;
  --lilac: #dad0e6;
  --lilac-soft: #ede7f3;
  --plum: #3f2b52;
  --ink: #4a4256;
  --muted: #7b7189;
  --accent: #704090;
  --accent-deep: #573071;
  --line: #e6dfee;
  --shadow: 0 18px 50px rgba(63, 43, 82, .10);
  --shadow-sm: 0 6px 20px rgba(63, 43, 82, .07);
  --radius: 22px;
  /* Заголовки — Nunito: круглые окончания штрихов, мягче и приветливее
     строгого Playfair, и кириллица у него полноценная */
  --display: "Nunito", "Manrope", -apple-system, "Segoe UI", sans-serif;
  --body: "Manrope", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
    "Helvetica Neue", Arial, sans-serif;

  /* Ниже — то, что раньше было вписано прямо в правила. Вынесено сюда
     целиком: тёмная тема переопределяет только этот список, а сами
     правила у обеих тем одни и те же. */
  --on-accent: #fff;             /* текст на сиреневой кнопке */
  --halo: rgba(218, 208, 230, .75);
  --top-glass: rgba(248, 245, 251, .55);
  --top-solid: rgba(248, 245, 251, .96);
  --glow-core: rgba(138, 84, 178, .34);
  --glow-mid: rgba(138, 84, 178, .14);
  --glow-edge: rgba(138, 84, 178, 0);
  --accent-half: rgba(112, 64, 144, .45);
  --accent-none: rgba(112, 64, 144, 0);
  --shadow-hover: 0 26px 60px rgba(63, 43, 82, .16);
  --shadow-stuck: 0 6px 24px rgba(63, 43, 82, .08);
  --btn-shadow: 0 10px 26px rgba(112, 64, 144, .28);
  --btn-shadow-hover: 0 16px 34px rgba(112, 64, 144, .34);
  --pick-shadow: 0 18px 44px rgba(112, 64, 144, .16);
  --mark-drop: 0 24px 50px rgba(87, 48, 113, .22);
  --loader-drop: 0 16px 28px rgba(87, 48, 113, .18);
  --fill-bg: #fdf3c7;
  --fill-ink: #6b5800;
  /* Знак в заставке: рамка, колбы, песок, блик */
  --mark-frame: #7a4fa0;
  --mark-glass-1: #f2ecf8;
  --mark-glass-2: #e2d7ee;
  --mark-sand: #b9a0d4;
  --mark-glare: #6b3f92;
  --mark-glare-a: .5;
  color-scheme: light;
}

/* ── Тёмная тема ────────────────────────────────────────────────────────

   Не инверсия светлой, а свой набор: фон — глубокий сливовый, а не
   чёрный, чтобы сиреневый знак не висел в пустоте; сиреневый акцент
   поднят до #c49bec, потому что рабочий #704090 на тёмном фоне
   недобирает по контрасту и ссылки перестают читаться. Текст на кнопке,
   наоборот, тёмный: светлая плашка со белыми буквами слепит.

   Тему ставит атрибут, а не медиазапрос: человек может выбрать её
   вручную, и выбор должен пережить перезагрузку. Системную настройку
   учитывает скрипт в <head> — он и ставит атрибут до первой отрисовки,
   поэтому страница не мигает светлым. */

[data-theme="dark"] {
  --paper: #131019;
  --surface: #1c1826;
  --lilac: #3b3050;
  --lilac-soft: #262036;
  --plum: #efe8f7;
  --ink: #cbc2da;
  --muted: #9c91af;
  --accent: #c49bec;
  --accent-deep: #d9bef8;
  --line: #302943;
  --shadow: 0 20px 52px rgba(0, 0, 0, .5);
  --shadow-sm: 0 8px 22px rgba(0, 0, 0, .34);

  --on-accent: #1d1428;
  --halo: rgba(104, 66, 148, .5);
  --top-glass: rgba(19, 16, 25, .62);
  --top-solid: rgba(19, 16, 25, .96);
  --glow-core: rgba(158, 104, 208, .26);
  --glow-mid: rgba(158, 104, 208, .11);
  --glow-edge: rgba(158, 104, 208, 0);
  --accent-half: rgba(196, 155, 236, .45);
  --accent-none: rgba(196, 155, 236, 0);
  --shadow-hover: 0 26px 60px rgba(0, 0, 0, .55);
  --shadow-stuck: 0 6px 24px rgba(0, 0, 0, .4);
  --btn-shadow: 0 10px 26px rgba(120, 72, 168, .38);
  --btn-shadow-hover: 0 16px 34px rgba(140, 88, 196, .46);
  --pick-shadow: 0 18px 44px rgba(140, 88, 196, .28);
  --mark-drop: 0 24px 50px rgba(0, 0, 0, .5);
  --loader-drop: 0 16px 28px rgba(0, 0, 0, .45);
  --fill-bg: #3a3319;
  --fill-ink: #f0dd93;
  --mark-frame: #8e64b8;
  --mark-glass-1: #2c2340;
  --mark-glass-2: #221a31;
  --mark-sand: #a583c8;
  --mark-glare: #e2cdf7;
  --mark-glare-a: .28;
  color-scheme: dark;
}

* { box-sizing: border-box; }

html { scroll-behavior: smooth; }

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--body);
  font-size: 17px;
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
}

/* height: auto обязательно: у снимков экрана проставлены атрибуты width и
   height, и без него картинка тянется на свои натуральные 1385 пикселей */
img { max-width: 100%; height: auto; display: block; }

a { color: var(--accent); text-decoration: none; }
a:hover { color: var(--accent-deep); text-decoration: underline; }

h1, h2, h3 {
  font-family: var(--display);
  color: var(--plum);
  font-weight: 700;
  line-height: 1.18;
  margin: 0 0 .5em;
}

/* Nunito шире Playfair, на крупных кеглях его стоит поджать */
h1 { font-size: clamp(2.3rem, 5.5vw, 3.7rem); letter-spacing: -.025em; }
h2 { font-size: clamp(1.7rem, 3.6vw, 2.4rem); letter-spacing: -.02em; }
h3 { font-size: 1.2rem; font-family: var(--body); font-weight: 700; }

p { margin: 0 0 1em; }

.wrap { width: min(1120px, 100% - 40px); margin-inline: auto; }
.narrow { width: min(760px, 100% - 40px); margin-inline: auto; }

/* ── Шапка ─────────────────────────────────────────────────────────────── */

/* Шапка матовая: сквозь неё видно, что под ней проезжает, но текст
   остаётся читаемым за счёт размытия. saturate добавлен затем, что при
   сильном blur цвета под стеклом выцветают и фон становится серым. */
.top {
  position: sticky;
  top: 0;
  z-index: 20;
  background: var(--top-glass);
  backdrop-filter: blur(18px) saturate(170%);
  -webkit-backdrop-filter: blur(18px) saturate(170%);
  border-bottom: 1px solid rgba(230, 223, 238, .8);
}

/* Без размытия полупрозрачная шапка превратилась бы в кашу из букв */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .top { background: var(--top-solid); }
}

.top .wrap {
  display: flex;
  align-items: center;
  gap: 26px;
  height: 68px;
}

.brand {
  display: flex;
  align-items: center;
  gap: 10px;
  font-family: var(--display);
  font-size: 1.25rem;
  color: var(--plum);
  font-weight: 600;
}
.brand:hover { text-decoration: none; }
.brand img { width: 26px; }

.top nav { margin-left: auto; display: flex; gap: 24px; align-items: center; }
/* Кнопку из этого правила исключаем: иначе оно перебивает её белый текст
   по специфичности, и «Скачать» становится тёмным на фиолетовом */
.top nav a:not(.btn) { color: var(--ink); font-size: .95rem; font-weight: 500; }
.top nav a:not(.btn):hover { color: var(--accent); text-decoration: none; }

@media (max-width: 760px) {
  .top nav a.hide-sm { display: none; }
}

/* ── Кнопки ────────────────────────────────────────────────────────────── */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 9px;
  padding: 14px 26px;
  border-radius: 999px;
  font-weight: 700;
  font-size: 1rem;
  border: 1px solid transparent;
  cursor: pointer;
  transition: transform .16s ease, box-shadow .16s ease, background .16s ease;
}
/* Увеличение, а не подъём. Подъём уводил нижний край кнопки из-под
   курсора: наведение терялось, кнопка возвращалась на место, наведение
   появлялось снова — и курсор мигал между рукой и стрелкой по нескольку
   раз в секунду. Кнопка, которая растёт, из-под курсора уйти не может. */
.btn:hover { text-decoration: none; transform: scale(1.035); }

.btn-main {
  background: var(--accent);
  color: var(--on-accent);
  box-shadow: var(--btn-shadow);
}
.btn-main:hover { background: var(--accent-deep); color: var(--on-accent); }

.btn-ghost {
  background: var(--surface);
  color: var(--plum);
  border-color: var(--line);
  box-shadow: var(--shadow-sm);
}
.btn-ghost:hover { color: var(--accent); }

.btn-sm { padding: 10px 20px; font-size: .92rem; }

/* ── Первый экран ──────────────────────────────────────────────────────── */

.hero { padding: 84px 0 60px; position: relative; overflow: hidden; }

.hero::before {
  content: "";
  position: absolute;
  inset: -180px -10% auto auto;
  width: 620px;
  height: 620px;
  background: radial-gradient(circle, var(--halo), transparent 62%);
  z-index: -1;
}

.hero .wrap {
  display: grid;
  grid-template-columns: 1.15fr .85fr;
  gap: 56px;
  align-items: center;
}

.eyebrow {
  display: inline-block;
  font-size: .82rem;
  letter-spacing: .09em;
  text-transform: uppercase;
  font-weight: 700;
  color: var(--accent);
  background: var(--lilac-soft);
  padding: 7px 15px;
  border-radius: 999px;
  margin-bottom: 20px;
}

.lead { font-size: 1.2rem; color: var(--muted); max-width: 30em; }

.hero-actions { display: flex; flex-wrap: wrap; gap: 14px; margin-top: 28px; }

.hero-art { display: flex; justify-content: center; }
.hero-art img { width: min(230px, 70%); filter: drop-shadow(var(--mark-drop)); }

@media (max-width: 900px) {
  .hero { padding-top: 56px; }
  .hero .wrap { grid-template-columns: 1fr; gap: 34px; }
  .hero-art { order: -1; }
  .hero-art img { width: 150px; }
}

/* ── Секции ────────────────────────────────────────────────────────────── */

section { padding: 72px 0; }

.section-head { max-width: 40em; margin-bottom: 44px; }
.section-head p { color: var(--muted); margin: 0; }

.cards { display: grid; gap: 22px; grid-template-columns: repeat(3, 1fr); }
.cards.two { grid-template-columns: repeat(2, 1fr); }

@media (max-width: 900px) { .cards, .cards.two { grid-template-columns: 1fr; } }

.card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 28px;
  box-shadow: var(--shadow-sm);
}

.card h3 { color: var(--plum); margin-bottom: .4em; }
.card p { color: var(--muted); font-size: .97rem; margin: 0; }

.card-mark {
  width: 44px;
  height: 44px;
  border-radius: 13px;
  background: var(--lilac-soft);
  display: flex;
  align-items: center;
  justify-content: center;
  margin-bottom: 16px;
  color: var(--accent);
}

/* Иконки Lucide вставлены кодом, а не картинкой: так они красятся текущим
   цветом и остаются чёткими на любом экране. Раньше тут стояли эмодзи, а их
   рисует система — на Windows, Android и айфоне они выходили разными. */
.card-mark svg { width: 22px; height: 22px; }

/* ── Снимки экрана ─────────────────────────────────────────────────────── */

/* minmax(0, 1fr), а не просто 1fr: снимок лежит в обёртке, и колонка
   брала за минимум натуральную ширину картинки — 620 пикселей. Колонки
   вылезали за контейнер, страница становилась шире экрана, и на телефоне
   из-под шапки выглядывала полоса фона. */
.shots {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 26px;
}
@media (max-width: 900px) {
  .shots { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* На телефоне две колонки давали снимки шириной с ноготь: разобрать на них
   было нечего, а подписи ломались по одному слову в строку. Поэтому здесь
   лента с прокруткой вбок — как в магазинах приложений. Соседний снимок
   выглядывает из-за края, и видно, что можно листать. */
@media (max-width: 700px) {
  .shots {
    display: flex;
    gap: 16px;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    /* Выходим за поля .wrap ровно на них: лента идёт от края до края
       экрана, но шире экрана не становится */
    margin-inline: -20px;
    padding: 4px 20px 16px;
    scroll-padding-inline: 20px;
    scrollbar-width: none;
  }
  .shots::-webkit-scrollbar { display: none; }

  .shot-slot {
    flex: 0 0 78%;
    scroll-snap-align: center;
  }
}

.shot {
  border-radius: 26px;
  overflow: hidden;
  border: 1px solid var(--line);
  background: var(--surface);
  box-shadow: var(--shadow);
}
.shot img { width: 100%; }
.shot figcaption {
  padding: 14px 16px 18px;
  font-family: var(--display);
  font-size: 1.02rem;
  font-weight: 700;
  letter-spacing: -.01em;
  color: var(--plum);
  text-align: center;
}

/* ── Тарифы ────────────────────────────────────────────────────────────── */

.plans { display: grid; gap: 22px; grid-template-columns: repeat(3, 1fr); }
@media (max-width: 900px) { .plans { grid-template-columns: 1fr; } }

.plan {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 30px;
  display: flex;
  flex-direction: column;
  box-shadow: var(--shadow-sm);
}

.plan.pick {
  border-color: var(--accent);
  box-shadow: var(--pick-shadow);
  position: relative;
}

.plan.pick::after {
  content: "Чаще выбирают";
  position: absolute;
  top: -13px;
  left: 30px;
  background: var(--accent);
  color: var(--on-accent);
  font-size: .74rem;
  font-weight: 700;
  letter-spacing: .05em;
  padding: 5px 13px;
  border-radius: 999px;
}

.plan h3 { font-size: 1.3rem; font-family: var(--display); font-weight: 700; }

.price {
  font-family: var(--display);
  font-size: 2.6rem;
  color: var(--plum);
  line-height: 1;
  margin: 6px 0 4px;
}
.price span { font-family: var(--body); font-size: .95rem; color: var(--muted); }

.plan ul { list-style: none; margin: 22px 0 26px; padding: 0; }
.plan li {
  position: relative;
  padding-left: 26px;
  margin-bottom: 11px;
  font-size: .96rem;
}
.plan li::before {
  content: "";
  position: absolute;
  left: 6px;
  top: .55em;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--lilac);
}
.plan li.yes::before { background: var(--accent); }
.plan .btn { margin-top: auto; }

/* ── Вопросы ───────────────────────────────────────────────────────────── */

details {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 16px;
  padding: 18px 22px;
  margin-bottom: 12px;
}
details[open] { box-shadow: var(--shadow-sm); }
summary {
  cursor: pointer;
  font-weight: 600;
  color: var(--plum);
  list-style: none;
}
summary::-webkit-details-marker { display: none; }
details p { margin: 14px 0 0; color: var(--muted); font-size: .96rem; }

/* ── Полоса призыва ────────────────────────────────────────────────────── */

.strip {
  background: linear-gradient(135deg, var(--lilac-soft), var(--lilac));
  border-radius: 30px;
  padding: 54px 40px;
  text-align: center;
}
.strip h2 { margin-bottom: .3em; }
.strip p { color: var(--ink); max-width: 34em; margin-inline: auto; }

/* ── Подвал ────────────────────────────────────────────────────────────── */

footer {
  border-top: 1px solid var(--line);
  padding: 54px 0 40px;
  font-size: .92rem;
  color: var(--muted);
}

.foot-grid {
  display: flex;
  flex-wrap: wrap;
  gap: 30px 78px;
  margin-bottom: 34px;
}

.foot-grid h4 {
  font-size: .82rem;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--plum);
  margin: 0 0 14px;
}
.foot-grid ul { list-style: none; margin: 0; padding: 0; }
.foot-grid li { margin-bottom: 9px; }
.foot-grid a { color: var(--ink); }

.requisites { line-height: 1.9; }
.fill { background: var(--fill-bg); padding: 0 6px; border-radius: 4px; color: var(--fill-ink); }

/* ── Юридические страницы ──────────────────────────────────────────────── */

.doc { padding: 60px 0 80px; }
.doc h1 { font-size: clamp(1.9rem, 4vw, 2.8rem); margin-bottom: .3em; }
.doc .updated { color: var(--muted); font-size: .92rem; margin-bottom: 40px; }
.doc h2 { font-size: 1.35rem; margin: 42px 0 .5em; }
.doc h3 { margin: 28px 0 .4em; }
.doc ul, .doc ol { padding-left: 22px; }
.doc li { margin-bottom: 8px; }

.doc table {
  width: 100%;
  border-collapse: collapse;
  margin: 20px 0;
  font-size: .94rem;
}
.doc th, .doc td {
  text-align: left;
  padding: 11px 14px;
  border-bottom: 1px solid var(--line);
  vertical-align: top;
}
.doc th { color: var(--plum); font-weight: 700; }

.callout {
  background: var(--lilac-soft);
  border-radius: 16px;
  padding: 20px 24px;
  margin: 26px 0;
}
.callout p:last-child { margin-bottom: 0; }

.back {
  display: inline-block;
  margin-bottom: 26px;
  font-size: .92rem;
  color: var(--muted);
}

/* ── Страница возврата после оплаты ────────────────────────────────────── */

.done {
  min-height: 78vh;
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: 40px 0;
}
.done img { width: 96px; margin: 0 auto 26px; }
.done .lead { margin-inline: auto; }

/* ── Заставка загрузки ─────────────────────────────────────────────────── */

/* Песочные часы с фирменного знака: та же рамка со скруглёнными углами, те
   же колбы с узкой перемычкой. Песок пересыпается сверху вниз, потом часы
   переворачиваются и всё повторяется. Переворот ровно на 180°, а колбы
   симметричны — поэтому в конце цикла картинка совпадает с началом и стык
   не виден. Блик на стекле в переворот не входит: он нарисован вне
   вращающейся группы и всегда остаётся сверху.

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

.loader {
  position: fixed;
  inset: 0;
  z-index: 100;
  display: flex;
  align-items: center;
  justify-content: center;
  background:
    radial-gradient(60% 50% at 50% 42%, var(--lilac-soft), transparent 70%),
    var(--paper);
  animation: loaderFailsafe .5s ease 4s forwards;
}

/* Уходит не просто прозрачностью, а с лёгким наездом: так переход к первому
   экрану читается как продолжение, а не как смена картинки */
.loader.is-done {
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transform: scale(1.06);
  transition:
    opacity .5s ease,
    transform .5s cubic-bezier(.4, 0, .2, 1),
    visibility 0s linear .5s;
}

@keyframes loaderFailsafe {
  to { opacity: 0; visibility: hidden; pointer-events: none; }
}

.loader-in { text-align: center; }

.loader-word {
  display: block;
  margin-top: 22px;
  font-family: var(--display);
  font-size: 1.45rem;
  color: var(--plum);
  letter-spacing: .06em;
}

/* Полоска не показывает долю загруженного — её никто не знает. Она просто
   говорит, что работа идёт */
.loader-track {
  display: block;
  width: 132px;
  height: 3px;
  margin: 18px auto 0;
  border-radius: 3px;
  background: var(--lilac);
  overflow: hidden;
}
.loader-track span {
  display: block;
  width: 45%;
  height: 100%;
  border-radius: 3px;
  background: var(--accent);
  animation: loaderSweep 1.4s cubic-bezier(.65, 0, .35, 1) infinite;
}
@keyframes loaderSweep {
  0% { transform: translateX(-105%); }
  100% { transform: translateX(325%); }
}

/* Размер задан стилем, а не только атрибутами: в разметке они нужны, чтобы
   место под знак было занято до того, как доедет css, и заставка не дёрнулась */
.loader-glass {
  display: block;
  margin: 0 auto;
  width: 112px;
  height: 183px;
  filter: drop-shadow(var(--loader-drop));
}

.tf-frame { fill: var(--mark-frame); }
.tf-body { fill: url(#tfBody); }
.tf-sand-top, .tf-sand-bottom, .tf-grain { fill: var(--mark-sand); }
.tf-glare { fill: var(--mark-glare); opacity: var(--mark-glare-a); }

/* transform-box: view-box — иначе сдвиг считался бы от рамки самой фигуры,
   а не от системы координат svg, и песок уехал бы мимо колбы */
.tf-glass, .tf-sand-top, .tf-sand-bottom, .tf-grain { transform-box: view-box; }

.tf-glass {
  transform-origin: 60px 98px;
  animation: tfFlip 3.6s cubic-bezier(.66, 0, .3, 1) infinite;
}
.tf-sand-top { animation: tfDrain 3.6s ease-in-out infinite; }
.tf-sand-bottom { animation: tfFill 3.6s ease-in-out infinite; }

/* Песчинки падают по одной дорожке, но с разным разбегом — получается
   непрерывная струйка, а не мигающая точка */
.tf-grain {
  transform-origin: 60px 98px;
  animation: tfGrain 1.05s cubic-bezier(.4, 0, .8, .4) infinite;
}
.tf-grain-2 { animation-delay: .35s; }
.tf-grain-3 { animation-delay: .7s; }
.tf-fall { animation: tfStream 3.6s linear infinite; }

@keyframes tfDrain {
  0%, 4% { transform: translateY(0); }
  74%, 100% { transform: translateY(66px); }
}
@keyframes tfFill {
  0%, 4% { transform: translateY(58px); }
  74%, 100% { transform: translateY(0); }
}
@keyframes tfFlip {
  0%, 80% { transform: rotate(0deg); }
  100% { transform: rotate(180deg); }
}
@keyframes tfGrain {
  0% { transform: translateY(-4px) scaleY(.7); opacity: 0; }
  15%, 85% { opacity: 1; }
  100% { transform: translateY(44px) scaleY(1.15); opacity: 0; }
}
@keyframes tfStream {
  0%, 5% { opacity: 0; }
  10%, 70% { opacity: 1; }
  75%, 100% { opacity: 0; }
}

/* ── Движение на странице ──────────────────────────────────────────────── */

/* Первый экран собирается по частям: сначала подпись, потом заголовок и так
   далее. Задержки маленькие — это должно читаться как «страница появилась»,
   а не как представление. */
.hero .eyebrow,
.hero h1,
.hero .lead,
.hero-actions,
.hero-art { animation: riseIn .7s cubic-bezier(.2, .7, .3, 1) both; }

.hero h1 { animation-delay: .06s; }
.hero .lead { animation-delay: .12s; }
.hero-actions { animation-delay: .18s; }
.hero-art { animation-delay: .1s; }

@keyframes riseIn {
  from { opacity: 0; transform: translateY(16px); }
  to { opacity: 1; transform: none; }
}

/* Знак едва заметно покачивается — как настоящие часы на подставке */
.hero-art img { animation: float 7s ease-in-out infinite; }
@keyframes float {
  0%, 100% { transform: translateY(0) rotate(0deg); }
  50% { transform: translateY(-9px) rotate(-1.2deg); }
}

/* Сиреневое пятно за первым экраном медленно дышит */
.hero::before { animation: breathe 16s ease-in-out infinite; }
@keyframes breathe {
  0%, 100% { transform: scale(1); opacity: 1; }
  50% { transform: scale(1.12) translate(-14px, 10px); opacity: .75; }
}

/* Блоки всплывают, когда до них долистали. Прячем их только при живом
   скрипте: без него класс is-in никто не поставит, и страница осталась бы
   пустой. Задержку внутри ряда ставит скрипт через --delay. */
.js .reveal:not(.is-in) {
  opacity: 0;
  transform: translateY(22px);
}
.reveal {
  transition:
    opacity .6s ease var(--delay, 0ms),
    transform .6s cubic-bezier(.2, .7, .3, 1) var(--delay, 0ms);
}

/* Шапка отделяется тенью, только когда под неё что-то уехало */
.top { transition: box-shadow .25s ease, background .25s ease; }
.top.is-stuck { box-shadow: var(--shadow-stuck); }

/* Карточки и снимки приподнимаются под курсором */
.card, .plan {
  /* Короче, чем у снимков: карточка ещё и наклоняется за курсором, и
     долгая плавность там читается как отставание от мыши */
  transition:
    transform .18s ease-out,
    box-shadow .28s ease,
    border-color .28s ease;
}
.shot {
  transition:
    transform .28s cubic-bezier(.2, .7, .3, 1),
    box-shadow .28s ease,
    border-color .28s ease;
}
.card:hover, .plan:hover {
  border-color: var(--lilac);
  box-shadow: var(--shadow);
}
.card-mark {
  transition: transform .35s cubic-bezier(.2, .7, .3, 1), background .28s ease;
}
.card:hover .card-mark {
  transform: translateZ(18px) translateY(-2px) rotate(-6deg) scale(1.06);
  background: var(--lilac);
}

.shot:hover { transform: translateY(-6px); box-shadow: var(--shadow-hover); }
/* Картинку внутри чуть приближаем — рамка обрезает лишнее сама */
.shot img { transition: transform .5s cubic-bezier(.2, .7, .3, 1); }
.shot:hover img { transform: scale(1.03); }

/* Нажатие должно чувствоваться: кнопка проседает под пальцем */
.btn:active { transform: scale(.97); }
.btn-main:hover { box-shadow: var(--btn-shadow-hover); }

/* Ссылки в тексте подчёркиваются растущей линией вместо резкого underline */
p a:not(.btn), .foot-grid a:not(.brand), .requisites a {
  text-decoration: none;
  background-image: linear-gradient(currentColor, currentColor);
  background-size: 0 1px;
  background-position: 0 100%;
  background-repeat: no-repeat;
  transition: background-size .3s cubic-bezier(.2, .7, .3, 1), color .2s ease;
}
p a:not(.btn):hover, .foot-grid a:not(.brand):hover, .requisites a:hover {
  text-decoration: none;
  background-size: 100% 1px;
  color: var(--accent);
}

/* Полоса призыва: градиент медленно переливается */
.strip {
  background: linear-gradient(135deg, var(--lilac-soft), var(--lilac), var(--lilac-soft));
  background-size: 220% 220%;
  animation: stripShift 18s ease-in-out infinite;
}
@keyframes stripShift {
  0%, 100% { background-position: 0% 50%; }
  50% { background-position: 100% 50%; }
}

/* Значок «частых вопросов»: две палочки, вертикальная отъезжает — плюс
   плавно становится минусом. Раньше символ просто подменялся текстом, и
   переход был рывком. Разметку это не трогает: обе палочки рисуются
   псевдоэлементами, так что работает на всех страницах сразу. */
summary { position: relative; padding-right: 34px; }

summary::before,
summary::after {
  content: "";
  position: absolute;
  right: 0;
  top: 50%;
  float: none;
  width: 14px;
  height: 2px;
  margin-top: -1px;
  border-radius: 2px;
  background: var(--accent);
  transition: transform .3s cubic-bezier(.2, .7, .3, 1);
}
summary::after { transform: rotate(90deg); }
details[open] summary::after { transform: rotate(0deg); }

/* Ответ разворачивается, а не выпрыгивает */
details[open] > p { animation: faqOpen .32s ease both; }
@keyframes faqOpen {
  from { opacity: 0; transform: translateY(-6px); }
  to { opacity: 1; transform: none; }
}

/* Видимая рамка при переходе с клавиатуры — её раньше не было вовсе */
a:focus-visible, summary:focus-visible, .btn:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
  border-radius: 6px;
}

/* Кому движение мешает — тому неподвижная страница. Здесь важно не просто
   убрать анимации, а не оставить блоки спрятанными навсегда. */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }

  *, *::before, *::after {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
  }

  .loader { animation-delay: 1.2s; }
  .tf-sand-top { transform: translateY(33px); }
  .tf-sand-bottom { transform: translateY(29px); }
  .tf-fall { opacity: 0; }
  .loader-track span { width: 100%; }

  .js .reveal:not(.is-in) { opacity: 1; transform: none; }
}

/* ── Снимки разъезжаются по прокрутке ──────────────────────────────────── */

/* Пока блок подъезжает снизу, боковые снимки лежат стопкой под средним и
   расходятся в стороны по мере прокрутки. Долю пройденного (0…1) считает
   скрипт и кладёт в --p на .shots.

   По умолчанию --p: 1 — то есть «уже разъехались». Это важно: без скрипта
   и при выключенном движении раздел должен выглядеть обычной тройкой
   снимков, а не кучей друг на друге. */
.shots { --p: 1; }

.shot-slot { --dir: 0; }
.shots > .shot-slot:first-child { --dir: 1; z-index: 1; }
.shots > .shot-slot:last-child { --dir: -1; z-index: 1; }
.shots > .shot-slot:nth-child(2) { z-index: 2; }

@media (min-width: 901px) {
  .shot-slot {
    /* Своего transition нет намеренно: положение задаёт прокрутка, и
       сглаживание превратилось бы в отставание от пальца */
    transform:
      translateX(calc(var(--dir) * (1 - var(--p)) * 64%))
      rotate(calc(var(--dir) * (1 - var(--p)) * -5deg))
      scale(calc(1 - (1 - var(--p)) * 0.13));
    opacity: calc(.34 + .66 * var(--p));
  }
  /* Средний не уезжает, только чуть подрастает — он остаётся точкой,
     из которой всё расходится */
  .shots > .shot-slot:nth-child(2) {
    transform: scale(calc(.94 + .06 * var(--p)));
    opacity: calc(.55 + .45 * var(--p));
  }
}

/* ── Наклон карточек за курсором ───────────────────────────────────────── */

/* Подъём под курсором и наклон живут в разных переменных, иначе одно
   правило затирало бы другое: transform у элемента один. */
.card, .plan {
  --lift: 0px;
  --rx: 0deg;
  --ry: 0deg;
  transform:
    perspective(760px)
    rotateX(var(--rx))
    rotateY(var(--ry))
    translateY(var(--lift));
  transform-style: preserve-3d;
}
.card:hover, .plan:hover { --lift: -5px; }

/* Значок в карточке приподнимается над ней — ради этого и включён 3d */
.card-mark { transform: translateZ(18px); }

/* ── Бегущий блик на главной кнопке ────────────────────────────────────── */

.btn-main { position: relative; overflow: hidden; }
.btn-main::after {
  content: "";
  position: absolute;
  top: 0;
  left: -60%;
  width: 45%;
  height: 100%;
  background: linear-gradient(
    100deg,
    transparent,
    rgba(255, 255, 255, .38),
    transparent
  );
  transform: skewX(-18deg);
  pointer-events: none;
}
.btn-main:hover::after { animation: shine .75s ease-out; }
@keyframes shine {
  to { left: 120%; }
}

/* ── Полоска прочитанного ──────────────────────────────────────────────── */

/* Тонкая линия по нижнему краю шапки: показывает, сколько страницы позади.
   Долю кладёт скрипт в --read. */
.top::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: -1px;
  height: 2px;
  background: linear-gradient(
    90deg,
    var(--accent),
    var(--accent-half) 78%,
    var(--accent-none)
  );
  opacity: .85;
  transform: scaleX(var(--read, 0));
  transform-origin: 0 50%;
}

/* ── Знак на первом экране ─────────────────────────────────────────────── */

/* Обёртка ходит за курсором, картинка внутри сама по себе покачивается */
.hero-art-in {
  display: block;
  transition: transform .5s cubic-bezier(.2, .7, .3, 1);
  will-change: transform;
}

/* ── Цена набегает ─────────────────────────────────────────────────────── */

/* Пока цифры бегут, ширина скачет и строка дёргается — поэтому цифры
   моноширинные по месту */
.price { font-variant-numeric: tabular-nums; }

@media (prefers-reduced-motion: reduce) {
  .shots { --p: 1; }
  .card, .plan { --rx: 0deg; --ry: 0deg; }
  .top::after { display: none; }
  .btn-main:hover::after { animation: none; }
}

/* ── Знак в шапке переворачивается ─────────────────────────────────────── */

/* Единственная анимация здесь, которая не выдумана: песочные часы для того
   и нужны, чтобы их переворачивать. Наводите — и они переворачиваются. */
.brand img { transition: transform .65s cubic-bezier(.5, 0, .2, 1); }
.brand:hover img { transform: rotate(180deg); }

/* ── Иконки карточек прорисовываются ───────────────────────────────────── */

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

   120 взято с запасом: самая длинная фигура здесь — рамка блокнота, у неё
   периметр 72. Будь число меньше длины фигуры, вместо линии вышел бы
   пунктир.

   Прячем только при живом скрипте: без него класс is-in никто не поставит,
   и иконки остались бы невидимыми навсегда. */
.js .card.reveal .card-mark svg > * {
  stroke-dasharray: 120;
  stroke-dashoffset: 120;
}
.js .card.reveal.is-in .card-mark svg > * {
  animation: drawIcon .85s cubic-bezier(.4, 0, .2, 1) forwards;
}

/* Фигуры выводятся не разом, а следом друг за другом */
.card-mark svg > *:nth-child(2) { animation-delay: .06s; }
.card-mark svg > *:nth-child(3) { animation-delay: .12s; }
.card-mark svg > *:nth-child(4) { animation-delay: .18s; }
.card-mark svg > *:nth-child(5) { animation-delay: .24s; }
.card-mark svg > *:nth-child(6) { animation-delay: .30s; }
.card-mark svg > *:nth-child(7) { animation-delay: .36s; }
.card-mark svg > *:nth-child(8) { animation-delay: .42s; }

@keyframes drawIcon {
  to { stroke-dashoffset: 0; }
}

/* ── Росчерк под заголовком раздела ────────────────────────────────────── */

/* Короткая черта, а не во всю ширину: это акцент, а не подчёркивание.
   Прочерчивается, когда до раздела долистали. */
.section-head h2 {
  position: relative;
  display: inline-block;
  padding-bottom: .16em;
}
.section-head h2::after {
  content: "";
  position: absolute;
  left: 0;
  bottom: 0;
  width: 54px;
  height: 3px;
  border-radius: 3px;
  background: linear-gradient(90deg, var(--accent), var(--accent-half));
  transform: scaleX(0);
  transform-origin: 0 50%;
  transition: transform .6s cubic-bezier(.2, .7, .3, 1) .12s;
}
.section-head.is-in h2::after { transform: scaleX(1); }

/* Без скрипта класса is-in не будет, и росчерк не появился бы вовсе */
html:not(.js) .section-head h2::after { transform: scaleX(1); }

/* ── Ответ в вопросах разворачивается по высоте ────────────────────────── */

/* Раньше ответ проявлялся, но карточка под ним прыгала на свою новую
   высоту рывком. Здесь анимируется сама высота.

   allow-keywords нужен затем, что высота едет от нуля к auto, а такое
   браузер по умолчанию анимировать не умеет. Где это не поддержано,
   останется прежнее поведение — открывается сразу, и ничего не ломается. */
:root { interpolate-size: allow-keywords; }

details::details-content {
  block-size: 0;
  overflow: hidden;
  transition:
    block-size .34s cubic-bezier(.2, .7, .3, 1),
    content-visibility .34s allow-discrete;
}
details[open]::details-content { block-size: auto; }

@media (prefers-reduced-motion: reduce) {
  .js .card.reveal .card-mark svg > * {
    stroke-dasharray: none;
    stroke-dashoffset: 0;
  }
  .section-head h2::after { transform: scaleX(1); }
  details::details-content { block-size: auto; overflow: visible; }
}

/* ── Свет от курсора ───────────────────────────────────────────────────── */

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

   z-index: -1 — ключевое. Слой лежит на фоне страницы, но под содержимым:
   свет ложится на фон между карточками и в отступах разделов, а сами
   карточки его загораживают, как предметы загораживают свет. Будь слой
   сверху, он размывал бы текст.

   Размер задан элементу, а не градиенту: двигать элемент через transform
   браузер умеет не трогая раскладку, а пересчитывать позицию градиента на
   каждый кадр — нет. */
.glow {
  position: fixed;
  top: 0;
  left: 0;
  width: 620px;
  height: 620px;
  margin: -310px 0 0 -310px;
  z-index: -1;
  pointer-events: none;
  border-radius: 50%;
  background: radial-gradient(
    circle closest-side,
    var(--glow-core),
    var(--glow-mid) 46%,
    var(--glow-edge) 72%
  );
  opacity: 0;
  /* Отставание нарочное: свет догоняет курсор, а не приклеен к нему —
     так он читается как лампа, а не как курсор нестандартной формы */
  transition: opacity .6s ease, transform .28s cubic-bezier(.2, .7, .3, 1);
  will-change: transform;
}

.glow.is-on { opacity: 1; }

/* Пальцем лампу не поводишь, а на телефоне пятно замерло бы там, где
   человек последний раз коснулся экрана */
@media (hover: none), (pointer: coarse) {
  .glow { display: none; }
}

@media (prefers-reduced-motion: reduce) {
  .glow { display: none; }
}

/* Колбы в заставке залиты градиентом из разметки, а его остановки — тоже
   свойства css, поэтому тема достаёт и их */
#tfBody stop:first-child { stop-color: var(--mark-glass-1); }
#tfBody stop:last-child { stop-color: var(--mark-glass-2); }

/* ── Часы на первом экране ─────────────────────────────────────────────

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

.hero-stage {
  position: relative;
  width: min(330px, 88%);
  aspect-ratio: .78;
  display: flex;
  align-items: center;
  justify-content: center;
}

.hero-stage .hero-art-in {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: opacity .5s ease;
}

.hero-glass {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  opacity: 0;
  transition: opacity .8s ease;
  /* Крутить часы можно мышью, но палец по ним должен листать страницу */
  touch-action: pan-y;
  cursor: grab;
  /* Протяжка по часам — это вращение, а не выделение текста вокруг */
  user-select: none;
  -webkit-user-select: none;
}
.hero-glass:active { cursor: grabbing; }

.hero-art.is-3d .hero-glass { opacity: 1; }
.hero-art.is-3d .hero-art-in { opacity: 0; pointer-events: none; }

@media (max-width: 900px) {
  .hero-stage { width: min(215px, 60%); }
}

/* ── Шапка на телефоне ─────────────────────────────────────────────────

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

@media (max-width: 560px) {
  .top .wrap { gap: 12px; }
  .top nav { gap: 12px; }
  .top nav a:not(.btn) { display: none; }
  .theme-switch { width: 34px; height: 34px; }
  .theme-switch svg { width: 17px; height: 17px; }
  .btn-sm { padding: 9px 16px; font-size: .88rem; }
}

/* ── Переключатель темы ────────────────────────────────────────────────

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

.theme-switch {
  width: 38px;
  height: 38px;
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  border: 1px solid var(--line);
  border-radius: 50%;
  background: var(--surface);
  color: var(--accent);
  cursor: pointer;
  box-shadow: var(--shadow-sm);
  transition: transform .2s ease, border-color .2s ease, background .25s ease;
}
.theme-switch:hover { transform: scale(1.08); border-color: var(--lilac); }
.theme-switch:active { transform: scale(.94); }
.theme-switch svg { width: 19px; height: 19px; overflow: visible; }

.theme-switch .rays,
.theme-switch .bite {
  transform-box: view-box;
  transform-origin: center;
  transition:
    transform .55s cubic-bezier(.36, 1.32, .5, 1),
    opacity .35s ease;
}

/* Светлая тема: вырез отведён за край диска, лучи на месте */
.theme-switch .bite { transform: translate(9.5px, -9.5px); }
.theme-switch .disc { fill: currentColor; }

[data-theme="dark"] .theme-switch .bite { transform: translate(4.6px, -4.6px); }
[data-theme="dark"] .theme-switch .rays { transform: scale(.4) rotate(40deg); opacity: 0; }

/* ── Смена темы ────────────────────────────────────────────────────────

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

   Переход именно круговой и от кнопки: так видно, что тему поменяли
   вы и вот этим нажатием. */

.theming,
.theming * {
  transition:
    background-color .34s ease,
    border-color .34s ease,
    color .34s ease,
    fill .34s ease,
    box-shadow .34s ease !important;
}

@media (prefers-reduced-motion: no-preference) {
  ::view-transition-old(root),
  ::view-transition-new(root) {
    animation: none;
    mix-blend-mode: normal;
  }
  /* Новая тема ложится поверх старой: круг вырезается по ней */
  ::view-transition-old(root) { z-index: 1; }
  ::view-transition-new(root) { z-index: 2; }
}

@media (prefers-reduced-motion: reduce) {
  .theme-switch .rays,
  .theme-switch .bite { transition: none; }
}
