{"id":791,"date":"2026-06-15T18:56:38","date_gmt":"2026-06-15T16:56:38","guid":{"rendered":"https:\/\/creamland.es\/?page_id=791"},"modified":"2026-07-15T15:57:51","modified_gmt":"2026-07-15T13:57:51","slug":"reservar","status":"publish","type":"page","link":"https:\/\/creamland.es\/index.php\/reservar\/","title":{"rendered":"Reserva"},"content":{"rendered":"\t\t<div data-elementor-type=\"wp-page\" data-elementor-id=\"791\" class=\"elementor elementor-791\" data-elementor-post-type=\"page\">\n\t\t\t\t<div class=\"elementor-element elementor-element-f7e1791 e-flex e-con-boxed e-con e-parent\" data-id=\"f7e1791\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t\t\t<div class=\"elementor-element elementor-element-a5059da elementor-widget elementor-widget-html\" data-id=\"a5059da\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t\t<!DOCTYPE html>\r\n<html lang=\"es\">\r\n<head>\r\n<meta charset=\"UTF-8\">\r\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\r\n<title>Realizar una reserva - Creamland<\/title>\r\n<link href=\"https:\/\/fonts.googleapis.com\/css2?family=Bebas+Neue&family=DM+Sans:wght@400;500;600&display=swap\" rel=\"stylesheet\">\r\n<style>\r\n\/* ============================================================\r\n   COLORES \u2014 edita aqu\u00ed para cambiar toda la paleta\r\n   ============================================================ *\/\r\n:root {\r\n  --azul:         #1b3a6b;\r\n  --azul-oscuro:  #122850;\r\n  --crema:        #f5e6c8;\r\n  --crema-oscuro: #e0c890;\r\n  --texto:        #1a1a2e;\r\n  --texto-suave:  #4a5568;\r\n  --blanco:       #ffffff;\r\n  --fondo:        #fffdf8;\r\n  --borde:        #d4bc8a;\r\n  --radio:        14px;\r\n  --font-titulo:  'Bebas Neue', Impact, sans-serif;\r\n  --font-body:    'DM Sans', 'Segoe UI', system-ui, sans-serif;\r\n  --ancho-max:    640px;\r\n}\r\n*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}\r\nbody{font-family:var(--font-body);background:var(--fondo);color:var(--texto);padding:0 0 80px}\r\n.contenedor{max-width:var(--ancho-max);margin:0 auto}\r\n.page-title{font-family:var(--font-titulo);font-size:4.5rem;font-weight:600;color:var(--azul);text-align:center;padding:28px 16px 4px;letter-spacing:.6px}\r\n.page-sub{text-align:center;font-size:.87rem;color:var(--texto-suave);padding:0 16px 16px;font-style:italic}\r\n.steps-nav{display:flex;justify-content:center;gap:8px;padding:4px 16px 16px;flex-wrap:wrap}\r\n.step-pill{border:2px solid var(--azul);color:var(--azul);border-radius:50px;padding:6px 18px;font-size:.82rem;font-weight:600;background:var(--blanco);font-family:var(--font-body)}\r\n.step-pill.activo{background:var(--azul);color:var(--crema)}\r\n.step-pill.hecho{background:var(--azul);color:var(--crema);opacity:.5}\r\n.resumen{background:var(--azul);color:var(--crema);border-radius:var(--radio);margin:0 16px 16px;padding:14px 18px;font-size:.88rem;line-height:1.7;min-height:50px}\r\n.resumen strong{display:block;font-size:.7rem;letter-spacing:.1em;text-transform:uppercase;color:#fffdf8;margin-bottom:2px}\r\n.resumen-linea{display:flex;justify-content:space-between;align-items:center;gap:6px;margin-top:3px;flex-wrap:wrap}\r\n.btn-eliminar{background:rgba(245,230,200,.18);border:1px solid rgba(245,230,200,.35);border-radius:50px;color:var(--crema);font-size:.72rem;padding:2px 10px;cursor:pointer;font-family:var(--font-body);transition:background .15s;white-space:nowrap;flex-shrink:0}\r\n.btn-eliminar:hover{background:rgba(245,230,200,.38)}\r\n.resumen-total{font-family:var(--font-titulo);font-size:1.5rem;text-align:center;margin-top:10px;letter-spacing:.5px}\r\n.resumen-nota{font-size:.7rem;font-weight:700;text-transform:uppercase;letter-spacing:.08em;text-align:center;opacity:.7;margin-top:1px}\r\n.resumen-sub{font-size:.76rem;font-style:italic;text-align:center;opacity:.82;margin-top:1px}\r\n.card{background:var(--blanco);border:1.5px solid var(--borde);border-radius:var(--radio);margin:0 16px;padding:22px 18px;box-shadow:0 2px 14px rgba(27,58,107,.08)}\r\n.card-title{font-family:var(--font-titulo);font-size:1.2rem;color:var(--azul);display:flex;align-items:center;gap:8px;margin-bottom:16px;letter-spacing:1.5px;word-spacing:1px;line-height:22px}\r\n.info-horario{background:var(--crema);border:1.5px solid var(--crema-oscuro);border-radius:8px;padding:10px 14px;font-size:.85rem;text-align:center;color:var(--azul);font-weight:500;margin-bottom:18px}\r\n.aviso{background:#fffbeb;border:1.5px solid #f0d060;border-radius:10px;padding:12px 14px;font-size:.83rem;line-height:1.65;margin-bottom:18px;color:#5a4a00}\r\n.aviso b{font-weight:700}\r\n.grid-fechas{display:grid;grid-template-columns:1fr 1fr;gap:9px;margin-bottom:16px}\r\n.btn-fecha{background:var(--blanco);border:1.5px solid var(--borde);border-radius:8px;padding:11px 8px;font-size:.86rem;cursor:pointer;text-align:center;color:var(--texto);font-family:var(--font-body);transition:all .15s}\r\n.btn-fecha:hover{border-color:var(--azul);color:var(--azul);background:#edf0f7}\r\n.btn-fecha.sel{background:var(--azul);color:var(--crema);border-color:var(--azul);font-weight:600}\r\n.label-lejana{font-size:.83rem;color:var(--texto-suave);margin-bottom:8px}\r\n.input-fecha{width:100%;border:1.5px solid var(--borde);border-radius:8px;padding:11px 14px;font-size:.9rem;font-family:var(--font-body);outline:none;transition:border-color .15s;background:var(--blanco);color:var(--texto)}\r\n.input-fecha:focus{border-color:var(--azul)}\r\n.buscador-wrap{display:flex;gap:8px;margin-bottom:16px}\r\n.input-buscar{flex:1;border:1.5px solid var(--borde);border-radius:8px;padding:10px 14px;font-size:.88rem;font-family:var(--font-body);outline:none;transition:border-color .15s;background:var(--blanco);min-width:0}\r\n.input-buscar:focus{border-color:var(--azul)}\r\n.btn-borrar{background:var(--texto-suave);color:#fff;border:none;border-radius:8px;padding:10px 14px;font-size:.83rem;cursor:pointer;font-family:var(--font-body);transition:background .15s;white-space:nowrap;flex-shrink:0}\r\n.btn-borrar:hover{background:var(--azul)}\r\n.lista-tartas{display:flex;flex-direction:column;gap:12px}\r\n.tarta-card{display:flex;flex-direction:column;gap:12px;background:var(--blanco);border:1.5px solid var(--borde);border-radius:12px;padding:14px;transition:border-color .15s,box-shadow .15s}\r\n.tarta-card.en-carrito{border-color:var(--azul);background:#f0f4fa}\r\n.tarta-header{display:flex;align-items:center;gap:14px}\r\n.tarta-foto{width:64px;height:64px;border-radius:50%;overflow:hidden;flex-shrink:0;background:var(--crema);border:3px solid var(--crema-oscuro);display:flex;align-items:center;justify-content:center;font-size:1.7rem}\r\n.tarta-foto img{width:100%;height:100%;object-fit:cover;display:block}\r\n.tarta-info{flex:1;min-width:0}\r\n.tarta-nombre{font-size:.95rem;font-weight:600;margin-bottom:3px;color:var(--texto);line-height:1.35}\r\n.tarta-desc{font-size:.77rem;color:var(--texto-suave);font-style:italic}\r\n.tamanos-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:8px}\r\n.btn-precio{border:none;border-radius:10px;padding:9px 10px;font-size:.78rem;font-weight:600;cursor:pointer;color:var(--crema);background:var(--azul);font-family:var(--font-body);line-height:1.3;transition:background .15s,color .15s;text-align:center}\r\n.btn-precio small{display:block;font-size:.68rem;font-weight:400;opacity:.78;margin-top:2px}\r\n.btn-precio:hover{background:var(--crema);color:var(--azul)}\r\n.btn-precio.en-carrito{background:var(--crema);color:var(--azul);box-shadow:0 0 0 2px var(--azul) inset}\r\n.contador-wrap{display:flex;align-items:center;justify-content:space-between;border:1.5px solid var(--azul);border-radius:10px;overflow:hidden;background:var(--blanco);padding:0}\r\n.contador-wrap .cnt-label{font-size:.72rem;color:var(--azul);font-weight:600;padding-left:10px;flex:1;line-height:1.2}\r\n.btn-cnt{background:var(--azul);color:var(--crema);border:none;width:28px;height:34px;font-size:1.05rem;font-weight:700;cursor:pointer;display:flex;align-items:center;justify-content:center;transition:background .15s;font-family:var(--font-body);flex-shrink:0}\r\n.btn-cnt:hover{background:var(--azul-oscuro)}\r\n.cnt-val{min-width:22px;text-align:center;font-size:.82rem;font-weight:700;color:var(--azul);padding:0 2px;flex-shrink:0}\r\n.badge{display:inline-block;font-size:.65rem;font-weight:700;padding:2px 7px;border-radius:50px;margin-left:5px;vertical-align:middle}\r\n.badge-el{background:var(--crema);color:var(--azul);border:1px solid var(--crema-oscuro)}\r\n.badge-sg{background:#d1fae5;color:#065f46}\r\n\/* \u2500\u2500 Contenedor de paneles de toppings (uno por cada tarta\/unidad) \u2500\u2500 *\/\r\n.paneles-toppings-cont{display:flex;flex-direction:column;gap:10px;margin-top:10px}\r\n.panel-unidad{background:#fdf8ee;border:1.5px dashed var(--crema-oscuro);border-radius:10px;padding:12px}\r\n.panel-unidad-head{display:flex;align-items:center;justify-content:space-between;gap:8px;margin-bottom:8px;flex-wrap:wrap}\r\n.panel-toppings-titulo{font-size:.8rem;font-weight:700;color:var(--azul)}\r\n.topping-precio-info{font-weight:400;opacity:.75;font-size:.74rem}\r\n.btn-copiar{background:var(--azul);color:var(--crema);border:none;border-radius:50px;font-size:.68rem;font-weight:700;padding:3px 10px;cursor:pointer;font-family:var(--font-body);white-space:nowrap;transition:background .15s}\r\n.btn-copiar:hover{background:var(--azul-oscuro)}\r\n.toppings-checks{display:grid;grid-template-columns:1fr 1fr;gap:6px}\r\n.topping-item{display:flex;align-items:center;gap:7px;font-size:.78rem;color:var(--texto);cursor:pointer;padding:4px 2px}\r\n.topping-item input{width:16px;height:16px;accent-color:var(--azul);cursor:pointer;flex-shrink:0}\r\n.topping-subtotal{margin-top:8px;font-size:.78rem;font-weight:700;color:var(--azul);text-align:right;min-height:1em}\r\n.btn-siguiente{display:none;background:var(--azul);color:var(--crema);border:none;border-radius:50px;padding:10px 24px;font-size:.88rem;font-weight:700;letter-spacing:.07em;text-transform:uppercase;cursor:pointer;margin-top:18px;font-family:var(--font-body);transition:background .15s}\r\n.btn-siguiente.visible{display:flex;margin-left:auto;margin-right:auto}\r\n.btn-siguiente:hover{background:var(--azul-oscuro)}\r\n.form-grupo{display:flex;flex-direction:column;gap:4px;margin-bottom:14px}\r\n.form-grupo label{font-size:.85rem;font-weight:600;color:var(--texto-suave)}\r\n.form-input{border:1.5px solid var(--borde);border-radius:8px;padding:11px 14px;font-size:.9rem;font-family:var(--font-body);outline:none;transition:border-color .15s;width:100%;background:var(--blanco);color:var(--texto)}\r\n.form-input:focus{border-color:var(--azul)}\r\n.form-input.err{border-color:var(--azul);box-shadow:0 0 0 2px rgba(27,58,107,.15)}\r\n.err-msg{color:var(--azul);font-size:.78rem;margin-top:3px;display:none;font-weight:600}\r\n.err-msg.show{display:block}\r\n.check-wrap{display:flex;align-items:flex-start;gap:10px;margin-bottom:10px;font-size:.82rem;line-height:1.55;color:var(--texto-suave);cursor:pointer}\r\n.check-wrap input{width:17px;height:17px;flex-shrink:0;margin-top:2px;accent-color:var(--azul);cursor:pointer}\r\n.check-wrap a{color:var(--azul);text-decoration:underline}\r\n.btn-confirmar{display:block;width:100%;background:var(--azul);color:var(--crema);border:1.5px solid var(--azul);border-radius:10px;padding:16px;font-size:.98rem;font-weight:700;letter-spacing:.07em;text-transform:uppercase;cursor:pointer;margin-top:18px;font-family:var(--font-body);transition:background .15s,color .15s,transform .1s}\r\n.btn-confirmar:hover{background:var(--crema);color:var(--azul)}\r\n.btn-confirmar:active{transform:scale(.98)}\r\n.btn-confirmar:disabled{opacity:.6;cursor:wait}\r\n.pantalla-exito{text-align:center;padding:36px 24px}\r\n.pantalla-exito .ok-icon{font-size:3.5rem;margin-bottom:14px}\r\n.pantalla-exito h2{font-family:var(--font-titulo);color:var(--azul);font-size:1.8rem;margin-bottom:10px;letter-spacing:.5px}\r\n.pantalla-exito p{color:var(--texto-suave);font-size:.88rem;line-height:1.7;margin-bottom:6px}\r\n.resumen-final{background:var(--crema);border:1.5px solid var(--crema-oscuro);border-radius:12px;padding:16px;margin:18px 0;text-align:left;font-size:.88rem;line-height:2;color:var(--azul)}\r\n.btn-nuevo{background:var(--azul);color:var(--crema);border:1.5px solid var(--azul);border-radius:10px;padding:13px 28px;font-size:.95rem;font-weight:700;cursor:pointer;font-family:var(--font-body);transition:background .15s,color .15s;margin-top:10px}\r\n.btn-nuevo:hover{background:var(--crema);color:var(--azul)}\r\n.btn-volver{display:flex;align-items:center;gap:6px;background:var(--azul);border:1.5px solid var(--azul);border-radius:50px;padding:7px 16px;font-size:.82rem;font-weight:600;color:var(--crema);cursor:pointer;font-family:var(--font-body);margin-bottom:18px;transition:background .15s,color .15s}\r\n.btn-volver:hover{background:var(--crema);color:var(--azul)}\r\n.paso{display:none}\r\n.paso.activo{display:block}\r\n@media (max-width:480px){\r\n  .page-title{font-size:2.8rem}\r\n  .card{margin:0 10px;padding:18px 14px}\r\n  .resumen{margin:0 10px 16px}\r\n  .tamanos-grid{grid-template-columns:repeat(2,1fr)}\r\n  .grid-fechas{grid-template-columns:1fr 1fr}\r\n}\r\n@media (max-width:360px){\r\n  .page-title{font-size:2.2rem}\r\n  .tamanos-grid{grid-template-columns:1fr}\r\n  .tarta-header{flex-wrap:wrap}\r\n  .toppings-checks{grid-template-columns:1fr}\r\n}\r\n@media (min-width:640px){\r\n  .tamanos-grid{grid-template-columns:repeat(4,1fr)}\r\n  .btn-precio{font-size:.74rem;padding:10px 6px}\r\n  .contador-wrap{padding:0 2px}\r\n  .contador-wrap .cnt-label{display:none}\r\n  .btn-cnt{width:26px;height:38px}\r\n}\r\n<\/style>\r\n<\/head>\r\n<body>\r\n\r\n<script>\r\n\/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\r\n   \u2699\ufe0f  FICHA DE DATOS \u2014 el cliente edita SOLO este bloque\r\n   \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 *\/\r\nconst CONFIG = {\r\n  negocio:         \"Creamland\",\r\n  subtitulo:       \"La mejor tarta de queso de Lugo\",\r\n  email:         \"info@creamland.es\",\r\n  horario:         \"Martes a domingo \u00b7 10:00 a 22:00h\",\r\n  privacidad_url:  \"\/politica-de-privacidad\",\r\n\r\n  \/* \u2500\u2500\u2500 Conexi\u00f3n con Fluent Forms \u2500\u2500\u2500\r\n     \u00b7 fluent_dominio: D\u00c9JALO VAC\u00cdO (\"\") si este HTML est\u00e1 dentro de tu\r\n       propia web WordPress (creamland.es). As\u00ed se env\u00eda a la MISMA web y\r\n       NO hay problemas de CORS. Solo pon el dominio completo\r\n       (ej. \"https:\/\/creamland.es\") si el HTML est\u00e1 alojado en otro sitio.\r\n     \u00b7 fluent_form_id: el ID del formulario en Fluent Forms (el tuyo es 3).\r\n  \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 *\/\r\n  fluent_dominio: \"\",\r\n  fluent_form_id: 3,\r\n\r\n  dias_antelacion: 2,\r\n  dias_cerrado:    [1],   \/* 0=dom 1=lun 2=mar 3=mie 4=jue 5=vie 6=sab *\/\r\n  dias_visibles:   14,\r\n\r\n  aviso_alergenos: true,\r\n  texto_alergenos: \"Puede contener trazas, no apto para cel\u00edacos. Las tartas sin gluten comparten obrador aunque est\u00e1n elaboradas con ingredientes sin gluten.\",\r\n  texto_toppings:  \"Puedes elegir un topping distinto para cada tarta al a\u00f1adirla al pedido.\",\r\n\r\n  tamanos: [\"S\", \"M\", \"L\", \"Normal\"],\r\n  tamanos_detalle: {\r\n    \"S\":      \"8 cm\",\r\n    \"M\":      \"10 cm\",\r\n    \"L\":      \"13 cm\",\r\n    \"Normal\": \"24 cm\"\r\n  },\r\n\r\n  \/* \u2500\u2500\u2500 \ud83c\udf6b TOPPINGS \u2500\u2500\u2500\r\n     precio_topping_por_tamano: precio de cada topping SEG\u00daN el tama\u00f1o de\r\n     la tarta. S\/M\/L cuestan 0,70\u20ac; la Normal cuesta 5\u20ac.\r\n  \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 *\/\r\n  precio_topping_por_tamano: {\r\n    \"S\":      0.70,\r\n    \"M\":      0.70,\r\n    \"L\":      0.70,\r\n    \"Normal\": 5.00\r\n  },\r\n  toppings: [\r\n    \"Crema Lotus\",\r\n    \"Crema Milkybar\",\r\n    \"Crema Kinder blanco\",\r\n    \"Crema Pistacho\",\r\n    \"Dulce de Leche\",\r\n    \"Nutella\",\r\n    \"Mermelada de fresa\",\r\n    \"Mermelada de frambuesa\",\r\n    \"Crunch de Oreo\",\r\n    \"Crunch de Chips Ahoy\",\r\n    \"Crunch de Filipinos blancos\",\r\n    \"Crunch de Lotus\",\r\n    \"Crunch de Pistachos\",\r\n    \"Lacasitos\"\r\n  ],\r\n\r\n  \/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\r\n     \ud83c\udf82 TARTAS \u2014 AQU\u00cd SE EDITA TODO\r\n  \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 *\/\r\n  tartas: [\r\n    {\r\n      id: 1,\r\n      nombre: \"Tarta Cl\u00e1sica\",\r\n      etiqueta: \"La cl\u00e1sica\",\r\n      descripcion: \"\",\r\n      precios: { S: 2.99, M: 4.99, L: 9.99, Normal: 22.99 },\r\n      imagen: \"https:\/\/creamland.es\/wp-content\/uploads\/2026\/06\/La-Clasica.webp\",\r\n      emoji: \"\ud83e\uddc0\",\r\n      edicion_limitada: false,\r\n      sin_gluten: false\r\n    },\r\n    {\r\n      id: 2,\r\n      nombre: \"Tarta de Oreo\",\r\n      etiqueta: \"La m\u00e1s crujiente\",\r\n      descripcion: \"\",\r\n      precios: { S: 3.99, M: 5.99, L: 10.99, Normal: 29.99 },\r\n      imagen: \"https:\/\/creamland.es\/wp-content\/uploads\/2026\/06\/Oreo.webp\",\r\n      emoji: \"\ud83d\udda4\",\r\n      edicion_limitada: false,\r\n      sin_gluten: false\r\n    },\r\n    {\r\n      id: 3,\r\n      nombre: \"Tarta de Lotus\",\r\n      etiqueta: \"Con Lotus\",\r\n      descripcion: \"\",\r\n      precios: { S: 3.99, M: 5.99, L: 10.99, Normal: 29.99 },\r\n      imagen: \"https:\/\/creamland.es\/wp-content\/uploads\/2026\/06\/Lotus.webp\",\r\n      emoji: \"\ud83c\udf6a\",\r\n      edicion_limitada: false,\r\n      sin_gluten: false\r\n    },\r\n    {\r\n      id: 4,\r\n      nombre: \"Tarta de Milkybar de Pistacho\",\r\n      etiqueta: \"Con pistacho\",\r\n      descripcion: \"\",\r\n      precios: { S: 3.99, M: 5.99, L: 10.99, Normal: 29.99 },\r\n      imagen: \"https:\/\/creamland.es\/wp-content\/uploads\/2026\/06\/Milkybar-de-Pistacho.webp\",\r\n      emoji: \"\ud83c\udf3f\",\r\n      edicion_limitada: false,\r\n      sin_gluten: false\r\n    },\r\n    {\r\n      id: 5,\r\n      nombre: \"Tarta de Chocolate Blanco Milkybar\",\r\n      etiqueta: \"Muy cremosa\",\r\n      descripcion: \"\",\r\n      precios: { S: 3.99, M: 5.99, L: 10.99, Normal: 29.99 },\r\n      imagen: \"https:\/\/creamland.es\/wp-content\/uploads\/2026\/06\/Milkybar-de-Chocolate-blanco.webp\",\r\n      emoji: \"\ud83e\udd0d\",\r\n      edicion_limitada: false,\r\n      sin_gluten: false\r\n    },\r\n    {\r\n      id: 6,\r\n      nombre: \"Tarta de Happy Hippo\",\r\n      etiqueta: \"Con Kinder\",\r\n      descripcion: \"\",\r\n      precios: { S: 3.99, M: 5.99, L: 10.99, Normal: 29.99 },\r\n      imagen: \"https:\/\/creamland.es\/wp-content\/uploads\/2026\/06\/Happy-hippo.webp\",\r\n      emoji: \"\ud83c\udf6b\",\r\n      edicion_limitada: false,\r\n      sin_gluten: false\r\n    },\r\n    {\r\n      id: 7,\r\n      nombre: \"Tarta Especial del Mes\",\r\n      etiqueta: \"Edici\u00f3n limitada\",\r\n      descripcion: \"Consultar disponibilidad\",\r\n      precios: { S: 3.99, M: 5.99, L: 10.99, Normal: 29.99 },\r\n      imagen: \"\",\r\n      emoji: \"\u2b50\",\r\n      edicion_limitada: true,\r\n      sin_gluten: false\r\n    }\r\n  ]\r\n};\r\n\/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 *\/\r\n\r\n\/* \u2500\u2500 Estado \u2500\u2500\r\n   Cada l\u00ednea del carrito guarda `unidades`: un array con una posici\u00f3n por\r\n   cada tarta pedida. Cada posici\u00f3n es a su vez un array con los toppings\r\n   elegidos PARA ESA tarta concreta. Ej: qty 3 con toppings distintos \u2192\r\n   unidades = [ [\"Nutella\"], [\"Crema Lotus\",\"Lacasitos\"], [] ]\r\n*\/\r\nconst E = {\r\n  paso: 1,\r\n  fecha: null,\r\n  fecha_texto: null,\r\n  carrito: []   \/\/ [{ k, tarta, tam, precio, qty, unidades:[[topping,...], ...] }]\r\n};\r\n\r\n\/* \u2500\u2500 Helpers \u2500\u2500 *\/\r\nconst DIAS  = [\"Domingo\",\"Lunes\",\"Martes\",\"Mi\u00e9rcoles\",\"Jueves\",\"Viernes\",\"S\u00e1bado\"];\r\nconst MESES = [\"enero\",\"febrero\",\"marzo\",\"abril\",\"mayo\",\"junio\",\"julio\",\"agosto\",\"septiembre\",\"octubre\",\"noviembre\",\"diciembre\"];\r\nconst fmtF  = d => `${DIAS[d.getDay()]} ${d.getDate()} de ${MESES[d.getMonth()]}`;\r\nconst fmtISO= d => `${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}-${String(d.getDate()).padStart(2,'0')}`;\r\nconst cerrado = d => CONFIG.dias_cerrado.includes(d.getDay());\r\nconst fmtEuro = n => n.toFixed(2).replace('.', ',');\r\n\r\n\/* Precio de un topping para un tama\u00f1o concreto *\/\r\nfunction precioToppingTam(tam){\r\n  return CONFIG.precio_topping_por_tamano[tam] ?? 0;\r\n}\r\n\r\nfunction proxFechas(){\r\n  const res=[], d=new Date(); d.setHours(0,0,0,0); d.setDate(d.getDate()+CONFIG.dias_antelacion);\r\n  while(res.length<CONFIG.dias_visibles){ if(!cerrado(d)) res.push(new Date(d)); d.setDate(d.getDate()+1) }\r\n  return res;\r\n}\r\n\r\n\/* \u2500\u2500 Carrito \u2500\u2500 *\/\r\nfunction carritoKey(id, tam){ return `${id}_${tam}` }\r\n\r\nfunction carritoAdd(tarta, tam, precio){\r\n  const k = carritoKey(tarta.id, tam);\r\n  const ex = E.carrito.find(x => x.k === k);\r\n  if(ex){ ex.qty++; ex.unidades.push([]); }              \/\/ nueva tarta = nueva unidad vac\u00eda\r\n  else { E.carrito.push({ k, tarta, tam, precio, qty:1, unidades:[[]] }); }\r\n  actualizarUI();\r\n}\r\nfunction carritoQuit(k){\r\n  const idx = E.carrito.findIndex(x => x.k === k);\r\n  if(idx === -1) return;\r\n  if(E.carrito[idx].qty > 1){ E.carrito[idx].qty--; E.carrito[idx].unidades.pop(); }\r\n  else { E.carrito.splice(idx, 1); }\r\n  actualizarUI();\r\n}\r\nfunction carritoEliminar(k){\r\n  E.carrito = E.carrito.filter(x => x.k !== k);\r\n  actualizarUI();\r\n}\r\n\r\n\/* Subtotal de una l\u00ednea: precio base \u00d7 cantidad + toppings de cada unidad *\/\r\nfunction subtotalLinea(x){\r\n  const base = x.precio * x.qty;\r\n  const tops = x.unidades.reduce((s, arr) => s + arr.length * precioToppingTam(x.tam), 0);\r\n  return base + tops;\r\n}\r\nfunction totalCarrito(){\r\n  return E.carrito.reduce((s,x) => s + subtotalLinea(x), 0);\r\n}\r\n\r\n\/* \u2500\u2500 Toppings por UNIDAD \u2500\u2500 *\/\r\nfunction toppingToggle(k, unidadIdx, nombre){\r\n  const item = E.carrito.find(x => x.k === k);\r\n  if(!item || !item.unidades[unidadIdx]) return;\r\n  const arr = item.unidades[unidadIdx];\r\n  const i = arr.indexOf(nombre);\r\n  if(i === -1) arr.push(nombre);\r\n  else arr.splice(i, 1);\r\n  actualizarUI();\r\n}\r\n\/* Copiar los toppings de una unidad a TODAS las tartas de esa l\u00ednea *\/\r\nfunction copiarToppingsATodas(k, unidadIdx){\r\n  const item = E.carrito.find(x => x.k === k);\r\n  if(!item || !item.unidades[unidadIdx]) return;\r\n  const modelo = item.unidades[unidadIdx].slice();\r\n  item.unidades = item.unidades.map(() => modelo.slice());\r\n  actualizarUI();\r\n}\r\n\r\n\/* \u2500\u2500 UI \u2500\u2500 *\/\r\nfunction actualizarUI(){\r\n  resumen();\r\n  actualizarContadores();\r\n  const btn = document.getElementById('btn-siguiente');\r\n  if(E.carrito.length > 0) btn.classList.add('visible');\r\n  else btn.classList.remove('visible');\r\n}\r\n\r\n\/* HTML de los paneles de toppings: uno por cada tarta de la l\u00ednea *\/\r\nfunction panelesUnidadHTML(item){\r\n  const precioTop = precioToppingTam(item.tam);\r\n  let html = '';\r\n  for(let u = 0; u < item.qty; u++){\r\n    const checks = CONFIG.toppings.map(top => {\r\n      const checked = item.unidades[u].includes(top) ? 'checked' : '';\r\n      const safe = top.replace(\/'\/g, \"\\\\'\");\r\n      const val  = top.replace(\/\"\/g, '&quot;');\r\n      return `<label class=\"topping-item\">\r\n        <input type=\"checkbox\" ${checked} value=\"${val}\"\r\n          onchange=\"toppingToggle('${item.k}',${u},'${safe}')\">\r\n        <span>${top}<\/span>\r\n      <\/label>`;\r\n    }).join('');\r\n    const sub = item.unidades[u].length * precioTop;\r\n    const tituloUnidad = item.qty > 1 ? `Tarta ${u+1} de ${item.qty} \u00b7 ${item.tam}` : `Toppings para ${item.tam}`;\r\n    const btnCopiar = item.qty > 1\r\n      ? `<button class=\"btn-copiar\" onclick=\"copiarToppingsATodas('${item.k}',${u})\">Aplicar a todas<\/button>`\r\n      : '';\r\n    html += `<div class=\"panel-unidad\">\r\n      <div class=\"panel-unidad-head\">\r\n        <span class=\"panel-toppings-titulo\">\ud83c\udf6b ${tituloUnidad} <span class=\"topping-precio-info\">(+${fmtEuro(precioTop)}\u20ac\/topping)<\/span><\/span>\r\n        ${btnCopiar}\r\n      <\/div>\r\n      <div class=\"toppings-checks\">${checks}<\/div>\r\n      <div class=\"topping-subtotal\">${sub > 0 ? `+ ${fmtEuro(sub)}\u20ac en toppings` : ''}<\/div>\r\n    <\/div>`;\r\n  }\r\n  return html;\r\n}\r\n\r\nfunction actualizarContadores(){\r\n  document.querySelectorAll('.tarta-card').forEach(card => {\r\n    const id = parseInt(card.dataset.id);\r\n    card.querySelectorAll('.btn-precio').forEach(btn => {\r\n      const tam = btn.dataset.tam;\r\n      const k   = carritoKey(id, tam);\r\n      const item = E.carrito.find(x => x.k === k);\r\n      const wrap = card.querySelector(`.contador-wrap[data-k=\"${k}\"]`);\r\n      const cont = card.querySelector(`.paneles-toppings-cont[data-k=\"${k}\"]`);\r\n      if(item){\r\n        btn.style.display = 'none';\r\n        if(wrap){ wrap.style.display='flex'; wrap.querySelector('.cnt-val').textContent = item.qty; }\r\n        if(cont){ cont.style.display='block'; cont.innerHTML = panelesUnidadHTML(item); }\r\n      } else {\r\n        btn.style.display = '';\r\n        if(wrap) wrap.style.display='none';\r\n        if(cont){ cont.style.display='none'; cont.innerHTML=''; }\r\n      }\r\n    });\r\n    card.classList.toggle('en-carrito', E.carrito.some(x => x.tarta.id === id));\r\n  });\r\n}\r\n\r\n\/* \u2500\u2500 Paso 1 \u2500\u2500 *\/\r\nfunction renderFechas(){\r\n  const g = document.getElementById('grid-fechas'); g.innerHTML='';\r\n  proxFechas().forEach(f => {\r\n    const b = document.createElement('button');\r\n    b.className = 'btn-fecha' + (E.fecha===fmtISO(f)?' sel':'');\r\n    b.textContent = fmtF(f); b.dataset.iso = fmtISO(f); b.dataset.txt = fmtF(f);\r\n    b.onclick = () => pickFecha(b);\r\n    g.appendChild(b);\r\n  });\r\n  const min = new Date(); min.setHours(0,0,0,0); min.setDate(min.getDate()+CONFIG.dias_antelacion);\r\n  while(cerrado(min)) min.setDate(min.getDate()+1);\r\n  document.getElementById('input-fecha').min = fmtISO(min);\r\n}\r\nfunction pickFecha(b){\r\n  document.querySelectorAll('.btn-fecha').forEach(x=>x.classList.remove('sel'));\r\n  b.classList.add('sel'); E.fecha=b.dataset.iso; E.fecha_texto=b.dataset.txt;\r\n  document.getElementById('input-fecha').value='';\r\n  resumen(); setTimeout(()=>irA(2), 200);\r\n}\r\nfunction onFechaManual(e){\r\n  const v=e.target.value; if(!v) return;\r\n  const [y,m,d]=v.split('-').map(Number); const f=new Date(y,m-1,d);\r\n  if(cerrado(f)){ alert('Ese d\u00eda estamos cerrados. Elige otro d\u00eda.'); e.target.value=''; return; }\r\n  document.querySelectorAll('.btn-fecha').forEach(x=>x.classList.remove('sel'));\r\n  E.fecha=v; E.fecha_texto=fmtF(f); resumen(); setTimeout(()=>irA(2), 200);\r\n}\r\n\r\n\/* \u2500\u2500 Paso 2 \u2500\u2500 *\/\r\nfunction renderTartas(q){\r\n  const l = document.getElementById('lista-tartas'); l.innerHTML='';\r\n  const query=(q||'').toLowerCase().trim();\r\n  const vis = CONFIG.tartas.filter(t=>!query||t.nombre.toLowerCase().includes(query)||(t.etiqueta||'').toLowerCase().includes(query));\r\n  if(!vis.length){\r\n    l.innerHTML='<p style=\"color:var(--texto-suave);text-align:center;padding:24px 0;font-style:italic\">Sin resultados.<\/p>';\r\n    return;\r\n  }\r\n  vis.forEach(t => {\r\n    const div = document.createElement('div');\r\n    div.className = 'tarta-card'; div.dataset.id = t.id;\r\n    const foto = t.imagen\r\n      ? `<div class=\"tarta-foto\"><img decoding=\"async\" src=\"${t.imagen}\" alt=\"${t.nombre}\" loading=\"lazy\"><\/div>`\r\n      : `<div class=\"tarta-foto\">${t.emoji}<\/div>`;\r\n    const bdgEL = t.edicion_limitada ? `<span class=\"badge badge-el\">ED. LIMITADA<\/span>` : '';\r\n    const bdgSG = t.sin_gluten       ? `<span class=\"badge badge-sg\">SIN GLUTEN<\/span>`   : '';\r\n\r\n    let botonesTamano = '';\r\n    let contenedoresToppings = '';\r\n\r\n    CONFIG.tamanos.forEach(tam => {\r\n      const precio = t.precios[tam];\r\n      if(precio === null || precio === undefined) return;\r\n      const k = carritoKey(t.id, tam);\r\n      const detalle = CONFIG.tamanos_detalle[tam] || '';\r\n\r\n      botonesTamano += `\r\n        <button class=\"btn-precio\" data-id=\"${t.id}\" data-tam=\"${tam}\" data-p=\"${precio}\"\r\n          onclick=\"carritoAdd(CONFIG.tartas.find(x=>x.id==${t.id}),'${tam}',${precio})\">\r\n          ${tam} \u00b7 ${fmtEuro(precio)}\u20ac<small>${detalle}<\/small>\r\n        <\/button>\r\n        <div class=\"contador-wrap\" data-k=\"${k}\" style=\"display:none\">\r\n          <button class=\"btn-cnt\" onclick=\"carritoQuit('${k}')\">\u2212<\/button>\r\n          <span class=\"cnt-label\">${tam}<\/span>\r\n          <span class=\"cnt-val\">1<\/span>\r\n          <button class=\"btn-cnt\" onclick=\"carritoAdd(CONFIG.tartas.find(x=>x.id==${t.id}),'${tam}',${precio})\">+<\/button>\r\n        <\/div>`;\r\n\r\n      \/* contenedor (vac\u00edo) donde se pintan los paneles por unidad *\/\r\n      contenedoresToppings += `<div class=\"paneles-toppings-cont\" data-k=\"${k}\" style=\"display:none\"><\/div>`;\r\n    });\r\n\r\n    div.innerHTML = `\r\n      <div class=\"tarta-header\">\r\n        ${foto}\r\n        <div class=\"tarta-info\">\r\n          <div class=\"tarta-nombre\">${t.nombre}${bdgEL}${bdgSG}<\/div>\r\n          ${t.descripcion ? `<div class=\"tarta-desc\">${t.descripcion}<\/div>` : ''}\r\n        <\/div>\r\n      <\/div>\r\n      <div class=\"tamanos-grid\">${botonesTamano}<\/div>\r\n      ${contenedoresToppings}`;\r\n    l.appendChild(div);\r\n  });\r\n  actualizarContadores();\r\n}\r\nfunction onBuscar(e){ renderTartas(e.target.value) }\r\nfunction borrarFiltro(){ document.getElementById('buscador').value=''; renderTartas('') }\r\n\r\n\/* \u2500\u2500 Resumen \u2500\u2500 *\/\r\nfunction resumen(){\r\n  const el = document.getElementById('resumen');\r\n  let h = `<strong>Reserva:<\/strong>`;\r\n  if(E.fecha_texto) h += ` ${E.fecha_texto}`;\r\n  if(E.carrito.length > 0){\r\n    E.carrito.forEach(x => {\r\n      let toppingsTxt = '';\r\n      x.unidades.forEach((arr, i) => {\r\n        if(arr.length){\r\n          const etiqueta = x.qty > 1 ? `Toppings de la Tarta ${i+1}` : 'Toppings';\r\n          toppingsTxt += `<div style=\"font-size:.74rem;opacity:.85;padding-left:10px\">+ ${etiqueta}: ${arr.join(', ')}<\/div>`;\r\n        }\r\n      });\r\n      h += `<div class=\"resumen-linea\">\r\n        <span>\u2192 ${x.tarta.nombre} \u00b7 ${x.tam} \u00d7 ${x.qty} = ${fmtEuro(subtotalLinea(x))}\u20ac<\/span>\r\n        <button class=\"btn-eliminar\" onclick=\"carritoEliminar('${x.k}')\">Quitar<\/button>\r\n      <\/div>${toppingsTxt}`;\r\n    });\r\n    if(E.paso === 3){\r\n      const notas = document.getElementById('notas') ? document.getElementById('notas').value.trim() : '';\r\n      if(notas) h += `<div style=\"margin-top:8px;font-size:.82rem;opacity:.9\">\ud83d\udcdd ${notas}<\/div>`;\r\n    }\r\n    h += `<div class=\"resumen-total\">Total: ${fmtEuro(totalCarrito())}\u20ac<\/div>\r\n          <div class=\"resumen-nota\">A pagar en tienda<\/div>\r\n          <div class=\"resumen-sub\">El total ser\u00e1 confirmado al recoger en el local.<\/div>`;\r\n  }\r\n  el.innerHTML = h;\r\n}\r\n\r\n\/* \u2500\u2500 Navegaci\u00f3n \u2500\u2500 *\/\r\nfunction irA(n){\r\n  E.paso = n;\r\n  document.querySelectorAll('.paso').forEach(s=>s.classList.remove('activo'));\r\n  document.getElementById(`paso${n}`).classList.add('activo');\r\n  document.querySelectorAll('.step-pill').forEach((p,i)=>{\r\n    p.classList.remove('activo','hecho');\r\n    if(i+1===n) p.classList.add('activo');\r\n    else if(i+1<n) p.classList.add('hecho');\r\n  });\r\n  resumen();\r\n  window.scrollTo({top:0,behavior:'smooth'});\r\n}\r\n\r\n\/* \u2500\u2500 Texto plano del pedido (para el campo tartas_1 de Fluent Forms) \u2500\u2500 *\/\r\nfunction textoPedido(){\r\n  return E.carrito.map(x=>{\r\n    let linea = `${x.tarta.nombre} (${x.tam}) x${x.qty} = ${fmtEuro(subtotalLinea(x))}\u20ac`;\r\n    const totalTops = x.unidades.reduce((s,a)=>s+a.length,0);\r\n    if(totalTops > 0){\r\n      x.unidades.forEach((arr,i)=>{\r\n        linea += `\\n   \u00b7 Toppings de la Tarta ${i+1}: ${arr.length ? arr.join(', ') : '(sin toppings)'}`;\r\n      });\r\n    }\r\n    return linea;\r\n  }).join('\\n');\r\n}\r\n\r\n\/* Saca el primer mensaje de texto de un error, est\u00e9 como est\u00e9 anidado\r\n   (Fluent Forms devuelve cosas como { email: { required: \"...\" } }) *\/\r\nfunction extraerMensaje(x){\r\n  if(x == null) return null;\r\n  if(typeof x === 'string') return x;\r\n  if(Array.isArray(x)){ for(const v of x){ const m=extraerMensaje(v); if(m) return m; } return null; }\r\n  if(typeof x === 'object'){ for(const k in x){ const m=extraerMensaje(x[k]); if(m) return m; } return null; }\r\n  return String(x);\r\n}\r\n\r\n\/* \u2500\u2500 Validaci\u00f3n y env\u00edo \u2500\u2500 *\/\r\nfunction enviar(){\r\n  const nombre = document.getElementById('nombre').value.trim();\r\n  const tel    = document.getElementById('tel').value.trim();\r\n  const mail   = document.getElementById('mail').value.trim();\r\n  const priv   = document.getElementById('chk-priv').checked;\r\n  let ok = true;\r\n\r\n  [{id:'nombre',v:nombre,e:'e-nombre',m:'Introduce tu nombre.'},\r\n   {id:'tel',   v:tel,   e:'e-tel',   m:'Introduce tu tel\u00e9fono.'},\r\n   {id:'mail',  v:mail,  e:'e-mail',  m:'Introduce tu email.'}\r\n  ].forEach(c=>{\r\n    const el=document.getElementById(c.e);\r\n    if(!c.v){ document.getElementById(c.id).classList.add('err'); el.textContent=c.m; el.classList.add('show'); ok=false }\r\n    else    { document.getElementById(c.id).classList.remove('err'); el.classList.remove('show') }\r\n  });\r\n\r\n  if(!priv){ alert('Debes aceptar la Pol\u00edtica de Privacidad para continuar.'); ok=false }\r\n  if(!E.fecha)          { alert('Falta la fecha de recogida.'); ok=false }\r\n  if(!E.carrito.length) { alert('No has seleccionado ninguna tarta.'); ok=false }\r\n  if(!ok) return;\r\n\r\n  const notas = document.getElementById('notas').value.trim();\r\n  const datos = { nombre, tel, mail, notas };\r\n\r\n  const btn = document.querySelector('.btn-confirmar');\r\n  btn.disabled = true;\r\n  btn.textContent = 'Enviando...';\r\n\r\n  \/* \u2500\u2500 ENV\u00cdO A FLUENT FORMS (v\u00eda admin-ajax.php) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\n     Fluent Forms NO recibe las reservas por \/wp-json\/... sino por\r\n     admin-ajax.php con la acci\u00f3n \"fluentform_submit\". El truco es\r\n     que TODOS los campos del formulario (nombre, telefono, email,\r\n     fecha, tartas_1, total, notas) van dentro del par\u00e1metro \"data\"\r\n     como una cadena codificada tipo query-string.\r\n     Los nombres deben COINCIDIR EXACTAMENTE con los del formulario 3.\r\n  \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 *\/\r\n  const campos = new URLSearchParams();\r\n  campos.append('nombre[first_name]', nombre);   \/\/ el campo \"Nombre\" es de tipo Nombre \u2192 subcampo first_name\r\n  campos.append('telefono', tel);\r\n  campos.append('email',    mail);\r\n  campos.append('fecha',    E.fecha_texto);\r\n  campos.append('tartas_1', textoPedido());\r\n  campos.append('total',    fmtEuro(totalCarrito()) + '\u20ac');\r\n  campos.append('notas',    notas);\r\n\r\n  const body = new URLSearchParams();\r\n  body.append('action',  'fluentform_submit');\r\n  body.append('form_id', String(CONFIG.fluent_form_id));\r\n  body.append('data',    campos.toString());\r\n\r\n  const url = `${CONFIG.fluent_dominio}\/wp-admin\/admin-ajax.php`;\r\n\r\n  fetch(url, {\r\n    method: 'POST',\r\n    headers: { 'X-Requested-With': 'XMLHttpRequest' },\r\n    body: body\r\n  })\r\n  .then(async r => {\r\n    let json = null;\r\n    try { json = await r.json(); } catch(e){ \/* respuesta no JSON *\/ }\r\n    \/\/ Fluent Forms responde 200 con { data: {...} } cuando todo va bien.\r\n    const okEnvio = r.ok && json && json.data && !json.errors;\r\n    if(okEnvio){\r\n      exito(datos);\r\n    } else {\r\n      btn.disabled = false;\r\n      btn.textContent = 'Confirmar reserva';\r\n      let msg = 'Hubo un problema al enviar la reserva. Por favor int\u00e9ntalo de nuevo.';\r\n      const errs = (json && (json.errors || (json.data && json.data.errors)));\r\n      if(errs){\r\n        \/\/ Busca el nombre del campo y el primer mensaje de texto (por muy anidado que est\u00e9)\r\n        const campo = Object.keys(errs)[0];\r\n        const texto = extraerMensaje(errs[campo]);\r\n        msg = (campo ? `Campo \"${campo}\": ` : '') + (texto || 'dato no v\u00e1lido');\r\n      } else if(json && json.message){\r\n        msg = extraerMensaje(json.message) || msg;\r\n      }\r\n      alert(msg);\r\n      console.warn('Respuesta completa de Fluent Forms:', r.status, JSON.stringify(json, null, 2));\r\n    }\r\n  })\r\n  .catch(err => {\r\n    btn.disabled = false;\r\n    btn.textContent = 'Confirmar reserva';\r\n    alert('Error de conexi\u00f3n. Revisa que el HTML est\u00e9 en la misma web que Fluent Forms.');\r\n    console.error(err);\r\n  });\r\n}\r\n\r\nfunction exito(d){\r\n  const lineas = E.carrito.map(x=>{\r\n    let l = `\ud83c\udf82 ${x.tarta.nombre} (${x.tam}) \u00d7 ${x.qty} = ${fmtEuro(subtotalLinea(x))}\u20ac`;\r\n    const totalTops = x.unidades.reduce((s,a)=>s+a.length,0);\r\n    if(totalTops > 0){\r\n      x.unidades.forEach((arr,i)=>{\r\n        l += `<br><span style=\"opacity:.8\">&nbsp;&nbsp;&nbsp;\u00b7 Toppings de la Tarta ${i+1}: ${arr.length ? arr.join(', ') : '(sin toppings)'}<\/span>`;\r\n      });\r\n    }\r\n    return l;\r\n  }).join('<br>');\r\n  document.getElementById('res-exito').innerHTML =\r\n    `\ud83d\udcc5 <b>Fecha:<\/b> ${E.fecha_texto}<br>${lineas}<br>` +\r\n    `\ud83d\udcb0 <b>Total:<\/b> ${fmtEuro(totalCarrito())}\u20ac<br>` +\r\n    `\ud83d\udc64 <b>Nombre:<\/b> ${d.nombre}<br>\ud83d\udcde <b>Tel\u00e9fono:<\/b> ${d.tel}<br>\ud83d\udce7 <b>Email:<\/b> ${d.mail}` +\r\n    (d.notas ? `<br>\ud83d\udcdd <b>Notas:<\/b> ${d.notas}` : '');\r\n  ['page-title','page-sub'].forEach(c=>{ const el=document.querySelector('.'+c); if(el) el.style.display='none' });\r\n  document.getElementById('steps-nav').style.display='none';\r\n  document.getElementById('resumen').style.display='none';\r\n  document.querySelectorAll('.paso').forEach(s=>s.style.display='none');\r\n  document.getElementById('exito').style.display='block';\r\n}\r\n\r\nfunction reset(){\r\n  E.paso=1; E.fecha=null; E.fecha_texto=null; E.carrito=[];\r\n  ['nombre','tel','mail','notas'].forEach(id=>document.getElementById(id).value='');\r\n  document.getElementById('chk-priv').checked=false;\r\n  document.getElementById('chk-com').checked=false;\r\n  const btn = document.querySelector('.btn-confirmar');\r\n  btn.disabled = false; btn.textContent = 'Confirmar reserva';\r\n  ['page-title','page-sub'].forEach(c=>{ const el=document.querySelector('.'+c); if(el) el.style.display='' });\r\n  document.getElementById('steps-nav').style.display='';\r\n  document.getElementById('resumen').style.display='';\r\n  document.querySelectorAll('.paso').forEach(s=>s.style.display='');\r\n  document.getElementById('exito').style.display='none';\r\n  renderFechas(); renderTartas(''); resumen(); irA(1);\r\n}\r\n\r\ndocument.addEventListener('DOMContentLoaded',()=>{\r\n  document.querySelector('.page-title').textContent = 'Realizar una reserva';\r\n  document.querySelector('.page-sub').textContent   = CONFIG.subtitulo;\r\n  document.getElementById('info-horario').textContent = CONFIG.horario;\r\n  if(!CONFIG.aviso_alergenos) document.getElementById('aviso').style.display='none';\r\n  document.getElementById('txt-alergenos').innerHTML =\r\n    `<b>Sobre al\u00e9rgenos:<\/b> ${CONFIG.texto_alergenos}<br><b>Toppings y extras:<\/b> ${CONFIG.texto_toppings}`;\r\n  renderFechas(); renderTartas(''); resumen(); irA(1);\r\n});\r\n<\/script>\r\n\r\n<div class=\"contenedor\">\r\n\r\n<h1 class=\"page-title\">Realizar una reserva<\/h1>\r\n<p class=\"page-sub\"><\/p>\r\n\r\n<nav class=\"steps-nav\" id=\"steps-nav\">\r\n  <span class=\"step-pill activo\">1. Fecha<\/span>\r\n  <span class=\"step-pill\">2. Sabores<\/span>\r\n  <span class=\"step-pill\">3. Datos<\/span>\r\n<\/nav>\r\n\r\n<div class=\"resumen\" id=\"resumen\"><strong>Reserva:<\/strong><\/div>\r\n\r\n<!-- PASO 1 -->\r\n<section class=\"paso activo\" id=\"paso1\">\r\n  <div class=\"card\">\r\n    <div class=\"card-title\">\ud83d\udcc5 Fecha de recogida<\/div>\r\n    <div class=\"info-horario\" id=\"info-horario\"><\/div>\r\n    <div class=\"grid-fechas\" id=\"grid-fechas\"><\/div>\r\n    <p class=\"label-lejana\">O selecciona una fecha m\u00e1s lejana...<\/p>\r\n    <input type=\"date\" class=\"input-fecha\" id=\"input-fecha\" onchange=\"onFechaManual(event)\">\r\n  <\/div>\r\n<\/section>\r\n\r\n<!-- PASO 2 -->\r\n<section class=\"paso\" id=\"paso2\">\r\n  <div class=\"card\">\r\n    <button class=\"btn-volver\" onclick=\"irA(1)\">\u2190 Cambiar fecha<\/button>\r\n    <div class=\"card-title\">\ud83c\udf82 Elige tus tartas<\/div>\r\n    <div class=\"buscador-wrap\">\r\n      <input type=\"text\" class=\"input-buscar\" id=\"buscador\"\r\n        placeholder=\"Busca un sabor... Ej: Oreo\" oninput=\"onBuscar(event)\">\r\n      <button class=\"btn-borrar\" onclick=\"borrarFiltro()\">Borrar filtro<\/button>\r\n    <\/div>\r\n    <div class=\"lista-tartas\" id=\"lista-tartas\"><\/div>\r\n    <div class=\"aviso\" id=\"aviso\" style=\"margin-top:16px\"><span id=\"txt-alergenos\"><\/span><\/div>\r\n    <div class=\"form-grupo\" style=\"margin-top:14px\">\r\n      <label for=\"notas\">Notas del pedido (alergias, dedicatoria...)<\/label>\r\n      <textarea class=\"form-input\" id=\"notas\" rows=\"3\"\r\n        placeholder=\"Ej: sin frutos secos, poner nombre en la tarta...\"><\/textarea>\r\n    <\/div>\r\n    <button class=\"btn-siguiente\" id=\"btn-siguiente\" onclick=\"irA(3)\">\r\n      Siguiente \u2192 Datos de contacto\r\n    <\/button>\r\n  <\/div>\r\n<\/section>\r\n\r\n<!-- PASO 3 -->\r\n<section class=\"paso\" id=\"paso3\">\r\n  <div class=\"card\">\r\n    <button class=\"btn-volver\" onclick=\"irA(2)\">\u2190 Cambiar tartas<\/button>\r\n    <div class=\"card-title\">\ud83d\udc64 Datos de contacto<\/div>\r\n    <div class=\"form-grupo\">\r\n      <label for=\"nombre\">Nombre<\/label>\r\n      <input type=\"text\" class=\"form-input\" id=\"nombre\" placeholder=\"Tu nombre completo\">\r\n      <span class=\"err-msg\" id=\"e-nombre\"><\/span>\r\n    <\/div>\r\n    <div class=\"form-grupo\">\r\n      <label for=\"tel\">Tel\u00e9fono<\/label>\r\n      <input type=\"tel\" class=\"form-input\" id=\"tel\" placeholder=\"600 000 000\">\r\n      <span class=\"err-msg\" id=\"e-tel\"><\/span>\r\n    <\/div>\r\n    <div class=\"form-grupo\">\r\n      <label for=\"mail\">Correo electr\u00f3nico<\/label>\r\n      <input type=\"email\" class=\"form-input\" id=\"mail\" placeholder=\"tu@email.com\">\r\n      <span class=\"err-msg\" id=\"e-mail\"><\/span>\r\n    <\/div>\r\n    <label class=\"check-wrap\">\r\n      <input type=\"checkbox\" id=\"chk-priv\">\r\n      He le\u00eddo y acepto la <a href=\"\/politica-de-privacidad\" target=\"_blank\">&nbsp;Pol\u00edtica de Privacidad<\/a>\r\n    <\/label>\r\n    <label class=\"check-wrap\">\r\n      <input type=\"checkbox\" id=\"chk-com\">\r\n      Acepto recibir comunicaciones comerciales y novedades de Creamland\r\n    <\/label>\r\n    <button class=\"btn-confirmar\" onclick=\"enviar()\">Confirmar reserva<\/button>\r\n  <\/div>\r\n<\/section>\r\n\r\n<!-- \u00c9XITO -->\r\n<div class=\"card pantalla-exito\" id=\"exito\" style=\"display:none\">\r\n  <div class=\"ok-icon\">\ud83c\udf89<\/div>\r\n  <h2>\u00a1Reserva recibida!<\/h2>\r\n  <p>Te hemos enviado un email de confirmaci\u00f3n.<br>Nos pondremos en contacto si fuera necesario.<\/p>\r\n  <div class=\"resumen-final\" id=\"res-exito\"><\/div>\r\n  <button class=\"btn-nuevo\" onclick=\"reset()\">Hacer otra reserva<\/button>\r\n<\/div>\r\n\r\n<\/div><!-- \/.contenedor -->\r\n\r\n<\/body>\r\n<\/html>\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t","protected":false},"excerpt":{"rendered":"<p>Realizar una reserva &#8211; Creamland Realizar una reserva 1. Fecha 2. Sabores 3. Datos Reserva: \ud83d\udcc5 Fecha de recogida O selecciona una fecha m\u00e1s lejana&#8230; \u2190 Cambiar fecha \ud83c\udf82 Elige tus tartas Borrar filtro Notas del pedido (alergias, dedicatoria&#8230;) Siguiente \u2192 Datos de contacto \u2190 Cambiar tartas \ud83d\udc64 Datos de contacto Nombre Tel\u00e9fono Correo electr\u00f3nico [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"_joinchat":[],"footnotes":""},"class_list":["post-791","page","type-page","status-publish","hentry"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v28.0 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Reserva - Creamland<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/creamland.es\/index.php\/reservar\/\" \/>\n<meta property=\"og:locale\" content=\"es_ES\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Reserva - Creamland\" \/>\n<meta property=\"og:description\" content=\"Realizar una reserva &#8211; Creamland Realizar una reserva 1. Fecha 2. Sabores 3. Datos Reserva: \ud83d\udcc5 Fecha de recogida O selecciona una fecha m\u00e1s lejana&#8230; \u2190 Cambiar fecha \ud83c\udf82 Elige tus tartas Borrar filtro Notas del pedido (alergias, dedicatoria&#8230;) Siguiente \u2192 Datos de contacto \u2190 Cambiar tartas \ud83d\udc64 Datos de contacto Nombre Tel\u00e9fono Correo electr\u00f3nico [&hellip;]\" \/>\n<meta property=\"og:url\" content=\"https:\/\/creamland.es\/index.php\/reservar\/\" \/>\n<meta property=\"og:site_name\" content=\"Creamland\" \/>\n<meta property=\"article:modified_time\" content=\"2026-07-15T13:57:51+00:00\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/creamland.es\\\/index.php\\\/reservar\\\/\",\"url\":\"https:\\\/\\\/creamland.es\\\/index.php\\\/reservar\\\/\",\"name\":\"Reserva - Creamland\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/creamland.es\\\/#website\"},\"datePublished\":\"2026-06-15T16:56:38+00:00\",\"dateModified\":\"2026-07-15T13:57:51+00:00\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/creamland.es\\\/index.php\\\/reservar\\\/#breadcrumb\"},\"inLanguage\":\"es\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/creamland.es\\\/index.php\\\/reservar\\\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/creamland.es\\\/index.php\\\/reservar\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Portada\",\"item\":\"https:\\\/\\\/creamland.es\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Reserva\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/creamland.es\\\/#website\",\"url\":\"https:\\\/\\\/creamland.es\\\/\",\"name\":\"Creamland\",\"description\":\"La mejor tarta de queso\",\"publisher\":{\"@id\":\"https:\\\/\\\/creamland.es\\\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/creamland.es\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"es\"},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/creamland.es\\\/#organization\",\"name\":\"Creamland\",\"url\":\"https:\\\/\\\/creamland.es\\\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"es\",\"@id\":\"https:\\\/\\\/creamland.es\\\/#\\\/schema\\\/logo\\\/image\\\/\",\"url\":\"https:\\\/\\\/creamland.es\\\/wp-content\\\/uploads\\\/2026\\\/05\\\/Logo-Creamland-Crema.webp\",\"contentUrl\":\"https:\\\/\\\/creamland.es\\\/wp-content\\\/uploads\\\/2026\\\/05\\\/Logo-Creamland-Crema.webp\",\"width\":1021,\"height\":372,\"caption\":\"Creamland\"},\"image\":{\"@id\":\"https:\\\/\\\/creamland.es\\\/#\\\/schema\\\/logo\\\/image\\\/\"},\"sameAs\":[\"https:\\\/\\\/www.instagram.com\\\/creamland_es\\\/\",\"https:\\\/\\\/www.tiktok.com\\\/@creamland_es\"]}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Reserva - Creamland","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/creamland.es\/index.php\/reservar\/","og_locale":"es_ES","og_type":"article","og_title":"Reserva - Creamland","og_description":"Realizar una reserva &#8211; Creamland Realizar una reserva 1. Fecha 2. Sabores 3. Datos Reserva: \ud83d\udcc5 Fecha de recogida O selecciona una fecha m\u00e1s lejana&#8230; \u2190 Cambiar fecha \ud83c\udf82 Elige tus tartas Borrar filtro Notas del pedido (alergias, dedicatoria&#8230;) Siguiente \u2192 Datos de contacto \u2190 Cambiar tartas \ud83d\udc64 Datos de contacto Nombre Tel\u00e9fono Correo electr\u00f3nico [&hellip;]","og_url":"https:\/\/creamland.es\/index.php\/reservar\/","og_site_name":"Creamland","article_modified_time":"2026-07-15T13:57:51+00:00","twitter_card":"summary_large_image","schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/creamland.es\/index.php\/reservar\/","url":"https:\/\/creamland.es\/index.php\/reservar\/","name":"Reserva - Creamland","isPartOf":{"@id":"https:\/\/creamland.es\/#website"},"datePublished":"2026-06-15T16:56:38+00:00","dateModified":"2026-07-15T13:57:51+00:00","breadcrumb":{"@id":"https:\/\/creamland.es\/index.php\/reservar\/#breadcrumb"},"inLanguage":"es","potentialAction":[{"@type":"ReadAction","target":["https:\/\/creamland.es\/index.php\/reservar\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/creamland.es\/index.php\/reservar\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Portada","item":"https:\/\/creamland.es\/"},{"@type":"ListItem","position":2,"name":"Reserva"}]},{"@type":"WebSite","@id":"https:\/\/creamland.es\/#website","url":"https:\/\/creamland.es\/","name":"Creamland","description":"La mejor tarta de queso","publisher":{"@id":"https:\/\/creamland.es\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/creamland.es\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"es"},{"@type":"Organization","@id":"https:\/\/creamland.es\/#organization","name":"Creamland","url":"https:\/\/creamland.es\/","logo":{"@type":"ImageObject","inLanguage":"es","@id":"https:\/\/creamland.es\/#\/schema\/logo\/image\/","url":"https:\/\/creamland.es\/wp-content\/uploads\/2026\/05\/Logo-Creamland-Crema.webp","contentUrl":"https:\/\/creamland.es\/wp-content\/uploads\/2026\/05\/Logo-Creamland-Crema.webp","width":1021,"height":372,"caption":"Creamland"},"image":{"@id":"https:\/\/creamland.es\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/www.instagram.com\/creamland_es\/","https:\/\/www.tiktok.com\/@creamland_es"]}]}},"_links":{"self":[{"href":"https:\/\/creamland.es\/index.php\/wp-json\/wp\/v2\/pages\/791","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/creamland.es\/index.php\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/creamland.es\/index.php\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/creamland.es\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/creamland.es\/index.php\/wp-json\/wp\/v2\/comments?post=791"}],"version-history":[{"count":71,"href":"https:\/\/creamland.es\/index.php\/wp-json\/wp\/v2\/pages\/791\/revisions"}],"predecessor-version":[{"id":918,"href":"https:\/\/creamland.es\/index.php\/wp-json\/wp\/v2\/pages\/791\/revisions\/918"}],"wp:attachment":[{"href":"https:\/\/creamland.es\/index.php\/wp-json\/wp\/v2\/media?parent=791"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}