---
// ReadingProgress — scroll-position bar. One instance per page.
//
// Renders the same markup the legacy `full-guide/index.html` line 13
// carried: `<div class="reading-progress" aria-hidden="true"><span></span></div>`.
// The scroll listener is registered with `{ passive: true }`, matching
// app.js line 406 and the rule in `.agents/rules/animation.md` that a
// passive listener is required for any handler bound to a frequently
// fired event like scroll. Animating `width` here is fine — the bar
// is a 3px element at the very top of the viewport, so layout cost
// is negligible compared to the alternative of subscribing to
// IntersectionObserver and computing progress from a single sentinel
// per section.
//
// Language: this island does not read language. It has no string
// labels and the same progress semantics apply to EN and PT.

interface Props {
  // Optional override selector. Defaults to `.reading-progress span`
  // to match the legacy app.js query, in case a page renders the bar
  // with a different class name.
  target?: string;
}

const { target = '.reading-progress span' } = Astro.props;
---

<div class="reading-progress" aria-hidden="true">
  <span data-reading-progress-span></span>
</div>

<script is:inline define:vars={{ selector: target }}>
  // ReadingProgress island — passive scroll listener.
  // Wire-once guard mirrors CopyPrompt: a page that renders the bar
  // twice would otherwise stack two scroll handlers on `window`.
  (function () {
    if (window.__readingProgressWired) return;
    window.__readingProgressWired = true;

    const span = document.querySelector(selector);
    if (!span) return;

    function update() {
      const height = document.documentElement.scrollHeight - window.innerHeight;
      span.style.transform = `scaleX(${height > 0 ? window.scrollY / height : 0})`;
    }

    // `{ passive: true }` is non-negotiable. The legacy app.js binds
    // this listener passive, and a non-passive scroll handler on the
    // top edge of the document is exactly the kind of input latency
    // that fails INP. See `.agents/rules/animation.md`.
    window.addEventListener('scroll', update, { passive: true });
    // Set initial state — important when the user navigates with
    // #hash deep links and lands partway down a long page.
    update();
  })();
</script>
