/* ── Text reveal ───────────────────────────────────────────────────────────
   One way in for every piece of writing on the site: each word rises
   through a mask of its own, a beat behind the one before it.

   Dense text is the exception. A table row or a run of prose arrives a
   line at a time instead — a hundred words each on their own clock reads
   as noise rather than as writing, and takes four times as long to say
   nothing more.

   The boxes carry the same 0.14em of descender room the .oh mask carries,
   so a "y" or a "g" is never clipped at the bottom of its own mask.
   ────────────────────────────────────────────────────────────────────────── */

.rv {
  display: inline-block;
  overflow: hidden;
  vertical-align: top;
  /* Room on two sides, and a negative margin that gives it back, so the box
     clips nothing while the line measures exactly as it did.

     Below: descenders. A "y" hangs well past the baseline and the box is only
     as tall as the line.

     Right: the page is set on negative tracking — -0.07em on .h-section — and
     letter-spacing is subtracted after every character, the last one included.
     An inline-block sized to its own text therefore ends up narrower than the
     final glyph's ink, and overflow:hidden shears it. That is the sliced "s"
     on the end of a word. 0.12em covers the deepest tracking on the page with
     room to spare. */
  padding: 0 0.12em 0.2em 0;
  margin: 0 -0.12em -0.2em 0;
}

.rv > span {
  display: inline-block;
  will-change: transform;
}

/* A line at a time: the whole element in one mask. */
.rv-block {
  display: block;
  overflow: hidden;
  padding-bottom: 0.2em;
  margin-bottom: -0.2em;
}

.rv-block > span {
  display: block;
  will-change: transform;
}

/* The hidden state is set by js/reveal.js, not here, and deliberately so.
   A CSS transform would be read back by GSAP as a pixel offset — a
   translateY(110%) computes to a matrix, and a matrix is pixels — which
   then survives every tween that moves the percentage back to zero and
   leaves the words sitting below their own masks for good. Setting it from
   the script keeps the two in the same unit, and means the worst a missed
   reveal can do is show the text early rather than hide it for ever. */

/* ── Line mask ─────────────────────────────────────────────────────────────
   Nested inside the text element, so the element keeps its own margins and
   the layout is untouched; the padding/negative-margin pair gives descenders
   room inside the mask.
   ────────────────────────────────────────────────────────────────────────── */

/* The outer box is always the binding one: .oh wraps the .rv spans, so an
   allowance here smaller than theirs clips the descenders they had already
   made room for. It matches them. */
.oh {
  display: block;
  overflow: hidden;
  padding-bottom: 0.2em;
  margin-bottom: -0.2em;
}

.oh__inner { display: block; transform: translate3d(0, 100%, 0); }

.no-js .oh__inner { transform: none; }

@media (prefers-reduced-motion: reduce) {
  .oh__inner { transform: none; }
}
