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

/* === Появление заголовка ===
   Общее правило для всех вариантов: текст не меняет геометрию, поэтому
   строки под заголовком никуда не скачут. */
.fx-h1 .fx-line { display: inline-block; }
.fx-h1 .fx-word { display: inline-block; white-space: nowrap; }
.fx-h1 .fx-ch { display: inline-block; }

/* Вариант «lines»: строки выезжают снизу по очереди */
.fx-lines .fx-line {
  opacity: 0;
  transform: translateY(0.34em);
  animation: fx-line-in .72s cubic-bezier(.22,.68,.35,1) forwards;
}
@keyframes fx-line-in {
  to { opacity: 1; transform: translateY(0); }
}

/* Вариант «wipe»: строка открывается шторкой слева направо */
.fx-wipe .fx-line {
  clip-path: inset(0 100% 0 0);
  animation: fx-wipe-in .78s cubic-bezier(.4,.5,.2,1) forwards;
}
@keyframes fx-wipe-in {
  to { clip-path: inset(0 -2% 0 0); }
}

/* Вариант «letters»: буквы проступают из размытия */
.fx-letters .fx-ch {
  opacity: 0;
  filter: blur(7px);
  animation: fx-ch-in .5s ease-out forwards;
}
@keyframes fx-ch-in {
  to { opacity: 1; filter: blur(0); }
}
/* Проявились - убираем фильтр и анимацию совсем: любой ненулевой filter
   (даже blur(0)) держит букву отдельным слоем, и текст теряет субпиксельное
   сглаживание - заголовок выглядит мягким рядом с обычным текстом. */
.fx-letters.fx-letters-done .fx-ch {
  opacity: 1;
  filter: none;
  animation: none;
}
/* На телефоне размытие четырёх десятков букв разом - дорогая операция:
   каждая буква становится отдельным слоем. Там проявляем подъёмом. */
@media (max-width: 900px) {
  .fx-letters .fx-ch {
    filter: none;
    transform: translateY(.2em);
    animation-name: fx-ch-in-lite;
  }
  @keyframes fx-ch-in-lite {
    to { opacity: 1; transform: translateY(0); }
  }
}

/* Вариант «sweep»: текст на месте, по нему проходит световая волна */
.fx-sweep .fx-line {
  background-image: linear-gradient(100deg,
    currentColor 0 38%, #fff 46%, #ff8080 52%, currentColor 60% 100%);
  background-size: 260% 100%;
  background-position: 100% 0;
  -webkit-background-clip: text;
          background-clip: text;
  -webkit-text-fill-color: transparent;
  animation: fx-sweep-run 1.5s .25s ease-out forwards;
}
@keyframes fx-sweep-run {
  to { background-position: 0 0; }
}

/* Вариант «decode»: буквы расшифровываются.
   Ширина каждой буквы прибита из JS по её финальному виду - строка не
   перекомпоновывается, и блок под заголовком стоит на месте. */
.fx-decode .fx-ch {
  text-align: center;
  color: var(--text-2);
  transition: color .25s;
}
.fx-decode .fx-ch.is-set { color: inherit; }
.fx-decode .fx-ch.accent.is-set { color: var(--accent); }

@media (prefers-reduced-motion: reduce) {
  .fx-h1 .fx-line, .fx-h1 .fx-ch { animation: none !important; opacity: 1 !important; filter: none !important; clip-path: none !important; }
  .fx-sweep .fx-line { -webkit-text-fill-color: currentColor; background: none; }
}

/* === Магнитная кнопка: тянется к курсору === */
.btn.is-magnetic {
  will-change: transform;
  /* базовый transition: all мешает магниту - для transform задаём свой */
  transition: background .2s, color .2s, box-shadow .2s, transform .18s ease-out;
}
.btn.is-magnetic.is-pulled {
  transition: background .2s, color .2s, box-shadow .2s;
}

/* === Живая рамка вокруг цены === */
/* Угол крутится через @property; где его нет - рамка просто статична. */
@property --price-ang {
  syntax: '<angle>';
  initial-value: 0deg;
  inherits: false;
}

.price-bracket.is-live { position: relative; }
.price-bracket.is-live::before,
.price-bracket.is-live::after {
  content: '';
  position: absolute;
  /* Боковой вылет ОБЯЗАН быть меньше отступа .container, иначе рамка торчит за
     край страницы. Было -34px при отступе 32 - страница становилась на пару
     пикселей шире экрана, а на телефоне (отступ 20) сразу на 14, и её можно
     было таскать пальцем вбок. Разбор 03.09.2026: именно этот блок держал
     переполнение, оттого «результаты» и не ездили - там блока цены нет. */
  inset: -22px -28px;
  pointer-events: none;
}
@media (max-width: 767px) {
  /* На телефоне .container сужается до отступа 20px - рамке оставляем 16. */
  .price-bracket.is-live::before,
  .price-bracket.is-live::after { inset: -18px -16px; }
}
.price-bracket.is-live::before {
  border: 1px solid rgba(220, 38, 38, .28);
}
/* Бегущий по периметру блик: заливка обрезается маской до толщины рамки. */
.price-bracket.is-live::after {
  padding: 1px;
  background: conic-gradient(from var(--price-ang),
    transparent 0 58%, rgba(220,38,38,.85) 74%, rgba(255,120,120,1) 80%, transparent 90%);
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
          mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
          mask-composite: exclude;
  animation: price-ang-spin 6s linear infinite;
}
@keyframes price-ang-spin { to { --price-ang: 360deg; } }

/* === Карточка, связанная с узлом схемы === */
/* Наведение подсвечивает узел на фоне - и сама карточка отзывается. */
[data-node] { transition: border-color .25s, background-color .25s; }
[data-node].is-linked {
  border-color: rgba(220, 38, 38, .55) !important;
}

/* === Счётчик в блоке статистики === */
.stat-num.is-counting { font-variant-numeric: tabular-nums; }

@media (prefers-reduced-motion: reduce) {
  .btn.is-magnetic { transition: none; transform: none !important; }
  .price-bracket.is-live::after { animation: none; }
}
