13
Spectacular Ocean View 24+ Acre Building Lot with Legal Water in Dominical Costa Rica
  • Property Type
  • Building Lot
  • House Size
  • 99957 m²
  • Land Size
Spectacular Ocean View 24+ Acre Building Lot with Legal Water in Dominical Costa Rica
  • $550,000

Description

Discover a rare investment and development opportunity in the lush mountains of San Cristóbal de Aguirre, Costa Rica. This expansive 10-hectare property (24.7 acres) offers the perfect balance between privacy, sustainability, breathtaking natural beauty, and long-term investment potential in one of the South Pacific’s emerging high-demand areas.

Strategically positioned within a peaceful mountain setting, this property features five large prepared building sites (planteles), four of which enjoy stunning ocean views overlooking the Pacific coastline, while the fifth offers serene mountain and jungle views surrounded by nature and cool breezes year-round.

This is an ideal opportunity for buyers seeking to develop a luxury eco-estate, boutique retreat, family compound, wellness center, or multiple vacation rental homes in an increasingly desirable region of Costa Rica.

Sustainable Off-Grid Living Potential

The property is designed for self-sustainable off-grid living, allowing future owners to embrace independence and eco-conscious development without sacrificing comfort or functionality.

Water service is available through the local ASADA water association, while electricity can be developed through several alternative energy solutions including:

  • Solar power systems
  • Hydroelectric turbine systems
  • Wind energy systems

Read More

Details

  • Property ID 74828
  • Price $550,000
  • Land Size 99957 m²
  • Property Type Building Lot
  • Property Status For Sale
  • Pool No
  • View Mountains
  • Drinking Water AyA or ASADA - Legal Source
  • Septic/Sewer Septic System
  • Internet Other High Speed
  • Air Conditioning None
  • Electrical Service Utility Grid Available - ICE
  • Elevation (approx) feet 1650
  • Agent MLS # bz-lc-0074
  • Financing Available No - No Financing Available
  • City/Town: Dominical
  • Province: Puntarenas

Contact Information

Inquire About This Property

Compare listings

Compare
- by the time it is submitted there is no data-hz-id // ancestor left to walk to, the same disconnect the WhatsApp fix above // works around. The one moment the card is still reachable is the click // that OPENED the modal, so that is captured here and handed to // captureLead() by the modal's own id when it submits. var pendingModalCtx = {}; document.addEventListener("click", function(e){ var t = e.target && e.target.closest ? e.target.closest("[data-model-id]") : null; if (!t) return; var modalId = t.dataset.modelId; if (!modalId) return; var lid = "", el = t, depth = 0, card = null; while (el && depth < 20) { if (el.dataset && el.dataset.hzId) { card = el; lid = el.dataset.hzId; break; } el = el.parentElement; depth++; } var url = cbpCardUrl(card); if (lid || url) pendingModalCtx[modalId] = { lid: lid, url: url }; }, true); var sentEmails = {}; function fieldVal(form, sel){ try { var el = form.querySelector(sel); return el && el.value ? String(el.value).trim() : ""; } catch (e) { return ""; } } function looksEmail(s){ return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(s); } function captureLead(form){ if (!form || typeof form.querySelectorAll !== "function") return; var inputs = form.querySelectorAll("input, textarea"), i, el, val; // Email: prefer a real email field; fall back to scanning values (Green // Popups/lepopup uses opaque id-based field names, so type/name won't match). var email = fieldVal(form, 'input[type=email]') || fieldVal(form, 'input[name*="email" i]'); if (!looksEmail(email)) { email = ""; for (i = 0; i < inputs.length; i++) { val = (inputs[i].value || "").trim(); if (looksEmail(val)) { email = val; break; } } } if (!email) return; // no email → not a lead form if (sentEmails[email]) return; // dedupe per page life sentEmails[email] = 1; // Name: prefer a name-ish field; else first short text value that isn't the // email, a phone, or a long message. var name = fieldVal(form, 'input[name*="name" i]:not([name*="user" i])'); if (!name) { for (i = 0; i < inputs.length; i++) { el = inputs[i]; val = (el.value || "").trim(); var t = (el.type || "").toLowerCase(); if (!val || looksEmail(val)) continue; if (t === "email" || t === "tel" || t === "number" || t === "hidden" || t === "checkbox" || t === "radio" || t === "submit" || t === "button") continue; var digits = val.replace(/\D/g, ""); if (digits.length >= 7 && digits.length >= val.length - 4) continue; // phone-like if (val.length > 60 || !/[A-Za-z]/.test(val)) continue; // message / non-name name = val; break; } } // v3.54.0: L (CBP_VE_LISTING) is only ever set on the listing's OWN page. // A card's Email button submitted from a search-results list has no L to // fall back on - use the context stashed when that card's button was // clicked (pendingModalCtx, above) instead of leaving this lead // unattributed. var modalEl = form.closest ? form.closest(".modal") : null; var modalCtx = modalEl && modalEl.id ? pendingModalCtx[modalEl.id] : null; var leadExtra = { event_type: "form_submit", email: email, name: name, listing_id: (modalCtx && modalCtx.lid) || (L ? L.listing_id : "") }; if (modalCtx && modalCtx.url) leadExtra.page_url = modalCtx.url; var data = body(leadExtra); try { if (navigator.sendBeacon) { navigator.sendBeacon(ENDPOINT, new Blob([data], { type: "application/x-www-form-urlencoded" })); } else { fetch(ENDPOINT, { method: "POST", credentials: "include", keepalive: true, headers: { "Content-Type": "application/x-www-form-urlencoded;charset=UTF-8" }, body: data }).catch(function(){}); } } catch (e) {} } document.addEventListener("submit", function(e){ captureLead(e.target); }, true); // Green Popups (lepopup) forms have NO
tag — the submit control is an // , so no submit event fires. Capture on button // click instead: find the popup container (nearest ancestor holding a // lepopup field) and scan it. captureLead is container-agnostic. function lepopupRoot(el){ var n = el; while (n && n !== document.body) { if (n.querySelector && n.querySelector('input[name^="lepopup-"]')) return n; n = n.parentElement; } return null; } document.addEventListener("click", function(e){ var t = e.target; var btn = t && t.closest ? t.closest(".lepopup-button") : null; if (!btn) return; var root = lepopupRoot(btn); if (root) captureLead(root); }, true); // ── Favourites (v2.44.0). Read from the live custom.min.js (houzez.Favorites), // 2026-09-06: the heart is . // A member's saved heart is re-classed to .remove_fav; a guest's (cookie // houzez_favorite_listings) keeps .add-favorite-js and gains .remove-favorite. // Both Houzez handlers are delegated on document in the BUBBLE phase, so // this capture-phase observer sees every click first and must not stop it: // Houzez still saves / un-saves / shows its login modal exactly as before. // The click itself is the signal -- even when Houzez saves nothing for a // guest, the person just told us which listing they want. document.addEventListener("click", function(e){ var btn = e.target && e.target.closest ? e.target.closest(".add-favorite-js, .remove_fav") : null; if (!btn) return; var id = parseInt(btn.getAttribute("data-listid") || "", 10); if (!id) return; var removing = btn.classList.contains("remove_fav") || btn.classList.contains("remove-favorite"); var onPage = L && String(L.listing_id) === String(id); var data = body({ event_type: removing ? "favorite_remove" : "favorite_add", listing_id: id, listing_price: onPage ? L.price : "", listing_type: onPage ? L.primary_type : "" }); // The answer to this one beacon is read, unlike every other: the portal // counts the saves and says when to offer to keep them (v2.98.0). The // rule lives there, not here, so it cannot drift from the rest of the // site. keepalive is dropped because we are waiting for the body. try { fetch(ENDPOINT, { method: "POST", credentials: "include", headers: { "Content-Type": "application/x-www-form-urlencoded;charset=UTF-8" }, body: data }) .then(function (r) { return r.json(); }) .then(function (j) { if (!j || !j.success || !j.data || !j.data.save_ask) return; if (window.CBPVE && window.CBPVE.saveCard) window.CBPVE.saveCard(j.data.save_ask, j.data.saves); }) .catch(function(){}); } catch (e2) {} }, true); // WhatsApp click (v3.53.0). Every "message the agent" button on the site — // Houzez's own per-card and per-listing contact icons, plus our co-broke // banner's Ask/Share-location buttons — is a plain or // , target=_blank, so the tab this // script runs in never navigates away: no sendBeacon needed, a normal // keepalive fetch has time to land. Chris, 2026-09-22: wants every one of // these logged so a listing+time-range search (or a site-wide search) can // find who reached for WhatsApp. listing_id comes from the nearest // Houzez v9 card wrapper (data-hz-id, grid/related cards) or, failing // that, the current single-listing page's own id — never both guessed at // once, so a related-listing card is never mis-attributed to the page // it happens to sit on. document.addEventListener("click", function(e){ var a = e.target && e.target.closest ? e.target.closest('a[href*="wa.me"],a[href*="api.whatsapp.com"]') : null; if (!a) return; var lid = "", el = a, depth = 0, card = null; while (el && depth < 20) { if (el.dataset && el.dataset.hzId) { card = el; lid = el.dataset.hzId; break; } el = el.parentElement; depth++; } if (!lid && L) lid = L.listing_id; // v3.54.0: on a search-results list the click's own page (location.href, // the "common" default) is the LIST, not the card's listing - read the // card's real URL instead so the VE timeline links to the property that // was actually asked about. var linkUrl = cbpWaUrl(a) || cbpCardUrl(card); var whatsappExtra = { event_type: "whatsapp_click", listing_id: lid }; if (linkUrl) whatsappExtra.page_url = linkUrl; var data = body(whatsappExtra); try { fetch(ENDPOINT, { method: "POST", credentials: "include", keepalive: true, headers: { "Content-Type": "application/x-www-form-urlencoded;charset=UTF-8" }, body: data }).catch(function(){}); } catch (e3) {} // Ad-platform conversion feed (v3.53.1). Chris, 2026-09-22: wants this // counted as a GA4/Google Ads/Bing conversion too. Checked in GTM // (container GTM-KDT3465R) with Tag Assistant: the site's existing // "WhatsApp - Link Click" / "WhatsApp 2 - Link Click" triggers (Just // Links, matching this exact URL) have three tags built and ready // (BingAds/GA4/GAds Conv. - WhatsApp - Click) but NONE of them have // fired on a real click — confirmed live, zero dataLayer activity. // Pushing our own named event here is a second, independent path into // the same three tags once GTM is repointed at a Custom Event trigger // for it, one that does not depend on GTM's own link-click listener // ever starting to catch this click. try { window.dataLayer = window.dataLayer || []; window.dataLayer.push({ event: "whatsapp_click", whatsapp_listing_id: lid || undefined }); } catch (e4) {} }, true); // Guest Save-Search (v2.40.0) — see the PHP comment above where // $guest_save_endpoint is set. Empty for a logged-in visitor: the whole // block below is inert (the early-return on the capture listener never // finds a target worth intercepting, since GSS_ENDPOINT is falsy) and // Houzez's own button keeps working exactly as it always has. var GSS_ENDPOINT = "https://portal.costaballenaproperty.com/wp-admin/admin-ajax.php?action=cbp_ve_guest_save_search"; if (GSS_ENDPOINT) { var gssStyleTag = document.createElement("style"); gssStyleTag.textContent = "#cbpss-ov{position:fixed;inset:0;background:rgba(20,20,30,.55);z-index:99999;display:flex;align-items:center;justify-content:center;padding:16px;box-sizing:border-box;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Arial,sans-serif}" + "#cbpss-ov .cbpss-card{background:#fff;border-radius:14px;max-width:380px;width:100%;padding:22px 22px 18px;box-shadow:0 20px 50px rgba(0,0,0,.25);box-sizing:border-box}" + "#cbpss-ov .cbpss-h{font-size:18px;font-weight:700;color:#16213d;margin-bottom:6px}" + "#cbpss-ov .cbpss-sum{font-size:13px;color:#5a6478;line-height:1.5;margin-bottom:14px}" + "#cbpss-ov .cbpss-name,#cbpss-ov .cbpss-email{width:100%;box-sizing:border-box;border:1px solid #cfd6e3;border-radius:10px;padding:12px 14px;font-size:16px;margin-bottom:6px}" + "#cbpss-ov .cbpss-name:focus,#cbpss-ov .cbpss-email:focus{outline:none;border-color:#0b5ed7;box-shadow:0 0 0 3px rgba(11,94,215,.15)}" + "#cbpss-ov .cbpss-err{color:#c0392b;font-size:13px;margin:4px 0}" + "#cbpss-ov .cbpss-row{display:flex;gap:10px;margin-top:14px}" + "#cbpss-ov button{border-radius:10px;padding:12px 16px;font-size:15px;font-weight:600;cursor:pointer;min-height:44px;border:1px solid #cfd6e3;background:#fff;color:#3f4a66;flex:0 0 auto}" + "#cbpss-ov .cbpss-save{background:#0b5ed7;border-color:#0b5ed7;color:#fff;flex:1 1 auto}" + "#cbpss-ov .cbpss-save:disabled{opacity:.6;cursor:default}"; document.head.appendChild(gssStyleTag); function gssOpen() { if (document.getElementById("cbpss-ov")) return; var S = window.CBP_VE_SEARCH; var summary = (S && S.summary) || "your current search"; var ov = document.createElement("div"); ov.id = "cbpss-ov"; var esc = function (s) { return String(s).replace(/&/g, "&").replace(/Save this search' + '
' + esc(summary) + '
' + '' + '' + '' + '
' + '' + '' + '
' + ''; document.body.appendChild(ov); var nameEl = ov.querySelector(".cbpss-name"); var emailEl = ov.querySelector(".cbpss-email"); var errEl = ov.querySelector(".cbpss-err"); var saveBtn = ov.querySelector(".cbpss-save"); setTimeout(function () { nameEl.focus(); }, 0); ov.addEventListener("click", function (e) { if (e.target === ov) ov.remove(); }); ov.querySelector(".cbpss-cancel").onclick = function () { ov.remove(); }; nameEl.addEventListener("keydown", function (e) { if (e.key === "Enter") emailEl.focus(); }); emailEl.addEventListener("keydown", function (e) { if (e.key === "Enter") saveBtn.click(); }); saveBtn.onclick = function () { var n = nameEl.value.trim(); var v = emailEl.value.trim(); if (!n) { errEl.textContent = "Enter your name."; errEl.style.display = "block"; return; } if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(v)) { errEl.textContent = "Enter a valid email address."; errEl.style.display = "block"; return; } errEl.style.display = "none"; saveBtn.textContent = "Saving…"; saveBtn.disabled = true; // The QUERY STRING, exactly as Houzez's own save-search sends it (its // hidden field is search_URI). This is the search; search_params is a // parsed rendering of it kept for display and editing. Storing only // the parsed form is what made the portal rebuild a URL on every send, // and the rebuilt one did not return the same listings. var qs = ""; try { qs = (window.location.search || "").replace(/^\?/, ""); } catch (e) { qs = ""; } var data = "name=" + encodeURIComponent(n) + "&email=" + encodeURIComponent(v) + "&search_uri=" + encodeURIComponent(qs) + "&search_params=" + encodeURIComponent(JSON.stringify((S && S.params) || [])) + "&summary=" + encodeURIComponent(summary); fetch(GSS_ENDPOINT, { method: "POST", credentials: "include", headers: { "Content-Type": "application/x-www-form-urlencoded;charset=UTF-8" }, body: data }).then(function (r) { return r.json(); }).then(function (j) { if (j && j.success) { ov.querySelector(".cbpss-card").innerHTML = '
✓ Saved
' + '
We\'ll email you when something new matches.
' + '
'; ov.querySelector(".cbpss-cancel").onclick = function () { ov.remove(); }; } else { errEl.textContent = (j && j.data && j.data.message) || "Something went wrong — try again."; errEl.style.display = "block"; saveBtn.textContent = "Save & get email alerts"; saveBtn.disabled = false; } }).catch(function () { errEl.textContent = "Network error — try again."; errEl.style.display = "block"; saveBtn.textContent = "Save & get email alerts"; saveBtn.disabled = false; }); }; } // Capture phase on document, attached as early as possible: this runs // BEFORE Houzez's own bubble-phase jQuery .on('click', ...) handler can // fire, regardless of attachment order. stopPropagation() here keeps the // event from ever reaching that handler (or the login modal it would // open) at all -- this is the only reason the fix works without editing // custom.js, which a Houzez update would overwrite wholesale anyway. document.addEventListener("click", function (e) { var btn = e.target && e.target.closest ? e.target.closest(".save_search_click") : null; if (!btn) return; e.preventDefault(); e.stopPropagation(); if (e.stopImmediatePropagation) e.stopImmediatePropagation(); gssOpen(); }, true); } // "Email me the flyer PDF" (v3.50.3) — see the PHP comment above where // $flyer_req_endpoint is set. Houzez's print button is
  • , d-none d-md-block (desktop only) in the live theme — // verified by reading the actual DOM, same rule as never editing custom.js. // The d-none/d-md-class removal (mobile-friendly, Chris's requirement) only // ever runs once FLYER_REQ_ENABLED is confirmed true (the fetch().then() // above) — Chris: unchecked, "use the default Houzez version," meaning // untouched, not just non-functional. Houzez's own button is left exactly // alone, visually and behaviourally, until the setting says otherwise. var PDFREQ_ENDPOINT = "https://portal.costaballenaproperty.com/wp-admin/admin-ajax.php?action=cbp_ve_flyer_request"; function cbpprUpgradeButtons() { if (!FLYER_REQ_ENABLED) return; var btns = document.querySelectorAll(".houzez-print"); for (var i = 0; i < btns.length; i++) { var li = btns[i]; if (li.getAttribute("data-cbppr")) continue; li.setAttribute("data-cbppr", "1"); li.classList.remove("d-none", "d-md-block"); var span = li.querySelector("[role=button]") || li; span.setAttribute("title", "Email me this listing's PDF"); } } window.cbpprUpgradeButtons = cbpprUpgradeButtons; document.addEventListener("DOMContentLoaded", cbpprUpgradeButtons); var cbpprStyleTag = document.createElement("style"); cbpprStyleTag.textContent = "#cbppr-ov{position:fixed;inset:0;background:rgba(20,20,30,.55);z-index:99999;display:flex;align-items:center;justify-content:center;padding:16px;box-sizing:border-box;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Arial,sans-serif}" + "#cbppr-ov .cbppr-card{background:#fff;border-radius:14px;max-width:380px;width:100%;padding:22px 22px 18px;box-shadow:0 20px 50px rgba(0,0,0,.25);box-sizing:border-box}" + "#cbppr-ov .cbppr-h{font-size:18px;font-weight:700;color:#16213d;margin-bottom:6px}" + "#cbppr-ov .cbppr-sum{font-size:13px;color:#5a6478;line-height:1.5;margin-bottom:14px}" + "#cbppr-ov .cbppr-name,#cbppr-ov .cbppr-email,#cbppr-ov .cbppr-fname,#cbppr-ov .cbppr-femail{width:100%;box-sizing:border-box;border:1px solid #cfd6e3;border-radius:10px;padding:12px 14px;font-size:16px;margin-bottom:6px}" + "#cbppr-ov .cbppr-name:focus,#cbppr-ov .cbppr-email:focus,#cbppr-ov .cbppr-fname:focus,#cbppr-ov .cbppr-femail:focus{outline:none;border-color:#0b5ed7;box-shadow:0 0 0 3px rgba(11,94,215,.15)}" + // Language — a small flip switch, not a form control the same size as // name/email. Chris, 2026-09-17: "much smaller... not such a big deal." // Unchecked = English (the default), checked = Spanish. "#cbppr-ov .cbppr-langrow{display:flex;align-items:center;gap:6px;font-size:12px;color:#8a93a6;margin:0 0 10px}" + "#cbppr-ov .cbppr-switch{position:relative;display:inline-block;width:32px;height:18px;flex:0 0 auto}" + "#cbppr-ov .cbppr-switch input{position:absolute;inset:0;opacity:0;margin:0;cursor:pointer;z-index:1}" + "#cbppr-ov .cbppr-switch-track{position:absolute;inset:0;background:#cfd6e3;border-radius:999px;transition:background .15s}" + "#cbppr-ov .cbppr-switch-thumb{position:absolute;top:2px;left:2px;width:14px;height:14px;background:#fff;border-radius:50%;transition:transform .15s;box-shadow:0 1px 2px rgba(0,0,0,.3)}" + "#cbppr-ov .cbppr-switch input:checked ~ .cbppr-switch-track{background:#0b5ed7}" + "#cbppr-ov .cbppr-switch input:checked ~ .cbppr-switch-thumb{transform:translateX(14px)}" + "#cbppr-ov .cbppr-friend-toggle{display:flex;align-items:center;gap:8px;font-size:14px;color:#3f4a66;margin:4px 0 4px;cursor:pointer;min-height:44px}" + "#cbppr-ov .cbppr-friend-toggle input{width:18px;height:18px}" + "#cbppr-ov .cbppr-friend-block{border-top:1px solid #eef1f8;margin-top:4px;padding-top:10px}" + "#cbppr-ov .cbppr-err{color:#c0392b;font-size:13px;margin:4px 0}" + "#cbppr-ov .cbppr-row{display:flex;gap:10px;margin-top:14px}" + "#cbppr-ov button{border-radius:10px;padding:12px 16px;font-size:15px;font-weight:600;cursor:pointer;min-height:44px;border:1px solid #cfd6e3;background:#fff;color:#3f4a66;flex:0 0 auto}" + "#cbppr-ov .cbppr-save{background:#0b5ed7;border-color:#0b5ed7;color:#fff;flex:1 1 auto}" + "#cbppr-ov .cbppr-save:disabled{opacity:.6;cursor:default}"; document.head.appendChild(cbpprStyleTag); // A little flip switch, not a dropdown — unchecked (default) = English, // checked = Spanish. $LABEL is what the switch turns ON, since English is // already the assumed default and doesn't need its own labelled state. function cbpprLangSwitch(cls, label) { return ''; } function cbpprOpen(pid) { if (document.getElementById("cbppr-ov")) return; var ov = document.createElement("div"); ov.id = "cbppr-ov"; ov.innerHTML = '
    ' + '
    Get the PDF
    ' + '
    Tell us where to send it and we\'ll email you the listing PDF right away.
    ' + '' + '' + cbpprLangSwitch("cbppr-lang", "Send it in Spanish") + '' + '' + '' + '
    ' + '' + '' + '
    ' + '
    '; document.body.appendChild(ov); var nameEl = ov.querySelector(".cbppr-name"); var emailEl = ov.querySelector(".cbppr-email"); var langEl = ov.querySelector(".cbppr-lang"); var friendCheck = ov.querySelector(".cbppr-friend-check"); var friendBlock = ov.querySelector(".cbppr-friend-block"); var fnameEl = ov.querySelector(".cbppr-fname"); var femailEl = ov.querySelector(".cbppr-femail"); var flangEl = ov.querySelector(".cbppr-flang"); var errEl = ov.querySelector(".cbppr-err"); var saveBtn = ov.querySelector(".cbppr-save"); setTimeout(function () { nameEl.focus(); }, 0); ov.addEventListener("click", function (e) { if (e.target === ov) ov.remove(); }); ov.querySelector(".cbppr-cancel").onclick = function () { ov.remove(); }; nameEl.addEventListener("keydown", function (e) { if (e.key === "Enter") emailEl.focus(); }); emailEl.addEventListener("keydown", function (e) { if (e.key === "Enter") saveBtn.click(); }); friendCheck.addEventListener("change", function () { friendBlock.style.display = friendCheck.checked ? "block" : "none"; if (friendCheck.checked) fnameEl.focus(); }); var emailRe = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; saveBtn.onclick = function () { var n = nameEl.value.trim(); var v = emailEl.value.trim(); if (!n) { errEl.textContent = "Enter your name."; errEl.style.display = "block"; return; } if (!emailRe.test(v)) { errEl.textContent = "Enter a valid email address."; errEl.style.display = "block"; return; } var sendFriend = friendCheck.checked; var fn = sendFriend ? fnameEl.value.trim() : ""; var fv = sendFriend ? femailEl.value.trim() : ""; if (sendFriend && (!fn || !emailRe.test(fv))) { errEl.textContent = "Enter your friend's name and a valid email address, or uncheck \"send to a friend\"."; errEl.style.display = "block"; return; } errEl.style.display = "none"; saveBtn.textContent = "Sending…"; saveBtn.disabled = true; var data = "pid=" + encodeURIComponent(pid) + "&name=" + encodeURIComponent(n) + "&email=" + encodeURIComponent(v) + "&lang=" + ( langEl.checked ? "es" : "en" ) + (sendFriend ? "&friend_name=" + encodeURIComponent(fn) + "&friend_email=" + encodeURIComponent(fv) + "&friend_lang=" + ( flangEl.checked ? "es" : "en" ) : ""); fetch(PDFREQ_ENDPOINT, { method: "POST", credentials: "include", headers: { "Content-Type": "application/x-www-form-urlencoded;charset=UTF-8" }, body: data }).then(function (r) { return r.json(); }).then(function (j) { if (j && j.success) { // Same beacon as every other signal (favourites, saved searches): // logged here, not inside the mail-sending endpoint, which only // owns identity + delivery, never the audit trail (cbp_events). fetch(ENDPOINT, { method: "POST", credentials: "include", keepalive: true, headers: { "Content-Type": "application/x-www-form-urlencoded;charset=UTF-8" }, body: body({ event_type: "pdf_request", listing_id: pid }) }).catch(function(){}); var sumMsg = (j.data && j.data.friend_sent) ? "Check your email in a minute or two — and your friend's." : "Check your email in a minute or two."; ov.querySelector(".cbppr-card").innerHTML = '
    ✓ On its way
    ' + '
    ' + sumMsg + '
    ' + '
    '; ov.querySelector(".cbppr-cancel").onclick = function () { ov.remove(); }; } else { errEl.textContent = (j && j.data && j.data.message) || "Something went wrong — try again."; errEl.style.display = "block"; saveBtn.textContent = "Email me the PDF"; saveBtn.disabled = false; } }).catch(function () { errEl.textContent = "Network error — try again."; errEl.style.display = "block"; saveBtn.textContent = "Email me the PDF"; saveBtn.disabled = false; }); }; } // Capture phase, same race-winning technique as guest-save-search above — // Houzez's own bubble-phase handler (window.print()) must never fire. // Gated on FLYER_REQ_ENABLED: unchecked in Settings, this listener still // exists but does nothing — the click falls through untouched and Houzez's // own print handler runs exactly as if this feature were never installed. document.addEventListener("click", function (e) { if (!FLYER_REQ_ENABLED) return; var btn = e.target && e.target.closest ? e.target.closest(".houzez-print") : null; if (!btn) return; e.preventDefault(); e.stopPropagation(); if (e.stopImmediatePropagation) e.stopImmediatePropagation(); var pid = parseInt(btn.getAttribute("data-propid") || (L ? L.listing_id : 0), 10); if (!pid) return; cbpprOpen(pid); }, true); })();