/**
 * Let's Code Cookie Banner – frontend logic
 *
 * @package LetsCodeCookieBanner
 *
 * Public API exposed on window:
 *   window.lccbConsent.isGranted(category)  → boolean
 *   window.lccbOpenPanel()                  → opens the preferences panel
 *
 * Consent cookie structure (JSON, 1-year expiry):
 *   { v:1, id:"<uuid>", ts:<unix-ms>, necessary:1, statistics:0, marketing:0, preferences:0 }
 *
 * Script blocking convention:
 *   Wrap third-party scripts in <script type="text/plain" data-lccb-category="statistics">
 *   and they will be activated after consent is granted for that category.
 */

( function () {
	'use strict';

	/* ---- references ------------------------------------------ */
	var cfg         = window.lccbSettings || {};
	var COOKIE_NAME = cfg.cookieName || 'lccb_consent';
	var COOKIE_DAYS = cfg.cookieDays || 365;

	/* ---- DOM elements ---------------------------------------- */
	var banner  = document.getElementById( 'lccb-banner' );
	var overlay = document.getElementById( 'lccb-overlay' );
	var panel   = document.getElementById( 'lccb-panel' );
	var fab     = document.getElementById( 'lccb-fab' );

	if ( ! banner ) {
		return; // Banner not rendered (disabled).
	}

	/* ---- cookie helpers -------------------------------------- */
	function setCookie( value ) {
		var expires     = new Date( Date.now() + COOKIE_DAYS * 864e5 ).toUTCString();
		document.cookie = COOKIE_NAME + '=' + encodeURIComponent( JSON.stringify( value ) )
			+ '; expires=' + expires + '; path=/; SameSite=Lax';
	}

	function getCookie() {
		var match = document.cookie.match( '(?:^|; )' + COOKIE_NAME + '=([^;]*)' );
		if ( ! match ) {
			return null;
		}
		try {
			return JSON.parse( decodeURIComponent( match[1] ) );
		} catch ( e ) {
			return null;
		}
	}

	/* ---- public consent API ---------------------------------- */
	var currentConsent = null;

	window.lccbConsent = {
		isGranted: function ( category ) {
			if ( ! currentConsent ) {
				return false;
			}
			if ( category === 'necessary' ) {
				return true;
			}
			return ! ! currentConsent[ category ];
		},
		get: function () {
			return currentConsent ? Object.assign( {}, currentConsent ) : null;
		}
	};

	function applyConsent( consent ) {
		currentConsent = consent;
		// Fire a custom event so themes/plugins can react (Pro handles script activation).
		var ev = new CustomEvent( 'lccbConsentReady', { detail: consent, bubbles: true } );
		document.dispatchEvent( ev );
	}

	/* ---- UI helpers ------------------------------------------ */
	function showBanner() {
		banner.hidden = false;
		banner.removeAttribute( 'aria-hidden' );
		// Move focus to the first actionable element.
		var firstBtn = banner.querySelector( '.lccb-btn' );
		if ( firstBtn ) {
			firstBtn.focus();
		}
	}

	function hideBanner() {
		banner.hidden = true;
		banner.setAttribute( 'aria-hidden', 'true' );
	}

	function syncToggleClass( chk, isOn ) {
		var toggle = chk.closest ? chk.closest( '.lccb-toggle' ) : chk.parentNode;
		if ( toggle ) {
			toggle.classList.toggle( 'lccb-on', isOn );
		}
	}

	function showPanel() {
		// Show panel first so the browser evaluates :checked on a visible element.
		overlay.hidden = false;
		overlay.removeAttribute( 'aria-hidden' );
		panel.hidden = false;
		panel.removeAttribute( 'aria-hidden' );

		// Use in-memory consent as primary source (avoids any cookie-read issues);
		// fall back to cookie for first open after a page reload.
		var saved = currentConsent || getCookie();
		[ 'statistics', 'marketing', 'preferences' ].forEach(
			function ( cat ) {
				var chk = document.getElementById( 'lccb-chk-' + cat );
				if ( chk ) {
						var isOn    = saved ? ! ! saved[ cat ] : false;
						chk.checked = isOn;
						syncToggleClass( chk, isOn );
				}
			}
		);

		// Focus the first focusable element in the panel.
		var firstFocusable = panel.querySelector( 'button, [href], input, [tabindex]:not([tabindex="-1"])' );
		if ( firstFocusable ) {
			firstFocusable.focus();
		}
	}

	function hidePanel() {
		overlay.hidden = true;
		overlay.setAttribute( 'aria-hidden', 'true' );
		panel.hidden = true;
		panel.setAttribute( 'aria-hidden', 'true' );
	}

	function showFab() {
		if ( fab ) {
			fab.hidden = false;
		}
	}

	/* ---- trap focus inside panel ----------------------------- */
	panel.addEventListener(
		'keydown',
		function ( e ) {
			if ( e.key !== 'Tab' ) {
				return;
			}
			var focusable = Array.prototype.slice.call(
				panel.querySelectorAll( 'button:not([disabled]), [href], input:not([disabled]), [tabindex]:not([tabindex="-1"])' )
			).filter(
				function ( el ) {
					return el.offsetParent !== null; }
			);
			if ( ! focusable.length ) {
				return;
			}
			var first = focusable[0];
			var last  = focusable[ focusable.length - 1 ];
			if ( e.shiftKey ) {
				if ( document.activeElement === first ) {
					e.preventDefault(); last.focus(); }
			} else {
				if ( document.activeElement === last ) {
					e.preventDefault(); first.focus(); }
			}
		}
	);

	/* ---- close via Escape ------------------------------------ */
	document.addEventListener(
		'keydown',
		function ( e ) {
			if ( e.key === 'Escape' ) {
				if ( ! panel.hidden ) {
					hidePanel();
				}
			}
		}
	);

	/* ---- AJAX save ------------------------------------------- */
	function saveConsent( actionType, choices ) {
		if ( ! cfg.ajaxUrl || ! cfg.nonce ) {
			return;
		}
		var fd = new FormData();
		fd.append( 'action', 'lccb_save_consent' );
		fd.append( 'nonce', cfg.nonce );
		fd.append( 'action_type', actionType );
		if ( choices ) {
			[ 'statistics', 'marketing', 'preferences' ].forEach(
				function ( cat ) {
					fd.append( cat, choices[ cat ] ? '1' : '0' );
				}
			);
		}
		// Best-effort; we don't block UX on network failure.
		if ( window.fetch ) {
			fetch( cfg.ajaxUrl, { method: 'POST', body: fd } ).catch( function () {} );
		}
	}

	/* ---- handle consent action ------------------------------- */
	function handleConsent( actionType, choices ) {
		var consent = Object.assign(
			{ v: 1, id: '', ts: Date.now(), necessary: 1 },
			choices
		);
		setCookie( consent );
		applyConsent( consent );
		saveConsent( actionType, choices );
		hideBanner();
		hidePanel();
		showFab();
	}

	/* ---- button handlers ------------------------------------- */
	document.getElementById( 'lccb-accept-all' ).addEventListener(
		'click',
		function () {
			handleConsent( 'accept_all', { statistics: 1, marketing: 1, preferences: 1 } );
		}
	);

	document.getElementById( 'lccb-reject-all' ).addEventListener(
		'click',
		function () {
			handleConsent( 'reject_all', { statistics: 0, marketing: 0, preferences: 0 } );
		}
	);

	document.getElementById( 'lccb-customize' ).addEventListener(
		'click',
		function () {
			showPanel();
		}
	);

	// X button on banner = reject all (GDPR requirement).
	document.getElementById( 'lccb-x-btn' ).addEventListener(
		'click',
		function () {
			handleConsent( 'close', { statistics: 0, marketing: 0, preferences: 0 } );
		}
	);

	// Panel close button.
	document.getElementById( 'lccb-panel-close' ).addEventListener(
		'click',
		function () {
			hidePanel();
		}
	);

	// Overlay click closes panel without saving.
	overlay.addEventListener(
		'click',
		function () {
			hidePanel();
		}
	);

	// Panel: Reject all.
	document.getElementById( 'lccb-pref-reject' ).addEventListener(
		'click',
		function () {
			handleConsent( 'reject_all', { statistics: 0, marketing: 0, preferences: 0 } );
		}
	);

	// Panel: Accept all.
	document.getElementById( 'lccb-pref-accept' ).addEventListener(
		'click',
		function () {
			handleConsent( 'accept_all', { statistics: 1, marketing: 1, preferences: 1 } );
		}
	);

	// Panel: Save custom preferences.
	document.getElementById( 'lccb-pref-save' ).addEventListener(
		'click',
		function () {
			var choices = {
				statistics:  document.getElementById( 'lccb-chk-statistics' ).checked ? 1 : 0,
				marketing:   document.getElementById( 'lccb-chk-marketing' ).checked ? 1 : 0,
				preferences: document.getElementById( 'lccb-chk-preferences' ).checked ? 1 : 0,
			};
			handleConsent( 'custom', choices );
		}
	);

	// FAB / floating button.
	if ( fab ) {
		document.getElementById( 'lccb-fab-btn' ).addEventListener(
			'click',
			function () {
				showPanel();
			}
		);
	}

	/* ---- keep toggle visual class in sync when user clicks ---------- */
	[ 'statistics', 'marketing', 'preferences' ].forEach(
		function ( cat ) {
			var chk = document.getElementById( 'lccb-chk-' + cat );
			if ( chk ) {
					chk.addEventListener(
						'change',
						function () {
							syncToggleClass( chk, chk.checked );
						}
					);
			}
		}
	);

	/* ---- cookie detail toggles inside the panel ----------- */
	document.querySelectorAll( '.lccb-cookie-toggle' ).forEach(
		function ( btn ) {
			btn.addEventListener(
				'click',
				function () {
					var list = btn.nextElementSibling;
					if ( ! list || ! list.classList.contains( 'lccb-cookie-list' ) ) {
						return;
					}
					var expanded = btn.getAttribute( 'aria-expanded' ) === 'true';
					if ( expanded ) {
							list.hidden = true;
							btn.setAttribute( 'aria-expanded', 'false' );
							btn.textContent = btn.getAttribute( 'data-show' );
					} else {
							list.hidden = false;
							btn.setAttribute( 'aria-expanded', 'true' );
							btn.textContent = btn.getAttribute( 'data-hide' );
					}
				}
			);
		}
	);

	/* ---- public re-open helper (used by shortcode) ----------- */
	window.lccbOpenPanel = function () {
		showPanel();
	};

	/* ---- YouTube consent placeholder ------------------------- */
	var ytCfg       = cfg.yt || {};
	var ytWarnText  = ytCfg.warn || 'By pressing Play you consent to marketing, profiling and analytics cookies from third parties. The video is loaded from YouTube.';
	var ytPlayLabel = ytCfg.play || 'Play video';
	var ytObserver  = null;

	function ytGetVideoId( src ) {
		if ( ! src ) {
			return null;
		}
		var m = src.match( /(?:youtube(?:-nocookie)?\.com\/(?:embed\/|v\/|watch\?v=)|youtu\.be\/)([a-zA-Z0-9_-]{11})/ );
		return m ? m[1] : null;
	}

	function ytMarketingGranted() {
		return ! ! ( window.lccbConsent && window.lccbConsent.isGranted( 'marketing' ) );
	}

	// Intercepts iframe src assignment at the prototype level so no YouTube network
	// request fires regardless of HOW or WHEN the iframe is created (Elementor, JS API, etc.).
	function ytInterceptPrototypes() {
		var nativeSetAttr = Element.prototype.setAttribute;
		try {
			var srcDesc = Object.getOwnPropertyDescriptor( HTMLIFrameElement.prototype, 'src' );
			if ( srcDesc && srcDesc.set ) {
				var nativeSrcSet = srcDesc.set;
				Object.defineProperty(
					HTMLIFrameElement.prototype,
					'src',
					{
						configurable: true,
						enumerable:   true,
						get: srcDesc.get,
						set: function ( value ) {
							if ( ytGetVideoId( value ) && ! ytMarketingGranted() ) {
								nativeSetAttr.call( this, 'data-lccb-yt-blocked', value );
								return;
							}
							nativeSrcSet.call( this, value );
						}
					}
				);
			}
		} catch ( e ) {
		}
		try {
			HTMLIFrameElement.prototype.setAttribute = function ( name, value ) {
				if ( name === 'src' && ytGetVideoId( value ) && ! ytMarketingGranted() ) {
					nativeSetAttr.call( this, 'data-lccb-yt-blocked', value );
					return;
				}
				nativeSetAttr.call( this, name, value );
			};
		} catch ( e ) {
		}
	}

	function ytEffectiveSrc( iframe ) {
		var blocked = iframe.getAttribute( 'data-lccb-yt-blocked' ) || '';
		var src     = iframe.getAttribute( 'src' ) || '';
		var lazySrc = iframe.getAttribute( 'data-lazy-src' ) || '';
		var dataSrc = iframe.getAttribute( 'data-src' ) || '';
		if ( ytGetVideoId( blocked ) ) {
			return blocked;
		}
		if ( ytGetVideoId( src ) ) {
			return src;
		}
		if ( ytGetVideoId( lazySrc ) ) {
			return lazySrc;
		}
		if ( ytGetVideoId( dataSrc ) ) {
			return dataSrc;
		}
		return '';
	}

	// Uses Element.prototype.setAttribute directly to bypass our intercept (consent already granted).
	function ytLoadIframe( placeholder, autoplay ) {
		if ( ! placeholder.parentNode ) {
			return;
		}
		var src           = placeholder.getAttribute( 'data-lccb-yt-src' );
		var width         = placeholder.getAttribute( 'data-lccb-yt-width' );
		var height        = placeholder.getAttribute( 'data-lccb-yt-height' );
		var iStyle        = placeholder.getAttribute( 'data-lccb-yt-style' );
		var iClass        = placeholder.getAttribute( 'data-lccb-yt-class' );
		var nativeSetAttr = Element.prototype.setAttribute;

		var iframe   = document.createElement( 'iframe' );
		var finalSrc = autoplay ? src + ( src.indexOf( '?' ) === -1 ? '?' : '&' ) + 'autoplay=1' : src;
		nativeSetAttr.call( iframe, 'src', finalSrc );
		if ( width ) {
			iframe.width = width;
		}
		if ( height ) {
			iframe.height = height;
		}
		nativeSetAttr.call( iframe, 'frameborder', '0' );
		nativeSetAttr.call( iframe, 'allow', 'accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share' );
		nativeSetAttr.call( iframe, 'allowfullscreen', '' );
		if ( iStyle ) {
			iframe.style.cssText = iStyle;
		}
		if ( iClass ) {
			iframe.className = iClass;
		}

		placeholder.parentNode.replaceChild( iframe, placeholder );
	}

	function ytMakePlaceholder( videoId, embedSrc, refEl ) {
		var placeholder       = document.createElement( 'div' );
		placeholder.className = 'lccb-yt-placeholder';
		placeholder.setAttribute( 'data-lccb-yt-src',    embedSrc );
		placeholder.setAttribute( 'data-lccb-yt-width',  refEl && refEl.getAttribute ? ( refEl.getAttribute( 'width' ) || '' ) : '' );
		placeholder.setAttribute( 'data-lccb-yt-height', refEl && refEl.getAttribute ? ( refEl.getAttribute( 'height' ) || '' ) : '' );
		placeholder.setAttribute( 'data-lccb-yt-style',  refEl && refEl.getAttribute ? ( refEl.getAttribute( 'style' ) || '' ) : '' );
		placeholder.setAttribute( 'data-lccb-yt-class',  refEl ? ( refEl.className || '' ) : '' );

		if ( refEl && refEl.getAttribute ) {
			var w = refEl.getAttribute( 'width' );
			if ( w ) {
				placeholder.style.width = ( isNaN( w ) ? w : w + 'px' );
			}
		}

		var img = document.createElement( 'img' );
		img.src = 'https://img.youtube.com/vi/' + videoId + '/hqdefault.jpg';
		img.alt = '';
		img.setAttribute( 'aria-hidden', 'true' );
		img.className = 'lccb-yt-thumb';
		placeholder.appendChild( img );

		var btn       = document.createElement( 'button' );
		btn.type      = 'button';
		btn.className = 'lccb-yt-play-btn';
		btn.setAttribute( 'aria-label', ytPlayLabel );
		btn.innerHTML =
			'<svg viewBox="0 0 68 48" aria-hidden="true" focusable="false">'
			+ '<path class="lccb-yt-play-bg" style="fill:#212121;fill-opacity:0.8" d="M66.52 7.74c-.78-2.93-2.49-5.41-5.42-6.19C55.79.13 34 0 34 0S12.21.13 6.9 1.55c-2.93.78-4.63 3.26-5.42 6.19C.06 13.05 0 24 0 24s.06 10.95 1.48 16.26c.78 2.93 2.49 5.41 5.42 6.19C12.21 47.87 34 48 34 48s21.79-.13 27.1-1.55c2.93-.78 4.64-3.26 5.42-6.19C67.94 34.95 68 24 68 24s-.06-10.95-1.48-16.26z"/>'
			+ '<path class="lccb-yt-play-arrow" style="fill:#fff" d="M 45,24 27,14 27,34"/>'
			+ '</svg>';
		placeholder.appendChild( btn );

		var warn         = document.createElement( 'p' );
		warn.className   = 'lccb-yt-warn';
		warn.textContent = ytWarnText;
		placeholder.appendChild( warn );

		btn.addEventListener(
			'click',
			function () {
				if ( ! ytMarketingGranted() ) {
					// Mark for autoplay: lccbConsentReady will load this placeholder.
					placeholder.setAttribute( 'data-lccb-yt-autoplay', '1' );
					handleConsent( 'accept_all', { statistics: 1, marketing: 1, preferences: 1 } );
				} else {
					ytLoadIframe( placeholder, true );
				}
			}
		);

		return placeholder;
	}

	function ytCreatePlaceholder( iframe, src ) {
		var videoId = ytGetVideoId( src );
		if ( ! videoId ) {
			return null;
		}
		return ytMakePlaceholder( videoId, src, iframe );
	}

	function ytMaybeReplace( iframe ) {
		if ( iframe.getAttribute( 'data-lccb-yt-done' ) ) {
			return;
		}
		if ( ytMarketingGranted() ) {
			return;
		}
		var src = ytEffectiveSrc( iframe );
		if ( ! src ) {
			return;
		}
		Element.prototype.setAttribute.call( iframe, 'data-lccb-yt-done', '1' );
		var ph = ytCreatePlaceholder( iframe, src );
		if ( ph && iframe.parentNode ) {
			iframe.parentNode.replaceChild( ph, iframe );
		}
	}

	// Intercepts Elementor video containers (lazy-load: no iframe in initial HTML).
	function ytHandleElementorContainers() {
		if ( ytMarketingGranted() ) {
			return;
		}
		document.querySelectorAll( '.elementor-video[data-settings], [data-video-url]' ).forEach(
			function ( el ) {
				if ( el.getAttribute( 'data-lccb-yt-done' ) ) {
					return;
				}
				var videoId = ytGetVideoId( el.getAttribute( 'data-video-url' ) || '' );
				if ( ! videoId ) {
					try {
						var s   = JSON.parse( el.getAttribute( 'data-settings' ) || '{}' );
						videoId = ytGetVideoId( s.youtube_url || '' );
					} catch ( e ) {
					}
				}
				if ( ! videoId ) {
					return;
				}
				el.setAttribute( 'data-lccb-yt-done', '1' );
				var embedSrc = 'https://www.youtube.com/embed/' + videoId + '?controls=1&rel=0&playsinline=0';
				var ph       = ytMakePlaceholder( videoId, embedSrc, null );
				if ( el.parentNode ) {
					var parent = el.parentNode;
					parent.replaceChild( ph, el );
					// Hide Elementor's own play-button overlay if it is a sibling of the video container.
					parent.querySelectorAll( '.elementor-custom-embed-play-button' ).forEach(
						function ( b ) {
							b.style.cssText = 'display:none!important';
						}
					);
				}
			}
		);
	}

	function ytScanAll() {
		ytHandleElementorContainers();
		document.querySelectorAll(
			'iframe[src*="youtube.com/embed"], iframe[src*="youtube-nocookie.com/embed"],'
			+ 'iframe[data-lazy-src*="youtube.com/embed"], iframe[data-lazy-src*="youtube-nocookie.com/embed"],'
			+ 'iframe[data-src*="youtube.com/embed"], iframe[data-src*="youtube-nocookie.com/embed"],'
			+ 'iframe[data-lccb-yt-blocked]'
		).forEach( ytMaybeReplace );
	}

	function ytStartObserver() {
		if ( ytObserver || ! window.MutationObserver ) {
			return;
		}
		ytObserver = new MutationObserver(
			function ( mutations ) {
				var needsScan = false;
				mutations.forEach(
					function ( mutation ) {
						if ( mutation.type === 'childList' && mutation.addedNodes.length ) {
								needsScan = true;
						} else if ( mutation.type === 'attributes' && mutation.target.tagName === 'IFRAME' ) {
							ytMaybeReplace( mutation.target );
						}
					}
				);
				if ( needsScan ) {
					ytScanAll();
				}
			}
		);
		ytObserver.observe(
			document.body,
			{
				childList: true,
				subtree: true,
				attributes: true,
				attributeFilter: [ 'src', 'data-lazy-src', 'data-src', 'data-lccb-yt-blocked' ],
			}
		);
	}

	function ytStopObserver() {
		if ( ytObserver ) {
			ytObserver.disconnect();
			ytObserver = null;
		}
	}

	function ytInitPlaceholders() {
		var cookie = getCookie();
		if ( cookie && cookie.marketing ) {
			return;
		}

		ytInterceptPrototypes(); // Must run first — intercepts any future iframe.src assignment.
		ytScanAll();
		ytStartObserver();

		// Fallback scans for deferred initialisation (Elementor, theme builders, etc.).
		[ 300, 1000, 3000 ].forEach(
			function ( delay ) {
				setTimeout(
					function () {
						if ( ytMarketingGranted() ) {
							return;
						}
						ytScanAll();
					},
					delay
				);
			}
		);
	}

	document.addEventListener(
		'lccbConsentReady',
		function ( e ) {
			if ( e.detail && e.detail.marketing ) {
				ytStopObserver();
				// Load placeholders (with autoplay for the one the user clicked).
				document.querySelectorAll( '.lccb-yt-placeholder' ).forEach(
					function ( ph ) {
						ytLoadIframe( ph, ph.getAttribute( 'data-lccb-yt-autoplay' ) === '1' );
					}
				);
				// Restore any iframes whose src was intercepted but not yet replaced.
				document.querySelectorAll( 'iframe[data-lccb-yt-blocked]' ).forEach(
					function ( iframe ) {
						var url = iframe.getAttribute( 'data-lccb-yt-blocked' );
						iframe.removeAttribute( 'data-lccb-yt-blocked' );
						Element.prototype.setAttribute.call( iframe, 'src', url );
					}
				);
			} else {
				// Marketing not granted / revoked — re-intercept and replace any live YouTube iframes.
				ytInterceptPrototypes();
				document.querySelectorAll(
					'iframe[src*="youtube.com/embed"], iframe[src*="youtube-nocookie.com/embed"]'
				).forEach(
					function ( iframe ) {
						iframe.removeAttribute( 'data-lccb-yt-done' );
						ytMaybeReplace( iframe );
					}
				);
				ytStartObserver();
			}
		}
	);

	ytInitPlaceholders();

	/* ---- Vimeo consent placeholder -------------------------- */
	var vmCfg       = cfg.vm || {};
	var vmWarnText  = vmCfg.warn || 'By pressing Play you consent to marketing, profiling and analytics cookies from third parties. The video is loaded from Vimeo.';
	var vmPlayLabel = vmCfg.play || 'Play video';
	var vmObserver  = null;

	function vmGetVideoId( src ) {
		if ( ! src ) {
			return null;
		}
		var m = src.match( /player\.vimeo\.com\/video\/(\d+)/ );
		return m ? m[1] : null;
	}

	function vmHasDnt( src ) {
		return /[?&]dnt=1/.test( src || '' );
	}

	function vmShouldBlock( src ) {
		return ! ! vmGetVideoId( src ) && ! vmHasDnt( src );
	}

	function vmMarketingGranted() {
		return ! ! ( window.lccbConsent && window.lccbConsent.isGranted( 'marketing' ) );
	}

	// Chains on top of the existing YT prototype interceptors (both use configurable:true).
	function vmInterceptPrototypes() {
		var nativeSetAttr = Element.prototype.setAttribute;
		try {
			var srcDesc = Object.getOwnPropertyDescriptor( HTMLIFrameElement.prototype, 'src' );
			if ( srcDesc && srcDesc.set ) {
				var nativeSrcSet = srcDesc.set;
				Object.defineProperty(
					HTMLIFrameElement.prototype,
					'src',
					{
						configurable: true,
						enumerable:   true,
						get: srcDesc.get,
						set: function ( value ) {
							if ( vmShouldBlock( value ) && ! vmMarketingGranted() ) {
								nativeSetAttr.call( this, 'data-lccb-vm-blocked', value );
								return;
							}
							nativeSrcSet.call( this, value );
						}
					}
				);
			}
		} catch ( e ) {
		}
		try {
			var prevSetAttr                          = HTMLIFrameElement.prototype.setAttribute;
			HTMLIFrameElement.prototype.setAttribute = function ( name, value ) {
				if ( name === 'src' && vmShouldBlock( value ) && ! vmMarketingGranted() ) {
					nativeSetAttr.call( this, 'data-lccb-vm-blocked', value );
					return;
				}
				prevSetAttr.call( this, name, value );
			};
		} catch ( e ) {
		}
	}

	function vmEffectiveSrc( iframe ) {
		var blocked = iframe.getAttribute( 'data-lccb-vm-blocked' ) || '';
		var src     = iframe.getAttribute( 'src' ) || '';
		var lazySrc = iframe.getAttribute( 'data-lazy-src' ) || '';
		var dataSrc = iframe.getAttribute( 'data-src' ) || '';
		if ( vmGetVideoId( blocked ) ) {
			return blocked;
		}
		if ( vmShouldBlock( src ) ) {
			return src;
		}
		if ( vmShouldBlock( lazySrc ) ) {
			return lazySrc;
		}
		if ( vmShouldBlock( dataSrc ) ) {
			return dataSrc;
		}
		return '';
	}

	// Uses Element.prototype.setAttribute directly to bypass our intercept (consent already granted).
	function vmLoadIframe( placeholder, autoplay ) {
		if ( ! placeholder.parentNode ) {
			return;
		}
		var src           = placeholder.getAttribute( 'data-lccb-vm-src' );
		var width         = placeholder.getAttribute( 'data-lccb-vm-width' );
		var height        = placeholder.getAttribute( 'data-lccb-vm-height' );
		var iStyle        = placeholder.getAttribute( 'data-lccb-vm-style' );
		var iClass        = placeholder.getAttribute( 'data-lccb-vm-class' );
		var nativeSetAttr = Element.prototype.setAttribute;

		var iframe   = document.createElement( 'iframe' );
		var finalSrc = autoplay ? src + ( src.indexOf( '?' ) === -1 ? '?' : '&' ) + 'autoplay=1' : src;
		nativeSetAttr.call( iframe, 'src', finalSrc );
		if ( width ) {
			iframe.width = width;
		}
		if ( height ) {
			iframe.height = height;
		}
		nativeSetAttr.call( iframe, 'frameborder', '0' );
		nativeSetAttr.call( iframe, 'allow', 'autoplay; fullscreen; picture-in-picture' );
		nativeSetAttr.call( iframe, 'allowfullscreen', '' );
		if ( iStyle ) {
			iframe.style.cssText = iStyle;
		}
		if ( iClass ) {
			iframe.className = iClass;
		}

		placeholder.parentNode.replaceChild( iframe, placeholder );
	}

	function vmMakePlaceholder( videoId, embedSrc, refEl ) {
		var placeholder       = document.createElement( 'div' );
		placeholder.className = 'lccb-vm-placeholder';
		placeholder.setAttribute( 'data-lccb-vm-src',    embedSrc );
		placeholder.setAttribute( 'data-lccb-vm-width',  refEl && refEl.getAttribute ? ( refEl.getAttribute( 'width' ) || '' ) : '' );
		placeholder.setAttribute( 'data-lccb-vm-height', refEl && refEl.getAttribute ? ( refEl.getAttribute( 'height' ) || '' ) : '' );
		placeholder.setAttribute( 'data-lccb-vm-style',  refEl && refEl.getAttribute ? ( refEl.getAttribute( 'style' ) || '' ) : '' );
		placeholder.setAttribute( 'data-lccb-vm-class',  refEl ? ( refEl.className || '' ) : '' );

		if ( refEl && refEl.getAttribute ) {
			var w = refEl.getAttribute( 'width' );
			if ( w ) {
				placeholder.style.width = ( isNaN( w ) ? w : w + 'px' );
			}
		}

		var btn       = document.createElement( 'button' );
		btn.type      = 'button';
		btn.className = 'lccb-vm-play-btn';
		btn.setAttribute( 'aria-label', vmPlayLabel );
		btn.innerHTML =
			'<svg viewBox="0 0 60 60" aria-hidden="true" focusable="false">'
			+ '<circle class="lccb-vm-play-bg" cx="30" cy="30" r="30"/>'
			+ '<polygon class="lccb-vm-play-arrow" points="24,18 46,30 24,42"/>'
			+ '</svg>';
		placeholder.appendChild( btn );

		var warn         = document.createElement( 'p' );
		warn.className   = 'lccb-vm-warn';
		warn.textContent = vmWarnText;
		placeholder.appendChild( warn );

		btn.addEventListener(
			'click',
			function () {
				if ( ! vmMarketingGranted() ) {
					placeholder.setAttribute( 'data-lccb-vm-autoplay', '1' );
					handleConsent( 'accept_all', { statistics: 1, marketing: 1, preferences: 1 } );
				} else {
					vmLoadIframe( placeholder, true );
				}
			}
		);

		return placeholder;
	}

	function vmCreatePlaceholder( iframe, src ) {
		var videoId = vmGetVideoId( src );
		if ( ! videoId ) {
			return null;
		}
		return vmMakePlaceholder( videoId, src, iframe );
	}

	function vmMaybeReplace( iframe ) {
		if ( iframe.getAttribute( 'data-lccb-vm-done' ) ) {
			return;
		}
		if ( vmMarketingGranted() ) {
			return;
		}
		var src = vmEffectiveSrc( iframe );
		if ( ! src ) {
			return;
		}
		Element.prototype.setAttribute.call( iframe, 'data-lccb-vm-done', '1' );
		var ph = vmCreatePlaceholder( iframe, src );
		if ( ph && iframe.parentNode ) {
			iframe.parentNode.replaceChild( ph, iframe );
		}
	}

	function vmScanAll() {
		document.querySelectorAll(
			'iframe[src*="player.vimeo.com/video"],'
			+ 'iframe[data-lazy-src*="player.vimeo.com/video"],'
			+ 'iframe[data-src*="player.vimeo.com/video"],'
			+ 'iframe[data-lccb-vm-blocked]'
		).forEach( vmMaybeReplace );
	}

	function vmStartObserver() {
		if ( vmObserver || ! window.MutationObserver ) {
			return;
		}
		vmObserver = new MutationObserver(
			function ( mutations ) {
				var needsScan = false;
				mutations.forEach(
					function ( mutation ) {
						if ( mutation.type === 'childList' && mutation.addedNodes.length ) {
							needsScan = true;
						} else if ( mutation.type === 'attributes' && mutation.target.tagName === 'IFRAME' ) {
							vmMaybeReplace( mutation.target );
						}
					}
				);
				if ( needsScan ) {
					vmScanAll();
				}
			}
		);
		vmObserver.observe(
			document.body,
			{
				childList: true,
				subtree: true,
				attributes: true,
				attributeFilter: [ 'src', 'data-lazy-src', 'data-src', 'data-lccb-vm-blocked' ],
			}
		);
	}

	function vmStopObserver() {
		if ( vmObserver ) {
			vmObserver.disconnect();
			vmObserver = null;
		}
	}

	function vmInitPlaceholders() {
		var cookie = getCookie();
		if ( cookie && cookie.marketing ) {
			return;
		}

		vmInterceptPrototypes();
		vmScanAll();
		vmStartObserver();

		// Fallback scans for deferred initialisation (page builders, lazy loaders, etc.).
		[ 300, 1000, 3000 ].forEach(
			function ( delay ) {
				setTimeout(
					function () {
						if ( vmMarketingGranted() ) {
							return;
						}
						vmScanAll();
					},
					delay
				);
			}
		);
	}

	document.addEventListener(
		'lccbConsentReady',
		function ( e ) {
			if ( e.detail && e.detail.marketing ) {
				vmStopObserver();
				document.querySelectorAll( '.lccb-vm-placeholder' ).forEach(
					function ( ph ) {
						vmLoadIframe( ph, ph.getAttribute( 'data-lccb-vm-autoplay' ) === '1' );
					}
				);
				document.querySelectorAll( 'iframe[data-lccb-vm-blocked]' ).forEach(
					function ( iframe ) {
						var url = iframe.getAttribute( 'data-lccb-vm-blocked' );
						iframe.removeAttribute( 'data-lccb-vm-blocked' );
						Element.prototype.setAttribute.call( iframe, 'src', url );
					}
				);
			} else {
				// Marketing not granted / revoked — re-intercept and replace any live Vimeo iframes.
				vmInterceptPrototypes();
				document.querySelectorAll(
					'iframe[src*="player.vimeo.com/video"]'
				).forEach(
					function ( iframe ) {
						iframe.removeAttribute( 'data-lccb-vm-done' );
						vmMaybeReplace( iframe );
					}
				);
				vmStartObserver();
			}
		}
	);

	vmInitPlaceholders();

	/* ---- init: check existing consent ----------------------- */
	var saved = getCookie();
	if ( saved && saved.v ) {
		// Consent already given — apply it and show FAB.
		applyConsent( saved );
		showFab();
	} else {
		// No consent yet — show banner after a short delay to avoid CLS.
		setTimeout( showBanner, 200 );
	}

} () );

/* ============================================================
	Admin-triggered scan mode — standalone IIFE
	Runs independently of the banner; does not need banner DOM.
	Activated by ?lccb_scan=1 added by the admin launcher form.
	============================================================ */
( function () {
	'use strict';

	var params = new URLSearchParams( window.location.search );
	if ( params.get( 'lccb_scan' ) !== '1' ) {
		return;
	}

	var cfg = window.lccbSettings || {};

	// Security: scanNonce is only injected by PHP for logged-in admins.
	if ( ! cfg.scanNonce ) {
		document.body.style.cssText = 'margin:0;padding:0;background:#fff';
		document.body.innerHTML     =
			'<p style="font-family:sans-serif;padding:60px 40px;font-size:18px;color:#c00;text-align:center">'
			+ 'Accesso negato: effettua il login come amministratore prima di avviare una scansione.'
			+ '</p>';
		return;
	}

	// Build overlay with inline styles — zero CSS dependency.
	var scanOv = document.createElement( 'div' );
	scanOv.setAttribute( 'id', 'lccb-scan-overlay' );
	scanOv.style.cssText =
		'position:fixed;inset:0;background:rgba(0,0,0,.82);z-index:2147483647;'
		+ 'display:flex;align-items:center;justify-content:center;'
		+ 'font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif';

	var boxStyle =
		'background:#fff;border-radius:14px;padding:36px 44px;max-width:500px;'
		+ 'width:90%;text-align:center;box-shadow:0 8px 40px rgba(0,0,0,.35)';

	scanOv.innerHTML =
		'<div style="' + boxStyle + '">'
		+ '<div style="font-size:18px;font-weight:700;color:#1a1a1a;margin:0 0 8px">'
		+ 'Let\'s Code &mdash; Scansione Cookie'
		+ '</div>'
		+ '<div id="lccb-scan-status" style="font-size:14px;color:#444;margin:0 0 20px;min-height:20px">'
		+ 'Inizializzazione script di terze parti&hellip;'
		+ '</div>'
		+ '<div style="height:8px;background:#e0e0e0;border-radius:4px;overflow:hidden;margin:0 0 12px">'
		+ '<div id="lccb-scan-fill" style="height:100%;background:#1a73e8;border-radius:4px;width:0%;transition:width .4s ease"></div>'
		+ '</div>'
		+ '<div id="lccb-scan-info" style="font-size:12px;color:#888">'
		+ 'Scroll: 0 / &mdash;'
		+ '</div>'
		+ '<div style="font-size:12px;color:#e65c00;margin-top:14px;font-style:italic;">'
		+ 'Attendere il messaggio di completamento prima di chiudere questa finestra per assicurarsi di aver salvato tutti i cookie rilevati.'
		+ '</div>'
		+ '</div>';

	document.body.appendChild( scanOv );

	var elStatus = document.getElementById( 'lccb-scan-status' );
	var elFill   = document.getElementById( 'lccb-scan-fill' );
	var elInfo   = document.getElementById( 'lccb-scan-info' );

	function setStatus( msg ) {
		if ( elStatus ) {
			elStatus.textContent = msg;
		} }
	function setProgress( pct ) {
		if ( elFill ) {
			elFill.style.width = Math.min( 100, pct ) + '%';
		} }
	function setInfo( found, cur, tot ) {
		if ( elInfo ) {
			elInfo.innerHTML = 'Scroll: ' + cur + ' / ' + ( tot !== undefined ? tot : '&mdash;' );
		}
	}

	// Force accept-all consent by writing the cookie directly.
	var forceConsent = params.get( 'lccb_force_consent' ) !== '0';
	if ( forceConsent ) {
		var COOKIE_NAME = cfg.cookieName || 'lccb_consent';
		var COOKIE_DAYS = cfg.cookieDays || 365;
		var expires     = new Date( Date.now() + COOKIE_DAYS * 864e5 ).toUTCString();
		var consent     = { v: 1, id: '', ts: Date.now(), necessary: 1, statistics: 1, marketing: 1, preferences: 1 };
		document.cookie = COOKIE_NAME + '=' + encodeURIComponent( JSON.stringify( consent ) )
			+ '; expires=' + expires + '; path=/; SameSite=Lax';
		// Signal the banner's lccbConsentReady listeners so blocked scripts are activated.
		try {
			document.dispatchEvent( new CustomEvent( 'lccbConsentReady', { detail: consent, bubbles: true } ) );
		} catch ( e ) {
		}
	}

	// Harvest all JS-accessible cookie names.
	var collected = {};
	function harvestCookies() {
		document.cookie.split( ';' ).forEach(
			function ( c ) {
				var name = c.trim().split( '=' )[0].trim();
				if ( name ) {
					collected[ name ] = true;
				}
			}
		);
	}

	function sendAndFinish() {
		var names = Object.keys( collected );
		setStatus( 'Invio risultati al server\u2026' );
		setProgress( 95 );

		function done() {
			setStatus( 'Scansione completata.' );
			setProgress( 100 );
			setInfo( names.length, '\u2014', '\u2014' );
			var box = scanOv.querySelector( 'div' );
			if ( box ) {
				var note           = document.createElement( 'p' );
				note.style.cssText = 'font-size:15px;font-weight:600;color:#2e7d32;margin:16px 0 0';
				note.textContent   = 'Puoi chiudere questa finestra e aggiornare il tab "Cookie Scanner" per vedere i risultati.';
				box.appendChild( note );
			}
		}

		// Collect third-party hostnames and script URLs via Performance API.
		var thirdPartyDomains = [];
		var thirdPartyScripts = [];
		var currentHost       = window.location.hostname;
		var seenDomains       = {};
		var seenScripts       = {};

		// Collect from DOM: <script src="..."> elements already on the page.
		if ( document.querySelectorAll ) {
			document.querySelectorAll( 'script[src]' ).forEach(
				function ( el ) {
					try {
							var parsed = new URL( el.src );
						if ( parsed.hostname && parsed.hostname !== currentHost ) {
							var norm = parsed.origin + parsed.pathname;
							if ( ! seenScripts[ norm ] ) {
									seenScripts[ norm ] = true;
									thirdPartyScripts.push( norm );
							}
							if ( ! seenDomains[ parsed.hostname ] ) {
								seenDomains[ parsed.hostname ] = true;
								thirdPartyDomains.push( parsed.hostname );
							}
						}
					} catch ( e ) {
					}
				}
			);
		}

		// Collect from Performance API: covers dynamically inserted scripts too.
		if ( window.performance && window.performance.getEntriesByType ) {
			window.performance.getEntriesByType( 'resource' ).forEach(
				function ( entry ) {
					try {
							var parsed = new URL( entry.name );
						if ( ! parsed.hostname || parsed.hostname === currentHost ) {
							return; }
						if ( entry.initiatorType === 'script' ) {
							var norm = parsed.origin + parsed.pathname;
							if ( ! seenScripts[ norm ] ) {
								seenScripts[ norm ] = true;
								thirdPartyScripts.push( norm );
							}
						}
						if ( ! seenDomains[ parsed.hostname ] ) {
							seenDomains[ parsed.hostname ] = true;
							thirdPartyDomains.push( parsed.hostname );
						}
					} catch ( e ) {
					}
				}
			);
		}

		// Merge approach A attributions (window._lccbAttribs) with scan results.
		var attribMap = {};
		if ( window._lccbAttribs ) {
			window._lccbAttribs.forEach(
				function ( a ) {
					if ( ! a.s || ! a.c ) {
						return; }
					try {
						var p = new URL( a.s );
						if ( p.hostname === currentHost ) {
							return; }
					} catch ( e ) {
						return; }
					if ( ! attribMap[ a.s ] ) {
						attribMap[ a.s ] = {}; }
					attribMap[ a.s ][ a.c ] = true;
				}
			);
		}

		// Build merged script items: { url, cookies[] }.
		// Scan results come first; approach-A-only scripts are appended.
		var scriptItems = [];
		var seenInItems = {};
		thirdPartyScripts.forEach(
			function ( url ) {
				if ( seenInItems[ url ] ) {
					return; }
				seenInItems[ url ] = true;
				scriptItems.push( { url: url, cookies: Object.keys( attribMap[ url ] || {} ) } );
			}
		);
		Object.keys( attribMap ).forEach(
			function ( url ) {
				if ( seenInItems[ url ] ) {
					return; }
				seenInItems[ url ] = true;
				scriptItems.push( { url: url, cookies: Object.keys( attribMap[ url ] ) } );
			}
		);

		if ( ! cfg.ajaxUrl || ! cfg.scanNonce || ( ! names.length && ! thirdPartyDomains.length && ! scriptItems.length ) ) {
			done();
			return;
		}

		var fd = new FormData();
		fd.append( 'action', 'lccb_scan_cookies' );
		fd.append( 'nonce', cfg.scanNonce );
		names.forEach(
			function ( n ) {
				fd.append( 'cookies[]', n ); }
		);
		thirdPartyDomains.forEach(
			function ( d ) {
				fd.append( 'domains[]', d ); }
		);
		if ( scriptItems.length ) {
			fd.append( 'script_attribs', JSON.stringify( scriptItems ) );
		}

		if ( window.fetch ) {
			fetch( cfg.ajaxUrl, { method: 'POST', body: fd } ).then( done ).catch( done );
		} else {
			done();
		}
	}

	function startScrollLoop() {
		harvestCookies();
		var totalH   = document.documentElement.scrollHeight;
		var stepPx   = window.innerHeight * 0.9;
		var maxSteps = Math.ceil( totalH / stepPx );
		var step     = 0;
		var deadline = Date.now() + 3 * 60 * 1000; // 3-minute safety net

		setStatus( 'Scansione in corso\u2026' );
		setInfo( Object.keys( collected ).length, step, maxSteps );

		function tick() {
			if ( Date.now() > deadline ) {
				setStatus( 'Timeout raggiunto.' );
				sendAndFinish();
				return;
			}

			harvestCookies();
			step++;
			// Recalculate height — lazy content may have extended the page.
			totalH    = document.documentElement.scrollHeight;
			maxSteps  = Math.ceil( totalH / stepPx );
			var found = Object.keys( collected ).length;
			setInfo( found, step, maxSteps );
			setProgress( Math.min( 90, ( step / Math.max( maxSteps, 1 ) ) * 90 ) );

			var atBottom = ( window.scrollY + window.innerHeight + 10 ) >= totalH;
			if ( atBottom ) {
				setTimeout(
					function () {
						harvestCookies();
						sendAndFinish();
					},
					1500
				);
				return;
			}

			window.scrollBy( { top: stepPx, behavior: 'smooth' } );
			setTimeout( tick, 3000 );
		}

		setTimeout( tick, 3000 );
	}

	setProgress( 10 );
	setTimeout( startScrollLoop, 2500 );

} () );
