let API_BASE="https://cadence.nprstations.org/api/cadence/widget/0dec512e-f616-4d6e-a217-e81ae3bef37b/day",METADATA_FILE="https://www.knkx.org/cadence-schedule/v0",DAYS=["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],MONTHS=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],state={mode:"day",selectedDate:startOfDay(new Date),metadata:{},weekCache:new Map,weekTooltipMap:new Map,openTooltipKey:null},MS_PER_DAY=864e5,BASE_TRACK_HEIGHT_PX=720,MIN_SLOT_HEIGHT_PX=44,MAX_TRACK_HEIGHT_PX=1200,SHORT_OVERLAY_THRESHOLD_MINUTES=30,SHORT_OVERLAY_MIN_HEIGHT_PX=44;function esc(e){return String(e??"").replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")}function startOfDay(e){e=new Date(e);return e.setHours(0,0,0,0),e}function addDays(e,t){e=new Date(e);return e.setDate(e.getDate()+t),e}function startOfWeek(e){return addDays(startOfDay(e),-startOfDay(e).getDay())}function getDateKey(e){e=startOfDay(e);return e.getFullYear()+`-${String(e.getMonth()+1).padStart(2,"0")}-`+String(e.getDate()).padStart(2,"0")}function formatDateShort(e){return MONTHS[e.getMonth()]+" "+e.getDate()}function formatTime(e){var t,a,e=String(e).match(/T(\d{2}):(\d{2})/);return e?(t=12<=(a=parseInt(e[1],10))?"PM":"AM",a=a%12||12,"00"===(e=e[2])?a+" "+t:a+`:${e} `+t):""}function formatSlot(e,t){return formatTime(e)+" \u2013 "+formatTime(t)}function minutesFromLocalIso(e){e=String(e).match(/T(\d{2}):(\d{2})/);return e?60*parseInt(e[1],10)+parseInt(e[2],10):null}function getDurationMinutes(e,t){e=minutesFromLocalIso(e),t=minutesFromLocalIso(t);if(null===e||null===t)return 60;let a=t-e;return a<=0&&(a+=1440),a}function formatHourTick(e){return(e%12||12)+" "+(12<=e?"PM":"AM")}function buildHourTicks(e){return`<div class="week-axis-scale" style="--day-track-height:${e}px">${Array.from({length:23},(e,t)=>{t+=1;return`<span class="week-axis-tick" style="top:${t/24*100}%">${esc(formatHourTick(t))}</span>`}).join("")}</div>`}function isLiveNow(e,t){try{var a=new Date,s=new Date(e),i=new Date(t);return s<=a&&a<i}catch(e){return!1}}function showLoading(e="Loading..."){var t=Array.from({length:6},()=>`\n    <div class="skeleton-program">\n      <div class="skeleton-thumb"></div>\n      <div class="skeleton-lines">\n        <div class="skeleton-line name"></div>\n        <div class="skeleton-line time"></div>\n      </div>\n    </div>\n  `).join("");document.getElementById("content").innerHTML=`\n    <div class="state loading-state">\n      <div class="loading-status">\n        <div class="spinner" aria-hidden="true"></div>\n        <div>${esc(e)}</div>\n      </div>\n      <div class="skeleton-list" aria-hidden="true">${t}</div>\n    </div>\n  `}function setFallbackVisible(e){var t=document.getElementById("scheduleFallback");t&&(t.hidden=!e)}function showError(){setFallbackVisible(!0),document.getElementById("content").innerHTML='<div class="state">Could not load the schedule. Please try again later or contact us at <a href="mailto:info@knkx.org">info@knkx.org</a>.</div>'}function showEmpty(){document.getElementById("content").innerHTML='<div class="state">No programs scheduled for this day.</div>'}function getProgramModel(e){var t=e?.episode||{},a=t.programId,s=state.metadata.programs?.[a]||{},e=Array.isArray(e?.hosts)?e.hosts:e?.hosts?[e.hosts]:[],e=0<e.length?e:Array.isArray(s.hosts)?s.hosts:[],i=minutesFromLocalIso(t.start?.local),n=getDurationMinutes(t.start?.local,t.end?.local),l=null===i?1440:Math.max(1,1440-i),l=null===i?n:Math.max(1,Math.min(n,l));return{id:a,name:t.programName||"Unknown Program",time:formatSlot(t.start?.local,t.end?.local),startMinutes:i,durationMinutes:n,cappedDurationMinutes:l,imageUrl:s.imageUrl||"",description:s.description||"",hosts:e,genre:Array.isArray(s.genre)?s.genre:[],links:Array.isArray(s.links)?s.links:[],isLive:isLiveNow(t.start?.utc,t.end?.utc)}}function renderPrograms(e){closeWeeklyTooltip(),setFallbackVisible(!1),e&&0!==e.length?(e=e.map(e=>{var e=getProgramModel(e),t=`<h2 class="program-name">${esc(e.name)}</h2>`,a=e.isLive?'<span class="live-badge">Live Now</span>':"",a=`<div class="program-time-row"><h3 class="program-time">${esc(e.time)}</h3>${a}</div>`,s=e.imageUrl?`<img src="${esc(e.imageUrl)}" alt="Cover art for ${esc(e.name)}" class="program-image" onerror="this.style.display='none'">`:'<div class="program-image"></div>',i=e.hosts.length||e.description||e.genre.length||e.links.length,n=e.hosts.length?`\n      <section class="program-hosts">\n        <h3 class="program-hosts-label">Hosts</h3>\n        <ul class="program-hosts-list">${e.hosts.map(e=>`<li class="program-host">${esc(e)}</li>`).join("")}</ul>\n      </section>\n    `:"",l=e.genre.length?`\n      <section class="program-genre">\n        <h3 class="program-genre-label">Genre</h3>\n        <ul class="program-genre-list">${e.genre.map(e=>`<li class="program-genre">${esc(e)}</li>`).join("")}</ul>\n      </section>\n    `:"",i=i?`\n      <div class="program-details">\n        ${e.description?`<p class="program-description">${esc(e.description)}</p>`:""}\n        ${n}\n        ${l}\n        ${e.links.length?`<ul class="program-links">${e.links.map(e=>`<li><a href="${esc(e.url)}" target="_blank" rel="noopener noreferrer" class="program-link">${esc(e.label)}<span class="program-icon-link material-symbols-outlined">arrow_outward</span></a></li>`).join("")}</ul>`:""}\n      </div>\n    `:"";return`<div class="program${e.isLive?" live":""}" data-program-id="${esc(e.id)}"><div class="program-header">${s}<div class="program-info">${a}${t}</div><span class="program-toggle material-symbols-outlined">expand_circle_right</span></div>${i}</div>`}).join(""),document.getElementById("content").innerHTML=e):showEmpty()}function renderWeekly(e){closeWeeklyTooltip(),setFallbackVisible(!1),state.weekTooltipMap.clear();var t=e.days.filter(e=>e.failed).length,t=0<t?`<div class="weekly-warning">Some days failed to load (${t}/7). Showing all available schedule data.</div>`:"",e=e.days.map(e=>({day:e,models:e.items.map(e=>getProgramModel(e)).sort((e,t)=>null===e.startMinutes&&null===t.startMinutes?0:null===e.startMinutes?1:null===t.startMinutes?-1:e.startMinutes-t.startMinutes)})),a=e.flatMap(e=>e.models.map(e=>e.cappedDurationMinutes)).filter(e=>Number.isFinite(e)&&0<e),a=a.length?Math.min(...a):60,a=Math.ceil(24*MIN_SLOT_HEIGHT_PX*60/a);let r=Math.min(MAX_TRACK_HEIGHT_PX,Math.max(BASE_TRACK_HEIGHT_PX,a));a=e.map(e=>{let{day:o,models:t}=e;var e=DAYS[o.date.getDay()],a=formatDateShort(o.date),s=t.length?t.map((e,t)=>{var t=o.dateKey+"-"+t,a=(null===e.startMinutes?0:e.startMinutes)/1440*r,s=e.cappedDurationMinutes/1440*r,i=e.cappedDurationMinutes<SHORT_OVERLAY_THRESHOLD_MINUTES;let n=s,l=a;i&&(n=Math.max(SHORT_OVERLAY_MIN_HEIGHT_PX,s),l=a-n),(l=l<0?0:l)+n>r&&(n=Math.max(1,r-l));s=e.isLive?" live":"",a=i?" short-overlay":"";return state.weekTooltipMap.set(t,e),`<button class="weekly-item${s}${a}" type="button" data-tooltip-key="${esc(t)}" style="--slot-top:${l}px;--slot-height:${n}px" aria-label="Show details for ${esc(e.name)}"><span class="weekly-item-title">${esc(e.name)}</span><span class="weekly-item-time">${esc(e.time)}</span></button>`}).join(""):'<p class="weekly-empty-day">No programs scheduled.</p>';return`\n      <section class="week-day-column" aria-label="${esc(e)} ${esc(a)}">\n        <header class="week-day-header">\n          <h2 class="week-day-label">${esc(e)}</h2>\n          <p class="week-day-date">${esc(a)}</p>\n        </header>\n        <div class="week-day-list" style="--day-track-height:${r}px">${s}</div>\n      </section>\n    `}).join("");document.getElementById("content").innerHTML=t+`<div class="week-scroll"><div class="week-calendar"><aside class="week-axis" aria-hidden="true"><div class="week-axis-header"></div>${buildHourTicks(r)}</aside><div class="week-grid">${a}</div></div></div>`}function renderTooltipContent(e){var t=e.imageUrl?`<img src="${esc(e.imageUrl)}" alt="Cover art for ${esc(e.name)}" class="weekly-tooltip-image" onerror="this.style.display='none'">`:'<div class="weekly-tooltip-image" aria-hidden="true"></div>',a=e.description?`<p class="weekly-tooltip-description">${esc(e.description)}</p>`:'<p class="weekly-tooltip-empty">No description available.</p>',s=e.genre.length?`<div><p class="weekly-tooltip-genre-label">Genre</p><ul class="weekly-tooltip-genre-list">${e.genre.map(e=>`<li>${esc(e)}</li>`).join("")}</ul></div>`:"",i=e.links.length?`<div><p class="weekly-tooltip-links-label">Links</p><ul class="weekly-tooltip-links">${e.links.map(e=>`<li><a href="${esc(e.url)}" target="_blank" rel="noopener noreferrer" class="weekly-tooltip-link">${esc(e.label)}<span class="program-icon-link material-symbols-outlined">arrow_outward</span></a></li>`).join("")}</ul></div>`:"";return`\n    <button type="button" class="weekly-tooltip-close" aria-label="Close details">\xd7</button>\n    <div class="weekly-tooltip-top">\n      ${t}\n      <div class="weekly-tooltip-heading">\n        <h3 class="weekly-tooltip-title">${esc(e.name)}</h3>\n        <p class="weekly-tooltip-time">${esc(e.time)}</p>\n      </div>\n    </div>\n    ${a}\n    ${s}\n    ${i}\n  `}function positionTooltip(e,t){var e=e.getBoundingClientRect(),a=t.getBoundingClientRect();let s=e.left,i=e.bottom+8;(s=s+a.width>window.innerWidth-12?window.innerWidth-a.width-12:s)<12&&(s=12),(i=i+a.height>window.innerHeight-12?e.top-a.height-8:i)<12&&(i=12),t.style.left=s+"px",t.style.top=i+"px"}function openWeeklyTooltip(e,t){var a=state.weekTooltipMap.get(t),s=document.getElementById("weeklyTooltip");a&&s&&(s.innerHTML=renderTooltipContent(a),s.hidden=!1,s.setAttribute("aria-hidden","false"),state.openTooltipKey=t,positionTooltip(e,s))}function closeWeeklyTooltip(){var e=document.getElementById("weeklyTooltip");e&&(e.hidden=!0,e.setAttribute("aria-hidden","true"),e.innerHTML="",state.openTooltipKey=null)}async function loadMetadata(){try{var e=await fetch(METADATA_FILE);if(e.ok)return void(state.metadata=await e.json())}catch(e){console.warn("Metadata file not found or failed to load:",e)}state.metadata={}}async function fetchDayItems(e){var t,a=`${API_BASE}?date=${encodeURIComponent(e)}&format=json`,a=await fetch(a);if(a.ok)return t=await a.json(),Array.isArray(t)?t:Object.values(t).find(e=>Array.isArray(e))??[];throw new Error(`HTTP ${a.status} for `+e)}async function getWeekData(e){let a=startOfWeek(e);e=getDateKey(a);if(state.weekCache.has(e))return state.weekCache.get(e);var t=Array.from({length:7},(e,t)=>addDays(a,t));let s=await Promise.allSettled(t.map(e=>fetchDayItems(getDateKey(e))));t={weekKey:e,days:t.map((e,t)=>{t=s[t];return{date:e,dateKey:getDateKey(e),items:"fulfilled"===t.status?t.value:[],failed:"fulfilled"!==t.status}})};return state.weekCache.set(e,t),t}function buildTabs(){let i=document.getElementById("tabs"),n=(i.innerHTML="",startOfWeek(state.selectedDate)),l=startOfDay(state.selectedDate).getTime();DAYS.forEach((e,t)=>{let a=addDays(n,t);var t=startOfDay(a).getTime()===l,s=document.createElement("button");s.className="tab"+(t?" active":""),s.innerHTML=`<span>${e}</span><span class="tab-date">${esc(formatDateShort(a))}</span>`,s.setAttribute("role","tab"),s.setAttribute("aria-selected",t?"true":"false"),s.addEventListener("click",async()=>{state.selectedDate=startOfDay(a),await renderActiveView()}),i.appendChild(s)});var e=i.querySelector(".tab.active");e&&e.scrollIntoView({behavior:"smooth",block:"nearest",inline:"center"})}function updateModeControls(){var e="day"===state.mode,t=document.getElementById("modeDay"),a=document.getElementById("modeWeek"),s=document.querySelector(".tabs-wrapper");t.classList.toggle("active",e),a.classList.toggle("active",!e),t.setAttribute("aria-selected",e?"true":"false"),a.setAttribute("aria-selected",e?"false":"true"),s.hidden=!e}async function renderDayView(){buildTabs(),showLoading("Loading day schedule...");try{renderPrograms(await fetchDayItems(getDateKey(state.selectedDate)))}catch(e){console.error("Day schedule fetch failed:",e),showError()}}async function renderWeeklyView(){showLoading("Loading weekly schedule...");try{renderWeekly(await getWeekData(state.selectedDate))}catch(e){console.error("Weekly schedule fetch failed:",e),showError()}}async function renderActiveView(){updateModeControls(),closeWeeklyTooltip(),"day"===state.mode?await renderDayView():await renderWeeklyView()}async function setMode(e){"day"!==e&&"week"!==e||state.mode!==e&&(state.mode=e,await renderActiveView())}async function handlePrevDay(){"day"===state.mode&&(state.selectedDate=addDays(state.selectedDate,-1),await renderActiveView())}async function handleNextDay(){"day"===state.mode&&(state.selectedDate=addDays(state.selectedDate,1),await renderActiveView())}function bindEvents(){document.getElementById("prevWeek").addEventListener("click",handlePrevDay),document.getElementById("nextWeek").addEventListener("click",handleNextDay),document.getElementById("modeDay").addEventListener("click",()=>setMode("day")),document.getElementById("modeWeek").addEventListener("click",()=>setMode("week")),document.getElementById("content").addEventListener("click",e=>{var t=e.target.closest(".weekly-item");if(t)return(a=t.getAttribute("data-tooltip-key"))?void(state.openTooltipKey===a?closeWeeklyTooltip():openWeeklyTooltip(t,a)):void 0;var a,t=e.target.closest(".program-header");t&&(a=t.closest(".program"))&&a.classList.toggle("expanded")}),document.addEventListener("click",e=>{!e.target.closest(".weekly-tooltip-close")&&(!state.openTooltipKey||e.target.closest("#weeklyTooltip")||e.target.closest(".weekly-item"))||closeWeeklyTooltip()}),document.addEventListener("keydown",e=>{"Escape"===e.key&&closeWeeklyTooltip()}),window.addEventListener("resize",()=>{state.openTooltipKey&&closeWeeklyTooltip()})}(async()=>{bindEvents(),await loadMetadata(),await renderActiveView()})();