{"id":22227,"date":"2024-11-18T16:00:00","date_gmt":"2024-11-18T15:00:00","guid":{"rendered":"https:\/\/telemedi.com\/?page_id=22227"},"modified":"2026-06-24T15:59:48","modified_gmt":"2026-06-24T13:59:48","slug":"formularz-refundacji","status":"publish","type":"page","link":"https:\/\/telemedi.com\/pl\/formularz-refundacji\/","title":{"rendered":"Formularz refundacji"},"content":{"rendered":"\n<script>\n\twindow.TELEMEDI_API_BASE_URL = 'https:\/\/ims-backend.tmdi00.com';\n<\/script>\n\n\n\n<script>\n    if (!window.TELEMEDI_API_BASE_URL) {\n        document.addEventListener('DOMContentLoaded', function() {\n            document.getElementById('form__wrapper').style.display = 'none';\n            document.getElementById('unavailablePage').classList.add('show');\n        });\n    }\n<\/script>\n\n\n\n\n<noscript>\n    <div class=\"bg-red-600 text-white p-4 text-center font-bold mb-4\">\n        Uwaga! Ta strona wymaga w\u0142\u0105czonego JavaScript, aby dzia\u0142a\u0107 poprawnie. W\u0142\u0105cz obs\u0142ug\u0119 JavaScript w przegl\u0105darce i od\u015bwie\u017c stron\u0119.\n    <\/div>\n<\/noscript>\n\n<!-- Customowy modal dla komunikat\u00f3w -->\n<div id=\"customModal\" class=\"custom-modal\">\n    <div class=\"modal-content\">\n        <div class=\"modal-header\">\n            <div id=\"modalIcon\" class=\"modal-icon\"><\/div>\n            <h3 id=\"modalTitle\" class=\"modal-title\"><\/h3>\n        <\/div>\n        <div class=\"modal-body\">\n            <div id=\"modalMessage\" class=\"modal-message\"><\/div>\n        <\/div>\n        <div class=\"modal-footer\">\n            <button id=\"modalCloseBtn\" class=\"modal-close-btn\">Zamknij<\/button>\n        <\/div>\n    <\/div>\n<\/div>\n\n<!-- Modal potwierdzenia wys\u0142ania -->\n<div id=\"confirmModal\" class=\"custom-modal\">\n    <div class=\"modal-content\">\n        <div class=\"confirm-modal-body\">\n            <h3>Sprawd\u017a wniosek przed wys\u0142aniem<\/h3>\n            <p>Upewnij si\u0119, \u017ce wszystkie dane s\u0105 poprawne.<\/p>\n            <div class=\"confirm-modal-note confirm-modal-note--warn\">\n                <p class=\"confirm-note-title\">B\u0142\u0119dne dane wyd\u0142u\u017c\u0105 weryfikacj\u0119<\/p>\n                <p>Nieprawid\u0142owe lub niekompletne informacje (np. dane g\u0142\u00f3wnego ubezpieczonego, dane wsp\u00f3\u0142ubezpieczonego, adres, dane plac\u00f3wki, dane \u015bwiadczenia) mog\u0105 znacznie op\u00f3\u017ani\u0107 rozpatrzenie wniosku.<\/p>\n            <\/div>\n            <div class=\"confirm-modal-note confirm-modal-note--danger\">\n                <p class=\"confirm-note-title\">Nieprawid\u0142owy produkt<\/p>\n                <p>Z\u0142y wyb\u00f3r produktu obj\u0119tego refundacj\u0105 mo\u017ce wi\u0105za\u0107 si\u0119 z konieczno\u015bci\u0105 z\u0142o\u017cenia wniosku od nowa. Upewnij si\u0119, \u017ce wybrany produkt jest zgodny z prawd\u0105.<\/p>\n            <\/div>\n            <div id=\"confirmModalVerifyWarning\" class=\"confirm-modal-note confirm-modal-note--warn hidden\">\n                <p class=\"confirm-note-title\">Wniosek nie zosta\u0142 w pe\u0142ni zweryfikowany<\/p>\n                <p>Niekt\u00f3re \u015bwiadczenia wymagaj\u0105 poprawy lub nie zosta\u0142y zweryfikowane. Wniosek mo\u017ce zosta\u0107 odrzucony. Mo\u017cesz wr\u00f3ci\u0107 i poprawi\u0107 dokumenty lub wys\u0142a\u0107 go w takiej formie na w\u0142asn\u0105 odpowiedzialno\u015b\u0107.<\/p>\n            <\/div>\n        <\/div>\n        <div class=\"confirm-modal-footer\">\n            <button type=\"button\" id=\"confirmBackBtn\" class=\"confirm-btn confirm-btn-back\">Wr\u00f3\u0107 i sprawd\u017a<\/button>\n            <button type=\"button\" id=\"confirmSubmitBtn\" class=\"confirm-btn confirm-btn-submit\">Tak, wysy\u0142am wniosek<\/button>\n        <\/div>\n    <\/div>\n<\/div>\n\n<!-- Modal weryfikacji - widoczny w trakcie wywo\u0142ania \/api\/refund\/verify -->\n<div id=\"verifyProgressModal\" class=\"custom-modal\">\n    <div class=\"modal-content\">\n        <div class=\"modal-header\">\n            <h3 class=\"modal-title\">Weryfikujemy dokumenty<\/h3>\n        <\/div>\n        <div class=\"modal-body\">\n            <div class=\"flex flex-col items-center gap-4 py-2\">\n                <span class=\"spinner-border show\" style=\"color:#3b82f6; width:2.5rem; height:2.5rem; border-width:0.3em;\"><\/span>\n                <p id=\"verifyProgressText\" class=\"text-center text-gray-700\">Przesy\u0142anie dokument\u00f3w&#8230;<\/p>\n            <\/div>\n        <\/div>\n    <\/div>\n<\/div>\n\n<!-- Modal potwierdzenia usuni\u0119cia \u015bwiadczenia -->\n<div id=\"deleteServiceModal\" class=\"custom-modal\">\n    <div class=\"modal-content\">\n        <div class=\"confirm-modal-body\">\n            <h3>Usun\u0105\u0107 to \u015bwiadczenie?<\/h3>\n            <p>Wszystkie dane wprowadzone w tym \u015bwiadczeniu (kategoria, daty, kwota, faktura) zostan\u0105 usuni\u0119te. Tej operacji nie mo\u017cna cofn\u0105\u0107.<\/p>\n        <\/div>\n        <div class=\"confirm-modal-footer\">\n            <button type=\"button\" id=\"deleteServiceCancelBtn\" class=\"confirm-btn confirm-btn-back\">Anuluj<\/button>\n            <button type=\"button\" id=\"deleteServiceConfirmBtn\" class=\"confirm-btn confirm-btn-danger\">Tak, usu\u0144<\/button>\n        <\/div>\n    <\/div>\n<\/div>\n\n<!-- Strona niedost\u0119pna (gdy brak konfiguracji API) -->\n<div id=\"unavailablePage\" class=\"unavailable-page\">\n    <div class=\"unavailable-card\">\n        <h1 class=\"unavailable-page-title\">Wniosek o Refundacj\u0119<\/h1>\n        <div class=\"unavailable-icon\">\n            <svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" fill=\"none\" viewBox=\"0 0 24 24\" stroke-width=\"1.5\" stroke=\"currentColor\">\n                <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M12 9v3.75m9-.75a9 9 0 1 1-18 0 9 9 0 0 1 18 0Zm-9 3.75h.008v.008H12v-.008Z\" \/>\n            <\/svg>\n        <\/div>\n        <h2>Strona chwilowo niedost\u0119pna<\/h2>\n        <p>Formularz refundacji jest w tej chwili niedost\u0119pny. Przepraszamy za utrudnienia.<\/p>\n        <div class=\"unavailable-divider\"><\/div>\n        <p class=\"unavailable-hint\">Spr\u00f3buj ponownie za kilka minut.<\/p>\n    <\/div>\n<\/div>\n\n<div id=\"form__wrapper\" class=\"p-8 flex justify-center min-h-screen\">\n    <form method=\"POST\" enctype=\"multipart\/form-data\" class=\"w-full max-w-3xl\">\n        <div class=\"flex justify-center mb-6\">\n            <img decoding=\"async\" src=\"https:\/\/telemedi.com\/wp-content\/themes\/telemedico-home-page\/src\/icons\/telemedi.svg\"\n                 alt=\"Telemedi Logo\"\n                 class=\"h-12\">\n        <\/div>\n\n        <h1 class=\"text-2xl font-bold mb-4 text-center\">Wniosek o Refundacj\u0119<\/h1>\n\n        <p class=\"text-gray-700 mb-6 text-center\">\n            Zwrot koszt\u00f3w dokonywany jest na podstawie faktur VAT lub rachunk\u00f3w wystawionych na osob\u0119 fizyczn\u0105 (osob\u0119 uprawnion\u0105 korzystaj\u0105c\u0105 ze \u015bwiadcze\u0144), wraz ze specyfikacj\u0105 wykonanych \u015bwiadcze\u0144 zawieraj\u0105c\u0105 nazw\u0119 us\u0142ugi, ilo\u015b\u0107 i cen\u0119 jednostkow\u0105 us\u0142ugi.\n            <br><br>\n            Nie dokonujemy zwrot\u00f3w na podstawie paragon\u00f3w fiskalnych.\n        <\/p>\n\n        <section class=\"form-section\" data-section-index=\"1\">\n            <h2 class=\"form-section-title has-subtitle\">Dane osoby uprawnionej do Refundacji *<\/h2>\n            <p class=\"form-section-subtitle\">Osoba, kt\u00f3ra skorzysta\u0142a z us\u0142ug<\/p>\n            <div class=\"form-grid-2\">\n                <div>\n                    <label for=\"firstName\" class=\"block text-gray-700\">Imi\u0119 *<\/label>\n                    <input type=\"text\" id=\"firstName\" name=\"firstName\" autocomplete=\"given-name\" maxlength=\"50\" class=\"block w-full border border-gray-300 rounded-md p-2\" required>\n                <\/div>\n                <div>\n                    <label for=\"lastName\" class=\"block text-gray-700\">Nazwisko *<\/label>\n                    <input type=\"text\" id=\"lastName\" name=\"lastName\" autocomplete=\"family-name\" maxlength=\"50\" class=\"block w-full border border-gray-300 rounded-md p-2\" required>\n                <\/div>\n                <div>\n                    <label for=\"pesel\" class=\"block text-gray-700\">PESEL *<\/label>\n                    <input type=\"text\" id=\"pesel\" name=\"pesel\" autocomplete=\"off\" maxlength=\"11\" class=\"block w-full border border-gray-300 rounded-md p-2\" required>\n                    <p id=\"peselError\" class=\"text-red-500 text-sm mt-1 hidden\">Wprowadzony numer PESEL jest nieprawid\u0142owy.<\/p>\n                    <p id=\"peselSameError\" class=\"text-red-500 text-sm mt-1 hidden\">PESEL osoby uprawnionej i g\u0142\u00f3wnego ubezpieczonego nie mog\u0105 by\u0107 identyczne dla opcji &#8222;Inna osoba&#8221;.<\/p>\n                <\/div>\n                <div>\n                    <label for=\"birthDate\" class=\"block text-gray-700\">Data urodzenia *<\/label>\n                    <input type=\"date\" id=\"birthDate\" name=\"birthDate\" autocomplete=\"bday\" class=\"block w-full border border-gray-300 rounded-md p-2\" required>\n                    <p id=\"birthDateError\" class=\"text-red-500 text-sm mt-1 hidden\">Data urodzenia nie mo\u017ce by\u0107 z przysz\u0142o\u015bci.<\/p>\n                <\/div>\n                <div>\n                    <label for=\"contactNumber\" class=\"block text-gray-700\">Numer kontaktowy *<\/label>\n                    <input type=\"tel\" id=\"contactNumber\" name=\"contactNumber\" autocomplete=\"tel\" class=\"block w-full border border-gray-300 rounded-md p-2\" value=\"+48\" required>\n                    <p id=\"phoneError\" class=\"text-red-500 text-sm mt-1 hidden\">Wprowad\u017a poprawny numer telefonu w formacie +48XXXXXXXXX.<\/p>\n                <\/div>\n                <div>\n                    <label for=\"email\" class=\"block text-gray-700\">Adres e-mail do korespondencji *<\/label>\n                    <input type=\"email\" id=\"email\" name=\"email\" autocomplete=\"email\" class=\"block w-full border border-gray-300 rounded-md p-2\" required>\n                <\/div>\n            <\/div>\n\n            <div class=\"mb-4\">\n                <label for=\"insuredPerson\" class=\"block text-gray-700\">Kogo dotyczy zg\u0142oszenie? *<\/label>\n                <select id=\"insuredPerson\" name=\"insuredPerson\" class=\"block w-full border border-gray-300 rounded-md p-2\" required>\n                    <option value=\"G\u0142\u00f3wny ubezpieczony (posiadacz polisy)\">G\u0142\u00f3wny ubezpieczony (posiadacz polisy)<\/option>\n                    <option value=\"Inna osoba\">Inna osoba<\/option>\n                <\/select>\n            <\/div>\n            <div id=\"relationshipDegreeContainer\" class=\"mb-4 hidden\">\n                <label for=\"relationshipDegree\" class=\"block text-gray-700\">Stopie\u0144 pokrewie\u0144stwa<\/label>\n                <input type=\"text\" id=\"relationshipDegree\" name=\"relationshipDegree\" autocomplete=\"off\" maxlength=\"50\" class=\"block w-full border border-gray-300 rounded-md p-2\">\n            <\/div>\n\n            <div>\n                <label for=\"insuredPesel\" class=\"block text-gray-700\">PESEL g\u0142\u00f3wnego ubezpieczonego *<\/label>\n                <input type=\"text\" id=\"insuredPesel\" name=\"insuredPesel\" autocomplete=\"off\" maxlength=\"11\" class=\"block w-full border border-gray-300 rounded-md p-2\" required>\n                <p id=\"insuredPeselError\" class=\"text-red-500 text-sm mt-1 hidden\">Wprowadzony numer PESEL jest nieprawid\u0142owy.<\/p>\n                <p id=\"insuredPeselSameError\" class=\"text-red-500 text-sm mt-1 hidden\">PESEL g\u0142\u00f3wnego ubezpieczonego i osoby uprawnionej nie mog\u0105 by\u0107 identyczne dla opcji &#8222;Inna osoba&#8221;.<\/p>\n            <\/div>\n        <\/section>\n\n        <section class=\"form-section\" data-section-index=\"2\">\n            <h2 class=\"form-section-title\">Adres do korespondencji *<\/h2>\n            <div class=\"form-grid-2\" style=\"margin-bottom: 0;\">\n                <div>\n                    <label for=\"street\" class=\"block text-gray-700\">Ulica *<\/label>\n                    <input type=\"text\" id=\"street\" name=\"street\" autocomplete=\"street-address\" maxlength=\"100\" class=\"block w-full border border-gray-300 rounded-md p-2\" required>\n                <\/div>\n                <div class=\"form-grid-2-inner\">\n                    <div>\n                        <label for=\"houseNumber\" class=\"block text-gray-700\">Nr domu *<\/label>\n                        <input type=\"text\" id=\"houseNumber\" name=\"houseNumber\" autocomplete=\"address-line2\" maxlength=\"10\" class=\"block w-full border border-gray-300 rounded-md p-2\" required>\n                    <\/div>\n                    <div>\n                        <label for=\"apartmentNumber\" class=\"block text-gray-700\">Nr lokalu<\/label>\n                        <input type=\"text\" id=\"apartmentNumber\" name=\"apartmentNumber\" autocomplete=\"address-line3\" maxlength=\"10\" class=\"block w-full border border-gray-300 rounded-md p-2\">\n                    <\/div>\n                <\/div>\n                <div>\n                    <label for=\"postalCode\" class=\"block text-gray-700\">Kod pocztowy *<\/label>\n                    <input type=\"text\" id=\"postalCode\" name=\"postalCode\" autocomplete=\"postal-code\" class=\"block w-full border border-gray-300 rounded-md p-2\" required>\n                    <p id=\"postalCodeError\" class=\"text-red-500 text-sm mt-1 hidden\">Wprowad\u017a poprawny kod pocztowy w formacie 00-000.<\/p>\n                <\/div>\n                <div>\n                    <label for=\"city\" class=\"block text-gray-700\">Miejscowo\u015b\u0107 *<\/label>\n                    <input type=\"text\" id=\"city\" name=\"city\" autocomplete=\"address-level2\" maxlength=\"100\" class=\"block w-full border border-gray-300 rounded-md p-2\" required>\n                <\/div>\n            <\/div>\n        <\/section>\n\n        <section class=\"form-section\" data-section-index=\"3\">\n            <h2 class=\"form-section-title\">Dane do wyp\u0142aty *<\/h2>\n            <div class=\"mb-4\">\n                <label for=\"bank\" class=\"block text-gray-700\">Bank\/Oddzia\u0142 *<\/label>\n                <input type=\"text\" id=\"bank\" name=\"bank\" autocomplete=\"off\" maxlength=\"100\" class=\"block w-full border border-gray-300 rounded-md p-2\" required>\n            <\/div>\n            <div class=\"mb-4\">\n                <label for=\"accountOwner\" class=\"block text-gray-700\">W\u0142a\u015bciciel konta *<\/label>\n                <input type=\"text\" id=\"accountOwner\" name=\"accountOwner\" autocomplete=\"off\" maxlength=\"100\" class=\"block w-full border border-gray-300 rounded-md p-2\" required>\n            <\/div>\n            <div>\n                <label for=\"accountNumber\" class=\"block text-gray-700\">Nr rachunku *<\/label>\n                <input type=\"text\" id=\"accountNumber\" name=\"accountNumber\" autocomplete=\"off\" class=\"block w-full border border-gray-300 rounded-md p-2\" required>\n                <p id=\"accountNumberError\" class=\"text-red-500 text-sm mt-1 hidden\">Wprowadzony numer konta jest nieprawid\u0142owy. Sprawd\u017a czy poda\u0142e\u015b poprawny numer IBAN.<\/p>\n            <\/div>\n        <\/section>\n\n        <section class=\"form-section\" data-section-index=\"4\">\n            <h2 class=\"form-section-title\">Informacje o ubezpieczeniu<\/h2>\n            <div>\n                <label for=\"productType\" class=\"block text-gray-700\">Wybierz polis\u0119 z kt\u00f3rej ubiegasz si\u0119 o refundacj\u0119 *<\/label>\n                <select id=\"productType\" name=\"productType\" class=\"block w-full border border-gray-300 rounded-md p-2\" required>\n                    <option value=\"\">Wybierz polis\u0119<\/option>\n                    <option value=\"5\">Twoje \u017bycie \u2013 Assistance Medyczny (Allianz)<\/option>\n                    <option value=\"6\">Opieka Medyczna (Ergo Hestia)<\/option>\n                    <option value=\"7\">Assistance Medyczny (Generali)<\/option>\n                    <option value=\"8\">Assistance (GSU)<\/option>\n                    <option value=\"9\">Antidotum Plus (Interrisk)<\/option>\n                    <option value=\"10\">Moje Zdrowie Plus (Nationale Nederlanden)<\/option>\n                    <option value=\"11\">Plan na Zdrowie (PZU)<\/option>\n                    <option value=\"12\">Pe\u0142nia Zdrowia (Signal Iduna)<\/option>\n                    <option value=\"3\">Opieka24 (Uniqa)<\/option>\n                    <option value=\"4\">Medipomoc (Uniqa)<\/option>\n                    <option value=\"13\">Plan Medyczny, Pakiet Kleszcz, NNW szkolne, Mentalhealth (Uniqa)<\/option>\n                    <option value=\"19\">Medycyna bez granic (Uniqa)<\/option>\n                    <option value=\"14\">Pakiet Telezdrowie (Warta)<\/option>\n                    <option value=\"15\">Pakiet Zdrowie i Pomoc, Pakiet Zdrowie i Ochrona (We4med)<\/option>\n                    <option value=\"16\">Badania medycyny pracy<\/option>\n                    <option value=\"17\">Profilaktyka, Badania Diagnostyczne, Konsultacje (Unum)<\/option>\n                    <option value=\"18\">Direct Ubezpieczenia (UNEXT)<\/option>\n                <\/select>\n                <div class=\"mt-3 px-4 py-3 rounded-lg warning-box\">\n                    <p class=\"text-sm warning-box-text\"><span class=\"font-bold\">Uwaga!<\/span> Wyb\u00f3r nieprawid\u0142owej polisy spowoduje odrzucenie wniosku o refundacj\u0119.<\/p>\n                <\/div>\n            <\/div>\n\n            <div class=\"mt-4\">\n                <label for=\"policyNumber\" class=\"block text-gray-700\">Nr polisy (opcjonalnie)<\/label>\n                <input type=\"text\" id=\"policyNumber\" name=\"policyNumber\" autocomplete=\"off\" placeholder=\"np. 0001234567\" maxlength=\"50\" class=\"block w-full border border-gray-300 rounded-md p-2\">\n            <\/div>\n\n            <h3 class=\"form-subsection-title mt-6 hidden\" id=\"reasonSectionHeader\">Pow\u00f3d refundacji *<\/h3>\n            <div class=\"mb-4 hidden\" id=\"eventDateContainer\">\n                <label for=\"eventDate\" class=\"block text-gray-700\">Data pierwszej diagnozy choroby\/schorzenia, data urazu *<\/label>\n                <input type=\"date\" id=\"eventDate\" name=\"eventDate\" class=\"block w-full border border-gray-300 rounded-md p-2\" disabled>\n                <p id=\"eventDateError\" class=\"text-red-500 text-sm mt-1 hidden\">Data zdarzenia musi mie\u015bci\u0107 si\u0119 w ostatnich 3 latach.<\/p>\n            <\/div>\n            <div class=\"mb-4 hidden\" id=\"reasonContainer\">\n                <label for=\"reason\" class=\"block text-gray-700\">Co by\u0142o przyczyn\u0105 zdarzenia? *<\/label>\n                <select id=\"reason\" name=\"reason\" class=\"block w-full border border-gray-300 rounded-md p-2\" disabled>\n                    <option value=\"Choroba\">Choroba<\/option>\n                    <option value=\"Wypadek\">Wypadek<\/option>\n                    <option value=\"Inne\">Inne<\/option>\n                <\/select>\n            <\/div>\n            <div class=\"mb-4 hidden\" id=\"descriptionContainer\">\n                <label for=\"description\" class=\"block text-gray-700\">Opis choroby\/wypadku *<\/label>\n                <textarea id=\"description\" name=\"description\" maxlength=\"500\" class=\"block w-full border border-gray-300 rounded-md p-2\" disabled><\/textarea>\n                <p id=\"descriptionCounter\" class=\"text-sm text-gray-500 mt-1 text-right\">0 \/ 500<\/p>\n            <\/div>\n\n            <h3 class=\"form-subsection-title mt-6 hidden\" id=\"attachmentsSectionHeader\">Za\u0142\u0105czniki *<\/h3>\n            <div class=\"hidden\" id=\"attachmentsContainer\">\n                <label class=\"block text-gray-700 mb-1\">Dodaj dodatkowe za\u0142\u0105czniki (skierowanie, dokumentacja medyczna) *<\/label>\n                <div class=\"file-upload-zone\" data-upload-for=\"attachments\" data-multiple=\"true\">\n                    <input type=\"file\" id=\"attachments\" name=\"attachments[]\" multiple accept=\"image\/*,.pdf\" data-was-required=\"true\" disabled>\n                    <p class=\"text-sm text-gray-600\"><span class=\"file-upload-browse\">Wybierz pliki<\/span> lub przeci\u0105gnij tutaj<\/p>\n                    <div class=\"file-upload-meta\">Zdj\u0119cia i PDF &middot; maks. 10 MB na plik &middot; mo\u017cna dodawa\u0107 wielokrotnie<\/div>\n                <\/div>\n                <div class=\"file-list\" data-list-for=\"attachments\"><\/div>\n            <\/div>\n        <\/section>\n\n        <section class=\"form-section\" data-section-index=\"5\">\n            <h2 class=\"form-section-title\">\u015awiadczenia zdrowotne oraz dowody poniesionych koszt\u00f3w *<\/h2>\n            <div class=\"mb-4 px-4 py-3 rounded-lg info-box\">\n                <p class=\"text-sm info-box-text\"><span class=\"font-bold\">Wa\u017cne:<\/span> Plac\u00f3wka medyczna musi by\u0107 zarejestrowana w <span class=\"font-bold\">Rejestrze Podmiot\u00f3w Wykonuj\u0105cych Dzia\u0142alno\u015b\u0107 Lecznicz\u0105 (RPWDL)<\/span>. Sprawd\u017a plac\u00f3wk\u0119 przed wype\u0142nieniem wniosku.<\/p>\n                <a href=\"https:\/\/rpwdl2.ezdrowie.gov.pl\/wyszukiwarka\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"info-box-link\">Sprawd\u017a w rejestrze RPWDL\n                    <svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6\"\/><polyline points=\"15 3 21 3 21 9\"\/><line x1=\"10\" y1=\"14\" x2=\"21\" y2=\"3\"\/><\/svg>\n                <\/a>\n            <\/div>\n            <div id=\"servicesContainer\">\n                <div class=\"service-group service-subsection\" data-service-index=\"0\">\n                    <div class=\"service-subsection-header\">\n                        <h3 class=\"service-subsection-title\">\u015awiadczenie 1<\/h3>\n                        <button type=\"button\" class=\"remove-service confirm-btn confirm-btn-back remove-service-btn remove-service-btn--hidden\">\n                            Usu\u0144 to \u015bwiadczenie\n                        <\/button>\n                    <\/div>\n                    <div class=\"mb-4 service\">\n                        <label class=\"block text-gray-700\">Nazwa \u015awiadczenia *<\/label>\n                        <select name=\"serviceName[]\" class=\"block w-full border border-gray-300 rounded-md p-2\" required>\n                            <option value=\"\">Wybierz kategori\u0119<\/option>\n                            <option value=\"Badania obrazowe\">Badania obrazowe<\/option>\n                            <option value=\"Us\u0142uga stacjonarna\">Us\u0142uga stacjonarna<\/option>\n                            <option value=\"Wizyta domowa\">Wizyta domowa<\/option>\n                            <option value=\"Rehabilitacje\">Rehabilitacje<\/option>\n                            <option value=\"Sprz\u0119t rehabilitacyjny\">Sprz\u0119t rehabilitacyjny<\/option>\n                            <option value=\"Transport\">Transport<\/option>\n                            <option value=\"Badania laboratoryjne\">Badania laboratoryjne<\/option>\n                            <option value=\"Us\u0142uga Assistance (np. korepetycje)\">Us\u0142uga Assistance (np. korepetycje)<\/option>\n                            <option value=\"Us\u0142uga stomatologiczna\">Us\u0142uga stomatologiczna<\/option>\n                            <option value=\"Badania medycyny pracy\">Badania medycyny pracy<\/option>\n                        <\/select>\n                        <div class=\"mt-3 px-4 py-3 rounded-lg warning-box\">\n                            <p class=\"text-sm warning-box-text\"><span class=\"font-bold\">Uwaga!<\/span> Wybranie z\u0142ego \u015bwiadczenia spowoduje odrzucenie wniosku refundacyjnego.<\/p>\n                        <\/div>\n                    <\/div>\n                    <div class=\"mb-4 service hidden\">\n                        <label class=\"block text-gray-700\">Czy w ramach wizyty lekarskiej by\u0142y realizowane dodatkowe badania? *<\/label>\n                        <div class=\"flex gap-4\">\n                            <label class=\"flex items-center\">\n                                <input type=\"radio\" name=\"additionalTests_0\" value=\"tak\" class=\"mr-2 additional-tests-radio\">\n                                <span>Tak<\/span>\n                            <\/label>\n                            <label class=\"flex items-center\">\n                                <input type=\"radio\" name=\"additionalTests_0\" value=\"nie\" class=\"mr-2 additional-tests-radio\">\n                                <span>Nie<\/span>\n                            <\/label>\n                        <\/div>\n                    <\/div>\n                    <div class=\"mb-4 service additional-tests-details hidden\">\n                        <label class=\"block text-gray-700\">Jakie badania by\u0142y realizowane? *<\/label>\n                        <input type=\"text\" name=\"examName[]\" class=\"block w-full border border-gray-300 rounded-md p-2\">\n                    <\/div>\n                    <div class=\"form-grid-2\">\n                        <div class=\"service\">\n                            <label class=\"block text-gray-700\">Data wykonania *<\/label>\n                            <input type=\"date\" name=\"serviceDate[]\" class=\"block w-full border border-gray-300 rounded-md p-2 service-date\" required>\n                            <p class=\"service-date-error text-red-500 text-sm mt-1 hidden\">Data wykonania \u015bwiadczenia musi mie\u015bci\u0107 si\u0119 w ostatnich 3 latach.<\/p>\n                        <\/div>\n                        <div class=\"service\">\n                            <label class=\"block text-gray-700\">Nazwa plac\u00f3wki *<\/label>\n                            <input type=\"text\" name=\"servicePlace[]\" maxlength=\"200\" class=\"block w-full border border-gray-300 rounded-md p-2\" required>\n                        <\/div>\n                        <div class=\"service form-grid-2-full\">\n                            <label class=\"block text-gray-700\">Adres plac\u00f3wki *<\/label>\n                            <input type=\"text\" name=\"serviceAddress[]\" maxlength=\"100\" class=\"block w-full border border-gray-300 rounded-md p-2\" required>\n                        <\/div>\n                        <div class=\"service\">\n                            <label class=\"block text-gray-700\">Kod pocztowy plac\u00f3wki *<\/label>\n                            <input type=\"text\" name=\"servicePostalCode[]\" class=\"block w-full border border-gray-300 rounded-md p-2 servicePostalCode\" required>\n                            <p class=\"servicePostalCodeError text-red-500 text-sm mt-1 hidden\">Wprowad\u017a poprawny kod pocztowy w formacie 00-000.<\/p>\n                        <\/div>\n                        <div class=\"service\">\n                            <label class=\"block text-gray-700\">Miejscowo\u015b\u0107 plac\u00f3wki *<\/label>\n                            <input type=\"text\" name=\"serviceCity[]\" maxlength=\"100\" class=\"block w-full border border-gray-300 rounded-md p-2\" required>\n                        <\/div>\n                        <div class=\"service form-grid-2-full\">\n                            <label class=\"block text-gray-700\">NIP plac\u00f3wki *<\/label>\n                            <input type=\"text\" name=\"serviceNIP[]\" class=\"block w-full border border-gray-300 rounded-md p-2 service-nip\" inputmode=\"numeric\" placeholder=\"np. 1234567890\" required>\n                            <p class=\"service-nip-error text-red-500 text-sm mt-1 hidden\">Wprowad\u017a poprawny numer NIP (10 cyfr).<\/p>\n                            <p class=\"service-nip-hint field-info-hint hidden\"><\/p>\n                        <\/div>\n                        <div class=\"service\">\n                            <label class=\"block text-gray-700\">Kwota (PLN) *<\/label>\n                            <input type=\"text\" name=\"amount[]\" maxlength=\"12\" class=\"block w-full border border-gray-300 rounded-md p-2 amount-input\" required>\n                            <p class=\"amount-error text-red-500 text-sm mt-1 hidden\">Wprowad\u017a poprawn\u0105 kwot\u0119 (np. 123,45 lub 123.45).<\/p>\n                        <\/div>\n                        <div class=\"service\">\n                            <label class=\"block text-gray-700\">Nr faktury\/rachunku *<\/label>\n                            <input type=\"text\" name=\"invoiceNumber[]\" class=\"block w-full border border-gray-300 rounded-md p-2 invoice-number\" maxlength=\"50\" required>\n                            <p class=\"invoice-number-error text-red-500 text-sm mt-1 hidden\">Numer faktury\/rachunku nie mo\u017ce przekracza\u0107 50 znak\u00f3w.<\/p>\n                        <\/div>\n                    <\/div>\n                    <div class=\"service\">\n                        <label class=\"block text-gray-700 mb-1\">Za\u0142\u0105cz faktur\u0119\/rachunek *<\/label>\n                        <div class=\"file-upload-zone\" data-upload-for=\"invoiceFile0\">\n                            <input type=\"file\" name=\"invoiceFile0\" accept=\"image\/*,.pdf\" data-was-required=\"true\">\n                            <p class=\"text-sm text-gray-600\"><span class=\"file-upload-browse\">Wybierz plik<\/span> lub przeci\u0105gnij tutaj<\/p>\n                            <div class=\"file-upload-meta\">Zdj\u0119cia i PDF &middot; maks. 10 MB<\/div>\n                        <\/div>\n                        <div class=\"file-list\" data-list-for=\"invoiceFile0\"><\/div>\n                    <\/div>\n                <\/div>\n            <\/div>\n            <button type=\"button\" id=\"addService\" class=\"bg-green-500 text-white font-bold py-2 px-4 rounded-md hover:bg-green-600\">Dodaj kolejne \u015bwiadczenie<\/button>\n        <\/section>\n\n        <section class=\"form-section\" data-section-index=\"6\">\n            <h2 class=\"form-section-title\">Zgody i o\u015bwiadczenia<\/h2>\n            <div class=\"mb-3\">\n                <label class=\"consent-checkbox\">\n                    <input type=\"checkbox\" id=\"medicalConsent\" name=\"medicalConsent\" required>\n                    <span class=\"consent-check\"><svg viewBox=\"0 0 14 14\" fill=\"none\"><path d=\"M2.5 7.5L5.5 10.5L11.5 3.5\" stroke=\"white\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"\/><\/svg><\/span>\n                    <span class=\"consent-text\">Upowa\u017cniam Telmedicin Sp. z o.o. do dost\u0119pu do dokumentacji medycznej oraz weryfikacji przedstawionej faktury z podmiotem kt\u00f3ry j\u0105 wystawi\u0142 w celu weryfikacji zasadno\u015bci realizacji wniosku o refundacj\u0119 *<\/span>\n                <\/label>\n            <\/div>\n\n            <div class=\"mb-3\">\n                <label class=\"consent-checkbox\">\n                    <input type=\"checkbox\" id=\"privacyPolicy\" name=\"privacyPolicy\" required>\n                    <span class=\"consent-check\"><svg viewBox=\"0 0 14 14\" fill=\"none\"><path d=\"M2.5 7.5L5.5 10.5L11.5 3.5\" stroke=\"white\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"\/><\/svg><\/span>\n                    <span class=\"consent-text\">Je\u017celi prze\u015blesz do nas formularz, administratorem Twoich danych osobowych b\u0119dzie Telmedicin sp. z o.o. Twoje dane b\u0119dziemy przetwarza\u0107 w celu obs\u0142ugi zg\u0142oszenia (jako nasz prawnie uzasadniony interes). Szczeg\u00f3\u0142owe informacje o zasadach ochrony Twoich danych osobowych i przys\u0142uguj\u0105cych Ci prawach znajdziesz w naszej <a href=\"https:\/\/telemedi.com\/pl\/privacy-policy\/\" target=\"_blank\" rel=\"noopener\">Polityce Prywatno\u015bci<\/a>. *<\/span>\n                <\/label>\n            <\/div>\n\n            <div class=\"mb-3\">\n                <label class=\"consent-checkbox\">\n                    <input type=\"checkbox\" id=\"dataAccuracy\" name=\"dataAccuracy\" required>\n                    <span class=\"consent-check\"><svg viewBox=\"0 0 14 14\" fill=\"none\"><path d=\"M2.5 7.5L5.5 10.5L11.5 3.5\" stroke=\"white\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"\/><\/svg><\/span>\n                    <span class=\"consent-text\">O\u015bwiadczam, \u017ce wszystkie dane zawarte w formularzu s\u0105 zgodne ze stanem faktycznym oraz jestem \u015bwiadomy\/\u015bwiadoma odpowiedzialno\u015bci za zeznanie nieprawdy lub zatajenie okoliczno\u015bci, mog\u0105cych mie\u0107 wp\u0142yw na proces likwidacyjny *<\/span>\n                <\/label>\n            <\/div>\n\n            <div>\n                <label class=\"consent-checkbox\">\n                    <input type=\"checkbox\" id=\"communicationConsent\" name=\"communicationConsent\" required>\n                    <span class=\"consent-check\"><svg viewBox=\"0 0 14 14\" fill=\"none\"><path d=\"M2.5 7.5L5.5 10.5L11.5 3.5\" stroke=\"white\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"\/><\/svg><\/span>\n                    <span class=\"consent-text\">O\u015bwiadczam, \u017ce wyra\u017cam zgod\u0119 na przekazywanie informacji dotycz\u0105cych umowy ubezpieczenia z wykorzystaniem \u015brodk\u00f3w porozumiewania si\u0119 na odleg\u0142o\u015b\u0107 na wskazany przeze mnie w formularzu adres email *<\/span>\n                <\/label>\n            <\/div>\n        <\/section>\n\n        <div class=\"mt-2 mb-2 flex flex-col items-end gap-2\">\n            <div class=\"flex items-center gap-3\">\n                <button type=\"button\" id=\"verifyButton\" disabled title=\"Zaznacz wszystkie zgody i o\u015bwiadczenia, aby zweryfikowa\u0107 wniosek\" class=\"bg-blue-500 text-white font-bold py-3 px-6 rounded-md hover:bg-blue-600 flex items-center gap-2 opacity-50 cursor-not-allowed\">\n                    <span>Weryfikuj wniosek<\/span>\n                    <span id=\"verifyLoader\" class=\"spinner-border\"><\/span>\n                <\/button>\n                <button type=\"submit\" id=\"submitButton\" disabled title=\"Najpierw zweryfikuj wniosek\" class=\"bg-green-500 text-white font-bold py-3 px-6 rounded-md hover:bg-green-600 flex items-center gap-2 opacity-50 cursor-not-allowed\">\n                    <span>Wy\u015blij wniosek<\/span>\n                    <span id=\"loader\" class=\"spinner-border\"><\/span>\n                <\/button>\n            <\/div>\n            <p id=\"verifyWarningHint\" class=\"text-sm text-amber-700 hidden\">Niekt\u00f3re \u015bwiadczenia wymagaj\u0105 poprawy lub nie zosta\u0142y zweryfikowane &#8211; wniosek mo\u017ce zosta\u0107 odrzucony.<\/p>\n        <\/div>\n    <\/form>\n<\/div>\n\n<!-- Weryfikacja wniosku - panel walidacji -->\n<div id=\"issuesPanel\" class=\"issues-panel\" role=\"region\" aria-live=\"polite\" aria-label=\"Lista problem\u00f3w w formularzu\">\n    <div class=\"issues-panel-header\">\n        <span class=\"issues-panel-header-icon\" aria-hidden=\"true\">\n            <svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z\"\/><path d=\"m9 12 2 2 4-4\"\/><\/svg>\n        <\/span>\n        <h2 class=\"issues-panel-header-title\">Weryfikacja wniosku<\/h2>\n        <button type=\"button\" id=\"issuesPanelClose\" class=\"issues-panel-close\" aria-label=\"Zwi\u0144 panel\">\u00d7<\/button>\n    <\/div>\n    <div class=\"issues-panel-chips\">\n        <span id=\"issuesChipError\" class=\"issues-chip issues-chip--error is-hidden\">0 b\u0142\u0119d\u00f3w<\/span>\n        <span id=\"issuesChipWarning\" class=\"issues-chip issues-chip--warning is-hidden\">0 ostrze\u017ce\u0144<\/span>\n        <span id=\"issuesChipSuccess\" class=\"issues-chip issues-chip--success is-hidden\">0 potwierdze\u0144<\/span>\n        <span id=\"issuesChipInfo\" class=\"issues-chip issues-chip--info is-hidden\">0 informacji<\/span>\n    <\/div>\n    <div id=\"issuesPanelBody\" class=\"issues-panel-body\"><\/div>\n    <button type=\"button\" id=\"issuesPanelToggleAll\" class=\"issues-panel-toggle-all\" aria-expanded=\"false\">\n        <span id=\"issuesPanelToggleAllText\">Poka\u017c wszystkie<\/span>\n    <\/button>\n<\/div>\n\n<button type=\"button\" id=\"issuesBadge\" class=\"issues-badge\" aria-label=\"Rozwi\u0144 panel weryfikacji\">\n    <span id=\"issuesBadgeDot\" class=\"issues-badge-dot\"><\/span>\n    <span id=\"issuesBadgeText\">0 b\u0142\u0119d\u00f3w<\/span>\n<\/button>\n\n<div id=\"issuesSuccessPill\" class=\"issues-success-pill\" aria-live=\"polite\">\n    <svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M20 6 9 17l-5-5\"\/><\/svg>\n    <span>Wniosek wygl\u0105da dobrze<\/span>\n<\/div>\n\n<div id=\"issuesSubmitAnnouncement\" aria-live=\"assertive\" role=\"status\" style=\"position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0;\"><\/div>\n\n\n\n\n\n<script data-cookieconsent=\"ignore\">\n    const REFUND_API_ENDPOINT = '\/api\/refund\/issue\/create';\n\n    \/\/ Funkcje do obs\u0142ugi customowego modala\n    function showModal(type, title, message) {\n        \/\/ Sprawd\u017a czy parametry nie s\u0105 puste\n        if (!title && !message) {\n            console.error('showModal: brak tytu\u0142u i wiadomo\u015bci');\n            return;\n        }\n        \n        const modal = document.getElementById('customModal');\n        const modalIcon = document.getElementById('modalIcon');\n        const modalTitle = document.getElementById('modalTitle');\n        const modalMessage = document.getElementById('modalMessage');\n        \n        \/\/ Ustawienie ikony i klasy w zale\u017cno\u015bci od typu\n        if (type === 'error') {\n            modalIcon.innerHTML = '\u274c';\n            modalIcon.className = 'modal-icon error';\n        } else if (type === 'success') {\n            modalIcon.innerHTML = '\u2705';\n            modalIcon.className = 'modal-icon success';\n        } else if (type === 'warning') {\n            modalIcon.innerHTML = '\u26a0\ufe0f';\n            modalIcon.className = 'modal-icon error';\n        } else {\n            modalIcon.innerHTML = '\u2139\ufe0f';\n            modalIcon.className = 'modal-icon';\n        }\n        \n        modalTitle.textContent = title || 'Powiadomienie';\n        modalMessage.textContent = message || '';\n        \n        \/\/ Pokazanie modala\n        modal.classList.add('show');\n        \n        \/\/ Zapobieganie przewijaniu strony w tle\n        document.body.style.overflow = 'hidden';\n        \n        \/\/ Fokus na przycisku zamkni\u0119cia dla dost\u0119pno\u015bci\n        setTimeout(() => {\n            document.getElementById('modalCloseBtn').focus();\n        }, 100);\n    }\n    \n    function hideModal() {\n        const modal = document.getElementById('customModal');\n        modal.classList.remove('show');\n        \n        \/\/ Przywr\u00f3\u0107 przewijanie strony\n        document.body.style.overflow = '';\n    }\n    \n    \/\/ Event listeners dla zamykania modala\n    document.getElementById('modalCloseBtn').addEventListener('click', hideModal);\n    \n    \/\/ Zamkni\u0119cie modala klawiszem Enter na przycisku\n    document.getElementById('modalCloseBtn').addEventListener('keydown', function(e) {\n        if (e.key === 'Enter') {\n            hideModal();\n        }\n    });\n    \n    \/\/ Zamkni\u0119cie modala przy klikni\u0119ciu w t\u0142o\n    document.getElementById('customModal').addEventListener('click', function(e) {\n        if (e.target === this) {\n            hideModal();\n        }\n    });\n    \n    \/\/ Zamkni\u0119cie modala klawiszem Escape\n    document.addEventListener('keydown', function(e) {\n        if (e.key === 'Escape') {\n            const modal = document.getElementById('customModal');\n            if (modal.classList.contains('show')) {\n                hideModal();\n            }\n        }\n    });\n\n    document.getElementById('addService').addEventListener('click', function() {\n        const servicesContainer = document.getElementById('servicesContainer');\n        const serviceCount = document.querySelectorAll('.service-group').length;\n        const newService = document.createElement('div');\n        newService.classList.add('service-group', 'service-subsection');\n        newService.setAttribute('data-service-index', serviceCount);\n        newService.innerHTML = `\n            <div class=\"service-subsection-header\">\n                <h3 class=\"service-subsection-title\">\u015awiadczenie ${serviceCount + 1}<\/h3>\n                <button type=\"button\" class=\"remove-service confirm-btn confirm-btn-back remove-service-btn\">\n                    Usu\u0144 to \u015bwiadczenie\n                <\/button>\n            <\/div>\n            <div class=\"mb-4 service\">\n                <label class=\"block text-gray-700\">Nazwa \u015awiadczenia *<\/label>\n                <select name=\"serviceName[]\" class=\"block w-full border border-gray-300 rounded-md p-2\" required>\n                    <option value=\"\">Wybierz kategori\u0119<\/option>\n                    <option value=\"Badania obrazowe\">Badania obrazowe<\/option>\n                    <option value=\"Us\u0142uga stacjonarna\">Us\u0142uga stacjonarna<\/option>\n                    <option value=\"Wizyta domowa\">Wizyta domowa<\/option>\n                    <option value=\"Rehabilitacje\">Rehabilitacje<\/option>\n                    <option value=\"Sprz\u0119t rehabilitacyjny\">Sprz\u0119t rehabilitacyjny<\/option>\n                    <option value=\"Transport\">Transport<\/option>\n                    <option value=\"Badania laboratoryjne\">Badania laboratoryjne<\/option>\n                    <option value=\"Us\u0142uga Assistance (np. korepetycje)\">Us\u0142uga Assistance (np. korepetycje)<\/option>\n                    <option value=\"Us\u0142uga stomatologiczna\">Us\u0142uga stomatologiczna<\/option>\n                    <option value=\"Badania medycyny pracy\">Badania medycyny pracy<\/option>\n                <\/select>\n                <div class=\"mt-3 px-4 py-3 rounded-lg warning-box\">\n                    <p class=\"text-sm warning-box-text\"><span class=\"font-bold\">Uwaga!<\/span> Wybranie z\u0142ego \u015bwiadczenia spowoduje odrzucenie wniosku refundacyjnego.<\/p>\n                <\/div>\n            <\/div>\n            <div class=\"mb-4 service hidden\">\n                <label class=\"block text-gray-700\">Czy w ramach wizyty lekarskiej by\u0142y realizowane dodatkowe badania? *<\/label>\n                <div class=\"flex gap-4\">\n                    <label class=\"flex items-center\">\n                        <input type=\"radio\" name=\"additionalTests_${serviceCount}\" value=\"tak\" class=\"mr-2 additional-tests-radio\">\n                        <span>Tak<\/span>\n                    <\/label>\n                    <label class=\"flex items-center\">\n                        <input type=\"radio\" name=\"additionalTests_${serviceCount}\" value=\"nie\" class=\"mr-2 additional-tests-radio\">\n                        <span>Nie<\/span>\n                    <\/label>\n                <\/div>\n            <\/div>\n            <div class=\"mb-4 service additional-tests-details hidden\">\n                <label class=\"block text-gray-700\">Jakie badania by\u0142y realizowane? *<\/label>\n                <input type=\"text\" name=\"examName[]\" class=\"block w-full border border-gray-300 rounded-md p-2\">\n            <\/div>\n            <div class=\"form-grid-2\">\n                <div class=\"service\">\n                    <label class=\"block text-gray-700\">Data wykonania *<\/label>\n                    <input type=\"date\" name=\"serviceDate[]\" class=\"block w-full border border-gray-300 rounded-md p-2 service-date\" required>\n                    <p class=\"service-date-error text-red-500 text-sm mt-1 hidden\">Data wykonania \u015bwiadczenia musi mie\u015bci\u0107 si\u0119 w ostatnich 3 latach.<\/p>\n                <\/div>\n                <div class=\"service\">\n                    <label class=\"block text-gray-700\">Nazwa plac\u00f3wki *<\/label>\n                    <input type=\"text\" name=\"servicePlace[]\" maxlength=\"200\" class=\"block w-full border border-gray-300 rounded-md p-2\" required>\n                <\/div>\n                <div class=\"service form-grid-2-full\">\n                    <label class=\"block text-gray-700\">Adres plac\u00f3wki *<\/label>\n                    <input type=\"text\" name=\"serviceAddress[]\" maxlength=\"100\" class=\"block w-full border border-gray-300 rounded-md p-2\" required>\n                <\/div>\n                <div class=\"service\">\n                    <label class=\"block text-gray-700\">Kod pocztowy plac\u00f3wki *<\/label>\n                    <input type=\"text\" name=\"servicePostalCode[]\" class=\"block w-full border border-gray-300 rounded-md p-2 servicePostalCode\" required>\n                    <p class=\"servicePostalCodeError text-red-500 text-sm mt-1 hidden\">Wprowad\u017a poprawny kod pocztowy w formacie 00-000.<\/p>\n                <\/div>\n                <div class=\"service\">\n                    <label class=\"block text-gray-700\">Miejscowo\u015b\u0107 plac\u00f3wki *<\/label>\n                    <input type=\"text\" name=\"serviceCity[]\" maxlength=\"100\" class=\"block w-full border border-gray-300 rounded-md p-2\" required>\n                <\/div>\n                <div class=\"service form-grid-2-full\">\n                    <label class=\"block text-gray-700\">NIP plac\u00f3wki *<\/label>\n                    <input type=\"text\" name=\"serviceNIP[]\" class=\"block w-full border border-gray-300 rounded-md p-2 service-nip\" inputmode=\"numeric\" placeholder=\"np. 1234567890\" required>\n                    <p class=\"service-nip-error text-red-500 text-sm mt-1 hidden\">Wprowad\u017a poprawny numer NIP (10 cyfr).<\/p>\n                    <p class=\"service-nip-hint field-info-hint hidden\"><\/p>\n                <\/div>\n                <div class=\"service\">\n                    <label class=\"block text-gray-700\">Kwota (PLN) *<\/label>\n                    <input type=\"text\" name=\"amount[]\" maxlength=\"12\" class=\"block w-full border border-gray-300 rounded-md p-2 amount-input\" required>\n                    <p class=\"amount-error text-red-500 text-sm mt-1 hidden\">Wprowad\u017a poprawn\u0105 kwot\u0119 (np. 123,45 lub 123.45).<\/p>\n                <\/div>\n                <div class=\"service\">\n                    <label class=\"block text-gray-700\">Nr faktury\/rachunku *<\/label>\n                    <input type=\"text\" name=\"invoiceNumber[]\" class=\"block w-full border border-gray-300 rounded-md p-2 invoice-number\" maxlength=\"50\" required>\n                    <p class=\"invoice-number-error text-red-500 text-sm mt-1 hidden\">Numer faktury\/rachunku nie mo\u017ce przekracza\u0107 50 znak\u00f3w.<\/p>\n                <\/div>\n            <\/div>\n            <div class=\"service\">\n                <label class=\"block text-gray-700 mb-1\">Za\u0142\u0105cz faktur\u0119\/rachunek *<\/label>\n                <div class=\"file-upload-zone\" data-upload-for=\"invoiceFile${serviceCount}\">\n                    <input type=\"file\" name=\"invoiceFile${serviceCount}\" accept=\"image\/*,.pdf\" data-was-required=\"true\">\n                    <p class=\"text-sm text-gray-600\"><span class=\"file-upload-browse\">Wybierz plik<\/span> lub przeci\u0105gnij tutaj<\/p>\n                    <div class=\"file-upload-meta\">Zdj\u0119cia i PDF &middot; maks. 10 MB<\/div>\n                <\/div>\n                <div class=\"file-list\" data-list-for=\"invoiceFile${serviceCount}\"><\/div>\n            <\/div>\n        `;\n        servicesContainer.appendChild(newService);\n\n        \/\/ Aktualizuj numeracj\u0119 po dodaniu\n        updateServiceNumbers();\n        \/\/ Aktualizuj widoczno\u015b\u0107 przycisk\u00f3w usuwania\n        updateRemoveButtonsVisibility();\n        \/\/ Rewaliduj panel weryfikacji - mo\u017ce pojawi\u0107 si\u0119 nowe ostrze\u017cenie dla nowego \u015bwiadczenia\n        if (window.revalidateFormIssues) window.revalidateFormIssues(true);\n        if (window.refundVerify) window.refundVerify.notifyServiceAdded();\n    });\n\n    \/\/ Funkcja do usuwania \u015bwiadcze\u0144\n    let pendingDeleteServiceGroup = null;\n\n    document.addEventListener('click', function(e) {\n        if (e.target.classList.contains('remove-service')) {\n            pendingDeleteServiceGroup = e.target.closest('.service-group');\n            const modal = document.getElementById('deleteServiceModal');\n            modal.classList.add('show');\n            document.body.style.overflow = 'hidden';\n        }\n    });\n\n    document.getElementById('deleteServiceCancelBtn').addEventListener('click', function() {\n        pendingDeleteServiceGroup = null;\n        const modal = document.getElementById('deleteServiceModal');\n        modal.classList.remove('show');\n        document.body.style.overflow = '';\n    });\n\n    document.getElementById('deleteServiceModal').addEventListener('click', function(e) {\n        if (e.target === this) {\n            pendingDeleteServiceGroup = null;\n            this.classList.remove('show');\n            document.body.style.overflow = '';\n        }\n    });\n\n    document.getElementById('deleteServiceConfirmBtn').addEventListener('click', function() {\n        const modal = document.getElementById('deleteServiceModal');\n        modal.classList.remove('show');\n        document.body.style.overflow = '';\n\n        if (pendingDeleteServiceGroup) {\n            \/\/ Capture the removed origIdx BEFORE the DOM node is detached - refundVerify\n            \/\/ needs it to shift its per-service verdict cache and dirty set.\n            const removedIdxRaw = pendingDeleteServiceGroup.getAttribute('data-service-index');\n            const removedIdx = parseInt(removedIdxRaw, 10);\n\n            \/\/ Drop file store entry before the DOM node goes away\n            const zone = pendingDeleteServiceGroup.querySelector('.file-upload-zone');\n            if (zone) {\n                const oldKey = zone.dataset.uploadFor;\n                delete fileStores[oldKey];\n            }\n\n            \/\/ Abort any in-flight RPWDL lookup on the group being removed\n            if (pendingDeleteServiceGroup._rpwdlAbort) {\n                try { pendingDeleteServiceGroup._rpwdlAbort.abort(); } catch (e) { \/* ignored *\/ }\n            }\n            if (pendingDeleteServiceGroup._rpwdlDebounceTimer) {\n                clearTimeout(pendingDeleteServiceGroup._rpwdlDebounceTimer);\n            }\n\n            pendingDeleteServiceGroup.remove();\n            pendingDeleteServiceGroup = null;\n            updateServiceNumbers();\n            updateRemoveButtonsVisibility();\n\n            \/\/ Per-field validators rebind after a service is removed\n            setTimeout(function() {\n                setupServicePostalCodeValidation();\n                setupAmountValidation();\n                setupInvoiceNumberValidation();\n                setupServiceDateValidation();\n            }, 100);\n\n            \/\/ Indices shifted: rekey each surviving group's RPWDL entries to its new idx\n            \/\/ without re-fetching. If the group already has a cached verdict from a prior\n            \/\/ lookup we just replay it under the new idx (zero network); otherwise we ask\n            \/\/ handleNipChange to start one only if the NIP+date are both valid.\n            document.querySelectorAll('.service-group').forEach(function (group) {\n                const gIdx = getServiceIndex(group);\n                formIssues.remove(`warn.service.${gIdx}.rpwdl.notFound`);\n                formIssues.remove(`warn.service.${gIdx}.rpwdl.inactive`);\n                formIssues.remove(`success.service.${gIdx}.rpwdl.ok`);\n                if (group._rpwdlLastVerdict) {\n                    applyRpwdlVerdict(group);\n                } else {\n                    \/\/ No cached verdict (lookup never completed for this group, or was\n                    \/\/ cleared by an invalid edit). Fall through to handleNipChange so a\n                    \/\/ fresh lookup can fire IF the inputs are currently valid; otherwise\n                    \/\/ the gate inside handleNipChange short-circuits with no network.\n                    group.dataset.rpwdlLookupKey = '';\n                    group.dataset.rpwdlLookupStatus = '';\n                    handleNipChange(group, true);\n                }\n            });\n\n            \/\/ Revalidate the issues panel since indices shifted\n            if (window.revalidateFormIssues) window.revalidateFormIssues(true);\n            if (window.refundVerify && !isNaN(removedIdx)) {\n                window.refundVerify.notifyServiceRemoved(removedIdx);\n            }\n        }\n    });\n\n    \/\/ Funkcja do aktualizacji numeracji \u015bwiadcze\u0144\n    function updateServiceNumbers() {\n        const serviceGroups = document.querySelectorAll('.service-group');\n        serviceGroups.forEach((group, index) => {\n            \/\/ Aktualizuj data-service-index\n            group.setAttribute('data-service-index', index);\n\n            \/\/ Aktualizuj nazwy radio button\u00f3w dla dodatkowych bada\u0144\n            const radioButtons = group.querySelectorAll('.additional-tests-radio');\n            radioButtons.forEach(radio => {\n                radio.name = `additionalTests_${index}`;\n            });\n\n            \/\/ Aktualizuj widoczny tytu\u0142 podsekcji \u015bwiadczenia\n            const titleEl = group.querySelector('.service-subsection-title');\n            if (titleEl) {\n                titleEl.textContent = `\u015awiadczenie ${index + 1}`;\n            }\n        });\n        \/\/ Renumbering services also has to reattach files\/dropzones so the fileStores key and the\n        \/\/ file input name stay in sync with the current group index.\n        updateFileInputNames();\n    }\n\n    \/\/ Funkcja do aktualizacji nazw plik\u00f3w po usuni\u0119ciu \u015bwiadczenia\n    function updateFileInputNames() {\n        \/\/ Zbierz stare store'y i przeindeksuj\n        const newStores = {};\n        const serviceGroups = document.querySelectorAll('.service-group');\n        serviceGroups.forEach((group, index) => {\n            const zone = group.querySelector('.file-upload-zone');\n            const fileInput = group.querySelector('input[type=\"file\"]');\n            const fileList = group.querySelector('.file-list');\n            if (zone && fileInput) {\n                const oldKey = zone.dataset.uploadFor;\n                const newKey = `invoiceFile${index}`;\n\n                \/\/ Przenie\u015b pliki ze starego klucza do nowego\n                if (fileStores[oldKey] && oldKey !== newKey) {\n                    newStores[newKey] = fileStores[oldKey];\n                    delete fileStores[oldKey];\n                } else if (fileStores[newKey]) {\n                    newStores[newKey] = fileStores[newKey];\n                } else if (fileStores[oldKey]) {\n                    newStores[newKey] = fileStores[oldKey];\n                }\n\n                zone.dataset.uploadFor = newKey;\n                fileInput.name = newKey;\n                if (fileList) {\n                    fileList.dataset.listFor = newKey;\n                }\n            }\n        });\n\n        \/\/ Zastosuj przeindeksowane store'y\n        Object.keys(newStores).forEach(key => {\n            fileStores[key] = newStores[key];\n        });\n\n        \/\/ Rerenderuj listy plik\u00f3w\n        serviceGroups.forEach((group, index) => {\n            renderFileList(`invoiceFile${index}`);\n        });\n    }\n\n    \/\/ Funkcja do kontrolowania widoczno\u015bci przycisk\u00f3w usuwania\n    function updateRemoveButtonsVisibility() {\n        const serviceGroups = document.querySelectorAll('.service-group');\n        const removeButtons = document.querySelectorAll('.remove-service');\n        \n        \/\/ Poka\u017c przyciski usuwania tylko gdy jest wi\u0119cej ni\u017c jedno \u015bwiadczenie\n        removeButtons.forEach(button => {\n            if (serviceGroups.length > 1) {\n                button.classList.remove('remove-service-btn--hidden');\n            } else {\n                button.classList.add('remove-service-btn--hidden');\n            }\n        });\n    }\n\n    document.getElementById('insuredPerson').addEventListener('change', function() {\n        const relationshipDegreeContainer = document.getElementById('relationshipDegreeContainer');\n        const insuredPeselInput = document.getElementById('insuredPesel');\n        const peselInput = document.getElementById('pesel');\n        const peselSameError = document.getElementById('peselSameError');\n        const insuredPeselSameError = document.getElementById('insuredPeselSameError');\n        \n        if (this.value === 'Inna osoba') {\n            relationshipDegreeContainer.classList.remove('hidden');\n            insuredPeselInput.value = '';\n            insuredPeselInput.readOnly = false;\n            insuredPeselInput.classList.remove('bg-gray-100');\n        } else {\n            \/\/ G\u0142\u00f3wny ubezpieczony - kopiujemy PESEL z pola PESEL i blokujemy edycj\u0119\n            relationshipDegreeContainer.classList.add('hidden');\n            insuredPeselInput.value = peselInput.value;\n            insuredPeselInput.readOnly = true;\n            insuredPeselInput.classList.add('bg-gray-100');\n            \n            \/\/ Ukryj komunikaty b\u0142\u0119d\u00f3w o identycznych PESEL-ach\n            peselSameError.classList.add('hidden');\n            insuredPeselSameError.classList.add('hidden');\n            \n            \/\/ Usu\u0144 podkre\u015blenia b\u0142\u0119d\u00f3w je\u015bli by\u0142y zwi\u0105zane tylko z identycznymi PESEL-ami\n            if (validatePESEL(peselInput.value)) {\n                peselInput.classList.remove('border-red-500');\n            }\n            if (validatePESEL(insuredPeselInput.value)) {\n                insuredPeselInput.classList.remove('border-red-500');\n            }\n        }\n    });\n\n    \/\/ Modyfikujemy event listenery dla p\u00f3l PESEL\n    document.getElementById('pesel').addEventListener('input', function(e) {\n        \/\/ Pozostaw tylko cyfry\n        let value = e.target.value.replace(\/[^0-9]\/g, '');\n\n        \/\/ Ogranicz d\u0142ugo\u015b\u0107 do 11 cyfr\n        value = value.substring(0, 11);\n\n        \/\/ Zaktualizuj warto\u015b\u0107 pola\n        e.target.value = value;\n\n        \/\/ Je\u015bli wybrana opcja to g\u0142\u00f3wny ubezpieczony, aktualizujemy r\u00f3wnie\u017c pole insuredPesel\n        const insuredPerson = document.getElementById('insuredPerson');\n        if (insuredPerson.value === 'G\u0142\u00f3wny ubezpieczony (posiadacz polisy)') {\n            document.getElementById('insuredPesel').value = value;\n        }\n\n        \/\/ Automatyczne wype\u0142nienie daty urodzenia na podstawie PESEL.\n        \/\/ Wpisujemy tylko gdy pole jest puste lub gdy poprzednia warto\u015b\u0107 pochodzi\u0142a z auto-fill.\n        \/\/ U\u017cytkownik mo\u017ce zawsze r\u0119cznie edytowa\u0107 - wtedy flaga jest kasowana i nie nadpisujemy.\n        if (value.length === 11 && validatePESEL(value)) {\n            const decoded = decodePeselBirthDate(value);\n            if (decoded) {\n                const birthEl = document.getElementById('birthDate');\n                if (birthEl && (!birthEl.value || birthEl.dataset.autofilledFromPesel === 'true')) {\n                    if (birthEl.value !== decoded) {\n                        birthEl.value = decoded;\n                    }\n                    birthEl.dataset.autofilledFromPesel = 'true';\n                }\n            }\n        }\n    });\n\n    document.getElementById('pesel').addEventListener('blur', function() {\n        const pesel = this.value;\n        const errorElement = document.getElementById('peselError');\n        const sameErrorElement = document.getElementById('peselSameError');\n        \n        let hasError = false;\n        \n        \/\/ Sprawd\u017a poprawno\u015b\u0107 PESEL\n        if (!validatePESEL(pesel)) {\n            errorElement.classList.remove('hidden');\n            this.classList.add('border-red-500');\n            hasError = true;\n        } else {\n            errorElement.classList.add('hidden');\n        }\n        \n        \/\/ Sprawd\u017a czy PESEL-e nie s\u0105 identyczne\n        if (!validatePESELUnique()) {\n            sameErrorElement.classList.remove('hidden');\n            this.classList.add('border-red-500');\n            hasError = true;\n        } else {\n            sameErrorElement.classList.add('hidden');\n        }\n        \n        if (!hasError) {\n            this.classList.remove('border-red-500');\n        }\n    });\n\n    document.getElementById('insuredPesel').addEventListener('input', function(e) {\n        \/\/ Pozostaw tylko cyfry\n        let value = e.target.value.replace(\/[^0-9]\/g, '');\n        \n        \/\/ Ogranicz d\u0142ugo\u015b\u0107 do 11 cyfr\n        value = value.substring(0, 11);\n        \n        \/\/ Zaktualizuj warto\u015b\u0107 pola\n        e.target.value = value;\n    });\n\n    document.getElementById('insuredPesel').addEventListener('blur', function() {\n        const pesel = this.value;\n        const errorElement = document.getElementById('insuredPeselError');\n        const sameErrorElement = document.getElementById('insuredPeselSameError');\n        \n        let hasError = false;\n        \n        \/\/ Sprawd\u017a poprawno\u015b\u0107 PESEL\n        if (!validatePESEL(pesel)) {\n            errorElement.classList.remove('hidden');\n            this.classList.add('border-red-500');\n            hasError = true;\n        } else {\n            errorElement.classList.add('hidden');\n        }\n        \n        \/\/ Sprawd\u017a czy PESEL-e nie s\u0105 identyczne\n        if (!validatePESELUnique()) {\n            sameErrorElement.classList.remove('hidden');\n            this.classList.add('border-red-500');\n            hasError = true;\n        } else {\n            sameErrorElement.classList.add('hidden');\n        }\n        \n        if (!hasError) {\n            this.classList.remove('border-red-500');\n        }\n    });\n\n\n    \/\/ Obs\u0142uga wyboru produktu - pokazywanie p\u00f3l tylko dla produkt\u00f3w 3 i 4\n    document.getElementById('productType').addEventListener('change', function() {\n        const showFields = this.value === '3' || this.value === '4';\n\n        const eventDateInput = document.getElementById('eventDate');\n        const reasonSelect = document.getElementById('reason');\n        const descriptionTextarea = document.getElementById('description');\n        const attachmentsInput = document.getElementById('attachments');\n\n        const containers = [\n            document.getElementById('eventDateContainer'),\n            document.getElementById('reasonContainer'),\n            document.getElementById('descriptionContainer'),\n            document.getElementById('attachmentsContainer')\n        ];\n        const headers = [\n            document.getElementById('reasonSectionHeader'),\n            document.getElementById('attachmentsSectionHeader')\n        ];\n        const fields = [eventDateInput, reasonSelect, descriptionTextarea, attachmentsInput];\n\n        if (showFields) {\n            containers.forEach(c => c.classList.remove('hidden'));\n            headers.forEach(h => h.classList.remove('hidden'));\n            fields.forEach(f => f.disabled = false);\n            eventDateInput.required = true;\n            reasonSelect.required = true;\n            descriptionTextarea.required = true;\n            \/\/ attachments file validation handled by JS file store check\n        } else {\n            containers.forEach(c => c.classList.add('hidden'));\n            headers.forEach(h => h.classList.add('hidden'));\n            fields.forEach(f => {\n                f.required = false;\n                f.disabled = true;\n                if (f.type === 'file') {\n                    f.value = '';\n                } else {\n                    f.value = '';\n                }\n            });\n        }\n\n        \/\/ Policies without the Za\u0142\u0105czniki field have no backend checklist, so disable verification.\n        if (window.refundVerify) window.refundVerify.setVerificationEnabled(showFields);\n\n        \/\/ Rewaliduj panel - zmiana polisy mo\u017ce wp\u0142yn\u0105\u0107 na widoczne pola\n        if (window.revalidateFormIssues) window.revalidateFormIssues(true);\n    });\n\n    \/\/ Inicjalizacja stanu pola PESEL g\u0142\u00f3wnego ubezpieczonego przy za\u0142adowaniu strony\n    document.addEventListener('DOMContentLoaded', function() {\n        \/\/ Symulujemy zmian\u0119, \u017ceby zainicjalizowa\u0107 stan pola\n        const insuredPerson = document.getElementById('insuredPerson');\n        \/\/ Wywo\u0142ujemy r\u0119cznie zdarzenie change\n        const event = new Event('change');\n        insuredPerson.dispatchEvent(event);\n    });\n\n    const form = document.querySelector('form');\n    const submitButton = document.getElementById('submitButton');\n    \n    form.addEventListener('submit', function(event) {\n        let hasMissingFiles = false;\n        let hasValidationErrors = false;\n\n        \/\/ Oznacz, \u017ce u\u017cytkownik pr\u00f3bowa\u0142 wys\u0142a\u0107 (uruchamia walidacj\u0119 zg\u00f3d w panelu)\n        if (window.formIssues) {\n            window.formIssues.markSubmitAttempted();\n            if (window.revalidateFormIssues) window.revalidateFormIssues(true);\n        }\n\n        \/\/ Submit gate: verify must have run at least once and not be in flight.\n        \/\/ Skipped for invoice-only policies where verification is disabled.\n        if (window.refundVerify && window.refundVerify.isVerificationEnabled()) {\n            if (window.refundVerify.isInFlight()) {\n                event.preventDefault();\n                showModal('warning', 'Trwa weryfikacja', 'Trwa weryfikacja dokument\u00f3w. Zaczekaj na jej zako\u0144czenie.');\n                return false;\n            }\n            if (!window.refundVerify.hasEverVerified()) {\n                event.preventDefault();\n                if (window.formIssues) {\n                    formIssues.upsert('err.verify.notRun', {\n                        severity: 'error',\n                        title: 'Najpierw zweryfikuj wniosek przed wys\u0142aniem.',\n                        section: 'Wys\u0142anie wniosku',\n                        bypassGate: true,\n                        fieldRef: '#verifyButton'\n                    });\n                    formIssues.setCollapsed(false);\n                    formIssues.pulsePanel();\n                }\n                return false;\n            }\n            if (window.formIssues) formIssues.remove('err.verify.notRun');\n        }\n\n        \/\/ Sprawd\u017a czy wymagane uploady maj\u0105 pliki\n        document.querySelectorAll('.file-upload-zone').forEach(zone => {\n            const storeKey = zone.dataset.uploadFor;\n            const input = zone.querySelector('input[type=\"file\"]');\n            const files = fileStores[storeKey] || [];\n            const isRequired = input && (input.dataset.wasRequired === 'true' || input.hasAttribute('required'));\n\n            \/\/ Sprawd\u017a czy pole jest widoczne (nie ukryte przez warunki)\n            const container = zone.closest('.hidden');\n            if (container) return;\n\n            if (isRequired && files.length === 0) {\n                hasMissingFiles = true;\n                zone.style.borderColor = '#ef4444';\n            } else {\n                zone.style.borderColor = '';\n            }\n        });\n\n        if (hasMissingFiles) {\n            event.preventDefault();\n            showModal('error', 'Brak wymaganych plik\u00f3w', 'Dodaj wymagane pliki (faktury\/rachunki, za\u0142\u0105czniki) przed wys\u0142aniem formularza.');\n            submitButton.disabled = false;\n            submitButton.classList.remove('opacity-50', 'cursor-not-allowed');\n            return false;\n        }\n\n        \/\/ Dodatkowa walidacja numeru telefonu\n        const phoneInput = document.getElementById('contactNumber');\n        const phoneError = document.getElementById('phoneError');\n        if (!\/^\\+48\\d{9}$\/.test(phoneInput.value)) {\n            phoneError.classList.remove('hidden');\n            phoneInput.classList.add('border-red-500');\n            hasValidationErrors = true;\n            phoneInput.scrollIntoView({ behavior: 'smooth', block: 'center' });\n        }\n        \n        \/\/ Dodatkowa walidacja PESEL\n        const peselInput = document.getElementById('pesel');\n        const peselError = document.getElementById('peselError');\n        if (!validatePESEL(peselInput.value)) {\n            peselError.classList.remove('hidden');\n            peselInput.classList.add('border-red-500');\n            hasValidationErrors = true;\n            if (!phoneError.classList.contains('hidden')) {\n                peselInput.scrollIntoView({ behavior: 'smooth', block: 'center' });\n            }\n        }\n        \n        \/\/ Dodatkowa walidacja PESEL g\u0142\u00f3wnego ubezpieczonego\n        const insuredPeselInput = document.getElementById('insuredPesel');\n        const insuredPeselError = document.getElementById('insuredPeselError');\n        const insuredPeselSameError = document.getElementById('insuredPeselSameError');\n        if (!validatePESEL(insuredPeselInput.value)) {\n            insuredPeselError.classList.remove('hidden');\n            insuredPeselInput.classList.add('border-red-500');\n            hasValidationErrors = true;\n            if (phoneError.classList.contains('hidden') && peselError.classList.contains('hidden')) {\n                insuredPeselInput.scrollIntoView({ behavior: 'smooth', block: 'center' });\n            }\n        }\n        \n        \/\/ Dodatkowa walidacja czy PESEL-e nie s\u0105 identyczne\n        const peselSameError = document.getElementById('peselSameError');\n        if (!validatePESELUnique()) {\n            peselSameError.classList.remove('hidden');\n            insuredPeselSameError.classList.remove('hidden');\n            peselInput.classList.add('border-red-500');\n            insuredPeselInput.classList.add('border-red-500');\n            hasValidationErrors = true;\n            if (phoneError.classList.contains('hidden') && peselError.classList.contains('hidden') && insuredPeselError.classList.contains('hidden')) {\n                peselInput.scrollIntoView({ behavior: 'smooth', block: 'center' });\n            }\n        }\n        \n        \/\/ Dodatkowa walidacja daty urodzenia\n        const birthDateInput = document.getElementById('birthDate');\n        const birthDateError = document.getElementById('birthDateError');\n        if (!validateDateNotFromFuture(birthDateInput.value)) {\n            birthDateError.classList.remove('hidden');\n            birthDateInput.classList.add('border-red-500');\n            hasValidationErrors = true;\n        }\n        \n        \/\/ Dodatkowa walidacja daty zdarzenia\n        const eventDateInput = document.getElementById('eventDate');\n        const eventDateError = document.getElementById('eventDateError');\n        if (eventDateInput.value && (!validateDateNotFromFuture(eventDateInput.value) || !validateDateWithin3Years(eventDateInput.value))) {\n            eventDateError.classList.remove('hidden');\n            eventDateInput.classList.add('border-red-500');\n            hasValidationErrors = true;\n        }\n\n        \/\/ Dodatkowa walidacja dat wykonania \u015bwiadcze\u0144\n        const serviceDateInputs = document.querySelectorAll('.service-date');\n        let hasServiceDateError = false;\n        serviceDateInputs.forEach(input => {\n            if (input.value && (!validateDateNotFromFuture(input.value) || !validateDateWithin3Years(input.value))) {\n                const errorElement = input.parentElement.querySelector('.service-date-error');\n                errorElement.classList.remove('hidden');\n                input.classList.add('border-red-500');\n                hasValidationErrors = true;\n                hasServiceDateError = true;\n            }\n        });\n        \n        \/\/ Dodatkowa walidacja numer\u00f3w faktur\/rachunk\u00f3w\n        const invoiceNumberInputs = document.querySelectorAll('.invoice-number');\n        let hasInvoiceNumberError = false;\n        invoiceNumberInputs.forEach(input => {\n            if (input.value.length > 50) {\n                const errorElement = input.nextElementSibling;\n                errorElement.classList.remove('hidden');\n                input.classList.add('border-red-500');\n                hasValidationErrors = true;\n                hasInvoiceNumberError = true;\n                if (!hasInvoiceNumberError && phoneError.classList.contains('hidden') && peselError.classList.contains('hidden') && insuredPeselError.classList.contains('hidden')) {\n                    input.scrollIntoView({ behavior: 'smooth', block: 'center' });\n                }\n            }\n        });\n        \n        \/\/ Walidacja checkbox\u00f3w zg\u00f3d\n        let hasUncheckedConsent = false;\n        let firstUncheckedConsent = null;\n        const consentCheckboxes = document.querySelectorAll('.consent-checkbox input[type=\"checkbox\"][required]');\n        consentCheckboxes.forEach(checkbox => {\n            const label = checkbox.closest('.consent-checkbox');\n            if (!checkbox.checked) {\n                label.classList.add('consent-error');\n                hasValidationErrors = true;\n                hasUncheckedConsent = true;\n                if (!firstUncheckedConsent) {\n                    firstUncheckedConsent = label;\n                }\n            } else {\n                label.classList.remove('consent-error');\n            }\n        });\n\n        \/\/ Scroll do pierwszego niezaznaczonego checkboxa je\u015bli to jedyny b\u0142\u0105d\n        if (hasUncheckedConsent && firstUncheckedConsent) {\n            firstUncheckedConsent.scrollIntoView({ behavior: 'smooth', block: 'center' });\n        }\n\n        \/\/ Je\u015bli s\u0105 b\u0142\u0119dy walidacji (legacy lub w panelu), nie wysy\u0142aj formularza\n        const panelSnapshot = (window.formIssues && window.formIssues.snapshot)\n            ? window.formIssues.snapshot() : { errorCount: 0, warningCount: 0 };\n        const panelErrorCount = panelSnapshot.errorCount;\n        if (hasValidationErrors || panelErrorCount > 0) {\n            event.preventDefault();\n            if (window.formIssues) {\n                window.formIssues.setCollapsed(false);\n                window.formIssues.pulsePanel();\n            }\n            const announceEl = document.getElementById('issuesSubmitAnnouncement');\n            if (announceEl) {\n                const total = Math.max(panelErrorCount, 1);\n                const word = total === 1 ? 'b\u0142\u0105d' : (total % 10 >= 2 &&  4 >= total % 10 && (10 > total % 100 || total % 100 >= 20) ? 'b\u0142\u0119dy' : 'b\u0142\u0119d\u00f3w');\n                announceEl.textContent = '';\n                \/\/ Reassign on next tick so screen readers re-announce the message.\n                setTimeout(function () {\n                    announceEl.textContent = `Nie mo\u017cna wys\u0142a\u0107 wniosku: ${total} ${word} do poprawy. Lista znajduje si\u0119 w panelu \"Weryfikacja wniosku\".`;\n                }, 50);\n            }\n            submitButton.disabled = false;\n            submitButton.classList.remove('opacity-50', 'cursor-not-allowed');\n            return false;\n        }\n        \n        event.preventDefault();\n\n        \/\/ Poka\u017c modal potwierdzenia\n        const confirmModal = document.getElementById('confirmModal');\n        const verifyWarnEl = document.getElementById('confirmModalVerifyWarning');\n        if (verifyWarnEl && window.refundVerify) {\n            verifyWarnEl.classList.toggle('hidden', !window.refundVerify.hasVerifyWarnings());\n        }\n        confirmModal.classList.add('show');\n        document.body.style.overflow = 'hidden';\n    });\n\n    \/\/ Obs\u0142uga przycisk\u00f3w modala potwierdzenia\n    document.getElementById('confirmBackBtn').addEventListener('click', function() {\n        const confirmModal = document.getElementById('confirmModal');\n        confirmModal.classList.remove('show');\n        document.body.style.overflow = '';\n    });\n\n    document.getElementById('confirmModal').addEventListener('click', function(e) {\n        if (e.target === this) {\n            this.classList.remove('show');\n            document.body.style.overflow = '';\n        }\n    });\n\n    document.getElementById('confirmSubmitBtn').addEventListener('click', function() {\n        const confirmModal = document.getElementById('confirmModal');\n        confirmModal.classList.remove('show');\n        document.body.style.overflow = '';\n\n        \/\/ Wy\u0142\u0105cz przycisk i zmie\u0144 jego wygl\u0105d\n        submitButton.disabled = true;\n        submitButton.classList.add('opacity-50', 'cursor-not-allowed');\n\n        const loader = document.getElementById('loader');\n        loader.classList.add('show');\n\n        const formData = new FormData(form);\n\n        \/\/ Dodaj pliki ze store'\u00f3w do FormData\n        appendStoredFilesToFormData(formData);\n\n        \/\/ Zbierz wszystkie warto\u015bci additionalTests\n        const additionalTests = [];\n        document.querySelectorAll('.service-group').forEach((group, index) => {\n            const value = formData.get(`additionalTests_${index}`);\n            if (value) {\n                additionalTests.push(value);\n                formData.delete(`additionalTests_${index}`);\n            }\n        });\n\n        \/\/ Dodaj zebrane warto\u015bci jako tablic\u0119\n        additionalTests.forEach(value => {\n            formData.append('additionalTests[]', value);\n        });\n\n        const baseUrl = window.TELEMEDI_API_BASE_URL.replace(\/\\\/+$\/, '');\n        const originalEndpoint = baseUrl + REFUND_API_ENDPOINT;\n\n        \/\/ Wysy\u0142anie formularza - jedna pr\u00f3ba\n        const sendForm = async () => {\n            try {\n                \/\/ Kontroler do anulowania \u017c\u0105dania po timeout\n                const controller = new AbortController();\n                const timeoutId = setTimeout(() => {\n                    controller.abort();\n                }, 60000); \/\/ 60 sekund timeout\n                \n                const response = await fetch(originalEndpoint, {\n                    method: 'POST',\n                    body: formData,\n                    signal: controller.signal\n                });\n                \n                clearTimeout(timeoutId);\n                \n                if (response.ok) {\n                    \/\/ Przekierowujemy u\u017cytkownika na stron\u0119 sukcesu po pomy\u015blnym zako\u0144czeniu\n                    window.location.href = window.location.origin + '\/pl\/formularz-refundacji-success\/';\n                } else {\n                    \/\/ API error logging\n                    console.error('=== B\u0141\u0104D API - ODPOWIED\u0179 SERWERA NIE OK ===');\n                    console.error('Timestamp:', new Date().toISOString());\n                    console.error('Response status:', response.status);\n                    console.error('Response statusText:', response.statusText);\n                    console.error('Response headers:', Object.fromEntries(response.headers.entries()));\n                    console.error('Response URL:', response.url);\n                    console.error('Endpoint:', originalEndpoint);\n                    console.error('User Agent:', navigator.userAgent);\n                    console.error('Online status:', navigator.onLine);\n                    \n                    \/\/ Pobierz tre\u015b\u0107 b\u0142\u0119du z odpowiedzi API\n                    let errorData;\n                    try {\n                        const responseText = await response.text();\n                        console.error('Response body (raw):', responseText);\n                        \n                        if (responseText) {\n                            errorData = JSON.parse(responseText);\n                            console.error('Response body (parsed):', errorData);\n                        }\n                    } catch (parseError) {\n                        console.error('B\u0142\u0105d parsowania odpowiedzi serwera:', parseError);\n                        errorData = { message: `HTTP ${response.status}: ${response.statusText}` };\n                    }\n                    \n                    throw new Error(JSON.stringify(errorData));\n                }\n            } catch (error) {\n                \/\/ Network\/fetch error logging\n                console.error('=== B\u0141\u0104D PODCZAS WYSY\u0141ANIA FORMULARZA ===');\n                console.error('Timestamp:', new Date().toISOString());\n                console.error('Error:', error);\n                console.error('User Agent:', navigator.userAgent);\n                console.error('URL:', window.location.href);\n                console.error('Endpoint:', originalEndpoint);\n                console.error('Online status:', navigator.onLine);\n                console.error('Connection type:', navigator.connection ? navigator.connection.effectiveType : 'unknown');\n                console.error('Platform:', navigator.platform);\n                console.error('Language:', navigator.language);\n                \n                \/\/ Szczeg\u00f3\u0142owe logowanie r\u00f3\u017cnych typ\u00f3w b\u0142\u0119d\u00f3w\n                if (error.name === 'AbortError') {\n                    console.error('\ud83d\udd50 B\u0142\u0105d: Timeout - \u017c\u0105danie przekroczy\u0142o limit czasu (60s)');\n                    console.error('Mo\u017cliwe przyczyny: wolne po\u0142\u0105czenie internetowe, przeci\u0105\u017cony serwer');\n                } else if (error.name === 'TypeError' && error.message.includes('fetch')) {\n                    console.error('\ud83c\udf10 B\u0142\u0105d: TypeError podczas fetch - prawdopodobnie problem sieciowy');\n                    console.error('Mo\u017cliwe przyczyny:');\n                    console.error('- Brak po\u0142\u0105czenia internetowego');\n                    console.error('- Problem z DNS');\n                    console.error('- Firewall\/antywirus blokuje po\u0142\u0105czenie');\n                    console.error('- Problem z certyfikatem SSL');\n                    console.error('- CORS error');\n                    console.error('- Serwer niedost\u0119pny');\n                } else if (error.name === 'NetworkError') {\n                    console.error('\ud83d\udce1 B\u0142\u0105d sieci - problem z po\u0142\u0105czeniem');\n                }\n                \n                \/\/ Logowanie dodatkowych informacji diagnostycznych\n                console.error('Error name:', error.name);\n                console.error('Error message:', error.message);\n                console.error('Error stack:', error.stack);\n                \n                throw error;\n            }\n        };\n        \n        \/\/ Wywo\u0142anie funkcji wysy\u0142ania z obs\u0142ug\u0105 b\u0142\u0119d\u00f3w\n        sendForm()\n        .catch(error => {\n            console.error('=== OSTATECZNY B\u0141\u0104D WYSY\u0141ANIA FORMULARZA ===');\n            console.error('Timestamp:', new Date().toISOString());\n            console.error('Final error:', error);\n            \n            let errorMessage = '';\n            \n            \/\/ Sprawd\u017a typ b\u0142\u0119du i dostosuj komunikat\n            if (error.name === 'AbortError') {\n                errorMessage += '\ud83d\udd50 Przekroczono limit czasu odpowiedzi serwera. Sprawd\u017a po\u0142\u0105czenie internetowe i spr\u00f3buj ponownie.';\n                console.error('\ud83d\udca1 Sugestia: U\u017cytkownik mo\u017ce mie\u0107 wolne po\u0142\u0105czenie internetowe');\n            } else if (error.name === 'TypeError' && error.message.includes('fetch')) {\n                errorMessage += '\ud83c\udf10 Problem z po\u0142\u0105czeniem internetowym.\\n\\nSpr\u00f3buj:\\n1. Od\u015bwie\u017cy\u0107 stron\u0119 (F5) i wys\u0142a\u0107 ponownie\\n2. U\u017cy\u0107 innej sieci (np. internet mobilny zamiast WiFi firmowego)\\n3. Wy\u0142\u0105czy\u0107 VPN je\u015bli jest w\u0142\u0105czony\\n\\nProblem mo\u017ce by\u0107 spowodowany ustawieniami sieci, z kt\u00f3rej korzystasz.';\n                console.error('\ud83d\udca1 Sugestia: Problem sieciowy po stronie u\u017cytkownika lub firewall');\n            } else {\n                \/\/ Sprawd\u017a czy mamy odpowied\u017a z API\n                try {\n                    const errorData = JSON.parse(error.message);\n                    \n                    \/\/ Logowanie szczeg\u00f3\u0142\u00f3w b\u0142\u0119du API do konsoli\n                    console.error('Szczeg\u00f3\u0142y b\u0142\u0119du API:', errorData);\n                    \n                    \/\/ Obs\u0142uga formatu b\u0142\u0119d\u00f3w z tablic\u0105 errors\n                    if (errorData.errors && Array.isArray(errorData.errors) && errorData.errors.length > 0) {\n                        errorMessage += 'B\u0142\u0119dy:';\n                        \n                        \/\/ Dodaj wszystkie b\u0142\u0119dy z tablicy errors\n                        errorData.errors.forEach(err => {\n                            if (err.field && err.message) {\n                                errorMessage += `\\n- ${err.message}`;\n                            } else if (err.message) {\n                                errorMessage += `\\n- ${err.message}`;\n                            }\n                        });\n                    } \n                    \/\/ Obs\u0142uga standardowego formatu z message\/error\n                    else if (errorData.message) {\n                        errorMessage += 'Tre\u015b\u0107 b\u0142\u0119du: ' + errorData.message;\n                    } else if (errorData.error) {\n                        errorMessage += 'Tre\u015b\u0107 b\u0142\u0119du: ' + errorData.error;\n                    }\n                } catch (e) {\n                    \/\/ Je\u015bli nie mamy odpowiedzi JSON, poka\u017c standardowy komunikat z b\u0142\u0119dem\n                    console.error('B\u0142\u0105d parsowania odpowiedzi JSON:', e);\n                    errorMessage += error.message;\n                }\n            }\n            \n            \/\/ Dodatkowe informacje dla u\u017cytkownika w przypadku problem\u00f3w sieciowych\n            if (!navigator.onLine) {\n                errorMessage += '\\n\\n\u26a0\ufe0f Uwaga: Brak po\u0142\u0105czenia internetowego. Sprawd\u017a po\u0142\u0105czenie i spr\u00f3buj ponownie.';\n            }\n            \n            \/\/ Dodaj informacj\u0119 o infolinii na ko\u0144cu\n            errorMessage += '\\n\\nW razie pyta\u0144 skontaktuj si\u0119 z infolini\u0105 +48 22 307 49 94.';\n            \n            showModal('error', 'B\u0142\u0105d wysy\u0142ania formularza', errorMessage);\n            \n            \/\/ Przywr\u00f3\u0107 przycisk do stanu pocz\u0105tkowego w przypadku b\u0142\u0119du\n            submitButton.disabled = false;\n            submitButton.classList.remove('opacity-50', 'cursor-not-allowed');\n            \n\n        })\n        .finally(() => {\n            loader.classList.remove('show');\n        });\n    });\n\n\n\n\n\n    function validateIBAN(accountNumber) {\n        \/\/ Dodaj PL na pocz\u0105tku je\u015bli go nie ma\n        let iban = accountNumber;\n        if (!iban.startsWith('PL')) {\n            iban = 'PL' + iban;\n        }\n        \n        \/\/ Usu\u0144 wszystkie spacje i zamie\u0144 na wielkie litery\n        iban = iban.replace(\/\\s\/g, '').toUpperCase();\n        \n        \/\/ Sprawd\u017a czy mamy dok\u0142adnie 26 cyfr\n        if (!\/^\\d{26}$\/.test(accountNumber)) {\n            return false;\n        }\n        \n        \/\/ Przesu\u0144 pierwsze 4 znaki na koniec\n        const rearranged = iban.substring(4) + iban.substring(0, 4);\n        \n        \/\/ Zamie\u0144 litery na cyfry (A=10, B=11, ...)\n        const converted = rearranged.split('').map(char => {\n            if (\/[0-9]\/.test(char)) {\n                return char;\n            }\n            return (char.charCodeAt(0) - 55).toString();\n        }).join('');\n        \n        \/\/ Oblicz modulo 97\n        let checksum = 0;\n        for (let i = 0; i < converted.length; i++) {\n\t\t\/\/> this comment is used to circumvent the https:\/\/core.trac.wordpress.org\/ticket\/43785 bug\n            checksum = ((checksum * 10) + parseInt(converted[i])) % 97;\n        }\n        \n        return checksum === 1;\n    }\n\n    function validatePESEL(pesel) {\n        \/\/ Sprawd\u017a czy PESEL ma dok\u0142adnie 11 cyfr\n        if (!\/^\\d{11}$\/.test(pesel)) {\n            return false;\n        }\n\n        \/\/ Wagi dla poszczeg\u00f3lnych cyfr PESEL\n        const weights = [1, 3, 7, 9, 1, 3, 7, 9, 1, 3, 1];\n\n        \/\/ Obliczanie sumy kontrolnej\n        let sum = 0;\n        for (let i = 0; i < 11; i++) {\n\t\t\t\/\/> this comment is used to circumvent the https:\/\/core.trac.wordpress.org\/ticket\/43785 bug\n            sum += parseInt(pesel.charAt(i)) * weights[i];\n        }\n\n        \/\/ Sprawdzenie sumy kontrolnej - ostatnia cyfra sumy musi by\u0107 0\n        return sum % 10 === 0;\n    }\n\n    function normalizeNIP(nip) {\n        if (typeof nip !== 'string') return '';\n        \/\/ Strip every non-digit so any separator (space, hyphen, dot, slash, stray letter)\n        \/\/ is removed before length\/checksum checks. The input handler in wireRpwdlLookup\n        \/\/ relies on this returning digits only, then slices to 10.\n        return nip.replace(\/\\D\/g, '');\n    }\n\n    function validateNIP(nip) {\n        const clean = normalizeNIP(nip);\n        if (!\/^\\d{10}$\/.test(clean)) return false;\n        const weights = [6, 5, 7, 2, 3, 4, 5, 6, 7];\n        let sum = 0;\n        for (let i = 0; i < 9; i++) {\n\t\t\t\/\/> this comment is used to circumvent the https:\/\/core.trac.wordpress.org\/ticket\/43785 bug\n            sum += parseInt(clean.charAt(i), 10) * weights[i];\n        }\n        const check = sum % 11;\n        if (check === 10) return false;\n        return check === parseInt(clean.charAt(9), 10);\n    }\n\n    function isValidIsoDate(s) {\n        \/\/ Parse YYYY-MM-DD strictly via the regex captures and Date.UTC, mirroring the\n        \/\/ existing parseIsoDate helper used elsewhere. Avoids Date.parse, which is\n        \/\/ engine- and timezone-dependent for date-only strings.\n        if (typeof s !== 'string') return false;\n        const m = \/^(\\d{4})-(\\d{2})-(\\d{2})$\/.exec(s);\n        if (!m) return false;\n        const year = parseInt(m[1], 10);\n        const month = parseInt(m[2], 10);\n        const day = parseInt(m[3], 10);\n        const d = new Date(Date.UTC(year, month - 1, day));\n        if (isNaN(d.getTime())) return false;\n        \/\/ Round-trip the UTC components back to the original string. Catches roll-over\n        \/\/ cases like \"2026-02-31\" (silently becomes 2026-03-03) and \"2026-13-01\".\n        return d.getUTCFullYear() === year\n            && d.getUTCMonth() === month - 1\n            && d.getUTCDate() === day;\n    }\n\n    function validatePESELUnique() {\n        const insuredPerson = document.getElementById('insuredPerson').value;\n        const pesel = document.getElementById('pesel').value;\n        const insuredPesel = document.getElementById('insuredPesel').value;\n        \n        \/\/ Sprawdzamy tylko gdy wybrana jest opcja \"Inna osoba\"\n        if (insuredPerson === 'Inna osoba' && pesel && insuredPesel) {\n            return pesel !== insuredPesel;\n        }\n        \n        return true; \/\/ Je\u015bli nie ma konfliktu lub nie wybrano \"Inna osoba\"\n    }\n\n    function validateDateNotFromFuture(dateString) {\n        if (!dateString) return true; \/\/ Puste pole mo\u017ce by\u0107, wymagane sprawdza HTML\n\n        const inputDate = new Date(dateString);\n        const today = new Date();\n        today.setHours(23, 59, 59, 999); \/\/ Ustaw na koniec dnia, \u017ceby dzisiejsza data by\u0142a OK\n\n        return inputDate <= today;\n\t\t\/\/> this comment is used to circumvent the https:\/\/core.trac.wordpress.org\/ticket\/43785 bug\n    }\n\n    function validateDateWithin3Years(dateString) {\n        if (!dateString) return true;\n        if (!isValidIsoDate(dateString)) return false;\n        const [year, month, day] = dateString.split('-').map(Number);\n        const inputDate = new Date(year, month - 1, day);\n        const threshold = new Date();\n        threshold.setHours(0, 0, 0, 0);\n        threshold.setFullYear(threshold.getFullYear() - 3);\n        return inputDate >= threshold;\n    }\n\n    function toLocalIsoDate(d) {\n        const yyyy = d.getFullYear();\n        const mm = String(d.getMonth() + 1).padStart(2, '0');\n        const dd = String(d.getDate()).padStart(2, '0');\n        return `${yyyy}-${mm}-${dd}`;\n    }\n\n    function get3YearsAgoIso() {\n        const d = new Date();\n        d.setHours(0, 0, 0, 0);\n        d.setFullYear(d.getFullYear() - 3);\n        return toLocalIsoDate(d);\n    }\n\n    function getTodayIso() {\n        return toLocalIsoDate(new Date());\n    }\n\n    function applyDateBoundsToServiceDate(input) {\n        if (!input) return;\n        input.setAttribute('min', get3YearsAgoIso());\n        input.setAttribute('max', getTodayIso());\n    }\n\n    \/\/ Modyfikujemy event listener dla pola numeru konta\n    document.getElementById('accountNumber').addEventListener('input', function(e) {\n        \/\/ Pozostaw tylko cyfry\n        let value = e.target.value.replace(\/[^0-9]\/g, '');\n        \n        \/\/ Ogranicz d\u0142ugo\u015b\u0107 do 26 cyfr\n        value = value.substring(0, 26);\n        \n        \/\/ Zaktualizuj warto\u015b\u0107 pola\n        e.target.value = value;\n    });\n\n    document.getElementById('accountNumber').addEventListener('blur', function() {\n        const numerKonta = this.value;\n        const errorElement = document.getElementById('accountNumberError');\n        \n        if (!validateIBAN(numerKonta)) {\n            errorElement.classList.remove('hidden');\n            this.classList.add('border-red-500');\n        } else {\n            errorElement.classList.add('hidden');\n            this.classList.remove('border-red-500');\n        }\n    });\n\n    \/\/ Dodajemy walidacj\u0119 numeru telefonu\n    document.getElementById('contactNumber').addEventListener('input', function(e) {\n        let value = e.target.value;\n        \n        \/\/ Upewnij si\u0119, \u017ce numer zaczyna si\u0119 od +48\n        if (!value.startsWith('+48')) {\n            value = '+48';\n        }\n        \n        \/\/ Usu\u0144 wszystkie nie-cyfry po +48\n        const prefix = value.substring(0, 3);\n        let rest = value.substring(3).replace(\/[^0-9]\/g, '');\n        \n        \/\/ Ogranicz d\u0142ugo\u015b\u0107 do 9 cyfr po +48\n        rest = rest.substring(0, 9);\n        \n        \/\/ Zaktualizuj warto\u015b\u0107 pola\n        e.target.value = prefix + rest;\n    });\n\n    document.getElementById('contactNumber').addEventListener('blur', function() {\n        const phone = this.value;\n        const errorElement = document.getElementById('phoneError');\n        \n        \/\/ Sprawd\u017a czy numer jest w formacie +48 + 9 cyfr\n        if (!\/^\\+48\\d{9}$\/.test(phone)) {\n            errorElement.classList.remove('hidden');\n            this.classList.add('border-red-500');\n        } else {\n            errorElement.classList.add('hidden');\n            this.classList.remove('border-red-500');\n        }\n    });\n\n    \/\/ Usuwanie b\u0142\u0119du z checkbox\u00f3w zg\u00f3d po zaznaczeniu\n    document.querySelectorAll('.consent-checkbox input[type=\"checkbox\"]').forEach(checkbox => {\n        checkbox.addEventListener('change', function() {\n            const label = this.closest('.consent-checkbox');\n            if (this.checked) {\n                label.classList.remove('consent-error');\n            }\n        });\n    });\n\n    \/\/ Dodajemy walidacj\u0119 daty urodzenia\n    document.getElementById('birthDate').addEventListener('blur', function() {\n        const date = this.value;\n        const errorElement = document.getElementById('birthDateError');\n\n        if (!validateDateNotFromFuture(date)) {\n            errorElement.classList.remove('hidden');\n            this.classList.add('border-red-500');\n        } else {\n            errorElement.classList.add('hidden');\n            this.classList.remove('border-red-500');\n        }\n    });\n\n    \/\/ R\u0119czna edycja daty urodzenia kasuje flag\u0119 auto-fill - od tej pory PESEL nie nadpisze daty.\n    document.getElementById('birthDate').addEventListener('input', function() {\n        delete this.dataset.autofilledFromPesel;\n    });\n\n    \/\/ Dodajemy walidacj\u0119 daty zdarzenia\n    document.getElementById('eventDate').addEventListener('blur', function() {\n        const date = this.value;\n        const errorElement = document.getElementById('eventDateError');\n\n        if (date && (!validateDateNotFromFuture(date) || !validateDateWithin3Years(date))) {\n            errorElement.classList.remove('hidden');\n            this.classList.add('border-red-500');\n        } else {\n            errorElement.classList.add('hidden');\n            this.classList.remove('border-red-500');\n        }\n    });\n\n    (function () {\n        const description = document.getElementById('description');\n        const counter = document.getElementById('descriptionCounter');\n        if (!description || !counter) return;\n        const limit = parseInt(description.getAttribute('maxlength') || '500', 10);\n        function updateDescriptionCounter() {\n            const length = description.value.length;\n            counter.textContent = length + ' \/ ' + limit;\n            if (length >= limit) {\n                counter.classList.remove('text-gray-500');\n                counter.classList.add('text-red-500');\n            } else {\n                counter.classList.remove('text-red-500');\n                counter.classList.add('text-gray-500');\n            }\n        }\n        description.addEventListener('input', updateDescriptionCounter);\n        updateDescriptionCounter();\n    })();\n\n    document.addEventListener('DOMContentLoaded', function() {\n        document.body.addEventListener('change', function(e) {\n            if (e.target.classList.contains('additional-tests-radio')) {\n                const serviceGroup = e.target.closest('.service-group');\n                const detailsDiv = serviceGroup.querySelector('.additional-tests-details');\n                const detailsInput = detailsDiv.querySelector('input');\n                \n                if (e.target.value === 'tak') {\n                    detailsDiv.classList.remove('hidden');\n                    detailsInput.required = true;\n                } else {\n                    detailsDiv.classList.add('hidden');\n                    detailsInput.required = false;\n                    detailsInput.value = '';\n                }\n            }\n        });\n        \n        \/\/ Inicjalizacja uploadu plik\u00f3w\n        initFileUploads();\n    });\n\n    \/\/ Globalne przechowywanie plik\u00f3w per upload zone\n    const fileStores = {};\n\n    function validateFileType(file) {\n        const acceptedTypes = ['image\/jpeg', 'image\/png', 'image\/gif', 'image\/jpg', 'image\/webp', 'image\/heic', 'image\/heif', 'application\/pdf'];\n        return acceptedTypes.includes(file.type);\n    }\n\n    function validateFileSize(file) {\n        const maxSize = 10 * 1024 * 1024;\n        return file.size <= maxSize;\n\t\t\/\/> this comment is used to circumvent the https:\/\/core.trac.wordpress.org\/ticket\/43785 bug\n    }\n\n    function formatFileSize(bytes) {\n        if (bytes === 0) return '0 B';\n        const k = 1024;\n        const sizes = ['B', 'KB', 'MB', 'GB'];\n        const i = Math.floor(Math.log(bytes) \/ Math.log(k));\n        return parseFloat((bytes \/ Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];\n    }\n\n    function getFileIcon(file) {\n        if (file.type === 'application\/pdf') {\n            return '<svg viewBox=\"0 0 14 14\" fill=\"none\"><path d=\"M3 1h5l4 4v8H3V1z\" stroke=\"currentColor\" stroke-width=\"1.2\"\/><path d=\"M8 1v4h4\" stroke=\"currentColor\" stroke-width=\"1.2\"\/><\/svg>';\n        }\n        return '<svg viewBox=\"0 0 14 14\" fill=\"none\"><rect x=\"1.5\" y=\"2\" width=\"11\" height=\"10\" rx=\"1.5\" stroke=\"currentColor\" stroke-width=\"1.2\"\/><circle cx=\"5\" cy=\"6\" r=\"1.2\" stroke=\"currentColor\" stroke-width=\"1\"\/><path d=\"M1.5 10l3-3 2 2 2.5-2.5L12.5 10\" stroke=\"currentColor\" stroke-width=\"1\"\/><\/svg>';\n    }\n\n    function renderFileList(storeKey) {\n        const listEl = document.querySelector(`.file-list[data-list-for=\"${storeKey}\"]`);\n        if (!listEl) return;\n\n        const files = fileStores[storeKey] || [];\n        listEl.innerHTML = '';\n\n        files.forEach((file, index) => {\n            const item = document.createElement('div');\n            item.className = 'file-list-item';\n            item.innerHTML = `\n                <div class=\"file-list-item-info\">\n                    <div class=\"file-list-item-icon\">${getFileIcon(file)}<\/div>\n                    <span class=\"file-list-item-name\">${file.name}<\/span>\n                    <span class=\"file-list-item-size\">${formatFileSize(file.size)}<\/span>\n                <\/div>\n                <button type=\"button\" class=\"file-list-item-remove\" data-store=\"${storeKey}\" data-index=\"${index}\">\n                    <svg viewBox=\"0 0 14 14\" fill=\"none\"><path d=\"M3 3l8 8M11 3l-8 8\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\"\/><\/svg>\n                <\/button>\n            `;\n            listEl.appendChild(item);\n        });\n\n        \/\/ Wyczy\u015b\u0107 czerwon\u0105 ramk\u0119 po dodaniu pliku\n        if (files.length > 0) {\n            const zone = document.querySelector(`.file-upload-zone[data-upload-for=\"${storeKey}\"]`);\n            if (zone) {\n                zone.style.borderColor = '';\n            }\n        }\n    }\n\n    function addFilesToStore(storeKey, newFiles, isMultiple) {\n        if (!fileStores[storeKey]) {\n            fileStores[storeKey] = [];\n        }\n\n        let hasInvalidFile = false;\n        let hasOversizedFile = false;\n        const validFiles = [];\n\n        for (let i = 0; i < newFiles.length; i++) {\n\t\t\/\/> this comment is used to circumvent the https:\/\/core.trac.wordpress.org\/ticket\/43785 bug\n            const file = newFiles[i];\n            if (!validateFileType(file)) {\n                hasInvalidFile = true;\n                break;\n            }\n            if (!validateFileSize(file)) {\n                hasOversizedFile = true;\n                break;\n            }\n            \/\/ Sprawd\u017a duplikaty po nazwie i rozmiarze\n            const isDuplicate = fileStores[storeKey].some(\n                existing => existing.name === file.name && existing.size === file.size\n            );\n            if (!isDuplicate) {\n                validFiles.push(file);\n            }\n        }\n\n        if (hasInvalidFile) {\n            showModal('error', 'B\u0142\u0105d formatu pliku', 'Wykryto niedozwolony format pliku. Dozwolone s\u0105 tylko zdj\u0119cia i pliki PDF.');\n            return false;\n        }\n\n        if (hasOversizedFile) {\n            showModal('error', 'B\u0142\u0105d rozmiaru pliku', 'Wykryto plik przekraczaj\u0105cy maksymalny rozmiar 10MB. Ka\u017cdy plik nie mo\u017ce by\u0107 wi\u0119kszy ni\u017c 10MB.');\n            return false;\n        }\n\n        if (isMultiple) {\n            fileStores[storeKey] = fileStores[storeKey].concat(validFiles);\n        } else {\n            fileStores[storeKey] = validFiles.length > 0 ? [validFiles[0]] : fileStores[storeKey];\n        }\n\n        renderFileList(storeKey);\n        if (window.revalidateFormIssues) window.revalidateFormIssues(true);\n        notifyVerifyFileChange(storeKey);\n        return true;\n    }\n\n    function removeFileFromStore(storeKey, index) {\n        if (fileStores[storeKey]) {\n            fileStores[storeKey].splice(index, 1);\n            renderFileList(storeKey);\n            if (window.revalidateFormIssues) window.revalidateFormIssues(true);\n            notifyVerifyFileChange(storeKey);\n        }\n    }\n\n    function notifyVerifyFileChange(storeKey) {\n        if (!window.refundVerify) return;\n        if (storeKey === 'attachments') {\n            window.refundVerify.markAttachmentsDirty();\n            return;\n        }\n        const m = \/^invoiceFile(\\d+)$\/.exec(storeKey);\n        if (m) {\n            window.refundVerify.markServiceDirty(parseInt(m[1], 10));\n        }\n    }\n\n    \/\/ Delegowany event listener dla przycisk\u00f3w usuwania plik\u00f3w\n    document.addEventListener('click', function(e) {\n        const removeBtn = e.target.closest('.file-list-item-remove');\n        if (removeBtn) {\n            const storeKey = removeBtn.dataset.store;\n            const index = parseInt(removeBtn.dataset.index);\n            removeFileFromStore(storeKey, index);\n        }\n    });\n\n    function initFileUploads() {\n        document.querySelectorAll('.file-upload-zone').forEach(zone => {\n            \/\/ Pomijamy ju\u017c zainicjalizowane strefy\n            if (zone.dataset.initialized === 'true') return;\n            zone.dataset.initialized = 'true';\n\n            const input = zone.querySelector('input[type=\"file\"]');\n            const initialKey = zone.dataset.uploadFor;\n\n            if (!fileStores[initialKey]) {\n                fileStores[initialKey] = [];\n            }\n\n            \/\/ Dynamiczne odczytywanie klucza i trybu z atrybut\u00f3w\n            function getCurrentKey() { return zone.dataset.uploadFor; }\n            function isMultiple() { return zone.dataset.multiple === 'true'; }\n\n            \/\/ Klik na stref\u0119 otwiera dialog plik\u00f3w\n            zone.addEventListener('click', function(e) {\n                if (e.target.closest('.file-list-item-remove')) return;\n                input.click();\n            });\n\n            \/\/ Obs\u0142uga wyboru plik\u00f3w\n            input.addEventListener('change', function() {\n                if (this.files.length > 0) {\n                    addFilesToStore(getCurrentKey(), this.files, isMultiple());\n                }\n                this.value = '';\n            });\n\n            \/\/ Drag & drop\n            zone.addEventListener('dragover', function(e) {\n                e.preventDefault();\n                this.classList.add('dragover');\n            });\n\n            zone.addEventListener('dragleave', function(e) {\n                e.preventDefault();\n                this.classList.remove('dragover');\n            });\n\n            zone.addEventListener('drop', function(e) {\n                e.preventDefault();\n                this.classList.remove('dragover');\n                if (e.dataTransfer.files.length > 0) {\n                    addFilesToStore(getCurrentKey(), e.dataTransfer.files, isMultiple());\n                }\n            });\n        });\n    }\n\n    \/\/ Funkcja do pobrania wszystkich plik\u00f3w ze store'\u00f3w do FormData\n    function appendStoredFilesToFormData(formData) {\n        \/\/ Usu\u0144 pliki z natywnych input\u00f3w (s\u0105 puste bo resetujemy po ka\u017cdym wyborze)\n        \/\/ Dodaj pliki ze store'\u00f3w\n        Object.keys(fileStores).forEach(storeKey => {\n            const files = fileStores[storeKey];\n            if (storeKey === 'attachments') {\n                \/\/ Usu\u0144 istniej\u0105ce wpisy attachments[]\n                formData.delete('attachments[]');\n                files.forEach(file => {\n                    formData.append('attachments[]', file);\n                });\n            } else {\n                \/\/ Invoice files - pojedyncze pliki\n                formData.delete(storeKey);\n                if (files.length > 0) {\n                    formData.append(storeKey, files[0]);\n                }\n            }\n        });\n    }\n\n    \/\/ Funkcja waliduj\u0105ca kod pocztowy\n    function validatePostalCode(postalCode) {\n        \/\/ Format kodu pocztowego: 00-000\n        return \/^\\d{2}-\\d{3}$\/.test(postalCode);\n    }\n    \n    \/\/ Walidacja kodu pocztowego u\u017cytkownika\n    document.getElementById('postalCode').addEventListener('input', function(e) {\n        let value = e.target.value.replace(\/[^0-9-]\/g, '');\n        \/\/ Automatycznie dodajemy my\u015blnik po dw\u00f3ch cyfrach, je\u015bli go nie ma\n        if (value.length === 2 && !value.includes('-')) {\n            value += '-';\n        }\n        \n        \/\/ Usuwamy wszystkie my\u015blniki poza pierwszym po 2 cyfrach\n        if (value.length > 3) {\n            const firstPart = value.substring(0, 3); \/\/ Pierwsze 2 cyfry + my\u015blnik\n            const secondPart = value.substring(3).replace(\/-\/g, ''); \/\/ Reszta bez my\u015blnik\u00f3w\n            value = firstPart + secondPart;\n        }\n        \n        \/\/ Ograniczamy d\u0142ugo\u015b\u0107 do 6 znak\u00f3w (2 cyfry + my\u015blnik + 3 cyfry)\n        value = value.substring(0, 6);\n        \n        \/\/ Aktualizujemy warto\u015b\u0107 pola\n        e.target.value = value;\n    });\n    \n    document.getElementById('postalCode').addEventListener('blur', function() {\n        const postalCode = this.value;\n        const errorElement = document.getElementById('postalCodeError');\n        \n        if (!validatePostalCode(postalCode)) {\n            errorElement.classList.remove('hidden');\n            this.classList.add('border-red-500');\n        } else {\n            errorElement.classList.add('hidden');\n            this.classList.remove('border-red-500');\n        }\n    });\n    \n    \/\/ Funkcja do dodawania event listener\u00f3w dla kod\u00f3w pocztowych plac\u00f3wek\n    function setupServicePostalCodeValidation() {\n        document.querySelectorAll('.servicePostalCode').forEach(input => {\n            \/\/ Usuwamy poprzednie event listenery \u017ceby unikn\u0105\u0107 duplikacji\n            input.removeEventListener('input', postalCodeInputHandler);\n            input.removeEventListener('blur', postalCodeBlurHandler);\n            \n            \/\/ Dodajemy nowe event listenery\n            input.addEventListener('input', postalCodeInputHandler);\n            input.addEventListener('blur', postalCodeBlurHandler);\n        });\n    }\n    \n    \/\/ Funkcja do dodawania event listener\u00f3w dla dat \u015bwiadcze\u0144\n    function setupServiceDateValidation() {\n        document.querySelectorAll('.service-date').forEach(input => {\n            \/\/ Usuwamy poprzednie event listenery \u017ceby unikn\u0105\u0107 duplikacji\n            input.removeEventListener('blur', serviceDateBlurHandler);\n            \n            \/\/ Dodajemy nowe event listenery\n            input.addEventListener('blur', serviceDateBlurHandler);\n        });\n    }\n    \n    function serviceDateBlurHandler() {\n        const date = this.value;\n        const errorElement = this.parentElement.querySelector('.service-date-error');\n\n        if (date && (!validateDateNotFromFuture(date) || !validateDateWithin3Years(date))) {\n            errorElement.classList.remove('hidden');\n            this.classList.add('border-red-500');\n        } else {\n            errorElement.classList.add('hidden');\n            this.classList.remove('border-red-500');\n        }\n    }\n    \n    function postalCodeInputHandler(e) {\n        let value = e.target.value.replace(\/[^0-9-]\/g, '');\n        \n        \/\/ Automatycznie dodajemy my\u015blnik po dw\u00f3ch cyfrach, je\u015bli go nie ma\n        if (value.length === 2 && !value.includes('-')) {\n            value += '-';\n        }\n        \n        \/\/ Usuwamy wszystkie my\u015blniki poza pierwszym po 2 cyfrach\n        if (value.length > 3) {\n            const firstPart = value.substring(0, 3); \/\/ Pierwsze 2 cyfry + my\u015blnik\n            const secondPart = value.substring(3).replace(\/-\/g, ''); \/\/ Reszta bez my\u015blnik\u00f3w\n            value = firstPart + secondPart;\n        }\n        \n        \/\/ Ograniczamy d\u0142ugo\u015b\u0107 do 6 znak\u00f3w (2 cyfry + my\u015blnik + 3 cyfry)\n        value = value.substring(0, 6);\n        \n        \/\/ Aktualizujemy warto\u015b\u0107 pola\n        e.target.value = value;\n    }\n    \n    function postalCodeBlurHandler() {\n        const postalCode = this.value;\n        const errorElement = this.parentElement.querySelector('.servicePostalCodeError');\n        \n        if (!validatePostalCode(postalCode)) {\n            errorElement.classList.remove('hidden');\n            this.classList.add('border-red-500');\n        } else {\n            errorElement.classList.add('hidden');\n            this.classList.remove('border-red-500');\n        }\n    }\n    \n    function applyDateBoundsToAllServiceDates() {\n        document.querySelectorAll('.service-date').forEach(applyDateBoundsToServiceDate);\n    }\n\n    document.addEventListener('DOMContentLoaded', function() {\n        \/\/ Inicjalizacja walidacji kod\u00f3w pocztowych plac\u00f3wek\n        setupServicePostalCodeValidation();\n        \/\/ Inicjalizacja walidacji kwot\n        setupAmountValidation();\n        \/\/ Inicjalizacja walidacji numer\u00f3w faktur\n        setupInvoiceNumberValidation();\n        \/\/ Inicjalizacja walidacji dat \u015bwiadcze\u0144\n        setupServiceDateValidation();\n\n        const eventDateInput = document.getElementById('eventDate');\n        if (eventDateInput) {\n            eventDateInput.setAttribute('min', get3YearsAgoIso());\n            eventDateInput.setAttribute('max', getTodayIso());\n        }\n        applyDateBoundsToAllServiceDates();\n\n        \/\/ Dodajemy event listener do przycisku dodawania nowego \u015bwiadczenia\n        document.getElementById('addService').addEventListener('click', function() {\n            \/\/ Po dodaniu nowego \u015bwiadczenia czekamy chwil\u0119, a\u017c DOM si\u0119 zaktualizuje\n            setTimeout(function() {\n                setupServicePostalCodeValidation();\n                setupAmountValidation();\n                setupInvoiceNumberValidation();\n                setupServiceDateValidation();\n                applyDateBoundsToAllServiceDates();\n                initFileUploads();\n            }, 100);\n        });\n        \n        \/\/ Wywo\u0142ujemy aktualizacj\u0119 numeracji przy za\u0142adowaniu strony\n        updateServiceNumbers();\n        \/\/ Wywo\u0142ujemy aktualizacj\u0119 widoczno\u015bci przycisk\u00f3w przy za\u0142adowaniu strony\n        updateRemoveButtonsVisibility();\n    });\n\n    \/\/ Funkcja do walidacji kwoty\n    function validateAmount(amount) {\n        \/\/ Akceptujemy liczby z przecinkiem lub kropk\u0105\n        return \/^(\\d+)([\\.,]\\d{1,2})?$\/.test(amount);\n    }\n    \n    \/\/ Funkcja do dodawania event listener\u00f3w dla p\u00f3l kwot\n    function setupAmountValidation() {\n        document.querySelectorAll('.amount-input').forEach(input => {\n            \/\/ Usuwamy poprzednie event listenery \u017ceby unikn\u0105\u0107 duplikacji\n            input.removeEventListener('input', amountInputHandler);\n            input.removeEventListener('blur', amountBlurHandler);\n            \n            \/\/ Dodajemy nowe event listenery\n            input.addEventListener('input', amountInputHandler);\n            input.addEventListener('blur', amountBlurHandler);\n        });\n    }\n    \n    function amountInputHandler(e) {\n        \/\/ Pozwalamy tylko na cyfry, kropk\u0119 i przecinek\n        let value = e.target.value.replace(\/[^0-9.,]\/g, '');\n        \n        \/\/ Ograniczamy do jednego separatora dziesi\u0119tnego\n        let dotIndex = value.indexOf('.');\n        let commaIndex = value.indexOf(',');\n        \n        if (dotIndex !== -1 && commaIndex !== -1) {\n            \/\/ Je\u015bli s\u0105 oba separatory, usuwamy drugi\n            if (dotIndex < commaIndex) {\n                value = value.replace(',', '');\n            } else {\n                value = value.replace('.', '');\n            }\n        }\n        \n        \/\/ Aktualizujemy warto\u015b\u0107 pola\n        e.target.value = value;\n    }\n    \n    function amountBlurHandler() {\n        const amount = this.value;\n        const errorElement = this.parentElement.querySelector('.amount-error');\n        \n        if (!validateAmount(amount)) {\n            errorElement.classList.remove('hidden');\n            this.classList.add('border-red-500');\n        } else {\n            errorElement.classList.add('hidden');\n            this.classList.remove('border-red-500');\n            \n            \/\/ Zamieniamy przecinek na kropk\u0119, aby zapewni\u0107 poprawny format dla API\n            this.value = amount.replace(',', '.');\n        }\n    }\n\n    \/\/ Funkcja do walidacji d\u0142ugo\u015bci numeru faktury\/rachunku\n    function setupInvoiceNumberValidation() {\n        document.querySelectorAll('.invoice-number').forEach(input => {\n            \/\/ Usuwamy poprzednie event listenery \u017ceby unikn\u0105\u0107 duplikacji\n            input.removeEventListener('input', invoiceNumberInputHandler);\n            \n            \/\/ Dodajemy nowe event listenery\n            input.addEventListener('input', invoiceNumberInputHandler);\n        });\n    }\n    \n    function invoiceNumberInputHandler(e) {\n        let value = e.target.value;\n        const errorElement = e.target.nextElementSibling;\n        \n        \/\/ Sprawdzamy d\u0142ugo\u015b\u0107\n        if (value.length > 50) {\n            \/\/ Przycinamy warto\u015b\u0107 do 50 znak\u00f3w\n            value = value.substring(0, 50);\n            e.target.value = value;\n            \n            \/\/ Pokazujemy b\u0142\u0105d\n            errorElement.classList.remove('hidden');\n            e.target.classList.add('border-red-500');\n        } else {\n            \/\/ Ukrywamy b\u0142\u0105d\n            errorElement.classList.add('hidden');\n            e.target.classList.remove('border-red-500');\n        }\n    }\n\n    \/\/ Validation panel (\"Weryfikacja wniosku\")\n    const formIssues = (function () {\n        const entries = new Map();\n        let submitAttempted = false;\n        let collapsed = false;\n        let highestReachedSection = 0;\n        \/\/ True only once the user actually touches the form. Without this, a blank form would\n        \/\/ immediately show the green \"Wniosek wygl\u0105da dobrze\" success pill.\n        let hasInteracted = false;\n        \/\/ Mobile: by default only the top entry is visible, the rest sit behind a \"Poka\u017c wszystkie\" toggle.\n        let mobileExpanded = false;\n\n        const panelEl = document.getElementById('issuesPanel');\n        const bodyEl = document.getElementById('issuesPanelBody');\n        const chipErrorEl = document.getElementById('issuesChipError');\n        const chipWarningEl = document.getElementById('issuesChipWarning');\n        const chipSuccessEl = document.getElementById('issuesChipSuccess');\n        const chipInfoEl = document.getElementById('issuesChipInfo');\n        const closeBtn = document.getElementById('issuesPanelClose');\n        const badgeEl = document.getElementById('issuesBadge');\n        const badgeDotEl = document.getElementById('issuesBadgeDot');\n        const badgeTextEl = document.getElementById('issuesBadgeText');\n        const successPillEl = document.getElementById('issuesSuccessPill');\n        const toggleAllBtn = document.getElementById('issuesPanelToggleAll');\n        const toggleAllText = document.getElementById('issuesPanelToggleAllText');\n\n        function pluralPL(count, one, few, many) {\n            const abs = Math.abs(count);\n            if (abs === 1) return one;\n            const mod10 = abs % 10;\n            const mod100 = abs % 100;\n            if (mod10 >= 2 && 4 >= mod10  && (12 > mod100 || mod100 > 14)) return few;\n\t\t\t\/\/> this comment is used to circumvent the https:\/\/core.trac.wordpress.org\/ticket\/43785 bug\n            return many;\n        }\n\n        function errorsPlural(n) {\n            return `${n} ${pluralPL(n, 'b\u0142\u0105d', 'b\u0142\u0119dy', 'b\u0142\u0119d\u00f3w')}`;\n        }\n\n        function warningsPlural(n) {\n            return `${n} ${pluralPL(n, 'ostrze\u017cenie', 'ostrze\u017cenia', 'ostrze\u017ce\u0144')}`;\n        }\n\n        function successesPlural(n) {\n            return `${n} ${pluralPL(n, 'potwierdzenie', 'potwierdzenia', 'potwierdze\u0144')}`;\n        }\n\n        function infosPlural(n) {\n            return `${n} ${pluralPL(n, 'informacja', 'informacje', 'informacji')}`;\n        }\n\n        function severityIconSvg(severity) {\n            if (severity === 'error') {\n                return '<svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><circle cx=\"12\" cy=\"12\" r=\"10\"\/><line x1=\"12\" y1=\"8\" x2=\"12\" y2=\"13\"\/><line x1=\"12\" y1=\"16\" x2=\"12.01\" y2=\"16\"\/><\/svg>';\n            }\n            if (severity === 'warning') {\n                return '<svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M10.29 3.86 1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z\"\/><line x1=\"12\" y1=\"9\" x2=\"12\" y2=\"13\"\/><line x1=\"12\" y1=\"17\" x2=\"12.01\" y2=\"17\"\/><\/svg>';\n            }\n            if (severity === 'success') {\n                return '<svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M20 6 9 17l-5-5\"\/><\/svg>';\n            }\n            if (severity === 'info') {\n                return '<svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><circle cx=\"12\" cy=\"12\" r=\"10\"\/><line x1=\"12\" y1=\"16\" x2=\"12\" y2=\"12\"\/><line x1=\"12\" y1=\"8\" x2=\"12.01\" y2=\"8\"\/><\/svg>';\n            }\n            return '<svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><circle cx=\"12\" cy=\"12\" r=\"10\"\/><line x1=\"12\" y1=\"16\" x2=\"12\" y2=\"12\"\/><line x1=\"12\" y1=\"8\" x2=\"12.01\" y2=\"8\"\/><\/svg>';\n        }\n\n        function buildEntryEl(id, payload) {\n            const el = document.createElement('div');\n            el.className = `issues-entry issues-entry--${payload.severity}`;\n            el.dataset.issueId = id;\n            el.innerHTML = `\n                <div class=\"issues-entry-row\">\n                    <span class=\"issues-entry-icon\">${severityIconSvg(payload.severity)}<\/span>\n                    <div class=\"issues-entry-content\">\n                        <p class=\"issues-entry-title\"><\/p>\n                        <p class=\"issues-entry-section\" style=\"display:none;\"><\/p>\n                        <p class=\"issues-entry-footnote\" style=\"display:none;\">Trzeba poprawi\u0107 - bez tego nie wy\u015blesz wniosku.<\/p>\n                        <button type=\"button\" class=\"issues-entry-action\" style=\"display:none;\">\n                            <span class=\"issues-entry-action-label\">Przejd\u017a i popraw<\/span>\n                            <svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><polyline points=\"9 18 15 12 9 6\"\/><\/svg>\n                        <\/button>\n                    <\/div>\n                    <button type=\"button\" class=\"issues-entry-close\" aria-label=\"Zamknij\" style=\"display:none;\">\n                        <svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\"\/><line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\"\/><\/svg>\n                    <\/button>\n                <\/div>\n            `;\n            applyPayload(el, payload);\n            return el;\n        }\n\n        function applyPayload(el, payload) {\n            const titleEl = el.querySelector('.issues-entry-title');\n            const sectionEl = el.querySelector('.issues-entry-section');\n            const footnoteEl = el.querySelector('.issues-entry-footnote');\n            const actionEl = el.querySelector('.issues-entry-action');\n            const actionLabelEl = el.querySelector('.issues-entry-action-label');\n            const closeEl = el.querySelector('.issues-entry-close');\n\n            if (titleEl.textContent !== payload.title) {\n                titleEl.textContent = payload.title;\n            }\n\n            if (payload.section) {\n                const text = `Sekcja: ${payload.section}`;\n                if (sectionEl.textContent !== text) sectionEl.textContent = text;\n                sectionEl.style.display = '';\n            } else {\n                sectionEl.style.display = 'none';\n            }\n\n            footnoteEl.style.display = payload.severity === 'error' ? '' : 'none';\n\n            const actionLabel = (payload.severity === 'success' || payload.severity === 'info') ? 'Przejd\u017a' : 'Przejd\u017a i popraw';\n            if (actionLabelEl && actionLabelEl.textContent !== actionLabel) {\n                actionLabelEl.textContent = actionLabel;\n            }\n\n            if (payload.fieldRef) {\n                actionEl.style.display = '';\n                actionEl.onclick = function () {\n                    goToField(payload.fieldRef);\n                };\n            } else {\n                actionEl.style.display = 'none';\n                actionEl.onclick = null;\n            }\n\n            if (closeEl) {\n                if (payload.severity === 'success' || payload.severity === 'info') {\n                    closeEl.style.display = '';\n                    closeEl.onclick = function () {\n                        if (typeof payload.onClose === 'function') payload.onClose();\n                        remove(el.dataset.issueId);\n                    };\n                } else {\n                    closeEl.style.display = 'none';\n                    closeEl.onclick = null;\n                }\n            }\n        }\n\n        function goToField(fieldRef) {\n            let el = typeof fieldRef === 'string' ? document.querySelector(fieldRef) : fieldRef;\n            if (!el) return;\n            \/\/ Ukryte inputy plik\u00f3w nie nadaj\u0105 si\u0119 do scrollu\/focus - przejd\u017a na opakowuj\u0105cy dropzone.\n            if (el.tagName === 'INPUT' && el.type === 'file') {\n                const zone = el.closest('.file-upload-zone');\n                if (zone) el = zone;\n            }\n            el.scrollIntoView({ behavior: 'smooth', block: 'center' });\n            try {\n                if (typeof el.focus === 'function') {\n                    el.focus({ preventScroll: true });\n                }\n            } catch (e) {\n                if (typeof el.focus === 'function') el.focus();\n            }\n            el.classList.remove('field-pulse');\n            void el.offsetWidth;\n            el.classList.add('field-pulse');\n            setTimeout(function () { el.classList.remove('field-pulse'); }, 1000);\n        }\n\n        function recomputeCounts() {\n            let errorCount = 0;\n            let warningCount = 0;\n            let successCount = 0;\n            let infoCount = 0;\n            entries.forEach(function (record) {\n                if (record.payload.severity === 'error') errorCount++;\n                else if (record.payload.severity === 'warning') warningCount++;\n                else if (record.payload.severity === 'success') successCount++;\n                else if (record.payload.severity === 'info') infoCount++;\n            });\n            return { errorCount: errorCount, warningCount: warningCount, successCount: successCount, infoCount: infoCount };\n        }\n\n        function render() {\n            const counts = recomputeCounts();\n\n            if (counts.errorCount > 0) {\n                chipErrorEl.classList.remove('is-hidden');\n                chipErrorEl.textContent = errorsPlural(counts.errorCount);\n            } else {\n                chipErrorEl.classList.add('is-hidden');\n            }\n\n            if (counts.warningCount > 0) {\n                chipWarningEl.classList.remove('is-hidden');\n                chipWarningEl.textContent = warningsPlural(counts.warningCount);\n            } else {\n                chipWarningEl.classList.add('is-hidden');\n            }\n\n            if (chipSuccessEl) {\n                if (counts.successCount > 0) {\n                    chipSuccessEl.classList.remove('is-hidden');\n                    chipSuccessEl.textContent = successesPlural(counts.successCount);\n                } else {\n                    chipSuccessEl.classList.add('is-hidden');\n                }\n            }\n\n            if (chipInfoEl) {\n                if (counts.infoCount > 0) {\n                    chipInfoEl.classList.remove('is-hidden');\n                    chipInfoEl.textContent = infosPlural(counts.infoCount);\n                } else {\n                    chipInfoEl.classList.add('is-hidden');\n                }\n            }\n\n            const hasAny = entries.size > 0;\n\n            if (!hasAny) {\n                panelEl.classList.remove('is-visible');\n                badgeEl.classList.remove('is-visible');\n                \/\/ Show the green \"Wniosek wygl\u0105da dobrze\" pill only when nothing is queued AND the\n                \/\/ user has actually started filling the form (otherwise a fresh, empty form would\n                \/\/ proudly announce it \"looks good\").\n                if (pendingSuppressed.size === 0 && hasInteracted) {\n                    successPillEl.classList.add('is-visible');\n                } else {\n                    successPillEl.classList.remove('is-visible');\n                }\n                return;\n            }\n\n            successPillEl.classList.remove('is-visible');\n\n            if (collapsed) {\n                panelEl.classList.remove('is-visible');\n                badgeEl.classList.add('is-visible');\n                badgeDotEl.classList.remove('issues-badge-dot--warning', 'issues-badge-dot--info', 'issues-badge-dot--success');\n                if (counts.errorCount > 0) {\n                    badgeTextEl.textContent = errorsPlural(counts.errorCount);\n                } else if (counts.warningCount > 0) {\n                    badgeDotEl.classList.add('issues-badge-dot--warning');\n                    badgeTextEl.textContent = warningsPlural(counts.warningCount);\n                } else if (counts.infoCount > 0) {\n                    badgeDotEl.classList.add('issues-badge-dot--info');\n                    badgeTextEl.textContent = infosPlural(counts.infoCount);\n                } else {\n                    badgeDotEl.classList.add('issues-badge-dot--success');\n                    badgeTextEl.textContent = successesPlural(counts.successCount);\n                }\n            } else {\n                panelEl.classList.add('is-visible');\n                badgeEl.classList.remove('is-visible');\n            }\n\n            \/\/ Mobile: show \"Poka\u017c wszystkie (N)\" when there are 2+ entries.\n            if (entries.size > 1) {\n                panelEl.classList.add('has-multiple');\n                if (mobileExpanded) {\n                    panelEl.classList.remove('mobile-collapsed');\n                    toggleAllText.textContent = 'Poka\u017c mniej';\n                    toggleAllBtn.setAttribute('aria-expanded', 'true');\n                } else {\n                    panelEl.classList.add('mobile-collapsed');\n                    toggleAllText.textContent = `Poka\u017c wszystkie (${entries.size})`;\n                    toggleAllBtn.setAttribute('aria-expanded', 'false');\n                }\n            } else {\n                panelEl.classList.remove('has-multiple', 'mobile-collapsed');\n                mobileExpanded = false;\n            }\n        }\n\n        \/\/ Rules whose section has not yet been \"opened\" by the user wait here instead of appearing in the panel.\n        const pendingSuppressed = new Map();\n\n        function isPayloadVisible(payload) {\n            if (payload && payload.bypassGate) return true;\n            if (!payload || typeof payload.sectionIndex !== 'number') return true;\n            return payload.sectionIndex < highestReachedSection;\n\t\t\t\/\/> this comment is used to circumvent the https:\/\/core.trac.wordpress.org\/ticket\/43785 bug\n        }\n\n        \/\/ Map<id, { timeoutId, el, payload }> for entries that are mid-fade-out. We track them so a\n        \/\/ follow-up upsert with the same id can cancel the pending DOM removal and re-attach the\n        \/\/ existing node to entries, instead of leaving an orphan and creating a duplicate.\n        const pendingRemovals = new Map();\n\n        function cancelPendingRemoval(id) {\n            const pending = pendingRemovals.get(id);\n            if (!pending) return;\n            clearTimeout(pending.timeoutId);\n            pendingRemovals.delete(id);\n            pending.el.classList.remove('is-entering');\n            if (!entries.has(id) && pending.el.parentNode) {\n                entries.set(id, { el: pending.el, payload: pending.payload });\n            }\n        }\n\n        function renderEntry(id, payload) {\n            cancelPendingRemoval(id);\n            if (entries.has(id)) {\n                const record = entries.get(id);\n                if (record.payload.severity !== payload.severity) {\n                    record.el.className = `issues-entry issues-entry--${payload.severity}`;\n                    record.el.querySelector('.issues-entry-icon').innerHTML = severityIconSvg(payload.severity);\n                }\n                applyPayload(record.el, payload);\n                record.payload = payload;\n            } else {\n                const el = buildEntryEl(id, payload);\n                el.classList.add('is-entering');\n                bodyEl.appendChild(el);\n                requestAnimationFrame(function () {\n                    el.classList.remove('is-entering');\n                });\n                entries.set(id, { el: el, payload: payload });\n            }\n        }\n\n        function upsert(id, payload) {\n            if (isPayloadVisible(payload)) {\n                pendingSuppressed.delete(id);\n                renderEntry(id, payload);\n            } else {\n                \/\/ Section not opened yet - park the rule, do not render it.\n                pendingSuppressed.set(id, payload);\n                if (entries.has(id)) {\n                    \/\/ A previously visible rule became gated again (rare); remove it.\n                    removeFromVisible(id);\n                }\n            }\n            render();\n        }\n\n        function removeFromVisible(id) {\n            const record = entries.get(id);\n            if (!record) return;\n            \/\/ If a previous removal for this id is already in flight, cancel it so we don't end up\n            \/\/ with two timers racing to detach the same node.\n            const existing = pendingRemovals.get(id);\n            if (existing) clearTimeout(existing.timeoutId);\n            entries.delete(id);\n            const el = record.el;\n            const payload = record.payload;\n            el.classList.add('is-entering');\n            const timeoutId = setTimeout(function () {\n                pendingRemovals.delete(id);\n                if (el.parentNode) el.parentNode.removeChild(el);\n            }, 150);\n            pendingRemovals.set(id, { timeoutId: timeoutId, el: el, payload: payload });\n        }\n\n        function remove(id) {\n            pendingSuppressed.delete(id);\n            if (entries.has(id)) {\n                removeFromVisible(id);\n                render();\n            }\n        }\n\n        function removeMatching(prefix) {\n            const toRemove = [];\n            entries.forEach(function (_, id) {\n                if (id.indexOf(prefix) === 0) toRemove.push(id);\n            });\n            pendingSuppressed.forEach(function (_, id) {\n                if (id.indexOf(prefix) === 0 && toRemove.indexOf(id) === -1) toRemove.push(id);\n            });\n            toRemove.forEach(remove);\n        }\n\n        function has(id) {\n            return entries.has(id);\n        }\n\n        function flushPending() {\n            const toFlush = [];\n            pendingSuppressed.forEach(function (payload, id) {\n                if (isPayloadVisible(payload)) toFlush.push(id);\n            });\n            toFlush.forEach(function (id) {\n                const payload = pendingSuppressed.get(id);\n                pendingSuppressed.delete(id);\n                renderEntry(id, payload);\n            });\n            if (toFlush.length > 0) render();\n        }\n\n        function notifyFocusedSection(sectionIndex) {\n            if (typeof sectionIndex !== 'number' || !(sectionIndex > 0)) return;\n            hasInteracted = true;\n            if (sectionIndex > highestReachedSection) {\n                highestReachedSection = sectionIndex;\n                flushPending();\n            }\n        }\n\n        function markInteracted() {\n            if (hasInteracted) return;\n            hasInteracted = true;\n            render();\n        }\n\n        function getHighestReachedSection() {\n            return highestReachedSection;\n        }\n\n        function snapshot() {\n            return recomputeCounts();\n        }\n\n        function markSubmitAttempted() {\n            submitAttempted = true;\n            \/\/ Submit attempt = user reached the end of the form; flush everything queued in earlier sections.\n            notifyFocusedSection(6);\n        }\n\n        function isSubmitAttempted() {\n            return submitAttempted;\n        }\n\n        function setCollapsed(value) {\n            collapsed = !!value;\n            render();\n        }\n\n        function pulsePanel() {\n            const target = panelEl.classList.contains('is-visible') ? panelEl : badgeEl;\n            if (!target.classList.contains('is-visible')) return;\n            target.style.transition = 'transform 0.2s ease-out';\n            target.style.transform = 'scale(1.04)';\n            setTimeout(function () {\n                target.style.transform = 'scale(1)';\n                setTimeout(function () { target.style.transition = ''; target.style.transform = ''; }, 220);\n            }, 200);\n        }\n\n        closeBtn.addEventListener('click', function () { setCollapsed(true); });\n        badgeEl.addEventListener('click', function () { setCollapsed(false); });\n        toggleAllBtn.addEventListener('click', function () {\n            mobileExpanded = !mobileExpanded;\n            render();\n        });\n\n        return {\n            upsert: upsert,\n            remove: remove,\n            removeMatching: removeMatching,\n            has: has,\n            snapshot: snapshot,\n            markSubmitAttempted: markSubmitAttempted,\n            isSubmitAttempted: isSubmitAttempted,\n            setCollapsed: setCollapsed,\n            goToField: goToField,\n            pulsePanel: pulsePanel,\n            render: render,\n            notifyFocusedSection: notifyFocusedSection,\n            getHighestReachedSection: getHighestReachedSection,\n            markInteracted: markInteracted\n        };\n    })();\n\n    window.formIssues = formIssues;\n\n    \/\/ Validation rules - evaluated live\n    function decodePeselBirthDate(pesel) {\n        if (!\/^\\d{11}$\/.test(pesel)) return null;\n        let year = parseInt(pesel.substring(0, 2), 10);\n        let month = parseInt(pesel.substring(2, 4), 10);\n        const day = parseInt(pesel.substring(4, 6), 10);\n        if (month >= 81 && month <= 92) { year += 1800; month -= 80; }\n\t\t\/\/> this comment is used to circumvent the https:\/\/core.trac.wordpress.org\/ticket\/43785 bug\n        else if (month >= 1 && month <= 12) { year += 1900; }\n\t\t\/\/> this comment is used to circumvent the https:\/\/core.trac.wordpress.org\/ticket\/43785 bug\n        else if (month >= 21 && month <= 32) { year += 2000; month -= 20; }\n\t\t\/\/> this comment is used to circumvent the https:\/\/core.trac.wordpress.org\/ticket\/43785 bug\n        else if (month >= 41 && month <= 52) { year += 2100; month -= 40; }\n\t\t\/\/> this comment is used to circumvent the https:\/\/core.trac.wordpress.org\/ticket\/43785 bug\n        else if (month >= 61 && month <= 72) { year += 2200; month -= 60; }\n\t\t\/\/> this comment is used to circumvent the https:\/\/core.trac.wordpress.org\/ticket\/43785 bug\n        else return null;\n        const d = new Date(Date.UTC(year, month - 1, day));\n        if (d.getUTCFullYear() !== year || d.getUTCMonth() !== month - 1 || d.getUTCDate() !== day) return null;\n        \/\/ Date in the future means the PESEL is meaningless even if its checksum happens to be valid.\n        \/\/ Refuse to decode so we don't autofill a future birthDate from a typo.\n        const todayUtc = new Date();\n        todayUtc.setUTCHours(0, 0, 0, 0);\n        if (d.getTime() > todayUtc.getTime()) return null;\n        const mm = String(month).padStart(2, '0');\n        const dd = String(day).padStart(2, '0');\n        return `${year}-${mm}-${dd}`;\n    }\n\n    function parseAmountValue(raw) {\n        if (!raw) return NaN;\n        const normalized = String(raw).replace(',', '.').trim();\n        if (!\/^\\d+(\\.\\d+)?$\/.test(normalized)) return NaN;\n        return parseFloat(normalized);\n    }\n\n    \/\/ Wymagane pola w kolejno\u015bci wyst\u0119powania w formularzu.\n    \/\/ sectionIndex pomaga gate'owa\u0107 b\u0142\u0105d: pole poka\u017ce si\u0119 dopiero gdy u\u017cytkownik dotrze do nast\u0119pnej sekcji.\n    \/\/ skipIf pozwala pomin\u0105\u0107 pole w niekt\u00f3rych warunkach (np. insuredPesel jest wymagany tylko dla \"Inna osoba\").\n    const REQUIRED_FIELDS = [\n        { id: 'firstName', label: 'Imi\u0119', sectionIndex: 1, sectionName: 'Dane osobowe' },\n        { id: 'lastName', label: 'Nazwisko', sectionIndex: 1, sectionName: 'Dane osobowe' },\n        { id: 'pesel', label: 'PESEL', sectionIndex: 1, sectionName: 'Dane osobowe' },\n        { id: 'birthDate', label: 'Data urodzenia', sectionIndex: 1, sectionName: 'Dane osobowe' },\n        { id: 'contactNumber', label: 'Numer kontaktowy', sectionIndex: 1, sectionName: 'Dane osobowe',\n          isEmpty: function (el) { const v = (el.value || '').trim(); return !v || v === '+48'; } },\n        { id: 'email', label: 'Adres e-mail do korespondencji', sectionIndex: 1, sectionName: 'Dane osobowe' },\n        { id: 'insuredPesel', label: 'PESEL g\u0142\u00f3wnego ubezpieczonego', sectionIndex: 1, sectionName: 'Dane osobowe',\n          skipIf: function () {\n              const ip = document.getElementById('insuredPerson');\n              return !ip || ip.value !== 'Inna osoba';\n          } },\n        { id: 'street', label: 'Ulica', sectionIndex: 2, sectionName: 'Adres do korespondencji' },\n        { id: 'houseNumber', label: 'Nr domu', sectionIndex: 2, sectionName: 'Adres do korespondencji' },\n        { id: 'postalCode', label: 'Kod pocztowy', sectionIndex: 2, sectionName: 'Adres do korespondencji' },\n        { id: 'city', label: 'Miejscowo\u015b\u0107', sectionIndex: 2, sectionName: 'Adres do korespondencji' },\n        { id: 'bank', label: 'Bank\/Oddzia\u0142', sectionIndex: 3, sectionName: 'Dane do wyp\u0142aty' },\n        { id: 'accountOwner', label: 'W\u0142a\u015bciciel konta', sectionIndex: 3, sectionName: 'Dane do wyp\u0142aty' },\n        { id: 'accountNumber', label: 'Nr rachunku', sectionIndex: 3, sectionName: 'Dane do wyp\u0142aty' },\n        { id: 'productType', label: 'Polisa', sectionIndex: 4, sectionName: 'Informacje o ubezpieczeniu' }\n    ];\n\n    function evalDeterministic() {\n        \/\/ Pierwsze niewype\u0142nione wymagane pole z sekcji, kt\u00f3r\u0105 u\u017cytkownik ju\u017c opu\u015bci\u0142\n        const highest = formIssues.getHighestReachedSection();\n        let firstMissing = null;\n        for (let i = 0; i < REQUIRED_FIELDS.length; i++) {\n\t\t\/\/> this comment is used to circumvent the https:\/\/core.trac.wordpress.org\/ticket\/43785 bug\n            const field = REQUIRED_FIELDS[i];\n            if (field.sectionIndex >= highest) break; \/\/ dalsze sekcje jeszcze nieotwarte\n            if (field.skipIf && field.skipIf()) continue;\n            const el = document.getElementById(field.id);\n            if (!el) continue;\n            const empty = field.isEmpty ? field.isEmpty(el) : !(el.value || '').trim();\n            if (empty) { firstMissing = field; break; }\n        }\n\n        \/\/ Warunkowe pola sekcji 4 (Informacje o ubezpieczeniu) - widoczne dla niekt\u00f3rych polis\n        if (!firstMissing && highest > 4) {\n            const productTypeEl = document.getElementById('productType');\n            const showsConditional = productTypeEl && (productTypeEl.value === '3' || productTypeEl.value === '4');\n            if (showsConditional) {\n                const conditionalSection4 = [\n                    { id: 'eventDate', label: 'Data pierwszej diagnozy \/ urazu' },\n                    { id: 'reason', label: 'Przyczyna zdarzenia' },\n                    { id: 'description', label: 'Opis choroby\/wypadku' }\n                ];\n                for (let i = 0; i < conditionalSection4.length; i++) {\n\t\t\t\t\/\/> this comment is used to circumvent the https:\/\/core.trac.wordpress.org\/ticket\/43785 bug\n                    const f = conditionalSection4[i];\n                    const el = document.getElementById(f.id);\n                    if (!el) continue;\n                    if (!(el.value || '').trim()) {\n                        firstMissing = {\n                            id: f.id, label: f.label,\n                            sectionIndex: 4, sectionName: 'Informacje o ubezpieczeniu'\n                        };\n                        break;\n                    }\n                }\n                \/\/ Za\u0142\u0105czniki (skierowanie \/ dokumentacja medyczna)\n                if (!firstMissing) {\n                    const attachmentFiles = (typeof fileStores !== 'undefined' && fileStores['attachments']) || [];\n                    if (attachmentFiles.length === 0) {\n                        const attachZone = document.querySelector('.file-upload-zone[data-upload-for=\"attachments\"]');\n                        firstMissing = {\n                            _customFieldRef: attachZone || null,\n                            id: 'attachments', label: 'Za\u0142\u0105czniki (skierowanie, dokumentacja medyczna)',\n                            sectionIndex: 4, sectionName: 'Informacje o ubezpieczeniu'\n                        };\n                    }\n                }\n            }\n        }\n\n        \/\/ Pola wymagane w sekcji \"\u015awiadczenia\" - per \u015bwiadczenie, w kolejno\u015bci wy\u015bwietlania w karcie\n        if (!firstMissing && highest > 5) {\n            const serviceFieldSpec = [\n                { name: 'serviceName[]', label: 'Nazwa \u015bwiadczenia' },\n                { name: 'serviceDate[]', label: 'Data wykonania' },\n                { name: 'servicePlace[]', label: 'Nazwa plac\u00f3wki' },\n                { name: 'serviceAddress[]', label: 'Adres plac\u00f3wki' },\n                { name: 'servicePostalCode[]', label: 'Kod pocztowy plac\u00f3wki' },\n                { name: 'serviceCity[]', label: 'Miejscowo\u015b\u0107 plac\u00f3wki' },\n                { name: 'serviceNIP[]', label: 'NIP plac\u00f3wki' },\n                { name: 'amount[]', label: 'Kwota (PLN)' },\n                { name: 'invoiceNumber[]', label: 'Nr faktury\/rachunku' }\n            ];\n            const serviceGroups = document.querySelectorAll('.service-group');\n            for (let idx = 0; !firstMissing && idx < serviceGroups.length; idx++) {\n\t\t\t\/\/> this comment is used to circumvent the https:\/\/core.trac.wordpress.org\/ticket\/43785 bug\n                const n = idx + 1;\n                const group = serviceGroups[idx];\n                \/\/ Pola tekstowe\/numeryczne w kolejno\u015bci\n                for (let j = 0; j < serviceFieldSpec.length; j++) {\n\t\t\t\t\/\/> this comment is used to circumvent the https:\/\/core.trac.wordpress.org\/ticket\/43785 bug\n                    const spec = serviceFieldSpec[j];\n                    const el = group.querySelector(`[name=\"${spec.name}\"]`);\n                    if (!el) continue;\n                    if (!(el.value || '').trim()) {\n                        firstMissing = {\n                            _customFieldRef: el,\n                            id: spec.name,\n                            label: `${spec.label} dla \u015bwiadczenia nr ${n}`,\n                            sectionIndex: 5, sectionName: `\u015awiadczenie ${n}`\n                        };\n                        break;\n                    }\n                }\n                if (firstMissing) break;\n                \/\/ Za\u0142\u0105cznik z faktur\u0105\/rachunkiem (plik) - na ko\u0144cu ka\u017cdego \u015bwiadczenia\n                const key = `invoiceFile${idx}`;\n                const files = (typeof fileStores !== 'undefined' && fileStores[key]) || [];\n                if (files.length === 0) {\n                    const zone = group.querySelector('.file-upload-zone');\n                    firstMissing = {\n                        _customFieldRef: zone || null,\n                        id: key,\n                        label: `Faktura\/rachunek dla \u015bwiadczenia nr ${n}`,\n                        sectionIndex: 5, sectionName: `\u015awiadczenie ${n}`\n                    };\n                }\n            }\n        }\n\n        if (firstMissing) {\n            formIssues.upsert('err.required.missing', {\n                severity: 'error',\n                title: `Pole ${firstMissing.label} jest wymagane`,\n                section: firstMissing.sectionName,\n                sectionIndex: firstMissing.sectionIndex,\n                fieldRef: firstMissing._customFieldRef || ('#' + firstMissing.id)\n            });\n        } else {\n            formIssues.remove('err.required.missing');\n        }\n\n        \/\/ PESEL - rozr\u00f3\u017cniamy \"za ma\u0142o cyfr\" (u\u017cytkownik wci\u0105\u017c wpisuje) od \"niepoprawna suma kontrolna\"\n        const peselEl = document.getElementById('pesel');\n        const peselVal = peselEl ? peselEl.value.trim() : '';\n        if (peselVal.length > 0 && peselVal.length < 11) {\n\t\t\/\/> this comment is used to circumvent the https:\/\/core.trac.wordpress.org\/ticket\/43785 bug\n            formIssues.upsert('err.pesel.tooShort', {\n                severity: 'error',\n                title: `Numer PESEL musi mie\u0107 11 cyfr. Wpisano dot\u0105d: ${peselVal.length}.`,\n                section: 'Dane osobowe',\n                bypassGate: true,\n                fieldRef: '#pesel'\n            });\n            formIssues.remove('err.pesel.checksum');\n        } else if (peselVal.length === 11 && !validatePESEL(peselVal)) {\n            formIssues.upsert('err.pesel.checksum', {\n                severity: 'error',\n                title: 'Numer PESEL jest nieprawid\u0142owy. Sprawd\u017a, czy nie pomyli\u0142e\u015b \u017cadnej cyfry.',\n                section: 'Dane osobowe',\n                bypassGate: true,\n                fieldRef: '#pesel'\n            });\n            formIssues.remove('err.pesel.tooShort');\n        } else {\n            formIssues.remove('err.pesel.tooShort');\n            formIssues.remove('err.pesel.checksum');\n        }\n\n        \/\/ Insured PESEL - same length \/ checksum validation as the main PESEL.\n        \/\/ Only runs when \"Inna osoba\" is selected (otherwise the field is auto-filled from #pesel).\n        const insuredPersonElEarly = document.getElementById('insuredPerson');\n        const insuredPeselElEarly = document.getElementById('insuredPesel');\n        const insuredPeselValEarly = insuredPeselElEarly ? insuredPeselElEarly.value.trim() : '';\n        const isInnaOsoba = insuredPersonElEarly && insuredPersonElEarly.value === 'Inna osoba';\n        if (isInnaOsoba && insuredPeselValEarly.length > 0 && insuredPeselValEarly.length < 11) {\n\t\t\/\/> this comment is used to circumvent the https:\/\/core.trac.wordpress.org\/ticket\/43785 bug\n            formIssues.upsert('err.insuredPesel.tooShort', {\n                severity: 'error',\n                title: `Numer PESEL g\u0142\u00f3wnego ubezpieczonego musi mie\u0107 11 cyfr. Wpisano dot\u0105d: ${insuredPeselValEarly.length}.`,\n                section: 'Dane osobowe',\n                bypassGate: true,\n                fieldRef: '#insuredPesel'\n            });\n            formIssues.remove('err.insuredPesel.checksum');\n        } else if (isInnaOsoba && insuredPeselValEarly.length === 11 && !validatePESEL(insuredPeselValEarly)) {\n            formIssues.upsert('err.insuredPesel.checksum', {\n                severity: 'error',\n                title: 'Numer PESEL g\u0142\u00f3wnego ubezpieczonego jest nieprawid\u0142owy. Sprawd\u017a, czy nie pomyli\u0142e\u015b \u017cadnej cyfry.',\n                section: 'Dane osobowe',\n                bypassGate: true,\n                fieldRef: '#insuredPesel'\n            });\n            formIssues.remove('err.insuredPesel.tooShort');\n        } else {\n            formIssues.remove('err.insuredPesel.tooShort');\n            formIssues.remove('err.insuredPesel.checksum');\n        }\n\n        \/\/ PESEL <-> data urodzenia mismatch (warning)\n        const birthEl = document.getElementById('birthDate');\n        const birthVal = birthEl ? birthEl.value : '';\n        if (peselVal && validatePESEL(peselVal) && birthVal) {\n            const peselDate = decodePeselBirthDate(peselVal);\n            if (peselDate && peselDate !== birthVal) {\n                formIssues.upsert('warn.pesel.birthdate.mismatch', {\n                    severity: 'warning',\n                    title: `Data urodzenia (${birthVal}) nie zgadza si\u0119 z dat\u0105 zapisan\u0105 w numerze PESEL (${peselDate}). Przed wys\u0142aniem sprawd\u017a oba pola.`,\n                    section: 'Dane osobowe',\n                    bypassGate: true,\n                    fieldRef: '#birthDate'\n                });\n            } else {\n                formIssues.remove('warn.pesel.birthdate.mismatch');\n            }\n        } else {\n            formIssues.remove('warn.pesel.birthdate.mismatch');\n        }\n\n        \/\/ \"Inna osoba\": PESEL osoby uprawnionej i g\u0142\u00f3wnego ubezpieczonego musz\u0105 by\u0107 r\u00f3\u017cne\n        const insuredPersonEl = document.getElementById('insuredPerson');\n        const insuredPeselEl = document.getElementById('insuredPesel');\n        const insuredPeselVal = insuredPeselEl ? insuredPeselEl.value.trim() : '';\n        if (insuredPersonEl && insuredPersonEl.value === 'Inna osoba' &&\n            peselVal && insuredPeselVal && peselVal === insuredPeselVal) {\n            formIssues.upsert('err.pesel.sameAsInsured', {\n                severity: 'error',\n                title: 'PESEL osoby uprawnionej i g\u0142\u00f3wnego ubezpieczonego nie mog\u0105 by\u0107 takie same. Dla opcji \"Inna osoba\" musz\u0105 to by\u0107 r\u00f3\u017cne osoby.',\n                section: 'Dane osobowe',\n                bypassGate: true,\n                fieldRef: '#insuredPesel'\n            });\n        } else {\n            formIssues.remove('err.pesel.sameAsInsured');\n        }\n\n        \/\/ Email - format\n        const emailEl = document.getElementById('email');\n        const emailVal = emailEl ? emailEl.value.trim() : '';\n        if (emailVal.length > 0 && !\/^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$\/.test(emailVal)) {\n            formIssues.upsert('err.email.format', {\n                severity: 'error',\n                title: 'Adres e-mail jest nieprawid\u0142owy. Powinien mie\u0107 format np. jan.kowalski@example.com.',\n                section: 'Dane osobowe',\n                bypassGate: true,\n                fieldRef: '#email'\n            });\n        } else {\n            formIssues.remove('err.email.format');\n        }\n\n        \/\/ Kod pocztowy\n        const postalEl = document.getElementById('postalCode');\n        const postalVal = postalEl ? postalEl.value.trim() : '';\n        if (postalVal.length > 0 && !\/^\\d{2}-\\d{3}$\/.test(postalVal)) {\n            formIssues.upsert('err.postal.format', {\n                severity: 'error',\n                title: 'Kod pocztowy musi by\u0107 w formacie XX-XXX (na przyk\u0142ad 00-001).',\n                section: 'Adres do korespondencji',\n                bypassGate: true,\n                fieldRef: '#postalCode'\n            });\n        } else {\n            formIssues.remove('err.postal.format');\n        }\n\n        \/\/ Numer rachunku\n        const accEl = document.getElementById('accountNumber');\n        const accVal = accEl ? accEl.value.trim() : '';\n        if (accVal.length > 0 && !validateIBAN(accVal)) {\n            formIssues.upsert('err.account.invalid', {\n                severity: 'error',\n                title: 'Numer rachunku bankowego jest nieprawid\u0142owy. Sprawd\u017a, czy zawiera 26 cyfr (lub PL i 26 cyfr).',\n                section: 'Dane do wyp\u0142aty',\n                bypassGate: true,\n                fieldRef: '#accountNumber'\n            });\n        } else {\n            formIssues.remove('err.account.invalid');\n        }\n\n        \/\/ Service issues are evaluated per (rule, index). We do NOT bulk-purge before re-evaluating,\n        \/\/ because that caused a remove+add cycle on every keystroke and entries would flicker and\n        \/\/ re-order. Instead each rule below upserts when it applies and remove()s its own id when\n        \/\/ it does not. Stale ids that belong to services that no longer exist are cleaned at the\n        \/\/ end of this block.\n        const serviceGroups = document.querySelectorAll('.service-group');\n        const today = new Date();\n        today.setHours(23, 59, 59, 999);\n\n        \/\/ Data pierwszej diagnozy\/urazu (pole warunkowe - tylko dla niekt\u00f3rych polis)\n        const eventDateEl = document.getElementById('eventDate');\n        const eventDateContainer = document.getElementById('eventDateContainer');\n        const eventDateVisible = eventDateContainer && !eventDateContainer.classList.contains('hidden');\n        const eventDateValRaw = (eventDateVisible && eventDateEl && eventDateEl.value) ? eventDateEl.value : '';\n        \/\/ Parse ISO YYYY-MM-DD as UTC midnight so we can compare with Date math instead of relying on\n        \/\/ the strings being lexicographically equivalent to chronological order.\n        function parseIsoDate(value) {\n            if (!value) return null;\n            const m = \/^(\\d{4})-(\\d{2})-(\\d{2})$\/.exec(value);\n            if (!m) return null;\n            const d = new Date(Date.UTC(parseInt(m[1], 10), parseInt(m[2], 10) - 1, parseInt(m[3], 10)));\n            return isNaN(d.getTime()) ? null : d;\n        }\n        const eventDateParsed = parseIsoDate(eventDateValRaw);\n\n        if (eventDateValRaw && !validateDateWithin3Years(eventDateValRaw)) {\n            formIssues.upsert('err.eventDate.tooOld', {\n                severity: 'error',\n                title: 'Data zdarzenia musi mie\u015bci\u0107 si\u0119 w ostatnich 3 latach (roszczenia o refundacj\u0119 przedawniaj\u0105 si\u0119).',\n                section: 'Informacje o ubezpieczeniu',\n                sectionIndex: 4,\n                bypassGate: true,\n                fieldRef: '#eventDate'\n            });\n        } else {\n            formIssues.remove('err.eventDate.tooOld');\n        }\n\n        serviceGroups.forEach(function (group, idx) {\n            const n = idx + 1;\n            const dateInput = group.querySelector('.service-date');\n            const amountInput = group.querySelector('.amount-input');\n            const invoiceInput = group.querySelector('.invoice-number');\n            const addressInput = group.querySelector('input[name=\"serviceAddress[]\"]');\n            const serviceNameSelect = group.querySelector('select[name=\"serviceName[]\"]');\n            const nipInput = group.querySelector('.service-nip');\n\n            const idDateFuture = `err.service.${idx}.date.future`;\n            const idDateTooOld = `err.service.${idx}.date.tooOld`;\n            const idDateBeforeEvent = `err.service.${idx}.date.beforeEvent`;\n            const idAmountZero = `err.service.${idx}.amount.zero`;\n            const idInvoiceMissing = `warn.service.${idx}.invoice.missing`;\n            const idAddressMissing = `warn.service.${idx}.address.missing`;\n            const idNipMissing = `err.service.${idx}.nip.missing`;\n            const idNipInvalid = `err.service.${idx}.nip.invalid`;\n\n            const serviceDateParsed = dateInput ? parseIsoDate(dateInput.value) : null;\n            if (dateInput && dateInput.value) {\n                const d = new Date(dateInput.value);\n                if (d > today) {\n                    formIssues.upsert(idDateFuture, {\n                        severity: 'error',\n                        title: `\u015awiadczenie nr ${n}: data wizyty nie mo\u017ce by\u0107 z przysz\u0142o\u015bci.`,\n                        section: `\u015awiadczenie ${n}`,\n                        bypassGate: true,\n                        fieldRef: dateInput\n                    });\n                } else {\n                    formIssues.remove(idDateFuture);\n                }\n\n                if (!validateDateWithin3Years(dateInput.value)) {\n                    formIssues.upsert(idDateTooOld, {\n                        severity: 'error',\n                        title: `\u015awiadczenie nr ${n}: data wykonania \u015bwiadczenia musi mie\u015bci\u0107 si\u0119 w ostatnich 3 latach (roszczenia o refundacj\u0119 przedawniaj\u0105 si\u0119).`,\n                        section: `\u015awiadczenie ${n}`,\n                        bypassGate: true,\n                        fieldRef: dateInput\n                    });\n                } else {\n                    formIssues.remove(idDateTooOld);\n                }\n\n                if (eventDateParsed && serviceDateParsed && serviceDateParsed.getTime() < eventDateParsed.getTime()) {\n\t\t\t\t\/\/> this comment is used to circumvent the https:\/\/core.trac.wordpress.org\/ticket\/43785 bug\n                    formIssues.upsert(idDateBeforeEvent, {\n                        severity: 'error',\n                        title: `\u015awiadczenie nr ${n}: data wizyty (${dateInput.value}) nie mo\u017ce by\u0107 wcze\u015bniejsza ni\u017c data pierwszej diagnozy \/ urazu (${eventDateValRaw}).`,\n                        section: `\u015awiadczenie ${n}`,\n                        bypassGate: true,\n                        fieldRef: dateInput\n                    });\n                } else {\n                    formIssues.remove(idDateBeforeEvent);\n                }\n            } else {\n                formIssues.remove(idDateFuture);\n                formIssues.remove(idDateTooOld);\n                formIssues.remove(idDateBeforeEvent);\n            }\n\n            if (amountInput && amountInput.value.trim()) {\n                const val = parseAmountValue(amountInput.value);\n                if (!isNaN(val) && val <= 0) {\n\t\t\t\t\/\/> this comment is used to circumvent the https:\/\/core.trac.wordpress.org\/ticket\/43785 bug\n                    formIssues.upsert(idAmountZero, {\n                        severity: 'error',\n                        title: `\u015awiadczenie nr ${n}: kwota musi by\u0107 wi\u0119ksza od zera.`,\n                        section: `\u015awiadczenie ${n}`,\n                        bypassGate: true,\n                        fieldRef: amountInput\n                    });\n                } else {\n                    formIssues.remove(idAmountZero);\n                }\n            } else {\n                formIssues.remove(idAmountZero);\n            }\n\n            const hasServiceChoice = serviceNameSelect && serviceNameSelect.value;\n            if (hasServiceChoice && invoiceInput && !invoiceInput.value.trim()) {\n                formIssues.upsert(idInvoiceMissing, {\n                    severity: 'warning',\n                    title: `\u015awiadczenie nr ${n}: brakuje numeru faktury lub rachunku. Znajdziesz go na dokumencie z plac\u00f3wki.`,\n                    section: `\u015awiadczenie ${n}`,\n                    sectionIndex: 5,\n                    fieldRef: invoiceInput\n                });\n            } else {\n                formIssues.remove(idInvoiceMissing);\n            }\n\n            if (hasServiceChoice && addressInput && !addressInput.value.trim()) {\n                formIssues.upsert(idAddressMissing, {\n                    severity: 'warning',\n                    title: `\u015awiadczenie nr ${n}: brakuje adresu plac\u00f3wki, w kt\u00f3rej odby\u0142a si\u0119 wizyta.`,\n                    section: `\u015awiadczenie ${n}`,\n                    sectionIndex: 5,\n                    fieldRef: addressInput\n                });\n            } else {\n                formIssues.remove(idAddressMissing);\n            }\n\n            if (nipInput) {\n                \/\/ Empty-NIP case is handled by the ordered serviceFieldSpec \u2192 err.required.missing\n                \/\/ flow above, so the user is walked to the NIP field in form order alongside the\n                \/\/ other missing fields. The dedicated rule below only flags checksum\/format\n                \/\/ problems on a filled value.\n                formIssues.remove(idNipMissing);\n                const nipClean = normalizeNIP(nipInput.value);\n                if (nipClean && !validateNIP(nipClean)) {\n                    formIssues.upsert(idNipInvalid, {\n                        severity: 'error',\n                        title: `\u015awiadczenie nr ${n}: NIP plac\u00f3wki jest nieprawid\u0142owy. Sprawd\u017a, czy poda\u0142e\u015b wszystkie 10 cyfr.`,\n                        section: `\u015awiadczenie ${n}`,\n                        bypassGate: true,\n                        fieldRef: nipInput\n                    });\n                } else {\n                    formIssues.remove(idNipInvalid);\n                }\n            }\n\n        });\n\n        \/\/ Drop stale ids that belonged to services that have since been removed. Anything keyed\n        \/\/ by an index >= current group count can no longer map to a DOM group. Probe a small\n        \/\/ window above the live count instead of bulk-purging by prefix, so we never touch ids\n        \/\/ that still belong to a real service.\n        const liveCount = serviceGroups.length;\n        for (let staleIdx = liveCount; staleIdx < liveCount + 20; staleIdx++) {\n\t\t\/\/> this comment is used to circumvent the https:\/\/core.trac.wordpress.org\/ticket\/43785 bug\n            formIssues.remove(`err.service.${staleIdx}.date.future`);\n            formIssues.remove(`err.service.${staleIdx}.date.tooOld`);\n            formIssues.remove(`err.service.${staleIdx}.date.beforeEvent`);\n            formIssues.remove(`err.service.${staleIdx}.amount.zero`);\n            formIssues.remove(`warn.service.${staleIdx}.invoice.missing`);\n            formIssues.remove(`warn.service.${staleIdx}.address.missing`);\n            formIssues.remove(`err.service.${staleIdx}.nip.missing`);\n            formIssues.remove(`err.service.${staleIdx}.nip.invalid`);\n            formIssues.remove(`warn.service.${staleIdx}.rpwdl.notFound`);\n            formIssues.remove(`warn.service.${staleIdx}.rpwdl.inactive`);\n            formIssues.remove(`success.service.${staleIdx}.rpwdl.ok`);\n        }\n\n        \/\/ Consent checkboxes - only after the first submit attempt.\n        if (formIssues.isSubmitAttempted()) {\n            const consents = [\n                { id: 'err.consent.medical', el: 'medicalConsent', text: 'Musisz zaznaczy\u0107 upowa\u017cnienie do dost\u0119pu do dokumentacji medycznej.' },\n                { id: 'err.consent.privacy', el: 'privacyPolicy', text: 'Musisz zaznaczy\u0107 zgod\u0119 na przetwarzanie danych osobowych.' },\n                { id: 'err.consent.accuracy', el: 'dataAccuracy', text: 'Musisz potwierdzi\u0107, \u017ce dane w formularzu s\u0105 zgodne z prawd\u0105.' },\n                { id: 'err.consent.communication', el: 'communicationConsent', text: 'Musisz wyrazi\u0107 zgod\u0119 na kontakt e-mailem w sprawie wniosku.' }\n            ];\n            consents.forEach(function (c) {\n                const cb = document.getElementById(c.el);\n                if (cb && !cb.checked) {\n                    formIssues.upsert(c.id, {\n                        severity: 'error',\n                        title: c.text,\n                        section: 'Zgody i o\u015bwiadczenia',\n                        sectionIndex: 6,\n                        fieldRef: '#' + c.el\n                    });\n                } else {\n                    formIssues.remove(c.id);\n                }\n            });\n        } else {\n            formIssues.removeMatching('err.consent.');\n        }\n    }\n\n    let revalidateTimer = null;\n    function revalidateAll(immediate) {\n        if (immediate) {\n            if (revalidateTimer) { clearTimeout(revalidateTimer); revalidateTimer = null; }\n            evalDeterministic();\n            return;\n        }\n        if (revalidateTimer) clearTimeout(revalidateTimer);\n        revalidateTimer = setTimeout(function () {\n            revalidateTimer = null;\n            evalDeterministic();\n        }, 200);\n    }\n\n    window.revalidateFormIssues = revalidateAll;\n\n    \/\/ Wire up field listeners\n    document.addEventListener('DOMContentLoaded', function () {\n        const liveFields = [\n            'firstName', 'lastName', 'pesel', 'birthDate',\n            'contactNumber', 'email',\n            'insuredPerson', 'insuredPesel',\n            'street', 'houseNumber', 'postalCode', 'city',\n            'bank', 'accountOwner', 'accountNumber',\n            'productType', 'eventDate', 'reason', 'description'\n        ];\n        liveFields.forEach(function (id) {\n            const el = document.getElementById(id);\n            if (!el) return;\n            el.addEventListener('input', function () { revalidateAll(false); });\n            el.addEventListener('change', function () { revalidateAll(false); });\n            el.addEventListener('blur', function () { revalidateAll(true); });\n        });\n\n        \/\/ \u015awiadczenia - deleguj na servicesContainer\n        const servicesContainer = document.getElementById('servicesContainer');\n        if (servicesContainer) {\n            servicesContainer.addEventListener('input', function () { revalidateAll(false); });\n            servicesContainer.addEventListener('change', function () { revalidateAll(false); });\n            servicesContainer.addEventListener('blur', function () { revalidateAll(true); }, true);\n        }\n\n        \/\/ Consents - revalidate on change (only takes effect after submitAttempted)\n        document.querySelectorAll('.consent-checkbox input[type=\"checkbox\"]').forEach(function (cb) {\n            cb.addEventListener('change', function () { revalidateAll(true); });\n        });\n\n        \/\/ Track the latest section the user clicked or focused into.\n        \/\/ Rules without bypassGate only show for sections lower than the current one.\n        \/\/ We listen to both focusin (keyboard \/ clicking an input) and input\/change because\n        \/\/ clicking a checkbox\/radio label does not always bubble focusin up to the section\n        \/\/ (e.g. mouse click on a label).\n        const formEl = document.querySelector('form');\n        if (formEl) {\n            const bumpFromEvent = function (e) {\n                const target = e.target;\n                if (!target || !target.closest) return;\n                const section = target.closest('section.form-section');\n                if (!section) {\n                    formIssues.markInteracted();\n                    return;\n                }\n                const idx = parseInt(section.dataset.sectionIndex, 10);\n                if (!isNaN(idx)) {\n                    formIssues.notifyFocusedSection(idx);\n                } else {\n                    formIssues.markInteracted();\n                }\n            };\n            formEl.addEventListener('focusin', bumpFromEvent);\n            formEl.addEventListener('input', bumpFromEvent);\n            formEl.addEventListener('change', bumpFromEvent);\n        }\n\n        wireRpwdlLookup();\n        if (window.refundVerify && typeof window.refundVerify.init === 'function') {\n            window.refundVerify.init();\n        }\n\n        \/\/ Initial evaluation.\n        revalidateAll(true);\n    });\n\n    \/\/ \/api\/refund\/verify integration. Optional pre-flight check that lets the user preview\n    \/\/ the per-\u015bwiadczenie verdict before posting to \/api\/refund\/issue\/create.\n    const refundVerify = (function () {\n        const VERIFY_ENDPOINT = '\/api\/refund\/verify';\n        const POLL_INTERVAL_MS = 10000;\n        const MAX_POLL_ATTEMPTS = 10;\n        \/\/ Hard timeout is a defensive cap. With 10 polls at 3 s each the loop bounds itself\n        \/\/ long before this fires, but we keep it as a backstop for a hung POST or stuck await.\n        const HARD_TIMEOUT_MS = 3 * 60 * 1000;\n\n        \/\/ All form-side category values are forwarded to the backend; no client-side filtering.\n\n        let inFlight = false;\n        let lastTaskId = null;\n        \/\/ 32-hex-char opaque capability token tied to the current task; required on every poll GET.\n        \/\/ Kept in module memory only; never logged, never persisted.\n        let lastPollToken = null;\n        let abortController = null;\n        let pollTimer = null;\n        let hardTimeoutId = null;\n        let lastSubtaskCount = 0;\n        let lastVerifyHadWarnings = false;\n        \/\/ Per-subtask FAILED outcomes from the last verify - drives the same submit-hint as warnings.\n        let lastVerifyHadFailures = false;\n        \/\/ True once a verify has finished at least once.\n        let everVerified = false;\n        \/\/ False for invoice-only policies without the shared Za\u0142\u0105czniki field: verify is hidden, submit goes direct.\n        let verificationEnabled = true;\n        \/\/ Set true when a verify attempt produced no per-service verdicts (POST 4xx\/5xx, network,\n        \/\/ hard timeout, 404 on poll, poll-attempts exhausted). The verify button stays enabled\n        \/\/ for retry per UX choice.\n        let lastVerifyWasWholeTaskFailure = false;\n\n        \/\/ Per-service verdict cache: map from origIdx -> 'ok' | 'warning' | 'failed'.\n        \/\/ Used to decide which services to re-send on re-verify, and (with dirtyOrigIdxSet)\n        \/\/ to decide whether a document-change should re-enable the verify button.\n        let lastVerdictByIdx = {};\n        \/\/ Services the user has touched since the last verify - candidates for re-verify.\n        let dirtyOrigIdxSet = new Set();\n        \/\/ Shared attachments touched since the last verify.\n        let dirtyAttachments = false;\n\n        let verifyBtn, submitBtn, verifyLoader, hintEl, modalEl, modalProgressEl,\n            confirmModalWarningEl;\n\n        function init() {\n            verifyBtn = document.getElementById('verifyButton');\n            submitBtn = document.getElementById('submitButton');\n            verifyLoader = document.getElementById('verifyLoader');\n            hintEl = document.getElementById('verifyWarningHint');\n            modalEl = document.getElementById('verifyProgressModal');\n            modalProgressEl = document.getElementById('verifyProgressText');\n            confirmModalWarningEl = document.getElementById('confirmModalVerifyWarning');\n\n            if (verifyBtn) verifyBtn.addEventListener('click', runVerify);\n\n            \/\/ Stale-state listener: any category change invalidates verify verdicts.\n            const servicesContainer = document.getElementById('servicesContainer');\n            if (servicesContainer) {\n                servicesContainer.addEventListener('change', function (e) {\n                    const t = e.target;\n                    if (t && t.tagName === 'SELECT' && t.name === 'serviceName[]') {\n                        markStale();\n                    }\n                });\n            }\n\n            \/\/ Consent-gate: verify button is disabled until every required consent checkbox is ticked.\n            document.querySelectorAll('.consent-checkbox input[type=\"checkbox\"][required]').forEach(function (cb) {\n                cb.addEventListener('change', updateVerifyButtonAvailability);\n            });\n            updateVerifyButtonAvailability();\n\n            \/\/ Mirror the current policy: the verify step applies only when the shared Za\u0142\u0105czniki field is present and visible.\n            const attachmentsContainer = document.getElementById('attachmentsContainer');\n            setVerificationEnabled(!!attachmentsContainer && !attachmentsContainer.classList.contains('hidden'));\n        }\n\n        function allConsentsChecked() {\n            const required = document.querySelectorAll('.consent-checkbox input[type=\"checkbox\"][required]');\n            for (let i = 0; i < required.length; i++) {\n\t\t\t\/\/> this comment is used to circumvent the https:\/\/core.trac.wordpress.org\/ticket\/43785 bug\n                if (!required[i].checked) return false;\n            }\n            return true;\n        }\n\n        function needsVerification() {\n            \/\/ Before the first verify, the button is always \"needed\" (assuming consents tick).\n            if (!everVerified) return true;\n            \/\/ Whole-task failure: user can retry directly with no document edits.\n            if (lastVerifyWasWholeTaskFailure) return true;\n            if (dirtyAttachments) return true;\n            if (dirtyOrigIdxSet.size > 0) return true;\n            return false;\n        }\n\n        function updateVerifyButtonAvailability() {\n            if (!verifyBtn) return;\n            \/\/ Invoice-only policies hide the verify button entirely; nothing to gate.\n            if (!verificationEnabled) return;\n            \/\/ While a verify is in flight, runVerify owns the disabled state (button locked during the call).\n            if (inFlight) return;\n            const consentsOk = allConsentsChecked();\n            const needed = needsVerification();\n            const ok = consentsOk && needed;\n            verifyBtn.disabled = !ok;\n            verifyBtn.classList.toggle('opacity-50', !ok);\n            verifyBtn.classList.toggle('cursor-not-allowed', !ok);\n            if (ok) {\n                verifyBtn.removeAttribute('title');\n            } else if (!consentsOk) {\n                verifyBtn.setAttribute('title', 'Zaznacz wszystkie zgody i o\u015bwiadczenia, aby zweryfikowa\u0107 wniosek');\n            } else {\n                verifyBtn.setAttribute('title', 'Zmie\u0144 dokumenty w niezweryfikowanym \u015bwiadczeniu lub dokumentacji wsp\u00f3lnej, aby ponownie zweryfikowa\u0107');\n            }\n        }\n\n        \/\/ Toggles the whole verify step on\/off based on whether the chosen policy exposes the\n        \/\/ shared Za\u0142\u0105czniki field. Invoice-only policies have no checklist on the backend, so we\n        \/\/ hide the verify button and let the form submit directly once standard validation passes.\n        function setVerificationEnabled(enabled) {\n            verificationEnabled = !!enabled;\n            if (!verificationEnabled) {\n                if (verifyBtn) verifyBtn.classList.add('hidden');\n                if (submitBtn) {\n                    submitBtn.disabled = false;\n                    submitBtn.classList.remove('opacity-50', 'cursor-not-allowed');\n                    submitBtn.removeAttribute('title');\n                }\n                if (hintEl) hintEl.classList.add('hidden');\n                if (confirmModalWarningEl) confirmModalWarningEl.classList.add('hidden');\n                \/\/ Drop any verdicts left over from a previously selected policy that had verification.\n                if (window.formIssues) {\n                    formIssues.removeMatching('warn.verify.');\n                    formIssues.removeMatching('success.verify.');\n                    formIssues.removeMatching('info.verify.');\n                    formIssues.removeMatching('err.verify.');\n                }\n                lastVerifyHadWarnings = false;\n                lastVerifyHadFailures = false;\n                lastVerifyWasWholeTaskFailure = false;\n                lastVerdictByIdx = {};\n                dirtyOrigIdxSet.clear();\n                dirtyAttachments = false;\n                everVerified = false;\n                return;\n            }\n            if (verifyBtn) verifyBtn.classList.remove('hidden');\n            \/\/ Re-arm the verify-first gate unless a verify has already run this session (success or failure).\n            if (!everVerified && submitBtn) {\n                submitBtn.disabled = true;\n                submitBtn.classList.add('opacity-50', 'cursor-not-allowed');\n                submitBtn.setAttribute('title', 'Najpierw zweryfikuj wniosek');\n            }\n            updateVerifyButtonAvailability();\n        }\n\n        function isVerificationEnabled() { return verificationEnabled; }\n\n        \/\/ Selects which service indices to include in the current verify run.\n        \/\/ First run: send everything. Whole-task-failure retry: send everything not 'ok'.\n        \/\/ Re-verify: only services that aren't 'ok' or were marked dirty by the user.\n        function pickServicesToVerify() {\n            const groups = document.querySelectorAll('.service-group');\n            const total = groups.length;\n            const out = [];\n            if (!everVerified) {\n                for (let i = 0; i < total; i++) out.push(i);\n\t\t\t\t\/\/> this comment is used to circumvent the https:\/\/core.trac.wordpress.org\/ticket\/43785 bug\n                return out;\n            }\n            if (lastVerifyWasWholeTaskFailure) {\n                for (let i = 0; i < total; i++) {\n\t\t\t\t\/\/> this comment is used to circumvent the https:\/\/core.trac.wordpress.org\/ticket\/43785 bug\n                    if (lastVerdictByIdx[i] !== 'ok') out.push(i);\n                }\n                \/\/ Defensive: if everything is somehow 'ok' but we still failed, send everything.\n                if (out.length === 0) for (let i = 0; i < total; i++) out.push(i);\n                return out;\n            }\n            for (let i = 0; i < total; i++) {\n\t\t\t\/\/> this comment is used to circumvent the https:\/\/core.trac.wordpress.org\/ticket\/43785 bug\n                const verdict = lastVerdictByIdx[i];\n                if (verdict !== 'ok') out.push(i);\n                else if (dirtyOrigIdxSet.has(i)) out.push(i);\n            }\n            \/\/ Attachments-only change with no per-service dirty: spec says \"re-verify is enabled\" -\n            \/\/ so we resend all non-ok services (which is what we did above), but if EVERY service is\n            \/\/ 'ok' and only attachments changed, we still resend everything so the LLM gets the new docs.\n            if (out.length === 0 && dirtyAttachments) {\n                for (let i = 0; i < total; i++) out.push(i);\n\t\t\t\t\/\/> this comment is used to circumvent the https:\/\/core.trac.wordpress.org\/ticket\/43785 bug\n            }\n            return out;\n        }\n\n        function buildVerifyFormData(serviceGroups, includeIdxList) {\n            const formData = new FormData();\n            const toSend = [];\n            const includeSet = new Set(includeIdxList);\n\n            serviceGroups.forEach(function (group, origIdx) {\n                if (!includeSet.has(origIdx)) return;\n                const select = group.querySelector('select[name=\"serviceName[]\"]');\n                const raw = (select && select.value) ? select.value.trim() : '';\n                const verifyIdx = toSend.length;\n                toSend.push({ origIdx: origIdx, category: raw });\n                formData.append('serviceName[]', raw);\n                \/\/ Re-index invoiceFileN contiguously per the API contract; the server echoes the\n                \/\/ same index back as invoiceFileIndex and toSend[invoiceFileIndex].origIdx maps it\n                \/\/ to the original service-group.\n                const files = (typeof fileStores !== 'undefined' && fileStores['invoiceFile' + origIdx]) || [];\n                if (files.length > 0) {\n                    formData.append('invoiceFile' + verifyIdx, files[0]);\n                }\n            });\n\n            const attachments = (typeof fileStores !== 'undefined' && fileStores['attachments']) || [];\n            attachments.forEach(function (f) {\n                formData.append('attachments[]', f);\n            });\n\n            return { formData: formData, toSend: toSend };\n        }\n\n        function showOverlay() {\n            if (modalEl) modalEl.classList.add('show');\n            document.body.style.overflow = 'hidden';\n        }\n\n        function hideOverlay() {\n            if (modalEl) modalEl.classList.remove('show');\n            document.body.style.overflow = '';\n        }\n\n        function setProgressText(t) {\n            if (modalProgressEl) modalProgressEl.textContent = t;\n        }\n\n        function cleanupInFlight() {\n            inFlight = false;\n            abortController = null;\n            if (pollTimer) { clearTimeout(pollTimer); pollTimer = null; }\n            if (hardTimeoutId) { clearTimeout(hardTimeoutId); hardTimeoutId = null; }\n            hideOverlay();\n            if (verifyLoader) verifyLoader.classList.remove('show');\n            \/\/ Re-evaluate against consent + needs-verification gates.\n            updateVerifyButtonAvailability();\n        }\n\n        function finalizePass(submitHint) {\n            everVerified = true;\n\n            if (submitBtn) {\n                submitBtn.disabled = false;\n                submitBtn.classList.remove('opacity-50', 'cursor-not-allowed');\n                submitBtn.removeAttribute('title');\n            }\n            if (submitHint) {\n                if (hintEl) hintEl.classList.remove('hidden');\n            } else {\n                if (hintEl) hintEl.classList.add('hidden');\n            }\n            \/\/ Verify button stays visible; disabled state is computed by needsVerification()\n            \/\/ via updateVerifyButtonAvailability() in cleanupInFlight().\n            if (verifyBtn) verifyBtn.classList.remove('hidden');\n        }\n\n        function markStale() {\n            \/\/ Wipe-everything path - used when the user adds\/removes a service or changes a category.\n            if (!everVerified) {\n                \/\/ Even before the first verify, clear any stragglers (defensive).\n                if (window.formIssues) {\n                    formIssues.removeMatching('warn.verify.');\n                    formIssues.removeMatching('success.verify.');\n                    formIssues.removeMatching('info.verify.');\n                    formIssues.removeMatching('err.verify.');\n                }\n                lastVerdictByIdx = {};\n                dirtyOrigIdxSet.clear();\n                dirtyAttachments = false;\n                updateVerifyButtonAvailability();\n                return;\n            }\n            if (window.formIssues) {\n                formIssues.removeMatching('warn.verify.');\n                formIssues.removeMatching('success.verify.');\n                formIssues.removeMatching('info.verify.');\n                formIssues.removeMatching('err.verify.');\n            }\n            lastVerifyHadWarnings = false;\n            lastVerifyHadFailures = false;\n            lastVerifyWasWholeTaskFailure = false;\n            lastVerdictByIdx = {};\n            dirtyOrigIdxSet.clear();\n            dirtyAttachments = false;\n            everVerified = false;\n\n            if (submitBtn) {\n                submitBtn.disabled = true;\n                submitBtn.classList.add('opacity-50', 'cursor-not-allowed');\n                submitBtn.setAttribute('title', 'Najpierw zweryfikuj wniosek');\n            }\n            if (verifyBtn) verifyBtn.classList.remove('hidden');\n            if (hintEl) hintEl.classList.add('hidden');\n            if (confirmModalWarningEl) confirmModalWarningEl.classList.add('hidden');\n            updateVerifyButtonAvailability();\n        }\n\n        function markServiceDirty(origIdx) {\n            if (!everVerified) return;\n            dirtyOrigIdxSet.add(origIdx);\n            updateVerifyButtonAvailability();\n        }\n\n        function markAttachmentsDirty() {\n            if (!everVerified) return;\n            dirtyAttachments = true;\n            updateVerifyButtonAvailability();\n        }\n\n        function notifyServiceAdded() {\n            if (!everVerified) {\n                updateVerifyButtonAvailability();\n                return;\n            }\n            const groups = document.querySelectorAll('.service-group');\n            const newIdx = groups.length - 1;\n            dirtyOrigIdxSet.add(newIdx);\n            updateVerifyButtonAvailability();\n        }\n\n        \/\/ Drop the removed index from caches and shift higher ones down by one.\n        \/\/ Re-emit formIssues entries under the new indices using the cached verdict kinds.\n        function notifyServiceRemoved(removedIdx) {\n            if (!everVerified) {\n                updateVerifyButtonAvailability();\n                return;\n            }\n            const newVerdictMap = {};\n            const newDirty = new Set();\n            Object.keys(lastVerdictByIdx).forEach(function (kStr) {\n                const k = parseInt(kStr, 10);\n                if (k === removedIdx) return;\n                const nk = k > removedIdx ? k - 1 : k;\n                newVerdictMap[nk] = lastVerdictByIdx[k];\n            });\n            dirtyOrigIdxSet.forEach(function (k) {\n                if (k === removedIdx) return;\n                newDirty.add(k > removedIdx ? k - 1 : k);\n            });\n            lastVerdictByIdx = newVerdictMap;\n            dirtyOrigIdxSet = newDirty;\n            rekeyVerifyEntries();\n            \/\/ Recompute the \"is there any non-ok left?\" hint flags.\n            let anyWarn = false, anyFail = false;\n            Object.keys(lastVerdictByIdx).forEach(function (kStr) {\n                const v = lastVerdictByIdx[parseInt(kStr, 10)];\n                if (v === 'warning') anyWarn = true;\n                else if (v === 'failed') anyFail = true;\n            });\n            lastVerifyHadWarnings = anyWarn;\n            lastVerifyHadFailures = anyFail;\n            if (hintEl) hintEl.classList.toggle('hidden', !(anyWarn || anyFail));\n            updateVerifyButtonAvailability();\n        }\n\n        \/\/ Wipe and rebuild verify-emitted formIssues entries from the current lastVerdictByIdx.\n        \/\/ Detailed warning\/failure text is not preserved (formIssues has no public iteration);\n        \/\/ we emit a single placeholder info entry per non-ok service so the user is reminded to\n        \/\/ re-verify, and re-add it to the dirty set so the button enables.\n        function rekeyVerifyEntries() {\n            const groups = document.querySelectorAll('.service-group');\n            formIssues.removeMatching('warn.verify.');\n            formIssues.removeMatching('success.verify.');\n            formIssues.removeMatching('info.verify.');\n            formIssues.removeMatching('err.verify.');\n            Object.keys(lastVerdictByIdx).forEach(function (kStr) {\n                const k = parseInt(kStr, 10);\n                const v = lastVerdictByIdx[k];\n                const n = k + 1;\n                const group = groups[k];\n                const select = group ? group.querySelector('select[name=\"serviceName[]\"]') : null;\n                const categoryLabel = (select && select.value) || '';\n                if (v === 'ok') {\n                    formIssues.upsert('success.verify.service.' + k + '.ok', {\n                        severity: 'success',\n                        title: '\u015awiadczenie nr ' + n + ' (' + categoryLabel + '): dokumenty wygl\u0105daj\u0105 poprawnie.',\n                        section: '\u015awiadczenie ' + n,\n                        bypassGate: true\n                    });\n                } else if (v === 'warning' || v === 'failed') {\n                    formIssues.upsert('info.verify.service.' + k + '.needsReverify', {\n                        severity: 'info',\n                        title: '\u015awiadczenie nr ' + n + ' (' + categoryLabel + '): wymaga ponownej weryfikacji po zmianie kolejno\u015bci \u015bwiadcze\u0144.',\n                        section: '\u015awiadczenie ' + n,\n                        bypassGate: true\n                    });\n                    dirtyOrigIdxSet.add(k);\n                }\n            });\n        }\n\n        \/\/ Map error_code to a Polish toast. Actionable codes (files\/category\/AV\/token) warn + expand the panel; soft codes stay quiet info.\n        function applyTaskLevelError(errBody) {\n            const code = (errBody && errBody.error_code) || 'UNKNOWN';\n            let title;\n            let actionable = false;\n            switch (code) {\n                case 'RATE_LIMIT_CREATE': {\n                    const wait = errBody && errBody.retry_after_seconds;\n                    title = wait\n                        ? 'Zbyt wiele pr\u00f3b weryfikacji. Spr\u00f3buj ponownie za ' + wait + ' s.'\n                        : 'Zbyt wiele pr\u00f3b weryfikacji. Spr\u00f3buj ponownie za kilka minut.';\n                    break;\n                }\n                case 'RATE_LIMIT_POLL': {\n                    title = 'Zbyt wiele pr\u00f3b pobrania wyniku. Odczekaj chwil\u0119 i spr\u00f3buj ponownie.';\n                    break;\n                }\n                case 'VALIDATION_FILES':\n                case 'VALIDATION_SWIADCZENIA': {\n                    const detail = ((errBody && errBody.errors) || [])\n                        .map(function (e) { return e && e.message; })\n                        .filter(Boolean)\n                        .join(' ');\n                    title = detail\n                        ? 'Weryfikacja odrzucona: ' + detail\n                        : 'Plik lub dane \u015bwiadczenia odrzucone przez serwer. Sprawd\u017a pliki i spr\u00f3buj ponownie.';\n                    actionable = true;\n                    break;\n                }\n                case 'VALIDATION_UNKNOWN_CATEGORY':\n                    title = 'Wybrana kategoria \u015bwiadczenia nie jest obs\u0142ugiwana w weryfikacji.';\n                    break;\n                case 'ANTIVIRUS_INFECTED': {\n                    const fname = (errBody && errBody.filename) || 'plik';\n                    title = 'Plik \"' + fname + '\" zosta\u0142 odrzucony przez skaner antywirusowy. Wgraj inny plik.';\n                    actionable = true;\n                    break;\n                }\n                case 'INPUT_TOKEN_LIMIT_EXCEEDED': {\n                    const subIdx = (errBody && typeof errBody.subtask_index === 'number') ? errBody.subtask_index : null;\n                    const sn = subIdx !== null ? (subIdx + 1) : '?';\n                    title = '\u015awiadczenie nr ' + sn + ': dokumenty s\u0105 zbyt obszerne. Zmniejsz liczb\u0119 lub rozmiar plik\u00f3w dla tego \u015bwiadczenia lub w dokumentacji wsp\u00f3lnej.';\n                    actionable = true;\n                    break;\n                }\n                case 'TASK_NOT_FOUND':\n                    title = 'Sesja weryfikacji wygas\u0142a. Spr\u00f3buj ponownie.';\n                    break;\n                case 'POLL_ATTEMPTS_EXHAUSTED':\n                    title = 'Weryfikacja trwa zbyt d\u0142ugo. Mo\u017cesz wys\u0142a\u0107 wniosek bez niej lub spr\u00f3bowa\u0107 ponownie p\u00f3\u017aniej.';\n                    break;\n                default:\n                    title = 'Nie uda\u0142o si\u0119 przeprowadzi\u0107 weryfikacji. Mo\u017cesz wys\u0142a\u0107 wniosek bez niej lub spr\u00f3bowa\u0107 ponownie.';\n            }\n            formIssues.upsert('info.verify.task.error', {\n                severity: actionable ? 'warning' : 'info',\n                title: title,\n                section: 'Weryfikacja wniosku',\n                bypassGate: true\n            });\n            if (actionable && window.formIssues) {\n                formIssues.setCollapsed(false);\n                formIssues.pulsePanel();\n            }\n        }\n\n        function applyPollError(err) {\n            const msg = (err && err.message) || '';\n            \/\/ pollVerify throws 'poll HTTP 404' on a missing task \/ bad pollToken,\n            \/\/ and 'poll attempts exhausted' when the per-task poll budget is used up.\n            if (\/HTTP\\s*404\/.test(msg)) {\n                applyTaskLevelError({ error_code: 'TASK_NOT_FOUND' });\n            } else if (\/poll attempts exhausted\/.test(msg)) {\n                applyTaskLevelError({ error_code: 'POLL_ATTEMPTS_EXHAUSTED' });\n            } else {\n                applyTaskLevelError({ error_code: 'UNKNOWN' });\n            }\n        }\n\n        function applyVerdicts(body, built, includeIdxList) {\n            \/\/ Clear stale entries ONLY for services we just re-verified. Services not in\n            \/\/ includeIdxList keep their existing entries (success carries over across re-verifies).\n            includeIdxList.forEach(function (idx) {\n                formIssues.removeMatching('warn.verify.service.' + idx + '.');\n                formIssues.removeMatching('success.verify.service.' + idx + '.');\n                formIssues.removeMatching('info.verify.service.' + idx + '.');\n                formIssues.removeMatching('err.verify.service.' + idx + '.');\n            });\n\n            let hadWarnings = false;\n            let hadFailures = false;\n\n            if (!body || body.status === 'FAILED' || !Array.isArray(body.swiadczenia)) {\n                \/\/ Parent FAILED with no usable per-service breakdown - flag every included\n                \/\/ service so the user sees at least something. Reuses the info-toast severity.\n                includeIdxList.forEach(function (idx) {\n                    const n = idx + 1;\n                    lastVerdictByIdx[idx] = 'failed';\n                    formIssues.upsert('info.verify.service.' + idx + '.taskFailed', {\n                        severity: 'info',\n                        title: '\u015awiadczenie nr ' + n + ': nie zosta\u0142o zweryfikowane (b\u0142\u0105d serwera).',\n                        section: '\u015awiadczenie ' + n,\n                        bypassGate: true\n                    });\n                    hadFailures = true;\n                });\n                \/\/ Combine with prior verdict-warnings from other services that weren't re-verified.\n                Object.keys(lastVerdictByIdx).forEach(function (kStr) {\n                    const v = lastVerdictByIdx[parseInt(kStr, 10)];\n                    if (v === 'warning') hadWarnings = true;\n                    else if (v === 'failed') hadFailures = true;\n                });\n                lastVerifyHadWarnings = hadWarnings;\n                lastVerifyHadFailures = hadFailures;\n                return;\n            }\n\n            body.swiadczenia.forEach(function (s) {\n                const verifyIdx = (typeof s.invoiceFileIndex === 'number') ? s.invoiceFileIndex : null;\n                const origInfo = (verifyIdx !== null && built.toSend[verifyIdx]) || null;\n                const origIdx = origInfo ? origInfo.origIdx : verifyIdx;\n                const n = origIdx + 1;\n                const groupEl = document.querySelectorAll('.service-group')[origIdx];\n                const zone = groupEl ? groupEl.querySelector('.file-upload-zone') : null;\n                const categoryLabel = s.categoryLabel || (origInfo && origInfo.category) || '';\n\n                if (s.status === 'FAILED') {\n                    const subCode = s.errorCode || 'UNKNOWN';\n                    let subTitle;\n                    switch (subCode) {\n                        case 'GENERIC_AI_CALL_FAILED':\n                            subTitle = '\u015awiadczenie nr ' + n + ' (' + categoryLabel + '): weryfikacja nie powiod\u0142a si\u0119. Spr\u00f3buj ponownie za chwil\u0119.';\n                            break;\n                        case 'GENERIC_AI_RESPONSE_EMPTY':\n                        case 'GENERIC_AI_RESPONSE_INVALID':\n                            subTitle = '\u015awiadczenie nr ' + n + ' (' + categoryLabel + '): wyst\u0105pi\u0142 b\u0142\u0105d techniczny. Skontaktuj si\u0119 z pomoc\u0105 techniczn\u0105 lub wy\u015blij wniosek bez weryfikacji.';\n                            break;\n                        case 'INPUT_TOKEN_LIMIT_EXCEEDED':\n                            subTitle = '\u015awiadczenie nr ' + n + ' (' + categoryLabel + '): dokumenty s\u0105 zbyt obszerne. Zmniejsz liczb\u0119 lub rozmiar plik\u00f3w dla tego \u015bwiadczenia lub w dokumentacji wsp\u00f3lnej.';\n                            break;\n                        default:\n                            \/\/ s.error is sanitised by the backend (no internal-service leaks).\n                            subTitle = '\u015awiadczenie nr ' + n + ' (' + categoryLabel + '): nie uda\u0142o si\u0119 zweryfikowa\u0107 dokument\u00f3w.' + (s.error ? ' ' + s.error : '');\n                    }\n                    formIssues.upsert('info.verify.service.' + origIdx + '.failed', {\n                        severity: 'info',\n                        title: subTitle,\n                        section: '\u015awiadczenie ' + n,\n                        bypassGate: true,\n                        fieldRef: zone\n                    });\n                    lastVerdictByIdx[origIdx] = 'failed';\n                    hadFailures = true;\n                    return;\n                }\n                if (s.status !== 'COMPLETED' || !s.verdict) return;\n\n                if (s.verdict.ok === true) {\n                    formIssues.upsert('success.verify.service.' + origIdx + '.ok', {\n                        severity: 'success',\n                        title: '\u015awiadczenie nr ' + n + ' (' + categoryLabel + '): dokumenty wygl\u0105daj\u0105 poprawnie.',\n                        section: '\u015awiadczenie ' + n,\n                        bypassGate: true\n                    });\n                    lastVerdictByIdx[origIdx] = 'ok';\n                    return;\n                }\n\n                const missing = Array.isArray(s.verdict.missing) ? s.verdict.missing : [];\n\n                if (missing.length === 0) {\n                    \/\/ Defensive: contract says ok=false implies non-empty missing[], but handle anyway.\n                    hadWarnings = true;\n                    lastVerdictByIdx[origIdx] = 'warning';\n                    formIssues.upsert('warn.verify.service.' + origIdx + '.note', {\n                        severity: 'warning',\n                        title: '\u015awiadczenie nr ' + n + ' (' + categoryLabel + '): ' + (s.verdict.note || 'weryfikacja zwr\u00f3ci\u0142a zastrze\u017cenia.'),\n                        section: '\u015awiadczenie ' + n,\n                        bypassGate: true,\n                        fieldRef: zone\n                    });\n                    return;\n                }\n\n                hadWarnings = true;\n                lastVerdictByIdx[origIdx] = 'warning';\n                const attachmentsZone = document.querySelector('.file-upload-zone[data-upload-for=\"attachments\"]');\n                missing.forEach(function (item) {\n                    const rawId = String(item.itemId || 'unknown');\n                    const safeItemId = rawId.replace(\/[^a-zA-Z0-9_-]\/g, '_');\n                    \/\/ Invoice items belong to the service dropzone; every other document type goes to the shared attachments zone.\n                    const isInvoiceItem = \/_invoice$\/.test(rawId);\n                    const targetRef = isInvoiceItem ? zone : (attachmentsZone || zone);\n                    formIssues.upsert('warn.verify.service.' + origIdx + '.missing.' + safeItemId, {\n                        severity: 'warning',\n                        title: '\u015awiadczenie nr ' + n + ' (' + categoryLabel + '): ' + (item.reason || item.itemLabel || 'uzupe\u0142nij brakuj\u0105ce informacje.'),\n                        section: isInvoiceItem ? ('\u015awiadczenie ' + n) : 'Za\u0142\u0105czniki',\n                        bypassGate: true,\n                        fieldRef: targetRef\n                    });\n                });\n            });\n\n            \/\/ Combine with prior verdicts from services we did NOT re-verify this run.\n            Object.keys(lastVerdictByIdx).forEach(function (kStr) {\n                const k = parseInt(kStr, 10);\n                if (includeIdxList.indexOf(k) !== -1) return;\n                const v = lastVerdictByIdx[k];\n                if (v === 'warning') hadWarnings = true;\n                else if (v === 'failed') hadFailures = true;\n            });\n\n            lastVerifyHadWarnings = hadWarnings;\n            lastVerifyHadFailures = hadFailures;\n        }\n\n        function pollVerify(taskId, pollToken, signal) {\n            const baseUrl = (window.TELEMEDI_API_BASE_URL || '').replace(\/\\\/+$\/, '');\n            \/\/ pollToken is required - server returns 404 (indistinguishable from \"unknown task\") without it.\n            const url = baseUrl + VERIFY_ENDPOINT + '\/' + encodeURIComponent(taskId)\n                + '?pollToken=' + encodeURIComponent(pollToken);\n\n            return new Promise(function (resolve, reject) {\n                let attempts = 0;\n                function tick() {\n                    if (signal.aborted) { reject(new Error('aborted')); return; }\n                    attempts++;\n                    fetch(url, { signal: signal })\n                        .then(function (res) {\n                            if (!res.ok) throw new Error('poll HTTP ' + res.status);\n                            return res.json();\n                        })\n                        .then(function (body) {\n                            const completed = (body.swiadczenia || []).filter(function (s) {\n                                return s.status !== 'PENDING';\n                            }).length;\n                            setProgressText('Weryfikujemy dokumenty (' + completed + '\/' + lastSubtaskCount + ')...');\n\n                            if (body.status !== 'PENDING') {\n                                resolve(body);\n                                return;\n                            }\n                            if (attempts >= MAX_POLL_ATTEMPTS) {\n                                \/\/ Give up: the backend hasn't finished within our poll budget.\n                                \/\/ Surface as a whole-task failure with a distinct error message\n                                \/\/ so applyPollError can render an appropriate Polish toast.\n                                reject(new Error('poll attempts exhausted'));\n                                return;\n                            }\n                            pollTimer = setTimeout(tick, POLL_INTERVAL_MS);\n                        })\n                        .catch(function (err) { reject(err); });\n                }\n                tick();\n            });\n        }\n\n        async function runVerify() {\n            if (inFlight) return;\n\n            if (window.formIssues) {\n                formIssues.markSubmitAttempted();\n                if (typeof window.revalidateFormIssues === 'function') {\n                    window.revalidateFormIssues(true);\n                }\n                formIssues.removeMatching('err.verify.');\n            }\n\n            const serviceGroups = document.querySelectorAll('.service-group');\n            \/\/ Only check invoice presence for services we're about to verify; passed services\n            \/\/ already supplied a valid invoice, and we don't want a missing file in a passed\n            \/\/ service to block re-verifying a different one.\n            const candidateIdxList = pickServicesToVerify();\n            candidateIdxList.forEach(function (idx) {\n                const group = serviceGroups[idx];\n                if (!group) return;\n                const files = (typeof fileStores !== 'undefined' && fileStores['invoiceFile' + idx]) || [];\n                if (files.length === 0) {\n                    const zone = group.querySelector('.file-upload-zone');\n                    formIssues.upsert('err.verify.service.' + idx + '.invoiceFile.missing', {\n                        severity: 'error',\n                        title: '\u015awiadczenie nr ' + (idx + 1) + ': do\u0142\u0105cz faktur\u0119\/rachunek przed weryfikacj\u0105.',\n                        section: '\u015awiadczenie ' + (idx + 1),\n                        bypassGate: true,\n                        fieldRef: zone || group\n                    });\n                }\n            });\n\n            const snap = (window.formIssues && formIssues.snapshot)\n                ? formIssues.snapshot() : { errorCount: 0 };\n            if (snap.errorCount > 0) {\n                if (window.formIssues) {\n                    formIssues.setCollapsed(false);\n                    formIssues.pulsePanel();\n                }\n                return;\n            }\n\n            if (candidateIdxList.length === 0) {\n                \/\/ Defensive: button shouldn't enable when there's nothing to send.\n                return;\n            }\n\n            const built = buildVerifyFormData(serviceGroups, candidateIdxList);\n\n            inFlight = true;\n            lastVerifyWasWholeTaskFailure = false;\n            showOverlay();\n            setProgressText('Przesy\u0142anie dokument\u00f3w...');\n            if (verifyLoader) verifyLoader.classList.add('show');\n            if (verifyBtn) verifyBtn.disabled = true;\n\n            \/\/ Clear info entries for services we're about to verify + any prior whole-task toast.\n            formIssues.removeMatching('info.verify.task.');\n            candidateIdxList.forEach(function (idx) {\n                formIssues.removeMatching('info.verify.service.' + idx + '.');\n            });\n\n            abortController = new AbortController();\n            hardTimeoutId = setTimeout(function () {\n                try { abortController.abort(); } catch (e) { \/* ignored *\/ }\n            }, HARD_TIMEOUT_MS);\n\n            const baseUrl = (window.TELEMEDI_API_BASE_URL || '').replace(\/\\\/+$\/, '');\n            const url = baseUrl + VERIFY_ENDPOINT;\n\n            try {\n                const res = await fetch(url, {\n                    method: 'POST',\n                    body: built.formData,\n                    signal: abortController.signal\n                });\n                if (!res.ok) {\n                    \/\/ Try to parse the structured error body; tolerate empty\/non-JSON.\n                    let errBody = {};\n                    try { errBody = await res.json(); } catch (e) { \/* ignored *\/ }\n                    applyTaskLevelError(errBody);\n                    lastVerifyWasWholeTaskFailure = true;\n                    everVerified = true;\n                    finalizePass(lastVerifyHadWarnings || lastVerifyHadFailures);\n                    return;\n                }\n                const body = await res.json();\n                if (!body || !body.pollToken) {\n                    applyTaskLevelError({ error_code: 'UNKNOWN' });\n                    lastVerifyWasWholeTaskFailure = true;\n                    everVerified = true;\n                    finalizePass(lastVerifyHadWarnings || lastVerifyHadFailures);\n                    return;\n                }\n                lastTaskId = body.taskId;\n                lastPollToken = body.pollToken;\n                lastSubtaskCount = body.subtaskCount || built.toSend.length;\n                setProgressText('Weryfikujemy dokumenty (0\/' + lastSubtaskCount + ')...');\n\n                let finalBody;\n                try {\n                    finalBody = await pollVerify(lastTaskId, lastPollToken, abortController.signal);\n                } catch (pollErr) {\n                    applyPollError(pollErr);\n                    lastVerifyWasWholeTaskFailure = true;\n                    everVerified = true;\n                    finalizePass(lastVerifyHadWarnings || lastVerifyHadFailures);\n                    return;\n                }\n                applyVerdicts(finalBody, built, candidateIdxList);\n                finalizePass(lastVerifyHadWarnings || lastVerifyHadFailures);\n                \/\/ Successful run (any outcome including partial): the services we verified are\n                \/\/ no longer dirty from the user's perspective.\n                candidateIdxList.forEach(function (idx) { dirtyOrigIdxSet.delete(idx); });\n                dirtyAttachments = false;\n            } catch (err) {\n                console.warn('refund-verify:', err);\n                applyTaskLevelError({ error_code: 'UNKNOWN' });\n                lastVerifyWasWholeTaskFailure = true;\n                everVerified = true;\n                finalizePass(lastVerifyHadWarnings || lastVerifyHadFailures);\n            } finally {\n                cleanupInFlight();\n            }\n        }\n\n        function isInFlight() { return inFlight; }\n        function hasEverVerified() { return everVerified; }\n        function hasVerifyWarnings() { return lastVerifyHadWarnings || lastVerifyHadFailures; }\n\n        return {\n            init: init,\n            runVerify: runVerify,\n            isInFlight: isInFlight,\n            hasEverVerified: hasEverVerified,\n            hasVerifyWarnings: hasVerifyWarnings,\n            setVerificationEnabled: setVerificationEnabled,\n            isVerificationEnabled: isVerificationEnabled,\n            markStale: markStale,\n            markServiceDirty: markServiceDirty,\n            markAttachmentsDirty: markAttachmentsDirty,\n            notifyServiceAdded: notifyServiceAdded,\n            notifyServiceRemoved: notifyServiceRemoved\n        };\n    })();\n    window.refundVerify = refundVerify;\n\n    const RPWDL_CHECK_ENDPOINT = '\/api\/public\/rpwdl\/check';\n    \/\/ 4s covers the backend's worst-case CeZ chain (~3s total budget per the backend\n    \/\/ handoff) plus a buffer for slow networks. Lookup is non-blocking for form submit,\n    \/\/ so timing out longer than this only delays the panel entry, never the user.\n    const RPWDL_LOOKUP_TIMEOUT_MS = 4000;\n    const RPWDL_DEBOUNCE_MS = 400;\n\n    function getServiceIndex(group) {\n        if (!group) return -1;\n        const raw = group.getAttribute('data-service-index');\n        const n = parseInt(raw, 10);\n        return isNaN(n) ? -1 : n;\n    }\n\n    function clearRpwdlLookupRules(idx) {\n        formIssues.remove(`warn.service.${idx}.rpwdl.notFound`);\n        formIssues.remove(`warn.service.${idx}.rpwdl.inactive`);\n        formIssues.remove(`success.service.${idx}.rpwdl.ok`);\n    }\n\n    function setNipHint(group, text) {\n        const hint = group.querySelector('.service-nip-hint');\n        if (!hint) return;\n        if (text) {\n            hint.textContent = text;\n            hint.classList.remove('hidden');\n        } else {\n            hint.textContent = '';\n            hint.classList.add('hidden');\n        }\n    }\n\n    function lookupRpwdl(group, nipClean, dateIso) {\n        const idx = getServiceIndex(group);\n        if (idx < 0) return;\n\t\t\/\/> this comment is used to circumvent the https:\/\/core.trac.wordpress.org\/ticket\/43785 bug\n        const n = idx + 1;\n        const nipInput = group.querySelector('.service-nip');\n        if (!nipInput) return;\n\n        if (group._rpwdlAbort) {\n            try { group._rpwdlAbort.abort(); } catch (e) { \/* ignored *\/ }\n        }\n        const controller = new AbortController();\n        \/\/ Both supersede (a newer lookup) and timeout abort the same controller, so the\n        \/\/ fetch catch handler can't tell them apart from err.name alone. Flag timeouts\n        \/\/ explicitly so the catch can promote them to 'error' (clears stuck 'pending'\n        \/\/ and allows retry on the next handleNipChange tick), while a true supersede\n        \/\/ stays silent and lets the newer lookup replace the verdict.\n        controller.timedOut = false;\n        group._rpwdlAbort = controller;\n        const timeoutId = setTimeout(function () {\n            controller.timedOut = true;\n            controller.abort();\n        }, RPWDL_LOOKUP_TIMEOUT_MS);\n\n        \/\/ Composite cache key: a lookup is uniquely identified by (nip, date). Changing\n        \/\/ either invalidates the cached verdict so the user always sees a status that\n        \/\/ matches the date they typed.\n        const lookupKey = nipClean + '|' + dateIso;\n        group.dataset.rpwdlLookupKey = lookupKey;\n        group.dataset.rpwdlLookupStatus = 'pending';\n\n        const baseUrl = (window.TELEMEDI_API_BASE_URL || '').replace(\/\\\/+$\/, '');\n        const params = new URLSearchParams({ nip: nipClean, date: dateIso });\n        const url = baseUrl + RPWDL_CHECK_ENDPOINT + '?' + params.toString();\n\n        fetch(url, { signal: controller.signal })\n            .then(function (res) {\n                if (!res.ok) throw new Error('http ' + res.status);\n                return res.json();\n            })\n            .then(function (data) {\n                clearTimeout(timeoutId);\n                if (group._rpwdlAbort !== controller) return;\n                group._rpwdlAbort = null;\n                if (group.dataset.rpwdlLookupKey !== lookupKey) return;\n\n                const exists = !!(data && (data.exists_rpwdl || data.existsRpwdl));\n                const rawEntries = (data && (data.rpwdl_entries || data.rpwdlEntries));\n                const entries = Array.isArray(rawEntries) ? rawEntries : [];\n                const entry = entries.length > 0 ? entries[0] : null;\n\n                \/\/ Render the service date in Polish DD.MM.YYYY for human-readable warnings.\n                \/\/ dateIso is the ISO YYYY-MM-DD we passed in; cached on the closure.\n                const datePl = dateIso ? dateIso.split('-').reverse().join('.') : '';\n\n                const facilityName = entry ? (entry.name || '') : '';\n                const kind = !exists || !entry\n                    ? 'miss'\n                    : (entry.is_active === false ? 'inactive' : 'active');\n\n                \/\/ Cache the resolved verdict on the group so service add\/remove can replay\n                \/\/ the same entry under a new idx without hitting the network again.\n                group._rpwdlLastVerdict = { lookupKey: lookupKey, kind: kind, facilityName: facilityName, datePl: datePl };\n                group.dataset.rpwdlLookupStatus = kind === 'miss' ? 'miss' : 'hit';\n                applyRpwdlVerdict(group);\n            })\n            .catch(function (err) {\n                clearTimeout(timeoutId);\n                const wasTimeout = controller.timedOut === true;\n                if (group._rpwdlAbort === controller) group._rpwdlAbort = null;\n                if (group.dataset.rpwdlLookupKey !== lookupKey) return;\n                \/\/ AbortError fires in two cases: the user typed a new NIP\/date mid-flight\n                \/\/ (supersede), or the timeout above tripped. Supersede leaves the existing\n                \/\/ entry alone so the new lookup can replace it. Timeout falls through to\n                \/\/ the error branch below to clear the stuck 'pending' status and free the\n                \/\/ (nip,date) key for retry on the next handleNipChange tick.\n                if (err && err.name === 'AbortError' && !wasTimeout) return;\n                if (wasTimeout) {\n                    console.warn('RPWDL lookup timed out for ' + lookupKey);\n                } else {\n                    console.warn('RPWDL lookup failed for ' + lookupKey + ':', err);\n                }\n                group.dataset.rpwdlLookupStatus = 'error';\n                \/\/ Unknown state: clear any stale verdict from an earlier lookup. We make no\n                \/\/ claim either way and the user can still submit if the NIP checksum is valid.\n                group._rpwdlLastVerdict = null;\n                setNipHint(group, '');\n                clearRpwdlLookupRules(getServiceIndex(group));\n            });\n    }\n\n    \/\/ Re-emit the cached RPWDL verdict for `group` under its current service index. Called\n    \/\/ by lookupRpwdl right after a fetch resolves, and by the service add\/remove flow when\n    \/\/ indices shift, so we can rekey formIssues entries without re-fetching from the backend.\n    function applyRpwdlVerdict(group) {\n        const verdict = group && group._rpwdlLastVerdict;\n        if (!verdict) return;\n        const idx = getServiceIndex(group);\n        if (idx < 0) return;\n\t\t\/\/> this comment is used to circumvent the https:\/\/core.trac.wordpress.org\/ticket\/43785 bug\n        const n = idx + 1;\n        const nipInput = group.querySelector('.service-nip');\n        const facilityName = verdict.facilityName;\n        const datePl = verdict.datePl;\n\n        if (verdict.kind === 'miss') {\n            setNipHint(group, '');\n            formIssues.remove(`warn.service.${idx}.rpwdl.inactive`);\n            formIssues.remove(`success.service.${idx}.rpwdl.ok`);\n            formIssues.upsert(`warn.service.${idx}.rpwdl.notFound`, {\n                severity: 'warning',\n                title: `\u015awiadczenie nr ${n}: nie znaleziono plac\u00f3wki o tym numerze NIP w rejestrze RPWDL dla daty wykonania ${datePl}. Sprawd\u017a, czy NIP oraz data wykonania \u015bwiadczenia s\u0105 poprawne.`,\n                section: `\u015awiadczenie ${n}`,\n                bypassGate: true,\n                fieldRef: nipInput\n            });\n            return;\n        }\n\n        formIssues.remove(`warn.service.${idx}.rpwdl.notFound`);\n        setNipHint(group, facilityName ? `Plac\u00f3wka: ${facilityName}` : '');\n\n        if (verdict.kind === 'inactive') {\n            formIssues.remove(`success.service.${idx}.rpwdl.ok`);\n            formIssues.upsert(`warn.service.${idx}.rpwdl.inactive`, {\n                severity: 'warning',\n                title: `\u015awiadczenie nr ${n}: plac\u00f3wka${facilityName ? ` \u201e${facilityName}\u201d` : ''} nie by\u0142a aktywna w rejestrze RPWDL w dniu wykonania \u015bwiadczenia (${datePl}). Sprawd\u017a, czy NIP oraz data wykonania s\u0105 poprawne przed wys\u0142aniem wniosku.`,\n                section: `\u015awiadczenie ${n}`,\n                bypassGate: true,\n                fieldRef: nipInput\n            });\n        } else {\n            formIssues.remove(`warn.service.${idx}.rpwdl.inactive`);\n            formIssues.upsert(`success.service.${idx}.rpwdl.ok`, {\n                severity: 'success',\n                title: `\u015awiadczenie nr ${n}: plac\u00f3wka${facilityName ? ` \u201e${facilityName}\u201d` : ''} by\u0142a aktywna w rejestrze RPWDL w dniu wykonania \u015bwiadczenia (${datePl}).`,\n                section: `\u015awiadczenie ${n}`,\n                bypassGate: true,\n                fieldRef: nipInput\n            });\n        }\n    }\n\n    function setNipInlineError(group, nipInput, hasError) {\n        const errEl = group.querySelector('.service-nip-error');\n        if (!errEl) return;\n        if (hasError) {\n            errEl.classList.remove('hidden');\n            nipInput.classList.add('border-red-500');\n        } else {\n            errEl.classList.add('hidden');\n            nipInput.classList.remove('border-red-500');\n        }\n    }\n\n    function handleNipChange(group, immediate) {\n        const idx = getServiceIndex(group);\n        if (idx < 0) return;\n\t\t\/\/> this comment is used to circumvent the https:\/\/core.trac.wordpress.org\/ticket\/43785 bug\n        const nipInput = group.querySelector('.service-nip');\n        if (!nipInput) return;\n        const nipClean = normalizeNIP(nipInput.value);\n\n        \/\/ Inline NIP error UI: empty value clears the indicator (required-handling\n        \/\/ covers the empty case via formIssues); a non-empty value that fails\n        \/\/ `validateNIP` flips the red border + inline error on. Mirrors the panel\n        \/\/ entry `err.service.${idx}.nip.invalid` evaluated in evalDeterministic.\n        setNipInlineError(group, nipInput, nipClean.length > 0 && !validateNIP(nipClean));\n\n        \/\/ Read the per-service serviceDate (\"Data wykonania *\") so the lookup can ask\n        \/\/ the backend whether the facility was active on the date of \u015bwiadczenie, not\n        \/\/ just today. Both fields must be valid before we hit the network.\n        const dateInput = group.querySelector('.service-date');\n        const dateRaw = dateInput ? dateInput.value : '';\n        const dateIso = isValidIsoDate(dateRaw) ? dateRaw : '';\n\n        if (!nipClean || !validateNIP(nipClean) || !dateIso) {\n            \/\/ Either field missing\/invalid: cancel everything pending and clear any prior\n            \/\/ verdict. We never show a verdict that ignores the date the user typed.\n            if (group._rpwdlAbort) { try { group._rpwdlAbort.abort(); } catch (e) {} group._rpwdlAbort = null; }\n            if (group._rpwdlDebounceTimer) { clearTimeout(group._rpwdlDebounceTimer); group._rpwdlDebounceTimer = null; }\n            group.dataset.rpwdlLookupKey = '';\n            group.dataset.rpwdlLookupStatus = '';\n            group._rpwdlLastVerdict = null;\n            setNipHint(group, '');\n            clearRpwdlLookupRules(idx);\n            return;\n        }\n\n        if (immediate && group._rpwdlDebounceTimer) {\n            clearTimeout(group._rpwdlDebounceTimer);\n            group._rpwdlDebounceTimer = null;\n        }\n\n        const lookupKey = nipClean + '|' + dateIso;\n        if (group.dataset.rpwdlLookupKey === lookupKey && group.dataset.rpwdlLookupStatus && group.dataset.rpwdlLookupStatus !== 'error') {\n            return;\n        }\n\n        if (immediate) {\n            lookupRpwdl(group, nipClean, dateIso);\n        } else {\n            if (group._rpwdlDebounceTimer) clearTimeout(group._rpwdlDebounceTimer);\n            group._rpwdlDebounceTimer = setTimeout(function () {\n                group._rpwdlDebounceTimer = null;\n                lookupRpwdl(group, nipClean, dateIso);\n            }, RPWDL_DEBOUNCE_MS);\n        }\n    }\n\n    function wireRpwdlLookup() {\n        const servicesContainer = document.getElementById('servicesContainer');\n        if (!servicesContainer) return;\n\n        servicesContainer.addEventListener('input', function (e) {\n            const target = e.target;\n            if (!target || !target.classList) return;\n            const group = target.closest('.service-group');\n            if (!group) return;\n            if (target.classList.contains('service-nip')) {\n                \/\/ Strip non-digits in place and cap at 10 digits, so a paste like\n                \/\/ \"123-456-32-18\" (12 chars) is not truncated by the input maxlength before\n                \/\/ validation can normalize it.\n                const cleaned = normalizeNIP(target.value).slice(0, 10);\n                if (target.value !== cleaned) {\n                    target.value = cleaned;\n                }\n                handleNipChange(group, false);\n            } else if (target.classList.contains('service-date')) {\n                \/\/ Service-date edits also refire the RPWDL check, since the verdict\n                \/\/ depends on (NIP, date). Debounced path so partial-typing in a text-mode\n                \/\/ date input does not spam the network.\n                handleNipChange(group, false);\n            }\n        });\n\n        \/\/ <input type=\"date\"> commits via 'change', not 'blur', when picked from a date\n        \/\/ picker. Treat that as the \"user committed\" moment and fire immediately.\n        servicesContainer.addEventListener('change', function (e) {\n            const target = e.target;\n            if (!target || !target.classList) return;\n            if (!target.classList.contains('service-date')) return;\n            const group = target.closest('.service-group');\n            if (!group) return;\n            handleNipChange(group, true);\n        });\n\n        servicesContainer.addEventListener('blur', function (e) {\n            const target = e.target;\n            if (!target || !target.classList) return;\n            if (!target.classList.contains('service-nip')) return;\n            const group = target.closest('.service-group');\n            if (!group) return;\n            handleNipChange(group, true);\n        }, true);\n    }\n<\/script>\n","protected":false},"excerpt":{"rendered":"<p>Uwaga! Ta strona wymaga w\u0142\u0105czonego JavaScript, aby dzia\u0142a\u0107 poprawnie. W\u0142\u0105cz obs\u0142ug\u0119 JavaScript w przegl\u0105darce i od\u015bwie\u017c stron\u0119. Zamknij Sprawd\u017a wniosek przed wys\u0142aniem Upewnij si\u0119, \u017ce wszystkie dane s\u0105 poprawne. B\u0142\u0119dne dane wyd\u0142u\u017c\u0105 weryfikacj\u0119 Nieprawid\u0142owe lub niekompletne informacje (np. dane g\u0142\u00f3wnego ubezpieczonego, dane wsp\u00f3\u0142ubezpieczonego, adres, dane plac\u00f3wki, dane \u015bwiadczenia) mog\u0105 znacznie op\u00f3\u017ani\u0107 rozpatrzenie wniosku. Nieprawid\u0142owy produkt [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"_acf_changed":false,"content-type":"","footnotes":""},"class_list":["post-22227","page","type-page","status-publish","hentry"],"acf":[],"lang":"pl","translations":{"pl":22227},"_links":{"self":[{"href":"https:\/\/telemedi.com\/tm-api\/wp\/v2\/pages\/22227","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/telemedi.com\/tm-api\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/telemedi.com\/tm-api\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/telemedi.com\/tm-api\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/telemedi.com\/tm-api\/wp\/v2\/comments?post=22227"}],"version-history":[{"count":39,"href":"https:\/\/telemedi.com\/tm-api\/wp\/v2\/pages\/22227\/revisions"}],"predecessor-version":[{"id":25771,"href":"https:\/\/telemedi.com\/tm-api\/wp\/v2\/pages\/22227\/revisions\/25771"}],"wp:attachment":[{"href":"https:\/\/telemedi.com\/tm-api\/wp\/v2\/media?parent=22227"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}