/**
 * About-page rail active-numeral highlight.
 *
 * The Liner Notes layout carries one oversized Bodoni numeral per movement in a
 * persistent left rail. This module lights the numeral of whichever movement is
 * currently in view by toggling `.is-active` on the movement element; the CSS does
 * the colour transition. Pure progressive enhancement — the numerals render
 * statically in low-opacity navy without this, so a load failure or a no-JS client
 * degrades to a still-legible page.
 *
 * One observer at a time, re-pointed on each `observe` call. The active movement is
 * the one nearest the top of the viewport among those currently intersecting, which
 * keeps a single numeral lit during the scroll rather than flickering between
 * adjacent movements at the boundary.
 */

let observer: IntersectionObserver | null = null;
let observed: Element[] = [];

function refreshActive(): void {
    let best: Element | null = null;
    let bestTop = Number.POSITIVE_INFINITY;

    for (const el of observed) {
        const rect = el.getBoundingClientRect();
        const viewportH = window.innerHeight || document.documentElement.clientHeight;
        // In view at all (any overlap with the viewport).
        const inView = rect.bottom > 0 && rect.top < viewportH;
        if (!inView) continue;
        // Prefer the movement whose top is closest to (but not far below) the fold.
        const distance = Math.abs(rect.top);
        if (distance < bestTop) {
            bestTop = distance;
            best = el;
        }
    }

    for (const el of observed) {
        el.classList.toggle('is-active', el === best);
    }
}

export function observe(...elements: Element[]): void {
    unobserve();
    observed = elements.filter(Boolean);
    if (observed.length === 0) return;

    // IntersectionObserver only tells us *that* visibility changed; the actual
    // "which is nearest the fold" decision is recomputed from live rects so the
    // choice stays correct mid-scroll.
    observer = new IntersectionObserver(() => refreshActive(), {
        threshold: [0, 0.25, 0.5, 0.75, 1],
    });
    for (const el of observed) observer.observe(el);

    // Seed once so the first movement lights before any scroll.
    refreshActive();
}

export function unobserve(): void {
    observer?.disconnect();
    observer = null;
    for (const el of observed) el.classList.remove('is-active');
    observed = [];
}
