/* Animated text emphasis - see public/js/text-fx.js for the markup contract.
   Adding an effect: add a rule block here + its name to EFFECTS in text-fx.js. */

/* Structure written by TextFx.decorate(). A word is one nowrap box so lines
   still break at spaces; characters are inline-block so they can transform. */
.fx-w { display: inline-block; white-space: nowrap; }
.fx-c { display: inline-block; }

/* Wraps a marked run together with the unmarked letters it is glued to, so
   emphasising one letter inside a word cannot break the word across lines.
   Must stay `inline` - inline-block here would reintroduce the very break
   opportunity it exists to remove. */
.fx-nb { white-space: nowrap; }

/* Motion is sized in em, not px: the reader's font-size control spans 14-28px,
   and a fixed 3px bob that shimmers at 14px is nearly invisible at 28px. */

/* ==================== wave ==================== */
/* Negative stagger: the letters start already spread across the cycle instead
   of visibly cascading into motion when the page loads. ~20 characters per
   full wavelength at these numbers. */
.fx-wave .fx-c {
  animation: fx-wave 1.8s ease-in-out infinite;
  animation-delay: calc(var(--fx-i, 0) * -90ms);
}

@keyframes fx-wave {
  0%, 100% { transform: translateY(0); }
  50%      { transform: translateY(-0.16em); }
}

/* ==================== shake ==================== */
.fx-shake .fx-c {
  animation: fx-shake 0.32s steps(2, end) infinite;
  animation-delay: calc(var(--fx-i, 0) * -37ms);
}

@keyframes fx-shake {
  0%   { transform: translate(0.025em, -0.02em); }
  100% { transform: translate(-0.025em, 0.02em); }
}

/* ==================== accessibility ==================== */
/* Text stays exactly where it is and remains fully legible. */
@media (prefers-reduced-motion: reduce) {
  .fx-wave .fx-c,
  .fx-shake .fx-c {
    animation: none;
  }
}
