(function () {
	'use strict';

	window.LogisticaTabLoading = (function () {
		var delayTimers = new WeakMap();

		function resolveHost(container) {
			var host = container;

			if (!host) {
				return null;
			}

			if (typeof host === 'string') {
				return document.querySelector(host);
			}

			if (host.matches && host.matches('[data-logistica-tab-loading], [data-user-panel-loading]')) {
				host = host.parentElement;
			}

			if (host.matches && (
				host.matches('[data-tab-panel]') ||
				host.matches('.logistica-user-panel') ||
				host.matches('.logistica-panel') ||
				host.matches('[data-planeacion-board]') ||
				host.matches('[data-warehouse-board]') ||
				host.matches('[data-minutas-board]') ||
				host.matches('[data-conversion-board]') ||
				host.matches('[data-cierre-caracteristicas-board]') ||
				host.matches('[data-etiqueta-catalog-board]') ||
				host.matches('.logistica-conversion-board') ||
				host.matches('.logistica-cierre-caracteristicas-board') ||
				host.matches('.logistica-etiqueta-catalog-board') ||
				host.matches('[data-close-workspace]')
			)) {
				return host;
			}

			return host.closest(
				'[data-tab-panel], .logistica-user-panel, .logistica-panel, [data-planeacion-board], [data-warehouse-board], [data-minutas-board], [data-conversion-board], [data-cierre-caracteristicas-board], [data-etiqueta-catalog-board], .logistica-conversion-board, .logistica-cierre-caracteristicas-board, .logistica-etiqueta-catalog-board, [data-close-workspace], .wrap.logistica-admin'
			) || host;
		}

		function getOverlay(host) {
			var overlay;
			var panel;

			if (!host) {
				return null;
			}

			overlay = host.querySelector('[data-logistica-tab-loading], [data-user-panel-loading], [data-close-loading]');
			if (overlay) {
				return overlay;
			}

			panel = host.closest('[data-tab-panel]');
			if (panel && panel !== host) {
				return panel.querySelector('[data-logistica-tab-loading], [data-user-panel-loading], [data-close-loading]');
			}

			return null;
		}

		function getMessageNode(overlay) {
			if (!overlay) {
				return null;
			}

			return overlay.querySelector('[data-logistica-tab-loading-message], [data-user-panel-loading-message], p');
		}

		function reveal(host, overlay) {
			overlay.hidden = false;
			overlay.setAttribute('aria-busy', 'true');
			host.classList.add('is-tab-loading');
		}

		function show(container, message, immediate) {
			var host = resolveHost(container);
			var overlay = getOverlay(host);

			if (!host || !overlay) {
				return;
			}

			clearTimeout(delayTimers.get(host));

			if (message) {
				var msgNode = getMessageNode(overlay);
				if (msgNode) {
					msgNode.textContent = message;
				}
			}

			if (immediate) {
				reveal(host, overlay);
				return;
			}

			delayTimers.set(host, window.setTimeout(function () {
				reveal(host, overlay);
			}, 500));
		}

		function hide(container) {
			var host = resolveHost(container);
			var overlay;
			var panel;

			if (!host) {
				return;
			}

			panel = host.closest('[data-tab-panel]');
			clearTimeout(delayTimers.get(host));
			if (panel) {
				clearTimeout(delayTimers.get(panel));
			}

			overlay = getOverlay(host);

			if (overlay) {
				overlay.hidden = true;
				overlay.setAttribute('aria-busy', 'false');
			}

			host.classList.remove('is-tab-loading');
			if (panel) {
				panel.classList.remove('is-tab-loading');
			}
		}

		return {
			show: show,
			hide: hide,
			resolveHost: resolveHost
		};
	}());

	var MONEY_FIELDS = {
		precio_kg: { decimals: 0, integer: true },
		precio_total: { decimals: 0, integer: true },
		precio_und: { decimals: 2, integer: false },
		precio_fabricacion: { decimals: 0, integer: true },
		service_total: { decimals: 0, integer: true }
	};

	function isMoneyField(name) {
		return !!name && Object.prototype.hasOwnProperty.call(MONEY_FIELDS, name);
	}

	function moneyFieldConfig(name, decimals, integer) {
		var cfg = MONEY_FIELDS[name];

		if (!cfg) {
			return {
				decimals: typeof decimals === 'number' ? decimals : 0,
				integer: !!integer
			};
		}

		return {
			decimals: typeof decimals === 'number' ? decimals : cfg.decimals,
			integer: typeof integer === 'boolean' ? integer : cfg.integer
		};
	}

	function parseMoneyValue(raw) {
		var text = String(raw || '').trim();

		if (!text) {
			return 0;
		}

		text = text.replace(/\$/g, '').replace(/\s/g, '');

		if (text.indexOf(',') >= 0) {
			text = text.replace(/\./g, '').replace(',', '.');
		} else if ((text.match(/\./g) || []).length > 1) {
			text = text.replace(/\./g, '');
		} else if (/^\d+\.\d{3}$/.test(text)) {
			text = text.replace(/\./g, '');
		}

		var parsed = Number.parseFloat(text.replace(/[^\d.-]/g, ''));

		return Number.isFinite(parsed) ? parsed : 0;
	}

	function numberValue(element) {
		if (!element) {
			return 0;
		}

		var fieldName = element.getAttribute('data-field');

		if (fieldName && isMoneyField(fieldName)) {
			return parseMoneyValue(element.value);
		}

		if (fieldName && isUnitsIntegerField(fieldName)) {
			return Math.round(parseMoneyValue(element.value));
		}

		if (element.classList && element.classList.contains('logistica-units-field')) {
			return Math.round(parseMoneyValue(element.value));
		}

		return Number.parseFloat(String(element.value).replace(',', '.')) || 0;
	}

	function updateProductTotal(form) {
		var perBox = numberValue(form.querySelector('[name="cantidad_por_caja"]'));
		var boxes = numberValue(form.querySelector('[name="numero_cajas"]'));
		var loose = numberValue(form.querySelector('[name="saldos_sueltos"]'));
		var result = form.querySelector('.js-total-result');

		if (result) {
			result.value = Math.round((perBox * boxes) + loose);
		}
	}

	function updateCalculator(calculator) {
		var type = calculator.getAttribute('data-calculator');
		var result = calculator.querySelector('[data-result]');
		var value = 0;

		if (!result) {
			return;
		}

		if (type === 'kg-hojas') {
			value = Math.floor(numberValue(calculator.querySelector('[data-field="kg"]')) / numberValue(calculator.querySelector('[data-field="kg_per_sheet"]')));
		}

		if (type === 'hojas-kg') {
			value = numberValue(calculator.querySelector('[data-field="hojas"]')) * numberValue(calculator.querySelector('[data-field="kg_per_sheet"]'));
		}

		if (type === 'consumo') {
			value = numberValue(calculator.querySelector('[data-field="units"]')) * numberValue(calculator.querySelector('[data-field="consumption"]'));
		}

		if (type === 'cajas') {
			var perBox = numberValue(calculator.querySelector('[data-field="per_box"]'));
			value = perBox > 0 ? Math.ceil(numberValue(calculator.querySelector('[data-field="units"]')) / perBox) : 0;
		}

		if (type === 'unidades') {
			value = (numberValue(calculator.querySelector('[data-field="boxes"]')) * numberValue(calculator.querySelector('[data-field="per_box"]'))) + numberValue(calculator.querySelector('[data-field="loose"]'));
		}

		if (type === 'saldo') {
			value = Math.max(0, numberValue(calculator.querySelector('[data-field="available"]')) - numberValue(calculator.querySelector('[data-field="used"]')));
		}

		result.textContent = Number.isFinite(value) ? String(Math.round(value * 1000) / 1000) : '0';
	}

	function displayNumber(value, decimals, integer) {
		if (!Number.isFinite(value)) {
			return '-';
		}

		if (integer) {
			var intValue = Math.round(value);
			var parts = String(intValue).split('');
			var out = [];
			var count = 0;

			for (var i = parts.length - 1; i >= 0; i--) {
				out.unshift(parts[i]);
				count += 1;
				if (count === 3 && i > 0) {
					out.unshift('.');
					count = 0;
				}
			}

			return out.join('');
		}

		var fixed = value.toFixed(decimals);
		var split = fixed.split('.');
		var whole = split[0];
		var decimalPart = split[1] || '';
		var digits = whole.replace(/[^0-9-]/g, '');
		var sign = digits.charAt(0) === '-' ? '-' : '';
		var numbers = sign ? digits.slice(1) : digits;
		var groups = [];

		while (numbers.length > 3) {
			groups.unshift(numbers.slice(-3));
			numbers = numbers.slice(0, -3);
		}
		if (numbers) {
			groups.unshift(numbers);
		}

		return sign + groups.join('.') + (decimalPart ? ',' + decimalPart : '');
	}

	function displayMoney(value, decimals, integer) {
		if (!Number.isFinite(value)) {
			return '-';
		}

		return '$ ' + displayNumber(value, decimals, integer);
	}

	function formatMoneyInput(input) {
		var fieldName = input ? input.getAttribute('data-field') : '';
		var cfg;
		var value;

		if (!input || !isMoneyField(fieldName)) {
			return;
		}

		cfg = moneyFieldConfig(fieldName);
		value = parseMoneyValue(input.value);
		input.value = Number.isFinite(value) ? displayMoney(value, cfg.decimals, cfg.integer) : '';
	}

	function bindMoneyInput(input) {
		var fieldName;

		if (!input || input.dataset.moneyBound === '1') {
			return;
		}

		fieldName = input.getAttribute('data-field');

		if (!isMoneyField(fieldName)) {
			return;
		}

		input.dataset.moneyBound = '1';
		input.setAttribute('data-money-field', '1');
		input.classList.add('logistica-money-field');

		if (input.type === 'number') {
			input.type = 'text';
		}

		input.setAttribute('inputmode', 'decimal');
		formatMoneyInput(input);

		input.addEventListener('focus', function () {
			var value = parseMoneyValue(input.value);

			input.value = Number.isFinite(value) ? String(value) : '';
		});

		input.addEventListener('blur', function () {
			formatMoneyInput(input);
		});
	}

	function initMoneyFields(root) {
		var scope = root || document;

		scope.querySelectorAll('input[data-field], select[data-field]').forEach(function (input) {
			bindMoneyInput(input);
		});

		scope.querySelectorAll('[data-output]').forEach(function (outputNode) {
			var fieldName = outputNode.getAttribute('data-output');

			if (isMoneyField(fieldName)) {
				outputNode.classList.add('is-money');
			}
		});
	}

	function isUnitsIntegerField(name) {
		return name === 'unidades';
	}

	function formatUnitsIntegerInput(input) {
		var value;

		if (!input || !isUnitsIntegerField(input.getAttribute('data-field'))) {
			return;
		}

		value = parseMoneyValue(input.value);

		if (!String(input.value || '').trim()) {
			input.value = '';
			return;
		}

		input.value = Number.isFinite(value) ? displayNumber(value, 0, true) : '';
	}

	function bindUnitsIntegerInput(input) {
		if (!input || input.dataset.unitsBound === '1' || !isUnitsIntegerField(input.getAttribute('data-field'))) {
			return;
		}

		input.dataset.unitsBound = '1';
		input.classList.add('logistica-units-field');

		if (input.type === 'number') {
			input.type = 'text';
		}

		input.setAttribute('inputmode', 'numeric');
		formatUnitsIntegerInput(input);

		input.addEventListener('focus', function () {
			var value = parseMoneyValue(input.value);

			input.value = Number.isFinite(value) ? String(Math.round(value)) : '';
		});

		input.addEventListener('blur', function () {
			formatUnitsIntegerInput(input);
		});
	}

	function initClosingUnitsFields(root) {
		var scope = root || document;

		scope.querySelectorAll('[data-close-reference] [data-field="unidades"]').forEach(function (input) {
			bindUnitsIntegerInput(input);
		});
	}

	function field(row, name) {
		return numberValue(row.querySelector('[data-field="' + name + '"]'));
	}

	function isNumericFieldMissing(card, fieldName) {
		var input = card ? card.querySelector('[data-field="' + fieldName + '"]') : null;
		var value;

		if (!input) {
			return true;
		}

		if (!String(input.value || '').trim()) {
			return true;
		}

		value = numberValue(input);

		return !Number.isFinite(value) || value <= 0;
	}

	function isNumericFieldUnset(card, fieldName, minValue) {
		var input = card ? card.querySelector('[data-field="' + fieldName + '"]') : null;
		var value;
		var min = minValue === undefined ? 0 : minValue;

		if (!input) {
			return true;
		}

		if (!String(input.value || '').trim()) {
			return true;
		}

		value = numberValue(input);

		return !Number.isFinite(value) || value < min;
	}

	function isResinaPctMissing(card) {
		var input = card ? card.querySelector('[data-field="resina_pct"]') : null;
		var value;

		if (!input || !String(input.value || '').trim()) {
			return true;
		}

		value = numberValue(input);

		return !Number.isFinite(value) || value < 0 || value > 100;
	}

	function output(row, name, value, decimals, integer) {
		var element = row.querySelector('[data-output="' + name + '"]');
		var cfg;

		if (!element) {
			return;
		}

		if (isMoneyField(name)) {
			cfg = moneyFieldConfig(name, decimals, integer);
			element.classList.add('is-money');
			element.textContent = displayMoney(value, cfg.decimals, cfg.integer);
			return;
		}

		element.textContent = displayNumber(value, decimals, integer);
	}

	function outputText(row, name, value) {
		var element = row.querySelector('[data-output="' + name + '"]');

		if (element) {
			element.textContent = value;
		}
	}

	function storageKey() {
		return 'logistica_conversion_tables_v1';
	}

	var conversionTablesSaveTimer = null;

	function conversionTablesNonce() {
		return window.LogisticaAdmin && window.LogisticaAdmin.conversionTablesNonce
			? window.LogisticaAdmin.conversionTablesNonce
			: '';
	}

	function conversionTablesAjaxUrl() {
		return window.LogisticaAdmin && window.LogisticaAdmin.ajaxUrl
			? window.LogisticaAdmin.ajaxUrl
			: '';
	}

	/**
	 * Caché en memoria del estado completo de tablas (todas las hojas).
	 * Evita que un guardado desde una sola pestaña borre el resto.
	 * @type {Object|null}
	 */
	var conversionStateCache = null;

	function mergeConversionStates(base, overlay) {
		var merged = {};

		if (base && typeof base === 'object') {
			Object.keys(base).forEach(function (sheetKey) {
				merged[sheetKey] = base[sheetKey];
			});
		}

		if (overlay && typeof overlay === 'object') {
			Object.keys(overlay).forEach(function (sheetKey) {
				merged[sheetKey] = overlay[sheetKey];
			});
		}

		return merged;
	}

	function preferRicherConversionStates(serverState, localState) {
		var merged = {};
		var keys = {};

		[serverState, localState].forEach(function (source) {
			if (!source || typeof source !== 'object') {
				return;
			}
			Object.keys(source).forEach(function (sheetKey) {
				keys[sheetKey] = true;
			});
		});

		Object.keys(keys).forEach(function (sheetKey) {
			var serverSheet = serverState && serverState[sheetKey] ? serverState[sheetKey] : null;
			var localSheet = localState && localState[sheetKey] ? localState[sheetKey] : null;
			var serverRows = serverSheet && Array.isArray(serverSheet.rows) ? serverSheet.rows.length : -1;
			var localRows = localSheet && Array.isArray(localSheet.rows) ? localSheet.rows.length : -1;

			if (serverRows < 0 && localRows < 0) {
				return;
			}

			if (localRows > serverRows) {
				merged[sheetKey] = localSheet;
			} else if (serverSheet) {
				merged[sheetKey] = serverSheet;
			} else {
				merged[sheetKey] = localSheet;
			}
		});

		return merged;
	}

	function collectDomConversionPartial() {
		var partial = {};

		document.querySelectorAll('.logistica-sheet[data-sheet]').forEach(function (sheet) {
			var data = serializeSheet(sheet);
			partial[data.type] = data;
		});

		return partial;
	}

	function collectConversionState() {
		var partial = collectDomConversionPartial();
		conversionStateCache = mergeConversionStates(conversionStateCache || readLocalConversionState(), partial);
		return conversionStateCache;
	}

	function readLocalConversionState() {
		if (typeof window === 'undefined' || !window.localStorage) {
			return {};
		}

		try {
			return JSON.parse(window.localStorage.getItem(storageKey()) || '{}');
		} catch (error) {
			return {};
		}
	}

	function writeLocalConversionState(state) {
		if (typeof window === 'undefined' || !window.localStorage) {
			return;
		}

		try {
			window.localStorage.setItem(storageKey(), JSON.stringify(state || {}));
		} catch (error) {
			// Local storage can be disabled; calculations should still work.
		}
	}

	function saveConversionStateToServer(state) {
		var ajaxUrl = conversionTablesAjaxUrl();
		var nonce = conversionTablesNonce();

		if (!ajaxUrl || !nonce) {
			return Promise.resolve(false);
		}

		// DOM gana en hojas abiertas; el resto del caché/local se conserva para no perderlas.
		var toSend = mergeConversionStates(state || conversionStateCache || {}, collectDomConversionPartial());

		var body = new FormData();
		body.append('action', 'logistica_save_conversion_tables');
		body.append('nonce', nonce);
		body.append('state', JSON.stringify(toSend || {}));

		return fetch(ajaxUrl, {
			method: 'POST',
			credentials: 'same-origin',
			body: body
		}).then(function (response) {
			return response.json();
		}).then(function (payload) {
			if (payload && payload.success && payload.data && payload.data.state && typeof payload.data.state === 'object') {
				conversionStateCache = payload.data.state;
				writeLocalConversionState(conversionStateCache);
			}
			return !!(payload && payload.success);
		}).catch(function () {
			return false;
		});
	}

	function loadConversionStateFromServer() {
		var ajaxUrl = conversionTablesAjaxUrl();
		var nonce = conversionTablesNonce();

		if (!ajaxUrl || !nonce) {
			return Promise.resolve(null);
		}

		var body = new FormData();
		body.append('action', 'logistica_get_conversion_tables');
		body.append('nonce', nonce);

		return fetch(ajaxUrl, {
			method: 'POST',
			credentials: 'same-origin',
			body: body
		}).then(function (response) {
			return response.json();
		}).then(function (payload) {
			if (payload && payload.success && payload.data && payload.data.state && typeof payload.data.state === 'object') {
				return payload.data.state;
			}
			return null;
		}).catch(function () {
			return null;
		});
	}

	function saveConversionState() {
		var state = collectConversionState();

		writeLocalConversionState(state);

		if (conversionTablesSaveTimer) {
			window.clearTimeout(conversionTablesSaveTimer);
		}

		conversionTablesSaveTimer = window.setTimeout(function () {
			saveConversionStateToServer(state);
		}, 800);
	}

	/**
	 * Aviso de seguridad antes de eliminar datos en el panel.
	 *
	 * @param {string} message Mensaje principal.
	 * @param {string} [detail]  Detalle opcional (nombre de fila, variante, etc.).
	 * @param {boolean} [soft]   Si true, indica que irá a papelera (recuperable).
	 * @return {boolean} true si el usuario confirma.
	 */
	function confirmLogisticaDelete(message, detail, soft) {
		var text = String(message || '¿Eliminar este registro?');

		if (detail) {
			text += '\n\n' + String(detail);
		}

		if (soft) {
			text += '\n\nSe enviará a la papelera de trazabilidad y se podrá restaurar desde Configuración.';
		} else {
			text += '\n\nEsta acción no se puede deshacer.';
		}

		if (typeof window.confirm !== 'function') {
			return true;
		}

		return window.confirm(text);
	}

	var cierreAutosaveTimers = new WeakMap();
	var closingNombreAutosaveTimers = new WeakMap();
	var closingWorkspaceUpdateTimers = new WeakMap();
	var closingFilterTimers = new WeakMap();
	var closingUiSaveTimer = null;

	function rowInputData(row) {
		var data = {};

		row.querySelectorAll('input[data-field], select[data-field]').forEach(function (input) {
			var fieldName = input.getAttribute('data-field');

			if (isMoneyField(fieldName)) {
				data[fieldName] = parseMoneyValue(input.value);
				return;
			}

			data[fieldName] = input.value;
		});

		return data;
	}

	function applyRowInputData(row, data) {
		if (!data) {
			return;
		}

		row.querySelectorAll('input[data-field], select[data-field]').forEach(function (input) {
			var fieldName = input.getAttribute('data-field');
			var stored;

			if (!Object.prototype.hasOwnProperty.call(data, fieldName)) {
				return;
			}

			stored = data[fieldName];

			if (isMoneyField(fieldName)) {
				var numeric = typeof stored === 'number' ? stored : parseMoneyValue(stored);

				input.value = Number.isFinite(numeric) ? String(numeric) : '';
				formatMoneyInput(input);
				return;
			}

			input.value = stored;
		});
	}

	function tapasMainRows(body) {
		return body ? Array.from(body.querySelectorAll('[data-tapas-record]')) : [];
	}

	function tapasRecordCount(body) {
		return tapasMainRows(body).length;
	}

	function ensureTapasRowCount(body, desiredRows, sheet) {
		while (body && tapasRecordCount(body) < desiredRows) {
			addTapasRow(sheet, true);
		}
	}

	function tapasKnownStatuses() {
		return ['pendiente', 'fabricacion', 'formado', 'finalizado', 'cancelado', 'otro'];
	}

	function toggleTapasStatusOther(card) {
		var select = card ? card.querySelector('select[data-field="status"]') : null;
		var otherWrap = card ? card.querySelector('.logistica-tapas-field--status-other') : null;

		if (!select || !otherWrap) {
			return;
		}

		otherWrap.classList.toggle('is-hidden', select.value !== 'otro');
	}

	function updateTapasStatusBadge(card) {
		var select = card ? card.querySelector('select[data-field="status"]') : null;
		var otherInput = card ? card.querySelector('input[data-field="status_other"]') : null;
		var statusValue = 'fabricacion';

		if (!select) {
			return;
		}

		statusValue = select.value === 'otro' && otherInput && otherInput.value.trim()
			? otherInput.value.trim().toLowerCase().replace(/\s+/g, '-')
			: select.value;

		card.classList.remove(
			'is-status-pendiente',
			'is-status-fabricacion',
			'is-status-formado',
			'is-status-finalizado',
			'is-status-cancelado',
			'is-status-otro'
		);
		card.classList.add('is-status-' + statusValue.replace(/[^a-z0-9_-]/g, ''));
	}

	function applyTapasRowData(card, data) {
		if (!card || !data) {
			return;
		}

		if (data.row_id) {
			card.setAttribute('data-row-id', String(data.row_id));
		} else {
			ensureConversionRowId(card);
		}

		applyRowInputData(card, data);

		if (data.status && tapasKnownStatuses().indexOf(data.status) === -1) {
			var select = card.querySelector('select[data-field="status"]');
			var otherInput = card.querySelector('input[data-field="status_other"]');

			if (select) {
				select.value = 'otro';
			}

			if (otherInput) {
				otherInput.value = data.status;
			}
		}

		if (data.precio_fabricacion !== undefined && data.precio_kg === undefined) {
			var precioInput = card.querySelector('[data-field="precio_kg"]');
			if (precioInput) {
				precioInput.value = String(data.precio_fabricacion);
				formatMoneyInput(precioInput);
			}
		}

		toggleTapasStatusOther(card);
		updateTapasStatusBadge(card);
	}

	function tapasPrecioKg(row) {
		var precioKg = Math.max(0, field(row, 'precio_kg'));
		if (!precioKg) {
			precioKg = Math.max(0, field(row, 'precio_fabricacion'));
		}
		return precioKg;
	}

	function resetTapasCard(card) {
		if (!card) {
			return;
		}

		card.removeAttribute('data-row-id');
		ensureConversionRowId(card);

		card.querySelectorAll('input[data-field]').forEach(function (input) {
			if (input.getAttribute('data-field') === 'status_other') {
				input.value = '';
				return;
			}

			if (input.type === 'number') {
				if (input.getAttribute('data-field') === 'calibre') {
					input.value = '20';
				} else if (input.getAttribute('data-field') === 'und_molde') {
					input.value = '12';
				} else if (input.getAttribute('data-field') === 'resina_pct') {
					input.value = '40';
				} else {
					input.value = '0';
				}
				return;
			}

			input.value = '';
		});

		card.querySelectorAll('select[data-field]').forEach(function (select) {
			select.value = 'fabricacion';
		});

		card.querySelectorAll('[data-output]').forEach(function (outputNode) {
			outputNode.textContent = '';
		});

		toggleTapasStatusOther(card);
		updateTapasStatusBadge(card);
	}

	function initTapasCards(sheet) {
		var body = sheet ? sheet.querySelector('[data-tapas-rows]') : null;

		if (!body) {
			return;
		}

		tapasMainRows(body).forEach(function (card) {
			toggleTapasStatusOther(card);
			updateTapasStatusBadge(card);
		});
	}

	function tapasStatusSlug(card) {
		var select = card.querySelector('select[data-field="status"]');

		if (!select || !select.value) {
			return 'fabricacion';
		}

		if (select.value === 'otro') {
			return 'otro';
		}

		return select.value;
	}

	function tapasStatusLabel(card) {
		var select = card.querySelector('select[data-field="status"]');
		var other = card.querySelector('input[data-field="status_other"]');

		if (!select) {
			return '';
		}

		if (select.value === 'otro' && other && other.value.trim()) {
			return other.value.trim();
		}

		return select.options[select.selectedIndex] ? select.options[select.selectedIndex].textContent : select.value;
	}

	function tapasFilterState(sheet) {
		var wrap = sheet ? sheet.querySelector('[data-tapas-filters]') : null;
		var status = '';
		var product = '';
		var color = '';

		if (!wrap) {
			return { status: status, product: product, color: color, active: false };
		}

		status = (wrap.querySelector('[data-tapas-filter="status"]') || {}).value || '';
		product = (wrap.querySelector('[data-tapas-filter="product"]') || {}).value || '';
		color = (wrap.querySelector('[data-tapas-filter="color"]') || {}).value || '';

		return {
			status: status,
			product: product,
			color: color,
			active: !!(status || product || color)
		};
	}

	function tapasCardMatchesFilter(card, filters) {
		var productText;
		var colorText;

		if (!filters || !filters.active) {
			return true;
		}

		if (filters.status && tapasStatusSlug(card) !== filters.status) {
			return false;
		}

		if (filters.product) {
			productText = tapasFieldText(card, 'product').trim().toLowerCase();
			if (productText !== String(filters.product).trim().toLowerCase()) {
				return false;
			}
		}

		if (filters.color) {
			colorText = tapasFieldText(card, 'color').trim().toLowerCase();
			if (colorText !== String(filters.color).trim().toLowerCase()) {
				return false;
			}
		}

		return true;
	}

	function refreshTapasFilterOptions(sheet) {
		var wrap = sheet ? sheet.querySelector('[data-tapas-filters]') : null;
		var productSelect;
		var colorSelect;
		var products = [];
		var colors = [];
		var currentProduct;
		var currentColor;
		var productMap = {};
		var colorMap = {};

		if (!wrap) {
			return;
		}

		productSelect = wrap.querySelector('[data-tapas-filter="product"]');
		colorSelect = wrap.querySelector('[data-tapas-filter="color"]');

		if (!productSelect || !colorSelect) {
			return;
		}

		currentProduct = productSelect.value;
		currentColor = colorSelect.value;

		tapasMainRows(sheet.querySelector('[data-tapas-rows]')).forEach(function (card) {
			var product = tapasFieldText(card, 'product').trim();
			var color = tapasFieldText(card, 'color').trim();

			if (product && !productMap[product]) {
				productMap[product] = true;
				products.push(product);
			}

			if (color && !colorMap[color]) {
				colorMap[color] = true;
				colors.push(color);
			}
		});

		products.sort(function (a, b) {
			return a.localeCompare(b, 'es', { sensitivity: 'base' });
		});
		colors.sort(function (a, b) {
			return a.localeCompare(b, 'es', { sensitivity: 'base' });
		});

		productSelect.innerHTML = '<option value="">Todos</option>';
		products.forEach(function (value) {
			var option = document.createElement('option');
			option.value = value;
			option.textContent = value;
			productSelect.appendChild(option);
		});

		colorSelect.innerHTML = '<option value="">Todos</option>';
		colors.forEach(function (value) {
			var option = document.createElement('option');
			option.value = value;
			option.textContent = value;
			colorSelect.appendChild(option);
		});

		if (currentProduct && productMap[currentProduct]) {
			productSelect.value = currentProduct;
		}

		if (currentColor && colorMap[currentColor]) {
			colorSelect.value = currentColor;
		}
	}

	function applyTapasFilters(sheet, options) {
		var filters = tapasFilterState(sheet);
		var cards = tapasMainRows(sheet.querySelector('[data-tapas-rows]'));
		var tbody = sheet.querySelector('[data-tapas-table-body]');
		var wrap = sheet.querySelector('[data-tapas-filters]');
		var meta = sheet.querySelector('[data-tapas-filter-meta]');
		var badge = sheet.querySelector('[data-tapas-filter-badge]');
		var clearButton = wrap ? wrap.querySelector('[data-action="clear-tapas-filters"]') : null;
		var visible = 0;
		var row;
		var rowList;

		options = options || {};

		cards.forEach(function (card, index) {
			var match = tapasCardMatchesFilter(card, filters);

			card.classList.toggle('is-tapas-filter-hidden', !match);

			if (tbody) {
				row = tbody.querySelector('[data-tapas-table-row="' + String(index) + '"]');
				if (row) {
					row.dataset.logisticaFilterVisible = match ? '1' : '0';
					row.classList.toggle('is-tapas-filter-hidden', !match);
				}
			}

			if (match) {
				visible += 1;
			}
		});

		rowList = tbody ? Array.prototype.slice.call(tbody.querySelectorAll('[data-tapas-table-row]')) : [];
		applyTablePagination(sheet, 'tapas', rowList, options);

		if (meta) {
			if (filters.active) {
				meta.textContent = visible + ' de ' + cards.length + ' referencias visibles';
				meta.hidden = false;
			} else {
				meta.textContent = '';
				meta.hidden = true;
			}
		}

		if (wrap) {
			wrap.classList.toggle('is-active', filters.active);
		}

		if (badge) {
			badge.hidden = !filters.active;
		}

		if (clearButton) {
			clearButton.disabled = !filters.active;
		}

		sheet.classList.toggle('is-tapas-filtered', filters.active);
	}

	function clearTapasFilters(sheet) {
		var wrap = sheet ? sheet.querySelector('[data-tapas-filters]') : null;

		if (!wrap) {
			return;
		}

		wrap.querySelectorAll('[data-tapas-filter]').forEach(function (select) {
			select.value = '';
		});

		applyTapasFilters(sheet, { resetPage: true });
	}

	function tapasFieldText(card, name) {
		var input = card.querySelector('[data-field="' + name + '"]');

		return input ? input.value : '';
	}

	function tapasFieldNumberText(card, name, decimals, integer) {
		var input = card ? card.querySelector('[data-field="' + name + '"]') : null;
		var value;

		if (!input) {
			return '';
		}

		value = numberValue(input);

		if (isMoneyField(name)) {
			var moneyCfg = moneyFieldConfig(name, decimals, integer);
			return Number.isFinite(value) ? displayMoney(value, moneyCfg.decimals, moneyCfg.integer) : '';
		}

		return Number.isFinite(value) ? displayNumber(value, decimals, integer) : '';
	}

	function tapasOutputText(card, name) {
		var outputNode = card.querySelector('[data-output="' + name + '"]');
		var cfg;

		if (!outputNode) {
			return '';
		}

		if (isMoneyField(name)) {
			cfg = moneyFieldConfig(name);
			return displayMoney(parseMoneyValue(outputNode.textContent), cfg.decimals, cfg.integer);
		}

		return outputNode.textContent;
	}

	function tapasExcelTableCell(text, className) {
		var cell = document.createElement('td');

		if (className) {
			cell.className = className;
		}

		cell.textContent = text;
		return cell;
	}

	function calcExcelValidationCell() {
		var cell = document.createElement('td');

		cell.className = 'logistica-tapas-excel__validacion';
		cell.setAttribute('data-output', 'validacion');

		return cell;
	}

	function tapasRowMissingDetails(card) {
		var items = [];

		if (!normalizeText(tapasFieldText(card, 'product'))) {
			items.push('Producto');
		}

		if (!normalizeText(tapasFieldText(card, 'color'))) {
			items.push('Color');
		}

		if (isNumericFieldUnset(card, 'pedido', 0)) {
			items.push('Pedido (und)');
		}

		if (isNumericFieldUnset(card, 'stock', 0)) {
			items.push('Stock (und)');
		}

		if (isResinaPctMissing(card)) {
			items.push('% Resina');
		}

		if (isNumericFieldMissing(card, 'calibre')) {
			items.push('Calibre (milésimas)');
		}

		if (isNumericFieldMissing(card, 'ancho')) {
			items.push('Ancho (cm)');
		}

		if (isNumericFieldMissing(card, 'largo')) {
			items.push('Largo (cm)');
		}

		if (isNumericFieldMissing(card, 'und_molde')) {
			items.push('Und x molde (und)');
		}

		if (isNumericFieldMissing(card, 'peso_gr')) {
			items.push('Peso tamaño (gr)');
		}

		if (field(card, 'material_llega') > 0 && tapasPrecioKg(card) <= 0) {
			items.push('Precio fabricación (COP/kg)');
		}

		return items;
	}

	function bolsasRowMissingDetails(card) {
		var items = [];
		var kilos = field(card, 'kilos');
		var pesoPaquete10 = field(card, 'peso_paquete_10');

		if (!normalizeText(bolsasFieldText(card, 'descripcion'))) {
			items.push('Descripción');
		}

		if (isNumericFieldMissing(card, 'kilos')) {
			items.push('Kilos (kg)');
		}

		if (kilos > 0 && isNumericFieldMissing(card, 'peso_paquete_10')) {
			items.push('Peso paquete x10 (gr)');
		}

		if (isNumericFieldMissing(card, 'ancho')) {
			items.push('Ancho');
		}

		if (isNumericFieldMissing(card, 'largo')) {
			items.push('Largo');
		}

		if (isNumericFieldMissing(card, 'calibre')) {
			items.push('Calibre');
		}

		if (!normalizeText(bolsasFieldText(card, 'codigo_graficos'))) {
			items.push('Código GRAFICOSTOS');
		}

		return items;
	}

	function applyCalcRowValidation(row, card, missingDetailsFn) {
		var missing = missingDetailsFn(card);

		if (missing.length) {
			setClosingState(row, 'is-missing', 'Faltan datos', missing, 'validacion');
			return;
		}

		setClosingState(row, 'is-formula', 'OK', [], 'validacion');
	}

	function tapasValidationExportLabel(card) {
		var missing = tapasRowMissingDetails(card);

		return missing.length ? 'Faltan datos' : 'OK';
	}

	function bolsasValidationExportLabel(card) {
		var missing = bolsasRowMissingDetails(card);

		return missing.length ? 'Faltan datos' : 'OK';
	}

	function tapasExcelTableCopyField(card, fieldName, className) {
		var cell = document.createElement('td');
		var input = document.createElement('input');
		var source = card ? card.querySelector('[data-field="' + fieldName + '"]') : null;
		var value = source ? source.value : '';

		cell.className = 'is-copy-cell is-editable is-text ' + (className || '');

		input.type = 'text';
		input.className = 'logistica-tapas-excel-copy-input';
		input.value = value;
		input.setAttribute('data-field', fieldName);
		input.setAttribute('data-excel-field', fieldName);
		input.setAttribute('autocomplete', 'off');
		input.setAttribute('spellcheck', 'false');
		input.setAttribute('aria-label', fieldName);
		input.setAttribute('title', 'Clic para copiar');

		cell.appendChild(input);

		return cell;
	}

	function copyTextToClipboard(value, feedbackElement, options) {
		var copiedTitle = 'Copiado';
		var defaultTitle = 'Clic para copiar';

		options = options || {};

		if (!value) {
			return;
		}

		function showCopied() {
			if (!feedbackElement) {
				return;
			}

			feedbackElement.classList.add('is-copied');

			if (feedbackElement.hasAttribute('title')) {
				feedbackElement.setAttribute('title', copiedTitle);
			}

			window.setTimeout(function () {
				feedbackElement.classList.remove('is-copied');

				if (feedbackElement.hasAttribute('title')) {
					feedbackElement.setAttribute('title', options.defaultTitle || defaultTitle);
				}
			}, options.duration || 1200);
		}

		if (navigator.clipboard && navigator.clipboard.writeText) {
			navigator.clipboard.writeText(value).then(showCopied).catch(function () {
				if (options.fallbackInput) {
					copyExcelFieldFallback(options.fallbackInput, value);
				}

				showCopied();
			});
			return;
		}

		if (options.fallbackInput) {
			copyExcelFieldFallback(options.fallbackInput, value);
		}

		showCopied();
	}

	function copyExcelInputValue(input) {
		var value = input ? String(input.value || '').trim() : '';

		copyTextToClipboard(value, input, { fallbackInput: input });
	}

	function copyClosingCodLoteValue(element) {
		var value = element ? String(element.textContent || '').trim() : '';

		copyTextToClipboard(value, element);
	}

	function upgradeClosingCodLoteCells(scope) {
		var root = scope || document;
		var copyFields = ['cod', 'lote', 'ajustado'];

		root.querySelectorAll('.logistica-close-matrix [data-close-line]').forEach(function (row) {
			copyFields.forEach(function (name) {
				var target = row.querySelector('[data-output="' + name + '"]');

				var span;

				if (!target || target.classList.contains('logistica-close-copy-value')) {
					return;
				}

				if (target.tagName !== 'TD') {
					return;
				}

				span = document.createElement('span');
				span.className = 'logistica-close-copy-value';
				span.setAttribute('data-output', name);
				span.setAttribute('tabindex', '0');
				span.setAttribute('role', 'button');
				span.setAttribute('title', 'Clic para copiar');
				span.textContent = target.textContent;
				target.classList.add('is-close-copy');
				target.removeAttribute('data-output');
				target.textContent = '';
				target.appendChild(span);
			});
		});
	}

	function copyExcelFieldFallback(input, value) {
		if (!input) {
			return;
		}

		input.focus();
		input.select();

		try {
			document.execCommand('copy');
		} catch (error) {
			window.alert('No se pudo copiar. Selecciona el texto y usa Ctrl+C.');
		}
	}

	function syncTapasExcelTable(sheet) {
		var tbody = sheet.querySelector('[data-tapas-table-body]');
		var cards = tapasMainRows(sheet.querySelector('[data-tapas-rows]'));
		var row;
		var index;

		if (!tbody) {
			return;
		}

		tbody.innerHTML = '';

		cards.forEach(function (card, index) {
			row = document.createElement('tr');
			row.setAttribute('data-tapas-table-row', String(index));
			row.dataset.logisticaFilterVisible = '1';
			row.className = 'logistica-tapas-excel-row is-row-status-' + tapasStatusSlug(card);
			row.appendChild(calcExcelValidationCell());
			applyCalcRowValidation(row, card, tapasRowMissingDetails);
			row.appendChild(tapasExcelTableCell(tapasStatusLabel(card), 'logistica-tapas-excel__status is-text'));
			row.appendChild(tapasExcelTableCell(tapasFieldText(card, 'product'), 'is-text'));
			row.appendChild(tapasExcelTableCell(tapasFieldText(card, 'color'), 'is-text'));
			row.appendChild(tapasExcelTableCell(tapasFieldNumberText(card, 'pedido', 0, true), 'is-num'));
			row.appendChild(tapasExcelTableCell(tapasFieldNumberText(card, 'stock', 0, true), 'is-num'));
			row.appendChild(tapasExcelTableCell(tapasOutputText(card, 'fabricar'), 'is-auto is-num'));
			row.appendChild(tapasExcelTableCell(tapasFieldText(card, 'calibre'), 'is-num'));
			row.appendChild(tapasExcelTableCell(tapasFieldText(card, 'ancho'), 'is-num'));
			row.appendChild(tapasExcelTableCell(tapasFieldText(card, 'largo'), 'is-num'));
			row.appendChild(tapasExcelTableCell(tapasFieldText(card, 'und_molde'), 'is-num'));
			row.appendChild(tapasExcelTableCell(tapasFieldText(card, 'peso_gr'), 'is-num'));
			row.appendChild(tapasExcelTableCell(tapasFieldText(card, 'resina_pct'), 'is-num'));
			row.appendChild(tapasExcelTableCell(tapasOutputText(card, 'resina_kg'), 'is-auto is-num is-col-base-kg is-col-base-kg--resina'));
			row.appendChild(tapasExcelTableCell(tapasOutputText(card, 'retal_kg'), 'is-auto is-num is-col-base-kg is-col-base-kg--retal'));
			row.appendChild(tapasExcelTableCell(tapasOutputText(card, 'total_kg'), 'is-auto is-num is-col-base-kg is-col-base-kg--total'));
			row.appendChild(tapasExcelTableCell(tapasOutputText(card, 'resina_merma'), 'is-auto is-num is-col-envio is-col-envio-resina'));
			row.appendChild(tapasExcelTableCell(tapasOutputText(card, 'retal_merma'), 'is-auto is-num is-col-envio is-col-envio-retal'));
			row.appendChild(tapasExcelTableCell(tapasOutputText(card, 'total_merma'), 'is-auto is-num is-col-envio is-col-envio-total'));
			row.appendChild(tapasExcelTableCell(tapasOutputText(card, 'resina_multiplo'), 'is-auto is-num'));
			row.appendChild(tapasExcelTableCell(tapasOutputText(card, 'rollos'), 'is-auto is-num'));
			row.appendChild(tapasExcelTableCell(tapasFieldNumberText(card, 'retal_sent', 2, false), 'is-num'));
			row.appendChild(tapasExcelTableCell(tapasFieldNumberText(card, 'resina_sent', 2, false), 'is-num'));
			row.appendChild(tapasExcelTableCell(tapasFieldNumberText(card, 'material_llega', 2, false), 'is-num'));
			row.appendChild(tapasExcelTableCell(tapasOutputText(card, 'retal_due'), 'is-auto is-num'));
			row.appendChild(tapasExcelTableCell(tapasOutputText(card, 'resina_due'), 'is-auto is-num'));
			row.appendChild(tapasExcelTableCell(tapasOutputText(card, 'total_due'), 'is-auto is-num'));
			row.appendChild(tapasExcelTableCell(tapasFieldNumberText(card, 'precio_kg', 2, false), 'is-num'));
			row.appendChild(tapasExcelTableCell(tapasOutputText(card, 'service_total'), 'is-auto is-num'));
			row.appendChild(calcExcelActionsCell({
				editAction: 'edit-tapas-row',
				duplicateAction: 'duplicate-tapas-row',
				deleteAction: 'remove-tapas-row',
				indexAttr: 'data-tapas-row-index',
				index: index
			}));
			tbody.appendChild(row);
		});

		initTapasExcelScroll(sheet);
	}

	function tapasExportFilename() {
		return logisticaExportTimestampFilename('calculo-tapas');
	}

	function escapeHtml(value) {
		return String(value || '')
			.replace(/&/g, '&amp;')
			.replace(/</g, '&lt;')
			.replace(/>/g, '&gt;')
			.replace(/"/g, '&quot;')
			.replace(/'/g, '&#39;');
	}

	var logisticaCrc32TableCache = null;

	function logisticaCrc32Table() {
		var index;
		var table;
		var byte;

		if (logisticaCrc32TableCache) {
			return logisticaCrc32TableCache;
		}

		table = new Uint32Array(256);
		for (index = 0; index < 256; index++) {
			byte = index;
			for (var bit = 0; bit < 8; bit++) {
				byte = (byte & 1) ? (0xedb88320 ^ (byte >>> 1)) : (byte >>> 1);
			}
			table[index] = byte >>> 0;
		}

		logisticaCrc32TableCache = table;
		return table;
	}

	function logisticaCrc32(bytes) {
		var crc = 0 ^ (-1);
		var table = logisticaCrc32Table();
		var index;

		for (index = 0; index < bytes.length; index++) {
			crc = (crc >>> 8) ^ table[(crc ^ bytes[index]) & 0xff];
		}

		return (crc ^ (-1)) >>> 0;
	}

	function logisticaUtf8Bytes(value) {
		return new TextEncoder().encode(String(value == null ? '' : value));
	}

	function logisticaXlsxEscape(value) {
		return String(value == null ? '' : value)
			.replace(/&/g, '&amp;')
			.replace(/</g, '&lt;')
			.replace(/>/g, '&gt;')
			.replace(/"/g, '&quot;')
			.replace(/'/g, '&apos;')
			.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F]/g, '');
	}

	function logisticaXlsxColumnLetter(index) {
		var letter = '';

		index = Math.max(0, parseInt(index, 10) || 0);
		do {
			letter = String.fromCharCode(65 + (index % 26)) + letter;
			index = Math.floor(index / 26) - 1;
		} while (index >= 0);

		return letter;
	}

	function logisticaXlsxColumnIndex(letters) {
		var index = 0;
		var charIndex;

		letters = String(letters || '').toUpperCase();
		for (charIndex = 0; charIndex < letters.length; charIndex++) {
			index = index * 26 + (letters.charCodeAt(charIndex) - 64);
		}

		return Math.max(0, index - 1);
	}

	function logisticaBuildXlsxRowXml(rowNumber, cells) {
		var xml = '<row r="' + rowNumber + '">';

		cells.forEach(function (value, index) {
			var ref = logisticaXlsxColumnLetter(index) + rowNumber;
			xml += '<c r="' + ref + '" t="inlineStr"><is><t>' + logisticaXlsxEscape(value) + '</t></is></c>';
		});
		return xml + '</row>';
	}

	function logisticaBuildXlsxSheetXml(matrixRows) {
		var sheetRows = matrixRows.map(function (row, index) {
			return logisticaBuildXlsxRowXml(index + 1, row);
		});

		return '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'
			+ '<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">'
			+ '<sheetData>' + sheetRows.join('') + '</sheetData>'
			+ '</worksheet>';
	}

	function logisticaBuildZipStoreBlob(entries) {
		var localChunks = [];
		var centralChunks = [];
		var offset = 0;

		entries.forEach(function (entry) {
			var nameBytes = logisticaUtf8Bytes(entry.name);
			var dataBytes = entry.bytes;
			var crc = logisticaCrc32(dataBytes);
			var size = dataBytes.length;
			var local = new Uint8Array(30 + nameBytes.length + size);
			var view = new DataView(local.buffer);
			var central = new Uint8Array(46 + nameBytes.length);
			var cview = new DataView(central.buffer);

			view.setUint32(0, 0x04034b50, true);
			view.setUint16(4, 20, true);
			view.setUint16(6, 0, true);
			view.setUint16(8, 0, true);
			view.setUint16(10, 0, true);
			view.setUint16(12, 0, true);
			view.setUint32(14, crc, true);
			view.setUint32(18, size, true);
			view.setUint32(22, size, true);
			view.setUint16(26, nameBytes.length, true);
			view.setUint16(28, 0, true);
			local.set(nameBytes, 30);
			local.set(dataBytes, 30 + nameBytes.length);
			localChunks.push(local);

			cview.setUint32(0, 0x02014b50, true);
			cview.setUint16(4, 20, true);
			cview.setUint16(6, 20, true);
			cview.setUint16(8, 0, true);
			cview.setUint16(10, 0, true);
			cview.setUint16(12, 0, true);
			cview.setUint16(14, 0, true);
			cview.setUint32(16, crc, true);
			cview.setUint32(20, size, true);
			cview.setUint32(24, size, true);
			cview.setUint16(28, nameBytes.length, true);
			cview.setUint16(30, 0, true);
			cview.setUint16(32, 0, true);
			cview.setUint16(34, 0, true);
			cview.setUint16(36, 0, true);
			cview.setUint32(38, 0, true);
			cview.setUint32(42, offset, true);
			central.set(nameBytes, 46);
			centralChunks.push(central);

			offset += local.length;
		});

		var centralSize = centralChunks.reduce(function (sum, chunk) {
			return sum + chunk.length;
		}, 0);
		var end = new Uint8Array(22);
		var eview = new DataView(end.buffer);

		eview.setUint32(0, 0x06054b50, true);
		eview.setUint16(8, entries.length, true);
		eview.setUint16(10, entries.length, true);
		eview.setUint32(12, centralSize, true);
		eview.setUint32(16, offset, true);

		return new Blob(localChunks.concat(centralChunks).concat([end]), {
			type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
		});
	}

	function logisticaBuildXlsxBlob(sheetName, matrixRows) {
		var safeName = String(sheetName || 'Hoja1').replace(/[<>:"/\\|?*]/g, '').slice(0, 31) || 'Hoja1';
		var sheetXml = logisticaBuildXlsxSheetXml(matrixRows);

		return logisticaBuildZipStoreBlob([
			{
				name: '[Content_Types].xml',
				bytes: logisticaUtf8Bytes(
					'<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'
					+ '<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">'
					+ '<Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>'
					+ '<Override PartName="/xl/workbook.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml"/>'
					+ '<Override PartName="/xl/worksheets/sheet1.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"/>'
					+ '</Types>'
				)
			},
			{
				name: '_rels/.rels',
				bytes: logisticaUtf8Bytes(
					'<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'
					+ '<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">'
					+ '<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="xl/workbook.xml"/>'
					+ '</Relationships>'
				)
			},
			{
				name: 'xl/workbook.xml',
				bytes: logisticaUtf8Bytes(
					'<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'
					+ '<workbook xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">'
					+ '<sheets><sheet name="' + logisticaXlsxEscape(safeName) + '" sheetId="1" r:id="rId1"/></sheets>'
					+ '</workbook>'
				)
			},
			{
				name: 'xl/_rels/workbook.xml.rels',
				bytes: logisticaUtf8Bytes(
					'<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'
					+ '<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">'
					+ '<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet" Target="worksheets/sheet1.xml"/>'
					+ '</Relationships>'
				)
			},
			{
				name: 'xl/worksheets/sheet1.xml',
				bytes: logisticaUtf8Bytes(sheetXml)
			}
		]);
	}

	function logisticaDownloadBlob(blob, filename) {
		var url = window.URL.createObjectURL(blob);
		var link = document.createElement('a');

		link.href = url;
		link.download = filename;
		document.body.appendChild(link);
		link.click();
		document.body.removeChild(link);
		window.setTimeout(function () {
			window.URL.revokeObjectURL(url);
		}, 0);
	}

	function logisticaExportMatrixCsv(matrixRows, filename) {
		var lines = matrixRows.map(function (row) {
			return row.map(function (cell) {
				var text = String(cell == null ? '' : cell);

				if (/[;"\n\r]/.test(text)) {
					return '"' + text.replace(/"/g, '""') + '"';
				}

				return text;
			}).join(';');
		});

		logisticaDownloadBlob(
			new Blob(['\ufeff' + lines.join('\r\n')], { type: 'text/csv;charset=utf-8;' }),
			filename.replace(/\.xlsx$/i, '.csv')
		);
	}

	function logisticaExportMatrixXlsx(matrixRows, filename, sheetName) {
		try {
			logisticaDownloadBlob(logisticaBuildXlsxBlob(sheetName, matrixRows), filename);
		} catch (error) {
			logisticaExportMatrixCsv(matrixRows, filename);
		}
	}

	function logisticaColombiaTimeHms() {
		var parts = {};
		new Intl.DateTimeFormat('en-GB', {
			timeZone: 'America/Bogota',
			hour: '2-digit',
			minute: '2-digit',
			second: '2-digit',
			hour12: false
		}).formatToParts(new Date()).forEach(function (part) {
			if (part.type !== 'literal') {
				parts[part.type] = part.value;
			}
		});

		return parts.hour + ':' + parts.minute + ':' + parts.second;
	}

	function logisticaColombiaYmdHmsParts() {
		var parts = {
			year: '',
			month: '',
			day: '',
			hour: '',
			minute: '',
			second: ''
		};

		new Intl.DateTimeFormat('en-GB', {
			timeZone: 'America/Bogota',
			year: 'numeric',
			month: '2-digit',
			day: '2-digit',
			hour: '2-digit',
			minute: '2-digit',
			second: '2-digit',
			hour12: false
		}).formatToParts(new Date()).forEach(function (part) {
			if (part.type !== 'literal') {
				parts[part.type] = part.value;
			}
		});

		return parts;
	}

	function logisticaExportTimestampFilename(prefix) {
		var colombia = logisticaColombiaYmdHmsParts();

		return prefix + '-' +
			colombia.year +
			colombia.month +
			colombia.day + '-' +
			colombia.hour +
			colombia.minute +
			colombia.second + '.xlsx';
	}

	function logisticaReadUint16(bytes, offset) {
		return bytes[offset] | (bytes[offset + 1] << 8);
	}

	function logisticaReadUint32(bytes, offset) {
		return (bytes[offset] | (bytes[offset + 1] << 8) | (bytes[offset + 2] << 16) | (bytes[offset + 3] << 24)) >>> 0;
	}

	function logisticaExtractZipEntryStore(buffer, entryName) {
		var bytes = new Uint8Array(buffer);
		var offset = 0;
		var name;
		var compMethod;
		var compSize;
		var nameLen;
		var extraLen;
		var dataStart;

		while (offset + 30 < bytes.length) {
			if (logisticaReadUint32(bytes, offset) !== 0x04034b50) {
				break;
			}

			compMethod = logisticaReadUint16(bytes, offset + 8);
			compSize = logisticaReadUint32(bytes, offset + 18);
			nameLen = logisticaReadUint16(bytes, offset + 26);
			extraLen = logisticaReadUint16(bytes, offset + 28);
			name = new TextDecoder('utf-8').decode(bytes.subarray(offset + 30, offset + 30 + nameLen));
			dataStart = offset + 30 + nameLen + extraLen;

			if (name === entryName) {
				if (compMethod !== 0) {
					return null;
				}

				return bytes.subarray(dataStart, dataStart + compSize);
			}

			offset = dataStart + compSize;
		}

		return null;
	}

	function logisticaMatrixRowsFromXlsxSheetXml(xmlText) {
		var doc = new DOMParser().parseFromString(xmlText, 'application/xml');
		var rows = [];

		doc.querySelectorAll('sheetData row').forEach(function (rowNode) {
			var cells = [];
			var maxIndex = 0;

			rowNode.querySelectorAll('c').forEach(function (cell) {
				var ref = cell.getAttribute('r') || '';
				var colIndex = logisticaXlsxColumnIndex(ref.replace(/[0-9]/g, ''));
				var value = '';
				var tNode = cell.querySelector('t');
				var vNode = cell.querySelector('v');

				if (tNode) {
					value = tNode.textContent || '';
				} else if (vNode) {
					value = vNode.textContent || '';
				}

				cells[colIndex] = value;
				if (colIndex > maxIndex) {
					maxIndex = colIndex;
				}
			});

			var row = [];
			var index;

			for (index = 0; index <= maxIndex; index++) {
				row.push(cells[index] || '');
			}

			if (row.some(function (value) {
				return String(value).trim() !== '';
			})) {
				rows.push(row);
			}
		});

		return rows;
	}

	function logisticaMatrixRowsFromXlsxBuffer(buffer) {
		var sheetBytes = logisticaExtractZipEntryStore(buffer, 'xl/worksheets/sheet1.xml');

		if (!sheetBytes) {
			return [];
		}

		return logisticaMatrixRowsFromXlsxSheetXml(new TextDecoder('utf-8').decode(sheetBytes));
	}

	function tapasExportRows(sheet) {
		var filters = tapasFilterState(sheet);

		return tapasMainRows(sheet.querySelector('[data-tapas-rows]')).filter(function (card) {
			return tapasCardMatchesFilter(card, filters);
		}).map(function (card) {
			return [
				'',
				tapasValidationExportLabel(card),
				tapasStatusLabel(card),
				tapasFieldText(card, 'product'),
				tapasFieldText(card, 'color'),
				tapasFieldText(card, 'pedido'),
				tapasFieldText(card, 'stock'),
				tapasOutputText(card, 'fabricar'),
				tapasFieldText(card, 'calibre'),
				tapasFieldText(card, 'ancho'),
				tapasFieldText(card, 'largo'),
				tapasFieldText(card, 'und_molde'),
				tapasFieldText(card, 'peso_gr'),
				tapasFieldText(card, 'resina_pct'),
				tapasOutputText(card, 'resina_kg'),
				tapasOutputText(card, 'retal_kg'),
				tapasOutputText(card, 'total_kg'),
				tapasOutputText(card, 'resina_merma'),
				tapasOutputText(card, 'retal_merma'),
				tapasOutputText(card, 'total_merma'),
				tapasOutputText(card, 'resina_multiplo'),
				tapasOutputText(card, 'rollos'),
				tapasFieldText(card, 'retal_sent'),
				tapasFieldText(card, 'resina_sent'),
				tapasFieldText(card, 'material_llega'),
				tapasOutputText(card, 'retal_due'),
				tapasOutputText(card, 'resina_due'),
				tapasOutputText(card, 'total_due'),
				tapasFieldText(card, 'precio_kg'),
				tapasOutputText(card, 'service_total')
			];
		});
	}

	function exportTapasExcel(button) {
		var sheet = button.closest('.logistica-sheet[data-sheet="tapas-poliestireno"]');
		var matrixRows;
		var labels = [
			'Acción',
			'Validación',
			'Estado',
			'Producto',
			'Color',
			'Pedido (und)',
			'Stock (und)',
			'Fabricar (und)',
			'Calibre (milésimas)',
			'Ancho (cm)',
			'Largo (cm)',
			'Und x molde (und)',
			'Peso tamaño (gr)',
			'% Resina',
			'Resina (kg)',
			'Retal (kg)',
			'Total (kg)',
			'Resina + merma',
			'Retal + merma',
			'Total c/merma',
			'Mult. 25 resina',
			'Rollos',
			'Retal enviado (kg)',
			'Resina enviada (kg)',
			'Material que llega (kg)',
			'Retal debiendo (kg)',
			'Resina debiendo (kg)',
			'Total debiendo (kg)',
			'Precio fabricación (COP/kg)',
			'Costo servicio (COP)'
		];
		var settings;

		if (!sheet) {
			return;
		}

		updateTapasSheet(sheet);
		settings = sheetSettings(sheet);
		matrixRows = [
			['Configuración', 'Valor'],
			['Peso rollo resina (kg)', String(settings.roll_kg)],
			['Merma resina/retal (%)', String(settings.waste_pct)],
			['Múltiplo redondeo resina (kg)', String(settings.resin_step)],
			[''],
			labels
		].concat(tapasExportRows(sheet));

		logisticaExportMatrixXlsx(matrixRows, tapasExportFilename(), 'Cálculo tapas');
	}

	function parseTapasImportNumber(value, fallback) {
		var raw = String(value || '').trim();
		var normalized = raw;
		var parsed = parseFloat(normalized);

		if (raw.indexOf('$') >= 0 || (raw.match(/\./g) || []).length > 1) {
			parsed = parseMoneyValue(raw);
			return Number.isFinite(parsed) ? parsed : fallback;
		}

		if (raw.indexOf(',') >= 0 && raw.indexOf('.') >= 0) {
			normalized = raw.replace(/\./g, '').replace(',', '.');
		} else if (raw.indexOf(',') >= 0) {
			normalized = raw.replace(',', '.');
		}

		normalized = normalized.replace(/[^\d.-]/g, '');
		parsed = parseFloat(normalized);
		return Number.isFinite(parsed) ? parsed : fallback;
	}

	function normalizeTapasImportedStatus(value) {
		var raw = String(value || '').trim();
		var slug = raw.toLowerCase()
			.normalize('NFD')
			.replace(/[\u0300-\u036f]/g, '')
			.replace(/[^a-z0-9]+/g, '-')
			.replace(/(^-|-$)/g, '');
		var map = {
			pendiente: 'pendiente',
			fabricacion: 'fabricacion',
			formado: 'formado',
			finalizado: 'finalizado',
			cancelado: 'cancelado',
			otro: 'otro'
		};

		if (map[slug]) {
			return { status: map[slug], status_other: '' };
		}

		if (!raw) {
			return { status: 'fabricacion', status_other: '' };
		}

		return { status: 'otro', status_other: raw };
	}

	function applyTapasImportData(sheet, imported) {
		var body = sheet.querySelector('[data-tapas-rows]');
		var cards;

		if (!body || !imported || !Array.isArray(imported.rows) || !imported.rows.length) {
			window.alert('No se encontraron filas válidas para importar.');
			return;
		}

		while (tapasRecordCount(body) < imported.rows.length) {
			addTapasRow(sheet, true);
		}

		while (tapasRecordCount(body) > imported.rows.length && tapasRecordCount(body) > 1) {
			body.lastElementChild.remove();
		}

		if (imported.settings && typeof imported.settings === 'object') {
			Object.keys(imported.settings).forEach(function (key) {
				var input = sheet.querySelector('[data-sheet-setting="' + key + '"]');
				if (input && Number.isFinite(imported.settings[key])) {
					input.value = String(imported.settings[key]);
				}
			});
		}

		cards = tapasMainRows(body);
		cards.forEach(function (card, index) {
			var row = imported.rows[index];
			if (!row) {
				return;
			}
			applyTapasRowData(card, row);
		});

		updateTapasSheet(sheet);
		saveConversionState();
		closeTapasCardsPanel(sheet);
	}

	function parseTapasImportRowColumns(cols) {
		var offset;
		var statusInfo;
		var statusText;

		if (cols.length < 12) {
			return null;
		}

		offset = cols.length >= 21 ? 0 : -1;
		statusText = cols[1 + offset] || '';
		if (/^totales?/i.test(statusText)) {
			return null;
		}

		statusInfo = normalizeTapasImportedStatus(statusText);
		return {
			status: statusInfo.status,
			status_other: statusInfo.status_other,
			product: cols[2 + offset] || '',
			color: cols[3 + offset] || '',
			pedido: parseTapasImportNumber(cols[4 + offset], 0),
			stock: parseTapasImportNumber(cols[5 + offset], 0),
			calibre: parseTapasImportNumber(cols[7 + offset], 20),
			ancho: parseTapasImportNumber(cols[8 + offset], 0),
			largo: parseTapasImportNumber(cols[9 + offset], 0),
			und_molde: parseTapasImportNumber(cols[10 + offset], 12),
			peso_gr: parseTapasImportNumber(cols[11 + offset], 0),
			resina_pct: parseTapasImportNumber(cols[12 + offset], 40)
		};
	}

	function parseTapasImportFromMatrix(matrixRows) {
		var settings = {};
		var rows = [];
		var dataStarted = false;

		matrixRows.forEach(function (row) {
			var cols = row.map(function (value) {
				return String(value == null ? '' : value).trim();
			});
			var label;
			var value;
			var parsedRow;

			if (!cols.length || cols.every(function (value) {
				return value === '';
			})) {
				return;
			}

			if (/^configuracion$/i.test(cols[0]) && /^valor$/i.test(cols[1])) {
				return;
			}

			if (!dataStarted && cols[0] && /rollo|merma|multiplo|redondeo/i.test(cols[0].toLowerCase())) {
				label = cols[0].toLowerCase();
				value = parseTapasImportNumber(cols[1], Number.NaN);
				if (/rollo/.test(label)) {
					settings.roll_kg = value;
				}
				if (/merma/.test(label)) {
					settings.waste_pct = value;
				}
				if (/multiplo|redondeo/.test(label)) {
					settings.resin_step = value;
				}
				return;
			}

			if (/^editar$/i.test(cols[0]) || /producto/i.test(cols.join(' '))) {
				dataStarted = true;
				return;
			}

			if (!dataStarted) {
				return;
			}

			parsedRow = parseTapasImportRowColumns(cols);
			if (parsedRow) {
				rows.push(parsedRow);
			}
		});

		return { rows: rows, settings: settings };
	}

	function parseTapasImportFromDocument(doc) {
		var tables = Array.from(doc.querySelectorAll('table'));
		var settings = {};
		var dataTable = null;
		var rows = [];

		tables.forEach(function (table) {
			var headerText = (table.querySelector('tr th') || {}).textContent || '';
			if (!dataTable && /editar|estado|producto/i.test(table.textContent || '')) {
				dataTable = table;
			}
			if (/configuracion/i.test(headerText)) {
				Array.from(table.querySelectorAll('tr')).forEach(function (tr) {
					var cols = tr.querySelectorAll('td');
					var label;
					var value;

					if (cols.length < 2) {
						return;
					}
					label = (cols[0].textContent || '').toLowerCase();
					value = parseTapasImportNumber(cols[1].textContent, Number.NaN);
					if (/rollo/.test(label)) {
						settings.roll_kg = value;
					}
					if (/merma/.test(label)) {
						settings.waste_pct = value;
					}
					if (/multiplo|redondeo/.test(label)) {
						settings.resin_step = value;
					}
				});
			}
		});

		if (!dataTable && tables.length) {
			dataTable = tables[tables.length - 1];
		}

		if (!dataTable) {
			return { rows: [], settings: settings };
		}

		Array.from(dataTable.querySelectorAll('tr')).slice(1).forEach(function (tr) {
			var cols = Array.from(tr.querySelectorAll('td,th')).map(function (cell) {
				return (cell.textContent || '').trim();
			});
			var parsedRow = parseTapasImportRowColumns(cols);

			if (parsedRow) {
				rows.push(parsedRow);
			}
		});

		return { rows: rows, settings: settings };
	}

	function importTapasExcelFromFile(input) {
		var file = input.files && input.files[0];
		var sheet = input.closest('.logistica-sheet[data-sheet="tapas-poliestireno"]');
		var reader;

		if (!file || !sheet) {
			return;
		}

		reader = new FileReader();
		reader.onload = function (event) {
			var buffer = event.target ? event.target.result : null;
			var bytes;
			var text;
			var imported;
			var matrixRows;

			if (!(buffer instanceof ArrayBuffer)) {
				input.value = '';
				return;
			}

			bytes = new Uint8Array(buffer);
			if (bytes[0] === 0x50 && bytes[1] === 0x4b) {
				matrixRows = logisticaMatrixRowsFromXlsxBuffer(buffer);
				if (!matrixRows.length) {
					window.alert('No se pudo leer el archivo .xlsx. Usa el archivo exportado por esta vista o guarda como CSV.');
					input.value = '';
					return;
				}
				imported = parseTapasImportFromMatrix(matrixRows);
			} else {
				text = new TextDecoder('utf-8').decode(bytes);
				if (text.indexOf('<') >= 0) {
					imported = parseTapasImportFromDocument(new DOMParser().parseFromString(text, 'text/html'));
				} else {
					matrixRows = text.split(/\r?\n/).map(function (line) {
						return line.split(';').map(function (cell) {
							return cell.replace(/^"|"$/g, '').replace(/""/g, '"');
						});
					});
					imported = parseTapasImportFromMatrix(matrixRows);
				}
			}

			applyTapasImportData(sheet, imported);
			input.value = '';
		};
		reader.onerror = function () {
			window.alert('No se pudo leer el archivo Excel.');
			input.value = '';
		};
		reader.readAsArrayBuffer(file);
	}

	var EXCEL_ZOOM_MIN = 70;
	var EXCEL_ZOOM_MAX = 140;
	var EXCEL_ZOOM_STEP = 10;
	var EXCEL_ZOOM_DEFAULT = 100;
	var EXCEL_DENSITY_STORAGE_KEY = 'logistica_table_density_v1';

	function excelZoomRoot(element) {
		if (!element) {
			return null;
		}

		return element.closest('[data-excel-zoom-root], .logistica-sheet');
	}

	function excelTableScrollElement(root) {
		if (!root) {
			return null;
		}

		return root.querySelector(
			'[data-tapas-excel-scroll], [data-bolsas-excel-scroll], [data-warehouse-excel-scroll], [data-cierre-car-excel-scroll], [data-etiqueta-catalog-scroll], [data-conversion-excel-scroll], [data-planeacion-scroll], [data-minutas-excel-scroll], [data-pedidos-excel-scroll]'
		);
	}

	function excelZoomStorageKey(root) {
		var key = root.getAttribute('data-excel-zoom-root') || root.getAttribute('data-sheet') || 'excel';
		return 'logistica_' + key + '_zoom_v1';
	}

	function clampExcelZoom(percent) {
		return Math.min(EXCEL_ZOOM_MAX, Math.max(EXCEL_ZOOM_MIN, percent));
	}

	function getExcelTableZoom(sheet) {
		var saved = parseInt(localStorage.getItem(excelZoomStorageKey(sheet)), 10);

		if (!Number.isFinite(saved)) {
			return EXCEL_ZOOM_DEFAULT;
		}

		return clampExcelZoom(saved);
	}

	function isExcelTableCompact() {
		try {
			return localStorage.getItem(EXCEL_DENSITY_STORAGE_KEY) === 'compact';
		} catch (error) {
			return false;
		}
	}

	function setExcelTableCompact(compact) {
		var roots = document.querySelectorAll('[data-excel-zoom-root], .logistica-sheet');
		var buttons = document.querySelectorAll('[data-action="excel-density-toggle"]');

		document.documentElement.classList.toggle('logistica-table-compact', !!compact);
		document.body.classList.toggle('logistica-table-compact', !!compact);

		roots.forEach(function (root) {
			root.classList.toggle('is-table-compact', !!compact);
		});

		buttons.forEach(function (btn) {
			btn.classList.toggle('is-active', !!compact);
			btn.setAttribute('aria-pressed', compact ? 'true' : 'false');
			btn.title = compact ? 'Modo normal: filas estándar' : 'Modo compacto: filas más densas';
		});

		try {
			localStorage.setItem(EXCEL_DENSITY_STORAGE_KEY, compact ? 'compact' : 'normal');
		} catch (error) {
			// Storage may be unavailable in private mode.
		}
	}

	function toggleExcelTableCompact() {
		setExcelTableCompact(!isExcelTableCompact());
	}

	function setExcelTableZoom(sheet, percent) {
		var scroll = excelTableScrollElement(sheet);
		var label = sheet.querySelector('[data-excel-zoom-label]');
		var minusBtn = sheet.querySelector('[data-action="excel-zoom-out"]');
		var plusBtn = sheet.querySelector('[data-action="excel-zoom-in"]');

		if (!scroll) {
			return;
		}

		percent = clampExcelZoom(percent);
		scroll.style.zoom = String(percent / 100);
		scroll.dataset.excelZoom = String(percent);

		if (label) {
			label.textContent = percent + '%';
		}

		if (minusBtn) {
			minusBtn.disabled = percent <= EXCEL_ZOOM_MIN;
		}

		if (plusBtn) {
			plusBtn.disabled = percent >= EXCEL_ZOOM_MAX;
		}

		try {
			localStorage.setItem(excelZoomStorageKey(sheet), String(percent));
		} catch (error) {
			// Storage may be unavailable in private mode.
		}

		window.dispatchEvent(new Event('resize'));
	}

	function stepExcelTableZoom(sheet, delta) {
		setExcelTableZoom(sheet, getExcelTableZoom(sheet) + delta);
	}

	function initExcelTableZoom(sheet) {
		if (!sheet || !sheet.querySelector('[data-excel-zoom]')) {
			return;
		}

		setExcelTableZoom(sheet, getExcelTableZoom(sheet));
		setExcelTableCompact(isExcelTableCompact());
	}

	window.LogisticaExcelZoom = {
		init: initExcelTableZoom,
		setCompact: setExcelTableCompact,
		isCompact: isExcelTableCompact
	};

	function conversionActionCell(duplicateAction, deleteAction, deleteLabel) {
		deleteLabel = deleteLabel || 'Eliminar';

		return '<td class="logistica-calc-excel-actions-cell"><div class="logistica-calc-excel-actions">' +
			'<button type="button" class="button button-small logistica-table-icon-btn logistica-calc-excel-btn-duplicate" data-action="' + duplicateAction + '" aria-label="Duplicar" title="Duplicar">' +
			'<span class="dashicons dashicons-admin-page" aria-hidden="true"></span></button>' +
			'<button type="button" class="button button-small logistica-table-icon-btn logistica-calc-excel-btn-delete" data-action="' + deleteAction + '" aria-label="' + deleteLabel + '" title="' + deleteLabel + '">' +
			'<span class="dashicons dashicons-trash" aria-hidden="true"></span></button></div></td>';
	}

	function calcExcelActionsCell(config) {
		var cell = document.createElement('td');
		var wrap = document.createElement('div');
		var editButton = document.createElement('button');
		var duplicateButton = null;
		var deleteButton = document.createElement('button');

		cell.className = 'logistica-calc-excel-actions-cell';
		wrap.className = 'logistica-calc-excel-actions';

		editButton.type = 'button';
		editButton.className = 'button button-small logistica-table-icon-btn logistica-calc-excel-btn-edit';
		editButton.setAttribute('data-action', config.editAction);
		editButton.setAttribute(config.indexAttr, String(config.index));
		editButton.setAttribute('aria-label', 'Editar');
		editButton.setAttribute('title', 'Editar');
		editButton.innerHTML = '<span class="dashicons dashicons-edit" aria-hidden="true"></span>';

		if (config.duplicateAction) {
			duplicateButton = document.createElement('button');
			duplicateButton.type = 'button';
			duplicateButton.className = 'button button-small logistica-table-icon-btn logistica-calc-excel-btn-duplicate';
			duplicateButton.setAttribute('data-action', config.duplicateAction);
			duplicateButton.setAttribute(config.indexAttr, String(config.index));
			duplicateButton.setAttribute('aria-label', 'Duplicar');
			duplicateButton.setAttribute('title', 'Duplicar');
			duplicateButton.innerHTML = '<span class="dashicons dashicons-admin-page" aria-hidden="true"></span>';
		}

		deleteButton.type = 'button';
		deleteButton.className = 'button button-small logistica-table-icon-btn logistica-calc-excel-btn-delete';
		deleteButton.setAttribute('data-action', config.deleteAction);
		deleteButton.setAttribute(config.indexAttr, String(config.index));
		deleteButton.setAttribute('aria-label', 'Eliminar');
		deleteButton.setAttribute('title', 'Eliminar');
		deleteButton.innerHTML = '<span class="dashicons dashicons-trash" aria-hidden="true"></span>';

		wrap.appendChild(editButton);
		if (duplicateButton) {
			wrap.appendChild(duplicateButton);
		}
		wrap.appendChild(deleteButton);
		cell.appendChild(wrap);

		return cell;
	}

	function toggleTapasCollapse(trigger) {
		var section = trigger.closest('.logistica-tapas-totals, .logistica-tapas-totals__group');
		var body = section ? section.querySelector(':scope > [data-tapas-collapse-body]') : null;
		var open = body ? body.hidden : false;

		if (!body) {
			return;
		}

		body.hidden = !open;
		trigger.setAttribute('aria-expanded', open ? 'true' : 'false');
		section.classList.toggle('is-expanded', open);
		section.classList.toggle('is-collapsed', !open);
	}

	function wrapTapasCollapseTitle(section, titleSelector, defaultExpanded) {
		var title = section.querySelector(':scope > ' + titleSelector);
		var body;
		var button;
		var sibling;
		var next;
		var label;

		if (!title || title.matches('[data-action="toggle-tapas-collapse"]')) {
			return;
		}

		body = document.createElement('div');
		body.className = 'logistica-tapas-collapse__body';
		body.setAttribute('data-tapas-collapse-body', '');

		sibling = title.nextElementSibling;
		while (sibling) {
			next = sibling.nextElementSibling;
			body.appendChild(sibling);
			sibling = next;
		}
		section.appendChild(body);

		label = (title.textContent || '').trim();
		button = document.createElement('button');
		button.type = 'button';
		button.className = title.className + ' logistica-tapas-collapse__trigger';
		button.setAttribute('data-action', 'toggle-tapas-collapse');
		button.setAttribute('aria-expanded', defaultExpanded ? 'true' : 'false');
		if (label) {
			button.setAttribute('aria-label', label);
		}
		button.innerHTML = title.innerHTML + '<span class="logistica-tapas-collapse__chevron" aria-hidden="true"></span>';
		title.replaceWith(button);

		body.hidden = !defaultExpanded;
		section.classList.add('is-tapas-collapsible');
		section.classList.toggle('is-expanded', defaultExpanded);
		section.classList.toggle('is-collapsed', !defaultExpanded);
	}

	function initTapasTotalsCollapsibles(scope) {
		var root = scope || document;

		root.querySelectorAll('.logistica-tapas-totals, .logistica-bolsas-totals').forEach(function (panel) {
			if (panel.dataset.tapasCollapseReady === '1') {
				return;
			}

			wrapTapasCollapseTitle(panel, '.logistica-tapas-totals__title', false);
			panel.querySelectorAll(':scope > .logistica-tapas-totals__groups > .logistica-tapas-totals__group').forEach(function (group) {
				wrapTapasCollapseTitle(group, '.logistica-tapas-section__title', false);
			});
			panel.dataset.tapasCollapseReady = '1';
		});
	}

	function initTapasExcelScroll(sheet) {
		var main = sheet.querySelector('[data-tapas-excel-scroll]');
		var hint = sheet.querySelector('[data-tapas-scroll-hint]');

		if (!main) {
			return;
		}

		function updateScrollHint() {
			var needsScroll = main.scrollWidth > main.clientWidth + 2;

			if (hint) {
				hint.hidden = !needsScroll;
			}
		}

		if (!main.dataset.tapasScrollBound) {
			main.dataset.tapasScrollBound = '1';
			window.addEventListener('resize', updateScrollHint);

			if (typeof window.ResizeObserver === 'function') {
				new window.ResizeObserver(updateScrollHint).observe(main);
			}
		}

		updateScrollHint();
	}

	function getTapasCardsUi(sheet) {
		return {
			panel: sheet ? sheet.querySelector('[data-tapas-cards-panel]') : null,
			toolbar: sheet ? sheet.querySelector('[data-tapas-cards-toolbar]') : null,
			toggleBtn: sheet ? sheet.querySelector('[data-action="toggle-tapas-cards"]') : null,
			formActions: sheet ? sheet.querySelector('[data-tapas-form-actions]') : null,
			body: sheet ? sheet.querySelector('[data-tapas-rows]') : null
		};
	}

	function setTapasCardsView(sheet, mode, activeIndex) {
		var ui = getTapasCardsUi(sheet);
		var cards = ui.body ? tapasMainRows(ui.body) : [];
		var cardsTitle = ui.panel ? ui.panel.querySelector('.logistica-tapas-cards-title') : null;
		var isSingle = mode === 'single';

		if (!ui.panel) {
			return;
		}

		ui.panel.dataset.tapasView = mode;

		cards.forEach(function (card, index) {
			var isActive = isSingle && index === activeIndex;

			card.hidden = isSingle && !isActive;
			card.classList.toggle('is-tapas-card-hidden', isSingle && !isActive);
			card.classList.toggle('is-tapas-card-active', isActive);
		});

		if (ui.formActions) {
			ui.formActions.hidden = !isSingle;
		}

		if (cardsTitle) {
			cardsTitle.hidden = isSingle;
		}

		if (ui.toolbar) {
			ui.toolbar.hidden = isSingle;
		}
	}

	function openTapasCardsPanel(sheet, mode, activeIndex, options) {
		var ui = getTapasCardsUi(sheet);
		var cards = ui.body ? tapasMainRows(ui.body) : [];
		var card = cards[activeIndex];

		options = options || {};

		if (!ui.panel) {
			return;
		}

		ui.panel.dataset.tapasPendingAdd = options.pendingAdd ? '1' : '0';
		ui.panel.hidden = false;
		ui.panel.classList.add('is-open');

		if (ui.toggleBtn) {
			ui.toggleBtn.setAttribute('aria-expanded', 'true');
			ui.toggleBtn.classList.add('is-active');
		}

		setTapasCardsView(sheet, mode, activeIndex);

		ui.panel.scrollIntoView({
			behavior: 'smooth',
			block: 'nearest'
		});

		if (card) {
			focusGridField(card.querySelector('input[data-field="product"]'));
		}
	}

	function closeTapasCardsPanel(sheet) {
		var ui = getTapasCardsUi(sheet);

		if (!ui.panel) {
			return;
		}

		ui.panel.hidden = true;
		ui.panel.classList.remove('is-open');
		ui.panel.dataset.tapasView = '';
		ui.panel.dataset.tapasPendingAdd = '0';

		if (ui.toggleBtn) {
			ui.toggleBtn.setAttribute('aria-expanded', 'false');
			ui.toggleBtn.classList.remove('is-active');
		}

		if (ui.toolbar) {
			ui.toolbar.hidden = false;
		}

		setTapasCardsView(sheet, 'all');
	}

	function confirmTapasForm(sheet) {
		if (sheet) {
			closeTapasCardsPanel(sheet);
		}
	}

	function cancelTapasForm(sheet) {
		var ui;
		var cards;
		var activeCard;
		var index;

		if (!sheet) {
			return;
		}

		ui = getTapasCardsUi(sheet);

		if (ui.panel && ui.panel.dataset.tapasPendingAdd === '1' && ui.body) {
			cards = tapasMainRows(ui.body);
			activeCard = cards.find(function (card) {
				return card.classList.contains('is-tapas-card-active');
			});

			if (!activeCard && cards.length) {
				activeCard = cards[cards.length - 1];
			}

			if (activeCard && tapasRecordCount(ui.body) > 1) {
				activeCard.remove();
				updateTapasSheet(sheet);
			}
		}

		closeTapasCardsPanel(sheet);
	}

	function editTapasRow(button) {
		var sheet = button.closest('.logistica-sheet');
		var index = parseInt(button.getAttribute('data-tapas-row-index'), 10);

		if (!sheet || Number.isNaN(index)) {
			return;
		}

		openTapasCardsPanel(sheet, 'single', index);
	}

	function closeTapasEdit(button) {
		var sheet = button.closest('.logistica-sheet');

		if (sheet) {
			closeTapasCardsPanel(sheet);
		}
	}

	function toggleTapasCardsPanel(button) {
		var sheet = button.closest('.logistica-sheet');
		var ui = getTapasCardsUi(sheet);
		var willShow = ui.panel ? ui.panel.hidden : false;

		if (!ui.panel) {
			return;
		}

		if (willShow) {
			openTapasCardsPanel(sheet, 'all');
			return;
		}

		closeTapasCardsPanel(sheet);
	}

	function getTapasCardIndex(card) {
		var body = card ? card.closest('[data-tapas-rows]') : null;

		if (!body) {
			return -1;
		}

		return tapasMainRows(body).indexOf(card);
	}

	function generateConversionRowId() {
		if (window.crypto && typeof window.crypto.randomUUID === 'function') {
			return 'ct_' + window.crypto.randomUUID().replace(/-/g, '').slice(0, 16);
		}
		return 'ct_' + Date.now().toString(36) + Math.random().toString(36).slice(2, 10);
	}

	function ensureConversionRowId(element) {
		if (!element) {
			return '';
		}
		var existing = element.getAttribute('data-row-id') || '';
		if (existing) {
			return existing;
		}
		existing = generateConversionRowId();
		element.setAttribute('data-row-id', existing);
		return existing;
	}

	function serializeSheet(sheet) {
		var type = sheet.getAttribute('data-sheet');
		var rows;

		if ('tapas-poliestireno' === type) {
			rows = tapasMainRows(sheet.querySelector('[data-tapas-rows]'));
		} else if ('bolsas-calculo' === type) {
			rows = bolsasMainRows(sheet.querySelector('[data-bolsas-rows]'));
		} else {
			rows = Array.from(sheet.querySelectorAll('tbody tr')).filter(function (row) {
				return !row.hasAttribute('data-blank-ref');
			});
		}
		var payload = {
			type: type,
			rows: rows.map(function (row) {
				var data = rowInputData(row);
				data.row_id = ensureConversionRowId(row);
				return data;
			})
		};

		if ('tapas-poliestireno' === type) {
			payload.settings = sheetSettings(sheet);
		}

		return payload;
	}

	function ensureRowCount(body, desiredRows, addCallback, sheet) {
		var currentRows = body ? body.querySelectorAll('tr:not([data-blank-ref])').length : 0;

		while (body && currentRows < desiredRows) {
			addCallback(sheet, true);
			currentRows += 1;
		}
	}

	function applyConversionState(state) {
		if (!state || typeof state !== 'object') {
			state = {};
		}

		document.querySelectorAll('.logistica-sheet[data-sheet]').forEach(function (sheet) {
			var type = sheet.getAttribute('data-sheet');
			var data = state[type];
			var rows;

			if (!data || !Array.isArray(data.rows)) {
				return;
			}

			if (type === 'montaje-compartido') {
				ensureRowCount(sheet.querySelector('[data-shared-refs]'), data.rows.length - 1, addSharedReference, sheet);
			}

			if (type === 'pliego-conversion') {
				ensureRowCount(sheet.querySelector('[data-pliego-cuts]'), data.rows.length, addPliegoCut, sheet);
			}

			if (type === 'tapas-poliestireno') {
				ensureTapasRowCount(sheet.querySelector('[data-tapas-rows]'), data.rows.length, sheet);

				if (data.settings && typeof data.settings === 'object') {
					Object.keys(data.settings).forEach(function (key) {
						var input = sheet.querySelector('[data-sheet-setting="' + key + '"]');

						if (input) {
							input.value = data.settings[key];
						}
					});
				}

				tapasMainRows(sheet.querySelector('[data-tapas-rows]')).forEach(function (row, index) {
					applyTapasRowData(row, data.rows[index]);
				});

				updateTapasSheet(sheet);
				return;
			}

			if (type === 'bolsas-calculo') {
				ensureBolsasRowCount(sheet.querySelector('[data-bolsas-rows]'), data.rows.length, sheet);
				bolsasMainRows(sheet.querySelector('[data-bolsas-rows]')).forEach(function (row, index) {
					applyBolsasRowData(row, data.rows[index]);
				});
				updateBolsasSheet(sheet);
				return;
			}

			rows = Array.from(sheet.querySelectorAll('tbody tr')).filter(function (row) {
				return !row.hasAttribute('data-blank-ref');
			});

			rows.forEach(function (row, index) {
				applyRowInputData(row, data.rows[index]);
			});
		});
	}

	function restoreConversionState() {
		loadConversionStateFromServer().then(function (serverState) {
			var localState = readLocalConversionState();
			var state = {};
			var hasServerState = serverState && typeof serverState === 'object' && Object.keys(serverState).length > 0;
			var hasLocalState = localState && typeof localState === 'object' && Object.keys(localState).length > 0;

			if (hasServerState && hasLocalState) {
				// Conserva hojas locales más ricas si el servidor las perdió (p. ej. guardado parcial antiguo).
				state = preferRicherConversionStates(serverState, localState);
				if (JSON.stringify(state) !== JSON.stringify(serverState)) {
					saveConversionStateToServer(state);
				}
			} else if (hasServerState) {
				state = serverState;
			} else if (hasLocalState) {
				state = localState;
				saveConversionStateToServer(localState);
			}

			conversionStateCache = state;
			writeLocalConversionState(state);
			applyConversionState(state);
		});
	}

	function isTemplateSupportedSheet(type) {
		return type === 'bobina-pliego' || type === 'pliego-conversion' || type === 'montaje-compartido';
	}

	var conversionTemplatesCache = {};

	function templatesNonce() {
		return window.LogisticaAdmin && window.LogisticaAdmin.conversionTemplatesNonce
			? window.LogisticaAdmin.conversionTemplatesNonce
			: '';
	}

	function templatesAjax(action, payload) {
		var ajaxUrl = window.LogisticaAdmin && window.LogisticaAdmin.ajaxUrl ? window.LogisticaAdmin.ajaxUrl : '';
		var params = new URLSearchParams();

		if (!ajaxUrl) {
			return Promise.reject(new Error('Falta configuración AJAX.'));
		}

		params.set('action', action);
		params.set('nonce', templatesNonce());
		Object.keys(payload || {}).forEach(function (key) {
			if (typeof payload[key] !== 'undefined' && payload[key] !== null) {
				params.set(key, String(payload[key]));
			}
		});

		return fetch(ajaxUrl, {
			method: 'POST',
			credentials: 'same-origin',
			headers: {
				'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
			},
			body: params.toString()
		}).then(function (response) {
			return response.json();
		});
	}

	function templatesList(type) {
		var list = conversionTemplatesCache && Array.isArray(conversionTemplatesCache[type]) ? conversionTemplatesCache[type] : [];
		return list;
	}

	function templateRowsForSheet(sheet, type) {
		if (type === 'pliego-conversion') {
			return Array.from(sheet.querySelectorAll('[data-pliego-cuts] tr'));
		}

		if (type === 'montaje-compartido') {
			return Array.from(sheet.querySelectorAll('[data-shared-refs] tr')).filter(function (row) {
				return !row.hasAttribute('data-blank-ref');
			});
		}

		return [];
	}

	function trimTemplateRows(sheet, type, desiredRows) {
		var rows = templateRowsForSheet(sheet, type);
		var removableSelector = type === 'pliego-conversion' ? '[data-action="remove-pliego-cut"]' : '[data-action="remove-shared-ref"]';

		while (rows.length > desiredRows) {
			var row = rows[rows.length - 1];
			if (!row || !row.querySelector(removableSelector)) {
				break;
			}
			row.remove();
			rows = templateRowsForSheet(sheet, type);
		}
	}

	function applySheetTemplateData(sheet, payload) {
		var type = sheet ? sheet.getAttribute('data-sheet') : '';
		var rows;

		if (!sheet || !isTemplateSupportedSheet(type) || !payload || !Array.isArray(payload.rows)) {
			return;
		}

		if (type === 'bobina-pliego') {
			rows = Array.from(sheet.querySelectorAll('tbody tr'));
			rows.forEach(function (row, index) {
				applyRowInputData(row, payload.rows[index]);
			});
			updateSheet(sheet);
			saveConversionState();
			return;
		}

		trimTemplateRows(sheet, type, payload.rows.length);

		if (type === 'pliego-conversion') {
			ensureRowCount(sheet.querySelector('[data-pliego-cuts]'), payload.rows.length, addPliegoCut, sheet);
		}

		if (type === 'montaje-compartido') {
			ensureRowCount(sheet.querySelector('[data-shared-refs]'), payload.rows.length, addSharedReference, sheet);
		}

		rows = templateRowsForSheet(sheet, type);
		rows.forEach(function (row, index) {
			applyRowInputData(row, payload.rows[index]);
		});

		updateSheet(sheet);
		saveConversionState();
	}

	function templateUi(sheet) {
		return sheet ? sheet.querySelector('[data-sheet-presets]') : null;
	}

	function renderSheetTemplates(sheet, selectedId) {
		var type = sheet ? sheet.getAttribute('data-sheet') : '';
		var ui = templateUi(sheet);
		var select = ui ? ui.querySelector('[data-template-select]') : null;
		var options = templatesList(type);

		if (!select) {
			return;
		}

		select.innerHTML = '<option value="">Selecciona una conversión</option>';
		options.forEach(function (item) {
			var option = document.createElement('option');
			option.value = item.id;
			option.textContent = item.name;
			select.appendChild(option);
		});

		if (selectedId) {
			select.value = selectedId;
		}
	}

	var CONVERSION_NAV_COLLAPSED_KEY = 'logistica_conversion_nav_collapsed_v1';

	function setConversionNavCollapsed(board, collapsed) {
		if (window.LogisticaPanelNav && typeof window.LogisticaPanelNav.setSectionNavCollapsed === 'function') {
			window.LogisticaPanelNav.setSectionNavCollapsed(board, collapsed);
			return;
		}

		var navToggle;
		var reopenButton;
		var shell;

		if (!board) {
			return;
		}

		board.classList.toggle('is-nav-collapsed', collapsed);
		navToggle = board.querySelector('.logistica-conversion-nav__toggle');
		reopenButton = board.querySelector('.logistica-conversion-nav-reopen');

		if (navToggle) {
			navToggle.setAttribute('aria-expanded', collapsed ? 'false' : 'true');
			navToggle.title = collapsed ? 'Mostrar menú' : 'Ocultar menú';
		}

		if (reopenButton) {
			reopenButton.hidden = !collapsed;
			reopenButton.setAttribute('aria-expanded', collapsed ? 'false' : 'true');
		}

		shell = board.closest('[data-logistica-inventory-shell]');
		if (shell) {
			syncAppRailSectionButton(shell);
			updateSectionNavBackdrop(shell);
		}

		try {
			localStorage.setItem(CONVERSION_NAV_COLLAPSED_KEY, collapsed ? '1' : '0');
		} catch (error) {
			// Ignore storage errors.
		}
	}

	function toggleConversionNav(board) {
		if (!board) {
			return;
		}

		setConversionNavCollapsed(board, !board.classList.contains('is-nav-collapsed'));
	}

	function scrollToConversionCalc(slug) {
		var target = document.getElementById('logistica-calc-' + slug);

		if (target) {
			target.scrollIntoView({ behavior: 'smooth', block: 'start' });
		}
	}

	function initConversionExcelScroll() {
		document.querySelectorAll('[data-conversion-excel-scroll]').forEach(function (scrollHost) {
			var hint = scrollHost.parentElement ? scrollHost.parentElement.querySelector('[data-conversion-scroll-hint]') : null;

			function updateScrollHint() {
				if (!hint) {
					return;
				}

				hint.hidden = scrollHost.scrollWidth <= scrollHost.clientWidth + 2;
			}

			if (!scrollHost.dataset.conversionScrollBound) {
				scrollHost.dataset.conversionScrollBound = '1';
				window.addEventListener('resize', updateScrollHint);

				if (typeof window.ResizeObserver === 'function') {
					new window.ResizeObserver(updateScrollHint).observe(scrollHost);
				}
			}

			updateScrollHint();
		});
	}

	function initConversionNav() {
		document.querySelectorAll('[data-conversion-board]').forEach(function (board) {
			initConversionNavBoard(board);
		});
	}

	function initConversionNavBoard(board) {
		var links;
		var sheets;

		if (!board || board.dataset.conversionNavEnhanced === '1') {
			return;
		}

		board.dataset.conversionNavEnhanced = '1';

		if (window.LogisticaPanelNav && typeof window.LogisticaPanelNav.initSectionNavs === 'function') {
			window.LogisticaPanelNav.initSectionNavs();
		} else if (board.dataset.conversionNavBound !== '1') {
			var collapsed = true;
			var stored;

			board.dataset.conversionNavBound = '1';

			try {
				stored = localStorage.getItem(CONVERSION_NAV_COLLAPSED_KEY);
				if (stored === null) {
					collapsed = true;
				} else {
					collapsed = stored === '1';
				}
			} catch (error) {
				collapsed = true;
			}

			setConversionNavCollapsed(board, collapsed);
		}

		board.addEventListener('click', function (event) {
			var navLink = event.target.closest('[data-calc-nav]');

			if (event.target.closest('[data-action="toggle-conversion-nav"]')) {
				return;
			}

			if (navLink && board.contains(navLink)) {
				event.preventDefault();
				scrollToConversionCalc(navLink.getAttribute('data-calc-nav'));
				if (window.history && window.history.replaceState) {
					window.history.replaceState(null, '', navLink.getAttribute('href'));
				}
			}
		});

		links = board.querySelectorAll('[data-calc-nav]');
		sheets = board.querySelectorAll('.logistica-conversion-main .logistica-sheet[id]');

		if (typeof window.IntersectionObserver === 'function' && sheets.length) {
			var navObserver = new window.IntersectionObserver(function (entries) {
				var visible = entries.filter(function (entry) {
					return entry.isIntersecting;
				}).sort(function (left, right) {
					return right.intersectionRatio - left.intersectionRatio;
				});
				var activeSlug;

				if (!visible.length) {
					return;
				}

				activeSlug = visible[0].target.id.replace('logistica-calc-', '');
				links.forEach(function (link) {
					link.classList.toggle('is-active', link.getAttribute('data-calc-nav') === activeSlug);
				});
			}, {
				root: null,
				rootMargin: '-20% 0px -55% 0px',
				threshold: [0, 0.15, 0.5, 1]
			});

			sheets.forEach(function (sheet) {
				navObserver.observe(sheet);
			});
		} else if (links.length) {
			links[0].classList.add('is-active');
		}

		if (window.location.hash && window.location.hash.indexOf('#logistica-calc-') === 0) {
			window.setTimeout(function () {
				var hashTarget = document.querySelector(window.location.hash);

				if (hashTarget) {
					hashTarget.scrollIntoView({ block: 'start' });
				}
			}, 120);
		}
	}

	function initConversionTemplates() {
		templatesAjax('logistica_get_conversion_templates', {})
			.then(function (response) {
				if (response && response.success && response.data && response.data.templates) {
					conversionTemplatesCache = response.data.templates;
				}
			})
			.catch(function () {
				conversionTemplatesCache = {};
			})
			.finally(function () {
				document.querySelectorAll('.logistica-sheet[data-sheet]').forEach(function (sheet) {
					var type = sheet.getAttribute('data-sheet');
					if (isTemplateSupportedSheet(type)) {
						renderSheetTemplates(sheet);
					}
				});
			});
	}

	function selectedTemplate(sheet) {
		var type = sheet ? sheet.getAttribute('data-sheet') : '';
		var ui = templateUi(sheet);
		var select = ui ? ui.querySelector('[data-template-select]') : null;
		var id = select ? select.value : '';
		var list = templatesList(type);
		var item = null;

		if (id) {
			item = list.find(function (entry) { return entry.id === id; }) || null;
		}

		return {
			type: type,
			select: select,
			list: list,
			id: id,
			item: item
		};
	}

	function updateTemplatesFromResponse(response) {
		if (response && response.success && response.data && response.data.templates) {
			conversionTemplatesCache = response.data.templates;
		}
	}

	function saveSheetTemplate(button) {
		var sheet = button.closest('.logistica-sheet[data-sheet]');
		var type = sheet ? sheet.getAttribute('data-sheet') : '';
		var name;

		if (!sheet || !isTemplateSupportedSheet(type)) {
			return;
		}

		name = window.prompt('Nombre para guardar esta conversión:');
		if (!name || !name.trim()) {
			return;
		}

		templatesAjax('logistica_save_conversion_template', {
			sheet_type: type,
			name: name.trim(),
			payload: JSON.stringify(serializeSheet(sheet))
		}).then(function (response) {
			updateTemplatesFromResponse(response);
			renderSheetTemplates(sheet, response && response.data ? response.data.template_id : '');
		}).catch(function () {
			window.alert('No se pudo guardar la conversión.');
		});
	}

	function loadSheetTemplate(button) {
		var sheet = button.closest('.logistica-sheet[data-sheet]');
		var info = selectedTemplate(sheet);

		if (!sheet || !info.item || !info.item.data) {
			window.alert('Selecciona una conversión guardada para cargar.');
			return;
		}

		applySheetTemplateData(sheet, info.item.data);
	}

	function editSheetTemplate(button) {
		var sheet = button.closest('.logistica-sheet[data-sheet]');
		var info = selectedTemplate(sheet);
		var newName;

		if (!sheet || !info.item) {
			window.alert('Selecciona una conversión guardada para editar.');
			return;
		}

		newName = window.prompt('Editar nombre de la conversión:', info.item.name || '');
		if (!newName || !newName.trim()) {
			return;
		}

		templatesAjax('logistica_save_conversion_template', {
			sheet_type: info.type,
			template_id: info.item.id,
			name: newName.trim(),
			payload: JSON.stringify(serializeSheet(sheet))
		}).then(function (response) {
			updateTemplatesFromResponse(response);
			renderSheetTemplates(sheet, info.item.id);
		}).catch(function () {
			window.alert('No se pudo editar la conversión.');
		});
	}

	function deleteSheetTemplate(button) {
		var sheet = button.closest('.logistica-sheet[data-sheet]');
		var info = selectedTemplate(sheet);

		if (!sheet || !info.item) {
			window.alert('Selecciona una conversión guardada para eliminar.');
			return;
		}

		if (!confirmLogisticaDelete('¿Eliminar la conversión guardada?', '"' + info.item.name + '"')) {
			return;
		}

		templatesAjax('logistica_delete_conversion_template', {
			sheet_type: info.type,
			template_id: info.item.id
		}).then(function (response) {
			updateTemplatesFromResponse(response);
			renderSheetTemplates(sheet);
		}).catch(function () {
			window.alert('No se pudo eliminar la conversión.');
		});
	}

	function sheetMetrics(row) {
		var heightM = field(row, 'height_cm') / 100;
		var widthM = field(row, 'width_cm') / 100;
		var area = heightM * widthM;
		var gsmKg = field(row, 'gsm') / 1000;
		var weight = area * gsmKg;

		return {
			heightM: heightM,
			widthM: widthM,
			area: area,
			gsmKg: gsmKg,
			weight: weight
		};
	}

	function metricsWithGsm(row, gsm) {
		var heightM = field(row, 'height_cm') / 100;
		var widthM = field(row, 'width_cm') / 100;
		var area = heightM * widthM;
		var gsmKg = gsm / 1000;

		return {
			heightM: heightM,
			widthM: widthM,
			area: area,
			gsmKg: gsmKg,
			weight: area * gsmKg
		};
	}

	function updateBobinaPliego(sheet) {
		sheet.querySelectorAll('tbody tr').forEach(function (row) {
			var metrics = sheetMetrics(row);
			var mode = row.getAttribute('data-mode');
			var quantity = mode === 'pliegos' ? field(row, 'quantity') : 0;
			var kg = mode === 'kilos' ? field(row, 'kg') : 0;

			if (mode === 'pliegos') {
				kg = quantity * metrics.weight;
			}

			if (mode === 'kilos') {
				quantity = metrics.weight > 0 ? kg / metrics.weight : 0;
			}

			output(row, 'width_m', metrics.widthM, 2);
			output(row, 'height_m', metrics.heightM, 2);
			output(row, 'area_m2', metrics.area, 4);
			output(row, 'gsm_kg', metrics.gsmKg, 3);
			output(row, 'sheet_weight', metrics.weight, 5);
			output(row, 'kg', Math.round(kg), 0, true);
			output(row, 'quantity', quantity, 0, true);
		});
	}

	function updateGramajePapel(sheet) {
		var row = sheet.querySelector('tbody tr');
		var area = field(row, 'width_cm') * field(row, 'height_cm');
		var oneM2 = 10000;
		var gsm = area > 0 ? (field(row, 'weight_gr') * oneM2) / area : 0;

		output(row, 'area_cm2', area, 0, true);
		output(row, 'one_m2', oneM2, 0, true);
		output(row, 'gsm', gsm, 0, true);
	}

	function updateCalibreConversion(sheet) {
		sheet.querySelectorAll('tbody tr').forEach(function (row) {
			var mode = row.getAttribute('data-mode');
			var mm = 0;
			var inches = 0;
			var commercial = Number.NaN;

			if (mode === 'mm') {
				mm = field(row, 'calibre_mm');
				inches = mm > 0 ? mm / 25.4 : 0;
				commercial = closingCaliberThousandthsFromMm(mm);
			} else if (mode === 'pulgadas') {
				inches = field(row, 'calibre_inches');
				mm = inches > 0 ? inches * 25.4 : 0;
				commercial = closingCaliberThousandthsFromInches(inches);
			} else if (mode === 'comercial') {
				commercial = field(row, 'calibre_commercial');
				inches = closingCaliberInchesFromCommercial(commercial);
				mm = closingCaliberMmFromCommercial(commercial);
				if (!Number.isFinite(mm)) {
					mm = 0;
				}
				if (!Number.isFinite(inches)) {
					inches = 0;
				}
			}

			output(row, 'calibre_mm', mm, 3);
			output(row, 'calibre_inches', inches, 4);
			outputText(row, 'calibre_commercial', formatCaliberCommercialLabel(commercial));
		});
	}

	function sheetSettings(sheet) {
		var settings = {
			roll_kg: 35,
			waste_pct: 5,
			resin_step: 25
		};

		if (!sheet) {
			return settings;
		}

		sheet.querySelectorAll('[data-sheet-setting]').forEach(function (input) {
			var key = input.getAttribute('data-sheet-setting');

			if (key) {
				settings[key] = numberValue(input);
			}
		});

		return settings;
	}

	function roundUpToStep(value, step) {
		if (!Number.isFinite(value) || value <= 0 || !Number.isFinite(step) || step <= 0) {
			return 0;
		}

		return Math.ceil(value / step) * step;
	}

	function updateTapasSheet(sheet, options) {
		var settings = sheetSettings(sheet);
		var filters = tapasFilterState(sheet);
		var wasteFactor = 1 + (settings.waste_pct > 0 ? settings.waste_pct : 5) / 100;
		var resinStep = settings.resin_step > 0 ? settings.resin_step : 25;
		var rollKg = settings.roll_kg > 0 ? settings.roll_kg : 35;
		var sums = {
			pedido: 0,
			stock: 0,
			fabricar: 0,
			resina_kg: 0,
			retal_kg: 0,
			total_kg: 0,
			retal_merma: 0,
			resina_merma: 0,
			total_merma: 0,
			resina_multiplo: 0,
			rollos: 0,
			retal_sent: 0,
			resina_sent: 0,
			retal_due: 0,
			resina_due: 0,
			total_due: 0,
			material_llega: 0,
			service_total: 0
		};

		tapasMainRows(sheet.querySelector('[data-tapas-rows]')).forEach(function (row) {
			var pedido = field(row, 'pedido');
			var stock = field(row, 'stock');
			var fabricar = Math.max(0, pedido - stock);
			var undMolde = Math.max(1, field(row, 'und_molde'));
			var pesoGr = field(row, 'peso_gr');
			var resinaPct = Math.max(0, Math.min(100, field(row, 'resina_pct')));
			var retalPct = 100 - resinaPct;
			var ciclos = fabricar / undMolde;
			var baseKg = (ciclos * pesoGr) / 1000;
			var resinaKg = baseKg * (resinaPct / 100);
			var retalKg = baseKg * (retalPct / 100);
			var totalKg = resinaKg + retalKg;
			var retalMerma = retalKg * wasteFactor;
			var resinaMerma = resinaKg * wasteFactor;
			var totalMerma = retalMerma + resinaMerma;
			var resinaMultiplo = roundUpToStep(resinaMerma, resinStep);
			var rollos = rollKg > 0 ? Math.ceil(resinaMerma / rollKg) : 0;
			var retalSent = Math.max(0, field(row, 'retal_sent'));
			var resinaSent = Math.max(0, field(row, 'resina_sent'));
			var retalDue = Math.max(0, retalMerma - retalSent);
			var resinaDue = Math.max(0, resinaMerma - resinaSent);
			var totalDue = retalDue + resinaDue;
			var materialLlega = Math.max(0, field(row, 'material_llega'));
			var precioKg = tapasPrecioKg(row);
			var serviceTotal = materialLlega * precioKg;

			output(row, 'fabricar', fabricar, 0, true);
			output(row, 'resina_kg', resinaKg, 2);
			output(row, 'retal_kg', retalKg, 2);
			output(row, 'total_kg', totalKg, 2);
			output(row, 'retal_merma', retalMerma, 2);
			output(row, 'resina_merma', resinaMerma, 2);
			output(row, 'total_merma', totalMerma, 2);
			output(row, 'resina_multiplo', resinaMultiplo, 0, true);
			output(row, 'rollos', rollos, 0, true);
			output(row, 'retal_due', retalDue, 2);
			output(row, 'resina_due', resinaDue, 2);
			output(row, 'total_due', totalDue, 2);
			output(row, 'service_total', serviceTotal, 0, true);
			updateTapasStatusBadge(row);

			if (!tapasCardMatchesFilter(row, filters)) {
				return;
			}

			sums.pedido += pedido;
			sums.stock += stock;
			sums.fabricar += fabricar;
			sums.resina_kg += resinaKg;
			sums.retal_kg += retalKg;
			sums.total_kg += totalKg;
			sums.retal_merma += retalMerma;
			sums.resina_merma += resinaMerma;
			sums.total_merma += totalMerma;
			sums.resina_multiplo += resinaMultiplo;
			sums.rollos += rollos;
			sums.retal_sent += retalSent;
			sums.resina_sent += resinaSent;
			sums.retal_due += retalDue;
			sums.resina_due += resinaDue;
			sums.total_due += totalDue;
			sums.material_llega += materialLlega;
			sums.service_total += serviceTotal;
		});

		var summaryInputs = sheet.querySelector('[data-tapas-summary-inputs]');
		var summary = sheet.querySelector('[data-tapas-summary]');
		var summaryControl = sheet.querySelector('[data-tapas-summary-control]');
		var tableSummaryInputs = sheet.querySelector('[data-tapas-table-summary-inputs]');
		var tableSummary = sheet.querySelector('[data-tapas-table-summary]');
		var tableSummaryControl = sheet.querySelector('[data-tapas-table-summary-control]');

		if (summaryInputs) {
			output(summaryInputs, 'sum_pedido', sums.pedido, 0, true);
			output(summaryInputs, 'sum_stock', sums.stock, 0, true);
			output(summaryInputs, 'sum_fabricar', sums.fabricar, 0, true);
		}

		if (summary) {
			output(summary, 'sum_resina_kg', sums.resina_kg, 2);
			output(summary, 'sum_retal_kg', sums.retal_kg, 2);
			output(summary, 'sum_total_kg', sums.total_kg, 2);
			output(summary, 'sum_retal_merma', sums.retal_merma, 2);
			output(summary, 'sum_resina_merma', sums.resina_merma, 2);
			output(summary, 'sum_total_merma', sums.total_merma, 2);
			output(summary, 'sum_resina_multiplo', sums.resina_multiplo, 0, true);
			output(summary, 'sum_rollos', sums.rollos, 0, true);
		}

		if (tableSummaryInputs) {
			output(tableSummaryInputs, 'sum_pedido', sums.pedido, 0, true);
			output(tableSummaryInputs, 'sum_stock', sums.stock, 0, true);
			output(tableSummaryInputs, 'sum_fabricar', sums.fabricar, 0, true);
		}

		if (tableSummary) {
			output(tableSummary, 'sum_resina_kg', sums.resina_kg, 2);
			output(tableSummary, 'sum_retal_kg', sums.retal_kg, 2);
			output(tableSummary, 'sum_total_kg', sums.total_kg, 2);
			output(tableSummary, 'sum_retal_merma', sums.retal_merma, 2);
			output(tableSummary, 'sum_resina_merma', sums.resina_merma, 2);
			output(tableSummary, 'sum_total_merma', sums.total_merma, 2);
			output(tableSummary, 'sum_resina_multiplo', sums.resina_multiplo, 0, true);
			output(tableSummary, 'sum_rollos', sums.rollos, 0, true);
		}

		if (summaryControl) {
			output(summaryControl, 'sum_retal_sent', sums.retal_sent, 2);
			output(summaryControl, 'sum_resina_sent', sums.resina_sent, 2);
			output(summaryControl, 'sum_retal_due', sums.retal_due, 2);
			output(summaryControl, 'sum_resina_due', sums.resina_due, 2);
			output(summaryControl, 'sum_total_due', sums.total_due, 2);
			output(summaryControl, 'sum_material_llega', sums.material_llega, 2);
			output(summaryControl, 'sum_service_total', sums.service_total, 0, true);
		}

		if (tableSummaryControl) {
			output(tableSummaryControl, 'sum_retal_sent', sums.retal_sent, 2);
			output(tableSummaryControl, 'sum_resina_sent', sums.resina_sent, 2);
			output(tableSummaryControl, 'sum_retal_due', sums.retal_due, 2);
			output(tableSummaryControl, 'sum_resina_due', sums.resina_due, 2);
			output(tableSummaryControl, 'sum_total_due', sums.total_due, 2);
			output(tableSummaryControl, 'sum_material_llega', sums.material_llega, 2);
			output(tableSummaryControl, 'sum_service_total', sums.service_total, 0, true);
		}

		syncTapasExcelTable(sheet);
		refreshTapasFilterOptions(sheet);
		applyTapasFilters(sheet, options);
		initMoneyFields(sheet);
	}

	function appendTapasRecord(body) {
		var prototype = body ? body.querySelector('[data-tapas-record]') : null;
		var card;

		if (!prototype) {
			return null;
		}

		card = prototype.cloneNode(true);
		body.appendChild(card);
		resetTapasCard(card);

		return card;
	}

	function addTapasRow(sheet, silent) {
		var body = sheet.querySelector('[data-tapas-rows]');
		var mainRow;

		if (!body) {
			return;
		}

		mainRow = appendTapasRecord(body);
		updateTapasSheet(sheet);

		if (!silent) {
			openTapasCardsPanel(sheet, 'single', tapasRecordCount(body) - 1, { pendingAdd: true });
		}
	}

	function removeTapasRow(button) {
		var card = button.closest('[data-tapas-record]');
		var sheet = button.closest('.logistica-sheet');
		var body = sheet ? sheet.querySelector('[data-tapas-rows]') : null;
		var ui = sheet ? getTapasCardsUi(sheet) : null;
		var wasSingle = ui.panel && ui.panel.dataset.tapasView === 'single';
		var label;
		var index;
		var cards;

		if (!card && button.hasAttribute('data-tapas-row-index')) {
			index = parseInt(button.getAttribute('data-tapas-row-index'), 10);
			cards = body ? tapasMainRows(body) : [];
			card = !Number.isNaN(index) ? cards[index] : null;
		}

		if (!card || !body) {
			return;
		}

		if (tapasRecordCount(body) <= 1) {
			window.alert('Debe quedar al menos una referencia en la tabla.');
			return;
		}

		label = tapasFieldText(card, 'product');
		if (tapasFieldText(card, 'color')) {
			label = label ? label + ' / ' + tapasFieldText(card, 'color') : tapasFieldText(card, 'color');
		}
		if (!label) {
			label = 'Referencia ' + String(getTapasCardIndex(card) + 1);
		}

		if (!confirmLogisticaDelete('¿Eliminar esta referencia de cálculo de tapas?', label, true)) {
			return;
		}

		ensureConversionRowId(card);
		card.remove();

		if (sheet) {
			updateTapasSheet(sheet);

			if (wasSingle) {
				closeTapasCardsPanel(sheet);
			}
		}
	}

	function duplicateTapasRow(button) {
		var card = button.closest('[data-tapas-record]');
		var sheet = button.closest('.logistica-sheet');
		var body = sheet ? sheet.querySelector('[data-tapas-rows]') : null;
		var data;
		var copy;
		var newIndex;
		var index;
		var cards;

		if (!card && button.hasAttribute('data-tapas-row-index')) {
			index = parseInt(button.getAttribute('data-tapas-row-index'), 10);
			cards = body ? tapasMainRows(body) : [];
			card = !Number.isNaN(index) ? cards[index] : null;
		}

		if (!card || !sheet || !body) {
			return;
		}

		data = rowInputData(card);
		copy = card.cloneNode(true);
		body.appendChild(copy);
		applyTapasRowData(copy, data);
		updateTapasSheet(sheet);
		newIndex = getTapasCardIndex(copy);
		openTapasCardsPanel(sheet, 'single', newIndex);
	}

	function bolsasMainRows(body) {
		return body ? Array.from(body.querySelectorAll('[data-bolsas-record]')) : [];
	}

	function bolsasRecordCount(body) {
		return bolsasMainRows(body).length;
	}

	function bolsasFieldText(card, name) {
		var input = card ? card.querySelector('[data-field="' + name + '"]') : null;
		return input ? input.value : '';
	}

	function bolsasFieldNumberText(card, name, decimals, integer) {
		var input = card ? card.querySelector('[data-field="' + name + '"]') : null;
		var value;

		if (!input) {
			return '';
		}

		value = numberValue(input);

		if (isMoneyField(name)) {
			var moneyCfg = moneyFieldConfig(name, decimals, integer);
			return Number.isFinite(value) ? displayMoney(value, moneyCfg.decimals, moneyCfg.integer) : '';
		}

		return Number.isFinite(value) ? displayNumber(value, decimals, integer) : '';
	}

	function bolsasOutputText(card, name) {
		var outputNode = card ? card.querySelector('[data-output="' + name + '"]') : null;
		var cfg;

		if (!outputNode) {
			return '';
		}

		if (isMoneyField(name)) {
			cfg = moneyFieldConfig(name);
			return displayMoney(parseMoneyValue(outputNode.textContent), cfg.decimals, cfg.integer);
		}

		return outputNode.textContent;
	}

	function resetBolsasCard(card) {
		if (!card) {
			return;
		}

		card.querySelectorAll('input[data-field]').forEach(function (input) {
			if (input.getAttribute('data-field') === 'status_other') {
				input.value = '';
				return;
			}

			input.value = '0';
		});

		card.querySelectorAll('select[data-field]').forEach(function (select) {
			select.value = 'fabricacion';
		});

		card.querySelectorAll('[data-output]').forEach(function (outputNode) {
			outputNode.textContent = '';
		});

		toggleTapasStatusOther(card);
		updateTapasStatusBadge(card);
	}

	function applyBolsasRowData(card, data) {
		applyTapasRowData(card, data);
	}

	function initBolsasCards(sheet) {
		var body = sheet ? sheet.querySelector('[data-bolsas-rows]') : null;

		if (!body) {
			return;
		}

		bolsasMainRows(body).forEach(function (card) {
			toggleTapasStatusOther(card);
			updateTapasStatusBadge(card);
		});
	}

	function bolsasFilterState(sheet) {
		var wrap = sheet ? sheet.querySelector('[data-bolsas-filters]') : null;
		var descripcion = '';
		var status = '';

		if (!wrap) {
			return { descripcion: descripcion, status: status, active: false };
		}

		descripcion = (wrap.querySelector('[data-bolsas-filter="descripcion"]') || {}).value || '';
		status = (wrap.querySelector('[data-bolsas-filter="status"]') || {}).value || '';

		return {
			descripcion: descripcion,
			status: status,
			active: !!(descripcion || status)
		};
	}

	function bolsasCardMatchesFilter(card, filters) {
		var descText;

		if (!filters || !filters.active) {
			return true;
		}

		if (filters.descripcion) {
			descText = bolsasFieldText(card, 'descripcion').trim().toLowerCase();
			if (descText !== String(filters.descripcion).trim().toLowerCase()) {
				return false;
			}
		}

		if (filters.status && tapasStatusSlug(card) !== filters.status) {
			return false;
		}

		return true;
	}

	function refreshBolsasFilterOptions(sheet) {
		var wrap = sheet ? sheet.querySelector('[data-bolsas-filters]') : null;
		var descripcionSelect;
		var descripciones = [];
		var currentDescripcion;
		var descripcionMap = {};

		if (!wrap) {
			return;
		}

		descripcionSelect = wrap.querySelector('[data-bolsas-filter="descripcion"]');

		if (!descripcionSelect) {
			return;
		}

		currentDescripcion = descripcionSelect.value;

		bolsasMainRows(sheet.querySelector('[data-bolsas-rows]')).forEach(function (card) {
			var descripcion = bolsasFieldText(card, 'descripcion').trim();

			if (descripcion && !descripcionMap[descripcion]) {
				descripcionMap[descripcion] = true;
				descripciones.push(descripcion);
			}
		});

		descripciones.sort(function (a, b) {
			return a.localeCompare(b, 'es', { sensitivity: 'base' });
		});

		descripcionSelect.innerHTML = '<option value="">Todos</option>';
		descripciones.forEach(function (value) {
			var option = document.createElement('option');
			option.value = value;
			option.textContent = value;
			descripcionSelect.appendChild(option);
		});

		if (currentDescripcion && descripcionMap[currentDescripcion]) {
			descripcionSelect.value = currentDescripcion;
		}
	}

	function applyBolsasFilters(sheet, options) {
		var filters = bolsasFilterState(sheet);
		var cards = bolsasMainRows(sheet.querySelector('[data-bolsas-rows]'));
		var tbody = sheet.querySelector('[data-bolsas-table-body]');
		var wrap = sheet.querySelector('[data-bolsas-filters]');
		var meta = sheet.querySelector('[data-bolsas-filter-meta]');
		var badge = sheet.querySelector('[data-bolsas-filter-badge]');
		var clearButton = wrap ? wrap.querySelector('[data-action="clear-bolsas-filters"]') : null;
		var visible = 0;
		var row;
		var rowList;

		options = options || {};

		cards.forEach(function (card, index) {
			var match = bolsasCardMatchesFilter(card, filters);

			card.classList.toggle('is-tapas-filter-hidden', !match);

			if (tbody) {
				row = tbody.querySelector('[data-bolsas-table-row="' + String(index) + '"]');
				if (row) {
					row.dataset.logisticaFilterVisible = match ? '1' : '0';
					row.classList.toggle('is-tapas-filter-hidden', !match);
				}
			}

			if (match) {
				visible += 1;
			}
		});

		rowList = tbody ? Array.prototype.slice.call(tbody.querySelectorAll('[data-bolsas-table-row]')) : [];
		applyTablePagination(sheet, 'bolsas', rowList, options);

		if (meta) {
			if (filters.active) {
				meta.textContent = visible + ' de ' + cards.length + ' referencias visibles';
				meta.hidden = false;
			} else {
				meta.textContent = '';
				meta.hidden = true;
			}
		}

		if (wrap) {
			wrap.classList.toggle('is-active', filters.active);
		}

		if (badge) {
			badge.hidden = !filters.active;
		}

		if (clearButton) {
			clearButton.disabled = !filters.active;
		}

		sheet.classList.toggle('is-bolsas-filtered', filters.active);
	}

	function clearBolsasFilters(sheet) {
		var wrap = sheet ? sheet.querySelector('[data-bolsas-filters]') : null;

		if (!wrap) {
			return;
		}

		wrap.querySelectorAll('[data-bolsas-filter]').forEach(function (select) {
			select.value = '';
		});

		applyBolsasFilters(sheet, { resetPage: true });
	}

	function getBolsasCardsUi(sheet) {
		return {
			panel: sheet ? sheet.querySelector('[data-bolsas-cards-panel]') : null,
			formActions: sheet ? sheet.querySelector('[data-bolsas-form-actions]') : null,
			body: sheet ? sheet.querySelector('[data-bolsas-rows]') : null
		};
	}

	function setBolsasCardsView(sheet, mode, activeIndex) {
		var ui = getBolsasCardsUi(sheet);
		var cards = ui.body ? bolsasMainRows(ui.body) : [];
		var isSingle = mode === 'single';

		if (!ui.panel) {
			return;
		}

		ui.panel.dataset.bolsasView = mode;

		cards.forEach(function (card, index) {
			var isActive = isSingle && index === activeIndex;

			card.hidden = isSingle && !isActive;
			card.classList.toggle('is-tapas-card-hidden', isSingle && !isActive);
			card.classList.toggle('is-tapas-card-active', isActive);
		});

		if (ui.formActions) {
			ui.formActions.hidden = !isSingle;
		}
	}

	function openBolsasCardsPanel(sheet, mode, activeIndex, options) {
		var ui = getBolsasCardsUi(sheet);
		var cards = ui.body ? bolsasMainRows(ui.body) : [];
		var card = cards[activeIndex];

		options = options || {};

		if (!ui.panel) {
			return;
		}

		ui.panel.dataset.bolsasPendingAdd = options.pendingAdd ? '1' : '0';
		ui.panel.hidden = false;
		ui.panel.classList.add('is-open');
		setBolsasCardsView(sheet, mode, activeIndex);

		ui.panel.scrollIntoView({
			behavior: 'smooth',
			block: 'nearest'
		});

		if (card) {
			focusGridField(card.querySelector('input[data-field="descripcion"]'));
		}
	}

	function closeBolsasCardsPanel(sheet) {
		var ui = getBolsasCardsUi(sheet);

		if (!ui.panel) {
			return;
		}

		ui.panel.hidden = true;
		ui.panel.classList.remove('is-open');
		ui.panel.dataset.bolsasView = '';
		ui.panel.dataset.bolsasPendingAdd = '0';
		setBolsasCardsView(sheet, 'all');
	}

	function confirmBolsasForm(sheet) {
		if (sheet) {
			closeBolsasCardsPanel(sheet);
		}
	}

	function cancelBolsasForm(sheet) {
		var ui;
		var cards;
		var activeCard;

		if (!sheet) {
			return;
		}

		ui = getBolsasCardsUi(sheet);

		if (ui.panel && ui.panel.dataset.bolsasPendingAdd === '1' && ui.body) {
			cards = bolsasMainRows(ui.body);
			activeCard = cards.find(function (card) {
				return card.classList.contains('is-tapas-card-active');
			});

			if (!activeCard && cards.length) {
				activeCard = cards[cards.length - 1];
			}

			if (activeCard && bolsasRecordCount(ui.body) > 1) {
				activeCard.remove();
				updateBolsasSheet(sheet);
			}
		}

		closeBolsasCardsPanel(sheet);
	}

	function closeBolsasEdit(button) {
		var sheet = button.closest('.logistica-sheet');

		if (sheet) {
			closeBolsasCardsPanel(sheet);
		}
	}

	function getBolsasCardIndex(card) {
		var body = card ? card.closest('[data-bolsas-rows]') : null;

		if (!body) {
			return -1;
		}

		return bolsasMainRows(body).indexOf(card);
	}

	function editBolsasRow(button) {
		var sheet = button.closest('.logistica-sheet');
		var index = parseInt(button.getAttribute('data-bolsas-row-index'), 10);

		if (!sheet || Number.isNaN(index)) {
			return;
		}

		openBolsasCardsPanel(sheet, 'single', index);
	}

	function syncBolsasExcelTable(sheet) {
		var tbody = sheet.querySelector('[data-bolsas-table-body]');
		var cards = bolsasMainRows(sheet.querySelector('[data-bolsas-rows]'));
		var row;
		var index;

		if (!tbody) {
			return;
		}

		tbody.innerHTML = '';

		cards.forEach(function (card, cardIndex) {
			row = document.createElement('tr');
			row.setAttribute('data-bolsas-table-row', String(cardIndex));
			row.dataset.logisticaFilterVisible = '1';
			row.className = 'logistica-tapas-excel-row is-row-status-' + tapasStatusSlug(card);
			row.appendChild(calcExcelValidationCell());
			applyCalcRowValidation(row, card, bolsasRowMissingDetails);
			row.appendChild(tapasExcelTableCell(tapasStatusLabel(card), 'logistica-tapas-excel__status is-text'));
			row.appendChild(tapasExcelTableCell(String(cardIndex + 1), 'is-num'));
			row.appendChild(tapasExcelTableCell(bolsasFieldText(card, 'descripcion'), 'is-text'));
			row.appendChild(tapasExcelTableCell(bolsasFieldNumberText(card, 'kilos', 2, false), 'is-num is-col-bolsas-peso'));
			row.appendChild(tapasExcelTableCell(bolsasOutputText(card, 'gramos'), 'is-auto is-num is-col-bolsas-peso'));
			row.appendChild(tapasExcelTableCell(bolsasFieldNumberText(card, 'peso_paquete_10', 2, false), 'is-num is-col-bolsas-peso'));
			row.appendChild(tapasExcelTableCell(bolsasOutputText(card, 'peso_bolsa'), 'is-auto is-num is-col-bolsas-peso'));
			row.appendChild(tapasExcelTableCell(bolsasOutputText(card, 'cantidad_bolsas'), 'is-auto is-num is-col-bolsas-cantidad'));
			row.appendChild(tapasExcelTableCell(bolsasFieldNumberText(card, 'precio_kg', 0, true), 'is-num is-col-bolsas-precio is-money-col'));
			row.appendChild(tapasExcelTableCell(bolsasFieldNumberText(card, 'precio_total', 0, true), 'is-num is-col-bolsas-precio is-money-col'));
			row.appendChild(tapasExcelTableCell(bolsasOutputText(card, 'precio_und'), 'is-auto is-num is-col-bolsas-precio'));
			row.appendChild(tapasExcelTableCopyField(card, 'codigo_graficos', 'is-col-bolsas-codigo'));
			row.appendChild(tapasExcelTableCell(bolsasFieldNumberText(card, 'ancho', 2, false), 'is-num'));
			row.appendChild(tapasExcelTableCell(bolsasFieldNumberText(card, 'largo', 2, false), 'is-num'));
			row.appendChild(tapasExcelTableCell(bolsasFieldNumberText(card, 'calibre', 2, false), 'is-num'));
			row.appendChild(calcExcelActionsCell({
				editAction: 'edit-bolsas-row',
				duplicateAction: 'duplicate-bolsas-row',
				deleteAction: 'remove-bolsas-row',
				indexAttr: 'data-bolsas-row-index',
				index: cardIndex
			}));
			tbody.appendChild(row);
		});

		initBolsasExcelScroll(sheet);
	}

	function initBolsasExcelScroll(sheet) {
		var main = sheet.querySelector('[data-bolsas-excel-scroll]');
		var hint = sheet.querySelector('[data-bolsas-scroll-hint]');

		if (!main) {
			return;
		}

		function updateScrollHint() {
			var needsScroll = main.scrollWidth > main.clientWidth + 2;

			if (hint) {
				hint.hidden = !needsScroll;
			}
		}

		if (!main.dataset.bolsasScrollBound) {
			main.dataset.bolsasScrollBound = '1';
			main.addEventListener('scroll', updateScrollHint);
			window.addEventListener('resize', updateScrollHint);
		}

		updateScrollHint();
	}

	function updateBolsasSheet(sheet, options) {
		var filters = bolsasFilterState(sheet);
		var sums = {
			kilos: 0,
			gramos: 0,
			cantidad_bolsas: 0,
			precio_total: 0
		};

		bolsasMainRows(sheet.querySelector('[data-bolsas-rows]')).forEach(function (row) {
			updateTapasStatusBadge(row);
			var kilos = Math.max(0, field(row, 'kilos'));
			var pesoPaquete10 = Math.max(0, field(row, 'peso_paquete_10'));
			var precioTotal = Math.max(0, field(row, 'precio_total'));
			var gramos = kilos * 1000;
			var pesoBolsa = pesoPaquete10 > 0 ? pesoPaquete10 / 10 : 0;
			var cantidadBolsas = pesoBolsa > 0 ? gramos / pesoBolsa : 0;
			var precioUnd = cantidadBolsas > 0 ? precioTotal / cantidadBolsas : 0;

			output(row, 'gramos', gramos, 0, true);
			output(row, 'peso_bolsa', pesoBolsa, 2);
			output(row, 'cantidad_bolsas', cantidadBolsas, 0, true);
			output(row, 'precio_und', precioUnd, 2);

			if (!bolsasCardMatchesFilter(row, filters)) {
				return;
			}

			sums.kilos += kilos;
			sums.gramos += gramos;
			sums.cantidad_bolsas += cantidadBolsas;
			sums.precio_total += precioTotal;
		});

		var summary = sheet.querySelector('[data-bolsas-summary]');
		var tableSummary = sheet.querySelector('[data-bolsas-table-summary]');

		if (summary) {
			output(summary, 'sum_kilos', sums.kilos, 2);
			output(summary, 'sum_gramos', sums.gramos, 0, true);
			output(summary, 'sum_cantidad_bolsas', sums.cantidad_bolsas, 0, true);
			output(summary, 'sum_precio_total', sums.precio_total, 0, true);
		}

		if (tableSummary) {
			output(tableSummary, 'sum_kilos', sums.kilos, 2);
			output(tableSummary, 'sum_gramos', sums.gramos, 0, true);
			output(tableSummary, 'sum_cantidad_bolsas', sums.cantidad_bolsas, 0, true);
			output(tableSummary, 'sum_precio_total', sums.precio_total, 0, true);
		}

		syncBolsasExcelTable(sheet);
		refreshBolsasFilterOptions(sheet);
		applyBolsasFilters(sheet, options);
		initMoneyFields(sheet);
	}

	function appendBolsasRecord(body) {
		var prototype = body ? body.querySelector('[data-bolsas-record]') : null;
		var card;

		if (!prototype) {
			return null;
		}

		card = prototype.cloneNode(true);
		body.appendChild(card);
		resetBolsasCard(card);

		return card;
	}

	function ensureBolsasRowCount(body, desiredRows, sheet) {
		while (body && bolsasRecordCount(body) < desiredRows) {
			addBolsasRow(sheet, true);
		}

		while (body && bolsasRecordCount(body) > desiredRows && bolsasRecordCount(body) > 1) {
			body.lastElementChild.remove();
		}
	}

	function addBolsasRow(sheet, silent) {
		var body = sheet.querySelector('[data-bolsas-rows]');
		var mainRow;

		if (!body) {
			return;
		}

		mainRow = appendBolsasRecord(body);
		updateBolsasSheet(sheet);

		if (!silent) {
			openBolsasCardsPanel(sheet, 'single', bolsasRecordCount(body) - 1, { pendingAdd: true });
		}
	}

	function removeBolsasRow(button) {
		var card = button.closest('[data-bolsas-record]');
		var sheet = button.closest('.logistica-sheet');
		var body = sheet ? sheet.querySelector('[data-bolsas-rows]') : null;
		var ui = sheet ? getBolsasCardsUi(sheet) : null;
		var wasSingle = ui.panel && ui.panel.dataset.bolsasView === 'single';
		var label;
		var index;
		var cards;

		if (!card && button.hasAttribute('data-bolsas-row-index')) {
			index = parseInt(button.getAttribute('data-bolsas-row-index'), 10);
			cards = body ? bolsasMainRows(body) : [];
			card = !Number.isNaN(index) ? cards[index] : null;
		}

		if (!card || !body) {
			return;
		}

		if (bolsasRecordCount(body) <= 1) {
			window.alert('Debe quedar al menos una referencia en la tabla.');
			return;
		}

		label = bolsasFieldText(card, 'descripcion').trim();
		if (!label) {
			label = 'Referencia ' + String(getBolsasCardIndex(card) + 1);
		}

		if (!confirmLogisticaDelete('¿Eliminar esta referencia de cálculo de bolsas?', label, true)) {
			return;
		}

		ensureConversionRowId(card);
		card.remove();

		if (sheet) {
			updateBolsasSheet(sheet);

			if (wasSingle) {
				closeBolsasCardsPanel(sheet);
			}
		}
	}

	function duplicateBolsasRow(button) {
		var card = button.closest('[data-bolsas-record]');
		var sheet = button.closest('.logistica-sheet');
		var body = sheet ? sheet.querySelector('[data-bolsas-rows]') : null;
		var data;
		var copy;
		var newIndex;
		var index;
		var cards;

		if (!card && button.hasAttribute('data-bolsas-row-index')) {
			index = parseInt(button.getAttribute('data-bolsas-row-index'), 10);
			cards = body ? bolsasMainRows(body) : [];
			card = !Number.isNaN(index) ? cards[index] : null;
		}

		if (!card || !sheet || !body) {
			return;
		}

		data = rowInputData(card);
		copy = card.cloneNode(true);
		body.appendChild(copy);
		applyBolsasRowData(copy, data);
		updateBolsasSheet(sheet);
		newIndex = getBolsasCardIndex(copy);
		openBolsasCardsPanel(sheet, 'single', newIndex);
	}

	function bolsasExportRows(sheet) {
		var filters = bolsasFilterState(sheet);

		return bolsasMainRows(sheet.querySelector('[data-bolsas-rows]')).filter(function (card) {
			return bolsasCardMatchesFilter(card, filters);
		}).map(function (card, index) {
			return [
				'',
				bolsasValidationExportLabel(card),
				tapasStatusLabel(card),
				String(index + 1),
				bolsasFieldText(card, 'descripcion'),
				bolsasFieldText(card, 'kilos'),
				bolsasOutputText(card, 'gramos'),
				bolsasFieldText(card, 'peso_paquete_10'),
				bolsasOutputText(card, 'peso_bolsa'),
				bolsasOutputText(card, 'cantidad_bolsas'),
				bolsasFieldText(card, 'precio_kg'),
				bolsasFieldText(card, 'precio_total'),
				bolsasOutputText(card, 'precio_und'),
				bolsasFieldText(card, 'codigo_graficos'),
				bolsasFieldText(card, 'ancho'),
				bolsasFieldText(card, 'largo'),
				bolsasFieldText(card, 'calibre')
			];
		});
	}

	function exportBolsasExcel(button) {
		var sheet = button.closest('.logistica-sheet[data-sheet="bolsas-calculo"]');
		var labels = [
			'Acción',
			'Validación',
			'Estado',
			'Id',
			'Descripción',
			'Kilos (kg)',
			'Gramos (gr)',
			'Peso paquete x10 (gr)',
			'Peso bolsa (und)',
			'Cantidad bolsas (und)',
			'Precio (kg)',
			'Precio total',
			'Precio (und)',
			'Código GRAFICOSTOS',
			'Ancho',
			'Largo',
			'Calibre'
		];
		var matrixRows;

		if (!sheet) {
			return;
		}

		updateBolsasSheet(sheet);
		matrixRows = [labels].concat(bolsasExportRows(sheet));
		logisticaExportMatrixXlsx(matrixRows, logisticaExportTimestampFilename('calculo-bolsas'), 'Cálculo bolsas');
	}

	function bolsasImportColumnMap(headers) {
		var map = {
			status: -1,
			descripcion: -1,
			kilos: -1,
			gramos: -1,
			peso_paquete_10: -1,
			peso_bolsa: -1,
			cantidad_bolsas: -1,
			precio_kg: -1,
			precio_total: -1,
			precio_und: -1,
			codigo_graficos: -1,
			ancho: -1,
			largo: -1,
			calibre: -1
		};

		headers.forEach(function (label, index) {
			var text = String(label || '').toLowerCase();

			if (/^validacion$/.test(text)) {
				return;
			}

			if (/^estado$/.test(text) || text.indexOf('estado') === 0) {
				map.status = index;
			} else if (/descripcion/.test(text)) {
				map.descripcion = index;
			} else if (/^kilos/.test(text)) {
				map.kilos = index;
			} else if (/gramos/.test(text)) {
				map.gramos = index;
			} else if (/peso paquete|paquete x10/.test(text)) {
				map.peso_paquete_10 = index;
			} else if (/peso bolsa/.test(text)) {
				map.peso_bolsa = index;
			} else if (/cantidad/.test(text)) {
				map.cantidad_bolsas = index;
			} else if (/precio \(kg\)|precio kg/.test(text)) {
				map.precio_kg = index;
			} else if (/precio total/.test(text)) {
				map.precio_total = index;
			} else if (/precio \(und\)|precio und/.test(text)) {
				map.precio_und = index;
			} else if (/graficostos|codigo/.test(text)) {
				map.codigo_graficos = index;
			} else if (/^ancho/.test(text)) {
				map.ancho = index;
			} else if (/^largo/.test(text)) {
				map.largo = index;
			} else if (/^calibre/.test(text)) {
				map.calibre = index;
			}
		});

		if (map.descripcion < 0) {
			map.descripcion = map.status >= 0 ? 4 : 3;
		}
		if (map.kilos < 0) {
			map.kilos = map.descripcion + 1;
		}
		if (map.peso_paquete_10 < 0) {
			map.peso_paquete_10 = map.kilos + 2;
		}
		if (map.cantidad_bolsas < 0) {
			map.cantidad_bolsas = map.peso_paquete_10 + 2;
		}
		if (map.precio_kg < 0) {
			map.precio_kg = map.cantidad_bolsas + 1;
		}
		if (map.precio_total < 0) {
			map.precio_total = map.precio_kg + 1;
		}
		if (map.codigo_graficos < 0) {
			map.codigo_graficos = map.precio_total + 2;
		}
		if (map.ancho < 0) {
			map.ancho = map.codigo_graficos + 1;
		}
		if (map.largo < 0) {
			map.largo = map.ancho + 1;
		}
		if (map.calibre < 0) {
			map.calibre = map.largo + 1;
		}

		return map;
	}

	function parseBolsasImportFromMatrix(matrixRows) {
		var rows = [];
		var headerCells = [];
		var columnMap;
		var dataStarted = false;

		matrixRows.forEach(function (row) {
			var cols = row.map(function (value) {
				return String(value == null ? '' : value).trim();
			});
			var statusInfo;
			var statusText;

			if (!cols.length || cols.every(function (value) {
				return value === '';
			})) {
				return;
			}

			if (!dataStarted && cols.some(function (cell) {
				return /descripcion|estado|kilos|editar/i.test(cell);
			})) {
				headerCells = cols;
				columnMap = bolsasImportColumnMap(headerCells);
				dataStarted = true;
				return;
			}

			if (!dataStarted || !columnMap) {
				return;
			}

			if (cols.length < 8) {
				return;
			}

			statusText = columnMap.status >= 0 ? (cols[columnMap.status] || '') : '';
			if (/^totales?/i.test(statusText) || /^totales?/i.test(cols[columnMap.descripcion] || '')) {
				return;
			}

			statusInfo = columnMap.status >= 0
				? normalizeTapasImportedStatus(statusText)
				: { status: 'fabricacion', status_other: '' };

			rows.push({
				status: statusInfo.status,
				status_other: statusInfo.status_other,
				descripcion: cols[columnMap.descripcion] || '',
				kilos: parseTapasImportNumber(cols[columnMap.kilos], 0),
				peso_paquete_10: parseTapasImportNumber(cols[columnMap.peso_paquete_10], 0),
				precio_kg: parseTapasImportNumber(cols[columnMap.precio_kg], 0),
				precio_total: parseTapasImportNumber(cols[columnMap.precio_total], 0),
				codigo_graficos: cols[columnMap.codigo_graficos] || '',
				ancho: parseTapasImportNumber(cols[columnMap.ancho], 0),
				largo: parseTapasImportNumber(cols[columnMap.largo], 0),
				calibre: parseTapasImportNumber(cols[columnMap.calibre], 0)
			});
		});

		return { rows: rows };
	}

	function parseBolsasImportFromDocument(doc) {
		var tables = Array.from(doc.querySelectorAll('table'));
		var dataTable = null;
		var rows = [];
		var headerCells = [];
		var columnMap;

		tables.forEach(function (table) {
			if (!dataTable && /editar|estado|descripcion|kilos/i.test(table.textContent || '')) {
				dataTable = table;
			}
		});

		if (!dataTable && tables.length) {
			dataTable = tables[tables.length - 1];
		}

		if (!dataTable) {
			return { rows: [] };
		}

		headerCells = Array.from(dataTable.querySelectorAll('tr')).map(function (tr) {
			return Array.from(tr.querySelectorAll('th,td')).map(function (cell) {
				return (cell.textContent || '').trim();
			});
		}).filter(function (cells) {
			return cells.some(function (cell) {
				return /descripcion|estado|kilos|editar/i.test(cell);
			});
		})[0] || [];

		columnMap = bolsasImportColumnMap(headerCells);

		Array.from(dataTable.querySelectorAll('tr')).slice(1).forEach(function (tr) {
			var cols = Array.from(tr.querySelectorAll('td,th')).map(function (cell) {
				return (cell.textContent || '').trim();
			});
			var statusInfo;
			var statusText;

			if (cols.length < 8) {
				return;
			}

			statusText = columnMap.status >= 0 ? (cols[columnMap.status] || '') : '';
			if (/^totales?/i.test(statusText) || /^totales?/i.test(cols[columnMap.descripcion] || '')) {
				return;
			}

			statusInfo = columnMap.status >= 0
				? normalizeTapasImportedStatus(statusText)
				: { status: 'fabricacion', status_other: '' };

			rows.push({
				status: statusInfo.status,
				status_other: statusInfo.status_other,
				descripcion: cols[columnMap.descripcion] || '',
				kilos: parseTapasImportNumber(cols[columnMap.kilos], 0),
				peso_paquete_10: parseTapasImportNumber(cols[columnMap.peso_paquete_10], 0),
				precio_kg: parseTapasImportNumber(cols[columnMap.precio_kg], 0),
				precio_total: parseTapasImportNumber(cols[columnMap.precio_total], 0),
				codigo_graficos: cols[columnMap.codigo_graficos] || '',
				ancho: parseTapasImportNumber(cols[columnMap.ancho], 0),
				largo: parseTapasImportNumber(cols[columnMap.largo], 0),
				calibre: parseTapasImportNumber(cols[columnMap.calibre], 0)
			});
		});

		return { rows: rows };
	}

	function applyBolsasImportData(sheet, imported) {
		var body = sheet.querySelector('[data-bolsas-rows]');
		var cards;

		if (!body || !imported || !Array.isArray(imported.rows) || !imported.rows.length) {
			window.alert('No se encontraron filas válidas para importar.');
			return;
		}

		ensureBolsasRowCount(body, imported.rows.length, sheet);

		cards = bolsasMainRows(body);
		cards.forEach(function (card, index) {
			var row = imported.rows[index];
			if (!row) {
				return;
			}
			applyBolsasRowData(card, row);
		});

		updateBolsasSheet(sheet);
		saveConversionState();
		closeBolsasCardsPanel(sheet);
	}

	function importBolsasExcelFromFile(input) {
		var file = input.files && input.files[0];
		var sheet = input.closest('.logistica-sheet[data-sheet="bolsas-calculo"]');
		var reader;

		if (!file || !sheet) {
			return;
		}

		reader = new FileReader();
		reader.onload = function (event) {
			var buffer = event.target ? event.target.result : null;
			var bytes;
			var text;
			var imported;
			var matrixRows;

			if (!(buffer instanceof ArrayBuffer)) {
				input.value = '';
				return;
			}

			bytes = new Uint8Array(buffer);
			if (bytes[0] === 0x50 && bytes[1] === 0x4b) {
				matrixRows = logisticaMatrixRowsFromXlsxBuffer(buffer);
				if (!matrixRows.length) {
					window.alert('No se pudo leer el archivo .xlsx. Usa el archivo exportado por esta vista o guarda como CSV.');
					input.value = '';
					return;
				}
				imported = parseBolsasImportFromMatrix(matrixRows);
			} else {
				text = new TextDecoder('utf-8').decode(bytes);
				if (text.indexOf('<') >= 0) {
					imported = parseBolsasImportFromDocument(new DOMParser().parseFromString(text, 'text/html'));
				} else {
					matrixRows = text.split(/\r?\n/).map(function (line) {
						return line.split(';').map(function (cell) {
							return cell.replace(/^"|"$/g, '').replace(/""/g, '"');
						});
					});
					imported = parseBolsasImportFromMatrix(matrixRows);
				}
			}

			applyBolsasImportData(sheet, imported);
			input.value = '';
		};
		reader.onerror = function () {
			window.alert('No se pudo leer el archivo Excel.');
			input.value = '';
		};
		reader.readAsArrayBuffer(file);
	}

	function updateAreaConversionSheet(sheet) {
		var rows = Array.from(sheet.querySelectorAll('tbody tr'));
		var baseMetrics = rows.length ? sheetMetrics(rows[0]) : null;
		var baseQuantityInput = rows.length ? rows[0].querySelector('[data-field="quantity"]') : null;
		var baseQuantity = numberValue(baseQuantityInput);
		var baseGsm = rows.length ? field(rows[0], 'gsm') : 0;
		var usedArea = 0;
		var summaryRow = sheet.querySelector('.logistica-cut-summary');
		var cutKgRaw = [];
		var totalCutKgRaw = 0;
		var baseKg = 0;

		rows.forEach(function (row, index) {
			var metrics = index === 0 ? sheetMetrics(row) : metricsWithGsm(row, baseGsm);
			var factor = metrics.area > 0 && baseMetrics && baseMetrics.area > 0 ? baseMetrics.area / metrics.area : 0;
			var quantity = index === 0 ? field(row, 'quantity') : (factor > 0 ? baseQuantity / factor : 0);
			var kgUsed = quantity * metrics.weight;
			var cuts = index === 0 ? 0 : index;
			var cutArea = index === 0 ? baseMetrics.area : metrics.area;

			if (index > 0) {
				var label = row.querySelector('[data-field="cut_label"]');
				if (label && !label.value.trim()) {
					label.value = 'Corte ' + index;
				}
			}

			if (index > 0) {
				usedArea += cutArea;
			}

			output(row, 'cuts_label', cuts, 0, true);
			output(row, 'height_m', metrics.heightM, 2);
			output(row, 'width_m', metrics.widthM, 3);
			output(row, 'area_m2', metrics.area, 3);
			output(row, 'gsm_kg', metrics.gsmKg, 3);
			output(row, 'base_gsm', baseGsm, 0, true);
			output(row, 'sheet_weight', metrics.weight, 3);
			output(row, 'factor', factor > 0 ? factor : Number.NaN, 3);
			output(row, 'quantity', quantity, 0, true);
			if (index === 0) {
				baseKg = kgUsed;
				output(row, 'kg_used', Math.ceil(kgUsed), 0, true);
			} else {
				cutKgRaw.push({ row: row, kg: kgUsed });
				totalCutKgRaw += kgUsed;
			}
			outputText(row, 'cut_status', index === 0 ? 'Tamaño inicial' : (cutArea > 0 ? 'Incluido' : '-'));
			row.classList.remove('has-cut-error', 'has-cut-ok');
		});

		if (baseKg > 0 && totalCutKgRaw > 0) {
			cutKgRaw.forEach(function (entry) {
				var adjusted = baseKg * (entry.kg / totalCutKgRaw);
				output(entry.row, 'kg_used', adjusted, 0, true);
			});
		} else {
			cutKgRaw.forEach(function (entry) {
				output(entry.row, 'kg_used', Math.ceil(entry.kg), 0, true);
			});
		}

		if (baseMetrics && summaryRow) {
			var tolerance = 0.0005;
			var remaining = baseMetrics.area - usedArea;
			var status = '';
			var remainingWidthCm = baseMetrics.heightM > 0 ? (Math.abs(remaining) / baseMetrics.heightM) * 100 : 0;
			var remainingSize = displayNumber(baseMetrics.heightM * 100, 1) + ' x ' + displayNumber(remainingWidthCm, 1) + ' cm';

			summaryRow.classList.remove('has-cut-error', 'has-cut-ok');

			if (Math.abs(remaining) <= tolerance) {
				status = 'OK: los cortes coinciden con el tamaño inicial';
				summaryRow.classList.add('has-cut-ok');
			} else if (remaining > 0) {
				status = 'Falta área por usar: ' + displayNumber(remaining, 3) + ' m2. Tamaño restante aprox: ' + remainingSize;
				summaryRow.classList.add('has-cut-error');
			} else {
				status = 'Excede el tamaño inicial: ' + displayNumber(Math.abs(remaining), 3) + ' m2. Exceso aprox: ' + remainingSize;
				summaryRow.classList.add('has-cut-error');
			}

			output(summaryRow, 'used_area', usedArea, 3);
			output(summaryRow, 'remaining_area', remaining, 3);
			outputText(summaryRow, 'cut_summary', status);
		}
	}

	function addPliegoCut(sheet, silent) {
		var body = sheet.querySelector('[data-pliego-cuts]');
		var row;

		if (!body) {
			return;
		}

		row = document.createElement('tr');
		row.innerHTML = [
			'<th class="is-editable"><input type="text" data-field="cut_label" value=""></th>',
			'<td data-output="cuts_label"></td>',
			'<td class="is-editable"><input type="number" step="0.01" data-field="height_cm" value="0"></td>',
			'<td class="is-editable"><input type="number" step="0.01" data-field="width_cm" value="0"></td>',
			'<td><span data-output="base_gsm"></span></td>',
			'<td data-output="height_m"></td>',
			'<td data-output="width_m"></td>',
			'<td data-output="area_m2"></td>',
			'<td data-output="gsm_kg"></td>',
			'<td data-output="sheet_weight"></td>',
			'<td data-output="factor"></td>',
			'<td class="is-result"><span data-output="quantity"></span></td>',
			'<td class="is-result" data-output="kg_used"></td>',
			'<td data-output="cut_status"></td>',
			conversionActionCell('duplicate-pliego-cut', 'remove-pliego-cut', 'Quitar corte')
		].join('');
		body.appendChild(row);
		updateAreaConversionSheet(sheet);
		if (!silent) {
			focusGridField(row.querySelector('input'));
		}
	}

	function nextPliegoCutLabel(body) {
		var rows = body ? Array.from(body.querySelectorAll('tr')) : [];
		var highest = 0;

		rows.forEach(function (row, index) {
			var input;
			var match;
			var number;

			if (index <= 0) {
				return;
			}

			input = row.querySelector('[data-field="cut_label"]');
			match = String(input ? input.value : '').match(/corte\s*(\d+)/i);
			number = match ? Number.parseInt(match[1], 10) : index;
			highest = Math.max(highest, number);
		});

		return 'Corte ' + String(highest + 1);
	}

	function duplicatePliegoCut(button) {
		var row = button.closest('tr');
		var sheet = button.closest('.logistica-sheet');
		var body = row ? row.closest('[data-pliego-cuts]') : null;
		var rows = body ? Array.from(body.querySelectorAll('tr')) : [];
		var data;
		var newRow;
		var labelInput;

		if (!row || !sheet || !body || rows.indexOf(row) <= 0) {
			return;
		}

		data = rowInputData(row);
		addPliegoCut(sheet, true);
		newRow = body.querySelectorAll('tr')[body.querySelectorAll('tr').length - 1];
		applyRowInputData(newRow, data);
		labelInput = newRow ? newRow.querySelector('[data-field="cut_label"]') : null;

		if (labelInput) {
			labelInput.value = nextPliegoCutLabel(body);
		}

		updateAreaConversionSheet(sheet);
		focusGridField(newRow ? newRow.querySelector('input') : null);
	}

	function removePliegoCut(button) {
		var row = button.closest('tr');
		var sheet = button.closest('.logistica-sheet');
		var body = row ? row.closest('[data-pliego-cuts]') : null;
		var rows = body ? Array.from(body.querySelectorAll('tr')) : [];
		var labelInput;
		var label;

		if (!row || rows.indexOf(row) <= 0) {
			return;
		}

		labelInput = row.querySelector('[data-field="cut_label"]');
		label = labelInput && labelInput.value.trim() ? labelInput.value.trim() : 'Corte ' + String(rows.indexOf(row));

		if (!confirmLogisticaDelete('¿Eliminar este corte del pliego?', label)) {
			return;
		}

		row.remove();

		if (sheet) {
			updateAreaConversionSheet(sheet);
		}
	}

	function updateSharedMountSheet(sheet) {
		var baseRow = sheet.querySelector('[data-mount-base]');
		var refRows = Array.from(sheet.querySelectorAll('[data-shared-refs] tr:not([data-blank-ref])'));
		var blankRow = sheet.querySelector('[data-blank-ref]');
		var metrics = baseRow ? sheetMetrics(baseRow) : { weight: 0 };
		var totalCavities = 0;
		var targetCavities = baseRow ? field(baseRow, 'target_cavities') : 0;
		var totalReferenceSheets = 0;
		var usedReferences = 0;
		var mountSheets = 0;
		var blankCavities = 0;
		var cavitiesStatus = '';
		var shortageCount = 0;
		var surplusCount = 0;

		refRows.forEach(function (row) {
			var cavities = field(row, 'cavities');
			var units = field(row, 'units');
			var sheetsRef = cavities > 0 ? Math.ceil(units / cavities) : 0;

			totalCavities += cavities;
			totalReferenceSheets += sheetsRef;
			if (cavities > 0 && units > 0) {
				usedReferences += 1;
			}
			output(row, 'sheets_ref', sheetsRef, 0, true);
		});

		mountSheets = usedReferences > 0 ? Math.ceil(totalReferenceSheets / usedReferences) : 0;
		blankCavities = Math.max(0, targetCavities - totalCavities);

		refRows.forEach(function (row) {
			var cavities = field(row, 'cavities');
			var units = field(row, 'units');
			var sheetsRef = cavities > 0 ? Math.ceil(units / cavities) : 0;
			var distributedSize = sheetsRef;
			var possibleUnits = mountSheets * cavities;
			var difference = possibleUnits - units;
			var status = '';

			row.classList.remove('has-ref-shortage', 'has-ref-surplus', 'has-ref-ok');

			if (cavities > 0 && units > 0) {
				if (difference < 0) {
					status = 'Faltan ' + Math.abs(difference) + ' und / ' + Math.max(0, sheetsRef - mountSheets) + ' pliegos';
					shortageCount += 1;
					row.classList.add('has-ref-shortage');
				} else if (difference > 0) {
					// Sobrante menor que una cabida: resto normal del último pliego (ceil).
					if (difference < cavities) {
						status = 'OK';
						row.classList.add('has-ref-ok');
					} else {
						status = 'Sobran ' + difference + ' und / ' + Math.max(0, mountSheets - sheetsRef) + ' pliegos';
						surplusCount += 1;
						row.classList.add('has-ref-surplus');
					}
				} else {
					status = 'OK';
					row.classList.add('has-ref-ok');
				}
			} else {
				status = '-';
			}

			output(row, 'distributed_size', distributedSize, 0, true);
			output(row, 'possible_units', possibleUnits, 0, true);
			outputText(row, 'ref_status', status);
		});

		if (blankRow) {
			output(blankRow, 'blank_cavities', blankCavities, 0, true);
			output(blankRow, 'blank_distributed_size', blankCavities > 0 ? mountSheets : 0, 0, true);
			output(blankRow, 'blank_possible_units', mountSheets * blankCavities, 0, true);
			outputText(blankRow, 'blank_status', blankCavities > 0 ? 'Sobrante' : '-');
		}

		if (baseRow) {
			if (targetCavities === totalCavities) {
				cavitiesStatus = 'OK';
				baseRow.classList.remove('has-cavity-error');
			} else if (targetCavities > totalCavities) {
				cavitiesStatus = 'Faltan ' + Math.round(targetCavities - totalCavities);
				baseRow.classList.add('has-cavity-error');
			} else {
				cavitiesStatus = 'Sobran ' + Math.round(totalCavities - targetCavities);
				baseRow.classList.add('has-cavity-error');
			}

			output(baseRow, 'sheet_weight', metrics.weight, 3);
			outputText(baseRow, 'cavities_status', cavitiesStatus);
			if (shortageCount > 0) {
				outputText(baseRow, 'distribution_status', 'Faltan unidades en ' + shortageCount + ' ref.');
				baseRow.classList.add('has-cavity-error');
			} else if (surplusCount > 0) {
				outputText(baseRow, 'distribution_status', 'Sobran unidades en ' + surplusCount + ' ref.');
				baseRow.classList.add('has-cavity-error');
			} else {
				outputText(baseRow, 'distribution_status', usedReferences > 0 ? 'OK' : '-');
			}
			output(baseRow, 'required_sheets', mountSheets, 0, true);
			output(baseRow, 'kg_used', Math.ceil(mountSheets * metrics.weight), 0, true);
		}
	}

	function normalizeText(value) {
		return String(value || '')
			.trim()
			.toLowerCase()
			.normalize('NFD')
			.replace(/[\u0300-\u036f]/g, '');
	}

	function baseRows(sheet, baseName) {
		return Array.from(sheet.querySelectorAll('[data-close-base="' + baseName + '"] tbody tr'));
	}

	function findCloseBase(sheet, baseName, size) {
		var normalizedSize = normalizeText(size);
		var rows = baseRows(sheet, baseName);
		var match = rows.find(function (row) {
			return normalizeText(row.querySelector('[data-field="size"]') ? row.querySelector('[data-field="size"]').value : '') === normalizedSize;
		});

		return match || rows[0] || null;
	}

	function closeBaseValue(row, fieldName) {
		var input = row ? row.querySelector('[data-field="' + fieldName + '"]') : null;

		return input ? input.value : '';
	}

	function outputCloseLine(row, cod, lote, unitkg, adjusted) {
		var consumo = unitkg > 0 ? adjusted : 0;

		outputText(row, 'cod', cod || '');
		outputText(row, 'lote', lote || '');
		output(row, 'unitkg', consumo, 0, true);
		output(row, 'adjusted', adjusted, 0, true);
	}

	function textField(scope, name) {
		var element = scope ? scope.querySelector('[data-field="' + name + '"]') : null;

		return element ? String(element.value || '').trim() : '';
	}

	function setTextField(scope, name, value) {
		var element = scope ? scope.querySelector('[data-field="' + name + '"]') : null;

		if (element) {
			element.value = value == null ? '' : String(value);
		}
	}

	function newClosingLinkId(prefix) {
		return (prefix || 'link') + '-' + Date.now();
	}

	function closingBlockLinkId(row) {
		return normalizeText(textField(row, 'base_link_id'));
	}

	function closingBaseLinkId(baseCard) {
		return normalizeText(textField(baseCard, 'id'));
	}

	function baseTypeLabel(baseType) {
		var labels = window.LogisticaAdmin && window.LogisticaAdmin.closingBaseLabels;

		return (labels && labels[baseType]) || baseType;
	}

	function lineTypeForBaseType(baseType) {
		var known = {
			cajas: 'cajas',
			cajas_saldo: 'caja_saldos',
			bolsas: 'bolsas',
			rollos: 'rollos',
			plastico: 'plastico_kg',
			acetato: 'acetato',
			papel_hojas: 'papel_hojas',
			papel_bobinas: 'rollos',
			microcorrugado: 'microcorrugado'
		};

		return known[baseType] || 'nuevo_bloque';
	}

	var CLOSING_LINE_SORT_RANK = {
		cajas: 10,
		caja_saldos: 20,
		caja_mediana: 25,
		bolsas: 30,
		rollos: 40,
		plastico_kg: 50,
		acetato: 55,
		papel_hojas: 60,
		microcorrugado: 70,
		nuevo_bloque: 500
	};

	var CLOSING_BASE_TYPE_SORT_RANK = {
		cajas: 10,
		cajas_saldo: 15,
		bolsas: 30,
		rollos: 40,
		plastico: 50,
		acetato: 55,
		papel_hojas: 60,
		papel_bobinas: 65,
		microcorrugado: 70
	};

	function closingLineSortRank(row) {
		var lineAttr;
		var lineTipo;
		var baseTipo;
		var mapped;

		if (isCajaSaldoRow(row)) {
			return CLOSING_LINE_SORT_RANK.caja_saldos;
		}

		lineAttr = normalizeText(row.getAttribute('data-close-line') || '');
		lineTipo = normalizeText(textField(row, 'tipo') || '');
		baseTipo = normalizeText(textField(row, 'base_tipo') || '');

		if (lineAttr && CLOSING_LINE_SORT_RANK[lineAttr] != null) {
			return CLOSING_LINE_SORT_RANK[lineAttr];
		}

		if (lineTipo && CLOSING_LINE_SORT_RANK[lineTipo] != null) {
			return CLOSING_LINE_SORT_RANK[lineTipo];
		}

		if (baseTipo && CLOSING_BASE_TYPE_SORT_RANK[baseTipo] != null) {
			return CLOSING_BASE_TYPE_SORT_RANK[baseTipo];
		}

		mapped = lineTypeForBaseType(baseTipo);

		if (mapped && CLOSING_LINE_SORT_RANK[mapped] != null) {
			return CLOSING_LINE_SORT_RANK[mapped];
		}

		return 900;
	}

	function closingLineSortLabel(row) {
		return normalizeText(textField(row, 'label') || textField(row, 'tipo') || '');
	}

	function sortClosingReferenceBlocks(reference) {
		var body;
		var rows;

		if (!reference) {
			return;
		}

		body = reference.querySelector('[data-close-lines]');

		if (!body) {
			return;
		}

		rows = Array.from(body.querySelectorAll('tr[data-close-line]'));

		if (rows.length < 2) {
			return;
		}

		rows.sort(function (a, b) {
			var rankDiff = closingLineSortRank(a) - closingLineSortRank(b);

			if (rankDiff !== 0) {
				return rankDiff;
			}

			return closingLineSortLabel(a).localeCompare(closingLineSortLabel(b), 'es', {
				numeric: true,
				sensitivity: 'base'
			});
		});

		rows.forEach(function (row) {
			body.appendChild(row);
		});
	}

	function defaultFormulaForLineType(lineType) {
		var formulas = {
			cajas: 'paquetes * unidades_paquete',
			caja_saldos: 'saldos / cajas',
			bolsas: 'unidades / cantidad',
			rollos: 'unidades * consumo unidad',
			plastico_kg: 'plastico_peso_molde',
			acetato: 'acetato_laminas',
			papel_hojas: '',
			microcorrugado: ''
		};

		return formulas[lineType] || '';
	}

	function defaultFormulaForBaseType(baseType, row) {
		var baseFormulas = {
			cajas: 'paquetes * unidades_paquete',
			cajas_saldo: 'saldos / cajas',
			bolsas: 'unidades / cantidad',
			rollos: 'unidades * consumo unidad',
			papel_bobinas: 'unidades * consumo unidad',
			plastico: 'plastico_peso_molde',
			acetato: 'acetato_laminas',
			papel_hojas: '',
			microcorrugado: ''
		};

		if (isCajaSaldoRow(row)) {
			return 'saldos / cajas';
		}

		if (Object.prototype.hasOwnProperty.call(baseFormulas, baseType)) {
			return baseFormulas[baseType];
		}

		return defaultFormulaForLineType(lineTypeForBaseType(baseType));
	}

	function formulaLabelForValue(formula) {
		var labels = window.LogisticaAdmin && window.LogisticaAdmin.closingFormulaLabels;

		if (labels && Object.prototype.hasOwnProperty.call(labels, formula)) {
			return labels[formula];
		}

		return formula || 'Sin fórmula';
	}

	function upgradeRowToAutoFormulaMarkup(row, formula, label) {
		var formulaSelect = row.querySelector('select[data-field="formula"]');
		var formulaHidden = row.querySelector('input[type="hidden"][data-field="formula"]');
		var formulaCell = formulaSelect
			? formulaSelect.closest('td')
			: (formulaHidden ? formulaHidden.closest('td') : null);
		var formulaName = '';
		var span;

		if (!formulaCell) {
			return;
		}

		if (formulaSelect) {
			formulaName = formulaSelect.getAttribute('name') || '';
		} else if (formulaHidden) {
			formulaName = formulaHidden.getAttribute('name') || '';
		}

		formulaCell.className = 'is-auto';
		span = formulaCell.querySelector('.logistica-formula-auto');
		formulaHidden = formulaHidden || formulaCell.querySelector('input[type="hidden"][data-field="formula"]');

		if (span && formulaHidden && formulaHidden.value === (formula || '') && span.textContent === label) {
			return;
		}

		if (!span) {
			formulaCell.innerHTML = '<span class="logistica-formula-auto"></span>'
				+ '<input type="hidden" data-field="formula" value="' + String(formula || '').replace(/"/g, '&quot;') + '"'
				+ (formulaName ? ' name="' + formulaName.replace(/"/g, '&quot;') + '"' : '')
				+ '>';
			span = formulaCell.querySelector('.logistica-formula-auto');
			formulaHidden = formulaCell.querySelector('input[type="hidden"][data-field="formula"]');
		}

		if (span) {
			span.textContent = label;
		}

		if (formulaHidden) {
			formulaHidden.value = formula || '';
		}
	}

	function applyAutoFormulaToRow(row) {
		var baseTipo;
		var formula;
		var label;
		var lineType;
		var estadoField = row.querySelector('[data-field="estado_formula"]');

		if (!row) {
			return;
		}

		if (!isCajaSaldoRow(row) && !normalizeText(textField(row, 'base_tipo'))) {
			row.setAttribute('data-close-line', 'nuevo_bloque');
			setTextField(row, 'tipo', 'nuevo_bloque');
			upgradeRowToAutoFormulaMarkup(row, '', formulaLabelForValue(''));

			if (estadoField) {
				estadoField.value = 'pendiente';
			}

			return;
		}

		baseTipo = textField(row, 'base_tipo') || 'cajas';
		formula = defaultFormulaForBaseType(baseTipo, row);
		label = formulaLabelForValue(formula);

		if (isCajaSaldoRow(row)) {
			setTextField(row, 'tipo', 'caja_saldos');
			row.setAttribute('data-close-line', 'caja_saldos');
			upgradeRowToCajaSaldoMarkup(row);
			formula = 'saldos / cajas';
			label = formulaLabelForValue(formula);
		} else {
			lineType = lineTypeForBaseType(baseTipo);
			row.setAttribute('data-close-line', lineType);
			setTextField(row, 'tipo', lineType);
		}

		upgradeRowToAutoFormulaMarkup(row, formula, label);

		if (estadoField) {
			estadoField.value = formula ? 'formulada' : 'pendiente';
		}
	}

	function isCajaSaldoRow(row) {
		var baseTipo;
		var lineAttr;
		var tipo;

		if (!row) {
			return false;
		}

		baseTipo = normalizeText(textField(row, 'base_tipo'));
		lineAttr = row.getAttribute('data-close-line');
		tipo = normalizeText(textField(row, 'tipo'));

		if (baseTipo && baseTipo !== 'cajas_saldo') {
			return false;
		}

		return baseTipo === 'cajas_saldo' || lineAttr === 'caja_saldos' || tipo === 'caja_saldos';
	}

	function normalizeCajaSaldoRow(row) {
		if (!isCajaSaldoRow(row)) {
			return;
		}

		setTextField(row, 'tipo', 'caja_saldos');
		setTextField(row, 'label', 'Cajas saldo');
		row.setAttribute('data-close-line', 'caja_saldos');
		ensureCajaSaldoRowFormula(row);
		upgradeRowToCajaSaldoMarkup(row);
	}

	function referenceCajaSaldoRow(reference) {
		if (!reference) {
			return null;
		}

		return closingReferenceLineRows(reference, { visibleOnly: false }).find(isCajaSaldoRow) || null;
	}

	function findCajaSaldoTemplateRow(workspace, reference) {
		var saldoRow = reference ? reference.querySelector('[data-close-line="caja_saldos"]') : null;

		if (saldoRow && isCajaSaldoRow(saldoRow)) {
			return saldoRow;
		}

		if (workspace) {
			saldoRow = Array.from(workspace.querySelectorAll('[data-close-line="caja_saldos"]')).find(isCajaSaldoRow);

			if (saldoRow) {
				return saldoRow;
			}
		}

		return referenceCajaSaldoRow(reference);
	}

	function downgradeCajaSaldoMarkup(row) {
		var paquetesInput = row ? row.querySelector('[data-field="paquetes"]') : null;
		var undInput = row ? row.querySelector('[data-field="unidades_paquete"]') : null;
		var flagField = row ? row.querySelector('[data-field="saldo_und_manual"]') : null;

		if (!row) {
			return;
		}

		if (paquetesInput) {
			paquetesInput.removeAttribute('readonly');
			paquetesInput.removeAttribute('tabindex');
			paquetesInput.removeAttribute('data-saldo-auto');
			paquetesInput.className = '';
			paquetesInput.closest('td').className = 'is-editable';
		}

		if (undInput) {
			undInput.removeAttribute('data-saldo-input');
			undInput.removeAttribute('data-saldo-und-manual');
			undInput.closest('td').className = 'is-editable';
		}

		if (flagField) {
			flagField.remove();
		}
	}

	function upgradeRowToCajaSaldoMarkup(row) {
		var paquetesInput;
		var undInput;
		var flagField;
		var formulaSelect;
		var formulaHidden;
		var formulaCell;
		var formulaName;

		if (!row || !isCajaSaldoRow(row)) {
			return;
		}

		paquetesInput = row.querySelector('[data-field="paquetes"]');

		if (paquetesInput) {
			paquetesInput.type = 'number';
			paquetesInput.step = '0.001';
			paquetesInput.className = 'logistica-readonly-field';
			paquetesInput.readOnly = true;
			paquetesInput.tabIndex = -1;
			paquetesInput.setAttribute('data-saldo-auto', 'paquetes');
			paquetesInput.closest('td').className = 'is-auto';
		}

		undInput = row.querySelector('[data-field="unidades_paquete"]');

		if (undInput) {
			undInput.removeAttribute('readonly');
			undInput.setAttribute('data-saldo-input', 'und');
			undInput.closest('td').className = 'is-editable';

			if (!row.querySelector('[data-field="saldo_und_manual"]')) {
				flagField = document.createElement('input');
				flagField.type = 'hidden';
				flagField.setAttribute('data-field', 'saldo_und_manual');
				flagField.value = undInput.getAttribute('data-saldo-und-manual') === '1' ? '1' : '';
				undInput.parentNode.appendChild(flagField);
			}
		}
	}

	function ensureCajaSaldoRow(workspace, reference) {
		var body;
		var template;
		var clone;
		var existing = referenceCajaSaldoRow(reference);

		if (!reference) {
			return null;
		}

		if (existing) {
			normalizeCajaSaldoRow(existing);
			setTextField(existing, 'base_tipo', 'cajas_saldo');

			if (!closingBlockLinkId(existing)) {
				ensureClosingPairForRow(workspace, existing, reference);
			}

			return existing;
		}

		body = reference.querySelector('[data-close-lines]');
		template = findCajaSaldoTemplateRow(workspace, reference)
			|| (body ? body.querySelector('[data-close-line]') : null)
			|| getClosingLineTemplateRow(workspace);

		if (!body || !template) {
			return null;
		}

		clone = template.cloneNode(true);
		ensureRowLinkField(clone);
		applyClosingRowFields(clone, {
			baseTipo: 'cajas_saldo',
			lineType: 'caja_saldos',
			label: 'Cajas saldo',
			linkId: '',
			resetMetrics: true
		});

		if (template.getAttribute('data-close-line') !== 'caja_saldos') {
			upgradeRowToCajaSaldoMarkup(clone);
		}

		body.appendChild(clone);
		ensureClosingPairForRow(workspace, clone, reference);
		sortClosingReferenceBlocks(reference);

		var packSource = resolveClosingSharedPackSource(reference, closingMainCajasRow(reference));
		if (packSource) {
			syncClosingSharedUnitsPerPackage(reference, packSource);
		}

		return clone;
	}

	function getClosingLineTemplateRow(workspace) {
		var template = workspace ? workspace.querySelector('#logistica-close-line-template') : null;

		if (!template || !template.content) {
			return null;
		}

		return template.content.querySelector('[data-close-line]');
	}

	function findClosingBlockRowInWorkspace(workspace, reference, lineType) {
		var references = workspace ? Array.from(workspace.querySelectorAll('[data-close-reference]')) : [];
		var refIndex;
		var ref;
		var body;
		var match;

		for (refIndex = 0; refIndex < references.length; refIndex += 1) {
			ref = references[refIndex];

			if (ref === reference) {
				continue;
			}

			body = ref.querySelector('[data-close-lines]');

			if (!body) {
				continue;
			}

			if (lineType === 'caja_saldos') {
				match = findCajaSaldoTemplateRow(workspace, ref) || body.querySelector('[data-close-line]');
			} else {
				match = Array.from(body.querySelectorAll('[data-close-line]')).find(function (row) {
					return textField(row, 'tipo') === lineType || row.getAttribute('data-close-line') === lineType;
				});
				match = match || body.querySelector('[data-close-line]');
			}

			if (match) {
				return match;
			}
		}

		return null;
	}

	function clearClosingReferenceLines(reference) {
		var body = reference ? reference.querySelector('[data-close-lines]') : null;

		if (!body) {
			return;
		}

		body.querySelectorAll('[data-close-line]').forEach(function (row) {
			row.remove();
		});
	}

	function findClosingBlockCloneSource(workspace, reference, lineType) {
		var body = reference ? reference.querySelector('[data-close-lines]') : null;
		var match;

		if (body) {
			if (lineType === 'caja_saldos') {
				match = findCajaSaldoTemplateRow(workspace, reference) || body.querySelector('[data-close-line]');
			} else {
				match = Array.from(body.querySelectorAll('[data-close-line]')).find(function (row) {
					return textField(row, 'tipo') === lineType || row.getAttribute('data-close-line') === lineType;
				});
				match = match || body.querySelector('[data-close-line]');
			}

			if (match) {
				return match;
			}
		}

		match = findClosingBlockRowInWorkspace(workspace, reference, lineType);

		if (match) {
			return match;
		}

		return getClosingLineTemplateRow(workspace);
	}

	function closingMainCajasRow(reference) {
		var cajasRow = null;

		if (!reference) {
			return null;
		}

		closingReferenceLineRows(reference, { visibleOnly: true }).forEach(function (row) {
			if (isCajaSaldoRow(row)) {
				return;
			}

			if (textField(row, 'tipo') === 'cajas' || row.getAttribute('data-close-line') === 'cajas') {
				cajasRow = row;
			}
		});

		return cajasRow;
	}

	function isClosingSharedPackRow(row) {
		var tipo;
		var baseTipo;

		if (!row) {
			return false;
		}

		if (isCajaSaldoRow(row)) {
			return true;
		}

		tipo = normalizeText(textField(row, 'tipo') || row.getAttribute('data-close-line') || '');
		baseTipo = normalizeText(textField(row, 'base_tipo'));

		return tipo === 'cajas' || tipo === 'bolsas' || baseTipo === 'cajas' || baseTipo === 'bolsas';
	}

	function syncClosingSharedUnitsPerPackage(reference, sourceRow) {
		var sourceInput;
		var value;
		var changed = false;

		if (!reference || !sourceRow || !isClosingSharedPackRow(sourceRow)) {
			return false;
		}

		sourceInput = sourceRow.querySelector('[data-field="unidades_paquete"]');
		if (!sourceInput) {
			return false;
		}

		value = String(sourceInput.value || '');

		reference.querySelectorAll('[data-close-line]').forEach(function (row) {
			var input;

			if (row === sourceRow || !isClosingSharedPackRow(row)) {
				return;
			}

			input = row.querySelector('[data-field="unidades_paquete"]');
			if (!input || String(input.value || '') === value) {
				return;
			}

			input.value = value;
			changed = true;

			if (isCajaSaldoRow(row) && typeof setSaldoUndManual === 'function') {
				setSaldoUndManual(input, false);
			}
		});

		return changed;
	}

	function resolveClosingSharedPackSource(reference, preferredRow) {
		var source = preferredRow && isClosingSharedPackRow(preferredRow) ? preferredRow : null;
		var cajasRow;
		var candidates;
		var index;
		var row;
		var value;

		if (source && String(textField(source, 'unidades_paquete') || '').trim() !== '') {
			return source;
		}

		cajasRow = closingMainCajasRow(reference);
		if (cajasRow && String(textField(cajasRow, 'unidades_paquete') || '').trim() !== '') {
			return cajasRow;
		}

		candidates = reference ? Array.from(reference.querySelectorAll('[data-close-line]')) : [];
		for (index = 0; index < candidates.length; index += 1) {
			row = candidates[index];
			if (!isClosingSharedPackRow(row)) {
				continue;
			}
			value = String(textField(row, 'unidades_paquete') || '').trim();
			if (value !== '') {
				return row;
			}
		}

		return source || cajasRow || null;
	}

	function closingCajasEmpaqueSize(cajasRow) {
		var packages;
		var unitsPerPackage;

		if (!cajasRow) {
			return 0;
		}

		packages = closeNumber(cajasRow, 'paquetes');
		unitsPerPackage = closeNumber(cajasRow, 'unidades_paquete');

		if (packages > 0 && unitsPerPackage > 0) {
			return packages * unitsPerPackage;
		}

		if (unitsPerPackage > 0) {
			return unitsPerPackage;
		}

		return closeNumber(cajasRow, 'cantidad');
	}

	function closingCajaSaldoQty(produced, reference) {
		var empaqueSize = closingCajasEmpaqueSize(closingMainCajasRow(reference));
		var empaqueMatch;
		var fallbackSize;

		if (!produced) {
			return 0;
		}

		if (empaqueSize > 0) {
			return produced - empaqueSize * Math.floor(produced / empaqueSize);
		}

		empaqueMatch = normalizeText(textField(reference, 'unidad_empaque')).match(/(\d+(?:\.\d+)?)/);
		fallbackSize = empaqueMatch ? parseFloat(empaqueMatch[1]) || 0 : 0;

		if (fallbackSize > 0) {
			return produced - fallbackSize * Math.floor(produced / fallbackSize);
		}

		return 0;
	}

	function isSaldoUndManual(row) {
		var input = row ? row.querySelector('[data-field="unidades_paquete"][data-saldo-input="und"]') : null;

		return !!(input && input.getAttribute('data-saldo-und-manual') === '1');
	}

	function setSaldoUndManual(input, manual) {
		var row;
		var flagField;

		if (!input) {
			return;
		}

		if (manual) {
			input.setAttribute('data-saldo-und-manual', '1');
		} else {
			input.removeAttribute('data-saldo-und-manual');
		}

		row = input.closest('[data-close-line]');
		flagField = row ? row.querySelector('[data-field="saldo_und_manual"]') : null;

		if (flagField) {
			flagField.value = manual ? '1' : '';
		}
	}

	/**
	 * El flag data-saldo-und-manual solo vive en el DOM de la sesión.
	 * Tras recargar, hay que restaurarlo desde el valor guardado o desde saldo_und_manual
	 * para que applyCajaSaldoRowMetrics no pise Und x paquete.
	 */
	function restoreClosingSaldoUndManualFlags(workspace) {
		if (!workspace) {
			return;
		}

		workspace.querySelectorAll('[data-close-line="caja_saldos"]').forEach(function (row) {
			var undInput = row.querySelector('[data-field="unidades_paquete"][data-saldo-input="und"]')
				|| row.querySelector('[data-field="unidades_paquete"]');
			var savedFlag = row.querySelector('[data-field="saldo_und_manual"]');
			var undValue;

			if (!undInput) {
				return;
			}

			undValue = String(undInput.value || '').trim();

			if (
				undInput.getAttribute('data-saldo-und-manual') === '1' ||
				(savedFlag && String(savedFlag.value || '').trim() === '1') ||
				undValue !== ''
			) {
				setSaldoUndManual(undInput, true);
			}
		});
	}

	function closingSaldoUnitsPerPackage(row, reference) {
		var cajasRow = closingMainCajasRow(reference);

		if (isSaldoUndManual(row)) {
			return closeNumber(row, 'unidades_paquete');
		}

		return cajasRow ? closeNumber(cajasRow, 'unidades_paquete') : 0;
	}

	function closingSaldoPackages(saldoUnits, unitsPerPackage, empaqueSize) {
		var packages;

		if (!saldoUnits || saldoUnits <= 0) {
			return 0;
		}

		if (empaqueSize > 0 && saldoUnits < empaqueSize) {
			return 1;
		}

		if (!unitsPerPackage || unitsPerPackage <= 0) {
			return saldoUnits > 0 ? 1 : 0;
		}

		packages = saldoUnits / unitsPerPackage;

		if (Math.abs(packages - Math.round(packages)) < 0.0001) {
			return Math.round(packages);
		}

		return packages;
	}

	function closingCajasEnterasConsumidas(produced, empaqueSize) {
		if (!produced || !empaqueSize || empaqueSize <= 0) {
			return 0;
		}

		return Math.floor(produced / empaqueSize);
	}

	function applyCajaSaldoRowMetrics(row, reference, saldoUnits) {
		var unitsPerPackage;
		var packages;
		var paquetesField;
		var empaqueSize = closingCajasEmpaqueSize(closingMainCajasRow(reference));

		if (!isCajaSaldoRow(row)) {
			return saldoUnits;
		}

		saldoUnits = Math.max(0, saldoUnits);

		if (!isSaldoUndManual(row)) {
			unitsPerPackage = closingSaldoUnitsPerPackage(row, reference);

			if (saldoUnits > 0 && empaqueSize > 0 && saldoUnits < empaqueSize) {
				packages = 1;
			} else {
				packages = closingSaldoPackages(saldoUnits, unitsPerPackage, empaqueSize);
			}

			setTextField(row, 'unidades_paquete', unitsPerPackage > 0 ? String(unitsPerPackage) : '');
		} else {
			unitsPerPackage = closingSaldoUnitsPerPackage(row, reference);
			packages = closingSaldoPackages(saldoUnits, unitsPerPackage, empaqueSize);
		}

		setTextField(row, 'paquetes', packages > 0 ? String(packages) : '');
		paquetesField = row.querySelector('[data-field="paquetes"]');

		if (paquetesField) {
			paquetesField.value = packages > 0 ? String(packages) : '';
		}

		return saldoUnits;
	}

	function ensureCajaSaldoRowFormula(row) {
		if (!isCajaSaldoRow(row)) {
			return;
		}

		applyAutoFormulaToRow(row);
	}

	function defaultLabelForLineType(lineType, baseTipo) {
		var labels = {
			cajas: 'Cajas',
			caja_saldos: 'Cajas saldo',
			bolsas: 'Bolsas',
			rollos: 'Rollo fondo',
			plastico_kg: 'Plástico en kg',
			acetato: 'Acetato (láminas)',
			papel_hojas: 'Papel en hojas',
			microcorrugado: 'Microcorrugado',
			nuevo_bloque: 'Nuevo bloque'
		};

		return labels[lineType] || baseTypeLabel(baseTipo) || 'Nuevo bloque';
	}

	function retargetClosingBlockBase(workspace, row, reference) {
		var baseTipo = textField(row, 'base_tipo');
		var lineType = lineTypeForBaseType(baseTipo);
		var label = normalizeText(textField(row, 'label'));
		var linkedBase = findClosingBaseCardForRow(workspace, row);
		var sharedBase;

		if (!normalizeText(baseTipo)) {
			applyClosingRowFields(row, {
				baseTipo: '',
				lineType: 'nuevo_bloque',
				label: textField(row, 'label')
			});
			setTextField(row, 'base_link_id', '');

			if (linkedBase && !linesBoundToBase(workspace, linkedBase).length) {
				linkedBase.remove();
			}

			return;
		}

		applyClosingRowFields(row, {
			baseTipo: baseTipo,
			lineType: lineType,
			label: label || suggestBlockLabel(reference, lineType, baseTipo, row)
		});

		if (lineType === 'caja_saldos') {
			upgradeRowToCajaSaldoMarkup(row);
		} else {
			downgradeCajaSaldoMarkup(row);
		}

		if (!linkedBase) {
			ensureClosingPairForRow(workspace, row, reference);
			return;
		}

		sharedBase = linesBoundToBase(workspace, linkedBase).some(function (boundRow) {
			return boundRow !== row;
		});

		if (sharedBase) {
			setTextField(row, 'base_link_id', '');
			ensureClosingPairForRow(workspace, row, reference);
			return;
		}

		syncClosingBaseFromRow(linkedBase, row, reference);
	}

	function setRowFormula(row) {
		applyAutoFormulaToRow(row);
	}

	function isClosingBaseUsedInReference(reference, baseCard, excludeRow) {
		var linkId = closingBaseLinkId(baseCard);

		if (!linkId || !reference) {
			return false;
		}

		return closingReferenceLineRows(reference, { visibleOnly: true }).some(function (row) {
			return row !== excludeRow && closingBlockLinkId(row) === linkId;
		});
	}

	function findAvailableCatalogBase(workspace, baseTipo, variant, reference, excludeRow) {
		var normalizedType = normalizeText(baseTipo);
		var normalizedVariant = normalizeText(variant);
		var activeRef = reference ? baseReferenceKeyForActiveReference(reference) : '';
		var usedLinkIds = {};
		var saldoCandidate;
		var catalogBase;
		var linkId;

		if (reference) {
			closingReferenceLineRows(reference, { visibleOnly: true }).forEach(function (row) {
				linkId = closingBlockLinkId(row);

				if (linkId && row !== excludeRow) {
					usedLinkIds[linkId] = true;
				}
			});
		}

		if (normalizedType === 'cajas_saldo') {
			saldoCandidate = closeWorkspaceBases(workspace).find(function (base) {
				var baseLinkId = closingBaseLinkId(base.node);

				return normalizeText(base.tipo) === 'cajas_saldo'
					&& normalizeText(base.variante) === normalizedVariant
					&& baseReferenceMatchesFilter(base.referencia, activeRef)
					&& baseLinkId
					&& !usedLinkIds[baseLinkId];
			});

			if (saldoCandidate) {
				return saldoCandidate;
			}
		}

		catalogBase = findClosingBase(workspace, baseTipo, variant, reference);

		if (!catalogBase || !catalogBase.node) {
			return null;
		}

		if (isClosingBaseUsedInReference(reference, catalogBase.node, excludeRow)) {
			return null;
		}

		return catalogBase;
	}

	function suggestBlockLabel(reference, lineType, baseTipo, excludeRow) {
		var stem = defaultLabelForLineType(lineType, baseTipo);
		var matches = closingReferenceLineRows(reference, { visibleOnly: true }).filter(function (row) {
			if (excludeRow && row === excludeRow) {
				return false;
			}

			return lineTypeForBaseType(textField(row, 'base_tipo')) === lineType;
		});

		if (!matches.length) {
			return stem;
		}

		if (lineType === 'bolsas' && matches.length === 1) {
			return 'Bolsa 2';
		}

		return stem + ' ' + (matches.length + 1);
	}

	function applyClosingRowFields(row, options) {
		var baseTipo;
		var lineType;
		var label;
		var formula;
		var linkId;
		var resetMetrics;

		if (!row) {
			return;
		}

		options = options || {};
		baseTipo = options.baseTipo != null ? options.baseTipo : (textField(row, 'base_tipo') || '');
		lineType = options.lineType != null ? options.lineType : (baseTipo ? lineTypeForBaseType(baseTipo) : 'nuevo_bloque');
		label = options.label != null ? options.label : (baseTipo ? defaultLabelForLineType(lineType, baseTipo) : '');
		formula = baseTipo ? defaultFormulaForBaseType(baseTipo, row) : '';
		linkId = options.linkId;
		resetMetrics = !!options.resetMetrics;

		row.setAttribute('data-close-line', lineType);
		row.querySelectorAll('input, select').forEach(function (input) {
			var fieldName = input.getAttribute('data-field');

			if (fieldName === 'label') {
				input.value = label;
			} else if (fieldName === 'tipo') {
				input.value = lineType;
			} else if (fieldName === 'base_tipo') {
				input.value = baseTipo;
			} else if (fieldName === 'base_link_id' && linkId !== undefined) {
				input.value = linkId || '';
			} else if (resetMetrics && ['formula', 'cantidad', 'paquetes', 'unidades_paquete'].indexOf(fieldName) !== -1) {
				input.value = '';
			} else if (resetMetrics && fieldName === 'estado_formula') {
				input.value = 'pendiente';
			}
		});
		applyAutoFormulaToRow(row);
	}

	function syncClosingRowFromBase(row, baseCard) {
		var baseTipo;
		var lineType;
		var blockLabel;
		var label;
		var linkId;

		if (!row || !baseCard) {
			return;
		}

		baseTipo = textField(baseCard, 'tipo') || 'cajas';
		lineType = lineTypeForBaseType(baseTipo);
		blockLabel = baseCard.getAttribute('data-close-block-label');
		label = blockLabel || defaultLabelForLineType(lineType, baseTipo);
		linkId = closingBaseLinkId(baseCard);

		applyClosingRowFields(row, {
			baseTipo: baseTipo,
			lineType: lineType,
			label: label,
			linkId: linkId
		});
	}

	function syncClosingBaseFromRow(baseCard, row, reference) {
		var baseTipo;
		var lineLabel;

		if (!baseCard || !row) {
			return;
		}

		baseTipo = textField(row, 'base_tipo') || textField(baseCard, 'tipo') || 'cajas';
		lineLabel = textField(row, 'label');

		setTextField(baseCard, 'tipo', baseTipo);

		if (reference) {
			setTextField(baseCard, 'variante', textField(reference, 'variante'));
			setTextField(baseCard, 'referencia', baseReferenceKeyForActiveReference(reference));
			baseCard.setAttribute('data-base-display-reference', textField(reference, 'nombre') || '');
		}

		if (lineLabel) {
			baseCard.setAttribute('data-close-block-label', lineLabel);
		}

		updateClosingBaseCard(baseCard);
		syncClosingRowFromBase(row, baseCard);
	}

	function ensureRowLinkField(row) {
		if (!row || row.querySelector('[data-field="base_link_id"]')) {
			return;
		}

		var hidden = document.createElement('input');
		hidden.type = 'hidden';
		hidden.setAttribute('data-field', 'base_link_id');
		row.appendChild(hidden);
	}

	function findClosingBaseCardByLinkId(workspace, linkId) {
		var normalized = normalizeText(linkId);

		if (!normalized) {
			return null;
		}

		return Array.from(workspace.querySelectorAll('[data-close-base]')).find(function (card) {
			return closingBaseLinkId(card) === normalized;
		}) || null;
	}

	function findClosingBaseCardForRow(workspace, row) {
		var linkId = closingBlockLinkId(row);

		if (linkId) {
			return findClosingBaseCardByLinkId(workspace, linkId);
		}

		return null;
	}

	function findClosingBaseForRow(workspace, row, reference, basesCache) {
		var card = findClosingBaseCardForRow(workspace, row);
		var bases = basesCache || closeWorkspaceBases(workspace);

		if (!card) {
			return null;
		}

		return bases.find(function (base) {
			return base.node === card;
		}) || null;
	}

	function linesBoundToBase(workspace, baseCard) {
		var linkId = closingBaseLinkId(baseCard);

		if (!linkId) {
			return [];
		}

		return Array.from(workspace.querySelectorAll('[data-close-line]')).filter(function (row) {
			return closingBlockLinkId(row) === linkId;
		});
	}

	function closingReferenceLineRows(reference, options) {
		var visibleOnly = options && options.visibleOnly;

		return Array.from(reference.querySelectorAll('[data-close-line]')).filter(function (row) {
			if (visibleOnly && row.hidden) {
				return false;
			}

			return true;
		});
	}

	function activeClosingBlockLinkFilter(workspace) {
		var linkIds = [];

		workspace.querySelectorAll('[data-close-reference-pick]:checked').forEach(function (input) {
			var reference = input.closest('[data-close-reference]');

			if (!reference) {
				return;
			}

			closingReferenceLineRows(reference, { visibleOnly: true }).forEach(function (row) {
				var linkId = closingBlockLinkId(row);

				if (linkId && linkIds.indexOf(linkId) === -1) {
					linkIds.push(linkId);
				}
			});
		});

		return linkIds.length ? linkIds : null;
	}

	function isReferenceFilteredOut(reference) {
		return !!(reference && reference.hasAttribute('data-ref-filtered-out'));
	}

	function setReferenceFilterVisibility(reference, filteredOut) {
		var pick = reference ? reference.querySelector('[data-close-reference-pick]') : null;

		if (!reference) {
			return;
		}

		if (filteredOut) {
			reference.setAttribute('data-ref-filtered-out', '1');
			reference.hidden = true;

			if (pick) {
				pick.checked = false;
			}

			reference.classList.remove('is-reference-pick-selected');
			return;
		}

		reference.removeAttribute('data-ref-filtered-out');
	}

	function clearHiddenReferencePicks(workspace) {
		workspace.querySelectorAll('[data-close-reference]').forEach(function (reference) {
			var pick = reference.querySelector('[data-close-reference-pick]');

			if (!reference.hidden && !isReferenceFilteredOut(reference)) {
				return;
			}

			if (pick) {
				pick.checked = false;
			}

			reference.classList.remove('is-reference-pick-selected');
		});
	}

	function syncReferencePickState(workspace) {
		if (!workspace) {
			return;
		}

		clearHiddenReferencePicks(workspace);

		workspace.querySelectorAll('[data-close-reference]').forEach(function (reference) {
			var pick = reference.querySelector('[data-close-reference-pick]');

			reference.classList.toggle(
				'is-reference-pick-selected',
				!!(pick && pick.checked && !reference.hidden && !isReferenceFilteredOut(reference))
			);
		});
	}

	function showClosingReference(reference) {
		var workspace = reference.closest('[data-close-workspace]');

		if (!reference || isReferenceFilteredOut(reference)) {
			return;
		}

		workspace.querySelectorAll('[data-close-reference]').forEach(function (candidate) {
			if (candidate !== reference && sameReferenceFamily(candidate, reference) && !isReferenceFilteredOut(candidate)) {
				candidate.hidden = true;
			}
		});

		reference.hidden = false;
		setClosingReferenceEmpty(reference, false);
	}

	function showAllFilteredClosingReferences(workspace) {
		if (!workspace) {
			return;
		}

		workspace.querySelectorAll('[data-close-reference]').forEach(function (reference) {
			if (isReferenceFilteredOut(reference)) {
				reference.hidden = true;
				return;
			}

			reference.hidden = false;
			setClosingReferenceEmpty(reference, false);
		});
	}

	function applyReferencePickFilter(workspace, activePickInput) {
		var pickedInput = activePickInput || (workspace ? workspace.querySelector('[data-close-reference-pick]:checked') : null);
		var reference = pickedInput ? pickedInput.closest('[data-close-reference]') : null;
		var sidebarSelect = workspace ? workspace.querySelector('[data-base-reference-filter]') : null;

		if (!workspace) {
			return;
		}

		if (pickedInput && pickedInput.checked && reference && (reference.hidden || isReferenceFilteredOut(reference))) {
			pickedInput.checked = false;
			reference.classList.remove('is-reference-pick-selected');
			workspace.setAttribute('data-base-filter-mode', 'active');
			filterClosingBases(workspace);
			return;
		}

		if (pickedInput && pickedInput.checked) {
			workspace.querySelectorAll('[data-close-reference-pick]').forEach(function (input) {
				input.checked = input === pickedInput;
			});
			pickedInput.checked = true;
			reference = pickedInput.closest('[data-close-reference]');

			if (!isReferenceFilterPinned(workspace) && sidebarSelect) {
				sidebarSelect.value = '';
				workspace.setAttribute('data-base-filter-mode', 'active');
			}

			if (reference && !isReferenceFilteredOut(reference)) {
				showClosingReference(reference);
				syncClosingReferenceBases(workspace, reference);
				reindexClosingWorkspace(workspace);
			}
		} else if (!isReferenceFilterPinned(workspace)) {
			setReferenceFilterMode(workspace, false);
		}

		syncReferencePickState(workspace);
		filterClosingBases(workspace);
		ensureClosingBasesSyncedForPick(workspace);
	}

	function syncClosingReferenceBases(workspace, reference) {
		if (!workspace || !reference) {
			return;
		}

		reconcileClosingReference(workspace, reference);

		closingReferenceLineRows(reference, { visibleOnly: true }).forEach(function (row) {
			var linkId = closingBlockLinkId(row);

			if (linkId && !findClosingBaseCardByLinkId(workspace, linkId)) {
				setTextField(row, 'base_link_id', '');
			}
		});

		reconcileClosingReference(workspace, reference);
	}

	function resolveClosingReferenceForAutoPick(workspace) {
		var picked = workspace.querySelector('[data-close-reference-pick]:checked');
		var pickedReference = picked ? picked.closest('[data-close-reference]') : null;
		var visible;
		var referenceSelect;
		var referenceFilter;

		if (pickedReference && !pickedReference.hidden && !isReferenceFilteredOut(pickedReference)) {
			return pickedReference;
		}

		visible = visibleClosingReferences(workspace);

		if (visible.length === 1) {
			return visible[0];
		}

		referenceSelect = workspace.querySelector('[data-base-reference-filter]');
		referenceFilter = referenceSelect ? referenceSelect.value : '';

		if (referenceFilter) {
			return visible.find(function (reference) {
				return referenceMatchesFilter(reference, referenceFilter);
			}) || visible[0] || null;
		}

		return visible[0] || null;
	}

	function autoPickClosingReferenceBases(workspace) {
		var reference;
		var pick;
		var visible;
		var referenceSelect;
		var referenceFilter;
		var searchInput;
		var search;

		if (!workspace) {
			return null;
		}

		visible = visibleClosingReferences(workspace);
		referenceSelect = workspace.querySelector('[data-base-reference-filter]');
		referenceFilter = referenceSelect ? referenceSelect.value : '';
		searchInput = workspace.querySelector('[data-base-search]');
		search = normalizeText(searchInput ? searchInput.value : '');

		// Auto-pick when the view is scoped (filter, search, or a single visible reference).
		if (!referenceFilter && !search && visible.length !== 1) {
			return null;
		}

		reference = resolveClosingReferenceForAutoPick(workspace);

		if (!reference) {
			return null;
		}

		pick = reference.querySelector('[data-close-reference-pick]');

		if (!pick) {
			return reference;
		}

		if (!pick.checked) {
			workspace.querySelectorAll('[data-close-reference-pick]').forEach(function (input) {
				input.checked = input === pick;
			});
			pick.checked = true;
		}

		return reference;
	}

	function ensureClosingBasesSyncedForPick(workspace) {
		var pick = workspace ? workspace.querySelector('[data-close-reference-pick]:checked') : null;
		var reference = pick ? pick.closest('[data-close-reference]') : null;
		var blockCount;
		var linkedCount;

		if (!workspace || !reference || reference.hidden || isReferenceFilteredOut(reference)) {
			return;
		}

		blockCount = visibleBlockCountForReference(reference);
		linkedCount = closingReferenceLineRows(reference, { visibleOnly: true }).filter(function (row) {
			var linkId = closingBlockLinkId(row);
			return !!(linkId && findClosingBaseCardByLinkId(workspace, linkId));
		}).length;

		if (blockCount > 0 && linkedCount !== blockCount) {
			syncClosingReferenceBases(workspace, reference);
			reindexClosingWorkspace(workspace);
			filterClosingBases(workspace);
		}
	}

	function ensureClosingPairForRow(workspace, row, reference) {
		var linkId = closingBlockLinkId(row);
		var baseCard = linkId ? findClosingBaseCardByLinkId(workspace, linkId) : null;
		var baseTipo = textField(row, 'base_tipo');
		var variant = textField(reference, 'variante');
		var activeRef = baseReferenceKeyForActiveReference(reference);

		ensureRowLinkField(row);

		if (linkId && !baseCard) {
			// Stale link: block points to a base that no longer exists.
			setTextField(row, 'base_link_id', '');
			linkId = '';
		}

		if (!isCajaSaldoRow(row) && !normalizeText(baseTipo)) {
			return;
		}

		baseTipo = baseTipo || 'cajas';

		if (isCajaSaldoRow(row)) {
			normalizeCajaSaldoRow(row);
			baseTipo = 'cajas_saldo';
			setTextField(row, 'base_tipo', baseTipo);

			if (baseCard && textField(baseCard, 'tipo') !== 'cajas_saldo') {
				baseCard = null;
				setTextField(row, 'base_link_id', '');
				linkId = '';
			}
		}

		if (!baseCard) {
			var catalogBase = findAvailableCatalogBase(workspace, baseTipo, variant, reference, row);

			if (catalogBase && catalogBase.node) {
				baseCard = catalogBase.node;
			}
		}

		if (!baseCard) {
			linkId = linkId || newClosingLinkId('base');
			setTextField(row, 'base_link_id', linkId);
			baseCard = createClosingBaseCard(workspace, reference, {
				linkId: linkId,
				baseTipo: baseTipo,
				variant: variant,
				referencia: activeRef
			});

			if (!baseCard) {
				setTextField(row, 'base_link_id', '');
				return;
			}
		} else {
			linkId = closingBaseLinkId(baseCard) || linkId || newClosingLinkId('base');

			if (!closingBaseLinkId(baseCard)) {
				setTextField(baseCard, 'id', linkId);
			}

			setTextField(row, 'base_link_id', linkId);
		}

		syncClosingBaseFromRow(baseCard, row, reference);
	}

	function removeOrphanClosingBases(workspace) {
		Array.from(workspace.querySelectorAll('[data-close-base]')).forEach(function (baseCard) {
			if (!linesBoundToBase(workspace, baseCard).length) {
				baseCard.remove();
			}
		});
	}

	function pruneOrphanBasesForReference(workspace, reference) {
		var linkIds = {};
		var variant = normalizeText(textField(reference, 'variante'));
		var refKey = normalizeText(baseReferenceKeyForActiveReference(reference));

		closingReferenceLineRows(reference, { visibleOnly: true }).forEach(function (row) {
			var linkId = closingBlockLinkId(row);

			if (linkId) {
				linkIds[linkId] = true;
			}
		});

		Array.from(workspace.querySelectorAll('[data-close-base]')).forEach(function (baseCard) {
			var linkId = closingBaseLinkId(baseCard);
			var baseVariant = normalizeText(textField(baseCard, 'variante'));
			var baseReferencia = normalizeText(textField(baseCard, 'referencia'));

			if (baseVariant !== variant || !baseReferenceMatchesFilter(baseReferencia, refKey)) {
				return;
			}

			if (!linkId || !linkIds[linkId]) {
				baseCard.remove();
			}
		});
	}

	function reconcileClosingReference(workspace, reference) {
		closingReferenceLineRows(reference, { visibleOnly: true }).forEach(function (row) {
			ensureClosingPairForRow(workspace, row, reference);
		});

		reference.querySelectorAll('[data-close-line]').forEach(function (row) {
			var linkId;
			var baseCard;

			if (!row.hidden) {
				return;
			}

			linkId = closingBlockLinkId(row);

			if (!linkId) {
				return;
			}

			baseCard = findClosingBaseCardByLinkId(workspace, linkId);
			setTextField(row, 'base_link_id', '');

			if (baseCard) {
				baseCard.remove();
			}
		});

		pruneOrphanBasesForReference(workspace, reference);
		removeOrphanClosingBases(workspace);
		sortClosingReferenceBlocks(reference);
	}

	function reconcileClosingPairsForWorkspace(workspace) {
		workspace.querySelectorAll('[data-close-reference]').forEach(function (reference) {
			reconcileClosingReference(workspace, reference);
		});
	}

	function setClosingWorkspaceLoading(workspace, loading) {
		if (!workspace) {
			return;
		}

		if (window.LogisticaTabLoading) {
			if (loading) {
				window.LogisticaTabLoading.show(workspace, 'Cargando cierre de órdenes…', true);
			} else {
				window.LogisticaTabLoading.hide(workspace);
			}
			return;
		}

		workspace.classList.toggle('is-close-workspace-loading', !!loading);
		var overlay = workspace.querySelector('[data-close-loading]');

		if (overlay) {
			overlay.hidden = !loading;
			overlay.setAttribute('aria-busy', loading ? 'true' : 'false');
		}
	}

	function finalizeCierreTabPanelLoading(workspace) {
		var panel;

		if (!workspace) {
			return;
		}

		panel = workspace.closest('.logistica-tab-panel[data-tab-panel="logistica-cierre-ordenes"]');
		if (!panel || !window.LogisticaTabLoading) {
			return;
		}

		panel.dataset.tabLoaded = '1';
		window.LogisticaTabLoading.hide(panel);
	}

	function revealClosingWorkspace(workspace) {
		window.requestAnimationFrame(function () {
			window.requestAnimationFrame(function () {
				setClosingWorkspaceLoading(workspace, false);
				finalizeCierreTabPanelLoading(workspace);
			});
		});
	}

	function fitClosingSidebarTitle(title) {
		var sidebar;
		var sidebarStyles;
		var padLeft;
		var padRight;
		var maxWidth;
		var minSize = 10;
		var maxSize = 22;
		var size;
		var bestSize = minSize;

		if (!title || !title.textContent.trim()) {
			return;
		}

		sidebar = title.closest('.logistica-close-sidebar');
		if (!sidebar) {
			return;
		}

		sidebarStyles = window.getComputedStyle(sidebar);
		padLeft = parseFloat(sidebarStyles.paddingLeft) || 0;
		padRight = parseFloat(sidebarStyles.paddingRight) || 0;
		maxWidth = sidebar.clientWidth - padLeft - padRight;
		if (maxWidth <= 0) {
			return;
		}

		title.style.setProperty('white-space', 'nowrap', 'important');

		for (size = minSize; size <= maxSize; size += 1) {
			title.style.setProperty('font-size', size + 'px', 'important');
			if (title.scrollWidth > maxWidth) {
				break;
			}
			bestSize = size;
		}

		title.style.setProperty('font-size', bestSize + 'px', 'important');
	}

	function fitClosingSidebarTitles(workspace) {
		var root = workspace || document;

		root.querySelectorAll('.logistica-close-sidebar-title').forEach(fitClosingSidebarTitle);
	}

	function bindClosingSidebarTitleFit(workspace) {
		var sidebar;

		if (!workspace || workspace.dataset.sidebarTitleFitBound === '1') {
			return;
		}

		sidebar = workspace.querySelector('.logistica-close-sidebar');
		if (!sidebar) {
			return;
		}

		workspace.dataset.sidebarTitleFitBound = '1';

		if (typeof window.ResizeObserver === 'function') {
			new window.ResizeObserver(function () {
				fitClosingSidebarTitles(workspace);
			}).observe(sidebar);
		}

		window.addEventListener('resize', function () {
			fitClosingSidebarTitles(workspace);
		});

		fitClosingSidebarTitles(workspace);
	}

	function closingUiStorageKey() {
		return 'logistica_cierre_ui_' + String(window.location.pathname || 'default').replace(/[^\w.-]+/g, '_');
	}

	function captureClosingUiState(workspace) {
		var searchInput = workspace.querySelector('[data-base-search]');
		var variantSelect = workspace.querySelector('[data-base-variant-filter]');
		var typeSelect = workspace.querySelector('[data-base-type-filter]');
		var referenceSelect = workspace.querySelector('[data-base-reference-filter]');
		var pickedInput = workspace.querySelector('[data-close-reference-pick]:checked');
		var pickedReference = pickedInput ? pickedInput.closest('[data-close-reference]') : null;
		var visibleReference = visibleClosingReferences(workspace)[0];
		var referenceValue = referenceSelect ? referenceSelect.value : '';

		return {
			scrollY: window.scrollY || 0,
			hash: window.location.hash || '',
			filters: {
				search: searchInput ? searchInput.value : '',
				variant: variantSelect ? variantSelect.value : '',
				type: typeSelect ? typeSelect.value : '',
				reference: referenceValue
			},
			referenceFilter: referenceValue,
			referencePickActive: !!(pickedInput && pickedInput.checked),
			referenceAnchor: pickedReference
				? (pickedReference.id || '')
				: (visibleReference ? visibleReference.id || '' : '')
		};
	}

	function saveClosingUiState(workspace) {
		if (!workspace || typeof window === 'undefined' || !window.localStorage) {
			return;
		}

		try {
			window.localStorage.setItem(closingUiStorageKey(), JSON.stringify(captureClosingUiState(workspace)));
		} catch (error) {
			// Storage may be unavailable; ignore.
		}
	}

	function scheduleSaveClosingUiState(workspace) {
		if (!workspace) {
			return;
		}

		window.clearTimeout(closingUiSaveTimer);
		closingUiSaveTimer = window.setTimeout(function () {
			saveClosingUiState(workspace);
		}, 350);
	}

	function readClosingUiState() {
		if (typeof window === 'undefined' || !window.localStorage) {
			return null;
		}

		try {
			return JSON.parse(window.localStorage.getItem(closingUiStorageKey()) || 'null');
		} catch (error) {
			return null;
		}
	}

	function applyClosingUiFiltersEarly(workspace, state) {
		var filters;
		var field;

		if (!workspace || !state || !state.filters) {
			return;
		}

		filters = state.filters;

		if (filters.search && (field = workspace.querySelector('[data-base-search]'))) {
			field.value = filters.search;
		}

		if (filters.variant && (field = workspace.querySelector('[data-base-variant-filter]'))) {
			field.value = filters.variant;
		}

		if (filters.type && (field = workspace.querySelector('[data-base-type-filter]'))) {
			field.value = filters.type;
		}

		if (filters.reference && (field = workspace.querySelector('[data-base-reference-filter]'))) {
			field.value = filters.reference;
		}

		workspace.setAttribute('data-base-filter-mode', 'active');
	}

	function closingViewportOffset() {
		var nav = document.querySelector('.logistica-front-nav');
		var height = nav ? nav.getBoundingClientRect().height : 0;

		return (height > 0 ? height : 0) + 16;
	}

	function restoreClosingUiViewport(workspace, state) {
		var anchor;
		var top;

		if (!state) {
			return;
		}

		if (state.hash && state.hash.indexOf('#') === 0) {
			anchor = document.querySelector(state.hash);

			if (anchor) {
				top = anchor.getBoundingClientRect().top + window.scrollY - closingViewportOffset();
				window.scrollTo(0, Math.max(0, top));
				return;
			}
		}

		if (state.referenceAnchor) {
			anchor = document.getElementById(state.referenceAnchor);
		}

		if (!anchor && state.referenceFilter) {
			anchor = Array.from(workspace.querySelectorAll('[data-close-reference]')).find(function (reference) {
				return !isReferenceFilteredOut(reference) && referenceMatchesFilter(reference, state.referenceFilter);
			});
		}

		if (anchor) {
			top = anchor.getBoundingClientRect().top + window.scrollY - 80;
			window.scrollTo(0, Math.max(0, top));
			return;
		}

		if (state.scrollY) {
			window.scrollTo(0, state.scrollY);
		}
	}

	function restoreClosingUiAfterInit(workspace, state) {
		var referenceSelect;
		var targetReference;
		var pick;

		if (!workspace || !state) {
			return;
		}

		if (state.filters) {
			applyClosingUiFiltersEarly(workspace, state);
		}

		referenceSelect = workspace.querySelector('[data-base-reference-filter]');

		refreshBaseReferenceOptions(workspace);

		if (state.referenceFilter && referenceSelect && referenceFilterOptionExists(referenceSelect, state.referenceFilter)) {
			referenceSelect.value = state.referenceFilter;
			workspace.setAttribute('data-base-filter-mode', 'active');
		} else {
			setReferenceFilterMode(workspace, false);
			state.referenceFilter = '';
		}

		syncClosingReferencesFromFilters(workspace);

		targetReference = null;

		if (state.referenceAnchor) {
			targetReference = document.getElementById(state.referenceAnchor);
		}

		if (!targetReference && state.referenceFilter) {
			targetReference = Array.from(workspace.querySelectorAll('[data-close-reference]')).find(function (reference) {
				return !isReferenceFilteredOut(reference) && referenceMatchesFilter(reference, state.referenceFilter);
			});
		}

		workspace.querySelectorAll('[data-close-reference-pick]').forEach(function (input) {
			input.checked = false;
		});
		syncReferencePickState(workspace);

		if (!targetReference) {
			targetReference = resolveClosingReferenceForAutoPick(workspace);
		}

		if (targetReference && (state.referencePickActive || state.referenceFilter || visibleClosingReferences(workspace).length === 1)) {
			pick = targetReference.querySelector('[data-close-reference-pick]');

			if (pick) {
				pick.checked = true;
				applyReferencePickFilter(workspace, pick);
			} else {
				showAllFilteredClosingReferences(workspace);
				filterClosingBases(workspace);
			}
		} else {
			showAllFilteredClosingReferences(workspace);
			filterClosingBases(workspace);
		}

		updateClosingWorkspace(workspace);

		window.requestAnimationFrame(function () {
			restoreClosingUiViewport(workspace, state);
		});
	}

	function closingWorkspacePairsReady(workspace) {
		var ready = true;

		workspace.querySelectorAll('[data-close-reference]').forEach(function (reference) {
			if (reference.hasAttribute('data-variant-empty')) {
				return;
			}

			closingReferenceLineRows(reference, { visibleOnly: true }).forEach(function (row) {
				var linkId = closingBlockLinkId(row);

				if (!linkId || !findClosingBaseCardByLinkId(workspace, linkId)) {
					ready = false;
				}
			});
		});

		return ready;
	}

	function bindClosingUiPersistence(workspace) {
		if (!workspace) {
			return;
		}

		var form = getClosingSaveForm(workspace);

		window.addEventListener('scroll', function () {
			scheduleSaveClosingUiState(workspace);
		}, { passive: true });

		window.addEventListener('beforeunload', function () {
			flushClosingWorkspaceSave(workspace);
			saveClosingUiState(workspace);
		});

		workspace.addEventListener('change', function () {
			scheduleSaveClosingUiState(workspace);
		});

		workspace.addEventListener('input', function (event) {
			if (event.target.closest('[data-base-search]')) {
				scheduleSaveClosingUiState(workspace);
			}
		});

		workspace.addEventListener('focusin', function (event) {
			var nombreInput = event.target.closest('[data-close-reference] [data-field="nombre"]');
			var codInput = event.target.closest('[data-close-base] [data-field="cod"]');
			var baseCard;

			if (nombreInput && !nombreInput.readOnly) {
				beginClosingReferenceNameEdit(workspace, nombreInput.closest('[data-close-reference]'));
			}

			if (codInput) {
				baseCard = codInput.closest('[data-close-base]');
				if (baseCard) {
					baseCard.setAttribute('data-cod-before-edit', normalizeClosingCod(codInput.value));
				}
			}
		});

		workspace.addEventListener('focusout', function (event) {
			var nombreInput = event.target.closest('[data-close-reference] [data-field="nombre"]');

			if (!nombreInput) {
				return;
			}

			window.setTimeout(function () {
				var activeElement = document.activeElement;

				if (activeElement && workspace.contains(activeElement) && activeElement.matches('[data-close-reference] [data-field="nombre"]')) {
					return;
				}

				finishClosingReferenceNameEdit(workspace, nombreInput.closest('[data-close-reference]'));
			}, 0);
		});

		if (form) {
			form.addEventListener(
				'submit',
				function () {
					flushClosingWorkspaceSave(workspace);
				},
				true
			);
		}
	}

	function initClosingWorkspace(workspace) {
		var references = Array.from(workspace.querySelectorAll('[data-close-reference]'));
		var savedState = readClosingUiState();
		var index = 0;
		var chunkSize = 6;
		var skipReconcile = closingWorkspacePairsReady(workspace);

		bindClosingSidebarTitleFit(workspace);
		bindClosingBaseNavigatorLayout(workspace);
		applyClosingUiFiltersEarly(workspace, savedState);
		setClosingWorkspaceLoading(workspace, true);
		refreshBaseReferenceOptions(workspace);

		function finishInit() {
			initClosingUnitsFields(workspace);
			upgradeClosingCodLoteCells(workspace);
			workspace.querySelectorAll('[data-close-reference]').forEach(syncClosingReferenceMetaLock);
			syncClosingWorkspaceVariantChrome(workspace);
			restoreClosingSaldoUndManualFlags(workspace);

			if (savedState) {
				restoreClosingUiAfterInit(workspace, savedState);
			} else {
				syncClosingReferencesFromFilters(workspace);
				filterClosingBases(workspace);
				updateClosingWorkspace(workspace);
			}

			saveClosingUiState(workspace);
				fitClosingSidebarTitles(workspace);
				constrainClosingBaseNavigator(workspace);
				filterClosingBases(workspace);
			revealClosingWorkspace(workspace);
			refreshClosingSustitucionSummary(workspace);

			if (typeof syncClosingLotesFromWarehouse === 'function') {
				syncClosingLotesFromWarehouse();
			}
			if (typeof syncClosingCharacteristicsFromCatalog === 'function') {
				syncClosingCharacteristicsFromCatalog();
			}
		}

		function processChunk() {
			var processed = 0;

			while (index < references.length && processed < chunkSize) {
				reconcileClosingReference(workspace, references[index]);
				index += 1;
				processed += 1;
			}

			if (index < references.length) {
				window.requestAnimationFrame(processChunk);
				return;
			}

			finishInit();
		}

		if (skipReconcile || !references.length) {
			window.requestAnimationFrame(finishInit);
			return;
		}

		window.requestAnimationFrame(processChunk);
	}

	function visibleBlockCountForReference(reference) {
		return closingReferenceLineRows(reference, { visibleOnly: true }).length;
	}

	function syncClosingBlockForBase(workspace, baseCard, reference) {
		var linkId;
		var baseTipo;
		var body;
		var source;
		var clone;
		var lineType;
		var blockLabel;

		if (!baseCard) {
			return;
		}

		if (!reference) {
			reference = Array.from(workspace.querySelectorAll('[data-close-reference]')).find(function (item) {
				return !item.hidden;
			});
		}

		if (!reference) {
			return;
		}

		linkId = closingBaseLinkId(baseCard) || newClosingLinkId('base');
		setTextField(baseCard, 'id', linkId);
		baseTipo = textField(baseCard, 'tipo');
		blockLabel = baseCard.getAttribute('data-close-block-label') || baseTypeLabel(baseTipo);

		if (Array.from(reference.querySelectorAll('[data-close-line]')).some(function (row) {
			return closingBlockLinkId(row) === linkId;
		})) {
			return;
		}

		body = reference.querySelector('[data-close-lines]');
		lineType = lineTypeForBaseType(baseTipo);
		source = findClosingBlockCloneSource(workspace, reference, lineType);

		if (!body || !source) {
			return;
		}

		clone = source.cloneNode(true);
		ensureRowLinkField(clone);
		applyClosingRowFields(clone, {
			baseTipo: baseTipo,
			lineType: lineType,
			label: blockLabel,
			linkId: linkId,
			resetMetrics: true
		});
		body.appendChild(clone);
		sortClosingReferenceBlocks(reference);
		updateClosingBaseCard(baseCard);
	}

	function createClosingBaseCard(workspace, reference, options) {
		var list = workspace.querySelector('[data-close-bases]');
		var preferredType = options.baseTipo || '';
		var variant = options.variant || (reference ? textField(reference, 'variante') : '');
		var activeRef = options.referencia || (reference ? baseReferenceKeyForActiveReference(reference) : '');
		var linkId = options.linkId || newClosingLinkId('base');
		var source = options.templateSource || findClosingBaseTemplate(workspace, variant, preferredType, reference);
		var clone;
		var tipoField;

		if (!list) {
			return null;
		}

		if (!source) {
			return null;
		}

		clone = source.cloneNode(true);
		clone.removeAttribute('data-just-created-base');
		clone.removeAttribute('data-cod-before-edit');
		clone.classList.remove('is-sustitucion-activa', 'is-sustitucion-overdue');
		clone.setAttribute('data-sustitucion-activa', '0');
		clone.querySelectorAll('input, select').forEach(function (input) {
			var fieldName = input.getAttribute('data-field');

			if (input.hasAttribute('data-sustitucion-toggle')) {
				input.checked = false;
			}

			if (fieldName === 'id') {
				input.value = linkId;
			} else if (fieldName === 'variante') {
				input.value = variant || 'Estándar';
			} else if (fieldName === 'referencia') {
				input.value = activeRef || input.value || '';
			} else if (fieldName === 'tipo' && preferredType) {
				input.value = preferredType;
			} else if (['cod', 'lote', 'cod_saldo', 'lote_saldo', 'gramaje', 'ancho', 'calibre', 'rendimiento', 'ajuste', 'largo', 'alto', 'clave', 'largo_saldo', 'ancho_saldo', 'alto_saldo', 'clave_saldo', 'und_molde', 'peso_tamano', 'cod_habitual', 'sustitucion_desde', 'sustitucion_hasta'].indexOf(fieldName) !== -1) {
				input.value = '';
			} else if (fieldName === 'sustitucion_activa') {
				input.value = '0';
			}
		});
		tipoField = clone.querySelector('[data-field="tipo"]');
		if (tipoField && preferredType) {
			tipoField.value = preferredType;
		}
		collapseClosingBase(clone);
		clone.hidden = false;
		if (reference) {
			clone.setAttribute('data-base-display-reference', textField(reference, 'nombre') || '');
		}
		list.appendChild(clone);
		updateClosingBaseCard(clone);

		return clone;
	}

	function syncClosingBaseForBlock(workspace, row, reference) {
		if (!row) {
			return;
		}

		if (!reference) {
			reference = row.closest('[data-close-reference]');
		}

		if (!reference) {
			return;
		}

		ensureClosingPairForRow(workspace, row, reference);
	}

	function closeNumber(scope, name) {
		return numberValue(scope ? scope.querySelector('[data-field="' + name + '"]') : null);
	}

	function closeWorkspaceBases(workspace) {
		return Array.from(workspace.querySelectorAll('[data-close-base]')).map(function (card) {
			return {
				node: card,
				variante: normalizeText(textField(card, 'variante')),
				tipo: textField(card, 'tipo'),
				referencia: normalizeText(textField(card, 'referencia')),
				cod: textField(card, 'cod'),
				lote: textField(card, 'lote'),
				codSaldo: textField(card, 'cod_saldo'),
				loteSaldo: textField(card, 'lote_saldo'),
				largo: closeNumber(card, 'largo'),
				gramaje: closeNumber(card, 'gramaje'),
				ancho: closeNumber(card, 'ancho'),
				alto: closeNumber(card, 'alto'),
				clave: textField(card, 'clave'),
				calibre: closeNumber(card, 'calibre'),
				undMolde: closeNumber(card, 'und_molde'),
				pesoTamano: closeNumber(card, 'peso_tamano'),
				rendimiento: rollUnitConsumption(closeNumber(card, 'ancho'), closeNumber(card, 'gramaje')),
				ajuste: closeNumber(card, 'ajuste')
			};
		});
	}

	function rollWidthMeters(width) {
		if (!width) {
			return 0;
		}

		// La UI captura ancho en mm. Valores legacy pequeños (p. ej. 6.8) se
		// interpretan como cm para no romper bases antiguas.
		if (width <= 10) {
			return width / 100;
		}

		return width / 1000;
	}

	function rollUnitConsumption(width, gramaje) {
		var widthMeters = rollWidthMeters(width);

		return widthMeters > 0 && gramaje > 0 ? widthMeters * widthMeters * gramaje : 0;
	}

	function closingCaliberThousandthsFromMm(caliberMm) {
		if (!caliberMm || caliberMm <= 0) {
			return Number.NaN;
		}

		return Math.round((caliberMm / 25.4) * 1000);
	}

	function closingCaliberThousandthsFromInches(caliberInches) {
		if (!caliberInches || caliberInches <= 0) {
			return Number.NaN;
		}

		return Math.round(caliberInches * 1000);
	}

	function closingCaliberInchesFromCommercial(caliberCommercial) {
		if (!caliberCommercial || caliberCommercial <= 0) {
			return Number.NaN;
		}

		return caliberCommercial / 1000;
	}

	function closingCaliberMmFromCommercial(caliberCommercial) {
		var inches = closingCaliberInchesFromCommercial(caliberCommercial);

		if (!Number.isFinite(inches)) {
			return Number.NaN;
		}

		return inches * 25.4;
	}

	function formatClosingCaliberInches(caliberMm) {
		var thousandths = closingCaliberThousandthsFromMm(caliberMm);

		if (!Number.isFinite(thousandths)) {
			return '-';
		}

		return thousandths + '"';
	}

	function formatCaliberCommercialLabel(thousandths) {
		if (!Number.isFinite(thousandths) || thousandths <= 0) {
			return '-';
		}

		return thousandths + '"';
	}

	function closingPlasticUnitKg(pesoGr, undMolde) {
		if (!pesoGr || !undMolde || undMolde <= 0) {
			return 0;
		}

		return pesoGr / (undMolde * 1000);
	}

	function closingPlasticTotalKg(produced, pesoGr, undMolde) {
		var unitKg = closingPlasticUnitKg(pesoGr, undMolde);

		if (!produced || !unitKg) {
			return 0;
		}

		return produced * unitKg;
	}

	function closingBaseCardConfirmLabel(card) {
		var badge = card ? card.querySelector('[data-base-type-badge]') : null;
		var referenceEl = card ? card.querySelector('[data-base-reference]') : null;
		var variantChip = card ? card.querySelector('[data-base-variant-chip]') : null;
		var parts = [];

		if (badge && badge.textContent) {
			parts.push(badge.textContent.trim());
		}

		if (referenceEl && referenceEl.textContent) {
			parts.push(referenceEl.textContent.trim());
		}

		if (variantChip && variantChip.textContent) {
			parts.push(variantChip.textContent.trim());
		}

		return parts.join(' · ');
	}

	function updateClosingBaseCardTitle(card, meta) {
		var typeBadge = card ? card.querySelector('[data-base-type-badge]') : null;
		var variantChip = card ? card.querySelector('[data-base-variant-chip]') : null;
		var referenceEl = card ? card.querySelector('[data-base-reference]') : null;
		var blockLabelEl = card ? card.querySelector('[data-base-block-label]') : null;
		var normalizedBlockLabel = normalizeText(meta.blockLabel || '');
		var showBlockLabel = normalizedBlockLabel
			&& normalizedBlockLabel !== normalizeText(meta.typeLabel)
			&& normalizedBlockLabel !== normalizeText(meta.reference);

		if (!card || !meta) {
			return;
		}

		if (typeBadge) {
			typeBadge.textContent = meta.typeLabel || meta.type || 'Base';
			typeBadge.setAttribute('data-base-tipo', meta.type || '');
			typeBadge.title = typeBadge.textContent;
		}

		if (variantChip) {
			variantChip.textContent = meta.variant || 'Sin variante';
			variantChip.title = variantChip.textContent;
		}

		if (referenceEl) {
			referenceEl.textContent = meta.reference || 'Sin referencia';
			referenceEl.title = referenceEl.textContent;
		}

		if (blockLabelEl) {
			blockLabelEl.textContent = showBlockLabel ? meta.blockLabel : '';
			blockLabelEl.hidden = !showBlockLabel;
		}

		if (meta.type) {
			card.setAttribute('data-base-tipo', meta.type);
		}

		refreshClosingBaseSustitucion(card);
	}

	function logisticaColombiaYmd() {
		var parts = logisticaColombiaYmdHmsParts();
		return parts.year + '-' + parts.month + '-' + parts.day;
	}

	function parseClosingYmd(value) {
		var match = String(value || '').trim().match(/^(\d{4})-(\d{2})-(\d{2})$/);
		if (!match) {
			return null;
		}
		return {
			year: Number(match[1]),
			month: Number(match[2]),
			day: Number(match[3])
		};
	}

	function closingYmdUtcMs(ymd) {
		var parts = parseClosingYmd(ymd);
		if (!parts) {
			return Number.NaN;
		}
		return Date.UTC(parts.year, parts.month - 1, parts.day);
	}

	function closingDaysBetweenYmd(fromYmd, toYmd) {
		var fromMs = closingYmdUtcMs(fromYmd);
		var toMs = closingYmdUtcMs(toYmd);
		if (!Number.isFinite(fromMs) || !Number.isFinite(toMs)) {
			return null;
		}
		return Math.floor((toMs - fromMs) / 86400000);
	}

	function formatClosingEsDate(ymd) {
		var parts = parseClosingYmd(ymd);
		var months;
		if (!parts) {
			return '';
		}
		months = ['ene', 'feb', 'mar', 'abr', 'may', 'jun', 'jul', 'ago', 'sep', 'oct', 'nov', 'dic'];
		return parts.day + ' ' + months[parts.month - 1] + ' ' + parts.year;
	}

	function closingSustitucionTipoPhrase(tipo, tipoLabel) {
		var key = normalizeText(tipo || '');
		var map = {
			cajas: 'esta caja',
			cajas_saldo: 'esta caja saldo',
			bolsas: 'esta bolsa',
			rollos: 'este rollo fondo',
			papel_bobinas: 'esta bobina',
			plastico: 'este plástico',
			acetato: 'este acetato',
			papel_hojas: 'este papel',
			microcorrugado: 'este microcorrugado'
		};

		if (map[key]) {
			return map[key];
		}

		return 'este insumo' + (tipoLabel ? ' (' + String(tipoLabel).toLowerCase() + ')' : '');
	}

	function buildClosingSustitucionAlertText(card) {
		var activa = textField(card, 'sustitucion_activa') === '1';
		var habitual = normalizeClosingCod(textField(card, 'cod_habitual'));
		var actual = normalizeClosingCod(textField(card, 'cod'));
		var desde = String(textField(card, 'sustitucion_desde') || '').trim();
		var hasta = String(textField(card, 'sustitucion_hasta') || '').trim();
		var tipo = textField(card, 'tipo');
		var tipoLabel = baseTypeLabel(tipo) || tipo || 'insumo';
		var referencia = textField(card, 'referencia') || '';
		var variante = textField(card, 'variante') || '';
		var today = logisticaColombiaYmd();
		var daysUsed = desde ? closingDaysBetweenYmd(desde, today) : null;
		var overdue = false;
		var contextBits = [];
		var title = '';
		var details = [];
		var tipoPhrase = closingSustitucionTipoPhrase(tipo, tipoLabel);

		if (!activa) {
			return { title: '', text: '', overdue: false, daysUsed: null };
		}

		if (tipoLabel) {
			contextBits.push(tipoLabel);
		}
		if (referencia) {
			contextBits.push(referencia);
		}
		if (variante) {
			contextBits.push(variante);
		}

		if (!habitual) {
			title = 'Falta el código habitual';
			details.push('Indica el código que normalmente usas en ' + tipoPhrase + ' para comparar con el temporal.');
		} else if (!actual) {
			title = 'Código temporal vacío';
			details.push('La sustitución está activa (habitual ' + habitual + '), pero el Cod actual está vacío.');
		} else if (actual === habitual) {
			title = 'Ya estás en el código habitual';
			details.push('El Cod actual (' + actual + ') coincide con el habitual. Si ya terminó la sustitución, desactiva el aviso.');
		} else {
			title = 'Usas código temporal ' + actual;
			details.push(
				'En lugar del habitual ' + habitual +
				(contextBits.length ? ' · ' + contextBits.join(' · ') : '') + '.'
			);
		}

		if (daysUsed !== null && daysUsed >= 0) {
			details.push(
				daysUsed === 0
					? 'Empezó hoy (' + formatClosingEsDate(desde) + ').'
					: ('Llevas ' + daysUsed + (daysUsed === 1 ? ' día' : ' días') +
						' descontando con esta sustitución (desde el ' + formatClosingEsDate(desde) + ').')
			);
		} else if (!desde) {
			details.push('Completa la fecha «Desde» para contar los días de uso temporal.');
		}

		if (hasta) {
			if (closingDaysBetweenYmd(hasta, today) >= 0) {
				overdue = true;
				title = overdue && habitual && actual && actual !== habitual
					? 'Debías retomar el ' + habitual
					: title;
				details.push(
					'La fecha de retorno era el ' + formatClosingEsDate(hasta) +
					'. Si sigues con el temporal puedes descuadrar el inventario.'
				);
			} else {
				details.push('Debes retomar el habitual el ' + formatClosingEsDate(hasta) + '.');
			}
		} else if (daysUsed !== null && daysUsed >= 3) {
			overdue = true;
			details.push('Llevas varios días con la sustitución. Confirma si ya llegó el habitual.');
		}

		if (habitual && actual && actual !== habitual) {
			details.push('Al volver el stock, usa «Restaurar código habitual».');
		}

		return {
			title: title,
			text: details.join(' '),
			overdue: overdue,
			daysUsed: daysUsed
		};
	}

	function refreshClosingBaseSustitucion(card) {
		var activa;
		var fields;
		var hint;
		var alertBox;
		var alertTitle;
		var alertText;
		var chip;
		var toggle;
		var info;
		var chipLabel;

		if (!card) {
			return;
		}

		activa = textField(card, 'sustitucion_activa') === '1';
		fields = card.querySelector('[data-sustitucion-fields]');
		hint = card.querySelector('[data-sustitucion-hint]');
		alertBox = card.querySelector('[data-sustitucion-alert]');
		alertTitle = card.querySelector('[data-sustitucion-alert-title]');
		alertText = card.querySelector('[data-sustitucion-alert-text]');
		chip = card.querySelector('[data-base-sustitucion-chip]');
		toggle = card.querySelector('[data-sustitucion-toggle]');
		info = buildClosingSustitucionAlertText(card);

		card.classList.toggle('is-sustitucion-activa', activa);
		card.classList.toggle('is-sustitucion-overdue', activa && !!info.overdue);
		card.setAttribute('data-sustitucion-activa', activa ? '1' : '0');

		if (toggle) {
			toggle.checked = activa;
		}
		if (hint) {
			hint.hidden = activa;
		}
		if (fields) {
			fields.hidden = !activa;
		}
		if (alertBox) {
			alertBox.hidden = !activa;
			alertBox.classList.toggle('is-overdue', activa && !!info.overdue);
		}
		if (alertTitle) {
			alertTitle.textContent = activa ? (info.title || '') : '';
		}
		if (alertText) {
			alertText.textContent = activa ? info.text : '';
		}
		if (chip) {
			chipLabel = 'Temporal';
			if (activa && info.daysUsed !== null && info.daysUsed >= 0) {
				chipLabel = info.daysUsed === 0
					? 'Temporal · hoy'
					: ('Temporal · ' + info.daysUsed + 'd');
			}
			chip.textContent = chipLabel;
			chip.hidden = !activa;
			chip.title = activa ? ((info.title ? info.title + '. ' : '') + (info.text || '')) : '';
		}

		refreshClosingSustitucionSummary(card.closest('[data-close-workspace]'));
	}

	function refreshClosingSustitucionSummary(workspace) {
		var summary;
		var summaryText;
		var items = [];
		var hasOverdue = false;

		if (!workspace) {
			return;
		}

		summary = workspace.querySelector('[data-close-sustitucion-summary]');
		summaryText = workspace.querySelector('[data-close-sustitucion-summary-text]');
		if (!summary) {
			return;
		}

		workspace.querySelectorAll('[data-close-base]').forEach(function (card) {
			var activa = textField(card, 'sustitucion_activa') === '1';
			var habitual;
			var actual;
			var tipoLabel;
			var referencia;
			var info;

			if (!activa) {
				return;
			}

			info = buildClosingSustitucionAlertText(card);
			habitual = normalizeClosingCod(textField(card, 'cod_habitual')) || '¿?';
			actual = normalizeClosingCod(textField(card, 'cod')) || '¿?';
			tipoLabel = baseTypeLabel(textField(card, 'tipo')) || textField(card, 'tipo') || 'Base';
			referencia = textField(card, 'referencia') || '';
			if (info.overdue) {
				hasOverdue = true;
			}
			items.push(
				tipoLabel +
				(referencia ? ' ' + referencia : '') +
				': temporal ' + actual + ' (habitual ' + habitual + ')' +
				(info.daysUsed !== null && info.daysUsed >= 0
					? (', ' + (info.daysUsed === 0 ? 'desde hoy' : info.daysUsed + ' día(s)'))
					: '') +
				(info.overdue ? ' — revisar retorno' : '')
			);
		});

		if (!items.length) {
			summary.hidden = true;
			summary.classList.remove('is-overdue');
			if (summaryText) {
				summaryText.textContent = '';
			}
			return;
		}

		summary.hidden = false;
		summary.classList.toggle('is-overdue', hasOverdue);
		if (summaryText) {
			summaryText.textContent = items.join(' · ');
		}
	}

	function setClosingBaseSustitucionActiva(card, active) {
		var input = card ? card.querySelector('[data-field="sustitucion_activa"]') : null;
		var desdeInput;
		var habitualInput;
		var codActual;
		var previousCod;

		if (!card || !input) {
			return;
		}

		input.value = active ? '1' : '0';

		if (active) {
			desdeInput = card.querySelector('[data-field="sustitucion_desde"]');
			habitualInput = card.querySelector('[data-field="cod_habitual"]');
			codActual = normalizeClosingCod(textField(card, 'cod'));
			previousCod = normalizeClosingCod(card.getAttribute('data-cod-before-edit') || '');

			if (desdeInput && !String(desdeInput.value || '').trim()) {
				desdeInput.value = logisticaColombiaYmd();
			}

			if (habitualInput && !normalizeClosingCod(habitualInput.value)) {
				if (previousCod && previousCod !== codActual) {
					habitualInput.value = previousCod;
				}
			}
		}

		refreshClosingBaseSustitucion(card);
	}

	function restoreClosingHabitualCod(button) {
		var card = button ? button.closest('[data-close-base]') : null;
		var workspace = card ? card.closest('[data-close-workspace]') : null;
		var habitual;
		var codInput;

		if (!card) {
			return;
		}

		habitual = normalizeClosingCod(textField(card, 'cod_habitual'));
		codInput = card.querySelector('[data-field="cod"]');

		if (!habitual) {
			window.alert('Indica primero el código habitual que quieres restaurar.');
			return;
		}

		if (window.confirm && !window.confirm(
			'¿Restaurar el código ' + habitual + ' y desactivar el aviso de sustitución temporal?'
		)) {
			return;
		}

		if (codInput) {
			codInput.value = habitual;
		}
		setClosingBaseSustitucionActiva(card, false);
		updateClosingBaseCard(card);
		if (workspace) {
			propagateBaseFieldsToLines(card, workspace);
			handleClosingCodLoteSync(workspace, card, 'cod');
			scheduleUpdateClosingWorkspace(workspace, { immediate: true });
			autosaveClosingWorkspace(workspace);
		}
	}

	function closingBaseTypeFieldsMap() {
		var cfg = window.LogisticaAdmin || {};
		return cfg.closingBaseTypeFields && typeof cfg.closingBaseTypeFields === 'object'
			? cfg.closingBaseTypeFields
			: {};
	}

	function fieldsForClosingBaseType(tipo) {
		var map = closingBaseTypeFieldsMap();
		var key = normalizeText(tipo);
		var defaults = {
			cajas: ['largo', 'ancho', 'alto', 'clave', 'consumo_cajas'],
			cajas_saldo: ['largo', 'ancho', 'alto', 'clave'],
			bolsas: ['largo', 'ancho', 'calibre', 'consumo_bolsas'],
			rollos: ['ancho', 'gramaje', 'consumo_rollos'],
			papel_bobinas: ['ancho', 'gramaje', 'consumo_rollos'],
			plastico: ['largo', 'ancho', 'und_molde', 'peso_tamano', 'consumo_plastico'],
			acetato: ['largo', 'ancho', 'und_molde', 'consumo_acetato'],
			papel_hojas: ['ancho', 'gramaje', 'calibre'],
			microcorrugado: ['ancho', 'gramaje', 'calibre']
		};
		var fields;

		if (map[key] && Array.isArray(map[key])) {
			fields = map[key].slice();
		} else if (defaults[key]) {
			fields = defaults[key].slice();
		} else {
			fields = ['largo', 'ancho'];
		}

		if (key === 'acetato') {
			['largo', 'ancho', 'und_molde', 'consumo_acetato'].forEach(function (requiredField) {
				if (fields.indexOf(requiredField) === -1) {
					fields.push(requiredField);
				}
			});
		}

		return fields;
	}

	function closingBaseTypeShowsField(tipo, field) {
		return fieldsForClosingBaseType(tipo).indexOf(field) !== -1;
	}

	function updateClosingBaseCard(card) {
		var normalizedType = normalizeText(textField(card, 'tipo'));
		var isMainBoxes = normalizedType === 'cajas';
		var isSaldoBoxes = normalizedType === 'cajas_saldo';
		var isBags = normalizedType === 'bolsas';
		var isRoll = closingBaseTypeShowsField(normalizedType, 'consumo_rollos');
		var isPlastic = closingBaseTypeShowsField(normalizedType, 'consumo_plastico')
			|| (closingBaseTypeShowsField(normalizedType, 'und_molde')
				&& closingBaseTypeShowsField(normalizedType, 'peso_tamano'));
		var isAcetato = normalizedType === 'acetato'
			|| closingBaseTypeShowsField(normalizedType, 'consumo_acetato');
		var plasticUnitKg = closingPlasticUnitKg(closeNumber(card, 'peso_tamano'), closeNumber(card, 'und_molde'));
		var undPorLamina = closeNumber(card, 'und_molde');
		var lengthLabel = card.querySelector('[data-length-label]');
		var widthLabel = card.querySelector('[data-width-label]');
		var heightLabel = card.querySelector('[data-height-label]');
		var undMoldeLabel = card.querySelector('[data-und-molde-label]');
		var variant = textField(card, 'variante') || 'Sin variante';
		var typeBadge = card.querySelector('[data-base-type-badge]');
		var type = textField(card, 'tipo') || 'Base';
		var typeLabel = baseTypeLabel(normalizedType) || type;
		var reference = card.getAttribute('data-base-display-reference')
			|| textField(card, 'referencia')
			|| 'Sin referencia';
		var blockLabel = card.getAttribute('data-close-block-label');
		var caliberMm = closeNumber(card, 'calibre');
		var rendimientoInput = card.querySelector('[data-field="rendimiento"]');
		var consumoUnidad = isRoll ? rollUnitConsumption(closeNumber(card, 'ancho'), closeNumber(card, 'gramaje')) : 0;

		card.querySelectorAll('[data-caja-saldo-field]').forEach(function (field) {
			var input = field.querySelector('input');

			field.hidden = true;
			if (input) {
				input.value = '';
			}
		});

		card.querySelectorAll('[data-base-field]').forEach(function (field) {
			var fieldKey = field.getAttribute('data-base-field') || '';
			var shouldShow = closingBaseTypeShowsField(normalizedType, fieldKey);
			if (shouldShow) {
				field.hidden = false;
				field.removeAttribute('hidden');
				field.classList.remove('is-base-field-hidden');
				field.style.removeProperty('display');
			} else {
				field.hidden = true;
				field.setAttribute('hidden', 'hidden');
				field.classList.add('is-base-field-hidden');
				field.style.setProperty('display', 'none', 'important');
			}
		});

		card.querySelectorAll('[data-clave-label]').forEach(function (label) {
			label.textContent = isSaldoBoxes ? 'Clave cajas saldo' : 'Clave caja principal';
		});

		updateClosingBaseCardTitle(card, {
			variant: variant,
			type: type,
			typeLabel: typeLabel,
			reference: reference,
			blockLabel: blockLabel
		});

		if (lengthLabel) {
			lengthLabel.textContent = isAcetato
				? 'Largo lámina (cm)'
				: (isPlastic
					? 'Largo (cm)'
					: (isBags
						? 'Largo bolsa (pulg.)'
						: (isSaldoBoxes ? 'Largo cajas saldo (mm)' : 'Largo caja principal (mm)')));
		}

		if (widthLabel) {
			widthLabel.textContent = isAcetato
				? 'Ancho lámina (cm)'
				: (isPlastic
					? 'Ancho (cm)'
					: (isBags
						? 'Ancho bolsa (pulg.)'
						: (isSaldoBoxes
							? 'Ancho cajas saldo (mm)'
							: (isMainBoxes
								? 'Ancho caja principal (mm)'
								: (normalizedType === 'papel_bobinas'
									? 'Ancho bobina (mm)'
									: (normalizedType === 'rollos'
										? 'Ancho rollo (mm)'
										: (normalizedType === 'papel_hojas'
											? 'Ancho hoja (mm)'
											: (normalizedType === 'microcorrugado' ? 'Ancho microcorrugado (mm)' : 'Ancho'))))))));
		}

		if (heightLabel) {
			heightLabel.textContent = isSaldoBoxes ? 'Alto cajas saldo (mm)' : 'Alto caja principal (mm)';
		}

		if (undMoldeLabel) {
			undMoldeLabel.textContent = isAcetato ? 'Cortes por lámina (despiece)' : 'Und x molde (und)';
		}

		if (rendimientoInput) {
			rendimientoInput.value = consumoUnidad > 0 ? displayNumber(consumoUnidad, 3) : '';
		}

		outputText(card, 'calibre_pulgadas', formatClosingCaliberInches(caliberMm));
		outputText(card, 'plastico_consumo_unidad', plasticUnitKg > 0 ? displayNumber(plasticUnitKg, 4) + ' kg' : '-');
		outputText(
			card,
			'acetato_consumo_unidad',
			undPorLamina > 0
				? 'Unid. prod. ÷ ' + displayNumber(undPorLamina, 0) + ' cortes'
				: 'Ingrese el despiece (cortes)'
		);

		if (typeBadge) {
			typeBadge.setAttribute('data-base-tipo', normalizedType);
			typeBadge.textContent = typeLabel;
		}

		card.setAttribute('data-base-tipo', normalizedType);
		syncClosingVariantChrome(card);
		lockClosingBaseCatalogFields(card);
		lockClosingBaseIdentity(card);
		outputText(card, 'rendimiento_formula', consumoUnidad > 0 ? displayNumber(consumoUnidad, 3) + ' gr/unid' : '-');
	}

	function closingCatalogBaseTypes() {
		return ['cajas', 'cajas_saldo', 'bolsas', 'acetato'];
	}

	function closingCatalogFieldsForType(tipo) {
		var normalizedType = normalizeText(tipo);

		if (normalizedType === 'bolsas') {
			return ['largo', 'ancho', 'calibre'];
		}

		if (normalizedType === 'cajas' || normalizedType === 'cajas_saldo') {
			return ['largo', 'ancho', 'alto', 'clave'];
		}

		if (normalizedType === 'acetato') {
			return ['largo', 'ancho', 'alto', 'clave'];
		}

		return [];
	}

	function setClosingInputLocked(input, locked) {
		if (!input) {
			return;
		}

		input.readOnly = !!locked;
		input.tabIndex = locked ? -1 : 0;
		input.classList.toggle('logistica-readonly-field', !!locked);
		input.setAttribute('aria-readonly', locked ? 'true' : 'false');

		if (locked) {
			input.setAttribute('data-closing-locked', '1');
		} else {
			input.removeAttribute('data-closing-locked');
		}
	}

	function lockClosingBaseCatalogFields(card) {
		var normalizedType = normalizeText(textField(card, 'tipo'));
		var catalogFields = closingCatalogFieldsForType(normalizedType);
		var label;

		// Lote siempre maestro desde inventario bodega (solo lectura).
		setClosingInputLocked(card.querySelector('[data-field="lote"]'), true);
		label = card.querySelector('[data-field="lote"]');
		label = label ? label.closest('label') : null;
		if (label) {
			label.classList.add('logistica-base-catalog-field');
			label.title = 'Tomado del inventario bodega (lote vigente)';
		}

		['largo', 'ancho', 'alto', 'clave', 'calibre'].forEach(function (fieldName) {
			var input = card.querySelector('[data-field="' + fieldName + '"]');
			var locked = catalogFields.indexOf(fieldName) !== -1;

			setClosingInputLocked(input, locked);
			label = input ? input.closest('label') : null;
			if (label && label.hasAttribute('data-base-field')) {
				label.classList.toggle('logistica-base-catalog-field', locked);
			}
		});
	}

	function applyClosingCharacteristicsToBase(baseCard, row) {
		var normalizedType;
		var catalogFields;
		var changed = false;

		if (!baseCard || !row) {
			return changed;
		}

		normalizedType = normalizeText(textField(baseCard, 'tipo'));
		catalogFields = closingCatalogFieldsForType(normalizedType);
		if (!catalogFields.length) {
			return changed;
		}

		catalogFields.forEach(function (fieldName) {
			var input = baseCard.querySelector('[data-field="' + fieldName + '"]');

			if (!input || row[fieldName] === undefined) {
				return;
			}

			if (String(input.value || '') !== String(row[fieldName] || '')) {
				input.value = String(row[fieldName] || '');
				changed = true;
			}
		});

		return changed;
	}

	function syncClosingCharacteristicsByCod(workspace, codigo, row) {
		var changed = false;

		if (!workspace || !codigo || !row) {
			return changed;
		}

		workspace.querySelectorAll('[data-close-base]').forEach(function (baseCard) {
			var codInput = baseCard.querySelector('[data-field="cod"]');

			if (!codInput || normalizeClosingCod(codInput.value) !== codigo) {
				return;
			}

			if (applyClosingCharacteristicsToBase(baseCard, row)) {
				changed = true;
				updateClosingBaseCard(baseCard);
				propagateBaseFieldsToLines(baseCard, workspace);
			}
		});

		return changed;
	}

	function updateClosingBaseCards(workspace) {
		workspace.querySelectorAll('[data-close-base]').forEach(updateClosingBaseCard);
	}

	function collapseClosingBase(card) {
		var body = card ? card.querySelector('[data-base-body]') : null;
		var toggle = card ? card.querySelector('[data-action="toggle-close-base"]') : null;

		if (body) {
			body.hidden = true;
		}

		if (toggle) {
			toggle.setAttribute('aria-expanded', 'false');
		}
	}

	function collapseClosingBases(workspace) {
		if (!workspace) {
			return;
		}

		workspace.querySelectorAll('[data-close-base]').forEach(collapseClosingBase);
	}

	function toggleClosingBase(button) {
		var card = button.closest('[data-close-base]');
		var body = card ? card.querySelector('[data-base-body]') : null;
		var open = body ? body.hidden : false;

		if (!body) {
			return;
		}

		body.hidden = !open;
		button.setAttribute('aria-expanded', open ? 'true' : 'false');
	}

	function removeClosingBase(button) {
		var card = button.closest('[data-close-base]');
		var workspace = button.closest('[data-close-workspace]');
		var label = closingBaseCardConfirmLabel(card);

		if (!card || !workspace) {
			return;
		}

		if (!confirmLogisticaDelete('¿Eliminar esta base permanente y su bloque de cálculo enlazado?', label || '')) {
			return;
		}

		linesBoundToBase(workspace, card).forEach(function (row) {
			row.remove();
		});
		card.remove();
		reindexClosingWorkspace(workspace);
		filterClosingBases(workspace);
		updateClosingWorkspace(workspace);
		autosaveClosingWorkspace(workspace, true);
	}

	function isEditableClosingField(element) {
		if (!element || !element.matches) {
			return false;
		}

		return element.matches('input:not([type="button"]):not([type="submit"]):not([type="reset"]), textarea, [contenteditable="true"]');
	}

	function baseReferenceLookupKeys(reference, options) {
		var keys = [];
		var nombre = normalizeText(textField(reference, 'nombre'));
		var template = normalizeText(referenceTemplateValue(reference));
		var includeTemplate = !options || options.includeTemplate !== false;

		if (nombre) {
			keys.push(nombre);
		}

		if (includeTemplate && template && keys.indexOf(template) === -1) {
			keys.push(template);
		}

		if (!keys.length && template) {
			keys.push(template);
		}

		return keys;
	}

	function baseReferenceMatrixLookupKeys(reference) {
		var keys = [];
		var nombre = normalizeText(textField(reference, 'nombre'));
		var template = normalizeText(referenceTemplateValue(reference));
		var unidad = normalizeText(textField(reference, 'unidad_empaque'));
		var sizeMatch;

		if (template) {
			keys.push(template);
		}

		if (unidad && keys.indexOf(unidad) === -1) {
			keys.push(unidad);
		}

		if (nombre) {
			sizeMatch = nombre.match(/(\d+(?:[.,]\d+)?\s*oz(?:\s*dp)?)/i);

			if (sizeMatch) {
				sizeMatch = normalizeText(sizeMatch[1]);

				if (keys.indexOf(sizeMatch) === -1) {
					keys.push(sizeMatch);
				}
			}
		}

		if (nombre && keys.indexOf(nombre) === -1) {
			keys.push(nombre);
		}

		if (!keys.length && template) {
			keys.push(template);
		}

		return keys;
	}

	function baseReferenceKeyForActiveReference(reference) {
		var template = normalizeText(referenceTemplateValue(reference));

		// Permanent bases are keyed by pack/size template (e.g. "4 OZ"), not product name
		// ("VASO 4 OZ"). Using the name breaks server-side sync_catalog_bases matching.
		if (template) {
			return template;
		}

		return normalizeText(textField(reference, 'nombre'));
	}

	function closingReferenceKeyAliases(value) {
		var keys = [];
		var normalized = normalizeText(value);
		var stripped;

		if (!normalized) {
			return keys;
		}

		keys.push(normalized);
		stripped = normalizeText(String(value || '').replace(/^(vaso|taz[oó]n|bowl|tapa|collar[ií]n)\s+/i, ''));

		if (stripped && keys.indexOf(stripped) === -1) {
			keys.push(stripped);
		}

		return keys;
	}

	function baseReferenceMatchesFilter(baseReference, activeReference) {
		var baseKeys = closingReferenceKeyAliases(baseReference);
		var targetKeys = closingReferenceKeyAliases(activeReference);
		var index;

		if (!targetKeys.length) {
			return true;
		}

		if (!baseKeys.length) {
			return true;
		}

		for (index = 0; index < baseKeys.length; index += 1) {
			if (targetKeys.indexOf(baseKeys[index]) !== -1) {
				return true;
			}
		}

		return false;
	}

	function findClosingBaseForKey(bases, lookupKey, normalizedVariant) {
		var normalizedKey = normalizeText(lookupKey);
		var match;

		if (!normalizedKey) {
			return null;
		}

		match = bases.find(function (base) {
			return base.variante === normalizedVariant && base.referencia === normalizedKey;
		});

		if (!match) {
			match = bases.find(function (base) {
				return !base.variante && base.referencia === normalizedKey;
			});
		}

		if (!match) {
			match = bases.find(function (base) {
				return base.referencia === normalizedKey
					&& (!normalizedVariant || !base.variante || base.variante === normalizedVariant);
			});
		}

		if (!match) {
			match = bases.find(function (base) {
				return base.variante === normalizedVariant && !base.referencia;
			});
		}

		return match || null;
	}

	function findClosingBase(workspace, type, variant, reference) {
		var normalizedVariant = normalizeText(variant);
		var normalizedType = normalizeText(type);
		var lookupKeys = reference ? baseReferenceMatrixLookupKeys(reference) : [];
		var bases = closeWorkspaceBases(workspace).filter(function (base) {
			return normalizeText(base.tipo) === normalizedType;
		});
		var index;
		var match = null;

		for (index = 0; index < lookupKeys.length; index += 1) {
			match = findClosingBaseForKey(bases, lookupKeys[index], normalizedVariant);

			if (match) {
				return match;
			}
		}

		return null;
	}

	function shouldUseBlockLinkFilter(workspace, referenceFilter) {
		var pickedInput = workspace ? workspace.querySelector('[data-close-reference-pick]:checked') : null;
		var pickedReference = pickedInput ? pickedInput.closest('[data-close-reference]') : null;

		if (!pickedInput || !pickedReference || pickedReference.hidden || isReferenceFilteredOut(pickedReference)) {
			return false;
		}

		if (referenceFilter && !referenceMatchesFilter(pickedReference, referenceFilter)) {
			return false;
		}

		var searchInput = workspace.querySelector('[data-base-search]');
		var search = normalizeText(searchInput ? searchInput.value : '');

		if (search && referenceSearchText(workspace, pickedReference).indexOf(search) === -1) {
			return false;
		}

		var variantSelect = workspace.querySelector('[data-base-variant-filter]');
		var variantFilter = normalizeText(variantSelect ? variantSelect.value : '');

		if (variantFilter && normalizeText(textField(pickedReference, 'variante')) !== variantFilter) {
			return false;
		}

		return true;
	}

	function sidebarVariantFilterValue(workspace) {
		var variantSelect = workspace ? workspace.querySelector('[data-base-variant-filter]') : null;

		return variantSelect ? normalizeText(variantSelect.value) : '';
	}

	function isVariantFilterPinned(workspace) {
		return !!sidebarVariantFilterValue(workspace);
	}

	function closingBaseFilterContext(workspace) {
		var variantSelect = workspace.querySelector('[data-base-variant-filter]');
		var explicitVariant = variantSelect ? variantSelect.value : '';
		var normalizedVariant = explicitVariant ? normalizeText(explicitVariant) : '';
		var searchInput = workspace.querySelector('[data-base-search]');
		var typeSelect = workspace.querySelector('[data-base-type-filter]');
		var referenceSelect = workspace.querySelector('[data-base-reference-filter]');
		var referenceFilter = referenceSelect ? referenceSelect.value : '';
		var scopedReferences = referenceFilter
			? closingReferencesForFilter(workspace, referenceFilter, normalizedVariant)
			: [];

		return {
			normalizedVariant: normalizedVariant,
			explicitVariant: explicitVariant,
			search: normalizeText(searchInput ? searchInput.value : ''),
			typeFilter: typeSelect ? typeSelect.value : '',
			referenceFilter: referenceFilter ? normalizeText(referenceFilter) : '',
			referenceFilterKeys: referenceFilter ? lookupKeysForReferences(scopedReferences) : [],
			blockLinkIds: shouldUseBlockLinkFilter(workspace, referenceFilter)
				? activeClosingBlockLinkFilter(workspace)
				: null
		};
	}

	function isClosingBaseHidden(base, context) {
		var baseVariant = normalizeText(textField(base, 'variante'));
		var baseType = textField(base, 'tipo');
		var baseReference = normalizeText(textField(base, 'referencia'));
		var linkId = closingBaseLinkId(base);
		var linkedToActivePick = !!(
			context.blockLinkIds
			&& context.blockLinkIds.length
			&& linkId
			&& context.blockLinkIds.indexOf(linkId) !== -1
		);
		var haystack = normalizeText([
			textField(base, 'referencia'),
			base.getAttribute('data-base-display-reference') || '',
			textField(base, 'tipo'),
			baseTypeLabel(baseType),
			textField(base, 'cod'),
			textField(base, 'lote'),
			textField(base, 'cod_saldo'),
			textField(base, 'lote_saldo')
		].join(' '));
		var hidden = false;

		if (context.normalizedVariant && baseVariant && baseVariant !== context.normalizedVariant) {
			hidden = true;
		}

		if (!hidden && context.typeFilter && baseType !== context.typeFilter) {
			hidden = true;
		}

		if (!hidden && context.referenceFilter && !baseMatchesClosingReferenceFilter(baseReference, baseVariant, context)) {
			hidden = true;
		}

		// Search often matches the product name ("tapa") while permanent bases use the
		// template key ("62 MM (4 OZ)"). When Ver bases is active, keep linked bases visible.
		if (!hidden && context.search && haystack.indexOf(context.search) === -1 && !linkedToActivePick) {
			hidden = true;
		}

		if (!hidden && context.blockLinkIds && context.blockLinkIds.length) {
			if (context.blockLinkIds.indexOf(linkId) === -1) {
				hidden = true;
			}
		}

		return hidden;
	}

	function renderClosingBaseCount(countElement, lines) {
		var specs = [];
		var lineIndex;
		var entry;

		if (!countElement) {
			return;
		}

		for (lineIndex = 0; lineIndex < lines.length; lineIndex += 1) {
			entry = lines[lineIndex];

			if (!entry) {
				continue;
			}

			if (entry.text) {
				specs.push(entry);
			} else if (entry.textContent) {
				specs.push({
					text: entry.textContent,
					className: entry.className
				});
			}
		}

		if (typeof window.logisticaApplyBaseCountLines === 'function') {
			window.logisticaApplyBaseCountLines(countElement, specs);
			return;
		}

		while (countElement.firstChild) {
			countElement.removeChild(countElement.firstChild);
		}

		for (lineIndex = 0; lineIndex < specs.length; lineIndex += 1) {
			countElement.appendChild(closingBaseCountLine(specs[lineIndex].text, specs[lineIndex].className));
		}
	}

	function constrainClosingBaseNavigator(workspace) {
		var navigator = workspace ? workspace.querySelector('.logistica-base-navigator') : null;
		var sidebar = workspace ? workspace.querySelector('.logistica-close-sidebar') : null;
		var available;
		var padLeft;
		var padRight;
		var sidebarStyles;

		if (!navigator) {
			return;
		}

		if (sidebar) {
			sidebarStyles = window.getComputedStyle(sidebar);
			padLeft = parseFloat(sidebarStyles.paddingLeft) || 0;
			padRight = parseFloat(sidebarStyles.paddingRight) || 0;
			available = Math.max(0, sidebar.clientWidth - padLeft - padRight);
		} else {
			available = navigator.parentElement ? navigator.parentElement.clientWidth : navigator.clientWidth;
		}

		if (!available) {
			return;
		}

		navigator.style.setProperty('width', available + 'px', 'important');
		navigator.style.setProperty('max-width', available + 'px', 'important');

		navigator.querySelectorAll('.logistica-base-field-control').forEach(function (wrap) {
			wrap.style.setProperty('max-width', '100%', 'important');
			wrap.style.setProperty('width', '100%', 'important');
		});

		navigator.querySelectorAll('.logistica-base-field-control > input, .logistica-base-field-control > select').forEach(function (field) {
			field.style.setProperty('max-width', '100%', 'important');
			field.style.setProperty('width', '100%', 'important');
			field.style.setProperty('box-sizing', 'border-box', 'important');
		});

		var countNode = navigator.querySelector('[data-base-count]');

		if (countNode) {
			countNode.style.setProperty('max-width', '100%', 'important');
			countNode.style.setProperty('width', '100%', 'important');
		}
	}

	function bindClosingBaseNavigatorLayout(workspace) {
		var navigator;
		var sidebar;

		if (!workspace || workspace.dataset.baseNavigatorLayoutBound === '1') {
			return;
		}

		navigator = workspace.querySelector('.logistica-base-navigator');
		sidebar = workspace.querySelector('.logistica-close-sidebar');

		if (!navigator) {
			return;
		}

		workspace.dataset.baseNavigatorLayoutBound = '1';

		if (typeof window.ResizeObserver === 'function' && sidebar) {
			new window.ResizeObserver(function () {
				constrainClosingBaseNavigator(workspace);
			}).observe(sidebar);
		}

		window.addEventListener('resize', function () {
			constrainClosingBaseNavigator(workspace);
		});

		constrainClosingBaseNavigator(workspace);
	}

	function closingBaseCountLine(text, className) {
		var line = document.createElement('span');

		line.className = className || 'logistica-base-count-line';
		line.textContent = text;

		return line;
	}

	function filterClosingBases(workspace) {
		var countElement = workspace.querySelector('[data-base-count]');
		var context = closingBaseFilterContext(workspace);
		var visibleCount = 0;
		var pickedReference = workspace.querySelector('[data-close-reference-pick]:checked');
		var pickedRefElement = pickedReference ? pickedReference.closest('[data-close-reference]') : null;

		if (pickedRefElement && (pickedRefElement.hidden || isReferenceFilteredOut(pickedRefElement))) {
			pickedRefElement = null;
		}

		var blockCount = pickedRefElement ? visibleBlockCountForReference(pickedRefElement) : 0;

		workspace.querySelectorAll('[data-close-base]').forEach(function (base) {
			var hidden = isClosingBaseHidden(base, context);

			if (base.getAttribute('data-just-created-base') === '1') {
				hidden = false;
			}

			base.hidden = hidden;
			if (!hidden) {
				visibleCount += 1;
			}
		});

		if (countElement) {
			var referenceSelect = workspace.querySelector('[data-base-reference-filter]');
			var visibleRefCount = visibleClosingReferences(workspace).length;
			var countLines = [];
			var hintLine = '';

			if (pickedRefElement) {
				countLines.push({
					text: visibleCount + ' bases visibles · ' + blockCount + ' bloques'
						+ (visibleCount !== blockCount ? ' (sincronizando...)' : ''),
					className: 'logistica-base-count-summary'
				});
			} else if (isReferenceFilterPinned(workspace) && referenceSelect) {
				countLines.push({
					text: visibleCount + ' bases · referencia: ' + referenceSelect.value,
					className: 'logistica-base-count-summary'
				});
				if (!isVariantFilterPinned(workspace)) {
					countLines.push({ text: 'Todas las variantes', className: 'logistica-base-count-meta' });
				}
			} else if (isVariantFilterPinned(workspace)) {
				countLines.push({
					text: visibleCount + ' bases · ' + visibleRefCount + ' referencias',
					className: 'logistica-base-count-summary'
				});
				countLines.push({
					text: 'Variante: ' + (workspace.querySelector('[data-base-variant-filter]') ? workspace.querySelector('[data-base-variant-filter]').value : ''),
					className: 'logistica-base-count-meta'
				});
			} else {
				countLines.push({
					text: visibleCount + ' bases · ' + visibleRefCount + ' referencias',
					className: 'logistica-base-count-summary'
				});
				countLines.push({ text: 'Todas las variantes', className: 'logistica-base-count-meta' });
			}

			if (visibleCount === 0 && pickedRefElement && blockCount > 0) {
				hintLine = 'Sincronizando bases con los bloques…';
			} else if (visibleCount === 0 && context.blockLinkIds && context.blockLinkIds.length) {
				hintLine = 'Sincronizando bases con los bloques…';
			} else if (visibleCount === 0 && context.referenceFilter) {
				hintLine = 'Limpie el filtro Referencia o elija otra referencia';
			}

			if (hintLine) {
				countLines.push({ text: hintLine, className: 'logistica-base-count-hint' });
			}

			renderClosingBaseCount(countElement, countLines);
		}

		constrainClosingBaseNavigator(workspace);
		collapseClosingBases(workspace);
	}


	function referenceMatchesVariantScope(nombre, variantFilter) {
		var upper = String(nombre || '').toUpperCase();
		var variant = normalizeText(variantFilter);

		if (!variant) {
			return true;
		}

		if (variant === 'exportacion' && upper.indexOf('TAZ') === 0) {
			return false;
		}

		if (variant === 'nacional' && upper.indexOf('BOWL') === 0) {
			return false;
		}

		return true;
	}

	function referenceFilterValue(reference) {
		return textField(reference, 'nombre') || textField(reference, 'formula_template') || textField(reference, 'unidad_empaque');
	}

	function closingReferenceStableId(reference) {
		return textField(reference, 'id') || (reference ? reference.id || '' : '');
	}

	function isClosingNameEditActive(workspace) {
		return !!(workspace && workspace.getAttribute('data-closing-name-edit-id'));
	}

	function isClosingReferenceNameEditing(workspace, reference) {
		var editId = workspace ? workspace.getAttribute('data-closing-name-edit-id') : '';

		if (!editId || !reference) {
			return false;
		}

		return closingReferenceStableId(reference) === editId;
	}

	function beginClosingReferenceNameEdit(workspace, reference) {
		var select = workspace ? workspace.querySelector('[data-base-reference-filter]') : null;
		var filterValue = select ? select.value : '';
		var nombre = reference ? referenceFilterValue(reference) : '';

		if (!workspace || !reference) {
			return;
		}

		workspace.setAttribute('data-closing-name-edit-id', closingReferenceStableId(reference));
		workspace.setAttribute('data-closing-name-edit-filter', filterValue || nombre);
		if (reference.id) {
			workspace.setAttribute('data-closing-name-edit-anchor', reference.id);
		}
	}

	function finishClosingReferenceNameEdit(workspace, reference) {
		var select = workspace ? workspace.querySelector('[data-base-reference-filter]') : null;
		var newName = reference ? referenceFilterValue(reference) : '';

		if (!workspace || !isClosingNameEditActive(workspace)) {
			return;
		}

		workspace.removeAttribute('data-closing-name-edit-id');
		workspace.removeAttribute('data-closing-name-edit-filter');
		workspace.removeAttribute('data-closing-name-edit-anchor');

		window.clearTimeout(closingNombreAutosaveTimers.get(workspace));
		closingNombreAutosaveTimers.delete(workspace);

		refreshBaseReferenceOptions(workspace);

		if (select && newName) {
			if (!referenceFilterOptionExists(select, newName)) {
				select.appendChild(new Option(newName, newName));
			}

			select.value = newName;
			workspace.setAttribute('data-base-filter-mode', 'active');
		}

		filterClosingReferences(workspace);
		filterClosingBases(workspace);
		autosaveClosingWorkspace(workspace, true);
	}

	function scheduleClosingNombreAutosave(workspace) {
		if (!workspace) {
			return;
		}

		window.clearTimeout(closingNombreAutosaveTimers.get(workspace));
		closingNombreAutosaveTimers.set(workspace, window.setTimeout(function () {
			closingNombreAutosaveTimers.delete(workspace);
			autosaveClosingWorkspace(workspace);
		}, 1200));
	}

	function isReferenceFilterPinned(workspace) {
		var select = workspace ? workspace.querySelector('[data-base-reference-filter]') : null;

		return !!(workspace
			&& workspace.getAttribute('data-base-filter-mode') === 'picker'
			&& select
			&& select.value);
	}

	function setReferenceFilterMode(workspace, pinned, selectedValue) {
		var select = workspace ? workspace.querySelector('[data-base-reference-filter]') : null;

		if (!workspace || !select) {
			return;
		}

		if (pinned && selectedValue) {
			workspace.setAttribute('data-base-filter-mode', 'picker');
			select.value = selectedValue;
			return;
		}

		workspace.setAttribute('data-base-filter-mode', 'active');
		select.value = '';
	}

	function referenceTemplateValue(reference) {
		return textField(reference, 'formula_template') || textField(reference, 'unidad_empaque');
	}

	function closingReferencesForFilter(workspace, referenceFilter, variantFilter) {
		var filter = normalizeText(referenceFilter);

		if (!filter) {
			return Array.from(workspace.querySelectorAll('[data-close-reference]'));
		}

		var directMatches = Array.from(workspace.querySelectorAll('[data-close-reference]')).filter(function (reference) {
			return referenceMatchesFilter(reference, referenceFilter);
		});

		if (variantFilter) {
			return directMatches.filter(function (reference) {
				var referenceVariant = normalizeText(textField(reference, 'variante'));

				if (referenceVariant && referenceVariant !== variantFilter) {
					return false;
				}

				return referenceMatchesVariantScope(textField(reference, 'nombre'), variantFilter);
			});
		}

		return directMatches;
	}

	function lookupKeysForReferences(references) {
		var keys = [];

		references.forEach(function (reference) {
			baseReferenceMatrixLookupKeys(reference).forEach(function (key) {
				var normalizedKey = normalizeText(key);

				if (normalizedKey && keys.indexOf(normalizedKey) === -1) {
					keys.push(normalizedKey);
				}
			});
		});

		return keys;
	}

	function referenceInClosingFilterScope(workspace, reference, referenceFilter, variantFilter) {
		if (!normalizeText(referenceFilter)) {
			return true;
		}

		if (isClosingReferenceNameEditing(workspace, reference)) {
			return true;
		}

		return closingReferencesForFilter(workspace, referenceFilter, variantFilter).indexOf(reference) !== -1;
	}

	function baseMatchesClosingReferenceFilter(baseReference, baseVariant, context) {
		var baseRef = normalizeText(baseReference);
		var baseKeys;
		var keyIndex;

		if (!context.referenceFilter) {
			return true;
		}

		if (!baseRef) {
			return true;
		}

		if (context.referenceFilterKeys && context.referenceFilterKeys.length) {
			baseKeys = closingReferenceKeyAliases(baseRef);

			for (keyIndex = 0; keyIndex < baseKeys.length; keyIndex += 1) {
				if (context.referenceFilterKeys.indexOf(baseKeys[keyIndex]) !== -1) {
					break;
				}
			}

			if (keyIndex >= baseKeys.length) {
				return false;
			}
		} else if (!baseReferenceMatchesFilter(baseRef, context.referenceFilter)) {
			return false;
		}

		if (context.normalizedVariant && baseVariant && baseVariant !== context.normalizedVariant) {
			return false;
		}

		return true;
	}

	function referenceKeyFromFilter(workspace, filterValue) {
		var keys = lookupKeysForReferences(closingReferencesForFilter(workspace, filterValue, ''));

		return keys.length ? keys[0] : filterValue;
	}

	function referenceSearchText(workspace, reference, basesCache) {
		var referenceValue = normalizeText(referenceTemplateValue(reference));
		var variant = normalizeText(textField(reference, 'variante'));
		var relatedBaseText = (basesCache || closeWorkspaceBases(workspace)).filter(function (base) {
			var refKey = baseReferenceKeyForActiveReference(reference);
			var sameReference = !refKey || baseReferenceMatchesFilter(base.referencia, refKey);
			var sameVariant = !variant || !base.variante || base.variante === variant;

			return sameReference && sameVariant;
		}).map(function (base) {
			return [
				base.referencia,
				base.variante,
				base.tipo,
				base.cod,
				base.lote,
				base.codSaldo,
				base.loteSaldo
			].join(' ');
		}).join(' ');

		return normalizeText([
			textField(reference, 'nombre'),
			textField(reference, 'variante'),
			textField(reference, 'tipo_producto'),
			textField(reference, 'formula_template'),
			textField(reference, 'unidad_empaque'),
			relatedBaseText
		].join(' '));
	}

	function setClosingFilterEmpty(workspace, show) {
		var panel = workspace ? workspace.querySelector('[data-close-filter-empty]') : null;

		if (panel) {
			panel.hidden = !show;
		}
	}

	function visibleClosingReferences(workspace) {
		return Array.from(workspace.querySelectorAll('[data-close-reference]')).filter(function (reference) {
			return !reference.hidden && !isReferenceFilteredOut(reference);
		});
	}

	function syncReferencePickToVisibleFilter(workspace) {
		var referenceSelect = workspace.querySelector('[data-base-reference-filter]');
		var referenceFilter = referenceSelect ? referenceSelect.value : '';
		var syncedPick = workspace.querySelector('[data-close-reference-pick]:checked');

		if (syncedPick) {
			var pickReference = syncedPick.closest('[data-close-reference]');

			if (pickReference && !pickReference.hidden && !isReferenceFilteredOut(pickReference)) {
				syncReferencePickState(workspace);
				return;
			}

			syncedPick.checked = false;
		}

		clearHiddenReferencePicks(workspace);

		if (referenceFilter && isReferenceFilterPinned(workspace)) {
			setReferenceFilterMode(workspace, true, referenceFilter);
		} else if (referenceFilter) {
			workspace.setAttribute('data-base-filter-mode', 'active');
			showAllFilteredClosingReferences(workspace);
		} else {
			setReferenceFilterMode(workspace, false);
			showAllFilteredClosingReferences(workspace);
		}

		syncReferencePickState(workspace);
	}

	function scheduleClosingFilters(workspace) {
		var timerId;

		if (!workspace) {
			return;
		}

		timerId = closingFilterTimers.get(workspace);

		if (timerId) {
			window.clearTimeout(timerId);
		}

		closingFilterTimers.set(workspace, window.setTimeout(function () {
			closingFilterTimers.delete(workspace);
			syncClosingReferencesFromFilters(workspace, { skipWorkspaceUpdate: true });
		}, 120));
	}

	function syncClosingReferencesFromFilters(workspace, options) {
		var searchInput = workspace.querySelector('[data-base-search]');
		var variantSelect = workspace.querySelector('[data-base-variant-filter]');
		var referenceSelect = workspace.querySelector('[data-base-reference-filter]');
		var search = normalizeText(searchInput ? searchInput.value : '');
		var variantFilter = normalizeText(variantSelect ? variantSelect.value : '');
		var referenceFilter = referenceSelect ? referenceSelect.value : '';
		var hasFilter = search || variantFilter || referenceFilter;
		var visible = [];
		var firstReference = workspace.querySelector('[data-close-reference]');
		var basesCache = closeWorkspaceBases(workspace);
		var skipWorkspaceUpdate = options && options.skipWorkspaceUpdate;

		workspace.querySelectorAll('[data-close-reference]').forEach(function (reference) {
			var haystack = referenceSearchText(workspace, reference, basesCache);
			var referenceVariant = normalizeText(textField(reference, 'variante'));
			var hidden = false;

			if (reference.hasAttribute('data-reference-seed')) {
				reference.hidden = true;
				return;
			}

			if (reference.hasAttribute('data-new-reference')) {
				setReferenceFilterVisibility(reference, false);
				reference.hidden = false;
				visible.push(reference);
				return;
			}

			if (search && haystack.indexOf(search) === -1) {
				hidden = true;
			}

			if (!hidden && variantFilter && referenceVariant !== variantFilter) {
				hidden = true;
			}

			if (!hidden && variantFilter && !referenceMatchesVariantScope(textField(reference, 'nombre'), variantFilter)) {
				hidden = true;
			}

			if (!hidden && referenceFilter && !referenceInClosingFilterScope(workspace, reference, referenceFilter, variantFilter)) {
				hidden = true;
			}

			setReferenceFilterVisibility(reference, hidden);

			if (!hidden) {
				reference.hidden = false;
				visible.push(reference);
			}
		});

		if (visible.length) {
			setClosingFilterEmpty(workspace, false);
			showAllFilteredClosingReferences(workspace);
		} else if (hasFilter) {
			workspace.querySelectorAll('[data-close-reference]').forEach(function (reference) {
				setReferenceFilterVisibility(reference, true);
			});
			clearHiddenReferencePicks(workspace);
			syncReferencePickState(workspace);
			setClosingFilterEmpty(workspace, true);
		} else {
			setClosingFilterEmpty(workspace, false);
			showAllFilteredClosingReferences(workspace);
		}

		syncReferencePickToVisibleFilter(workspace);

		var syncedPick = workspace.querySelector('[data-close-reference-pick]:checked');
		var autoReference;

		if (!syncedPick) {
			autoReference = autoPickClosingReferenceBases(workspace);
			if (autoReference) {
				syncedPick = autoReference.querySelector('[data-close-reference-pick]');
			}
		}

		if (syncedPick) {
			applyReferencePickFilter(workspace, syncedPick);
		} else {
			filterClosingBases(workspace);
		}

		if (!skipWorkspaceUpdate) {
			updateClosingWorkspace(workspace);
		}
	}

	function filterClosingReferences(workspace, options) {
		syncClosingReferencesFromFilters(workspace, options);
	}

	function activeClosingVariant(workspace) {
		var activeReference = visibleClosingReferences(workspace)[0] || workspace.querySelector('[data-close-reference]');
		var selected = activeReference ? activeReference.querySelector('[data-field="variante_selector"]') : null;
		var typed = activeReference ? activeReference.querySelector('[data-field="variante"]') : null;

		if (selected && selected.value && selected.value !== '__new__') {
			return selected.value;
		}

		return typed ? typed.value : '';
	}

	function activeClosingBaseReference(workspace) {
		var activeReference = Array.from(workspace.querySelectorAll('[data-close-reference]')).find(function (reference) {
			return !reference.hidden;
		});

		if (!activeReference) {
			return '';
		}

		return baseReferenceKeyForActiveReference(activeReference);
	}

	function referenceMatchesFilter(reference, filterValue) {
		var filter = normalizeText(filterValue);

		if (!filter) {
			return true;
		}

		if (filter && normalizeText(textField(reference, 'nombre')) === filter) {
			return true;
		}

		return [
			textField(reference, 'formula_template'),
			textField(reference, 'unidad_empaque')
		].some(function (value) {
			return !!String(value || '').trim() && normalizeText(value) === filter;
		});
	}

	function refreshBaseReferenceOptions(workspace) {
		var select = workspace.querySelector('[data-base-reference-filter]');
		var variantSelect = workspace.querySelector('[data-base-variant-filter]');
		var variantFilter = variantSelect ? variantSelect.value : '';
		var editingFilter = workspace.getAttribute('data-closing-name-edit-filter') || '';
		var current = select ? select.value : '';
		var groups = {};
		var groupNames;
		var option;
		var optgroup;

		if (!select) {
			return;
		}

		if (isClosingNameEditActive(workspace) && editingFilter) {
			current = editingFilter;
		}

		Array.from(select.querySelectorAll('optgroup, option:not(:first-child)')).forEach(function (existing) {
			existing.remove();
		});

		workspace.querySelectorAll('[data-close-reference]').forEach(function (reference) {
			var nombre = referenceFilterValue(reference);
			var tipo = textField(reference, 'tipo_producto') || 'Otro';

			if (!nombre) {
				return;
			}

			if (!referenceMatchesVariantScope(nombre, variantFilter)) {
				return;
			}

			if (!groups[tipo]) {
				groups[tipo] = [];
			}

			if (groups[tipo].indexOf(nombre) === -1) {
				groups[tipo].push(nombre);
			}
		});

		groupNames = Object.keys(groups).sort(function (a, b) {
			return a.localeCompare(b, 'es', { numeric: true, sensitivity: 'base' });
		});

		groupNames.forEach(function (tipo) {
			groups[tipo].sort(function (a, b) {
				return a.localeCompare(b, 'es', { numeric: true, sensitivity: 'base' });
			});

			optgroup = document.createElement('optgroup');
			optgroup.label = tipo;
			groups[tipo].forEach(function (value) {
				option = document.createElement('option');
				option.value = value;
				option.textContent = value;
				optgroup.appendChild(option);
			});
			select.appendChild(optgroup);
		});

		if (isClosingNameEditActive(workspace) && editingFilter) {
			if (!referenceFilterOptionExists(select, editingFilter)) {
				option = document.createElement('option');
				option.value = editingFilter;
				option.textContent = editingFilter;
				select.appendChild(option);
			}

			select.value = editingFilter;
		} else if (current && Array.from(select.options).some(function (existingOption) {
			return existingOption.value === current;
		})) {
			select.value = current;
		} else {
			select.value = '';
		}
	}

	function referenceFilterOptionExists(select, value) {
		if (!select || !value) {
			return false;
		}

		return Array.from(select.options).some(function (option) {
			return option.value === value;
		});
	}

	function baseTypeLabelFromRow(row, baseType) {
		var select = row ? row.querySelector('[data-field="base_tipo"]') : null;

		if (select && select.options[select.selectedIndex]) {
			return select.options[select.selectedIndex].textContent.trim();
		}

		return baseType || 'Base';
	}

	function closingBaseCodLoteMissingDetails(base, row, lineType) {
		var items = [];

		if (!base) {
			return items;
		}

		var baseLabel = baseTypeLabelFromRow(row, textField(row, 'base_tipo'));
		var useSaldo = lineType === 'caja_saldos' || base.tipo === 'cajas_saldo';
		var cod = base.cod;
		var lote = base.lote;

		if (!normalizeText(cod)) {
			items.push((useSaldo ? 'Cod. saldo' : 'Código') + ' en base (' + baseLabel + ')');
		}

		if (!normalizeText(lote)) {
			items.push((useSaldo ? 'Lote saldo' : 'Lote') + ' en base (' + baseLabel + ')');
		}

		return items;
	}

	function closingLineMissingDetails(reference, row, baseType, produced, qty, packages, unitsPerPackage, base, lineType) {
		var items = [];
		var lineLabel = textField(row, 'label') || textField(row, 'tipo') || 'esta fila';
		var resolvedLineType = lineType || textField(row, 'tipo');
		var qtyRequired = baseType !== 'rollos' && baseType !== 'bolsas' && baseType !== 'papel_bobinas' && baseType !== 'plastico' && baseType !== 'acetato';

		if (!produced) {
			items.push('Unid. producidas (cabecera de la referencia)');
		}

		if (baseType === 'plastico' && base) {
			if (!base.ancho) {
				items.push('Ancho (cm) en base plástico');
			}

			if (!base.largo) {
				items.push('Largo (cm) en base plástico');
			}

			if (!base.undMolde) {
				items.push('Und x molde en base plástico');
			}

			if (!base.pesoTamano) {
				items.push('Peso tamaño (gr) en base plástico');
			}
		}

		if (baseType === 'acetato' && base) {
			if (!base.ancho) {
				items.push('Ancho lámina (cm) en base acetato');
			}

			if (!base.largo) {
				items.push('Largo lámina (cm) en base acetato');
			}

			if (!(base.undMolde > 0 || unitsPerPackage > 0)) {
				items.push('Cortes por lámina (despiece): en la base o en Und x paquete');
			}
		}

		if (resolvedLineType === 'caja_saldos' || isCajaSaldoRow(row)) {
			if (produced && !closingCajasEmpaqueSize(closingMainCajasRow(reference))) {
				items.push('Fila Cajas: paquetes x und x paquete (unidad de empaque, ej. 60 x 50 = 3000)');
			}

			if (qty > 0 && !closingSaldoUnitsPerPackage(row, reference)) {
				items.push('Und x paquete en Cajas saldo (tamaño del paquete de saldo, ej. 50)');
			}
		} else if (qtyRequired && !qty) {
			if (!packages) {
				items.push('Paquetes en "' + lineLabel + '"');
			}

			if (!unitsPerPackage) {
				items.push('Und x paquete en "' + lineLabel + '"');
			}
		}

		closingBaseCodLoteMissingDetails(base, row, resolvedLineType).forEach(function (item) {
			if (items.indexOf(item) === -1) {
				items.push(item);
			}
		});

		return items.length ? items : ['Revise los campos de la fila'];
	}

	function closingStateDetails(state, context) {
		if (!context) {
			return [];
		}

		if (state === 'is-missing') {
			return closingLineMissingDetails(
				context.reference,
				context.row,
				context.baseType,
				context.produced,
				context.qty,
				context.packages,
				context.unitsPerPackage,
				context.base,
				context.lineType
			);
		}

		if (state === 'is-pending') {
			return ['Seleccione una fórmula en la columna Fórmula'];
		}

		if (state === 'is-error' && !context.base) {
			return [
				'No existe base permanente para:',
				'Tipo: ' + baseTypeLabelFromRow(context.row, context.baseType),
				'Variante: ' + (context.variant || 'activa'),
				'Referencia: ' + (context.unit || 'activa'),
				'Agregue un bloque de cálculo en la referencia; la base se creará al vincularse'
			];
		}

		if (state === 'is-error') {
			var errorItems = ['Revise fórmula, consumo o ajuste de la base'];

			closingBaseCodLoteMissingDetails(context.base, context.row, context.lineType).forEach(function (item) {
				if (errorItems.indexOf(item) === -1) {
					errorItems.push(item);
				}
			});

			return errorItems;
		}

		return [];
	}

	function populateClosingStatusLabel(label, message) {
		var slashAt = String(message || '').indexOf('/');

		label.textContent = '';

		if (slashAt === -1) {
			label.textContent = message || '';
			return;
		}

		label.appendChild(document.createTextNode(message.slice(0, slashAt + 1)));

		var tail = document.createElement('span');
		tail.className = 'logistica-close-status-tail';
		tail.textContent = message.slice(slashAt + 1);
		label.appendChild(tail);
	}

	function setClosingState(row, state, message, details, outputKey) {
		var output = outputKey || 'estado';
		var stateCell = row.querySelector('[data-output="' + output + '"]');
		var detailList = Array.isArray(details) ? details.filter(Boolean) : (details ? [String(details)] : []);
		var formulaField = output === 'estado' ? row.querySelector('[data-field="estado_formula"]') : null;
		var cacheAttr = output === 'estado' ? 'data-close-state-cache' : 'data-row-state-cache-' + output;
		var cacheKey = state + '|' + message + '|' + detailList.join(';');

		if (row.getAttribute(cacheAttr) === cacheKey) {
			return;
		}

		row.setAttribute(cacheAttr, cacheKey);

		if (output === 'estado') {
			row.classList.remove('is-formula', 'is-pending', 'is-missing', 'is-error', 'is-ok');
			row.classList.add(state);
		} else if (output === 'validacion') {
			row.classList.remove('is-calc-missing', 'is-calc-ok');
			row.classList.add(state === 'is-missing' ? 'is-calc-missing' : 'is-calc-ok');
		}

		if (formulaField) {
			formulaField.value = state.replace('is-', '');
		}

		if (!stateCell) {
			return;
		}

		stateCell.textContent = '';
		stateCell.classList.toggle('has-status-detail', detailList.length > 0);

		if (!detailList.length) {
			if (String(message || '').indexOf('/') !== -1) {
				var plainLabel = document.createElement('span');
				plainLabel.className = 'logistica-close-status-label';
				populateClosingStatusLabel(plainLabel, message);
				stateCell.appendChild(plainLabel);
			} else {
				stateCell.textContent = message;
			}
			return;
		}

		var status = document.createElement('span');
		var hint = document.createElement('span');
		var popover = document.createElement('span');
		var list = document.createElement('ul');
		var detailText = detailList.join('\n');

		var label = document.createElement('span');

		status.className = 'logistica-close-status';
		label.className = 'logistica-close-status-label';
		populateClosingStatusLabel(label, message);
		status.setAttribute('title', 'Falta: ' + detailList.join(', '));
		status.setAttribute('tabindex', '0');
		status.setAttribute('role', 'button');
		status.setAttribute('aria-expanded', 'false');
		status.setAttribute('aria-label', message + '. Falta: ' + detailList.join(', '));

		hint.className = 'logistica-close-status-hint';
		hint.setAttribute('aria-hidden', 'true');
		hint.textContent = 'i';

		popover.className = 'logistica-close-status-detail';
		detailList.forEach(function (item) {
			var li = document.createElement('li');
			li.textContent = item;
			list.appendChild(li);
		});
		popover.appendChild(list);
		status.appendChild(label);
		status.appendChild(hint);
		status.appendChild(popover);
		stateCell.appendChild(status);
		stateCell.setAttribute('data-status-detail', detailText);
	}

	var closingStatusPortalHideTimer = null;
	var closingStatusActivePortal = null;
	var closingStatusActiveAnchor = null;

	function hideAllClosingStatusPortals() {
		if (closingStatusPortalHideTimer) {
			clearTimeout(closingStatusPortalHideTimer);
			closingStatusPortalHideTimer = null;
		}

		if (closingStatusActivePortal) {
			closingStatusActivePortal.remove();
			closingStatusActivePortal = null;
			closingStatusActiveAnchor = null;
		}
	}

	function scheduleHideClosingStatusPortal() {
		if (closingStatusPortalHideTimer) {
			clearTimeout(closingStatusPortalHideTimer);
		}

		closingStatusPortalHideTimer = setTimeout(function () {
			var pinned = document.querySelector('.logistica-close-status.is-open');

			if (pinned) {
				showClosingStatusPortal(pinned);
				return;
			}

			var hoveredStatus = document.querySelector('.logistica-close-status:hover');

			if (hoveredStatus) {
				showClosingStatusPortal(hoveredStatus);
				return;
			}

			hideAllClosingStatusPortals();
		}, 140);
	}

	function positionClosingStatusPortal(portal, anchor) {
		var anchorRect = anchor.getBoundingClientRect();
		var portalWidth = portal.offsetWidth;
		var portalHeight = portal.offsetHeight;
		var margin = 8;
		var gap = 6;
		var top = anchorRect.bottom + gap;
		var left = anchorRect.left;

		if (left + portalWidth > window.innerWidth - margin) {
			left = Math.max(margin, anchorRect.right - portalWidth);
		}

		if (left < margin) {
			left = margin;
		}

		if (top + portalHeight > window.innerHeight - margin) {
			top = Math.max(margin, anchorRect.top - portalHeight - gap);
		}

		portal.style.top = top + 'px';
		portal.style.left = left + 'px';
	}

	function showClosingStatusPortal(anchor) {
		var source = anchor.querySelector('.logistica-close-status-detail');

		if (!source) {
			return;
		}

		if (closingStatusActiveAnchor === anchor && closingStatusActivePortal) {
			positionClosingStatusPortal(closingStatusActivePortal, anchor);
			return;
		}

		hideAllClosingStatusPortals();

		var portal = source.cloneNode(true);

		portal.classList.remove('logistica-close-status-detail');
		portal.classList.add('logistica-close-status-portal', 'is-visible');
		portal.setAttribute('role', 'tooltip');
		portal.style.visibility = 'hidden';
		document.body.appendChild(portal);
		positionClosingStatusPortal(portal, anchor);
		portal.style.visibility = 'visible';

		portal.addEventListener('mouseenter', function () {
			if (closingStatusPortalHideTimer) {
				clearTimeout(closingStatusPortalHideTimer);
				closingStatusPortalHideTimer = null;
			}
		});

		portal.addEventListener('mouseleave', function () {
			if (!anchor.classList.contains('is-open')) {
				scheduleHideClosingStatusPortal();
			}
		});

		closingStatusActivePortal = portal;
		closingStatusActiveAnchor = anchor;
	}

	function setClosingOutput(row, cod, lote, unitkg, adjusted) {
		var consumo = unitkg > 0 ? adjusted : 0;

		outputText(row, 'cod', cod || '');
		outputText(row, 'lote', lote || '');
		output(row, 'unitkg', consumo, 0, true);
		output(row, 'ajustado', adjusted, 0, true);
	}

	function closingRowDisplayedConsumo(unitkg, adjusted) {
		return unitkg > 0 ? adjusted : 0;
	}

	function mergeClosingCajaSaldoConsumoSameCod(entries) {
		var cajasEntry = null;
		var saldoEntry = null;
		var merged;
		var index;

		if (!entries || !entries.length) {
			return;
		}

		for (index = 0; index < entries.length; index += 1) {
			if (entries[index].isSaldo) {
				saldoEntry = entries[index];
			} else if (entries[index].isCajas) {
				cajasEntry = entries[index];
			}
		}

		if (!cajasEntry || !saldoEntry || !cajasEntry.row || !saldoEntry.row) {
			return;
		}

		if (!cajasEntry.cod || cajasEntry.cod !== saldoEntry.cod) {
			return;
		}

		merged = closingRowDisplayedConsumo(cajasEntry.unitkg, cajasEntry.adjusted)
			+ closingRowDisplayedConsumo(saldoEntry.unitkg, saldoEntry.adjusted);

		setClosingOutput(cajasEntry.row, cajasEntry.cod, cajasEntry.lote, merged > 0 ? merged : 0, merged);
		setClosingOutput(saldoEntry.row, saldoEntry.cod, saldoEntry.lote, 0, 0);
	}

	function isClosingMatrixMetricField(target) {
		var fieldName = target && target.getAttribute ? target.getAttribute('data-field') : '';

		if (!fieldName) {
			return false;
		}

		if (target.closest('[data-close-line]') && ['paquetes', 'unidades_paquete'].indexOf(fieldName) !== -1) {
			return true;
		}

		return target.closest('[data-close-reference]') && fieldName === 'unidades';
	}

	function scheduleUpdateClosingWorkspace(workspace, options) {
		var delay;
		var timerId;

		if (!workspace) {
			return;
		}

		options = options || {};
		delay = options.immediate ? 0 : 60;
		timerId = closingWorkspaceUpdateTimers.get(workspace);

		if (timerId) {
			window.clearTimeout(timerId);
		}

		closingWorkspaceUpdateTimers.set(workspace, window.setTimeout(function () {
			closingWorkspaceUpdateTimers.delete(workspace);

			if (options.scopeReference) {
				updateClosingReference(workspace, options.scopeReference, {
					skipBaseCards: true
				});
				return;
			}

			updateClosingWorkspace(workspace);
		}, delay));
	}

	function updateClosingReference(workspace, reference, options) {
		var basesCache = closeWorkspaceBases(workspace);
		var unitKey = baseReferenceKeyForActiveReference(reference);
		var productType = normalizeText(textField(reference, 'tipo_producto') + ' ' + textField(reference, 'nombre') + ' ' + unitKey);
		var variant = textField(reference, 'variante');
		var isTapas = productType.indexOf('tapa') !== -1;
		var produced = closeNumber(reference, 'unidades');
		var errors = 0;
		var missing = 0;
		var pending = 0;

		options = options || {};

		if (!options.skipBaseCards) {
			updateClosingBaseCards(workspace);
		}

		var sharedPackSource = resolveClosingSharedPackSource(reference);
		if (sharedPackSource) {
			syncClosingSharedUnitsPerPackage(reference, sharedPackSource);
		}

		var cajaSaldoConsumoEntries = [];

		reference.querySelectorAll('[data-close-line]').forEach(function (row) {
				var type = textField(row, 'tipo');
				var isSaldoRow = isCajaSaldoRow(row);
				var baseType = textField(row, 'base_tipo');
				var formula = textField(row, 'formula');
				var packages = closeNumber(row, 'paquetes');
				var unitsPerPackage = closeNumber(row, 'unidades_paquete');
				var qty = packages > 0 || unitsPerPackage > 0 ? packages * unitsPerPackage : closeNumber(row, 'cantidad');
				var qtyInput = row.querySelector('[data-field="cantidad"]');
				var saldo = 0;
				var qtyMissing;

				if (isSaldoRow) {
					type = 'caja_saldos';
					ensureCajaSaldoRowFormula(row);
					formula = 'saldos / cajas';
					saldo = closingCajaSaldoQty(produced, reference);
				}
				var base = findClosingBaseForRow(workspace, row, reference, basesCache);
				var unitkg = 0;
				var adjusted = 0;
				var cod = base ? base.cod : '';
				var lote = base ? base.lote : '';

				if (type === 'plastico_kg' && !isTapas && row.classList.contains('is-plastic-only')) {
					row.hidden = true;
					return;
				}

				row.hidden = false;

				if (isSaldoRow) {
					qty = applyCajaSaldoRowMetrics(row, reference, Math.max(0, saldo));
					packages = closeNumber(row, 'paquetes');
					unitsPerPackage = closeNumber(row, 'unidades_paquete');
				}

				if (type === 'rollos') {
					qty = 0;
					outputText(row, 'roll_total', '-');
				}

				if (qtyInput) {
					qtyInput.value = qty ? String(qty) : '';
				}
				output(row, 'cantidad_total', qty, 0, true);

				var stateContext = {
					reference: reference,
					row: row,
					baseType: baseType,
					base: base,
					variant: variant,
					unit: unitKey,
					lineType: type,
					produced: produced,
					qty: qty,
					packages: packages,
					unitsPerPackage: unitsPerPackage
				};
				var codLoteMissing = base ? closingBaseCodLoteMissingDetails(base, row, type) : [];
				var qtyRequired = baseType !== 'rollos' && baseType !== 'bolsas' && baseType !== 'papel_bobinas' && baseType !== 'plastico' && baseType !== 'acetato';

				if (isSaldoRow) {
					qtyMissing = !produced
						|| !closingCajasEmpaqueSize(closingMainCajasRow(reference))
						|| (qty > 0 && !closingSaldoUnitsPerPackage(row, reference));
				} else if (baseType === 'plastico') {
					qtyMissing = !produced
						|| !base
						|| !base.pesoTamano
						|| !base.undMolde
						|| !base.ancho
						|| !base.largo;
				} else if (baseType === 'acetato') {
					qtyMissing = !produced
						|| !base
						|| !(base.undMolde > 0 || unitsPerPackage > 0)
						|| !base.ancho
						|| !base.largo;
				} else {
					qtyMissing = qtyRequired && !qty;
				}

				if (!formula) {
					pending += 1;
					setClosingOutput(row, cod, lote, 0, 0);
					setClosingState(row, 'is-pending', 'Pendiente de fórmula', closingStateDetails('is-pending', stateContext));
					return;
				}

				if (!base) {
					errors += 1;
					setClosingOutput(row, '', '', 0, 0);
					setClosingState(row, 'is-error', 'Base rota/inexistente', closingStateDetails('is-error', stateContext));
					return;
				}

				if (isSaldoRow) {
					cod = base.codSaldo || base.cod;
					lote = base.loteSaldo || base.lote;
					unitkg = packages > 0 ? packages : (qty > 0 ? 1 : 0);
					adjusted = unitkg > 0 ? unitkg + base.ajuste : 0;
				} else if (baseType === 'rollos') {
					unitkg = base.rendimiento > 0 ? (produced * base.rendimiento) / 1000 : 0;
					adjusted = unitkg > 0 ? Math.round(unitkg * 10) / 10 + base.ajuste : 0;
				} else if (baseType === 'plastico') {
					unitkg = closingPlasticUnitKg(base.pesoTamano, base.undMolde);
					adjusted = closingPlasticTotalKg(produced, base.pesoTamano, base.undMolde);
					adjusted = adjusted > 0 ? Math.ceil(adjusted) + base.ajuste : 0;
				} else if (baseType === 'acetato') {
					// Despiece = cortes por lámina (base) o Und x paquete de la fila.
					qty = base.undMolde > 0 ? base.undMolde : (unitsPerPackage > 0 ? unitsPerPackage : 0);
					if (qty > 0 && base.node && !(base.undMolde > 0)) {
						var acetatoUndInput = base.node.querySelector('[data-field="und_molde"]');
						if (acetatoUndInput) {
							acetatoUndInput.value = String(qty);
							base.undMolde = qty;
						}
					}
					if (qtyInput) {
						qtyInput.value = qty ? String(qty) : '';
					}
					if (!(packages > 0) && qty > 0) {
						packages = 1;
					}
					unitsPerPackage = qty > 0 ? qty : 0;
					setTextField(row, 'paquetes', packages > 0 ? String(packages) : '');
					setTextField(row, 'unidades_paquete', unitsPerPackage > 0 ? String(unitsPerPackage) : '');
					output(row, 'cantidad_total', qty, 0, true);
					unitkg = qty > 0 ? Math.ceil(produced / qty) : 0;
					adjusted = unitkg > 0 ? unitkg + base.ajuste : 0;
				} else if (baseType === 'papel_hojas' || baseType === 'microcorrugado') {
					unitkg = qty;
					adjusted = unitkg > 0 ? Math.ceil(unitkg) + base.ajuste : 0;
				} else if (baseType === 'papel_bobinas') {
					unitkg = base.rendimiento > 0 ? (produced * base.rendimiento) / 1000 : 0;
					adjusted = unitkg > 0 ? Math.round(unitkg * 10) / 10 + base.ajuste : 0;
				} else if (baseType === 'cajas' || type === 'cajas') {
					unitkg = closingCajasEnterasConsumidas(produced, qty);
					adjusted = unitkg > 0 ? unitkg + base.ajuste : 0;
				} else {
					unitkg = qty > 0 ? produced / qty : 0;
					adjusted = unitkg > 0 ? Math.ceil(unitkg) + base.ajuste : 0;
				}
				if (baseType === 'rollos' || baseType === 'papel_bobinas' || baseType === 'plastico' || baseType === 'acetato') {
					outputText(row, 'cod', cod || '');
					outputText(row, 'lote', lote || '');
					if (baseType === 'plastico') {
						output(row, 'unitkg', unitkg, 4);
					} else if (baseType === 'acetato') {
						output(row, 'unitkg', unitkg, 0, true);
					} else {
						output(row, 'unitkg', unitkg > 0 ? Math.ceil(adjusted) : 0, 0, true);
					}
					output(
						row,
						'ajustado',
						adjusted,
						baseType === 'plastico' ? 2 : (baseType === 'rollos' || baseType === 'papel_bobinas' ? 1 : 0),
						baseType !== 'plastico' && baseType !== 'rollos' && baseType !== 'papel_bobinas'
					);
				} else {
					setClosingOutput(row, cod, lote, unitkg, adjusted);
				}

				if (isSaldoRow || baseType === 'cajas' || type === 'cajas') {
					cajaSaldoConsumoEntries.push({
						row: row,
						isSaldo: isSaldoRow,
						isCajas: !isSaldoRow && (baseType === 'cajas' || type === 'cajas'),
						cod: normalizeClosingCod(cod),
						lote: lote,
						unitkg: unitkg,
						adjusted: adjusted
					});
				}

				if (!produced || qtyMissing || codLoteMissing.length > 0) {
					missing += 1;
					setClosingState(row, 'is-missing', 'Faltan datos', closingStateDetails('is-missing', stateContext));
				} else if (!Number.isFinite(unitkg) || adjusted < 0) {
					errors += 1;
					setClosingState(row, 'is-error', 'Revisar fórmula', closingStateDetails('is-error', stateContext));
				} else {
					setClosingState(row, 'is-formula', isSaldoRow ? 'Saldo automático' : 'OK');
				}
			});

			mergeClosingCajaSaldoConsumoSameCod(cajaSaldoConsumoEntries);

			outputText(reference, 'reference_status', errors ? 'Hay posibles errores o referencias rotas' : (missing ? 'Hay datos faltantes' : (pending ? 'Hay campos pendientes de formulación' : 'Referencia formulada correctamente')));

		reference.querySelectorAll('[data-action="add-close-saldo-block"]').forEach(function (saldoButton) {
			saldoButton.hidden = !!referenceCajaSaldoRow(reference) || !closingMainCajasRow(reference);
		});
	}

	function updateClosingWorkspace(workspace) {
		hideAllClosingStatusPortals();
		updateClosingBaseCards(workspace);
		workspace.querySelectorAll('[data-close-reference]').forEach(function (reference) {
			updateClosingReference(workspace, reference, { skipBaseCards: true });
		});
	}

	function equivalentReference(source, target, selectedVariant) {
		var sameVariant = normalizeText(textField(source, 'variante')) === selectedVariant;

		return source !== target && sameVariant && sameReferenceFamily(source, target);
	}

	function findClosingVariantReference(workspace, reference, variant) {
		var normalizedVariant = normalizeText(variant);

		if (!workspace || !reference || !normalizedVariant) {
			return null;
		}

		if (normalizeText(textField(reference, 'variante')) === normalizedVariant) {
			return reference;
		}

		return Array.from(workspace.querySelectorAll('[data-close-reference]')).find(function (candidate) {
			return equivalentReference(candidate, reference, normalizedVariant);
		}) || null;
	}

	function setClosingReferenceEmpty(reference, isEmpty, selectedVariant) {
		var empty = reference ? reference.querySelector('[data-close-ref-empty]') : null;
		var body = reference ? reference.querySelector('[data-close-ref-body]') : null;
		var select = reference ? reference.querySelector('[data-field="variante_selector"]') : null;

		if (!reference) {
			return;
		}

		if (isEmpty) {
			reference.setAttribute('data-variant-empty', '1');
			if (select && selectedVariant) {
				select.value = selectedVariant;
			}
			if (empty) {
				empty.hidden = false;
			}
			if (body) {
				body.hidden = true;
			}
			outputText(reference, 'reference_status', 'Sin referencias creadas');
			return;
		}

		reference.removeAttribute('data-variant-empty');
		if (empty) {
			empty.hidden = true;
		}
		if (body) {
			body.hidden = false;
		}
	}

	function sameReferenceFamily(source, target) {
		var sourceName = normalizeText(textField(source, 'nombre'));
		var targetName = normalizeText(textField(target, 'nombre'));
		var sourceTemplate = normalizeText(textField(source, 'formula_template'));
		var targetTemplate = normalizeText(textField(target, 'formula_template'));
		var sourceUnit = normalizeText(textField(source, 'unidad_empaque'));
		var targetUnit = normalizeText(textField(target, 'unidad_empaque'));

		if (sourceName && targetName && sourceName === targetName) {
			return true;
		}

		if (sourceTemplate && targetTemplate && sourceTemplate === targetTemplate) {
			return true;
		}

		if (sourceUnit && targetUnit && sourceUnit === targetUnit) {
			return true;
		}

		return false;
	}

	var CLOSING_KNOWN_VARIANT_THEMES = {
		nacional: true,
		exportacion: true,
		bionatura: true,
		estandar: true,
		'sin-variante': true
	};

	function closingVariantThemeSlug(variant) {
		var slug = normalizeText(variant).replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '');

		return slug || 'sin-variante';
	}

	function closingVariantThemeClasses(variant) {
		var slug = closingVariantThemeSlug(variant);
		var classes = ['is-variant-' + slug];

		if (!CLOSING_KNOWN_VARIANT_THEMES[slug]) {
			classes.push('is-variant-custom');
		}

		return classes;
	}

	function syncClosingVariantChrome(element) {
		var variant = '';
		var select;

		if (!element) {
			return;
		}

		if (element.matches('[data-close-reference]')) {
			variant = textField(element, 'variante');
			select = element.querySelector('[data-field="variante_selector"]');

			if (!variant && select && select.value && select.value !== '__new__') {
				variant = select.value;
			}
		} else if (element.matches('[data-close-base]')) {
			variant = textField(element, 'variante');
		}

		Array.from(element.classList).forEach(function (className) {
			if (className.indexOf('is-variant-') === 0) {
				element.classList.remove(className);
			}
		});

		closingVariantThemeClasses(variant).forEach(function (className) {
			element.classList.add(className);
		});
		element.setAttribute('data-variant-theme', closingVariantThemeSlug(variant));
	}

	function syncClosingWorkspaceVariantChrome(workspace) {
		if (!workspace) {
			return;
		}

		workspace.querySelectorAll('[data-close-reference], [data-close-base]').forEach(syncClosingVariantChrome);
	}

	function setReferenceVariant(reference, variant) {
		var input = reference.querySelector('[data-field="variante"]');
		var select = reference.querySelector('[data-field="variante_selector"]');

		if (input) {
			input.value = variant;
			input.hidden = true;
		}

		if (select) {
			select.value = variant;
		}

		syncClosingVariantChrome(reference);
	}

	function activateClosingReference(reference) {
		var workspace = reference.closest('[data-close-workspace]');
		var pick = reference.querySelector('[data-close-reference-pick]');

		if (!isReferenceFilteredOut(reference)) {
			if (isVariantFilterPinned(workspace) || (pick && pick.checked)) {
				showClosingReference(reference);
			} else {
				showAllFilteredClosingReferences(workspace);
			}
		}

		if (pick && pick.checked && !isReferenceFilteredOut(reference)) {
			applyReferencePickFilter(workspace, pick);
		} else {
			filterClosingBases(workspace);
		}

		updateClosingWorkspace(workspace);
	}

	function createVariantReference(reference, variant) {
		var workspace = reference.closest('[data-close-workspace]');
		var refs = workspace.querySelector('[data-close-references]');
		var clone = reference.cloneNode(true);

		clone.hidden = false;
		clone.querySelectorAll('[data-field="id"]').forEach(function (input) {
			input.value = 'ref-' + Date.now();
		});
		setReferenceVariant(clone, variant);
		refs.appendChild(clone);
		setClosingReferenceMetaLocked(clone, true);
		reindexClosingWorkspace(workspace);

		return clone;
	}

	function loadClosingVariant(input) {
		var reference = input.closest('[data-close-reference]');
		var workspace = input.closest('[data-close-workspace]');
		var selectedVariant = normalizeText(input.value);
		var source;

		if (!reference || !workspace || !selectedVariant) {
			return;
		}

		source = findClosingVariantReference(workspace, reference, selectedVariant);

		if (!source) {
			setClosingReferenceEmpty(reference, true, input.value);
			filterClosingBases(workspace);
			updateClosingWorkspace(workspace);
			return;
		}

		setClosingReferenceEmpty(reference, false);
		outputText(source, 'reference_status', 'Configuración de variante cargada sin recargar la página');
		activateClosingReference(source);
		autosaveClosingWorkspace(workspace);
	}

	function selectClosingReferenceName(select) {
		var reference = select.closest('[data-close-reference]');
		var input = reference ? reference.querySelector('[data-field="nombre"]') : null;
		var workspace = select.closest('[data-close-workspace]');
		var nombre = String(select.value || '').trim();

		if (!input) {
			return;
		}

		if (isClosingReferenceMetaLocked(reference) && !reference.hasAttribute('data-new-reference')) {
			select.value = input.value || select.value;
			return;
		}

		input.value = nombre;
		input.hidden = true;

		if (!nombre) {
			outputText(reference, 'reference_status', 'Elige una referencia del catálogo (Configuraciones → Cierre de órdenes).');
			updateClosingWorkspace(workspace);
			autosaveClosingWorkspace(workspace);
			return;
		}

		setClosingReferenceEmpty(reference, false);
		refreshBaseReferenceOptions(workspace);
		filterClosingBases(workspace);
		updateClosingWorkspace(workspace);
		autosaveClosingWorkspace(workspace);
		outputText(reference, 'reference_status', 'Referencia asignada desde el catálogo.');
	}

	function selectClosingVariant(select) {
		var reference = select.closest('[data-close-reference]');
		var input = reference ? reference.querySelector('[data-field="variante"]') : null;
		var workspace = select.closest('[data-close-workspace]');

		if (!input) {
			return;
		}

		if (isClosingReferenceMetaLocked(reference) && !reference.hasAttribute('data-new-reference')) {
			select.value = input.value || select.value;
			return;
		}

		if (select.value === '__new__') {
			// Alta de variantes solo desde Configuraciones.
			select.value = input.value || '';
			outputText(reference, 'reference_status', 'Las variantes nuevas se crean en Configuraciones.');
			return;
		}

		if (reference.hasAttribute('data-new-reference')) {
			setClosingReferenceEmpty(reference, false);
			setReferenceVariant(reference, select.value);
			filterClosingBases(workspace);
			updateClosingWorkspace(workspace);
			autosaveClosingWorkspace(workspace);
			outputText(reference, 'reference_status', 'Variante asignada a la nueva referencia. Se guardará de forma independiente.');
			return;
		}

		if (!findClosingVariantReference(workspace, reference, select.value)) {
			setClosingReferenceEmpty(reference, true, select.value);
			filterClosingBases(workspace);
			updateClosingWorkspace(workspace);
			return;
		}

		setReferenceVariant(reference, select.value);
		setClosingReferenceEmpty(reference, false);
		autosaveClosingWorkspace(workspace);
		loadClosingVariant(input);
	}

	function addVariantOption(workspace, variant) {
		if (!variant) {
			return;
		}

		workspace.querySelectorAll('[data-field="variante_selector"]').forEach(function (select) {
			var exists = Array.from(select.options).some(function (option) {
				return normalizeText(option.value) === normalizeText(variant);
			});
			var option;

			if (!exists) {
				option = document.createElement('option');
				option.value = variant;
				option.textContent = variant;
				select.appendChild(option);
			}
		});
		workspace.querySelectorAll('[data-base-variant-filter]').forEach(function (select) {
			var exists = Array.from(select.options).some(function (option) {
				return normalizeText(option.value) === normalizeText(variant);
			});
			var option;

			if (!exists) {
				option = document.createElement('option');
				option.value = variant;
				option.textContent = variant;
				select.appendChild(option);
			}
		});
	}

	function saveTypedVariant(input) {
		var reference = input.closest('[data-close-reference]');
		var workspace = input.closest('[data-close-workspace]');
		var oldVariant = input.getAttribute('data-editing-variant') || textField(reference, 'variante');
		var variant = input.value.trim();

		if (!variant) {
			return;
		}

		addVariantOption(workspace, variant);
		setReferenceVariant(reference, variant);
		setClosingReferenceEmpty(reference, false);

		if (reference.hasAttribute('data-new-reference')) {
			filterClosingBases(workspace);
			updateClosingWorkspace(workspace);
			autosaveClosingWorkspace(workspace);
			outputText(reference, 'reference_status', 'Variante creada para la nueva referencia.');
			return;
		}

		if (!findClosingVariantReference(workspace, reference, variant)) {
			var created = createVariantReference(reference, variant);
			setClosingReferenceEmpty(created, false);
			outputText(created, 'reference_status', 'Variante creada. Ajusta sus consumos y bases; se autoguardará.');
			activateClosingReference(created);
			filterClosingBases(workspace);
			updateClosingWorkspace(workspace);
			autosaveClosingWorkspace(workspace);
			return;
		}

		workspace.querySelectorAll('[data-close-base]').forEach(function (base) {
			var baseVariant = base.querySelector('[data-field="variante"]');

			if (baseVariant && normalizeText(baseVariant.value) === normalizeText(oldVariant)) {
				baseVariant.value = variant;
			}
		});
		input.removeAttribute('data-editing-variant');
		filterClosingBases(workspace);
		updateClosingWorkspace(workspace);
		autosaveClosingWorkspace(workspace);
		outputText(reference, 'reference_status', 'Variante guardada automáticamente.');
	}

	function addProductTypeOption(workspace, productType) {
		if (!productType) {
			return;
		}

		workspace.querySelectorAll('[data-field="tipo_producto_selector"]').forEach(function (select) {
			var exists = Array.from(select.options).some(function (option) {
				return normalizeText(option.value) === normalizeText(productType);
			});
			var option;

			if (!exists) {
				option = document.createElement('option');
				option.value = productType;
				option.textContent = productType;
				select.appendChild(option);
			}
		});
	}

	function selectProductType(select) {
		var reference = select.closest('[data-close-reference]');
		var input = reference ? reference.querySelector('[data-field="tipo_producto"]') : null;

		if (!input) {
			return;
		}

		if (isClosingReferenceMetaLocked(reference) && !reference.hasAttribute('data-new-reference')) {
			select.value = input.value || select.value;
			return;
		}

		if (select.value === '__new__') {
			// Alta de tipos de producto solo desde Configuraciones.
			select.value = input.value || '';
			outputText(reference, 'reference_status', 'Los tipos de producto nuevos se crean en Configuraciones.');
			return;
		}

		input.value = select.value;
		input.hidden = true;
		updateClosingWorkspace(select.closest('[data-close-workspace]'));
		autosaveClosingWorkspace(select.closest('[data-close-workspace]'));
	}

	function saveTypedProductType(input) {
		var reference = input.closest('[data-close-reference]');
		var workspace = input.closest('[data-close-workspace]');
		var productType = input.value.trim();
		var select = reference ? reference.querySelector('[data-field="tipo_producto_selector"]') : null;

		if (!productType) {
			return;
		}

		addProductTypeOption(workspace, productType);
		if (select) {
			select.value = productType;
		}
		input.hidden = true;
		updateClosingWorkspace(workspace);
		autosaveClosingWorkspace(workspace);
	}

	function closingReferenceMetaFields(reference) {
		if (!reference) {
			return [];
		}

		return Array.from(reference.querySelectorAll(
			'[data-field="nombre_selector"], [data-field="nombre"], [data-field="variante_selector"], [data-field="variante"], [data-field="tipo_producto_selector"], [data-field="tipo_producto"], [data-field="formula_template"]'
		));
	}

	function isClosingReferenceMetaLocked(reference) {
		return !!(reference && reference.getAttribute('data-ref-meta-locked') !== '0');
	}

	function closingBaseIdentityFields(card) {
		if (!card) {
			return [];
		}

		return Array.from(card.querySelectorAll('[data-field="variante"], [data-field="tipo"], [data-field="referencia"]'));
	}

	function isClosingBaseIdentityLocked(card) {
		return !!(card && card.getAttribute('data-base-identity-locked') !== '0');
	}

	function setClosingBaseIdentityLocked(card, locked) {
		if (!card) {
			return;
		}

		card.classList.toggle('is-base-identity-locked', !!locked);
		card.setAttribute('data-base-identity-locked', locked ? '1' : '0');

		closingBaseIdentityFields(card).forEach(function (field) {
			if (field.tagName === 'SELECT') {
				field.disabled = !!locked;
			} else {
				field.readOnly = !!locked;
			}

			field.tabIndex = locked ? -1 : 0;

			if (locked) {
				field.classList.add('logistica-readonly-field');
				field.removeAttribute('title');
			} else {
				field.classList.remove('logistica-readonly-field');
				field.removeAttribute('title');
			}
		});

		card.querySelectorAll('.logistica-base-identity-lock').forEach(function (lockIcon) {
			lockIcon.hidden = !locked;
			lockIcon.title = locked ? 'Campo bloqueado' : '';
		});
	}

	function lockClosingBaseIdentity(card) {
		setClosingBaseIdentityLocked(card, true);
	}

	function setClosingReferenceNombreLocked(reference, locked) {
		var nombreInput = reference ? reference.querySelector('[data-field="nombre"]') : null;
		var workspace = reference ? reference.closest('[data-close-workspace]') : null;

		if (!nombreInput) {
			return;
		}

		if (locked && workspace && isClosingReferenceNameEditing(workspace, reference)) {
			finishClosingReferenceNameEdit(workspace, reference);
		}

		nombreInput.readOnly = !!locked;
		nombreInput.tabIndex = locked ? -1 : 0;
		nombreInput.classList.toggle('logistica-readonly-field', !!locked);

		if (locked) {
			nombreInput.blur();
		}
	}

	function setClosingReferenceMetaLocked(reference, locked) {
		var editButton;
		var variantInput;
		var variantSelect;

		if (!reference) {
			return;
		}

		reference.classList.toggle('is-ref-meta-locked', !!locked);
		reference.setAttribute('data-ref-meta-locked', locked ? '1' : '0');

		setClosingReferenceNombreLocked(reference, !!locked);

		closingReferenceMetaFields(reference).forEach(function (field) {
			field.disabled = !!locked;
		});

		editButton = reference.querySelector('[data-action="edit-close-variant"]');

		if (editButton) {
			editButton.setAttribute('aria-pressed', locked ? 'false' : 'true');
			editButton.textContent = locked ? 'Editar' : 'Listo';
		}

		if (locked) {
			variantInput = reference.querySelector('[data-field="variante"]');
			variantSelect = reference.querySelector('[data-field="variante_selector"]');

			if (variantInput && variantSelect && !variantInput.hidden && variantInput.value.trim()) {
				saveTypedVariant(variantInput);
			} else if (variantInput && variantSelect && variantInput.value && variantSelect.value !== '__new__') {
				variantInput.hidden = true;
			}

			variantInput = reference.querySelector('[data-field="tipo_producto"]');
			variantSelect = reference.querySelector('[data-field="tipo_producto_selector"]');

			if (variantInput && variantSelect && !variantInput.hidden && variantInput.value.trim()) {
				saveTypedProductType(variantInput);
			} else if (variantInput && variantSelect && variantInput.value && variantSelect.value !== '__new__') {
				variantInput.hidden = true;
			}
		}
	}

	function syncClosingReferenceMetaLock(reference) {
		if (!reference) {
			return;
		}

		if (reference.hasAttribute('data-new-reference')) {
			setClosingReferenceMetaLocked(reference, false);
			return;
		}

		setClosingReferenceMetaLocked(reference, true);
	}

	function toggleClosingReferenceMetaEdit(button) {
		var reference = button.closest('[data-close-reference]');

		if (!reference) {
			return;
		}

		if (isClosingReferenceMetaLocked(reference)) {
			setClosingReferenceMetaLocked(reference, false);
			outputText(
				reference,
				'reference_status',
				'Edición habilitada: referencia, variante, tipo de producto y plantilla fórmula.'
			);

			if (reference.querySelector('[data-field="nombre_selector"]')) {
				reference.querySelector('[data-field="nombre_selector"]').focus();
			} else if (reference.querySelector('[data-field="variante_selector"]')) {
				reference.querySelector('[data-field="variante_selector"]').focus();
			}

			return;
		}

		setClosingReferenceMetaLocked(reference, true);
		outputText(
			reference,
			'reference_status',
			'Campos protegidos. Pulse Editar para modificar referencia, variante, tipo o plantilla.'
		);
	}

	function deleteClosingVariant(button) {
		var reference = button.closest('[data-close-reference]');
		var workspace = button.closest('[data-close-workspace]');
		var variant = textField(reference, 'variante');
		var candidates = Array.from(workspace.querySelectorAll('[data-close-reference]'));
		var nextReference;

		if (candidates.length <= 1) {
			outputText(reference, 'reference_status', 'Debe existir al menos una variante.');
			return;
		}

		if (!confirmLogisticaDelete('¿Eliminar esta variante y sus bases asociadas?', '"' + variant + '"')) {
			return;
		}

		workspace.querySelectorAll('[data-close-base]').forEach(function (base) {
			if (normalizeText(textField(base, 'variante')) === normalizeText(variant)) {
				base.remove();
			}
		});

		nextReference = candidates.find(function (candidate) {
			return candidate !== reference && sameReferenceFamily(candidate, reference);
		}) || candidates.find(function (candidate) {
			return candidate !== reference;
		});

		reference.remove();
		reindexClosingWorkspace(workspace);

		if (nextReference) {
			activateClosingReference(nextReference);
		}

		refreshBaseReferenceOptions(workspace);
		autosaveClosingWorkspace(workspace);
	}

	function reindexClosingWorkspace(workspace) {
		workspace.querySelectorAll('[data-close-base]').forEach(function (base, index) {
			base.querySelectorAll('[name^="cierre[bases]"]').forEach(function (input) {
				input.name = input.name.replace(/cierre\\[bases\\]\\[\\d+\\]/, 'cierre[bases][' + index + ']');
			});
		});

		workspace.querySelectorAll('[data-close-reference]').forEach(function (reference, refIndex) {
			reference.querySelectorAll('[name^="cierre[referencias]"]').forEach(function (input) {
				input.name = input.name.replace(/cierre\\[referencias\\]\\[\\d+\\]/, 'cierre[referencias][' + refIndex + ']');
				input.name = input.name.replace(/\\[lineas\\]\\[\\d+\\]/, function () {
					var row = input.closest('[data-close-line]');
					var rows = Array.from(reference.querySelectorAll('[data-close-line]'));
					return '[lineas][' + Math.max(0, rows.indexOf(row)) + ']';
				});
			});
		});
	}

	function resetClosingSidebarFilters(workspace) {
		var searchInput = workspace.querySelector('[data-base-search]');
		var variantSelect = workspace.querySelector('[data-base-variant-filter]');
		var typeSelect = workspace.querySelector('[data-base-type-filter]');
		var referenceSelect = workspace.querySelector('[data-base-reference-filter]');

		if (searchInput) {
			searchInput.value = '';
		}
		if (variantSelect) {
			variantSelect.value = '';
		}
		if (typeSelect) {
			typeSelect.value = '';
		}
		if (referenceSelect) {
			referenceSelect.value = '';
		}

		setReferenceFilterMode(workspace, false);
	}

	function getClosingReferenceCloneSource(workspace) {
		var refs = workspace.querySelector('[data-close-references]');
		var source = null;

		if (!refs) {
			return null;
		}

		source = refs.querySelector('[data-close-reference][data-reference-seed]');
		if (source) {
			return source;
		}

		source = Array.from(refs.querySelectorAll('[data-close-reference]')).find(function (reference) {
			return !reference.hasAttribute('data-reference-seed') && !reference.hidden && !isReferenceFilteredOut(reference);
		});

		if (source) {
			return source;
		}

		return refs.querySelector('[data-close-reference]:not([data-reference-seed])')
			|| refs.querySelector('[data-close-reference]');
	}

	function addClosingReference(workspace) {
		var refs = workspace.querySelector('[data-close-references]');
		var source = getClosingReferenceCloneSource(workspace);
		var clone;
		var globalEmpty;
		var nombreField;

		if (!refs || !source) {
			window.alert('No se pudo crear la referencia. Recarga la página e intenta de nuevo.');
			return;
		}

		resetClosingSidebarFilters(workspace);

		clone = source.cloneNode(true);
		clone.hidden = false;
		clone.removeAttribute('data-reference-seed');
		clone.removeAttribute('data-ref-filtered-out');
		clone.setAttribute('data-new-reference', '1');
		clone.querySelectorAll('input').forEach(function (input) {
			if (input.getAttribute('data-field') === 'nombre') {
				input.value = '';
				input.hidden = true;
			} else if (input.getAttribute('data-field') === 'variante') {
				input.value = '';
				input.hidden = true;
			} else if (input.getAttribute('data-field') === 'tipo_producto') {
				input.hidden = true;
			} else if (input.getAttribute('data-field') === 'id') {
				input.value = 'ref-' + Date.now();
			} else if (['unidades', 'cantidad', 'paquetes', 'unidades_paquete'].indexOf(input.getAttribute('data-field')) !== -1) {
				input.value = '';
			}
		});
		clone.querySelectorAll('select[data-field="nombre_selector"], select[data-field="variante_selector"], select[data-field="tipo_producto_selector"]').forEach(function (select) {
			select.value = '';
		});
		clone.querySelectorAll('[data-close-reference-pick]').forEach(function (pick) {
			pick.checked = false;
		});
		clone.classList.remove('is-reference-pick-selected');
		clearClosingReferenceLines(clone);
		refs.appendChild(clone);
		setClosingReferenceMetaLocked(clone, false);
		reindexClosingWorkspace(workspace);
		refreshBaseReferenceOptions(workspace);
		outputText(clone, 'reference_status', 'Nueva referencia sin bloques. Elige nombre, variante y tipo desde las listas (se editan en Configuraciones). Usa Nuevo bloque de cálculo para agregar los que necesites.');
		reconcileClosingReference(workspace, clone);
		globalEmpty = refs.querySelector(':scope > [data-close-ref-empty].is-visible');
		if (globalEmpty) {
			globalEmpty.classList.remove('is-visible');
			globalEmpty.hidden = true;
		}
		setClosingFilterEmpty(workspace, false);
		setReferenceFilterVisibility(clone, false);
		showClosingReference(clone);
		filterClosingBases(workspace);
		updateClosingWorkspace(workspace);
		initClosingUnitsFields(clone);
		clone.scrollIntoView({ behavior: 'smooth', block: 'start' });
		nombreField = clone.querySelector('[data-field="nombre_selector"]') || clone.querySelector('[data-field="nombre"]');
		if (nombreField) {
			nombreField.focus();
			if (typeof nombreField.select === 'function' && nombreField.tagName === 'INPUT') {
				nombreField.select();
			}
		}
	}

	function findClosingBaseTemplate(workspace, variant, preferredType, preferredReference) {
		var list = workspace.querySelector('[data-close-bases]');
		var bases = list ? Array.from(list.querySelectorAll('[data-close-base]')) : [];
		var normalizedVariant = normalizeText(variant);
		var source = null;
		var lookupKeys;
		var keyIndex;

		if (!bases.length) {
			return null;
		}

		if (preferredReference) {
			lookupKeys = baseReferenceMatrixLookupKeys(preferredReference);

			for (keyIndex = 0; keyIndex < lookupKeys.length; keyIndex += 1) {
				source = bases.find(function (base) {
					return normalizeText(textField(base, 'variante')) === normalizedVariant
						&& normalizeText(textField(base, 'referencia')) === lookupKeys[keyIndex]
						&& (!preferredType || textField(base, 'tipo') === preferredType);
				});

				if (source) {
					return source;
				}
			}
		}

		source = bases.find(function (base) {
			return normalizeText(textField(base, 'variante')) === normalizedVariant
				&& (!preferredType || textField(base, 'tipo') === preferredType);
		});

		if (!source) {
			source = bases.find(function (base) {
				return normalizeText(textField(base, 'variante')) === normalizedVariant;
			});
		}

		return source || bases[0];
	}

	function ensureBaseTipoPlaceholderOption(select) {
		var placeholder;

		if (!select || select.querySelector('option[data-placeholder="1"]')) {
			return;
		}

		placeholder = document.createElement('option');
		placeholder.value = '';
		placeholder.setAttribute('data-placeholder', '1');
		placeholder.textContent = 'Seleccionar tipo...';
		select.insertBefore(placeholder, select.firstChild);
	}

	function addClosingBlock(button) {
		var reference = button.closest('[data-close-reference]');
		var workspace = button.closest('[data-close-workspace]');
		var body = reference ? reference.querySelector('[data-close-lines]') : null;
		var source;
		var clone;
		var baseTipoSelect;

		if (!body || !reference || !workspace) {
			return;
		}

		source = getClosingLineTemplateRow(workspace) || findClosingBlockCloneSource(workspace, reference, 'nuevo_bloque');

		if (!source) {
			return;
		}

		clone = source.cloneNode(true);
		ensureRowLinkField(clone);
		baseTipoSelect = clone.querySelector('[data-field="base_tipo"]');
		ensureBaseTipoPlaceholderOption(baseTipoSelect);
		applyClosingRowFields(clone, {
			baseTipo: '',
			lineType: 'nuevo_bloque',
			label: '',
			linkId: '',
			resetMetrics: true
		});

		if (baseTipoSelect) {
			baseTipoSelect.value = '';
		}

		body.appendChild(clone);
		sortClosingReferenceBlocks(reference);
		reindexClosingWorkspace(workspace);
		applyReferencePickFilter(workspace);
		filterClosingBases(workspace);
		updateClosingWorkspace(workspace);

		if (baseTipoSelect) {
			baseTipoSelect.focus();
		}
	}

	function addClosingSaldoBlock(button) {
		var reference = button.closest('[data-close-reference]');
		var workspace = button.closest('[data-close-workspace]');

		if (!reference || !workspace) {
			return;
		}

		ensureCajaSaldoRow(workspace, reference);
		reconcileClosingReference(workspace, reference);
		reindexClosingWorkspace(workspace);
		updateClosingWorkspace(workspace);
	}

	function removeClosingBlock(button) {
		var row = button.closest('[data-close-line]');
		var workspace = button.closest('[data-close-workspace]');
		var reference = button.closest('[data-close-reference]');
		var baseCard;
		var label;

		if (!row || !workspace) {
			return;
		}

		baseCard = findClosingBaseCardForRow(workspace, row);

		label = textField(row, 'label') || textField(row, 'tipo') || '';

		if (!confirmLogisticaDelete('¿Eliminar este bloque de cálculo y su base permanente enlazada?', label)) {
			return;
		}

		row.remove();
		sortClosingReferenceBlocks(reference);

		if (baseCard) {
			baseCard.remove();
		}

		if (reference) {
			reconcileClosingReference(workspace, reference);
		}

		reindexClosingWorkspace(workspace);
		updateClosingWorkspace(workspace);
		filterClosingBases(workspace);
		autosaveClosingWorkspace(workspace, true);
	}

	function setAutosaveStatus(workspace, message, state) {
		var status = workspace ? workspace.querySelector('[data-autosave-status]') : null;

		if (!status) {
			return;
		}

		status.textContent = message;
		status.classList.remove('is-saving', 'is-saved', 'is-error');
		if (state) {
			status.classList.add(state);
		}
	}

	function getClosingSaveForm(workspace) {
		var jsonField;

		if (!workspace) {
			return null;
		}

		return workspace.querySelector('#logistica-cierre-save-form')
			|| ((jsonField = workspace.querySelector('[data-cierre-json]')) ? jsonField.closest('form') : null);
	}

	function getClosingJsonField(workspace) {
		var form;
		var field;

		if (!workspace) {
			return null;
		}

		form = getClosingSaveForm(workspace);
		field = form ? form.querySelector('[data-cierre-json]') : null;

		return field || workspace.querySelector('[data-cierre-json]');
	}

	function closingFieldsObject(scope) {
		var data = {};
		var saldoUndInput;

		scope.querySelectorAll('[data-field]').forEach(function (fieldElement) {
			var key = fieldElement.getAttribute('data-field');

			if (!key || key.indexOf('_selector') !== -1) {
				return;
			}

			if (fieldElement.disabled && fieldElement.tagName === 'SELECT') {
				return;
			}

			if (isUnitsIntegerField(key)) {
				if (!String(fieldElement.value || '').trim()) {
					data[key] = '';
				} else {
					data[key] = String(Math.round(parseMoneyValue(fieldElement.value)));
				}
				return;
			}

			data[key] = String(fieldElement.value || '');
		});

		saldoUndInput = scope.querySelector('[data-field="unidades_paquete"][data-saldo-input="und"]');

		if (saldoUndInput) {
			data.saldo_und_manual = saldoUndInput.getAttribute('data-saldo-und-manual') === '1' ? '1' : '';
		}

		return data;
	}

	function serializeClosingWorkspace(workspace) {
		var config = {
			bases: [],
			referencias: []
		};

		workspace.querySelectorAll('[data-close-base]').forEach(function (base) {
			config.bases.push(closingFieldsObject(base));
		});

		workspace.querySelectorAll('[data-close-reference]').forEach(function (reference) {
			var ref;

			if (reference.hasAttribute('data-variant-empty') || reference.hasAttribute('data-reference-seed')) {
				return;
			}

			ref = closingFieldsObject(reference);
			ref.lineas = [];
			reference.querySelectorAll('[data-close-line]').forEach(function (line) {
				ref.lineas.push(closingFieldsObject(line));
			});
			config.referencias.push(ref);
		});

		return config;
	}

	function updateClosingJsonField(workspace) {
		var jsonField = getClosingJsonField(workspace);

		if (jsonField) {
			jsonField.value = JSON.stringify(serializeClosingWorkspace(workspace));
		}
	}

	function flushClosingWorkspaceSave(workspace) {
		if (!workspace) {
			return;
		}

		updateClosingWorkspace(workspace);
		updateClosingJsonField(workspace);
		autosaveClosingWorkspace(workspace, true);
	}

	function normalizeClosingCod(value) {
		return String(value || '').trim().toUpperCase();
	}

	function closingAjax(action, params) {
		if (!window.LogisticaAdmin || !window.fetch) {
			return Promise.resolve(null);
		}

		var data = new URLSearchParams(params || {});
		data.set('action', action);
		data.set('nonce', window.LogisticaAdmin.cierreNonce || '');

		return window.fetch(window.LogisticaAdmin.ajaxUrl, {
			method: 'POST',
			credentials: 'same-origin',
			headers: {
				'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
			},
			body: data.toString()
		}).then(function (response) {
			return response.json();
		});
	}

	function warehouseAjax(action, params) {
		if (!window.LogisticaAdmin || !window.fetch) {
			return Promise.resolve(null);
		}

		var data = new URLSearchParams(params || {});
		data.set('action', action);
		data.set('nonce', window.LogisticaAdmin.warehouseNonce || '');

		return window.fetch(window.LogisticaAdmin.ajaxUrl, {
			method: 'POST',
			credentials: 'same-origin',
			headers: {
				'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
			},
			body: data.toString()
		}).then(function (response) {
			return response.text().then(function (text) {
				var payload = null;

				try {
					payload = text ? JSON.parse(text) : null;
				} catch (error) {
					payload = null;
				}

				if (!response.ok || !payload) {
					return {
						success: false,
						data: {
							message: 'No se pudo consultar el inventario (respuesta inválida).'
						}
					};
				}

				return payload;
			});
		});
	}

	function syncClosingLoteByCod(workspace, codigo, lote, sourceBase) {
		var normalizedCod = normalizeClosingCod(codigo);
		var changedAny = false;

		if (!workspace || !normalizedCod) {
			return changedAny;
		}

		workspace.querySelectorAll('[data-close-base]').forEach(function (baseCard) {
			var codInput;
			var loteInput;
			var codSaldoInput;
			var loteSaldoInput;
			var changed = false;

			if (sourceBase && baseCard === sourceBase) {
				return;
			}

			codInput = baseCard.querySelector('[data-field="cod"]');
			loteInput = baseCard.querySelector('[data-field="lote"]');
			codSaldoInput = baseCard.querySelector('[data-field="cod_saldo"]');
			loteSaldoInput = baseCard.querySelector('[data-field="lote_saldo"]');

			if (codInput && normalizeClosingCod(codInput.value) === normalizedCod && loteInput && String(loteInput.value || '').trim() !== lote) {
				loteInput.value = lote;
				changed = true;
			}

			if (codSaldoInput && normalizeClosingCod(codSaldoInput.value) === normalizedCod && loteSaldoInput && String(loteSaldoInput.value || '').trim() !== lote) {
				loteSaldoInput.value = lote;
				changed = true;
			}

			if (changed) {
				changedAny = true;
				updateClosingBaseCard(baseCard);
				propagateBaseFieldsToLines(baseCard, workspace);
			}
		});

		return changedAny;
	}

	function syncClosingAjusteByCod(workspace, codigo, ajuste, sourceBase) {
		var normalizedCod = normalizeClosingCod(codigo);
		var ajusteValue = ajuste == null ? '' : String(ajuste);
		var changedAny = false;

		if (!workspace || !normalizedCod) {
			return changedAny;
		}

		workspace.querySelectorAll('[data-close-base]').forEach(function (baseCard) {
			var codInput;
			var codSaldoInput;
			var ajusteInput;
			var matchesCod = false;

			if (sourceBase && baseCard === sourceBase) {
				return;
			}

			codInput = baseCard.querySelector('[data-field="cod"]');
			codSaldoInput = baseCard.querySelector('[data-field="cod_saldo"]');
			ajusteInput = baseCard.querySelector('[data-field="ajuste"]');

			if (!ajusteInput) {
				return;
			}

			matchesCod = !!(codInput && normalizeClosingCod(codInput.value) === normalizedCod)
				|| !!(codSaldoInput && normalizeClosingCod(codSaldoInput.value) === normalizedCod);

			if (!matchesCod || String(ajusteInput.value || '') === ajusteValue) {
				return;
			}

			ajusteInput.value = ajusteValue;
			changedAny = true;
			updateClosingBaseCard(baseCard);
			propagateBaseFieldsToLines(baseCard, workspace);
		});

		return changedAny;
	}

	function applyWarehouseLoteMapToClosing(loteMap, options) {
		var opts = options || {};
		var changed = false;
		var codigo;
		var lote;

		if (!loteMap || typeof loteMap !== 'object') {
			return changed;
		}

		document.querySelectorAll('[data-close-workspace]').forEach(function (workspace) {
			var workspaceChanged = false;

			Object.keys(loteMap).forEach(function (rawCodigo) {
				codigo = normalizeClosingCod(rawCodigo);
				lote = String(loteMap[rawCodigo] || '').trim();

				if (!codigo) {
					return;
				}

				if (syncClosingLoteByCod(workspace, codigo, lote, null)) {
					workspaceChanged = true;
				}
			});

			if (workspaceChanged) {
				changed = true;
				updateClosingWorkspace(workspace);
				if (opts.autosave !== false) {
					autosaveClosingWorkspace(workspace, true);
				}
			}
		});

		return changed;
	}

	function applyWarehouseLoteToClosing(codigo, lote) {
		var normalizedCod = normalizeClosingCod(codigo);
		var map = {};

		if (!normalizedCod) {
			return;
		}

		map[normalizedCod] = String(lote || '').trim();

		if (applyWarehouseLoteMapToClosing(map) && typeof window.CustomEvent === 'function') {
			window.dispatchEvent(new window.CustomEvent('logistica-warehouse-lote-synced', {
				detail: {
					codigo: normalizedCod,
					lote: map[normalizedCod]
				}
			}));
		}
	}

	function syncClosingLotesFromWarehouse(options) {
		return warehouseAjax('logistica_get_warehouse_lote_map', {}).then(function (payload) {
			if (!payload || !payload.success || !payload.data || !payload.data.map) {
				return false;
			}

			return applyWarehouseLoteMapToClosing(payload.data.map, options);
		});
	}

	function applyCharacteristicsMapToClosing(charMap, options) {
		var opts = options || {};
		var changed = false;
		var codigo;
		var row;

		if (!charMap || typeof charMap !== 'object') {
			return changed;
		}

		document.querySelectorAll('[data-close-workspace]').forEach(function (workspace) {
			var workspaceChanged = false;

			Object.keys(charMap).forEach(function (rawCodigo) {
				codigo = normalizeClosingCod(rawCodigo);
				row = charMap[rawCodigo];

				if (!codigo || !row) {
					return;
				}

				if (syncClosingCharacteristicsByCod(workspace, codigo, row)) {
					workspaceChanged = true;
				}
			});

			if (workspaceChanged) {
				changed = true;
				updateClosingWorkspace(workspace);
				if (opts.autosave !== false) {
					autosaveClosingWorkspace(workspace, true);
				}
			}
		});

		return changed;
	}

	function syncClosingCharacteristicsFromCatalog(options) {
		return closingAjax('logistica_get_cierre_caracteristicas_map', {}).then(function (payload) {
			if (!payload || !payload.success || !payload.data || !payload.data.map) {
				return false;
			}

			return applyCharacteristicsMapToClosing(payload.data.map, options);
		});
	}

	function fetchCierreCaracteristicaForCod(codigo) {
		return closingAjax('logistica_get_cierre_caracteristica', {
			codigo: codigo
		});
	}

	function postWarehouseLoteFromCierre(codigo, lote) {
		if (!codigo) {
			return;
		}

		warehouseAjax('logistica_update_warehouse_lote_from_cierre', {
			codigo: codigo,
			lote: lote
		});
	}

	function fetchWarehouseLoteForCod(codigo) {
		return warehouseAjax('logistica_get_warehouse_lote', {
			codigo: codigo
		});
	}

	function handleClosingCodLoteSync(closingWorkspace, baseCard, fieldName) {
		var codInput;
		var loteInput;
		var codigo;
		var lote;
		var changed;

		if (!baseCard || !closingWorkspace || fieldName !== 'cod') {
			return;
		}

		codInput = baseCard.querySelector('[data-field="cod"]');
		loteInput = baseCard.querySelector('[data-field="lote"]');
		codigo = normalizeClosingCod(codInput ? codInput.value : '');

		if (!codigo) {
			return;
		}

		Promise.all([
			fetchWarehouseLoteForCod(codigo),
			fetchCierreCaracteristicaForCod(codigo)
		]).then(function (results) {
			var lotePayload = results[0];
			var charPayload = results[1];

			changed = false;

			if (lotePayload && lotePayload.success && lotePayload.data && lotePayload.data.found) {
				lote = String(lotePayload.data.lote || '').trim();
				if (loteInput && String(loteInput.value || '').trim() !== lote) {
					loteInput.value = lote;
					changed = true;
				}
				if (syncClosingLoteByCod(closingWorkspace, codigo, lote, baseCard)) {
					changed = true;
				}
			}

			if (charPayload && charPayload.success && charPayload.data && charPayload.data.found && charPayload.data.row) {
				if (applyClosingCharacteristicsToBase(baseCard, charPayload.data.row)) {
					changed = true;
				}
				if (syncClosingCharacteristicsByCod(closingWorkspace, codigo, charPayload.data.row)) {
					changed = true;
				}
			}

			if (changed) {
				updateClosingBaseCard(baseCard);
				propagateBaseFieldsToLines(baseCard, closingWorkspace);
				scheduleUpdateClosingWorkspace(closingWorkspace, { immediate: true });
				autosaveClosingWorkspace(closingWorkspace);
			}
		});
	}

	var WAREHOUSE_QUANTITY_DECIMALS = 2;

	function formatWarehouseNumber(value) {
		var number = Number(value);
		if (!Number.isFinite(number)) {
			number = 0;
		}
		return number.toFixed(WAREHOUSE_QUANTITY_DECIMALS);
	}

	function formatWarehouseInputValue(value) {
		return formatWarehouseNumber(value);
	}

	function warehouseStatusBadge(row) {
		if (row.mejoro) {
			return '<span class="logistica-warehouse-badge is-improved">Mejoró</span>';
		}
		if (row.tiene_alerta) {
			return '<span class="logistica-warehouse-badge is-alert">Diferencia</span>';
		}
		return '<span class="logistica-warehouse-badge is-ok">OK</span>';
	}

	function warehouseRegistroBadge(row) {
		var activo = row.registro_activo !== false && row.estado !== 'inactivo';
		return '<span class="logistica-warehouse-badge ' + (activo ? 'is-registro-activo' : 'is-registro-inactivo') + '">'
			+ (activo ? 'Activo' : 'Inactivo')
			+ '</span>';
	}

	function warehouseLoteCellHtml(row) {
		var html = warehouseCopyValueHtml(row.lote, 'logistica-warehouse-lote', 'lote');
		if (row.es_lote_activo) {
			html += '<span class="logistica-warehouse-lote-vigente">Vigente</span>';
		}
		return html;
	}

	function warehouseRowById(board, rowId) {
		return board ? board.querySelector('[data-warehouse-row][data-row-id="' + CSS.escape(String(rowId)) + '"]') : null;
	}

	function syncWarehouseVigenteBadges(board, codigo, vigenteRowId) {
		if (!board || !codigo) {
			return;
		}

		board.querySelectorAll('[data-warehouse-row][data-codigo="' + CSS.escape(String(codigo)) + '"]').forEach(function (tableRow) {
			var loteCell = tableRow.querySelector('.is-lote');
			var badge;
			var isVigente = String(tableRow.getAttribute('data-row-id') || '') === String(vigenteRowId || '');

			tableRow.setAttribute('data-warehouse-vigente', isVigente ? '1' : '0');

			if (!loteCell) {
				return;
			}

			badge = loteCell.querySelector('.logistica-warehouse-lote-vigente');
			if (badge) {
				badge.remove();
			}

			if (isVigente) {
				badge = document.createElement('span');
				badge.className = 'logistica-warehouse-lote-vigente';
				badge.textContent = 'Vigente';
				loteCell.appendChild(badge);
			}
		});
	}

	function warehouseTextFields() {
		return ['descripcion', 'materia_prima', 'medida', 'lote', 'clase'];
	}

	function warehouseDiffClass(value) {
		var number = Number(value);
		if (!Number.isFinite(number) || Math.abs(number) < 0.005) {
			return 'is-zero';
		}
		return number > 0 ? 'is-positive' : 'is-negative';
	}

	function warehouseRowStatus(data) {
		if (data && data.mejoro) {
			return 'improved';
		}
		if (data && data.tiene_alerta) {
			return 'diff';
		}
		return 'ok';
	}

	function warehouseRowSearchText(data) {
		if (!data) {
			return '';
		}

		return [
			data.codigo,
			data.descripcion,
			data.materia_prima,
			data.medida,
			data.lote,
			data.clase
		].map(function (value) {
			return String(value || '').trim();
		}).join(' ').toLowerCase();
	}

	function warehouseCopyValueHtml(value, extraClass, dataOutput) {
		var className = 'logistica-warehouse-copy-value';
		var attrs = ' tabindex="0" role="button" title="Clic para copiar"';

		if (extraClass) {
			className += ' ' + extraClass;
		}

		if (dataOutput) {
			attrs += ' data-output="' + String(dataOutput).replace(/"/g, '&quot;') + '"';
		}

		return '<span class="' + className + '"' + attrs + '>' + String(value || '') + '</span>';
	}

	function warehouseRowFilterAttrs(data) {
		if (!data) {
			return '';
		}

		return ''
			+ ' data-warehouse-descripcion="' + normalizeText(data.descripcion || '') + '"'
			+ ' data-warehouse-clase="' + normalizeText(data.clase || '') + '"';
	}

	function warehouseRowSortAttrs(data) {
		var status;
		var estadoSort;

		if (!data) {
			return '';
		}

		status = warehouseRowStatus(data);
		estadoSort = status === 'diff' ? '1' : (status === 'improved' ? '2' : '0');

		return ''
			+ ' data-warehouse-sort-codigo="' + normalizeText(data.codigo || '') + '"'
			+ ' data-warehouse-sort-descripcion="' + normalizeText(data.descripcion || '') + '"'
			+ ' data-warehouse-sort-materia_prima="' + normalizeText(data.materia_prima || '') + '"'
			+ ' data-warehouse-sort-medida="' + normalizeText(data.medida || '') + '"'
			+ ' data-warehouse-sort-lote="' + normalizeText(data.lote || '') + '"'
			+ ' data-warehouse-sort-fecha_creacion="' + String(Date.parse(data.fecha_creacion || data.created_at || '') || 0) + '"'
			+ ' data-warehouse-sort-saldo_bodega="' + String(Number(data.saldo_bodega || 0)) + '"'
			+ ' data-warehouse-sort-clase="' + normalizeText(data.clase || '') + '"'
			+ ' data-warehouse-sort-inventario_fisico="' + String(Number(data.inventario_fisico || 0)) + '"'
			+ ' data-warehouse-sort-diferencia="' + String(Number(data.diferencia || 0)) + '"'
			+ ' data-warehouse-sort-estado="' + estadoSort + '"'
			+ ' data-warehouse-sort-registro="' + (data.registro_activo === false || data.estado === 'inactivo' ? 'inactivo' : 'activo') + '"';
	}

	function applyWarehouseRowMeta(row, data) {
		var status;
		var estadoSort;

		if (!row || !data) {
			return;
		}

		status = warehouseRowStatus(data);
		estadoSort = status === 'diff' ? '1' : (status === 'improved' ? '2' : '0');

		row.setAttribute('data-warehouse-status', status);
		row.setAttribute('data-warehouse-search', warehouseRowSearchText(data));
		row.setAttribute('data-warehouse-descripcion', normalizeText(data.descripcion || ''));
		row.setAttribute('data-warehouse-clase', normalizeText(data.clase || ''));
		row.setAttribute('data-warehouse-sort-codigo', normalizeText(data.codigo || ''));
		row.setAttribute('data-warehouse-sort-descripcion', normalizeText(data.descripcion || ''));
		row.setAttribute('data-warehouse-sort-materia_prima', normalizeText(data.materia_prima || ''));
		row.setAttribute('data-warehouse-sort-medida', normalizeText(data.medida || ''));
		row.setAttribute('data-warehouse-sort-lote', normalizeText(data.lote || ''));
		row.setAttribute('data-warehouse-sort-saldo_bodega', String(Number(data.saldo_bodega || 0)));
		row.setAttribute('data-warehouse-sort-clase', normalizeText(data.clase || ''));
		row.setAttribute('data-warehouse-sort-inventario_fisico', String(Number(data.inventario_fisico || 0)));
		row.setAttribute('data-warehouse-sort-diferencia', String(Number(data.diferencia || 0)));
		row.setAttribute('data-warehouse-sort-estado', estadoSort);
		row.setAttribute('data-warehouse-sort-fecha_creacion', String(Date.parse(data.fecha_creacion || data.created_at || '') || 0));
		row.setAttribute('data-warehouse-sort-registro', data.registro_activo === false || data.estado === 'inactivo' ? 'inactivo' : 'activo');
		row.setAttribute('data-row-id', String(data.id || ''));
		row.setAttribute('data-warehouse-registro', data.registro_activo === false || data.estado === 'inactivo' ? 'inactivo' : 'activo');
		row.setAttribute('data-warehouse-vigente', data.es_lote_activo ? '1' : '0');
		row.classList.toggle('is-warehouse-inactive', data.registro_activo === false || data.estado === 'inactivo');
	}

	function compareWarehouseSortValues(valueA, valueB, dir) {
		var numA = parseFloat(String(valueA).replace(/[^\d.-]/g, ''));
		var numB = parseFloat(String(valueB).replace(/[^\d.-]/g, ''));

		if (!isNaN(numA) && !isNaN(numB) && (String(valueA).match(/\d/) || String(valueB).match(/\d/))) {
			return (numA - numB) * dir;
		}

		valueA = String(valueA).toLowerCase();
		valueB = String(valueB).toLowerCase();

		if (valueA < valueB) {
			return -1 * dir;
		}

		if (valueA > valueB) {
			return 1 * dir;
		}

		return 0;
	}

	function setTableSortIndicator(indicator, dir) {
		if (!indicator) {
			return;
		}

		indicator.textContent = '';
		indicator.classList.remove('is-asc', 'is-desc');

		if (dir === 1) {
			indicator.classList.add('is-asc');
		} else if (dir === -1) {
			indicator.classList.add('is-desc');
		}
	}

	function applyWarehouseSort(board, key, dir, activeHeader) {
		var table;
		var headers;

		if (!board || !key) {
			return;
		}

		table = board.querySelector('.logistica-warehouse-table');
		headers = table ? table.querySelectorAll('thead th.logistica-warehouse-sortable') : [];

		headers.forEach(function (header) {
			var indicator = header.querySelector('.logistica-warehouse-sort-indicator');
			header.classList.remove('logistica-warehouse-sort-active');
			setTableSortIndicator(indicator, null);
		});

		if (activeHeader) {
			activeHeader.classList.add('logistica-warehouse-sort-active');
			setTableSortIndicator(activeHeader.querySelector('.logistica-warehouse-sort-indicator'), dir);
		} else {
			headers.forEach(function (header) {
				if (header.getAttribute('data-warehouse-sort') === key) {
					header.classList.add('logistica-warehouse-sort-active');
					setTableSortIndicator(header.querySelector('.logistica-warehouse-sort-indicator'), dir);
				}
			});
		}

		board._warehouseSortState = { key: key, dir: dir };
		loadWarehousePage(board, { resetPage: true });
	}

	function initWarehouseSort(board) {
		var table;
		var headers;
		var sortState;

		if (!board || board.dataset.warehouseSortBound === '1') {
			return;
		}

		table = board.querySelector('.logistica-warehouse-table');
		headers = table ? table.querySelectorAll('thead th.logistica-warehouse-sortable') : [];
		sortState = { key: 'codigo', dir: 1 };
		board._warehouseSortState = sortState;

		headers.forEach(function (header) {
			header.addEventListener('click', function () {
				var key = header.getAttribute('data-warehouse-sort') || '';
				var dir;

				if (!key) {
					return;
				}

				dir = sortState.key === key ? sortState.dir * -1 : 1;
				sortState.key = key;
				sortState.dir = dir;
				board._warehouseSortState = sortState;
				applyWarehouseSort(board, key, dir, header);
			});
		});

		board.dataset.warehouseSortBound = '1';
	}

	function getTablePaginationState(board, prefix) {
		var storageKey = 'logistica_' + prefix + '_page_size_v1';
		var savedSize;

		if (!board._tablePaginationState) {
			board._tablePaginationState = {};
		}

		if (!board._tablePaginationState[prefix]) {
			savedSize = parseInt(localStorage.getItem(storageKey), 10);
			board._tablePaginationState[prefix] = {
				page: 1,
				pageSize: Number.isFinite(savedSize) ? savedSize : 25
			};
		}

		return board._tablePaginationState[prefix];
	}

	function isTablePaginationBound(board, prefix) {
		if (!board._tablePaginationBound) {
			return false;
		}

		return board._tablePaginationBound[prefix] === true;
	}

	function markTablePaginationBound(board, prefix) {
		if (!board._tablePaginationBound) {
			board._tablePaginationBound = {};
		}

		board._tablePaginationBound[prefix] = true;
	}

	function applyTablePagination(board, prefix, rows, options) {
		var state = getTablePaginationState(board, prefix);
		var pagination = board.querySelector('[data-' + prefix + '-pagination]');
		var pageSizeSelect = board.querySelector('[data-' + prefix + '-page-size]');
		var prevBtn = board.querySelector('[data-action="' + prefix + '-page-prev"]');
		var nextBtn = board.querySelector('[data-action="' + prefix + '-page-next"]');
		var label = board.querySelector('[data-' + prefix + '-page-label]');
		var info = board.querySelector('[data-' + prefix + '-page-info]');
		var visibleRows = [];
		var totalPages;
		var start;
		var end;
		var from;
		var to;

		options = options || {};

		if (options.resetPage) {
			state.page = 1;
		}

		if (pageSizeSelect) {
			if (!pageSizeSelect.dataset.paginationBound) {
				pageSizeSelect.value = String(state.pageSize);
			} else if (parseInt(pageSizeSelect.value, 10) !== state.pageSize) {
				state.pageSize = parseInt(pageSizeSelect.value, 10) || 25;
			}
		}

		rows.forEach(function (row) {
			if (row.dataset.logisticaFilterVisible === '1') {
				visibleRows.push(row);
			}
		});

		totalPages = Math.max(1, Math.ceil(visibleRows.length / state.pageSize));

		if (state.page > totalPages) {
			state.page = totalPages;
		}

		if (state.page < 1) {
			state.page = 1;
		}

		start = (state.page - 1) * state.pageSize;
		end = start + state.pageSize;

		visibleRows.forEach(function (row, index) {
			row.dataset.logisticaVisibleIndex = String(index);
		});

		rows.forEach(function (row) {
			var index;

			if (row.dataset.logisticaFilterVisible !== '1') {
				row.hidden = true;
				return;
			}

			index = parseInt(row.dataset.logisticaVisibleIndex, 10);
			row.hidden = index < start || index >= end;
		});

		if (label) {
			label.textContent = visibleRows.length > 0
				? ('Página ' + state.page + ' de ' + totalPages)
				: '';
		}

		if (info) {
			if (visibleRows.length === 0) {
				info.textContent = '';
			} else {
				from = start + 1;
				to = Math.min(end, visibleRows.length);
				info.textContent = 'Mostrando ' + from + '-' + to + ' de ' + visibleRows.length + ' registros';
			}
		}

		if (prevBtn) {
			prevBtn.disabled = state.page <= 1 || visibleRows.length === 0;
		}

		if (nextBtn) {
			nextBtn.disabled = state.page >= totalPages || visibleRows.length === 0;
		}

		if (pagination) {
			pagination.hidden = rows.length === 0;
		}
	}

	function bindTablePagination(board, prefix, onApply) {
		var state = getTablePaginationState(board, prefix);
		var pageSizeSelect = board.querySelector('[data-' + prefix + '-page-size]');

		if (isTablePaginationBound(board, prefix)) {
			return;
		}

		if (pageSizeSelect) {
			pageSizeSelect.value = String(state.pageSize);
			pageSizeSelect.dataset.paginationBound = '1';
			pageSizeSelect.addEventListener('change', function () {
				state.pageSize = parseInt(pageSizeSelect.value, 10) || 25;
				state.page = 1;

				try {
					localStorage.setItem('logistica_' + prefix + '_page_size_v1', String(state.pageSize));
				} catch (error) {
					// Storage may be unavailable.
				}

				onApply({ resetPage: true });
			});
		}

		board.addEventListener('click', function (event) {
			if (event.target.closest('[data-action="' + prefix + '-page-prev"]')) {
				state.page -= 1;
				onApply();
				return;
			}

			if (event.target.closest('[data-action="' + prefix + '-page-next"]')) {
				state.page += 1;
				onApply();
			}
		});

		markTablePaginationBound(board, prefix);
	}

	function applyWarehouseServerStats(board, stats) {
		var alertCount;
		var improvedCount;
		var okCount;
		var total;
		var alertText;
		var alertBox;
		var improvedStat;

		if (!board || !stats) {
			return;
		}

		total = Number(stats.total) || 0;
		alertCount = Number(stats.alert) || 0;
		okCount = Number(stats.ok);
		improvedCount = Number(stats.improved) || 0;

		if (!Number.isFinite(okCount)) {
			okCount = Math.max(0, total - alertCount);
		}

		board.querySelectorAll('[data-warehouse-stat="total"]').forEach(function (node) {
			node.textContent = String(total);
		});
		board.querySelectorAll('[data-warehouse-stat="alert"]').forEach(function (node) {
			node.textContent = String(alertCount);
		});
		board.querySelectorAll('[data-warehouse-stat="ok"]').forEach(function (node) {
			node.textContent = String(okCount);
		});

		improvedStat = board.querySelector('[data-warehouse-stat="improved"]');
		if (improvedStat) {
			improvedStat.textContent = String(improvedCount);
			if (improvedStat.closest('.logistica-warehouse-stat')) {
				improvedStat.closest('.logistica-warehouse-stat').hidden = improvedCount <= 0;
			}
		}

		alertBox = board.querySelector('[data-warehouse-alert]');
		alertText = board.querySelector('[data-warehouse-alert-text]');
		if (alertCount > 0) {
			if (!alertBox) {
				alertBox = document.createElement('div');
				alertBox.className = 'logistica-warehouse-alert';
				alertBox.setAttribute('role', 'alert');
				alertBox.setAttribute('data-warehouse-alert', '');
				alertBox.innerHTML = ''
					+ '<span class="logistica-warehouse-alert__icon dashicons dashicons-warning" aria-hidden="true"></span>'
					+ '<div><strong>Diferencias Kardex vs físico</strong><p data-warehouse-alert-text></p></div>';
				board.insertBefore(alertBox, board.querySelector('.logistica-warehouse-table-wrap') || board.querySelector('.logistica-warehouse-table-card'));
				alertText = alertBox.querySelector('[data-warehouse-alert-text]');
			}
			if (alertText) {
				alertText.textContent = 'Hay ' + alertCount + ' códigos con diferencia entre saldo bodega e inventario físico. Revisa el inventario físico o edita el registro.';
			}
		} else if (alertBox) {
			alertBox.remove();
		}
	}

	function updateWarehousePaginationUi(board, page, totalPages, total, perPage) {
		var pagination = board.querySelector('[data-warehouse-pagination]');
		var prevBtn = board.querySelector('[data-action="warehouse-page-prev"]');
		var nextBtn = board.querySelector('[data-action="warehouse-page-next"]');
		var label = board.querySelector('[data-warehouse-page-label]');
		var info = board.querySelector('[data-warehouse-page-info]');
		var from;
		var to;

		page = Math.max(1, Number(page) || 1);
		totalPages = Math.max(1, Number(totalPages) || 1);
		total = Math.max(0, Number(total) || 0);
		perPage = Math.max(1, Number(perPage) || 25);

		if (label) {
			label.textContent = total > 0 ? ('Página ' + page + ' de ' + totalPages) : '';
		}

		if (info) {
			if (total === 0) {
				info.textContent = '';
			} else {
				from = ((page - 1) * perPage) + 1;
				to = Math.min(page * perPage, total);
				info.textContent = 'Mostrando ' + from + '-' + to + ' de ' + total + ' registros';
			}
		}

		if (prevBtn) {
			prevBtn.disabled = page <= 1 || total === 0;
		}

		if (nextBtn) {
			nextBtn.disabled = page >= totalPages || total === 0;
		}

		if (pagination) {
			pagination.hidden = false;
		}
	}

	function renderWarehouseRows(board, rows) {
		var tbody = board ? board.querySelector('[data-warehouse-rows]') : null;
		var html = '';

		if (!tbody) {
			return;
		}

		if (!rows || !rows.length) {
			tbody.innerHTML = '<tr data-warehouse-empty><td colspan="13">Sin registros. Descarga la plantilla o agrega un código.</td></tr>';
			return;
		}

		rows.forEach(function (row) {
			html += buildWarehouseRowHtml(row);
		});
		tbody.innerHTML = html;
	}

	function loadWarehousePage(board, options) {
		var state;
		var sortState;
		var searchInput;
		var descripcionSelect;
		var claseSelect;
		var statusSelect;
		var saldoSelect;
		var clearButton;
		var emptyMessage;
		var meta;
		var filtersWrap;
		var badge;
		var query;
		var descripcion;
		var clase;
		var status;
		var saldo;
		var hasActiveFilter;
		var requestId;
		var tbody;

		if (!board) {
			return Promise.resolve();
		}

		options = options || {};
		state = getTablePaginationState(board, 'warehouse');
		sortState = board._warehouseSortState || { key: 'codigo', dir: 1 };

		if (options.resetPage) {
			state.page = 1;
		}

		searchInput = board.querySelector('[data-warehouse-filter-search]');
		descripcionSelect = board.querySelector('[data-warehouse-filter-descripcion]');
		claseSelect = board.querySelector('[data-warehouse-filter-clase]');
		statusSelect = board.querySelector('[data-warehouse-filter-status]');
		saldoSelect = board.querySelector('[data-warehouse-filter-saldo]');
		clearButton = board.querySelector('[data-action="clear-warehouse-filters"]');
		emptyMessage = board.querySelector('[data-warehouse-filter-empty]');
		meta = board.querySelector('[data-warehouse-filter-meta]');
		filtersWrap = board.querySelector('[data-warehouse-filters]');
		badge = board.querySelector('[data-warehouse-filter-badge]');
		tbody = board.querySelector('[data-warehouse-rows]');

		query = searchInput ? String(searchInput.value || '').trim() : '';
		descripcion = descripcionSelect ? String(descripcionSelect.value || '') : '';
		clase = claseSelect ? String(claseSelect.value || '') : '';
		status = statusSelect ? String(statusSelect.value || '') : '';
		saldo = saldoSelect ? String(saldoSelect.value || '') : '';
		hasActiveFilter = !!query || !!descripcion || !!clase || !!status || !!saldo;

		if (filtersWrap) {
			filtersWrap.classList.toggle('is-active', hasActiveFilter);
		}
		if (badge) {
			badge.hidden = !hasActiveFilter;
		}
		if (clearButton) {
			clearButton.disabled = !hasActiveFilter;
		}

		requestId = (board._warehouseRequestId || 0) + 1;
		board._warehouseRequestId = requestId;
		board.classList.add('is-warehouse-loading');

		if (tbody && !tbody.querySelector('[data-warehouse-row]')) {
			tbody.innerHTML = '<tr data-warehouse-loading><td colspan="13">Cargando inventario…</td></tr>';
		}

		return warehouseAjax('logistica_query_warehouse_rows', {
			search: query,
			descripcion: descripcion,
			clase: clase,
			status: status,
			saldo: saldo,
			sort: sortState.key || 'codigo',
			dir: sortState.dir === -1 ? 'desc' : 'asc',
			page: state.page,
			per_page: state.pageSize,
			include_stats: options.refreshStats ? '1' : '0'
		}).then(function (payload) {
			var data;
			var total;
			var page;
			var totalPages;
			var perPage;

			if (board._warehouseRequestId !== requestId) {
				return;
			}

			board.classList.remove('is-warehouse-loading');

			if (!payload || !payload.success || !payload.data) {
				if (tbody) {
					tbody.innerHTML = '<tr data-warehouse-empty><td colspan="13">'
						+ ((payload && payload.data && payload.data.message)
							? String(payload.data.message)
							: 'No se pudo cargar el inventario.')
						+ '</td></tr>';
				}
				return;
			}

			data = payload.data;
			total = Number(data.total) || 0;
			page = Number(data.page) || 1;
			totalPages = Number(data.total_pages) || 1;
			perPage = Number(data.per_page) || state.pageSize;
			state.page = page;
			state.pageSize = perPage;

			renderWarehouseRows(board, data.rows || []);
			updateWarehousePaginationUi(board, page, totalPages, total, perPage);
			if (data.stats) {
				applyWarehouseServerStats(board, data.stats);
			}

			if (emptyMessage) {
				emptyMessage.hidden = total > 0 || !hasActiveFilter;
				if (hasActiveFilter && total === 0) {
					emptyMessage.hidden = false;
				}
			}

			if (meta) {
				if (hasActiveFilter) {
					meta.textContent = total + ' códigos coinciden con el filtro';
					meta.hidden = false;
				} else {
					meta.textContent = '';
					meta.hidden = true;
				}
			}
		}).catch(function () {
			if (board._warehouseRequestId !== requestId) {
				return;
			}
			board.classList.remove('is-warehouse-loading');
			if (tbody) {
				tbody.innerHTML = '<tr data-warehouse-empty><td colspan="13">No se pudo cargar el inventario.</td></tr>';
			}
		});
	}

	function applyWarehouseFilters(board, options) {
		loadWarehousePage(board, options || {});
	}

	function resetWarehouseFilters(board) {
		var searchInput = board ? board.querySelector('[data-warehouse-filter-search]') : null;
		var descripcionSelect = board ? board.querySelector('[data-warehouse-filter-descripcion]') : null;
		var claseSelect = board ? board.querySelector('[data-warehouse-filter-clase]') : null;
		var statusSelect = board ? board.querySelector('[data-warehouse-filter-status]') : null;
		var saldoSelect = board ? board.querySelector('[data-warehouse-filter-saldo]') : null;

		if (searchInput) {
			searchInput.value = '';
		}

		if (descripcionSelect) {
			descripcionSelect.value = '';
		}

		if (claseSelect) {
			claseSelect.value = '';
		}

		if (statusSelect) {
			statusSelect.value = '';
		}

		if (saldoSelect) {
			saldoSelect.value = '';
		}

		applyWarehouseFilters(board, { resetPage: true });
	}

	function refreshWarehouseBoardStats(board) {
		loadWarehousePage(board, { refreshStats: true });
	}

	function warehouseActionsHtml(row) {
		var activo = row.registro_activo !== false && row.estado !== 'inactivo';
		var toggleLabel = activo ? 'Desactivar' : 'Activar';
		return ''
			+ '<div class="logistica-warehouse-actions">'
			+ '<button type="button" class="button button-small logistica-table-icon-btn logistica-warehouse-btn-edit" data-action="warehouse-edit" data-row-id="' + String(row.id || '').replace(/"/g, '&quot;') + '" aria-label="Editar" title="Editar">'
			+ '<span class="dashicons dashicons-edit" aria-hidden="true"></span></button>'
			+ '<button type="button" class="button button-small logistica-table-icon-btn logistica-warehouse-btn-duplicate" data-action="warehouse-duplicate" data-row-id="' + String(row.id || '').replace(/"/g, '&quot;') + '" aria-label="Duplicar" title="Duplicar">'
			+ '<span class="dashicons dashicons-admin-page" aria-hidden="true"></span></button>'
			+ '<button type="button" class="button button-small logistica-table-icon-btn logistica-warehouse-btn-toggle" data-action="warehouse-toggle" data-row-id="' + String(row.id || '').replace(/"/g, '&quot;') + '" data-estado="' + (activo ? 'activo' : 'inactivo') + '" aria-label="' + toggleLabel + '" title="' + toggleLabel + '">'
			+ '<span class="dashicons ' + (activo ? 'dashicons-hidden' : 'dashicons-visibility') + '" aria-hidden="true"></span></button>'
			+ '<button type="button" class="button button-small logistica-table-icon-btn logistica-warehouse-btn-delete" data-action="warehouse-delete" data-row-id="' + String(row.id || '').replace(/"/g, '&quot;') + '" aria-label="Eliminar" title="Eliminar">'
			+ '<span class="dashicons dashicons-trash" aria-hidden="true"></span></button>'
			+ '</div>';
	}

	function applyWarehouseRowState(row, data) {
		var diferenciaCell;
		var estadoCell;
		var fisicoInput;
		var outputCell;
		var className = '';
		var fieldIndex;
		var board;

		if (!row || !data) {
			return;
		}

		diferenciaCell = row.querySelector('[data-output="diferencia"]');
		estadoCell = row.querySelector('[data-output="estado"]');
		fisicoInput = row.querySelector('[data-field="inventario_fisico"]');

		for (fieldIndex = 0; fieldIndex < warehouseTextFields().length; fieldIndex += 1) {
			outputCell = row.querySelector('[data-output="' + warehouseTextFields()[fieldIndex] + '"]');
			if (outputCell && data[warehouseTextFields()[fieldIndex]] !== undefined) {
				outputCell.textContent = String(data[warehouseTextFields()[fieldIndex]] || '');
			}
		}

		if (fisicoInput && data.inventario_fisico !== undefined) {
			fisicoInput.value = formatWarehouseInputValue(data.inventario_fisico);
		}

		if (row.querySelector('[data-output="saldo_bodega"]') && data.saldo_bodega !== undefined) {
			row.querySelector('[data-output="saldo_bodega"]').textContent = formatWarehouseNumber(data.saldo_bodega);
		}

		if (diferenciaCell) {
			diferenciaCell.textContent = formatWarehouseNumber(data.diferencia);
			diferenciaCell.classList.remove('is-negative', 'is-positive', 'is-zero');
			diferenciaCell.classList.add(warehouseDiffClass(data.diferencia));
		}

		if (estadoCell) {
			estadoCell.innerHTML = warehouseStatusBadge(data);
		}

		if (row.querySelector('[data-output="registro"]')) {
			row.querySelector('[data-output="registro"]').innerHTML = warehouseRegistroBadge(data);
		}

		if (row.querySelector('[data-output="fecha_creacion"]') && data.fecha_creacion_label !== undefined) {
			row.querySelector('[data-output="fecha_creacion"]').textContent = String(data.fecha_creacion_label || '');
		}

		if (row.querySelector('.is-lote')) {
			row.querySelector('.is-lote').innerHTML = warehouseLoteCellHtml(data);
		}

		if (row.querySelector('.logistica-warehouse-actions-cell')) {
			row.querySelector('.logistica-warehouse-actions-cell').innerHTML = warehouseActionsHtml(data);
		}

		className = data.tiene_alerta ? 'has-warehouse-diff' : '';
		if (data.mejoro) {
			className += ' has-warehouse-improved';
		}
		row.className = className.trim();
		applyWarehouseRowMeta(row, data);
	}

	function fillWarehouseEditForm(form, row) {
		var field;

		if (!form || !row) {
			return;
		}

		form.querySelectorAll('[data-field]').forEach(function (input) {
			field = input.getAttribute('data-field') || '';
			if (!field) {
				return;
			}

			if (field === 'row_id') {
				input.value = row.id !== undefined && row.id !== null ? String(row.id) : '';
				return;
			}

			if (row[field] === undefined || row[field] === null) {
				input.value = '';
				return;
			}

			input.value = String(row[field]);
		});
	}

	function openWarehouseEditModal(board, rowId) {
		var modal = board.querySelector('[data-warehouse-modal]');
		var form = board.querySelector('[data-warehouse-edit-form]');
		var codigoLabel = board.querySelector('[data-warehouse-modal-codigo]');

		if (!modal || !form || !rowId) {
			return;
		}

		warehouseAjax('logistica_get_warehouse_row', { row_id: rowId }).then(function (payload) {
			if (!payload || !payload.success || !payload.data || !payload.data.row) {
				window.alert((payload && payload.data && payload.data.message) || 'No se pudo cargar el registro.');
				return;
			}

			fillWarehouseEditForm(form, payload.data.row);
			if (codigoLabel) {
				codigoLabel.textContent = (payload.data.row.codigo || '') + (payload.data.row.lote ? ' · ' + payload.data.row.lote : '');
			}
			modal.hidden = false;
			document.body.classList.add('logistica-warehouse-modal-open');

			var firstField = form.querySelector('input:not([type="hidden"]), textarea, select');
			if (firstField) {
				firstField.focus();
			}
		});
	}

	function closeWarehouseEditModal(board) {
		var modal = board ? board.querySelector('[data-warehouse-modal]') : null;

		if (!modal) {
			return;
		}

		modal.hidden = true;
		document.body.classList.remove('logistica-warehouse-modal-open');
	}

	function buildWarehouseRowHtml(row) {
		var className = row.tiene_alerta ? 'has-warehouse-diff' : '';
		if (row.mejoro) {
			className += ' has-warehouse-improved';
		}

		var registro = row.registro_activo === false || row.estado === 'inactivo' ? 'inactivo' : 'activo';
		if (registro === 'inactivo') {
			className += ' is-warehouse-inactive';
		}

		return ''
			+ '<tr data-warehouse-row data-row-id="' + String(row.id || '').replace(/"/g, '&quot;') + '" data-codigo="' + String(row.codigo || '').replace(/"/g, '&quot;') + '" data-warehouse-status="' + warehouseRowStatus(row) + '" data-warehouse-registro="' + registro + '" data-warehouse-vigente="' + (row.es_lote_activo ? '1' : '0') + '" data-warehouse-search="' + String(warehouseRowSearchText(row)).replace(/"/g, '&quot;') + '"' + warehouseRowFilterAttrs(row) + warehouseRowSortAttrs(row) + ' class="' + className.trim() + '">'
			+ '<td class="is-codigo">' + warehouseCopyValueHtml(row.codigo, 'logistica-warehouse-codigo') + '</td>'
			+ '<td data-output="descripcion" class="is-description">' + String(row.descripcion || '') + '</td>'
			+ '<td data-output="materia_prima">' + String(row.materia_prima || '') + '</td>'
			+ '<td data-output="medida"><span class="logistica-warehouse-medida">' + String(row.medida || '') + '</span></td>'
			+ '<td class="is-lote">' + warehouseLoteCellHtml(row) + '</td>'
			+ '<td data-output="fecha_creacion" class="is-fecha">' + String(row.fecha_creacion_label || '') + '</td>'
			+ '<td data-output="saldo_bodega" class="is-numeric">' + formatWarehouseNumber(row.saldo_bodega) + '</td>'
			+ '<td data-output="clase"><span class="logistica-warehouse-clase">' + String(row.clase || '') + '</span></td>'
			+ '<td class="is-inventario-fisico"><input type="number" step="0.01" data-field="inventario_fisico" value="' + formatWarehouseInputValue(row.inventario_fisico ?? 0) + '" aria-label="Inventario físico"></td>'
			+ '<td data-output="diferencia" class="is-numeric is-diff ' + warehouseDiffClass(row.diferencia) + '">' + formatWarehouseNumber(row.diferencia) + '</td>'
			+ '<td data-output="estado">' + warehouseStatusBadge(row) + '</td>'
			+ '<td data-output="registro">' + warehouseRegistroBadge(row) + '</td>'
			+ '<td class="logistica-warehouse-actions-cell">' + warehouseActionsHtml(row) + '</td>'
			+ '</tr>';
	}

	function parseMinutasNumber(value) {
		var normalized = String(value || '').replace(/[^\d,.\-]/g, '');
		if (normalized.indexOf(',') !== -1 && normalized.indexOf('.') !== -1) {
			normalized = normalized.replace(/\./g, '').replace(',', '.');
		} else {
			normalized = normalized.replace(',', '.');
		}
		var parsed = parseFloat(normalized);
		return isFinite(parsed) ? parsed : 0;
	}

	function formatMinutasTotal(value) {
		var rounded = Math.round(parseMinutasNumber(value));
		return rounded.toLocaleString(undefined, { maximumFractionDigits: 0 });
	}

	function updateMinutasTableTotals(board, visibleCount, rowList) {
		var summary;
		var totalMinutos = 0;
		var totalBuenas = 0;
		var totalMalas = 0;
		var countNode;
		var row;

		if (!board) {
			return;
		}

		summary = board.querySelector('[data-minutas-table-summary]');
		if (!summary) {
			return;
		}

		rowList = rowList || [];
		rowList.forEach(function (item) {
			if (item.dataset.logisticaFilterVisible !== '1') {
				return;
			}
			totalMinutos += parseMinutasNumber(item.getAttribute('data-minutas-minutos'));
			totalBuenas += parseMinutasNumber(item.getAttribute('data-minutas-buenas'));
			totalMalas += parseMinutasNumber(item.getAttribute('data-minutas-malas'));
		});

		row = summary.querySelector('[data-minutas-total="minutos"]');
		if (row) {
			row.textContent = formatMinutasTotal(totalMinutos);
		}
		row = summary.querySelector('[data-minutas-total="buenas"]');
		if (row) {
			row.textContent = formatMinutasTotal(totalBuenas);
		}
		row = summary.querySelector('[data-minutas-total="malas"]');
		if (row) {
			row.textContent = formatMinutasTotal(totalMalas);
		}

		countNode = summary.querySelector('[data-minutas-summary-count]');
		if (countNode) {
			countNode.textContent = '(' + String(visibleCount || 0) + ')';
		}
	}

	function resolveMinutasTableField(target) {
		var cell;

		if (!target || !target.closest) {
			return null;
		}

		cell = target.closest('.logistica-minutas-table td[data-minutas-nav-cell]');
		if (!cell) {
			return null;
		}

		return minutasCellFocusTarget(cell);
	}

	function handleMinutasTableKeydown(event, board) {
		var field = resolveMinutasTableField(event.target);
		var copyInput = event.target.closest('.logistica-minutas-copy-input');
		var arrowKeys = {
			ArrowLeft: 'left',
			ArrowRight: 'right',
			ArrowUp: 'up',
			ArrowDown: 'down'
		};

		if (!field || !arrowKeys[event.key]) {
			if (copyInput && board && board.contains(copyInput) && (event.key === 'Enter' || event.key === ' ')) {
				event.preventDefault();
				copyExcelInputValue(copyInput);
			}
			return;
		}

		if (moveMinutasTableFocus(field, arrowKeys[event.key], board)) {
			event.preventDefault();
			event.stopImmediatePropagation();
			return;
		}

		if (copyInput || event.target.closest('[data-minutas-revision]')) {
			event.preventDefault();
		}
	}

	function initMinutasTableNavigation(board) {
		var table = board ? board.querySelector('.logistica-minutas-table') : null;

		if (!table || table.dataset.minutasNavBound === '1') {
			return;
		}

		table.dataset.minutasNavBound = '1';
		table.addEventListener('keydown', function (event) {
			handleMinutasTableKeydown(event, board);
		}, true);
		table.addEventListener('focusin', function (event) {
			var field = resolveMinutasTableField(event.target);
			var previous;

			if (!field) {
				return;
			}

			previous = table.querySelector('.is-minutas-cell-focused');
			if (previous && previous !== field) {
				previous.classList.remove('is-minutas-cell-focused');
			}

			field.classList.add('is-minutas-cell-focused');
		});
	}

	function getMinutasProcesoFilterValues(board) {
		var root = board ? board.querySelector('[data-minutas-filter-proceso]') : null;

		if (!root) {
			return [];
		}

		return Array.from(root.querySelectorAll('input[type="checkbox"]:checked')).map(function (checkbox) {
			return normalizeText(checkbox.value);
		}).filter(Boolean);
	}

	function updateMinutasProcesoFilterLabel(root) {
		var label = root ? root.querySelector('[data-minutas-filter-proceso-label]') : null;
		var checked;
		var defaultLabel;

		if (!label) {
			return;
		}

		checked = Array.from(root.querySelectorAll('input[type="checkbox"]:checked'));
		defaultLabel = label.getAttribute('data-default-label') || 'Todos';

		if (!checked.length) {
			label.textContent = defaultLabel;
			return;
		}

		if (checked.length === 1) {
			label.textContent = checked[0].value;
			return;
		}

		label.textContent = (label.getAttribute('data-selected-label') || '%d seleccionados').replace('%d', String(checked.length));
	}

	function closeMinutasProcesoFilter(root) {
		var trigger;
		var panel;

		if (!root) {
			return;
		}

		trigger = root.querySelector('[data-minutas-filter-proceso-trigger]');
		panel = root.querySelector('[data-minutas-filter-proceso-panel]');

		if (panel) {
			panel.hidden = true;
		}

		root.classList.remove('is-open');

		if (trigger) {
			trigger.setAttribute('aria-expanded', 'false');
		}
	}

	function resetMinutasProcesoFilter(board) {
		var root = board ? board.querySelector('[data-minutas-filter-proceso]') : null;

		if (!root) {
			return;
		}

		root.querySelectorAll('input[type="checkbox"]').forEach(function (checkbox) {
			checkbox.checked = false;
		});
		updateMinutasProcesoFilterLabel(root);
		closeMinutasProcesoFilter(root);
	}

	function initMinutasProcesoMultiselect(board) {
		var root = board ? board.querySelector('[data-minutas-filter-proceso]') : null;
		var trigger;
		var panel;

		if (!root || root.dataset.minutasProcesoBound === '1') {
			return;
		}

		root.dataset.minutasProcesoBound = '1';
		trigger = root.querySelector('[data-minutas-filter-proceso-trigger]');
		panel = root.querySelector('[data-minutas-filter-proceso-panel]');

		if (trigger && panel) {
			trigger.addEventListener('click', function (event) {
				var open = panel.hidden;

				event.preventDefault();
				event.stopPropagation();
				panel.hidden = !open;
				root.classList.toggle('is-open', open);
				trigger.setAttribute('aria-expanded', open ? 'true' : 'false');
			});
		}

		root.querySelectorAll('input[type="checkbox"]').forEach(function (checkbox) {
			checkbox.addEventListener('change', function () {
				updateMinutasProcesoFilterLabel(root);
				applyMinutasFilters(board, { resetPage: true });
			});
		});
	}

	function applyMinutasFilters(board, options) {
		var tbody;
		var rows;
		var searchInput;
		var clienteSelect;
		var revisionSelect;
		var clearButton;
		var meta;
		var filtersWrap;
		var badge;
		var procesoValues;
		var query;
		var cliente;
		var revision;
		var visible = 0;
		var total = 0;
		var hasActiveFilter;
		var rowList;

		if (!board) {
			return;
		}

		options = options || {};

		tbody = board.querySelector('[data-minutas-rows]');
		searchInput = board.querySelector('[data-minutas-filter-search]');
		clienteSelect = board.querySelector('[data-minutas-filter-cliente]');
		revisionSelect = board.querySelector('[data-minutas-filter-revision]');
		clearButton = board.querySelector('[data-action="clear-minutas-filters"]');
		meta = board.querySelector('[data-minutas-filter-meta]');
		filtersWrap = board.querySelector('[data-minutas-filters]');
		badge = board.querySelector('[data-minutas-filter-badge]');
		rows = tbody ? tbody.querySelectorAll('[data-minutas-row]') : [];
		rowList = Array.prototype.slice.call(rows);
		query = searchInput ? normalizeText(searchInput.value) : '';
		cliente = clienteSelect ? normalizeText(clienteSelect.value) : '';
		procesoValues = getMinutasProcesoFilterValues(board);
		revision = revisionSelect ? String(revisionSelect.value || '') : '';
		hasActiveFilter = !!query || !!cliente || procesoValues.length > 0 || !!revision;

		rowList.forEach(function (row) {
			var haystack = normalizeText(row.getAttribute('data-minutas-search') || '');
			var rowCliente = normalizeText(row.getAttribute('data-minutas-cliente') || '');
			var rowProceso = normalizeText(row.getAttribute('data-minutas-proceso') || '');
			var rowRevision = String(row.getAttribute('data-minutas-revision') || '');
			var matchesSearch = !query || haystack.indexOf(query) !== -1;
			var matchesCliente = !cliente || rowCliente === cliente;
			var matchesProceso = !procesoValues.length || procesoValues.indexOf(rowProceso) !== -1;
			var matchesRevision = !revision || rowRevision === revision;
			var show = matchesSearch && matchesCliente && matchesProceso && matchesRevision;

			total += 1;
			row.dataset.logisticaFilterVisible = show ? '1' : '0';

			if (show) {
				visible += 1;
			}
		});

		applyTablePagination(board, 'minutas', rowList, options);
		updateMinutasTableTotals(board, visible, rowList);

		if (filtersWrap) {
			filtersWrap.classList.toggle('is-active', hasActiveFilter);
		}

		if (badge) {
			badge.hidden = !hasActiveFilter;
		}

		if (clearButton) {
			clearButton.disabled = !hasActiveFilter;
		}

		if (meta) {
			if (hasActiveFilter) {
				meta.textContent = visible + ' de ' + total + ' minutas visibles';
				meta.hidden = false;
			} else {
				meta.textContent = '';
				meta.hidden = true;
			}
		}
	}

	function resetMinutasFilters(board) {
		if (!board) {
			return;
		}

		['[data-minutas-filter-search]', '[data-minutas-filter-cliente]', '[data-minutas-filter-fecha]', '[data-minutas-filter-empleado]', '[data-minutas-filter-revision]'].forEach(function (selector) {
			var field = board.querySelector(selector);
			if (field) {
				field.value = '';
			}
		});

		resetMinutasProcesoFilter(board);

		applyMinutasFilters(board, { resetPage: true });
	}

	function saveMinutaRevision(row, revision) {
		var cfg = window.LogisticaAdmin || {};
		var rowId = row ? row.getAttribute('data-row-id') : '';

		if (!rowId || !cfg.ajaxUrl || !window.fetch) {
			return Promise.resolve();
		}

		var data = new FormData();
		data.set('action', 'logistica_save_minuta_revision');
		data.set('nonce', cfg.minutasNonce || '');
		data.set('row_id', rowId);
		data.set('revision', revision || '');

		return window.fetch(cfg.ajaxUrl, {
			method: 'POST',
			credentials: 'same-origin',
			body: data
		}).then(function (response) {
			return response.json();
		}).then(function (payload) {
			if (!payload || !payload.success) {
				throw new Error((payload && payload.data && payload.data.message) || 'No se pudo guardar la revisión.');
			}
			row.setAttribute('data-minutas-revision', revision);
			row.classList.toggle('is-minutas-pendiente', revision === 'Pendiente');
		});
	}

	function hideMinutasTabLoading(boardOrPanel) {
		var panel = null;
		var board = null;
		var nodes = [];
		var i;
		var node;
		var overlays;
		var j;

		if (!boardOrPanel) {
			document.querySelectorAll('[data-minutas-board]').forEach(function (minutasBoard) {
				hideMinutasTabLoading(minutasBoard);
			});
			document.querySelectorAll('[data-tab-panel="logistica-revision-minuta"]').forEach(function (minutasPanel) {
				hideMinutasTabLoading(minutasPanel);
			});
			return;
		}

		if (boardOrPanel.matches && boardOrPanel.matches('[data-tab-panel]')) {
			panel = boardOrPanel;
			board = panel.querySelector('[data-minutas-board]');
		} else {
			board = boardOrPanel;
			panel = board.closest('[data-tab-panel]');
		}

		if (panel) {
			nodes.push(panel);
		}
		if (board && nodes.indexOf(board) === -1) {
			nodes.push(board);
		}

		for (i = 0; i < nodes.length; i++) {
			node = nodes[i];
			node.dataset.tabLoaded = '1';
			node.classList.remove('is-tab-loading');
			overlays = node.querySelectorAll('[data-logistica-tab-loading], [data-user-panel-loading], [data-close-loading]');
			for (j = 0; j < overlays.length; j++) {
				overlays[j].hidden = true;
				overlays[j].setAttribute('aria-busy', 'false');
			}
			if (window.LogisticaTabLoading) {
				window.LogisticaTabLoading.hide(node);
			}
		}
	}

	function initMinutasBoard() {
		document.querySelectorAll('[data-minutas-board]').forEach(function (board) {
			var searchInput = board.querySelector('[data-minutas-filter-search]');
			var clienteSelect = board.querySelector('[data-minutas-filter-cliente]');
			var fechaSelect = board.querySelector('[data-minutas-filter-fecha]');
			var empleadoSelect = board.querySelector('[data-minutas-filter-empleado]');
			var revisionSelect = board.querySelector('[data-minutas-filter-revision]');
			var importInput = board.querySelector('[data-minutas-import-file]');

			initMinutasProcesoMultiselect(board);
			initMinutasTableNavigation(board);

			if (searchInput && searchInput.dataset.minutasFilterBound !== '1') {
				searchInput.dataset.minutasFilterBound = '1';
				searchInput.addEventListener('input', function () {
					applyMinutasFilters(board, { resetPage: true });
				});
			}

			[clienteSelect, fechaSelect, empleadoSelect, revisionSelect].forEach(function (field) {
				if (field && field.dataset.minutasFilterBound !== '1') {
					field.dataset.minutasFilterBound = '1';
					field.addEventListener('change', function () {
						applyMinutasFilters(board, { resetPage: true });
					});
				}
			});

			bindTablePagination(board, 'minutas', function (options) {
				applyMinutasFilters(board, options || {});
			});

			if (board.querySelector('[data-excel-zoom-root]')) {
				initExcelTableZoom(board.querySelector('[data-excel-zoom-root]'));
			}

			if (board.dataset.minutasBoardBound !== '1') {
				board.dataset.minutasBoardBound = '1';

				board.addEventListener('click', function (event) {
					var copyInput = event.target.closest('.logistica-minutas-copy-input');
					var navCell = event.target.closest('td[data-minutas-nav-cell][tabindex]');
					var procesoFilter = event.target.closest('[data-minutas-filter-proceso]');

					if (event.target.closest('[data-action="clear-minutas-filters"]')) {
						resetMinutasFilters(board);
						return;
					}

					if (!procesoFilter) {
						board.querySelectorAll('[data-minutas-filter-proceso].is-open').forEach(function (root) {
							closeMinutasProcesoFilter(root);
						});
					}

					if (copyInput && board.contains(copyInput)) {
						copyExcelInputValue(copyInput);
						copyInput.focus();
						return;
					}

					if (navCell && board.contains(navCell)) {
						navCell.focus();
					}
				});

				board.addEventListener('change', function (event) {
					var select = event.target.closest('[data-minutas-revision]');
					if (!select || !board.contains(select)) {
						return;
					}
					var row = select.closest('[data-minutas-row]');
					if (!row) {
						return;
					}
					saveMinutaRevision(row, select.value).catch(function (error) {
						window.alert(error && error.message ? error.message : 'No se pudo guardar la revisión.');
					});
				});
			}

			if (importInput && importInput.dataset.minutasImportBound !== '1') {
				importInput.dataset.minutasImportBound = '1';
				importInput.addEventListener('change', function () {
					var form = document.getElementById('logistica-minutas-excel-upload');
					if (form && importInput.files && importInput.files.length) {
						form.submit();
					}
				});
			}

			try {
				applyMinutasFilters(board);
			} catch (error) {
				// La paginación no debe bloquear la vista si falla la inicialización.
			}

			hideMinutasTabLoading(board);
		});
	}

	function warehouseFisicoInputs(board) {
		if (!board) {
			return [];
		}

		return Array.from(board.querySelectorAll('[data-warehouse-row]:not([hidden]) [data-field="inventario_fisico"]')).filter(function (input) {
			var row = input.closest('[data-warehouse-row]');
			return !!(row && !row.hidden && input.offsetParent !== null);
		});
	}

	function moveWarehouseFisicoFocus(input, direction) {
		var board;
		var inputs;
		var index;
		var next;

		if (!input || !input.closest) {
			return false;
		}

		board = input.closest('[data-warehouse-board]');
		if (!board) {
			return false;
		}

		inputs = warehouseFisicoInputs(board);
		index = inputs.indexOf(input);
		if (index < 0) {
			return false;
		}

		if (direction === 'up' || direction === 'prev') {
			next = inputs[index - 1] || null;
		} else if (direction === 'down' || direction === 'next') {
			next = inputs[index + 1] || null;
		} else {
			return false;
		}

		if (!next) {
			return false;
		}

		next.focus();
		if (typeof next.select === 'function') {
			window.requestAnimationFrame(function () {
				next.select();
			});
		}

		return true;
	}

	function initWarehouseInventory() {
		var boards = document.querySelectorAll('[data-warehouse-board]');
		var fisicoTimers = new WeakMap();

		boards.forEach(function (board) {
			var tbody = board.querySelector('[data-warehouse-rows]');
			var editForm = board.querySelector('[data-warehouse-edit-form]');
			var searchInput = board.querySelector('[data-warehouse-filter-search]');
			var descripcionSelect = board.querySelector('[data-warehouse-filter-descripcion]');
			var claseSelect = board.querySelector('[data-warehouse-filter-clase]');
			var statusSelect = board.querySelector('[data-warehouse-filter-status]');
			var saldoSelect = board.querySelector('[data-warehouse-filter-saldo]');

			if (searchInput && searchInput.dataset.warehouseFilterBound !== '1') {
				searchInput.dataset.warehouseFilterBound = '1';
				searchInput.addEventListener('input', function () {
					window.clearTimeout(board._warehouseSearchTimer);
					board._warehouseSearchTimer = window.setTimeout(function () {
						applyWarehouseFilters(board, { resetPage: true });
					}, 300);
				});
			}

			if (descripcionSelect && descripcionSelect.dataset.warehouseFilterBound !== '1') {
				descripcionSelect.dataset.warehouseFilterBound = '1';
				descripcionSelect.addEventListener('input', function () {
					window.clearTimeout(board._warehouseDescripcionTimer);
					board._warehouseDescripcionTimer = window.setTimeout(function () {
						applyWarehouseFilters(board, { resetPage: true });
					}, 300);
				});
				descripcionSelect.addEventListener('change', function () {
					window.clearTimeout(board._warehouseDescripcionTimer);
					applyWarehouseFilters(board, { resetPage: true });
				});
			}

			if (claseSelect && claseSelect.dataset.warehouseFilterBound !== '1') {
				claseSelect.dataset.warehouseFilterBound = '1';
				claseSelect.addEventListener('change', function () {
					applyWarehouseFilters(board, { resetPage: true });
				});
			}

			if (statusSelect && statusSelect.dataset.warehouseFilterBound !== '1') {
				statusSelect.dataset.warehouseFilterBound = '1';
				statusSelect.addEventListener('change', function () {
					applyWarehouseFilters(board, { resetPage: true });
				});
			}

			if (saldoSelect && saldoSelect.dataset.warehouseFilterBound !== '1') {
				saldoSelect.dataset.warehouseFilterBound = '1';
				saldoSelect.addEventListener('change', function () {
					applyWarehouseFilters(board, { resetPage: true });
				});
			}

			bindTablePagination(board, 'warehouse', function (options) {
				applyWarehouseFilters(board, options || {});
			});

			initWarehouseSort(board);

			if (board.querySelector('[data-excel-zoom-root]')) {
				initExcelTableZoom(board.querySelector('[data-excel-zoom-root]'));
			}

			board.addEventListener('click', function (event) {
				var clearButton = event.target.closest('[data-action="clear-warehouse-filters"]');
				var copyValue = event.target.closest('.logistica-warehouse-copy-value');

				if (clearButton) {
					resetWarehouseFilters(board);
					return;
				}

				if (copyValue && board.contains(copyValue)) {
					copyClosingCodLoteValue(copyValue);
				}
			});

			applyWarehouseFilters(board);

			var addForm = board.querySelector('[data-warehouse-add-form]');

			if (addForm && addForm.dataset.warehouseAddBound !== '1') {
				addForm.dataset.warehouseAddBound = '1';
				addForm.addEventListener('submit', function (event) {
					var saveButton;
					var params;
					var emptyRow;
					var newRow;
					var codigo;

					event.preventDefault();
					saveButton = addForm.querySelector('[data-warehouse-add-submit]');
					params = new URLSearchParams(new window.FormData(addForm));
					codigo = String(params.get('codigo') || '').trim();

					if (!codigo) {
						window.alert('El código es obligatorio.');
						return;
					}

					if (saveButton) {
						saveButton.disabled = true;
					}

					warehouseAjax('logistica_warehouse_add_row', Object.fromEntries(params.entries())).then(function (payload) {
						if (saveButton) {
							saveButton.disabled = false;
						}

						if (!payload || !payload.success || !payload.data || !payload.data.row) {
							window.alert((payload && payload.data && payload.data.message) || 'No se pudo crear el código.');
							return;
						}

						hideWarehouseAddForm(board);
						addForm.reset();
						loadWarehousePage(board, { resetPage: true, refreshStats: true });
					});
				});
			}

			if (editForm && editForm.dataset.warehouseEditBound !== '1') {
				editForm.dataset.warehouseEditBound = '1';
				editForm.addEventListener('submit', function (event) {
					var saveButton;
					var params;
					var tableRow;

					event.preventDefault();
					saveButton = editForm.querySelector('[data-warehouse-save]');
					params = new URLSearchParams(new window.FormData(editForm));

					if (saveButton) {
						saveButton.disabled = true;
					}

					warehouseAjax('logistica_save_warehouse_row', Object.fromEntries(params.entries())).then(function (payload) {
						if (saveButton) {
							saveButton.disabled = false;
						}

						if (!payload || !payload.success || !payload.data || !payload.data.row) {
							window.alert((payload && payload.data && payload.data.message) || 'No se pudo guardar.');
							return;
						}

						tableRow = tbody ? warehouseRowById(board, payload.data.row.id) : null;
						if (tableRow) {
							applyWarehouseRowState(tableRow, payload.data.row);
						}
						if (payload.data.row && payload.data.row.codigo) {
							syncWarehouseVigenteBadges(board, payload.data.row.codigo, payload.data.vigente_row_id || 0);
						}
						if (payload.data.lote_changed && payload.data.row && payload.data.row.lote !== undefined) {
							applyWarehouseLoteToClosing(payload.data.row.codigo, String(payload.data.row.lote || '').trim());
						} else if (payload.data.bases_synced > 0 && payload.data.row && payload.data.row.codigo) {
							applyWarehouseLoteToClosing(payload.data.row.codigo, String(payload.data.row.lote || '').trim());
						}
						closeWarehouseEditModal(board);
					});
				});
			}

			board.addEventListener('click', function (event) {
				var deleteButton = event.target.closest('[data-action="warehouse-delete"]');
				var duplicateButton = event.target.closest('[data-action="warehouse-duplicate"]');
				var toggleButton = event.target.closest('[data-action="warehouse-toggle"]');
				var editButton = event.target.closest('[data-action="warehouse-edit"]');
				var closeButton = event.target.closest('[data-action="warehouse-close-modal"]');
				var codigo;
				var lote;
				var rowId;
				var newLote;
				var tableRow;
				var emptyRow;
				var newRow;

				if (closeButton) {
					closeWarehouseEditModal(board);
					return;
				}

				if (editButton) {
					openWarehouseEditModal(board, editButton.getAttribute('data-row-id') || '');
					return;
				}

				if (toggleButton) {
					rowId = toggleButton.getAttribute('data-row-id') || '';
					warehouseAjax('logistica_warehouse_toggle_estado', {
						row_id: rowId,
						estado: toggleButton.getAttribute('data-estado') === 'activo' ? 'inactivo' : 'activo'
					}).then(function (payload) {
						if (!payload || !payload.success || !payload.data || !payload.data.row) {
							window.alert((payload && payload.data && payload.data.message) || 'No se pudo cambiar el estado.');
							return;
						}

						loadWarehousePage(board, { refreshStats: true });
					});
					return;
				}

				if (duplicateButton) {
					rowId = duplicateButton.getAttribute('data-row-id') || '';
					tableRow = duplicateButton.closest('[data-warehouse-row]');
					lote = tableRow ? (tableRow.querySelector('[data-output="lote"]') ? tableRow.querySelector('[data-output="lote"]').textContent.trim() : '') : '';
					newLote = window.prompt('Lote para la copia:', lote ? lote + '-COPIA' : 'LOTE-COPIA');
					if (!newLote || !String(newLote).trim()) {
						return;
					}

					warehouseAjax('logistica_warehouse_duplicate_row', {
						row_id: rowId,
						new_lote: String(newLote).trim()
					}).then(function (payload) {
						if (!payload || !payload.success || !payload.data || !payload.data.row) {
							window.alert((payload && payload.data && payload.data.message) || 'No se pudo duplicar el código.');
							return;
						}

						loadWarehousePage(board, { resetPage: true, refreshStats: true });
					});
					return;
				}

				if (deleteButton) {
					rowId = deleteButton.getAttribute('data-row-id') || '';
					tableRow = deleteButton.closest('[data-warehouse-row]');
					codigo = tableRow ? tableRow.getAttribute('data-codigo') || '' : '';
					lote = tableRow && tableRow.querySelector('[data-output="lote"]') ? tableRow.querySelector('[data-output="lote"]').textContent.trim() : '';
					if (!confirmLogisticaDelete('¿Eliminar este registro del inventario bodega?', codigo + (lote ? ' · ' + lote : ''))) {
						return;
					}

					warehouseAjax('logistica_warehouse_delete_row', { row_id: rowId }).then(function (payload) {
						if (!payload || !payload.success) {
							window.alert((payload && payload.data && payload.data.message) || 'No se pudo eliminar el código.');
							return;
						}

						loadWarehousePage(board, { refreshStats: true });
					});
					return;
				}
			});

			board.addEventListener('focusin', function (event) {
				var input = event.target;

				if (!input.matches('[data-field="inventario_fisico"]')) {
					return;
				}

				window.requestAnimationFrame(function () {
					input.select();
				});
			});

			board.addEventListener('keydown', function (event) {
				var input = event.target;
				var direction;

				if (!input.matches || !input.matches('[data-field="inventario_fisico"]')) {
					return;
				}

				if (event.key === 'ArrowUp') {
					direction = 'up';
				} else if (event.key === 'ArrowDown' || event.key === 'Enter') {
					direction = 'down';
				} else {
					return;
				}

				if (moveWarehouseFisicoFocus(input, direction)) {
					event.preventDefault();
					event.stopPropagation();
				} else if (event.key === 'ArrowUp' || event.key === 'ArrowDown') {
					// Evita que type=number cambie el valor con las flechas.
					event.preventDefault();
				}
			});

			board.addEventListener('mouseup', function (event) {
				var input = event.target;

				if (!input.matches('[data-field="inventario_fisico"]')) {
					return;
				}

				event.preventDefault();
			});

			board.addEventListener('change', function (event) {
				var input = event.target;
				var row;
				var rowId;

				if (!input.matches('[data-field="inventario_fisico"]')) {
					return;
				}

				row = input.closest('[data-warehouse-row]');
				rowId = row ? row.getAttribute('data-row-id') || '' : '';
				if (!rowId) {
					return;
				}

				window.clearTimeout(fisicoTimers.get(input));
				fisicoTimers.set(input, window.setTimeout(function () {
					warehouseAjax('logistica_save_warehouse_fisico', {
						row_id: rowId,
						inventario_fisico: input.value
					}).then(function (payload) {
						if (!payload || !payload.success || !payload.data || !payload.data.row) {
							return;
						}
						applyWarehouseRowState(row, payload.data.row);
					});
				}, 400));
			});

			document.addEventListener('keydown', function (event) {
				var modal = board.querySelector('[data-warehouse-modal]');

				if (event.key === 'Escape' && modal && !modal.hidden) {
					closeWarehouseEditModal(board);
				}
			});
		});
	}

	function propagateBaseFieldsToLines(baseCard, workspace) {
		if (!baseCard || !workspace) {
			return;
		}

		linesBoundToBase(workspace, baseCard).forEach(function (row) {
			syncClosingRowFromBase(row, baseCard);
		});
	}

	function isClosingBaseDataField(fieldName) {
		return [
			'cod',
			'lote',
			'largo',
			'ancho',
			'alto',
			'gramaje',
			'calibre',
			'ajuste',
			'clave',
			'rendimiento',
			'und_molde',
			'peso_tamano',
			'cod_saldo',
			'lote_saldo'
		].indexOf(fieldName) !== -1;
	}

	function handleClosingWorkspaceFieldEvent(event) {
		var closingWorkspace = event.target.closest('[data-close-workspace]');
		var baseCard;
		var fieldName;
		var nombreReference;

		if (!closingWorkspace) {
			return;
		}

		if (event.target.closest('[data-base-search]')) {
			scheduleClosingFilters(closingWorkspace);
			return;
		}

		fieldName = event.target.getAttribute('data-field') || '';

		if (fieldName === 'nombre') {
			if (event.target.readOnly) {
				return;
			}

			nombreReference = event.target.closest('[data-close-reference]');
			scheduleUpdateClosingWorkspace(closingWorkspace, {
				scopeReference: nombreReference
			});
			scheduleClosingNombreAutosave(closingWorkspace);

			if (event.type === 'change') {
				finishClosingReferenceNameEdit(closingWorkspace, nombreReference);
			}

			return;
		}

		var scopeReference = isClosingMatrixMetricField(event.target)
			? event.target.closest('[data-close-reference]')
			: null;

		if (event.target.matches('[data-saldo-input="und"]')) {
			setSaldoUndManual(event.target, String(event.target.value || '').trim() !== '');
		}

		if (fieldName === 'unidades_paquete') {
			var packRow = event.target.closest('[data-close-line]');
			var packReference = event.target.closest('[data-close-reference]');
			var acetatoBaseCard;
			var acetatoUndInput;
			var cortesValor;

			if (packReference && packRow && isClosingSharedPackRow(packRow)) {
				syncClosingSharedUnitsPerPackage(packReference, packRow);
			}

			// Acetato: Und x paquete de la matriz = despiece (cortes por lámina).
			if (packRow && normalizeText(textField(packRow, 'base_tipo')) === 'acetato') {
				cortesValor = closeNumber(packRow, 'unidades_paquete');
				acetatoBaseCard = findClosingBaseCardForRow(closingWorkspace, packRow);
				acetatoUndInput = acetatoBaseCard
					? acetatoBaseCard.querySelector('[data-field="und_molde"]')
					: null;

				if (acetatoUndInput && cortesValor > 0
					&& String(acetatoUndInput.value || '') !== String(cortesValor)) {
					acetatoUndInput.value = String(cortesValor);
					updateClosingBaseCard(acetatoBaseCard);
				}
			}
		}

		scheduleUpdateClosingWorkspace(closingWorkspace, {
			scopeReference: scopeReference
		});

		baseCard = event.target.closest('[data-close-base]');
		if (baseCard) {
			updateClosingBaseCard(baseCard);
			if (isClosingBaseDataField(fieldName)) {
				propagateBaseFieldsToLines(baseCard, closingWorkspace);
				scheduleUpdateClosingWorkspace(closingWorkspace, { immediate: true });
			}
			if (fieldName === 'cod') {
				handleClosingCodLoteSync(closingWorkspace, baseCard, fieldName);
			}
			if (fieldName === 'ajuste') {
				var ajusteCodigo = normalizeClosingCod(textField(baseCard, 'cod'))
					|| normalizeClosingCod(textField(baseCard, 'cod_saldo'));
				var ajusteValue = textField(baseCard, 'ajuste');

				if (ajusteCodigo && syncClosingAjusteByCod(closingWorkspace, ajusteCodigo, ajusteValue, baseCard)) {
					scheduleUpdateClosingWorkspace(closingWorkspace, { immediate: true });
				}
			}
		}

		if (['variante', 'formula_template', 'unidad_empaque'].indexOf(fieldName) !== -1) {
			refreshBaseReferenceOptions(closingWorkspace);
			filterClosingReferences(closingWorkspace);
			filterClosingBases(closingWorkspace);
		}

		if (fieldName !== 'variante') {
			autosaveClosingWorkspace(closingWorkspace);
		}
	}

	function refreshClosingBasesFromCatalog(button) {
		var workspace = button ? button.closest('[data-close-workspace]') : null;
		var status = workspace ? workspace.querySelector('[data-autosave-status]') : null;

		if (!window.LogisticaAdmin || !window.fetch) {
			return;
		}

		if (window.confirm && !window.confirm('Actualizar todas las bases permanentes según el catálogo de referencias y bloques? Se recargará la página.')) {
			return;
		}

		if (status) {
			setAutosaveStatus(workspace, 'Actualizando bases...', 'is-saving');
		}

		if (button) {
			button.disabled = true;
		}

		window.fetch(window.LogisticaAdmin.ajaxUrl, {
			method: 'POST',
			credentials: 'same-origin',
			headers: {
				'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
			},
			body: new URLSearchParams({
				action: 'logistica_refresh_cierre_bases',
				nonce: window.LogisticaAdmin.cierreRefreshNonce || '',
			}).toString()
		}).then(function (response) {
			return response.json();
		}).then(function (payload) {
			if (payload && payload.success) {
				window.location.reload();
				return;
			}

			if (status) {
				setAutosaveStatus(workspace, (payload && payload.data && payload.data.message) || 'No se pudieron actualizar las bases', 'is-error');
			}

			if (button) {
				button.disabled = false;
			}
		}).catch(function () {
			if (status) {
				setAutosaveStatus(workspace, 'Error al actualizar las bases', 'is-error');
			}

			if (button) {
				button.disabled = false;
			}
		});
	}

	function closingAutosaveErrorMessage(payload, fallback) {
		var message = fallback || 'No se pudo autoguardar';

		if (payload && payload.data) {
			if (typeof payload.data.message === 'string' && payload.data.message) {
				return payload.data.message;
			}

			if (Array.isArray(payload.data) && payload.data[0] && payload.data[0].message) {
				return payload.data[0].message;
			}
		}

		return message;
	}

	function autosaveClosingWorkspace(workspace, immediate) {
		var jsonField = getClosingJsonField(workspace);
		var data;
		var nonce;

		if (window.__logisticaSessionDead) {
			if (workspace) {
				setAutosaveStatus(workspace, 'Sesión expirada. Vuelve a iniciar sesión.', 'is-error');
			}
			if (window.LogisticaSessionGuardApi && typeof window.LogisticaSessionGuardApi.markExpired === 'function') {
				window.LogisticaSessionGuardApi.markExpired();
			}
			return;
		}

		if (!jsonField || typeof window === 'undefined' || !window.LogisticaAdmin || !window.fetch) {
			if (workspace) {
				setAutosaveStatus(workspace, 'Autoguardado no disponible (falta configuración AJAX)', 'is-error');
			}
			return;
		}

		nonce = window.LogisticaAdmin.cierreAutosaveNonce || '';
		if (!nonce) {
			if (workspace) {
				setAutosaveStatus(workspace, 'Autoguardado no disponible (falta nonce). Recarga la página.', 'is-error');
			}
			return;
		}

		window.clearTimeout(cierreAutosaveTimers.get(workspace));

		cierreAutosaveTimers.set(workspace, window.setTimeout(function () {
			setAutosaveStatus(workspace, 'Guardando en base de datos...', 'is-saving');
			updateClosingJsonField(workspace);

			if (!String(jsonField.value || '').trim()) {
				setAutosaveStatus(workspace, 'Sin datos para guardar. Recarga la página.', 'is-error');
				return;
			}

			data = new FormData();
			data.set('action', 'logistica_autosave_cierre_ordenes');
			data.set('nonce', nonce);
			data.set('cierre_json', jsonField.value || '');

			window.fetch(window.LogisticaAdmin.ajaxUrl, {
				method: 'POST',
				body: data,
				credentials: 'same-origin'
			})
				.then(function (response) {
					return response
						.json()
						.catch(function () {
							return null;
						})
						.then(function (payload) {
							return {
								ok: response.ok,
								payload: payload
							};
						});
				})
				.then(function (result) {
					var payload = result.payload;
					var timeLabel = logisticaColombiaTimeHms();

					if (result.ok && payload && payload.success) {
						setAutosaveStatus(
							workspace,
							'Guardado en base de datos ' + timeLabel,
							'is-saved'
						);
						return;
					}

					setAutosaveStatus(
						workspace,
						closingAutosaveErrorMessage(payload, 'No se pudo autoguardar en base de datos'),
						'is-error'
					);
				})
				.catch(function () {
					setAutosaveStatus(workspace, 'Error de conexión al guardar en base de datos', 'is-error');
				});
		}, immediate ? 0 : 900));
	}

	function updateCloseOrders(sheet) {
		sheet.querySelectorAll('[data-close-order]').forEach(function (order) {
			var produced = numberValue(order.querySelector('[data-field="produced_units"]'));
			var size = order.querySelector('[data-field="product_size"]') ? order.querySelector('[data-field="product_size"]').value : '';
			var cajasBase = findCloseBase(sheet, 'cajas', size);
			var saldoBase = findCloseBase(sheet, 'cajas_saldo', size);
			var bolsasBase = findCloseBase(sheet, 'bolsas', size);
			var rollosBase = findCloseBase(sheet, 'rollos', size);
			var cajaQty = numberValue(order.querySelector('[data-close-line="cajas"] [data-field="quantity"]'));

			order.querySelectorAll('[data-close-line]').forEach(function (row) {
				var type = row.getAttribute('data-close-line');
				var qty = field(row, 'quantity');
				var unitkg = 0;
				var adjusted = 0;
				var ajuste = 0;

				if (type === 'cajas') {
					ajuste = numberValue(cajasBase ? cajasBase.querySelector('[data-field="ajuste"]') : null);
					unitkg = qty > 0 ? produced / qty : 0;
					adjusted = unitkg > 0 ? Math.ceil(unitkg) + ajuste : 0;
					outputCloseLine(row, closeBaseValue(cajasBase, 'cod'), closeBaseValue(cajasBase, 'lote'), unitkg, adjusted);
				}

				if (type === 'caja_saldos') {
					ajuste = numberValue(saldoBase ? saldoBase.querySelector('[data-field="ajuste"]') : null);
					unitkg = cajaQty > 0 ? qty / cajaQty : 0;
					adjusted = unitkg > 0 ? Math.ceil(unitkg) + ajuste : 0;
					outputCloseLine(row, closeBaseValue(saldoBase, 'cod'), closeBaseValue(saldoBase, 'lote'), unitkg, adjusted);
				}

				if (type === 'bolsas') {
					ajuste = numberValue(bolsasBase ? bolsasBase.querySelector('[data-field="ajuste"]') : null);
					unitkg = qty > 0 ? produced / qty : 0;
					adjusted = unitkg > 0 ? Math.ceil(unitkg) + ajuste : 0;
					outputCloseLine(row, closeBaseValue(bolsasBase, 'cod'), closeBaseValue(bolsasBase, 'lote'), unitkg, adjusted);
				}

				if (type === 'rollos') {
					ajuste = numberValue(rollosBase ? rollosBase.querySelector('[data-field="ajuste"]') : null);
					unitkg = numberValue(rollosBase ? rollosBase.querySelector('[data-field="rendimiento"]') : null);
					unitkg = unitkg > 0 ? produced / unitkg : 0;
					adjusted = unitkg > 0 ? Math.ceil(unitkg) + ajuste : 0;
					outputCloseLine(row, closeBaseValue(rollosBase, 'cod'), closeBaseValue(rollosBase, 'lote'), unitkg, adjusted);
				}
			});
		});
	}

	function addSharedReference(sheet, silent) {
		var body = sheet.querySelector('[data-shared-refs]');
		var refs = body ? Array.from(body.querySelectorAll('tr:not([data-blank-ref]) [data-field="reference"]')) : [];
		var count = refs.reduce(function (highest, input) {
			var match = String(input.value || '').match(/ref\s*(\d+)/i);
			var number = match ? Number.parseInt(match[1], 10) : 0;

			return Math.max(highest, number);
		}, 0) + 1;
		var row;

		if (!body) {
			return;
		}

		row = document.createElement('tr');
		row.innerHTML = [
			'<td class="is-editable"><input type="text" data-field="reference" value="Ref ' + count + '"></td>',
			'<td class="is-editable"><input type="number" step="1" data-field="cavities" value="0"></td>',
			'<td class="is-editable"><input type="number" step="1" data-field="units" value="0"></td>',
			'<td data-output="sheets_ref"></td>',
			'<td class="is-result" data-output="distributed_size"></td>',
			'<td class="is-result" data-output="possible_units"></td>',
			'<td data-output="ref_status"></td>',
			conversionActionCell('duplicate-shared-ref', 'remove-shared-ref', 'Quitar referencia')
		].join('');
		body.insertBefore(row, body.querySelector('[data-blank-ref]'));
		updateSharedMountSheet(sheet);
		if (!silent) {
			focusGridField(row.querySelector('input'));
		}
	}

	function nextSharedRefLabel(body) {
		var refs = body ? Array.from(body.querySelectorAll('tr:not([data-blank-ref]) [data-field="reference"]')) : [];
		var highest = 0;

		refs.forEach(function (input) {
			var match = String(input.value || '').match(/ref\s*(\d+)/i);
			var number = match ? Number.parseInt(match[1], 10) : 0;

			highest = Math.max(highest, number);
		});

		return 'Ref ' + String(highest + 1);
	}

	function duplicateSharedReference(button) {
		var row = button.closest('tr');
		var sheet = button.closest('.logistica-sheet');
		var body = row ? row.closest('[data-shared-refs]') : null;
		var blankRow;
		var data;
		var newRow;
		var refInput;

		if (!row || !sheet || !body || row.hasAttribute('data-blank-ref')) {
			return;
		}

		data = rowInputData(row);
		addSharedReference(sheet, true);
		blankRow = body.querySelector('[data-blank-ref]');
		newRow = blankRow ? blankRow.previousElementSibling : null;
		applyRowInputData(newRow, data);
		refInput = newRow ? newRow.querySelector('[data-field="reference"]') : null;

		if (refInput) {
			refInput.value = nextSharedRefLabel(body);
		}

		updateSharedMountSheet(sheet);

		if (newRow) {
			focusGridField(newRow.querySelector('input'));
		}
	}

	function removeSharedReference(button) {
		var row = button.closest('tr');
		var sheet = button.closest('.logistica-sheet');
		var refInput;
		var label;

		if (!row || row.hasAttribute('data-blank-ref')) {
			return;
		}

		refInput = row.querySelector('[data-field="reference"]');
		label = refInput && refInput.value.trim() ? refInput.value.trim() : 'Referencia compartida';

		if (!confirmLogisticaDelete('¿Eliminar esta referencia del montaje compartido?', label)) {
			return;
		}

		row.remove();

		if (sheet) {
			updateSharedMountSheet(sheet);
		}
	}

	function focusableGridFields(container) {
		var fields;

		if (!container) {
			return [];
		}

		fields = Array.from(
			container.querySelectorAll(
				'input:not([type="hidden"]):not([readonly]):not(.logistica-readonly-field):not([tabindex="-1"]), select:not([disabled]):not([tabindex="-1"])'
			)
		);

		if (container.closest && container.closest('.logistica-close-matrix')) {
			fields = fields.concat(
				Array.from(container.querySelectorAll('.logistica-close-copy-value[tabindex="0"]'))
			);
			fields.sort(function (a, b) {
				if (a === b) {
					return 0;
				}

				return a.compareDocumentPosition(b) & Node.DOCUMENT_POSITION_FOLLOWING ? -1 : 1;
			});
		}

		return fields;
	}

	function focusGridField(target) {
		if (!target) {
			return false;
		}

		target.focus();

		if (typeof target.select === 'function') {
			window.requestAnimationFrame(function () {
				target.select();
			});
		}

		return true;
	}

	function isClosingPanelNavField(field) {
		if (!field || !field.matches || !field.closest('[data-close-workspace]')) {
			return false;
		}

		if (
			field.matches(
				'.logistica-close-matrix input:not([readonly]):not(.logistica-readonly-field), .logistica-close-matrix select:not([disabled]), .logistica-close-matrix .logistica-close-copy-value'
			)
		) {
			return true;
		}

		if (
			field.closest('[data-close-base]') &&
			field.closest('.logistica-base-grid') &&
			!field.readOnly &&
			!field.classList.contains('logistica-readonly-field') &&
			field.type !== 'hidden'
		) {
			return !field.closest('[data-base-body][hidden]');
		}

		return field.matches('[data-close-reference] .logistica-ref-meta [data-field="unidades"]');
	}

	function focusableClosingPanelFields(container) {
		if (!container) {
			return [];
		}

		return focusableGridFields(container).filter(function (field) {
			var label = field.closest('label');

			if (field.disabled || field.hidden) {
				return false;
			}

			if (label && label.hidden) {
				return false;
			}

			if (field.closest('[data-base-body][hidden]')) {
				return false;
			}

			return !field.closest('[hidden]') || !!field.closest('.logistica-base-grid');
		});
	}

	function visibleClosingBaseBody(card) {
		var body = card ? card.querySelector('[data-base-body]') : null;

		return !!(body && !body.hidden);
	}

	function focusableClosingBaseFields(card) {
		var grid = card ? card.querySelector('.logistica-base-grid') : null;

		if (!grid || !visibleClosingBaseBody(card)) {
			return [];
		}

		return focusableClosingPanelFields(grid);
	}

	function focusableClosingReferenceFields(reference) {
		var fields = [];
		var meta = reference ? reference.querySelector('.logistica-ref-meta') : null;
		var table = reference ? reference.querySelector('.logistica-close-matrix') : null;

		if (meta) {
			fields = fields.concat(focusableClosingPanelFields(meta));
		}

		if (table) {
			tableNavigableRows(table).forEach(function (row) {
				fields = fields.concat(focusableGridFields(row));
			});
		}

		return fields;
	}

	function moveIndexedPanelFocus(fields, field, direction) {
		var fieldIndex = fields.indexOf(field);
		var target;

		if (fieldIndex < 0) {
			return false;
		}

		if (direction === 'right' || direction === 'next' || direction === 'down') {
			if (fieldIndex < fields.length - 1) {
				target = fields[fieldIndex + 1];
			} else if (direction === 'next' || direction === 'down') {
				target = fields[0];
			}
		}

		if (direction === 'left' || direction === 'prev' || direction === 'up') {
			if (fieldIndex > 0) {
				target = fields[fieldIndex - 1];
			} else if (direction === 'prev' || direction === 'up') {
				target = fields[fields.length - 1];
			}
		}

		return focusGridField(target);
	}

	function moveClosingBaseGridFocus(field, direction) {
		var base = field.closest('[data-close-base]');
		var workspace = field.closest('[data-close-workspace]');
		var bases = workspace
			? Array.from(workspace.querySelectorAll('[data-close-base]')).filter(visibleClosingBaseBody)
			: [];
		var fields = focusableClosingBaseFields(base);
		var fieldIndex = fields.indexOf(field);
		var baseIndex = bases.indexOf(base);
		var nextFields;
		var target;
		var i;

		if (fieldIndex < 0) {
			return false;
		}

		if (direction === 'right' || direction === 'next' || direction === 'down') {
			if (fieldIndex < fields.length - 1) {
				target = fields[fieldIndex + 1];
			} else if (direction === 'next' || direction === 'down') {
				for (i = baseIndex + 1; i < bases.length; i++) {
					nextFields = focusableClosingBaseFields(bases[i]);
					if (nextFields.length) {
						target = nextFields[0];
						break;
					}
				}
			}
		}

		if (direction === 'left' || direction === 'prev' || direction === 'up') {
			if (fieldIndex > 0) {
				target = fields[fieldIndex - 1];
			} else if (direction === 'prev' || direction === 'up') {
				for (i = baseIndex - 1; i >= 0; i--) {
					nextFields = focusableClosingBaseFields(bases[i]);
					if (nextFields.length) {
						target = nextFields[nextFields.length - 1];
						break;
					}
				}
			}
		}

		return focusGridField(target);
	}

	function moveClosingFocus(field, direction) {
		if (field.closest('.logistica-close-matrix')) {
			return moveTableFocus(field, direction);
		}

		if (field.closest('[data-close-base]') && field.closest('.logistica-base-grid')) {
			return moveClosingBaseGridFocus(field, direction);
		}

		if (field.closest('[data-close-reference]')) {
			return moveIndexedPanelFocus(focusableClosingReferenceFields(field.closest('[data-close-reference]')), field, direction);
		}

		return false;
	}

	function tableNavigableRows(table) {
		var tbody = table ? table.querySelector('tbody') : null;
		var rows;

		if (tbody) {
			rows = Array.from(tbody.querySelectorAll('tr'));
		} else {
			rows = Array.from(table.querySelectorAll('tr'));
		}

		return rows.filter(function (navRow) {
			return !navRow.classList.contains('is-tapas-filter-hidden') && focusableGridFields(navRow).length > 0;
		});
	}

	function moveTableFocus(field, direction) {
		var cell = field.closest('td, th');
		var row = field.closest('tr');
		var table = field.closest('table');
		var rows;
		var rowIndex;
		var cellIndex;
		var target;
		var rowFields;
		var fieldIndex;
		var nextRow;
		var nextFields;
		var i;

		if (!cell || !row || !table) {
			return false;
		}

		rows = tableNavigableRows(table);
		rowIndex = rows.indexOf(row);
		cellIndex = Array.from(row.children).indexOf(cell);

		if (direction === 'left' || direction === 'right' || direction === 'next' || direction === 'prev') {
			rowFields = focusableGridFields(row);
			fieldIndex = rowFields.indexOf(field);

			if (direction === 'right' || direction === 'next') {
				if (fieldIndex < rowFields.length - 1) {
					target = rowFields[fieldIndex + 1];
				} else if (direction === 'next') {
					for (i = rowIndex + 1; i < rows.length; i++) {
						nextFields = focusableGridFields(rows[i]);
						if (nextFields.length) {
							target = nextFields[0];
							break;
						}
					}
				}
			}

			if (direction === 'left' || direction === 'prev') {
				if (fieldIndex > 0) {
					target = rowFields[fieldIndex - 1];
				} else if (direction === 'prev') {
					for (i = rowIndex - 1; i >= 0; i--) {
						nextFields = focusableGridFields(rows[i]);
						if (nextFields.length) {
							target = nextFields[nextFields.length - 1];
							break;
						}
					}
				}
			}
		}

		if (direction === 'up' || direction === 'down') {
			nextRow = rows[rowIndex + (direction === 'down' ? 1 : -1)];
			if (nextRow && nextRow.children[cellIndex]) {
				nextFields = focusableGridFields(nextRow.children[cellIndex]);
				target = nextFields[0] || nextRow.children[cellIndex].querySelector('input, select');
			}
		}

		return focusGridField(target);
	}

	function focusMinutasGridField(target) {
		var previous;

		if (!target) {
			return false;
		}

		previous = document.querySelector('.logistica-minutas-table .is-minutas-cell-focused');
		if (previous && previous !== target) {
			previous.classList.remove('is-minutas-cell-focused');
		}

		target.focus();
		target.classList.add('is-minutas-cell-focused');

		return true;
	}

	function minutasCellFocusTarget(cell) {
		if (!cell || !cell.matches || !cell.matches('td')) {
			return null;
		}

		return (
			cell.querySelector('.logistica-minutas-copy-input') ||
			cell.querySelector('select[data-minutas-revision]') ||
			(cell.hasAttribute('tabindex') ? cell : null)
		);
	}

	function focusableMinutasFields(container) {
		if (!container) {
			return [];
		}

		if (container.matches && container.matches('tr[data-minutas-row]')) {
			return Array.from(container.children).map(minutasCellFocusTarget).filter(Boolean);
		}

		var target = minutasCellFocusTarget(container);

		return target ? [target] : [];
	}

	function minutasNavigableRows(table) {
		var tbody = table ? table.querySelector('[data-minutas-rows]') : null;

		if (!tbody) {
			return [];
		}

		return Array.from(tbody.querySelectorAll('[data-minutas-row]')).filter(function (row) {
			return row.dataset.logisticaFilterVisible !== '0' && !row.hasAttribute('hidden');
		});
	}

	function minutasFocusPageRow(board, table, cellIndex, pageAction) {
		var button;
		var rows;
		var targetRow;
		var target;

		if (!board || !table || !pageAction) {
			return null;
		}

		button = board.querySelector('[data-action="' + pageAction + '"]');
		if (!button || button.disabled) {
			return null;
		}

		button.click();
		rows = minutasNavigableRows(table);
		targetRow = pageAction === 'minutas-page-next' ? rows[0] : rows[rows.length - 1];

		if (!targetRow || !targetRow.children[cellIndex]) {
			return null;
		}

		target = minutasCellFocusTarget(targetRow.children[cellIndex]);
		return target || null;
	}

	function moveMinutasTableFocus(field, direction, board) {
		var cell = field.closest('td, th');
		var row = field.closest('tr');
		var table = field.closest('.logistica-minutas-table');
		var rows;
		var rowIndex;
		var cellIndex;
		var target;
		var nextRow;

		if (!cell || !row || !table) {
			return false;
		}

		rows = minutasNavigableRows(table);
		rowIndex = rows.indexOf(row);

		if (rowIndex === -1) {
			return false;
		}

		cellIndex = Array.from(row.children).indexOf(cell);

		if (direction === 'left' || direction === 'right') {
			var nextCellIndex = cellIndex + (direction === 'right' ? 1 : -1);

			if (nextCellIndex >= 0 && nextCellIndex < row.children.length) {
				target = minutasCellFocusTarget(row.children[nextCellIndex]);
			}
		}

		if (direction === 'up' || direction === 'down') {
			nextRow = rows[rowIndex + (direction === 'down' ? 1 : -1)];
			if (nextRow && nextRow.children[cellIndex]) {
				target = minutasCellFocusTarget(nextRow.children[cellIndex]);
			} else if (board) {
				target = minutasFocusPageRow(
					board,
					table,
					cellIndex,
					direction === 'down' ? 'minutas-page-next' : 'minutas-page-prev'
				);
			}
		}

		if (!focusMinutasGridField(target)) {
			return false;
		}

		if (target.scrollIntoView) {
			target.scrollIntoView({ block: 'nearest', inline: 'nearest' });
		}

		return true;
	}

	function moveSheetFocus(input, direction) {
		return moveTableFocus(input, direction);
	}

	function visibleTapasCardFields(card) {
		return focusableGridFields(card).filter(function (field) {
			var otherWrap = field.closest('.logistica-tapas-field--status-other');

			return !(otherWrap && otherWrap.classList.contains('is-hidden'));
		});
	}

	function moveTapasCardFocus(field, direction) {
		var card = field.closest('[data-tapas-record]');
		var sheet = field.closest('.logistica-sheet[data-sheet="tapas-poliestireno"]');
		var body = sheet ? sheet.querySelector('[data-tapas-rows]') : null;
		var cards = body ? tapasMainRows(body) : [];
		var fields;
		var fieldIndex;
		var cardIndex;
		var nextFields;
		var target;
		var i;

		if (!card) {
			return false;
		}

		fields = visibleTapasCardFields(card);
		fieldIndex = fields.indexOf(field);
		cardIndex = cards.indexOf(card);

		if (direction === 'right' || direction === 'next' || direction === 'down') {
			if (fieldIndex >= 0 && fieldIndex < fields.length - 1) {
				target = fields[fieldIndex + 1];
			} else if (direction === 'next' || direction === 'down') {
				for (i = cardIndex + 1; i < cards.length; i++) {
					nextFields = visibleTapasCardFields(cards[i]);
					if (nextFields.length) {
						target = nextFields[0];
						break;
					}
				}
			}
		}

		if (direction === 'left' || direction === 'prev' || direction === 'up') {
			if (fieldIndex > 0) {
				target = fields[fieldIndex - 1];
			} else if (direction === 'prev' || direction === 'up') {
				for (i = cardIndex - 1; i >= 0; i--) {
					nextFields = visibleTapasCardFields(cards[i]);
					if (nextFields.length) {
						target = nextFields[nextFields.length - 1];
						break;
					}
				}
			}
		}

		return focusGridField(target);
	}

	function moveTapasSettingsFocus(field, direction) {
		var settings = field.closest('.logistica-tapas-settings');
		var fields = settings ? focusableGridFields(settings) : [];
		var fieldIndex = fields.indexOf(field);
		var target;

		if (fieldIndex < 0) {
			return false;
		}

		if (direction === 'right' || direction === 'next' || direction === 'down') {
			target = fields[fieldIndex + 1] || fields[0];
		}

		if (direction === 'left' || direction === 'prev' || direction === 'up') {
			target = fields[fieldIndex - 1] || fields[fields.length - 1];
		}

		return focusGridField(target);
	}

	function moveTapasFocus(field, direction) {
		if (field.closest('[data-tapas-record]')) {
			return moveTapasCardFocus(field, direction);
		}

		if (field.closest('.logistica-tapas-settings')) {
			return moveTapasSettingsFocus(field, direction);
		}

		if (field.closest('.logistica-tapas-excel-table')) {
			return moveTableFocus(field, direction);
		}

		return false;
	}

	function visibleBolsasCardFields(card) {
		return focusableGridFields(card).filter(function (field) {
			var otherWrap = field.closest('.logistica-tapas-field--status-other');

			return !(otherWrap && otherWrap.classList.contains('is-hidden'));
		});
	}

	function moveBolsasCardFocus(field, direction) {
		var card = field.closest('[data-bolsas-record]');
		var sheet = field.closest('.logistica-sheet[data-sheet="bolsas-calculo"]');
		var body = sheet ? sheet.querySelector('[data-bolsas-rows]') : null;
		var cards = body ? bolsasMainRows(body) : [];
		var fields;
		var fieldIndex;
		var cardIndex;
		var nextFields;
		var target;
		var i;

		if (!card) {
			return false;
		}

		fields = visibleBolsasCardFields(card);
		fieldIndex = fields.indexOf(field);
		cardIndex = cards.indexOf(card);

		if (direction === 'right' || direction === 'next' || direction === 'down') {
			if (fieldIndex >= 0 && fieldIndex < fields.length - 1) {
				target = fields[fieldIndex + 1];
			} else if (direction === 'next' || direction === 'down') {
				for (i = cardIndex + 1; i < cards.length; i++) {
					nextFields = visibleBolsasCardFields(cards[i]);
					if (nextFields.length) {
						target = nextFields[0];
						break;
					}
				}
			}
		}

		if (direction === 'left' || direction === 'prev' || direction === 'up') {
			if (fieldIndex > 0) {
				target = fields[fieldIndex - 1];
			} else if (direction === 'prev' || direction === 'up') {
				for (i = cardIndex - 1; i >= 0; i--) {
					nextFields = visibleBolsasCardFields(cards[i]);
					if (nextFields.length) {
						target = nextFields[nextFields.length - 1];
						break;
					}
				}
			}
		}

		return focusGridField(target);
	}

	function moveBolsasFiltersFocus(field, direction) {
		var wrap = field.closest('[data-bolsas-filters]');
		var fields = wrap ? focusableGridFields(wrap) : [];
		var fieldIndex = fields.indexOf(field);
		var target;

		if (fieldIndex < 0) {
			return false;
		}

		if (direction === 'right' || direction === 'next' || direction === 'down') {
			target = fields[fieldIndex + 1];
			if (!target && (direction === 'next' || direction === 'down')) {
				target = fields[0];
			}
		}

		if (direction === 'left' || direction === 'prev' || direction === 'up') {
			target = fields[fieldIndex - 1];
			if (!target && (direction === 'prev' || direction === 'up')) {
				target = fields[fields.length - 1];
			}
		}

		return focusGridField(target);
	}

	function moveBolsasFocus(field, direction) {
		if (field.closest('[data-bolsas-record]')) {
			return moveBolsasCardFocus(field, direction);
		}

		if (field.closest('[data-bolsas-filters]')) {
			return moveBolsasFiltersFocus(field, direction);
		}

		if (field.classList.contains('logistica-tapas-excel-copy-input')) {
			return moveTableFocus(field, direction);
		}

		return false;
	}

	function updateSheet(sheet) {
		var type = sheet.getAttribute('data-sheet');

		if (type === 'bobina-pliego') {
			updateBobinaPliego(sheet);
		}

		if (type === 'gramaje-papel') {
			updateGramajePapel(sheet);
		}

		if (type === 'calibre-material') {
			updateCalibreConversion(sheet);
		}

		if (type === 'montaje-compartido') {
			updateSharedMountSheet(sheet);
		}

		if (type === 'pliego-conversion') {
			updateAreaConversionSheet(sheet);
		}

		if (type === 'tapas-poliestireno') {
			updateTapasSheet(sheet);
		}

		if (type === 'bolsas-calculo') {
			updateBolsasSheet(sheet);
		}

		if (type === 'cierre-ordenes') {
			updateCloseOrders(sheet);
		}
	}

	document.addEventListener('focusin', function (event) {
		if (event.target.matches('.logistica-tapas-excel-copy-input:not(.logistica-minutas-copy-input)')) {
			window.requestAnimationFrame(function () {
				event.target.select();
			});
		}
	});

	document.addEventListener('input', function (event) {
		var productForm = event.target.closest('.logistica-form');
		var calculator = event.target.closest('.logistica-calculator');
		var sheet = event.target.closest('.logistica-sheet');
		var tapasCard = event.target.closest('[data-tapas-record]');

		if (productForm && event.target.classList.contains('js-total-source')) {
			updateProductTotal(productForm);
		}

		if (calculator) {
			updateCalculator(calculator);
		}

		if (tapasCard && event.target.getAttribute('data-field') === 'status_other') {
			updateTapasStatusBadge(tapasCard);
		}

		var bolsasCardInput = event.target.closest('[data-bolsas-record]');

		if (bolsasCardInput && event.target.getAttribute('data-field') === 'status_other') {
			updateTapasStatusBadge(bolsasCardInput);
		}

		if (sheet && tapasCard) {
			var ui = getTapasCardsUi(sheet);

			if (ui.panel && ui.panel.dataset.tapasView === 'single') {
				setTapasCardsView(sheet, 'single', getTapasCardIndex(tapasCard));
			}
		}

		var bolsasCard = event.target.closest('[data-bolsas-record]');

		if (sheet && bolsasCard) {
			var bolsasUi = getBolsasCardsUi(sheet);

			if (bolsasUi.panel && bolsasUi.panel.dataset.bolsasView === 'single') {
				setBolsasCardsView(sheet, 'single', getBolsasCardIndex(bolsasCard));
				if (bolsasUi.editLabel) {
					bolsasUi.editLabel.textContent = bolsasFieldText(bolsasCard, 'descripcion') || '—';
				}
			}
		}

		if (sheet) {
			updateSheet(sheet);
			saveConversionState();
		}
	});

	document.addEventListener('change', function (event) {
		var tapasCard = event.target.closest('[data-tapas-record]');
		var bolsasCard = event.target.closest('[data-bolsas-record]');
		var sheet = event.target.closest('.logistica-sheet');
		var tapasImportInput = event.target.closest('[data-tapas-import-file]');
		var bolsasImportInput = event.target.closest('[data-bolsas-import-file]');

		if (tapasCard && event.target.getAttribute('data-field') === 'status') {
			toggleTapasStatusOther(tapasCard);
			updateTapasStatusBadge(tapasCard);

			if (sheet) {
				updateSheet(sheet);
				saveConversionState();
			}
		}

		if (bolsasCard && event.target.getAttribute('data-field') === 'status') {
			toggleTapasStatusOther(bolsasCard);
			updateTapasStatusBadge(bolsasCard);

			if (sheet) {
				updateBolsasSheet(sheet);
				saveConversionState();
			}
		}

		if (tapasImportInput) {
			importTapasExcelFromFile(tapasImportInput);
		}

		if (bolsasImportInput) {
			importBolsasExcelFromFile(bolsasImportInput);
		}

		if (event.target.matches('[data-warehouse-import-file]') && event.target.files && event.target.files.length) {
			var warehouseImportForm = document.getElementById('logistica-warehouse-excel-upload');
			if (warehouseImportForm) {
				warehouseImportForm.submit();
			}
		}

		if (event.target.matches('[data-etiqueta-catalog-import-file]') && event.target.files && event.target.files.length) {
			var etiquetaCatalogImportForm = document.getElementById('logistica-etiqueta-productos-excel-upload');
			if (etiquetaCatalogImportForm) {
				etiquetaCatalogImportForm.submit();
			}
		}

		if (event.target.matches('[data-rotulo-rollos-import-file]') && event.target.files && event.target.files.length) {
			var rotuloRollosImportForm = document.getElementById('logistica-rotulo-rollos-excel-upload');
			if (rotuloRollosImportForm) {
				rotuloRollosImportForm.submit();
			}
		}

		if (event.target.matches('[data-cierre-car-import-file]') && event.target.files && event.target.files.length) {
			var cierreCarImportForm = document.getElementById('logistica-cierre-caracteristicas-excel-upload');
			if (cierreCarImportForm) {
				cierreCarImportForm.submit();
			}
		}

		if (event.target.matches('[data-cierre-import-file]') && event.target.files && event.target.files.length) {
			var cierreImportForm = document.getElementById('logistica-cierre-excel-upload');
			if (cierreImportForm) {
				cierreImportForm.submit();
			}
		}

		if (event.target.closest('[data-tapas-filters]') && event.target.matches('[data-tapas-filter]')) {
			sheet = event.target.closest('.logistica-sheet[data-sheet="tapas-poliestireno"]');
			if (sheet) {
				updateTapasSheet(sheet, { resetPage: true });
			}
		}

		if (event.target.closest('[data-bolsas-filters]') && event.target.matches('[data-bolsas-filter]')) {
			sheet = event.target.closest('.logistica-sheet[data-sheet="bolsas-calculo"]');
			if (sheet) {
				updateBolsasSheet(sheet, { resetPage: true });
			}
		}
	});

	function showWarehouseAddForm(board) {
		var formCard;
		var firstField;

		if (!board) {
			return;
		}

		formCard = board.querySelector('[data-warehouse-form-card]');
		if (!formCard) {
			return;
		}

		formCard.hidden = false;
		formCard.classList.add('is-open');
		formCard.scrollIntoView({ behavior: 'smooth', block: 'nearest' });

		firstField = board.querySelector('[data-warehouse-field]:not([readonly])');
		if (firstField) {
			window.requestAnimationFrame(function () {
				firstField.focus();
				if (typeof firstField.select === 'function' && firstField.type !== 'select-one') {
					firstField.select();
				}
			});
		}
	}

	function hideWarehouseAddForm(board) {
		var formCard;
		var form;

		if (!board) {
			return;
		}

		formCard = board.querySelector('[data-warehouse-form-card]');
		if (!formCard || formCard.hidden) {
			return;
		}

		form = formCard.querySelector('[data-warehouse-add-form]');
		formCard.hidden = true;
		formCard.classList.remove('is-open');

		if (form) {
			form.reset();
		}
	}

	function showCierreCaracteristicaForm(board) {
		var formCard;
		var firstField;

		if (!board) {
			return;
		}

		formCard = board.querySelector('[data-cierre-car-form-card]');
		if (!formCard) {
			return;
		}

		formCard.hidden = false;
		formCard.classList.add('is-open');
		formCard.scrollIntoView({ behavior: 'smooth', block: 'nearest' });

		firstField = board.querySelector('[data-cierre-car-field]:not([readonly])');
		if (firstField) {
			window.requestAnimationFrame(function () {
				firstField.focus();
				if (typeof firstField.select === 'function' && firstField.type !== 'select-one') {
					firstField.select();
				}
			});
		}
	}

	function hideCierreCaracteristicaForm(board) {
		var formCard;
		var form;

		if (!board) {
			return;
		}

		formCard = board.querySelector('[data-cierre-car-form-card]');
		if (!formCard || formCard.hidden) {
			return;
		}

		form = formCard.querySelector('[data-cierre-caracteristicas-form]');
		formCard.hidden = true;
		formCard.classList.remove('is-open');

		populateCierreCarForm(board, null);
		stripCierreCarEditUrl();
		setCierreCarStatus(board, '', '');
	}

	document.addEventListener('click', function (event) {
		var zoomInButton = event.target.closest('[data-action="excel-zoom-in"]');
		var zoomOutButton = event.target.closest('[data-action="excel-zoom-out"]');
		var densityButton = event.target.closest('[data-action="excel-density-toggle"]');
		var zoomControl = zoomInButton || zoomOutButton;
		var zoomRoot = zoomControl ? excelZoomRoot(zoomControl) : null;

		if (densityButton) {
			event.preventDefault();
			toggleExcelTableCompact();
			return;
		}

		if (zoomRoot) {
			stepExcelTableZoom(zoomRoot, zoomInButton ? EXCEL_ZOOM_STEP : -EXCEL_ZOOM_STEP);
			return;
		}

		var importWarehouseButton = event.target.closest('[data-action="import-warehouse-excel"]');
		var importCierreCarButton = event.target.closest('[data-action="import-cierre-car-excel"]');
		var importCierreButton = event.target.closest('[data-action="import-cierre-excel"]');
		var importEtiquetaCatalogButton = event.target.closest('[data-action="import-etiqueta-catalog-excel"]');
		var importRotuloRollosButton = event.target.closest('[data-action="import-rotulo-rollos-excel"]');
		var importMinutasButton = event.target.closest('[data-action="import-minutas-excel"]');
		var warehouseImportInput;
		var cierreCarImportInput;
		var cierreImportInput;
		var etiquetaCatalogImportInput;
		var rotuloRollosImportInput;
		var minutasImportInput;

		if (importMinutasButton) {
			minutasImportInput = importMinutasButton.closest('[data-minutas-board]');
			minutasImportInput = minutasImportInput ? minutasImportInput.querySelector('[data-minutas-import-file]') : null;
			if (minutasImportInput) {
				minutasImportInput.click();
			}
			return;
		}

		if (importRotuloRollosButton) {
			rotuloRollosImportInput = importRotuloRollosButton.closest('[data-rotulo-rollos-board]');
			rotuloRollosImportInput = rotuloRollosImportInput ? rotuloRollosImportInput.querySelector('[data-rotulo-rollos-import-file]') : null;
			if (rotuloRollosImportInput) {
				rotuloRollosImportInput.click();
			}
			return;
		}

		if (importEtiquetaCatalogButton) {
			etiquetaCatalogImportInput = importEtiquetaCatalogButton.closest('[data-etiqueta-catalog-board]');
			etiquetaCatalogImportInput = etiquetaCatalogImportInput ? etiquetaCatalogImportInput.querySelector('[data-etiqueta-catalog-import-file]') : null;
			if (etiquetaCatalogImportInput) {
				etiquetaCatalogImportInput.click();
			}
			return;
		}

		if (importWarehouseButton) {
			warehouseImportInput = importWarehouseButton.closest('[data-warehouse-board]');
			warehouseImportInput = warehouseImportInput ? warehouseImportInput.querySelector('[data-warehouse-import-file]') : null;
			if (warehouseImportInput) {
				warehouseImportInput.click();
			}
			return;
		}

		if (importCierreCarButton) {
			cierreCarImportInput = importCierreCarButton.closest('[data-cierre-caracteristicas-board]');
			cierreCarImportInput = cierreCarImportInput ? cierreCarImportInput.querySelector('[data-cierre-car-import-file]') : null;
			if (cierreCarImportInput) {
				cierreCarImportInput.click();
			}
			return;
		}

		if (importCierreButton) {
			cierreImportInput = document.getElementById('logistica-cierre-excel-file');
			if (cierreImportInput) {
				cierreImportInput.click();
			}
			return;
		}

		var addCierreCarButton = event.target.closest('[data-action="cierre-car-focus-form"]');
		var closeCierreCarFormButton = event.target.closest('[data-action="cierre-car-close-form"]');

		if (addCierreCarButton) {
			populateCierreCarForm(addCierreCarButton.closest('[data-cierre-caracteristicas-board]'), null);
			showCierreCaracteristicaForm(addCierreCarButton.closest('[data-cierre-caracteristicas-board]'));
			return;
		}

		if (closeCierreCarFormButton) {
			hideCierreCaracteristicaForm(closeCierreCarFormButton.closest('[data-cierre-caracteristicas-board]'));
			return;
		}

		var cancelCierreCarEditButton = event.target.closest('[data-action="cierre-car-cancel-edit"]');
		if (cancelCierreCarEditButton) {
			hideCierreCaracteristicaForm(cancelCierreCarEditButton.closest('[data-cierre-caracteristicas-board]'));
			return;
		}

		var editCierreCarButton = event.target.closest('[data-action="cierre-car-edit"]');
		if (editCierreCarButton) {
			var cierreCarBoard = editCierreCarButton.closest('[data-cierre-caracteristicas-board]');
			var cierreCarRowId = editCierreCarButton.getAttribute('data-row-id') || '';

			cierreCarAjax('logistica_get_cierre_caracteristica_row', { row_id: cierreCarRowId }).then(function (payload) {
				if (!payload || !payload.success || !payload.data || !payload.data.row) {
					window.alert((payload && payload.data && payload.data.message) || 'No se pudo cargar la característica.');
					return;
				}

				populateCierreCarForm(cierreCarBoard, payload.data.row);
				showCierreCaracteristicaForm(cierreCarBoard);
			});
			return;
		}

		var deleteCierreCarButton = event.target.closest('[data-action="cierre-car-delete"]');
		if (deleteCierreCarButton) {
			var deleteBoard = deleteCierreCarButton.closest('[data-cierre-caracteristicas-board]');
			var deleteRowId = deleteCierreCarButton.getAttribute('data-row-id') || '';

			if (!window.confirm('¿Eliminar esta característica?')) {
				return;
			}

			cierreCarAjax('logistica_delete_cierre_caracteristica', { row_id: deleteRowId }).then(function (payload) {
				if (!payload || !payload.success) {
					window.alert((payload && payload.data && payload.data.message) || 'No se pudo eliminar.');
					return;
				}

				removeCierreCarRow(deleteBoard, deleteRowId);
				refreshCierreCarStats(deleteBoard, payload.data.total);
				setCierreCarStatus(deleteBoard, payload.data.message || 'Característica eliminada.', 'success');

				var activeForm = deleteBoard.querySelector('[data-cierre-caracteristicas-form]');
				var activeRowIdField = activeForm ? activeForm.querySelector('[name="row_id"]') : null;
				if (activeRowIdField && String(activeRowIdField.value || '') === String(deleteRowId)) {
					hideCierreCaracteristicaForm(deleteBoard);
				}

				if (typeof deleteBoard.__logisticaRefreshCierreCarFilters === 'function') {
					deleteBoard.__logisticaRefreshCierreCarFilters({ resetPage: false });
				}
			});
			return;
		}

		var addWarehouseButton = event.target.closest('[data-action="warehouse-add-row"]');
		var closeWarehouseAddFormButton = event.target.closest('[data-action="warehouse-close-add-form"]');

		if (addWarehouseButton) {
			showWarehouseAddForm(addWarehouseButton.closest('[data-warehouse-board]'));
			return;
		}

		if (closeWarehouseAddFormButton) {
			hideWarehouseAddForm(closeWarehouseAddFormButton.closest('[data-warehouse-board]'));
			return;
		}

		var addButton = event.target.closest('[data-action="add-shared-ref"]');
		var addCutButton = event.target.closest('[data-action="add-pliego-cut"]');
		var addTapasButton = event.target.closest('[data-action="add-tapas-row"]');
		var saveSheetTemplateButton = event.target.closest('[data-action="save-sheet-template"]');
		var loadSheetTemplateButton = event.target.closest('[data-action="load-sheet-template"]');
		var editSheetTemplateButton = event.target.closest('[data-action="edit-sheet-template"]');
		var deleteSheetTemplateButton = event.target.closest('[data-action="delete-sheet-template"]');
		var removeTapasButton = event.target.closest('[data-action="remove-tapas-row"]');
		var duplicateTapasButton = event.target.closest('[data-action="duplicate-tapas-row"]');
		var closeTapasRowButton = event.target.closest('[data-action="close-tapas-row"]');
		var editTapasButton = event.target.closest('[data-action="edit-tapas-row"]');
		var confirmTapasButton = event.target.closest('[data-action="confirm-tapas-form"]');
		var cancelTapasButton = event.target.closest('[data-action="cancel-tapas-form"]');
		var toggleTapasCardsButton = event.target.closest('[data-action="toggle-tapas-cards"]');
		var exportTapasExcelButton = event.target.closest('[data-action="export-tapas-excel"]');
		var importTapasExcelButton = event.target.closest('[data-action="import-tapas-excel"]');
		var clearTapasFiltersButton = event.target.closest('[data-action="clear-tapas-filters"]');
		var addBolsasButton = event.target.closest('[data-action="add-bolsas-row"]');
		var removeBolsasButton = event.target.closest('[data-action="remove-bolsas-row"]');
		var duplicateBolsasButton = event.target.closest('[data-action="duplicate-bolsas-row"]');
		var closeBolsasRowButton = event.target.closest('[data-action="close-bolsas-row"]');
		var editBolsasButton = event.target.closest('[data-action="edit-bolsas-row"]');
		var confirmBolsasButton = event.target.closest('[data-action="confirm-bolsas-form"]');
		var cancelBolsasButton = event.target.closest('[data-action="cancel-bolsas-form"]');
		var exportBolsasExcelButton = event.target.closest('[data-action="export-bolsas-excel"]');
		var importBolsasExcelButton = event.target.closest('[data-action="import-bolsas-excel"]');
		var clearBolsasFiltersButton = event.target.closest('[data-action="clear-bolsas-filters"]');
		var copyExcelInput = event.target.closest('.logistica-tapas-excel-copy-input');
		var closeCopyCell = event.target.closest('.logistica-close-copy-value');
		var removeButton = event.target.closest('[data-action="remove-shared-ref"]');
		var removeCutButton = event.target.closest('[data-action="remove-pliego-cut"]');
		var duplicateSharedRefButton = event.target.closest('[data-action="duplicate-shared-ref"]');
		var duplicatePliegoCutButton = event.target.closest('[data-action="duplicate-pliego-cut"]');
		var addCloseRefButton = event.target.closest('[data-action="add-close-ref"]');
		var refreshCloseBasesButton = event.target.closest('[data-action="refresh-close-bases"]');
		var addCloseBlockButton = event.target.closest('[data-action="add-close-block"]');
		var addCloseSaldoBlockButton = event.target.closest('[data-action="add-close-saldo-block"]');
		var removeCloseBlockButton = event.target.closest('[data-action="remove-close-block"]');
		var editVariantButton = event.target.closest('[data-action="edit-close-variant"]');
		var deleteVariantButton = event.target.closest('[data-action="delete-close-variant"]');
		var toggleTapasCollapseButton = event.target.closest('[data-action="toggle-tapas-collapse"]');
		var toggleCloseBaseButton = event.target.closest('[data-action="toggle-close-base"]');
		var removeCloseBaseButton = event.target.closest('[data-action="remove-close-base"]');
		var restoreHabitualCodButton = event.target.closest('[data-action="restore-closing-habitual-cod"]');
		var sustitucionToggle = event.target.closest('[data-sustitucion-toggle]');
		var sheet = addButton ? addButton.closest('.logistica-sheet') : null;
		var cutSheet = addCutButton ? addCutButton.closest('.logistica-sheet') : null;
		var tapasSheet = addTapasButton ? addTapasButton.closest('.logistica-sheet') : (removeTapasButton ? removeTapasButton.closest('.logistica-sheet') : (duplicateTapasButton ? duplicateTapasButton.closest('.logistica-sheet') : (closeTapasRowButton ? closeTapasRowButton.closest('.logistica-sheet') : (confirmTapasButton ? confirmTapasButton.closest('.logistica-sheet') : (cancelTapasButton ? cancelTapasButton.closest('.logistica-sheet') : (toggleTapasCardsButton ? toggleTapasCardsButton.closest('.logistica-sheet') : (exportTapasExcelButton ? exportTapasExcelButton.closest('.logistica-sheet') : (importTapasExcelButton ? importTapasExcelButton.closest('.logistica-sheet') : (clearTapasFiltersButton ? clearTapasFiltersButton.closest('.logistica-sheet') : null)))))))));
		var bolsasSheet = addBolsasButton ? addBolsasButton.closest('.logistica-sheet') : (removeBolsasButton ? removeBolsasButton.closest('.logistica-sheet') : (duplicateBolsasButton ? duplicateBolsasButton.closest('.logistica-sheet') : (closeBolsasRowButton ? closeBolsasRowButton.closest('.logistica-sheet') : (exportBolsasExcelButton ? exportBolsasExcelButton.closest('.logistica-sheet') : (importBolsasExcelButton ? importBolsasExcelButton.closest('.logistica-sheet') : (editBolsasButton ? editBolsasButton.closest('.logistica-sheet') : (confirmBolsasButton ? confirmBolsasButton.closest('.logistica-sheet') : (cancelBolsasButton ? cancelBolsasButton.closest('.logistica-sheet') : (clearBolsasFiltersButton ? clearBolsasFiltersButton.closest('.logistica-sheet') : null)))))))));
		var closingWorkspace = addCloseRefButton ? addCloseRefButton.closest('[data-close-workspace]') : null;

		if (sheet) {
			addSharedReference(sheet);
			saveConversionState();
		}

		if (cutSheet) {
			addPliegoCut(cutSheet);
			saveConversionState();
		}

		if (addTapasButton && tapasSheet) {
			addTapasRow(tapasSheet);
			saveConversionState();
		}

		if (saveSheetTemplateButton) {
			saveSheetTemplate(saveSheetTemplateButton);
		}

		if (loadSheetTemplateButton) {
			loadSheetTemplate(loadSheetTemplateButton);
		}

		if (editSheetTemplateButton) {
			editSheetTemplate(editSheetTemplateButton);
		}

		if (deleteSheetTemplateButton) {
			deleteSheetTemplate(deleteSheetTemplateButton);
		}

		if (removeTapasButton && tapasSheet) {
			removeTapasRow(removeTapasButton);
			saveConversionState();
		}

		if (duplicateTapasButton && tapasSheet) {
			duplicateTapasRow(duplicateTapasButton);
			saveConversionState();
		}

		if (toggleTapasCardsButton) {
			toggleTapasCardsPanel(toggleTapasCardsButton);
		}

		if (clearTapasFiltersButton && tapasSheet) {
			clearTapasFilters(tapasSheet);
		}

		if (exportTapasExcelButton) {
			exportTapasExcel(exportTapasExcelButton);
		}

		if (importTapasExcelButton && tapasSheet) {
			var importInput = tapasSheet.querySelector('[data-tapas-import-file]');
			if (importInput) {
				importInput.click();
			}
		}

		if (editTapasButton) {
			editTapasRow(editTapasButton);
		}

		if (confirmTapasButton && tapasSheet) {
			confirmTapasForm(tapasSheet);
			saveConversionState();
		}

		if (cancelTapasButton && tapasSheet) {
			cancelTapasForm(tapasSheet);
			saveConversionState();
		}

		if (closeTapasRowButton && tapasSheet) {
			cancelTapasForm(tapasSheet);
			saveConversionState();
		}

		if (addBolsasButton && bolsasSheet) {
			addBolsasRow(bolsasSheet);
			saveConversionState();
		}

		if (removeBolsasButton) {
			removeBolsasRow(removeBolsasButton);
			saveConversionState();
		}

		if (duplicateBolsasButton) {
			duplicateBolsasRow(duplicateBolsasButton);
			saveConversionState();
		}

		if (clearBolsasFiltersButton && bolsasSheet) {
			clearBolsasFilters(bolsasSheet);
		}

		if (copyExcelInput && copyExcelInput.closest('[data-sheet="bolsas-calculo"]')) {
			copyExcelInputValue(copyExcelInput);
		}

		if (closeCopyCell && closeCopyCell.closest('.logistica-close-matrix')) {
			copyClosingCodLoteValue(closeCopyCell);
		}


		if (exportBolsasExcelButton) {
			exportBolsasExcel(exportBolsasExcelButton);
		}

		if (importBolsasExcelButton && bolsasSheet) {
			var bolsasImportInput = bolsasSheet.querySelector('[data-bolsas-import-file]');
			if (bolsasImportInput) {
				bolsasImportInput.click();
			}
		}

		if (editBolsasButton) {
			editBolsasRow(editBolsasButton);
		}

		if (confirmBolsasButton && bolsasSheet) {
			confirmBolsasForm(bolsasSheet);
			saveConversionState();
		}

		if (cancelBolsasButton && bolsasSheet) {
			cancelBolsasForm(bolsasSheet);
			saveConversionState();
		}

		if (closeBolsasRowButton && bolsasSheet) {
			cancelBolsasForm(bolsasSheet);
			saveConversionState();
		}

		if (duplicateSharedRefButton) {
			duplicateSharedReference(duplicateSharedRefButton);
			saveConversionState();
		}

		if (duplicatePliegoCutButton) {
			duplicatePliegoCut(duplicatePliegoCutButton);
			saveConversionState();
		}

		if (removeButton) {
			removeSharedReference(removeButton);
			saveConversionState();
		}

		if (removeCutButton) {
			removePliegoCut(removeCutButton);
			saveConversionState();
		}

		if (addCloseRefButton && closingWorkspace) {
			addClosingReference(closingWorkspace);
			autosaveClosingWorkspace(closingWorkspace, true);
		}

		if (refreshCloseBasesButton) {
			refreshClosingBasesFromCatalog(refreshCloseBasesButton);
		}

		if (addCloseBlockButton) {
			addClosingBlock(addCloseBlockButton);
			autosaveClosingWorkspace(addCloseBlockButton.closest('[data-close-workspace]'), true);
		}

		if (addCloseSaldoBlockButton) {
			addClosingSaldoBlock(addCloseSaldoBlockButton);
			autosaveClosingWorkspace(addCloseSaldoBlockButton.closest('[data-close-workspace]'), true);
		}

		if (removeCloseBlockButton) {
			closingWorkspace = removeCloseBlockButton.closest('[data-close-workspace]');
			removeClosingBlock(removeCloseBlockButton);
			autosaveClosingWorkspace(closingWorkspace, true);
		}

		if (editVariantButton) {
			toggleClosingReferenceMetaEdit(editVariantButton);
		}

		if (deleteVariantButton) {
			deleteClosingVariant(deleteVariantButton);
		}

		if (toggleTapasCollapseButton) {
			event.preventDefault();
			toggleTapasCollapse(toggleTapasCollapseButton);
		}

		if (toggleCloseBaseButton) {
			toggleClosingBase(toggleCloseBaseButton);
		}

		if (removeCloseBaseButton) {
			removeClosingBase(removeCloseBaseButton);
		}

		if (sustitucionToggle) {
			(function () {
				var sustCard = sustitucionToggle.closest('[data-close-base]');
				var sustWorkspace = sustCard ? sustCard.closest('[data-close-workspace]') : null;
				var habitual;

				if (!sustitucionToggle.checked && sustCard) {
					habitual = normalizeClosingCod(textField(sustCard, 'cod_habitual'));
					if (window.confirm && !window.confirm(
						habitual
							? ('¿Desactivar el aviso de sustitución temporal? Asegúrate de haber vuelto al código habitual ' + habitual + '.')
							: '¿Desactivar el aviso de sustitución temporal?'
					)) {
						sustitucionToggle.checked = true;
						return;
					}
				}

				setClosingBaseSustitucionActiva(sustCard, !!sustitucionToggle.checked);
				if (sustWorkspace) {
					autosaveClosingWorkspace(sustWorkspace);
				}
			})();
		}

		if (restoreHabitualCodButton) {
			event.preventDefault();
			restoreClosingHabitualCod(restoreHabitualCodButton);
		}

		var closeStatus = event.target.closest('.logistica-close-status');

		if (closeStatus) {
			event.preventDefault();
			event.stopPropagation();
			var wasOpen = closeStatus.classList.contains('is-open');

			document.querySelectorAll('.logistica-close-status.is-open').forEach(function (openStatus) {
				openStatus.classList.remove('is-open');
				openStatus.setAttribute('aria-expanded', 'false');
			});

			hideAllClosingStatusPortals();

			if (!wasOpen) {
				closeStatus.classList.add('is-open');
				closeStatus.setAttribute('aria-expanded', 'true');
				showClosingStatusPortal(closeStatus);
			}
		} else {
			document.querySelectorAll('.logistica-close-status.is-open').forEach(function (openStatus) {
				openStatus.classList.remove('is-open');
				openStatus.setAttribute('aria-expanded', 'false');
			});
			hideAllClosingStatusPortals();
		}

	});

	document.addEventListener('mouseover', function (event) {
		var closeStatus = event.target.closest('.logistica-close-status');

		if (!closeStatus) {
			return;
		}

		if (closingStatusPortalHideTimer) {
			clearTimeout(closingStatusPortalHideTimer);
			closingStatusPortalHideTimer = null;
		}

		showClosingStatusPortal(closeStatus);
	});

	document.addEventListener('mouseout', function (event) {
		var closeStatus = event.target.closest('.logistica-close-status');

		if (!closeStatus) {
			return;
		}

		var related = event.relatedTarget;

		if (related && (closeStatus.contains(related) || (closingStatusActivePortal && closingStatusActivePortal.contains(related)))) {
			return;
		}

		if (closeStatus.classList.contains('is-open')) {
			return;
		}

		scheduleHideClosingStatusPortal();
	});

	document.addEventListener('scroll', function () {
		if (closingStatusActivePortal && closingStatusActiveAnchor) {
			positionClosingStatusPortal(closingStatusActivePortal, closingStatusActiveAnchor);
		}
	}, true);

	window.addEventListener('resize', function () {
		if (closingStatusActivePortal && closingStatusActiveAnchor) {
			positionClosingStatusPortal(closingStatusActivePortal, closingStatusActiveAnchor);
		}
	});


	document.addEventListener('change', function (event) {
		var nombreSelect = event.target.closest('[data-close-reference] [data-field="nombre_selector"]');
		var variantSelect = event.target.closest('[data-close-reference] [data-field="variante_selector"]');
		var variantInput = event.target.closest('[data-close-reference] [data-field="variante"]');
		var productTypeSelect = event.target.closest('[data-close-reference] [data-field="tipo_producto_selector"]');
		var productTypeInput = event.target.closest('[data-close-reference] [data-field="tipo_producto"]');
		var referencePick = event.target.closest('[data-close-reference-pick]');
		var baseFilter = event.target.closest('[data-base-variant-filter], [data-base-type-filter], [data-base-reference-filter]');
		var baseTypeSelect = event.target.closest('[data-close-base] [data-field="tipo"]');

		if (nombreSelect) {
			selectClosingReferenceName(nombreSelect);
		}

		if (variantSelect) {
			selectClosingVariant(variantSelect);
		}

		if (variantInput) {
			saveTypedVariant(variantInput);
		}

		if (productTypeSelect) {
			selectProductType(productTypeSelect);
		}

		if (productTypeInput) {
			saveTypedProductType(productTypeInput);
		}

		if (referencePick) {
			var pickWorkspace = referencePick.closest('[data-close-workspace]');

			applyReferencePickFilter(pickWorkspace, referencePick);
			updateClosingWorkspace(pickWorkspace);
		}

		if (baseFilter) {
			var filterWorkspace = baseFilter.closest('[data-close-workspace]');

			refreshBaseReferenceOptions(filterWorkspace);

			if (baseFilter.matches('[data-base-reference-filter]')) {
				if (!baseFilter.value) {
					setReferenceFilterMode(filterWorkspace, false);
				} else {
					filterWorkspace.setAttribute('data-base-filter-mode', 'active');
				}
			}

			filterClosingReferences(filterWorkspace);
		}

		if (baseTypeSelect) {
			var baseCard = baseTypeSelect.closest('[data-close-base]');
			var baseWorkspace = baseCard ? baseCard.closest('[data-close-workspace]') : null;

			if (baseCard && isClosingBaseIdentityLocked(baseCard)) {
				lockClosingBaseIdentity(baseCard);
				return;
			}

			if (baseCard && baseWorkspace) {
				linesBoundToBase(baseWorkspace, baseCard).forEach(function (row) {
					syncClosingRowFromBase(row, baseCard);
				});
			}

			updateClosingBaseCard(baseCard);
			updateClosingWorkspace(baseWorkspace);
			autosaveClosingWorkspace(baseWorkspace);
		}

		var rowBaseTipoSelect = event.target.closest('[data-close-line] [data-field="base_tipo"]');

		if (rowBaseTipoSelect) {
			var linkedRow = rowBaseTipoSelect.closest('[data-close-line]');
			var linkedWorkspace = linkedRow ? linkedRow.closest('[data-close-workspace]') : null;
			var linkedReference = linkedRow ? linkedRow.closest('[data-close-reference]') : null;
			var linkedBaseTipo;
			var linkedLineType;

			if (linkedRow && linkedWorkspace && linkedReference) {
				linkedBaseTipo = textField(linkedRow, 'base_tipo');
				linkedLineType = lineTypeForBaseType(linkedBaseTipo);

				retargetClosingBlockBase(linkedWorkspace, linkedRow, linkedReference);

				if (linkedLineType === 'caja_saldos') {
					applyCajaSaldoRowMetrics(linkedRow, linkedReference, closingCajaSaldoQty(closeNumber(linkedReference, 'unidades'), linkedReference));
				}

				reconcileClosingReference(linkedWorkspace, linkedReference);
				sortClosingReferenceBlocks(linkedReference);
				reindexClosingWorkspace(linkedWorkspace);
				updateClosingWorkspace(linkedWorkspace);
				autosaveClosingWorkspace(linkedWorkspace);
			}
		}

		var rowLabelInput = event.target.closest('[data-close-line] [data-field="label"]');

		if (rowLabelInput) {
			var labelRow = rowLabelInput.closest('[data-close-line]');
			var labelWorkspace = labelRow ? labelRow.closest('[data-close-workspace]') : null;
			var labelBase = labelWorkspace && labelRow ? findClosingBaseCardForRow(labelWorkspace, labelRow) : null;

			if (labelBase) {
				labelBase.setAttribute('data-close-block-label', textField(labelRow, 'label'));
				updateClosingBaseCard(labelBase);
			}
		}

		if (event.target.closest('[data-close-workspace]') && event.target.matches('select[data-field]')) {
			var closeField = event.target.getAttribute('data-field');
			var closeWorkspace = event.target.closest('[data-close-workspace]');

			if (closeWorkspace && ['nombre_selector', 'variante_selector', 'tipo_producto_selector'].indexOf(closeField) === -1) {
				updateClosingWorkspace(closeWorkspace);
				autosaveClosingWorkspace(closeWorkspace);
			}
		}

		if (
			event.target.closest('[data-close-workspace]') &&
			(event.target.matches('[data-field]') || event.target.hasAttribute('data-saldo-input') || event.target.closest('[data-base-search]'))
		) {
			handleClosingWorkspaceFieldEvent(event);
		}
	});

	document.addEventListener('input', function (event) {
		if (
			!event.target ||
			!event.target.closest ||
			!event.target.closest('[data-close-workspace]')
		) {
			return;
		}

		if (
			event.target.matches('[data-field]') ||
			event.target.hasAttribute('data-saldo-input') ||
			event.target.closest('[data-base-search]')
		) {
			handleClosingWorkspaceFieldEvent(event);
		}
	});

	document.addEventListener('mousedown', function (event) {
		var lockedField = event.target.closest(
			'[data-close-workspace] input.logistica-readonly-field, [data-close-workspace] input[readonly], [data-close-workspace] input[data-closing-locked="1"]'
		);

		if (lockedField) {
			event.preventDefault();
			if (document.activeElement === lockedField) {
				lockedField.blur();
			}
		}
	}, true);

	document.addEventListener('focusin', function (event) {
		var field = event.target;
		var lockedField;

		if (
			field.matches(
				'[data-close-workspace] input.logistica-readonly-field, [data-close-workspace] input[readonly], [data-close-workspace] input[data-closing-locked="1"]'
			)
		) {
			lockedField = field;
			window.requestAnimationFrame(function () {
				if (lockedField && document.activeElement === lockedField) {
					lockedField.blur();
				}
			});
			return;
		}

		if (
			field.matches(
				'.logistica-sheet input, .logistica-sheet select, .logistica-close-matrix input:not([readonly]):not(.logistica-readonly-field), .logistica-close-matrix select:not([disabled]), .logistica-close-matrix .logistica-close-copy-value, [data-close-workspace] .logistica-base-grid input:not([readonly]):not(.logistica-readonly-field):not([type="hidden"]), [data-close-workspace] [data-field="unidades"]'
			)
		) {
			if (typeof field.select === 'function') {
				window.requestAnimationFrame(function () {
					field.select();
				});
			}
		}
	});

	document.addEventListener('keydown', function (event) {
		var sheetField = event.target.closest('.logistica-sheet input, .logistica-sheet select');
		var tapasSheet = sheetField ? sheetField.closest('[data-sheet="tapas-poliestireno"]') : null;
		var bolsasSheet = sheetField ? sheetField.closest('[data-sheet="bolsas-calculo"]') : null;
		var matrixField = event.target.closest(
			'.logistica-close-matrix input:not([readonly]):not(.logistica-readonly-field), .logistica-close-matrix select:not([disabled]), .logistica-close-matrix .logistica-close-copy-value'
		);
		var closingPanelField = isClosingPanelNavField(event.target);
		var closingWorkspace = event.target.closest('[data-close-workspace]');
		var minutasTable = event.target.closest('.logistica-minutas-table');
		var minutasBoard = minutasTable ? minutasTable.closest('[data-minutas-board]') : null;
		var minutasField = minutasTable ? resolveMinutasTableField(event.target) : null;
		var keys = {
			ArrowLeft: 'left',
			ArrowRight: 'right',
			ArrowUp: 'up',
			ArrowDown: 'down'
		};
		var enterDirection;

		if (closingWorkspace && (event.key === 'Backspace' || event.key === 'Delete')) {
			if (!isEditableClosingField(event.target)) {
				event.preventDefault();
				return;
			}

			event.stopPropagation();
		}

		if (event.key === 'Enter' && event.target.matches('.logistica-close-copy-value')) {
			event.preventDefault();
			copyClosingCodLoteValue(event.target);
			return;
		}

		if (event.key === 'Enter' && (sheetField || matrixField || closingPanelField)) {
			enterDirection = event.shiftKey ? 'prev' : 'next';

			if (closingPanelField && closingWorkspace) {
				event.preventDefault();
				updateClosingWorkspace(closingWorkspace);
				autosaveClosingWorkspace(closingWorkspace);
				moveClosingFocus(event.target, enterDirection);
				return;
			}

			if (matrixField && closingWorkspace) {
				event.preventDefault();
				updateClosingWorkspace(closingWorkspace);
				autosaveClosingWorkspace(closingWorkspace);
				moveTableFocus(matrixField, enterDirection);
				return;
			}

			if (sheetField && tapasSheet) {
				event.preventDefault();
				moveTapasFocus(sheetField, enterDirection);
				return;
			}

			if (sheetField && bolsasSheet) {
				event.preventDefault();
				moveBolsasFocus(sheetField, enterDirection);
				return;
			}

			if (sheetField) {
				event.preventDefault();
				moveSheetFocus(sheetField, enterDirection);
				return;
			}
		}

		if (minutasField && keys[event.key] && moveMinutasTableFocus(minutasField, keys[event.key], minutasBoard)) {
			event.preventDefault();
			event.stopImmediatePropagation();
			return;
		}

		if (sheetField && tapasSheet && keys[event.key] && moveTapasFocus(sheetField, keys[event.key])) {
			event.preventDefault();
			return;
		}

		if (sheetField && bolsasSheet && keys[event.key] && moveBolsasFocus(sheetField, keys[event.key])) {
			event.preventDefault();
			return;
		}

		if (sheetField && keys[event.key] && moveSheetFocus(sheetField, keys[event.key])) {
			event.preventDefault();
		}

		if (closingPanelField && keys[event.key] && moveClosingFocus(event.target, keys[event.key])) {
			event.preventDefault();
			return;
		}

		if (event.key === 'Enter' && event.target.matches('.logistica-warehouse-copy-value')) {
			event.preventDefault();
			copyClosingCodLoteValue(event.target);
			return;
		}

		if (
			event.target.matches &&
			event.target.matches('[data-field="inventario_fisico"]') &&
			(event.key === 'ArrowUp' || event.key === 'ArrowDown' || event.key === 'Enter')
		) {
			if (moveWarehouseFisicoFocus(event.target, event.key === 'ArrowUp' ? 'up' : 'down')) {
				event.preventDefault();
				event.stopImmediatePropagation();
				return;
			}
			if (event.key === 'ArrowUp' || event.key === 'ArrowDown') {
				event.preventDefault();
				return;
			}
		}

		if (matrixField && keys[event.key] && moveTableFocus(matrixField, keys[event.key])) {
			event.preventDefault();
		}
	});

	document.addEventListener('submit', function (event) {
		var workspace = event.target.closest('[data-close-workspace]');

		if (workspace) {
			flushClosingWorkspaceSave(workspace);
		}
	});

	document.querySelectorAll('.logistica-form').forEach(updateProductTotal);
	document.querySelectorAll('.logistica-calculator').forEach(updateCalculator);
	restoreConversionState();
	initConversionNav();
	initConversionExcelScroll();
	document.querySelectorAll('[data-conversion-board] .logistica-sheet[data-sheet]').forEach(function (sheet) {
		initExcelTableZoom(sheet);
	});
	initConversionTemplates();
	initTapasTotalsCollapsibles(document);
	document.querySelectorAll('[data-sheet="tapas-poliestireno"]').forEach(function (sheet) {
		initTapasCards(sheet);
		initTapasExcelScroll(sheet);
		initExcelTableZoom(sheet);
		refreshTapasFilterOptions(sheet);
		bindTablePagination(sheet, 'tapas', function (options) {
			applyTapasFilters(sheet, options);
		});
		applyTapasFilters(sheet);
	});
	initMoneyFields(document);
	initClosingUnitsFields(document);
	document.querySelectorAll('[data-sheet="bolsas-calculo"]').forEach(function (sheet) {
		initBolsasCards(sheet);
		initBolsasExcelScroll(sheet);
		initExcelTableZoom(sheet);
		bindTablePagination(sheet, 'bolsas', function (options) {
			applyBolsasFilters(sheet, options);
		});
		updateBolsasSheet(sheet);
	});
	document.querySelectorAll('.logistica-sheet').forEach(updateSheet);
	initClosingWorkspacesIfNeeded();
	initLabelPrintSettingsLogo();

	function initNotices() {
		document.querySelectorAll('[data-logistica-notice]').forEach(function (notice) {
			var closeButton = notice.querySelector('.logistica-notice-close');
			var hideNotice = function () {
				notice.remove();
			};

			if (closeButton) {
				closeButton.addEventListener('click', hideNotice);
			}

			window.setTimeout(hideNotice, 6000);
		});
	}

	function initFormSubmitFeedback() {
		document.querySelectorAll('.logistica-form').forEach(function (form) {
			form.addEventListener('submit', function () {
				var button = form.querySelector('.button-primary');

				form.classList.add('is-submitting');
				if (button && !button.dataset.originalLabel) {
					button.dataset.originalLabel = button.textContent;
					button.textContent = button.dataset.originalLabel.replace(/\.\.\.$/, '') + '...';
				}
			});
		});
	}

	function shouldInitClosingWorkspaceNow(workspace) {
		var app = document.querySelector('[data-logistica-app]');
		var panel;

		if (!workspace) {
			return false;
		}

		if (!app) {
			return true;
		}

		panel = workspace.closest('.logistica-tab-panel');
		if (!panel) {
			return true;
		}

		return !panel.hidden && panel.classList.contains('is-active');
	}

	function initClosingWorkspacesIfNeeded(force) {
		document.querySelectorAll('[data-close-workspace]').forEach(function (workspace) {
			if (workspace.dataset.closingInitBound === '1') {
				return;
			}

			if (!force && !shouldInitClosingWorkspaceNow(workspace)) {
				return;
			}

			workspace.dataset.closingInitBound = '1';
			bindClosingUiPersistence(workspace);
			bindClosingBaseNavigatorLayout(workspace);
			upgradeClosingCodLoteCells(workspace);
			setClosingWorkspaceLoading(workspace, true);
			window.requestAnimationFrame(function () {
				initClosingWorkspace(workspace);
			});
		});
	}

	function refreshSheetsInPanel(panel) {
		if (!panel) {
			return;
		}

		panel.querySelectorAll('.logistica-sheet').forEach(updateSheet);
		panel.querySelectorAll('[data-sheet="tapas-poliestireno"]').forEach(function (sheet) {
			updateTapasSheet(sheet);
		});
		panel.querySelectorAll('[data-sheet="bolsas-calculo"]').forEach(function (sheet) {
			updateBolsasSheet(sheet);
		});
	}

	function syncInventoryNavHeight() {
		// Reservado por compatibilidad; el menú lateral ya no fija altura en el body.
	}

	var INVENTORY_NAV_COLLAPSED_KEY = 'logistica_inventory_nav_collapsed_v2';

	function getActiveConversionBoard(shell) {
		if (window.LogisticaPanelNav && typeof window.LogisticaPanelNav.getActiveConversionBoard === 'function') {
			return window.LogisticaPanelNav.getActiveConversionBoard(shell);
		}

		var activePanel;

		if (!shell) {
			return null;
		}

		activePanel = shell.querySelector('.logistica-tab-panel.is-active:not([hidden])');
		if (!activePanel) {
			activePanel = shell.querySelector('[data-conversion-board]');
		}

		return activePanel ? activePanel.querySelector('[data-conversion-board]') : null;
	}

	function syncAppRailInventoryButton(shell, collapsed) {
		var railMenuBtn;
		var visibleTabLinks;

		if (!shell) {
			return;
		}

		if (window.LogisticaPanelNav && typeof window.LogisticaPanelNav.syncAppRailInventoryButton === 'function') {
			window.LogisticaPanelNav.syncAppRailInventoryButton(shell, collapsed);
			return;
		}

		railMenuBtn = shell.querySelector('.logistica-app-rail [data-action="toggle-inventory-nav"]');
		if (!railMenuBtn) {
			return;
		}

		visibleTabLinks = shell.querySelectorAll('[data-logistica-tabs] [data-logistica-tab]:not([hidden])');
		if (!visibleTabLinks.length) {
			railMenuBtn.hidden = true;
			railMenuBtn.setAttribute('aria-expanded', 'false');
			railMenuBtn.classList.remove('is-active');
			return;
		}

		railMenuBtn.hidden = false;
		railMenuBtn.setAttribute('aria-expanded', collapsed ? 'false' : 'true');
		railMenuBtn.classList.toggle('is-active', !collapsed);
	}

	function syncAppRailContextPins(shell, tab) {
		if (window.LogisticaPanelNav && typeof window.LogisticaPanelNav.syncAppRailContextPins === 'function') {
			window.LogisticaPanelNav.syncAppRailContextPins(shell, tab);
		}
	}

	function syncAppRailSectionButton(shell) {
		if (window.LogisticaPanelNav && typeof window.LogisticaPanelNav.syncAppRailSectionButton === 'function') {
			window.LogisticaPanelNav.syncAppRailSectionButton(shell);
			return;
		}

		var railSectionBtn;
		var board;
		var collapsed;

		if (!shell) {
			return;
		}

		railSectionBtn = shell.querySelector('.logistica-app-rail [data-action="toggle-section-nav"]');
		if (!railSectionBtn) {
			return;
		}

		board = getActiveConversionBoard(shell);
		if (
			!board ||
			!board.querySelector('[data-conversion-nav]') ||
			!board.querySelector('.logistica-conversion-nav__link:not([hidden]), [data-config-section-nav]:not([hidden]), [data-user-panel-section-nav]:not([hidden])')
		) {
			railSectionBtn.hidden = true;
			railSectionBtn.setAttribute('aria-expanded', 'false');
			railSectionBtn.classList.remove('is-active');
			if (board) {
				board.classList.add('is-nav-collapsed');
			}
			return;
		}

		railSectionBtn.hidden = false;
		collapsed = board.classList.contains('is-nav-collapsed');
		railSectionBtn.setAttribute('aria-expanded', collapsed ? 'false' : 'true');
		railSectionBtn.classList.toggle('is-active', !collapsed);
	}

	function updateSectionNavBackdrop(shell) {
		var board;
		var open;
		var backdrop;

		if (!shell) {
			return;
		}

		board = getActiveConversionBoard(shell);
		open = !!(board && !board.classList.contains('is-nav-collapsed'));
		backdrop = shell.querySelector('.logistica-section-nav-backdrop');
		document.body.classList.toggle('logistica-section-nav-open', open);

		if (backdrop) {
			backdrop.setAttribute('aria-hidden', open ? 'false' : 'true');
			backdrop.tabIndex = open ? 0 : -1;
		}
	}

	function closeAllSectionNavs(shell) {
		if (!shell) {
			return;
		}

		shell.querySelectorAll('[data-conversion-board]:not(.is-nav-collapsed)').forEach(function (board) {
			setConversionNavCollapsed(board, true);
		});
	}

	function toggleSectionNav(shell) {
		if (window.LogisticaPanelNav && typeof window.LogisticaPanelNav.toggleSectionNav === 'function') {
			window.LogisticaPanelNav.toggleSectionNav(shell);
			return;
		}

		var board;

		if (!shell) {
			return;
		}

		board = getActiveConversionBoard(shell);
		if (!board) {
			return;
		}

		if (board.classList.contains('is-nav-collapsed') && !shell.classList.contains('is-nav-collapsed')) {
			setInventoryNavCollapsed(shell, true);
		}

		toggleConversionNav(board);
	}

	function setInventoryNavCollapsed(shell, collapsed) {
		var navToggle;
		var reopenButton;
		var backdrop;

		if (!shell) {
			return;
		}

		if (!collapsed) {
			closeAllSectionNavs(shell);
		}

		shell.classList.toggle('is-nav-collapsed', collapsed);
		navToggle = shell.querySelector('.logistica-inventory-nav__toggle');
		reopenButton = shell.querySelector('.logistica-inventory-nav-reopen');
		backdrop = shell.querySelector('.logistica-inventory-nav-backdrop');

		if (navToggle) {
			navToggle.setAttribute('aria-expanded', collapsed ? 'false' : 'true');
			navToggle.title = collapsed ? 'Mostrar menú' : 'Ocultar menú';
		}

		if (reopenButton) {
			reopenButton.setAttribute('aria-expanded', collapsed ? 'false' : 'true');
			reopenButton.setAttribute('aria-hidden', collapsed ? 'false' : 'true');
			reopenButton.tabIndex = collapsed ? 0 : -1;
		}

		if (backdrop) {
			backdrop.setAttribute('aria-hidden', collapsed ? 'true' : 'false');
			backdrop.tabIndex = collapsed ? -1 : 0;
		}

		syncAppRailInventoryButton(shell, collapsed);
		syncAppRailSectionButton(shell);

		document.body.classList.toggle('logistica-inventory-nav-open', !collapsed);

		try {
			localStorage.setItem(INVENTORY_NAV_COLLAPSED_KEY, collapsed ? '1' : '0');
		} catch (error) {
			// Ignore storage errors.
		}
	}

	function toggleInventoryNav(shell) {
		if (!shell) {
			return;
		}

		setInventoryNavCollapsed(shell, !shell.classList.contains('is-nav-collapsed'));
	}

	function readInventoryNavCollapsedState(shell) {
		var collapsed = true;
		var stored;
		var isConfig = shell.getAttribute('data-logistica-panel-context') === 'config'
			|| shell.classList.contains('logistica-config');

		if (isConfig) {
			return shell.classList.contains('is-nav-collapsed');
		}

		try {
			stored = localStorage.getItem(INVENTORY_NAV_COLLAPSED_KEY);
			if (stored !== null) {
				collapsed = stored === '1';
			}
		} catch (error) {
			collapsed = true;
		}

		return collapsed;
	}

	function initInventoryNav() {
		if (window.LogisticaPanelNav && typeof window.LogisticaPanelNav.init === 'function') {
			window.LogisticaPanelNav.init();
			return;
		}

		document.querySelectorAll('[data-logistica-inventory-shell]').forEach(function (shell) {
			if (shell.dataset.inventoryNavBound === '1') {
				return;
			}

			shell.dataset.inventoryNavBound = '1';
			setInventoryNavCollapsed(shell, readInventoryNavCollapsedState(shell));
			syncAppRailSectionButton(shell);

			shell.addEventListener('click', function (event) {
				if (event.target.closest('[data-action="toggle-section-nav"]')) {
					event.preventDefault();
					toggleSectionNav(shell);
					return;
				}

				if (event.target.closest('[data-action="toggle-inventory-nav"]')) {
					event.preventDefault();
					toggleInventoryNav(shell);
					return;
				}

				var tabLink = event.target.closest('[data-logistica-tab]');

				if (!tabLink || tabLink.classList.contains('logistica-inventory-nav__frontend-link')) {
					return;
				}

				if (!shell.classList.contains('is-nav-collapsed')) {
					setInventoryNavCollapsed(shell, true);
				}
			});
		});
	}

	function initLogisticaResumeTracker() {
		var cfg = window.LogisticaAdmin || {};

		if (!cfg.ajaxUrl || !cfg.userPanelNonce) {
			return null;
		}

		var SKIP_TABS = {
			logistica: true,
			'logistica-configuracion': true,
			'logistica-ayuda': true
		};
		var MIN_MS = 3000;
		var HEARTBEAT_MS = 60000;
		var activeTab = resolveCurrentTab();
		var activeSince = Date.now();
		var lastFingerprint = '';

		function resolveCurrentTab() {
			var app = document.querySelector('[data-logistica-app]');

			if (app) {
				var activePanel = app.querySelector('.logistica-tab-panel.is-active');
				if (activePanel) {
					return activePanel.getAttribute('data-tab-panel') || 'logistica';
				}
			}

			return new URLSearchParams(window.location.search).get('tab') || 'logistica';
		}

		function tabScope(tab) {
			var app = document.querySelector('[data-logistica-app]');

			if (!app) {
				return document;
			}

			var panel = app.querySelector('[data-tab-panel="' + tab + '"]');
			return panel || document;
		}

		function fieldValue(root, fieldName) {
			var field = root.querySelector('[data-field="' + fieldName + '"]');
			if (!field) {
				return '';
			}

			return String(field.value || field.textContent || '').trim();
		}

		function tabLabel(tab) {
			var tabs = cfg.navTabs || {};
			return tabs[tab] || tab;
		}

		function captureDeepLink(tab) {
			var scope = tabScope(tab);

			if ('logistica-conversiones' === tab) {
				var calcLink = scope.querySelector('[data-calc-nav].is-active') || scope.querySelector('[data-calc-nav]');
				if (calcLink) {
					return calcLink.getAttribute('href') || '';
				}
			}

			if ('logistica-cierre-ordenes' === tab) {
				var sectionLink = scope.querySelector('.logistica-closing-nav__link.is-active, [data-closing-section-nav].is-active');
				if (sectionLink) {
					return sectionLink.getAttribute('href') || '';
				}
			}

			if ('logistica-configuracion' === tab) {
				var configLink = scope.querySelector('[data-config-section-nav].is-active');
				if (configLink) {
					return configLink.getAttribute('href') || '';
				}
			}

			return '';
		}

		function captureContext(tab) {
			var scope = tabScope(tab);
			var parts = [];

			if ('logistica-cierre-ordenes' === tab) {
				var workspace = scope.querySelector('[data-close-workspace]');
				if (workspace) {
					var refFilter = workspace.querySelector('[data-base-reference-filter]');
					if (refFilter && refFilter.value) {
						parts.push('Filtro: ' + refFilter.value);
					}

					var picked = workspace.querySelector('[data-close-reference-pick]:checked');
					var refEl = picked ? picked.closest('[data-close-reference]') : null;
					if (!refEl) {
						var refs = workspace.querySelectorAll('[data-close-reference]');
						for (var i = 0; i < refs.length; i++) {
							if (!refs[i].hidden) {
								refEl = refs[i];
								break;
							}
						}
					}

					if (refEl) {
						var referencia = fieldValue(refEl, 'referencia');
						var variante = fieldValue(refEl, 'variante');
						if (referencia) {
							parts.push('Ref. ' + referencia + (variante ? ' · ' + variante : ''));
						}
					}
				}
			} else if ('logistica-inventario-bodega' === tab) {
				var search = scope.querySelector('[data-warehouse-filter-search]');
				if (search && search.value.trim()) {
					parts.push('Búsqueda: ' + search.value.trim());
				}
			} else if ('logistica-planeacion-impresion' === tab || 'logistica-planeacion-formado' === tab) {
				var board = scope.querySelector('[data-planeacion-board]') || document.querySelector('[data-planeacion-board]');
				if (board) {
					var weekLabel = board.querySelector('[data-planeacion-week-label]');
					var machineSelect = board.querySelector('[data-planeacion-machine-select]');
					var activeMachine = machineSelect && machineSelect.selectedOptions && machineSelect.selectedOptions[0]
						? machineSelect.selectedOptions[0]
						: board.querySelector('[data-planeacion-machines] .is-active, [data-planeacion-machines] [aria-current="page"]');
					if (weekLabel && weekLabel.textContent.trim()) {
						parts.push(weekLabel.textContent.trim());
					}
					if (activeMachine && activeMachine.textContent.trim()) {
						parts.push('Máquina: ' + activeMachine.textContent.trim());
					}
				}
			} else if ('logistica-cierre-caracteristicas' === tab) {
				var carSearch = scope.querySelector('[data-cierre-car-filter-search]');
				var codigo = scope.querySelector('[data-cierre-car-field][name="codigo"]');
				if (codigo && codigo.value.trim()) {
					parts.push('Código: ' + codigo.value.trim());
				} else if (carSearch && carSearch.value.trim()) {
					parts.push('Búsqueda: ' + carSearch.value.trim());
				}
			} else if ('logistica-conversiones' === tab) {
				var calcLink = scope.querySelector('[data-calc-nav].is-active');
				if (calcLink && calcLink.textContent.trim()) {
					parts.push(calcLink.textContent.trim());
				}
			} else if ('logistica-cuadro-pedidos' === tab) {
				var pedidosSelect = scope.querySelector('[data-pedidos-board-select]');
				if (pedidosSelect && pedidosSelect.selectedOptions && pedidosSelect.selectedOptions.length) {
					parts.push('Estilo: ' + pedidosSelect.selectedOptions[0].textContent.trim());
				}
			}

			return parts.join('\n');
		}

		function buildTitle(tab, context) {
			var label = tabLabel(tab);
			if (!context) {
				return 'Trabajo en ' + label;
			}

			var firstLine = context.split('\n')[0];
			if (firstLine.length > 90) {
				firstLine = firstLine.slice(0, 87) + '…';
			}

			return label + ': ' + firstLine;
		}

		function notifyResumeUpdate(data) {
			if (!data || !Array.isArray(data.resume_points)) {
				return;
			}

			if (typeof window.logisticaMergeResumePoints === 'function') {
				window.logisticaMergeResumePoints(data.resume_points);
			}

			document.dispatchEvent(new CustomEvent('logistica:resume-updated', {
				detail: data
			}));
		}

		function sendResumePoint(tab, title, context, useBeacon, deepLink) {
			var body = new FormData();
			body.append('action', 'logistica_record_resume_point');
			body.append('nonce', cfg.userPanelNonce || '');
			body.append('tab_slug', tab);
			body.append('title', title);
			body.append('context', context);
			body.append('deep_link', deepLink || '');

			if (useBeacon && navigator.sendBeacon) {
				navigator.sendBeacon(cfg.ajaxUrl, body);
				return;
			}

			fetch(cfg.ajaxUrl, {
				method: 'POST',
				credentials: 'same-origin',
				body: body,
				keepalive: true
			})
				.then(function (response) {
					return response.json();
				})
				.then(function (result) {
					if (result && result.success && result.data) {
						notifyResumeUpdate(result.data);
					}
				})
				.catch(function () {
					/* noop */
				});
		}

		function recordTab(tab, reason, options) {
			var opts = options || {};

			if (!tab || SKIP_TABS[tab]) {
				return;
			}

			if (!opts.force && Date.now() - activeSince < MIN_MS && 'heartbeat' !== reason) {
				return;
			}

			var context = captureContext(tab);
			var title = buildTitle(tab, context);
			var deepLink = captureDeepLink(tab);
			var fingerprint = tab + '|' + title + '|' + context + '|' + deepLink;

			if ('heartbeat' === reason && fingerprint === lastFingerprint) {
				return;
			}

			lastFingerprint = fingerprint;
			sendResumePoint(tab, title, context, !!opts.useBeacon, deepLink);
		}

		function switchTab(nextTab, options) {
			var opts = options || {};

			if (!opts.skipRecord && activeTab && activeTab !== nextTab) {
				recordTab(activeTab, 'switch');
			}

			activeTab = nextTab;
			activeSince = Date.now();
			lastFingerprint = '';
		}

		document.addEventListener('click', function (event) {
			var link = event.target.closest('[data-logistica-tab]');
			if (!link || link.classList.contains('nav-tab-active')) {
				return;
			}

			var nextTab = link.getAttribute('data-logistica-tab');
			if (!nextTab || nextTab === activeTab) {
				return;
			}

			recordTab(activeTab, 'nav', { useBeacon: true });
		}, true);

		document.addEventListener('visibilitychange', function () {
			if ('hidden' === document.visibilityState) {
				recordTab(activeTab, 'hidden', { useBeacon: true, force: true });
			}
		});

		window.addEventListener('pagehide', function () {
			recordTab(activeTab, 'unload', { useBeacon: true, force: true });
		});

		window.setInterval(function () {
			recordTab(activeTab, 'heartbeat');
		}, HEARTBEAT_MS);

		return {
			switchTab: switchTab,
			recordCurrent: function (reason, options) {
				recordTab(activeTab, reason || 'manual', options || {});
			}
		};
	}

	var logisticaResumeTracker = initLogisticaResumeTracker();

	var labelPrintState = {
		catalogRow: null,
		barcode: ''
	};

	var labelPackHistoryCache = {};
	var labelPackHistorySaveRequest = null;

	function normalizeLabelPackHistoryEntry(paquetes, undPorPaquete) {
		return {
			paquetes: String(paquetes == null ? '' : paquetes).trim(),
			und_por_paquete: String(undPorPaquete == null ? '' : undPorPaquete).trim()
		};
	}

	function isValidLabelPackHistoryEntry(entry) {
		if (!entry) {
			return false;
		}
		var paquetes = parseFloat(String(entry.paquetes || '').replace(',', '.'));
		var und = parseFloat(String(entry.und_por_paquete || '').replace(',', '.'));
		return paquetes > 0 && und > 0;
	}

	function getLabelPackHistoryForCatalog(catalogId) {
		var key = String(catalogId || '').trim();
		if (!key || !Array.isArray(labelPackHistoryCache[key])) {
			return [];
		}
		return labelPackHistoryCache[key].filter(isValidLabelPackHistoryEntry);
	}

	function setLabelPackHistoryCache(catalogId, entries) {
		var key = String(catalogId || '').trim();
		if (!key) {
			return;
		}
		labelPackHistoryCache[key] = Array.isArray(entries) ? entries.filter(isValidLabelPackHistoryEntry) : [];
	}

	function fetchLabelPackHistory(catalogId) {
		var key = String(catalogId || '').trim();
		if (!key) {
			return Promise.resolve([]);
		}
		return postLabelPrint('logistica_get_label_pack_history', {
			catalog_id: key
		}).then(function (payload) {
			var entries = payload && payload.success && payload.data && Array.isArray(payload.data.entries)
				? payload.data.entries
				: [];
			setLabelPackHistoryCache(key, entries);
			return entries;
		}).catch(function () {
			return getLabelPackHistoryForCatalog(key);
		});
	}

	function saveLabelPackHistory(catalogId, paquetes, undPorPaquete) {
		var key = String(catalogId || '').trim();
		var entry = normalizeLabelPackHistoryEntry(paquetes, undPorPaquete);
		if (!key || !isValidLabelPackHistoryEntry(entry)) {
			return Promise.resolve(null);
		}

		if (labelPackHistorySaveRequest && typeof labelPackHistorySaveRequest.abort === 'function') {
			labelPackHistorySaveRequest.abort();
			labelPackHistorySaveRequest = null;
		}

		var controller = typeof AbortController !== 'undefined' ? new AbortController() : null;
		if (!window.LogisticaAdmin || !window.fetch) {
			return Promise.resolve(null);
		}

		var data = new FormData();
		data.set('action', 'logistica_save_label_pack_history');
		data.set('nonce', window.LogisticaAdmin.labelPrintNonce || '');
		data.set('catalog_id', key);
		data.set('paquetes', entry.paquetes);
		data.set('und_por_paquete', entry.und_por_paquete);

		var fetchOptions = {
			method: 'POST',
			credentials: 'same-origin',
			body: data
		};
		if (controller) {
			fetchOptions.signal = controller.signal;
			labelPackHistorySaveRequest = controller;
		}

		return window.fetch(window.LogisticaAdmin.ajaxUrl, fetchOptions).then(function (response) {
			return response.json();
		}).then(function (payload) {
			if (payload && payload.success && payload.data && Array.isArray(payload.data.entries)) {
				setLabelPackHistoryCache(key, payload.data.entries);
				return payload.data.entries;
			}
			return null;
		}).catch(function (error) {
			if (error && error.name === 'AbortError') {
				return null;
			}
			return null;
		}).finally(function () {
			if (controller && labelPackHistorySaveRequest === controller) {
				labelPackHistorySaveRequest = null;
			}
		});
	}

	function formatLabelPackHistoryLabel(entry) {
		return String(entry.paquetes) + ' paq × ' + String(entry.und_por_paquete) + ' und';
	}

	function applyLabelPackHistoryEntry(form, entry) {
		if (!form || !entry) {
			return;
		}
		setLabelFieldValue(form, 'paquetes', entry.paquetes || '');
		setLabelFieldValue(form, 'und_por_paquete', entry.und_por_paquete || '');
		syncLabelCantidadPreview(form);
		triggerLabelVisualPreview();
		syncLabelPackHistoryActiveState(form);
	}

	function syncLabelPackHistoryActiveState(form) {
		if (!form) {
			return;
		}
		var paquetes = labelFieldValue(form, 'paquetes');
		var undPorPaquete = labelFieldValue(form, 'und_por_paquete');
		var signature = paquetes + '|' + undPorPaquete;
		form.querySelectorAll('[data-label-pack-history-apply]').forEach(function (button) {
			var buttonSignature = String(button.getAttribute('data-paquetes') || '') + '|' +
				String(button.getAttribute('data-und') || '');
			button.classList.toggle('is-active', !!paquetes && !!undPorPaquete && buttonSignature === signature);
		});
	}

	function renderLabelPackHistoryListFromEntries(form, entries) {
		if (!form) {
			return;
		}
		var wrap = form.querySelector('[data-label-pack-history]');
		var list = form.querySelector('[data-label-pack-history-list]');
		if (!wrap || !list) {
			return;
		}

		entries = Array.isArray(entries) ? entries.filter(isValidLabelPackHistoryEntry) : [];
		if (!entries.length) {
			wrap.hidden = true;
			list.innerHTML = '';
			return;
		}

		wrap.hidden = false;
		list.innerHTML = entries.map(function (entry) {
			return ''
				+ '<button type="button" class="logistica-label-pack-history__item" role="listitem"'
				+ ' data-label-pack-history-apply'
				+ ' data-paquetes="' + escapeHtml(entry.paquetes) + '"'
				+ ' data-und="' + escapeHtml(entry.und_por_paquete) + '"'
				+ ' title="' + escapeHtml(formatLabelPackHistoryLabel(entry)) + '">'
				+ escapeHtml(formatLabelPackHistoryLabel(entry))
				+ '</button>';
		}).join('');
		syncLabelPackHistoryActiveState(form);
	}

	function renderLabelPackHistoryList(form, catalogId) {
		renderLabelPackHistoryListFromEntries(form, getLabelPackHistoryForCatalog(catalogId));
	}

	function applyLabelPackHistoryDefaultsFromEntries(form, entries) {
		entries = Array.isArray(entries) ? entries.filter(isValidLabelPackHistoryEntry) : [];
		if (entries.length) {
			applyLabelPackHistoryEntry(form, entries[0]);
			return;
		}
		setLabelFieldValue(form, 'paquetes', '');
		setLabelFieldValue(form, 'und_por_paquete', '');
	}

	function loadLabelPackHistoryForModal(form, catalogId) {
		if (!form || !catalogId) {
			return Promise.resolve([]);
		}
		setLabelFieldValue(form, 'paquetes', '');
		setLabelFieldValue(form, 'und_por_paquete', '');
		renderLabelPackHistoryListFromEntries(form, []);
		return fetchLabelPackHistory(catalogId).then(function (entries) {
			applyLabelPackHistoryDefaultsFromEntries(form, entries);
			renderLabelPackHistoryListFromEntries(form, entries);
			syncLabelCantidadPreview(form);
			return entries;
		});
	}

	var labelLogoHistoryCache = null;

	function getLabelLogoHistoryCache() {
		if (labelLogoHistoryCache) {
			return labelLogoHistoryCache;
		}
		var cfg = window.LogisticaAdmin && window.LogisticaAdmin.labelPrintConfig;
		labelLogoHistoryCache = cfg && cfg.logoHistory ? cfg.logoHistory : { image: [], text: [] };
		if (!Array.isArray(labelLogoHistoryCache.image)) {
			labelLogoHistoryCache.image = [];
		}
		if (!Array.isArray(labelLogoHistoryCache.text)) {
			labelLogoHistoryCache.text = [];
		}
		return labelLogoHistoryCache;
	}

	function setLabelLogoHistoryCache(history) {
		history = history || { image: [], text: [] };
		labelLogoHistoryCache = {
			image: Array.isArray(history.image) ? history.image : [],
			text: Array.isArray(history.text) ? history.text : []
		};
		if (window.LogisticaAdmin && window.LogisticaAdmin.labelPrintConfig) {
			window.LogisticaAdmin.labelPrintConfig.logoHistory = labelLogoHistoryCache;
		}
	}

	function collectLabelLogoContext(root) {
		if (!root) {
			return null;
		}
		if (root.matches('[data-label-print-settings-form]')) {
			var modeRadio = root.querySelector('[data-label-settings-logo-mode]:checked');
			var urlInput = root.querySelector('[data-label-settings-logo-url]');
			var line1Input = root.querySelector('[data-label-settings-logo-line1]');
			var line2Input = root.querySelector('[data-label-settings-logo-line2]');
			return {
				logo_mode: modeRadio ? modeRadio.value : 'image',
				logo_url: urlInput ? String(urlInput.value || '').trim() : '',
				logo_text_line1: line1Input ? String(line1Input.value || '').trim() : '',
				logo_text_line2: line2Input ? String(line2Input.value || '').trim() : ''
			};
		}
		return {
			logo_mode: labelFieldValue(root, 'logo_mode') || 'image',
			logo_url: labelFieldValue(root, 'logo_url'),
			logo_text_line1: labelFieldValue(root, 'logo_text_line1'),
			logo_text_line2: labelFieldValue(root, 'logo_text_line2')
		};
	}

	function isValidLabelLogoHistoryPayload(payload) {
		if (!payload) {
			return false;
		}
		if ('image' === payload.logo_mode) {
			return !!payload.logo_url;
		}
		return !!(payload.logo_text_line1 || payload.logo_text_line2);
	}

	function saveLabelLogoHistory(root) {
		var payload = collectLabelLogoContext(root);
		if (!payload || !isValidLabelLogoHistoryPayload(payload) || !window.LogisticaAdmin || !window.fetch) {
			return Promise.resolve(null);
		}

		var data = new FormData();
		data.set('action', 'logistica_save_label_logo_history');
		data.set('nonce', window.LogisticaAdmin.labelPrintNonce || '');
		data.set('logo_mode', payload.logo_mode);
		data.set('logo_url', payload.logo_url);
		data.set('logo_text_line1', payload.logo_text_line1);
		data.set('logo_text_line2', payload.logo_text_line2);

		return window.fetch(window.LogisticaAdmin.ajaxUrl, {
			method: 'POST',
			credentials: 'same-origin',
			body: data
		}).then(function (response) {
			return response.json();
		}).then(function (result) {
			if (result && result.success && result.data && result.data.history) {
				setLabelLogoHistoryCache(result.data.history);
				renderLabelLogoHistory(root);
				return result.data.history;
			}
			return null;
		}).catch(function () {
			return null;
		});
	}

	function saveLabelHistoriesOnPrint(form) {
		if (!form) {
			return Promise.resolve();
		}

		var tasks = [saveLabelLogoHistory(form)];

		var ctx = collectLabelPrintContext();
		if (ctx && ctx.catalog_id) {
			var paquetes = labelFieldValue(form, 'paquetes');
			var undPorPaquete = labelFieldValue(form, 'und_por_paquete');
			if (isValidLabelPackHistoryEntry(normalizeLabelPackHistoryEntry(paquetes, undPorPaquete))) {
				tasks.push(
					saveLabelPackHistory(ctx.catalog_id, paquetes, undPorPaquete).then(function (entries) {
						if (entries) {
							renderLabelPackHistoryListFromEntries(form, entries);
						}
					})
				);
			}
		}

		return Promise.all(tasks);
	}

	function syncLabelLogoHistoryActiveState(root) {
		if (!root) {
			return;
		}
		var payload = collectLabelLogoContext(root);
		if (!payload) {
			return;
		}
		root.querySelectorAll('[data-label-logo-history-apply="image"]').forEach(function (button) {
			button.classList.toggle('is-active', payload.logo_mode === 'image' && button.getAttribute('data-logo-url') === payload.logo_url);
		});
		root.querySelectorAll('[data-label-logo-history-apply="text"]').forEach(function (button) {
			var signature = String(button.getAttribute('data-line1') || '') + '|' + String(button.getAttribute('data-line2') || '');
			var current = String(payload.logo_text_line1 || '') + '|' + String(payload.logo_text_line2 || '');
			button.classList.toggle('is-active', payload.logo_mode === 'text' && signature === current);
		});
	}

	function renderLabelLogoHistoryList(root, type, entries) {
		var wrap = root.querySelector('[data-label-logo-history="' + type + '"]');
		var list = root.querySelector('[data-label-logo-history-list="' + type + '"]');
		if (!wrap || !list) {
			return;
		}
		entries = Array.isArray(entries) ? entries : [];
		if (!entries.length) {
			wrap.hidden = true;
			list.innerHTML = '';
			return;
		}
		wrap.hidden = false;
		if ('image' === type) {
			list.innerHTML = entries.map(function (entry) {
				var url = String(entry.url || '');
				var label = String(entry.label || url);
				return ''
					+ '<button type="button" class="logistica-label-logo-history__item" role="listitem"'
					+ ' data-label-logo-history-apply="image"'
					+ ' data-logo-url="' + escapeHtml(url) + '"'
					+ ' title="' + escapeHtml(url) + '">'
					+ (url ? '<img src="' + escapeHtml(url) + '" alt="">' : '')
					+ '<span>' + escapeHtml(label) + '</span>'
					+ '</button>';
			}).join('');
		} else {
			list.innerHTML = entries.map(function (entry) {
				var line1 = String(entry.line1 || '');
				var line2 = String(entry.line2 || '');
				var label = String(entry.label || (line1 + (line2 ? ' · ' + line2 : '')));
				return ''
					+ '<button type="button" class="logistica-label-logo-history__item" role="listitem"'
					+ ' data-label-logo-history-apply="text"'
					+ ' data-line1="' + escapeHtml(line1) + '"'
					+ ' data-line2="' + escapeHtml(line2) + '"'
					+ ' title="' + escapeHtml(label) + '">'
					+ '<span>' + escapeHtml(label) + '</span>'
					+ '</button>';
			}).join('');
		}
		syncLabelLogoHistoryActiveState(root);
	}

	function renderLabelLogoHistory(root) {
		var history = getLabelLogoHistoryCache();
		if (!root) {
			return;
		}
		renderLabelLogoHistoryList(root, 'image', history.image);
		renderLabelLogoHistoryList(root, 'text', history.text);
	}

	function applyLabelLogoHistoryEntry(root, type, entry) {
		if (!root || !entry) {
			return;
		}
		if ('image' === type) {
			if (root.matches('[data-label-print-settings-form]')) {
				var urlInput = root.querySelector('[data-label-settings-logo-url]');
				var modeRadio = root.querySelector('[data-label-settings-logo-mode][value="image"]');
				if (modeRadio) {
					modeRadio.checked = true;
					modeRadio.dispatchEvent(new Event('change', { bubbles: true }));
				}
				if (urlInput) {
					urlInput.value = entry.url || '';
					urlInput.dispatchEvent(new Event('input', { bubbles: true }));
				}
			} else {
				setLabelFieldValue(root, 'logo_mode', 'image');
				setLabelFieldValue(root, 'logo_url', entry.url || '');
				syncLabelLogoPanels(root);
			}
		} else {
			if (root.matches('[data-label-print-settings-form]')) {
				var textMode = root.querySelector('[data-label-settings-logo-mode][value="text"]');
				var line1Input = root.querySelector('[data-label-settings-logo-line1]');
				var line2Input = root.querySelector('[data-label-settings-logo-line2]');
				if (textMode) {
					textMode.checked = true;
					textMode.dispatchEvent(new Event('change', { bubbles: true }));
				}
				if (line1Input) {
					line1Input.value = entry.line1 || '';
				}
				if (line2Input) {
					line2Input.value = entry.line2 || '';
				}
			} else {
				setLabelFieldValue(root, 'logo_mode', 'text');
				setLabelFieldValue(root, 'logo_text_line1', entry.line1 || '');
				setLabelFieldValue(root, 'logo_text_line2', entry.line2 || '');
				syncLabelLogoPanels(root);
			}
		}
		syncLabelLogoHistoryActiveState(root);
		triggerLabelVisualPreview();
	}

	function bindLabelLogoHistory(root) {
		if (!root || root.dataset.labelLogoHistoryBound === '1') {
			return;
		}
		root.dataset.labelLogoHistoryBound = '1';
		renderLabelLogoHistory(root);
		root.querySelectorAll('[data-label-logo-history-list]').forEach(function (list) {
			list.addEventListener('click', function (event) {
				var button = event.target.closest('[data-label-logo-history-apply]');
				if (!button || !root.contains(button)) {
					return;
				}
				event.preventDefault();
				var type = button.getAttribute('data-label-logo-history-apply') || 'image';
				if ('image' === type) {
					applyLabelLogoHistoryEntry(root, 'image', { url: button.getAttribute('data-logo-url') || '' });
				} else {
					applyLabelLogoHistoryEntry(root, 'text', {
						line1: button.getAttribute('data-line1') || '',
						line2: button.getAttribute('data-line2') || ''
					});
				}
			});
		});
	}

	function labelFieldValue(form, name) {
		if (!form) {
			return '';
		}
		var checkedRadio = form.querySelector('[data-label-field="' + name + '"]:checked');
		if (checkedRadio) {
			return String(checkedRadio.value || '').trim();
		}
		var field = form.querySelector('[data-label-field="' + name + '"]');
		if (!field) {
			return '';
		}
		if (field.type === 'checkbox') {
			return field.checked ? '1' : '0';
		}
		return String(field.value || '').trim();
	}

	function setLabelFieldValue(form, name, value) {
		if (!form) {
			return;
		}
		var fields = form.querySelectorAll('[data-label-field="' + name + '"]');
		if (!fields.length) {
			return;
		}
		if (fields[0].type === 'checkbox') {
			fields[0].checked = !!value && value !== '0';
			fields[0].dispatchEvent(new Event('change', { bubbles: true }));
			return;
		}
		if (fields[0].type === 'radio') {
			fields.forEach(function (field) {
				field.checked = String(field.value) === String(value);
			});
			fields[0].dispatchEvent(new Event('change', { bubbles: true }));
			return;
		}
		fields[0].value = value == null ? '' : String(value);
	}

	function buildEtiquetaProductoLabel(tipoLabel, tamano) {
		tipoLabel = String(tipoLabel || '').trim();
		tamano = String(tamano || '').trim();

		if (tipoLabel && tamano) {
			return tipoLabel + ' ' + tamano;
		}

		return tipoLabel || tamano;
	}

	function collectCatalogRowContext(row) {
		if (!row) {
			return null;
		}

		var board = row.closest('[data-etiqueta-catalog-board]');
		var tipoKey = row.getAttribute('data-tipo') || '';
		var tipoLabel = row.getAttribute('data-tipo-label') || '';
		var tamano = row.getAttribute('data-tamano') || '';

		if (!tipoLabel && board) {
			tipoLabel = getEtiquetaTipoOptions(board)[tipoKey] || tipoKey;
		}

		return {
			catalog_id: row.getAttribute('data-catalog-id') || '',
			referencia: row.getAttribute('data-referencia') || '',
			tipo: tipoKey,
			tipo_label: tipoLabel,
			mercado: row.getAttribute('data-mercado') || '',
			tamano: tamano,
			producto: buildEtiquetaProductoLabel(tipoLabel, tamano),
			unidad_empaque: row.getAttribute('data-unidad-empaque') || '',
			barcode: row.getAttribute('data-barcode') || ''
		};
	}

	function applyLabelLogoDefaults(form) {
		var cfg = window.LogisticaAdmin && window.LogisticaAdmin.labelPrintConfig;
		if (!form || !cfg) {
			return;
		}
		setLabelFieldValue(form, 'logo_mode', cfg.logoMode || 'image');
		setLabelFieldValue(form, 'logo_url', cfg.logoUrl || cfg.defaultLogoUrl || '');
		setLabelFieldValue(form, 'logo_text_line1', cfg.logoTextLine1 || '');
		setLabelFieldValue(form, 'logo_text_line2', cfg.logoTextLine2 || '');
		syncLabelLogoPanels(form);
		renderLabelLogoHistory(form);
	}

	function syncLabelLogoPanels(form) {
		if (!form) {
			return;
		}
		var mode = labelFieldValue(form, 'logo_mode') || 'image';
		form.querySelectorAll('[data-label-logo-panel]').forEach(function (panel) {
			panel.hidden = panel.getAttribute('data-label-logo-panel') !== mode;
		});
		updateLabelLogoPreview(form);
		syncLabelLogoHistoryActiveState(form);
	}

	function updateLabelLogoPreview(form) {
		if (!form) {
			return;
		}
		var preview = form.querySelector('[data-label-logo-preview]');
		var urlInput = form.querySelector('[data-label-field="logo_url"]');
		if (!preview || !urlInput) {
			return;
		}
		var url = String(urlInput.value || '').trim();
		preview.innerHTML = url ? ('<img src="' + url.replace(/"/g, '&quot;') + '" alt="">') : '';
	}

	function openLabelLogoMediaPicker(urlInput, previewSelector) {
		if (!urlInput) {
			return;
		}
		if (!window.wp || !window.wp.media) {
			window.alert('La biblioteca de medios no está disponible. Pegue la URL de la imagen manualmente.');
			return;
		}
		var frame = window.wp.media({
			title: 'Seleccionar logo',
			button: { text: 'Usar imagen' },
			library: { type: 'image' },
			multiple: false
		});
		frame.on('select', function () {
			var attachment = frame.state().get('selection').first().toJSON();
			if (!attachment || !attachment.url) {
				return;
			}
			urlInput.value = attachment.url;
			urlInput.dispatchEvent(new Event('input', { bubbles: true }));
			if (previewSelector) {
				var preview = urlInput.closest('form, [data-label-print-form]') || document;
				var previewEl = preview.querySelector(previewSelector);
				if (previewEl) {
					previewEl.innerHTML = '<img src="' + attachment.url.replace(/"/g, '&quot;') + '" alt="">';
				}
			}
			updateLabelLogoPreview(urlInput.closest('[data-label-print-form]') || urlInput.form);
			triggerLabelVisualPreview();
		});
		frame.open();
	}

	function initLabelLogoField(form) {
		if (!form || form.dataset.labelLogoBound === '1') {
			return;
		}
		form.dataset.labelLogoBound = '1';
		bindLabelLogoHistory(form);

		form.querySelectorAll('[data-label-field="logo_mode"]').forEach(function (radio) {
			radio.addEventListener('change', function () {
				syncLabelLogoPanels(form);
				syncLabelLogoHistoryActiveState(form);
				triggerLabelVisualPreview();
			});
		});

		var urlInput = form.querySelector('[data-label-field="logo_url"]');
		if (urlInput) {
			urlInput.addEventListener('input', function () {
				updateLabelLogoPreview(form);
				syncLabelLogoHistoryActiveState(form);
				triggerLabelVisualPreview();
			});
		}

		['logo_text_line1', 'logo_text_line2'].forEach(function (fieldName) {
			var field = form.querySelector('[data-label-field="' + fieldName + '"]');
			if (field) {
				field.addEventListener('input', function () {
					syncLabelLogoHistoryActiveState(form);
					triggerLabelVisualPreview();
				});
			}
		});

		var mediaBtn = form.querySelector('[data-label-logo-media]');
		if (mediaBtn && urlInput) {
			mediaBtn.addEventListener('click', function () {
				openLabelLogoMediaPicker(urlInput, '[data-label-logo-preview]');
			});
		}
	}

	function initLabelPrintSettingsLogo() {
		var settingsForm = document.querySelector('[data-label-print-settings-form]');
		if (!settingsForm || settingsForm.dataset.labelSettingsLogoBound === '1') {
			return;
		}
		settingsForm.dataset.labelSettingsLogoBound = '1';
		bindLabelLogoHistory(settingsForm);

		settingsForm.querySelectorAll('[data-label-settings-logo-mode]').forEach(function (radio) {
			radio.addEventListener('change', function () {
				var mode = settingsForm.querySelector('[data-label-settings-logo-mode]:checked');
				var selected = mode ? mode.value : 'text';
				settingsForm.querySelectorAll('[data-label-settings-logo-panel]').forEach(function (panel) {
					panel.hidden = panel.getAttribute('data-label-settings-logo-panel') !== selected;
				});
				syncLabelLogoHistoryActiveState(settingsForm);
			});
		});

		var urlInput = settingsForm.querySelector('[data-label-settings-logo-url]');
		var preview = settingsForm.querySelector('[data-label-settings-logo-preview]');
		if (urlInput && preview) {
			urlInput.addEventListener('input', function () {
				var url = String(urlInput.value || '').trim();
				preview.innerHTML = url ? ('<img src="' + url.replace(/"/g, '&quot;') + '" alt="">') : '';
				syncLabelLogoHistoryActiveState(settingsForm);
			});
		}

		[settingsForm.querySelector('[data-label-settings-logo-line1]'), settingsForm.querySelector('[data-label-settings-logo-line2]')].forEach(function (field) {
			if (!field) {
				return;
			}
			field.addEventListener('input', function () {
				syncLabelLogoHistoryActiveState(settingsForm);
			});
		});

		var mediaBtn = settingsForm.querySelector('[data-label-settings-logo-media]');
		if (mediaBtn && urlInput) {
			mediaBtn.addEventListener('click', function () {
				openLabelLogoMediaPicker(urlInput, '[data-label-settings-logo-preview]');
			});
		}
	}

	function formatLabelShortDate(date) {
		var d = date || new Date();
		var day = String(d.getDate());
		var month = String(d.getMonth() + 1);
		var year = String(d.getFullYear()).slice(-2);
		if (day.length < 2) {
			day = '0' + day;
		}
		if (month.length < 2) {
			month = '0' + month;
		}
		return day + '.' + month + '.' + year;
	}

	function parseLabelShortDate(text) {
		var parts = String(text || '').trim().split('.');
		if (parts.length !== 3) {
			return null;
		}
		var day = parseInt(parts[0], 10);
		var month = parseInt(parts[1], 10);
		var year = parseInt(parts[2], 10);
		if (isNaN(day) || isNaN(month) || isNaN(year) || day < 1 || month < 1 || month > 12) {
			return null;
		}
		if (year < 100) {
			year += 2000;
		}
		var date = new Date(year, month - 1, day);
		if (date.getFullYear() !== year || date.getMonth() !== (month - 1) || date.getDate() !== day) {
			return null;
		}
		return date;
	}

	function labelDateToIso(date) {
		var year = date.getFullYear();
		var month = date.getMonth() + 1;
		var day = date.getDate();
		return year + '-' + (month < 10 ? '0' : '') + month + '-' + (day < 10 ? '0' : '') + day;
	}

	function formatLabelDateInput(raw) {
		var digits = String(raw || '').replace(/\D/g, '').slice(0, 6);
		if (digits.length <= 2) {
			return digits;
		}
		if (digits.length <= 4) {
			return digits.slice(0, 2) + '.' + digits.slice(2);
		}
		return digits.slice(0, 2) + '.' + digits.slice(2, 4) + '.' + digits.slice(4);
	}

	function syncLabelNativeFecha(textInput, nativeInput) {
		if (!textInput || !nativeInput) {
			return;
		}
		var parsed = parseLabelShortDate(textInput.value);
		nativeInput.value = parsed ? labelDateToIso(parsed) : '';
	}

	function initLabelFechaField(form) {
		if (!form || form.dataset.labelFechaBound === '1') {
			return;
		}
		var textInput = form.querySelector('[data-label-field="fecha"]');
		var nativeInput = form.querySelector('[data-label-fecha-native]');
		var pickerBtn = form.querySelector('[data-label-fecha-picker]');
		if (!textInput || !nativeInput) {
			return;
		}
		form.dataset.labelFechaBound = '1';

		textInput.addEventListener('input', function () {
			var cursor = textInput.selectionStart;
			var beforeLen = textInput.value.length;
			textInput.value = formatLabelDateInput(textInput.value);
			var afterLen = textInput.value.length;
			if (cursor != null) {
				var nextCursor = Math.max(0, cursor + (afterLen - beforeLen));
				textInput.setSelectionRange(nextCursor, nextCursor);
			}
			syncLabelNativeFecha(textInput, nativeInput);
			if (labelPrintState.barcode) {
				triggerLabelVisualPreview();
			}
		});

		textInput.addEventListener('blur', function () {
			var parsed = parseLabelShortDate(textInput.value);
			if (parsed) {
				textInput.value = formatLabelShortDate(parsed);
				syncLabelNativeFecha(textInput, nativeInput);
				triggerLabelVisualPreview();
			}
		});

		nativeInput.addEventListener('change', function () {
			if (!nativeInput.value) {
				return;
			}
			var chunks = nativeInput.value.split('-');
			if (chunks.length !== 3) {
				return;
			}
			var picked = new Date(
				parseInt(chunks[0], 10),
				parseInt(chunks[1], 10) - 1,
				parseInt(chunks[2], 10)
			);
			textInput.value = formatLabelShortDate(picked);
			triggerLabelVisualPreview();
		});

		if (pickerBtn) {
			pickerBtn.addEventListener('click', function () {
				syncLabelNativeFecha(textInput, nativeInput);
				if (!nativeInput.value) {
					nativeInput.value = labelDateToIso(new Date());
				}
				if (typeof nativeInput.showPicker === 'function') {
					nativeInput.showPicker();
					return;
				}
				nativeInput.click();
			});
		}
	}

	function labelPrintModalEl() {
		return document.querySelector('[data-label-print-modal]');
	}

	function setLabelPrintStatus(message, isError) {
		var status = document.querySelector('[data-label-print-status]');
		if (!status) {
			return;
		}
		status.textContent = message || '';
		status.classList.toggle('is-error', !!isError);
	}

	function renderLabelBarcodePreview(data) {
		var wrap = document.querySelector('[data-label-barcode-preview]');
		var segEl = document.querySelector('[data-label-barcode-segments]');
		if (!wrap || !data) {
			return;
		}
		wrap.hidden = false;
		labelPrintState.barcode = data.barcode || '';
		if (segEl && data.preview) {
			segEl.textContent = '770 · ' + String(data.preview.mercado_digit) + ' · ' + String(data.preview.tipo_code) + ' · ' + String(data.preview.codigo_producto);
		}
	}

	function syncLabelSaldoFieldsState(form) {
		if (!form) {
			return;
		}
		var includeSaldo = form.querySelector('[data-label-field="include_saldo"]');
		var saldoSinCantidad = form.querySelector('[data-label-field="saldo_sin_cantidad"]');
		var saldoPaquetes = form.querySelector('[data-label-field="paquetes_saldo"]');
		var saldoCantidadWrap = form.querySelector('[data-label-saldo-cantidad-wrap]');
		var includeChecked = !!(includeSaldo && includeSaldo.checked);
		var sinCantidadChecked = !!(saldoSinCantidad && saldoSinCantidad.checked);

		if (saldoSinCantidad) {
			saldoSinCantidad.disabled = !includeChecked;
			if (!includeChecked) {
				saldoSinCantidad.checked = false;
				sinCantidadChecked = false;
			}
		}

		if (saldoPaquetes) {
			saldoPaquetes.disabled = !includeChecked || sinCantidadChecked;
			if (!includeChecked || sinCantidadChecked) {
				saldoPaquetes.value = '';
			}
		}

		if (saldoCantidadWrap) {
			saldoCantidadWrap.hidden = !includeChecked || sinCantidadChecked;
		}
	}

	function syncLabelCantidadPreview(form) {
		if (!form || !window.LogisticaLabelPrint) {
			return;
		}
		var paquetes = labelFieldValue(form, 'paquetes');
		var undPorPaquete = labelFieldValue(form, 'und_por_paquete');
		var cantidadField = form.querySelector('[data-label-field="cantidad_calculada"]');
		var saldoPaquetes = labelFieldValue(form, 'paquetes_saldo');
		var saldoCantidadField = form.querySelector('[data-label-field="cantidad_saldo_calculada"]');
		if (cantidadField) {
			cantidadField.value = window.LogisticaLabelPrint.formatMillar(
				window.LogisticaLabelPrint.computeCantidad(paquetes, undPorPaquete)
			);
		}
		if (saldoCantidadField) {
			var saldoSinCantidad = form.querySelector('[data-label-field="saldo_sin_cantidad"]');
			if (saldoSinCantidad && saldoSinCantidad.checked) {
				saldoCantidadField.value = '';
			} else {
				saldoCantidadField.value = window.LogisticaLabelPrint.formatMillar(
					window.LogisticaLabelPrint.computeCantidad(saldoPaquetes, undPorPaquete)
				);
			}
		}

		var totalWrap = form.querySelector('[data-label-field="total_produccion"]');
		var totalDetail = form.querySelector('[data-label-total-produccion-detail]');
		var totalData = window.LogisticaLabelPrint.computeTotalProduccion(form);
		if (totalWrap) {
			totalWrap.textContent = totalData.total || '—';
		}
		if (totalDetail) {
			totalDetail.textContent = totalData.detail || '';
		}
	}

	function triggerLabelVisualPreview() {
		if (labelPrintState.barcode) {
			previewLabelVisualFromModal();
		}
	}

	function bindLabelPrintAutoPreview(form) {
		var skipFields = {
			catalog_id: true,
			ref: true,
			cantidad_calculada: true,
			cantidad_saldo_calculada: true,
			total_produccion: true,
			paquetes: true,
			und_por_paquete: true,
			paquetes_saldo: true,
			num_cajas: true,
			include_saldo: true,
			saldo_sin_cantidad: true,
			logo_mode: true,
			logo_url: true,
			logo_text_line1: true,
			logo_text_line2: true,
			fecha: true
		};

		if (!form) {
			return;
		}

		form.querySelectorAll('[data-label-field]').forEach(function (field) {
			var fieldName = field.getAttribute('data-label-field');
			if (!fieldName || skipFields[fieldName] || field.readOnly || field.type === 'hidden') {
				return;
			}
			if (field.dataset.labelAutoPreviewBound === '1') {
				return;
			}
			field.dataset.labelAutoPreviewBound = '1';
			var eventName = field.type === 'checkbox' || field.type === 'radio' ? 'change' : 'input';
			field.addEventListener(eventName, triggerLabelVisualPreview);
			if (field.type === 'number' || fieldName === 'fecha') {
				field.addEventListener('change', triggerLabelVisualPreview);
			}
		});
	}

	function previewLabelVisualFromModal() {
		var form = document.querySelector('[data-label-print-form]');
		var mount = document.querySelector('[data-label-visual-preview-mount]');
		if (!form || !mount || !window.LogisticaLabelPrint) {
			return false;
		}

		if (!labelPrintState.barcode) {
			setLabelPrintStatus('Calcula primero el código EAN-13.', true);
			return false;
		}

		var job = window.LogisticaLabelPrint.buildPreviewJobFromForm(form);
		var validation = window.LogisticaLabelPrint.validateJobs(
			window.LogisticaLabelPrint.buildJobsFromForm(form, collectLabelPrintContext()),
			form
		);
		window.LogisticaLabelPrint.renderPreview(mount, job, labelPrintState.barcode).then(function () {
			setLabelPrintStatus(validation || 'Vista previa actualizada.', !!validation);
		}).catch(function () {
			setLabelPrintStatus('No se pudo renderizar la vista previa.', true);
		});

		return true;
	}

	function scrollLabelPreviewIntoView() {
		var modal = labelPrintModalEl();
		var preview = modal ? modal.querySelector('[data-label-visual-preview]') : null;
		var scroller = modal ? modal.querySelector('.logistica-label-print-modal__scroll') : null;
		if (!preview || !scroller) {
			return;
		}

		var top = scroller.scrollTop + (preview.getBoundingClientRect().top - scroller.getBoundingClientRect().top) - 8;
		scroller.scrollTo({ top: Math.max(top, 0), behavior: 'smooth' });
	}

	function revealLabelVisualPreview() {
		if (previewLabelVisualFromModal()) {
			scrollLabelPreviewIntoView();
		}
	}

	function postLabelPrint(action, payload) {
		if (!window.LogisticaAdmin || !window.fetch) {
			return Promise.reject(new Error('Panel no disponible.'));
		}
		var data = new FormData();
		data.set('action', action);
		data.set('nonce', window.LogisticaAdmin.labelPrintNonce || '');
		Object.keys(payload || {}).forEach(function (key) {
			data.set(key, payload[key] == null ? '' : String(payload[key]));
		});
		return window.fetch(window.LogisticaAdmin.ajaxUrl, {
			method: 'POST',
			credentials: 'same-origin',
			body: data
		}).then(function (response) {
			return response.json();
		});
	}

	function collectLabelPrintContext() {
		return collectCatalogRowContext(labelPrintState.catalogRow);
	}

	function openLabelPrintModal(catalogRow) {
		var modal = labelPrintModalEl();
		var form = modal ? modal.querySelector('[data-label-print-form]') : null;
		var ctx = collectCatalogRowContext(catalogRow);
		if (!modal || !form || !ctx || !ctx.catalog_id) {
			return;
		}
		labelPrintState.catalogRow = catalogRow;
		labelPrintState.barcode = '';
		ensureLabelPrintModalPortal();
		modal.hidden = false;
		modal.setAttribute('aria-hidden', 'false');
		syncLogisticaModalBodyLock();
		setLabelPrintStatus('', false);

		var previewMount = modal.querySelector('[data-label-visual-preview-mount]');
		if (previewMount) {
			previewMount.innerHTML = '';
		}
		var barcodeWrap = modal.querySelector('[data-label-barcode-preview]');
		if (barcodeWrap) {
			barcodeWrap.hidden = true;
		}

		var summary = modal.querySelector('[data-label-print-ref-summary]');
		if (summary) {
			summary.textContent = (ctx.referencia || '—') + ' · ' + (ctx.tipo_label || ctx.tipo || '—') + ' · ' + (ctx.mercado || '—') + (ctx.tamano ? ' · ' + ctx.tamano : '');
		}

		setLabelFieldValue(form, 'catalog_id', ctx.catalog_id);
		setLabelFieldValue(form, 'ref', ctx.referencia);
		setLabelFieldValue(form, 'producto', ctx.producto);
		loadLabelPackHistoryForModal(form, ctx.catalog_id);
		setLabelFieldValue(form, 'num_cajas', '1');
		setLabelFieldValue(form, 'caja_inicio', getLabelPrintConfig().allowBlankCajaNum ? '' : '1');
		syncLabelCajaInicioState(form);
		setLabelFieldValue(form, 'fecha', formatLabelShortDate(new Date()));
		syncLabelNativeFecha(
			form.querySelector('[data-label-field="fecha"]'),
			form.querySelector('[data-label-fecha-native]')
		);
		setLabelFieldValue(form, 'cliente', '');
		setLabelFieldValue(form, 'op', '');
		setLabelFieldValue(form, 'operario', '');
		setLabelFieldValue(form, 'include_saldo', '0');
		setLabelFieldValue(form, 'saldo_sin_cantidad', '0');
		setLabelFieldValue(form, 'paquetes_saldo', '');
		syncLabelSaldoFieldsState(form);
		applyLabelLogoDefaults(form);
		syncLabelCantidadPreview(form);

		form.querySelector('[data-label-field="cliente"]').focus();
		previewLabelBarcodeFromModal();
	}

	function closeLabelPrintModal() {
		var modal = labelPrintModalEl();
		if (!modal) {
			return;
		}
		modal.hidden = true;
		modal.setAttribute('aria-hidden', 'true');
		syncLogisticaModalBodyLock();
		labelPrintState.catalogRow = null;
		labelPrintState.barcode = '';
	}

	function ensureLabelPrintModalPortal() {
		var modal = labelPrintModalEl();
		if (!modal || modal.parentElement === document.body) {
			return;
		}
		document.body.appendChild(modal);
	}

	function previewLabelBarcodeFromModal() {
		var ctx = collectLabelPrintContext();
		if (!ctx || !ctx.catalog_id) {
			setLabelPrintStatus('Registro de etiqueta no válido.', true);
			return;
		}
		setLabelPrintStatus('Calculando código…', false);
		postLabelPrint('logistica_preview_label_barcode', {
			catalog_id: ctx.catalog_id
		}).then(function (payload) {
			if (!payload || !payload.success) {
				setLabelPrintStatus((payload && payload.data && payload.data.message) || 'No se pudo calcular el código.', true);
				return;
			}
			renderLabelBarcodePreview(payload.data);
			previewLabelVisualFromModal();
		}).catch(function () {
			setLabelPrintStatus('Error de conexión al calcular el código.', true);
		});
	}

	function submitLabelPrintModal(event) {
		if (event) {
			event.preventDefault();
			event.stopPropagation();
		}
		var form = event ? event.target : document.querySelector('[data-label-print-form]');
		if (!form || !window.LogisticaLabelPrint) {
			return false;
		}

		var jobs = window.LogisticaLabelPrint.buildJobsFromForm(form, collectLabelPrintContext());
		var validation = window.LogisticaLabelPrint.validateJobs(jobs, form);
		if (validation) {
			setLabelPrintStatus(validation, true);
			return false;
		}

		function doPrint(barcode) {
			if (!barcode) {
				setLabelPrintStatus('No hay código EAN-13 para imprimir.', true);
				return;
			}
			setLabelPrintStatus('Abriendo impresión. Usa márgenes «Ninguno» y desactiva encabezados/pies de página.', false);
			window.LogisticaLabelPrint.printLabels(jobs, barcode).then(function (result) {
				saveLabelHistoriesOnPrint(form);
				setLabelPrintStatus(
					(result && result.count > 1) ?
						result.count + ' etiquetas enviadas a imprimir.' :
						'Etiqueta enviada a imprimir.',
					false
				);
			}).catch(function (error) {
				setLabelPrintStatus((error && error.message) || 'No se pudo abrir la impresión.', true);
			});
		}

		if (labelPrintState.barcode) {
			doPrint(labelPrintState.barcode);
			return false;
		}

		var ctx = collectLabelPrintContext();
		if (!ctx || !ctx.catalog_id) {
			setLabelPrintStatus('Registro de etiqueta no válido.', true);
			return false;
		}

		setLabelPrintStatus('Calculando código…', false);
		postLabelPrint('logistica_preview_label_barcode', {
			catalog_id: ctx.catalog_id
		}).then(function (payload) {
			if (!payload || !payload.success) {
				setLabelPrintStatus((payload && payload.data && payload.data.message) || 'No se pudo calcular el código.', true);
				return;
			}
			renderLabelBarcodePreview(payload.data);
			doPrint(payload.data.barcode);
		}).catch(function () {
			setLabelPrintStatus('Error de conexión al calcular el código.', true);
		});

		return false;
	}

	function initLabelPrintModal() {
		var modal = labelPrintModalEl();
		if (!modal || modal.dataset.labelPrintBound === '1') {
			return;
		}
		ensureLabelPrintModalPortal();
		modal.dataset.labelPrintBound = '1';
		modal.querySelectorAll('[data-label-print-close]').forEach(function (button) {
			button.addEventListener('click', closeLabelPrintModal);
		});
		var form = modal.querySelector('[data-label-print-form]');
		if (form) {
			initLabelFechaField(form);
			initLabelLogoField(form);
			bindLabelPrintAutoPreview(form);
			form.addEventListener('submit', submitLabelPrintModal);
			var saldoToggle = form.querySelector('[data-label-field="include_saldo"]');
			var saldoSinCantidadToggle = form.querySelector('[data-label-field="saldo_sin_cantidad"]');
			if (saldoToggle) {
				saldoToggle.addEventListener('change', function () {
					syncLabelSaldoFieldsState(form);
					syncLabelCantidadPreview(form);
					triggerLabelVisualPreview();
				});
			}
			if (saldoSinCantidadToggle) {
				saldoSinCantidadToggle.addEventListener('change', function () {
					syncLabelSaldoFieldsState(form);
					syncLabelCantidadPreview(form);
					triggerLabelVisualPreview();
				});
			}
			syncLabelSaldoFieldsState(form);
			syncLabelCajaInicioState(form);
			['paquetes', 'und_por_paquete', 'paquetes_saldo', 'num_cajas', 'caja_inicio'].forEach(function (fieldName) {
				var field = form.querySelector('[data-label-field="' + fieldName + '"]');
				if (field) {
					field.addEventListener('input', function () {
						syncLabelCantidadPreview(form);
						syncLabelPackHistoryActiveState(form);
						triggerLabelVisualPreview();
					});
				}
			});
			var historyList = form.querySelector('[data-label-pack-history-list]');
			if (historyList && form.dataset.labelPackHistoryBound !== '1') {
				form.dataset.labelPackHistoryBound = '1';
				historyList.addEventListener('click', function (event) {
					var button = event.target.closest('[data-label-pack-history-apply]');
					if (!button) {
						return;
					}
					event.preventDefault();
					applyLabelPackHistoryEntry(form, {
						paquetes: button.getAttribute('data-paquetes') || '',
						und_por_paquete: button.getAttribute('data-und') || ''
					});
					syncLabelPackHistoryActiveState(form);
				});
			}
		}
		var previewLabelBtn = modal.querySelector('[data-label-print-preview-label]');
		if (previewLabelBtn) {
			previewLabelBtn.addEventListener('click', revealLabelVisualPreview);
		}
		document.addEventListener('keydown', function (event) {
			if (event.key === 'Escape' && modal && !modal.hidden) {
				closeLabelPrintModal();
			}
		});
	}

	function syncLogisticaModalBodyLock() {
		var anyOpen = document.querySelector('.logistica-label-print-modal:not([hidden])');
		document.body.classList.toggle('logistica-label-print-modal-open', !!anyOpen);
	}

	var rotuloRollosState = {
		row: null
	};

	function rotuloFieldValue(form, name) {
		if (!form) {
			return '';
		}
		var field = form.querySelector('[data-rotulo-field="' + name + '"]');
		return field ? String(field.value || '').trim() : '';
	}

	function setRotuloFieldValue(form, name, value) {
		if (!form) {
			return;
		}
		var field = form.querySelector('[data-rotulo-field="' + name + '"]');
		if (!field) {
			return;
		}
		field.value = value == null ? '' : String(value);
	}

	function collectRotuloRowContext(row) {
		if (!row) {
			return null;
		}
		return {
			rotulo_id: row.getAttribute('data-rotulo-id') || '',
			tipo: row.getAttribute('data-tipo') || '',
			tipo_label: row.getAttribute('data-tipo-label') || '',
			tamano: row.getAttribute('data-tamano') || '',
			ancho_label: row.getAttribute('data-ancho-label') || '',
			accent_color: row.getAttribute('data-accent-color') || ''
		};
	}

	function rotuloPrintModalEl() {
		return document.querySelector('[data-rotulo-print-modal]');
	}

	function rotuloEditModalEl() {
		return document.querySelector('[data-rotulo-edit-modal]');
	}

	function setRotuloPrintStatus(message, isError) {
		var status = document.querySelector('[data-rotulo-print-status]');
		if (!status) {
			return;
		}
		status.textContent = message || '';
		status.classList.toggle('is-error', !!isError);
	}

	function setRotuloRollosStatus(board, message, type) {
		var node = board ? board.querySelector('[data-rotulo-rollos-status]') : document.querySelector('[data-rotulo-rollos-status]');
		if (!node) {
			return;
		}
		node.textContent = message || '';
		node.classList.toggle('is-error', type === 'error');
		node.classList.toggle('is-success', type === 'success');
	}

	function rotuloRollosAjax(action, params) {
		if (!window.LogisticaAdmin || !window.LogisticaAdmin.ajaxUrl) {
			return Promise.resolve(null);
		}

		var data = new URLSearchParams(params || {});
		data.set('action', action);
		data.set('nonce', window.LogisticaAdmin.rotulosRollosNonce || '');

		return window.fetch(window.LogisticaAdmin.ajaxUrl, {
			method: 'POST',
			credentials: 'same-origin',
			headers: {
				'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
			},
			body: data.toString()
		}).then(function (response) {
			return response.json();
		});
	}

	var rotuloPrintHistoryCache = {};

	function isValidRotuloPrintHistoryEntry(entry) {
		return !!(entry && String(entry.lote_inicio || '').trim());
	}

	function getRotuloPrintHistoryForRotulo(rotuloId) {
		var key = String(rotuloId || '').trim();
		return key && Array.isArray(rotuloPrintHistoryCache[key]) ? rotuloPrintHistoryCache[key] : [];
	}

	function setRotuloPrintHistoryCache(rotuloId, entries) {
		var key = String(rotuloId || '').trim();
		if (!key) {
			return;
		}
		rotuloPrintHistoryCache[key] = Array.isArray(entries)
			? entries.filter(isValidRotuloPrintHistoryEntry)
			: [];
	}

	function formatRotuloPrintHistoryWhen(mysqlGmt) {
		var text = String(mysqlGmt || '').trim();
		var date;
		if (!text) {
			return '';
		}
		date = new Date(text.replace(' ', 'T') + (text.indexOf('Z') >= 0 || text.indexOf('+') >= 0 ? '' : 'Z'));
		if (isNaN(date.getTime())) {
			return text;
		}
		try {
			return date.toLocaleString('es-CO', { dateStyle: 'short', timeStyle: 'short' });
		} catch (error) {
			return date.toLocaleString();
		}
	}

	function formatRotuloPrintHistoryRange(entry) {
		var start = String(entry && entry.lote_inicio ? entry.lote_inicio : '').trim();
		var end = String(entry && entry.lote_fin ? entry.lote_fin : '').trim();
		var count = Math.max(1, parseInt(entry && entry.num_consecutivo, 10) || 1);
		if (!start) {
			return '';
		}
		if (count <= 1 || !end || end === start) {
			return 'Lote ' + start;
		}
		return 'Lotes ' + start + ' → ' + end + ' (' + count + ')';
	}

	function formatRotuloPrintHistoryLabel(entry) {
		var parts = [formatRotuloPrintHistoryRange(entry)];
		var gramaje = String(entry && entry.gramaje ? entry.gramaje : '').trim();
		var when = formatRotuloPrintHistoryWhen(entry && entry.printed_at);
		if (gramaje) {
			parts.push('Gr ' + gramaje);
		}
		if (when) {
			parts.push(when);
		}
		return parts.filter(Boolean).join(' · ');
	}

	function collectRotuloPrintPesos(form) {
		var pesos = [];
		var indexed;
		var single;
		if (!form) {
			return pesos;
		}
		indexed = form.querySelectorAll('[data-rotulo-peso-index]');
		if (indexed.length) {
			indexed.forEach(function (input) {
				pesos.push(String(input.value || '').trim());
			});
			return pesos;
		}
		single = rotuloFieldValue(form, 'peso');
		if (single) {
			pesos.push(single);
		}
		return pesos;
	}

	function applyRotuloPrintPesos(form, pesos) {
		var list;
		var i;
		if (!form) {
			return;
		}
		pesos = Array.isArray(pesos) ? pesos : [];
		syncRotuloPesoFields(form);
		list = form.querySelectorAll('[data-rotulo-peso-index]');
		if (list.length) {
			for (i = 0; i < list.length; i++) {
				list[i].value = pesos[i] != null ? String(pesos[i]) : '';
			}
			return;
		}
		setRotuloFieldValue(form, 'peso', pesos[0] != null ? pesos[0] : '');
	}

	function applyRotuloPrintHistoryEntry(form, entry) {
		if (!form || !isValidRotuloPrintHistoryEntry(entry)) {
			return;
		}

		setRotuloFieldValue(form, 'lote', String(entry.lote_inicio || '').trim());
		setRotuloFieldValue(form, 'gramaje', entry.gramaje || '');
		setRotuloFieldValue(form, 'num_consecutivo', String(Math.max(1, parseInt(entry.num_consecutivo, 10) || 1)));
		setRotuloFieldValue(form, 'num_copias', String(Math.max(1, parseInt(entry.num_copias, 10) || 1)));
		delete form.dataset.rotuloPesoFieldsSignature;
		applyRotuloPrintPesos(form, entry.pesos || []);
		syncRotuloPrintHistoryActiveState(form);
		previewRotuloFromModal();
		setRotuloPrintStatus('Datos del historial cargados.', false);
	}

	function syncRotuloPrintHistoryActiveState(form) {
		var start;
		var count;
		var signature;
		if (!form) {
			return;
		}
		start = rotuloFieldValue(form, 'lote');
		count = String(Math.max(1, parseInt(rotuloFieldValue(form, 'num_consecutivo'), 10) || 1));
		signature = start + '|' + count + '|' + rotuloFieldValue(form, 'gramaje');
		form.querySelectorAll('[data-rotulo-print-history-apply]').forEach(function (button) {
			var buttonSignature = String(button.getAttribute('data-lote-inicio') || '') + '|' +
				String(button.getAttribute('data-num-consecutivo') || '1') + '|' +
				String(button.getAttribute('data-gramaje') || '');
			button.classList.toggle('is-active', !!start && buttonSignature === signature);
		});
	}

	function renderRotuloPrintHistoryListFromEntries(form, entries) {
		var wrap;
		var list;
		if (!form) {
			return;
		}
		wrap = form.querySelector('[data-rotulo-print-history]');
		list = form.querySelector('[data-rotulo-print-history-list]');
		if (!wrap || !list) {
			return;
		}

		entries = Array.isArray(entries) ? entries.filter(isValidRotuloPrintHistoryEntry).slice(0, 2) : [];
		if (!entries.length) {
			wrap.hidden = true;
			list.innerHTML = '';
			return;
		}

		wrap.hidden = false;
		list.innerHTML = entries.map(function (entry, index) {
			var badge = index === 0 ? 'Más reciente' : 'Anterior';
			var when = formatRotuloPrintHistoryWhen(entry.printed_at) || '—';
			var metaParts = [];
			if (entry.gramaje) {
				metaParts.push('Gramaje ' + entry.gramaje);
			}
			if (Math.max(1, parseInt(entry.num_copias, 10) || 1) > 1) {
				metaParts.push(entry.num_copias + ' copias');
			}
			if (!metaParts.length) {
				metaParts.push('Sin gramaje');
			}

			return ''
				+ '<button type="button" class="logistica-rotulo-print-history__item" role="listitem"'
				+ ' data-rotulo-print-history-apply'
				+ ' data-entry-id="' + escapeHtml(String(entry.id || '')) + '"'
				+ ' data-lote-inicio="' + escapeHtml(entry.lote_inicio || '') + '"'
				+ ' data-lote-fin="' + escapeHtml(entry.lote_fin || '') + '"'
				+ ' data-num-consecutivo="' + escapeHtml(String(Math.max(1, parseInt(entry.num_consecutivo, 10) || 1))) + '"'
				+ ' data-num-copias="' + escapeHtml(String(Math.max(1, parseInt(entry.num_copias, 10) || 1))) + '"'
				+ ' data-gramaje="' + escapeHtml(entry.gramaje || '') + '"'
				+ ' data-pesos="' + escapeHtml(JSON.stringify(Array.isArray(entry.pesos) ? entry.pesos : [])) + '"'
				+ ' title="' + escapeHtml('Cargar: ' + formatRotuloPrintHistoryLabel(entry)) + '">'
				+ '<span class="logistica-rotulo-print-history__item-badge">' + escapeHtml(badge) + '</span>'
				+ '<span class="logistica-rotulo-print-history__item-when">' + escapeHtml(when) + '</span>'
				+ '<span class="logistica-rotulo-print-history__item-range">' + escapeHtml(formatRotuloPrintHistoryRange(entry)) + '</span>'
				+ '<span class="logistica-rotulo-print-history__item-meta">' + escapeHtml(metaParts.join(' · ')) + '</span>'
				+ '</button>';
		}).join('');
		syncRotuloPrintHistoryActiveState(form);
	}

	function fetchRotuloPrintHistory(rotuloId) {
		var key = String(rotuloId || '').trim();
		if (!key) {
			return Promise.resolve([]);
		}
		return rotuloRollosAjax('logistica_get_rotulo_rollos_print_history', {
			rotulo_id: key
		}).then(function (payload) {
			var entries = payload && payload.success && payload.data && Array.isArray(payload.data.entries)
				? payload.data.entries
				: [];
			setRotuloPrintHistoryCache(key, entries);
			return entries;
		}).catch(function () {
			return getRotuloPrintHistoryForRotulo(key);
		});
	}

	function saveRotuloPrintHistory(form) {
		var rotuloId;
		var loteInicio;
		var numConsecutivo;
		var loteFin;
		var pesos;
		if (!form) {
			return Promise.resolve(null);
		}
		rotuloId = rotuloFieldValue(form, 'rotulo_id');
		loteInicio = rotuloFieldValue(form, 'lote');
		numConsecutivo = Math.max(1, parseInt(rotuloFieldValue(form, 'num_consecutivo'), 10) || 1);
		if (!rotuloId || !loteInicio) {
			return Promise.resolve(null);
		}
		loteFin = loteInicio;
		if (window.LogisticaRotuloRollosPrint && typeof window.LogisticaRotuloRollosPrint.incrementLote === 'function') {
			loteFin = window.LogisticaRotuloRollosPrint.incrementLote(loteInicio, Math.max(0, numConsecutivo - 1));
		}
		pesos = collectRotuloPrintPesos(form);

		return rotuloRollosAjax('logistica_save_rotulo_rollos_print_history', {
			rotulo_id: rotuloId,
			lote_inicio: loteInicio,
			lote_fin: loteFin,
			num_consecutivo: String(numConsecutivo),
			num_copias: String(Math.max(1, parseInt(rotuloFieldValue(form, 'num_copias'), 10) || 1)),
			gramaje: rotuloFieldValue(form, 'gramaje'),
			pesos: JSON.stringify(pesos)
		}).then(function (payload) {
			if (payload && payload.success && payload.data && Array.isArray(payload.data.entries)) {
				setRotuloPrintHistoryCache(rotuloId, payload.data.entries);
				renderRotuloPrintHistoryListFromEntries(form, payload.data.entries);
				return payload.data.entries;
			}
			return null;
		}).catch(function () {
			return null;
		});
	}

	function loadRotuloPrintHistoryForModal(form, rotuloId) {
		if (!form || !rotuloId) {
			return Promise.resolve([]);
		}
		renderRotuloPrintHistoryListFromEntries(form, []);
		return fetchRotuloPrintHistory(rotuloId).then(function (entries) {
			renderRotuloPrintHistoryListFromEntries(form, entries);
			return entries;
		});
	}

	function rotuloPrintHistoryEntryFromButton(button) {
		var pesos = [];
		if (!button) {
			return null;
		}
		try {
			pesos = JSON.parse(button.getAttribute('data-pesos') || '[]');
		} catch (error) {
			pesos = [];
		}
		if (!Array.isArray(pesos)) {
			pesos = [];
		}
		return {
			id: button.getAttribute('data-entry-id') || '',
			lote_inicio: button.getAttribute('data-lote-inicio') || '',
			lote_fin: button.getAttribute('data-lote-fin') || '',
			num_consecutivo: parseInt(button.getAttribute('data-num-consecutivo'), 10) || 1,
			num_copias: parseInt(button.getAttribute('data-num-copias'), 10) || 1,
			gramaje: button.getAttribute('data-gramaje') || '',
			pesos: pesos
		};
	}

	function rotuloRollosRowById(board, rotuloId) {
		var rows;
		var i;

		if (!board) {
			return null;
		}

		rows = board.querySelectorAll('[data-rotulo-row]');
		for (i = 0; i < rows.length; i++) {
			if (String(rows[i].getAttribute('data-rotulo-id') || '') === String(rotuloId)) {
				return rows[i];
			}
		}

		return null;
	}

	function compareRotuloRollosRows(a, b) {
		var tipoA = String(a.tipo || '').toUpperCase();
		var tipoB = String(b.tipo || '').toUpperCase();
		if (tipoA !== tipoB) {
			return tipoA.localeCompare(tipoB);
		}
		var tamA = String(a.tamano || '').toUpperCase();
		var tamB = String(b.tamano || '').toUpperCase();
		if (tamA !== tamB) {
			return tamA.localeCompare(tamB, undefined, { numeric: true, sensitivity: 'base' });
		}
		return (parseInt(a.id, 10) || 0) - (parseInt(b.id, 10) || 0);
	}

	function findRotuloRollosRowInsertBefore(tbody, row) {
		var rows;
		var i;
		var existing;

		if (!tbody || !row) {
			return null;
		}

		rows = tbody.querySelectorAll('[data-rotulo-row]');
		for (i = 0; i < rows.length; i++) {
			existing = {
				id: rows[i].getAttribute('data-rotulo-id'),
				tipo: rows[i].getAttribute('data-tipo') || '',
				tamano: rows[i].getAttribute('data-tamano') || ''
			};
			if (compareRotuloRollosRows(row, existing) < 0) {
				return rows[i];
			}
		}

		return null;
	}

	function buildRotuloRowSearchText(row) {
		return [
			row.tipo_label,
			row.tipo,
			row.tamano,
			row.ancho_label,
			row.observaciones
		].map(function (value) {
			return String(value || '').toLowerCase().trim();
		}).filter(Boolean).join(' ');
	}

	function buildRotuloRollosRowHtml(board, row) {
		var postUrl = board ? (board.getAttribute('data-rotulo-post-url') || '') : '';
		var redirectBase = board ? (board.getAttribute('data-rotulo-redirect-base') || '') : '';
		var deleteNonce = board ? (board.getAttribute('data-rotulo-delete-nonce') || '') : '';
		var obs = String(row.observaciones || '').trim();
		var obsText = obs || '—';
		var accent = row.accent_color || '';

		return (
			'<tr' +
				' data-rotulo-row' +
				' data-rotulo-id="' + escapeHtml(String(row.id || '')) + '"' +
				' data-tipo="' + escapeHtml(row.tipo || '') + '"' +
				' data-tipo-label="' + escapeHtml(row.tipo_label || row.tipo || '') + '"' +
				' data-tamano="' + escapeHtml(row.tamano || '') + '"' +
				' data-ancho-label="' + escapeHtml(row.ancho_label || '') + '"' +
				' data-accent-color="' + escapeHtml(accent) + '"' +
				' data-rotulo-search="' + escapeHtml(buildRotuloRowSearchText(row)) + '"' +
			'>' +
				'<td><span class="logistica-rotulo-rollos-color-chip" style="background-color:' + escapeHtml(accent) + ';" title="' + escapeHtml(accent) + '"></span></td>' +
				'<td>' + escapeHtml(row.tipo_label || row.tipo || '') + '</td>' +
				'<td>' + escapeHtml(row.tamano || '') + '</td>' +
				'<td><strong>' + escapeHtml(row.ancho_label || '') + '</strong></td>' +
				'<td class="logistica-rotulo-rollos-observaciones-cell">' + escapeHtml(obsText) + '</td>' +
				'<td class="logistica-rotulo-rollos-actions-cell">' +
					'<div class="logistica-rotulo-rollos-actions">' +
						'<button type="button" class="button button-small logistica-table-icon-btn logistica-rotulo-rollos-btn-print" data-action="print-rotulo-rollos" aria-label="Imprimir rótulo" title="Imprimir rótulo">' +
							'<span class="dashicons dashicons-printer" aria-hidden="true"></span>' +
						'</button>' +
						'<button type="button" class="button button-small logistica-table-icon-btn logistica-rotulo-rollos-btn-edit" data-action="edit-rotulo-rollos" data-rotulo-id="' + escapeHtml(String(row.id || '')) + '" aria-label="Editar" title="Editar">' +
							'<span class="dashicons dashicons-edit" aria-hidden="true"></span>' +
						'</button>' +
						'<form method="post" action="' + escapeHtml(postUrl) + '" class="logistica-inline-delete-form" onsubmit="return confirm(\'¿Eliminar este registro?\');">' +
							'<input type="hidden" name="action" value="logistica_delete_rotulo_rollos">' +
							'<input type="hidden" name="_wpnonce" value="' + escapeHtml(deleteNonce) + '">' +
							'<input type="hidden" name="redirect_to" value="' + escapeHtml(redirectBase) + '">' +
							'<input type="hidden" name="id" value="' + escapeHtml(String(row.id || '')) + '">' +
							'<button type="submit" class="button button-small logistica-table-icon-btn logistica-rotulo-rollos-btn-delete" aria-label="Eliminar" title="Eliminar">' +
								'<span class="dashicons dashicons-trash" aria-hidden="true"></span>' +
							'</button>' +
						'</form>' +
					'</div>' +
				'</td>' +
			'</tr>'
		);
	}

	function applyRotuloRollosRowState(rowEl, row) {
		var cells;
		var chip;
		var obs;
		var obsText;
		var editBtn;

		if (!rowEl || !row) {
			return;
		}

		obs = String(row.observaciones || '').trim();
		obsText = obs || '—';

		rowEl.setAttribute('data-rotulo-id', String(row.id || ''));
		rowEl.setAttribute('data-tipo', row.tipo || '');
		rowEl.setAttribute('data-tipo-label', row.tipo_label || row.tipo || '');
		rowEl.setAttribute('data-tamano', row.tamano || '');
		rowEl.setAttribute('data-ancho-label', row.ancho_label || '');
		rowEl.setAttribute('data-accent-color', row.accent_color || '');
		rowEl.setAttribute('data-rotulo-search', buildRotuloRowSearchText(row));

		chip = rowEl.querySelector('.logistica-rotulo-rollos-color-chip');
		if (chip) {
			chip.style.backgroundColor = row.accent_color || '';
			chip.setAttribute('title', row.accent_color || '');
		}

		cells = rowEl.querySelectorAll('td');
		if (cells.length >= 5) {
			cells[1].textContent = row.tipo_label || row.tipo || '';
			cells[2].textContent = row.tamano || '';
			cells[3].innerHTML = '<strong>' + escapeHtml(row.ancho_label || '') + '</strong>';
			cells[4].textContent = obsText;
			cells[4].className = 'logistica-rotulo-rollos-observaciones-cell';
		}

		editBtn = rowEl.querySelector('[data-action="edit-rotulo-rollos"]');
		if (editBtn) {
			editBtn.setAttribute('data-rotulo-id', String(row.id || ''));
		}
	}

	function refreshRotuloRollosTotal(board, total) {
		var node = board ? board.querySelector('[data-rotulo-rollos-total]') : null;
		if (node && total !== undefined && total !== null) {
			node.textContent = String(total);
		}
	}

	function syncRotuloRollosEmptyState(board) {
		var tbody = board ? board.querySelector('[data-rotulo-rollos-rows]') : null;
		var emptyHint = board ? board.querySelector('[data-rotulo-rollos-empty-hint]') : null;
		var tableCard = board ? board.querySelector('[data-rotulo-rollos-table-card]') : null;
		var count = tbody ? tbody.querySelectorAll('[data-rotulo-row]').length : 0;

		if (emptyHint) {
			emptyHint.hidden = count > 0;
		}
		if (tableCard) {
			tableCard.hidden = count === 0;
		}
		refreshRotuloRollosTotal(board, count);
	}

	function insertRotuloRollosRow(board, row, existingEl) {
		var tbody;
		var emptyHint;
		var tableCard;
		var temp;
		var rowEl;
		var before;

		if (!board || !row) {
			return null;
		}

		tbody = board.querySelector('[data-rotulo-rollos-rows]');
		if (!tbody) {
			return null;
		}

		emptyHint = board.querySelector('[data-rotulo-rollos-empty-hint]');
		tableCard = board.querySelector('[data-rotulo-rollos-table-card]');
		if (emptyHint) {
			emptyHint.hidden = true;
		}
		if (tableCard) {
			tableCard.hidden = false;
		}

		rowEl = existingEl || null;
		if (!rowEl) {
			temp = document.createElement('tbody');
			temp.innerHTML = buildRotuloRollosRowHtml(board, row);
			rowEl = temp.firstElementChild;
		} else {
			applyRotuloRollosRowState(rowEl, row);
			if (rowEl.parentNode) {
				rowEl.parentNode.removeChild(rowEl);
			}
		}

		if (!rowEl) {
			return null;
		}

		before = findRotuloRollosRowInsertBefore(tbody, row);
		if (before) {
			tbody.insertBefore(rowEl, before);
		} else {
			tbody.appendChild(rowEl);
		}

		return rowEl;
	}

	function resetRotuloNewForm(form, board) {
		var idField;
		var tipoField;

		if (!form) {
			return;
		}

		form.reset();
		idField = form.querySelector('[name="id"]');
		if (idField) {
			idField.value = '0';
		}

		tipoField = form.querySelector('[data-rotulo-tipo-select]');
		if (tipoField) {
			tipoField.value = '';
		}

		updateRotuloNewFormColorPreview(board);

		if (tipoField) {
			window.requestAnimationFrame(function () {
				tipoField.focus();
			});
		}
	}

	function submitRotuloRollosSave(board, form, options) {
		options = options || {};
		var submitBtn = options.submitBtn || form.querySelector('[type="submit"]');
		var params = new URLSearchParams(new window.FormData(form));

		params.delete('action');
		params.delete('_wpnonce');
		params.delete('redirect_to');

		if (options.setStatus) {
			options.setStatus('Guardando…', false);
		} else {
			setRotuloRollosStatus(board, 'Guardando…', false);
		}

		if (submitBtn) {
			submitBtn.disabled = true;
		}

		return rotuloRollosAjax('logistica_save_rotulo_rollos_ajax', params).then(function (payload) {
			if (submitBtn) {
				submitBtn.disabled = false;
			}

			if (!payload || !payload.success || !payload.data || !payload.data.row) {
				var errorMessage = (payload && payload.data && payload.data.message) || 'No se pudo guardar.';
				if (options.setStatus) {
					options.setStatus(errorMessage, true);
				} else {
					setRotuloRollosStatus(board, errorMessage, 'error');
				}
				return null;
			}

			var row = payload.data.row;
			var existingRow = rotuloRollosRowById(board, row.id);

			if (existingRow) {
				insertRotuloRollosRow(board, row, existingRow);
			} else {
				insertRotuloRollosRow(board, row);
			}

			refreshRotuloRollosTotal(board, payload.data.total);
			syncRotuloRollosEmptyState(board);
			setRotuloRollosStatus(board, payload.data.message || 'Registro guardado.', 'success');

			if (typeof board.__logisticaRefreshRotuloRollosFilters === 'function') {
				board.__logisticaRefreshRotuloRollosFilters();
			}

			if (options.onSuccess) {
				options.onSuccess(row, payload.data);
			}

			return payload.data;
		});
	}

	function initRotuloNewForm(board) {
		var form = board ? board.querySelector('[data-rotulo-new-form]') : null;

		if (!form || form.dataset.rotuloNewAjaxBound === '1') {
			return;
		}

		form.dataset.rotuloNewAjaxBound = '1';
		form.addEventListener('submit', function (event) {
			event.preventDefault();

			var tipoField = form.querySelector('[data-rotulo-tipo-select]');
			var tamanoField = form.querySelector('[data-rotulo-tamano-field]');

			if (!tipoField || !String(tipoField.value || '').trim()) {
				setRotuloRollosStatus(board, 'Selecciona un tipo.', 'error');
				return;
			}
			if (!tamanoField || !String(tamanoField.value || '').trim()) {
				setRotuloRollosStatus(board, 'Indica el tamaño.', 'error');
				return;
			}

			submitRotuloRollosSave(board, form, {
				onSuccess: function () {
					resetRotuloNewForm(form, board);
				}
			});
		});
	}

	function ensureRotuloPrintModalPortal() {
		var modal = rotuloPrintModalEl();
		if (!modal || modal.parentElement === document.body) {
			return;
		}
		document.body.appendChild(modal);
	}

	function ensureRotuloEditModalPortal() {
		var modal = rotuloEditModalEl();
		if (!modal || modal.parentElement === document.body) {
			return;
		}
		document.body.appendChild(modal);
	}

	function syncRotuloPesoFields(form) {
		var numConsecutivo = parseInt(rotuloFieldValue(form, 'num_consecutivo'), 10) || 1;
		var startLote = rotuloFieldValue(form, 'lote');
		var singleWrap = form ? form.querySelector('[data-rotulo-peso-single-wrap]') : null;
		var listWrap = form ? form.querySelector('[data-rotulo-peso-por-lote-wrap]') : null;
		var list = form ? form.querySelector('[data-rotulo-peso-por-lote-list]') : null;
		var signature = String(numConsecutivo) + '|' + startLote;
		var existing = {};
		var html = '';
		var i;
		var loteVal;
		var pesoVal;

		if (!form || !window.LogisticaRotuloRollosPrint) {
			return;
		}

		if (numConsecutivo <= 1) {
			if (singleWrap) {
				singleWrap.hidden = false;
			}
			if (listWrap) {
				listWrap.hidden = true;
			}
			form.dataset.rotuloPesoFieldsSignature = signature;
			return;
		}

		if (singleWrap) {
			singleWrap.hidden = true;
		}
		if (listWrap) {
			listWrap.hidden = false;
		}
		if (!list) {
			return;
		}

		if (form.dataset.rotuloPesoFieldsSignature === signature) {
			return;
		}

		list.querySelectorAll('[data-rotulo-peso-index]').forEach(function (input) {
			existing[input.getAttribute('data-rotulo-peso-index')] = input.value;
		});

		for (i = 0; i < numConsecutivo; i++) {
			loteVal = window.LogisticaRotuloRollosPrint.incrementLote(startLote, i);
			if (!loteVal) {
				loteVal = String(i + 1);
			}
			pesoVal = existing[String(i)] !== undefined ? existing[String(i)] : '';
			html += (
				'<label class="logistica-rotulo-peso-por-lote__item">' +
					'<span class="logistica-rotulo-peso-por-lote__label">' + escapeHtml('Lote ' + loteVal) + '</span>' +
					'<input type="text" inputmode="numeric" data-rotulo-peso-index="' + i + '" value="' + escapeHtml(pesoVal) + '" placeholder="Opcional" autocomplete="off">' +
				'</label>'
			);
		}

		list.innerHTML = html;
		form.dataset.rotuloPesoFieldsSignature = signature;
		list.querySelectorAll('[data-rotulo-peso-index]').forEach(function (field) {
			field.addEventListener('input', previewRotuloFromModal);
		});
	}

	function previewRotuloFromModal() {
		var form = document.querySelector('[data-rotulo-print-form]');
		var mount = document.querySelector('[data-rotulo-visual-preview-mount]');
		var hint = document.querySelector('[data-rotulo-consecutivo-hint]');
		var rangeHint = '';

		if (!form || !mount || !window.LogisticaRotuloRollosPrint) {
			return false;
		}

		syncRotuloPesoFields(form);

		if (typeof window.LogisticaRotuloRollosPrint.describeConsecutivoRange === 'function') {
			rangeHint = window.LogisticaRotuloRollosPrint.describeConsecutivoRange(form);
		}

		if (hint) {
			if (rangeHint) {
				hint.textContent = rangeHint;
				hint.hidden = false;
			} else {
				hint.textContent = '';
				hint.hidden = true;
			}
		}

		var job = window.LogisticaRotuloRollosPrint.buildPreviewJobFromForm(form, collectRotuloRowContext(rotuloRollosState.row));
		window.LogisticaRotuloRollosPrint.renderPreview(mount, job).then(function () {
			setRotuloPrintStatus('Vista previa actualizada.', false);
		});

		return true;
	}

	function scrollRotuloPreviewIntoView() {
		var modal = rotuloPrintModalEl();
		var preview = modal ? modal.querySelector('[data-rotulo-visual-preview]') : null;
		var scroller = modal ? modal.querySelector('.logistica-label-print-modal__scroll') : null;
		if (!preview || !scroller) {
			return;
		}

		var top = scroller.scrollTop + (preview.getBoundingClientRect().top - scroller.getBoundingClientRect().top) - 8;
		scroller.scrollTo({ top: Math.max(top, 0), behavior: 'smooth' });
	}

	function revealRotuloVisualPreview() {
		if (previewRotuloFromModal()) {
			scrollRotuloPreviewIntoView();
		}
	}

	function getRotuloRollosPrintConfig() {
		var cfg = window.LogisticaAdmin && window.LogisticaAdmin.rotuloRollosPrintConfig;
		return cfg || { widthMm: 216, heightMm: 140, cssUrl: '', colorPalette: [] };
	}

	function updateRotuloPrintSizeHint() {
		var cfg = getRotuloRollosPrintConfig();
		var hintText = 'Diseño ' + cfg.widthMm + ' × ' + cfg.heightMm + ' mm · Impresión: Carta vertical, 2 media cartas por hoja. Márgenes «Ninguno», sin encabezados/pies.';
		var badgeText = cfg.widthMm + ' × ' + cfg.heightMm + ' mm';

		document.querySelectorAll('[data-rotulo-print-size-hint]').forEach(function (hint) {
			hint.textContent = hintText;
		});
		document.querySelectorAll('[data-rotulo-print-size-badge]').forEach(function (badge) {
			badge.textContent = badgeText;
		});
	}

	function setRotuloPrintSettingsStatus(root, message, type) {
		var status = root ? root.querySelector('[data-rotulo-print-settings-status]') : null;
		if (!status) {
			return;
		}
		status.textContent = message || '';
		status.classList.remove('is-error', 'is-success');
		if (type === 'error') {
			status.classList.add('is-error');
		} else if (type === 'success') {
			status.classList.add('is-success');
		}
	}

	function saveRotuloPrintSettings(form, root) {
		var data;

		if (!window.LogisticaAdmin || !window.fetch) {
			return Promise.reject(new Error('Panel no disponible.'));
		}

		data = new FormData(form);
		data.set('action', 'logistica_save_rotulo_rollos_print_settings');
		data.set('nonce', window.LogisticaAdmin.rotulosRollosNonce || '');

		return window.fetch(window.LogisticaAdmin.ajaxUrl, {
			method: 'POST',
			credentials: 'same-origin',
			body: data
		}).then(function (response) {
			return response.json();
		}).then(function (result) {
			if (!result || !result.success) {
				throw new Error((result && result.data && result.data.message) || 'No se pudo guardar el tamaño.');
			}

			if (result.data && result.data.config && window.LogisticaAdmin) {
				if (!window.LogisticaAdmin.rotuloRollosPrintConfig) {
					window.LogisticaAdmin.rotuloRollosPrintConfig = getRotuloRollosPrintConfig();
				}
				Object.assign(window.LogisticaAdmin.rotuloRollosPrintConfig, result.data.config);
				updateRotuloPrintSizeHint();
			}

			return result;
		});
	}

	function initRotuloPrintSettingsForm(root) {
		var form;

		if (!root) {
			return;
		}

		form = root.querySelector('[data-rotulo-print-settings-form]');
		if (!form || form.dataset.rotuloPrintSettingsBound === '1') {
			return;
		}

		form.dataset.rotuloPrintSettingsBound = '1';
		form.querySelectorAll('[data-rotulo-print-width], [data-rotulo-print-height]').forEach(function (field) {
			field.addEventListener('input', function () {
				syncRotuloPrintSettingsPreview(root);
			});
		});

		form.addEventListener('submit', function (event) {
			var button;

			event.preventDefault();
			button = form.querySelector('[type="submit"]');
			if (button) {
				button.disabled = true;
			}

			setRotuloPrintSettingsStatus(root, 'Guardando tamaño…', '');
			saveRotuloPrintSettings(form, root).then(function (result) {
				setRotuloPrintSettingsStatus(
					root,
					(result && result.data && result.data.message) || 'Tamaño de rótulo guardado.',
					'success'
				);
			}).catch(function (error) {
				setRotuloPrintSettingsStatus(root, error.message || 'No se pudo guardar el tamaño.', 'error');
			}).finally(function () {
				if (button) {
					button.disabled = false;
				}
			});
		});
	}

	function syncLabelCajaInicioState(form) {
		var field;
		var allowed;

		if (!form) {
			form = document.querySelector('[data-label-print-form]');
		}
		if (!form) {
			return;
		}

		field = form.querySelector('[data-label-field="caja_inicio"]');
		if (!field) {
			return;
		}

		allowed = !!(window.LogisticaAdmin && window.LogisticaAdmin.labelPrintConfig && window.LogisticaAdmin.labelPrintConfig.allowBlankCajaNum);

		if (allowed) {
			field.removeAttribute('min');
			field.removeAttribute('required');
			field.setAttribute('placeholder', 'Manual');
		} else {
			field.setAttribute('min', '1');
			field.removeAttribute('placeholder');
			if (!String(field.value || '').trim()) {
				field.value = '1';
			}
		}
	}

	function applyLabelPrintConfigOption(config) {
		if (!window.LogisticaAdmin) {
			return;
		}
		if (!window.LogisticaAdmin.labelPrintConfig) {
			window.LogisticaAdmin.labelPrintConfig = getLabelPrintConfig();
		}
		if (config) {
			Object.assign(window.LogisticaAdmin.labelPrintConfig, config);
		}
		syncLabelCajaInicioState();
	}

	function getLabelPrintConfig() {
		var cfg = window.LogisticaAdmin && window.LogisticaAdmin.labelPrintConfig;
		return cfg || { widthMm: 100, heightMm: 100, cssUrl: '', allowBlankCajaNum: false };
	}

	function updateLabelPrintSizeBadge(board) {
		var cfg = getLabelPrintConfig();
		var badgeText = cfg.widthMm + ' × ' + cfg.heightMm + ' mm';
		var root = board || document;

		root.querySelectorAll('[data-label-print-size-badge]').forEach(function (badge) {
			badge.textContent = badgeText;
		});
	}

	function syncLabelPrintSettingsPreview(board) {
		var widthField;
		var heightField;
		var modal;
		var mount;

		if (!board || !window.LogisticaAdmin) {
			return;
		}

		widthField = board.querySelector('[data-label-print-width]');
		heightField = board.querySelector('[data-label-print-height]');
		if (!widthField || !heightField) {
			return;
		}

		if (!window.LogisticaAdmin.labelPrintConfig) {
			window.LogisticaAdmin.labelPrintConfig = getLabelPrintConfig();
		}

		window.LogisticaAdmin.labelPrintConfig.widthMm = parseInt(widthField.value, 10) || 100;
		window.LogisticaAdmin.labelPrintConfig.heightMm = parseInt(heightField.value, 10) || 100;
		updateLabelPrintSizeBadge(board);

		modal = labelPrintModalEl();
		mount = modal ? modal.querySelector('[data-label-visual-preview-mount]') : null;
		if (mount && mount.innerHTML && modal && !modal.hidden) {
			triggerLabelVisualPreview();
		}
	}

	function saveLabelPrintSettings(formOrData, board) {
		var data;

		if (!window.LogisticaAdmin || !window.fetch) {
			return Promise.reject(new Error('Panel no disponible.'));
		}

		data = formOrData instanceof FormData ? formOrData : new FormData(formOrData);
		data.set('action', 'logistica_save_label_print_settings');
		data.set('nonce', window.LogisticaAdmin.labelPrintNonce || '');

		return window.fetch(window.LogisticaAdmin.ajaxUrl, {
			method: 'POST',
			credentials: 'same-origin',
			body: data
		}).then(function (response) {
			return response.json();
		}).then(function (result) {
			if (!result || !result.success) {
				throw new Error((result && result.data && result.data.message) || 'No se pudo guardar el tamaño.');
			}

			if (result.data && result.data.config && window.LogisticaAdmin) {
				applyLabelPrintConfigOption(result.data.config);
				updateLabelPrintSizeBadge(board);
			}

			return result;
		});
	}

	function initLabelPrintSettingsForm(root) {
		var form;
		var board = root || document.querySelector('[data-etiqueta-catalog-board]');

		if (!board) {
			return;
		}

		form = board.querySelector('[data-label-print-settings-form]');
		if (!form || form.dataset.labelPrintSettingsBound === '1') {
			return;
		}

		form.dataset.labelPrintSettingsBound = '1';
		form.querySelectorAll('[data-label-print-width], [data-label-print-height]').forEach(function (field) {
			field.addEventListener('input', function () {
				syncLabelPrintSettingsPreview(board);
			});
		});

		form.addEventListener('submit', function (event) {
			var button;
			var data;

			event.preventDefault();
			button = form.querySelector('[type="submit"]');
			if (button) {
				button.disabled = true;
			}

			data = new FormData(form);
			data.set('label_print_save_mode', 'size');

			setEtiquetaCatalogStatus(board, 'Guardando tamaño…', false);
			saveLabelPrintSettings(data, board).then(function () {
				setEtiquetaCatalogStatus(board, '', '');
			}).catch(function (error) {
				setEtiquetaCatalogStatus(board, error.message || 'No se pudo guardar el tamaño.', 'error');
			}).finally(function () {
				if (button) {
					button.disabled = false;
				}
			});
		});
	}

	function setLabelPrintOptionsStatus(form, message, type) {
		var statusNode = form ? form.querySelector('[data-label-print-options-status]') : null;

		if (!statusNode) {
			return;
		}

		statusNode.textContent = message || '';
		statusNode.classList.toggle('is-error', type === 'error');
		statusNode.classList.toggle('is-success', type === 'success');
	}

	function initLabelPrintOptionsForm(root) {
		var form;
		var board = root || document.querySelector('[data-etiqueta-catalog-board]');

		if (!board) {
			return;
		}

		form = board.querySelector('[data-label-print-options-form]');
		if (!form || form.dataset.labelPrintOptionsBound === '1') {
			return;
		}

		form.dataset.labelPrintOptionsBound = '1';
		form.querySelectorAll('[data-label-print-allow-blank-caja]').forEach(function (checkbox) {
			checkbox.addEventListener('change', function () {
				if (!window.LogisticaAdmin.labelPrintConfig) {
					window.LogisticaAdmin.labelPrintConfig = getLabelPrintConfig();
				}
				window.LogisticaAdmin.labelPrintConfig.allowBlankCajaNum = checkbox.checked;
				syncLabelCajaInicioState();
			});
		});

		form.addEventListener('submit', function (event) {
			var button;
			var data;

			event.preventDefault();
			button = form.querySelector('[type="submit"]');
			if (button) {
				button.disabled = true;
			}

			setLabelPrintOptionsStatus(form, 'Guardando…', '');
			data = new FormData(form);

			saveLabelPrintSettings(data, board).then(function (result) {
				setLabelPrintOptionsStatus(form, (result.data && result.data.message) || 'Opción guardada.', 'success');
			}).catch(function (error) {
				setLabelPrintOptionsStatus(form, error.message || 'No se pudo guardar la opción.', 'error');
			}).finally(function () {
				if (button) {
					button.disabled = false;
				}
			});
		});
	}

	function syncRotuloPrintSettingsPreview(board) {
		var root;
		var widthField;
		var heightField;
		var mount;

		if (!board) {
			board = document.querySelector('[data-rotulo-rollos-board]')
				|| document.querySelector('[data-rotulo-print-settings-root]')
				|| document.querySelector('[data-etiqueta-catalog-board]');
		}
		if (!board || !window.LogisticaAdmin) {
			return;
		}

		root = board.matches('[data-rotulo-print-settings-root]')
			? board
			: (board.querySelector('[data-rotulo-print-settings-root]') || board);
		widthField = root.querySelector('[data-rotulo-print-width]');
		heightField = root.querySelector('[data-rotulo-print-height]');
		if (!widthField || !heightField) {
			return;
		}
		if (!window.LogisticaAdmin.rotuloRollosPrintConfig) {
			window.LogisticaAdmin.rotuloRollosPrintConfig = getRotuloRollosPrintConfig();
		}
		window.LogisticaAdmin.rotuloRollosPrintConfig.widthMm = parseInt(widthField.value, 10) || 216;
		window.LogisticaAdmin.rotuloRollosPrintConfig.heightMm = parseInt(heightField.value, 10) || 140;
		updateRotuloPrintSizeHint();
		mount = document.querySelector('[data-rotulo-visual-preview-mount]');
		if (mount && mount.innerHTML && rotuloPrintModalEl() && !rotuloPrintModalEl().hidden) {
			previewRotuloFromModal();
		}
	}

	function initRotuloPrintSettingsForms() {
		document.querySelectorAll('[data-rotulo-print-settings-root], [data-rotulo-rollos-board]').forEach(function (root) {
			initRotuloPrintSettingsForm(root);
		});
		updateRotuloPrintSizeHint();
	}

	function openRotuloPrintModal(row) {
		var modal = rotuloPrintModalEl();
		var form = modal ? modal.querySelector('[data-rotulo-print-form]') : null;
		var ctx = collectRotuloRowContext(row);
		if (!modal || !form || !ctx || !ctx.rotulo_id) {
			return;
		}

		rotuloRollosState.row = row;
		ensureRotuloPrintModalPortal();
		modal.hidden = false;
		modal.setAttribute('aria-hidden', 'false');
		syncLogisticaModalBodyLock();
		setRotuloPrintStatus('', false);

		var summary = modal.querySelector('[data-rotulo-print-summary]');
		if (summary) {
			summary.textContent = (ctx.ancho_label || '—') + ' · ' + (ctx.tipo_label || ctx.tipo || '—');
		}

		setRotuloFieldValue(form, 'rotulo_id', ctx.rotulo_id);
		setRotuloFieldValue(form, 'ancho_label', ctx.ancho_label);
		setRotuloFieldValue(form, 'tamano', ctx.tamano);
		setRotuloFieldValue(form, 'accent_color', ctx.accent_color);
		setRotuloFieldValue(form, 'ancho_display', ctx.ancho_label);
		setRotuloFieldValue(form, 'lote', '');
		setRotuloFieldValue(form, 'peso', '');
		setRotuloFieldValue(form, 'gramaje', '');
		setRotuloFieldValue(form, 'num_consecutivo', '1');
		setRotuloFieldValue(form, 'num_copias', '1');
		delete form.dataset.rotuloPesoFieldsSignature;

		syncRotuloPesoFields(form);
		updateRotuloPrintSizeHint();
		renderRotuloPrintHistoryListFromEntries(form, []);
		form.querySelector('[data-rotulo-field="lote"]').focus();
		previewRotuloFromModal();
		loadRotuloPrintHistoryForModal(form, ctx.rotulo_id);
	}

	function closeRotuloPrintModal() {
		var modal = rotuloPrintModalEl();
		if (!modal) {
			return;
		}
		modal.hidden = true;
		modal.setAttribute('aria-hidden', 'true');
		syncLogisticaModalBodyLock();
		rotuloRollosState.row = null;
	}

	function openRotuloEditModal(row) {
		var modal = rotuloEditModalEl();
		var form = modal ? modal.querySelector('[data-rotulo-edit-form]') : null;
		var rotuloId = row ? row.getAttribute('data-rotulo-id') : '';
		if (!modal || !form || !rotuloId) {
			return;
		}

		ensureRotuloEditModalPortal();
		modal.hidden = false;
		modal.setAttribute('aria-hidden', 'false');
		syncLogisticaModalBodyLock();

		var idField = form.querySelector('[data-rotulo-edit-id]');
		var tipoField = form.querySelector('[data-rotulo-edit-tipo]');
		var tamanoField = form.querySelector('[data-rotulo-edit-tamano]');
		var obsField = form.querySelector('[data-rotulo-edit-observaciones]');

		if (idField) {
			idField.value = rotuloId;
		}
		if (tipoField) {
			tipoField.value = row.getAttribute('data-tipo') || '';
		}
		if (tamanoField) {
			tamanoField.value = row.getAttribute('data-tamano') || '';
		}
		if (obsField) {
			obsField.value = row.querySelector('.logistica-rotulo-rollos-observaciones-cell')
				? row.querySelector('.logistica-rotulo-rollos-observaciones-cell').textContent.replace('—', '').trim()
				: '';
		}
	}

	function closeRotuloEditModal() {
		var modal = rotuloEditModalEl();
		if (!modal) {
			return;
		}
		modal.hidden = true;
		modal.setAttribute('aria-hidden', 'true');
		syncLogisticaModalBodyLock();
	}

	function submitRotuloPrintModal(event) {
		if (event) {
			event.preventDefault();
			event.stopPropagation();
		}
		var form = event ? event.target : document.querySelector('[data-rotulo-print-form]');
		if (!form || !window.LogisticaRotuloRollosPrint) {
			return false;
		}

		var jobs = window.LogisticaRotuloRollosPrint.buildJobsFromForm(form, collectRotuloRowContext(rotuloRollosState.row));
		var validation = window.LogisticaRotuloRollosPrint.validateJobs(jobs, form);
		if (validation) {
			setRotuloPrintStatus(validation, true);
			return false;
		}

		setRotuloPrintStatus('Abriendo impresión. Elige Carta (vertical), márgenes «Ninguno», activa «Gráficos de fondo» y desactiva encabezados/pies. Salen 2 rótulos por hoja.', false);
		window.LogisticaRotuloRollosPrint.printLabels(jobs).then(function (result) {
			var message = 'Rótulo enviado a imprimir.';
			if (result && result.count > 1) {
				message = result.count + ' rótulos enviados a imprimir.';
			}
			setRotuloPrintStatus(message, false);
			return saveRotuloPrintHistory(form).then(function () {
				return result;
			});
		}).catch(function (error) {
			setRotuloPrintStatus((error && error.message) || 'No se pudo abrir la impresión.', true);
		});

		return false;
	}

	function updateRotuloNewFormColorPreview(board) {
		if (!board || !window.LogisticaRotuloRollosPrint) {
			return;
		}
		var tamanoField = board.querySelector('[data-rotulo-tamano-field]');
		var preview = board.querySelector('[data-rotulo-color-preview]');
		var swatch = board.querySelector('[data-rotulo-color-swatch]');
		var label = board.querySelector('[data-rotulo-color-label]');
		if (!tamanoField || !preview || !swatch || !label) {
			return;
		}
		var tamano = String(tamanoField.value || '').trim();
		if (!tamano) {
			preview.hidden = true;
			return;
		}
		var color = window.LogisticaRotuloRollosPrint.colorForTamano(tamano);
		swatch.style.backgroundColor = color;
		label.textContent = 'Color automático: ' + color;
		preview.hidden = false;
	}

	function bindRotuloRollosRowActions() {
		document.addEventListener('click', function (event) {
			var printBtn = event.target.closest('[data-action="print-rotulo-rollos"]');
			if (printBtn) {
				event.preventDefault();
				openRotuloPrintModal(printBtn.closest('[data-rotulo-row]'));
				return;
			}
			var editBtn = event.target.closest('[data-action="edit-rotulo-rollos"]');
			if (editBtn) {
				event.preventDefault();
				openRotuloEditModal(editBtn.closest('[data-rotulo-row]'));
			}
		});
	}

	function initRotuloPrintModal() {
		var modal = rotuloPrintModalEl();
		if (!modal || modal.dataset.rotuloPrintBound === '1') {
			return;
		}
		ensureRotuloPrintModalPortal();
		modal.dataset.rotuloPrintBound = '1';

		modal.querySelectorAll('[data-rotulo-print-close]').forEach(function (button) {
			button.addEventListener('click', closeRotuloPrintModal);
		});

		var form = modal.querySelector('[data-rotulo-print-form]');
		if (form) {
			form.addEventListener('submit', submitRotuloPrintModal);
			['lote', 'num_consecutivo', 'peso', 'gramaje', 'num_copias'].forEach(function (fieldName) {
				var field = form.querySelector('[data-rotulo-field="' + fieldName + '"]');
				if (field) {
					field.addEventListener('input', function () {
						syncRotuloPrintHistoryActiveState(form);
						previewRotuloFromModal();
					});
					if (fieldName === 'lote' || fieldName === 'num_consecutivo') {
						field.addEventListener('change', function () {
							syncRotuloPrintHistoryActiveState(form);
							previewRotuloFromModal();
						});
					}
				}
			});

			form.addEventListener('click', function (event) {
				var applyBtn = event.target.closest('[data-rotulo-print-history-apply]');
				if (applyBtn) {
					event.preventDefault();
					applyRotuloPrintHistoryEntry(form, rotuloPrintHistoryEntryFromButton(applyBtn));
				}
			});
		}

		var previewBtn = modal.querySelector('[data-rotulo-print-preview]');
		if (previewBtn) {
			previewBtn.addEventListener('click', revealRotuloVisualPreview);
		}
	}

	function initRotuloEditModal() {
		var modal = rotuloEditModalEl();
		var form;
		var board;

		if (!modal || modal.dataset.rotuloEditBound === '1') {
			return;
		}
		ensureRotuloEditModalPortal();
		modal.dataset.rotuloEditBound = '1';
		modal.querySelectorAll('[data-rotulo-edit-close]').forEach(function (button) {
			button.addEventListener('click', closeRotuloEditModal);
		});

		form = modal.querySelector('[data-rotulo-edit-form]');
		if (!form) {
			return;
		}

		form.addEventListener('submit', function (event) {
			event.preventDefault();
			board = document.querySelector('[data-rotulo-rollos-board]');

			var tipoField = form.querySelector('[data-rotulo-edit-tipo]');
			var tamanoField = form.querySelector('[data-rotulo-edit-tamano]');

			if (!tipoField || !String(tipoField.value || '').trim()) {
				setRotuloRollosStatus(board, 'Selecciona un tipo.', 'error');
				return;
			}
			if (!tamanoField || !String(tamanoField.value || '').trim()) {
				setRotuloRollosStatus(board, 'Indica el tamaño.', 'error');
				return;
			}

			submitRotuloRollosSave(board, form, {
				onSuccess: function () {
					closeRotuloEditModal();
				}
			});
		});
	}

	function bindRotuloRollosFilterEvents() {
		if (document.documentElement.dataset.rotuloRollosFilterEventsBound === '1') {
			return;
		}

		document.documentElement.dataset.rotuloRollosFilterEventsBound = '1';

		document.addEventListener('input', function (event) {
			var board;

			if (!event.target || !event.target.matches('[data-rotulo-rollos-filter-search]')) {
				return;
			}

			board = event.target.closest('[data-rotulo-rollos-board]');
			if (!board) {
				return;
			}

			initRotuloRollosFilters(board);
			if (typeof board.__logisticaRefreshRotuloRollosFilters === 'function') {
				board.__logisticaRefreshRotuloRollosFilters({ resetPage: true });
			}
		});

		document.addEventListener('change', function (event) {
			var board;

			if (!event.target || !event.target.matches('[data-rotulo-rollos-filter-tipo], [data-rotulo-rollos-filter-tamano]')) {
				return;
			}

			board = event.target.closest('[data-rotulo-rollos-board]');
			if (!board) {
				return;
			}

			initRotuloRollosFilters(board);
			if (typeof board.__logisticaRefreshRotuloRollosFilters === 'function') {
				board.__logisticaRefreshRotuloRollosFilters({ resetPage: true });
			}
		});

		document.addEventListener('click', function (event) {
			var clearButton = event.target.closest('[data-action="clear-rotulo-rollos-filters"]');
			var board;

			if (!clearButton) {
				return;
			}

			board = clearButton.closest('[data-rotulo-rollos-board]');
			if (!board) {
				return;
			}

			initRotuloRollosFilters(board);
			if (typeof board.__logisticaClearRotuloRollosFilters === 'function') {
				event.preventDefault();
				board.__logisticaClearRotuloRollosFilters();
			}
		});
	}

	function normalizeRotuloRollosFilterValue(value) {
		return String(value || '').toLowerCase().trim();
	}

	function refreshRotuloRollosFilterRows(board) {
		var tbody = board ? board.querySelector('[data-rotulo-rollos-rows]') : null;

		if (!tbody) {
			return [];
		}

		return Array.prototype.slice.call(tbody.querySelectorAll('[data-rotulo-row]'));
	}

	function updateRotuloRollosFilterState(board, options) {
		var searchInput = board.querySelector('[data-rotulo-rollos-filter-search]');
		var tipoSelect = board.querySelector('[data-rotulo-rollos-filter-tipo]');
		var tamanoSelect = board.querySelector('[data-rotulo-rollos-filter-tamano]');
		var clearButton = board.querySelector('[data-action="clear-rotulo-rollos-filters"]');
		var meta = board.querySelector('[data-rotulo-rollos-filter-meta]');
		var emptyMessage = board.querySelector('[data-rotulo-rollos-filter-empty]');
		var filtersWrap = board.querySelector('[data-rotulo-rollos-filters]');
		var badge = board.querySelector('[data-rotulo-rollos-filter-badge]');
		var rows = refreshRotuloRollosFilterRows(board);
		var query = normalizeRotuloRollosFilterValue(searchInput ? searchInput.value : '');
		var tipo = normalizeRotuloRollosFilterValue(tipoSelect ? tipoSelect.value : '');
		var tamano = normalizeRotuloRollosFilterValue(tamanoSelect ? tamanoSelect.value : '');
		var visible = 0;
		var total = rows.length;
		var hasFilters = Boolean(query || tipo || tamano);

		options = options || {};

		rows.forEach(function (row) {
			var rowTipo = normalizeRotuloRollosFilterValue(row.getAttribute('data-tipo') || '');
			var rowTamano = normalizeRotuloRollosFilterValue(row.getAttribute('data-tamano') || '');
			var rowSearch = normalizeRotuloRollosFilterValue(row.getAttribute('data-rotulo-search') || '');
			var matchesSearch = !query || rowSearch.indexOf(query) !== -1;
			var matchesTipo = !tipo || rowTipo === tipo;
			var matchesTamano = !tamano || rowTamano === tamano;
			var show = matchesSearch && matchesTipo && matchesTamano;

			row.dataset.logisticaFilterVisible = show ? '1' : '0';
			if (show) {
				visible += 1;
			}
		});

		applyTablePagination(board, 'rotulo-rollos', rows, options);

		if (filtersWrap) {
			filtersWrap.classList.toggle('is-active', hasFilters);
		}

		if (badge) {
			badge.hidden = !hasFilters;
		}

		if (meta) {
			if (hasFilters) {
				meta.textContent = visible + ' de ' + total + ' registros visibles';
				meta.hidden = false;
			} else {
				meta.textContent = '';
				meta.hidden = true;
			}
		}

		if (emptyMessage) {
			emptyMessage.hidden = visible > 0 || total === 0;
		}

		if (clearButton) {
			clearButton.disabled = !hasFilters;
		}
	}

	function initRotuloRollosFilters(board) {
		if (!board || !board.querySelector('[data-rotulo-rollos-rows]')) {
			return;
		}

		bindRotuloRollosFilterEvents();

		board.__logisticaRefreshRotuloRollosFilters = function (options) {
			updateRotuloRollosFilterState(board, options || {});
		};

		board.__logisticaClearRotuloRollosFilters = function () {
			var searchInput = board.querySelector('[data-rotulo-rollos-filter-search]');
			var tipoSelect = board.querySelector('[data-rotulo-rollos-filter-tipo]');
			var tamanoSelect = board.querySelector('[data-rotulo-rollos-filter-tamano]');

			if (searchInput) {
				searchInput.value = '';
			}
			if (tipoSelect) {
				tipoSelect.value = '';
			}
			if (tamanoSelect) {
				tamanoSelect.value = '';
			}

			updateRotuloRollosFilterState(board, { resetPage: true });
		};

		if (board.dataset.rotuloRollosFiltersBound !== '1') {
			board.dataset.rotuloRollosFiltersBound = '1';

			bindTablePagination(board, 'rotulo-rollos', function (options) {
				updateRotuloRollosFilterState(board, options || {});
			});

			try {
				updateRotuloRollosFilterState(board);
			} catch (error) {
				// La paginación no debe bloquear la vista si falla la inicialización.
			}
		}
	}

	function initRotulosRollosBoard() {
		var board = document.querySelector('[data-rotulo-rollos-board]');

		initRotuloPrintSettingsForms();

		if (!board) {
			return;
		}

		if (board.dataset.rotuloRollosCoreBound !== '1') {
			board.dataset.rotuloRollosCoreBound = '1';
			bindRotuloRollosRowActions();
			initRotuloPrintModal();
			initRotuloEditModal();
			initRotuloNewForm(board);

			var tamanoField = board.querySelector('[data-rotulo-tamano-field]');
			if (tamanoField) {
				tamanoField.addEventListener('input', function () {
					updateRotuloNewFormColorPreview(board);
				});
			}

			document.addEventListener('keydown', function (event) {
				if (event.key !== 'Escape') {
					return;
				}
				if (rotuloPrintModalEl() && !rotuloPrintModalEl().hidden) {
					closeRotuloPrintModal();
				}
				if (rotuloEditModalEl() && !rotuloEditModalEl().hidden) {
					closeRotuloEditModal();
				}
			});

			if (board.querySelector('[data-excel-zoom-root]')) {
				initExcelTableZoom(board.querySelector('[data-excel-zoom-root]'));
			}
		}

		initRotuloRollosFilters(board);
		updateRotuloNewFormColorPreview(board);
		updateRotuloPrintSizeHint();
	}

	function etiquetaCatalogAjax(action, params) {
		if (!window.LogisticaAdmin || !window.LogisticaAdmin.ajaxUrl) {
			return Promise.resolve(null);
		}

		var data = new URLSearchParams(params || {});
		data.set('action', action);
		data.set('nonce', window.LogisticaAdmin.labelPrintNonce || '');

		return window.fetch(window.LogisticaAdmin.ajaxUrl, {
			method: 'POST',
			credentials: 'same-origin',
			headers: {
				'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
			},
			body: data.toString()
		}).then(function (response) {
			return response.json();
		});
	}

	function stripEtiquetaEditUrl() {
		if (!window.history || !window.history.replaceState) {
			return;
		}

		var url = new URL(window.location.href);
		if (!url.searchParams.has('edit_id')) {
			return;
		}

		url.searchParams.delete('edit_id');
		window.history.replaceState(null, '', url.toString());
	}

	function getEtiquetaCatalogBoard() {
		return document.querySelector('[data-etiqueta-catalog-board]');
	}

	function getEtiquetaHiddenOptions(board) {
		board = board || getEtiquetaCatalogBoard();
		var raw = board ? board.getAttribute('data-etiqueta-hidden-options') : '';
		var hidden = {
			tipos: {},
			mercados: {},
			tamanos: {}
		};

		if (raw) {
			try {
				hidden = JSON.parse(raw) || hidden;
			} catch (error) {
				hidden = {
					tipos: {},
					mercados: {},
					tamanos: {}
				};
			}
		}

		if (!hidden || typeof hidden !== 'object') {
			hidden = {
				tipos: {},
				mercados: {},
				tamanos: {}
			};
		}

		hidden.tipos = hidden.tipos && typeof hidden.tipos === 'object' ? hidden.tipos : {};
		hidden.mercados = hidden.mercados && typeof hidden.mercados === 'object' ? hidden.mercados : {};
		hidden.tamanos = hidden.tamanos && typeof hidden.tamanos === 'object' ? hidden.tamanos : {};

		return hidden;
	}

	function setEtiquetaHiddenOptions(board, hidden) {
		if (!board) {
			return;
		}

		board.setAttribute(
			'data-etiqueta-hidden-options',
			JSON.stringify(
				hidden || {
					tipos: {},
					mercados: {},
					tamanos: {}
				}
			)
		);
	}

	function getEtiquetaHiddenBucketKey(kind) {
		if ('tipo' === kind) {
			return 'tipos';
		}
		if ('mercado' === kind) {
			return 'mercados';
		}
		if ('tamano' === kind) {
			return 'tamanos';
		}

		return '';
	}

	function normalizeEtiquetaHiddenLookup(kind, key) {
		var value = String(key || '').trim();

		if ('tamano' === kind) {
			return value.toUpperCase();
		}

		return value.toUpperCase();
	}

	function isEtiquetaOptionHidden(kind, key, board) {
		var hidden = getEtiquetaHiddenOptions(board);
		var bucketKey = getEtiquetaHiddenBucketKey(kind);
		var bucket;
		var lookup;
		var storedKey;

		if (!bucketKey || !key) {
			return false;
		}

		bucket = hidden[bucketKey] || {};
		lookup = normalizeEtiquetaHiddenLookup(kind, key);

		if (Object.prototype.hasOwnProperty.call(bucket, key)) {
			return true;
		}

		for (storedKey in bucket) {
			if (!Object.prototype.hasOwnProperty.call(bucket, storedKey)) {
				continue;
			}

			if (normalizeEtiquetaHiddenLookup(kind, storedKey) === lookup) {
				return true;
			}
		}

		return false;
	}

	function getEtiquetaHiddenOptionLabel(kind, key, board) {
		var hidden = getEtiquetaHiddenOptions(board);
		var bucketKey = getEtiquetaHiddenBucketKey(kind);
		var bucket;
		var lookup;
		var storedKey;

		if (!bucketKey || !key) {
			return String(key || '');
		}

		bucket = hidden[bucketKey] || {};
		if (Object.prototype.hasOwnProperty.call(bucket, key)) {
			return String(bucket[key] || key);
		}

		lookup = normalizeEtiquetaHiddenLookup(kind, key);
		for (storedKey in bucket) {
			if (!Object.prototype.hasOwnProperty.call(bucket, storedKey)) {
				continue;
			}

			if (normalizeEtiquetaHiddenLookup(kind, storedKey) === lookup) {
				return String(bucket[storedKey] || storedKey);
			}
		}

		return String(key || '');
	}

	function removeEtiquetaHiddenOption(board, kind, key) {
		var hidden = getEtiquetaHiddenOptions(board);
		var bucketKey = getEtiquetaHiddenBucketKey(kind);
		var bucket;
		var lookup;
		var storedKey;

		if (!board || !bucketKey || !key) {
			return;
		}

		bucket = hidden[bucketKey] || {};
		delete bucket[key];

		lookup = normalizeEtiquetaHiddenLookup(kind, key);
		for (storedKey in bucket) {
			if (!Object.prototype.hasOwnProperty.call(bucket, storedKey)) {
				continue;
			}

			if (normalizeEtiquetaHiddenLookup(kind, storedKey) === lookup) {
				delete bucket[storedKey];
			}
		}

		hidden[bucketKey] = bucket;
		setEtiquetaHiddenOptions(board, hidden);
	}

	function getEtiquetaMercadoOptions(board) {
		board = board || getEtiquetaCatalogBoard();
		var raw = board ? board.getAttribute('data-etiqueta-mercados') : '';
		var options = {};

		if (raw) {
			try {
				options = JSON.parse(raw) || {};
			} catch (error) {
				options = {};
			}
		}

		return options && typeof options === 'object' ? options : {};
	}

	function setEtiquetaMercadoOptions(board, options) {
		if (!board) {
			return;
		}

		board.setAttribute('data-etiqueta-mercados', JSON.stringify(options || {}));
	}

	function markEtiquetaSelectOptionHidden(option, isHidden) {
		if (!option) {
			return;
		}

		option.dataset.hiddenOption = isHidden ? '1' : '0';
		if (isHidden) {
			option.classList.add('logistica-etiqueta-select-option--hidden');
			return;
		}

		option.classList.remove('logistica-etiqueta-select-option--hidden');
	}

	function etiquetaCatalogCellHtml(text, isHidden, title) {
		if (!isHidden) {
			return escapeHtml(text);
		}

		return '<span class="logistica-etiqueta-catalog-cell--hidden" title="' + escapeHtml(title || 'Opción oculta del catálogo') + '">' + escapeHtml(text) + '</span>';
	}

	function collectEtiquetaFormHiddenIssues(form, board) {
		var issues = [];
		var tipoField = form ? form.querySelector('[data-etiqueta-tipo-select]') : null;
		var mercadoField = form ? form.querySelector('[data-etiqueta-mercado-select]') : null;
		var tamanoField = form ? form.querySelector('[data-etiqueta-tamano-select]') : null;
		var tipo = tipoField ? tipoField.value : '';
		var mercado = mercadoField ? mercadoField.value : '';
		var tamano = tamanoField ? tamanoField.value : '';

		if (tipo && isEtiquetaOptionHidden('tipo', tipo, board)) {
			issues.push({
				kind: 'tipo',
				key: tipo,
				label: getEtiquetaHiddenOptionLabel('tipo', tipo, board),
				field_label: 'Tipo producto'
			});
		}

		if (mercado && isEtiquetaOptionHidden('mercado', mercado, board)) {
			issues.push({
				kind: 'mercado',
				key: mercado,
				label: getEtiquetaHiddenOptionLabel('mercado', mercado, board),
				field_label: 'Mercado'
			});
		}

		if (tamano && isEtiquetaOptionHidden('tamano', tamano, board)) {
			issues.push({
				kind: 'tamano',
				key: tamano,
				label: getEtiquetaHiddenOptionLabel('tamano', tamano, board),
				field_label: 'Tamaño'
			});
		}

		return issues;
	}

	function updateEtiquetaHiddenOptionsAlert(form, board, issues) {
		var alertNode = form ? form.querySelector('[data-etiqueta-hidden-options-alert]') : null;
		var listNode = form ? form.querySelector('[data-etiqueta-hidden-options-list]') : null;
		var html = '';
		var i;
		var issue;

		if (!alertNode || !listNode) {
			return;
		}

		issues = Array.isArray(issues) ? issues : collectEtiquetaFormHiddenIssues(form, board);

		if (!issues.length) {
			alertNode.hidden = true;
			listNode.innerHTML = '';
			return;
		}

		for (i = 0; i < issues.length; i++) {
			issue = issues[i];
			html += '<li class="logistica-etiqueta-hidden-options-alert__item" data-etiqueta-hidden-issue="' + escapeHtml(issue.kind || '') + '" data-key="' + escapeHtml(issue.key || '') + '">'
				+ '<span><strong>' + escapeHtml(issue.field_label || '') + ':</strong> ' + escapeHtml(issue.label || issue.key || '') + ' <em>(oculto)</em></span>'
				+ '<button type="button" class="button button-small logistica-etiqueta-hidden-options-alert__restore" data-etiqueta-restore-hidden="' + escapeHtml(issue.kind || '') + '" data-key="' + escapeHtml(issue.key || '') + '">Restaurar</button>'
				+ '</li>';
		}

		alertNode.hidden = false;
		listNode.innerHTML = html;
	}

	function restoreEtiquetaCatalogOption(kind, key, board, form) {
		var action = 'tipo' === kind
			? 'logistica_restore_etiqueta_tipo_producto'
			: ('mercado' === kind ? 'logistica_restore_etiqueta_mercado' : 'logistica_restore_etiqueta_tamano');

		return etiquetaCatalogAjax(action, { key: key }).then(function (payload) {
			if (!payload || !payload.success || !payload.data) {
				return payload;
			}

			removeEtiquetaHiddenOption(board, kind, key);

			if ('tipo' === kind && payload.data.options) {
				setEtiquetaTipoOptions(board, payload.data.options);
			} else if ('mercado' === kind && payload.data.options) {
				setEtiquetaMercadoOptions(board, payload.data.options);
			} else if ('tamano' === kind && payload.data.options) {
				setEtiquetaTamanoOptions(board, payload.data.options);
			}

			syncEtiquetaTipoSelects(board);
			syncEtiquetaMercadoSelects(board);
			syncEtiquetaTamanoSelects(board);
			updateEtiquetaHiddenOptionsAlert(form, board);

			return payload;
		});
	}

	function collectEtiquetaRowHiddenIssues(row, board) {
		var issues = [];

		if (!row) {
			return issues;
		}

		if (Array.isArray(row.hidden_issues) && row.hidden_issues.length) {
			return row.hidden_issues;
		}

		if (row.tipo_producto && isEtiquetaOptionHidden('tipo', row.tipo_producto, board)) {
			issues.push({
				kind: 'tipo',
				key: row.tipo_producto,
				label: getEtiquetaHiddenOptionLabel('tipo', row.tipo_producto, board) || row.tipo_label || row.tipo_producto,
				field_label: 'Tipo producto'
			});
		}

		if (row.mercado && isEtiquetaOptionHidden('mercado', row.mercado, board)) {
			issues.push({
				kind: 'mercado',
				key: row.mercado,
				label: getEtiquetaHiddenOptionLabel('mercado', row.mercado, board) || row.mercado,
				field_label: 'Mercado'
			});
		}

		if (row.tamano && isEtiquetaOptionHidden('tamano', row.tamano, board)) {
			issues.push({
				kind: 'tamano',
				key: row.tamano,
				label: getEtiquetaHiddenOptionLabel('tamano', row.tamano, board) || row.tamano,
				field_label: 'Tamaño'
			});
		}

		return issues;
	}

	function buildEtiquetaHiddenRowHint(row, board) {
		var issues = collectEtiquetaRowHiddenIssues(row, board);
		var parts = [];
		var i;

		if (!issues.length) {
			return '';
		}

		for (i = 0; i < issues.length; i++) {
			parts.push(issues[i].field_label + ' (' + (issues[i].label || issues[i].key || '') + ')');
		}

		return 'Este código usa opciones ocultas del catálogo: ' + parts.join(', ') + '. Edítalo para restaurarlas o elegir otras disponibles.';
	}

	function syncEtiquetaHiddenRowHintAttributes(rowEl, row, board) {
		var hint;
		var hasHidden;
		var refCell;
		var tooltip;
		var issues;

		if (!rowEl) {
			return;
		}

		board = board || getEtiquetaCatalogBoard();
		issues = collectEtiquetaRowHiddenIssues(row, board);
		hasHidden = issues.length > 0;
		hint = buildEtiquetaHiddenRowHint(row, board);

		rowEl.classList.toggle('logistica-etiqueta-catalog-row--hidden-option', hasHidden);
		rowEl.setAttribute('data-has-hidden-options', hasHidden ? '1' : '0');

		if (hasHidden && hint) {
			rowEl.setAttribute('title', hint);
			rowEl.setAttribute('data-hidden-options-hint', hint);
			rowEl.setAttribute('tabindex', '0');
		} else {
			rowEl.removeAttribute('title');
			rowEl.removeAttribute('data-hidden-options-hint');
			rowEl.removeAttribute('tabindex');
			rowEl.classList.remove('is-selected');
		}

		refCell = rowEl.querySelector('td');
		if (!refCell) {
			return;
		}

		refCell.classList.add('logistica-etiqueta-catalog-ref-cell');
		tooltip = refCell.querySelector('.logistica-etiqueta-catalog-row-tooltip');

		if (hasHidden && hint) {
			if (!tooltip) {
				tooltip = document.createElement('span');
				tooltip.className = 'logistica-etiqueta-catalog-row-tooltip';
				tooltip.setAttribute('role', 'tooltip');
				refCell.appendChild(tooltip);
			}
			tooltip.textContent = hint;
			return;
		}

		if (tooltip) {
			tooltip.remove();
		}
	}

	function clearEtiquetaHiddenRowSelection(board, exceptRow) {
		if (!board) {
			return;
		}

		board.querySelectorAll('[data-etiqueta-row].logistica-etiqueta-catalog-row--hidden-option.is-selected').forEach(function (rowEl) {
			if (exceptRow && rowEl === exceptRow) {
				return;
			}

			rowEl.classList.remove('is-selected');
		});
	}

	function selectEtiquetaHiddenCatalogRow(rowEl, board) {
		var hint;

		if (!rowEl || rowEl.getAttribute('data-has-hidden-options') !== '1') {
			return;
		}

		board = board || rowEl.closest('[data-etiqueta-catalog-board]');
		hint = rowEl.getAttribute('data-hidden-options-hint') || '';

		clearEtiquetaHiddenRowSelection(board, rowEl);
		rowEl.classList.add('is-selected');

		if (board && hint) {
			setEtiquetaCatalogStatus(board, hint, 'warning');
		}
	}

	function initEtiquetaCatalogHiddenRowHints(board) {
		if (!board || board.dataset.etiquetaHiddenRowHintsBound === '1') {
			return;
		}

		board.dataset.etiquetaHiddenRowHintsBound = '1';

		board.addEventListener('click', function (event) {
			var rowEl = event.target.closest('[data-etiqueta-row][data-has-hidden-options="1"]');

			if (!rowEl || event.target.closest('button, a, form, input, select, textarea, label')) {
				return;
			}

			selectEtiquetaHiddenCatalogRow(rowEl, board);
		});

		board.addEventListener('focusin', function (event) {
			var rowEl = event.target.closest('[data-etiqueta-row][data-has-hidden-options="1"]');

			if (!rowEl) {
				return;
			}

			selectEtiquetaHiddenCatalogRow(rowEl, board);
		});

		board.addEventListener('mouseover', function (event) {
			var rowEl = event.target.closest('[data-etiqueta-row][data-has-hidden-options="1"]');
			var hint;

			if (!rowEl) {
				return;
			}

			hint = rowEl.getAttribute('data-hidden-options-hint') || '';
			if (hint) {
				setEtiquetaCatalogStatus(board, hint, 'warning');
			}
		});

		board.addEventListener('mouseout', function (event) {
			var rowEl = event.target.closest('[data-etiqueta-row][data-has-hidden-options="1"]');
			var related;

			if (!rowEl || rowEl.classList.contains('is-selected')) {
				return;
			}

			related = event.relatedTarget;
			if (related && rowEl.contains(related)) {
				return;
			}

			setEtiquetaCatalogStatus(board, '', '');
		});
	}

	function applyEtiquetaCatalogRowHiddenState(rowEl, row, board) {
		var issues;
		var tipoHidden;
		var mercadoHidden;
		var tamanoHidden;
		var mercadoOptions;
		var mercadoLabel;
		var tamanoText;
		var cells;

		if (!rowEl || !row) {
			return;
		}

		board = board || getEtiquetaCatalogBoard();
		issues = collectEtiquetaRowHiddenIssues(row, board);
		tipoHidden = issues.some(function (issue) { return issue.kind === 'tipo'; });
		mercadoHidden = issues.some(function (issue) { return issue.kind === 'mercado'; });
		tamanoHidden = issues.some(function (issue) { return issue.kind === 'tamano'; });
		mercadoOptions = getEtiquetaMercadoOptions(board);
		mercadoLabel = mercadoOptions[row.mercado] || row.mercado || '';
		tamanoText = row.tamano ? row.tamano : '—';

		syncEtiquetaHiddenRowHintAttributes(rowEl, row, board);

		cells = rowEl.querySelectorAll('td');
		if (cells.length >= 4) {
			cells[1].innerHTML = etiquetaCatalogCellHtml(row.tipo_label || row.tipo_producto || '', tipoHidden, 'Tipo oculto del catálogo');
			cells[2].innerHTML = etiquetaCatalogCellHtml(mercadoLabel, mercadoHidden, 'Mercado oculto del catálogo');
			cells[3].innerHTML = etiquetaCatalogCellHtml(tamanoText, tamanoHidden, 'Tamaño oculto del catálogo');
		}
	}

	function showEtiquetaAddPanel(panel) {
		if (!panel) {
			return;
		}

		panel.hidden = false;
		panel.removeAttribute('hidden');
	}

	function hideEtiquetaAddPanel(panel) {
		if (!panel) {
			return;
		}

		panel.hidden = true;
		panel.setAttribute('hidden', '');
	}

	function getEtiquetaTamanoOptions(board) {
		board = board || getEtiquetaCatalogBoard();
		var raw = board ? board.getAttribute('data-etiqueta-tamanos') : '';
		var options = [];

		if (raw) {
			try {
				options = JSON.parse(raw) || [];
			} catch (error) {
				options = [];
			}
		}

		return Array.isArray(options) ? options : [];
	}

	function setEtiquetaTamanoOptions(board, options) {
		if (!board) {
			return;
		}

		board.setAttribute('data-etiqueta-tamanos', JSON.stringify(options || []));
	}

	function rebuildEtiquetaTamanoSelect(select, options, selectedValue, board) {
		var current = selectedValue != null ? String(selectedValue) : (select ? select.value : '');
		var option;
		var exists = false;
		var i;
		var boardRef = board || getEtiquetaCatalogBoard();

		if (!select) {
			return;
		}

		select.innerHTML = '';
		option = document.createElement('option');
		option.value = '';
		option.textContent = '— Seleccionar —';
		select.appendChild(option);

		for (i = 0; i < options.length; i++) {
			option = document.createElement('option');
			option.value = options[i];
			option.textContent = options[i];
			markEtiquetaSelectOptionHidden(option, false);
			if (current && String(options[i]).toLowerCase() === String(current).toLowerCase()) {
				option.selected = true;
				exists = true;
				current = options[i];
			}
			select.appendChild(option);
		}

		if (current && !exists) {
			option = document.createElement('option');
			option.value = current;
			option.textContent = current + (isEtiquetaOptionHidden('tamano', current, boardRef) ? ' (oculto)' : '');
			markEtiquetaSelectOptionHidden(option, isEtiquetaOptionHidden('tamano', current, boardRef));
			option.selected = true;
			select.appendChild(option);
		}

		if (current) {
			select.value = current;
		}
	}

	function syncEtiquetaTamanoSelects(board, selectedValue) {
		var options = getEtiquetaTamanoOptions(board);

		document.querySelectorAll('[data-etiqueta-tamano-select]').forEach(function (select) {
			var value = selectedValue != null ? selectedValue : select.value;
			rebuildEtiquetaTamanoSelect(select, options, value, board);
		});
	}

	function initEtiquetaTamanoSelects(board) {
		board = board || getEtiquetaCatalogBoard();
		if (board) {
			syncEtiquetaTamanoSelects(board);
		}
	}

	function getEtiquetaTipoOptions(board) {
		board = board || getEtiquetaCatalogBoard();
		var raw = board ? board.getAttribute('data-etiqueta-tipos') : '';
		var options = {};

		if (raw) {
			try {
				options = JSON.parse(raw) || {};
			} catch (error) {
				options = {};
			}
		}

		return options && typeof options === 'object' ? options : {};
	}

	function setEtiquetaTipoOptions(board, options) {
		if (!board) {
			return;
		}

		board.setAttribute('data-etiqueta-tipos', JSON.stringify(options || {}));
		board.setAttribute('data-etiqueta-type-labels', JSON.stringify(options || {}));
	}

	function rebuildEtiquetaTipoSelect(select, options, selectedValue, board) {
		var current = selectedValue != null ? String(selectedValue) : (select ? select.value : '');
		var keys = Object.keys(options || {}).sort(function (a, b) {
			return String(options[a]).localeCompare(String(options[b]), 'es', { sensitivity: 'base' });
		});
		var option;
		var exists = false;
		var i;
		var boardRef = board || getEtiquetaCatalogBoard();
		var hiddenLabel;

		if (!select) {
			return;
		}

		select.innerHTML = '';
		option = document.createElement('option');
		option.value = '';
		option.textContent = '— Seleccionar —';
		select.appendChild(option);

		for (i = 0; i < keys.length; i++) {
			option = document.createElement('option');
			option.value = keys[i];
			option.textContent = options[keys[i]];
			markEtiquetaSelectOptionHidden(option, false);
			if (current && String(keys[i]).toUpperCase() === String(current).toUpperCase()) {
				option.selected = true;
				exists = true;
				current = keys[i];
			}
			select.appendChild(option);
		}

		if (current && !exists) {
			hiddenLabel = getEtiquetaHiddenOptionLabel('tipo', current, boardRef) || current;
			option = document.createElement('option');
			option.value = current;
			option.textContent = hiddenLabel + (isEtiquetaOptionHidden('tipo', current, boardRef) ? ' (oculto)' : '');
			markEtiquetaSelectOptionHidden(option, isEtiquetaOptionHidden('tipo', current, boardRef));
			option.selected = true;
			select.appendChild(option);
		}

		if (current) {
			select.value = current;
		}
	}

	function syncEtiquetaTipoSelects(board, selectedValue) {
		var options = getEtiquetaTipoOptions(board);

		document.querySelectorAll('[data-etiqueta-tipo-select]').forEach(function (select) {
			var value = selectedValue != null ? selectedValue : select.value;
			rebuildEtiquetaTipoSelect(select, options, value, board);
		});
	}

	function initEtiquetaTipoSelects(board) {
		board = board || getEtiquetaCatalogBoard();
		if (board) {
			syncEtiquetaTipoSelects(board);
		}
	}

	function rebuildEtiquetaMercadoSelect(select, options, selectedValue, board) {
		var current = selectedValue != null ? String(selectedValue) : (select ? select.value : '');
		var keys = Object.keys(options || {}).sort(function (a, b) {
			return String(options[a]).localeCompare(String(options[b]), 'es', { sensitivity: 'base' });
		});
		var option;
		var exists = false;
		var i;
		var boardRef = board || getEtiquetaCatalogBoard();
		var hiddenLabel;

		if (!select) {
			return;
		}

		select.innerHTML = '';

		for (i = 0; i < keys.length; i++) {
			option = document.createElement('option');
			option.value = keys[i];
			option.textContent = options[keys[i]];
			markEtiquetaSelectOptionHidden(option, false);
			if (current && String(keys[i]).toUpperCase() === String(current).toUpperCase()) {
				option.selected = true;
				exists = true;
				current = keys[i];
			}
			select.appendChild(option);
		}

		if (current && !exists) {
			hiddenLabel = getEtiquetaHiddenOptionLabel('mercado', current, boardRef) || current;
			option = document.createElement('option');
			option.value = current;
			option.textContent = hiddenLabel + (isEtiquetaOptionHidden('mercado', current, boardRef) ? ' (oculto)' : '');
			markEtiquetaSelectOptionHidden(option, isEtiquetaOptionHidden('mercado', current, boardRef));
			option.selected = true;
			select.appendChild(option);
		}

		if (current) {
			select.value = current;
		}
	}

	function syncEtiquetaMercadoSelects(board, selectedValue) {
		var options = getEtiquetaMercadoOptions(board);

		document.querySelectorAll('[data-etiqueta-mercado-select]').forEach(function (select) {
			var value = selectedValue != null ? selectedValue : select.value;
			rebuildEtiquetaMercadoSelect(select, options, value, board);
		});
	}

	function initEtiquetaMercadoSelects(board) {
		board = board || getEtiquetaCatalogBoard();
		if (board) {
			syncEtiquetaMercadoSelects(board);
		}
	}

	function etiquetaTamanoIsValid(form) {
		var select = form ? form.querySelector('[data-etiqueta-tamano-select]') : null;
		return !!(select && select.value);
	}

	function etiquetaTipoIsValid(form) {
		var select = form ? form.querySelector('[data-etiqueta-tipo-select]') : null;
		return !!(select && select.value);
	}

	function etiquetaEditModalEl() {
		return document.querySelector('[data-etiqueta-edit-modal]');
	}

	function ensureEtiquetaEditModalPortal() {
		var modal = etiquetaEditModalEl();
		if (!modal || modal.parentElement === document.body) {
			return;
		}
		document.body.appendChild(modal);
	}

	function setEtiquetaCatalogStatus(board, message, type) {
		var node = board ? board.querySelector('[data-etiqueta-catalog-status]') : null;
		if (!node) {
			return;
		}

		node.textContent = message || '';
		node.classList.toggle('is-error', type === 'error');
		node.classList.toggle('is-success', type === 'success');
		node.classList.toggle('is-warning', type === 'warning');
	}

	function normalizeEtiquetaReferenciaField(event) {
		var field = event && event.target ? event.target : event;

		if (!field || typeof field.value !== 'string') {
			return;
		}

		field.value = field.value.toUpperCase();
	}

	function bindEtiquetaReferenciaFields(root) {
		if (!root) {
			return;
		}

		root.querySelectorAll('[data-etiqueta-referencia-field]').forEach(function (field) {
			if (field.dataset.etiquetaReferenciaBound === '1') {
				return;
			}

			field.dataset.etiquetaReferenciaBound = '1';
			field.classList.add('logistica-field-uppercase');
			field.addEventListener('input', normalizeEtiquetaReferenciaField);
			field.addEventListener('blur', normalizeEtiquetaReferenciaField);
		});
	}

	function getEtiquetaCatalogPostConfig(board) {
		return {
			saveAction: board ? (board.getAttribute('data-etiqueta-post-url') || '') : '',
			redirect: board ? (board.getAttribute('data-etiqueta-redirect-base') || '') : '',
			deleteNonce: board ? (board.getAttribute('data-etiqueta-delete-nonce') || '') : ''
		};
	}

	function etiquetaCatalogActionsHtml(board, rowId) {
		var config = getEtiquetaCatalogPostConfig(board);
		var deleteConfirm = '¿Eliminar este código?';

		return ''
			+ '<div class="logistica-etiqueta-catalog-actions">'
			+ '<button type="button" class="button button-small logistica-table-icon-btn logistica-etiqueta-catalog-btn-print logistica-label-print-btn" data-action="print-production-label" aria-label="Imprimir etiqueta" title="Imprimir etiqueta">'
			+ '<span class="dashicons dashicons-printer" aria-hidden="true"></span>'
			+ '</button>'
			+ '<button type="button" class="button button-small logistica-table-icon-btn logistica-etiqueta-catalog-btn-edit" data-action="edit-etiqueta-catalog" data-catalog-id="' + escapeHtml(String(rowId || '')) + '" aria-label="Editar" title="Editar">'
			+ '<span class="dashicons dashicons-edit" aria-hidden="true"></span>'
			+ '</button>'
			+ '<form method="post" action="' + escapeHtml(config.saveAction) + '" class="logistica-inline-delete-form" onsubmit="return confirm(' + JSON.stringify(deleteConfirm) + ');">'
			+ '<input type="hidden" name="action" value="logistica_delete_etiqueta_producto">'
			+ '<input type="hidden" name="_wpnonce" value="' + escapeHtml(config.deleteNonce) + '">'
			+ '<input type="hidden" name="redirect_to" value="' + escapeHtml(config.redirect) + '">'
			+ '<input type="hidden" name="id" value="' + escapeHtml(String(rowId || '')) + '">'
			+ '<button type="submit" class="button button-small logistica-table-icon-btn logistica-etiqueta-catalog-btn-delete" aria-label="Eliminar" title="Eliminar">'
			+ '<span class="dashicons dashicons-trash" aria-hidden="true"></span>'
			+ '</button>'
			+ '</form>'
			+ '</div>';
	}

	function buildEtiquetaCatalogDuplicateReportFromDom(board) {
		var byCodigo = {};
		var byEan = {};
		var codigoGroups = 0;
		var eanGroups = 0;
		var rowIds = {};
		var codigos = [];
		var eans = [];

		if (!board) {
			return {
				has_duplicates: false,
				codigo_groups: 0,
				ean_groups: 0,
				row_ids: [],
				codigos: [],
				eans: []
			};
		}

		board.querySelectorAll('[data-etiqueta-row]').forEach(function (rowEl) {
			var id = String(rowEl.getAttribute('data-catalog-id') || '');
			var codigo = String(rowEl.getAttribute('data-codigo-producto') || '').replace(/\D/g, '');
			var ean = String(rowEl.getAttribute('data-barcode') || '').trim();

			if (codigo.length > 6) {
				codigo = codigo.slice(-6);
			}
			while (codigo.length < 6 && codigo.length > 0) {
				codigo = '0' + codigo;
			}

			if (id && codigo.length === 6) {
				if (!byCodigo[codigo]) {
					byCodigo[codigo] = [];
				}
				byCodigo[codigo].push(id);
			}

			if (id && ean && ean !== '—') {
				if (!byEan[ean]) {
					byEan[ean] = [];
				}
				byEan[ean].push(id);
			}
		});

		Object.keys(byCodigo).forEach(function (codigo) {
			if (byCodigo[codigo].length < 2) {
				return;
			}
			codigoGroups += 1;
			codigos.push(codigo);
			byCodigo[codigo].forEach(function (id) {
				rowIds[id] = true;
			});
		});

		Object.keys(byEan).forEach(function (ean) {
			if (byEan[ean].length < 2) {
				return;
			}
			eanGroups += 1;
			eans.push(ean);
			byEan[ean].forEach(function (id) {
				rowIds[id] = true;
			});
		});

		codigos.sort();
		eans.sort();

		return {
			has_duplicates: Object.keys(rowIds).length > 0,
			codigo_groups: codigoGroups,
			ean_groups: eanGroups,
			row_ids: Object.keys(rowIds),
			codigos: codigos,
			eans: eans
		};
	}

	function buildEtiquetaDuplicateAlertText(report) {
		var parts = [];

		if (!report || !report.has_duplicates) {
			return '';
		}

		if (report.codigo_groups) {
			parts.push(
				report.codigo_groups === 1
					? '1 código de producto repetido'
					: (report.codigo_groups + ' códigos de producto repetidos')
			);
		}

		if (report.ean_groups) {
			parts.push(
				report.ean_groups === 1
					? '1 EAN-13 repetido'
					: (report.ean_groups + ' EAN-13 repetidos')
			);
		}

		var text = 'Hay ' + parts.join(' y ') + '. Revise las filas resaltadas para evitar errores en los códigos de barras.';
		if (report.codigos && report.codigos.length) {
			text += ' Códigos: ' + report.codigos.join(', ') + '.';
		}

		return text;
	}

	function refreshEtiquetaCatalogDuplicateState(board, report) {
		var alertBox;
		var alertText;
		var duplicateIds;
		var duplicateHint;

		if (!board) {
			return;
		}

		report = report || buildEtiquetaCatalogDuplicateReportFromDom(board);
		duplicateIds = {};
		(report.row_ids || []).forEach(function (id) {
			duplicateIds[String(id)] = true;
		});
		duplicateHint = 'Código o EAN-13 duplicado. Revisa este registro para evitar errores en códigos de barras.';

		board.querySelectorAll('[data-etiqueta-row]').forEach(function (rowEl) {
			var id = String(rowEl.getAttribute('data-catalog-id') || '');
			var isDuplicate = !!duplicateIds[id];
			var hiddenHint = rowEl.getAttribute('data-hidden-options-hint') || '';
			var titleParts = [];

			rowEl.classList.toggle('logistica-etiqueta-catalog-row--duplicate', isDuplicate);
			rowEl.setAttribute('data-etiqueta-duplicate', isDuplicate ? '1' : '0');

			if (isDuplicate) {
				titleParts.push(duplicateHint);
			}
			if (hiddenHint) {
				titleParts.push(hiddenHint);
			}

			if (titleParts.length) {
				rowEl.setAttribute('title', titleParts.join(' '));
			} else if (!hiddenHint) {
				rowEl.removeAttribute('title');
			}
		});

		alertBox = board.querySelector('[data-etiqueta-duplicate-alert]');
		alertText = board.querySelector('[data-etiqueta-duplicate-alert-text]');

		if (!alertBox) {
			return;
		}

		if (report.has_duplicates) {
			alertBox.hidden = false;
			if (alertText) {
				alertText.textContent = buildEtiquetaDuplicateAlertText(report);
			}
		} else {
			alertBox.hidden = true;
			if (alertText) {
				alertText.textContent = '';
			}
		}
	}

	function buildEtiquetaCatalogRowHtml(board, row) {
		var tamanoText = row.tamano ? row.tamano : '—';
		var eanText = row.ean13 || '—';
		var observacionesText = String(row.observaciones || '').trim();
		var searchText = buildEtiquetaRowSearch(row);
		var productoLabel = buildEtiquetaProductoLabel(row.tipo_label || row.tipo_producto, row.tamano);
		var mercadoOptions = getEtiquetaMercadoOptions(board);
		var mercadoLabel = mercadoOptions[row.mercado] || row.mercado || '';
		var tipoHidden = isEtiquetaOptionHidden('tipo', row.tipo_producto, board);
		var mercadoHidden = isEtiquetaOptionHidden('mercado', row.mercado, board);
		var tamanoHidden = isEtiquetaOptionHidden('tamano', row.tamano, board);
		var hasHidden = !!(row.has_hidden_options || tipoHidden || mercadoHidden || tamanoHidden);
		var hiddenHint = hasHidden ? buildEtiquetaHiddenRowHint(row, board) : '';

		return ''
			+ '<tr'
			+ (hasHidden ? ' class="logistica-etiqueta-catalog-row--hidden-option"' : '')
			+ ' data-etiqueta-row'
			+ ' data-catalog-id="' + escapeHtml(String(row.id || '')) + '"'
			+ ' data-referencia="' + escapeHtml(row.referencia || '') + '"'
			+ ' data-tipo="' + escapeHtml(row.tipo_producto || '') + '"'
			+ ' data-tipo-label="' + escapeHtml(row.tipo_label || row.tipo_producto || '') + '"'
			+ ' data-mercado="' + escapeHtml(row.mercado || '') + '"'
			+ ' data-tamano="' + escapeHtml(row.tamano || '') + '"'
			+ ' data-producto="' + escapeHtml(productoLabel) + '"'
			+ ' data-barcode="' + escapeHtml(eanText) + '"'
			+ ' data-codigo-producto="' + escapeHtml(row.codigo_producto || '') + '"'
			+ ' data-etiqueta-search="' + escapeHtml(searchText) + '"'
			+ ' data-has-hidden-options="' + (hasHidden ? '1' : '0') + '"'
			+ ' data-etiqueta-duplicate="0"'
			+ (hiddenHint ? ' title="' + escapeHtml(hiddenHint) + '" data-hidden-options-hint="' + escapeHtml(hiddenHint) + '" tabindex="0"' : '')
			+ '>'
			+ '<td class="logistica-etiqueta-catalog-ref-cell">' + escapeHtml(row.referencia || '')
			+ (hiddenHint ? '<span class="logistica-etiqueta-catalog-row-tooltip" role="tooltip">' + escapeHtml(hiddenHint) + '</span>' : '')
			+ '</td>'
			+ '<td>' + etiquetaCatalogCellHtml(row.tipo_label || row.tipo_producto || '', tipoHidden, 'Tipo oculto del catálogo') + '</td>'
			+ '<td>' + etiquetaCatalogCellHtml(mercadoLabel, mercadoHidden, 'Mercado oculto del catálogo') + '</td>'
			+ '<td>' + etiquetaCatalogCellHtml(tamanoText, tamanoHidden, 'Tamaño oculto del catálogo') + '</td>'
			+ '<td><code>' + escapeHtml(row.codigo_producto || '') + '</code></td>'
			+ '<td><code>' + escapeHtml(eanText) + '</code></td>'
			+ '<td class="logistica-etiqueta-catalog-observaciones-cell"' + (observacionesText ? ' title="' + escapeHtml(observacionesText) + '"' : '') + '>' + escapeHtml(observacionesText || '—') + '</td>'
			+ '<td class="logistica-etiqueta-catalog-actions-cell">' + etiquetaCatalogActionsHtml(board, row.id) + '</td>'
			+ '</tr>';
	}

	function appendEtiquetaFilterSelectOption(select, value, label) {
		var option;
		var exists = false;
		var normalizedValue;

		if (!select || !value) {
			return;
		}

		normalizedValue = String(value).toLowerCase();
		Array.prototype.forEach.call(select.options, function (item) {
			if (String(item.value).toLowerCase() === normalizedValue) {
				exists = true;
			}
		});

		if (exists) {
			return;
		}

		option = document.createElement('option');
		option.value = value;
		option.textContent = label || value;
		select.appendChild(option);

		Array.prototype.slice.call(select.options, 1).sort(function (a, b) {
			return String(a.textContent).localeCompare(String(b.textContent), 'es', { sensitivity: 'base' });
		}).forEach(function (item) {
			select.appendChild(item);
		});
	}

	function syncEtiquetaCatalogFilterOptions(board, row) {
		var tipoSelect;
		var mercadoSelect;
		var tamanoSelect;
		var marketOptions;

		if (!board || !row) {
			return;
		}

		tipoSelect = board.querySelector('[data-etiqueta-catalog-filter-tipo]');
		mercadoSelect = board.querySelector('[data-etiqueta-catalog-filter-mercado]');
		tamanoSelect = board.querySelector('[data-etiqueta-catalog-filter-tamano]');

		if (row.tipo_producto) {
			appendEtiquetaFilterSelectOption(
				tipoSelect,
				row.tipo_producto,
				row.tipo_label || row.tipo_producto
			);
		}

		if (row.mercado) {
			marketOptions = {
				EXP: 'Exportación',
				NAC: 'Nacional'
			};
			appendEtiquetaFilterSelectOption(
				mercadoSelect,
				row.mercado,
				marketOptions[row.mercado] || row.mercado
			);
		}

		if (row.tamano) {
			appendEtiquetaFilterSelectOption(tamanoSelect, row.tamano, row.tamano);
		}
	}

	function syncEtiquetaCatalogRowMeta(board, row) {
		if (!board || !row) {
			return;
		}

		if (row.tamano) {
			var tamanoOptions = getEtiquetaTamanoOptions(board);
			var tamanoLabel = row.tamano;
			var hasTamano = tamanoOptions.some(function (item) {
				return String(item).toLowerCase() === String(tamanoLabel).toLowerCase();
			});

			if (!hasTamano && !isEtiquetaOptionHidden('tamano', tamanoLabel, board)) {
				tamanoOptions.push(tamanoLabel);
				tamanoOptions.sort(function (a, b) {
					return String(a).localeCompare(String(b), 'es', { sensitivity: 'base' });
				});
				setEtiquetaTamanoOptions(board, tamanoOptions);
				syncEtiquetaTamanoSelects(board);
			}
		}

		if (row.tipo_producto) {
			var tipoOptions = getEtiquetaTipoOptions(board);

			if (!tipoOptions[row.tipo_producto] && !isEtiquetaOptionHidden('tipo', row.tipo_producto, board)) {
				tipoOptions[row.tipo_producto] = row.tipo_label || row.tipo_producto;
				setEtiquetaTipoOptions(board, tipoOptions);
				syncEtiquetaTipoSelects(board);
			}
		}

		if (row.mercado) {
			var mercadoOptions = getEtiquetaMercadoOptions(board);

			if (!mercadoOptions[row.mercado] && !isEtiquetaOptionHidden('mercado', row.mercado, board)) {
				mercadoOptions[row.mercado] = row.mercado;
				setEtiquetaMercadoOptions(board, mercadoOptions);
				syncEtiquetaMercadoSelects(board);
			}
		}

		syncEtiquetaCatalogFilterOptions(board, row);
	}

	function insertEtiquetaCatalogRow(board, row) {
		var tbody;
		var emptyHint;
		var temp;
		var rowEl;

		if (!board || !row) {
			return null;
		}

		tbody = board.querySelector('[data-etiqueta-catalog-rows]');
		if (!tbody) {
			return null;
		}

		emptyHint = board.querySelector('[data-etiqueta-catalog-empty-hint]');
		if (emptyHint) {
			emptyHint.hidden = true;
		}

		temp = document.createElement('tbody');
		temp.innerHTML = buildEtiquetaCatalogRowHtml(board, row);
		rowEl = temp.firstElementChild;
		if (!rowEl) {
			return null;
		}

		tbody.insertBefore(rowEl, tbody.firstChild);
		return rowEl;
	}

	function resetEtiquetaNewForm(form, board) {
		var mercadoField;
		var idField;
		var referenciaField;

		if (!form) {
			return;
		}

		form.reset();
		idField = form.querySelector('[name="id"]');
		if (idField) {
			idField.value = '0';
		}

		mercadoField = form.querySelector('[name="mercado"]');
		if (mercadoField) {
			mercadoField.value = 'NAC';
		}

		syncEtiquetaTamanoSelects(board, '');
		syncEtiquetaTipoSelects(board, '');

		referenciaField = form.querySelector('[data-etiqueta-referencia-field]');
		if (referenciaField) {
			window.requestAnimationFrame(function () {
				referenciaField.focus();
			});
		}
	}

	function initEtiquetaNewForm(board) {
		var form = board ? board.querySelector('[data-etiqueta-new-form]') : null;
		var submitBtn;

		if (!form || form.dataset.etiquetaNewAjaxBound === '1') {
			return;
		}

		form.dataset.etiquetaNewAjaxBound = '1';
		bindEtiquetaReferenciaFields(form);

		form.addEventListener('submit', function (event) {
			event.preventDefault();

			if (!etiquetaTamanoIsValid(form)) {
				setEtiquetaCatalogStatus(board, 'Selecciona o guarda un tamaño.', 'error');
				return;
			}

			if (!etiquetaTipoIsValid(form)) {
				setEtiquetaCatalogStatus(board, 'Selecciona o guarda un tipo de producto.', 'error');
				return;
			}

			var referenciaField = form.querySelector('[data-etiqueta-referencia-field]');
			if (referenciaField) {
				normalizeEtiquetaReferenciaField(referenciaField);
			}

			var params = new URLSearchParams(new window.FormData(form));
			submitBtn = form.querySelector('[data-etiqueta-new-submit]');

			setEtiquetaCatalogStatus(board, 'Guardando…', false);
			if (submitBtn) {
				submitBtn.disabled = true;
			}

			etiquetaCatalogAjax('logistica_save_etiqueta_producto_ajax', params).then(function (payload) {
				if (submitBtn) {
					submitBtn.disabled = false;
				}

				if (!payload || !payload.success || !payload.data || !payload.data.row) {
					setEtiquetaCatalogStatus(board, (payload && payload.data && payload.data.message) || 'No se pudo guardar.', 'error');
					return;
				}

				var row = payload.data.row;
				var existingRow = etiquetaCatalogRowById(board, row.id);

				if (existingRow) {
					applyEtiquetaCatalogRowState(existingRow, row, board);
				} else {
					insertEtiquetaCatalogRow(board, row);
				}

				syncEtiquetaCatalogRowMeta(board, row);

				if (typeof board.__logisticaRefreshEtiquetaCatalogFilters === 'function') {
					board.__logisticaRefreshEtiquetaCatalogFilters({ resetPage: true });
				}

				refreshEtiquetaCatalogTotal(board, payload.data.total);
				refreshEtiquetaCatalogDuplicateState(board, payload.data.duplicates || null);
				setEtiquetaCatalogStatus(board, payload.data.message || 'Registro guardado.', 'success');
				resetEtiquetaNewForm(form, board);
			});
		});
	}

	function setEtiquetaEditStatus(form, message, isError) {
		var node = form ? form.querySelector('[data-etiqueta-edit-status]') : null;
		if (!node) {
			return;
		}

		node.textContent = message || '';
		node.classList.toggle('is-error', !!isError);
	}

	function refreshEtiquetaCatalogTotal(board, total) {
		var node = board ? board.querySelector('[data-etiqueta-catalog-total]') : null;
		if (node && total !== undefined && total !== null) {
			node.textContent = String(total);
		}
	}

	function etiquetaCatalogRowById(board, catalogId) {
		var rows;
		var i;

		if (!board) {
			return null;
		}

		rows = board.querySelectorAll('[data-etiqueta-row]');
		for (i = 0; i < rows.length; i++) {
			if (String(rows[i].getAttribute('data-catalog-id') || '') === String(catalogId)) {
				return rows[i];
			}
		}

		return null;
	}

	function populateEtiquetaEditForm(form, row, board) {
		var fieldMap = {
			id: 'id',
			referencia: 'referencia',
			tipo_producto: 'tipo_producto',
			mercado: 'mercado',
			tamano: 'tamano',
			codigo_producto: 'codigo_producto',
			observaciones: 'observaciones'
		};

		if (!form || !row) {
			return;
		}

		board = board || getEtiquetaCatalogBoard();

		Object.keys(fieldMap).forEach(function (fieldName) {
			var input = form.querySelector('[data-etiqueta-edit-field="' + fieldName + '"]');
			if (input) {
				input.value = row[fieldMap[fieldName]] || '';
			}
		});

		syncEtiquetaTamanoSelects(board, row.tamano || '');
		syncEtiquetaTipoSelects(board, row.tipo_producto || '');
		syncEtiquetaMercadoSelects(board, row.mercado || '');
		updateEtiquetaEditEanPreview(form, row.ean13 || '');
		updateEtiquetaHiddenOptionsAlert(form, board, row.hidden_issues || collectEtiquetaFormHiddenIssues(form, board));
	}

	function updateEtiquetaEditEanPreview(form, ean13) {
		var wrap = form ? form.querySelector('[data-etiqueta-edit-ean-preview]') : null;
		var code = form ? form.querySelector('[data-etiqueta-edit-ean-value]') : null;

		if (!wrap || !code) {
			return;
		}

		if (ean13) {
			code.textContent = ean13;
			wrap.hidden = false;
			return;
		}

		code.textContent = '';
		wrap.hidden = true;
	}

	function scheduleEtiquetaEditEanPreview(form) {
		if (!form) {
			return;
		}

		if (form.__etiquetaEanPreviewTimer) {
			window.clearTimeout(form.__etiquetaEanPreviewTimer);
		}

		form.__etiquetaEanPreviewTimer = window.setTimeout(function () {
			var tipoField = form.querySelector('[data-etiqueta-edit-field="tipo_producto"]');
			var mercadoField = form.querySelector('[data-etiqueta-edit-field="mercado"]');
			var codigoField = form.querySelector('[data-etiqueta-edit-field="codigo_producto"]');

			etiquetaCatalogAjax('logistica_preview_etiqueta_ean_fields', {
				tipo_producto: tipoField ? tipoField.value : '',
				mercado: mercadoField ? mercadoField.value : '',
				codigo_producto: codigoField ? codigoField.value : ''
			}).then(function (payload) {
				if (!payload || !payload.success) {
					updateEtiquetaEditEanPreview(form, '');
					return;
				}

				updateEtiquetaEditEanPreview(form, payload.data.ean13 || '');
			});
		}, 250);
	}

	function buildEtiquetaRowSearch(row) {
		var productoLabel = buildEtiquetaProductoLabel(row.tipo_label || row.tipo_producto, row.tamano);

		return [
			row.referencia,
			row.tipo_label || row.tipo_producto,
			productoLabel,
			row.mercado,
			row.tamano,
			row.codigo_producto,
			row.ean13,
			row.observaciones
		].map(function (value) {
			return String(value || '').toLowerCase().trim();
		}).filter(Boolean).join(' ');
	}

	function applyEtiquetaCatalogRowState(rowEl, row, board) {
		var cells;
		var tamanoText;

		if (!rowEl || !row) {
			return;
		}

		board = board || getEtiquetaCatalogBoard();

		tamanoText = row.tamano ? row.tamano : '—';
		rowEl.setAttribute('data-catalog-id', String(row.id || ''));
		rowEl.setAttribute('data-referencia', row.referencia || '');
		rowEl.setAttribute('data-tipo', row.tipo_producto || '');
		rowEl.setAttribute('data-tipo-label', row.tipo_label || row.tipo_producto || '');
		rowEl.setAttribute('data-mercado', row.mercado || '');
		rowEl.setAttribute('data-tamano', row.tamano || '');
		rowEl.setAttribute('data-producto', buildEtiquetaProductoLabel(row.tipo_label || row.tipo_producto, row.tamano));
		rowEl.setAttribute('data-barcode', row.ean13 || '');
		rowEl.setAttribute('data-codigo-producto', row.codigo_producto || '');
		rowEl.setAttribute('data-etiqueta-search', buildEtiquetaRowSearch(row));

		cells = rowEl.querySelectorAll('td');
		if (cells.length >= 8) {
			cells[0].textContent = row.referencia || '';
			cells[1].innerHTML = etiquetaCatalogCellHtml(row.tipo_label || row.tipo_producto || '', isEtiquetaOptionHidden('tipo', row.tipo_producto, board), 'Tipo oculto del catálogo');
			cells[2].innerHTML = etiquetaCatalogCellHtml(getEtiquetaMercadoOptions(board)[row.mercado] || row.mercado || '', isEtiquetaOptionHidden('mercado', row.mercado, board), 'Mercado oculto del catálogo');
			cells[3].innerHTML = etiquetaCatalogCellHtml(tamanoText, isEtiquetaOptionHidden('tamano', row.tamano, board), 'Tamaño oculto del catálogo');
			cells[4].innerHTML = '<code>' + escapeHtml(row.codigo_producto || '') + '</code>';
			cells[5].innerHTML = '<code>' + escapeHtml(row.ean13 || '—') + '</code>';
			cells[6].textContent = String(row.observaciones || '').trim() || '—';
			cells[6].className = 'logistica-etiqueta-catalog-observaciones-cell';
			if (row.observaciones) {
				cells[6].setAttribute('title', String(row.observaciones));
			} else {
				cells[6].removeAttribute('title');
			}
		}

		applyEtiquetaCatalogRowHiddenState(rowEl, row, board);

		var editBtn = rowEl.querySelector('[data-action="edit-etiqueta-catalog"]');
		if (editBtn) {
			editBtn.setAttribute('data-catalog-id', String(row.id || ''));
		}
	}

	function openEtiquetaEditModal(board, catalogId) {
		var modal = etiquetaEditModalEl();
		var form = modal ? modal.querySelector('[data-etiqueta-edit-form]') : null;

		if (!modal || !form || !catalogId) {
			return;
		}

		ensureEtiquetaEditModalPortal();
		modal.hidden = false;
		modal.setAttribute('aria-hidden', 'false');
		syncLogisticaModalBodyLock();
		setEtiquetaEditStatus(form, 'Cargando…', false);

		etiquetaCatalogAjax('logistica_get_etiqueta_producto_row', {
			catalog_id: catalogId
		}).then(function (payload) {
			if (!payload || !payload.success || !payload.data || !payload.data.row) {
				setEtiquetaEditStatus(form, (payload && payload.data && payload.data.message) || 'No se pudo cargar el registro.', true);
				return;
			}

			populateEtiquetaEditForm(form, payload.data.row, board);
			setEtiquetaEditStatus(form, '', false);

			var firstField = form.querySelector('[data-etiqueta-edit-field="referencia"]');
			if (firstField) {
				window.requestAnimationFrame(function () {
					firstField.focus();
					if (typeof firstField.select === 'function') {
						firstField.select();
					}
				});
			}
		});
	}

	function closeEtiquetaEditModal() {
		var modal = etiquetaEditModalEl();
		var form = modal ? modal.querySelector('[data-etiqueta-edit-form]') : null;

		if (!modal) {
			return;
		}

		modal.hidden = true;
		modal.setAttribute('aria-hidden', 'true');
		syncLogisticaModalBodyLock();

		if (form) {
			form.reset();
			updateEtiquetaEditEanPreview(form, '');
			updateEtiquetaHiddenOptionsAlert(form, getEtiquetaCatalogBoard(), []);
			setEtiquetaEditStatus(form, '', false);
		}
	}

	function initEtiquetaEditModal(board) {
		var modal = etiquetaEditModalEl();
		var form = modal ? modal.querySelector('[data-etiqueta-edit-form]') : null;
		var previewFields;

		if (!modal || !form || modal.dataset.etiquetaEditBound === '1') {
			return;
		}

		modal.dataset.etiquetaEditBound = '1';

		modal.querySelectorAll('[data-etiqueta-edit-close]').forEach(function (button) {
			button.addEventListener('click', closeEtiquetaEditModal);
		});

		previewFields = ['tipo_producto', 'mercado', 'codigo_producto'];
		previewFields.forEach(function (fieldName) {
			var field = form.querySelector('[data-etiqueta-edit-field="' + fieldName + '"]');
			if (field) {
				field.addEventListener('input', function () {
					scheduleEtiquetaEditEanPreview(form);
				});
				field.addEventListener('change', function () {
					scheduleEtiquetaEditEanPreview(form);
					updateEtiquetaHiddenOptionsAlert(form, board);
				});
			}
		});

		['tipo_producto', 'tamano'].forEach(function (fieldName) {
			var field = form.querySelector('[data-etiqueta-edit-field="' + fieldName + '"]');
			if (field) {
				field.addEventListener('change', function () {
					updateEtiquetaHiddenOptionsAlert(form, board);
				});
			}
		});

		form.addEventListener('click', function (event) {
			var restoreBtn = event.target.closest('[data-etiqueta-restore-hidden]');
			var kind;
			var key;

			if (!restoreBtn) {
				return;
			}

			event.preventDefault();
			kind = restoreBtn.getAttribute('data-etiqueta-restore-hidden') || '';
			key = restoreBtn.getAttribute('data-key') || '';

			if (!kind || !key) {
				return;
			}

			restoreBtn.disabled = true;
			setEtiquetaEditStatus(form, 'Restaurando…', false);

			restoreEtiquetaCatalogOption(kind, key, board, form).then(function (payload) {
				restoreBtn.disabled = false;

				if (!payload || !payload.success) {
					setEtiquetaEditStatus(form, (payload && payload.data && payload.data.message) || 'No se pudo restaurar.', true);
					return;
				}

				setEtiquetaEditStatus(form, payload.data.message || 'Opción restaurada.', false);
			});
		});

		form.addEventListener('submit', function (event) {
			event.preventDefault();

			if (!etiquetaTamanoIsValid(form)) {
				setEtiquetaEditStatus(form, 'Selecciona o guarda un tamaño.', true);
				return;
			}

			if (!etiquetaTipoIsValid(form)) {
				setEtiquetaEditStatus(form, 'Selecciona o guarda un tipo de producto.', true);
				return;
			}

			var params = new URLSearchParams(new FormData(form));
			var submitBtn = form.querySelector('[type="submit"]');
			var referenciaField = form.querySelector('[data-etiqueta-edit-field="referencia"]');

			if (referenciaField) {
				normalizeEtiquetaReferenciaField(referenciaField);
			}

			setEtiquetaEditStatus(form, 'Guardando…', false);
			if (submitBtn) {
				submitBtn.disabled = true;
			}

			etiquetaCatalogAjax('logistica_save_etiqueta_producto_ajax', params).then(function (payload) {
				if (submitBtn) {
					submitBtn.disabled = false;
				}

				if (!payload || !payload.success || !payload.data || !payload.data.row) {
					setEtiquetaEditStatus(form, (payload && payload.data && payload.data.message) || 'No se pudo guardar.', true);
					return;
				}

				var row = payload.data.row;
				var existingRow = etiquetaCatalogRowById(board, row.id);

				if (existingRow) {
					applyEtiquetaCatalogRowState(existingRow, row, board);
				}

				syncEtiquetaCatalogRowMeta(board, row);

				if (typeof board.__logisticaRefreshEtiquetaCatalogFilters === 'function') {
					board.__logisticaRefreshEtiquetaCatalogFilters();
				}

				refreshEtiquetaCatalogTotal(board, payload.data.total);
				refreshEtiquetaCatalogDuplicateState(board, payload.data.duplicates || null);
				setEtiquetaCatalogStatus(board, payload.data.message || 'Registro actualizado.', 'success');
				closeEtiquetaEditModal();
			});
		});

		bindEtiquetaReferenciaFields(form);

		document.addEventListener('keydown', function (event) {
			if (event.key === 'Escape' && modal && !modal.hidden) {
				closeEtiquetaEditModal();
			}
		});
	}

	function bindEtiquetaCatalogFilterEvents() {
		if (document.documentElement.dataset.etiquetaCatalogFilterEventsBound === '1') {
			return;
		}

		document.documentElement.dataset.etiquetaCatalogFilterEventsBound = '1';

		document.addEventListener('input', function (event) {
			var board;

			if (!event.target || !event.target.matches('[data-etiqueta-catalog-filter-search]')) {
				return;
			}

			board = event.target.closest('[data-etiqueta-catalog-board]');
			if (!board) {
				return;
			}

			initEtiquetaCatalogFilters(board);
			if (typeof board.__logisticaRefreshEtiquetaCatalogFilters === 'function') {
				board.__logisticaRefreshEtiquetaCatalogFilters({ resetPage: true });
			}
		});

		document.addEventListener('change', function (event) {
			var board;

			if (!event.target || !event.target.matches('[data-etiqueta-catalog-filter-tipo], [data-etiqueta-catalog-filter-mercado], [data-etiqueta-catalog-filter-tamano]')) {
				return;
			}

			board = event.target.closest('[data-etiqueta-catalog-board]');
			if (!board) {
				return;
			}

			initEtiquetaCatalogFilters(board);
			if (typeof board.__logisticaRefreshEtiquetaCatalogFilters === 'function') {
				board.__logisticaRefreshEtiquetaCatalogFilters({ resetPage: true });
			}
		});

		document.addEventListener('click', function (event) {
			var clearButton = event.target.closest('[data-action="clear-etiqueta-catalog-filters"]');
			var board;

			if (!clearButton) {
				return;
			}

			board = clearButton.closest('[data-etiqueta-catalog-board]');
			if (!board) {
				return;
			}

			initEtiquetaCatalogFilters(board);
			if (typeof board.__logisticaClearEtiquetaCatalogFilters === 'function') {
				event.preventDefault();
				board.__logisticaClearEtiquetaCatalogFilters();
			}
		});
	}

	function normalizeEtiquetaCatalogFilterValue(value) {
		return String(value || '').toLowerCase().trim();
	}

	function refreshEtiquetaCatalogFilterRows(board) {
		var tbody = board ? board.querySelector('[data-etiqueta-catalog-rows]') : null;

		if (!tbody) {
			return [];
		}

		return Array.prototype.slice.call(tbody.querySelectorAll('[data-etiqueta-row]'));
	}

	function updateEtiquetaCatalogFilterState(board, options) {
		var searchInput = board.querySelector('[data-etiqueta-catalog-filter-search]');
		var tipoSelect = board.querySelector('[data-etiqueta-catalog-filter-tipo]');
		var mercadoSelect = board.querySelector('[data-etiqueta-catalog-filter-mercado]');
		var tamanoSelect = board.querySelector('[data-etiqueta-catalog-filter-tamano]');
		var clearButton = board.querySelector('[data-action="clear-etiqueta-catalog-filters"]');
		var meta = board.querySelector('[data-etiqueta-catalog-filter-meta]');
		var emptyMessage = board.querySelector('[data-etiqueta-catalog-filter-empty]');
		var filtersWrap = board.querySelector('[data-etiqueta-catalog-filters]');
		var badge = board.querySelector('[data-etiqueta-catalog-filter-badge]');
		var rows = refreshEtiquetaCatalogFilterRows(board);
		var query = normalizeEtiquetaCatalogFilterValue(searchInput ? searchInput.value : '');
		var tipo = normalizeEtiquetaCatalogFilterValue(tipoSelect ? tipoSelect.value : '');
		var mercado = normalizeEtiquetaCatalogFilterValue(mercadoSelect ? mercadoSelect.value : '');
		var tamano = normalizeEtiquetaCatalogFilterValue(tamanoSelect ? tamanoSelect.value : '');
		var visible = 0;
		var total = rows.length;
		var hasFilters = Boolean(query || tipo || mercado || tamano);

		options = options || {};

		rows.forEach(function (row) {
			var rowMercado = normalizeEtiquetaCatalogFilterValue(row.getAttribute('data-mercado') || '');
			var rowTipo = normalizeEtiquetaCatalogFilterValue(row.getAttribute('data-tipo') || '');
			var rowTamano = normalizeEtiquetaCatalogFilterValue(row.getAttribute('data-tamano') || '');
			var rowSearch = normalizeEtiquetaCatalogFilterValue(row.getAttribute('data-etiqueta-search') || '');
			var matchesSearch = !query || rowSearch.indexOf(query) !== -1;
			var matchesTipo = !tipo || rowTipo === tipo;
			var matchesMercado = !mercado || rowMercado === mercado;
			var matchesTamano = !tamano || rowTamano === tamano;
			var show = matchesSearch && matchesTipo && matchesMercado && matchesTamano;

			row.dataset.logisticaFilterVisible = show ? '1' : '0';
			if (show) {
				visible += 1;
			}
		});

		applyTablePagination(board, 'etiqueta-catalog', rows, options);

		if (filtersWrap) {
			filtersWrap.classList.toggle('is-active', hasFilters);
		}

		if (badge) {
			badge.hidden = !hasFilters;
		}

		if (meta) {
			if (hasFilters) {
				meta.textContent = visible + ' de ' + total + ' registros visibles';
				meta.hidden = false;
			} else {
				meta.textContent = '';
				meta.hidden = true;
			}
		}

		if (emptyMessage) {
			emptyMessage.hidden = visible > 0 || total === 0;
		}

		if (clearButton) {
			clearButton.disabled = !hasFilters;
		}
	}

	function initEtiquetaCatalogFilters(board) {
		if (!board || !board.querySelector('[data-etiqueta-catalog-rows]')) {
			return;
		}

		bindEtiquetaCatalogFilterEvents();

		board.__logisticaRefreshEtiquetaCatalogFilters = function (options) {
			updateEtiquetaCatalogFilterState(board, options || {});
		};

		board.__logisticaClearEtiquetaCatalogFilters = function () {
			var searchInput = board.querySelector('[data-etiqueta-catalog-filter-search]');
			var tipoSelect = board.querySelector('[data-etiqueta-catalog-filter-tipo]');
			var mercadoSelect = board.querySelector('[data-etiqueta-catalog-filter-mercado]');
			var tamanoSelect = board.querySelector('[data-etiqueta-catalog-filter-tamano]');

			if (searchInput) {
				searchInput.value = '';
			}
			if (tipoSelect) {
				tipoSelect.value = '';
			}
			if (mercadoSelect) {
				mercadoSelect.value = '';
			}
			if (tamanoSelect) {
				tamanoSelect.value = '';
			}

			updateEtiquetaCatalogFilterState(board, { resetPage: true });
		};

		if (board.dataset.etiquetaCatalogFiltersBound !== '1') {
			board.dataset.etiquetaCatalogFiltersBound = '1';

			bindTablePagination(board, 'etiqueta-catalog', function (options) {
				updateEtiquetaCatalogFilterState(board, options || {});
			});

			try {
				updateEtiquetaCatalogFilterState(board);
			} catch (error) {
				// La paginación no debe bloquear la vista si falla la inicialización.
			}
		}
	}

	function hideEtiquetaCatalogTabLoading(boardOrPanel) {
		var panel = null;
		var board = null;
		var nodes;
		var i;
		var node;
		var overlays;
		var j;

		if (!boardOrPanel) {
			document.querySelectorAll('[data-etiqueta-catalog-board]').forEach(function (catalogBoard) {
				hideEtiquetaCatalogTabLoading(catalogBoard);
			});
			document.querySelectorAll('[data-tab-panel="logistica-codigos-etiqueta"]').forEach(function (catalogPanel) {
				hideEtiquetaCatalogTabLoading(catalogPanel);
			});
			return;
		}

		if (boardOrPanel.matches && boardOrPanel.matches('[data-tab-panel]')) {
			panel = boardOrPanel;
			board = panel.querySelector('[data-etiqueta-catalog-board]');
		} else {
			board = boardOrPanel;
			panel = board.closest('[data-tab-panel]');
		}

		nodes = [];
		if (panel) {
			nodes.push(panel);
		}
		if (board && nodes.indexOf(board) === -1) {
			nodes.push(board);
		}

		for (i = 0; i < nodes.length; i++) {
			node = nodes[i];
			node.dataset.tabLoaded = '1';
			node.classList.remove('is-tab-loading');
			overlays = node.querySelectorAll('[data-logistica-tab-loading], [data-user-panel-loading], [data-close-loading]');
			for (j = 0; j < overlays.length; j++) {
				overlays[j].hidden = true;
				overlays[j].setAttribute('aria-busy', 'false');
			}
			if (window.LogisticaTabLoading) {
				window.LogisticaTabLoading.hide(node);
			}
		}
	}

	function bindEtiquetaCatalogRowActions() {
		if (document.documentElement.dataset.etiquetaCatalogActionsBound === '1') {
			return;
		}

		document.documentElement.dataset.etiquetaCatalogActionsBound = '1';

		document.addEventListener('click', function (event) {
			var editBtn = event.target.closest('[data-action="edit-etiqueta-catalog"]');
			var board;
			var printBtn;

			if (editBtn) {
				board = editBtn.closest('[data-etiqueta-catalog-board]');
				if (board) {
					event.preventDefault();
					openEtiquetaEditModal(board, editBtn.getAttribute('data-catalog-id') || '');
				}
				return;
			}

			printBtn = event.target.closest('[data-action="print-production-label"]');
			if (printBtn) {
				board = printBtn.closest('[data-etiqueta-catalog-board]');
				if (board) {
					event.preventDefault();
					openLabelPrintModal(printBtn.closest('[data-etiqueta-row]'));
				}
			}
		});
	}

	function initSelectCatalogOptionsAdmin(root) {
		var statusNode;
		var cfg = window.LogisticaAdmin || {};

		if (!root) {
			document.querySelectorAll('[data-select-catalog-options]').forEach(function (node) {
				initSelectCatalogOptionsAdmin(node);
			});
			return;
		}

		if (root.dataset.selectCatalogBound === '1') {
			return;
		}

		root.dataset.selectCatalogBound = '1';
		statusNode = root.querySelector('[data-select-catalog-status]');

		function setStatus(message, type) {
			if (!statusNode) {
				return;
			}
			statusNode.textContent = message || '';
			statusNode.classList.toggle('is-error', type === 'error');
			statusNode.classList.toggle('is-success', type === 'success');
		}

		function toSentenceCase(value) {
			var text = String(value || '');
			if (!text) {
				return '';
			}
			text = text.toLocaleLowerCase('es');
			return text.charAt(0).toLocaleUpperCase('es') + text.slice(1);
		}

		function actionButtonsHtml() {
			return '<div class="logistica-etiqueta-catalog-options__actions">'
				+ '<button type="button" class="button button-small logistica-table-icon-btn logistica-etiqueta-catalog-options__btn-save" data-select-catalog-save aria-label="Guardar" title="Guardar">'
				+ '<span class="dashicons dashicons-saved" aria-hidden="true"></span></button>'
				+ '<button type="button" class="button button-small logistica-table-icon-btn logistica-etiqueta-catalog-options__btn-delete" data-select-catalog-delete aria-label="Eliminar" title="Eliminar">'
				+ '<span class="dashicons dashicons-trash" aria-hidden="true"></span></button>'
				+ '</div>';
		}

		function baseFieldDefinitions() {
			return cfg.closingBaseFieldDefinitions && typeof cfg.closingBaseFieldDefinitions === 'object'
				? cfg.closingBaseFieldDefinitions
				: {};
		}

		function baseTypeFieldsMap() {
			return cfg.closingBaseTypeFields && typeof cfg.closingBaseTypeFields === 'object'
				? cfg.closingBaseTypeFields
				: {};
		}

		function fieldsEditorHtml(tipoKey) {
			var defs = baseFieldDefinitions();
			var selected = baseTypeFieldsMap()[tipoKey] || [];
			var html = '<details class="logistica-base-type-fields" data-base-type-fields>'
				+ '<summary>Campos del tipo</summary>'
				+ '<p class="logistica-base-type-fields__note">Código, lote y ajuste siempre se muestran.</p>'
				+ '<div class="logistica-base-type-fields__grid">';
			var fieldKey;
			var meta;

			Object.keys(defs).forEach(function (key) {
				fieldKey = key;
				meta = defs[fieldKey] || {};
				html += '<label class="logistica-base-type-fields__item">'
					+ '<input type="checkbox" value="' + escapeHtml(fieldKey) + '" data-base-type-field'
					+ (selected.indexOf(fieldKey) !== -1 ? ' checked' : '')
					+ '>'
					+ '<span>' + escapeHtml(meta.label || fieldKey) + '</span>'
					+ '</label>';
			});

			html += '</div></details>';
			return html;
		}

		function collectRowFields(row) {
			var fields = [];
			if (!row) {
				return fields;
			}
			row.querySelectorAll('[data-base-type-field]:checked').forEach(function (input) {
				fields.push(String(input.value || ''));
			});
			return fields;
		}

		function applyBaseTypeFieldsPayload(payload) {
			if (payload && payload.baseTypeFields && typeof payload.baseTypeFields === 'object') {
				cfg.closingBaseTypeFields = payload.baseTypeFields;
				window.LogisticaAdmin = window.LogisticaAdmin || {};
				window.LogisticaAdmin.closingBaseTypeFields = payload.baseTypeFields;
				document.querySelectorAll('[data-close-workspace]').forEach(function (workspace) {
					if (typeof updateClosingBaseCards === 'function') {
						updateClosingBaseCards(workspace);
					}
				});
			}
		}

		function renderRows(section, entries) {
			var tbody = section.querySelector('[data-select-catalog-rows]');
			var mode = section.getAttribute('data-select-catalog-mode') || 'map';
			var caseMode = section.getAttribute('data-select-catalog-case') || '';
			var hasFields = section.getAttribute('data-select-catalog-fields') === '1';
			var countNode = section.querySelector('.logistica-etiqueta-catalog-options__count');
			var html = '';
			var i;
			var entry;
			var keyAttr;
			var realKey;
			var inputClass;

			if (!tbody || !Array.isArray(entries)) {
				return;
			}

			inputClass = 'upper' === caseMode ? 'logistica-input-uppercase' : 'logistica-input-sentence-case';

			for (i = 0; i < entries.length; i++) {
				entry = entries[i] || {};
				keyAttr = (entry.key === '' || entry.key == null) ? '__empty__' : String(entry.key);
				realKey = (entry.key === '' || entry.key == null) ? '' : String(entry.key);
				html += '<tr data-select-catalog-row data-key="' + escapeHtml(keyAttr) + '" data-can-delete="1">'
					+ '<td><input type="text" class="' + inputClass + '" value="' + escapeHtml(entry.label || '') + '" data-select-catalog-label autocomplete="off">';
				if (hasFields) {
					html += fieldsEditorHtml(realKey);
				}
				html += '</td>';
				if ('map' === mode) {
					html += '<td><span class="logistica-etiqueta-catalog-options__key-badge">'
						+ escapeHtml(entry.key === '' ? '—' : String(entry.key || ''))
						+ '</span></td>';
				}
				html += '<td>' + actionButtonsHtml() + '</td></tr>';
			}

			tbody.innerHTML = html;
			if (countNode) {
				countNode.textContent = String(entries.length);
			}
		}

		function request(action, payload) {
			var body = new FormData();
			var key;
			var i;

			body.append('action', action);
			body.append('nonce', cfg.selectCatalogNonce || '');

			for (key in payload) {
				if (!Object.prototype.hasOwnProperty.call(payload, key)) {
					continue;
				}
				if (Array.isArray(payload[key])) {
					for (i = 0; i < payload[key].length; i++) {
						body.append(key + '[]', payload[key][i]);
					}
				} else {
					body.append(key, payload[key]);
				}
			}

			return fetch(cfg.ajaxUrl || '', {
				method: 'POST',
				credentials: 'same-origin',
				body: body
			}).then(function (response) {
				return response.json();
			});
		}

		root.addEventListener('input', function (event) {
			var input = event.target;
			if (!input || !input.classList) {
				return;
			}
			if (input.classList.contains('logistica-input-uppercase') && input.value !== input.value.toUpperCase()) {
				input.value = input.value.toUpperCase();
				return;
			}
			if (input.classList.contains('logistica-input-sentence-case')) {
				var next = toSentenceCase(input.value);
				if (input.value !== next) {
					input.value = next;
				}
			}
		});

		root.addEventListener('click', function (event) {
			var saveBtn = event.target.closest('[data-select-catalog-save]');
			var deleteBtn = event.target.closest('[data-select-catalog-delete]');
			var row;
			var section;
			var catalog;
			var labelInput;
			var key;
			var payload;

			if (saveBtn) {
				row = saveBtn.closest('[data-select-catalog-row]');
				section = saveBtn.closest('[data-select-catalog-section]');
				if (!row || !section) {
					return;
				}
				catalog = section.getAttribute('data-select-catalog-section') || '';
				labelInput = row.querySelector('[data-select-catalog-label]');
				key = row.getAttribute('data-key') || '';
				payload = {
					catalog: catalog,
					key: key,
					label: labelInput ? labelInput.value : ''
				};
				if (section.getAttribute('data-select-catalog-fields') === '1') {
					payload.fields_submitted = '1';
					payload.fields = collectRowFields(row);
				}
				setStatus('Guardando…');
				request('logistica_select_catalog_update', payload).then(function (json) {
					if (!json || !json.success) {
						setStatus((json && json.data && json.data.message) || 'No se pudo guardar.', 'error');
						return;
					}
					applyBaseTypeFieldsPayload(json.data || {});
					renderRows(section, json.data.entries || []);
					setStatus(json.data.message || 'Guardado.', 'success');
				}).catch(function () {
					setStatus('Error de red al guardar.', 'error');
				});
				return;
			}

			if (deleteBtn) {
				row = deleteBtn.closest('[data-select-catalog-row]');
				section = deleteBtn.closest('[data-select-catalog-section]');
				if (!row || !section) {
					return;
				}
				catalog = section.getAttribute('data-select-catalog-section') || '';
				key = row.getAttribute('data-key') || '';
				setStatus('Comprobando uso…');
				request('logistica_select_catalog_check_usage', {
					catalog: catalog,
					key: key
				}).then(function (usageJson) {
					var usage = usageJson && usageJson.success && usageJson.data ? (usageJson.data.usage || 0) : 0;
					var confirmMessage = '¿Eliminar esta opción de la lista?';

					if (usage > 0) {
						confirmMessage = (usageJson.data.message || ('Se usa en ' + usage + ' registro(s).'))
							+ '\n\nSi la eliminas, esos registros conservarán el valor, pero ya no aparecerá en el select.\n\n¿Eliminar de todos modos?';
					}

					if (!window.confirm(confirmMessage)) {
						setStatus('');
						return null;
					}

					setStatus('Eliminando…');
					return request('logistica_select_catalog_delete', {
						catalog: catalog,
						key: key
					});
				}).then(function (json) {
					if (!json) {
						return;
					}
					if (!json.success) {
						setStatus((json.data && json.data.message) || 'No se pudo eliminar.', 'error');
						return;
					}
					applyBaseTypeFieldsPayload(json.data || {});
					renderRows(section, json.data.entries || []);
					setStatus(json.data.message || 'Eliminado.', 'success');
				}).catch(function () {
					setStatus('Error de red al eliminar.', 'error');
				});
			}
		});

		root.addEventListener('submit', function (event) {
			var form = event.target.closest('[data-select-catalog-add]');
			var section;
			var catalog;
			var labelInput;

			if (!form || !root.contains(form)) {
				return;
			}

			event.preventDefault();
			section = form.closest('[data-select-catalog-section]');
			if (!section) {
				return;
			}

			catalog = section.getAttribute('data-select-catalog-section') || '';
			labelInput = form.querySelector('[data-select-catalog-add-label]');
			setStatus('Agregando…');
			request('logistica_select_catalog_add', {
				catalog: catalog,
				label: labelInput ? labelInput.value : ''
			}).then(function (json) {
				if (!json || !json.success) {
					setStatus((json && json.data && json.data.message) || 'No se pudo agregar.', 'error');
					return;
				}
				if (labelInput) {
					labelInput.value = '';
				}
				applyBaseTypeFieldsPayload(json.data || {});
				renderRows(section, json.data.entries || []);
				setStatus(json.data.message || 'Agregado.', 'success');
			}).catch(function () {
				setStatus('Error de red al agregar.', 'error');
			});
		});
	}

	function initEtiquetaCatalogOptionsAdmin(board) {
		var root = board ? board.querySelector('[data-etiqueta-catalog-options]') : null;
		var statusNode;

		if (!root || root.dataset.etiquetaOptionsBound === '1') {
			return;
		}

		root.dataset.etiquetaOptionsBound = '1';
		statusNode = root.querySelector('[data-etiqueta-options-status]');

		root.addEventListener('input', function (event) {
			var input = event.target;
			var sentenceCase;

			if (!input || !input.classList) {
				return;
			}

			if (input.classList.contains('logistica-input-uppercase')) {
				if (input.value !== input.value.toUpperCase()) {
					input.value = input.value.toUpperCase();
				}
				return;
			}

			if (!input.classList.contains('logistica-input-sentence-case')) {
				return;
			}

			sentenceCase = toEtiquetaSentenceCase(input.value);
			if (input.value !== sentenceCase) {
				input.value = sentenceCase;
			}
		});

		function toEtiquetaSentenceCase(value) {
			var text = String(value || '');

			if (!text) {
				return '';
			}

			text = text.toLocaleLowerCase('es');
			return text.charAt(0).toLocaleUpperCase('es') + text.slice(1);
		}

		function setStatus(message, type) {
			if (!statusNode) {
				return;
			}

			statusNode.textContent = message || '';
			statusNode.classList.toggle('is-error', type === 'error');
			statusNode.classList.toggle('is-success', type === 'success');
		}

		function syncBoardTipoOptions(options) {
			setEtiquetaTipoOptions(board, options || {});
		}

		function syncBoardTamanoOptions(options) {
			setEtiquetaTamanoOptions(board, options || []);
		}

		function etiquetaOptionsActionButtons(kind) {
			return '<div class="logistica-etiqueta-catalog-options__actions">'
				+ '<button type="button" class="button button-small logistica-table-icon-btn logistica-etiqueta-catalog-options__btn-save" data-etiqueta-options-save="' + escapeHtml(kind) + '" aria-label="Guardar" title="Guardar">'
				+ '<span class="dashicons dashicons-saved" aria-hidden="true"></span></button>'
				+ '<button type="button" class="button button-small logistica-table-icon-btn logistica-etiqueta-catalog-options__btn-delete" data-etiqueta-options-delete="' + escapeHtml(kind) + '" aria-label="Eliminar" title="Eliminar">'
				+ '<span class="dashicons dashicons-trash" aria-hidden="true"></span></button>'
				+ '</div>';
		}

		function renderTipoRows(entries) {
			var tbody = root.querySelector('[data-etiqueta-options-tipo-rows]');
			var section = root.querySelector('[data-etiqueta-options-section="tipos"]');
			var countNode;
			var html = '';
			var i;
			var entry;

			if (!tbody || !Array.isArray(entries)) {
				return;
			}

			for (i = 0; i < entries.length; i++) {
				entry = entries[i];
				html += '<tr data-etiqueta-options-row="tipo" data-key="' + escapeHtml(entry.key || '') + '" data-built-in="' + (entry.built_in ? '1' : '0') + '" data-can-delete="' + (entry.can_delete ? '1' : '0') + '">'
					+ '<td><input type="text" class="logistica-input-sentence-case" value="' + escapeHtml(entry.label || '') + '" data-etiqueta-options-label autocomplete="off"></td>'
					+ '<td><input type="text" value="' + escapeHtml(entry.type_code || '') + '" data-etiqueta-options-type-code inputmode="numeric" maxlength="2" readonly class="logistica-readonly-field" title="El código EAN no se puede modificar tras crearlo."></td>'
					+ '<td>' + etiquetaOptionsActionButtons('tipo') + '</td></tr>';
			}

			tbody.innerHTML = html;
			if (section) {
				countNode = section.querySelector('.logistica-etiqueta-catalog-options__count');
				if (countNode) {
					countNode.textContent = String(entries.length);
				}
			}
		}

		function renderTamanoRows(options) {
			var tbody = root.querySelector('[data-etiqueta-options-tamano-rows]');
			var section = root.querySelector('[data-etiqueta-options-section="tamanos"]');
			var countNode;
			var html = '';
			var i;
			var entry;
			var label;
			var canDelete;

			if (!tbody || !Array.isArray(options)) {
				return;
			}

			for (i = 0; i < options.length; i++) {
				entry = options[i];
				label = typeof entry === 'string' ? entry : (entry.label || '');
				canDelete = typeof entry === 'object' && entry ? !!entry.can_delete : true;
				html += '<tr data-etiqueta-options-row="tamano" data-key="' + escapeHtml(label) + '" data-can-delete="' + (canDelete ? '1' : '0') + '">'
					+ '<td><input type="text" class="logistica-input-uppercase" value="' + escapeHtml(label) + '" data-etiqueta-options-label autocomplete="off"></td>'
					+ '<td>' + etiquetaOptionsActionButtons('tamano') + '</td></tr>';
			}

			tbody.innerHTML = html;
			if (section) {
				countNode = section.querySelector('.logistica-etiqueta-catalog-options__count');
				if (countNode) {
					countNode.textContent = String(options.length);
				}
			}
		}

		function renderMercadoRows(entries) {
			var tbody = root.querySelector('[data-etiqueta-options-mercado-rows]');
			var section = root.querySelector('[data-etiqueta-options-section="mercados"]');
			var countNode;
			var html = '';
			var i;
			var entry;

			if (!tbody || !Array.isArray(entries)) {
				return;
			}

			for (i = 0; i < entries.length; i++) {
				entry = entries[i];
				html += '<tr data-etiqueta-options-row="mercado" data-key="' + escapeHtml(entry.key || '') + '" data-built-in="' + (entry.built_in ? '1' : '0') + '" data-can-delete="' + (entry.can_delete ? '1' : '0') + '">'
					+ '<td><span class="logistica-etiqueta-catalog-options__key-badge">' + escapeHtml(entry.key || '') + '</span></td>'
					+ '<td><input type="text" class="logistica-input-sentence-case" value="' + escapeHtml(entry.label || '') + '" data-etiqueta-options-label autocomplete="off"></td>'
					+ '<td><input type="text" value="' + escapeHtml(entry.market_digit || '') + '" data-etiqueta-options-market-digit inputmode="numeric" maxlength="1" readonly class="logistica-readonly-field" title="El dígito EAN no se puede modificar tras crearlo."></td>'
					+ '<td>' + etiquetaOptionsActionButtons('mercado') + '</td></tr>';
			}

			tbody.innerHTML = html;
			if (section) {
				countNode = section.querySelector('.logistica-etiqueta-catalog-options__count');
				if (countNode) {
					countNode.textContent = String(entries.length);
				}
			}
		}

		function kindLabel(kind) {
			if ('tipo' === kind) {
				return 'Tipo';
			}
			if ('mercado' === kind) {
				return 'Mercado';
			}
			if ('tamano' === kind) {
				return 'Tamaño';
			}

			return kind || '';
		}

		function syncHiddenEntriesFromPayload(data) {
			if (data && Array.isArray(data.hidden_entries)) {
				renderHiddenRows(data.hidden_entries);
			}
		}

		function renderHiddenRows(entries) {
			var tbody = root.querySelector('[data-etiqueta-options-hidden-rows]');
			var section = root.querySelector('[data-etiqueta-options-section="ocultos"]');
			var emptyNode = root.querySelector('[data-etiqueta-options-hidden-empty]');
			var wrap = root.querySelector('[data-etiqueta-options-hidden-wrap]');
			var countNode = root.querySelector('[data-etiqueta-options-hidden-count]');
			var html = '';
			var i;
			var entry;
			var nameCell;
			var usageCell;

			if (!tbody || !Array.isArray(entries)) {
				return;
			}

			for (i = 0; i < entries.length; i++) {
				entry = entries[i];
				nameCell = 'mercado' === entry.kind
					? escapeHtml(entry.key || '') + ' · ' + escapeHtml(entry.label || '')
					: escapeHtml(entry.label || entry.key || '');
				usageCell = entry.usage > 0 ? escapeHtml(String(entry.usage)) : '—';
				html += '<tr data-etiqueta-options-row="' + escapeHtml(entry.kind || '') + '" data-key="' + escapeHtml(entry.key || '') + '">'
					+ '<td><span class="logistica-etiqueta-catalog-options__kind-badge">' + escapeHtml(kindLabel(entry.kind)) + '</span></td>'
					+ '<td>' + nameCell + '</td>'
					+ '<td>' + escapeHtml(entry.meta || '') + '</td>'
					+ '<td>' + usageCell + '</td>'
					+ '<td><button type="button" class="button button-small logistica-table-icon-btn logistica-etiqueta-catalog-options__btn-restore" data-etiqueta-options-restore="' + escapeHtml(entry.kind || '') + '" aria-label="Restaurar" title="Restaurar">'
					+ '<span class="dashicons dashicons-undo" aria-hidden="true"></span></button></td></tr>';
			}

			tbody.innerHTML = html;
			if (countNode) {
				countNode.textContent = String(entries.length);
			}
			if (emptyNode) {
				emptyNode.hidden = entries.length > 0;
			}
			if (wrap) {
				wrap.hidden = entries.length === 0;
			}
			if (section) {
				section.hidden = false;
			}
		}

		function confirmEtiquetaOptionDelete(kind, key) {
			return etiquetaCatalogAjax('logistica_check_etiqueta_option_usage', {
				kind: kind,
				key: key
			}).then(function (payload) {
				var usage = payload && payload.success && payload.data ? (payload.data.usage || 0) : 0;
				var confirmMessage = '¿Ocultar esta opción de la lista?';

				if (usage > 0) {
					confirmMessage = (payload.data.message || ('Se usa en ' + usage + ' producto(s) del catálogo.')) + '\n\n¿Ocultarla de todos modos?';
				}

				return window.confirm(confirmMessage);
			}).catch(function () {
				return window.confirm('¿Ocultar esta opción de la lista?');
			});
		}

		function applyEtiquetaOptionsMutation(kind, data) {
			if ('tipo' === kind) {
				syncBoardTipoOptions(data.options || {});
				renderTipoRows(data.entries || []);
			} else if ('mercado' === kind) {
				renderMercadoRows(data.entries || []);
			} else if ('tamano' === kind) {
				syncBoardTamanoOptions(data.options || []);
				renderTamanoRows(data.entries || data.options || []);
			}

			syncHiddenEntriesFromPayload(data);
		}

		root.addEventListener('click', function (event) {
			var saveBtn = event.target.closest('[data-etiqueta-options-save]');
			var deleteBtn = event.target.closest('[data-etiqueta-options-delete]');
			var restoreBtn = event.target.closest('[data-etiqueta-options-restore]');
			var row;
			var kind;
			var labelInput;
			var codeInput;
			var digitInput;
			var label;
			var typeCode;
			var marketDigit;
			var key;
			var action;
			var ajaxParams;

			if (saveBtn) {
				event.preventDefault();
				row = saveBtn.closest('[data-etiqueta-options-row]');
				kind = saveBtn.getAttribute('data-etiqueta-options-save');
				if (!row || !kind) {
					return;
				}

				labelInput = row.querySelector('[data-etiqueta-options-label]');
				label = labelInput ? labelInput.value.trim() : '';
				key = row.getAttribute('data-key') || '';

				if (!label) {
					setStatus('Indica el valor.', 'error');
					return;
				}

				ajaxParams = {
					key: key,
					label: label
				};

				if ('tipo' === kind) {
					codeInput = row.querySelector('[data-etiqueta-options-type-code]');
					typeCode = codeInput ? codeInput.value.trim() : '';
					action = 'logistica_update_etiqueta_tipo_producto';
					ajaxParams.type_code = typeCode;
				} else if ('tamano' === kind) {
					action = 'logistica_update_etiqueta_tamano';
				} else if ('mercado' === kind) {
					digitInput = row.querySelector('[data-etiqueta-options-market-digit]');
					marketDigit = digitInput ? digitInput.value.trim() : '';
					action = 'logistica_update_etiqueta_mercado';
					ajaxParams.market_digit = marketDigit;
				} else {
					return;
				}

				saveBtn.disabled = true;
				setStatus('Guardando…', '');

				etiquetaCatalogAjax(action, ajaxParams).then(function (payload) {
					saveBtn.disabled = false;

					if (!payload || !payload.success || !payload.data) {
						setStatus((payload && payload.data && payload.data.message) || 'No se pudo guardar.', 'error');
						return;
					}

					if ('tipo' === kind) {
						syncBoardTipoOptions(payload.data.options || {});
						renderTipoRows(payload.data.entries || []);
					} else if ('tamano' === kind) {
						syncBoardTamanoOptions(payload.data.options || []);
						renderTamanoRows(payload.data.entries || payload.data.options || []);
						row.setAttribute('data-key', payload.data.label || label);
					} else if ('mercado' === kind) {
						renderMercadoRows(payload.data.entries || []);
					}

					setStatus(payload.data.message || 'Guardado.', 'success');
				});
				return;
			}

			if (deleteBtn) {
				event.preventDefault();
				row = deleteBtn.closest('[data-etiqueta-options-row]');
				kind = deleteBtn.getAttribute('data-etiqueta-options-delete');
				key = row ? row.getAttribute('data-key') || '' : '';

				if (!row || !kind || !key) {
					return;
				}

				confirmEtiquetaOptionDelete(kind, key).then(function (confirmed) {
					if (!confirmed) {
						return;
					}

					action = 'tipo' === kind ? 'logistica_delete_etiqueta_tipo_producto' : ('mercado' === kind ? 'logistica_delete_etiqueta_mercado' : 'logistica_delete_etiqueta_tamano');
					deleteBtn.disabled = true;
					setStatus('Ocultando…', '');

					etiquetaCatalogAjax(action, { key: key }).then(function (payload) {
						deleteBtn.disabled = false;

						if (!payload || !payload.success || !payload.data) {
							setStatus((payload && payload.data && payload.data.message) || 'No se pudo ocultar.', 'error');
							return;
						}

						applyEtiquetaOptionsMutation(kind, payload.data);
						setStatus(payload.data.message || 'Opción oculta.', 'success');
					});
				});
				return;
			}

			if (restoreBtn) {
				event.preventDefault();
				row = restoreBtn.closest('[data-etiqueta-options-row]');
				kind = restoreBtn.getAttribute('data-etiqueta-options-restore');
				key = row ? row.getAttribute('data-key') || '' : '';

				if (!row || !kind || !key) {
					return;
				}

				action = 'tipo' === kind ? 'logistica_restore_etiqueta_tipo_producto' : ('mercado' === kind ? 'logistica_restore_etiqueta_mercado' : 'logistica_restore_etiqueta_tamano');
				restoreBtn.disabled = true;
				setStatus('Restaurando…', '');

				etiquetaCatalogAjax(action, { key: key }).then(function (payload) {
					restoreBtn.disabled = false;

					if (!payload || !payload.success || !payload.data) {
						setStatus((payload && payload.data && payload.data.message) || 'No se pudo restaurar.', 'error');
						return;
					}

					applyEtiquetaOptionsMutation(kind, payload.data);
					setStatus(payload.data.message || 'Opción restaurada.', 'success');
				});
			}
		});

		root.addEventListener('submit', function (event) {
			var form = event.target.closest('[data-etiqueta-options-add]');
			var kind;
			var labelInput;
			var keyInput;
			var codeInput;
			var digitInput;
			var label;
			var keyValue;
			var typeCode;
			var marketDigit;
			var action;
			var submitBtn;
			var ajaxParams;

			if (!form) {
				return;
			}

			event.preventDefault();
			kind = form.getAttribute('data-etiqueta-options-add');
			labelInput = form.querySelector('[data-etiqueta-options-add-label]');
			keyInput = form.querySelector('[data-etiqueta-options-add-key]');
			codeInput = form.querySelector('[data-etiqueta-options-add-type-code]');
			digitInput = form.querySelector('[data-etiqueta-options-add-market-digit]');
			label = labelInput ? labelInput.value.trim() : '';
			keyValue = keyInput ? keyInput.value.trim() : '';
			typeCode = codeInput ? codeInput.value.trim() : '';
			marketDigit = digitInput ? digitInput.value.trim() : '';
			submitBtn = form.querySelector('[type="submit"]');

			if ('mercado' === kind) {
				if (!keyValue || !label) {
					setStatus('Indica la clave y la etiqueta del mercado.', 'error');
					return;
				}
				action = 'logistica_save_etiqueta_mercado';
				ajaxParams = {
					key: keyValue,
					label: label,
					market_digit: marketDigit
				};
			} else if (!label) {
				setStatus('Indica el valor.', 'error');
				return;
			} else {
				action = 'tipo' === kind ? 'logistica_save_etiqueta_tipo_producto' : 'logistica_save_etiqueta_tamano';
				ajaxParams = {
					label: label,
					type_code: typeCode
				};
			}

			if (submitBtn) {
				submitBtn.disabled = true;
			}
			setStatus('Guardando…', '');

			etiquetaCatalogAjax(action, ajaxParams).then(function (payload) {
				if (submitBtn) {
					submitBtn.disabled = false;
				}

				if (!payload || !payload.success || !payload.data) {
					setStatus((payload && payload.data && payload.data.message) || 'No se pudo guardar.', 'error');
					return;
				}

				if ('tipo' === kind) {
					syncBoardTipoOptions(payload.data.options || {});
					renderTipoRows(payload.data.entries || []);
				} else if ('mercado' === kind) {
					renderMercadoRows(payload.data.entries || []);
				} else {
					syncBoardTamanoOptions(payload.data.options || []);
					renderTamanoRows(payload.data.entries || payload.data.options || []);
				}

				if (labelInput) {
					labelInput.value = '';
				}
				if (keyInput) {
					keyInput.value = '';
				}
				if (codeInput) {
					codeInput.value = '';
				}
				if (digitInput) {
					digitInput.value = '';
				}

				setStatus(payload.data.message || 'Guardado.', 'success');
			});
		});
	}

	function initEtiquetaCatalogBoard() {
		var board = document.querySelector('[data-etiqueta-catalog-board]');

		if (!board) {
			return;
		}

		hideEtiquetaCatalogTabLoading(board);

		if (board.dataset.etiquetaAdminSettingsOnly === '1') {
			if (board.dataset.etiquetaCatalogCoreBound !== '1') {
				initLabelPrintSettingsForm(board);
				initLabelPrintOptionsForm(board);
				initEtiquetaCatalogOptionsAdmin(board);
				board.dataset.etiquetaCatalogCoreBound = '1';
			}
			hideEtiquetaCatalogTabLoading(board);
			return;
		}

		bindEtiquetaCatalogRowActions();

		if (board.dataset.etiquetaCatalogCoreBound !== '1') {
			try {
				initLabelPrintModal();
				initEtiquetaTamanoSelects(board);
				initEtiquetaTipoSelects(board);
				initEtiquetaMercadoSelects(board);
				initEtiquetaEditModal(board);
				initEtiquetaCatalogHiddenRowHints(board);
				initEtiquetaNewForm(board);
				bindEtiquetaReferenciaFields(board);
				initLabelPrintSettingsForm(board);
				initLabelPrintOptionsForm(board);

				if (board.querySelector('[data-excel-zoom-root]')) {
					initExcelTableZoom(board.querySelector('[data-excel-zoom-root]'));
				}

				board.dataset.etiquetaCatalogCoreBound = '1';

				var legacyEditId = new URL(window.location.href).searchParams.get('edit_id');
				if (legacyEditId) {
					openEtiquetaEditModal(board, legacyEditId);
					stripEtiquetaEditUrl();
				}
			} catch (error) {
				// Permite reintentar la inicialización en la siguiente visita a la pestaña.
			}
		}

		initEtiquetaCatalogFilters(board);
		refreshEtiquetaCatalogDuplicateState(board);
		updateRotuloPrintSizeHint();
		hideEtiquetaCatalogTabLoading(board);
	}

	function initLogisticaTabs() {
		var app = document.querySelector('[data-logistica-app]');

		if (!app) {
			return;
		}

		initInventoryNav();

		var wrapper = app.querySelector('[data-logistica-tabs]');

		if (!wrapper) {
			return;
		}

		var context = wrapper.getAttribute('data-logistica-tabs') || 'frontend';
		var links = Array.from(wrapper.querySelectorAll('[data-logistica-tab]'));
		var panels = Array.from(app.querySelectorAll('[data-tab-panel]'));

		if (!links.length || !panels.length) {
			return;
		}

		function syncFrontendPanelHeader(tab) {
			var header = app.querySelector('[data-logistica-panel-header]');
			var userBar = app.querySelector('[data-logistica-header-user-bar]');
			var hideUserBar = tab === 'logistica' || tab === 'logistica-perfil';

			// Tarjeta Admin / Ver perfil: no mostrar en Inicio ni en Mi perfil.
			if (userBar) {
				userBar.hidden = hideUserBar;
				userBar.setAttribute('aria-hidden', hideUserBar ? 'true' : 'false');
			}

			if (header && !userBar) {
				// Frontend sin barra de usuario: el encabezado solo acompaña Inicio.
				header.hidden = 'logistica' !== tab;
			} else if (header && userBar) {
				// Con barra: el header puede quedarse para el título; la tarjeta se oculta aparte.
				header.hidden = false;
			}
		}

		function tabLoadingMessage(tab) {
			var messages = {
				'logistica-conversiones': 'Cargando calculadoras…',
				'logistica-calculo-tapas': 'Cargando cálculo de tapas…',
				'logistica-calculos': 'Cargando cálculos de bolsas…',
				'logistica-inventario-bodega': 'Cargando inventario de bodega…',
				'logistica-cierre-ordenes': 'Cargando cierre de órdenes…',
				'logistica-planeacion-impresion': 'Cargando planeación de impresión…',
				'logistica-planeacion-formado': 'Cargando planeación de formado…',
				'logistica-cierre-caracteristicas': 'Cargando características…',
				'logistica-codigos-etiqueta': 'Cargando códigos de etiqueta…',
				'logistica-revision-minuta': 'Cargando revisión de minutas…',
				'logistica-rotulos-rollos': 'Cargando rótulos de rollos…',
				'logistica-cuadro-pedidos': 'Cargando cuadro de pedidos…'
			};

			return messages[tab] || 'Cargando…';
		}

		function tabUsesSyncRender(tab) {
			return 'logistica-codigos-etiqueta' === tab || 'logistica-rotulos-rollos' === tab;
		}

		function finalizeTabPanelLoading(panel, tab) {
			if (!panel || 'logistica' === tab || !window.LogisticaTabLoading) {
				return;
			}

			if ('logistica-cierre-ordenes' === tab) {
				return;
			}

			panel.dataset.tabLoaded = '1';
			window.LogisticaTabLoading.hide(panel);
		}

		function bootModulesInPanel(panel, tab) {
			if (!panel) {
				return;
			}

			if ('logistica-inventario-bodega' === tab) {
				initWarehouseInventory();
			}

			if ('logistica-conversiones' === tab || 'logistica-calculo-tapas' === tab || 'logistica-calculos' === tab) {
				if (typeof restoreConversionState === 'function') {
					restoreConversionState();
				}
				if (typeof initConversionNav === 'function') {
					initConversionNav();
				}
				if (typeof initConversionExcelScroll === 'function') {
					initConversionExcelScroll();
				}
				if (typeof initConversionTemplates === 'function') {
					initConversionTemplates();
				}
				panel.querySelectorAll('.logistica-sheet[data-sheet]').forEach(function (sheet) {
					if (typeof initExcelTableZoom === 'function') {
						initExcelTableZoom(sheet);
					}
					if (sheet.getAttribute('data-sheet') === 'tapas-poliestireno') {
						if (typeof initTapasCards === 'function') {
							initTapasCards(sheet);
						}
						if (typeof initTapasExcelScroll === 'function') {
							initTapasExcelScroll(sheet);
						}
						if (typeof refreshTapasFilterOptions === 'function') {
							refreshTapasFilterOptions(sheet);
						}
						if (typeof bindTablePagination === 'function' && typeof applyTapasFilters === 'function') {
							bindTablePagination(sheet, 'tapas', function (options) {
								applyTapasFilters(sheet, options);
							});
							applyTapasFilters(sheet);
						}
					}
				});
				if (typeof initTapasTotalsCollapsibles === 'function') {
					initTapasTotalsCollapsibles(panel);
				}
				if (typeof initMoneyFields === 'function') {
					initMoneyFields(panel);
				}
			}

			if ('logistica-planeacion-impresion' === tab && window.LogisticaPlaneacionImpresion && typeof window.LogisticaPlaneacionImpresion.init === 'function') {
				window.LogisticaPlaneacionImpresion.init();
			}

			if ('logistica-planeacion-formado' === tab && window.LogisticaPlaneacionFormado && typeof window.LogisticaPlaneacionFormado.init === 'function') {
				window.LogisticaPlaneacionFormado.init();
			}

			if (('logistica-configuracion' === tab || panel.querySelector('[data-lifecycle-board]')) && window.LogisticaLifecycleTables && typeof window.LogisticaLifecycleTables.init === 'function') {
				window.LogisticaLifecycleTables.init();
			}

			if (
				panel.querySelector('[data-config-section-board]') &&
				window.LogisticaConfigNav &&
				typeof window.LogisticaConfigNav.init === 'function'
			) {
				window.LogisticaConfigNav.init(panel);
			}

			document.dispatchEvent(new CustomEvent('logistica:tab-loaded', {
				detail: { tab: tab, panel: panel }
			}));
		}

		function loadTabPanelContent(panel, tab) {
			var cfg = window.LogisticaAdmin || {};
			var body;
			var panelContext;

			if (!panel) {
				return Promise.resolve(null);
			}

			if (panel.dataset.tabLazy !== '1' || panel.dataset.tabContentLoaded === '1') {
				return Promise.resolve(panel);
			}

			if (panel._tabLoadPromise) {
				return panel._tabLoadPromise;
			}

			panelContext = app.getAttribute('data-logistica-panel-context') || context || 'operations';
			if (panelContext === 'frontend') {
				panelContext = 'operations';
			}

			if (window.LogisticaTabLoading) {
				window.LogisticaTabLoading.show(panel, tabLoadingMessage(tab), true);
			}

			body = new FormData();
			body.append('action', 'logistica_render_tab_panel');
			body.append('nonce', cfg.tabPanelNonce || '');
			body.append('tab', tab);
			body.append('context', panelContext);

			panel._tabLoadPromise = fetch(cfg.ajaxUrl || '', {
				method: 'POST',
				credentials: 'same-origin',
				body: body
			}).then(function (response) {
				return response.json();
			}).then(function (json) {
				var overlay;
				var html;

				if (!json || !json.success || !json.data || !json.data.html) {
					throw new Error((json && json.data && json.data.message) || 'No se pudo cargar la pestaña.');
				}

				html = String(json.data.html);
				overlay = panel.querySelector('[data-logistica-tab-loading]');
				panel.innerHTML = '';
				if (overlay) {
					panel.appendChild(overlay);
				} else if ('logistica' !== tab && window.LogisticaTabLoading) {
					// Keep a loading host if needed.
				}
				panel.insertAdjacentHTML('beforeend', html);
				panel.dataset.tabContentLoaded = '1';
				panel.dataset.tabLazy = '0';
				panel._tabLoadPromise = null;
				return panel;
			}).catch(function (error) {
				panel._tabLoadPromise = null;
				if (window.LogisticaTabLoading) {
					window.LogisticaTabLoading.hide(panel);
				}
				window.alert(error && error.message ? error.message : 'Error al cargar la pestaña.');
				throw error;
			});

			return panel._tabLoadPromise;
		}

		function runTabPanelInits(panel, tab, opts) {
			opts = opts || {};

			if (!panel) {
				return;
			}

			if (('operations' === context || 'config' === context || 'frontend' === context) && 'logistica' !== tab && !tabUsesSyncRender(tab) && panel.dataset.tabLoaded !== '1' && window.LogisticaTabLoading) {
				window.LogisticaTabLoading.show(panel, tabLoadingMessage(tab), true);
			}

			if ('logistica-codigos-etiqueta' === tab) {
				panel.dataset.tabLoaded = '1';
				hideEtiquetaCatalogTabLoading(panel);
			}

			if ('logistica-rotulos-rollos' === tab) {
				panel.dataset.tabLoaded = '1';
				if (window.LogisticaTabLoading) {
					window.LogisticaTabLoading.hide(panel);
				}
			}

			if ('logistica-revision-minuta' === tab) {
				panel.dataset.tabLoaded = '1';
				hideMinutasTabLoading(panel);
			}

			var workspace = panel.querySelector('[data-close-workspace]');

			if (workspace && typeof scheduleSaveClosingUiState === 'function') {
				scheduleSaveClosingUiState(workspace);
			}

			if ('logistica-cierre-ordenes' === tab) {
				initClosingWorkspacesIfNeeded(true);
				// Loading overlay is cleared in revealClosingWorkspace after init finishes.
				if (typeof syncClosingLotesFromWarehouse === 'function') {
					syncClosingLotesFromWarehouse();
				}
				if (typeof syncClosingCharacteristicsFromCatalog === 'function') {
					syncClosingCharacteristicsFromCatalog();
				}
			}

			if ('logistica-codigos-etiqueta' === tab) {
				window.requestAnimationFrame(function () {
					initEtiquetaCatalogBoard();
					initSelectCatalogOptionsAdmin();
				});
			}

			if ('logistica-rotulos-rollos' === tab) {
				window.requestAnimationFrame(function () {
					initRotulosRollosBoard();
					initRotuloPrintSettingsForms();
					initSelectCatalogOptionsAdmin();
				});
			}

			if ('logistica-revision-minuta' === tab) {
				window.requestAnimationFrame(function () {
					initMinutasBoard();
				});
			}

			if ('logistica-cuadro-pedidos' === tab && typeof window.initLogisticaPedidosBoard === 'function') {
				window.requestAnimationFrame(function () {
					window.initLogisticaPedidosBoard();
				});
			}

			refreshSheetsInPanel(panel);
			bootModulesInPanel(panel, tab);

			if ('logistica-cierre-caracteristicas' === tab) {
				window.requestAnimationFrame(function () {
					var carBoard = panel ? panel.querySelector('[data-cierre-caracteristicas-board]') : null;
					if (carBoard) {
						initCierreCaracteristicasBoard(carBoard);
					} else {
						initCierreCaracteristicas();
					}
				});
			}

			if ('logistica' === tab && typeof window.initLogisticaUserPanel === 'function') {
				window.initLogisticaUserPanel(panel);
			}

			if ('logistica' === tab && window.LogisticaUserPanelNav && typeof window.LogisticaUserPanelNav.init === 'function') {
				window.LogisticaUserPanelNav.init();
			}

			window.requestAnimationFrame(function () {
				initSelectCatalogOptionsAdmin(panel ? panel.querySelector('[data-select-catalog-options]') : null);
				if (!panel) {
					initSelectCatalogOptionsAdmin();
				}
			});

			if (('operations' === context || 'config' === context || 'frontend' === context) && 'logistica' !== tab) {
				window.requestAnimationFrame(function () {
					window.requestAnimationFrame(function () {
						finalizeTabPanelLoading(panel, tab);
					});
				});
			}
		}

		function activate(tab, options) {
			var opts = options || {};
			var panel = null;

			if (logisticaResumeTracker && typeof logisticaResumeTracker.switchTab === 'function') {
				logisticaResumeTracker.switchTab(tab, { skipRecord: !!opts.skipHistory });
			}

			if ('frontend' === context || 'operations' === context) {
				syncFrontendPanelHeader(tab);
			}

			links.forEach(function (link) {
				var active = link.getAttribute('data-logistica-tab') === tab;

				link.classList.toggle('nav-tab-active', active);
				if (active) {
					link.setAttribute('aria-current', 'page');
				} else {
					link.removeAttribute('aria-current');
				}
			});

			panels.forEach(function (section) {
				var active = section.getAttribute('data-tab-panel') === tab;

				section.hidden = !active;
				section.classList.toggle('is-active', active);
				if (active) {
					panel = section;
				}
			});

			closeAllSectionNavs(app);
			syncAppRailSectionButton(app);
			syncAppRailContextPins(app, tab);
			updateSectionNavBackdrop(app);

			if ('admin' !== context && window.history && window.history.replaceState && !opts.skipHistory) {
				var url = new URL(window.location.href);

				url.searchParams.set('tab', tab);
				window.history.replaceState(null, '', url.toString());
			}

			if (panel) {
				loadTabPanelContent(panel, tab).then(function (loadedPanel) {
					runTabPanelInits(loadedPanel || panel, tab, opts);
				}).catch(function () {
					// Error already surfaced in loadTabPanelContent.
				});
			}

			if (opts.scrollTop) {
				window.scrollTo({ top: 0, behavior: 'smooth' });
			}

			syncInventoryNavHeight();
		}

		function resolveInitialTab() {
			var params = new URLSearchParams(window.location.search);
			var hasTabParam = params.has('tab');
			var tab = hasTabParam ? (params.get('tab') || '') : (app.getAttribute('data-logistica-initial-tab') || 'logistica');
			var hasPanel = !!app.querySelector('[data-tab-panel="' + tab + '"]');

			// Permitir pestañas ocultas del menú (p. ej. Mi perfil) si el panel existe.
			if (!hasPanel) {
				tab = app.querySelector('[data-tab-panel="logistica"]')
					? 'logistica'
					: (links.length ? links[0].getAttribute('data-logistica-tab') : 'logistica');
			}

			return tab;
		}

		if ('admin' !== context) {
			wrapper.addEventListener('click', function (event) {
				var link = event.target.closest('[data-logistica-tab]');
				var activePanel;
				var closingWorkspace;

				if (!link || !wrapper.contains(link)) {
					return;
				}

				activePanel = app.querySelector('.logistica-tab-panel.is-active');
				if (activePanel) {
					closingWorkspace = activePanel.querySelector('[data-close-workspace]');
					if (closingWorkspace) {
						flushClosingWorkspaceSave(closingWorkspace);
					}
				}

				event.preventDefault();
				setInventoryNavCollapsed(app, true);
				activate(link.getAttribute('data-logistica-tab'), { scrollTop: true });
			});

			app.addEventListener('click', function (event) {
				var profileBtn = event.target.closest('[data-logistica-open-profile]');
				var profileTab;

				if (!profileBtn || !app.contains(profileBtn)) {
					return;
				}

				event.preventDefault();
				profileTab = profileBtn.getAttribute('data-profile-tab') || 'logistica-perfil';
				setInventoryNavCollapsed(app, true);
				activate(profileTab, { scrollTop: true });
			});

			activate(resolveInitialTab(), { skipHistory: true });
			return;
		}
	}

	function initFrontNav() {
		if (document.querySelector('[data-logistica-app]')) {
			return;
		}

		var nav = document.querySelector('.logistica-front-nav');

		if (!nav) {
			return;
		}

		var wrap = nav.closest('.logistica-front-nav-wrap');
		var links = Array.from(nav.querySelectorAll('.logistica-nav-link[data-section]'));
		var sections = links.map(function (link) {
			return document.getElementById(link.getAttribute('data-section'));
		}).filter(Boolean);
		var scrollTicking = false;

		function syncNavHeight() {
			var height = Math.ceil(nav.getBoundingClientRect().height);

			document.documentElement.style.setProperty('--logistica-nav-height', height + 'px');

			if (wrap) {
				wrap.style.minHeight = height + 'px';
			}
		}

		function navScrollOffset() {
			var height = Number.parseFloat(
				window.getComputedStyle(document.documentElement).getPropertyValue('--logistica-nav-height')
			);

			return (Number.isFinite(height) && height > 0 ? height : nav.getBoundingClientRect().height) + 12;
		}

		function setActiveSection(sectionId) {
			var activeLink = null;

			links.forEach(function (link) {
				var active = link.getAttribute('data-section') === sectionId;

				link.classList.toggle('is-active', active);
				if (active) {
					link.setAttribute('aria-current', 'true');
					activeLink = link;
				} else {
					link.removeAttribute('aria-current');
				}
			});

			if (activeLink && nav.scrollWidth > nav.clientWidth) {
				activeLink.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'center' });
			}
		}

		function sectionDocumentTop(section) {
			return section.getBoundingClientRect().top + window.scrollY;
		}

		function resolveActiveSection() {
			if (!sections.length) {
				return '';
			}

			var marker = window.scrollY + navScrollOffset() + 24;
			var current = sections[0].id;

			sections.forEach(function (section) {
				if (sectionDocumentTop(section) <= marker) {
					current = section.id;
				}
			});

			return current;
		}

		function updateActiveFromScroll() {
			setActiveSection(resolveActiveSection());
			nav.classList.toggle('is-stuck', document.body.classList.contains('logistica-panel-page') || nav.getBoundingClientRect().top <= 1);
		}

		function scrollToSection(target) {
			var top = target.getBoundingClientRect().top + window.scrollY - navScrollOffset();

			window.scrollTo({ top: Math.max(0, top), behavior: 'smooth' });
		}

		nav.addEventListener('click', function (event) {
			var link = event.target.closest('.logistica-nav-link[href^="#"]');

			if (!link) {
				return;
			}

			var target = document.querySelector(link.getAttribute('href'));

			if (target) {
				var workspace = document.querySelector('[data-close-workspace]');

				event.preventDefault();
				setActiveSection(link.getAttribute('data-section'));
				scrollToSection(target);
				if (window.history && window.history.replaceState) {
					window.history.replaceState(null, '', link.getAttribute('href'));
				}

				if (workspace) {
					scheduleSaveClosingUiState(workspace);
				}
			}
		});

		window.addEventListener('scroll', function () {
			if (scrollTicking) {
				return;
			}

			scrollTicking = true;
			window.requestAnimationFrame(function () {
				updateActiveFromScroll();
				scrollTicking = false;
			});
		}, { passive: true });

		window.addEventListener('resize', function () {
			syncNavHeight();
			updateActiveFromScroll();
		}, { passive: true });

		syncNavHeight();

		if (window.location.hash) {
			var hashTarget = document.querySelector(window.location.hash);

			if (hashTarget) {
				window.requestAnimationFrame(function () {
					scrollToSection(hashTarget);
					setActiveSection(window.location.hash.replace('#', ''));
				});
			}
		} else {
			var savedUi = readClosingUiState();

			if (savedUi && savedUi.hash) {
				var savedTarget = document.querySelector(savedUi.hash);

				if (savedTarget) {
					window.requestAnimationFrame(function () {
						scrollToSection(savedTarget);
						setActiveSection(savedUi.hash.replace('#', ''));
					});
				}
			}
		}

		updateActiveFromScroll();
	}

	function applyCierreCarSort(board, key, dir, activeHeader, refreshFilters) {
		var tbody;
		var table;
		var headers;
		var rows;
		var colIndex;

		if (!board || !key) {
			return;
		}

		tbody = board.querySelector('[data-cierre-car-rows]');
		table = board.querySelector('.logistica-cierre-caracteristicas-table');
		headers = table ? table.querySelectorAll('thead th.logistica-cierre-car-sortable') : [];
		rows = tbody ? Array.prototype.slice.call(tbody.querySelectorAll('[data-cierre-car-row]')) : [];

		headers.forEach(function (header) {
			var indicator = header.querySelector('.logistica-cierre-car-sort-indicator');
			header.classList.remove('logistica-cierre-car-sort-active');
			setTableSortIndicator(indicator, null);
		});

		colIndex = -1;
		if (activeHeader) {
			activeHeader.classList.add('logistica-cierre-car-sort-active');
			colIndex = Array.prototype.indexOf.call(activeHeader.parentNode.children, activeHeader);
			setTableSortIndicator(activeHeader.querySelector('.logistica-cierre-car-sort-indicator'), dir);
		} else {
			headers.forEach(function (header, index) {
				if (header.getAttribute('data-cierre-car-sort') === key) {
					colIndex = index;
					header.classList.add('logistica-cierre-car-sort-active');
					setTableSortIndicator(header.querySelector('.logistica-cierre-car-sort-indicator'), dir);
				}
			});
		}

		rows.sort(function (rowA, rowB) {
			var valueA = rowA.getAttribute('data-cierre-car-sort-' + key) || '';
			var valueB = rowB.getAttribute('data-cierre-car-sort-' + key) || '';

			if (!valueA && colIndex >= 0) {
				valueA = rowA.children[colIndex] ? rowA.children[colIndex].textContent.trim() : '';
			}

			if (!valueB && colIndex >= 0) {
				valueB = rowB.children[colIndex] ? rowB.children[colIndex].textContent.trim() : '';
			}

			return compareWarehouseSortValues(valueA, valueB, dir);
		});

		rows.forEach(function (row) {
			tbody.appendChild(row);
		});

		board._cierreCarSortState = { key: key, dir: dir };

		if (typeof refreshFilters === 'function') {
			refreshFilters();
		}
	}

	function initCierreCarSort(board, refreshFilters) {
		var table;
		var headers;
		var sortState;

		if (!board || board.dataset.cierreCarSortBound === '1') {
			return;
		}

		table = board.querySelector('.logistica-cierre-caracteristicas-table');
		headers = table ? table.querySelectorAll('thead th.logistica-cierre-car-sortable') : [];
		sortState = board._cierreCarSortState || { key: null, dir: 1 };

		headers.forEach(function (header) {
			function toggleCierreCarSort() {
				var key = header.getAttribute('data-cierre-car-sort') || '';
				var dir;

				if (!key) {
					return;
				}

				dir = sortState.key === key ? sortState.dir * -1 : 1;
				sortState.key = key;
				sortState.dir = dir;
				applyCierreCarSort(board, key, dir, header, refreshFilters);
			}

			header.setAttribute('role', 'button');
			header.setAttribute('tabindex', '0');
			header.addEventListener('click', toggleCierreCarSort);
			header.addEventListener('keydown', function (event) {
				if (event.key === 'Enter' || event.key === ' ') {
					event.preventDefault();
					toggleCierreCarSort();
				}
			});
		});

		board.dataset.cierreCarSortBound = '1';
	}

	function cierreCarAjax(action, params) {
		if (!window.LogisticaAdmin || !window.fetch) {
			return Promise.resolve(null);
		}

		var data = new URLSearchParams(params || {});
		data.set('action', action);
		data.set('nonce', window.LogisticaAdmin.cierreCaracteristicasNonce || '');

		return window.fetch(window.LogisticaAdmin.ajaxUrl, {
			method: 'POST',
			credentials: 'same-origin',
			headers: {
				'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
			},
			body: data.toString()
		}).then(function (response) {
			return response.json();
		});
	}

	function stripCierreCarEditUrl() {
		if (!window.history || !window.history.replaceState) {
			return;
		}

		var url = new URL(window.location.href);
		if (!url.searchParams.has('edit_id')) {
			return;
		}

		url.searchParams.delete('edit_id');
		window.history.replaceState(null, '', url.toString());
	}

	function setCierreCarStatus(board, message, type) {
		var node = board ? board.querySelector('[data-cierre-car-status]') : null;
		if (!node) {
			return;
		}

		node.textContent = message || '';
		node.classList.toggle('is-error', type === 'error');
		node.classList.toggle('is-success', type === 'success');
	}

	function refreshCierreCarStats(board, total) {
		var node = board ? board.querySelector('[data-cierre-car-total]') : null;
		if (node && total !== undefined && total !== null) {
			node.textContent = String(total);
		}
	}

	function cierreCarRowById(board, rowId) {
		var rows;
		var i;

		if (!board) {
			return null;
		}

		rows = board.querySelectorAll('[data-cierre-car-row]');
		for (i = 0; i < rows.length; i++) {
			if (String(rows[i].getAttribute('data-row-id') || '') === String(rowId)) {
				return rows[i];
			}
		}

		return null;
	}

	function cierreCarActionsHtml(row) {
		return ''
			+ '<div class="logistica-cierre-caracteristicas-actions">'
			+ '<button type="button" class="button button-small logistica-table-icon-btn logistica-cierre-caracteristicas-btn-edit" data-action="cierre-car-edit" data-row-id="' + escapeHtml(String(row.id || '')) + '" aria-label="Editar" title="Editar">'
			+ '<span class="dashicons dashicons-edit" aria-hidden="true"></span>'
			+ '</button>'
			+ '<button type="button" class="button button-small logistica-table-icon-btn logistica-cierre-caracteristicas-btn-delete" data-action="cierre-car-delete" data-row-id="' + escapeHtml(String(row.id || '')) + '" aria-label="Eliminar" title="Eliminar">'
			+ '<span class="dashicons dashicons-trash" aria-hidden="true"></span>'
			+ '</button>'
			+ '</div>';
	}

	function buildCierreCarRowHtml(row) {
		var stateClass = row.complete ? 'is-car-complete' : 'is-car-incomplete';

		return ''
			+ '<tr class="' + stateClass + '" data-cierre-car-row data-row-id="' + escapeHtml(String(row.id || '')) + '"'
			+ ' data-cierre-car-search="' + escapeHtml(row.search_text || '') + '"'
			+ ' data-cierre-car-descripcion="' + escapeHtml(String(row.descripcion || '').toLowerCase()) + '"'
			+ ' data-cierre-car-tipo="' + escapeHtml(String(row.tipo || '')) + '"'
			+ ' data-cierre-car-sort-codigo="' + escapeHtml(String(row.codigo || '').toLowerCase()) + '"'
			+ ' data-cierre-car-sort-descripcion="' + escapeHtml(String(row.descripcion || '').toLowerCase()) + '"'
			+ ' data-cierre-car-sort-tipo="' + escapeHtml(String(row.tipo_label || '').toLowerCase()) + '"'
			+ ' data-cierre-car-sort-largo="' + escapeHtml(String(parseFloat(row.largo || 0) || 0)) + '"'
			+ ' data-cierre-car-sort-ancho="' + escapeHtml(String(parseFloat(row.ancho || 0) || 0)) + '"'
			+ ' data-cierre-car-sort-alto="' + escapeHtml(String(parseFloat(row.alto || 0) || 0)) + '"'
			+ ' data-cierre-car-sort-clave="' + escapeHtml(String(row.clave || '').toLowerCase()) + '"'
			+ ' data-cierre-car-sort-calibre="' + escapeHtml(String(parseFloat(row.calibre || 0) || 0)) + '">'
			+ '<td class="is-codigo"><span class="logistica-cierre-caracteristicas-codigo">' + escapeHtml(row.codigo || '') + '</span></td>'
			+ '<td>' + escapeHtml(row.descripcion || '') + '</td>'
			+ '<td>' + escapeHtml(row.tipo_label || row.tipo || '') + '</td>'
			+ '<td class="is-numeric">' + escapeHtml(row.largo || '') + '</td>'
			+ '<td class="is-numeric">' + escapeHtml(row.ancho || '') + '</td>'
			+ '<td class="is-numeric">' + escapeHtml(row.alto || '') + '</td>'
			+ '<td>' + escapeHtml(row.clave || '') + '</td>'
			+ '<td class="is-numeric">' + escapeHtml(row.calibre || '') + '</td>'
			+ '<td class="logistica-cierre-caracteristicas-actions-cell">' + cierreCarActionsHtml(row) + '</td>'
			+ '</tr>';
	}

	function applyCierreCarRowState(rowEl, row) {
		if (!rowEl || !row) {
			return;
		}

		rowEl.classList.toggle('is-car-complete', !!row.complete);
		rowEl.classList.toggle('is-car-incomplete', !row.complete);
		rowEl.setAttribute('data-row-id', String(row.id || ''));
		rowEl.setAttribute('data-cierre-car-search', row.search_text || '');
		rowEl.setAttribute('data-cierre-car-descripcion', String(row.descripcion || '').toLowerCase());
		rowEl.setAttribute('data-cierre-car-tipo', String(row.tipo || ''));
		rowEl.setAttribute('data-cierre-car-sort-codigo', String(row.codigo || '').toLowerCase());
		rowEl.setAttribute('data-cierre-car-sort-descripcion', String(row.descripcion || '').toLowerCase());
		rowEl.setAttribute('data-cierre-car-sort-tipo', String(row.tipo_label || '').toLowerCase());
		rowEl.setAttribute('data-cierre-car-sort-largo', String(parseFloat(row.largo || 0) || 0));
		rowEl.setAttribute('data-cierre-car-sort-ancho', String(parseFloat(row.ancho || 0) || 0));
		rowEl.setAttribute('data-cierre-car-sort-alto', String(parseFloat(row.alto || 0) || 0));
		rowEl.setAttribute('data-cierre-car-sort-clave', String(row.clave || '').toLowerCase());
		rowEl.setAttribute('data-cierre-car-sort-calibre', String(parseFloat(row.calibre || 0) || 0));

		var cells = rowEl.children;
		if (cells[0]) {
			cells[0].innerHTML = '<span class="logistica-cierre-caracteristicas-codigo">' + escapeHtml(row.codigo || '') + '</span>';
		}
		if (cells[1]) {
			cells[1].textContent = row.descripcion || '';
		}
		if (cells[2]) {
			cells[2].textContent = row.tipo_label || row.tipo || '';
		}
		if (cells[3]) {
			cells[3].textContent = row.largo || '';
		}
		if (cells[4]) {
			cells[4].textContent = row.ancho || '';
		}
		if (cells[5]) {
			cells[5].textContent = row.alto || '';
		}
		if (cells[6]) {
			cells[6].textContent = row.clave || '';
		}
		if (cells[7]) {
			cells[7].textContent = row.calibre || '';
		}
		if (cells[8]) {
			cells[8].innerHTML = cierreCarActionsHtml(row);
		}
	}

	function removeCierreCarRow(board, rowId) {
		var rowEl = cierreCarRowById(board, rowId);
		var tbody = board ? board.querySelector('[data-cierre-car-rows]') : null;

		if (rowEl) {
			rowEl.remove();
		}

		if (tbody && !tbody.querySelector('[data-cierre-car-row]')) {
			tbody.innerHTML = '<tr><td colspan="9">Sin características registradas. Agrega el primer código arriba.</td></tr>';
		}
	}

	function parseCierreCarJsonAttr(board, attrName) {
		if (!board) {
			return {};
		}

		try {
			return JSON.parse(board.getAttribute(attrName) || '{}') || {};
		} catch (error) {
			return {};
		}
	}

	function refreshCierreCarFormFields(board) {
		var form = board ? board.querySelector('[data-cierre-caracteristicas-form]') : null;
		var tipoSelect = form ? form.querySelector('[name="tipo"]') : null;
		var tipo = tipoSelect ? String(tipoSelect.value || 'cajas') : 'cajas';
		var fieldMap = parseCierreCarJsonAttr(board, 'data-cierre-car-field-map');
		var labelMap = parseCierreCarJsonAttr(board, 'data-cierre-car-label-map');
		var fieldsForTipo = fieldMap[tipo] || fieldMap.cajas || ['largo', 'ancho', 'alto', 'clave'];
		var labelsForTipo = labelMap[tipo] || {};

		['largo', 'ancho', 'alto', 'clave', 'calibre'].forEach(function (dim) {
			var wrap = form ? form.querySelector('[data-cierre-car-dim="' + dim + '"]') : null;
			var label = form ? form.querySelector('[data-cierre-car-dim-label="' + dim + '"]') : null;
			var show = fieldsForTipo.indexOf(dim) !== -1;

			if (!wrap) {
				return;
			}

			wrap.hidden = !show;
			wrap.classList.toggle('is-base-field-hidden', !show);
			if (show) {
				wrap.style.removeProperty('display');
			} else {
				wrap.style.setProperty('display', 'none', 'important');
			}

			if (label && labelsForTipo[dim]) {
				label.textContent = labelsForTipo[dim];
			}
		});
	}

	function populateCierreCarForm(board, row) {
		var form = board ? board.querySelector('[data-cierre-caracteristicas-form]') : null;
		var submitBtn = board ? board.querySelector('[data-cierre-car-submit]') : null;
		var submitLabel = submitBtn ? submitBtn.querySelector('[data-cierre-car-submit-label]') : null;
		var cancelEditBtn = board ? board.querySelector('[data-action="cierre-car-cancel-edit"]') : null;
		var cancelNewBtn = board ? board.querySelector('[data-action="cierre-car-close-form"]') : null;
		var codigoField;
		var rowIdField;
		var calibreField;
		var isEdit = !!(row && row.id);

		if (!form) {
			return;
		}

		form.reset();
		rowIdField = form.querySelector('[name="row_id"]');
		codigoField = form.querySelector('[name="codigo"]');
		calibreField = form.querySelector('[name="calibre"]');

		if (rowIdField) {
			rowIdField.value = isEdit ? String(row.id) : '';
		}

		if (isEdit) {
			form.querySelector('[name="descripcion"]').value = row.descripcion || '';
			form.querySelector('[name="tipo"]').value = row.tipo || 'cajas';
			form.querySelector('[name="largo"]').value = row.largo || '';
			form.querySelector('[name="ancho"]').value = row.ancho || '';
			form.querySelector('[name="alto"]').value = row.alto || '';
			form.querySelector('[name="clave"]').value = row.clave || '';
			if (calibreField) {
				calibreField.value = row.calibre || '';
			}
			if (codigoField) {
				codigoField.value = row.codigo || '';
				codigoField.readOnly = true;
				codigoField.classList.add('logistica-readonly-field');
			}
		} else if (codigoField) {
			codigoField.readOnly = false;
			codigoField.classList.remove('logistica-readonly-field');
		}

		refreshCierreCarFormFields(board);

		if (submitLabel && submitBtn) {
			submitLabel.textContent = isEdit
				? (submitBtn.getAttribute('data-label-edit') || 'Actualizar característica')
				: (submitBtn.getAttribute('data-label-add') || 'Agregar característica');
		}

		if (cancelEditBtn) {
			cancelEditBtn.hidden = !isEdit;
		}

		if (cancelNewBtn) {
			cancelNewBtn.hidden = isEdit;
		}
	}

	function initCierreCaracteristicas() {
		document.querySelectorAll('[data-cierre-caracteristicas-board]').forEach(function (board) {
			initCierreCaracteristicasBoard(board);
		});
	}

	function initCierreCaracteristicasBoard(board) {
		var fields;
		var submitButton;
		var rows;
		var searchInput;
		var descripcionSelect;
		var tipoSelect;
		var clearButton;
		var meta;
		var emptyMessage;
		var filtersWrap;
		var badge;

		if (!board || board.dataset.cierreCarBound === '1') {
			return;
		}

		fields = Array.prototype.slice.call(board.querySelectorAll('[data-cierre-car-field]'));
		submitButton = board.querySelector('[data-cierre-car-submit]');
		rows = Array.prototype.slice.call(board.querySelectorAll('[data-cierre-car-row]'));
		searchInput = board.querySelector('[data-cierre-car-filter-search]');
		descripcionSelect = board.querySelector('[data-cierre-car-filter-descripcion]');
		tipoSelect = board.querySelector('[data-cierre-car-filter-tipo]');
		clearButton = board.querySelector('[data-action="clear-cierre-car-filters"]');
		meta = board.querySelector('[data-cierre-car-filter-meta]');
		emptyMessage = board.querySelector('[data-cierre-car-filter-empty]');
		filtersWrap = board.querySelector('[data-cierre-car-filters]');
		badge = board.querySelector('[data-cierre-car-filter-badge]');

		function refreshVisibleCaracteristicaFields() {
			fields = Array.prototype.slice.call(board.querySelectorAll('[data-cierre-car-field]')).filter(function (field) {
				var wrap = field.closest('[data-cierre-car-dim]');
				if (!wrap) {
					return true;
				}
				return !wrap.hidden && !wrap.classList.contains('is-base-field-hidden');
			});
		}

		refreshCierreCarFormFields(board);
		refreshVisibleCaracteristicaFields();

		var formTipoSelect = board.querySelector('[data-cierre-car-tipo], [data-cierre-caracteristicas-form] [name="tipo"]');
		if (formTipoSelect && formTipoSelect.dataset.cierreCarTipoBound !== '1') {
			formTipoSelect.dataset.cierreCarTipoBound = '1';
			formTipoSelect.addEventListener('change', function () {
				refreshCierreCarFormFields(board);
				refreshVisibleCaracteristicaFields();
			});
		}

		function focusCaracteristicaField(index) {
			var field;

			if (index < 0 || index >= fields.length) {
				return;
			}

			field = fields[index];
			field.focus();

			if (typeof field.select === 'function' && field.type !== 'select-one') {
				window.requestAnimationFrame(function () {
					field.select();
				});
			}
		}

		function caracteristicaFieldIndex(target) {
			return fields.indexOf(target);
		}

		function normalizeFilterValue(value) {
			return String(value || '').toLowerCase().trim();
		}

		function updateCaracteristicasFilters(options) {
			var tbody = board.querySelector('[data-cierre-car-rows]');
			var query = normalizeFilterValue(searchInput ? searchInput.value : '');
			var descripcion = normalizeFilterValue(descripcionSelect ? descripcionSelect.value : '');
			var tipo = normalizeFilterValue(tipoSelect ? tipoSelect.value : '');
			var visible = 0;
			var total = 0;
			var hasFilters = Boolean(query || descripcion || tipo);

			options = options || {};
			rows = tbody ? Array.prototype.slice.call(tbody.querySelectorAll('[data-cierre-car-row]')) : [];
			total = rows.length;

			rows.forEach(function (row) {
				var matchesSearch = !query || normalizeFilterValue(row.dataset.cierreCarSearch).indexOf(query) !== -1;
				var matchesDescripcion = !descripcion || normalizeFilterValue(row.dataset.cierreCarDescripcion) === descripcion;
				var matchesTipo = !tipo || normalizeFilterValue(row.dataset.cierreCarTipo) === tipo;
				var show = matchesSearch && matchesDescripcion && matchesTipo;

				row.dataset.logisticaFilterVisible = show ? '1' : '0';
				if (show) {
					visible += 1;
				}
			});

			applyTablePagination(board, 'cierre-car', rows, options);

			if (filtersWrap) {
				filtersWrap.classList.toggle('is-active', hasFilters);
			}

			if (badge) {
				badge.hidden = !hasFilters;
			}

			if (meta) {
				if (hasFilters) {
					meta.textContent = visible + ' de ' + total + ' códigos visibles';
					meta.hidden = false;
				} else {
					meta.textContent = '';
					meta.hidden = true;
				}
			}

			if (emptyMessage) {
				emptyMessage.hidden = visible > 0 || rows.length === 0;
			}

			if (clearButton) {
				clearButton.disabled = !hasFilters;
			}
		}

		function focusCaracteristicaForm() {
			showCierreCaracteristicaForm(board);
		}

		board.addEventListener('focusin', function (event) {
			var input = event.target;

			if (!input.matches('[data-cierre-car-field]')) {
				return;
			}

			if (typeof input.select === 'function' && input.type !== 'select-one') {
				window.requestAnimationFrame(function () {
					input.select();
				});
			}
		});

		board.addEventListener('mouseup', function (event) {
			if (event.target.matches('[data-cierre-car-field]') && event.target.type !== 'select-one') {
				event.preventDefault();
			}
		});

		board.addEventListener('keydown', function (event) {
			var target = event.target;
			var index;
			var nextIndex;

			if (!target.matches('[data-cierre-car-field]')) {
				return;
			}

			index = caracteristicaFieldIndex(target);
			if (index < 0) {
				return;
			}

			if (event.key === 'Enter') {
				event.preventDefault();
				if (index < fields.length - 1) {
					focusCaracteristicaField(index + 1);
				} else if (submitButton) {
					submitButton.click();
				}
				return;
			}

			if (event.key === 'ArrowRight' || event.key === 'ArrowDown') {
				event.preventDefault();
				nextIndex = index + 1;
				if (nextIndex < fields.length) {
					focusCaracteristicaField(nextIndex);
				}
				return;
			}

			if (event.key === 'ArrowLeft' || event.key === 'ArrowUp') {
				event.preventDefault();
				nextIndex = index - 1;
				if (nextIndex >= 0) {
					focusCaracteristicaField(nextIndex);
				}
			}
		});

		bindTablePagination(board, 'cierre-car', function (options) {
			updateCaracteristicasFilters(options || {});
		});

		initCierreCarSort(board, function () {
			updateCaracteristicasFilters();
		});

		if (searchInput) {
			searchInput.addEventListener('input', function () {
				updateCaracteristicasFilters({ resetPage: true });
			});
		}

		if (descripcionSelect) {
			descripcionSelect.addEventListener('change', function () {
				updateCaracteristicasFilters({ resetPage: true });
			});
		}

		if (tipoSelect) {
			tipoSelect.addEventListener('change', function () {
				updateCaracteristicasFilters({ resetPage: true });
			});
		}

		if (clearButton) {
			clearButton.addEventListener('click', function () {
				if (searchInput) {
					searchInput.value = '';
				}
				if (descripcionSelect) {
					descripcionSelect.value = '';
				}
				if (tipoSelect) {
					tipoSelect.value = '';
				}
				updateCaracteristicasFilters({ resetPage: true });
				if (searchInput) {
					searchInput.focus();
				}
			});
		}

		if (board.querySelector('[data-excel-zoom-root]')) {
			initExcelTableZoom(board.querySelector('[data-excel-zoom-root]'));
		}

		board.__logisticaRefreshCierreCarFilters = updateCaracteristicasFilters;

		var form = board.querySelector('[data-cierre-caracteristicas-form]');
		if (form && form.dataset.cierreCarAjaxBound !== '1') {
			form.dataset.cierreCarAjaxBound = '1';
			form.addEventListener('submit', function (event) {
				var saveButton;
				var params;
				var tbody;
				var existingRow;
				var newRowWrap;
				var rowIdField;
				var editingId;

				event.preventDefault();
				saveButton = form.querySelector('[data-cierre-car-submit]');
				params = Object.fromEntries(new window.FormData(form).entries());
				tbody = board.querySelector('[data-cierre-car-rows]');
				rowIdField = form.querySelector('[name="row_id"]');
				editingId = rowIdField ? String(rowIdField.value || '').trim() : '';

				if (!String(params.codigo || '').trim()) {
					window.alert('El código es obligatorio.');
					return;
				}

				if (saveButton) {
					saveButton.disabled = true;
				}

				setCierreCarStatus(board, 'Guardando…', '');

				cierreCarAjax('logistica_save_cierre_caracteristica', params).then(function (payload) {
					if (saveButton) {
						saveButton.disabled = false;
					}

					if (!payload || !payload.success || !payload.data || !payload.data.row) {
						setCierreCarStatus(board, (payload && payload.data && payload.data.message) || 'No se pudo guardar.', 'error');
						return;
					}

					existingRow = editingId ? cierreCarRowById(board, editingId) : cierreCarRowById(board, payload.data.row.id);
					if (existingRow) {
						applyCierreCarRowState(existingRow, payload.data.row);
					} else if (tbody) {
						var emptyRow = tbody.querySelector('tr:not([data-cierre-car-row])');
						if (emptyRow) {
							emptyRow.remove();
						}

						newRowWrap = document.createElement('tbody');
						newRowWrap.innerHTML = buildCierreCarRowHtml(payload.data.row);
						if (newRowWrap.firstElementChild) {
							tbody.appendChild(newRowWrap.firstElementChild);
						}
					}

					refreshCierreCarStats(board, payload.data.total);
					setCierreCarStatus(board, payload.data.message || 'Característica guardada.', 'success');
					updateCaracteristicasFilters({ resetPage: false });
					hideCierreCaracteristicaForm(board);

					if (typeof syncClosingCharacteristicsFromCatalog === 'function') {
						syncClosingCharacteristicsFromCatalog();
					}
				});
			});
		}

		board.dataset.cierreCarBound = '1';
		updateCaracteristicasFilters();

		if (board.querySelector('[data-cierre-car-form-card]:not([hidden])')) {
			if (board.querySelector('[data-cierre-car-field][readonly]')) {
				focusCaracteristicaField(1);
			} else {
				focusCaracteristicaField(0);
			}
		}
	}

	function clearStuckTabLoadingStates() {
		document.querySelectorAll('[data-logistica-tab-loading]:not([hidden]), [data-user-panel-loading]:not([hidden])').forEach(function (overlay) {
			var host = overlay.parentElement;

			overlay.hidden = true;
			overlay.setAttribute('aria-busy', 'false');

			if (host) {
				host.classList.remove('is-tab-loading');
			}
		});

		document.querySelectorAll('.logistica-tab-panel.is-tab-loading, [data-planeacion-board].is-tab-loading').forEach(function (host) {
			var visibleOverlay = host.querySelector('[data-logistica-tab-loading]:not([hidden]), [data-user-panel-loading]:not([hidden])');
			if (!visibleOverlay) {
				host.classList.remove('is-tab-loading');
			}
		});
	}

	function removeThemePageLoader() {
		var loader = document.getElementById('loader');

		if (!loader) {
			return;
		}

		loader.classList.remove('show');
		loader.remove();
	}

	function initPanelPageScroll() {
		if (!document.body.classList.contains('logistica-panel-page') && !document.querySelector('[data-logistica-inventory-shell]')) {
			return;
		}

		removeThemePageLoader();
		clearStuckTabLoadingStates();
	}

	function finishInitialTabLoading() {
		var app = document.querySelector('[data-logistica-app]');
		var panel;
		var tab;
		var board;

		if (app) {
			panel = app.querySelector('.logistica-tab-panel.is-active');
			if (panel && window.LogisticaTabLoading) {
				tab = panel.getAttribute('data-tab-panel') || '';
				if ('logistica-codigos-etiqueta' === tab) {
					board = panel.querySelector('[data-etiqueta-catalog-board]');
					hideEtiquetaCatalogTabLoading(panel);
					if (board) {
						initEtiquetaCatalogBoard();
					}
				} else if ('logistica-rotulos-rollos' === tab) {
					board = panel.querySelector('[data-rotulo-rollos-board]');
					if (window.LogisticaTabLoading) {
						window.LogisticaTabLoading.hide(panel);
					}
					initRotuloPrintSettingsForms();
					if (board) {
						initRotulosRollosBoard();
					}
				} else if ('logistica-revision-minuta' === tab) {
					hideMinutasTabLoading(panel);
					initMinutasBoard();
				} else if ('logistica-cierre-caracteristicas' === tab) {
					board = panel.querySelector('[data-cierre-caracteristicas-board]');
					panel.dataset.tabLoaded = '1';
					window.LogisticaTabLoading.hide(panel);
					if (board) {
						initCierreCaracteristicasBoard(board);
					}
				} else if ('logistica' !== tab && 'logistica-cierre-ordenes' !== tab) {
					panel.dataset.tabLoaded = '1';
					window.LogisticaTabLoading.hide(panel);
				}
			}
			return;
		}

		if (!window.LogisticaTabLoading) {
			return;
		}

		document.querySelectorAll('[data-etiqueta-catalog-board]').forEach(function (etiquetaBoard) {
			initEtiquetaCatalogBoard();
		});

		document.querySelectorAll(
			'[data-warehouse-board], [data-minutas-board], [data-planeacion-board], [data-conversion-board], [data-cierre-caracteristicas-board], .logistica-conversion-board, .logistica-cierre-caracteristicas-board'
		).forEach(function (warehouseBoard) {
			window.LogisticaTabLoading.hide(warehouseBoard);
		});
	}

	initInventoryNav();

	initNotices();
	initFormSubmitFeedback();
	initLogisticaTabs();
	initFrontNav();
	initSelectCatalogOptionsAdmin();
	setExcelTableCompact(isExcelTableCompact());

	window.requestAnimationFrame(function () {
		initPanelPageScroll();
		finishInitialTabLoading();
	});
}());
