/* Titles write themselves in (Lino 2026-11-11, after terminal-industries.com):
   each title waits as a faint ghost, then a wave of brand aqua runs through it letter by letter,
   left to right and line by line, and every letter settles into its own final color.
   Body texts fade and rise in a beat later. Classes are set by the script at the end of index.html:
   .lw on a split title, .lw-on while it plays, .lf on a text, .lf-in once it is shown.
   Reduced motion never gets the split or the hidden states. */

.lw-w { white-space: nowrap; }
.lw .lw-c { opacity: 0.14; }
/* Tight wave (Lino 2026-11-11, second pass): the accent is only a short band at the front,
   and each letter settles into its final color right behind it, like the reference. */
.lw.lw-on .lw-c {
  animation: lw-wave 0.42s cubic-bezier(0.3, 0.6, 0.3, 1) calc(var(--ci, 0) * var(--lw-step, 13ms)) both;
}
@keyframes lw-wave {
  0%   { opacity: 0.14; }
  14%  { opacity: 1; color: var(--lw-accent, #35d0c6); }
  32%  { opacity: 1; color: var(--lw-accent, #35d0c6); }
  62%  { opacity: 1; color: inherit; }
  100% { opacity: 1; color: inherit; }
}

/* The wave replaces the old line-mask slide on titles, so a title never moves and waves at once.
   The mask itself stays: a title whose section has not arrived yet is still held below its line
   box (and the script waits for it), so it never shows over the scene before it (Lino 2026-11-11). */
.lw .ln > span { transition: none !important; }

/* Scroll-marked titles (frame 6, Lino 2026-11-11): instead of playing on their own, the letters
   are marked as the page scrolls. --lp (0 to 1) is the progress, written by the script; the accent
   band runs at the front of the marked letters and each one settles into its color behind it. */
.lw.lw-scrub .lw-c {
  --lw-d: calc(var(--lp, 0) * (var(--lw-n, 30) + 4) - var(--ci, 0));
  opacity: calc(0.14 + 0.86 * clamp(0, var(--lw-d), 1));
  color: color-mix(in srgb, var(--lw-accent, #35d0c6) calc(clamp(0, var(--lw-d), 1) * clamp(0, (4 - var(--lw-d)) / 3, 1) * 100%), currentColor);
  animation: none;
}

.lf:not(.lf-in) { filter: opacity(0); }
.lf.lf-in { animation: lf-in 0.9s cubic-bezier(0.16, 1, 0.3, 1) 0.22s backwards; }   /* no filter left behind once in: it can shave glyph edges at fractional zoom */
@keyframes lf-in {
  from { filter: opacity(0); translate: 0 14px; }
  to   { filter: opacity(1); translate: 0 0; }
}

@media (prefers-reduced-motion: reduce) {
  .lw .lw-c, .lw.lw-on .lw-c, .lw.lw-scrub .lw-c { opacity: 1; animation: none; color: inherit; }
  .lf:not(.lf-in), .lf.lf-in { filter: none; animation: none; }
}
