(function(){
  const STORAGE_KEY = 'hotel-pineta-lang';
  const DEFAULT_LANG = 'it';
  const SUPPORTED = ['it','en','ru','uk','fr','de'];
  const variant = window.GUEST_PAGE_VARIANT === 'discount' ? 'discount' : 'regular';
  const ROOM_NAMES = {
    it: {"1А":"№ 1A — camera doppia con vista giardino","2А":"№ 2A — camera doppia con vista mare","3А":"№ 3A — camera tripla con vista mare","1":"№ 1 — camera tripla con vista mare","2":"№ 2 — camera doppia con vista mare","3":"№ 3 — camera tripla con vista mare","4":"№ 4 — piccola camera doppia con vista giardino","5":"№ 5 — camera doppia con vista giardino","6":"№ 6 — camera doppia con vista mare","7":"№ 7 — camera doppia con vista mare","8":"№ 8 — camera doppia con vista mare","9":"№ 9 — camera doppia con vista mare","10":"№ 10 — camera tripla con vista mare","11":"№ 11 — piccola camera doppia con vista giardino"},
    en: {"1А":"№ 1A — double room with garden view","2А":"№ 2A — double room with sea view","3А":"№ 3A — triple room with sea view","1":"№ 1 — triple room with sea view","2":"№ 2 — double room with sea view","3":"№ 3 — triple room with sea view","4":"№ 4 — small double room with garden view","5":"№ 5 — double room with garden view","6":"№ 6 — double room with sea view","7":"№ 7 — double room with sea view","8":"№ 8 — double room with sea view","9":"№ 9 — double room with sea view","10":"№ 10 — triple room with sea view","11":"№ 11 — small double room with garden view"},
    ru: {"1А":"№ 1А — двухместный номер с видом на сад","2А":"№ 2А — двухместный номер с видом на море","3А":"№ 3А — трехместный номер с видом на море","1":"№ 1 — трехместный номер с видом на море","2":"№ 2 — двухместный номер с видом на море","3":"№ 3 — трехместный номер с видом на море","4":"№ 4 — небольшой двухместный номер с видом на сад","5":"№ 5 — двухместный номер с видом на сад","6":"№ 6 — двухместный номер с видом на море","7":"№ 7 — двухместный номер с видом на море","8":"№ 8 — двухместный номер с видом на море","9":"№ 9 — двухместный номер с видом на море","10":"№ 10 — трехместный номер с видом на море","11":"№ 11 — небольшой двухместный номер с видом на сад"},
    uk: {"1А":"№ 1А — двомісний номер з видом на сад","2А":"№ 2А — двомісний номер з видом на море","3А":"№ 3А — тримісний номер з видом на море","1":"№ 1 — тримісний номер з видом на море","2":"№ 2 — двомісний номер з видом на море","3":"№ 3 — тримісний номер з видом на море","4":"№ 4 — невеликий двомісний номер з видом на сад","5":"№ 5 — двомісний номер з видом на сад","6":"№ 6 — двомісний номер з видом на море","7":"№ 7 — двомісний номер з видом на море","8":"№ 8 — двомісний номер з видом на море","9":"№ 9 — двомісний номер з видом на море","10":"№ 10 — тримісний номер з видом на море","11":"№ 11 — невеликий двомісний номер з видом на сад"},
    fr: {"1А":"№ 1A — chambre double avec vue sur le jardin","2А":"№ 2A — chambre double avec vue sur la mer","3А":"№ 3A — chambre triple avec vue sur la mer","1":"№ 1 — chambre triple avec vue sur la mer","2":"№ 2 — chambre double avec vue sur la mer","3":"№ 3 — chambre triple avec vue sur la mer","4":"№ 4 — petite chambre double avec vue sur le jardin","5":"№ 5 — chambre double avec vue sur le jardin","6":"№ 6 — chambre double avec vue sur la mer","7":"№ 7 — chambre double avec vue sur la mer","8":"№ 8 — chambre double avec vue sur la mer","9":"№ 9 — chambre double avec vue sur la mer","10":"№ 10 — chambre triple avec vue sur la mer","11":"№ 11 — petite chambre double avec vue sur le jardin"},
    de: {"1А":"№ 1A — Doppelzimmer mit Gartenblick","2А":"№ 2A — Doppelzimmer mit Meerblick","3А":"№ 3A — Dreibettzimmer mit Meerblick","1":"№ 1 — Dreibettzimmer mit Meerblick","2":"№ 2 — Doppelzimmer mit Meerblick","3":"№ 3 — Dreibettzimmer mit Meerblick","4":"№ 4 — kleines Doppelzimmer mit Gartenblick","5":"№ 5 — Doppelzimmer mit Gartenblick","6":"№ 6 — Doppelzimmer mit Meerblick","7":"№ 7 — Doppelzimmer mit Meerblick","8":"№ 8 — Doppelzimmer mit Meerblick","9":"№ 9 — Doppelzimmer mit Meerblick","10":"№ 10 — Dreibettzimmer mit Meerblick","11":"№ 11 — kleines Doppelzimmer mit Gartenblick"}
  };
  const M = {
    it: {
      pageTitleRegular: 'Prenotazione ospite — Hotel Pineta', pageTitleDiscount: 'Prenotazione con sconto — Hotel Pineta',
      topHome: '← Home', topLoginDiscount: 'Accesso con sconto', topRegisterDiscount: 'Registrazione con sconto', topRegular: 'Modulo normale senza sconto', topLogout: 'Esci',
      eyebrowRegular: 'Prenotazione ospite senza sconto', titleRegular: 'Prenotazione dal sito',
      eyebrowDiscount: 'Prenotazione ospite con sconto', titleDiscount: 'Prenotazione con sconto',
      subtext: 'La fonte della prenotazione viene registrata come <strong>Site</strong>. Prezzi e limiti di soggiorno vengono caricati dalla Google Sheet ospiti.', sourceSite: 'Fonte: Site',
      legendFree: 'Libero', legendHeld: 'In attesa di pagamento', legendOccupied: 'Confermato', legendSelected: 'Selezionato',
      chooseRoomTitle: 'Scegli la camera', floor3: 'Terzo piano', floor2: 'Secondo piano', holdNote: 'Il colore giallo indica che la camera è temporaneamente bloccata in attesa del pagamento.', discountNote: 'I prezzi sotto indicati includono già il vostro sconto e i limiti minimi di soggiorno.',
      galleryBase: 'Foto — {room}', galleryClose: 'Chiudi', photoBtn: 'Foto', roomFallback: 'Camera {room}',
      nameLabel: 'Nome e cognome *', phoneLabel: 'Telefono *', emailLabel: 'Email', arrivalLabel: 'Data di arrivo *', roomLabel: 'Camera selezionata *', nightsLabel: 'Notti *', guestsLabel: 'Ospiti *', childrenLabel: 'Bambini fino a 2 anni', breakfastLabel: 'Colazione inclusa', commentLabel: 'Commento dell’ospite', commentPlaceholder: 'Richieste sulla camera, orario di arrivo ecc.',
      guestLabel: 'Ospite', promoLabel: 'Codice promo', discountLabel: 'Sconto', discountValue: 'Fino al 25%', chooseFileButton: 'Seleziona file', noFileChosen: 'Nessun file scelto',
      submitRegular: 'Crea prenotazione preliminare', submitDiscount: 'Crea prenotazione preliminare con sconto', altRegister: 'Registrati e ottieni lo sconto', altRegular: 'Modulo normale senza sconto',
      formLoading: 'Carichiamo prezzi, totale e camere occupate…', submitLoading: 'Stiamo creando la prenotazione preliminare. Attendere il messaggio verde…',
      pricesTitle: 'Prezzi e limiti al giorno', externalRatesTitle: 'Prezzi su altri siti', discountRatesTitle: 'Prezzi con lo sconto Hotel Pineta', priceWB: 'Prezzo con colazione', priceNB: 'Prezzo senza colazione', priceWB3: 'Prezzo con colazione > 3 notti', priceNB3: 'Prezzo senza colazione > 3 notti', priceWBD: 'Prezzo scontato con colazione', priceNBD: 'Prezzo scontato senza colazione', priceWBD3: 'Prezzo scontato con colazione > 3 notti', priceNBD3: 'Prezzo scontato senza colazione > 3 notti', minNights: 'Minimo notti per questa data', tariffHint: 'Evidenziamo la riga da cui viene calcolato il totale.', activeRateHint: 'Il totale è calcolato dalla riga: {label}.', activeBadge: 'Totale',
      totalTitle: 'Totale', prepaymentTitle: 'Acconto:', remainingTitle: 'Saldo all’arrivo:', quoteDefault: 'Compilate data, camera e notti per vedere il calcolo e i limiti.',
      messagesTitle: 'Messaggio per l’ospite', copyMessage: 'Copia messaggio', paymentTitle: 'Dati per il pagamento', copyPayment: 'Copia dati', copied: 'Copiato',
      receiptTitle: 'Caricamento ricevuta di acconto', receiptNote: 'Dopo il pagamento potete caricare sul sito una foto o un PDF della ricevuta. Il file verrà salvato subito nella cartella <strong>Ricevuta</strong>.', reservationNumber: 'Numero prenotazione:', uploadReceipt: 'Carica ricevuta', receiptHelp: 'Formati consentiti: JPG, PNG, WEBP o PDF fino a 8 MB.', receiptLoading: 'Carichiamo la ricevuta nella cartella Ricevuta…', receiptProgress: 'Carichiamo la ricevuta… {percent}%', receiptUploaded: 'Ricevuta caricata.',
      chooseRoomPlaceholder: 'Scegli la camera', chooseDateFirst: 'Prima scegli la data',
      prepaymentToday: 'Per confermare la prenotazione dovete versare l’acconto di {amount} oggi.', prepaymentTodayTomorrow: 'Per confermare la prenotazione dovete versare l’acconto di {amount} oggi o domani.', prepaymentThreeDays: 'Per confermare la prenotazione dovete versare l’acconto di {amount} entro tre giorni.',
      waitingToday: 'La prenotazione preliminare blocca la camera e attende l’acconto oggi.', waitingTodayTomorrow: 'La prenotazione preliminare blocca la camera e attende l’acconto oggi o domani.', waitingThreeDays: 'La prenotazione preliminare blocca la camera e attende l’acconto per 3 giorni.',
      breakfastYes: 'con colazione', breakfastNo: 'senza colazione', hello: 'Buongiorno!', helloName: '{name}, buongiorno!',
      offerRoom: 'Vi proponiamo: {room}.', offerDate: 'Data di arrivo: {date}', offerNights: 'Numero di notti: {nights}', offerGuests: 'Numero di ospiti: {guests}', offerMeal: 'Pasti: {meal}', offerTotal: 'Totale: {total}', offerPrepayment: 'Acconto: {prepayment}', offerTail: 'In caso di cancellazione l’acconto non è rimborsabile. Dopo il pagamento dell’acconto riceverete il messaggio di conferma della prenotazione.',
      paymentHeader: 'Dati per il pagamento della prenotazione preliminare', paymentAmount: 'Importo dell’acconto: {amount}', paymentRecipient: 'Beneficiario: PINETA SRL', paymentBank: 'Banca: BANCO BPM',
      minNightsStatus: 'Minimo notti per la data scelta: {min}.', changeDates: 'Modificate le date o il numero di notti.', fetchFailed: 'Non è stato possibile ottenere i dati dalla Google Sheet ospiti.', retryLater: 'Riprovate tra qualche secondo.',
      holdSuccessStatus: 'La prenotazione preliminare è già stata creata. La camera è temporaneamente bloccata.', paymentAlert: 'Attenzione! I dati per il pagamento anticipato si trovano in basso nella pagina, nel secondo messaggio per l’ospite.',
      sending: 'Invio in corso…', uploading: 'Caricamento…', invalidResponse: 'Il server ha restituito una risposta non valida durante il caricamento della ricevuta.', uploadFailed: 'Non è stato possibile caricare la ricevuta.', sendFailed: 'Non è stato possibile inviare il file al server.', reservationNotFound: 'Numero di prenotazione non trovato per il caricamento della ricevuta.', chooseFileFirst: 'Selezionate prima un file JPG, PNG, WEBP o PDF.'
    },
    en: {
      pageTitleRegular: 'Guest booking — Hotel Pineta', pageTitleDiscount: 'Discount booking — Hotel Pineta',
      topHome: '← Home', topLoginDiscount: 'Login with discount', topRegisterDiscount: 'Register for discount', topRegular: 'Regular form without discount', topLogout: 'Log out',
      eyebrowRegular: 'Guest booking without discount', titleRegular: 'Booking from the website', eyebrowDiscount: 'Guest booking with discount', titleDiscount: 'Discount booking',
      subtext: 'The booking source is recorded as <strong>Site</strong>. Prices and stay restrictions are loaded from the guest Google Sheet.', sourceSite: 'Source: Site',
      legendFree: 'Free', legendHeld: 'Waiting for payment', legendOccupied: 'Confirmed', legendSelected: 'Selected', chooseRoomTitle: 'Choose a room', floor3: 'Third floor', floor2: 'Second floor', holdNote: 'Yellow means the room is temporarily held while waiting for payment.', discountNote: 'The prices below already include your discount and the minimum stay restrictions.',
      galleryBase: 'Photos — {room}', galleryClose: 'Close', photoBtn: 'Photos', roomFallback: 'Room {room}',
      nameLabel: 'Full name *', phoneLabel: 'Phone *', emailLabel: 'Email', arrivalLabel: 'Arrival date *', roomLabel: 'Selected room *', nightsLabel: 'Nights *', guestsLabel: 'Guests *', childrenLabel: 'Children under 2', breakfastLabel: 'Breakfast needed', commentLabel: 'Guest comment', commentPlaceholder: 'Preferences about the room, arrival time, etc.',
      guestLabel: 'Guest', promoLabel: 'Promo code', discountLabel: 'Discount', discountValue: 'Up to 25%', chooseFileButton: 'Choose file', noFileChosen: 'No file chosen',
      submitRegular: 'Create provisional booking', submitDiscount: 'Create discounted provisional booking', altRegister: 'Register and get a discount', altRegular: 'Regular form without discount',
      formLoading: 'Loading prices, total and occupied rooms…', submitLoading: 'Creating the provisional booking. Please wait for the green message…',
      pricesTitle: 'Prices and limits per day', externalRatesTitle: 'Prices on other sites', discountRatesTitle: 'Prices with the Hotel Pineta discount', priceWB: 'Price with breakfast', priceNB: 'Price without breakfast', priceWB3: 'Price with breakfast > 3 nights', priceNB3: 'Price without breakfast > 3 nights', priceWBD: 'Discounted price with breakfast', priceNBD: 'Discounted price without breakfast', priceWBD3: 'Discounted price with breakfast > 3 nights', priceNBD3: 'Discounted price without breakfast > 3 nights', minNights: 'Minimum nights for this date', tariffHint: 'We will highlight the row used for the total.', activeRateHint: 'The total is currently calculated from: {label}.', activeBadge: 'Total', totalTitle: 'Total', prepaymentTitle: 'Prepayment:', remainingTitle: 'Balance on arrival:', quoteDefault: 'Fill in the date, room and nights to see the calculation and restrictions.',
      messagesTitle: 'Message for the guest', copyMessage: 'Copy message', paymentTitle: 'Payment details', copyPayment: 'Copy payment details', copied: 'Copied',
      receiptTitle: 'Upload prepayment receipt', receiptNote: 'After payment you can upload a photo or PDF of the receipt directly on the website. The file will be saved immediately in the <strong>Ricevuta</strong> folder.', reservationNumber: 'Booking number:', uploadReceipt: 'Upload receipt', receiptHelp: 'Allowed files: JPG, PNG, WEBP or PDF up to 8 MB.', receiptLoading: 'Uploading the receipt to the Ricevuta folder…', receiptProgress: 'Uploading the receipt… {percent}%', receiptUploaded: 'Receipt uploaded.',
      chooseRoomPlaceholder: 'Choose a room', chooseDateFirst: 'First choose a date', prepaymentToday: 'To secure the booking you need to pay the prepayment of {amount} today.', prepaymentTodayTomorrow: 'To secure the booking you need to pay the prepayment of {amount} today or tomorrow.', prepaymentThreeDays: 'To secure the booking you need to pay the prepayment of {amount} within three days.', waitingToday: 'The provisional booking holds the room and waits for the prepayment today.', waitingTodayTomorrow: 'The provisional booking holds the room and waits for the prepayment today or tomorrow.', waitingThreeDays: 'The provisional booking holds the room and waits for the prepayment for 3 days.', breakfastYes: 'with breakfast', breakfastNo: 'without breakfast', hello: 'Hello!', helloName: 'Hello, {name}!', offerRoom: 'We offer you: {room}.', offerDate: 'Arrival date: {date}', offerNights: 'Number of nights: {nights}', offerGuests: 'Number of guests: {guests}', offerMeal: 'Meal plan: {meal}', offerTotal: 'Total: {total}', offerPrepayment: 'Prepayment: {prepayment}', offerTail: 'In case of cancellation, the prepayment is non-refundable. After the prepayment is paid, you will receive a booking confirmation message.', paymentHeader: 'Payment details for the provisional booking', paymentAmount: 'Prepayment amount: {amount}', paymentRecipient: 'Recipient: PINETA SRL', paymentBank: 'Bank: BANCO BPM', minNightsStatus: 'Minimum nights for the selected date: {min}.', changeDates: 'Change the dates or number of nights.', fetchFailed: 'Could not load data from the guest Google Sheet.', retryLater: 'Please try again in a few seconds.', holdSuccessStatus: 'The provisional booking has already been created. The room is temporarily held.', paymentAlert: 'Attention! The prepayment details are shown at the bottom of the page in the second guest message.', sending: 'Sending…', uploading: 'Uploading…', invalidResponse: 'The server returned an invalid response while uploading the receipt.', uploadFailed: 'Could not upload the receipt.', sendFailed: 'Could not send the file to the server.', reservationNotFound: 'Booking number for the receipt upload was not found.', chooseFileFirst: 'First choose a JPG, PNG, WEBP or PDF file.'
    },
    ru: {
      pageTitleRegular: 'Гостевое бронирование — Hotel Pineta', pageTitleDiscount: 'Бронирование со скидкой — Hotel Pineta',
      topHome: '← На главную', topLoginDiscount: 'Вход со скидкой', topRegisterDiscount: 'Регистрация со скидкой', topRegular: 'Обычная форма без скидки', topLogout: 'Выйти', eyebrowRegular: 'Гостевое бронирование без скидки', titleRegular: 'Бронирование с сайта', eyebrowDiscount: 'Гостевое бронирование со скидкой', titleDiscount: 'Бронирование со скидкой', subtext: 'Источник бронирования фиксируется как <strong>Site</strong>. Цены и ограничения по дням подгружаются из гостевой Google Таблицы.', sourceSite: 'Источник: Site', legendFree: 'Свободно', legendHeld: 'Ждём оплату', legendOccupied: 'Подтверждено', legendSelected: 'Выбрано', chooseRoomTitle: 'Выберите номер', floor3: 'Третий этаж', floor2: 'Второй этаж', holdNote: 'Жёлтый цвет означает, что номер уже удержан до получения оплаты.', discountNote: 'Цены ниже уже учитывают вашу скидку и ограничения по минимальному сроку проживания.', galleryBase: 'Фотографии — {room}', galleryClose: 'Закрыть', photoBtn: 'Фото', roomFallback: 'Комната {room}', nameLabel: 'Имя и фамилия *', phoneLabel: 'Телефон *', emailLabel: 'Email', arrivalLabel: 'Дата заезда *', roomLabel: 'Выбранный номер *', nightsLabel: 'Ночей *', guestsLabel: 'Гостей *', childrenLabel: 'Дети до 2 лет', breakfastLabel: 'Нужен завтрак', commentLabel: 'Комментарий гостя', commentPlaceholder: 'Пожелания по номеру, времени прибытия и т.п.', guestLabel: 'Гость', promoLabel: 'Промокод', discountLabel: 'Скидка', discountValue: 'До 25%', chooseFileButton: 'Выберите файл', noFileChosen: 'Файл не выбран', submitRegular: 'Создать предварительное бронирование', submitDiscount: 'Создать предварительное бронирование со скидкой', altRegister: 'Зарегистрироваться и получить скидку', altRegular: 'Обычная форма без скидки', formLoading: 'Загружаем цены, итог и занятые номера…', submitLoading: 'Создаём предварительное бронирование. Пожалуйста, дождитесь зелёного сообщения…', pricesTitle: 'Стоимость и ограничения за день', externalRatesTitle: 'Стоимость на других сайтах', discountRatesTitle: 'Стоимость со скидкой Hotel Pineta', priceWB: 'Цена с завтраком', priceNB: 'Цена без завтрака', priceWB3: 'Цена с завтраком > 3 дней', priceNB3: 'Цена без завтрака > 3 дней', priceWBD: 'Цена с завтраком со скидкой', priceNBD: 'Цена без завтрака со скидкой', priceWBD3: 'Цена с завтраком со скидкой > 3 дней', priceNBD3: 'Цена без завтрака со скидкой > 3 дней', minNights: 'Минимум дней на эту дату', tariffHint: 'Подсветим строку, из которой сейчас рассчитывается итог.', activeRateHint: 'Итог сейчас берётся из строки: {label}.', activeBadge: 'Итог', totalTitle: 'Итого', prepaymentTitle: 'Предоплата:', remainingTitle: 'Остаток при заезде:', quoteDefault: 'Заполните дату, номер и количество ночей, чтобы увидеть расчёт и ограничения.', messagesTitle: 'Сообщение гостю', copyMessage: 'Скопировать сообщение', paymentTitle: 'Реквизиты для оплаты', copyPayment: 'Скопировать реквизиты', copied: 'Скопировано', receiptTitle: 'Загрузка квитанции о предоплате', receiptNote: 'После оплаты Вы можете загрузить фото или PDF квитанции прямо на сайт. Файл сразу попадёт в папку <strong>Ricevuta</strong>.', reservationNumber: 'Номер бронирования:', uploadReceipt: 'Загрузить квитанцию', receiptHelp: 'Допустимые файлы: JPG, PNG, WEBP или PDF до 8 МБ.', receiptLoading: 'Загружаем квитанцию в папку Ricevuta…', receiptProgress: 'Загружаем квитанцию… {percent}%', receiptUploaded: 'Квитанция загружена.', chooseRoomPlaceholder: 'Выберите номер', chooseDateFirst: 'Сначала выберите дату', prepaymentToday: 'Для бронирования Вы должны сделать предоплату {amount} сегодня.', prepaymentTodayTomorrow: 'Для бронирования Вы должны сделать предоплату {amount} сегодня или завтра.', prepaymentThreeDays: 'Для бронирования Вы должны сделать предоплату {amount} в течение трёх дней.', waitingToday: 'Предварительная бронь удерживает номер и ждёт предоплату сегодня.', waitingTodayTomorrow: 'Предварительная бронь удерживает номер и ждёт предоплату сегодня или завтра.', waitingThreeDays: 'Предварительная бронь удерживает номер и ждёт предоплату в течение 3 дней.', breakfastYes: 'с завтраком', breakfastNo: 'без завтрака', hello: 'Здравствуйте!', helloName: '{name}, здравствуйте!', offerRoom: 'Предлагаем Вам: {room}.', offerDate: 'Дата заезда: {date}', offerNights: 'Количество ночей: {nights}', offerGuests: 'Количество гостей: {guests}', offerMeal: 'Питание: {meal}', offerTotal: 'Итого: {total}', offerPrepayment: 'Предоплата: {prepayment}', offerTail: 'В случае отмены бронирования предоплата не возвращается. После оплаты предоплаты Вы получите сообщение о подтверждении бронирования.', paymentHeader: 'Реквизиты для оплаты предварительного бронирования', paymentAmount: 'Сумма предоплаты: {amount}', paymentRecipient: 'Получатель: PINETA SRL', paymentBank: 'Банк: BANCO BPM', minNightsStatus: 'Минимум дней на выбранную дату: {min}.', changeDates: 'Измените даты или количество ночей.', fetchFailed: 'Не удалось получить данные из гостевой таблицы.', retryLater: 'Повторите попытку через несколько секунд.', holdSuccessStatus: 'Предварительное бронирование уже создано. Номер временно удержан.', paymentAlert: 'Внимание! Реквизиты для предоплаты находятся внизу страницы во втором гостевом сообщении.', sending: 'Отправляем…', uploading: 'Загружаем…', invalidResponse: 'Сервер вернул некорректный ответ при загрузке квитанции.', uploadFailed: 'Не удалось загрузить квитанцию.', sendFailed: 'Не удалось отправить файл на сервер.', reservationNotFound: 'Не найден номер бронирования для загрузки квитанции.', chooseFileFirst: 'Сначала выберите файл JPG, PNG, WEBP или PDF.'
    },
    uk: {
      pageTitleRegular: 'Гостьове бронювання — Hotel Pineta', pageTitleDiscount: 'Бронювання зі знижкою — Hotel Pineta', topHome: '← На головну', topLoginDiscount: 'Вхід зі знижкою', topRegisterDiscount: 'Реєстрація зі знижкою', topRegular: 'Звичайна форма без знижки', topLogout: 'Вийти', eyebrowRegular: 'Гостьове бронювання без знижки', titleRegular: 'Бронювання з сайту', eyebrowDiscount: 'Гостьове бронювання зі знижкою', titleDiscount: 'Бронювання зі знижкою', subtext: 'Джерело бронювання фіксується як <strong>Site</strong>. Ціни та обмеження за кількістю днів завантажуються з гостьової Google Таблиці.', sourceSite: 'Джерело: Site', legendFree: 'Вільно', legendHeld: 'Чекаємо оплату', legendOccupied: 'Підтверджено', legendSelected: 'Обрано', chooseRoomTitle: 'Оберіть номер', floor3: 'Третій поверх', floor2: 'Другий поверх', holdNote: 'Жовтий колір означає, що номер уже утримується до отримання оплати.', discountNote: 'Ціни нижче вже враховують вашу знижку та мінімальний строк проживання.', galleryBase: 'Фотографії — {room}', galleryClose: 'Закрити', photoBtn: 'Фото', roomFallback: 'Кімната {room}', nameLabel: 'Ім’я та прізвище *', phoneLabel: 'Телефон *', emailLabel: 'Email', arrivalLabel: 'Дата заїзду *', roomLabel: 'Обраний номер *', nightsLabel: 'Ночей *', guestsLabel: 'Гостей *', childrenLabel: 'Діти до 2 років', breakfastLabel: 'Потрібен сніданок', commentLabel: 'Коментар гостя', commentPlaceholder: 'Побажання щодо номера, часу прибуття тощо.', guestLabel: 'Гість', promoLabel: 'Промокод', discountLabel: 'Знижка', discountValue: 'До 25%', chooseFileButton: 'Виберіть файл', noFileChosen: 'Файл не вибрано', submitRegular: 'Створити попереднє бронювання', submitDiscount: 'Створити попереднє бронювання зі знижкою', altRegister: 'Зареєструватися та отримати знижку', altRegular: 'Звичайна форма без знижки', formLoading: 'Завантажуємо ціни, підсумок і зайняті номери…', submitLoading: 'Створюємо попереднє бронювання. Будь ласка, дочекайтеся зеленого повідомлення…', pricesTitle: 'Вартість і обмеження за день', externalRatesTitle: 'Вартість на інших сайтах', discountRatesTitle: 'Вартість зі знижкою Hotel Pineta', priceWB: 'Ціна зі сніданком', priceNB: 'Ціна без сніданку', priceWB3: 'Ціна зі сніданком > 3 днів', priceNB3: 'Ціна без сніданку > 3 днів', priceWBD: 'Ціна зі знижкою зі сніданком', priceNBD: 'Ціна зі знижкою без сніданку', priceWBD3: 'Ціна зі знижкою зі сніданком > 3 днів', priceNBD3: 'Ціна зі знижкою без сніданку > 3 днів', minNights: 'Мінімум днів на цю дату', tariffHint: 'Підсвітимо рядок, з якого зараз рахується підсумок.', activeRateHint: 'Підсумок зараз береться з рядка: {label}.', activeBadge: 'Підсумок', totalTitle: 'Усього', prepaymentTitle: 'Передоплата:', remainingTitle: 'Залишок при заїзді:', quoteDefault: 'Заповніть дату, номер і кількість ночей, щоб побачити розрахунок та обмеження.', messagesTitle: 'Повідомлення гостю', copyMessage: 'Скопіювати повідомлення', paymentTitle: 'Реквізити для оплати', copyPayment: 'Скопіювати реквізити', copied: 'Скопійовано', receiptTitle: 'Завантаження квитанції про передоплату', receiptNote: 'Після оплати Ви можете завантажити фото або PDF квитанції прямо на сайт. Файл одразу потрапить у папку <strong>Ricevuta</strong>.', reservationNumber: 'Номер бронювання:', uploadReceipt: 'Завантажити квитанцію', receiptHelp: 'Дозволені файли: JPG, PNG, WEBP або PDF до 8 МБ.', receiptLoading: 'Завантажуємо квитанцію в папку Ricevuta…', receiptProgress: 'Завантажуємо квитанцію… {percent}%', receiptUploaded: 'Квитанцію завантажено.', chooseRoomPlaceholder: 'Оберіть номер', chooseDateFirst: 'Спочатку оберіть дату', prepaymentToday: 'Для бронювання Ви повинні зробити передоплату {amount} сьогодні.', prepaymentTodayTomorrow: 'Для бронювання Ви повинні зробити передоплату {amount} сьогодні або завтра.', prepaymentThreeDays: 'Для бронювання Ви повинні зробити передоплату {amount} протягом трьох днів.', waitingToday: 'Попередня бронь утримує номер і чекає на передоплату сьогодні.', waitingTodayTomorrow: 'Попередня бронь утримує номер і чекає на передоплату сьогодні або завтра.', waitingThreeDays: 'Попередня бронь утримує номер і чекає на передоплату протягом 3 днів.', breakfastYes: 'зі сніданком', breakfastNo: 'без сніданку', hello: 'Вітаємо!', helloName: '{name}, вітаємо!', offerRoom: 'Пропонуємо Вам номер: {room}.', offerDate: 'Дата заїзду: {date}', offerNights: 'Кількість ночей: {nights}', offerGuests: 'Кількість гостей: {guests}', offerMeal: 'Харчування: {meal}', offerTotal: 'Усього: {total}', offerPrepayment: 'Передоплата: {prepayment}', offerTail: 'У разі скасування бронювання передоплата не повертається. Після оплати передоплати Ви отримаєте повідомлення про підтвердження бронювання.', paymentHeader: 'Реквізити для оплати попереднього бронювання', paymentAmount: 'Сума передоплати: {amount}', paymentRecipient: 'Отримувач: PINETA SRL', paymentBank: 'Банк: BANCO BPM', minNightsStatus: 'Мінімум днів на обрану дату: {min}.', changeDates: 'Змініть дати або кількість ночей.', fetchFailed: 'Не вдалося отримати дані з гостьової таблиці.', retryLater: 'Повторіть спробу через кілька секунд.', holdSuccessStatus: 'Попереднє бронювання вже створено. Номер тимчасово утримується.', paymentAlert: 'Увага! Реквізити для передоплати знаходяться внизу сторінки у другому гостьовому повідомленні.', sending: 'Надсилаємо…', uploading: 'Завантажуємо…', invalidResponse: 'Сервер повернув некоректну відповідь під час завантаження квитанції.', uploadFailed: 'Не вдалося завантажити квитанцію.', sendFailed: 'Не вдалося надіслати файл на сервер.', reservationNotFound: 'Не знайдено номер бронювання для завантаження квитанції.', chooseFileFirst: 'Спочатку оберіть файл JPG, PNG, WEBP або PDF.'
    },
    fr: {
      pageTitleRegular: 'Réservation client — Hotel Pineta', pageTitleDiscount: 'Réservation avec réduction — Hotel Pineta', topHome: '← Accueil', topLoginDiscount: 'Connexion avec réduction', topRegisterDiscount: 'Inscription avec réduction', topRegular: 'Formulaire normal sans réduction', topLogout: 'Déconnexion', eyebrowRegular: 'Réservation client sans réduction', titleRegular: 'Réservation depuis le site', eyebrowDiscount: 'Réservation client avec réduction', titleDiscount: 'Réservation avec réduction', subtext: 'La source de réservation est enregistrée comme <strong>Site</strong>. Les prix et les restrictions de séjour sont chargés depuis la feuille Google des clients.', sourceSite: 'Source : Site', legendFree: 'Libre', legendHeld: 'En attente du paiement', legendOccupied: 'Confirmé', legendSelected: 'Sélectionné', chooseRoomTitle: 'Choisissez une chambre', floor3: 'Troisième étage', floor2: 'Deuxième étage', holdNote: 'La couleur jaune signifie que la chambre est temporairement bloquée en attente du paiement.', discountNote: 'Les prix ci-dessous incluent déjà votre réduction et les restrictions de séjour minimum.', galleryBase: 'Photos — {room}', galleryClose: 'Fermer', photoBtn: 'Photos', roomFallback: 'Chambre {room}', nameLabel: 'Nom et prénom *', phoneLabel: 'Téléphone *', emailLabel: 'Email', arrivalLabel: 'Date d’arrivée *', roomLabel: 'Chambre choisie *', nightsLabel: 'Nuits *', guestsLabel: 'Clients *', childrenLabel: 'Enfants de moins de 2 ans', breakfastLabel: 'Petit-déjeuner souhaité', commentLabel: 'Commentaire du client', commentPlaceholder: 'Préférences de chambre, heure d’arrivée, etc.', guestLabel: 'Client', promoLabel: 'Code promo', discountLabel: 'Réduction', discountValue: 'Jusqu’à 25 %', chooseFileButton: 'Choisir un fichier', noFileChosen: 'Aucun fichier choisi', submitRegular: 'Créer une pré-réservation', submitDiscount: 'Créer une pré-réservation avec réduction', altRegister: 'S’inscrire et obtenir une réduction', altRegular: 'Formulaire normal sans réduction', formLoading: 'Chargement des prix, du total et des chambres occupées…', submitLoading: 'Création de la pré-réservation. Veuillez attendre le message vert…', pricesTitle: 'Prix et limites par jour', externalRatesTitle: 'Prix sur les autres sites', discountRatesTitle: 'Prix avec la remise Hotel Pineta', priceWB: 'Prix avec petit-déjeuner', priceNB: 'Prix sans petit-déjeuner', priceWB3: 'Prix avec petit-déjeuner > 3 nuits', priceNB3: 'Prix sans petit-déjeuner > 3 nuits', priceWBD: 'Prix réduit avec petit-déjeuner', priceNBD: 'Prix réduit sans petit-déjeuner', priceWBD3: 'Prix réduit avec petit-déjeuner > 3 nuits', priceNBD3: 'Prix réduit sans petit-déjeuner > 3 nuits', minNights: 'Nombre minimum de nuits pour cette date', tariffHint: 'Nous mettons en évidence la ligne utilisée pour le total.', activeRateHint: 'Le total est actuellement calculé depuis la ligne : {label}.', activeBadge: 'Total', totalTitle: 'Total', prepaymentTitle: 'Acompte :', remainingTitle: 'Solde à l’arrivée :', quoteDefault: 'Remplissez la date, la chambre et le nombre de nuits pour voir le calcul et les restrictions.', messagesTitle: 'Message pour le client', copyMessage: 'Copier le message', paymentTitle: 'Coordonnées de paiement', copyPayment: 'Copier les coordonnées', copied: 'Copié', receiptTitle: 'Téléchargement du reçu d’acompte', receiptNote: 'Après le paiement vous pouvez télécharger une photo ou un PDF du reçu directement sur le site. Le fichier sera enregistré immédiatement dans le dossier <strong>Ricevuta</strong>.', reservationNumber: 'Numéro de réservation :', uploadReceipt: 'Télécharger le reçu', receiptHelp: 'Fichiers autorisés : JPG, PNG, WEBP ou PDF jusqu’à 8 Mo.', receiptLoading: 'Téléchargement du reçu dans le dossier Ricevuta…', receiptProgress: 'Téléchargement du reçu… {percent}%', receiptUploaded: 'Reçu téléchargé.', chooseRoomPlaceholder: 'Choisissez une chambre', chooseDateFirst: 'Choisissez d’abord une date', prepaymentToday: 'Pour réserver, vous devez payer l’acompte de {amount} aujourd’hui.', prepaymentTodayTomorrow: 'Pour réserver, vous devez payer l’acompte de {amount} aujourd’hui ou demain.', prepaymentThreeDays: 'Pour réserver, vous devez payer l’acompte de {amount} dans les trois jours.', waitingToday: 'La pré-réservation bloque la chambre et attend l’acompte aujourd’hui.', waitingTodayTomorrow: 'La pré-réservation bloque la chambre et attend l’acompte aujourd’hui ou demain.', waitingThreeDays: 'La pré-réservation bloque la chambre et attend l’acompte pendant 3 jours.', breakfastYes: 'avec petit-déjeuner', breakfastNo: 'sans petit-déjeuner', hello: 'Bonjour !', helloName: '{name}, bonjour !', offerRoom: 'Nous vous proposons : {room}.', offerDate: 'Date d’arrivée : {date}', offerNights: 'Nombre de nuits : {nights}', offerGuests: 'Nombre de clients : {guests}', offerMeal: 'Repas : {meal}', offerTotal: 'Total : {total}', offerPrepayment: 'Acompte : {prepayment}', offerTail: 'En cas d’annulation, l’acompte n’est pas remboursable. Après le paiement de l’acompte, vous recevrez le message de confirmation de la réservation.', paymentHeader: 'Coordonnées pour le paiement de la pré-réservation', paymentAmount: 'Montant de l’acompte : {amount}', paymentRecipient: 'Bénéficiaire : PINETA SRL', paymentBank: 'Banque : BANCO BPM', minNightsStatus: 'Nombre minimum de nuits pour la date choisie : {min}.', changeDates: 'Modifiez les dates ou le nombre de nuits.', fetchFailed: 'Impossible de charger les données depuis la feuille Google des clients.', retryLater: 'Réessayez dans quelques secondes.', holdSuccessStatus: 'La pré-réservation a déjà été créée. La chambre est temporairement bloquée.', paymentAlert: 'Attention ! Les coordonnées pour le prépaiement se trouvent en bas de la page, dans le deuxième message client.', sending: 'Envoi…', uploading: 'Téléchargement…', invalidResponse: 'Le serveur a renvoyé une réponse invalide lors du téléchargement du reçu.', uploadFailed: 'Impossible de télécharger le reçu.', sendFailed: 'Impossible d’envoyer le fichier au serveur.', reservationNotFound: 'Numéro de réservation introuvable pour télécharger le reçu.', chooseFileFirst: 'Choisissez d’abord un fichier JPG, PNG, WEBP ou PDF.'
    },
    de: {
      pageTitleRegular: 'Gästebuchung — Hotel Pineta', pageTitleDiscount: 'Buchung mit Rabatt — Hotel Pineta', topHome: '← Startseite', topLoginDiscount: 'Anmeldung mit Rabatt', topRegisterDiscount: 'Registrierung mit Rabatt', topRegular: 'Normales Formular ohne Rabatt', topLogout: 'Abmelden', eyebrowRegular: 'Gästebuchung ohne Rabatt', titleRegular: 'Buchung über die Website', eyebrowDiscount: 'Gästebuchung mit Rabatt', titleDiscount: 'Buchung mit Rabatt', subtext: 'Die Buchungsquelle wird als <strong>Site</strong> gespeichert. Preise und Mindestaufenthaltsregeln werden aus der Gäste-Google-Tabelle geladen.', sourceSite: 'Quelle: Site', legendFree: 'Frei', legendHeld: 'Warten auf Zahlung', legendOccupied: 'Bestätigt', legendSelected: 'Ausgewählt', chooseRoomTitle: 'Zimmer auswählen', floor3: 'Dritter Stock', floor2: 'Zweiter Stock', holdNote: 'Gelb bedeutet, dass das Zimmer vorübergehend bis zum Zahlungseingang reserviert ist.', discountNote: 'Die unten stehenden Preise berücksichtigen bereits Ihren Rabatt und die Mindestaufenthaltsregeln.', galleryBase: 'Fotos — {room}', galleryClose: 'Schließen', photoBtn: 'Fotos', roomFallback: 'Zimmer {room}', nameLabel: 'Vor- und Nachname *', phoneLabel: 'Telefon *', emailLabel: 'E-Mail', arrivalLabel: 'Anreisedatum *', roomLabel: 'Ausgewähltes Zimmer *', nightsLabel: 'Nächte *', guestsLabel: 'Gäste *', childrenLabel: 'Kinder unter 2 Jahren', breakfastLabel: 'Frühstück gewünscht', commentLabel: 'Kommentar des Gastes', commentPlaceholder: 'Wünsche zum Zimmer, zur Ankunftszeit usw.', guestLabel: 'Gast', promoLabel: 'Promocode', discountLabel: 'Rabatt', discountValue: 'Bis zu 25 %', chooseFileButton: 'Datei auswählen', noFileChosen: 'Keine Datei ausgewählt', submitRegular: 'Vorläufige Buchung erstellen', submitDiscount: 'Vorläufige Buchung mit Rabatt erstellen', altRegister: 'Registrieren und Rabatt erhalten', altRegular: 'Normales Formular ohne Rabatt', formLoading: 'Preise, Gesamtbetrag und belegte Zimmer werden geladen…', submitLoading: 'Die vorläufige Buchung wird erstellt. Bitte warten Sie auf die grüne Meldung…', pricesTitle: 'Preise und Grenzen pro Tag', externalRatesTitle: 'Preise auf anderen Websites', discountRatesTitle: 'Preise mit Hotel-Pineta-Rabatt', priceWB: 'Preis mit Frühstück', priceNB: 'Preis ohne Frühstück', priceWB3: 'Preis mit Frühstück > 3 Nächte', priceNB3: 'Preis ohne Frühstück > 3 Nächte', priceWBD: 'Rabattpreis mit Frühstück', priceNBD: 'Rabattpreis ohne Frühstück', priceWBD3: 'Rabattpreis mit Frühstück > 3 Nächte', priceNBD3: 'Rabattpreis ohne Frühstück > 3 Nächte', minNights: 'Mindestnächte für dieses Datum', tariffHint: 'Wir markieren die Zeile, aus der der Gesamtbetrag berechnet wird.', activeRateHint: 'Der Gesamtbetrag wird derzeit aus folgender Zeile berechnet: {label}.', activeBadge: 'Gesamt', totalTitle: 'Gesamt', prepaymentTitle: 'Anzahlung:', remainingTitle: 'Restbetrag bei Anreise:', quoteDefault: 'Geben Sie Datum, Zimmer und Nächte ein, um die Berechnung und die Einschränkungen zu sehen.', messagesTitle: 'Nachricht an den Gast', copyMessage: 'Nachricht kopieren', paymentTitle: 'Zahlungsdaten', copyPayment: 'Zahlungsdaten kopieren', copied: 'Kopiert', receiptTitle: 'Anzahlungsbeleg hochladen', receiptNote: 'Nach der Zahlung können Sie ein Foto oder PDF des Belegs direkt auf die Website hochladen. Die Datei wird sofort im Ordner <strong>Ricevuta</strong> gespeichert.', reservationNumber: 'Buchungsnummer:', uploadReceipt: 'Beleg hochladen', receiptHelp: 'Erlaubte Dateien: JPG, PNG, WEBP oder PDF bis 8 MB.', receiptLoading: 'Beleg wird in den Ordner Ricevuta hochgeladen…', receiptProgress: 'Beleg wird hochgeladen… {percent}%', receiptUploaded: 'Beleg hochgeladen.', chooseRoomPlaceholder: 'Zimmer auswählen', chooseDateFirst: 'Zuerst ein Datum auswählen', prepaymentToday: 'Für die Buchung müssen Sie die Anzahlung von {amount} heute leisten.', prepaymentTodayTomorrow: 'Für die Buchung müssen Sie die Anzahlung von {amount} heute oder morgen leisten.', prepaymentThreeDays: 'Für die Buchung müssen Sie die Anzahlung von {amount} innerhalb von drei Tagen leisten.', waitingToday: 'Die vorläufige Buchung blockiert das Zimmer und wartet heute auf die Anzahlung.', waitingTodayTomorrow: 'Die vorläufige Buchung blockiert das Zimmer und wartet heute oder morgen auf die Anzahlung.', waitingThreeDays: 'Die vorläufige Buchung blockiert das Zimmer und wartet 3 Tage auf die Anzahlung.', breakfastYes: 'mit Frühstück', breakfastNo: 'ohne Frühstück', hello: 'Guten Tag!', helloName: 'Guten Tag, {name}!', offerRoom: 'Wir bieten Ihnen an: {room}.', offerDate: 'Anreisedatum: {date}', offerNights: 'Anzahl der Nächte: {nights}', offerGuests: 'Anzahl der Gäste: {guests}', offerMeal: 'Verpflegung: {meal}', offerTotal: 'Gesamt: {total}', offerPrepayment: 'Anzahlung: {prepayment}', offerTail: 'Im Falle einer Stornierung wird die Anzahlung nicht zurückerstattet. Nach Zahlung der Anzahlung erhalten Sie eine Bestätigung Ihrer Buchung.', paymentHeader: 'Zahlungsdaten für die vorläufige Buchung', paymentAmount: 'Anzahlungsbetrag: {amount}', paymentRecipient: 'Empfänger: PINETA SRL', paymentBank: 'Bank: BANCO BPM', minNightsStatus: 'Mindestnächte für das ausgewählte Datum: {min}.', changeDates: 'Ändern Sie die Daten oder die Anzahl der Nächte.', fetchFailed: 'Daten aus der Gäste-Google-Tabelle konnten nicht geladen werden.', retryLater: 'Bitte versuchen Sie es in einigen Sekunden erneut.', holdSuccessStatus: 'Die vorläufige Buchung wurde bereits erstellt. Das Zimmer ist vorübergehend reserviert.', paymentAlert: 'Achtung! Die Angaben für die Anzahlung finden Sie unten auf der Seite in der zweiten Gästemitteilung.', sending: 'Senden…', uploading: 'Hochladen…', invalidResponse: 'Der Server hat beim Hochladen des Belegs eine ungültige Antwort zurückgegeben.', uploadFailed: 'Der Beleg konnte nicht hochgeladen werden.', sendFailed: 'Die Datei konnte nicht an den Server gesendet werden.', reservationNotFound: 'Für den Beleg-Upload wurde keine Buchungsnummer gefunden.', chooseFileFirst: 'Bitte wählen Sie zuerst eine JPG-, PNG-, WEBP- oder PDF-Datei aus.'
    }
  };

  function normalizeLang(lang) {
    const raw = String(lang || '').trim().toLowerCase().split('-')[0];
    if (raw === 'ua') return 'uk';
    return SUPPORTED.includes(raw) ? raw : DEFAULT_LANG;
  }
  function detectInitialLang() {
    try {
      const stored = localStorage.getItem(STORAGE_KEY);
      if (stored) return normalizeLang(stored);
    } catch (_e) {}
    return normalizeLang(document.documentElement.lang || navigator.language || DEFAULT_LANG);
  }
  let currentLang = detectInitialLang();
  function msg(key, vars) {
    let value = (M[currentLang] && M[currentLang][key]) || (M[DEFAULT_LANG] && M[DEFAULT_LANG][key]) || key;
    if (vars) Object.keys(vars).forEach(k => value = String(value).replace(new RegExp('\{'+k+'\}','g'), String(vars[k])));
    return value;
  }
  function q(sel) { return document.querySelector(sel); }
  function qa(sel) { return Array.from(document.querySelectorAll(sel)); }
  function setText(sel, value) { const el=q(sel); if (el) el.textContent = value; }
  function setHtml(sel, value) { const el=q(sel); if (el) el.innerHTML = value; }
  function setPlaceholder(sel, value) { const el=q(sel); if (el) el.setAttribute('placeholder', value); }
  function updateReceiptFileName() {
    const input = q('#receiptFileInput');
    const name = q('#receiptFileName');
    if (!name) return;
    name.textContent = input && input.files && input.files.length ? input.files[0].name : msg('noFileChosen');
  }
  function roomTextParts(code) {
    const label = ((ROOM_NAMES[currentLang]||ROOM_NAMES[DEFAULT_LANG]||{})[code]) || `№ ${code}`;
    const parts = String(label).split('—');
    return {full: label, desc: (parts[1] || '').trim()};
  }
  function updateDynamicRooms() {
    qa('.room-card').forEach(card => {
      const nameEl = card.querySelector('.room-name');
      const descEl = card.querySelector('.room-desc');
      const match = nameEl ? nameEl.textContent.match(/№\s*([^\s]+)/) : null;
      if (!match) return;
      const code = match[1].replace('A','А');
      const rt = roomTextParts(code);
      if (descEl && rt.desc) descEl.textContent = rt.desc;
    });
    qa('.room-photo-btn').forEach(btn => btn.textContent = msg('photoBtn'));
    qa('#roomSelect option').forEach(opt => {
      const code = opt.dataset.code || '';
      if (!code) { if (opt.value === '') opt.textContent = msg('chooseRoomPlaceholder'); return; }
      opt.textContent = roomTextParts(code).full;
    });
  }
  function createdData() { return (window.PinetaBookingFormConfig && window.PinetaBookingFormConfig.createdBookingData) || null; }
  function formatMoney(value) { const n = Number(value || 0); return isFinite(n) ? `${n.toFixed(2)} €` : '—'; }
  function formatDateLocalized(iso) {
    if (!iso) return '—';
    const m = String(iso).match(/^(\d{4})-(\d{2})-(\d{2})$/);
    if (!m) return iso;
    const dt = new Date(Number(m[1]), Number(m[2]) - 1, Number(m[3]));
    const localeMap = {it:'it-IT', en:'en-GB', ru:'ru-RU', uk:'uk-UA', fr:'fr-FR', de:'de-DE'};
    return new Intl.DateTimeFormat(localeMap[currentLang] || 'it-IT').format(dt);
  }
  function timingKey(arrivalDate) {
    const today = new Date(); today.setHours(0,0,0,0);
    const m = String(arrivalDate || '').match(/^(\d{4})-(\d{2})-(\d{2})$/);
    if (!m) return 'three';
    const arrival = new Date(Number(m[1]), Number(m[2]) - 1, Number(m[3])); arrival.setHours(0,0,0,0);
    const diff = Math.round((arrival.getTime() - today.getTime()) / 86400000);
    if (diff <= 1) return 'today';
    if (diff === 2) return 'todayTomorrow';
    return 'three';
  }
  function prepaymentTimingLine(arrivalDate, amountText, deadlineDays) {
    if (deadlineDays !== null && deadlineDays !== undefined && deadlineDays !== '') {
      const days = Math.max(1, parseInt(deadlineDays, 10) || 1);
      const redDays = `🔴 ${days}`;
      if (currentLang === 'uk') return `Щоб підтвердити бронювання, потрібно внести передоплату ${amountText} протягом ${redDays} дн.`;
      if (currentLang === 'it') return `Per confermare la prenotazione dovete versare l’acconto di ${amountText} entro ${redDays} giorni.`;
      if (currentLang === 'en') return `To secure the booking you need to pay the prepayment of ${amountText} within ${redDays} day(s).`;
      if (currentLang === 'fr') return `Pour réserver, vous devez payer l’acompte de ${amountText} dans un délai de ${redDays} jour(s).`;
      if (currentLang === 'de') return `Für die Buchung müssen Sie die Anzahlung von ${amountText} innerhalb von ${redDays} Tag(en) leisten.`;
      return `Для бронирования Вы должны сделать предоплату ${amountText} в течение ${redDays} дн.`;
    }
    const key = timingKey(arrivalDate);
    if (key === 'today') return msg('prepaymentToday', {amount: amountText});
    if (key === 'todayTomorrow') return msg('prepaymentTodayTomorrow', {amount: amountText});
    return msg('prepaymentThreeDays', {amount: amountText});
  }

  const offerRoomPrefixTexts = {
    it: 'Vi proponiamo: {room}.',
    en: 'We offer you: {room}.',
    ru: 'Предлагаем Вам: {room}.',
    uk: 'Пропонуємо Вам: {room}.',
    fr: 'Nous vous proposons : {room}.',
    de: 'Wir bieten Ihnen an: {room}.'
  };
  function normalizeOfferRoomCode(value) {
    const text = String(value || '').replace(/A/g, 'А').replace(/a/g, 'а').replace(/\s+/g, ' ').trim();
    const match = text.match(/(?:№\s*)?([0-9]{1,2}\s*[Аа]?)/u);
    return match ? match[1].replace(/\s+/g, '').toUpperCase().replace(/A/g, 'А') : '';
  }
  function offerRoomFullLabel(room) {
    const code = normalizeOfferRoomCode(room);
    const labels = ROOM_NAMES[currentLang] || ROOM_NAMES[DEFAULT_LANG] || {};
    return (code && labels[code]) ? labels[code] : String(room || '—');
  }
  function offerRoomLine(room) {
    const label = offerRoomFullLabel(room);
    const template = offerRoomPrefixTexts[currentLang] || offerRoomPrefixTexts.ru;
    return template.replace('{room}', label);
  }
  function buildOfferMessage(data) {
    const rawName = variant === 'discount' ? (window.guestName || '') : String((data && data.guest_name) || '').trim();
    const room = (data && data.room_label) || '—';
    const date = formatDateLocalized(data && data.arrival_date);
    const nights = (data && data.nights) || '—';
    const guests = (data && data.guests_count) || '—';
    const meal = data && data.breakfast ? msg('breakfastYes') : msg('breakfastNo');
    const total = formatMoney(data && data.full_amount);
    const prep = formatMoney(data && data.prepayment_amount);
    const greeting = rawName ? msg('helloName', {name: rawName}) : msg('hello');
    return `${greeting}

${offerRoomLine(room)}
${msg('offerDate', {date})}
${msg('offerNights', {nights})}
${msg('offerGuests', {guests})}
${msg('offerMeal', {meal})}
${msg('offerTotal', {total})}
${msg('offerPrepayment', {prepayment: prep})}

${prepaymentTimingLine(data && data.arrival_date, prep, data && data.payment_deadline_days)} ${msg('offerTail')}`;
  }
  function buildPaymentMessage(data) {
    const prep = formatMoney(data && data.prepayment_amount);
    return `${msg('paymentHeader')}

${msg('paymentAmount', {amount: prep})}
${msg('paymentRecipient')}
${msg('paymentBank')}
Codice Iban: IT25T0503449251000000007146
Codice SWIFT: BAPPIT21B7C`;
  }
  function updateFlashMessages() {
    qa('.alert').forEach(alert => {
      const txt = alert.textContent.trim();
      if (/Предварительное бронирование создано|prenotazione preliminare|provisional booking|préliminaire|vorläufige/i.test(txt)) {
        alert.textContent = msg('holdSuccessStatus');
      }
    });
  }
  function applyLanguage() {
    document.documentElement.lang = currentLang;
    try { localStorage.setItem(STORAGE_KEY, currentLang); } catch (_e) {}
    qa('.lang-btn').forEach(btn => { const active = btn.dataset.lang === currentLang; btn.classList.toggle('active', active); btn.setAttribute('aria-pressed', active ? 'true' : 'false'); });
    const bookingLang = document.getElementById('bookingLangInput'); if (bookingLang) bookingLang.value = currentLang;
    const guestLang = document.getElementById('guestLang'); if (guestLang && guestLang.value !== currentLang) { guestLang.value = currentLang; guestLang.dispatchEvent(new Event('change', {bubbles:true})); }
    const receiptLang = document.getElementById('receiptLangInput'); if (receiptLang) receiptLang.value = currentLang;
    document.title = variant === 'discount' ? msg('pageTitleDiscount') : msg('pageTitleRegular');

    const links = qa('.top-actions a');
    if (links[0]) links[0].textContent = msg('topHome');
    if (variant === 'discount') {
      if (links[1]) links[1].textContent = msg('topRegular');
      if (links[2]) links[2].textContent = msg('topRegular').includes('norm') ? msg('topRegular') : msg('topRegular');
      const logoutBtn = q('.top-actions form button'); if (logoutBtn) logoutBtn.textContent = msg('topLogout');
      if (links[2]) links[2].textContent = msg('topLogout') === links[2].textContent ? links[2].textContent : (links[2].textContent || msg('topLogout'));
      if (links[2] && links[2].href && links[2].href.includes('/bookings')) links[2].textContent = msg('copyMessage') ? (M[currentLang].topBookings || links[2].textContent) : links[2].textContent;
    } else {
      if (links[1]) links[1].textContent = msg('topLoginDiscount');
      if (links[2]) links[2].textContent = msg('topRegisterDiscount');
      if (links[3]) links[3].textContent = (M[currentLang].topRooms || 'Rooms');
    }
    // explicit fallback texts for guest top links
    if (variant === 'discount') {
      if (links[1]) links[1].textContent = {it:'Modulo normale senza sconto',en:'Regular form without discount',ru:'Обычная форма без скидки',uk:'Звичайна форма без знижки',fr:'Formulaire normal sans remise',de:'Normales Formular ohne Rabatt'}[currentLang];
      if (links[2]) links[2].textContent = {it:'Le mie prenotazioni',en:'My bookings',ru:'Мои брони',uk:'Мої бронювання',fr:'Mes réservations',de:'Meine Buchungen'}[currentLang];
    } else {
      if (links[3]) links[3].textContent = {it:'Camere',en:'Rooms',ru:'Номера',uk:'Номери',fr:'Chambres',de:'Zimmer'}[currentLang];
    }

    setText('.page-grid > div:first-child > .card.layout-block .eyebrow', variant === 'discount' ? msg('eyebrowDiscount') : msg('eyebrowRegular'));
    setText('.page-grid > div:first-child > .card.layout-block h1', variant === 'discount' ? msg('titleDiscount') : msg('titleRegular'));
    setHtml('.page-grid > div:first-child > .card.layout-block .subtext', msg('subtext'));
    setText('.page-grid > div:first-child > .card.layout-block .source-pill', msg('sourceSite'));
    const legend = qa('.legend .legend-item');
    if (legend[0]) legend[0].lastChild.textContent = msg('legendFree');
    if (legend[1]) legend[1].lastChild.textContent = msg('legendHeld');
    if (legend[2]) legend[2].lastChild.textContent = msg('legendOccupied');
    if (legend[3]) legend[3].lastChild.textContent = msg('legendSelected');
    const titles = qa('.layout-title');
    if (titles[0]) titles[0].textContent = msg('chooseRoomTitle');
    const floors = qa('.floor-title'); if (floors[0]) floors[0].textContent = msg('floor3'); if (floors[1]) floors[1].textContent = msg('floor2');
    setText('.note-line', variant === 'discount' ? msg('discountNote') : msg('holdNote'));
    setText('#roomGalleryClose', msg('galleryClose'));

    const isDiscount = variant === 'discount';
    if (!isDiscount) {
      setText('#guestBookingForm .col-6:nth-of-type(1) label', msg('nameLabel'));
      setText('#guestBookingForm .col-6:nth-of-type(2) label', msg('phoneLabel'));
      setText('#guestBookingForm .col-6:nth-of-type(3) label', msg('emailLabel'));
    } else {
      const labels = qa('#guestBookingDiscountForm > .col-6 label');
      if (labels[0]) labels[0].textContent = msg('guestLabel');
      if (labels[1]) labels[1].textContent = msg('phoneLabel');
      if (labels[2]) labels[2].textContent = msg('emailLabel');
      const strongs = qa('.discount-box strong');
      if (strongs[0]) strongs[0].textContent = msg('guestLabel');
      if (strongs[1]) strongs[1].textContent = msg('discountLabel');
      const discountValueEl = q('.discount-percent-value'); if (discountValueEl) discountValueEl.textContent = msg('discountValue');
      if (strongs[2]) strongs[2].textContent = msg('promoLabel');
    }
    setText('label[for="arrivalDate"]', msg('arrivalLabel'));
    const roomLabelEl = q('#roomSelect')?.closest('.col-6')?.querySelector('label'); if (roomLabelEl) roomLabelEl.textContent = msg('roomLabel');
    const nightsLabelEl = q('#nightsInput')?.closest('.col-3')?.querySelector('label'); if (nightsLabelEl) nightsLabelEl.textContent = msg('nightsLabel');
    const guestsLabelEl = q('#guestsCount')?.closest('.col-3')?.querySelector('label'); if (guestsLabelEl) guestsLabelEl.textContent = msg('guestsLabel');
    const childrenSpan = q('input[name="children_under_2"]')?.closest('label')?.querySelector('span'); if (childrenSpan) childrenSpan.textContent = msg('childrenLabel');
    const breakfastSpan = q('input[name="breakfast"]')?.closest('label')?.querySelector('span'); if (breakfastSpan) breakfastSpan.textContent = msg('breakfastLabel');
    const commentWrap = q('textarea[name="comment_guest"]')?.closest('.col-12'); if (commentWrap) { const label=commentWrap.querySelector('label'); if(label) label.textContent = msg('commentLabel'); }
    setPlaceholder('textarea[name="comment_guest"]', msg('commentPlaceholder'));
    const formButtons = qa('.form-actions a, .form-actions button');
    if (formButtons[0]) formButtons[0].textContent = isDiscount ? msg('submitDiscount') : msg('submitRegular');
    if (formButtons[1]) formButtons[1].textContent = isDiscount ? {it:'Le mie prenotazioni',en:'My bookings',ru:'Мои брони',uk:'Мої бронювання',fr:'Mes réservations',de:'Meine Buchungen'}[currentLang] : msg('altRegister');
    setText('#formLoadingIndicator .loading-text', msg('formLoading'));
    setText('#submitLoadingIndicator .loading-text', msg('submitLoading'));

    const priceLabels = [
      ['#priceLineWB strong', msg('priceWB')],
      ['#priceLineNB strong', msg('priceNB')],
      ['#priceLineWB3 strong', msg('priceWB3')],
      ['#priceLineNB3 strong', msg('priceNB3')],
      ['#priceLineWBD strong', msg('priceWBD')],
      ['#priceLineNBD strong', msg('priceNBD')],
      ['#priceLineWBD3 strong', msg('priceWBD3')],
      ['#priceLineNBD3 strong', msg('priceNBD3')]
    ];
    priceLabels.forEach(([sel,txt]) => setText(sel, txt));
    setText('#externalRatesTitle', msg('externalRatesTitle'));
    setText('#discountRatesTitle', msg('discountRatesTitle'));
    const minStrong = q('#minNightsValue')?.closest('.price-line')?.querySelector('strong'); if (minStrong) minStrong.textContent = msg('minNights');
    setText('#tariffHint', msg('tariffHint'));
    setText('.quote-label', msg('totalTitle'));
    const quoteRows = qa('.quote-list > div');
    if (quoteRows[0]) quoteRows[0].childNodes[0].textContent = {it:'Notti: ',en:'Nights: ',ru:'Ночей: ',uk:'Ночей: ',fr:'Nuits : ',de:'Nächte: '}[currentLang];
    if (quoteRows[1]) quoteRows[1].childNodes[0].textContent = msg('prepaymentTitle') + ' ';
    if (quoteRows[2]) quoteRows[2].childNodes[0].textContent = msg('remainingTitle') + ' ';
    const qWarn = q('#quoteWarning'); if (qWarn && !qWarn.textContent.trim()) qWarn.textContent = msg('quoteDefault');
    const rightTitles = qa('.page-grid > div:last-child .layout-block .layout-title');
    if (rightTitles[0]) rightTitles[0].textContent = msg('pricesTitle');
    if (rightTitles[1]) rightTitles[1].textContent = {it:'Primo messaggio per l’ospite',en:'First guest message',ru:'Первое гостевое сообщение',uk:'Перше гостьове повідомлення',fr:'Premier message invité',de:'Erste Gästemitteilung'}[currentLang];
    if (rightTitles[2]) rightTitles[2].textContent = msg('paymentTitle');
    if (rightTitles[3]) rightTitles[3].textContent = msg('receiptTitle');
    setText('#copyOfferBtn', msg('copyMessage'));
    setText('#copyPaymentBtn', msg('copyPayment'));
    setHtml('.receipt-upload-note', msg('receiptNote'));
    const meta = q('.receipt-upload-meta'); if (meta) meta.childNodes[0].textContent = msg('reservationNumber') + ' ';
    setText('#receiptUploadBtn', msg('uploadReceipt'));
    setText('.receipt-upload-help', msg('receiptHelp'));
    setText('#receiptUploadLoadingText', msg('receiptLoading'));
    setText('#receiptFileButton', msg('chooseFileButton'));
    updateReceiptFileName();

    updateFlashMessages();
    updateDynamicRooms();
    document.dispatchEvent(new Event('pineta:guest-language-changed'));
    const data = createdData();
    if (data) {
      const offer = q('#offerMessage'); if (offer) offer.value = buildOfferMessage(data);
      const pay = q('#paymentDetailsMessage'); if (pay) pay.value = buildPaymentMessage(data);
    }
  }
  document.addEventListener('pineta:booking-form-updated', updateDynamicRooms);
  q('#receiptFileInput')?.addEventListener('change', updateReceiptFileName);
  applyLanguage();
})();