{"id":1064,"date":"2025-05-25T14:27:43","date_gmt":"2025-05-25T12:27:43","guid":{"rendered":"http:\/\/hosting2545269.online.pro\/autoinstalator\/wordpress6\/?page_id=1064"},"modified":"2026-02-12T12:37:53","modified_gmt":"2026-02-12T11:37:53","slug":"parafie-1","status":"publish","type":"page","link":"https:\/\/new.archidiecezjalubelska.pl\/index.php\/parafie-1\/","title":{"rendered":"Parafie 1"},"content":{"rendered":"<div class=\"woocommerce\">\n    \n    <section class=\"u-clearfix u-white u-section-2\" id=\"sec-e3bd\">\n      <div class=\"u-clearfix u-sheet u-valign-middle-lg u-valign-middle-md u-valign-middle-sm u-sheet-1\">\n        <div class=\"data-layout-selected u-clearfix u-expanded-width u-layout-wrap u-layout-wrap-1\">\n          <div class=\"u-layout\">\n            <div class=\"u-layout-row\">\n              <div class=\"u-container-style u-layout-cell u-size-60 u-layout-cell-1\">\n                <div class=\"u-container-layout u-valign-top u-container-layout-1\">\n                  <div class=\"u-container-style u-expanded-width u-grey-5 u-group u-group-1\">\n                    <div class=\"u-container-layout u-container-layout-2\">\n                      <p class=\"u-text u-text-default u-text-1\">Parafie<\/p>\n                    <\/div>\n                  <\/div>\n                  <div class=\"custom-expanded u-expanded-width-lg u-expanded-width-md u-expanded-width-sm u-expanded-width-xl u-shortcode u-shortcode-1\"><h4> [>A<]  <a href=\"https:\/\/new.archidiecezjalubelska.pl\/index.php\/parafie-1\/?litera=B\">B<\/a>  <a href=\"https:\/\/new.archidiecezjalubelska.pl\/index.php\/parafie-1\/?litera=C\">C<\/a>  <a href=\"https:\/\/new.archidiecezjalubelska.pl\/index.php\/parafie-1\/?litera=\u0106\">\u0106<\/a>  <a href=\"https:\/\/new.archidiecezjalubelska.pl\/index.php\/parafie-1\/?litera=D\">D<\/a>  <a href=\"https:\/\/new.archidiecezjalubelska.pl\/index.php\/parafie-1\/?litera=E\">E<\/a>  <a href=\"https:\/\/new.archidiecezjalubelska.pl\/index.php\/parafie-1\/?litera=F\">F<\/a>  <a href=\"https:\/\/new.archidiecezjalubelska.pl\/index.php\/parafie-1\/?litera=G\">G<\/a>  <a href=\"https:\/\/new.archidiecezjalubelska.pl\/index.php\/parafie-1\/?litera=H\">H<\/a>  <a href=\"https:\/\/new.archidiecezjalubelska.pl\/index.php\/parafie-1\/?litera=I\">I<\/a>  <a href=\"https:\/\/new.archidiecezjalubelska.pl\/index.php\/parafie-1\/?litera=J\">J<\/a>  <a href=\"https:\/\/new.archidiecezjalubelska.pl\/index.php\/parafie-1\/?litera=K\">K<\/a>  <a href=\"https:\/\/new.archidiecezjalubelska.pl\/index.php\/parafie-1\/?litera=L\">L<\/a>  <a href=\"https:\/\/new.archidiecezjalubelska.pl\/index.php\/parafie-1\/?litera=\u0141\">\u0141<\/a>  <a href=\"https:\/\/new.archidiecezjalubelska.pl\/index.php\/parafie-1\/?litera=M\">M<\/a>  <a href=\"https:\/\/new.archidiecezjalubelska.pl\/index.php\/parafie-1\/?litera=N\">N<\/a>  <a href=\"https:\/\/new.archidiecezjalubelska.pl\/index.php\/parafie-1\/?litera=O\">O<\/a>  <a href=\"https:\/\/new.archidiecezjalubelska.pl\/index.php\/parafie-1\/?litera=P\">P<\/a>  <a href=\"https:\/\/new.archidiecezjalubelska.pl\/index.php\/parafie-1\/?litera=R\">R<\/a>  <a href=\"https:\/\/new.archidiecezjalubelska.pl\/index.php\/parafie-1\/?litera=S\">S<\/a>  <a href=\"https:\/\/new.archidiecezjalubelska.pl\/index.php\/parafie-1\/?litera=\u015a\">\u015a<\/a>  <a href=\"https:\/\/new.archidiecezjalubelska.pl\/index.php\/parafie-1\/?litera=T\">T<\/a>  <a href=\"https:\/\/new.archidiecezjalubelska.pl\/index.php\/parafie-1\/?litera=U\">U<\/a>  <a href=\"https:\/\/new.archidiecezjalubelska.pl\/index.php\/parafie-1\/?litera=W\">W<\/a>  <a href=\"https:\/\/new.archidiecezjalubelska.pl\/index.php\/parafie-1\/?litera=Y\">Y<\/a>  <a href=\"https:\/\/new.archidiecezjalubelska.pl\/index.php\/parafie-1\/?litera=Z\">Z<\/a>  <a href=\"https:\/\/new.archidiecezjalubelska.pl\/index.php\/parafie-1\/?litera=\u0179\">\u0179<\/a>  <a href=\"https:\/\/new.archidiecezjalubelska.pl\/index.php\/parafie-1\/?litera=\u017b\">\u017b<\/a> <\/h4><br \/><br \/><br \/><ul><li><a href=\"https:\/\/new.archidiecezjalubelska.pl\/index.php\/parafie-1\/?id=102162\">Abram\u00f3w, pw. Matki Bo\u017cej Szkaplerznej<\/a><\/li><\/ul> <\/div>\n                <\/div>\n              <\/div>\n            <\/div>\n          <\/div>\n        <\/div>\n        <div class=\"u-expanded-width-xs u-shortcode u-shortcode-2\">    <link rel=\"stylesheet\" href=\"https:\/\/unpkg.com\/leaflet@1.9.4\/dist\/leaflet.css\" \/>\n    <style>\n\t:root{\n  --archi-brown: #a8834a; \n  --archi-brown-dark: #8a6d3c;\n}\n        #mapid { height: 600px; margin: 20px 0; }\n        #sidebar {\n            position: fixed; top: 80px; right: 0; width: 350px; max-width: 100%;\n            height: calc(100% - 100px); background: #f7f7f7;\n            box-shadow: -2px 0 10px rgba(0,0,0,0.2); padding: 20px; overflow-y: auto;\n            display: none; z-index: 1000;\n        }\n        #sidebar h3 { margin-top: 0; }\n        #sidebar input[type=\"text\"] {\n            width: 100%; padding: 5px; margin-bottom: 10px;\n        }\n        #sidebar .close-btn {\n            background: #d33; color: white; border: none; padding: 5px 10px;\n            float: right; cursor: pointer;\n        }\n\t\t.parafia-label { display: none !important; }\n\n\n\/* nie przycinaj powi\u0119kszonego obrazka pinezki *\/\n.leaflet-marker-pane .pin-wrapper { overflow: visible !important; }\n\/* dopilnuj, \u017ceby obrazek by\u0142 blokiem i m\u00f3g\u0142 si\u0119 skalowa\u0107 *\/\n.leaflet-marker-pane .pin-wrapper .pin-img { display:block; transform-origin:50% 100%; }\n\n.parafia-popup .leaflet-popup-content-wrapper { max-width: 520px; }\n.parafia-popup .leaflet-popup-content { white-space: normal; line-height: 1.35; }\n\n\/* CSS dla tabeli i galerii *\/\n\t  .parafia-details-wrap{\n  display:grid;\n  grid-template-columns: minmax(0, 1.4fr) minmax(260px, 0.9fr);\n  gap:24px;\n  align-items:start;\n  margin-top:20px;\n}\n\n.parafia-details-main h2{\n  margin-top:0;\n}\n\n.parafia-table td{\n  border:1px solid #ccc;\n  padding:10px;\n  vertical-align:top;\n}\n\n.parafia-gallery-preview,\n.parafia-gallery-all{\n  display:grid;\n  gap:12px;\n}\n\n.parafia-gallery-all{\n  margin-top:12px;\n}\n\n.parafia-gallery-item{\n  display:block;\n  text-decoration:none;\n}\n\n.parafia-gallery-item img{\n  display:block;\n  width:100%;\n  height:auto;\n  border-radius:8px;\n  box-shadow:0 2px 10px rgba(0,0,0,.12);\n}\n\n.parafia-gallery-toggle{\n  margin-top:12px;\n  padding:8px 12px;\n  background:#0073aa;\n  color:#fff;\n  border:none;\n  cursor:pointer;\n  border-radius:4px;\n}\n\n@media (max-width: 900px){\n  .parafia-details-wrap{\n    grid-template-columns:1fr;\n  }\n}\n\n.parafia-thumb.is-active{\n    border-color:var(--archi-brown);\n}\n.parafia-thumb:hover{\n    border-color:var(--archi-brown);\n    opacity:0.9;\n}\n\n.parafia-gallery-open-btn{\n  display:inline-block;\n  padding:8px 14px;\n  background:var(--archi-brown);\n  color:#fff !important;\n  text-decoration:none;\n  border-radius:4px;\n  font-weight:500;\n}\n\n.parafia-gallery-open-btn:hover{\n  background:var(--archi-brown-dark);\n}\n\n.parafia-miejscowosci-btn{\n  padding:10px 18px;\n  background:var(--archi-brown);\n  color:#fff;\n  border:none;\n  border-radius:6px;\n  cursor:pointer;\n  font-family:'Merriweather', serif;\n  font-size:15px;\n  box-shadow:0 3px 8px rgba(0,0,0,0.15);\n  transition:all .2s ease;\n}\n\n.parafia-miejscowosci-btn:hover{\n  background:var(--archi-brown-dark);\n  transform:translateY(-2px);\n}\n    <\/style>\n\n    <script src=\"https:\/\/unpkg.com\/leaflet@1.9.4\/dist\/leaflet.js\"><\/script>\n    <script src=\"https:\/\/cdn.jsdelivr.net\/npm\/@turf\/turf@6\/turf.min.js\"><\/script>\n\n    <div id=\"global-search\" style=\"margin:10px 0;\">\n    <input type=\"text\" id=\"global-adres-search\" \n           placeholder=\"Miejscowo\u015b\u0107, Ulica, Numer lub Miejscowo\u015b\u0107, Numer\"\n           style=\"width:70%; padding:6px;\">\n    <button id=\"btn-global-search\" \n            style=\"padding:6px; background:#0073aa; color:white; border:none; cursor:pointer;\">\n        Szukaj na mapie\n    <\/button>\n    <div id=\"global-search-result\" style=\"margin-top:5px; font-weight:bold;\"><\/div>\n<\/div>\n\n    <div id=\"mapid\"><\/div>\n\t\n\t<div id=\"parafia-miejscowosci-toggle\" style=\"text-align:center; margin:20px 0; display:none;\">\n  <button class=\"parafia-miejscowosci-btn\">\n    Poka\u017c miejscowo\u015bci nale\u017c\u0105ce do parafii\n  <\/button>\n<\/div>\n\n    <div id=\"parafia-details\" style=\"margin:25px 0\"><\/div>\n\n\n\n    <div id=\"sidebar\">\n        <button class=\"close-btn\" onclick=\"document.getElementById('sidebar').style.display='none';\">Zamknij<\/button>\n        <h3><\/h3>\n        <div id=\"sidebar-content\">\n            <p>Wybierz parafi\u0119 z mapy.<\/p>\n        <\/div>\n    <\/div>\n\n<script>\ndocument.addEventListener(\"DOMContentLoaded\", function () {\n  const map = L.map('mapid').setView([51.25, 22.57], 9);\n\n  L.tileLayer('https:\/\/{s}.basemaps.cartocdn.com\/light_all\/{z}\/{x}\/{y}{r}.png', {\n    attribution: '\u00a9 OpenStreetMap | \u00a9 CartoDB',\n    subdomains: 'abcd', maxZoom: 19\n  }).addTo(map);\n\n  let wszystkiePunktyGeoJSON, warstwaPunktow, warstwaGranice;\n  let aktywnaGranica = null;\n  let etykietaParafii = null;\n  let animacjaInterval = null;\n\n  \/* ==== PINY + SKALOWANIE ==== *\/\n  const PIN_BASE = 15;\n  const PIN_MAX  = 30;\n  const Z_MIN    = 9;\n  const Z_MAX    = 17;\n\n  const baseDivIcon = L.divIcon({\n    className: 'pin-wrapper',\n    html: '<img decoding=\"async\" class=\"pin-img\" src=\"https:\/\/new.archidiecezjalubelska.pl\/wp-content\/uploads\/mapy\/pin.svg\" style=\"width:15px;height:15px;display:block;\" \/>',\n    iconSize: [PIN_BASE, PIN_BASE],\n    iconAnchor: [Math.round(PIN_BASE\/2), PIN_BASE],\n    popupAnchor: [0, -PIN_BASE]\n  });\n\n  function targetSizeForZoom(z) {\n    const t = Math.max(0, Math.min(1, (z - Z_MIN) \/ (Z_MAX - Z_MIN)));\n    return PIN_BASE + (PIN_MAX - PIN_BASE) * t;\n  }\n\n  function applyMarkerScale() {\n    if (!warstwaPunktow) return;\n    const scale = targetSizeForZoom(map.getZoom()) \/ PIN_BASE;\n    warstwaPunktow.eachLayer(m => {\n      if (!(m instanceof L.Marker)) return;\n      const img = m._icon && m._icon.querySelector && m._icon.querySelector('img.pin-img');\n      if (img) {\n        img.style.transformOrigin = '50% 100%';\n        img.style.transform = 'scale(' + scale + ')';\n      }\n    });\n  }\n  \n\/* ==== GLOBALNE HELPERY DLA MAPY ==== *\/\n\n\/\/ normalizacja tekstu \u2013 dost\u0119pna globalnie\nwindow.normalizeText = function (s) {\n  return (s || '')\n    .replace(\/\\u00A0\/g, ' ')         \/\/ NBSP -> spacja\n    .replace(\/[\\u2010-\\u2015]\/g, '-')\/\/ r\u00f3\u017cne my\u015blniki -> '-'\n    .replace(\/\\s+\/g, ' ')\n    .trim();\n};\n\n\/\/ punkt w pier\u015bcieniu (ray casting)\nfunction _pointInRing(pt, ring) { \/\/ pt:[lng,lat], ring:[[lng,lat],...]\n  let x = pt[0], y = pt[1], inside = false;\n  for (let i = 0, j = ring.length - 1; i < ring.length; j = i++) {\n    const xi = ring[i][0], yi = ring[i][1];\n    const xj = ring[j][0], yj = ring[j][1];\n    const intersect = ((yi > y) !== (yj > y)) &&\n      (x < (((xj - xi) * (y - yi)) \/ (yj - yi) + xi));\n    if (intersect) inside = !inside;\n  }\n  return inside;\n}\n\n\/\/ punkt w poligonie (z obs\u0142ug\u0105 dziur)\nfunction _pointInPolygon(pt, polyRings) {\n  if (!_pointInRing(pt, polyRings[0])) return false;      \/\/ poza obrysem\n  for (let k = 1; k < polyRings.length; k++) {\n    if (_pointInRing(pt, polyRings[k])) return false;     \/\/ w dziurze\n  }\n  return true;\n}\n\n\/\/ czy feature poligonowy zawiera punkt\nfunction _featureContainsPoint(feature, pt) {\n  const g = feature.geometry;\n  if (!g) return false;\n  if (g.type === 'Polygon')   return _pointInPolygon(pt, g.coordinates);\n  if (g.type === 'MultiPolygon')\n    return g.coordinates.some(rings => _pointInPolygon(pt, rings));\n  return false;\n}\n\n\/\/ Znajd\u017a \"najlepsz\u0105\" pinezk\u0119 w granicy:\n\/\/ 1) kandydaci faktycznie w poligonie,\n\/\/ 2) je\u015bli kt\u00f3rykolwiek ma nazw\u0119 identyczn\u0105 po normalizacji -> bierzemy go,\n\/\/ 3) je\u015bli \u017cadna nie jest identyczna, preferuj \"zawiera\/rozszerza\" nazw\u0119 z poligonu,\n\/\/ 4) je\u015bli dalej remis \u2013 we\u017a najbli\u017csz\u0105 do centroidu.\nwindow.znajdzPunktDlaGranicy = function (boundaryFeature) {\n  if (!window.wszystkiePunktyGeoJSON || !window.wszystkiePunktyGeoJSON.features) return null;\n\n  const bbox = L.geoJSON(boundaryFeature).getBounds();\n  const centroid = bbox.getCenter();\n\n  const propsG = boundaryFeature.properties || {};\n  const nameFromPoly = window.normalizeText(\n    propsG.wezwanie_k || propsG.WEZWANIE_K || propsG.wezwanie || propsG.WEZWANIE || ''\n  );\n\n  const inside = [];\n  for (const f of window.wszystkiePunktyGeoJSON.features) {\n    const c = f.geometry && f.geometry.coordinates; \/\/ [lng, lat]\n    if (!c) continue;\n    if (!bbox.contains(L.latLng(c[1], c[0]))) continue;\n    if (!_featureContainsPoint(boundaryFeature, c)) continue;\n    inside.push(f);\n  }\n  if (inside.length === 0) return null;\n\n  \/\/ 2a) identyczna nazwa\n  for (const f of inside) {\n    const nm = window.normalizeText(f.properties?.WEZWANIE || '');\n    if (nameFromPoly && nm && nm === nameFromPoly) return f;\n  }\n  \/\/ 2b) nazwa punktu zawiera nazw\u0119 z poligonu albo odwrotnie\n  const partial = inside.filter(f => {\n    const nm = window.normalizeText(f.properties?.WEZWANIE || '');\n    return (nameFromPoly && nm) && (nm.includes(nameFromPoly) || nameFromPoly.includes(nm));\n  });\n  if (partial.length === 1) return partial[0];\n\n  \/\/ 3) najbli\u017csza do centroidu (je\u015bli zosta\u0142o kilka)\n  const list = partial.length ? partial : inside;\n  let best = null, bestDist2 = Infinity;\n  for (const f of list) {\n    const c = f.geometry.coordinates; \/\/ [lng, lat]\n    const dlat = c[1] - centroid.lat, dlng = c[0] - centroid.lng;\n    const d2 = dlat * dlat + dlng * dlng;\n    if (d2 < bestDist2) { bestDist2 = d2; best = f; }\n  }\n  return best || list[0] || null;\n};\n\n\n\n\n\n  \/* ==== PUNKTY ==== *\/\n  fetch(\"https:\/\/new.archidiecezjalubelska.pl\/wp-content\/uploads\/mapy\/punktyadresowe.geojson\")\n  .then(res => res.json())\n  .then(data => {\n      wszystkiePunktyGeoJSON = data;\n      window.wszystkiePunktyGeoJSON = data;\n\n      warstwaPunktow = L.geoJSON(data, {\n        pointToLayer: (f, latlng) => L.marker(latlng, { icon: baseDivIcon }),\n        onEachFeature: (feature, layer) => {\n          const props = feature.properties || {};\n          layer.bindPopup(\n            `<strong>Parafia:<\/strong> ${props.WEZWANIE || 'Brak'}<br>` +\n            `<strong>Adres:<\/strong> ${props.ADRES || 'Brak'}`\n          );\n\n          \/\/ Klik w pinezk\u0119 \u2013 szczeg\u00f3\u0142y + pod\u015bwietlenie granicy\nlayer.on('click', () => {\n  \/\/ Normalizacja \u2013 usu\u0144 twarde spacje i \u201edziwne\u201d my\u015blniki,\n  \/\/ bo takie znaki cz\u0119sto s\u0105 w Twoich GeoJSON-ach i w bazie\n  const normalize = (s) =>\n    (s || '')\n      .replace(\/\\u00A0\/g, ' ')            \/\/ NBSP -> zwyk\u0142a spacja\n      .replace(\/[\\u2010-\\u2015]\/g, '-')   \/\/ wszystkie my\u015blniki -> '-'\n      .replace(\/\\s+\/g, ' ')               \/\/ zbij wielokrotne spacje\n      .trim();\n\n  const nazwa = normalize(props.WEZWANIE || props.wezwanie || '');\n  const adres = normalize(props.ADRES || '');\n\n  \/\/ PUNKTY NIE MAJ\u0104 ID \u2192 zawsze \u0142aduj po nazwie + adresie\n  \/\/ (to jest ten tryb, kt\u00f3ry dzia\u0142a Ci poprawnie z listy)\n  window.zaladujParafiePoNazwieIAdresie(nazwa, adres, true);\n\n  \/\/ Pod\u015bwietlenie granicy i etykieta \u2014 tylko UI\n  const layerParafii = znajdzGraniceDlaPunktu(layer.getLatLng());\n  if (layerParafii) {\n    if (aktywnaGranica) {\n      warstwaGranice.resetStyle(aktywnaGranica);\n      if (animacjaInterval) clearInterval(animacjaInterval);\n    }\n    aktywnaGranica = layerParafii;\n\n    let colors = ['#ff6600', '#ffa500'], i = 0;\n    animacjaInterval = setInterval(() => {\n      aktywnaGranica.setStyle({ color: colors[i % colors.length], weight: 4, fillOpacity: 0.4 });\n      i++;\n    }, 500);\n\n    const propsG = layerParafii?.feature?.properties || {};\n    const wezwanie =\n      normalize(propsG.wezwanie_k || propsG.WEZWANIE_K || propsG.wezwanie || propsG.WEZWANIE || nazwa || 'Parafia');\n\n    if (etykietaParafii) map.removeLayer(etykietaParafii);\n    etykietaParafii = L.marker(layerParafii.getBounds().getCenter(), {\n      icon: L.divIcon({\n        className: 'parafia-label',\n        html: `<div style=\"background:#fff; border:1px solid rgba(0,0,0,0.2); padding:4px 8px; border-radius:6px; box-shadow:0 1px 3px rgba(0,0,0,0.2); font-weight:bold;\">Parafia: ${wezwanie}<\/div>`\n      }),\n      interactive: false\n    }).addTo(map);\n\n    map.fitBounds(layerParafii.getBounds());\n  }\n});\n\n        }\n      }).addTo(map);\n\t  \n\t  \n\t  \/\/ --- utils ---\nfunction norm(s){ return (s||'').replace(\/\\u00A0\/g,' ').replace(\/[\\u2010-\\u2015]\/g,'-').replace(\/\\s+\/g,' ').trim(); }\nfunction getPolyId(props){\n  return (props?.par_id ?? props?.par_ID ?? props?.ID ?? props?.id ?? props?.nr_paraf_i ?? null);\n}\n\n\/* Znajd\u017a polygon po ID z GeoJSON granic\nfunction findPolygonById(id){\n  let found=null;\n  if(!id || !window.warstwaGranice) return null;\n  window.warstwaGranice.eachLayer(l=>{\n    const pid = getPolyId(l.feature?.properties);\n    if(String(pid)===String(id)) found=l;\n  });\n  return found;\n} *\/\n\n\/\/ Ulepszona wersja znajd\u017a polygon po ID \n\nfunction findPolygonById(id){\n  let found = null;\n  if (!id || !window.warstwaGranice) return null;\n\n  window.warstwaGranice.eachLayer(layer => {\n    const pid = getPolyId(layer.feature?.properties || {});\n    if (String(pid) === String(id)) {\n      found = layer;\n    }\n  });\n\n  return found;\n}\n\n\/\/ Zaznacz polygon (jak przy klikni\u0119ciu) + etykieta\nfunction highlightPolygon(layer){\n  if(!layer) return;\n  if(window.aktywnaGranica){\n    window.warstwaGranice?.resetStyle(window.aktywnaGranica);\n    if(window.animacjaInterval) clearInterval(window.animacjaInterval);\n  }\n  window.aktywnaGranica = layer;\n  let colors=['#ff6600','#ffa500'], i=0;\n  window.animacjaInterval = setInterval(()=> {\n    window.aktywnaGranica.setStyle({color:colors[i++%colors.length], weight:4, fillOpacity:0.4});\n  },500);\n\n  const propsG = layer.feature?.properties || {};\n  const wezwanie = norm(propsG.wezwanie_k || propsG.WEZWANIE_K || propsG.wezwanie || propsG.WEZWANIE || 'Parafia');\n  if(window.etykietaParafii) map.removeLayer(window.etykietaParafii);\n  window.etykietaParafii = L.marker(layer.getBounds().getCenter(),{\n    icon:L.divIcon({className:'parafia-label',html:`<div style=\"background:#fff;border:1px solid #999;padding:3px 8px;border-radius:4px;box-shadow:0 0 6px rgba(0,0,0,.2);font-weight:bold;\">Parafia: ${wezwanie}<\/div>`})\n  }).addTo(map);\n  map.fitBounds(layer.getBounds());\n}\n\n\/\/ Z polygonu wybierz pinezk\u0119 (punkt) w \u015brodku \u2013 najlepiej najbli\u017csz\u0105 \u015brodka\nfunction findPointInsidePolygon(layer){\n  if(!window.wszystkiePunktyGeoJSON) return null;\n  const center = layer.getBounds().getCenter();\n  let best=null, bestD=Infinity;\n  (wszystkiePunktyGeoJSON.features||[]).forEach(f=>{\n    if(!f.geometry || f.geometry.type!==\"Point\") return;\n    const [lng,lat]=f.geometry.coordinates;\n    const ll=L.latLng(lat,lng);\n    if(layer.getBounds().contains(ll)){\n      const d = ll.distanceTo(center);\n      if(d<bestD){ best=f; bestD=d; }\n    }\n  });\n  return best;\n}\n\n\/* G\u0142\u00f3wna akcja: wyb\u00f3r po ID \u2192 highlight + pobranie szczeg\u00f3\u0142\u00f3w\nfunction selectParafiaById(id){\n  const poly = findPolygonById(id);\n  if(!poly) return;\n  highlightPolygon(poly);\n  \/\/ spr\u00f3buj dopasowa\u0107 pinezk\u0119 i \u0142adowa\u0107 address-first\n  const pt = findPointInsidePolygon(poly);\n  if(pt){\n    const nazwa = norm(pt.properties?.WEZWANIE || pt.properties?.wezwanie || '');\n    const adres = norm(pt.properties?.ADRES || '');\n    if(typeof window.zaladujParafiePoNazwieIAdresie==='function'){\n      window.zaladujParafiePoNazwieIAdresie(nazwa, adres, true);\n    }\n  }else{\n    \/\/ awaryjnie spr\u00f3buj po samym ID (je\u015bli Tw\u00f3j backend to obs\u0142uguje)\n    const pid = getPolyId(poly.feature?.properties);\n    if(pid && typeof window.zaladujParafiePoId==='function'){\n      window.zaladujParafiePoId(pid, '', '');\n    }\n  }\n} *\/\n\n\/\/ G\u0142\u00f3wna akcja wyb\u00f3r po ID + pobranie szczeg\u00f3\u0142\u00f3w (nowa ods\u0142ona)\n\nfunction selectParafiaById(id){\n  const poly = findPolygonById(id);\n\n  \/\/ 1) highlight mapy\n  if (poly) {\n    highlightPolygon(poly);\n  }\n\n  \/\/ 2) dok\u0142adne \u0142adowanie szczeg\u00f3\u0142\u00f3w po ID\n  if (typeof window.zaladujParafiePoId === 'function') {\n    window.zaladujParafiePoId(id);\n  }\n\n  \/\/ 3) sensowne ustawienie widoku mapy\n  if (poly) {\n    const pt = window.znajdzPunktDlaGranicy ? window.znajdzPunktDlaGranicy(poly.feature) : null;\n\n    if (pt && pt.geometry && pt.geometry.coordinates) {\n      const [lng, lat] = pt.geometry.coordinates;\n      map.setView([lat, lng], Math.max(map.getZoom(), 13));\n    } else {\n      map.fitBounds(poly.getBounds());\n    }\n  }\n}\n\n\/\/ Jeden handler po ID dla wszystkich \n\ndocument.addEventListener('click', function(e) {\n  const a = e.target.closest('a[href*=\"\/index.php\/parafie-1\/?id=\"]');\n  if (!a) return;\n\n  e.preventDefault();\n\n  const url = new URL(a.href, location.origin);\n  const parId = url.searchParams.get('id');\n  if (!parId) return;\n\n  selectParafiaById(parId);\n}, true);\n\n\n\/* Delegacja klik\u00f3w: linki z listy parafii typu ...\/parafie-1\/?id=XXXX\ndocument.addEventListener('click', (e)=>{\n  const a = e.target.closest('a[href*=\"parafie-1\"][href*=\"?id=\"]');\n  if(!a) return;\n  const url = new URL(a.href, location.origin);\n  const id  = url.searchParams.get('id');\n  if(!id) return;\n  \/\/ Przejmujemy klik, nie prze\u0142adowujemy strony\n  e.preventDefault();\n  selectParafiaById(id);\n}); *\/\n\n\t  \n\t  \n\t  \n\t  \n\/\/ ===== obs\u0142uga klik\u00f3w w linki listy parafii -> \u0142aduj szczeg\u00f3\u0142y POD map\u0105 =====\n\n\n\/\/ uniwersalna normalizacja\nfunction _normTxt(s) {\n  return (s || '')\n    .replace(\/\\u00A0\/g, ' ')       \/\/ NBSP -> spacja\n    .replace(\/[\\u2010-\\u2015]\/g, '-') \/\/ r\u00f3\u017cne my\u015blniki -> '-'\n    .replace(\/\\s+\/g, ' ')\n    .trim()\n    .toLowerCase();\n}\n\n\/\/ z napisu linku robi \u201enag\u0105\u201d nazw\u0119 wezwania\n\/\/ np. \"Bychawa, pw. \u015bw. Jana Chrzciciela i \u015bw. Franciszka z Asy\u017cu\"\n\/\/  -> \"jana chrzciciela i franciszka z asyzu\"\nfunction _extractWezwanieFromLinkText(txt) {\n  const t = _normTxt(txt);\n  \/\/ obetnij wszystko do \"pw.\" je\u017celi wyst\u0119puje lub po przecinku (miejscowo\u015b\u0107)\n  let out = t;\n  const pwIdx = t.indexOf('pw.');\n  if (pwIdx >= 0) out = t.slice(pwIdx + 3);\n  else if (t.includes(',')) out = t.split(',').slice(1).join(','); \/\/ po przecinku\n  \/\/ usu\u0144 prefiksy typu \"pw.\", \"parafia\", \"\u015bw.\", \"\u015bw\"\n  out = out.replace(\/\\b(pw\\.?|parafia|par\\.?)\\b\/g, ' ')\n           .replace(\/\\b\u015bw\\.?\\b\/g, ' ')\n           .replace(\/\\s+\/g, ' ')\n           .trim();\n  return out;\n}\n\n\/\/ spr\u00f3buj znale\u017a\u0107 pinezk\u0119 po nazwie z linku\nfunction _findPointByWezwanieFromList(txt) {\n  if (!window.wsZnalezionePunktyCache && window.wszystkiePunktyGeoJSON) {\n    \/\/ przygotuj cache znormalizowanych nazw (szybciej przy wielu klikach)\n    window.wsZnalezionePunktyCache = (wszystkiePunktyGeoJSON.features || []).map(f => ({\n      f,\n      wezwanie: _normTxt(f.properties?.WEZWANIE || f.properties?.wezwanie || ''),\n      adres: _normTxt(f.properties?.ADRES || '')\n    }));\n  }\n  const clean = _extractWezwanieFromLinkText(txt);\n  if (!clean) return null;\n  \/\/ dopasowanie \u201ezawiera\u201d \u2013 nazwy z listy bywaj\u0105 d\u0142u\u017csze\/kr\u00f3tsze\n  const hit = (window.wsZnalezionePunktyCache || []).find(p =>\n    p.wezwanie.includes(clean)\n  );\n  return hit?.f || null;\n}\n\n\/\/ pod\u015bwietl granic\u0119 dla wskazanego punktu (jak przy klikni\u0119ciu w marker)\nfunction _highlightPolygonForPoint(latlng) {\n  if (typeof window.znajdzGraniceDlaPunktu !== 'function') return;\n  const poly = window.znajdzGraniceDlaPunktu(latlng);\n  if (!poly) return;\n\n  if (window.aktywnaGranica) {\n    window.warstwaGranice?.resetStyle(window.aktywnaGranica);\n    if (window.animacjaInterval) clearInterval(window.animacjaInterval);\n  }\n  window.aktywnaGranica = poly;\n  let colors = ['#ff6600', '#ffa500'], i = 0;\n  window.animacjaInterval = setInterval(() => {\n    window.aktywnaGranica.setStyle({ color: colors[i++ % colors.length], weight: 4, fillOpacity: 0.4 });\n  }, 500);\n\n  const propsG = poly?.feature?.properties || {};\n  const wezwanieLab = _normTxt(propsG.wezwanie_k || propsG.WEZWANIE_K || propsG.wezwanie || propsG.WEZWANIE || '');\n  if (window.etykietaParafii) map.removeLayer(window.etykietaParafii);\n  window.etykietaParafii = L.marker(poly.getBounds().getCenter(), {\n    icon: L.divIcon({\n      className: 'parafia-label',\n      html: `<div style=\"background:#fff; border:1px solid rgba(0,0,0,.2); padding:4px 8px; border-radius:6px; box-shadow:0 1px 3px rgba(0,0,0,.2); font-weight:bold;\">Parafia: ${wezwanieLab || 'Parafia'}<\/div>`\n    }),\n    interactive: false\n  }).addTo(map);\n\n  map.fitBounds(poly.getBounds());\n}\n\n\/* DELGACJA KLIK\u00d3W: ca\u0142a strona, ale filtrujemy na list\u0119 parafii (UL\/LI z linkami)\ndocument.addEventListener('click', (ev) => {\n  const a = ev.target.closest('a');\n  if (!a) return;\n\n  \/\/ heurystyka: link jest elementem listy parafii (masz u siebie list\u0119 <ul><li><a>...)\n  \/\/ je\u015bli masz konkretny kontener, podmie\u0144 selektor poni\u017cej na np. '.parafie-list'\n  const inList = a.closest('li');\n  if (!inList) return;\n\n  \/\/ Najpierw spr\u00f3buj znale\u017a\u0107 pinezk\u0119 po nazwie z linku\n  const feature = _findPointByWezwanieFromList(a.textContent || '');\n  if (!feature) return; \/\/ nie ingerujemy, niech stary kod zrobi swoje\n\n  \/\/ Mamy dopasowan\u0105 pinezk\u0119 \u2013 przejmij klik i zr\u00f3b wszystko my\n  ev.preventDefault();\n\n  \/\/ koordy z GeoJSON (lng, lat)\n  const [lng, lat] = feature.geometry.coordinates;\n  const latlng = L.latLng(lat, lng);\n\n  \/\/ 1) za\u0142aduj szczeg\u00f3\u0142y POD map\u0105 \u2014 address-first (strict)\n  const nazwa = _normTxt(feature.properties?.WEZWANIE || feature.properties?.wezwanie || '');\n  const adres = _normTxt(feature.properties?.ADRES || '');\n  if (typeof window.zaladujParafiePoNazwieIAdresie === 'function') {\n    window.zaladujParafiePoNazwieIAdresie(nazwa, adres, true);\n  }\n\n  \/\/ 2) pod\u015bwietl odpowiadaj\u0105c\u0105 granic\u0119\n  _highlightPolygonForPoint(latlng);\n\n  \/\/ 3) delikatny zoom\/centrowanie na punkt (opcjonalnie)\n  map.setView(latlng, Math.max(map.getZoom(), 13));\n}); *\/\n\n\n\n\n\t  \n\t  \n\n      requestAnimationFrame(applyMarkerScale);\n    });\n\n  map.whenReady(applyMarkerScale);\n  ['zoom', 'zoomend', 'moveend', 'resize'].forEach(ev => map.on(ev, applyMarkerScale));\n\n  const markerPane = map.getPanes().markerPane;\n  const mo = new MutationObserver(() => requestAnimationFrame(applyMarkerScale));\n  mo.observe(markerPane, { childList: true, subtree: true });\n  \n\n\/\/ === KONFIG ===\nconst DETAILS_ID = 'parafia-details'; \/\/ kontener pod map\u0105, do kt\u00f3rego kopiujemy tre\u015b\u0107\nconst SIDEBAR_SELECTORS = [\n  '.parafia-panel .content',\n  '.parafia-panel',\n  '.parafia-sidebar .content',\n  '.parafia-sidebar',\n  '#parafia-panel',\n  '.parafiaDialog',\n  '[data-parafia-panel]'\n];\n\n\/\/ znajd\u017a w DOM pierwszy dzia\u0142aj\u0105cy sidebar wg listy selektor\u00f3w\nfunction findRightPanelNode() {\n  for (const sel of SIDEBAR_SELECTORS) {\n    const el = document.querySelector(sel);\n    if (el && el.innerHTML && el.innerHTML.trim() !== '') return el;\n  }\n  return null;\n}\n\n\/\/ natychmiast spr\u00f3buj skopiowa\u0107 HTML sidebara pod map\u0119\nfunction mirrorRightPanelUnderMapOnce() {\n  const target = document.getElementById(DETAILS_ID);\n  if (!target) return false;\n  const panel = findRightPanelNode();\n  if (!panel) return false;\n\n  \/\/ kopiujemy zawarto\u015b\u0107 1:1 (je\u015bli wolisz p\u0142ytki klon: target.append(panel.cloneNode(true)))\n  target.innerHTML = panel.innerHTML;\n\n  \/\/ przewi\u0144 do szczeg\u00f3\u0142\u00f3w\n  target.scrollIntoView({ behavior: 'smooth', block: 'start' });\n  return true;\n}\n\n\/\/ ustaw obserwatora \u2013 gdy sidebar pojawi si\u0119\/zmieni, skopiuj pod map\u0119\nlet _sidebarMirrorObserver = null;\nfunction mirrorRightPanelUnderMap() {\n  \/\/ 1) spr\u00f3buj od razu\n  if (mirrorRightPanelUnderMapOnce()) return;\n\n  \/\/ 2) je\u015bli jeszcze nic nie ma, za\u0142\u00f3\u017c MutationObserver na <body> i poczekaj\n  if (_sidebarMirrorObserver) {\n    \/\/ ju\u017c dzia\u0142a \u2013 nie dublujemy\n    return;\n  }\n  _sidebarMirrorObserver = new MutationObserver((_mutList, obs) => {\n    if (mirrorRightPanelUnderMapOnce()) {\n      obs.disconnect();\n      _sidebarMirrorObserver = null;\n    }\n  });\n  _sidebarMirrorObserver.observe(document.body, {\n    childList: true,\n    subtree: true\n  });\n\n  \/\/ 3) bezpieczne wy\u0142\u0105czenie obserwatora po 5s (gdyby co\u015b posz\u0142o nie tak)\n  setTimeout(() => {\n    if (_sidebarMirrorObserver) {\n      _sidebarMirrorObserver.disconnect();\n      _sidebarMirrorObserver = null;\n      \/\/ jeszcze ostatnia pr\u00f3ba na koniec\n      mirrorRightPanelUnderMapOnce();\n    }\n  }, 5000);\n}\n\n \/\/ === czytanie nazwy i adresu z prawego panelu ===\nfunction _findSidebarContent() {\n  return (\n    document.getElementById('sidebar-content') ||\n    document.querySelector('.parafia-panel .content') ||\n    document.querySelector('.parafia-panel') ||\n    document.querySelector('#parafia-panel') ||\n    null\n  );\n}\n\nfunction _extractNameAndAddressFromSidebar() {\n  const root = _findSidebarContent();\n  if (!root) return { name: '', address: '' };\n\n  let name = '', address = '';\n  const norm = s => (window.normalizeText ? window.normalizeText(s) : (s || '').trim());\n\n  \/\/ Parafia: ..., Adres parafii: ...\n  root.querySelectorAll('p, li, div').forEach(el => {\n    const txt = (el.textContent || '').replace(\/\\s+\/g,' ').trim();\n    if (!name && \/^Parafia:\\s*\/i.test(txt))  name   = txt.replace(\/^Parafia:\\s*\/i,'').trim();\n    if (!address && \/^Adres parafii:\\s*\/i.test(txt)) address = txt.replace(\/^Adres parafii:\\s*\/i,'').trim();\n  });\n\n  \/\/ dodatkowy fallback po <strong>\n  if (!name || !address) {\n    root.querySelectorAll('strong').forEach(st => {\n      const lab = (st.textContent || '').toLowerCase();\n      const par = st.parentElement;\n      if (par) {\n        const t = (par.textContent || '').replace(\/\\s+\/g,' ').trim();\n        if (!name && lab.startsWith('parafia')) name = t.replace(\/^Parafia:\\s*\/i,'').trim();\n        if (!address && lab.startsWith('adres parafii')) address = t.replace(\/^Adres parafii:\\s*\/i,'').trim();\n      }\n    });\n  }\n\n  \/\/ odszum nazwy (pw., \u015bw., parafia itp.)\n  name = norm(name).replace(\/\\b(pw\\.?|par\\.?|parafia|\u015bw\\.?)\\b\/gi,'').replace(\/\\s+\/g,' ').trim();\n  address = norm(address);\n  return { name, address };\n}\n\n\/\/ wyzw\u00f3l \u0142adowanie POD map\u0105 z NAZWY+ADRESU z prawego panelu (AJAX address-first)\nfunction loadDetailsUnderMapFromSidebarOnce() {\n  const target = document.getElementById('parafia-details');\n  if (!target) return false;\n  const { name, address } = _extractNameAndAddressFromSidebar();\n  if (!name && !address) return false;\n\n  if (typeof window.zaladujParafiePoNazwieIAdresie === 'function') {\n    window.zaladujParafiePoNazwieIAdresie(name, address, true);\n    return true;\n  }\n  return false;\n}\n\nlet _sidebarToDetailsObserver = null;\nfunction loadDetailsUnderMapFromSidebar() {\n  if (loadDetailsUnderMapFromSidebarOnce()) return;\n\n  const waitRoot = _findSidebarContent() || document.body;\n  if (_sidebarToDetailsObserver) return;\n\n  _sidebarToDetailsObserver = new MutationObserver(() => {\n    if (loadDetailsUnderMapFromSidebarOnce()) {\n      _sidebarToDetailsObserver.disconnect();\n      _sidebarToDetailsObserver = null;\n    }\n  });\n  _sidebarToDetailsObserver.observe(waitRoot, { childList:true, subtree:true, characterData:true });\n\n  setTimeout(() => {\n    if (_sidebarToDetailsObserver) {\n      _sidebarToDetailsObserver.disconnect();\n      _sidebarToDetailsObserver = null;\n      loadDetailsUnderMapFromSidebarOnce();\n    }\n  }, 6000);\n}\n\n\/\/ Po definicji loadDetailsUnderMapFromSidebar():\nwindow.addEventListener('DOMContentLoaded', () => {\n  \/\/ uzbr\u00f3j observer ju\u017c teraz \u2013 zadzia\u0142a, gdy tylko pojawi si\u0119\/od\u015bwie\u017cy prawy panel\n  loadDetailsUnderMapFromSidebar();\n});\n\n \n\n\/* ==== GRANICE ==== *\/\nfetch(\"https:\/\/new.archidiecezjalubelska.pl\/wp-content\/uploads\/mapy\/graniceparafii.geojson\")\n  .then(res => res.json())\n  .then(data => {\n    warstwaGranice = L.geoJSON(data, {\n      style: { color: \"#444\", fillColor: \"#ccc\", fillOpacity: 0.3, weight: 2 },\n      onEachFeature: function (feature, layer) {\n        layer.on('click', function () {\n  \/\/ reset poprzedniego highlightu\n  if (aktywnaGranica) {\n    warstwaGranice.resetStyle(aktywnaGranica);\n    if (animacjaInterval) clearInterval(animacjaInterval);\n  }\n  aktywnaGranica = layer;\n\n  \/\/ animowany highlight\n  let colors = ['#ff6600', '#ffa500'], i = 0;\n  animacjaInterval = setInterval(() => {\n    aktywnaGranica.setStyle({ color: colors[i % colors.length], weight: 4, fillOpacity: 0.4 });\n    i++;\n  }, 500);\n\n  \/\/ etykieta na \u015brodku granicy\n  const propsG = feature?.properties || {};\n  const wezwanie = window.normalizeText(\n    propsG.wezwanie_k || propsG.WEZWANIE_K || propsG.wezwanie || propsG.WEZWANIE || \"Parafia\"\n  );\n\n  if (etykietaParafii) map.removeLayer(etykietaParafii);\n  const srodek = layer.getBounds().getCenter();\n  etykietaParafii = L.marker(srodek, {\n    icon: L.divIcon({\n      className: 'parafia-label',\n      html: `<div style=\"background:#fff; border:1px solid #999; padding:3px 8px; border-radius:4px; box-shadow:0 0 6px rgba(0,0,0,0.2); font-weight:bold;\">Parafia: ${wezwanie}<\/div>`,\n      iconSize: [100, 30],\n      iconAnchor: [50, 15]\n    }),\n    interactive: false\n  }).addTo(map);\n\n  map.fitBounds(layer.getBounds());\n\n  \/\/ nadal mo\u017cesz zostawi\u0107 prawy panel, je\u015bli chcesz\n  pokazSzczegolyParafii(feature);\n\n  \/\/ ===== NOWE: do\u0142aduj d\u00f3\u0142 strony przez nazw\u0119 + adres z najlepszego punktu =====\n  const bestPoint = window.znajdzPunktDlaGranicy ? window.znajdzPunktDlaGranicy(feature) : null;\n\n  if (bestPoint && bestPoint.properties) {\n    const nazwa = window.normalizeText(\n      bestPoint.properties.WEZWANIE || bestPoint.properties.wezwanie || ''\n    );\n\n    const adres = window.normalizeText(\n      bestPoint.properties.ADRES || bestPoint.properties.adres || ''\n    );\n\n    if (typeof window.zaladujParafiePoNazwieIAdresie === 'function') {\n      window.zaladujParafiePoNazwieIAdresie(nazwa, adres, true);\n    }\n  } else {\n    \/\/ awaryjnie, je\u015bli punktu nie uda si\u0119 dobra\u0107\n    loadDetailsUnderMapFromSidebar();\n  }\n});\n      }\n    }).addTo(map);\n\twindow.warstwaGranice = warstwaGranice;\n  });\n\n\n\n\n \/* ==== AJAXY (GLOBALNE) ==== *\/\n\/\/ U\u017cyj tej wersji \u2014 NAJPIERW adres (nawet bez nazwy), potem nazwa+adres (strict), potem nazwa+adres (relaxed)\nwindow.zaladujParafiePoNazwieIAdresie = async function(nazwa, adres, _unused = true) {\n window.aktualneWezwanie = nazwa;\nwindow.aktualnyAdres = adres;\n  const target = document.getElementById('parafia-details');\n  if (!target) { console.warn('Brak #parafia-details'); return; }\n\n  \/\/ normalizacje + odszumianie prefiks\u00f3w w nazwie (pw., \u015bw., itp.)\n  const norm = s => (window.normalizeText ? window.normalizeText(s) : (s || '').trim());\n  const normName = norm(nazwa).replace(\/\\b(pw\\.?|par\\.?|parafia|\u015bw\\.?)\\b\/gi,'').replace(\/\\s+\/g,' ').trim();\n  const normAddr = norm(adres);\n\n  target.innerHTML = '\u23f3 \u0141adowanie danych parafii...';\n\n  async function fetchHtml(useName, strictName) {\n    const url = \"https:\/\/new.archidiecezjalubelska.pl\/wp-admin\/admin-ajax.php\"\n      + \"?action=load_parafia_by_name_address\"\n      + \"&nazwa=\" + encodeURIComponent(useName ? normName : '')   \/\/ \u2190 address-first: NA POCZ\u0104TKU wysy\u0142amy PUST\u0104 nazw\u0119\n      + \"&adres=\" + encodeURIComponent(normAddr)\n      + \"&strict_name=\" + (strictName ? '1' : '0');\n    const res = await fetch(url, { credentials: 'same-origin' });\n    const json = await res.json();\n    return (json && json.success && json.data && json.data.html) ? json.data.html : '';\n  }\n\n  try {\n    \/\/ 1) **tylko adres** (bez nazwy)\n    let html = await fetchHtml(false, false);\n\n    \/\/ 2) je\u015bli brak \u2192 **nazwa+adres (strict)**\n    if (!html.trim() || \/Nie znaleziono parafii\/i.test(html)) {\n      html = await fetchHtml(true, true);\n    }\n\n    \/\/ 3) je\u015bli dalej brak \u2192 **nazwa+adres (relaxed)**\n    if (!html.trim() || \/Nie znaleziono parafii\/i.test(html)) {\n      html = await fetchHtml(true, false);\n    }\n\n    target.innerHTML = html && html.trim()\n      ? html\n      : '<div style=\"padding:8px 12px;border-left:3px solid #f90;background:#fff7ea\">Nie znaleziono parafii po adresie\/nazwie.<\/div>';\n\n    const mapid = document.getElementById('mapid');\n    if (mapid) {\n      const headerOffset = 120;\n      const y = mapid.getBoundingClientRect().top + window.scrollY - headerOffset;\n      window.scrollTo({ top: y, behavior: 'smooth' });\n    }\n  } catch(e) {\n    console.error(e);\n    target.innerHTML = 'Wyst\u0105pi\u0142 b\u0142\u0105d po\u0142\u0105czenia.';\n  }\n    \/\/ przewi\u0144 pod map\u0119\n  const mapid = document.getElementById('mapid');\n  if (mapid) {\n    const headerOffset = 120;\n    const elementPosition = mapid.getBoundingClientRect().top + window.scrollY;\n    const offsetPosition = elementPosition - headerOffset;\n    window.scrollTo({ top: offsetPosition, behavior: 'smooth' });\n  }\n};\n\n\n\/\/ Funkcja szukania nowa po adresie i nazwie na pr\u00f3be\n\nfunction normalizeSearchText(s) {\n  return (s || '')\n    .toLowerCase()\n    .normalize('NFD')\n    .replace(\/[\\u0300-\\u036f]\/g, '')\n    .replace(\/\\u00A0\/g, ' ')\n    .replace(\/[\\u2010-\\u2015]\/g, '-')\n    .replace(\/[.,\/]\/g, ' ')\n    .replace(\/\\bul\\.?\\b\/g, ' ')\n    .replace(\/\\bal\\.\\b\/g, ' ')\n    .replace(\/\\bpw\\.?\\b\/g, ' ')\n    .replace(\/\\bparafia\\b\/g, ' ')\n    .replace(\/\\b\u015bw\\.?\\b\/g, ' swiety ')\n    .replace(\/\\b\u015bwi\u0119tego\\b\/g, ' swiety ')\n    .replace(\/\\b\u015bwi\u0119tej\\b\/g, ' swieta ')\n    .replace(\/\\bnaj\u015bwi\u0119tszej\\b\/g, ' najswietszej ')\n    .replace(\/\\bnmp\\b\/g, ' najswietszej maryi panny ')\n    .replace(\/\\bbm\\b\/g, ' biskupa meczennika ')\n    .replace(\/\\s+\/g, ' ')\n    .trim();\n}\n\nfunction splitTokens(s) {\n  return normalizeSearchText(s).split(' ').filter(Boolean);\n}\n\nfunction tokenScore(a, b) {\n  const A = new Set(splitTokens(a));\n  const B = new Set(splitTokens(b));\n\n  if (!A.size || !B.size) return 0;\n\n  let common = 0;\n  for (const t of A) {\n    if (B.has(t)) common++;\n  }\n\n  return common \/ Math.max(A.size, B.size);\n}\n\nfunction findPointByWezwanieAddress(wezwanie, adres) {\n  if (!window.wszystkiePunktyGeoJSON || !window.wszystkiePunktyGeoJSON.features) return null;\n\n  const wezwanieNorm = normalizeSearchText(wezwanie);\n  const adresNorm = normalizeSearchText(adres);\n\n  let best = null;\n  let bestScore = 0;\n\n  for (const f of window.wszystkiePunktyGeoJSON.features) {\n    const p = f.properties || {};\n    const pWezwanie = p.WEZWANIE || p.wezwanie || '';\n    const pAdres = p.ADRES || p.adres || '';\n\n    const pWezwanieNorm = normalizeSearchText(pWezwanie);\n    const pAdresNorm = normalizeSearchText(pAdres);\n\n    let score = 0;\n\n    if (wezwanieNorm && pWezwanieNorm) {\n      if (pWezwanieNorm === wezwanieNorm) score += 100;\n      else {\n        score += tokenScore(wezwanieNorm, pWezwanieNorm) * 60;\n        if (pWezwanieNorm.includes(wezwanieNorm) || wezwanieNorm.includes(pWezwanieNorm)) {\n          score += 20;\n        }\n      }\n    }\n\n    if (adresNorm && pAdresNorm) {\n      if (pAdresNorm === adresNorm) score += 80;\n      else {\n        score += tokenScore(adresNorm, pAdresNorm) * 40;\n        if (pAdresNorm.includes(adresNorm) || adresNorm.includes(pAdresNorm)) {\n          score += 15;\n        }\n      }\n    }\n\n    const kodA = (adresNorm.match(\/\\b\\d{2}\\s*-\\s*\\d{3}\\b\/) || [])[0];\n    const kodB = (pAdresNorm.match(\/\\b\\d{2}\\s*-\\s*\\d{3}\\b\/) || [])[0];\n    if (kodA && kodB && kodA.replace(\/\\s+\/g, '') === kodB.replace(\/\\s+\/g, '')) {\n      score += 25;\n    }\n\n    if (score > bestScore) {\n      bestScore = score;\n      best = f;\n    }\n  }\n\n  console.log('Dopasowanie punktu:', { wezwanie, adres, bestScore, best });\n\n  return bestScore >= 35 ? best : null;\n}\n\n\n\n  \/\/ \u0141adowanie po ID z opcjonalnym fallbackiem po nazwie+adresie\n  \nwindow.zaladujParafiePoId = async function(parafiaId) {\n  window.aktualnaParafiaId = parafiaId;\n  window.aktualneWezwanie = '';\n  window.aktualnyAdres = '';\n\n  const target = document.getElementById('parafia-details');\n  if (!target || !parafiaId) return;\n\n  target.innerHTML = '\u23f3 \u0141adowanie danych parafii...';\n\n  const url = \"https:\/\/new.archidiecezjalubelska.pl\/wp-admin\/admin-ajax.php\"\n    + \"?action=load_parafia_by_id\"\n    + \"&id=\" + encodeURIComponent(parafiaId);\n\n  try {\n    const res = await fetch(url, { credentials: 'same-origin' });\n    const json = await res.json();\n\n    if (json && json.success && json.data && json.data.html) {\n      window.aktualneWezwanie = json.data.wezwanie || '';\n      window.aktualnyAdres = json.data.adres || '';\n\n      target.innerHTML = json.data.html;\n\n      const toggle = document.getElementById('parafia-miejscowosci-toggle');\n      if (toggle) toggle.style.display = 'block';\n\n      const mapid = document.getElementById('mapid');\nif (mapid) {\n  mapid.scrollIntoView({ behavior: 'smooth', block: 'start' });\n}\n    } else {\n      target.innerHTML = '<div style=\"padding:8px 12px;border-left:3px solid #f90;background:#fff7ea\">Nie znaleziono parafii.<\/div>';\n    }\n  } catch (e) {\n    console.error(e);\n    target.innerHTML = 'Wyst\u0105pi\u0142 b\u0142\u0105d po\u0142\u0105czenia.';\n  }\n};\n\n\n\/\/ Przycisk poka\u017c wi\u0119cej do galerii\n\nfunction initParafiaGalleryToggle(root = document) {\n  const btn = root.querySelector('.parafia-gallery-toggle');\n  const box = root.querySelector('.parafia-gallery-all');\n\n  if (!btn || !box) return;\n\n  btn.addEventListener('click', function() {\n    const isOpen = box.style.display === 'block';\n    box.style.display = isOpen ? 'none' : 'block';\n    btn.textContent = isOpen ? 'Poka\u017c wi\u0119cej' : 'Poka\u017c mniej';\n  });\n}\n  \n  \n  \n  \/*\nwindow.zaladujParafiePoId = async function (parafiaId, fallbackName = '', fallbackAddr = '') {\n  const target = document.getElementById('parafia-details');\n  if (!target || !parafiaId) {\n    if (fallbackName || fallbackAddr) {\n      return window.zaladujParafiePoNazwieIAdresie(fallbackName, fallbackAddr, true);\n    }\n    return;\n  }\n\n  target.innerHTML = '\u23f3 \u0141adowanie danych parafii...';\n\n  const url = \"https:\/\/new.archidiecezjalubelska.pl\/wp-admin\/admin-ajax.php\"\n    + \"?action=load_parafia_by_name_address\"\n    + \"&id=\" + encodeURIComponent(parafiaId);\n\n  try {\n    const res = await fetch(url, { credentials: 'same-origin' });\n    const json = await res.json();\n    const html = (json && json.success && json.data && json.data.html) ? json.data.html : '';\n\n    \/\/ je\u015bli backend nic nie zwr\u00f3ci\u0142 \/ \u201enie znaleziono\u201d \u2192 fallback\n    if (!html || !html.trim() || \/Nie znaleziono parafii|Brak ID parafii\/i.test(html)) {\n      if (fallbackName || fallbackAddr) {\n        return window.zaladujParafiePoNazwieIAdresie(fallbackName, fallbackAddr, true);\n      }\n    } else {\n      target.innerHTML = html;\n      const mapid = document.getElementById('mapid');\n      if (mapid){\n        const headerOffset = 120;\n        const elementPosition = mapid.getBoundingClientRect().top + window.scrollY;\n        const offsetPosition = elementPosition - headerOffset;\n        window.scrollTo({ top: offsetPosition, behavior: 'smooth' });\n      }\n    }\n  } catch(e){\n    console.error(e);\n    if (fallbackName || fallbackAddr) {\n      return window.zaladujParafiePoNazwieIAdresie(fallbackName, fallbackAddr, true);\n    }\n    target.innerHTML = 'Wyst\u0105pi\u0142 b\u0142\u0105d po\u0142\u0105czenia.';\n  }\n}; *\/\n\n\n  \/* ==== UTILs ==== *\/\n  function normTxt(s) {\n    return (s || '')\n      .toLowerCase()\n      .normalize('NFD').replace(\/[\\u0300-\\u036f]\/g, '')\n      .replace(\/\\b(parafia|pw\\.?|pw|sw\\.?|\u015bw\\.?)\\b\/g, '')\n      .replace(\/[^\\p{L}\\p{N}\\s]\/gu, ' ')\n      .replace(\/\\s+\/g, ' ')\n      .trim();\n  }\n  function tokens(s){ return new Set(normTxt(s).split(' ').filter(Boolean)); }\n  function jaccard(aSet,bSet){\n    const a=[...aSet], b=[...bSet];\n    const inter = a.filter(x=>bSet.has(x)).length;\n    const uni = new Set([...aSet, ...bSet]).size || 1;\n    return inter\/uni;\n  }\n  function extractTownFromLinkText(txt){\n    const firstComma = (txt || '').split(',');\n    return (firstComma[0] || '').trim();\n  }\n\n  \/\/ \ud83d\udd27 pod\u015bwietlenie (brakowa\u0142o tej funkcji)\n  function podswietlGranice(layer){\n    if (aktywnaGranica){\n      warstwaGranice.resetStyle(aktywnaGranica);\n      if (animacjaInterval) clearInterval(animacjaInterval);\n    }\n    aktywnaGranica = layer;\n    let i = 0, colors = ['#ff6600','#ffa500'];\n    animacjaInterval = setInterval(()=>{\n      aktywnaGranica.setStyle({ color: colors[i%colors.length], weight:4, fillOpacity:0.4 });\n      i++;\n    }, 500);\n  }\n\n  \/\/  te dwie funkcje by\u0142y u\u017cywane przez \u201eSzukaj na mapie\u201d\n  function parseAddress(input){\n    let raw = (input || '').trim().replace(\/\\s+\/g, ' ');\n    raw = raw.replace(\/\\bul\\.?\\s*\/i, '');\n    let parts = raw.split(',').map(s => s.trim()).filter(Boolean);\n    let city = '', street = '', number = '';\n    if (parts.length >= 2) {\n      city = parts[0];\n      const rest = parts.slice(1).join(' ');\n      const m = rest.match(\/^(.*?)[\\s,]+(\\d+[A-Za-z]?)$\/);\n      if (m) { street = m[1].trim(); number = m[2].trim(); }\n      else { street = rest; }\n    } else {\n      const m = raw.match(\/^(.*?)[\\s,]+(\\d+[A-Za-z]?)$\/);\n      if (m) { street = m[1].trim(); number = m[2].trim(); }\n      else { street = raw; }\n    }\n    if (!city && \/^lublin\\b\/i.test(street)) {\n      street = street.replace(\/^lublin\\b[\\s,]*\/i, '').trim();\n      city = 'Lublin';\n    }\n    return { city, street, number };\n  }\n\n  async function geocodeAddressSmart(input, viewboxBias){\n    const { city, street, number } = parseAddress(input);\n\n    \/\/ 1) Nominatim structured\n    try {\n      const url1 = new URL('https:\/\/nominatim.openstreetmap.org\/search');\n      url1.search = new URLSearchParams({\n        format:'json', addressdetails:'1', countrycodes:'pl',\n        city: city || '', street: number ? `${street} ${number}` : street,\n        limit:'1', 'accept-language':'pl'\n      }).toString();\n      const r1 = await fetch(url1, { headers:{ 'User-Agent':'archilubelska\/1.0 (kontakt@example.com)' }});\n      if (r1.ok) { const j = await r1.json(); if (j && j.length) return j[0]; }\n    } catch(e){}\n\n    \/\/ 2) Nominatim q= + bias\n    try {\n      const url2 = new URL('https:\/\/nominatim.openstreetmap.org\/search');\n      const q = [street, number].filter(Boolean).join(' ') + (city ? `, ${city}` : '') + ', Polska';\n      const params = { format:'json', addressdetails:'1', countrycodes:'pl', q, limit:'1', 'accept-language':'pl' };\n      if (viewboxBias && viewboxBias.length === 4) {\n        params.viewbox = viewboxBias.join(','); params.bounded = '1';\n      }\n      url2.search = new URLSearchParams(params).toString();\n      const r2 = await fetch(url2, { headers:{ 'User-Agent':'archilubelska\/1.0 (kontakt@example.com)' }});\n      if (r2.ok) { const j = await r2.json(); if (j && j.length) return j[0]; }\n    } catch(e){}\n\n    \/\/ 3) Photon fallback\n    try {\n      const q3 = [street, number, city || 'Polska'].filter(Boolean).join(', ');\n      const url3 = new URL('https:\/\/photon.komoot.io\/api');\n      url3.search = new URLSearchParams({ q:q3, lang:'pl', limit:'1' }).toString();\n      const r3 = await fetch(url3);\n      if (r3.ok) {\n        const j = await r3.json();\n        if (j && j.features && j.features.length) {\n          const f = j.features[0];\n          return { lat:f.geometry.coordinates[1], lon:f.geometry.coordinates[0], display_name:f.properties.name };\n        }\n      }\n    } catch(e){}\n\n    \/\/ 4) sama ulica\n    try {\n      const url4 = new URL('https:\/\/nominatim.openstreetmap.org\/search');\n      const q4 = (city ? `${street}, ${city}` : street) + ', Polska';\n      url4.search = new URLSearchParams({\n        format:'json', addressdetails:'1', countrycodes:'pl',\n        q:q4, limit:'1', 'accept-language':'pl'\n      }).toString();\n      const r4 = await fetch(url4, { headers:{ 'User-Agent':'archilubelska\/1.0 (kontakt@example.com)' }});\n      if (r4.ok) { const j = await r4.json(); if (j && j.length) return j[0]; }\n    } catch(e){}\n\n    return null;\n  }\n\n  \/* ==== LOGIKA NAZW \/ GRANIC ==== *\/\n  \/\/ --- ZAMIANA CA\u0141EJ FUNKCJI ---\n  \/*\nwindow.pokazParafiePoNazwie = function(nazwaLinku){\n  \/\/ 1) spr\u00f3buj najpierw trafi\u0107 MARKER po nazwie + miejscowo\u015bci (adresie)\n  const markerHit = znajdzMarkerPoNazwieIMiejscowosci(nazwaLinku);\n\n  let layerDoPodswietlenia = null;\n  let propsDoEtykiety = null;\n\n  if (markerHit && markerHit.layer) {\n    const latlng = markerHit.layer.getLatLng();\n    layerDoPodswietlenia = dobierzGraniceDlaPunktu(latlng, markerHit.props);\n    propsDoEtykiety = markerHit.props;\n  }\n\n  \/\/ 2) je\u015bli nie uda\u0142o si\u0119 przez marker, wr\u00f3\u0107 do fuzzy po granicach\n  if (!layerDoPodswietlenia) {\n    const hit = znajdzGranicePoNazwieFuzzy(nazwaLinku);\n    if (hit && hit.layer){ layerDoPodswietlenia = hit.layer; propsDoEtykiety = hit.props; }\n  }\n\n  \/\/ 3) pod\u015bwietl, je\u015bli cokolwiek znaleziono\n  if (layerDoPodswietlenia){\n    if (aktywnaGranica) {\n      warstwaGranice.resetStyle(aktywnaGranica);\n      if (animacjaInterval) clearInterval(animacjaInterval);\n    }\n    aktywnaGranica = layerDoPodswietlenia;\n\n    let colors = ['#ff6600','#ffa500'], i=0;\n    animacjaInterval = setInterval(()=>{\n      aktywnaGranica.setStyle({ color: colors[i%colors.length], weight:4, fillOpacity:0.4 });\n      i++;\n    },500);\n\n    const p = propsDoEtykiety || layerDoPodswietlenia?.feature?.properties || {};\n    const wezwanie = p.wezwanie_k || p.WEZWANIE_K || p.wezwanie || p.WEZWANIE || nazwaLinku;\n\n    if (etykietaParafii) map.removeLayer(etykietaParafii);\n    etykietaParafii = L.marker(layerDoPodswietlenia.getBounds().getCenter(), {\n      icon: L.divIcon({\n        className: 'parafia-label',\n        html: `<div style=\"background:#fff;border:1px solid rgba(0,0,0,.2);padding:4px 8px;border-radius:6px;box-shadow:0 1px 3px rgba(0,0,0,.2);font-weight:bold\">Parafia: ${wezwanie}<\/div>`\n      }),\n      interactive:false\n    }).addTo(map);\n\n    map.fitBounds(layerDoPodswietlenia.getBounds());\n  }\n\n  \/\/ 4) zawsze do\u0142aduj tabel\u0119 pod map\u0105\n  window.zaladujParafiePoNazwieIAdresie(nazwaLinku, '', false);\n\n  \/\/ 5) przewijanie\n  document.getElementById('mapid')?.scrollIntoView({behavior:'smooth', block:'start'});\n  document.getElementById('parafia-details')?.scrollIntoView({behavior:'smooth', block:'start'});\n};\n*\/\n\n\n  function znajdzMarkerPoNazwieIMiejscowosci(nazwaSurowa){\n    if (!warstwaPunktow) return null;\n\n    const town = normTxt(extractTownFromLinkText(nazwaSurowa));\n    const nameTokens = tokens(nazwaSurowa);\n\n    let best = {score:0, layer:null, props:null};\n\n    warstwaPunktow.eachLayer(layer=>{\n      const p = layer?.feature?.properties || {};\n      const wezwanie = [p.WEZWANIE, p.wezwanie, p.WEZWANIE_K, p.wezwanie_k].find(Boolean) || '';\n      const adres = p.ADRES || p.adres || '';\n\n      const tName = tokens(wezwanie);\n      const sName = jaccard(nameTokens, tName);\n      const hasTown = town && (normTxt(adres).includes(town));\n      const score = sName + (hasTown ? 0.6 : 0);\n\n      if (score > best.score){\n        best = {score, layer, props:p};\n      }\n    });\n\n    return best.score >= 0.45 ? best : null;\n  }\n\n  function dobierzGraniceDlaPunktu(latlng, preferProps){\n    if (!warstwaGranice) return null;\n\n    let kandydaci = [];\n    warstwaGranice.eachLayer(layer=>{\n      const p = layer?.feature?.properties || {};\n      const bounds = layer.getBounds();\n      const contains = bounds.contains(latlng);\n\n      let prefer = 0;\n      if (preferProps){\n        const pw = [p.WEZWANIE, p.wezwanie, p.WEZWANIE_K, p.wezwanie_k].find(Boolean) || '';\n        const mw = [p.MIEJSCOWOSC, p.miejscowosc].find(Boolean) || '';\n        const mkName = [preferProps.WEZWANIE, preferProps.wezwanie, preferProps.WEZWANIE_K, preferProps.wezwanie_k].find(Boolean) || '';\n        const mkAddr = preferProps.ADRES || preferProps.adres || '';\n        const town = extractTownFromLinkText(mkAddr) || extractTownFromLinkText(mw);\n\n        const nScore = jaccard(tokens(pw), tokens(mkName));\n        const tMatch = town ? normTxt(mw).includes(normTxt(town)) || normTxt(mkAddr).includes(normTxt(mw)) : false;\n        prefer = nScore + (tMatch ? 0.5 : 0);\n      }\n\n      const dist = bounds.getCenter().distanceTo(latlng);\n      kandydaci.push({layer, contains, prefer, dist});\n    });\n\n    kandydaci.sort((a,b)=>{\n      if (a.contains !== b.contains) return a.contains ? -1 : 1;\n      if (b.prefer !== a.prefer) return b.prefer - a.prefer;\n      return a.dist - b.dist;\n    });\n\n    return kandydaci.length ? kandydaci[0].layer : null;\n  }\n\n  function znajdzGranicePoNazwieFuzzy(nazwaSurowa){\n    if(!warstwaGranice) return null;\n    const tNeedle = tokens(nazwaSurowa);\n    let best = {score:0, layer:null, props:null};\n\n    warstwaGranice.eachLayer(layer=>{\n      const p = layer?.feature?.properties || {};\n      const kandydaci = [\n        p.wezwanie_k, p.WEZWANIE_K, p.wezwanie, p.WEZWANIE,\n        p.NAZWA, p.nazwa, p.MIEJSCOWOSC, p.miejscowosc, p.ADRES, p.adres\n      ].filter(Boolean);\n\n      if (kandydaci.length === 0) return;\n\n      const txt = kandydaci.join(' ');\n      const score = jaccard(tNeedle, tokens(txt));\n      if(score > best.score){\n        best = {score, layer, props:p};\n      }\n    });\n\n    return best.score >= 0.25 ? best : null;\n  }\n\n  function znajdzGraniceDlaPunktu(latlng) {\n    if (!warstwaGranice || !latlng) return null;\n\n    const lon = latlng.lng !== undefined ? latlng.lng : latlng[1];\n    const lat = latlng.lat !== undefined ? latlng.lat : latlng[0];\n\n    if (typeof lat !== 'number' || typeof lon !== 'number') {\n      console.warn('B\u0142\u0105d: niepoprawne wsp\u00f3\u0142rz\u0119dne', latlng);\n      return null;\n    }\n\n    let znalezionyLayer = null;\n    warstwaGranice.eachLayer(layer => {\n      const f = layer.feature;\n      const pt = turf.point([lon, lat]);\n      if (turf.booleanPointInPolygon(pt, f)) {\n        znalezionyLayer = layer;\n      }\n    });\n    return znalezionyLayer;\n  }\n\n  function znajdzMarkerWPoligonieStrict(layerParafii){\n    if (!warstwaPunktow || !layerParafii) return null;\n    const featPoly = layerParafii.feature;\n    if (!featPoly) return null;\n\n    let found = null;\n    warstwaPunktow.eachLayer(m=>{\n      const c = m?.feature?.geometry?.coordinates;\n      if (!c || c.length < 2) return;\n      const pt = turf.point([c[0], c[1]]);\n      if (turf.booleanPointInPolygon(pt, featPoly)) found = m;\n    });\n    return found;\n  }\n\n  function flyToWithOffset(map, latlng, zoom = 17, offsetTopPx = 120) {\n    const pt = map.project(latlng, zoom).subtract([0, offsetTopPx]);\n    const latlngOffset = map.unproject(pt, zoom);\n    map.flyTo(latlngOffset, zoom, { duration: 0.8 });\n  }\n\n  function esc(str){ return String(str||'').replace(\/[&<>\"']\/g, s => ({'&':'&amp;','<':'&lt;','>':'&gt;','\"':'&quot;',\"'\":'&#39;'}[s])); }\n\n  \/* ==== GLOBALNA WYSZUKIWARKA ==== *\/\n  let globalMarker = null;\n  const btnGlobal = document.getElementById('btn-global-search');\n\n  if (btnGlobal) btnGlobal.addEventListener('click', async (ev) => {\n    ev.preventDefault();\n\n    const inputEl  = document.getElementById('global-adres-search');\n    const resultBox = document.getElementById('global-search-result');\n    const val = (inputEl?.value || '').trim();\n\n    if (resultBox) resultBox.innerHTML = '';\n    if (!val) {\n      if (resultBox) resultBox.innerHTML = '<span style=\"color:red\">\u274c Podaj adres<\/span>';\n      return;\n    }\n\n    const b = map.getBounds();\n    const bias = [b.getWest(), b.getSouth(), b.getEast(), b.getNorth()];\n\n    const hit = await geocodeAddressSmart(val, bias);\n    if (!hit) {\n      if (resultBox) resultBox.innerHTML = `<span style=\"color:red\">\u274c Nie znaleziono adresu \"${esc(val)}\"<\/span>`;\n      return;\n    }\n\n    const lat = parseFloat(hit.lat), lon = parseFloat(hit.lon);\n    const latlng = L.latLng(lat, lon);\n\n    if (globalMarker) { map.removeLayer(globalMarker); globalMarker = null; }\n    globalMarker = L.circleMarker(latlng, { radius:8, color:'black', fillColor:'black', fillOpacity:1 }).addTo(map);\n\n    const layerParafii = znajdzGraniceDlaPunktu(latlng);\n    if (layerParafii){\n      podswietlGranice(layerParafii);\n      map.flyToBounds(layerParafii.getBounds(), {\n        paddingTopLeft:[0,120], maxZoom:16, duration:0.8\n      });\n\n      const markerInside = znajdzMarkerWPoligonieStrict(layerParafii);\n      if (markerInside){\n        if (globalMarker && globalMarker.closePopup) globalMarker.closePopup();\n        markerInside.fire('click');\n      } else {\n        const p = layerParafii.feature?.properties || {};\n        const nazwaParafii = p.wezwanie_k || p.WEZWANIE_K || p.wezwanie || p.WEZWANIE || '';\n        const pid = p.par_id || p.ID || p.id;\n        if (pid) window.zaladujParafiePoId(pid);\n        else     window.zaladujParafiePoNazwieIAdresie(nazwaParafii, '', false);\n      }\n    } else {\n      flyToWithOffset(map, latlng, 17, 120);\n    }\n  });\n\n  \/* ==== OSM\/OVERPASS \u2013 panel boczny ==== *\/\n  async function sprawdzAdresOverpass(miejscowosc, ulica, numer, granicaFeature) {\n    const bbox = turf.bbox(granicaFeature);\n    const [w, s, e, n] = bbox;\n\n    const query = `\n[out:json][timeout:25];\n(\n  node[\"addr:housenumber\"=\"${numer}\"](${s},${w},${n},${e});\n  way[\"addr:housenumber\"=\"${numer}\"](${s},${w},${n},${e});\n  relation[\"addr:housenumber\"=\"${numer}\"](${s},${w},${n},${e});\n);\nout center;`;\n\n    const res = await fetch(\"https:\/\/overpass-api.de\/api\/interpreter\", { method: \"POST\", body: query });\n    const data = await res.json();\n\n    const normalize = str => (str || \"\").toLowerCase().replace(\/[-_]\/g, ' ').trim();\n\n    for (const el of data.elements) {\n      const tags = el.tags || {};\n      const lat = el.type === \"node\" ? el.lat : el.center && el.center.lat;\n      const lon = el.type === \"node\" ? el.lon : el.center && el.center.lon;\n      if (!lat || !lon) continue;\n\n      const m = tags[\"addr:city\"] || tags[\"addr:place\"] || tags[\"addr:village\"] || \"\";\n      const u = tags[\"addr:street\"] || tags[\"addr:place\"] || \"\";\n      const nmr = (tags[\"addr:housenumber\"] || \"\").toLowerCase();\n\n      if (\n        normalize(m).includes(normalize(miejscowosc)) &&\n        nmr === (numer || '').toLowerCase() &&\n        (!ulica || normalize(u).includes(normalize(ulica)))\n      ) {\n        const pt = turf.point([lon, lat]);\n        const wGranicy = turf.booleanPointInPolygon(pt, granicaFeature);\n        return { wGranicy, latlng: [lat, lon] };\n      }\n    }\n    return null;\n  }\n\n  function pobierzAdresyZOverpass(granicaFeature, callback) {\n    const bbox = turf.bbox(granicaFeature);\n    const [w, s, e, n] = bbox;\n    const cacheKey = `adresy_${s}_${w}_${n}_${e}`;\n    const cached = sessionStorage.getItem(cacheKey);\n    if (cached) {\n      callback(JSON.parse(cached));\n      return;\n    }\n\n    const query = `\n[out:json][timeout:25];\n(\n  node[\"addr:housenumber\"](${s},${w},${n},${e});\n  way[\"addr:housenumber\"](${s},${w},${n},${e});\n  relation[\"addr:housenumber\"](${s},${w},${n},${e});\n);\nout center;`;\n\n    fetch(\"https:\/\/overpass-api.de\/api\/interpreter\", { method: \"POST\", body: query })\n      .then(res => res.json())\n      .then(data => {\n        const uporzadkowane = {};\n\n        data.elements.forEach(el => {\n          const tags = el.tags || {};\n          const lat = el.type === \"node\" ? el.lat : el.center && el.center.lat;\n          const lon = el.type === \"node\" ? el.lon : el.center && el.center.lon;\n          if (lat === undefined || lon === undefined) return;\n\n          const point = turf.point([lon, lat]);\n          if (!turf.booleanPointInPolygon(point, granicaFeature)) return;\n\n          const miejscowosc = tags[\"addr:city\"] || tags[\"addr:place\"] || tags[\"addr:hamlet\"] || tags[\"addr:village\"];\n\n          let ulica = tags[\"addr:street\"];\n          if (!ulica && tags[\"addr:place\"]) ulica = tags[\"addr:place\"];\n          if (!ulica) ulica = \"Bez nazwy ulicy\";\n          const numer = tags[\"addr:housenumber\"] || \"-\";\n\n          const kluczMiejscowosc = miejscowosc || \"\";\n          if (!uporzadkowane[kluczMiejscowosc]) uporzadkowane[kluczMiejscowosc] = {};\n          if (!uporzadkowane[kluczMiejscowosc][ulica]) uporzadkowane[kluczMiejscowosc][ulica] = new Set();\n          uporzadkowane[kluczMiejscowosc][ulica].add(numer);\n        });\n\n        for (let m in uporzadkowane) {\n          for (let u in uporzadkowane[m]) {\n            uporzadkowane[m][u] = Array.from(uporzadkowane[m][u])\n              .sort((a, b) => a.localeCompare(b, undefined, { numeric: true }));\n          }\n        }\n\n        sessionStorage.setItem(cacheKey, JSON.stringify(uporzadkowane));\n        callback(uporzadkowane);\n      })\n      .catch(err => {\n        console.error(\"B\u0142\u0105d Overpass:\", err);\n        callback({});\n      });\n  }\n\n  function pokazSzczegolyParafii(granicaFeature) {\n    if (!wszystkiePunktyGeoJSON) return;\n\n    const przypisanePunkty = wszystkiePunktyGeoJSON.features.filter(f =>\n      turf.booleanPointInPolygon(f, granicaFeature)\n    );\n\n    let adresParafii = przypisanePunkty.length > 0\n      ? przypisanePunkty[0].properties.ADRES\n      : \"Brak adresu\";\n\n    document.getElementById('sidebar-content').innerHTML = `\n     \n      <h4>\ud83c\udfe0 Miejscowo\u015bci nale\u017c\u0105ce do parafii:<\/h4>\n      <div id=\"adresy-osm\">\u23f3 \u0141adowanie adres\u00f3w...<\/div>\n    `;\n\n    document.getElementById('sidebar').style.display = 'block';\n\n    pobierzAdresyZOverpass(granicaFeature, function(daneAdresowe) {\n  const kontener = document.getElementById(\"adresy-osm\");\n  if (!kontener) return;\n\n  \/\/ sp\u0142aszczamy dane (jak dot\u0105d)\n  const danePosortowane = Object.keys(daneAdresowe).sort().flatMap(m => {\n    const ulice = Object.keys(daneAdresowe[m]).sort();\n    return ulice.map(u => ({\n      miejscowosc: m,\n      ulica: u,\n      numery: daneAdresowe[m][u]\n    }));\n  });\n\n  const COLLAPSED_COUNT = 2;   \/\/ ile wierszy w skr\u00f3cie\n  const PAGE_SIZE       = 10;  \/\/ ile na stron\u0119 po rozwini\u0119ciu\n\n  \/\/ \u2014\u2014\u2014 Paginacja (widok \u201ejak dotychczas\u201d) \u2014\u2014\u2014\n  function renderPaged(page) {\n    const total = danePosortowane.length;\n    const pages = Math.max(1, Math.ceil(total \/ PAGE_SIZE));\n    const p = Math.min(Math.max(1, page || 1), pages);\n\n    const start = (p - 1) * PAGE_SIZE;\n    const end   = start + PAGE_SIZE;\n    const slice = danePosortowane.slice(start, end);\n\n    let html = \"<ul style='margin:0;padding-left:18px'>\";\n    slice.forEach(w => {\n      html += `<li>${\n        w.miejscowosc && w.miejscowosc.trim().length > 0 ? `<strong>${w.miejscowosc}<\/strong>, ` : ''\n      }${w.ulica} ${w.numery.join(\", \")}<\/li>`;\n    });\n    html += \"<\/ul>\";\n\n    \/\/ pager: \u00ab poprzednia 1 2 3 ... nast\u0119pna \u00bb\n    let pager = `<div class=\"parafia-pager\" style=\"margin-top:10px; display:flex; gap:6px; flex-wrap:wrap; align-items:center;\">`;\n\n    function btn(label, targetPage, disabled) {\n      return `<button type=\"button\" class=\"parafia-page-btn\" data-page=\"${targetPage}\" ${disabled?'disabled':''}>${label}<\/button>`;\n    }\n\n    pager += btn('\u00ab', 1, p===1);\n    pager += btn('\u2039', p-1, p===1);\n\n    \/\/ kr\u00f3tkie numerowanie (maks 9 przycisk\u00f3w)\n    const span = 4;\n    let from = Math.max(1, p - span);\n    let to   = Math.min(pages, p + span);\n    if (to - from < span*2) {\n      if (from === 1) to = Math.min(pages, from + span*2);\n      else if (to === pages) from = Math.max(1, to - span*2);\n    }\n\n    for (let i = from; i <= to; i++) {\n      pager += `<button type=\"button\" class=\"parafia-page-btn\" data-page=\"${i}\" ${i===p?'disabled':''}>${i}<\/button>`;\n    }\n\n    pager += btn('\u203a', p+1, p===pages);\n    pager += btn('\u00bb', pages, p===pages);\n\n    \/\/ (opcjonalnie) link do powrotu w skr\u00f3t\n    pager += `<span style=\"flex:1\"><\/span><button type=\"button\" id=\"parafia-less\" class=\"parafia-less-btn\">Poka\u017c mniej<\/button>`;\n\n    pager += `<\/div>`;\n\n    kontener.innerHTML = html + pager;\n\n    \/\/ wystylizuj przyciski jak \u201eZamknij\u201d\n    const closeBtn = document.querySelector('#sidebar button, .parafia-panel button, .parafiaDialog button');\n    const styleBtn = (el) => {\n      if (!el) return;\n      const cs = closeBtn ? getComputedStyle(closeBtn) : null;\n      el.style.backgroundColor = cs?.backgroundColor || '#d9534f';\n      el.style.color           = cs?.color           || '#fff';\n      el.style.border          = cs?.border          || '1px solid #c9433f';\n      el.style.borderRadius    = cs?.borderRadius    || '3px';\n      el.style.padding         = cs?.padding         || '6px 10px';\n      el.style.cursor          = 'pointer';\n    };\n\n    kontener.querySelectorAll('.parafia-page-btn').forEach(btn => {\n      styleBtn(btn);\n      btn.addEventListener('click', (ev) => {\n        const target = parseInt(ev.currentTarget.getAttribute('data-page'), 10);\n        if (!isNaN(target)) renderPaged(target);\n      });\n    });\n\n    const lessBtn = kontener.querySelector('#parafia-less');\n    if (lessBtn) {\n      styleBtn(lessBtn);\n      lessBtn.addEventListener('click', () => renderCollapsed());\n    }\n  }\n\n  \/\/ \u2014\u2014\u2014 Skr\u00f3t (5 pozycji + \u201ePoka\u017c wi\u0119cej\u201d) \u2014\u2014\u2014\n  function renderCollapsed() {\n    const items = danePosortowane.slice(0, COLLAPSED_COUNT);\n\n    let html = \"<ul style='margin:0;padding-left:18px'>\";\n    items.forEach(w => {\n      html += `<li>${\n        w.miejscowosc && w.miejscowosc.trim().length > 0 ? `<strong>${w.miejscowosc}<\/strong>, ` : ''\n      }${w.ulica} ${w.numery.join(\", \")}<\/li>`;\n    });\n    html += \"<\/ul>\";\n\n    \/\/ przycisk \u201ePoka\u017c wi\u0119cej\u201d gdy lista d\u0142u\u017csza ni\u017c 5\n    if (danePosortowane.length > COLLAPSED_COUNT) {\n      html += `\n        <div style=\"text-align:center; margin-top:10px;\">\n          <button type=\"button\" id=\"btn-parafia-more\" class=\"parafia-more-btn\">Poka\u017c wi\u0119cej<\/button>\n        <\/div>\n      `;\n    }\n\n    kontener.innerHTML = html;\n\n    const moreBtn = kontener.querySelector('#btn-parafia-more');\n    if (moreBtn) {\n      \/\/ styl jak \u201eZamknij\u201d\n      const closeBtn = document.querySelector('#sidebar button, .parafia-panel button, .parafiaDialog button');\n      const cs = closeBtn ? getComputedStyle(closeBtn) : null;\n      moreBtn.style.backgroundColor = cs?.backgroundColor || '#d9534f';\n      moreBtn.style.color           = cs?.color           || '#fff';\n      moreBtn.style.border          = cs?.border          || '1px solid #c9433f';\n      moreBtn.style.borderRadius    = cs?.borderRadius    || '3px';\n      moreBtn.style.padding         = cs?.padding         || '6px 10px';\n      moreBtn.style.cursor          = 'pointer';\n\n      moreBtn.addEventListener('click', () => renderPaged(1));\n    }\n  }\n\n  \/\/ start: skr\u00f3t + \u201ePoka\u017c wi\u0119cej\u201d\n  renderCollapsed();\n});\n\n\n  }\n  \n  \/* Znajd\u017a parafie pod ID na u\u017cytek klikania w list\u0119\n  \n  function findPointByParafiaId(id){\n  if (!window.wszystkiePunktyGeoJSON) return null;\n\n  return (wszystkiePunktyGeoJSON.features || []).find(f => {\n    return String(f.properties?.par_ID) === String(id)\n        || String(f.properties?.par_id) === String(id)\n        || String(f.properties?.ID) === String(id);\n  }) || null;\n} *\/\n\n  \/* ==== PODPINANIE LINK\u00d3W Z LISTY ==== \n  document.addEventListener('click', function(e){\n    const a = e.target.closest('a');\n    if (!a) return;\n\n    const href = (a.getAttribute('href')||'').toLowerCase();\n\n    if (\/\\\/parafie-1\\\/\\?id=\\d+\/.test(href)) {\n      e.preventDefault();\n      const nazwa = (a.textContent || '').trim();\n      if (nazwa) window.pokazParafiePoNazwie(nazwa);\n    }\n  }, true);\n\n  document.addEventListener('click', function(e) {\n    const a = e.target.closest('a');\n    if (!a) return;\n\n    if (a.closest('.entry-content, .post-content, #content, .parafie-list') && \/parafie\/i.test(a.href)) {\n      const nazwa = (a.textContent || '').trim();\n      if (nazwa) {\n        e.preventDefault();\n        window.pokazParafiePoNazwie(nazwa);\n      }\n    }\n  }, true); *\/\n  \n  \n  \/* Do obs\u0142ugi przycisku Poka\u017c szczeg\u00f3\u0142y parafii i panelu bocznego\n  \n  document.addEventListener('click', function(e){\n  const btn = e.target.closest('.parafia-miejscowosci-btn');\n  if (!btn) return;\n\n  e.preventDefault();\n  e.stopPropagation();\n\n  const sidebar = document.getElementById('sidebar');\n  if (sidebar) {\n    sidebar.style.display = 'block';\n\n    sidebar.scrollIntoView({\n      behavior: 'smooth',\n      block: 'start'\n    });\n  }\n}); *\/\n\ndocument.addEventListener('click', function(e){\n  const btn = e.target.closest('.parafia-miejscowosci-btn');\n  if (!btn) return;\n\n  e.preventDefault();\n\n  const wezwanie = window.aktualneWezwanie || '';\n  const adres = window.aktualnyAdres || '';\n\n  if (!wezwanie && !adres) {\n    alert('Najpierw wybierz parafi\u0119');\n    return;\n  }\n\n  const point = findPointByWezwanieAddress(wezwanie, adres);\n  console.log('Klik przycisku:', { wezwanie, adres, point });\n\n  if (!point) {\n    console.warn('Nie znaleziono punktu dla parafii:', wezwanie, adres);\n    return;\n  }\n\n  const [lng, lat] = point.geometry.coordinates;\n  const latlng = L.latLng(lat, lng);\n\n  const poly = znajdzGraniceDlaPunktu(latlng);\n\n  if (!poly) { \n    console.warn('Nie znaleziono granicy dla punktu:', point);\n    return;\n  }\n\n  pokazSzczegolyParafii(poly.feature);\n\n  const sidebar = document.getElementById('sidebar');\n  if (sidebar) {\n    sidebar.style.display = 'block';\n  }\n});\n\n\n\n\n});\n<\/script>\n\n\n\n\n    <\/div>\n        <div class=\"u-expanded-width-xs u-shortcode u-shortcode-3\"><\/div>\n      <\/div>\n    <\/section>\n    \n    \n    \n    \n  \n<\/div>","protected":false},"excerpt":{"rendered":"<p>Parafie [>A<\/p>\n","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"footnotes":""},"class_list":["post-1064","page","type-page","status-publish","has-post-thumbnail","hentry"],"_links":{"self":[{"href":"https:\/\/new.archidiecezjalubelska.pl\/index.php\/wp-json\/wp\/v2\/pages\/1064","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/new.archidiecezjalubelska.pl\/index.php\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/new.archidiecezjalubelska.pl\/index.php\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/new.archidiecezjalubelska.pl\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/new.archidiecezjalubelska.pl\/index.php\/wp-json\/wp\/v2\/comments?post=1064"}],"version-history":[{"count":37,"href":"https:\/\/new.archidiecezjalubelska.pl\/index.php\/wp-json\/wp\/v2\/pages\/1064\/revisions"}],"predecessor-version":[{"id":1563,"href":"https:\/\/new.archidiecezjalubelska.pl\/index.php\/wp-json\/wp\/v2\/pages\/1064\/revisions\/1563"}],"wp:attachment":[{"href":"https:\/\/new.archidiecezjalubelska.pl\/index.php\/wp-json\/wp\/v2\/media?parent=1064"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}