/**
 * Пять появлений. Исходное состояние задаёт CSS, а не сценарий: иначе блок
 * мелькнёт до того, как сценарий отработает.
 *
 * Оборотная сторона — при выключенном сценарии содержимое осталось бы
 * невидимым. Поэтому прячется оно только при классе er-ready на корне,
 * который ставит сам сценарий, и возвращается при scripting: none.
 */

:root {
  --er-rise: 14px;
  --er-time: .55s;
  --er-ease: cubic-bezier(.2, .7, .3, 1);
  --er-step: 45ms;          /* шаг каскада, не больше 60 мс */
  --er-blur: 12px;          /* больше — заметно тормозит слабые машины */
}

/* пока сценарий не завёлся, ничего не спрятано */
.er-ready .er-up,
.er-ready .er-mask,
.er-ready .er-blur,
.er-ready .er-stagger > span { opacity: 0; }

.er-up { transform: translateY(var(--er-rise)); }
.er-up[data-er-in] {
  opacity: 1;
  transform: none;
  transition: opacity var(--er-time) var(--er-ease),
              transform var(--er-time) var(--er-ease);
}

.er-stagger > span {
  display: inline-block;
  transform: translateY(var(--er-rise));
}
.er-stagger[data-er-in] > span {
  opacity: 1;
  transform: none;
  transition: opacity var(--er-time) var(--er-ease),
              transform var(--er-time) var(--er-ease);
  transition-delay: calc(var(--i, 0) * var(--er-step));
}

.er-mask {
  -webkit-mask: linear-gradient(90deg, #000 0 50%, transparent 50%) 100% 0 / 200% 100%;
  mask: linear-gradient(90deg, #000 0 50%, transparent 50%) 100% 0 / 200% 100%;
}
.er-mask[data-er-in] {
  opacity: 1;
  -webkit-mask-position: 0 0;
  mask-position: 0 0;
  transition: opacity .2s linear,
              -webkit-mask-position .7s var(--er-ease),
              mask-position .7s var(--er-ease);
}

/* печать: число шагов обязано совпадать с числом знаков, иначе строка
   дёргается. Приём работает только с моноширинным шрифтом и одной строкой */
.er-type {
  display: inline-block;
  font-family: "JetBrains Mono", ui-monospace, monospace;
  white-space: nowrap;
  overflow: hidden;
  border-right: 2px solid currentColor;
  width: 0;
}
.er-type[data-er-in] {
  width: calc(var(--er-chars, 12) * 1ch);
  transition: width calc(var(--er-chars, 12) * 70ms)
              steps(var(--er-chars, 12), end);
  animation: er-caret .8s step-end infinite;
}
@keyframes er-caret { 50% { border-color: transparent; } }

.er-blur { filter: blur(var(--er-blur)); }
.er-blur[data-er-in] {
  opacity: 1;
  filter: none;
  transition: opacity var(--er-time) ease-out, filter var(--er-time) ease-out;
}

/* при выключенном сценарии ничего не прячем */
@media (scripting: none) {
  .er-up, .er-mask, .er-blur, .er-stagger > span {
    opacity: 1 !important;
    transform: none !important;
    filter: none !important;
    -webkit-mask: none !important;
    mask: none !important;
  }
  .er-type { width: auto; border-right: none; }
}

/**
 * При просьбе о покое содержимое появляется СРАЗУ, а не остаётся скрытым:
 * убрать движение — не то же самое, что убрать содержимое.
 */
@media (prefers-reduced-motion: reduce) {
  .er-up, .er-mask, .er-blur, .er-stagger > span, .er-type {
    opacity: 1 !important;
    transform: none !important;
    filter: none !important;
    transition: none !important;
    animation: none !important;
    -webkit-mask: none !important;
    mask: none !important;
  }
  .er-type { width: auto; border-right: none; }
}
