/* motion.css — визуальная часть моушн-системы. Пара к motion.js.
 *
 * Анимируем только transform / opacity / filter / clip-path — то, что
 * браузер отдаёт композитору. Ни width, ни top, ни margin.
 *
 * Кривые взяты не с потолка: это то, чем реально пользуются сайты уровня Linear.
 *   --ease-out-expo   вход элементов: резкий старт, долгое мягкое торможение
 *   --ease-signature  фирменное «дорогое» движение, чуть с оттяжкой
 *   --ease-out-quart  короткие hover-реакции
 * Дефолтный `ease` не используем нигде — он и делает движение стоковым.
 */

:root {
  --ease-out-expo: cubic-bezier(0.16, 1, 0.3, 1);
  --ease-signature: cubic-bezier(0.32, 0.72, 0, 1);
  --ease-out-quart: cubic-bezier(0.25, 1, 0.5, 1);
  --ease-in-out-soft: cubic-bezier(0.65, 0, 0.35, 1);

  --dur-micro: 180ms;   /* hover, focus, нажатие */
  --dur-ui: 320ms;      /* раскрытие, переключение */
  --dur-reveal: 820ms;  /* появление блока при скролле */
  --dur-scene: 1200ms;  /* крупные сцены, hero */

  --reveal-shift: 28px; /* насколько элемент «подъезжает» */
  --reveal-delay: 0ms;
}

/* ---------- Появление при скролле ---------- */
/* Прячем только если JS жив: без него контент обязан быть виден. */
[data-motion-ready] [data-reveal]:not([data-shown]) {
  opacity: 0;
  will-change: opacity, transform;
}
[data-motion-ready] [data-reveal="up"]:not([data-shown]),
[data-motion-ready] [data-reveal=""]:not([data-shown]) { transform: translate3d(0, var(--reveal-shift), 0); }
[data-motion-ready] [data-reveal="down"]:not([data-shown]) { transform: translate3d(0, calc(var(--reveal-shift) * -1), 0); }
[data-motion-ready] [data-reveal="left"]:not([data-shown]) { transform: translate3d(var(--reveal-shift), 0, 0); }
[data-motion-ready] [data-reveal="right"]:not([data-shown]) { transform: translate3d(calc(var(--reveal-shift) * -1), 0, 0); }
[data-motion-ready] [data-reveal="scale"]:not([data-shown]) { transform: scale(0.94); }

/* Шторка вместо clip-path на самом элементе.
   clip-path здесь применять нельзя: по спецификации IntersectionObserver учитывает
   клип, полностью склипанный элемент считается непересекающимся, и наблюдатель
   не срабатывает никогда — картинка остаётся скрытой навсегда (поймано 21.08.2026).
   Поэтому клипим псевдоэлемент-шторку, а сам элемент трогаем только прозрачностью. */
[data-motion-ready] [data-reveal="clip"] { position: relative; overflow: hidden; opacity: 1; }
[data-motion-ready] [data-reveal="clip"]::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 2;
  background: var(--color-bg, #fff);
  transform-origin: 50% 100%;
  transition: transform var(--dur-reveal) var(--ease-signature) var(--reveal-delay);
}
[data-motion-ready] [data-reveal="clip"][data-shown]::after { transform: scaleY(0); }
[data-motion-ready] [data-reveal="blur"]:not([data-shown]) { filter: blur(12px); transform: translate3d(0, 16px, 0); }

[data-reveal] {
  transition:
    opacity var(--dur-reveal) var(--ease-out-expo) var(--reveal-delay),
    transform var(--dur-reveal) var(--ease-out-expo) var(--reveal-delay),
    filter var(--dur-reveal) var(--ease-out-expo) var(--reveal-delay),
    clip-path var(--dur-reveal) var(--ease-signature) var(--reveal-delay);
}
[data-reveal][data-shown] {
  opacity: 1;
  transform: none;
  filter: none;
  will-change: auto;
}

/* ---------- Разбитый текст ---------- */
/* Строки едут из-под маски — приём, который читается как «дорогая» типографика. */
[data-split] { display: block; }
[data-split][data-split="lines"] .split-i,
.split-i.split-line { display: block; overflow: hidden; }

.split-i {
  display: inline-block;
  /* Слово, положенное в inline-block, перестаёт переноситься внутри себя:
     на узком экране одно длинное слово вылезает за вьюпорт и даёт
     горизонтальную прокрутку. Разрешаем ему рваться и не быть шире экрана. */
  max-width: 100%;
  overflow-wrap: break-word;
  hyphens: auto;
  opacity: 0;
  transform: translate3d(0, 0.5em, 0);
  transition:
    opacity var(--dur-reveal) var(--ease-out-expo) var(--reveal-delay),
    transform var(--dur-reveal) var(--ease-signature) var(--reveal-delay);
}
[data-split][data-shown] .split-i { opacity: 1; transform: none; }
.split-line-in { display: inline-block; }

/* ---------- Параллакс ---------- */
[data-parallax] {
  transform: translate3d(0, var(--parallax-y, 0), 0);
  will-change: transform;
}

/* ---------- Бегущая строка ---------- */
[data-marquee] {
  display: flex;
  gap: var(--marquee-gap, 3rem);
  overflow: hidden;
  white-space: nowrap;
  mask-image: linear-gradient(90deg, transparent, #000 6%, #000 94%, transparent);
}
[data-marquee] > * {
  display: flex;
  gap: var(--marquee-gap, 3rem);
  flex: 0 0 auto;
  animation: marquee-run var(--marquee-duration, 24s) linear infinite;
}
[data-marquee][data-paused] > * { animation-play-state: paused; }
[data-marquee]:hover > * { animation-play-state: var(--marquee-hover, running); }
@keyframes marquee-run { to { transform: translate3d(-100%, 0, 0); } }

/* ---------- Липкая сцена ---------- */
/* Внутри [data-scene] доступна --progress: 0..1 по мере прохода через экран. */
[data-scene] { position: relative; }
[data-scene] > .scene-sticky { position: sticky; top: 0; height: 100svh; overflow: hidden; }

/* ---------- Состояния: то, чего у нас не было ---------- */
/* Фокус обязан быть видимым — это и доступность, и признак проработанности. */
:where(a, button, [role="button"], input, textarea, select, summary):focus-visible {
  outline: 2px solid var(--color-accent, currentColor);
  outline-offset: 3px;
  border-radius: var(--radius-xs, 2px);
}
:where(a, button, [role="button"]) {
  transition:
    color var(--dur-micro) var(--ease-out-quart),
    background-color var(--dur-micro) var(--ease-out-quart),
    border-color var(--dur-micro) var(--ease-out-quart),
    transform var(--dur-micro) var(--ease-out-quart),
    opacity var(--dur-micro) var(--ease-out-quart);
}
:where(button, [role="button"]):active { transform: translate3d(0, 1px, 0) scale(0.995); }

/* Подчёркивание, которое выезжает, а не появляется. */
.link-underline {
  background-image: linear-gradient(currentColor, currentColor);
  background-size: 0% 1px;
  background-repeat: no-repeat;
  background-position: 0 100%;
  transition: background-size var(--dur-ui) var(--ease-signature);
}
.link-underline:hover,
.link-underline:focus-visible { background-size: 100% 1px; }

/* ---------- Нативные переходы между страницами (MPA) ---------- */
/* Прогрессивно: где не поддержано — просто обычная навигация. */
@view-transition { navigation: auto; }
::view-transition-old(root) { animation: vt-out 240ms var(--ease-out-quart) both; }
::view-transition-new(root) { animation: vt-in 380ms var(--ease-out-expo) both; }
@keyframes vt-out { to { opacity: 0; transform: translate3d(0, -8px, 0); } }
@keyframes vt-in { from { opacity: 0; transform: translate3d(0, 12px, 0); } }

/* ---------- Полный стоп для тех, кто просил не двигать ---------- */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
  [data-reveal], .split-i { opacity: 1 !important; transform: none !important; filter: none !important; clip-path: none !important; }
  [data-parallax] { transform: none !important; }
  @view-transition { navigation: none; }
}
