// Text-to-Scene comparison videos: one player and a scene picker (scripts/t2s_sandbox_round.py writes the data). // The video plays muted while it is on screen, like a moving figure; not with reduced motion, and not again after the // viewer pauses it. const card = document.getElementById('t2s-compare'); if (card) { const items = JSON.parse(document.getElementById('cmp-data').textContent); const tabs = [...card.querySelectorAll('.cmp-picker [role=tab]')]; const video = card.querySelector('.cmp-video'); const brief = document.getElementById('cmp-brief'); const more = Object.assign(document.createElement('button'), {type: 'button', className: 'cmp-more'}); brief.after(more); const fit = () => { // "Full brief" only when the clamp actually hides text brief.classList.remove('open'); more.textContent = 'Full brief'; more.hidden = brief.scrollHeight <= brief.clientHeight + 1; }; more.addEventListener('click', () => { more.textContent = brief.classList.toggle('open') ? 'Less' : 'Full brief'; }); const calm = matchMedia('(prefers-reduced-motion: reduce)').matches; let inView = false, held = false, ours = false; video.addEventListener('pause', () => { if (!ours && !video.ended) held = true; ours = false; }); video.addEventListener('play', () => { held = false; }); const stop = () => { if (!video.paused) { ours = true; video.pause(); } }; const auto = () => { if (!calm && !held && inView && card.offsetParent !== null) video.play().catch(() => {}); }; new IntersectionObserver(([en]) => { inView = en.intersectionRatio >= 0.4; inView ? auto() : stop(); }, {threshold: [0, 0.4]}).observe(video); function select(i, focus) { const it = items[i]; tabs.forEach((b, j) => { const on = j === i; b.setAttribute('aria-selected', String(on)); b.tabIndex = on ? 0 : -1; }); if (focus) tabs[i].focus(); stop(); video.poster = it.poster; video.src = it.mp4; video.setAttribute('aria-label', it.title + ': four agents build the scene, then an orbit of each finished scene'); auto(); document.getElementById('cmp-title').textContent = it.title; brief.textContent = it.brief; document.getElementById('cmp-scores').replaceChildren(...it.scores.map(([name, score]) => { const li = document.createElement('li'), n = document.createElement('span'), s = document.createElement('b'); n.textContent = name; s.textContent = score; li.append(n, s); return li; })); document.getElementById('cmp-gallery').href = it.gallery; fit(); } tabs.forEach((b, i) => { b.addEventListener('click', () => select(i)); b.addEventListener('keydown', e => { if (!['ArrowLeft', 'ArrowRight', 'Home', 'End'].includes(e.key)) return; e.preventDefault(); const j = e.key === 'Home' ? 0 : e.key === 'End' ? tabs.length - 1 : (i + (e.key === 'ArrowRight' ? 1 : -1) + tabs.length) % tabs.length; select(j, true); }); }); // the Image-to-Scene tab hides this card: stop the video with it, and resume when the tab comes back document.querySelectorAll('.seg[data-for=cases] button').forEach(b => b.addEventListener('click', () => { stop(); setTimeout(() => { fit(); auto(); }); })); fit(); addEventListener('resize', () => { if (!brief.classList.contains('open')) fit(); }); }