// Default template bootstrap for The Studio Player.
// This script runs only for players rendered with data-template-slug="default".
// Responsibilities:
// - extend the shared THESPL_Player_UI facade with default-specific UI helpers,
// - bind playlist, variants, transport and infotext to DOM elements of this template,
// - drive the custom canvas-based ring and waveform visualization,
// - coordinate with THESPL_Player_Store and THESPL_Player_Core without overriding them.
(function(){
	var ensureAsset = function(url, done, fail){
		var scripts = Array.prototype.slice.call(document.scripts || []);
		var existed = scripts.some(function(s){ return (s.src || '').indexOf(url) !== -1; });
		if (existed) {
			try {
				if (typeof done === 'function') {
					done();
				}
			} catch (e) {}
			return;
		}
		var el = document.createElement('script');
		el.src = url;
		el.async = false;
		el.onload = function(){
			try {
				if (typeof done === 'function') {
					done();
				}
			} catch (e) {}
		};
		el.onerror = function(){
			try {
				if (typeof fail === 'function') {
					fail();
				}
			} catch (e) {}
		};
		try {
			document.head.appendChild(el);
		} catch (e) {}
	};
	var getPluginBase = function(){
		var scripts = Array.prototype.slice.call(document.scripts || []);
		for (var k = 0; k < scripts.length; k++) {
			var src = String(scripts[k].src || '');
			var m = src.indexOf('/assets/js/player-core.js');
			if (m > 0) {
				return src.slice(0, m + 1);
			}
		}
		var cur = document.currentScript && document.currentScript.src ? document.currentScript.src : '';
		var i = cur.indexOf('/templates/');
		if (i > 0) {
			return cur.slice(0, i + 1);
		}
		return '';
	};
	var existing = window.THESPL_Player_UI || {};
	var UI = {
		renderVariants: function(wrap, variants, onSelect){
			wrap.innerHTML = '';
			for (var i = 0; i < variants.length; i++) {
				var label = variants[i] || ('Variant ' + (i + 1));
				var btn = document.createElement('button');
				btn.type = 'button';
				btn.className = 'thespl-player__variant';
				btn.setAttribute('role', 'tab');
				btn.setAttribute('aria-selected', i === 0 ? 'true' : 'false');
				btn.textContent = label;
				btn.addEventListener('click', function(i, btn){ return function(){
					var all = wrap.querySelectorAll('.thespl-player__variant');
					all.forEach(function(b){ b.setAttribute('aria-selected', 'false'); });
					btn.setAttribute('aria-selected', 'true');
					onSelect(i);
				}; }(i, btn));
				wrap.appendChild(btn);
			}
		},
		renderPlaylist: function(wrap, tracks, onSelect, fallbackCoverUrl){
			wrap.innerHTML = '';
			for (var i = 0; i < tracks.length; i++) {
				var t = tracks[i];
				var item = document.createElement('div');
				item.className = 'thespl-player__track';
				item.setAttribute('role', 'listitem');
				item.setAttribute('tabindex', '0');
				item.setAttribute('aria-current', i === 0 ? 'true' : 'false');
				var img = document.createElement('img');
				img.className = 'thespl-player__cover';
				img.alt = '';
				img.src = (t.cover && t.cover.url) ? t.cover.url : (fallbackCoverUrl || '');
				var meta = document.createElement('div');
				var title = document.createElement('div');
				title.className = 'thespl-player__track-title';
				title.textContent = t.title || '';
				var artist = document.createElement('div');
				artist.className = 'thespl-player__track-artist';
				artist.textContent = t.artist || '';
				meta.appendChild(title);
				meta.appendChild(artist);
				item.appendChild(img);
				item.appendChild(meta);
				var select = (function(i, item){ return function(){
					var all = wrap.querySelectorAll('.thespl-player__track');
					all.forEach(function(n){ n.setAttribute('aria-current', 'false'); });
					item.setAttribute('aria-current', 'true');
					onSelect(i);
				}; })(i, item);
				item.addEventListener('click', select);
				item.addEventListener('keydown', function(e){
					if (e.key === 'Enter' || e.key === ' ') {
						e.preventDefault();
						select();
					}
				});
				wrap.appendChild(item);
			}
		}
	};
	var initDefaultCanvas = function(root){
		var canvas = root.querySelector('.thespl-player__canvas');
		var audio = root.querySelector('.thespl-player__audio');
		if (!canvas || !audio || !canvas.getContext) {
			return;
		}
		var ctx = canvas.getContext('2d');
		if (!ctx) {
			return;
		}
		var store = window.THESPL_Player_Store || null;
		var size = Math.min(canvas.width || 0, canvas.height || 0) || 340;
		var center = size / 2;
		var ringWidth = 24;
		var knobRadius = 9;
		var radius = Math.max(10, (size / 2) - ringWidth * 1.4);
		var isDragging = false;
		var dragProgress = 0;
		var touchPreventScrollActive = false;
		var lastScrubUpTimeMs = 0;
		var api = null;
		try {
			api = root._thesplPlayerApi || null;
		} catch (e) {}
		var drawProgress = 0;
		var targetProgress = 0;
		var drawBufferedProgress = 0;
		var targetBufferedProgress = 0;
		var drawCurrentSec = 0;
		var drawDurationSec = 0;
		var coverImg = null;
		var coverReady = false;
		var coverBufferCanvas = null;
		var waveformsByTrack = {};
		var currentWaveforms = [];
		var waveformReqId = 0;
		var lastTrackKey = '';
		var clamp01 = function(v){
			var n = typeof v === 'number' && isFinite(v) ? v : 0;
			if (n < 0) {
				return 0;
			}
			if (n > 1) {
				return 1;
			}
			return n;
		};
		var applyPreviewProgress = function(progress){
			var d = audio.duration;
			if (!d || !isFinite(d)) {
				return;
			}
			var p = clamp01(progress);
			var tgt = p * d;
			drawCurrentSec = tgt;
			drawDurationSec = d;
			targetProgress = p;
		};
		var commitSeekFromProgress = function(progress){
			var d = audio.duration;
			if (!d || !isFinite(d)) {
				return;
			}
			var p = clamp01(progress);
			var tgt = p * d;
			try {
				audio.currentTime = tgt;
			} catch (e) {}
			applyPreviewProgress(p);
		};
		var clientToCanvasXY = function(clientX, clientY){
			if (!canvas) {
				return null;
			}
			var rect = canvas.getBoundingClientRect();
			var w = rect.width || 0;
			var h = rect.height || 0;
			if (!w || !h) {
				return null;
			}
			var scaleX = (canvas.width || w) / w;
			var scaleY = (canvas.height || h) / h;
			return {
				x: (clientX - rect.left) * scaleX,
				y: (clientY - rect.top) * scaleY,
				scale: (scaleX + scaleY) / 2
			};
		};
		var progressFromClientXYLoose = function(clientX, clientY){
			if (!canvas || !ctx) {
				return null;
			}
			var pt = clientToCanvasXY(clientX, clientY);
			if (!pt) {
				return null;
			}
			var x = pt.x - center;
			var y = pt.y - center;
			var dist = Math.sqrt(x * x + y * y);
			if (!isFinite(dist) || dist < 6) {
				return null;
			}
			var angle = Math.atan2(y, x) + (Math.PI / 2);
			if (angle < 0) {
				angle += Math.PI * 2;
			}
			return clamp01(angle / (Math.PI * 2));
		};
		var progressFromClientXYStrict = function(clientX, clientY){
			if (!canvas || !ctx) {
				return null;
			}
			var pt = clientToCanvasXY(clientX, clientY);
			if (!pt) {
				return null;
			}
			var x = pt.x - center;
			var y = pt.y - center;
			var dist = Math.sqrt(x * x + y * y);
			if (!isFinite(dist) || dist < 6) {
				return null;
			}
			if (dist < (radius - ringWidth * 1.8) || dist > (radius + ringWidth * 1.8)) {
				return null;
			}
			var angle = Math.atan2(y, x) + (Math.PI / 2);
			if (angle < 0) {
				angle += Math.PI * 2;
			}
			return clamp01(angle / (Math.PI * 2));
		};
		var getTheme = function(){
			var ringBase = 'rgba(17,24,39,0.85)';
			var progress = 'rgba(34,211,238,0.9)';
			var buffered = 'rgba(34,211,238,0.3)';
			var waveform = 'rgba(90,90,90,0.65)';
			var knob = '#fff';
			var makeBufferedFrom = function(col){
				var s = String(col || '').trim();
				if (!s.length) {
					return buffered;
				}
				var m = null;
				if (s.indexOf('rgba(') === 0) {
					m = s.match(/rgba\(\s*(\d+)[^\d]+(\d+)[^\d]+(\d+)[^\d]+([0-9.]+)/i);
					if (m) {
						var r = parseInt(m[1], 10) || 0;
						var g = parseInt(m[2], 10) || 0;
						var b = parseInt(m[3], 10) || 0;
						return 'rgba(' + r + ',' + g + ',' + b + ',0.3)';
					}
				}
				if (s.indexOf('rgb(') === 0) {
					m = s.match(/rgb\(\s*(\d+)[^\d]+(\d+)[^\d]+(\d+)/i);
					if (m) {
						var r2 = parseInt(m[1], 10) || 0;
						var g2 = parseInt(m[2], 10) || 0;
						var b2 = parseInt(m[3], 10) || 0;
						return 'rgba(' + r2 + ',' + g2 + ',' + b2 + ',0.3)';
					}
				}
				if (s.charAt(0) === '#') {
					var hex = s.replace(/[^0-9a-fA-F]/g, '');
					if (hex.length === 3) {
						var r3 = hex.charAt(0);
						var g3 = hex.charAt(1);
						var b3 = hex.charAt(2);
						hex = r3 + r3 + g3 + g3 + b3 + b3;
					}
					if (hex.length === 6) {
						var r4 = parseInt(hex.slice(0, 2), 16) || 0;
						var g4 = parseInt(hex.slice(2, 4), 16) || 0;
						var b4 = parseInt(hex.slice(4, 6), 16) || 0;
						return 'rgba(' + r4 + ',' + g4 + ',' + b4 + ',0.3)';
					}
				}
				return buffered;
			};
			try {
				if (root && window.getComputedStyle) {
					var style = window.getComputedStyle(root);
					var cssRing = style.getPropertyValue('--thespl-ring-base') || '';
					var cssAccent = style.getPropertyValue('--thespl-accent') || '';
					var cssWave = style.getPropertyValue('--thespl-waveform') || style.getPropertyValue('--thespl-wave') || '';
					var cssBuffered = style.getPropertyValue('--thespl-buffered') || '';
					cssRing = cssRing.trim();
					cssAccent = cssAccent.trim();
					cssWave = cssWave.trim();
					cssBuffered = cssBuffered.trim();
					if (cssRing) {
						ringBase = cssRing;
					}
					if (cssAccent) {
						progress = cssAccent;
						if (!cssBuffered) {
							buffered = makeBufferedFrom(cssAccent);
						}
					}
					if (cssBuffered) {
						buffered = cssBuffered;
					}
					if (cssWave) {
						waveform = cssWave;
					}
				}
			} catch (e) {}
			return {
				ringBase: ringBase,
				progress: progress,
				buffered: buffered,
				waveform: waveform,
				knob: knob
			};
		};
		var formatTime = function(sec){
			var s = Math.max(0, isFinite(sec) ? sec : 0);
			var m = Math.floor(s / 60);
			var r = Math.floor(s % 60);
			var rStr = String(r);
			if (rStr.length < 2) {
				rStr = ('0' + rStr).slice(-2);
			}
			return m + ':' + rStr;
		};
		var getBufferedEndSec = function(){
			try {
				var b = audio.buffered;
				if (!b || !b.length) {
					return 0;
				}
				var maxEnd = 0;
				for (var i = 0; i < b.length; i++) {
					var end = b.end(i);
					if (end > maxEnd) {
						maxEnd = end;
					}
				}
				return maxEnd;
			} catch (e) {
				return 0;
			}
		};
		var getDisplayDurationSecForTrack = function(track){
			var dNative = audio.duration;
			if (dNative && isFinite(dNative) && dNative > 0) {
				return dNative;
			}
			var t = track || null;
			var dMeta = t && isFinite(t.duration) && t.duration > 0 ? t.duration : 0;
			return dMeta;
		};
		var setCoverFromTrack = function(track){
			coverImg = null;
			coverReady = false;
			coverBufferCanvas = null;
			var url = '';
			if (track && track.cover && typeof track.cover.url === 'string' && track.cover.url) {
				url = track.cover.url;
			} else {
				try {
					var cfg = window.thesplPlayer || {};
					if (typeof cfg.defaultCoverUrl === 'string') {
						url = cfg.defaultCoverUrl;
					}
				} catch (e) {}
			}
			if (!canvas || !url) {
				return;
			}
			var img = new Image();
			try {
				img.crossOrigin = 'anonymous';
			} catch (e) {}
			img.onload = function(){
				coverImg = img;
				coverReady = true;
				if (!ctx || !size || !isFinite(size)) {
					return;
				}
				var buf = document.createElement('canvas');
				buf.width = size;
				buf.height = size;
				var bctx = buf.getContext('2d');
				if (!bctx) {
					return;
				}
				var innerR = radius - ringWidth * 0.9;
				bctx.save();
				bctx.beginPath();
				bctx.arc(center, center, innerR, 0, Math.PI * 2);
				bctx.clip();
				var iw = img.naturalWidth || img.width;
				var ih = img.naturalHeight || img.height;
				if (iw && ih) {
					var sLen = Math.min(iw, ih);
					var sx = (iw - sLen) / 2;
					var sy = (ih - sLen) / 2;
					try {
						bctx.imageSmoothingQuality = 'high';
					} catch (e) {}
					bctx.drawImage(img, sx, sy, sLen, sLen, center - innerR, center - innerR, innerR * 2, innerR * 2);
				}
				bctx.restore();
				coverBufferCanvas = buf;
			};
			img.onerror = function(){
				coverImg = null;
				coverReady = false;
				coverBufferCanvas = null;
				try {
					var cfg2 = window.thesplPlayer || {};
					var fallback = typeof cfg2.defaultCoverUrl === 'string' ? cfg2.defaultCoverUrl : '';
					if (fallback && fallback !== url) {
						img.src = fallback;
						return;
					}
				} catch (e) {}
			};
			img.src = url;
		};
		var loadWaveformForTrack = function(track, trackIndex){
			if (!track) {
				currentWaveforms = [];
				return;
			}
			var hasWaveformUrl = !!(track.waveform && typeof track.waveform.url === 'string' && track.waveform.url);
			if (!hasWaveformUrl) {
				currentWaveforms = [];
				return;
			}
			currentWaveforms = [];
			var key = '';
			if (track.audio && typeof track.audio.attachment_id !== 'undefined' && track.audio.attachment_id !== null) {
				key = String(track.audio.attachment_id);
			} else {
				key = track.waveform.url;
			}
			if (waveformsByTrack[key]) {
				currentWaveforms = waveformsByTrack[key] || [];
				return;
			}
			waveformReqId++;
			var myId = waveformReqId;
			try {
				fetch(track.waveform.url, { method: 'GET', headers: { 'Accept': 'application/json' } }).then(function(res){
					if (!res || !res.ok) {
						try {
							if (res && res.status === 404 && window.THESPL_Player_Pro && typeof window.THESPL_Player_Pro.init === 'function') {
								var cfg = window.thesplPlayer || {};
								var key = 'error-waveform-not-found';
								var msg = '';
								if (cfg.l10n && typeof cfg.l10n[key] === 'string' && cfg.l10n[key]) {
									msg = cfg.l10n[key];
								}
								if (!msg) {
									msg = 'Waveform file not found (404).';
								}
								var wrap = root.querySelector('.thespl-player__playlist');
								if (wrap) {
									var items = wrap.querySelectorAll('.thespl-player__track');
									var idx = isFinite(trackIndex) ? trackIndex : -1;
									if (idx >= 0 && idx < items.length) {
										var item = items[idx];
										if (item && !item.classList.contains('thespl-player__track--unplayable')) {
											var titleEl = item.querySelector('.thespl-player__track-title');
											if (titleEl) {
												var baseTitle = titleEl.getAttribute('data-thespl-base-title') || titleEl.textContent || '';
												if (!titleEl.hasAttribute('data-thespl-base-title')) {
													titleEl.setAttribute('data-thespl-base-title', baseTitle);
												}
												if (baseTitle) {
													titleEl.textContent = baseTitle + ' – ' + msg;
												} else {
													titleEl.textContent = msg;
												}
											}
										}
									}
								}
							}
						} catch (err) {}
						return null;
					}
					return res.json();
				}).then(function(data){
					if (!data || waveformReqId !== myId) {
						return;
					}
					var perVariant = [];
					if (data && Object.prototype.hasOwnProperty.call(data, 'pairs') && Array.isArray(data.pairs) && data.pairs.length) {
						perVariant = data.pairs.map(function(p){
							var src = null;
							if (p && Array.isArray(p.pairComposite)) {
								src = p.pairComposite;
							} else if (p && Array.isArray(p.composite)) {
								src = p.composite;
							}
							if (!src || !src.length) {
								return new Float32Array(0);
							}
							var out = new Float32Array(src.length);
							var max = 0;
							for (var i = 0; i < src.length; i++) {
								var v = typeof src[i] === 'number' ? src[i] : 0;
								var a = Math.abs(v);
								out[i] = a;
								if (a > max) {
									max = a;
								}
							}
							if (max > 0) {
								var inv = 1 / max;
								for (var j = 0; j < out.length; j++) {
									out[j] *= inv;
								}
							}
							return out;
						});
					} else if (data && Object.prototype.hasOwnProperty.call(data, 'data') && Array.isArray(data.data) && data.data.length) {
						var arr = data.data;
						var joined = Array.isArray(arr[0]) ? arr[0] : arr;
						var outArr = new Float32Array(joined.length);
						var maxVal = 0;
						for (var k = 0; k < joined.length; k++) {
							var v2 = typeof joined[k] === 'number' ? joined[k] : 0;
							var a2 = Math.abs(v2);
							outArr[k] = a2;
							if (a2 > maxVal) {
								maxVal = a2;
							}
						}
						if (maxVal > 0) {
							var inv2 = 1 / maxVal;
							for (var m = 0; m < outArr.length; m++) {
								outArr[m] *= inv2;
							}
						}
						perVariant = [outArr];
					}
					if (!perVariant || waveformReqId !== myId) {
						return;
					}
					waveformsByTrack[key] = perVariant;
					currentWaveforms = perVariant;
				}).catch(function(){
				});
			} catch (e) {}
		};
		var drawCoverCircle = function(){
			if (!ctx || !coverImg || !coverReady) {
				return;
			}
			if (coverBufferCanvas) {
				ctx.drawImage(coverBufferCanvas, 0, 0);
				return;
			}
			var innerR = radius - ringWidth * 0.9;
			ctx.save();
			ctx.beginPath();
			ctx.arc(center, center, innerR, 0, Math.PI * 2);
			ctx.clip();
			var iw = coverImg.naturalWidth || coverImg.width;
			var ih = coverImg.naturalHeight || coverImg.height;
			if (iw && ih) {
				var sLen = Math.min(iw, ih);
				var sx = (iw - sLen) / 2;
				var sy = (ih - sLen) / 2;
				try {
					ctx.imageSmoothingQuality = 'high';
				} catch (e) {}
				ctx.drawImage(coverImg, sx, sy, sLen, sLen, center - innerR, center - innerR, innerR * 2, innerR * 2);
			}
			ctx.restore();
		};
		var drawCenterTime = function(){
			if (!ctx) {
				return;
			}
			var label = formatTime(drawCurrentSec) + (drawDurationSec ? ' / ' + formatTime(drawDurationSec) : '');
			if (!label) {
				return;
			}
			ctx.font = 'bold 14px system-ui, sans-serif';
			ctx.textAlign = 'center';
			ctx.textBaseline = 'middle';
			var metrics = ctx.measureText(label);
			var padding = 12;
			var bgW = metrics.width + padding * 2;
			var bgH = 28;
			ctx.save();
			ctx.fillStyle = 'rgba(0,0,0,0.5)';
			if (typeof ctx.roundRect === 'function') {
				ctx.beginPath();
				ctx.roundRect(center - bgW / 2, center - bgH / 2, bgW, bgH, 14);
				ctx.fill();
			} else {
				ctx.fillRect(center - bgW / 2, center - bgH / 2, bgW, bgH);
			}
			ctx.restore();
			ctx.fillStyle = '#fff';
			ctx.fillText(label, center, center);
		};
		var drawWaveform = function(progress, wf){
			if (!ctx || !wf || !wf.length) {
				return;
			}
			var innerR = radius - ringWidth / 2;
			var outerR = radius + ringWidth / 2;
			var bars = wf.length;
			var cacheKey = String(bars);
			if (!drawWaveform._polarCache) {
				drawWaveform._polarCache = {};
			}
			var polar = drawWaveform._polarCache[cacheKey] || null;
			if (!polar) {
				var cos = new Float32Array(bars);
				var sin = new Float32Array(bars);
				for (var i = 0; i < bars; i++) {
					var t = i / bars;
					var a = -Math.PI / 2 + t * 2 * Math.PI;
					cos[i] = Math.cos(a);
					sin[i] = Math.sin(a);
				}
				polar = { cos: cos, sin: sin };
				drawWaveform._polarCache[cacheKey] = polar;
			}
			var theme = getTheme();
			var lw = Math.max(1.3, (2 * Math.PI * radius) / (bars * 1.5));
			ctx.strokeStyle = theme.waveform;
			ctx.lineWidth = lw;
			ctx.lineCap = 'round';
			ctx.beginPath();
			for (var j = 0; j < bars; j++) {
				var amp = wf[j];
				var h = amp * (outerR - innerR);
				var r0 = radius - h / 2;
				var r1 = radius + h / 2;
				var c = polar.cos[j];
				var s = polar.sin[j];
				ctx.moveTo(center + r0 * c, center + r0 * s);
				ctx.lineTo(center + r1 * c, center + r1 * s);
			}
			ctx.stroke();
		};
		var ensureTrackAssets = function(){
			if (!store) {
				return;
			}
			var state = store.getPlaylist(root) || {};
			var sel = store.getSelection(root) || {};
			var tracks = Array.isArray(state.tracks) ? state.tracks : [];
			if (!tracks.length) {
				return;
			}
			var trackIndex = isFinite(sel.trackIndex) ? sel.trackIndex : (isFinite(state.currentTrackIndex) ? state.currentTrackIndex : 0);
			if (!isFinite(trackIndex) || trackIndex < 0 || trackIndex >= tracks.length) {
				trackIndex = 0;
			}
			var t = tracks[trackIndex] || null;
			if (!t) {
				return;
			}
			var key = '';
			if (t.audio && typeof t.audio.attachment_id !== 'undefined' && t.audio.attachment_id !== null) {
				key = String(t.audio.attachment_id);
			} else if (t.waveform && typeof t.waveform.url === 'string' && t.waveform.url) {
				key = t.waveform.url;
			} else if (t.cover && typeof t.cover.url === 'string' && t.cover.url) {
				key = t.cover.url;
			}
			if (!key || key === lastTrackKey) {
				return;
			}
			lastTrackKey = key;
			setCoverFromTrack(t);
			loadWaveformForTrack(t, trackIndex);
		};
		var renderFrame = function(){
			var cur = 0;
			var dur = 0;
			var bufEnd = 0;
			try {
				cur = typeof audio.currentTime === 'number' ? audio.currentTime : 0;
			} catch (e) {}
			var trackForDur = null;
			var variantIndex = 0;
			if (store) {
				var state = store.getPlaylist(root) || {};
				var sel = store.getSelection(root) || {};
				var tracks = Array.isArray(state.tracks) ? state.tracks : [];
				if (tracks.length) {
					var trackIndex = isFinite(sel.trackIndex) ? sel.trackIndex : (isFinite(state.currentTrackIndex) ? state.currentTrackIndex : 0);
					if (!isFinite(trackIndex) || trackIndex < 0 || trackIndex >= tracks.length) {
						trackIndex = 0;
					}
					trackForDur = tracks[trackIndex] || null;
				}
				if (isFinite(sel.variantIndex)) {
					variantIndex = sel.variantIndex;
				} else if (isFinite(state.currentVariantIndex)) {
					variantIndex = state.currentVariantIndex;
				}
				if (!isFinite(variantIndex) || variantIndex < 0) {
					variantIndex = 0;
				}
			}
			var live = null;
			if (store && typeof store.getLive === 'function') {
				try {
					live = store.getLive(root) || null;
				} catch (e) {}
			}
			try {
				dur = getDisplayDurationSecForTrack(trackForDur);
				bufEnd = getBufferedEndSec();
			} catch (e) {}
			if (!isDragging) {
				drawCurrentSec = cur;
				drawDurationSec = dur;
				if (dur && isFinite(dur) && dur > 0) {
					var p = cur / dur;
					var pb = bufEnd && isFinite(bufEnd) ? bufEnd / dur : 0;
					if (live && typeof live.bufferFraction === 'number' && isFinite(live.bufferFraction)) {
						pb = live.bufferFraction;
					}
					targetProgress = clamp01(p);
					targetBufferedProgress = clamp01(pb);
				} else {
					targetProgress = 0;
					targetBufferedProgress = 0;
				}
			} else {
				if (dur && isFinite(dur) && dur > 0) {
					var pb2 = bufEnd && isFinite(bufEnd) ? bufEnd / dur : 0;
					if (live && typeof live.bufferFraction === 'number' && isFinite(live.bufferFraction)) {
						pb2 = live.bufferFraction;
					}
					targetBufferedProgress = clamp01(pb2);
				}
			}
			drawProgress += (targetProgress - drawProgress) * 0.18;
			drawBufferedProgress += (targetBufferedProgress - drawBufferedProgress) * 0.18;
			ctx.clearRect(0, 0, size, size);
			ensureTrackAssets();
			var theme = getTheme();
			if (coverImg && coverReady) {
				drawCoverCircle();
			}
			ctx.beginPath();
			ctx.arc(center, center, radius, 0, Math.PI * 2);
			ctx.lineWidth = ringWidth;
			ctx.strokeStyle = theme.ringBase;
			ctx.stroke();
			var wf = currentWaveforms && currentWaveforms.length ? currentWaveforms[variantIndex] || currentWaveforms[0] || [] : [];
			if (wf && wf.length) {
				drawWaveform(drawProgress, wf);
			}
			if (drawBufferedProgress > 0.001) {
				var bufEndAngle = -Math.PI / 2 + drawBufferedProgress * Math.PI * 2;
				ctx.beginPath();
				ctx.arc(center, center, radius, -Math.PI / 2, bufEndAngle);
				ctx.lineWidth = ringWidth + 1;
				ctx.strokeStyle = theme.buffered;
				ctx.lineCap = 'round';
				ctx.stroke();
			}
			var endAngle = -Math.PI / 2 + drawProgress * Math.PI * 2;
			ctx.beginPath();
			ctx.arc(center, center, radius, -Math.PI / 2, endAngle);
			ctx.lineWidth = ringWidth;
			ctx.strokeStyle = theme.progress;
			ctx.lineCap = 'round';
			ctx.stroke();
			var knobX = center + Math.cos(endAngle) * radius;
			var knobY = center + Math.sin(endAngle) * radius;
			ctx.save();
			ctx.beginPath();
			ctx.arc(knobX, knobY, knobRadius, 0, Math.PI * 2);
			ctx.fillStyle = theme.knob;
			ctx.shadowColor = 'rgba(0,0,0,0.4)';
			ctx.shadowBlur = 8;
			ctx.fill();
			ctx.restore();
			drawCenterTime();
			try {
				window.requestAnimationFrame(renderFrame);
			} catch (e) {}
		};
		if (canvas && canvas.addEventListener) {
			canvas.addEventListener('touchstart', function(e){
				var t = e.touches && e.touches[0] ? e.touches[0] : null;
				if (!t) {
					return;
				}
				var pt = clientToCanvasXY(t.clientX, t.clientY);
				if (!pt) {
					return;
				}
				var currentP = clamp01(isDragging ? dragProgress : targetProgress);
				var knobAngle = (-Math.PI / 2) + currentP * Math.PI * 2;
				var knobX = center + Math.cos(knobAngle) * radius;
				var knobY = center + Math.sin(knobAngle) * radius;
				var dx = pt.x - knobX;
				var dy = pt.y - knobY;
				var knobHitRadius = Math.max(knobRadius * 2.6, 50 * pt.scale);
				if ((dx * dx + dy * dy) <= (knobHitRadius * knobHitRadius)) {
					touchPreventScrollActive = true;
					try {
						e.preventDefault();
					} catch (err) {}
				} else {
					touchPreventScrollActive = false;
				}
			}, { passive: false });
			canvas.addEventListener('touchmove', function(e){
				if (touchPreventScrollActive) {
					try {
						e.preventDefault();
					} catch (err) {}
				}
			}, { passive: false });
			canvas.addEventListener('touchend', function(){
				touchPreventScrollActive = false;
			}, { passive: false });
			canvas.addEventListener('touchcancel', function(){
				touchPreventScrollActive = false;
			}, { passive: false });
			canvas.addEventListener('pointerdown', function(e){
				var pt = clientToCanvasXY(e.clientX, e.clientY);
				if (!pt) {
					return;
				}
				var currentP = clamp01(isDragging ? dragProgress : targetProgress);
				var knobAngle = (-Math.PI / 2) + currentP * Math.PI * 2;
				var knobX = center + Math.cos(knobAngle) * radius;
				var knobY = center + Math.sin(knobAngle) * radius;
				var dx = pt.x - knobX;
				var dy = pt.y - knobY;
				var knobHitRadius = Math.max(knobRadius * 2.6, 50 * pt.scale);
				if (e.pointerType !== 'mouse' && (dx * dx + dy * dy) > (knobHitRadius * knobHitRadius)) {
					return;
				}
				var p = e.pointerType === 'mouse' ? progressFromClientXYStrict(e.clientX, e.clientY) : progressFromClientXYLoose(e.clientX, e.clientY);
				if (p === null) {
					return;
				}
				isDragging = true;
				dragProgress = p;
				try {
					canvas.setPointerCapture(e.pointerId);
				} catch (err) {}
				applyPreviewProgress(p);
			});
			canvas.addEventListener('pointermove', function(e){
				if (!isDragging) {
					return;
				}
				var p = progressFromClientXYLoose(e.clientX, e.clientY);
				if (p === null) {
					return;
				}
				dragProgress = p;
				applyPreviewProgress(p);
			});
			var stopScrub = function(e){
				if (!isDragging) {
					return;
				}
				isDragging = false;
				try {
					canvas.releasePointerCapture(e.pointerId);
				} catch (err) {}
				commitSeekFromProgress(dragProgress);
				var nowMs = (typeof performance !== 'undefined' && performance && typeof performance.now === 'function') ? performance.now() : Date.now();
				lastScrubUpTimeMs = nowMs;
				if (api && typeof api.play === 'function') {
					try {
						api.play();
					} catch (err) {}
				}
			};
			canvas.addEventListener('pointerup', stopScrub);
			canvas.addEventListener('pointercancel', stopScrub);
			canvas.addEventListener('lostpointercapture', stopScrub);
			canvas.addEventListener('click', function(e){
				if (isDragging) {
					return;
				}
				var nowMs = (typeof performance !== 'undefined' && performance && typeof performance.now === 'function') ? performance.now() : Date.now();
				if (nowMs - lastScrubUpTimeMs < 350) {
					return;
				}
				var pt = clientToCanvasXY(e.clientX, e.clientY);
				if (!pt) {
					return;
				}
				var currentP = clamp01(targetProgress);
				var knobAngle = (-Math.PI / 2) + currentP * Math.PI * 2;
				var knobX = center + Math.cos(knobAngle) * radius;
				var knobY = center + Math.sin(knobAngle) * radius;
				var dx = pt.x - knobX;
				var dy = pt.y - knobY;
				var knobHitRadius = Math.max(knobRadius * 2.6, 50 * pt.scale);
				if ((dx * dx + dy * dy) <= (knobHitRadius * knobHitRadius)) {
					return;
				}
				var x = pt.x - center;
				var y = pt.y - center;
				var dist = Math.sqrt(x * x + y * y);
				var innerLimit = radius - ringWidth * 1.8;
				var outerLimit = radius + ringWidth * 1.8;
				if (dist >= innerLimit && dist <= outerLimit) {
					var p = progressFromClientXYStrict(e.clientX, e.clientY);
					if (p === null) {
						return;
					}
					commitSeekFromProgress(p);
					try {
						if (audio.ended) {
							audio.currentTime = 0;
						}
					} catch (err) {}
					if ((audio.paused || audio.ended) && api && typeof api.play === 'function') {
						try {
							api.play();
						} catch (err) {}
					}
					return;
				}
				if (dist < innerLimit) {
					var playing = (!audio.paused && !audio.ended);
					if (api && typeof api.toggle === 'function') {
						try {
							api.toggle();
						} catch (err) {}
						return;
					}
					if (playing) {
						try {
							audio.pause();
						} catch (err) {}
						return;
					}
					try {
						if (audio.ended) {
							audio.currentTime = 0;
						}
						audio.play();
					} catch (err) {}
				}
			});
		}
		renderFrame();
	};
	for (var key in UI) {
		if (Object.prototype.hasOwnProperty.call(UI, key)) {
			existing[key] = UI[key];
		}
	}
	window.THESPL_Player_UI = existing;
	var initDefault = function(){
		var base = getPluginBase();
		var uiUrl = base ? base + 'assets/js/player-ui.js' : '';
		var storeUrl = base ? base + 'assets/js/player-store.js' : '';
		var runBindings = function(){
			var roots = document.querySelectorAll('.thespl-player[data-template-slug="default"]');
			if (!roots || !roots.forEach) {
				return;
			}
			roots.forEach(function(root){
				try {
					if (window.THESPL_Player_Store && typeof window.THESPL_Player_Store.init === 'function') {
						window.THESPL_Player_Store.init(root);
					}
				} catch (e) {}
				var attempts = 0;
				var maxAttempts = 30;
				var delay = 100;
				var tryBind = function(){
					var api = null;
					try {
						api = root._thesplPlayerApi || null;
					} catch (e) {}
					try {
						if (window.THESPL_Player_UI && typeof window.THESPL_Player_UI.bindAudioInfo === 'function') {
							window.THESPL_Player_UI.bindAudioInfo(root);
						}
					} catch (e) {}
					try {
						if (window.THESPL_Player_UI) {
							if (typeof window.THESPL_Player_UI.setInfotextMode === 'function') {
								window.THESPL_Player_UI.setInfotextMode(root, 'scroll');
							}
							if (typeof window.THESPL_Player_UI.bindInfotext === 'function') {
								window.THESPL_Player_UI.bindInfotext(root);
							}
						}
					} catch (e) {}
					if (!api) {
						if (attempts >= maxAttempts) {
							return true;
						}
						attempts++;
						try {
							window.setTimeout(tryBind, delay);
						} catch (e) {}
						return false;
					}
					try {
						if (window.THESPL_Player_UI && typeof window.THESPL_Player_UI.bindPlaylist === 'function') {
							window.THESPL_Player_UI.bindPlaylist(root, api);
						}
					} catch (e) {}
					try {
						if (window.THESPL_Player_UI && typeof window.THESPL_Player_UI.bindVariants === 'function') {
							window.THESPL_Player_UI.bindVariants(root, api);
						}
					} catch (e) {}
					try {
						if (window.THESPL_Player_UI && typeof window.THESPL_Player_UI.bindTransport === 'function') {
							window.THESPL_Player_UI.bindTransport(root, api);
						}
					} catch (e) {}
					try {
						initDefaultCanvas(root);
					} catch (e) {}
					return true;
				};
				tryBind();
			});
		};
		var start = function(){
			try {
				runBindings();
			} catch (e) {}
		};
		if (storeUrl) {
			ensureAsset(storeUrl, function(){
				if (uiUrl) {
					ensureAsset(uiUrl, start, start);
				} else {
					start();
				}
			}, start);
		} else {
			start();
		}
	};
	if (document.readyState === 'loading') {
		document.addEventListener('DOMContentLoaded', initDefault);
	} else {
		initDefault();
	}
})();
