
(function () {
  const api = window.wp?.apiFetch;
  if (!api) return;

  api.use((options, next) => {
    options.headers = options.headers || {};
    if (window.VSTRB?.nonce) options.headers['X-WP-Nonce'] = window.VSTRB.nonce;
    return next(options);
  });

  function qs(el, sel) { return el.querySelector(sel); }
  function qsa(el, sel) { return Array.from(el.querySelectorAll(sel)); }
  function getLocale(root) {
    const bookingRoot = root?.closest?.('.vstrb-booking') || root;
    const lang = bookingRoot?.dataset?.language;
    if (lang) return lang;
    const htmlLang = document.documentElement.lang;
    if (htmlLang) return htmlLang.slice(0, 2);
    const nav = navigator.language || navigator.userLanguage;
    return nav ? nav.slice(0, 2) : '';
  }
  function getI18n(root) {
    const scope = root?.closest?.('.vstrb-booking') || root?.closest?.('.vstrb-calendar') || root;
    if (scope?.dataset?.i18n) {
      try {
        return JSON.parse(scope.dataset.i18n);
      } catch (e) {}
    }
    return window.VSTRB?.i18n || {};
  }
  function t(root, key, fallback) {
    const i18n = getI18n(root);
    return i18n?.[key] || fallback;
  }
  function fmtMonthTitle(ym, locale) {
    const [y, m] = ym.split('-').map(x => parseInt(x, 10));
    const d = new Date(y, m - 1, 1);
    const intlLocale = locale ? String(locale).replace('_', '-') : (window.VSTRB?.locale || undefined);
    return d.toLocaleString(intlLocale, { month: 'long', year: 'numeric' });
  }
  function fmtMonthName(date, locale) {
    const intlLocale = locale ? String(locale).replace('_', '-') : (window.VSTRB?.locale || undefined);
    return date.toLocaleString(intlLocale, { month: 'long' });
  }

  async function loadServices(box, unitId, root) {
    try {
      const locale = getLocale(root);
      const currency = box.closest('.vstrb-booking')?.dataset.currency || 'EUR';
      const res = await api({ path: `/vstrb/v1/services?unit_id=${unitId}&locale=${locale}&currency=${currency}` });
      const services = res.services || [];
      if (!services.length) {
        box.innerHTML = `<div style="opacity:.7">${t(root, 'noServices', 'No optional services.')}</div>`;
        return;
      }
      const i18n = getI18n(root);
      box.innerHTML = services.map(s => {
        const typeLabel = i18n?.[s.pricing_type] || s.pricing_type;

        let metaHtml = '';
        if (s.meta_data && s.meta_data.length) {
          const rows = s.meta_data.map(m => `
                <div class="vstrb-svc-meta vstrb-meta-row" style="font-size:0.9em;color:#666">
                    <strong class="vstrb-meta-key">${escapeHtml(m.key)}:</strong> ${escapeHtml(m.value)}
                </div>
             `).join('');
          metaHtml = `<div class="vstrb-meta-container" style="margin-left:24px;margin-top:2px">${rows}</div>`;
        }

        return `
        <div class="vstrb-svc">
          <label style="display:flex;gap:10px;align-items:center">
            <input type="checkbox" class="vstrb-svc-check" value="${s.id}" ${s.is_optional == 0 ? 'checked disabled' : ''}>
            <span>${escapeHtml(s.name)} <small>(${typeLabel}, ${Number(s.price).toFixed(2)} ${escapeHtml(currency)})</small></span>
          </label>
          ${metaHtml}
        </div>
      `;
      }).join('');
    } catch (e) {
      box.innerHTML = `<div style="color:#b00">${t(root, 'errorLoadingServices', 'Error loading services.')}</div>`;
    }
  }

  async function loadContactFields(box, unitId, root) {
    try {
      const locale = getLocale(root);
      const res = await api({ path: `/vstrb/v1/contact-fields?unit_id=${unitId}&locale=${locale}` });
      const fields = res.fields || [];
      if (!fields.length) return;

      box.innerHTML = fields.map(f => `
        <div class="vstrb-row vstrb-custom-field-row" style="margin-bottom:10px">
          <label>${escapeHtml(f.label)}</label>
          <input type="text" class="vstrb-custom-input" data-key="${escapeHtml(f.key)}" style="width:100%" />
        </div>
      `).join('');
    } catch (e) {
      console.error('Error loading contact fields', e);
    }
  }

  function escapeHtml(s) {
    return String(s).replace(/[&<>"']/g, (c) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#039;" }[c]));
  }

  function formatMoney(value, currency) {
    return `${Number(value || 0).toFixed(2)} ${currency}`;
  }

  function formatQty(value) {
    const num = Number(value || 0);
    if (Number.isInteger(num)) return String(num);
    return num.toFixed(2).replace(/\.?0+$/, '');
  }

  function selectedPaymentMethod(root) {
    return qs(root, '.vstrb-payment-method:checked')?.value || '';
  }

  function paymentNeedsBilling(method) {
    return method === 'card' || method === 'card_stripe' || method === 'card_mollie' || method === 'bank_transfer';
  }

  function toggleBookingBillingBox(root) {
    const box = qs(root, '.vstrb-booking-billing-box');
    if (!box) return;
    const enabled = qs(root, '.vstrb-book-billing-enabled')?.checked || false;
    const fields = qs(box, '.vstrb-booking-billing-fields');
    if (fields) fields.style.display = enabled ? 'block' : 'none';
    qsa(box, 'input:not(.vstrb-book-billing-enabled)').forEach(input => {
      input.disabled = !enabled;
    });
  }

  function contactFullName(firstName, lastName) {
    return [firstName, lastName].map(v => String(v || '').trim()).filter(Boolean).join(' ');
  }

  function fillBillingNameFromContact(root, firstName, lastName) {
    const billingNameInput = qs(root, '.vstrb-book-billing-name');
    const companyIdInput = qs(root, '.vstrb-book-billing-company-id');
    if (!billingNameInput || billingNameInput.value.trim() || (companyIdInput && companyIdInput.value.trim())) return;
    billingNameInput.value = contactFullName(firstName, lastName);
  }

  function fixCompanyIdAutofill(root, firstName = '', lastName = '') {
    const billingNameInput = qs(root, '.vstrb-book-billing-name');
    const companyIdInput = qs(root, '.vstrb-book-billing-company-id');
    if (!billingNameInput || !companyIdInput) return;

    const companyId = companyIdInput.value.trim();
    if (!companyId || !/[^\d\s]/.test(companyId)) return;

    const contactName = contactFullName(firstName, lastName);
    const billingName = billingNameInput.value.trim();
    if (!billingName || billingName === contactName) {
      billingNameInput.value = companyId;
    }
    companyIdInput.value = '';
  }

  function getPaymentReturnState() {
    try {
      const params = new URLSearchParams(window.location.search || '');
      const status = (params.get('vstrb_payment') || '').trim().toLowerCase();
      if (!status) return null;
      return {
        status,
        bookingId: (params.get('booking_id') || '').trim()
      };
    } catch (e) {
      return null;
    }
  }

  function cleanupPaymentReturnParams() {
    try {
      const url = new URL(window.location.href);
      url.searchParams.delete('vstrb_payment');
      url.searchParams.delete('booking_id');
      window.history.replaceState({}, document.title, url.toString());
    } catch (e) {
      // no-op
    }
  }

  function applyPaymentReturnState(root, state) {
    const i18n = getI18n(root);
    if (!state || !state.status) return false;
    const msg = qs(root, '.vstrb-msg');
    const thanks = root.parentNode?.querySelector?.('.vstrb-thanks');
    const bookingSuffix = state.bookingId ? ` #${state.bookingId}` : '';

    if (state.status === 'success') {
      root.style.display = 'none';
      if (thanks) {
        const p = thanks.querySelector('p');
        if (p && i18n?.paymentSuccessReturn) {
          p.textContent = `${i18n.paymentSuccessReturn}${bookingSuffix}`;
        }
        thanks.style.display = 'block';
        thanks.scrollIntoView({ behavior: 'smooth', block: 'center' });
      } else if (msg) {
        msg.textContent = `${i18n?.paymentSuccessReturn || 'Payment successful. Thank you!'}${bookingSuffix}`;
      }
      return true;
    }

    if (msg) {
      msg.textContent = i18n?.paymentFailedReturn || 'There was a problem processing the payment. Please try again or contact us.';
    }
    if (thanks) {
      thanks.style.display = 'none';
    }
    root.style.display = '';
    root.scrollIntoView({ behavior: 'smooth', block: 'center' });
    return true;
  }

  function resetQuote(root, message = '') {
    const i18n = getI18n(root);
    const out = qs(root, '.vstrb-total');
    const breakdown = qs(root, '.vstrb-price-breakdown');
    if (out) out.textContent = message;
    if (!breakdown) return;

    breakdown.classList.remove('is-visible');

    const nightsEl = qs(breakdown, '.vstrb-price-nights');
    const baseEl = qs(breakdown, '.vstrb-price-base');
    const servicesEl = qs(breakdown, '.vstrb-price-services');
    const servicesRow = qs(breakdown, '.vstrb-price-services-row');
    const serviceLinesEl = qs(breakdown, '.vstrb-price-service-lines');
    const discountEl = qs(breakdown, '.vstrb-price-discount');
    const discountLabelEl = qs(breakdown, '.vstrb-price-discount-label');
    const discountRow = qs(breakdown, '.vstrb-price-discount-row');
    const discountLinesEl = qs(breakdown, '.vstrb-price-discount-lines');
    const surchargeEl = qs(breakdown, '.vstrb-price-surcharge');
    const surchargeLabelEl = qs(breakdown, '.vstrb-price-surcharge-label');
    const surchargeRow = qs(breakdown, '.vstrb-price-surcharge-row');
    const surchargeLinesEl = qs(breakdown, '.vstrb-price-surcharge-lines');
    const depositEl = qs(breakdown, '.vstrb-price-deposit');
    const depositRow = qs(breakdown, '.vstrb-price-deposit-row');
    const finalEl = qs(breakdown, '.vstrb-price-final');

    if (nightsEl) nightsEl.textContent = '-';
    if (baseEl) baseEl.textContent = '-';
    if (servicesEl) servicesEl.textContent = '';
    if (servicesRow) servicesRow.hidden = true;
    if (serviceLinesEl) {
      serviceLinesEl.innerHTML = '';
      serviceLinesEl.hidden = true;
    }
    if (discountEl) discountEl.textContent = '';
    if (surchargeEl) surchargeEl.textContent = '';
    if (depositEl) depositEl.textContent = '';
    if (discountLabelEl) discountLabelEl.textContent = i18n?.discount || 'Discount';
    if (surchargeLabelEl) surchargeLabelEl.textContent = i18n?.surcharge || 'Surcharge';
    if (discountLinesEl) {
      discountLinesEl.innerHTML = '';
      discountLinesEl.hidden = true;
    }
    if (surchargeLinesEl) {
      surchargeLinesEl.innerHTML = '';
      surchargeLinesEl.hidden = true;
    }
    if (finalEl) finalEl.textContent = '-';
    if (discountRow) discountRow.hidden = true;
    if (surchargeRow) surchargeRow.hidden = true;
    if (depositRow) depositRow.hidden = true;
  }

  function renderQuoteBreakdown(root, res) {
    const i18n = getI18n(root);
    const out = qs(root, '.vstrb-total');
    const breakdown = qs(root, '.vstrb-price-breakdown');
    const currency = root.dataset.currency || 'EUR';
    const nightsLabel = i18n?.nights || 'nights';

    if (out) out.textContent = Number(res.total ?? 0).toFixed(2);
    if (!breakdown) return;

    breakdown.classList.add('is-visible');

    const nightsEl = qs(breakdown, '.vstrb-price-nights');
    const baseEl = qs(breakdown, '.vstrb-price-base');
    const servicesEl = qs(breakdown, '.vstrb-price-services');
    const servicesRow = qs(breakdown, '.vstrb-price-services-row');
    const serviceLinesEl = qs(breakdown, '.vstrb-price-service-lines');
    const discountEl = qs(breakdown, '.vstrb-price-discount');
    const discountLabelEl = qs(breakdown, '.vstrb-price-discount-label');
    const discountRow = qs(breakdown, '.vstrb-price-discount-row');
    const discountLinesEl = qs(breakdown, '.vstrb-price-discount-lines');
    const surchargeEl = qs(breakdown, '.vstrb-price-surcharge');
    const surchargeLabelEl = qs(breakdown, '.vstrb-price-surcharge-label');
    const surchargeRow = qs(breakdown, '.vstrb-price-surcharge-row');
    const surchargeLinesEl = qs(breakdown, '.vstrb-price-surcharge-lines');
    const depositEl = qs(breakdown, '.vstrb-price-deposit');
    const depositRow = qs(breakdown, '.vstrb-price-deposit-row');
    const finalEl = qs(breakdown, '.vstrb-price-final');

    const nights = Number(res.nights ?? 0);
    const base = Number(res.base ?? 0);
    const discount = Number(res.discount_total ?? 0);
    const discountKind = String(res.discount_kind || 'none');
    const discountAmountNights = Number(res.discount_amount_nights ?? 0);
    const discountAmountStays = Number(res.discount_amount_stays ?? 0);
    const surcharge = Number(res.surcharge_total ?? 0);
    const refundableDeposit = Number(res.refundable_deposit ?? 0);
    const servicesTotal = Number(res.services_total ?? 0);
    const serviceLines = Array.isArray(res.services) ? res.services : [];
    const discountLines = Array.isArray(res.discount_lines) ? res.discount_lines : [];
    const surchargeLines = Array.isArray(res.surcharge_lines) ? res.surcharge_lines : [];

    if (nightsEl) nightsEl.textContent = `${nights} ${nightsLabel}`;
    if (baseEl) baseEl.textContent = formatMoney(base, currency);
    if (servicesRow && servicesEl) {
      if (servicesTotal > 0 || serviceLines.length) {
        servicesRow.hidden = false;
        servicesEl.textContent = formatMoney(servicesTotal, currency);
      } else {
        servicesRow.hidden = true;
        servicesEl.textContent = '';
      }
    }
    if (serviceLinesEl) {
      if (serviceLines.length) {
        serviceLinesEl.hidden = false;
        serviceLinesEl.innerHTML = serviceLines.map((line) => {
          const name = escapeHtml(line.name || '');
          const qty = formatQty(line.qty);
          const unit = formatMoney(line.unit_price ?? 0, currency);
          const total = formatMoney(line.total ?? 0, currency);
          return `
            <div class="vstrb-price-line vstrb-price-service-line">
              <span>${name} <small class="vstrb-price-service-calc">(${qty} x ${unit})</small></span>
              <span>${total}</span>
            </div>
          `;
        }).join('');
      } else {
        serviceLinesEl.innerHTML = '';
        serviceLinesEl.hidden = true;
      }
    }
    if (finalEl) finalEl.textContent = formatMoney(res.total ?? 0, currency);

    if (discountRow && discountEl) {
      if (discount > 0) {
        discountRow.hidden = false;
        let discountDetail = '';
        if (discountKind === 'amount' && discountAmountNights > 0) {
          const perNightDiscount = discount / discountAmountNights;
          const perNightLabel = i18n?.priceModes?.per_night || 'per night';
          discountDetail = ` (${formatMoney(perNightDiscount, currency)} ${perNightLabel})`;
        } else if (discountKind === 'amount_stay' && discountAmountStays > 0) {
          const perStayLabel = i18n?.priceModes?.per_stay || 'per stay';
          discountDetail = ` (${perStayLabel})`;
        } else if (discountKind === 'percent_stay') {
          const perStayLabel = i18n?.priceModes?.per_stay || 'per stay';
          discountDetail = ` (${perStayLabel})`;
        } else if (discountKind === 'percent') {
          const discountPct = base > 0 ? (discount / base) * 100 : 0;
          if (discountPct > 0) {
            discountDetail = ` (${discountPct.toFixed(1)}%)`;
          }
        }
        if (discountLabelEl) discountLabelEl.textContent = `${i18n?.discount || 'Discount'}${discountDetail}`;
        discountEl.textContent = `-${formatMoney(discount, currency)}`;
        if (discountLinesEl) {
          if (discountLines.length) {
            discountLinesEl.hidden = false;
            discountLinesEl.innerHTML = discountLines.map((line) => {
              const label = escapeHtml(line.label || i18n?.discount || 'Discount');
              return `
                <div class="vstrb-price-line vstrb-price-service-line">
                  <span>${label}</span>
                  <span>-${formatMoney(line.amount ?? 0, currency)}</span>
                </div>
              `;
            }).join('');
          } else {
            discountLinesEl.innerHTML = '';
            discountLinesEl.hidden = true;
          }
        }
      } else {
        discountRow.hidden = true;
        discountEl.textContent = '';
        if (discountLabelEl) discountLabelEl.textContent = i18n?.discount || 'Discount';
        if (discountLinesEl) {
          discountLinesEl.innerHTML = '';
          discountLinesEl.hidden = true;
        }
      }
    }

    if (surchargeRow && surchargeEl) {
      if (surcharge > 0) {
        surchargeRow.hidden = false;
        if (surchargeLabelEl) surchargeLabelEl.textContent = i18n?.surcharge || 'Surcharge';
        surchargeEl.textContent = `+${formatMoney(surcharge, currency)}`;
        if (surchargeLinesEl) {
          if (surchargeLines.length) {
            surchargeLinesEl.hidden = false;
            surchargeLinesEl.innerHTML = surchargeLines.map((line) => {
              const label = escapeHtml(line.label || i18n?.surcharge || 'Surcharge');
              return `
                <div class="vstrb-price-line vstrb-price-service-line">
                  <span>${label}</span>
                  <span>+${formatMoney(line.amount ?? 0, currency)}</span>
                </div>
              `;
            }).join('');
          } else {
            surchargeLinesEl.innerHTML = '';
            surchargeLinesEl.hidden = true;
          }
        }
      } else {
        surchargeRow.hidden = true;
        surchargeEl.textContent = '';
        if (surchargeLabelEl) surchargeLabelEl.textContent = i18n?.surcharge || 'Surcharge';
        if (surchargeLinesEl) {
          surchargeLinesEl.innerHTML = '';
          surchargeLinesEl.hidden = true;
        }
      }
    }

    if (depositRow && depositEl) {
      if (refundableDeposit > 0) {
        depositRow.hidden = false;
        depositEl.textContent = `+${formatMoney(refundableDeposit, currency)}`;
      } else {
        depositRow.hidden = true;
        depositEl.textContent = '';
      }
    }
  }

  function guestCounts(root) {
    const adultInput = qs(root, '.vstrb-guests');
    const childInput = qs(root, '.vstrb-children');
    const adults = Math.max(1, parseInt(adultInput?.value || '1', 10) || 1);
    const children = Math.max(0, parseInt(childInput?.value || '0', 10) || 0);
    return { adults, children, guests: adults + children };
  }

  function preselectCountryFromPageLanguage(root) {
    const select = qs(root, '.vstrb-country');
    if (!select || select.value) return;

    const pageLanguage = (document.documentElement.lang || '').replace('-', '_');
    const parts = pageLanguage.split('_');
    const languageCountries = {
      cs: 'CZ', de: 'DE', en: 'US', es: 'ES', fr: 'FR', it: 'IT',
      nl: 'NL', pl: 'PL', pt: 'PT', ru: 'RU', sk: 'SK', uk: 'UA',
      zh: 'CN', ja: 'JP'
    };
    const country = (parts[1] || languageCountries[parts[0]?.toLowerCase()] || '').toUpperCase();
    if (country && Array.from(select.options).some(option => option.value === country)) {
      select.value = country;
    }
  }

  function initCountrySelect(root) {
    const select = qs(root, '.vstrb-country');
    if (!select || typeof window.TomSelect !== 'function' || select.tomselect) return;

    const i18n = getI18n(root);
    const nativeStyle = window.getComputedStyle(select);
    const nativeStyles = {
      backgroundColor: nativeStyle.backgroundColor,
      borderColor: nativeStyle.borderTopColor,
      borderRadius: nativeStyle.borderTopLeftRadius,
      color: nativeStyle.color,
      fontFamily: nativeStyle.fontFamily,
      fontSize: nativeStyle.fontSize,
      lineHeight: nativeStyle.lineHeight
    };
    const nativeHeight = select.getBoundingClientRect().height;
    new window.TomSelect(select, {
      allowEmptyOption: true,
      closeAfterSelect: true,
      create: false,
      maxItems: 1,
      plugins: ['dropdown_input'],
      searchField: ['text', 'value'],
      onInitialize() {
        const wrapperStyle = this.wrapper.style;
        wrapperStyle.setProperty('--vstrb-country-background', nativeStyles.backgroundColor);
        wrapperStyle.setProperty('--vstrb-country-border-color', nativeStyles.borderColor);
        wrapperStyle.setProperty('--vstrb-country-border-radius', nativeStyles.borderRadius);
        wrapperStyle.setProperty('--vstrb-country-color', nativeStyles.color);
        wrapperStyle.setProperty('--vstrb-country-font-family', nativeStyles.fontFamily);
        wrapperStyle.setProperty('--vstrb-country-font-size', nativeStyles.fontSize);
        wrapperStyle.setProperty('--vstrb-country-line-height', nativeStyles.lineHeight);
        wrapperStyle.setProperty('--vstrb-country-min-height', `${Math.max(42, nativeHeight)}px`);
        if (this.control_input) {
          this.control_input.placeholder = i18n?.searchCountry || 'Search country...';
        }
      }
    });
  }

  function enforceGuestCapacity(root, changedInput) {
    const adultInput = qs(root, '.vstrb-guests');
    const childInput = qs(root, '.vstrb-children');
    const capacity = parseInt(adultInput?.max || '0', 10);
    if (!adultInput || !childInput || capacity <= 0) return;

    const counts = guestCounts(root);
    if (counts.guests <= capacity) return;

    if (changedInput === childInput) {
      childInput.value = Math.max(0, capacity - counts.adults);
    } else {
      if (counts.children >= capacity) {
        childInput.value = Math.max(0, capacity - 1);
      }
      adultInput.value = Math.max(1, capacity - Math.min(counts.children, capacity - 1));
    }
  }

  async function quote(root) {
    const i18n = getI18n(root);
    const unitId = root.dataset.unit;
    const df = qs(root, '.vstrb-date-from')?.value;
    const dt = qs(root, '.vstrb-date-to')?.value;
    const currency = root.dataset.currency || 'EUR';
    const { guests } = guestCounts(root);
    const checks = qsa(root, '.vstrb-svc-check:checked').map(i => parseInt(i.value, 10));
    if (!df || !dt) {
      resetQuote(root);
      return;
    }

    try {
      const locale = getLocale(root);
      const res = await api({
        path: '/vstrb/v1/quote',
        method: 'POST',
        data: { unit_id: parseInt(unitId, 10), date_from: df, date_to: dt, guests, services: checks, currency, locale }
      });
      renderQuoteBreakdown(root, res);
    } catch (e) {
      if (e?.data?.error === 'min_stay_violation') resetQuote(root, (i18n?.minStay || 'Minimum stay is %d nights.').replace('%d', e.data.limit));
      else if (e?.data?.error === 'max_stay_violation') resetQuote(root, (i18n?.maxStay || 'Maximum stay is %d nights.').replace('%d', e.data.limit));
      else if (e?.data?.error === 'guest_capacity_violation') resetQuote(root, (i18n?.maxGuests || 'Maximum number of guests is %d.').replace('%d', e.data.limit));
      else if (e?.data?.error === 'date_conflict' || e?.data?.error === 'unit_closed') resetQuote(root, i18n?.occupied || 'Dates are already occupied.');
      else resetQuote(root);
    }
  }

  async function book(root) {
    const i18n = getI18n(root);
    const unitId = parseInt(root.dataset.unit, 10);
    const df = qs(root, '.vstrb-date-from')?.value;
    const dt = qs(root, '.vstrb-date-to')?.value;
    const currency = root.dataset.currency || 'EUR';
    const { adults, children, guests } = guestCounts(root);
    const services = qsa(root, '.vstrb-svc-check:checked').map(i => parseInt(i.value, 10));
    const name = qs(root, '.vstrb-name')?.value || '';
    const lastname = qs(root, '.vstrb-lastname')?.value || '';
    const gender = qs(root, '.vstrb-gender')?.value || '';
    const country = qs(root, '.vstrb-country')?.value?.trim?.() || '';
    const email = qs(root, '.vstrb-email')?.value || '';
    const phonePrefix = qs(root, '.vstrb-phone-prefix')?.value || '';
    const phoneNum = qs(root, '.vstrb-phone')?.value || '';
    const phone = phonePrefix + phoneNum;
    const note = qs(root, '.vstrb-note')?.value || '';
    const hp = qs(root, '.vstrb-honeypot')?.value || '';
    const captchaRow = qs(root, '.vstrb-captcha-row');
    const captcha = qs(root, '.vstrb-captcha')?.value || '';
    const captchaToken = captchaRow?.dataset?.captchaToken || '';
    const paymentMethod = selectedPaymentMethod(root) || 'cash';
    const hasPaymentMethods = root.dataset.hasPaymentMethods === '1';
    const billingEnabled = hasPaymentMethods && (qs(root, '.vstrb-book-billing-enabled')?.checked || false);
    if (billingEnabled) {
      fixCompanyIdAutofill(root, name, lastname);
      fillBillingNameFromContact(root, name, lastname);
    }
    const billing = {
      enabled: billingEnabled,
      name: qs(root, '.vstrb-book-billing-name')?.value?.trim?.() || '',
      company_id: qs(root, '.vstrb-book-billing-company-id')?.value?.trim?.() || '',
      vat_id: qs(root, '.vstrb-book-billing-vat-id')?.value?.trim?.() || '',
      street: qs(root, '.vstrb-book-billing-street')?.value?.trim?.() || '',
      city: qs(root, '.vstrb-book-billing-city')?.value?.trim?.() || '',
      zip: qs(root, '.vstrb-book-billing-zip')?.value?.trim?.() || '',
      country: qs(root, '.vstrb-book-billing-country')?.value?.trim?.() || ''
    };

    const customFields = {};
    qsa(root, '.vstrb-custom-input').forEach(input => {
      customFields[input.dataset.key] = input.value;
    });

    const msg = qs(root, '.vstrb-msg');
    const confirmBox = qs(root, '.vstrb-confirm-now');
    const confirmNow = confirmBox && confirmBox.checked ? 1 : 0;

    msg.textContent = i18n?.loading || 'Sending…';

    const restUrl = window.VSTRB?.restUrl || '/wp-json/vstrb/v1';
    const nonce = window.VSTRB?.nonce || '';

    if (!df || !dt) {
      msg.textContent = i18n?.selectDates || 'Select dates.';
      return;
    }

    const guestInput = qs(root, '.vstrb-guests');
    const maxGuests = parseInt(guestInput?.max || '0', 10);
    if (maxGuests > 0 && guests > maxGuests) {
      msg.textContent = (i18n?.maxGuests || 'Maximum number of guests is %d.').replace('%d', maxGuests);
      return;
    }

    if (!name || !lastname || !email || !phoneNum || !captcha) {
      msg.textContent = i18n?.missingContact || 'Fill in all required fields.';
      return;
    }
    if (billingEnabled && (!(billing.name || billing.company_id) || !billing.street || !billing.city || !billing.zip || !billing.country)) {
      msg.textContent = i18n?.missingBilling || 'Fill in billing details for selected payment method.';
      return;
    }

    try {
      const res = await api({
        path: '/vstrb/v1/book',
        method: 'POST',
        data: { unit_id: unitId, date_from: df, date_to: dt, guests, adults, children, services, name, lastname, gender, country, email, phone, note, confirm_now: confirmNow, hp, captcha, captcha_token: captchaToken, custom_fields: customFields, currency, locale: getLocale(root), payment_method: paymentMethod, billing }
      });

      if (res?.payment_redirect_url) {
        msg.textContent = i18n?.paymentRedirecting || 'Redirecting to payment gateway...';
        window.location.href = res.payment_redirect_url;
        return;
      }
      if (res?.payment_instructions) {
        showPaymentInstructions(root, res);
        return;
      }
      if (res?.payment_instructions_missing && paymentMethod === 'bank_transfer') {
        msg.textContent = `${i18n?.paymentInstructionsMissing || 'Booking was created, but bank transfer instructions are not configured. Please contact us.'} #${res.booking_id}.`;
        return;
      }
      if (res?.payment_error && paymentNeedsBilling(paymentMethod)) {
        msg.textContent = `${i18n?.paymentLinkFailed || 'Booking was created, but the card payment link could not be created. Please contact us.'} (${res.payment_error})`;
        return;
      }

      // Success: Hide form and show thanks message
      root.style.display = 'none';
      const thanks = root.parentNode.querySelector('.vstrb-booking-thanks');
      if (thanks) {
        thanks.style.display = 'block';
        thanks.scrollIntoView({ behavior: 'smooth', block: 'center' });
      } else {
        msg.textContent = `${i18n?.success || 'Done.'} #${res.booking_id} (${res.status}).`;
      }
    } catch (e) {
      if (e?.data?.error === 'date_conflict' || e?.data?.error === 'unit_closed') msg.textContent = i18n?.occupied || 'Dates are already occupied.';
      else if (e?.data?.error === 'missing_contact') msg.textContent = i18n?.missingContact || 'Provide name and email.';
      else if (e?.data?.error === 'min_stay_violation') msg.textContent = (i18n?.minStay || 'Minimum stay is %d nights.').replace('%d', e.data.limit);
      else if (e?.data?.error === 'max_stay_violation') msg.textContent = (i18n?.maxStay || 'Maximum stay is %d nights.').replace('%d', e.data.limit);
      else if (e?.data?.error === 'guest_capacity_violation') msg.textContent = (i18n?.maxGuests || 'Maximum number of guests is %d.').replace('%d', e.data.limit);
      else if (e?.data?.error === 'captcha_invalid') msg.textContent = i18n?.captchaError || 'Captcha failed.';
      else if (e?.data?.error === 'missing_billing') msg.textContent = i18n?.missingBilling || 'Fill in billing details for selected payment method.';
      else if (e?.data?.error === 'invalid_country') msg.textContent = i18n?.invalidCountry || 'Select a valid country.';
      else if (e?.data?.error === 'robot_detected') msg.textContent = i18n?.robotError || 'Robot detected.';
      else msg.textContent = i18n?.error || 'Error sending.';

      // Append detailed error if available and not one of the above known ones for better UX/Debugging
      if (e?.data?.error && !['date_conflict', 'unit_closed', 'missing_contact', 'missing_billing', 'invalid_country', 'min_stay_violation', 'max_stay_violation', 'guest_capacity_violation', 'captcha_invalid', 'robot_detected'].includes(e.data.error)) {
        msg.textContent += ' (' + e.data.error + ')';
      }
    }
  }

  function showPaymentInstructions(root, res) {
    const i18n = getI18n(root);
    const existing = root.parentNode.querySelector('.vstrb-payment-result');
    if (existing) existing.remove();

    const box = document.createElement('div');
    box.className = 'vstrb-payment-result';

    const title = document.createElement('h3');
    title.textContent = i18n?.paymentInstructionsTitle || 'Payment instructions';
    box.appendChild(title);

    const intro = document.createElement('p');
    intro.textContent = `${i18n?.success || 'Done.'} #${res.booking_id}.`;
    box.appendChild(intro);

    const instructions = document.createElement('div');
    instructions.className = 'vstrb-payment-result-text';
    instructions.textContent = res.payment_instructions || '';
    box.appendChild(instructions);

    if (res.payment_qr_payload) {
      const qr = document.createElement('div');
      qr.className = 'vstrb-payment-qr';
      qr.dataset.vstrbPaymentQr = res.payment_qr_payload;

      const qrTitle = document.createElement('strong');
      qrTitle.textContent = `${i18n?.paymentBank || 'Bank transfer'} (QR)`;
      qr.appendChild(qrTitle);

      const qrCode = document.createElement('div');
      qrCode.className = 'vstrb-payment-qr-code';
      qr.appendChild(qrCode);
      box.appendChild(qr);
    }

    root.parentNode.insertBefore(box, root.nextSibling);
    window.VSTRBPaymentQR?.render(box);
    root.style.display = 'none';
    box.scrollIntoView({ behavior: 'smooth', block: 'center' });
  }

  function initBooking() {
    const paymentReturnState = getPaymentReturnState();
    if (paymentReturnState) {
      cleanupPaymentReturnParams();
    }
    let paymentReturnHandled = false;

    document.querySelectorAll('.vstrb-booking').forEach(async (root) => {
      const i18n = getI18n(root);
      preselectCountryFromPageLanguage(root);
      initCountrySelect(root);
      // Admin: Inject Confirm Checkbox
      if (window.VSTRB?.isAdmin) {
        const btnRow = qs(root, '.vstrb-submit')?.parentNode;
        if (btnRow) {
          const div = document.createElement('div');
          div.className = 'vstrb-row';
          div.style.marginBottom = '10px';
          div.innerHTML = `<label style="background:#ffc;padding:5px;display:inline-block;border:1px solid #ee9"><input type="checkbox" class="vstrb-confirm-now" value="1"> ${i18n?.confirmNow || 'Confirm immediately (Admin)'}</label>`;
          btnRow.parentNode.insertBefore(div, btnRow);
        }
      }

      const unitId = parseInt(root.dataset.unit, 10);
      const box = qs(root, '.vstrb-services-box');
      if (box) await loadServices(box, unitId, root);

      const customBox = qs(root, '.vstrb-custom-fields');
      if (customBox) await loadContactFields(customBox, unitId, root);
      toggleBookingBillingBox(root);

      ['change', 'input'].forEach(ev => {
        root.addEventListener(ev, (e) => {
          if (e.target.matches('.vstrb-guests,.vstrb-children')) enforceGuestCapacity(root, e.target);
          if (e.target.matches('.vstrb-date-from,.vstrb-date-to,.vstrb-guests,.vstrb-children,.vstrb-svc-check')) quote(root);
        });
      });
      root.addEventListener('change', (e) => {
        if (e.target.matches('.vstrb-payment-method')) {
          setTimeout(() => fixCompanyIdAutofill(root), 300);
        }
        if (e.target.matches('.vstrb-book-billing-enabled')) toggleBookingBillingBox(root);
        if (e.target.matches('.vstrb-book-billing-company-id,.vstrb-book-billing-name')) fixCompanyIdAutofill(root);
      });
      root.addEventListener('input', (e) => {
        if (e.target.matches('.vstrb-book-billing-company-id')) fixCompanyIdAutofill(root);
      });

      const btn = qs(root, '.vstrb-submit');
      if (btn) btn.addEventListener('click', (e) => { e.preventDefault(); book(root); });

      if (!paymentReturnHandled && paymentReturnState) {
        paymentReturnHandled = applyPaymentReturnState(root, paymentReturnState);
      }
    });
  }

  function buildMonthGrid(container, month, occupiedSet, ranges = [], dayPrices = {}, calPriceMode = 'off', calCurrency = '', dayPriceKinds = {}, closedSet = new Set(), root = null) {
    const i18n = getI18n(root);
    const locale = getLocale(root);
    const [y, m] = month.split('-').map(n => parseInt(n, 10));
    const first = new Date(y, m - 1, 1);
    const startDow = (first.getDay() + 6) % 7; // 0=Mon
    const daysInMonth = new Date(y, m, 0).getDate();

    const today = new Date();
    today.setHours(0, 0, 0, 0);

    const weekdays = [
      i18n?.mon || 'Mo',
      i18n?.tue || 'Tu',
      i18n?.wed || 'We',
      i18n?.thu || 'Th',
      i18n?.fri || 'Fr',
      i18n?.sat || 'Sa',
      i18n?.sun || 'Su'
    ];

    const monthBlock = document.createElement('div');
    monthBlock.className = 'vstrb-month-block';
    monthBlock.innerHTML = `
      <div class="vstrb-month-title">${fmtMonthTitle(month, locale)}</div>
      <div class="vstrb-cal-grid"></div>
    `;
    const grid = monthBlock.querySelector('.vstrb-cal-grid');

    const cells = [];
    weekdays.forEach(w => cells.push(`<div class="vstrb-cal-wd">${w}</div>`));
    for (let i = 0; i < startDow; i++) cells.push(`<div></div>`);

    for (let d = 1; d <= daysInMonth; d++) {
      const day = `${month}-${String(d).padStart(2, '0')}`;
      const occ = occupiedSet.has(day);
      const closed = closedSet.has(day);
      const isCheckin = ranges.some(r => r.date_from === day);
      const isCheckout = ranges.some(r => r.date_to === day);

      const parts = day.split('-').map(Number);
      const dObj = new Date(parts[0], parts[1] - 1, parts[2]);
      const isPast = dObj < today;

      let cls = 'vstrb-cal-day';
      if (occ) cls += ' occ';
      if (closed) cls += ' vstrb-closed';
      if (isPast) cls += ' past';

      if (isCheckin && isCheckout) cls += ' vstrb-checkout-checkin';
      else if (isCheckin) cls += ' vstrb-checkin';
      else if (isCheckout) cls += ' vstrb-checkout';

      if (!occ && !closed && !isPast) cls += ' free';

      let tag = occ ? (i18n?.occupied || 'occupied') : (isPast ? '' : (i18n?.free || 'available'));
      if (isCheckout && !occ && !isPast) tag = i18n?.checkout || 'checkout';
      if (isCheckin && isCheckout) tag = i18n?.transition || 'transition';
      if (closed) tag = i18n?.closed || 'Closed';

      let priceHtml = '';
      let priceTitle = '';
      if (calPriceMode !== 'off' && !occ && !closed && !isPast && dayPrices[day] !== undefined) {
        const val = dayPrices[day];
        if (calPriceMode === 'price') {
          const kindCls = dayPriceKinds[day] === 'disc' ? ' has-disc' : (dayPriceKinds[day] === 'surch' ? ' has-surch' : '');
          const priceValue = Number(val).toFixed(0);
          let formattedPrice = priceValue.replace(/\B(?=(\d{3})+(?!\d))/g, ' ');
          let currencyLabel = calCurrency;
          try {
            const currencyParts = new Intl.NumberFormat(getLocale(root) || undefined, {
              style: 'currency',
              currency: calCurrency,
              currencyDisplay: 'narrowSymbol',
              minimumFractionDigits: 0,
              maximumFractionDigits: 0
            }).formatToParts(Number(val));
            formattedPrice = currencyParts
              .filter(part => part.type === 'integer' || part.type === 'group')
              .map(part => part.value)
              .join('')
              .replace(/\s/g, ' ');
            currencyLabel = currencyParts.find(part => part.type === 'currency')?.value || calCurrency;
          } catch (e) {}
          priceTitle = `${formattedPrice} ${currencyLabel}`;
          priceHtml = `<div class="price${kindCls}"><span class="vstrb-price-value">${escapeHtml(formattedPrice)}</span><span class="vstrb-price-currency">${escapeHtml(currencyLabel)}</span></div>`;
        } else if (calPriceMode === 'diff') {
          const valNum = Number(val);
          const sign = valNum > 0 ? '+' : '';
          const decimals = Math.abs(valNum % 1) < 0.0001 ? 0 : 1;
          const diffCls = val < 0 ? 'disc' : 'surch';
          priceTitle = `${sign}${valNum.toFixed(decimals)}%`;
          priceHtml = `<div class="price ${diffCls}"><span class="vstrb-price-value">${priceTitle}</span></div>`;
        }
      }

      const priceTitleAttr = priceTitle ? ` data-price="${escapeHtml(priceTitle)}"` : '';

      cells.push(`
        <div class="${cls}" data-day="${day}"${priceTitleAttr}>
          <div class="top-row"><span class="d">${d}</span><span class="tag">${tag}</span></div>
          ${priceHtml}
        </div>
      `);
    }
    grid.innerHTML = cells.join('');
    container.appendChild(monthBlock);
  }

  function initCalendar() {
    document.querySelectorAll('.vstrb-calendar').forEach(async (root) => {
      const i18n = getI18n(root);
      const unitId = parseInt(root.dataset.unit, 10);
      const monthsCount = parseInt(root.dataset.months || '1', 10);
      const requestedGrid = parseInt(root.dataset.grid || '1', 10);
      const gridContainer = qs(root, '.vstrb-cal-grid-container');
      const safeMonths = Number.isFinite(monthsCount) && monthsCount > 0 ? monthsCount : 1;

      let gridCols = Number.isFinite(requestedGrid) && requestedGrid > 0 ? requestedGrid : safeMonths;
      gridCols = Math.min(gridCols, safeMonths);
      let refreshPriceVisibility = () => {};

      if (gridContainer) {
        gridContainer.style.setProperty('--vstrb-grid', gridCols);

        refreshPriceVisibility = () => {
          qsa(gridContainer, '.vstrb-cal-day[data-price]').forEach(cell => {
            const price = qs(cell, '.price');
            if (!price) return;

            const selectionClasses = ['sel', 'inrange'].filter(className => cell.classList.contains(className));
            selectionClasses.forEach(className => cell.classList.remove(className));

            price.style.setProperty('display', 'block', 'important');
            price.style.setProperty('visibility', 'hidden', 'important');
            const cellStyles = window.getComputedStyle(cell);
            const availableWidth = cell.clientWidth
              - (parseFloat(cellStyles.paddingLeft) || 0)
              - (parseFloat(cellStyles.paddingRight) || 0);
            const fits = price.getBoundingClientRect().width <= availableWidth;
            price.style.removeProperty('display');
            price.style.removeProperty('visibility');
            selectionClasses.forEach(className => cell.classList.add(className));
            cell.classList.toggle('vstrb-price-fits', fits);
          });
        };

        const updateResponsiveGrid = () => {
          const minMonthWidth = 280;
          const columnGap = parseFloat(window.getComputedStyle(gridContainer).columnGap) || 0;
          const rootStyles = window.getComputedStyle(root);
          // Read the parent card's content width. The grid itself can already be
          // overflowing, so measuring it here would keep the invalid column count.
          const availableWidth = root.clientWidth
            - (parseFloat(rootStyles.paddingLeft) || 0)
            - (parseFloat(rootStyles.paddingRight) || 0);
          const fittingColumns = Math.max(1, Math.floor((availableWidth + columnGap) / (minMonthWidth + columnGap)));
          gridContainer.style.setProperty('--vstrb-responsive-grid', Math.min(gridCols, fittingColumns));
          window.requestAnimationFrame(refreshPriceVisibility);
        };

        updateResponsiveGrid();
        if ('ResizeObserver' in window) {
          new ResizeObserver(updateResponsiveGrid).observe(root);
        } else {
          window.addEventListener('resize', updateResponsiveGrid);
        }
      }

      if (monthsCount === 1) {
        root.classList.add('vstrb-single-month');
      } else {
        root.classList.remove('vstrb-single-month');
      }

      const selectedLabel = qs(root, '.vstrb-selected-range');
      const bookingRoot = root.closest('.vstrb-booking');
      const prevLabel = qs(root, '.vstrb-prev-label');
      const nextLabel = qs(root, '.vstrb-next-label');

      let cur = new Date();
      let selFrom = null;
      let selTo = null;
      let calendarOccupied = new Set();
      let calendarClosed = new Set();

      const calCurrency = root.dataset.currency || bookingRoot?.dataset?.currency || '';
      const dateFromInput = bookingRoot ? qs(bookingRoot, '.vstrb-date-from') : null;
      const dateToInput = bookingRoot ? qs(bookingRoot, '.vstrb-date-to') : null;
      let repaintSelection = () => {};

      const syncSelectionFromDateInputs = () => {
        const from = dateFromInput?.value || '';
        const to = dateToInput?.value || '';
        if (!from) {
          selFrom = null;
          selTo = null;
          repaintSelection();
          return;
        }

        const rangeEnd = to && to > from ? to : from;
        let hasConflict = false;
        const cursor = new Date(`${from}T00:00:00`);
        const end = new Date(`${rangeEnd}T00:00:00`);
        do {
          const day = `${cursor.getFullYear()}-${String(cursor.getMonth() + 1).padStart(2, '0')}-${String(cursor.getDate()).padStart(2, '0')}`;
          if (calendarOccupied.has(day) || calendarClosed.has(day)) {
            hasConflict = true;
            break;
          }
          cursor.setDate(cursor.getDate() + 1);
        } while (cursor < end);

        if (hasConflict) {
          if (dateToInput) dateToInput.value = '';
          if (dateFromInput) dateFromInput.value = '';
          selFrom = null;
          selTo = null;
          repaintSelection();
          quote(bookingRoot);
          if (i18n?.occupied) alert(i18n.occupied);
          return;
        }

        selFrom = from;
        selTo = to && to > from ? to : null;
        if (dateToInput) dateToInput.min = from;
        repaintSelection();
      };

      dateFromInput?.addEventListener('change', syncSelectionFromDateInputs);
      dateToInput?.addEventListener('change', syncSelectionFromDateInputs);

      async function load() {
        const startMonth = `${cur.getFullYear()}-${String(cur.getMonth() + 1).padStart(2, '0')}`;
        const locale = getLocale(root);

        if (prevLabel) {
          const prevMonthDate = new Date(cur.getFullYear(), cur.getMonth() - 1, 1);
          prevLabel.textContent = fmtMonthName(prevMonthDate, locale);
        }
        if (nextLabel) {
          const nextMonthDate = new Date(cur.getFullYear(), cur.getMonth() + monthsCount, 1);
          nextLabel.textContent = fmtMonthName(nextMonthDate, locale);
        }

        const currencyParam = calCurrency ? `&currency=${encodeURIComponent(calCurrency)}` : '';
        const res = await api({ path: `/vstrb/v1/calendar?unit_id=${unitId}&month=${startMonth}&months=${monthsCount}${currencyParam}` });
        const occupied = new Set(res.occupiedDays || []);
        const closed = new Set(res.closedDays || []);

        calendarOccupied = occupied;
        calendarClosed = closed;
        const ranges = res.ranges || [];
        const dayPrices = res.dayPrices || {};
        const dayPriceKinds = res.dayPriceKinds || {};
        const calPriceMode = res.calPriceMode || 'off';
        const resCurrency = res.currency || calCurrency || 'EUR';
        const minPrice = res.minPrice ?? null;
        const priceModeFull = res.priceMode || '';


        gridContainer.innerHTML = '';
        for (let i = 0; i < monthsCount; i++) {
          const mDate = new Date(cur.getFullYear(), cur.getMonth() + i, 1);
          const monthStr = `${mDate.getFullYear()}-${String(mDate.getMonth() + 1).padStart(2, '0')}`;
          buildMonthGrid(gridContainer, monthStr, occupied, ranges, dayPrices, calPriceMode, resCurrency, dayPriceKinds, closed, root);
        }
        window.requestAnimationFrame(refreshPriceVisibility);

        // Hide main header title if multi-month
        const mainTitle = qs(root, '.vstrb-cal-title');
        if (monthsCount > 1) {
          mainTitle.style.display = 'none';
        } else {
          mainTitle.textContent = fmtMonthTitle(startMonth, locale);
          mainTitle.style.display = '';
        }

        function clearSel() {
          qsa(gridContainer, '.vstrb-cal-day').forEach(el => el.classList.remove('sel', 'inrange'));
        }
        function paintSel() {
          clearSel();
          if (!selFrom) {
            return;
          }
          const from = selFrom;
          const to = selTo;
          qsa(gridContainer, '.vstrb-cal-day').forEach(el => {
            const day = el.dataset.day;
            if (day === from) el.classList.add('sel');
            if (to && day === to) el.classList.add('sel');
            if (to && day > from && day < to) el.classList.add('inrange');
          });
          selectedLabel.textContent = to ? `${from} → ${to}` : `${from} → …`;

          if (bookingRoot) {
            if (dateFromInput) dateFromInput.value = from || '';
            if (dateToInput) {
              dateToInput.min = from || '';
              dateToInput.value = to || '';
            }
            quote(bookingRoot);
          }

          window.requestAnimationFrame(refreshPriceVisibility);

        }
        repaintSelection = paintSel;

        const legend = qs(root, '.vstrb-cal-legend');
        const selected = qs(root, '.vstrb-cal-selected');

        if (legend) {
          const existingPriceModeLabel = qs(legend, '.vstrb-price-mode-label');
          if (existingPriceModeLabel) existingPriceModeLabel.remove();
          if (calPriceMode !== 'off') {
            let labelText = '';
            if (minPrice !== null) {
              const priceStr = `${Number(minPrice).toFixed(0)}\u00a0${escapeHtml(resCurrency)}`;
              const tpl = i18n?.priceFrom?.[priceModeFull] || `from %s`;
              labelText = tpl.replace('%s', priceStr);
            } else {
              labelText = i18n?.priceModes?.[priceModeFull] || '';
            }
            if (labelText) {
              const span = document.createElement('span');
              span.className = 'vstrb-price-mode-label';
              span.textContent = labelText;
              legend.appendChild(span);
            }
          }
        }

        if (!bookingRoot) {
          if (legend) legend.style.display = 'none';
          if (selected) selected.style.display = 'none';
        }

        gridContainer.onclick = (e) => {
          if (!bookingRoot) return;
          const cell = e.target.closest('.vstrb-cal-day');
          if (!cell || cell.classList.contains('past')) return;

          const isOcc = cell.classList.contains('occ');
          const isClosed = cell.classList.contains('vstrb-closed');
          const isCheckin = cell.classList.contains('vstrb-checkin') || cell.classList.contains('vstrb-checkout-checkin');
          const isCheckout = cell.classList.contains('vstrb-checkout') || cell.classList.contains('vstrb-checkout-checkin');
          const day = cell.dataset.day;
          if (isClosed) {
            if (i18n?.occupied) alert(i18n.occupied);
            return;
          }

          // If selecting start date (or reset)
          if (!selFrom || (selFrom && selTo)) {
            if (isOcc && !isCheckout) return;
            selFrom = day; selTo = null; paintSel(); return;
          }

          // Selecting end date: must be after start
          if (day <= selFrom) {
            if (isOcc && !isCheckout) return;
            selFrom = day; selTo = null; paintSel(); return;
          }

          // Rule for end date: can be free or a check-in day for someone else
          if (isOcc && !isCheckin) return;

          // check all days between are free and not past
          const days = [];
          const dParts = selFrom.split('-').map(Number);
          let d = new Date(dParts[0], dParts[1] - 1, dParts[2]);
          const eParts = day.split('-').map(Number);
          const end = new Date(eParts[0], eParts[1] - 1, eParts[2]);

          while (d < end) {
            const s = `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`;
            days.push(s);
            d.setDate(d.getDate() + 1);
          }
          const conflict = days.some(x => occupied.has(x) || closed.has(x));
          if (conflict) {
            if (i18n?.occupied) alert(i18n.occupied);
            selFrom = null; selTo = null; clearSel(); selectedLabel.textContent = ''; return;
          }
          selTo = day; paintSel();
        };

        paintSel();
      }

      qs(root, '.vstrb-prev')?.addEventListener('click', () => { cur.setMonth(cur.getMonth() - 1); load(); });
      qs(root, '.vstrb-next')?.addEventListener('click', () => { cur.setMonth(cur.getMonth() + 1); load(); });

      await load();
    });
  }

  document.addEventListener('DOMContentLoaded', () => {
    initBooking();
    initCalendar();
  });
})();
