/* ============================================================
   Lunify — премиум-слой
   Подключается ПОСЛЕ styles.css и только дополняет его.
   Удалить <link> на этот файл = полный откат к прежнему виду.
   ============================================================ */

:root {
  /* Тени тонируются в цвет фона (глубокий навигационный синий),
     а не чистым чёрным — так глубина читается, а не пачкает. */
  --sh-hue: 226;
  --sh-1: 0 1px 2px hsl(var(--sh-hue) 60% 4% / 0.4);
  --sh-2: 0 4px 12px hsl(var(--sh-hue) 60% 4% / 0.36), 0 1px 2px hsl(var(--sh-hue) 60% 4% / 0.3);
  --sh-3: 0 12px 28px hsl(var(--sh-hue) 62% 3% / 0.42), 0 4px 8px hsl(var(--sh-hue) 60% 4% / 0.3);
  --sh-4: 0 28px 60px hsl(var(--sh-hue) 64% 3% / 0.52), 0 10px 20px hsl(var(--sh-hue) 60% 4% / 0.34);
  --sh-glow: 0 22px 60px hsl(22 85% 45% / 0.18);

  /* Кромка стекла: светлая фаска сверху, тёмная снизу. */
  --edge-top: inset 0 1px 0 hsl(0 0% 100% / 0.07);
  --edge-bottom: inset 0 -1px 0 hsl(var(--sh-hue) 60% 4% / 0.5);

  /* Шкала скруглений: контейнер мягче, вложенное — плотнее. */
  --r-shell: 26px;
  --r-card: 18px;
  --r-inner: 12px;
  --r-chip: 8px;

  /* Пружины вместо линейного ease. */
  --spring: linear(0, 0.006, 0.025 2.8%, 0.101 6.1%, 0.539 18.9%, 0.721 25.3%, 0.849 31.5%, 0.937 38.1%, 0.968 41.8%, 0.991 45.7%, 1.006 50.1%, 1.015 55%, 1.017 63.9%, 1.001);
  --ease-out: cubic-bezier(0.16, 1, 0.3, 1);
  --ease-io: cubic-bezier(0.65, 0, 0.35, 1);
}

:root[data-theme="light"] {
  --sh-1: 0 1px 2px hsl(var(--sh-hue) 30% 40% / 0.1);
  --sh-2: 0 4px 12px hsl(var(--sh-hue) 30% 40% / 0.1), 0 1px 2px hsl(var(--sh-hue) 30% 40% / 0.08);
  --sh-3: 0 12px 28px hsl(var(--sh-hue) 30% 38% / 0.13), 0 4px 8px hsl(var(--sh-hue) 30% 40% / 0.08);
  --sh-4: 0 28px 60px hsl(var(--sh-hue) 32% 36% / 0.16), 0 10px 20px hsl(var(--sh-hue) 30% 40% / 0.1);
  --sh-glow: 0 22px 60px hsl(22 80% 45% / 0.14);
  --edge-top: inset 0 1px 0 hsl(0 0% 100% / 0.9);
  --edge-bottom: inset 0 -1px 0 hsl(var(--sh-hue) 30% 40% / 0.14);
}

/* ------------------------------------------------------------
   1. Зерно — снимает цифровую стерильность плоских градиентов.
   ------------------------------------------------------------ */
:root::after {
  content: "";
  position: fixed;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  opacity: 0.038;
  mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)'/%3E%3C/svg%3E");
}

:root[data-theme="light"]::after { opacity: 0.05; mix-blend-mode: multiply; }

/* ------------------------------------------------------------
   2. Наклон плоскости — сдержанный, только у карточек «Почему».
   ------------------------------------------------------------ */
.tilt-scene { perspective: 1400px; perspective-origin: 50% 40%; }

.tilt {
  transform-style: preserve-3d;
  transform:
    rotateX(var(--rx, 0deg))
    rotateY(var(--ry, 0deg))
    translate3d(0, var(--lift, 0px), var(--tz, 0px));
  transition:
    transform 0.7s var(--spring),
    box-shadow 0.45s var(--ease-out),
    border-color 0.45s var(--ease-out);
  will-change: transform;
}

/* Пока курсор внутри — за transform отвечает rAF, гасим догоняющий переход. */
.tilt.is-live { transition: box-shadow 0.3s var(--ease-out), border-color 0.3s var(--ease-out); }

.bento-card {
  /* overflow:hidden схлопнул бы preserve-3d — режем по clip-path,
     тогда слои внутри карточки реально живут на разной глубине. */
  overflow: visible;
  clip-path: inset(0 round var(--r-card));
  border-radius: var(--r-card);
  box-shadow: var(--sh-2), var(--edge-top), var(--edge-bottom);
}

.bento-card:hover {
  translate: none;
  box-shadow: var(--sh-4), var(--sh-glow), var(--edge-top), var(--edge-bottom);
}

.teaser-card {
  border-radius: var(--r-card);
  box-shadow: var(--sh-2), var(--edge-top);
}

.teaser-card:hover {
  translate: none;
  box-shadow: var(--sh-4), var(--sh-glow), var(--edge-top);
}

/* Слои глубины — только у трёх карточек «Почему». Иконка ближе всех к
   зрителю, текст следом, декоративная графика уходит назад. С плиток вики
   и статистики параллакс снят: там он давал десятки лишних слоёв
   композитора, а выигрыша во внешнем виде почти не было. */
.bento-body { transform-style: preserve-3d; }

.bento-card .card-icon {
  transform: translateZ(52px);
  transition: transform 0.7s var(--spring), box-shadow 0.45s var(--ease-out);
  box-shadow: var(--sh-2);
}

.bento-card h3 { transform: translateZ(30px); transition: transform 0.7s var(--spring); }
.bento-card p { transform: translateZ(14px); transition: transform 0.7s var(--spring); }

.bento-orb,
.bento-wave,
.tps-bars,
.crew-pixels { transform: translateZ(-30px); transition: transform 0.7s var(--spring); }

html.is-touch .bento-card .card-icon,
html.is-touch .bento-card h3,
html.is-touch .bento-card p,
html.is-touch .bento-orb,
html.is-touch .bento-wave,
html.is-touch .tps-bars,
html.is-touch .crew-pixels { transform: none; }

/* Блик: подвижное отражение источника света на «стекле» карточки.
   Живёт над содержимым, поэтому вынесен в отдельный слой. */
.tilt::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  pointer-events: none;
  z-index: 3;
  opacity: 0;
  transition: opacity 0.45s var(--ease-out);
  background: radial-gradient(
    46% 34% at var(--mx, 50%) var(--my, 50%),
    hsl(0 0% 100% / 0.1),
    hsl(0 0% 100% / 0.03) 40%,
    transparent 72%
  );
}

.tilt:hover::before { opacity: 1; }

/* Существующая оранжевая подсветка под курсором — усиливаем и уводим вглубь. */
.bento-card::after,
.teaser-card::after,
.sponsor-card::after,
.map-card::after {
  background: radial-gradient(
    340px circle at var(--mx, 50%) var(--my, 50%),
    hsl(22 88% 52% / 0.17),
    hsl(22 88% 52% / 0.05) 42%,
    transparent 72%
  );
}

/* ------------------------------------------------------------
   3. Кнопки: вес, отклик, световой пробег.
   ------------------------------------------------------------ */
.btn {
  position: relative;
  overflow: hidden;
  isolation: isolate;
  box-shadow: var(--sh-1);
  transition:
    background-color 0.3s var(--ease-out),
    border-color 0.3s var(--ease-out),
    box-shadow 0.3s var(--ease-out),
    transform 0.5s var(--spring);
}

/* Пробег света слева направо при наведении. */
.btn::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  translate: -110% 0;
  background: linear-gradient(
    100deg,
    transparent 20%,
    hsl(0 0% 100% / 0.12) 50%,
    transparent 80%
  );
  transition: translate 0.7s var(--ease-io);
}

.btn:hover {
  transform: translateY(-2px);
  box-shadow: var(--sh-3);
}

.btn:hover::after { translate: 110% 0; }

.btn:active {
  translate: none;
  transform: translateY(0) scale(0.975);
  transition-duration: 0.1s;
  box-shadow: var(--sh-1);
}

.btn:focus-visible,
.ip-plate:focus-visible {
  outline: 2px solid var(--orange);
  outline-offset: 3px;
  border-radius: 999px;
}

/* Главный CTA — адрес сервера. Дышит, пока на него не навели. */
.ip-plate {
  position: relative;
  transition:
    transform 0.5s var(--spring),
    box-shadow 0.35s var(--ease-out),
    border-color 0.35s var(--ease-out);
  box-shadow: var(--sh-2), 0 0 0 0 hsl(22 90% 55% / 0.4);
}

.ip-plate:hover {
  transform: translateY(-2px) scale(1.015);
  box-shadow: var(--sh-4), var(--sh-glow);
}

.ip-plate:active { transform: translateY(0) scale(0.985); transition-duration: 0.1s; }

/* ------------------------------------------------------------
   4. Плотные поверхности: карта, плеер, спонсорка, шаги.
   ------------------------------------------------------------ */
.map-card,
.music-card {
  border-radius: var(--r-shell);
  box-shadow: var(--sh-3), var(--edge-top);
  transition: box-shadow 0.45s var(--ease-out), border-color 0.45s var(--ease-out);
}

.map-card:hover,
.music-card:hover { box-shadow: var(--sh-4), var(--sh-glow), var(--edge-top); }

/* Спонсорка — карточки внутри карточки. Убираем вложенную рамку у
   внутренних плиток: их роль отыгрывают фон и отступ, а не ещё один контур. */
.sponsor-card {
  border-radius: var(--r-shell);
  box-shadow: var(--sh-3), var(--edge-top);
}

.sponsor-list li {
  border-radius: var(--r-inner);
  border-color: transparent;
  background: hsl(var(--sh-hue) 40% 12% / 0.5);
  box-shadow: var(--edge-top);
  transition:
    background-color 0.3s var(--ease-out),
    transform 0.5s var(--spring),
    box-shadow 0.3s var(--ease-out);
}

.sponsor-list li:hover {
  transform: translateY(-3px);
  background: hsl(var(--sh-hue) 42% 16% / 0.7);
  box-shadow: var(--sh-2), var(--edge-top);
}

:root[data-theme="light"] .sponsor-list li { background: hsl(var(--sh-hue) 40% 96% / 0.9); }
:root[data-theme="light"] .sponsor-list li:hover { background: hsl(var(--sh-hue) 40% 99% / 1); }

/* Цифры шагов — объёмные пиксельные плиты, а не плоские квадраты. */
.step::before {
  border-radius: var(--r-inner);
  box-shadow: var(--sh-2), var(--edge-top);
  transition: transform 0.5s var(--spring), box-shadow 0.35s var(--ease-out);
}

.step:hover::before { transform: translateY(-3px) scale(1.05); box-shadow: var(--sh-3), var(--sh-glow); }

/* ------------------------------------------------------------
   5. Появление по скроллу: каскад с пружиной вместо общего фейда.
   ------------------------------------------------------------ */
html.js .reveal {
  opacity: 0;
  translate: 0 22px;
  filter: blur(6px);
  transition:
    opacity 0.8s var(--ease-out),
    translate 0.9s var(--spring),
    filter 0.7s var(--ease-out);
  transition-delay: var(--d, 0s);
}

html.js .reveal.in { opacity: 1; translate: 0 0; filter: blur(0); }

/* ------------------------------------------------------------
   6. Типографика: вес и воздух.
   ------------------------------------------------------------ */
h1, .section h2 { text-wrap: balance; letter-spacing: -0.03em; }

.hero-sub,
.section > p,
.sponsor-head p { text-wrap: pretty; }

.bento-card h3,
.teaser-card h3 { letter-spacing: -0.015em; }

/* ------------------------------------------------------------
   7. Навигация: стекло с настоящей кромкой.
   ------------------------------------------------------------ */
.nav.scrolled {
  box-shadow: var(--sh-3), inset 0 -1px 0 hsl(0 0% 100% / 0.06);
  backdrop-filter: blur(20px) saturate(160%);
  -webkit-backdrop-filter: blur(20px) saturate(160%);
}

/* ------------------------------------------------------------
   8. Уважение к настройкам доступности и тач-устройствам.
   ------------------------------------------------------------ */
/* Класс вешает app.js. Медиа-фича (hover: none) для этого не годится:
   headless-браузеры и часть вебвью репортят её ложно даже на десктопе. */
html.is-touch .tilt { transform: none; }
html.is-touch .tilt::before { display: none; }

@media (prefers-reduced-motion: reduce) {
  :root::after { display: none; }

  .tilt,
  .tilt::before,
  .bento-card .card-icon,
  .bento-card h3,
  .bento-card p,
  .bento-orb, .bento-wave, .tps-bars, .crew-pixels {
    transform: none !important;
    transition: none !important;
  }

  .btn::after { display: none; }
  .btn:hover, .ip-plate:hover, .sponsor-list li:hover { transform: none; }

  html.js .reveal {
    opacity: 1;
    translate: 0 0;
    filter: none;
    transition: none;
  }
}

/* ============================================================
   Волна 2: глубина в оставшихся блоках
   ============================================================ */

@property --angle {
  syntax: "<angle>";
  initial-value: 0deg;
  inherits: false;
}

@keyframes spin-edge { to { --angle: 360deg; } }

/* ------------------------------------------------------------
   9. Живая кромка — только у главной карточки блока «Почему».
   Один акцент на секцию, иначе превращается в мишуру.
   ------------------------------------------------------------ */
.bento-card.bento-wide::before {
  content: "";
  position: absolute;
  inset: -1px;
  border-radius: inherit;
  padding: 1px;
  pointer-events: none;
  z-index: 2;
  opacity: 0;
  transition: opacity 0.5s var(--ease-out);
  background: conic-gradient(
    from var(--angle),
    transparent 0%,
    hsl(22 90% 58% / 0.65) 12%,
    transparent 26%,
    transparent 100%
  );
  /* Оставляем только рамку: заливка вырезается композитингом масок. */
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  mask-composite: exclude;
  animation: spin-edge 6s linear infinite;
}

.bento-card.bento-wide:hover::before { opacity: 1; }

/* ------------------------------------------------------------
   10. Статистика — стеклянные плиты вместо цифр на пустом фоне.
   ------------------------------------------------------------ */
.stats-inner { gap: 16px; }

.stat {
  padding: 26px 18px;
  border-radius: var(--r-card);
  border: 1px solid var(--line);
  background: linear-gradient(180deg, var(--surface-fade) 0%, transparent 100%);
  box-shadow: var(--sh-1), var(--edge-top);
  transition:
    transform 0.7s var(--spring),
    box-shadow 0.45s var(--ease-out),
    border-color 0.45s var(--ease-out);
}

.stat:hover {
  border-color: hsl(22 88% 52% / 0.3);
  box-shadow: var(--sh-3), var(--sh-glow), var(--edge-top);
}

/* ------------------------------------------------------------
   11. Галерея — слайды получают объём и приближаются при наведении.
   ------------------------------------------------------------ */
.carousel-slide {
  border-radius: var(--r-card);
  box-shadow: var(--sh-2), var(--edge-top);
  transition:
    transform 0.6s var(--spring),
    box-shadow 0.45s var(--ease-out),
    border-color 0.45s var(--ease-out);
}

.carousel-slide:hover {
  transform: translateY(-6px) scale(1.014);
  border-color: hsl(22 88% 52% / 0.32);
  box-shadow: var(--sh-4), var(--sh-glow), var(--edge-top);
  z-index: 2;
}

.carousel-slide img {
  transition: transform 0.9s var(--ease-out), filter 0.6s var(--ease-out);
  filter: saturate(0.94);
}

.carousel-slide:hover img { transform: scale(1.06); filter: saturate(1.06); }

/* ------------------------------------------------------------
   12. FAQ — строки с весом и откликом.
   ------------------------------------------------------------ */
.faq-item {
  border-radius: var(--r-inner);
  box-shadow: var(--sh-1), var(--edge-top);
  transition:
    transform 0.5s var(--spring),
    box-shadow 0.35s var(--ease-out),
    border-color 0.35s var(--ease-out),
    background-color 0.35s var(--ease-out);
}

.faq-item:hover {
  transform: translateX(4px);
  box-shadow: var(--sh-2), var(--edge-top);
  border-color: hsl(22 88% 52% / 0.26);
}

.faq-item.open {
  transform: none;
  box-shadow: var(--sh-3), var(--edge-top);
  border-color: hsl(22 88% 52% / 0.22);
}

.faq-marker { transition: transform 0.5s var(--spring); }
.faq-item:hover .faq-marker { transform: scale(1.15); }

/* ------------------------------------------------------------
   13. Бейджи и чипы — плотнее контейнеров, как и положено вложенному.
   ------------------------------------------------------------ */
/* Бейджи остаются «таблетками» — форма у них своя, менять её было незачем. */
.badge {
  box-shadow: var(--sh-1), var(--edge-top);
}

.faq-tabs button {
  transition:
    transform 0.45s var(--spring),
    background-color 0.3s var(--ease-out),
    color 0.3s var(--ease-out),
    box-shadow 0.3s var(--ease-out);
}

.faq-tabs button:hover { transform: translateY(-2px); box-shadow: var(--sh-2); }

/* ------------------------------------------------------------
   14. Доступность и тач — для волны 2.
   ------------------------------------------------------------ */
html.is-touch .bento-card.bento-wide::before { display: none; }

@media (prefers-reduced-motion: reduce) {
  .bento-card.bento-wide::before { animation: none; display: none; }
  .stat:hover,
  .carousel-slide:hover,
  .faq-item:hover,
  .faq-tabs button:hover { transform: none !important; }
  .carousel-slide img,
  .carousel-slide:hover img { transform: none !important; transition: none !important; }
  .faq-item:hover .faq-marker { transform: none !important; }
}

/* ------------------------------------------------------------
   15. Светлая тема: белое на светло-лавандовом само по себе не
   читается. Глубину там держат граница и тень, а не блик —
   белая фаска на белой поверхности не видна в принципе.
   ------------------------------------------------------------ */
:root[data-theme="light"] {
  --sh-1: 0 1px 2px hsl(var(--sh-hue) 34% 42% / 0.14);
  --sh-2: 0 6px 16px hsl(var(--sh-hue) 34% 42% / 0.15), 0 1px 3px hsl(var(--sh-hue) 34% 42% / 0.12);
  --sh-3: 0 16px 34px hsl(var(--sh-hue) 34% 40% / 0.18), 0 5px 10px hsl(var(--sh-hue) 34% 42% / 0.12);
  --sh-4: 0 34px 70px hsl(var(--sh-hue) 36% 38% / 0.24), 0 12px 24px hsl(var(--sh-hue) 34% 42% / 0.14);
  --edge-bottom: inset 0 -1px 0 hsl(var(--sh-hue) 30% 40% / 0.1);
}

:root[data-theme="light"] .bento-card,
:root[data-theme="light"] .teaser-card,
:root[data-theme="light"] .stat,
:root[data-theme="light"] .carousel-slide,
:root[data-theme="light"] .faq-item,
:root[data-theme="light"] .map-card,
:root[data-theme="light"] .music-card,
:root[data-theme="light"] .sponsor-card {
  border-color: hsl(var(--sh-hue) 28% 58% / 0.24);
}

:root[data-theme="light"] .bento-card:hover,
:root[data-theme="light"] .teaser-card:hover,
:root[data-theme="light"] .stat:hover,
:root[data-theme="light"] .carousel-slide:hover {
  border-color: hsl(22 80% 48% / 0.4);
}

/* Внутренние плитки спонсорки в светлой теме — обратно с контуром,
   иначе на белом фоне они перестают быть отдельными объектами. */
:root[data-theme="light"] .sponsor-list li {
  border-color: hsl(var(--sh-hue) 28% 58% / 0.2);
}
/* ------------------------------------------------------------
   16. Лента построек — непрерывная, как «Как выглядит город» на aurles.ru.

   Вместо рамки-карточки полоса во всю ширину с растворением краёв
   маской: кадры не упираются в границу, а уходят в фон, и лента
   читается как бесконечная.
   ------------------------------------------------------------ */
.carousel-viewport {
  overflow: hidden;
  border: 0;
  background: none;
  border-radius: 0;
  padding: 6px 0 14px;
  -webkit-mask-image: linear-gradient(90deg, transparent 0%, #000 6%, #000 94%, transparent 100%);
          mask-image: linear-gradient(90deg, transparent 0%, #000 6%, #000 94%, transparent 100%);
}

.carousel-track {
  width: max-content;
  gap: 14px;
  /* Никаких transition: сдвиг пишет rAF каждый кадр, переход бы с ним дрался. */
  transition: none;
  will-change: transform;
  backface-visibility: hidden;
  cursor: grab;
}

.carousel-track.dragging { cursor: grabbing; }

.carousel-slide {
  flex: 0 0 auto;
  width: clamp(260px, 26vw, 380px);
  aspect-ratio: 16 / 9;
  border-radius: var(--r-card);
  box-shadow: var(--sh-2), var(--edge-top);
  transition:
    transform 0.5s var(--spring),
    box-shadow 0.4s var(--ease-out),
    border-color 0.4s var(--ease-out);
}

.carousel-slide:hover {
  transform: translateY(-5px);
  border-color: hsl(22 88% 52% / 0.32);
  box-shadow: var(--sh-4), var(--sh-glow), var(--edge-top);
  z-index: 2;
}

.carousel-slide img {
  -webkit-user-drag: none;
  filter: saturate(0.95);
  transition: transform 0.8s var(--ease-out), filter 0.5s var(--ease-out);
}

.carousel-slide:hover img { transform: scale(1.05); filter: saturate(1.05); }

/* Пока тянем — гасим ховер-подъём, иначе слайд прыгает под курсором. */
.carousel-track.dragging .carousel-slide:hover { transform: none; }
.carousel-track.dragging .carousel-slide:hover img { transform: none; }

/* В styles.css у слайдов заданы flex-basis по брейкпоинтам (calc((100% - 16px)/2)
   и 100%) — это логика старой пошаговой карусели «N штук в кадре». Для ленты
   ширина фиксированная, поэтому basis глушим на всех брейкпоинтах. */
@media (max-width: 1080px) {
  .carousel-slide { flex-basis: auto; width: clamp(240px, 40vw, 320px); }
}

@media (max-width: 640px) {
  .carousel-slide { flex-basis: auto; width: clamp(220px, 68vw, 300px); }
  .carousel-track { gap: 10px; }
  .carousel-viewport { padding: 4px 0 12px; }
}

@media (prefers-reduced-motion: reduce) {
  .carousel-slide,
  .carousel-slide:hover { transform: none !important; }
  .carousel-slide img,
  .carousel-slide:hover img { transform: none !important; transition: none !important; }
}

/* ============================================================
   17. ОПТИМИЗАЦИЯ ОТРИСОВКИ

   Замеры (headless, CPU x4, медиана кадра при скролле):
   было 133 мс -> 50 мс при отключении полноэкранных слоёв с
   mix-blend-mode, blur-фильтров и backdrop-filter.

   mix-blend-mode на fixed-слое во весь экран — худший случай для
   скролла: композитор обязан перечитывать и пересмешивать весь
   вьюпорт каждый кадр. Убираем смешивание везде, где оно не несёт
   смысла, сохраняя внешний вид.
   ============================================================ */

/* Зерно без смешивания: на тёмном фоне overlay почти неотличим от
   обычного наложения с малой прозрачностью, а стоит на порядок дороже. */
:root::after {
  mix-blend-mode: normal;
  opacity: 0.03;
  /* Слой статичен — говорим об этом браузеру, чтобы он не пересобирал его. */
  contain: strict;
}

:root[data-theme="light"]::after { mix-blend-mode: normal; opacity: 0.035; }

/* Звёздная пыль: слой был растянут на 640px во ВСЕ стороны, хотя дрейф
   идёт только вправо-вниз. Половина площади не была видна никогда.
   1440x900: было 2720x2180 (5.9 Мпикс), стало 2080x1540 (3.2 Мпикс). */
body::before {
  inset: auto;
  top: -640px;
  left: -640px;
  right: 0;
  bottom: 0;
}

/* Курсорное свечение: blur(10px) поверх радиального градиента —
   лишний проход фильтра (градиент и так мягкий), а mix-blend-mode
   заставлял пересмешивать экран на каждом движении мыши. */
.cursor-glow,
:root[data-theme="light"] .cursor-glow {
  filter: none;
  mix-blend-mode: normal;
  opacity: 0.55;
}

/* Навигация: blur(16px) достаточно, второй проход saturate() — нет.
   Возвращаем один фильтр вместо двух. */
.nav.scrolled {
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
}

/* will-change держим только на время взаимодействия. Постоянный
   will-change на всех карточках — это постоянные слои композитора
   и лишняя видеопамять на слабых машинах. */
.tilt { will-change: auto; }
.tilt:hover,
.tilt.is-live { will-change: transform; }

/* FAQ раскрывается анимацией grid-template-rows — это layout на каждом
   кадре. Изолируем пересчёт внутри строки, чтобы он не поднимался
   до всей страницы. */
.faq-item {
  contain: layout paint;
}

.faq-list { contain: layout; }

/* ============================================================
   18. КИНЕМАТОГРАФИЧНЫЙ ГЕРОЙ + ЖИВАЯ ТИПОГРАФИКА
   Шрифты Unbounded и Onest подключены вариативными (wght 200..900),
   поэтому вес интерполируется плавно, а не ступенями.
   ============================================================ */

/* --- Заголовок героя: проявляется маской слева направо и одновременно
       набирает вес, будто выходит из темноты. Вес и трекинг меняются
       ТОЛЬКО во время входа: делать это на ховере нельзя — глифы меняют
       ширину, и строка бы прыгала под курсором. --- */
html.js .hero h1 {
  -webkit-mask-image: linear-gradient(100deg, #000 44%, rgba(0, 0, 0, 0) 56%);
          mask-image: linear-gradient(100deg, #000 44%, rgba(0, 0, 0, 0) 56%);
  -webkit-mask-size: 230% 100%;
          mask-size: 230% 100%;
  -webkit-mask-repeat: no-repeat;
          mask-repeat: no-repeat;
  -webkit-mask-position: 100% 0;
          mask-position: 100% 0;
}

html.js .hero h1.in {
  /* Ровная кривая, а не --ease-out: тот проходит 93% пути за 44% времени,
     и «кинематографичное» проявление читалось как рывок. */
  animation:
    hero-wipe 1.7s cubic-bezier(0.62, 0.05, 0.3, 1) 0.15s both,
    hero-weight 1.9s cubic-bezier(0.55, 0.1, 0.3, 1) 0.15s both;
}

@keyframes hero-wipe {
  from { -webkit-mask-position: 100% 0; mask-position: 100% 0; }
  to   { -webkit-mask-position: 0% 0;   mask-position: 0% 0; }
}

@keyframes hero-weight {
  from {
    font-variation-settings: "wght" 250;
    letter-spacing: 0.06em;
  }
  to {
    font-variation-settings: "wght" 600;
    letter-spacing: -0.03em;
  }
}

/* --- Заголовки секций набирают вес при входе в кадр. Одноразово,
       на границе появления — поэтому перекомпоновка строки происходит
       вместе с самим появлением и глазом не читается как скачок. --- */
html.js .section h2.in,
html.js .reveal.in > h2,
html.js h2.reveal.in {
  animation: heading-weight 1.2s cubic-bezier(0.55, 0.1, 0.3, 1) both;
}

@keyframes heading-weight {
  from { font-variation-settings: "wght" 380; }
  to   { font-variation-settings: "wght" 600; }
}

/* --- Параллакс героя от прокрутки. Смещение приходит из JS переменной,
       transform собирается здесь. Движение от курсора убрано намеренно:
       луна и горы, ездящие за мышью, читались как ошибка. --- */
[data-parallax] {
  transform: translate3d(0, var(--py, 0px), 0);
  will-change: transform;
}

@media (prefers-reduced-motion: reduce) {
  html.js .hero h1 {
    -webkit-mask-image: none;
            mask-image: none;
  }
  html.js .hero h1.in,
  html.js .section h2.in,
  html.js .reveal.in > h2,
  html.js h2.reveal.in { animation: none; }
  [data-parallax] { transform: none; }
}


/* ============================================================
   19. ПАУЗА АНИМАЦИЙ ВНЕ ЭКРАНА

   На странице ~79 бесконечных CSS-анимаций (эквалайзер музыки,
   волны в бенто, пульс разделителей, светлячки). Каждая тикает в
   главном потоке КАЖДЫЙ кадр независимо от того, видно её или нет.
   Замер: пересчёт стилей в простое — 4328 мс на 160 кадров, а со
   всеми выключенными анимациями — 330 мс. То есть 92% фоновой
   нагрузки давали анимации, большая часть которых за экраном.

   Класс anim-idle вешает IntersectionObserver в app.js.
   ============================================================ */
.anim-idle,
.anim-idle *,
.anim-idle *::before,
.anim-idle *::after {
  animation-play-state: paused !important;
}

/* Вращающаяся кромка главной карточки видна только под курсором
   (в покое opacity:0) — крутить её постоянно было чистой тратой. */
.bento-card.bento-wide::before { animation-play-state: paused; }
.bento-card.bento-wide:hover::before { animation-play-state: running; }
