/* Glass Stage section shortcuts participate in layout; Classic keeps its rail. */ (function (root) { 'use strict'; function bottomClearance(viewport, boxes, gap = 16) { const occupied = boxes.reduce((height, b) => b.fixed && b.height > 0 && b.top < viewport && b.bottom > 0 ? Math.max(height, viewport - b.top) : height, 0); return Math.ceil(occupied + (occupied ? gap : 0)); } function mount(doc) { const win = doc.defaultView, html = doc.documentElement, rail = doc.getElementById('sectionRail'), panel = doc.getElementById('listPane'); if (!rail || !panel) return; const original = doc.createComment('section rail original position'); rail.before(original); let frame = 0; function active() { return html.dataset.layout === 'glass-stage'; } function schedule() { if (!frame) frame = win.requestAnimationFrame(measure); } function set(name, value) { if (html.style.getPropertyValue(name) !== value) html.style.setProperty(name, value); } function box(el) { const r = el.getBoundingClientRect(); return { top: r.top, bottom: r.bottom, height: el.classList.contains('hidden') ? 0 : r.height, fixed: win.getComputedStyle(el).position === 'fixed' }; } function measure() { frame = 0; if (!active()) return; const nav = doc.getElementById('bottomNav'), mini = doc.getElementById('miniBar'); const navBox = box(nav); set('--glass-nav-height', `${Math.ceil(navBox.height)}px`); // Read again after nav-height changes: the mini player's bottom depends on it. set('--glass-bottom-clearance', `${bottomClearance(win.innerHeight, [box(nav), box(mini)])}px`); } function sync() { if (active()) { if (rail.parentElement !== panel) panel.prepend(rail); schedule(); } else { if (rail.parentNode !== original.parentNode) original.after(rail); html.style.removeProperty('--glass-nav-height'); html.style.removeProperty('--glass-bottom-clearance'); } } const observer = new MutationObserver(sync); observer.observe(html, { attributes: true, attributeFilter: ['data-layout'] }); const chrome = [doc.getElementById('bottomNav'), doc.getElementById('miniBar')].filter(Boolean); const changes = new MutationObserver(schedule); chrome.forEach(el => changes.observe(el, { attributes: true, attributeFilter: ['class', 'style'] })); const resize = typeof win.ResizeObserver === 'function' ? new win.ResizeObserver(schedule) : null; chrome.forEach(el => resize?.observe(el)); win.addEventListener('resize', schedule); win.addEventListener('orientationchange', schedule); win.visualViewport?.addEventListener('resize', schedule); sync(); } if (typeof module !== 'undefined') module.exports = { bottomClearance }; else if (root.document.readyState === 'loading') root.document.addEventListener('DOMContentLoaded', () => mount(root.document), { once: true }); else mount(root.document); }(typeof window !== 'undefined' ? window : globalThis));