/* ============================================================
   spirit-dv.ru — эффекты и движение

   Принципы:
   1. Анимируем ТОЛЬКО transform / opacity / filter — эти свойства
      считает компоситор GPU, лейаут не пересчитывается.
   2. Scroll-driven анимации — нативные (animation-timeline).
      Браузер крутит их вне主 потока, JS не участвует вообще.
   3. Fallback для Safari/Firefox — IntersectionObserver на ~700 байт.
   4. Всё глушится через prefers-reduced-motion.
   ============================================================ */

/* ============================================================
   1. ЖИВОЙ ГРАДИЕНТНЫЙ ФОН
   Медленно дышащая мешевая заливка. Один слой, transform-анимация,
   перерисовки нет.
   ============================================================ */
.mesh {
  position: absolute;
  inset: -20%;
  background: var(--gr-mesh);
  filter: blur(20px);
  will-change: transform;
  animation: mesh-drift 28s var(--ease-io) infinite alternate;
  pointer-events: none;
}

@keyframes mesh-drift {
  0%   { transform: translate3d(0, 0, 0) scale(1); }
  50%  { transform: translate3d(2%, -1.5%, 0) scale(1.06); }
  100% { transform: translate3d(-1.5%, 1%, 0) scale(1.03); }
}

/* Зернистая плёнка — убирает «пластик» с больших градиентов */
.grain::after {
  content: "";
  position: absolute;
  inset: 0;
  background-image: var(--noise);
  opacity: 0.35;
  mix-blend-mode: overlay;
  pointer-events: none;
}

/* ============================================================
   2. ГЕРОЙ: построчный выезд заголовка
   Каждая строка в маске, выезжает снизу со сдвигом по времени.
   Работает на load, без скролла и без JS.
   ============================================================ */
.line-mask { display: block; overflow: hidden; padding-bottom: 0.08em; }

.line-mask > span {
  display: block;
  will-change: transform;
  animation: line-up 1.05s var(--ease) both;
}

.line-mask:nth-of-type(1) > span { animation-delay: 0.05s; }
.line-mask:nth-of-type(2) > span { animation-delay: 0.16s; }
.line-mask:nth-of-type(3) > span { animation-delay: 0.27s; }

@keyframes line-up {
  from { transform: translate3d(0, 105%, 0); }
  to   { transform: translate3d(0, 0, 0); }
}

/* Мягкое проявление остального героя */
.fade-up {
  animation: fade-up 1s var(--ease) both;
}
.fade-up--1 { animation-delay: 0.38s; }
.fade-up--2 { animation-delay: 0.48s; }
.fade-up--3 { animation-delay: 0.58s; }

@keyframes fade-up {
  from { opacity: 0; transform: translate3d(0, 1.5rem, 0); }
  to   { opacity: 1; transform: none; }
}

/* ============================================================
   3. SCROLL-DRIVEN: нативные анимации по прокрутке
   Ключевая часть. Ни строчки JS.
   ============================================================ */
@supports (animation-timeline: view()) {

  /* Появление блока по мере входа в вьюпорт */
  [data-anim="rise"] {
    animation: rise linear both;
    animation-timeline: view();
    animation-range: entry 0% entry 60%;
  }
  @keyframes rise {
    from { opacity: 0; transform: translate3d(0, 3rem, 0); }
    to   { opacity: 1; transform: none; }
  }

  /* Выезд сбоку — для парных блоков */
  [data-anim="slide-l"] {
    animation: slide-l linear both;
    animation-timeline: view();
    animation-range: entry 0% entry 65%;
  }
  @keyframes slide-l {
    from { opacity: 0; transform: translate3d(-2.5rem, 0, 0); }
    to   { opacity: 1; transform: none; }
  }

  [data-anim="slide-r"] {
    animation: slide-r linear both;
    animation-timeline: view();
    animation-range: entry 0% entry 65%;
  }
  @keyframes slide-r {
    from { opacity: 0; transform: translate3d(2.5rem, 0, 0); }
    to   { opacity: 1; transform: none; }
  }

  /* Карточки в ряду — каскад через сдвиг диапазона, а не delay.
     Привязано к скроллу, поэтому ощущается «физично». */
  [data-anim="cascade"] > * {
    animation: rise linear both;
    animation-timeline: view();
    /* Базовый диапазон нужен ВСЕМ детям. Без него элементы дальше шестого
       получали значение по умолчанию (cover 0% cover 100%), которое
       у длинных блоков не доигрывает — карточки оставались полупрозрачными. */
    animation-range: entry 0% entry 60%;
  }
  [data-anim="cascade"] > *:nth-child(1) { animation-range: entry 0%  entry 45%; }
  [data-anim="cascade"] > *:nth-child(2) { animation-range: entry 4%  entry 50%; }
  [data-anim="cascade"] > *:nth-child(3) { animation-range: entry 8%  entry 55%; }
  [data-anim="cascade"] > *:nth-child(4) { animation-range: entry 12% entry 60%; }
  [data-anim="cascade"] > *:nth-child(5) { animation-range: entry 16% entry 65%; }
  [data-anim="cascade"] > *:nth-child(6) { animation-range: entry 20% entry 70%; }

  /* Панель героя: «встаёт» из перспективы по мере прокрутки.
     Тот самый вау-момент на первом экране. */
  [data-anim="unfold"] {
    animation: unfold linear both;
    animation-timeline: view();
    animation-range: entry 5% entry 70%;
    transform-origin: 50% 0%;
  }
  @keyframes unfold {
    from {
      opacity: 0.4;
      transform: perspective(1400px) rotateX(9deg) scale(0.92) translateY(2rem);
    }
    to {
      opacity: 1;
      transform: perspective(1400px) rotateX(0) scale(1) translateY(0);
    }
  }

  /* Параллакс фоновых слоёв — привязка к скроллу всей страницы */
  [data-anim="parallax"] {
    animation: parallax linear both;
    animation-timeline: scroll(root);
  }
  @keyframes parallax {
    from { transform: translate3d(0, 0, 0); }
    to   { transform: translate3d(0, -12%, 0); }
  }

  /* Заголовок секции: буквы «собираются» из разреженного трекинга */
  [data-anim="tighten"] {
    animation: tighten linear both;
    animation-timeline: view();
    animation-range: entry 0% entry 65%;
  }
  @keyframes tighten {
    from { opacity: 0; letter-spacing: 0.3em; }
    to   { opacity: 1; letter-spacing: 0.01em; }
  }

  /* Линия-прогресс под шапкой — показывает глубину прокрутки */
  .scroll-progress {
    animation: progress linear both;
    animation-timeline: scroll(root);
    transform-origin: 0 50%;
  }
  @keyframes progress {
    from { transform: scaleX(0); }
    to   { transform: scaleX(1); }
  }
}

/* ============================================================
   3b. ФОЛБЭК для Safari / Firefox
   Классы .js-anim / .is-visible вешает app.js и ТОЛЬКО если
   IntersectionObserver реально запустился. Без JS контент виден.
   ============================================================ */
.js-anim {
  opacity: 0;
  transform: translate3d(0, 2rem, 0);
  transition:
    opacity var(--dur-enter) var(--ease),
    transform var(--dur-enter) var(--ease);
}
[data-anim="slide-l"].js-anim { transform: translate3d(-2.5rem, 0, 0); }
[data-anim="slide-r"].js-anim { transform: translate3d(2.5rem, 0, 0); }
[data-anim="unfold"].js-anim {
  transform: perspective(1400px) rotateX(9deg) scale(0.94);
  transform-origin: 50% 0%;
}

.js-anim.is-visible {
  opacity: 1;
  transform: none;
}

/* ============================================================
   4. ИНТЕРАКТИВНЫЕ АКЦЕНТЫ
   ============================================================ */

/* Градиентная обводка через маску — один div, без псевдо-костылей */
.gradient-ring {
  position: relative;
  isolation: isolate;
}
.gradient-ring::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  padding: 1px;
  background: var(--gr-border);
  -webkit-mask:
    linear-gradient(#000 0 0) content-box,
    linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
          mask-composite: exclude;
  pointer-events: none;
  opacity: 0;
  transition: opacity var(--dur) var(--ease);
}
.gradient-ring:hover::before { opacity: 1; }

/* Блик, пробегающий по кнопке при наведении */
.shine { position: relative; overflow: hidden; }
.shine::after {
  content: "";
  position: absolute;
  top: 0;
  left: -60%;
  width: 45%;
  height: 100%;
  background: linear-gradient(100deg, transparent, rgba(255,255,255,0.42), transparent);
  transform: skewX(-18deg);
  transition: left var(--dur-slow) var(--ease);
  pointer-events: none;
}
.shine:hover::after { left: 130%; }

/* Текст с градиентной заливкой */
.text-gradient {
  background: var(--gr-accent);
  -webkit-background-clip: text;
          background-clip: text;
  color: transparent;
}

/* Бесконечная лента брендов */
.marquee {
  display: flex;
  gap: var(--s-7);
  width: max-content;
  animation: marquee 38s linear infinite;
  will-change: transform;
}
@keyframes marquee {
  from { transform: translate3d(0, 0, 0); }
  to   { transform: translate3d(-50%, 0, 0); }
}
.marquee-wrap {
  overflow: hidden;
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 12%, #000 88%, transparent);
          mask-image: linear-gradient(90deg, transparent, #000 12%, #000 88%, transparent);
}
.marquee-wrap:hover .marquee { animation-play-state: paused; }

/* ============================================================
   5. ГЛУШИМ ВСЁ ПРИ prefers-reduced-motion
   ============================================================ */
@media (prefers-reduced-motion: reduce) {
  .mesh,
  .marquee,
  .line-mask > span,
  .fade-up,
  [data-anim] {
    animation: none !important;
    opacity: 1 !important;
    transform: none !important;
    letter-spacing: normal !important;
  }
  .shine::after { display: none; }
}
