`);
document.querySelector('.login-card').addEventListener('submit',async e=>{e.preventDefault();const b=e.target.querySelector('button');b.disabled=true;document.querySelector('#login-error').textContent='Connecting…';try{const r=await fetch('/api/login',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(Object.fromEntries(new FormData(e.target)))}),o=await r.json();if(!r.ok)throw Error(o.error);document.querySelector('#login-screen').remove();await loadData()}catch(x){document.querySelector('#login-error').textContent=x.message;b.disabled=false}});
async function api(p){const r=await fetch(p);if(!r.ok)throw Error((await r.json()).error||'Request failed');return r.json()}
async function loadData(){[categories,channels]=await Promise.all([api('/api/categories'),api('/api/channels')]);tabs.innerHTML=categories.map((c,i)=>``).join('');selectedCategory=categories[0]?.category_id||null;render();tabs.querySelectorAll('.category').forEach(b=>b.onclick=()=>{tabs.querySelectorAll('.category').forEach(x=>x.classList.remove('active'));b.classList.add('active');selectedCategory=b.dataset.id;document.querySelector('.guide-panel h3').textContent=`${b.textContent} Guide`;render()})}
function render(){const rs=channels.filter(c=>!selectedCategory||String(c.category_id)===String(selectedCategory));list.innerHTML=rs.map((c,i)=>`
${c.stream_icon?``:esc((c.name||'?')[0])}
${esc(c.num||'')}
${esc(c.name)}
NOW Live programming12:00 PM – 1:00 PM
`).join('');document.querySelectorAll('.channel-row').forEach(r=>{r.onclick=e=>{if(!e.target.closest('.star'))select(r)};r.ondblclick=e=>{if(!e.target.closest('.star')){select(r);openFullscreen()}}});if(rs[0])select(document.querySelector('.channel-row'))}
let hls, playbackToken=0;
function select(r){
const token=++playbackToken;
document.querySelectorAll('.channel-row').forEach(x=>x.classList.remove('selected'));r.classList.add('selected');
document.querySelector('#hero-title').textContent=r.dataset.name;document.querySelector('#hero-meta').textContent=`Channel ${r.dataset.number} • Live now`;
const v=document.querySelector('#video-player'), status=document.querySelector('#player-status'), src=`/stream/${r.dataset.stream}?mode=mp4`;
if(hls){hls.destroy();hls=null} v.pause(); v.removeAttribute('src'); v.load(); status.textContent='Loading…';
const report=(e)=>{ if(token!==playbackToken || e?.name==='AbortError' || /interrupted/i.test(e?.message||'')) return; status.textContent=`Playback blocked: ${e.message||'browser denied play'}` };
const start=()=>{if(token===playbackToken)v.play().catch(report)};
// Prefer hls.js when available so Safari/Chrome use the same proxied
// playlist and segment handling. Native HLS is only the fallback.
v.src=src; v.load(); start();
v.onplaying=()=>{if(token===playbackToken)status.textContent=''};v.onerror=()=>{if(token===playbackToken)status.textContent=`Video error: ${v.error?.message||v.error?.code||'unknown'}`}
}
function openFullscreen(){const v=document.querySelector('#video-player');if(v.requestFullscreen)v.requestFullscreen().catch(()=>{});else if(v.webkitEnterFullscreen)v.webkitEnterFullscreen()}
function esc(s){return String(s).replace(/[&<>"']/g,x=>({'&':'&','<':'<','>':'>','"':'"',"'":'''}[x]))}