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

   Всё держится на transform и opacity — только они не заставляют браузер
   пересчитывать раскладку. Ни одного свойства, меняющего геометрию, здесь
   быть не должно, иначе десяток точек начнёт стоить кадров.

   Под prefers-reduced-motion всё гаснет целиком, а не замедляется: мерцание
   на периферии зрения — ровно то, что этой настройкой и просят убрать.

   Но включать анимацию через (prefers-reduced-motion: no-preference) нельзя,
   и на этом я обжёгся. Этот запрос истинен, только когда система явно
   сообщила «предпочтений нет». Стоит человеку выключить эффекты анимации в
   настройках Windows — или системе промолчать — и запрос ложен, а значит не
   включается ничего и нигде. Правильный порядок обратный: анимация идёт по
   умолчанию, а гасит её отдельное правило под (prefers-reduced-motion:
   reduce). Тогда неизвестное состояние означает «показывать», а не
   «промолчать». */

@media (prefers-reduced-motion: reduce) {
  .hb-firefly,
  .hb-app .hb-settlement-hero::after {
    animation: none !important;
  }
  .hb-firefly { opacity: 0 !important; }
}

/* Слой светлячков позиционируется абсолютно, значит ему нужен
   позиционированный родитель. Контейнеров у ключевого арта несколько
   (hb-hunt-hero, hb-settlement-hero, hb-prepare-hero и другие), и часть из
   них не позиционирована — слой уходил считать координаты от страницы и
   вылезал за правый край окна на восемь пикселей. Ловим родителя по самому
   арту, а не по списку классов: список рано или поздно разойдётся с
   разметкой. */
/* Контейнер сцены обязан быть позиционирован: иначе слой светлячков считает
   координаты от страницы и растягивается на всё окно. Ловим контейнер по
   самому арту, а не по списку классов — список рано или поздно разойдётся с
   разметкой. Саму картинку не трогаем: она уже абсолютная и привязана к
   этому же контейнеру. */
.hb-app :has(> .hb-promo-art) {
  position: relative;
}


.hb-fireflies {
  position: absolute;
  inset: 0;
  z-index: 1;
  overflow: hidden;
  pointer-events: none;
}

.hb-firefly {
  position: absolute;
  width: 3px;
  height: 3px;
  border-radius: 50%;
  background: #d8ff86;
  box-shadow: 0 0 6px 2px rgba(183, 242, 74, .55);
  opacity: 0;
  will-change: transform, opacity;
}

/* Девять точек, у каждой свои угол вылета, длительность и задержка. Общий
   цикл специально не кратен: если траектории совпадут по фазе, рой начнёт
   мигать разом и превратится в гирлянду. */
.hb-firefly:nth-child(1) { left: 12%; top: 62%; --dx:  38px; --dy: -46px; --t: 9.3s;  --d: 0s;    }
.hb-firefly:nth-child(2) { left: 24%; top: 78%; --dx: -26px; --dy: -60px; --t: 11.7s; --d: -2.4s; }
.hb-firefly:nth-child(3) { left: 37%; top: 55%; --dx:  44px; --dy: -34px; --t: 8.6s;  --d: -5.1s; }
.hb-firefly:nth-child(4) { left: 49%; top: 84%; --dx: -34px; --dy: -52px; --t: 13.2s; --d: -1.2s; }
.hb-firefly:nth-child(5) { left: 61%; top: 66%; --dx:  30px; --dy: -58px; --t: 10.4s; --d: -7.3s; }
.hb-firefly:nth-child(6) { left: 72%; top: 80%; --dx: -42px; --dy: -40px; --t: 12.1s; --d: -3.6s; }
.hb-firefly:nth-child(7) { left: 83%; top: 58%; --dx:  24px; --dy: -64px; --t: 9.9s;  --d: -8.8s; }
.hb-firefly:nth-child(8) { left: 91%; top: 74%; --dx: -30px; --dy: -44px; --t: 14.5s; --d: -4.7s; }
.hb-firefly:nth-child(9) { left:  5%; top: 71%; --dx:  36px; --dy: -50px; --t: 10.8s; --d: -6.2s; }
/* Семь добавочных точек для широких сцен меню. Экран входа обходится первыми
   девятью — там полоса уже и рой на ней и так плотный. */
.hb-firefly:nth-child(10) { left: 17%; top: 88%; --dx: -22px; --dy: -66px; --t: 11.2s; --d: -0.7s;  }
.hb-firefly:nth-child(11) { left: 30%; top: 68%; --dx:  40px; --dy: -42px; --t: 13.9s; --d: -9.4s;  }
.hb-firefly:nth-child(12) { left: 43%; top: 60%; --dx: -36px; --dy: -56px; --t: 9.1s;  --d: -3.1s;  }
.hb-firefly:nth-child(13) { left: 56%; top: 90%; --dx:  28px; --dy: -70px; --t: 12.6s; --d: -6.9s;  }
.hb-firefly:nth-child(14) { left: 67%; top: 63%; --dx: -44px; --dy: -38px; --t: 10.1s; --d: -1.9s;  }
.hb-firefly:nth-child(15) { left: 78%; top: 86%; --dx:  32px; --dy: -62px; --t: 14.8s; --d: -8.2s;  }
.hb-firefly:nth-child(16) { left: 88%; top: 61%; --dx: -26px; --dy: -48px; --t: 8.9s;  --d: -5.6s;  }

.hb-firefly {
  animation: hb-drift var(--t) ease-in-out var(--d) infinite;
}

@keyframes hb-drift {
  0%   { transform: translate3d(0, 0, 0);                                   opacity: 0; }
  14%  {                                                                    opacity: .85; }
  46%  { transform: translate3d(calc(var(--dx) * .55), calc(var(--dy) * .5), 0); opacity: .35; }
  62%  {                                                                    opacity: .9; }
  100% { transform: translate3d(var(--dx), var(--dy), 0);                   opacity: 0; }
}

/* ---- костёр в деревне --------------------------------------------
   На арте деревни костёр горит ровным пятном. Подкладываем под него тёплый
   пульс: он не рисует пламя, а лишь заставляет свет дышать, и этого хватает,
   чтобы сцена перестала читаться остановленной.

   Координаты сняты с отрисованной сцены, а не с исходного файла: у арта
   object-fit: cover и object-position: right center, поэтому видимая часть
   картинки зависит от ширины панели и доли внутри файла не совпадают с
   долями внутри контейнера. Ищутся не глазами, а поиском самой яркой тёплой
   точки в кадре — при перегенерации арта достаточно повторить замер.
   После смены кадрирования костёр стоит на 54% ширины и 50% высоты. */
/* Кадрирование сцены деревни.
   Арт 2016x864 показывается в полосе 1386x315 через cover, то есть по высоте
   видна чуть больше половины. При выравнивании по центру костёр приходился на
   95% высоты полосы — то есть срезался нижним краем. Смещаем окно вниз, чтобы
   огонь встал примерно на две трети высоты и был виден целиком. */
.hb-app .hb-settlement-hero .hb-promo-art {
  object-position: right 86% !important;
}

.hb-app .hb-settlement-hero { position: relative; }

.hb-app .hb-settlement-hero::after {
  content: "";
  position: absolute;
  left: 54%;
  top: 50%;
  width: 13%;
  aspect-ratio: 1;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  background: radial-gradient(circle,
    rgba(255, 176, 74, .34) 0%,
    rgba(255, 138, 46, .18) 38%,
    transparent 68%);
  pointer-events: none;
  mix-blend-mode: screen;
  z-index: 1;
}

.hb-app .hb-settlement-hero::after {
  animation: hb-ember 2.7s ease-in-out infinite;
}

/* Пламя не мигает равномерно: оно вздрагивает. Отсюда неровные доли и
   несовпадающие пики яркости и размера. */
@keyframes hb-ember {
  0%   { opacity: .72; transform: translate(-50%, -50%) scale(1);    }
  17%  { opacity: 1;   transform: translate(-50%, -50%) scale(1.07); }
  34%  { opacity: .64; transform: translate(-50%, -50%) scale(.97);  }
  51%  { opacity: .92; transform: translate(-50%, -50%) scale(1.04); }
  68%  { opacity: .7;  transform: translate(-50%, -50%) scale(1);    }
  84%  { opacity: .98; transform: translate(-50%, -50%) scale(1.05); }
  100% { opacity: .72; transform: translate(-50%, -50%) scale(1);    }
}

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

   Слой обязан лежать ПОВЕРХ затемнения, и это была ошибка. Сперва я положил
   его между артом и скримом, рассуждая, что точки должны гаснуть к левому краю
   вместе с лесом. Но скрим на входе глухой до 38% ширины и наполовину плотный
   до 62% — он просто съедал почти весь рой, а то, что оставалось справа, было
   слишком тусклым, чтобы это заметить.

   Поэтому слой поднят над затемнением, а сами точки на входе крупнее и ярче,
   чем в меню: там они лежат на светлой сцене, а тут на затемнённой. */
.hb-fireflies--gate {
  position: absolute;
  inset: 0;
  z-index: var(--hb-ui-layer-local-chrome, 3);
  overflow: hidden;
  pointer-events: none;
}

.hb-fireflies--gate .hb-firefly {
  width: 4px;
  height: 4px;
  background: #eaffb0;
  box-shadow: 0 0 10px 3px rgba(183, 242, 74, .75);
}


/* ---- живой лагерь на экране входа ---------------------------------
   Прежний вход был анимированной гифкой: у друга там дышали фонари и летали
   светлячки. Я заменил его неподвижным PNG, и экран, который человек видит
   первым и дольше всего, стал мёртвым. Девяти тусклых точек, половина которых
   к тому же сидит под глухой левой частью затемнения, для замены мало.

   Очаги света найдены не на глаз, а поиском ярких тёплых пятен в отрисованном
   кадре: два фонаря у ворот на 45% и 57% ширины, 35% высоты, и костёр справа
   на 88% и 75%. При перегенерации арта замер достаточно повторить.

   Фонари и костёр дышат вразнобой: разная длительность и разная фаза. Совпади
   они — и лагерь начнёт мигать как ёлочная гирлянда. */

.hb-account-gate__glow {
  position: absolute;
  inset: 0;
  z-index: var(--hb-ui-layer-local-chrome, 20);
  pointer-events: none;
  overflow: hidden;
}

.hb-account-gate__glow i {
  position: absolute;
  display: block;
  border-radius: 50%;
  transform: translate(-50%, -50%);
  mix-blend-mode: screen;
  background: radial-gradient(circle,
    rgba(255, 186, 92, .40) 0%,
    rgba(255, 146, 48, .20) 40%,
    transparent 70%);
}

/* Размеры пятен подрезаны после того, как я посмотрел на снимок кадра, а не
   на код. Фонарь в кадре шириной примерно двадцать пять точек, а ореол я задал
   в девять процентов полосы — это полторы сотни. Получались два ровных
   оранжевых шара, живущих отдельно от фонарей, которые их будто бы дают.
   Шесть процентов держат свет при фонаре. */
/* левый фонарь у ворот */
.hb-account-gate__glow i:nth-child(1) {
  left: 45%; top: 35%; width: 6%; aspect-ratio: 1;
  animation: hb-lamp 3.4s ease-in-out infinite;
}
/* правый фонарь */
.hb-account-gate__glow i:nth-child(2) {
  left: 57%; top: 35%; width: 6%; aspect-ratio: 1;
  animation: hb-lamp 4.1s ease-in-out -1.7s infinite;
}
/* костёр справа: теплее и заметнее фонарей */
.hb-account-gate__glow i:nth-child(3) {
  left: 88%; top: 75%; width: 10%; aspect-ratio: 1;
  background: radial-gradient(circle,
    rgba(255, 168, 66, .46) 0%,
    rgba(255, 120, 34, .22) 42%,
    transparent 72%);
  animation: hb-ember 2.7s ease-in-out -0.9s infinite;
}

@keyframes hb-lamp {
  0%   { opacity: .70; transform: translate(-50%, -50%) scale(1);    }
  23%  { opacity: 1;   transform: translate(-50%, -50%) scale(1.06); }
  47%  { opacity: .78; transform: translate(-50%, -50%) scale(.98);  }
  71%  { opacity: .95; transform: translate(-50%, -50%) scale(1.03); }
  100% { opacity: .70; transform: translate(-50%, -50%) scale(1);    }
}

/* Светлячки на входе держатся правее: слева затемнение глухое, и точки там
   просто пропадают. */
.hb-fireflies--gate .hb-firefly:nth-child(1) { left: 46%; }
.hb-fireflies--gate .hb-firefly:nth-child(2) { left: 54%; }
.hb-fireflies--gate .hb-firefly:nth-child(3) { left: 61%; }
.hb-fireflies--gate .hb-firefly:nth-child(9) { left: 50%; }

@media (prefers-reduced-motion: reduce) {
  .hb-account-gate__glow i { animation: none !important; }
}

/* ---- костёр на экране охоты ---------------------------------------
   На ключевом арте у палатки горит костёр, и он единственный источник тепла
   в кадре. Раньше окно арта стояло так, что от огня была видна верхушка;
   окно опущено, и теперь ему стоит дышать так же, как костру в деревне.

   Пятно намеренно меньше и глуше деревенского: там костёр в середине сцены и
   держит кадр, здесь он сбоку и не должен спорить с кнопкой Start Hunt.

   Висит пятно не на самом hero, и это важно. У hero уже занят ::after — там
   лежит затемнение, ради которого читается панель с кнопкой. Псевдоэлемент
   у узла один, так что мой огонь просто вытеснил затемнение, и панель
   осталась стоять на голом лесу. Вешаем на слой светлячков: он для того и
   заведён, чтобы держать всё живое, и растянут ровно по hero. */
.hb-app .hb-hunt-hero { position: relative; }

.hb-app .hb-hunt-hero .hb-fireflies::after {
  content: "";
  position: absolute;
  left: 64%;
  top: 66%;
  width: 9%;
  aspect-ratio: 1;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  background: radial-gradient(circle,
    rgba(255, 172, 70, .30) 0%,
    rgba(255, 132, 40, .15) 40%,
    transparent 70%);
  pointer-events: none;
  mix-blend-mode: screen;
  z-index: 1;
  animation: hb-ember 2.9s ease-in-out -1.3s infinite;
}

@media (prefers-reduced-motion: reduce) {
  .hb-app .hb-hunt-hero .hb-fireflies::after { animation: none !important; }
}
