8
Prime 1/3-Acre Building Lot in Bahia near Uvita Costa Rica
  • Property Type
  • Building Lot
  • House Size
  • 1225 m²
  • Land Size
Prime 1/3-Acre Building Lot in Bahia near Uvita Costa Rica
  • $149,000

Description

Discover a rare opportunity to build in one of the most desirable areas of Bahía Ballena. This 0.30-acre lot offers approximately 65% usable terrain, providing around 8,500 square feet of buildable space—ideal for a variety of residential or income-producing projects.

The property features two separate access points: one via easement and another directly from a public road. The public road entrance is already equipped with an entrance gate and carport, adding immediate value and convenience.
With its versatile layout, this lot is perfectly suited for:

  • Multiple cabinas or rental units
  • Two homes with separate entrances
  • One large private residence with pool

Read More

Details

  • Property ID 74850
  • Price $149,000
  • Land Size 1225 m²
  • Property Type Building Lot
  • Property Status For Sale
  • Pool No
  • Drinking Water AyA or ASADA - Legal Source
  • Septic/Sewer Septic System
  • Roof Metal
  • Air Conditioning None
  • Electrical Service Utility Grid Available - ICE
  • Elevation (approx) feet 10
  • Floors None (Land) or Unknown
  • Agent MLS # otp-mo-4580
  • Financing Available No - No Financing Available
  • City/Town: Bahia
  • 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); })();