1<!DOCTYPE html> 2<html class="no-js" lang=en> 3<head> 4 <meta charset="utf-8"> 5 <meta http-equiv="x-ua-compatible" content="ie=edge"> 6 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 7 <meta name="twitter:card" content="summary_large_image"> 8 <meta property="og:type" content="website"> 9 <title>Recently Sold Properties in CA, WA, MA, TX | Reali</title> 10 <meta property="og:title" content="Recently Sold Properties in CA, WA, MA, TX | Reali"> 11 <meta name="twitter:title" content="Recently Sold Properties in CA, WA, MA, TX | Reali"> 12 <meta name="description" content="Looking to track your past real estate transactions? Check out Reali, the go-to platform for California real estate. Discover all your past deals with ease."> 13 <meta property="og:description" content="Looking to track your past real estate transactions? Check out Reali, the go-to platform for California real estate. Discover all your past deals with ease."> 14 <meta name="twitter:description" content="Looking to track your past real estate transactions? Check out Reali, the go-to platform for California real estate. Discover all your past deals with ease."> 15 <meta property="og:image" content="https://media-production.lp-cdn.com/cdn-cgi/image/format=auto,quality=85/https://media-production.lp-cdn.com/media/b7161822-46d1-44a3-9885-88a8fe56d2de"> 16 <meta name="twitter:image" content="https://media-production.lp-cdn.com/cdn-cgi/image/format=auto,quality=85/https://media-production.lp-cdn.com/media/b7161822-46d1-44a3-9885-88a8fe56d2de"> 17 <meta property="og:url" content="https://reali.com/properties/sold"> 18 <link rel="canonical" href="https://reali.com/properties/sold"> 19
19<script>(function(html){html.className = html.className.replace(/\bno-js\b/,'js')})(document.documentElement);</script>
19 20 <link rel="preconnect" href="https://fonts.googleapis.com"> 21 <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> 22 <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Lexend%20Deca:100,200,300,400,500,600,700,800,900"><link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Nunito:200,300,400,500,600,700,800,900,200i,300i,400i,500i,600i,700i,800i,900i"> 23 <link rel="preload" href="https://cdnjs.cloudflare.com/ajax/libs/ion-rangeslider/2.3.1/css/ion.rangeSlider.min.css" as="style" onload="this.onload=null;this.rel='stylesheet'"> 24 <noscript><link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/ion-rangeslider/2.3.1/css/ion.rangeSlider.min.css"></noscript> 25 <link rel="icon" type="image/x-icon" href="/favicon.ico"> 26 <link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png"> 27 <link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png"> 28 <link rel="icon" type="image/png" sizes="48x48" href="/favicon-48x48.png"> 29 <style id="global-css">:root{--global-primary-font-family:'Lexend Deca',sans-serif;--global-primary-font-family-short:Lexend Deca;--global-secondary-font-family:Nunito,sans-serif;--global-secondary-font-family-short:Nunito;--global-body-padding:0px;--global-background-color:#fff;--global-body-font-size:16px;--global-h1-font-size:64px;--global-h2-font-size:52px;--global-h3-font-size:42px;--global-h4-font-size:24px;--global-h5-font-size:12px;--global-h6-font-size:32px;--global-section-padding:96px;}@media (max-width: 768px){:root{--global-section-padding:64px;}}body{padding:0px;background-color:#fff;font-family:Nunito,sans-serif;font-size:16px;}@media (min-width: 768px){body{padding:0px 0px 0px 0px;}}h1,h2,h3,h4,h5,h6,button{font-family:'Lexend Deca',sans-serif;}h1{font-size:64px;}h2{font-size:52px;}h3{font-size:42px;}h4{font-size:24px;}h5{font-size:12px;}h6{font-size:32px;}</style> 30 <link rel="stylesheet" type="text/css" href="https://styles.luxurypresence.com/visionary/index.css" /> 31 <style id="website-css">@import url('https://fonts.googleapis.com/css2?family=Belanosima:wght@400;600;700&family=Montserrat:ital,wght@0,100..900;1,100..900&display=swap');:root{--global-h4-font-size:32px;--global-h5-font-size:24px;--global-h6-font-size:20px;--black:#192F38;--red:#d40000;--gray:#F8F8F8;--white:#fff;--secondary:#2E8DAB;--accent:#1F5E73;}@media(max-width:1024px) and (min-width:560px){:root{--global-h2-font-size:38px!important;}}.watermark{font-variation-settings:'GRAD' 0,'slnt' 0,'XTRA' 511,'XOPQ' 96,'YOPQ' 50,'YTLC' 537,'YTUC' 712,'YTAS' 750,'YTDE' -221,'YTFI' 738,'opsz' 44;}@media only screen and (max-width:768px){.contact-form-modal__close:after{height:25px;width:20px;--iconColor:var(--red);}}@media only screen and (min-width:769px){.contact-form-modal__close:after{height:30px;width:40px;}}@media only screen and (min-width:769px){body.page-sellers .hoverable-section{padding-bottom:0;padding-top:0;}}@media only screen and (max-width:768px){body.page-sellers .hoverable-section{padding-bottom:80px;padding-top:80px;}}@media only screen and (min-width:769px){body.page-buyers .hoverable-section{padding-bottom:0;padding-top:0;}}@media only screen and (max-width:768px){body.page-buyers .hoverable-section{padding-bottom:80px;padding-top:80px;}}@media only screen and (min-width:769px){.page-about-sir .lp-vertical-paddings:not([class*=pb-]){padding-bottom:128px;padding-top:128px;}}@media only screen and (max-width:768px){.page-about-sir .lp-vertical-paddings:not([class*=pb-]){padding-bottom:80px;padding-top:80px;}}@media only screen and (min-width:769px){.page-sir-auction .lp-vertical-paddings:not([class*=pb-]){padding-bottom:128px;padding-top:128px;}}@media only screen and (max-width:768px){.page-sir-auction .lp-vertical-paddings:not([class*=pb-]){padding-bottom:80px;padding-top:80px;}}.page-concierge-auctions .custom-intro__title:after{content:"";background-image:url(https://media-production.lp-cdn.com/media/97d074d2-9190-4504-942c-236898102d72);height:150px;width:500px;max-width:100%;-webkit-filter:invert(1);filter:invert(1);-webkit-background-size:contain;background-size:contain;background-repeat:no-repeat;display:block;background-position-x:center;}@media only screen and (max-width:768px){.page-concierge-auctions .custom-intro__title:after{height:100px;}}.page-concierge-auctions .custom-intro__title{font-size:0;}.lp-h1,h1,.lp-h2,h2,.lp-h3,h3,.lp-h4,h4,.lp-h5,h5,.lp-h6,h6,.testimonials-slider-slider__author,.testimonials-l
31ist__author,.serif,[class*="__title"]{text-transform:none;letter-spacing:4px!important;font-weight:700;}div,p{letter-spacing:0!important;}.lp-text--pretitle,.lp-text--subtitle,.pretitle{letter-spacing:4px!important;font-size:14px!important;font-weight:700!important;}.lp-btn [class*="__content"]:after{content:none!important;}[class*="pre-title"],[class*="pretitle"]{padding-left:0!important;}[class*="pre-title"]:before,[class*="pretitle"]:before{content:none!important;}div.full-bleed-cta__pre-title--with-line{padding-left:0;}.lp-btn .lp-btn--filled__content,.lp-btn .lp-btn__content{padding-right:0;position:relative;white-space:nowrap;font-size:16px;}lp-pagination.pagination-container ul li a:hover:not(:disabled)::after,.pagination-container ul li a:hover:not(:disabled)::after{color:var(--white);}.lp-lightbox__close{border-color:var(--white);}.lp-lightbox__close:hover{background-color:#fff!important;}.lp-lightbox__close:hover:after,.lp-lightbox__close:hover:before{background-color:var(--red);}.lp-lightbox-arrow{background-color:var(--white);border-color:var(--white)!important;}.lp-lightbox-arrow:before{border-color:var(--red)!important;}.lp-lightbox-arrow:hover{background-color:transparent!important;border-color:var(--white)!important;}.lp-lightbox-arrow:hover:before{border-color:var(--white)!important;}.pac-container .pac-item:hover{background-color:var(--red);color:var(--white);}.pac-container .pac-item:hover .pac-item-query,.pac-container .pac-item:hover .pac-matched{color:var(--white);}div#global-mobile-contact .pop-up .lp-container,div#global-mobile-contact .contact-form{padding:0;}div#global-mobile-contact .lp-input{background:var(--gray);color:var(--black);}div#global-mobile-contact .lp-input:focus{background-color:var(--gray);}div#global-mobile-contact .lp-input:not(:placeholder-shown):invalid{border-color:var(--red);}div#global-mobile-contact .contact-form__container{padding:60px;border:none;}@media only screen and (max-width: 768px){div#global-mobile-contact .contact-form__container{padding:24px 24px 0 24px;}}div#global-mobile-contact .custom-select .custom-select-toggle:after{background-color:var(--black);}.floating-contact-button .custom-select .custom-select-toggle.is-placeholder{text-transform:none;}.instagram-feed .instagram-feed__title{font-size:2.5rem;}@media only screen and (min-device-width: 768px) and (max-device-width: 1024px){.instagram-feed .instagram-feed__title{font-size:2rem;}}@media only screen and (max-width: 767px){.instagram-feed .instagram-feed__title{font-size:1.25rem;}}.instagram-feed__title-group .instagram-feed__icon{background-color:var(--red);}.jsIGName{font-size:var(--global-h3-font-size);}@media (max-width: 768px){.jsIGName{font-size:22px;}}.simple-cta p{margin-bottom:0;}.custom-select .custom-select-content{background:var(--white);color:var(--black);}.custom-select .custom-select-content .custom-select-item:hover{color:var(--white);background:var(--red);}#modal-global-contact-us .contact-form-modal-form .lp-input{background:var(--gray);color:var(--black);}#modal-global-contact-us .contact-form-modal__close:after{-webkit-filter:brightness(0) saturate(100%) invert(14%) sepia(63%) saturate(396%) hue-rotate(151deg) brightness(89%) contrast(92%);filter:brightness(0) saturate(100%) invert(14%) sepia(63%) saturate(396%) hue-rotate(151deg) brightness(89%) contrast(92%);padding-top:50px;}@media (max-width: 768px){#modal-global-contact-us .contact-form-modal__close:after{-webkit-filter:brightness(100%) saturate(100%) invert(100%) sepia(0%) saturate(0%) hue-rotate(282deg) brightness(0%) contrast(101%)!important;filter:brightness(100%) saturate(100%) invert(100%) sepia(0%) saturate(0%) hue-rotate(282deg) brightness(0%) contrast(101%)!important;padding-top:50px;}}#modal-global-contact-us .contact-form-modal__close-container{position:absolute;right:50px;top:80px;}@media (max-width: 768px){#modal-global-contact-us .contact-form-modal__close-container{right:0px;top:20px;}}#modal-global-contact-us .lp-input:not(:placeholder-shown):invalid{border-color:var(--red);}.contact-form-modal .lp-a a:hover{text-decoration-color:var(--secondary);color:var(--secondary);}#global-navbar .header-container .header-navigation__link{text-transform:uppercase;letter-spacing:1px;}#global-navbar .header-container .header-navigation__link:after,#global-navbar .header-container .header-navigation__link:before{background-color:var(--white);}#global-navbar .header-container .header-navigation__sub-nav{background-color:#fff;color:var(--black);padding:0;}#global-navbar .header-container .header-navigation__sub-nav li:hover{background-color:var(--red);color:var(--white);}#global-navbar .header-container .header-navigation__sub-nav li:hover a{color:var(--white);}#global-navbar .header-container .header-navigation__sub-nav .header-navigation__sub-nav-link{color:var(--black);text-transform:uppercase;letter-spacing:1px;padding:1rem;}#global-navbar .header-container .header-search-btn:hover:after{background-color:var(--red);}#global-navbar .header-container .header-hamburger-btn:empty{background-color:transparent!important;border-color:var(--white);}#global-navbar .header-container .header-hamburger-btn:empty:before,#global-navbar .header-container .header-hamburger-btn:empty:after{background-color:var(--white);}#global-navbar .header-container .header-hamburger-btn:empty:hover{border-color:var(--white);background-color:var(--white)!important;}#global-navbar .header-container .header-hamburger-btn:empty:hover:before,#global-navbar .header-container .header-hamburger-btn:empty:hover:after{background-color:var(--red);}#global-navbar .header.scroll{background-color:#000!important;color:var(--white)!important;}#global-navbar .header-navigation__link{font-family:var(--global-primary-font-family);font-weight:600;text-transform:none;}#global-navbar .header-navigation__sub-nav-link{font-weight:600;text-transform:none;}@media (max-width: 768px){.header{background-color:#000!important;}}div#global-sidemenu .sidemenu-close{background-color:transparent!important;border-color:var(--red);}
31div#global-sidemenu .sidemenu-close::after{background-color:var(--red)!important;}div#global-sidemenu .sidemenu-close:hover{border-color:var(--red);background-color:var(--red)!important;}div#global-sidemenu .sidemenu-close:hover::after{background-color:var(--white)!important;}div#global-sidemenu .sidemenu-navigation__item{text-transform:none;white-space:nowrap;}div#global-sidemenu .sidemenu-navigation__link{font-size:18px;}div#global-sidemenu .sidemenu-navigation__link:hover,div#global-sidemenu .sidemenu-navigation-sub-nav__link:hover{color:var(--secondary);}div#global-sidemenu .sidemenu-navigation-sub-nav__link:hover{text-decoration-color:var(--secondary);}div#global-sidemenu .sidemenu__col-description a:hover{color:var(--secondary);text-decoration-color:var(--secondary);}div#global-sidemenu .sidemenu-footer:before{background:var(--accent);opacity:1;}@media (min-width: 1024px){div#global-sidemenu .sidemenu-nav-holder{display:-ms-grid;display:grid;-ms-grid-columns:1fr 1fr 1fr 1fr;grid-template-columns:1fr 1fr 1fr 1fr;}}div#global-sidemenu .lp-a .link,div#global-sidemenu .lp-a a{text-decoration-color:#192F3820;}div#global-sidemenu .lp-a .link:hover,div#global-sidemenu .lp-a a:hover{text-decoration-color:var(--secondary)!important;}div#global-sidemenu .sidemenu-navigation__link.sidemenu-navigation__link--active{color:var(--secondary);}#global-footer .link{color:var(--black);text-decoration-color:var(--gray)!important;display:inline-block;font-size:14px;font-weight:300;line-height:1.43;position:relative;-webkit-text-decoration:underline;text-decoration:underline;text-underline-offset:4px;}#global-footer .link:hover{color:var(--secondary);text-decoration-color:var(--secondary)!important;}@media(max-width:910px){footer#global-footer .footer__col-content{max-width:100%;}}footer#global-footer .footer-navigation__link{color:var(--black);text-transform:none;-webkit-text-decoration:underline;text-decoration:underline;text-decoration-color:var(--black);cursor:pointer;}footer#global-footer .footer-navigation__link:hover{color:var(--secondary);text-decoration-color:var(--secondary);}footer#global-footer .lp-a a{color:var(--black);text-decoration-color:var(--gray);}footer#global-footer .lp-a a:hover{color:var(--secondary);text-decoration-color:var(--secondary);}footer#global-footer .footer-contact-row,footer#global-footer .footer-info{border-color:var(--accent);}footer#global-footer .footer-logos-row .footer-logo{max-height:120px;max-width:368px;margin-bottom:-30px;}@media (max-width: 768px){footer#global-footer .footer-logos-row .footer-logo{margin-bottom:50px;}}@media (max-width: 767px){footer#global-footer .footer-logos-row .footer-logo .footer__section-content{display:block;}footer#global-footer .footer-logos-row .footer-logo .footer__col-content:last-of-type{margin-top:24px;}footer#global-footer .footer-logos-row .footer-logo ul.lp-socials{gap:24px;}}@media (max-width: 768px){footer#global-footer .footer__section-content{-webkit-flex-direction:column!important;-ms-flex-direction:column!important;flex-direction:column!important;}}@media(min-width:768px){.floating-contact-button__btn-holder,.floating-contact-button-pop-up{left:unset;right:70px;bottom:10px!important;}}.blog-post-intro__scroll-btn,.neighborhood-intro__scroll-btn,.agent-intro__scroll-btn,.custom-intro__scroll-btn{margin-top:20px;border-color:var(--white);background-color:transparent;}.blog-post-intro__scroll-btn:before,.neighborhood-intro__scroll-btn:before,.agent-intro__scroll-btn:before,.custom-intro__scroll-btn:before{background:var(--white);}.blog-post-intro__scroll-btn:hover,.neighborhood-intro__scroll-btn:hover,.agent-intro__scroll-btn:hover,.custom-intro__scroll-btn:hover{border-color:var(--white);background-color:var(--white);}.blog-post-intro__scroll-btn:hover:after,.neighborhood-intro__scroll-btn:hover:after,.agent-intro__scroll-btn:hover:after,.custom-intro__scroll-btn:hover:after{background:var(--red);}@media(max-width:910px){.text-grid .text-grid-scenes__scene--columns-3{--flexBasis:100%!important;}}@media(max-width:1024px) and (min-width:560px){.pagination-container{margin-top:40px!important;}}.cmpl-disclaimer p:nth-child(2) span strong{font-size:0px;}.cmpl-disclaimer p:nth-child(2) span strong::before{content:"CA DRE# 02022092";font-size:10pt;}div#global-mobile-contact .lp-input::-webkit-input-placeholder{text-transform:none;}div#global-mobile-contact .lp-input::-moz-placeholder{text-transform:none;}div#global-mobile-contact .lp-input:-ms-input-placeholder{text-transform:none;}div#global-mobile-contact .lp-input::placeholder{text-transform:none;}</style> 32 <style id="wow-hide-elements">.wow{visibility:hidden!important}</style> 33 <style id="page-css">
34 #section-cb4f0ad5-895f-4cf1-9273-df0dedffadbc{color:#fff;--fontColor:#fff;--fontColor_H:0;--fontColor_S:0%;--fontColor_L:100%;--fontColor_A:1;--fontColor_darkenDir:-1;--bgColor:#ffffff;--bgColor_H:0;--bgColor_S:0%;--bgColor_L:100%;--bgColor_A:1;--bgColor_darkenDir:-1;background-color:#ffffff;--gradient-color:var(--overlay-color, rgba(0, 0, 0, 0.4));background-image:linear-gradient(var(--gradient-color), var(--gradient-color)),url('https://media-production.lp-cdn.com/cdn-cgi/image/format=auto,quality=85,fit=scale-down,width=1280/https://media-production.lp-cdn.com/media/b7161822-46d1-44a3-9885-88a8fe56d2de');background-repeat:no-repeat;-webkit-background-position:center;background-position:center;-webkit-background-size:cover;background-size:cover;}#section-cb4f0ad5-895f-4cf1-9273-df0dedffadbc .lp-text--pretitle,#section-cb4f0ad5-895f-4cf1-9273-df0dedffadbc .lp-text--subtitle{margin-bottom:0;}@media (min-width: 960px) and (orientation: landscape){#section-cb4f0ad5-895f-4cf1-9273-df0dedffadbc{background-image:linear-gradient(var(--gradient-color), var(--gradient-color)),url('https://media-production.lp-cdn.com/cdn-cgi/image/format=auto,quality=85,fit=scale-down,width=1280/https://media-production.lp-cdn.com/media/b7161822-46d1-44a3-9885-88a8fe56d2de');}}@media (min-width: 1280px) and (orientation: landscape){#section-cb4f0ad5-895f-4cf1-9273-df0dedffadbc{background-image:linear-gradient(var(--gradient-color), var(--gradient-color)),url('https://media-production.lp-cdn.com/cdn-cgi/image/format=auto,quality=85/https://media-production.lp-cdn.com/media/b7161822-46d1-44a3-9885-88a8fe56d2de');}}@media (min-width: 1920px) and (orientation: landscape){#section-cb4f0ad5-895f-4cf1-9273-df0dedffadbc{background-image:linear-gradient(var(--gradient-color), var(--gradient-color)),url('https://media-production.lp-cdn.com/cdn-cgi/image/format=auto,quality=85/https://media-production.lp-cdn.com/media/b7161822-46d1-44a3-9885-88a8fe56d2de');}}@media (min-width: 2560px) and (orientation: landscape){#section-cb4f0ad5-895f-4cf1-9273-df0dedffadbc{background-image:linear-gradient(var(--gradient-color), var(--gradient-color)),url('https://media-production.lp-cdn.com/cdn-cgi/image/format=auto,quality=85/https://media-production.lp-cdn.com/media/b7161822-46d1-44a3-9885-88a8fe56d2de');}}#section-7b127eaf-712f-4e2f-a3ac-785e7ab56a45{color:#192F38;--fontColor:#192F38;--fontColor_H:197;--fontColor_S:38%;--fontColor_L:16%;--fontColor_A:1;--fontColor_darkenDir:1;--bgColor:#FFFFFF;--bgColor_H:0;--bgColor_S:0%;--bgColor_L:100%;--bgColor_A:1;--bgColor_darkenDir:-1;--section-padding-bottom:calc(2 / 4 * var(--global-section-padding));background-color:#FFFFFF;}#section-7b127eaf-712f-4e2f-a3ac-785e7ab56a45 h1{color:#192F38;}#section-7b127eaf-712f-4e2f-a3ac-785e7ab56a45 h2{color:#192F38;}#section-7b127eaf-712f-4e2f-a3ac-785e7ab56a45 h3{color:#192F38;}#section-7b127eaf-712f-4e2f-a3ac-785e7ab56a45 p{color:#192F38;}#section-7b127eaf-712f-4e2f-a3ac-785e7ab56a45>div:last-child{padding-bottom:calc(2 / 4 * var(--global-section-padding))!important;}#section-ba9ae458-99fa-408d-b931-ca44d53d9839{display:none;color:#192F38;--fontColor:#192F38;--fontColor_H:197;--fontColor_S:38%;--fontColor_L:16%;--fontColor_A:1;--fontColor_darkenDir:1;--bgColor:#ffffff;--bgColor_H:0;--bgColor_S:0%;--bgColor_L:100%;--bgColor_A:1;--bgColor_darkenDir:-1;--section-padding-bottom:calc(4 / 4 * var(--global-section-padding));background-color:#ffffff;}#section-ba9ae458-99fa-408d-b931-ca44d53d9839 .lp-input{background:var(--gray);}#section-ba9ae458-99fa-408d-b931-ca44d53d9839 .lp-input-icon:after{--iconColor:var(--accent);}#section-ba9ae458-99fa-408d-b931-ca44d53d9839 .properties-list-range__range .noUi-horizontal .noUi-connect{background-color:var(--accent);}#section-ba9ae458-99fa-408d-b931-ca44d53d9839 .properties-list-range__range .noUi-horizontal .noUi-handle{border:5px solid var(--white);box-shadow:0 0 0 1px var(--accent);}#section-ba9ae458-99fa-408d-b931-ca44d53d9839 .custom-select .custom-select-toggle:after{background-color:var(--red);}@media (max-width: 768px){#section-ba9ae458-99fa-408d-b931-ca44d53d9839 .lp-btn{background-color:var(--red);}#section-ba9ae458-99fa-408d-b931-ca44d53d9839 .properties-list__hide-btn{border-color:var(--red);color:var(--white);}}#section-ba9ae458-99fa-408d-b931-ca44d53d9839 .lp-tag{background-color:var(--red);color:var(--white);text-transform:uppercase;}#section-ba9ae458-99fa-408d-b931-ca44d53d9839>div:last-child{padding-bottom:calc(4 / 4 * var(--global-section-padding))!important;}#section-f3de70dc-17c3-43de-a8ed-1cf5c7484236{color:#192F38;--fontColor:#192F38;--fontColor_H:197;--fontColor_S:38%;--fontColor_L:16%;--fontColor_A:1;--fontColor_darkenDir:1;--bgColor:#ffffff;--bgColor_H:0;--bgColor_S:0%;--bgColor_L:100%;--bgColor_A:1;--bgColor_darkenDir:-1;background-color:#ffffff;--gradient-color:var(--overlay-color, rgba(25, 47, 56, 0.4));background-image:linear-gradient(var(--gradient-color), var(--gradient-color)),url('https://media-production.lp-cdn.com/cdn-cgi/image/format=auto,quality=85,fit=scale-down,width=1280/https://media-production.lp-cdn.com/media/3075991e-93d9-40a3-adbc-d109716ca241');background-repeat:no-repeat;-webkit-background-position:center;background-position:center;-webkit-background-size:cover;background-size:cover;}#section-f3de70dc-17c3-43de-a8ed-1cf5c7484236 .lp-input-icon::after{--iconColor:var(--accent);}#section-f3de70dc-17c3-43de-a8ed-1cf5c7484236 .lp-text--pretitle,#section-f3de70dc-17c3-43de-a8ed-1cf5c7484236 .lp-text--subtitle{color:var(--red);}#section-f3de70dc-17c3-43de-a8ed-1cf5c7484236 h2:before{border-color:var(--secondary);}#section-f3de70dc-17c3-43de-a8ed-1cf5c7484236 .lp-input:not(:placeholder-shown):invalid{border-color:var(--red);}#section-f3de70dc-17c3-43de-a8ed-1cf5c7484236 .lp-input:not(:placeholder-shown):invalid+.lp-input-icon:after{background-color:var(--red);}#section-f3de70dc-17c3-43de-a8ed-1cf5c7484236 .lp-input:not(:placeholder-shown){background-color:var(--gray);border-color:transparent;}#section-f3de70dc-17c3-43de-a8ed-1cf5c7484236 .newsletter-contact-cta__btn:before{display:none;}@media (min-width: 960px) and (orientation: landscape){#section-f3de70dc-17c3-43de-a8ed-1cf5c7484236{background-image:linear-gradient(var(--gradient-color), var(--gradient-color)),url('https://media-production.lp-cdn.com/cdn-cgi/image/format=auto,quality=85,fit=scale-down,width=1280/https://media-production.lp-cdn.com/media/3075991e-93d9-40a3-adbc-d109716ca241');}}@media (min-width: 1280px) and (orientation: landscape){#section-f3de70dc-17c3-43de-a8ed-1cf5c7484236{background-image:linear-gradient(var(--gradient-color), var(--gradient-color)),url('https://media-production.lp-cdn.com/cdn-cgi/image/format=auto,quality=85/https://media-production.lp-cdn.com/media/3075991e-93d9-40a3-adbc-d109716ca241');}}@media (min-width: 1920px) and (orientation: landscape){#section-f3de70dc-17c3-43de-a8ed-1cf5c7484236{background-image:linear-gradient(var(--gradient-color), var(--gradient-color)),url('https://media-production.lp-cdn.com/cdn-cgi/image/format=auto,quality=85/https://media-production.lp-cdn.com/media/3075991e-93d9-40a3-adbc-d109716ca241');}}@media (min-width: 2560px) and (orientation: landscape){#section-f3de70dc-17c3-43de-a8ed-1cf5c7484236{background-image:linear-gradient(var(--gradient-color), var(--gradient-color)),url('https://media-production.lp-cdn.com/cdn-cgi/image/format=auto,quality=85/https://media-production.lp-cdn.com/media/3075991e-93d9-40a3-adbc-d109716ca241');}}
34#section-9f328597-3b81-4ba9-bfda-00595329a24d{color:#1a1a1a;--fontColor:#1a1a1a;--fontColor_H:0;--fontColor_S:0%;--fontColor_L:10%;--fontColor_A:1;--fontColor_darkenDir:1;--bgColor:#ffffff;--bgColor_H:0;--bgColor_S:0%;--bgColor_L:100%;--bgColor_A:1;--bgColor_darkenDir:-1;background-color:#ffffff;}body>div.sidemenu{background-color:rgba(248, 248, 248, 1);color:rgba(25, 47, 56, 1);--fontColor:rgba(25, 47, 56, 1);--fontColor_H:197;--fontColor_S:38%;--fontColor_L:16%;--fontColor_A:1;--fontColor_darkenDir:1;--bgColor:rgba(248, 248, 248, 1);--bgColor_H:0;--bgColor_S:0%;--bgColor_L:97%;--bgColor_A:1;--bgColor_darkenDir:-1;}footer{background-color:rgba(255, 255, 255, 1);color:rgba(25, 47, 56, 1);--fontColor:rgba(25, 47, 56, 1);--fontColor_H:197;--fontColor_S:38%;--fontColor_L:16%;--fontColor_A:1;--fontColor_darkenDir:1;--bgColor:rgba(255, 255, 255, 1);--bgColor_H:0;--bgColor_S:0%;--bgColor_L:100%;--bgColor_A:1;--bgColor_darkenDir:-1;} 35 </style> 36 <style id="custom-style-css">.hyperlink-style-1{color:#192F38!important;}.hyperlink-style-1:hover{color:#1F5E73!important;}.button-style-1{color:#D40000!important;background-color:rgba(0, 0, 0, 0)!important;border-color:#D40000!important;}.button-style-1:hover{color:#FFFFFF!important;background-color:#D40000!important;border-color:#D40000!important;}.social_icons-style-1 a{border-color:#000000!important;background-color:rgba(0, 0, 0, 0)!important;color:#000000!important;}.social_icons-style-1 a:hover{border-color:#000000!important;background-color:#000000!important;color:#FFFFFF!important;}.social_icons-style-1 a i{color:inherit!important;}
36.social_icons-style-1 a svg,.social_icons-style-1 a svg path{fill:currentColor!important;}.pagination-style-1 li a{color:#D40000!important;background-color:rgba(0, 0, 0, 0)!important;border-color:rgba(0, 0, 0, 0)!important;}.pagination-style-1 li a:hover{color:#FFFFFF!important;background-color:#D40000!important;border-color:#D40000!important;}.pagination-style-1 li.active a{color:#FFFFFF!important;background-color:#D40000!important;border-color:#D40000!important;}.hyperlink-style-2{color:#FFFFFF!important;}.hyperlink-style-2:hover{color:#F8F8F8!important;}.pagination-style-2 li a{color:#FFFFFF!important;background-color:rgba(0, 0, 0, 0)!important;border-color:rgba(177, 132, 99, 0)!important;}.pagination-style-2 li a:hover{color:#D40000!important;background-color:#FFFFFF!important;border-color:#FFFFFF!important;}.pagination-style-2 li.active a{color:#D40000!important;background-color:#FFFFFF!important;border-color:#FFFFFF!important;}.social_icons-style-2 a{border-color:#FFFFFF!important;background-color:rgba(0, 0, 0, 0)!important;color:#FFFFFF!important;}.social_icons-style-2 a:hover{border-color:#FFFFFF!important;background-color:#FFFFFF!important;color:#D40000!important;}.social_icons-style-2 a i{color:inherit!important;}
36.social_icons-style-2 a svg,.social_icons-style-2 a svg path{fill:currentColor!important;}.button-style-2{color:#FFFFFF!important;background-color:rgba(0, 0, 0, 0)!important;border-color:#FFFFFF!important;}.button-style-2:hover{color:#D40000!important;background-color:#FFFFFF!important;border-color:#FFFFFF!important;}.hyperlink-style-3{color:#192F38!important;}.hyperlink-style-3:hover{color:#1F5E73!important;}</style> 37 <!-- LP Consent bootstrap: blocking; resolves consent state, pushes Consent Mode 38 defaults, and dynamically loads the locale + main (and banner if needed). --> 39
39<script src="https://cdn.luxurypresence.com/scripts/lp-consent/latest/lp-consent-boot.js" data-website-id="5c1357ff-b286-47e5-bde4-3b0c9cb734db"></script>
39 40 41 42
42<script> 43 (function utmParse() { 44 const url = new URL(window.location.href); 45 if (url.search) { 46 const utm = url.search.split(/\?|&/g).reduce((acc, current) => { 47 const matched = current.match(/utm_(content|campaign|medium|source|term|id)|gclid/g); 48 if (matched) { 49 acc[matched[0]] = current.replace(`${matched[0]}=`, ''); 50 } 51 return acc; 52 }, {}); 53 if (Object.keys(utm).length) { 54 window.localStorage.setItem('utm_vars', JSON.stringify(utm)); 55 } else { 56 // Remove old utm vars 57 window.localStorage.removeItem('utm_vars'); 58 } 59 } 60 // Set referrer in local storage 61 // This is meant to be stored once to avoid self-referrals and to have the first referrer only. 62 if (!window.localStorage.getItem('referrer')) { 63 // Capture the Referrer URL 64 const referrer = document.referrer; 65 if (referrer) { 66 // Only store the referrer if it comes from outside the current domain (to avoid self-referrals) 67 const currentDomain = window.location.hostname; 68 const referrerDomain = new URL(referrer).hostname; 69 70 if (referrerDomain !== currentDomain) { 71 // Store it in localStorage 72 localStorage.setItem('referrer', referrer); 73 } 74 } 75 } 76 })(); 77 </script>
77 78 <!-- Bing Webmaster Tools Verification -->
79<script> 80 // Check if on the homepage before the body loads 81 if (window.location.pathname === '/' || window.location.pathname === '/index.html') { 82 document.write('<meta name="msvalidate.01" content="FF55484A4BB02534A920E3081D2A91D0" />'); 83 } 84</script>
84 85 86<meta name="google-site-verification" content="DU7UqA4VatM7bfJWLUvOl3pSOOHTnNryt8sCgD0WT94" /> 87 <!-- GOOGLE TRANSLATE STYLES are moved to external CSS --> 88</head> 89<body> 90 <div id="google_translate_element" style="display: none"></div> 91 92 <nav id="global-navbar" data-el-id="3242b8f0-253e-46f7-b814-162ccf958ec2"> 93 94<header class="header js-header" style="--textColor: rgba(255, 255, 255, 1); --backgroundColor: rgba(0, 0, 0, 1);"> 95 <div class="header-container "> 96 97 <a href="/" class="header-logo"> 98 <img src="https://media-production.lp-cdn.com/cdn-cgi/image/format=auto,quality=85/https://media-production.lp-cdn.com/media/b5c1dd8a-0d56-41b4-a200-5c15cde75a15" srcset="https://media-production.lp-cdn.com/cdn-cgi/image/format=auto,quality=85,fit=scale-down,width=320/https://media-production.lp-cdn.com/media/b5c1dd8a-0d56-41b4-a200-5c15cde75a15 320w, https://media-production.lp-cdn.com/cdn-cgi/image/format=auto,quality=85/https://media-production.lp-cdn.com/media/b5c1dd8a-0d56-41b4-a2
9800-5c15cde75a15 943w" sizes="350px" alt="Company logo" class="header-logo__img header-logo__img--dark"> 99 <img src="https://media-production.lp-cdn.com/cdn-cgi/image/format=auto,quality=85/https://media-production.lp-cdn.com/media/7f01ebb5-7184-4190-9d13-9b2713c5afdb" srcset="https://media-production.lp-cdn.com/cdn-cgi/image/format=auto,quality=85,fit=scale-down,width=320/https://media-production.lp-cdn.com/media/7f01ebb5-7184-4190-9d13-9b2713c5afdb 320w, https://media-production.lp-cdn.com/cdn-cgi/image/format=auto,quality=85/https://media-production.lp-cdn.com/media/7f01ebb5-7184-4190-9d13-9b2713c5afdb 943w" sizes="350px" alt="Company logo" class="header-logo__img header-logo__img--light"> 100 </a> 101 102 <ul class="header-navigation"> 103 <li class="header-navigation__item header-navigation__item--sub-nav"> 104 105 <a class="header-navigation__link js-subnav-opener" style="--delay: 0s">Properties</a> 106 107 <ul class="header-navigation__sub-nav"> 108 <li class="header-navigation__sub-nav-item"> 109 <a class="header-navigation__sub-nav-link" href="/properties/sale" >Featured Properties</a> 110 </li> 111 <li class="header-navigation__sub-nav-item"> 112 <a class="header-navigation__sub-nav-link" href="/properties/sold" >Past Transactions</a> 113 </li> 114 </ul> 115 </li> 116 <li class="header-navigation__item "> 117 118 <a class="header-navigation__link " href="/our-markets" style="--delay: 0.1s">Our Markets</a> 119 </li> 120 <li class="header-navigation__item header-navigation__item--sub-nav"> 121 122 <a class="header-navigation__link js-subnav-opener" style="--delay: 0.2s">Home Search</a> 123 124 <ul class="header-navigation__sub-nav"> 125 <li class="header-navigation__sub-nav-item"> 126 <a class="header-navigation__sub-nav-link" href="/seattle" >Seattle </a> 127 </li> 128 <li class="header-navigation__sub-nav-item"> 129 <a class="header-navigation__sub-nav-link" href="/home-search/listings?listingStatus=%5B"ACTIVE"%2C"COMING_SOON"%5D&center=%7B"lat"%3A37.87863797096627%2C"lng"%3A-122.42033749132605%7D&zoom=9&boundary=%5B%5B%5B38.70425760481226%2C-123.99687557726357%5D%2C%5B38.70425760481226%2C-120.84379940538857%5D%2C%5B37.04365915444092%2C-120.84379940538857%5D%2C%5B37.04365915444092%2C-123.99687557726357%5D%2C%5B38.70425760481226%2C-123.99687557726357%5D%5D%5D&omnibox=Bay+Area%2C+CA%2C+USA" >Bay Area</a> 130 </li> 131 <li class="header-navigation__sub-nav-item"> 132 <a class="header-navigation__sub-nav-link" href="/home-search/listings?listingStatus=%5B"ACTIVE"%2C"COMING_SOON"%5D&center=%7B"lat"%3A34.16937206490837%2C"lng"%3A-117.78469346070369%7D&zoom=7&boundary=%5B%5B%5B37.57794093813595%2C-124.09084580445369%5D%2C%5B37.57794093813595%2C-111.4785411169537%5D%2C%5B30.617406839293242%2C-111.4785411169537%5D%2C%5B30.617406839293242%2C-124.09084580445369%5D%2C%5B37.57794093813595%2C-124.09084580445369%5D%5D%5D&omnibox=Southern+California%2C+CA%2C+USA" >Southern California</a> 133 </li> 134 <li class="header-navigation__sub-nav-item"> 135 <a class="header-navigation__sub-nav-link" href="/boston" >Boston</a> 136 </li> 137 <li class="header-navigation__sub-nav-item"> 138 <a class="header-navigation__sub-nav-link" href="/dallas" >Dallas</a> 139 </li> 140 </ul> 141 </li> 142 <li class="header-navigation__item "> 143 144 <a class="header-navigation__link " href="/home-valuation" style="--delay: 0.30000000000000004s">Home Valuation</a> 145 </li> 146 <li class="header-navigation__item "> 147 148 <a class="header-navigation__link " data-type="CONTACT_US" tabindex="0" style="--delay: 0.4s">Contact Us</a> 149 </li> 150 </ul> 151 152 153 <div class="header-buttons-wrapper"> 154 155 156 157 <button type="button" class="header-hamburger-btn lp-btn hamburger-component hamburger js-sidemenu-opener" 158 title="Toggle Sidemenu"></button> 159 </div> 160 </div> 161 162</header> 163 164 </nav> 165 <div id="global-sidemenu" class="sidemenu" data-el-id="033c3235-a8bd-45bf-a4a8-96977e33190d"> 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182<button class="sidemenu-close toggle close lp-btn lp-icon link-icon--plus"></button> 183 184 185<div class="sidemenu-wrapper " style="--navItemNumberColor: rgba(212, 0, 0, 1);"> 186 <div class="sidemenu-nav-holder "> 187 <ul class="sidemenu-navigation sidemenu-navigation--index"> 188 <li class="sidemenu-navigation__item js-idemenu-navigation-item "> 189 <a 190 href="/" 191 class="sidemenu-navigation__link">Home</a> 192 193 </li> 194 195 <li class="sidemenu-navigation__item js-idemenu-navigation-item "> 196 <a 197 href="/agents" 198 class="sidemenu-navigation__link">Meet Our Agents</a> 199 200 </li> 201 202 <li class="sidemenu-navigation__item js-idemenu-navigation-item sidemenu-navigation__item--sub-nav">
203 <span class="sidemenu-navigation__link sidemenu-navigation__link--sub-nav sidemenu-navigation__link--sub-nav-active js-sub-nav-opener lp-icon"> 204 Properties 205 </span> 206 207 <ul class="sidemenu-navigation-sub-nav lp-a"> 208 <li class="sidemenu-navigation-sub-nav__item"><a href="/properties/sale" class="sidemenu-navigation-sub-nav__link">Featured Properties</a></li> 209 <li class="sidemenu-navigation-sub-nav__item"><a href="/properties/sold" class="sidemenu-navigation-sub-nav__link">Past Transactions</a></li> 210 </ul> 211 </li> 212 213 </ul> 214 215 <ul class="sidemenu-navigation sidemenu-navigation--index"> 216 <li class="sidemenu-navigation__item js-idemenu-navigation-item sidemenu-navigation__item--sub-nav"> 217 <span class="sidemenu-navigation__link sidemenu-navigation__link--sub-nav sidemenu-navigation__link--sub-nav-active js-sub-nav-opener lp-icon"> 218 Home Search 219 </span> 220 221 <ul class="sidemenu-navigation-sub-nav lp-a"> 222 <li class="sidemenu-navigation-sub-nav__item"><a href="/seattle" class="sidemenu-navigation-sub-nav__link">Seattle</a></li> 223 <li class="sidemenu-navigation-sub-nav__item"><a href="/home-search/listings?listingStatus=%5B"ACTIVE"%2C"COMING_SOON"%5D&center=%7B"lat"%3A37.87863797096627%2C"lng"%3A-122.42033749132605%7D&zoom=9&boundary=%5B%5B%5B38.70425760481226%2C-123.99687557726357%5D%2C%5B38.70425760481226%2C-120.84379940538857%5D%2C%5B37.04365915444092%2C-120.84379940538857%5D%2C%5B37.04365915444092%2C-123.99687557726357%5D%2C%5B38.70425760481226%2C-123.99687557726357%5D%5D%5D&omnibox=Bay+Area%2C+CA%2C+USA" class="sidemenu-navigation-sub-nav__link">Bay Area</a></li> 224 <li class="sidemenu-navigation-sub-nav__item"><a href="/home-search/listings?listingStatus=%5B"ACTIVE"%2C"COMING_SOON"%5D&center=%7B"lat"%3A34.16937206490837%2C"lng"%3A-117.78469346070369%7D&zoom=7&boundary=%5B%5B%5B37.57794093813595%2C-124.09084580445369%5D%2C%5B37.57794093813595%2C-111.4785411169537%5D%2C%5B30.617406839293242%2C-111.4785411169537%5D%2C%5B30.617406839293242%2C-124.09084580445369%5D%2C%5B37.57794093813595%2C-124.09084580445369%5D%5D%5D&omnibox=Southern+California%2C+CA%2C+USA" class="sidemenu-navigation-sub-nav__link">Southern California</a></li> 225 <li class="sidemenu-navigation-sub-nav__item"><a href="/boston" class="sidemenu-navigation-sub-nav__link">Boston</a></li> 226 <li class="sidemenu-navigation-sub-nav__item"><a href="/dallas" class="sidemenu-navigation-sub-nav__link">Dallas</a></li> 227 </ul> 228 </li> 229 230 <li class="sidemenu-navigation__item js-idemenu-navigation-item "> 231 <a 232 href="/home-valuation" 233 class="sidemenu-navigation__link">Home Valuation</a> 234 235 </li> 236 237 <li class="sidemenu-navigation__item js-idemenu-navigation-item "> 238 <a 239 target="_blank" href="https://reali.hifello.com/lp/6a0c7d271e7e23d4e5cc3506" 240 class="sidemenu-navigation__link">Sell My Home</a> 241 242 </li> 243 244 </ul> 245 246 <ul class="sidemenu-navigation sidemenu-navigation--index"> 247 <li class="sidemenu-navigation__item js-idemenu-navigation-item "> 248 <a 249 target="_blank" href="https://reali.hifello.com/lp/6a0c7d3546539a058eb2ab4c" 250 class="sidemenu-navigation__link">Get Cash Offer</a> 251 252 </li> 253 254 <li class="sidemenu-navigation__item js-idemenu-navigation-item sidemenu-navigation__item--sub-nav">
255 <span class="sidemenu-navigation__link sidemenu-navigation__link--sub-nav sidemenu-navigation__link--sub-nav-active js-sub-nav-opener lp-icon"> 256 Real Estate Calculators 257 </span> 258 259 <ul class="sidemenu-navigation-sub-nav lp-a"> 260 <li class="sidemenu-navigation-sub-nav__item"><a href="/Home-Sale-Calculator" class="sidemenu-navigation-sub-nav__link">Home Sale Calculator</a></li> 261 <li class="sidemenu-navigation-sub-nav__item"><a href="/Mortgage-Calculator" class="sidemenu-navigation-sub-nav__link">Mortgage Calculator</a></li> 262 <li class="sidemenu-navigation-sub-nav__item"><a href="/Affordability-Calculator" class="sidemenu-navigation-sub-nav__link">Affordability Calculator</a></li> 263 </ul> 264 </li> 265 266 <li class="sidemenu-navigation__item js-idemenu-navigation-item "> 267 <a 268 href="/testimonials" 269 class="sidemenu-navigation__link">Testimonials</a> 270 271 </li> 272 273 </ul> 274 275 <ul class="sidemenu-navigation sidemenu-navigation--index"> 276 <li class="sidemenu-navigation__item js-idemenu-navigation-item "> 277 <a 278 href="/blog" 279 class="sidemenu-navigation__link">Blog</a> 280 281 </li> 282 283 <li class="sidemenu-navigation__item js-idemenu-navigation-item "> 284 <a 285 href="/recruitment" 286 class="sidemenu-navigation__link">Recruitment</a> 287 288 </li> 289 290 <li class="sidemenu-navigation__item js-idemenu-navigation-item sidemenu-navigation__item--sub-nav"> 291 <span class="sidemenu-navigation__link sidemenu-navigation__link--sub-nav sidemenu-navigation__link--sub-nav-active js-sub-nav-opener lp-icon"> 292 Financial Products 293 </span> 294 295 <ul class="sidemenu-navigation-sub-nav lp-a"> 296 <li class="sidemenu-navigation-sub-nav__item"><a href="/cash-offers" class="sidemenu-navigation-sub-nav__link">Cash Offers Buyers</a></li> 297 <li class="sidemenu-navigation-sub-nav__item"><a href="/buy-before-you-sell" class="sidemenu-navigation-sub-nav__link">Buy Before You Sell</a></li> 298 </ul> 299 </li> 300 301 </ul> 302 </div> 303 304 <div class="sidemenu-footer"> 305 <div class="sidemenu__info-group sidemenu__col"> 306 <div class="sidemenu__section-content"> 307 <div class="sidemenu__col-content"> 308 <p class="sidemenu__col-title">Contact</p><div class="sidemenu__col-description lp-a"> 309 <p class="sidemenu__col-agent-name">Reali | CA DRE #02022092</p> 310 <a href="tel:8558467334" class="sidemenu__col-phone">(855) 846-7334</a> 311 <a href="/cdn-cgi/l/email-protection#600c0903050e13090e07201205010c094e030f0d" class="sidemenu__col-email"><span class="__cf_email__" data-cfemail="dfb3b6bcbab1acb6b1b89fadbabeb3b6f1bcb0b2">[email protected]</span></a> 312 </div> 313 </div> 314 <div class="sidemenu__col-content"> 315 <p class="sidemenu__col-title">Office</p> <div class="sidemenu__col-description lp-a sidemenu__col-description--pre-line"><style type="text/css"><!--td {border: 1px solid #cccccc;}br {mso-data-placement:same-cell;}--></style><p><strong><span data-sheets-root="1">WA </span></strong><span data-sheets-root="1">- 39899 Balentine Dr, Suite 200, Newark, CA 94560 <br></span><strong><span data-sheets-root="1">CA </span></strong><span data-sheets-root="1">- 1420 Kettner Blvd, Suite 100, San Diego, CA 92101 <br></span><strong><span data-sheets-root="1">MA</span></strong><span data-sheets-root="1"> - 90 Canal Street, Boston, MA 02114 <br></span><strong><span data-sheets-root="1">TX </span></strong><span data-sheets-root="1">- 2626 Cole Ave, Suite 300, Dallas, TX 75204</span></p><p> </p></div> 316 317 </div> 318 </div> 319 320 </div> 321 322 <div class="sidemenu-socials-block"> 323 <p class="sidemenu__col-title">Follow Us</p> 324 <ul class="lp-socials social_icons-style-1"> 325 326 <li> 327 <a 328 href="https://www.linkedin.com/company/reali" 329 class="lp-socials__link" 330 data-wow-delay="0s" 331 target="_blank" 332 aria-label="LINKEDIN" 333 >
334 <span class="lp-i lp-i--linkedin" aria-hidden="true"></span> 335 </a> 336 </li> 337 </ul> </div> 338 </div> 339</div> 340 </div> 341 <div id="global-mobile-contact" class="mobile-contact-wrapper" data-el-id="4c08210d-8fa1-4e4b-ad13-3209994c2ceb"> 342 <div class="floating-contact-button"> 343 <div class="floating-contact-button__btn-holder js-btn-holder" 344 style="--backgroundColor: rgba(255, 255, 255, 1); --textColor: rgba(25, 47, 56, 1); --pillTextColor: rgba(255, 255, 255, 1); --pillBackgroundColor: rgba(0, 0, 0, 1);"> 345 <button type="button" class="floating-contact-button__btn-connect js-btn-connect lp-icon lp-ico-arrow-top">Contact Us</button> 346 </div> 347 348 <div class="floating-contact-button-pop-up js-pop-up" style="--backgroundColor: rgba(255, 255, 255, 1); --textColor: rgba(25, 47, 56, 1);"> 349 <form class="floating-contact-button-pop-up__form js-form" data-type="contact-form"> 350 <h3 class="floating-contact-button-pop-up__title">Leave a Message</h3> 351 352 <div class="floating-contact-button-pop-up__input-group lp-input-group"> 353 <input id="__FullName" type="text" name="name" 354 class="lp-input js-input-name lp-input--light" 355 required placeholder="Full Name"> 356 </div> 357 358 <div class="floating-contact-button-pop-up__input-group lp-input-group"> 359 <input id="__Email" type="email" name="email" 360 class="lp-input js-email-input " 361 required placeholder="Email"> 362 </div> 363 364 <div class="floating-contact-button-pop-up__input-group lp-input-group"> 365 <input class="lp-input " type="tel" placeholder="Phone" id="__Phone" 366 name="phone" required> 367 </div> 368 369 <select name="interest" id="-field-interest" 370 class="floating-contact-button-pop-up__input-group lp-input js-select " required> 371 <option value="" selected disabled data-label="Interest">Interested in...</option> 372 <option value="Selling & Buying">Selling & Buying</option> 373 <option value="Selling">Selling</option> 374 <option value="Buying">Buying</option> 375 <option value="Renting">Renting</option> 376 <option value="Other">Other</option> 377 <label for="-field-interest" class="lp-label sr-only">Interest</label> 378 </select> 379 380 <div class="floating-contact-button-pop-up__input-group lp-input-group"> 381 <textarea name="message" rows="4" id="__Message" class="lp-input " 382 placeholder="Message" required></textarea> 383 </div> 384 385 <div class="lp-disclaimer"> <label style='display: none;' for='-termsAccepted'>Opt In/Disclaimer Consent:</label> 386 <label class='lp-tcr-content'> 387 <span class='lp-tcr-checkbox'> 388 <input 389 type='checkbox' 390 id='-termsAccepted' 391 name='termsAccepted' 392 oninvalid="this.setCustomValidity('Please accept the terms and conditions')" 393 oninput="this.setCustomValidity('')" 394 required 395 /></span> 396 <span class='lp-tcr-message'> 397 398 I agree to be contacted by 399 Reali 400 via call, email, and text for real estate services. To opt out, you can reply 'stop' at any time or reply 'help' for assistance. 401 You can also click the unsubscribe link in the emails. Message and data rates may apply. Message frequency may vary. 402 <a href='/terms-and-conditions' target='_blank'>Privacy Policy</a>. 403 </span> 404 </label> 405 406</div> 407 408 <button type="submit" class="floating-contact-button-pop-up__btn-submit lp-btn lp-btn--mob-full-width button-style-1">Submit</button> 409 410 <div class="lp-h-pot"> 411 <input id="__newsSignMiddleName" name="middleName" value="" autocomplete="off" 412 tabindex="-1"> 413 </div> 414 <input style="display: none;" type="text" name="source" value="CONTACT_INQUIRY" /> 415 <input type="hidden" name="button_text" value="Contact Us"> 416 </form> 417 418 <div class="floating-contact-button-pop-up__success js-success"> 419 <h3 class="floating-contact-button-pop-up__title">Thank you for your message. We will be in touch with you shortly.</h3> 420 421 <a 422 href="/properties/sale" 423 424 class="lp-btn floating-contact-button-pop-up__success-btn button-style-1" 425 426 427 428 429 430 > 431 Explore Our Properties 432 </a> 433 </div> 434 <button type="button" class="floating-contact-button-pop-up__close js-close-btn lp-icon"></button> 435 </div> 436 437</div> 438 439 </div> 440 <section class="image-section is-font-color-light is-background-color-light" id="section-cb4f0ad5-895f-4cf1-9273-df0
440dedffadbc" data-el-id="d1637c2a-e69e-42d9-8d39-7fe5c68c7ee9"> 441 <div class="custom-intro js-scroll-section custom-intro__horizon-center custom-intro__vertical-bottom" 442 style=" 443 --titleColor: rgba(255, 255, 255, 1); 444 --boxBackColor: rgba(255, 255, 255, 0); 445 --backOverlayColor: rgba(255, 255, 255, 1); 446 " 447 > 448 <div class="custom-intro__container"> 449 <div class="custom-intro__content-wrapper"> 450 <div class="custom-intro__content-box"> 451 <div class="custom-intro__pre-title lp-text--pretitle">Proven Results</div> 452 <h1 class="custom-intro__title custom-intro__title--with-pretitle">Past Transactions</h1> 453 <div class="custom-intro__scroll-btn jsScrollBtn"></div> 454 </div> 455 </div> 456 </div> 457</div> 458 459</section> 460<section id="section-7b127eaf-712f-4e2f-a3ac-785e7ab56a45" class="solid-section is-font-color-dark is-background-color-light" data-el-id="d543005b-a163-4683-8264-831985860dd4">
vendor: 109 bytes, lines 460-461
460 461<script data-cfasync="false" src="/cdn-cgi/scripts/5c5dd728/cloudflare-static/email-decode.min.js"></script>
461<script type="text/x-handlebars-template" class="gridItem pagination-script"> <div class="mls-properties-with-map__grid-item"> 462 <a class="lp-listing-card mls-properties-with-map-card" 463 href="{{#if fromMLS}}/home-search/listings/{{id}}{{else}}/properties/{{slug}}{{/if}}" target="_self"> 464 465 <div class="mls-properties-with-map-card__img-wrap"> 466 {{{mediaImage media.[0] class='mls-properties-with-map-card__image' sizes='660px' alt="img"}}} 467 468 {{#if mlsAttribution}} 469 <div class="lp-mls-compliance__mls-attribution mls-properties-with-map-card__attribution">{{mlsAttribution}} 470 </div> 471 {{/if}} 472 {{{mediaImage mlsLogo class="lp-mls-compliance__mls-logo mls-properties-with-map-card__mls-logo" alt="MLS logo" 473 sizes="70px"}}} 474 475 </div> 476 477 <div class="mls-properties-with-map-card__info"> 478 <p class="mls-properties-with-map-card__price">{{formatPropertyPrice this 'short' separator = ' | ' 479 priceClass="sales" leaseClass="lease"}}</p> 480 481 {{#if (or bedroomCount bathCount livingSpaceSize)}} 482 <div class="mls-properties-with-map-card__attributes"> 483 {{#if bedroomCount}}<span class="mls-properties-with-map-card__attribute">{{bedroomCount}} bd</span>{{/if}} 484 {{#if bathCount}}<span class="mls-properties-with-map-card__attribute">{{bathCount}} ba</span>{{/if}} 485 {{#if livingSpaceSize}} 486 {{#numCompare livingSpaceSize "0" "!="}} 487 <span class="mls-properties-with-map-card__attribute">{{lpFormatNumber livingSpaceSize}} {{#numCompare 488 livingSpaceSize 0 ">"}}{{formatAreaUnit livingSpaceUnits}}</span>{{/numCompare}} 489 {{/numCompare}} 490 {{/if}} 491 {{#if status}}<span class="mls-properties-with-map-card__attribute">{{formatPropertyStatus status false (hbFormatDate soldDate)}}</span>{{/if}} 492 </div> 493 {{/if}} 494 495 <div class="mls-properties-with-map-card__address"> 496 {{#unless privateAddress}}{{fullAddress}}{{else}}Call for Address{{/unless}} 497 </div> 498 499 {{#if mlsId}} 500 <div class="mls-properties-with-map-card__mls-key">MLS®: {{mlsId}}</div> 501 {{/if}} 502 </div> 503 </a> 504 </div> 505</script>
505 506
506<script type="text/x-handlebars-template" class="js-card-template"> 507 <div class="mls-properties-with-map-map__listing-card"> 508 <a class="mls-properties-with-map-card listing-card--map " href="{{customSlug}}" target="_self"> 509 510 <div class="mls-properties-with-map-card__img-wrap"> 511 {{{mediaImage pic class='mls-properties-with-map-card__image' sizes='260px' alt="img"}}} 512 513 {{#if mlsAttribution}} 514 <div class="lp-mls-compliance__mls-attribution mls-properties-with-map-card__attribution">{{mlsAttribution}}</div> 515 {{/if}} 516 {{{mediaImage mlsLogo class="lp-mls-compliance__mls-logo mls-properties-with-map-card__mls-logo" alt="MLS logo" sizes="70px"}}} 517 518 </div> 519 520 <div class="listing-card__info mls-properties-with-map-card__info"> 521 <p class="listing-card__price mls-properties-with-map-card__price">{{formatPropertyPrice this 'short' separator = ' | ' priceClass="sales" leaseClass="lease"}}</p> 522 523 {{#if (or bedroomCount bathCount livingSpaceSize)}} 524 <div class="listing-card__attributes mls-properties-with-map-card__attributes"> 525 {{#numCompare bedroomCount "0" "!="}}<span class="mls-properties-with-map-card__attribute">{{bedroomCount}} bd</span>{{/numCompare}} 526 {{#numCompare bathCount "0" "!="}}<span class="mls-properties-with-map-card__attribute">{{bathCount}} ba</span>{{/numCompare}} 527 {{#if livingSpaceSize}} 528 {{#numCompare livingSpaceSize "0" "!="}} 529 <span class="mls-properties-with-map-card__attribute">{{lpFormatNumber livingSpaceSize}} {{#numCompare livingSpaceSize 0 ">"}}{{formatAreaUnit livingSpaceUnits}}</span>{{/numCompare}} 530 {{/numCompare}} 531 {{/if}} 532 {{#if status}}<span class="mls-properties-with-map-card__attribute">{{formatPropertyStatus status false (hbFormatDate soldDate)}}</span>{{/if}} 533 </div> 534 {{/if}} 535 536 <div class="listing-card__address mls-properties-with-map-card__address"> 537 {{#ifEquals privateAddress "false"}}{{fullAddress}}{{else}}Call for Address{{/ifEquals}} 538 </div> 539 540 {{#if (and fromMLS mlsId)}} 541 <div class="mls-properties-with-map-card__mls-key listing-card__mls-key" data-fromMlS="{{fromMLS}}">MLS®: {{mlsId}}</div> 542 {{/if}} 543 </div> 544 </a> 545 </div> 546 </script>
546 547 <div class="mls-properties-with-map lp-vertical-paddings js-grid" 548 data-variables="{"sort":"salesPrice","order":"salesPrice","sortDir":"DESC","statusIds":["5f528253-abb7-484e-95c3-330269ac1105","959c11cf-8655-4f91-874c-292b0ab7ea6b","a0012964-4f51-4430-abf8-6547c5ab6441","df04ccbe-4621-4140-a504-ee1a17430bb7","88b4ace6-f39b-4b25-a051-8f6dba976833","96031d77-bbe5-4de3-90d8-1e4e70de8ca8"],"leaseProperty":false,"advancedFilters":{"query":{"statusIds":{"in":["b0e88b38-7437-438d-a6fd-b13f135e1683"]}}},"backfillProviders":["bridge_sfar","cws_metrolist","cws_mlsl","mlsgrid_nwmls","rapattoni_bareis"],"displayMLSListings":"false","backfillMLSAgentIds":["01724366","01938680","MLSL487095","01975049","01970447","SYANLEAN","02091856","MLSL5078780","MLSL5106806","02134254","01893910","EB153620","02092814","MLSL5081020","02116845","02023493","MLSL5082676","01993720","MLSL5079382","MLSL5066011","02097370","01779414","MLSL5099111"],"hostname":"reali.com","websiteId":"5c1357ff-b286-47e5-bde4-3b0c9cb734db"}"> 549 <div class="mls-properties-with-map__container"> 550 551 552 553 <form class="mls-properties-with-map__filters search-container" data-fetch-properties="true" 554 aria-labelledby="filtersTitle"> 555 <input name="companyId" style="display:none" value="9994eaf9-a60f-4f64-89b4-cb9e3cc349b1" /> 556 <div id="filtersModal" class="mls-properties-with-map__filters-wrapper" aria-expanded="false"> 557 558 <div class="mls-properties-with-map__filters-grid" style="display: none;" > 559 <div class="mls-properties-with-map__filters-container"> 560 <div class="mls-properties-with-map__filters-content"> 561 <div 562 class="lp-input-group mls-properties-with-map__input-group mls-properties-with-map__input-group--search"> 563 <label class="lp-label mls-properties-with-map__label" for="search-input" style="display: none">Search 564 by Address, City, or Neighborhood</label> 565 <input type="text" class="lp-input mls-properties-with-map__input" placeholder="" id="search-input" 566 name="search-input"> 567 <button type="button" class="lp-input-reset mls-properties-with-map__input-reset js-input-reset" 568 aria-label="Clear search"> 569 </button> 570 </div> 571 <div 572 class="mls-properties-with-map-filters__grid-item mls-properties-with-map-filters__grid-switcher js-switcher js-switcher-lease-main" 573 data-name="lease-switcher"> 574 <button data-val="0" type="button" class="mls-properties-with-map-filters__grid-switcher-tab active">For 575 Sale</button> 576 <button data-val="1" type="button" class="mls-properties-with-map-filters__grid-switcher-tab">For 577 Rent</button> 578 </div> 579 <div class="mls-properties-with-map-filters__grid-item"> 580 <div class="mls-properties-with-map-filters__grid-price-range js-price-range"> 581 <button 582 class="mls-properties-with-map-filters__grid-btn lp-input is-placeholder js-price-range-cta"><span>Any 583 Price</span></button> 584 <div class="mls-properties-with-map-filters__grid-price-range-popup"> 585 <label class="mls-properties-with-map-filters__grid-price-range-label">Price Range</label> 586 <div class="mls-properties-with-map-filters__grid-price-wrapper"> 587 <div
588 class="mls-properties-with-map__group-input mls-properties-with-map-filters__grid-price-range-select" 589 data-select="price-min"> 590 <select id="price-min" name="price-min" class="lp-input js-select" data-name="select-price-min"> 591 <option value="">No Min</option> 592 <option value="300000">$300,000</option> 593 <option value="400000">$400,000</option> 594 <option value="500000">$500,000</option> 595 <option value="600000">$600,000</option> 596 <option value="700000">$700,000</option> 597 <option value="800000">$800,000</option> 598 <option value="900000">$900,000</option> 599 <option value="1000000">$1M</option> 600 <option value="1250000">$1.25M</option> 601 <option value="1500000">$1.5M</option> 602 <option value="1750000">$1.75M</option> 603 <option value="2000000">$2M</option> 604 <option value="2500000">$2.5M</option> 605 <option value="3000000">$3M</option> 606 <option value="4000000">$4M</option> 607 <option value="5000000">$5M</option> 608 <option value="6000000">$6M</option> 609 <option value="7000000">$7M</option> 610 <option value="8000000">$8M</option> 611 <option value="9000000">$9M</option> 612 <option value="10000000">$10M</option> 613 <option value="12000000">$12M</option> 614 <option value="15000000">$15M</option> 615 </select> 616 </div> 617 <span class="dash"> â </span> 618 <div 619 class="mls-properties-with-map__group-input mls-properties-with-map-filters__grid-price-range-select" 620 data-select="price-max"> 621 <select id="price-max" name="price-max" class="lp-input js-select" data-name="select-price-max"> 622 <option value="300000">$300,000</option> 623 <option value="400000">$400,000</option> 624 <option value="500000">$500,000</option> 625 <option value="600000">$600,000</option> 626 <option value="700000">$700,000</option> 627 <option value="800000">$800,000</option> 628 <option value="900000">$900,000</option> 629 <option value="1000000">$1M</option> 630 <option value="1250000">$1.25M</option> 631 <option value="1500000">$1.5M</option> 632 <option value="1750000">$1.75M</option> 633 <option value="2000000">$2M</option> 634 <option value="2500000">$2.5M</option> 635 <option value="3000000">$3M</option> 636 <option value="4000000">$4M</option> 637 <option value="5000000">$5M</option> 638 <option value="6000000">$6M</option> 639 <option value="7000000">$7M</option> 640 <option value="8000000">$8M</option> 641 <option value="9000000">$9M</option> 642 <option value="10000000">$10M</option> 643 <option value="12000000">$12M</option> 644 <option value="15000000">$15M</option> 645 <option value="" selected="selected">No Max</option> 646 </select> 647 </div> 648 </div> 649 </div> 650 </div> 651 </div> 652 <div
653 class="mls-properties-with-map-filters__grid-item mls-properties-with-map-filters__grid-item--bedrooms"> 654 <div class="mls-properties-with-map__group-input" data-select="bedrooms" data-select="bedrooms"> 655 <select id="bedrooms" name="bedrooms" class="lp-input js-select"> 656 <option value="">Beds</option> 657 <option value="1">1+ Beds</option> 658 <option value="2">2+ Beds</option> 659 <option value="3">3+ Beds</option> 660 <option value="4">4+ Beds</option> 661 <option value="5">5+ Beds</option> 662 </select> 663 </div> 664 </div> 665 <div 666 class="mls-properties-with-map-filters__grid-item mls-properties-with-map-filters__grid-item--bathrooms"> 667 <div class="mls-properties-with-map__group-input" data-select="bathrooms"> 668 <select id="bathrooms" name="bathrooms" class="lp-input js-select" data-name="select-bathrooms"> 669 <option value="">Baths</option> 670 <option value="1">1+ Baths</option> 671 <option value="2">2+ Baths</option> 672 <option value="3">3+ Baths</option> 673 <option value="4">4+ Baths</option> 674 <option value="5">5+ Baths</option> 675 </select> 676 </div> 677 </div> 678 <div 679 class="mls-properties-with-map-filters__grid-item mls-properties-with-map-filters__grid-item--property-type"> 680 <div class="mls-properties-with-map__group-input" data-select="property-type"> 681 <select id="property" name="propertyType" data-name="propertyType" 682 class="lp-input mls-properties-with-map-filters__property-select js-property-types-controls hide js-select" 683 multiple> 684 <option value="">Any Property Type</option> 685 <option value="9a70fec3-5946-4d04-9acb-74d98781f636">Residential</option> 686 <option value="c68279bc-2904-4ab8-9e84-42ecf6af008c">Townhouse</option> 687 <option value="daf8aaa2-8b6f-4f2d-a324-754e0316d2b6">Condo</option> 688 <option value="c5a58e1c-ca87-4546-b7f8-9330fb90badd">Commercial</option> 689 <option value="9ac67b4b-bf76-4e0d-999e-701908f8f9ab">Multi-Family</option> 690 <option value="2c63593e-4869-41f8-bc78-03fa80e24580">Land</option> 691 <option value="dea9daeb-4eb9-4bd8-8380-1a5c9384e817">Co-op</option> 692 <option value="d6b17d8b-a7a2-4de5-95c0-c2783b26de7a">Manufactured</option> 693 <option value="c075eebc-850c-479d-a79d-90499bae0d87">Other</option> 694 </select> 695 </div> 696 </div> 697 <button 698 class="mls-properties-with-map-filters__grid-item mls-properties-with-map-filters__grid-btn more-filters js-modal-filters">More 699 Filters</button> 700 </div> 701 <div class="mls-properties-with-map-filters__sort "> 702 <label class="mls-properties-with-map-filters__sort-label">Sort By:</label> 703 <div class="mls-properties-with-map__group-input" data-select="sort-price"> 704 <select id="sort-price" name="sort-price" class="lp-input js-select" data-name="select-sort-price"> 705 <option value="DESC" selected>Highest price</option> 706 <option value="ASC">Lowest price</option> 707 </select> 708 </div> 709 </div> 710 </div> 711 <div class="mls-properties-with-map-modal__container js-modal"> 712 <div class="mls-properties-with-map-modal__modal"> 713 <div class="mls-properties-with-map-modal__close-container"> 714 <button class="lp-close mls-properties-with-ma
714p-modal__close js-close"><span 715 class="lp-close-icon"></span></button> 716 </div> 717 <div class="mls-properties-with-map-modal__items"> 718 <div class="lp-input-group mls-properties-with-map__input-group"> 719 <label class="lp-label" for="modal-search-input" style="display: none">Search by Address, City, or 720 Neighborhood</label> 721 <input type="text" class="lp-input mls-properties-with-map__modal-input filters__modal-input" 722 placeholder="" id="modal-search-input" name="search-input"> 723 </div> 724 <div class="mls-properties-with-map-modal__item"> 725 <div class="mls-properties-with-map-filters__grid-switcher js-switcher" data-name="lease-switcher"> 726 <button data-val="0" type="button" 727 class="mls-properties-with-map-filters__grid-switcher-tab active">For Sale</button> 728 <button data-val="1" type="button" class="mls-properties-with-map-filters__grid-switcher-tab">For 729 Rent</button> 730 </div> 731 </div> 732 <div 733 class="mls-properties-with-map-modal__item mls-properties-with-map-modal__price-container mls-properties-with-map-filters__grid-price-range-popup js-price-range"> 734 <label class="mls-properties-with-map-filters__grid-price-range-label">Price Range</label> 735 <div class="mls-properties-with-map-filters__grid-price-wrapper mls-properties-with-map-modal__price"> 736 <div 737 class="mls-properties-with-map__group-input mls-properties-with-map-filters__grid-price-range-select mls-properties-with-map-modal__select" 738 data-select="price-min"> 739 <select id="modal-price-min" name="price-min" class="lp-input js-select"> 740 <option value="">No Min</option> 741 <option value="300000">$300,000</option> 742 <option value="400000">$400,000</option> 743 <option value="500000">$500,000</option> 744 <option value="600000">$600,000</option> 745 <option value="700000">$700,000</option> 746 <option value="800000">$800,000</option> 747 <option value="900000">$900,000</option> 748 <option value="1000000">$1M</option> 749 <option value="1250000">$1.25M</option> 750 <option value="1500000">$1.5M</option> 751 <option value="1750000">$1.75M</option> 752 <option value="2000000">$2M</option> 753 <option value="2500000">$2.5M</option> 754 <option value="3000000">$3M</option> 755 <option value="4000000">$4M</option> 756 <option value="5000000">$5M</option> 757 <option value="6000000">$6M</option> 758 <option value="7000000">$7M</option> 759 <option value="8000000">$8M</option> 760 <option value="9000000">$9M</option> 761 <option value="10000000">$10M</option> 762 <option value="12000000">$12M</option> 763 <option value="15000000">$15M</option> 764 </select> 765 </div> 766 <span class="dash"> â </span> 767 <div 768 class="mls-properties-with-map__group-input mls-properties-with-map-filters__grid-price-range-select mls-properties-with-map-modal__select" 769 data-select="price-max"> 770 <select id="modal-price-max" name="price-max" class="lp-input js-select"> 771 <option value="300000">$300,000</option> 772 <option value="400000">$400,000</option> 773 <option value="500000">$500,000</option> 774 <option value="600000">$600,000</option> 775 <option value="700000">$700,000</option> 776 <option value="800000">$800,000</option> 777 <option value="900000">$900,000</option> 778 <option value="1000000">$1M</option> 779 <option value="1250000">$1.25M</option> 780 <option value="1500000">$1.5M</option> 781 <option value="1750000">$1.75M</option> 782 <option value="2000000">$2M</option> 783 <option value="2500000">$2.5M</option> 784 <option value="3000000">$3M</option> 785 <option value="4000000">$4M</option> 786 <option value="5000000">$5M</option> 787 <option value="6000000">$6M</option> 788 <option value="7000000">$7M</option> 789 <option value="8000000">$8M</option> 790 <option value="9000000">$9M</option> 791 <option value="10000000">$10M</option> 792 <option value="12000000">$12M</option> 793 <option value="15000000">$15M</option> 794 <option value="" selected="selected">No Max</option> 795 </select> 796 </div> 797 </div>
798 <div class="mls-properties-with-map-modal__row"> 799 <div class="mls-properties-with-map-modal__row-item"> 800 <div class="mls-properties-with-map__group-input " data-select="bedrooms"> 801 <label for="modal-bedrooms">Beds</label> 802 <select id="modal-bedrooms" name="bedrooms" class="lp-input js-select"> 803 <option value="">Beds</option> 804 <option value="1">1+ Beds</option> 805 <option value="2">2+ Beds</option> 806 <option value="3">3+ Beds</option> 807 <option value="4">4+ Beds</option> 808 <option value="5">5+ Beds</option> 809 </select> 810 </div> 811 </div> 812 <div class="mls-properties-with-map-modal__row-item"> 813 <div class="mls-properties-with-map__group-input " data-select="bathrooms"> 814 <label for="modal-bathrooms">Baths</label> 815 <select id="modal-bathrooms" name="bathrooms" class="lp-input js-select"> 816 <option value="">Baths</option> 817 <option value="1">1+ Baths</option> 818 <option value="2">2+ Baths</option> 819 <option value="3">3+ Baths</option> 820 <option value="4">4+ Baths</option> 821 <option value="5">5+ Baths</option> 822 </select> 823 </div> 824 </div> 825 </div> 826 </div> 827 828 <div class="mls-properties-with-map-modal__item"> 829 <label class="mls-properties-with-map-filters__grid-price-range-label">Property Type</label> 830 <div class="mls-properties-with-map-modal__checkbox-container"> 831 <div class="lp-input-group mls-properties-with-map__input-group"> 832 <input id="c5a58e1c-ca87-4546-b7f8-9330fb90badd" 833 class="mls-properties-with-map-modal__input-checkbox lp-visually-hidden js-property-type-checkbox js-property-types-controls" 834 type="checkbox" value="c5a58e1c-ca87-4546-b7f8-9330fb90badd" /> 835 <div class="mls-properties-with-map-modal__checkbox js-fake-checkbox"></div> 836 <label for="c5a58e1c-ca87-4546-b7f8-9330fb90badd">Commercial</label> 837 </div> 838 <div class="lp-input-group mls-properties-with-map__input-group"> 839 <input id="9a70fec3-5946-4d04-9acb-74d98781f636" 840 class="mls-properties-with-map-modal__input-checkbox lp-visually-hidden js-property-type-checkbox js-property-types-controls" 841 type="checkbox" value="9a70fec3-5946-4d04-9acb-74d98781f636" /> 842 <div class="mls-properties-with-map-modal__checkbox js-fake-checkbox"></div> 843 <label for="9a70fec3-5946-4d04-9acb-74d98781f636">Residential</label> 844 </div> 845 <div class="lp-input-group mls-properties-with-map__input-group"> 846 <input id="9ac67b4b-bf76-4e0d-999e-701908f8f9ab" 847 class="mls-properties-with-map-modal__input-checkbox lp-visually-hidden js-property-type-checkbox js-property-types-controls" 848 type="checkbox" value="9ac67b4b-bf76-4e0d-999e-701908f8f9ab" /> 849 <div class="mls-properties-with-map-modal__checkbox js-fake-checkbox"></div> 850 <label for="9ac67b4b-bf76-4e0d-999e-701908f8f9ab">Multi-Family</label> 851 </div> 852 <div class="lp-input-group mls-properties-with-map__input-group"> 853 <input id="dea9daeb-4eb9-4bd8-8380-1a5c9384e817"
854 class="mls-properties-with-map-modal__input-checkbox lp-visually-hidden js-property-type-checkbox js-property-types-controls" 855 type="checkbox" value="dea9daeb-4eb9-4bd8-8380-1a5c9384e817" /> 856 <div class="mls-properties-with-map-modal__checkbox js-fake-checkbox"></div> 857 <label for="dea9daeb-4eb9-4bd8-8380-1a5c9384e817">Co-op</label> 858 </div> 859 <div class="lp-input-group mls-properties-with-map__input-group"> 860 <input id="daf8aaa2-8b6f-4f2d-a324-754e0316d2b6" 861 class="mls-properties-with-map-modal__input-checkbox lp-visually-hidden js-property-type-checkbox js-property-types-controls" 862 type="checkbox" value="daf8aaa2-8b6f-4f2d-a324-754e0316d2b6" /> 863 <div class="mls-properties-with-map-modal__checkbox js-fake-checkbox"></div> 864 <label for="daf8aaa2-8b6f-4f2d-a324-754e0316d2b6">Condo</label> 865 </div> 866 <div class="lp-input-group mls-properties-with-map__input-group"> 867 <input id="c68279bc-2904-4ab8-9e84-42ecf6af008c" 868 class="mls-properties-with-map-modal__input-checkbox lp-visually-hidden js-property-type-checkbox js-property-types-controls" 869 type="checkbox" value="c68279bc-2904-4ab8-9e84-42ecf6af008c" /> 870 <div class="mls-properties-with-map-modal__checkbox js-fake-checkbox"></div> 871 <label for="c68279bc-2904-4ab8-9e84-42ecf6af008c">Town House</label> 872 </div> 873 <div class="lp-input-group mls-properties-with-map__input-group"> 874 <input id="d6b17d8b-a7a2-4de5-95c0-c2783b26de7a" 875 class="mls-properties-with-map-modal__input-checkbox lp-visually-hidden js-property-type-checkbox js-property-types-controls" 876 type="checkbox" value="d6b17d8b-a7a2-4de5-95c0-c2783b26de7a" /> 877 <div class="mls-properties-with-map-modal__checkbox js-fake-checkbox"></div> 878 <label for="d6b17d8b-a7a2-4de5-95c0-c2783b26de7a">Manufactured</label> 879 </div> 880 <div class="lp-input-group mls-properties-with-map__input-group"> 881 <input id="2c63593e-4869-41f8-bc78-03fa80e24580" 882 class="mls-properties-with-map-modal__input-checkbox lp-visually-hidden js-property-type-checkbox js-property-types-controls" 883 type="checkbox" value="2c63593e-4869-41f8-bc78-03fa80e24580" /> 884 <div class="mls-properties-with-map-modal__checkbox js-fake-checkbox"></div> 885 <label for="2c63593e-4869-41f8-bc78-03fa80e24580">Land</label> 886 </div> 887 <div class="lp-input-group mls-properties-with-map__input-group"> 888 <input id="c075eebc-850c-479d-a79d-90499bae0d87" 889 class="mls-properties-with-map-modal__input-checkbox lp-visually-hidden js-property-type-checkbox js-property-types-controls" 890 type="checkbox" value="c075eebc-850c-479d-a79d-90499bae0d87" /> 891 <div class="mls-properties-with-map-modal__checkbox js-fake-checkbox"></div> 892 <label for="c075eebc-850c-479d-a79d-90499bae0d87">Other</label> 893 </div> 894 </div> 895 </div> 896 <div class="mls-properties-with-map-modal__item"> 897 <label class="mls-properties-with-map-filters__grid-price-range-label">Square Footage</label> 898 <div class="mls-properties-with-map-filters__grid-price-wrapper"> 899 <div 900 class="mls-properties-with-map__group-input mls-properties-with-map-filters__grid-price-range-select mls-properties-with-map-modal__select" 901 data-select="sqft-min"> 902 <select id="modal-sqft-min" name="sqft-min" class="lp-in
902put js-select"> 903 <option value="">No Min</option> 904 <option value="0">0</option> 905 <option value="2000">2,000 sq.ft.</option> 906 <option value="4000">4,000 sq.ft.</option> 907 <option value="6000">6,000 sq.ft.</option> 908 <option value="8000">8,000 sq.ft.</option> 909 <option value="10000">10,000 sq.ft.</option> 910 <option value="12000">12,000 sq.ft.</option> 911 <option value="14000">14,000 sq.ft.</option> 912 <option value="16000">16,000 sq.ft.</option> 913 <option value="18000">18,000 sq.ft.</option> 914 <option value="20000">20,000 sq.ft.</option> 915 </select> 916 </div> 917 <span class="filters__select-dash"> â </span> 918 <div 919 class="mls-properties-with-map__group-input mls-properties-with-map-filters__grid-price-range-select mls-properties-with-map-modal__select" 920 data-select="sqft-max"> 921 <select id="modal-sqft-max" name="sqft-max" class="lp-input js-select"> 922 <option value="0">0</option> 923 <option value="2000">2,000 sq.ft.</option> 924 <option value="4000">4,000 sq.ft.</option> 925 <option value="6000">6,000 sq.ft.</option> 926 <option value="8000">8,000 sq.ft.</option> 927 <option value="10000">10,000 sq.ft.</option> 928 <option value="12000">12,000 sq.ft.</option> 929 <option value="14000">14,000 sq.ft.</option> 930 <option value="16000">16,000 sq.ft.</option> 931 <option value="18000">18,000 sq.ft.</option> 932 <option value="20000">20,000 sq.ft.</option> 933 <option value="" selected="selected">No Max</option> 934 </select> 935 </div> 936 </div> 937 </div> 938 <div class="mls-properties-with-map-modal__item"> 939 <label class="mls-properties-with-map-filters__grid-price-range-label">Status</label> 940 <div class="mls-properties-with-map-modal__checkbox-container"> 941 <div class="lp-input-group mls-properties-with-map__input-group"> 942 <input id="5f528253-abb7-484e-95c3-330269ac1105" 943 class="mls-properties-with-map-modal__input-checkbox lp-visually-hidden js-status-checkbox" 944 type="checkbox" value="5f528253-abb7-484e-95c3-330269ac1105" /> 945 <div class="mls-properties-with-map-modal__checkbox js-fake-checkbox"></div> 946 <label for="5f528253-abb7-484e-95c3-330269ac1105">Active</label> 947 </div> 948 <div class="lp-input-group mls-properties-with-map__input-group"> 949 <input id="a0012964-4f51-4430-abf8-6547c5ab6441" 950 class="mls-properties-with-map-modal__input-checkbox lp-visually-hidden js-status-checkbox" 951 type="checkbox" value="a0012964-4f51-4430-abf8-6547c5ab6441" /> 952 <div class="mls-properties-with-map-modal__checkbox js-fake-checkbox"></div> 953 <label for="a0012964-4f51-4430-abf8-6547c5ab6441">Under Contract</label> 954 </div> 955 <div class="lp-input-group mls-properties-with-map__input-group"> 956 <input id="959c11cf-8655-4f91-874c-292b0ab7ea6b" 957 class="mls-properties-with-map-modal__input-checkbox lp-visually-hidden js-status-checkbox" 958 type="checkbox" value="959c11cf-8655-4f91-874c-292b0ab7ea6b" />
959 <div class="mls-properties-with-map-modal__checkbox js-fake-checkbox"></div> 960 <label for="959c11cf-8655-4f91-874c-292b0ab7ea6b">Pending</label> 961 </div> 962 </div> 963 </div> 964 <div class="mls-properties-with-map-modal__item"> 965 <div class="mls-properties-with-map-modal__row"> 966 <label for="">Show Open Houses Only</label> 967 <div class="mls-properties-with-map-filters__grid-switcher js-switcher js-switcher-compact" 968 data-type="compact-switcher"> 969 <button data-val="0" type="button" 970 class="mls-properties-with-map-filters__grid-switcher-tab active"></button> 971 <button data-val="1" type="button" 972 class="mls-properties-with-map-filters__grid-switcher-tab"></button> 973 </div> 974 </div> 975 </div> 976 </div> 977 <div class="mls-properties-with-map-modal__buttons-sticky"> 978 <div class="mls-properties-with-map-modal__row mls-properties-with-map-modal__buttons"> 979 <button class="lp-btn mls-properties-with-map-modal__reset-button js-reset-filters">Reset all 980 filters</button> 981 <button class="lp-btn mls-properties-with-map-modal__close-modal js-close button-style-1">View 982 Properties</button> 983 </div> 984 </div> 985 </div> 986 </div> 987 988 </div> 989 </form> 990 991 <div 992 class="mls-properties-with-map__grid-container data-container"> 993 <div class="mls-properties-with-map__grid-item"> 994 <a class="lp-listing-card mls-properties-with-map-card" 995 href="/properties/2148-pine-street-san-francisco-ca-us-94115-ml82043326" target="_self"> 996 997 <div class="mls-properties-with-map-card__img-wrap"> 998 <img src="https://dlajgvw9htjpb.cloudfront.net/cms/9994eaf9-a60f-4f64-89b4-cb9e3cc349b1/ML82043326/-33160504788850245.jpg" alt="img" class="mls-properties-with-map-card__image" loading="lazy"> 999 1000 <div class="lp-mls-compliance__mls-attribution mls-properties-with-map-card__attribution"> 1001 </div> 1002 <img src="https://res.cloudinary.com/luxuryp/images/f_auto,q_auto/ygkifjyaoikbgxbka6md/mlsl_logo_85x11-300x174" alt="MLS logo" class="lp-mls-compliance__mls-logo mls-properties-with-map-card__mls-logo" loading="lazy"> 1003 1004 </div> 1005 1006 <div class="mls-properties-with-map-card__info"> 1007 <p class="mls-properties-with-map-card__price"><span class="sales">$4,795,000</span></p> 1008 1009 <div class="mls-properties-with-map-card__attributes"> 1010 <span class="mls-properties-with-map-card__attribute">4 bd</span> 1011 <span class="mls-properties-with-map-card__attribute">4 ba</span> 1012 <span class="mls-properties-with-map-card__attribute">3,539 Sq.Ft.</span> 1013 <span class="mls-properties-with-map-card__attribute">Sold</span> 1014 </div> 1015 1016 <div class="mls-properties-with-map-card__address"> 1017 2148 Pine ST, San Francisco, CA 94115 1018 </div> 1019 1020 <div class="mls-properties-with-map-card__mls-key">MLS®: ML82043326</div> 1021 </div> 1022 </a> 1023 </div> 1024 <div class="mls-properties-with-map__grid-item"> 1025 <a class="lp-listing-card mls-properties-with-map-card" 1026 href="/properties/2404-hillside-drive-burlingame-ca-us-94010-ml82012637" target="_self"> 1027 1028 <div class="mls-properties-with-map-card__img-wrap"> 1029 <img src="https://dlajgvw9htjpb.cloudfront.net/cms/9994eaf9-a60f-4f64-89b4-cb9e3cc349b1/ML82012637/6916827140140501019.jpg" alt="img" class="mls-properties-with-ma
1029p-card__image" loading="lazy"> 1030 1031 <div class="lp-mls-compliance__mls-attribution mls-properties-with-map-card__attribution"> 1032 </div> 1033 <img src="https://res.cloudinary.com/luxuryp/images/f_auto,q_auto/ygkifjyaoikbgxbka6md/mlsl_logo_85x11-300x174" alt="MLS logo" class="lp-mls-compliance__mls-logo mls-properties-with-map-card__mls-logo" loading="lazy"> 1034 1035 </div> 1036 1037 <div class="mls-properties-with-map-card__info"> 1038 <p class="mls-properties-with-map-card__price"><span class="sales">$3,807,500</span></p> 1039 1040 <div class="mls-properties-with-map-card__attributes"> 1041 <span class="mls-properties-with-map-card__attribute">4 bd</span> 1042 <span class="mls-properties-with-map-card__attribute">4 ba</span> 1043 <span class="mls-properties-with-map-card__attribute">2,990 Sq.Ft.</span> 1044 <span class="mls-properties-with-map-card__attribute">Sold</span> 1045 </div> 1046 1047 <div class="mls-properties-with-map-card__address"> 1048 2404 Hillside DR, Burlingame, CA 94010 1049 </div> 1050 1051 <div class="mls-properties-with-map-card__mls-key">MLS®: ML82012637</div> 1052 </div> 1053 </a> 1054 </div> 1055 <div class="mls-properties-with-map__grid-item"> 1056 <a class="lp-listing-card mls-properties-with-map-card" 1057 href="/properties/6686-mount-holly-dr-san-jose-ca-us-95120-ml81987128" target="_self"> 1058 1059 <div class="mls-properties-with-map-card__img-wrap"> 1060 <img src="https://dlajgvw9htjpb.cloudfront.net/cms/9994eaf9-a60f-4f64-89b4-cb9e3cc349b1/ML81987128/4782704197781283678.jpg" alt="img" class="mls-properties-with-map-card__image" loading="lazy"> 1061 1062 <div class="lp-mls-compliance__mls-attribution mls-properties-with-map-card__attribution"> 1063 </div> 1064 <img src="https://res.cloudinary.com/luxuryp/images/f_auto,q_auto/ygkifjyaoikbgxbka6md/mlsl_logo_85x11-300x174" alt="MLS logo" class="lp-mls-compliance__mls-logo mls-properties-with-map-card__mls-logo" loading="lazy"> 1065 1066 </div> 1067 1068 <div class="mls-properties-with-map-card__info"> 1069 <p class="mls-properties-with-map-card__price"><span class="sales">$2,335,000</span></p> 1070 1071 <div class="mls-properties-with-map-card__attributes"> 1072 <span class="mls-properties-with-map-card__attribute">4 bd</span> 1073 <span class="mls-properties-with-map-card__attribute">2 ba</span> 1074 <span class="mls-properties-with-map-card__attribute">1,999 Sq.Ft.</span> 1075 <span class="mls-properties-with-map-card__attribute">Sold</span> 1076 </div> 1077 1078 <div class="mls-properties-with-map-card__address"> 1079 6686 Mount Holly DR, San Jose, CA 95120 1080 </div> 1081 1082 <div class="mls-properties-with-map-card__mls-key">MLS®: ML81987128</div> 1083 </div> 1084 </a> 1085 </div> 1086 <div class="mls-properties-with-map__grid-item"> 1087 <a class="lp-listing-card mls-properties-with-map-card" 1088 href="/properties/3454-pinnacle-dr-san-jose-ca-us-95132-ml81957720" target="_self"> 1089 1090 <div class="mls-properties-with-map-card__img-wrap"> 1091 <img src="https://dlajgvw9htjpb.cloudfront.net/cms/9994eaf9-a60f-4f64-89b4-cb9e3cc349b1/ML81957720/4117366806890251434.jpg" alt="img" class="mls-properties-with-map-card__image" loading="lazy"> 1092 1093 <img src="https://res.cloudinary.com/luxuryp/images/f_auto,q_auto/ygkifjyaoikbgxbka6md/mlsl_logo_85x11-300x174" alt="MLS logo" class="lp-mls-compliance__mls-logo mls-properties-with-map-card__mls-logo" loading="lazy"> 1094 1095 </div> 1096
1097 <div class="mls-properties-with-map-card__info"> 1098 <p class="mls-properties-with-map-card__price"><span class="sales">$2,265,000</span></p> 1099 1100 <div class="mls-properties-with-map-card__attributes"> 1101 <span class="mls-properties-with-map-card__attribute">4 bd</span> 1102 <span class="mls-properties-with-map-card__attribute">2.5 ba</span> 1103 <span class="mls-properties-with-map-card__attribute">1,910 Sq.Ft.</span> 1104 <span class="mls-properties-with-map-card__attribute">Sold</span> 1105 </div> 1106 1107 <div class="mls-properties-with-map-card__address"> 1108 3454 Pinnacle Drive, SAN JOSE, CA 95132 1109 </div> 1110 1111 <div class="mls-properties-with-map-card__mls-key">MLS®: ML81957720</div> 1112 </div> 1113 </a> 1114 </div> 1115 <div class="mls-properties-with-map__grid-item"> 1116 <a class="lp-listing-card mls-properties-with-map-card" 1117 href="/properties/2171-monterey-ave-santa-clara-ca-us-95051-ml81973687" target="_self"> 1118 1119 <div class="mls-properties-with-map-card__img-wrap"> 1120 <img src="https://dlajgvw9htjpb.cloudfront.net/cms/9994eaf9-a60f-4f64-89b4-cb9e3cc349b1/ML81973687/3487540585512622359.jpg" alt="img" class="mls-properties-with-map-card__image" loading="lazy"> 1121 1122 <div class="lp-mls-compliance__mls-attribution mls-properties-with-map-card__attribution"> 1123 </div> 1124 1125 1126 </div> 1127 1128 <div class="mls-properties-with-map-card__info"> 1129 <p class="mls-properties-with-map-card__price"><span class="sales">$1,886,000</span></p> 1130 1131 <div class="mls-properties-with-map-card__attributes"> 1132 <span class="mls-properties-with-map-card__attribute">3 bd</span> 1133 <span class="mls-properties-with-map-card__attribute">2 ba</span> 1134 <span class="mls-properties-with-map-card__attribute">1,078 Sq.Ft.</span> 1135 <span class="mls-properties-with-map-card__attribute">Sold</span> 1136 </div> 1137 1138 <div class="mls-properties-with-map-card__address"> 1139 2171 Monterey AVE, Santa Clara, CA 95051 1140 </div> 1141 1142 <div class="mls-properties-with-map-card__mls-key">MLS®: ML81973687</div> 1143 </div> 1144 </a> 1145 </div> 1146 <div class="mls-properties-with-map__grid-item"> 1147 <a class="lp-listing-card mls-properties-with-map-card" 1148 href="/properties/755-sycamore-ave-san-bruno-ca-us-94066-ml81941932" target="_self"> 1149 1150 <div class="mls-properties-with-map-card__img-wrap"> 1151 <img src="https://dlajgvw9htjpb.cloudfront.net/cms/9994eaf9-a60f-4f64-89b4-cb9e3cc349b1/ML81941932/-3230824895796350266.jpg" alt="img" class="mls-properties-with-map-card__image" loading="lazy"> 1152 1153 <img src="https://res.cloudinary.com/luxuryp/images/f_auto,q_auto/ygkifjyaoikbgxbka6md/mlsl_logo_85x11-300x174" alt="MLS logo" class="lp-mls-compliance__mls-logo mls-properties-with-map-card__mls-logo" loading="lazy"> 1154 1155 </div> 1156 1157 <div class="mls-properties-with-map-card__info"> 1158 <p class="mls-properties-with-map-card__price"><span class="sales">$1,760,000</span></p> 1159 1160 <div class="mls-properties-with-map-card__attributes"> 1161 <span class="mls-properties-with-map-card__attribute">7 bd</span> 1162 <span class="mls-properties-with-map-card__attribute">3 ba</span> 1163 <span class="mls-properties-with-map-card__attribute">3,100 Sq.Ft.</span> 1164 <span class="mls-properties-with-map-card__attribute">Sold</span> 1165 </div> 1166
1167 <div class="mls-properties-with-map-card__address"> 1168 755 Sycamore Ave, SAN BRUNO, CA 94066 1169 </div> 1170 1171 <div class="mls-properties-with-map-card__mls-key">MLS®: ML81941932</div> 1172 </div> 1173 </a> 1174 </div> 1175 <div class="mls-properties-with-map__grid-item"> 1176 <a class="lp-listing-card mls-properties-with-map-card" 1177 href="/properties/3555-butcher-dr-santa-clara-ca-us-95051-ml81950267" target="_self"> 1178 1179 <div class="mls-properties-with-map-card__img-wrap"> 1180 <img src="https://dlajgvw9htjpb.cloudfront.net/cms/9994eaf9-a60f-4f64-89b4-cb9e3cc349b1/ML81950267/-5917994666004369898.jpg" alt="img" class="mls-properties-with-map-card__image" loading="lazy"> 1181 1182 <img src="https://res.cloudinary.com/luxuryp/images/f_auto,q_auto/ygkifjyaoikbgxbka6md/mlsl_logo_85x11-300x174" alt="MLS logo" class="lp-mls-compliance__mls-logo mls-properties-with-map-card__mls-logo" loading="lazy"> 1183 1184 </div> 1185 1186 <div class="mls-properties-with-map-card__info"> 1187 <p class="mls-properties-with-map-card__price"><span class="sales">$1,685,000</span></p> 1188 1189 <div class="mls-properties-with-map-card__attributes"> 1190 <span class="mls-properties-with-map-card__attribute">3 bd</span> 1191 <span class="mls-properties-with-map-card__attribute">2 ba</span> 1192 <span class="mls-properties-with-map-card__attribute">1,166 Sq.Ft.</span> 1193 <span class="mls-properties-with-map-card__attribute">Sold</span> 1194 </div> 1195 1196 <div class="mls-properties-with-map-card__address"> 1197 3555 Butcher Drive, SANTA CLARA, CA 95051 1198 </div> 1199 1200 <div class="mls-properties-with-map-card__mls-key">MLS®: ML81950267</div> 1201 </div> 1202 </a> 1203 </div> 1204 <div class="mls-properties-with-map__grid-item"> 1205 <a class="lp-listing-card mls-properties-with-map-card" 1206 href="/properties/866-alma-place-oakland-ca-us-94610-ml81941896" target="_self"> 1207 1208 <div class="mls-properties-with-map-card__img-wrap"> 1209 <img src="https://dlajgvw9htjpb.cloudfront.net/cms/9994eaf9-a60f-4f64-89b4-cb9e3cc349b1/ML81941896/-5464725880316482944.jpg" alt="img" class="mls-properties-with-map-card__image" loading="lazy"> 1210 1211 <img src="https://res.cloudinary.com/luxuryp/images/f_auto,q_auto/ygkifjyaoikbgxbka6md/mlsl_logo_85x11-300x174" alt="MLS logo" class="lp-mls-compliance__mls-logo mls-properties-with-map-card__mls-logo" loading="lazy"> 1212 1213 </div> 1214 1215 <div class="mls-properties-with-map-card__info"> 1216 <p class="mls-properties-with-map-card__price"><span class="sales">$1,650,000</span></p> 1217 1218 <div class="mls-properties-with-map-card__attributes"> 1219 <span class="mls-properties-with-map-card__attribute">4 bd</span> 1220 <span class="mls-properties-with-map-card__attribute">3.5 ba</span> 1221 <span class="mls-properties-with-map-card__attribute">2,160 Sq.Ft.</span> 1222 <span class="mls-properties-with-map-card__attribute">Sold</span> 1223 </div> 1224 1225 <div class="mls-properties-with-map-card__address"> 1226 866 Alma Place, OAKLAND, CA 94610 1227 </div> 1228 1229 <div class="mls-properties-with-map-card__mls-key">MLS®: ML81941896</div> 1230 </div> 1231 </a> 1232 </div> 1233 <div class="mls-properties-with-map__grid-item"> 1234 <a class="lp-listing-card mls-properties-with-map-card" 1235 href="/properties/916-almaden-ave-sunnyvale-ca-us-94085-ml81943545" target="_self"> 1236
1237 <div class="mls-properties-with-map-card__img-wrap"> 1238 <img src="https://dlajgvw9htjpb.cloudfront.net/cms/9994eaf9-a60f-4f64-89b4-cb9e3cc349b1/ML81943545/7189421042508521177.jpg" alt="img" class="mls-properties-with-map-card__image" loading="lazy"> 1239 1240 <img src="https://res.cloudinary.com/luxuryp/images/f_auto,q_auto/ygkifjyaoikbgxbka6md/mlsl_logo_85x11-300x174" alt="MLS logo" class="lp-mls-compliance__mls-logo mls-properties-with-map-card__mls-logo" loading="lazy"> 1241 1242 </div> 1243 1244 <div class="mls-properties-with-map-card__info"> 1245 <p class="mls-properties-with-map-card__price"><span class="sales">$1,610,000</span></p> 1246 1247 <div class="mls-properties-with-map-card__attributes"> 1248 <span class="mls-properties-with-map-card__attribute">4 bd</span> 1249 <span class="mls-properties-with-map-card__attribute">2 ba</span> 1250 <span class="mls-properties-with-map-card__attribute">1,326 Sq.Ft.</span> 1251 <span class="mls-properties-with-map-card__attribute">Sold</span> 1252 </div> 1253 1254 <div class="mls-properties-with-map-card__address"> 1255 916 Almaden AVE, SUNNYVALE, CA 94085 1256 </div> 1257 1258 <div class="mls-properties-with-map-card__mls-key">MLS®: ML81943545</div> 1259 </div> 1260 </a> 1261 </div> 1262 <div class="mls-properties-with-map__grid-item"> 1263 <a class="lp-listing-card mls-properties-with-map-card" 1264 href="/properties/90-valmar-ter-san-francisco-ca-us-94112-ml81980423" target="_self"> 1265 1266 <div class="mls-properties-with-map-card__img-wrap"> 1267 <img src="https://dlajgvw9htjpb.cloudfront.net/cms/9994eaf9-a60f-4f64-89b4-cb9e3cc349b1/ML81980423/2810630497440174229.jpg" alt="img" class="mls-properties-with-map-card__image" loading="lazy"> 1268 1269 <div class="lp-mls-compliance__mls-attribution mls-properties-with-map-card__attribution"> 1270 </div> 1271 <img src="https://res.cloudinary.com/luxuryp/images/f_auto,q_auto/ygkifjyaoikbgxbka6md/mlsl_logo_85x11-300x174" alt="MLS logo" class="lp-mls-compliance__mls-logo mls-properties-with-map-card__mls-logo" loading="lazy"> 1272 1273 </div> 1274 1275 <div class="mls-properties-with-map-card__info"> 1276 <p class="mls-properties-with-map-card__price"><span class="sales">$1,595,000</span></p> 1277 1278 <div class="mls-properties-with-map-card__attributes"> 1279 <span class="mls-properties-with-map-card__attribute">4 bd</span> 1280 <span class="mls-properties-with-map-card__attribute">3 ba</span> 1281 <span class="mls-properties-with-map-card__attribute">1,983 Sq.Ft.</span> 1282 <span class="mls-properties-with-map-card__attribute">Sold</span> 1283 </div> 1284 1285 <div class="mls-properties-with-map-card__address"> 1286 90 Valmar TER, San Francisco, CA 94112 1287 </div> 1288 1289 <div class="mls-properties-with-map-card__mls-key">MLS®: ML81980423</div> 1290 </div> 1291 </a> 1292 </div> 1293 <div class="mls-properties-with-map__grid-item"> 1294 <a class="lp-listing-card mls-properties-with-map-card" 1295 href="/properties/1956-rock-st-mountain-view-ca-us-94043-ml81965024" target="_self"> 1296 1297 <div class="mls-properties-with-map-card__img-wrap"> 1298 <img src="https://dlajgvw9htjpb.cloudfront.net/cms/9994eaf9-a60f-4f64-89b4-cb9e3cc349b1/ML81965024/3525027213997098151.jpg" alt="img" class="mls-properties-with-map-card__image" loading="lazy"> 1299 1300 <img src="https://res.cloudinary.com/luxuryp/images/f_auto,q_auto/ygkifjyaoikbgxbka6md/mlsl_logo_85x11-300x174" alt="MLS logo" class="lp-mls-compliance__mls-logo mls-properties-with-ma
1300p-card__mls-logo" loading="lazy"> 1301 1302 </div> 1303 1304 <div class="mls-properties-with-map-card__info"> 1305 <p class="mls-properties-with-map-card__price"><span class="sales">$1,536,000</span></p> 1306 1307 <div class="mls-properties-with-map-card__attributes"> 1308 <span class="mls-properties-with-map-card__attribute">2 bd</span> 1309 <span class="mls-properties-with-map-card__attribute">2.5 ba</span> 1310 <span class="mls-properties-with-map-card__attribute">1,464 Sq.Ft.</span> 1311 <span class="mls-properties-with-map-card__attribute">Sold</span> 1312 </div> 1313 1314 <div class="mls-properties-with-map-card__address"> 1315 1956 Rock Street, MOUNTAIN VIEW, CA 94043 1316 </div> 1317 1318 <div class="mls-properties-with-map-card__mls-key">MLS®: ML81965024</div> 1319 </div> 1320 </a> 1321 </div> 1322 <div class="mls-properties-with-map__grid-item"> 1323 <a class="lp-listing-card mls-properties-with-map-card" 1324 href="/properties/3756-evangelho-cir-san-jose-ca-us-95148-ml81969531" target="_self"> 1325 1326 <div class="mls-properties-with-map-card__img-wrap"> 1327 <img src="https://dlajgvw9htjpb.cloudfront.net/cms/9994eaf9-a60f-4f64-89b4-cb9e3cc349b1/ML81969531/-2493391256649294139.jpg" alt="img" class="mls-properties-with-map-card__image" loading="lazy"> 1328 1329 <div class="lp-mls-compliance__mls-attribution mls-properties-with-map-card__attribution"> 1330 </div> 1331 <img src="https://res.cloudinary.com/luxuryp/images/f_auto,q_auto/ygkifjyaoikbgxbka6md/mlsl_logo_85x11-300x174" alt="MLS logo" class="lp-mls-compliance__mls-logo mls-properties-with-map-card__mls-logo" loading="lazy"> 1332 1333 </div> 1334 1335 <div class="mls-properties-with-map-card__info"> 1336 <p class="mls-properties-with-map-card__price"><span class="sales">$1,435,000</span></p> 1337 1338 <div class="mls-properties-with-map-card__attributes"> 1339 <span class="mls-properties-with-map-card__attribute">3 bd</span> 1340 <span class="mls-properties-with-map-card__attribute">3 ba</span> 1341 <span class="mls-properties-with-map-card__attribute">1,820 Sq.Ft.</span> 1342 <span class="mls-properties-with-map-card__attribute">Sold</span> 1343 </div> 1344 1345 <div class="mls-properties-with-map-card__address"> 1346 3756 Evangelho CIR, San Jose, CA 95148 1347 </div> 1348 1349 <div class="mls-properties-with-map-card__mls-key">MLS®: ML81969531</div> 1350 </div> 1351 </a> 1352 </div> 1353 <div class="mls-properties-with-map__grid-item"> 1354 <a class="lp-listing-card mls-properties-with-map-card" 1355 href="/properties/1368-el-camino-real-412-santa-clara-ca-us-95050-ml81999848" target="_self"> 1356 1357 <div class="mls-properties-with-map-card__img-wrap"> 1358 <img src="https://dlajgvw9htjpb.cloudfront.net/cms/9994eaf9-a60f-4f64-89b4-cb9e3cc349b1/ML81999848/2901469167640758528.jpg" alt="img" class="mls-properties-with-map-card__image" loading="lazy"> 1359 1360 <div class="lp-mls-compliance__mls-attribution mls-properties-with-map-card__attribution"> 1361 </div> 1362 1363 1364 </div> 1365 1366 <div class="mls-properties-with-map-card__info"> 1367 <p class="mls-properties-with-map-card__price"><span class="sales">$1,375,000</span></p> 1368 1369 <div class="mls-properties-with-map-card__attributes"> 1370 <span class="mls-properties-with-map-card__attribute">4 bd</span> 1371 <span class="mls-properties-with-map-card__attribute">3 ba</span>
1372 <span class="mls-properties-with-map-card__attribute">2,087 Sq.Ft.</span> 1373 <span class="mls-properties-with-map-card__attribute">Sold</span> 1374 </div> 1375 1376 <div class="mls-properties-with-map-card__address"> 1377 1368 El Camino Real 412, Santa Clara, CA 95050 1378 </div> 1379 1380 <div class="mls-properties-with-map-card__mls-key">MLS®: ML81999848</div> 1381 </div> 1382 </a> 1383 </div> 1384 <div class="mls-properties-with-map__grid-item"> 1385 <a class="lp-listing-card mls-properties-with-map-card" 1386 href="/properties/3355-kimberly-way-san-mateo-ca-us-94403-17883063" target="_self"> 1387 1388 <div class="mls-properties-with-map-card__img-wrap"> 1389 <img src="https://dlajgvw9htjpb.cloudfront.net/cms/9994eaf9-a60f-4f64-89b4-cb9e3cc349b1/ML82055942/102069064076538764.jpg" alt="img" class="mls-properties-with-map-card__image" loading="lazy"> 1390 1391 <div class="lp-mls-compliance__mls-attribution mls-properties-with-map-card__attribution"> 1392 </div> 1393 <img src="https://res.cloudinary.com/luxuryp/images/f_auto,q_auto/ygkifjyaoikbgxbka6md/mlsl_logo_85x11-300x174" alt="MLS logo" class="lp-mls-compliance__mls-logo mls-properties-with-map-card__mls-logo" loading="lazy"> 1394 1395 </div> 1396 1397 <div class="mls-properties-with-map-card__info"> 1398 <p class="mls-properties-with-map-card__price"><span class="sales">$1,375,000</span></p> 1399 1400 <div class="mls-properties-with-map-card__attributes"> 1401 <span class="mls-properties-with-map-card__attribute">3 bd</span> 1402 <span class="mls-properties-with-map-card__attribute">2 ba</span> 1403 <span class="mls-properties-with-map-card__attribute">1,450 Sq.Ft.</span> 1404 <span class="mls-properties-with-map-card__attribute">Sold</span> 1405 </div> 1406 1407 <div class="mls-properties-with-map-card__address"> 1408 3355 Kimberly WAY, San Mateo, CA 94403 1409 </div> 1410 1411 <div class="mls-properties-with-map-card__mls-key">MLS®: ML82055942</div> 1412 </div> 1413 </a> 1414 </div> 1415 <div class="mls-properties-with-map__grid-item"> 1416 <a class="lp-listing-card mls-properties-with-map-card" 1417 href="/properties/123-incense-terrace-sunnyvale-ca-us-94086-ml82003015" target="_self"> 1418 1419 <div class="mls-properties-with-map-card__img-wrap"> 1420 <img src="https://dlajgvw9htjpb.cloudfront.net/cms/9994eaf9-a60f-4f64-89b4-cb9e3cc349b1/ML82003015/-7303079306715787472.jpg" alt="img" class="mls-properties-with-map-card__image" loading="lazy"> 1421 1422 <div class="lp-mls-compliance__mls-attribution mls-properties-with-map-card__attribution"> 1423 </div> 1424 <img src="https://res.cloudinary.com/luxuryp/images/f_auto,q_auto/ygkifjyaoikbgxbka6md/mlsl_logo_85x11-300x174" alt="MLS logo" class="lp-mls-compliance__mls-logo mls-properties-with-map-card__mls-logo" loading="lazy"> 1425 1426 </div> 1427 1428 <div class="mls-properties-with-map-card__info"> 1429 <p class="mls-properties-with-map-card__price"><span class="sales">$1,325,000</span></p> 1430 1431 <div class="mls-properties-with-map-card__attributes"> 1432 <span class="mls-properties-with-map-card__attribute">2 bd</span> 1433 <span class="mls-properties-with-map-card__attribute">3 ba</span> 1434 <span class="mls-properties-with-map-card__attribute">1,375 Sq.Ft.</span> 1435 <span class="mls-properties-with-map-card__attribute">Sold</span> 1436 </div> 1437 1438 <div class="mls-properties-with-map-card__address"> 1439 123 Incense TER, Sunnyvale, CA 94086 1440 </div> 1441
1442 <div class="mls-properties-with-map-card__mls-key">MLS®: ML82003015</div> 1443 </div> 1444 </a> 1445 </div> 1446 <div class="mls-properties-with-map__grid-item"> 1447 <a class="lp-listing-card mls-properties-with-map-card" 1448 href="/properties/169-manly-terrace-4-san-jose-ca-us-95136-ml81957183" target="_self"> 1449 1450 <div class="mls-properties-with-map-card__img-wrap"> 1451 <img src="https://dlajgvw9htjpb.cloudfront.net/cms/9994eaf9-a60f-4f64-89b4-cb9e3cc349b1/ML81957183/1933544402881414036.jpg" alt="img" class="mls-properties-with-map-card__image" loading="lazy"> 1452 1453 <img src="https://res.cloudinary.com/luxuryp/images/f_auto,q_auto/ygkifjyaoikbgxbka6md/mlsl_logo_85x11-300x174" alt="MLS logo" class="lp-mls-compliance__mls-logo mls-properties-with-map-card__mls-logo" loading="lazy"> 1454 1455 </div> 1456 1457 <div class="mls-properties-with-map-card__info"> 1458 <p class="mls-properties-with-map-card__price"><span class="sales">$1,320,000</span></p> 1459 1460 <div class="mls-properties-with-map-card__attributes"> 1461 <span class="mls-properties-with-map-card__attribute">4 bd</span> 1462 <span class="mls-properties-with-map-card__attribute">3.5 ba</span> 1463 <span class="mls-properties-with-map-card__attribute">1,995 Sq.Ft.</span> 1464 <span class="mls-properties-with-map-card__attribute">Sold</span> 1465 </div> 1466 1467 <div class="mls-properties-with-map-card__address"> 1468 169 Manly Terrace 4, SAN JOSE, CA 95136 1469 </div> 1470 1471 <div class="mls-properties-with-map-card__mls-key">MLS®: ML81957183</div> 1472 </div> 1473 </a> 1474 </div> 1475 <div class="mls-properties-with-map__grid-item"> 1476 <a class="lp-listing-card mls-properties-with-map-card" 1477 href="/properties/2110540745036861239-9665-ne-dronawood-drive" target="_self"> 1478 1479 <div class="mls-properties-with-map-card__img-wrap"> 1480 <img src="https://dlajgvw9htjpb.cloudfront.net/cms/9994eaf9-a60f-4f64-89b4-cb9e3cc349b1/2250069/1446635395908836585.jpg" alt="img" class="mls-properties-with-map-card__image" loading="lazy"> 1481 1482 1483 1484 </div> 1485 1486 <div class="mls-properties-with-map-card__info"> 1487 <p class="mls-properties-with-map-card__price"><span class="sales">$1,300,000</span></p> 1488 1489 <div class="mls-properties-with-map-card__attributes"> 1490 <span class="mls-properties-with-map-card__attribute">4 bd</span> 1491 <span class="mls-properties-with-map-card__attribute">6 ba</span> 1492 <span class="mls-properties-with-map-card__attribute">3,511 Sq.Ft.</span> 1493 <span class="mls-properties-with-map-card__attribute">Sold</span> 1494 </div> 1495 1496 <div class="mls-properties-with-map-card__address"> 1497 9665 NE Dronawood Drive, Kingston, WA 98346 1498 </div> 1499 1500 <div class="mls-properties-with-map-card__mls-key">MLS®: 2250069</div> 1501 </div> 1502 </a> 1503 </div> 1504 <div class="mls-properties-with-map__grid-item"> 1505 <a class="lp-listing-card mls-properties-with-map-card" 1506 href="/properties/10-lavenham-rd-novato-ca-us-94949-ml81980424" target="_self"> 1507 1508 <div class="mls-properties-with-map-card__img-wrap"> 1509 <img src="https://dlajgvw9htjpb.cloudfront.net/cms/9994eaf9-a60f-4f64-89b4-cb9e3cc349b1/ML81980424/922725107865711436.jpg" alt="img" class="mls-properties-with-map-card__image" loading="lazy"> 1510 1511 <div class="lp-mls-compliance__mls-attribution mls-properties-with-map-card__attribution"> 1512 </div> 1513 <img src="https://res.cloudinary.com/luxuryp/images/f_auto,q_auto/ygkifjyaoikbgxbka6md/mlsl_logo_85x11-300x174" alt="MLS logo" class="lp-mls-compliance__mls-logo mls-properties-with-ma
1513p-card__mls-logo" loading="lazy"> 1514 1515 </div> 1516 1517 <div class="mls-properties-with-map-card__info"> 1518 <p class="mls-properties-with-map-card__price"><span class="sales">$1,255,000</span></p> 1519 1520 <div class="mls-properties-with-map-card__attributes"> 1521 <span class="mls-properties-with-map-card__attribute">4 bd</span> 1522 <span class="mls-properties-with-map-card__attribute">3 ba</span> 1523 <span class="mls-properties-with-map-card__attribute">2,133 Sq.Ft.</span> 1524 <span class="mls-properties-with-map-card__attribute">Sold</span> 1525 </div> 1526 1527 <div class="mls-properties-with-map-card__address"> 1528 10 Lavenham RD, Novato, CA 94949 1529 </div> 1530 1531 <div class="mls-properties-with-map-card__mls-key">MLS®: ML81980424</div> 1532 </div> 1533 </a> 1534 </div> 1535 <div class="mls-properties-with-map__grid-item"> 1536 <a class="lp-listing-card mls-properties-with-map-card" 1537 href="/properties/3231354381684483424-373-monterey-road" target="_self"> 1538 1539 <div class="mls-properties-with-map-card__img-wrap"> 1540 <img src="https://dlajgvw9htjpb.cloudfront.net/cms/9994eaf9-a60f-4f64-89b4-cb9e3cc349b1/PF23058956/-372145939605623474.jpg" alt="img" class="mls-properties-with-map-card__image" loading="lazy"> 1541 1542 <img src="https://res.cloudinary.com/luxuryp/images/f_auto,q_auto/ayfqwu5unpcjgnh1uh7k/crmls-logo-simplified-600x600-1-1024x296" alt="MLS logo" class="lp-mls-compliance__mls-logo mls-properties-with-map-card__mls-logo" loading="lazy"> 1543 1544 </div> 1545 1546 <div class="mls-properties-with-map-card__info"> 1547 <p class="mls-properties-with-map-card__price"><span class="sales">$1,249,000</span></p> 1548 1549 <div class="mls-properties-with-map-card__attributes"> 1550 <span class="mls-properties-with-map-card__attribute">4 bd</span> 1551 <span class="mls-properties-with-map-card__attribute">3 ba</span> 1552 <span class="mls-properties-with-map-card__attribute">2,170 Sq.Ft.</span> 1553 <span class="mls-properties-with-map-card__attribute">Sold</span> 1554 </div> 1555 1556 <div class="mls-properties-with-map-card__address"> 1557 373 Monterey Road, South Pasadena, CA 91030 1558 </div> 1559 1560 <div class="mls-properties-with-map-card__mls-key">MLS®: PF23058956</div> 1561 </div> 1562 </a> 1563 </div> 1564 <div class="mls-properties-with-map__grid-item"> 1565 <a class="lp-listing-card mls-properties-with-map-card" 1566 href="/properties/4018-central-ave-100-fremont-ca-us-94536-ml81992277" target="_self"> 1567 1568 <div class="mls-properties-with-map-card__img-wrap"> 1569 <img src="https://dlajgvw9htjpb.cloudfront.net/cms/9994eaf9-a60f-4f64-89b4-cb9e3cc349b1/ML81992277/8959838023414002675.jpg" alt="img" class="mls-properties-with-map-card__image" loading="lazy"> 1570 1571 <div class="lp-mls-compliance__mls-attribution mls-properties-with-map-card__attribution"> 1572 </div> 1573 <img src="https://res.cloudinary.com/luxuryp/images/f_auto,q_auto/ygkifjyaoikbgxbka6md/mlsl_logo_85x11-300x174" alt="MLS logo" class="lp-mls-compliance__mls-logo mls-properties-with-map-card__mls-logo" loading="lazy"> 1574 1575 </div> 1576 1577 <div class="mls-properties-with-map-card__info"> 1578 <p class="mls-properties-with-map-card__price"><span class="sales">$1,230,000</span></p> 1579 1580 <div class="mls-properties-with-map-card__attributes"> 1581 <span class="mls-properties-with-map-card__attribute">4 bd</span>
1582 <span class="mls-properties-with-map-card__attribute">3 ba</span> 1583 <span class="mls-properties-with-map-card__attribute">1,850 Sq.Ft.</span> 1584 <span class="mls-properties-with-map-card__attribute">Sold</span> 1585 </div> 1586 1587 <div class="mls-properties-with-map-card__address"> 1588 4018 Central AVE 100, Fremont, CA 94536 1589 </div> 1590 1591 <div class="mls-properties-with-map-card__mls-key">MLS®: ML81992277</div> 1592 </div> 1593 </a> 1594 </div> 1595 <div class="mls-properties-with-map__grid-item"> 1596 <a class="lp-listing-card mls-properties-with-map-card" 1597 href="/properties/636-bair-island-road-312-redwood-city-ca-94063-ml82016570" target="_self"> 1598 1599 <div class="mls-properties-with-map-card__img-wrap"> 1600 <img src="https://dlajgvw9htjpb.cloudfront.net/cms/9994eaf9-a60f-4f64-89b4-cb9e3cc349b1/ML82016570/-1887587561729636559.jpg" alt="img" class="mls-properties-with-map-card__image" loading="lazy"> 1601 1602 <div class="lp-mls-compliance__mls-attribution mls-properties-with-map-card__attribution"> 1603 </div> 1604 <img src="https://res.cloudinary.com/luxuryp/images/f_auto,q_auto/ygkifjyaoikbgxbka6md/mlsl_logo_85x11-300x174" alt="MLS logo" class="lp-mls-compliance__mls-logo mls-properties-with-map-card__mls-logo" loading="lazy"> 1605 1606 </div> 1607 1608 <div class="mls-properties-with-map-card__info"> 1609 <p class="mls-properties-with-map-card__price"><span class="sales">$1,198,000</span></p> 1610 1611 <div class="mls-properties-with-map-card__attributes"> 1612 <span class="mls-properties-with-map-card__attribute">2 bd</span> 1613 <span class="mls-properties-with-map-card__attribute">2 ba</span> 1614 <span class="mls-properties-with-map-card__attribute">1,508 Sq.Ft.</span> 1615 <span class="mls-properties-with-map-card__attribute">Sold</span> 1616 </div> 1617 1618 <div class="mls-properties-with-map-card__address"> 1619 636 Bair Island RD 312, Redwood City, CA 94063 1620 </div> 1621 1622 <div class="mls-properties-with-map-card__mls-key">MLS®: ML82016570</div> 1623 </div> 1624 </a> 1625 </div> 1626 <div class="mls-properties-with-map__grid-item"> 1627 <a class="lp-listing-card mls-properties-with-map-card" 1628 href="/properties/682-crescent-ave-sunnyvale-ca-us-94087-ml81970389" target="_self"> 1629 1630 <div class="mls-properties-with-map-card__img-wrap"> 1631 <img src="https://dlajgvw9htjpb.cloudfront.net/cms/9994eaf9-a60f-4f64-89b4-cb9e3cc349b1/ML81970389/8546014701685807131.jpg" alt="img" class="mls-properties-with-map-card__image" loading="lazy"> 1632 1633 <div class="lp-mls-compliance__mls-attribution mls-properties-with-map-card__attribution"> 1634 </div> 1635 <img src="https://res.cloudinary.com/luxuryp/images/f_auto,q_auto/ygkifjyaoikbgxbka6md/mlsl_logo_85x11-300x174" alt="MLS logo" class="lp-mls-compliance__mls-logo mls-properties-with-map-card__mls-logo" loading="lazy"> 1636 1637 </div> 1638 1639 <div class="mls-properties-with-map-card__info"> 1640 <p class="mls-properties-with-map-card__price"><span class="sales">$1,185,000</span></p> 1641 1642 <div class="mls-properties-with-map-card__attributes"> 1643 <span class="mls-properties-with-map-card__attribute">2 bd</span> 1644 <span class="mls-properties-with-map-card__attribute">2 ba</span> 1645 <span class="mls-properties-with-map-card__attribute">1,222 Sq.Ft.</span> 1646 <span class="mls-properties-with-map-card__attribute">Sold</span> 1647 </div> 1648
1649 <div class="mls-properties-with-map-card__address"> 1650 682 Crescent AVE, Sunnyvale, CA 94087 1651 </div> 1652 1653 <div class="mls-properties-with-map-card__mls-key">MLS®: ML81970389</div> 1654 </div> 1655 </a> 1656 </div> 1657 <div class="mls-properties-with-map__grid-item"> 1658 <a class="lp-listing-card mls-properties-with-map-card" 1659 href="/properties/10350-doris-ave-san-jose-ca-us-95127-ml82002999" target="_self"> 1660 1661 <div class="mls-properties-with-map-card__img-wrap"> 1662 <img src="https://dlajgvw9htjpb.cloudfront.net/cms/9994eaf9-a60f-4f64-89b4-cb9e3cc349b1/ML82002999/5457118035169944027.jpg" alt="img" class="mls-properties-with-map-card__image" loading="lazy"> 1663 1664 <div class="lp-mls-compliance__mls-attribution mls-properties-with-map-card__attribution"> 1665 </div> 1666 <img src="https://res.cloudinary.com/luxuryp/images/f_auto,q_auto/ygkifjyaoikbgxbka6md/mlsl_logo_85x11-300x174" alt="MLS logo" class="lp-mls-compliance__mls-logo mls-properties-with-map-card__mls-logo" loading="lazy"> 1667 1668 </div> 1669 1670 <div class="mls-properties-with-map-card__info"> 1671 <p class="mls-properties-with-map-card__price"><span class="sales">$1,180,000</span></p> 1672 1673 <div class="mls-properties-with-map-card__attributes"> 1674 <span class="mls-properties-with-map-card__attribute">4 bd</span> 1675 <span class="mls-properties-with-map-card__attribute">2 ba</span> 1676 <span class="mls-properties-with-map-card__attribute">1,342 Sq.Ft.</span> 1677 <span class="mls-properties-with-map-card__attribute">Sold</span> 1678 </div> 1679 1680 <div class="mls-properties-with-map-card__address"> 1681 10350 Doris AVE, San Jose, CA 95127 1682 </div> 1683 1684 <div class="mls-properties-with-map-card__mls-key">MLS®: ML82002999</div> 1685 </div> 1686 </a> 1687 </div> 1688 <div class="mls-properties-with-map__grid-item"> 1689 <a class="lp-listing-card mls-properties-with-map-card" 1690 href="/properties/568-mayten-dr-livermore-ca-us-94551-ml81993107" target="_self"> 1691 1692 <div class="mls-properties-with-map-card__img-wrap"> 1693 <img src="https://dlajgvw9htjpb.cloudfront.net/cms/9994eaf9-a60f-4f64-89b4-cb9e3cc349b1/ML81993107/-8210699693387325643.jpg" alt="img" class="mls-properties-with-map-card__image" loading="lazy"> 1694 1695 <div class="lp-mls-compliance__mls-attribution mls-properties-with-map-card__attribution"> 1696 </div> 1697 <img src="https://res.cloudinary.com/luxuryp/images/f_auto,q_auto/ygkifjyaoikbgxbka6md/mlsl_logo_85x11-300x174" alt="MLS logo" class="lp-mls-compliance__mls-logo mls-properties-with-map-card__mls-logo" loading="lazy"> 1698 1699 </div> 1700 1701 <div class="mls-properties-with-map-card__info"> 1702 <p class="mls-properties-with-map-card__price"><span class="sales">$1,180,000</span></p> 1703 1704 <div class="mls-properties-with-map-card__attributes"> 1705 <span class="mls-properties-with-map-card__attribute">3 bd</span> 1706 <span class="mls-properties-with-map-card__attribute">2 ba</span> 1707 <span class="mls-properties-with-map-card__attribute">1,130 Sq.Ft.</span> 1708 <span class="mls-properties-with-map-card__attribute">Sold</span> 1709 </div> 1710 1711 <div class="mls-properties-with-map-card__address"> 1712 568 Mayten DR, Livermore, CA 94551 1713 </div> 1714 1715 <div class="mls-properties-with-map-card__mls-key">MLS®: ML81993107</div> 1716 </div> 1717 </a> 1718 </div>
1719 <div class="mls-properties-with-map__grid-item"> 1720 <a class="lp-listing-card mls-properties-with-map-card" 1721 href="/properties/1147-tiffany-lane-pleasanton-ca-us-94566-224067696" target="_self"> 1722 1723 <div class="mls-properties-with-map-card__img-wrap"> 1724 <img src="https://dlajgvw9htjpb.cloudfront.net/cms/9994eaf9-a60f-4f64-89b4-cb9e3cc349b1/224067696/5362236552203204219.jpg" alt="img" class="mls-properties-with-map-card__image" loading="lazy"> 1725 1726 <div class="lp-mls-compliance__mls-attribution mls-properties-with-map-card__attribution">Courtesy of FlyHomes, Inc. 1727 </div> 1728 <img src="https://res.cloudinary.com/luxuryp/images/f_auto,q_auto/kson0bhlww5cbvbkh4y6/logo-notag" alt="MLS logo" class="lp-mls-compliance__mls-logo mls-properties-with-map-card__mls-logo" loading="lazy"> 1729 1730 </div> 1731 1732 <div class="mls-properties-with-map-card__info"> 1733 <p class="mls-properties-with-map-card__price"><span class="sales">$1,175,000</span></p> 1734 1735 <div class="mls-properties-with-map-card__attributes"> 1736 <span class="mls-properties-with-map-card__attribute">3 bd</span> 1737 <span class="mls-properties-with-map-card__attribute">3 ba</span> 1738 <span class="mls-properties-with-map-card__attribute">1,707 Sq.Ft.</span> 1739 <span class="mls-properties-with-map-card__attribute">Sold</span> 1740 </div> 1741 1742 <div class="mls-properties-with-map-card__address"> 1743 1147 Tiffany Lane, Pleasanton, CA 94566 1744 </div> 1745 1746 <div class="mls-properties-with-map-card__mls-key">MLS®: 224067696</div> 1747 </div> 1748 </a> 1749 </div> 1750 <div class="mls-properties-with-map__grid-item"> 1751 <a class="lp-listing-card mls-properties-with-map-card" 1752 href="/properties/5976-capriana-cmn-143-fremont-ca-us-94555-ml81945992" target="_self"> 1753 1754 <div class="mls-properties-with-map-card__img-wrap"> 1755 <img src="https://dlajgvw9htjpb.cloudfront.net/cms/9994eaf9-a60f-4f64-89b4-cb9e3cc349b1/ML81945992/3080117888879069977.jpg" alt="img" class="mls-properties-with-map-card__image" loading="lazy"> 1756 1757 <img src="https://res.cloudinary.com/luxuryp/images/f_auto,q_auto/ygkifjyaoikbgxbka6md/mlsl_logo_85x11-300x174" alt="MLS logo" class="lp-mls-compliance__mls-logo mls-properties-with-map-card__mls-logo" loading="lazy"> 1758 1759 </div> 1760 1761 <div class="mls-properties-with-map-card__info"> 1762 <p class="mls-properties-with-map-card__price"><span class="sales">$1,155,000</span></p> 1763 1764 <div class="mls-properties-with-map-card__attributes"> 1765 <span class="mls-properties-with-map-card__attribute">2 bd</span> 1766 <span class="mls-properties-with-map-card__attribute">2.5 ba</span> 1767 <span class="mls-properties-with-map-card__attribute">1,395 Sq.Ft.</span> 1768 <span class="mls-properties-with-map-card__attribute">Sold</span> 1769 </div> 1770 1771 <div class="mls-properties-with-map-card__address"> 1772 5976 Capriana Common 143, FREMONT, CA 94555 1773 </div> 1774 1775 <div class="mls-properties-with-map-card__mls-key">MLS®: ML81945992</div> 1776 </div> 1777 </a> 1778 </div> 1779 <div class="mls-properties-with-map__grid-item"> 1780 <a class="lp-listing-card mls-properties-with-map-card" 1781 href="/properties/2001-mcallister-st-255-san-francisco-ca-us-94118-ml81985297" target="_self"> 1782 1783 <div class="mls-properties-with-map-card__img-wrap"> 1784 <img src="https://dlajgvw9htjpb.cloudfront.net/cms/9994eaf9-a60f-4f64-89b4-cb9e3cc349b1/ML81985297/-3833546806971429845.jpg" alt="img" class="mls-properties-with-ma
1784p-card__image" loading="lazy"> 1785 1786 <div class="lp-mls-compliance__mls-attribution mls-properties-with-map-card__attribution"> 1787 </div> 1788 <img src="https://res.cloudinary.com/luxuryp/images/f_auto,q_auto/ygkifjyaoikbgxbka6md/mlsl_logo_85x11-300x174" alt="MLS logo" class="lp-mls-compliance__mls-logo mls-properties-with-map-card__mls-logo" loading="lazy"> 1789 1790 </div> 1791 1792 <div class="mls-properties-with-map-card__info"> 1793 <p class="mls-properties-with-map-card__price"><span class="sales">$1,090,000</span></p> 1794 1795 <div class="mls-properties-with-map-card__attributes"> 1796 <span class="mls-properties-with-map-card__attribute">2 bd</span> 1797 <span class="mls-properties-with-map-card__attribute">3 ba</span> 1798 <span class="mls-properties-with-map-card__attribute">1,405 Sq.Ft.</span> 1799 <span class="mls-properties-with-map-card__attribute">Sold</span> 1800 </div> 1801 1802 <div class="mls-properties-with-map-card__address"> 1803 2001 Mcallister ST 255, San Francisco, CA 94118 1804 </div> 1805 1806 <div class="mls-properties-with-map-card__mls-key">MLS®: ML81985297</div> 1807 </div> 1808 </a> 1809 </div> 1810 <div class="mls-properties-with-map__grid-item"> 1811 <a class="lp-listing-card mls-properties-with-map-card" 1812 href="/properties/9600-carrari-court-rancho-cucamonga-ca-us-91737-pf25061625" target="_self"> 1813 1814 <div class="mls-properties-with-map-card__img-wrap"> 1815 <img src="https://dlajgvw9htjpb.cloudfront.net/cms/9994eaf9-a60f-4f64-89b4-cb9e3cc349b1/PF25061625/-6246298912172648039.jpg" alt="img" class="mls-properties-with-map-card__image" loading="lazy"> 1816 1817 <img src="https://res.cloudinary.com/luxuryp/images/f_auto,q_auto/ayfqwu5unpcjgnh1uh7k/crmls-logo-simplified-600x600-1-1024x296" alt="MLS logo" class="lp-mls-compliance__mls-logo mls-properties-with-map-card__mls-logo" loading="lazy"> 1818 1819 </div> 1820 1821 <div class="mls-properties-with-map-card__info"> 1822 <p class="mls-properties-with-map-card__price"><span class="sales">$1,075,000</span></p> 1823 1824 <div class="mls-properties-with-map-card__attributes"> 1825 <span class="mls-properties-with-map-card__attribute">5 bd</span> 1826 <span class="mls-properties-with-map-card__attribute">3 ba</span> 1827 <span class="mls-properties-with-map-card__attribute">2,852 Sq.Ft.</span> 1828 <span class="mls-properties-with-map-card__attribute">Sold</span> 1829 </div> 1830 1831 <div class="mls-properties-with-map-card__address"> 1832 9600 Carrari Court, Rancho Cucamonga, CA 91737 1833 </div> 1834 1835 <div class="mls-properties-with-map-card__mls-key">MLS®: PF25061625</div> 1836 </div> 1837 </a> 1838 </div> 1839 <div class="mls-properties-with-map__grid-item"> 1840 <a class="lp-listing-card mls-properties-with-map-card" 1841 href="/properties/211040936172561156-6002-25th-avenue-ne" target="_self"> 1842 1843 <div class="mls-properties-with-map-card__img-wrap"> 1844 <img src="https://dlajgvw9htjpb.cloudfront.net/cms/9994eaf9-a60f-4f64-89b4-cb9e3cc349b1/2245978/-4029913105464168620.jpg" alt="img" class="mls-properties-with-map-card__image" loading="lazy"> 1845 1846 1847 1848 </div> 1849 1850 <div class="mls-properties-with-map-card__info"> 1851 <p class="mls-properties-with-map-card__price"><span class="sales">$1,040,000</span></p> 1852 1853 <div class="mls-properties-with-map-card__attributes">
1854 <span class="mls-properties-with-map-card__attribute">3 bd</span> 1855 <span class="mls-properties-with-map-card__attribute">2 ba</span> 1856 <span class="mls-properties-with-map-card__attribute">2,290 Sq.Ft.</span> 1857 <span class="mls-properties-with-map-card__attribute">Sold</span> 1858 </div> 1859 1860 <div class="mls-properties-with-map-card__address"> 1861 6002 25th Avenue NE, Seattle, WA 98115 1862 </div> 1863 1864 <div class="mls-properties-with-map-card__mls-key">MLS®: 2245978</div> 1865 </div> 1866 </a> 1867 </div> 1868 <div class="mls-properties-with-map__grid-item"> 1869 <a class="lp-listing-card mls-properties-with-map-card" 1870 href="/properties/3231228703450574486-8-firebird-78" target="_self"> 1871 1872 <div class="mls-properties-with-map-card__img-wrap"> 1873 <img src="https://dlajgvw9htjpb.cloudfront.net/cms/9994eaf9-a60f-4f64-89b4-cb9e3cc349b1/PF24139393/737010921845354884.jpg" alt="img" class="mls-properties-with-map-card__image" loading="lazy"> 1874 1875 <img src="https://res.cloudinary.com/luxuryp/images/f_auto,q_auto/ayfqwu5unpcjgnh1uh7k/crmls-logo-simplified-600x600-1-1024x296" alt="MLS logo" class="lp-mls-compliance__mls-logo mls-properties-with-map-card__mls-logo" loading="lazy"> 1876 1877 </div> 1878 1879 <div class="mls-properties-with-map-card__info"> 1880 <p class="mls-properties-with-map-card__price"><span class="sales">$1,015,000</span></p> 1881 1882 <div class="mls-properties-with-map-card__attributes"> 1883 <span class="mls-properties-with-map-card__attribute">2 bd</span> 1884 <span class="mls-properties-with-map-card__attribute">2 ba</span> 1885 <span class="mls-properties-with-map-card__attribute">990 Sq.Ft.</span> 1886 <span class="mls-properties-with-map-card__attribute">Sold</span> 1887 </div> 1888 1889 <div class="mls-properties-with-map-card__address"> 1890 8 Firebird 78, Irvine, CA 92604 1891 </div> 1892 1893 <div class="mls-properties-with-map-card__mls-key">MLS®: PF24139393</div> 1894 </div> 1895 </a> 1896 </div> 1897 <div class="mls-properties-with-map__grid-item"> 1898 <a class="lp-listing-card mls-properties-with-map-card" 1899 href="/properties/1847-bristol-bay-cmn-san-jose-ca-us-95131-ml81940643" target="_self"> 1900 1901 <div class="mls-properties-with-map-card__img-wrap"> 1902 <img src="https://dlajgvw9htjpb.cloudfront.net/cms/9994eaf9-a60f-4f64-89b4-cb9e3cc349b1/ML81940643/-1728060457626181669.jpg" alt="img" class="mls-properties-with-map-card__image" loading="lazy"> 1903 1904 <img src="https://res.cloudinary.com/luxuryp/images/f_auto,q_auto/ygkifjyaoikbgxbka6md/mlsl_logo_85x11-300x174" alt="MLS logo" class="lp-mls-compliance__mls-logo mls-properties-with-map-card__mls-logo" loading="lazy"> 1905 1906 </div> 1907 1908 <div class="mls-properties-with-map-card__info"> 1909 <p class="mls-properties-with-map-card__price"><span class="sales">$1,001,000</span></p> 1910 1911 <div class="mls-properties-with-map-card__attributes"> 1912 <span class="mls-properties-with-map-card__attribute">2 bd</span> 1913 <span class="mls-properties-with-map-card__attribute">2.5 ba</span> 1914 <span class="mls-properties-with-map-card__attribute">1,181 Sq.Ft.</span> 1915 <span class="mls-properties-with-map-card__attribute">Sold</span> 1916 </div> 1917 1918 <div class="mls-properties-with-map-card__address"> 1919 1847 Bristol Bay Common, SAN JOSE, CA 95131 1920 </div> 1921
1922 <div class="mls-properties-with-map-card__mls-key">MLS®: ML81940643</div> 1923 </div> 1924 </a> 1925 </div> 1926 <div class="mls-properties-with-map__grid-item"> 1927 <a class="lp-listing-card mls-properties-with-map-card" 1928 href="/properties/3231282154509221426-320-ford-road" target="_self"> 1929 1930 <div class="mls-properties-with-map-card__img-wrap"> 1931 <img src="https://dlajgvw9htjpb.cloudfront.net/cms/9994eaf9-a60f-4f64-89b4-cb9e3cc349b1/PF25037166/-96995209564040372.jpg" alt="img" class="mls-properties-with-map-card__image" loading="lazy"> 1932 1933 <img src="https://res.cloudinary.com/luxuryp/images/f_auto,q_auto/ayfqwu5unpcjgnh1uh7k/crmls-logo-simplified-600x600-1-1024x296" alt="MLS logo" class="lp-mls-compliance__mls-logo mls-properties-with-map-card__mls-logo" loading="lazy"> 1934 1935 </div> 1936 1937 <div class="mls-properties-with-map-card__info"> 1938 <p class="mls-properties-with-map-card__price"><span class="sales">$990,000</span></p> 1939 1940 <div class="mls-properties-with-map-card__attributes"> 1941 <span class="mls-properties-with-map-card__attribute">2 bd</span> 1942 <span class="mls-properties-with-map-card__attribute">3 ba</span> 1943 <span class="mls-properties-with-map-card__attribute">1,289 Sq.Ft.</span> 1944 <span class="mls-properties-with-map-card__attribute">Sold</span> 1945 </div> 1946 1947 <div class="mls-properties-with-map-card__address"> 1948 320 Ford Road, Costa Mesa, CA 92627 1949 </div> 1950 1951 <div class="mls-properties-with-map-card__mls-key">MLS®: PF25037166</div> 1952 </div> 1953 </a> 1954 </div> 1955 <div class="mls-properties-with-map__grid-item"> 1956 <a class="lp-listing-card mls-properties-with-map-card" 1957 href="/properties/2407-vernal-dr-bay-point-ca-us-94565-ml81941427" target="_self"> 1958 1959 <div class="mls-properties-with-map-card__img-wrap"> 1960 <img src="https://dlajgvw9htjpb.cloudfront.net/cms/9994eaf9-a60f-4f64-89b4-cb9e3cc349b1/ML81941427/-3404234320124401831.jpg" alt="img" class="mls-properties-with-map-card__image" loading="lazy"> 1961 1962 <img src="https://res.cloudinary.com/luxuryp/images/f_auto,q_auto/ygkifjyaoikbgxbka6md/mlsl_logo_85x11-300x174" alt="MLS logo" class="lp-mls-compliance__mls-logo mls-properties-with-map-card__mls-logo" loading="lazy"> 1963 1964 </div> 1965 1966 <div class="mls-properties-with-map-card__info"> 1967 <p class="mls-properties-with-map-card__price"><span class="sales">$975,000</span></p> 1968 1969 <div class="mls-properties-with-map-card__attributes"> 1970 <span class="mls-properties-with-map-card__attribute">5 bd</span> 1971 <span class="mls-properties-with-map-card__attribute">3.5 ba</span> 1972 <span class="mls-properties-with-map-card__attribute">3,583 Sq.Ft.</span> 1973 <span class="mls-properties-with-map-card__attribute">Sold</span> 1974 </div> 1975 1976 <div class="mls-properties-with-map-card__address"> 1977 2407 Vernal DR, BAY POINT, CA 94565 1978 </div> 1979 1980 <div class="mls-properties-with-map-card__mls-key">MLS®: ML81941427</div> 1981 </div> 1982 </a> 1983 </div> 1984 <div class="mls-properties-with-map__grid-item"> 1985 <a class="lp-listing-card mls-properties-with-map-card" 1986 href="/properties/3231406085388522152-303-vista-avenue-2" target="_self"> 1987 1988 <div class="mls-properties-with-map-card__img-wrap"> 1989 <img src="https://dlajgvw9htjpb.cloudfront.net/cms/9994eaf9-a60f-4f64-89b4-cb9e3cc349b1/PF23229801/598074361849705562.jpg" alt="img" class="mls-properties-with-ma
1989p-card__image" loading="lazy"> 1990 1991 <img src="https://res.cloudinary.com/luxuryp/images/f_auto,q_auto/ayfqwu5unpcjgnh1uh7k/crmls-logo-simplified-600x600-1-1024x296" alt="MLS logo" class="lp-mls-compliance__mls-logo mls-properties-with-map-card__mls-logo" loading="lazy"> 1992 1993 </div> 1994 1995 <div class="mls-properties-with-map-card__info"> 1996 <p class="mls-properties-with-map-card__price"><span class="sales">$970,000</span></p> 1997 1998 <div class="mls-properties-with-map-card__attributes"> 1999 <span class="mls-properties-with-map-card__attribute">3 bd</span> 2000 <span class="mls-properties-with-map-card__attribute">3 ba</span> 2001 <span class="mls-properties-with-map-card__attribute">1,808 Sq.Ft.</span> 2002 <span class="mls-properties-with-map-card__attribute">Sold</span> 2003 </div> 2004 2005 <div class="mls-properties-with-map-card__address"> 2006 303 Vista Avenue 2, Pasadena, CA 91107 2007 </div> 2008 2009 <div class="mls-properties-with-map-card__mls-key">MLS®: PF23229801</div> 2010 </div> 2011 </a> 2012 </div> 2013 <div class="mls-properties-with-map__grid-item"> 2014 <a class="lp-listing-card mls-properties-with-map-card" 2015 href="/properties/19-cross-rd-boxford-ma-us-01921-73390123" target="_self"> 2016 2017 <div class="mls-properties-with-map-card__img-wrap"> 2018 <img src="https://dlajgvw9htjpb.cloudfront.net/cms/9994eaf9-a60f-4f64-89b4-cb9e3cc349b1/73390123/-5846528170972717071.jpg" alt="img" class="mls-properties-with-map-card__image" loading="lazy"> 2019 2020 <div class="lp-mls-compliance__mls-attribution mls-properties-with-map-card__attribution">Courtesy of with 2021 </div> 2022 2023 2024 </div> 2025 2026 <div class="mls-properties-with-map-card__info"> 2027 <p class="mls-properties-with-map-card__price"><span class="sales">$940,000</span></p> 2028 2029 <div class="mls-properties-with-map-card__attributes"> 2030 <span class="mls-properties-with-map-card__attribute">3 bd</span> 2031 <span class="mls-properties-with-map-card__attribute">2 ba</span> 2032 <span class="mls-properties-with-map-card__attribute">2,008 Sq.Ft.</span> 2033 <span class="mls-properties-with-map-card__attribute">Sold</span> 2034 </div> 2035 2036 <div class="mls-properties-with-map-card__address"> 2037 19 Cross Rd, Boxford, MA 01921 2038 </div> 2039 2040 <div class="mls-properties-with-map-card__mls-key">MLS®: 73390123</div> 2041 </div> 2042 </a> 2043 </div> 2044 <div class="mls-properties-with-map__grid-item"> 2045 <a class="lp-listing-card mls-properties-with-map-card" 2046 href="/properties/525-east-marcello-avenue-mountain-house-ca-us-95391-ml82012562" target="_self"> 2047 2048 <div class="mls-properties-with-map-card__img-wrap"> 2049 <img src="https://dlajgvw9htjpb.cloudfront.net/cms/9994eaf9-a60f-4f64-89b4-cb9e3cc349b1/ML82012562/1288258132918016294.jpg" alt="img" class="mls-properties-with-map-card__image" loading="lazy"> 2050 2051 <div class="lp-mls-compliance__mls-attribution mls-properties-with-map-card__attribution"> 2052 </div> 2053 <img src="https://res.cloudinary.com/luxuryp/images/f_auto,q_auto/ygkifjyaoikbgxbka6md/mlsl_logo_85x11-300x174" alt="MLS logo" class="lp-mls-compliance__mls-logo mls-properties-with-map-card__mls-logo" loading="lazy"> 2054 2055 </div> 2056 2057 <div class="mls-properties-with-map-card__info"> 2058 <p class="mls-properties-with-map-card__price"><span class="sales">$920,000</span></p> 2059
2060 <div class="mls-properties-with-map-card__attributes"> 2061 <span class="mls-properties-with-map-card__attribute">4 bd</span> 2062 <span class="mls-properties-with-map-card__attribute">4 ba</span> 2063 <span class="mls-properties-with-map-card__attribute">2,551 Sq.Ft.</span> 2064 <span class="mls-properties-with-map-card__attribute">Sold</span> 2065 </div> 2066 2067 <div class="mls-properties-with-map-card__address"> 2068 525 E Marcello AVE, Mountain House, CA 95391 2069 </div> 2070 2071 <div class="mls-properties-with-map-card__mls-key">MLS®: ML82012562</div> 2072 </div> 2073 </a> 2074 </div> 2075 <div class="mls-properties-with-map__grid-item"> 2076 <a class="lp-listing-card mls-properties-with-map-card" 2077 href="/properties/2662-22nd-st-a-san-francisco-ca-us-94110-ml81942705" target="_self"> 2078 2079 <div class="mls-properties-with-map-card__img-wrap"> 2080 <img src="https://dlajgvw9htjpb.cloudfront.net/cms/9994eaf9-a60f-4f64-89b4-cb9e3cc349b1/ML81942705/-7718757467407719459.jpg" alt="img" class="mls-properties-with-map-card__image" loading="lazy"> 2081 2082 <div class="lp-mls-compliance__mls-attribution mls-properties-with-map-card__attribution"> 2083 </div> 2084 <img src="https://res.cloudinary.com/luxuryp/images/f_auto,q_auto/ygkifjyaoikbgxbka6md/mlsl_logo_85x11-300x174" alt="MLS logo" class="lp-mls-compliance__mls-logo mls-properties-with-map-card__mls-logo" loading="lazy"> 2085 2086 </div> 2087 2088 <div class="mls-properties-with-map-card__info"> 2089 <p class="mls-properties-with-map-card__price"><span class="sales">$900,000</span></p> 2090 2091 <div class="mls-properties-with-map-card__attributes"> 2092 <span class="mls-properties-with-map-card__attribute">2 bd</span> 2093 <span class="mls-properties-with-map-card__attribute">2 ba</span> 2094 <span class="mls-properties-with-map-card__attribute">1,312 Sq.Ft.</span> 2095 <span class="mls-properties-with-map-card__attribute">Sold</span> 2096 </div> 2097 2098 <div class="mls-properties-with-map-card__address"> 2099 2662 22nd ST A, San Francisco, CA 94110 2100 </div> 2101 2102 <div class="mls-properties-with-map-card__mls-key">MLS®: ML81942705</div> 2103 </div> 2104 </a> 2105 </div> 2106 <div class="mls-properties-with-map__grid-item"> 2107 <a class="lp-listing-card mls-properties-with-map-card" 2108 href="/properties/26754-contessa-street-hayward-ca-us-94545-225127532" target="_self"> 2109 2110 <div class="mls-properties-with-map-card__img-wrap"> 2111 <img src="https://dlajgvw9htjpb.cloudfront.net/cms/9994eaf9-a60f-4f64-89b4-cb9e3cc349b1/225127532/-4581520787116551827.jpg" alt="img" class="mls-properties-with-map-card__image" loading="lazy"> 2112 2113 <div class="lp-mls-compliance__mls-attribution mls-properties-with-map-card__attribution">Courtesy of Real Broker 2114 </div> 2115 <img src="https://res.cloudinary.com/luxuryp/images/f_auto,q_auto/kson0bhlww5cbvbkh4y6/logo-notag" alt="MLS logo" class="lp-mls-compliance__mls-logo mls-properties-with-map-card__mls-logo" loading="lazy"> 2116 2117 </div> 2118 2119 <div class="mls-properties-with-map-card__info"> 2120 <p class="mls-properties-with-map-card__price"><span class="sales">$900,000</span></p> 2121 2122 <div class="mls-properties-with-map-card__attributes"> 2123 <span class="mls-properties-with-map-card__attribute">3 bd</span> 2124 <span class="mls-properties-with-map-card__attribute">2 ba</span>
2125 <span class="mls-properties-with-map-card__attribute">1,527 Sq.Ft.</span> 2126 <span class="mls-properties-with-map-card__attribute">Sold</span> 2127 </div> 2128 2129 <div class="mls-properties-with-map-card__address"> 2130 26754 Contessa Street, Hayward, CA 94545 2131 </div> 2132 2133 <div class="mls-properties-with-map-card__mls-key">MLS®: 225127532</div> 2134 </div> 2135 </a> 2136 </div> 2137 <div class="mls-properties-with-map__grid-item"> 2138 <a class="lp-listing-card mls-properties-with-map-card" 2139 href="/properties/3396-el-monte-dr-concord-ca-us-94519-ml81963054" target="_self"> 2140 2141 <div class="mls-properties-with-map-card__img-wrap"> 2142 <img src="https://dlajgvw9htjpb.cloudfront.net/cms/9994eaf9-a60f-4f64-89b4-cb9e3cc349b1/ML81963054/8369929113183726991.jpg" alt="img" class="mls-properties-with-map-card__image" loading="lazy"> 2143 2144 <img src="https://res.cloudinary.com/luxuryp/images/f_auto,q_auto/ygkifjyaoikbgxbka6md/mlsl_logo_85x11-300x174" alt="MLS logo" class="lp-mls-compliance__mls-logo mls-properties-with-map-card__mls-logo" loading="lazy"> 2145 2146 </div> 2147 2148 <div class="mls-properties-with-map-card__info"> 2149 <p class="mls-properties-with-map-card__price"><span class="sales">$900,000</span></p> 2150 2151 <div class="mls-properties-with-map-card__attributes"> 2152 <span class="mls-properties-with-map-card__attribute">3 bd</span> 2153 <span class="mls-properties-with-map-card__attribute">2 ba</span> 2154 <span class="mls-properties-with-map-card__attribute">1,396 Sq.Ft.</span> 2155 <span class="mls-properties-with-map-card__attribute">Sold</span> 2156 </div> 2157 2158 <div class="mls-properties-with-map-card__address"> 2159 3396 El Monte Drive, CONCORD, CA 94519 2160 </div> 2161 2162 <div class="mls-properties-with-map-card__mls-key">MLS®: ML81963054</div> 2163 </div> 2164 </a> 2165 </div> 2166 <div class="mls-properties-with-map__grid-item"> 2167 <a class="lp-listing-card mls-properties-with-map-card" 2168 href="/properties/1388-gough-st-402-san-francisco-ca-us-94109-ml81990748" target="_self"> 2169 2170 <div class="mls-properties-with-map-card__img-wrap"> 2171 <img src="https://dlajgvw9htjpb.cloudfront.net/cms/9994eaf9-a60f-4f64-89b4-cb9e3cc349b1/ML81990748/-7886848196192093700.jpg" alt="img" class="mls-properties-with-map-card__image" loading="lazy"> 2172 2173 <div class="lp-mls-compliance__mls-attribution mls-properties-with-map-card__attribution"> 2174 </div> 2175 <img src="https://res.cloudinary.com/luxuryp/images/f_auto,q_auto/ygkifjyaoikbgxbka6md/mlsl_logo_85x11-300x174" alt="MLS logo" class="lp-mls-compliance__mls-logo mls-properties-with-map-card__mls-logo" loading="lazy"> 2176 2177 </div> 2178 2179 <div class="mls-properties-with-map-card__info"> 2180 <p class="mls-properties-with-map-card__price"><span class="sales">$875,000</span></p> 2181 2182 <div class="mls-properties-with-map-card__attributes"> 2183 <span class="mls-properties-with-map-card__attribute">2 bd</span> 2184 <span class="mls-properties-with-map-card__attribute">2 ba</span> 2185 <span class="mls-properties-with-map-card__attribute">1,187 Sq.Ft.</span> 2186 <span class="mls-properties-with-map-card__attribute">Sold</span> 2187 </div> 2188 2189 <div class="mls-properties-with-map-card__address"> 2190 1388 Gough ST 402, San Francisco, CA 94109 2191 </div> 2192
2193 <div class="mls-properties-with-map-card__mls-key">MLS®: ML81990748</div> 2194 </div> 2195 </a> 2196 </div> 2197 <div class="mls-properties-with-map__grid-item"> 2198 <a class="lp-listing-card mls-properties-with-map-card" 2199 href="/properties/27-sawin-st-watertown-ma-us-02472-73204714" target="_self"> 2200 2201 <div class="mls-properties-with-map-card__img-wrap"> 2202 <img src="https://dlajgvw9htjpb.cloudfront.net/cms/9994eaf9-a60f-4f64-89b4-cb9e3cc349b1/73204714/4053794771195753711.jpg" alt="img" class="mls-properties-with-map-card__image" loading="lazy"> 2203 2204 <div class="lp-mls-compliance__mls-attribution mls-properties-with-map-card__attribution">Courtesy of Athoye Sharif with FlyHomes Brokerage LLC 2205 </div> 2206 2207 2208 </div> 2209 2210 <div class="mls-properties-with-map-card__info"> 2211 <p class="mls-properties-with-map-card__price"><span class="sales">$870,000</span></p> 2212 2213 <div class="mls-properties-with-map-card__attributes"> 2214 <span class="mls-properties-with-map-card__attribute">3 bd</span> 2215 <span class="mls-properties-with-map-card__attribute">3 ba</span> 2216 <span class="mls-properties-with-map-card__attribute">1,564 Sq.Ft.</span> 2217 <span class="mls-properties-with-map-card__attribute">Sold</span> 2218 </div> 2219 2220 <div class="mls-properties-with-map-card__address"> 2221 27 Sawin St, Watertown, MA 02472 2222 </div> 2223 2224 <div class="mls-properties-with-map-card__mls-key">MLS®: 73204714</div> 2225 </div> 2226 </a> 2227 </div> 2228 <div class="mls-properties-with-map__grid-item"> 2229 <a class="lp-listing-card mls-properties-with-map-card" 2230 href="/properties/379-alvarado-st-san-leandro-ca-us-94577-ml81974481" target="_self"> 2231 2232 <div class="mls-properties-with-map-card__img-wrap"> 2233 <img src="https://dlajgvw9htjpb.cloudfront.net/cms/9994eaf9-a60f-4f64-89b4-cb9e3cc349b1/ML81974481/1520114984924665738.jpg" alt="img" class="mls-properties-with-map-card__image" loading="lazy"> 2234 2235 <div class="lp-mls-compliance__mls-attribution mls-properties-with-map-card__attribution"> 2236 </div> 2237 <img src="https://res.cloudinary.com/luxuryp/images/f_auto,q_auto/ygkifjyaoikbgxbka6md/mlsl_logo_85x11-300x174" alt="MLS logo" class="lp-mls-compliance__mls-logo mls-properties-with-map-card__mls-logo" loading="lazy"> 2238 2239 </div> 2240 2241 <div class="mls-properties-with-map-card__info"> 2242 <p class="mls-properties-with-map-card__price"><span class="sales">$870,000</span></p> 2243 2244 <div class="mls-properties-with-map-card__attributes"> 2245 <span class="mls-properties-with-map-card__attribute">3 bd</span> 2246 <span class="mls-properties-with-map-card__attribute">3 ba</span> 2247 <span class="mls-properties-with-map-card__attribute">1,599 Sq.Ft.</span> 2248 <span class="mls-properties-with-map-card__attribute">Sold</span> 2249 </div> 2250 2251 <div class="mls-properties-with-map-card__address"> 2252 379 Alvarado ST, San Leandro, CA 94577 2253 </div> 2254 2255 <div class="mls-properties-with-map-card__mls-key">MLS®: ML81974481</div> 2256 </div> 2257 </a> 2258 </div> 2259 <div class="mls-properties-with-map__grid-item"> 2260 <a class="lp-listing-card mls-properties-with-map-card" 2261 href="/properties/1281-homestead-ave-2a-walnut-creek-ca-us-94598-ml81937990" target="_self"> 2262 2263 <div class="mls-properties-with-map-card__img-wrap"> 2264 <img src="https://dlajgvw9htjpb.cloudfront.net/cms/9994eaf9-a60f-4f64-89b4-cb9e3cc349b1/ML81937990/-1486618020060460361.jpg" alt="img" class="mls-properties-with-ma
2264p-card__image" loading="lazy"> 2265 2266 <img src="https://res.cloudinary.com/luxuryp/images/f_auto,q_auto/ygkifjyaoikbgxbka6md/mlsl_logo_85x11-300x174" alt="MLS logo" class="lp-mls-compliance__mls-logo mls-properties-with-map-card__mls-logo" loading="lazy"> 2267 2268 </div> 2269 2270 <div class="mls-properties-with-map-card__info"> 2271 <p class="mls-properties-with-map-card__price"><span class="sales">$850,000</span></p> 2272 2273 <div class="mls-properties-with-map-card__attributes"> 2274 <span class="mls-properties-with-map-card__attribute">3 bd</span> 2275 <span class="mls-properties-with-map-card__attribute">2 ba</span> 2276 <span class="mls-properties-with-map-card__attribute">1,427 Sq.Ft.</span> 2277 <span class="mls-properties-with-map-card__attribute">Sold</span> 2278 </div> 2279 2280 <div class="mls-properties-with-map-card__address"> 2281 1281 Homestead Avenue 2A, WALNUT CREEK, CA 94598 2282 </div> 2283 2284 <div class="mls-properties-with-map-card__mls-key">MLS®: ML81937990</div> 2285 </div> 2286 </a> 2287 </div> 2288 <div class="mls-properties-with-map__grid-item"> 2289 <a class="lp-listing-card mls-properties-with-map-card" 2290 href="/properties/89-overland-rd-1-waltham-ma-us-02451-73401897" target="_self"> 2291 2292 <div class="mls-properties-with-map-card__img-wrap"> 2293 <img src="https://dlajgvw9htjpb.cloudfront.net/cms/9994eaf9-a60f-4f64-89b4-cb9e3cc349b1/73401897/-5218438816382089852.jpg" alt="img" class="mls-properties-with-map-card__image" loading="lazy"> 2294 2295 <div class="lp-mls-compliance__mls-attribution mls-properties-with-map-card__attribution">Courtesy of with 2296 </div> 2297 2298 2299 </div> 2300 2301 <div class="mls-properties-with-map-card__info"> 2302 <p class="mls-properties-with-map-card__price"><span class="sales">$850,000</span></p> 2303 2304 <div class="mls-properties-with-map-card__attributes"> 2305 <span class="mls-properties-with-map-card__attribute">3 bd</span> 2306 <span class="mls-properties-with-map-card__attribute">3 ba</span> 2307 <span class="mls-properties-with-map-card__attribute">2,541 Sq.Ft.</span> 2308 <span class="mls-properties-with-map-card__attribute">Sold</span> 2309 </div> 2310 2311 <div class="mls-properties-with-map-card__address"> 2312 89 Overland Rd # 1, Waltham, MA 02451 2313 </div> 2314 2315 <div class="mls-properties-with-map-card__mls-key">MLS®: 73401897</div> 2316 </div> 2317 </a> 2318 </div> 2319 <div class="mls-properties-with-map__grid-item"> 2320 <a class="lp-listing-card mls-properties-with-map-card" 2321 href="/properties/333-grove-st-norwell-ma-us-02061-73285315" target="_self"> 2322 2323 <div class="mls-properties-with-map-card__img-wrap"> 2324 <img src="https://dlajgvw9htjpb.cloudfront.net/cms/9994eaf9-a60f-4f64-89b4-cb9e3cc349b1/73285315/8221380904576925403.jpg" alt="img" class="mls-properties-with-map-card__image" loading="lazy"> 2325 2326 <div class="lp-mls-compliance__mls-attribution mls-properties-with-map-card__attribution">Courtesy of with 2327 </div> 2328 2329 2330 </div> 2331 2332 <div class="mls-properties-with-map-card__info"> 2333 <p class="mls-properties-with-map-card__price"><span class="sales">$845,000</span></p> 2334 2335 <div class="mls-properties-with-map-card__attributes"> 2336 <span class="mls-properties-with-map-card__attribute">4 bd</span>
2337 <span class="mls-properties-with-map-card__attribute">2 ba</span> 2338 <span class="mls-properties-with-map-card__attribute">2,100 Sq.Ft.</span> 2339 <span class="mls-properties-with-map-card__attribute">Sold</span> 2340 </div> 2341 2342 <div class="mls-properties-with-map-card__address"> 2343 333 Grove St, Norwell, MA 02061 2344 </div> 2345 2346 <div class="mls-properties-with-map-card__mls-key">MLS®: 73285315</div> 2347 </div> 2348 </a> 2349 </div> 2350 <div class="mls-properties-with-map__grid-item"> 2351 <a class="lp-listing-card mls-properties-with-map-card" 2352 href="/properties/98-preda-st-san-leandro-ca-us-94577-ml81955379" target="_self"> 2353 2354 <div class="mls-properties-with-map-card__img-wrap"> 2355 <img src="https://dlajgvw9htjpb.cloudfront.net/cms/9994eaf9-a60f-4f64-89b4-cb9e3cc349b1/ML81955379/564298491766850425.jpg" alt="img" class="mls-properties-with-map-card__image" loading="lazy"> 2356 2357 <img src="https://res.cloudinary.com/luxuryp/images/f_auto,q_auto/ygkifjyaoikbgxbka6md/mlsl_logo_85x11-300x174" alt="MLS logo" class="lp-mls-compliance__mls-logo mls-properties-with-map-card__mls-logo" loading="lazy"> 2358 2359 </div> 2360 2361 <div class="mls-properties-with-map-card__info"> 2362 <p class="mls-properties-with-map-card__price"><span class="sales">$838,000</span></p> 2363 2364 <div class="mls-properties-with-map-card__attributes"> 2365 <span class="mls-properties-with-map-card__attribute">4 bd</span> 2366 <span class="mls-properties-with-map-card__attribute">2 ba</span> 2367 <span class="mls-properties-with-map-card__attribute">1,176 Sq.Ft.</span> 2368 <span class="mls-properties-with-map-card__attribute">Sold</span> 2369 </div> 2370 2371 <div class="mls-properties-with-map-card__address"> 2372 98 Preda Street, SAN LEANDRO, CA 94577 2373 </div> 2374 2375 <div class="mls-properties-with-map-card__mls-key">MLS®: ML81955379</div> 2376 </div> 2377 </a> 2378 </div> 2379 <div class="mls-properties-with-map__grid-item"> 2380 <a class="lp-listing-card mls-properties-with-map-card" 2381 href="/properties/3231909441746002138-2634-gage-avenue" target="_self"> 2382 2383 <div class="mls-properties-with-map-card__img-wrap"> 2384 <img src="https://dlajgvw9htjpb.cloudfront.net/cms/9994eaf9-a60f-4f64-89b4-cb9e3cc349b1/PF24075494/-5636827370149623716.jpg" alt="img" class="mls-properties-with-map-card__image" loading="lazy"> 2385 2386 2387 2388 </div> 2389 2390 <div class="mls-properties-with-map-card__info"> 2391 <p class="mls-properties-with-map-card__price"><span class="sales">$831,400</span></p> 2392 2393 <div class="mls-properties-with-map-card__attributes"> 2394 <span class="mls-properties-with-map-card__attribute">4 bd</span> 2395 <span class="mls-properties-with-map-card__attribute">3 ba</span> 2396 <span class="mls-properties-with-map-card__attribute">1,630 Sq.Ft.</span> 2397 <span class="mls-properties-with-map-card__attribute">Sold</span> 2398 </div> 2399 2400 <div class="mls-properties-with-map-card__address"> 2401 2634 Gage Avenue, El Monte, CA 91733 2402 </div> 2403 2404 <div class="mls-properties-with-map-card__mls-key">MLS®: PF24075494</div> 2405 </div> 2406 </a> 2407 </div> 2408 <div class="mls-properties-with-map__grid-item"> 2409 <a class="lp-listing-card mls-properties-with-map-card" 2410 href="/properties/3231115097630185164-453-n-altadena-drive-5" target="_self"> 2411
2412 <div class="mls-properties-with-map-card__img-wrap"> 2413 <img src="https://dlajgvw9htjpb.cloudfront.net/cms/9994eaf9-a60f-4f64-89b4-cb9e3cc349b1/PF25169493/-578569710187203344.jpg" alt="img" class="mls-properties-with-map-card__image" loading="lazy"> 2414 2415 <img src="https://res.cloudinary.com/luxuryp/images/f_auto,q_auto/ayfqwu5unpcjgnh1uh7k/crmls-logo-simplified-600x600-1-1024x296" alt="MLS logo" class="lp-mls-compliance__mls-logo mls-properties-with-map-card__mls-logo" loading="lazy"> 2416 2417 </div> 2418 2419 <div class="mls-properties-with-map-card__info"> 2420 <p class="mls-properties-with-map-card__price"><span class="sales">$820,000</span></p> 2421 2422 <div class="mls-properties-with-map-card__attributes"> 2423 <span class="mls-properties-with-map-card__attribute">2 bd</span> 2424 <span class="mls-properties-with-map-card__attribute">3 ba</span> 2425 <span class="mls-properties-with-map-card__attribute">1,264 Sq.Ft.</span> 2426 <span class="mls-properties-with-map-card__attribute">Sold</span> 2427 </div> 2428 2429 <div class="mls-properties-with-map-card__address"> 2430 453 N Altadena Drive 5, Pasadena, CA 91107 2431 </div> 2432 2433 <div class="mls-properties-with-map-card__mls-key">MLS®: PF25169493</div> 2434 </div> 2435 </a> 2436 </div> 2437 <div class="mls-properties-with-map__grid-item"> 2438 <a class="lp-listing-card mls-properties-with-map-card" 2439 href="/properties/3231216161976699631-1097-blanche-street-310" target="_self"> 2440 2441 <div class="mls-properties-with-map-card__img-wrap"> 2442 <img src="https://dlajgvw9htjpb.cloudfront.net/cms/9994eaf9-a60f-4f64-89b4-cb9e3cc349b1/PF24069266/-2548983084682532913.jpg" alt="img" class="mls-properties-with-map-card__image" loading="lazy"> 2443 2444 2445 2446 </div> 2447 2448 <div class="mls-properties-with-map-card__info"> 2449 <p class="mls-properties-with-map-card__price"><span class="sales">$800,000</span></p> 2450 2451 <div class="mls-properties-with-map-card__attributes"> 2452 <span class="mls-properties-with-map-card__attribute">2 bd</span> 2453 <span class="mls-properties-with-map-card__attribute">2 ba</span> 2454 <span class="mls-properties-with-map-card__attribute">1,066 Sq.Ft.</span> 2455 <span class="mls-properties-with-map-card__attribute">Sold</span> 2456 </div> 2457 2458 <div class="mls-properties-with-map-card__address"> 2459 1097 Blanche Street 310, Pasadena, CA 91106 2460 </div> 2461 2462 <div class="mls-properties-with-map-card__mls-key">MLS®: PF24069266</div> 2463 </div> 2464 </a> 2465 </div> 2466 <div class="mls-properties-with-map__grid-item"> 2467 <a class="lp-listing-card mls-properties-with-map-card" 2468 href="/properties/5514-florida-dr-concord-ca-us-94521-ml81944911" target="_self"> 2469 2470 <div class="mls-properties-with-map-card__img-wrap"> 2471 <img src="https://dlajgvw9htjpb.cloudfront.net/cms/9994eaf9-a60f-4f64-89b4-cb9e3cc349b1/ML81944911/-4327873375353139476.jpg" alt="img" class="mls-properties-with-map-card__image" loading="lazy"> 2472 2473 <img src="https://res.cloudinary.com/luxuryp/images/f_auto,q_auto/ygkifjyaoikbgxbka6md/mlsl_logo_85x11-300x174" alt="MLS logo" class="lp-mls-compliance__mls-logo mls-properties-with-map-card__mls-logo" loading="lazy"> 2474 2475 </div> 2476 2477 <div class="mls-properties-with-map-card__info"> 2478 <p class="mls-properties-with-map-card__price"><span class="sales">$800,000</span></p> 2479
2480 <div class="mls-properties-with-map-card__attributes"> 2481 <span class="mls-properties-with-map-card__attribute">3 bd</span> 2482 <span class="mls-properties-with-map-card__attribute">2 ba</span> 2483 <span class="mls-properties-with-map-card__attribute">1,355 Sq.Ft.</span> 2484 <span class="mls-properties-with-map-card__attribute">Sold</span> 2485 </div> 2486 2487 <div class="mls-properties-with-map-card__address"> 2488 5514 Florida Drive, CONCORD, CA 94521 2489 </div> 2490 2491 <div class="mls-properties-with-map-card__mls-key">MLS®: ML81944911</div> 2492 </div> 2493 </a> 2494 </div> 2495 </div> 2496 2497 2498 <div class="mls-properties-with-map__no-content js-empty-result" style="display: none;"> 2499 <p class="mls-properties-with-map__no-content-text">There are no properties that match your search results.</p> 2500 </div> 2501 2502 <div class="mls-properties-with-map__footer"> <a 2503 href="/home-search/listings" 2504 2505 class="lp-btn 2506 mls-properties-with-map__footer-btn button-style-1" 2507 2508 2509 2510 2511 2512 > 2513 Search Homes 2514 </a> 2515</div> 2516 </div> 2517 </div> 2518</section> 2519<section id="section-ba9ae458-99fa-408d-b931-ca44d53d9839" class="solid-section is-font-color-dark is-background-color-light" data-el-id="194f7b4b-fe39-4449-93f2-7e4994a1baa6"> 2520
2520<script src="https://cdnjs.cloudflare.com/ajax/libs/noUiSlider/15.8.1/nouislider.min.js" integrity="sha512-g/feAizmeiVKSwvfW0Xk3ZHZqv5Zs8PEXEBKzL15pM0SevEvoX8eJ4yFWbqakvRj7vtw1Q97bLzEpG2IVWX0Mg==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
2520 2521<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/noUiSlider/15.8.1/nouislider.css" integrity="sha512-MKxcSu/LDtbIYHBNAWUQwfB3iVoG9xeMCm32QV5hZ/9lFaQZJVaXfz9aFa0IZExWzCpm7OWvp9zq9gVip/nLMg==" crossorigin="anonymous" referrerpolicy="no-referrer" /> 2522 2523
2524<script type="text/x-handlebars-template" class="propertyItem pagination-script"> <a href="{{#if fromMLS}}/home-search/listings/{{id}}{{else}}/properties/{{slug}}{{/if}}" class="properties-list-item"> 2525 <div class="properties-list-item__image-container lp_animation-img-zoom"> 2526 <span class="properties-list-item__tag lp-tag">{{formatPropertyStatus status false (hbFormatDate soldDate)}}</span> 2527 {{#if openHouseHours}} 2528 <span class="properties-list-item__open-houses lp-tag">Open House: {{openHouseHours}}</span> 2529 {{/if}} 2530 {{{mediaImage media.[0] alt=name width="400" height="270" class="properties-list-item__image"}}} 2531 </div> 2532 2533 <div class="properties-list-item__info"> 2534 2535 <div class="properties-list-item__top-block"> 2536 <h6 class="properties-list-item__title">{{name}}</h6> 2537 <p class="properties-list-item__address">{{#if privateAddress}}Private Address{{else}}{{fullAddress}}{{/if}}</p> 2538 <h4 class="properties-list-item__price">{{formatPropertyPrice this}}</h4> 2539 2540 {{#if (or bedroomCount livingSpaceSize bathCount fullBathCount halfBathCount threeQuarterBathCount)}} 2541 <div class="properties-list-item__icons-row"> 2542 {{#if bedroomCount}} 2543 <div class="properties-list-item__icon-tag lp-tag lp-icon icon-bed">{{bedroomCount}} Bed{{#numCompare bedroomCount 1 ">"}}s{{/numCompare}}</div> 2544 {{/if}} 2545 2546 {{#if bathCount}} 2547 <div class="properties-list-item__icon-tag lp-tag lp-icon icon-bath">{{bathCount}} Bath{{#numCompare bathCount 1 ">"}}s{{/numCompare}}</div> 2548 {{/if}} 2549 2550 {{#if livingSpaceSize}} 2551 <div class="properties-list-item__icon-tag lp-tag lp-icon icon-sqft">{{lpFormatNumber livingSpaceSize}} {{formatAreaUnit livingSpaceUnits}}</div> 2552 {{/if}} 2553 </div> 2554 {{/if}} 2555 </div> 2556 2557 <div class="properties-list-item__bottom-block"> 2558 {{#if description}} 2559 <p class="properties-list-item__bottom-label">Property description</p> 2560 <p class="properties-list-item__description">{{{truncate (striptags description) 200 '...'}}}</p> 2561 {{/if}} 2562 <span class="properties-list-item__btn lp-btn button-style-1">Property Details</span> 2563 </div> 2564 2565 </div> 2566 </a> 2567</script>
2567 2568 2569 <div class="properties-list lp-vertical-paddings" style="--titleColor: rgba(177, 132, 99, 1);"> 2570 2571 2572 2573 <div class="properties-list__collection data-container"> 2574 <a href="/properties/2148-pine-street-san-francisco-ca-us-94115-ml82043326" class="properties-list-item"> 2575 <div class="properties-list-item__image-container lp_animation-img-zoom"> 2576 <span class="properties-list-item__tag lp-tag">Sold</span> 2577 <img src="https://dlajgvw9htjpb.cloudfront.net/cms/9994eaf9-a60f-4f64-89b4-cb9e3cc349b1/ML82043326/-33160504788850245.jpg" class="properties-list-item__image" height="270" width="400" alt="2148 Pine Street" loading="lazy"> 2578 </div> 2579 2580 <div class="properties-list-item__info"> 2581 2582 <div class="properties-list-item__top-block"> 2583 <h6 class="properties-list-item__title">2148 Pine Street</h6> 2584 <p class="properties-list-item__address">2148 Pine ST, San Francisco, CA 94115</p> 2585 <h4 class="properties-list-item__price">$4,795,000</h4> 2586 2587 <div class="properties-list-item__icons-row"> 2588 <div class="properties-list-item__icon-tag lp-tag lp-icon icon-bed">4 Beds</div> 2589 2590 <div class="properties-list-item__icon-tag lp-tag lp-icon icon-bath">4 Baths</div> 2591 2592 <div class="properties-list-item__icon-tag lp-tag lp-icon icon-sqft">3,539 Sq.Ft.</div> 2593 </div> 2594 </div> 2595 2596 <div class="properties-list-item__bottom-block"> 2597 <p class="properties-list-item__bottom-label">Property description</p> 2598 <p class="properties-list-item__description">Welcome to 2148 Pine Street! Located in Lower Pacific Heights, this exceptional Victorian home offers a seamless blend of period details and modern upgrades. Features include high ceilings, crown m...</p> 2599 <span class="properties-list-item__btn lp-btn button-style-1">Property Details</span> 2600 </div> 2601 2602 </div> 2603 </a> 2604 <a href="/properties/2404-hillside-drive-burlingame-ca-us-94010-ml82012637" class="properties-list-item"> 2605 <div class="properties-list-item__image-container lp_animation-img-zoom"> 2606 <span class="properties-list-item__tag lp-tag">Sold</span> 2607 <img src="https://dlajgvw9htjpb.cloudfront.net/cms/9994eaf9-a60f-4f64-89b4-cb9e3cc349b1/ML82012637/6916827140140501019.jpg" class="properties-list-item__image" height="270" width="400" alt="2404 Hillside Drive" loading="lazy"> 2608 </div> 2609 2610 <div class="properties-list-item__info"> 2611 2612 <div class="properties-list-item__top-block"> 2613 <h6 class="properties-list-item__title">2404 Hillside Drive</h6> 2614 <p class="properties-list-item__address">2404 Hillside DR, Burlingame, CA 94010</p> 2615 <h4 class="properties-list-item__price">$3,807,500</h4> 2616 2617 <div class="properties-list-item__icons-row"> 2618 <div class="properties-list-item__icon-tag lp-tag lp-icon icon-bed">4 Beds</div> 2619 2620 <div class="properties-list-item__icon-tag lp-tag lp-icon icon-bath">4 Baths</div> 2621 2622 <div class="properties-list-item__icon-tag lp-tag lp-icon icon-sqft">2,990 Sq.Ft.</div> 2623 </div> 2624 </div> 2625 2626 <div class="properties-list-item__bottom-block"> 2627 <p class="properties-list-item__bottom-label">Property description</p> 2628 <p class="properties-list-item__description">Welcome to 2404 Hillside Drive, a beautifully Mediterranean gem in Burlingames coveted Easton Addition. This elegant 4BD/4BA home offers nearly 3,000 sq ft of refined living space with timeless cha...</p> 2629 <span class="properties-list-item__btn lp-btn button-style-1">Property Details</span> 2630 </div> 2631 2632 </div> 2633 </a> 2634 <a href="/properties/6686-mount-holly-dr-san-jose-ca-us-95120-ml81987128" class="properties-list-item"> 2635 <div class="properties-list-item__image-container lp_animation-img-zoom">
2636 <span class="properties-list-item__tag lp-tag">Sold</span> 2637 <img src="https://dlajgvw9htjpb.cloudfront.net/cms/9994eaf9-a60f-4f64-89b4-cb9e3cc349b1/ML81987128/4782704197781283678.jpg" class="properties-list-item__image" height="270" width="400" alt="6686 Mount Holly DR" loading="lazy"> 2638 </div> 2639 2640 <div class="properties-list-item__info"> 2641 2642 <div class="properties-list-item__top-block"> 2643 <h6 class="properties-list-item__title">6686 Mount Holly DR</h6> 2644 <p class="properties-list-item__address">6686 Mount Holly DR, San Jose, CA 95120</p> 2645 <h4 class="properties-list-item__price">$2,335,000</h4> 2646 2647 <div class="properties-list-item__icons-row"> 2648 <div class="properties-list-item__icon-tag lp-tag lp-icon icon-bed">4 Beds</div> 2649 2650 <div class="properties-list-item__icon-tag lp-tag lp-icon icon-bath">2 Baths</div> 2651 2652 <div class="properties-list-item__icon-tag lp-tag lp-icon icon-sqft">1,999 Sq.Ft.</div> 2653 </div> 2654 </div> 2655 2656 <div class="properties-list-item__bottom-block"> 2657 <p class="properties-list-item__bottom-label">Property description</p> 2658 <p class="properties-list-item__description">Welcome to this charming 4-bedroom, 2-bathroom home located in a highly desirable Almaden neighborhood. This nearly 2,000 sq ft spacious home is generously set on an almost 8,000 sq ft lot. The wel...</p> 2659 <span class="properties-list-item__btn lp-btn button-style-1">Property Details</span> 2660 </div> 2661 2662 </div> 2663 </a> 2664 </div> 2665 2666 <div class="pagination-container pagination-style-1"></div> 2667 </div> 2668 2669 2670 2671</section> 2672<section class="image-section is-font-color-dark is-background-color-light" id="section-f3de70dc-17c3-43de-a8ed-1cf5c7484236" data-el-id="1ed7f92a-2189-48c5-889c-3838656da504"> 2673 <div class="newsletter-contact-cta lp-vertical-paddings" 2674 style=" 2675 --titleColorFirst: rgba(25, 47, 56, 1); 2676 --newsletterBack: rgba(248, 248, 248, 1); 2677 --titleColorSecond: rgba(25, 47, 56, 1); 2678 --workWithBack: rgba(255, 255, 255, 1); 2679 --newsletterTextColor: rgba(25, 47, 56, 1); 2680 --rightBack: rgba(255, 255, 255, 0); 2681 " 2682> 2683 <div class="newsletter-contact-cta__right-back"></div> 2684 <div class="newsletter-contact-cta__container lp-container"> 2685 <div class="newsletter-contact-cta__wrapper"> 2686 <div class="newsletter-contact-cta__block newsletter-contact-cta__block--newsletter"> 2687 <div class="newsletter-contact-cta__heading"> 2688 <div class="newsletter-contact-cta__pre-title newsletter-contact-cta__pre-title--newsletter lp-text--pretitle">Join Our</div> 2689 <h2 class="newsletter-contact-cta__title newsletter-contact-cta__title--newsletter newsletter-contact-cta__title--with-pretitle">Newsletter</h2> 2690 <div class="newsletter-contact-cta__description"><p>Stay up-to-date with exclusive news and market updates.</p></div> 2691 </div> 2692 <form data-type="contact-form" class="newsletter-contact-cta__form"> 2693 <div class="newsletter-contact-cta__form-wrapper"> 2694 <div class="newsletter-contact-cta__input-group lp-input-group"> 2695 <div class="h-pot"> 2696 <label for="section-f3de70dc-17c3-43de-a8ed-1cf5c7484236-middleName">Middle Name</label> 2697 <input 2698 id="section-f3de70dc-17c3-43de-a8ed-1cf5c7484236-middleName" 2699 name="middleName" 2700 value="" 2701 autocomplete="off" 2702 tabindex="-1" 2703 /> 2704 </div> 2705 <label for="section-f3de70dc-17c3-43de-a8ed-1cf5c7484236__wwu-email" class="newsletter-contact-cta__label lp-label">Enter Your Email Address</label> 2706 <input 2707 id="section-f3de70dc-17c3-43de-a8ed-1cf5c7484236__wwu-email" 2708 name="email" 2709 type="email" 2710 class="newsletter-contact-cta__text-input lp-input" 2711 required 2712 placeholder="Enter Your Email Address" 2713 pattern="[a-zA-Z0-9.!#$%&â*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)+" 2714 /> 2715 <div class="lp-icon lp-input-icon lp-input-icon--mail"></div> 2716 </div> 2717 <div class="newsletter-contact-cta__disclaimer lp-disclaimer"> <label style='display: none;' for='section-f3de70dc-17c3-43de-a8ed-1cf5c7484236-termsAccepted'>Opt In/Disclaimer Consent:</label> 2718 <label class='lp-tcr-content'> 2719 <span class='lp-tcr-checkbox'> 2720 <input 2721 type='checkbox' 2722 id='section-f3de70dc-17c3-43de-a8ed-1cf5c7484236-termsAccepted' 2723 name='termsAccepted' 2724 oninvalid="this.setCustomValidity('Please accept the terms and conditions')"
2725 oninput="this.setCustomValidity('')" 2726 required 2727 /></span> 2728 <span class='lp-tcr-message'> 2729 2730 I agree to be contacted by 2731 Reali 2732 via call, email, and text for real estate services. To opt out, you can reply 'stop' at any time or reply 'help' for assistance. 2733 You can also click the unsubscribe link in the emails. Message and data rates may apply. Message frequency may vary. 2734 <a href='/terms-and-conditions' target='_blank'>Privacy Policy</a>. 2735 </span> 2736 </label> 2737 2738</div> 2739 <input style="display: none;" type="text" name="source" value="NEWSLETTER_SIGNUP" /> 2740 <button type="submit" class="newsletter-contact-cta__btn lp-btn lp-btn--mob-full-width lp-icon button-style-1"> 2741 <span class="lp-btn__content">Subscribe</span> 2742 </button> 2743 </div> 2744 </form> 2745 </div> 2746 <div class="newsletter-contact-cta__block newsletter-contact-cta__block--touch"> 2747 <div class="newsletter-contact-cta__heading"> 2748 <div class="newsletter-contact-cta__pre-title newsletter-contact-cta__pre-title--touch lp-text--pretitle">Get in Touch</div> 2749 <h2 class="newsletter-contact-cta__title newsletter-contact-cta__title--touch newsletter-contact-cta__title--with-pretitle">Work With Us</h2> 2750 <div class="newsletter-contact-cta__description"><p>Join a brokerage where kindness and collaboration are core values, not just buzzwords. We cultivate a supportive national community where top-producing agents actively share best practices, knowledge, and referrals.</p></div> 2751 </div> 2752 <div class="newsletter-contact-cta__btn-wrap"> 2753 <a 2754 role="button" 2755 data-type="CONTACT_US" 2756 class="newsletter-contact-cta__btn lp-btn button-style-1" 2757 2758 2759 2760 2761 2762 > 2763 <span class="lp-btn__content">Contact Us</span> 2764 </a> 2765 </div> 2766 <div class="newsletter-contact-cta__word-divide"><span class="newsletter-contact-cta__word-word">or</span></div> 2767 <div class="newsletter-contact-cta__call-cell"> 2768 <span class="newsletter-contact-cta__prefix-cell">Call us at</span> 2769 <a 2770 href="tel:8558467334" 2771 class="newsletter-contact-cta__link lp-a lp-link hyperlink-style-1" 2772 > 2773 <span class="newsletter-contact-cta__phone-wrapper lp-icon link-icon--phone"></span> 2774 (855) 846-7334 2775 </a> 2776 </div> 2777 </div> 2778 </div> 2779 </div> 2780</div> 2781</section> 2782<section id="section-9f328597-3b81-4ba9-bfda-00595329a24d" class="solid-section is-font-color-dark is-background-color-light" data-el-id="85dc9e17-a4c7-449c-8890-869fdc11b485"> 2783 <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@splidejs/[email protected]/dist/css/splide-core.min.css"/>
2784<script src="https://cdn.jsdelivr.net/npm/@splidejs/[email protected]/dist/js/splide.min.js"></script>
vendor: 1 bytes, line 2784
2784
2785<script type="text/x-handlebars-template" class="jsIGTemplate"> 2786 {{#each items}} 2787 <a href="{{permalink}}" target="_blank" class="instagram-feed__item lp_animation-img-zoom splide__slide insta-item insta-type-{{mediaType}}"> 2788 <img class="instagram-feed__image" src="{{#ifEquals mediaType 'VIDEO'}}{{thumbnailUrl}}{{else}}{{mediaUrl}}{{/ifEquals}}" alt="Instagram post by @{{username}}"> 2789 </a> 2790 {{/each}} 2791</script>
2791 2792 2793<div class="instagram-feed lp-vertical-paddings"> 2794 <div class="instagram-feed__container" style="--titleColor: #B18463; --boxColor: rgba(238, 237, 233, 1);"> 2795 <div class="instagram-feed__title-group lp-title-group"> 2796 <div class="instagram-feed__icon lp-icon"></div> 2797 <p class="instagram-feed__pretitle">Follow Us On Instagram</p> 2798 <h2 class="instagram-feed__title instagram-feed__title--with-pretitle"><a href="#" target="_blank" class="instagram-feed__link jsIGLink jsIGName"></a></h2> 2799 </div> 2800 2801 <div class="instagram-feed__collection splide js-slider"> 2802 <div class="splide__track"> 2803 <ul class="splide__list jsIGContainer"> 2804 </ul> 2805 </div> 2806 </div> 2807 </div> 2808</div> 2809 2810 2811 2812 2813</section> 2814 2815 <div id="modals" class="modals"></div> 2816 <footer id="global-footer" class="is-font-color-dark is-background-color-light" data-el-id="b8c9ffd0-3b1f-4577-96fc-828efdac86e7"> 2817 2818 2819<div class="footer"> 2820 <div class="footer-logos-row"> 2821 <img src="https://media-production.lp-cdn.com/cdn-cgi/image/format=auto,quality=85/https://media-production.lp-cdn.com/media/4b06c355-f579-44e2-9a1f-0e362718eaca" srcset="https://media-production.lp-cdn.com/cdn-cgi/image/format=auto,quality=85,fit=scale-down,width=320/https://media-production.lp-cdn.com/media/4b06c355-f579-44e2-9a1f-0e362718eaca 320w, https://media-production.lp-cdn.com/cdn-cgi/image/format=auto,quality=85/https://media-production.lp-cdn.com/media/4b06c355-f579-44e2-9a1f-0e362718eaca 943w" class="footer-logo" sizes="350px" alt=""> 2822 2823 </div> 2824 <div class="footer-contact-row"> 2825 <div class="footer-info "> 2826 <div class="footer__col"> 2827 2828 <div class="footer__section-content"> 2829 <div class="footer__col-content"> 2830 <p class="footer__col-title">Contact</p> <div class="footer__col-description lp-a lp-pre-line"><div><a href="tel:8558467334">(855) 846-7334</a><br><a href="/cdn-cgi/l/email-protection#ec80858f89829f85828bac9e898d8085c28f8381"><span class="__cf_email__" data-cfemail="e4888d87818a978d8a83a4968185888dca878b89">[email protected]</span></a></div></div> 2831 </div> 2832 2833 <div class="footer__col-content"> 2834 <p class="footer__col-title">Office</p> <div class="footer__col-description lp-a lp-pre-line"><style type="text/css"><!--td {border: 1px solid #cccccc;}br {mso-data-placement:same-cell;}--></style> 2835 <p><span data-sheets-root="1"><strong>WA </strong>- 39899 Balentine Dr, Suite 200, Newark, CA 94560 <br><strong>CA </strong>- 1420 Kettner Blvd, Suite 100, San Diego, CA 92101 <br><strong>MA</strong> - 90 Canal Street, Boston, MA 02114 <br><strong>TX </strong>- 2626 Cole Ave, Suite 300, Dallas, TX 75204 </span></p></div> 2836 </div> 2837 </div> 2838 </div> 2839 2840 2841 2842 </div> 2843 2844 <ul class="lp-socials social_icons-style-1 footer-socials"> 2845 2846 <li> 2847 <a 2848 href="https://www.linkedin.com/company/reali" 2849 class="lp-socials__link socials__link" 2850 data-wow-delay="0s" 2851 target="_blank" 2852 aria-label="LINKEDIN" 2853 > 2854 <span class="lp-i lp-i--linkedin" aria-hidden="true"></span> 2855 </a> 2856 </li> 2857 </ul> </div> 2858 2859 2860 <div class="footer-navigation-row"> 2861 <p class="footer-navigation-title">navigation</p> 2862 2863 <ul class="footer-navigation lp-a"> 2864 <li class="footer-navigation__item"> 2865 <a href="/" class="footer-navigation__link ">Homepage</a> 2866 2867 </li> 2868 <li class="footer-navigation__item"> 2869 <a href="/agents" class="footer-navigation__link ">Meet Our Agents</a> 2870 2871 </li> 2872 <li class="footer-navigation__item"> 2873 <a href="/properties/sale" class="footer-navigation__link ">Featured Properties</a> 2874 2875 </li> 2876 <li class="footer-navigation__item"> 2877 <a href="/home-valuation" class="footer-navigation__link ">Home Valuation</a> 2878 2879 </li> 2880 <li class="footer-navigation__item"> 2881 2882 <a data-type="CONTACT_US" type="button" role="button" class="footer-navigation__link " >Contact Us</a> 2883 </li> 2884 </ul> 2885 </div> 2886 2887 2888 2889 <div class="footer-disclaimer-row"> 2890 <div class="footer-disclaimer"><p style="font-size: 14px;">Reali | CA DRE #02022092</p> 2891<p style="font-size: 14px;"><a class="link" style="font-weight: 400;" href="https://d1e1jt2fj4r8r.cloudfront.net/b8947b11-20b5-447c-afdb-bd8d92c8512d/BwYS9e60W/Texas%20ConsumerProtection%20Notice.pdf" target="_blank" rel="noopener">Texas Real Estate Commission Consumer Protection Notice</a><br><a style="font-weight: 400;" target="_blank" rel="noopener">Texas Real Estate Commission Information About Brokerage Services</a><br><a class="link" style="font-weight: 400;" href="https://d1e1jt2fj4r8r.cloudfront.net/938208c7-113c-4b76-96bb-f65cefa93252/pkR5n4vC6/trec.pdf" target="_blank" rel="noopener">TREC Disclaimer</a></p> 2892<p style="font-size: 12px;">All information is deemed reliable but not guaranteed and should be independently reviewed and verified. <br><img style="margin-top: 20px; filter: invert(0);" src="https://media-production.lp-cdn.com/cdn-cgi/image/format=auto,quality=85/https://media-production.lp-cdn.com/media/c0475fb5-4e0c-4da3-b1cd-e482baddacf5" width="200px;"></p></div> 2893 2894 2895 </div> 2896 2897 2898 <div class="footer-bottom-row"> 2899 <img src="https://media-production.lp-cdn.com/cdn-cgi/image/format=auto,quality=85/https://media-production.lp-cdn.com/media/jnymuan6tigs629jigsz" srcset="https://media-production.lp-cdn.com/cdn-cgi/image/format=auto,quality=85,fit=scale-down,width=320/https://media-production.lp-cdn.com/media/jnymuan6tigs629jigsz 320w, https://media-production.lp-cdn.com/cdn-cgi/image/format=auto,quality=85/https://media-production.lp-cdn.com/media/jnymuan6tigs629jigsz 390w" class="footer-logo" sizes="350px" alt=""> 2900 2901 <div class="footer-copyright lp-a"> 2902 <div class="footer-copyright__powered"> 2903 Powered by <a href="https://www.luxurypresence.com/" class="" target="_blank">Luxury Presence</a> 2904 </div> 2905 2906 <div class="footer-copyright__privacy">
2907 <span>Copyright © <span class="auto-year-update">2026</span></span> 2908 2909 2910 <a href="/terms-and-conditions" class="footer-copyright__privacy-policy">Privacy Policy</a> 2911 </div> 2912 </div> 2913 </div> 2914 2915 <div class="footer-mls-disclaimers lp-footer-disclaimers"> 2916 2917 2918 2919 <div class="disclaimer-item"><div class="cmpl-logo" data-feed-id="mlsgrid_nwmls"><img src="https://res.cloudinary.com/luxuryp/images/f_auto,q_auto/pv3nzs2ntl6l765u6u0g/symbol-color-nwmls " alt="" height="50" loading="lazy"/></div><div class="cmpl-disclaimer" data-feed-id="mlsgrid_nwmls"><p style="text-align: center;"><span style="font-size: 8pt; font-weight: 400;">The IDX display presents information sourced from the Northwest Multiple Listing Service as of <span class="cmpl-date">9/21/2026</span>. The data is intended for personal, non-commercial use and should not be used for any other purpose except to identify potential properties for purchase. While the MLS data displayed is generally deemed reliable, it is NOT guaranteed to be accurate by the MLS. Buyers are responsible for verifying the accuracy of all information and are advised to conduct their own investigations or seek professional assistance. Other sources besides the Listing Agent may have contributed to the MLS data presented. Unless expressly specified in writing, the Broker/Agent has not confirmed any information obtained from external sources. The Broker/Agent, may or may not have acted as the Listing and/or Selling Agent and cannot guarantee the accuracy of property locations displayed on any map. The property locations displayed on any map are merely best approximations and exact locations should be independently verified. Based on information submitted to the MLS GRID as of <span class="cmpl-date">9/21/2026</span> at <span class="cmpl-time">2:33 AM UTC</span> . All data is obtained from various sources and may not have been verified by broker or MLS GRID. Supplied Open House Information is subject to change without notice. All information should be independently reviewed and verified for accuracy. Properties may or may not be listed by the office/agent presenting the information.</span> 2920 2921</p><p style="text-align: center;"><span style="font-size: 10pt;"><strong>CA DRE# 01989218</strong></span> 2922 2923</p><p style="text-align: center;"><span style="font-size: 8pt; font-weight: 400;">©2026 Northwest Multiple Listing Service all rights reserved.</span> 2924 2925</p><p style="text-align: center;"><span style="font-size: 8pt;"><img src="https://res.cloudinary.com/luxuryp/images/f_auto,q_auto/pv3nzs2ntl6l765u6u0g/symbol-color-nwmls" alt="" width="63" height="62"></span></p></div></div> 2926 </div> 2927</div> 2928 2929 </footer> 2930 2931 <div id="modal-global-contact-us" class="modal " 2932 data-el-id="" 2933 style="background-color: ;" 2934 > 2935 <div class="modal-content"> 2936 2937 2938 <div class="contact-form-modal lp-vertical-paddings" style="--leftPanelBgColor: rgba(248, 248, 248, 1); --textColor:rgba(25, 47, 56, 1); --formBgColor: rgba(255, 255, 255, 1); --formTextColor: rgba(25, 47, 56, 1)"> 2939 <div class="contact-form-modal__bg"> 2940 <img src="https://media-production.lp-cdn.com/cdn-cgi/image/format=auto,quality=85,fit=scale-down,width=1280/https://media-production.lp-cdn.com/media/52ef4b6d-0e80-4c60-acb1-711f462e3f3d" srcset="https://media-production.lp-cdn.com/cdn-cgi/image/format=auto,quality=85,fit=scale-down,width=320/https://media-production.lp-cdn.com/media/52ef4b6d-0e80-4c60-acb1-711f462e3f3d 320w, https://media-production.lp-cdn.com/cdn-cgi/image/format=auto,quality=85,fit=scale-down,width=960/https://media-production.lp-cdn.com/media/52ef4b6d-0e80-4c60-acb1-711f462e3f3d 960w, https://media-production.lp-cdn.com/cdn-cgi/image/format=auto,quality=85,fit=scale-down,width=1280/https://media-production.lp-cdn.com/media/52ef4b6d-0e80-4c60-acb1-711f462e3f3d 1280w, https://media-production.lp-cdn.com/cdn-cgi/image/format=auto,quality=85/https://media-production.lp-cdn.com/media/52ef4b6d-0e80-4c60-acb1-711f462e3f3d 1920w" sizes="(min-width: 1981px) 12.2222vw, (min-width: 1025px) 424px, 100vw" class="contact-form-modal__bg-image" alt=""> 2941 </div> 2942 <div class="contact-form-modal__close-container"> 2943 <button class="contact-form-modal__close toggle close lp-btn lp-icon link-icon--plus"></button> 2944 </div> 2945 <div class="contact-form-modal__container"> 2946 <div class="contact-form-modal-details only-desktop"> 2947 <h2 class="contact-form-modal-details__title only-desktop js-contact-title">Get in Touch</h2> 2948 <div class="contact-form-modal-agent"> 2949 <div class="contact-form-modal-agent__contact"> 2950 <p class="contact-form-modal-agent__name">Reali | CA DRE #02022092</p> 2951 <p class="contact-form-modal-agent__phone lp-a"> 2952 <a class="contact-form-modal-agent__link" href="tel:8558467334">(855) 846-7334</a> 2953 </p> 2954 <p class="contact-form-modal-agent__email lp-a"> 2955 <a class="contact-form-modal-agent__link" href="/cdn-cgi/l/email-protection#9ff3f6f
2955cfaf1ecf6f1f8dfedfafef3f6b1fcf0f2"><span class="__cf_email__" data-cfemail="2b4742484e455842454c6b594e4a474205484446">[email protected]</span></a> 2956 </p> 2957 </div> 2958 <div class="contact-form-modal-agent__contact"> 2959 <p class="contact-form-modal-agent__addres lp-pre-line">WA - 39899 Balentine Dr, Suite 200, Newark, CA 94560 2960 CA - 1420 Kettner Blvd, Suite 100, San Diego, CA 92101 2961 MA - 90 Canal Street, Boston, MA 02114 2962 TX - 2626 Cole Ave, Suite 300, Dallas, TX 75204</p> 2963 </div> 2964 </div> 2965 2966 2967 2968 2969 <div class="contact-form-modal-details__socials"> 2970 <ul class="lp-socials social_icons-style-1 contact-form-modal-details__socials-list lp-socials"> 2971 2972 <li> 2973 <a 2974 href="https://www.instagram.com/" 2975 class="lp-socials__link contact-form-modal-details__socials-link" 2976 data-wow-delay="0s" 2977 target="_blank" 2978 aria-label="INSTAGRAM" 2979 > 2980 <span class="lp-i lp-i--instagram" aria-hidden="true"></span> 2981 </a> 2982 </li> 2983 </ul> </div> 2984 </div> 2985 <div class="contact-form-modal-form"> 2986 <h2 class="contact-form-modal-form__title only-mobile js-contact-title">Get in Touch</h2> 2987 <form action="" class="contact-form-modal-form__form js-form" data-type="contact-form"> 2988 <div class="lp-h-pot"> 2989 <label for="-if-middleName">Middle Name</label> 2990 <input id="-if-middleName" name="middleName" value="" autocomplete="off" tabindex="-1"> 2991 </div> 2992 <div class="contact-form-modal-form__group"> 2993 <div class="contact-form-modal-form__group-container lp-input-group"> 2994 <input required id="-name" name="name" type="text" class="lp-input js-input-name" placeholder="Enter your full name"> 2995 <label for="-name" class="lp-label contact-form-modal-form__label">Name</label> 2996 </div> 2997 <div class="contact-form-modal-form__group-container lp-input-group"> 2998 <input required id="-phone" name="phone" type="tel" class="lp-input" placeholder="Your phone"> 2999 <label for="-phone" class="lp-label contact-form-modal-form__label">Phone</label> 3000 </div> 3001 </div> 3002 <div class="contact-form-modal-form__group lp-input-group"> 3003 <div class="contact-form-modal-form__input-container"> 3004 <input required id="-email" name="email" type="email" class="lp-input js-email-input" placeholder="@"> 3005 <div class="contact-form-modal-form__error error">Enter a valid email</div> 3006 </div> 3007 3008 <label for="-email" class="contact-form-modal-form__label lp-label">Email</label> 3009 </div> 3010 <div class="contact-form-modal-form__group lp-input-group"> 3011 <textarea id="-message" name="message" class="contact-form-modal-form__textarea lp-input" placeholder="Type your message"></textarea> 3012 <label for="-message" class="contact-form-modal-form__label lp-label">Your Message</label> 3013 </div> 3014 3015 <div class="contact-form-modal-form__disclaimer lp-disclaimer"> <label style='display: none;' for='-termsAccepted'>Opt In/Disclaimer Consent:</label> 3016 <label class='lp-tcr-content'>
3017 <span class='lp-tcr-checkbox'> 3018 <input 3019 type='checkbox' 3020 id='-termsAccepted' 3021 name='termsAccepted' 3022 oninvalid="this.setCustomValidity('Please accept the terms and conditions')" 3023 oninput="this.setCustomValidity('')" 3024 required 3025 /></span> 3026 <span class='lp-tcr-message'> 3027 3028 I agree to be contacted by 3029 Reali 3030 via call, email, and text for real estate services. To opt out, you can reply 'stop' at any time or reply 'help' for assistance. 3031 You can also click the unsubscribe link in the emails. Message and data rates may apply. Message frequency may vary. 3032 <a href='/terms-and-conditions' target='_blank'>Privacy Policy</a>. 3033 </span> 3034 </label> 3035 3036 </div> 3037 3038 <button type="submit" class="contact-form-modal-submit lp-btn lp-icon button-style-1"> 3039 <div class="contact-form-modal-submit__default"> 3040 <span class="contact-form-modal-submit__content lp-btn__content">Send Message</span> 3041 </div> 3042 <div class="contact-form-modal-submit__loading"> 3043 <span class="contact-form-modal-submit__content lp-btn__content">Sending</span> 3044 </div> 3045 <div class="contact-form-modal-submit__success"> 3046 <span class="contact-form-modal-submit__content lp-btn__content">Message Sent!</span> 3047 </div> 3048 </button> 3049 </form> 3050 </div> 3051 3052 <div class="contact-form-modal-details only-mobile"> 3053 <h2 class="contact-form-modal-details__title only-desktop js-contact-title">Get in Touch</h2> 3054 <div class="contact-form-modal-agent"> 3055 <div class="contact-form-modal-agent__contact"> 3056 <p class="contact-form-modal-agent__name">Reali | CA DRE #02022092</p> 3057 <p class="contact-form-modal-agent__phone lp-a"> 3058 <a class="contact-form-modal-agent__link" href="tel:8558467334">(855) 846-7334</a> 3059 </p> 3060 <p class="contact-form-modal-agent__email lp-a"> 3061 <a class="contact-form-modal-agent__link" href="/cdn-cgi/l/email-protection#f39f9a90969d809a9d94b38196929f9add909c9e"><span class="__cf_email__" data-cfemail="65090c06000b160c0b0225170004090c4b060a08">[email protected]</span></a> 3062 </p> 3063 </div> 3064 <div class="contact-form-modal-agent__contact"> 3065 <p class="contact-form-modal-agent__addres lp-pre-line">WA - 39899 Balentine Dr, Suite 200, Newark, CA 94560 3066 CA - 1420 Kettner Blvd, Suite 100, San Diego, CA 92101 3067 MA - 90 Canal Street, Boston, MA 02114 3068 TX - 2626 Cole Ave, Suite 300, Dallas, TX 75204</p> 3069 </div> 3070 </div> 3071 3072 3073 3074 3075 <div class="contact-form-modal-details__socials"> 3076 <ul class="lp-socials social_icons-style-1 contact-form-modal-details__socials-list lp-socials"> 3077 3078 <li> 3079 <a 3080 href="https://www.instagram.com/" 3081 class="lp-socials__link contact-form-modal-details__socials-l
3081ink" 3082 data-wow-delay="0s" 3083 target="_blank" 3084 aria-label="INSTAGRAM" 3085 > 3086 <span class="lp-i lp-i--instagram" aria-hidden="true"></span> 3087 </a> 3088 </li> 3089 </ul> </div> 3090 </div> 3091 </div> 3092 </div> 3093 3094 </div> 3095 <button class="close lp-close" type="button" aria-label="close modal"> 3096 <span class="lp-close-icon" aria-hidden="true"></span> 3097 </button> 3098 </div> 3099 <div id="qualifying-questions-modal" tabindex="-1" role="dialog" aria-modal="true" aria-labelledby="qualifying-questions-modal-title" aria-hidden="true" style="position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.7); z-index: 1050; display: none; align-items: center; justify-content: center;"> 3100 <style> 3101 #qualifying-questions-modal .custom-select-toggle { 3102 border: 1px solid #848484; 3103 border-radius: 8px; 3104 padding: 12px 16px; 3105 } 3106 3107 #qualifying-questions-modal .custom-select-toggle.is-placeholder { 3108 color: #848484; 3109 } 3110 3111 #qualifying-questions-modal #qualifying-questions-modal-phoneNumber::placeholder { 3112 color: #848484; 3113 opacity: 1; 3114 } 3115 3116 #qualifying-questions-modal .disclaimer-more { 3117 display: none; 3118 } 3119 3120 #qualifying-questions-modal #qualifying-questions-modal-disclaimer-text:not(.collapsed) .disclaimer-more { 3121 display: inline; 3122 } 3123 3124 #qualifying-questions-modal #qualifying-questions-modal-disclaimer-text:not(.collapsed) .disclaimer-ellipsis { 3125 display: none; 3126 } 3127 </style>
vendor: 119 bytes, lines 3127-3128
3127 3128 <script data-cfasync="false" src="/cdn-cgi/scripts/5c5dd728/cloudflare-static/email-decode.min.js"></script>
3128<script> 3129 function toggleDisclaimerQualifyingQuestionsModal() { 3130 const disclaimerText = document.getElementById('qualifying-questions-modal-disclaimer-text'); 3131 const toggleButton = document.getElementById('qualifying-questions-modal-toggle-disclaimer'); 3132 3133 if (disclaimerText.classList.contains('collapsed')) { 3134 disclaimerText.classList.remove('collapsed'); 3135 toggleButton.textContent = 'Read less'; 3136 toggleButton.setAttribute('aria-expanded', 'true'); 3137 } else { 3138 disclaimerText.classList.add('collapsed'); 3139 toggleButton.textContent = 'Read more'; 3140 toggleButton.setAttribute('aria-expanded', 'false'); 3141 } 3142 } 3143 </script>
3143 3144 <div style="max-width: 400px; width: 95%; margin: 0 auto;"> 3145 <div style="background: #fff; border: none; border-radius: 16px; color: black; box-shadow: none; position: relative;"> 3146 <button type="button" data-dismiss="modal" aria-label="Close" style="position: absolute; right: 20px; top: 10px; font-size: 28px; font-weight: 300; color: black; opacity: 1; background: none; border: none; padding: 0; width: auto; height: auto; cursor: pointer; z-index: 10;"> 3147 <span aria-hidden="true">×</span> 3148 </button> 3149 <div style="padding: 40px 30px 32px;"> 3150 <div style="text-align: left; margin-bottom: 32px;"> 3151 <h2 id="qualifying-questions-modal-title" style="margin: 0; color: #333; font-size: 18px; line-height: 1.4; text-transform: uppercase; letter-spacing: 0.5px; font-weight: normal;"> 3152 Thanks, please provide more information to help serve you 3153 </h2> 3154 </div> 3155 <form id="qualifying-questions-modal-form" data-type="contact-form"> 3156 <input type="hidden" name="source" value="PROFILE_UPDATE" /> 3157 <input type="hidden" name="email" value="" id="qualifying-questions-modal-email" /> 3158 <input type="hidden" name="triggerSource" id="qualifying-questions-modal-triggerSource" value="" /> 3159 <label for="qualifying-questions-modal-email" style="display:none;"> 3160 Email 3161 </label> 3162 <label for="qualifying-questions-modal-triggerSource" style="display:none;"> 3163 Trigger Source 3164 </label> 3165 3166 <div style="margin-bottom: 20px;"> 3167 <label for="qualifying-questions-modal-phoneNumber" style="display:none;"> 3168 Phone Number 3169 </label> 3170 <input 3171 type="tel" 3172 id="qualifying-questions-modal-phoneNumber" 3173 name="phoneNumber" 3174 style="padding: 12px 16px; font-size: 16px; border: 1px solid #848484; border-radius: 8px; background: transparent; color: black; width: 100%; box-sizing: border-box; outline: none;" 3175 pattern="^\+?1?[-.\s]?\(?[0-9]{3}\)?[-.\s]?[0-9]{3}[-.\s]?[0-9]{4}$" 3176 required 3177 placeholder="Phone" 3178 aria-label="Phone Number" 3179 inputmode="tel" 3180 autocomplete="tel" 3181 onfocus="this.style.borderColor='black';" 3182 onblur="this.style.borderColor='#848484';" 3183 /> 3184 </div> 3185 3186 <div style="margin-bottom: 20px;"> 3187 <label for="qualifying-questions-modal-interest" style="display:none;"> 3188 Interest 3189 </label> 3190 <select 3191 id="qualifying-questions-modal-interest" 3192 name="interest" 3193 required 3194 data-label="Interest" 3195 aria-label="Interest" 3196 style="padding: 12px 16px; font-size: 16px; border: 1px solid #848484; border-radius: 8px; background: #fff; color: #848484; width: 100%; box-sizing: border-box; outline: none; appearance: none; -webkit-appearance: none; -moz-appearance: none; background-image: url('data:image/svg+xml;charset=utf8,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 16 16%22%3E%3Cpath fill=%22%23666%22 d=%22M4.427 7.427l3.396 3.396a.25.25 0 00.354 0l3.396-3.396A.25.25 0 0011.396 7H4.604a.25.25 0 00-.177.427z%22/%3E%3C/svg%3E'); background-repeat: no-repeat; background-position: right 16px center; background-size: 16px;" 3197 onfocus="this.style.borderColor='black'; this.style.color='black';" 3198 onblur="this.style.borderColor='#848484'; if(this.value === '') this.style.color='#848484'; else this.style.color='black';" 3199 > 3200 <option value="" style="background: #fff; color: #848484;" disabled selected>Interested in...</option> 3201 <option value="Selling & Buying" style="background: #fff; color: black;">Selling & Buying</option> 3202 <option value="Selling" style="background: #fff; color: black;">Selling</option> 3203 <option value="Buying" style="background: #fff; color: black;">Buying</option> 3204 <option value="Renting" style="background: #fff; color: black;">Renting</option> 3205 <option value="Other" style="background: #fff; color: black;">Other</option> 3206 </select> 3207 </div> 3208 3209 <div style="margin-bottom: 32px; font-size: 12px; line-height: 1.2;"> 3210 <label style='display: none;' for='qualifying-questions-modal-qualifying-questions-termsAccepted'>Opt In/Disclaimer Consent:</label> 3211 <label class='lp-tcr-content'>
3212 <span class='lp-tcr-checkbox'> 3213 <input 3214 type='checkbox' 3215 id='qualifying-questions-modal-qualifying-questions-termsAccepted' 3216 name='termsAccepted' 3217 aria-describedby='qualifying-questions-modal-disclaimer-text' 3218 oninvalid="this.setCustomValidity('Please accept the terms and conditions')" 3219 oninput="this.setCustomValidity('')" 3220 required 3221 /></span> 3222 <span class='lp-tcr-message'> 3223 <div id="qualifying-questions-modal-disclaimer-text" class="collapsed" style="color: #848484;"> 3224 3225 I agree to be contacted by 3226 <strong>Reali</strong> 3227 via call, email, and text <span class="disclaimer-more">for real estate services. 3228 To opt out, you can reply 'stop' at any time or reply 'help' for assistance. 3229 You can also click the unsubscribe link in the emails. 3230 Message and data rates may apply. Message frequency may vary.</span> 3231 <span class="disclaimer-ellipsis">...</span> 3232 <a href='/terms-and-conditions' target='_blank' rel='noopener noreferrer' style='text-decoration: underline;'>Privacy Policy</a>. 3233 </div> 3234 <button 3235 id="qualifying-questions-modal-toggle-disclaimer" 3236 type="button" 3237 style="background: none; border: none; color: #848484; font-size: inherit; font-weight: bold; padding: 0; margin-top: 4px; cursor: pointer;" 3238 aria-expanded="false" 3239 onclick="toggleDisclaimerQualifyingQuestionsModal()" 3240 > 3241 Read more 3242 </button> 3243 </span> 3244 </label> 3245 </div> 3246 3247 <div> 3248 <button 3249 type="submit" 3250 style="padding: 12px 24px; font-size: 16px; font-weight: normal; border-radius: 8px; border: 1px solid black; background: black; color: white; width: 100%; cursor: pointer; transition: all 0.2s ease; text-transform: uppercase; letter-spacing: 0.5px;" 3251 onmouseover="this.style.background='transparent'; this.style.color='black';" 3252 onmouseout="this.style.background='black'; this.style.color='white';" 3253 > 3254 Submit 3255 </button> 3256 </div> 3257 </form> 3258 </div> 3259 </div> 3260 </div> 3261 </div> 3262 3263
3263<script> 3264 (function() { 3265 var lazyVideos = [].slice.call(document.querySelectorAll("video.lazy")); 3266 3267 if ("IntersectionObserver" in window && lazyVideos.length > 0) { 3268 var lazyVideoObserver = new IntersectionObserver(function(entries, observer) { 3269 entries.forEach(function(video) { 3270 if (video.isIntersecting) { 3271 for (var source in video.target.children) { 3272 var videoSource = video.target.children[source]; 3273 if (typeof videoSource.tagName === "string" && videoSource.tagName === "SOURCE") { 3274 videoSource.src = videoSource.dataset.src; 3275 } 3276 } 3277 3278 video.target.load(); 3279 video.target.classList.remove("lazy"); 3280 lazyVideoObserver.unobserve(video.target); 3281 } 3282 }); 3283 }); 3284 3285 lazyVideos.forEach(function(lazyVideo) { 3286 lazyVideoObserver.observe(lazyVideo); 3287 }); 3288 } 3289 })(); 3290 </script>
3290 3291 3292 <!-- Global site tag (gtag.js) - Google Analytics --> 3293
3293<script defer src="https://www.googletagmanager.com/gtag/js"></script>
3293 3294 3295 <!-- Google Tag Manager --> 3296
3296<script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start': 3297 new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0], 3298 j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.defer=true;j.async=false;j.src= 3299 'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f); 3300 })(window,document,'script','dataLayer','GTM-T9SFQGHZ');</script>
3300 3301 <!-- End Google Tag Manager --> 3302 3303 3304 <!-- LP Tracker (divolte.js) - LP Divolte --> 3305
3305<script>window.divolteLp=window.divolteLp||function(){(divolteLp.q=divolteLp.q||[]).push(arguments)}</script>
3305 3306 <!-- LP Tracker (eventBus) - LP EventBus --> 3307
3307<script>window.eventBusLp=window.eventBusLp||function(){(eventBusLp.q=eventBusLp.q||[]).push(arguments)}</script>
3307 3308 3309
3309<script> 3310 window.dataLayer = window.dataLayer || []; 3311 document.sendGoogleTagManagerEvent = function() { 3312 /* Honor GPC-enabled browsers and prevent GTM event if GPC is enabled. More details: https://global-privacy-control.glitch.me/ */ 3313 if (window.navigator.globalPrivacyControl) { 3314 return; 3315 } 3316 3317 dataLayer.push(arguments); 3318 } 3319 3320 document.sendGoogleTagManagerEvent('js', new Date()); 3321 document.sendGoogleTagManagerEvent('config', 'G-2NCXVR1P16', { page: location.pathname, location: location.href }); 3322 document.sendGoogleAnalyticsEvent = function(googleEvent) { 3323 document.sendGoogleTagManagerEvent('event', googleEvent.eventAction || googleEvent.hitType, { 3324 event_category: googleEvent.eventCategory, 3325 event_label: googleEvent.eventLabel, 3326 value: googleEvent.eventValue, 3327 page: googleEvent.page, 3328 location: googleEvent.location 3329 }) 3330 }; 3331 3332 document.addEventListener("DOMContentLoaded", function() { 3333 (function() { 3334 //If the page has an element with ID of auto-year-update the element will be populated with the current year. 3335 var date = new Date(); 3336 var elements = document.getElementsByClassName('auto-year-update'); 3337 var i; 3338 for (i = 0; i < elements.length; i++) { 3339 elements[i].innerText = date.getFullYear(); 3340 } 3341 })(); 3342 if (window.divolteLp) { 3343 window.divolteLp('sendLpPageView', {}); 3344 } 3345 if (window.eventBusLp) { 3346 window.eventBusLp('sendLpPageView', {}); 3347 } 3348 }); 3349 </script>
3349 3350 3351 3352
3352<script> 3353 // Function to dynamically load a script 3354 function loadScript(url, callback) { 3355 var script = document.createElement('script'); 3356 script.type = 'text/javascript'; 3357 script.src = url; 3358 script.defer = true; 3359 script.async = true; 3360 script.onload = callback; 3361 document.head.appendChild(script); 3362 } 3363 3364 // Function to dynamically load CSS file 3365 function loadCSS(url) { 3366 var link = document.createElement("link"); 3367 link.rel = "stylesheet"; 3368 link.href = url; 3369 document.head.appendChild(link); 3370 } 3371 3372 // Global methods / variables that can be accessed by element js 3373 window.luxuryPresence = { 3374 libs: { 3375 loadJs: loadScript, 3376 loadCss: loadCSS, 3377 jsLibsMap: { 3378 paroller: { 3379 jsSrc: 'https://d1e1jt2fj4r8r.cloudfront.net/javascript/jquery.paroller.min.js', 3380 isLoaded: false, 3381 }, 3382 ionRangeSlider: { 3383 jsSrc: 'https://cdnjs.cloudflare.com/ajax/libs/ion-rangeslider/2.3.1/js/ion.rangeSlider.min.js', 3384 cssSrc: 'https://cdnjs.cloudflare.com/ajax/libs/ion-rangeslider/2.3.1/css/ion.rangeSlider.min.css', 3385 isLoaded: false, 3386 requires: ['jquery'], 3387 }, 3388 splide: { 3389 jsSrc: 'https://cdnjs.cloudflare.com/ajax/libs/splidejs/4.1.4/js/splide.min.js', 3390 cssSrc: 'https://cdnjs.cloudflare.com/ajax/libs/splidejs/4.1.4/css/splide-core.min.css', 3391 isLoaded: false, 3392 }, 3393 wow: { 3394 jsSrc: 'https://cdnjs.cloudflare.com/ajax/libs/wow/1.1.2/wow.min.js', 3395 isLoaded: false, 3396 }, 3397 jquery: { 3398 jsSrc: 'https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js', 3399 isLoaded: false, 3400 }, 3401 slick: { 3402 jsSrc: 'https://cdnjs.cloudflare.com/ajax/libs/slick-carousel/1.8.1/slick.min.js', 3403 isLoaded: false, 3404 requires: ['jquery'], 3405 }, 3406 libphonenumber: { 3407 jsSrc: 'https://cdnjs.cloudflare.com/ajax/libs/libphonenumber-js/1.11.8/libphonenumber-js.min.js', 3408 isLoaded: false, 3409 }, 3410 }, 3411 }, 3412 }; 3413 </script>
3413 3414
3414<script defer src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js" integrity="sha512-v2CJ7UaYy4JwqLDIrZUI/4hqeoQieOmAZNXBeQyjo21dadnwR+8ZaIJVT8EE2iyI61OV8e6M8PP2/4hpQINQ/g==" crossorigin="anonymous" referrerpolicy="no-referrer" onload="window.luxuryPresence.libs.jsLibsMap.jquery.isLoaded = true"></script>
3414 3415
3415<script defer src="https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/4.7.8/handlebars.min.js"></script>
3415 3416
3416<script defer src="https://cdn.jsdelivr.net/npm/[email protected]/dist/handlebars-intl.min.js"></script>
3416 3417
3417<script defer src="https://cdnjs.cloudflare.com/ajax/libs/superagent/4.1.0/superagent.min.js"></script>
3417 3418
3418<script defer src="https://cdnjs.cloudflare.com/ajax/libs/slick-carousel/1.8.1/slick.min.js" onload="window.luxuryPresence.libs.jsLibsMap.slick.isLoaded = true"></script>
3418 3419
3419<script defer src="https://cdnjs.cloudflare.com/ajax/libs/wow/1.1.2/wow.min.js" onload="window.luxuryPresence.libs.jsLibsMap.wow.isLoaded = true"></script>
3419 3420
3420<script defer src="https://cdnjs.cloudflare.com/ajax/libs/ion-rangeslider/2.3.1/js/ion.rangeSlider.min.js" onload="window.luxuryPresence.libs.jsLibsMap.ionRangeSlider.isLoaded = true"></script>
3420 3421 3422 <!-- GOOGLE TRANSLATE SCRIPT --> 3423
3423<script> 3424 function googleTranslateElementInit() { 3425 // list of languages can be overridden by setting window.customLanguages in the head JS. example `window.customLanguages = 'en,de,fr,ro,sk,pl'` 3426 // Language codes can be taken from here https://cloud.google.com/translate/docs/languages 3427 const DEFAULT_LANGUAGES = 'en,es,zh-CN,zh-TW,tl,vi,ar,ko,ru,de,fr,hi,pt'; 3428 document.querySelectorAll('[data-type="TRANSLATE"]').forEach(function(translate){ 3429 translate.innerText = ''; 3430 new google.translate.TranslateElement({ 3431 pageLanguage: 'en', 3432 layout: google.translate.TranslateElement.InlineLayout.SIMPLE, 3433 includedLanguages: window.customLanguages || DEFAULT_LANGUAGES, 3434 }, translate); 3435 }); 3436 } 3437 </script>
3437 3438
3438<script defer src="//translate.google.com/translate_a/element.js?cb=googleTranslateElementInit"></script>
3438 3439
3439<script defer src="https://accounts.google.com/gsi/intermediate"></script>
3439 3440 3441 <!-- GOOGLE TRANSLATE SCRIPT END--> 3442
3442<script> 3443 3444 // UI helpers 3445 (function () { 3446 3447 function getSideMenu () { 3448 return document.querySelector('body > div.sidemenu'); 3449 } 3450 3451 function sideMenuHide (instant) { 3452 var sideMenu = getSideMenu(); 3453 if (!sideMenu || !sideMenu.classList.contains('visible')) { 3454 return; 3455 } 3456 sideMenu.classList.remove('visible'); 3457 sideMenu.dispatchEvent(new CustomEvent('lpui-hide', { detail: { instant: !!instant } })); 3458 window.dispatchEvent(new CustomEvent('lpui-sidemenu-hide', { detail: {instant: !!instant} })); 3459 } 3460 3461 function sideMenuShow () { 3462 var sideMenu = getSideMenu(); 3463 if (!sideMenu || sideMenu.classList.contains('visible')) { 3464 return; 3465 } 3466 sideMenu.classList.add('visible'); 3467 sideMenu.dispatchEvent(new Event('lpui-show')); 3468 window.dispatchEvent(new Event('lpui-sidemenu-show')); 3469 } 3470 3471 function sideMenuToggle () { 3472 var sideMenu = getSideMenu(); 3473 if (!sideMenu) { 3474 return; 3475 } 3476 var isSideMenuVisible = sideMenu.classList.contains('visible'); 3477 if (isSideMenuVisible) { 3478 sideMenuHide(); 3479 } else { 3480 sideMenuShow(); 3481 } 3482 } 3483 3484 var scrollLockRequests = {}; 3485 window.__debug_scrollLockRequests = scrollLockRequests; 3486 var hideScrollStyles = ''+ 3487 'body {'+ 3488 'overflow: hidden;' + 3489 '}'; 3490 var hideScrollId = 'hide-body-scroll-style'; 3491 3492 function scrollLock () { 3493 var style = document.createElement('style'); 3494 style.id = hideScrollId; 3495 style.textContent = hideScrollStyles; 3496 document.head.appendChild(style); 3497 window.dispatchEvent(new Event('lpui-scroll-locked')); 3498 } 3499 3500 function scrollUnlock () { 3501 document.getElementById(hideScrollId)?.remove(); 3502 window.dispatchEvent(new Event('lpui-scroll-unlocked')); 3503 } 3504 3505 function requestScrollLock (lockId) { 3506 var wasLocked = Object.keys(scrollLockRequests).length > 0; 3507 scrollLockRequests[lockId || '__anonymous_lock'] = true; 3508 if (!wasLocked) { 3509 scrollLock(); 3510 } 3511 } 3512 3513 function releaseScrollLock (lockId) { 3514 if (lockId) { 3515 delete scrollLockRequests[lockId]; 3516 if (!Object.keys(scrollLockRequests).length) { 3517 scrollUnlock(); 3518 } 3519 } else { 3520 scrollLockRequests = {}; 3521 window.__debug_scrollLockRequests = scrollLockRequests; 3522 scrollUnlock(); 3523 } 3524 } 3525 3526 function showModal (modalId, options) { 3527 var modal = document.getElementById(modalId); 3528 if (!modal) { 3529 return; 3530 } 3531 3532 if (!modal.classList.contains('visible')) { 3533 modal.classList.add('visible'); 3534 if (options && options.scrollLock) { 3535 window.lpUI.requestScrollLock(modalId); 3536 } 3537 modal.dispatchEvent(new Event('lpui-show')); 3538 window.dispatchEvent(new CustomEvent('lpui-modal-show', { detail: { id: modalId } })); 3539 } 3540 3541 const visibleModals = document.querySelectorAll('.modal.visible'); 3542 3543 if (visibleModals.length > 1) { 3544 3545 const maxZIndex = Array.from(visibleModals).reduce((maxZIndex, modal) => { 3546 const zIndex = window.getComputedStyle(modal).zIndex; 3547 const parsedZIndex = zIndex !== 'auto' ? parseInt(zIndex) : 0; 3548 return Math.max(maxZIndex, parsedZIndex); 3549 }, 0); 3550 3551 modal.style.zIndex = maxZIndex + 1; 3552 } 3553 3554 } 3555 3556 function _hideModal (modalEl) { 3557 if (!modalEl) { 3558 return; 3559 } 3560 if (!modalEl.classList.contains('visible')) { 3561 return; 3562 } 3563 modalEl.classList.remove('visible'); 3564 modalEl.style.zIndex = '';
3565 modalEl.dispatchEvent(new Event('lpui-hide')); 3566 var modalId = modalEl.getAttribute('id'); 3567 if (modalId) { 3568 window.lpUI.releaseScrollLock(modalId); 3569 window.dispatchEvent(new CustomEvent('lpui-modal-hide', { detail: { id: modalId } })); 3570 } 3571 } 3572 3573 function hideModal (modalId) { 3574 _hideModal(document.getElementById(modalId)); 3575 } 3576 3577 function hideClosestModal (el) { 3578 const modal = el?.closest('.modal'); 3579 if (!modal) { 3580 return; 3581 } 3582 if (modal.classList) { 3583 _hideModal(modal); 3584 return; 3585 } 3586 if (modal.length && modal[0].classList) { 3587 _hideModal(modal[0]); 3588 } 3589 } 3590 3591 window.lpUI = { 3592 sideMenuHide: sideMenuHide, 3593 sideMenuShow: sideMenuShow, 3594 sideMenuToggle: sideMenuToggle, 3595 requestScrollLock: requestScrollLock, 3596 releaseScrollLock: releaseScrollLock, 3597 showModal: showModal, 3598 hideModal: hideModal, 3599 hideClosestModal: hideClosestModal 3600 } 3601 })(); 3602 3603 var stringifyPrimitive = function(v) { 3604 switch (typeof v) { 3605 case 'string': 3606 return v; 3607 3608 case 'boolean': 3609 return v ? 'true' : 'false'; 3610 3611 case 'number': 3612 return isFinite(v) ? v : ''; 3613 3614 default: 3615 return ''; 3616 } 3617 }; 3618 3619 function objectToQuerystring(obj, sep, eq, name) { 3620 // https://github.com/Gozala/querystring/blob/master/encode.js 3621 sep = sep || '&'; 3622 eq = eq || '='; 3623 if (obj === null) { 3624 obj = undefined; 3625 } 3626 3627 if (typeof obj === 'object') { 3628 return Object.keys(obj).map(function(k) { 3629 var ks = encodeURIComponent(stringifyPrimitive(k)) + eq; 3630 if (Array.isArray(obj[k])) { 3631 return obj[k].map(function(v) { 3632 return ks + encodeURIComponent(stringifyPrimitive(v)); 3633 }).join(sep); 3634 } else { 3635 return ks + encodeURIComponent(stringifyPrimitive(obj[k])); 3636 } 3637 }).filter(Boolean).join(sep); 3638 3639 } 3640 3641 if (!name) return ''; 3642 return encodeURIComponent(stringifyPrimitive(name)) + eq + 3643 encodeURIComponent(stringifyPrimitive(obj)); 3644 } 3645 3646 function getPath(url, options) { 3647 var parser = document.createElement('a'); 3648 parser.href = url || ''; 3649 var path = parser.pathname || ''; 3650 if (path[0] !== '/') { 3651 // IE does not return path starting with a slash 3652 path = '/' + path; 3653 } 3654 3655 if (options && options.queryString) { 3656 path += parser.search; 3657 } 3658 3659 return path; 3660 } 3661 3662 function getMLSAutocomplete(keyword) { 3663 var searchURL = window.location.protocol + '//' + window.location.hostname + '/home-search/auto_complete'; 3664 var query = { 3665 kind: 'listings', 3666 query: keyword 3667 }; 3668 3669 return superagent 3670 .get(searchURL) 3671 .query(query) 3672 .then(function(res) { 3673 var data = res.body || []; 3674 /* 3675 Of the format: 3676 [{ 3677 "_index": "listing_auto_completes_production_20190723232720775", 3678 "_type": "listing_auto_complete", 3679 "_id": "235875624", 3680 "_score": 0, 3681 "status_modified_at": 1553250708, 3682 "city": "Jupiter", 3683 "price": 1335743, 3684 "display_address": "10088 Calabrese Trail Unit: 9 Jupiter FL 33478", 3685 "sold_price": null, 3686 "id": 235875624, 3687 "state": "FL", 3688 "photo_urls": [ 3689 "http://cdn.photos.sparkplatform.com/fl/20190322153144208228000000-o.jpg" 3690 ], 3691 "status": "Pending" 3692 }, ...] 3693 */ 3694 3695 return data; 3696 }); 3697 } 3698 3699 function getMLSAutocompleteWithFilters(keyword, filters = {}) { 3700 var searchURL = window.location.protocol + '//' + window.location.hostname + '/home-search/auto_complete'; 3701 3702 return superagent 3703 .get(searchURL) 3704 .query({ kind: 'listings', query: keyword, ...filters }) 3705 .then(function(res) { 3706 /* 3707 Of the format: 3708 [{ 3709 "_index": "listing_auto_completes_production_20190723232720775", 3710 "_type": "listing_auto_complete", 3711 "_id": "235875624", 3712 "_score": 0, 3713 "status_modified_at": 1553250708, 3714 "city": "Jupiter", 3715 "price": 1335743, 3716 "display_address": "10088 Calabrese Trail Unit: 9 Jupiter FL 33478", 3717 "sold_price": null, 3718 "id": 235875624, 3719 "state": "FL", 3720 "photo_urls": [ 3721 "http://cdn.photos.sparkplatform.com/fl/20190322153144208228000000-o.jpg" 3722 ], 3723 "status": "Pending" 3724 }, ...] 3725 */ 3726 return res.body || []; 3727 }); 3728 } 3729 3730 function makeMlsLink(data) { 3731 // Code Dupe from utils/googlePlace 3732 // mapping of google data type to mls data type 3733 var mapping = { 3734 neighborhood: 'neighborhood', 3735 locality: 'place', 3736 administrative_area_level_2: 'county', 3737 administrative_area_level_1: 'state', 3738 postal_code: 'postalcode', 3739 }; 3740 3741 function _filterTypes(types) { 3742 return types.filter(function(type) { return type !== 'political' }); 3743 } 3744 3745 if (!data || !Object.keys(data).length) { 3746 return '/home-search/listings'; 3747 } 3748 3749 var newData = {};
3750 _filterTypes(data.types).forEach(function(type) { 3751 if (mapping[type]) { 3752 newData.search_type = mapping[type]; 3753 } 3754 }); 3755 newData.omnibox = data.formatted_address; 3756 data.address_components.forEach(function(component) { 3757 _filterTypes(component.types).forEach(function(type) { 3758 if (mapping[type]) { 3759 newData[mapping[type]] = component.long_name; 3760 } 3761 }); 3762 }); 3763 var bounds = data.geometry.bounds; 3764 var convertedData = Object.assign(bounds, newData); 3765 3766 return '/home-search/listings?' + objectToQuerystring(convertedData); 3767 } 3768 3769 function debounce(func, wait) { 3770 let timeout; 3771 return function(...args) { 3772 const context = this; 3773 clearTimeout(timeout); 3774 timeout = setTimeout(() => func.apply(context, args), wait) 3775 } 3776 } 3777 3778 // Global methods / variables that can be accessed by element js 3779 window.luxuryPresence = { 3780 ...window.luxuryPresence, 3781 defaults: { 3782 companyId: '9994eaf9-a60f-4f64-89b4-cb9e3cc349b1', 3783 apiGatewayUrl: '/api-gw', 3784 routerUrl: '/api-nv', 3785 gql: {"agents":"\n query Agents (\n $agentId: ID\n $agentIds: [ID!]\n $companyId: String\n $network: Boolean\n $networkId: String\n $officeId: [ID!]\n $excludeOfficeId: [ID!]\n $offset: Int\n $limit: Int\n $sort: String\n $sortDir: SortDirectionEnum\n $search: String\n $tags: [String]\n $tagsMode: TagsModeEnum\n $teamId: [ID!]\n $developmentId: [ID!]\n $prioritizeIds: Boolean\n ) {\n agents (\n agentId: $agentId\n agentIds: $agentIds\n companyId: $companyId\n network: $network\n networkId: $networkId\n officeId: $officeId\n excludeOfficeId: $excludeOfficeId\n offset: $offset\n limit: $limit\n sort: $sort\n sortDir: $sortDir\n search: $search\n tags: $tags\n tagsMode: $tagsMode\n teamId: $teamId\n developmentId: $developmentId\n prioritizeIds: $prioritizeIds\n ) {\n id\n firstName\n lastName\n avatar {\n smallUrl\n mediumUrl\n largeUrl\n height\n width\n }\n position\n bioLong\n bioShort\n seoTitle\n seoDescription\n slug\n phoneNumber\n phoneNumber2\n license\n email\n leadAgent\n greaterArea\n tags\n }\n agentsCount(\n agentIds: $agentIds\n companyId: $companyId\n network: $network\n networkId: $networkId\n officeId: $officeId\n excludeOfficeId: $excludeOfficeId\n search: $search\n tags: $tags\n tagsMode: $tagsMode\n teamId: $teamId\n developmentId: $developmentId\n prioritizeIds: $prioritizeIds\n ) {\n count\n }\n }\n","properties":"\n query Properties(\n $agentIds: [ID!]\n $teamIds: [ID!]\n $neighborhoodIds: [ID!]\n $officeIds: [ID!]\n $propertyId: ID\n $companyId: String\n $networkId: String\n $network: Boolean\n $statusId: String\n $propertyIds: [ID!]\n $prioritizeIds: Boolean\n $statusIds: [String!]\n $excludeStatusId: [String!]\n $neighborhoodId: String\n $addressState: [String!]\n $addressCity: [String!]\n $relatedNeighborhoodPropertyId: String\n $developmentId: String\n $featuredListing: Boolean\n $leaseProperty: Boolean\n $search: String\n $searchTermMode: SearchTermModeEnum\n $globalProperty: Boolean\n $archived: Boolean\n $salesPriceGTE: Float\n $salesPriceLTE: Float\n $leasePriceGTE: Float\n $leasePriceLTE: Float\n $livingSpaceSizeGTE: Float\n $livingSpaceSizeLTE: Float\n $bathCountGTE: Float\n $bathCountLTE: Float\n $bedroomCountGTE: Float\n $bedroomCountLTE: Float\n $architectureStyle: String\n $lifestyle: String\n $propertyTypeId: String\n $propertyTypeIds: [String!]\n $spaceTypeIds: [ID!]\n $tag: String\n $backfillMLSResults: Boolean\n $displayMLSListings: String\n $hostname: String\n $websiteId: ID\n $backfillProviders: [String!]\n $backfillMLSListingIds: [String!]\n $backfillMLSAgentIds: [String!]\n $backfillMLSOfficeIds: [String!]\n $backfillBoundary: JSON\n $openHouse: Boolean\n $withGeo: Boolean\n $advancedFilters: JSON\n $seasonalPriceId: ID\n $offset: Int\n $limit: Int\n $sort: String\n $sortDir: SortDirectionEnum\n ) {\n properties(\n \n agentIds: $agentIds\n teamIds: $teamIds\n propertyId: $propertyId\n propertyIds: $propertyIds\n prioritizeIds: $prioritizeIds\n companyId: $companyId\n network: $network\n networkId: $networkId\n statusId: $statusId\n statusIds: $statusIds\n excludeStatusId: $excludeStatusId\n neighborhoodId: $neighborhoodId\n neighborhoodIds: $neighborhoodIds\n officeIds: $officeIds\n addressState: $addressState\n addressCity: $addressCity\n developmentId: $developmentId\n featuredListing: $featuredListing\n leaseProperty: $leaseProperty\n search: $search\n searchTermMode: $searchTermMode\n salesPriceGTE: $salesPriceGTE\n salesPriceLTE: $salesPriceLTE\n leasePriceGTE: $leasePriceGTE\n leasePriceLTE: $leasePriceLTE\n livingSpaceSizeGTE: $livingSpaceSizeGTE\n livingSpaceSizeLTE: $livingSpaceSizeLTE\n bathCountGTE: $bathCountGTE\n bathCountLTE: $bathCountLTE\n bedroomCountGTE: $bedroomCountGTE\n bedroomCountLTE: $bedroomCountLTE\n architectureStyle: $architectureStyle\n lifestyle: $lifestyle\n propertyTypeId: $propertyTypeId\n propertyTypeIds: $propertyTypeIds\n spaceTypeIds: $spaceTypeIds\n tag: $tag\n archived: $archived\n globalProperty: $globalProperty\n withGeo: $withGeo\n openHouse: $openHouse\n displayMLSListings: $displayMLSListings\n advancedFilters: $advancedFilters\n seasonalPriceId: $seasonalPriceId\n\n relatedNeighborhoodPropertyId: $relatedNeighborhoodPropertyId\n backfillMLSResults: $backfillMLSResults\n hostname: $hostname\n websiteId: $websiteId\n backfillProviders: $backfillProviders\n backfillMLSListingIds: $backfillMLSListingIds\n backfillMLSAgentIds: $backfillMLSAgentIds\n backfillMLSOfficeIds: $backfillMLSOfficeIds\n backfillBoundary: $backfillBoundary\n offset: $offset\n limit: $limit\n sort: $sort,\n sortDir: $sortDir\n ){\n id\n name\n status\n originalStatus {\n id\n name\n }\n salesPrice\n reducedPrice\n isPasswordProtected\n bedroomCount\n bathCount\n fullBathCount\n halfBathCount\n threeQuarterBathCount\n fullAddress\n addressLine1\n addressLine2\n addressCity\n addressState\n addressCountry\n postalCode\n description\n syncedAt\n officeName\n attributionContact\n neighborhood {\n id\n }\n media {\n smallUrl\n mediumUrl\n largeUrl\n xLargeUrl\n xxLargeUrl\n height\n width\n }\n seoTitle\n seoDescription\n slug\n fromMLS\n mlsId\n mlsLogo\n mlsAttribution\n openHouse\n openHouseHours\n priceUponRequest\n privateAddress\n leaseProperty\n leasePrice\n currency\n leaseTermFrequencyInterval\n leaseTermFrequencyCount\n leasePeriod\n livingSpaceSize\n livingSpaceUnits\n lotAreaSize\n lotAreaUnits\n tags\n latitude\n longitude\n soldDate\n timeZone\n buyerAgencyCompensation\n buyerAgencyCompensationType\n landIdUrl\
3785n }\n propertiesCount(\n \n agentIds: $agentIds\n teamIds: $teamIds\n propertyId: $propertyId\n propertyIds: $propertyIds\n prioritizeIds: $prioritizeIds\n companyId: $companyId\n network: $network\n networkId: $networkId\n statusId: $statusId\n statusIds: $statusIds\n excludeStatusId: $excludeStatusId\n neighborhoodId: $neighborhoodId\n neighborhoodIds: $neighborhoodIds\n officeIds: $officeIds\n addressState: $addressState\n addressCity: $addressCity\n developmentId: $developmentId\n featuredListing: $featuredListing\n leaseProperty: $leaseProperty\n search: $search\n searchTermMode: $searchTermMode\n salesPriceGTE: $salesPriceGTE\n salesPriceLTE: $salesPriceLTE\n leasePriceGTE: $leasePriceGTE\n leasePriceLTE: $leasePriceLTE\n livingSpaceSizeGTE: $livingSpaceSizeGTE\n livingSpaceSizeLTE: $livingSpaceSizeLTE\n bathCountGTE: $bathCountGTE\n bathCountLTE: $bathCountLTE\n bedroomCountGTE: $bedroomCountGTE\n bedroomCountLTE: $bedroomCountLTE\n architectureStyle: $architectureStyle\n lifestyle: $lifestyle\n propertyTypeId: $propertyTypeId\n propertyTypeIds: $propertyTypeIds\n spaceTypeIds: $spaceTypeIds\n tag: $tag\n archived: $archived\n globalProperty: $globalProperty\n withGeo: $withGeo\n openHouse: $openHouse\n displayMLSListings: $displayMLSListings\n advancedFilters: $advancedFilters\n seasonalPriceId: $seasonalPriceId\n\n ) {\n count\n }\n }\n","pressReleases":"\n query PressReleases (\n $pressReleaseId: ID\n $companyId: String\n $offset: Int\n $limit: Int\n $sort: String\n $sortDir: SortDirectionEnum\n $search: String\n $categories: [String!]\n $propertyId: [String!]\n $developmentId: [String!]\n $agentId: [String!]\n $teamId: [String!]\n $year: [Int!]\n ) {\n pressReleases (\n pressReleaseId: $pressReleaseId\n companyId: $companyId\n offset: $offset\n limit: $limit\n sort: $sort\n sortDir: $sortDir\n search: $search\n categories: $categories\n propertyId: $propertyId\n developmentId: $developmentId\n agentId: $agentId\n teamId: $teamId\n year: $year\n ) {\n id\n title\n description\n author\n externalUrl\n media {\n smallUrl\n mediumUrl\n largeUrl\n xLargeUrl\n xxLargeUrl\n height\n width\n }\n publishedAt\n categories\n }\n pressReleasesCount(\n companyId: $companyId\n search: $search\n categories: $categories\n propertyId: $propertyId\n developmentId: $developmentId\n agentId: $agentId\n teamId: $teamId\n year: $year\n ) {\n count\n }\n }\n","posts":"\n query Posts (\n $postId: ID\n $companyId: String\n $offset: Int\n $limit: Int\n $order: String\n $sortDir: SortDirectionEnum\n $search: String\n $featured: Boolean\n $categoryId: String\n $categoryIds: [String!]\n $neighborhoodId: [String!]\n ) {\n posts (\n postId: $postId\n companyId: $companyId\n offset: $offset\n limit: $limit\n order: $order\n sortDir: $sortDir\n search: $search\n featured: $featured\n postStatusId: \"5f528253-abb7-484e-95c3-330269ac1102\"\n categoryId: $categoryId\n categoryIds: $categoryIds\n neighborhoodId: $neighborhoodId\n ) {\n id\n title\n subtitle\n description\n status\n featured\n slug\n categories {\n id\n name\n }\n createdAt\n publishedAt\n scheduledAt\n createdBy {\n firstName\n lastName\n }\n customAuthor\n media {\n smallUrl\n mediumUrl\n largeUrl\n xLargeUrl\n xxLargeUrl\n height\n width\n }\n seoTitle\n seoDescription\n slug\n }\n postsCount(\n companyId: $companyId\n search: $search\n featured: $featured\n postStatusId: \"5f528253-
3785abb7-484e-95c3-330269ac1102\"\n categoryId: $categoryId\n categoryIds: $categoryIds\n neighborhoodId: $neighborhoodId\n ) {\n count\n }\n }\n","testimonials":"\n query Testimonials(\n $testimonialId: ID\n $agentId: ID\n $teamId: ID\n $companyId: String\n $offset: Int\n $limit: Int\n $sort: String\n $sortDir: SortDirectionEnum\n $search: String\n ) {\n testimonials(\n testimonialId: $testimonialId\n agentId: $agentId\n teamId: $teamId\n companyId: $companyId\n offset: $offset\n limit: $limit\n sort: $sort\n sortDir: $sortDir\n search: $search\n ){\n id\n body\n author\n position\n media {\n smallUrl\n mediumUrl\n largeUrl\n xLargeUrl\n xxLargeUrl\n height\n width\n }\n seoTitle\n seoDescription\n agents {\n slug\n email\n firstName\n lastName\n avatar {\n smallUrl\n height\n width\n }\n }\n }\n testimonialsCount(\n companyId: $companyId\n agentId: $agentId\n teamId: $teamId\n search: $search\n ) {\n count\n }\n }\n","neighborhoods":"\n query Neighborhoods (\n $neighborhoodId: ID\n $companyId: String\n $search: String\n $tag: String\n $offset: Int\n $limit: Int\n $sort: String\n $sortDir: SortDirectionEnum\n ) {\n neighborhoods (\n neighborhoodId: $neighborhoodId\n companyId: $companyId\n search: $search\n tag: $tag\n offset: $offset\n limit: $limit\n sort: $sort\n sortDir: $sortDir\n ) {\n id\n name\n description\n descriptionShort\n slug\n tags\n googlePlaceData\n mlsLink\n createdAt\n media {\n smallUrl\n mediumUrl\n largeUrl\n xLargeUrl\n xxLargeUrl\n height\n width\n }\n seoTitle\n seoDescription\n }\n neighborhoodsCount(\n companyId: $companyId\n search: $search\n tag: $tag\n ) {\n count\n }\n }\n","developments":"\n query Developments (\n $developmentId: ID\n $companyId: String\n $offset: Int\n $limit: Int\n $sort: String\n $sortDir: SortDirectionEnum\n $search: String\n $tag: [String!]\n $agentId: [ID!]\n $neighborhoodId: ID\n $neighborhoodIds: [ID!]\n ) {\n developments (\n developmentId: $developmentId\n companyId: $companyId\n offset: $offset\n limit: $limit\n sort: $sort\n sortDir: $sortDir\n search: $search\n tag: $tag\n agentId: $agentId\n neighborhoodId: $neighborhoodId\n neighborhoodIds: $neighborhoodIds\n ) {\n id\n name\n description\n descriptionShort\n slug\n createdAt\n media {\n smallUrl\n mediumUrl\n largeUrl\n xLargeUrl\n xxLargeUrl\n height\n width\n }\n googlePlaceData\n seoTitle\n seoDescription\n tags\n }\n developmentsCount(\n companyId: $companyId\n search: $search\n tag: $tag\n agentId: $agentId\n neighborhoodId: $neighborhoodId\n neighborhoodIds: $neighborhoodIds\n ) {\n count\n }\n }\n","pois":"\n query PointsOfInterest (\n $regionId: UUID!\n $radius: Int\n $offset: Int\n $limit: Int\n $categories: [AreasOfInterestCategories!]\n $sortBy: AreasOfInterestSorting\n ) {\n pois: areasOfInterest (\n input: {\n regionId: $regi
3785onId\n radius: $radius\n offset: $offset\n limit: $limit\n categories: $categories\n sortBy: $sortBy\n }\n ) {\n items {\n name\n imageUrl\n url\n reviewCount\n rating\n price\n displayPhone\n distance\n category\n location {\n address1\n address2\n address3\n city\n zipCode\n country\n state\n }\n }\n total\n }\n }\n","offices":"\n query Offices (\n $companyId: ID!\n $officeIds: [ID!]\n $officeMlsIds: [String!]\n $externalIds: [String!]\n $externalSource: String\n $agentId: ID\n $archived: Boolean\n $tags: [String!]\n $slug: String\n $search: String\n $sort: String\n $sortDir: SortDirection\n $offset: Int\n $limit: Int\n ) {\n offices (\n companyId: $companyId\n officeIds: $officeIds\n officeMlsIds: $officeMlsIds\n externalIds: $externalIds\n externalSource: $externalSource\n agentId: $agentId\n archived: $archived\n tags: $tags\n slug: $slug\n search: $search\n sort: $sort\n sortDir: $sortDir\n offset: $offset\n limit: $limit\n ) {\n items {\n id\n companyId\n name\n slug\n officeMlsId\n shortDescription\n fullDescription\n addressLine1\n addressLine2\n addressCity\n addressState\n addressPostalCode\n addressCountry\n phone\n email\n officeHours\n coordinates\n googlePlaceData\n seoTitle\n seoDescription\n fullAddress\n tags\n media {\n id\n originalUrl\n thumbnailUrl\n smallUrl\n mediumUrl\n largeUrl\n xLargeUrl\n xxLargeUrl\n }\n }\n total\n }\n }\n","teams":"\n query Teams (\n $companyId: ID\n $teamIds: [ID!]\n $agentId: ID\n $externalIds: [String!]\n $externalSource: String\n $slug: String\n $search: String\n $sort: String\n $sortDir: String\n $offset: Int\n $limit: Int\n ) {\n teams (\n companyId: $companyId\n teamIds: $teamIds\n agentId: $agentId\n externalIds: $externalIds\n externalSource: $externalSource\n slug: $slug\n search: $search\n sort: $sort\n sortDir: $sortDir\n offset: $offset\n limit: $limit\n ) {\n items {\n id\n companyId\n name\n email\n shortBio\n addressLine1\n addressLine2\n addressCity\n addressState\n addressCountry\n phoneNumber1\n phoneNumber2\n postalCode\n avatar {\n id\n thumbnailUrl\n smallUrl\n mediumUrl\n largeUrl\n }\n urlWebsite\n socialLinks\n socialLinksArray\n tags\n seoTitle\n seoDesc\n slug\n displayOrder\n }\n total\n }\n }\n"}, 3786 propertyPlaceholderImage: {"id":"611d2b78-92ea-4224-a494-10184c9cb31f","alt":null,"bytes":35581,"width":1920,"format":"png","height":1200,"pngUrl":"","shared":true,"curated":false,"duration":null,"largeUrl":"https://media-production.lp-cdn.com/cdn-cgi/image/format=auto,quality=85/https://media-production.lp-cdn.com/media/0c809558-c1be-4cbb-92d6-59eec98965d7","smallUrl":"https://media-production.lp-cdn.com/cdn-cgi/image/format=auto,quality=85,fit=scale-down,width=960/https://media-production.lp-cdn.com/media/0c809558-c1be-4cbb-92d6-59eec98965d7","authorUrl":null,"createdAt":"2025-10-29T19:36:47.178Z","mediumUrl":"https://media-production.lp-cdn.com/cdn-cgi/image/format=auto,quality=85,fit=scale-down,width=1280/https://media-production.lp-cdn.com/media/0c809558-c1be-4cbb-92d6-59eec98965d7","sourceUrl":null,"updatedAt":"2025-10-29T19:36:47.178Z","xLargeUrl":"https://media-production.lp-cdn.com/cdn-cgi/image/format=auto,quality=85/https://media-production.lp-cdn.com/media/0c809558-c1be-4cbb-92d6-59eec98965d7","__typename":"Media","altTagText":null,"authorName":null,"sourceName":"r2","xxLargeUrl":"https://media-production.lp-cdn.com/cdn-cgi/image/format=auto,quality=85/https://media-production.lp-cdn.com/media/0c809558-c1be-4cbb-92d6-59eec98965d7","description":null,"displayName":"property_placeholder-logo-2","originalUrl":"https://media-production.lp-cdn.com/cdn-cgi/image/format=auto,quality=85/https://media-production.lp-cdn.com/media/0c809558-c1be-4cbb-92d6-59eec98965d7","resourceType":"image","thumbnailUrl":"https://media-production.lp-cdn.com/cdn-cgi/image/format=auto,quality=85,fit=scale-down,width=320/https://media-production.lp-cdn.com/media/0c809558-c1be-4cbb-92d6-59eec98965d7","originalFileName":"Property_placeholder-logo (2)","cloudinaryPublicId":"0c809558-c1be-4cbb-92d6-59eec98965d7"} 3787 }, 3788 objectToQuerystring: objectToQuerystring, 3789 getPath: getPath, 3790 getMLSAutocomplete: getMLSAutocomplete, 3791 getMLSAutocompleteWithFilters: getMLSAutocompleteWithFilters, 3792 makeMlsLink: makeMlsLink, 3793 debounce: debounce, 3794 }; 3795 3796 var wowOffset = 100; 3797 </script>
3797 3798
3798<script> 3799 (()=>{var e={},t={};t={getVideoFirstFrame(e){let t="https://res.cloudinary.com/";if(!e||!e.startsWith(t))return null;let i=e.replace(t,"").replace("f_auto:video","f_auto").split("/");if(-1===i[1].toLowerCase().indexOf("video"))return null;let s=i[i.length-1];s.lastIndexOf(".")>-1?s=`${s.slice(0,s.lastIndexOf("."))}.jpg`:s+=".jpg",i[i.length-1]=s;let r=3;"videos"===i[1].toLowerCase()&&""!==i[2]&&(r=2);let a=/^(?:[a-z]{1,3}|\$\w+)_\S+$/;for(;r<i.length-1&&a.test(i[r]);)r++;return i.splice(r,0,"so_0,eo_0"),t+i.join("/")}},e=function(e,{isEqual:i}){let s=[{field:"thumbnailUrl",w:320},{field:"smallUrl",w:960},{field:"mediumUrl",w:1280},{field:"largeUrl",w:1920},{field:"xLargeUrl",w:2560},{field:"xxLargeUrl",w:3456}],r=function(e,t,i){let s="en-US";if(!e)return"";if("string"==typeof e){let t=e.trim();if(/[^+\-0-9.]/.test(t))return e;if(!(e=parseFloat(t)))return""}let r="number"==typeof i?i:0,a=Number.isInteger(e)?0:r,o={minimumFractionDigits:a,maximumFractionDigits:a};return"USD"===t?`$${e.toLocaleString(s,o)}`:"CAD"===t?`$${e.toLocaleString(s,o)} CAD`:"EUR"===t?`\u{20AC}${e.toLocaleString(s,o)}`:"GBP"===t?`\xa3${e.toLocaleString(s,o)}`:"PERCENT"===t?`${e}%`:"number"==typeof i&&i>-1?e.toLocaleString(s,{maximumFractionDigits:i}):e.toLocaleString(s)},a=function(e){switch(e){case"DAY":return"daily";case"MONTH":return"monthly";case"WEEK":return"weekly";case"YEAR":return"yearly";case"QUARTER":return"quarterly";case"HALF_YEAR":return"semi-annually";case"NIGHTLY":return"nightly";case"SEASONAL":return"seasonally";default:return""}},o=function(e){switch(e){case"DAY":return"day";case"WEEK":return"wk";case"MONTH":return"mo";case"YEAR":return"yr";case"QUARTER":return"3 mos";case"HALF_YEAR":return"6 mos";case"NIGHTLY":return"night";case"SEASONAL":return"season";default:return""}},n=function(e){switch(e){case"DAY":return"day";case"WEEK":return"week";case"MONTH":return"month";case"YEAR":return"year";case"QUARTER":return"quarter";case"HALF_YEAR":return"6 months";case"NIGHTLY":return"night";case"SEASONAL":return"season";default:return""}},l=function(e,t){let i,s,r=t||{};switch(r.mode){case"short":i=o(e),s=r.frequencySeparator||"/";break;case"standard":case"std":i=n(e),s=r.frequencySeparator||"/";break;default:i=a(e),s=r.frequencySeparator||" "}return i?r.frequencyTag||r.frequencyClass?`${s}<${r.frequencyTag||"span"}${r.frequencyClass?` class="${r.frequencyClass}"`:""}>${i}</${r.frequencyTag||"span"}>`:s+i:""};return{isEmpty(e,t){let i=!e||"string"==typeof e&&!e.trim()||!Object.keys(e).length;return t.fn?i?t.fn(this):t.inverse(this):i},ifEquals(e,t,s){return i(e,t)?s.fn(this):s.inverse(this)},"raw-helper":function(e){return e.fn()},striptags:e=>e?(e=(e=(e=e.replace(/<\!--(.|[\r\n])*?-\->/g,"")).replace(/<(xml|style|svg)("[^"]*"|'[^']*'|[^>])*>(.|[\r\n])*?<\/\1>/gi,"")).replace(/ /g," ")).replace(/<\/?("[^"]*"|'[^']*'|[^>])*(>|$)/g,""):"",striptagsLineBreaks:e=>e?(e=(e=(e=(e=e.replace(/<\!--(.|[\r\n])*?-\->/g,"")).replace(/<(xml|style|svg)("[^"]*"|'[^']*'|[^>])*>(.|[\r\n])*?<\/\1>/gi,"")).replace(/(<br\s*\/?>)|(<\/p\s*>)(?=.*(<\/p\s*>|<br\s*\/?>))/g," ")).replace(/ /g," ")).replace(/<\/?("[^"]*"|'[^']*'|[^>])*(>|$)/g,""):"",truncate:(e,t,i)=>e?e.length>t?"string"==typeof i?e.slice(0,t-i.length)+i:e.slice(0,t):e:"",setSubArray(e=[],t,i,s,r){r.data.root[s]=e.slice(t,i)},formatAreaUnit(e){switch(e){case"ACRE":return"Acres";case"SQUARE_METER":return"Sq.M.";case"SQUARE_YARD":return"Sq.Yd.";default:return"Sq.Ft."}},formatFrequency:a,formatFrequencyShort:o,lpFormatFrequency:(e,t)=>l(e,t&&t.hash),subArray(e,t,i,s){return e>=t&&e<i?s.fn(this):s.inverse(this)},formatPropertyStatus(e,t,i){let s="";switch(e){case"SOLD":s="Sold";break;case"FOR_SALE":s="For Sale";break;case"COMING_SOON":s="Coming Soon";break;case"INACTIVE":s="Inactive";break;case"POCKET_LISTING":s="Pocket Listing";break;case"PENDING":s="Pending";break;case"UNDER_CONTRACT":s="Under Contract";break;case"ACTIVE_UNDER_CONTRACT":s="Active Under Contract";break;default:s=e}return("Sold"===s||"Leased"===s)&&t&&"string"==typeof i&&i&&(s+=` on ${i}`),s},formatGrade(e){switch(e){case"UNDERGRADUATE_COURSES":return"Undergrad";case"PRE_KINDERGARTEN":return"PreK";case"KINDERGARTEN":return"K";case"FIRST_GRADE":return"1st";case"SECOND_GRADE":return"2nd";case"THIRD_GRADE":return"3rd";case"FOURTH_GRADE":return"4th";case"FIFTH_GRADE":return"5th";case"SIXTH_GRADE":return"6th";case"SEVENTH_GRADE":return"7th";case"EIGHTH_GRADE":return"8th";case"NINTH_GRADE":return"9th";case"TENTH_GRADE":return"10th";case"ELEVENTH_GRADE":return"11th";case"TWELFTH_GRADE":return"12th";default:return""}},encodeURI:e=>encodeURI(e),encodeURIComponent:e=>encodeURIComponent(e),hbFormatDate(e,t){if(!e)return"";let i="current"===e?new Date:new Date(e);if(Number.isNaN(i.getTime()))return"";if("YYYY"===t||"year"===t)return`${i.getFullYear()}`;if("MM/DD/YY"===t){let e=i.getMonth()+1;return e<10&&(e=`0${e.toString()}`),`${e}/${i.getDate()}/${i.getFullYear().toString().substr(-2)}`}return`${["January","February","March","April","May","June","July","August","September","October","November","December"][i.getMonth()]} ${i.getDate()}, ${i.getFullYear()}`},openHousesFormat(e,t,i){if(!e||!t)return"";let s=e=>{let t=e.getHours();t>12&&(t-=12);let i=`${t}:${e.getMinutes()>9?e.getMinutes():"0"+e.getMinutes()}`;return e.getHours()>11?i+="PM":i+="AM",i},r="",a=e,o=t;
3799i&&(a=new Date(e).toLocaleString("en-US",{timeZone:i}),o=new Date(t).toLocaleString("en-US",{timeZone:i}),[,,,r]=new Date(t).toLocaleString("en-US",{timeZone:i,timeZoneName:"short"}).split(" "));let n=new Date(a),l=n.toLocaleDateString("en-US"),d=s(n),c=s(new Date(o));return`${l} ${d} - ${c} ${r}`},math:(e,t,i)=>(e=parseFloat(e),({"+":e+(i=parseFloat(i)),"-":e-i,"*":e*i,"/":e/i,"%":e%i})[t]),findByKey(e,t,i,s){let r=(e||[]).find(e=>e[t]===i);return s.fn(r)},capitalize(e,t,i){let s;return"string"==typeof e&&e.length&&(s=e.toLowerCase().split(t).map(e=>e.charAt(0).toUpperCase()+e.slice(1)).join(" ").toString()),s},and:(...e)=>Array.prototype.slice.call(e).every(Boolean),or:(...e)=>Array.prototype.slice.call(e,0,-1).some(Boolean),not:e=>!e,orderByIds(t,i,s){let r="",a=null;s.data&&(a=e.createFrame(s.data));let o=(i||[]).filter(Boolean);if(o&&o.length){let e=0;r=o.reduce((i,r,o)=>{let n=(t||[]).find(e=>e.id===r);return n?(a&&(a.index=o-e),i+s.fn(n,{data:a})):(e+=1,i)},"")}else for(let e=0,i=(t||[]).length;e<i;e++)a&&(a.index=e),r+=s.fn(t[e],{data:a});return r},ifIn(e,t,i){return t&&t.indexOf(e)>-1?i.fn(this):i.inverse(this)},partial:(t,i)=>(e.registerPartial(t,e.compile(i.fn())),`
3799<script type="text/x-handlebars-template" class="${t} pagination-script">${i.fn()}<\/script>`),numCompare(e,t,i,s){return({"==":function(e,t){return e==t},"===":function(e,t){return e===t},"!=":function(e,t){return e!=t},"<":function(e,t){return e<t},">":function(e,t){return e>t},"<=":function(e,t){return e<=t},">=":function(e,t){return e>=t}})[i](e,t)?!s.fn||s.fn(this):!!s.fn&&s.inverse(this)},lpFormatNumber:r,arrayToHtmlList:(e,t)=>e.reduce((e,t)=>{try{if("object"==typeof t||null===t)return e;return e+=`<li>${t}</li>`}catch(e){}},""),join:(e,t,i)=>e&&e.join?e.join("string"==typeof t?t:", "):"",dedupe(t,i,s){let r=null,a=0,o=new Set;return s.data&&(r=e.createFrame(s.data)),(t||[]).reduce((e,t,n)=>{let l=i?t[i]:t;return!o.has(l)||i&&void 0===l?(o.add(l),r&&(r.index=n-a),e+s.fn(t,{data:r})):(a+=1,e)},"")},JSONstringify:(e,t)=>JSON.stringify(e),formatPropertyPrice(...t){let i=t[t.length-1].hash,s=t.length>1?t[0]:i.property,a=i.precision||0;t.length>2&&(i.mode=t[1]),i.mode||(i.mode="short");let o=t.length>3?t[2]:i.separator;if("string"!=typeof o&&(o=" | "),s.isPasswordProtected)return"Exclusive Listing";let n=t.length>4?t[3]:i.priceUponRequestText;if("string"!=typeof n&&(n="Price Upon Request"),s.priceUponRequest)return n;let d=s.salesPrice,c=null;"SOLD"!==s.status&&"Leased"!==s.status&&s.reducedPrice&&(i.reducedClass?c=s.reducedPrice:d=s.reducedPrice);let u="";c&&(u+=`<${i.reducedTag||"span"} class="${i.reducedClass}">${r(c,s.currency,a)}</${i.reducedTag||"span"}>`),d&&(d=r(d,s.currency,a),i.priceTag||i.priceClass?u+=`<${i.priceTag||"span"}${i.priceClass?` class="${i.priceClass}"`:""}>${d}</${i.priceTag||"span"}>`:u+=d);let h=s.leasePrice&&r(s.leasePrice,s.currency,a);if(h){u&&(u+=o);let e=`${h}${l(s.leasePeriod,i)}`;i.leaseTag||i.leaseClass?u+=`<${i.leaseTag||"span"}${i.leaseClass?` class="${i.leaseClass}"`:""}>${e}</${i.leaseTag||"span"}>`:u+=e}
3799return new e.SafeString(u)},mediaImage(t,i={}){if(!t)return;let{lazy:r,...a}=i.hash,o="",n="";if("string"==typeof t)o=t;else{let e={},{height:r,width:l,altTagText:d}=t;s.forEach(i=>{t[i.field]&&!e[t[i.field]]&&(e[t[i.field]]=i.w)});let c=Object.keys(e);if(!c.length)return;if(d&&(a.alt=d),r&&l&&(r>=l&&(a.class?a.class+=" portrait":a.class="portrait"),"setDims"in i.hash&&(a.width=l,a.height=r)),c.length>1&&l&&l<e[c[c.length-1]]){let t=c.findIndex(t=>e[t]>=l);c.splice(t+1),e[c[c.length-1]]=l}c.length>1&&(n=c.map(t=>`${t} ${e[t]}w`).join(", "),c.sort((t,i)=>Math.abs(e[t]-1280)-Math.abs(e[i]-1280))),o=c[0]}delete a.setDims,n||delete a.sizes,"alt"in a||(a.alt=""),!("loading"in a)&&(i?.data?.root?.sectionIndex>1||r)&&(a.loading="lazy");let l=Object.keys(a).map(t=>`${e.escapeExpression(t)}="${e.escapeExpression(a[t])}"`).join(" ");return new e.SafeString(`<img src="${o}"${n?` srcset="${n}"`:""}${l?` ${l}`:""}>`)},bgImage:e=>e?"string"==typeof e?e:e.largeUrl||e.url||"":"",concat(...e){let t="";for(let i in e)null!==e[i]&&void 0!==e[i]&&"object"!=typeof e[i]&&(t+=e[i]);return t},setVideoExtension(e,t){if(!e||!t)return"";let i=new URL(e);if("res.cloudinary.com"!==i.host)return i.pathname.endsWith(`.${t}`)?e:"";let s=i.pathname.lastIndexOf("."),r=i.pathname;return -1!==s&&(r=i.pathname.substr(0,s)),i.pathname=`${r}.${t}`,i.toString()},addVideoSources(e){if(!e)return"";if(!e.includes("res.cloudinary.com"))return`<source data-src="${e}">`;let t=e.replace(/\.(?:3gp|mpeg|mp4|m4v|m4p|ogv|ogg|mov|webm)$/,"");return[{format:"webm",codec:"vp9",htmlCodecs:";codecs=vp9"},{format:"mp4",codec:"h265",htmlCodecs:";codecs=hvc1"},{format:"mp4",codec:"h264",htmlCodecs:""}].map(e=>{let i=`${t.replace(/f_auto(?::video)?(?:,q_auto(?::\w+)?)?/,`f_${e.format},vc_${e.codec},w_1920,c_limit`)}.${e.format}`;return`<source data-src="${i}" type="video/${e.format}${e.htmlCodecs}">`}).join("\n")},cloudinaryVideoFirstFrame:t.getVideoFirstFrame,initials(e,t){if(!e)return"";t?.hash?.endChar&&(e=e.split(t.hash.endChar)[0]);let i=+t?.hash?.maxChars||2,s=e.match(/\b\w/g)?.slice(0,i);return s?(t?.hash?.addText&&(s=s.map(e=>e+t.hash.addText)),s.join(t?.hash?.joinWith||"").toUpperCase()):""},normalizePhone(e){if(!e)return"";let t=String(e).trim();return(t.startsWith("+")?"+":"")+t.replace(/\D/g,"")}}};class i{constructor(e,t,i){if(this.config=e,this.window=t,this.document=i,!this.destroy)throw Error("Controllers must implement a deconstructor")}}let s="lp_lead_form_conversion_fired";function r(e,t){if(e&&t&&"function"==typeof t.sendGoogleTagManagerEvent&&!function(e){try{return"true"===e.sessionStorage.getItem(s)}catch{return!1}}(e))try{t.sendGoogleTagManagerEvent("event","generate_lead");try{e.sessionStorage.setItem(s,"true")}catch{}}catch{}}function a(e){return!!e&&(!!e.expired||Date.now()>=e.expiresAt)}let o="/api/v1/home-valuation";class n extends i{destroy(){}_cleanAddressString(e){if(!e||"string"!=typeof e)return"";let t=e.trim();if(t.includes("User entered address:")){let e=t.match(/User entered address:\s*\n?\s*([^\n]+)/);e&&e[1]&&(t=e[1].trim())}let i=t.indexOf(", call time frame:");return -1!==i&&(t=t.substring(0,i).trim()),t}_transformAddressComponents(e){let t=t=>(e.find(e=>e.componentType===t)||{}).componentName?.text,i={},s=t("street_number"),r=t("route"),a=t("administrative_area_level_1"),o=t("postal_code"),n=t("subpremise"),l=t("locality")||t("sublocality_level_1")||t("neighborhood");return s&&(i.streetNumber=s),r&&(i.streetName=r),l&&(i.city=l),a&&(i.state=a),o&&(i.zipcode=o),n&&(i.addressLine2=n),i}_extractAddressAndCoordinates(e){let t=null,i=null;if(e?.result){let{result:s}=e;s.address?.addressComponents&&(t=this._transformAddressComponents(s.address.addressComponents)),s.geocode?.location&&(i={latitude:s.geocode.location.latitude,longitude:s.geocode.location.longitude})}return{structuredAddress:t,coordinates:i}}_filterEmptyStrings(e){let t={};for(let[i,s]of Object.entries(e))""!==s&&null!=s&&(t[i]=s);return t}_getRawAddress(e){let t=e.activity.content||{},i=e.activity.comments_attributes?.[0]?.text;return[t.address,t["property-address"],t.property_address,t.Property_Address,i,t.message,t.homeValuationAddress,t.propertyAddress].map(e=>e&&this._cleanAddressString(e)).find(Boolean)||""}_logLeadEvent(e,t,i=null){let s={event:e,companyId:this.config.companyId,websiteId:this.config.websiteId,timestamp:new Date().toISOString(),hasEmail:!!t.activity?.author_email,hasAddress:!!this._getRawAddress(t)};if(i&&(s.errorMessage=i.userMessage||(i.status?`Request failed with status ${i.status}`:"An error occurred"),s.errorName=i.name,s.errorStatus=i.status),console.error(`[HomeValuation] ${e}`,JSON.stringify(s)),this.window.posthog)try{this.window.posthog.capture(e,s)}catch(e){console.error("[HomeValuation] PostHog capture failed:",e)}}_getUserFriendlyErrorMessage(e){return"AbortError"===e.name||e.message?.includes("timeout")?"Connection timed out. Please try again.":"TypeError"===e.name&&e.message?.includes("fetch")?"Unable to connect. Please check your internet connection and try again.":503===e.status||502===e.status||504===e.status?"Service temporarily unavailable. Please try again in a moment.":400===e.status?"Please check your information and try again.":"Something went wrong. Please try again or c
3799ontact us directly."}_formatStructuredAddress(e){if(!e||0===Object.keys(e).length)return"";let t=[];return t.push([e.streetNumber,e.streetName,e.addressLine2].filter(Boolean).join(" ")),t.push([[e.city,e.state].filter(Boolean).join(", "),e.zipcode].filter(Boolean).join(" ")),t.filter(Boolean).join(", ")}_buildHomeValuationData(e,t,i,s){let r=this._formatStructuredAddress(i)||this._getRawAddress(e);if(!r)throw Error("Unable to extract home valuation address from form data");let a={companyId:e.companyId,homeValuationAddress:r,homeValuationEstimate:t?new Intl.NumberFormat("en-US",{style:"currency",currency:"USD",maximumFractionDigits:0,minimumFractionDigits:0}).format(t):null,websiteId:e.page.websiteId,sourceUrl:e.activity.source_url},o={leadEmailAddress:e.activity.author_email,leadFullName:e.activity.author_name,leadFirstName:e.activity.firstName,leadLastName:e.activity.lastName,leadPhoneNumber:e.activity.author_phone,parcelId:e.activity.content?.parcelId,resourceId:e.page.pageMeta?.resourceId,pageId:e.page.pageId,sourceResource:e.page.pageMeta?.s
3799ourceResource,pageElementId:e.page.pageMeta?.pageElementId,formId:e.page.pageMeta?.formId,utmSource:e.activity.content?.utm_source,utmMedium:e.activity.content?.utm_medium,utmCampaign:e.activity.content?.utm_campaign,utmTerm:e.activity.content?.utm_term,utmContent:e.activity.content?.utm_content,utmId:e.activity.content?.utm_id,timeToSell:e.activity.content?.timeToSell,lookingToBuy:e.activity.content?.lookingToBuy,consented:!!e.activity.content?.termsAccepted,referrer:e.referrer,content:e.activity.content||{},fields:e.fields||{}},n=this._filterEmptyStrings(o);return{...a,...n,...i&&{address:i},...s&&{coordinates:s}}}async _validateAddressSafely(e){if(!e)return console.error("Home valuation: No address found in form data"),null;try{return await this._validateAddressWithGoogleMaps(e)}catch(e){throw console.error("Error validating address with Google Maps:",e),e.userMessage=this._getUserFriendlyErrorMessage(e),e}}async _getHomeValuationEstimateSafely(e){try{return await this._getHomeValuationEstimate(e)}catch(e){throw console.error("Error getting home valuation estimate:",e),e.userMessage=this._getUserFriendlyErrorMessage(e),e}}_isSafeRedirectUrl(e){try{let{protocol:t}=new URL(e,this.window.location.href);return"https:"===t||"http:"===t}catch{return!1}}_parseResponsePayload(e){try{return e?JSON.parse(e):{}}catch{return{raw:e}}}_createResponseError(e,t){let i=Error(e&&"object"==typeof e&&Object.keys(e).length?e.message||JSON.stringify(e):"Request failed");return e&&"object"==typeof e&&Object.keys(e).length&&Object.assign(i,e),i.status=t,i}_resolveValuationApiBase(){let{config:e}=this;if(void 0!==e.valuationApiBase&&null!==e.valuationApiBase&&(""!==e.valuationApiBase||!e.isBuilder))return e.valuationApiBase;if(e.websiteApiGatewayUrl)return e.websiteApiGatewayUrl;throw Error("websiteApiGatewayUrl/valuationApiBase not configured for home valuation")}async _submitLead(e,t,i){let s=new AbortController,r=setTimeout(()=>s.abort(),1e4);try{let o=await fetch(e,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(t),signal:s.signal});clearTimeout(r);let n=await o.text(),l=this._parseResponsePayload(n);if(!o.ok)throw this._createResponseError(l,o.status);if(l&&"success"===l.status&&l.redirectUrl){if(a(i))return l;if(!this._isSafeRedirectUrl(l.redirectUrl))return console.error("Home valuation: refusing redirect to unsupported protocol"),l;return this.window.location.href=l.redirectUrl,{redirected:!0,url:l.redirectUrl}}return l}catch(e){throw clearTimeout(r),e}}async _submitHomeValuationRequest(e){let t=this._resolveValuationApiBase();this._logLeadEvent("home_valuation_submission_started",e);try{let i=this._getRawAddress(e),s=null;try{s=await this._validateAddressSafely(i)}catch(t){throw this._logLeadEvent("home_valuation_address_validation_failed",e,t),t}let r=null;try{r=await this._getHomeValuationEstimateSafely(s)}catch(t){this._logLeadEvent("home_valuation_estimate_failed",e,t),r=null}let{structuredAddress:a,coordinates:n}=this._extractAddressAndCoordinates(s),l=`${t}${o}`,d=this._buildHomeValuationData(e,r,a,n),c=await this._submitLead(l,d,e.deadline);return this._logLeadEvent("home_valuation_submission_success",e),c}catch(t){throw t.userMessage||(t.userMessage=this._getUserFriendlyErrorMessage(t)),this._logLeadEvent("home_valuation_submission_failed",e,t),t}}async _validateAddressWithGoogleMaps(e){let{config:t,window:i}=this,s=t.googleMapsAPIKey;if(!s&&i.luxuryPresence?.mapApiLoader&&(s=await i.luxuryPresence.mapApiLoader.getMapApiKey()),!s)throw Error("Google Maps API key not configured");let r=new AbortController,a=setTimeout(()=>r.abort(),1e4);try{let t=await fetch(`https://addressvalidation.googleapis.com/v1:validateAddress?key=${s}`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({address:{addressLines:[e]},enableUspsCass:!1}),signal:r.signal});if(clearTimeout(a),!t.ok){let e=Error(`Address validation failed: ${t.status}`);throw e.status=t.status,e}return t.json()}catch(e){throw clearTimeout(a),e}}_hasMandatoryAddressFields(e){return!!(e.streetNumber&&e.streetName&&e.city&&e.state&&e.zipcode)}_logMissingAddressFields(e){let t=[];e.streetNumber||t.push("streetNumber"),e.streetName||t.push("streetName"),e.city||t.push("city"),e.state||t.push("state"),e.zipcode||t.push("zipcode"),console.log("Home valuation estimate skipped - missing required fields:",t.join(", "),"Available fields:",Object.keys(e).join(", "))}_buildAddressQueryString(e){return Object.entries(e).map(([e,t])=>`${encodeURIComponent(e)}=${encodeURIComponent(t)}`).join("&")}async _getHomeValuationEstimate(e=null){let t=`${this._resolveValuationApiBase()}${o}`;if(!e?.result?.address?.addressComponents)return null;let i=this._transformAddressComponents(e.result.address.addressComponents);if(!this._hasMandatoryAddressFields(i))return this._logMissingAddressFields(i),null;let s=this._buildAddressQueryString(i),r=`${t}?${s}`,a=new AbortController,n=setTimeout(()=>a.abort(),1e4);try{let e=await fetch(r,{headers:{Accept:"application/json"},signal:a.signal});if(clearTimeout(n),!e.ok){let t=Error(`Home valuation estimate failed: ${e.status}`);throw t.status=e.status,t}let t=await e.json();return t?.valueDollars??null}catch(e){throw clearTimeout(n),e}}}let l="middleName",d="redirect-pending",c=["name","firstName","lastName",l,"email","phone","phoneNumber","source","message","mlsId","sourceUrl"],u="lp_map_key_failed",h="oauthSuccess",p={cookieyes:{eventName:"cookieyes_consent_update",hasAnalyticsConsent:e=>(function(e){let t=e.cookie.match(/cookieyes-consent=([^;
3799]+)/);if(!t)return!1;try{let e=decodeURIComponent(t[1]);if(e.includes(",")){let t=e.split(",").map(e=>e.trim());if(t.some(e=>e.includes(":")))return t.some(e=>"analytics:yes"===e);return t.includes("analytics")}return e.includes("analytics")}catch(e){return!1}})(e),isAnalyticsAccepted:e=>Array.isArray(e?.accepted)&&e.accepted.includes("analytics")},"lp-consent":{eventName:"lp_consent_update",hasAnalyticsConsent:(e,t)=>!!t.__lpConsent?.state?.c?.analytics,isAnalyticsAccepted:e=>!!e?.choices?.analytics}};function m(e){return"object"==typeof e&&null!==e&&!Array.isArray(e)}function g(e,t){t.forEach(t=>{let i=e[t];Array.isArray(i)&&0===i.length&&delete e[t]})}function y(e,t,i,s){let r=t.map(t=>(function e(t){return Array.isArray(t)?t.flatMap(t=>e(t)):"string"==typeof t?t.split(/\s+/).filter(Boolean):null==t?[]:[t]})(e[t])),a=s?.merge?r.flat():r.find(e=>e.length>0)??[];t.forEach(t=>{delete e[t]}),a.length>0&&(e[i]=[...new Set(a)])}function f(e){let t={...e};if(t.urlWebsite||(t.urlWebsite=null),t.urlYoutube=null,t.urlFacebook=null,t.urlInstagram=null,t.urlLinkedin=null,t.urlPodcast=null,t.urlTwitter=null,Array.isArray(e.socialLinks)){let i=[...e.socialLinks].sort((e,t)=>{let i=e.type?.toLowerCase()||"",s=t.type?.toLowerCase()||"";return i.localeCompare(s)});t.socialLinks=i;let s={youtube:"urlYoutube",facebook:"urlFacebook",instagram:"urlInstagram",linkedin:"urlLinkedin",podcast:"urlPodcast",twitter:"urlTwitter"};i.forEach(e=>{let i=s[e.type?.toLowerCase()??""];i&&(t[i]=e.link)})}return t}function w(e){return e&&Array.isArray(e)?e.map(e=>f(e)):e}function v(e){let{region:t}=e;if(!m(t))return e;let{schools:i,pois:s,...r}=t,a={...r,schools:m(i)?i.data??[]:[],...Array.isArray(s)?{pois:s.map(e=>({...e,category:"string"==typeof e.category?e.category.toUpperCase():e.category}))}:{}};return{...e,region:a}}function b(e){return e&&Array.isArray(e)?e.map(e=>v(e)):e}function S(e){let t=e.featuresAndAmenities;if(!Array.isArray(t))return e;let i=t.map(e=>({featureIds:e.featureIds??[],features:e.features??[],description:e.description}));return{...e,featuresAndAmenities:i}}function _(e){return e&&Array.isArray(e)?e.map(e=>S(e)):e}function E(e,t){return m(e)?t(e):e}function I(e,t){return Array.isArray(e)?t(e):e}let A=new class{constructor(e){this.isBuilder=!1,this.companyId=null,this.websiteId=null,this.pageId=null,this.shareId=null,this.pageMeta=null,this.websiteApiGatewayUrl=null,this.buyerSellerServiceUrl=null,this.leadIngesterUrl=null,this.divolteServiceUrl=null,this.eventBus=null,this.templateId=null,this.myhomeDashboardEnabled=!1,this.mlsEnabled=!1,this.hstValuationRedirectEnabled=!1,this.hvStepTwoCaptureEnabled=!1,this.googleMapsAPIKey=null}get valuationApiBase(){return this.isBuilder?this.websiteApiGatewayUrl:""}destroy(){}setPage(e,t={}){this.pageId=e,this.shareId=t.shareId||null,this.pageMeta={sourceResource:t.sourceResource,pageElementId:t.pageElementId,pageQueryVariables:t.pageQueryVariables||{}}}setIsBuilder(e){this.isBuilder=e}setCompany(e,t={}){this.companyId=e}setWebsite(e,t={}){this.websiteId=e,t.myhomeDashboardEnabled&&(this.myhomeDashboardEnabled=t.myhomeDashboardEnabled),t.mlsEnabled&&(this.mlsEnabled=t.mlsEnabled),t.hstValuationRedirectEnabled&&(this.hstValuationRedirectEnabled=t.hstValuationRedirectEnabled),t.hvStepTwoCaptureEnabled&&(this.hvStepTwoCaptureEnabled=t.hvStepTwoCaptureEnabled)}setWebsiteApiGatewayUrl(e,t={}){this.websiteApiGatewayUrl=e}setBuyerSellerServiceUrl(e,t={}){this.buyerSellerServiceUrl=e}setDivolteServiceUrl(e,t={}){this.divolteServiceUrl=e}setEventBusUrl(e,t={}){this.eventBusUrl=e}setTemplateId(e,t={}){this.templateId=e}setLeadIngesterUrl(e,t={}){this.leadIngesterUrl=e}setGoogleMapsAPIKey(e,t={}){this.googleMapsAPIKey=e}setEventBus({enabled:e,apiKey:t,url:i}){this.eventBus={enabled:e,apiKey:t,url:i}}}({}),L=new class extends i{constructor(e,t,i){super(e,t,i),this.setLoginState=this.setLoginState.bind(this),this.pageId=null,this.modals=[],this.exitModalTriggered={},this.exitModalEventListeners=[]}initialize(e){try{let{config:t,document:i}=this;this.pageId=t.pageId,this.modals=i.querySelectorAll("body > #modals >
3799 .modal"),e.modals.forEach(e=>{this._attachModalHandler(e)})}catch(e){console.log(e)}}destroy(){let{document:e}=this;this.exitModalEventListeners.forEach(t=>{e.removeEventListener("mouseout",t)}),this.modals=[],this.exitModalTriggered={},this.exitModalEventListeners=[]}setModalWithExpiry(e){let{window:t}=this,i=new Date().getTime()+864e5,s=`modal-controller-${e}`;try{t.localStorage.setItem(s,JSON.stringify(i))}catch(e){}}setLoginState(){let{window:e}=this;try{e.localStorage.setItem("isSignedUp","true")}catch(e){}}getModalWithExpiry(e){let{window:t}=this,i=t.localStorage,s=i.getItem(`modal-controller-${e}`);if(!s)return!1;let r=JSON.parse(s);return!(new Date().getTime()>r)||(i.removeItem(`modal-controller-${e}`),!1)}show(e,t={}){let{window:i}=this,s=this.getModalWithExpiry(e);(e&&!s||!0===t.forced)&&(i.lpUI.showModal(`modal-${e}`,t),this.setModalWithExpiry(e))}_attachModalHandler(e){let{pageId:t}=this,{trigger:i,pageSpecific:s}=e;(!s||e.pages.map(e=>e.pageId).includes(t))&&("EXIT"===i?this._exitModalHandler(e):"TIMER"===i?this._timerModalHandler(e):"FORCED"===i&&this._forcedModalHandler(e))}_exitModalHandler(e){let t=this,{document:i,exitModalTriggered:s}=this,{modalId:r}=e;function a(e){!s[r]&&(e.toElement||e.relatedTarget||(t.show(r),s[r]=!0))}i.addEventListener("mouseout",a),this.exitModalEventListeners.push(a)}_timerModalHandler(e){let{modalId:t,timerDelay:i}=e;setTimeout(()=>{this.show(t)},i)}_forcedModalHandler(e){let{modalId:t}=e,{window:i,document:s}=this;if("true"!==i.localStorage.getItem("isSignedUp")){let e=s.querySelector(`#modal-${t}`),r=e?.querySelector("form"),a=r?.dataset.delay||2e3;r.addEventListener("formsubmitted",()=>{this.setLoginState()}),i.addEventListener("oauthSuccess",()=>{this.setLoginState(),r.classList.add("success"),setTimeout(()=>{i.lpUI.hideModal(`modal-${t}`),r.reset(),r.classList.remove("success")},a)}),this.show(t,{forced:!0})}}}(A,window,document),$=new class extends i{constructor(e,t,i){super(e,t,i),this._boundClickHandler=this._buttonClickHandler.bind(this)}initialize(e={}){try{let{document:e}=this;e.body.removeEventListener("click",this._boundClickHandler),e.body.addEventListener("click",this._boundClickHandler)}catch(e){console.error(e)}}destroy(){let{document:e}=this;e.body.removeEventListener("click",this._boundClickHandler)}_buttonClickHandler(e){this._handleButtonClick(e).catch(e=>console.error(e))}async _handleButtonClick(e){let{window:t,document:i}=this,s=e.target.closest("button, a");if(!s)return;let r=s.dataset.type||"",a=s.href||s.dataset.href||"",o=s.target||"",{submit:n}=s.dataset;if(n){try{await this._handleSubmit(s)}catch(e){console.error(e)}((e,t=!1)=>{if(e){if(t)return window.open(e);window.location.assign(e)}})(a,"_blank"===o)}if(this._trackConversion(s),"VIDEO"===r)return void this._handleVideoButton(s);if("CONTACT_US"===r)return void this._handleContactUsButton(s);if("SUBSCRIBE"===r)return void t.lpUI.showModal("modal-global-subscribe");if("MY_ACCOUNT"===r)return void t.lpUI.showModal("modal-global-my-account");if("SHARE_POPUP"===r){let t=s.querySelector(".share-popup");t&&(t.style.display="flex"),e.stopPropagation();return}if("MODAL_"===r.substring(0,6)){let e=r.substring(6);t.lpUI.showModal(`modal-${e}`);return}if(a?.startsWith("mailto:")){let e=a.substring(7);i.sendGoogleAnalyticsEvent({hitType:"event",eventCategory:"button",eventAction:"click",eventLabel:"Email",eventValue:e})}else if(a?.startsWith("tel:")){let e=a.substring(4);i.sendGoogleAnalyticsEvent({hitType:"event",eventCategory:"button",eventAction:"click",eventLabel:"Phone",eventValue:e})}}async _handleSubmit(e){let t=e.closest("form"),{window:i}=this;return t?await i.luxuryPresence.contactForms.submitForm(t):null}_getVimeoSrc(e){if(!e||!e.includes("vimeo.com")||e.length>2e3)return null;let t=e.match(/(?:(?:www\.)?vimeo\.com\/(?:showcase\/\d+(?:\/video\/|\?video[=/])|channels\/(?:\w+\/)?|groups\/(?:[^/]*)\/videos\/|)|player\.vimeo\.com\/video\/)(\d+)(?:(?:\?.*&*h=|\/)+([\d,a-f]+))?/);if(!t)return null;let[,i,s]=t,r=`https://player.vimeo.com/video/${i}?`;return s&&(r+=`h=${s}&`),r+="autoplay=1"}_handleVideoButton(e){let{document:t}=this,i=e.closest("section"),{src:s}=e.dataset,r="";if(s&&(s.includes("youtube.com")||s.includes("youtu.be"))&&!s.includes("youtube.com/embed")){let e=s.match(/(?:youtube\.com\/\S*(?:(?:\/e(?:mbed))?\/|watch\?(?:\S*?&?v=)
3799)|youtu.be\/)([a-zA-Z0-9_-]{6,11})/);e&&(r=e[1])}let a=this._getVimeoSrc(s);if(r?s=`https://youtube.com/embed/${r}?autoplay=1&controls=0`:a&&(s=a),!s||!i)return;let o=s.endsWith(".mp4")||s.endsWith(".webm")||s.includes("cloudinary.com"),n=` 3800 <div id="modal-${i.id}" class="modal visible"> 3801 <div class="modal-content"> 3802 <div class="lightbox-video"> 3803 <div class="video-player"> 3804 <div class="embed-container"> 3805 ${o?`<video controls autoplay src="${s}"></video>`:`<iframe frameborder="0" allowfullscreen="1" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" title="YouTube video player" width="100%" height="100%" src="${s}"></iframe>`} 3806 </div> 3807 </div> 3808 </div> 3809 </div> 3810 <button class="close lp-close" type="button" aria-label="close modal"><span class="lp-close-icon" aria-hidden="true"></span></button> 3811 </div> 3812 `;t.body.insertAdjacentHTML("beforeend",n);let l=t.getElementById(`modal-${i.id}`);l.querySelector(".close").addEventListener("click",()=>{l.remove()})}_handleContactUsButton(e){let{window:t,document:i}=this;i.sendGoogleAnalyticsEvent({hitType:"event",eventCategory:"button",eventAction:"click",eventLabel:"contact"}),t.lpUI.showModal("modal-global-contact-us",{scrollLock:!0})}_trackConversion(e){let{window:t,document:i}=this,{gtagConversionId:s,gtagConversionLabel:r,fbEventName:a}=e.dataset;if(s&&r){t.dataLayer&&t.dataLayer.length&&(t.dataLayer.some(e=>e.length&&"config"===e[0]&&e[1]===s)||i.sendGoogleTagManagerEvent("config",s));let e=`${s}/${r}`;i.sendGoogleTagManagerEvent("event","conversion",{send_to:e})}t.fbq&&a&&t.fbq("track",a,{})}}(A,window,document),x=new class extends i{constructor(e,t,i){super(e,t,i),this.validatedForms=[]}initialize(e={}){try{let{window:t,document:i}=this,{selector:s}=e,r=[];if(r=s?i.querySelectorAll(`${s} form[data-type="contact-form"]`):i.querySelectorAll('form[data-type="contact-form"]'),i.querySelectorAll('input[type="tel"]').length&&!t.libphonenumber&&t?.luxuryPresence?.libs){let{loadJs:e,jsLibsMap:i}=t.luxuryPresence.libs,{libphonenumber:s}=i;e(s.jsSrc,this.attachValidation.bind(this,r))}else this.attachValidation(r);this.validatedForms=this.validatedForms.concat(r)}catch(e){console.log(e)}}destroy(){this.validatedForms=[]}attachValidation(e){e.forEach(e=>{e.querySelectorAll("input, select, textarea").forEach(e=>{this.applyFieldValidation(e)})})}applyFieldValidation(e){"tel"===e.getAttribute("type")&&this._applyPhoneValidation(e)}_applyPhoneValidation(e){let{window:t}=this;if(t.libphonenumber){let i=t.libphonenumber.isValidPhoneNumber;e.addEventListener("keyup",function(){if(this.hasAttribute("required")||this.value.replace(/\s/g,"").length)try{let e=this.value;i(e,"US")?this.setCustomValidity(""):this.setCustomValidity("Please enter a valid phone number.")}catch(e){this.setCustomValidity("Please enter a valid phone number.")}else this.setCustomValidity("")})}}}(A,window,document),T=new class extends i{constructor(e,t,i){super(e,t,i),this.forms=[],this.homeValuationService=new n(e,t,i)}initialize(e={}){try{let{document:t}=this,{selector:i}=e,s=[];(s=i?t.querySelectorAll(`${i} form[data-type="contact-form"]`):t.querySelectorAll('form[data-type="contact-form"]')).forEach(e=>{this._attachSubmitHandler(e)}),this.forms=this.forms.concat(s)}catch(e){console.log(e)}}destroy(){this.forms=[]}submitForm(e){this._handleSubmit(null,e).catch(e=>console.error(e))}_attachValidation(e){let{window:t}=this;e.querySelectorAll("input, select, textarea").forEach(e=>{t.luxuryPresence.validation.applyFieldValidation(e)})}_attachSubmitHandler(e){e.addEventListener("submit",t=>this._handleSubmit(t,e))}_extractFormValues(e){let t=new FormData(e),i={},s=[],r=/^name(?:-\d+)?$/;return t.forEach((e,t)=>{if("string"==typeof e){let a=e.trim();r.test(t)&&a&&s.push(a);let o=i[t];i[t]=o?`${o} ${a}`:a}else i[t]=e}),s.length?i.name=s.join(" ").trim():!i.name&&i.firstName&&(i.name=i.firstName,i.lastName&&(i.name+=` ${i.lastName}`)),this._deriveNameParts(i)}_deriveNameParts(e){if(e.firstName||!e.name)return e;let t=e.name.indexOf(" ");return -1===t?{...e,firstName:e.name}:{...e,firstName:e.name.slice(0,t),lastName:e.lastName||e.name.slice(t+1).trim()}}_buildSubmissionData(e,t,i){let{window:s,config:r}=this,a=Object.keys(e).filter(e=>!c.includes(e)).reduce((t,i)=>(t[i]=e[i],t),{}),{pageId:o,companyId:n,websiteId:l,pageMeta:d}=r,u=s.localStorage.getItem("utm_vars")||"{}",h=s.localStorage.getItem("referrer")||"",p=this._getFormId(i);return{activity:{author_email:e.email,author_name:e.name,firstName:e.firstName||void 0,lastName:e.lastName||void 0,author_phone:e.phone||e.phoneNumber,activity_source:e.source||"CONTACT_INQUIRY",comments_attributes:[{text:e.message||void 0}],source_url:s.location.href,content:{...a,...JSON.parse(u)}},page:{pageId:o,companyId:n,websiteId:l,pageMeta:{...d,formId:p}},companyId:n,referrer:h,fields:t}}_handleSubmissionSuccess(e){let{window:t}=this,i=e.dataset.delay||2e3;e.classList.remove("loading"),e.classList.remove("error"),e.classList.add("success"),e.querySelectorAll(".success").forEach(e
3812=>e.classList.remove("hide")),e.dispatchEvent(new Event("formsubmitted")),setTimeout(()=>{t.lpUI.hideClosestModal(e),e.reset(),t.lpUI.sideMenuHide(),e.querySelectorAll(".success").forEach(e=>e.classList.add("hide")),e.classList.remove("success")},i)}async _handleSubmit(e,t){if(e&&e.preventDefault(),!t.classList.contains("loading")){this._hideSubmissionError(t),t.classList.remove("error"),t.classList.add("loading");try{let e=this._extractFormValues(t);if(e.middleName)return void t.classList.remove("loading");let i=this._getFields(t),s=this._buildSubmissionData(e,i,t);r(this.window,this.document);let a=await this._waitForLeadSubmit(e=>this._submitLeadRequest(s,e));if(this._sendGAEvent(s),a?.redirected)return void this._showRedirectPending(t);this._handleSubmissionSuccess(t)}catch(e){console.error("Form submission failed:",e),t.dispatchEvent(new Event("formsubmiterror")),t.classList.remove("loading"),t.classList.add("error"),e.userMessage?this._showSubmissionError(t,e.userMessage):this._showSubmissionError(t,"Something went wrong. Please try again.")}}}async _waitForLeadSubmit(e){let t,i={expired:!1,expiresAt:Date.now()+6e3},s=new Promise(e=>{t=setTimeout(()=>{i.expired=!0,e(null)},6e3)}),r=e(i).catch(e=>{if(a(i))return null;throw e});try{return await Promise.race([r,s])}finally{clearTimeout(t)}}_showRedirectPending(e){if(e.classList.add(d),e.querySelector(`.${d}__indicator`))return;let t=this.document.createElement("div");t.className=`${d}__indicator`,t.setAttribute("role","status"),t.setAttribute("aria-label","Loading"),t.innerHTML='<span class="lp-i lp-i--spinner lp-i--pulse" aria-hidden="true"></span>',e.appendChild(t)}_aliasUserWithEventBus(e){try{if(!this.window.eventBusProvider?.alias||!this.window.eventBusProvider?.isIdentified||!this.window.eventBusProvider?.getDistinctId||!this.window.LuxuryPresenceEventBus?.generateUUIDv5FromCompanyEmail)return;if(this.window.eventBusProvider.isIdentified())return void console.log("[EventBus] User already identified, skipping alias");let t=e.activity?.author_email,{companyId:i}=this.config;if(!t||!i)return void console.warn("[EventBus] Missing required data for user alias");let s=this.window.eventBusProvider.getDistinctId();if(!s)return void console.warn("[EventBus] No anonymous ID available for alias");let r=this.window.LuxuryPresenceEventBus.generateUUIDv5FromCompanyEmail(i,t);this.window.eventBusProvider.alias({anonId:s,canonicalId:r}),console.log("[EventBus] User aliased with canonical ID")}catch(e){console.error("[EventBus] User alias failed:",e)}}async _submitLeadRequest(e,t){let{config:i}=this;return(this._aliasUserWithEventBus(e),i.myhomeDashboardEnabled&&(e.activity?.activity_source==="HOME_VALUATION"||e.activity?.activity_source==="HOME_VALUE"))?this.homeValuationService._submitHomeValuationRequest({...e,deadline:t}):fetch(`${i.leadIngesterUrl}/api/v1/leads`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(e)}).then(e=>e.ok?e.json():e.json().then(e=>{throw e}))}_sendGAEvent(e){let{document:t}=this,i={hitType:"event",eventCategory:"button",eventAction:"click"};switch(e.activity.activity_source){case"NEWSLETTER_SIGNUP":i.eventLabel="Newsletter",t.sendGoogleTagManagerEvent("event","newsletter_signup");break;case"HOME_SEARCH":i.eventLabel="HomeSearch",t.sendGoogleTagManagerEvent("event","home_search");break;case"HOME_VALUE":case"HOME_VALUATION":i.eventLabel="HomeValue",t.sendGoogleTagManagerEvent("event","home_valuation");break;case"EBOOK":i.eventLabel="Ebook",t.sendGoogleTagManagerEvent("event","ebook_download");break;default:i={hitType:"pageview",page:"/thank-you"},t.sendGoogleTagManagerEvent("event","contact_inquiry")}t.sendGoogleAnalyticsEvent(i)}_getFields(e){let t=e.querySelectorAll("input, select, textarea"),i={};return t.forEach(t=>{let{id:s,name:r,type:a}=t;if(r===l)return!0;let o="";if(s){let i=e.querySelector(`label[for='${s}']`);i?o=i.textContent.trim():t.placeholder&&(o=t.placeholder.trim())}i[r]={label:o,type:t.tagName,inputType:"INPUT"===t.tagName?a:"text"}}),i}_getFormId(e){let t=e.closest("section[id^='section-'], footer[id], .modal[id^='modal-']");return t?.id?this._cleanFormId(t.id):""}_cleanFormId(e){for(let t of["section-","modal-"])if(e.startsWith(t))return e.substring(t.length);return e}_showSubmissionError(e,t){let i=e.querySelector(".submission-error-message");i||((i=this.document.createElement("div")).className="submission-error-message",i.setAttribute("role","alert"),e.appendChild(i)),i.textContent=t||"Something went wrong. Please try again.",i.style.display="block"}_hideSubmissionError(e){let t=e.querySelector(".submission-error-message");t&&(t.style.display="none")}}(A,window,document),k=new class extends i{constructor(e,t,i){super(e,t,i),this.retries=3,this.callbacks=[],this.id=`__googleMapsScriptId${Date.now()}`,this._apiKey="",this.reset()}set apiKey(e){this._apiKey=e,this.config.setGoogleMapsAPIKey(e)}get apiKey(){return this._apiKey}reset(){this.deleteScript(),this.done=!1,this.loading=!1,this.failed=!1,this.errors=[],this.onerrorEvent=null}destroy(){this.reset()}async getMapApiKey(){if(this.apiKey)return this.apiKey;let e=this._hadPreviousFailure(),t=!1;for(let i=1;i<=3;i+=1){let s=e&&!t;try{let e=await fetch(this._getMapKeyUrl(i,s));if(t=t||s,this._setFailureFlag(!1),!e.ok)throw Error(`Failed to fetch API key: ${e.status}`);let r=await e.text();if(!r)return"";return this.apiKey=r,this.apiKey}catch(e){console.error(e),i<3&&await this._delay(300*2**(i-1))}}return this._setFailureFlag(!0),""}_getMapKeyUrl(e,t){let i=[];e>1&&i.push(`attempt=${e}`),t&&i.push("recovered_after_failure=1");let s=i.length?`?${i.join("&")}`:"";return`${this._getBaseUrl()}/api/v1/map${s}`}_hadPreviousFailure(){try{return"1"===this.window.sessionStorage.getItem(u)}catch(e){return!1}}_setFailureFlag(e){try{e?this.window.sessionStorage.setItem(u,"1"):this.window.sessionStorage.removeItem(u)}catch(e){}}_delay(e){return new Promise(t=>{this.window.setTimeout(t,e)})}async createUrl(){let e=await this.getMapApiKey();if(!e)throw Error("Refusing to load Google Maps without an API key");
3812let t="https://maps.googleapis.com/maps/api/js";return t+`?callback=__googleMapsCallback&key=${e}&libraries=places`}async setScript(){let e;if(this.document.getElementById(this.id))return void this.callback();try{e=await this.createUrl()}catch(e){console.error(e),this.failed=!0,this.loading=!1,this.onerrorEvent=e,this.callback();return}let t=this.document.createElement("script");t.id=this.id,t.type="text/javascript",t.src=e,t.onerror=this.loadErrorCallback.bind(this),t.defer=!0,t.async=!0,this.document.head.appendChild(t)}loadErrorCallback(e){if(this.errors.push(e),this.errors.length<=this.retries){let e=this.errors.length*2**this.errors.length;console.log(`Failed to load Google Maps script, retrying in ${e} ms.`),setTimeout(()=>{this.deleteScript(),this.setScript().catch(e=>console.error(e))},e)}else this.failed=!0,this.onerrorEvent=e,this.callback()}deleteScript(){let e=this.document.getElementById(this.id);e&&e.remove()}load(){return this.loadPromise()}loadPromise(){return new Promise((e,t)=>{this.loadCallback(i=>{i?t(i):e()})})}loadCallback(e){this.callbacks.push(e),this.execute()}setCallback(){this.window.__googleMapsCallback=this.callback.bind(this)}resetIfRetryingFailed(){this.failed&&this.reset()}callback(){this.done=!0,this.loading=!1,this.callbacks.forEach(e=>{e(this.onerrorEvent)}),this.callbacks=[]}execute(){this.window.google&&this.window.google.maps&&this.window.google.maps.version&&this.callback(),this.resetIfRetryingFailed(),this.done?this.callback():this.loading||(this.loading=!0,this.setCallback(),this.setScript().catch(e=>console.error(e)))}_getBaseUrl(){let{isBuilder:e,websiteApiGatewayUrl:t}=this.config;return e?t:""}resizeStaticMapDimensions(e,t){if(e<=1024&&t<=1024)return{width:e,height:t};let i=Math.min(1024/e,1024/t);return{width:parseInt(e*i),height:parseInt(t*i)}}convertThemeToStaticApiUrl(e){return e.map(e=>{let t=e.stylers.reduce((e,t)=>{let i=t.visibility?`visibility:${t.visibility}`:"",s=t.color?`color:${t.color.replace("#","0x")}`:"",r=t.lightness?`lightness:${t.lightness}`:"";return`${e}|${i}${s}${r}`},""),i=e.elementType?`|element:${e.elementType}`:"";return`feature:${e.featureType}${i}${t}`})}async getMapApiSignedUrl(e){try{let t=await fetch(`${this._getBaseUrl()}/api/v1/map/signature?${e}`);if(!t.ok)throw Error("Failed to fetch signed URL");let{url:i}=await t.json();return i}catch(e){return console.error(e),""}}generateStaticMapUrl(e,t,i,s,r,a){let{width:o,height:n}=this.resizeStaticMapDimensions(r,a),l={center:`${e},${t}`,zoom:i,scale:2,size:`${o}x${n}`,key:this.apiKey};s&&(l.style=this.convertThemeToStaticApiUrl(s));let d=new URLSearchParams;return Object.entries(l).forEach(([e,t])=>{Array.isArray(t)?t.forEach(t=>d.append(e,t.toString())):d.append(e,t.toString())}),`https://maps.googleapis.com/maps/api/staticmap?${d.toString()}`}async generateDigitallySignedStaticMapUrl({srcWidth:e,srcHeight:t,theme:i,...s}){let{width:r,height:a}=this.resizeStaticMapDimensions(e,t),o={...s,size:`${r}x${a}`};i&&(o.style=this.convertThemeToStaticApiUrl(i));let n=new URLSearchParams;Object.entries(o).forEach(([e,t])=>{Array.isArray(t)?t.forEach(t=>n.append(e,t.toString())):n.append(e,t.toString())});let l=n.toString();try{return await this.getMapApiSignedUrl(l)}catch(e){return console.error(e),""}}}(A,window,document),C=new class extends i{destroy(){}updateQueryString(e,t,i){i||(i=this.window.location.href);let s=RegExp(`([?&])${e}=.*?(&|#|$)`,"i"),r="";null!=t&&(Array.isArray(t)?t.length&&(r=this.serializeArray(e,t)):"object"==typeof t?(s=RegExp(`([?&])${e}\\[[^=]*\\]=.*?(&|#|$)`,"i"),r=this.serializeObj(t,"",e)):r=`${e}=${encodeURIComponent(t)}`);let a=i.split("#"),o=a[0];if(s.test(i)){let e=o;for(r=r?`$1${r}$2`:"$1";s.test(e);)o=e.replace(s,r),e=e.replace(s,"$1");o=o.replace(/(&|\?)$/,"")}else if(r){let e=-1!==o.indexOf("?")?"&":"?";o+=`${e}${r}`}return void 0!==a[1]&&null!==a[1]&&(o+=`#${a[1]}`),o}getParameterByName(e,t){t||(t=this.window.location.href),e=e.replace(/[[\]]/g,"\\$&");let i=RegExp(`[?&]${e}(=([^&#]*)|&|#|$)`),s=Array.from(RegExp.prototype[Symbol.matchAll].call(i,t)).flat();if(!s.length){let i=RegExp(`[?&]${e}(\\[.*?\\])=(.*?)(?=&|#|$)`,"g");return s=Array.from(RegExp.prototype[Symbol.matchAll].call(i,t)).flat(),this._parseParamToObject(s,"advancedFilters"===e)}return s.length>3?s.reduce((e,t,i)=>((i+1)%3==0&&e.push(decodeURIComponent(t.replace(/\+/g," "))),e),[]):s[2]?decode
3812URIComponent(s[2].replace(/\+/g," ")):""}makePageKey(e){return e.replace("section-","").replace(/-/g,"")}makeParamKey(e,t){return e.global?e.urlKey:`${t}-${e.urlKey}`}deserializeParams(e,t){let i={},s=this.makePageKey(t),r=this.getParameterByName(s);return r&&(i.page=parseInt(r)),Array.isArray(e)&&e.forEach(e=>{let t=this.makeParamKey(e,s),r=this.getParameterByName(t);if("int"===e.type)isFinite(r=parseInt(r))||(r=null);else if("bool"===e.type||"boolean"===e.type)r="true"===r||"false"!==r&&null;else if("json"===e.type)try{r=JSON.parse(r)}catch(e){}i[e.formKey||e.param]=r}),i}serializeParams(e,t,i){let s=this.window.location.href,r=this.makePageKey(t);return s=this.updateQueryString(r,i.page,s),Array.isArray(e)&&e.forEach(e=>{let t=this.makeParamKey(e,r),a=i[e.formKey||e.param];"json"===e.type&&null!=a&&(a=JSON.stringify(a)),s=this.updateQueryString(t,a,s)}),s}formatSliderAtom(e,t=""){return t+(Number.isInteger(e)?e:e.toFixed(2))}formatSliderValue(e,t="",i=""){return e<1e3?this.formatSliderAtom(e,t):e<1e6?`${this.formatSliderAtom(e/1e3,t)}${i}K`:`${this.formatSliderAtom(e/1e6,t)}${i}M`}formatSliderPriceRange(e,t,i,s){return t.min===e&&0!==e?`${s.lowPrefix}${this.formatSliderValue(e,i," ")}`:t.max===e?`${this.formatSliderValue(e,i," ")}${s.highSuffix}`:`${this.formatSliderValue(e,i," ")}`}formatSliderAreaRange(e,t,i,s){return t.min===e&&0!==e?`${s.lowPrefix}${this.formatSliderValue(e)} ${i}`:t.max===e?`${this.formatSliderValue(e)}${s.highSuffix} ${i}`:`${this.formatSliderValue(e)} ${i}`}getSearchFormValues(e){let{queryElement:t,priceSliderElement:i,areaSliderElement:s,neighborhoodSelect:r}=e,a={};if(t&&(a.search=t.value||null),i){let e=i.value.split(";"),t=parseInt(i.dataset.min),s=parseInt(i.dataset.max),r=parseInt(e[0]),o=parseInt(e[1]);a.priceMin=r!==t?r:null,a.priceMax=o!==s?o:null}if(s){let e=s.value.split(";"),t=parseInt(s.dataset.min),i=parseInt(s.dataset.max),r=parseInt(e[0]),o=parseInt(e[1]);a.areaMin=r!==t?r:null,a.areaMax=o!==i?o:null}return r&&(a.neighborhoodId=r.value||null),a}serializeObj(e,t="",i=""){let s=[];for(let r in e)if(e.hasOwnProperty(r)){let a,o=e[r];if(i){let e=t?`[${t}][${r}]`:`[${r}]`;a=`${i}${e}`}else a=t?`${t}[${r}]`:r;null!==o&&Array.isArray(o)?o.length&&s.push(this.serializeArray(a,o)):null!==o&&"object"==typeof o?s.push(this.serializeObj(o,a)):s.push(`${a}=${encodeURIComponent(o)}`)}return s.join("&")}serializeArray(e,t,i=""){let s=[];return t.forEach(t=>{let r=i?`${i}${e}`:e;s.push(`${r}=${encodeURIComponent(t)}`)}),s.join("&")}_parseParamToObject(e,t=!1){if(!e.length)return null;let i={};for(let s=0;s<e.length;s++)if((s+1)%3==0){let r=decodeURIComponent(e[s].replace(/\+/g," ")),a=e[s-1];Object.assign(i,this._setObjectPath(a,r,i,t))}return i}_setObjectPath(e,t,i,s=!1){let r=e.split(/[\[\]]/).filter(Boolean),a=["in","nin","btw","ciin","cinin"],o={},n=o,l=((e,t,i)=>{if(!t)return;let s=(Array.isArray(t)?t:t.match(/([^[.\]])+/g)).reduce((e,t)=>e&&e[t],e);return void 0===s?i:s})(i,r.join("."));return r.forEach((e,o)=>{o===r.length-1?l?n[e]=Array.isArray(l)?[...l,t]:[l,t]:n[e]=s&&a.includes(e)?[t]:t:(n[e]=i[e]?i[e]:{},n=n[e])}),o}}(A,window,document),M=new class extends i{constructor(e,t,i){super(e,t,i),this._createLightbox=this._createLightbox.bind(this),this.expandSliders=[]}initialize(e={}){try{let{document:e}=this,t=e.querySelectorAll(".has-mobile-expand");t.forEach(e=>{this._addExpandButton(e)}),this.expandSliders=this.expandSliders.concat(t)}catch(e){console.log(e)}}destroy(){let{document:e}=this;e.querySelectorAll(".lp-expand-btn, .lp-expand-btn-wrapper").forEach(e=>e.remove()),this.expandSliders=[]}_addExpandButton(e){let{document:t}=this,i=e.closest("section[id]"),s=e.dataset.append,r=i.querySelector(".lp-expand-btn");if(!r)if((r=t.createElement("button")).className="lp-expand-btn",r.setAttribute("aria-label","Open a lightbox"),r.innerHTML='<span class="lp-i lp-i--expand-alt" aria-hidden="true"></span>',s){let e=i.querySelector(s);e&&e.appendChild(r)}else{let i=t.createElement("div");i.className="lp-expand-btn-wrapper",i.style.position="relative",i.appendChild(r),e.parentNode.insertBefore(i,e.nextSibling)}r.addEventListener("click",()=>{this._createLightbox({slider:e,section:i})})}_createLightbox({slider:e,section:t,initialIndex:i}){let{window:s,document:r}=this,a=`lightbox-for-${t?.id}`,o=s.$&&s.$.fn.slick,n=o&&e.slick,l="function"==typeof s.Splide,d=e.classList.contains("is-initialized")&&e.classList.contains("splide");function c(){let t=r.createElement("div");t.id=a,t.className="lp-lightbox",t.setAttribute("tabindex","-1"),t.setAttribute("role","dialog"),t.setAttribute("aria-modal","true"),t.innerHTML=` 3813 <button class="lp-lightbox__close" aria-label="close modal"></button> 3814 <div class="lp-lightbox__content"> 3815 <div class="lp-lightbox__carousel splide" role="group" aria-label="Image Gallery"> 3816 <div class="splide__arrows"> 3817 <button class="splide__arrow splide__arrow--prev lp-lightbox-arrow lp-lightbox-arrow--prev"></button> 3818 <button class="splide__arrow splide__arrow--next lp-lightbox-arrow lp-lightbox-arrow--next"></button> 3819 </div> 3820 <div class="splide__track"> 3821 <ul class="splide__list"></ul> 3822 </div> 3823 </div> 3824 </div> 3825 `,"function"==typeof s.lpUI.requestScrollLock?s.lpUI.requestScrollLock(a):r.body.style.overflow="hidden",r.body.appendChild(t);let o=t.querySelector(".lp-lightbox__carousel"),n=o.querySelector(".splide__list");t.classList.add("show"),(d?e.querySelectorAll(".splide__slide:not(.splide__slide--clone) img"):e.querySelectorAll("img")).forEac
3825h(e=>{let t=e.cloneNode(!0);t.removeAttribute("style");let i=r.createElement("li");i.className="lp-lightbox__slide splide__slide",i.appendChild(t),n.appendChild(i)});let l=new s.Splide(o,{rewind:!0,arrows:!0,arrowPath:"",type:"loop",clones:1,pagination:!1,perPage:1,perMove:1});l.mount(),void 0!==i&&i>=0&&l.go(i),setTimeout(()=>{t.focus()},100);let c=e=>{if(!t.classList.contains("show"))return;let i=t.querySelector(".splide__arrow--prev"),s=t.querySelector(".splide__arrow--next"),r=t.querySelector(".lp-lightbox__close");"ArrowLeft"===e.key&&i?(e.preventDefault(),i.click()):"ArrowRight"===e.key&&s?(e.preventDefault(),s.click()):"Escape"===e.key&&r&&(e.preventDefault(),r.click())};r.addEventListener("keydown",c),t.querySelector(".lp-lightbox__close").addEventListener("click",()=>{r.removeEventListener("keydown",c),t.classList.remove("show"),l&&l.destroy(),"function"==typeof s.lpUI.releaseScrollLock?s.lpUI.releaseScrollLock(a):r.body.style.overflow="",t.remove()})}if("function"==typeof s.lpUI.requestScrollLock?s.lpUI.requestScrollLock(a):r.body.style.overflow="hidden",o&&!d){let t,i,o,l,d;i={arrows:!0,dots:!1,infinite:!0,draggable:!0,prevArrow:'<button type="button" class="lp-lightbox-arrow--prev lp-lightbox-arrow" aria-label="Previous"></button>',nextArrow:'<button type="button" class="lp-lightbox-arrow--next lp-lightbox-arrow" aria-label="Next"></button>'},(o=r.createElement("div")).id=a,o.className="lp-lightbox",o.setAttribute("tabindex","-1"),o.setAttribute("role","dialog"),o.setAttribute("aria-modal","true"),o.innerHTML=` 3826 <button class="lp-lightbox__close" aria-label="close modal"></button> 3827 <div class="lp-lightbox__content"> 3828 <div class="lp-lightbox__carousel"></div> 3829 </div> 3830 `,r.body.appendChild(o),l=o.querySelector(".lp-lightbox__carousel"),o.classList.add("show"),n?(t=e.slick.$slides.find("img"),i.initialSlide=e.slick?.currentSlide||0,t.each(function(){s.$(".lp-lightbox__carousel").append(s.$(this).clone(!1).removeAttr("style").wrap('<div class="lp-lightbox__slide"></div>').parent())})):(t=e.querySelectorAll("img")).forEach(e=>{let t=e.cloneNode(!0);t.removeAttribute("style");let i=r.createElement("div");i.className="lp-lightbox__slide",i.appendChild(t),l.appendChild(i)}),s.$(l).slick(i),setTimeout(()=>{o.focus()},100),d=e=>{if(!o.classList.contains("show"))return;let t=o.querySelector(".lp-lightbox-arrow--prev"),i=o.querySelector(".lp-lightbox-arrow--next"),s=o.querySelector(".lp-lightbox__close");"ArrowLeft"===e.key&&t?(e.preventDefault(),t.click()):"ArrowRight"===e.key&&i?(e.preventDefault(),i.click()):"Escape"===e.key&&s&&(e.preventDefault(),s.click())},r.addEventListener("keydown",d),o.querySelector(".lp-lightbox__close").addEventListener("click",()=>{r.removeEventListener("keydown",d),o.classList.remove("show");let t=l?.slick?s.$(l).slick("slickCurrentSlide"):0;l?.slick&&s.$(l).slick("unslick"),n&&s.$(e).slick("slickGoTo",t),"function"==typeof s.lpUI.releaseScrollLock?s.lpUI.releaseScrollLock(a):r.body.style.overflow="",o.remove()})}else l?c():(s.luxuryPresence.libs.loadCss(s.luxuryPresence.libs.jsLibsMap.splide.cssSrc),s.luxuryPresence.libs.loadJs(s.luxuryPresence.libs.jsLibsMap.splide.jsSrc,()=>{c(),s.luxuryPresence.libs.jsLibsMap.splide.isLoaded=!0}))}}(A,window,document),P=new class extends i{destroy(){this.window.divolte||(this.window.divolteLp=function(){(this.window.divolteLp.q=this.window.divolteLp.q||[]).push(arguments)})}initialize(e={}){let{divolteServiceUrl:t}=this.config;t&&!this.window.divolte&&this.loadScript()}loadScript(){let{divolteServiceUrl:e}=this.config,t=this.document.createElement("script");t.id="divolte-script",t.type="text/javascript",t.defer=!0,t.async=!0,t.src=`${e}/divolte.js`,this.document.head.appendChild(t),t.addEventListener("load",this.executeQueue.bind(this))}executeQueue(){(this.window.divolteLp.q||[]).forEach(([e,t])=>{this[e](t)}),this.window.divolteLp=this.divolteLp.bind(this)}divolteLp(e,t){try{this[e](t)}catch(e){console.log(e)}}sendLpEvent(e,t){if(!e.includes("pageView"))return void console.log(`${e} IS NOT SUPPORTED`);let{companyId:i,templateId:s,websiteId:r,pageId:a,shareId:o,pageMeta:n}=this.config;this.window.divolte.signal(e,Object.assign({companyId:i,templateId:s,websiteId:r,pageId:a,shareId:o,pageMeta:n},t))}sendLpPageView(){this.sendLpEvent("pageView",{})}}(A,window,document),U=new class extends i{constructor(e,t,i){super(e,t,i)}initialize(e={}){(void 0!==e.enabled?e.enabled:this.config.eventBus?.enabled)&&this.config.eventBus?.apiKey&&this.config.eventBus?.url&&this.loadScript()}loadScript(){let{eventBusUrl:e}=this.config,t=this.document.createElement("script");t.id="posthog-script",t.type="text/javascript",t.defer=!0,t.async=!0,t.src=`${e}/v2.8.0/event-bus-js.js`,this.document.head.appendChild(t),t.addEventListener("load",this.executeQueue.bind(this))}executeQueue(){this.window.LuxuryPresenceEventBus&&(this.window.eventBusProvider=this.window.LuxuryPresenceEventBus.createEventBusProvider({apiKey:this.config.eventBus.apiKey,host:this.config.eventBus.url,defaultSource:"frontend://website-render-engine"}
3830)),(this.window.eventBusLp.q||[]).forEach(([e,t])=>{this[e](t)}),this.window.eventBusLp=this.eventBusLp.bind(this)}destroy(){if(!this.window.eventBusProvider){this.window.eventBusLp=function(){(this.window.eventBusLp.q=this.window.eventBusLp.q||[]).push(arguments)};return}this.window.eventBusProvider.shutdown()}sendLpEvent(e,t){if("user.activity.page.viewed"!==e)return void console.log(`${e} IS NOT SUPPORTED`);if(!this.window.eventBusProvider)return;let{companyId:i}=this.config;try{this.window.eventBusProvider.capture(e,{company:i,data:{}})}catch(e){console.log("EventBus capture error:",e)}}eventBusLp(e,t){try{this[e](t)}catch(e){console.log(e)}}sendLpPageView(){this.sendLpEvent("user.activity.page.viewed",{})}}(A,window,document),N=new class extends i{constructor(e,t,i){super(e,t,i),this.successListeners=[],this._listenToMessages=this._listenToMessages.bind(this),this._listenToOneTapMessage=this._listenToOneTapMessage.bind(this)}initialize(e={}){try{e.googleSignOnEnabled&&!e.iframe&&this._renderGoogleOneTap(),this.document.querySelectorAll('div[data-signin-type="google-sign-in"]').forEach(e=>this._convertGoogleSignIn(e)),this.document.querySelectorAll('div[data-signin-type="facebook-sign-in"]').forEach(e=>this._convertFacebookSignIn(e))}catch(e){console.log(e)}}destroy(){this.successListeners.forEach(e=>{this.window.removeEventListener("message",this._listenToMessages),this.window.removeEventListener("message",this._listenToOneTapMessage),this.window.removeEventListener(h,this.window[e])})}_renderGoogleOneTap(){if(!this._isGoogleOneTapOnCooldown()){let e=this.document.createElement("div");google.accounts.id.initializeIntermediate({src:this._buildUrlAndCopyAttributes(e,e,"googleOneTap")}),this.window.localStorage.removeItem("googleOneTapExpiration"),this.window.addEventListener("message",this._listenToOneTapMessage)}}_listenToOneTapMessage(e){let t="show_intermediate_iframe",{buyerSellerServiceUrl:i}=this.config,{data:s,origin:r}=e,{type:a,height:o,event:n,source:l,token:d}=s;if(a===t||"hide_intermediate_iframe"===a){let e=this.document.getElementById("onetap_google_intermediate_iframe");e&&(e.style.height=o,e.style.display=a===t?"unset":"none")}r===i&&"GOOGLE_SIGN_ON"===l&&("success"===n?(this._setExpirationLsItem(864e5),this._handleQualifyingQuestionsModal(d)):"cancel"===n&&this._setExpirationLsItem(72e5))}_setExpirationLsItem(e){let t=new Date().getTime()+e;this.window.localStorage.setItem("googleOneTapExpiration",t)}_isGoogleOneTapOnCooldown(){return Number(this.window.localStorage.getItem("googleOneTapExpiration"))>new Date().getTime()}_convertGoogleSignIn(e){this._replaceWithIframe("googleSignIn",e)}_convertFacebookSignIn(e){this._replaceWithIframe("facebookSignIn",e)}_replaceWithIframe(e,t){this._attachSuccessCallback(t);let i=this.document.createElement("iframe");i.src=this._buildUrlAndCopyAttributes(t,i,e),i.dataset.isSocialOauthButton=!0,t.replaceWith(i)}_listenToMessages(e){let{buyerSellerServiceUrl:t}=this.config;if(e.origin===t&&"success"===e.data.event){let e=new Event(h);this.window.dispatchEvent(e),r(this.window,this.document),this.successListeners.forEach(t=>{this.window[t]?this.window[t](e):console.error(`Callback function ${t} not found on window object`)}),this.document.documentElement.classList.add("is-login-success")}}_attachSuccessCallback(e){this.window.addEventListener("message",this._listenToMessages);let t=e.dataset.onSuccessFunctionName;t&&this.successListeners.push(t)}_buildUrlAndCopyAttributes(e,t,i){let{buyerSellerServiceUrl:s,companyId:r,websiteId:a,pageId:o,pageMeta:n}=this.config,{location:l}=this.window,d=l.origin+l.pathname,c=new URL(`${s}/buttons/${i}`),u=this.window.localStorage.getItem("utm_vars"),h=this.window.localStorage.getItem("referrer");c.searchParams.append("companyId",r),c.searchParams.append("websiteId",a),c.searchParams.append("pageId",o),c.searchParams.append("sourceUrl",d),c.searchParams.append("pageMeta",JSON.stringify(n)),u&&c.searchParams.append("utm",u),h&&c.searchParams.append("referrer",h);let p=["data-size","data-width","data-text","data-button-type","data-shape","data-layout","data-max-rows","data-use-continue-as","data-type","data-theme","data-logo_alignment","data-locale"];
3830return Array.from(e.attributes).forEach(e=>{p.includes(e.nodeName)?c.searchParams.append(e.nodeName,e.nodeValue):t.setAttribute("id"===e.nodeName?"data-id":e.nodeName,e.nodeValue)}),c.toString()}_decodeToken(e){try{let t=e.split(".")[1].replace(/-/g,"+").replace(/_/g,"/"),i=4-t.length%4,s=4===i?t:t+"=".repeat(i);return JSON.parse(atob(s))}catch(e){return null}}_handleQualifyingQuestionsModal(e){if(e&&this.window.luxuryPresence&&this.window.luxuryPresence.qualifyingQuestionsModal){let t=this._decodeToken(e);t&&t.email&&this.window.luxuryPresence.qualifyingQuestionsModal.showModal(t.email,"Google Sign On")}}}(A,window,document),R=new class extends i{constructor(e,t,i){super(e,t,i),this.showModal=this.showModal.bind(this),this.hasModalBeenSubmitted=this.hasModalBeenSubmitted.bind(this),this.modalId="qualifying-questions-modal",this.cookieKey="qualifyingQuestionsModalSubmitted",this.qualifyingQuestionsModalEnabled=!0,this.eventListeners=[],this.timeouts=[]}initialize(e={}){this.qualifyingQuestionsModalEnabled=e.qualifyingQuestionsModalEnabled??this.qualifyingQuestionsModalEnabled,this.qualifyingQuestionsModalEnabled&&this.attachModalCloseListeners()}destroy(){this.eventListeners.forEach(({element:e,event:t,handler:i})=>{e&&e.removeEventListener&&e.removeEventListener(t,i)}),this.timeouts.forEach(e=>{clearTimeout(e)}),this.restoreBodyScroll(),this.eventListeners=[],this.timeouts=[]}hasModalBeenSubmitted(){return"true"===this.getCookie(this.cookieKey)}setModalSubmittedCookie(){this.setCookie(this.cookieKey,"true",365)}showModal(e,t){!this.qualifyingQuestionsModalEnabled||this.hasModalBeenSubmitted()||(this.updateEmailValue(e),this.updateTriggerSourceValue(t),this.showModalElement())}getModalElement(){let e=this.document.getElementById(this.modalId);return e||console.error("Modal element not found:",this.modalId),e}updateEmailValue(e){let t=this.getModalElement();if(!t)return;let i=t.querySelector('input[name="email"]');i&&"string"==typeof e&&(i.value=e)}updateTriggerSourceValue(e){let t=this.getModalElement();if(!t)return;let i=t.querySelector(`#${this.modalId}-triggerSource`);i&&"string"==typeof e&&(i.value=e)}showModalElement(){let e=this.getModalElement();e&&(e.style.display="flex",this.blockBodyScroll())}getCookie(e){let t=`; ${this.document.cookie}`.split(`; ${e}=`);return 2===t.length?decodeURIComponent(t.pop().split(";").shift()):null}setCookie(e,t,i){let s="";if(i){let e=new Date;e.setTime(e.getTime()+864e5*i),s=`; expires=${e.toUTCString()}`}let r="; path=/; SameSite=Lax";this.window&&this.window.location&&"https:"===this.window.location.protocol&&(r+="; Secure"),this.document.cookie=`${e}=${encodeURIComponent(t||"")}${s}${r}`}blockBodyScroll(){this._prevOverflow=this.document.body.style.overflow,this.document.body.style.overflow="hidden"}restoreBodyScroll(){this.document.body.style.overflow=this._prevOverflow||"",this._prevOverflow=null}attachModalCloseListeners(){let e=this.getModalElement();if(!e)return;let t=this.document.getElementById(`${this.modalId}-form`);if(t){let i=()=>{this.setModalSubmittedCookie();let t=setTimeout(()=>{e.style.display="none",this.restoreBodyScroll()},100);this.timeouts.push(t)};t.addEventListener("formsubmitted",i),this.eventListeners.push({element:t,event:"formsubmitted",handler:i})}let i=e.querySelector('[data-dismiss="modal"]');if(i){let t=()=>{e.style.display="none",this.restoreBodyScroll()};i.addEventListener("click",t),this.eventListeners.push({element:i,event:"click",handler:t})}let s=t=>{(t.target===e||t.target.id===this.modalId)&&(e.style.display="none",this.restoreBodyScroll())};e.addEventListener("click",s),this.eventListeners.push({element:e,event:"click",handler:s})}}(A,window,document),B=new class extends i{constructor(e,t,i){super(e,t,i),this.isValidating=!1,this.lastValidationResult=null,this.lastValidationTime=null,this.validationCacheDuration=3e5}destroy(){this.isValidating=!1,this.lastValidationResult=null,this.lastValidationTime=null}async validateSession(e={}){let{useCache:t=!0}=e;if(t&&null!==this.lastValidationResult&&this.lastValidationTime&&Date.now()-this.lastValidationTime<this.validationCacheDuration)return this.lastValidationResult;if(this.isValidating)return null!==this.lastValidationResult&&this.lastValidationResult;this.isValidating=!0;let i=` 3831 query { 3832 validSession { 3833 success 3834 } 3835 } 3836 `;try{let e=await this.window.fetch("/graphql",{method:"POST",headers:{"Content-Type":"application/json"},credentials:"include",body:JSON.stringify({query:i})});if(!e.ok)return console.error("SessionController: HTTP error during session validation",e.status),this.lastValidationResult=!1,this.lastValidationTime=Date.now(),!1;let t=await e.json();if(t.errors)return this.lastValidationResult=!1,this.lastValidationTime=Date.now(),!1;let s=t.data?.validSession?.success===!0;return this.lastValidationResult=s,this.lastValidationTime=Date.now(),s}catch(e){return console.error("SessionController: Error validating session",e),this.lastValidationResult=!1,this.lastValidationTime=Date.now(),!1}finally{this.isValidating=!1}}clearCache(){this.lastValidationResult=null,this.lastValidationTime=null}}(A,window,document),O=new class extends i{constructor(e,t,i){super(e,t,i),this.consentListener=null,this.eventName=null}destroy(){this.consentListener&&this.eventName&&(this.document.removeEventListener(this.eventName,this.consentListener),this.consentListener=null)}loadScript(){this.document.getElementById("posthog-script")||this.window.luxuryPresence.eventBus.initialize({enabled:this.config.eventBus?.enabled}),this.document.getElementById("divolte-script")||this.window.luxuryPresence.divolte.initialize({})}removeScripts(){this.window.luxuryPresence?.eventBus?.destroy?.(),this.window.luxuryPresence?.divolte?.destroy?.(),this.document.getElementById("posthog-script")?.remove(),this.document.getElementById("divolte-script")?.remove()}async initialize(e={}){let t=p[e.provider];if(!t)return;this.eventName=t.eventName;let i=await this.window.luxuryPresence.session.validateSession();(t.hasAnalyticsConsent(this.document,this.window)||i)&&this.loadScript(),this.consentListener||(this.consentListener=e=>{t.isAnalyticsAccepted(e.detail)?this.loadScript():this.removeScripts()},this.document.addEventListener(t.eventName,this.consentListener))}}(A,window,document),V=new class extends i{destroy(){}transformVariables(e,t){let i;return g(i={...t},["testimonialId"]),y(i,["testimonialId"],"testimonialIds"),0===i.limit&&delete i.limit,(void 0===i.limit||null===i.limit)&&(("testimonials"===e||"testimonialsCount"===e)&&(i.limit=50),("agents"===e||"agentsCount"===e||"neighborhoods"===e||"neighborhoodsCount"===e||"posts"===e||"postsCount"===e||"pressReleases"===e||"pressReleasesCount"===e)&&(i.limit=100)),!function(e){let{sortDir:t}=e;"string"==typeof t&&t.includes("_")&&(e.sortDir=t.replace(/_/g," "))}(i),!function(e,t){switch(t){case"agent":break;case"testimonials":case"testimonialsCount":g(e,["testimonialIds"]);break;case"agents":case"agentsCount":g(e,["officeId","teamId","developmentId","agentIds","tags"]),y(e,["officeId"],"officeIds"),y(e,["teamId"],"teamIds"),y(e,["developmentId"],"developmentIds");break;case"neighborhoods":case"neighborhoodsCount":g(e,["neighborhoodIds","neighborhoodId"]),y(e,["neighborhoodIds","neighborhoodId"],"neighborhoodId");break;case"developments":case"developmentsCount":g(e,["agentId","neighborhoodIds","neighborhoodId","developmentIds","tag"]),e.agentId&&Array.isArray(e.agentId)&&([e.agentId]=e.agentId),y(e,["neighborhoodIds","neighborhoodId"],"neighborhoodIds");break;case"properties":case"propertiesCount":g(e,["statusIds","statusId","propertyTypeIds","propertyTypeId","propertyId","networkId","propertyIds","agentIds","teamIds","officeIds","neighborhoodIds","
3836spaceTypeIds","addressState","addressCity","backfillProviders","backfillMLSListingIds","backfillMLSAgentIds","backfillMLSOfficeIds"]),["propertyIds","agentIds","teamIds","officeIds","neighborhoodIds","spaceTypeIds","backfillMLSListingIds","backfillMLSAgentIds","backfillMLSOfficeIds"].forEach(t=>y(e,[t],t)),y(e,["statusIds","statusId"],"propertyStatusId"),e.excludeStatusId&&(e.excludePropertyStatusId=e.excludeStatusId,delete e.excludeStatusId),y(e,["propertyTypeIds","propertyTypeId"],"propertyTypeId"),y(e,["networkId"],"networkId"),y(e,["propertyId"],"propertyIds"),["salesPriceGTE","salesPriceLTE","leasePriceGTE","leasePriceLTE","livingSpaceSizeGTE","livingSpaceSizeLTE"].forEach(t=>{null!=e[t]&&(e[t]=Math.trunc(Number(e[t])))});break;case"posts":case"postsCount":g(e,["categoryIds","categoryId","neighborhoodId"]),void 0!==e.order&&(e.sort=e.order,delete e.order),y(e,["categoryIds","categoryId"],"categoryId");break;case"postCategories":g(e,["categoryId","categoryIds"]),void 0!==e.order&&(e.sort=e.order,delete e.order),y(e,["categoryIds","categoryId"],"categoryId");break;case"pressReleases":case"pressReleasesCount":g(e,["pressReleaseId","categories","propertyId","developmentId","agentId","teamId"]),y(e,["pressReleaseId"],"pressReleaseIds");break;case"offices":case"officesCount":g(e,["officeId","officeIds","tags"]),y(e,["officeId"],"officeIds");break;case"teams":case"teamsCount":g(e,["teamId","teamIds"]),y(e,["teamId"],"teamIds");break;case"pois":g(e,["categories"]),Array.isArray(e.categories)&&(e.categories=e.categories.map(e=>e.toLowerCase())),"string"==typeof e.sortBy&&(e.sortBy=e.sortBy.toLowerCase())}}(i,e),i}transformResponse(e,t){return"agent"===e?E(t,f):"agents"===e||"agentsCount"===e?I(t,w):"neighborhood"===e?E(t,v):"neighborhoods"===e||"neighborhoodsCount"===e?I(t,b):"development"===e?E(t,S):"developments"===e||"developmentsCount"===e?I(t,_):t}transformPage(e){return{items:e&&e.items||[],count:e&&e.total||0}}}(A,window,document);function D(e,t){if(window.luxuryPresence[e])try{window.luxuryPresence[e].destroy()}catch(e){console.log(e)}window.luxuryPresence[e]=t}D("config",A),D("modals",L),D("buttons",$),D("validation",x),D("contactForms",T),D("mapApiLoader",k),D("pagination",C),D("expandSliders",M),D("divolte",P),D("eventBus",U),D("oauth",N),D("qualifyingQuestionsModal",R),D("session",B),D("consent",O),D("redirects",{checkHstRedirect:function(e){let{config:t}=window.luxuryPresence;return!t.hvStepTwoCaptureEnabled&&!!t.hstValuationRedirectEnabled&&!!t.mlsEnabled&&!!t.myhomeDashboardEnabled&&(window.location.href=`/home-search/home-valuation?address=${encodeURIComponent(e)}`,!0)},destroy(){}}),D("routerApi",V),window.luxuryPresence.formatSliderPriceRange=C.formatSliderPriceRange.bind(C),window.luxuryPresence.formatSliderAreaRange=C.formatSliderAreaRange.bind(C),window.luxuryPresence.getSearchFormValues=C.getSearchFormValues.bind(C),window.luxuryPresence.handlebarsHelpersFactory=function(t){var i;return((i=e)&&i.__esModule?i.default:i)(t,{isEqual:(e,t)=>e==t})}})(); 3837//# sourceMappingURL=lp-sdk.js.map 3838 3839 </script>
3839 3840 3841
3841<script> 3842 var lpGlobalInitialized = false; 3843 3844 function lpGlobalInit () { 3845 if (lpGlobalInitialized) { 3846 return; 3847 } 3848 lpGlobalInitialized = true; 3849 // Initialize wow animation plugin only according to turbolinks 3850 var wow; 3851 if (window.WOW) { 3852 wow = new WOW({ 3853 animateClass: 'animated', 3854 offset: wowOffset, 3855 live: false, 3856 callback: function(box) {} 3857 }); 3858 } 3859 // Initialize handlebar helpers only once 3860 function handlebarsInit() { 3861 if (!window.Handlebars) { 3862 return; 3863 } 3864 HandlebarsIntl.registerWith(Handlebars); 3865 Handlebars.registerHelper(window.luxuryPresence.handlebarsHelpersFactory(Handlebars)); 3866 } 3867 3868 handlebarsInit(); 3869 3870 var modalContentUrl = "/modals.html"; 3871 const modalsContainer = document.getElementById('modals'); 3872 if (modalsContainer) { 3873 fetch(modalContentUrl).then(function(res) { 3874 if (!res.ok) { 3875 throw new Error(`HTTP error! Status: ${res.status}`); 3876 } 3877 return res.text(); 3878 }).then(function(resultText) { 3879 modalsContainer.innerHTML = resultText; 3880 3881 // now we need to execute all script elements 3882 Array.from(modalsContainer.getElementsByTagName('script')).forEach(function (s) { 3883 const script = document.createElement("script"); 3884 script.text = s.innerHTML; 3885 Array.from(s.attributes).forEach(function (a) { 3886 script.setAttribute(a.name, a.value); 3887 }) 3888 s.parentNode.replaceChild(script, s); 3889 }); 3890 3891 try { 3892 window.luxuryPresence.modals.initialize(window.luxuryPresence._modalIncludes); 3893 window.luxuryPresence.contactForms.initialize({ selector: '#modals' }); 3894 window.luxuryPresence.validation.initialize({ selector: '#modals' }); 3895 window.luxuryPresence.oauth.initialize({ 3896 googleSignOnEnabled: true, 3897 iframe: false }); 3898 } catch (e) { 3899 console.log(e); 3900 } 3901 document.querySelectorAll('.modals > .modal .close').forEach(closeModalBtn => { 3902 closeModalBtn.addEventListener('click', function() { 3903 window.lpUI.hideClosestModal(closeModalBtn); 3904 }); 3905 }); 3906 }).catch((e) => console.log(e)); 3907 } 3908 /* Initialize Luxury Presence SDK */ 3909 try { 3910 window.luxuryPresence.config.setCompany("9994eaf9-a60f-4f64-89b4-cb9e3cc349b1", {}); 3911 window.luxuryPresence.config.setWebsite("5c1357ff-b286-47e5-bde4-3b0c9cb734db", { 3912 myhomeDashboardEnabled: false, 3913 mlsEnabled: true, 3914 hstValuationRedirectEnabled: true, 3915 hvStepTwoCaptureEnabled: false 3916 }); 3917 const url = new URL(window.location.href); 3918 const shareId = url.searchParams.get('shareId'); 3919 window.luxuryPresence.config.setPage("6e9c86fc-e4a1-465f-9799-5ba17f93b61e", { 3920 sourceResource: "", 3921 pageElementId: "", 3922 pageQueryVariables: {}, 3923 shareId: "", 3924 }); 3925 window.luxuryPresence.config.setWebsiteApiGatewayUrl("https://wgw.luxurypresence.com", {}); 3926 window.luxuryPresence.config.setBuyerSellerServiceUrl("https://bss.luxurypresence.com", {}); 3927 window.luxuryPresence.config.setDivolteServiceUrl("https://t.luxurypresence.com", {}); 3928 window.luxuryPresence.config.setEventBusUrl("https://cdn.luxurypresence.com/scripts/event-bus-js", {}); 3929 window.luxuryPresence.config.setEventBus({ 3930 enabled: true, 3931 apiKey: "phc_PMYT31xlmXBaTCwpIRx1v6RrpLVZIuP47N3mOT6KLnr", 3932 url: "https://p.luxurypresence.com", 3933 }); 3934 window.luxuryPresence.config.setTemplateId("1d3967df-14f2-449d-87b7-bd68bf93906f", {}); 3935 window.luxuryPresence.config.setLeadIngesterUrl("https://leads.luxurypresence.com", {}); 3936 window.luxuryPresence.buttons.initialize({}); 3937 window.luxuryPresence.consent.initialize({ provider: 'lp-consent' }); 3938 window.luxuryPresence.contactForms.initialize({}); 3939 window.luxuryPresence.validation.initialize({}); 3940 window.luxuryPresence.expandSliders.initialize({}); 3941 window.luxuryPresence.oauth.initialize({ googleSignOnEnabled: true, iframe: false }); 3942 window.luxuryPresence.qualifyingQuestionsModal.initialize({ qualifyingQuestionsModalEnabled: true }); 3943 } catch (e) { 3944 console.log(e); 3945 } 3946 3947 /* Inject Section Specific Javascript */ 3948 try { 3949(function init(sectionDomId) { 3950 const section = document.getElementById(sectionDomId); 3951 const contactTitle = section.querySelector('.js-contact-title'); 3952 const emailInput = section.querySelector('.js-email-input'); 3953 const emailRegex = /[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~\-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)+/; 3954 emailInput.addEventListener('input', function () { 3955 if (emailRegex.test(this.value)) { 3956 this.setCustomValidity(''); 3957 } else { 3958 this.setCustomValidity('Please enter a valid email address.'); 3959 } 3960 this.reportValidity(); 3961 }); 3962 const inputName = section.querySelector('.js-input-name'); 3963 inputName.addEventListener('keyup', function () { 3964 if (this.value.replaceAll(/\d/g, "").trim().split(" ").length < 2 || this.value.replaceAll(/\d/g, "").trim() === "") { 3965 this.setCustomValidity("Please input your first name and last name."); 3966 } else { 3967 this.setCustomValidity(""); 3968 } 3969 }); 3970 function setHttp(link) { 3971 if (link.search(/^http[s]?\:\/\/|\//) == -1) { 3972 link = 'https://' + link; 3973 } 3974 return link; 3975 } 3976 const socialLinks = section.querySelectorAll('.lp-socials__link, .contact-form-modal-details__socials-l
3976ink'); 3977 socialLinks.forEach(linkElement => { 3978 const href = linkElement.getAttribute('href'); 3979 if (href) { 3980 linkElement.setAttribute('href', setHttp(href)); 3981 } 3982 }); 3983 if (contactTitle.length < 1) return; 3984 let words = contactTitle.textContent.split(" "); 3985 if (words.length > 1) { 3986 contactTitle.innerHTML = `<span class="contact__title-top">${words[0]} </span><span class="contact__title-bottom">${words.splice(1).join(" ")}</span>`; 3987 } 3988 const addRedirectToForm = () => { 3989 const redirectLink = document.getElementById(`${sectionDomId}`).querySelector('.js-redirect-link'); 3990 const form = document.getElementById(`${sectionDomId}`).querySelector('.js-form'); 3991 function redirectPage(link) { 3992 if (link) { 3993 const rawHref = link.getAttribute('href'); 3994 link.setAttribute('href', setHttp(rawHref)); 3995 link.click(); 3996 } 3997 } 3998 function setHttp(link) { 3999 if (link.search(/^http[s]?\:\/\/|\//) == -1) { 4000 link = 'http://' + link; 4001 } 4002 return link; 4003 } 4004 form.addEventListener("reset", function () { 4005 redirectPage(redirectLink); 4006 }); 4007 }; 4008 addRedirectToForm(); 4009})('modal-global-contact-us'); 4010} catch(e) { 4011console.log(e); 4012} 4013 4014 try { 4015(function init(sectionDomId) { 4016 (function () { 4017 const observer = new IntersectionObserver(entries => { 4018 entries.forEach(entry => { 4019 if (entry.isIntersecting) entry.target.classList.add('is-in-viewport'); 4020 }); 4021 }, { 4022 threshold: 0.5 4023 }); 4024 const initialElements = document.querySelectorAll('[data-animate="true"]'); 4025 initialElements.forEach(element => observer.observe(element)); 4026 const container = document.querySelector('.data-container'); 4027 if (container) { 4028 const mutationObserver = new MutationObserver(mutations => { 4029 mutations.forEach(mutation => { 4030 if (mutation.type === 'childList') { 4031 mutation.addedNodes.forEach(node => { 4032 if (node.nodeType === 1 && node.getAttribute('data-animate') === 'true') observer.observe(node); 4033 }); 4034 } 4035 }); 4036 }); 4037 mutationObserver.observe(container, { 4038 childList: true, 4039 subtree: true 4040 }); 4041 } 4042 })(); 4043 const sectionSelector = document.getElementById(sectionDomId); 4044 const searchToggleSelector = sectionSelector.querySelector('.js-search-toggle-btn'); 4045 const searchWrapperSelector = sectionSelector.querySelector('.js-header-search'); 4046 const header = sectionSelector.querySelector('.js-header'); 4047 let lastScrollTop = 0; 4048 const subNavOpeners = sectionSelector.querySelectorAll('.js-subnav-opener'); 4049 subNavOpeners.forEach(opener => { 4050 opener.addEventListener('click', function (e) { 4051 e.preventDefault(); 4052 e.stopPropagation(); 4053 const parent = opener.parentElement; 4054 subNavOpeners.forEach(otherOpener => { 4055 if (otherOpener !== opener) { 4056 otherOpener.parentElement.classList.remove('header-navigation__item--sub-nav--opened'); 4057 } 4058 }); 4059 parent.classList.toggle('header-navigation__item--sub-nav--opened'); 4060 }); 4061 }); 4062 document.addEventListener('click', function (e) { 4063 if (!e.target.closest('.js-subnav-opener') && !e.target.closest('.header-navigation__item--sub-nav--opened')) { 4064 subNavOpeners.forEach(opener => { 4065 opener.parentElement.classList.remove('header-navigation__item--sub-nav--opened'); 4066 }); 4067 } 4068 }); 4069 function initHeaderColors() { 4070 try { 4071 var colorStr = window.getComputedStyle(header).getPropertyValue("--backgroundColor"); 4072 var bgColor = makeColor(colorStr); 4073 var isDark = (bgColor.rgba.r * 299 + bgColor.rgba.g * 587 + bgColor.rgba.b * 114) / 1000 < 128; 4074 sectionSelector.classList.add('is-background-color-' + (isDark ? 'dark' : 'light')); 4075 } catch (e) {} 4076 } 4077 function makeColor(str) { 4078 var color = { 4079 rgba: { 4080 r: 0, 4081 g: 0, 4082 b: 0, 4083 a: 0 4084 } 4085 }; 4086 if (str && str.match) { 4087 var m = str.match(/rgba?\s*\((\d+)\s*,\s*(\d+)\s*,\s*(\d+)(?:\s*,\s*([\.0-9]+))?\)/); 4088 if (m) { 4089 color.rgba.r = parseInt(m[1]); 4090 color.rgba.g = parseInt(m[2]); 4091 color.rgba.b = parseInt(m[3]); 4092 color.rgba.a = parseFloat(m[4]); 4093 if (isNaN(color.rgba.a)) { 4094 color.rgba.a = 1; 4095 } 4096 } 4097 } 4098 return color; 4099 } 4100 initHeaderColors(); 4101 var navEl = document.querySelector('nav > header') || document.querySelector('nav'); 4102 var navbarHeight = navEl ? navEl.offsetHeight : 0; 4103 if (navEl) { 4104 if (window.ResizeObserver) { 4105 new ResizeObserver(function (entries) { 4106 if (!entries.length) return; 4107 navbarHeight = entries[0].target.offsetHeight; 4108 }).observe(navEl); 4109 } else { 4110 window.addEventListener('resize', function () { 4111 navbarHeight = navEl.offsetHeight; 4112 }); 4113 } 4114 } 4115 function isLocomotiveActive() { 4116 return document.body.hasAttribute('data-scroll-container'); 4117 } 4118 function updateHeaderVisibility(currentScrollTop) {
4119 if (!searchWrapperSelector || searchWrapperSelector.style.display !== 'block') { 4120 if (currentScrollTop === 0) header.classList.remove('header--hide');else if (currentScrollTop > lastScrollTop) header.classList.add('header--hide');else header.classList.remove('header--hide'); 4121 } 4122 lastScrollTop = currentScrollTop; 4123 } 4124 function customHeaderHideShow(currentScrollTop) { 4125 if (!navEl) return; 4126 if (currentScrollTop > 0) { 4127 navEl.classList.add('scroll'); 4128 } else { 4129 navEl.classList.remove('scroll'); 4130 } 4131 if (currentScrollTop > lastScrollTop && currentScrollTop > navbarHeight) { 4132 document.documentElement.style.setProperty('--global-header-height', '0px'); 4133 } else { 4134 document.documentElement.style.setProperty('--global-header-height', navbarHeight + 'px'); 4135 } 4136 } 4137 window.addEventListener('scroll', function () { 4138 if (isLocomotiveActive()) return; 4139 var currentScrollTop = window.scrollY || document.documentElement.scrollTop; 4140 updateHeaderVisibility(currentScrollTop); 4141 }); 4142 var isTicking = false; 4143 var latestLocoScrollY = 0; 4144 window.addEventListener('locomotive-scroll', function (e) { 4145 latestLocoScrollY = e.detail.scrollY; 4146 if (!isTicking) { 4147 window.requestAnimationFrame(function () { 4148 customHeaderHideShow(latestLocoScrollY); 4149 updateHeaderVisibility(latestLocoScrollY); 4150 isTicking = false; 4151 }); 4152 isTicking = true; 4153 } 4154 }); 4155 if (searchToggleSelector !== null) { 4156 searchToggleSelector.addEventListener('click', function () { 4157 if (searchWrapperSelector.style.display === 'block') { 4158 searchWrapperSelector.style.display = 'none'; 4159 } else { 4160 searchWrapperSelector.style.display = 'block'; 4161 header.classList.remove('header--hide'); 4162 } 4163 }); 4164 document.addEventListener('click', function (evt) { 4165 if (!searchToggleSelector.contains(evt.target) && !searchWrapperSelector.contains(evt.target)) searchWrapperSelector.style.display = 'none'; 4166 }); 4167 } 4168 window.luxuryPresence.mapApiLoader.load().then(() => {
4169 if (!searchWrapperSelector) return; 4170 const templateHTML = sectionSelector.querySelector('.js-card-template')?.innerHTML; 4171 const itemTemplate = templateHTML && Handlebars.compile(templateHTML); 4172 const searchMls = searchWrapperSelector.dataset.searchMls === 'true'; 4173 const searchProperties = searchWrapperSelector.dataset.searchProperties === 'true'; 4174 const selectedCountries = searchWrapperSelector.dataset.locations; 4175 const searchInputSelector = sectionSelector.querySelector('.js-header-search-input'); 4176 const searchIconSelector = sectionSelector.querySelector('.js-header-btn-search'); 4177 const closeIconSelector = sectionSelector.querySelector('.js-header-btn-close'); 4178 const searchResultsSelector = sectionSelector.querySelector('.js-header-search-results-holder'); 4179 const resultsContainerSelector = sectionSelector.querySelector('.js-header-results-container'); 4180 const loaderSelector = sectionSelector.querySelector('.js-header-spinner'); 4181 const predefinedSelector = sectionSelector.querySelector('.js-header-search-results-predefined'); 4182 const nothingFoundSelector = sectionSelector.querySelector('.js-header-spinner-nothing-found'); 4183 const service = new google.maps.places.AutocompleteService(); 4184 const geocoder = new google.maps.Geocoder(); 4185 var keyword; 4186 var leasePropertyState = false; 4187 var leasePropertySearched; 4188 var isLoading = false; 4189 var newSearchTimer; 4190 var placesResult = {}; 4191 function updateSearchDisplay() { 4192 var hasResults = !!resultsContainerSelector.innerHTML; 4193 var hasPredefined = predefinedSelector.querySelectorAll('a:not(.not-processed)').length > 0; 4194 nothingFoundSelector.style.display = 'none'; 4195 if (hasResults) { 4196 predefinedSelector.style.display = 'none'; 4197 loaderSelector.style.display = 'none'; 4198 resultsContainerSelector.style.display = 'block'; 4199 } else if (isLoading) { 4200 resultsContainerSelector.style.display = 'none'; 4201 predefinedSelector.style.display = 'none'; 4202 loaderSelector.style.display = 'block'; 4203 } else if (hasPredefined) { 4204 resultsContainerSelector.style.display = 'none'; 4205 loaderSelector.style.display = 'none'; 4206 predefinedSelector.style.display = 'block'; 4207 } else { 4208 resultsContainerSelector.style.display = 'none'; 4209 loaderSelector.style.display = 'none'; 4210 predefinedSelector.style.display = 'none'; 4211 if (keyword) nothingFoundSelector.style.display = 'block'; 4212 } 4213 } 4214 function moveSelection(up) { 4215 var count = resultsContainerSelector.innerHTML ? resultsContainerSelector : predefinedSelector; 4216 var links = count.querySelectorAll('.results-link'); 4217 if (!links.length) return; 4218 var idx = -1; 4219 links.forEach((link, i) => { 4220 if (idx === -1 && link.classList.contains('header-search-results__link--selected')) idx = i; 4221 }); 4222 var newIdx; 4223 if (up) { 4224 newIdx = idx - 1; 4225 if (newIdx < 0) newIdx = links.length - 1; 4226 } else { 4227 newIdx = idx + 1; 4228 if (newIdx > links.length - 1) newIdx = 0; 4229 } 4230 if (newIdx !== idx) { 4231 searchResultsSelector.querySelector('.header-search-results__link--selected')?.classList.remove('header-search-results__link--selected'); 4232 links[newIdx].classList.add('header-search-results__link--selected'); 4233 } 4234 } 4235 function startSearch() { 4236 const term = searchInputSelector.value; 4237 if (term === keyword && leasePropertySearched === leasePropertyState) return; 4238 leasePropertySearched = leasePropertyState; 4239 keyword = term; 4240 if (term) doSearch(term, leasePropertySearched);else setResults(null); 4241 } 4242 function mlsSearch(term) { 4243 var query = { 4244 omnibox: term, 4245 limit: 5 4246 }; 4247 return superagent.get(window.location.protocol + '//' + window.location.host + '/home-search/listings.json').query(query).then(function (res) { 4248 if (!Array.isArray(res.body)) return []; 4249 return res.body.map(function (item) { 4250 return { 4251 id: item.id, 4252 display_address: [item.address_1, item.city, [item.state, item.postal_code].filter(function (i) { 4253 return i; 4254 }).join(' ')].filter(function (i) { 4255 return i; 4256 }).join(', ') 4257 }; 4258 }); 4259 }, function (e) { 4260 console.log(e); 4261 return []; 4262 }); 4263 } 4264 function resolveGeoBounds(placeId, description) { 4265 return new Promise(function (resolve, reject) { 4266 geocoder.geocode({ 4267 'placeId': placeId 4268 }, function (results, status) { 4269 if (status != 'OK') return resolve(null); 4270 var result = results[0]; 4271 var bounds = new google.maps.LatLngBounds();
4272 bounds.extend(result.geometry.viewport.getSouthWest()); 4273 bounds.extend(result.geometry.viewport.getNorthEast()); 4274 result.geometry.bounds = bounds.toJSON(); 4275 var placeUrl = window.luxuryPresence.makeMlsLink(result); 4276 return resolve({ 4277 placeUrl: placeUrl, 4278 description: description 4279 }); 4280 }); 4281 }); 4282 } 4283 function addRestrictions(request) { 4284 switch (selectedCountries) { 4285 case 'Worldwide': 4286 return request; 4287 case 'US & Canada': 4288 request.componentRestrictions = { 4289 country: ['us', 'ca'] 4290 }; 4291 return request; 4292 default: 4293 request.componentRestrictions = { 4294 country: 'us' 4295 }; 4296 return request; 4297 } 4298 } 4299 function resolveAutocomplete(term, deeper) { 4300 var request = addRestrictions({ 4301 input: term, 4302 types: deeper ? ['geocode'] : ['(regions)'], 4303 limit: 1 4304 }); 4305 return new Promise(function (resolve, reject) { 4306 service.getPlacePredictions(request, function (results, status) { 4307 if (status !== google.maps.places.PlacesServiceStatus.OK) { 4308 resolve(null); 4309 return; 4310 } 4311 if (!Array.isArray(results)) { 4312 resolve(null); 4313 return; 4314 } 4315 if (deeper) { 4316 var allowedTypes = { 4317 'neighborhood': true, 4318 'administrative_area_level_3': true, 4319 'administrative_area_level_4': true, 4320 'administrative_area_level_5': true, 4321 'sublocality_level_1': true, 4322 'sublocality_level_2': true, 4323 'sublocality_level_3': true 4324 }; 4325 results = results.filter(function (res) { 4326 return res.types.find(t => allowedTypes[t]); 4327 }); 4328 } 4329 if (!results.length) { 4330 resolve(null); 4331 return; 4332 } 4333 resolve(results); 4334 }); 4335 }); 4336 } 4337 function placesSearch(term, ignoreInput) { 4338 if (placesResult.term !== term) { 4339 placesResult = { 4340 term: term, 4341 result: resolveAutocomplete(term).then(function (results) { 4342 if (!ignoreInput && term !== keyword) return null; 4343 if (ignoreInput && !results) return resolveAutocomplete(term, true); 4344 return results; 4345 }).then(function (results) { 4346 if (!ignoreInput && term !== keyword || !results) return []; 4347 var geoResultPromises = results.map(function (result) { 4348 return resolveGeoBounds(result.place_id, result.description); 4349 }); 4350 return Promise.all(geoResultPromises).then(function (geoLinks) { 4351 return geoLinks.filter(function (i) { 4352 return i; 4353 }).slice(0, 3); 4354 }); 4355 }) 4356 }; 4357 } 4358 return placesResult.result; 4359 } 4360 function propertiesSearch(term) { 4361 var payload = { 4362 companyId: window.luxuryPresence.defaults.companyId, 4363 search: term, 4364 offset: 0, 4365 limit: 4, 4366 statusIds: ["5f528253-abb7-484e-95c3-330269ac1105", "959c11cf-8655-4f91-874c-292b0ab7ea6b", "a0012964-4f51-4430-abf8-6547c5ab6441", "df04ccbe-4621-4140-a504-ee1a17430bb7", "88b4ace6-f39b-4b25-a051-8f6dba976833", "96031d77-bbe5-4de3-90d8-1e4e70de8ca8"] 4367 }; 4368 return superagent.post(window.luxuryPresence.defaults.apiGatewayUrl + '/graphql').send({ 4369 query: window.luxuryPresence.defaults.gql.properties, 4370 variables: payload 4371 }).then(function (res) { 4372 return (res.body.data || {}).properties || []; 4373 }, function (err) { 4374 return []; 4375 }); 4376 } 4377 function doSearch(term, leaseProperty) { 4378 isLoading = true; 4379 updateSearchDisplay(); 4380 Promise.all([searchMls ? mlsSearch(term, leaseProperty) : [], searchMls ? placesSearch(term) : [], searchProperties ? propertiesSearch(term, leaseProperty) : []]).then(function (res) { 4381 if (term !== keyword) return; 4382 setResults(res[0], res[1], res[2], leaseProperty); 4383 }); 4384 } 4385 function makeResultsBlockHtml(title, elementClass, links) { 4386 if (!links || !links.length) return '';else return '<div class="header-search-results__block"><div class="header-search-results__title">' + title + '</div><div class="' + elementClass + '">' + links.join('') + '</div></div>'; 4387 } 4388 function setResults(mlsResults, placesResults, propertiesResults, leaseProperty) { 4389 searchResultsSelector.querySelector('.header-search-results__link--selected')?.classList.remove('header-search-results__link--selected'); 4390 isLoading = false;
4391 updateSearchDisplay(); 4392 if (!mlsResults) { 4393 resultsContainerSelector.innerHTML = ''; 4394 updateSearchDisplay(); 4395 return; 4396 } 4397 if (!mlsResults.length && !placesResults.length && !propertiesResults.length) { 4398 updateSearchDisplay(); 4399 return; 4400 } 4401 var propertiesHtml = makeResultsBlockHtml('Featured Properties', 'header-properties', propertiesResults.map(function (result) { 4402 return itemTemplate(result); 4403 })); 4404 var placesHtml = makeResultsBlockHtml('Locations', '', placesResults.map(function (result) { 4405 return `<a class="header-search-results__link lp-icon" href="${result.placeUrl}${leaseProperty ? '&lease_property=true' : ''}">${result.description}</a>`; 4406 })); 4407 var mlsHtml = makeResultsBlockHtml('Address', '', mlsResults.map(function (result) { 4408 return `<a href="/home-search/listings/${result.id}" class="header-search-results__link lp-icon">${result.display_address}</a>`; 4409 })); 4410 resultsContainerSelector.innerHTML = propertiesHtml + placesHtml + mlsHtml; 4411 updateSearchDisplay(); 4412 } 4413 var neighborhoods = null; 4414 function fetchNeighborhoods() { 4415 var url = window.luxuryPresence.defaults.apiGatewayUrl; 4416 var gqlQuery = 'query ($companyId: String, $offset: Int, $limit: Int) {neighborhoods (companyId: $companyId, offset: $offset, limit: $limit) {id, name, mlsLink, googlePlaceData}}'; 4417 var payload = {}; 4418 payload.offset = 0; 4419 payload.limit = 100; 4420 payload.companyId = window.luxuryPresence.defaults.companyId; 4421 return superagent.post(url + '/graphql').withCredentials().send({ 4422 query: gqlQuery, 4423 variables: payload 4424 }).then(function (res) { 4425 neighborhoods = res.body.data && res.body.data.neighborhoods; 4426 if (neighborhoods && !neighborhoods.length) { 4427 neighborhoods = null; 4428 } 4429 }); 4430 } 4431 function setPredefinedNeighborhoods() { 4432 return fetchNeighborhoods().then(function () { 4433 if (!neighborhoods) return; 4434 var placesHtml = makeResultsBlockHtml('Locations', neighborhoods.map(function (item) { 4435 return `<a class="header-search-results__link lp-icon" href="${item.mlsLink}${leasePropertyState ? '&lease_property=true' : ''}">${item.googlePlaceData && item.googlePlaceData.formatted_address || item.name}</a>`; 4436 })); 4437 predefinedSelector.innerHTML = placesHtml; 4438 updateSearchDisplay(); 4439 }); 4440 } 4441 if (searchMls) setPredefinedNeighborhoods(); 4442 closeIconSelector.style.opacity = '0'; 4443 closeIconSelector.style.pointerEvents = 'none'; 4444 searchInputSelector.setAttribute('inputmode', 'search'); 4445 searchInputSelector.addEventListener('input', function () { 4446 if (this.value === '') { 4447 closeIconSelector.style.opacity = '0'; 4448 closeIconSelector.style.pointerEvents = 'none'; 4449 searchIconSelector.style.opacity = '1'; 4450 searchIconSelector.style.pointerEvents = 'auto'; 4451 } else { 4452 closeIconSelector.style.opacity = '1'; 4453 closeIconSelector.style.pointerEvents = 'auto'; 4454 searchIconSelector.style.opacity = '0'; 4455 searchIconSelector.style.pointerEvents = 'none'; 4456 } 4457 window.clearTimeout(newSearchTimer); 4458 newSearchTimer = window.setTimeout(startSearch, 300); 4459 }); 4460 searchInputSelector.addEventListener('keydown', function (ev) { 4461 if (ev.which === 38) { 4462 ev.preventDefault(); 4463 moveSelection(true); 4464 return false; 4465 } 4466 if (ev.which === 40) { 4467 ev.preventDefault(); 4468 moveSelection(); 4469 return false; 4470 } 4471 if (ev.which === 13) { 4472 let $lnk = searchResultsSelector.querySelector('.header-search-results__link--selected'); 4473 if ($lnk) { 4474 $lnk.click(); 4475 searchActive = false; 4476 updateSearchDisplay(); 4477 } else { 4478 $searchBtn.click(); 4479 } 4480 } 4481 }); 4482 closeIconSelector.addEventListener('click', function () { 4483 searchInputSelector.value = ''; 4484 searchInputSelector.dispatchEvent(new Event('input')); 4485 searchActive = false;
4486 updateSearchDisplay(); 4487 }); 4488 searchResultsSelector.addEventListener('mouseenter', function (ev) { 4489 searchResultsSelector.querySelector('.header-search-results__link--selected')?.classList.remove('header-search-results__link--selected'); 4490 }); 4491 function outsideClickHandler(e) { 4492 if (searchWrapperSelector.contains(e.target)) return; 4493 searchActive = false; 4494 updateSearchDisplay(); 4495 } 4496 document.addEventListener('click', outsideClickHandler); 4497 }); 4498})('global-navbar'); 4499} catch(e) { 4500console.log(e); 4501} 4502 try { 4503(function init(sectionDomId, options) { 4504 var elementSelector = options.elementSelector || 'nav'; 4505 var element = document.querySelector(elementSelector); 4506 var sections = document.querySelectorAll('body > section'); 4507 var subNavContainers = element.querySelectorAll('.sub-nav'); 4508 var initialBackgroundColor = options.initialBackgroundColor || element.style.backgroundColor; 4509 var backgroundColorScroll = options.backgroundColorScroll || '#191919'; 4510 var fontColor = options.fontColor || '#fff'; 4511 var fontColorScroll = options.fontColorScroll || '#000'; 4512 var didScroll = false; 4513 var lastScrollTop = 0; 4514 var delta = 5; 4515 4516 // eslint-disable-next-line sonarjs/no-collapsible-if 4517 if (wow) { 4518 // Wowjs might not trigger for position fixed elements 4519 if (window.scrollY > 0) { 4520 element.querySelectorAll('.wow').forEach(el => wow.show(el)); 4521 } 4522 } 4523 function adjustNavPinState() { 4524 // Remove 'visible' class from all subNavContainers 4525 for (let i = 0; i < subNavContainers.length; i++) { 4526 subNavContainers[i].classList.remove('visible'); 4527 } 4528 if (window.scrollY > 0) { 4529 element.style.backgroundColor = backgroundColorScroll; 4530 element.style.color = fontColorScroll; 4531 element.classList.add('scroll'); 4532 } else { 4533 element.style.backgroundColor = initialBackgroundColor; 4534 element.style.color = fontColor; 4535 element.classList.remove('scroll'); 4536 } 4537 } 4538 adjustNavPinState(); 4539 function onScroll() { 4540 didScroll = true; 4541 } 4542 window.addEventListener('scroll', onScroll); 4543 4544 // eslint-disable-next-line 4545 var debounceInterval = setInterval(() => { 4546 if (didScroll) { 4547 // eslint-disable-next-line 4548 hasScrolled(); 4549 didScroll = false; 4550 } 4551 }, 250); 4552 function hasScrolled() { 4553 adjustNavPinState(); 4554 // eslint-disable-next-line 4555 var navbarHeight = element.offsetHeight; 4556 // eslint-disable-next-line 4557 var st = window.scrollY; 4558 4559 // Make sure they scroll more than delta 4560 if (Math.abs(lastScrollTop - st) <= delta) { 4561 return; 4562 } 4563 if (st > lastScrollTop && st > navbarHeight) { 4564 // Scroll Down 4565 element.style.transform = `translateY(-${navbarHeight}px) scaleY(0)`; 4566 document.documentElement.style.setProperty('--global-header-height', '0px'); 4567 4568 // Some sections "follow" the navbar with class sticky (i.e. Producer search) 4569 sections.forEach(section => { 4570 if (section.classList.contains('sticky')) { 4571 section.style.top = '0px'; 4572 } 4573 }); 4574 } else { 4575 // Scroll Up 4576 // eslint-disable-next-line 4577 if (st + window.innerHeight < document.documentElement.scrollHeight) { 4578 element.style.transform = `translateY(0) scaleY(1)`; 4579 document.documentElement.style.setProperty('--global-header-height', `${navbarHeight}px`); 4580 sections.forEach(section => { 4581 if (section.classList.contains('sticky')) { 4582 section.style.top = `${navbarHeight}px`; 4583 } 4584 }); 4585 } 4586 } 4587 lastScrollTop = st; 4588 } 4589})('NAVBAR', JSON.parse("{\"fontColor\":\"#161515\",\"elementSelector\":\".header\",\"fontColorScroll\":\"#fff\",\"backgroundColorScroll\":\"rgba(26,26,26, .8)\",\"initialBackgroundColor\":\"#fff\"}")); 4590} catch(e) { 4591console.log(e); 4592} 4593 try { 4594(function init(sectionDomId) { 4595 if (sectionDomId === "main-section") sectionDomId = "global-sidemenu"; 4596 var sidemenu = document.getElementById(sectionDomId); 4597 var openers = sidemenu.querySelectorAll('.js-sub-nav-opener'); 4598 var items = sidemenu.querySelectorAll('.js-idemenu-navigation-item'); 4599 var watermark = document.querySelector('.js-sidemenu-watermark'); 4600 var onShow = function () { 4601 window.lpUI.requestScrollLock(sectionDomId); 4602 }; 4603 var onHide = function () { 4604 window.lpUI.releaseScrollLock(sectionDomId); 4605 }; 4606 sidemenu.addEventListener('lpui-show', onShow); 4607 sidemenu.addEventListener('lpui-hide', onHide);
4608 openers.forEach(function (opener) { 4609 opener.addEventListener('click', function (event) { 4610 event.preventDefault(); 4611 this.classList.toggle('sidemenu-navigation__link--active'); 4612 this.classList.toggle('sidemenu-navigation__link--sub-nav-active'); 4613 }); 4614 }); 4615 document.body.addEventListener('click', function (evt) { 4616 if (!evt.target.closest('.sidemenu') && !evt.target.closest('.js-sidemenu-opener')) { 4617 window.lpUI.sideMenuHide(); 4618 } 4619 }); 4620 const handleLinkClick = function (href) { 4621 if (href && href.match(/^#[^#].*$/)) { 4622 window.lpUI.sideMenuHide(); 4623 } 4624 }; 4625 const mainNavLinks = sidemenu.querySelectorAll('.sidemenu-navigation__link'); 4626 mainNavLinks.forEach(function (link) { 4627 link.addEventListener('click', function (event) { 4628 const href = this.getAttribute('href'); 4629 handleLinkClick(href); 4630 }); 4631 }); 4632 const subNavLinks = sidemenu.querySelectorAll('.sidemenu-navigation-sub-nav__link'); 4633 subNavLinks.forEach(function (link) { 4634 link.addEventListener('click', function (event) { 4635 const href = this.getAttribute('href'); 4636 handleLinkClick(href); 4637 }); 4638 }); 4639 if (watermark) { 4640 items.forEach(function (item, index) { 4641 item.setAttribute("data-id", index <= 9 ? "0" + (index + 1) : index + 1); 4642 }); 4643 items.forEach(function (item) { 4644 item.addEventListener('mouseenter', function () { 4645 watermark.setAttribute("data-id", item.getAttribute("data-id")); 4646 watermark.style.opacity = 1; 4647 }); 4648 item.addEventListener('mouseleave', function () { 4649 watermark.style.opacity = 0; 4650 }); 4651 }); 4652 } 4653})('global-sidemenu'); 4654} catch(e) { 4655console.log(e); 4656} 4657 4658 try { 4659(function init(sectionDomId) { 4660 const sectionDom = document.getElementById(sectionDomId); 4661 const backToTopBtn = sectionDom.querySelector('.js-back-to-top'); 4662 backToTopBtn?.addEventListener('click', () => { 4663 window.scrollTo({ 4664 top: 0, 4665 behavior: 'smooth' 4666 }); 4667 }); 4668})('global-footer'); 4669} catch(e) { 4670console.log(e); 4671} 4672 4673 try { 4674(function init(sectionDomId) { 4675 const customDropdownHTML = ` 4676 <div class="custom-select" role="presentation"> 4677 <button class="custom-select-toggle" type="button" aria-haspopup="true" aria-expanded="false" role="combobox"></button> 4678 <div class="custom-select-menu"><div class="custom-select-content"></div></div> 4679 </div>`; 4680 const hideOriginalSelect = originalSelect => { 4681 originalSelect.classList.remove(...originalSelect.classList); 4682 originalSelect.classList.add("sr-only"); 4683 }; 4684 const openCustomSelect = customSelectToggle => {
4685 let customSelectMenu = customSelectToggle.nextElementSibling; 4686 customSelectMenu.classList.add("open"); 4687 customSelectToggle.classList.add("open"); 4688 customSelectToggle.ariaExpanded = true; 4689 }; 4690 const closeCustomSelect = customSelectToggle => { 4691 let customSelectMenu = customSelectToggle.nextElementSibling; 4692 customSelectMenu.classList.remove("open"); 4693 customSelectToggle.classList.remove("open"); 4694 customSelectToggle.ariaExpanded = false; 4695 }; 4696 const inheritAttributes = (originalSelect, customSelectToggle, customSelectMenu) => { 4697 customSelectToggle.className = customSelectToggle.className.concat(' ', originalSelect.className); 4698 customSelectToggle.id = originalSelect.getAttribute("id") + "custom"; 4699 customSelectToggle.dataset.name = originalSelect.getAttribute("name"); 4700 customSelectMenu.setAttribute("aria-labelledby", customSelectToggle.id); 4701 }; 4702 const initiateQuerySelectors = customSelect => { 4703 let customSelectToggle = customSelect.querySelector('.custom-select-toggle'), 4704 customSelectMenu = customSelect.querySelector('.custom-select-menu'), 4705 customSelectContent = customSelect.querySelector('.custom-select-content'); 4706 return { 4707 customSelectToggle, 4708 customSelectMenu, 4709 customSelectContent 4710 }; 4711 }; 4712 const initiateToggleText = (originalSelect, customSelectToggle) => { 4713 const dataLabelOption = originalSelect.querySelector("[data-label]"); 4714 const isMultiple = originalSelect?.multiple; 4715 if (!isMultiple) { 4716 const toggleText = dataLabelOption && dataLabelOption.dataset.label || originalSelect.querySelector(":checked")?.textContent || originalSelect.querySelector("option")?.textContent; 4717 customSelectToggle.classList.add("is-placeholder"); 4718 customSelectToggle.innerHTML = toggleText; 4719 } else { 4720 const placeholderText = dataLabelOption && dataLabelOption.dataset.label || originalSelect.querySelector("option")?.textContent || "Select"; 4721 const selectedOptions = Array.from(originalSelect.querySelectorAll("option:checked")).filter(opt => opt.value !== ""); 4722 if (selectedOptions.length === 0) { 4723 customSelectToggle.innerHTML = placeholderText; 4724 customSelectToggle.classList.add("is-placeholder"); 4725 } else if (selectedOptions.length === 1) { 4726 customSelectToggle.innerHTML = selectedOptions[0].textContent; 4727 customSelectToggle.classList.remove("is-placeholder"); 4728 } else { 4729 customSelectToggle.innerHTML = `${selectedOptions[0].textContent} +${selectedOptions.length - 1}`; 4730 customSelectToggle.classList.remove("is-placeholder"); 4731 } 4732 } 4733 }; 4734 const convertOptionsToButtons = (originalSelect, customSelectContent) => { 4735 originalSelect.querySelectorAll('option').forEach(option => { 4736 const button = createButtonFromOption(option); 4737 customSelectContent.appendChild(button); 4738 }); 4739 }; 4740 const createButtonFromOption = option => { 4741 let button = document.createElement('button'); 4742 button.className = "custom-select-item"; 4743 button.setAttribute("role", "option"); 4744 button.setAttribute("type", "button"); 4745 button.textContent = option.textContent; 4746 button.setAttribute('value', option.value); 4747 if (option.disabled) { 4748 button.setAttribute("disabled", "true"); 4749 } 4750 if (option.selected && option.value !== "") { 4751 button.classList.add("is-selected"); 4752 button.setAttribute("aria-selected", "true"); 4753 } 4754 return button; 4755 }; 4756 const initiateEventListeners = (originalSelect, customSelectToggle, customSelectMenu) => { 4757 const isMultiple = originalSelect?.multiple; 4758 originalSelect.addEventListener("focus", function () { 4759 openCustomSelect(customSelectToggle); 4760 customSelectToggle.focus(); 4761 }); 4762 customSelectToggle.addEventListener("click", function () { 4763 if (customSelectToggle.getAttribute("aria-expanded") === "true") { 4764 closeCustomSelect(customSelectToggle); 4765 } else { 4766 openCustomSelect(customSelectToggle); 4767 } 4768 }); 4769 customSelectMenu.addEventListener("click", function (event) { 4770 if (event.target.classList.contains('custom-select-item')) { 4771 const clickedValue = event.target.value; 4772 const clickedOption = originalSelect.querySelector(`option[value="${clickedValue}"]`); 4773 const placeholderOption = originalSelect.querySelector("[data-label]"); 4774 const placeholderText = placeholderOption?.dataset.label || originalSelect.querySelector("option")?.textContent || "Select"; 4775 if (!isMultiple) { 4776 let opts = customSelectMenu.querySelectorAll(".is-selected"); 4777 [].forEach.call(opts, function (el) { 4778 el.classList.remove("is-selected"); 4779 }); 4780 event.target.classList.add("is-selected"); 4781 clickedOption.selected = true; 4782 customSelectToggle.innerHTML = clickedOption.textContent; 4783 if (clickedValue === "" || event.target.classList.contains("is-placeholder")) { 4784 customSelectToggle.classList.add("is-placeholder"); 4785 } else { 4786 customSelectToggle.classList.remove("is-placeholder"); 4787 } 4788 let changeEvent = new Event('change', { 4789 bubbles: true 4790 }); 4791 originalSelect.dispatchEvent(changeEvent); 4792 closeCustomSelect(customSelectToggle); 4793 } else { 4794 if (clickedValue === "") { 4795 customSelectMenu.querySelectorAll(".is-selected").forEach(el => { 4796 el.classList.remove("is-selected"); 4797 el.setAttribute("aria-selected", "false"); 4798 }); 4799 originalSelect.querySelectorAll("option").forEach(opt => opt.selected = false); 4800 customSelectToggle.innerHTML = placeholderText; 4801 customSelectToggle.classList.add("is-placeholder"); 4802 } else { 4803 const isCurrentlySelected = event.target.classList.contains("is-selected"); 4804 event.target.classList.toggle("is-selected"); 4805 event.target.setAttribute("aria-selected", (!isCurrentlySelected).toString()); 4806 clickedOption.selected = !isCurrentlySelected; 4807 const allItem = customSelectMenu.querySelector('.custom-select-item[value=""]'); 4808 if (allItem) { 4809 allItem.classList.remove("is-selected"); 4810 allItem.setAttribute("aria-selected", "false"); 4811 } 4812 const allOption = originalSelect.querySelector('option[value=""]'); 4813 if (allOption) allOption.selected = false;
4814 const selectedItems = customSelectMenu.querySelectorAll(".custom-select-item.is-selected"); 4815 if (selectedItems.length === 0) { 4816 customSelectToggle.innerHTML = placeholderText; 4817 customSelectToggle.classList.add("is-placeholder"); 4818 } else if (selectedItems.length === 1) { 4819 customSelectToggle.innerHTML = selectedItems[0].textContent; 4820 customSelectToggle.classList.remove("is-placeholder"); 4821 } else { 4822 customSelectToggle.innerHTML = `${selectedItems[0].textContent} +${selectedItems.length - 1}`; 4823 customSelectToggle.classList.remove("is-placeholder"); 4824 } 4825 } 4826 let changeEvent = new Event('change', { 4827 bubbles: true 4828 }); 4829 originalSelect.dispatchEvent(changeEvent); 4830 } 4831 } 4832 }); 4833 document.addEventListener("click", function (event) { 4834 if (!customSelectMenu.contains(event.target) && !customSelectToggle.contains(event.target)) { 4835 closeCustomSelect(customSelectToggle); 4836 } 4837 }); 4838 }; 4839 const createCustomSelect = originalSelect => { 4840 const customSelect = document.createElement('div'); 4841 customSelect.innerHTML = customDropdownHTML; 4842 const { 4843 customSelectToggle, 4844 customSelectMenu, 4845 customSelectContent 4846 } = initiateQuerySelectors(customSelect); 4847 inheritAttributes(originalSelect, customSelectToggle, customSelectMenu); 4848 hideOriginalSelect(originalSelect); 4849 initiateToggleText(originalSelect, customSelectToggle); 4850 convertOptionsToButtons(originalSelect, customSelectContent); 4851 initiateEventListeners(originalSelect, customSelectToggle, customSelectMenu); 4852 return customSelect; 4853 }; 4854 const setCustomSelectValue = (originalSelect, value) => {
4855 let customSelect = originalSelect.nextElementSibling; 4856 let customSelectToggle = customSelect.querySelector(".custom-select-toggle"); 4857 let customSelectContent = customSelect.querySelector(".custom-select-content"); 4858 if (!customSelectContent) return; 4859 let selectedOption = value ? Array.from(customSelectContent.children).find(function (b) { 4860 return b.value === value; 4861 }) : customSelectContent.firstChild; 4862 if (!selectedOption) { 4863 return; 4864 } 4865 if (selectedOption.classList.contains("is-disabled")) return; 4866 let elementNumber = Array.from(selectedOption.parentNode.children).indexOf(selectedOption); 4867 let linkedOption = originalSelect.querySelectorAll("option")[elementNumber]; 4868 linkedOption.selected = true; 4869 selectedOption.classList.add("is-selected"); 4870 selectedOption.parentNode.querySelectorAll(".custom-select-item").forEach(el => { 4871 if (el !== selectedOption) { 4872 el.classList.remove("is-selected"); 4873 } 4874 }); 4875 closeCustomSelect(customSelectToggle); 4876 customSelectToggle.innerHTML = linkedOption.innerHTML; 4877 if (linkedOption.value) { 4878 customSelectToggle.classList.remove("is-placeholder"); 4879 if (linkedOption.value !== "") { 4880 customSelect.classList.add("has-selection"); 4881 } else { 4882 customSelect.classList.remove("has-selection"); 4883 } 4884 } else { 4885 customSelect.classList.remove("has-selection"); 4886 customSelectToggle.classList.add("is-placeholder"); 4887 } 4888 }; 4889 const customSelect = originalSelect => { 4890 const customSelect = createCustomSelect(originalSelect); 4891 originalSelect.insertAdjacentElement('afterend', customSelect); 4892 return customSelect; 4893 }; 4894 const resetCustomSelect = originalSelect => { 4895 const customSelectEl = originalSelect.nextElementSibling; 4896 const customSelectToggle = customSelectEl?.querySelector('.custom-select-toggle'); 4897 const customSelectContent = customSelectEl?.querySelector('.custom-select-content'); 4898 if (!customSelectToggle || !customSelectContent) return; 4899 const placeholderOption = originalSelect.querySelector("[data-label]"); 4900 const placeholderText = placeholderOption?.dataset.label || originalSelect.querySelector("option")?.textContent || "Select"; 4901 customSelectContent.querySelectorAll('.custom-select-item').forEach(item => { 4902 item.classList.remove('is-selected'); 4903 item.setAttribute('aria-selected', 'false'); 4904 }); 4905 originalSelect.querySelectorAll('option').forEach(opt => opt.selected = false); 4906 customSelectToggle.innerHTML = placeholderText; 4907 customSelectToggle.classList.add('is-placeholder'); 4908 const customSelectWrapper = customSelectEl.querySelector('.custom-select'); 4909 if (customSelectWrapper) { 4910 customSelectWrapper.classList.remove('has-selection'); 4911 } 4912 }; 4913 const section = document.getElementById(sectionDomId); 4914 const closeBtn = section.querySelector(".js-close-btn"); 4915 const connectBtn = section.querySelector(".js-btn-connect"); 4916 const popUp = section.querySelector(".js-pop-up"); 4917 const btnHolder = section.querySelector(".js-btn-holder"); 4918 const form = section.querySelector(".js-form"); 4919 const successMessage = section.querySelector(".js-success"); 4920 const redirectLink = section.querySelector(".js-redirect-link"); 4921 function setHttp(link) { 4922 if (link.search(/^(http[s]?\:\/\/|\/)/) == -1) { 4923 link = "https://" + link; 4924 } 4925 return link; 4926 } 4927 function redirectPage(link) { 4928 if (link) { 4929 window.location.href = setHttp(link.getAttribute("href")); 4930 } 4931 } 4932 const selects = document.querySelectorAll(".floating-contact-button select.js-select"); 4933 selects.forEach(function (el) { 4934 customSelect(el); 4935 }); 4936 const inputName = form.querySelector('.js-input-name'); 4937 inputName.addEventListener('keyup', function () { 4938 if (this.value.replaceAll(/\d/g, "").trim().split(" ").length < 2 || this.value.replaceAll(/\d/g, "").trim() === "") { 4939 this.setCustomValidity("Please input your first name and last name."); 4940 } else { 4941 this.setCustomValidity(""); 4942 } 4943 }); 4944 function showSuccessMessage() { 4945 successMessage.style.display = "block"; 4946 form.style.display = "none"; 4947 } 4948 form.addEventListener("formsubmitted", showSuccessMessage); 4949 form.addEventListener("reset", function () { 4950 showSuccessMessage(); 4951 redirectPage(redirectLink); 4952 }); 4953 window.addEventListener("scroll", function () { 4954 if (window.scrollY >= 150 && window.getComputedStyle(popUp).display !== "block") btnHolder.classList.add("floating-contact-button__btn-holder--visible");else btnHolder.classList.remove("floating-contact-button__btn-holder--visible"); 4955 }); 4956 closeBtn.addEventListener("click", function () { 4957 popUp.style.display = "none"; 4958 btnHolder.classList.add("floating-contact-button__btn-holder--visible"); 4959 }); 4960 connectBtn.addEventListener("click", function () { 4961 popUp.style.display = "block"; 4962 btnHolder.classList.remove("floating-contact-button__btn-holder--visible"); 4963 }); 4964})('global-mobile-contact'); 4965} catch(e) { 4966console.log(e); 4967} 4968 4969 try { 4970(function init(sectionDomId) { 4971 const scrollSection = document.getElementById(`${sectionDomId}`).querySelector('.js-scroll-section'); 4972 const introBtn = document.getElementById(`${sectionDomId}`).querySelector('.jsScrollBtn'); 4973 introBtn.addEventListener("click", function (e) { 4974 e.preventDefault(); 4975 window.scrollTo({ 4976 top: scrollSection.offsetTop + scrollSection.offsetHeight, 4977 behavior: "smooth" 4978 }); 4979 }); 4980}
4980)('section-cb4f0ad5-895f-4cf1-9273-df0dedffadbc', 'undefined'); 4981} catch(e) { 4982console.log(e); 4983} 4984 try { 4985(function init(sectionDomId, options) { 4986 let section = null; 4987 switch (sectionDomId) { 4988 case 'NAVBAR': 4989 section = document.querySelector('body > nav, #global-navbar'); 4990 break; 4991 case 'FOOTER': 4992 section = document.querySelector('body > footer, #global-footer'); 4993 break; 4994 case 'SIDEMENU': 4995 section = document.querySelector('body > div.sidemenu, #global-sidemenu'); 4996 break; 4997 case 'CONTACT': 4998 section = document.querySelector('#modal-global-contact-us'); 4999 break; 5000 case 'MOBILE_CONTACT': 5001 section = document.querySelector('body > div.mobile-contact-wrapper, #global-mobile-contact'); 5002 break; 5003 case 'SUBSCRIBE': 5004 // Subscribe Not Implemented 5005 section = null; 5006 break; 5007 case 'ACCOUNT': 5008 // Account Not Implemented 5009 section = null; 5010 break; 5011 default: 5012 section = document.querySelector('#' + sectionDomId); 5013 } 5014 if (!section) { 5015 return; 5016 } 5017 5018 // Where content / items are rendered 5019 const dataContainer = section.querySelector('.data-container'); 5020 if (!dataContainer) { 5021 // no place to render items = no need to query server or do anything else 5022 return; 5023 } 5024 let variables = options.variables || {}; 5025 5026 // Some variables in options may not have been injected - remove 5027 Object.keys(variables).forEach(function (variableName) { 5028 if (typeof variables[variableName] === 'string' && variables[variableName].match(/^{{.*}}$/)) { 5029 delete variables[variableName]; 5030 } 5031 }); 5032 5033 // Merge page query variables 5034 if (options.usePageResource) { 5035 variables = { 5036 ...variables, 5037 ...window.luxuryPresence.config.pageMeta.pageQueryVariables[options.resource] 5038 }; 5039 } 5040 5041 // ROUTER-CLEANUP: keep router branch, drop legacy when API retired. Callers 5042 // resolve the flag â fetchData reads the section stamp, aux fetches read 5043 // their own resource's stamp (auxUsesRouter). 5044 function apiUrl(useRouter) { 5045 return options.url || (useRouter ? window.luxuryPresence.defaults.routerUrl : window.luxuryPresence.defaults.apiGatewayUrl); 5046 } 5047 5048 // ROUTER-CLEANUP: aux resources roll out on their own flag, not the section's 5049 // (options.routerApiAux is stamped per aux resource server-side). 5050 function auxUsesRouter(resource) { 5051 return !!(options.routerApiAux && options.routerApiAux[resource]); 5052 } 5053 5054 // Pagination Executes on a Numbered Pagination Container 5055 // Or via a load more button 5056 let mode = 'BUTTONLESS_PAGINATION'; 5057 const container = section.querySelector('.pagination-container');
5058 const loadMoreButton = section.querySelector(options?.loadMoreSelector); 5059 if (container) { 5060 mode = 'NUMBERED_PAGINATION'; 5061 } 5062 if (loadMoreButton) { 5063 mode = 'LOAD_MORE_BUTTON'; 5064 } 5065 let searchContainer = section.querySelector('.search-container'); 5066 const hidePaginationOnFirstPage = options.hidePaginationOnFirstPage || false; 5067 const eventPrefix = '__pagination-'; 5068 const searchFormParams = options.searchFormParams || (options.resource === 'properties' ? [{ 5069 param: 'neighborhoodId', 5070 urlKey: 'neighborhood' 5071 }, { 5072 param: 'search', 5073 urlKey: 'search' 5074 }, { 5075 param: 'salesPriceGTE', 5076 urlKey: 'pricemin', 5077 formKey: 'priceMin', 5078 type: 'int' 5079 }, { 5080 param: 'salesPriceLTE', 5081 urlKey: 'pricemax', 5082 formKey: 'priceMax', 5083 type: 'int' 5084 }, { 5085 param: 'livingSpaceSizeGTE', 5086 urlKey: 'areamin', 5087 formKey: 'areaMin', 5088 type: 'int' 5089 }, { 5090 param: 'livingSpaceSizeLTE', 5091 urlKey: 'areamax', 5092 formKey: 'areaMax', 5093 type: 'int' 5094 }] : []); 5095 5096 // Total entries - initialized in first render 5097 let totalNumber = 0; 5098 const DefaultSearchParams = { 5099 page: 1 5100 }; 5101 searchFormParams.forEach(function (formParam) { 5102 DefaultSearchParams[formParam.formKey || formParam.param] = null; 5103 }); 5104 let searchParams = { 5105 ...DefaultSearchParams 5106 }; 5107 const offset = options.variables?.offset || 0; 5108 let scrollTarget = options.scrollTargetSelector ? section.querySelector(options.scrollTargetSelector) || section : section; 5109 let scrollOffset = (parseInt(options.scrollOffset, 10) || 0) ?? 100; 5110 5111 // entries of per page 5112 // when query variables for a given section would't be filled options.pageSize can store 5113 // template string, ex: "{{variables.limit}}", below code handles that case and set 9 as a default value 5114 let pageSize = parseInt(options.pageSize, 10) || 9; 5115 5116 // Adding mobile pageRange support 5117 var NARROW_BREAKPOINT = 600; 5118 var mobileBreakpoint = options.mobileBreakpoint > 0 ? options.mobileBreakpoint : NARROW_BREAKPOINT; 5119 var paginationMql = window.matchMedia(`(max-width: ${mobileBreakpoint}px)`); 5120 5121 // Page range (pages on both sides of the current page) 5122 var mobilePageRange = options.mobilePageRange > 0 ? options.mobilePageRange : 0; 5123 var desktopPageRange = options.pageRange >= 0 ? options.pageRange : 2; 5124 var pageRange = paginationMql.matches ? mobilePageRange : desktopPageRange; 5125 5126 // Whether to display the 'Previous' button 5127 var showPrevious = true; 5128 5129 // Whether to display the 'Next' button 5130 var showNext = true; 5131 5132 // Whether to display the page buttons 5133 var showPageNumbers = true; 5134 5135 // 'Previous' text 5136 var prevText = options.prevText || '«'; 5137 5138 // 'Next' text 5139 var nextText = options.nextText || '»'; 5140 5141 // Ellipsis text 5142 var ellipsisText = '…'; 5143 var classPrefix = 'paginationjs'; 5144 5145 // Default active class 5146 var activeClassName = 'active'; 5147 5148 // Default disable class 5149 var disableClassName = 'disabled'; 5150 5151 // Pagination element's position in the container 5152 var position = 'bottom'; 5153 5154 // Whether to trigger pagination at initialization 5155 var triggerPagingOnInit = !options.skipInitialLoad; 5156 var showFirstOnEllipsisShow = true; 5157 var showLastOnEllipsisShow = true; 5158 5159 // Instantiated in init() 5160 var el = null; 5161 5162 // Controls scrollTop logic 5163 var firstRender = true; 5164 var renderBySearch = false; 5165 var disabled = false; 5166 5167 // Pagination script class selector 5168 var paginationScriptSelector = options.paginationPartialName ? `.${options.paginationPartialName}` : '.pagination-script'; 5169 5170 // Property Specific Image Override if No Image 5171 var propertyPlaceholderImage = window.luxuryPresence.defaults.propertyPlaceholderImage; 5172 5173 // Construct template 5174 var templateHTML = section.querySelector(`script${paginationScriptSelector}`)?.innerHTML || ''; 5175 var template = Handlebars.compile(templateHTML); 5176 5177 // Initialize 5178 init(); 5179 function init() { 5180 Object.assign(searchParams, window.luxuryPresence.pagination.deserializeParams(searchFormParams, sectionDomId)); 5181 5182 // Append/prepend pagination element to the pagination container 5183 if (mode === 'NUMBERED_PAGINATION') { 5184 el = document.createElement('div'); 5185 el.className = 'paginationjs';
5186 container[position === 'bottom' ? 'append' : 'prepend'](el); 5187 } 5188 if (searchContainer) { 5189 initSearchContainer({ 5190 fetchNeighborhoods: searchContainer.dataset.fetchNeighborhoods === 'true', 5191 fetchPostCategories: searchContainer.dataset.fetchPostCategories === 'true', 5192 fetchOffices: searchContainer.dataset.fetchOffices === 'true', 5193 fetchRentalRates: searchContainer.dataset.fetchRentalRates === 'true' 5194 }, function () { 5195 syncSearchFormState(searchParams); 5196 }); 5197 } 5198 5199 // Bind events 5200 observer(); 5201 } 5202 function syncSearchFormState(params) { 5203 searchContainer.dispatchEvent(new CustomEvent('sync', { 5204 detail: params 5205 })); 5206 } 5207 function initSearchContainer(options, done) { 5208 var promises = []; 5209 if (options.fetchNeighborhoods) { 5210 promises.push(fetchAllNeighborhoods(function (neighborhoods) { 5211 searchContainer.dispatchEvent(new CustomEvent('neighborhoods-data', { 5212 detail: { 5213 data: neighborhoods.map(function (item) { 5214 return { 5215 value: item.id, 5216 label: item.name 5217 }; 5218 }) 5219 } 5220 })); 5221 })); 5222 } 5223 if (options.fetchPostCategories) { 5224 promises.push(fetchPostCategories(function (categories) { 5225 searchContainer.dispatchEvent(new CustomEvent('post-categories-data', { 5226 detail: { 5227 data: categories 5228 } 5229 })); 5230 })); 5231 } 5232 if (options.fetchOffices) { 5233 promises.push(fetchOffices(function (offices) { 5234 searchContainer.dispatchEvent(new CustomEvent('offices-data', { 5235 detail: { 5236 data: offices 5237 } 5238 })); 5239 })); 5240 } 5241 if (options.fetchRentalRates) { 5242 promises.push(fetchAllRentalRates(function (rentalRates) { 5243 searchContainer.dispatchEvent(new CustomEvent('rental-rates-data', { 5244 detail: { 5245 data: rentalRates 5246 } 5247 })); 5248 })); 5249 } 5250 if (promises.length > 0) { 5251 Promise.all(promises).then(function () { 5252 done(); 5253 }); 5254 } else { 5255 // element's custom code is not executed yet, so we're scheduling callback to after element's code 5256 setTimeout(done); 5257 } 5258 } 5259 function fetchAllNeighborhoods(done) { 5260 var useRouter = auxUsesRouter('neighborhoods'); 5261 // ROUTER-CLEANUP: keep router branch, drop legacy when API retired. Same 5262 // args on both APIs (the federated neighborhoods field accepts 5263 // withProperties/withBlogPosts); only the selection set differs. 5264 var gqlQuery = 'query ($companyId: String, $withProperties: NeighborhoodPropertiesFilter, $withBlogPosts: NeighborhoodBlogPostsFilter, $offset: Int, $limit: Int) {' + 'neighborhoods (companyId: $companyId, withProperties: $withProperties, withBlogPosts: $withBlogPosts, offset: $offset, limit: $limit) {' + (useRouter ? 'items {id, name}' : 'id, name') + '}}'; 5265 var payload = {}; 5266 payload.offset = 0; 5267 payload.limit = 100; 5268 payload.companyId = variables.companyId; 5269 if (options.resource === 'properties') { 5270 payload.withProperties = { 5271 active: true 5272 }; 5273 ['agentIds', 'tag', 'propertyTypeId', 'statusIds', 'architectureStyle', 'lifestyle', 'salesPriceGTE', 'salesPriceLTE', 'leasePriceGTE', 'leasePriceLTE', 'livingSpaceSizeGTE', 'livingSpaceSizeLTE', 'bathCountGTE', 'bathCountLTE', 'bedroomCountGTE', 'bedroomCountLTE', 'bathCountGTE', 'bathCountLTE', 'bedroomCountGTE', 'bedroomCountLTE', 'featuredListing', 'leaseProperty', 'neighborhoodId', 'developmentId', 'openHouse'].forEach(function (field) { 5274 if (variables[field] !== undefined && variables[field] !== null) { 5275 payload.withProperties[field] = variables[field]; 5276 } 5277 }); 5278 } 5279 if (options.resource === 'posts') { 5280 payload.withBlogPosts = { 5281 active: true 5282 }; 5283 ['search', 'postStatusId', 'featured', 'categoryId', 'categoryIds', 'neighborhoodId', 'slug'].forEach(function (field) { 5284 if (variables[field] !== undefined && variables[field] !== null) { 5285 payload.withBlogPosts[field] = variables[field]; 5286 } 5287 }); 5288 } 5289 5290 // ROUTER-CLEANUP: keep router branch, drop legacy when API retired 5291 if (useRouter) { 5292 payload = window.luxuryPresence.routerApi.transformVariables('neighborhoods', payload); 5293 } 5294 superagent.post(apiUrl(useRouter) + '/graphql').withCredentials().send({ 5295 query: gqlQuery, 5296 variables: payload 5297 }).then(function (res) { 5298 var value = res.body.data && res.body.data.neighborhoods || {}; 5299 // ROUTER-CLEANUP: keep router branch, drop legacy when API retired 5300 var items = useRouter ? window.luxuryPresence.routerApi.transformPage(value).items : value || []; 5301 done(items, items.length); 5302 }); 5303 } 5304 function fetchPostCategories(done) { 5305 var featured = options.resource === 'posts' && !!variables.featured; 5306 var useRouter = auxUsesRouter('postCategories'); 5307 // ROUTER-CLEANUP: keep router branch, drop legacy when API retired. Only the 5308 // selection set differs ({ items {...} } on the router). 5309 var selection = 'id, name, publishedCount, featuredPublishedCount'; 5310 var gqlQuery = 'query ($companyId: String, $offset: Int, $limit: Int) {' + 'postCategories (companyId: $companyId, offset: $offset, limit: $limit ' + (featured ? ' withFeaturedPublishedOnly: true' : ' withPublishedOnly: true') + ') {' + (useRouter ? 'items {' + selection + '}' : selection) + '}}'; 5311 var payload = {}; 5312 payload.offset = 0; 5313 payload.limit = 100; 5314 payload.companyId = variables.companyId; 5315 superagent.post(apiUrl(useRouter) + '/graphql').withCredentials().send({ 5316 query: gqlQuery, 5317 variables: payload 5318 }).then(function (res) { 5319 var value = res.body.data && res.body.data.postCategories || {}; 5320 // ROUTER-CLEANUP: keep router branch, drop legacy when API retired 5321 var items = useRouter ? window.luxuryPresence.routerApi.transformPage(value).items : value || []; 5322 done(items.map(function (item) { 5323 item.count = featured ? item.featuredPublishedCount : item.publishedCount; 5324 return item; 5325 }), items.length); 5326 }); 5327 } 5328 function fetchOffices(done) { 5329 var useRouter = auxUsesRouter('offices'); 5330 // ROUTER-CLEANUP: keep router branch, drop legacy when API retired. Only the 5331 // selection set differs ({ items {...} } on the router). 5332 var selection = 'id, name, tags'; 5333 // ROUTER-CLEANUP: keep router branch, drop legacy when API retired. The two 5334 // APIs differ in more than the items wrapper: federated offices takes 5335 // companyId: ID! (legacy gateway: String), so each branch builds its whole doc. 5336 var gqlQuery = useRouter ? 'query ($companyId: ID!, $offset: Int, $limit: Int) {' + 'offices (companyId: $companyId, offset: $offset, limit: $limit) {' + 'items {' + selection + '}}}' : 'query ($companyId: String, $offset: Int, $limit: Int) {' + 'offices (companyId: $companyId, offset: $offset, limit: $limit) {' + selection + '}}'; 5337 var payload = { 5338 offset: 0, 5339 limit: 100, 5340 companyId: variables.companyId 5341 }; 5342 5343 // ROUTER-CLEANUP: keep router branch, drop legacy when API retired 5344 if (useRouter) { 5345 payload = window.luxuryPresence.routerApi.transformVariables('office
5345s', payload); 5346 } 5347 superagent.post(apiUrl(useRouter) + '/graphql').withCredentials().send({ 5348 query: gqlQuery, 5349 variables: payload 5350 }).then(function (res) { 5351 var value = res.body.data && res.body.data.offices || {}; 5352 // ROUTER-CLEANUP: keep router branch, drop legacy when API retired 5353 var items = useRouter ? window.luxuryPresence.routerApi.transformPage(value).items : value || []; 5354 done(items, items.length); 5355 }); 5356 } 5357 5358 // ROUTER-CLEANUP: no rentalRates field on the federated router â legacy only. 5359 function fetchAllRentalRates(done) { 5360 var url = options.url || window.luxuryPresence.defaults.apiGatewayUrl; 5361 var gqlQuery = 'query ($companyId: String, $offset: Int, $limit: Int) {' + 'rentalRates (companyId: $companyId, offset: $offset, limit: $limit) {rentalRateId, label}}'; 5362 var payload = { 5363 offset: 0, 5364 limit: 100, 5365 companyId: variables.companyId 5366 }; 5367 superagent.post(url + '/graphql').withCredentials().send({ 5368 query: gqlQuery, 5369 variables: payload 5370 }).then(function (res) { 5371 var items = res.body.data && res.body.data.rentalRates || []; 5372 done(items, items.length); 5373 }).catch(function (err) { 5374 console.error('Error fetching rental rates:', err); 5375 done([], 0); 5376 }); 5377 } 5378 function fetchData(params, done) { 5379 var resource = options.resource; 5380 var gqlQuery = options.query || window.luxuryPresence.defaults.gql[resource]; 5381 var payload = {}; 5382 try { 5383 payload = JSON.parse(JSON.stringify(variables)); 5384 } catch (e) { 5385 Object.keys(variables).forEach(function (variableName) { 5386 payload[variableName] = variables[variableName]; 5387 }); 5388 } 5389 payload.offset = (params.page - 1) * pageSize + offset; 5390 payload.limit = pageSize; 5391 payload.companyId = options.companyId; 5392 searchFormParams.forEach(function (formParam) { 5393 var val = params[formParam.formKey || formParam.param]; 5394 if (val !== null && val !== undefined) { 5395 objSet(payload, formParam.param, val); 5396 } 5397 }); 5398 5399 // ROUTER-CLEANUP: keep router branch, drop legacy when API retired 5400 if (options.useRouterApi) { 5401 payload = window.luxuryPresence.routerApi.transformVariables(resource, payload); 5402 } 5403 5404 // TODO: Disabled loading due to jarring effect 5405 // dataContainer.html('Loading...'); 5406 superagent.post(apiUrl(options.useRouterApi) + '/graphql').withCredentials().send({ 5407 query: gqlQuery, 5408 variables: payload 5409 }).then(function (res) { 5410 if (res.body.errors) { 5411 done(res.body.errors); 5412 return; 5413 } 5414 var data = res.body.data || {}; 5415 // ROUTER-CLEANUP: keep router branch, drop legacy when API retired 5416 var resources; 5417 var resourceCount; 5418 if (options.useRouterApi) { 5419 var page = window.luxuryPresence.routerApi.transformPage(data[resource]); 5420 resources = window.luxuryPresence.routerApi.transformResponse(resource, page.items); 5421 resourceCount = page.count; 5422 } else { 5423 resources = data[resource] || []; 5424 resourceCount = data[resource + 'Count'] && data[resource + 'Count'].count ? data[resource + 'Count'].count : 0; 5425 } 5426 if (resource === 'properties' && propertyPlaceholderImage?.id) { 5427 resources = resources.map(resource => { 5428 if (resource.media?.length === 0) { 5429 return Object.assign(resource, { 5430 media: [propertyPlaceholderImage] 5431 }); 5432 } 5433 return resource; 5434 }); 5435 } 5436 done(null, resources, resourceCount); 5437 }); 5438 } 5439 function getTotalPage() { 5440 return Math.ceil(totalNumber / pageSize); 5441 } 5442 function renderPaginationList() { 5443 var totalPages = getTotalPage(); 5444 var rangeStart = searchParams.page - pageRange; 5445 var rangeEnd = searchParams.page + pageRange; 5446 if (rangeEnd > totalPages) { 5447 rangeEnd = totalPages; 5448 rangeStart = totalPages - pageRange * 2; 5449 rangeStart = rangeStart < 1 ? 1 : rangeStart; 5450 } 5451 if (rangeStart <= 1) { 5452 rangeStart = 1; 5453 rangeEnd = Math.min(pageRange * 2 + 1, totalPages); 5454 } 5455 5456 // Making sure that 1 more page button is shown in edge positions 5457 if (pageRange === 0) { 5458 if (searchParams.page === totalPages) { 5459 rangeStart = totalPages <= 1 ? 1 : totalPages - 1; 5460 } 5461 if (searchParams.page === 1) { 5462 rangeEnd = Math.min(2, totalPages); 5463 } 5464 } 5465 5466 // Render pagination list 5467 if (mode === 'NUMBERED_PAGINATION') { 5468 el.innerHTML = generateHTML({ 5469 currentPage: searchParams.page, 5470 pageRange: pageRange, 5471 rangeStart: rangeStart, 5472 rangeEnd: rangeEnd 5473 }); 5474 if (totalPages < 2 && hidePaginationOnFirstPage) { 5475 container.style.display = 'none'; 5476 } else { 5477 container.style.display = ''; 5478 } 5479 } else if (mode === 'LOAD_MORE_BUTTON') { 5480 if (searchParams.page >= totalPages) {
5481 loadMoreButton.style.display = 'none'; 5482 } else { 5483 loadMoreButton.style.display = ''; 5484 } 5485 } 5486 } 5487 function render(data) { 5488 renderPaginationList(); 5489 5490 // Render new content with handlebars 5491 var html = ''; 5492 if (mode === 'LOAD_MORE_BUTTON' && searchParams.page > 1) { 5493 // If using load more, append search results to existing results 5494 html = dataContainer.innerHTML; 5495 } 5496 for (var i = 0; i < data.length; i++) { 5497 var itemData = recursivelyParse(data[i]); 5498 itemData.sectionIndex = options.sectionIndex; 5499 itemData.renderOptions = options.renderOptions; 5500 html += template(itemData); 5501 } 5502 dataContainer.innerHTML = html; 5503 5504 // Scroll Top 5505 if (!firstRender && mode === 'NUMBERED_PAGINATION' && (!renderBySearch || !options.skipScrollOnSearch)) { 5506 // Only animate to the top if we replace dataContainer (numbered pagination) 5507 setTimeout(() => { 5508 const scrollTargetOffsetTop = scrollTarget.getBoundingClientRect().top + window.scrollY - scrollOffset; 5509 window.scrollTo({ 5510 top: scrollTargetOffsetTop, 5511 behavior: 'smooth' 5512 }); 5513 }, 250); 5514 } 5515 return true; 5516 } 5517 function generateHTML(args) { 5518 const totalPages = getTotalPage(); 5519 const { 5520 rangeStart, 5521 rangeEnd 5522 } = args; 5523 const makeLnk = page => window.luxuryPresence.pagination.serializeParams(searchFormParams, sectionDomId, { 5524 ...searchParams, 5525 page 5526 }); 5527 let html = ''; 5528 if (showPrevious || showPageNumbers || showNext) { 5529 html += '<div class="paginationjs-pages"><ul>'; 5530 5531 // Previous button 5532 if (showPrevious) { 5533 if (searchParams.page <= 1) { 5534 html += `<li class="${classPrefix}-prev ${disableClassName}"><a>${prevText}</a></li>`; 5535 } else { 5536 html += `<li class="${classPrefix}-prev J-paginationjs-previous" data-num="${searchParams.page - 1}" title="Previous page"><a href="${makeLnk(searchParams.page - 1)}">${prevText}</a></li>`; 5537 } 5538 } 5539 5540 // Pages 5541 if (showPageNumbers) { 5542 if (rangeStart <= 3) { 5543 for (let i = 1; i < rangeStart; i++) { 5544 html += `<li class="${classPrefix}-page J-paginationjs-page ${i === searchParams.page ? activeClassName : ''}" data-num="${i}"><a ${i === searchParams.page ? '' : `href="${makeLnk(i)}"`}>${i}</a></li>`; 5545 } 5546 } else { 5547 if (showFirstOnEllipsisShow) { 5548 html += `<li class="${classPrefix}-page ${classPrefix}-first J-paginationjs-page" data-num="1"><a href="${makeLnk(1)}">1</a></li>`; 5549 } 5550 html += `<li class="${classPrefix}-ellipsis ${disableClassName}"><a>${ellipsisText}</a></li>`; 5551 } 5552 for (let i = rangeStart; i <= rangeEnd; i++) { 5553 html += `<li class="${classPrefix}-page J-paginationjs-page ${i === searchParams.page ? activeClassName : ''}" data-num="${i}"><a ${i === searchParams.page ? '' : `href="${makeLnk(i)}"`}>${i}</a></li>`; 5554 } 5555 if (rangeEnd < totalPages - 2) { 5556 html += `<li class="${classPrefix}-ellipsis ${disableClassName}"><a>${ellipsisText}</a></li>`; 5557 if (showLastOnEllipsisShow) { 5558 html += `<li class="${classPrefix}-page ${classPrefix}-last J-paginationjs-page" data-num="${totalPages}"><a href="${makeLnk(totalPages)}">${totalPages}</a></li>`; 5559 } 5560 } else { 5561 for (let i = rangeEnd + 1; i <= totalPages; i++) { 5562 html += `<li class="${classPrefix}-page J-paginationjs-page" data-num="${i}"><a href="${makeLnk(i)}">${i}</a></li>`; 5563 } 5564 } 5565 } 5566 5567 // Next button 5568 if (showNext) { 5569 if (searchParams.page >= totalPages) { 5570 html += `<li class="${classPrefix}-next ${disableClassName}"><a>${nextText}</a></li>`; 5571 } else { 5572 html += `<li class="${classPrefix}-next J-paginationjs-next" data-num="${searchParams.page + 1}" title="Next page"><a href="${makeLnk(searchParams.page + 1)}">${nextText}</a></li>`; 5573 } 5574 } 5575 html += '</ul></div>'; 5576 } 5577 return html; 5578 } 5579 function go(newSearchParams, callback, restorationVisit) { 5580 if (disabled) return; 5581 var goToPageNumber = newSearchParams.page; 5582 5583 // Page number is out of bounds 5584 if (!goToPageNumber || goToPageNumber < 1) return; 5585 var totalPage = getTotalPage(); 5586 5587 // Page number is out of bounds 5588 if (!restorationVisit && totalNumber > 0) { 5589 if (goToPageNumber > totalPage) return; 5590 } 5591 if (!options.skipSerialization && (mode === 'NUMBERED_PAGINATION' || searchContainer)) {
5592 var newURL = window.luxuryPresence.pagination.serializeParams(searchFormParams, sectionDomId, { 5593 ...newSearchParams, 5594 page: goToPageNumber !== 1 && mode === 'NUMBERED_PAGINATION' ? newSearchParams.page : null 5595 }); 5596 if (!areQueryParamsEqual(newSearchParams, searchParams) && !restorationVisit) { 5597 window.history.pushState({}, '', window.luxuryPresence.getPath(newURL, { 5598 queryString: true 5599 })); 5600 } 5601 } 5602 5603 // Fetch and update page 5604 fetchData(newSearchParams, renderWithData); 5605 disabled = true; 5606 function renderWithData(err, data, count) { 5607 if (err) { 5608 dataContainer.dispatchEvent(new Event('error')); 5609 } else { 5610 searchParams = { 5611 ...newSearchParams 5612 }; 5613 totalNumber = count - offset; 5614 if (data.length === 0) { 5615 dataContainer.dispatchEvent(new Event('no-data')); 5616 } else { 5617 dataContainer.dispatchEvent(new Event('data')); 5618 } 5619 render(data); 5620 firstRender = false; 5621 } 5622 disabled = false; 5623 renderBySearch = false; 5624 } 5625 } 5626 function areQueryParamsEqual(first, second) { 5627 var differ = Object.keys(first).find(function (key) { 5628 return first[key] !== undefined && first[key] !== null && first[key] !== second[key]; 5629 }); 5630 if (differ) { 5631 return false; 5632 } 5633 differ = Object.keys(second).find(function (key) { 5634 return second[key] !== undefined && second[key] !== null && second[key] !== first[key]; 5635 }); 5636 return !differ; 5637 } 5638 function next(callback) { 5639 go({ 5640 ...searchParams, 5641 page: searchParams.page + 1 5642 }, callback); 5643 } 5644 function previous(callback) { 5645 go({ 5646 ...searchParams, 5647 page: searchParams.page - 1 5648 }, callback); 5649 } 5650 function refresh(callback) { 5651 go(searchParams, callback); 5652 } 5653 function update(params, callback) { 5654 renderBySearch = true; 5655 go(params, callback); 5656 } 5657 function observer() { 5658 if (searchContainer) { 5659 searchContainer.addEventListener('search', function (event, newSearchParams) { 5660 newSearchParams = event.detail || newSearchParams; 5661 update({ 5662 ...searchParams, 5663 ...newSearchParams, 5664 page: 1 5665 }); 5666 }); 5667 searchContainer.addEventListener('clear-search', function () { 5668 const defaultSearchParams = { 5669 ...DefaultSearchParams 5670 }; 5671 const isCurrentSearchFormStateDefault = areQueryParamsEqual({ 5672 ...defaultSearchParams, 5673 page: null 5674 }, { 5675 ...searchParams, 5676 page: null 5677 }); 5678 5679 // if mode is LOAD_MORE_BUTTON we should't make anything on clear 5680 // when search form have default state 5681 if (isCurrentSearchFormStateDefault && mode !== 'NUMBERED_PAGINATION') { 5682 return; 5683 } 5684 5685 // if someone initially go to some page and click 'Clear' on search from 5686 // we should't navigate to first page. We should preserve page value 5687 if (isCurrentSearchFormStateDefault) { 5688 defaultSearchParams.page = searchParams.page; 5689 } 5690 syncSearchFormState(defaultSearchParams); 5691 update(defaultSearchParams); 5692 }); 5693 } 5694 if (mode === 'LOAD_MORE_BUTTON') { 5695 loadMoreButton.addEventListener('click', function (event) { 5696 go({ 5697 ...searchParams, 5698 page: searchParams.page + 1 5699 }); 5700 return false; 5701 }); 5702 } 5703 if (mode === 'LOAD_MORE_BUTTON' || mode === 'BUTTONLESS_PAGINATION') { 5704 // Do initial render 5705 go(Object.assign({}, searchParams, { 5706 page: 1 5707 })); 5708 return; 5709 } 5710 if (mode === 'NUMBERED_PAGINATION') { 5711 // Go to specified page number 5712 container.addEventListener(eventPrefix + 'go', function (event) { 5713 let { 5714 pageNumber, 5715 done 5716 } = event.detail || {}; 5717 pageNumber = parseInt(pageNumber, 10); 5718 if (!pageNumber) { 5719 return; 5720 } 5721 go({ 5722 ...searchParams, 5723 page: pageNumber 5724 }, done); 5725 }); 5726 5727 // Page number button click 5728 el.addEventListener('click', function (event) { 5729 let clickTarget = event.target; 5730 let current = clickTarget.closest('.J-paginationjs-page, .J-paginationjs-previous, .J-paginationjs-next'); 5731 let pageNumber = current?.getAttribute('data-num'); 5732 if (!pageNumber || current.classList.contains(disableClassName) || current.classList.contains(activeClassName)) { 5733 return; 5734 } else { 5735 go({ 5736 ...searchParams, 5737 page: parseInt(pageNumber, 10) 5738 }); 5739 } 5740 event.preventDefault(); 5741 }); 5742 5743 // Whether to load the default page 5744 var defaultPageNumber = searchParams.page; 5745 if (triggerPagingOnInit) {
5746 container.dispatchEvent(new CustomEvent(eventPrefix + 'go', { 5747 detail: { 5748 pageNumber: defaultPageNumber 5749 } 5750 })); 5751 } 5752 } 5753 window.addEventListener('popstate', function (event) { 5754 var newParams = Object.assign({}, DefaultSearchParams, window.luxuryPresence.pagination.deserializeParams(searchFormParams, sectionDomId)); 5755 if (!areQueryParamsEqual(newParams, searchParams)) { 5756 go(newParams, null, true); 5757 if (searchContainer) { 5758 syncSearchFormState(newParams); 5759 } 5760 } 5761 }); 5762 } 5763 function replaceVariables(template, variables) { 5764 var formattedString; 5765 for (var key in variables) { 5766 var value = variables[key]; 5767 var regexp = new RegExp('<%=\\s*' + key + '\\s*%>', 'img'); 5768 formattedString = (formattedString || template).replace(regexp, value); 5769 } 5770 return formattedString; 5771 } 5772 function isTextEditorValue(obj) { 5773 return 'raw' in obj || 'gjs' in obj; 5774 } 5775 function getTextEditorValue(obj) { 5776 if (!obj.html) { 5777 return ''; 5778 } 5779 var data = obj.html; 5780 var css = (obj.raw || obj.gjs || {}).css; 5781 if (css) { 5782 data = data + '<style>' + css + '</style>'; 5783 } 5784 return data; 5785 } 5786 5787 /** 5788 * check rendered resources for html and raw properties that could indicate if resource is from an advanced text editor. 5789 * strips out raw proprties and returns only HTML if it exists. 5790 * @param {Object} resources a collection retrieved from the database 5791 * @return {Object} the newly modified object. 5792 */ 5793 function recursivelyParse(resources) { 5794 forEach(resources, function (value, key) { 5795 if (!value) { 5796 return; 5797 } 5798 if (value instanceof Object) { 5799 if (isTextEditorValue(value)) { 5800 resources[key] = getTextEditorValue(value); 5801 return; 5802 } else { 5803 return recursivelyParse(value); 5804 } 5805 } 5806 try { 5807 var parsedData = JSON.parse(value); 5808 if (isTextEditorValue(parsedData)) { 5809 resources[key] = getTextEditorValue(parsedData); 5810 } 5811 } catch (e) { 5812 // do nothing if we failed to parse value as JSON 5813 } 5814 }); 5815 return resources; 5816 } 5817 function forEach(collection, iteratee) { 5818 var func = Array.isArray(collection) ? arrayEach : baseFor; 5819 return func(collection, iteratee); 5820 } 5821 function baseFor(object, iteratee) { 5822 if (!object) { 5823 return; 5824 } 5825 var iterable = Object(object); 5826 var props = Object.keys(object); 5827 var length = props.length; 5828 var index = -1; 5829 while (length--) { 5830 var key = props[++index]; 5831 if (iteratee(iterable[key], key, iterable) === false) { 5832 break; 5833 } 5834 } 5835 return object; 5836 } 5837 function objSet(obj, path, value) { 5838 const pathArray = Array.isArray(path) ? path : path.match(/([^[.\]])+/g); 5839 pathArray.reduce((acc, key, i) => { 5840 if (acc[key] === undefined) acc[key] = {}; 5841 if (i === pathArray.length - 1) acc[key] = value; 5842 return acc[key]; 5843 }, obj); 5844 } 5845 function arrayEach(array, iteratee) { 5846 var index = -1; 5847 var length = array.length; 5848 while (++index < length) { 5849 if (iteratee(array[index], index, array) === false) { 5850 break; 5851 } 5852 } 5853 return array; 5854 } 5855 function selectPageRangeMode(e) { 5856 pageRange = e.matches ? mobilePageRange : desktopPageRange; 5857 renderPaginationList(); 5858 } 5859 paginationMql.addEventListener('change', selectPageRangeMode); 5860})('section-7b127eaf-712f-4e2f-a3ac-785e7ab56a45', JSON.parse("{\"pageSize\":\"{{variables.limit}}\",\"resource\":\"properties\",\"pageRange\":1,\"variables\":{\"sort\":\"salesPrice\",\"limit\":\"{{variables.limit}}\",\"offset\":0,\"sortDir\":\"DESC\",\"statusId\":\"{{variables.statusId}}\",\"companyId\":\"9994eaf9-a60f-4f64-89b4-cb9e3cc349b1\",\"backfillMLSResults\":\"{{variables.backfillMLSResults}}\",\"order\":\"salesPrice\",\"statusIds\":[\"5f528253-abb7-484e-95c3-330269ac1105\",\"959c11cf-8655-4f91-874c-292b0ab7ea6b\",\"a0012964-4f51-4430-abf8-6547c5ab6441\",\"df04ccbe-4621-4140-a504-ee1a17430bb7\",\"88b4ace6-f39b-4b25-a051-8f6dba976833\",\"96031d77-bbe5-4de3-90d8-1e4e70de8ca8\"],\"leaseProperty\":false,\"advancedFilters\":{\"query\":{\"statusIds\":{\"in\":[\"b0e88b38-7437-438d-a6fd-b13f135e1683\"]}}},\"backfillProviders\":[\"bridge_sfar\",\"cws_metrolist\",\"cws_mlsl\",\"mlsgrid_nwmls\",\"rapattoni_bareis\"],\"displayMLSListings\":\"false\",\"backfillMLSAgentIds\":[\"01724366\",\"01938680\",\"MLSL487095\",\"01975049\",\"01970447\",\"SYANLEAN\",\"02091856\",\"MLSL5078780\",\"MLSL5106806\",\"02134254\",\"01893910\",\"EB153620\",\"02092814\",\"MLSL5081020\",\"02116845\",\"02023493\",\"MLSL5082676\",\"01993720\",\"MLSL5079382\",\"MLSL5066011\",\"02097370\",\"01779414\",\"MLSL5099111\"],\"hostname\":\"reali.com\",\"websiteId\":\"5c1357ff-b286-47e5-bde4-3b0c9cb734db\"},\"searchFormParams\":[{\"param\":\"search\",\"global\":true,\"urlKey\":\"search\"},{\"param\":\"advancedFilters\",\"global\":true,\"urlKey\":\"advancedFilters\"},{\"type\":\"int\",\"param\":\"salesPriceGTE\",\"global\":true,\"urlKey\":\"pricemin\"},{\"type\":\"int\",\"param\":\"salesPriceLTE\",\"global\":true,\"urlKey\":\"pricemax\"},{\"type\":\"int\",\"param\":\"livingSpaceSizeGTE\",\"global\":true,\"urlKey\":\"livingSpaceSizeGTE\"},{\"type\":\"int\",\"param\":\"livingSpaceSizeLTE\",\"global\":true,\"urlKey\":\"livingSpaceSizeLTE\"},{\"type\":\"int\",\"param\":\"bedroomCountGTE\",\"global\":true,\"urlKey\":\"bedmin\"},{\"type\":\"int\",\"param\":\"bathCountGTE\",\"global\":true,\"urlKey\":\"bathmin\"},{\"param\":\"propertyTypeIds\",\"global\":true,\"urlKey\":\"propertyType\"},{\"param\":\"sortDir\",\"urlKey\":\"sortBy\"},{\"param\":\"sort\",\"urlKey\":\"sort\"},{\"param\":\"statusIds\",\"global\":true,\"urlKey\":\"statusIds\"},{\"type\":\"bool\",\"param\":\"leaseProperty\",\"global\":true,\"urlKey\":\"leaseProperty\"},{\"type\":\"bool\",\"param\":\"openHouse\",\"global\":true,\"urlKey\":\"openHouse\"}],\"skipScrollOnSearch\":true,\"hidePaginationOnFirstPage\":true,\"companyId\":\"9994eaf9-a60f-4f64-89b4-cb9e3cc349b1\",\"usePageResource\":false,\"sectionIndex\":2,\"useRouterApi\":false,\"routerApiAux\":{\"testimonials\":false,\"agents\":false,\"neighborhoods\":false,\"developments\":false,\"properties\":false,\"posts\":false,\"pressReleases\":false,\"offices\":true,\"teams\":true,\"pois\":true,\"postCategories\":false}}")); 5861} catch(e) { 5862console.log(e); 5863} 5864 try { 5865(function init(sectionDomId) { 5866 function initializeElement(mapsApiAvailable) { 5867 const section = document.getElementById(sectionDomId); 5868 const openModal = section.querySelector(".js-modal-filters"); 5869 const closeModal = Array.from(section.querySelectorAll(".js-close")); 5870 const modal = section.querySelector(".js-modal"); 5871 const fakeCheckbox = Array.from(section.querySelectorAll(".js-fake-checkbox")); 5872 const resetFiltersBtn = section.querySelector(".js-reset-filters"); 5873 const mapContainer = section.querySelector(".js-map-container"); 5874 const allSelects = Array.from(section.querySelectorAll(".js-select")); 5875 openModal?.addEventListener("click", function () { 5876 modal.classList.add("is-visible"); 5877 window.lpUI.requestScrollLock(sectionDomId); 5878 }); 5879 closeModal.forEach(btn => { 5880 btn.addEventListener("click", function () { 5881 modal.classList.remove("is-visible"); 5882 window.lpUI.releaseScrollLock(sectionDomId); 5883 }); 5884 });
5885 fakeCheckbox.forEach(checkbox => { 5886 checkbox.addEventListener("click", function () { 5887 this.previousElementSibling.checked = !this.previousElementSibling.checked; 5888 const eventData = { 5889 checked: this.previousElementSibling.checked 5890 }; 5891 const customEvent = new CustomEvent('customClick', { 5892 detail: eventData 5893 }); 5894 this.previousElementSibling.dispatchEvent(customEvent); 5895 }); 5896 }); 5897 fakeCheckbox.forEach(checkbox => { 5898 checkbox.previousElementSibling.addEventListener('customClick', function (event) { 5899 if (event.detail.checked) { 5900 section.querySelector(`button[value="${event.target.value}"]`).click(); 5901 } else { 5902 let dropdown = propertyTypeSelect.nextElementSibling; 5903 if (!dropdown) return; 5904 let dropdownToggle = dropdown.querySelector(".custom-select-toggle"); 5905 let dropdownMenu = dropdown.querySelector(".custom-select-menu"); 5906 let dropdownContent = dropdown.querySelector(".custom-select-content"); 5907 unselectValues(propertyTypeSelect, checkbox.previousElementSibling.value); 5908 updateDropdown(propertyTypeSelect, getSelectedValues(propertyTypeSelect), true); 5909 } 5910 if (!Array.from(propertyTypeCheckboxes).filter(el => el.checked === true).length) { 5911 propertyTypeSelect.nextElementSibling.querySelector(`button[value=""]`).click(); 5912 propertyTypesResetted = true; 5913 } 5914 }); 5915 }); 5916 let mapData; 5917 let initMap; 5918 if (mapContainer && mapsApiAvailable) { 5919 let markers = []; 5920 const ONE_POINT_BOUNDS = 0.01; 5921 const cardTemplate = Handlebars.compile(section.querySelector(".js-card-template").innerHTML); 5922 const useBWTheme = mapContainer.classList.contains("black-white"); 5923 const initZoom = parseInt(mapContainer.dataset.initZoom); 5924 mapContainer.style["white-space"] = "pre"; 5925 function dataBounds() { 5926 const data = mapData; 5927 const sw = {}; 5928 const ne = {}; 5929 if (!Array.isArray(data) || !data.length) { 5930 return null; 5931 } 5932 if (data.length === 1) { 5933 const { 5934 lng, 5935 lat 5936 } = data[0]; 5937 sw.lat = lat - ONE_POINT_BOUNDS; 5938 sw.lng = lng - ONE_POINT_BOUNDS; 5939 ne.lat = lat + ONE_POINT_BOUNDS; 5940 ne.lng = lng + ONE_POINT_BOUNDS; 5941 } else { 5942 data.forEach(point => { 5943 const { 5944 lng, 5945 lat 5946 } = point; 5947 sw.lat = !sw.lat || lat < sw.lat ? lat : sw.lat; 5948 sw.lng = !sw.lng || lng < sw.lng ? lng : sw.lng; 5949 ne.lat = !ne.lat || lat > ne.lat ? lat : ne.lat; 5950 ne.lng = !ne.lng || lng > ne.lng ? lng : ne.lng; 5951 }); 5952 } 5953 return { 5954 ne, 5955 sw 5956 }; 5957 } 5958 function PropertyMarker(property, index) { 5959 if (!google || !google.maps || !google.maps.LatLng) { 5960 console.error('Google Maps API not available for PropertyMarker'); 5961 return; 5962 } 5963 this.position = new google.maps.LatLng(property.lat, property.lng); 5964 this.icon = document.createElement("div"); 5965 const formattedPrice = new Intl.NumberFormat("en", { 5966 minimumFractionDigits: 1, 5967 maximumFractionDigits: 1 5968 }).format(property.salesPrice / 1000000); 5969 if (parseFloat(formattedPrice)) { 5970 this.icon.classList.add("property-marker-price"); 5971 this.price = document.createElement("span"); 5972 this.price.textContent = parseFloat(formattedPrice) + "M"; 5973 this.icon.appendChild(this.price); 5974 } else { 5975 this.icon.classList.add("property-marker"); 5976 } 5977 this.el = document.createElement("div"); 5978 this.el.classList.add("property-marker-container"); 5979 this.el.appendChild(this.icon); 5980 this.el.addEventListener("mouseenter", function () { 5981 markerMouseEnter(index); 5982 }); 5983 this.el.addEventListener('click', () => { 5984 markerMouseClicked(index); 5985 }); 5986 this.el.addEventListener("mouseleave", function () { 5987 markerMouseLeave(index); 5988 }); 5989 } 5990 if (google && google.maps && google.maps.OverlayView) { 5991 PropertyMarker.prototype = new google.maps.OverlayView(); 5992 } 5993 PropertyMarker.prototype.onAdd = function () { 5994 this.getPanes().overlayMouseTarget.appendChild(this.el); 5995 }; 5996 PropertyMarker.prototype.onRemove = function () { 5997 if (this.el.parentElement) { 5998 this.el.parentElement.removeChild(this.el); 5999 } 6000 }; 6001 PropertyMarker.prototype.draw = function () { 6002 const pixelPos = this.getProjection().fromLatLngToDivPixel(this.position); 6003 this.el.style.left = pixelPos.x + "px"; 6004 this.el.style.top = pixelPos.y + "px"; 6005 }; 6006 PropertyMarker.prototype.setActive = function () { 6007 this.el.classList.add("active"); 6008 }; 6009 PropertyMarker.prototype.unsetActive = function () { 6010 this.el.classList.remove("active"); 6011 }; 6012 function PropertyCard() { 6013 this.content = document.createElement("div"); 6014 this.content.classList.add("card-content"); 6015 this.el = document.createElement("div");
6016 this.el.classList.add("mls-properties-with-map__property-card"); 6017 this.el.appendChild(this.content); 6018 this.el.addEventListener("mouseenter", cardMouseEnter); 6019 this.el.addEventListener("mouseleave", cardMouseLeave); 6020 if (google && google.maps && google.maps.OverlayView && google.maps.OverlayView.preventMapHitsAndGesturesFrom) { 6021 google.maps.OverlayView.preventMapHitsAndGesturesFrom(this.el); 6022 } 6023 } 6024 if (google && google.maps && google.maps.OverlayView) { 6025 PropertyCard.prototype = new google.maps.OverlayView(); 6026 } 6027 PropertyCard.prototype.onAdd = function () { 6028 this.getPanes().floatPane.appendChild(this.el); 6029 }; 6030 PropertyCard.prototype.onRemove = function () { 6031 if (this.el.parentElement) { 6032 this.el.parentElement.removeChild(this.el); 6033 } 6034 }; 6035 PropertyCard.prototype.draw = function () { 6036 var projection = this.getProjection(); 6037 const divPos = projection.fromLatLngToDivPixel(this.position); 6038 const containerPos = projection.fromLatLngToContainerPixel(this.position); 6039 const containerRect = mapContainer.getBoundingClientRect(); 6040 const cardRect = this.el.getBoundingClientRect(); 6041 if (containerPos.y > containerRect.height / 2) { 6042 if (!this.el.classList.contains("on-top")) { 6043 this.el.classList.add("on-top"); 6044 } 6045 this.el.style.top = divPos.y - cardRect.height + "px"; 6046 } else { 6047 if (this.el.classList.contains("on-top")) { 6048 this.el.classList.remove("on-top"); 6049 } 6050 this.el.style.top = divPos.y + "px"; 6051 } 6052 this.el.style.left = divPos.x - cardRect.width / 2 + "px"; 6053 }; 6054 PropertyCard.prototype.setProperty = function (property) { 6055 this.position = new google.maps.LatLng(property.lat, property.lng); 6056 property.customSlug = property.fromMLS === "true" ? `/home-search/listings/${property.mlsID}` : `/properties/${property.slug}`; 6057 this.content.innerHTML = cardTemplate(property); 6058 }; 6059 var unsetActiveTimer = null; 6060 var activeIdx = null; 6061 var hoveredIdx = null; 6062 var map = null; 6063 var propertyCard = null; 6064 var inDetails = false; 6065 var previousActive = null; 6066 function cardMouseEnter() { 6067 window.clearTimeout(unsetActiveTimer); 6068 inDetails = true; 6069 } 6070 function cardMouseLeave() { 6071 inDetails = false; 6072 clearActive(); 6073 } 6074 function markerMouseClicked(idx) { 6075 hoveredIdx = idx; 6076 if (previousActive === idx) { 6077 propertyCard.setMap(null); 6078 previousActive = null; 6079 return; 6080 } 6081 if (idx !== previousActive) { 6082 previousActive = idx; 6083 } 6084 if (inDetails) { 6085 return; 6086 } 6087 propertyCard.setProperty(mapData[activeIdx]); 6088 propertyCard.setMap(map); 6089 } 6090 function markerMouseEnter(idx) { 6091 hoveredIdx = idx; 6092 if (inDetails) { 6093 return; 6094 } 6095 setActive(hoveredIdx); 6096 } 6097 function markerMouseLeave(idx) { 6098 if (hoveredIdx === idx) { 6099 hoveredIdx = null; 6100 } 6101 if (inDetails) { 6102 return; 6103 } 6104 if (activeIdx === idx) { 6105 clearActive(); 6106 } 6107 } 6108 function setActive(idx) { 6109 window.clearTimeout(unsetActiveTimer); 6110 if (activeIdx === idx) { 6111 return; 6112 } 6113 if (activeIdx !== null) { 6114 mapData[activeIdx].marker.unsetActive(); 6115 } 6116 activeIdx = idx; 6117 mapData[activeIdx].marker.setActive(); 6118 } 6119 function clearActive() { 6120 window.clearTimeout(unsetActiveTimer); 6121 unsetActiveTimer = window.setTimeout(function () { 6122 if (activeIdx !== null) { 6123 mapData[activeIdx].marker.unsetActive(); 6124 activeIdx = null; 6125 } 6126 inDetails = false; 6127 if (hoveredIdx) {
6128 setActive(hoveredIdx); 6129 } 6130 }, 400); 6131 } 6132 initMap = function () { 6133 if (!google || !google.maps || !google.maps.Map) { 6134 console.error('Google Maps API not available for map initialization'); 6135 return; 6136 } 6137 const theme = useBWTheme ? themeWB : themeSimple; 6138 let elementConfig = {}; 6139 try { 6140 elementConfig = JSON.parse(section.querySelector(".js-element-config").innerHTML); 6141 mapData = elementConfig.properties || []; 6142 } catch (e) {} 6143 if (!mapData || !mapData.length) { 6144 return; 6145 } 6146 const autoPosition = elementConfig && (!elementConfig.initialPosition || !elementConfig.initialPosition.mapOptions || !elementConfig.initialPosition.mapOptions.zoom || !elementConfig.initialPosition.mapOptions.center || !elementConfig.initialPosition.mapOptions.center.lat || !elementConfig.initialPosition.mapOptions.center.lng); 6147 var center; 6148 var zoom; 6149 var bounds; 6150 if (autoPosition) { 6151 const b = dataBounds(); 6152 if (!b) { 6153 console.error('Failed to calculate map bounds'); 6154 return; 6155 } 6156 bounds = new google.maps.LatLngBounds(b.sw, b.ne); 6157 center = bounds.getCenter(); 6158 zoom = initZoom ? initZoom : 14; 6159 } else { 6160 center = elementConfig.initialPosition.mapOptions.center; 6161 zoom = initZoom ? initZoom : elementConfig.initialPosition.mapOptions.zoom; 6162 } 6163 try { 6164 map = new google.maps.Map(mapContainer, { 6165 center, 6166 zoom, 6167 streetViewControl: false, 6168 styles: theme 6169 }); 6170 } catch (error) { 6171 console.error('Error creating Google Map:', error); 6172 return; 6173 } 6174 if (autoPosition) { 6175 if (!initZoom) map.fitBounds(bounds, 20); 6176 } else {} 6177 markers.forEach(marker => { 6178 marker.setMap(null); 6179 }); 6180 markers = []; 6181 mapData.forEach((property, idx) => { 6182 if (property.lat && property.lng) { 6183 try { 6184 const marker = new PropertyMarker(property, idx); 6185 if (marker && marker.setMap) { 6186 marker.setMap(map); 6187 property.marker = marker; 6188 markers.push(marker); 6189 } 6190 } catch (error) { 6191 console.error(`Error creating marker for property ${idx}:`, error); 6192 } 6193 } 6194 }); 6195 try { 6196 propertyCard = new PropertyCard(); 6197 } catch (error) { 6198 console.error('Error creating PropertyCard:', error); 6199 } 6200 }; 6201 var themeSimple = [{ 6202 featureType: "poi", 6203 stylers: [{ 6204 visibility: "off" 6205 }] 6206 }]; 6207 var themeWB = [{ 6208 elementType: "geometry", 6209 stylers: [{ 6210 color: "#f5f5f5" 6211 }] 6212 }, { 6213 elementType: "labels.icon", 6214 stylers: [{ 6215 visibility: "off" 6216 }] 6217 }, { 6218 elementType: "labels.text.fill", 6219 stylers: [{ 6220 color: "#616161" 6221 }] 6222 }, { 6223 elementType: "labels.text.stroke", 6224 stylers: [{ 6225 color: "#f5f5f5" 6226 }] 6227 }, { 6228 featureType: "administrative.land_parcel", 6229 elementType: "labels.text.fill", 6230 stylers: [{ 6231 color: "#bdbdbd" 6232 }] 6233 }, { 6234 featureType: "poi", 6235 elementType: "geometry", 6236 stylers: [{ 6237 color: "#eeeeee" 6238 }] 6239 }, { 6240 featureType: "poi", 6241 elementType: "labels.text.fill", 6242 stylers: [{ 6243 color: "#757575" 6244 }] 6245 }, { 6246 featureType: "poi.park", 6247 elementType: "geometry", 6248 stylers: [{ 6249 color: "#e5e5e5" 6250 }] 6251 }, { 6252 featureType: "poi.park", 6253 elementType: "labels.text.fill", 6254 stylers: [{ 6255 color: "#9e9e9e" 6256 }] 6257 }, { 6258 featureType: "road", 6259 elementType: "geometry", 6260 stylers: [{ 6261 color: "#ffffff" 6262 }] 6263 }, { 6264 featureType: "road.arterial", 6265 elementType: "labels.text.fill", 6266 stylers: [{ 6267 color: "#757575" 6268 }] 6269 }, { 6270 featureType: "road.highway", 6271 elementType: "geometry", 6272 stylers: [{ 6273 color: "#dadada" 6274 }] 6275 }, { 6276 featureType: "road.highway", 6277 elementType: "labels.text.fill", 6278 stylers: [{ 6279 color: "#616161" 6280 }] 6281 }, { 6282 featureType: "road.local", 6283 elementType: "labels.text.fill", 6284 stylers: [{ 6285 color: "#9e9e9e" 6286 }] 6287 }, { 6288 featureType: "transit.line", 6289 elementType: "geometry", 6290 stylers: [{ 6291 color: "#e5e5e5" 6292 }] 6293 }, { 6294 featureType: "transit.station", 6295 elementType: "geometry", 6296 stylers: [{ 6297 color: "#eeeeee" 6298 }] 6299 }, { 6300 featureType: "water", 6301 elementType: "geometry", 6302 stylers: [{ 6303 color: "#c9c9c9" 6304 }] 6305 }, { 6306 featureType: "water", 6307 elementType: "labels.text.fill", 6308 stylers: [{ 6309 color: "#9e9e9e" 6310 }] 6311 }]; 6312 } 6313 const searchContainer = section.querySelector(".search-container"); 6314 const grid = section.querySelector(".js-grid"); 6315 let variables = grid.dataset.variables; 6316 let propertyTypesResetted = false;
6317 let defaultPropertyTypes; 6318 let defaultStatusIds; 6319 let statusIds = []; 6320 if (variables?.propertyTypeIds) defaultPropertyTypes = variables.propertyTypeIds; 6321 if (variables?.statusIds) defaultStatusIds = variables.statusIds; 6322 if (!searchContainer) { 6323 return; 6324 } 6325 const emptyResultElement = section.querySelector(".js-empty-result"); 6326 const dataContainer = section.querySelector(".data-container"); 6327 const propertyTypes = section.querySelectorAll(".js-property-types-controls"); 6328 const propertyTypeSelect = searchContainer.querySelector('select[name="propertyType"]'); 6329 const propertyTypeCheckboxes = searchContainer.querySelectorAll(".js-property-type-checkbox"); 6330 const statusCheckboxes = searchContainer.querySelectorAll(".js-status-checkbox"); 6331 const bedroomSelect = Array.from(searchContainer.querySelectorAll('select[name="bedrooms"]')); 6332 const bathroomSelect = Array.from(searchContainer.querySelectorAll('select[name="bathrooms"]')); 6333 const priceMinSelect = Array.from(searchContainer.querySelectorAll('select[name="price-min"]')); 6334 const priceMaxSelect = Array.from(searchContainer.querySelectorAll('select[name="price-max"]')); 6335 const minsqftSelect = searchContainer.querySelector('select[name="sqft-min"]'); 6336 const maxsqftSelect = searchContainer.querySelector('select[name="sqft-max"]'); 6337 const priceSortSelect = searchContainer.querySelector('select[name="sort-price"]'); 6338 const searchInput = Array.from(searchContainer.querySelectorAll('input[name="search-input"]')); 6339 const resetSearchInput = searchContainer.querySelector(".js-input-reset"); 6340 const leaseSwitcher = Array.from(searchContainer.querySelectorAll(".js-switcher")); 6341 searchContainer.addEventListener("submit", function (e) { 6342 e.preventDefault(); 6343 }); 6344 const resetFilters = () => { 6345 Array.from(searchInput).forEach(input => input.value = ""); 6346 updateSwitcher(leaseSwitcher, "0"); 6347 updateSwitcher(section.querySelector('.js-switcher[data-type="compact-switcher"]'), "0"); 6348 updateDropdown(bathroomSelect[0], ""); 6349 updateDropdown(bedroomSelect[0], ""); 6350 updateDropdown(priceMinSelect[0], ""); 6351 updateDropdown(priceMaxSelect[0], ""); 6352 updateDropdown(minsqftSelect, ""); 6353 updateDropdown(maxsqftSelect, ""); 6354 updateDropdown(propertyTypeSelect, ""); 6355 Array.from(statusCheckboxes).forEach(checkbox => checkbox.checked = false); 6356 setTimeout(onSearch, 1000); 6357 }; 6358 resetFiltersBtn?.addEventListener("click", resetFilters); 6359 function triggerInputEvent(element, eventType = "input") { 6360 const event = new Event(eventType, { 6361 bubbles: true, 6362 cancelable: true 6363 }); 6364 element.dispatchEvent(event); 6365 } 6366 searchContainer.addEventListener("sync", function (ev, params) { 6367 params = ev.detail || params; 6368 searchInput[0].value = params.search; 6369 triggerInputEvent(searchInput[0]); 6370 triggerInputEvent(bathroomSelect[0], 'change'); 6371 triggerInputEvent(bedroomSelect[0], 'change'); 6372 triggerInputEvent(propertyTypeSelect, 'change'); 6373 }); 6374 if (dataContainer) { 6375 dataContainer.addEventListener("no-data", function () { 6376 emptyResultElement.style.display = "block"; 6377 if (mapContainer) mapContainer.style.display = "none"; 6378 }); 6379 dataContainer.addEventListener("data", function (e) { 6380 emptyResultElement.style.display = "none"; 6381 if (mapsApiAvailable && initMap) { 6382 mapContainer.style.display = "block"; 6383 let retryCount = 0; 6384 const maxRetries = 3; 6385 const retryDelay = 200; 6386 function tryInitMap() { 6387 if (google && google.maps && google.maps.Map) { 6388 initMap(); 6389 } else if (retryCount < maxRetries) { 6390 retryCount++; 6391 console.log(`Google Maps API not ready, retry ${retryCount}/${maxRetries}`); 6392 setTimeout(tryInitMap, retryDelay * retryCount); 6393 } else { 6394 console.error('Google Maps API failed to load after multiple retries'); 6395 } 6396 } 6397 setTimeout(tryInitMap, 100); 6398 } else { 6399 if (mapContainer) { 6400 mapContainer.style.display = "none"; 6401 } 6402 console.warn('Map initialization skipped - Google Maps API not available'); 6403 } 6404 }); 6405 } else if (mapsApiAvailable && initMap) { 6406 initMap(); 6407 } 6408 function syncInputs(source, target) { 6409 source.addEventListener("input", window.luxuryPresence.debounce(function (evt) { 6410 target.value = evt.target.value; 6411 onSearch(); 6412 }, 1000)); 6413 } 6414 syncInputs(searchInput[0], searchInput[1]); 6415 syncInputs(searchInput[1], searchInput[0]); 6416 resetSearchInput.addEventListener("click", function (event) { 6417 event.preventDefault(); 6418 searchInput[0].value = ""; 6419 searchInput[1].value = ""; 6420 onSearch(); 6421 }); 6422 function getSelectedValues(selectElement) { 6423 const selectedValues = []; 6424 const options = selectElement && selectElement.options; 6425 for (let i = 0; i < options.length; i++) { 6426 if (options[i].selected) { 6427 selectedValues.push(options[i].value); 6428 } 6429 } 6430 return selectedValues; 6431 } 6432 function unselectValues(selectElement, value = null) { 6433 if (value) { 6434 selectElement.querySelector(`[value="${value}"]`).selected = false;
6435 section.querySelector(`button[value="${value}"]`).classList.remove('is-selected'); 6436 return; 6437 } 6438 const options = selectElement && selectElement.options; 6439 for (let i = 0; i < options.length; i++) { 6440 options[i].selected = false; 6441 } 6442 } 6443 propertyTypeSelect.addEventListener('change', function () { 6444 const values = getSelectedValues(this); 6445 updateDropdown(this, values, true); 6446 onSearch(); 6447 }); 6448 const checkboxes = {}; 6449 const propertyTypesChange = evt => { 6450 propertyTypesResetted = false; 6451 if (evt.target.id === "property") { 6452 const values = getSelectedValues(evt.target); 6453 Array.from(propertyTypeCheckboxes).forEach(checkbox => { 6454 checkbox.checked = false; 6455 }); 6456 if (values[0] === '') { 6457 propertyTypesResetted = true; 6458 unselectValues(propertyTypeSelect); 6459 Array.from(propertyTypeCheckboxes).forEach(checkbox => { 6460 checkbox.checked = false; 6461 checkboxes[checkbox.value] = false; 6462 }); 6463 return; 6464 } 6465 values.forEach(id => { 6466 Array.from(propertyTypeCheckboxes).forEach(checkbox => { 6467 if (checkbox.value === id) { 6468 checkbox.checked = true; 6469 checkboxes[checkbox.value] = true; 6470 } 6471 }); 6472 }); 6473 } else {} 6474 }; 6475 Array.from(propertyTypes).forEach(control => { 6476 control.addEventListener("change", propertyTypesChange); 6477 }); 6478 section.querySelector("form").addEventListener("reset", function () { 6479 this.querySelectorAll(".custom-select").forEach(customSelect => { 6480 updateDropdown(customSelect.parentNode.previousElementSibling, ""); 6481 }); 6482 }); 6483 if (leaseSwitcher.length > 0) { 6484 initSwitcher(leaseSwitcher, true); 6485 leaseSwitcher.forEach(switcher => { 6486 switcher.addEventListener("change", function (event) { 6487 onSearch(); 6488 }); 6489 }); 6490 } 6491 const changeEvent = new Event("change"); 6492 function initSwitcher(switcher, dispatch = false) { 6493 Array.from(switcher).forEach(item => { 6494 Array.from(item.querySelectorAll('button')).forEach(button => { 6495 button.addEventListener("click", function (event) { 6496 const switcherName = this.parentNode.dataset.name; 6497 const switcher = switcherName ? Array.from(section.querySelectorAll(`.js-switcher[data-name=${switcherName}]`)) : this.parentNode; 6498 updateSwitcher(switcher, this.dataset.val); 6499 if (dispatch) this.parentNode.dispatchEvent(changeEvent); 6500 }); 6501 }); 6502 }); 6503 } 6504 function updateSwitcher(switcher, value) { 6505 if (switcher.length > 1) { 6506 switcher.forEach(el => { 6507 el.querySelector('.active').classList.remove('active'); 6508 }); 6509 switcher.forEach(el => { 6510 el.querySelector(`button[data-val="${value}"]`).classList.add('active'); 6511 }); 6512 } else { 6513 switcher.querySelector('.active').classList.remove('active'); 6514 switcher.querySelector(`button[data-val="${value}"]`).classList.add('active'); 6515 if (getSwitcherValue(switcher) == "0") { 6516 switcher.classList.remove("active"); 6517 return; 6518 } 6519 switcher.classList.add("active"); 6520 } 6521 } 6522 function getSwitcherValue(switcher) { 6523 const active = switcher.querySelector(".active"); 6524 let value = null; 6525 if (active) value = active.dataset.val; 6526 if (value == "-1") value = null; 6527 return value; 6528 } 6529 const priceRange = Array.from(section.querySelectorAll(".js-price-range")); 6530 const priceRangeCTA = section.querySelector(".js-price-range-cta"); 6531 const priceRangeMin = Array.from(section.querySelectorAll('select[name="price-min"]')); 6532 const priceRangeMax = Array.from(section.querySelectorAll('select[name="price-max"]')); 6533 let isSyncing = false; 6534 function syncSelect(source, target) { 6535 source.addEventListener("change", function (evt) { 6536 if (isSyncing) return; 6537 isSyncing = true; 6538 const button = target.nextElementSibling.querySelector(`button[value="${evt.target.value}"]`); 6539 button.click(); 6540 isSyncing = false;
6541 }); 6542 } 6543 syncSelect(priceRangeMin[0], priceRangeMin[1]); 6544 syncSelect(priceRangeMin[1], priceRangeMin[0]); 6545 syncSelect(priceRangeMax[0], priceRangeMax[1]); 6546 syncSelect(priceRangeMax[1], priceRangeMax[0]); 6547 syncSelect(bathroomSelect[0], bathroomSelect[1]); 6548 syncSelect(bathroomSelect[1], bathroomSelect[0]); 6549 syncSelect(bedroomSelect[0], bedroomSelect[1]); 6550 syncSelect(bedroomSelect[1], bedroomSelect[0]); 6551 priceRangeMin.forEach(select => { 6552 select.addEventListener("change", function (event) { 6553 priceRangeMax.forEach(rangeMax => { 6554 rangeMax.parentNode.querySelectorAll('.custom-select-item').forEach(item => { 6555 if (parseInt(item.value) < parseInt(select.value)) { 6556 item.style.display = 'none'; 6557 } else { 6558 item.style.display = 'block'; 6559 } 6560 if (parseInt(item.value) == parseInt(select.value) && parseInt(select.value) > parseInt(rangeMax.value)) { 6561 item.click(); 6562 } 6563 }); 6564 }); 6565 setPriceRange(); 6566 }); 6567 }); 6568 priceRangeMax.forEach(select => { 6569 select.addEventListener("change", function (event) { 6570 priceRangeMin.forEach(rangeMin => { 6571 rangeMin.parentNode.querySelectorAll('.custom-select-item').forEach(item => { 6572 if (parseInt(item.value) > parseInt(select.value)) { 6573 item.style.display = 'none'; 6574 } else { 6575 item.style.display = 'block'; 6576 } 6577 }); 6578 }); 6579 setPriceRange(); 6580 }); 6581 }); 6582 priceRangeCTA.addEventListener("click", function () { 6583 priceRange.forEach(item => item.classList.toggle('opened')); 6584 }); 6585 document.addEventListener("click", function (event) { 6586 const isClickInsideCTA = priceRangeCTA.contains(event.target); 6587 const isClickInsideRange = priceRange.some(item => item.contains(event.target)); 6588 if (!isClickInsideCTA && !isClickInsideRange) { 6589 priceRange.forEach(item => item.classList.remove('opened')); 6590 } 6591 }); 6592 function setPriceRange(select) { 6593 var priceMin = ""; 6594 var priceMax = ""; 6595 var total; 6596 priceRangeMin.forEach(rangeMin => { 6597 priceMin = parseInt(rangeMin.value) ? parseInt(rangeMin.value) : ""; 6598 if (priceMin != "") priceMin = priceMin >= 1000000 ? priceMin / 1000000 + "M" : priceMin / 1000 + "K"; 6599 total = priceMin; 6600 }); 6601 priceRangeMax.forEach(rangeMax => { 6602 priceMax = parseInt(rangeMax.value) ? parseInt(rangeMax.value) : ""; 6603 if (priceMax != "") { 6604 priceMax = priceMax >= 1000000 ? priceMax / 1000000 + "M" : priceMax / 1000 + "K"; 6605 total = `${priceMin} ${priceMin !== "" && priceMax !== "" ? "-" : ""} ${priceMax}`; 6606 } 6607 }); 6608 if (total === "") { 6609 priceRangeCTA.classList.add('is-placeholder'); 6610 } else { 6611 priceRangeCTA.classList.remove('is-placeholder'); 6612 } 6613 priceRangeCTA.querySelector("span").textContent = total === "" ? "Any Price" : total; 6614 } 6615 const buildParams = () => { 6616 let params = {}; 6617 statusIds = Array.from(statusCheckboxes).map(checkbox => checkbox.checked ? checkbox.value : null).filter(statusId => statusId !== null); 6618 params.salesPriceGTE = Number(priceRangeMin[0].value) || null; 6619 params.salesPriceLTE = Number(priceRangeMax[0].value) || null; 6620 params.bedroomCountGTE = Number(bedroomSelect[0].value) || null; 6621 params.bathCountGTE = Number(bathroomSelect[0].value) || null; 6622 params.propertyTypeIds = getSelectedValues(propertyTypeSelect).includes("") ? [] : getSelectedValues(propertyTypeSelect) ? getSelectedValues(propertyTypeSelect) : defaultPropertyTypes ? defaultPropertyTypes : []; 6623 params.leaseProperty = leaseSwitcher.length ? Boolean(Number(getSwitcherValue(leaseSwitcher[0]))) : null; 6624 params.search = searchInput[0].value || null; 6625 params.sortDir = priceSortSelect.value; 6626 params.sort = "salesPrice"; 6627 params.statusIds = statusIds.length > 0 ? statusIds : defaultStatusIds ? defaultStatusIds : []; 6628 params.livingSpaceSizeLTE = Number(maxsqftSelect.value) || null; 6629 params.livingSpaceSizeGTE = Number(minsqftSelect.value) || null; 6630 params.openHouse = leaseSwitcher.length ? Boolean(Number(getSwitcherValue(leaseSwitcher[2]))) === false ? null : Boolean(Number(getSwitcherValue(leaseSwitcher[2]))) : null; 6631 return params; 6632 }; 6633 function onSearch() { 6634 let params = buildParams(); 6635 searchContainer?.dispatchEvent(new CustomEvent("search", { 6636 detail: params 6637 })); 6638 } 6639 searchContainer.addEventListener("submit", function (e) { 6640 e.preventDefault(); 6641 }); 6642 Array.from(searchInput).forEach(input => { 6643 input.addEventListener("input", window.luxuryPresence.debounce(onSearch, 1000)); 6644 }); 6645 bedroomSelect[0].addEventListener("change", onSearch); 6646 bathroomSelect[0].addEventListener("change", onSearch); 6647 priceRangeMin[0].addEventListener("change", onSearch); 6648 priceRangeMax[0].addEventListener("change", onSearch);
6649 Array.from(statusCheckboxes).forEach(checkbox => checkbox.addEventListener("change", onSearch)); 6650 priceSortSelect.addEventListener("change", onSearch); 6651 minsqftSelect.addEventListener("change", onSearch); 6652 maxsqftSelect.addEventListener("change", onSearch); 6653 var debouncer; 6654 function onResize() { 6655 if (window.innerWidth < 768) { 6656 Array.from(searchInput).forEach(input => { 6657 input.setAttribute('placeholder', 'Search'); 6658 }); 6659 } else { 6660 Array.from(searchInput).forEach(input => { 6661 input.setAttribute('placeholder', 'Search by Address, City, or Neighborhood'); 6662 }); 6663 } 6664 } 6665 propertyTypeSelect.classList.remove("hide"); 6666 const customDropdownHTML = ` 6667 <div class="custom-select" role="presentation"> 6668 <button class="custom-select-toggle" type="button" aria-haspopup="true" aria-expanded="false" role="combobox"></button> 6669 <div class="custom-select-menu"><div class="custom-select-content"></div></div> 6670 </div>`; 6671 const hideOriginalSelect = originalSelect => { 6672 originalSelect.classList.remove(...originalSelect.classList); 6673 originalSelect.classList.add("sr-only"); 6674 }; 6675 const openCustomSelect = customSelectToggle => { 6676 let customSelectMenu = customSelectToggle.nextElementSibling; 6677 customSelectMenu.classList.add("open"); 6678 customSelectToggle.classList.add("open"); 6679 customSelectToggle.ariaExpanded = true; 6680 }; 6681 const closeCustomSelect = customSelectToggle => { 6682 let customSelectMenu = customSelectToggle.nextElementSibling; 6683 customSelectMenu.classList.remove("open"); 6684 customSelectToggle.classList.remove("open"); 6685 customSelectToggle.ariaExpanded = false; 6686 }; 6687 const inheritAttributes = (originalSelect, customSelectToggle, customSelectMenu) => { 6688 customSelectToggle.className = customSelectToggle.className.concat(' ', originalSelect.className); 6689 customSelectToggle.id = originalSelect.getAttribute("id") + "custom"; 6690 customSelectToggle.dataset.name = originalSelect.getAttribute("name"); 6691 customSelectMenu.setAttribute("aria-labelledby", customSelectToggle.id); 6692 }; 6693 const initiateQuerySelectors = customSelect => { 6694 let customSelectToggle = customSelect.querySelector('.custom-select-toggle'), 6695 customSelectMenu = customSelect.querySelector('.custom-select-menu'), 6696 customSelectContent = customSelect.querySelector('.custom-select-content'); 6697 return { 6698 customSelectToggle, 6699 customSelectMenu, 6700 customSelectContent 6701 }; 6702 }; 6703 const initiateToggleText = (originalSelect, customSelectToggle) => { 6704 const dataLabelOption = originalSelect.querySelector("[data-label]"); 6705 const isMultiple = originalSelect?.multiple; 6706 if (!isMultiple) { 6707 const toggleText = dataLabelOption && dataLabelOption.dataset.label || originalSelect.querySelector(":checked")?.textContent || originalSelect.querySelector("option")?.textContent; 6708 customSelectToggle.classList.add("is-placeholder"); 6709 customSelectToggle.innerHTML = toggleText; 6710 } else { 6711 const placeholderText = dataLabelOption && dataLabelOption.dataset.label || originalSelect.querySelector("option")?.textContent || "Select"; 6712 const selectedOptions = Array.from(originalSelect.querySelectorAll("option:checked")).filter(opt => opt.value !== ""); 6713 if (selectedOptions.length === 0) { 6714 customSelectToggle.innerHTML = placeholderText; 6715 customSelectToggle.classList.add("is-placeholder"); 6716 } else if (selectedOptions.length === 1) { 6717 customSelectToggle.innerHTML = selectedOptions[0].textContent; 6718 customSelectToggle.classList.remove("is-placeholder"); 6719 } else { 6720 customSelectToggle.innerHTML = `${selectedOptions[0].textContent} +${selectedOptions.length - 1}`; 6721 customSelectToggle.classList.remove("is-placeholder"); 6722 } 6723 } 6724 }; 6725 const convertOptionsToButtons = (originalSelect, customSelectContent) => { 6726 originalSelect.querySelectorAll('option').forEach(option => { 6727 const button = createButtonFromOption(option); 6728 customSelectContent.appendChild(button); 6729 }); 6730 }; 6731 const createButtonFromOption = option => { 6732 let button = document.createElement('button'); 6733 button.className = "custom-select-item"; 6734 button.setAttribute("role", "option"); 6735 button.setAttribute("type", "button"); 6736 button.textContent = option.textContent; 6737 button.setAttribute('value', option.value); 6738 if (option.disabled) { 6739 button.setAttribute("disabled", "true"); 6740 } 6741 if (option.selected && option.value !== "") { 6742 button.classList.add("is-selected"); 6743 button.setAttribute("aria-selected", "true"); 6744 } 6745 return button; 6746 }; 6747 const initiateEventListeners = (originalSelect, customSelectToggle, customSelectMenu) => { 6748 const isMultiple = originalSelect?.multiple; 6749 originalSelect.addEventListener("focus", function () { 6750 openCustomSelect(customSelectToggle); 6751 customSelectToggle.focus(); 6752 }); 6753 customSelectToggle.addEventListener("click", function () { 6754 if (customSelectToggle.getAttribute("aria-expanded") === "true") { 6755 closeCustomSelect(customSelectToggle); 6756 } else { 6757 openCustomSelect(customSelectToggle); 6758 } 6759 }); 6760 customSelectMenu.addEventListener("click", function (event) { 6761 if (event.target.classList.contains('custom-select-item')) { 6762 const clickedValue = event.target.value; 6763 const clickedOption = originalSelect.querySelector(`option[value="${clickedValue}"]`); 6764 const placeholderOption = originalSelect.querySelector("[data-label]"); 6765 const placeholderText = placeholderOption?.dataset.label || originalSelect.querySelector("option")?.textContent || "Select"; 6766 if (!isMultiple) { 6767 let opts = customSelectMenu.querySelectorAll(".is-selected"); 6768 [].forEach.call(opts, function (el) { 6769 el.classList.remove("is-selected"); 6770 }); 6771 event.target.classList.add("is-selected"); 6772 clickedOption.selected = true; 6773 customSelectToggle.innerHTML = clickedOption.textContent; 6774 if (clickedValue === "" || event.target.classList.contains("is-placeholder")) { 6775 customSelectToggle.classList.add("is-placeholder"); 6776 } else { 6777 customSelectToggle.classList.remove("is-placeholder"); 6778 } 6779 let changeEvent = new Event('change', { 6780 bubbles: true 6781 }); 6782 originalSelect.dispatchEvent(changeEvent); 6783 closeCustomSelect(customSelectToggle); 6784 } else { 6785 if (clickedValue === "") { 6786 customSelectMenu.querySelectorAll(".is-selected").forEach(el => { 6787 el.classList.remove("is-selected"); 6788 el.setAttribute("aria-selected", "false"); 6789 }); 6790 originalSelect.querySelectorAll("option").forEach(opt => opt.selected = false); 6791 customSelectToggle.innerHTML = placeholderText; 6792 customSelectToggle.classList.add("is-placeholder"); 6793 } else { 6794 const isCurrentlySelected = event.target.classList.contains("is-selected"); 6795 event.target.classList.toggle("is-selected"); 6796 event.target.setAttribute("aria-selected", (!isCurrentlySelected).toString()); 6797 clickedOption.selected = !isCurrentlySelected; 6798 const allItem = customSelectMenu.querySelector('.custom-select-item[value=""]'); 6799 if (allItem) { 6800 allItem.classList.remove("is-selected"); 6801 allItem.setAttribute("aria-selected", "false"); 6802 } 6803 const allOption = originalSelect.querySelector('option[value=""]'); 6804 if (allOption) allOption.selected = false;
6805 const selectedItems = customSelectMenu.querySelectorAll(".custom-select-item.is-selected"); 6806 if (selectedItems.length === 0) { 6807 customSelectToggle.innerHTML = placeholderText; 6808 customSelectToggle.classList.add("is-placeholder"); 6809 } else if (selectedItems.length === 1) { 6810 customSelectToggle.innerHTML = selectedItems[0].textContent; 6811 customSelectToggle.classList.remove("is-placeholder"); 6812 } else { 6813 customSelectToggle.innerHTML = `${selectedItems[0].textContent} +${selectedItems.length - 1}`; 6814 customSelectToggle.classList.remove("is-placeholder"); 6815 } 6816 } 6817 let changeEvent = new Event('change', { 6818 bubbles: true 6819 }); 6820 originalSelect.dispatchEvent(changeEvent); 6821 } 6822 } 6823 }); 6824 document.addEventListener("click", function (event) { 6825 if (!customSelectMenu.contains(event.target) && !customSelectToggle.contains(event.target)) { 6826 closeCustomSelect(customSelectToggle); 6827 } 6828 }); 6829 }; 6830 const createCustomSelect = originalSelect => { 6831 const customSelect = document.createElement('div'); 6832 customSelect.innerHTML = customDropdownHTML; 6833 const { 6834 customSelectToggle, 6835 customSelectMenu, 6836 customSelectContent 6837 } = initiateQuerySelectors(customSelect); 6838 inheritAttributes(originalSelect, customSelectToggle, customSelectMenu); 6839 hideOriginalSelect(originalSelect); 6840 initiateToggleText(originalSelect, customSelectToggle); 6841 convertOptionsToButtons(originalSelect, customSelectContent); 6842 initiateEventListeners(originalSelect, customSelectToggle, customSelectMenu); 6843 return customSelect; 6844 }; 6845 const setCustomSelectValue = (originalSelect, value) => { 6846 let customSelect = originalSelect.nextElementSibling; 6847 let customSelectToggle = customSelect.querySelector(".custom-select-toggle"); 6848 let customSelectContent = customSelect.querySelector(".custom-select-content"); 6849 if (!customSelectContent) return; 6850 let selectedOption = value ? Array.from(customSelectContent.children).find(function (b) { 6851 return b.value === value; 6852 }) : customSelectContent.firstChild; 6853 if (!selectedOption) { 6854 return; 6855 } 6856 if (selectedOption.classList.contains("is-disabled")) return; 6857 let elementNumber = Array.from(selectedOption.parentNode.children).indexOf(selectedOption); 6858 let linkedOption = originalSelect.querySelectorAll("option")[elementNumber]; 6859 linkedOption.selected = true; 6860 selectedOption.classList.add("is-selected"); 6861 selectedOption.parentNode.querySelectorAll(".custom-select-item").forEach(el => { 6862 if (el !== selectedOption) { 6863 el.classList.remove("is-selected"); 6864 } 6865 }); 6866 closeCustomSelect(customSelectToggle); 6867 customSelectToggle.innerHTML = linkedOption.innerHTML; 6868 if (linkedOption.value) { 6869 customSelectToggle.classList.remove("is-placeholder"); 6870 if (linkedOption.value !== "") { 6871 customSelect.classList.add("has-selection"); 6872 } else { 6873 customSelect.classList.remove("has-selection"); 6874 } 6875 } else { 6876 customSelect.classList.remove("has-selection"); 6877 customSelectToggle.classList.add("is-placeholder"); 6878 } 6879 }; 6880 const customSelect = originalSelect => { 6881 const customSelect = createCustomSelect(originalSelect); 6882 originalSelect.insertAdjacentElement('afterend', customSelect); 6883 return customSelect; 6884 }; 6885 const resetCustomSelect = originalSelect => { 6886 const customSelectEl = originalSelect.nextElementSibling; 6887 const customSelectToggle = customSelectEl?.querySelector('.custom-select-toggle'); 6888 const customSelectContent = customSelectEl?.querySelector('.custom-select-content'); 6889 if (!customSelectToggle || !customSelectContent) return; 6890 const placeholderOption = originalSelect.querySelector("[data-label]"); 6891 const placeholderText = placeholderOption?.dataset.label || originalSelect.querySelector("option")?.textContent || "Select"; 6892 customSelectContent.querySelectorAll('.custom-select-item').forEach(item => { 6893 item.classList.remove('is-selected'); 6894 item.setAttribute('aria-selected', 'false'); 6895 }); 6896 originalSelect.querySelectorAll('option').forEach(opt => opt.selected = false); 6897 customSelectToggle.innerHTML = placeholderText; 6898 customSelectToggle.classList.add('is-placeholder'); 6899 const customSelectWrapper = customSelectEl.querySelector('.custom-select'); 6900 if (customSelectWrapper) { 6901 customSelectWrapper.classList.remove('has-selection'); 6902 } 6903 };
6904 allSelects.forEach(select => { 6905 const customEl = customSelect(select); 6906 if (select === priceSortSelect) { 6907 customEl.querySelector('.custom-select-toggle')?.classList.remove('is-placeholder'); 6908 } 6909 }); 6910 function updateDropdown(select, value, multiple = false) { 6911 let dropdown = select.nextElementSibling; 6912 if (!dropdown) return; 6913 let dropdownToggle = dropdown.querySelector(".custom-select-toggle"); 6914 let dropdownMenu = dropdown.querySelector(".custom-select-menu"); 6915 let dropdownContent = dropdown.querySelector(".custom-select-content"); 6916 if (Array.isArray(value) && value?.includes("")) { 6917 value?.forEach(val => { 6918 dropdownContent.querySelector(`button[value="${val}"]`).classList.remove('is-selected'); 6919 }); 6920 return; 6921 } 6922 if (Array.isArray(value)) { 6923 value?.forEach(val => { 6924 let selectedOption = dropdownContent.querySelector(`button[value="${val}"]`); 6925 if (selectedOption) { 6926 selectedOption.classList.add("is-selected"); 6927 } 6928 }); 6929 if (dropdownToggle) dropdownToggle.textContent = `${dropdownContent.querySelector('.is-selected')?.textContent} ${value.length > 1 ? ` +${value.length - 1}` : ''}`; 6930 } else { 6931 let selectedOption = dropdownContent.querySelector(`button[value="${value}"]`); 6932 if (selectedOption) selectedOption.click(); 6933 } 6934 } 6935 window.addEventListener("resize", function () { 6936 clearTimeout(debouncer); 6937 debouncer = setTimeout(onResize, 200); 6938 }); 6939 onResize(); 6940 } 6941 window.luxuryPresence.mapApiLoader.load().then(() => { 6942 const mapsApiAvailable = typeof google !== 'undefined' && typeof google.maps !== 'undefined'; 6943 if (!mapsApiAvailable) { 6944 console.error('Google Maps API failed to load properly'); 6945 } 6946 initializeElement(mapsApiAvailable); 6947 }).catch(error => { 6948 console.error('Error loading Google Maps API:', error); 6949 initializeElement(false); 6950 }); 6951})('section-7b127eaf-712f-4e2f-a3ac-785e7ab56a45', JSON.parse("{\"resource\":\"properties\",\"useRouterApi\":false,\"companyId\":\"9994eaf9-a60f-4f64-89b4-cb9e3cc349b1\",\"usePageResource\":false,\"sectionIndex\":2,\"routerApiAux\":{\"testimonials\":false,\"agents\":false,\"neighborhoods\":false,\"developments\":false,\"properties\":false,\"posts\":false,\"pressReleases\":false,\"offices\":true,\"teams\":true,\"pois\":true,\"postCategories\":false}}")); 6952} catch(e) { 6953console.log(e); 6954} 6955 try { 6956(function init(sectionDomId, options) { 6957 let section = null; 6958 switch (sectionDomId) { 6959 case 'NAVBAR': 6960 section = document.querySelector('body > nav, #global-navbar'); 6961 break; 6962 case 'FOOTER': 6963 section = document.querySelector('body > footer, #global-footer'); 6964 break; 6965 case 'SIDEMENU': 6966 section = document.querySelector('body > div.sidemenu, #global-sidemenu'); 6967 break; 6968 case 'CONTACT': 6969 section = document.querySelector('#modal-global-contact-us'); 6970 break; 6971 case 'MOBILE_CONTACT': 6972 section = document.querySelector('body > div.mobile-contact-wrapper, #global-mobile-contact'); 6973 break; 6974 case 'SUBSCRIBE': 6975 // Subscribe Not Implemented 6976 section = null; 6977 break; 6978 case 'ACCOUNT': 6979 // Account Not Implemented 6980 section = null; 6981 break; 6982 default: 6983 section = document.querySelector('#' + sectionDomId); 6984 } 6985 if (!section) { 6986 return; 6987 } 6988 6989 // Where content / items are rendered 6990 const dataContainer = section.querySelector('.data-container'); 6991 if (!dataContainer) { 6992 // no place to render items = no need to query server or do anything else 6993 return; 6994 } 6995 let variables = options.variables || {}; 6996 6997 // Some variables in options may not have been injected - remove 6998 Object.keys(variables).forEach(function (variableName) { 6999 if (typeof variables[variableName] === 'string' && variables[variableName].match(/^{{.*}}$/)) { 7000 delete variables[variableName]; 7001 } 7002 }); 7003 7004 // Merge page query variables 7005 if (options.usePageResource) { 7006 variables = { 7007 ...variables, 7008 ...window.luxuryPresence.config.pageMeta.pageQueryVariables[options.resource] 7009 }; 7010 } 7011 7012 // ROUTER-CLEANUP: keep router branch, drop legacy when API retired. Callers 7013 // resolve the flag â fetchData reads the section stamp, aux fetches read 7014 // their own resource's stamp (auxUsesRouter). 7015 function apiUrl(useRouter) { 7016 return options.url || (useRouter ? window.luxuryPresence.defaults.routerUrl : window.luxuryPresence.defaults.apiGatewayUrl); 7017 } 7018 7019 // ROUTER-CLEANUP: aux resources roll out on their own flag, not the section's 7020 // (options.routerApiAux is stamped per aux resource server-side). 7021 function auxUsesRouter(resource) { 7022 return !!(options.routerApiAux && options.routerApiAux[resource]); 7023 } 7024 7025 // Pagination Executes on a Numbered Pagination Container 7026 // Or via a load more button 7027 let mode = 'BUTTONLESS_PAGINATION'; 7028 const container = section.querySelector('.pagination-container');
7029 const loadMoreButton = section.querySelector(options?.loadMoreSelector); 7030 if (container) { 7031 mode = 'NUMBERED_PAGINATION'; 7032 } 7033 if (loadMoreButton) { 7034 mode = 'LOAD_MORE_BUTTON'; 7035 } 7036 let searchContainer = section.querySelector('.search-container'); 7037 const hidePaginationOnFirstPage = options.hidePaginationOnFirstPage || false; 7038 const eventPrefix = '__pagination-'; 7039 const searchFormParams = options.searchFormParams || (options.resource === 'properties' ? [{ 7040 param: 'neighborhoodId', 7041 urlKey: 'neighborhood' 7042 }, { 7043 param: 'search', 7044 urlKey: 'search' 7045 }, { 7046 param: 'salesPriceGTE', 7047 urlKey: 'pricemin', 7048 formKey: 'priceMin', 7049 type: 'int' 7050 }, { 7051 param: 'salesPriceLTE', 7052 urlKey: 'pricemax', 7053 formKey: 'priceMax', 7054 type: 'int' 7055 }, { 7056 param: 'livingSpaceSizeGTE', 7057 urlKey: 'areamin', 7058 formKey: 'areaMin', 7059 type: 'int' 7060 }, { 7061 param: 'livingSpaceSizeLTE', 7062 urlKey: 'areamax', 7063 formKey: 'areaMax', 7064 type: 'int' 7065 }] : []); 7066 7067 // Total entries - initialized in first render 7068 let totalNumber = 0; 7069 const DefaultSearchParams = { 7070 page: 1 7071 }; 7072 searchFormParams.forEach(function (formParam) { 7073 DefaultSearchParams[formParam.formKey || formParam.param] = null; 7074 }); 7075 let searchParams = { 7076 ...DefaultSearchParams 7077 }; 7078 const offset = options.variables?.offset || 0; 7079 let scrollTarget = options.scrollTargetSelector ? section.querySelector(options.scrollTargetSelector) || section : section; 7080 let scrollOffset = (parseInt(options.scrollOffset, 10) || 0) ?? 100; 7081 7082 // entries of per page 7083 // when query variables for a given section would't be filled options.pageSize can store 7084 // template string, ex: "{{variables.limit}}", below code handles that case and set 9 as a default value 7085 let pageSize = parseInt(options.pageSize, 10) || 9; 7086 7087 // Adding mobile pageRange support 7088 var NARROW_BREAKPOINT = 600; 7089 var mobileBreakpoint = options.mobileBreakpoint > 0 ? options.mobileBreakpoint : NARROW_BREAKPOINT; 7090 var paginationMql = window.matchMedia(`(max-width: ${mobileBreakpoint}px)`); 7091 7092 // Page range (pages on both sides of the current page) 7093 var mobilePageRange = options.mobilePageRange > 0 ? options.mobilePageRange : 0; 7094 var desktopPageRange = options.pageRange >= 0 ? options.pageRange : 2; 7095 var pageRange = paginationMql.matches ? mobilePageRange : desktopPageRange; 7096 7097 // Whether to display the 'Previous' button 7098 var showPrevious = true; 7099 7100 // Whether to display the 'Next' button 7101 var showNext = true; 7102 7103 // Whether to display the page buttons 7104 var showPageNumbers = true; 7105 7106 // 'Previous' text 7107 var prevText = options.prevText || '«'; 7108 7109 // 'Next' text 7110 var nextText = options.nextText || '»'; 7111 7112 // Ellipsis text 7113 var ellipsisText = '…'; 7114 var classPrefix = 'paginationjs'; 7115 7116 // Default active class 7117 var activeClassName = 'active'; 7118 7119 // Default disable class 7120 var disableClassName = 'disabled'; 7121 7122 // Pagination element's position in the container 7123 var position = 'bottom'; 7124 7125 // Whether to trigger pagination at initialization 7126 var triggerPagingOnInit = !options.skipInitialLoad; 7127 var showFirstOnEllipsisShow = true; 7128 var showLastOnEllipsisShow = true; 7129 7130 // Instantiated in init() 7131 var el = null; 7132 7133 // Controls scrollTop logic 7134 var firstRender = true; 7135 var renderBySearch = false; 7136 var disabled = false; 7137 7138 // Pagination script class selector 7139 var paginationScriptSelector = options.paginationPartialName ? `.${options.paginationPartialName}` : '.pagination-script'; 7140 7141 // Property Specific Image Override if No Image 7142 var propertyPlaceholderImage = window.luxuryPresence.defaults.propertyPlaceholderImage; 7143 7144 // Construct template 7145 var templateHTML = section.querySelector(`script${paginationScriptSelector}`)?.innerHTML || ''; 7146 var template = Handlebars.compile(templateHTML); 7147 7148 // Initialize 7149 init(); 7150 function init() { 7151 Object.assign(searchParams, window.luxuryPresence.pagination.deserializeParams(searchFormParams, sectionDomId)); 7152 7153 // Append/prepend pagination element to the pagination container 7154 if (mode === 'NUMBERED_PAGINATION') { 7155 el = document.createElement('div'); 7156 el.className = 'paginationjs';
7157 container[position === 'bottom' ? 'append' : 'prepend'](el); 7158 } 7159 if (searchContainer) { 7160 initSearchContainer({ 7161 fetchNeighborhoods: searchContainer.dataset.fetchNeighborhoods === 'true', 7162 fetchPostCategories: searchContainer.dataset.fetchPostCategories === 'true', 7163 fetchOffices: searchContainer.dataset.fetchOffices === 'true', 7164 fetchRentalRates: searchContainer.dataset.fetchRentalRates === 'true' 7165 }, function () { 7166 syncSearchFormState(searchParams); 7167 }); 7168 } 7169 7170 // Bind events 7171 observer(); 7172 } 7173 function syncSearchFormState(params) { 7174 searchContainer.dispatchEvent(new CustomEvent('sync', { 7175 detail: params 7176 })); 7177 } 7178 function initSearchContainer(options, done) { 7179 var promises = []; 7180 if (options.fetchNeighborhoods) { 7181 promises.push(fetchAllNeighborhoods(function (neighborhoods) { 7182 searchContainer.dispatchEvent(new CustomEvent('neighborhoods-data', { 7183 detail: { 7184 data: neighborhoods.map(function (item) { 7185 return { 7186 value: item.id, 7187 label: item.name 7188 }; 7189 }) 7190 } 7191 })); 7192 })); 7193 } 7194 if (options.fetchPostCategories) { 7195 promises.push(fetchPostCategories(function (categories) { 7196 searchContainer.dispatchEvent(new CustomEvent('post-categories-data', { 7197 detail: { 7198 data: categories 7199 } 7200 })); 7201 })); 7202 } 7203 if (options.fetchOffices) { 7204 promises.push(fetchOffices(function (offices) { 7205 searchContainer.dispatchEvent(new CustomEvent('offices-data', { 7206 detail: { 7207 data: offices 7208 } 7209 })); 7210 })); 7211 } 7212 if (options.fetchRentalRates) { 7213 promises.push(fetchAllRentalRates(function (rentalRates) { 7214 searchContainer.dispatchEvent(new CustomEvent('rental-rates-data', { 7215 detail: { 7216 data: rentalRates 7217 } 7218 })); 7219 })); 7220 } 7221 if (promises.length > 0) { 7222 Promise.all(promises).then(function () { 7223 done(); 7224 }); 7225 } else { 7226 // element's custom code is not executed yet, so we're scheduling callback to after element's code 7227 setTimeout(done); 7228 } 7229 } 7230 function fetchAllNeighborhoods(done) { 7231 var useRouter = auxUsesRouter('neighborhoods'); 7232 // ROUTER-CLEANUP: keep router branch, drop legacy when API retired. Same 7233 // args on both APIs (the federated neighborhoods field accepts 7234 // withProperties/withBlogPosts); only the selection set differs. 7235 var gqlQuery = 'query ($companyId: String, $withProperties: NeighborhoodPropertiesFilter, $withBlogPosts: NeighborhoodBlogPostsFilter, $offset: Int, $limit: Int) {' + 'neighborhoods (companyId: $companyId, withProperties: $withProperties, withBlogPosts: $withBlogPosts, offset: $offset, limit: $limit) {' + (useRouter ? 'items {id, name}' : 'id, name') + '}}'; 7236 var payload = {}; 7237 payload.offset = 0; 7238 payload.limit = 100; 7239 payload.companyId = variables.companyId; 7240 if (options.resource === 'properties') { 7241 payload.withProperties = { 7242 active: true 7243 }; 7244 ['agentIds', 'tag', 'propertyTypeId', 'statusIds', 'architectureStyle', 'lifestyle', 'salesPriceGTE', 'salesPriceLTE', 'leasePriceGTE', 'leasePriceLTE', 'livingSpaceSizeGTE', 'livingSpaceSizeLTE', 'bathCountGTE', 'bathCountLTE', 'bedroomCountGTE', 'bedroomCountLTE', 'bathCountGTE', 'bathCountLTE', 'bedroomCountGTE', 'bedroomCountLTE', 'featuredListing', 'leaseProperty', 'neighborhoodId', 'developmentId', 'openHouse'].forEach(function (field) { 7245 if (variables[field] !== undefined && variables[field] !== null) { 7246 payload.withProperties[field] = variables[field]; 7247 } 7248 }); 7249 } 7250 if (options.resource === 'posts') { 7251 payload.withBlogPosts = { 7252 active: true 7253 }; 7254 ['search', 'postStatusId', 'featured', 'categoryId', 'categoryIds', 'neighborhoodId', 'slug'].forEach(function (field) { 7255 if (variables[field] !== undefined && variables[field] !== null) { 7256 payload.withBlogPosts[field] = variables[field]; 7257 } 7258 }); 7259 } 7260 7261 // ROUTER-CLEANUP: keep router branch, drop legacy when API retired 7262 if (useRouter) { 7263 payload = window.luxuryPresence.routerApi.transformVariables('neighborhoods', payload); 7264 } 7265 superagent.post(apiUrl(useRouter) + '/graphql').withCredentials().send({ 7266 query: gqlQuery, 7267 variables: payload 7268 }).then(function (res) { 7269 var value = res.body.data && res.body.data.neighborhoods || {}; 7270 // ROUTER-CLEANUP: keep router branch, drop legacy when API retired 7271 var items = useRouter ? window.luxuryPresence.routerApi.transformPage(value).items : value || []; 7272 done(items, items.length); 7273 }); 7274 } 7275 function fetchPostCategories(done) { 7276 var featured = options.resource === 'posts' && !!variables.featured; 7277 var useRouter = auxUsesRouter('postCategories'); 7278 // ROUTER-CLEANUP: keep router branch, drop legacy when API retired. Only the 7279 // selection set differs ({ items {...} } on the router). 7280 var selection = 'id, name, publishedCount, featuredPublishedCount'; 7281 var gqlQuery = 'query ($companyId: String, $offset: Int, $limit: Int) {' + 'postCategories (companyId: $companyId, offset: $offset, limit: $limit ' + (featured ? ' withFeaturedPublishedOnly: true' : ' withPublishedOnly: true') + ') {' + (useRouter ? 'items {' + selection + '}' : selection) + '}}'; 7282 var payload = {}; 7283 payload.offset = 0; 7284 payload.limit = 100; 7285 payload.companyId = variables.companyId; 7286 superagent.post(apiUrl(useRouter) + '/graphql').withCredentials().send({ 7287 query: gqlQuery, 7288 variables: payload 7289 }).then(function (res) { 7290 var value = res.body.data && res.body.data.postCategories || {}; 7291 // ROUTER-CLEANUP: keep router branch, drop legacy when API retired 7292 var items = useRouter ? window.luxuryPresence.routerApi.transformPage(value).items : value || []; 7293 done(items.map(function (item) { 7294 item.count = featured ? item.featuredPublishedCount : item.publishedCount; 7295 return item; 7296 }), items.length); 7297 }); 7298 } 7299 function fetchOffices(done) { 7300 var useRouter = auxUsesRouter('offices'); 7301 // ROUTER-CLEANUP: keep router branch, drop legacy when API retired. Only the 7302 // selection set differs ({ items {...} } on the router). 7303 var selection = 'id, name, tags'; 7304 // ROUTER-CLEANUP: keep router branch, drop legacy when API retired. The two 7305 // APIs differ in more than the items wrapper: federated offices takes 7306 // companyId: ID! (legacy gateway: String), so each branch builds its whole doc. 7307 var gqlQuery = useRouter ? 'query ($companyId: ID!, $offset: Int, $limit: Int) {' + 'offices (companyId: $companyId, offset: $offset, limit: $limit) {' + 'items {' + selection + '}}}' : 'query ($companyId: String, $offset: Int, $limit: Int) {' + 'offices (companyId: $companyId, offset: $offset, limit: $limit) {' + selection + '}}'; 7308 var payload = { 7309 offset: 0, 7310 limit: 100, 7311 companyId: variables.companyId 7312 }; 7313 7314 // ROUTER-CLEANUP: keep router branch, drop legacy when API retired 7315 if (useRouter) { 7316 payload = window.luxuryPresence.routerApi.transformVariables('office
7316s', payload); 7317 } 7318 superagent.post(apiUrl(useRouter) + '/graphql').withCredentials().send({ 7319 query: gqlQuery, 7320 variables: payload 7321 }).then(function (res) { 7322 var value = res.body.data && res.body.data.offices || {}; 7323 // ROUTER-CLEANUP: keep router branch, drop legacy when API retired 7324 var items = useRouter ? window.luxuryPresence.routerApi.transformPage(value).items : value || []; 7325 done(items, items.length); 7326 }); 7327 } 7328 7329 // ROUTER-CLEANUP: no rentalRates field on the federated router â legacy only. 7330 function fetchAllRentalRates(done) { 7331 var url = options.url || window.luxuryPresence.defaults.apiGatewayUrl; 7332 var gqlQuery = 'query ($companyId: String, $offset: Int, $limit: Int) {' + 'rentalRates (companyId: $companyId, offset: $offset, limit: $limit) {rentalRateId, label}}'; 7333 var payload = { 7334 offset: 0, 7335 limit: 100, 7336 companyId: variables.companyId 7337 }; 7338 superagent.post(url + '/graphql').withCredentials().send({ 7339 query: gqlQuery, 7340 variables: payload 7341 }).then(function (res) { 7342 var items = res.body.data && res.body.data.rentalRates || []; 7343 done(items, items.length); 7344 }).catch(function (err) { 7345 console.error('Error fetching rental rates:', err); 7346 done([], 0); 7347 }); 7348 } 7349 function fetchData(params, done) { 7350 var resource = options.resource; 7351 var gqlQuery = options.query || window.luxuryPresence.defaults.gql[resource]; 7352 var payload = {}; 7353 try { 7354 payload = JSON.parse(JSON.stringify(variables)); 7355 } catch (e) { 7356 Object.keys(variables).forEach(function (variableName) { 7357 payload[variableName] = variables[variableName]; 7358 }); 7359 } 7360 payload.offset = (params.page - 1) * pageSize + offset; 7361 payload.limit = pageSize; 7362 payload.companyId = options.companyId; 7363 searchFormParams.forEach(function (formParam) { 7364 var val = params[formParam.formKey || formParam.param]; 7365 if (val !== null && val !== undefined) { 7366 objSet(payload, formParam.param, val); 7367 } 7368 }); 7369 7370 // ROUTER-CLEANUP: keep router branch, drop legacy when API retired 7371 if (options.useRouterApi) { 7372 payload = window.luxuryPresence.routerApi.transformVariables(resource, payload); 7373 } 7374 7375 // TODO: Disabled loading due to jarring effect 7376 // dataContainer.html('Loading...'); 7377 superagent.post(apiUrl(options.useRouterApi) + '/graphql').withCredentials().send({ 7378 query: gqlQuery, 7379 variables: payload 7380 }).then(function (res) { 7381 if (res.body.errors) { 7382 done(res.body.errors); 7383 return; 7384 } 7385 var data = res.body.data || {}; 7386 // ROUTER-CLEANUP: keep router branch, drop legacy when API retired 7387 var resources; 7388 var resourceCount; 7389 if (options.useRouterApi) { 7390 var page = window.luxuryPresence.routerApi.transformPage(data[resource]); 7391 resources = window.luxuryPresence.routerApi.transformResponse(resource, page.items); 7392 resourceCount = page.count; 7393 } else { 7394 resources = data[resource] || []; 7395 resourceCount = data[resource + 'Count'] && data[resource + 'Count'].count ? data[resource + 'Count'].count : 0; 7396 } 7397 if (resource === 'properties' && propertyPlaceholderImage?.id) { 7398 resources = resources.map(resource => { 7399 if (resource.media?.length === 0) { 7400 return Object.assign(resource, { 7401 media: [propertyPlaceholderImage] 7402 }); 7403 } 7404 return resource; 7405 }); 7406 } 7407 done(null, resources, resourceCount); 7408 }); 7409 } 7410 function getTotalPage() { 7411 return Math.ceil(totalNumber / pageSize); 7412 } 7413 function renderPaginationList() { 7414 var totalPages = getTotalPage(); 7415 var rangeStart = searchParams.page - pageRange; 7416 var rangeEnd = searchParams.page + pageRange; 7417 if (rangeEnd > totalPages) { 7418 rangeEnd = totalPages; 7419 rangeStart = totalPages - pageRange * 2; 7420 rangeStart = rangeStart < 1 ? 1 : rangeStart; 7421 } 7422 if (rangeStart <= 1) { 7423 rangeStart = 1; 7424 rangeEnd = Math.min(pageRange * 2 + 1, totalPages); 7425 } 7426 7427 // Making sure that 1 more page button is shown in edge positions 7428 if (pageRange === 0) { 7429 if (searchParams.page === totalPages) { 7430 rangeStart = totalPages <= 1 ? 1 : totalPages - 1; 7431 } 7432 if (searchParams.page === 1) { 7433 rangeEnd = Math.min(2, totalPages); 7434 } 7435 } 7436 7437 // Render pagination list 7438 if (mode === 'NUMBERED_PAGINATION') { 7439 el.innerHTML = generateHTML({ 7440 currentPage: searchParams.page, 7441 pageRange: pageRange, 7442 rangeStart: rangeStart, 7443 rangeEnd: rangeEnd 7444 }); 7445 if (totalPages < 2 && hidePaginationOnFirstPage) { 7446 container.style.display = 'none'; 7447 } else { 7448 container.style.display = ''; 7449 } 7450 } else if (mode === 'LOAD_MORE_BUTTON') { 7451 if (searchParams.page >= totalPages) {
7452 loadMoreButton.style.display = 'none'; 7453 } else { 7454 loadMoreButton.style.display = ''; 7455 } 7456 } 7457 } 7458 function render(data) { 7459 renderPaginationList(); 7460 7461 // Render new content with handlebars 7462 var html = ''; 7463 if (mode === 'LOAD_MORE_BUTTON' && searchParams.page > 1) { 7464 // If using load more, append search results to existing results 7465 html = dataContainer.innerHTML; 7466 } 7467 for (var i = 0; i < data.length; i++) { 7468 var itemData = recursivelyParse(data[i]); 7469 itemData.sectionIndex = options.sectionIndex; 7470 itemData.renderOptions = options.renderOptions; 7471 html += template(itemData); 7472 } 7473 dataContainer.innerHTML = html; 7474 7475 // Scroll Top 7476 if (!firstRender && mode === 'NUMBERED_PAGINATION' && (!renderBySearch || !options.skipScrollOnSearch)) { 7477 // Only animate to the top if we replace dataContainer (numbered pagination) 7478 setTimeout(() => { 7479 const scrollTargetOffsetTop = scrollTarget.getBoundingClientRect().top + window.scrollY - scrollOffset; 7480 window.scrollTo({ 7481 top: scrollTargetOffsetTop, 7482 behavior: 'smooth' 7483 }); 7484 }, 250); 7485 } 7486 return true; 7487 } 7488 function generateHTML(args) { 7489 const totalPages = getTotalPage(); 7490 const { 7491 rangeStart, 7492 rangeEnd 7493 } = args; 7494 const makeLnk = page => window.luxuryPresence.pagination.serializeParams(searchFormParams, sectionDomId, { 7495 ...searchParams, 7496 page 7497 }); 7498 let html = ''; 7499 if (showPrevious || showPageNumbers || showNext) { 7500 html += '<div class="paginationjs-pages"><ul>'; 7501 7502 // Previous button 7503 if (showPrevious) { 7504 if (searchParams.page <= 1) { 7505 html += `<li class="${classPrefix}-prev ${disableClassName}"><a>${prevText}</a></li>`; 7506 } else { 7507 html += `<li class="${classPrefix}-prev J-paginationjs-previous" data-num="${searchParams.page - 1}" title="Previous page"><a href="${makeLnk(searchParams.page - 1)}">${prevText}</a></li>`; 7508 } 7509 } 7510 7511 // Pages 7512 if (showPageNumbers) { 7513 if (rangeStart <= 3) { 7514 for (let i = 1; i < rangeStart; i++) { 7515 html += `<li class="${classPrefix}-page J-paginationjs-page ${i === searchParams.page ? activeClassName : ''}" data-num="${i}"><a ${i === searchParams.page ? '' : `href="${makeLnk(i)}"`}>${i}</a></li>`; 7516 } 7517 } else { 7518 if (showFirstOnEllipsisShow) { 7519 html += `<li class="${classPrefix}-page ${classPrefix}-first J-paginationjs-page" data-num="1"><a href="${makeLnk(1)}">1</a></li>`; 7520 } 7521 html += `<li class="${classPrefix}-ellipsis ${disableClassName}"><a>${ellipsisText}</a></li>`; 7522 } 7523 for (let i = rangeStart; i <= rangeEnd; i++) { 7524 html += `<li class="${classPrefix}-page J-paginationjs-page ${i === searchParams.page ? activeClassName : ''}" data-num="${i}"><a ${i === searchParams.page ? '' : `href="${makeLnk(i)}"`}>${i}</a></li>`; 7525 } 7526 if (rangeEnd < totalPages - 2) { 7527 html += `<li class="${classPrefix}-ellipsis ${disableClassName}"><a>${ellipsisText}</a></li>`; 7528 if (showLastOnEllipsisShow) { 7529 html += `<li class="${classPrefix}-page ${classPrefix}-last J-paginationjs-page" data-num="${totalPages}"><a href="${makeLnk(totalPages)}">${totalPages}</a></li>`; 7530 } 7531 } else { 7532 for (let i = rangeEnd + 1; i <= totalPages; i++) { 7533 html += `<li class="${classPrefix}-page J-paginationjs-page" data-num="${i}"><a href="${makeLnk(i)}">${i}</a></li>`; 7534 } 7535 } 7536 } 7537 7538 // Next button 7539 if (showNext) { 7540 if (searchParams.page >= totalPages) { 7541 html += `<li class="${classPrefix}-next ${disableClassName}"><a>${nextText}</a></li>`; 7542 } else { 7543 html += `<li class="${classPrefix}-next J-paginationjs-next" data-num="${searchParams.page + 1}" title="Next page"><a href="${makeLnk(searchParams.page + 1)}">${nextText}</a></li>`; 7544 } 7545 } 7546 html += '</ul></div>'; 7547 } 7548 return html; 7549 } 7550 function go(newSearchParams, callback, restorationVisit) { 7551 if (disabled) return; 7552 var goToPageNumber = newSearchParams.page; 7553 7554 // Page number is out of bounds 7555 if (!goToPageNumber || goToPageNumber < 1) return; 7556 var totalPage = getTotalPage(); 7557 7558 // Page number is out of bounds 7559 if (!restorationVisit && totalNumber > 0) { 7560 if (goToPageNumber > totalPage) return; 7561 } 7562 if (!options.skipSerialization && (mode === 'NUMBERED_PAGINATION' || searchContainer)) {
7563 var newURL = window.luxuryPresence.pagination.serializeParams(searchFormParams, sectionDomId, { 7564 ...newSearchParams, 7565 page: goToPageNumber !== 1 && mode === 'NUMBERED_PAGINATION' ? newSearchParams.page : null 7566 }); 7567 if (!areQueryParamsEqual(newSearchParams, searchParams) && !restorationVisit) { 7568 window.history.pushState({}, '', window.luxuryPresence.getPath(newURL, { 7569 queryString: true 7570 })); 7571 } 7572 } 7573 7574 // Fetch and update page 7575 fetchData(newSearchParams, renderWithData); 7576 disabled = true; 7577 function renderWithData(err, data, count) { 7578 if (err) { 7579 dataContainer.dispatchEvent(new Event('error')); 7580 } else { 7581 searchParams = { 7582 ...newSearchParams 7583 }; 7584 totalNumber = count - offset; 7585 if (data.length === 0) { 7586 dataContainer.dispatchEvent(new Event('no-data')); 7587 } else { 7588 dataContainer.dispatchEvent(new Event('data')); 7589 } 7590 render(data); 7591 firstRender = false; 7592 } 7593 disabled = false; 7594 renderBySearch = false; 7595 } 7596 } 7597 function areQueryParamsEqual(first, second) { 7598 var differ = Object.keys(first).find(function (key) { 7599 return first[key] !== undefined && first[key] !== null && first[key] !== second[key]; 7600 }); 7601 if (differ) { 7602 return false; 7603 } 7604 differ = Object.keys(second).find(function (key) { 7605 return second[key] !== undefined && second[key] !== null && second[key] !== first[key]; 7606 }); 7607 return !differ; 7608 } 7609 function next(callback) { 7610 go({ 7611 ...searchParams, 7612 page: searchParams.page + 1 7613 }, callback); 7614 } 7615 function previous(callback) { 7616 go({ 7617 ...searchParams, 7618 page: searchParams.page - 1 7619 }, callback); 7620 } 7621 function refresh(callback) { 7622 go(searchParams, callback); 7623 } 7624 function update(params, callback) { 7625 renderBySearch = true; 7626 go(params, callback); 7627 } 7628 function observer() { 7629 if (searchContainer) { 7630 searchContainer.addEventListener('search', function (event, newSearchParams) { 7631 newSearchParams = event.detail || newSearchParams; 7632 update({ 7633 ...searchParams, 7634 ...newSearchParams, 7635 page: 1 7636 }); 7637 }); 7638 searchContainer.addEventListener('clear-search', function () { 7639 const defaultSearchParams = { 7640 ...DefaultSearchParams 7641 }; 7642 const isCurrentSearchFormStateDefault = areQueryParamsEqual({ 7643 ...defaultSearchParams, 7644 page: null 7645 }, { 7646 ...searchParams, 7647 page: null 7648 }); 7649 7650 // if mode is LOAD_MORE_BUTTON we should't make anything on clear 7651 // when search form have default state 7652 if (isCurrentSearchFormStateDefault && mode !== 'NUMBERED_PAGINATION') { 7653 return; 7654 } 7655 7656 // if someone initially go to some page and click 'Clear' on search from 7657 // we should't navigate to first page. We should preserve page value 7658 if (isCurrentSearchFormStateDefault) { 7659 defaultSearchParams.page = searchParams.page; 7660 } 7661 syncSearchFormState(defaultSearchParams); 7662 update(defaultSearchParams); 7663 }); 7664 } 7665 if (mode === 'LOAD_MORE_BUTTON') { 7666 loadMoreButton.addEventListener('click', function (event) { 7667 go({ 7668 ...searchParams, 7669 page: searchParams.page + 1 7670 }); 7671 return false; 7672 }); 7673 } 7674 if (mode === 'LOAD_MORE_BUTTON' || mode === 'BUTTONLESS_PAGINATION') { 7675 // Do initial render 7676 go(Object.assign({}, searchParams, { 7677 page: 1 7678 })); 7679 return; 7680 } 7681 if (mode === 'NUMBERED_PAGINATION') { 7682 // Go to specified page number 7683 container.addEventListener(eventPrefix + 'go', function (event) { 7684 let { 7685 pageNumber, 7686 done 7687 } = event.detail || {}; 7688 pageNumber = parseInt(pageNumber, 10); 7689 if (!pageNumber) { 7690 return; 7691 } 7692 go({ 7693 ...searchParams, 7694 page: pageNumber 7695 }, done); 7696 }); 7697 7698 // Page number button click 7699 el.addEventListener('click', function (event) { 7700 let clickTarget = event.target; 7701 let current = clickTarget.closest('.J-paginationjs-page, .J-paginationjs-previous, .J-paginationjs-next'); 7702 let pageNumber = current?.getAttribute('data-num'); 7703 if (!pageNumber || current.classList.contains(disableClassName) || current.classList.contains(activeClassName)) { 7704 return; 7705 } else { 7706 go({ 7707 ...searchParams, 7708 page: parseInt(pageNumber, 10) 7709 }); 7710 } 7711 event.preventDefault(); 7712 }); 7713 7714 // Whether to load the default page 7715 var defaultPageNumber = searchParams.page; 7716 if (triggerPagingOnInit) {
7717 container.dispatchEvent(new CustomEvent(eventPrefix + 'go', { 7718 detail: { 7719 pageNumber: defaultPageNumber 7720 } 7721 })); 7722 } 7723 } 7724 window.addEventListener('popstate', function (event) { 7725 var newParams = Object.assign({}, DefaultSearchParams, window.luxuryPresence.pagination.deserializeParams(searchFormParams, sectionDomId)); 7726 if (!areQueryParamsEqual(newParams, searchParams)) { 7727 go(newParams, null, true); 7728 if (searchContainer) { 7729 syncSearchFormState(newParams); 7730 } 7731 } 7732 }); 7733 } 7734 function replaceVariables(template, variables) { 7735 var formattedString; 7736 for (var key in variables) { 7737 var value = variables[key]; 7738 var regexp = new RegExp('<%=\\s*' + key + '\\s*%>', 'img'); 7739 formattedString = (formattedString || template).replace(regexp, value); 7740 } 7741 return formattedString; 7742 } 7743 function isTextEditorValue(obj) { 7744 return 'raw' in obj || 'gjs' in obj; 7745 } 7746 function getTextEditorValue(obj) { 7747 if (!obj.html) { 7748 return ''; 7749 } 7750 var data = obj.html; 7751 var css = (obj.raw || obj.gjs || {}).css; 7752 if (css) { 7753 data = data + '<style>' + css + '</style>'; 7754 } 7755 return data; 7756 } 7757 7758 /** 7759 * check rendered resources for html and raw properties that could indicate if resource is from an advanced text editor. 7760 * strips out raw proprties and returns only HTML if it exists. 7761 * @param {Object} resources a collection retrieved from the database 7762 * @return {Object} the newly modified object. 7763 */ 7764 function recursivelyParse(resources) { 7765 forEach(resources, function (value, key) { 7766 if (!value) { 7767 return; 7768 } 7769 if (value instanceof Object) { 7770 if (isTextEditorValue(value)) { 7771 resources[key] = getTextEditorValue(value); 7772 return; 7773 } else { 7774 return recursivelyParse(value); 7775 } 7776 } 7777 try { 7778 var parsedData = JSON.parse(value); 7779 if (isTextEditorValue(parsedData)) { 7780 resources[key] = getTextEditorValue(parsedData); 7781 } 7782 } catch (e) { 7783 // do nothing if we failed to parse value as JSON 7784 } 7785 }); 7786 return resources; 7787 } 7788 function forEach(collection, iteratee) { 7789 var func = Array.isArray(collection) ? arrayEach : baseFor; 7790 return func(collection, iteratee); 7791 } 7792 function baseFor(object, iteratee) { 7793 if (!object) { 7794 return; 7795 } 7796 var iterable = Object(object); 7797 var props = Object.keys(object); 7798 var length = props.length; 7799 var index = -1; 7800 while (length--) { 7801 var key = props[++index]; 7802 if (iteratee(iterable[key], key, iterable) === false) { 7803 break; 7804 } 7805 } 7806 return object; 7807 } 7808 function objSet(obj, path, value) { 7809 const pathArray = Array.isArray(path) ? path : path.match(/([^[.\]])+/g); 7810 pathArray.reduce((acc, key, i) => { 7811 if (acc[key] === undefined) acc[key] = {}; 7812 if (i === pathArray.length - 1) acc[key] = value; 7813 return acc[key]; 7814 }, obj); 7815 } 7816 function arrayEach(array, iteratee) { 7817 var index = -1; 7818 var length = array.length; 7819 while (++index < length) { 7820 if (iteratee(array[index], index, array) === false) { 7821 break; 7822 } 7823 } 7824 return array; 7825 } 7826 function selectPageRangeMode(e) { 7827 pageRange = e.matches ? mobilePageRange : desktopPageRange; 7828 renderPaginationList(); 7829 } 7830 paginationMql.addEventListener('change', selectPageRangeMode); 7831})('section-ba9ae458-99fa-408d-b931-ca44d53d9839', JSON.parse("{\"pageSize\":\"3\",\"resource\":\"properties\",\"pageRange\":1,\"variables\":{\"sort\":\"salesPrice\",\"limit\":3,\"order\":\"salesPrice\",\"offset\":0,\"sortDir\":\"DESC\",\"statusId\":\"{{variables.statusId}}\",\"companyId\":\"9994eaf9-a60f-4f64-89b4-cb9e3cc349b1\",\"backfillMLSResults\":\"{{variables.backfillMLSResults}}\",\"statusIds\":[\"5f528253-abb7-484e-95c3-330269ac1105\"],\"propertyIds\":[],\"advancedFilters\":{\"query\":{\"statusIds\":{\"in\":[\"b0e88b38-7437-438d-a6fd-b13f135e1683\"]}}},\"websiteId\":\"5c1357ff-b286-47e5-bde4-3b0c9cb734db\"},\"searchFormParams\":[{\"param\":\"search\",\"global\":true,\"urlKey\":\"search\"},{\"type\":\"string\",\"param\":\"neighborhoodId\",\"global\":true,\"urlKey\":\"neighborhoodId\"},{\"type\":\"int\",\"param\":\"salesPriceGTE\",\"global\":true,\"urlKey\":\"pricemin\"},{\"type\":\"int\",\"param\":\"salesPriceLTE\",\"global\":true,\"urlKey\":\"pricemax\"},{\"type\":\"int\",\"param\":\"livingSpaceSizeGTE\",\"global\":true,\"urlKey\":\"areamin\"},{\"type\":\"int\",\"param\":\"livingSpaceSizeLTE\",\"global\":true,\"urlKey\":\"areamax\"},{\"type\":\"string\",\"param\":\"seasonalPriceId\",\"global\":true,\"urlKey\":\"seasonalPriceId\"}],\"skipScrollOnSearch\":true,\"hidePaginationOnFirstPage\":true,\"companyId\":\"9994eaf9-a60f-4f64-89b4-cb9e3cc349b1\",\"usePageResource\":false,\"sectionIndex\":3,\"useRouterApi\":false,\"routerApiAux\":{\"testimonials\":false,\"agents\":false,\"neighborhoods\":false,\"developments\":false,\"properties\":false,\"posts\":false,\"pressReleases\":false,\"offices\":true,\"teams\":true,\"pois\":true,\"postCategories\":false}}")); 7832} catch(e) { 7833console.log(e); 7834} 7835 try { 7836(function init(sectionDomId) { 7837 const customDropdownHTML = `
7838 <div class="custom-select" role="presentation"> 7839 <button class="custom-select-toggle" type="button" aria-haspopup="true" aria-expanded="false" role="combobox"></button> 7840 <div class="custom-select-menu"><div class="custom-select-content"></div></div> 7841 </div>`; 7842 const hideOriginalSelect = originalSelect => { 7843 originalSelect.classList.remove(...originalSelect.classList); 7844 originalSelect.classList.add("sr-only"); 7845 }; 7846 const openCustomSelect = customSelectToggle => { 7847 let customSelectMenu = customSelectToggle.nextElementSibling; 7848 customSelectMenu.classList.add("open"); 7849 customSelectToggle.classList.add("open"); 7850 customSelectToggle.ariaExpanded = true; 7851 }; 7852 const closeCustomSelect = customSelectToggle => { 7853 let customSelectMenu = customSelectToggle.nextElementSibling; 7854 customSelectMenu.classList.remove("open"); 7855 customSelectToggle.classList.remove("open"); 7856 customSelectToggle.ariaExpanded = false; 7857 }; 7858 const inheritAttributes = (originalSelect, customSelectToggle, customSelectMenu) => { 7859 customSelectToggle.className = customSelectToggle.className.concat(' ', originalSelect.className); 7860 customSelectToggle.id = originalSelect.getAttribute("id") + "custom"; 7861 customSelectToggle.dataset.name = originalSelect.getAttribute("name"); 7862 customSelectMenu.setAttribute("aria-labelledby", customSelectToggle.id); 7863 }; 7864 const initiateQuerySelectors = customSelect => {
7865 let customSelectToggle = customSelect.querySelector('.custom-select-toggle'), 7866 customSelectMenu = customSelect.querySelector('.custom-select-menu'), 7867 customSelectContent = customSelect.querySelector('.custom-select-content'); 7868 return { 7869 customSelectToggle, 7870 customSelectMenu, 7871 customSelectContent 7872 }; 7873 }; 7874 const initiateToggleText = (originalSelect, customSelectToggle) => { 7875 const dataLabelOption = originalSelect.querySelector("[data-label]"); 7876 const isMultiple = originalSelect?.multiple; 7877 if (!isMultiple) { 7878 const toggleText = dataLabelOption && dataLabelOption.dataset.label || originalSelect.querySelector(":checked")?.textContent || originalSelect.querySelector("option")?.textContent; 7879 customSelectToggle.classList.add("is-placeholder"); 7880 customSelectToggle.innerHTML = toggleText; 7881 } else { 7882 const placeholderText = dataLabelOption && dataLabelOption.dataset.label || originalSelect.querySelector("option")?.textContent || "Select"; 7883 const selectedOptions = Array.from(originalSelect.querySelectorAll("option:checked")).filter(opt => opt.value !== ""); 7884 if (selectedOptions.length === 0) { 7885 customSelectToggle.innerHTML = placeholderText; 7886 customSelectToggle.classList.add("is-placeholder"); 7887 } else if (selectedOptions.length === 1) { 7888 customSelectToggle.innerHTML = selectedOptions[0].textContent; 7889 customSelectToggle.classList.remove("is-placeholder"); 7890 } else { 7891 customSelectToggle.innerHTML = `${selectedOptions[0].textContent} +${selectedOptions.length - 1}`; 7892 customSelectToggle.classList.remove("is-placeholder"); 7893 } 7894 } 7895 }; 7896 const convertOptionsToButtons = (originalSelect, customSelectContent) => { 7897 originalSelect.querySelectorAll('option').forEach(option => { 7898 const button = createButtonFromOption(option); 7899 customSelectContent.appendChild(button); 7900 }); 7901 }; 7902 const createButtonFromOption = option => { 7903 let button = document.createElement('button'); 7904 button.className = "custom-select-item"; 7905 button.setAttribute("role", "option"); 7906 button.setAttribute("type", "button"); 7907 button.textContent = option.textContent; 7908 button.setAttribute('value', option.value); 7909 if (option.disabled) { 7910 button.setAttribute("disabled", "true"); 7911 } 7912 if (option.selected && option.value !== "") { 7913 button.classList.add("is-selected"); 7914 button.setAttribute("aria-selected", "true"); 7915 } 7916 return button; 7917 }; 7918 const initiateEventListeners = (originalSelect, customSelectToggle, customSelectMenu) => { 7919 const isMultiple = originalSelect?.multiple; 7920 originalSelect.addEventListener("focus", function () { 7921 openCustomSelect(customSelectToggle); 7922 customSelectToggle.focus(); 7923 }); 7924 customSelectToggle.addEventListener("click", function () { 7925 if (customSelectToggle.getAttribute("aria-expanded") === "true") { 7926 closeCustomSelect(customSelectToggle); 7927 } else { 7928 openCustomSelect(customSelectToggle); 7929 } 7930 }); 7931 customSelectMenu.addEventListener("click", function (event) { 7932 if (event.target.classList.contains('custom-select-item')) { 7933 const clickedValue = event.target.value; 7934 const clickedOption = originalSelect.querySelector(`option[value="${clickedValue}"]`); 7935 const placeholderOption = originalSelect.querySelector("[data-label]"); 7936 const placeholderText = placeholderOption?.dataset.label || originalSelect.querySelector("option")?.textContent || "Select"; 7937 if (!isMultiple) { 7938 let opts = customSelectMenu.querySelectorAll(".is-selected"); 7939 [].forEach.call(opts, function (el) { 7940 el.classList.remove("is-selected"); 7941 }); 7942 event.target.classList.add("is-selected"); 7943 clickedOption.selected = true; 7944 customSelectToggle.innerHTML = clickedOption.textContent; 7945 if (clickedValue === "" || event.target.classList.contains("is-placeholder")) { 7946 customSelectToggle.classList.add("is-placeholder"); 7947 } else { 7948 customSelectToggle.classList.remove("is-placeholder"); 7949 } 7950 let changeEvent = new Event('change', { 7951 bubbles: true 7952 }); 7953 originalSelect.dispatchEvent(changeEvent); 7954 closeCustomSelect(customSelectToggle); 7955 } else { 7956 if (clickedValue === "") { 7957 customSelectMenu.querySelectorAll(".is-selected").forEach(el => { 7958 el.classList.remove("is-selected"); 7959 el.setAttribute("aria-selected", "false"); 7960 }); 7961 originalSelect.querySelectorAll("option").forEach(opt => opt.selected = false); 7962 customSelectToggle.innerHTML = placeholderText; 7963 customSelectToggle.classList.add("is-placeholder"); 7964 } else { 7965 const isCurrentlySelected = event.target.classList.contains("is-selected"); 7966 event.target.classList.toggle("is-selected"); 7967 event.target.setAttribute("aria-selected", (!isCurrentlySelected).toString()); 7968 clickedOption.selected = !isCurrentlySelected; 7969 const allItem = customSelectMenu.querySelector('.custom-select-item[value=""]'); 7970 if (allItem) { 7971 allItem.classList.remove("is-selected"); 7972 allItem.setAttribute("aria-selected", "false"); 7973 } 7974 const allOption = originalSelect.querySelector('option[value=""]'); 7975 if (allOption) allOption.selected = false;
7976 const selectedItems = customSelectMenu.querySelectorAll(".custom-select-item.is-selected"); 7977 if (selectedItems.length === 0) { 7978 customSelectToggle.innerHTML = placeholderText; 7979 customSelectToggle.classList.add("is-placeholder"); 7980 } else if (selectedItems.length === 1) { 7981 customSelectToggle.innerHTML = selectedItems[0].textContent; 7982 customSelectToggle.classList.remove("is-placeholder"); 7983 } else { 7984 customSelectToggle.innerHTML = `${selectedItems[0].textContent} +${selectedItems.length - 1}`; 7985 customSelectToggle.classList.remove("is-placeholder"); 7986 } 7987 } 7988 let changeEvent = new Event('change', { 7989 bubbles: true 7990 }); 7991 originalSelect.dispatchEvent(changeEvent); 7992 } 7993 } 7994 }); 7995 document.addEventListener("click", function (event) { 7996 if (!customSelectMenu.contains(event.target) && !customSelectToggle.contains(event.target)) { 7997 closeCustomSelect(customSelectToggle); 7998 } 7999 }); 8000 }; 8001 const createCustomSelect = originalSelect => { 8002 const customSelect = document.createElement('div'); 8003 customSelect.innerHTML = customDropdownHTML; 8004 const { 8005 customSelectToggle, 8006 customSelectMenu, 8007 customSelectContent 8008 } = initiateQuerySelectors(customSelect); 8009 inheritAttributes(originalSelect, customSelectToggle, customSelectMenu); 8010 hideOriginalSelect(originalSelect); 8011 initiateToggleText(originalSelect, customSelectToggle); 8012 convertOptionsToButtons(originalSelect, customSelectContent); 8013 initiateEventListeners(originalSelect, customSelectToggle, customSelectMenu); 8014 return customSelect; 8015 }; 8016 const setCustomSelectValue = (originalSelect, value) => {
8017 let customSelect = originalSelect.nextElementSibling; 8018 let customSelectToggle = customSelect.querySelector(".custom-select-toggle"); 8019 let customSelectContent = customSelect.querySelector(".custom-select-content"); 8020 if (!customSelectContent) return; 8021 let selectedOption = value ? Array.from(customSelectContent.children).find(function (b) { 8022 return b.value === value; 8023 }) : customSelectContent.firstChild; 8024 if (!selectedOption) { 8025 return; 8026 } 8027 if (selectedOption.classList.contains("is-disabled")) return; 8028 let elementNumber = Array.from(selectedOption.parentNode.children).indexOf(selectedOption); 8029 let linkedOption = originalSelect.querySelectorAll("option")[elementNumber]; 8030 linkedOption.selected = true; 8031 selectedOption.classList.add("is-selected"); 8032 selectedOption.parentNode.querySelectorAll(".custom-select-item").forEach(el => { 8033 if (el !== selectedOption) { 8034 el.classList.remove("is-selected"); 8035 } 8036 }); 8037 closeCustomSelect(customSelectToggle); 8038 customSelectToggle.innerHTML = linkedOption.innerHTML; 8039 if (linkedOption.value) { 8040 customSelectToggle.classList.remove("is-placeholder"); 8041 if (linkedOption.value !== "") { 8042 customSelect.classList.add("has-selection"); 8043 } else { 8044 customSelect.classList.remove("has-selection"); 8045 } 8046 } else { 8047 customSelect.classList.remove("has-selection"); 8048 customSelectToggle.classList.add("is-placeholder"); 8049 } 8050 }; 8051 const customSelect = originalSelect => { 8052 const customSelect = createCustomSelect(originalSelect); 8053 originalSelect.insertAdjacentElement('afterend', customSelect); 8054 return customSelect; 8055 }; 8056 const resetCustomSelect = originalSelect => { 8057 const customSelectEl = originalSelect.nextElementSibling; 8058 const customSelectToggle = customSelectEl?.querySelector('.custom-select-toggle'); 8059 const customSelectContent = customSelectEl?.querySelector('.custom-select-content'); 8060 if (!customSelectToggle || !customSelectContent) return; 8061 const placeholderOption = originalSelect.querySelector("[data-label]"); 8062 const placeholderText = placeholderOption?.dataset.label || originalSelect.querySelector("option")?.textContent || "Select"; 8063 customSelectContent.querySelectorAll('.custom-select-item').forEach(item => { 8064 item.classList.remove('is-selected'); 8065 item.setAttribute('aria-selected', 'false'); 8066 }); 8067 originalSelect.querySelectorAll('option').forEach(opt => opt.selected = false); 8068 customSelectToggle.innerHTML = placeholderText; 8069 customSelectToggle.classList.add('is-placeholder'); 8070 const customSelectWrapper = customSelectEl.querySelector('.custom-select'); 8071 if (customSelectWrapper) { 8072 customSelectWrapper.classList.remove('has-selection'); 8073 } 8074 }; 8075 const section = document.getElementById(sectionDomId); 8076 const searchContainer = section.querySelector('.search-container'); 8077 if (!searchContainer) return; 8078 const emptyResultElement = section.querySelector('.empty-result'); 8079 const dataContainer = section.querySelector('.data-container'); 8080 const areaSlider = searchContainer.querySelector('input[name="area-slider"]'); 8081 const neighborhoodSelect = searchContainer.querySelector('select[name="neighborhood-select"]'); 8082 const seasonalPriceSelect = section.querySelector('select[name="seasonal-price"]'); 8083 const searchInput = searchContainer.querySelector('input[name="search-input"]'); 8084 const resetSearchInput = section.querySelector(".js-reset"); 8085 const showFilter = section.querySelector('.js-show-filter'); 8086 const filtersCount = section.querySelector('.js-selected-filters'); 8087 const hideFilter = section.querySelector('.js-hide-filter'); 8088 const neighborhoodsSelectHolder = section.querySelector('.js-neighborhoods-select-holder'); 8089 const seasonalPriceSelectHolder = section.querySelector('.js-rental-rates-select-holder'); 8090 const _priceSlider = section.querySelector('.js-price-slider'); 8091 const _priceSliderMinLabel = section.querySelector('.js-price-slider-min'); 8092 const _priceSliderMaxLabel = section.querySelector('.js-price-slider-max');
8093 const _areaSlider = section.querySelector('.js-area-slider'); 8094 const _areaSliderMinLabel = section.querySelector('.js-area-slider-min'); 8095 const _areaSliderMaxLabel = section.querySelector('.js-area-slider-max'); 8096 const _priceMin = parseInt(_priceSlider.getAttribute("data-min"), 10); 8097 const _priceMax = parseInt(_priceSlider.getAttribute("data-max"), 10); 8098 const _priceStep = parseInt(_priceSlider.getAttribute("data-step"), 10); 8099 const _areaMin = parseInt(_areaSlider.getAttribute("data-min"), 10); 8100 const _areaMax = parseInt(_areaSlider.getAttribute("data-max"), 10); 8101 const _areaStep = parseInt(_areaSlider.getAttribute("data-step"), 10); 8102 let _currentPriceSliderMin, _currentPriceSliderMax; 8103 let _currentAreaSliderMin, _currentAreaSliderMax; 8104 showFilter.addEventListener('click', function () { 8105 searchContainer.style.display = 'block'; 8106 showFilter.style.display = 'none'; 8107 hideFilter.style.display = 'flex'; 8108 }); 8109 hideFilter.addEventListener('click', function () { 8110 searchContainer.style.display = 'none'; 8111 showFilter.style.display = 'flex'; 8112 hideFilter.style.display = 'none'; 8113 }); 8114 if (_priceSlider) { 8115 noUiSlider.create(_priceSlider, { 8116 start: [_priceMin, _priceMax], 8117 connect: true, 8118 step: _priceStep, 8119 range: { 8120 'min': _priceMin, 8121 'max': _priceMax 8122 }, 8123 format: { 8124 to: function (val) { 8125 return window.luxuryPresence.formatSliderPriceRange(val, { 8126 min: _priceMin, 8127 max: _priceMax 8128 }, "$", { 8129 lowPrefix: "<", 8130 highSuffix: "+" 8131 }); 8132 }, 8133 from: function (val) { 8134 return Number(val.replace(/[^0-9.-]+/g, "")); 8135 } 8136 } 8137 }); 8138 _priceSlider.noUiSlider.on('update', function (values) { 8139 _priceSliderMinLabel.innerHTML = `${values[0]}`; 8140 _priceSliderMaxLabel.innerHTML = `${values[1]}`; 8141 const [rawMin, rawMax] = _priceSlider.noUiSlider.get(true); 8142 _currentPriceSliderMin = rawMin; 8143 _currentPriceSliderMax = rawMax; 8144 }); 8145 _priceSlider.noUiSlider.on('end', function () { 8146 onSearch(); 8147 }); 8148 } 8149 if (_areaSlider) { 8150 noUiSlider.create(_areaSlider, { 8151 start: [_areaMin, _areaMax], 8152 connect: true, 8153 step: _areaStep, 8154 range: { 8155 'min': _areaMin, 8156 'max': _areaMax 8157 }, 8158 format: { 8159 to: function (val) { 8160 return window.luxuryPresence.formatSliderPriceRange(val, { 8161 min: _areaMin, 8162 max: _areaMax 8163 }, "", { 8164 lowPrefix: "<", 8165 highSuffix: "+" 8166 }); 8167 }, 8168 from: function (val) { 8169 return Number(val.replace(/[^0-9.-]+/g, "")); 8170 } 8171 } 8172 }); 8173 _areaSlider.noUiSlider.on('update', function (values) { 8174 _areaSliderMinLabel.innerHTML = `${values[0]}`; 8175 _areaSliderMaxLabel.innerHTML = `${values[1]}`; 8176 const [rawMin, rawMax] = _areaSlider.noUiSlider.get(true); 8177 _currentAreaSliderMin = rawMin; 8178 _currentAreaSliderMax = rawMax; 8179 }); 8180 _areaSlider.noUiSlider.on('end', function () { 8181 onSearch(); 8182 }); 8183 } 8184 function onSearch() { 8185 const params = { 8186 salesPriceGTE: _currentPriceSliderMin != _priceMin ? _currentPriceSliderMin : null, 8187 salesPriceLTE: _currentPriceSliderMax != _priceMax ? _currentPriceSliderMax : null, 8188 livingSpaceSizeGTE: _currentAreaSliderMin != _areaMin ? _currentAreaSliderMin : null, 8189 livingSpaceSizeLTE: _currentAreaSliderMax != _areaMax ? _currentAreaSliderMax : null, 8190 bedroomCountGTE: null, 8191 bathCountGTE: null, 8192 neighborhoodId: neighborhoodSelect.value || null, 8193 seasonalPriceId: seasonalPriceSelect?.value || null, 8194 search: searchInput.value || null 8195 }; 8196 const paramCount = Object.values(params).filter(value => value !== null).length; 8197 if (paramCount > 0) { 8198 filtersCount.textContent = paramCount; 8199 filtersCount.style.display = 'inline-block'; 8200 } else { 8201 filtersCount.style.display = 'none'; 8202 } 8203 const event = new CustomEvent('search', { 8204 detail: params 8205 });
8206 searchContainer.dispatchEvent(event); 8207 } 8208 searchContainer.addEventListener('neighborhoods-data', function (ev) { 8209 const neighborhoods = ev.detail; 8210 neighborhoods.data.forEach(item => { 8211 const option = document.createElement('option'); 8212 option.value = item.value; 8213 option.textContent = item.label; 8214 neighborhoodSelect.appendChild(option); 8215 }); 8216 customSelect(section.querySelector(".dropdown-select[name='neighborhood-select']")); 8217 if (neighborhoods.data.length) neighborhoodsSelectHolder.style.display = 'block'; 8218 }); 8219 if (seasonalPriceSelect) { 8220 searchContainer.addEventListener('rental-rates-data', function (ev) { 8221 const rentalRates = ev.detail; 8222 rentalRates.data.forEach(item => { 8223 const option = document.createElement('option'); 8224 option.value = item.rentalRateId; 8225 option.textContent = item.label; 8226 seasonalPriceSelect.appendChild(option); 8227 }); 8228 customSelect(section.querySelector(".dropdown-select[name='seasonal-price']")); 8229 if (rentalRates.data.length) seasonalPriceSelectHolder.style.display = 'block'; 8230 }); 8231 } 8232 searchContainer.addEventListener('sync', function (ev) { 8233 const params = ev.detail; 8234 searchInput.value = params.search || ''; 8235 if (neighborhoodSelect) setCustomSelectValue(neighborhoodSelect, params.neighborhoodId); 8236 if (seasonalPriceSelect) setCustomSelectValue(seasonalPriceSelect, params.seasonalPriceId); 8237 _priceSlider.noUiSlider.set([params.salesPriceGTE || _priceMin, params.salesPriceLTE || _priceMax]); 8238 _areaSlider.noUiSlider.set([params.livingSpaceSizeGTE || _areaMin, params.livingSpaceSizeLTE || _areaMax]); 8239 }); 8240 searchInput.addEventListener('input', window.luxuryPresence.debounce(onSearch, 1000)); 8241 resetSearchInput.addEventListener('click', function () { 8242 neighborhoodSelect.value = ''; 8243 searchInput.value = ''; 8244 if (seasonalPriceSelect) { 8245 seasonalPriceSelect.value = ''; 8246 setCustomSelectValue(seasonalPriceSelect, ''); 8247 } 8248 setCustomSelectValue(neighborhoodSelect); 8249 _priceSlider.noUiSlider.set([_priceMin, _priceMax]); 8250 _areaSlider.noUiSlider.set([_areaMin, _areaMax]); 8251 setTimeout(() => { 8252 const params = { 8253 salesPriceGTE: null, 8254 salesPriceLTE: null, 8255 livingSpaceSizeGTE: null, 8256 livingSpaceSizeLTE: null, 8257 bedroomCountGTE: null, 8258 bathCountGTE: null, 8259 neighborhoodId: null, 8260 seasonalPriceId: null, 8261 search: null 8262 }; 8263 const event = new CustomEvent('search', { 8264 detail: params 8265 }); 8266 searchContainer.dispatchEvent(event); 8267 }, 1000); 8268 }); 8269 neighborhoodSelect.addEventListener('change', onSearch); 8270 if (seasonalPriceSelect) { 8271 seasonalPriceSelect.addEventListener('change', onSearch); 8272 } 8273})('section-ba9ae458-99fa-408d-b931-ca44d53d9839', JSON.parse("{\"resource\":\"properties\",\"useRouterApi\":false,\"companyId\":\"9994eaf9-a60f-4f64-89b4-cb9e3cc349b1\",\"usePageResource\":false,\"sectionIndex\":3,\"routerApiAux\":{\"testimonials\":false,\"agents\":false,\"neighborhoods\":false,\"developments\":false,\"properties\":false,\"posts\":false,\"pressReleases\":false,\"offices\":true,\"teams\":true,\"pois\":true,\"postCategories\":false}}")); 8274} catch(e) { 8275console.log(e); 8276} 8277 try { 8278(function init(sectionDomId) {})('section-f3de70dc-17c3-43de-a8ed-1cf5c7484236', 'undefined'); 8279} catch(e) { 8280console.log(e); 8281} 8282 try { 8283(function init(sectionDomId, options) { 8284 let section = null; 8285 switch (sectionDomId) { 8286 case 'NAVBAR': 8287 section = document.querySelector('body > nav, #global-navbar'); 8288 break; 8289 case 'FOOTER': 8290 section = document.querySelector('body > footer, #global-footer'); 8291 break; 8292 case 'SIDEMENU': 8293 section = document.querySelector('body > div.sidemenu, #global-sidemenu'); 8294 break; 8295 case 'CONTACT': 8296 section = document.querySelector('#modal-global-contact-us'); 8297 break; 8298 case 'MOBILE_CONTACT': 8299 section = document.querySelector('body > div.mobile-contact-wrapper, #global-mobile-contact'); 8300 break; 8301 case 'SUBSCRIBE': 8302 // Subscribe Not Implemented 8303 section = null; 8304 break; 8305 case 'ACCOUNT': 8306 // Account Not Implemented 8307 section = null; 8308 break; 8309 default: 8310 section = document.querySelector('#' + sectionDomId); 8311 } 8312 if (!section) { 8313 return; 8314 } 8315 var url = window.luxuryPresence.defaults.routerUrl; 8316 var companyId = window.luxuryPresence.defaults.companyId; 8317 var limit = options.limit || 8; 8318 if (typeof limit === 'string') { 8319 limit = parseInt(limit, 10) || 8; // this NaN protection also filters out 0 - this is expected 8320 } 8321 var templateSelector = options.templateSelector || '.jsIGTemplate'; 8322 var containerSelector = options.containerSelector || '.jsIGContainer'; 8323 var hasDataClass = options.hasDataClass || 'has-data'; 8324 var hideActivateMessage = options.hasDataClass || false; 8325 var payload = { 8326 companyId: companyId, 8327 limit: limit 8328 }; 8329 if (options.resourceId && typeof options.resourceId === 'string' && !
8329options.resourceId.match(/^{{.*}}$/)) { 8330 payload.resourceId = options.resourceId; 8331 // Federated enum values are uppercase (AGENT/TEAM); element configs carry lowercase 8332 payload.resource = String(options.resource || 'agent').toUpperCase(); 8333 payload.fallbackToNoResource = options.fallbackToNoResource === 'true' || options.fallbackToNoResource === true; 8334 } 8335 var query = 'query InstagramFeed (\n' + ' $companyId: ID!\n' + ' $instagramAuthorizationId: ID\n' + ' $username: String\n' + (payload.resourceId ? '$resource: InstagramContentAuthResourceEnum\n $resourceId: ID\n $fallbackToNoResource: Boolean' : '') + ' $limit: Int\n' + ') {\n' + ' instagramContentFeed (\n' + ' companyId: $companyId\n' + ' instagramAuthorizationId: $instagramAuthorizationId\n' + ' username: $username\n' + (payload.resourceId ? 'resource: $resource\n resourceId: $resourceId\n fallbackToNoResource: $fallbackToNoResource' : '') + ' limit: $limit\n' + ' ) {\n' + ' active\n' + ' feedUrl\n' + ' username\n' + ' items {\n' + ' id\n' + ' caption\n' + ' commentsCount\n' + ' likeCount\n' + ' mediaType\n' + ' mediaUrl\n' + ' permalink\n' + ' shortcode\n' + ' thumbnailUrl\n' + ' timestamp\n' + ' username\n' + ' }\n' + ' }\n' + '}\n'; 8336 var templateHTML = section.querySelector(templateSelector)?.innerHTML || ''; 8337 var template = Handlebars.compile(templateHTML); 8338 var container = section.querySelector(containerSelector); 8339 if (options.variables) { 8340 Object.keys(options.variables).forEach(function (key) { 8341 if (options.variables.hasOwnProperty(key) && (typeof options.variables[key] !== 'string' || !options.variables[key].match(/^{{.*}}$/))) { 8342 payload[key] = options.variables[key]; 8343 } 8344 }); 8345 } 8346 function getData() { 8347 var feed = { 8348 error: true 8349 }; 8350 if (payload.companyId == null || payload.companyId === '') { 8351 // instagramContentFeed requires companyId (ID!) â nothing to fetch without it. 8352 // Checked after the options.variables merge, which may override the default. 8353 // Deferred so element listeners (attached after this template runs) still receive it. 8354 setTimeout(function () { 8355 section.dispatchEvent(new CustomEvent('ig-data', { 8356 detail: feed 8357 })); 8358 }, 0); 8359 return; 8360 } 8361 superagent.post(url + '/graphql').send({ 8362 query: query, 8363 variables: payload 8364 }).then(res => { 8365 try { 8366 if (!res.body.errors) { 8367 feed = res.body.data.instagramContentFeed; 8368 if (!feed.active) { 8369 if (!hideActivateMessage && sectionDomId === 'section-temp' && container) { 8370 if (payload.resourceId) { 8371 container.innerHTML = '<div style="text-align: center">Instagram account is not connected to selected ' + payload.resource + '.<br /> Connect it on the ' + payload.resource + ' page</div>'; 8372 } else { 8373 container.innerHTML = '<div style="text-align: center">Instagram account is not connected.<br /> Connect it on <a href="/profile">Profile page</a></div>'; 8374 } 8375 } 8376 } else { 8377 if (feed.items.length) { 8378 if (hasDataClass) { 8379 section.classList.add(hasDataClass); 8380 } 8381 const feedName = feed.username; 8382 section.querySelectorAll('.jsIGLink').forEach(link => link.setAttribute('href', feed.feedUrl)); 8383 section.querySelectorAll('.jsIGName').forEach(name => { 8384 name.textContent = '@' + feedName; 8385 name.setAttribute('title', '@' + feedName); 8386 }); 8387 if (container && template) { 8388 container.innerHTML = template(feed); 8389 } 8390 } 8391 } 8392 } 8393 } catch (e) {} 8394 section.dispatchEvent(new CustomEvent('ig-data', { 8395 detail: feed 8396 })); 8397 }).catch(e => { 8398 console.log('err', e); 8399 section.dispatchEvent(new CustomEvent('ig-data', { 8400 detail: feed 8401 })); 8402 }); 8403 } 8404 getData(); 8405})('section-9f328597-3b81-4ba9-bfda-00595329a24d', JSON.parse("{\"limit\":11,\"resource\":\"agent\",\"resourceId\":\"{{id}}\",\"fallbackToNoResource\":\"{{{fallbackToNoResource}}}\",\"companyId\":\"9994eaf9-a60f-4f64-89b4-cb9e3cc349b1\",\"usePageResource\":false,\"sectionIndex\":5,\"useRouterApi\":false,\"routerApiAux\":{\"testimonials\":false,\"agents\":false,\"neighborhoods\":false,\"developments\":false,\"properties\":false,\"posts\":false,\"pressReleases\":false,\"offices\":true,\"teams\":true,\"pois\":true,\"postCategories\":false}}")); 8406} catch(e) { 8407console.log(e); 8408} 8409 try { 8410(function init(sectionDomId) { 8411 const section = document.getElementById(sectionDomId); 8412 const slider = section.querySelector(".js-slider"); 8413 section.addEventListener("ig-data", function () { 8414 var splide = new Splide(slider, { 8415 rewind: true,
8416 arrows: false, 8417 type: 'loop', 8418 padding: '20%', 8419 gap: '16px', 8420 pagination: false, 8421 clones: 1, 8422 perPage: 1, 8423 perMove: 1, 8424 destroy: true, 8425 breakpoints: { 8426 768: { 8427 destroy: false 8428 } 8429 } 8430 }); 8431 splide.mount(); 8432 }); 8433})('section-9f328597-3b81-4ba9-bfda-00595329a24d', JSON.parse("{\"resource\":\"agent\",\"useRouterApi\":false,\"companyId\":\"9994eaf9-a60f-4f64-89b4-cb9e3cc349b1\",\"usePageResource\":false,\"sectionIndex\":5,\"routerApiAux\":{\"testimonials\":false,\"agents\":false,\"neighborhoods\":false,\"developments\":false,\"properties\":false,\"posts\":false,\"pressReleases\":false,\"offices\":true,\"teams\":true,\"pois\":true,\"postCategories\":false}}")); 8434} catch(e) { 8435console.log(e); 8436} 8437 8438 var { body } = document; 8439 var globalNav = body.querySelector(':scope > nav'); 8440 var globalSideMenu = body.querySelector(':scope > div.sidemenu'); 8441 8442 document.querySelectorAll('body > .modal .close').forEach(modalCloseBtn => { 8443 modalCloseBtn.addEventListener('click', function() { 8444 window.lpUI.hideClosestModal(modalCloseBtn); 8445 }); 8446 }); 8447 8448 window.addEventListener('click', function() { 8449 document.querySelectorAll('.share-popup').forEach(sharedPopup => { 8450 sharedPopup.style.display = 'none' ; 8451 }); 8452 }); 8453 8454 /* Misc Partials / Addons */ 8455 function initWOW() { 8456 if (wow) { 8457 wow.init(); 8458 wow.scrollHandler(); 8459 setTimeout(function () { 8460 const wowHideStyle = document.getElementById('wow-hide-elements'); 8461 if(wowHideStyle) { 8462 wowHideStyle.disabled = true; 8463 } 8464 }, 1); 8465 } 8466 } 8467 8468 const wowHideElements = document.getElementById('wow-hide-elements'); 8469 if (wowHideElements) { 8470 wowHideElements.disabled = false; 8471 } 8472 8473 initWOW(); 8474 8475 /* Generic Navbar Handling */ 8476 globalNav?.querySelectorAll('.hamburger-component').forEach(hamburgerComponent => { 8477 hamburgerComponent.addEventListener('click', function() { 8478 window.lpUI.sideMenuToggle(); 8479 }); 8480 }); 8481 globalSideMenu?.querySelectorAll('.toggle').forEach(toggleEl => { 8482 toggleEl.addEventListener('click', function() { 8483 window.lpUI.sideMenuToggle(); 8484 }); 8485 }); 8486 8487 document.querySelectorAll('body > nav .sub-nav-container > a, body > div.sidemenu .sub-nav-container > a').forEach(linkEl => { 8488 linkEl.addEventListener('click', function(e) { 8489 e.preventDefault(); 8490 e.stopPropagation(); 8491 var subNav = linkEl.parentElement.querySelector(':scope > .sub-nav'); 8492 if (subNav && subNav.classList.contains('visible')){ 8493 subNav.classList.remove('visible'); 8494 } else { 8495 globalNav?.querySelectorAll('.sub-nav-container .sub-nav').forEach(globalSubNav => { 8496 globalSubNav.classList.remove('visible'); 8497 }); 8498 globalSideMenu?.querySelectorAll('.sub-nav').forEach(sideMenuSubNav => { 8499 sideMenuSubNav.classList.remove('visible'); 8500 }); 8501 subNav?.classList.add('visible'); 8502 } 8503 }); 8504 }); 8505 window.addEventListener('click', function() { 8506 document.querySelectorAll('nav .sub-nav.visible, .sidemenu .sub-nav.visible').forEach(subNavEl => { 8507 subNavEl.classList.remove('visible'); 8508 }); 8509 }); 8510 8511 } 8512 8513 document.addEventListener('DOMContentLoaded', lpGlobalInit); 8514 8515 </script>
8515 8516 8517
8517<script type="module"> 8518 function selectorExists(selector) { 8519 return document.querySelector(selector) !== null; 8520 } 8521 8522 // Function to initialize the third-party library 8523 function initParoller(selector = '.my-paroller') { 8524 var $parollerElements = $(selector); 8525 if ($parollerElements.length > 0 && typeof $parollerElements.paroller === 'function') { 8526 $parollerElements.paroller(); 8527 } 8528 } 8529 8530 const parollerSelector = '.my-paroller'; 8531 8532 if (selectorExists(parollerSelector)) { 8533 window.luxuryPresence.libs.loadJs(window.luxuryPresence.libs.jsLibsMap.paroller.jsSrc, function() { 8534 initParoller(parollerSelector); 8535 window.luxuryPresence.libs.jsLibsMap.paroller.isLoaded = true; 8536 }); 8537 } 8538 </script>
8538 8539 8540 <!-- Google Tag Manager (noscript) -->
vendor: 80 bytes, lines 8540-8541
8540 8541 <noscript><iframe src="https://www.googletagmanager.com/ns.html?id=
8541GTM-T9SFQGHZ
vendor: 107 bytes, lines 8541-8543
8541" 8542 height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript> 8543
8543<!-- End Google Tag Manager --> 8544 8545
8545<script 8546 src="https://cdn.luxurypresence.com/scripts/a11y-widget/latest/widget.js" 8547 data-website-id="5c1357ff-b286-47e5-bde4-3b0c9cb734db" 8548 8549 defer></script>
vendor: 1 bytes, line 8549
8549
8550<script type="module" src="https://static.cloudflareinsights.com/beacon.min.js/v31edd6df95cf4e85bb4c19e7a9bdbcba1788362987495" integrity="sha512-iIg7k2xntmwu6/uSb5tpc/hySgZc4eoL31yB29W6tJFo2akwjPWcEqnCEdJvGexCL0KEQwVYv5BlowfhVz26hg==" data-cf-beacon='{"version":"2024.11.0","token":"d2a8f4b3d65944edb8fcd6ffce49ee5e","spa":2}' crossorigin="anonymous"></script>
vendor: 1 bytes, line 8550
8550
8551<script>(function(){function c(){var b=a.contentDocument||(a.contentWindow&&a.contentWindow.document);if(b){var d=b.createElement('script');d.innerHTML="window.__CF$cv$params={r:'a422778a28bbeae8',t:'MTc5MDU5NTU2MA=='};var a=document.createElement('script');a.src='/cdn-cgi/challenge-platform/scripts/jsd/main.js';document.getElementsByTagName('head')[0].appendChild(a);";b.getElementsByTagName('head')[0].appendChild(d)}}if(document.body){var a=document.createElement('iframe');a.height=1;a.width=1;a.style.position='absolute';a.style.top=0;a.style.left=0;a.style.border='none';a.style.visibility='hidden';document.body.appendChild(a);if('loading'!==document.readyState)c();else if(window.addEventListener)document.addEventListener('DOMContentLoaded',c);else{var e=document.onreadystatechange||function(){};document.onreadystatechange=function(b){e(b);'loading'!==document.readyState&&(document.onreadystatechange=e,c())}}}})();</script>
8551</body> 8552</html>
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.