/* ═══════════════════════════════════════════════════════════════════════════
   fx-progress — brain cells remaining.
   A specimen tag hanging off the header rule: "BRAIN CELLS REMAINING: 1" and
   a hand-drawn bar of drool. The count is pinned at 1 — there is nowhere
   lower to go — so all the motion is in how badly that 1 is holding on: the
   bar drains as the reader scrolls and the digit starts to shake.
   Where the browser has scroll timelines both run in CSS with no script.
   fx-progress.js adds what CSS can't (the spelling slips, the footer note,
   the measured header offset) and, in browsers without animation-timeline,
   seeks these same keyframes by hand.
   ═══════════════════════════════════════════════════════════════════════════ */

.fxp {
  position: sticky;
  /* Measured by fx-progress.js. The fallback is the header's height at a
     16px root, so the tag still sits under the rule with JS off. */
  top: var(--fxp-top, 52px);
  z-index: 50;                      /* under the header (60), over the page */
  /* Zero height: the tag overhangs into the hero's top padding instead of
     pushing the whole page down by a line. */
  height: 0;
  display: flex; justify-content: flex-end; align-items: flex-start;
  padding-inline: var(--pad-x);     /* lines the tag up under the nav */
  pointer-events: none;             /* nothing to click; don't shadow the page */
}

/* Same hairline and tint as the header's bottom rule, minus the top edge, so
   it reads as a tag hanging from that rule rather than a second toolbar. */
.fxp__tag {
  --fxp-track: .16em;               /* tracking, shared with the word's reserved width */
  display: inline-flex; align-items: center; gap: .6em;
  max-width: 100%;
  padding: .32rem .6rem .34rem;
  background: var(--paper-warm);
  border: var(--rule-thin) solid rgb(20 16 16 / .22);
  border-top: 0;
  border-radius: 0 0 7px 4px / 0 0 4px 7px;
  font-family: var(--font-ui); font-size: .68rem; font-weight: 700; line-height: 1;
  letter-spacing: var(--fxp-track); text-transform: uppercase;
  color: var(--ink-faint);
  white-space: nowrap;
}

/* On a viewport too narrow for "…: 1 (SOMEHOW)" the label ellipsises rather
   than running off the left edge. The padding/negative-margin pair gives the
   wobbling digit vertical room so the overflow clip never shaves it. */
.fxp__label {
  min-width: 0; overflow: hidden; text-overflow: ellipsis;
  padding-block: 3px; margin-block: -3px;
}

/* Reserve the full word's width so the 8-letter misspelling can't shove the
   number and bar sideways for a frame — the slip is in the letters, not the
   layout. Nine glyphs plus the letter-spacing that follows each of them. */
.fxp__word { display: inline-block; min-width: calc(9ch + 9 * var(--fxp-track)); }

.fxp__n { display: inline-block; color: var(--ink-soft); transform-origin: 50% 55%; }

.fxp__somehow { display: none; }
.fxp.is-footer .fxp__somehow { display: inline; }

/* The bar: one wobbly outline and a drool fill clipped to its inside. The
   fill is a long capsule with a rounded right end that slides left as the
   page scrolls, so the level always has a meniscus instead of a hard cut. */
.fxp__bar { flex: none; width: 64px; height: 10px; overflow: visible; }
.fxp__line { fill: none; stroke: var(--ink); stroke-width: 1.5; stroke-linejoin: round; }
.fxp__fill { fill: var(--drool); }

/* Phones: same tag, tighter, so the full "(SOMEHOW)" line still clears a
   375px viewport inside the nav's inset. It never touches the nav — it hangs
   below the header at every width — it just shouldn't hog the top edge. */
@media (max-width: 720px) {
  .fxp__tag { --fxp-track: .1em; font-size: .62rem; gap: .5em; padding: .28rem .5rem .3rem; }
  .fxp__bar { width: 56px; }
}

@media (prefers-reduced-motion: no-preference) {
  /* Scroll timelines: no script involved. The duration is 1ms rather than
     auto because Firefox needs a non-zero time before it attaches the
     timeline; either value is normalised to the full scroll range. */
  @supports (animation-timeline: scroll(root)) {
    .fxp__fill {
      animation: fxp-drain 1ms cubic-bezier(.45, .05, .75, .5) both;
      animation-timeline: scroll(root);
    }
    .fxp__n {
      animation: fxp-wobble 1ms linear both;
      animation-timeline: scroll(root);
    }
  }
  /* Everyone else: the same keyframes, paused, seeked from scrollY by
     fx-progress.js. 1000ms so currentTime is a plain 0–1000 scale. */
  .fxp.is-scrubbed .fxp__fill { animation: fxp-drain 1000ms cubic-bezier(.45, .05, .75, .5) both paused; }
  .fxp.is-scrubbed .fxp__n    { animation: fxp-wobble 1000ms linear both paused; }
}

/* Reduced motion: a still bar at half. Nothing drains, shakes or flickers. */
@media (prefers-reduced-motion: reduce) {
  .fxp__fill { transform: translateX(-29.5px); }
}

/* -59 user units leaves a 3–4 unit blob at the left end: a sliver, never empty. */
@keyframes fxp-drain {
  from { transform: translateX(0); }
  to   { transform: translateX(-59px); }
}

/* A scroll-linked tremor. Irregular spacing and growing amplitude: steady at
   the top of the page, a visible shake by the footer. Sub-pixel shifts and
   single-digit degrees — past that it stops reading as a hand and reads as
   a bug. */
@keyframes fxp-wobble {
  0%   { transform: none; }
  5%   { transform: translate(.06px, -.04px) rotate(.7deg); }
  9%   { transform: translate(-.08px, .05px) rotate(-1deg); }
  11%  { transform: translate(-.02px, .1px) rotate(.4deg); }
  17%  { transform: translate(.12px, -.07px) rotate(-1.5deg); }
  20%  { transform: translate(-.14px, .04px) rotate(1.8deg); }
  26%  { transform: translate(.05px, .14px) rotate(-.9deg); }
  29%  { transform: translate(-.2px, -.1px) rotate(2.5deg); }
  31%  { transform: translate(.17px, .06px) rotate(-2.2deg); }
  37%  { transform: translate(-.08px, .2px) rotate(3.1deg); }
  42%  { transform: translate(.26px, -.14px) rotate(-3.6deg); }
  44%  { transform: translate(-.22px, -.05px) rotate(1.4deg); }
  50%  { transform: translate(.1px, .26px) rotate(-4.4deg); }
  53%  { transform: translate(-.32px, .12px) rotate(4.8deg); }
  58%  { transform: translate(.3px, -.22px) rotate(-2deg); }
  61%  { transform: translate(-.12px, .3px) rotate(5.6deg); }
  63%  { transform: translate(.38px, .08px) rotate(-6deg); }
  69%  { transform: translate(-.4px, -.26px) rotate(3.2deg); }
  73%  { transform: translate(.2px, .34px) rotate(-6.9deg); }
  76%  { transform: translate(-.46px, .1px) rotate(7.3deg); }
  81%  { transform: translate(.5px, -.3px) rotate(-4deg); }
  84%  { transform: translate(-.18px, .38px) rotate(8.1deg); }
  88%  { transform: translate(.54px, .16px) rotate(-8.6deg); }
  91%  { transform: translate(-.56px, -.34px) rotate(5deg); }
  94%  { transform: translate(.3px, .42px) rotate(-9.3deg); }
  97%  { transform: translate(-.6px, .2px) rotate(9.8deg); }
  100% { transform: translate(.62px, -.4px) rotate(-10deg); }
}
