{"id":50074,"date":"2026-04-17T21:12:32","date_gmt":"2026-04-17T21:12:32","guid":{"rendered":"https:\/\/zeas.immo\/?page_id=50074"},"modified":"2026-07-30T23:17:22","modified_gmt":"2026-07-30T23:17:22","slug":"exp-luxembourg-properties","status":"publish","type":"page","link":"https:\/\/zeas.immo\/en\/exp-luxembourg-properties\/","title":{"rendered":"eXp Luxembourg properties"},"content":{"rendered":"\t\t<div data-elementor-type=\"wp-page\" data-elementor-id=\"50074\" class=\"elementor elementor-50074\" data-elementor-post-type=\"page\">\n\t\t\t\t<div class=\"elementor-element elementor-element-22ae88c e-flex e-con-boxed e-con e-parent\" data-id=\"22ae88c\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t\t\t<div class=\"elementor-element elementor-element-cfc7c35 elementor-widget elementor-widget-html\" data-id=\"cfc7c35\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<!-- \n  ZEAS \u2014 eXp Luxembourg Properties Page  (v4)\n  Paste into Elementor HTML widget on \/en\/exp-luxembourg-properties\/\n  Section \u2192 Full Width, Container \u2192 Full Width, Padding 0\n-->\n<style>\n\/* \u2500\u2500 Base \u2500\u2500 *\/\n\/* :where() keeps this reset at zero specificity so the page's own class rules\n   (card padding, count\/pagination margins) actually win instead of being\n   collapsed by the ID-scoped \"#zlapp *\" reset. *\/\n:where(#zlapp) * { box-sizing: border-box; margin: 0; padding: 0; }\n#zlapp {\n  font-family: var(--font-body);\n  color: var(--surface-dark);\n  padding: 60px 0 80px;\n}\n\/* One consistent font across the page + its modals (matches homepage) *\/\n#zlapp, #zlapp *,\n.zl-overlay, .zl-overlay *,\n.zl-enq-overlay, .zl-enq-overlay *,\n.zl-lightbox, .zl-lightbox * {\n  font-family: var(--font-body) !important;\n}\n.zl-inner {\n  max-width: 1240px;\n  margin: 0 auto;\n  padding: 0 60px;\n}\n\n\/* \u2500\u2500 Page heading \u2500\u2500 *\/\n.zl-page-hd {\n  text-align: center;\n  margin-bottom: 20px !important;\n}\n.zl-eyebrow {\n  display: flex; align-items: center; justify-content: center; gap: 12px;\n  font-size: var(--text-sm); font-weight: var(--weight-semibold);\n  letter-spacing: var(--track-eyebrow); text-transform: uppercase;\n  color: var(--text-dark-secondary); margin-bottom: 22px;\n}\n.zl-eyebrow::before, .zl-eyebrow::after {\n  content: ''; width: 24px; height: 1px; background: var(--text-dark-secondary); flex-shrink: 0;\n}\n.zl-page-title {\n  font-family: var(--font-body);\n  font-size: var(--h1);\n  font-weight: var(--weight-medium); color: var(--surface-dark);\n  line-height: var(--lh-tight); letter-spacing: var(--track-tight);\n  margin: 0 0 20px 0 !important;\n}\n.zl-page-sub {\n  font-size: var(--text-md); color: var(--text-sec); line-height: var(--lh-relaxed);\n  text-align: center !important;\n  max-width: 660px !important;\n  margin: 0 auto !important;\n}\n.zl-page-sub em { color: var(--surface-dark); font-style: italic; }\n.zl-section-title {\n  font-family: var(--font-body);\n  font-size: var(--text-lg); font-weight: var(--weight-semibold); color: var(--surface-dark);\n  margin: 28px 0 16px 0 !important;\n}\n\n\/* \u2500\u2500 Scope tabs: \"All Listings\" \/ \"Our Listings\" \u2014 a filter dimension,\n      styled to match the equivalent tabs on the homepage. \"Our Listings\"\n      stays visually orange even when inactive, so it reads as the\n      special\/branded option next to the neutral \"All Listings\". \u2500\u2500 *\/\n.zl-tabs { display: flex; justify-content: center; gap: 10px; margin: 0 0 28px; flex-wrap: wrap; }\n.zl-tab {\n  display: inline-flex; align-items: center; gap: 8px;\n  font-family: var(--font-body);\n  font-size: var(--text-base); font-weight: var(--weight-semibold); letter-spacing: var(--track-wide);\n  padding: 14px 28px; border: 1.5px solid var(--line); background: #ffffff; color: var(--surface-dark);\n  cursor: pointer; transition: border-color 150ms, color 150ms, background 150ms;\n}\n.zl-tab:hover { border-color: var(--accent); color: var(--accent); }\n.zl-tab.active { background: var(--surface-dark); border-color: var(--surface-dark); color: #ffffff; }\n.zl-tab-count { font-size: var(--text-sm); font-weight: var(--weight-body); color: var(--text-muted); }\n.zl-tab.active .zl-tab-count { color: rgba(255,255,255,.62); }\n.zl-tab-mine { border-color: var(--accent); color: var(--accent); }\n.zl-tab-mine .zl-tab-count { color: var(--accent); opacity: .75; }\n.zl-tab-mine:hover { background: var(--accent); color: #ffffff; }\n.zl-tab-mine:hover .zl-tab-count { color: #ffffff; opacity: .8; }\n.zl-tab-mine.active { background: var(--accent); border-color: var(--accent); color: #ffffff; }\n.zl-tab-mine.active .zl-tab-count { color: #ffffff; opacity: .8; }\n@media (max-width: 480px) { .zl-tabs { flex-direction: column; } }\n\n\/* \u2500\u2500 Filter bar \u2500\u2500 *\/\n.zl-filters {\n  display: flex; gap: 8px; flex-wrap: wrap;\n  margin-bottom: 12px;\n}\n.zl-filter-row {\n  display: flex !important; align-items: center !important; gap: 12px !important;\n  flex-wrap: wrap !important; margin-bottom: 14px !important;\n  justify-content: center !important;\n}\n.zl-filter-label {\n  font-size: var(--text-eyebrow) !important; font-weight: var(--weight-semibold) !important;\n  letter-spacing: var(--track-eyebrow) !important; text-transform: uppercase !important;\n  color: var(--text-muted) !important; flex-shrink: 0 !important;\n  font-family: var(--font-body) !important;\n}\n.zl-filters { display: flex; gap: 6px; flex-wrap: wrap; }\n.zl-filter-btn {\n  font-family: var(--font-body) !important;\n  font-size: var(--text-eyebrow) !important; font-weight: var(--weight-medium) !important;\n  padding: 5px 14px !important;\n  border: 1.5px solid var(--line) !important;\n  background: #ffffff !important; color: var(--text-sec) !important;\n  cursor: pointer !important;\n  transition: all 150ms;\n  text-transform: uppercase !important; letter-spacing: var(--track-wide) !important;\n  white-space: nowrap !important;\n}\n.zl-filter-btn:hover { border-color: var(--accent) !important; color: var(--accent) !important; }\n.zl-filter-btn.active { background: var(--accent) !important; border-color: var(--accent) !important; color: #ffffff !important; }\n\n.zl-filter-select {\n  font-family: var(--font-body) !important;\n  font-size: var(--text-xs) !important; color: var(--text-sec) !important;\n  padding: 5px 26px 5px 11px !important;\n  border: 1.5px solid var(--line) !important;\n  background: #ffffff url(\"data:image\/svg+xml,%3Csvg xmlns='http:\/\/www.w3.org\/2000\/svg' width='10' height='6' viewBox='0 0 10 6'%3E%3Cpath d='M1 1l4 4 4-4' stroke='%23999895' stroke-width='1.5' fill='none' stroke-linecap='round'\/%3E%3C\/svg%3E\") no-repeat right 9px center !important;\n  appearance: none !important; -webkit-appearance: none !important;\n  cursor: pointer !important; outline: none !important;\n}\n.zl-filter-select:focus { border-color: var(--accent) !important; }\n\n\/* \u2500\u2500 Refine: commune + price, no box, spaced out \u2500\u2500 *\/\n.zl-refine {\n  display: flex; flex-wrap: wrap; align-items: center; justify-content: center;\n  gap: 48px; margin: 34px auto 40px !important; max-width: 760px !important;\n}\n.zl-f-field { display: flex; flex-direction: column; gap: 11px; min-width: 0; }\n.zl-f-label {\n  font-size: var(--text-eyebrow); font-weight: var(--weight-semibold); letter-spacing: var(--track-eyebrow); text-transform: uppercase;\n  color: var(--text-muted); text-align: left;\n}\n.zl-f-divider { display: none; }\n.zl-f-price { flex: 1 1 300px; }\n\n.zl-select-wrap { position: relative; display: block; }\n.zl-select-pin { position: absolute; left: 15px !important; top: 50%; transform: translateY(-50%); color: var(--accent); pointer-events: none; z-index: 1; }\n.zl-select {\n  font-size: var(--text-sm); color: var(--surface-dark); font-weight: var(--weight-medium);\n  padding: 12px 38px 12px 42px !important; min-width: 210px; width: 100%;\n  border: 1.5px solid var(--line); background-color: #ffffff;\n  background-image: url(\"data:image\/svg+xml,%3Csvg xmlns='http:\/\/www.w3.org\/2000\/svg' width='10' height='6' viewBox='0 0 10 6'%3E%3Cpath d='M1 1l4 4 4-4' stroke='%23555552' stroke-width='1.5' fill='none' stroke-linecap='round'\/%3E%3C\/svg%3E\");\n  background-repeat: no-repeat; background-position: right 14px center;\n  appearance: none; -webkit-appearance: none; cursor: pointer; outline: none;\n  transition: border-color 180ms;\n}\n.zl-select:hover { border-color: var(--text-dark-body); }\n.zl-select:focus { border-color: var(--accent); }\n\n\/* Price range \u2014 two plain dropdowns (min \/ max) instead of a slider,\n   matching the homepage's price filter structure. Reuses .zl-select\n   styling; just narrower, since two sit side by side. *\/\n.zl-price-fields { display: flex; align-items: center; gap: 10px; height: 48px; }\n.zl-price-select { flex: 1 1 0; min-width: 0; }\n.zl-price-select .zl-select { min-width: 0; width: 100%; padding: 12px 30px 12px 14px !important; }\n.zl-price-sep { color: var(--text-muted); font-size: var(--text-sm); flex-shrink: 0; }\n\n@media (max-width: 620px) {\n  .zl-refine { flex-direction: column; gap: 26px; }\n  .zl-f-divider { display: none; }\n  \/* flex: 1 1 auto resets .zl-f-price's desktop flex-basis (300px) \u2014\n     under flex-direction:column that basis controls height, not width,\n     stretching the price row and leaving a large empty gap below it. *\/\n  .zl-f-field, .zl-f-price { width: 100%; flex: 1 1 auto; }\n}\n\n\/* \u2500\u2500 Count \u2014 PATCH 1: larger + darker \u2500\u2500 *\/\n.zl-count {\n  font-size: var(--text-sm); color: var(--text-sec); font-weight: var(--weight-medium);\n  margin: 8px 0 44px; text-align: center;\n}\n.zl-count strong { color: var(--surface-dark); font-weight: var(--weight-bold); }\n\n\/* \u2500\u2500 Loading \/ error \/ empty \u2500\u2500 *\/\n.zl-loading {\n  display: flex; align-items: center; gap: 12px;\n  color: var(--text-muted); font-size: var(--text-sm); padding: 40px 0;\n}\n.zl-spinner {\n  width: 20px; height: 20px;\n  border: 2px solid var(--line); border-top-color: var(--accent);\n  border-radius: 50%; animation: zlspin .7s linear infinite; flex-shrink: 0;\n}\n@keyframes zlspin { to { transform: rotate(360deg); } }\n.zl-error { font-size: var(--text-sm); color: var(--error); padding: 20px 0; }\n.zl-empty { text-align: center; padding: 60px 0; color: var(--text-muted); font-size: var(--text-base); }\n\n\/* \u2500\u2500 Grid \u2500\u2500 *\/\n.zl-grid {\n  display: grid;\n  grid-template-columns: repeat(3, 1fr);\n  gap: 28px;\n}\n\n\/* \u2500\u2500 Card \u2014 aligned to the International \/ homepage editorial card \u2500\u2500 *\/\n.zl-card {\n  border: 1px solid var(--line); background: #ffffff;\n  display: flex; flex-direction: column; cursor: pointer;\n  transition: transform .25s, box-shadow .25s;\n}\n.zl-card:hover { transform: translateY(-4px); box-shadow: 0 14px 40px rgba(17,17,16,.12); }\n.zl-card.is-featured { border-color: var(--accent); }\n.zl-card.is-featured:hover { border-color: var(--accent); }\n.zl-card-img {\n  position: relative; aspect-ratio: 4\/3;\n  overflow: hidden; background: var(--surface-sunken);\n}\n.zl-card-img img {\n  width: 100%; height: 100%;\n  object-fit: cover; display: block;\n  transition: transform .5s;\n}\n.zl-card:hover .zl-card-img img { transform: scale(1.045); }\n.zl-card-badge {\n  position: absolute; top: 14px; left: 14px; z-index: 2;\n  font-size: var(--text-xs); font-weight: var(--weight-bold);\n  letter-spacing: var(--track-eyebrow); text-transform: uppercase;\n  padding: 6px 11px;\n  background: var(--accent); color: #ffffff;\n}\n.zl-card-badge.rental { background: var(--surface-dark); }\n.zl-card-type {\n  position: absolute; bottom: 14px; left: 14px; z-index: 2;\n  font-size: var(--text-xs); font-weight: var(--weight-medium); letter-spacing: var(--track-wide);\n  background: rgba(17,17,16,.68); backdrop-filter: blur(5px);\n  color: #ffffff; padding: 3px 9px;\n}\n\/* eXp network badge \u2014 top-right, styled like the International country chip *\/\n.zl-card-network-badge {\n  position: absolute; top: 14px; right: 14px; z-index: 2;\n  font-family: var(--font-body);\n  font-size: var(--text-xs); font-weight: var(--weight-semibold);\n  letter-spacing: var(--track-eyebrow); text-transform: uppercase;\n  padding: 5px 10px;\n  background: rgba(255,255,255,0.92);\n  color: var(--surface-dark);\n}\n\/* Direct listing badge \u2014 same slot, orange, for your own Luxembourg listings *\/\n.zl-card-mine-badge {\n  position: absolute; top: 14px; right: 14px; z-index: 2;\n  font-family: var(--font-body);\n  font-size: var(--text-xs); font-weight: var(--weight-bold);\n  letter-spacing: var(--track-eyebrow); text-transform: uppercase;\n  padding: 5px 10px;\n  background: var(--accent);\n  color: #ffffff;\n}\n.zl-card-body { padding: 24px 24px 26px; display: flex; flex-direction: column; gap: 12px; flex: 1; }\n.zl-card-city {\n  font-size: var(--text-sm); letter-spacing: var(--track-wide); text-transform: uppercase; color: var(--text-muted);\n  display: flex; align-items: center; gap: 6px;\n}\n.zl-card-city svg { flex-shrink: 0; color: var(--accent); }\n.zl-card-title { font-size: var(--text-md); font-weight: var(--weight-medium); line-height: var(--lh-body); letter-spacing: var(--track-normal); color: var(--surface-dark); }\n.zl-card-desc { font-size: var(--text-base); line-height: var(--lh-relaxed); color: var(--text-sec); flex: 1; }\n.zl-card-foot {\n  display: flex; align-items: flex-end; justify-content: space-between;\n  padding-top: 16px; border-top: 1px solid var(--line); margin-top: 4px; gap: 12px;\n  \/* The call to action is nowrap and cannot shrink, so a long price used to\n     break mid-number and orphan the euro sign onto its own line. Allowing\n     the row to wrap drops the CTA below instead \u2014 the price always stays\n     whole. *\/\n  flex-wrap: wrap;\n}\n.zl-card-price { font-size: var(--text-xl); font-weight: var(--weight-semibold); color: var(--accent); line-height: var(--lh-none); white-space: nowrap; font-variant-numeric: tabular-nums; }\n.zl-card-price small { display: block; font-size: var(--text-sm); color: var(--text-dark-secondary); font-weight: var(--weight-body); margin-top: 6px; letter-spacing: var(--track-wide); }\n.zl-card-enq {\n  font-size: var(--text-sm); font-weight: var(--weight-semibold); color: var(--surface-dark);\n  display: inline-flex; align-items: center; gap: 6px;\n  border-bottom: 1.5px solid var(--accent); padding-bottom: 2px; white-space: nowrap;\n}\n\n\/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n   PROPERTY MODAL\n\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 *\/\n.zl-overlay {\n  display: none; position: fixed; inset: 0;\n  background: rgba(17,17,16,.78);\n  backdrop-filter: blur(4px); -webkit-backdrop-filter: blur(4px);\n  z-index: 99999; overflow-y: auto;\n  padding: 24px 20px;\n  align-items: flex-start; justify-content: center;\n}\n.zl-overlay.open { display: flex; }\n.zl-modal {\n  background: #ffffff; width: 100%; max-width: 1100px;\n  margin: auto; position: relative; overflow: hidden;\n  animation: zlmodalin .22s ease;\n}\n@keyframes zlmodalin {\n  from { opacity:0; transform:translateY(14px); }\n  to   { opacity:1; transform:translateY(0); }\n}\n.zl-modal-close {\n  position: absolute; top: 14px; right: 14px; z-index: 10;\n  width: 40px; height: 40px;\n  background: #ffffff; border: none; border-radius: 50%;\n  cursor: pointer; display: flex; align-items: center; justify-content: center;\n  color: var(--surface-dark); transition: background 180ms, color 180ms;\n  box-shadow: 0 2px 8px rgba(17,17,16,.22);\n}\n.zl-modal-close:hover { background: var(--accent); color: #ffffff; }\n.zl-modal-gallery {\n  position: relative; height: 60vh; min-height: 320px; max-height: 640px;\n  background: var(--surface-dark); overflow: hidden; cursor: zoom-in;\n}\n.zl-modal-gallery img { width:100%; height:100%; object-fit:cover; display:block; }\n.zl-modal-gallery.has-multi::before,\n.zl-modal-gallery.has-multi::after {\n  content: ''; position: absolute; top: 0; bottom: 0; width: 110px; z-index: 2; pointer-events: none;\n}\n.zl-modal-gallery.has-multi::before { left: 0;  background: linear-gradient(to right, rgba(17,17,16,.45), rgba(17,17,16,0)); }\n.zl-modal-gallery.has-multi::after  { right: 0; background: linear-gradient(to left,  rgba(17,17,16,.45), rgba(17,17,16,0)); }\n.zl-gal-btn {\n  position: absolute; top: 50%; transform: translateY(-50%);\n  width: 48px; height: 48px;\n  background: rgba(17,17,16,.62); border: 1.5px solid rgba(255,255,255,.7); border-radius: 50%;\n  cursor: pointer; color: #ffffff;\n  display: flex; align-items: center; justify-content: center;\n  transition: background 180ms, transform 180ms; z-index: 6;\n  box-shadow: 0 3px 14px rgba(0,0,0,.4);\n}\n.zl-gal-btn svg { width: 22px; height: 22px; }\n.zl-gal-btn:hover { background: var(--accent); border-color: var(--accent); transform: translateY(-50%) scale(1.06); }\n.zl-gal-prev { left: 16px; }\n.zl-gal-next { right: 16px; }\n.zl-gal-ctr {\n  position: absolute; bottom: 14px; right: 16px; z-index: 6;\n  background: rgba(17,17,16,.72); color: #ffffff;\n  font-size: var(--text-xs); font-weight: var(--weight-semibold); padding: 5px 12px; letter-spacing: var(--track-wide);\n  display: inline-flex; align-items: center; gap: 6px;\n}\n.zl-modal-body { padding: 28px 32px 32px; }\n.zl-modal-top {\n  display: flex; align-items: flex-start;\n  justify-content: space-between; gap: 16px; flex-wrap: wrap;\n  margin-bottom: 20px;\n}\n.zl-modal-title {\n  font-size: var(--text-lg); font-weight: var(--weight-medium); color: var(--surface-dark);\n  letter-spacing: var(--track-snug); margin-bottom: 6px;\n}\n\/* PATCH 7: modal address 14px \/ var(--text-dark-secondary) *\/\n.zl-modal-addr {\n  font-size: var(--text-sm); color: var(--text-dark-secondary);\n  display: flex; align-items: center; gap: 5px;\n}\n\/* PATCH 8: attribution line *\/\n.zl-listing-attr {\n  font-family: var(--font-body);\n  font-size: var(--text-xs); color: var(--text-muted); font-style: italic;\n  margin-top: 6px; letter-spacing: var(--track-wide);\n}\n.zl-modal-price {\n  font-size: var(--text-2xl); font-weight: var(--weight-medium); color: var(--accent); line-height: var(--lh-none);\n white-space: nowrap; font-variant-numeric: tabular-nums;}\n.zl-modal-price .pm { font-size: var(--text-sm); font-weight: var(--weight-body); color: var(--text-muted); }\n.zl-energy {\n  display: inline-flex; align-items: center;\n  font-size: var(--text-xs); font-weight: var(--weight-bold);\n  padding: 3px 9px; letter-spacing: var(--track-wide);\n  background: var(--surface-raised); color: var(--text-sec); margin-top: 6px;\n}\n.zl-energy[data-c=\"A\"]{background:var(--green);color:#fff}\n.zl-energy[data-c=\"B\"]{background:var(--epc-b);color:#fff}\n.zl-energy[data-c=\"C\"]{background:var(--epc-c);color:#333}\n.zl-energy[data-c=\"D\"]{background:var(--epc-d);color:#fff}\n.zl-energy[data-c=\"E\"]{background:var(--epc-e);color:#fff}\n.zl-energy[data-c=\"F\"]{background:var(--epc-f);color:#fff}\n.zl-energy[data-c=\"G\"]{background:var(--epc-g);color:#fff}\n.zl-modal-specs {\n  display: flex; gap: 20px; flex-wrap: wrap;\n  padding: 16px 0;\n  border-top: 1px solid var(--surface-raised); border-bottom: 1px solid var(--surface-raised);\n  margin: 16px 0;\n}\n\/* PATCH 6: modal spec 14px *\/\n.zl-modal-spec {\n  display: flex; align-items: center; gap: 7px;\n  font-size: var(--text-sm); color: var(--surface-dark);\n}\n.zl-modal-spec svg { color: var(--accent); flex-shrink: 0; }\n.zl-modal-desc {\n  font-size: var(--text-sm); line-height: var(--lh-relaxed); color: var(--text-sec);\n  white-space: pre-line;\n}\n\n\/* \u2500\u2500 Read more \/ read less \u2500\u2500 *\/\n.zl-desc-wrap { position: relative; }\n.zl-desc-wrap.is-collapsed .zl-modal-desc { max-height: 230px; overflow: hidden; }\n.zl-desc-fade {\n  display: none; position: absolute; left: 0; right: 0; bottom: 40px; height: 64px;\n  background: linear-gradient(to bottom, rgba(255,255,255,0), #ffffff 85%);\n  pointer-events: none;\n}\n.zl-desc-wrap.is-collapsed .zl-desc-fade { display: block; }\n.zl-read-more {\n  display: inline-flex; align-items: center; gap: 6px; margin-top: 14px;\n  font-family: var(--font-body);\n  font-size: var(--text-sm); font-weight: var(--weight-semibold); letter-spacing: var(--track-wide);\n  color: var(--accent); background: none; border: none; cursor: pointer; padding: 0;\n  transition: color 150ms;\n}\n.zl-read-more:hover { color: var(--accent); }\n.zl-read-more svg { transition: transform 200ms; flex-shrink: 0; }\n.zl-desc-wrap.is-expanded .zl-read-more svg { transform: rotate(180deg); }\n\/* Column layout \u2014 holds a lead line plus a 3-column action row\n   (Ask \/ Book a viewing \/ See full cost overview), matching the action\n   bar on render.php's direct-listing pages. *\/\n.zl-modal-footer {\n  display: flex; flex-direction: column; align-items: stretch;\n  gap: 14px;\n  margin-top: 24px; padding-top: 20px;\n  border-top: 1px solid var(--surface-raised);\n}\n.zl-modal-footer-text {\n  font-size: var(--text-sm); color: var(--text-sec); line-height: var(--lh-body);\n}\n.zl-modal-footer-text span { display: block; font-size: var(--text-xs); color: var(--text-muted); margin-top: 2px; }\n\/* Auto-fit means the row still fills evenly with 2 buttons (non-LU\n   listings) and stacks to one column on narrow phones. *\/\n.zl-actions { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 12px; }\n.zl-actions .zl-cta-btn { justify-content: center; width: 100%; }\n.zl-cta-btn.zl-cta-btn--ghost { background: #ffffff; color: var(--surface-dark); border: 1.5px solid var(--surface-dark); }\n.zl-cta-btn.zl-cta-btn--ghost:hover { background: var(--surface-dark); color: #ffffff; }\n.zl-cta-btn {\n  display: inline-flex; align-items: center; gap: 8px;\n  background: var(--accent); color: #ffffff;\n  font-size: var(--text-sm); font-weight: var(--weight-semibold);\n  padding: 11px 24px; border: none; cursor: pointer;\n  text-decoration: none; letter-spacing: var(--track-wide);\n  transition: background 180ms, transform 120ms;\n}\n.zl-cta-btn:hover { background: var(--accent); transform: translateY(-1px); color: #fff; text-decoration: none; }\n\n\/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n   ACQUISITION COST MODAL (Luxembourg listings only)\n   Same pattern as the homepage's eXp modal and render.php's native\n   calculator on direct-listing pages: a table of the three flat charges\n   every buyer pays, then a short form for what varies by buyer (B\u00ebllegen\n   Akt, resale duty, commune surcharge). Computed entirely client-side \u2014\n   this page has no server render to hang it off of. Keep the rates in\n   sync across all three copies if Luxembourg changes them.\n\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 *\/\n.zl-acq-overlay {\n  display: none; position: fixed; inset: 0;\n  background: rgba(17,17,16,.82);\n  backdrop-filter: blur(4px); -webkit-backdrop-filter: blur(4px);\n  z-index: 999999; overflow-y: auto;\n  padding: 24px 20px; align-items: flex-start; justify-content: center;\n}\n.zl-acq-overlay.open { display: flex; }\n.zl-acq-modal {\n  background: #ffffff; width: 100%; max-width: 640px;\n  margin: auto; padding: 36px 36px 32px; position: relative;\n  animation: zlmodalin .22s ease;\n}\n.zl-acq-close {\n  position: absolute; top: 14px; right: 14px;\n  width: 40px; height: 40px; background: #ffffff;\n  border: none; border-radius: 50%; cursor: pointer;\n  display: flex; align-items: center; justify-content: center;\n  color: var(--surface-dark); transition: background 180ms, color 180ms;\n  box-shadow: 0 2px 8px rgba(17,17,16,.22);\n}\n.zl-acq-close:hover { background: var(--accent); color: #ffffff; }\n.zl-acq-h {\n  font-size: var(--text-md); font-weight: var(--weight-semibold); color: var(--surface-dark); margin: 2px 0 6px;\n}\n.zl-acq-lead {\n  font-size: var(--text-sm); color: var(--text-sec); line-height: var(--lh-body); margin-bottom: 16px;\n}\n.zl-acq-eyebrow {\n  font-size: var(--text-xs); letter-spacing: var(--track-eyebrow); text-transform: uppercase;\n  color: var(--accent); font-weight: var(--weight-semibold); display: block; margin-bottom: 6px;\n}\n.zl-acq-table { border: 1px solid var(--line); }\n.zl-acq-row {\n  display: flex; justify-content: space-between; align-items: center; gap: 16px;\n  padding: 12px 16px; border-bottom: 1px solid var(--line); font-size: var(--text-sm);\n}\n.zl-acq-row:last-child { border-bottom: none; }\n.zl-acq-row span { color: var(--text-dark-tertiary); font-weight: var(--weight-body); }\n.zl-acq-row span i { display: block; font-style: normal; font-size: var(--text-xs); color: var(--text-muted); margin-top: 2px; }\n.zl-acq-row b { font-weight: var(--weight-semibold); color: var(--surface-dark); white-space: nowrap; }\n.zl-acq-row--tot { background: var(--surface-dark); }\n.zl-acq-row--tot span { color: #fff; font-weight: var(--weight-medium); }\n.zl-acq-row--tot span i { color: var(--text-tertiary); }\n.zl-acq-row--tot b { color: var(--accent); font-size: var(--text-md); }\n.zl-acq-hr { border: none; border-top: 1px solid var(--line); margin: 26px 0 18px; }\n#zlAcqForm { display: flex; flex-direction: column; gap: 4px; }\n.zl-acq-check {\n  display: flex !important; flex-direction: row !important; align-items: center; gap: 8px !important;\n  font-size: var(--text-sm) !important; font-weight: var(--weight-medium) !important; text-transform: none !important;\n  color: var(--surface-dark) !important; letter-spacing: var(--track-normal) !important; margin-bottom: 16px;\n}\n.zl-acq-check input { width: 16px; height: 16px; accent-color: var(--accent); }\n.zl-acq-gate-note { font-size: var(--text-sm); color: var(--text-muted); margin: 0 0 10px; }\n.zl-acq-results { margin-top: 22px; }\n.zl-acq-fine { font-size: var(--text-xs); color: var(--text-muted); margin-top: 12px; line-height: var(--lh-body); }\n.zl-acq-fine a { color: var(--accent); font-weight: var(--weight-semibold); text-decoration: none; }\n.zl-acq-fine a:hover { text-decoration: underline; }\n@media (max-width: 620px) { .zl-acq-modal { padding: 24px 20px; } }\n\n\/* Acquisition-cost teaser inside the property modal itself *\/\n.zl-acq-teaser {\n  display: flex; align-items: center; justify-content: space-between; gap: 16px; flex-wrap: wrap;\n  background: var(--surface-page); border: 1px solid var(--line); padding: 18px 20px; margin: 16px 0;\n}\n.zl-acq-teaser-text { flex: 1 1 260px; min-width: 200px; }\n.zl-acq-teaser-text h4 { font-size: var(--text-base); font-weight: var(--weight-semibold); color: var(--surface-dark); margin: 2px 0 4px; }\n.zl-acq-teaser-text p { font-size: var(--text-sm); color: var(--text-sec); line-height: var(--lh-body); margin: 0; }\n.zl-acq-teaser .zl-cta-btn { flex-shrink: 0; }\n\n.zl-view-full-link {\n  display: inline-flex; align-items: center; gap: 6px;\n  font-size: var(--text-sm); font-weight: var(--weight-semibold); color: var(--accent);\n  text-decoration: none; margin-top: 8px;\n  border-bottom: 1.5px solid var(--accent); padding-bottom: 1px;\n  transition: color 180ms, border-color 180ms;\n}\n.zl-view-full-link:hover { color: var(--accent); border-color: var(--accent); text-decoration: none; }\n.zl-view-full-link svg { transition: transform 180ms; }\n.zl-view-full-link:hover svg { transform: translateX(2px); }\n\n.zl-modal-similar { margin-top: 32px; padding-top: 26px; border-top: 1px solid var(--surface-raised); }\n.zl-modal-similar-head { margin-bottom: 18px; }\n.zl-sim-eyebrow {\n  font-size: var(--text-xs); letter-spacing: var(--track-eyebrow); text-transform: uppercase;\n  color: var(--accent); font-weight: var(--weight-semibold); display: block; margin-bottom: 6px;\n}\n.zl-modal-similar-head h3 { font-size: var(--text-md); font-weight: var(--weight-semibold); margin: 0; color: var(--surface-dark); }\n.zl-modal-similar-track {\n  display: grid; grid-auto-flow: column; grid-auto-columns: minmax(220px,1fr);\n  gap: 16px; overflow-x: auto; scroll-snap-type: x mandatory;\n  padding-bottom: 6px; scrollbar-width: none;\n}\n.zl-modal-similar-track::-webkit-scrollbar { display: none; }\n@media(min-width:700px){ .zl-modal-similar-track{ grid-auto-columns: calc((100% - 32px)\/3); } }\n.zl-sim-card {\n  scroll-snap-align: start; border: 1px solid var(--line); background: #fff;\n  display: flex; flex-direction: column; text-decoration: none; color: inherit;\n  transition: transform 200ms, box-shadow 200ms;\n}\n.zl-sim-card:hover { transform: translateY(-3px); box-shadow: 0 12px 28px rgba(17,17,16,.10); }\n.zl-sim-img { aspect-ratio: 4\/3; overflow: hidden; background: var(--surface-raised); }\n.zl-sim-img img { width:100%; height:100%; object-fit:cover; display:block; transition: transform 350ms; }\n.zl-sim-card:hover .zl-sim-img img { transform: scale(1.04); }\n.zl-sim-body { padding: 14px 16px 16px; display: flex; flex-direction: column; flex: 1; }\n.zl-sim-city {\n  font-size: var(--text-xs); color: var(--text-dark-secondary); display: flex; align-items: center; gap: 4px;\n  margin-bottom: 6px; letter-spacing: var(--track-wide);\n}\n.zl-sim-city svg { flex-shrink: 0; color: var(--accent); }\n.zl-sim-title {\n  font-size: var(--text-sm); font-weight: var(--weight-semibold); color: var(--surface-dark); line-height: var(--lh-body);\n  margin-bottom: 10px;\n  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;\n  min-height: 2.5em;\n}\n.zl-sim-specs { display: flex; gap: 10px; margin-bottom: 12px; flex-wrap: wrap; }\n.zl-sim-spec { font-size: var(--text-xs); color: var(--text-sec); display: flex; align-items: center; gap: 4px; }\n.zl-sim-spec svg { color: var(--accent); flex-shrink: 0; }\n.zl-sim-spec strong { color: var(--surface-dark); font-weight: var(--weight-bold); }\n.zl-sim-foot {\n  margin-top: auto; padding-top: 12px; border-top: 1px solid var(--surface-raised);\n  display: flex; align-items: center; justify-content: space-between;\n}\n.zl-sim-price { font-size: var(--text-base); font-weight: var(--weight-semibold); color: var(--accent); }\n.zl-sim-cta { font-size: var(--text-xs); font-weight: var(--weight-semibold); color: var(--surface-dark); display: flex; align-items: center; gap: 3px; }\n.zl-sim-cta svg { transition: transform 200ms; }\n.zl-sim-card:hover .zl-sim-cta { color: var(--accent); }\n.zl-sim-card:hover .zl-sim-cta svg { transform: translateX(3px); }\n\n\/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n   ENQUIRY MODAL (pre-filled contact form)\n\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 *\/\n.zl-enq-overlay {\n  display: none; position: fixed; inset: 0;\n  background: rgba(17,17,16,.82);\n  backdrop-filter: blur(4px); -webkit-backdrop-filter: blur(4px);\n  z-index: 999999; overflow-y: auto;\n  padding: 24px 20px;\n  align-items: flex-start; justify-content: center;\n}\n.zl-enq-overlay.open { display: flex; }\n.zl-enq-modal {\n  background: #ffffff; width: 100%; max-width: 560px;\n  margin: auto; padding: 36px 36px 32px; position: relative;\n  animation: zlmodalin .22s ease;\n}\n.zl-enq-close {\n  position: absolute; top: 14px; right: 14px;\n  width: 40px; height: 40px;\n  background: #ffffff; border: none; border-radius: 50%;\n  cursor: pointer; display: flex; align-items: center; justify-content: center;\n  color: var(--surface-dark); transition: background 180ms, color 180ms;\n  box-shadow: 0 2px 8px rgba(17,17,16,.22);\n}\n.zl-enq-close:hover { background: var(--accent); color: #ffffff; }\n.zl-enq-prop-ref {\n  background: var(--surface-page); border-left: 3px solid var(--accent);\n  padding: 12px 16px; margin-bottom: 24px;\n  font-size: var(--text-sm); color: var(--text-sec); line-height: var(--lh-body);\n}\n.zl-enq-prop-ref strong { display: block; font-size: var(--text-sm); color: var(--surface-dark); margin-bottom: 2px; }\n.zl-enq-title {\n  font-size: var(--text-lg); font-weight: var(--weight-medium); color: var(--surface-dark);\n  letter-spacing: var(--track-snug); margin-bottom: 6px;\n}\n.zl-enq-sub {\n  font-size: var(--text-sm); color: var(--text-muted); margin-bottom: 24px; line-height: var(--lh-body);\n}\n.zl-enq-label {\n  display: block; font-size: var(--text-eyebrow); font-weight: var(--weight-semibold);\n  letter-spacing: var(--track-eyebrow); text-transform: uppercase;\n  color: var(--text-sec); margin-bottom: 6px;\n}\n.zl-enq-field {\n  width: 100%; font-family: var(--font-body);\n  font-size: var(--text-sm); color: var(--surface-dark);\n  padding: 10px 14px; margin-bottom: 16px;\n  border: 1.5px solid var(--line); background: #ffffff; outline: none;\n  transition: border-color 180ms;\n}\n.zl-enq-field:focus { border-color: var(--accent); }\n.zl-enq-textarea {\n  min-height: 120px; resize: vertical; line-height: var(--lh-body);\n}\n.zl-enq-row { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }\n.zl-enq-submit {\n  width: 100%; display: flex; align-items: center; justify-content: center; gap: 8px;\n  background: var(--accent); color: #ffffff;\n  font-family: var(--font-body);\n  font-size: var(--text-sm); font-weight: var(--weight-semibold);\n  padding: 13px 24px; border: none; cursor: pointer;\n  letter-spacing: var(--track-wide); margin-top: 8px;\n  transition: background 180ms;\n}\n.zl-enq-submit:hover { background: var(--accent); }\n.zl-enq-note {\n  font-size: var(--text-xs); color: var(--text-muted); text-align: center;\n  margin-top: 12px; line-height: var(--lh-body);\n}\n.zl-enq-success {\n  text-align: center; padding: 20px 0;\n}\n.zl-enq-success .zl-tick {\n  width: 48px; height: 48px; background: var(--accent);\n  border-radius: 50%; display: flex; align-items: center; justify-content: center;\n  margin: 0 auto 16px;\n}\n.zl-enq-success h3 { font-size: var(--text-md); font-weight: var(--weight-medium); color: var(--surface-dark); margin-bottom: 8px; }\n.zl-enq-success p  { font-size: var(--text-sm); color: var(--text-sec); line-height: var(--lh-body); }\n\n\/* \u2500\u2500 Responsive \u2500\u2500 *\/\n@media (max-width: 960px) {\n  .zl-inner { padding: 0 32px; }\n  .zl-grid  { grid-template-columns: repeat(2, 1fr); }\n}\n@media (max-width: 600px) {\n  .zl-inner { padding: 0 20px; }\n  .zl-grid  { grid-template-columns: 1fr; }\n  #zlapp    { padding: 40px 0 60px; }\n  .zl-modal-body { padding: 20px; }\n  .zl-enq-modal  { padding: 24px 20px; }\n  .zl-enq-row    { grid-template-columns: 1fr; }\n  .zl-card-foot  { flex-direction: column; align-items: flex-start; gap: 14px; }\n}\n\n\/* \u2500\u2500 Fullscreen lightbox \u2500\u2500 *\/\n.zl-lightbox {\n  display: none; position: fixed; inset: 0;\n  background: rgba(0,0,0,0.96); z-index: 9999999;\n  align-items: center; justify-content: center;\n}\n.zl-lightbox.open { display: flex; }\n.zl-lightbox-img { max-width: 95vw; max-height: 95vh; object-fit: contain; display: block; }\n.zl-lb-btn {\n  position: fixed; width: 44px; height: 44px;\n  background: rgba(255,255,255,0.15); border: none; border-radius: 50%;\n  cursor: pointer; color: #ffffff;\n  display: flex; align-items: center; justify-content: center;\n  transition: background 180ms; z-index: 10;\n}\n.zl-lb-btn:hover { background: var(--accent); }\n.zl-lb-close { top: 16px; right: 16px; }\n.zl-lb-prev  { left: 16px; top: 50%; transform: translateY(-50%); }\n.zl-lb-next  { right: 16px; top: 50%; transform: translateY(-50%); }\n.zl-lb-ctr {\n  position: fixed; bottom: 16px; left: 50%; transform: translateX(-50%);\n  background: rgba(255,255,255,0.15); color: #ffffff;\n  font-family: var(--font-body);\n  font-size: var(--text-xs); padding: 4px 14px; border-radius: 20px;\n}\n\n\n\/* \u2500\u2500 Pagination \u2500\u2500 *\/\n.zl-pagination {\n  display: flex; align-items: center; justify-content: center;\n  gap: 6px; margin-top: 68px; flex-wrap: wrap;\n}\n.zl-page-btn {\n  font-family: var(--font-body);\n  font-size: var(--text-sm); font-weight: var(--weight-medium);\n  min-width: 36px; height: 36px;\n  border: 1.5px solid var(--line);\n  background: #ffffff; color: var(--text-sec);\n  cursor: pointer;\n  display: inline-flex; align-items: center; justify-content: center;\n  transition: border-color 150ms, background 150ms, color 150ms;\n  padding: 0 10px; line-height: var(--lh-none);\n  white-space: nowrap;\n}\n.zl-page-btn:hover:not(.active):not(.disabled) {\n  border-color: var(--accent); color: var(--accent);\n}\n.zl-page-btn.active {\n  background: var(--accent); border-color: var(--accent); color: #ffffff; cursor: default;\n}\n.zl-page-btn.disabled {\n  opacity: 0.32; cursor: default; pointer-events: none;\n}\n.zl-page-ellipsis {\n  font-family: var(--font-body);\n  font-size: var(--text-sm); color: var(--text-muted);\n  display: inline-flex; align-items: center; padding: 0 4px; height: 36px;\n  user-select: none;\n}\n\n<\/style>\n\n\n<div id=\"zlapp\">\n<div class=\"zl-inner\">\n\n  <!-- Page heading -->\n  <div class=\"zl-page-hd\">\n    <p class=\"zl-eyebrow\">eXp Luxembourg Properties \u00b7 All Listings<\/p>\n    <h1 class=\"zl-page-title\">eXp Luxembourg Properties<\/h1>\n    <p class=\"zl-page-sub\">The listings we represent directly, and the wider eXp Luxembourg network across the country \u2014 one search, both included. If one of these fits what you're looking for, we represent <em>you<\/em> on it, at no cost to you as the buyer.<\/p>\n  <\/div>\n\n  <!-- Scope: All Listings \/ Our Listings -->\n  <div class=\"zl-tabs\" id=\"zlTabs\" role=\"tablist\">\n    <button class=\"zl-tab active\" id=\"zlTabAll\" data-scope=\"all\" role=\"tab\" aria-selected=\"true\" type=\"button\">All Listings <span class=\"zl-tab-count\" id=\"zlTabAllCount\"><\/span><\/button>\n    <button class=\"zl-tab zl-tab-mine\" id=\"zlTabMine\" data-scope=\"mine\" role=\"tab\" aria-selected=\"false\" type=\"button\">Our Listings <span class=\"zl-tab-count\" id=\"zlTabMineCount\"><\/span><\/button>\n  <\/div>\n\n  <!-- Filters -->\n  <div id=\"zlFilterBar\"><\/div>\n\n  <!-- Count -->\n  <div id=\"zlCount\" class=\"zl-count\" style=\"display:none\"><\/div>\n\n  <h2 class=\"zl-section-title\">All eXp Luxembourg Properties<\/h2>\n\n  <!-- State \/ Grid -->\n  <div id=\"zlState\">\n    <div class=\"zl-loading\"><div class=\"zl-spinner\"><\/div> Loading listings\u2026<\/div>\n  <\/div>\n  <div id=\"zlGrid\" class=\"zl-grid\" style=\"display:none\"><\/div>\n  <div id=\"zlPagination\" class=\"zl-pagination\"><\/div>\n\n<\/div>\n<\/div>\n<!-- Fullscreen lightbox -->\n<div class=\"zl-lightbox\" id=\"zlLightbox\">\n  <button class=\"zl-lb-btn zl-lb-close\" id=\"zlLbClose\"><svg width=\"14\" height=\"14\" viewBox=\"0 0 16 16\" fill=\"none\"><path d=\"M2 2l12 12M14 2L2 14\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\"\/><\/svg><\/button>\n  <button class=\"zl-lb-btn zl-lb-prev\" id=\"zlLbPrev\"><svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\"><path d=\"M15 18l-6-6 6-6\"\/><\/svg><\/button>\n  <img decoding=\"async\" class=\"zl-lightbox-img\" id=\"zlLbImg\" src=\"\" alt=\"\">\n  <button class=\"zl-lb-btn zl-lb-next\" id=\"zlLbNext\"><svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\"><path d=\"M9 18l6-6-6-6\"\/><\/svg><\/button>\n  <span class=\"zl-lb-ctr\" id=\"zlLbCtr\"><\/span>\n<\/div>\n\n\n<!-- Property detail modal -->\n<div class=\"zl-overlay\" id=\"zlOverlay\">\n  <div class=\"zl-modal\" id=\"zlModal\">\n    <button class=\"zl-modal-close\" id=\"zlModalClose\">\n      <svg width=\"18\" height=\"18\" viewBox=\"0 0 16 16\" fill=\"none\">\n        <path d=\"M2 2l12 12M14 2L2 14\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\"\/>\n      <\/svg>\n    <\/button>\n    <div id=\"zlModalBody\"><\/div>\n  <\/div>\n<\/div>\n\n<!-- Enquiry modal -->\n<div class=\"zl-enq-overlay\" id=\"zlEnqOverlay\">\n  <div class=\"zl-enq-modal\" id=\"zlEnqModal\">\n    <button class=\"zl-enq-close\" id=\"zlEnqClose\">\n      <svg width=\"18\" height=\"18\" viewBox=\"0 0 16 16\" fill=\"none\">\n        <path d=\"M2 2l12 12M14 2L2 14\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\"\/>\n      <\/svg>\n    <\/button>\n    <div id=\"zlEnqBody\"><\/div>\n  <\/div>\n<\/div>\n\n<!-- Acquisition cost modal \u2014 Luxembourg listings only. Native, not an\n     iframe: same fixed-cost-table + short-form pattern as render.php's\n     calculator on direct-listing pages and the homepage's eXp modal,\n     computed client-side since this page has no server render either. -->\n<div class=\"zl-acq-overlay\" id=\"zlAcqOverlay\">\n  <div class=\"zl-acq-modal\">\n    <button class=\"zl-acq-close\" id=\"zlAcqClose\" aria-label=\"Close\">\n      <svg width=\"18\" height=\"18\" viewBox=\"0 0 16 16\" fill=\"none\">\n        <path d=\"M2 2l12 12M14 2L2 14\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\"\/>\n      <\/svg>\n    <\/button>\n    <div id=\"zlAcqBody\"><\/div>\n  <\/div>\n<\/div>\n\n\n<script>\n(function () {\n  const PROXY     = '\/zeas-proxy.php';   \/\/ still used for the enquiry POST only\n  const API       = '\/wp-json\/zeas\/v1\/listings';\n  const PAGE_SIZE = 18;\n\n  \/* \u2500\u2500 State \u2500\u2500 *\/\n  let all      = [];   \/\/ combined pool \u2014 your own LU listings + network\n  let zxListings = []; \/\/ your own listings, kept separately for featured-sort\/tab counts\n  let luListings = [];\n  let filtered = [];\n  let activeScope   = 'all'; \/\/ 'all' | 'mine' \u2014 the \"All Listings\"\/\"Our Listings\" tabs\n  let activeType    = 'all';\n  let activeStatus  = 'all';\n  let activeCommune = 'all';\n  let PRICE_LO = 0, PRICE_HI = 0, priceMin = 0, priceMax = 0;\n  let galImages = [];\n  let galIdx    = 0;\n  let currentListing = null;\n  let currentPage    = 1;\n\n  \/* \u2500\u2500 Exclude our own (Adrian Buzea) international listings \u2500\u2500 *\/\n  var MY_AGENT_TOKENS  = ['adrian', 'buzea'];\n  var AGENT_STR_FIELDS = ['agent_first_name','agent_last_name','agent_email','agent_name','listing_agent'];\n  function agentName(l) {\n    var s = '';\n    AGENT_STR_FIELDS.forEach(function(f){ if (typeof l[f] === 'string') s += ' ' + l[f]; });\n    return s.toLowerCase();\n  }\n  function isMine(l) {\n    var n = agentName(l);\n    return MY_AGENT_TOKENS.every(function(tok){ return n.indexOf(tok) > -1; });\n  }\n\n  \/\/ The feed's top-level country_code is unreliable (reads \"LU\" even for\n  \/\/ properties abroad) \u2014 the real country lives in full_payload.country.\n  \/\/ This page is Luxembourg-only, so \"your own\" here always means isZx,\n  \/\/ never the international (Spain\/Morocco) listings.\n  function realCountry(l) {\n    var fp = l.full_payload || {};\n    return (fp.country || l.country_code || '').toString().toUpperCase();\n  }\n  function isAbroad(l) { var c = realCountry(l); return !!c && c !== 'LU'; }\n  function isZx(l) { return isMine(l) && !isAbroad(l); }\n  \/\/ Gate for the acquisition-cost calculator \u2014 Luxembourg tax rules only\n  \/\/ apply to Luxembourg properties, and the table needs a real price.\n  function isLU(l) { return realCountry(l) === 'LU' && +l.price > 0; }\n\n  \/* \u2500\u2500 English by default: pull the EN title\/description from full_payload.comments\n        (same behaviour as the website); falls back to the original if no EN exists. \u2500\u2500 *\/\n  function localizeEN(l) {\n    var fp = l.full_payload || {};\n    var cs = fp.comments || [];\n    var en = cs.filter(function(c){ return c && c.language === 'en'; })[0];\n    if (en) {\n      if (en.title)   l.title = en.title;\n      if (en.comment) l.description = en.comment;\n    }\n    return l;\n  }\n\n  \/* \u2500\u2500 Remove other agents' contact details (names + phone numbers + emails)\n        so they are never published on the cards or modal. \u2500\u2500 *\/\n  function scrubContact(s) {\n    return String(s || '')\n      .replace(\/[\\u{1F300}-\\u{1FAFF}\\u{2600}-\\u{27BF}\\u{2190}-\\u{21FF}]\/gu, ' ')           \/\/ emoji\n      .replace(\/[\\w.\\-]+@[\\w\\-]+\\.[\\w.\\-]+\/g, ' ')                                          \/\/ emails\n      .replace(\/[A-Z\u00c0-\u00de][A-Z\u00c0-\u00de'\u2019 \\-]{1,}\\s*\\(\\s*\\+?[0-9][0-9 .\\-()]{5,}\\)\/g, ' ')          \/\/ \"ELHORRY ( +352 691 807 888 )\"\n      .replace(\/\\(\\s*\\+?[0-9][0-9 .\\-()]{5,}\\)\/g, ' ')                                       \/\/ any parenthesised phone\n      .replace(\/\\+?\\(?\\s*352\\s*\\)?[\\s.\\-]?[0-9][0-9 .\\-]{5,}[0-9]\/g, ' ')                    \/\/ Luxembourg numbers (352 \u2026)\n      .replace(\/\\+[0-9][0-9 .\\-]{6,}[0-9]\/g, ' ')                                            \/\/ international +\u2026\n      .replace(\/\\b0[0-9][0-9 .\\-]{6,}[0-9]\\b\/g, ' ')                                         \/\/ national 0\u2026\n      .replace(\/[ \\t]{2,}\/g, ' ')\n      .replace(\/ *\\n *\/g, '\\n')\n      .replace(\/\\n{3,}\/g, '\\n\\n')\n      .trim();\n  }\n\n  \/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n     ADAPTER \u2014 reshapes the new \/wp-json\/zeas\/v1 output into the\n     legacy \"l\" shape this page was built against. Identical to\n     the ones on the homepage and international page; keep in sync.\n\n     scope=network means Adrian's own listings and every duplicate\n     are already excluded server-side \u2014 the isMine() filter below\n     stays in place purely as a second line of defence.\n  \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 *\/\n  function toLegacy(item) {\n    var content  = item.content || {};\n    var en       = content.en || {};\n    var comments = Object.keys(content).map(function (lang) {\n      var c = content[lang] || {};\n      return { language: lang, title: c.headline, subtitle: c.hook, comment: c.summary, comment_full: c.narrative };\n    });\n    return {\n      id: item.reference,\n      source_id: item.reference,\n      title: en.headline || '',\n      description: en.summary || '',\n      narrative: en.narrative || '',\n      url: item.url || null,\n      full_payload: { country: item.country, comments: comments },\n      country_code: item.country,\n      price: item.price,\n      listing_type: String(item.listing_type || 1),\n      bedrooms: item.bedrooms,\n      bathrooms: item.bathrooms,\n      square_feet: item.area_living,\n      total_rooms: item.rooms,\n      construction_year: item.year_built,\n      property_type: item.property_type,\n      city: item.city,\n      address: item.address,\n      energy_efficiency_class: (item.energy && item.energy.class) || null,\n      images: (item.photos || []).map(function (p) { return { url: p.url, is_front_cover: false }; }),\n      status: item.status || 'live',\n      agent_first_name: item.is_mine ? 'Adrian' : '',\n      agent_last_name:  item.is_mine ? 'Buzea'  : '',\n      agent_email:      item.is_mine ? 'adrian.buzea@expluxembourg.com' : ''\n    };\n  }\n\n  async function fetchAll() {\n    \/\/ scope=all \u2014 this used to be scope=network, which excludes your own\n    \/\/ (is_mine) listings server-side entirely. Now that this page merges\n    \/\/ your Luxembourg listings into the same grid, it needs both.\n    var out = [], page = 1, LIMIT = 100, SAFETY = 6;\n    while (page <= SAFETY) {\n      var res = await fetch(API + '?scope=all&per_page=' + LIMIT + '&page=' + page);\n      if (!res.ok) throw new Error('HTTP ' + res.status);\n      var data = await res.json();\n      var batch = (data.items || []).map(toLegacy);\n      out = out.concat(batch);\n      if (!data.pages || page >= data.pages) break;\n      page++;\n    }\n    return out.filter(function (l) { return l.status !== 'sold'; });\n  }\n\n  function fmtPrice(v) {\n    if (v >= 1000000) { var m = v \/ 1000000; return '\u20ac' + (m % 1 ? m.toFixed(1) : m) + 'M'; }\n    return '\u20ac' + Math.round(v \/ 1000) + 'k';\n  }\n  function matchFilters(l) {\n    var p = +l.price || 0;\n    var commune = (l.city || l.address || '').trim();\n    return (activeScope   === 'all' || isZx(l)) &&\n           (activeType    === 'all' || l.property_type === activeType) &&\n           (activeStatus  === 'all' || l.listing_type  === activeStatus) &&\n           (activeCommune === 'all' || commune === activeCommune) &&\n           (!p || (p >= priceMin && p <= priceMax));\n  }\n\n  \/* \u2500\u2500 DOM \u2500\u2500 *\/\n  const $state    = document.getElementById('zlState');\n  const $grid     = document.getElementById('zlGrid');\n  const $count    = document.getElementById('zlCount');\n  const $filterBar= document.getElementById('zlFilterBar');\n  const $overlay  = document.getElementById('zlOverlay');\n  const $modalBody= document.getElementById('zlModalBody');\n  const $enqOvl   = document.getElementById('zlEnqOverlay');\n  const $enqBody      = document.getElementById('zlEnqBody');\n  const $acqOvl   = document.getElementById('zlAcqOverlay');\n  const $acqBody  = document.getElementById('zlAcqBody');\n  let   knownContact = null; \/\/ { firstName, email } \u2014 captured from a successful\n                              \/\/ enquiry send, reused so the acquisition-cost\n                              \/\/ gate below doesn't ask twice in the same visit.\n  const $pagination   = document.getElementById('zlPagination');\n  const $tabs         = document.getElementById('zlTabs');\n  const $tabAll       = document.getElementById('zlTabAll');\n  const $tabMine      = document.getElementById('zlTabMine');\n  const $tabAllCount  = document.getElementById('zlTabAllCount');\n  const $tabMineCount = document.getElementById('zlTabMineCount');\n\n  \/* \u2500\u2500 Fetch \u2500\u2500 *\/\n  async function init() {\n    try {\n      var feed = await fetchAll();\n      \/\/ Your own Luxembourg listings, kept out of the \"network\" bucket but\n      \/\/ merged into the one combined pool everything below actually filters \u2014\n      \/\/ see matchFilters()'s activeScope check and applyFilters()'s sort.\n      zxListings = feed.filter(isZx).map(localizeEN);\n      luListings = feed.filter(function(l){ return !isMine(l); }).map(localizeEN);\n      all = zxListings.concat(luListings);\n      setupScopeTabs();\n      buildFilterBar();\n      applyFilters();\n    } catch(e) {\n      $state.innerHTML = '<div class=\"zl-error\">Could not load listings. (' + e.message + ')<\/div>';\n    }\n  }\n\n  \/* \u2500\u2500 Scope tabs \u2014 \"All Listings\" vs \"Our Listings\". A filter dimension,\n        not a separate dataset switch (matches the homepage). \u2500\u2500 *\/\n  function setupScopeTabs() {\n    if ($tabAllCount)  $tabAllCount.textContent  = all.length;\n    if ($tabMineCount) $tabMineCount.textContent = zxListings.length;\n\n    if (!zxListings.length) {\n      if ($tabs) $tabs.style.display = 'none';\n      return;\n    }\n    if ($tabAll)  $tabAll.addEventListener('click',  function(){ switchScope('all'); });\n    if ($tabMine) $tabMine.addEventListener('click', function(){ switchScope('mine'); });\n  }\n\n  function switchScope(scope) {\n    activeScope = scope;\n    var mineActive = scope === 'mine';\n    if ($tabAll)  { $tabAll.classList.toggle('active', !mineActive);  $tabAll.setAttribute('aria-selected', !mineActive); }\n    if ($tabMine) { $tabMine.classList.toggle('active', mineActive); $tabMine.setAttribute('aria-selected', mineActive); }\n    applyFilters();\n  }\n\n  \/* \u2500\u2500 Filters \u2500\u2500 *\/\n  function buildFilterBar() {\n    var types = ['all'].concat(\n      all.map(function(l){ return l.property_type; }).filter(function(t,i,a){ return t && a.indexOf(t) === i; })\n    );\n    var communes = all.map(function(l){ return (l.city || l.address || '').trim(); })\n      .filter(function(c,i,a){ return c && a.indexOf(c) === i; })\n      .sort(function(a,b){ return a.localeCompare(b); });\n\n    var prices = all.map(function(l){ return +l.price; }).filter(function(p){ return p > 0; });\n    var hasPrice = prices.length > 0;\n    var RAW_LO = hasPrice ? Math.min.apply(null, prices) : 0;\n    var RAW_HI = hasPrice ? Math.max.apply(null, prices) : 0;\n    \/\/ Round the dropdown scale to clean, portal-familiar steps rather than\n    \/\/ raw \u20ac10k increments \u2014 a 200k\u20132M spread doesn't need 180 options.\n    var DSTEP = (RAW_HI - RAW_LO) <= 500000 ? 50000 : (RAW_HI - RAW_LO) <= 1500000 ? 100000 : 250000;\n    PRICE_LO = Math.floor(RAW_LO \/ DSTEP) * DSTEP;\n    PRICE_HI = Math.ceil(RAW_HI \/ DSTEP) * DSTEP;\n    priceMin = PRICE_LO; priceMax = PRICE_HI;\n\n    var priceSteps = [];\n    for (var pv = PRICE_LO; pv <= PRICE_HI; pv += DSTEP) priceSteps.push(pv);\n    var priceOptionsHTML = priceSteps.map(function(v){ return '<option value=\"' + v + '\">' + fmtPrice(v) + '<\/option>'; }).join('');\n\n    var priceBlock = hasPrice\n      ? '<div class=\"zl-price-fields\">' +\n          '<div class=\"zl-select-wrap zl-price-select\">' +\n            '<select class=\"zl-select\" id=\"zlPriceMin\"><option value=\"\">No min<\/option>' + priceOptionsHTML + '<\/select>' +\n          '<\/div>' +\n          '<span class=\"zl-price-sep\">\u2013<\/span>' +\n          '<div class=\"zl-select-wrap zl-price-select\">' +\n            '<select class=\"zl-select\" id=\"zlPriceMax\"><option value=\"\">No max<\/option>' + priceOptionsHTML + '<\/select>' +\n          '<\/div>' +\n        '<\/div>'\n      : '';\n\n    $filterBar.innerHTML =\n      '<div class=\"zl-filter-row\">' +\n        '<span class=\"zl-filter-label\">Transaction<\/span>' +\n        '<div class=\"zl-filters\" id=\"zlStatusFilters\">' +\n          '<button class=\"zl-filter-btn active\" data-s=\"all\">All<\/button>' +\n          '<button class=\"zl-filter-btn\" data-s=\"1\">For sale<\/button>' +\n          '<button class=\"zl-filter-btn\" data-s=\"2\">For rent<\/button>' +\n        '<\/div>' +\n      '<\/div>' +\n      '<div class=\"zl-filter-row\">' +\n        '<span class=\"zl-filter-label\">Type<\/span>' +\n        '<div class=\"zl-filters\" id=\"zlTypeFilters\">' +\n          types.map(function(t){\n            return '<button class=\"zl-filter-btn' + (t==='all'?' active':'') + '\" data-type=\"' + t + '\">' + (t==='all'?'All':t) + '<\/button>';\n          }).join('') +\n        '<\/div>' +\n      '<\/div>' +\n      '<div class=\"zl-refine\">' +\n        '<div class=\"zl-f-field\">' +\n          '<span class=\"zl-f-label\">Commune<\/span>' +\n          '<div class=\"zl-select-wrap\">' +\n            '<svg class=\"zl-select-pin\" width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M12 2C8.13 2 5 5.13 5 9c0 5.25 7 13 7 13s7-7.75 7-13c0-3.87-3.13-7-7-7zm0 9.5a2.5 2.5 0 110-5 2.5 2.5 0 010 5z\"\/><\/svg>' +\n            '<select class=\"zl-select\" id=\"zlCommune\">' +\n              '<option value=\"all\">All communes<\/option>' +\n              communes.map(function(c){ return '<option value=\"' + esc(c) + '\">' + esc(c) + '<\/option>'; }).join('') +\n            '<\/select>' +\n          '<\/div>' +\n        '<\/div>' +\n        (hasPrice\n          ? '<div class=\"zl-f-divider\"><\/div>' +\n            '<div class=\"zl-f-field zl-f-price\">' +\n              '<span class=\"zl-f-label\">Price range<\/span>' +\n              priceBlock +\n            '<\/div>'\n          : '') +\n      '<\/div>';\n\n    document.getElementById('zlStatusFilters').querySelectorAll('.zl-filter-btn').forEach(function(btn){\n      btn.addEventListener('click', function(){\n        document.getElementById('zlStatusFilters').querySelectorAll('.zl-filter-btn').forEach(function(b){ b.classList.remove('active'); });\n        btn.classList.add('active');\n        activeStatus = btn.dataset.s;\n        applyFilters();\n      });\n    });\n    document.getElementById('zlTypeFilters').querySelectorAll('.zl-filter-btn').forEach(function(btn){\n      btn.addEventListener('click', function(){\n        document.getElementById('zlTypeFilters').querySelectorAll('.zl-filter-btn').forEach(function(b){ b.classList.remove('active'); });\n        btn.classList.add('active');\n        activeType = btn.dataset.type;\n        applyFilters();\n      });\n    });\n    document.getElementById('zlCommune').addEventListener('change', function(){\n      activeCommune = this.value;\n      applyFilters();\n    });\n\n    if (hasPrice) {\n      var minEl = document.getElementById('zlPriceMin');\n      var maxEl = document.getElementById('zlPriceMax');\n      function priceChanged(which) {\n        var lo = minEl.value === '' ? PRICE_LO : parseInt(minEl.value, 10);\n        var hi = maxEl.value === '' ? PRICE_HI : parseInt(maxEl.value, 10);\n        if (lo > hi) {\n          if (which === 'min') { hi = lo; maxEl.value = (hi === PRICE_HI) ? '' : String(hi); }\n          else { lo = hi; minEl.value = (lo === PRICE_LO) ? '' : String(lo); }\n        }\n        priceMin = lo; priceMax = hi;\n        applyFilters();\n      }\n      minEl.addEventListener('change', function(){ priceChanged('min'); });\n      maxEl.addEventListener('change', function(){ priceChanged('max'); });\n    }\n  }\n\n  function applyFilters() {\n    \/\/ Featured-first: any of your own listings matching the active search\n    \/\/ sort ahead of network results, the same as the homepage.\n    filtered = all.filter(matchFilters);\n    filtered.sort(function(a, b){ return (isZx(b) ? 1 : 0) - (isZx(a) ? 1 : 0); });\n    currentPage = 1;\n    renderGrid();\n  }\n\n  \/* \u2500\u2500 Grid \u2500\u2500 *\/\n  function renderGrid() {\n    $state.innerHTML = '';\n    $pagination.innerHTML = '';\n\n    if (!filtered.length) {\n      $grid.style.display = 'none';\n      $state.innerHTML = '<div class=\"zl-empty\">No listings match your filters.<\/div>';\n      $count.style.display = 'none';\n      return;\n    }\n\n    var total      = filtered.length;\n    var totalPages = Math.ceil(total \/ PAGE_SIZE);\n    currentPage    = Math.min(currentPage, totalPages);\n    var start      = (currentPage - 1) * PAGE_SIZE;\n    var end        = Math.min(start + PAGE_SIZE, total);\n    var pageItems  = filtered.slice(start, end);\n\n    $count.innerHTML =\n      'Showing <strong>' + (start + 1) + '\u2013' + end + '<\/strong> of <strong>' + total + '<\/strong> listings';\n    $count.style.display = 'block';\n\n    $grid.innerHTML = pageItems.map(cardHTML).join('');\n    $grid.style.display = 'grid';\n\n    $grid.querySelectorAll('.zl-card').forEach(function(card, i) {\n      card.addEventListener('click', function() { openPropertyModal(pageItems[i]); });\n    });\n    $grid.querySelectorAll('.zl-card-cta').forEach(function(btn, i) {\n      btn.addEventListener('click', function(e) {\n        e.stopPropagation();\n        openEnquiry(pageItems[i]);\n      });\n    });\n\n    renderPagination(totalPages);\n  }\n\n  \/* \u2500\u2500 Pagination \u2500\u2500 *\/\n  function renderPagination(totalPages) {\n    if (totalPages <= 1) { $pagination.innerHTML = ''; return; }\n\n    \/\/ Build array of page numbers to show\n    var pages = [];\n    for (var p = 1; p <= totalPages; p++) {\n      if (\n        p === 1 || p === totalPages ||\n        (p >= currentPage - 1 && p <= currentPage + 1)\n      ) {\n        pages.push(p);\n      }\n    }\n\n    \/\/ Insert ellipsis where gaps exist\n    var items = [];\n    for (var j = 0; j < pages.length; j++) {\n      if (j > 0 && pages[j] - pages[j-1] > 1) {\n        items.push('\u2026');\n      }\n      items.push(pages[j]);\n    }\n\n    var html = '';\n\n    \/\/ Prev button\n    html += '<button class=\"zl-page-btn' + (currentPage === 1 ? ' disabled' : '') + '\" id=\"zlPgPrev\">' +\n      '<svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\"><path d=\"M15 18l-6-6 6-6\"\/><\/svg>' +\n      '&nbsp;Prev<\/button>';\n\n    \/\/ Page number \/ ellipsis buttons\n    items.forEach(function(item) {\n      if (item === '\u2026') {\n        html += '<span class=\"zl-page-ellipsis\">\u2026<\/span>';\n      } else {\n        html += '<button class=\"zl-page-btn' + (item === currentPage ? ' active' : '') + '\" data-page=\"' + item + '\">' + item + '<\/button>';\n      }\n    });\n\n    \/\/ Next button\n    html += '<button class=\"zl-page-btn' + (currentPage === totalPages ? ' disabled' : '') + '\" id=\"zlPgNext\">' +\n      'Next&nbsp;<svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\"><path d=\"M9 18l6-6-6-6\"\/><\/svg><\/button>';\n\n    $pagination.innerHTML = html;\n\n    \/\/ Wire up page buttons\n    $pagination.querySelectorAll('.zl-page-btn[data-page]').forEach(function(btn) {\n      btn.addEventListener('click', function() {\n        goToPage(parseInt(btn.dataset.page));\n      });\n    });\n    var prevBtn = document.getElementById('zlPgPrev');\n    var nextBtn = document.getElementById('zlPgNext');\n    if (prevBtn && !prevBtn.classList.contains('disabled')) {\n      prevBtn.addEventListener('click', function() { goToPage(currentPage - 1); });\n    }\n    if (nextBtn && !nextBtn.classList.contains('disabled')) {\n      nextBtn.addEventListener('click', function() { goToPage(currentPage + 1); });\n    }\n  }\n\n  function goToPage(p) {\n    currentPage = p;\n    renderGrid();\n    \/\/ Scroll to top of listings section\n    var section = document.getElementById('zlapp');\n    if (section) {\n      var top = section.getBoundingClientRect().top + window.pageYOffset - 80;\n      window.scrollTo({ top: top, behavior: 'smooth' });\n    }\n  }\n\n  \/* \u2500\u2500 Card \u2500\u2500 *\/\n  function cardHTML(l) {\n    var cover  = (l.images && l.images.find(function(i){ return i.is_front_cover; })) || (l.images && l.images[0]);\n    var img    = cover ? (cover.thumbnail_url || cover.url) : '';\n    var rental = l.listing_type === '2';\n    var price  = l.price ? l.price.toLocaleString('de-DE') + ' \u20ac' : 'Price on request';\n    var mine   = isZx(l);\n\n    var desc = scrubContact(l.description).replace(\/\\r?\\n+\/g, ' ').replace(\/\\s{2,}\/g, ' ').trim();\n    desc = desc ? (desc.substring(0, 165) + (desc.length > 165 ? '\u2026' : '')) : '';\n\n    var specs = [];\n    if (l.bedrooms)    specs.push(l.bedrooms + ' bed');\n    if (l.bathrooms)   specs.push(l.bathrooms + ' bath');\n    if (l.square_feet) specs.push(Math.round(l.square_feet) + ' m\u00b2');\n\n    return '<div class=\"zl-card' + (mine ? ' is-featured' : '') + '\">' +\n      '<div class=\"zl-card-img\">' +\n        (img ? '<img decoding=\"async\" src=\"' + esc(img) + '\" alt=\"' + esc(l.title) + '\" loading=\"lazy\">' : '') +\n        '<span class=\"zl-card-badge' + (rental ? ' rental' : '') + '\">' + (rental ? 'For rent' : 'For sale') + '<\/span>' +\n        (l.property_type ? '<span class=\"zl-card-type\">' + esc(l.property_type) + '<\/span>' : '') +\n        (mine ? '<span class=\"zl-card-mine-badge\">Direct Listing<\/span>' : '<span class=\"zl-card-network-badge\">eXp Network<\/span>') +\n      '<\/div>' +\n      '<div class=\"zl-card-body\">' +\n        '<div class=\"zl-card-city\">' + si('pin','12') + esc(l.city || l.address || '') + '<\/div>' +\n        '<div class=\"zl-card-title\">' + esc(l.title) + '<\/div>' +\n        (desc ? '<div class=\"zl-card-desc\">' + esc(desc) + '<\/div>' : '') +\n        '<div class=\"zl-card-foot\">' +\n          '<div class=\"zl-card-price\">' + price + (rental ? ' <small>\/ month<\/small>' : '') +\n            (specs.length ? '<small>' + specs.join(' \u00b7 ') + '<\/small>' : '') + '<\/div>' +\n          '<span class=\"zl-card-enq zl-card-cta\">Request information \u2192<\/span>' +\n        '<\/div>' +\n      '<\/div>' +\n    '<\/div>';\n  }\n\n  \/* \u2500\u2500 Property modal \u2500\u2500 *\/\n  \/* \u2500\u2500 Similar properties \u2014 same property_type only, so a garage or\n        parking lot doesn't surface a house \u2500\u2500 *\/\n  function similarCardsHTML(current) {\n    var pool = all.filter(function(x){ return x !== current && x.property_type === current.property_type; }).slice(0, 3);\n    if (!pool.length) return '';\n\n    var cards = pool.map(function(x){\n      var img = (x.images && x.images[0]) ? (x.images[0].url || x.images[0].original_url) : '';\n      var xPrice = x.price ? x.price.toLocaleString('de-DE') + ' \u20ac' : 'Price on request';\n      var xSpecs = '';\n      if (x.bedrooms)    xSpecs += '<span class=\"zl-sim-spec\">' + si('bed','15') + '<strong>' + x.bedrooms + '<\/strong> bed<\/span>';\n      if (x.square_feet) xSpecs += '<span class=\"zl-sim-spec\">' + si('sqm','15') + '<strong>' + Math.round(x.square_feet) + '<\/strong> m\u00b2<\/span>';\n      return '<a class=\"zl-sim-card\" href=\"#\" data-idx=\"' + all.indexOf(x) + '\">' +\n        '<div class=\"zl-sim-img\">' + (img ? '<img decoding=\"async\" src=\"' + esc(img) + '\" alt=\"\" loading=\"lazy\">' : '') + '<\/div>' +\n        '<div class=\"zl-sim-body\">' +\n          '<div class=\"zl-sim-city\">' + si('pin','12') + esc(x.city || x.address || '') + '<\/div>' +\n          '<div class=\"zl-sim-title\">' + esc(x.title) + '<\/div>' +\n          (xSpecs ? '<div class=\"zl-sim-specs\">' + xSpecs + '<\/div>' : '') +\n          '<div class=\"zl-sim-foot\"><span class=\"zl-sim-price\">' + xPrice + '<\/span><span class=\"zl-sim-cta\">View ' + si('chevR','13') + '<\/span><\/div>' +\n        '<\/div>' +\n      '<\/a>';\n    }).join('');\n\n    return '<div class=\"zl-modal-similar\">' +\n      '<div class=\"zl-modal-similar-head\"><span class=\"zl-sim-eyebrow\">You may also like<\/span><h3>Similar properties<\/h3><\/div>' +\n      '<div class=\"zl-modal-similar-track\">' + cards + '<\/div>' +\n    '<\/div>';\n  }\n\n  function openPropertyModal(l) {\n    currentListing = l;\n    galImages = (l.images || []).map(function(i){ return i.url || i.original_url; }).filter(Boolean);\n    galIdx = 0;\n\n    var rental = l.listing_type === '2';\n    var price  = l.price ? l.price.toLocaleString('de-DE') + ' \u20ac' : 'Price on request';\n    var ec     = l.energy_efficiency_class;\n\n    var specs = '';\n    if (l.bedrooms)        specs += '<div class=\"zl-modal-spec\">' + si('bed','16')  + '<strong>' + l.bedrooms + '<\/strong> bed' + (l.bedrooms > 1 ? 's' : '') + '<\/div>';\n    if (l.bathrooms)       specs += '<div class=\"zl-modal-spec\">' + si('bath','16') + '<strong>' + l.bathrooms + '<\/strong> bath' + (l.bathrooms > 1 ? 's' : '') + '<\/div>';\n    if (l.square_feet)     specs += '<div class=\"zl-modal-spec\">' + si('sqm','16')  + '<strong>' + Math.round(l.square_feet) + '<\/strong> m\u00b2<\/div>';\n    if (l.plot_size_sqm)   specs += '<div class=\"zl-modal-spec\">' + si('sqm','16')  + '<strong>' + Math.round(l.plot_size_sqm) + '<\/strong> m\u00b2 plot<\/div>';\n    if (l.total_rooms)     specs += '<div class=\"zl-modal-spec\">' + si('rooms','16')+ '<strong>' + l.total_rooms + '<\/strong> rooms<\/div>';\n    if (l.construction_year) specs += '<div class=\"zl-modal-spec\">' + si('cal','16') + '<strong>' + l.construction_year + '<\/strong><\/div>';\n\n    var rawDesc = scrubContact(l.description);\n    var narrative = scrubContact(l.narrative || '');\n    var desc = [rawDesc, narrative].filter(Boolean).join('\\n\\n');\n\n    \/\/ Same \"Book a viewing\" destination as render.php's direct-listing\n    \/\/ pages \u2014 a generic contact page, pre-filled with whatever identifies\n    \/\/ this listing (network listings have no WP permalink, so l.url falls\n    \/\/ back to the page the visitor is already on).\n    var bookingUrl = '\/en\/contact-us\/buying-opportunity?' +\n      'property=' + encodeURIComponent(l.title || '') +\n      '&ref=' + encodeURIComponent(l.source_id || l.id || '') +\n      '&url=' + encodeURIComponent(l.url || location.href);\n\n    $modalBody.innerHTML =\n      '<div class=\"zl-modal-gallery' + (galImages.length > 1 ? ' has-multi' : '') + '\">' +\n        (galImages.length ? '<img decoding=\"async\" id=\"zlGalImg\" src=\"' + esc(galImages[0]) + '\" alt=\"' + esc(l.title) + '\">' : '') +\n        (galImages.length > 1\n          ? '<button class=\"zl-gal-btn zl-gal-prev\" id=\"zlGalPrev\" aria-label=\"Previous photo\">' + si('chevL','22') + '<\/button>' +\n            '<button class=\"zl-gal-btn zl-gal-next\" id=\"zlGalNext\" aria-label=\"Next photo\">' + si('chevR','22') + '<\/button>' +\n            '<span class=\"zl-gal-ctr\" id=\"zlGalCtr\">' + si('camera','13') + '1 \/ ' + galImages.length + '<\/span>'\n          : '') +\n      '<\/div>' +\n      '<div class=\"zl-modal-body\">' +\n        '<div class=\"zl-modal-top\">' +\n          '<div>' +\n            '<div class=\"zl-modal-title\">' + esc(l.title) + '<\/div>' +\n            '<div class=\"zl-modal-addr\">' + si('pin','13') + esc(l.address || l.city || '') + '<\/div>' +\n            '<p class=\"zl-listing-attr\">' + (isZx(l) ? 'Listed directly by zeas.immo' : 'Listing presented by an eXp Realty Luxembourg agent') + '<\/p>' +\n            (l.url ? '<a class=\"zl-view-full-link\" href=\"' + esc(l.url) + '\" target=\"_blank\" rel=\"noopener\">View full listing page ' + si('chevR','12') + '<\/a>' : '') +\n          '<\/div>' +\n          '<div style=\"text-align:right\">' +\n            '<div class=\"zl-modal-price\">' + price + (rental ? ' <span class=\"pm\">\/ month<\/span>' : '') + '<\/div>' +\n            (ec && ec.length <= 2 ? '<span class=\"zl-energy\" data-c=\"' + esc(ec) + '\">EPC ' + esc(ec) + '<\/span>' : '') +\n          '<\/div>' +\n        '<\/div>' +\n        '<div class=\"zl-modal-footer\">' +\n          '<div class=\"zl-actions\">' +\n            '<button type=\"button\" class=\"zl-cta-btn zl-cta-btn--ghost\" id=\"zlModalEnqBtn\">' + si('mail','14') + ' Ask about this property<\/button>' +\n            '<a class=\"zl-cta-btn\" href=\"' + esc(bookingUrl) + '\" target=\"_blank\" rel=\"noopener noreferrer\">Book a viewing<\/a>' +\n            (isLU(l) ? '<button type=\"button\" class=\"zl-cta-btn zl-cta-btn--ghost js-zl-acq-open\">See full cost overview<\/button>' : '') +\n          '<\/div>' +\n        '<\/div>' +\n        (specs ? '<div class=\"zl-modal-specs\">' + specs + '<\/div>' : '') +\n        (isLU(l) ? '<div class=\"zl-acq-teaser\">' +\n            '<div class=\"zl-acq-teaser-text\">' +\n              '<span class=\"zl-acq-eyebrow\">Acquisition cost<\/span>' +\n              '<h4>What would it cost to acquire this property?<\/h4>' +\n              '<p>Registration duties, notary fees and VAT typically add 8\u201313% on top of the asking price \u2014 the B\u00ebllegen Akt credit can hand back up to \u20ac40.000.<\/p>' +\n            '<\/div>' +\n            '<button type=\"button\" class=\"zl-cta-btn zl-cta-btn--ghost js-zl-acq-open\">See full cost breakdown<\/button>' +\n          '<\/div>' : '') +\n        (desc ? '<div class=\"zl-desc-wrap is-collapsed\" id=\"zlDescWrap\">' +\n                  '<div class=\"zl-modal-desc\" id=\"zlDescText\">' + esc(desc) + '<\/div>' +\n                  '<div class=\"zl-desc-fade\"><\/div>' +\n                  '<button class=\"zl-read-more\" id=\"zlReadMore\"><span>Read full description<\/span>' +\n                    '<svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M6 9l6 6 6-6\"\/><\/svg>' +\n                  '<\/button>' +\n                '<\/div>' : '') +\n        similarCardsHTML(l) +\n      '<\/div>';\n\n    if (galImages.length > 1) {\n      document.getElementById('zlGalPrev').addEventListener('click', function(){ galNav(-1); });\n      document.getElementById('zlGalNext').addEventListener('click', function(){ galNav(1); });\n    }\n    var gi = document.getElementById('zlGalImg');\n    if (gi) gi.addEventListener('click', function(){ openLB(galIdx); });\n    document.getElementById('zlModalEnqBtn').addEventListener('click', function() {\n      closePropertyModal();\n      openEnquiry(l);\n    });\n    \/\/ Two triggers can open the acquisition modal now (the teaser card and\n    \/\/ the footer's action-row button) \u2014 both share this class rather than\n    \/\/ an id, since an id can only ever belong to one element.\n    document.querySelectorAll('.js-zl-acq-open').forEach(function(b){\n      b.addEventListener('click', function(){\n        closePropertyModal();\n        openAcqModal(l);\n      });\n    });\n\n    $modalBody.querySelectorAll('.zl-sim-card').forEach(function(card){\n      card.addEventListener('click', function(e){\n        e.preventDefault();\n        var idx = parseInt(card.dataset.idx, 10);\n        if (!isNaN(idx) && all[idx]) openPropertyModal(all[idx]);\n      });\n    });\n\n    initReadMore('zl');\n\n    $overlay.classList.add('open');\n    document.body.style.overflow = 'hidden';\n  }\n\n  function galNav(dir) {\n    galIdx = (galIdx + dir + galImages.length) % galImages.length;\n    document.getElementById('zlGalImg').src = galImages[galIdx];\n    document.getElementById('zlGalCtr').innerHTML = si('camera','13') + (galIdx + 1) + ' \/ ' + galImages.length;\n  }\n\n  function closePropertyModal() {\n    $overlay.classList.remove('open');\n    document.body.style.overflow = '';\n  }\n\n  document.getElementById('zlModalClose').addEventListener('click', closePropertyModal);\n  $overlay.addEventListener('click', function(e){ if (e.target === $overlay) closePropertyModal(); });\n\n  \/* \u2500\u2500 Enquiry modal \u2500\u2500 *\/\n  function openEnquiry(l) {\n    currentListing = l;\n    var rental = l.listing_type === '2';\n    var price  = l.price ? l.price.toLocaleString('de-DE') + ' \u20ac' + (rental ? ' \/ month' : '') : 'Price on request';\n    var ref    = l.source_id || l.id.substring(0, 8).toUpperCase();\n    var defaultMsg = 'Hello,\\n\\nI am interested in the following property:\\n\\n' +\n      esc(l.title) + '\\n' +\n      esc(l.address || l.city || '') + '\\n' +\n      'Price: ' + price + '\\n' +\n      'Ref: ' + ref + '\\n\\n' +\n      'Could you please provide more information and arrange a viewing at your earliest convenience?\\n\\nThank you.';\n\n    $enqBody.innerHTML =\n      '<div class=\"zl-enq-prop-ref\">' +\n        '<strong>' + esc(l.title) + '<\/strong>' +\n        esc(l.address || l.city || '') + ' \u00b7 ' + price +\n      '<\/div>' +\n      '<h3 class=\"zl-enq-title\">Request more information<\/h3>' +\n      '<p class=\"zl-enq-sub\">We\\'ll contact the listing agent on your behalf and get back to you within 24 hours.<\/p>' +\n      '<div id=\"zlEnqForm\">' +\n        '<div class=\"zl-enq-row\">' +\n          '<div><label class=\"zl-enq-label\">First name<\/label><input class=\"zl-enq-field\" id=\"zlEnqFirst\" type=\"text\" placeholder=\"Your first name\"><\/div>' +\n          '<div><label class=\"zl-enq-label\">Last name<\/label><input class=\"zl-enq-field\" id=\"zlEnqLast\" type=\"text\" placeholder=\"Your last name\"><\/div>' +\n        '<\/div>' +\n        '<label class=\"zl-enq-label\">Email<\/label>' +\n        '<input class=\"zl-enq-field\" id=\"zlEnqEmail\" type=\"email\" placeholder=\"your@email.com\">' +\n        '<label class=\"zl-enq-label\">Phone (optional)<\/label>' +\n        '<input class=\"zl-enq-field\" id=\"zlEnqPhone\" type=\"tel\" placeholder=\"+352 ...\">' +\n        '<label class=\"zl-enq-label\">Message<\/label>' +\n        '<textarea class=\"zl-enq-field zl-enq-textarea\" id=\"zlEnqMsg\">' + defaultMsg + '<\/textarea>' +\n        '<button class=\"zl-enq-submit\" id=\"zlEnqSend\">' + si('mail','15') + ' Send enquiry<\/button>' +\n        '<p class=\"zl-enq-note\">Your details are only used to respond to your enquiry. We never share them with third parties.<\/p>' +\n      '<\/div>';\n\n    document.getElementById('zlEnqSend').addEventListener('click', function() {\n      var first = document.getElementById('zlEnqFirst').value.trim();\n      var email = document.getElementById('zlEnqEmail').value.trim();\n      if (!first || !email) {\n        alert('Please enter your first name and email address.');\n        return;\n      }\n      submitEnquiry({\n        first:   first,\n        last:    document.getElementById('zlEnqLast').value.trim(),\n        email:   email,\n        phone:   document.getElementById('zlEnqPhone').value.trim(),\n        message: document.getElementById('zlEnqMsg').value.trim(),\n        ref:     ref,\n        listing: l.title,\n        address: l.address || l.city || ''\n      });\n    });\n\n    $enqOvl.classList.add('open');\n    document.body.style.overflow = 'hidden';\n  }\n\n  function submitEnquiry(data) {\n    var btn = document.getElementById('zlEnqSend');\n    btn.disabled = true; btn.textContent = 'Sending...';\n    var fd = new FormData();\n    fd.append('action',   'enquiry');\n    fd.append('name',     data.first + (data.last ? ' ' + data.last : ''));\n    fd.append('email',    data.email);\n    fd.append('phone',    data.phone || '');\n    fd.append('message',  data.message);\n    fd.append('property', data.listing || '');\n    fd.append('ref',      data.ref || '');\n    fd.append('price',    data.price || '');\n    fetch(PROXY, { method: 'POST', body: fd })\n      .then(function(r){ return r.json(); })\n      .then(function(res){\n        if (res.success) {\n          \/\/ Reused so the acquisition-cost gate doesn't ask the same visitor\n          \/\/ to retype their name\/email if they open it later in this visit.\n          knownContact = { firstName: data.first, email: data.email };\n          showEnquirySuccess();\n        } else {\n          btn.disabled = false; btn.textContent = 'Send enquiry';\n          alert(res.error || 'Something went wrong. Please email hello@zeas.immo directly.');\n        }\n      })\n      .catch(function(){\n        btn.disabled = false; btn.textContent = 'Send enquiry';\n        alert('Something went wrong. Please email hello@zeas.immo directly.');\n      });\n  }\n\n  function showEnquirySuccess() {\n    $enqBody.innerHTML =\n      '<div class=\"zl-enq-success\">' +\n        '<div class=\"zl-tick\">' +\n          '<svg width=\"22\" height=\"22\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#fff\" stroke-width=\"2.5\" stroke-linecap=\"round\"><path d=\"M5 13l4 4L19 7\"\/><\/svg>' +\n        '<\/div>' +\n        '<h3>Enquiry sent!<\/h3>' +\n        '<p>Your message has been prepared. We\\'ll be in touch within 24 hours to discuss next steps.<\/p>' +\n      '<\/div>';\n  }\n\n  function closeEnquiry() {\n    $enqOvl.classList.remove('open');\n    document.body.style.overflow = '';\n  }\n\n  document.getElementById('zlEnqClose').addEventListener('click', closeEnquiry);\n  $enqOvl.addEventListener('click', function(e){ if (e.target === $enqOvl) closeEnquiry(); });\n\n  \/* \u2500\u2500 Acquisition cost modal \u2500\u2500\n     Fixed-cost table (registration\/transcription\/notary \u2014 never depend on\n     who's buying) + a short form for what does (use, commune, first-buyer,\n     marital, agency\/expert). calc mirrors the fixed-rate + B\u00ebllegen Akt\n     portion of runCalc() in buying-cost-calculator.html; VEFA\/VAT is\n     intentionally left out here too, same as render.php and the homepage's\n     eXp modal, since this feed has no reliable \"new build\" flag. Keep the\n     rates in sync across all copies if Luxembourg changes them. *\/\n  function fmtEUR(n) { return '\u20ac ' + Math.round(n).toLocaleString('de-DE'); }\n\n  function openAcqModal(l) {\n    var price = +l.price || 0;\n    var regDuty      = price * 0.06;\n    var transcript   = price * 0.01;\n    var notaryEst    = price * 0.01;\n    var certainTotal = price + regDuty + transcript + notaryEst;\n    var priceFmt     = price.toLocaleString('de-DE') + ' \u20ac';\n\n    $acqBody.innerHTML =\n      '<div class=\"zl-enq-prop-ref\"><strong>' + esc(l.title) + '<\/strong>' + esc(l.address || l.city || '') + ' \u00b7 ' + priceFmt + '<\/div>' +\n      '<span class=\"zl-acq-eyebrow\">Fixed costs<\/span>' +\n      '<h3 class=\"zl-acq-h\">What you\\'ll pay no matter what<\/h3>' +\n      '<p class=\"zl-acq-lead\">These apply to every buyer of this property, whatever your personal situation.<\/p>' +\n      '<div class=\"zl-acq-table\">' +\n        '<div class=\"zl-acq-row\"><span>Purchase price<\/span><b>' + fmtEUR(price) + '<\/b><\/div>' +\n        '<div class=\"zl-acq-row\"><span>Registration duty<i>6% of price<\/i><\/span><b>' + fmtEUR(regDuty) + '<\/b><\/div>' +\n        '<div class=\"zl-acq-row\"><span>Transcription duty<i>1% of price<\/i><\/span><b>' + fmtEUR(transcript) + '<\/b><\/div>' +\n        '<div class=\"zl-acq-row\"><span>Notary fee<i>~1% estimate<\/i><\/span><b>' + fmtEUR(notaryEst) + '<\/b><\/div>' +\n        '<div class=\"zl-acq-row zl-acq-row--tot\"><span>Estimated minimum total<\/span><b>' + fmtEUR(certainTotal) + '<\/b><\/div>' +\n      '<\/div>' +\n      '<hr class=\"zl-acq-hr\">' +\n      '<span class=\"zl-acq-eyebrow\">Your situation<\/span>' +\n      '<h3 class=\"zl-acq-h\">A few things that change your number<\/h3>' +\n      '<p class=\"zl-acq-lead\">The B\u00ebllegen Akt credit, resale duty and Luxembourg City\\'s surcharge depend on who\\'s buying.<\/p>' +\n      '<form id=\"zlAcqForm\">' +\n        '<div class=\"zl-enq-row\">' +\n          '<div><label class=\"zl-enq-label\">How will you use it?<\/label>' +\n            '<select class=\"zl-enq-field\" id=\"acqUse\"><option value=\"primary\">Primary residence<\/option><option value=\"secondary\">2nd home \/ holiday<\/option><option value=\"resale\">Buy-to-sell \/ investor<\/option><\/select><\/div>' +\n          '<div><label class=\"zl-enq-label\">Registration commune<\/label>' +\n            '<select class=\"zl-enq-field\" id=\"acqCommune\"><option value=\"other\" selected>Other commune<\/option><option value=\"lux\">Luxembourg City<\/option><\/select><\/div>' +\n        '<\/div>' +\n        '<div class=\"zl-enq-row\" id=\"acqPrimaryRow\">' +\n          '<div><label class=\"zl-enq-label\">First property in Luxembourg?<\/label>' +\n            '<select class=\"zl-enq-field\" id=\"acqFirstBuyer\"><option value=\"yes\">Yes \u2014 first buy<\/option><option value=\"no\">No \u2014 second buy<\/option><\/select><\/div>' +\n          '<div><label class=\"zl-enq-label\">Purchasing as<\/label>' +\n            '<select class=\"zl-enq-field\" id=\"acqMarital\"><option value=\"single\">Single buyer<\/option><option value=\"couple\">Couple \/ PACS<\/option><\/select><\/div>' +\n        '<\/div>' +\n        '<div id=\"acqCreditRow\" style=\"display:none\">' +\n          '<label class=\"zl-enq-label\">Remaining B\u00ebllegen Akt credit (\u20ac)<\/label>' +\n          '<input class=\"zl-enq-field\" id=\"acqRemCredit\" type=\"text\" placeholder=\"e.g. 25.000\" inputmode=\"numeric\">' +\n        '<\/div>' +\n        '<label class=\"zl-acq-check\"><input type=\"checkbox\" id=\"acqAgency\" checked> Include buyer\\'s agency fee (3.51%)<\/label>' +\n        '<label class=\"zl-acq-check\"><input type=\"checkbox\" id=\"acqExpert\"> Include expert valuation (\u20ac1.000)<\/label>' +\n        '<div id=\"acqGate\">' +\n          '<p class=\"zl-acq-gate-note\">Enter your name and email to see your exact total.<\/p>' +\n          '<div class=\"zl-enq-row\">' +\n            '<div><label class=\"zl-enq-label\">First name<\/label><input class=\"zl-enq-field\" id=\"acqFName\" type=\"text\"><\/div>' +\n            '<div><label class=\"zl-enq-label\">Email<\/label><input class=\"zl-enq-field\" id=\"acqEmail\" type=\"email\"><\/div>' +\n          '<\/div>' +\n        '<\/div>' +\n        '<button type=\"submit\" class=\"zl-enq-submit\" id=\"acqCalcBtn\">Calculate my exact total<\/button>' +\n      '<\/form>' +\n      '<div class=\"zl-acq-results\" id=\"acqResults\" style=\"display:none\">' +\n        '<div class=\"zl-acq-table\">' +\n          '<div class=\"zl-acq-row\"><span>State taxes<i id=\"acqCreditLine\"><\/i><\/span><b id=\"r_acqTax\">\u2014<\/b><\/div>' +\n          '<div class=\"zl-acq-row\"><span>Professional fees<\/span><b id=\"r_acqFees\">\u2014<\/b><\/div>' +\n          '<div class=\"zl-acq-row zl-acq-row--tot\"><span>Final acquisition cost<\/span><b id=\"r_acqFinal\">\u2014<\/b><\/div>' +\n        '<\/div>' +\n        '<p class=\"zl-acq-fine\">Estimate for guidance only \u2014 confirm exact figures with your notary. Need VEFA, VAT or a mortgage estimate too? <a href=\"\/en\/buying-cost-calculator-luxembourg\/?price=' + Math.round(price) + '&type=existing\" target=\"_blank\" rel=\"noopener noreferrer\">Open the full calculator \u2197<\/a><\/p>' +\n      '<\/div>';\n\n    var useSel        = document.getElementById('acqUse');\n    var communeSel     = document.getElementById('acqCommune');\n    var firstBuyerSel = document.getElementById('acqFirstBuyer');\n    var maritalSel    = document.getElementById('acqMarital');\n    var remCreditEl   = document.getElementById('acqRemCredit');\n    var agencyEl      = document.getElementById('acqAgency');\n    var expertEl      = document.getElementById('acqExpert');\n    var primaryRow    = document.getElementById('acqPrimaryRow');\n    var creditRow     = document.getElementById('acqCreditRow');\n    var gate          = document.getElementById('acqGate');\n    var fNameEl       = document.getElementById('acqFName');\n    var emailEl       = document.getElementById('acqEmail');\n\n    function updateVisibility() {\n      var isPrimary = useSel.value === 'primary';\n      primaryRow.style.display = isPrimary ? '' : 'none';\n      creditRow.style.display = (isPrimary && firstBuyerSel.value === 'no') ? '' : 'none';\n    }\n    useSel.addEventListener('change', updateVisibility);\n    firstBuyerSel.addEventListener('change', updateVisibility);\n    updateVisibility();\n\n    if (knownContact) {\n      if (fNameEl && !fNameEl.value) fNameEl.value = knownContact.firstName || '';\n      if (emailEl && !emailEl.value) emailEl.value = knownContact.email || '';\n      if (knownContact.firstName && knownContact.email) gate.style.display = 'none';\n    }\n\n    document.getElementById('zlAcqForm').addEventListener('submit', function(e){\n      e.preventDefault();\n      if (gate.style.display !== 'none') {\n        if (!fNameEl.value.trim() || !\/^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$\/.test(emailEl.value)) {\n          alert('Please enter your first name and a valid email.');\n          return;\n        }\n        knownContact = { firstName: fNameEl.value, email: emailEl.value };\n        gate.style.display = 'none';\n      }\n\n      var isResale  = useSel.value === 'resale';\n      var isPrimary = useSel.value === 'primary';\n      var isLuxC    = communeSel.value === 'lux';\n      var is1st     = firstBuyerSel.value === 'yes';\n      var isCouple  = maritalSel.value === 'couple';\n      var remCredit = parseFloat((remCreditEl.value || '0').replace(\/\\.\/g,'').replace(',', '.')) || 0;\n\n      var rd        = price * (isResale ? 0.072 : 0.06);\n      var tr        = price * 0.01;\n      var luxSurch  = isLuxC ? (price * 0.06) * 0.5 : 0;\n      var taxGross  = rd + tr + luxSurch;\n      var eligTaxes = rd + tr;\n      var creditApplied = 0, taxNet = taxGross;\n      if (isPrimary) {\n        var creditAvail = is1st ? (isCouple ? 80000 : 40000) : Math.min(remCredit, isCouple ? 80000 : 40000);\n        creditApplied = Math.min(eligTaxes, creditAvail);\n        taxNet = Math.max(0, eligTaxes - creditApplied) + luxSurch;\n      }\n      var notaryFee  = price * 0.01;\n      var agencyFee  = agencyEl.checked ? price * 0.03 * 1.17 : 0;\n      var expertFee  = expertEl.checked ? 1000 : 0;\n      var totalFees  = notaryFee + agencyFee + expertFee;\n      var finalTotal = price + taxNet + totalFees;\n\n      document.getElementById('r_acqTax').textContent   = fmtEUR(taxNet);\n      document.getElementById('r_acqFees').textContent  = fmtEUR(totalFees);\n      document.getElementById('r_acqFinal').textContent = fmtEUR(finalTotal);\n      document.getElementById('acqCreditLine').textContent = creditApplied > 0\n        ? (' \u00b7 B\u00ebllegen Akt \u2212' + fmtEUR(creditApplied) + ' applied') : '';\n      var results = document.getElementById('acqResults');\n      results.style.display = 'block';\n      results.scrollIntoView({ behavior: 'smooth', block: 'nearest' });\n    });\n\n    $acqOvl.classList.add('open');\n    document.body.style.overflow = 'hidden';\n  }\n  function closeAcqModal() {\n    $acqOvl.classList.remove('open');\n    document.body.style.overflow = '';\n  }\n  document.getElementById('zlAcqClose').addEventListener('click', closeAcqModal);\n  $acqOvl.addEventListener('click', function(e){ if (e.target === $acqOvl) closeAcqModal(); });\n\n  \/* \u2500\u2500 Lightbox \u2500\u2500 *\/\n  var $lb    = document.getElementById('zlLightbox');\n  var $lbImg = document.getElementById('zlLbImg');\n  var $lbCtr = document.getElementById('zlLbCtr');\n  var lbIdx  = 0;\n  function openLB(idx) {\n    lbIdx = idx; $lbImg.src = galImages[lbIdx];\n    $lbCtr.textContent = (lbIdx+1)+' \/ '+galImages.length;\n    $lb.classList.add('open');\n  }\n  function lbNav(d) {\n    lbIdx = (lbIdx+d+galImages.length)%galImages.length;\n    $lbImg.src = galImages[lbIdx];\n    $lbCtr.textContent = (lbIdx+1)+' \/ '+galImages.length;\n  }\n  function closeLB() { $lb.classList.remove('open'); }\n  document.getElementById('zlLbClose').addEventListener('click', closeLB);\n  document.getElementById('zlLbPrev').addEventListener('click', function(){ lbNav(-1); });\n  document.getElementById('zlLbNext').addEventListener('click', function(){ lbNav(1); });\n  $lb.addEventListener('click', function(e){ if(e.target===$lb||e.target===$lbImg) closeLB(); });\n\n  document.addEventListener('keydown', function(e){\n    if(e.key==='Escape'){closeLB();closePropertyModal();closeEnquiry();closeAcqModal();}\n    if(e.key==='ArrowLeft'&&$lb.classList.contains('open'))lbNav(-1);\n    if(e.key==='ArrowRight'&&$lb.classList.contains('open'))lbNav(1);\n  });\n\n  \/* \u2500\u2500 Icons \u2500\u2500 *\/\n  function si(name, size) {\n    size = size || '11';\n    var a = ' width=\"' + size + '\" height=\"' + size + '\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"';\n    var p = {\n      bed:   '<path d=\"M2 20V8a2 2 0 012-2h16a2 2 0 012 2v12\"\/><path d=\"M2 14h20\"\/><path d=\"M7 14V9\"\/>',\n      bath:  '<path d=\"M9 6a3 3 0 00-3 3v7h12V9\"\/><path d=\"M3 13h18v3a4 4 0 01-4 4H7a4 4 0 01-4-4v-3z\"\/>',\n      sqm:   '<rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"1\"\/><path d=\"M3 9h18M9 3v18\"\/>',\n      pin:   '<path d=\"M12 2C8.13 2 5 5.13 5 9c0 5.25 7 13 7 13s7-7.75 7-13c0-3.87-3.13-7-7-7zm0 9.5a2.5 2.5 0 110-5 2.5 2.5 0 010 5z\"\/>',\n      rooms: '<rect x=\"3\" y=\"3\" width=\"7\" height=\"7\"\/><rect x=\"14\" y=\"3\" width=\"7\" height=\"7\"\/><rect x=\"3\" y=\"14\" width=\"7\" height=\"7\"\/><rect x=\"14\" y=\"14\" width=\"7\" height=\"7\"\/>',\n      cal:   '<rect x=\"3\" y=\"4\" width=\"18\" height=\"18\" rx=\"2\"\/><path d=\"M16 2v4M8 2v4M3 10h18\"\/>',\n      mail:  '<path d=\"M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z\"\/><polyline points=\"22,6 12,13 2,6\"\/>',\n      chevL: '<path d=\"M15 18l-6-6 6-6\"\/>',\n      chevR: '<path d=\"M9 18l6-6-6-6\"\/>',\n      camera:'<path d=\"M23 19a2 2 0 01-2 2H3a2 2 0 01-2-2V8a2 2 0 012-2h4l2-3h6l2 3h4a2 2 0 012 2z\"\/><circle cx=\"12\" cy=\"13\" r=\"4\"\/>',\n    };\n    return '<svg' + a + '>' + (p[name] || '') + '<\/svg>';\n  }\n\n  function esc(s) {\n    return String(s || '')\n      .replace(\/&\/g,'&amp;').replace(\/<\/g,'&lt;')\n      .replace(\/>\/g,'&gt;').replace(\/\"\/g,'&quot;').replace(\/'\/g,'&#39;');\n  }\n\n  \/* \u2500\u2500 Read more \/ read less \u2014 only shown when the text actually overflows\n        the collapsed height, so short descriptions never get an inert button. \u2500\u2500 *\/\n  function initReadMore(prefix) {\n    var wrap = document.getElementById(prefix + 'DescWrap');\n    if (!wrap) return;\n    var text = document.getElementById(prefix + 'DescText');\n    var btn  = document.getElementById(prefix + 'ReadMore');\n    requestAnimationFrame(function () {\n      if (text.scrollHeight <= text.clientHeight + 4) {\n        wrap.classList.remove('is-collapsed');\n        btn.style.display = 'none';\n        return;\n      }\n      btn.addEventListener('click', function () {\n        var expanding = wrap.classList.contains('is-collapsed');\n        wrap.classList.toggle('is-collapsed', !expanding);\n        wrap.classList.toggle('is-expanded', expanding);\n        btn.querySelector('span').textContent = expanding ? 'Show less' : 'Read full description';\n      });\n    });\n  }\n\n  init();\n})();\n<\/script>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t","protected":false},"excerpt":{"rendered":"<p>eXp Luxembourg Properties \u00b7 All Listings eXp Luxembourg Properties The listings we represent directly, and the wider eXp Luxembourg network across the country \u2014 one search, both included. If one of these fits what you&#8217;re looking for, we represent you on it, at no cost to you as the buyer. All Listings Our Listings All [&hellip;]<\/p>\n","protected":false},"author":4,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"footnotes":""},"class_list":["post-50074","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/zeas.immo\/en\/wp-json\/wp\/v2\/pages\/50074","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/zeas.immo\/en\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/zeas.immo\/en\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/zeas.immo\/en\/wp-json\/wp\/v2\/users\/4"}],"replies":[{"embeddable":true,"href":"https:\/\/zeas.immo\/en\/wp-json\/wp\/v2\/comments?post=50074"}],"version-history":[{"count":4,"href":"https:\/\/zeas.immo\/en\/wp-json\/wp\/v2\/pages\/50074\/revisions"}],"predecessor-version":[{"id":51318,"href":"https:\/\/zeas.immo\/en\/wp-json\/wp\/v2\/pages\/50074\/revisions\/51318"}],"wp:attachment":[{"href":"https:\/\/zeas.immo\/en\/wp-json\/wp\/v2\/media?parent=50074"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}