From cab6ba97fc78aa325dacdea37394d38700ba7a8c Mon Sep 17 00:00:00 2001
From: Jake Vanderwerf <get@jakevanderwerf.ca>
Date: Mon, 05 Jan 2026 18:16:33 +0000
Subject: [PATCH] Merge branch 'main' of https://github.com/jakevdwerf/jvb

---
 inc/forms/TaxonomySelector.php           |   16 
 assets/js/concise/TaxonomyCreator.js     |  370 --
 /dev/null                                | 3141 ---------------------------
 assets/js/min/selector.min.js            |    2 
 assets/js/concise/TaxonomySelector.js    | 1646 ++++++-------
 inc/meta/MetaForm.php                    |    3 
 assets/js/concise/TaxonomySelectorOld.js | 1561 +++++++++++++
 assets/js/min/creator.min.js             |    2 
 8 files changed, 2,434 insertions(+), 4,307 deletions(-)

diff --git a/assets/js/concise/TaxonomyCreator.js b/assets/js/concise/TaxonomyCreator.js
index 2ea387b..b9adaf7 100644
--- a/assets/js/concise/TaxonomyCreator.js
+++ b/assets/js/concise/TaxonomyCreator.js
@@ -1,25 +1,32 @@
 /**
  * TaxonomyCreator - Handles term creation for TaxonomySelector
- * Simplified to focus only on creation logic
  */
 class TaxonomyCreator {
 
 	constructor(selector) {
 		this.selector = selector;
+		this.queue = window.jvbQueue;
 
-		// Only initialize modal elements if modal exists
-		if (selector.modal) {
-			this.createNew = selector.modal.querySelector('.create-new-term');
-			this.toggle = selector.modal.querySelector('.new-term-toggle');
-			this.form = this.createNew?.querySelector('.create-new-term-section');
-		}
-
+		this.initElements();
 		this.initListeners();
+	}
 
-		// Only init term creation UI if we have modal elements
-		if (this.form) {
-			this.initTermCreation();
-		}
+	initElements() {
+		this.selectors = {
+			details: 'details.create-new',
+			parent: '#select_parent',
+			summary: '.create-new summary',
+			suggestion: '.term-suggestions',
+			name: '#term_name',
+			button: '.submit-term',
+			form: 'form.create-term',
+			label: {
+				name: '[for="term_name"]',
+				parent: '[for="select_parent"]'
+			},
+			loading: '.loading-message.create-term'
+		};
+		this.ui = window.uiFromSelectors(this.selectors, this.selector.container);
 	}
 
 	/**
@@ -28,6 +35,13 @@
 	initListeners() {
 		this.clickHandler = this.handleClick.bind(this);
 		document.addEventListener('click', this.clickHandler);
+
+		if (this.ui.form) {
+			this.ui.form.addEventListener('change', (e) => {
+				e.preventDefault();
+				e.stopPropagation();
+			})
+		}
 	}
 
 	/**
@@ -35,55 +49,58 @@
 	 */
 	handleClick(e) {
 		// Handle opening create term form
-		if (window.targetCheck(e, '.create-new-term summary')) {
-			if (this.createNew.open) {
-				this.createNew.querySelector('input[name="term_name"]').focus();
+		if (window.targetCheck(e, this.selectors.summary)) {
+			if (this.ui.details.open) {
+				this.ui.name?.focus();
 			}
 			this.resetParentOptions();
 		}
 
 		// Handle term creation submission
-		if (window.targetCheck(e, '.submit-term')) {
+		if (window.targetCheck(e, this.selectors.button)) {
 			this.handleTermCreation(e).then(() => {});
 		}
-
-		// Handle autocomplete create button
-		if (window.targetCheck(e, '.create-term')) {
-			this.handleAutocompleteCreate(e).then(() => {});
-		}
 	}
 
 	/**
 	 * Handle term creation from modal form
 	 */
 	async handleTermCreation(e) {
-		const taxonomy = this.selector.currentConfig?.taxonomy;
+		let field = this.selector.currentField();
+		const taxonomy = field.taxonomy;
 		if (!taxonomy) return;
 
-		const termName = this.form.querySelector('input[name="term_name"]').value.trim();
-		const parentId = parseInt(this.form.querySelector('input#select_parent')?.value) || 0;
-
-		if (!termName) return;
-
-		const submitButton = this.form.querySelector('button');
+		let data = {
+			parent: 0,
+			taxonomy: taxonomy
+		};
+		// If it's open, we can get the parent element and everything
+		if (this.selector.container.open) {
+			data.name = this.ui.name.value.trim();
+			data.parent = parseInt(this.ui.parent?.value??0);
+		} else if (this.selector.activeField) {
+			//It's autocomplete, so just the term name
+			data.name = this.selector.store.query;
+		}
+		if (!data.name || data.name.length < 2) return;
 
 		try {
-			if (submitButton) {
-				submitButton.disabled = true;
+			if (this.ui.button) {
+				this.ui.button.disabled = true;
 			}
 
-			const response = await this.createTerm(termName, parentId, taxonomy);
+			const response = await this.createTerm(data);
 
 			if (response.success && response.term) {
-				await this.handleSuccessfulCreation(response.term, taxonomy, parentId);
+				await this.handleSuccessfulCreation(response.term, data);
 				this.clearForm();
 			}
 		} catch (error) {
 			console.error('Error creating term:', error);
 			this.selector.handleError(error, 'handleTermCreation');
 		} finally {
-			if (submitButton) {
-				submitButton.disabled = false;
+			if (this.ui.button) {
+				this.ui.button.disabled = false;
 			}
 		}
 	}
@@ -91,11 +108,10 @@
 	/**
 	 * Handle successful term creation
 	 */
-	async handleSuccessfulCreation(term, taxonomy, parentId) {
+	async handleSuccessfulCreation(term, data) {
 		const termPath = term.path || term.name;
-
 		// Close create form
-		this.createNew.open = false;
+		this.ui.details.open = false;
 
 		// Clear cache to ensure fresh data
 		await this.selector.store.clearCache();
@@ -105,161 +121,65 @@
 			id: term.id,
 			name: term.name,
 			path: termPath,
-			taxonomy: taxonomy,
-			parent: parentId,
-			count: 0,
-			hasChildren: false,
-			slug: term.slug || termName.toLowerCase().replace(/\s+/g, '-')
-		});
-
-		// Add to modal selection
-		this.selector.addSelectedTermToModal(term.id, term.name, termPath);
-
-		// Refresh current view if we're viewing the same parent
-		const currentParent = this.selector.store.filters.parent || 0;
-		if (currentParent === parentId) {
-			await this.selector.store.setFilters({
-				taxonomy,
-				parent: parentId,
-				page: 1,
-				search: ''
-			});
-		}
-	}
-
-	/**
-	 * Handle autocomplete create button
-	 */
-	async handleAutocompleteCreate(e) {
-		const button = e.target.closest('.create-term');
-		const fieldId = this.selector.getFieldId(button);
-		const field = this.selector.fields.get(fieldId);
-
-		if (!field) return;
-
-		const input = field.container.querySelector('input[data-autocomplete]');
-		const termName = input?.value.trim() || button.dataset.query;
-
-		if (!termName) return;
-
-		const originalHTML = button.innerHTML;
-
-		try {
-			button.disabled = true;
-			button.textContent = 'Creating...';
-
-			const response = await this.createTerm(termName, 0, field.taxonomy);
-
-			if (response.success && response.term) {
-				await this.handleAutocompleteSuccess(response.term, field, input);
-			} else if (response.reason === 'exists' && response.term) {
-				this.handleExistingTerm(response.term, field, input);
-			}
-		} catch (error) {
-			console.error('Error creating term:', error);
-			this.selector.handleError(error, 'handleAutocompleteCreate');
-		} finally {
-			button.innerHTML = originalHTML;
-			button.disabled = false;
-		}
-	}
-
-	/**
-	 * Handle successful autocomplete creation
-	 */
-	async handleAutocompleteSuccess(term, field, input) {
-		const termPath = term.path || term.name;
-
-		// Add to field
-		field.selectedTerms.add(parseInt(term.id));
-
-		// Add to DataStore
-		this.selector.store.data.set(term.id, {
-			id: term.id,
-			name: term.name,
-			path: termPath,
-			taxonomy: field.taxonomy,
-			parent: 0,
+			taxonomy: data.taxonomy,
+			parent: data.parent,
 			count: 0,
 			hasChildren: false,
 			slug: term.slug || term.name.toLowerCase().replace(/\s+/g, '-')
 		});
 
-		// Update display
-		this.selector.addTermDisplay(term.id, term.name, termPath, 'field', field.id);
+		// Add to modal selection
+		this.selector.addSelected(term.id,this.selector.activeField);
 
-		// Update input and trigger change
-		field.input.value = Array.from(field.selectedTerms).join(',');
-		field.input.dispatchEvent(new Event('change', { bubbles: true }));
-
-		// Clear and hide dropdown
-		field.autocompleteDropdown.hidden = true;
-		if (input) input.value = '';
-
-		// Clear cache for this taxonomy
-		await this.selector.store.clearCache();
-	}
-
-	/**
-	 * Handle selecting existing term from autocomplete
-	 */
-	handleExistingTerm(term, field, input) {
-		field.selectedTerms.add(parseInt(term.id));
-		this.selector.addTermDisplay(term.id, term.name, term.path || term.name, 'field', field.id);
-
-		field.input.value = Array.from(field.selectedTerms).join(',');
-		field.input.dispatchEvent(new Event('change', { bubbles: true }));
-
-		field.autocompleteDropdown.hidden = true;
-		if (input) input.value = '';
-	}
-
-	/**
-	 * Initialize term creation form
-	 */
-	initTermCreation() {
-		if (!this.form) return;
-
-		this.form.addEventListener('change', (e) => {
-			e.preventDefault();
-			e.stopPropagation();
-		});
+		// Refresh current view if we're viewing the same parent
+		if (this.selector.container.open) {
+			const currentParent = this.selector.store.filters.parent || 0;
+			if (currentParent === data.parent) {
+				await this.selector.store.setFilters({
+					taxonomy: data.taxonomy,
+					parent: data.parent,
+					page: 1,
+					search: ''
+				});
+			}
+		}
 	}
 
 	/**
 	 * Reset parent options in create form
 	 */
 	resetParentOptions() {
-		const taxonomy = this.selector.currentConfig?.taxonomy;
+		const field = this.selector.currentField();
+		if (!field) return;
+		const taxonomy = field.taxonomy;
 		if (!taxonomy) return;
 
-		let select = this.createNew.querySelector('#select_parent');
-		if (!select) return;
+		if (!this.ui.parent) return;
 
-		let defaultOption = select.querySelector('option');
+		let defaultOption = this.ui.parent.querySelector('option');
 		if (!defaultOption) return;
 
 		// Clear existing options
-		window.removeChildren(select);
-		select.append(defaultOption.cloneNode(true));
+		window.removeChildren(this.ui.parent);
+		this.ui.parent.append(defaultOption.cloneNode(true));
 
 		// Get current parent from store filters
 		const currentParent = this.selector.store.filters.parent || 0;
 
 		// If we're in a sub-category, add the current parent as an option
 		if (currentParent !== 0) {
-			const parentTerm = this.selector.store.data.get(currentParent);
+			const parentTerm = this.selector.store.get(currentParent);
 			if (parentTerm) {
 				let parentOption = defaultOption.cloneNode(true);
 				parentOption.value = parentTerm.id;
 				parentOption.textContent = parentTerm.name;
-				select.append(parentOption);
+				this.ui.parent.append(parentOption);
 			}
 		}
 
 		// Add all terms currently visible in the taxonomy
 		const visibleTerms = [];
-		this.selector.store.data.forEach(term => {
+		this.selector.store.getFiltered().forEach(term => {
 			if (term.taxonomy === taxonomy && term.parent === currentParent) {
 				visibleTerms.push(term);
 			}
@@ -274,53 +194,24 @@
 			option.id = `select-parent-${term.id}`;
 			option.value = term.id;
 			option.textContent = '  — ' + term.name;
-			select.append(option);
+			this.ui.parent.append(option);
 		});
 	}
 
 	/**
 	 * Create a new term
 	 */
-	async createTerm(name, parent = 0, taxonomy) {
+	async createTerm(data) {
+		if (!data.name || data.parent === undefined || !data.taxonomy) return;
 		try {
-			// Search to check for duplicates
-			await this.selector.store.setFilters({
-				taxonomy: taxonomy,
-				search: name,
-				page: 1,
-				parent: 0
-			});
 
-			// Wait for data to load
-			await new Promise(resolve => setTimeout(resolve, 100));
-
-			// Check if exact match exists
-			const exactMatch = Array.from(this.selector.store.data.values())
-				.find(term =>
-					term.taxonomy === taxonomy &&
-					term.name.toLowerCase() === name.toLowerCase()
-				);
-
-			if (exactMatch) {
-				// For modal context, show suggestions
-				if (this.createNew) {
-					this.showTermSuggestions([exactMatch], true);
-				}
-				return { success: false, reason: 'exists', term: exactMatch };
-			}
-
-			// Term doesn't exist, create it
 			const response = await fetch(`${jvbSettings.api}terms`, {
 				method: 'POST',
 				headers: {
 					'Content-Type': 'application/json',
 					'X-WP-Nonce': window.auth.getNonce()
 				},
-				body: JSON.stringify({
-					taxonomy: taxonomy,
-					name: name,
-					parent: parent
-				})
+				body: JSON.stringify(data)
 			});
 
 			if (!response.ok) {
@@ -336,91 +227,11 @@
 	}
 
 	/**
-	 * Show term suggestions when similar terms exist
-	 */
-	showTermSuggestions(suggestions, isExact = false) {
-		const suggestionContainer = this.createNew.querySelector('.term-suggestions') ||
-			this.createSuggestionContainer();
-
-		// Clear existing suggestions
-		window.removeChildren(suggestionContainer);
-
-		// Add heading
-		const heading = document.createElement('h4');
-		heading.textContent = isExact ?
-			'This term already exists:' :
-			'Similar terms already exist:';
-		suggestionContainer.appendChild(heading);
-
-		// Create list of suggestions
-		const list = document.createElement('ul');
-		list.className = 'term-suggestion-list';
-
-		suggestions.forEach(term => {
-			const item = document.createElement('li');
-			const button = this.createSuggestionButton(term);
-			item.appendChild(button);
-			list.appendChild(item);
-		});
-
-		suggestionContainer.appendChild(list);
-		suggestionContainer.hidden = false;
-	}
-
-	/**
-	 * Create suggestion button
-	 */
-	createSuggestionButton(term) {
-		const button = document.createElement('button');
-		button.type = 'button';
-		button.className = 'use-existing-term';
-		button.dataset.id = term.id;
-		button.textContent = term.path || term.name;
-
-		button.addEventListener('click', () => {
-			// Add this term to modal selection
-			this.selector.addSelectedTermToModal(term.id, term.name, term.path || term.name);
-
-			// Close the create new section
-			this.createNew.open = false;
-
-			// Clear suggestions and form
-			const suggestionContainer = this.createNew.querySelector('.term-suggestions');
-			if (suggestionContainer) {
-				suggestionContainer.hidden = true;
-			}
-
-			this.clearForm();
-		});
-
-		return button;
-	}
-
-	/**
-	 * Create container for term suggestions
-	 */
-	createSuggestionContainer() {
-		const container = document.createElement('div');
-		container.className = 'term-suggestions';
-		container.hidden = true;
-
-		// Insert after the form
-		this.createNew.querySelector('form').after(container);
-		return container;
-	}
-
-	/**
 	 * Clear the creation form
 	 */
 	clearForm() {
-		const nameInput = this.form.querySelector('input[name="term_name"]');
-		if (nameInput) {
-			nameInput.value = '';
-		}
-
-		const suggestionContainer = this.createNew.querySelector('.term-suggestions');
-		if (suggestionContainer) {
-			suggestionContainer.hidden = true;
+		if (this.ui.name) {
+			this.ui.name.value = '';
 		}
 	}
 
@@ -434,15 +245,8 @@
 		}
 
 		// Clear any pending operations
-		const loadingMessage = this.createNew?.querySelector('.loading-message.create-term');
-		if (loadingMessage) {
-			loadingMessage.hidden = true;
-		}
-
-		// Clear suggestions
-		const suggestionContainer = this.createNew?.querySelector('.term-suggestions');
-		if (suggestionContainer) {
-			suggestionContainer.hidden = true;
+		if (this.ui.loading) {
+			this.ui.loading.hidden = true;
 		}
 	}
 }
diff --git a/assets/js/concise/TaxonomySelector.js b/assets/js/concise/TaxonomySelector.js
index b411d11..3cdaf4d 100644
--- a/assets/js/concise/TaxonomySelector.js
+++ b/assets/js/concise/TaxonomySelector.js
@@ -1,501 +1,253 @@
-/**
- * TaxonomySelector - Streamlined version
- * Manages taxonomy selection fields with DataStore integration
- */
 class TaxonomySelector {
 	constructor() {
+		this.container = document.querySelector('dialog#jvb-selector');
+		if (!this.container) return;
+
 		this.a11y = window.jvbA11y;
 		this.error = window.jvbError;
-		this.index = -1;
 
-		this.isInitializing = true;
-		this.taxonomiesToFetch = new Set();
 		this.subscribers = new Set();
-
-		// Register DataStore
-		const store = window.jvbStore.register('taxonomies', {
-			storeName: 'terms',
-			keyPath: 'id',
-			showLoading: false,
-			indexes: [
-				{name: 'taxonomy', keyPath: 'taxonomy'},
-				{name: 'parent', keyPath: 'parent'},
-				{name: 'slug', keyPath: 'slug', unique: true},
-				{name: 'count', keyPath: 'count'},
-			],
-			endpoint: 'terms',
-			TTL: 2 * 60 * 1000,
-			filters: {
-				taxonomy: '',
-				page: 1,
-				search: '',
-				parent: 0
-			},
-			required: 'taxonomy',
-			delayFetch: true,
-		});
-		this.store = store.terms;
-
-		// Field management
 		this.fields = new Map();
-		this.selectedTerms = new Map(); // Current modal selection
+		this.selectedTerms = new Map();  // a map of fieldId => Set of selected term Ids
+		this.loadedTaxonomies = new Set(); // a set of taxonomies, to know whether we should preload a newly registered field
+		this.batchFetch = new Set();
 
-		// Modal context
 		this.activeField = null;
-		this.currentConfig = null;
-		this.disabled = false;
-
-		// Search contexts
-		this.searchContexts = new Map();
-
+		this.isInitializing = true;
 		this.init();
 	}
 
 	init() {
+		this.initStore();
+		this.initElements();
 		this.initModal();
 		this.scanExistingFields();
-		this.initGlobalListeners();
+		this.initListeners();
 
-		// Initialize creator if needed
 		if (this.needsCreator() && window.jvbTaxCreator) {
 			this.creator = new window.jvbTaxCreator(this);
 		}
+		this.isInitializing = false
+		this.batchFetchTaxonomies().then(()=> {});
+	}
+
+	initStore() {
+		const store = window.jvbStore.register(
+			'taxonomies',
+			{
+				storeName: 'terms',
+				keyPath: 'id',
+				showLoading: false,
+				indexes: [
+					{name: 'taxonomy', keyPath: 'taxonomy'},
+					{name: 'parent', keyPath: 'parent'},
+					{name: 'slug', keyPath: 'slug', unique: true},
+					{name: 'count', keyPath: 'count'},
+				],
+				endpoint: 'terms',
+				TTL: 2 * 60 * 1000,
+				filters: {
+					taxonomy: '',
+					page: 1,
+					search: '',
+					parent: 0
+				},
+				required: 'taxonomy',
+				delayFetch: true,
+			}
+		);
+		this.store = store.terms;
 
 		this.store.subscribe(this.handleStoreEvent.bind(this));
-
-		this.isInitializing = false;
-		this.batchFetchTaxonomies();
 	}
 
-	needsCreator() {
-		return Array.from(this.fields.values()).some(field =>
-			field.canCreate || field.hasAutocomplete
-		);
-	}
-
-	/***********************************************************************
-	 * DATASTORE EVENT HANDLING
-	 ***********************************************************************/
-
-	handleStoreEvent(event, data) {
-		const handlers = {
-			'data-loaded': () => this.handleDataLoaded(data),
-			'filters-changed': () => this.handleFiltersChanged(data),
-			'fetch-error': () => this.handleFetchError(data.error),
-		};
-
-		handlers[event]?.();
-	}
-
-	handleDataLoaded(data) {
-		const taxonomy = this.store.filters.taxonomy;
-
-		// Update field states for affected taxonomies
-		if (taxonomy) {
-			const taxonomies = taxonomy.includes(',')
-				? taxonomy.split(',').map(t => t.trim())
-				: [taxonomy];
-
-			taxonomies.forEach(tax => this.updateFieldsForTaxonomy(tax));
-		}
-
-		// Initialize displays on first load
-		if (this.isInitializing) {
-			this.fields.forEach((config, fieldId) => {
-				if (config.selectedTerms.size > 0) {
-					this.initFieldDisplay(fieldId);
-				}
-			});
-		}
-
-		// Render based on context
-		this.renderSearchResults(data);
-	}
-
-	renderSearchResults(data) {
-		const context = this.getActiveSearchContext();
-
-		if (context === 'modal') {
-			this.renderModalResults(data);
-		} else if (context === 'autocomplete') {
-			this.renderAutocompleteResults(data);
-		}
-	}
-
-	getActiveSearchContext() {
-		if (this.modal?.open) return 'modal';
-		if (this.activeField && this.searchContexts.has(this.activeField)) {
-			return this.searchContexts.get(this.activeField);
-		}
-		return null;
-	}
-
-	renderModalResults(data) {
-		this.hideLoading();
-		const terms = this.store.getFiltered();
-		const response = this.store.lastResponse?.page || {};
-		const isSearch = data.filters?.search?.length > 0;
-		const append = response.page > 1;
-
-		this.notify('terms-loaded', { terms, filters: data.filters });
-
-		if (terms.length === 0) {
-			if (!append) {
-				this.showEmptyState(isSearch ? 'No results found.' : 'No items available.');
-			}
-			this.observer.unobserve(this.ui.sentinel);
-		} else {
-			this.renderTerms(terms, append, isSearch);
-
-			if (response.has_more) {
-				this.observer.observe(this.ui.sentinel);
-			} else {
-				this.observer.unobserve(this.ui.sentinel);
-			}
-		}
-
-		this.a11y?.announce(terms.length, append);
-	}
-
-	renderAutocompleteResults(data) {
-		const field = this.fields.get(this.activeField);
-		if (!field?.autocompleteDropdown) return;
-
-		const terms = this.store.getFiltered();
-		const query = data.filters?.search || '';
-
-		this.showAutocompleteResults(field, terms, query);
-		this.searchContexts.delete(this.activeField);
-	}
-
-	handleFiltersChanged(data) {
-		if (this.modal?.open) {
-			this.showLoading();
-		}
-	}
-
-	handleFetchError(error) {
-		this.hideLoading();
-
-		const context = this.getActiveSearchContext();
-
-		if (context === 'autocomplete') {
-			this.showAutocompleteError(this.activeField);
-			this.searchContexts.delete(this.activeField);
-		} else {
-			this.handleError(error, 'fetch');
-		}
-	}
-
-	/***********************************************************************
-	 * FIELD MANAGEMENT
-	 ***********************************************************************/
-
-	updateFieldsForTaxonomy(taxonomy) {
-		this.getFieldsForTaxonomy(taxonomy).forEach(field => {
-			this.updateFieldButtonState(field.id);
-		});
-	}
-
-	updateFieldButtonState(fieldId) {
-		const field = this.fields.get(fieldId);
-		if (!field) return;
-
-		const hasTerms = Array.from(this.store.data.values())
-			.some(term => term.taxonomy === field.taxonomy);
-
-		if (field.toggle) {
-			field.toggle.disabled = !hasTerms && !field.canCreate;
-			field.toggle.title = !hasTerms
-				? `No ${this.getLabel(field.taxonomy, 'single')} available`
-				: `Select ${this.getLabel(field.taxonomy, 'plural')}`;
-		}
-	}
-
-	getFieldsForTaxonomy(taxonomy) {
-		return Array.from(this.fields.values())
-			.filter(field => field.taxonomy === taxonomy);
-	}
-
-	scanExistingFields(container = document.body) {
-		container.querySelectorAll('.field.taxonomy, .field.post').forEach(selector => {
-			try {
-				this.registerField(selector);
-			} catch (error) {
-				this.handleError(error, 'scanExistingFields', selector.dataset.name);
-			}
-		});
-	}
-
-	registerField(field) {
-		const input = field.querySelector('input[type=hidden]');
-		if (!input) return false;
-
-		const fieldId = this.createFieldId(field);
-		field.dataset.fieldId = fieldId;
-
-		const button = field.querySelector('button.taxonomy-toggle');
-		const config = {
-			id: fieldId,
-			input: input,
-			container: field,
-			taxonomy: button.dataset.taxonomy,
-			name: field.dataset.field,
-			maxSelection: parseInt(button.dataset.max) || 0,
-			canSearch: 'search' in button.dataset,
-			hasAutocomplete: 'autocomplete' in button.dataset,
-			autocompleteDropdown: field.querySelector('.autocomplete-dropdown') || null,
-			canCreate: 'creatable' in button.dataset,
-			isRequired: 'required' in button.dataset,
-			selectedTerms: new Set(),
-			toggle: button,
-			selectedContainer: field.querySelector('.selected-items'),
-		};
-
-		// Parse initial values
-		const value = input.value.trim();
-		if (value) {
-			value.split(',')
-				.map(id => parseInt(id.trim()))
-				.filter(id => !isNaN(id))
-				.forEach(id => config.selectedTerms.add(id));
-		}
-
-		this.fields.set(fieldId, config);
-
-		// Queue for batch fetch
-		if (this.isInitializing) {
-			this.taxonomiesToFetch.add(config.taxonomy);
-		}
-
-		// Initialize display
-		if (config.selectedTerms.size > 0) {
-			this.initFieldDisplay(fieldId);
-		}
-
-		return fieldId;
-	}
-
-	createFieldId(field) {
-		this.index++;
-		return 'selector-' + this.index;
-	}
-
-	async initFieldDisplay(fieldId) {
-		const field = this.fields.get(fieldId);
-		if (!field || field.selectedTerms.size === 0) return;
-
-		Array.from(field.selectedTerms).forEach(termId => {
-			const term = this.store.get(termId);
-			if (term) {
-				this.addTermDisplay(termId, term.name, term.path, 'field', fieldId);
-			}
-		});
-	}
-
-	/***********************************************************************
-	 * MODAL INITIALIZATION
-	 ***********************************************************************/
-
-	initModal() {
-		this.modal = document.querySelector('dialog#jvb-selector');
-		if (!this.modal) {
-			console.warn('Taxonomy selector modal not found');
-			return;
-		}
-
-		this.initModalElements();
-
-		this.modalInstance = new window.jvbModal(this.modal, {
-			handleForm: false
-		});
-
-		this.modalInstance.subscribe((event) => {
-			if (event === 'modal-open') this.openModal();
-			if (event === 'modal-close') this.closeModal();
-		});
-	}
-
-	initModalElements() {
-		const selectors = {
+	/******************************************************************
+	 ELEMENTS
+	 ******************************************************************/
+	initElements() {
+		this.selectors = {
 			search: {
 				input: '[type=search]',
-				container: '.search-wrapper'
+				clear: '.clear-search',
+				container: '.search-wrapper',
+				results: '.search-results'
 			},
-			termsList: '.items-container',
-			termsWrap: '.items-wrap',
-			breadcrumbs: {
+			terms: {
+				list: '.items-container',
+				wrap: '.items-wrap',
+				sentinel: '.scroll-sentinel',
+			},
+			nav: {
 				nav: 'nav.term-navigation',
 				back: '.back-to-parent',
+				child: '.toggle-children',
+				pathLevel: '.path-level',
 			},
 			loading: {
 				loading: '.loading',
-				text: '.loading span'
+				text: '.loading span',
 			},
-			selectedTerms: '.selected-items',
-			sentinel: '.scroll-sentinel',
+			selected: '.selected-items',
 			modal: {
 				title: '#modal-title',
+				content: '.modal-content',
+				count: '.selection-count'
 			},
-			create: {
-				details: '.create-new-term',
-				summary: '.create-new-term summary',
-				label: {
-					name: '[for=term_name]',
-					parent: '[for=select_parent]'
-				}
+			favourites: '.favourite-terms',
+			field: {
+				toggle: 'button.taxonomy-toggle',
+				value: 'input[type="hidden"]',
+				selected: '.selected-items',
+				dropdown: '.search-results',
+				search: '[data-autocomplete]',
 			}
-		};
+		}
 
-		this.ui = window.uiFromSelectors(selectors);
+		this.ui = window.uiFromSelectors(this.selectors);
+	}
 
-		// Initialize infinite scroll observer
+	initListeners() {
 		this.observer = new IntersectionObserver((entries) => {
 			entries.forEach(entry => {
 				if (entry.isIntersecting) {
-					this.loadMoreTerms();
+					this.nextPage();
 				}
 			});
 		}, {
-			root: this.ui.termsWrap,
+			root: this.ui.terms.sentinel,
 			threshold: 0.5
 		});
-	}
 
-	/***********************************************************************
-	 * GLOBAL EVENT LISTENERS
-	 ***********************************************************************/
+		this.clickHandler = this.handleClick.bind(this);
+		this.changeHandler = this.handleChange.bind(this);
+		this.inputHandler = this.handleInput.bind(this);
+		this.focusHandler = this.handleFocus.bind(this);
+		this.blurHandler = this.handleBlur.bind(this);
 
-	initGlobalListeners() {
-		document.addEventListener('click', this.handleClick.bind(this));
-		document.addEventListener('change', this.handleChange.bind(this));
-		document.addEventListener('input', this.handleInput.bind(this));
-		document.addEventListener('focus', this.handleFocus.bind(this), true);
-		document.addEventListener('blur', this.handleBlur.bind(this), true);
+		document.addEventListener('click', this.clickHandler);
+		document.addEventListener('change', this.changeHandler);
+		document.addEventListener('input', this.inputHandler);
+		document.addEventListener('focus', this.focusHandler, true);
+		document.addEventListener('blur', this.blurHandler, true);
 	}
 
 	handleClick(e) {
-		// Toggle button
-		if (window.targetCheck(e, '.taxonomy-toggle')) {
-			e.preventDefault();
-			const fieldId = this.getFieldId(e.target);
-			const field = this.fields.get(fieldId);
-			if (field) this.setActiveField(fieldId, true);
-			return;
-		}
-
-		// Remove selected term
-		const removeButton = window.targetCheck(e, 'button.remove-item');
-		if (removeButton && e.target.closest('.jvb-selector')) {
-			const fieldId = this.getFieldId(removeButton);
-			const termId = removeButton.closest('.selected-item').dataset.id;
-			this.removeSelectedTerm(fieldId, termId);
-			return;
-		}
-
-		// Modal close
-		if (e.target.matches('.modal-close')) {
-			this.modalInstance?.handleClose();
-			return;
-		}
-
-		// Modal clicks
-		if (this.modal?.contains(e.target)) {
-			this.handleModalClick(e);
-		}
-	}
-
-	handleChange(e) {
-		// Hidden input changes
-		const taxonomyField = window.targetCheck(e, '.taxonomy.field, .post.field');
-		if (taxonomyField && e.target.type === 'hidden') {
-			const fieldId = this.getFieldId(e.target);
-			this.updateFieldFromInput(fieldId);
-			return;
-		}
-
-		// Modal checkboxes
-		if (this.modal?.contains(e.target)) {
-			this.handleModalChange(e);
-		}
-	}
-
-	handleInput(e) {
-		// Modal search
-		if (this.modal?.contains(e.target) && e.target.type === 'search') {
-			this.performSearch(e.target.value.trim(), 'modal');
-			return;
-		}
-
-		// Autocomplete
-		if ('autocomplete' in e.target.dataset) {
-			const fieldId = this.getFieldId(e.target);
-			const field = this.fields.get(fieldId);
-			if (field?.hasAutocomplete) {
-				this.performSearch(e.target.value.trim(), 'autocomplete', fieldId);
-			}
-		}
-	}
-
-	handleFocus(e) {
-		if (!('autocomplete' in e.target.dataset)) return;
-
 		const fieldId = this.getFieldId(e.target);
 		const field = this.fields.get(fieldId);
+		if (!fieldId || !field) return;
 
-		if (field?.hasAutocomplete) {
-			this.preloadTaxonomy(field.taxonomy);
-		}
-	}
-
-	handleBlur(e) {
-		if (!('autocomplete' in e.target.dataset)) return;
-
-		setTimeout(() => {
-			const fieldId = this.getFieldId(e.target);
-			const field = this.fields.get(fieldId);
-
-			if (field?.autocompleteDropdown) {
-				field.autocompleteDropdown.hidden = true;
+		const autoComplete = window.targetCheck(e, '[data-autocomplete-select]');
+		if (autoComplete) {
+			let termId = parseInt(autoComplete.dataset.id);
+			this.addSelected(termId, fieldId);
+			if (field.ui.dropdown) {
+				field.ui.dropdown.hidden = true;
 			}
 
-			this.searchContexts.delete(fieldId);
-		}, 200);
+			if (field.ui.search) {
+				field.ui.search.value = '';
+			}
+		}
+
+		const toggleButton = window.targetCheck(e, field.ui.toggle);
+		if (toggleButton) {
+			e.preventDefault();
+			this.openModal(fieldId);
+			return;
+		}
+
+		const removeButton = window.targetCheck(e, 'button.remove-item');
+		if (removeButton) {
+			const fieldId = this.getFieldId(removeButton);
+			const termId = removeButton.closest('.selected-item').dataset.id??false;
+			if (fieldId && termId) {
+				this.removeSelected(termId, fieldId);
+			}
+			return;
+		}
+
+		if (e.target.matches('.modal-close')) {
+			this.modal?.handleClose();
+			return;
+		}
+
+		const backToParent = window.targetCheck(e, this.selectors.nav.back);
+		if (backToParent) {
+			this.navigateToParent();
+			return;
+		}
+
+		const toChild = window.targetCheck(e, this.selectors.nav.child);
+		if (toChild) {
+			const termItem = e.target.closest('li');
+			const termId = parseInt(termItem.dataset.id);
+
+			if (termId) {
+				this.navigateTo(termId);
+			}
+			return;
+		}
+
+		const pathLevel = window.targetCheck(e, this.selectors.nav.pathLevel);
+		if (pathLevel) {
+			const termId = parseInt(pathLevel.dataset.id)??0;
+			this.navigateTo(termId);
+		}
+
+		const dropdown = window.targetCheck(e, field.selectors.dropdown);
+		if (dropdown) {
+			// reset the timer for hiding the dropdown
+			this.scheduleHideDropdown(fieldId);
+			return;
+		}
+
+		const clearSearch = window.targetCheck(e, this.selectors.search.clear);
+		if (clearSearch) {
+			const field = this.currentField();
+			if (field && field.ui.search) {
+				field.ui.search.value = '';
+				this.store.setFilters({
+					search: '',
+					page: 1,
+					parent: this.store.filters.parent || 0
+				});
+			}
+			if (this.ui.search.input) {
+				this.ui.search.input.value = '';
+			}
+		}
+
 	}
+	handleChange(e) {
+		if (!this.container.contains(e.target)) {
+			return;
+		}
+		if (e.target.type !== 'checkbox') return;
+		e.preventDefault();
+		e.stopPropagation();
 
-	/***********************************************************************
-	 * UNIFIED SEARCH
-	 ***********************************************************************/
-
-	performSearch(query, context = 'modal', fieldId = null) {
-		const field = context === 'autocomplete'
-			? this.fields.get(fieldId)
-			: this.currentConfig;
-
+		const termId = parseInt(e.target.dataset.id);
+		let fieldId = this.getFieldId(e.target);
+		if (e.target.checked) {
+			this.addSelected(termId, fieldId);
+		} else {
+			this.removeSelected(termId, fieldId);
+		}
+	}
+	//For search in modal or field autocomplete
+	handleInput(e) {
+		let fieldId = this.getFieldId(e.target)??this.activeField;
+		if (!fieldId) return;
+		const field = this.fields.get(fieldId);
 		if (!field) return;
 
-		// Autocomplete validation
-		if (context === 'autocomplete') {
-			field.currentAutocompleteQuery = query;
-
-			if (query.length < 2) {
-				if (field.autocompleteDropdown) {
-					field.autocompleteDropdown.hidden = true;
-				}
-				return;
-			}
-
-			this.searchContexts.set(fieldId, 'autocomplete');
+		if (!this.container.open) {
 			this.activeField = fieldId;
-
-			if (field.autocompleteDropdown) {
-				field.autocompleteDropdown.hidden = false;
-			}
 		}
 
-		// Debounced search
+		const query = e.target.value.trim();
 		window.debouncer.schedule(
-			`taxonomy-search-${context}-${fieldId || 'modal'}`,
+			`${fieldId}-search`,
 			async () => {
 				await this.store.setFilters({
 					taxonomy: field.taxonomy,
@@ -503,507 +255,537 @@
 					page: 1,
 					parent: query ? 0 : (this.store.filters.parent || 0)
 				});
-
-				if (context === 'modal') {
-					window.removeChildren(this.ui.termsList);
+				if (this.container.open) {
+					window.removeChildren(this.ui.terms.list);
 				}
 			},
-			300
+			100
 		);
 	}
 
-	/***********************************************************************
-	 * MODAL OPERATIONS
-	 ***********************************************************************/
+	handleFocus(e) {
+		const fieldId = this.getFieldId(e.target);
+		const field = this.fields.get(fieldId);
+		if (!fieldId || !field) return;
+		if (!field.hasAutocomplete && !field.hasSearch) return;
 
-	setActiveField(fieldId, openModal = false) {
-		this.activeField = fieldId;
-		this.currentConfig = this.fields.get(fieldId);
+		window.debouncer.cancel(`${fieldId}-search-results`);
 
-		if (openModal) {
-			this.modalInstance.handleOpen();
+		if (!this.container.open){
+			this.activeField = fieldId;
+			this.preloadTaxonomy(field.taxonomy);
 		}
+	}
 
-		this.store.setFilter('taxonomy', this.currentConfig.taxonomy);
+	//Hide autocomplete dropdown on blur
+	handleBlur(e) {
+		const fieldId = this.getFieldId(e.target);
+		const field = this.fields.get(fieldId);
+		if (!fieldId || ! field) return;
+		if (!field.hasAutocomplete) return;
 
-		// Reset modal selection state
-		this.selectedTerms.clear();
+		this.scheduleHideDropdown(fieldId);
+	}
 
-		// Copy field selections to modal
-		this.currentConfig.selectedTerms.forEach(termId => {
-			const term = this.store.get(termId);
-			if (term) {
-				this.selectedTerms.set(termId, {
-					id: termId,
-					name: term.name,
-					path: term.path
-				});
+	scheduleHideDropdown(fieldId){
+		const field = this.fields.get(fieldId);
+		if (!field) return;
+
+		window.debouncer.schedule(
+			`${fieldId}-search-results`,
+			() => {
+				this.activeField = null;
+				field.ui.dropdown.hidden = true;
+			},
+			1500
+		);
+	}
+
+	/******************************************************************
+	 MODAL
+	 ******************************************************************/
+	initModal() {
+		this.modalID = 'dialog#jvb-selector';
+		this.container = document.querySelector(this.modalID);
+
+		this.modal = new window.jvbModal(
+			this.container,
+			{
+				handleForm: false,
+				save: null,
+				open: null
+			}
+		);
+		this.modal.subscribe((event, data) => {
+			switch (event) {
+
 			}
 		});
 	}
 
-	handleModalClick(e) {
-		if (window.targetCheck(e, '.remove-item')) {
-			const selectedItem = window.targetCheck(e, '.selected-item');
-			if (selectedItem) {
-				this.removeSelectedTermFromModal(selectedItem.dataset.id);
-			}
-		} else if (window.targetCheck(e, '.back-to-parent')) {
-			this.navigateToParent();
-		} else if (window.targetCheck(e, '.toggle-children')) {
-			const termItem = e.target.closest('li');
-			this.navigateToChild(
-				parseInt(termItem.dataset.id),
-				termItem.querySelector('.term-name').textContent
-			);
-		} else if (window.targetCheck(e, '.path-level')) {
-			const pathLevel = window.targetCheck(e, '.path-level');
-			this.navigateToPath(parseInt(pathLevel.dataset.id) || 0);
-		}
-	}
+	toggleModal(fieldId, open = true) {
+		const field = this.fields.get(fieldId);
+		if (!field) return;
 
-	handleModalChange(e) {
-		if (e.target.type !== 'checkbox') return;
-
-		e.preventDefault();
-		e.stopPropagation();
-
-		const termId = parseInt(e.target.closest('li').dataset.id);
-		const label = e.target.closest('li').querySelector('label');
-
-		if (e.target.checked) {
-			this.addSelectedTermToModal(termId, label.title, label.dataset.path);
+		if (open) {
+			this.openModal(fieldId);
 		} else {
-			this.removeSelectedTermFromModal(termId);
+			this.closeModal();
 		}
 	}
 
-	openModal() {
-		if (!this.currentConfig) {
-			console.error('No active field set');
-			return;
-		}
+	openModal(fieldId) {
+		const field = this.fields.get(fieldId);
+		if (!field) return;
 
-		this.updateModalUI();
-		this.updateModalSelections();
-
-		window.removeChildren(this.ui.termsList);
-		this.showLoading();
-	}
-
-	closeModal() {
-		this.observer.unobserve(this.ui.sentinel);
-		window.removeChildren(this.ui.termsList);
-
-		this.notify('selected-terms', {
-			terms: this.selectedTerms,
-			taxonomy: this.currentConfig.taxonomy
-		});
-
-		if (this.activeField) {
-			this.saveSelectionsToField(this.activeField);
-		}
-
-		this.activeField = null;
-		this.currentConfig = null;
-	}
-
-	updateModalUI() {
-		const singular = this.getLabel(this.currentConfig.taxonomy, 'single');
-		const plural = this.getLabel(this.currentConfig.taxonomy, 'plural');
-
-		this.ui.modal.title.textContent = `Select ${plural}`;
-
+		this.activeField = fieldId;
+		this.ui.modal.title.textContent = `Select ${field.plural}`;
 		if (this.ui.search.container) {
-			this.ui.search.container.style.display = this.currentConfig.canSearch ? 'block' : 'none';
+			this.ui.search.container.hidden = !field.canSearch;
 		}
-
 		if (this.ui.create.details) {
-			this.ui.create.details.style.display = this.currentConfig.canCreate ? 'block' : 'none';
-			this.ui.create.details.hidden = !this.currentConfig.canCreate;
+			this.ui.create.details.hidden = !field.canCreate;
 
 			if (this.ui.create.summary) {
-				this.ui.create.summary.textContent = `Add new ${singular}`;
+				this.ui.create.summary.textContent = `Add new ${field.singular}`;
 			}
-
 			if (this.ui.create.label.name) {
-				this.ui.create.label.name.textContent = `Name this ${singular}`;
+				this.ui.create.label.name.textContent = `Name this ${field.singular}`;
 			}
 			if (this.ui.create.label.parent) {
 				this.ui.create.label.parent.textContent = `Nest it under`;
 			}
 		}
+		let message = `Opened ${field.singular} selection. Choose from checkboxes, or search to filter results.`;
 
-		this.a11y?.announce(`Opened ${singular} selection. Choose from checkboxes or search to filter results.`);
-	}
+		window.removeChildren(this.ui.terms.list);
+		this.modal.handleOpen();
+		this.setLoading();
 
-	updateModalSelections() {
-		window.removeChildren(this.ui.selectedTerms);
-
-		this.selectedTerms.forEach((termData, id) => {
-			this.addTermDisplay(id, termData.name, termData.path, 'modal');
+		this.store.setFilters({
+			taxonomy: field.taxonomy,
+			page: 1,
+			search: '',
+			parent: 0,
 		});
 
-		this.checkSelectionLimits();
+		this.a11y.announce(message);
 	}
 
-	addSelectedTermToModal(id, name, path) {
-		this.selectedTerms.set(id, { id, name, path });
-
-		this.addTermDisplay(id, name, path, 'modal');
-		this.checkSelectionLimits();
-
-		const checkbox = this.ui.termsList.querySelector(`input[value="${id}"]`);
-		if (checkbox) checkbox.checked = true;
-	}
-
-	removeSelectedTermFromModal(id) {
-		this.selectedTerms.delete(parseInt(id));
-
-		const selectedItem = this.ui.selectedTerms.querySelector(`[data-id="${id}"]`);
-		if (selectedItem) selectedItem.remove();
-
-		const checkbox = this.ui.termsList.querySelector(`input[value="${id}"]`);
-		if (checkbox) checkbox.checked = false;
-
-		this.checkSelectionLimits();
-	}
-
-	checkSelectionLimits() {
-		if (!this.currentConfig || this.currentConfig.maxSelection === 0) {
-			return;
-		}
-
-		this.disabled = this.selectedTerms.size >= this.currentConfig.maxSelection;
-
-		this.ui.termsList.querySelectorAll('input[type="checkbox"]').forEach(checkbox => {
-			if (!checkbox.checked) {
-				checkbox.disabled = this.disabled;
-			}
-		});
-	}
-
-	saveSelectionsToField(fieldId) {
-		const field = this.fields.get(fieldId);
+	closeModal() {
+		this.modal.handleClose();
+		const field = this.fields.get(this.activeField);
 		if (!field) return;
+		this.observer.unobserve(this.ui.terms.sentinel);
+		window.removeChildren(this.ui.terms.list);
 
-		field.selectedTerms.clear();
-		window.removeChildren(field.selectedContainer);
-
-		this.selectedTerms.forEach((termData, id) => {
-			field.selectedTerms.add(id);
-			this.addTermDisplay(id, termData.name, termData.path, 'field', fieldId);
+		this.notify('selected-terms', {
+			terms: this.selectedTerms.get(this.activeField),
+			taxonomy: field.taxonomy
 		});
 
-		field.input.value = Array.from(field.selectedTerms).join(',');
-		field.input.dispatchEvent(new Event('change', { bubbles: true }));
+		this.activeField = null;
+
+		let message = `Closed ${field.singular} selector.`;
+		this.a11y.announce(message);
 	}
 
-	/***********************************************************************
-	 * TERM DISPLAY
-	 ***********************************************************************/
+	navigateToParent() {
+		const current = this.store.filters.parent;
+		if (current === 0) return;
+		let term = this.store.get(parseInt(current));
+		if (!term) return;
+		let parent = term.parent;
+		this.navigateTo(parseInt(parent));
+	}
+	navigateTo(termId = 0) {
+		termId = parseInt(termId)??0;
+		this.store.setFilters({parent: termId, page: 1});
+		window.removeChildren(this.ui.terms.list);
+		this.updateBreadcrumbs(termId);
+	}
 
-	addTermDisplay(termId, termName, termPath, context = 'field', fieldId = null) {
-		const config = context === 'field'
-			? this.fields.get(fieldId)
-			: this.currentConfig;
+	nextPage() {
+		let current = this.store.filters.page;
+		let page = Math.min(current++, this.store.lastResponse.total);
+		this.store.setFilters({page:page});
+	}
+	prevPage() {
+		let current = this.store.filters.page;
+		let page = Math.max(current - 1, 1);
+		this.store.setFilters({page:page});
+	}
 
-		const container = context === 'field'
-			? config.selectedContainer
-			: this.ui.selectedTerms;
-
-		if (container.querySelector(`[data-id="${termId}"]`)) return;
+	addTermToModal(termId) {
+		const term = this.store.get(termId);
+		if (!term) return;
 
 		const item = window.getTemplate('selectedTerm');
 		item.dataset.id = termId;
-		item.dataset.path = termPath;
-		item.dataset.name = termName;
-		item.dataset.taxonomy = config.taxonomy;
-		item.querySelector('.item-name').textContent = termPath;
-		item.querySelector('button').title = `Remove ${termName}`;
+		item.querySelector('span').textContent = term.path;
+		item.querySelector('button').title = `Remove ${name}`;
 
-		container.appendChild(item);
-
-		if (context === 'modal') {
-			const checkbox = this.ui.termsList.querySelector(`input[value="${termId}"]`);
-			if (checkbox) checkbox.checked = true;
-		}
+		this.ui.selected.append(item);
 	}
-
-	removeSelectedTerm(fieldId, termId) {
-		const field = this.fields.get(fieldId);
-		if (!field) return;
-
-		field.selectedTerms.delete(parseInt(termId));
-
-		const selectedItem = field.selectedContainer.querySelector(`[data-id="${termId}"]`);
-		if (selectedItem) selectedItem.remove();
-
-		field.input.value = Array.from(field.selectedTerms).join(',');
-		field.input.dispatchEvent(new Event('change', { bubbles: true }));
-	}
-
-	updateFieldFromInput(fieldId) {
-		const field = this.fields.get(fieldId);
-		if (!field) return;
-
-		const value = field.input.value.trim();
-		field.selectedTerms.clear();
-		window.removeChildren(field.selectedContainer);
-
-		if (value) {
-			value.split(',')
-				.map(id => parseInt(id.trim()))
-				.filter(id => !isNaN(id))
-				.forEach(id => field.selectedTerms.add(id));
-
-			this.initFieldDisplay(fieldId);
-		}
-	}
-
-	/***********************************************************************
-	 * NAVIGATION
-	 ***********************************************************************/
-
-	navigateToParent() {
-		this.store.setFilters({ parent: 0, page: 1 });
-		window.removeChildren(this.ui.termsList);
-		this.ui.breadcrumbs.back.hidden = true;
-	}
-
-	navigateToChild(termId, termName) {
-		this.store.setFilters({ parent: termId, page: 1 });
-		window.removeChildren(this.ui.termsList);
-		this.updateBreadcrumbs(termId, termName);
-		this.ui.breadcrumbs.back.hidden = false;
-	}
-
-	navigateToPath(parentId) {
-		this.store.setFilters({ parent: parentId, page: 1 });
-		window.removeChildren(this.ui.termsList);
-		this.ui.breadcrumbs.back.hidden = parentId === 0;
-	}
-
-	loadMoreTerms() {
-		const currentPage = this.store.filters.page || 1;
-		this.store.setFilter('page', currentPage + 1);
-	}
-
-	updateBreadcrumbs(termId, termName) {
-		const breadcrumb = window.getTemplate('termBreadcrumb');
-		breadcrumb.dataset.id = termId;
-		breadcrumb.textContent = termName;
-		breadcrumb.title = termName;
-
-		const existingCrumb = this.ui.breadcrumbs.nav.querySelector(`[data-id="${termId}"]`);
-		if (existingCrumb) {
-			while (existingCrumb.nextElementSibling) {
-				existingCrumb.nextElementSibling.remove();
+	/******************************************************************
+	 FIELDS
+	 ******************************************************************/
+	scanExistingFields(container = document.body) {
+		container.querySelectorAll('[data-type="selector"]').forEach(
+			selector => {
+				try {
+					this.registerField(selector);
+				} catch (error) {
+					this.error.log(error, {
+						component: 'TaxonomySelector',
+						action: 'scanExistingFields',
+						container: selector.dataset.name
+					});
+				}
 			}
-		} else {
-			this.ui.breadcrumbs.nav.appendChild(breadcrumb);
-		}
+		);
 	}
 
-	/***********************************************************************
-	 * RENDERING
-	 ***********************************************************************/
-
-	renderTerms(terms = null, append = false, showPath = false) {
-		if (!terms) terms = this.store.getFiltered();
-
-		if (!append) window.removeChildren(this.ui.termsList);
-
-		if (terms.length === 0) {
-			if (!append) this.showEmptyState();
+	registerField(element, options = {}) {
+		let input = element.querySelector('input[type="hidden"]');
+		if (!input) {
+			console.warn('TaxonomySelector: No hidden input found for field', element);
 			return;
 		}
 
-		const currentParent = this.store.filters.parent || 0;
-		this.ui.breadcrumbs.back.hidden = currentParent === 0;
+		if (!('fieldId' in element.dataset)) {
+			element.dataset.fieldId = window.generateID('selector');
+		}
+		const fieldId = element.dataset.fieldId;
+
+
+		let selectors = this.selectors.field;
+		let button = element.querySelector('button.taxonomy-toggle');
+		if (options.size === 0){
+			if (!button) return;
+			options = button.dataset;
+			if (options.size === 0) return;
+		} else if (Object.hasOwn(options, 'toggle')) {
+			button = document.querySelector(options.toggle);
+			selectors.toggle = options.toggle;
+		}
+
+		const config = {
+			id: fieldId,
+			value: input,
+			element: element,
+			taxonomy: options.taxonomy??false,
+			singular: options.single??'',
+			plural: options.plural??'',
+			name: element.dataset.field,
+			canSearch: Object.hasOwn(options, 'search'),
+			limit: options.limit??0,
+			hasAutocomplete: Object.hasOwn(options, 'autocomplete'),
+			canCreate: Object.hasOwn(options, 'creatable'),
+			isRequired: Object.hasOwn(options, 'required'),
+			toggle: button,
+			selectors: selectors,
+			ui: window.uiFromSelectors(selectors, element),
+			checked: false,
+		};
+		if (!config.taxonomy) return;
+		this.fields.set(fieldId, config);
+
+		//Check for stored selected terms in hidden input
+		let selected = new Set();
+		input.value.value.trim()
+			.split(',')
+			.map(id => parseInt(id.trim()))
+			.filter(id => !isNaN(id))
+			.forEach(id => selected.add(id));
+		this.selectedTerms.set(fieldId, selected);
+
+		if (this.isInitializing) {
+			this.batchFetch.add(config.taxonomy);
+		}
+		this.updateFieldUI(fieldId);
+
+		return fieldId;
+	}
+
+	addSelected(termId, fieldId = null) {
+		if (!fieldId) fieldId = this.activeField;
+
+		const field = this.fields.get(fieldId);
+		const term = this.store.get(termId);
+		if (!field || !term) return;
+
+		const selected = this.selectedTerms.get(fieldId);
+		if (field.limit !== 0 && selected.size >= field.limit) return;
+
+		selected.add(parseInt(termId));
+		this.addTermToDisplay(termId, fieldId);
+		this.updateFieldValue(fieldId);
+		this.checkLimits(fieldId);
+	}
+	removeSelected(termId, fieldId = null) {
+		if (!fieldId) fieldId = this.activeField;
+		const field = this.fields.get(fieldId);
+		const term = this.store.get(termId);
+		if (!field || !term) return;
+		this.selectedTerms.get(fieldId).delete(parseInt(termId));
+
+		const selectedItem = field.ui.selected.querySelector(`[data-i"${termId}"]`);
+		if (selectedItem) selectedItem.remove();
+		if (this.container.open) {
+			let item = this.ui.selected.querySelector(`[data-id="${termId}"]`);
+			if (item) item.remove();
+		}
+		this.updateFieldValue(fieldId);
+		this.checkLimits(fieldId);
+	}
+	updateFieldValue(fieldId) {
+		const field = this.fields.get(fieldId);
+		if (!field) return;
+		let selected = Array.from(this.selectedTerms.get(fieldId));
+		field.ui.value = selected.join(',');
+	}
+
+	checkLimits(fieldId) {
+		if (!this.container.open) return;
+		const field = this.fields.get(fieldId);
+		if (!field || field.limit === 0) return;
+		const disabled = this.selectedTerms.get(fieldId).size >= field.limit;
+		this.setCheckboxes(disabled);
+	}
+
+	updateFieldUI(fieldId) {
+		const field = this.fields.get(fieldId);
+		let selected = this.selectedTerms.get(fieldId);
+		if (!field || selected.size === 0) return;
+
+		Array.from(selected).forEach(termId => {
+			this.addTermToDisplay(termId, fieldId);
+		});
+	}
+
+	updateFieldsForTaxonomy(taxonomy) {
+		let fields = Array.from(this.fields.values())
+			.filter(field => !field.checked && field.taxonomy === taxonomy);
+		const hasItems = Array.from(this.store.data.values())
+			.some(term=>term.taxonomy === taxonomy);
+
+		fields.forEach(field => {
+			field.ui.toggle.disabled = !hasItems && !field.canCreate;
+			field.ui.toggle.title = !hasItems
+				? `No ${field.singular} available`
+				: `Select ${field.plural}`;
+
+			field.checked = true;
+		});
+	}
+
+	showModalTerms(append = true, showPath = false) {
+		const terms = this.store.getFiltered();
+		if (terms.size === 0) return;
+		if (!append) {
+			window.removeChildren(this.ui.terms.list);
+		}
+
+		const currentParent = this.store.filters.parent??0;
+		this.ui.nav.back.hidden = currentParent === 0;
 
 		const fragment = document.createDocumentFragment();
 		terms.forEach(term => {
 			const element = this.createTermElement({
-				id: parseInt(term.id),
-				name: term.name,
-				hasChildren: term.hasChildren,
-				path: term.path || null,
-				show: showPath
+				show: showPath,
+				... term
 			});
-
-			if (element) fragment.appendChild(element);
+			if (element) {
+				fragment.appendChild(element);
+			}
 		});
 
-		this.ui.termsList.appendChild(fragment);
+		this.ui.terms.list.append(fragment);
 	}
+	createTermElement(term) {
+		if (!term || !term.name) return null;
 
-	createTermElement(termData) {
-		if (!termData?.name) return null;
+		const item = window.getTemplate('termListItem');
+		item.dataset.id = term.id;
 
-		const listItem = window.getTemplate('termListItem');
-		listItem.dataset.id = termData.id;
+		const isSelected = this.selectedTerms.get(this.activeField).has(term.id);
+		let [
+			checkbox,
+			label,
+			nameSpan
+		] = [
+			item.querySelector('input'),
+			item.querySelector('label'),
+			item.querySelector('span, .term-name')
+		];
 
-		const isSelected = this.selectedTerms.has(termData.id);
-		const checkbox = listItem.querySelector('input');
-		const label = listItem.querySelector('label');
-		const nameSpan = listItem.querySelector('.term-name');
-
-		checkbox.id = `${this.currentConfig.container.id}${termData.id}`;
-		checkbox.name = `${this.currentConfig.container.id}${this.currentConfig.taxonomy}-select`;
-		checkbox.value = termData.id;
-		checkbox.disabled = !isSelected && this.disabled;
-		checkbox.checked = isSelected;
-
-		label.htmlFor = checkbox.id;
-		label.title = termData.path || termData.name;
-		label.dataset.path = termData.path;
-
-		nameSpan.textContent = termData.show ? termData.path : termData.name;
-
-		if (termData.hasChildren) {
-			const childrenToggle = window.getTemplate('termChildrenToggle');
-			childrenToggle.ariaLabel = `View sub-terms of ${termData.name}`;
-			listItem.appendChild(childrenToggle);
+		let field = this.currentField();
+		let limitReached = field.limit > 0 && this.selectedTerms.get(this.activeField).size >= field.limit;
+		if (checkbox && label && nameSpan) {
+			[
+				checkbox.id,
+				checkbox.name,
+				checkbox.value,
+				checkbox.disabled,
+				checkbox.checked,
+				label.htmlFor,
+				label.title,
+				label.dataset.path,
+				nameSpan.textContent
+			] = [
+				`${field.element.id}-${term.id}`,
+				`${field.container.id}-${field.taxonomy}-select`,
+				term.id,
+				!isSelected && limitReached,
+				isSelected,
+				`${field.element.id}-${term.id}`,
+				term.path??term.name,
+				term.path,
+				term.show ? term.path : term.name
+			];
+			if (term.hasChildren) {
+				const toggle = window.getTemplate('termChildrenToggle');
+				if (toggle) {
+					toggle.ariaLabel = `View ${field.plural} nested under ${term.name}`;
+					item.append(toggle);
+				}
+			}
 		}
 
-		return listItem;
+		return item;
 	}
 
-	/***********************************************************************
-	 * AUTOCOMPLETE
-	 ***********************************************************************/
+	showAutocompleteTerms() {
+		const field = this.currentField();
+		const terms = this.currentTerms();
+		if (!field || terms.size ===0) return;
 
-	showAutocompleteResults(field, terms, query) {
-		if (!field?.autocompleteDropdown) return;
-
-		const dropdown = field.autocompleteDropdown;
+		const dropdown = field.ui.dropdown;
 		window.removeChildren(dropdown);
-
 		if (terms.length === 0) {
-			this.showEmptyState('No items found.', dropdown);
+			this.showEmptyState(`No ${field.plural} found.`, dropdown);
 		} else {
-			const fragment = document.createDocumentFragment();
-
 			terms.forEach(term => {
-				const item = this.createAutocompleteItem(field, term);
-				if (item) fragment.appendChild(item);
-			});
-
-			dropdown.appendChild(fragment);
+				const item = this.createAutocompleteTerm(term);
+				if (item) {
+					dropdown.append(item);
+				}
+			})
 		}
 
-		// Create button if allowed and no exact match
-		const currentQuery = field.currentAutocompleteQuery || query;
-		if (field.canCreate && currentQuery) {
-			const exactMatch = terms.find(term =>
-				term.name.toLowerCase() === currentQuery.toLowerCase()
-			);
-
-			if (!exactMatch) {
-				dropdown.appendChild(this.createAutocompleteCreateButton(currentQuery));
+		const query = field.ui.search?.value;
+		if (field.canCreate && query.length >= 2 && this.creator) {
+			const createButton = this.createTermButton(query);
+			if (createButton) {
+				dropdown.append(createButton);
 			}
 		}
 
 		dropdown.hidden = false;
 	}
+	createAutocompleteTerm(term) {
+		const item = window.getTemplate('autocompleteItem');
+		if (!item) return;
 
-	createAutocompleteItem(field, term) {
-		const button = document.createElement('button');
-		button.type = 'button';
-		button.className = 'autocomplete-item';
-		button.dataset.id = term.id;
-		button.dataset.name = term.name;
-		button.dataset.path = term.path || term.name;
-		button.textContent = term.path || term.name;
-
-		button.addEventListener('click', () => {
-			field.selectedTerms.add(parseInt(term.id));
-			this.addTermDisplay(term.id, term.name, term.path, 'field', field.id);
-
-			field.input.value = Array.from(field.selectedTerms).join(',');
-			field.input.dispatchEvent(new Event('change', { bubbles: true }));
-
-			field.autocompleteDropdown.hidden = true;
-			const input = field.container.querySelector('input[data-autocomplete]');
-			if (input) input.value = '';
-		});
-
-		return button;
+		item.dataset.id = term.id;
+		item.textContent = term.path || term.name;
+		return item;
 	}
-
-	createAutocompleteCreateButton(query) {
-		const button = document.createElement('button');
-		button.type = 'button';
-		button.className = 'autocomplete-item create-term';
-		button.dataset.query = query;
-
-		const strong = document.createElement('strong');
-		strong.textContent = 'Create: ';
-
-		button.appendChild(strong);
-		button.appendChild(document.createTextNode(`"${query}"`));
-
-		return button;
-	}
-
-	showAutocompleteError(fieldId) {
+	/******************************************************************
+	 UI
+	 ******************************************************************/
+	addTermToDisplay(termId, fieldId) {
+		const term = this.store.get(termId);
 		const field = this.fields.get(fieldId);
-		if (!field?.autocompleteDropdown) return;
+		if (!term || !field) return;
+		//if the term already exists in the selected items, bail early
+		if (field.ui.selected.querySelector(`[data-id="${termId}"]`)) return;
 
-		window.removeChildren(field.autocompleteDropdown);
-		this.showEmptyState('Hmmm... something went wrong', field.autocompleteDropdown);
+		const item = window.getTemplate('selectedTerm');
+		if (!item) return;
+
+		item.dataset.id = termId;
+		item.dataset.taxonomy = field.taxonomy;
+		item.querySelector('.item-name').textContent = term.path;
+		item.querySelector('button').title = `Remove ${term.name}`;
+
+		field.ui.selected.append(item);
+
+		if (this.container.open) {
+			this.addTermToModal(termId);
+			const checkbox = this.ui.terms.list.querySelector(`input[value="${termId}"]`);
+			if (checkbox) checkbox.checked = true;
+		}
+	}
+	createTermButton(query) {
+		const button = window.getTemplate('autocompleteButton');
+		if(!button) return;
+
+		let queryEl = button.querySelector('span');
+		queryEl.textContent = `"${query}"`;
+
+		return button;
 	}
 
-	/***********************************************************************
-	 * UI STATES
-	 ***********************************************************************/
+	updateBreadcrumbs(termId) {
+		const nav = this.ui.nav.nav;
+		if (!nav) return;
+		const existingCrumb = Array.from(nav.children)
+			.find(crumb => parseInt(crumb.dataset.id) === termId);
 
-	showLoading() {
-		this.ui.loading.loading.hidden = false;
-		this.modal.classList.add('loading');
-
-		const searchQuery = this.store.filters.search || '';
-		const currentParent = this.store.filters.parent || 0;
-
-		const message = searchQuery
-			? `searching for "${searchQuery}" items`
-			: currentParent === 0
-				? 'loading items'
-				: 'loading child items';
-
-		if (window.typeLoop) {
-			this.stopTyping = window.typeLoop(this.ui.loading.text, message);
+		if (existingCrumb) {
+			// Remove all siblings after this crumb
+			let nextSibling = existingCrumb.nextElementSibling;
+			while (nextSibling) {
+				const toRemove = nextSibling;
+				nextSibling = nextSibling.nextElementSibling;
+				toRemove.remove();
+			}
 		} else {
-			this.ui.loading.text.textContent = message;
+			// Add new breadcrumb
+			const term = this.store.get(termId);
+			if (!term) return;
+
+			const crumb = window.getTemplate('termBreadcrumb');
+			if (!crumb) return;
+
+			crumb.dataset.id = termId;
+			crumb.textContent = term.name;
+			crumb.title = term.name;
+
+			nav.append(crumb);
 		}
 	}
 
-	hideLoading() {
-		this.ui.loading.loading.hidden = true;
-		this.modal.classList.remove('loading');
+	updateSelectionCount() {
+		if (!this.container.open) return;
+		const field = this.fields.get(this.activeField);
+		if (!field) return;
 
-		if (this.stopTyping) {
-			this.stopTyping();
-		}
-	}
+		if (this.ui.modal.count) {
+			const total = this.selectedTerms.get(this.activeField).size;
 
-	showEmptyState(message = 'No items found.', container = null) {
-		if (!container) container = this.ui.termsList;
-
-		const emptyElement = window.getTemplate('noResults');
-		const messageSpan = emptyElement.querySelector('span');
-
-		if (message && messageSpan) {
-			messageSpan.textContent = message;
+			this.ui.modal.count.textContent = field.limit > 0
+				? `${total} of ${field.limit} ${field.plural} selected`
+				: `${total} ${field.plural} selected`;
 		}
 
-		container.appendChild(emptyElement);
 	}
-
-	/***********************************************************************
-	 * UTILITIES
-	 ***********************************************************************/
+	/******************************************************************
+	 UTILITY
+	 ******************************************************************/
+	currentField() {
+		return this.fields.get(this.activeField)??false;
+	}
+	currentTerms() {
+		return this.store.getFiltered();
+	}
+	needsCreator() {
+		return Array.from(this.fields.values()).some(field =>
+			field.canCreate || field.hasAutocomplete
+		);
+	}
 
 	getFieldId(element) {
 		if (element.dataset.fieldId) return element.dataset.fieldId;
@@ -1012,55 +794,170 @@
 		return fieldContainer?.dataset.fieldId || null;
 	}
 
-	getLabel(taxonomy, type = 'single') {
-		return jvbSettings.labels[taxonomy]?.[type] || taxonomy;
-	}
-
-	async batchFetchTaxonomies() {
-		if (this.taxonomiesToFetch.size === 0) return;
-
-		const taxonomies = Array.from(this.taxonomiesToFetch);
-		this.taxonomiesToFetch.clear();
-
-		this.store.setFilters({
-			taxonomy: taxonomies.join(','),
-			page: 1,
-			search: '',
-			parent: 0
+	/**
+	 * Sets all checkbox disabled (or not)
+	 * @param {Boolean} disabled
+	 */
+	setCheckboxes(disabled) {
+		this.ui.terms.list.querySelectorAll('input[type=checkbox]').forEach(checkbox => {
+			if (!checkbox.checked) {
+				checkbox.disabled = disabled;
+			}
 		});
 	}
 
-	async preloadTaxonomy(taxonomy) {
-		await this.store.setFilters({
+	/******************************************************************
+	 DATASTORE HELPERS
+	******************************************************************/
+	handleStoreEvent(event, data) {
+		const handlers = {
+			'data-loaded': () => this.handleDataLoaded(),
+			'filters-changed': () => this.handleFiltersChanged(),
+			'fetch-error': () => this.handleFetchError()
+		};
+
+		handlers[event]?.();
+	}
+	handleDataLoaded() {
+		const taxonomy = this.store.filters.taxonomy;
+		if (taxonomy?.includes(',')) {
+			const taxonomies = taxonomy.split(',').map(t => t.trim());
+			taxonomies.forEach(tax => this.updateFieldsForTaxonomy(tax));
+		}
+
+		if (this.container.open) {
+			this.showResults();
+			return;
+		}
+		if (this.activeField) {
+			this.showResults(true);
+		}
+	}
+
+	showResults(isAutoComplete = false) {
+		this.setLoading(false);
+		const terms = this.store.getFiltered();
+		const filters = this.store.filters;
+		const response = this.store.lastResponse?.page || {};
+		const isSearch = filters.search && filters.search.length > 0;
+		const append = filters.page > 1;
+		const field = this.currentField();
+
+		this.notify('terms-loaded', {
+			terms,
+			filters
+		});
+
+		if (terms.length === 0) {
+			if (!append) {
+				this.showEmptyState(isSearch ? `No matching ${field.plural}.` : `No ${field.plural} available.`);
+			}
+			this.observer.unobserve(this.ui.terms.sentinel);
+		} else {
+			if (!isAutoComplete) {
+				this.showModalTerms(append, isSearch);
+
+				if (response.has_more) {
+					this.observer.observe(this.ui.terms.sentinel);
+				} else {
+					this.observer.unobserve(this.ui.terms.sentinel);
+				}
+			} else {
+				this.showAutocompleteTerms()
+			}
+		}
+
+		this.a11y.announce(terms.length, append);
+	}
+	handleFiltersChanged() {
+		// if (this.modal?.open) {
+		// 	this.setLoading();
+		// }
+	}
+
+	handleFetchError(error) {
+		this.setLoading(false);
+	}
+	async batchFetchTaxonomies() {
+		if (this.batchFetch.size === 0) return;
+
+		const taxonomies = Array.from(this.batchFetch);
+		taxonomies.forEach(tax => this.loadedTaxonomies.add(tax));
+		this.batchFetch.clear();
+
+		try {
+			taxonomies.forEach(tax => this.loadedTaxonomies.add(tax));
+
+			await this.store.setFilters({
+				taxonomy: taxonomies.join(','),
+				page: 1,
+				search: '',
+				parent: 0
+			});
+		} catch (error) {
+			console.error('Failed to batch fetch taxonomies:', error);
+		}
+	}
+
+	preloadTaxonomy(taxonomy) {
+		if (this.loadedTaxonomies.has(taxonomy)) return;
+
+		this.store.setFilters( {
 			taxonomy: taxonomy,
 			page: 1,
 			search: '',
 			parent: 0
 		});
+
+		this.loadedTaxonomies.add(taxonomy);
 	}
 
-	handleError(error, context, detail = null) {
-		console.error(`Taxonomy ${context} error:`, error, detail);
+	/**************************************************
+	 LOADING
+	**************************************************/
+	setLoading(on = true) {
+		this.ui.loading.loading.hidden = on;
+		this.modal.classList.toggle('loading', on);
 
-		if (this.error?.log) {
-			this.error.log(error, {
-				component: 'TaxonomySelector',
-				action: context,
-				detail: detail
-			});
-		}
+		if (on) {
+			let searchQuery = this.store.filters.search || '';
+			searchQuery = searchQuery === '' ? false : searchQuery;
+			const currentParent = this.store.filters.parent || 0;
+			const message = searchQuery
+				? `Searching for "${searchQuery} items` :
+				currentParent === 0
+					? 'loading items'
+					: 'loading child items';
 
-		if (this.modal?.open) {
-			this.showEmptyState('Error loading. Please try again.');
+			if (window.typeLoop && this.ui.loading.text) {
+				this.stopTyping = window.typeLoop(this.ui.loading.text, message);
+			} else {
+				this.ui.loading.text.textContenet = message;
+			}
+		} else {
+			if (this.stopTyping) {
+				this.stopTyping();
+				this.stopTyping = null;
+			}
 		}
 	}
-
+	showEmptyState(message = 'No items found.', container = null) {
+		if (!container) container = this.ui.terms.list;
+		const emptyElement = window.getTemplate('noTermResults');
+		const span = emptyElement.querySelector('span');
+		if (message && span) {
+			span.textContent = message;
+		}
+		container.append(emptyElement);
+	}
+	/**************************************************
+	 SUBSCRIBERS
+	**************************************************/
 	subscribe(callback) {
 		this.subscribers.add(callback);
 		return () => this.subscribers.delete(callback);
 	}
-
-	notify(event, data = {}) {
+	notify(event, data={}) {
 		this.subscribers.forEach(callback => {
 			try {
 				callback(event, data);
@@ -1069,25 +966,24 @@
 			}
 		});
 	}
-
+	/******************************************************
+	 CLEANUP
+	******************************************************/
 	destroy() {
-		document.removeEventListener('click', this.handleClick);
-		document.removeEventListener('change', this.handleChange);
-		document.removeEventListener('input', this.handleInput);
-		document.removeEventListener('focus', this.handleFocus);
-		document.removeEventListener('blur', this.handleBlur);
+		document.removeEventListener('click', this.clickHandler);
+		document.removeEventListener('change', this.changeHandler);
+		document.removeEventListener('input', this.inputHandler);
+		document.removeEventListener('focus', this.focusHandler);
+		document.removeEventListener('blur', this.blurHandler);
 
 		this.observer?.disconnect();
-		this.store.destroy();
 		this.subscribers.clear();
 		this.fields.clear();
 		this.selectedTerms.clear();
-		this.searchContexts.clear();
 	}
 }
 
-// Initialize on auth ready
-document.addEventListener('DOMContentLoaded', () => {
+document.addEventListener('DOMContentLoaded', function() {
 	window.auth.subscribe((event) => {
 		if (event === 'auth-loaded') {
 			window.jvbSelector = new TaxonomySelector();
diff --git a/assets/js/concise/TaxonomySelectorOld.js b/assets/js/concise/TaxonomySelectorOld.js
new file mode 100644
index 0000000..711dc4e
--- /dev/null
+++ b/assets/js/concise/TaxonomySelectorOld.js
@@ -0,0 +1,1561 @@
+/**
+ * Centralized Taxonomy Selector with DataStore Integration
+ * Handles all taxonomy selection fields using DataStore for state management
+ */
+class TaxonomySelectorOld {
+	constructor() {
+		this.a11y = window.jvbA11y;
+		this.error = window.jvbError;
+		this.index = -1;
+
+		this.hasAutocomplete = false;
+		this.isInitializing = true;
+		this.taxonomiesToFetch = new Set();
+
+		this.triggers = new Set(['.taxonomy-toggle']);
+
+		this.subscribers = new Set();
+
+		const store = window.jvbStore.register(
+			'taxonomies',
+			{
+				storeName: `terms`,
+				keyPath: 'id',
+				showLoading: false,
+				indexes: [
+					{name: 'taxonomy', keyPath: 'taxonomy'},
+					{name: 'parent', keyPath: 'parent'},
+					{name: 'slug', keyPath: 'slug', unique: true},
+					{name: 'count', keyPath: 'count'},
+				],
+				endpoint: 'terms',
+				TTL: 2 * 60 * 1000, //2 hours
+				filters: {
+					taxonomy: '',
+					page: 1,
+					search: '',
+					parent: 0
+				},
+				required: 'taxonomy',
+				delayFetch: true,
+			});
+		this.store = store.terms;
+
+		// Central field management
+		this.fields = new Map();
+		this.selectedTerms = new Map();    // Current modal selection
+
+		// Current modal context
+		this.activeField = null;
+		this.currentConfig = null;
+		this.currentSingular = null;
+		this.currentPlural = null;
+
+		// Modal state
+		this.disabled = false;
+
+		// Search debouncing
+		this.searchHandler = null;
+		this.autocompleteHandler = null;
+		this.isAutocompleteActive = false;
+
+		this.init();
+	}
+
+	/**
+	 * Initialize the selector
+	 */
+	init() {
+		this.initModal();
+		this.scanExistingFields();
+		this.initGlobalListeners();
+
+		if (this.hasAutocomplete && window.jvbTaxCreator) {
+			this.creator = new window.jvbTaxCreator(this);
+
+		}
+		this.store.subscribe(this.handleStoreEvent.bind(this));
+		// Complete initialization
+		this.isInitializing = false;
+		this.batchFetchTaxonomies();
+	}
+
+	/**
+	 * Handle DataStore events
+	 */
+	handleStoreEvent(event, data) {
+		switch (event) {
+			case 'data-loaded':
+				const taxonomy = this.store.filters.taxonomy;
+				// Handle batch taxonomy loading (comma-separated)
+				if (taxonomy?.includes(',')) {
+					this.handleBatchDataLoaded(taxonomy, data);
+				}
+				// Update button states for this taxonomy (or taxonomies)
+				if (taxonomy) {
+					// Handle comma-separated taxonomies from batch fetch
+					const taxonomies = taxonomy.includes(',')
+						? taxonomy.split(',').map(t => t.trim())
+						: [taxonomy];
+
+					taxonomies.forEach(tax => {
+						this.updateFieldsForTaxonomy(tax);
+					});
+				}
+
+				// Only render if modal is open OR autocomplete active
+				if (this.modal?.open) {
+					this.handleTermsLoaded(data);
+				}
+
+				if (this.isAutocompleteActive && this.activeField) {
+					const field = this.fields.get(this.activeField);
+					const terms = data.data?.items || [];
+					const query = data.filters?.search || '';
+					this.showAutocompleteResults(field, terms, query);
+					this.isAutocompleteActive = false;
+				}
+				break;
+
+			case 'filters-changed':
+				if (this.modal?.open) {
+					this.showLoading();
+				}
+				break;
+
+			case 'fetch-error':
+				if (this.isAutocompleteActive && this.activeField) {
+					this.showAutocompleteError(this.activeField);
+					this.isAutocompleteActive = false;
+				}
+				this.handleFetchError(data.error);
+				break;
+		}
+	}
+
+	/**
+	 * Handle loaded terms from DataStore
+	 */
+	handleTermsLoaded(data) {
+		this.hideLoading();
+		const terms = this.store.getFiltered();  // Use getFiltered() instead of getFilteredItems()
+		const response = this.store.lastResponse?.page || {};
+		const isSearch = data.filters?.search && data.filters.search.length > 0;
+		const append = response.page > 1;
+
+		this.notify('terms-loaded', { terms, filters: data.filters });
+
+		if (terms.length === 0) {
+			if (!append) {
+				this.showEmptyState(isSearch ? 'No results found.' : 'No items available.');
+			}
+			this.observer.unobserve(this.ui.sentinel);
+		} else {
+			this.renderTerms();
+
+			// Handle pagination
+			if (response.has_more) {
+				this.observer.observe(this.ui.sentinel);
+			} else {
+				this.observer.unobserve(this.ui.sentinel);
+			}
+		}
+
+		// Announce to screen readers
+		this.a11y?.announce(terms.length, append);
+	}
+
+	/**
+	 * Handle fetch errors
+	 */
+	handleFetchError(error) {
+		console.error('Taxonomy fetch error:', error);
+		this.hideLoading();
+
+		if (this.error?.log) {
+			this.error.log(error, {
+				component: 'TaxonomySelector',
+				action: 'fetchTerms'
+			}, () => this.fetchCurrentTerms());
+		} else {
+			this.showEmptyState('Error loading terms. Please try again.');
+		}
+	}
+
+
+	/**
+	 * Check if taxonomy has terms and update button states
+	 */
+	updateFieldButtonState(fieldId) {
+		const field = this.fields.get(fieldId);
+		if (!field) return;
+
+		// Check store for items of this specific taxonomy
+		const hasTerms = Array.from(this.store.data.values())
+			.some(term => term.taxonomy === field.taxonomy);
+
+		if (field.toggle) {
+			field.toggle.disabled = !hasTerms && !field.canCreate;
+			field.toggle.title = !hasTerms
+				? `No ${this.getSingular(field.taxonomy)} available`
+				: `Select ${this.getPlural(field.taxonomy)}`;
+		}
+	}
+	/**
+	 * Update fields when taxonomy items are updated
+	 */
+	updateFieldsForTaxonomy(taxonomy) {
+		this.getFieldsForTaxonomy(taxonomy).forEach(field => {
+			this.updateFieldButtonState(field.id);
+		});
+	}
+
+	/**
+	 * Get fields for a specific taxonomy
+	 */
+	getFieldsForTaxonomy(taxonomy) {
+		return Array.from(this.fields.values())
+			.filter(field => field.taxonomy === taxonomy);
+	}
+
+
+
+	/**
+	 * Scan page for existing taxonomy fields and register them
+	 */
+	scanExistingFields(container = null) {
+		if (!container) {
+			container = document.body;
+		}
+		const selectors = container.querySelectorAll('.field.taxonomy, .field.post');
+
+		selectors.forEach(selector => {
+			try {
+				this.registerField(selector);
+			} catch (error) {
+				this.error.log(error, {
+					component: 'TaxonomySelector',
+					action: 'scanExistingFields',
+					container: selector.dataset.name
+				});
+			}
+		});
+	}
+
+	/**
+	 * Register a taxonomy field
+	 */
+	registerField(field, options = {}) {
+		let input = field.querySelector('input[type=hidden]');
+		if (!input) {
+			return false;
+		}
+		if (!('fieldId' in field.dataset)) {
+			field.dataset.fieldId = this.createFieldId(field);
+		}
+		let fieldId = field.dataset.fieldId;
+
+		let button = (Object.hasOwn(options, 'button')) ? options.button : field.querySelector('button.taxonomy-toggle');
+
+		if (Object.hasOwn(options, 'buttonSelector')) {
+			this.triggers.add(options.buttonSelector);
+		}
+
+		let config = {
+			id: fieldId,
+			input: input,
+			container: field,
+			taxonomy: button.dataset.taxonomy,
+			name: field.dataset.field,
+			maxSelection: parseInt(button.dataset.max) || 0,
+			canSearch: 'search' in button.dataset,
+			hasAutocomplete: 'autocomplete' in button.dataset,
+			autocompleteDropdown: field.querySelector('.autocomplete-dropdown')??false,
+			canCreate: 'creatable' in button.dataset,
+			isRequired: 'required' in button.dataset,
+			selectedTerms: new Set(),
+			toggle: button,
+			selectedContainer: (Object.hasOwn(options, 'selected')) ? options.selected : field.querySelector('.selected-items'),
+			...options
+		};
+
+		if (!this.hasAutocomplete && config.hasAutocomplete) {
+			this.hasAutocomplete = true;
+			this.initAutocomplete();
+		}
+
+		// Parse initial selected values
+		const value = input.value.trim();
+		if (value !== '') {
+			const selectedIds = value.split(',')
+				.map(id => parseInt(id.trim()))
+				.filter(id => !isNaN(id));
+			selectedIds.forEach(id => config.selectedTerms.add(id));
+		}
+
+		if (Object.hasOwn(options, 'selectedItems')) {
+			options.selectedItems.forEach(id => {
+				config.selectedTerms.add(id);
+			});
+		}
+
+		this.fields.set(fieldId, config);
+
+		// Ensure store exists for this taxonomy
+		if (this.isInitializing) {
+			this.taxonomiesToFetch.add(config.taxonomy);
+		} else {
+			// this.store.setFilter('taxonomy', config.taxonomy);
+		}
+
+		// Initialize display for any pre-selected values
+		if (config.selectedTerms.size > 0) {
+			this.initFieldDisplay(fieldId);
+		}
+
+		return fieldId;
+	}
+
+	/**
+	 * Register a filter button (simplified registration for feed blocks)
+	 */
+	registerFilterButton(button, options = {}) {
+		const fieldId = this.createFieldId(button);
+		button.dataset.fieldId = fieldId;
+
+		if (options.buttonSelector) {
+			this.triggers.add(options.buttonSelector);
+		}
+
+		const config = {
+			id: fieldId,
+			input: null,
+			container: options.container || button.closest('.filters') || button.parentElement,
+			taxonomy: button.dataset.taxonomy,
+			name: `filter_${button.dataset.taxonomy}`,
+			maxSelection: parseInt(button.dataset.max) || 0,
+			canSearch: 'search' in button.dataset,
+			hasAutocomplete: false,
+			canCreate: false,
+			isRequired: false,
+			selectedTerms: new Set(options.selectedItems || []),
+			toggle: button,
+			selectedContainer: options.selected || null,
+			isFilterMode: true,
+			...options
+		};
+
+		this.fields.set(fieldId, config);
+
+		if (this.isInitializing) {
+			this.taxonomiesToFetch.add(config.taxonomy);
+		} else {
+			this.store.setFilter('taxonomy', config.taxonomy);
+		}
+
+		return fieldId;
+	}
+
+	/**
+	 * Create unique field ID
+	 */
+	createFieldId(field) {
+		this.index++;
+		return 'selector-' + this.index;
+	}
+
+	/**
+	 * Initialize display for a field with existing values
+	 */
+	async initFieldDisplay(fieldId) {
+		const field = this.fields.get(fieldId);
+		if (!field || field.selectedTerms.size === 0) return;
+
+		const selectedIds = Array.from(field.selectedTerms);
+
+		selectedIds.forEach(termId => {
+			const term = this.store.get(termId);  // Changed from getItem
+			if (term) {
+				this.addTermToDisplay(fieldId, term.id, term.name, term.path);
+			}
+		});
+	}
+
+	/**
+	 * Initialize modal elements
+	 */
+	initModal() {
+		this.modalID = 'dialog#jvb-selector';
+		this.modal = document.querySelector(this.modalID);
+
+		if (!this.modal) {
+			console.warn('Taxonomy selector modal not found');
+			return;
+		}
+
+		this.initModalElements();
+
+		// Initialize modal instance
+		this.modalInstance = new window.jvbModal(this.modal, {
+			handleForm: false,
+			save: null,
+			open: null
+		});
+		this.modalInstance.subscribe((event, data) => {
+			switch (event) {
+				case 'modal-open':
+					this.openModal(data);
+					break;
+				case 'modal-close':
+					this.closeModal(data);
+					break;
+			}
+		});
+	}
+
+	/**
+	 * Initialize modal element references
+	 */
+	initModalElements() {
+		this.selectors = {
+			search: {
+				input: '[type=search]',
+				clear: '.clear-search',
+				container: '.search-wrapper'
+			},
+			termsList: 	'.items-container',
+			termsWrap:	'.items-wrap',
+			breadcrumbs: {
+				nav: 'nav.term-navigation',
+				back: '.back-to-parent',
+			},
+			loading: {
+				loading: '.loading',
+				text: '.loading span'
+			},
+			selectedTerms: '.selected-items',
+			sentinel: '.scroll-sentinel',
+			modal: {
+				title: '#modal-title',
+				content: '.modal-content'
+			},
+			create: {
+				details: '.create-new-term',
+				parent: '#select_parent',
+				summary: '.create-new-term summary',
+				name: '#term_name',
+				button: '.submit-term',
+				label: {
+					name: '[for=term_name]',
+					parent: '[for=select_parent]'
+				}
+			},
+			favouriteTerms: '.favourite-terms'
+		}
+
+		this.ui = window.uiFromSelectors(this.selectors);
+
+		// Initialize intersection observer for infinite scroll
+		this.observer = new IntersectionObserver((entries) => {
+			entries.forEach(entry => {
+				if (entry.isIntersecting) {
+					this.loadMoreTerms();
+				}
+			});
+		}, {
+			root: this.ui.termsWrap,
+			threshold: 0.5
+		});
+	}
+
+	/**
+	 * Set up global event delegation
+	 */
+	initGlobalListeners() {
+		document.addEventListener('click', this.handleClick.bind(this));
+		document.addEventListener('change', this.handleChange.bind(this));
+		if (this.hasAutocomplete) {
+			this.initAutocomplete();
+		}
+	}
+
+	initAutocomplete()
+	{
+		this.autocompleteHandler = (e) => {
+			window.debouncer.schedule(
+				'taxonomy-autocomplete',
+				() => this.handleAutocomplete(e),
+				300
+			);
+		};
+		document.addEventListener('input', this.autocompleteHandler);
+		document.addEventListener('blur', this.cleanupAutocomplete.bind(this));
+		// Preload taxonomy data on focus
+		document.addEventListener('focus', (e) => {
+			if (!('autocomplete' in e.target.dataset)) {
+				return;
+			}
+
+			const fieldId = this.getFieldId(e.target);
+			const field = this.fields.get(fieldId);
+
+			if (!field) return;
+
+			// Preload this taxonomy's data
+			this.preloadTaxonomy(field.taxonomy);
+		}, true); // Use capture phase
+	}
+
+	/**
+	 * Handle global click events
+	 */
+	handleClick(e) {
+		// Handle taxonomy toggle buttons
+		const toggleButton = window.targetCheck(e, Array.from(this.triggers));
+
+		if (toggleButton) {
+			e.preventDefault();
+			this.handleToggleClick(toggleButton);
+			return;
+		}
+
+		// Handle remove selected term buttons
+		const removeButton = window.targetCheck(e, 'button.remove-item');
+		if (removeButton && e.target.closest('.jvb-selector')) {
+			const fieldId = this.getFieldId(removeButton);
+			const termId = removeButton.closest('.selected-item').dataset.id;
+			this.removeSelectedTerm(fieldId, termId);
+			return;
+		}
+
+		// Handle modal close button
+		if (e.target.matches('.modal-close')) {
+			if (this.modalInstance) {
+				this.modalInstance.handleClose();
+			}
+			return;
+		}
+
+		// Handle clicks within the modal
+		if (this.modal && this.modal.contains(e.target)) {
+			this.handleModalClick(e);
+		}
+	}
+
+	/**
+	 * Handle global change events
+	 */
+	handleChange(e) {
+		// Handle hidden input changes for taxonomy fields
+		const taxonomyField = window.targetCheck(e, '.taxonomy.field, .post.field');
+		if (taxonomyField && e.target.type === 'hidden') {
+			const fieldId = this.getFieldId(e.target);
+			this.updateFieldFromInput(fieldId);
+			return;
+		}
+
+		// Handle modal changes
+		if (this.modal && this.modal.contains(e.target)) {
+			this.handleModalChange(e);
+		}
+	}
+
+	/**
+	 * Handle toggle button click
+	 */
+	handleToggleClick(toggle) {
+		try {
+			const fieldId = this.getFieldId(toggle);
+			const field = this.fields.get(fieldId);
+
+			if (!field) {
+				console.error('Field not found for toggle:', fieldId);
+				return;
+			}
+
+
+			this.setActiveField(fieldId, true);
+
+		} catch (error) {
+			console.error('Error handling toggle click:', error);
+			if (this.error?.log) {
+				this.error.log(error, {
+					component: 'TaxonomySelector',
+					action: 'handleToggleClick'
+				});
+			}
+		}
+	}
+
+	/**
+	 * Set the active field for modal operations
+	 */
+	setActiveField(fieldId, openModal = false) {
+		this.activeField = fieldId;
+		this.currentConfig = this.fields.get(fieldId);
+
+		this.currentSingular = this.getSingular(this.currentConfig.taxonomy);
+		this.currentPlural = this.getPlural(this.currentConfig.taxonomy);
+
+		if (openModal) {
+			this.modalInstance.handleOpen();
+		}
+
+		// Set taxonomy filter - store handles the rest
+		this.store.setFilter('taxonomy', this.currentConfig.taxonomy);
+
+		// Clear modal selection state
+		this.selectedTerms.clear();
+
+		// Copy field's current selections to modal state
+		this.currentConfig.selectedTerms.forEach(termId => {
+			const term = this.store.get(termId);
+			if (term) {
+				this.selectedTerms.set(termId, {
+					id: termId,
+					name: term.name,
+					path: term.path
+				});
+			}
+		});
+	}
+
+
+	/**
+	 * Handle clicks within modal
+	 */
+	handleModalClick(e) {
+		if (window.targetCheck(e, '.remove-item')) {
+			let selectedItem = window.targetCheck(e, '.selected-item');
+			if (selectedItem) {
+				this.removeSelectedTermFromModal(selectedItem.dataset.id);
+			}
+		} else if (window.targetCheck(e, '.back-to-parent')) {
+			this.navigateToParent();
+		} else if (window.targetCheck(e, '.toggle-children')) {
+			let termItem = e.target.closest('li');
+			this.navigateToChild(
+				parseInt(termItem.dataset.id),
+				termItem.querySelector('.term-name').textContent
+			);
+		} else if (window.targetCheck(e, '.path-level')) {
+			let pathLevel = window.targetCheck(e, '.path-level');
+			this.navigateToPath(pathLevel);
+		}
+	}
+
+	/**
+	 * Handle changes within modal (checkboxes)
+	 */
+	handleModalChange(e) {
+		if (window.targetCheck(e, this.modalID) && e.target.type === 'checkbox') {
+			e.preventDefault();
+			e.stopPropagation();
+
+			const termId = parseInt(e.target.closest('li').dataset.id);
+			const label = e.target.closest('li').querySelector('label');
+
+			if (e.target.checked) {
+				this.addSelectedTermToModal(termId, label.title, label.dataset.path);
+			} else {
+				this.removeSelectedTermFromModal(termId);
+			}
+		}
+	}
+
+	/**
+	 * Open modal for filtering (without a field)
+	 * @param {string} taxonomy - The taxonomy to filter by
+	 * @param {Function} callback - Callback when terms are selected
+	 * @param {Array} preselected - Array of term IDs already selected
+	 */
+	openForFilter(taxonomy, callback, preselected = []) {
+		// Create a temporary virtual field config
+		const virtualFieldId = `filter-${taxonomy}-${Date.now()}`;
+
+		this.fields.set(virtualFieldId, {
+			id: virtualFieldId,
+			input: null, // No input for filter mode
+			container: null,
+			taxonomy: taxonomy,
+			name: `filter_${taxonomy}`,
+			maxSelection: 0, // No limit for filters
+			canSearch: true,
+			hasAutocomplete: false,
+			autocompleteDropdown: document.querySelector('.autocomplete-dropdown')??false,
+			canCreate: false, // Disable creation for filters
+			isRequired: false,
+			selectedTerms: new Set(preselected),
+			toggle: null,
+			selectedContainer: null,
+			isFilterMode: true, // Flag for filter mode
+			filterCallback: callback // Store the callback
+		});
+
+		this.setActiveField(virtualFieldId, true);
+		this.modalInstance.handleOpen();
+	}
+
+	/**
+	 * Open modal and initialize
+	 */
+	openModal() {
+		if (!this.currentConfig) {
+			console.error('No active field set');
+			return;
+		}
+
+		// Initialize creator if available
+		if (!this.creator && this.currentConfig.canCreate && 'jvbTaxCreator' in window) {
+			this.creator = new window.jvbTaxCreator(this);
+		}
+
+		// Update modal UI
+		this.updateModalForTaxonomy();
+
+		// Load selected terms display
+		this.updateModalSelections();
+		this.updateSelectionCount();
+
+		// Clear terms list and show loading
+		window.removeChildren(this.ui.termsList);
+		this.showLoading();
+	}
+
+	/**
+	 * Update selection count display in modal
+	 */
+	updateSelectionCount() {
+		if (!this.currentConfig) return;
+
+		const count = this.selectedTerms.size;
+		const max = this.currentConfig.maxSelection;
+
+		// Update any count display elements
+		const countElement = this.modal?.querySelector('.selection-count');
+		if (countElement) {
+			if (max > 0) {
+				countElement.textContent = `${count} of ${max} selected`;
+			} else {
+				countElement.textContent = `${count} selected`;
+			}
+		}
+	}
+
+
+
+	/**
+	 * Get singular label for taxonomy
+	 */
+	getSingular(taxonomy) {
+		return jvbSettings.labels[taxonomy]?.single || taxonomy;
+	}
+
+	/**
+	 * Get plural label for taxonomy
+	 */
+	getPlural(taxonomy) {
+		return jvbSettings.labels[taxonomy]?.plural || taxonomy;
+	}
+
+	/**
+	 * Close modal and save selections
+	 */
+	closeModal() {
+		this.observer.unobserve(this.ui.sentinel);
+		window.removeChildren(this.ui.termsList);
+
+		this.notify('selected-terms', {
+			terms: this.selectedTerms,
+			taxonomy: this.currentConfig.taxonomy
+		});
+
+		if (this.currentConfig?.isFilterMode) {
+			if (this.currentConfig.filterCallback) {
+				const selectedIds = Array.from(this.selectedTerms.keys());
+				this.currentConfig.filterCallback(selectedIds, this.currentConfig.taxonomy);
+			}
+			// this.fields.delete(this.activeField);
+		} else if (this.activeField) {
+			this.saveSelectionsToField(this.activeField);
+		}
+
+		// Cleanup
+		if (this.currentConfig?.canSearch && this.searchHandler) {
+			this.ui.search.input.removeEventListener('input', this.searchHandler);
+		}
+
+		if (!this.hasAutocomplete && this.creator) {
+			delete this.creator;
+		}
+
+		// Remove: this.activeStore = null;
+		this.activeField = null;
+		this.currentConfig = null;
+	}
+
+	/**
+	 * Reset modal state
+	 */
+	resetModalState() {
+		this.disabled = false;
+
+		window.removeChildren(this.ui.termsList);
+		window.removeChildren(this.ui.selectedTerms);
+		this.ui.search.input.value = '';
+
+		// Clear navigation breadcrumbs
+		window.removeChildren(this.ui.breadcrumbs.nav);
+		this.ui.breadcrumbs.nav.appendChild(this.ui.breadcrumbs.back);
+		this.ui.breadcrumbs.back.hidden = true;
+	}
+
+	/**
+	 * Update modal content for current taxonomy
+	 */
+	updateModalForTaxonomy() {
+		if (!this.currentConfig) return;
+
+		this.ui.modal.title.textContent = `Select ${this.currentPlural}`;
+
+		if (this.ui.search.container) {
+			this.ui.search.container.style.display = this.currentConfig.canSearch ? 'block' : 'none';
+		}
+
+		if (this.ui.create.details) {
+			this.ui.create.details.style.display = this.currentConfig.canCreate ? 'block' : 'none';
+			this.ui.create.details.hidden = !this.currentConfig.canCreate;
+
+			if (this.ui.create.summary) {
+				this.ui.create.summary.textContent = `Add new ${this.currentSingular}`;
+			}
+
+			if (this.ui.create.label.name) {
+				this.ui.create.label.name.textContent = `Name this ${this.currentSingular}`;
+			}
+			if (this.ui.create.label.parent) {
+				this.ui.create.label.parent.textContent = `Nest it under`;
+			}
+
+			if (this.ui.create.parent) {
+
+			}
+		}
+
+		const openMessage = `Opened ${this.currentSingular} selection. Choose from checkboxes or search to filter results.`;
+		this.a11y?.announce(openMessage);
+	}
+
+	/**
+	 * Update modal selections display
+	 */
+	updateModalSelections() {
+		window.removeChildren(this.ui.selectedTerms);
+
+		this.selectedTerms.forEach((termData, id) => {
+			this.addTermToModalDisplay(id, termData.name, termData.path);
+		});
+
+		this.checkSelectionLimits();
+	}
+
+	/**
+	 * Add selected term to modal
+	 */
+	addSelectedTermToModal(id, name, path) {
+		this.selectedTerms.set(id, {
+			id: id,
+			name: name,
+			path: path
+		});
+
+		this.addTermToModalDisplay(id, name, path);
+		this.checkSelectionLimits();
+
+		// Check the corresponding checkbox
+		const checkbox = this.ui.termsList.querySelector(`input[value="${id}"]`);
+		if (checkbox) {
+			checkbox.checked = true;
+		}
+	}
+
+	/**
+	 * Remove selected term from modal
+	 */
+	removeSelectedTermFromModal(id) {
+		this.selectedTerms.delete(parseInt(id));
+
+		// Remove from modal display
+		const selectedItem = this.ui.selectedTerms.querySelector(`[data-id="${id}"]`);
+		if (selectedItem) {
+			selectedItem.remove();
+		}
+
+		// Uncheck the corresponding checkbox
+		const checkbox = this.ui.termsList.querySelector(`input[value="${id}"]`);
+		if (checkbox) {
+			checkbox.checked = false;
+		}
+
+		this.checkSelectionLimits();
+	}
+
+	/**
+	 * Add term to modal display
+	 */
+	addTermToModalDisplay(id, name, path) {
+		const item = window.getTemplate('selectedTerm').cloneNode(true);
+		item.dataset.id = id;
+		item.dataset.path = path;
+		item.dataset.name = name;
+		item.dataset.taxonomy = this.currentConfig.taxonomy;
+		item.querySelector('span').textContent = path;
+		item.querySelector('button').title = `Remove ${name}`;
+
+		this.ui.selectedTerms.appendChild(item);
+	}
+
+	/**
+	 * Check selection limits and disable/enable checkboxes
+	 */
+	checkSelectionLimits() {
+		if (!this.currentConfig || this.currentConfig.maxSelection === 0) {
+			return;
+		}
+
+		this.disabled = this.selectedTerms.size >= this.currentConfig.maxSelection;
+		this.setCheckboxes(this.disabled);
+	}
+
+	/**
+	 * Set checkbox disabled state
+	 */
+	setCheckboxes(disabled) {
+		this.ui.termsList.querySelectorAll('input[type="checkbox"]').forEach(checkbox => {
+			if (!checkbox.checked) {
+				checkbox.disabled = disabled;
+			}
+		});
+	}
+
+	/**
+	 * Save modal selections to field
+	 */
+	saveSelectionsToField(fieldId) {
+		const field = this.fields.get(fieldId);
+		if (!field) return;
+
+		// Clear current field selections
+		field.selectedTerms.clear();
+		window.removeChildren(field.selectedContainer);
+
+		// Add modal selections to field
+		this.selectedTerms.forEach((termData, id) => {
+			field.selectedTerms.add(id);
+			this.addTermToDisplay(fieldId, id, termData.name, termData.path);
+		});
+
+		// Update hidden input
+		const selectedIds = Array.from(field.selectedTerms);
+		field.input.value = selectedIds.join(',');
+		field.input.dispatchEvent(new Event('change', { bubbles: true }));
+	}
+
+	/**
+	 * Remove selected term from field
+	 */
+	removeSelectedTerm(fieldId, termId) {
+		const field = this.fields.get(fieldId);
+		if (!field) return;
+
+		const id = parseInt(termId);
+		field.selectedTerms.delete(id);
+
+		// Remove from display
+		const selectedItem = field.selectedContainer.querySelector(`[data-id="${id}"]`);
+		if (selectedItem) {
+			selectedItem.remove();
+		}
+
+		// Update hidden input
+		const selectedIds = Array.from(field.selectedTerms);
+		field.input.value = selectedIds.join(',');
+		field.input.dispatchEvent(new Event('change', { bubbles: true }));
+	}
+
+	/**
+	 * Add term to field display
+	 */
+	addTermToDisplay(fieldId, id, name, path) {
+		const field = this.fields.get(fieldId);
+		if (!field || field.selectedContainer.querySelector(`[data-id="${id}"]`)) {
+			return; // Already displayed
+		}
+
+		const item = window.getTemplate('selectedTerm').cloneNode(true);
+		item.dataset.id = id;
+		item.dataset.path = path;
+		item.dataset.name = name;
+		item.dataset.taxonomy = field.taxonomy;
+		item.querySelector('span').textContent = path;
+		item.querySelector('button').title = `Remove ${name}`;
+
+		field.selectedContainer.appendChild(item);
+	}
+
+	/**
+	 * Update field from hidden input value
+	 */
+	updateFieldFromInput(fieldId) {
+		const field = this.fields.get(fieldId);
+		if (!field) return;
+
+		const value = field.input.value.trim();
+		field.selectedTerms.clear();
+		window.removeChildren(field.selectedContainer);
+
+		if (value !== '') {
+			const selectedIds = value.split(',')
+				.map(id => parseInt(id.trim()))
+				.filter(id => !isNaN(id));
+
+			selectedIds.forEach(id => field.selectedTerms.add(id));
+			this.initFieldDisplay(fieldId);
+		}
+	}
+
+	/**
+	 * Handle search input
+	 */
+	handleSearch(e) {
+		const query = e.target.value.trim();
+
+		// Clear existing debounce
+		if (this.searchHandler) {
+			clearTimeout(this.searchHandler);
+		}
+
+		this.searchHandler = setTimeout(() => {
+			// Single call - auto-fetches
+			this.store.setFilters({
+				search: query,
+				page: 1,
+				parent: query ? 0 : (this.store.filters.parent || 0)
+			});
+
+			window.removeChildren(this.ui.termsList);
+		}, 300);
+	}
+
+	async handleAutocomplete(e) {
+		if (!('autocomplete' in e.target.dataset)) {
+			return;
+		}
+
+		const fieldId = this.getFieldId(e.target);
+		const field = this.fields.get(fieldId);
+
+		if (!field) return;
+
+		// Store current value immediately (fixes fast typing issue)
+		const query = e.target.value.trim();
+		field.currentAutocompleteQuery = query;
+
+		if (query.length < 2) {
+			if (field.autocompleteDropdown) {
+				field.autocompleteDropdown.hidden = true;
+			}
+			this.isAutocompleteActive = false;
+			return;
+		}
+
+		this.activeField = fieldId;
+		this.isAutocompleteActive = true;
+
+		if (field.autocompleteDropdown) {
+			field.autocompleteDropdown.hidden = false;
+		}
+
+		this.store.setFilters({
+			taxonomy: field.taxonomy,
+			search: query,
+			page: 1
+		});
+	}
+
+	cleanupAutocomplete(e) {
+		if (!('autocomplete' in e.target.dataset)) {
+			return;
+		}
+
+		const fieldId = this.getFieldId(e.target);
+		const field = this.fields.get(fieldId);
+
+		if (!field) return;
+
+		if (this.creator) {
+			delete this.creator;
+		}
+	}
+
+	showAutocompleteError(fieldId) {
+
+		const field = this.fields.get(fieldId);
+		if (!field) {
+			return;
+		}
+		if (!field.config.autocompleteDropdown) {
+			field.config.autocompleteDropdown = field.element.querySelector('.autocomplete-dropdown');
+		}
+		const dropdown = field.config.autocompleteDropdown;
+		if (dropdown) {
+			window.removeChildren(dropdown);
+			this.showEmptyState('Hmmm... something went wrong', dropdown);
+		}
+	}
+
+	showAutocompleteResults(field, terms, query) {
+		if (!field || !field.autocompleteDropdown) {
+			return;
+		}
+
+		const dropdown = field.autocompleteDropdown;
+		window.removeChildren(dropdown);
+
+		if (terms.length === 0) {
+			this.showEmptyState('No items found.', dropdown);
+		} else {
+			terms.forEach(term => {
+				const element = this.createAutocompleteTermElement(field, term);
+				if (element) {
+					dropdown.appendChild(element);
+				}
+			});
+		}
+
+		// Use stored current query instead of debounced one
+		const currentQuery = field.currentAutocompleteQuery || query;
+		if (field.canCreate && currentQuery && window.jvbTaxCreator) {
+			const createOption = this.createNewTermOption(currentQuery);
+			dropdown.appendChild(createOption);
+		}
+
+		dropdown.hidden = false;
+	}
+
+	createNewTermOption(query) {
+		const button = document.createElement('button');
+		button.type = 'button';
+		button.className = 'autocomplete-item create-term';
+		button.dataset.query = query;
+		button.innerHTML = `<strong>Create:</strong> "${query}"`;
+
+		return button;
+	}
+
+	createAutocompleteTermElement(field, term) {
+		const item = document.createElement('button');
+		item.type = 'button';
+		item.className = 'autocomplete-item';
+		item.dataset.id = term.id;
+		item.dataset.name = term.name;
+		item.dataset.path = term.path || term.name;
+		item.textContent = term.path || term.name;
+
+		item.addEventListener('click', () => {
+			// Add term to field
+			field.selectedTerms.add(parseInt(term.id));
+			this.addTermToDisplay(field.id, term.id, term.name, term.path);
+
+			// Update input
+			field.input.value = Array.from(field.selectedTerms).join(',');
+			field.input.dispatchEvent(new Event('change', { bubbles: true }));
+
+			// Clear and hide dropdown
+			field.autocompleteDropdown.hidden = true;
+			const input = field.container.querySelector('input[data-autocomplete]');
+			if (input) input.value = '';
+		});
+
+		return item;
+	}
+
+	/**
+	 * Navigate to parent term
+	 */
+	navigateToParent() {
+		// Store handles fetch automatically
+		this.store.setFilters({
+			parent: 0,
+			page: 1
+		});
+
+		window.removeChildren(this.ui.termsList);
+		this.ui.breadcrumbs.back.hidden = true;
+	}
+
+	/**
+	 * Navigate to child term
+	 */
+	navigateToChild(termId, termName) {
+		// Store handles fetch automatically
+		this.store.setFilters({
+			parent: termId,
+			page: 1
+		});
+
+		window.removeChildren(this.ui.termsList);
+		this.updateBreadcrumbs(termId, termName);
+		this.ui.breadcrumbs.back.hidden = false;
+	}
+
+	/**
+	 * Navigate to specific path level
+	 */
+	navigateToPath(pathLevel) {
+		const parentId = parseInt(pathLevel.dataset.id) || 0;
+
+		// Store handles fetch automatically
+		this.store.setFilters({
+			parent: parentId,
+			page: 1
+		});
+
+		window.removeChildren(this.ui.termsList);
+		this.ui.breadcrumbs.back.hidden = parentId === 0;
+	}
+
+	/**
+	 * Load more terms (pagination)
+	 */
+	loadMoreTerms() {
+		const currentPage = this.store.filters.page || 1;
+		this.store.setFilter('page', currentPage + 1);
+	}
+
+	/**
+	 * Render terms list
+	 */
+	renderTerms(terms = null, append = false, showPath = false) {
+		// If no terms provided, get from store
+		if (!terms) {
+			terms = this.store.getFiltered();
+		}
+
+		if (!append) {
+			window.removeChildren(this.ui.termsList);
+		}
+
+		if (terms.length === 0) {
+			if (!append) {
+				this.showEmptyState();
+			}
+			return;
+		}
+
+		const currentParent = this.store.filters.parent || 0;
+		this.ui.breadcrumbs.back.hidden = currentParent === 0;
+
+		const fragment = document.createDocumentFragment();
+		terms.forEach(term => {
+			const element = this.createTermElement({
+				id: parseInt(term.id),
+				name: term.name,
+				hasChildren: term.hasChildren,
+				path: term.path || null,
+				show: showPath
+			});
+
+			if (element) {
+				fragment.appendChild(element);
+			}
+		});
+
+		this.ui.termsList.appendChild(fragment);
+	}
+
+	/**
+	 * Create individual term element
+	 */
+	createTermElement(termData) {
+		if (!termData || !termData.name) return null;
+
+		const listItem = window.getTemplate('termListItem').cloneNode(true);
+		listItem.dataset.id = termData.id;
+
+		const isSelected = this.selectedTerms.has(termData.id);
+		const checkbox = listItem.querySelector('input');
+		const label = listItem.querySelector('label');
+		const nameSpan = listItem.querySelector('span, .term-name');
+
+		if (checkbox && label && nameSpan) {
+			checkbox.id = `${this.currentConfig.container.id}${termData.id}`;
+			checkbox.name = `${this.currentConfig.container.id}${this.currentConfig.taxonomy}-select`;
+			checkbox.value = termData.id;
+			checkbox.disabled = !isSelected && this.disabled;
+			checkbox.checked = isSelected;
+
+			label.htmlFor = checkbox.id;
+			label.title = termData.path || termData.name;
+			label.dataset.path = termData.path;
+
+			nameSpan.textContent = termData.show ? termData.path : termData.name;
+		}
+
+		if (termData.hasChildren) {
+			const childrenToggle = window.getTemplate ?
+				window.getTemplate('termChildrenToggle') :
+				this.createChildrenToggle();
+
+			if (childrenToggle) {
+				childrenToggle.ariaLabel = `View sub-terms of ${termData.name}`;
+				listItem.appendChild(childrenToggle);
+			}
+		}
+
+		return listItem;
+	}
+
+	/**
+	 * Create children toggle button
+	 */
+	createChildrenToggle() {
+		const button = document.createElement('button');
+		button.type = 'button';
+		button.className = 'toggle-children';
+		button.innerHTML = '→';
+		return button;
+	}
+
+	/**
+	 * Update breadcrumb navigation
+	 */
+	updateBreadcrumbs(termId, termName) {
+		// This is a simplified version - you'd want to maintain a proper breadcrumb trail
+		const breadcrumb = window.getTemplate('termBreadcrumb').cloneNode(true);
+		breadcrumb.dataset.id = termId;
+		breadcrumb.textContent = termName;
+		breadcrumb.title = termName;
+
+		// Remove any existing breadcrumbs after this level
+		const existingCrumb = this.ui.breadcrumbs.nav.querySelector(`[data-id="${termId}"]`);
+		if (existingCrumb) {
+			// Remove all breadcrumbs after this one
+			while (existingCrumb.nextElementSibling) {
+				existingCrumb.nextElementSibling.remove();
+			}
+		} else {
+			this.ui.breadcrumbs.nav.appendChild(breadcrumb);
+		}
+	}
+
+	/**
+	 * Show loading state
+	 */
+	showLoading() {
+		this.ui.loading.loading.hidden = false;
+		this.modal.classList.add('loading');
+
+		const searchQuery = this.store?.filters?.search || '';
+		const currentParent = this.store?.filters?.parent || 0;
+
+		let message = searchQuery !== '' ?
+			`searching for "${searchQuery}" items` :
+			currentParent === 0 ?
+				'loading items' :
+				`loading child items`;
+
+		if (window.typeLoop) {
+			this.stopTyping = window.typeLoop(this.ui.loading.text, message);
+		} else {
+			this.ui.loading.text.textContent = message;
+		}
+	}
+
+	/**
+	 * Hide loading state
+	 */
+	hideLoading() {
+		this.ui.loading.loading.hidden = true;
+		this.modal.classList.remove('loading');
+
+		if (this.stopTyping) {
+			this.stopTyping();
+		}
+	}
+
+	/**
+	 * Show empty state message
+	 */
+	showEmptyState(message = 'No items found.', container = null) {
+		if (!container) {
+			container = this.ui.termsList;
+		}
+		const emptyElement = window.getTemplate('noResults').cloneNode(true);
+
+		if (message && emptyElement.querySelector('span')) {
+			emptyElement.querySelector('span').textContent = message;
+		}
+
+		container.appendChild(emptyElement);
+	}
+
+	/**
+	 * Get field ID from any element within the field
+	 */
+	getFieldId(element) {
+		if (element.dataset.fieldId) {
+			return element.dataset.fieldId;
+		}
+
+		const fieldContainer = element.closest('[data-field-id]');
+		if (fieldContainer) {
+			return fieldContainer.dataset.fieldId;
+		}
+
+		return null;
+	}
+	/********************************************
+	BATCH FETCH: fetches first page for all taxonomies in one call
+	 ********************************************/
+	async batchFetchTaxonomies() {
+		if (this.taxonomiesToFetch.size === 0) return;
+
+		const taxonomies = Array.from(this.taxonomiesToFetch);
+		this.taxonomiesToFetch.clear();
+
+		// Single fetch - the data-loaded event will handle cache splitting
+		this.store.setFilters({
+			taxonomy: taxonomies.join(','),
+			page: 1,
+			search: '',
+			parent: 0
+		});
+	}
+	handleBatchDataLoaded(taxonomyString, data) {
+		const taxonomies = taxonomyString.split(',').map(t => t.trim());
+		const storeInstance = this.store.getStore(); // Access actual store instance
+
+		taxonomies.forEach(taxonomy => {
+			const filters = {
+				taxonomy: taxonomy,
+				page: 1,
+				search: '',
+				parent: 0
+			};
+
+			// Use the internal generateCacheKey method via store instance
+			const cacheKey = this.generateCacheKeyForFilters(filters);
+
+			// Filter items for this specific taxonomy
+			const items = Array.from(this.store.data.values())
+				.filter(item => item.taxonomy === taxonomy)
+				.map(item => item.id);
+
+			const cacheEntry = {
+				key: cacheKey,
+				items: items,
+				timestamp: Date.now(),
+				endpoint: storeInstance.config.endpoint,
+				filters: filters
+			};
+
+			// Set in both memory and IndexedDB cache
+			storeInstance.cache.set(cacheKey, cacheEntry);
+
+			// Persist to IndexedDB (if available)
+			if (storeInstance.db?.objectStoreNames.contains('cache')) {
+				const tx = storeInstance.db.transaction(['cache'], 'readwrite');
+				const objectStore = tx.objectStore('cache');
+				objectStore.put(cacheEntry);
+			}
+
+			// Update button states for this taxonomy
+			this.updateFieldsForTaxonomy(taxonomy);
+		});
+
+		// Initialize field displays
+		this.fields.forEach((config, fieldId) => {
+			if (config.selectedTerms.size > 0) {
+				this.initFieldDisplay(fieldId);
+			}
+		});
+	}
+
+	/**
+	 * Generate cache key for given filters (matching DataStore's internal logic)
+	 */
+	generateCacheKeyForFilters(filters) {
+		const normalized = Object.keys(filters)
+			.sort()
+			.reduce((acc, key) => {
+				acc[key] = filters[key];
+				return acc;
+			}, {});
+
+		return JSON.stringify(normalized);
+	}
+
+	/**
+	 * Preload taxonomy data on hover
+	 */
+	async preloadTaxonomy(taxonomy) {
+		// Trigger fetch for this taxonomy
+		this.store.setFilters({
+			taxonomy: taxonomy,
+			page: 1,
+			search: '',
+			parent: 0
+		});
+	}
+	/*****************************************
+	SUBSCRIBERS
+	 *****************************************/
+
+	subscribe(callback) {
+		this.subscribers.add(callback);
+		return () => this.subscribers.delete(callback);
+	}
+
+	notify(event, data = {}) {
+		this.subscribers.forEach( callback => {
+			try {
+				callback(event, data);
+			} catch (error) {
+				console.error('Subscriber error:', error);
+			}
+		});
+	}
+
+	/**
+	 * Clean up
+	 */
+	destroy() {
+		// Remove event listeners
+		document.removeEventListener('click', this.handleClick);
+		document.removeEventListener('change', this.handleChange);
+
+		// Clear intervals and cleanup
+		this.observer?.disconnect();
+
+		// Destroy all stores
+		this.store.destroy();
+
+		this.subscribers.clear();
+		// Clear all maps
+		this.fields.clear();
+		this.selectedTerms.clear();
+	}
+}
+
+/**
+ * Initialize singleton
+ */
+document.addEventListener('DOMContentLoaded', function() {
+	window.auth.subscribe((event) => {
+		if (event === 'auth-loaded') {
+			window.jvbSelector = new TaxonomySelectorOld();
+		}
+	});
+
+});
diff --git a/assets/js/concise/TempForm.js b/assets/js/concise/TempForm.js
deleted file mode 100644
index f17a757..0000000
--- a/assets/js/concise/TempForm.js
+++ /dev/null
@@ -1,385 +0,0 @@
-/**
- * Simplified autosave approach for FormController
- * Leverages QueueManager for all server operations
- */
-
-// In FormController class - replace the autosave-related methods with:
-
-class FormController {
-	// ... existing constructor and init code ...
-
-	/**
-	 * Initialize form tracking for autosave
-	 */
-	registerForm(formElement, options = {}) {
-		const formId = formElement.dataset.formId || `form_${Date.now()}`;
-		formElement.dataset.formId = formId;
-
-		const formConfig = {
-			element: formElement,
-			id: formId,
-			contentId: formElement.dataset.contentId || null, // For existing content
-			contentType: formElement.dataset.contentType || null,
-			options: {
-				autoSave: true,
-				saveDelay: this.autoSaveDefaults.delay,
-				endpoint: formElement.dataset.save || formElement.action,
-				...options
-			},
-			lastSnapshot: {}, // Last saved state
-			isDirty: false
-		};
-
-		// Take initial snapshot
-		formConfig.lastSnapshot = this.collectFormData(formElement);
-
-		// Initialize special fields
-		this.initializeFormFields(formElement, formConfig);
-
-		// Store form config
-		this.forms.set(formId, formConfig);
-
-		return formConfig;
-	}
-
-	/**
-	 * Simplified change handler
-	 */
-	handleChange(event) {
-		const target = event.target;
-		const form = target.form || target.closest('form');
-		if (!form) return;
-
-		const formConfig = this.forms?.get(form.dataset.formId);
-		if (!formConfig) return;
-
-		// Check conditional fields (existing functionality)
-		const dependencies = formConfig.dependencies?.get(target.name);
-		if (dependencies) {
-			dependencies.forEach(dep => {
-				this.checkFieldDependency(form, dep.field, target.name, dep.requiredValue, dep.operator);
-			});
-		}
-
-		// Schedule autosave if enabled
-		if (formConfig.options.autoSave && !form.dataset.noautosave) {
-			const delay = this.getDelayForField(target);
-			this.scheduleSave(formConfig, delay);
-		}
-	}
-
-	/**
-	 * Get appropriate delay based on field type and context
-	 */
-	getDelayForField(field) {
-		// Text fields get longer delay for typing
-		if (field.type === 'text' || field.type === 'textarea') {
-			return this.autoSaveDefaults.typingDelay;
-		}
-
-		// Checkboxes, radios, selects get shorter delay
-		if (['checkbox', 'radio', 'select-one', 'select-multiple'].includes(field.type)) {
-			return 1000;
-		}
-
-		// Default delay
-		return this.autoSaveDefaults.delay;
-	}
-
-	/**
-	 * Simplified scheduleSave - just debounces the queue addition
-	 */
-	scheduleSave(formConfig, delay = this.autoSaveDefaults.delay) {
-		const saveKey = `autosave_${formConfig.id}`;
-
-		this.debouncer.schedule(
-			saveKey,
-			() => this.queueAutosave(formConfig),
-			delay
-		);
-	}
-
-	/**
-	 * Queue the autosave operation
-	 */
-	async queueAutosave(formConfig) {
-		const currentData = this.collectFormData(formConfig.element);
-		const changes = this.getChangedFields(formConfig.lastSnapshot, currentData);
-
-		// No changes? Don't save
-		if (Object.keys(changes).length === 0) return;
-
-		// For bulk edit forms, handle specially
-		if (formConfig.element.classList.contains('bulk-edit')) {
-			this.queueBulkSave(formConfig, changes);
-			return;
-		}
-
-		// Build the queue operation
-		const operation = {
-			endpoint: formConfig.options.endpoint || '/wp-json/jvb/v1/autosave',
-			method: formConfig.contentId ? 'PUT' : 'POST',
-			data: {
-				form_id: formConfig.id,
-				content_id: formConfig.contentId,
-				content_type: formConfig.contentType,
-				changes: changes,
-				full_data: currentData
-			},
-			title: `Autosaving ${formConfig.contentType || 'form'}`,
-			popup: null, // No popup for autosave
-			headers: {
-				'X-Autosave': 'true'
-			},
-			source: 'form',
-			formId: formConfig.id,
-			// For optimistic updates
-			localUpdate: formConfig.contentId ? {
-				action: 'update',
-				id: formConfig.contentId,
-				changes: changes
-			} : null,
-			dataStore: this.store
-		};
-
-		// Add to queue
-		const queueItem = await this.queue.addToQueue(operation);
-
-		if (queueItem) {
-			// Update snapshot to prevent re-saving same changes
-			formConfig.lastSnapshot = currentData;
-			formConfig.isDirty = false;
-
-			// Visual feedback
-			this.showFormStatus(formConfig.element, 'queued');
-
-			// Track the operation
-			this.trackOperation(formConfig, queueItem.id);
-		}
-	}
-
-	/**
-	 * Handle bulk edit forms specially
-	 */
-	queueBulkSave(formConfig, changes) {
-		const selectedItems = formConfig.element.querySelectorAll('.bulk-item input:checked');
-		const itemIds = Array.from(selectedItems).map(cb => cb.value);
-
-		if (itemIds.length === 0) return;
-
-		const operation = {
-			endpoint: formConfig.options.endpoint || '/wp-json/jvb/v1/bulk-update',
-			method: 'PUT',
-			data: {
-				content_type: formConfig.contentType,
-				item_ids: itemIds,
-				changes: changes
-			},
-			title: `Updating ${itemIds.length} items`,
-			popup: `${itemIds.length} items queued for update`,
-			headers: {
-				'X-Bulk-Operation': 'true'
-			},
-			source: 'form',
-			formId: formConfig.id,
-			// Optimistic update for each item
-			localUpdate: {
-				action: 'bulk-update',
-				ids: itemIds,
-				changes: changes
-			},
-			dataStore: this.store
-		};
-
-		// Add to queue
-		const queueItem = this.queue.addToQueue(operation);
-
-		if (queueItem) {
-			formConfig.lastSnapshot = this.collectFormData(formConfig.element);
-			formConfig.isDirty = false;
-			this.showFormStatus(formConfig.element, 'queued');
-		}
-	}
-
-	/**
-	 * Track operations for forms
-	 */
-	trackOperation(formConfig, operationId) {
-		if (!formConfig.operations) {
-			formConfig.operations = new Set();
-		}
-		formConfig.operations.add(operationId);
-
-		// Subscribe to queue updates for this operation
-		const unsubscribe = this.queue.subscribe((event, data) => {
-			if (data?.id !== operationId) return;
-
-			switch(event) {
-				case 'operation-status':
-					this.updateFormStatus(formConfig, data.status);
-					break;
-
-				case 'operation-completed':
-					this.handleSaveSuccess(formConfig, data);
-					formConfig.operations.delete(operationId);
-					unsubscribe();
-					break;
-
-				case 'operation-failed':
-					this.handleSaveFailure(formConfig, data);
-					formConfig.operations.delete(operationId);
-					unsubscribe();
-					break;
-			}
-		});
-	}
-
-	/**
-	 * Update form status based on queue status
-	 */
-	updateFormStatus(formConfig, status) {
-		const statusMap = {
-			'queued': 'queued',
-			'uploading': 'saving',
-			'processing': 'saving',
-			'pending': 'saving',
-			'completed': 'saved',
-			'failed': 'error',
-			'failed_permanent': 'error'
-		};
-
-		this.showFormStatus(formConfig.element, statusMap[status] || status);
-	}
-
-	/**
-	 * Handle successful save from queue
-	 */
-	handleSaveSuccess(formConfig, data) {
-		// Update content ID if this was a create operation
-		if (!formConfig.contentId && data.result?.id) {
-			formConfig.contentId = data.result.id;
-			formConfig.element.dataset.contentId = data.result.id;
-		}
-
-		// Reset dirty flag
-		formConfig.isDirty = false;
-
-		// Show success
-		this.showFormStatus(formConfig.element, 'saved');
-
-		// Notify subscribers
-		this.notify('form-saved', {
-			formId: formConfig.id,
-			contentId: formConfig.contentId,
-			data: data
-		});
-	}
-
-	/**
-	 * Handle save failure from queue
-	 */
-	handleSaveFailure(formConfig, data) {
-		// Mark as dirty so it will retry
-		formConfig.isDirty = true;
-
-		// Show error
-		this.showFormStatus(formConfig.element, 'error');
-
-		// Notify subscribers
-		this.notify('form-save-failed', {
-			formId: formConfig.id,
-			error: data.lastError
-		});
-	}
-
-	/**
-	 * Manual save (for submit button)
-	 */
-	async handleSubmit(event) {
-		const form = event.target;
-		if (!form.dataset.formId) return;
-
-		event.preventDefault();
-
-		const formConfig = this.forms.get(form.dataset.formId);
-		if (!formConfig) return;
-
-		// Force immediate save
-		this.debouncer.cancel(`autosave_${formConfig.id}`);
-
-		// Add to queue with higher priority
-		const currentData = this.collectFormData(form);
-
-		const operation = {
-			endpoint: formConfig.options.endpoint,
-			method: formConfig.contentId ? 'PUT' : 'POST',
-			data: {
-				content_id: formConfig.contentId,
-				content_type: formConfig.contentType,
-				...currentData
-			},
-			title: `Saving ${formConfig.contentType || 'form'}`,
-			popup: 'Saved successfully',
-			priority: 'high', // Process before autosaves
-			source: 'form',
-			formId: formConfig.id,
-			localUpdate: formConfig.contentId ? {
-				action: 'update',
-				id: formConfig.contentId,
-				changes: currentData
-			} : {
-				action: 'create',
-				data: currentData
-			},
-			dataStore: this.store
-		};
-
-		const queueItem = await this.queue.addToQueue(operation);
-
-		if (queueItem) {
-			formConfig.lastSnapshot = currentData;
-			this.trackOperation(formConfig, queueItem.id);
-			this.showFormStatus(form, 'saving');
-		}
-	}
-
-	/**
-	 * Check if form has unsaved changes
-	 */
-	hasUnsavedChanges(formId) {
-		const formConfig = this.forms.get(formId);
-		if (!formConfig) return false;
-
-		// Check if there are pending operations
-		if (formConfig.operations?.size > 0) return true;
-
-		// Check if current data differs from snapshot
-		const currentData = this.collectFormData(formConfig.element);
-		const changes = this.getChangedFields(formConfig.lastSnapshot, currentData);
-
-		return Object.keys(changes).length > 0;
-	}
-
-	/**
-	 * Cleanup when form is closed/destroyed
-	 */
-	cleanupForm(formId) {
-		const formConfig = this.forms.get(formId);
-		if (!formConfig) return;
-
-		// Cancel any pending debounced saves
-		this.debouncer.cancel(`autosave_${formId}`);
-
-		// Check for unsaved changes
-		if (this.hasUnsavedChanges(formId)) {
-			// Could show a warning or auto-save
-			this.queueAutosave(formConfig);
-		}
-
-		// Clean up special fields
-		this.cleanupSpecialFields();
-
-		// Remove form config
-		this.forms.delete(formId);
-	}
-}
diff --git a/assets/js/concise/TempQueue.js b/assets/js/concise/TempQueue.js
deleted file mode 100644
index e69de29..0000000
--- a/assets/js/concise/TempQueue.js
+++ /dev/null
diff --git a/assets/js/concise/TempUploads.js b/assets/js/concise/TempUploads.js
deleted file mode 100644
index e69de29..0000000
--- a/assets/js/concise/TempUploads.js
+++ /dev/null
diff --git a/assets/js/concise/UploadManagerC.js b/assets/js/concise/UploadManagerC.js
deleted file mode 100644
index 4e755ea..0000000
--- a/assets/js/concise/UploadManagerC.js
+++ /dev/null
@@ -1,2278 +0,0 @@
-/**
- * UploadManager - Refactored with simplified store architecture
- *
- * Architecture:
- * - uploadStore: Individual uploads with blob data, keyed by uploadId
- * - groupStore: Group metadata + upload references, keyed by groupId
- * - Runtime Maps: DOM references only (fieldElements, uploadElements, groupElements)
- *
- * Flow: File → Process → Store → Queue → Server → Clear stores
- * Recovery: Check stores on load → Show notification → Restore to DOM
- */
-class UploadManager {
-	constructor() {
-		this.queue = window.jvbQueue;
-		this.a11y = window.jvbA11y;
-		this.error = window.jvbError;
-
-		// Store initialization flags
-		this.storesReady = false;
-		this.hasCheckedForRecovery = false;
-
-		// Register stores
-		const { uploads, groups } = window.jvbStore.register(
-			'uploads',
-			[
-				{
-					storeName: 'uploads',
-					keyPath: 'id',
-					storeBlobs: true,
-					indexes: [
-						{ name: 'fieldId', keyPath: 'fieldId' },
-						{ name: 'status', keyPath: 'status' },
-						{ name: 'groupId', keyPath: 'groupId' },
-						{ name: 'pageUrl', keyPath: 'pageUrl' }
-					],
-					TTL: 7 * 24 * 60 * 60 * 1000, // 1 week
-					delayFetch: true
-				},
-				{
-					storeName: 'groups',
-					keyPath: 'id',
-					indexes: [
-						{ name: 'fieldId', keyPath: 'fieldId' },
-						{ name: 'pageUrl', keyPath: 'pageUrl' }
-					],
-					TTL: 7 * 24 * 60 * 60 * 1000,
-					delayFetch: true
-				}
-			]
-		);
-
-		this.uploadStore = uploads;
-		this.groupStore = groups;
-
-		// Subscribe to store events
-		this.uploadStore.subscribe(this.handleStoreEvent.bind(this, 'uploads'));
-		this.groupStore.subscribe(this.handleStoreEvent.bind(this, 'groups'));
-
-		// RUNTIME DATA - DOM references only, not persisted
-		this.fieldElements = new Map();   // fieldId → { element, ui, config }
-		this.uploadElements = new Map();  // uploadId → { element, preview, location }
-		this.groupElements = new Map();   // groupId → { element, grid, fieldId }
-
-		// Selection state
-		this.selected = new Map();		// fieldId -> { }
-		this.selectionHandlers = new Map();
-		this.sortableInstances = new Map();
-
-		// Preview URL tracking for cleanup
-		this.previewUrls = new Set();
-
-		// Worker for image processing
-		this.worker = this.createWorkerConfig();
-
-		// Notification subscribers
-		this.subscribers = new Set();
-
-		// Selectors
-		this.selectors = {
-			field: {
-				field: '[data-upload-field]',
-				input: 'input[type="file"]',
-				dropZone: '.file-upload-container',
-				preview: '.item-grid.preview',
-				progress: '.image-progress'
-			},
-			groups: {
-				container: '.upload-group',
-				grid: '.item-grid.group',
-				header: '.group-header',
-				selectAll: '[name="select-all-group"]',
-				actions: '.group-actions',
-				count: '.selection-controls .info'
-			},
-			items: {
-				item: '[data-upload-id]',
-				checkbox: '[name*="select-item"]',
-				featured: '[name="featured"]',
-				details: 'details'
-			}
-		};
-
-		this.statusMapping = {
-			'received': 'Image Received',
-			'processing': 'Processing Image...',
-			'queued': 'Waiting to upload...',
-			'uploading': 'Uploading to Server',
-			'pending': 'Sent to server, awaiting processing.',
-			'server_processing': 'Processing on server...',
-			'completed': 'Upload complete!',
-			'failed': 'Upload failed (will retry)',
-			'failed_permanent': 'Upload failed permanently'
-		};
-
-		this.init();
-	}
-
-	/*******************************************************************************
-	 * INITIALIZATION
-	 *******************************************************************************/
-
-	async init() {
-		this.initListeners();
-		this.initQueueSubscription();
-
-		window.addEventListener('beforeunload', () => this.cleanupAllPreviewUrls());
-	}
-
-	createWorkerConfig() {
-		return {
-			worker: null,
-			tasks: new Map(),
-			restart: { count: 0, max: 3 },
-			settings: {
-				timeout: 10000,
-				maxConcurrent: 3,
-				restartAfterTimeout: true
-			}
-		};
-	}
-
-	initQueueSubscription() {
-		this.queue.subscribe((event, operation) => {
-			if (!['uploads', 'uploads/meta', 'uploads/groups'].includes(operation.endpoint)) {
-				return;
-			}
-
-			const fieldId = operation.data instanceof FormData
-				? operation.data.get('fieldId')
-				: operation.data?.fieldId;
-
-			switch (event) {
-				case 'cancel-operation':
-					if (fieldId) this.handleOperationCancelled(fieldId);
-					break;
-				case 'operation-status':
-					if (fieldId) this.updateFieldStatus(fieldId, operation.status);
-					break;
-				case 'operation-complete':
-					this.handleOperationComplete(operation, fieldId);
-					break;
-				case 'operation-failed':
-				case 'operation-failed-permanent':
-					this.handleOperationFailed(operation, fieldId);
-					break;
-			}
-		});
-	}
-
-	/*******************************************************************************
-	 * STORE EVENT HANDLING & RECOVERY
-	 *******************************************************************************/
-
-	handleStoreEvent(storeName, event, data) {
-		if (event === 'data-loaded') {
-			this.checkStoresReady();
-		}
-	}
-
-	checkStoresReady() {
-		// Both stores need to be ready before checking for recovery
-		const uploadsReady = this.uploadStore.getStore()._initialized;
-		const groupsReady = this.groupStore.getStore()._initialized;
-
-		if (uploadsReady && groupsReady && !this.hasCheckedForRecovery) {
-			this.hasCheckedForRecovery = true;
-			this.storesReady = true;
-			this.checkForRecoverableUploads();
-		}
-	}
-
-	async checkForRecoverableUploads() {
-		const allUploads = this.uploadStore.getAll();
-
-		// Find uploads that weren't completed and don't have an active operation
-		const recoverableUploads = allUploads.filter(upload =>
-			!upload.operationId &&
-			['processed', 'processing', 'queued'].includes(upload.status)
-		);
-
-		if (recoverableUploads.length === 0) return;
-
-		// Group by fieldId for display
-		const byField = this.groupUploadsByField(recoverableUploads);
-		await this.showRecoveryNotification(byField);
-	}
-
-	groupUploadsByField(uploads) {
-		const byField = new Map();
-
-		uploads.forEach(upload => {
-			if (!byField.has(upload.fieldId)) {
-				byField.set(upload.fieldId, {
-					fieldId: upload.fieldId,
-					pageUrl: upload.pageUrl,
-					uploads: [],
-					groups: new Map()
-				});
-			}
-
-			const fieldData = byField.get(upload.fieldId);
-			fieldData.uploads.push(upload);
-
-			// Track groups
-			if (upload.groupId) {
-				const group = this.groupStore.get(upload.groupId);
-				if (group) {
-					fieldData.groups.set(upload.groupId, group);
-				}
-			}
-		});
-
-		return byField;
-	}
-
-	/*******************************************************************************
-	 * FIELD MANAGEMENT - Runtime only, no persistence
-	 *******************************************************************************/
-
-	scanFields(container, autoUpload = false) {
-		const fields = container.querySelectorAll(this.selectors.field.field);
-		fields.forEach(uploader => this.registerUploader(uploader, autoUpload));
-	}
-
-	registerUploader(uploader, autoUpload = false) {
-		const fieldId = this.determineFieldId(uploader);
-		const config = this.extractFieldConfig(uploader, autoUpload);
-		const ui = this.buildFieldUI(uploader);
-
-		// Store DOM references only - not persisted
-		this.fieldElements.set(fieldId, { element: uploader, ui, config });
-
-		uploader.dataset.uploader = fieldId;
-		this.addFieldSelectionHandler(fieldId);
-
-		if (config.type !== 'single') {
-			this.initSortable(fieldId);
-		}
-
-		return fieldId;
-	}
-
-	extractFieldConfig(fieldElement, autoUpload) {
-		return {
-			autoUpload,
-			destination: fieldElement.dataset.destination || 'meta',
-			content: fieldElement.dataset.content || null,
-			mode: fieldElement.dataset.mode || 'direct',
-			type: fieldElement.dataset.type || 'single',
-			name: fieldElement.dataset.field,
-			itemID: fieldElement.dataset.itemId || 0,
-			maxFiles: parseInt(fieldElement.dataset.maxFiles) || 999,
-			subtype: fieldElement.dataset.subtype || 'image'
-		};
-	}
-
-	buildFieldUI(fieldElement) {
-		const UI = {
-			field: fieldElement,
-			input: fieldElement.querySelector(this.selectors.field.input),
-			dropZone: fieldElement.querySelector(this.selectors.field.dropZone),
-			preview: fieldElement.querySelector(this.selectors.field.preview),
-			progress: {
-				container: fieldElement.querySelector(this.selectors.field.progress),
-				bar: fieldElement.querySelector('.bar'),
-				fill: fieldElement.querySelector('.fill'),
-				details: fieldElement.querySelector('.details'),
-				text: fieldElement.querySelector('.details .text'),
-				count: fieldElement.querySelector('.details .count')
-			}
-		};
-
-		const display = fieldElement.querySelector('.group-display');
-		if (display) {
-			UI.groups = {
-				display,
-				container: fieldElement.querySelector('.item-grid.groups'),
-				empty: fieldElement.querySelector('.empty-group'),
-				groups: new Map()
-			};
-		}
-
-		return UI;
-	}
-
-	/**
-	 * Get uploads for a field - derived from store, not cached
-	 */
-	getFieldUploads(fieldId) {
-		return this.uploadStore.getAll().filter(u => u.fieldId === fieldId);
-	}
-
-	/**
-	 * Get groups for a field - derived from store
-	 */
-	getFieldGroups(fieldId) {
-		return this.groupStore.getAll().filter(g => g.fieldId === fieldId);
-	}
-
-	/**
-	 * Get upload count for a field
-	 */
-	getFieldUploadCount(fieldId) {
-		return this.getFieldUploads(fieldId).length;
-	}
-
-	/*******************************************************************************
-	 * FILE PROCESSING
-	 *******************************************************************************/
-
-	async processFiles(fieldId, files) {
-		const fieldEl = this.fieldElements.get(fieldId);
-		if (!fieldEl) return;
-
-		const { config, ui } = fieldEl;
-
-		// Show group display, hide upload zone
-		if (ui.dropZone) ui.dropZone.hidden = true;
-		if (ui.groups?.display) ui.groups.display.hidden = false;
-
-		const totalFiles = files.length;
-		let processedCount = 0;
-
-		this.updateFieldProgress(fieldId, 0, totalFiles, 'Processing files...');
-
-		const processPromises = Array.from(files).map(async (file) => {
-			try {
-				const uploadId = this.generateId('upload');
-
-				// Create upload data
-				const uploadData = {
-					id: uploadId,
-					fieldId,
-					pageUrl: window.location.href,
-					status: 'processing',
-					groupId: null,
-					attachmentId: null,
-					meta: {
-						originalName: file.name,
-						size: file.size,
-						type: file.type
-					}
-				};
-
-				// Save initial state
-				await this.uploadStore.save(uploadData);
-
-				// Process file
-				const preview = this.createPreviewUrl(file);
-				const processedFile = file.type.startsWith('image/')
-					? await this.processImage(file, uploadId)
-					: file;
-
-				// Show progress
-				this.showUploadProgress(uploadId, true);
-				this.updateUploadItemProgress(uploadId, 50, 'processing');
-
-				// Store blob data
-				await this.saveBlobData(uploadId, processedFile || file);
-
-				// Create DOM element
-				const subtype = this.getSubtypeFromMime(file.type);
-				const element = this.createUploadElement({
-					id: uploadId,
-					preview,
-					meta: uploadData.meta,
-					subtype
-				}, config.destination === 'post_group');
-
-				// Add to preview grid
-				if (ui.preview) {
-					ui.preview.appendChild(element);
-					this.uploadElements.set(uploadId, { element, preview, location: ui.preview });
-				}
-
-				// Update status
-				await this.updateUploadStatus(uploadId, 'processed');
-
-				// Update progress
-				processedCount++;
-				this.updateFieldProgress(fieldId, processedCount, totalFiles, 'Processing files...');
-				this.updateUploadItemProgress(uploadId, 100, 'processed');
-
-				setTimeout(() => this.showUploadProgress(uploadId, false), 1000);
-
-				return uploadId;
-
-			} catch (error) {
-				console.error('Error processing file:', file.name, error);
-				processedCount++;
-				this.updateFieldProgress(fieldId, processedCount, totalFiles, 'Processing files...');
-				return null;
-			}
-		});
-
-		await Promise.all(processPromises);
-
-		this.updateFieldState(fieldId);
-		this.refreshSortable(fieldId);
-
-		// Queue for upload if auto-upload enabled
-		if (config.autoUpload && config.destination !== 'post_group') {
-			await this.queueUpload(fieldId);
-			this.maybeLockUploads(fieldId);
-		}
-	}
-
-	/*******************************************************************************
-	 * IMAGE PROCESSING (unchanged logic, just cleaner structure)
-	 *******************************************************************************/
-
-	async processImage(file, uploadId) {
-		const timeout = this.worker.settings.timeout;
-
-		return new Promise((resolve, reject) => {
-			let timeoutId;
-			let completed = false;
-
-			timeoutId = setTimeout(() => {
-				if (!completed) {
-					completed = true;
-					this.worker.tasks.delete(uploadId);
-					if (this.worker.settings.restartAfterTimeout) {
-						this.restartWorker();
-					}
-					reject(new Error(`Processing timeout for ${file.name}`));
-				}
-			}, timeout);
-
-			this.worker.tasks.set(uploadId, { file, timeoutId });
-
-			this.handleImageProcess(file, uploadId)
-				.then(result => {
-					if (!completed) {
-						completed = true;
-						clearTimeout(timeoutId);
-						this.worker.tasks.delete(uploadId);
-						resolve(result);
-					}
-				})
-				.catch(error => {
-					if (!completed) {
-						completed = true;
-						clearTimeout(timeoutId);
-						this.worker.tasks.delete(uploadId);
-						reject(error);
-					}
-				});
-		});
-	}
-
-	async handleImageProcess(file, uploadId) {
-		if (!file.type.startsWith('image/')) return file;
-
-		const maxDimension = this.getMaxDimension();
-		const quality = 0.95;
-
-		if (this.shouldUseWorker(file)) {
-			try {
-				if (!this.worker.worker) this.initWorker();
-				if (this.worker.worker) {
-					return await this.processWithWorker(file, uploadId, maxDimension, quality);
-				}
-			} catch (error) {
-				console.warn('Worker failed, using main thread:', error);
-			}
-		}
-
-		return this.processOnMainThread(file, maxDimension, quality);
-	}
-
-	async processOnMainThread(file, maxDimension, quality) {
-		return new Promise((resolve, reject) => {
-			const img = new Image();
-			const canvas = document.createElement('canvas');
-			const ctx = canvas.getContext('2d');
-			let objectUrl = null;
-
-			const cleanup = () => {
-				img.onload = img.onerror = null;
-				if (objectUrl) {
-					URL.revokeObjectURL(objectUrl);
-					objectUrl = null;
-				}
-				canvas.width = canvas.height = 1;
-				ctx.clearRect(0, 0, 1, 1);
-			};
-
-			img.onload = () => {
-				try {
-					const { width, height } = this.calculateDimensions(img, maxDimension);
-					canvas.width = width;
-					canvas.height = height;
-
-					ctx.imageSmoothingEnabled = true;
-					ctx.imageSmoothingQuality = 'high';
-					ctx.drawImage(img, 0, 0, width, height);
-
-					const outputFormat = this.getOptimalFormat(file);
-					const outputQuality = this.getOptimalQuality(file, quality);
-
-					canvas.toBlob(
-						(blob) => {
-							cleanup();
-							if (blob) {
-								resolve(new File(
-									[blob],
-									this.getProcessedFileName(file, outputFormat),
-									{ type: outputFormat, lastModified: Date.now() }
-								));
-							} else {
-								reject(new Error('Canvas toBlob failed'));
-							}
-						},
-						outputFormat,
-						outputQuality
-					);
-				} catch (error) {
-					cleanup();
-					reject(new Error(`Canvas processing failed: ${error.message}`));
-				}
-			};
-
-			img.onerror = () => {
-				cleanup();
-				reject(new Error(`Failed to load image: ${file.name}`));
-			};
-
-			try {
-				objectUrl = this.createPreviewUrl(file);
-				img.src = objectUrl;
-			} catch (error) {
-				cleanup();
-				reject(new Error(`Failed to create object URL: ${error.message}`));
-			}
-		});
-	}
-
-	// Worker methods (simplified)
-	initWorker() {
-		if (this.worker.worker || typeof Worker === 'undefined') return;
-
-		try {
-			const workerScript = `
-				self.onmessage = async function(e) {
-					const { messageId, file, maxDimension, quality, outputFormat } = e.data;
-					try {
-						const bitmap = await createImageBitmap(file);
-						const scale = Math.min(maxDimension / bitmap.width, maxDimension / bitmap.height, 1);
-						const width = Math.round(bitmap.width * scale);
-						const height = Math.round(bitmap.height * scale);
-						const canvas = new OffscreenCanvas(width, height);
-						const ctx = canvas.getContext('2d');
-						ctx.imageSmoothingEnabled = true;
-						ctx.imageSmoothingQuality = 'high';
-						ctx.drawImage(bitmap, 0, 0, width, height);
-						bitmap.close();
-						const blob = await canvas.convertToBlob({ type: outputFormat, quality });
-						self.postMessage({ messageId, success: true, blob, format: outputFormat });
-					} catch (error) {
-						self.postMessage({ messageId, success: false, error: error.message });
-					}
-				};
-			`;
-
-			const blob = new Blob([workerScript], { type: 'application/javascript' });
-			this.worker.worker = new Worker(this.createPreviewUrl(blob));
-		} catch (error) {
-			console.warn('Failed to initialize worker:', error);
-			this.worker.worker = null;
-		}
-	}
-
-	async processWithWorker(file, uploadId, maxDimension, quality) {
-		return new Promise((resolve, reject) => {
-			if (!this.worker.worker) {
-				reject(new Error('Worker not available'));
-				return;
-			}
-
-			const messageId = `${uploadId}_${Date.now()}`;
-			const outputFormat = this.getOptimalFormat(file);
-
-			const handler = (e) => {
-				if (e.data.messageId !== messageId) return;
-				this.worker.worker.removeEventListener('message', handler);
-				this.worker.worker.removeEventListener('error', errorHandler);
-
-				if (e.data.success) {
-					resolve(new File(
-						[e.data.blob],
-						this.getProcessedFileName(file, e.data.format || outputFormat),
-						{ type: e.data.format || outputFormat, lastModified: Date.now() }
-					));
-				} else {
-					reject(new Error(e.data.error || 'Worker processing failed'));
-				}
-			};
-
-			const errorHandler = (error) => {
-				this.worker.worker.removeEventListener('message', handler);
-				this.worker.worker.removeEventListener('error', errorHandler);
-				reject(new Error(`Worker error: ${error.message}`));
-			};
-
-			this.worker.worker.addEventListener('message', handler);
-			this.worker.worker.addEventListener('error', errorHandler);
-			this.worker.worker.postMessage({ messageId, file, maxDimension, quality, outputFormat });
-		});
-	}
-
-	restartWorker() {
-		if (this.worker.worker) {
-			this.worker.worker.terminate();
-			this.worker.worker = null;
-		}
-		this.worker.tasks.clear();
-
-		if (this.worker.restart.count < this.worker.restart.max) {
-			this.worker.restart.count++;
-			this.initWorker();
-		}
-	}
-
-	// Image processing helpers
-	calculateDimensions(img, maxDimension) {
-		let { width, height } = img;
-		if (width <= maxDimension && height <= maxDimension) {
-			return { width, height };
-		}
-		const scale = Math.min(maxDimension / width, maxDimension / height);
-		return { width: Math.round(width * scale), height: Math.round(height * scale) };
-	}
-
-	getMaxDimension() {
-		const screenWidth = window.screen.width;
-		const dpr = window.devicePixelRatio || 1;
-		if (screenWidth * dpr > 2560) return 2400;
-		if (screenWidth * dpr > 1920) return 1920;
-		return 1200;
-	}
-
-	shouldUseWorker(file) {
-		return this.worker.worker && file.size > 1024 * 1024 && typeof OffscreenCanvas !== 'undefined';
-	}
-
-	getOptimalFormat(file) {
-		if (file.type === 'image/gif' || file.type === 'image/svg+xml') return file.type;
-		return this.supportsWebP() ? 'image/webp' : 'image/jpeg';
-	}
-
-	getOptimalQuality(file, requestedQuality) {
-		if (file.size < 500 * 1024) return Math.max(requestedQuality, 0.9);
-		if (file.size < 2 * 1024 * 1024) return requestedQuality;
-		return Math.min(requestedQuality, 0.8);
-	}
-
-	getProcessedFileName(file, outputFormat) {
-		const baseName = file.name.replace(/\.[^/.]+$/, '');
-		const extensions = { 'image/webp': '.webp', 'image/jpeg': '.jpg', 'image/png': '.png', 'image/gif': '.gif' };
-		return baseName + (extensions[outputFormat] || '.jpg');
-	}
-
-	supportsWebP() {
-		const canvas = document.createElement('canvas');
-		return canvas.toDataURL('image/webp').startsWith('data:image/webp');
-	}
-
-	/*******************************************************************************
-	 * BLOB DATA MANAGEMENT
-	 *******************************************************************************/
-
-	async saveBlobData(uploadId, file) {
-		const arrayBuffer = await file.arrayBuffer();
-		const upload = this.uploadStore.get(uploadId) || { id: uploadId };
-
-		upload.blobData = {
-			buffer: arrayBuffer,
-			name: file.name,
-			type: file.type,
-			size: file.size,
-			lastModified: file.lastModified || Date.now()
-		};
-
-		await this.uploadStore.save(upload);
-	}
-
-	async getBlobData(uploadId) {
-		const upload = this.uploadStore.get(uploadId);
-		if (!upload?.blobData) return null;
-
-		const blob = new Blob([upload.blobData.buffer], { type: upload.blobData.type });
-		return new File([blob], upload.blobData.name, {
-			type: upload.blobData.type,
-			lastModified: upload.blobData.lastModified
-		});
-	}
-
-	/*******************************************************************************
-	 * QUEUE INTEGRATION
-	 *******************************************************************************/
-
-	async queueUpload(fieldId) {
-		const uploads = this.getFieldUploads(fieldId);
-		if (uploads.length === 0) return;
-
-		const fieldEl = this.fieldElements.get(fieldId);
-		if (!fieldEl) return;
-
-		const formData = await this.prepareUploadFormData(fieldId, uploads, fieldEl.config);
-
-		this.a11y.announce('Queuing for upload');
-
-		const operation = {
-			endpoint: 'uploads',
-			method: 'POST',
-			data: formData,
-			title: `Uploading ${uploads.length} file${uploads.length > 1 ? 's' : ''}...`,
-			popup: `Uploading ${uploads.length} file${uploads.length > 1 ? 's' : ''}...`,
-			canMerge: false,
-			headers: { 'action_nonce': window.auth.getNonce('dash') },
-			append: '_upload'
-		};
-
-		try {
-			const operationId = await this.queue.addToQueue(operation);
-
-			// Update upload statuses
-			for (const upload of uploads) {
-				upload.operationId = operationId;
-				upload.status = 'queued';
-				await this.uploadStore.save(upload);
-				this.updateUploadUI(upload.id);
-			}
-
-			return operationId;
-		} catch (error) {
-			throw error;
-		}
-	}
-
-	async prepareUploadFormData(fieldId, uploads, config) {
-		const formData = new FormData();
-
-		formData.append('content', config.content);
-		formData.append('mode', config.mode);
-		formData.append('field_name', config.name);
-		formData.append('fieldId', fieldId);
-		formData.append('field_type', config.type);
-		formData.append('subtype', config.subtype);
-		formData.append('item_id', config.itemID);
-		formData.append('destination', config.destination || 'meta');
-
-		const uploadMap = [];
-
-		for (const upload of uploads) {
-			const file = await this.getBlobData(upload.id);
-			if (file) {
-				formData.append('files[]', file);
-				uploadMap.push(upload.id);
-			}
-		}
-
-		formData.append('upload_ids', JSON.stringify(uploadMap));
-		return formData;
-	}
-
-	async submitGroupedUploads(fieldId) {
-		const uploads = this.getFieldUploads(fieldId);
-		const groups = this.getFieldGroups(fieldId);
-		const fieldEl = this.fieldElements.get(fieldId);
-
-		if (uploads.length === 0 || !fieldEl) return;
-
-		const formData = new FormData();
-		const posts = [];
-		const uploadMap = [];
-
-		// Process each group
-		for (const group of groups) {
-			const groupUploads = uploads.filter(u => u.groupId === group.id);
-
-			const post = {
-				images: [],
-				fields: { ...group.fields }
-			};
-
-			for (const upload of groupUploads) {
-				const file = await this.getBlobData(upload.id);
-				if (file) {
-					formData.append('files[]', file);
-					post.images.push({ upload_id: upload.id, index: uploadMap.length });
-					uploadMap.push(upload.id);
-
-					// Check featured
-					const uploadEl = this.uploadElements.get(upload.id);
-					const featured = uploadEl?.element?.querySelector('[name="featured"]');
-					if (featured?.checked) {
-						post.fields.featured = upload.id;
-					}
-				}
-			}
-
-			if (post.images.length > 0) {
-				posts.push(post);
-			}
-		}
-
-		// Handle ungrouped uploads - each becomes its own post
-		const ungrouped = uploads.filter(u => !u.groupId);
-		for (const upload of ungrouped) {
-			const file = await this.getBlobData(upload.id);
-			if (file) {
-				formData.append('files[]', file);
-				posts.push({
-					images: [{ upload_id: upload.id, index: uploadMap.length }],
-					fields: {}
-				});
-				uploadMap.push(upload.id);
-			}
-		}
-
-		formData.append('content', fieldEl.config.content);
-		formData.append('user', fieldEl.config.itemID);
-		formData.append('posts', JSON.stringify(posts));
-		formData.append('upload_ids', JSON.stringify(uploadMap));
-
-		const operation = {
-			endpoint: 'uploads/groups',
-			method: 'POST',
-			data: formData,
-			title: `Creating ${posts.length} ${fieldEl.config.content}${posts.length > 1 ? 's' : ''}...`,
-			popup: `Creating ${posts.length} post${posts.length > 1 ? 's' : ''}...`,
-			canMerge: false,
-			headers: { 'action_nonce': window.auth.getNonce('dash') },
-			append: '_upload'
-		};
-
-		try {
-			const operationId = await this.queue.addToQueue(operation);
-
-			for (const upload of uploads) {
-				upload.operationId = operationId;
-				upload.status = 'queued';
-				await this.uploadStore.save(upload);
-				this.updateUploadUI(upload.id);
-			}
-
-			this.a11y.announce(`Creating ${posts.length} post${posts.length > 1 ? 's' : ''}`);
-			return operationId;
-		} catch (error) {
-			this.error.log(error, { component: 'UploadManager', action: 'submitGroupedUploads', fieldId });
-			throw error;
-		}
-	}
-
-	async queueUploadMeta(e) {
-		const uploadId = this.getUploadIdFromElement(e.target);
-		const upload = this.uploadStore.get(uploadId);
-		if (!upload) return;
-
-		const data = { [e.target.name]: e.target.value };
-		upload.meta = { ...upload.meta, ...data };
-		await this.uploadStore.save(upload);
-
-		const queueData = { [upload.attachmentId ?? upload.id]: upload.meta };
-
-		const operation = {
-			endpoint: 'uploads/meta',
-			method: 'POST',
-			data: queueData,
-			title: 'Updating meta',
-			canMerge: true,
-			headers: { 'action_nonce': window.auth.getNonce('dash') }
-		};
-
-		try {
-			await this.queue.addToQueue(operation);
-		} catch (error) {
-			this.error.log(error, { component: 'UploadManager', action: 'queueUploadMeta', uploadId });
-		}
-	}
-
-	/*******************************************************************************
-	 * QUEUE EVENT HANDLERS - Cleanup after success
-	 *******************************************************************************/
-
-	async handleOperationComplete(operation, fieldId) {
-		const results = operation.result?.data || operation.serverData?.data || [];
-
-		// Update attachment IDs
-		for (const result of results) {
-			const upload = this.uploadStore.get(result.upload_id);
-			if (upload) {
-				upload.attachmentId = result.attachment_id;
-				upload.status = 'completed';
-				await this.uploadStore.save(upload);
-				this.updateUploadUI(result.upload_id);
-			}
-		}
-
-		if (!fieldId) return;
-
-		// Clear completed uploads and their groups
-		await this.clearCompletedUploads(fieldId);
-
-		this.updateFieldState(fieldId);
-		this.a11y.announce('All uploads completed successfully');
-	}
-
-	async clearCompletedUploads(fieldId) {
-		const uploads = this.getFieldUploads(fieldId);
-		const groupIds = new Set();
-
-		for (const upload of uploads) {
-			if (upload.status === 'completed') {
-				if (upload.groupId) groupIds.add(upload.groupId);
-				await this.clearUpload(upload.id);
-			}
-		}
-
-		// Clear associated groups
-		for (const groupId of groupIds) {
-			await this.groupStore.delete(groupId);
-			this.groupElements.delete(groupId);
-		}
-	}
-
-	handleOperationFailed(operation, fieldId) {
-		const uploadIds = operation.data instanceof FormData
-			? JSON.parse(operation.data.get('upload_ids') || '[]')
-			: operation.data?.upload_ids || [];
-
-		const status = operation.status === 'operation-failed-permanent' ? 'failed_permanent' : 'failed';
-
-		uploadIds.forEach(async uploadId => {
-			await this.updateUploadStatus(uploadId, status);
-		});
-
-		if (fieldId) this.updateFieldState(fieldId);
-	}
-
-	async handleOperationCancelled(fieldId) {
-		const uploads = this.getFieldUploads(fieldId);
-		const groups = this.getFieldGroups(fieldId);
-
-		for (const upload of uploads) {
-			await this.clearUpload(upload.id);
-		}
-
-		for (const group of groups) {
-			await this.groupStore.delete(group.id);
-			this.groupElements.delete(group.id);
-		}
-
-		this.updateFieldState(fieldId);
-		this.a11y.announce('Upload cancelled');
-	}
-
-	/*******************************************************************************
-	 * GROUP MANAGEMENT
-	 *******************************************************************************/
-
-	async createGroup(fieldId, groupId = null) {
-		const fieldEl = this.fieldElements.get(fieldId);
-		if (!fieldEl) return null;
-
-		groupId = groupId || this.generateId('group');
-
-		// Create group data
-		const groupData = {
-			id: groupId,
-			fieldId,
-			pageUrl: window.location.href,
-			uploads: [],
-			fields: {}
-		};
-
-		await this.groupStore.save(groupData);
-
-		// Create DOM element
-		const element = this.createGroupElement(groupId, fieldId);
-		if (!element) return null;
-
-		const grid = element.querySelector('.item-grid.group');
-
-		// Store DOM references
-		this.groupElements.set(groupId, { element, grid, fieldId });
-
-		// Insert into DOM
-		if (fieldEl.ui.groups?.container && fieldEl.ui.groups.empty) {
-			fieldEl.ui.groups.container.insertBefore(element, fieldEl.ui.groups.empty);
-		} else if (fieldEl.ui.groups?.container) {
-			fieldEl.ui.groups.container.appendChild(element);
-		}
-
-		// Initialize sortable and selection
-		this.addGroupSelectionHandler(fieldId, groupId);
-		if (grid) this.createSortableForGrid(grid, fieldId, groupId);
-
-		return { id: groupId, element, grid };
-	}
-
-	createGroupElement(groupId, fieldId) {
-		const element = window.getTemplate('imageGroup');
-		if (!element) return null;
-
-		element.dataset.groupId = groupId;
-		element.dataset.fieldId = fieldId;
-
-		const fields = window.getTemplate('groupMetadata');
-		const fieldsContainer = element.querySelector('.fields');
-
-		if (fieldsContainer && fields) {
-			fieldsContainer.append(fields);
-
-			const titleInput = fieldsContainer.querySelector('[name="post_title"]');
-			const excerptInput = fieldsContainer.querySelector('[name="post_excerpt"]');
-
-			if (titleInput) {
-				titleInput.id = `${groupId}_title`;
-				titleInput.name = `${groupId}[post_title]`;
-			}
-			if (excerptInput) {
-				excerptInput.id = `${groupId}_excerpt`;
-				excerptInput.name = `${groupId}[post_excerpt]`;
-			}
-
-			const fieldEl = this.fieldElements.get(fieldId);
-			if (fieldEl?.config.content) {
-				const summary = element.querySelector('summary');
-				if (summary) summary.textContent = fieldEl.config.content + ' Fields';
-			}
-		} else {
-			element.querySelector('details')?.remove();
-		}
-
-		const gridContainer = element.querySelector('.item-grid.group');
-		if (gridContainer) gridContainer.dataset.groupId = groupId;
-
-		return element;
-	}
-
-	async deleteGroup(groupId, moveUploadsToPreview = true) {
-		const groupEl = this.groupElements.get(groupId);
-		if (!groupEl) return;
-
-		const group = this.groupStore.get(groupId);
-		const fieldEl = this.fieldElements.get(groupEl.fieldId);
-
-		if (moveUploadsToPreview && group?.uploads) {
-			for (const uploadId of group.uploads) {
-				await this.removeFromGroup(uploadId);
-			}
-		}
-
-		// Remove from store
-		await this.groupStore.delete(groupId);
-
-		// Remove DOM element
-		groupEl.element?.remove();
-
-		// Cleanup
-		this.groupElements.delete(groupId);
-
-		const sortableKey = `${groupEl.fieldId}-group-${groupId}`;
-		this.sortableInstances.get(sortableKey)?.destroy();
-		this.sortableInstances.delete(sortableKey);
-
-		this.a11y.announce('Group removed');
-	}
-
-	async addToGroup(uploadId, targetGrid, persist = true) {
-		const upload = this.uploadStore.get(uploadId);
-		const uploadEl = this.uploadElements.get(uploadId);
-		if (!upload || !uploadEl) return;
-
-		const groupId = targetGrid.dataset.groupId;
-		const group = this.groupStore.get(groupId);
-
-		// Remove from previous group if needed
-		if (upload.groupId && upload.groupId !== groupId) {
-			const oldGroup = this.groupStore.get(upload.groupId);
-			if (oldGroup) {
-				oldGroup.uploads = oldGroup.uploads.filter(id => id !== uploadId);
-				if (oldGroup.uploads.length === 0) {
-					await this.deleteGroup(upload.groupId, false);
-				} else {
-					await this.groupStore.save(oldGroup);
-				}
-			}
-		}
-
-		// Add to new group
-		upload.groupId = groupId;
-		if (group && !group.uploads.includes(uploadId)) {
-			group.uploads.push(uploadId);
-			await this.groupStore.save(group);
-		}
-
-		// Update upload
-		await this.uploadStore.save(upload);
-
-		// Move DOM element
-		targetGrid.appendChild(uploadEl.element);
-		uploadEl.location = targetGrid;
-
-		// Show featured radio
-		const featured = uploadEl.element.querySelector('[name="featured"]');
-		if (featured) {
-			featured.hidden = false;
-			featured.name = `${groupId}_featured`;
-		}
-
-		// Clear checkbox
-		const checkbox = uploadEl.element.querySelector('[name*="select-item"]');
-		if (checkbox) checkbox.checked = false;
-
-		this.updateSortableState(targetGrid);
-	}
-
-	async removeFromGroup(uploadId) {
-		const upload = this.uploadStore.get(uploadId);
-		const uploadEl = this.uploadElements.get(uploadId);
-		if (!upload || !uploadEl) return;
-
-		const fieldEl = this.fieldElements.get(upload.fieldId);
-		if (!fieldEl?.ui?.preview) return;
-
-		// Update group
-		if (upload.groupId) {
-			const group = this.groupStore.get(upload.groupId);
-			if (group) {
-				group.uploads = group.uploads.filter(id => id !== uploadId);
-				if (group.uploads.length === 0) {
-					await this.deleteGroup(upload.groupId, false);
-				} else {
-					await this.groupStore.save(group);
-				}
-			}
-			upload.groupId = null;
-			await this.uploadStore.save(upload);
-		}
-
-		// Move to preview
-		fieldEl.ui.preview.appendChild(uploadEl.element);
-		uploadEl.location = fieldEl.ui.preview;
-
-		// Hide featured radio
-		const featured = uploadEl.element.querySelector('[name="featured"]');
-		if (featured) {
-			featured.hidden = true;
-			featured.checked = false;
-		}
-
-		this.updateSortableState(fieldEl.ui.preview);
-	}
-
-	async removeUpload(fieldId, uploadId) {
-		const upload = this.uploadStore.get(uploadId);
-		const uploadEl = this.uploadElements.get(uploadId);
-
-		if (upload?.groupId) {
-			const group = this.groupStore.get(upload.groupId);
-			if (group) {
-				group.uploads = group.uploads.filter(id => id !== uploadId);
-				if (group.uploads.length === 0) {
-					await this.deleteGroup(upload.groupId, false);
-				} else {
-					await this.groupStore.save(group);
-				}
-			}
-		}
-
-		uploadEl?.element?.remove();
-		await this.clearUpload(uploadId);
-
-		this.updateFieldState(fieldId);
-		this.maybeLockUploads(fieldId);
-
-		this.selectionHandlers.get(fieldId)?.deselect(uploadId);
-		this.a11y.announce('Upload removed');
-	}
-
-	async handleGroupMetaChange(input) {
-		const groupEl = input.closest(this.selectors.groups.container);
-		if (!groupEl) return;
-
-		const groupId = groupEl.dataset.groupId;
-		const group = this.groupStore.get(groupId);
-		if (!group) return;
-
-		let name = input.name;
-		if (name.includes(groupId)) {
-			name = name.replace(`${groupId}_`, '').replace(`${groupId}[`, '').replace(']', '');
-		}
-
-		group.fields[name] = input.value;
-		await this.groupStore.save(group);
-	}
-
-	/*******************************************************************************
-	 * SORTABLE & DRAG/DROP
-	 *******************************************************************************/
-
-	initSortable(fieldId) {
-		if (!window.Sortable) return;
-
-		if (!Sortable._multiDragMounted && Sortable.MultiDrag) {
-			Sortable.mount(new Sortable.MultiDrag());
-			Sortable._multiDragMounted = true;
-		}
-
-		const fieldEl = this.fieldElements.get(fieldId);
-		if (!fieldEl) return;
-
-		const grids = fieldEl.element.querySelectorAll('.item-grid.preview, .item-grid.group');
-		grids.forEach(grid => {
-			const groupId = grid.classList.contains('group')
-				? grid.closest('.upload-group')?.dataset.groupId
-				: null;
-			this.createSortableForGrid(grid, fieldId, groupId);
-		});
-
-		// Empty group drop zone
-		const emptyGroup = fieldEl.element.querySelector('.empty-group');
-		if (emptyGroup && !emptyGroup.sortableInstance) {
-			emptyGroup.sortableInstance = new Sortable(emptyGroup, {
-				animation: 150,
-				draggable: '.item',
-				multiDrag: true,
-				selectedClass: 'selected-for-drag',
-				avoidImplicitDeselect: true,
-				group: { name: fieldId, pull: false, put: true },
-				ghostClass: 'sortable-ghost',
-				onEnd: (evt) => this.handleDrop(evt, fieldId)
-			});
-		}
-	}
-
-	createSortableForGrid(grid, fieldId, groupId = null) {
-		if (!grid || grid.sortableInstance) return;
-
-		const instance = new Sortable(grid, {
-			animation: 150,
-			draggable: '.item',
-			multiDrag: true,
-			selectedClass: 'selected-for-drag',
-			avoidImplicitDeselect: true,
-			group: { name: fieldId, pull: true, put: true },
-			ghostClass: 'sortable-ghost',
-			chosenClass: 'sortable-chosen',
-			dragClass: 'sortable-drag',
-			onEnd: (evt) => this.handleDrop(evt, fieldId),
-			onSelect: (evt) => this.syncCheckboxToSortable(evt.item, true),
-			onDeselect: (evt) => this.syncCheckboxToSortable(evt.item, false),
-			onAdd: (evt) => this.updateSortableState(evt.to),
-			onRemove: (evt) => this.updateSortableState(evt.from)
-		});
-
-		grid.sortableInstance = instance;
-
-		const gridId = groupId ? `${fieldId}-group-${groupId}` : `${fieldId}-preview`;
-		this.sortableInstances.set(gridId, instance);
-
-		return instance;
-	}
-
-	syncCheckboxToSortable(item, selected) {
-		const checkbox = item.querySelector('[name*="select-item"]');
-		if (checkbox && checkbox.checked !== selected) {
-			checkbox.checked = selected;
-			checkbox.dispatchEvent(new Event('change', { bubbles: true }));
-		}
-	}
-
-	/**
-	 * Unified drop handler - consolidated from multiple methods
-	 */
-	async handleDrop(evt, fieldId) {
-		const target = evt.to;
-		const source = evt.from;
-		const items = evt.items?.length > 0 ? evt.items : [evt.item];
-		const uploadIds = items.map(item => item.dataset.uploadId);
-
-		// Same container = reorder only
-		if (target === source) {
-			this.handleReorder(evt);
-			return;
-		}
-
-		const targetType = this.getDropTargetType(target);
-
-		try {
-			switch (targetType) {
-				case 'empty-group':
-					const group = await this.createGroup(fieldId);
-					if (!group) throw new Error('Group creation failed');
-					for (const uploadId of uploadIds) {
-						await this.addToGroup(uploadId, group.grid, false);
-					}
-					break;
-
-				case 'preview':
-					for (const uploadId of uploadIds) {
-						await this.removeFromGroup(uploadId);
-					}
-					break;
-
-				case 'group':
-					for (const uploadId of uploadIds) {
-						await this.addToGroup(uploadId, target, false);
-					}
-					break;
-
-				default:
-					throw new Error('Unknown drop target');
-			}
-
-			this.finalizeDrop(fieldId, items.length, targetType);
-
-		} catch (error) {
-			console.error('Drop error:', error);
-			// Return items to preview as fallback
-			const fieldEl = this.fieldElements.get(fieldId);
-			if (fieldEl?.ui?.preview) {
-				items.forEach(item => fieldEl.ui.preview.appendChild(item));
-			}
-			this.a11y.announce('An error occurred. Items returned to preview.');
-		}
-
-		this.updateSortableState(target);
-		if (source !== target) this.updateSortableState(source);
-	}
-
-	finalizeDrop(fieldId, count, targetType) {
-		const messages = {
-			'empty-group': count > 1 ? `Created group with ${count} items` : 'Created group with item',
-			'preview': count > 1 ? `Moved ${count} items to preview` : 'Moved item to preview',
-			'group': count > 1 ? `Moved ${count} items to group` : 'Moved item to group'
-		};
-
-		this.a11y.announce(messages[targetType] || 'Items moved');
-		this.selectionHandlers.get(fieldId)?.clearSelection();
-	}
-
-	getDropTargetType(target) {
-		if (target.classList.contains('empty-group')) return 'empty-group';
-		if (target.classList.contains('preview')) return 'preview';
-		if (target.classList.contains('group')) return 'group';
-		return 'unknown';
-	}
-
-	handleReorder(evt) {
-		const grid = evt.to;
-		const fieldWrapper = grid.closest('.field, .upload');
-		if (!fieldWrapper) return;
-
-		const items = Array.from(grid.querySelectorAll('.item:not(.sortable-ghost):not(.sortable-clone)'))
-			.map(el => el.dataset.uploadId)
-			.filter(Boolean);
-
-		const hiddenInput = fieldWrapper.querySelector('input[type="hidden"]');
-		if (hiddenInput && items.length > 0) {
-			hiddenInput.value = items.join(',');
-		}
-
-		// Update group order in store
-		const groupId = grid.dataset.groupId;
-		if (groupId) {
-			const group = this.groupStore.get(groupId);
-			if (group) {
-				group.uploads = items;
-				this.groupStore.save(group);
-			}
-		}
-
-		this.a11y.announce('Item reordered');
-
-		fieldWrapper.dispatchEvent(new CustomEvent('jvb-items-reordered', {
-			detail: { from: evt.from, to: evt.to, items },
-			bubbles: true
-		}));
-	}
-
-	updateSortableState(grid) {
-		const sortable = grid?.sortableInstance;
-		if (sortable) sortable.option('disabled', false);
-	}
-
-	refreshSortable(fieldId) {
-		const fieldEl = this.fieldElements.get(fieldId);
-		if (!fieldEl) return;
-
-		const grids = fieldEl.element.querySelectorAll('.item-grid.preview, .item-grid.group');
-		grids.forEach(grid => this.updateSortableState(grid));
-	}
-
-	syncSortableSelection(fieldId, selectedItems) {
-		this.sortableInstances.forEach((instance, key) => {
-			if (key.startsWith(fieldId)) {
-				instance.el.querySelectorAll('.item').forEach(item => {
-					const uploadId = item.dataset.uploadId;
-					if (selectedItems.has(uploadId)) {
-						Sortable.utils.select(item);
-					} else {
-						Sortable.utils.deselect(item);
-					}
-				});
-			}
-		});
-	}
-
-	/*******************************************************************************
-	 * EVENT LISTENERS
-	 *******************************************************************************/
-
-	initListeners() {
-		this.clickHandler = this.handleClick.bind(this);
-		this.changeHandler = this.handleChange.bind(this);
-		this.dragEnterHandler = this.handleDragEnter.bind(this);
-		this.dragLeaveHandler = this.handleDragLeave.bind(this);
-		this.dragOverHandler = this.handleDragOver.bind(this);
-		this.dropHandler = this.handleExternalDrop.bind(this);
-
-		document.addEventListener('click', this.clickHandler);
-		document.addEventListener('change', this.changeHandler);
-		document.addEventListener('dragenter', this.dragEnterHandler);
-		document.addEventListener('dragleave', this.dragLeaveHandler);
-		document.addEventListener('dragover', this.dragOverHandler);
-		document.addEventListener('drop', this.dropHandler);
-	}
-
-	handleClick(e) {
-		// Trigger file input
-		const dropZone = e.target.closest(this.selectors.field.dropZone);
-		if (dropZone && !e.target.matches('input, button, a')) {
-			dropZone.querySelector(this.selectors.field.input)?.click();
-		}
-
-		// Action buttons
-		const actionButton = e.target.closest('[data-action]');
-		if (actionButton) this.handleAction(actionButton);
-	}
-
-	handleChange(e) {
-		const fieldId = this.getFieldIdFromElement(e.target);
-		if (!fieldId) return;
-
-		// File input
-		if (e.target.matches(this.selectors.field.input)) {
-			const files = Array.from(e.target.files);
-			if (files.length > 0) this.processFiles(fieldId, files);
-			return;
-		}
-
-		// Meta field changes
-		const fieldEl = this.fieldElements.get(fieldId);
-		if (!fieldEl?.config.autoUpload) return;
-
-		if (fieldEl.config.destination === 'post_group') {
-			this.handleGroupMetaChange(e.target);
-		} else {
-			this.queueUploadMeta(e);
-		}
-	}
-
-	handleDragEnter(e) {
-		if (!e.dataTransfer.types.includes('Files')) return;
-		const dropZone = e.target.closest(this.selectors.field.dropZone);
-		if (dropZone) {
-			e.preventDefault();
-			dropZone.classList.add('dragover');
-		}
-	}
-
-	handleDragLeave(e) {
-		const dropZone = e.target.closest(this.selectors.field.dropZone);
-		if (dropZone && !dropZone.contains(e.relatedTarget)) {
-			dropZone.classList.remove('dragover');
-		}
-	}
-
-	handleDragOver(e) {
-		if (!e.dataTransfer.types.includes('Files')) return;
-		const dropZone = e.target.closest(this.selectors.field.dropZone);
-		if (dropZone) {
-			e.preventDefault();
-			e.dataTransfer.dropEffect = 'copy';
-		}
-	}
-
-	handleExternalDrop(e) {
-		const dropZone = e.target.closest(this.selectors.field.dropZone);
-		if (!dropZone) return;
-
-		e.preventDefault();
-		dropZone.classList.remove('dragover');
-
-		const files = Array.from(e.dataTransfer.files);
-		if (files.length === 0) return;
-
-		const fieldId = this.getFieldIdFromElement(dropZone);
-		if (fieldId) {
-			this.processFiles(fieldId, files);
-			this.a11y.announce(`${files.length} file(s) dropped for upload`);
-		}
-	}
-
-	/*******************************************************************************
-	 * ACTION HANDLERS
-	 *******************************************************************************/
-
-	handleAction(button) {
-		const action = button.dataset.action;
-		const fieldId = this.getFieldIdFromElement(button);
-
-		switch (action) {
-			case 'add-to-group':
-				this.handleAddToGroup(fieldId);
-				break;
-			case 'delete-group':
-				this.handleDeleteGroup(button);
-				break;
-			case 'delete-upload':
-			case 'remove-from-group':
-				this.handleRemoveItem(button);
-				break;
-			case 'upload':
-				this.handleSubmitUploads(fieldId);
-				break;
-			case 'restore':
-				this.handleRestoreSelected();
-				break;
-			case 'restore-all':
-				this.handleRestoreAll();
-				break;
-			case 'clear-cache':
-				this.handleClearCache();
-				break;
-		}
-	}
-
-	async handleAddToGroup(fieldId) {
-		const selected = this.selected.get(fieldId);
-
-		if (!selected || selected.size === 0) {
-			await this.createGroup(fieldId);
-		} else {
-			const group = await this.createGroup(fieldId);
-			if (!group) return;
-
-			for (const uploadId of selected) {
-				await this.addToGroup(uploadId, group.grid, false);
-			}
-
-			this.selectionHandlers.get(fieldId)?.clearSelection();
-			this.a11y.announce(`Created group with ${selected.size} items`);
-		}
-	}
-
-	async handleDeleteGroup(button) {
-		const group = button.closest(this.selectors.groups.container);
-		if (!group) return;
-
-		const groupId = group.dataset.groupId;
-
-		if (!confirm('Delete this group? Items will be moved back to the upload area.')) {
-			return;
-		}
-
-		await this.deleteGroup(groupId, true);
-	}
-
-	async handleRemoveItem(button) {
-		const item = button.closest(this.selectors.items.item);
-		if (!item) return;
-
-		if (!confirm('Remove this item?')) return;
-
-		const uploadId = item.dataset.uploadId;
-		const fieldId = this.getFieldIdFromElement(item);
-
-		await this.removeUpload(fieldId, uploadId);
-	}
-
-	handleSubmitUploads(fieldId) {
-		const fieldEl = this.fieldElements.get(fieldId);
-		if (!fieldEl) return;
-
-		fieldEl.element.closest('details')?.removeAttribute('open');
-		document.body.classList.add('uploading');
-
-		if (fieldEl.config.destination === 'post_group') {
-			this.submitGroupedUploads(fieldId);
-		} else {
-			this.queueUpload(fieldId);
-		}
-	}
-
-	/*******************************************************************************
-	 * SELECTION MANAGEMENT
-	 *******************************************************************************/
-
-	addFieldSelectionHandler(fieldId) {
-		if (this.selectionHandlers.has(fieldId)) return;
-
-		const fieldEl = this.fieldElements.get(fieldId);
-		if (!fieldEl?.element) return;
-
-		const handler = new window.jvbHandleSelection({
-			container: fieldEl.element,
-			ui: {
-				selectAll: fieldEl.element.querySelector('[name="select-all-uploads"]'),
-				bulkControls: fieldEl.element.querySelector('.selection-actions'),
-				count: fieldEl.element.querySelector('.selection-count')
-			},
-			itemSelector: '[data-upload-id]',
-			checkboxSelector: '[name*="select-item"]'
-		});
-
-		handler.subscribe((event, data) => {
-			if (['item-selected', 'item-deselected', 'range-selected'].includes(event)) {
-				this.syncSortableSelection(fieldId, data.selectedItems);
-				this.selected.set(fieldId, data.selectedItems);
-			}
-		});
-
-		this.selectionHandlers.set(fieldId, handler);
-	}
-
-	addGroupSelectionHandler(fieldId, groupId) {
-		const key = `${fieldId}_${groupId}`;
-		if (this.selectionHandlers.has(key)) return;
-
-		const groupEl = this.groupElements.get(groupId);
-		if (!groupEl?.element) return;
-
-		const handler = new window.jvbHandleSelection({
-			container: groupEl.element,
-			ui: {
-				selectAll: groupEl.element.querySelector(this.selectors.groups.selectAll),
-				bulkControls: groupEl.element.querySelector(this.selectors.groups.actions),
-				count: groupEl.element.querySelector(this.selectors.groups.count)
-			},
-			itemSelector: '[data-upload-id]',
-			checkboxSelector: '[name*="select-item"]'
-		});
-
-		handler.subscribe((event, data) => {
-			if (['item-selected', 'item-deselected', 'range-selected'].includes(event)) {
-				this.selected.set(fieldId, data.selectedItems);
-			}
-		});
-
-		this.selectionHandlers.set(key, handler);
-	}
-
-	/*******************************************************************************
-	 * UI UPDATES
-	 *******************************************************************************/
-
-	updateFieldState(fieldId) {
-		const fieldEl = this.fieldElements.get(fieldId);
-		if (!fieldEl) return;
-
-		const uploadCount = this.getFieldUploadCount(fieldId);
-		const groupCount = this.getFieldGroups(fieldId).length;
-
-		fieldEl.element.dataset.hasUploads = uploadCount > 0;
-		fieldEl.element.dataset.uploadCount = uploadCount;
-		fieldEl.element.dataset.hasGroups = groupCount > 0;
-
-		if (fieldEl.ui.preview) {
-			fieldEl.ui.preview.setAttribute('aria-label',
-				`Upload preview area with ${uploadCount} item${uploadCount !== 1 ? 's' : ''}`
-			);
-		}
-	}
-
-	updateFieldProgress(fieldId, current, total, message) {
-		const fieldEl = this.fieldElements.get(fieldId);
-		const progress = fieldEl?.ui?.progress;
-		if (!progress?.container) return;
-
-		const percent = total > 0 ? (current / total) * 100 : 0;
-
-		if (progress.fill) progress.fill.style.width = `${percent}%`;
-		if (progress.text) progress.text.textContent = message;
-		if (progress.count) progress.count.textContent = `${current}/${total}`;
-
-		progress.container.hidden = current === total;
-	}
-
-	updateFieldStatus(fieldId, status) {
-		// Status is now derived from uploads, no field-level status needed
-	}
-
-	async updateUploadStatus(uploadId, status) {
-		const upload = this.uploadStore.get(uploadId);
-		if (!upload) return;
-
-		upload.status = status;
-		await this.uploadStore.save(upload);
-		this.updateUploadUI(uploadId);
-	}
-
-	updateUploadUI(uploadId) {
-		const uploadEl = this.uploadElements.get(uploadId);
-		const upload = this.uploadStore.get(uploadId);
-		if (!upload || !uploadEl?.element) return;
-
-		// Update class
-		uploadEl.element.className = uploadEl.element.className.replace(/status-[\w-]+/g, '');
-		uploadEl.element.classList.add(`status-${upload.status}`);
-
-		// Update progress
-		const progress = uploadEl.element.querySelector('.progress');
-		if (progress) {
-			this.updateUploadItemProgress(uploadId, this.getStatusProgress(upload.status), upload.status);
-		}
-	}
-
-	showUploadProgress(uploadId, show = true) {
-		const uploadEl = this.uploadElements.get(uploadId);
-		const progress = uploadEl?.element?.querySelector('.progress');
-		if (!progress) return;
-
-		if (show) {
-			progress.style.removeProperty('animation');
-			progress.hidden = false;
-		} else {
-			progress.style.animation = 'fadeOut var(--transition-base)';
-			setTimeout(() => { progress.hidden = true; }, 300);
-		}
-	}
-
-	updateUploadItemProgress(uploadId, percent, status = null) {
-		const uploadEl = this.uploadElements.get(uploadId);
-		const progress = uploadEl?.element?.querySelector('.progress');
-		if (!progress) return;
-
-		const fill = progress.querySelector('.fill');
-		const details = progress.querySelector('.details');
-		const icon = progress.querySelector('.icon');
-
-		if (fill) fill.style.width = `${percent}%`;
-		if (status && details) details.textContent = this.statusMapping[status] || status;
-		if (status && icon) icon.innerHTML = this.getStatusIcon(status).outerHTML;
-	}
-
-	maybeLockUploads(fieldId) {
-		const fieldEl = this.fieldElements.get(fieldId);
-		if (!fieldEl?.ui?.dropZone) return;
-
-		const uploadCount = this.getFieldUploadCount(fieldId);
-		const maxFiles = fieldEl.config.destination === 'post_group' ? 20 : (fieldEl.config.maxFiles || 999);
-
-		fieldEl.ui.dropZone.hidden = uploadCount >= maxFiles;
-		fieldEl.element.classList.toggle('at-max-uploads', uploadCount >= maxFiles);
-
-		if (fieldEl.config.destination === 'post_group' && uploadCount >= maxFiles) {
-			this.a11y.announce('Maximum of 20 uploads reached.');
-		}
-	}
-
-	/*******************************************************************************
-	 * CLEANUP
-	 *******************************************************************************/
-
-	async clearUpload(uploadId) {
-		const uploadEl = this.uploadElements.get(uploadId);
-
-		if (uploadEl) {
-			this.revokePreviewUrl(uploadEl.preview);
-			if (uploadEl.element?.dataset.previewUrl) {
-				this.revokePreviewUrl(uploadEl.element.dataset.previewUrl);
-			}
-		}
-
-		this.uploadElements.delete(uploadId);
-		await this.uploadStore.delete(uploadId);
-	}
-
-	createPreviewUrl(file) {
-		const url = URL.createObjectURL(file);
-		this.previewUrls.add(url);
-		return url;
-	}
-
-	revokePreviewUrl(url) {
-		if (url?.startsWith('blob:')) {
-			URL.revokeObjectURL(url);
-			this.previewUrls.delete(url);
-		}
-	}
-
-	cleanupAllPreviewUrls() {
-		this.previewUrls.forEach(url => {
-			try { URL.revokeObjectURL(url); } catch (e) {}
-		});
-		this.previewUrls.clear();
-	}
-
-	/*******************************************************************************
-	 * RECOVERY & RESTORATION
-	 *******************************************************************************/
-
-	async showRecoveryNotification(byField) {
-		let totalUploads = 0;
-		let totalGroups = 0;
-
-		byField.forEach(field => {
-			totalUploads += field.uploads.length;
-			totalGroups += field.groups.size;
-		});
-
-		const notification = window.getTemplate('restoreNotification');
-		if (!notification) return;
-
-		const message = totalGroups > 0
-			? `${totalGroups} group${totalGroups > 1 ? 's' : ''} with ${totalUploads} upload${totalUploads > 1 ? 's' : ''} can be restored.`
-			: `${totalUploads} upload${totalUploads > 1 ? 's' : ''} can be recovered.`;
-
-		const detailsEl = notification.querySelector('.restore-details');
-		if (detailsEl) detailsEl.textContent = message;
-
-		// Build preview
-		for (const [fieldId, fieldData] of byField) {
-			const fieldTemplate = window.getTemplate('restoreField');
-			if (!fieldTemplate) continue;
-
-			const titleEl = fieldTemplate.querySelector('h3');
-			if (titleEl) titleEl.textContent = fieldId;
-
-			const itemGrid = fieldTemplate.querySelector('.item-grid.restore');
-
-			for (const upload of fieldData.uploads) {
-				const file = await this.getBlobData(upload.id);
-				if (!file) continue;
-
-				const uploadItem = this.createUploadElement({
-					id: upload.id,
-					preview: this.createPreviewUrl(file),
-					meta: upload.meta,
-					subtype: this.getSubtypeFromMime(file.type)
-				}, false);
-
-				uploadItem.dataset.fieldId = fieldId;
-				itemGrid?.appendChild(uploadItem);
-			}
-
-			notification.querySelector('.wrap')?.appendChild(fieldTemplate);
-		}
-
-		document.querySelector('.field.upload')?.appendChild(notification);
-
-		const dialog = document.querySelector('dialog.restore-uploads');
-		if (dialog) {
-			this.restoreModal = new window.jvbModal(dialog);
-			this.restoreSelection = new window.jvbHandleSelection({
-				container: dialog,
-				ui: {
-					selectAll: dialog.querySelector('#select-all-restore'),
-					count: dialog.querySelector('.selection-count')
-				}
-			});
-			this.restoreModal.handleOpen();
-		}
-	}
-
-	async handleRestoreSelected() {
-		const dialog = document.querySelector('dialog.restore-uploads');
-		if (!dialog) return;
-
-		const selected = [];
-		dialog.querySelectorAll('[type=checkbox]:checked').forEach(checkbox => {
-			const item = checkbox.closest('.item');
-			if (item) {
-				selected.push({
-					uploadId: item.dataset.uploadId,
-					fieldId: item.dataset.fieldId
-				});
-			}
-		});
-
-		if (selected.length > 0) {
-			await this.restoreUploads(selected);
-		}
-
-		this.cleanupRestoreModal();
-	}
-
-	async handleRestoreAll() {
-		const dialog = document.querySelector('dialog.restore-uploads');
-		if (!dialog) return;
-
-		const all = [];
-		dialog.querySelectorAll('.item.upload').forEach(item => {
-			all.push({
-				uploadId: item.dataset.uploadId,
-				fieldId: item.dataset.fieldId
-			});
-		});
-
-		await this.restoreUploads(all);
-		this.cleanupRestoreModal();
-	}
-
-	async restoreUploads(items) {
-		const byField = new Map();
-
-		items.forEach(item => {
-			if (!byField.has(item.fieldId)) {
-				byField.set(item.fieldId, []);
-			}
-			byField.get(item.fieldId).push(item.uploadId);
-		});
-
-		for (const [fieldId, uploadIds] of byField) {
-			await this.restoreFieldUploads(fieldId, uploadIds);
-		}
-	}
-
-	async restoreFieldUploads(fieldId, uploadIds) {
-		// Find or register field element
-		let fieldEl = this.fieldElements.get(fieldId);
-
-		if (!fieldEl) {
-			// Try to find by data attribute
-			const element = document.querySelector(`[data-uploader="${fieldId}"]`);
-			if (element) {
-				this.registerUploader(element);
-				fieldEl = this.fieldElements.get(fieldId);
-			}
-		}
-
-		if (!fieldEl) {
-			console.warn(`Field ${fieldId} not found for restoration`);
-			return;
-		}
-
-		// Show upload UI
-		if (fieldEl.ui.dropZone) fieldEl.ui.dropZone.hidden = true;
-		if (fieldEl.ui.groups?.display) fieldEl.ui.groups.display.hidden = false;
-
-		// Restore groups first
-		const groups = this.getFieldGroups(fieldId);
-		for (const group of groups) {
-			await this.restoreGroup(fieldId, group);
-		}
-
-		// Restore uploads
-		for (const uploadId of uploadIds) {
-			const upload = this.uploadStore.get(uploadId);
-			if (upload) {
-				await this.restoreUploadElement(fieldId, upload);
-			}
-		}
-
-		this.updateFieldState(fieldId);
-		this.refreshSortable(fieldId);
-		this.maybeLockUploads(fieldId);
-
-		// Auto-upload if configured
-		if (fieldEl.config.autoUpload && fieldEl.config.destination !== 'post_group') {
-			await this.queueUpload(fieldId);
-		}
-	}
-
-	async restoreGroup(fieldId, groupData) {
-		const group = await this.createGroup(fieldId, groupData.id);
-		if (!group) return;
-
-		// Restore field values
-		if (groupData.fields) {
-			const titleInput = group.element.querySelector('[name*="post_title"]');
-			const excerptInput = group.element.querySelector('[name*="post_excerpt"]');
-
-			if (titleInput && groupData.fields.post_title) {
-				titleInput.value = groupData.fields.post_title;
-			}
-			if (excerptInput && groupData.fields.post_excerpt) {
-				excerptInput.value = groupData.fields.post_excerpt;
-			}
-		}
-	}
-
-	async restoreUploadElement(fieldId, upload) {
-		const fieldEl = this.fieldElements.get(fieldId);
-		if (!fieldEl) return;
-
-		const file = await this.getBlobData(upload.id);
-		if (!file) return;
-
-		const preview = this.createPreviewUrl(file);
-		const element = this.createUploadElement({
-			id: upload.id,
-			preview,
-			meta: upload.meta,
-			subtype: this.getSubtypeFromMime(file.type)
-		}, fieldEl.config.destination === 'post_group');
-
-		// Determine location
-		let location;
-		if (upload.groupId) {
-			const groupEl = this.groupElements.get(upload.groupId);
-			location = groupEl?.grid || fieldEl.ui.preview;
-		} else {
-			location = fieldEl.ui.preview;
-		}
-
-		location.appendChild(element);
-		this.uploadElements.set(upload.id, { element, preview, location });
-
-		// Update upload status
-		upload.status = 'processed';
-		await this.uploadStore.save(upload);
-	}
-
-	handleClearCache() {
-		if (!confirm('Discard these uploads?')) return;
-		this.cleanupStoredData();
-		this.cleanupRestoreModal();
-	}
-
-	async cleanupStoredData() {
-		await this.uploadStore.clear();
-		await this.groupStore.clear();
-	}
-
-	cleanupRestoreModal() {
-		if (this.restoreModal) {
-			this.restoreModal.handleClose();
-			this.restoreSelection?.destroy();
-			this.restoreModal.destroy();
-			this.restoreModal.modal.remove();
-			this.restoreModal = null;
-			this.restoreSelection = null;
-		}
-	}
-
-	/*******************************************************************************
-	 * HELPER METHODS
-	 *******************************************************************************/
-
-	generateId(prefix) {
-		return `${prefix}_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
-	}
-
-	determineFieldId(fieldElement) {
-		const content = fieldElement.dataset.content ||
-			fieldElement.closest('dialog')?.dataset.content ||
-			fieldElement.closest('form')?.dataset.save || '';
-		const itemID = fieldElement.dataset.itemId ||
-			fieldElement.closest('dialog')?.dataset.itemId || '';
-		const field = fieldElement.dataset.field || '';
-
-		return `${content}_${itemID}_${field}`;
-	}
-
-	getFieldIdFromElement(el) {
-		const field = el.closest(this.selectors.field.field);
-		return field?.dataset.uploader || null;
-	}
-
-	getUploadIdFromElement(el) {
-		const item = el.closest(this.selectors.items.item);
-		return item?.dataset.uploadId || null;
-	}
-
-	getSubtypeFromMime(mimeType) {
-		if (mimeType.startsWith('image/')) return 'image';
-		if (mimeType.startsWith('video/')) return 'video';
-		return 'document';
-	}
-
-	getStatusIcon(status) {
-		return window.getIcon(this.queue.icons[status]);
-	}
-
-	getStatusProgress(status) {
-		const progress = {
-			'processing': 28,
-			'queued': 50,
-			'uploading': 66,
-			'pending': 75,
-			'server_processing': 89,
-			'completed': 100
-		};
-		return progress[status] || 0;
-	}
-
-	formatBytes(bytes, decimals = 2) {
-		if (bytes === 0) return '0 Bytes';
-		const k = 1024;
-		const sizes = ['Bytes', 'KB', 'MB', 'GB'];
-		const i = Math.floor(Math.log(bytes) / Math.log(k));
-		return parseFloat((bytes / Math.pow(k, i)).toFixed(decimals)) + ' ' + sizes[i];
-	}
-
-	createUploadElement(upload, draggable = false) {
-		const element = window.getTemplate('uploadItem');
-		if (!element) return null;
-
-		element.dataset.uploadId = upload.id;
-		element.dataset.subtype = upload.subtype || 'image';
-
-		const featured = element.querySelector('[name="featured"]');
-		const img = element.querySelector('img');
-		const video = element.querySelector('video');
-		const preview = element.querySelector('label > span');
-		const details = element.querySelector('details');
-
-		if (featured) featured.value = upload.id;
-
-		switch (upload.subtype) {
-			case 'image':
-				if (img) {
-					img.src = upload.preview;
-					img.alt = upload.meta?.originalName || '';
-				}
-				video?.remove();
-				preview?.remove();
-				break;
-			case 'video':
-				if (video) video.src = upload.preview;
-				img?.remove();
-				preview?.remove();
-				break;
-			case 'document':
-				const fileName = upload.meta?.originalName || '';
-				const ext = fileName.split('.').pop()?.toLowerCase() || '';
-				const iconMap = {
-					'pdf': 'file-pdf', 'csv': 'file-csv',
-					'doc': 'file-doc', 'docx': 'file-doc',
-					'txt': 'file-txt', 'xls': 'file-xls', 'xlsx': 'file-xls'
-				};
-				const icon = window.getIcon(iconMap[ext] || 'file');
-				if (preview) {
-					preview.innerText = fileName;
-					preview.prepend(icon);
-				}
-				img?.remove();
-				video?.remove();
-				break;
-		}
-
-		if (details) {
-			const template = window.getTemplate('uploadMeta');
-			if (template) details.append(template);
-		}
-
-		element.draggable = draggable;
-
-		// Update input IDs
-		element.querySelectorAll('input').forEach(input => {
-			const id = input.id;
-			if (id) {
-				const newId = id + upload.id;
-				const label = input.parentNode.querySelector(`label[for="${id}"]`);
-				input.id = newId;
-				if (label) label.htmlFor = newId;
-			}
-		});
-
-		return element;
-	}
-
-	/**
-	 * Get all files for a form
-	 */
-	async getFilesForForm(formElement) {
-		const uploadFields = formElement.querySelectorAll('[data-upload-field]');
-		const allFiles = [];
-
-		for (const field of uploadFields) {
-			const fieldId = this.determineFieldId(field);
-			const files = await this.getFilesForField(fieldId);
-			allFiles.push(...files);
-		}
-
-		return allFiles;
-	}
-
-	/**
-	 * Get all files for a field
-	 */
-	async getFilesForField(fieldId) {
-		const uploads = this.getFieldUploads(fieldId);
-		const files = [];
-
-		for (const upload of uploads) {
-			const file = await this.getBlobData(upload.id);
-			if (file) {
-				files.push({
-					file,
-					uploadId: upload.id,
-					fieldName: this.fieldElements.get(fieldId)?.config.name,
-					meta: upload.meta || {}
-				});
-			}
-		}
-
-		return files;
-	}
-
-	/*******************************************************************************
-	 * EVENT SYSTEM
-	 *******************************************************************************/
-
-	subscribe(callback) {
-		this.subscribers.add(callback);
-		return () => this.subscribers.delete(callback);
-	}
-
-	notify(event, data = {}) {
-		this.subscribers.forEach(cb => {
-			try { cb(event, data); } catch (e) { console.error('Subscriber error:', e); }
-		});
-	}
-
-	/*******************************************************************************
-	 * DESTROY
-	 *******************************************************************************/
-
-	destroy() {
-		document.removeEventListener('click', this.clickHandler);
-		document.removeEventListener('change', this.changeHandler);
-		document.removeEventListener('dragenter', this.dragEnterHandler);
-		document.removeEventListener('dragleave', this.dragLeaveHandler);
-		document.removeEventListener('dragover', this.dragOverHandler);
-		document.removeEventListener('drop', this.dropHandler);
-
-		this.selectionHandlers.forEach(handler => handler.destroy());
-		this.selectionHandlers.clear();
-
-		this.cleanupAllPreviewUrls();
-
-		this.sortableInstances.forEach(instance => instance?.destroy?.());
-		this.sortableInstances.clear();
-
-		this.uploadElements.clear();
-		this.fieldElements.clear();
-		this.groupElements.clear();
-		this.selected.clear();
-		this.subscribers.clear();
-	}
-}
-
-// Initialize
-document.addEventListener('DOMContentLoaded', async function() {
-	window.auth.subscribe((event) => {
-		if (event === 'auth-loaded') {
-			window.jvbUploads = new UploadManager();
-		}
-	});
-});
diff --git a/assets/js/concise/UploadManagerOld.js b/assets/js/concise/UploadManagerOld.js
deleted file mode 100644
index 3f1ae58..0000000
--- a/assets/js/concise/UploadManagerOld.js
+++ /dev/null
@@ -1,3141 +0,0 @@
-/**
- * UploadManager - Refactored for clarity
- *
- * Architecture:
- * - DataStores (fieldStore, uploadStore) = Recovery cache only, cleared after successful upload
- * - Maps (uploadElements, fieldElements) = Runtime DOM references
- * - Upload data flows: File → Process → Queue → Server → Clean up stores
- */
-class UploadManager {
-	constructor() {
-		// Load dependencies
-		this.queue = window.jvbQueue;
-		this.a11y = window.jvbA11y;
-		this.error = window.jvbError;
-		this.fieldStoreReady = false;
-		this.uploadStoreReady = false;
-		this.hasCheckedForUploads = false;
-		const {fields, uploads} = window.jvbStore.register(
-			'uploads',
-			[
-				{
-					storeName: 'fields',
-					keyPath: 'id',
-					indexes: [
-						{ name: 'fieldId', keyPath: 'fieldId' },
-						{ name: 'timestamp', keyPath: 'timestamp' },
-						{ name: 'content', keyPath: 'content' },
-						{ name: 'itemId', keyPath: 'itemId' },
-						{ name: 'status', keyPath: 'status' }
-					],
-					TTL: 7 * 24 * 60 * 60 * 1000, // 1 week
-					delayFetch: true
-				},
-				{
-					storeName: 'uploads',
-					keyPath: 'id',
-					storeBlobs: true,
-					indexes: [
-						{ name: 'fieldId', keyPath: 'fieldId' },
-						{ name: 'status', keyPath: 'status' },
-						{ name: 'groupId', keyPath: 'groupId' },
-						{ name: 'attachmentId', keyPath: 'attachmentId' }
-					],
-					delayFetch: true
-				}
-			]
-		);
-		this.fieldStore = fields;
-		this.uploadStore = uploads;
-
-		window.jvbUploadBlobs = this.uploadStore;
-
-		// Subscribe to store events
-		this.fieldStore.subscribe(this.handleFieldStoreEvent.bind(this));
-		this.uploadStore.subscribe(this.handleUploadStoreEvent.bind(this));
-
-		// RUNTIME DATA - DOM references and ephemeral state
-		this.uploadElements = new Map();  // uploadId → { element, preview, location }
-		this.fieldElements = new Map();   // fieldId → { element, ui, config }
-		this.groupElements = new Map();   // groupId → { element, grid, fieldId }
-
-		// Selection and UI state
-		this.selected = new Map();
-		this.selectionHandlers = new Map();
-		this.previewUrls = new Set();
-		this.sortableInstances = new Map();
-
-		// Worker for image processing
-		this.initWorker();
-
-		// Notification subscribers
-		this.subscribers = new Set();
-
-		// Selectors
-		this.selectors = {
-			field: {
-				field: '[data-upload-field]',
-				input: 'input[type="file"]',
-				dropZone: '.file-upload-container',
-				preview: '.item-grid.preview',
-				progress: '.image-progress'
-			},
-			groups: {
-				container: '.upload-group',
-				grid: '.item-grid.group',
-				header: '.group-header',
-				selectAll: '[name="select-all-group"]',
-				actions: '.group-actions',
-				count: '.selection-controls .info'
-			},
-			items: {
-				item: '[data-upload-id]',
-				checkbox: '[name*="select-item"]',
-				featured: '[name="featured"]',
-				details: 'details'
-			}
-		};
-
-		this.statusMapping = {
-			'received': 'Image Received',
-			'local_processing': 'Processing Image...',
-			'queued': 'Waiting to upload...',
-			'uploading': 'Uploading to Server',
-			'pending': 'Successfully sent to server. In line for further processing.',
-			'processing': 'Processing on server...',
-			'completed': 'Upload complete!',
-			'failed': 'Upload failed (will retry)',
-			'failed_permanent': 'Upload failed permanently'
-		};
-
-		this.init();
-	}
-
-	async init() {
-		// this.initializeFields();
-		this.initListeners();
-
-		// Queue integration - handle completion/failure
-		this.queue.subscribe((event, operation) => {
-			if (!['uploads', 'uploads/meta', 'uploads/groups'].includes(operation.endpoint)) {
-				return;
-			}
-
-			const fieldId = operation.data instanceof FormData
-				? operation.data.get('fieldId')
-				: operation.data?.fieldId;
-
-			switch(event) {
-				case 'cancel-operation':
-					if (fieldId) this.handleOperationCancelled(fieldId);
-					break;
-				case 'operation-status':
-					if (fieldId) this.updateFieldStatus(fieldId, operation.status);
-					break;
-				case 'operation-complete':
-					this.handleOperationComplete(operation, fieldId);
-					break;
-				case 'operation-failed':
-				case 'operation-failed-permanent':
-					this.handleOperationFailed(operation, fieldId);
-					break;
-			}
-		});
-
-		window.addEventListener('beforeunload', () => {
-			this.cleanupAllPreviewUrls();
-		});
-	}
-
-	initWorker() {
-		this.worker = {
-			worker: null,
-			timeout: null,
-			tasks: new Map(),
-			restart: { count: 0, max: 3 },
-			settings: {
-				timeout: 10000,
-				batchSize: 1,
-				maxConcurrent: 3,
-				restartAfterTimeout: true
-			}
-		};
-	}
-
-	/*******************************************************************************
-	 * FIELD MANAGEMENT
-	 *******************************************************************************/
-	scanFields(container, autoUpload) {
-		console.log(autoUpload, 'autoUpload');
-		const fields = container.querySelectorAll(this.selectors.field.field);
-		fields.forEach(uploader => this.registerUploader(uploader, autoUpload));
-	}
-
-	registerUploader(uploader, autoUpload) {
-		const fieldId = this.determineFieldId(uploader);
-		const config = this.extractFieldConfig(uploader, autoUpload);
-		const ui = this.buildFieldUI(uploader);
-
-		console.log(config, 'registering with config');
-		// Store field data with Sets for runtime
-		const fieldData = {
-			id: fieldId,
-			config: config,
-			uploads: new Set(),
-			groups: [],
-			state: 'ready',
-			timestamp: Date.now()
-		};
-
-		// Save to store (will convert Sets to Arrays automatically)
-		this.fieldStore.save(fieldData);
-
-		// Store DOM references separately
-		this.fieldElements.set(fieldId, { element: uploader, ui, config });
-
-		uploader.dataset.uploader = fieldId;
-		this.addFieldSelectionHandler(fieldId);
-
-		if (config.type !== 'single') {
-			this.initSortable(fieldId);
-		}
-
-		return fieldId;
-	}
-
-	extractFieldConfig(fieldElement, autoUpload) {
-		return {
-			autoUpload: autoUpload,
-			destination: fieldElement.dataset.destination || 'meta',
-			content: fieldElement.dataset.content || null,
-			mode: fieldElement.dataset.mode || 'direct',
-			type: fieldElement.dataset.type || 'single',
-			name: fieldElement.dataset.field,
-			itemID: fieldElement.dataset.itemId || 0,
-			maxFiles: parseInt(fieldElement.dataset.maxFiles) || 999,
-			subtype: fieldElement.dataset.subtype || 'image'
-		};
-	}
-
-	buildFieldUI(fieldElement) {
-		let UI = {
-			field: fieldElement,
-			input: fieldElement.querySelector(this.selectors.field.input),
-			dropZone: fieldElement.querySelector(this.selectors.field.dropZone),
-			preview: fieldElement.querySelector(this.selectors.field.preview),
-			progress: {
-				progress: fieldElement.querySelector(this.selectors.field.progress),
-				bar: fieldElement.querySelector('.bar'),
-				fill: fieldElement.querySelector('.fill'),
-				details: fieldElement.querySelector('.details'),
-				text: fieldElement.querySelector('.details .text'),
-				count: fieldElement.querySelector('.details .count')
-			}
-		};
-
-		let display = fieldElement.querySelector('.group-display');
-		if (display) {
-			UI.groups = {
-				display: display,
-				container: fieldElement.querySelector('.item-grid.groups'),
-				empty: fieldElement.querySelector('.empty-group'),
-				groups: new Map()
-			};
-		}
-
-		return UI;
-	}
-
-	/*******************************************************************************
-	 * SORTABLE INITIALIZATION
-	 *******************************************************************************/
-	initSortable(fieldId) {
-		if (!window.Sortable) return;
-
-		// Mount MultiDrag plugin once
-		if (!Sortable._multiDragMounted && Sortable.MultiDrag) {
-			Sortable.mount(new Sortable.MultiDrag());
-			Sortable._multiDragMounted = true;
-		}
-
-		const fieldEl = this.fieldElements.get(fieldId);
-		if (!fieldEl) return;
-
-		// Initialize sortable on all existing grids
-		const grids = fieldEl.element.querySelectorAll('.item-grid.preview, .item-grid.group');
-		grids.forEach(grid => {
-			const groupId = grid.classList.contains('group')
-				? grid.closest('.upload-group')?.dataset.groupId
-				: null;
-			this.createSortableForGrid(grid, fieldId, groupId);
-		});
-
-		// Special handler for empty-group
-		const emptyGroup = fieldEl.element.querySelector('.empty-group');
-		if (emptyGroup && !emptyGroup.sortableInstance) {
-			emptyGroup.sortableInstance = new Sortable(emptyGroup, {
-				animation: 150,
-				draggable: '.item',
-				multiDrag: true,
-				selectedClass: 'selected-for-drag',
-				avoidImplicitDeselect: true,
-				group: { name: fieldId, pull: false, put: true },
-				ghostClass: 'sortable-ghost',
-				chosenClass: 'sortable-chosen',
-				dragClass: 'sortable-drag',
-				onEnd: (evt) => this.handleDrop(evt, fieldId)
-			});
-		}
-	}
-
-	syncSortableSelection(fieldId, selectedItems) {
-		// Update Sortable's selection state to match checkboxes
-		this.sortableInstances.forEach((instance, key) => {
-			if (key.startsWith(fieldId)) {
-				const grid = instance.el;
-				const items = grid.querySelectorAll('.item');
-
-				items.forEach(item => {
-					const uploadId = item.dataset.uploadId;
-					const shouldBeSelected = selectedItems.has(uploadId);
-
-					if (shouldBeSelected) {
-						Sortable.utils.select(item);
-					} else {
-						Sortable.utils.deselect(item);
-					}
-				});
-			}
-		});
-	}
-
-	handleDrop(evt, fieldId) {
-		const dropTarget = evt.to;
-		const sourceTarget = evt.from;
-		const items = evt.items?.length > 0 ? evt.items : [evt.item];
-		const uploadIds = items.map(item => item.dataset.uploadId);
-
-		// Determine drop target type
-		const targetType = this.getDropTargetType(dropTarget);
-
-		switch (targetType) {
-			case 'empty-group':
-				this.handleDropToEmptyGroup(items, uploadIds, fieldId);
-				break;
-
-			case 'preview':
-				this.handleDropToPreview(items, uploadIds, fieldId);
-				break;
-
-			case 'group':
-				this.handleDropToGroup(items, uploadIds, dropTarget, sourceTarget, fieldId);
-				break;
-			default:
-				// Fallback: return to preview
-				this.handleDropToPreview(items, uploadIds, fieldId);
-				break;
-		}
-
-		// Update UI
-		this.updateSortableState(dropTarget);
-		if (sourceTarget !== dropTarget) {
-			this.updateSortableState(sourceTarget);
-		}
-	}
-
-	/**
-	 * Determine what type of drop target this is
-	 */
-	getDropTargetType(target) {
-		if (target.classList.contains('empty-group')) {
-			return 'empty-group';
-		}
-
-		if (target.classList.contains('preview')) {
-			return 'preview';
-		}
-
-		if (target.classList.contains('group')) {
-			return 'group';
-		}
-
-		return 'unknown';
-	}
-
-	/**
-	 * Handle drop to group: add to existing group
-	 */
-	handleDropToGroup(items, uploadIds, dropTarget, sourceTarget, fieldId) {
-		try {
-			// If same container, it's just a reorder
-			if (dropTarget === sourceTarget) {
-				this.handleReorder({ to: dropTarget, items: items });
-				return;
-			}
-
-			// Moving to different group
-			uploadIds.forEach(uploadId => {
-				this.addToGroup(uploadId, dropTarget, false);
-			});
-
-			this.schedulePersistance(fieldId);
-
-			const message = items.length > 1
-				? `Moved ${items.length} items to group`
-				: 'Moved item to group';
-			this.a11y.announce(message);
-
-			// Clear selection
-			const handler = this.selectionHandlers.get(fieldId);
-			handler?.clearSelection();
-		}  catch (error) {
-			this.handleDropError(items, fieldId, error);
-		}
-	}
-
-	/**
-	 * Handle drop to preview: remove from groups
-	 */
-	handleDropToPreview(items, uploadIds, fieldId) {
-		try {
-			uploadIds.forEach(uploadId => {
-				this.removeFromGroup(uploadId);
-			});
-
-			this.schedulePersistance(fieldId);
-
-			const message = items.length > 1
-				? `Moved ${items.length} items to preview`
-				: 'Moved item to preview';
-			this.a11y.announce(message);
-
-			// Clear selection
-			const handler = this.selectionHandlers.get(fieldId);
-			handler?.clearSelection();
-		} catch (error) {
-			this.handleDropError(items, fieldId, error);
-		}
-	}
-
-	/**
-	 * Handle drop errors consistently
-	 */
-	handleDropError(items, fieldId, error, message = 'An error occurred') {
-		console.error('Drop error:', error);
-
-		// Return items to preview as fallback
-		const fieldEl = this.fieldElements.get(fieldId);
-		if (fieldEl?.ui?.preview) {
-			items.forEach(item => fieldEl.ui.preview.appendChild(item));
-		}
-
-		this.a11y.announce(`${message}. Items returned to preview.`);
-	}
-
-	/**
-	 * Handle drop to group: add to existing group
-	 */
-	handleDropToEmptyGroup(items, uploadIds, fieldId) {
-		try {
-			const group = this.createGroup(fieldId);
-			if (!group) {
-				this.handleDropError(items, fieldId, new Error('Group creation failed'), 'Failed to create group');
-				return;
-			}
-
-			// Move items to new group
-			items.forEach((item, index) => {
-				group.grid.appendChild(item);
-				this.addToGroup(uploadIds[index], group.grid, false);
-			});
-
-			this.schedulePersistance(fieldId);
-
-			const message = items.length > 1
-				? `Created group with ${items.length} items`
-				: 'Created group with item';
-			this.a11y.announce(message);
-
-			// Clear selection after move
-			const handler = this.selectionHandlers.get(fieldId);
-			handler?.clearSelection();
-		} catch (error) {
-			this.handleDropError(items, fieldId, error);
-		}
-	}
-
-	/**
-	 * Update sortable enabled/disabled state based on item count
-	 */
-	updateSortableState(grid) {
-		const sortable = grid?.sortableInstance;
-		if (!sortable) return;
-
-		// const hasItems = grid.querySelectorAll('.item').length > 0;
-		sortable.option('disabled', false);
-	}
-
-	/**
-	 * Refresh sortable for a field (call after adding/removing items dynamically)
-	 */
-	refreshSortable(fieldId) {
-		const fieldEl = this.fieldElements.get(fieldId);
-		if (!fieldEl) return;
-
-		const grids = fieldEl.element.querySelectorAll('.item-grid.preview, .item-grid.group');
-		grids.forEach(grid => this.updateSortableState(grid));
-	}
-
-	handleReorder(evt) {
-		const grid = evt.to;
-		const fieldWrapper = grid.closest('.field, .upload');
-		if (!fieldWrapper) return;
-
-		// Get current order from DOM
-		let items = Array.from(grid.querySelectorAll('.item:not(.sortable-ghost):not(.sortable-clone)'))
-			.map(upload => upload.dataset.uploadId)
-			.filter(id => id);
-
-
-		// Update hidden input (for form submission)
-		let hiddenInput = fieldWrapper.querySelector('input[type="hidden"]');
-		if (hiddenInput && items.length > 0) {
-			hiddenInput.value = items.join(',');
-		}
-
-		// Update fieldState with new order
-		const fieldId = this.getFieldIdFromElement(grid);
-		if (fieldId) {
-			const fieldData = this.getFieldData(fieldId);
-
-			// If reordering within a group, update that group's uploads array
-			if (grid.classList.contains('group')) {
-				const groupId = grid.dataset.groupId;
-				const group = fieldData?.groups?.find(g => g.id === groupId);
-				if (group) {
-					group.uploads = items; // Update order
-				}
-			}
-			// If reordering in preview, the order is implicit by DOM position
-			// (we don't store preview order separately)
-
-			this.schedulePersistance(fieldId);
-		}
-
-		this.a11y.announce('Item reordered');
-
-		fieldWrapper.dispatchEvent(new CustomEvent('jvb-items-reordered', {
-			detail: {
-				from: evt.from,
-				to: evt.to,
-				oldIndex: evt.oldIndex,
-				newIndex: evt.newIndex,
-				items: items
-			},
-			bubbles: true
-		}));
-	}
-
-	/*******************************************************************************
-	 * FILE DROP HANDLERS
-	 *******************************************************************************/
-
-	initListeners() {
-		this.clickHandler = this.handleClick.bind(this);
-		this.changeHandler = this.handleChange.bind(this);
-
-		document.addEventListener('click', this.clickHandler);
-		document.addEventListener('change', this.changeHandler);
-
-		this.dragEnterHandler = this.handleExternalDragEnter.bind(this);
-		this.dragLeaveHandler = this.handleExternalDragLeave.bind(this);
-		this.dragOverHandler = this.handleExternalDragOver.bind(this);
-		this.dropHandler = this.handleExternalDrop.bind(this);
-
-		document.addEventListener('dragenter', this.dragEnterHandler);
-		document.addEventListener('dragleave', this.dragLeaveHandler);
-		document.addEventListener('dragover', this.dragOverHandler);
-		document.addEventListener('drop', this.dropHandler);
-	}
-
-	handleExternalDragLeave(e) {
-		const dropZone = e.target.closest(this.selectors.field.dropZone);
-		if (dropZone && !dropZone.contains(e.relatedTarget)) {
-			dropZone.classList.remove('dragover');
-		}
-	}
-
-	handleExternalDragEnter(e) {
-		if (!e.dataTransfer.types.includes('Files')) return;
-		const dropZone = e.target.closest(this.selectors.field.dropZone);
-		if (dropZone) {
-			e.preventDefault();
-			dropZone.classList.add('dragover');
-		}
-	}
-
-	handleExternalDragOver(e) {
-		if (!e.dataTransfer.types.includes('Files')) return;
-		const dropZone = e.target.closest(this.selectors.field.dropZone);
-		if (dropZone) {
-			e.preventDefault();
-			e.dataTransfer.dropEffect = 'copy';
-		}
-	}
-
-	handleExternalDrop(e) {
-		const dropZone = e.target.closest(this.selectors.field.dropZone);
-		if (!dropZone) return;
-
-		e.preventDefault();
-		dropZone.classList.remove('dragover');
-
-		const files = Array.from(e.dataTransfer.files);
-		if (files.length === 0) return;
-
-		const fieldId = this.getFieldIdFromElement(dropZone);
-		if (fieldId) {
-			this.processFiles(fieldId, files);
-			this.a11y.announce(`${files.length} file(s) dropped for upload`);
-		}
-	}
-
-	/*******************************************************************************
-	 * CLICK & CHANGE HANDLERS
-	 *******************************************************************************/
-
-	handleClick(e) {
-		// Trigger file input
-		if (e.target.matches(this.selectors.field.dropZone) ||
-			e.target.closest(this.selectors.field.dropZone)) {
-			const dropZone = e.target.closest(this.selectors.field.dropZone);
-			if (dropZone && !e.target.matches('input, button, a')) {
-				const input = dropZone.querySelector(this.selectors.field.input);
-				input?.click();
-			}
-		}
-
-		// Group actions
-		const actionButton = e.target.closest('[data-action]');
-		if (actionButton) {
-			this.handleAction(actionButton);
-		}
-	}
-
-	handleChange(e) {
-		const fieldId = this.getFieldIdFromElement(e.target);
-
-		// File input change
-		if (e.target.matches(this.selectors.field.input)) {
-			const files = Array.from(e.target.files);
-			if (files.length > 0 && fieldId) {
-				this.processFiles(fieldId, files);
-			}
-		}
-
-		// Meta field changes
-		if (fieldId) {
-			const fieldData = this.getFieldData(fieldId);
-			if (!fieldData.config.autoUpload) {
-				return;
-			}
-			if (fieldData?.config.destination === 'post_group') {
-				this.handleGroupMetaChange(e.target);
-			} else {
-				this.queueUploadMeta(e);
-			}
-		}
-	}
-
-	/*******************************************************************************
-	 * FILE PROCESSING
-	 *******************************************************************************/
-
-	async processFiles(fieldId, files) {
-		const fieldData = this.getFieldData(fieldId);
-		const fieldEl = this.fieldElements.get(fieldId);
-		if (!fieldData || !fieldEl) return;
-
-		// Show group display, hide upload zone
-		if (fieldEl.ui.dropZone) {
-			fieldEl.ui.dropZone.hidden = true;
-		}
-		if (fieldEl.ui.groups?.display) {
-			fieldEl.ui.groups.display.hidden = false;
-		}
-
-		const totalFiles = files.length;
-		let processedCount = 0;
-
-		this.updateUploadProgress(fieldId, 0, totalFiles, 'Processing files...');
-
-		const processPromises = Array.from(files).map(async (file) => {
-			try {
-				const uploadId = `upload_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
-
-				// Create initial upload data
-				const uploadData = {
-					id: uploadId,
-					attachmentId: null,
-					fieldId: fieldId,
-					status: 'local_processing',
-					groupId: null,
-					meta: {
-						originalName: file.name,
-						size: file.size,
-						type: file.type
-					}
-				};
-
-				// Save initial data
-				await this.uploadStore.save(uploadData);
-
-				// Process file
-				const preview = this.createPreviewUrl(file);
-				const processedFile = file.type.startsWith('image/')
-					? await this.processImage(file, fieldData.config.subtype)
-					: file;
-
-				// Show progress
-				this.showUploadProgress(uploadId, true);
-				this.updateUploadItemProgress(uploadId, 50, 'local_processing');
-
-				// Store blob data (this updates the existing uploadData)
-				await this.saveBlobData(uploadId, processedFile || file);
-
-				// Create DOM element
-				const subtype = this.getSubtypeFromMime(file.type);
-				const element = this.createUploadElement({
-					id: uploadId,
-					preview: preview,
-					meta: uploadData.meta,
-					subtype: subtype
-				}, fieldData.config.destination === 'post_group');
-
-				// Add to preview grid
-				if (fieldEl.ui.preview) {
-					fieldEl.ui.preview.appendChild(element);
-
-					// Store runtime element data
-					this.uploadElements.set(uploadId, {
-						element: element,
-						preview: preview,
-						location: fieldEl.ui.preview
-					});
-				}
-
-				// Update status (gets existing data with blobData intact)
-				const storedUpload = this.uploadStore.get(uploadId);
-				if (storedUpload) {
-					storedUpload.status = 'processed';
-					await this.uploadStore.save(storedUpload);
-				}
-
-				// Add to field
-				fieldData.uploads.add(uploadId);
-				await this.saveFieldData(fieldData);
-
-				// Update progress
-				processedCount++;
-				this.updateUploadProgress(fieldId, processedCount, totalFiles, 'Processing files...');
-				this.updateUploadItemProgress(uploadId, 100, 'processed');
-
-				// Fade out progress
-				setTimeout(() => this.showUploadProgress(uploadId, false), 1000);
-
-				return uploadId;
-
-			} catch (error) {
-				console.error('Error processing file:', file.name, error);
-				processedCount++;
-				this.updateUploadProgress(fieldId, processedCount, totalFiles, 'Processing files...');
-				return null;
-			}
-		});
-
-		await Promise.all(processPromises);
-
-		this.updateFieldState(fieldId);
-		this.refreshSortable(fieldId);
-
-		// Queue for upload if in direct mode
-		if (fieldData.config.autoUpload && fieldData.config.destination !== 'post_group') {
-			await this.queueUpload(fieldId);
-			this.maybeLockUploads(fieldId);
-		}
-	}
-
-	/*******************************************************************************
-	 * IMAGE PROCESSING
-	 *******************************************************************************/
-
-	async processImage(file, uploadId) {
-		const timeout = this.worker.settings.timeout;
-
-		return new Promise((resolve, reject) => {
-			let timeoutId;
-			let taskCompleted = false;
-
-			timeoutId = setTimeout(() => {
-				if (!taskCompleted) {
-					taskCompleted = true;
-					this.worker.tasks.delete(uploadId);
-					if (this.worker.settings.restartAfterTimeout) {
-						this.restartCompressionWorker();
-					}
-					reject(new Error(`Processing timeout for ${file.name}`));
-				}
-			}, timeout);
-
-			this.worker.tasks.set(uploadId, { file, timeoutId });
-
-			this.handleProcess(file, uploadId)
-				.then(result => {
-					if (!taskCompleted) {
-						taskCompleted = true;
-						clearTimeout(timeoutId);
-						this.worker.tasks.delete(uploadId);
-						resolve(result);
-					}
-				})
-				.catch(error => {
-					if (!taskCompleted) {
-						taskCompleted = true;
-						clearTimeout(timeoutId);
-						this.worker.tasks.delete(uploadId);
-						reject(error);
-					}
-				});
-		});
-	}
-
-	async handleProcess(file, uploadId) {
-		if (!file.type.startsWith('image/')) {
-			return file;
-		}
-
-		const maxDimension = this.getMaxDimension();
-		const quality = 0.85;
-
-		if (this.shouldUseWorker(file)) {
-			try {
-				if (!this.worker.worker) {
-					this.initCompressionWorker();
-				}
-				if (this.worker.worker) {
-					return await this.processWithWorker(file, uploadId, maxDimension, quality);
-				}
-			} catch (error) {
-				console.warn('Worker processing failed, falling back to main thread:', error);
-			}
-		}
-
-		return await this.processOnMainThread(file, maxDimension, quality);
-	}
-
-	async processOnMainThread(file, maxDimension, quality) {
-		return new Promise((resolve, reject) => {
-			const img = new Image();
-			const canvas = document.createElement('canvas');
-			const ctx = canvas.getContext('2d');
-			let objectUrl = null;
-
-			const cleanup = () => {
-				img.onload = null;
-				img.onerror = null;
-				if (objectUrl) {
-					URL.revokeObjectURL(objectUrl);
-					objectUrl = null;
-				}
-				canvas.width = 1;
-				canvas.height = 1;
-				ctx.clearRect(0, 0, 1, 1);
-			};
-
-			img.onload = () => {
-				try {
-					const { width, height } = this.calculateOptimalDimensions(img, maxDimension);
-					canvas.width = width;
-					canvas.height = height;
-
-					ctx.imageSmoothingEnabled = true;
-					ctx.imageSmoothingQuality = 'high';
-					ctx.drawImage(img, 0, 0, width, height);
-
-					const outputFormat = this.getOptimalFormat(file);
-					const outputQuality = this.getOptimalQuality(file, quality);
-
-					canvas.toBlob(
-						(blob) => {
-							cleanup();
-							if (blob) {
-								const processedFile = new File(
-									[blob],
-									this.getProcessedFileName(file, outputFormat),
-									{ type: outputFormat, lastModified: Date.now() }
-								);
-								resolve(processedFile);
-							} else {
-								reject(new Error('Canvas toBlob failed'));
-							}
-						},
-						outputFormat,
-						outputQuality
-					);
-
-				} catch (error) {
-					cleanup();
-					reject(new Error(`Canvas processing failed: ${error.message}`));
-				}
-			};
-
-			img.onerror = () => {
-				cleanup();
-				reject(new Error(`Failed to load image: ${file.name}`));
-			};
-
-			try {
-				objectUrl = this.createPreviewUrl(file);
-				img.src = objectUrl;
-			} catch (error) {
-				cleanup();
-				reject(new Error(`Failed to create object URL: ${error.message}`));
-			}
-		});
-	}
-
-	getOptimalFormat(file) {
-		if (file.type === 'image/gif' || file.type === 'image/svg+xml') {
-			return file.type;
-		}
-		return this.supportsWebP() ? 'image/webp' : 'image/jpeg';
-	}
-
-	getOptimalQuality(file, requestedQuality) {
-		if (file.size < 500 * 1024) return Math.max(requestedQuality, 0.9);
-		if (file.size < 2 * 1024 * 1024) return requestedQuality;
-		return Math.min(requestedQuality, 0.8);
-	}
-
-	getProcessedFileName(originalFile, outputFormat) {
-		const baseName = originalFile.name.replace(/\.[^/.]+$/, '');
-		const extensions = {
-			'image/webp': '.webp',
-			'image/jpeg': '.jpg',
-			'image/png': '.png',
-			'image/gif': '.gif'
-		};
-		return baseName + (extensions[outputFormat] || '.jpg');
-	}
-
-	getMaxDimension() {
-		const screenWidth = window.screen.width;
-		const devicePixelRatio = window.devicePixelRatio || 1;
-		if (screenWidth * devicePixelRatio > 2560) return 2400;
-		if (screenWidth * devicePixelRatio > 1920) return 1920;
-		return 1200;
-	}
-
-	shouldUseWorker(file) {
-		return this.worker.worker &&
-			file.size > 1024 * 1024 &&
-			typeof OffscreenCanvas !== 'undefined';
-	}
-
-	async processWithWorker(file, uploadId, maxDimension, quality) {
-		return new Promise((resolve, reject) => {
-			if (!this.worker.worker) {
-				reject(new Error('Worker not available'));
-				return;
-			}
-
-			const messageId = `${uploadId}_${Date.now()}`;
-
-			const messageHandler = (e) => {
-				if (e.data.messageId !== messageId) return;
-
-				this.worker.worker.removeEventListener('message', messageHandler);
-				this.worker.worker.removeEventListener('error', errorHandler);
-
-				if (e.data.success) {
-					const processedFile = new File(
-						[e.data.blob],
-						this.getProcessedFileName(file, e.data.format || 'image/webp'),
-						{ type: e.data.format || 'image/webp', lastModified: Date.now() }
-					);
-					resolve(processedFile);
-				} else {
-					reject(new Error(e.data.error || 'Worker processing failed'));
-				}
-			};
-
-			const errorHandler = (error) => {
-				this.worker.worker.removeEventListener('message', messageHandler);
-				this.worker.worker.removeEventListener('error', errorHandler);
-				reject(new Error(`Worker error: ${error.message}`));
-			};
-
-			this.worker.worker.addEventListener('message', messageHandler);
-			this.worker.worker.addEventListener('error', errorHandler);
-
-			this.worker.worker.postMessage({
-				messageId,
-				file,
-				maxDimension,
-				quality,
-				outputFormat: this.getOptimalFormat(file)
-			});
-		});
-	}
-
-	restartCompressionWorker() {
-		if (this.worker.worker) {
-			this.worker.worker.terminate();
-			this.worker.worker = null;
-		}
-		this.worker.tasks.clear();
-		if (this.worker.restart.count >= this.worker.restart.max) {
-			console.error('Max worker restarts reached, disabling worker');
-			return;
-		}
-		this.worker.restart.count++;
-		this.initCompressionWorker();
-	}
-
-	initCompressionWorker() {
-		if (this.worker.worker || typeof Worker === 'undefined') return;
-
-		try {
-			const workerScript = `
-				self.onmessage = async function(e) {
-					const { messageId, file, maxDimension, quality, outputFormat } = e.data;
-					try {
-						const bitmap = await createImageBitmap(file);
-						const scale = Math.min(maxDimension / bitmap.width, maxDimension / bitmap.height, 1);
-						const width = Math.round(bitmap.width * scale);
-						const height = Math.round(bitmap.height * scale);
-						const canvas = new OffscreenCanvas(width, height);
-						const ctx = canvas.getContext('2d');
-						ctx.imageSmoothingEnabled = true;
-						ctx.imageSmoothingQuality = 'high';
-						ctx.drawImage(bitmap, 0, 0, width, height);
-						bitmap.close();
-						const blob = await canvas.convertToBlob({ type: outputFormat, quality: quality });
-						self.postMessage({ messageId, success: true, blob: blob, format: outputFormat });
-					} catch (error) {
-						self.postMessage({ messageId, success: false, error: error.message });
-					}
-				};
-			`;
-
-			const blob = new Blob([workerScript], { type: 'application/javascript' });
-			this.worker.worker = new Worker(this.createPreviewUrl(blob));
-
-		} catch (error) {
-			console.warn('Failed to initialize compression worker:', error);
-			this.worker.worker = null;
-		}
-	}
-
-	calculateOptimalDimensions(img, maxDimension) {
-		let { width, height } = img;
-		if (width <= maxDimension && height <= maxDimension) {
-			return { width, height };
-		}
-		const scale = Math.min(maxDimension / width, maxDimension / height);
-		return {
-			width: Math.round(width * scale),
-			height: Math.round(height * scale)
-		};
-	}
-
-	supportsWebP() {
-		const canvas = document.createElement('canvas');
-		return canvas.toDataURL('image/webp').indexOf('data:image/webp') === 0;
-	}
-
-	createPreviewUrl(file) {
-		const url = URL.createObjectURL(file);
-		if (!this.previewUrls) this.previewUrls = new Set();
-		this.previewUrls.add(url);
-		return url;
-	}
-
-	revokePreviewUrl(url) {
-		if (url?.startsWith('blob:')) {
-			URL.revokeObjectURL(url);
-			this.previewUrls?.delete(url);
-		}
-	}
-
-	/*******************************************************************************
-	 * QUEUE INTEGRATION
-	 *******************************************************************************/
-
-	async submitUploads(fieldId) {
-		const fieldData = this.getFieldData(fieldId);
-		const fieldEl = this.fieldElements.get(fieldId);
-		if (!fieldData?.uploads || fieldData.uploads.size === 0) {
-			return;
-		}
-
-		let uploadIds = Array.from(fieldData.uploads);
-		if (uploadIds.length === 0) {
-			this.error.log('No uploads to upload', {
-				component: 'UploadManager',
-				action: 'submitGroupedUploads',
-				fieldId: fieldId
-			});
-			return;
-		}
-
-		const fieldGroups = this.getFieldGroups(fieldId);
-
-		if (fieldGroups.length === 0) {
-			this.error.log('No groups created for post_group upload', {
-				component: 'UploadManager',
-				action: 'submitGroupedUploads',
-				fieldId: fieldId
-			});
-			return;
-		}
-
-		// Build posts array from groups
-		const posts = [];
-		const formData = new FormData();
-		let uploadMap = [];
-
-		// Process each group
-		for (const group of fieldGroups) {
-			const post = {
-				images: [],
-				fields: {}
-			};
-
-			// Add group metadata
-			for (let [name, value] of Object.entries(group.changes)) {
-				post.fields[name] = value;
-			}
-
-			// Get uploads for this group
-			const groupUploadIds = uploadIds.filter(uploadId => {
-				const upload = this.uploadStore.get(uploadId);
-				return upload?.groupId === group.id;
-			});
-
-			// Add files for this group
-			for (const uploadId of groupUploadIds) {
-				const file = await this.getBlobData(uploadId);
-				if (file) {
-					formData.append('files[]', file);
-
-					const imageData = {
-						upload_id: uploadId,
-						index: uploadMap.length
-					};
-
-					// Check if featured
-					const uploadEl = this.uploadElements.get(uploadId);
-					const radioInput = uploadEl?.element?.querySelector('[name="featured"]');
-					if (radioInput?.checked) {
-						post.fields.featured = uploadId;
-					}
-
-					post.images.push(imageData);
-					uploadMap.push(uploadId);
-				}
-			}
-
-			posts.push(post);
-		}
-
-		// Handle remaining uploads (without groupId) - each becomes its own post
-		const remainingUploadIds = uploadIds.filter(uploadId => {
-			const upload = this.uploadStore.get(uploadId);
-			return !upload?.groupId;
-		});
-
-		for (const uploadId of remainingUploadIds) {
-			const post = {
-				images: [],
-				fields: {}
-			};
-
-			const file = await this.getBlobData(uploadId);
-			if (file) {
-				formData.append('files[]', file);
-
-				const imageData = {
-					upload_id: uploadId,
-					index: uploadMap.length
-				};
-				post.images.push(imageData);
-				uploadMap.push(uploadId);
-			}
-
-			posts.push(post);
-		}
-
-		// Add metadata to FormData
-		formData.append('content', fieldData.config.content);
-		formData.append('user', fieldData.config.itemID);
-		formData.append('posts', JSON.stringify(posts));
-		formData.append('upload_ids', JSON.stringify(uploadMap));
-
-		const operation = {
-			endpoint: 'uploads/groups',
-			method: 'POST',
-			data: formData,
-			title: `Creating ${posts.length} ${fieldData.config.content}${posts.length > 1 ? 's' : ''} from uploads...`,
-			popup: `Creating ${posts.length} post${posts.length > 1 ? 's' : ''}...`,
-			canMerge: false,
-			headers: {
-				'action_nonce': window.auth.getNonce('dash')
-			},
-			append: '_upload',
-		};
-
-		try {
-			const operationId = await this.queue.addToQueue(operation);
-
-			// Update upload statuses
-			uploadIds.forEach(uploadId => {
-				const upload = this.uploadStore.get(uploadId);
-				if (upload) {
-					upload.operationId = operationId;
-					upload.status = 'queued';
-					this.uploadStore.save(upload);
-					this.updateUploadStatus(uploadId, 'queued');
-				}
-			});
-
-			fieldData.operationId = operationId;
-			await this.saveFieldData(fieldData);
-
-			this.a11y.announce(`Creating ${posts.length} post${posts.length > 1 ? 's' : ''} from your uploads`);
-
-			return operationId;
-		} catch (error) {
-			this.error.log(error, {
-				component: 'UploadManager',
-				action: 'submitGroupedUploads',
-				fieldId: fieldId
-			});
-			throw error;
-		}
-	}
-
-	async queueUpload(fieldId) {
-		const fieldData = this.getFieldData(fieldId);
-		if (!fieldData?.uploads || fieldData.uploads.size === 0) return;
-
-		const uploads = Array.from(fieldData.uploads);
-		const data = this.prepareUploadData(fieldData, uploads);
-
-		this.a11y.announce('Queuing for upload');
-
-		const operation = {
-			endpoint: 'uploads',
-			method: 'POST',
-			data: data,
-			title: `Uploading ${uploads.length} file${uploads.length > 1 ? 's' : ''} to server...`,
-			popup: `Uploading ${uploads.length} file${uploads.length > 1 ? 's' : ''}...`,
-			canMerge: false,
-			headers: { 'action_nonce': window.auth.getNonce('dash') },
-			append: '_upload'
-		};
-
-		try {
-			const operationId = await this.queue.addToQueue(operation);
-
-			// Update upload statuses
-			uploads.forEach(uploadId => {
-				const upload = this.uploadStore.get(uploadId);
-				if (upload) {
-					upload.operationId = operationId;
-					upload.status = 'queued';
-					this.uploadStore.save(upload);
-					this.updateUploadStatus(uploadId, 'queued');
-				}
-			});
-
-			fieldData.operationId = operationId;
-			await this.saveFieldData(fieldData);
-
-			return operationId;
-		} catch (error) {
-			throw error;
-		}
-	}
-
-	async prepareUploadData(fieldData, uploads) {
-		const formData = new FormData();
-		formData.append('content', fieldData.config.content);
-		formData.append('mode', fieldData.config.mode);
-		formData.append('field_name', fieldData.config.name);
-		formData.append('fieldId', fieldData.id);
-		formData.append('field_type', fieldData.config.type);
-		formData.append('subtype', fieldData.config.subtype);
-		formData.append('item_id', fieldData.config.itemID);
-		formData.append('destination', fieldData.config.destination || 'meta');
-
-		let uploadMap = [];
-
-
-		const blobPromises = uploads.map(async (uploadId) => {
-			const upload = this.uploadStore.get(uploadId);
-			if (!upload) return;
-
-			const file = await this.getBlobData(uploadId);
-			if (file) {
-				formData.append('files[]', file);
-				uploadMap.push(upload.id);
-			}
-		});
-
-		await Promise.all(blobPromises);
-
-		formData.append('upload_ids', JSON.stringify(uploadMap));
-		return formData;
-	}
-
-	async queueUploadMeta(e) {
-		const uploadId = this.getUploadIdFromElement(e.target);
-		const upload = this.uploadStore.get(uploadId);
-		if (!upload) return;
-
-		const fieldData = this.getFieldData(upload.fieldId);
-		if (!fieldData) return;
-
-		let data = {};
-		data[e.target.name] = e.target.value;
-
-		upload.meta = { ...upload.meta, ...data };
-		await this.uploadStore.save(upload);
-
-		let queueData = {};
-		queueData[upload.attachmentId ?? upload.id] = upload.meta;
-
-		const operation = {
-			endpoint: 'uploads/meta',
-			method: 'POST',
-			data: queueData,
-			title: 'Updating meta',
-			canMerge: true,
-			headers: { 'action_nonce': window.auth.getNonce('dash') }
-		};
-
-		try {
-			await this.queue.addToQueue(operation);
-		} catch (error) {
-			this.error.log(error, {
-				component: 'UploadManager',
-				action: 'sendMetaUpdate',
-				uploadId: upload.id
-			});
-		}
-	}
-
-	/*******************************************************************************
-	 * QUEUE EVENT HANDLERS - CLEANUP AFTER SUCCESS
-	 *******************************************************************************/
-
-	/**
-	 * Handle successful operation completion - CLEAR STORES
-	 */
-	async handleOperationComplete(operation, fieldId) {
-		const results = operation.result?.data || operation.serverData?.data || [];
-
-		// Update upload statuses with attachment IDs
-		results.forEach(result => {
-			const upload = this.uploadStore.get(result.upload_id);
-			if (upload) {
-				upload.attachmentId = result.attachment_id;
-				upload.status = 'completed';
-				this.uploadStore.save(upload);
-				this.updateUploadStatus(result.upload_id, 'completed');
-			}
-		});
-
-		if (!fieldId) return;
-
-		const fieldData = this.getFieldData(fieldId);
-		if (!fieldData) return;
-
-		// Clean up completed uploads from stores
-		const completedUploads = Array.from(fieldData.uploads).filter(uploadId => {
-			const upload = this.uploadStore.get(uploadId);
-			return upload?.status === 'completed';
-		});
-
-		for (const uploadId of completedUploads) {
-			await this.clearUpload(uploadId, false);
-			fieldData.uploads.delete(uploadId);
-		}
-
-		// If all uploads complete, clear entire field from stores
-		if (fieldData.uploads.size === 0) {
-			await this.clearFieldFromStores(fieldId);
-			this.a11y.announce('All uploads completed successfully');
-		} else {
-			// Otherwise just update field state
-			await this.saveFieldData(fieldData);
-		}
-
-		this.updateFieldState(fieldId);
-	}
-
-	/**
-	 * Handle operation failure
-	 */
-	handleOperationFailed(operation, fieldId) {
-		const uploadIds = operation.data instanceof FormData
-			? JSON.parse(operation.data.get('upload_ids') || '[]')
-			: operation.data.upload_ids || [];
-
-		uploadIds.forEach(uploadId => {
-			const upload = this.uploadStore.get(uploadId);
-			if (upload) {
-				upload.status = operation.status === 'operation-failed-permanent'
-					? 'failed_permanent'
-					: 'failed';
-				this.uploadStore.save(upload);
-				this.updateUploadStatus(uploadId, upload.status);
-			}
-		});
-
-		if (fieldId) {
-			this.updateFieldState(fieldId);
-		}
-	}
-
-	/**
-	 * Handle operation cancellation
-	 */
-	async handleOperationCancelled(fieldId) {
-		const fieldData = this.getFieldData(fieldId);
-		if (!fieldData) return;
-
-		const uploadsArray = fieldData.uploads instanceof Set
-			? Array.from(fieldData.uploads)
-			: fieldData.uploads;
-
-		for (const uploadId of uploadsArray) {
-			await this.clearUpload(uploadId, false);
-		}
-
-		await this.clearFieldFromStores(fieldId);
-		this.updateFieldState(fieldId);
-		this.a11y.announce('Upload cancelled');
-	}
-
-	getFieldGroups(fieldId) {
-		const fieldData = this.getFieldData(fieldId);
-		if (!fieldData?.groups) return [];
-
-		return fieldData.groups.map(group => ({
-			id: group.id,
-			uploads: group.uploads || [],
-			changes: group.changes || {}
-		}));
-	}
-
-	getSelectedRestorationUploads(notificationEl) {
-		let selected = [];
-		const checkboxes = notificationEl.querySelectorAll('[type=checkbox]:checked');
-
-		checkboxes.forEach(checkbox => {
-			const item = checkbox.closest('.item');
-			if (item) {
-				selected.push({
-					uploadId: item.dataset.uploadId,
-					fieldId: item.dataset.fieldId
-				});
-			}
-		});
-
-		return selected;
-	}
-
-	async restoreSelectedUploads(selectedUploads) {
-		const byField = new Map();
-		selectedUploads.forEach(item => {
-			if (!byField.has(item.fieldId)) {
-				byField.set(item.fieldId, []);
-			}
-			byField.get(item.fieldId).push(item.uploadId);
-		});
-
-		for (const [fieldId, uploadIds] of byField.entries()) {
-			const fieldState = this.fieldStore.get(fieldId);
-			if (fieldState) {
-				fieldState.uploads = uploadIds;
-				await this.restoreField(fieldState);
-			}
-		}
-	}
-
-	async restoreField(fieldState) {
-		const { config, context, uploads, groups, id } = fieldState;
-
-		// If in a modal, open it first
-		if (context?.modalType) {
-			await this.openModalForRestore(context);
-		}
-
-		// Find field element
-		let fieldElement = document.querySelector(`.field.upload[data-field="${config.name}"]`);
-
-		if (!fieldElement) {
-			const uploaderKey = `${config.content}_${config.itemID}_${config.name}`;
-			fieldElement = document.querySelector(`.field.upload[data-uploader="${uploaderKey}"]`);
-		}
-
-		if (!fieldElement) {
-			console.warn(`Field ${config.name} not found for restoration`, config);
-			return;
-		}
-
-		// Register the field if not already registered
-		let fieldKey = fieldElement.dataset.uploader;
-		if (!fieldKey || !this.fieldElements.has(fieldKey)) {
-			fieldKey = this.registerUploader(fieldElement);
-		}
-
-		const fieldEl = this.fieldElements.get(fieldKey);
-		const fieldData = this.getFieldData(fieldKey);
-
-		if (!fieldEl || !fieldData) {
-			console.error('Failed to register field for restoration');
-			return;
-		}
-
-		// Merge saved state back into field
-		fieldData.state = fieldState.state || 'ready';
-
-		// Rebuild UI references if needed
-		if (!fieldEl.ui) {
-			fieldEl.ui = this.buildFieldUI(fieldElement);
-		}
-
-		if (fieldEl.ui.groups?.display) {
-			fieldEl.ui.groups.display.hidden = false;
-		}
-		if (fieldEl.ui.dropZone) {
-			fieldEl.ui.dropZone.hidden = true;
-		}
-
-		// Restore groups first
-		if (groups && groups.length > 0) {
-			await this.restoreGroups(fieldKey, groups);
-		}
-
-		// Handle both Array and Set for uploads
-		const uploadsArray = uploads instanceof Set
-			? Array.from(uploads)
-			: Array.isArray(uploads)
-				? uploads
-				: [];
-
-		// Restore uploads
-		for (const uploadId of uploadsArray) {
-			// Get upload data from store
-			const uploadData = this.uploadStore.get(uploadId);
-			if (uploadData) {
-				await this.restoreUpload(fieldKey, uploadData);
-			}
-		}
-
-		// Update field state
-		await this.saveFieldData(fieldData);
-		this.updateFieldState(fieldKey);
-		this.maybeLockUploads(fieldKey);
-		this.refreshSortable(fieldKey);
-
-		// Queue for upload if needed
-		console.log(config);
-		if (config.autoUpload && config.mode === 'direct' && config.destination !== 'post_group') {
-			await this.queueUpload(fieldKey);
-		}
-	}
-
-	async restoreUpload(fieldId, uploadData) {
-		const fieldEl = this.fieldElements.get(fieldId);
-		const fieldData = this.getFieldData(fieldId);
-
-		if (!fieldEl || !fieldData) {
-			console.error('Field not found for upload restoration:', fieldId);
-			return;
-		}
-
-		// Get reconstructed File from blob data
-		const file = await this.getBlobData(uploadData.id);
-
-		if (!file) {
-			console.warn('Blob data not found for upload:', uploadData.id);
-			return;
-		}
-
-		// Create preview URL
-		const previewUrl = this.createPreviewUrl(file);
-
-		// Recreate DOM element
-		const subtype = this.getSubtypeFromMime(file.type);
-		const element = this.createUploadElement({
-			id: uploadData.id,
-			preview: previewUrl,
-			meta: uploadData.meta || {
-				originalName: file.name,
-				size: file.size,
-				type: file.type
-			},
-			subtype: subtype
-		}, fieldData.config.destination === 'post_group');
-
-		// Determine correct location
-		let location;
-		if (uploadData.groupId) {
-			// Check if group exists
-			const groupEl = this.groupElements.get(uploadData.groupId);
-			if (groupEl?.grid) {
-				location = groupEl.grid;
-
-				// Add to group's upload list
-				const group = fieldData.groups?.find(g => g.id === uploadData.groupId);
-				if (group) {
-					if (!group.uploads) group.uploads = [];
-					if (!group.uploads.includes(uploadData.id)) {
-						group.uploads.push(uploadData.id);
-					}
-				}
-			} else {
-				// Group doesn't exist, add to preview
-				location = fieldEl.ui.preview;
-				uploadData.groupId = null;
-			}
-		} else {
-			// No group, add to preview
-			location = fieldEl.ui.preview;
-		}
-
-		// Add element to DOM
-		if (location) {
-			location.appendChild(element);
-		} else if (fieldEl.ui.preview) {
-			fieldEl.ui.preview.appendChild(element);
-			location = fieldEl.ui.preview;
-		}
-
-		// Store runtime element data
-		this.uploadElements.set(uploadData.id, {
-			element: element,
-			preview: previewUrl,
-			location: location
-		});
-
-		// Add to field uploads
-		if (!fieldData.uploads) fieldData.uploads = new Set();
-		fieldData.uploads.add(uploadData.id);
-
-		// Update upload data in store
-		uploadData.status = 'processed';
-		await this.uploadStore.save(uploadData);
-
-		// Update sortable state for the grid
-		if (location) {
-			this.updateSortableState(location);
-		}
-	}
-
-	async restoreGroups(fieldId, groups) {
-		const fieldEl = this.fieldElements.get(fieldId);
-		const fieldData = this.getFieldData(fieldId);
-
-		if (!fieldEl || !fieldData) {
-			console.error('Field not found for group restoration:', fieldId);
-			return;
-		}
-
-		for (const groupData of groups) {
-			const group = this.createGroup(fieldId, groupData.id);
-			if (!group) {
-				console.warn('Failed to create group:', groupData.id);
-				continue;
-			}
-
-			const storedGroup = fieldData.groups?.find(g => g.id === groupData.id);
-			if (storedGroup) {
-				// Restore metadata
-				if (groupData.changes) {
-					storedGroup.changes = { ...groupData.changes };
-				}
-
-				// Preserve upload order
-				if (groupData.uploads) {
-					storedGroup.uploads = [...groupData.uploads];
-				}
-
-				// Restore form field values
-				if (groupData.changes) {
-					const titleInput = group.element.querySelector('[name*="post_title"]');
-					const excerptInput = group.element.querySelector('[name*="post_excerpt"]');
-
-					if (titleInput && groupData.changes.post_title) {
-						titleInput.value = groupData.changes.post_title;
-					}
-					if (excerptInput && groupData.changes.post_excerpt) {
-						excerptInput.value = groupData.changes.post_excerpt;
-					}
-				}
-			}
-		}
-
-		await this.saveFieldData(fieldData);
-	}
-
-	async openModalForRestore(context) {
-		if (!context) return;
-
-		const { modalType, itemId } = context;
-
-		// Find and click the appropriate button to open the modal
-		let trigger = null;
-
-		switch(modalType) {
-			case 'create':
-				trigger = document.querySelector('[data-action="create"]');
-				break;
-			case 'edit':
-				// Need to find the specific edit button
-				if (itemId) {
-					trigger = document.querySelector(`[data-action="edit"][data-id="${itemId}"]`);
-				}
-				break;
-			case 'bulkEdit':
-				trigger = document.querySelector('[data-action="bulk-edit"]');
-				break;
-		}
-
-		if (trigger) {
-			trigger.click();
-
-			// Wait for modal to open and render
-			await new Promise(resolve => setTimeout(resolve, 300));
-		} else {
-			console.warn('Modal trigger not found for restoration:', context);
-		}
-	}
-
-	formatBytes(bytes, decimals = 2) {
-		if (bytes === 0) return '0 Bytes';
-		const k = 1024;
-		const dm = decimals < 0 ? 0 : decimals;
-		const sizes = ['Bytes', 'KB', 'MB', 'GB'];
-		const i = Math.floor(Math.log(bytes) / Math.log(k));
-		return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
-	}
-
-	/*******************************************************************************
-	 * CLEANUP METHODS - AGGRESSIVE CLEANUP AFTER SUCCESS
-	 *******************************************************************************/
-
-	/**
-	 * Clear individual upload from stores (called after successful upload)
-	 */
-	async clearUpload(uploadId, persist = true) {
-		const uploadEl = this.uploadElements.get(uploadId);
-		if (uploadEl) {
-			this.revokePreviewUrl(uploadEl.preview);
-			if (uploadEl.element) {
-				const previewUrl = uploadEl.element.dataset.previewUrl;
-				this.revokePreviewUrl(previewUrl);
-				delete uploadEl.element.dataset.previewUrl;
-			}
-		}
-
-		// Remove from runtime memory
-		this.uploadElements.delete(uploadId);
-
-		// Remove from store (no separate blob store - it's part of the upload object)
-		await this.uploadStore.delete(uploadId);
-
-		// Update field if needed
-		if (persist) {
-			const upload = this.uploadStore.get(uploadId);
-			if (upload?.fieldId) {
-				await this.schedulePersistance(upload.fieldId);
-			}
-		}
-	}
-
-	/**
-	 * Clear entire field from stores (called when all uploads complete)
-	 */
-	async clearFieldFromStores(fieldId) {
-		const fieldData = this.getFieldData(fieldId);
-
-		// Clear all related uploads
-		if (fieldData?.uploads) {
-			const uploadsArray = fieldData.uploads instanceof Set
-				? Array.from(fieldData.uploads)
-				: fieldData.uploads;
-
-			for (const uploadId of uploadsArray) {
-				await this.uploadStore.delete(uploadId);
-			}
-		}
-
-		// Clear field from store
-		await this.fieldStore.delete(fieldId);
-
-		// Keep runtime references (fieldElements, etc) intact for reuse
-	}
-
-	cleanupAllPreviewUrls() {
-		if (this.previewUrls) {
-			this.previewUrls.forEach(url => {
-				try {
-					URL.revokeObjectURL(url);
-				} catch (e) {
-					// Ignore errors during cleanup
-				}
-			});
-			this.previewUrls.clear();
-		}
-	}
-
-	/*******************************************************************************
-	 * UI UPDATE METHODS
-	 *******************************************************************************/
-
-	updateFieldState(fieldId) {
-		const fieldEl = this.fieldElements.get(fieldId);
-		const fieldData = this.getFieldData(fieldId);
-		if (!fieldEl || !fieldData) return;
-
-		const container = fieldEl.element;
-		const uploadCount = fieldData.uploads?.size || 0;
-		const hasGroups = fieldEl.ui.groups?.container?.querySelectorAll('.upload-group').length > 0;
-
-		container.dataset.hasUploads = uploadCount > 0 ? 'true' : 'false';
-		container.dataset.uploadCount = uploadCount.toString();
-		container.dataset.hasGroups = hasGroups ? 'true' : 'false';
-
-		if (fieldEl.ui.preview) {
-			fieldEl.ui.preview.setAttribute('aria-label',
-				`Upload preview area with ${uploadCount} item${uploadCount !== 1 ? 's' : ''}`
-			);
-		}
-	}
-
-	updateUploadProgress(fieldId, current, total, message) {
-		const fieldEl = this.fieldElements.get(fieldId);
-		if (!fieldEl?.ui?.progress?.progress) return;
-
-		const progress = fieldEl.ui.progress;
-		const percent = total > 0 ? (current / total) * 100 : 0;
-
-		if (progress.fill) progress.fill.style.width = `${percent}%`;
-		if (progress.text) progress.text.textContent = message;
-		if (progress.count) progress.count.textContent = `${current}/${total}`;
-
-		progress.progress.hidden = (current === total);
-	}
-
-	updateFieldStatus(fieldId, status) {
-		const fieldData = this.getFieldData(fieldId);
-		if (!fieldData) return;
-
-		fieldData.state = status;
-		this.saveFieldData(fieldData);
-	}
-
-	updateUploadStatus(uploadId, status) {
-		const upload = this.uploadStore.get(uploadId);
-		if (!upload) return;
-
-		upload.status = status;
-		this.uploadStore.save(upload);
-		this.updateUploadUI(uploadId);
-	}
-
-	updateUploadUI(uploadId) {
-		const uploadEl = this.uploadElements.get(uploadId);
-		const upload = this.uploadStore.get(uploadId);
-		if (!upload || !uploadEl?.element) return;
-
-		uploadEl.element.className = uploadEl.element.className.replace(/status-[\w-]+/g, '');
-		uploadEl.element.classList.add(`status-${upload.status}`);
-
-		const progress = uploadEl.element.querySelector('.progress');
-		if (progress) {
-			this.updateUploadItemProgress(uploadId,
-				this.getStatusProgress(upload.status),
-				upload.status
-			);
-		}
-	}
-
-	showUploadProgress(uploadId, show = true) {
-		const uploadEl = this.uploadElements.get(uploadId);
-		if (!uploadEl?.element) return;
-
-		const progressEl = uploadEl.element.querySelector('.progress');
-		if (progressEl) {
-			if (show) {
-				progressEl.style.removeProperty('animation');
-				progressEl.hidden = false;
-			} else {
-				progressEl.style.animation = 'fadeOut var(--transition-base)';
-				setTimeout(() => { progressEl.hidden = true; }, 300);
-			}
-		}
-	}
-
-	updateUploadItemProgress(uploadId, percent, status = null) {
-		const uploadEl = this.uploadElements.get(uploadId);
-		if (!uploadEl?.element) return;
-
-		const progressEl = uploadEl.element.querySelector('.progress');
-		if (!progressEl) return;
-
-		const fill = progressEl.querySelector('.fill');
-		const details = progressEl.querySelector('.details');
-		const icon = progressEl.querySelector('.icon');
-
-		if (fill) fill.style.width = `${percent}%`;
-		if (status && details) details.textContent = this.getStatusText(status);
-		if (status && icon) icon.innerHTML = this.getStatusIcon(status).outerHTML;
-	}
-
-	maybeLockUploads(fieldId) {
-		const fieldEl = this.fieldElements.get(fieldId);
-		const fieldData = this.getFieldData(fieldId);
-		if (!fieldEl?.ui?.dropZone || !fieldData) return;
-
-		const uploadCount = fieldData.uploads?.size || 0;
-
-		// For groupable uploads, set max to 20
-		const maxFiles = fieldData.config.destination === 'post_group'
-			? 20
-			: (fieldData.config?.maxFiles || 999);
-
-		fieldEl.ui.dropZone.hidden = uploadCount >= maxFiles;
-		fieldEl.element.classList.toggle('at-max-uploads', uploadCount >= maxFiles);
-
-		// Show helpful message for groupable uploads
-		if (fieldData.config.destination === 'post_group' && uploadCount >= maxFiles) {
-			this.a11y.announce('Maximum of 20 uploads reached. Please submit current uploads before adding more.');
-		}
-	}
-
-	/*******************************************************************************
-	 * GROUP MANAGEMENT
-	 *******************************************************************************/
-	/**
-	 * Create sortable instance for a grid
-	 */
-	createSortableForGrid(grid, fieldId, groupId = null) {
-		if (!grid || grid.sortableInstance) return;
-
-		const sortableInstance = new Sortable(grid, {
-			animation: 150,
-			draggable: '.item',
-			multiDrag: true,
-			selectedClass: 'selected-for-drag',
-			avoidImplicitDeselect: true,
-			group: { name: fieldId, pull: true, put: true },
-			ghostClass: 'sortable-ghost',
-			chosenClass: 'sortable-chosen',
-			dragClass: 'sortable-drag',
-
-			// Centralized drop handler
-			onEnd: (evt) => this.handleDrop(evt, fieldId),
-
-			// Selection sync
-			onSelect: (evt) => {
-				const checkbox = evt.item.querySelector('[name*="select-item"]');
-				if (checkbox && !checkbox.checked) {
-					checkbox.checked = true;
-					checkbox.dispatchEvent(new Event('change', { bubbles: true }));
-				}
-			},
-
-			onDeselect: (evt) => {
-				const checkbox = evt.item.querySelector('[name*="select-item"]');
-				if (checkbox && checkbox.checked) {
-					checkbox.checked = false;
-					checkbox.dispatchEvent(new Event('change', { bubbles: true }));
-				}
-			},
-
-			onAdd: (evt) => this.updateSortableState(evt.to),
-			onRemove: (evt) => this.updateSortableState(evt.from)
-		});
-
-		grid.sortableInstance = sortableInstance;
-
-		const gridId = groupId
-			? `${fieldId}-group-${groupId}`
-			: `${fieldId}-preview`;
-
-		this.sortableInstances.set(gridId, sortableInstance);
-
-		return sortableInstance;
-	}
-	createGroup(fieldId, groupId = null) {
-		const fieldData = this.getFieldData(fieldId);
-		const fieldEl = this.fieldElements.get(fieldId);
-		if (!fieldData || !fieldEl) return null;
-
-		if (!groupId) {
-			groupId = `group_${Date.now()}_${Math.random().toString(36).slice(2, 9)}`;
-		}
-
-		const groupElement = this.createGroupElement(groupId, fieldId);
-		if (!groupElement) return null;
-
-		// Store in field UI Map
-		if (!fieldEl.ui.groups) {
-			fieldEl.ui.groups = {
-				groups: new Map(),
-				container: null,
-				empty: null,
-				display: null
-			};
-		}
-
-		fieldEl.ui.groups.groups.set(groupId, groupElement);
-
-		// Insert into DOM
-		if (fieldEl.ui.groups.container && fieldEl.ui.groups.empty) {
-			fieldEl.ui.groups.container.insertBefore(groupElement, fieldEl.ui.groups.empty);
-		} else if (fieldEl.ui.groups.container) {
-			fieldEl.ui.groups.container.appendChild(groupElement);
-		}
-
-		// Store group element reference
-		const grid = groupElement.querySelector('.item-grid.group');
-		this.groupElements.set(groupId, {
-			element: groupElement,
-			grid: grid,
-			fieldId: fieldId
-		});
-
-		// Add to field groups
-		if (!fieldData.groups) fieldData.groups = [];
-		const existingGroup = fieldData.groups.find(g => g.id === groupId);
-		if (!existingGroup) {
-			fieldData.groups.push({
-				id: groupId,
-				uploads: [],
-				changes: {}
-			});
-			this.saveFieldData(fieldData);
-		}
-
-		// Initialize selection handler
-		this.addGroupSelectionHandler(fieldId, groupId);
-
-		if (grid) {
-			this.createSortableForGrid(grid, fieldId, groupId);
-		}
-
-		return { id: groupId, element: groupElement, grid: grid };
-	}
-	createGroupElement(groupId, fieldId) {
-		let groupElement = window.getTemplate('imageGroup');
-		if (!groupElement) return;
-
-		groupElement.dataset.groupId = groupId;
-		groupElement.dataset.fieldId = fieldId;
-
-		let fields = window.getTemplate('groupMetadata');
-		const fieldsContainer = groupElement.querySelector('.fields');
-		if (fieldsContainer && fields) {
-			fieldsContainer.append(fields);
-
-			const titleInput = fieldsContainer.querySelector('[name="post_title"]');
-			const excerptInput = fieldsContainer.querySelector('[name="post_excerpt"]');
-
-			if (titleInput) {
-				titleInput.id = `${groupId}_title`;
-				titleInput.name = `${groupId}[post_title]`;
-			}
-			if (excerptInput) {
-				excerptInput.id = `${groupId}_excerpt`;
-				excerptInput.name = `${groupId}[post_excerpt]`;
-			}
-
-			const fieldData = this.getFieldData(fieldId);
-			if (fieldData && fieldData.config.content !== '') {
-				let summary = groupElement.querySelector('summary');
-				if (summary) summary.textContent = fieldData.config.content + ' Fields';
-			}
-		} else {
-			groupElement.querySelector('details')?.remove();
-		}
-
-		const gridContainer = groupElement.querySelector('.item-grid.group');
-		if (gridContainer) {
-			gridContainer.dataset.groupId = groupId;
-		}
-
-		return groupElement;
-	}
-
-	deleteGroup(groupId, confirm = true) {
-		const groupEl = this.groupElements.get(groupId);
-		if (!groupEl) return;
-
-		const fieldData = this.getFieldData(groupEl.fieldId);
-		if (!fieldData) return;
-
-		const group = fieldData.groups?.find(g => g.id === groupId);
-		let keepUploads = true;
-
-		if (confirm && group?.uploads?.length > 0) {
-			keepUploads = !window.confirm('Delete uploads in group?');
-		}
-
-		if (confirm && keepUploads && group?.uploads) {
-			// Move uploads back to preview
-			group.uploads.forEach(uploadId => {
-				this.removeFromGroup(uploadId);
-			});
-		}
-
-		// Remove from field groups
-		if (fieldData.groups) {
-			fieldData.groups = fieldData.groups.filter(g => g.id !== groupId);
-			this.saveFieldData(fieldData);
-		}
-
-		// Remove DOM element
-		if (groupEl.element) {
-			groupEl.element.remove();
-			this.a11y.announce('Group removed');
-		}
-
-		// Remove from maps
-		this.groupElements.delete(groupId);
-
-		// Clean up sortable
-		const sortableKey = `${groupEl.fieldId}-group-${groupId}`;
-		const sortable = this.sortableInstances.get(sortableKey);
-		if (sortable?.destroy) {
-			sortable.destroy();
-		}
-		this.sortableInstances.delete(sortableKey);
-
-		this.schedulePersistance(groupEl.fieldId);
-	}
-
-	addToGroup(uploadId, target = null, persist = true) {
-		const upload = this.uploadStore.get(uploadId);
-		const uploadEl = this.uploadElements.get(uploadId);
-		if (!upload || !uploadEl) return;
-
-		const fieldData = this.getFieldData(upload.fieldId);
-		const fieldEl = this.fieldElements.get(upload.fieldId);
-		if (!fieldData || !fieldEl) return;
-
-		// Already in correct location
-		if ((!target && uploadEl.location === fieldEl.ui.preview) || target === uploadEl.location) {
-			return;
-		}
-
-		// Remove from previous group
-		if (upload.groupId) {
-			const group = fieldData.groups?.find(g => g.id === upload.groupId);
-			if (group) {
-				group.uploads = group.uploads.filter(id => id !== uploadId);
-				if (group.uploads.length === 0) {
-					this.deleteGroup(upload.groupId);
-				}
-			}
-		}
-
-		// Clear selection checkbox
-		const checkbox = uploadEl.element.querySelector('[name*="select-item"]');
-		if (checkbox) checkbox.checked = false;
-
-		let featured = uploadEl.element.querySelector('[name="featured"]');
-		if (featured) featured.hidden = !target;
-
-		// Moving to preview or to group
-		if (!target || target.classList.contains('preview')) {
-			target = fieldEl.ui.preview;
-			upload.groupId = null;
-		} else {
-			// Moving to group
-			const groupId = target.dataset.groupId;
-			if (featured) featured.name = groupId + '_' + featured.name;
-
-			const group = fieldData.groups?.find(g => g.id === groupId);
-			if (group) {
-				if (!group.uploads) group.uploads = [];
-				group.uploads.push(uploadId);
-				upload.groupId = groupId;
-			}
-		}
-
-		// Update location
-		uploadEl.location = target;
-		target.append(uploadEl.element);
-
-		// Update stores
-		this.uploadStore.save(upload);
-		if (persist) {
-			this.saveFieldData(fieldData);
-		}
-
-		// Update sortable state
-		this.updateSortableState(target);
-		if (uploadEl.location && uploadEl.location !== target) {
-			this.updateSortableState(uploadEl.location);
-		}
-	}
-
-	removeFromGroup(uploadId) {
-		const upload = this.uploadStore.get(uploadId);
-		const uploadEl = this.uploadElements.get(uploadId);
-		if (!upload || !uploadEl) return;
-
-		const fieldData = this.getFieldData(upload.fieldId);
-		const fieldEl = this.fieldElements.get(upload.fieldId);
-		if (!fieldData || !fieldEl) return;
-
-		// Remove from current group
-		if (upload.groupId) {
-			const group = fieldData.groups?.find(g => g.id === upload.groupId);
-			if (group) {
-				group.uploads = group.uploads.filter(id => id !== uploadId);
-				if (group.uploads.length === 0) {
-					this.deleteGroup(upload.groupId, false);
-				}
-			}
-			upload.groupId = null;
-		}
-
-		// Move back to preview
-		if (fieldEl.ui?.preview) {
-			fieldEl.ui.preview.appendChild(uploadEl.element);
-			uploadEl.location = fieldEl.ui.preview;
-		}
-
-		// Hide featured radio
-		const featured = uploadEl.element.querySelector('[name="featured"]');
-		if (featured) {
-			featured.hidden = true;
-			featured.checked = false;
-		}
-
-		this.uploadStore.save(upload);
-		this.updateSortableState(fieldEl.ui.preview);
-	}
-
-	removeUpload(fieldId, uploadId) {
-		const fieldData = this.getFieldData(fieldId);
-		const upload = this.uploadStore.get(uploadId);
-		const uploadEl = this.uploadElements.get(uploadId);
-
-		if (!fieldData || !upload) return;
-
-		// Remove from field
-		fieldData.uploads?.delete(uploadId);
-
-		// Remove from group if grouped
-		if (upload.groupId) {
-			const group = fieldData.groups?.find(g => g.id === upload.groupId);
-			if (group) {
-				group.uploads = group.uploads.filter(id => id !== uploadId);
-				if (group.uploads.length === 0) {
-					this.deleteGroup(upload.groupId);
-				}
-			}
-		}
-
-		// Clean up element
-		uploadEl?.element?.remove();
-
-		// Clean up memory
-		this.clearUpload(uploadId);
-
-		// Update field state
-		this.saveFieldData(fieldData);
-		this.updateFieldState(fieldId);
-		this.maybeLockUploads(fieldId);
-
-		const handler = this.selectionHandlers.get(fieldId);
-		if (handler) {
-			handler.deselect(uploadId);
-		}
-
-		this.a11y.announce('Upload removed');
-	}
-
-	handleGroupMetaChange(input) {
-		const groupEl = this.getGroupFromElement(input);
-		if (!groupEl) return;
-
-		const fieldData = this.getFieldData(groupEl.fieldId);
-		const group = fieldData?.groups?.find(g => g.id === groupEl.element.dataset.groupId);
-		if (!group) return;
-
-		if (!group.changes) group.changes = {};
-
-		let name = input.name;
-		if (name.includes('group')) {
-			name = name.replace(`${group.id}_`, '').replace(`${group.id}[`, '').replace(']', '');
-		}
-
-		group.changes[name] = input.value;
-		this.saveFieldData(fieldData);
-		this.schedulePersistance(groupEl.fieldId);
-	}
-
-	/*******************************************************************************
-	 * ACTION HANDLERS
-	 *******************************************************************************/
-
-	handleAction(button) {
-		const action = button.dataset.action;
-		const fieldId = this.getFieldIdFromElement(button);
-
-		switch(action) {
-			case 'add-to-group':
-				this.handleAddToGroup(button);
-				break;
-			case 'delete-group':
-				this.handleDeleteGroup(button);
-				break;
-			case 'delete-upload':
-			case 'remove-from-group':
-				this.handleRemoveItem(button);
-				break;
-			case 'upload':
-				const fieldEl = this.fieldElements.get(fieldId);
-				if (fieldEl) {
-					fieldEl.element.closest('details').open = false;
-					document.body.classList.add('uploading');
-					this.submitUploads(fieldId);
-				}
-				break;
-			case 'restore':
-				this.handleRestoreUploads().then(() => {});
-				break;
-			case 'restore-all':
-				this.handleRestoreAll().then(() => {});
-				break;
-			case 'clear-cache':
-				if (!confirm('Save these uploads for later?')) {
-					this.cleanupStoredUploads();
-				}
-				this.cleanupRestore();
-				break;
-		}
-	}
-
-	handleAddToGroup(button) {
-		const fieldElement = button.closest(this.selectors.field.field);
-		const fieldId = fieldElement?.dataset.uploader;
-		if (!fieldId) return;
-
-		const selected = this.selected.get(fieldId);
-
-		if (!selected || selected.size === 0) {
-			this.createGroup(fieldId);
-		} else {
-			const group = this.createGroup(fieldId);
-			if (!group) return;
-
-			selected.forEach(uploadId => {
-				this.addToGroup(uploadId, group.grid);
-			});
-
-			const handler = this.selectionHandlers.get(fieldId);
-			handler?.clearSelection();
-
-			this.a11y.announce(`Created group with ${selected.size} items`);
-		}
-
-		this.schedulePersistance(fieldId);
-	}
-
-	handleDeleteGroup(button) {
-		const group = button.closest(this.selectors.groups.container);
-		if (!group) return;
-
-		const groupId = group.dataset.groupId;
-		const fieldId = this.getFieldIdFromElement(group);
-
-		if (!confirm('Delete this group? Items will be moved back to the upload area.')) {
-			return;
-		}
-
-		const items = group.querySelectorAll(this.selectors.items.item);
-		items.forEach(item => {
-			const uploadId = item.dataset.uploadId;
-			this.removeFromGroup(uploadId);
-		});
-
-		this.deleteGroup(groupId);
-		this.a11y.announce('Group deleted, items returned to upload area');
-		this.schedulePersistance(fieldId);
-	}
-
-	handleRemoveItem(button) {
-		const item = button.closest(this.selectors.items.item);
-		if (!item) return;
-
-		const uploadId = item.dataset.uploadId;
-		const fieldId = this.getFieldIdFromElement(item);
-
-		if (!confirm('Remove this item?')) return;
-
-		this.removeUpload(fieldId, uploadId);
-		this.a11y.announce('Item removed');
-		this.schedulePersistance(fieldId);
-	}
-
-	/*******************************************************************************
-	 * SELECTION MANAGEMENT
-	 *******************************************************************************/
-
-	addFieldSelectionHandler(fieldId) {
-		if (this.selectionHandlers.has(fieldId)) {
-			return this.selectionHandlers.get(fieldId);
-		}
-
-		const fieldEl = this.fieldElements.get(fieldId);
-		if (!fieldEl?.element) return;
-
-		const handler = new window.jvbHandleSelection({
-			container: fieldEl.element,
-			ui: {
-				selectAll: fieldEl.element.querySelector('[name="select-all-uploads"]'),
-				bulkControls: fieldEl.element.querySelector('.selection-actions'),
-				count: fieldEl.element.querySelector('.selection-count')
-			},
-			itemSelector: '[data-upload-id]',
-			checkboxSelector: '[name*="select-item"]'
-		});
-
-		handler.subscribe((event, data) => {
-			switch(event) {
-				case 'item-selected':
-					// Sync with Sortable
-					this.syncSortableSelection(fieldId, data.selectedItems);
-					this.selected.set(fieldId, data.selectedItems);
-					break;
-				case 'item-deselected':
-					this.syncSortableSelection(fieldId, data.selectedItems);
-					this.selected.set(fieldId, data.selectedItems);
-					break;
-				case 'range-selected':
-					this.syncSortableSelection(fieldId, data.selectedItems);
-					this.selected.set(fieldId, data.selectedItems);
-					break;
-				case 'select-all':
-					this.handleSelectAll(data.container, data.selected);
-					break;
-			}
-		});
-
-		this.selectionHandlers.set(fieldId, handler);
-		return handler;
-	}
-
-	addGroupSelectionHandler(fieldId, groupId) {
-		const handlerKey = `${fieldId}_${groupId}`;
-		if (this.selectionHandlers.has(handlerKey)) {
-			return this.selectionHandlers.get(handlerKey);
-		}
-
-		const groupEl = this.groupElements.get(groupId);
-		if (!groupEl?.element) return;
-
-		const handler = new window.jvbHandleSelection({
-			container: groupEl.element,
-			ui: {
-				selectAll: groupEl.element.querySelector(this.selectors.groups.selectAll),
-				bulkControls: groupEl.element.querySelector(this.selectors.groups.actions),
-				count: groupEl.element.querySelector(this.selectors.groups.count)
-			},
-			itemSelector: '[data-upload-id]',
-			checkboxSelector: '[name*="select-item"]'
-		});
-
-		handler.subscribe((event, data) => {
-			switch(event) {
-				case 'item-selected':
-				case 'item-deselected':
-				case 'range-selected':
-					this.selected.set(fieldId, data.selectedItems);
-					break;
-				case 'select-all':
-					this.handleSelectAll(data.container, data.selected);
-					break;
-			}
-		});
-
-		this.selectionHandlers.set(handlerKey, handler);
-		return handler;
-	}
-
-	handleSelectAll(container, selected) {
-		// Can add custom logic here if needed
-	}
-
-	/*******************************************************************************
-	 * HELPER METHODS
-	 *******************************************************************************/
-
-	/**
-	 * Get field data from store and normalize it
-	 * Always use this instead of directly accessing fieldStore.get()
-	 */
-	getFieldData(fieldId) {
-		const fieldData = this.fieldStore.get(fieldId);
-		if (!fieldData) return null;
-
-		// Only convert uploads back to Set (DataStore returns Arrays)
-		if (Array.isArray(fieldData.uploads)) {
-			fieldData.uploads = new Set(fieldData.uploads);
-		} else if (!fieldData.uploads) {
-			fieldData.uploads = new Set();
-		}
-
-		// Ensure groups is an array
-		if (!Array.isArray(fieldData.groups)) {
-			fieldData.groups = [];
-		}
-
-		return fieldData;
-	}
-
-	/**
-	 * Save field data to store, converting Sets to Arrays
-	 */
-	async saveFieldData(fieldData) {
-		await this.fieldStore.save({
-			...fieldData,
-			timestamp: Date.now()
-		});
-	}
-
-	determineFieldId(fieldElement) {
-		const content = fieldElement.dataset.content+'_' ||
-			fieldElement.closest('dialog')?.dataset.content+'_' ||
-			fieldElement.closest('form')?.dataset.save+'_' || '';
-		const itemID = fieldElement.dataset.itemId+'_' ||
-			fieldElement.closest('dialog')?.dataset.itemId+'_' || '';
-		const field = fieldElement.dataset.field || '';
-
-		return `${content}${itemID}${field}`;
-	}
-
-	getFromElement(element, type) {
-		const map = {
-			'field': {
-				selector: this.selectors.field.field,
-				key: 'uploader',
-				getRuntimeData: (id) => this.fieldElements.get(id),
-				getStoreData: (id) => this.getFieldData(id)
-			},
-			'upload': {
-				selector: this.selectors.items.item,
-				key: 'uploadId',
-				getRuntimeData: (id) => this.uploadElements.get(id),
-				getStoreData: (id) => this.uploadStore.get(id)
-			},
-			'group': {
-				selector: this.selectors.groups.container,
-				key: 'groupId',
-				getRuntimeData: (id) => this.groupElements.get(id),
-				getStoreData: (id) => {
-					// Groups are stored in field.groups array
-					const groupEl = this.groupElements.get(id);
-					if (!groupEl) return null;
-					const fieldData = this.getFieldData(groupEl.fieldId);
-					return fieldData?.groups?.find(g => g.id === id);
-				}
-			}
-		};
-
-		const config = map[type];
-		if (!config) return null;
-
-		const el = element.closest(config.selector);
-		if (!el) return null;
-
-		const id = el.dataset[config.key];
-
-		// Return combined runtime + store data for convenience
-		const runtime = config.getRuntimeData(id);
-		const store = config.getStoreData(id);
-
-		return { ...runtime, ...store };
-	}
-
-	getFieldFromElement(el) { return this.getFromElement(el, 'field'); }
-	getUploadFromElement(el) { return this.getFromElement(el, 'upload'); }
-	getGroupFromElement(el) { return this.getFromElement(el, 'group'); }
-
-	getFieldIdFromElement(el) {
-		const field = this.getFromElement(el, 'field');
-		return field?.id ?? null;
-	}
-	getUploadIdFromElement(el) {
-		const upload = this.getFromElement(el, 'upload');
-		return upload?.id ?? null;
-	}
-	getGroupIdFromElement(el) {
-		const group = this.getFromElement(el, 'group');
-		return group?.id ?? null;
-	}
-
-	getSubtypeFromMime(mimeType) {
-		if (mimeType.startsWith('image/')) return 'image';
-		if (mimeType.startsWith('video/')) return 'video';
-		return 'document';
-	}
-
-	getStatusText(status) {
-		return this.statusMapping[status] || status;
-	}
-
-	getStatusIcon(status) {
-		return window.getIcon(this.queue.icons[status]);
-	}
-
-	getStatusProgress(status) {
-		const progress = {
-			'local_processing': 28,
-			'queued': 50,
-			'uploading': 66,
-			'pending': 75,
-			'processing': 89,
-			'completed': 100
-		};
-		return progress[status] || 0;
-	}
-
-
-	createUploadElement(upload, draggable = false) {
-		let image = window.getTemplate('uploadItem');
-		if (!image) return;
-
-		image.dataset.uploadId = upload.id;
-		image.dataset.subtype = upload.subtype || 'image';
-
-		let [featured, img, video, preview, details] = [
-			image.querySelector('[name="featured"]'),
-			image.querySelector('img'),
-			image.querySelector('video'),
-			image.querySelector('label > span'),
-			image.querySelector('details')
-		];
-
-		if (featured) featured.value = upload.id;
-
-		switch (upload.subtype) {
-			case 'image':
-				if (img) {
-					img.src = upload.preview;
-					img.alt = upload.meta?.originalName || '';
-				}
-				video?.remove();
-				preview?.remove();
-				break;
-			case 'video':
-				if (video) video.src = upload.preview;
-				img?.remove();
-				preview?.remove();
-				break;
-			case 'document':
-				const fileName = upload.meta?.originalName || '';
-				const extension = fileName.split('.').pop()?.toLowerCase() || '';
-				const iconMap = {
-					'pdf': 'file-pdf', 'csv': 'file-csv',
-					'doc': 'file-doc', 'docx': 'file-doc',
-					'txt': 'file-txt', 'xls': 'file-xls', 'xlsx': 'file-xls'
-				};
-				const icon = window.getIcon(iconMap[extension] || 'file');
-				if (preview) {
-					preview.innerText = fileName;
-					preview.prepend(icon);
-				}
-				img?.remove();
-				video?.remove();
-				break;
-		}
-
-		if (details) {
-			let template = window.getTemplate('uploadMeta');
-			if (template) details.append(template);
-		}
-
-		image.draggable = draggable;
-
-		// Update input IDs
-		image.querySelectorAll('input').forEach(input => {
-			let id = input.id;
-			if (id) {
-				let newId = id + upload.id;
-				let label = input.parentNode.querySelector(`label[for="${id}"]`);
-				input.id = newId;
-				if (label) label.htmlFor = newId;
-			}
-		});
-
-		return image;
-	}
-
-	/*******************************************************************************
-	 * PERSISTENCE
-	 *******************************************************************************/
-
-	schedulePersistance(fieldId) {
-		const key = `persist_${fieldId}`;
-		window.debouncer.schedule(
-			key,
-			() => this.persistFieldState(fieldId),
-			250
-		);
-	}
-
-	async persistFieldState(fieldId) {
-		const fieldData = this.getFieldData(fieldId);
-		if (!fieldData) return;
-
-		// Save with updated timestamp
-		await this.saveFieldData(fieldData);
-	}
-
-	// In UploadManager, add blob conversion helpers
-	async saveBlobData(uploadId, file) {
-		const arrayBuffer = await file.arrayBuffer();
-
-		const uploadData = this.uploadStore.get(uploadId) || { id: uploadId };
-
-		// Store blob data as ArrayBuffer with metadata
-		uploadData.blobData = {
-			buffer: arrayBuffer,
-			name: file.name,
-			type: file.type,
-			size: file.size,
-			lastModified: file.lastModified || Date.now()
-		};
-
-		await this.uploadStore.save(uploadData);
-	}
-
-	async getBlobData(uploadId) {
-		const upload = this.uploadStore.get(uploadId);
-		if (!upload?.blobData) return null;
-
-		// Reconstruct File from ArrayBuffer
-		const blob = new Blob([upload.blobData.buffer], { type: upload.blobData.type });
-		return new File([blob], upload.blobData.name, {
-			type: upload.blobData.type,
-			lastModified: upload.blobData.lastModified
-		});
-	}
-
-	/*******************************************************************************
-	 HELPER to GET UPLOADED FILES
-	*******************************************************************************/
-	/**
-	 * Get all files for a form (searches all upload fields within form)
-	 */
-	async getFilesForForm(formElement) {
-		const uploadFields = formElement.querySelectorAll('[data-upload-field]');
-		const allFiles = [];
-
-		for (const field of uploadFields) {
-			const fieldId = this.determineFieldId(field);
-			const files = await this.getFilesForField(fieldId);
-			allFiles.push(...files);
-		}
-
-		return allFiles;
-	}
-
-	/**
-	 * Get all files for a specific field
-	 */
-	async getFilesForField(fieldId) {
-		const fieldData = this.getFieldData(fieldId);
-		if (!fieldData?.uploads) return [];
-
-		const files = [];
-		const uploadsArray = fieldData.uploads instanceof Set
-			? Array.from(fieldData.uploads)
-			: fieldData.uploads;
-
-		for (const uploadId of uploadsArray) {
-			const upload = this.uploadStore.get(uploadId);
-			if (!upload) continue;
-
-			// Get the actual File object from blob data
-			const file = await this.getBlobData(uploadId);
-			if (file) {
-				files.push({
-					file: file,
-					uploadId: uploadId,
-					fieldName: fieldData.config.name,
-					meta: upload.meta || {}
-				});
-			}
-		}
-
-		return files;
-	}
-
-	/*******************************************************************************
-	 * RECOVERY & RESTORATION
-	 *******************************************************************************/
-
-	handleFieldStoreEvent(event, data) {
-		switch(event) {
-			case 'data-loaded':
-				this.fieldStoreReady = true;
-				this.checkIfBothStoresReady();
-				break;
-		}
-	}
-
-	handleUploadStoreEvent(event, data) {
-		switch(event) {
-			case 'data-loaded':
-				this.uploadStoreReady = true;
-				this.checkIfBothStoresReady();
-				break;
-			case 'item-saved':
-				this.showSaveIndicator(data.key);
-				break;
-		}
-	}
-
-	checkIfBothStoresReady() {
-		if (this.fieldStoreReady && this.uploadStoreReady && !this.hasCheckedForUploads) {
-			this.hasCheckedForUploads = true;
-			this.checkForStoredUploads();
-		}
-	}
-
-	async checkForStoredUploads() {
-		const allFieldStates = this.fieldStore.getAll();
-
-		const pendingFields = allFieldStates.filter(field => {
-			if (!field.uploads) return false;
-
-			// Handle both Set and Array (from IndexedDB)
-			const uploadsArray = field.uploads instanceof Set
-				? Array.from(field.uploads)
-				: Array.isArray(field.uploads)
-					? field.uploads
-					: [];
-
-			return uploadsArray.some(uploadId => {
-				const upload = this.uploadStore.get(uploadId);
-				return upload && !upload.operationId &&
-					['completed', 'processed', 'local_processing', 'processed-original'].includes(upload.status);
-			});
-		});
-		if (pendingFields.length === 0) return;
-
-		await this.showRecoveryNotification(pendingFields);
-	}
-
-	async showRecoveryNotification(pendingFields) {
-		const totalUploads = pendingFields.reduce((sum, field) => sum + field.uploads.length, 0);
-		const totalGroups = pendingFields.reduce((sum, field) =>
-			sum + (field.groups?.length || 0), 0);
-
-		let notification = window.getTemplate('restoreNotification');
-		if (!notification) {
-			console.error('Restore notification template not found');
-			return;
-		}
-
-		// Build appropriate message
-		let message;
-		if (totalGroups > 0) {
-			let group = totalGroups > 1 ? 'groups' : 'group';
-			let upload = totalUploads > 1 ? 'uploads' : 'upload';
-			message = `${totalGroups} ${group} with ${totalUploads} ${upload} can be restored.`;
-		} else {
-			message = `${totalUploads} upload(s) from ${pendingFields.length} field(s) can be recovered.`;
-		}
-
-		const detailsEl = notification.querySelector('.restore-details');
-		if (detailsEl) {
-			detailsEl.textContent = message;
-		}
-
-		// Build the restoration preview
-		for (const field of pendingFields) {
-			let fieldTemplate = window.getTemplate('restoreField');
-			if (!fieldTemplate) continue;
-
-			// Set field name/title
-			const titleEl = fieldTemplate.querySelector('h3');
-			if (titleEl) {
-				titleEl.textContent = field.config.name || 'Unnamed Field';
-			}
-
-			const itemGrid = fieldTemplate.querySelector('.item-grid.restore');
-
-			// Process each upload
-			for (let uploadId of field.uploads) {
-				const upload = this.uploadStore.get(uploadId);
-				let uploadItem = window.getTemplate('uploadItem');
-				if (!uploadItem) continue;
-				//
-				// 	const imgEl = uploadItem.querySelector('img');
-				// 	const placeholderEl = uploadItem.querySelector('.image-placeholder');
-				//
-				const file = await this.getBlobData(upload.id);
-				if (file) {
-
-					try {
-						// Create new blob URL from stored data
-						const previewUrl = this.createPreviewUrl(file);
-
-						let [
-							featured,
-							img,
-							video,
-							preview,
-							details
-						] = [
-							uploadItem.querySelector('[name="featured"]'),
-							uploadItem.querySelector('img'),
-							uploadItem.querySelector('video'),
-							uploadItem.querySelector('label > span'),
-							uploadItem.querySelector('details')
-						];
-
-						uploadItem.dataset.uploadId = upload.id;
-
-
-						uploadItem.dataset.fieldId = field.id;
-
-						let subtype = this.getSubtypeFromMime(file.type);
-						uploadItem.dataset.subtype = subtype;
-						switch (subtype) {
-							case 'image':
-								[
-									img.src,
-									img.alt
-								] = [
-									previewUrl,
-									file.name ?? upload.meta?.originalName ?? ''
-								];
-								video.remove();
-								preview.remove();
-								break;
-							case 'video':
-								video.src = previewUrl;
-								img.remove();
-								preview.remove();
-								break;
-							case 'document':
-								let extension = '';
-								let icon;
-								switch (extension) {
-									case 'pdf':
-										icon = window.getIcon('file-pdf');
-										break;
-									case 'csv':
-										icon = window.getIcon('file-csv');
-										break;
-									case 'doc':
-										icon = window.getIcon('file-doc');
-										break;
-									case 'txt':
-										icon = window.getIcon('file-txt');
-										break;
-									case 'xls':
-										icon = window.getIcon('file-xls');
-										break;
-									default:
-										icon = window.getIcon('file');
-										break;
-								}
-
-								preview.innerText = upload.originalFile.name;
-								preview.prepend(icon);
-								img.remove();
-								video.remove();
-								break;
-						}
-
-						// Store URL for cleanup later
-						uploadItem.dataset.previewUrl = previewUrl;
-					} catch (error) {
-						console.warn('Failed to create preview for upload:', upload.id, error);
-					}
-				}
-
-				// Set upload metadata
-				const nameEl = uploadItem.querySelector('summary span');
-				if (nameEl) {
-					nameEl.textContent = upload.meta?.originalName || 'Unknown file';
-				}
-
-				const metaEl = uploadItem.querySelector('details');
-				if (metaEl && upload.meta) {
-					metaEl.textContent = `${this.formatBytes(upload.meta.size)} • ${upload.meta.type}`;
-				}
-
-				// Update input IDs safely
-				uploadItem.querySelectorAll('input').forEach(input => {
-					let id = input.id;
-					if (id) {
-						let newId = id + upload.id;
-						let label = input.parentNode.querySelector(`label[for="${id}"]`);
-						input.id = newId;
-						if (label) {
-							label.htmlFor = newId;
-						}
-					}
-				});
-
-				if (itemGrid) {
-					itemGrid.appendChild(uploadItem);
-				}
-			}
-
-			notification.querySelector('.wrap').appendChild(itemGrid);
-		}
-
-		document.querySelector('.field.upload').appendChild(notification);
-		notification = document.querySelector('dialog.restore-uploads');
-		this.restoreModal = new window.jvbModal(notification);
-		this.restoreSelection = new window.jvbHandleSelection({
-			container: notification,
-			ui: {
-				selectAll: notification.querySelector('#select-all-restore'),
-				count: notification.querySelector('.selection-count'),
-			},
-		});
-
-		this.restoreModal.handleOpen();
-
-	}
-
-	async handleRestoreUploads() {
-		let notification = document.querySelector('dialog.restore-uploads');
-		if (!notification) {
-			return;
-		}
-
-		const selectedUploads = this.getSelectedRestorationUploads(notification);
-		if (selectedUploads.length === 0) {
-			return;
-		}
-		await this.restoreSelectedUploads(selectedUploads);
-
-		this.cleanupRestore();
-	}
-
-	async handleRestoreAll() {
-		let notification = document.querySelector('dialog.restore-uploads');
-		if (!notification) {
-			return;
-		}
-		// Gets ALL uploads from notification without checking selection
-		const allUploads = [];
-		notification.querySelectorAll('.item.upload').forEach(item => {
-			let uploadId = item.dataset.uploadId;
-			let fieldId = item.dataset.fieldId;
-			allUploads.push({ uploadId, fieldId });
-		});
-
-		await this.restoreSelectedUploads(allUploads);
-		this.cleanupRestore();
-	}
-
-	showSaveIndicator(key) {
-		// Optional: show user that state is being saved
-	}
-
-	cleanupRestore() {
-		this.restoreModal.handleClose();
-		this.restoreSelection.destroy();
-		this.restoreSelection = null;
-		this.restoreModal.destroy();
-		this.restoreModal.modal.remove();
-		this.restoreModal = null;
-	}
-
-	async cleanupStoredUploads() {
-		await this.fieldStore.clear();
-		await this.uploadStore.clear();
-	}
-
-	/*******************************************************************************
-	 * EVENT SYSTEM
-	 *******************************************************************************/
-
-	subscribe(callback) {
-		this.subscribers.add(callback);
-		return () => this.subscribers.delete(callback);
-	}
-
-	notify(event, data = {}) {
-		this.subscribers.forEach(cb => {
-			try {
-				cb(event, data);
-			} catch (error) {
-				console.error('Subscriber error:', error);
-			}
-		});
-	}
-
-	/*******************************************************************************
-	 * DESTROY & CLEANUP
-	 *******************************************************************************/
-
-	destroy() {
-		document.removeEventListener('click', this.clickHandler);
-		document.removeEventListener('change', this.changeHandler);
-		document.removeEventListener('dragenter', this.dragEnterHandler);
-		document.removeEventListener('dragleave', this.dragLeaveHandler);
-		document.removeEventListener('dragover', this.dragOverHandler);
-		document.removeEventListener('drop', this.dropHandler);
-
-		if (this.dragController) {
-			this.dragController.destroy();
-		}
-
-		this.selectionHandlers.forEach(handler => handler.destroy());
-		this.selectionHandlers.clear();
-
-		this.cleanupAllPreviewUrls();
-
-		this.sortableInstances.forEach(instance => {
-			if (instance?.destroy) instance.destroy();
-		});
-		this.sortableInstances.clear();
-
-		this.uploadElements.clear();
-		this.fieldElements.clear();
-		this.groupElements.clear();
-		this.selected.clear();
-		this.subscribers.clear();
-	}
-}
-
-// Initialize when DOM is ready
-document.addEventListener('DOMContentLoaded', async function () {
-	window.auth.subscribe((event) => {
-		if (event === 'auth-loaded') {
-			window.jvbUploads = new UploadManager();
-		}
-	});
-});
diff --git a/assets/js/min/creator.min.js b/assets/js/min/creator.min.js
index 8ea287c..a08c3d8 100644
--- a/assets/js/min/creator.min.js
+++ b/assets/js/min/creator.min.js
@@ -1 +1 @@
-window.jvbTaxCreator=class{constructor(e){this.selector=e,e.modal&&(this.createNew=e.modal.querySelector(".create-new-term"),this.toggle=e.modal.querySelector(".new-term-toggle"),this.form=this.createNew?.querySelector(".create-new-term-section")),this.initListeners(),this.form&&this.initTermCreation()}initListeners(){this.clickHandler=this.handleClick.bind(this),document.addEventListener("click",this.clickHandler)}handleClick(e){window.targetCheck(e,".create-new-term summary")&&(this.createNew.open&&this.createNew.querySelector('input[name="term_name"]').focus(),this.resetParentOptions()),window.targetCheck(e,".submit-term")&&this.handleTermCreation(e).then((()=>{})),window.targetCheck(e,".create-term")&&this.handleAutocompleteCreate(e).then((()=>{}))}async handleTermCreation(e){const t=this.selector.currentConfig?.taxonomy;if(!t)return;const r=this.form.querySelector('input[name="term_name"]').value.trim(),n=parseInt(this.form.querySelector("input#select_parent")?.value)||0;if(!r)return;const s=this.form.querySelector("button");try{s&&(s.disabled=!0);const e=await this.createTerm(r,n,t);e.success&&e.term&&(await this.handleSuccessfulCreation(e.term,t,n),this.clearForm())}catch(e){console.error("Error creating term:",e),this.selector.handleError(e,"handleTermCreation")}finally{s&&(s.disabled=!1)}}async handleSuccessfulCreation(e,t,r){const n=e.path||e.name;this.createNew.open=!1,await this.selector.store.clearCache(),this.selector.store.data.set(e.id,{id:e.id,name:e.name,path:n,taxonomy:t,parent:r,count:0,hasChildren:!1,slug:e.slug||termName.toLowerCase().replace(/\s+/g,"-")}),this.selector.addSelectedTermToModal(e.id,e.name,n),(this.selector.store.filters.parent||0)===r&&await this.selector.store.setFilters({taxonomy:t,parent:r,page:1,search:""})}async handleAutocompleteCreate(e){const t=e.target.closest(".create-term"),r=this.selector.getFieldId(t),n=this.selector.fields.get(r);if(!n)return;const s=n.container.querySelector("input[data-autocomplete]"),a=s?.value.trim()||t.dataset.query;if(!a)return;const o=t.innerHTML;try{t.disabled=!0,t.textContent="Creating...";const e=await this.createTerm(a,0,n.taxonomy);e.success&&e.term?await this.handleAutocompleteSuccess(e.term,n,s):"exists"===e.reason&&e.term&&this.handleExistingTerm(e.term,n,s)}catch(e){console.error("Error creating term:",e),this.selector.handleError(e,"handleAutocompleteCreate")}finally{t.innerHTML=o,t.disabled=!1}}async handleAutocompleteSuccess(e,t,r){const n=e.path||e.name;t.selectedTerms.add(parseInt(e.id)),this.selector.store.data.set(e.id,{id:e.id,name:e.name,path:n,taxonomy:t.taxonomy,parent:0,count:0,hasChildren:!1,slug:e.slug||e.name.toLowerCase().replace(/\s+/g,"-")}),this.selector.addTermDisplay(e.id,e.name,n,"field",t.id),t.input.value=Array.from(t.selectedTerms).join(","),t.input.dispatchEvent(new Event("change",{bubbles:!0})),t.autocompleteDropdown.hidden=!0,r&&(r.value=""),await this.selector.store.clearCache()}handleExistingTerm(e,t,r){t.selectedTerms.add(parseInt(e.id)),this.selector.addTermDisplay(e.id,e.name,e.path||e.name,"field",t.id),t.input.value=Array.from(t.selectedTerms).join(","),t.input.dispatchEvent(new Event("change",{bubbles:!0})),t.autocompleteDropdown.hidden=!0,r&&(r.value="")}initTermCreation(){this.form&&this.form.addEventListener("change",(e=>{e.preventDefault(),e.stopPropagation()}))}resetParentOptions(){const e=this.selector.currentConfig?.taxonomy;if(!e)return;let t=this.createNew.querySelector("#select_parent");if(!t)return;let r=t.querySelector("option");if(!r)return;window.removeChildren(t),t.append(r.cloneNode(!0));const n=this.selector.store.filters.parent||0;if(0!==n){const e=this.selector.store.data.get(n);if(e){let n=r.cloneNode(!0);n.value=e.id,n.textContent=e.name,t.append(n)}}const s=[];this.selector.store.data.forEach((t=>{t.taxonomy===e&&t.parent===n&&s.push(t)})),s.sort(((e,t)=>e.name.localeCompare(t.name))),s.forEach((e=>{let n=r.cloneNode(!0);n.id=`select-parent-${e.id}`,n.value=e.id,n.textContent="  — "+e.name,t.append(n)}))}async createTerm(e,t=0,r){try{await this.selector.store.setFilters({taxonomy:r,search:e,page:1,parent:0}),await new Promise((e=>setTimeout(e,100)));const n=Array.from(this.selector.store.data.values()).find((t=>t.taxonomy===r&&t.name.toLowerCase()===e.toLowerCase()));if(n)return this.createNew&&this.showTermSuggestions([n],!0),{success:!1,reason:"exists",term:n};const s=await fetch(`${jvbSettings.api}terms`,{method:"POST",headers:{"Content-Type":"application/json","X-WP-Nonce":window.auth.getNonce()},body:JSON.stringify({taxonomy:r,name:e,parent:t})});if(!s.ok)throw new Error(`Server error: ${s.status}`);return await s.json()}catch(e){throw console.error("Error creating term:",e),e}}showTermSuggestions(e,t=!1){const r=this.createNew.querySelector(".term-suggestions")||this.createSuggestionContainer();window.removeChildren(r);const n=document.createElement("h4");n.textContent=t?"This term already exists:":"Similar terms already exist:",r.appendChild(n);const s=document.createElement("ul");s.className="term-suggestion-list",e.forEach((e=>{const t=document.createElement("li"),r=this.createSuggestionButton(e);t.appendChild(r),s.appendChild(t)})),r.appendChild(s),r.hidden=!1}createSuggestionButton(e){const t=document.createElement("button");return t.type="button",t.className="use-existing-term",t.dataset.id=e.id,t.textContent=e.path||e.name,t.addEventListener("click",(()=>{this.selector.addSelectedTermToModal(e.id,e.name,e.path||e.name),this.createNew.open=!1;const t=this.createNew.querySelector(".term-suggestions");t&&(t.hidden=!0),this.clearForm()})),t}createSuggestionContainer(){const e=document.createElement("div");return e.className="term-suggestions",e.hidden=!0,this.createNew.querySelector("form").after(e),e}clearForm(){const e=this.form.querySelector('input[name="term_name"]');e&&(e.value="");const t=this.createNew.querySelector(".term-suggestions");t&&(t.hidden=!0)}destroy(){this.clickHandler&&document.removeEventListener("click",this.clickHandler);const e=this.createNew?.querySelector(".loading-message.create-term");e&&(e.hidden=!0);const t=this.createNew?.querySelector(".term-suggestions");t&&(t.hidden=!0)}};
\ No newline at end of file
+window.jvbTaxCreator=class{constructor(e){this.selector=e,this.queue=window.jvbQueue,this.initElements(),this.initListeners()}initElements(){this.selectors={details:"details.create-new",parent:"#select_parent",summary:".create-new summary",suggestion:".term-suggestions",name:"#term_name",button:".submit-term",form:"form.create-term",label:{name:'[for="term_name"]',parent:'[for="select_parent"]'},loading:".loading-message.create-term"},this.ui=window.uiFromSelectors(this.selectors,this.selector.container)}initListeners(){this.clickHandler=this.handleClick.bind(this),document.addEventListener("click",this.clickHandler),this.ui.form&&this.ui.form.addEventListener("change",(e=>{e.preventDefault(),e.stopPropagation()}))}handleClick(e){window.targetCheck(e,this.selectors.summary)&&(this.ui.details.open&&this.ui.name?.focus(),this.resetParentOptions()),window.targetCheck(e,this.selectors.button)&&this.handleTermCreation(e).then((()=>{}))}async handleTermCreation(e){const t=this.selector.currentField().taxonomy;if(!t)return;let r={parent:0,taxonomy:t};if(this.selector.container.open?(r.name=this.ui.name.value.trim(),r.parent=parseInt(this.ui.parent?.value??0)):this.selector.activeField&&(r.name=this.selector.store.query),r.name&&!(r.name.length<2))try{this.ui.button&&(this.ui.button.disabled=!0);const e=await this.createTerm(r);e.success&&e.term&&(await this.handleSuccessfulCreation(e.term,r),this.clearForm())}catch(e){console.error("Error creating term:",e),this.selector.handleError(e,"handleTermCreation")}finally{this.ui.button&&(this.ui.button.disabled=!1)}}async handleSuccessfulCreation(e,t){const r=e.path||e.name;this.ui.details.open=!1,await this.selector.store.clearCache(),this.selector.store.data.set(e.id,{id:e.id,name:e.name,path:r,taxonomy:t.taxonomy,parent:t.parent,count:0,hasChildren:!1,slug:e.slug||e.name.toLowerCase().replace(/\s+/g,"-")}),this.selector.addSelected(e.id,this.selector.activeField),this.selector.container.open&&(this.selector.store.filters.parent||0)===t.parent&&await this.selector.store.setFilters({taxonomy:t.taxonomy,parent:t.parent,page:1,search:""})}resetParentOptions(){const e=this.selector.currentField();if(!e)return;const t=e.taxonomy;if(!t)return;if(!this.ui.parent)return;let r=this.ui.parent.querySelector("option");if(!r)return;window.removeChildren(this.ui.parent),this.ui.parent.append(r.cloneNode(!0));const i=this.selector.store.filters.parent||0;if(0!==i){const e=this.selector.store.get(i);if(e){let t=r.cloneNode(!0);t.value=e.id,t.textContent=e.name,this.ui.parent.append(t)}}const n=[];this.selector.store.getFiltered().forEach((e=>{e.taxonomy===t&&e.parent===i&&n.push(e)})),n.sort(((e,t)=>e.name.localeCompare(t.name))),n.forEach((e=>{let t=r.cloneNode(!0);t.id=`select-parent-${e.id}`,t.value=e.id,t.textContent="  — "+e.name,this.ui.parent.append(t)}))}async createTerm(e){if(e.name&&void 0!==e.parent&&e.taxonomy)try{const t=await fetch(`${jvbSettings.api}terms`,{method:"POST",headers:{"Content-Type":"application/json","X-WP-Nonce":window.auth.getNonce()},body:JSON.stringify(e)});if(!t.ok)throw new Error(`Server error: ${t.status}`);return await t.json()}catch(e){throw console.error("Error creating term:",e),e}}clearForm(){this.ui.name&&(this.ui.name.value="")}destroy(){this.clickHandler&&document.removeEventListener("click",this.clickHandler),this.ui.loading&&(this.ui.loading.hidden=!0)}};
\ No newline at end of file
diff --git a/assets/js/min/selector.min.js b/assets/js/min/selector.min.js
index 74ecd45..24e49c7 100644
--- a/assets/js/min/selector.min.js
+++ b/assets/js/min/selector.min.js
@@ -1 +1 @@
-(()=>{class e{constructor(){this.a11y=window.jvbA11y,this.error=window.jvbError,this.index=-1,this.isInitializing=!0,this.taxonomiesToFetch=new Set,this.subscribers=new Set;const e=window.jvbStore.register("taxonomies",{storeName:"terms",keyPath:"id",showLoading:!1,indexes:[{name:"taxonomy",keyPath:"taxonomy"},{name:"parent",keyPath:"parent"},{name:"slug",keyPath:"slug",unique:!0},{name:"count",keyPath:"count"}],endpoint:"terms",TTL:12e4,filters:{taxonomy:"",page:1,search:"",parent:0},required:"taxonomy",delayFetch:!0});this.store=e.terms,this.fields=new Map,this.selectedTerms=new Map,this.activeField=null,this.currentConfig=null,this.disabled=!1,this.searchContexts=new Map,this.init()}init(){this.initModal(),this.scanExistingFields(),this.initGlobalListeners(),this.needsCreator()&&window.jvbTaxCreator&&(this.creator=new window.jvbTaxCreator(this)),this.store.subscribe(this.handleStoreEvent.bind(this)),this.isInitializing=!1,this.batchFetchTaxonomies()}needsCreator(){return Array.from(this.fields.values()).some((e=>e.canCreate||e.hasAutocomplete))}handleStoreEvent(e,t){const i={"data-loaded":()=>this.handleDataLoaded(t),"filters-changed":()=>this.handleFiltersChanged(t),"fetch-error":()=>this.handleFetchError(t.error)};i[e]?.()}handleDataLoaded(e){const t=this.store.filters.taxonomy;if(t){(t.includes(",")?t.split(",").map((e=>e.trim())):[t]).forEach((e=>this.updateFieldsForTaxonomy(e)))}this.isInitializing&&this.fields.forEach(((e,t)=>{e.selectedTerms.size>0&&this.initFieldDisplay(t)})),this.renderSearchResults(e)}renderSearchResults(e){const t=this.getActiveSearchContext();"modal"===t?this.renderModalResults(e):"autocomplete"===t&&this.renderAutocompleteResults(e)}getActiveSearchContext(){return this.modal?.open?"modal":this.activeField&&this.searchContexts.has(this.activeField)?this.searchContexts.get(this.activeField):null}renderModalResults(e){this.hideLoading();const t=this.store.getFiltered(),i=this.store.lastResponse?.page||{},s=e.filters?.search?.length>0,o=i.page>1;this.notify("terms-loaded",{terms:t,filters:e.filters}),0===t.length?(o||this.showEmptyState(s?"No results found.":"No items available."),this.observer.unobserve(this.ui.sentinel)):(this.renderTerms(t,o,s),i.has_more?this.observer.observe(this.ui.sentinel):this.observer.unobserve(this.ui.sentinel)),this.a11y?.announce(t.length,o)}renderAutocompleteResults(e){const t=this.fields.get(this.activeField);if(!t?.autocompleteDropdown)return;const i=this.store.getFiltered(),s=e.filters?.search||"";this.showAutocompleteResults(t,i,s),this.searchContexts.delete(this.activeField)}handleFiltersChanged(e){this.modal?.open&&this.showLoading()}handleFetchError(e){this.hideLoading();"autocomplete"===this.getActiveSearchContext()?(this.showAutocompleteError(this.activeField),this.searchContexts.delete(this.activeField)):this.handleError(e,"fetch")}updateFieldsForTaxonomy(e){this.getFieldsForTaxonomy(e).forEach((e=>{this.updateFieldButtonState(e.id)}))}updateFieldButtonState(e){const t=this.fields.get(e);if(!t)return;const i=Array.from(this.store.data.values()).some((e=>e.taxonomy===t.taxonomy));t.toggle&&(t.toggle.disabled=!i&&!t.canCreate,t.toggle.title=i?`Select ${this.getLabel(t.taxonomy,"plural")}`:`No ${this.getLabel(t.taxonomy,"single")} available`)}getFieldsForTaxonomy(e){return Array.from(this.fields.values()).filter((t=>t.taxonomy===e))}scanExistingFields(e=document.body){e.querySelectorAll(".field.taxonomy, .field.post").forEach((e=>{try{this.registerField(e)}catch(t){this.handleError(t,"scanExistingFields",e.dataset.name)}}))}registerField(e){const t=e.querySelector("input[type=hidden]");if(!t)return!1;const i=this.createFieldId(e);e.dataset.fieldId=i;const s=e.querySelector("button.taxonomy-toggle"),o={id:i,input:t,container:e,taxonomy:s.dataset.taxonomy,name:e.dataset.field,maxSelection:parseInt(s.dataset.max)||0,canSearch:"search"in s.dataset,hasAutocomplete:"autocomplete"in s.dataset,autocompleteDropdown:e.querySelector(".autocomplete-dropdown")||null,canCreate:"creatable"in s.dataset,isRequired:"required"in s.dataset,selectedTerms:new Set,toggle:s,selectedContainer:e.querySelector(".selected-items")},a=t.value.trim();return a&&a.split(",").map((e=>parseInt(e.trim()))).filter((e=>!isNaN(e))).forEach((e=>o.selectedTerms.add(e))),this.fields.set(i,o),this.isInitializing&&this.taxonomiesToFetch.add(o.taxonomy),o.selectedTerms.size>0&&this.initFieldDisplay(i),i}createFieldId(e){return this.index++,"selector-"+this.index}async initFieldDisplay(e){const t=this.fields.get(e);t&&0!==t.selectedTerms.size&&Array.from(t.selectedTerms).forEach((t=>{const i=this.store.get(t);i&&this.addTermDisplay(t,i.name,i.path,"field",e)}))}initModal(){this.modal=document.querySelector("dialog#jvb-selector"),this.modal?(this.initModalElements(),this.modalInstance=new window.jvbModal(this.modal,{handleForm:!1}),this.modalInstance.subscribe((e=>{"modal-open"===e&&this.openModal(),"modal-close"===e&&this.closeModal()}))):console.warn("Taxonomy selector modal not found")}initModalElements(){this.ui=window.uiFromSelectors({search:{input:"[type=search]",container:".search-wrapper"},termsList:".items-container",termsWrap:".items-wrap",breadcrumbs:{nav:"nav.term-navigation",back:".back-to-parent"},loading:{loading:".loading",text:".loading span"},selectedTerms:".selected-items",sentinel:".scroll-sentinel",modal:{title:"#modal-title"},create:{details:".create-new-term",summary:".create-new-term summary",label:{name:"[for=term_name]",parent:"[for=select_parent]"}}}),this.observer=new IntersectionObserver((e=>{e.forEach((e=>{e.isIntersecting&&this.loadMoreTerms()}))}),{root:this.ui.termsWrap,threshold:.5})}initGlobalListeners(){document.addEventListener("click",this.handleClick.bind(this)),document.addEventListener("change",this.handleChange.bind(this)),document.addEventListener("input",this.handleInput.bind(this)),document.addEventListener("focus",this.handleFocus.bind(this),!0),document.addEventListener("blur",this.handleBlur.bind(this),!0)}handleClick(e){if(window.targetCheck(e,".taxonomy-toggle")){e.preventDefault();const t=this.getFieldId(e.target);return void(this.fields.get(t)&&this.setActiveField(t,!0))}const t=window.targetCheck(e,"button.remove-item");if(t&&e.target.closest(".jvb-selector")){const e=this.getFieldId(t),i=t.closest(".selected-item").dataset.id;this.removeSelectedTerm(e,i)}else e.target.matches(".modal-close")?this.modalInstance?.handleClose():this.modal?.contains(e.target)&&this.handleModalClick(e)}handleChange(e){if(window.targetCheck(e,".taxonomy.field, .post.field")&&"hidden"===e.target.type){const t=this.getFieldId(e.target);this.updateFieldFromInput(t)}else this.modal?.contains(e.target)&&this.handleModalChange(e)}handleInput(e){if(this.modal?.contains(e.target)&&"search"===e.target.type)this.performSearch(e.target.value.trim(),"modal");else if("autocomplete"in e.target.dataset){const t=this.getFieldId(e.target),i=this.fields.get(t);i?.hasAutocomplete&&this.performSearch(e.target.value.trim(),"autocomplete",t)}}handleFocus(e){if(!("autocomplete"in e.target.dataset))return;const t=this.getFieldId(e.target),i=this.fields.get(t);i?.hasAutocomplete&&this.preloadTaxonomy(i.taxonomy)}handleBlur(e){"autocomplete"in e.target.dataset&&setTimeout((()=>{const t=this.getFieldId(e.target),i=this.fields.get(t);i?.autocompleteDropdown&&(i.autocompleteDropdown.hidden=!0),this.searchContexts.delete(t)}),200)}performSearch(e,t="modal",i=null){const s="autocomplete"===t?this.fields.get(i):this.currentConfig;if(s){if("autocomplete"===t){if(s.currentAutocompleteQuery=e,e.length<2)return void(s.autocompleteDropdown&&(s.autocompleteDropdown.hidden=!0));this.searchContexts.set(i,"autocomplete"),this.activeField=i,s.autocompleteDropdown&&(s.autocompleteDropdown.hidden=!1)}window.debouncer.schedule(`taxonomy-search-${t}-${i||"modal"}`,(async()=>{await this.store.setFilters({taxonomy:s.taxonomy,search:e,page:1,parent:e?0:this.store.filters.parent||0}),"modal"===t&&window.removeChildren(this.ui.termsList)}),300)}}setActiveField(e,t=!1){this.activeField=e,this.currentConfig=this.fields.get(e),t&&this.modalInstance.handleOpen(),this.store.setFilter("taxonomy",this.currentConfig.taxonomy),this.selectedTerms.clear(),this.currentConfig.selectedTerms.forEach((e=>{const t=this.store.get(e);t&&this.selectedTerms.set(e,{id:e,name:t.name,path:t.path})}))}handleModalClick(e){if(window.targetCheck(e,".remove-item")){const t=window.targetCheck(e,".selected-item");t&&this.removeSelectedTermFromModal(t.dataset.id)}else if(window.targetCheck(e,".back-to-parent"))this.navigateToParent();else if(window.targetCheck(e,".toggle-children")){const t=e.target.closest("li");this.navigateToChild(parseInt(t.dataset.id),t.querySelector(".term-name").textContent)}else if(window.targetCheck(e,".path-level")){const t=window.targetCheck(e,".path-level");this.navigateToPath(parseInt(t.dataset.id)||0)}}handleModalChange(e){if("checkbox"!==e.target.type)return;e.preventDefault(),e.stopPropagation();const t=parseInt(e.target.closest("li").dataset.id),i=e.target.closest("li").querySelector("label");e.target.checked?this.addSelectedTermToModal(t,i.title,i.dataset.path):this.removeSelectedTermFromModal(t)}openModal(){this.currentConfig?(this.updateModalUI(),this.updateModalSelections(),window.removeChildren(this.ui.termsList),this.showLoading()):console.error("No active field set")}closeModal(){this.observer.unobserve(this.ui.sentinel),window.removeChildren(this.ui.termsList),this.notify("selected-terms",{terms:this.selectedTerms,taxonomy:this.currentConfig.taxonomy}),this.activeField&&this.saveSelectionsToField(this.activeField),this.activeField=null,this.currentConfig=null}updateModalUI(){const e=this.getLabel(this.currentConfig.taxonomy,"single"),t=this.getLabel(this.currentConfig.taxonomy,"plural");this.ui.modal.title.textContent=`Select ${t}`,this.ui.search.container&&(this.ui.search.container.style.display=this.currentConfig.canSearch?"block":"none"),this.ui.create.details&&(this.ui.create.details.style.display=this.currentConfig.canCreate?"block":"none",this.ui.create.details.hidden=!this.currentConfig.canCreate,this.ui.create.summary&&(this.ui.create.summary.textContent=`Add new ${e}`),this.ui.create.label.name&&(this.ui.create.label.name.textContent=`Name this ${e}`),this.ui.create.label.parent&&(this.ui.create.label.parent.textContent="Nest it under")),this.a11y?.announce(`Opened ${e} selection. Choose from checkboxes or search to filter results.`)}updateModalSelections(){window.removeChildren(this.ui.selectedTerms),this.selectedTerms.forEach(((e,t)=>{this.addTermDisplay(t,e.name,e.path,"modal")})),this.checkSelectionLimits()}addSelectedTermToModal(e,t,i){this.selectedTerms.set(e,{id:e,name:t,path:i}),this.addTermDisplay(e,t,i,"modal"),this.checkSelectionLimits();const s=this.ui.termsList.querySelector(`input[value="${e}"]`);s&&(s.checked=!0)}removeSelectedTermFromModal(e){this.selectedTerms.delete(parseInt(e));const t=this.ui.selectedTerms.querySelector(`[data-id="${e}"]`);t&&t.remove();const i=this.ui.termsList.querySelector(`input[value="${e}"]`);i&&(i.checked=!1),this.checkSelectionLimits()}checkSelectionLimits(){this.currentConfig&&0!==this.currentConfig.maxSelection&&(this.disabled=this.selectedTerms.size>=this.currentConfig.maxSelection,this.ui.termsList.querySelectorAll('input[type="checkbox"]').forEach((e=>{e.checked||(e.disabled=this.disabled)})))}saveSelectionsToField(e){const t=this.fields.get(e);t&&(t.selectedTerms.clear(),window.removeChildren(t.selectedContainer),this.selectedTerms.forEach(((i,s)=>{t.selectedTerms.add(s),this.addTermDisplay(s,i.name,i.path,"field",e)})),t.input.value=Array.from(t.selectedTerms).join(","),t.input.dispatchEvent(new Event("change",{bubbles:!0})))}addTermDisplay(e,t,i,s="field",o=null){const a="field"===s?this.fields.get(o):this.currentConfig,n="field"===s?a.selectedContainer:this.ui.selectedTerms;if(n.querySelector(`[data-id="${e}"]`))return;const r=window.getTemplate("selectedTerm");if(r.dataset.id=e,r.dataset.path=i,r.dataset.name=t,r.dataset.taxonomy=a.taxonomy,r.querySelector(".item-name").textContent=i,r.querySelector("button").title=`Remove ${t}`,n.appendChild(r),"modal"===s){const t=this.ui.termsList.querySelector(`input[value="${e}"]`);t&&(t.checked=!0)}}removeSelectedTerm(e,t){const i=this.fields.get(e);if(!i)return;i.selectedTerms.delete(parseInt(t));const s=i.selectedContainer.querySelector(`[data-id="${t}"]`);s&&s.remove(),i.input.value=Array.from(i.selectedTerms).join(","),i.input.dispatchEvent(new Event("change",{bubbles:!0}))}updateFieldFromInput(e){const t=this.fields.get(e);if(!t)return;const i=t.input.value.trim();t.selectedTerms.clear(),window.removeChildren(t.selectedContainer),i&&(i.split(",").map((e=>parseInt(e.trim()))).filter((e=>!isNaN(e))).forEach((e=>t.selectedTerms.add(e))),this.initFieldDisplay(e))}navigateToParent(){this.store.setFilters({parent:0,page:1}),window.removeChildren(this.ui.termsList),this.ui.breadcrumbs.back.hidden=!0}navigateToChild(e,t){this.store.setFilters({parent:e,page:1}),window.removeChildren(this.ui.termsList),this.updateBreadcrumbs(e,t),this.ui.breadcrumbs.back.hidden=!1}navigateToPath(e){this.store.setFilters({parent:e,page:1}),window.removeChildren(this.ui.termsList),this.ui.breadcrumbs.back.hidden=0===e}loadMoreTerms(){const e=this.store.filters.page||1;this.store.setFilter("page",e+1)}updateBreadcrumbs(e,t){const i=window.getTemplate("termBreadcrumb");i.dataset.id=e,i.textContent=t,i.title=t;const s=this.ui.breadcrumbs.nav.querySelector(`[data-id="${e}"]`);if(s)for(;s.nextElementSibling;)s.nextElementSibling.remove();else this.ui.breadcrumbs.nav.appendChild(i)}renderTerms(e=null,t=!1,i=!1){if(e||(e=this.store.getFiltered()),t||window.removeChildren(this.ui.termsList),0===e.length)return void(t||this.showEmptyState());const s=this.store.filters.parent||0;this.ui.breadcrumbs.back.hidden=0===s;const o=document.createDocumentFragment();e.forEach((e=>{const t=this.createTermElement({id:parseInt(e.id),name:e.name,hasChildren:e.hasChildren,path:e.path||null,show:i});t&&o.appendChild(t)})),this.ui.termsList.appendChild(o)}createTermElement(e){if(!e?.name)return null;const t=window.getTemplate("termListItem");t.dataset.id=e.id;const i=this.selectedTerms.has(e.id),s=t.querySelector("input"),o=t.querySelector("label"),a=t.querySelector(".term-name");if(s.id=`${this.currentConfig.container.id}${e.id}`,s.name=`${this.currentConfig.container.id}${this.currentConfig.taxonomy}-select`,s.value=e.id,s.disabled=!i&&this.disabled,s.checked=i,o.htmlFor=s.id,o.title=e.path||e.name,o.dataset.path=e.path,a.textContent=e.show?e.path:e.name,e.hasChildren){const i=window.getTemplate("termChildrenToggle");i.ariaLabel=`View sub-terms of ${e.name}`,t.appendChild(i)}return t}showAutocompleteResults(e,t,i){if(!e?.autocompleteDropdown)return;const s=e.autocompleteDropdown;if(window.removeChildren(s),0===t.length)this.showEmptyState("No items found.",s);else{const i=document.createDocumentFragment();t.forEach((t=>{const s=this.createAutocompleteItem(e,t);s&&i.appendChild(s)})),s.appendChild(i)}const o=e.currentAutocompleteQuery||i;if(e.canCreate&&o){t.find((e=>e.name.toLowerCase()===o.toLowerCase()))||s.appendChild(this.createAutocompleteCreateButton(o))}s.hidden=!1}createAutocompleteItem(e,t){const i=document.createElement("button");return i.type="button",i.className="autocomplete-item",i.dataset.id=t.id,i.dataset.name=t.name,i.dataset.path=t.path||t.name,i.textContent=t.path||t.name,i.addEventListener("click",(()=>{e.selectedTerms.add(parseInt(t.id)),this.addTermDisplay(t.id,t.name,t.path,"field",e.id),e.input.value=Array.from(e.selectedTerms).join(","),e.input.dispatchEvent(new Event("change",{bubbles:!0})),e.autocompleteDropdown.hidden=!0;const i=e.container.querySelector("input[data-autocomplete]");i&&(i.value="")})),i}createAutocompleteCreateButton(e){const t=document.createElement("button");t.type="button",t.className="autocomplete-item create-term",t.dataset.query=e;const i=document.createElement("strong");return i.textContent="Create: ",t.appendChild(i),t.appendChild(document.createTextNode(`"${e}"`)),t}showAutocompleteError(e){const t=this.fields.get(e);t?.autocompleteDropdown&&(window.removeChildren(t.autocompleteDropdown),this.showEmptyState("Hmmm... something went wrong",t.autocompleteDropdown))}showLoading(){this.ui.loading.loading.hidden=!1,this.modal.classList.add("loading");const e=this.store.filters.search||"",t=this.store.filters.parent||0,i=e?`searching for "${e}" items`:0===t?"loading items":"loading child items";window.typeLoop?this.stopTyping=window.typeLoop(this.ui.loading.text,i):this.ui.loading.text.textContent=i}hideLoading(){this.ui.loading.loading.hidden=!0,this.modal.classList.remove("loading"),this.stopTyping&&this.stopTyping()}showEmptyState(e="No items found.",t=null){t||(t=this.ui.termsList);const i=window.getTemplate("noResults"),s=i.querySelector("span");e&&s&&(s.textContent=e),t.appendChild(i)}getFieldId(e){if(e.dataset.fieldId)return e.dataset.fieldId;const t=e.closest("[data-field-id]");return t?.dataset.fieldId||null}getLabel(e,t="single"){return jvbSettings.labels[e]?.[t]||e}async batchFetchTaxonomies(){if(0===this.taxonomiesToFetch.size)return;const e=Array.from(this.taxonomiesToFetch);this.taxonomiesToFetch.clear(),this.store.setFilters({taxonomy:e.join(","),page:1,search:"",parent:0})}async preloadTaxonomy(e){await this.store.setFilters({taxonomy:e,page:1,search:"",parent:0})}handleError(e,t,i=null){console.error(`Taxonomy ${t} error:`,e,i),this.error?.log&&this.error.log(e,{component:"TaxonomySelector",action:t,detail:i}),this.modal?.open&&this.showEmptyState("Error loading. Please try again.")}subscribe(e){return this.subscribers.add(e),()=>this.subscribers.delete(e)}notify(e,t={}){this.subscribers.forEach((i=>{try{i(e,t)}catch(e){console.error("Subscriber error:",e)}}))}destroy(){document.removeEventListener("click",this.handleClick),document.removeEventListener("change",this.handleChange),document.removeEventListener("input",this.handleInput),document.removeEventListener("focus",this.handleFocus),document.removeEventListener("blur",this.handleBlur),this.observer?.disconnect(),this.store.destroy(),this.subscribers.clear(),this.fields.clear(),this.selectedTerms.clear(),this.searchContexts.clear()}}document.addEventListener("DOMContentLoaded",(()=>{window.auth.subscribe((t=>{"auth-loaded"===t&&(window.jvbSelector=new e)}))}))})();
\ No newline at end of file
+(()=>{class e{constructor(){this.container=document.querySelector("dialog#jvb-selector"),this.container&&(this.a11y=window.jvbA11y,this.error=window.jvbError,this.subscribers=new Set,this.fields=new Map,this.selectedTerms=new Map,this.loadedTaxonomies=new Set,this.batchFetch=new Set,this.activeField=null,this.isInitializing=!0,this.init())}init(){this.initStore(),this.initElements(),this.initModal(),this.scanExistingFields(),this.initListeners(),this.needsCreator()&&window.jvbTaxCreator&&(this.creator=new window.jvbTaxCreator(this)),this.isInitializing=!1,this.batchFetchTaxonomies().then((()=>{}))}initStore(){const e=window.jvbStore.register("taxonomies",{storeName:"terms",keyPath:"id",showLoading:!1,indexes:[{name:"taxonomy",keyPath:"taxonomy"},{name:"parent",keyPath:"parent"},{name:"slug",keyPath:"slug",unique:!0},{name:"count",keyPath:"count"}],endpoint:"terms",TTL:12e4,filters:{taxonomy:"",page:1,search:"",parent:0},required:"taxonomy",delayFetch:!0});this.store=e.terms,this.store.subscribe(this.handleStoreEvent.bind(this))}initElements(){this.selectors={search:{input:"[type=search]",clear:".clear-search",container:".search-wrapper",results:".search-results"},terms:{list:".items-container",wrap:".items-wrap",sentinel:".scroll-sentinel"},nav:{nav:"nav.term-navigation",back:".back-to-parent",child:".toggle-children",pathLevel:".path-level"},loading:{loading:".loading",text:".loading span"},selected:".selected-items",modal:{title:"#modal-title",content:".modal-content",count:".selection-count"},favourites:".favourite-terms",field:{toggle:"button.taxonomy-toggle",value:'input[type="hidden"]',selected:".selected-items",dropdown:".search-results",search:"[data-autocomplete]"}},this.ui=window.uiFromSelectors(this.selectors)}initListeners(){this.observer=new IntersectionObserver((e=>{e.forEach((e=>{e.isIntersecting&&this.nextPage()}))}),{root:this.ui.terms.sentinel,threshold:.5}),this.clickHandler=this.handleClick.bind(this),this.changeHandler=this.handleChange.bind(this),this.inputHandler=this.handleInput.bind(this),this.focusHandler=this.handleFocus.bind(this),this.blurHandler=this.handleBlur.bind(this),document.addEventListener("click",this.clickHandler),document.addEventListener("change",this.changeHandler),document.addEventListener("input",this.inputHandler),document.addEventListener("focus",this.focusHandler,!0),document.addEventListener("blur",this.blurHandler,!0)}handleClick(e){const t=this.getFieldId(e.target),i=this.fields.get(t);if(!t||!i)return;const s=window.targetCheck(e,"[data-autocomplete-select]");if(s){let e=parseInt(s.dataset.id);this.addSelected(e,t),i.ui.dropdown&&(i.ui.dropdown.hidden=!0),i.ui.search&&(i.ui.search.value="")}if(window.targetCheck(e,i.ui.toggle))return e.preventDefault(),void this.openModal(t);const n=window.targetCheck(e,"button.remove-item");if(n){const e=this.getFieldId(n),t=n.closest(".selected-item").dataset.id??!1;return void(e&&t&&this.removeSelected(t,e))}if(e.target.matches(".modal-close"))return void this.modal?.handleClose();if(window.targetCheck(e,this.selectors.nav.back))return void this.navigateToParent();if(window.targetCheck(e,this.selectors.nav.child)){const t=e.target.closest("li"),i=parseInt(t.dataset.id);return void(i&&this.navigateTo(i))}const r=window.targetCheck(e,this.selectors.nav.pathLevel);if(r){const e=parseInt(r.dataset.id)??0;this.navigateTo(e)}if(window.targetCheck(e,i.selectors.dropdown))return void this.scheduleHideDropdown(t);if(window.targetCheck(e,this.selectors.search.clear)){const e=this.currentField();e&&e.ui.search&&(e.ui.search.value="",this.store.setFilters({search:"",page:1,parent:this.store.filters.parent||0})),this.ui.search.input&&(this.ui.search.input.value="")}}handleChange(e){if(!this.container.contains(e.target))return;if("checkbox"!==e.target.type)return;e.preventDefault(),e.stopPropagation();const t=parseInt(e.target.dataset.id);let i=this.getFieldId(e.target);e.target.checked?this.addSelected(t,i):this.removeSelected(t,i)}handleInput(e){let t=this.getFieldId(e.target)??this.activeField;if(!t)return;const i=this.fields.get(t);if(!i)return;this.container.open||(this.activeField=t);const s=e.target.value.trim();window.debouncer.schedule(`${t}-search`,(async()=>{await this.store.setFilters({taxonomy:i.taxonomy,search:s,page:1,parent:s?0:this.store.filters.parent||0}),this.container.open&&window.removeChildren(this.ui.terms.list)}),100)}handleFocus(e){const t=this.getFieldId(e.target),i=this.fields.get(t);t&&i&&(i.hasAutocomplete||i.hasSearch)&&(window.debouncer.cancel(`${t}-search-results`),this.container.open||(this.activeField=t,this.preloadTaxonomy(i.taxonomy)))}handleBlur(e){const t=this.getFieldId(e.target),i=this.fields.get(t);t&&i&&i.hasAutocomplete&&this.scheduleHideDropdown(t)}scheduleHideDropdown(e){const t=this.fields.get(e);t&&window.debouncer.schedule(`${e}-search-results`,(()=>{this.activeField=null,t.ui.dropdown.hidden=!0}),1500)}initModal(){this.modalID="dialog#jvb-selector",this.container=document.querySelector(this.modalID),this.modal=new window.jvbModal(this.container,{handleForm:!1,save:null,open:null}),this.modal.subscribe(((e,t)=>{e}))}toggleModal(e,t=!0){this.fields.get(e)&&(t?this.openModal(e):this.closeModal())}openModal(e){const t=this.fields.get(e);if(!t)return;this.activeField=e,this.ui.modal.title.textContent=`Select ${t.plural}`,this.ui.search.container&&(this.ui.search.container.hidden=!t.canSearch),this.ui.create.details&&(this.ui.create.details.hidden=!t.canCreate,this.ui.create.summary&&(this.ui.create.summary.textContent=`Add new ${t.singular}`),this.ui.create.label.name&&(this.ui.create.label.name.textContent=`Name this ${t.singular}`),this.ui.create.label.parent&&(this.ui.create.label.parent.textContent="Nest it under"));let i=`Opened ${t.singular} selection. Choose from checkboxes, or search to filter results.`;window.removeChildren(this.ui.terms.list),this.modal.handleOpen(),this.setLoading(),this.store.setFilters({taxonomy:t.taxonomy,page:1,search:"",parent:0}),this.a11y.announce(i)}closeModal(){this.modal.handleClose();const e=this.fields.get(this.activeField);if(!e)return;this.observer.unobserve(this.ui.terms.sentinel),window.removeChildren(this.ui.terms.list),this.notify("selected-terms",{terms:this.selectedTerms.get(this.activeField),taxonomy:e.taxonomy}),this.activeField=null;let t=`Closed ${e.singular} selector.`;this.a11y.announce(t)}navigateToParent(){const e=this.store.filters.parent;if(0===e)return;let t=this.store.get(parseInt(e));if(!t)return;let i=t.parent;this.navigateTo(parseInt(i))}navigateTo(e=0){e=parseInt(e)??0,this.store.setFilters({parent:e,page:1}),window.removeChildren(this.ui.terms.list),this.updateBreadcrumbs(e)}nextPage(){let e=this.store.filters.page,t=Math.min(e++,this.store.lastResponse.total);this.store.setFilters({page:t})}prevPage(){let e=this.store.filters.page,t=Math.max(e-1,1);this.store.setFilters({page:t})}addTermToModal(e){const t=this.store.get(e);if(!t)return;const i=window.getTemplate("selectedTerm");i.dataset.id=e,i.querySelector("span").textContent=t.path,i.querySelector("button").title=`Remove ${name}`,this.ui.selected.append(i)}scanExistingFields(e=document.body){e.querySelectorAll('[data-type="selector"]').forEach((e=>{try{this.registerField(e)}catch(t){this.error.log(t,{component:"TaxonomySelector",action:"scanExistingFields",container:e.dataset.name})}}))}registerField(e,t={}){let i=e.querySelector('input[type="hidden"]');if(!i)return void console.warn("TaxonomySelector: No hidden input found for field",e);"fieldId"in e.dataset||(e.dataset.fieldId=window.generateID("selector"));const s=e.dataset.fieldId;let n=this.selectors.field,r=e.querySelector("button.taxonomy-toggle");if(0===t.size){if(!r)return;if(0===(t=r.dataset).size)return}else Object.hasOwn(t,"toggle")&&(r=document.querySelector(t.toggle),n.toggle=t.toggle);const a={id:s,value:i,element:e,taxonomy:t.taxonomy??!1,singular:t.single??"",plural:t.plural??"",name:e.dataset.field,canSearch:Object.hasOwn(t,"search"),limit:t.limit??0,hasAutocomplete:Object.hasOwn(t,"autocomplete"),canCreate:Object.hasOwn(t,"creatable"),isRequired:Object.hasOwn(t,"required"),toggle:r,selectors:n,ui:window.uiFromSelectors(n,e),checked:!1};if(!a.taxonomy)return;this.fields.set(s,a);let o=new Set;return i.value.value.trim().split(",").map((e=>parseInt(e.trim()))).filter((e=>!isNaN(e))).forEach((e=>o.add(e))),this.selectedTerms.set(s,o),this.isInitializing&&this.batchFetch.add(a.taxonomy),this.updateFieldUI(s),s}addSelected(e,t=null){t||(t=this.activeField);const i=this.fields.get(t),s=this.store.get(e);if(!i||!s)return;const n=this.selectedTerms.get(t);0!==i.limit&&n.size>=i.limit||(n.add(parseInt(e)),this.addTermToDisplay(e,t),this.updateFieldValue(t),this.checkLimits(t))}removeSelected(e,t=null){t||(t=this.activeField);const i=this.fields.get(t),s=this.store.get(e);if(!i||!s)return;this.selectedTerms.get(t).delete(parseInt(e));const n=i.ui.selected.querySelector(`[data-i"${e}"]`);if(n&&n.remove(),this.container.open){let t=this.ui.selected.querySelector(`[data-id="${e}"]`);t&&t.remove()}this.updateFieldValue(t),this.checkLimits(t)}updateFieldValue(e){const t=this.fields.get(e);if(!t)return;let i=Array.from(this.selectedTerms.get(e));t.ui.value=i.join(",")}checkLimits(e){if(!this.container.open)return;const t=this.fields.get(e);if(!t||0===t.limit)return;const i=this.selectedTerms.get(e).size>=t.limit;this.setCheckboxes(i)}updateFieldUI(e){const t=this.fields.get(e);let i=this.selectedTerms.get(e);t&&0!==i.size&&Array.from(i).forEach((t=>{this.addTermToDisplay(t,e)}))}updateFieldsForTaxonomy(e){let t=Array.from(this.fields.values()).filter((t=>!t.checked&&t.taxonomy===e));const i=Array.from(this.store.data.values()).some((t=>t.taxonomy===e));t.forEach((e=>{e.ui.toggle.disabled=!i&&!e.canCreate,e.ui.toggle.title=i?`Select ${e.plural}`:`No ${e.singular} available`,e.checked=!0}))}showModalTerms(e=!0,t=!1){const i=this.store.getFiltered();if(0===i.size)return;e||window.removeChildren(this.ui.terms.list);const s=this.store.filters.parent??0;this.ui.nav.back.hidden=0===s;const n=document.createDocumentFragment();i.forEach((e=>{const i=this.createTermElement({show:t,...e});i&&n.appendChild(i)})),this.ui.terms.list.append(n)}createTermElement(e){if(!e||!e.name)return null;const t=window.getTemplate("termListItem");t.dataset.id=e.id;const i=this.selectedTerms.get(this.activeField).has(e.id);let[s,n,r]=[t.querySelector("input"),t.querySelector("label"),t.querySelector("span, .term-name")],a=this.currentField(),o=a.limit>0&&this.selectedTerms.get(this.activeField).size>=a.limit;if(s&&n&&r&&([s.id,s.name,s.value,s.disabled,s.checked,n.htmlFor,n.title,n.dataset.path,r.textContent]=[`${a.element.id}-${e.id}`,`${a.container.id}-${a.taxonomy}-select`,e.id,!i&&o,i,`${a.element.id}-${e.id}`,e.path??e.name,e.path,e.show?e.path:e.name],e.hasChildren)){const i=window.getTemplate("termChildrenToggle");i&&(i.ariaLabel=`View ${a.plural} nested under ${e.name}`,t.append(i))}return t}showAutocompleteTerms(){const e=this.currentField(),t=this.currentTerms();if(!e||0===t.size)return;const i=e.ui.dropdown;window.removeChildren(i),0===t.length?this.showEmptyState(`No ${e.plural} found.`,i):t.forEach((e=>{const t=this.createAutocompleteTerm(e);t&&i.append(t)}));const s=e.ui.search?.value;if(e.canCreate&&s.length>=2&&this.creator){const e=this.createTermButton(s);e&&i.append(e)}i.hidden=!1}createAutocompleteTerm(e){const t=window.getTemplate("autocompleteItem");if(t)return t.dataset.id=e.id,t.textContent=e.path||e.name,t}addTermToDisplay(e,t){const i=this.store.get(e),s=this.fields.get(t);if(!i||!s)return;if(s.ui.selected.querySelector(`[data-id="${e}"]`))return;const n=window.getTemplate("selectedTerm");if(n&&(n.dataset.id=e,n.dataset.taxonomy=s.taxonomy,n.querySelector(".item-name").textContent=i.path,n.querySelector("button").title=`Remove ${i.name}`,s.ui.selected.append(n),this.container.open)){this.addTermToModal(e);const t=this.ui.terms.list.querySelector(`input[value="${e}"]`);t&&(t.checked=!0)}}createTermButton(e){const t=window.getTemplate("autocompleteButton");if(!t)return;return t.querySelector("span").textContent=`"${e}"`,t}updateBreadcrumbs(e){const t=this.ui.nav.nav;if(!t)return;const i=Array.from(t.children).find((t=>parseInt(t.dataset.id)===e));if(i){let e=i.nextElementSibling;for(;e;){const t=e;e=e.nextElementSibling,t.remove()}}else{const i=this.store.get(e);if(!i)return;const s=window.getTemplate("termBreadcrumb");if(!s)return;s.dataset.id=e,s.textContent=i.name,s.title=i.name,t.append(s)}}updateSelectionCount(){if(!this.container.open)return;const e=this.fields.get(this.activeField);if(e&&this.ui.modal.count){const t=this.selectedTerms.get(this.activeField).size;this.ui.modal.count.textContent=e.limit>0?`${t} of ${e.limit} ${e.plural} selected`:`${t} ${e.plural} selected`}}currentField(){return this.fields.get(this.activeField)??!1}currentTerms(){return this.store.getFiltered()}needsCreator(){return Array.from(this.fields.values()).some((e=>e.canCreate||e.hasAutocomplete))}getFieldId(e){if(e.dataset.fieldId)return e.dataset.fieldId;const t=e.closest("[data-field-id]");return t?.dataset.fieldId||null}setCheckboxes(e){this.ui.terms.list.querySelectorAll("input[type=checkbox]").forEach((t=>{t.checked||(t.disabled=e)}))}handleStoreEvent(e,t){const i={"data-loaded":()=>this.handleDataLoaded(),"filters-changed":()=>this.handleFiltersChanged(),"fetch-error":()=>this.handleFetchError()};i[e]?.()}handleDataLoaded(){const e=this.store.filters.taxonomy;if(e?.includes(",")){e.split(",").map((e=>e.trim())).forEach((e=>this.updateFieldsForTaxonomy(e)))}this.container.open?this.showResults():this.activeField&&this.showResults(!0)}showResults(e=!1){this.setLoading(!1);const t=this.store.getFiltered(),i=this.store.filters,s=this.store.lastResponse?.page||{},n=i.search&&i.search.length>0,r=i.page>1,a=this.currentField();this.notify("terms-loaded",{terms:t,filters:i}),0===t.length?(r||this.showEmptyState(n?`No matching ${a.plural}.`:`No ${a.plural} available.`),this.observer.unobserve(this.ui.terms.sentinel)):e?this.showAutocompleteTerms():(this.showModalTerms(r,n),s.has_more?this.observer.observe(this.ui.terms.sentinel):this.observer.unobserve(this.ui.terms.sentinel)),this.a11y.announce(t.length,r)}handleFiltersChanged(){}handleFetchError(e){this.setLoading(!1)}async batchFetchTaxonomies(){if(0===this.batchFetch.size)return;const e=Array.from(this.batchFetch);e.forEach((e=>this.loadedTaxonomies.add(e))),this.batchFetch.clear();try{e.forEach((e=>this.loadedTaxonomies.add(e))),await this.store.setFilters({taxonomy:e.join(","),page:1,search:"",parent:0})}catch(e){console.error("Failed to batch fetch taxonomies:",e)}}preloadTaxonomy(e){this.loadedTaxonomies.has(e)||(this.store.setFilters({taxonomy:e,page:1,search:"",parent:0}),this.loadedTaxonomies.add(e))}setLoading(e=!0){if(this.ui.loading.loading.hidden=e,this.modal.classList.toggle("loading",e),e){let e=this.store.filters.search||"";e=""!==e&&e;const t=this.store.filters.parent||0,i=e?`Searching for "${e} items`:0===t?"loading items":"loading child items";window.typeLoop&&this.ui.loading.text?this.stopTyping=window.typeLoop(this.ui.loading.text,i):this.ui.loading.text.textContenet=i}else this.stopTyping&&(this.stopTyping(),this.stopTyping=null)}showEmptyState(e="No items found.",t=null){t||(t=this.ui.terms.list);const i=window.getTemplate("noTermResults"),s=i.querySelector("span");e&&s&&(s.textContent=e),t.append(i)}subscribe(e){return this.subscribers.add(e),()=>this.subscribers.delete(e)}notify(e,t={}){this.subscribers.forEach((i=>{try{i(e,t)}catch(e){console.error("Subscriber error:",e)}}))}destroy(){document.removeEventListener("click",this.clickHandler),document.removeEventListener("change",this.changeHandler),document.removeEventListener("input",this.inputHandler),document.removeEventListener("focus",this.focusHandler),document.removeEventListener("blur",this.blurHandler),this.observer?.disconnect(),this.subscribers.clear(),this.fields.clear(),this.selectedTerms.clear()}}document.addEventListener("DOMContentLoaded",(function(){window.auth.subscribe((t=>{"auth-loaded"===t&&(window.jvbSelector=new e)}))}))})();
\ No newline at end of file
diff --git a/inc/forms/TaxonomySelector.php b/inc/forms/TaxonomySelector.php
index ab3d7d5..8156ebb 100644
--- a/inc/forms/TaxonomySelector.php
+++ b/inc/forms/TaxonomySelector.php
@@ -144,10 +144,10 @@
 				</div>
 
 				<!-- Create new term section -->
-				<details class="create-new-term" hidden>
+				<details class="create-term" hidden>
 					<summary class="row btw">Add New Term</summary>
 					<div class="create-new-term-section">
-						<form class="create-term-form" data-nocache data-form-id="create-term" data-save="terms">
+						<form class="create-term" data-nocache data-form-id="create-term" data-save="terms">
 							<div class="form-group">
 								<label for="term_name">Term Name:</label>
 								<input type="text" name="term_name" id="term_name" required>
@@ -162,7 +162,7 @@
 
 							<button type="button" class="submit-term">Add Term</button>
 						</form>
-
+						<div class="term-suggestions" hidden><h4></h4><ul class="term-suggestion-list"></ul></div>
 						<div class="loading-message create-term" hidden>
 							<span id="typed-text"></span>
 							<span class="cursor">|</span>
@@ -175,7 +175,13 @@
 		<template class="loadingItems">
 			<p>{ <span>loading items</span> }</p>
 		</template>
-		<template class="noResults">
+		<template class="autocompleteButton">
+			<button class="autocomplete submit-term" type="button"><strong>Create: </strong><span></span></button>
+		</template>
+		<template class="autocompleteItem">
+			<button class="autocomplete item" type="button" data-autocomplete-select></button>
+		</template>
+		<template class="noTermResults">
 			<p>{ <span>nothing found</span> }</p>
 		</template>
 		<template class="termListItem">
@@ -246,7 +252,7 @@
 				</button>
 				<?php if ($hasAutocomplete !== '') { ?>
 					<input type="text" id="<?= $this->base ?><?= esc_attr($this->config['name']) ?>-autocomplete" autocomplete="off" data-ignore data-autocomplete>
-					<ul class="autocomplete-dropdown" hidden>
+					<ul class="search-results" hidden>
 					</ul>
 				<?php } ?>
 			</div>
diff --git a/inc/meta/MetaForm.php b/inc/meta/MetaForm.php
index aa537c0..51959aa 100644
--- a/inc/meta/MetaForm.php
+++ b/inc/meta/MetaForm.php
@@ -1408,9 +1408,10 @@
 		}
 
 		?>
-		<div class="field <?= esc_attr($type) ?> <?= esc_attr($name) ?>"
+		<div class="field selector <?= esc_attr($type) ?> <?= esc_attr($name) ?>"
 			<?= $conditional ?>
 			 data-field="<?= esc_attr($name) ?>"
+			 data-type="selector" data-subtype="<?= esc_attr($type)?>"
 			<?= $validationAttrs ?>
 			<?= $describedBy ?>>
 

--
Gitblit v1.10.0