{"id":245,"date":"2026-06-11T01:39:39","date_gmt":"2026-06-11T01:39:39","guid":{"rendered":"https:\/\/vuelveid.com\/?page_id=245"},"modified":"2026-10-27T00:56:29","modified_gmt":"2026-10-27T00:56:29","slug":"order","status":"publish","type":"page","link":"https:\/\/vuelveid.com\/es\/order\/","title":{"rendered":"\u00a1Bienvenido! Gracias por dar este paso para proteger a tu mascota. Comienza escogiendo el color de su placa."},"content":{"rendered":"\n<!--\n  ============================================================================\n  VUELVE ID \u2014 P\u00e1gina de orden v13 (oferta $25 todo incluido)\n  v13.1: la opci\u00f3n del tel\u00e9fono queda APAGADA (CFG.PHONE_ENABLED = false).\n       El c\u00f3digo se queda listo: para encenderla, pon true y activa el\n       snippet PHP \"Tel\u00e9fono en la placa\".\n  v13: opci\u00f3n \"Tel\u00e9fono al lado de la placa\" (+$3 todo incluido \u2192 $28).\n       Casilla debajo del nombre; si la marcan, aparece el campo de tel\u00e9fono\n       (10 d\u00edgitos, obligatorio solo si la casilla est\u00e1 marcada). Viaja como\n       dato de la l\u00ednea (tag_phone) igual que pet_name. Requiere el snippet\n       PHP \"Tel\u00e9fono en la placa\", que suma el precio en WooCommerce.\n       TranslatePress: traduce los textos nuevos de la casilla.\n  v12: los colores salen SOLOS de WooCommerce. Requiere el snippet PHP\n       \"Cat\u00e1logo de la p\u00e1gina de orden\" (endpoint \/wp-json\/vuelve\/v1\/catalog).\n       En cada producto: marca \"Mostrar en \/order\", escoge el color del\n       cuadrito, la etiqueta opcional (\"Nuevo\") y el orden. Producto nuevo,\n       agotado o en borrador \u2192 aparece \/ desaparece sin tocar este HTML.\n       El PRECIO tambi\u00e9n viene de WooCommerce: cada placa calcula su total\n       (precio + IVU por l\u00ednea, igual que Woo). El precio grande y la etiqueta\n       de oferta muestran el total del color escogido.\n       Los botones escritos abajo son solo el RESPALDO si el cat\u00e1logo no carga.\n       TranslatePress: el texto de la etiqueta de oferta cambi\u00f3 (ahora el monto\n       va en su propio <span>); vuelve a traducir \"Offer \u00b7 $25 all included\".\n  v11: 8 colores (nuevo: Sol, ID 453, anaranjado). La etiqueta \"New\" pasa\n       de Brav\u00edo a Sol. Grid de colores a 6 sub-columnas: cada color ocupa 2,\n       as\u00ed una \u00faltima fila de 2 queda centrada (3 + 3 + 2) sin achicar los\n       cuadritos. Sol no tiene foto de respaldo en el HTML: la toma de la\n       Store API; si esa llamada falla, se ve el color s\u00f3lido y la placa\n       dibujada (SVG) en vez de una imagen rota.\n  v10: paso \"Tama\u00f1o\" ACTIVO (Large\/medium pets \/ Small\/Toy pets). Mismo producto por\n       color; el tama\u00f1o viaja como dato de la l\u00ednea (tag_size=regular|small)\n       igual que pet_name. Requiere el snippet PHP \"vid_tag_size\" para que\n       aparezca en el carrito, la orden y el email. Sin productos nuevos.\n  v9: 7 colores (nuevo: Brav\u00edo, ID 393, rojo) y fotos nuevas de WooCommerce.\n      Al cargar, la p\u00e1gina le pide a la Store API de WooCommerce la foto de\n      cada producto: si cambias una foto en WooCommerce, aqu\u00ed cambia sola.\n      Los links escritos abajo son solo el respaldo si esa llamada falla.\n      Cuadritos = miniatura 300x300; foto grande = 768x1024 (no la -scaled).\n  v8: texto fuente en INGL\u00c9S para que TranslatePress traduzca a \/es\/.\n      Los textos que arma el JavaScript (botones, carrito, total, errores,\n      mensaje de WhatsApp) salen del diccionario I18N seg\u00fan el idioma de la\n      URL (\/es\/ = espa\u00f1ol), porque TranslatePress no traduce frases que\n      cambian (\"Add Bosque tag for Canela\").\n  v7: bot\u00f3n \"Pagar con ATH M\u00f3vil\" va DIRECTO al checkout con ATH ya\n      seleccionado (si el carrito est\u00e1 vac\u00edo, a\u00f1ade la placa escogida en el\n      mismo toque); un solo link de WhatsApp para preguntas; checkout en\n      espa\u00f1ol (\/es\/checkout\/) cuando la p\u00e1gina est\u00e1 en \/es\/.\n      Requiere: CFG.ATH_GATEWAY_ID + snippet PHP \"vid_pay\" (preselecci\u00f3n).\n  Fusi\u00f3n de: v5 (IDs reales, fotos, checkout secuencial a WooCommerce)\n           + demo (titular con A\/B, vista de escaneo, \"qu\u00e9 es y qu\u00e9 no es\",\n             eventos con whitelist, ViewContent medible).\n\n  C\u00d3MO USARLO EN WORDPRESS\n    1. Borra la l\u00ednea <title> de arriba (solo sirve para el artifact).\n    2. Pega todo en un bloque \"Custom HTML\" de la p\u00e1gina \/order.\n    3. El texto est\u00e1 en INGL\u00c9S (fuente de TranslatePress). Traduce los\n       textos fijos en el editor de TranslatePress; los din\u00e1micos ya vienen\n       traducidos en I18N dentro del script.\n\n  LO QUE WOOCOMMERCE TIENE QUE TENER PARA QUE EL TOTAL CUADRE (cr\u00edtico)\n    \u00b7 Precios SIN IVU: $22.42 \u2192 22.42 + IVU 11.5% = $25.00 exacto\n    \u00b7 Env\u00edo: gratis (Free shipping) mientras dure la oferta\n    \u00b7 \"Round tax at subtotal level\": DESMARCADO (redondeo por l\u00ednea)\n    v12: el precio de cada placa sale de WooCommerce; ya no se edita aqu\u00ed.\n    FALLBACK_NET_CENTS solo se usa si el cat\u00e1logo no carga. Si la tienda\n    tiene \"precios con impuesto incluido\", la consola lo avisa.\n\n  DETERMIN\u00cdSTICO\n    \u00b7 Un solo CONFIG. Misma config => misma p\u00e1gina, mismos totales.\n    \u00b7 El total se calcula con LA MISMA agrupaci\u00f3n de l\u00edneas que se manda a\n      WooCommerce, y redondea el IVU por l\u00ednea igual que Woo.\n    \u00b7 Eventos del Pixel salen de una whitelist; un nombre mal escrito\n      lanza error en vez de mandar data basura a Meta.\n    \u00b7 A\/B de titular asignado por hash del visitante (misma persona =\n      misma versi\u00f3n). La versi\u00f3n viaja en todos los eventos.\n  ============================================================================\n-->\n<style>\n\/* Layout: una columna de 560px; orden primero, informaci\u00f3n debajo. *\/\n\/* Paleta de marca Vuelve ID (\u00fanico look claro, deliberado: va dentro del tema de WP). *\/\n.vsp {\n  --bg: #FFF8F2;\n  --surface: #FFFFFF;\n  --soft: #FFFAF5;\n  --ink: #3D2B1F;\n  --ink-soft: #5C4332;\n  --brand: #C97B2E;\n  --brand-dark: #A8611C;\n  --gold: #E8A84E;\n  --line: #F0E0CC;\n  --ok: #2E7D32;\n  --wa: #1F8F4E;\n  --note-bg: #1F3A5F;\n  --note-ink: #F3F7FC;\n  --serif: Georgia, \"Times New Roman\", serif;\n  --sans: system-ui, -apple-system, \"Segoe UI\", Roboto, sans-serif;\n}\n:root { color-scheme: light; }\nbody:has(#vsp-root) { background: #FFF8F2; }\n\n.vsp, .vsp * { box-sizing: border-box; }\n.vsp * { margin: 0; padding: 0; }\n.vsp {\n  font-family: var(--serif);\n  color: var(--ink);\n  background: var(--bg);\n  max-width: 560px;\n  margin: 0 auto;\n  padding-inline: 16px;\n  padding-block: 0 60px;\n  counter-reset: vsp-step;\n  font-size: 17px;\n  line-height: 1.55;\n}\n.vsp.has-bar { padding-block-end: 150px; }\n.vsp [hidden] { display: none !important; }\n.vsp :focus-visible { outline: 3px solid var(--brand); outline-offset: 2px; }\n\n\/* \u2500\u2500 Barra de oferta: una sola promo, sin timers \u2500\u2500 *\/\n.vsp-promo {\n  margin-inline: -16px;\n  background: var(--ink);\n  color: var(--bg);\n  text-align: center;\n  font: 600 15px\/1.4 var(--sans);\n  padding: 10px 16px;\n}\n\n\/* \u2500\u2500 Hero \u2500\u2500 *\/\n.vsp-hero { padding-block: 22px 6px; display: grid; gap: 10px; }\n.vsp-h1 {\n  font-size: clamp(30px, 8vw, 40px);\n  line-height: 1.12;\n  font-weight: 700;\n  text-wrap: balance;\n}\n.vsp-lede { font-size: 18px; color: var(--ink-soft); max-width: 60ch; }\n.vsp-notgps { font: 15px\/1.45 var(--sans); color: var(--ink-soft); }\n.vsp-notgps b { color: var(--ink); }\n\n.vsp-image-wrap {\n  position: relative;\n  background: #F5E8D8;\n  border-radius: 24px;\n  overflow: hidden;\n  aspect-ratio: 1;\n  max-width: 100%;\n  margin-block: 14px 10px;\n}\n.vsp-image-wrap img {\n  display: block; width: 100%; height: 100%;\n  object-fit: cover;\n  transition: opacity .18s ease;\n}\n.vsp-image-wrap img.fading { opacity: 0; }\n\/* Fallback dibujado si la foto no carga (tambi\u00e9n es lo que ves en el artifact). *\/\n.vsp-hero-svg { position: absolute; inset: 0; display: grid; place-items: center; }\n.vsp-hero-svg svg { width: 62%; height: auto; filter: drop-shadow(0 12px 20px rgba(61,43,31,.2)); }\n.vsp-badge {\n  position: absolute; top: 14px; left: 14px;\n  background: var(--ink); color: var(--gold);\n  font: 600 14px\/1 var(--sans);\n  padding: 9px 14px; border-radius: 20px;\n}\n.vsp-engrave {\n  position: absolute; left: 14px; bottom: 14px;\n  background: rgba(255,255,255,.94); color: var(--ink);\n  font: 15px\/1.3 var(--sans);\n  padding: 8px 12px; border-radius: 12px;\n}\n.vsp-engrave b { font-family: var(--serif); font-size: 17px; }\n\n.vsp-price { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; }\n.vsp-price strong { font-size: 34px; line-height: 1; }\n.vsp-price span { font: 16px\/1.4 var(--sans); color: var(--ink-soft); }\n\n\/* \u2500\u2500 Pasos numerados con contador CSS (es una secuencia real) \u2500\u2500 *\/\n.vsp-step {\n  display: flex; align-items: center; gap: 10px;\n  font-size: 19px; font-weight: 700;\n  margin-block: 26px 12px;\n}\n.vsp-step::before {\n  counter-increment: vsp-step;\n  content: counter(vsp-step);\n  flex-shrink: 0;\n  width: 32px; height: 32px; border-radius: 50%;\n  background: var(--ink); color: var(--gold);\n  font-size: 16px;\n  display: grid; place-items: center;\n}\n\n\/* \u2500\u2500 Tama\u00f1o \u2500\u2500 *\/\n.vsp-sizes { display: grid; grid-template-columns: repeat(2, minmax(0,1fr)); gap: 12px; }\n.vsp-size-btn {\n  min-height: 80px;\n  background: var(--surface); color: var(--ink);\n  border: 3px solid #E0D0BE; border-radius: 16px;\n  padding: 12px 10px;\n  font: inherit; cursor: pointer;\n  display: grid; place-items: center; gap: 2px;\n}\n.vsp-size-name { font-size: 18px; font-weight: 700; }\n.vsp-size-sub { font: 14px\/1.3 var(--sans); color: var(--ink-soft); }\n.vsp-size-btn.active { border-color: var(--ink); background: #FFF1E0; }\n.vsp-size-btn.active .vsp-size-name::before { content: \"\u2713 \"; color: var(--ok); }\n\n\/* \u2500\u2500 Colores: 3\u00d72, objetivos grandes \u2500\u2500 *\/\n.vsp-colors { display: grid; grid-template-columns: repeat(6, minmax(0,1fr)); gap: 14px; }\n.vsp-color-item { display: flex; flex-direction: column; min-width: 0; grid-column: span 2; }\n.vsp-color-btn {\n  aspect-ratio: 1; max-width: 100%;\n  border-radius: 14px; border: 3px solid transparent;\n  background: var(--c, #ddd);\n  padding: 0; cursor: pointer;\n  position: relative; overflow: hidden;\n  transition: transform .2s ease, border-color .2s ease;\n}\n.vsp-color-btn img { display: block; width: 100%; height: 100%; object-fit: cover; border-radius: 10px; }\n.vsp-color-btn.broken img { display: none; }   \/* si la foto no carga, queda el color s\u00f3lido *\/\n.vsp-color-btn.active { border-color: var(--ink); transform: translateY(-3px); }\n.vsp-color-btn.active::after {\n  content: \"\u2713\"; position: absolute; top: 6px; right: 6px;\n  width: 26px; height: 26px; border-radius: 50%;\n  background: var(--ink); color: #fff; font-size: 15px;\n  display: grid; place-items: center;\n}\n.vsp-color-name { font-size: 16px; text-align: center; margin-top: 6px; }\n\/* Centrado de la \u00faltima fila sin importar cu\u00e1ntos colores haya.\n   6 pistas = 3 columnas visuales de 2 pistas cada una.\n     \u00b7 Sobra 1 (3n+1): ese color empieza en la pista 3 \u2192 columna del medio.\n     \u00b7 Sobran 2 (3n+2): el pen\u00faltimo empieza en la pista 2 \u2192 los dos quedan\n       centrados con medio cuadrito de margen a cada lado.\n   Con 8 colores aplica el segundo caso: 3 + 3 + 2 centrado. *\/\n.vsp-color-item:last-child:nth-child(3n+1) { grid-column: 3 \/ span 2; }\n.vsp-color-item:nth-last-child(2):nth-child(3n+1) { grid-column: 2 \/ span 2; }\n\/* Etiqueta del color nuevo, encima del cuadrito *\/\n.vsp-color-item { position: relative; }\n.vsp-new {\n  position: absolute; top: 8px; left: 8px; z-index: 1;   \/* arriba-izquierda; el \u2713 va arriba-derecha *\/\n  background: var(--ink); color: var(--gold);\n  font: 700 12px\/1 var(--sans); letter-spacing: .04em; text-transform: uppercase;\n  padding: 5px 9px; border-radius: 999px; white-space: nowrap; pointer-events: none;\n}\n\n\/* \u2500\u2500 Nombre (opcional, abierto por defecto: es el gancho) \u2500\u2500 *\/\n.vsp-field-label { display: block; font-size: 19px; font-weight: 700; margin-block: 26px 10px; }\n.vsp-name-input {\n  width: 100%; min-height: 60px;\n  padding: 14px 18px;\n  border: 2px solid #D9C3A9; border-radius: 14px;\n  font: 20px var(--serif); color: var(--ink); background: var(--surface);\n}\n.vsp-name-input:focus { border-color: var(--brand); outline: none; }\n.vsp-name-input::placeholder { color: #A88C70; font-style: italic; }\n.vsp-hint { font: 15px\/1.45 var(--sans); color: var(--ink-soft); margin-top: 6px; }\n\n\/* \u2500\u2500 v13: tel\u00e9fono en la placa (opcional, +$3) \u2500\u2500 *\/\n.vsp-phone-opt {\n  display: flex; gap: 12px; align-items: flex-start; cursor: pointer;\n  margin-top: 18px; padding: 14px 16px;\n  background: var(--surface); border: 2px solid #E0D0BE; border-radius: 14px;\n}\n.vsp-phone-opt input { width: 24px; height: 24px; margin-top: 2px; flex: none; accent-color: var(--ink); cursor: pointer; }\n.vsp-phone-opt b { font-size: 17px; }\n.vsp-phone-opt small { font: 14px\/1.4 var(--sans); color: var(--ink-soft); }\n.vsp-phone-price { display: inline-block; background: var(--ink); color: var(--gold); font: 700 13px\/1 var(--sans); padding: 4px 8px; border-radius: 999px; margin-left: 4px; vertical-align: 2px; }\n.vsp-phone-opt.on { border-color: var(--ink); background: #FFF1E0; }\n.vsp-phone-wrap { margin-top: 10px; }\n.vsp-name-input.bad { border-color: #B42318; }\n.vsp-cart-phone { font: 14px var(--sans); color: var(--ink-soft); }\n\n\/* \u2500\u2500 Bot\u00f3n a\u00f1adir \u2500\u2500 *\/\n.vsp-add-btn {\n  width: 100%; min-height: 60px; margin-top: 18px;\n  background: var(--ink); color: #fff;\n  border: 0; border-radius: 14px; padding: 16px;\n  font: 700 19px var(--serif); cursor: pointer;\n}\n.vsp-add-btn:hover { background: var(--brand-dark); }\n.vsp-add-btn:disabled { opacity: .45; cursor: not-allowed; background: var(--ink); }\n\n.vsp-error {\n  background: #FFF4F4; border: 1.5px solid #FFCCCC; border-radius: 10px;\n  padding: 12px 14px; margin-top: 12px;\n  font: 15px\/1.45 var(--sans); color: #B42318;\n}\n\n\/* \u2500\u2500 Carrito \u2500\u2500 *\/\n.vsp-cart { margin-block: 24px 14px; }\n.vsp-cart-title { display: flex; justify-content: space-between; font-weight: 700; margin-bottom: 10px; }\n.vsp-cart-item {\n  display: flex; align-items: center; gap: 12px;\n  background: var(--surface); border: 1.5px solid var(--line); border-radius: 12px;\n  padding: 10px 12px; margin-bottom: 8px;\n}\n.vsp-cart-thumb { width: 52px; height: 52px; border-radius: 8px; flex-shrink: 0; background: var(--c, #ddd); object-fit: cover; }\n.vsp-cart-info { flex: 1; min-width: 0; }\n.vsp-cart-name { font-size: 17px; font-weight: 600; overflow-wrap: anywhere; }\n.vsp-cart-name.no-name { font: italic 15px var(--serif); color: #7A5C47; font-weight: 400; }\n.vsp-cart-model { font: 14px var(--sans); color: #6B6B6B; }\n.vsp-cart-price { font-weight: 700; color: var(--brand-dark); font-variant-numeric: tabular-nums; }\n.vsp-cart-remove {\n  width: 44px; height: 44px; flex-shrink: 0;\n  background: #FFF4F4; border: 1.5px solid #FFCCCC; border-radius: 10px;\n  color: #B42318; font-size: 18px; cursor: pointer;\n}\n\n\/* \u2500\u2500 Total: un n\u00famero grande, desglose peque\u00f1o por transparencia \u2500\u2500 *\/\n.vsp-total { background: var(--ink); color: #fff; border-radius: 14px; padding: 18px 20px; }\n.vsp-total-final { display: flex; justify-content: space-between; align-items: baseline; }\n.vsp-total-label { font-size: 17px; }\n.vsp-total-price { color: var(--gold); font-size: 30px; font-weight: 700; font-variant-numeric: tabular-nums; }\n.vsp-total-note { font: 14px\/1.5 var(--sans); color: rgba(255,255,255,.75); margin-top: 6px; font-variant-numeric: tabular-nums; }\n\n\/* \u2500\u2500 Pago \/ confianza \/ WhatsApp \u2500\u2500 *\/\n.vsp-pay { display: flex; flex-wrap: wrap; justify-content: center; gap: 8px; margin-block: 20px 12px; }\n.vsp-pay-chip { font: 15px var(--sans); background: var(--surface); border: 1.5px solid var(--line); border-radius: 20px; padding: 8px 14px; }\n.vsp-trust { display: flex; justify-content: center; flex-wrap: wrap; gap: 6px 16px; font: 15px var(--sans); color: var(--ink-soft); margin-bottom: 14px; }\n.vsp-whatsapp {\n  display: flex; align-items: center; justify-content: center; gap: 8px;\n  width: 100%; min-height: 56px; padding: 14px;\n  background: var(--surface); color: var(--ink);\n  border: 2px solid var(--wa); border-radius: 14px;\n  font: 17px var(--serif); text-decoration: none;\n}\n\/* Bot\u00f3n principal de pago (ATH M\u00f3vil). Es <button>: dispara el checkout, no un link. *\/\n.vsp-ath {\n  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 2px;\n  width: 100%; min-height: 64px; margin-top: 16px; padding: 12px 14px;\n  background: var(--wa); color: #fff; border: 0; border-radius: 14px;\n  font: 700 19px var(--serif); text-align: center; cursor: pointer;\n}\n.vsp-ath:hover { background: #18773F; }\n.vsp-ath:disabled { opacity: .7; cursor: wait; }\n.vsp-ath small { font: 400 14px\/1.3 var(--sans); color: rgba(255,255,255,.9); }\n\/* Alternativa discreta: mismo checkout, sin preseleccionar m\u00e9todo *\/\n.vsp-alt-pay {\n  display: block; width: 100%; min-height: 44px; margin-block: 6px 12px;\n  background: none; border: 0; cursor: pointer;\n  font: 16px var(--sans); color: var(--ink-soft); text-decoration: underline;\n}\n\/* WhatsApp: un solo link de texto para preguntas (no compite con el bot\u00f3n de pago) *\/\n.vsp-wa-link {\n  display: flex; align-items: center; justify-content: center; gap: 8px;\n  min-height: 48px; padding: 8px;\n  font: 17px var(--serif); color: var(--wa); text-decoration: underline; text-align: center;\n}\n\n\/* \u2500\u2500 Secciones informativas \u2500\u2500 *\/\n.vsp-section { padding-block: 34px 0; }\n.vsp-section + .vsp-section { border-top: 1px solid var(--line); margin-top: 34px; }\n.vsp-h2 { font-size: 26px; line-height: 1.2; margin-bottom: 10px; text-wrap: balance; }\n.vsp-p { max-width: 62ch; margin-bottom: 12px; }\n\n\/* Vista de escaneo: hace concreto el \"inteligente\" *\/\n.vsp-scan {\n  background: var(--surface); border: 1px solid var(--line); border-radius: 28px;\n  padding: 18px; max-width: 300px; margin: 14px auto 0;\n  box-shadow: 0 12px 30px rgba(61,43,31,.12);\n  font-family: var(--sans);\n}\n.vsp-scan-notch { width: 60px; height: 5px; border-radius: 3px; background: var(--line); margin: 0 auto 14px; }\n.vsp-scan-pet { display: flex; gap: 12px; align-items: center; margin-bottom: 6px; }\n.vsp-scan-avatar { width: 64px; height: 64px; border-radius: 50%; background: var(--c, #2F7DB8); display: grid; place-items: center; flex: none; }\n.vsp-scan-avatar svg { width: 34px; height: 34px; }\n.vsp-scan-name { font: 700 22px var(--serif); overflow-wrap: anywhere; }\n.vsp-chip { display: inline-block; background: rgba(46,125,50,.12); color: var(--ok); border-radius: 999px; padding: 2px 10px; font-size: 14px; font-weight: 600; }\n.vsp-scan-btn { min-height: 48px; border-radius: 12px; margin-top: 10px; display: grid; place-items: center; font-weight: 600; color: #fff; }\n.vsp-scan-btn.call { background: var(--brand); }\n.vsp-scan-btn.loc { background: var(--wa); }\n\n\/* C\u00f3mo funciona *\/\n.vsp-how { list-style: none; counter-reset: how; display: grid; gap: 16px; }\n.vsp-how li { counter-increment: how; display: grid; grid-template-columns: 40px 1fr; gap: 12px; align-items: start; }\n.vsp-how li::before {\n  content: counter(how);\n  width: 40px; height: 40px; border-radius: 50%;\n  background: var(--brand); color: #fff;\n  display: grid; place-items: center; font-weight: 700;\n}\n\n\/* Qu\u00e9 es \/ qu\u00e9 no es *\/\n.vsp-define { background: var(--surface); border-left: 5px solid var(--brand); border-radius: 0 12px 12px 0; padding: 18px; }\n\n\/* Dos maneras de escanear (2 columnas, se leen de un vistazo en el celular) *\/\n.vsp-ways { display: grid; grid-template-columns: repeat(2, minmax(0,1fr)); gap: 12px; margin-block: 14px; }\n.vsp-way {\n  background: var(--surface); border: 1.5px solid var(--line); border-radius: 14px;\n  padding: 16px 12px; display: grid; justify-items: center; text-align: center; gap: 6px;\n}\n.vsp-way svg { width: 38px; height: 38px; stroke: var(--brand); fill: none; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }\n.vsp-way b { font-size: 17px; }\n.vsp-way span { font: 15px\/1.4 var(--sans); color: var(--ink-soft); }\n\n\/* Mensaje clave en may\u00fasculas (CSS, as\u00ed el texto fuente queda normal para TranslatePress) *\/\n#vsp-root .vsp-caps { text-transform: uppercase; letter-spacing: .02em; color: var(--brand-dark); }\n\n\/* Pregunta honesta (microchip) *\/\n.vsp-callout { background: #FFF1E0; border-radius: 14px; padding: 16px 18px; margin-block: 14px; }\n\n\/* Tabla microchip vs Vuelve. #vsp-root para que el tema de WP no la pise. *\/\n#vsp-root .vsp-compare {\n  width: 100%; border-collapse: collapse; margin: 14px 0 0;\n  font: 15px\/1.4 var(--sans); background: var(--surface); border: 1.5px solid var(--line);\n}\n#vsp-root .vsp-compare th, #vsp-root .vsp-compare td {\n  padding: 10px 8px; border: 0; border-bottom: 1px solid var(--line);\n  text-align: left; vertical-align: top; color: var(--ink); background: none;\n}\n#vsp-root .vsp-compare thead th { background: var(--ink); color: #fff; font: 700 15px var(--serif); }\n#vsp-root .vsp-compare tbody th { font-weight: 600; width: 28%; }\n#vsp-root .vsp-compare td.yes { color: var(--ok); font-weight: 600; }\n\n.vsp-count { font-size: 44px; font-weight: 700; line-height: 1; font-variant-numeric: tabular-nums; }\n\n\/* Beneficios 3\u00d72 *\/\n.vsp-benefits { display: grid; grid-template-columns: repeat(3, minmax(0,1fr)); gap: 18px 8px; margin-top: 8px; }\n.vsp-benefit { display: grid; justify-items: center; text-align: center; gap: 8px; }\n.vsp-benefit-icon {\n  width: 64px; height: 64px; border-radius: 50%;\n  display: grid; place-items: center;\n  background: #FFF1E0; border: 1.5px solid var(--line);\n}\n.vsp-benefit-icon svg { width: 30px; height: 30px; stroke: var(--brand); fill: none; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }\n.vsp-benefit-label { font: 15px\/1.25 var(--sans); }\n\n\/* Capturas (carrusel horizontal con scroll-snap) *\/\n.vsp-shots { display: flex; gap: 12px; overflow-x: auto; scroll-snap-type: x mandatory; scrollbar-width: none; padding-bottom: 4px; margin-top: 10px; }\n.vsp-shots::-webkit-scrollbar { display: none; }\n.vsp-shots img { flex: 0 0 auto; width: 240px; height: auto; border-radius: 16px; scroll-snap-align: center; background: #F5E8D8; }\n.vsp-ref-img { width: 100%; height: auto; border-radius: 20px; display: block; margin-top: 10px; }\n\n\/* FAQ *\/\n.vsp-faq details { background: var(--soft); border: 1.5px solid var(--line); border-radius: 12px; padding: 16px 18px; margin-bottom: 10px; }\n.vsp-faq summary { font-size: 17px; cursor: pointer; list-style: none; display: flex; justify-content: space-between; gap: 12px; }\n.vsp-faq summary::-webkit-details-marker { display: none; }\n.vsp-faq summary::after { content: \"+\"; color: var(--brand); font-size: 24px; line-height: 1; }\n.vsp-faq details[open] summary::after { content: \"\u2212\"; }\n.vsp-faq p { font: 16px\/1.6 var(--sans); color: var(--ink-soft); margin-top: 10px; }\n\n\/* \u2500\u2500 Barra fija de checkout \u2500\u2500 *\/\n.vsp-bar {\n  position: fixed; left: 0; right: 0; bottom: 0; z-index: 9999;\n  background: rgba(255,248,242,.97);\n  border-top: 1.5px solid var(--line);\n  padding: 12px 16px calc(12px + env(safe-area-inset-bottom, 0px));\n  transform: translateY(110%);\n  transition: transform .28s cubic-bezier(.22,1,.36,1);\n}\n.vsp-bar.visible { transform: translateY(0); }\n.vsp-bar-inner { max-width: 560px; margin: 0 auto; display: flex; align-items: center; gap: 12px; }\n.vsp-bar-total { font: 700 22px Georgia, serif; color: #3D2B1F; font-variant-numeric: tabular-nums; }\n.vsp-bar-sub { font: 13px system-ui, sans-serif; color: #6B4E3A; }\n.vsp-cta {\n  flex: 1; min-height: 60px;\n  background: #C97B2E; color: #fff;\n  border: 0; border-radius: 14px; padding: 14px;\n  font: 700 19px Georgia, serif; cursor: pointer;\n}\n.vsp-cta:disabled { opacity: .7; cursor: wait; }\n\n\/* \u2500\u2500 Solo demo (fuera de vuelveid.com): notas del porqu\u00e9 + log de eventos \u2500\u2500 *\/\n.vsp-note {\n  display: none; background: var(--note-bg); color: var(--note-ink);\n  border-radius: 10px; padding: 12px 14px; margin-top: 12px;\n  font: 14px\/1.45 var(--sans);\n}\n.vsp-note b { display: block; margin-bottom: 2px; }\n.vsp.explain .vsp-note { display: block; }\n.vsp-dock {\n  position: fixed; top: env(safe-area-inset-top, 0px); left: 0; right: 0; z-index: 10000;\n  background: #3D2B1F; color: #FFF8F2;\n  display: flex; gap: 10px; align-items: center; padding: 8px 12px;\n  font: 13px\/1.3 ui-monospace, Menlo, monospace;\n}\n.vsp-dock button { flex: none; min-height: 40px; border: 0; border-radius: 10px; padding: 0 12px; font: 600 14px system-ui, sans-serif; background: #FFF8F2; color: #3D2B1F; cursor: pointer; }\n.vsp-dock .log { flex: 1; min-width: 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }\nbody.vsp-has-dock #vsp-root { padding-top: 56px; }\n\n\/* \u2500\u2500 Blindaje: el tema de WordPress no puede pisar esto \u2500\u2500 *\/\n#vsp-root h1, #vsp-root h2, #vsp-root h3 { color: var(--ink); background: none; text-transform: none; letter-spacing: normal; font-family: var(--serif); }\n#vsp-root p, #vsp-root b, #vsp-root strong { color: inherit; background: none; }\n#vsp-root .vsp-hero { background: none; color: var(--ink); }\n#vsp-root button { text-transform: none; letter-spacing: normal; line-height: 1.3; }\n#vsp-root .vsp-image-wrap img { height: 100%; max-width: 100%; }\n\n\/* \u2500\u2500 Galer\u00eda de fotos reales de clientes \u2500\u2500 *\/\n#vsp-root .vsp-pets { display: grid; grid-template-columns: repeat(2, minmax(0,1fr)); gap: 8px; margin-block: 14px 26px; }\n#vsp-root .vsp-pets img { display: block; width: 100%; height: auto; aspect-ratio: 1; max-width: 100%; object-fit: cover; border-radius: 14px; background: #F5E8D8; }\n#vsp-root .vsp-pets img:first-child { grid-column: 1 \/ -1; aspect-ratio: 4 \/ 3; }\n#vsp-root .vsp-pets img:last-child:nth-child(even) { grid-column: 1 \/ -1; aspect-ratio: 4 \/ 3; }\n\n@media (prefers-reduced-motion: reduce) {\n  .vsp *, .vsp-bar { transition: none !important; animation: none !important; }\n}\n<\/style>\n\n<div class=\"vsp\" id=\"vsp-root\">\n\n\n  <div class=\"vsp-image-wrap\" id=\"vsp-image-wrap\">\n    <img fetchpriority=\"high\" id=\"vsp-main-img\" width=\"600\" height=\"600\" decoding=\"async\" fetchpriority=\"high\"\n         src=\"https:\/\/vuelveid.com\/wp-content\/uploads\/2026\/10\/IMG_8345-768x1024.jpg\"\n         alt=\"Vuelve ID tag, Bosque model\">\n    <div class=\"vsp-hero-svg\" id=\"vsp-hero-svg\" hidden aria-hidden=\"true\">\n      <svg viewBox=\"0 0 200 240\">\n        <circle cx=\"100\" cy=\"22\" r=\"12\" fill=\"none\" stroke=\"#9A9A9A\" stroke-width=\"5\"\/>\n        <ellipse id=\"vsp-svg-body\" cx=\"100\" cy=\"128\" rx=\"88\" ry=\"100\" fill=\"#3E7B4F\"\/>\n        <ellipse cx=\"100\" cy=\"128\" rx=\"74\" ry=\"86\" fill=\"none\" stroke=\"rgba(255,255,255,.55)\" stroke-width=\"2\"\/>\n        <text x=\"100\" y=\"66\" text-anchor=\"middle\" font-family=\"Georgia\" font-size=\"17\" font-weight=\"700\" fill=\"#fff\" letter-spacing=\"2\">VUELVE<\/text>\n        <rect x=\"70\" y=\"80\" width=\"60\" height=\"60\" rx=\"6\" fill=\"#fff\"\/>\n        <g fill=\"#3D2B1F\"><rect x=\"76\" y=\"86\" width=\"16\" height=\"16\"\/><rect x=\"108\" y=\"86\" width=\"16\" height=\"16\"\/><rect x=\"76\" y=\"118\" width=\"16\" height=\"16\"\/><rect x=\"98\" y=\"108\" width=\"6\" height=\"6\"\/><rect x=\"110\" y=\"120\" width=\"6\" height=\"6\"\/><rect x=\"118\" y=\"110\" width=\"6\" height=\"6\"\/><\/g>\n        <rect x=\"40\" y=\"160\" width=\"120\" height=\"36\" rx=\"8\" fill=\"rgba(255,255,255,.92)\"\/>\n        <text id=\"vsp-svg-name\" x=\"100\" y=\"184\" text-anchor=\"middle\" font-family=\"Georgia\" font-size=\"19\" font-weight=\"700\" fill=\"#3D2B1F\">Your pet<\/text>\n      <\/svg>\n    <\/div>\n    <span class=\"vsp-badge\">Offer \u00b7 <span id=\"vsp-badge-amt\">$25<\/span> all included<\/span>\n    <span class=\"vsp-engrave\" id=\"vsp-engrave\" hidden>Engraved: <b id=\"vsp-engrave-name\"><\/b> + unique ID<\/span>\n  <\/div>\n\n  <div class=\"vsp-price\">\n    <strong id=\"vsp-price-amt\">$25<\/strong>\n    <span>per tag \u00b7 shipping and IVU included<\/span>\n  <\/div>\n\n  <!-- ===================== ORDEN ===================== -->\n  <div id=\"vsp-order\">\n\n    <!-- Paso: tama\u00f1o. Visible si CFG.SIZES_ENABLED; el tama\u00f1o viaja como tag_size. -->\n    <div id=\"vsp-size-step\" hidden>\n      <span class=\"vsp-step\">Size<\/span>\n      <div class=\"vsp-sizes\" id=\"vsp-sizes\">\n        <button type=\"button\" class=\"vsp-size-btn active\" aria-pressed=\"true\" data-size=\"regular\">\n          <span class=\"vsp-size-name\">Large\/medium pets<\/span>\n          <span class=\"vsp-size-sub\">30 mm<\/span>\n        <\/button>\n        <button type=\"button\" class=\"vsp-size-btn\" aria-pressed=\"false\" data-size=\"small\">\n          <span class=\"vsp-size-name\">Small\/Toy pets<\/span>\n          <span class=\"vsp-size-sub\">Smaller and lighter<\/span>\n        <\/button>\n      <\/div>\n    <\/div>\n\n    <!-- Paso: color. v12: RESPALDO. Si el cat\u00e1logo de WooCommerce carga, esta\n         lista se reemplaza completa. data-id = ID del producto en WooCommerce. -->\n    <span class=\"vsp-step\">Choose the color<\/span>\n    <div class=\"vsp-colors\" id=\"vsp-colors\">\n      <div class=\"vsp-color-item\">\n        <button type=\"button\" class=\"vsp-color-btn\" aria-pressed=\"false\" style=\"--c:#B3261E\" data-id=\"393\" data-name=\"Brav\u00edo\" data-hex=\"#B3261E\" data-img=\"https:\/\/vuelveid.com\/wp-content\/uploads\/2026\/10\/IMG_8340-768x1024.jpg\">\n          <img src=\"https:\/\/vuelveid.com\/wp-content\/uploads\/2026\/10\/IMG_8340-300x300.jpg\" alt=\"\" width=\"120\" height=\"120\" decoding=\"async\">\n        <\/button>\n        <span class=\"vsp-color-name\">Brav\u00edo<\/span>\n      <\/div>\n      <div class=\"vsp-color-item\">\n        <button type=\"button\" class=\"vsp-color-btn active\" aria-pressed=\"true\" style=\"--c:#3E7B4F\" data-id=\"56\" data-name=\"Bosque\" data-hex=\"#3E7B4F\" data-img=\"https:\/\/vuelveid.com\/wp-content\/uploads\/2026\/10\/IMG_8345-768x1024.jpg\">\n          <img src=\"https:\/\/vuelveid.com\/wp-content\/uploads\/2026\/10\/IMG_8345-300x300.jpg\" alt=\"\" width=\"120\" height=\"120\" decoding=\"async\">\n        <\/button>\n        <span class=\"vsp-color-name\">Bosque<\/span>\n      <\/div>\n      <div class=\"vsp-color-item\">\n        <button type=\"button\" class=\"vsp-color-btn\" aria-pressed=\"false\" style=\"--c:#E68AA8\" data-id=\"51\" data-name=\"Flamenco\" data-hex=\"#E68AA8\" data-img=\"https:\/\/vuelveid.com\/wp-content\/uploads\/2026\/10\/IMG_8347-768x1024.jpg\">\n          <img src=\"https:\/\/vuelveid.com\/wp-content\/uploads\/2026\/10\/IMG_8347-300x300.jpg\" alt=\"\" width=\"120\" height=\"120\" decoding=\"async\">\n        <\/button>\n        <span class=\"vsp-color-name\">Flamenco<\/span>\n      <\/div>\n      <div class=\"vsp-color-item\">\n        <button type=\"button\" class=\"vsp-color-btn\" aria-pressed=\"false\" style=\"--c:#E9E4DA\" data-id=\"52\" data-name=\"Luna\" data-hex=\"#E9E4DA\" data-img=\"https:\/\/vuelveid.com\/wp-content\/uploads\/2026\/10\/IMG_8346-768x1024.jpg\">\n          <img loading=\"lazy\" src=\"https:\/\/vuelveid.com\/wp-content\/uploads\/2026\/10\/IMG_8346-300x300.jpg\" alt=\"\" width=\"120\" height=\"120\" decoding=\"async\">\n        <\/button>\n        <span class=\"vsp-color-name\">Luna<\/span>\n      <\/div>\n      <div class=\"vsp-color-item\">\n        <button type=\"button\" class=\"vsp-color-btn\" aria-pressed=\"false\" style=\"--c:#2B2D42\" data-id=\"54\" data-name=\"Noche\" data-hex=\"#2B2D42\" data-img=\"https:\/\/vuelveid.com\/wp-content\/uploads\/2026\/10\/IMG_8341-768x1024.jpg\">\n          <img loading=\"lazy\" src=\"https:\/\/vuelveid.com\/wp-content\/uploads\/2026\/10\/IMG_8341-300x300.jpg\" alt=\"\" width=\"120\" height=\"120\" decoding=\"async\">\n        <\/button>\n        <span class=\"vsp-color-name\">Noche<\/span>\n      <\/div>\n      <div class=\"vsp-color-item\">\n        <button type=\"button\" class=\"vsp-color-btn\" aria-pressed=\"false\" style=\"--c:#2F7DB8\" data-id=\"53\" data-name=\"Oc\u00e9ano\" data-hex=\"#2F7DB8\" data-img=\"https:\/\/vuelveid.com\/wp-content\/uploads\/2026\/10\/IMG_8344-768x1024.jpg\">\n          <img loading=\"lazy\" src=\"https:\/\/vuelveid.com\/wp-content\/uploads\/2026\/10\/IMG_8344-300x300.jpg\" alt=\"\" width=\"120\" height=\"120\" decoding=\"async\">\n        <\/button>\n        <span class=\"vsp-color-name\">Oc\u00e9ano<\/span>\n      <\/div>\n      <div class=\"vsp-color-item\">\n        <button type=\"button\" class=\"vsp-color-btn\" aria-pressed=\"false\" style=\"--c:#9A6B45\" data-id=\"55\" data-name=\"Tierra\" data-hex=\"#9A6B45\" data-img=\"https:\/\/vuelveid.com\/wp-content\/uploads\/2026\/10\/IMG_8342-768x1024.jpg\">\n          <img loading=\"lazy\" src=\"https:\/\/vuelveid.com\/wp-content\/uploads\/2026\/10\/IMG_8342-300x300.jpg\" alt=\"\" width=\"120\" height=\"120\" decoding=\"async\">\n        <\/button>\n        <span class=\"vsp-color-name\">Tierra<\/span>\n      <\/div>\n      <!-- v11: Sol (ID 453). Sin foto de respaldo: data-img vac\u00edo y <img> sin src.\n           La Store API los llena al cargar. El <img> tiene que existir porque\n           syncProductPhotos() solo actualiza el cuadrito si lo encuentra. -->\n      <div class=\"vsp-color-item\">\n        <button type=\"button\" class=\"vsp-color-btn\" aria-pressed=\"false\" style=\"--c:#E8772E\" data-id=\"453\" data-name=\"Sol\" data-hex=\"#E8772E\" data-img=\"\">\n          <img loading=\"lazy\" alt=\"\" width=\"120\" height=\"120\" decoding=\"async\">\n        <\/button>\n        <span class=\"vsp-new\">New<\/span>\n        <span class=\"vsp-color-name\">Sol<\/span>\n      <\/div>\n    <\/div>\n\n    <!-- Paso: nombre. Visible (no colapsado): ver su nombre en la placa es el gancho. -->\n    <label class=\"vsp-field-label\" for=\"vsp-pet-name\">Name to engrave <span class=\"vsp-hint\" style=\"display:inline\">(optional)<\/span><\/label>\n    <input type=\"text\" id=\"vsp-pet-name\" class=\"vsp-name-input\" placeholder=\"e.g. Canela\" maxlength=\"20\" autocomplete=\"off\">\n    <p class=\"vsp-hint\">If you leave it blank, the tag comes with its unique ID and the profile works the same.<\/p>\n\n    <!-- v13: phone on the side of the tag. Optional, +$3 all included. -->\n    <label class=\"vsp-phone-opt\" id=\"vsp-phone-opt\" for=\"vsp-phone-on\" hidden>\n      <input type=\"checkbox\" id=\"vsp-phone-on\">\n      <span><b>Add your phone number on the side of the tag?<\/b><span class=\"vsp-phone-price\" id=\"vsp-phone-price\">+$3<\/span><br>\n        <small>So whoever finds your pet can call you, even without scanning.<\/small><\/span>\n    <\/label>\n    <div class=\"vsp-phone-wrap\" id=\"vsp-phone-wrap\" hidden>\n      <input type=\"tel\" id=\"vsp-phone\" class=\"vsp-name-input\" inputmode=\"numeric\" autocomplete=\"tel-national\" placeholder=\"(787) 555-1234\" maxlength=\"14\">\n      <p class=\"vsp-hint\">This number is engraved on the tag: anyone who finds it will see it.<\/p>\n    <\/div>\n\n    <button type=\"button\" class=\"vsp-add-btn\" id=\"vsp-add-btn\">Add Bosque tag<\/button>\n    <div class=\"vsp-error\" id=\"vsp-error\" role=\"alert\" hidden><\/div>\n\n    <div class=\"vsp-cart\" id=\"vsp-cart\" hidden>\n      <div class=\"vsp-cart-title\"><span>Your order<\/span><span id=\"vsp-cart-count\"><\/span><\/div>\n      <div id=\"vsp-cart-items\"><\/div>\n    <\/div>\n\n    <div class=\"vsp-total\" id=\"vsp-total\" hidden>\n      <div class=\"vsp-total-final\">\n        <span class=\"vsp-total-label\">Total to pay<\/span>\n        <span class=\"vsp-total-price\" id=\"vsp-total-price\">$0.00<\/span>\n      <\/div>\n      <div class=\"vsp-total-note\" id=\"vsp-total-note\"><\/div>\n    <\/div>\n\n    <!-- Pago: ATH M\u00f3vil es la acci\u00f3n principal. Un toque = carrito + checkout\n         con ATH ya seleccionado. Si no hay placas en la orden, a\u00f1ade la que\n         est\u00e1 escogida arriba (color + nombre) en ese mismo toque. -->\n    <button type=\"button\" class=\"vsp-ath\" id=\"vsp-ath\">\n      <span>Pay with ATH M\u00f3vil<\/span>\n      <small>We take you straight to checkout<\/small>\n    <\/button>\n    <button type=\"button\" class=\"vsp-alt-pay\" id=\"vsp-alt-pay\">or pay with card or PayPal<\/button>\n\n    <div class=\"vsp-trust\">\n      <span>Designed and Built in Puerto Rico<\/span>\n      <span>\ud83d\udd12 Secure payment<\/span>\n      <span>\ud83d\udce6 We ship island-wide<\/span>\n    <\/div>\n    <a class=\"vsp-wa-link\" id=\"vsp-whatsapp\" target=\"_blank\" rel=\"noopener\" href=\"https:\/\/wa.me\/19393620967\">\n      \ud83d\udcac Have questions? Message us on WhatsApp, tap here\n    <\/a>\n  <\/div>\n\n  <!-- ===================== INFORMACI\u00d3N ===================== -->\n  <section class=\"vsp-section\" data-needs-images>\n    <h2 class=\"vsp-h2\">Their profile and vaccines<\/h2>\n    <p class=\"vsp-p\">Several pets in one account.<\/p>\n    <div class=\"vsp-shots\">\n      <img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/vuelveid.com\/wp-content\/uploads\/2026\/08\/WhatsApp-Image-2026-08-26-at-20.40.42.jpeg\" alt=\"Digital profile, screen 1\">\n      <img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/vuelveid.com\/wp-content\/uploads\/2026\/08\/WhatsApp-Image-2026-08-26-at-20.40.42-1.jpeg\" alt=\"Digital profile, screen 2\">\n      <img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/vuelveid.com\/wp-content\/uploads\/2026\/08\/WhatsApp-Image-2026-08-26-at-20.40.42-3.jpeg\" alt=\"Account with several pets\">\n      <img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/vuelveid.com\/wp-content\/uploads\/2026\/08\/vaccines.jpeg\" alt=\"Vaccine record\">\n    <\/div>\n  <\/section>\n\n  <section class=\"vsp-section\">\n    <h2 class=\"vsp-h2\">How it works<\/h2>\n    <ol class=\"vsp-how\">\n      <li><div><b>Choose the color.<\/b> We engrave your pet\u2019s name and their unique ID.<\/div><\/li>\n      <li><div><b>Scan the tag when it arrives<\/b> and create their profile: your phone, their photo, allergies and vaccines. Edit it anytime.<\/div><\/li>\n      <li><div><b>Anyone who sees it<\/b> scans it and instantly knows who to call. If they share their location, you get it by email with a Google Maps link.<\/div><\/li>\n    <\/ol>\n  <\/section>\n\n  <!-- Qu\u00e9 es: identificaci\u00f3n r\u00e1pida + las dos maneras de escanear -->\n  <section class=\"vsp-section\">\n    <h2 class=\"vsp-h2\">What is Vuelve ID?<\/h2>\n    <p class=\"vsp-p\">A tag designed so that, if someone finds your pet, they know in seconds who they belong to and how to call you. No app to download.<\/p>\n    <div class=\"vsp-ways\">\n      <div class=\"vsp-way\">\n        <svg viewBox=\"0 0 24 24\" aria-hidden=\"true\"><path d=\"M3 8V5a2 2 0 0 1 2-2h3\"\/><path d=\"M16 3h3a2 2 0 0 1 2 2v3\"\/><path d=\"M21 16v3a2 2 0 0 1-2 2h-3\"\/><path d=\"M8 21H5a2 2 0 0 1-2-2v-3\"\/><rect x=\"7\" y=\"7\" width=\"4\" height=\"4\"\/><rect x=\"13\" y=\"13\" width=\"4\" height=\"4\"\/><path d=\"M13 7h4v2\"\/><path d=\"M7 15v2h2\"\/><\/svg>\n        <b>With the camera<\/b>\n        <span>Point the phone at the QR code on the tag<\/span>\n      <\/div>\n      <div class=\"vsp-way\">\n        <svg viewBox=\"0 0 24 24\" aria-hidden=\"true\"><rect x=\"6\" y=\"2\" width=\"12\" height=\"20\" rx=\"2.5\"\/><path d=\"M10 18h4\"\/><path d=\"M9.5 9.5a3.5 3.5 0 0 1 5 0\"\/><path d=\"M7.5 7.5a6.5 6.5 0 0 1 9 0\"\/><\/svg>\n        <b>With a tap<\/b>\n        <span>Hold the phone close to the tag (NFC)<\/span>\n      <\/div>\n    <\/div>\n    <p class=\"vsp-p\">Either way, their profile opens: their name, their photo and your phone number to call you.<\/p>\n    <p class=\"vsp-notgps\"><b>It is not a GPS:<\/b> it does not track in real time and has no battery. It is their identification.<\/p>\n  <\/section>\n\n  <!-- Microchip vs. Vuelve: qui\u00e9n lo puede leer es la diferencia real -->\n  <section class=\"vsp-section\">\n    <h2 class=\"vsp-h2\">How is it different from a microchip?<\/h2>\n    <p class=\"vsp-p\">A microchip can only be read with a special scanner, and only vets and shelters have one. Vuelve ID can be read by anyone with a phone.<\/p>\n    <div class=\"vsp-callout\">\n      <p class=\"vsp-p\"><b>Be honest: how many times have you seen someone put a lost dog in their own car to take it to the vet? Would you?<\/b><\/p>\n      <p class=\"vsp-p\" style=\"margin:0\">What most people do is walk up, pet them and look for a tag. That is where we come in.<\/p>\n    <\/div>\n    <table class=\"vsp-compare\">\n      <thead>\n        <tr><th scope=\"col\"><\/th><th scope=\"col\">Microchip<\/th><th scope=\"col\">Vuelve ID<\/th><\/tr>\n      <\/thead>\n      <tbody>\n        <tr><th scope=\"row\">Who can read it?<\/th><td>A vet or shelter with a scanner<\/td><td class=\"yes\">Anyone with a phone<\/td><\/tr>\n        <tr><th scope=\"row\">What do they see?<\/th><td>A number that has to be looked up in a registry<\/td><td class=\"yes\">Their name, their photo and your phone number<\/td><\/tr>\n        <tr><th scope=\"row\">Updating your info?<\/th><td>By contacting the chip registry<\/td><td class=\"yes\">You do it yourself, from your phone<\/td><\/tr>\n        <tr><th scope=\"row\">Where does it go?<\/th><td>Under the skin<\/td><td>On the collar<\/td><\/tr>\n      <\/tbody>\n    <\/table>\n    <p class=\"vsp-p\" style=\"margin-top:12px\">They don\u2019t compete: ideally, have both. The microchip is permanent; Vuelve ID is what any neighbor can read on the spot.<\/p>\n    <p class=\"vsp-p\">And best of all: you can change your info anytime, without going to the vet or calling anyone. <b class=\"vsp-caps\">You have full control of your pet\u2019s profile and identification.<\/b><\/p>\n  <\/section>\n\n  <section class=\"vsp-section\">\n    <p class=\"vsp-count\" id=\"vsp-count\">+100<\/p>\n    <p class=\"vsp-p\">pets registered in Puerto Rico. Here are some of them, in photos from their families.<\/p>\n    <!-- Fotos reales de clientes. La primera va grande (ancho completo);\n         el resto en 2 columnas. Si sobra una al final, tambi\u00e9n va a ancho\n         completo para que no quede sola con un hueco al lado. -->\n    <div class=\"vsp-pets\">\n      <img loading=\"lazy\" loading=\"lazy\" decoding=\"async\" width=\"600\" height=\"600\" src=\"https:\/\/vuelveid.com\/wp-content\/uploads\/2026\/10\/WhatsApp-Image-2026-10-09-at-20.49.10.jpeg\" alt=\"A customer\u2019s pet wearing its Vuelve ID tag\">\n      <img loading=\"lazy\" loading=\"lazy\" decoding=\"async\" width=\"600\" height=\"600\" src=\"https:\/\/vuelveid.com\/wp-content\/uploads\/2026\/06\/Imagen-4-scaled.jpg\" alt=\"A customer\u2019s pet wearing its Vuelve ID tag\">\n      <img loading=\"lazy\" loading=\"lazy\" decoding=\"async\" width=\"600\" height=\"600\" src=\"https:\/\/vuelveid.com\/wp-content\/uploads\/2026\/06\/EBAAAE93-23AF-445D-BEAD-5D98C075CD7F.jpeg\" alt=\"A customer\u2019s pet wearing its Vuelve ID tag\">\n      <img loading=\"lazy\" loading=\"lazy\" decoding=\"async\" width=\"600\" height=\"600\" src=\"https:\/\/vuelveid.com\/wp-content\/uploads\/2026\/10\/WhatsApp-Image-2026-10-09-at-20.34.16-1.jpeg\" alt=\"A customer\u2019s pet wearing its Vuelve ID tag\">\n      <img loading=\"lazy\" loading=\"lazy\" decoding=\"async\" width=\"600\" height=\"600\" src=\"https:\/\/vuelveid.com\/wp-content\/uploads\/2026\/10\/WhatsApp-Image-2026-10-09-at-20.34.15-4.jpeg\" alt=\"A customer\u2019s pet wearing its Vuelve ID tag\">\n      <img loading=\"lazy\" loading=\"lazy\" decoding=\"async\" width=\"600\" height=\"600\" src=\"https:\/\/vuelveid.com\/wp-content\/uploads\/2026\/10\/WhatsApp-Image-2026-10-09-at-20.34.16.jpeg\" alt=\"A customer\u2019s pet wearing its Vuelve ID tag\">\n      <img loading=\"lazy\" loading=\"lazy\" decoding=\"async\" width=\"600\" height=\"600\" src=\"https:\/\/vuelveid.com\/wp-content\/uploads\/2026\/10\/WhatsApp-Image-2026-10-09-at-20.34.15-3.jpeg\" alt=\"A customer\u2019s pet wearing its Vuelve ID tag\">\n      <img loading=\"lazy\" loading=\"lazy\" decoding=\"async\" width=\"600\" height=\"600\" src=\"https:\/\/vuelveid.com\/wp-content\/uploads\/2026\/10\/WhatsApp-Image-2026-10-09-at-20.34.15-2.jpeg\" alt=\"A customer\u2019s pet wearing its Vuelve ID tag\">\n      <img loading=\"lazy\" loading=\"lazy\" decoding=\"async\" width=\"600\" height=\"600\" src=\"https:\/\/vuelveid.com\/wp-content\/uploads\/2026\/10\/WhatsApp-Image-2026-10-09-at-20.34.15-1.jpeg\" alt=\"A customer\u2019s pet wearing its Vuelve ID tag\">\n      <img loading=\"lazy\" loading=\"lazy\" decoding=\"async\" width=\"600\" height=\"600\" src=\"https:\/\/vuelveid.com\/wp-content\/uploads\/2026\/10\/WhatsApp-Image-2026-10-09-at-20.34.15.jpeg\" alt=\"A customer\u2019s pet wearing its Vuelve ID tag\">\n      <img loading=\"lazy\" loading=\"lazy\" decoding=\"async\" width=\"600\" height=\"600\" src=\"https:\/\/vuelveid.com\/wp-content\/uploads\/2026\/10\/WhatsApp-Image-2026-10-09-at-20.40.09-1.jpeg\" alt=\"A customer\u2019s pet wearing its Vuelve ID tag\">\n      <img loading=\"lazy\" loading=\"lazy\" decoding=\"async\" width=\"600\" height=\"600\" src=\"https:\/\/vuelveid.com\/wp-content\/uploads\/2026\/10\/WhatsApp-Image-2026-10-09-at-20.40.09.jpeg\" alt=\"A customer\u2019s pet wearing its Vuelve ID tag\">\n    <\/div>\n  <\/section>\n\n  <section class=\"vsp-section vsp-faq\">\n    <h2 class=\"vsp-h2\">Questions<\/h2>\n    <details>\n      <summary>How do I create my pet\u2019s profile?<\/summary>\n      <p>When your tag arrives, scan it with your phone. Vuelve opens and guides you to create your account and their profile: photo, contact, allergies and vaccines. You can edit it anytime.<\/p>\n    <\/details>\n    <details>\n      <summary>Is there a monthly fee?<\/summary>\n      <p>No. You pay for the tag once. The profile and the vaccine record are included.<\/p>\n    <\/details>\n    <details>\n      <summary>Does the $25 include everything?<\/summary>\n      <p>Yes. Each tag costs $25 with IVU and shipping included. The total you see here is what you pay.<\/p>\n    <\/details>\n    <details>\n      <summary>What if I don\u2019t want the name engraved?<\/summary>\n      <p>Engraving is optional. The tag carries its unique ID and the profile works the same.<\/p>\n    <\/details>\n  <\/section>\n<\/div>\n\n<!-- Barra fija fuera de .vsp para que position:fixed no quede atrapado por el tema. -->\n<div class=\"vsp-bar\" id=\"vsp-bar\">\n  <div class=\"vsp-bar-inner\">\n    <div>\n      <div class=\"vsp-bar-total\" id=\"vsp-bar-total\">$0.00<\/div>\n      <div class=\"vsp-bar-sub\" id=\"vsp-bar-sub\"><\/div>\n    <\/div>\n    <button type=\"button\" class=\"vsp-cta\" id=\"vsp-btn\">Go to checkout<\/button>\n  <\/div>\n<\/div>\n\n<script>\n(function () {\n  'use strict';\n\n  \/* ==========================================================================\n     CONFIG \u2014 lo \u00fanico que editas para operar la p\u00e1gina.\n     ========================================================================== *\/\n  var CFG = {\n    \/\/ v12: el precio de cada placa viene del cat\u00e1logo (data-net en el bot\u00f3n).\n    \/\/ Este n\u00famero solo se usa con los botones de respaldo del HTML.\n    FALLBACK_NET_CENTS: 2242,                \/\/ 22.42 + 11.5% = 25.00\n    CATALOG_URL: '\/wp-json\/vuelve\/v1\/catalog',\n    IVU_RATE:    0.115,                      \/\/ 10.5% estatal + 1.0% municipal\n    SHIPPING_CENTS: 0,                       \/\/ env\u00edo gratis durante la oferta (configurar igual en Woo)\n\n    \/\/ Tama\u00f1o: mismo producto por color; el tama\u00f1o se manda como dato de la\n    \/\/ l\u00ednea (SIZE_PARAM) y el snippet PHP \"vid_tag_size\" lo pasa a la orden.\n    \/\/ false = esconde el paso y todo sale como regular.\n    SIZES_ENABLED: true,\n    SIZE_PARAM: 'tag_size',\n\n    \/\/ v13: phone on the tag. 269 + IVU 11.5% (31) = 300 \u2192 $3.00 exactos, so a tag\n    \/\/ with phone is $28.00. Must match EXTRA in the PHP snippet \"Tel\u00e9fono en la placa\".\n    PHONE_ENABLED: false,                    \/\/ v13.1: APAGADO. true = ense\u00f1a la opci\u00f3n (activa tambi\u00e9n el snippet PHP)\n    PHONE_NET_CENTS: 269,\n    PHONE_PARAM: 'tag_phone',\n\n    NAME_MAX: { regular: 20, small: 20 },    \/\/ TODO: confirmar caracteres que caben en cada placa\n    MAX_TAGS: 6,                             \/\/ 6 = lo que cabe en un paquete (vid-shipping per_package)\n    WHATSAPP: '19393620967',\n    REGISTERED_PETS: 100,\n\n    \/\/ A\/B de titular. Misma persona = misma versi\u00f3n (hash del visitante).\n    HEADLINES: {\n      A: 'Identificaci\u00f3n inteligente para tu mascota',\n      B: 'Su nombre, su perfil y tu tel\u00e9fono en una placa'\n    },\n\n    LIVE_HOSTS: ['vuelveid.com', 'www.vuelveid.com'],\n    \/\/ Checkout en el MISMO idioma que esta p\u00e1gina (TranslatePress usa \/es\/).\n    \/\/ Si TranslatePress traduce el slug, cambia '\/es\/checkout\/' por el real.\n    CHECKOUT_URL: location.pathname.indexOf('\/es\/') === 0 ? '\/es\/checkout\/' : '\/checkout\/',\n    \/\/ ID del m\u00e9todo de pago de ATH M\u00f3vil en WooCommerce (Settings \u2192 Payments:\n    \/\/ aparece en el URL como ...section=<ID>). Vac\u00edo = el bot\u00f3n igual va al\n    \/\/ checkout, solo que sin preseleccionar ATH.\n    ATH_GATEWAY_ID: '',\n    PAY_PARAM: 'vid_pay',                    \/\/ lo lee el snippet PHP de preselecci\u00f3n\n    PRIMARY_ENDPOINT: '\/?wc-ajax=add_to_cart',\n    FALLBACK_ENDPOINT: '\/wp-admin\/admin-ajax.php'\n  };\n\n  var IS_LIVE = CFG.LIVE_HOSTS.indexOf(location.hostname) !== -1;\n\n  \/* ==========================================================================\n     I18N \u2014 textos que arma el JavaScript.\n     TranslatePress traduce el HTML fijo, pero NO frases que cambian seg\u00fan lo\n     que escoge el cliente (\"Add Bosque tag for Canela\"). Por eso esas frases\n     salen de aqu\u00ed, seg\u00fan el idioma de la URL: \/es\/ = espa\u00f1ol; lo dem\u00e1s, ingl\u00e9s.\n     Los nombres de color (Bosque, Flamenco\u2026) son marca: no se traducen.\n     IMPORTANTE: sin el car\u00e1cter ampersand en este script (WordPress lo rompe).\n     ========================================================================== *\/\n  var LANG = \/^\\\/es(\\\/|$)\/.test(location.pathname) ? 'es'\n           : ((document.documentElement.lang || '').indexOf('es') === 0 ? 'es' : 'en');\n  var I18N = {\n    en: {\n      tag1: ' tag', tagN: ' tags',\n      add: function (c, n) { return 'Add ' + c + ' tag' + (n ? ' for ' + n : ''); },\n      imgAlt: function (c) { return 'Vuelve ID tag, ' + c + ' model'; },\n      yourPet: 'Your pet',\n      maxTags: function (m) { return 'Maximum ' + m + ' tags per order. For more, message us on WhatsApp.'; },\n      smallSize: ' \u00b7 Small\/Toy pets', regularSize: ' \u00b7 Large\/medium pets',\n      noName: 'No name engraved',\n      remove: 'Remove this tag',\n      totalNote: function (net, ivu, ship) { return 'Tags ' + net + ' \u00b7 IVU ' + ivu + ' \u00b7 Shipping ' + (ship || 'free') + '. No extra charges.'; },\n      allIncl: ' \u00b7 all included',\n      waOrder: function (items) { return 'Hi, I have a question about my order: ' + items + '.'; },\n      waGeneric: 'Hi, I have a question about Vuelve ID.',\n      demo: function (n, t) { return 'Demo: on vuelveid.com this button adds ' + n + ' to the cart and opens checkout for ' + t + '.'; },\n      fail: 'We couldn\u2019t prepare your order. Check your connection and try again, or message us on WhatsApp and we\u2019ll complete it with you.',\n      busy: 'Preparing your order\u2026', checkout: 'Go to checkout', payAth: 'Pay with ATH M\u00f3vil',\n      phoneBad: 'Write a 10-digit phone number, for example (787) 555-1234, or uncheck the phone option.'\n    },\n    es: {\n      tag1: ' placa', tagN: ' placas',\n      add: function (c, n) { return 'A\u00f1adir placa ' + c + (n ? ' para ' + n : ''); },\n      imgAlt: function (c) { return 'Placa Vuelve ID modelo ' + c; },\n      yourPet: 'Tu mascota',\n      maxTags: function (m) { return 'M\u00e1ximo ' + m + ' placas por orden. Para m\u00e1s, escr\u00edbenos por WhatsApp.'; },\n      smallSize: ' \u00b7 Mascotas peque\u00f1as\/toy', regularSize: ' \u00b7 Mascotas grandes\/medianas',\n      noName: 'Sin nombre grabado',\n      remove: 'Quitar esta placa',\n      totalNote: function (net, ivu, ship) { return 'Placas ' + net + ' \u00b7 IVU ' + ivu + ' \u00b7 Env\u00edo ' + (ship || 'gratis') + '. No hay cargos adicionales.'; },\n      allIncl: ' \u00b7 todo incluido',\n      waOrder: function (items) { return 'Hola, tengo una pregunta sobre mi orden: ' + items + '.'; },\n      waGeneric: 'Hola, tengo una pregunta sobre Vuelve ID.',\n      demo: function (n, t) { return 'Demo: en vuelveid.com este bot\u00f3n a\u00f1ade ' + n + ' al carrito y abre el checkout por ' + t + '.'; },\n      fail: 'No pudimos preparar tu orden. Revisa tu conexi\u00f3n e intenta otra vez, o escr\u00edbenos por WhatsApp y la completamos contigo.',\n      busy: 'Preparando tu orden\u2026', checkout: 'Ir a pagar', payAth: 'Pagar con ATH M\u00f3vil',\n      phoneBad: 'Escribe un tel\u00e9fono de 10 d\u00edgitos, por ejemplo (787) 555-1234, o desmarca la opci\u00f3n del tel\u00e9fono.'\n    }\n  };\n  var T = I18N[LANG];\n\n  \/* Whitelist de eventos (Meta standard events). Un nombre fuera de aqu\u00ed = error. *\/\n  var EVENTS = Object.freeze({\n    ViewContent: 'ViewContent', CustomizeProduct: 'CustomizeProduct',\n    AddToCart: 'AddToCart', InitiateCheckout: 'InitiateCheckout', Contact: 'Contact'\n  });\n\n  \/* ==========================================================================\n     DOM\n     ========================================================================== *\/\n  var $ = function (id) { return document.getElementById(id); };\n  var root = $('vsp-root');\n  var el = {\n    mainImg: $('vsp-main-img'), heroSvg: $('vsp-hero-svg'),\n    svgBody: $('vsp-svg-body'), svgName: $('vsp-svg-name'),\n    engrave: $('vsp-engrave'), engraveName: $('vsp-engrave-name'),\n    sizeStep: $('vsp-size-step'), sizes: $('vsp-sizes'), colors: $('vsp-colors'),\n    nameInput: $('vsp-pet-name'), addBtn: $('vsp-add-btn'), error: $('vsp-error'),\n    cart: $('vsp-cart'), cartItems: $('vsp-cart-items'), cartCount: $('vsp-cart-count'),\n    total: $('vsp-total'), totalPrice: $('vsp-total-price'), totalNote: $('vsp-total-note'),\n    bar: $('vsp-bar'), barTotal: $('vsp-bar-total'), barSub: $('vsp-bar-sub'), btn: $('vsp-btn'),\n    wa: $('vsp-whatsapp'), ath: $('vsp-ath'), altPay: $('vsp-alt-pay'),\n    count: $('vsp-count'), order: $('vsp-order'),\n    priceAmt: $('vsp-price-amt'), badgeAmt: $('vsp-badge-amt'),\n    phoneOn: $('vsp-phone-on'), phoneOpt: $('vsp-phone-opt'), phoneWrap: $('vsp-phone-wrap'),\n    phone: $('vsp-phone'), phonePrice: $('vsp-phone-price')\n  };\n\n  var sel = { size: 'regular', colorBtn: el.colors.querySelector('.vsp-color-btn.active') };\n  var order = [];   \/\/ [{ size, id, color, hex, img, net, petName|null, phone|null }]\n\n  var money  = function (c) { return '$' + (c \/ 100).toFixed(2); };\n  \/\/ \"$25\" for whole dollars (the big price), \"$25.50\" otherwise.\n  var moneyShort = function (c) { return c % 100 === 0 ? '$' + (c \/ 100) : money(c); };\n  var plural = function (n) { return n + (n === 1 ? T.tag1 : T.tagN); };\n  function escapeHtml(s) {\n    \/\/ OJO: este script NO puede contener el car\u00e1cter ampersand. WordPress lo\n    \/\/ convierte en su entidad HTML y rompe todo el JavaScript. Por eso se usa\n    \/\/ \\u0026 (mismo car\u00e1cter) y ternarios en vez del AND l\u00f3gico.\n    var AMP = '\\u0026';\n    var MAP = {}; MAP[AMP] = AMP + 'amp;'; MAP['<'] = AMP + 'lt;'; MAP['>'] = AMP + 'gt;';\n    MAP['\"'] = AMP + 'quot;'; MAP[\"'\"] = AMP + '#39;';\n    return String(s).replace(\/[\\u0026<>\"']\/g, function (c) {\n      return MAP[c];\n    });\n  }\n  function showError(m) { el.error.textContent = m; el.error.hidden = false; }\n  function clearError() { el.error.hidden = true; }\n\n  \/* ==========================================================================\n     A\/B DETERMIN\u00cdSTICO \u2014 FNV-1a de un ID persistente del visitante.\n     ========================================================================== *\/\n  function pickVariant() {\n    var keys = Object.keys(CFG.HEADLINES), id;\n    try {\n      id = localStorage.getItem('vid_visitor');\n      if (!id) {\n        id = (window.crypto ? crypto.randomUUID : null) ? crypto.randomUUID() : String(Math.random()).slice(2);\n        localStorage.setItem('vid_visitor', id);\n      }\n    } catch (e) { id = String(Math.random()).slice(2); }   \/\/ sin storage: ID de sesi\u00f3n\n    var h = 2166136261;\n    for (var i = 0; i < id.length; i++) { h ^= id.charCodeAt(i); h = Math.imul(h, 16777619); }\n    return keys[(h >>> 0) % keys.length];\n  }\n  var VARIANT = pickVariant();\n\n  \/* ==========================================================================\n     PRECIO \u2014 mismo agrupamiento y mismo redondeo que WooCommerce.\n     Woo (con \"round at subtotal\" desmarcado) redondea el IVU POR L\u00cdNEA, y\n     las l\u00edneas son las mismas que manda checkout(). Por eso el total de\n     aqu\u00ed es el total de all\u00e1, al centavo.\n     ========================================================================== *\/\n  function groupOrder() {\n    var map = new Map();\n    order.forEach(function (it) {\n      \/\/ El tama\u00f1o va en la llave: Bosque regular y Bosque peque\u00f1a con el mismo\n      \/\/ nombre son dos l\u00edneas distintas (Woo tambi\u00e9n las separa por tag_size).\n      \/\/ v13: the phone is part of the key too (Woo splits lines by tag_phone).\n      var key = it.id + '|' + it.size + '|' + (it.petName || '') + '|' + (it.phone || '');\n      if (map.has(key)) map.get(key).quantity += 1;\n      else map.set(key, { id: it.id, size: it.size, petName: it.petName, phone: it.phone, net: it.net, quantity: 1 });\n    });\n    return Array.from(map.values());\n  }\n\n  function computeTotals() {\n    var net = 0, ivu = 0;\n    groupOrder().forEach(function (line) {\n      var lineNet = line.net * line.quantity;\n      net += lineNet;\n      ivu += Math.round(lineNet * CFG.IVU_RATE);\n    });\n    var ship = order.length ? CFG.SHIPPING_CENTS : 0;\n    return { net: net, ivu: ivu, ship: ship, total: net + ivu + ship };\n  }\n\n  \/* Total de UNA placa sola, como lo cobra Woo: precio + IVU redondeado + env\u00edo. *\/\n  function allIn(net) {\n    return net + Math.round(net * CFG.IVU_RATE) + CFG.SHIPPING_CENTS;\n  }\n\n  \/* ==========================================================================\n     TRACKING\n     ========================================================================== *\/\n  var logEl = null;\n  function track(name, params) {\n    if (!EVENTS[name]) throw new Error('[Vuelve] Evento no permitido: ' + name);\n    params = params || {};\n    params.currency = 'USD';\n    params.variant = VARIANT;\n    try { if (typeof window.fbq === 'function') window.fbq('track', name, params); } catch (e) {}\n    if (logEl) logEl.textContent = '[' + VARIANT + '] ' + name + (params.value != null ? ' \u00b7 ' + money(Math.round(params.value * 100)) : '');\n  }\n\n  \/* ==========================================================================\n     v13 \u2014 TEL\u00c9FONO EN LA PLACA\n     10 d\u00edgitos (PR \/ EE. UU.). Ning\u00fan c\u00f3digo de \u00e1rea empieza con 1, as\u00ed que un\n     1 delante (+1) se ignora, tambi\u00e9n mientras escriben. Se guarda formateado\n     \"(787) 555-1234\", igual que lo formatea el snippet PHP.\n     ========================================================================== *\/\n  function phoneDigits(raw) {\n    var d = String(raw || '').replace(\/\\D\/g, '');\n    return d.charAt(0) === '1' ? d.slice(1) : d;\n  }\n  function phoneFormat(d) {\n    return '(' + d.slice(0, 3) + ') ' + d.slice(3, 6) + '-' + d.slice(6, 10);\n  }\n  \/\/ v13.1: with PHONE_ENABLED false the option never shows and never counts.\n  function phoneWanted() { return CFG.PHONE_ENABLED ? el.phoneOn.checked : false; }\n  el.phoneOpt.hidden = !CFG.PHONE_ENABLED;\n  if (!CFG.PHONE_ENABLED) { el.phoneOn.checked = false; el.phoneWrap.hidden = true; }\n\n  el.phonePrice.textContent = '+' + moneyShort(allIn(CFG.PHONE_NET_CENTS) - CFG.SHIPPING_CENTS);\n  el.phoneOn.addEventListener('change', function () {\n    var on = phoneWanted();\n    el.phoneWrap.hidden = !on;\n    el.phoneOpt.classList.toggle('on', on);\n    el.phone.classList.remove('bad');\n    clearError();\n    applySelection();\n    if (on) { el.phone.focus(); onCustomize('phone'); }\n  });\n  el.phone.addEventListener('input', function () {\n    \/\/ Live format while typing; keeps only digits underneath.\n    var d = phoneDigits(this.value).slice(0, 10);\n    this.value = d.length > 6 ? phoneFormat(d) : (d.length > 3 ? '(' + d.slice(0, 3) + ') ' + d.slice(3) : d);\n    this.classList.remove('bad');\n  });\n  el.phone.addEventListener('keydown', function (e) {\n    if (e.key === 'Enter') { e.preventDefault(); addTag(); }\n  });\n\n  \/* ==========================================================================\n     SELECCI\u00d3N\n     ========================================================================== *\/\n  el.sizeStep.hidden = !CFG.SIZES_ENABLED;\n\n  \/* Texto del tama\u00f1o para carrito y WhatsApp. Si el paso est\u00e1 apagado no se\n     muestra nada (todas son regulares y no hace falta decirlo). *\/\n  function sizeLabel(size) {\n    if (!CFG.SIZES_ENABLED) return '';\n    return size === 'small' ? T.smallSize : T.regularSize;\n  }\n\n  function resolveProduct() {\n    var b = sel.colorBtn.dataset;\n    return {\n      size: sel.size,          \/\/ 'regular' | 'small' -> viaja como tag_size\n      id: b.id,                \/\/ mismo producto en Woo para los dos tama\u00f1os\n      color: b.name, hex: b.hex,\n      img: b.img,\n      net: Number(b.net) > 0 ? Number(b.net) : CFG.FALLBACK_NET_CENTS   \/\/ centavos, sin IVU\n    };\n  }\n\n  function activate(group, btn, selector) {\n    group.querySelectorAll(selector).forEach(function (b) {\n      b.classList.remove('active'); b.setAttribute('aria-pressed', 'false');\n    });\n    btn.classList.add('active'); btn.setAttribute('aria-pressed', 'true');\n  }\n\n  var heroBroken = false;\n  function swapMainImage(src) {\n    if (heroBroken) return;\n    var next = new Image();\n    next.src = src;\n    el.mainImg.classList.add('fading');\n    var reveal = function () { el.mainImg.src = src; el.mainImg.classList.remove('fading'); };\n    if (next.decode) next.decode().then(reveal).catch(reveal);\n    else { next.onload = reveal; next.onerror = reveal; }\n  }\n\n  \/* Pinta TODO lo que depende de la selecci\u00f3n y del nombre. *\/\n  function applySelection() {\n    var p = resolveProduct();\n    var name = el.nameInput.value.trim();\n\n    \/\/ v13: with the phone option on, the button shows the tag's real price ($28).\n    el.addBtn.textContent = T.add(p.color, name) +\n      (phoneWanted() ? ' \u00b7 ' + moneyShort(allIn(p.net + CFG.PHONE_NET_CENTS)) : '');\n    \/\/ v12: the big price and the offer label follow the selected product.\n    el.priceAmt.textContent = moneyShort(allIn(p.net));\n    if (el.badgeAmt) el.badgeAmt.textContent = moneyShort(allIn(p.net));\n    el.mainImg.alt = T.imgAlt(p.color);\n    \/\/ v11: un color sin foto (Sol si falla la Store API) muestra la placa\n    \/\/ dibujada en su color, en vez de pedir src=\"\" y romper la imagen.\n    if (!p.img) {\n      el.mainImg.hidden = true; el.heroSvg.hidden = false;\n    } else if (!heroBroken) {\n      el.mainImg.hidden = false; el.heroSvg.hidden = true;\n      if (el.mainImg.getAttribute('src') !== p.img) swapMainImage(p.img);\n    }\n\n    \/\/ Vista previa en vivo del grabado (efecto IKEA)\n    el.engrave.hidden = !name;\n    el.engraveName.textContent = name;\n    el.svgBody.setAttribute('fill', p.hex);\n    el.svgName.textContent = name || T.yourPet;\n\n    var max = CFG.NAME_MAX[sel.size];\n    el.nameInput.maxLength = max;\n    if (el.nameInput.value.length > max) el.nameInput.value = el.nameInput.value.slice(0, max);\n  }\n\n  var customized = false;\n  function onCustomize(field) {\n    if (customized) return;            \/\/ un solo CustomizeProduct por visita\n    customized = true;\n    track(EVENTS.CustomizeProduct, { field: field });\n  }\n\n  el.sizes.addEventListener('click', function (e) {\n    var btn = e.target.closest('.vsp-size-btn'); if (!btn) return;\n    activate(el.sizes, btn, '.vsp-size-btn');\n    sel.size = btn.dataset.size;\n    applySelection(); onCustomize('size');\n  });\n\n  el.colors.addEventListener('click', function (e) {\n    var btn = e.target.closest('.vsp-color-btn'); if (!btn) return;\n    activate(el.colors, btn, '.vsp-color-btn');\n    sel.colorBtn = btn;\n    applySelection(); onCustomize('color');\n  });\n\n  el.nameInput.addEventListener('input', function () {\n    if (this.value) this.value = this.value.charAt(0).toUpperCase() + this.value.slice(1);\n    applySelection();\n    if (this.value.trim()) onCustomize('name');\n  });\n  el.nameInput.addEventListener('keydown', function (e) {\n    if (e.key === 'Enter') { e.preventDefault(); addTag(); }\n  });\n\n  \/* ==========================================================================\n     A\u00d1ADIR \/ QUITAR\n     ========================================================================== *\/\n  el.addBtn.addEventListener('click', addTag);\n\n  function addTag() {\n    if (order.length >= CFG.MAX_TAGS) {\n      showError(T.maxTags(CFG.MAX_TAGS));\n      return;\n    }\n    clearError();\n    var p = resolveProduct();\n    p.petName = el.nameInput.value.trim() || null;\n\n    \/\/ v13: phone is required only when the option is checked.\n    p.phone = null;\n    if (phoneWanted()) {\n      var d = phoneDigits(el.phone.value);\n      \/\/ Same rule as the PHP snippet: area code and exchange never start with 0 or 1.\n      if (!\/^[2-9][0-9]{2}[2-9][0-9]{6}$\/.test(d)) {\n        el.phone.classList.add('bad');\n        showError(T.phoneBad);\n        el.phone.focus();\n        return;\n      }\n      p.phone = phoneFormat(d);\n      p.net += CFG.PHONE_NET_CENTS;     \/\/ net per tag; IVU is added per line like Woo\n    }\n    order.push(p);\n\n    \/\/ The name is per pet, so it clears. The phone stays filled for the next tag.\n    el.nameInput.value = '';\n    applySelection();\n\n    track(EVENTS.AddToCart, {\n      content_ids: [p.id], content_type: 'product',\n      content_name: 'Vuelve ID ' + p.color + ' ' + p.size, value: allIn(p.net) \/ 100\n    });\n\n    render();\n    var reduce = window.matchMedia ? matchMedia('(prefers-reduced-motion: reduce)').matches : false;\n    el.cart.scrollIntoView({ behavior: reduce ? 'auto' : 'smooth', block: 'center' });\n  }\n\n  el.cartItems.addEventListener('click', function (e) {\n    var btn = e.target.closest('.vsp-cart-remove'); if (!btn) return;\n    order.splice(Number(btn.dataset.index), 1);\n    clearError(); render();\n  });\n\n  \/* ==========================================================================\n     RENDER \u2014 estado -> DOM en una pasada.\n     ========================================================================== *\/\n  function render() {\n    var count = order.length, has = count > 0, t = computeTotals();\n\n    el.cart.hidden = !has;\n    el.total.hidden = !has;\n    el.bar.classList.toggle('visible', has);\n    root.classList.toggle('has-bar', has);\n    el.addBtn.disabled = count >= CFG.MAX_TAGS;\n\n    var frag = document.createDocumentFragment();\n    order.forEach(function (it, i) {\n      var sizeTxt = sizeLabel(it.size);\n      var row = document.createElement('div');\n      row.className = 'vsp-cart-item';\n      row.innerHTML =\n        '<img loading=\"lazy\" class=\"vsp-cart-thumb\" style=\"--c:' + escapeHtml(it.hex) + '\"' + (it.img ? ' src=\"' + escapeHtml(it.img) + '\"' : '') + ' alt=\"\" width=\"52\" height=\"52\" decoding=\"async\">' +\n        '<div class=\"vsp-cart-info\">' +\n          (it.petName ? '<div class=\"vsp-cart-name\">\ud83d\udc3e ' + escapeHtml(it.petName) + '<\/div>'\n                      : '<div class=\"vsp-cart-name no-name\">' + T.noName + '<\/div>') +\n          '<div class=\"vsp-cart-model\">' + escapeHtml(it.color + sizeTxt) + '<\/div>' +\n          (it.phone ? '<div class=\"vsp-cart-phone\">\ud83d\udcde ' + escapeHtml(it.phone) + '<\/div>' : '') +\n        '<\/div>' +\n        '<span class=\"vsp-cart-price\">' + money(allIn(it.net)) + '<\/span>' +\n        '<button type=\"button\" class=\"vsp-cart-remove\" data-index=\"' + i + '\" aria-label=\"' + T.remove + '\">\u2715<\/button>';\n      frag.appendChild(row);\n    });\n    el.cartItems.textContent = '';\n    el.cartItems.appendChild(frag);\n    el.cartCount.textContent = has ? plural(count) : '';\n\n    \/\/ Desglose peque\u00f1o por transparencia; el n\u00famero grande es el que se paga.\n    el.totalPrice.textContent = money(t.total);\n    el.totalNote.textContent = T.totalNote(money(t.net), money(t.ivu), t.ship ? money(t.ship) : '');\n    el.barTotal.textContent = money(t.total);\n    el.barSub.textContent = plural(count) + T.allIncl;\n\n    \/\/ WhatsApp es solo para preguntas. Si ya hay orden, la incluye como\n    \/\/ contexto para que no tengan que explicar qu\u00e9 est\u00e1n mirando.\n    var msg = has\n      ? T.waOrder(plural(count) + ' Vuelve ID (' + order.map(function (it) {\n          return it.color + sizeLabel(it.size) + (it.petName ? ' - ' + it.petName : '') + (it.phone ? ' - \ud83d\udcde' : '');\n        }).join(', ') + ')')\n      : T.waGeneric;\n    el.wa.href = 'https:\/\/wa.me\/' + CFG.WHATSAPP + '?text=' + encodeURIComponent(msg);\n  }\n\n  el.wa.addEventListener('click', function () { track(EVENTS.Contact, { channel: 'whatsapp' }); });\n\n  \/* Pago directo. Si la orden est\u00e1 vac\u00eda, a\u00f1ade la placa que est\u00e1 escogida\n     (color + nombre del campo) para que el cliente llegue al pago en UN toque. *\/\n  function payNow(method) {\n    if (!order.length) addTag();\n    if (order.length) checkout(method);\n  }\n  el.ath.addEventListener('click', function () { payNow('ath'); });\n  el.altPay.addEventListener('click', function () { payNow('other'); });\n\n  \/* ==========================================================================\n     CHECKOUT \u2014 secuencial (la sesi\u00f3n de Woo no tiene lock), l\u00edneas agrupadas.\n     ========================================================================== *\/\n  el.btn.addEventListener('click', function () { checkout('other'); });\n\n  \/* URL del checkout. Para ATH a\u00f1ade ?vid_pay=<gateway>; el snippet PHP lo\n     guarda en la sesi\u00f3n de Woo y el checkout abre con ATH marcado.\n     (Sin el car\u00e1cter ampersand: este script no lo puede contener.) *\/\n  function checkoutUrl(method) {\n    var url = CFG.CHECKOUT_URL;\n    if (method === 'ath' ? CFG.ATH_GATEWAY_ID !== '' : false) {\n      url += '?' + CFG.PAY_PARAM + '=' + encodeURIComponent(CFG.ATH_GATEWAY_ID);\n    }\n    return url;\n  }\n\n  async function addToCartRequest(line, endpoint) {\n    var body = new FormData();\n    body.append('action', 'woocommerce_add_to_cart');\n    body.append('product_id', line.id);\n    body.append('quantity', line.quantity);\n    if (line.petName) body.append('pet_name', line.petName);\n    if (CFG.SIZES_ENABLED) body.append(CFG.SIZE_PARAM, line.size);   \/\/ snippet vid_tag_size\n    if (line.phone) body.append(CFG.PHONE_PARAM, line.phone);         \/\/ v13: snippet \"Tel\u00e9fono en la placa\"\n    var res = await fetch(endpoint, {\n      method: 'POST', body: body, credentials: 'same-origin',\n      headers: { 'X-Requested-With': 'XMLHttpRequest' }\n    });\n    if (!res.ok) throw new Error('HTTP ' + res.status);\n    var data = null;\n    try { data = JSON.parse(await res.text()); } catch (e) { \/* algunos temas devuelven HTML *\/ }\n    if (data ? data.error : false) throw new Error('WC_REJECTED');\n  }\n\n  async function checkout(method) {\n    if (!order.length) return;\n    var t = computeTotals();\n    var params = {\n      content_ids: order.map(function (o) { return o.id; }),\n      content_type: 'product', num_items: order.length, value: t.total \/ 100,\n      pay_method: method === 'ath' ? 'ath_movil' : 'card_paypal'   \/\/ para comparar en Meta\n    };\n\n    if (!IS_LIVE) {   \/\/ demo: no hay WooCommerce aqu\u00ed\n      track(EVENTS.InitiateCheckout, params);\n      showError(T.demo(plural(order.length), money(t.total)));\n      return;\n    }\n\n    setBusy(true); clearError();\n    try {\n      var lines = groupOrder();\n      for (var i = 0; i < lines.length; i++) {\n        try { await addToCartRequest(lines[i], CFG.PRIMARY_ENDPOINT); }\n        catch (err) {\n          if (err.message === 'WC_REJECTED') throw err;\n          await addToCartRequest(lines[i], CFG.FALLBACK_ENDPOINT);\n        }\n      }\n      track(EVENTS.InitiateCheckout, params);\n      location.href = checkoutUrl(method);\n    } catch (err) {\n      setBusy(false);\n      showError(T.fail);\n      console.error('[Vuelve] checkout fall\u00f3:', err);\n    }\n  }\n\n  \/* Bloquea los 3 botones de pago mientras se arma el carrito (evita doble toque\n     = placas duplicadas en Woo). *\/\n  function setBusy(b) {\n    [el.btn, el.ath, el.altPay].forEach(function (x) { x.disabled = b; });\n    el.btn.textContent = b ? T.busy : T.checkout;\n    el.ath.firstElementChild.textContent = b ? T.busy : T.payAth;\n  }\n\n  \/* ==========================================================================\n     IM\u00c1GENES QUE NO CARGAN \u2014 fallback determin\u00edstico (no cajas vac\u00edas).\n     ========================================================================== *\/\n  function isBroken(img) { return img.complete ? img.naturalWidth === 0 : false; }\n  function handleBroken(img) {\n    if (img === el.mainImg) {\n      heroBroken = true; img.hidden = true; el.heroSvg.hidden = false;\n    } else if (img.closest('.vsp-color-btn')) {\n      img.closest('.vsp-color-btn').classList.add('broken');\n    } else if (img.classList.contains('vsp-cart-thumb')) {\n      img.removeAttribute('src');\n    } else {\n      var sec = img.closest('[data-needs-images]');\n      img.hidden = true;\n      if (sec) { if (!sec.querySelector('img:not([hidden])')) sec.hidden = true; }\n    }\n  }\n  root.addEventListener('error', function (e) { if (e.target.tagName === 'IMG') handleBroken(e.target); }, true);\n  root.querySelectorAll('img').forEach(function (img) { if (isBroken(img)) handleBroken(img); });\n\n  \/* ==========================================================================\n     MEDICI\u00d3N: ViewContent cuando el selector de color est\u00e1 en pantalla.\n     ========================================================================== *\/\n  if ('IntersectionObserver' in window) {\n    new IntersectionObserver(function (entries, obs) {\n      if (entries[0].isIntersecting) {\n        track(EVENTS.ViewContent, { content_type: 'product', value: allIn(resolveProduct().net) \/ 100 });\n        obs.disconnect();\n      }\n    }, { threshold: 0.5 }).observe(el.colors);\n  }\n\n  \/* ==========================================================================\n     SOLO DEMO (fuera de vuelveid.com): notas del porqu\u00e9 + log de eventos.\n     Se generan aqu\u00ed para que el HTML de producci\u00f3n quede limpio.\n     ========================================================================== *\/\n  if (!IS_LIVE) {\n    var NOTES = [\n      ['#vsp-h1', 'Categor\u00eda correcta', 'Pone el producto en \"identificaci\u00f3n\", no en \"rastreo\". Es un A\/B: la versi\u00f3n ' + VARIANT + ' es la que ves, y viaja en cada evento.'],\n      ['.vsp-notgps', 'Definir antes de que lo comparen', 'Contesta la comparaci\u00f3n con GPS sin mencionar robo ni p\u00e9rdida.'],\n      ['.vsp-price', 'Un solo n\u00famero final', 'Costos extra en el checkout son la causa principal de abandono (Baymard). Tu dato: $25 todo incluido = 4 \u00f3rdenes en 3 d\u00edas.'],\n      ['.vsp-hint', 'Ver su nombre en la placa', 'El nombre aparece en vivo en la foto y en el celular (efecto IKEA: Norton, Mochon y Ariely, 2012).'],\n      ['.vsp-alt-pay', 'Pago en un toque', 'ATH M\u00f3vil lleva directo al checkout con ATH ya marcado. Si la orden est\u00e1 vac\u00eda, a\u00f1ade la placa escogida en el mismo toque.']\n    ];\n    NOTES.forEach(function (n) {\n      var target = root.querySelector(n[0]); if (!target) return;\n      var d = document.createElement('div');\n      d.className = 'vsp-note';\n      d.innerHTML = '<b>' + n[1] + '<\/b>' + n[2];\n      target.insertAdjacentElement('afterend', d);\n    });\n\n    var dock = document.createElement('div');\n    dock.className = 'vsp-dock';\n    dock.innerHTML = '<button type=\"button\" id=\"vsp-explain\" aria-pressed=\"false\">Ver por qu\u00e9 funciona<\/button><div class=\"log\">Eventos: \u2014<\/div>';\n    document.body.appendChild(dock);\n    document.body.classList.add('vsp-has-dock');\n    logEl = dock.querySelector('.log');\n    dock.querySelector('#vsp-explain').addEventListener('click', function () {\n      var on = root.classList.toggle('explain');\n      this.setAttribute('aria-pressed', String(on));\n      this.textContent = on ? 'Ocultar explicaci\u00f3n' : 'Ver por qu\u00e9 funciona';\n    });\n  }\n\n\n  \/* ==========================================================================\n     FOTOS DESDE WOOCOMMERCE (Store API p\u00fablica, sin llaves).\n     Una sola llamada para los 8 productos. Por cada uno:\n       \u00b7 cuadrito  \u2190 images[0].thumbnail (300x300)\n       \u00b7 foto grande \u2190 la versi\u00f3n de ~768 px del srcset (no la -scaled de 1920)\n     Si la llamada falla, se quedan las fotos escritas en el HTML (respaldo).\n     Sin ampersand en la URL: un solo par\u00e1metro (include). El per_page por\n     defecto de la API es 10, suficiente para 8 productos. Si llegas a 11+\n     colores, a\u00f1ade per_page (y entonces s\u00ed hace falta el ampersand).\n     https:\/\/github.com\/woocommerce\/woocommerce\/blob\/trunk\/plugins\/woocommerce\/src\/StoreApi\/docs\/products.md\n     ========================================================================== *\/\n  function pickFromSrcset(srcset, target) {\n    \/\/ srcset = \"url 225w, url 768w, ...\" \u2192 la m\u00e1s peque\u00f1a que sea >= target\n    var best = null;\n    String(srcset || '').split(',').forEach(function (part) {\n      var bits = part.trim().split(\/\\s+\/), w = parseInt(bits[1], 10);\n      if (!bits[0] || !(w >= target)) return;\n      if (best === null) best = { url: bits[0], w: w };\n      else if (w < best.w) best = { url: bits[0], w: w };\n    });\n    return best ? best.url : null;\n  }\n\n  function syncProductPhotos() {\n    if (!IS_LIVE) return;                       \/\/ fuera de vuelveid.com no hay API\n    if (!window.fetch) return;\n    var btns = el.colors.querySelectorAll('.vsp-color-btn');\n    var ids = Array.prototype.map.call(btns, function (b) { return b.dataset.id; });\n    fetch('\/wp-json\/wc\/store\/v1\/products?include=' + ids.join(','), { credentials: 'same-origin' })\n      .then(function (r) { if (!r.ok) throw new Error('HTTP ' + r.status); return r.json(); })\n      .then(function (products) {\n        var byId = {};\n        products.forEach(function (p) { byId[String(p.id)] = p; });\n        Array.prototype.forEach.call(btns, function (b) {\n          var p = byId[b.dataset.id];\n          var img = p ? (p.images || [])[0] : null;\n          if (!img) return;                     \/\/ producto sin foto: queda el respaldo\n          var main = pickFromSrcset(img.srcset, 700) || img.src;\n          b.dataset.img = main;\n          var thumb = b.querySelector('img');\n          if (thumb) { b.classList.remove('broken'); thumb.src = img.thumbnail || img.src; }\n        });\n        applySelection();                       \/\/ la foto grande usa la del color activo\n      })\n      .catch(function (err) { console.warn('[Vuelve] fotos de WooCommerce no cargaron, uso respaldo:', err); });\n  }\n\n  \/* ==========================================================================\n     v12 \u2014 CAT\u00c1LOGO DESDE WOOCOMMERCE\n     Una llamada al endpoint del snippet PHP. Si responde con productos, la\n     lista de colores se reconstruye completa: nombre, foto, color del\n     cuadrito, etiqueta y precio de cada uno. Si falla o viene vac\u00eda, se\n     quedan los botones del HTML y se intenta solo actualizar sus fotos.\n     ========================================================================== *\/\n  var HEX_RE = \/^#[0-9a-fA-F]{6}$\/;\n\n  function colorItemHtml(it) {\n    var hex = HEX_RE.test(it.hex || '') ? it.hex : '#C97B2E';\n    var badge = LANG === 'es' ? it.badge_es : (it.badge_en || it.badge_es);\n    return '<div class=\"vsp-color-item\">' +\n      '<button type=\"button\" class=\"vsp-color-btn\" aria-pressed=\"false\"' +\n        ' style=\"--c:' + hex + '\"' +\n        ' data-id=\"' + Number(it.id) + '\"' +\n        ' data-name=\"' + escapeHtml(it.name) + '\"' +\n        ' data-hex=\"' + hex + '\"' +\n        ' data-img=\"' + escapeHtml(it.img || '') + '\"' +\n        ' data-net=\"' + Number(it.net) + '\">' +\n        '<img' + (it.thumb ? ' src=\"' + escapeHtml(it.thumb) + '\"' : '') + ' alt=\"\" width=\"120\" height=\"120\" decoding=\"async\">' +\n      '<\/button>' +\n      (badge ? '<span class=\"vsp-new\">' + escapeHtml(badge) + '<\/span>' : '') +\n      '<span class=\"vsp-color-name\">' + escapeHtml(it.name) + '<\/span>' +\n    '<\/div>';\n  }\n\n  function buildColors(items) {\n    var keepId = sel.colorBtn ? sel.colorBtn.dataset.id : '';\n    el.colors.innerHTML = items.map(colorItemHtml).join('');\n    \/\/ Products without a photo show their solid colour instead of a broken image.\n    el.colors.querySelectorAll('.vsp-color-btn img:not([src])').forEach(function (img) {\n      img.closest('.vsp-color-btn').classList.add('broken');\n    });\n    var btn = el.colors.querySelector('.vsp-color-btn[data-id=\"' + keepId + '\"]') ||\n              el.colors.querySelector('.vsp-color-btn');\n    activate(el.colors, btn, '.vsp-color-btn');\n    sel.colorBtn = btn;\n    applySelection();\n  }\n\n  function loadCatalog() {\n    if (!IS_LIVE) return;                       \/\/ fuera de vuelveid.com no hay endpoint\n    if (!window.fetch) return;\n    fetch(CFG.CATALOG_URL, { credentials: 'same-origin' })\n      .then(function (r) { if (!r.ok) throw new Error('HTTP ' + r.status); return r.json(); })\n      .then(function (data) {\n        var items = (data ? data.items : null) || [];\n        if (!items.length) throw new Error('cat\u00e1logo vac\u00edo (\u00bfning\u00fan producto con \"Mostrar en \/order\"?)');\n        if (data.prices_include_tax) {\n          console.error('[Vuelve] WooCommerce tiene precios CON impuesto incluido; esta p\u00e1gina suma el IVU aparte. Los totales no van a cuadrar.');\n        }\n        buildColors(items);\n      })\n      .catch(function (err) {\n        console.warn('[Vuelve] cat\u00e1logo no carg\u00f3, uso los colores del HTML:', err);\n        syncProductPhotos();\n      });\n  }\n\n  \/* ==========================================================================\n     BOOT\n     ========================================================================== *\/\n  \/\/ (v7) Titular eliminado: VARIANT se sigue enviando en los eventos pero ya\n  \/\/ no cambia nada visible. Si vuelves a poner un <h1 id=\"vsp-h1\">, restaura:\n  \/\/ el.h1 = $('vsp-h1'); el.h1.textContent = CFG.HEADLINES[VARIANT];\n  el.count.textContent = '+' + CFG.REGISTERED_PETS;\n  applySelection();\n  loadCatalog();\n  render();\n})();\n<\/script>\n\n\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>VUELVE Your pet Offer \u00b7 $25 all included Engraved: + unique ID $25 per tag \u00b7 shipping and IVU included Size Large\/medium pets 30 mm Small\/Toy pets Smaller and lighter Choose the color Brav\u00edo Bosque Flamenco Luna Noche Oc\u00e9ano Tierra New Sol Name to engrave (optional) If you leave it blank, the tag comes with [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"_acf_changed":false,"footnotes":""},"class_list":["post-245","page","type-page","status-publish","hentry"],"acf":[],"_hostinger_reach_plugin_has_subscription_block":false,"_hostinger_reach_plugin_is_elementor":false,"_links":{"self":[{"href":"https:\/\/vuelveid.com\/es\/wp-json\/wp\/v2\/pages\/245","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/vuelveid.com\/es\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/vuelveid.com\/es\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/vuelveid.com\/es\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/vuelveid.com\/es\/wp-json\/wp\/v2\/comments?post=245"}],"version-history":[{"count":33,"href":"https:\/\/vuelveid.com\/es\/wp-json\/wp\/v2\/pages\/245\/revisions"}],"predecessor-version":[{"id":518,"href":"https:\/\/vuelveid.com\/es\/wp-json\/wp\/v2\/pages\/245\/revisions\/518"}],"wp:attachment":[{"href":"https:\/\/vuelveid.com\/es\/wp-json\/wp\/v2\/media?parent=245"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}