{"id":3088,"date":"2025-11-03T20:34:36","date_gmt":"2025-11-03T19:34:36","guid":{"rendered":"https:\/\/mvips.net\/?page_id=3088"},"modified":"2025-11-03T20:42:17","modified_gmt":"2025-11-03T19:42:17","slug":"en-bukas","status":"publish","type":"page","link":"https:\/\/mvips.net\/en\/en-bukas\/","title":{"rendered":"Joinas Booking en"},"content":{"rendered":"<!doctype html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n<title>Joinas \u2014 Booking (all in one, with fares + auto)<\/title>\n<!-- PWA optional (not required for the form) -->\n<link rel=\"manifest\" href=\"\/wp-content\/uploads\/bukas\/manifest.json\">\n<meta name=\"theme-color\" content=\"#6b5f3b\">\n<meta name=\"apple-mobile-web-app-capable\" content=\"yes\">\n<style>\n\n\/* Comments: textarea a bit lower *\/\n#comentarios textarea{\n  min-height: 80px;\n}\n\n\n\/* Hide only when element has [hidden] *\/\n#s1Price[hidden],\n#s2Price[hidden],\n#s3Price[hidden],\n#pagoTotal[hidden] { display:none !important; }\n\n\n:root{\n  --brand:#6b5f3b; --brand-2:#b19b61; --ink:#222; --muted:#555; --bg:#fcfcfa; --card:#fefdf9;\n  --border:#dcd7c9; --border-2:#e6e0d2; --focus-ring:rgba(177,155,97,.22);\n  --warn-bg:#fff9e6; --warn-border:#f2d174; --radius:12px; --radius-sm:8px;\n  --gap-x:12px; --gap-y:10px; --control-h:42px; --font:\"Segoe UI\",system-ui,Arial,sans-serif;\n}\n*{box-sizing:border-box}\nbody{background:#fff;color:var(--ink);font-family:var(--font);}\n#bukasForm{ max-width:900px; margin:30px auto; color:var(--ink); }\n.form-head{ display:flex; align-items:center; justify-content:space-between; gap:12px; margin:20px auto; max-width:900px; }\n.form-head h2{ margin:0; color:var(--brand); font-weight:800; letter-spacing:.5px; }\n.btn-gold{ appearance:none; border:0; border-radius:12px; cursor:pointer; padding:10px 16px; font:700 14px\/1 var(--font); color:#fff; background: linear-gradient(180deg, #c8b782 0%, var(--brand-2) 100%); box-shadow:0 6px 18px rgba(0,0,0,.16); transition:transform .06s ease, filter .18s ease, box-shadow .18s ease; white-space:nowrap; }\n.btn-gold:hover{ filter:brightness(1.03); box-shadow:0 8px 22px rgba(0,0,0,.18); }\n.btn-gold:active{ transform:translateY(1px); box-shadow:0 4px 14px rgba(0,0,0,.15); }\n#bukasForm fieldset{ border:1px solid var(--border); padding:6px 16px 22px; border-radius:var(--radius); margin:18px 0; background:var(--bg); box-shadow:0 1px 3px rgba(0,0,0,.04); }\n#bukasForm legend{ font-weight:700; color:var(--brand); padding:0 6px; font-size:1rem; }\n#bukasForm label{ display:grid; grid-template-rows:2.4rem auto; align-content:start; margin:.35rem 0; font-size:.95rem; }\n#bukasForm small.hint{color:var(--muted);font-size:.85rem;margin-top:.25rem}\n#bukasForm input[type=\"text\"],\n#bukasForm input[type=\"email\"],\n#bukasForm input[type=\"number\"],\n#bukasForm input[type=\"date\"],\n#bukasForm input[type=\"time\"],\n#bukasForm select,\n#bukasForm textarea{ width:100%; height:var(--control-h); padding:8px 10px; font-size:15px; border:1px solid #cfd3d7; border-radius:6px; background:#fff; transition:border-color .18s, box-shadow .18s, background-color .18s; }\n#bukasForm textarea{min-height:110px;height:auto;resize:vertical;}\n#bukasForm input:focus,#bukasForm select:focus,#bukasForm textarea:focus{ outline:none;border-color:var(--brand-2);box-shadow:0 0 0 3px var(--focus-ring); }\n#bukasForm input[readonly]{background:#fafafa;color:#555}\n.fs-grid{ display:grid; grid-template-columns:repeat(12,minmax(0,1fr)); gap:var(--gap-y) var(--gap-x); }\n.span-12{grid-column:span 12}\n@media (min-width:900px){ .span-2{grid-column:span 2} .span-3{grid-column:span 3} .span-4{grid-column:span 4} .span-5{grid-column:span 5} .span-6{grid-column:span 6} .span-7{grid-column:span 7} .span-8{grid-column:span 8} .span-9{grid-column:span 9} .span-10{grid-column:span 10} .span-11{grid-column:span 11} .span-12{grid-column:span 12} }\n@media (max-width:899.98px){ .fs-grid>*{grid-column:1 \/ -1 !important} }\n.extras{ display:grid; grid-template-columns:repeat(auto-fit,minmax(220px,1fr)); gap:var(--gap-y) var(--gap-x); padding:4px 0; }\n.extras label{ display:flex;align-items:center;gap:.65rem; min-height:56px;padding:10px 14px;border-radius:var(--radius); background:#fbfaf7;border:1px solid var(--border-2); transition:background .18s, box-shadow .18s, border-color .18s; }\n.extras input[type=\"checkbox\"]{width:22px;height:22px;flex:0 0 auto;accent-color:var(--brand);}\n.extras label:hover{background:#f7f4eb}\n.aviso-otros{ margin:.35rem 0 0;padding:.5rem .6rem;border:1px solid var(--warn-border); background:var(--warn-bg);border-radius:var(--radius-sm);font-size:.92rem; color:var(--brand); }\n.stepper {display:flex; gap:8px; align-items:center; margin:14px 0 18px; flex-wrap:wrap;}\n@media (min-width:860px){ .stepper{flex-wrap:nowrap;} }\n.stepper__item{ flex:1; display:flex; align-items:center; gap:10px; padding:10px 12px; border-radius:12px; border:1px solid var(--border); background:#fff; color:#6b6b6b; font-weight:600; font-size:.9rem; }\n.stepper__item[data-state=\"done\"]{ background:#e8f7e8; border-color:#b4e2b4; color:#116611; }\n.stepper__item[data-state=\"active\"]{ background:linear-gradient(180deg, #c8b782 0%, var(--brand-2) 100%); color:#fff; border-color:transparent; box-shadow:0 3px 12px rgba(0,0,0,.12); }\n.stepper__num{ width:24px; height:24px; border-radius:999px; display:inline-grid; place-items:center; font-weight:800; border:1px solid #d8d8d8; background:#fff; color:#333; font-size:.9rem; }\n.stepper__item[data-state=\"active\"] .stepper__num{ background:#fff; color:#6b5f3b; border-color:transparent; }\n.form-block[hidden]{ display:none !important; }\n.form-block[data-active=\"1\"]{ display:block; }\n.block-actions{ display:flex; justify-content:space-between; gap:12px; margin-top:10px; }\n.block-actions .btn-prev{ appearance:none; border:1px solid var(--border); background:#fff; color:#333; border-radius:12px; padding:10px 16px; font-weight:700; cursor:pointer; }\n.block-actions .btn-next{ appearance:none; border:0; border-radius:12px; padding:12px 24px; font-weight:800; cursor:pointer; background: linear-gradient(180deg, #c8b782 0%, var(--brand-2) 100%); color:#fff; box-shadow:0 6px 18px rgba(0,0,0,.16); }\n.block-actions .btn-next[disabled]{ opacity:.6; cursor:not-allowed; box-shadow:none; filter:grayscale(.2); }\n.precio-chip{ display:inline-flex; align-items:center; gap:.5rem; padding:.5rem .75rem; border:1px solid #e5e7eb; border-radius:999px; font-weight:600; font-size:14px; background:#f8fafc; color:#111827; }\n.precio-chip small{ font-weight:400; color:#6b7280 }\n.pay-info[hidden]{ display:none !important; }\n.bank-box{ background:#fbfaf7; border:1px solid var(--border-2); border-radius:var(--radius); padding:12px 14px; line-height:1.35; font-size:.95rem; }\n.toast-card{background:#fff;border:1px solid var(--border);border-radius:16px;padding:18px 20px;box-shadow:0 10px 40px rgba(0,0,0,.18);max-width:520px;width:calc(100% - 40px);text-align:center}\n.toast-card h3{margin:0 0 .35rem;color:var(--brand);font-weight:800}\n.toast-card p{margin:.25rem 0 .9rem;color:var(--muted)}\n.toast-actions{display:flex;gap:10px;justify-content:center}\n\/* Chips\/segmented with icon *\/\n.chips { display:flex; flex-wrap:wrap; gap:8px; margin-top:6px; }\n.chips .chip{ appearance:none; border:1px solid var(--border); border-radius:9999px; padding:10px 14px; font:600 14px\/1.2 var(--font); color:#2b2b2b; background:#fff; cursor:pointer; display:inline-flex; gap:8px; align-items:center; transition:transform .05s, box-shadow .18s, background .18s, color .18s, border-color .18s; }\n.chips .chip:hover{ background:#f7f4eb; }\n.chips .chip:active{ transform:translateY(1px); }\n.chips .chip.is-active{ background: linear-gradient(180deg, #c8b782 0%, var(--brand-2) 100%); color:#fff; border-color: transparent; box-shadow:0 3px 12px rgba(0,0,0,.10); }\n.chip .ico{ width:20px; height:20px; display:inline-grid; place-items:center; filter:grayscale(1) brightness(0); opacity:.9; }\n.chips .chip.is-active .ico{ filter:none; opacity:1; }\n\/* === Override: chips uniform width === *\/\n.chips{\n  display:grid;\n  grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));\n  gap:8px;\n}\n.chips .chip{\n  width:100%;\n  justify-content:center;\n}\n.hint-inline{ font-size:.86rem; color:#6b6b6b; margin-top:4px; }\n.badge{display:inline-block;padding:4px 8px;border-radius:9999px;font-weight:600;font-size:.8rem}\n.badge-ok{background:#e8f7e8;color:#116611;border:1px solid #b4e2b4}\n.badge-warn{background:#fff3cd;color:#7a5d00;border:1px solid #ffeeba}\n.badge-err{background:#ffe4e6;color:#7f1d1d;border:1px solid #fecdd3}\n.visually-hidden-select{ position:absolute !important;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden; clip:rect(0 0 0 0);white-space:nowrap;border:0; }\n.yn-toggle{ display:inline-flex;align-items:center;gap:6px;padding:3px;border-radius:9999px; border:1px solid #e0dccb;background:#fff;width:fit-content;max-width:100%;justify-self:start; box-shadow:0 1px 0 rgba(0,0,0,.03); }\n.yn-toggle button{ appearance:none;border:0;border-radius:9999px;padding:8px 14px;min-width:72px;line-height:1; font:600 14px\/1 var(--font);background:transparent;color:#3b3b3b;cursor:pointer; transition:background .18s, color .18s, box-shadow .18s, transform .02s; }\n.yn-toggle button:hover{background:#f7f4eb}\n.yn-toggle button.is-active{ background:var(--brand);color:#fff;box-shadow:0 1px 0 rgba(0,0,0,.12),0 0 0 2px rgba(177,155,97,.25); }\n<\/style>\n<style>\n  \/* Hide floating button and modal ONLY on this page *\/\n  #floating-button,\n  #custom-modal { display: none !important; }\n<\/style>\n\n<style id=\"pago-css\">\n\/* More space between Comments and Payment *\/\n#comentarios {\n  margin-bottom: 36px !important;\n}\n#comentarios + #pago {\n  margin-top: 32px !important;\n}\n\n\/* Payment header: each line in place, left aligned *\/\n#pago .pago-top{\n  margin:4px 0;\n}\n\n#pago .pago-top{\n  grid-column: 1 \/ -1;\n}\n\n#pago .pago-top label{\n  display:block !important;\n  margin:0;\n  padding:0;\n}\n\n#pago .pago-top label > span{\n  display:inline-block;\n  margin-right:8px;\n  white-space:normal;\n}\n\n#pago .precio-chip{\n  display:inline-flex;\n  align-items:center;\n  justify-content:center;\n  white-space:nowrap;\n}\n\n\/* Payment method buttons *\/\n.pago-buttons{\n  display:flex;\n  flex-wrap:wrap;\n  gap:.5rem;\n  align-items:center;\n  margin:.5rem 0 0;\n}\n.pago-btn{\n  display:inline-flex !important;\n  align-items:center;\n  justify-content:center;\n  flex:0 0 auto;\n  width:auto !important;\n  height:auto !important;\n  padding:.45rem .9rem;\n  line-height:1.1;\n  font:inherit;\n  cursor:pointer;\n  border:1px solid #c9c6b8;\n  border-radius:999px;\n  background:#f6f3e8;\n  box-shadow:none;\n}\n.pago-btn:focus{\n  outline:2px solid #b7ae8c;\n  outline-offset:2px;\n}\n.pago-btn.active,\n.pago-btn[aria-checked=\"true\"]{\n  background:#e0d9bf;\n  border-color:#b7ae8c;\n}\n\n\/* Bank box *\/\n.bank-box{\n  margin-top:.6rem;\n  border:1px solid #e2e2e2;\n  padding:.6rem .8rem;\n  border-radius:.5rem;\n  background:#faf9f6;\n}\n.muted{\n  color:#6b6b6b;\n  font-size:.85em;\n  margin-top:.25rem;\n}\n\/* Comments: shorter textarea *\/\n#comentarios textarea {\n  min-height: 80px !important;\n  height: auto;\n}\n\n<\/style>\n\n<style id=\"ux-css\">\n\/* Visible focus *\/\n:where(button, [type=\"button\"], [type=\"submit\"], select, input, textarea):focus-visible{\noutline: 2px solid var(--brand-2);\noutline-offset: 2px;\nbox-shadow: 0 0 0 3px var(--focus-ring);\n}\n\n\n\/* Inline errors *\/\n.field-error{ color:#7f1d1d; font-size:.85rem; margin-top:.35rem }\n.is-invalid{ border-color:#ef4444 !important; box-shadow:0 0 0 3px rgba(239,68,68,.15) !important; }\n\n\n\/* Sticky stepper on tall screens *\/\n@media (min-width: 900px){\n.stepper{ position: sticky; top: 0; z-index: 5; background: #fff; padding-top: 8px; }\n}\n\n\n\/* Submit button consistent *\/\nbutton[type=\"submit\"]{\nappearance:none; border:0; border-radius:12px; padding:12px 24px; font-weight:800; cursor:pointer;\nbackground: linear-gradient(180deg, #c8b782 0%, var(--brand-2) 100%); color:#fff; box-shadow:0 6px 18px rgba(0,0,0,.16);\n}\nbutton[type=\"submit\"][disabled]{ opacity:.6; cursor:not-allowed; box-shadow:none; filter:grayscale(.2) }\n<\/style>\n\n<style id=\"stepper-compact\">\n  \/* Narrower stepper buttons with centered text *\/\n  .stepper { gap: 6px; }\n\n  .stepper__item{\n    flex: 1 1 0 !important;\n    min-width: 0;\n    justify-content: center;\n    text-align: center;\n    white-space: nowrap;\n  }\n<\/style>\n\n<style>\n  \/* Hide any chips container *\/\n  .chips{ display:none !important; }\n<\/style>\n\n<style>\n  \/* Make output look like a normal input *\/\n  #s1Price,\n  #s2Price,\n  #s3Price{\n    display:flex;\n    align-items:center;\n    justify-content:flex-end;\n    height: var(--control-h);\n    padding: 8px 10px;\n    border: 1px solid #cfd3d7;\n    border-radius: 6px;\n    background:#fff;\n    font-size: 15px;\n    font-weight: 600;\n    color: var(--ink);\n    box-shadow: none;\n  }\n\n  #s1Price [data-c=\"EUR\"],\n  #s2Price [data-c=\"EUR\"],\n  #s3Price [data-c=\"EUR\"]{\n    margin-left:auto;\n  }\n\n  #s1Price[hidden],\n  #s2Price[hidden],\n  #s3Price[hidden]{ display:none !important; }\n\n  #s1Price, #s2Price, #s3Price { border-radius:6px !important; background:#fff !important; }\n<\/style>\n\n<style id=\"mobile-stepper-fix\">\n\/* ===== Mobile improvements: stepper + inputs ===== *\/\n@media (max-width: 480px){\n\n  \/* Stepper in a single row with smooth scroll *\/\n  .stepper{\n    overflow-x: auto;\n    -webkit-overflow-scrolling: touch;\n    gap: 6px;\n    padding-bottom: 6px;\n    scroll-snap-type: x proximity;\n  }\n  .stepper__item{\n    flex: 0 0 auto;\n    min-width: 118px;\n    padding: 8px 10px;\n    font-size: .9rem;\n    white-space: nowrap;\n    scroll-snap-align: start;\n  }\n  .stepper__item[data-state=\"active\"]{\n    box-shadow: 0 2px 10px rgba(0,0,0,.12);\n  }\n  \/* Hide circle numbers to save space *\/\n  .stepper__num{ display:none; }\n\n  \/* Yes\/No buttons: touch-friendly but compact *\/\n  .yn-toggle{ gap:4px; }\n  .yn-toggle button{\n    min-width: 64px;\n    padding: 10px 12px;\n    font-size: 14px;\n  }\n\n  \/* Inputs at 16px to avoid zoom on iOS\/Android *\/\n  #bukasForm input[type=\"text\"],\n  #bukasForm input[type=\"email\"],\n  #bukasForm input[type=\"number\"],\n  #bukasForm input[type=\"date\"],\n  #bukasForm input[type=\"time\"],\n  #bukasForm select,\n  #bukasForm textarea{\n    font-size: 16px;\n  }\n\n  \/* Slight padding adjustments *\/\n  #bukasForm fieldset{ padding:14px 12px 18px; }\n  .block-actions{ gap:8px; }\n}\n\n\/* iOS: enforce 16px font-size on inputs (avoid zoom on focus) *\/\n@supports (-webkit-touch-callout: none){\n  #bukasForm input,\n  #bukasForm select,\n  #bukasForm textarea{ font-size: 16px; }\n}\n<\/style>\n\n<\/head>\n<body>\n<div class=\"form-head\">\n  <h2>Joinas &#8211; Booking<\/h2>\n  <button id=\"btnInstall\" class=\"btn-gold\" hidden>Download our app<\/button>\n<\/div>\n<form id=\"bukasForm\" method=\"post\" novalidate action=\"\">\n  <!-- Stepper -->\n  <nav class=\"stepper\" aria-label=\"Booking progress\">\n    <div class=\"stepper__item\" data-step=\"1\" data-state=\"active\">Client<\/div>\n    <div class=\"stepper__item\" data-step=\"2\" data-state=\"idle\">Service 1<\/div>\n    <div class=\"stepper__item\" data-step=\"3\" data-state=\"idle\">Service 2<\/div>\n    <div class=\"stepper__item\" data-step=\"4\" data-state=\"idle\">Service 3<\/div>\n    <div class=\"stepper__item\" data-step=\"5\" data-state=\"idle\">Final<\/div>\n  <\/nav>\n  <!-- BLOCK 1: Client -->\n  <section class=\"form-block\" data-block=\"1\" data-active=\"1\">\n    <fieldset class=\"fs-grid\" id=\"bloqueCliente\">\n      <legend>Client details<\/legend>\n      <label class=\"span-4\">Client no. (optional)<br>\n        <input type=\"text\" name=\"N\u00famero de cliente\" id=\"cliNum\" autocomplete=\"off\" placeholder=\"e.g. 120045\">\n      <\/label>\n      <label class=\"span-8\">Tax ID \/ VAT \/ Passport*<br>\n        <input required type=\"text\" name=\"N\u00famero de Identificaci\u00f3n Fiscal (CIF, VAT o Pasaporte)\" id=\"cliTax\" autocomplete=\"off\" placeholder=\"ESB12345678 \/ GB123456789 \/ Passport\">\n      <\/label>\n      <div class=\"span-12\" id=\"clienteStatus\" aria-live=\"polite\"><\/div>\n      <label class=\"span-6\">Client name*<br>\n        <input required type=\"text\" name=\"Nombre del cliente\" id=\"cliName\" autocomplete=\"name\">\n      <\/label>\n      <label class=\"span-6\">Phone*<br>\n        <input required type=\"text\" name=\"Tel\u00e9fono\" id=\"cliPhone\" inputmode=\"tel\" autocomplete=\"tel\">\n      <\/label>\n      <label class=\"span-12\">Billing address*<br>\n        <input required type=\"text\" name=\"Domicilio de Facturaci\u00f3n\" id=\"cliBilling\" autocomplete=\"street-address\">\n      <\/label>\n      <label class=\"span-12\">Email address*<br>\n        <input required type=\"email\" name=\"Correo electr\u00f3nico\" id=\"cliEmail\" placeholder=\"email@example.com\" autocomplete=\"email\">\n        <small class=\"hint\">For this booking you can use a different email than the one on file.<\/small>\n      <\/label>\n    <\/fieldset>\n    <div class=\"block-actions\">\n      <button type=\"button\" class=\"btn-prev\" data-prev disabled>Previous<\/button>\n      <button type=\"button\" class=\"btn-next\" data-next>Next<\/button>\n    <\/div>\n  <\/section>\n\n <!-- BLOCK 2: Service 1 (split in 2 fieldsets) -->\n<section class=\"form-block\" data-block=\"2\" hidden>\n  <!-- Block A: core fields -->\n  <fieldset class=\"fs-grid\" id=\"s1-core\">\n    <legend>Service 1<\/legend>\n    <!-- Location -->\n    <label class=\"span-4\">City*<br>\n      <select id=\"s1Lugar\" name=\"Lugar\" required>\n        <option>Barcelona<\/option>\n        <option>Madrid<\/option>\n      <\/select>\n    <\/label>\n    <!-- Vehicle -->\n    <label class=\"span-4\">Vehicle*<br>\n      <select id=\"s1Vehiculo\" name=\"Veh\u00edculo\" required>\n        <option>Car<\/option>\n        <option>Van<\/option>\n        <option>Minibus 15<\/option>\n        <option>Minibus 25<\/option>\n        <option>Coach<\/option>\n      <\/select>\n      <div class=\"hint-inline\" id=\"s1PaxHint\" aria-live=\"polite\"><\/div>\n    <\/label>\n    <!-- Service type -->\n    <label class=\"span-4\">Service type*<br>\n      <select id=\"s1Servicio\" name=\"Servicio\" required>\n        <option value=\"\"><\/option>\n      <\/select>\n    <\/label>\n  <\/fieldset>\n  <!-- Block B: rest of fields -->\n\n<fieldset class=\"fs-grid\" id=\"s1\">\n  <legend>Service 1 details<\/legend>\n\n  <!-- Row 1 -->\n  <label class=\"span-6\">Pickup date*<br>\n    <input required type=\"date\" id=\"s1Date\" name=\"Fecha de recogida\">\n  <\/label>\n  <label class=\"span-6\">Pickup time*<br>\n    <input required type=\"time\" id=\"s1Time\" name=\"Hora de recogida\">\n  <\/label>\n\n  <!-- Row 2 -->\n  <label class=\"span-6\">Number of passengers*<br>\n    <input required type=\"number\" min=\"1\" step=\"1\" id=\"s1Pax\" name=\"Cantidad de pasajeros?\" inputmode=\"numeric\" placeholder=\"Max \u2014\">\n  <\/label>\n  <label class=\"span-6\">Flight\/Train no.<br>\n    <input type=\"text\" id=\"s1Flight\" name=\"Introduzca el n\u00famero de vuelo (si corresponde)\" placeholder=\"e.g. IB1234 \/ AVE 03192\">\n  <\/label>\n\n  <!-- Row 3 -->\n  <label class=\"span-6\">Pickup point*<br>\n    <input required type=\"text\" id=\"s1Pickup\" name=\"Punto de recogida\" list=\"s1PickupOptions\" placeholder=\"Hotel, street, airport\u2026\">\n    <datalist id=\"s1PickupOptions\"><\/datalist>\n  <\/label>\n  <label class=\"span-6\">Exact pickup address<br>\n    <input type=\"text\" id=\"s1PickupExact\" name=\"Direcci\u00f3n exacta de recogida\" placeholder=\"(auto if applicable)\">\n  <\/label>\n\n  <!-- Row 4 -->\n  <label class=\"span-6\">Drop-off point*<br>\n    <input required type=\"text\" id=\"s1Dropoff\" name=\"Punto de destino\" list=\"s1DropoffOptions\" placeholder=\"Hotel, street, event\u2026\">\n    <datalist id=\"s1DropoffOptions\"><\/datalist>\n  <\/label>\n  <label class=\"span-6\">Exact drop-off address<br>\n    <input type=\"text\" id=\"s1DropoffExact\" name=\"Direcci\u00f3n exacta de destino\" placeholder=\"(auto if applicable)\">\n  <\/label>\n\n  <!-- Row 5 -->\n  <label class=\"span-6\">Distance (km)<br>\n    <input type=\"text\" id=\"s1Distance\" name=\"Distancia (km)\" readonly placeholder=\"\u2014\">\n  <\/label>\n\n\n  <label class=\"span-6\">Price (VAT included)<br>\n  <output id=\"s1Price\" hidden aria-live=\"polite\">\n    <span data-c=\"EUR\">\u2014<\/span>\n  <\/output>\n<\/label>\n\n\n\n  <!-- Extras -->\n  <div class=\"span-12\">\n    <div class=\"extras\" data-for=\"1\" role=\"group\" aria-label=\"Extras for Service 1\">\n      <label><input type=\"checkbox\" value=\"Meet &#038; Greet at airport\"> Meet &amp; Greet at airport<\/label>\n      <label><input type=\"checkbox\" value=\"Name sign\"> Name sign<\/label>\n      <label><input type=\"checkbox\" value=\"Child seat\"> Child seat<\/label>\n      <label><input type=\"checkbox\" value=\"Luxury vehicle\"> Luxury vehicle<\/label>\n      <label><input type=\"checkbox\" value=\"Additional waiting time\"> Additional waiting time<\/label>\n      <label><input type=\"checkbox\" value=\"Intermediate stops\"> Intermediate stops<\/label>\n    <\/div>\n  <\/div>\n\n  <label class=\"span-12\">Other (please specify)<br>\n    <input type=\"text\" id=\"s1Otro\" name=\"Otro (especificar)\" placeholder=\"Describe any additional needs\">\n  <\/label>\n\n  <label class=\"span-6\">Add a contact person in addition to the client?<br>\n    <select id=\"s1AddContact\" name=\"Quiere agregar una persona de contacto adem\u00e1s del cliente?\">\n      <option>No<\/option><option>Yes<\/option>\n    <\/select>\n  <\/label>\n\n  <div id=\"c1\" class=\"span-12\" hidden>\n    <div class=\"fs-grid\">\n      <label class=\"span-4\">Contact name*<br><input type=\"text\" id=\"s1ContactName\" name=\"Nombre del contacto\"><\/label>\n      <label class=\"span-4\">Contact phone*<br><input type=\"text\" id=\"s1ContactPhone\" name=\"Tel\u00e9fono de contacto\" inputmode=\"tel\"><\/label>\n      <label class=\"span-4\">Contact email*<br><input type=\"email\" id=\"s1ContactEmail\" name=\"Email de contacto\" placeholder=\"email@example.com\"><\/label>\n    <\/div>\n  <\/div>\n<\/fieldset>\n\n\n\n\n  <!-- Activate Service 2 -->\n  <fieldset class=\"fs-grid\">\n    <label class=\"span-6\">Add another service?<br>\n      <select id=\"t2\" name=\"Desea agregar otro servicio al servicio Nro1?\" aria-controls=\"serv2\" aria-expanded=\"false\">\n        <option selected>No<\/option><option>Yes<\/option>\n      <\/select>\n    <\/label>\n    <small class=\"hint span-12\">If you choose \u201cYes\u201d, the Service 2 form will open in the next step.<\/small>\n  <\/fieldset>\n  <div class=\"block-actions\">\n    <button type=\"button\" class=\"btn-prev\" data-prev>Previous<\/button>\n    <button type=\"button\" class=\"btn-next\" data-next>Next<\/button>\n  <\/div>\n<\/section>\n\n  <!-- BLOCK 3: Service 2 -->\n  <section class=\"form-block\" data-block=\"3\" hidden>\n\n<fieldset class=\"fs-grid\" id=\"serv2\">\n  <legend>Service 2<\/legend>\n\n  <label class=\"span-4\">City*<br>\n    <select id=\"s2Lugar\" name=\"Lugar S2\" required>\n      <option>Barcelona<\/option><option>Madrid<\/option>\n    <\/select>\n    <div class=\"chips\" data-chips-for=\"s2Lugar\"><\/div>\n  <\/label>\n\n\n  <label class=\"span-4\">Vehicle*<br>\n    <select id=\"s2Vehiculo\" name=\"Veh\u00edculo S2\" required>\n      <option>Car<\/option><option>Van<\/option>\n      <option>Minibus 15<\/option><option>Minibus 25<\/option><option>Coach<\/option>\n    <\/select>\n    <div class=\"chips\" data-chips-for=\"s2Vehiculo\"><\/div>\n    <div class=\"hint-inline\" id=\"s2PaxHint\" aria-live=\"polite\"><\/div>\n  <\/label>\n\n  <label class=\"span-4\">Service type*<br>\n    <select id=\"s2Servicio\" name=\"Servicio S2\" required>\n      <option value=\"\"><\/option>\n    <\/select>\n    <div class=\"chips\" data-chips-for=\"s2Servicio\"><\/div>\n  <\/label>\n<\/fieldset>\n\n<fieldset class=\"fs-grid\" id=\"s2\">\n  <legend>Service 2 details<\/legend>\n\n  <label class=\"span-6\">Pickup date*<br>\n    <input required type=\"date\" id=\"s2Date\" name=\"2_Fecha de recogida\">\n  <\/label>\n  <label class=\"span-6\">Pickup time*<br>\n    <input required type=\"time\" id=\"s2Time\" name=\"2_Hora de recogida\">\n  <\/label>\n\n  <label class=\"span-6\">Number of passengers*<br>\n    <input required type=\"number\" min=\"1\" step=\"1\" id=\"s2Pax\" name=\"2_Cantidad de pasajeros?\" inputmode=\"numeric\" placeholder=\"Max \u2014\">\n  <\/label>\n  <label class=\"span-6\">Flight\/Train no.<br>\n    <input type=\"text\" id=\"s2Flight\" name=\"2_Introduzca el n\u00famero de vuelo (si corresponde)\" placeholder=\"e.g. IB1234 \/ AVE 03192\">\n  <\/label>\n\n  <label class=\"span-6\">Pickup point*<br>\n    <input required type=\"text\" id=\"s2Pickup\" name=\"2_Punto de recogida\" list=\"s2PickupOptions\" placeholder=\"Hotel, street, airport\u2026\">\n    <datalist id=\"s2PickupOptions\"><\/datalist>\n  <\/label>\n  <label class=\"span-6\">Exact pickup address<br>\n    <input type=\"text\" id=\"s2PickupExact\" name=\"2_Direcci\u00f3n exacta de recogida\" placeholder=\"(auto if applicable)\">\n  <\/label>\n\n  <label class=\"span-6\">Drop-off point*<br>\n    <input required type=\"text\" id=\"s2Dropoff\" name=\"2_Punto de destino\" list=\"s2DropoffOptions\" placeholder=\"Hotel, street, event\u2026\">\n    <datalist id=\"s2DropoffOptions\"><\/datalist>\n  <\/label>\n  <label class=\"span-6\">Exact drop-off address<br>\n    <input type=\"text\" id=\"s2DropoffExact\" name=\"2_Direcci\u00f3n exacta de destino\" placeholder=\"(auto if applicable)\">\n  <\/label>\n\n  <label class=\"span-6\">Distance (km)<br>\n    <input type=\"text\" id=\"s2Distance\" name=\"2_Distancia (km)\" readonly placeholder=\"\u2014\">\n  <\/label>\n  <label class=\"span-6\">Price (VAT included)<br>\n  <output id=\"s2Price\" hidden aria-live=\"polite\">\n    <span data-c=\"EUR\">\u2014<\/span>\n  <\/output>\n<\/label>\n\n\n  <div class=\"span-12\">\n    <div class=\"extras\" data-for=\"2\" role=\"group\" aria-label=\"Extras for Service 2\">\n      <label><input type=\"checkbox\" value=\"Meet &#038; Greet at airport\"> Meet &amp; Greet at airport<\/label>\n      <label><input type=\"checkbox\" value=\"Name sign\"> Name sign<\/label>\n      <label><input type=\"checkbox\" value=\"Child seat\"> Child seat<\/label>\n      <label><input type=\"checkbox\" value=\"Luxury vehicle\"> Luxury vehicle<\/label>\n      <label><input type=\"checkbox\" value=\"Additional waiting time\"> Additional waiting time<\/label>\n      <label><input type=\"checkbox\" value=\"Intermediate stops\"> Intermediate stops<\/label>\n    <\/div>\n  <\/div>\n\n  <label class=\"span-12\">Other (please specify)<br>\n    <input type=\"text\" id=\"s2Otro\" name=\"2_Otro (especificar)\" placeholder=\"Describe any additional needs\">\n  <\/label>\n\n  <label class=\"span-6\">Add a contact person in addition to the client?<br>\n    <select id=\"s2AddContact\" name=\"2_Quiere agregar una persona de contacto adem\u00e1s del cliente?\">\n      <option>No<\/option><option>Yes<\/option>\n    <\/select>\n  <\/label>\n  <div id=\"c2\" class=\"span-12\" hidden>\n    <div class=\"fs-grid\">\n      <label class=\"span-4\">Contact name*<br><input type=\"text\" id=\"s2ContactName\" name=\"2_Nombre\"><\/label>\n      <label class=\"span-4\">Contact phone*<br><input type=\"text\" id=\"s2ContactPhone\" name=\"2_Tel\u00e9fono\" inputmode=\"tel\"><\/label>\n      <label class=\"span-4\">Contact email*<br><input type=\"email\" id=\"s2ContactEmail\" name=\"2_Email\" placeholder=\"email@example.com\"><\/label>\n    <\/div>\n  <\/div>\n<\/fieldset>\n\n\n  <!-- Toggle Service 3 -->\n  <fieldset class=\"fs-grid\">\n    <label class=\"span-6\">Add another service?<br>\n      <select id=\"t3\" name=\"Desea agregar otro servicio al servicio Nro2?\" aria-controls=\"serv3\" aria-expanded=\"false\">\n        <option selected>No<\/option><option>Yes<\/option>\n      <\/select>\n    <\/label>\n    <small class=\"hint span-12\">If you choose \u201cYes\u201d, the Service 3 form will open in the next step.<\/small>\n  <\/fieldset>\n\n  <div class=\"block-actions\">\n    <button type=\"button\" class=\"btn-prev\" data-prev>Previous<\/button>\n    <button type=\"button\" class=\"btn-next\" data-next>Next<\/button>\n  <\/div>\n<\/section>\n\n  <!-- BLOCK 4: Service 3 -->\n  <section class=\"form-block\" data-block=\"4\" hidden>\n\n<fieldset class=\"fs-grid\" id=\"serv3\">\n  <legend>Service 3<\/legend>\n\n  <label class=\"span-4\">City*<br>\n    <select id=\"s3Lugar\" name=\"Lugar S3\" required>\n      <option>Barcelona<\/option><option>Madrid<\/option>\n    <\/select>\n    <div class=\"chips\" data-chips-for=\"s3Lugar\"><\/div>\n  <\/label>\n\n  <label class=\"span-4\">Vehicle*<br>\n    <select id=\"s3Vehiculo\" name=\"Veh\u00edculo S3\" required>\n      <option>Car<\/option><option>Van<\/option>\n      <option>Minibus 15<\/option><option>Minibus 25<\/option><option>Coach<\/option>\n    <\/select>\n    <div class=\"chips\" data-chips-for=\"s3Vehiculo\"><\/div>\n    <div class=\"hint-inline\" id=\"s3PaxHint\" aria-live=\"polite\"><\/div>\n  <\/label>\n\n  <label class=\"span-4\">Service type*<br>\n    <select id=\"s3Servicio\" name=\"Servicio S3\" required>\n      <option value=\"\"><\/option>\n    <\/select>\n    <div class=\"chips\" data-chips-for=\"s3Servicio\"><\/div>\n  <\/label>\n<\/fieldset>\n\n<fieldset class=\"fs-grid\" id=\"s3\">\n  <legend>Service 3 details<\/legend>\n\n  <label class=\"span-6\">Pickup date*<br>\n    <input required type=\"date\" id=\"s3Date\" name=\"3_Fecha de recogida\">\n  <\/label>\n  <label class=\"span-6\">Pickup time*<br>\n    <input required type=\"time\" id=\"s3Time\" name=\"3_Hora de recogida\">\n  <\/label>\n\n  <label class=\"span-6\">Number of passengers*<br>\n    <input required type=\"number\" min=\"1\" step=\"1\" id=\"s3Pax\" name=\"3_Cantidad de pasajeros?\" inputmode=\"numeric\" placeholder=\"Max \u2014\">\n  <\/label>\n  <label class=\"span-6\">Flight\/Train no.<br>\n    <input type=\"text\" id=\"s3Flight\" name=\"3_Introduzca el n\u00famero de vuelo (si corresponde)\" placeholder=\"e.g. IB1234 \/ AVE 03192\">\n  <\/label>\n\n  <label class=\"span-6\">Pickup point*<br>\n    <input required type=\"text\" id=\"s3Pickup\" name=\"3_Punto de recogida\" list=\"s3PickupOptions\" placeholder=\"Hotel, street, airport\u2026\">\n    <datalist id=\"s3PickupOptions\"><\/datalist>\n  <\/label>\n  <label class=\"span-6\">Exact pickup address<br>\n    <input type=\"text\" id=\"s3PickupExact\" name=\"3_Direcci\u00f3n exacta de recogida\" placeholder=\"(auto if applicable)\">\n  <\/label>\n\n  <label class=\"span-6\">Drop-off point*<br>\n    <input required type=\"text\" id=\"s3Dropoff\" name=\"3_Punto de destino\" list=\"s3DropoffOptions\" placeholder=\"Hotel, street, event\u2026\">\n    <datalist id=\"s3DropoffOptions\"><\/datalist>\n  <\/label>\n  <label class=\"span-6\">Exact drop-off address<br>\n    <input type=\"text\" id=\"s3DropoffExact\" name=\"3_Direcci\u00f3n exacta de destino\" placeholder=\"(auto if applicable)\">\n  <\/label>\n\n  <label class=\"span-6\">Distance (km)<br>\n    <input type=\"text\" id=\"s3Distance\" name=\"3_Distancia (km)\" readonly placeholder=\"\u2014\">\n  <\/label>\n  <label class=\"span-6\">Price (VAT included)<br>\n  <output id=\"s3Price\" hidden aria-live=\"polite\">\n    <span data-c=\"EUR\">\u2014<\/span>\n  <\/output>\n<\/label>\n\n\n  <div class=\"span-12\">\n    <div class=\"extras\" data-for=\"3\" role=\"group\" aria-label=\"Extras for Service 3\">\n      <label><input type=\"checkbox\" value=\"Meet &#038; Greet at airport\"> Meet &amp; Greet at airport<\/label>\n      <label><input type=\"checkbox\" value=\"Name sign\"> Name sign<\/label>\n      <label><input type=\"checkbox\" value=\"Child seat\"> Child seat<\/label>\n      <label><input type=\"checkbox\" value=\"Luxury vehicle\"> Luxury vehicle<\/label>\n      <label><input type=\"checkbox\" value=\"Additional waiting time\"> Additional waiting time<\/label>\n      <label><input type=\"checkbox\" value=\"Intermediate stops\"> Intermediate stops<\/label>\n    <\/div>\n  <\/div>\n\n  <label class=\"span-12\">Other (please specify)<br>\n    <input type=\"text\" id=\"s3Otro\" name=\"3_Otro (especificar)\" placeholder=\"Describe any additional needs\">\n  <\/label>\n\n  <label class=\"span-6\">Add a contact person in addition to the client?<br>\n    <select id=\"s3AddContact\" name=\"3_Quiere agregar una persona de contacto adem\u00e1s del cliente?\">\n      <option>No<\/option><option>Yes<\/option>\n    <\/select>\n  <\/label>\n  <div id=\"c3\" class=\"span-12\" hidden>\n    <div class=\"fs-grid\">\n      <label class=\"span-4\">Contact name*<br><input type=\"text\" id=\"s3ContactName\" name=\"3_Nombre\"><\/label>\n      <label class=\"span-4\">Contact phone*<br><input type=\"text\" id=\"s3ContactPhone\" name=\"3_Tel\u00e9fono\" inputmode=\"tel\"><\/label>\n      <label class=\"span-4\">Contact email*<br><input type=\"email\" id=\"s3ContactEmail\" name=\"3_Email\" placeholder=\"email@example.com\"><\/label>\n    <\/div>\n  <\/div>\n<\/fieldset>\n\n\n  <div class=\"block-actions\">\n    <button type=\"button\" class=\"btn-prev\" data-prev>Previous<\/button>\n    <button type=\"button\" class=\"btn-next\" data-next>Next<\/button>\n  <\/div>\n<\/section>\n\n\n  <!-- BLOCK 5: Comments + Payment + Submit -->\n  <section class=\"form-block\" data-block=\"5\" hidden>\n    <fieldset class=\"fs-grid\" id=\"comentarios\">\n      <legend>Comments<\/legend>\n      <label class=\"span-12\"><textarea name=\"Comentarios u observaciones\" rows=\"4\"><\/textarea><\/label>\n    <\/fieldset>\n  <fieldset class=\"fs-grid\" id=\"pago\">\n  <legend id=\"pagoLegend\">Payment<\/legend>\n  <div class=\"pago-top span-12\">\n    <label>\n      <span>Total price:<\/span>\n      <output id=\"pagoTotal\" class=\"precio-chip\" aria-live=\"polite\">0.00&nbsp;\u20ac<\/output>\n    <\/label>\n  <\/div>\n  <div class=\"pago-top span-12\">\n    <label>\n      <span>Services booked:<\/span>\n      <output id=\"pagoServiciosCount\" class=\"precio-chip\"><span>0<\/span><\/output>\n    <\/label>\n  <\/div>\n  <!-- REAL field sent to Apps Script (same name\/values). Hidden and NOT required -->\n <div class=\"form-group span-12\">\n  <label for=\"pagoMetodo\" id=\"lblPago\">Payment method*<\/label>\n  <!-- Real <select>, hidden but synchronized -->\n  <select id=\"pagoMetodo\"\n          name=\"Indique la forma de pago\"\n          class=\"visually-hidden-select\"\n          aria-hidden=\"true\" tabindex=\"-1\">\n    <option value=\"Transferencia bancaria\">Bank transfer<\/option>\n    <option value=\"Cargo a cuenta del cliente\">Charge to client account<\/option>\n    <option value=\"Tarjeta de cr\u00e9dito\">Credit card<\/option>\n  <\/select>\n  <!-- Pretty buttons -->\n  <div class=\"pago-buttons\" role=\"radiogroup\" aria-labelledby=\"lblPago\">\n    <button type=\"button\" class=\"pago-btn\" role=\"radio\" data-value=\"Transferencia bancaria\">Bank transfer<\/button>\n    <button type=\"button\" class=\"pago-btn\" role=\"radio\" data-value=\"Cargo a cuenta del cliente\">Charge to client account<\/button>\n    <button type=\"button\" class=\"pago-btn\" role=\"radio\" data-value=\"Tarjeta de cr\u00e9dito\">Credit card<\/button>\n  <\/div>\n  <!-- Bank data box (only for transfer\/charge) -->\n  <div id=\"bankBox\" class=\"bank-box\">\n    <strong>Company bank details<\/strong><br>\n    Account holder: MINI VIP&#8217;S CAR DRIVERS, S.L.<br>\n    Tax ID: B62551551<br>\n    Bank: Banco Sabadell<br>\n    IBAN: ES74 0081 7011 1500 0172 4681<br>\n    Reference: Booking \u2014 Client no. <span id=\"bankCliNum\">___<\/span> \u2014 Booking no. <span id=\"bankResNum\">\u2014<\/span>\n    <div class=\"muted\">If you choose bank transfer or charge to your account, please use these details. You can send us the payment proof by replying to the confirmation email.<\/div>\n  <\/div>\n<\/div>\n<\/fieldset>\n    <div class=\"block-actions\">\n      <button type=\"button\" class=\"btn-prev\" data-prev>Previous<\/button>\n      <div>\n        <button type=\"submit\">Send<\/button>\n        <span id=\"estado\" aria-live=\"polite\"><\/span>\n      <\/div>\n    <\/div>\n    <div id=\"toast\" role=\"dialog\" aria-modal=\"true\" aria-live=\"assertive\" data-open=\"0\" hidden>\n      <div class=\"toast-card\" role=\"document\">\n        <h3>Booking sent<\/h3>\n        <p>Your booking has been sent successfully.<\/p>\n        <div class=\"toast-actions\">\n          <button id=\"toastClose\" type=\"button\" class=\"btn-primary\">OK<\/button>\n        <\/div>\n      <\/div>\n    <\/div>\n  <\/section>\n<input type=\"hidden\" name=\"trp-form-language\" value=\"en\"\/><\/form>\n<!-- iOS helper modal -->\n<div id=\"bukas-ios-guide\" role=\"dialog\" aria-modal=\"true\" aria-labelledby=\"iosGuideTitle\" hidden>\n  <div class=\"card\" role=\"document\">\n    <h3 id=\"iosGuideTitle\">Add to Home Screen<\/h3>\n    <p>On iPhone\/iPad you can\u2019t install with a single click. Do this:<\/p>\n    <ol style=\"margin:.5rem 0 1rem; padding-left:1.2rem;\">\n      <li>Open this page in <strong>Safari<\/strong>.<\/li>\n      <li>Tap the <strong>Share<\/strong> button (square with arrow).<\/li>\n      <li>Choose <strong>Add to Home Screen<\/strong>.<\/li>\n    <\/ol>\n    <div class=\"row\">\n      <button id=\"iosGuideClose\" class=\"ghost\" type=\"button\">Close<\/button>\n      <a class=\"cta\" href=\"\/en\/bukas\/\" role=\"button\">Open in Safari<\/a>\n    <\/div>\n  <\/div>\n<\/div>\n\n<script type=\"module\">\n\/* ===========================\n   CONFIG: data and catalogs\n   =========================== *\/\nconst CONFIG = {\n  CLIENTS_URL: \"\",\n  clientesDemo: [\n    { num:\"120045\", tax:\"ESB62551551\", nombre:\"MINI VIP'S CAR DRIVERS, S.L.\", tel:\"+34 900 123 123\", email:\"reservas@minivips.es\", billing:\"C\/ Ejemplo 123, Barcelona\" },\n    { num:\"110010\", tax:\"ESX1234567Z\", nombre:\"Agencia Ficticia S.A.\", tel:\"+34 931 111 222\", email:\"ops@agenciaficticia.com\", billing:\"Av. Principal 456, Madrid\" }\n  ],\n  lugares: [\n    { value:\"Barcelona\", icon:\"\ud83c\udfd6\ufe0f\" },\n    { value:\"Madrid\",    icon:\"\ud83c\udfdb\ufe0f\" }\n  ],\n  vehiculos: [\n    { value:\"Car\",           icon:\"\ud83d\ude97\", maxPax:3 },\n    { value:\"Van\",           icon:\"\ud83d\ude90\", maxPax:7 },\n    { value:\"Minibus 15\",    icon:\"\ud83d\ude8c\", maxPax:15 },\n    { value:\"Minibus 25\",    icon:\"\ud83d\ude8c\", maxPax:25 },\n    { value:\"Coach\",         icon:\"\ud83d\ude8c\", maxPax:50 }\n  ],\n  servicios: {\n    Barcelona: {\n      Car:           [\"Transfer\",\"4h as directed\",\"3h city tour\"],\n      Van:           [\"Transfer\",\"4h as directed\",\"Event shuttle\"],\n      default:       [\"Point-to-point transfer\",\"Hourly as directed\"]\n    },\n    Madrid: {\n      Car:           [\"Transfer\",\"4h as directed\",\"3h city tour\"],\n      Van:           [\"Transfer\",\"4h as directed\",\"IFEMA transfer\"],\n      default:       [\"Point-to-point transfer\",\"Hourly as directed\"]\n    },\n    default: {\n      default: [\"Point-to-point transfer\",\"Hourly as directed\"]\n    }\n  },\n  \/\/ datalists per city: visible label -> exact\/normalized address\n  puntos: {\n    Barcelona: {\n      recogida: {\n        \"BCN Airport T1\": \"Aeroport de Barcelona-El Prat, Terminal 1\",\n        \"BCN Airport T2\": \"Aeroport de Barcelona-El Prat, Terminal 2\",\n        \"Sants Station\":  \"Pla\u00e7a dels Pa\u00efsos Catalans, 1, 08014 Barcelona\",\n        \"Hotel Arts\":     \"C\/ de la Marina, 19-21, 08005 Barcelona\"\n      },\n      destino: {\n        \"Fira Gran Via\":  \"Av. Joan Carles I, 64, 08908 L'Hospitalet de Llobregat\",\n        \"Camp Nou\":       \"C. d'Ar\u00edstides Maillol, 12, 08028 Barcelona\",\n        \"La Pedrera\":     \"Pg. de Gr\u00e0cia, 92, 08008 Barcelona\"\n      }\n    },\n    Madrid: {\n      recogida: {\n        \"Barajas T4\":     \"Av de la Hispanidad, s\/n, 28042 Madrid\",\n        \"Atocha\":         \"Plaza Emperador Carlos V, 28045 Madrid\",\n        \"Chamart\u00edn\":      \"Agust\u00edn de Fox\u00e1, s\/n, 28036 Madrid\",\n        \"Hotel Riu Plaza\": \"C\/ Gran V\u00eda, 84, 28013 Madrid\"\n      },\n      destino: {\n        \"IFEMA\":             \"Av. del Parten\u00f3n, 5, 28042 Madrid\",\n        \"Santiago Bernab\u00e9u\": \"Av. de Concha Espina, 1, 28036 Madrid\",\n        \"Plaza Mayor\":       \"Plaza Mayor, 28012 Madrid\"\n      }\n    }\n  }\n};\nconst SERVICIOS_FIJOS = [\"Transfer\",\"4h as directed\",\"8h as directed\"];\n\/* =============== Utils DOM =============== *\/\nconst qs  = (sel, ctx=document) => ctx.querySelector(sel);\nconst qsa = (sel, ctx=document) => [...ctx.querySelectorAll(sel)];\nfunction isVisible(el){\n  if (!el || el.disabled) return false;\n  let cur = el;\n  while (cur && cur !== document.body){\n    const cs = getComputedStyle(cur);\n    if (cs.display === 'none' || cs.visibility === 'hidden') return false;\n    cur = cur.parentElement;\n  }\n  return true;\n}\n\/* =============== Stepper =============== *\/\nfunction makeStepper(form){\n  const blocks  = qsa('.form-block', form);\n  const stepper = qsa('.stepper__item', form.parentElement);\n\n  function currentIndex(){\n    const i = blocks.findIndex(b => b.dataset.active === '1');\n    return i >= 0 ? i : 0;\n  }\n  function setActive(i){\n    i = Math.max(0, Math.min(i, blocks.length - 1));\n    blocks.forEach((b, idx) => { b.dataset.active = (idx===i?'1':'0'); b.hidden = idx!==i; });\n    stepper.forEach((s, idx) => s.dataset.state = idx < i ? 'done' : (idx===i ? 'active' : 'idle'));\n    const first = blocks[i].querySelector('input,select,textarea');\n    first?.focus?.();\n    refreshNext();\n  }\n  function visibleRequired(blk){\n    return qsa('input, select, textarea', blk).filter(el => isVisible(el) && el.required);\n  }\n  function canAdvance(i){\n    const blk = blocks[i];\n    if (!blk) return false;\n    if (blk.dataset.block==='3' && (qs('#t2')?.value!=='Yes' || qs('#serv2')?.hidden)) return true;\n    if (blk.dataset.block==='4' && (qs('#t3')?.value!=='Yes' || qs('#serv3')?.hidden)) return true;\n    return visibleRequired(blk).every(el => el.checkValidity());\n  }\n  function refreshNext(){\n    const i = currentIndex();\n    const nextBtn = blocks[i]?.querySelector('[data-next]');\n    if (!nextBtn) return;\n    nextBtn.disabled = !canAdvance(i);\n  }\n\n  \/\/ === NAV HELPERS ===\nfunction isS2On(){ \n  return (qs('#t2')?.value === 'Yes') && !qs('#serv2')?.hidden; \n}\nfunction isS3On(){ \n  return (qs('#t3')?.value === 'Yes') && !qs('#serv3')?.hidden; \n}\n\nfunction nextEnabledFrom(idx){\n  let t = Math.min(idx + 1, blocks.length - 1);\n  if (t === 2 && !isS2On()) t++;\n  if (t === 3 && !isS3On()) t++;\n  return t;\n}\n\nfunction prevEnabledFrom(idx){\n  let t = Math.max(idx - 1, 0);\n  if (t === 3 && !isS3On()) t--;\n  if (t === 2 && !isS2On()) t--;\n  return t;\n}\n\n  \nform.addEventListener('click', e=>{\n  const btn = e.target.closest('[data-next],[data-prev]');\n  if(!btn) return;\n  e.preventDefault();\n\n  const isS2On = () => (qs('#t2')?.value === 'Yes') && !qs('#serv2')?.hidden;\n  const isS3On = () => (qs('#t3')?.value === 'Yes') && !qs('#serv3')?.hidden;\n\n  const nextEnabledFrom = (idx) => {\n    let t = Math.min(idx + 1, blocks.length - 1);\n    if (t === 2 && !isS2On()) t++;\n    if (t === 3 && !isS3On()) t++;\n    return t;\n  };\n  const prevEnabledFrom = (idx) => {\n    let t = Math.max(idx - 1, 0);\n    if (t === 3 && !isS3On()) t--;\n    if (t === 2 && !isS2On()) t--;\n    return t;\n  };\n\n  const i = (()=>{\n    const k = blocks.findIndex(b => b.dataset.active === '1');\n    return k >= 0 ? k : 0;\n  })();\n\n  if (btn.hasAttribute('data-next')){\n    if (!canAdvance(i)) return;\n    setActive(nextEnabledFrom(i));\n  } else {\n    setActive(prevEnabledFrom(i));\n  }\n\n  \/\/ lazy init S2\/S3 if turned on\n  try{\n    const t2 = qs('#t2')?.value || 'No';\n    const t3 = qs('#t3')?.value || 'No';\n    const serv2 = qs('#serv2');\n    const serv3 = qs('#serv3');\n    if (t2 === 'Yes' && serv2 && !serv2.__init && typeof initServicio === 'function'){\n      serv2.hidden = false; serv2.dataset.open = '1';\n      initServicio(2); serv2.__init = true;\n    }\n    if (t3 === 'Yes' && serv3 && !serv3.__init && typeof initServicio === 'function'){\n      serv3.hidden = false; serv3.dataset.open = '1';\n      initServicio(3); serv3.__init = true;\n    }\n  } catch(_) {}\n\n  \/\/ recalc\n  queueMicrotask(()=>{\n    try{\n      window.cotizar?.('1'); window.cotizar?.('2'); window.cotizar?.('3');\n      typeof actualizarTotales === 'function' && actualizarTotales();\n      window.__bukasStepper?.refreshNext?.();\n    }catch(_){}\n  });\n});\n\n\n\n  ['input','change','blur'].forEach(ev=>{\n    form.addEventListener(ev,(e)=>{\n      const blk = blocks[currentIndex()];\n      if (blk && blk.contains(e.target)){\n        clearTimeout(blk.__deb);\n        blk.__deb=setTimeout(refreshNext,80);\n      }\n    });\n  });\n  return { setActive, refreshNext };\n}\n\/* =============== Chips =============== *\/\nfunction renderChipsForSelect(select){\n  const wrap = select.parentElement.querySelector(`.chips[data-chips-for=\"${select.id}\"]`);\n  if (!wrap) return;\n\n  let list = [];\n  if (select.id.includes('Lugar')) {\n    list = CONFIG.lugares;\n  } else if (select.id.includes('Vehiculo')) {\n    list = CONFIG.vehiculos;\n  } else if (select.id.includes('Servicio')) {\n    list = [...select.options]\n      .filter(o => o.value)\n      .map(o => ({ value: o.value, icon: \"\u2022\" }));\n  }\n\n  wrap.innerHTML = '';\n  list.forEach(opt => {\n    const btn = document.createElement('button');\n    btn.type = 'button';\n    btn.className = 'chip';\n    btn.dataset.value = opt.value;\n    btn.innerHTML = `<span class=\"ico\">${opt.icon || \"\u2022\"}<\/span><span>${opt.value}<\/span>`;\n    btn.addEventListener('click', () => {\n      select.value = opt.value;\n      select.dispatchEvent(new Event('change', { bubbles: true }));\n    });\n    wrap.appendChild(btn);\n  });\n\n  const sync = () => {\n    [...wrap.querySelectorAll('.chip')]\n      .forEach(ch => ch.classList.toggle('is-active', ch.dataset.value === select.value));\n  };\n  select.addEventListener('change', sync);\n  sync();\n}\n\n\n\/* =============== Services list (front combos) =============== *\/\nfunction fillServicios(n){\n  const sel = document.getElementById(`s${n}Servicio`);\n  if (!sel) return;\n\n  const lugar = document.getElementById(`s${n}Lugar`)?.value?.trim();\n  const veh   = document.getElementById(`s${n}Vehiculo`)?.value?.trim();\n\n  const list = (lugar && veh) ? SERVICIOS_FIJOS : [];\n\n  const current = sel.value;\n  sel.innerHTML = '<option value=\"\"><\/option>';\n  list.forEach(s => sel.insertAdjacentHTML('beforeend', `<option value=\"${s}\">${s}<\/option>`));\n\n  sel.disabled = list.length === 0;\n  if (list.includes(current)) { sel.value = current; } else { sel.value = ''; }\n  if (!sel.value && list.length) { sel.value = list[0]; }\n\n  if (typeof renderChipsForSelect === 'function') renderChipsForSelect(sel);\n\n  sel.dispatchEvent(new Event('change', { bubbles: true }));\n  if (typeof window.cotizar === 'function') window.cotizar(String(n));\n}\n\n['#cliNum','#cliTax','#s1Lugar','#s1Vehiculo'].forEach(id=>{\n  const el = document.querySelector(id);\n  el && el.addEventListener('change', ()=>fillServicios(1));\n});\n['#cliNum','#cliTax','#s2Lugar','#s2Vehiculo'].forEach(id=>{\n  const el = document.querySelector(id);\n  el && el.addEventListener('change', ()=>fillServicios(2));\n});\n['#cliNum','#cliTax','#s3Lugar','#s3Vehiculo'].forEach(id=>{\n  const el = document.querySelector(id);\n  el && el.addEventListener('change', ()=>fillServicios(3));\n});\ndocument.querySelector('#s2Vehiculo')?.addEventListener('change', ()=>applyMaxPax(2));\ndocument.querySelector('#s3Vehiculo')?.addEventListener('change', ()=>applyMaxPax(3));\n\n\/* =============== Max pax per vehicle =============== *\/\nfunction applyMaxPax(n = 1){\n  const veh  = qs(`#s${n}Vehiculo`)?.value || '';\n  const def  = CONFIG.vehiculos.find(v => v.value === veh);\n  const inp  = qs(`#s${n}Pax`);\n  const hint = qs(`#s${n}PaxHint`);\n  if (!inp) return;\n\n  const max = def?.maxPax || '';\n  if (max){\n    inp.max = String(max);\n    inp.placeholder = `Max ${max}`;\n\n    const prevMax = Number(inp.dataset.prevMax || 0);\n    const valNum  = Number(inp.value || 0);\n\n    if (!inp.value || valNum === prevMax){\n      inp.value = String(max);\n    }\n    else if (valNum > max){\n      inp.value = String(max);\n    }\n\n    inp.dataset.prevMax = String(max);\n  } else {\n    inp.removeAttribute('max');\n    inp.placeholder = `Max \u2014`;\n  }\n\n  if (hint){\n    hint.textContent = max ? `Maximum capacity for ${veh}: ${max} pax` : '';\n  }\n}\n\n\/* =============== Datalists per city + exact address auto-fill =============== *\/\nfunction fillDatalistsForCity(n=1){\n  const city = qs(`#s${n}Lugar`)?.value || '';\n  const pickList = qs(`#s${n}PickupOptions`);\n  const dropList = qs(`#s${n}DropoffOptions`);\n  if (!pickList || !dropList) return;\n  const cat = CONFIG.puntos[city] || { recogida:{}, destino:{} };\n  pickList.innerHTML = Object.keys(cat.recogida).map(k=>`<option value=\"${k}\">`).join('');\n  dropList.innerHTML = Object.keys(cat.destino).map(k=>`<option value=\"${k}\">`).join('');\n}\nfunction bindAddressAuto(n=1){\n  const city = qs(`#s${n}Lugar`)?.value || '';\n  const cat = CONFIG.puntos[city] || { recogida:{}, destino:{} };\n  const p = qs(`#s${n}Pickup`), pe = qs(`#s${n}PickupExact`);\n  const d = qs(`#s${n}Dropoff`), de = qs(`#s${n}DropoffExact`);\n  const copyIfEmpty = (src, dst) => {\n    if (!dst.value.trim()) dst.value = src.value.trim();\n  };\n  p?.addEventListener('change', ()=>{\n    const val = p.value.trim();\n    if (cat.recogida[val]) pe.value = cat.recogida[val];\n    else copyIfEmpty(p, pe);\n  });\n  d?.addEventListener('change', ()=>{\n    const val = d.value.trim();\n    if (cat.destino[val]) de.value = cat.destino[val];\n    else copyIfEmpty(d, de);\n  });\n  p?.addEventListener('blur', ()=>{ if(!pe.value.trim()) pe.value = p.value.trim(); });\n  d?.addEventListener('blur', ()=>{ if(!de.value.trim()) de.value = d.value.trim(); });\n}\n\/* =============== Yes\/No toggle =============== *\/\nfunction enhanceYesNoSelect(select){\n  if(!select) return;\n  const opts=[...select.options].map(o=>o.text.trim());\n  if(!(opts.includes('Yes') && opts.includes('No'))) return;\n  const w=document.createElement('div'); w.className='yn-toggle'; w.setAttribute('role','radiogroup');\n  const y=document.createElement('button'); y.type='button'; y.textContent='Yes'; y.setAttribute('role','radio');\n  const n=document.createElement('button'); n.type='button'; n.textContent='No'; n.setAttribute('role','radio');\n  select.classList.add('visually-hidden-select');\n  select.parentNode.insertBefore(w, select.nextSibling);\n  w.appendChild(y); w.appendChild(n);\n  const sync=()=>{ const v=(select.value||'').trim();\n    y.classList.toggle('is-active', v==='Yes'); y.setAttribute('aria-checked', v==='Yes'?'true':'false');\n    n.classList.toggle('is-active', v==='No'); n.setAttribute('aria-checked', v==='No'?'true':'false');\n  };\n  const set=v=>{ select.value=v; select.dispatchEvent(new Event('change',{bubbles:true})); sync(); };\n  if(!select.value){ set('No'); } else sync();\n  y.addEventListener('click',()=>set('Yes'));\n  n.addEventListener('click',()=>set('No'));\n}\n\/* =============== Extra contact (show\/required) =============== *\/\nfunction toggleContacto(selectEl, boxId){\n  const box = document.getElementById(boxId);\n  if(!selectEl || !box) return;\n  const sync = ()=>{\n    const on = selectEl.value === 'Yes';\n    box.hidden = !on;\n    box.querySelectorAll('input').forEach(i=> i.required = on);\n  };\n  selectEl.addEventListener('change', sync);\n  sync();\n}\n\/* =============== Submit (Apps Script) =============== *\/\nconst WEBAPP_URL = \"https:\/\/script.google.com\/macros\/s\/AKfycbwQhfOmGWmoYonNorV4o0g8Md2ItLsqUwAhgmrUZEYREd-Hq0dzxMalvPb23827l9GTGQ\/exec\";\nwindow.WEBAPP_URL = WEBAPP_URL;\nconst HEADERS = [\n  \"N\u00famero de cliente\",\"Nombre del cliente\",\"Correo electr\u00f3nico\",\"Tel\u00e9fono\",\n  \"N\u00famero de Identificaci\u00f3n Fiscal (CIF, VAT o Pasaporte)\",\"Domicilio de Facturaci\u00f3n\",\n  \"Lugar\",\"Servicio\",\"Veh\u00edculo\",\"Cantidad de pasajeros?\",\n  \"Punto de recogida\",\"Punto de destino\",\n  \"Direcci\u00f3n exacta de recogida\",\"Direcci\u00f3n exacta de destino\",\n  \"Distancia (km)\",\n  \"Fecha de recogida\",\"Hora de recogida\",\n  \"Marque los adicionales solicitados\",\"Otro (especificar)\",\n  \"Quiere agregar una persona de contacto adem\u00e1s del cliente?\",\"Nombre del contacto\",\"Tel\u00e9fono de contacto\",\"Email de contacto\",\n  \"Introduzca el n\u00famero de vuelo (si corresponde)\",\n  \"Desea agregar otro servicio al servicio Nro1?\",\n  \"Lugardos\",\"2_Servicio\",\"2_Veh\u00edculo\",\"2_Cantidad de pasajeros?\",\n  \"2_Punto de recogida\",\"2_Punto de destino\",\n  \"2_Direcci\u00f3n exacta de recogida\",\"2_Direcci\u00f3n exacta de destino\",\n  \"2_Distancia (km)\",\n  \"2_Fecha de recogida\",\"2_Hora de recogida\",\n  \"2_Marque los adicionales solicitados\",\"2_Otro (especificar)\",\n  \"2_Quiere agregar una persona de contacto adem\u00e1s del cliente?\",\"2_Nombre\",\"2_Tel\u00e9fono\",\"2_Email\",\n  \"2_Introduzca el n\u00famero de vuelo (si corresponde)\",\n  \"Desea agregar otro servicio al servicio Nro2?\",\n  \"Lugartres\",\"3_Servicio\",\"3_Veh\u00edculo\",\"3_Cantidad de pasajeros?\",\n  \"3_Punto de recogida\",\"3_Punto de destino\",\n  \"3_Direcci\u00f3n exacta de recogida\",\"3_Direcci\u00f3n exacta de destino\",\n  \"3_Distancia (km)\",\n  \"3_Fecha de recogida\",\"3_Hora de recogida\",\n  \"3_Marque los adicionales solicitados\",\"3_Otro (especificar)\",\n  \"3_Quiere agregar una persona de contacto adem\u00e1s del cliente?\",\"3_Nombre\",\"3_Tel\u00e9fono\",\"3_Email\",\n  \"3_Introduzca el n\u00famero de vuelo (si corresponde)\",\n  \"Comentarios u observaciones\",\n  \"Indique la forma de pago\"\n];\nconst SYNONYMS = {\n  \"N\u00famero de Identificaci\u00f3n Fiscal (CIF o VAT)\":\"N\u00famero de Identificaci\u00f3n Fiscal (CIF, VAT o Pasaporte)\",\n  \"Cantidad de pasajeros\":\"Cantidad de pasajeros?\",\n  \"Introduzca el n\u00famero de vuelo (si corresponde)\":\"Introduzca el n\u00famero de vuelo (si corresponde)\"\n};\nasync function fetchWithTimeout(resource, options={}){\n  const { timeout = 15000 } = options;\n  const controller = new AbortController();\n  const id = setTimeout(()=>controller.abort(), timeout);\n  try{\n    const resp = await fetch(resource, { ...options, signal: controller.signal });\n    return resp;\n  } finally { clearTimeout(id); }\n}\nwindow.fetchWithTimeout = fetchWithTimeout;\nfunction bindSubmit(form){\n  const toast = document.getElementById('toast');\n  const estado = document.getElementById('estado');\n  const toastClose = document.getElementById('toastClose');\n  const getExtras = (n)=>[...document.querySelectorAll(`.extras[data-for=\"${n}\"] input[type=\"checkbox\"]:checked`)]\n                          .map(i=>i.value)\n                          .join(\" | \");\n  const val = (sel)=>document.querySelector(sel)?.value?.trim() || '';\n  form.addEventListener('submit', async (e)=>{\n    e.preventDefault();\n    estado.textContent = 'Sending\u2026';\n    try{\n      const fd = new FormData(form);\n      const data = {};\n      for (const [k,v] of fd.entries()){\n        const key = (window.SYNONYMS && window.SYNONYMS[k]) ? window.SYNONYMS[k] : k;\n        data[key] = v;\n      }\n      const dist1 = val('#s1Distance'); if(dist1) data[\"Distancia (km)\"] = dist1;\n      data[\"Marque los adicionales solicitados\"] = getExtras(1);\n      data[\"Desea agregar otro servicio al servicio Nro1?\"] = (document.querySelector('#t2')?.value || '').trim();\n      data[\"Desea agregar otro servicio al servicio Nro2?\"] = (document.querySelector('#t3')?.value || '').trim();\n\n      const $  = (q)=>document.querySelector(q);\n      const get = (q)=>$(q)?.value?.trim() || '';\n      const s2Toggle  = (get('#t2') === 'Yes');\n      const s2HasData = ['#s2Lugar','#s2Vehiculo','#s2Servicio','#s2Pax',\n                         '#s2Pickup','#s2Dropoff','#s2Date','#s2Time',\n                         '#s2PickupExact','#s2DropoffExact','#s2Flight','#s2Otro']\n                        .some(sel => !!get(sel));\n      const s2Active  = s2Toggle || s2HasData;\n      if (s2Active){\n        data[\"Lugardos\"]                               = get('#s2Lugar');\n        data[\"2_Servicio\"]                            = get('#s2Servicio');\n        data[\"2_Veh\u00edculo\"]                            = get('#s2Vehiculo');\n        data[\"2_Cantidad de pasajeros?\"]              = get('#s2Pax');\n        data[\"2_Punto de recogida\"]                   = get('#s2Pickup');\n        data[\"2_Punto de destino\"]                    = get('#s2Dropoff');\n        data[\"2_Direcci\u00f3n exacta de recogida\"]        = get('#s2PickupExact');\n        data[\"2_Direcci\u00f3n exacta de destino\"]         = get('#s2DropoffExact');\n        const dist2 = get('#s2Distance'); if (dist2) data[\"2_Distancia (km)\"] = dist2;\n        data[\"2_Fecha de recogida\"]                   = get('#s2Date');\n        data[\"2_Hora de recogida\"]                    = get('#s2Time');\n        data[\"2_Marque los adicionales solicitados\"]  = getExtras(2);\n        data[\"2_Otro (especificar)\"]                  = get('#s2Otro');\n        const s2Add = get('#s2AddContact') || 'No';\n        data[\"2_Quiere agregar una persona de contacto adem\u00e1s del cliente?\"] = s2Add;\n        if (s2Add === 'Yes') {\n          data[\"2_Nombre\"]   = get('#s2ContactName');\n          data[\"2_Tel\u00e9fono\"] = get('#s2ContactPhone');\n          data[\"2_Email\"]    = get('#s2ContactEmail');\n        } else {\n          data[\"2_Nombre\"] = \"\"; data[\"2_Tel\u00e9fono\"] = \"\"; data[\"2_Email\"] = \"\";\n        }\n        data[\"2_Introduzca el n\u00famero de vuelo (si corresponde)\"] = get('#s2Flight');\n      } else {\n        [\"Lugardos\",\"2_Servicio\",\"2_Veh\u00edculo\",\"2_Cantidad de pasajeros?\",\"2_Punto de recogida\",\"2_Punto de destino\",\n         \"2_Direcci\u00f3n exacta de recogida\",\"2_Direcci\u00f3n exacta de destino\",\"2_Distancia (km)\",\"2_Fecha de recogida\",\"2_Hora de recogida\",\n         \"2_Marque los adicionales solicitados\",\"2_Otro (especificar)\",\n         \"2_Quiere agregar una persona de contacto adem\u00e1s del cliente?\",\"2_Nombre\",\"2_Tel\u00e9fono\",\"2_Email\",\n         \"2_Introduzca el n\u00famero de vuelo (si corresponde)\"]\n        .forEach(k=>data[k]='');\n      }\n\n      const s3Toggle  = (get('#t3') === 'Yes');\n      const s3HasData = ['#s3Lugar','#s3Vehiculo','#s3Servicio','#s3Pax',\n                         '#s3Pickup','#s3Dropoff','#s3Date','#s3Time',\n                         '#s3PickupExact','#s3DropoffExact','#s3Flight','#s3Otro']\n                        .some(sel => !!get(sel));\n      const s3Active  = s3Toggle || s3HasData;\n      if (s3Active){\n        data[\"Lugartres\"]                               = get('#s3Lugar');\n        data[\"3_Servicio\"]                            = get('#s3Servicio');\n        data[\"3_Veh\u00edculo\"]                            = get('#s3Vehiculo');\n        data[\"3_Cantidad de pasajeros?\"]              = get('#s3Pax');\n        data[\"3_Punto de recogida\"]                   = get('#s3Pickup');\n        data[\"3_Punto de destino\"]                    = get('#s3Dropoff');\n        data[\"3_Direcci\u00f3n exacta de recogida\"]        = get('#s3PickupExact');\n        data[\"3_Direcci\u00f3n exacta de destino\"]         = get('#s3DropoffExact');\n        const dist3 = get('#s3Distance'); if (dist3) data[\"3_Distancia (km)\"] = dist3;\n        data[\"3_Fecha de recogida\"]                   = get('#s3Date');\n        data[\"3_Hora de recogida\"]                    = get('#s3Time');\n        data[\"3_Marque los adicionales solicitados\"]  = getExtras(3);\n        data[\"3_Otro (especificar)\"]                  = get('#s3Otro');\n        const s3Add = get('#s3AddContact') || 'No';\n        data[\"3_Quiere agregar una persona de contacto adem\u00e1s del cliente?\"] = s3Add;\n        if (s3Add === 'Yes') {\n          data[\"3_Nombre\"]   = get('#s3ContactName');\n          data[\"3_Tel\u00e9fono\"] = get('#s3ContactPhone');\n          data[\"3_Email\"]    = get('#s3ContactEmail');\n        } else {\n          data[\"3_Nombre\"] = \"\"; data[\"3_Tel\u00e9fono\"] = \"\"; data[\"3_Email\"] = \"\";\n        }\n        data[\"3_Introduzca el n\u00famero de vuelo (si corresponde)\"] = get('#s3Flight');\n      } else {\n        [\"Lugartres\",\"3_Servicio\",\"3_Veh\u00edculo\",\"3_Cantidad de pasajeros?\",\"3_Punto de recogida\",\"3_Punto de destino\",\n         \"3_Direcci\u00f3n exacta de recogida\",\"3_Direcci\u00f3n exacta de destino\",\"3_Distancia (km)\",\"3_Fecha de recogida\",\"3_Hora de recogida\",\n         \"3_Marque los adicionales solicitados\",\"3_Otro (especificar)\",\n         \"3_Quiere agregar una persona de contacto adem\u00e1s del cliente?\",\"3_Nombre\",\"3_Tel\u00e9fono\",\"3_Email\",\n         \"3_Introduzca el n\u00famero de vuelo (si corresponde)\"]\n        .forEach(k=>data[k]='');\n      }\n\n      const payload = {};\n      HEADERS.forEach(h => { payload[h] = (h in data) ? data[h] : \"\"; });\n\n      await fetch(WEBAPP_URL, {\n        method: 'POST',\n        headers: { 'Content-Type': 'application\/json' },\n        body: JSON.stringify(payload),\n        mode: 'no-cors'\n      });\n      estado.textContent = '';\n      toast.hidden = false;\n    }catch(err){\n      console.warn('Submit error', err);\n      estado.textContent = 'Could not send. Please check your connection and try again.';\n    }\n  });\n  toastClose?.addEventListener('click', ()=>{\n    toast.hidden = true;\n    window.__bukasStepper?.setActive?.(0);\n  });\n}\n\/* =============== Client autocomplete =============== *\/\nasync function lookupCliente({ num, tax }) {\n  const n = (num || '').trim();\n  const t = (tax || '').trim();\n  if (!n || !t) return null;\n  try {\n    const qs = new URLSearchParams({ action: 'cliente', num: n, cif: t }).toString();\n    const r  = await fetch(`${WEBAPP_URL}?${qs}`, { cache: 'no-store' });\n    if (r.ok) {\n      const data = await r.json();\n      const c = data && data.cliente;\n      if (c) {\n        return {\n          nombre:  c.nombre    || '',\n          tel:     c.telefono  || '',\n          email:   c.email     || '',\n          billing: c.direccion || '',\n          num:     c.numero    || n,\n          tax:     c.cif       || t,\n          verified: !!data.verified\n        };\n      }\n    }\n  } catch (_) {}\n  try {\n    const hit = (CONFIG?.clientesDemo || []).find(c =>\n      String(c.num || '').trim().toLowerCase() === n.toLowerCase() &&\n      String(c.tax || '').trim().toLowerCase() === t.toLowerCase()\n    );\n    if (hit) {\n      return {\n        nombre:  hit.nombre  || '',\n        tel:     hit.tel     || '',\n        email:   hit.email   || '',\n        billing: hit.billing || '',\n        num:     hit.num     || n,\n        tax:     hit.tax     || t,\n        verified: false\n      };\n    }\n  } catch(_) {}\n  return null;\n}\nfunction bindClienteAuto() {\n  const num    = document.querySelector('#cliNum');\n  const tax    = document.querySelector('#cliTax');\n  const name   = document.querySelector('#cliName');\n  const tel    = document.querySelector('#cliPhone');\n  const email  = document.querySelector('#cliEmail');\n  const bill   = document.querySelector('#cliBilling');\n  const status = document.querySelector('#clienteStatus');\n  const setRO = (el, ro) => el && (el.readOnly = !!ro);\n  const lock  = (on) => { setRO(name,on); setRO(tel,on); setRO(bill,on); setRO(tax,on); setRO(num,on); };\n  const badge = (kind, text) => {\n    if (!status) return;\n    const cls = kind === 'ok' ? 'badge badge-ok'\n            : kind === 'warn' ? 'badge badge-warn'\n            : 'badge badge-err';\n    status.innerHTML = `<span class=\"${cls}\">${text}<\/span>`;\n  };\n  function triggerRevalidate() {\n    ['#cliTax','#cliName','#cliPhone','#cliBilling','#cliEmail'].forEach(sel=>{\n      const el = document.querySelector(sel);\n      if (el) el.dispatchEvent(new Event('input', { bubbles:true }));\n    });\n    window.__bukasStepper?.refreshNext();\n  }\n  let deb; const debounce = (fn, t=280) => { clearTimeout(deb); deb = setTimeout(fn, t); };\n  async function query() {\n    const vNum = (num?.value || '').trim();\n    const vTax = (tax?.value || '').trim();\n    if (!vNum || !vTax) {\n      badge('warn', 'Enter client number and Tax ID \/ VAT to autocomplete.');\n      lock(false);\n      triggerRevalidate();\n      return;\n    }\n    badge('warn', 'Looking up client\u2026');\n    const data = await lookupCliente({ num: vNum, tax: vTax });\n    if (data) {\n      if (data.nombre)  name.value  = data.nombre;\n      if (data.tel)     tel.value   = data.tel;\n      if (data.email && !email.value) email.value = data.email;\n      if (data.billing) bill.value  = data.billing;\n      if (num && data.num) num.value = data.num;\n      if (tax && data.tax) tax.value = data.tax;\n      if (data.verified) {\n        badge('ok', 'Client verified: data locked (email editable).');\n        lock(true);\n      } else {\n        badge('warn', 'Client found \u2014 you can edit the data.');\n        lock(false);\n      }\n      triggerRevalidate();\n    } else {\n      badge('err', 'Client not found. Please fill in manually.');\n      lock(false);\n      triggerRevalidate();\n    }\n  }\n  ['input','change','blur'].forEach(ev => {\n    num?.addEventListener(ev, () => debounce(query));\n    tax?.addEventListener(ev, () => debounce(query));\n  });\n  const n0 = (num?.value || '').trim();\n  const t0 = (tax?.value || '').trim();\n  if (n0 && t0) query(); else badge('warn', 'Enter client number and Tax ID \/ VAT to autocomplete.');\n}\n\/* =============== Enhancers =============== *\/\nfunction enhanceYesNo(){\n  ['#t2','#t3','#s1AddContact','#s2AddContact','#s3AddContact'].forEach(sel=>enhanceYesNoSelect(qs(sel)));\n  toggleContacto(qs('#s1AddContact'), 'c1');\n  toggleContacto(qs('#s2AddContact'), 'c2');\n  toggleContacto(qs('#s3AddContact'), 'c3');\n}\nfunction bindOtrosAviso(){\n  const inp = qs('#s1Otro'), avis = qs('#aviso-otros-1');\n  if (!inp || !avis) return;\n  const t=()=>{ avis.hidden = !(inp.value||'').trim(); };\n  inp.addEventListener('input', t); t();\n}\n\nconst t2   = document.querySelector('#t2');\nconst serv2= document.querySelector('#serv2');\nconst t3    = document.querySelector('#t3');\nconst serv3 = document.querySelector('#serv3');\nfunction initServicio(n){\n  renderChipsForSelect(document.querySelector(`#s${n}Lugar`));\n  renderChipsForSelect(document.querySelector(`#s${n}Vehiculo`));\n  renderChipsForSelect(document.querySelector(`#s${n}Servicio`));\n  ['#s'+n+'Lugar','#s'+n+'Vehiculo'].forEach(sel=>{\n    document.querySelector(sel)?.addEventListener('change', ()=>{\n      fillServicios(n);\n      applyMaxPax(n);\n      fillDatalistsForCity(n);\n      bindAddressAuto(n);\n      window.__bukasStepper?.refreshNext?.();\n    });\n  });\n  fillServicios(n);\n  applyMaxPax(n);\n  fillDatalistsForCity(n);\n  bindAddressAuto(n);\n  const today = new Date().toISOString().slice(0,10);\n  const d = document.getElementById(`s${n}Date`);\n  if (d && !d.min) d.min = today;\n  [\n    `#s${n}Lugar`,`#s${n}Vehiculo`,`#s${n}Servicio`,`#s${n}Pax`,\n    `#s${n}Pickup`,`#s${n}Dropoff`,`#s${n}Date`,`#s${n}Time`,\n    `#s${n}PickupExact`,`#s${n}DropoffExact`\n  ].forEach(sel=>{\n    const el = document.querySelector(sel);\n    el?.addEventListener('change', ()=>window.cotizar?.(String(n)));\n    el?.addEventListener('blur',   ()=>window.cotizar?.(String(n)));\n    el?.addEventListener('input',  ()=>window.cotizar?.(String(n)));\n  });\n  document.querySelectorAll(`.extras[data-for=\"${n}\"] input[type=\"checkbox\"]`)\n    .forEach(ch=> ch.addEventListener('change', ()=>window.cotizar?.(String(n))));\n  setTimeout(()=>window.cotizar?.(String(n)), 0);\n}\nt2?.addEventListener('change', ()=>{\n  const on = t2.value === 'Yes';\n  serv2.hidden = !on;\n  serv2.dataset.open = on ? '1' : '0';\n  if (on && !serv2.__init){ initServicio(2); serv2.__init = true; }\n});\nt3?.addEventListener('change', ()=>{\n  const on = t3.value === 'Yes';\n  serv3.hidden = !on;\n  serv3.dataset.open = '1';\n  if (on && !serv3.__init){ initServicio(3); serv3.__init = true; }\n});\nif (t3 && serv3 && t3.value === 'Yes') {\n  serv3.hidden = false;\n  serv3.dataset.open = '1';\n  if (!serv3.__init) { initServicio(3); serv3.__init = true; }\n}\n\/* =============== PWA =============== *\/\nfunction setupPWA(){\n  const btn = document.getElementById('btnInstall');\n  const iosGuide = document.getElementById('bukas-ios-guide');\n  const iosClose = document.getElementById('iosGuideClose');\n  let deferredPrompt = null;\n  window.addEventListener('beforeinstallprompt', (e) => { e.preventDefault(); deferredPrompt = e; if (btn) btn.hidden = false; });\n  window.addEventListener('appinstalled', () => { if (btn) btn.hidden = true; });\n  const isIOS = \/iphone|ipad|ipod\/i.test(navigator.userAgent);\n  const isStandalone = window.matchMedia('(display-mode: standalone)').matches || window.navigator.standalone;\n  if (isIOS && !isStandalone && btn) btn.hidden = false;\n  btn?.addEventListener('click', async () => {\n    if (isIOS && !isStandalone) { iosGuide.hidden = false; return; }\n    if (deferredPrompt) { deferredPrompt.prompt(); try { await deferredPrompt.userChoice; } catch(_) {} deferredPrompt = null; btn.hidden = true; }\n  });\n  iosClose?.addEventListener('click', () => iosGuide.hidden = true);\n}\n\/* =============== Bootstrap =============== *\/\ndocument.addEventListener('DOMContentLoaded', ()=>{\n  const form = document.getElementById('bukasForm');\n  if (!form) return;\n  setupPWA?.();\n  \nconst now = new Date();\nconst minDate = now.toISOString().slice(0,10);\nconst future = new Date(now.getTime() + 24*60*60*1000);\nconst tomorrowDate = future.toISOString().slice(0,10);\nconst hh = String(future.getHours()).padStart(2,'0');\nconst mm = String(future.getMinutes()).padStart(2,'0');\nconst defaultTime = `${hh}:${mm}`;\n\n['s1Date','s2Date','s3Date'].forEach(id => {\n  const el = document.getElementById(id);\n  if (!el) return;\n  el.min = minDate;\n  if (!el.value) el.value = tomorrowDate;\n});\n\n['s1Time','s2Time','s3Time'].forEach(id => {\n  const el = document.getElementById(id);\n  if (!el) return;\n  if (!el.value) el.value = defaultTime;\n});\n\n  const stepper = makeStepper(form);\n  window.__bukasStepper = stepper;\n\n\/* === AUTO-NAV BY TOGGLE === *\/\n(function () {\n  const form = document.getElementById('bukasForm');\n  if (!form || !window.__bukasStepper?.setActive) return;\n\n  const blocks = [...form.querySelectorAll('.form-block')];\n  const idxByBlock = n => blocks.findIndex(b => b.dataset.block === String(n));\n  const go = i => window.__bukasStepper.setActive(i);\n\n  form.querySelector('.form-block[data-block=\"2\"] [data-next]')?.remove();\n  form.querySelector('.form-block[data-block=\"3\"] [data-next]')?.remove();\n\n  const visibleRequiredOk = blk =>\n    [...blk.querySelectorAll('input,select,textarea')].filter(el => {\n      if (!el.required || el.disabled) return false;\n      let n = el;\n      while (n && n !== document.body) {\n        const cs = getComputedStyle(n);\n        if (cs.display === 'none' || cs.visibility === 'hidden') return false;\n        n = n.parentElement;\n      }\n      return true;\n    }).every(el => el.checkValidity());\n\n  function ensureInitServicio(n) {\n    const root = document.getElementById('serv' + n);\n    if (!root) return;\n    if (!root.__init && typeof window.initServicio === 'function') {\n      root.hidden = false;\n      root.dataset.open = '1';\n      window.initServicio(n);\n      root.__init = true;\n    }\n  }\n\n  const t2 = document.getElementById('t2');\n  const blk2 = blocks[idxByBlock(2)];\n  t2?.addEventListener('change', () => {\n    if (!blk2 || !visibleRequiredOk(blk2)) return;\n    if (t2.value === 'Yes') {\n      ensureInitServicio(2);\n      go(2);\n    } else {\n      go(4);\n    }\n  });\n\n  const t3 = document.getElementById('t3');\n  const blk3 = blocks[idxByBlock(3)];\n  t3?.addEventListener('change', () => {\n    if (!blk3 || !visibleRequiredOk(blk3)) return;\n    if (t3.value === 'Yes') {\n      ensureInitServicio(3);\n      go(3);\n    } else {\n      const s3 = document.getElementById('serv3');\n      if (s3) { s3.hidden = true; s3.dataset.open = '0'; }\n      go(4);\n    }\n  });\n})();\n\n  renderChipsForSelect(document.querySelector('#s1Lugar'));\n  renderChipsForSelect(document.querySelector('#s1Vehiculo'));\n  renderChipsForSelect(document.querySelector('#s1Servicio'));\n  ['#s1Lugar','#s1Vehiculo'].forEach(sel=>{\n    document.querySelector(sel)?.addEventListener('change', ()=>{\n      fillServicios(1);\n      applyMaxPax(1);\n      applyMaxPax(2);\n      applyMaxPax(3);\n      fillDatalistsForCity(1);\n      bindAddressAuto(1);\n      stepper.refreshNext();\n    });\n  });\n  fillServicios(1);\n  applyMaxPax(1);\n  fillDatalistsForCity(1);\n  bindAddressAuto(1);\n  enhanceYesNo();\n  bindOtrosAviso();\n  bindSubmit(form);\n  bindClienteAuto();\n  form.addEventListener('keydown',(e)=>{\n    if(e.key==='Enter' && !e.shiftKey){\n      const active = document.querySelector('.form-block[data-active=\"1\"]');\n      const nextBtn = active?.querySelector('[data-next]');\n      if(nextBtn && !nextBtn.disabled){ e.preventDefault(); nextBtn.click(); }\n    }\n  });\n  setTimeout(()=> stepper.refreshNext(), 0);\n});\n<\/script>\n\n<!-- Address lookup + distance (kept logic, messages in English if any) -->\n<script>\nfunction _ciudadDesdeDetalle_(det){\n  const c = (det && det.cityDetected || '').toLowerCase();\n  if (!c) return '';\n  if (\/\\bbarcelona\\b\/.test(c)) return 'Barcelona';\n  if (\/\\bmadrid\\b\/.test(c))    return 'Madrid';\n  return '';\n}\nfunction _setLugarSiConviene_(idx, ciudad){\n  const sel = document.getElementById(`s${idx}Lugar`);\n  if (!sel || !ciudad) return;\n  const prev = (sel.value || '').trim();\n  if (!prev || prev.toLowerCase() !== ciudad.toLowerCase()){\n    sel.value = ciudad;\n    sel.dispatchEvent(new Event('change', { bubbles:true }));\n  }\n}\nfunction _writeExactAddress_(inputEl, idx, formatted){\n  if (!inputEl || !formatted) return;\n  const isPickup  = \/Pickup$\/.test(inputEl.id);\n  const suffix    = isPickup ? 'PickupExact' : 'DropoffExact';\n  const exactEl   = document.getElementById(`s${idx}${suffix}`);\n  if (exactEl) exactEl.value = formatted;\n}\nfunction _haversineKm_(lat1, lon1, lat2, lon2){\n  const toRad = d => d * Math.PI \/ 180;\n  const R = 6371;\n  const dLat = toRad(lat2 - lat1);\n  const dLon = toRad(lon2 - lon1);\n  const a = Math.sin(dLat\/2)**2 +\n            Math.cos(toRad(lat1)) * Math.cos(toRad(lat2)) *\n            Math.sin(dLon\/2)**2;\n  return R * (2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a)));\n}\nfunction _updateDistance_(idx){\n  const p   = document.getElementById(`s${idx}Pickup`);\n  const d   = document.getElementById(`s${idx}Dropoff`);\n  const out = document.getElementById(`s${idx}Distance`);\n  if(!p || !d || !out) return;\n  const la  = parseFloat(p?.dataset?.lat);\n  const lo  = parseFloat(p?.dataset?.lng);\n  const la2 = parseFloat(d?.dataset?.lat);\n  const lo2 = parseFloat(d?.dataset?.lng);\n  if([la,lo,la2,lo2].every(Number.isFinite)){\n    const km = _haversineKm_(la,lo,la2,lo2);\n    out.value = km.toFixed(1).replace('.', ',');\n  }else{\n    out.value = '';\n  }\n}\nasync function _lookupYAutocompleta_(inputEl, idx){\n  const q = (inputEl && inputEl.value || '').trim();\n  if (!q || !window.WEBAPP_URL) return;\n  try{\n    const url = `${WEBAPP_URL}?action=lugar&query=${encodeURIComponent(q)}`;\n    const r = await fetch(url, { cache:'no-store' });\n    const j = await r.json();\n    if (!j || j.ok === false) return;\n    if (j.formatted) _writeExactAddress_(inputEl, idx, j.formatted);\n    if (typeof j.lat === 'number' && typeof j.lng === 'number'){\n      inputEl.dataset.lat = String(j.lat);\n      inputEl.dataset.lng = String(j.lng);\n    } else {\n      delete inputEl.dataset.lat; delete inputEl.dataset.lng;\n    }\n    const ciudad = _ciudadDesdeDetalle_(j);\n    if (ciudad) _setLugarSiConviene_(idx, ciudad);\n    _updateDistance_(idx);\n    if (typeof window.cotizar === 'function') window.cotizar(String(idx));\n  } catch(e){}\n}\nfunction _bindLookup_(inputSel, idx){\n  const el = document.querySelector(inputSel);\n  if (!el) return;\n  let t;\n  el.addEventListener('input', () => {\n    clearTimeout(t);\n    t = setTimeout(() => _lookupYAutocompleta_(el, idx), 700);\n  });\n  el.addEventListener('blur', () => _lookupYAutocompleta_(el, idx));\n}\n_bindLookup_('#s1Pickup',  1);\n_bindLookup_('#s1Dropoff', 1);\n_bindLookup_('#s2Pickup',  2);\n_bindLookup_('#s2Dropoff', 2);\n_bindLookup_('#s3Pickup',  3);\n_bindLookup_('#s3Dropoff', 3);\n<\/script>\n\n<!-- Pricing \/ cotizador -->\n<script>\n(function(){\n  const qs  = (sel, ctx=document) => ctx.querySelector(sel);\n  const qsa = (sel, ctx=document) => [...ctx.querySelectorAll(sel)];\n  const fmt2 = (v)=> new Intl.NumberFormat('es-ES',{minimumFractionDigits:2, maximumFractionDigits:2}).format(v);\n  window.__precios = { 1:null, 2:null, 3:null };\n\n  function listoCore(n){\n    return [`#s${n}Lugar`,`#s${n}Vehiculo`,`#s${n}Servicio`].every(sel=>{\n      const el=qs(sel); return el && el.value && el.checkValidity();\n    });\n  }\n  function servicioCompleto(n){\n    return [\n      `#s${n}Lugar`,`#s${n}Vehiculo`,`#s${n}Servicio`,\n      `#s${n}Pax`,`#s${n}Date`,`#s${n}Time`,`#s${n}Pickup`,`#s${n}Dropoff`\n    ].every(sel=>{ const el=qs(sel); return el && el.value && el.checkValidity(); });\n  }\n\n  const TARIFAS = {\n    default: {\n      Barcelona: {\n        Car:           { \"Transfer\": 45, \"4h as directed\": 160, \"8h as directed\": 300 },\n        Van:           { \"Transfer\": 60, \"4h as directed\": 200, \"8h as directed\": 360 },\n        default:       { \"Transfer\": 60, \"4h as directed\": 220, \"8h as directed\": 400 }\n      },\n      Madrid: {\n        Car:           { \"Transfer\": 40, \"4h as directed\": 150, \"8h as directed\": 280 },\n        Van:           { \"Transfer\": 55, \"4h as directed\": 190, \"8h as directed\": 340 },\n        default:       { \"Transfer\": 55, \"4h as directed\": 210, \"8h as directed\": 380 }\n      },\n      default: {\n        default: { \"Transfer\": 50, \"4h as directed\": 180, \"8h as directed\": 320 }\n      }\n    },\n    \"ESB62551551\": {\n      Barcelona: {\n        Car:           { \"Transfer\": 42, \"4h as directed\": 155, \"8h as directed\": 290 },\n        Van:           { \"Transfer\": 58, \"4h as directed\": 195, \"8h as directed\": 350 },\n        default:       { \"Transfer\": 58, \"4h as directed\": 215, \"8h as directed\": 390 }\n      },\n      Madrid: {\n        Car:           { \"Transfer\": 38, \"4h as directed\": 145, \"8h as directed\": 270 },\n        Van:           { \"Transfer\": 53, \"4h as directed\": 185, \"8h as directed\": 330 },\n        default:       { \"Transfer\": 53, \"4h as directed\": 205, \"8h as directed\": 370 }\n      }\n    }\n  };\n  function getClienteKey(){\n    const tax = (document.querySelector('#cliTax')?.value || '').trim();\n    const num = (document.querySelector('#cliNum')?.value || '').trim();\n    const taxKey = tax.replace(\/\\s+\/g,'');\n    if (taxKey && TARIFAS[taxKey]) return taxKey;\n    if (num && TARIFAS[num]) return num;\n    return 'default';\n  }\n  function precioDesdeTarifa(clienteKey, lugar, vehiculo, servicio){\n    const cat = TARIFAS[clienteKey] || TARIFAS.default;\n    const byLugar = cat[lugar] || cat.default || {};\n    const byVeh = byLugar[vehiculo] || byLugar.default || {};\n    const p = byVeh[servicio];\n    return (typeof p === 'number') ? p : null;\n  }\n  function setChipPrice(n, precio){\n    const out = document.querySelector(`#s${n}Price`);\n    const span= out?.querySelector('span[data-c=\"EUR\"]');\n    if (!out || !span) return;\n    if (precio==null){\n      out.hidden = true;\n      span.textContent = '\u2014';\n    } else {\n      span.textContent = `${fmt2(precio)} \u20ac`;\n      out.hidden = false;\n    }\n  }\n  function precioTarifaLocal(n){\n    const lugar = qs(`#s${n}Lugar`)?.value || '';\n    const veh   = qs(`#s${n}Vehiculo`)?.value || '';\n    const serv  = qs(`#s${n}Servicio`)?.value || '';\n    if (!lugar || !veh || !serv) return null;\n    const key = (typeof getClienteKey==='function') ? getClienteKey() : 'default';\n    const p   = (typeof precioDesdeTarifa==='function') ? precioDesdeTarifa(key, lugar, veh, serv) : null;\n    return (typeof p==='number') ? p : null;\n  }\n  async function precioBackend(n, fallback){\n    try{\n      if (!window.WEBAPP_URL) return fallback;\n      const city   = qs(`#s${n}Lugar`)?.value || '';\n      const veh    = qs(`#s${n}Vehiculo`)?.value || '';\n      const serv   = qs(`#s${n}Servicio`)?.value || '';\n      const date   = qs(`#s${n}Date`)?.value || '';\n      const time   = qs(`#s${n}Time`)?.value || '';\n\n      const vehApi  = \/van\/i.test(veh) ? 'Van'\n                    : \/15\/.test(veh)   ? 'Minibus (up to 15 pax)'\n                    : \/25\/.test(veh)   ? 'Minibus (up to 25 pax)'\n                    : \/coach\/i.test(veh) ? 'Coach (up to 50 pax)'\n                    : veh;\n\n      const servApi = \/transfer\/i.test(serv) ? 'Transfer' : serv;\n\n      const numCliente = qs('#cliNum')?.value?.trim() || '';\n      const taxId      = qs('#cliTax')?.value?.trim() || '';\n\n      const params = new URLSearchParams({\n        action: 'precio',\n        servicio: String(n),\n        city,\n        vehicle: vehApi,\n        service: servApi,\n        date,\n        time,\n        numCliente,\n        taxId\n      });\n\n      const r = await fetchWithTimeout(`${WEBAPP_URL}?${params.toString()}`, { cache:'no-store', timeout:7000 });\n      const j = await r.json().catch(()=>null);\n\n      const pick = (obj, ...names) => {\n        for (const k of names) {\n          const v = obj && obj[k];\n          if (v !== undefined && v !== null && v !== '') return v;\n        }\n        return null;\n      };\n\n      const parseNum = (v) => {\n        if (typeof v === 'number') return v;\n        if (typeof v === 'string') {\n          const clean = v.replace(\/\\s\/g,'').replace(\/\\.\/g,'').replace(',', '.');\n          const n = Number(clean);\n          return Number.isFinite(n) ? n : null;\n        }\n        return null;\n      };\n\n      const rawTotal = pick(j, 'total', 'totalConIva', 'precioFinal');\n      const totalNum = parseNum(rawTotal);\n\n      if (totalNum != null) {\n        return {\n          base:     parseNum(pick(j, 'base', 'importeBase')) ?? (fallback?.base ?? 0),\n          rec_noct: parseNum(pick(j, 'rec_noct', 'recNoct')) ?? 0,\n          rec_fest: parseNum(pick(j, 'rec_fest', 'recFest')) ?? 0,\n          total:    totalNum\n        };\n      }\n      return fallback;\n\n    }catch{\n      return fallback;\n    }\n  }\n  async function cotizar(n='1'){\n    const chip = qs(`#s${n}Price`);\n    if (!listoCore(n)){\n      if (chip) { chip.hidden = true; chip.querySelector('[data-c=\"EUR\"]').textContent = '\u2014'; }\n      window.__precios[n] = null;\n      actualizarTotales();\n      return;\n    }\n    const baseLocal = precioTarifaLocal(n);\n\n    if (baseLocal == null){\n      if (chip){\n        chip.hidden = false;\n        chip.querySelector('[data-c=\"EUR\"]').textContent = '\u2014';\n      }\n      window.__precios[n] = { base: 0, rec_noct: 0, rec_fest: 0, total: 0 };\n    } else {\n      if (chip){\n        chip.hidden = false;\n        chip.querySelector('[data-c=\"EUR\"]').textContent = `${fmt2(baseLocal)} \u20ac`;\n      }\n      window.__precios[n] = { base: baseLocal, rec_noct: 0, rec_fest: 0, total: baseLocal };\n    }\n\n    const det = await precioBackend(n, window.__precios[n]);\n    window.__precios[n] = det;\n    if (chip){\n      chip.hidden = false;\n      chip.querySelector('[data-c=\"EUR\"]').textContent = `${fmt2(det.total)} \u20ac`;\n    }\n    actualizarTotales();\n  }\n  function servicioActivo(n){\n    if (n === 1) return listoCore(1);\n    const t2 = qs('#t2')?.value === 'Yes';\n    const t3 = qs('#t3')?.value === 'Yes';\n    if (n === 2 && !t2) return false;\n    if (n === 3 && !t3) return false;\n    return listoCore(n);\n  }\n  function actualizarTotales(){\n    const activos = [1,2,3].filter(servicioActivo);\n    const suma = activos.reduce((acc, i) => {\n      const det = window.__precios[i];\n      if (det && typeof det.total === 'number') return acc + det.total;\n      const base = precioTarifaLocal(i);\n      return acc + (typeof base === 'number' ? base : 0);\n    }, 0);\n    const tot = qs('#pagoTotal');\n    if (tot) tot.textContent = `${fmt2(suma)} \u20ac`;\n    const cnt = qs('#pagoServiciosCount span');\n    if (cnt) cnt.textContent = String(activos.length);\n  }\n\n  window.cotizar = cotizar;\n  window.actualizarTotales = actualizarTotales;\n\n  [\n    '#s1Lugar','#s1Vehiculo','#s1Servicio','#s1Pax',\n    '#s1Pickup','#s1Dropoff','#s1Date','#s1Time',\n    '#s1PickupExact','#s1DropoffExact'\n  ].forEach(sel=>{\n    const el = qs(sel);\n    el?.addEventListener('change', ()=>cotizar('1'));\n    el?.addEventListener('input',  ()=>cotizar('1'));\n    el?.addEventListener('blur',   ()=>cotizar('1'));\n  });\n  qsa('.extras[data-for=\"1\"] input[type=\"checkbox\"]').forEach(ch =>\n    ch.addEventListener('change', ()=>cotizar('1'))\n  );\n  qs('#t2')?.addEventListener('change', ()=>cotizar('1'));\n  qs('#t3')?.addEventListener('change', ()=>cotizar('1'));\n  setTimeout(()=>cotizar('1'), 0);\n})();\n<\/script>\n\n<!-- Payment buttons sync -->\n<script>\n(function(){\n  const select  = document.getElementById('pagoMetodo');\n  const btns    = Array.from(document.querySelectorAll('.pago-btn'));\n  const bankBox = document.getElementById('bankBox');\n  const form    = document.querySelector('form');\n  function setPago(value){\n    select.value = value;\n    btns.forEach(b=>{\n      const on = b.dataset.value === value;\n      b.classList.toggle('active', on);\n      b.setAttribute('aria-checked', on ? 'true':'false');\n    });\n    const showBank = \/Transferencia bancaria|Cargo a cuenta del cliente\/i.test(value);\n    bankBox.hidden = !showBank;\n  }\n  btns.forEach(b=> b.addEventListener('click', ()=> setPago(b.dataset.value)));\n  if(!select.value) setPago('Transferencia bancaria'); else setPago(select.value);\n  if(form){\n    form.addEventListener('submit', ()=>{\n      if(!select.value) setPago('Transferencia bancaria');\n    });\n  }\n})();\n<\/script>\n\n<!-- UX patch: auto-advance, etc. -->\n<script id=\"ux-auto-advance-patch\">\n(function(){\n  const doc = document;\n  const blocks = [...doc.querySelectorAll('.form-block')];\n\n  const _vis = el => {\n    if (!el || el.disabled) return false;\n    let n = el;\n    while (n && n !== document.body){\n      const cs = getComputedStyle(n);\n      if (cs.display === 'none' || cs.visibility === 'hidden') return false;\n      n = n.parentElement;\n    }\n    return true;\n  };\n  const _reqIn = blk => [...blk.querySelectorAll('input,select,textarea')].filter(el => _vis(el) && el.required);\n  const _idx = () => blocks.findIndex(b => b.dataset.active === '1');\n\n  const goTo = (idx) => {\n    if (window.__bukasStepper?.setActive) {\n      window.__bukasStepper.setActive(idx);\n    } else {\n      const steps = [...doc.querySelectorAll('.stepper__item')];\n      blocks.forEach((b,i)=>{ b.dataset.active = (i===idx?'1':'0'); b.hidden = i!==idx; });\n      steps.forEach((s,i)=> s.dataset.state = i<idx?'done':(i===idx?'active':'idle'));\n    }\n    const first = blocks[idx]?.querySelector('input,select,textarea');\n    first?.focus?.();\n  };\n\n  const canAdvance = (idx) => {\n    const blk = blocks[idx]; if (!blk) return false;\n    if (blk.dataset.block==='3' && (doc.querySelector('#t2')?.value!=='Yes' || doc.querySelector('#serv2')?.hidden)) return true;\n    if (blk.dataset.block==='4' && (doc.querySelector('#t3')?.value!=='Yes' || doc.querySelector('#serv3')?.hidden)) return true;\n    return _reqIn(blk).every(el => el.checkValidity());\n  };\n\n  const softWarn = (selectEl, msg) => {\n    const id = selectEl.id + '-warn';\n    let hint = doc.getElementById(id);\n    if (!hint) {\n      hint = doc.createElement('small');\n      hint.id = id;\n      hint.className = 'hint';\n      selectEl.insertAdjacentElement('afterend', hint);\n    }\n    hint.textContent = msg;\n    clearTimeout(hint.__t);\n    hint.__t = setTimeout(()=> hint.textContent = '', 4000);\n  };\n\n  const ensureInitServicio = (n) => {\n    const root = doc.getElementById('serv'+n);\n    if (!root) return;\n    try {\n      if (!root.__init && typeof window.initServicio === 'function') {\n        root.hidden = false;\n        root.dataset.open = '1';\n        window.initServicio(n);\n        root.__init = true;\n      } else if (!root.__init) {\n        ['Lugar','Vehiculo','Servicio'].forEach(k => {\n          const sel = doc.getElementById(`s${n}${k}`);\n          if (sel && typeof window.renderChipsForSelect === 'function') window.renderChipsForSelect(sel);\n        });\n        root.__init = true;\n      }\n    } catch(_) {}\n  };\n\n  const announce = (msg) => {\n    let live = doc.getElementById('ux-live');\n    if (!live) {\n      live = doc.createElement('div');\n      live.id = 'ux-live';\n      live.setAttribute('aria-live','polite');\n      Object.assign(live.style, {position:'absolute',width:'1px',height:'1px',overflow:'hidden',clip:'rect(1px,1px,1px,1px)'});\n      doc.body.appendChild(live);\n    }\n    live.textContent = msg;\n  };\n\n  const bindAutoAdvance = (toggleId, targetIndex, servicioN) => {\n    const sel = doc.getElementById(toggleId);\n    if (!sel) return;\n\n    const maybeAdvance = () => {\n      if ((sel.value || '').trim() !== 'Yes') return;\n      const i = _idx();\n      if (canAdvance(i)) {\n        ensureInitServicio(servicioN);\n        goTo(targetIndex);\n        announce(`Service ${servicioN} enabled`);\n      } else {\n        softWarn(sel, 'Please complete the required fields to continue.');\n      }\n    };\n\n    sel.addEventListener('change', maybeAdvance);\n\n    const yn = sel.parentElement?.querySelector('.yn-toggle');\n    yn?.addEventListener('click', () => {\n      if ((sel.value || '').trim() === 'Yes') maybeAdvance();\n    });\n  };\n\n  bindAutoAdvance('t2', 2, 2);\n  bindAutoAdvance('t3', 3, 3);\n\n  const t3Sel = doc.getElementById('t3');\n  t3Sel?.addEventListener('change', () => {\n    if ((t3Sel.value || '').trim() !== 'No') return;\n    const i = _idx();\n    if (i === 2 && canAdvance(i)) {\n      const s3Root = doc.getElementById('serv3');\n      if (s3Root) { s3Root.hidden = true; s3Root.dataset.open = '0'; }\n      goTo(4);\n      announce('Service 3 disabled: moving to payment and send');\n    }\n  });\n\n  const btnNextS3 = doc.querySelector('.form-block[data-block=\"4\"] [data-next]');\n  if (btnNextS3) {\n    btnNextS3.textContent = 'Choose payment and send';\n    btnNextS3.setAttribute('aria-label','Choose payment and send');\n  }\n})();\n<\/script>\n\n<!-- Extras warning -->\n<script>\n(function(){\n  function setupExtrasWarning(n){\n    const wrap = document.querySelector('.extras[data-for=\"' + n + '\"]');\n    if (!wrap) return;\n\n    let aviso = document.getElementById('extrasAviso' + n);\n    if (!aviso){\n      aviso = document.createElement('div');\n      aviso.id = 'extrasAviso' + n;\n      aviso.className = 'aviso-otros';\n      aviso.hidden = true;\n      aviso.textContent = 'Some extras may carry an additional cost.';\n      wrap.insertAdjacentElement('afterend', aviso);\n    }\n\n    const update = () => {\n      const anyChecked = !!wrap.querySelector('input[type=\"checkbox\"]:checked');\n      aviso.hidden = !anyChecked;\n    };\n\n    wrap.addEventListener('change', update);\n    update();\n  }\n\n  document.addEventListener('DOMContentLoaded', function(){\n    [1,2,3].forEach(setupExtrasWarning);\n  });\n})();\n<\/script>\n\n<!-- Distance-based custom quote warning -->\n<script>\n(function(){\n  const CITY_COORDS = {\n    Barcelona: { lat: 41.3874, lng: 2.1686 },\n    Madrid:    { lat: 40.4168, lng: -3.7038 }\n  };\n\n  function haversineKm(lat1, lon1, lat2, lon2){\n    const toRad = d => d * Math.PI \/ 180;\n    const R = 6371;\n    const dLat = toRad(lat2 - lat1);\n    const dLon = toRad(lon2 - lon1);\n    const a = Math.sin(dLat\/2)**2 +\n              Math.cos(toRad(lat1)) * Math.cos(toRad(lat2)) *\n              Math.sin(dLon\/2)**2;\n    return R * (2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a)));\n  }\n\n  function ensureWarn(idx){\n    const distInput = document.getElementById('s' + idx + 'Distance');\n    if (!distInput) return null;\n\n    let aviso = document.getElementById('distWarn' + idx);\n    if (!aviso){\n      aviso = document.createElement('div');\n      aviso.id = 'distWarn' + idx;\n      aviso.className = 'aviso-otros span-12';\n      aviso.hidden = true;\n      aviso.textContent = 'Due to the pickup or drop-off location, this service might require a custom quotation.';\n      const label = distInput.closest('label') || distInput.parentElement;\n      label.insertAdjacentElement('afterend', aviso);\n    }\n    return aviso;\n  }\n\n  function check(idx){\n    const lugar = (document.getElementById('s' + idx + 'Lugar')?.value || '').trim();\n    const city = CITY_COORDS[lugar];\n    const aviso = ensureWarn(idx);\n    if (!aviso || !city){\n      if (aviso) aviso.hidden = true;\n      return;\n    }\n\n    const toNum = v => {\n      const n = Number(v);\n      return Number.isFinite(n) ? n : null;\n    };\n\n    const ids = ['s' + idx + 'Pickup', 's' + idx + 'Dropoff'];\n    let far = false;\n\n    ids.forEach(id => {\n      const el = document.getElementById(id);\n      if (!el) return;\n      const lat = toNum(el.dataset.lat);\n      const lng = toNum(el.dataset.lng);\n      if (lat == null || lng == null) return;\n\n      const km = haversineKm(city.lat, city.lng, lat, lng);\n      if (km > 50) far = true;\n    });\n\n    aviso.hidden = !far;\n  }\n\n  function bind(idx){\n    ['Lugar','Pickup','Dropoff'].forEach(field => {\n      const el = document.getElementById('s' + idx + field);\n      if (!el) return;\n      ['change','input','blur'].forEach(ev =>\n        el.addEventListener(ev, () => check(idx))\n      );\n    });\n  }\n\n  document.addEventListener('DOMContentLoaded', function(){\n    [1,2,3].forEach(bind);\n  });\n})();\n<\/script>\n\n<!-- Booking ID + bank reference -->\n<script>\n(function(){\n  function ensureReservaId(){\n    let id = sessionStorage.getItem('reservaId');\n    if(!id){\n      const d = new Date();\n      id = d.getFullYear().toString().padStart(4,'0')\n        + String(d.getMonth()+1).padStart(2,'0')\n        + String(d.getDate()).padStart(2,'0')\n        + String(d.getHours()).padStart(2,'0')\n        + String(d.getMinutes()).padStart(2,'0')\n        + String(d.getSeconds()).padStart(2,'0');\n      sessionStorage.setItem('reservaId', id);\n    }\n    const span = document.getElementById('bankResNum');\n    if(span) span.textContent = id;\n    const hid = document.getElementById('resNum');\n    if(hid) hid.value = id;\n    return id;\n  }\n\n  function updateBankConcept(){\n    const cli = (document.getElementById('cliNum')?.value || '').trim();\n    const cliSpan = document.getElementById('bankCliNum');\n    if(cliSpan) cliSpan.textContent = cli || '___';\n    ensureReservaId();\n  }\n\n  document.addEventListener('DOMContentLoaded', function(){\n    updateBankConcept();\n    ['input','change','blur'].forEach(ev=>{\n      document.getElementById('cliNum')?.addEventListener(ev, updateBankConcept);\n    });\n    document.getElementById('bukasForm')?.addEventListener('click', (e)=>{\n      if(e.target.closest('[data-next],[data-prev]')) setTimeout(updateBankConcept,0);\n    });\n    document.addEventListener('bukas:cliente-autocompletado', updateBankConcept);\n  });\n})();\n<\/script>\n\n<!-- Remove floating button if re-injected -->\n<script>\n(function () {\n  function removeBtn() {\n    var b = document.getElementById('floating-button');\n    if (b) b.remove();\n    var m = document.getElementById('custom-modal');\n    if (m) m.remove();\n  }\n  removeBtn();\n  new MutationObserver(removeBtn).observe(document.body, { childList: true, subtree: true });\n})();\n<\/script>\n\n<\/body>\n<\/html>","protected":false},"excerpt":{"rendered":"<p>Joinas \u2014 Booking (all in one, with fares + auto) Joinas &#8211; Booking Download our app Client Service 1 Service 2 Service 3 Final Client details Client no. (optional) Tax ID \/ VAT \/ Passport* Client name* Phone* Billing address* Email address* For this booking you can use a different email than the one on [&hellip;]<\/p>","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"footnotes":""},"class_list":["post-3088","page","type-page","status-publish","hentry"],"blocksy_meta":[],"_links":{"self":[{"href":"https:\/\/mvips.net\/en\/wp-json\/wp\/v2\/pages\/3088","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/mvips.net\/en\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/mvips.net\/en\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/mvips.net\/en\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/mvips.net\/en\/wp-json\/wp\/v2\/comments?post=3088"}],"version-history":[{"count":2,"href":"https:\/\/mvips.net\/en\/wp-json\/wp\/v2\/pages\/3088\/revisions"}],"predecessor-version":[{"id":3092,"href":"https:\/\/mvips.net\/en\/wp-json\/wp\/v2\/pages\/3088\/revisions\/3092"}],"wp:attachment":[{"href":"https:\/\/mvips.net\/en\/wp-json\/wp\/v2\/media?parent=3088"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}