/* =========================================================
   TITLE SWAP  ·  per-letter dissolve
   ---------------------------------------------------------
   A short "pre-title" shows first, then hands off to the real
   headline letter by letter — each character fades out and each
   incoming character fades in on its own staggered clock.

   The real headline is the h1's own DOM text. js/title-swap.js
   splits it into per-character spans and mirrors the full string
   onto an aria-label, so assistive tech reads the sentence rather
   than spelling it out. With JS off, the headline renders normally.
   ========================================================= */

/* --- tunables ------------------------------------------- */
:root {
  --ic-ch-out-dur: 90ms;   /* how long one outgoing letter takes  */
  --ic-ch-in-dur: 140ms;   /* how long one incoming letter takes  */
}

.ic-swap {
  position: relative;
}

/* Hide the real headline until JS has split it. visibility (not
   opacity/colour) so nested <strong> rules cannot leak through. */
html.ic-swap-js .ic-swap {
  visibility: hidden;
  /* Failsafe: if title-swap.js never runs, reveal anyway after 4s. */
  animation: ic-swap-failsafe 0s linear 4s forwards;
}

html.ic-swap-js .ic-swap.ic-swap--ready {
  visibility: visible;
  animation: none;
}

@keyframes ic-swap-failsafe {
  to { visibility: visible; }
}

/* --- the two layers ------------------------------------- */

.ic-swap__title {
  display: block;
}

.ic-swap__pre {
  position: absolute;
  inset: 0;
  display: block;
  /* Inherits size, weight, colour and letter-spacing from the
     heading, so both states occupy the same optical space. */
  pointer-events: none;
}

/* --- per-character cells --------------------------------
   display:inline (not inline-block) is deliberate: inline spans
   create no soft-wrap opportunity, so line breaking still happens
   only at spaces and the headline wraps exactly as it did before
   it was split.
   --------------------------------------------------------- */

.ic-swap__ch {
  display: inline;
  will-change: opacity;
}

/* Incoming letters: start dark, arrive on their own delay. */
.ic-swap__title .ic-swap__ch {
  opacity: 0;
  transition: opacity var(--ic-ch-in-dur, 140ms) linear var(--in, 0ms);
}

.ic-swap.is-resolved .ic-swap__title .ic-swap__ch {
  opacity: 1;
}

/* Outgoing letters: lit, then switch off on their own delay. */
.ic-swap__pre .ic-swap__ch {
  opacity: 1;
  transition: opacity var(--ic-ch-out-dur, 90ms) linear var(--out, 0ms);
}

.ic-swap.is-resolved .ic-swap__pre .ic-swap__ch {
  opacity: 0;
}

/* Once the handoff is done, drop the overlay out of the box model
   entirely so it can never trap a click or a text selection. */
.ic-swap.is-settled .ic-swap__pre {
  display: none;
}

/* --- reduced motion -------------------------------------
   Letters winking on and off is exactly the kind of effect that
   causes trouble for motion-sensitive and photosensitive readers.
   Skip the pre-title and show the headline immediately.
   --------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  .ic-swap__title .ic-swap__ch,
  .ic-swap.is-resolved .ic-swap__title .ic-swap__ch {
    opacity: 1;
    transition: none;
  }
  .ic-swap__pre {
    display: none;
  }
}


/* =========================================================
   MULTI-STAGE SWAP  ·  data-ic-pretitles
   ---------------------------------------------------------
   Two or more headlines play in order before the h1's own text
   arrives. The single-pre-title rules above key off the parent's
   .is-resolved, which switches every overlay at once — right for
   one stage, useless for a chain. These address each stage
   individually instead.

   Every stage starts hidden. .is-lit brings its letters on,
   .is-out takes them off, each on the per-character --in / --out
   delays the script writes. The title arrives with .is-in.
   ========================================================= */

.ic-swap--staged .ic-swap__stage .ic-swap__ch {
  opacity: 0;
  transition: opacity var(--ic-ch-in-dur, 140ms) linear var(--in, 0ms);
}

.ic-swap--staged .ic-swap__stage.is-lit .ic-swap__ch {
  opacity: 1;
}

.ic-swap--staged .ic-swap__stage.is-out .ic-swap__ch {
  opacity: 0;
  transition: opacity var(--ic-ch-out-dur, 90ms) linear var(--out, 0ms);
}

/* The real headline waits for .is-in rather than .is-resolved. */
.ic-swap--staged .ic-swap__title .ic-swap__ch {
  opacity: 0;
}

.ic-swap--staged .ic-swap__title.is-in .ic-swap__ch {
  opacity: 1;
}

/* Spent stages leave the box model so they cannot trap a click. */
.ic-swap--staged .ic-swap__stage[hidden] {
  display: none;
}

@media (prefers-reduced-motion: reduce) {
  .ic-swap--staged .ic-swap__stage {
    display: none;
  }
  .ic-swap--staged .ic-swap__title .ic-swap__ch,
  .ic-swap--staged .ic-swap__title.is-in .ic-swap__ch {
    opacity: 1;
    transition: none;
  }
}
