/* ===================================================================
   ASCII SCRAMBLE — the characters inside the drawing keep re-rolling,
   ScrambleText-style, in place.

   The still <img> stays in the markup and paints first, so the drawing
   is there with JS off, on a failed fetch, and under reduced motion.
   ascii-scramble.js injects the same file as an inline <svg> beside it
   and only then adds .is-scrambling, which is what hands the drawing
   over. Nothing here fires on its own.

   The injected <svg> is given the <img>'s OWN classes as well as
   .ascii-scramble__svg, so whatever positioned the artwork positions
   the live copy too and this file needs to know nothing about the
   section. In the hero that is .hero__map — absolute, 750px, anchored
   240px off the container's right edge; in .reasons it is .reasons__art,
   which carries no rules at all and is simply a block in the flow.

   ⚠️ THE <svg> REPLACES THE <img>, it does not overlay it — which is why
   the <img> goes to `display: none` and not to `opacity: 0`. It carries
   the same classes and the same intrinsic ratio, so it lays out exactly
   where the <img> did whether that box is positioned or in the flow.
   Overlaying was the first cut and it only worked for the hero, where
   .hero__map is absolute so the two coincided: in .reasons, where the
   <img> is in the flow, the two stacked and the wrapper went from 466px
   to 932px tall, dropping the drawing a full height down the band.

   ⚠️ Which means the accessible NAME has to move with it. The script
   copies the <img>'s alt onto the <svg> as role="img" + aria-label, or
   marks it aria-hidden when the alt is empty — so exactly one of the two
   is in the accessibility tree, and it is the one being rendered.
   `display: none` would otherwise have dropped the hero drawing's alt.

   (ascii-reveal.css makes the opposite call for the opposite reason: its
   canvas is an absolute overlay that never replaces the <img>, so there
   the <img> must keep its box.)
   ------------------------------------------------------------------- */
.ascii-scramble__svg {
    /* Chrome/Safari/Firefox all take the ratio from the root width/height
       attributes, so `height: auto` resolves against the 785x530 box the
       same way it does for the <img> this replaces. */
    height: auto;
}

[data-ascii-scramble].is-scrambling > img { display: none; }

/* Belt and braces: with the effect off the live copy must not paint on
   top of the still one. It is only ever injected by the script, which
   adds .is-scrambling in the same breath, so this is the state between
   the two statements. */
[data-ascii-scramble]:not(.is-scrambling) > .ascii-scramble__svg { display: none; }

/* -------------------------------------------------------------------
   The fade that softens each swap (added 2026-08-21, "thodu smooth").

   An href swap is instantaneous, so on its own every change lands as a
   hard pop. A cell is therefore dropped to --scramble-dip in the same
   frame as the swap — with `transition: none`, so the drop itself is not
   animated and the new character arrives already faint — and then let go
   again, which is what fades it up. So a change reads as a soft bloom
   rather than a flicker.

   ⚠️ The fade MUST stay shorter than the shortest hold in
   ascii-scramble.js (FLIP_MIN). If it does not finish before the cell
   rolls again, cells in the busy parts of the drawing never get back to
   full opacity and the artwork visibly greys out where it is churning.
   150ms against a 110ms floor leaves that margin.

   opacity on a <use> is not compositor-promoted, so this repaints rather
   than composites. Measured worth it: with ~169 cells fading at once the
   rAF loop still holds 60fps with no frame over 20ms.
   ------------------------------------------------------------------- */
.ascii-scramble__svg {
    --scramble-fade: 0ms;
    --scramble-dip: 0.45;
}

.ascii-scramble__svg use {
    transition: opacity var(--scramble-fade) linear;
}

/* On for exactly one frame, in the frame the character changes. */
.ascii-scramble__svg use.is-dim {
    opacity: var(--scramble-dip);
    transition: none;
}

/* ⚠️ Set while the loop is stopped, and it is not cosmetic. A hidden tab
   does not advance CSS transitions either, so pausing mid-fade freezes a
   scatter of faint glyphs in the drawing — measured 261 of 2232 still
   under full opacity 700ms after a pause. This lands them all on 1 with
   no transition to wait for, so a stopped drawing is the artwork exactly,
   which is what every other bail path in here already guarantees. */
.ascii-scramble__svg.is-settled use {
    opacity: 1;
    transition: none;
}
