<!DOCTYPE html>
<html lang="es">
<head>
	<meta charset="utf-8">
	<meta name="viewport" content="width=device-width, initial-scale=1">
	<title><?php echo html_escape($catalog_name); ?></title>
	<?php
		$primary = !empty($template['primary_color']) ? $template['primary_color'] : '#d62828';
		$secondary = !empty($template['secondary_color']) ? $template['secondary_color'] : '#003049';
		$bg = !empty($template['bg_color']) ? $template['bg_color'] : '#fcfaf5';
		$image_bg = !empty($template['image_bg_color']) ? $template['image_bg_color'] : '#f4f1e6';
		$border = !empty($template['border_color']) ? $template['border_color'] : '#e4ddce';
		$text = !empty($template['text_color']) ? $template['text_color'] : '#0f172a';
		$muted_text = !empty($template['muted_text_color']) ? $template['muted_text_color'] : '#546278';
		$title_size = !empty($template['title_size_px']) ? (int) $template['title_size_px'] : 17;
		$desc_size = !empty($template['desc_size_px']) ? (int) $template['desc_size_px'] : 13;
		$price_size = !empty($template['price_size_px']) ? (int) $template['price_size_px'] : 24;
		$logo_size = !empty($template['logo_size_px']) ? (int) $template['logo_size_px'] : 64;
		$card_style = !empty($template['card_style']) ? $template['card_style'] : 'rounded';
		$radius = '16px';
		if ($card_style === 'soft') { $radius = '24px'; }
		if ($card_style === 'sharp') { $radius = '6px'; }
	?>
	<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 html_escape($text); ?>; --muted:<?php echo html_escape($muted_text); ?>; --brand:<?php echo html_escape($primary); ?>; --bg:<?php echo html_escape($bg); ?>; --img-bg:<?php echo html_escape($image_bg); ?>; --card:#fff; --line:<?php echo html_escape($border); ?>; --hero:<?php echo html_escape($secondary); ?>; --ok:#2a9d8f; --radius:<?php echo $radius; ?>; --title-size:<?php echo (int)$title_size; ?>px; --desc-size:<?php echo (int)$desc_size; ?>px; --price-size:<?php echo (int)$price_size; ?>px; --logo-size:<?php echo (int)$logo_size; ?>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 44px; }
		.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 0 7px; font-size:32px; letter-spacing:-.02em; }
		.hero p { margin:0; color:#e2efff; }
		.hero-head { display:flex; align-items:center; gap:12px; margin-bottom:0; }
		.hero-text { display:flex; flex-direction:column; gap:4px; }
		.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; }
		.badge { background:#e5faf7; color:#0b6e63; border-radius:999px; padding:6px 10px; font-weight:700; font-size:12px; }
		.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; }
		.price { font-size:var(--price-size); font-weight:800; color:var(--brand); }
		.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; }
		textarea { width:100%; border:1px solid #d6dde8; border-radius:11px; padding:10px; min-height:88px; font:inherit; }
		.btn { margin-top:10px; background:var(--brand); color:#fff; border:none; border-radius:10px; font:inherit; font-weight:800; padding:12px 18px; cursor:pointer; }
		.btn:hover { filter:brightness(.96); }
		.muted { color:var(--muted); font-size:13px; }
		.floating-buy { 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-buy:hover { filter:brightness(.96); }
		.floating-buy.hidden-by-viewer { display:none !important; }
		.buy-modal { position:fixed; inset:0; display:none; align-items:center; justify-content:center; z-index:2200; background:rgba(2,6,23,.62); padding:14px; }
		.buy-modal.show { display:flex; }
		.buy-card { width:min(560px, 96vw); background:#fff; border-radius:14px; border:1px solid #e2e8f0; box-shadow:0 16px 34px rgba(2,6,23,.25); }
		.buy-head { display:flex; align-items:center; justify-content:space-between; gap:8px; padding:12px 14px; border-bottom:1px solid #e2e8f0; }
		.buy-title { margin:0; font-size:19px; }
		.buy-close { border:none; background:#e2e8f0; border-radius:8px; padding:6px 10px; cursor:pointer; font-weight:700; }
		.buy-body { padding:14px; }
		.buy-actions { display:flex; justify-content:flex-end; gap:8px; margin-top:12px; }
		.btn-outline { background:#fff; color:#1e293b; border:1px solid #cbd5e1; }
		.terms-box { margin-top:12px; border:1px solid #d6dde8; border-radius:10px; background:#f8fafc; padding:10px; max-height:140px; overflow:auto; font-size:13px; color:#334155; }
		.terms-check { display:flex; align-items:flex-start; gap:8px; margin-top:8px; font-size:13px; color:#334155; }
		.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; }
			.price { font-size:calc(var(--price-size) - 2px); }
			.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-buy { right:12px; bottom:12px; width:calc(100vw - 24px); border-radius:12px; text-align:center; }
		}
	</style>
</head>
<body>
	<div class="wrap">
		<section class="hero">
			<div class="hero-head">
				<?php if (!empty($logo_file_id)): ?>
					<img class="logo" src="<?php echo site_url('catalog/image/'.(int)$logo_file_id); ?>" alt="Logo empresa">
				<?php endif; ?>
				<div class="hero-text">
					<h1><?php echo html_escape($catalog_name); ?></h1>
					<p>Cliente: <strong><?php echo html_escape($link['customer_name']); ?></strong> | Expira: <strong><?php echo html_escape($link['expires_at']); ?></strong></p>
					<?php if (empty($can_order)): ?>
						<p><span class="badge">Modo solo catalogo: precios y compra deshabilitados</span></p>
					<?php endif; ?>
				</div>
			</div>
		</section>
		<?php if (trim((string) $catalog_announcement) !== ''): ?>
			<div class="announcement"><?php echo nl2br(html_escape($catalog_announcement)); ?></div>
		<?php endif; ?>

		<form id="catalogOrderForm" method="post" action="<?php echo site_url('catalog/l/'.rawurlencode($link['token']).'/buy'); ?>">
			<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 $category_id => $category_name): ?>
						<option value="<?php echo (int) $category_id; ?>"><?php echo html_escape($category_name); ?></option>
					<?php endforeach; ?>
				</select>
			</div>

			<section id="productGrid">
				<?php
					$grouped_products = array();
					foreach ($products as $p)
					{
						$group_category_id = !empty($p['category_id']) ? (int) $p['category_id'] : 0;
						$group_category_name = trim((string) $p['category_name']) !== '' ? (string) $p['category_name'] : 'Sin categoria';
						if (!isset($grouped_products[$group_category_id]))
						{
							$grouped_products[$group_category_id] = array(
								'name' => $group_category_name,
								'items' => array()
							);
						}
						$grouped_products[$group_category_id]['items'][] = $p;
					}
				?>
				<?php foreach ($grouped_products as $group_category_id => $group_data): ?>
					<div class="category-block" data-category-group="<?php echo (int) $group_category_id; ?>">
						<h2 class="category-title"><?php echo html_escape($group_data['name']); ?></h2>
						<div class="grid">
							<?php foreach ($group_data['items'] as $p): ?>
								<?php $images_json = json_encode(array_values($p['image_ids'])); ?>
								<article
									class="card product-card"
									data-text="<?php echo html_escape(strtolower($p['name'].' '.$p['description'])); ?>"
									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 html_escape($p['name']); ?>"
								data-images="<?php echo html_escape($images_json); ?>"
							>
												<img class="image" src="<?php echo site_url('catalog/image/'.(int)$p['image_id']); ?>" alt="<?php echo html_escape($p['name']); ?>">
											</button>
										<?php else: ?>
											<div class="noimg">Sin imagen</div>
										<?php endif; ?>
									</div>
									<div class="body">
										<h3 class="name"><?php echo html_escape($p['name']); ?></h3>
										<?php $desc = trim(substr(strip_tags((string) $p['description']), 0, 90)); ?>
										<?php if ($desc !== ''): ?>
											<p class="desc"><?php echo html_escape($desc); ?></p>
										<?php endif; ?>
										<?php if (!empty($can_order)): ?>
											<div class="price"><?php echo html_escape($currency_symbol . number_format((float)$p['final_price'], 2)); ?></div>
											<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="<?php echo isset($draft_quantities[(int)$p['item_id']]) ? (float) $draft_quantities[(int)$p['item_id']] : 0; ?>" data-price="<?php echo html_escape((string) (float) $p['final_price']); ?>" 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>
										<?php endif; ?>
									</div>
								</article>
							<?php endforeach; ?>
						</div>
					</div>
				<?php endforeach; ?>
			</section>

			<input type="hidden" id="customer_note_input" name="customer_note" value="<?php echo html_escape(isset($draft_note) ? $draft_note : ''); ?>">
			<input type="hidden" id="terms_accepted_input" name="terms_accepted" value="0">
		</form>
	</div>

	<?php if (!empty($can_order)): ?>
		<button type="button" class="floating-buy" id="floatingBuyBtn">Comprar</button>
	<?php endif; ?>

	<?php if (!empty($can_order)): ?>
		<div class="buy-modal" id="buyModal" aria-hidden="true">
			<div class="buy-card" role="dialog" aria-modal="true" aria-labelledby="buyModalTitle">
				<div class="buy-head">
					<h3 class="buy-title" id="buyModalTitle">Confirmar pedido</h3>
					<button type="button" class="buy-close" id="buyModalClose">X</button>
				</div>
				<div class="buy-body">
					<p class="muted" style="margin-top:0;">Agrega un mensaje.</p>
					<textarea id="buyModalNote"><?php echo html_escape(isset($draft_note) ? $draft_note : ''); ?></textarea>
					<?php if (trim((string) $checkout_terms) !== ''): ?>
						<div class="terms-box"><?php echo nl2br(html_escape($checkout_terms)); ?></div>
						<label class="terms-check">
							<input type="checkbox" id="buyModalTermsCheck" value="1">
							<span>Acepto los terminos y condiciones.</span>
						</label>
					<?php endif; ?>
					<div class="buy-actions">
						<button type="button" class="btn btn-outline" id="buyModalCancel">Cancelar</button>
						<button type="button" class="btn" id="buyModalConfirm">Confirmar compra</button>
					</div>
				</div>
			</div>
		</div>
	<?php endif; ?>

	<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">
				<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>
				<?php if (!empty($can_order)): ?>
					<div class="viewer-order" id="viewerOrderControls">
						<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>
				<?php else: ?>
					<div class="viewer-order" aria-hidden="true"></div>
				<?php endif; ?>
				<button type="button" class="viewer-nav viewer-next" id="viewerNext">&#8250;</button>
			</div>
		</div>
	</div>

	<script>
		(function() {
			var search = document.getElementById('search');
			var categoryFilter = document.getElementById('categoryFilter');
			var cards = document.querySelectorAll('.product-card');
			var categoryBlocks = document.querySelectorAll('[data-category-group]');

			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';
				});
			}

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

			var viewer = document.getElementById('imgViewer');
			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 viewerStage = document.querySelector('.viewer-stage');
			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 '<?php echo site_url('catalog/image/'); ?>' + String(fileId || '');
			}

			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 applyImageZoom() {
				if (!viewerImg) return;
				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 (!viewerImg) return;
				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 getCurrentQtyInput() {
				if (!currentProductItemId) return null;
				return document.getElementById('qty_' + String(currentProductItemId));
			}

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

			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 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 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');
				var floatingBuy = document.getElementById('floatingBuyBtn');
				if (floatingBuy) {
					floatingBuy.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();
				if (viewerQtyValue) viewerQtyValue.value = '0';
				var floatingBuy = document.getElementById('floatingBuyBtn');
				if (floatingBuy) {
					floatingBuy.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); });
			if (viewerQtyMinus) {
				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;
					setQtyInputValueRaw(input, next);
					updateViewerQty();
					if (typeof saveDraftToStorage === 'function') {
						saveDraftToStorage();
					}
				});
			}
			if (viewerQtyPlus) {
				viewerQtyPlus.addEventListener('click', function() {
					var input = getCurrentQtyInput();
					if (!input) return;
					var current = Number(input.value || 0);
					if (!isFinite(current) || current < 0) current = 0;
					setQtyInputValueRaw(input, current + 1);
					updateViewerQty();
					if (typeof saveDraftToStorage === 'function') {
						saveDraftToStorage();
					}
				});
			}
			viewer.addEventListener('click', function(e) {
				if (e.target === viewer) closeViewer();
			});
			if (viewerImg) {
				viewerImg.addEventListener('dblclick', function(e) {
					e.preventDefault();
					toggleImageZoom();
				});
			}
			if (viewerStage) {
				viewerStage.addEventListener('touchstart', onTouchStart, { passive: false });
				viewerStage.addEventListener('touchmove', onTouchMove, { passive: false });
				viewerStage.addEventListener('touchend', onTouchEnd, { passive: false });
			}
			document.addEventListener('keydown', function(e) {
				if (!viewer.classList.contains('show')) return;
				if (e.key === 'Escape') closeViewer();
				if (e.key === 'ArrowLeft') move(-1);
				if (e.key === 'ArrowRight') move(1);
			});

			<?php if (!empty($can_order)): ?>
				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) {}
					if (viewer && viewer.classList.contains('show')) {
						var name = String(input.name || '');
						var match = name.match(/^qty\[(\d+)\]$/);
						if (match && Number(match[1]) === Number(currentProductItemId || 0)) {
							updateViewerQty();
						}
					}
					updateFloatingBuySummary();
				}

				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);
						var min = Number(input.getAttribute('min') || 0);
						var next = current + step;
						if (next < min) next = min;
						setQtyInputValue(input, next);
						saveDraftToStorage();
					});
				});

				var orderForm = document.getElementById('catalogOrderForm');
				var floatingBuyBtn = document.getElementById('floatingBuyBtn');
				var buyModal = document.getElementById('buyModal');
				var buyModalClose = document.getElementById('buyModalClose');
				var buyModalCancel = document.getElementById('buyModalCancel');
				var buyModalConfirm = document.getElementById('buyModalConfirm');
				var buyModalNote = document.getElementById('buyModalNote');
				var customerNoteInput = document.getElementById('customer_note_input');
				var termsAcceptedInput = document.getElementById('terms_accepted_input');
				var buyModalTermsCheck = document.getElementById('buyModalTermsCheck');
				var qtyInputs = document.querySelectorAll('.qty-input');
				var draftStorageKey = 'catalog_draft_' + <?php echo json_encode((string) $link['token']); ?>;
				var currencySymbol = <?php echo json_encode((string) $currency_symbol); ?>;

				function updateFloatingBuySummary() {
					if (!floatingBuyBtn) return;
					var totalQty = 0;
					var totalAmount = 0;
					qtyInputs.forEach(function(input) {
						var qty = Number(input.value || 0);
						if (!isFinite(qty) || qty < 0) qty = 0;
						var price = Number(input.getAttribute('data-price') || 0);
						if (!isFinite(price) || price < 0) price = 0;
						totalQty += qty;
						totalAmount += (qty * price);
					});
					floatingBuyBtn.textContent = 'Comprar (' + String(totalQty) + ') ' + String(currencySymbol) + Number(totalAmount).toFixed(2);
				}

				function collectDraftFromInputs() {
					var quantities = {};
					qtyInputs.forEach(function(input) {
						var name = String(input.name || '');
						var match = name.match(/^qty\[(\d+)\]$/);
						if (!match) return;
						var itemId = match[1];
						var value = Number(input.value || 0);
						if (!isFinite(value) || value < 0) value = 0;
						quantities[itemId] = value;
					});
					return {
						quantities: quantities,
						note: String((buyModalNote && buyModalNote.value) ? buyModalNote.value : '')
					};
				}

				function saveDraftToStorage() {
					try {
						window.localStorage.setItem(draftStorageKey, JSON.stringify(collectDraftFromInputs()));
					} catch (e) {}
					updateFloatingBuySummary();
				}

				function restoreDraftFromStorage() {
					var raw = '';
					try { raw = window.localStorage.getItem(draftStorageKey) || ''; } catch (e) { raw = ''; }
					if (!raw) return;
					var data = null;
					try { data = JSON.parse(raw); } catch (e) { data = null; }
					if (!data || typeof data !== 'object') return;

					var quantities = data.quantities && typeof data.quantities === 'object' ? data.quantities : {};
					qtyInputs.forEach(function(input) {
						var name = String(input.name || '');
						var match = name.match(/^qty\[(\d+)\]$/);
						if (!match) return;
						var itemId = match[1];
						if (!Object.prototype.hasOwnProperty.call(quantities, itemId)) return;
						var value = Number(quantities[itemId]);
						if (!isFinite(value) || value < 0) value = 0;
						input.value = String(value);
					});

					if (buyModalNote && typeof data.note === 'string') {
						buyModalNote.value = data.note;
					}
					if (customerNoteInput && typeof data.note === 'string') {
						customerNoteInput.value = data.note;
					}
				}

				function hasSelectedItems() {
					for (var i = 0; i < qtyInputs.length; i++) {
						if (Number(qtyInputs[i].value || 0) > 0) {
							return true;
						}
					}
					return false;
				}

				function openBuyModal() {
					if (!hasSelectedItems()) {
						alert('Debes seleccionar al menos un producto.');
						return;
					}
					if (termsAcceptedInput) {
						termsAcceptedInput.value = '0';
					}
					if (buyModalTermsCheck) {
						buyModalTermsCheck.checked = false;
					}
					buyModal.classList.add('show');
					buyModalNote.focus();
				}

				function closeBuyModal() {
					buyModal.classList.remove('show');
				}

				restoreDraftFromStorage();
				updateFloatingBuySummary();
				qtyInputs.forEach(function(input) {
					input.addEventListener('input', saveDraftToStorage);
					input.addEventListener('change', saveDraftToStorage);
					input.addEventListener('input', function() {
						if (!viewer || !viewer.classList.contains('show')) return;
						var name = String(input.name || '');
						var match = name.match(/^qty\[(\d+)\]$/);
						if (!match) return;
						if (Number(match[1]) === Number(currentProductItemId || 0)) {
							updateViewerQty();
						}
					});
					input.addEventListener('change', function() {
						if (!viewer || !viewer.classList.contains('show')) return;
						var name = String(input.name || '');
						var match = name.match(/^qty\[(\d+)\]$/);
						if (!match) return;
						if (Number(match[1]) === Number(currentProductItemId || 0)) {
							updateViewerQty();
						}
					});
				});
				if (buyModalNote) {
					buyModalNote.addEventListener('input', saveDraftToStorage);
				}

				floatingBuyBtn.addEventListener('click', openBuyModal);
				buyModalClose.addEventListener('click', closeBuyModal);
				buyModalCancel.addEventListener('click', closeBuyModal);

				buyModalConfirm.addEventListener('click', function() {
					if (buyModalTermsCheck) {
						if (!buyModalTermsCheck.checked) {
							alert('Debes aceptar los terminos y condiciones para continuar.');
							return;
						}
						if (termsAcceptedInput) {
							termsAcceptedInput.value = '1';
						}
					} else if (termsAcceptedInput) {
						termsAcceptedInput.value = '0';
					}
					customerNoteInput.value = buyModalNote.value || '';
					saveDraftToStorage();
					orderForm.submit();
				});

				buyModal.addEventListener('click', function(e) {
					if (e.target === buyModal) {
						closeBuyModal();
					}
				});
			<?php endif; ?>
		})();
	</script>
</body>
</html>
