<?php

$settings = require __DIR__ . '/config.php';
$db = $settings['db'];
$schema = str_replace('`', '``', (string) $settings['catalog_schema']);
$locationId = (int) $settings['location_id'];
$minVisibleStock = (int) $settings['min_visible_stock'];
$title = (string) $settings['title'];

$mysqli = @new mysqli($db['host'], $db['user'], $db['pass'], $db['name'], (int) $db['port']);
if ($mysqli->connect_errno) {
	http_response_code(500);
	exit('Error de conexion a la base de datos.');
}
$mysqli->set_charset($db['charset']);

function json_response($payload, $status = 200)
{
	http_response_code((int) $status);
	header('Content-Type: application/json; charset=utf-8');
	echo json_encode($payload);
	exit;
}

function fetch_one_assoc($mysqli, $sql, $params = array())
{
	$stmt = $mysqli->prepare($sql);
	if (!$stmt) {
		return null;
	}
	if (!empty($params)) {
		$types = str_repeat('s', count($params));
		$bindValues = array($types);
		foreach ($params as $p) {
			$bindValues[] = $p;
		}
		$tmp = array();
		foreach ($bindValues as $k => $v) {
			$tmp[$k] = &$bindValues[$k];
		}
		call_user_func_array(array($stmt, 'bind_param'), $tmp);
	}
	$stmt->execute();
	$res = $stmt->get_result();
	$row = $res ? $res->fetch_assoc() : null;
	$stmt->close();
	return $row ?: null;
}

function fetch_all_assoc($mysqli, $sql, $params = array())
{
	$stmt = $mysqli->prepare($sql);
	if (!$stmt) {
		return array();
	}
	if (!empty($params)) {
		$types = str_repeat('s', count($params));
		$bindValues = array($types);
		foreach ($params as $p) {
			$bindValues[] = $p;
		}
		$tmp = array();
		foreach ($bindValues as $k => $v) {
			$tmp[$k] = &$bindValues[$k];
		}
		call_user_func_array(array($stmt, 'bind_param'), $tmp);
	}
	$stmt->execute();
	$res = $stmt->get_result();
	$rows = array();
	if ($res) {
		while ($row = $res->fetch_assoc()) {
			$rows[] = $row;
		}
	}
	$stmt->close();
	return $rows;
}

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
	$rawBody = file_get_contents('php://input');
	$input = json_decode((string) $rawBody, true);
	if (!is_array($input)) {
		json_response(array('ok' => false, 'message' => 'Solicitud invalida.'), 400);
	}
	if (($input['action'] ?? '') !== 'save_independent_order') {
		json_response(array('ok' => false, 'message' => 'Accion no soportada.'), 400);
	}

	$customer = isset($input['customer']) && is_array($input['customer']) ? $input['customer'] : array();
	$customerName = trim((string) ($customer['name'] ?? ''));
	$customerCompany = trim((string) ($customer['company'] ?? ''));
	$customerPhone = trim((string) ($customer['phone'] ?? ''));
	if ($customerName === '' || $customerCompany === '' || $customerPhone === '') {
		json_response(array('ok' => false, 'message' => 'Completa nombre, empresa y telefono.'), 400);
	}

	$items = isset($input['items']) && is_array($input['items']) ? $input['items'] : array();
	$normalizedItems = array();
	$itemIds = array();
	foreach ($items as $it) {
		if (!is_array($it)) {
			continue;
		}
		$itemId = (int) ($it['item_id'] ?? 0);
		$qty = (float) ($it['quantity'] ?? 0);
		if ($itemId <= 0 || $qty <= 0) {
			continue;
		}
		$qty = floor($qty);
		if ($qty <= 0) {
			continue;
		}
		$normalizedItems[] = array(
			'item_id' => $itemId,
			'quantity' => $qty
		);
		$itemIds[] = $itemId;
	}

	if (count($normalizedItems) === 0) {
		json_response(array('ok' => false, 'message' => 'No hay productos seleccionados.'), 400);
	}

	$itemIds = array_values(array_unique($itemIds));
	$inPlaceholders = implode(',', array_fill(0, count($itemIds), '?'));
	$sqlCatalogItems = "
		SELECT
			i.item_id,
			i.name,
			COALESCE(li.unit_price, i.unit_price, 0) AS unit_price,
			i.main_image_id
		FROM phppos_items i
		LEFT JOIN phppos_location_items li
			ON li.item_id = i.item_id
			AND li.location_id = ?
		WHERE i.item_id IN ({$inPlaceholders})
			AND i.deleted = 0
	";
	$queryParams = array((string) $locationId);
	foreach ($itemIds as $id) {
		$queryParams[] = (string) $id;
	}
	$productRows = fetch_all_assoc($mysqli, $sqlCatalogItems, $queryParams);
	$productMap = array();
	foreach ($productRows as $pr) {
		$productMap[(int) $pr['item_id']] = $pr;
	}

	$cartRows = array();
	$subtotal = 0.0;
	foreach ($normalizedItems as $it) {
		$itemId = (int) $it['item_id'];
		if (!isset($productMap[$itemId])) {
			continue;
		}
		$quantity = (float) $it['quantity'];
		$unitPrice = (float) $productMap[$itemId]['unit_price'];
		$lineTotal = $quantity * $unitPrice;
		$cartRows[] = array(
			'item_id' => $itemId,
			'item_name' => (string) $productMap[$itemId]['name'],
			'quantity' => $quantity,
			'unit_price' => $unitPrice,
			'line_total' => $lineTotal,
			'image_id' => !empty($productMap[$itemId]['main_image_id']) ? (int) $productMap[$itemId]['main_image_id'] : null
		);
		$subtotal += $lineTotal;
	}

	if (count($cartRows) === 0) {
		json_response(array('ok' => false, 'message' => 'No se encontraron productos validos para guardar.'), 400);
	}

	$customerDisplayName = $customerCompany !== '' ? $customerCompany : $customerName;
	try {
		$token = 'ind-' . bin2hex(random_bytes(12));
	} catch (Exception $e) {
		$token = 'ind-' . sha1(uniqid('', true));
	}
	$createdAt = date('Y-m-d H:i:s');
	$expiresAt = date('Y-m-d H:i:s', strtotime('+30 days'));
	$customerNote = "Pedido catalogo independiente\n"
		. 'Nombre: ' . $customerName . "\n"
		. 'Empresa: ' . $customerCompany . "\n"
		. 'Telefono: ' . $customerPhone;

	$mysqli->begin_transaction();
	try {
		$sqlLink = "INSERT INTO `{$schema}`.catalog_links
			(token, customer_id, customer_name, tier_id, employee_id, location_id, expires_at, created_at, created_by, last_opened_at, status)
			VALUES (?, 0, ?, NULL, NULL, ?, ?, ?, 'catalogo_independiente', NULL, 0)";
		$stmtLink = $mysqli->prepare($sqlLink);
		if (!$stmtLink) {
			throw new Exception('No se pudo crear link interno.');
		}
		$loc = (int) $locationId;
		$stmtLink->bind_param('ssiss', $token, $customerDisplayName, $loc, $expiresAt, $createdAt);
		if (!$stmtLink->execute()) {
			$stmtLink->close();
			throw new Exception('No se pudo guardar datos del cliente.');
		}
		$linkId = (int) $stmtLink->insert_id;
		$stmtLink->close();

		$sqlOrder = "INSERT INTO `{$schema}`.catalog_orders
			(link_id, customer_id, sale_id, customer_note, ordered_at, subtotal, tax, total, status)
			VALUES (?, 0, NULL, ?, ?, ?, 0, ?, 'cliente_nuevo')";
		$stmtOrder = $mysqli->prepare($sqlOrder);
		if (!$stmtOrder) {
			throw new Exception('No se pudo crear pedido.');
		}
		$total = $subtotal;
		$stmtOrder->bind_param('issdd', $linkId, $customerNote, $createdAt, $subtotal, $total);
		if (!$stmtOrder->execute()) {
			$stmtOrder->close();
			throw new Exception('No se pudo guardar pedido.');
		}
		$orderId = (int) $stmtOrder->insert_id;
		$stmtOrder->close();

		$sqlItem = "INSERT INTO `{$schema}`.catalog_order_items
			(order_id, item_id, item_name, quantity, unit_price, line_total, image_id)
			VALUES (?, ?, ?, ?, ?, ?, ?)";
		$stmtItem = $mysqli->prepare($sqlItem);
		if (!$stmtItem) {
			throw new Exception('No se pudieron guardar los items del pedido.');
		}
		foreach ($cartRows as $row) {
			$orderItemId = (int) $orderId;
			$itemId = (int) $row['item_id'];
			$itemName = (string) $row['item_name'];
			$qty = (float) $row['quantity'];
			$unitPrice = (float) $row['unit_price'];
			$lineTotal = (float) $row['line_total'];
			$imageId = !empty($row['image_id']) ? (int) $row['image_id'] : null;
			$stmtItem->bind_param('iisdddi', $orderItemId, $itemId, $itemName, $qty, $unitPrice, $lineTotal, $imageId);
			if (!$stmtItem->execute()) {
				$stmtItem->close();
				throw new Exception('No se pudieron guardar todos los items.');
			}
		}
		$stmtItem->close();

		$mysqli->commit();
		json_response(array('ok' => true, 'order_id' => $orderId, 'message' => 'Pedido guardado correctamente.'));
	} catch (Exception $e) {
		$mysqli->rollback();
		json_response(array('ok' => false, 'message' => $e->getMessage()), 500);
	}
}

$template = fetch_one_assoc(
	$mysqli,
	"SELECT * FROM `{$schema}`.catalog_templates WHERE is_active = 1 ORDER BY id DESC LIMIT 1"
);

$announcementRow = fetch_one_assoc(
	$mysqli,
	"SELECT `value` FROM `{$schema}`.catalog_settings WHERE `key` = 'catalog_announcement' LIMIT 1"
);
$catalogAnnouncement = $announcementRow ? (string) $announcementRow['value'] : '';
$thankYouRow = fetch_one_assoc(
	$mysqli,
	"SELECT `value` FROM `{$schema}`.catalog_settings WHERE `key` = 'thank_you_message' LIMIT 1"
);
$thankYouMessage = $thankYouRow ? trim((string) $thankYouRow['value']) : '';
if ($thankYouMessage === '') {
	$thankYouMessage = 'Gracias por su pedido. Pronto nos estaremos comunicando.';
}

$logoFileId = 0;
$logoRow = fetch_one_assoc(
	$mysqli,
	'SELECT company_logo FROM phppos_locations WHERE location_id = ? LIMIT 1',
	[(string) $locationId]
);
if ($logoRow && !empty($logoRow['company_logo']) && (int) $logoRow['company_logo'] > 0) {
	$logoFileId = (int) $logoRow['company_logo'];
}
if ($logoFileId <= 0) {
	$fallbackLogoRow = fetch_one_assoc(
		$mysqli,
		"SELECT `value` FROM phppos_app_config WHERE `key` = 'company_logo' LIMIT 1"
	);
	if ($fallbackLogoRow && !empty($fallbackLogoRow['value']) && (int) $fallbackLogoRow['value'] > 0) {
		$logoFileId = (int) $fallbackLogoRow['value'];
	}
}

$sql = "
SELECT
	i.item_id,
	i.name,
	i.category_id,
	COALESCE(cat.name, 'Sin categoria') AS category_name,
	i.description,
	i.long_description,
	COALESCE(img.main_image_id, img.first_image_id) AS image_id,
	COALESCE(img.image_ids, '') AS image_ids
FROM phppos_items i
LEFT JOIN phppos_categories cat
	ON cat.id = i.category_id
	AND COALESCE(cat.deleted, 0) = 0
LEFT JOIN `{$schema}`.catalog_category_sort csort
	ON csort.category_id = i.category_id
LEFT JOIN phppos_location_items li
	ON li.item_id = i.item_id
	AND li.location_id = ?
LEFT JOIN (
	SELECT
		ii.item_id,
		MAX(CASE WHEN i2.main_image_id IS NOT NULL AND ii.image_id = i2.main_image_id THEN ii.image_id ELSE NULL END) AS main_image_id,
		CAST(SUBSTRING_INDEX(
			GROUP_CONCAT(
				ii.image_id
				ORDER BY
					COALESCE(cis.sort_order,
						CASE
							WHEN i2.main_image_id IS NOT NULL AND ii.image_id = i2.main_image_id THEN 0
							ELSE 2147483647
						END
					),
					ii.id ASC
				SEPARATOR ','
			),
			',',
			1
		) AS UNSIGNED) AS first_image_id,
		GROUP_CONCAT(
			ii.image_id
			ORDER BY
				COALESCE(cis.sort_order,
					CASE
						WHEN i2.main_image_id IS NOT NULL AND ii.image_id = i2.main_image_id THEN 0
						ELSE 2147483647
					END
				),
				ii.id ASC
			SEPARATOR ','
		) AS image_ids
	FROM phppos_item_images ii
	INNER JOIN phppos_items i2 ON i2.item_id = ii.item_id
	LEFT JOIN `{$schema}`.catalog_item_image_sort cis
		ON cis.item_id = ii.item_id
		AND cis.image_id = ii.image_id
	GROUP BY ii.item_id
) img ON img.item_id = i.item_id
WHERE i.deleted = 0
	AND COALESCE(i.item_inactive, 0) = 0
	AND COALESCE(i.is_ecommerce, 1) = 1
	AND COALESCE(img.main_image_id, img.first_image_id) IS NOT NULL
	AND COALESCE(li.quantity, 0) >= ?
ORDER BY COALESCE(csort.sort_order, 9999) ASC, category_name ASC, i.name ASC
";

$stmt = $mysqli->prepare($sql);
if (!$stmt) {
	http_response_code(500);
	exit('Error preparando consulta de productos.');
}
$stmt->bind_param('ii', $locationId, $minVisibleStock);
$stmt->execute();
$res = $stmt->get_result();
$products = [];
$categories = [];
while ($row = $res->fetch_assoc()) {
	$itemId = (int) $row['item_id'];
	$categoryId = !empty($row['category_id']) ? (int) $row['category_id'] : 0;
	$categoryName = trim((string) $row['category_name']) !== '' ? (string) $row['category_name'] : 'Sin categoria';
	$imageId = !empty($row['image_id']) ? (int) $row['image_id'] : 0;
	$description = trim((string) $row['description']);
	if ($description === '') {
		$description = trim((string) $row['long_description']);
	}

	$imageIds = [];
	$rawIds = isset($row['image_ids']) ? (string) $row['image_ids'] : '';
	if ($rawIds !== '') {
		$parts = explode(',', $rawIds);
		foreach ($parts as $part) {
			$id = (int) $part;
			if ($id > 0) {
				$imageIds[$id] = $id;
			}
		}
	}
	if ($imageId > 0 && !isset($imageIds[$imageId])) {
		$imageIds[$imageId] = $imageId;
	}
	$imageIds = array_values($imageIds);

	$products[] = [
		'item_id' => $itemId,
		'name' => (string) $row['name'],
		'category_id' => $categoryId,
		'category_name' => $categoryName,
		'description' => $description,
		'image_id' => $imageId,
		'image_ids' => $imageIds,
	];

	if ($categoryId > 0) {
		$categories[$categoryId] = $categoryName;
	}
}
$stmt->close();
$mysqli->close();

asort($categories);

$groupedProducts = [];
foreach ($products as $p) {
	$cid = $p['category_id'];
	$cname = $p['category_name'];
	if (!isset($groupedProducts[$cid])) {
		$groupedProducts[$cid] = [
			'name' => $cname,
			'items' => [],
		];
	}
	$groupedProducts[$cid]['items'][] = $p;
}

$primary = !empty($template['primary_color']) ? (string) $template['primary_color'] : '#d62828';
$secondary = !empty($template['secondary_color']) ? (string) $template['secondary_color'] : '#003049';
$bg = !empty($template['bg_color']) ? (string) $template['bg_color'] : '#fcfaf5';
$imageBg = !empty($template['image_bg_color']) ? (string) $template['image_bg_color'] : '#f4f1e6';
$border = !empty($template['border_color']) ? (string) $template['border_color'] : '#e4ddce';
$text = !empty($template['text_color']) ? (string) $template['text_color'] : '#0f172a';
$mutedText = !empty($template['muted_text_color']) ? (string) $template['muted_text_color'] : '#546278';
$titleSize = !empty($template['title_size_px']) ? (int) $template['title_size_px'] : 17;
$descSize = !empty($template['desc_size_px']) ? (int) $template['desc_size_px'] : 13;
$logoSize = !empty($template['logo_size_px']) ? (int) $template['logo_size_px'] : 64;
$cardStyle = !empty($template['card_style']) ? (string) $template['card_style'] : 'rounded';
$radius = '16px';
if ($cardStyle === 'soft') {
	$radius = '24px';
}
if ($cardStyle === 'sharp') {
	$radius = '6px';
}
$thanksRequested = isset($_GET['gracias']) && (string) $_GET['gracias'] === '1';
?>
<!DOCTYPE html>
<html lang="es">
<head>
	<meta charset="utf-8">
	<meta name="viewport" content="width=device-width, initial-scale=1">
	<title><?php echo htmlspecialchars($title, ENT_QUOTES, 'UTF-8'); ?></title>
	<link rel="preconnect" href="https://fonts.googleapis.com">
	<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
	<link href="https://fonts.googleapis.com/css2?family=Manrope:wght@400;600;700;800&display=swap" rel="stylesheet">
	<style>
		:root { --ink:<?php echo htmlspecialchars($text, ENT_QUOTES, 'UTF-8'); ?>; --muted:<?php echo htmlspecialchars($mutedText, ENT_QUOTES, 'UTF-8'); ?>; --brand:<?php echo htmlspecialchars($primary, ENT_QUOTES, 'UTF-8'); ?>; --bg:<?php echo htmlspecialchars($bg, ENT_QUOTES, 'UTF-8'); ?>; --img-bg:<?php echo htmlspecialchars($imageBg, ENT_QUOTES, 'UTF-8'); ?>; --card:#fff; --line:<?php echo htmlspecialchars($border, ENT_QUOTES, 'UTF-8'); ?>; --hero:<?php echo htmlspecialchars($secondary, ENT_QUOTES, 'UTF-8'); ?>; --radius:<?php echo $radius; ?>; --title-size:<?php echo (int) $titleSize; ?>px; --desc-size:<?php echo (int) $descSize; ?>px; --logo-size:<?php echo (int) $logoSize; ?>px; }
		* { box-sizing:border-box; }
		body { margin:0; font-family:'Manrope',sans-serif; color:var(--ink); background:var(--bg); }
		.wrap { max-width:1200px; margin:0 auto; padding:20px 14px 84px; }
		.hero { background:linear-gradient(120deg, var(--hero), #264653); color:#fff; border-radius:calc(var(--radius) + 4px); padding:22px; margin-bottom:16px; box-shadow:0 14px 32px rgba(0,48,73,.28); }
		.hero h1 { margin:0; font-size:32px; letter-spacing:-.02em; }
		.hero-head { display:flex; align-items:center; gap:12px; margin-bottom:0; }
		.logo { width:var(--logo-size); height:var(--logo-size); object-fit:contain; background:rgba(255,255,255,.92); border-radius:12px; padding:6px; border:1px solid rgba(255,255,255,.45); }
		.controls { display:flex; gap:10px; align-items:center; margin:14px 0; }
		.announcement { margin:0 0 12px; padding:10px 12px; border-radius:12px; border:1px solid #d1dbe8; background:#fff; color:var(--ink); font-weight:700; line-height:1.3; }
		.search { flex:1; border:1px solid #d6dde8; border-radius:10px; padding:11px; font:inherit; }
		.controls-secondary { display:flex; gap:10px; align-items:center; margin:0 0 14px; }
		.category-filter { width:100%; max-width:320px; border:1px solid #d6dde8; border-radius:10px; padding:11px; font:inherit; background:#fff; }
		.category-block { margin:0 0 16px; }
		.category-title { margin:0 0 8px; padding:8px 12px; border-left:5px solid var(--brand); background:#fff; border-radius:10px; font-size:20px; font-weight:800; color:var(--ink); }
		.grid { display:grid; grid-template-columns:repeat(auto-fill, minmax(230px, 1fr)); gap:12px; }
		.card { border:1px solid var(--line); background:var(--card); border-radius:var(--radius); overflow:hidden; box-shadow:0 4px 16px rgba(12,17,43,.06); display:flex; flex-direction:column; }
		.media { width:100%; height:220px; overflow:hidden; background:var(--img-bg); display:flex; align-items:center; justify-content:center; }
		.image { width:100%; height:100%; object-fit:contain; object-position:center; display:block; }
		.zoomable { width:100%; height:100%; border:none; background:transparent; padding:0; cursor:zoom-in; }
		.noimg { height:100%; width:100%; display:flex; align-items:center; justify-content:center; color:#9ca3af; font-size:14px; }
		.body { padding:12px; display:flex; flex-direction:column; gap:8px; flex:1; }
		.name { margin:0; font-size:var(--title-size); line-height:1.25; color:var(--ink); }
		.desc { margin:0; font-size:var(--desc-size); color:var(--muted); min-height:32px; }
		.qty { display:flex; align-items:center; justify-content:space-between; gap:10px; }
		.qty-stepper { display:flex; align-items:center; border:1px solid #d5deee; border-radius:10px; overflow:hidden; background:#fff; }
		.qty-btn { width:34px; height:34px; border:none; background:#eef2ff; color:#1e293b; font-size:20px; line-height:1; cursor:pointer; }
		.qty-btn:active { filter:brightness(.94); }
		.qty-input { width:52px; height:34px; border:none; border-left:1px solid #d5deee; border-right:1px solid #d5deee; text-align:center; font:inherit; font-weight:700; }
		.floating-copy { position:fixed; right:18px; bottom:18px; z-index:2100; border:none; background:var(--brand); color:#fff; border-radius:999px; padding:14px 18px; font:inherit; font-weight:800; cursor:pointer; box-shadow:0 12px 26px rgba(2,6,23,.25); }
		.floating-copy:hover { filter:brightness(.96); }
		.floating-copy.hidden-by-viewer { display:none !important; }
		.order-modal { position:fixed; inset:0; z-index:2200; background:rgba(2,6,23,.68); display:none; align-items:center; justify-content:center; padding:14px; }
		.order-modal.show { display:flex; }
		.order-modal-box { width:min(760px, 100%); max-height:92vh; overflow:auto; border-radius:14px; background:#fff; border:1px solid #d8e0ec; box-shadow:0 20px 34px rgba(2,6,23,.28); }
		.order-modal-head { display:flex; justify-content:space-between; align-items:center; padding:12px 14px; border-bottom:1px solid #e5e9f2; }
		.order-modal-title { margin:0; font-size:22px; color:var(--ink); }
		.order-modal-close { border:none; background:#e2e8f0; border-radius:8px; width:36px; height:36px; cursor:pointer; font-weight:800; color:#1e293b; }
		.order-modal-body { padding:14px; display:grid; gap:12px; }
		.form-grid { display:grid; grid-template-columns:repeat(3, minmax(0, 1fr)); gap:10px; }
		.field label { display:block; margin-bottom:6px; font-weight:700; color:#334155; }
		.field input { width:100%; border:1px solid #d6dde8; border-radius:10px; padding:11px; font:inherit; }
		.field input.invalid { border-color:#dc2626; background:#fff1f2; box-shadow:0 0 0 2px rgba(220,38,38,.08); }
		.validation-msg { margin:0; font-size:13px; color:#b91c1c; font-weight:700; }
		.order-summary { border:1px solid #dce4f2; border-radius:12px; background:#f8fafc; padding:10px; min-height:150px; white-space:pre-wrap; font-family:ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace; font-size:13px; color:#0f172a; }
		.order-modal-actions { display:flex; gap:10px; justify-content:flex-end; }
		.btn { border:none; border-radius:10px; padding:10px 14px; font:inherit; font-weight:700; cursor:pointer; }
		.btn-copy { background:#e2e8f0; color:#0f172a; }
		.btn-save { background:var(--brand); color:#fff; }
		.modal-note { margin:0; color:#64748b; font-size:13px; }
		.thanks-wrap { min-height:100vh; display:flex; align-items:center; justify-content:center; padding:20px; }
		.thanks-card { width:min(680px, 100%); background:#fff; border:1px solid #d8e0ec; border-radius:18px; box-shadow:0 16px 34px rgba(2,6,23,.18); padding:26px 20px; text-align:center; }
		.thanks-logo { width:min(140px, 38vw); height:auto; max-height:110px; object-fit:contain; margin:0 auto 14px; display:block; }
		.thanks-title { margin:0 0 10px; font-size:30px; color:var(--ink); letter-spacing:-0.02em; }
		.thanks-message { margin:0; font-size:17px; color:var(--muted); line-height:1.5; white-space:pre-line; }
		.viewer { position:fixed; inset:0; display:none; align-items:center; justify-content:center; z-index:2000; background:rgba(2,6,23,.78); padding:16px; }
		.viewer.show { display:flex; }
		.viewer-box { width:min(980px, 96vw); max-height:92vh; background:#fff; border-radius:14px; overflow:hidden; display:flex; flex-direction:column; }
		.viewer-head { display:flex; justify-content:space-between; align-items:center; padding:10px 12px; border-bottom:1px solid #e2e8f0; }
		.viewer-title { font-weight:700; font-size:15px; }
		.viewer-close { border:none; background:#e2e8f0; border-radius:8px; padding:6px 9px; cursor:pointer; font-weight:700; }
		.viewer-stage { position:relative; background:#f8fafc; display:flex; align-items:center; justify-content:center; min-height:56vh; touch-action:none; overflow:hidden; }
		.viewer-image { max-width:100%; max-height:66vh; object-fit:contain; display:block; transform-origin:center center; transition:transform .08s linear; }
		.viewer-controls-row { display:flex; justify-content:space-between; align-items:center; gap:10px; padding:10px; border-top:1px solid #e2e8f0; background:#fff; }
		.viewer-nav { width:42px; height:42px; border:none; border-radius:999px; background:rgba(15,23,42,.65); color:#fff; cursor:pointer; font-size:20px; display:flex; align-items:center; justify-content:center; }
		.viewer-nav:disabled { opacity:.35; cursor:default; }
		.viewer-thumbs { display:flex; gap:8px; overflow:auto; padding:10px; border-top:1px solid #e2e8f0; background:#fff; }
		.viewer-thumb { width:62px; height:62px; border:2px solid transparent; border-radius:8px; overflow:hidden; cursor:pointer; background:#f8fafc; flex:0 0 auto; padding:0; }
		.viewer-thumb img { width:100%; height:100%; object-fit:cover; display:block; }
		.viewer-thumb.active { border-color:var(--brand); }
		.viewer-order { display:flex; justify-content:center; align-items:center; gap:10px; flex:1; min-width:0; }
		.viewer-order-label { font-weight:700; color:#334155; }
		.viewer-order-stepper { display:flex; align-items:center; border:1px solid #d5deee; border-radius:10px; overflow:hidden; background:#fff; }
		.viewer-order-btn { width:36px; height:36px; border:none; background:#eef2ff; color:#1e293b; font-size:22px; line-height:1; cursor:pointer; }
		.viewer-order-value { width:58px; height:36px; border:none; border-left:1px solid #d5deee; border-right:1px solid #d5deee; text-align:center; font:inherit; font-weight:700; }
		@media (max-width: 700px) {
			.hero h1 { font-size:25px; }
			.grid { grid-template-columns:repeat(2, minmax(0, 1fr)); gap:8px; }
			.media { height:160px; }
			.body { padding:9px; gap:6px; }
			.name { font-size:calc(var(--title-size) - 2px); line-height:1.2; }
			.desc { font-size:calc(var(--desc-size) - 1px); min-height:20px; }
			.qty { flex-direction:column; align-items:flex-start; gap:6px; }
			.qty-stepper { width:100%; max-width:132px; }
			.qty-btn { width:38px; height:38px; font-size:22px; }
			.qty-input { width:56px; height:38px; }
			.floating-copy { right:12px; bottom:12px; width:calc(100vw - 24px); border-radius:12px; text-align:center; }
			.form-grid { grid-template-columns:1fr; }
			.order-modal-actions { flex-direction:column; }
			.order-modal-actions .btn { width:100%; }
		}
	</style>
</head>
<body>
	<?php if ($thanksRequested): ?>
		<div class="thanks-wrap">
			<section class="thanks-card">
				<?php if ($logoFileId > 0): ?>
					<img class="thanks-logo" src="image.php?id=<?php echo (int) $logoFileId; ?>" alt="Logo empresa">
				<?php endif; ?>
				<h1 class="thanks-title">Gracias por su pedido</h1>
				<p class="thanks-message"><?php echo nl2br(htmlspecialchars($thankYouMessage, ENT_QUOTES, 'UTF-8')); ?></p>
			</section>
		</div>
	<?php else: ?>
	<div class="wrap">
		<section class="hero">
			<div class="hero-head">
				<?php if ($logoFileId > 0): ?>
					<img class="logo" src="image.php?id=<?php echo (int) $logoFileId; ?>" alt="Logo empresa">
				<?php endif; ?>
				<div>
					<h1><?php echo htmlspecialchars($title, ENT_QUOTES, 'UTF-8'); ?></h1>
				</div>
			</div>
		</section>

		<?php if (trim($catalogAnnouncement) !== ''): ?>
			<div class="announcement"><?php echo nl2br(htmlspecialchars($catalogAnnouncement, ENT_QUOTES, 'UTF-8')); ?></div>
		<?php endif; ?>

		<div class="controls">
			<input id="search" class="search" type="text" placeholder="Buscar producto por nombre o descripcion...">
		</div>
		<div class="controls-secondary">
			<select id="categoryFilter" class="category-filter">
				<option value="">Todas las categorias</option>
				<?php foreach ($categories as $categoryId => $categoryName): ?>
					<option value="<?php echo (int) $categoryId; ?>"><?php echo htmlspecialchars($categoryName, ENT_QUOTES, 'UTF-8'); ?></option>
				<?php endforeach; ?>
			</select>
		</div>

		<section id="productGrid">
			<?php foreach ($groupedProducts as $groupCategoryId => $groupData): ?>
				<div class="category-block" data-category-group="<?php echo (int) $groupCategoryId; ?>">
					<h2 class="category-title"><?php echo htmlspecialchars((string) $groupData['name'], ENT_QUOTES, 'UTF-8'); ?></h2>
					<div class="grid">
						<?php foreach ($groupData['items'] as $p): ?>
							<?php
							$desc = trim(substr(strip_tags((string) $p['description']), 0, 90));
							$searchText = strtolower((string) $p['name'] . ' ' . (string) $p['description']);
							$imagesJson = json_encode(array_values($p['image_ids']));
							?>
							<article class="card product-card" data-text="<?php echo htmlspecialchars($searchText, ENT_QUOTES, 'UTF-8'); ?>" data-category="<?php echo (int) $p['category_id']; ?>">
								<div class="media">
									<?php if (!empty($p['image_id'])): ?>
										<button type="button" class="zoomable js-open-viewer" data-item-id="<?php echo (int) $p['item_id']; ?>" data-name="<?php echo htmlspecialchars((string) $p['name'], ENT_QUOTES, 'UTF-8'); ?>" data-images="<?php echo htmlspecialchars((string) $imagesJson, ENT_QUOTES, 'UTF-8'); ?>">
											<img class="image" src="image.php?id=<?php echo (int) $p['image_id']; ?>" alt="<?php echo htmlspecialchars((string) $p['name'], ENT_QUOTES, 'UTF-8'); ?>">
										</button>
									<?php else: ?>
										<div class="noimg">Sin imagen</div>
									<?php endif; ?>
								</div>
								<div class="body">
									<h3 class="name"><?php echo htmlspecialchars((string) $p['name'], ENT_QUOTES, 'UTF-8'); ?></h3>
									<?php if ($desc !== ''): ?>
										<p class="desc"><?php echo htmlspecialchars($desc, ENT_QUOTES, 'UTF-8'); ?></p>
									<?php endif; ?>
									<div class="qty">
										<label for="qty_<?php echo (int) $p['item_id']; ?>">Cantidad</label>
										<div class="qty-stepper">
											<button type="button" class="qty-btn js-qty-btn" data-target="qty_<?php echo (int) $p['item_id']; ?>" data-step="-1" aria-label="Disminuir">-</button>
											<input class="qty-input" id="qty_<?php echo (int) $p['item_id']; ?>" name="qty[<?php echo (int) $p['item_id']; ?>]" type="number" step="1" min="0" value="0" inputmode="numeric">
											<button type="button" class="qty-btn js-qty-btn" data-target="qty_<?php echo (int) $p['item_id']; ?>" data-step="1" aria-label="Aumentar">+</button>
										</div>
									</div>
								</div>
							</article>
						<?php endforeach; ?>
					</div>
				</div>
			<?php endforeach; ?>
		</section>
	</div>

	<button type="button" class="floating-copy" id="floatingCopyBtn">Enviar pedido (0)</button>

	<div class="order-modal" id="orderModal" aria-hidden="true">
		<div class="order-modal-box">
			<div class="order-modal-head">
				<h2 class="order-modal-title">Enviar pedido</h2>
				<button type="button" class="order-modal-close" id="orderModalClose" aria-label="Cerrar">X</button>
			</div>
			<div class="order-modal-body">
				<div class="form-grid">
					<div class="field">
						<label for="clientName">Nombre</label>
						<input id="clientName" type="text" maxlength="120" placeholder="Nombre del cliente">
					</div>
					<div class="field">
						<label for="clientCompany">Empresa</label>
						<input id="clientCompany" type="text" maxlength="160" placeholder="Empresa">
					</div>
					<div class="field">
						<label for="clientPhone">Telefono</label>
						<input id="clientPhone" type="text" maxlength="40" placeholder="Telefono">
					</div>
				</div>

				<div>
					<label for="orderSummaryBox" style="display:block; margin-bottom:6px; font-weight:700; color:#334155;">Resumen pedido (sin precios)</label>
					<pre id="orderSummaryBox" class="order-summary"></pre>
				</div>
				<p class="validation-msg" id="orderValidationMsg" style="display:none;">Completa nombre, empresa y telefono.</p>
				<div class="order-modal-actions">
					<button type="button" class="btn btn-copy" id="copyOrderBtn">Copiar</button>
					<button type="button" class="btn btn-save" id="saveOrderBtn">Enviar pedido</button>
				</div>
			</div>
		</div>
	</div>

	<div class="viewer" id="imgViewer">
		<div class="viewer-box">
			<div class="viewer-head">
				<div class="viewer-title" id="viewerTitle">Imagen</div>
				<button type="button" class="viewer-close" id="viewerClose">X</button>
			</div>
			<div class="viewer-stage" id="viewerStage">
				<img id="viewerImage" class="viewer-image" src="" alt="Vista imagen">
			</div>
			<div class="viewer-thumbs" id="viewerThumbs"></div>
			<div class="viewer-controls-row">
				<button type="button" class="viewer-nav viewer-prev" id="viewerPrev">&#8249;</button>
				<div class="viewer-order">
					<span class="viewer-order-label">Cantidad</span>
					<div class="viewer-order-stepper">
						<button type="button" class="viewer-order-btn" id="viewerQtyMinus" aria-label="Disminuir cantidad">-</button>
						<input type="text" class="viewer-order-value" id="viewerQtyValue" value="0" readonly>
						<button type="button" class="viewer-order-btn" id="viewerQtyPlus" aria-label="Aumentar cantidad">+</button>
					</div>
				</div>
				<button type="button" class="viewer-nav viewer-next" id="viewerNext">&#8250;</button>
			</div>
		</div>
	</div>

	<script>
	(function() {
		var storageKey = 'catalogo_independiente_qty_v1';
		var search = document.getElementById('search');
		var categoryFilter = document.getElementById('categoryFilter');
		var cards = document.querySelectorAll('.product-card');
		var categoryBlocks = document.querySelectorAll('[data-category-group]');
		var qtyInputs = document.querySelectorAll('.qty-input');
		var floatingCopyBtn = document.getElementById('floatingCopyBtn');
		var orderModal = document.getElementById('orderModal');
		var orderModalClose = document.getElementById('orderModalClose');
		var clientNameInput = document.getElementById('clientName');
		var clientCompanyInput = document.getElementById('clientCompany');
		var clientPhoneInput = document.getElementById('clientPhone');
		var orderSummaryBox = document.getElementById('orderSummaryBox');
		var orderValidationMsg = document.getElementById('orderValidationMsg');
		var copyOrderBtn = document.getElementById('copyOrderBtn');
		var saveOrderBtn = document.getElementById('saveOrderBtn');

		function setQtyInputValueRaw(input, value) {
			if (!input) return;
			var next = Number(value || 0);
			if (!isFinite(next) || next < 0) next = 0;
			next = Math.floor(next);
			input.value = String(next);
		}

		function saveQtyState() {
			var payload = {};
			qtyInputs.forEach(function(input) {
				var name = String(input.name || '');
				var m = name.match(/^qty\[(\d+)\]$/);
				if (!m) return;
				payload[m[1]] = Number(input.value || 0);
			});
			try { localStorage.setItem(storageKey, JSON.stringify(payload)); } catch (e) {}
		}

		function restoreQtyState() {
			var raw = '';
			try { raw = localStorage.getItem(storageKey) || ''; } catch (e) { raw = ''; }
			if (!raw) return;
			var data = null;
			try { data = JSON.parse(raw); } catch (e) { data = null; }
			if (!data || typeof data !== 'object') return;
			qtyInputs.forEach(function(input) {
				var name = String(input.name || '');
				var m = name.match(/^qty\[(\d+)\]$/);
				if (!m) return;
				if (!Object.prototype.hasOwnProperty.call(data, m[1])) return;
				setQtyInputValueRaw(input, data[m[1]]);
			});
		}

		function updateFloatingCopySummary() {
			var totalQty = 0;
			qtyInputs.forEach(function(input) {
				var qty = Number(input.value || 0);
				if (!isFinite(qty) || qty < 0) qty = 0;
				totalQty += qty;
			});
			floatingCopyBtn.textContent = 'Enviar pedido (' + String(totalQty) + ')';
		}

		function applyFilters() {
			var term = (search.value || '').toLowerCase();
			var selectedCategory = categoryFilter ? (categoryFilter.value || '') : '';
			cards.forEach(function(card) {
				var text = card.getAttribute('data-text') || '';
				var cardCategory = card.getAttribute('data-category') || '';
				var matchText = text.indexOf(term) >= 0;
				var matchCategory = selectedCategory === '' || selectedCategory === cardCategory;
				card.style.display = (matchText && matchCategory) ? '' : 'none';
			});

			categoryBlocks.forEach(function(block) {
				var cardsInBlock = block.querySelectorAll('.product-card');
				var hasVisible = false;
				cardsInBlock.forEach(function(card) {
					if (card.style.display !== 'none') {
						hasVisible = true;
					}
				});
				block.style.display = hasVisible ? '' : 'none';
			});
		}

		function setQtyInputValue(input, value) {
			setQtyInputValueRaw(input, value);
			try { input.dispatchEvent(new Event('input', { bubbles: true })); } catch (e) {}
			try { input.dispatchEvent(new Event('change', { bubbles: true })); } catch (e) {}
			updateFloatingCopySummary();
		}

		document.querySelectorAll('.js-qty-btn').forEach(function(btn) {
			btn.addEventListener('click', function() {
				var input = document.getElementById(btn.getAttribute('data-target'));
				if (!input) return;
				var step = Number(btn.getAttribute('data-step') || 0);
				var current = Number(input.value || 0);
				if (!isFinite(current) || current < 0) current = 0;
				var min = Number(input.getAttribute('min') || 0);
				var next = current + step;
				if (next < min) next = min;
				setQtyInputValue(input, next);
				saveQtyState();
				if (viewer && viewer.classList.contains('show')) {
					updateViewerQty();
				}
			});
		});

		qtyInputs.forEach(function(input) {
			input.addEventListener('input', function() {
				saveQtyState();
				updateFloatingCopySummary();
				if (viewer && viewer.classList.contains('show')) {
					updateViewerQty();
				}
			});
			input.addEventListener('change', function() {
				saveQtyState();
				updateFloatingCopySummary();
				if (viewer && viewer.classList.contains('show')) {
					updateViewerQty();
				}
			});
		});

		function getSelectedItems() {
			var lines = [];
			document.querySelectorAll('.product-card').forEach(function(card) {
				var btn = card.querySelector('.js-open-viewer');
				if (!btn) return;
				var itemId = String(btn.getAttribute('data-item-id') || '');
				if (!itemId) return;
				var input = document.getElementById('qty_' + itemId);
				if (!input) return;
				var qty = Number(input.value || 0);
				if (!isFinite(qty) || qty <= 0) return;
				var name = String(btn.getAttribute('data-name') || '');
				lines.push({
					item_id: Number(itemId),
					name: name,
					quantity: Math.floor(qty)
				});
			});
			return lines;
		}

		function copyToClipboard(text, onDone) {
			if (navigator.clipboard && window.isSecureContext) {
				navigator.clipboard.writeText(text).then(function() {
					if (typeof onDone === 'function') onDone();
				});
				return;
			}
			var ta = document.createElement('textarea');
			ta.value = text;
			ta.setAttribute('readonly', 'readonly');
			ta.style.position = 'absolute';
			ta.style.left = '-9999px';
			document.body.appendChild(ta);
			ta.select();
			document.execCommand('copy');
			document.body.removeChild(ta);
			if (typeof onDone === 'function') onDone();
		}

		function buildOrderSummary(items) {
			if (!items || !items.length) return 'No hay productos seleccionados.';
			var totalUnits = 0;
			var lines = items.map(function(it, idx) {
				totalUnits += Number(it.quantity || 0);
				return String(idx + 1) + '. ' + String(it.name || '') + ' x' + String(it.quantity || 0);
			});
			lines.push('');
			lines.push('Productos: ' + String(items.length));
			lines.push('Unidades: ' + String(totalUnits));
			return lines.join('\n');
		}

		function buildOrderCopyText(items) {
			var customerName = String(clientNameInput.value || '').trim();
			var customerCompany = String(clientCompanyInput.value || '').trim();
			var customerPhone = String(clientPhoneInput.value || '').trim();
			return [
				'Pedido catalogo',
				'Nombre: ' + customerName,
				'Empresa: ' + customerCompany,
				'Telefono: ' + customerPhone,
				'',
				buildOrderSummary(items)
			].join('\n');
		}

		function openOrderModal() {
			var items = getSelectedItems();
			if (!items.length) {
				alert('No hay productos seleccionados.');
				return;
			}
			orderSummaryBox.textContent = buildOrderSummary(items);
			orderModal.setAttribute('data-items', JSON.stringify(items));
			orderModal.classList.add('show');
			orderModal.setAttribute('aria-hidden', 'false');
		}

		function closeOrderModal() {
			orderModal.classList.remove('show');
			orderModal.setAttribute('aria-hidden', 'true');
		}

		function getModalItems() {
			var raw = orderModal.getAttribute('data-items') || '[]';
			try {
				var parsed = JSON.parse(raw);
				return Array.isArray(parsed) ? parsed : [];
			} catch (e) {
				return [];
			}
		}

		function markFieldValidity(input, isValid) {
			if (!input) return;
			if (isValid) input.classList.remove('invalid');
			else input.classList.add('invalid');
		}

		function validateCustomerFields() {
			var name = String(clientNameInput.value || '').trim();
			var company = String(clientCompanyInput.value || '').trim();
			var phone = String(clientPhoneInput.value || '').trim();

			var nameOk = name !== '';
			var companyOk = company !== '';
			var phoneOk = phone !== '';

			markFieldValidity(clientNameInput, nameOk);
			markFieldValidity(clientCompanyInput, companyOk);
			markFieldValidity(clientPhoneInput, phoneOk);

			var ok = nameOk && companyOk && phoneOk;
			if (orderValidationMsg) {
				orderValidationMsg.style.display = ok ? 'none' : 'block';
			}
			if (!ok) {
				if (!nameOk) clientNameInput.focus();
				else if (!companyOk) clientCompanyInput.focus();
				else clientPhoneInput.focus();
			}
			return ok;
		}

		floatingCopyBtn.addEventListener('click', function() {
			openOrderModal();
		});

		copyOrderBtn.addEventListener('click', function() {
			var items = getModalItems();
			var text = buildOrderCopyText(items);
			copyToClipboard(text, function() {
				var prev = copyOrderBtn.textContent;
				copyOrderBtn.textContent = 'Copiado';
				setTimeout(function() { copyOrderBtn.textContent = prev; }, 1200);
			});
		});

		[clientNameInput, clientCompanyInput, clientPhoneInput].forEach(function(input) {
			input.addEventListener('input', function() {
				markFieldValidity(input, String(input.value || '').trim() !== '');
				if (orderValidationMsg) {
					var hasInvalid = clientNameInput.classList.contains('invalid')
						|| clientCompanyInput.classList.contains('invalid')
						|| clientPhoneInput.classList.contains('invalid');
					if (!hasInvalid) orderValidationMsg.style.display = 'none';
				}
			});
		});

		saveOrderBtn.addEventListener('click', function() {
			var items = getModalItems();
			if (!items.length) {
				alert('No hay productos para guardar.');
				return;
			}
			var payload = {
				action: 'save_independent_order',
				customer: {
					name: String(clientNameInput.value || '').trim(),
					company: String(clientCompanyInput.value || '').trim(),
					phone: String(clientPhoneInput.value || '').trim()
				},
				items: items.map(function(it) {
					return {
						item_id: Number(it.item_id || 0),
						quantity: Number(it.quantity || 0)
					};
				})
			};

			if (!validateCustomerFields()) {
				return;
			}

			saveOrderBtn.disabled = true;
			saveOrderBtn.textContent = 'Guardando...';
			fetch(window.location.pathname, {
				method: 'POST',
				headers: { 'Content-Type': 'application/json' },
				body: JSON.stringify(payload)
			})
			.then(function(res) { return res.json(); })
			.then(function(data) {
				if (!data || !data.ok) {
					throw new Error((data && data.message) ? data.message : 'No se pudo guardar el pedido.');
				}
				var orderId = data && data.order_id ? String(data.order_id) : '';
				qtyInputs.forEach(function(input) { setQtyInputValueRaw(input, 0); });
				saveQtyState();
				updateFloatingCopySummary();
				orderSummaryBox.textContent = '';
				orderModal.setAttribute('data-items', '[]');
				closeOrderModal();
				var nextUrl = window.location.pathname + '?gracias=1';
				if (orderId) {
					nextUrl += '&pedido=' + encodeURIComponent(orderId);
				}
				window.location.href = nextUrl;
			})
			.catch(function(err) {
				alert(err && err.message ? err.message : 'Error al guardar el pedido.');
			})
			.finally(function() {
				saveOrderBtn.disabled = false;
				saveOrderBtn.textContent = 'Enviar pedido';
			});
		});

		orderModalClose.addEventListener('click', closeOrderModal);
		orderModal.addEventListener('click', function(e) {
			if (e.target === orderModal) closeOrderModal();
		});

		search.addEventListener('input', applyFilters);
		if (categoryFilter) {
			categoryFilter.addEventListener('change', applyFilters);
		}

		var viewer = document.getElementById('imgViewer');
		var viewerStage = document.getElementById('viewerStage');
		var viewerImg = document.getElementById('viewerImage');
		var viewerTitle = document.getElementById('viewerTitle');
		var viewerClose = document.getElementById('viewerClose');
		var viewerPrev = document.getElementById('viewerPrev');
		var viewerNext = document.getElementById('viewerNext');
		var viewerThumbs = document.getElementById('viewerThumbs');
		var viewerQtyMinus = document.getElementById('viewerQtyMinus');
		var viewerQtyPlus = document.getElementById('viewerQtyPlus');
		var viewerQtyValue = document.getElementById('viewerQtyValue');
		var currentImages = [];
		var currentIndex = 0;
		var currentViewerButtons = [];
		var currentProductIndex = 0;
		var currentProductItemId = 0;
		var touchStartX = null;
		var touchStartY = null;
		var isPinching = false;
		var pinchStartDistance = 0;
		var pinchStartScale = 1;
		var imageScale = 1;
		var imagePanX = 0;
		var imagePanY = 0;
		var isPanning = false;
		var panStartX = 0;
		var panStartY = 0;
		var panOriginX = 0;
		var panOriginY = 0;
		var lastTapAt = 0;
		var lastTapX = 0;
		var lastTapY = 0;

		function imageUrl(fileId) {
			return 'image.php?id=' + String(fileId || '');
		}

		function getCurrentQtyInput() {
			if (!currentProductItemId) return null;
			return document.getElementById('qty_' + String(currentProductItemId));
		}

		function updateViewerQty() {
			var qtyInput = getCurrentQtyInput();
			var qty = 0;
			if (qtyInput) {
				qty = Number(qtyInput.value || 0);
				if (!isFinite(qty) || qty < 0) qty = 0;
			}
			viewerQtyValue.value = String(Math.floor(qty));
		}

		function parseImagesFromButton(btn) {
			var images = [];
			try { images = JSON.parse(btn.getAttribute('data-images') || '[]'); } catch (e) { images = []; }
			return (images || []).filter(function(v) { return Number(v) > 0; });
		}

		function getVisibleViewerButtons() {
			var all = Array.prototype.slice.call(document.querySelectorAll('.js-open-viewer'));
			return all.filter(function(btn) {
				var card = btn.closest ? btn.closest('.product-card') : null;
				return !card || card.style.display !== 'none';
			});
		}

		function getCurrentProductBaseIndex() {
			currentViewerButtons = getVisibleViewerButtons();
			if (!currentViewerButtons.length) return -1;
			for (var i = 0; i < currentViewerButtons.length; i++) {
				if (String(currentViewerButtons[i].getAttribute('data-item-id') || '') === String(currentProductItemId || '')) {
					return i;
				}
			}
			if (currentProductIndex >= 0 && currentProductIndex < currentViewerButtons.length) {
				return currentProductIndex;
			}
			return -1;
		}

		function canMove(step) {
			if (!currentImages.length) return false;
			if (step < 0 && currentIndex > 0) return true;
			if (step > 0 && currentIndex < currentImages.length - 1) return true;
			var base = getCurrentProductBaseIndex();
			if (base < 0) return false;
			var nextProductIndex = base + step;
			return nextProductIndex >= 0 && nextProductIndex < currentViewerButtons.length;
		}

		function applyImageZoom() {
			viewerImg.style.transform = 'translate3d(' + String(imagePanX) + 'px,' + String(imagePanY) + 'px,0) scale(' + String(imageScale) + ')';
		}

		function resetImageZoom() {
			imageScale = 1;
			imagePanX = 0;
			imagePanY = 0;
			isPanning = false;
			applyImageZoom();
		}

		function clampPan() {
			if (imageScale <= 1) {
				imagePanX = 0;
				imagePanY = 0;
				return;
			}
			var maxX = (viewerImg.clientWidth * (imageScale - 1)) / 2;
			var maxY = (viewerImg.clientHeight * (imageScale - 1)) / 2;
			if (imagePanX > maxX) imagePanX = maxX;
			if (imagePanX < -maxX) imagePanX = -maxX;
			if (imagePanY > maxY) imagePanY = maxY;
			if (imagePanY < -maxY) imagePanY = -maxY;
		}

		function toggleImageZoom() {
			if (imageScale > 1) {
				resetImageZoom();
				return;
			}
			imageScale = 2;
			imagePanX = 0;
			imagePanY = 0;
			applyImageZoom();
		}

		function touchDistance(t1, t2) {
			var dx = t1.clientX - t2.clientX;
			var dy = t1.clientY - t2.clientY;
			return Math.sqrt((dx * dx) + (dy * dy));
		}

		function renderViewer() {
			if (!currentImages.length) return;
			var id = currentImages[currentIndex];
			viewerImg.src = imageUrl(id);
			viewerPrev.style.display = 'flex';
			viewerNext.style.display = 'flex';
			viewerPrev.disabled = !canMove(-1);
			viewerNext.disabled = !canMove(1);
			resetImageZoom();

			viewerThumbs.innerHTML = '';
			currentImages.forEach(function(imgId, idx) {
				var btn = document.createElement('button');
				btn.type = 'button';
				btn.className = 'viewer-thumb' + (idx === currentIndex ? ' active' : '');
				btn.addEventListener('click', function() {
					currentIndex = idx;
					renderViewer();
				});
				var im = document.createElement('img');
				im.src = imageUrl(imgId);
				im.alt = 'Miniatura';
				btn.appendChild(im);
				viewerThumbs.appendChild(btn);
			});
		}

		function tryMoveProduct(step) {
			currentViewerButtons = getVisibleViewerButtons();
			if (!currentViewerButtons.length) return false;
			var baseIndex = -1;
			for (var i = 0; i < currentViewerButtons.length; i++) {
				if (String(currentViewerButtons[i].getAttribute('data-item-id') || '') === String(currentProductItemId || '')) {
					baseIndex = i;
					break;
				}
			}
			if (baseIndex < 0) {
				baseIndex = currentProductIndex;
			}
			var nextProductIndex = baseIndex + step;
			if (nextProductIndex < 0 || nextProductIndex >= currentViewerButtons.length) return false;
			var nextBtn = currentViewerButtons[nextProductIndex];
			if (!nextBtn) return false;
			var nextImages = parseImagesFromButton(nextBtn);
			if (!nextImages.length) return false;

			currentProductIndex = nextProductIndex;
			currentProductItemId = Number(nextBtn.getAttribute('data-item-id') || 0);
			currentImages = nextImages;
			currentIndex = step > 0 ? 0 : (currentImages.length - 1);
			viewerTitle.textContent = nextBtn.getAttribute('data-name') || 'Imagen';
			renderViewer();
			updateViewerQty();
			return true;
		}

		function openViewer(name, images, sourceBtn) {
			currentImages = (images || []).filter(function(v) { return Number(v) > 0; });
			if (!currentImages.length) return;
			currentIndex = 0;
			currentViewerButtons = getVisibleViewerButtons();
			if (sourceBtn && currentViewerButtons.length) {
				var idx = currentViewerButtons.indexOf(sourceBtn);
				currentProductIndex = idx >= 0 ? idx : 0;
				currentProductItemId = Number(sourceBtn.getAttribute('data-item-id') || 0);
			} else {
				currentProductIndex = 0;
				currentProductItemId = 0;
			}
			viewerTitle.textContent = name || 'Imagen';
			renderViewer();
			updateViewerQty();
			viewer.classList.add('show');
			floatingCopyBtn.classList.add('hidden-by-viewer');
		}

		function closeViewer() {
			viewer.classList.remove('show');
			viewerImg.src = '';
			viewerThumbs.innerHTML = '';
			currentImages = [];
			currentIndex = 0;
			currentViewerButtons = [];
			currentProductIndex = 0;
			currentProductItemId = 0;
			isPinching = false;
			pinchStartDistance = 0;
			pinchStartScale = 1;
			resetImageZoom();
			viewerQtyValue.value = '0';
			floatingCopyBtn.classList.remove('hidden-by-viewer');
		}

		function move(step) {
			if (!currentImages.length) return;
			var nextIndex = currentIndex + step;
			if (nextIndex >= 0 && nextIndex < currentImages.length) {
				currentIndex = nextIndex;
				renderViewer();
				return;
			}
			if (tryMoveProduct(step)) {
				return;
			}
			currentIndex = step > 0 ? 0 : (currentImages.length - 1);
			renderViewer();
		}

		function onTouchStart(e) {
			if (!viewer.classList.contains('show')) return;
			if (e.touches && e.touches.length === 2) {
				isPinching = true;
				pinchStartDistance = touchDistance(e.touches[0], e.touches[1]);
				pinchStartScale = imageScale;
				touchStartX = null;
				touchStartY = null;
				e.preventDefault();
				return;
			}
			if (e.touches && e.touches.length === 1) {
				if (imageScale > 1) {
					isPanning = true;
					panStartX = e.touches[0].clientX;
					panStartY = e.touches[0].clientY;
					panOriginX = imagePanX;
					panOriginY = imagePanY;
					return;
				}
				touchStartX = e.touches[0].clientX;
				touchStartY = e.touches[0].clientY;
			}
		}

		function onTouchMove(e) {
			if (!viewer.classList.contains('show')) return;
			if (e.touches && e.touches.length === 2) {
				isPanning = false;
				if (!isPinching) {
					isPinching = true;
					pinchStartDistance = touchDistance(e.touches[0], e.touches[1]);
					pinchStartScale = imageScale;
				}
				var currentDistance = touchDistance(e.touches[0], e.touches[1]);
				if (pinchStartDistance > 0) {
					var nextScale = pinchStartScale * (currentDistance / pinchStartDistance);
					if (nextScale < 1) nextScale = 1;
					if (nextScale > 4) nextScale = 4;
					imageScale = nextScale;
					clampPan();
					applyImageZoom();
				}
				e.preventDefault();
				return;
			}
			if (isPanning && e.touches && e.touches.length === 1) {
				var dx = e.touches[0].clientX - panStartX;
				var dy = e.touches[0].clientY - panStartY;
				imagePanX = panOriginX + dx;
				imagePanY = panOriginY + dy;
				clampPan();
				applyImageZoom();
				e.preventDefault();
			}
		}

		function onTouchEnd(e) {
			if (!viewer.classList.contains('show')) return;
			if (isPinching) {
				if (!e.touches || e.touches.length < 2) {
					isPinching = false;
					pinchStartDistance = 0;
					pinchStartScale = imageScale;
				}
				return;
			}
			if (isPanning) {
				isPanning = false;
				return;
			}
			if (touchStartX === null || touchStartY === null) return;
			var t = e.changedTouches && e.changedTouches[0];
			if (!t) return;

			var deltaX = t.clientX - touchStartX;
			var deltaY = t.clientY - touchStartY;
			var absX = Math.abs(deltaX);
			var absY = Math.abs(deltaY);
			touchStartX = null;
			touchStartY = null;

			if (absX < 18 && absY < 18) {
				var now = Date.now();
				var tapDelta = now - lastTapAt;
				var dxTap = Math.abs(t.clientX - lastTapX);
				var dyTap = Math.abs(t.clientY - lastTapY);
				if (tapDelta > 0 && tapDelta < 320 && dxTap < 26 && dyTap < 26) {
					toggleImageZoom();
					lastTapAt = 0;
					return;
				}
				lastTapAt = now;
				lastTapX = t.clientX;
				lastTapY = t.clientY;
			}

			if (absX < 35 || absX <= absY) return;
			if (deltaX < 0) move(1);
			else move(-1);
		}

		document.querySelectorAll('.js-open-viewer').forEach(function(btn) {
			btn.addEventListener('click', function() {
				var images = [];
				try { images = JSON.parse(btn.getAttribute('data-images') || '[]'); } catch (e) { images = []; }
				openViewer(btn.getAttribute('data-name') || 'Imagen', images, btn);
			});
		});

		viewerClose.addEventListener('click', closeViewer);
		viewerPrev.addEventListener('click', function() { move(-1); });
		viewerNext.addEventListener('click', function() { move(1); });
		viewerQtyMinus.addEventListener('click', function() {
			var input = getCurrentQtyInput();
			if (!input) return;
			var current = Number(input.value || 0);
			if (!isFinite(current) || current < 0) current = 0;
			var next = current - 1;
			if (next < 0) next = 0;
			setQtyInputValue(input, next);
			saveQtyState();
			updateViewerQty();
		});
		viewerQtyPlus.addEventListener('click', function() {
			var input = getCurrentQtyInput();
			if (!input) return;
			var current = Number(input.value || 0);
			if (!isFinite(current) || current < 0) current = 0;
			setQtyInputValue(input, current + 1);
			saveQtyState();
			updateViewerQty();
		});
		viewer.addEventListener('click', function(e) {
			if (e.target === viewer) closeViewer();
		});
		viewerImg.addEventListener('dblclick', function(e) {
			e.preventDefault();
			toggleImageZoom();
		});
		viewerStage.addEventListener('touchstart', onTouchStart, { passive: false });
		viewerStage.addEventListener('touchmove', onTouchMove, { passive: false });
		viewerStage.addEventListener('touchend', onTouchEnd, { passive: false });
		document.addEventListener('keydown', function(e) {
			if (e.key === 'Escape' && orderModal.classList.contains('show')) {
				closeOrderModal();
				return;
			}
			if (!viewer.classList.contains('show')) return;
			if (e.key === 'Escape') closeViewer();
			if (e.key === 'ArrowLeft') move(-1);
			if (e.key === 'ArrowRight') move(1);
		});

		restoreQtyState();
		updateFloatingCopySummary();
		applyFilters();
	})();
	</script>
	<?php endif; ?>
</body>
</html>
