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>Honolulu Luxury Real Estate â Discover Your Dream Home</title> 10 <meta property="og:title" content="Honolulu Luxury Real Estate â Discover Your Dream Home"> 11 <meta name="twitter:title" content="Honolulu Luxury Real Estate â Discover Your Dream Home"> 12 <meta name="description" content="Explore Honolulu luxury real estate with The Choi Group. Experience unrivaled expertise and find your perfect island home. Contact us now!"> 13 <meta property="og:description" content="Explore Honolulu luxury real estate with The Choi Group. Experience unrivaled expertise and find your perfect island home. Contact us now!"> 14 <meta name="twitter:description" content="Explore Honolulu luxury real estate with The Choi Group. Experience unrivaled expertise and find your perfect island home. Contact us now!"> 15 <meta property="og:image" content="https://media-production.lp-cdn.com/cdn-cgi/image/format=auto,quality=85,fit=scale-down,width=1920/https://media-production.lp-cdn.com/media/1a6365c0-d9da-471f-a248-00f5bb3c4704"> 16 <meta name="twitter:image" content="https://media-production.lp-cdn.com/cdn-cgi/image/format=auto,quality=85,fit=scale-down,width=1920/https://media-production.lp-cdn.com/media/1a6365c0-d9da-471f-a248-00f5bb3c4704"> 17 <meta property="og:url" content="https://choirealty.com/"> 18 <link rel="canonical" href="https://choirealty.com/"> 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=Questrial:400"><link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Karla:200,300,400,500,600,700,800,200i,300i,400i,500i,600i,700i,800i"> 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:Questrial,sans-serif;--global-primary-font-family-short:Questrial;--global-secondary-font-family:Karla,sans-serif;--global-secondary-font-family-short:Karla;--global-body-padding:0px;--global-background-color:#fff;--global-body-font-size:16px;--global-h1-font-size:56px;--global-h2-font-size:44px;--global-h3-font-size:32px;--global-h4-font-size:28px;--global-h5-font-size:22px;--global-h6-font-size:20px;--global-section-padding:96px;}@media (max-width: 768px){:root{--global-section-padding:64px;}}body{padding:0px;background-color:#fff;font-family:Karla,sans-serif;font-size:16px;}@media (min-width: 768px){body{padding:0px 0px 0px 0px;}}h1,h2,h3,h4,h5,h6,button{font-family:Questrial,sans-serif;}h1{font-size:56px;}h2{font-size:44px;}h3{font-size:32px;}h4{font-size:28px;}h5{font-size:22px;}h6{font-size:20px;}</style> 30 <link rel="stylesheet" type="text/css" href="https://styles.luxurypresence.com/masterpiece/index.css" /> 31 <style id="website-css">:root{--primary-dark:#EBE7E0;--primary-light:#FFFFFF;--accent-dark:#B1191E;--accent-light:#FFFFFF;--background-dark-inactive:rgba(0, 0, 0, 0.05);--background-light-inactive:rgba(255, 255, 255, 0.1);--border-dark-inactive:rgba(0, 0, 0, 0.125);--border-dark-active:rgba(0, 0, 0, 0.75);--border-light-inactive:rgba(255, 255, 255, 0.25);--border-light-active:rgba(255, 255, 255, 0.75);--typography-dark:#000000;--typography-light:#FFFFFF;--text-case:uppercase;}h1,h2,h3,h4,h5,h6{text-transform:var(--text-case)!important;}a.lp-link,span.lp-link,a.link:not(.sidebar__nav-link):not(.sub-nav-heading):not(.sub-nav-link):not(.main-nav),span.link,a[href="/terms-and-conditions"],footer li.footer-navigation__item>*,footer a:not(.lp-socials__link){position:relative;-webkit-transition:0.4s;transition:0.4s;opacity:1;-webkit-text-decoration:none!important;text-decoration:none!important;}a.lp-link:before,span.lp-link:before,a.link:not(.sidebar__nav-link):not(.sub-nav-heading):not(.sub-nav-link):not(.main-nav):before,span.link:before,a[href="/terms-and-conditions"]:before,footer li.footer-navigation__item>*:before,footer a:not(.lp-socials__link):before,a.lp-link:after,span.lp-link:after,a.link:not(.sidebar__nav-link):not(.sub-nav-heading):not(.sub-nav-link):not(.main-nav):after,span.link:after,a[href="/terms-and-conditions"]:after,footer li.footer-navigation__item>*:after,footer a:not(.lp-socials__link):after{opacity:1;-webkit-transition:0.4s;transition:0.4s;-webkit-text-decoration:none;text-decoration:none;line-height:1;content:"";height:1px;position:absolute;bottom:0;left:0;}a.lp-link:before,span.lp-link:before,a.link:not(.sidebar__nav-link):not(.sub-nav-heading):not(.sub-nav-link):not(.main-nav):before,span.link:before,a[href="/terms-and-conditions"]:before,footer li.footer-navigation__item>*:before,footer a:not(.lp-socials__link):before{width:100%;background:rgb(from currentColor r g b / 15%)!important;}a.lp-link:after,span.lp-link:after,a.link:not(.sidebar__nav-link):not(.sub-nav-heading):not(.sub-nav-link):not(.main-nav):after,span.link:after,a[href="/terms-and-conditions"]:after,footer li.footer-navigation__item>*:after,footer a:not(.lp-socials__link):after{width:0%;background:rgb(from currentColor r g b / 100%)!important;}a.lp-link:hover,span.lp-link:hover,a.link:not(.sidebar__nav-link):not(.sub-nav-heading):not(.sub-nav-link):not(.main-nav):hover,span.link:hover,a[href="/terms-and-conditions"]:
31hover,footer li.footer-navigation__item>*:hover,footer a:not(.lp-socials__link):hover{-webkit-text-decoration:none!important;text-decoration:none!important;opacity:1;}a.lp-link:hover:after,span.lp-link:hover:after,a.link:not(.sidebar__nav-link):not(.sub-nav-heading):not(.sub-nav-link):not(.main-nav):hover:after,span.link:hover:after,a[href="/terms-and-conditions"]:hover:after,footer li.footer-navigation__item>*:hover:after,footer a:not(.lp-socials__link):hover:after{width:100%;}@media (max-width: 768px){a.lp-link,span.lp-link,a.link:not(.sidebar__nav-link):not(.sub-nav-heading):not(.sub-nav-link):not(.main-nav),span.link,a[href="/terms-and-conditions"],footer li.footer-navigation__item>*,footer a:not(.lp-socials__link){-webkit-text-decoration:underline!important;text-decoration:underline!important;text-decoration-color:currentColor!important;}a.lp-link:before,span.lp-link:before,a.link:not(.sidebar__nav-link):not(.sub-nav-heading):not(.sub-nav-link):not(.main-nav):before,span.link:before,a[href="/terms-and-conditions"]:before,footer li.footer-navigation__item>*:before,footer a:not(.lp-socials__link):before,a.lp-link:after,span.lp-link:after,a.link:not(.sidebar__nav-link):not(.sub-nav-heading):not(.sub-nav-link):not(.main-nav):after,span.link:after,a[href="/terms-and-conditions"]:after,footer li.footer-navigation__item>*:after,footer a:not(.lp-socials__link):after{display:none;}a.lp-link:hover,span.lp-link:hover,a.link:not(.sidebar__nav-link):not(.sub-nav-heading):not(.sub-nav-link):not(.main-nav):hover,span.link:hover,a[href="/terms-and-conditions"]:hover,footer li.footer-navigation__item>*:hover,footer a:not(.lp-socials__link):hover{color:var(--accent-dark);}}a[href="/terms-and-conditions"]{display:inline-block;}div.uf-cus-sel div.uf-cus-sel-menu div.uf-cus-sel-content,div.custom-select div.uf-cus-sel-menu div.uf-cus-sel-content,div.uf-cus-sel-menu div.uf-cus-sel-menu div.uf-cus-sel-content,div.uf-cus-sel div.custom-select-menu div.uf-cus-sel-content,div.custom-select div.custom-select-menu div.uf-cus-sel-content,div.uf-cus-sel-menu div.custom-select-menu div.uf-cus-sel-content,div.uf-cus-sel div.uf-cus-sel-content div.uf-cus-sel-content,div.custom-select div.uf-cus-sel-content div.uf-cus-sel-content,div.uf-cus-sel-menu div.uf-cus-sel-content div.uf-cus-sel-content,div.uf-cus-sel div.uf-cus-sel-menu div.custom-select-content,div.custom-select div.uf-cus-sel-menu div.custom-select-content,div.uf-cus-sel-menu div.uf-cus-sel-menu div.custom-select-content,div.uf-cus-sel div.custom-select-menu div.custom-select-content,div.custom-select div.custom-select-menu div.custom-select-content,div.uf-cus-sel-menu div.custom-select-menu div.custom-select-content,div.uf-cus-sel div.uf-cus-sel-content div.custom-select-content,div.custom-select div.uf-cus-sel-content div.custom-select-content,div.uf-cus-sel-menu div.uf-cus-sel-content div.custom-select-content{background:var(--primary-light)!important;border-color:var(--border-dark-inactive)!important;}div.uf-cus-sel div.uf-cus-sel-menu div.uf-cus-sel-content .uf-cus-sel-item,div.custom-select div.uf-cus-sel-menu div.uf-cus-sel-content .uf-cus-sel-item,div.uf-cus-sel-menu div.uf-cus-sel-menu div.uf-cus-sel-content .uf-cus-sel-item,div.uf-cus-sel div.custom-select-menu div.uf-cus-sel-content .uf-cus-sel-item,div.custom-select div.custom-select-menu div.uf-cus-sel-content .uf-cus-sel-item,div.uf-cus-sel-menu div.custom-select-menu div.uf-cus-sel-content .uf-cus-sel-item,div.uf-cus-sel div.uf-cus-sel-content div.uf-cus-sel-content .uf-cus-sel-item,div.custom-select div.uf-cus-sel-content div.uf-cus-sel-content .uf-cus-sel-item,div.uf-cus-sel-menu div.uf-cus-sel-content div.uf-cus-sel-content .uf-cus-sel-item,div.uf-cus-sel div.uf-cus-sel-menu div.custom-select-content .uf-cus-sel-item,div.custom-select div.uf-cus-sel-menu div.custom-select-content .uf-cus-sel-item,div.uf-cus-sel-menu div.uf-cus-sel-menu div.custom-select-content .uf-cus-sel-item,div.uf-cus-sel div.custom-select-menu div.custom-select-content .uf-cus-sel-item,div.custom-select div.custom-select-menu div.custom-select-content .uf-cus-sel-item,div.uf-cus-sel-menu div.custom-select-menu div.custom-select-content .uf-cus-sel-item,div.uf-cus-sel div.uf-cus-sel-content div.custom-select-content .uf-cus-sel-item,div.custom-select div.uf-cus-sel-content div.custom-select-content .uf-cus-sel-item,div.uf-cus-sel-menu div.uf-cus-sel-content div.custom-select-content .uf-cus-sel-item,div.uf-cus-sel div.uf-cus-sel-menu div.uf-cus-sel-content button.uf-cus-sel-item,div.custom-select div.uf-cus-sel-menu div.uf-cus-sel-content button.uf-cus-sel-item,div.uf-cus-sel-menu div.uf-cus-sel-menu div.uf-cus-sel-content button.uf-cus-sel-item,div.uf-cus-sel div.custom-select-menu div.uf-cus-sel-content button.uf-cus-sel-item,div.custom-select div.custom-select-menu div.uf-cus-sel-content button.uf-cus-sel-item,div.uf-cus-sel-menu div.custom-select-menu div.uf-cus-sel-content button.uf-cus-sel-item,div.uf-cus-sel div.uf-cus-sel-content div.uf-cus-sel-content button.uf-cus-sel-item,div.custom-select div.uf-cus-sel-content div.uf-cus-sel-content button.uf-cus-sel-item,div.uf-cus-sel-menu div.uf-cus-sel-content div.uf-cus-sel-content button.uf-cus-sel-item,div.uf-cus-sel div.uf-cus-sel-menu div.custom-select-content button.uf-cus-sel-item,div.custom-select div.uf-cus-sel-menu div.custom-select-content button.uf-cus-sel-item,div.uf-cus-sel-menu div.uf-cus-sel-menu div.custom-select-content button.uf-cus-sel-item,div.uf-cus-sel div.custom-select-menu div.custom-select-content button.uf-cus-sel-item,div.custom-select div.custom-select-menu div.custom-select-content button.uf-cus-sel-item,div.uf-cus-sel-menu div.custom-select-menu div.custom-select-content button.uf-cus-sel-item,div.uf-cus-sel div.uf-cus-sel-content div.custom-select-content button.uf-cus-sel-item,div.custom-select div.uf-cus-sel-content div.custom-select-content button.uf-cus-sel-item,div.uf-cus-sel-menu div.uf-cus-sel-content div.custom-select-content button.uf-cus-sel-item,div.uf-cus-sel div.uf-cus-sel-menu div.uf-cus-sel-content button.custom-select-item,div.custom-select div.uf-cus-sel-menu div.uf-cus-sel-content button.custom-select-item,div.uf-cus-sel-menu div.uf-cus-sel-menu div.uf-cus-sel-content button.custom-select-item,div.uf-cus-sel div.custom-select-menu div.uf-cus-sel-content button.custom-select-item,div.custom-select div.custom-select-menu div.uf-cus-sel-content button.custom-select-item,div.uf-cus-sel-menu div.custom-select-menu div.uf-cus-sel-content button.custom-select-item,div.uf-cus-sel div.uf-cus-sel-content div.uf-cus-sel-content button.custom-select-item,div.custom-select div.uf-cus-sel-content div.uf-cus-sel-content button.custom-select-item,div.uf-cus-sel-menu div.uf-cus-sel-content div.uf-cus-sel-content button.custom-select-item,div.uf-cus-sel div.uf-cus-sel-menu div.custom-select-content button.custom-select-item,div.custom-select div.uf-cus-sel-menu div.custom-select-content button.custom-select-item,div.uf-cus-sel-menu div.uf-cus-sel-menu div.custom-select-content button.custom-select-item,div.uf-cus-sel div.custom-select-menu div.custom-select-content button.custom-select-item,div.custom-select div.custom-select-menu div.custom-select-content button.custom-select-item,div.uf-cus-sel-menu div.custom-select-menu div.custom-select-content button.custom-select-item,div.uf-cus-sel div.uf-cus-sel-content div.custom-select-content button.custom-select-item,div.custom-select div.uf-cus-sel-content div.custom-select-content button.custom-select-item,div.uf-cus-sel-menu div.uf-cus-sel-content div.custom-select-content button.custom-select-item{background:var(--primary-light)!important;color:var(--typography-dark)!important;}div.uf-cus-sel div.uf-cus-sel-menu div.uf-cus-sel-content .uf-cus-sel-item:hover,div.custom-select div.uf-cus-sel-menu div.uf-cus-sel-content .uf-cus-sel-item:hover,div.uf-cus-sel-menu div.uf-cus-sel-menu div.uf-cus-sel-content .uf-cus-sel-item:hover,div.uf-cus-sel div.custom-select-menu div.uf-cus-sel-content .uf-cus-sel-item:hover,div.custom-select div.custom-select-menu div.uf-cus-sel-content .uf-cus-sel-item:hover,div.uf-cus-sel-menu div.custom-select-menu div.uf-cus-sel-content .uf-cus-sel-item:hover,div.uf-cus-sel div.uf-cus-sel-content div.uf-cus-sel-content .uf-cus-sel-item:hover,div.custom-select div.uf-cus-sel-content div.uf-cus-sel-content .uf-cus-sel-item:hover,div.uf-cus-sel-menu div.uf-cus-sel-content div.uf-cus-sel-content .uf-cus-sel-item:hover,div.uf-cus-sel div.uf-cus-sel-menu div.custom-select-content .uf-cus-sel-item:hover,div.custom-select div.uf-cus-sel-menu div.custom-select-content .uf-cus-sel-item:hover,div.uf-cus-sel-menu div.uf-cus-sel-menu div.custom-select-content .uf-cus-sel-item:hover,div.uf-cus-sel div.custom-select-menu div.custom-select-content .uf-cus-sel-item:hover,div.custom-select div.custom-select-menu div.custom-select-content .uf-cus-sel-item:hover,div.uf-cus-sel-menu div.custom-select-menu div.custom-select-content .uf-cus-sel-item:hover,div.uf-cus-sel div.uf-cus-sel-content div.custom-select-content .uf-cus-sel-item:hover,div.custom-select div.uf-cus-sel-content div.custom-select-content .uf-cus-sel-item:hover,div.uf-cus-sel-menu div.uf-cus-sel-content div.custom-select-content .uf-cus-sel-item:hover,div.uf-cus-sel div.uf-cus-sel-menu div.uf-cus-sel-content button.uf-cus-sel-item:hover,div.custom-select div.uf-cus-sel-menu div.uf-cus-sel-content button.uf-cus-sel-item:hover,div.uf-cus-sel-menu div.uf-cus-sel-menu div.uf-cus-sel-content button.uf-cus-sel-item:hover,div.uf-cus-sel div.custom-select-menu div.uf-cus-sel-content button.uf-cus-sel-item:hover,div.custom-select div.custom-select-menu div.uf-cus-sel-content button.uf-cus-sel-item:hover,div.uf-cus-sel-menu div.custom-select-menu div.uf-cus-sel-content button.uf-cus-sel-item:hover,div.uf-cus-sel div.uf-cus-sel-content div.uf-cus-sel-content button.uf-cus-sel-item:hover,div.custom-select div.uf-cus-sel-content div.uf-cus-sel-content button.uf-cus-sel-item:hover,div.uf-cus-sel-menu div.uf-cus-sel-content div.uf-cus-sel-content button.uf-cus-sel-item:hover,div.uf-cus-sel div.uf-cus-sel-menu div.custom-select-content button.uf-cus-sel-item:hover,div.custom-select div.uf-cus-sel-menu div.custom-select-content button.uf-cus-sel-item:hover,div.uf-cus-sel-menu div.uf-cus-sel-menu div.custom-select-content button.uf-cus-sel-item:hover,div.uf-cus-sel div.custom-select-menu div.custom-select-content button.uf-cus-sel-item:hover,div.custom-select div.custom-select-menu div.custom-select-content button.uf-cus-sel-item:hover,div.uf-cus-sel-menu div.custom-select-menu div.custom-select-content button.uf-cus-sel-item:hover,div.uf-cus-sel div.uf-cus-sel-content div.custom-select-content button.uf-cus-sel-item:hover,div.custom-select div.uf-cus-sel-content div.custom-select-content button.uf-cus-sel-item:hover,div.uf-cus-sel-menu div.uf-cus-sel-content div.custom-select-content button.uf-cus-sel-item:hover,div.uf-cus-sel div.uf-cus-sel-menu div.uf-cus-sel-content button.custom-select-item:hover,div.custom-select div.uf-cus-sel-menu div.uf-cus-sel-content button.custom-select-item:hover,div.uf-cus-sel-menu div.uf-cus-sel-menu div.uf-cus-sel-content button.custom-select-item:hover,div.uf-cus-sel div.custom-select-menu div.uf-cus-sel-content button.custom-select-item:hover,div.custom-select div.custom-select-menu div.uf-cus-sel-content button.custom-select-item:hover,div.uf-cus-sel-menu div.custom-select-menu div.uf-cus-sel-content button.custom-select-item:hover,div.uf-cus-sel div.uf-cus-sel-content div.uf-cus-sel-content button.custom-select-item:hover,div.custom-select div.uf-cus-sel-content div.uf-cus-sel-content button.custom-select-item:hover,div.uf-cus-sel-menu div.uf-cus-sel-content div.uf-cus-sel-content button.custom-select-item:hover,div.uf-cus-sel div.uf-cus-sel-menu div.custom-select-content button.custom-select-item:hover,div.custom-select div.uf-cus-sel-menu div.custom-select-content button.custom-select-item:hover,div.uf-cus-sel-menu div.uf-cus-sel-menu div.custom-select-content button.custom-select-item:hover,div.uf-cus-sel div.custom-select-menu div.custom-select-content button.custom-select-item:hover,div.custom-select div.custom-select-menu div.custom-select-content button.custom-select-item:hover,div.uf-cus-sel-menu div.custom-select-menu div.custom-select-content button.custom-select-item:hover,div.uf-cus-sel div.uf-cus-sel-content div.custom-select-content button.custom-select-item:hover,div.custom-select div.uf-cus-sel-content div.custom-select-content button.custom-select-item:hover,div.uf-cus-sel-menu div.uf-cus-sel-content div.custom-select-content button.custom-select-item:hover,div.uf-cus-sel div.uf-cus-sel-menu div.uf-cus-sel-content .uf-cus-sel-item:is(.is-selected),div.custom-select div.uf-cus-sel-menu div.uf-cus-sel-content .uf-cus-sel-item:is(.is-selected),div.uf-cus-sel-menu div.uf-cus-sel-menu div.uf-cus-sel-content .uf-cus-sel-item:is(.is-selected),div.uf-cus-sel div.custom-select-menu div.uf-cus-sel-content .uf-cus-sel-item:is(.is-selected),div.custom-select div.custom-select-menu div.uf-cus-sel-content .uf-cus-sel-item:is(.is-selected),div.uf-cus-sel-menu div.custom-select-menu div.uf-cus-sel-content .uf-cus-sel-item:is(.is-selected),div.uf-cus-sel div.uf-cus-sel-content div.uf-cus-sel-content .uf-cus-sel-item:is(.is-selected),div.custom-select div.uf-cus-sel-content div.uf-cus-sel-content .uf-cus-sel-item:is(.is-selected),div.uf-cus-sel-menu div.uf-cus-sel-content div.uf-cus-sel-content .uf-cus-sel-item:is(.is-selected),div.uf-cus-sel div.uf-cus-sel-menu div.custom-select-content .uf-cus-sel-item:is(.is-selected),div.custom-select div.uf-cus-sel-menu div.custom-select-content .uf-cus-sel-item:is(.is-selected),div.uf-cus-sel-menu div.uf-cus-sel-menu div.custom-select-content .uf-cus-sel-item:is(.is-selected),div.uf-cus-sel div.custom-select-menu div.custom-select-content .uf-cus-sel-item:is(.is-selected),div.custom-select div.custom-select-menu div.custom-select-content .uf-cus-sel-item:is(.is-selected),div.uf-cus-sel-menu div.custom-select-menu div.custom-select-content .uf-cus-sel-item:is(.is-selected),div.uf-cus-sel div.uf-cus-sel-content div.custom-select-content .uf-cus-sel-item:is(.is-selected),div.custom-select div.uf-cus-sel-content div.custom-select-content .uf-cus-sel-item:is(.is-selected),div.uf-cus-sel-menu div.uf-cus-sel-content div.custom-select-content .uf-cus-sel-item:is(.is-selected),div.uf-cus-sel div.uf-cus-sel-menu div.uf-cus-sel-content button.uf-cus-sel-item:is(.is-selected),div.custom-select div.uf-cus-sel-menu div.uf-cus-sel-content button.uf-cus-sel-item:is(.is-selected),div.uf-cus-sel-menu div.uf-cus-sel-menu div.uf-cus-sel-content button.uf-cus-sel-item:is(.is-selected),div.uf-cus-sel div.custom-select-menu div.uf-cus-sel-content button.uf-cus-sel-item:is(.is-selected),div.custom-select div.custom-select-menu div.uf-cus-sel-content button.uf-cus-sel-item:is(.is-selected),div.uf-cus-sel-menu div.custom-select-menu div.uf-cus-sel-content button.uf-cus-sel-item:is(.is-selected),div.uf-cus-sel div.uf-cus-sel-content div.uf-cus-sel-content button.uf-cus-sel-item:is(.is-selected),div.custom-select div.uf-cus-sel-content div.uf-cus-sel-content button.uf-cus-sel-item:is(.is-selected),div.uf-cus-sel-menu div.uf-cus-sel-content div.uf-cus-sel-content button.uf-cus-sel-item:is(.is-selected),div.uf-cus-sel div.uf-cus-sel-menu div.custom-select-content button.uf-cus-sel-item:is(.is-selected),div.custom-select div.uf-cus-sel-menu div.custom-select-content button.uf-cus-sel-item:is(.is-selected),div.uf-cus-sel-menu div.uf-cus-sel-menu div.custom-select-content button.uf-cus-sel-item:is(.is-selected),div.uf-cus-sel div.custom-select-menu div.custom-select-content button.uf-cus-sel-item:is(.is-selected),div.custom-select div.custom-select-menu div.custom-select-content button.uf-cus-sel-item:is(.is-selected),div.uf-cus-sel-menu div.custom-select-menu div.custom-select-content button.uf-cus-sel-item:is(.is-selected),div.uf-cus-sel div.uf-cus-sel-content div.custom-select-content button.uf-cus-sel-item:is(.is-selected),div.custom-select div.uf-cus-sel-content div.custom-select-content button.uf-cus-sel-item:is(.is-selected),div.uf-cus-sel-menu div.uf-cus-sel-content div.custom-select-content button.uf-cus-sel-item:is(.is-selected),div.uf-cus-sel div.uf-cus-sel-menu div
31.uf-cus-sel-content button.custom-select-item:is(.is-selected),div.custom-select div.uf-cus-sel-menu div.uf-cus-sel-content button.custom-select-item:is(.is-selected),div.uf-cus-sel-menu div.uf-cus-sel-menu div.uf-cus-sel-content button.custom-select-item:is(.is-selected),div.uf-cus-sel div.custom-select-menu div.uf-cus-sel-content button.custom-select-item:is(.is-selected),div.custom-select div.custom-select-menu div.uf-cus-sel-content button.custom-select-item:is(.is-selected),div.uf-cus-sel-menu div.custom-select-menu div.uf-cus-sel-content button.custom-select-item:is(.is-selected),div.uf-cus-sel div.uf-cus-sel-content div.uf-cus-sel-content button.custom-select-item:is(.is-selected),div.custom-select div.uf-cus-sel-content div.uf-cus-sel-content button.custom-select-item:is(.is-selected),div.uf-cus-sel-menu div.uf-cus-sel-content div.uf-cus-sel-content button.custom-select-item:is(.is-selected),div.uf-cus-sel div.uf-cus-sel-menu div.custom-select-content button.custom-select-item:is(.is-selected),div.custom-select div.uf-cus-sel-menu div.custom-select-content button.custom-select-item:is(.is-selected),div.uf-cus-sel-menu div.uf-cus-sel-menu div.custom-select-content button.custom-select-item:is(.is-selected),div.uf-cus-sel div.custom-select-menu div.custom-select-content button.custom-select-item:is(.is-selected),div.custom-select div.custom-select-menu div.custom-select-content button.custom-select-item:is(.is-selected),div.uf-cus-sel-menu div.custom-select-menu div.custom-select-content button.custom-select-item:is(.is-selected),div.uf-cus-sel div.uf-cus-sel-content div.custom-select-content button.custom-select-item:is(.is-selected),div.custom-select div.uf-cus-sel-content div.custom-select-content button.custom-select-item:is(.is-selected),div.uf-cus-sel-menu div.uf-cus-sel-content div.custom-select-content button.custom-select-item:is(.is-selected){background:var(--accent-dark)!important;color:var(--typography-light)!important;}.lp-pagination.pagination-container .paginationjs .paginationjs-pages li.paginationjs-next a,.lp-pagination.pagination-container .paginationjs .paginationjs-pages li.paginationjs-prev a{width:40px;background:transparent!important;border:2px solid transparent!important;color:var(--accent-dark)!important;}.lp-pagination.pagination-container .paginationjs .paginationjs-pages li.paginationjs-next a:focus,.lp-pagination.pagination-container .paginationjs .paginationjs-pages li.paginationjs-prev a:focus,.lp-pagination.pagination-container .paginationjs .paginationjs-pages li.paginationjs-next a:active,.lp-pagination.pagination-container .paginationjs .paginationjs-pages li.paginationjs-prev a:active,.lp-pagination.pagination-container .paginationjs .paginationjs-pages li.paginationjs-next a:hover,.lp-pagination.pagination-container .paginationjs .paginationjs-pages li.paginationjs-prev a:hover{background:var(--accent-dark)!important;border:2px solid var(--accent-dark)!important;color:var(--accent-light)!important;}.lp-pagination.pagination-container .paginationjs .paginationjs-pages li.paginationjs-next:is(.paginationjs-next) a:after,.lp-pagination.pagination-container .paginationjs .paginationjs-pages li.paginationjs-prev:is(.paginationjs-next) a:after{left:45%;}.lp-pagination.pagination-container .paginationjs .paginationjs-pages li.paginationjs-next:is(.paginationjs-prev) a:after,.lp-pagination.pagination-container .paginationjs .paginationjs-pages li.paginationjs-prev:is(.paginationjs-prev) a:after{left:50%;}nav#global-navbar a.logo{height:auto;width:100%;max-width:300px;}@media (max-width: 768px){nav#global-navbar a.logo{max-width:240px;width:auto;}}nav#global-navbar a.logo img{width:100%;height:100%;}nav#global-navbar:not(.scroll) div.header__logo a.logo img.logo__img:is(.dark){display:none;}nav#global-navbar:not(.scroll) div.header__logo a.logo img.logo__img:is(.light){display:block;}nav#global-navbar:is(.scroll) div.header__logo a.logo img.logo__img:is(.dark){display:block;}nav#global-navbar:is(.scroll) div.header__logo a.logo img.logo__img:is(.light){display:none;}nav#global-navbar{-webkit-transform:unset!important;-moz-transform:unset!important;-ms-transform:unset!important;transform:unset!important;}nav#global-navbar div.header{-webkit-transition:0.4s;transition:0.4s;}
31nav#global-navbar div.header__left,nav#global-navbar div.header__right{gap:24px;}nav#global-navbar div.header__left ul.navigation,nav#global-navbar div.header__right ul.navigation{gap:24px;}@media (max-width: 1366px){nav#global-navbar div.header__left ul.navigation,nav#global-navbar div.header__right ul.navigation{gap:32px;}}nav#global-navbar div.header__left ul.navigation li.navigation__item,nav#global-navbar div.header__right ul.navigation li.navigation__item{padding-left:0px;text-transform:uppercase;}nav#global-navbar div.header__left ul.navigation li.navigation__item:is(.sub-nav-container) a.navigation__link,nav#global-navbar div.header__right ul.navigation li.navigation__item:is(.sub-nav-container) a.navigation__link{padding-left:0px;}nav#global-navbar div.header__left ul.navigation li.navigation__item:not(.sub-nav-container) a.navigation__link,nav#global-navbar div.header__right ul.navigation li.navigation__item:not(.sub-nav-container) a.navigation__link{padding-left:0px;padding-right:0px;}nav#global-navbar div.header__left ul.navigation li.navigation__item a.navigation__link,nav#global-navbar div.header__right ul.navigation li.navigation__item a.navigation__link{font-size:var(--global-body-font-size);padding-bottom:8px;padding-top:8px;}@media (max-width: 1366px){nav#global-navbar div.header__left ul.navigation li.navigation__item a.navigation__link,nav#global-navbar div.header__right ul.navigation li.navigation__item a.navigation__link{font-size:14px;}}nav#global-navbar div.header__left ul.navigation li.navigation__item ul.sub-nav li.sub-nav__item a.sub-nav__link,nav#global-navbar div.header__right ul.navigation li.navigation__item ul.sub-nav li.sub-nav__item a.sub-nav__link{max-width:unset;}nav#global-navbar div.header__left button.hamburger-component,nav#global-navbar div.header__right button.hamburger-component{margin-left:0px;}nav#global-navbar ul.sub-nav li.sub-nav__item a.sub-nav__link{color:var(--typography-dark);background:var(--primary-light);-webkit-transition:0.25s;transition:0.25s;font-size:var(--global-body-font-size);}nav#global-navbar ul.sub-nav li.sub-nav__item a.sub-nav__link:hover{color:var(--typography-light);background:var(--accent-dark);}@media (max-width: 769px){nav#global-navbar div.header div.header__container{padding-left:unset;padding-right:unset;}}nav#global-navbar:not(.scroll){background:transparent!important;}nav#global-navbar:not(.scroll) div.header{padding:16px;}nav#global-navbar:not(.scroll) div.header li.navigation__item:after{border-color:var(--typography-light);}nav#global-navbar:not(.scroll) div.header a.navigation__link{color:var(--typography-light);}nav#global-navbar:not(.scroll) div.header a.navigation__link:after{background:var(--typography-light);}nav#global-navbar:not(.scroll) div.header a.navigation__link:hover{color:var(--accent-light);}nav#global-navbar:not(.scroll) div.header a.navigation__link:hover:after{background:var(--accent-light);}nav#global-navbar:not(.scroll) div.header button.hamburger-component circle{fill:var(--typography-light);}nav#global-navbar:not(.scroll) div.header button.hamburger-component rect{fill:var(--accent-dark);}nav#global-navbar:not(.scroll) div.header button.hamburger-component:hover circle{fill:var(--accent-dark);}nav#global-navbar:not(.scroll) div.header button.hamburger-component:hover rect{fill:var(--accent-light);}nav#global-navbar:is(.scroll){background:var(--primary-dark)!important;}@media (min-width: 769px){nav#global-navbar:is(.scroll) div.header{padding:24px 24px;}}@media (max-width: 768px){nav#global-navbar:is(.scroll) div.header{padding:16px;}}nav#global-navbar:is(.scroll) div.header li.navigation__item:after{border-color:var(--typography-dark);}nav#global-navbar:is(.scroll) div.header li.navigation__item:hover:after{border-color:var(--accent-dark);}nav#global-navbar:is(.scroll) div.header a.navigation__link{color:var(--typography-dark);}nav#global-navbar:is(.scroll) div.header a.navigation__link:after{background:var(--typography-dark);}nav#global-navbar:is(.scroll) div.header a.navigation__link:hover{color:var(--accent-dark);}nav#global-navbar:is(.scroll) div.header a.navigation__link:hover:after{background:var(--accent-dark);}nav#global-navbar:is(.scroll) div.header button.hamburger-component circle{fill:var
31(--accent-dark);-webkit-transition:0.4s;transition:0.4s;}nav#global-navbar:is(.scroll) div.header button.hamburger-component rect{fill:var(--accent-light);}nav#global-navbar:is(.scroll) div.header button.hamburger-component:hover circle{fill:var(--accent-dark);}nav#global-navbar:is(.scroll) div.header button.hamburger-component:hover rect{fill:var(--accent-light);}body:is(.page-property-details) nav#global-navbar:not(.scroll) div.header__logo a.logo img.logo__img:is(.dark){display:block;}body:is(.page-property-details) nav#global-navbar:not(.scroll) div.header__logo a.logo img.logo__img:is(.light){display:none;}body:is(.page-property-details) nav#global-navbar:not(.scroll){background:transparent!important;}body:is(.page-property-details) nav#global-navbar:not(.scroll) div.header li.navigation__item:after{border-color:var(--typography-dark);}body:is(.page-property-details) nav#global-navbar:not(.scroll) div.header a.navigation__link{color:var(--typography-dark);}body:is(.page-property-details) nav#global-navbar:not(.scroll) div.header a.navigation__link:after{background:var(--typography-dark);}body:is(.page-property-details) nav#global-navbar:not(.scroll) div.header a.navigation__link:hover{color:var(--accent-dark);}body:is(.page-property-details) nav#global-navbar:not(.scroll) div.header a.navigation__link:hover:after{background:var(--accent-dark);}body:is(.page-property-details) nav#global-navbar:not(.scroll) div.header button.hamburger-component circle{fill:var(--accent-dark);-webkit-transition:0.4s;transition:0.4s;}body:is(.page-property-details) nav#global-navbar:not(.scroll) div.header button.hamburger-component rect{fill:var(--accent-light);}body:is(.page-property-details) nav#global-navbar:not(.scroll) div.header button.hamburger-component:hover circle{fill:var(--accent-dark);}body:is(.page-property-details) nav#global-navbar:not(.scroll) div.header button.hamburger-component:hover rect{fill:var(--accent-light);}footer#global-footer div.site-footer__disclaimer{opacity:1;}footer#global-footer div.site-footer__disclaimer:after{background-color:var(--accent-dark);opacity:1;}@media (min-width: 1025px){footer#global-footer div.site-footer__disclaimer{padding-top:32px;padding-bottom:32px;}}footer#global-footer div.site-footer__col-content{opacity:1;}footer#global-footer div.site-footer__logo-bar{padding-bottom:48px;}footer#global-footer div.site-footer__logo-bar div.site-footer__logo-container img{max-height:96px;}footer#global-footer ul.lp-socials{gap:16px;}footer#global-footer ul.lp-socials>li{margin:0px;}footer#global-footer div.site-footer__col h5.site-footer__col-title{color:var(--accent-dark);}footer#global-footer ul.footer-navigation a.footer-navigation__link,footer#global-footer ul.footer-navigation button.footer-navigation__link{text-transform:var(--text-case);}footer#global-footer a:not(.socials__item),footer#global-footer button.footer-navigation__link{color:var(--typography-dark);}footer#global-footer a:not(.socials__item):hover,footer#global-footer button.footer-navigation__link:hover{color:var(--accent-dark);}@media (max-width: 768px){footer#global-footer div.site-footer__logo-bar{-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;}}footer#global-footer div.site-footer__logo-bar div.site-footer__logo-container{max-width:300px;}@media (max-width: 768px){footer#global-footer div.site-footer__logo-bar div.site-footer__logo-container{margin:unset;}}footer#global-footer div.site-footer__logo-bar div.site-footer__logo-container img{width:100%;}@media (max-width: 768px){footer#global-footer div.site-footer__logo-bar{-webkit-box-pack:center;-ms-flex-pack:center;-webkit-justify-content:center;justify-content:center;}footer#global-footer div.site-footer__info{text-align:center;}footer#global-footer div.site-footer__info>*{width:100%;padding-right:0px;}footer#global-footer div.site-footer__info div.site-footer__navigation-wrap{-webkit-align-items:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;}footer#global-footer div.site-footer__info div.site-footer__navigation-wrap ul.footer-navigation{padding-right:0px;}footer#global-footer div.site-footer__info div.site-footer__navigation-wrap ul.lp-socials{margin-right:0px;-webkit-box-pack:center;-ms-flex-pack:center;
31-webkit-justify-content:center;justify-content:center;}footer#global-footer div.site-footer__disclaimer{text-align:center;}footer#global-footer div.site-footer__copy{text-align:center;}footer#global-footer div.site-footer__copy div.site-footer__logo-container img{width:100%;object-position:center;max-width:unset;}footer#global-footer div.site-footer__copy>*{width:100%;}}div.sidemenu button.sidemenu__toggle{text-transform:var(--text-case);-webkit-transition:0.4s;transition:0.4s;}div.sidemenu button.sidemenu__toggle:hover{opacity:0.25;}div.sidemenu a.navigation__link,div.sidemenu a.sub-nav__link{text-transform:uppercase;-webkit-transition:0.25s;transition:0.25s;}div.sidemenu a.navigation__link:hover,div.sidemenu a.sub-nav__link:hover{color:var(--accent-dark)!important;}div.sidemenu a.navigation__link svg.arrow,div.sidemenu a.sub-nav__link svg.arrow{-webkit-transition:0.2s;transition:0.2s;}@media (max-width: 768px){div#global-sidemenu div.sidemenu__socials{width:100%;}}@media (max-width: 768px){div#global-sidemenu div.sidemenu__socials ul.lp-socials{-webkit-box-pack:center;-ms-flex-pack:center;-webkit-justify-content:center;justify-content:center;gap:16px;}}@media (max-width: 768px){div#global-sidemenu div.sidemenu__socials ul.lp-socials li{margin:0px;}}div#modal-global-contact-us p.lp-pre-line:after{background:var(--accent-dark);}div#modal-global-contact-us h2{color:var(--accent-dark);}div#global-mobile-contact{z-index:102;}.contact-form__content .lp-btn{background:var(--accent-dark)!important;color:var(--typography-light)!important;border-color:var(--accent-dark)!important;}.contact-form__content .lp-btn:hover{background:transparent!important;color:var(--accent-dark)!important;}div#global-mobile-contact a.search-btn{background:var(--accent-dark);}div#global-mobile-contact a.search-btn:before{color:var(--accent-light);}#global-mobile-contact .pop-up .lp-disclaimer a{color:var(--typography-dark)!important;}#global-mobile-contact .pop-up .lp-disclaimer a:hover{color:var(--accent-dark)!important;}div#global-mobile-contact div.contact-form__container h3.lp-h3{color:var(--accent-dark);}#global-mobile-contact .pop-up input,#global-mobile-contact .pop-up textarea{border-color:var(--border-dark-inactive);-webkit-text-fill-color:var(--typography-dark);}#global-mobile-contact .pop-up input:focus,#global-mobile-contact .pop-up textarea:focus{border-color:var(--border-dark-active);}#global-mobile-contact .pop-up .close-btn{color:var(--accent-dark);}@media (max-width: 768px){div#global-mobile-contact div.pop-up{height:100%;border-radius:0px;}}div#global-mobile-contact div.pop-up div.lp-container{padding:64px;}@media only screen and (max-width: 1366px){div#global-mobile-contact div.pop-up div.lp-container{padding:32px;}}@media only screen and (max-width: 768px){div#global-mobile-contact div.pop-up div.lp-container{padding:24px 24px;}}#global-mobile-contact .pop-up{box-shadow:0px 8px 16px rgba(0, 0, 0, 0.15);}</style> 32 <style id="wow-hide-elements">.wow{visibility:hidden!important}</style> 33 <style id="page-css"> 34 #section-9c8b971e-fc4a-4d61-8e47-06eb416e4be9{color:#ffffff;--fontColor:#ffffff;--fontColor_H:0;--fontColor_S:0%;--fontColor_L:100%;--fontColor_A:1;--fontColor_darkenDir:-1;--bgColor:#000000;--bgColor_H:0;--bgColor_S:0%;--bgColor_L:0%;--bgColor_A:1;--bgColor_darkenDir:1;}#section-9c8b971e-fc4a-4d61-8e47-06eb416e4be9 div.lp-container div.lp-title-group{max-width:unset;}#section-9c8b971e-fc4a-4d61-8e47-06eb416e4be9 div.lp-container div.lp-title-group h1.opening-title{max-width:1400px;}@media (max-width: 1280px) and (min-width: 1025px){#section-9c8b971e-fc4a-4d61-8e47-06eb416e4be9 div.lp-container div.lp-title-group h1.opening-title{font-size:52px;}}#section-9c8b971e-fc4a-4d61-8e47-06eb416e4be9 div.lp-container div.lp-title-group div.opening-description{max-width:900px;font-weight:normal;}#section-9c8b971e-fc4a-4d61-8e47-06eb416e4be9 div.lp-container div.lp-title-group div.opening-description p,#section-9c8b971e-fc4a-4d61-8e47-06eb416e4be9 div.lp-container div.lp-title-group div.opening-description div{font-family:var(--global-primary-font-family);font-size:var(--global-body-font-size);}#section-9c8b971e-fc4a-4d61-8e47-06eb416e4be9 div.lp-container div.lp-title-group h1.opening-title,#section-9c8b971e-fc4a-4d61-8e47-06eb416e4be9 div.lp-container div.lp-title-group div.opening-description{margin-left:auto;margin-right:auto;}#section-9c8b971e-fc4a-4d61-8e47-06eb416e4be9 div.opening-description>*{font-family:var(--global-primary-font-family);font-weight:normal;font-size:21px;text-transform:initial!important;}#section-9c8b971e-fc4a-4d61-8e47-06eb416e4be9 div.opening-with-search div.search-results{background:rgb(from var(--accent-dark) r g b / 90%);}#section-9c8b971e-fc4a-4d61-8e47-06eb416e4be9 div.opening-with-search div.search-results a{color:var(--typography-light);}#section-9c8b971e-fc4a-4d61-8e47-06eb416e4be9{overflow:visible;z-index:1;}#section-9c8b971e-fc4a-4d61-8e47-06eb416e4be9>.video-wrapper-extended>.full-screen-video-component>.video-wrapper::before{content:'';position:absolute;width:100%;height:100%;top:0;left:0;background-color:#000;z-index:-1000;}#section-9c8b971e-fc4a-4d61-8e47-06eb416e4be9>.video-wrapper-extended>.full-screen-video-component>.video-wrapper iframe,#section-9c8b971e-fc4a-4d61-8e47-06eb416e4be9>.video-wrapper-extended>.full-screen-video-component>.video-wrapper video{pointer-events:none;-webkit-transition:opacity 2s cubic-bezier(0.835, 0, 0.19, 1);transition:opacity 2s cubic-bezier(0.835, 0, 0.19, 1);opacity:0;max-width:none;}#section-9c8b971e-fc4a-4d61-8e47-06eb416e4be9>.video-wrapper-extended>.full-screen-video-component>.video-wrapper.playing iframe,#section-9c8b971e-fc4a-4d61-8e47-06eb416e4be9>.video-wrapper-extended>.full-screen-video-component>.video-wrapper.playing video{opacity:1;}#section-9c8b971e-fc4a-4d61-8e47-06eb416e4be9>.video-wrapper-extended>.full-screen-video-component>.video-wrapper iframe{position:absolute;top:50%;left:50%;width:100%;height:100%;-webkit-transform:translate(-50%, -50%);-moz-transform:translate(-50%, -50%);-ms-transform:translate(-50%, -50%);transform:translate(-50%, -50%);pointer-events:none;}@media (min-aspect-ratio: 16/9){#section-9c8b971e-fc4a-4d61-8e47-06eb416e4be9>.video-wrapper-extended>.full-screen-video-component>.video-wrapper iframe{height:56.25vw;}}@media (max-aspect-ratio: 16/9){#section-9c8b971e-fc4a-4d61-8e47-06eb416e4be9>.video-wrapper-extended>.full-screen-video-component>.video-wrapper iframe{width:177.78vh;}}#section-9c8b971e-fc4a-4d61-8e47-06eb416e4be9 .video-wrapper{position:absolute;top:0;left:0;height:100%;width:100%;}#section-9c8b971e-fc4a-4d61-8e47-06eb416e4be9 .full-screen-video-component{position:relative;height:100%;overflow:hidden;}#section-9c8b971e-fc4a-4d61-8e47-06eb416e4be9 .opening-with-search{position:relative;--top-padding:170px;--bottom-padding:30px;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;width:100%;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;min-height:770px;padding-top:var(--top-padding);padding-bottom:var(--bottom-padding);}#section-9c8b971e-fc4a-4d61-8e47-06eb416e4be9 .opening-with-search.height-class-Full{min-height:100vh;}#section-9c8b971e-fc4a-4d61-8e47-06eb416e4be9 .opening-with-search.height-class-600px{min-height:600px;}#section-9c8b971e-fc4a-4d61-8e47-06eb416e4be9 .opening-with-search .bottom-sp
34acer{position:relative;z-index:0;-webkit-flex:1 0 0%;-ms-flex:1 0 0%;flex:1 0 0%;max-height:calc(var(--top-padding) - var(--bottom-padding));}#section-9c8b971e-fc4a-4d61-8e47-06eb416e4be9 .opening-with-search .center-block{-webkit-flex:1 0 auto;-ms-flex:1 0 auto;flex:1 0 auto;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-align-items:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;-webkit-justify-content:center;justify-content:center;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;text-align:center;position:relative;z-index:1;}#section-9c8b971e-fc4a-4d61-8e47-06eb416e4be9 .opening-with-search .opening-description{letter-spacing:.1em;margin-bottom:15px;}@media (max-width: 768px){#section-9c8b971e-fc4a-4d61-8e47-06eb416e4be9 .opening-with-search .opening-description a{white-space:pre-line;}}#section-9c8b971e-fc4a-4d61-8e47-06eb416e4be9 .opening-with-search .opening-title{margin-bottom:15px;}#section-9c8b971e-fc4a-4d61-8e47-06eb416e4be9 .opening-with-search .opening-button{margin:0 15px;}#section-9c8b971e-fc4a-4d61-8e47-06eb416e4be9 .opening-with-search .lp-title-group{max-width:100%;}#section-9c8b971e-fc4a-4d61-8e47-06eb416e4be9 .opening-with-search .search-form{margin-top:30px;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;width:100%;-webkit-box-pack:center;-ms-flex-pack:center;-webkit-justify-content:center;justify-content:center;-webkit-align-items:stretch;-webkit-box-align:stretch;-ms-flex-align:stretch;align-items:stretch;}#section-9c8b971e-fc4a-4d61-8e47-06eb416e4be9 .opening-with-search .search-form.with-filters{padding-top:32px;}#section-9c8b971e-fc4a-4d61-8e47-06eb416e4be9 .opening-with-search .search-button{margin:5px;border-radius:0;outline:none;background-color:#000;color:#fff;height:44px;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;padding-left:50px;padding-right:50px;-webkit-align-items:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;-webkit-justify-content:center;justify-content:center;cursor:pointer;-webkit-transition:all .3s ease;transition:all .3s ease;}#section-9c8b971e-fc4a-4d61-8e47-06eb416e4be9 .opening-with-search .search-button:hover{background-color:#fff;color:#000;}#section-9c8b971e-fc4a-4d61-8e47-06eb416e4be9 .opening-with-search .search-lease-tabs{position:absolute;left:0;bottom:100%;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;}#section-9c8b971e-fc4a-4d61-8e47-06eb416e4be9 .opening-with-search .search-lease-tab{padding:4px 20px;background-color:#000;color:#fff;cursor:pointer;}#section-9c8b971e-fc4a-4d61-8e47-06eb416e4be9 .opening-with-search .search-lease-tab.active{background-color:#fff;color:#000;}#section-9c8b971e-fc4a-4d61-8e47-06eb416e4be9 .opening-with-search .search-wrapper{margin:5px;letter-spacing:normal;width:100%;max-width:444px;position:relative;}#section-9c8b971e-fc4a-4d61-8e47-06eb416e4be9 .opening-with-search .search-input-container{width:100%;border:none;height:43px;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;padding:0 16px;-webkit-align-items:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;background-color:#fff;}#section-9c8b971e-fc4a-4d61-8e47-06eb416e4be9 .opening-with-search .search-input-container .search-icon{color:#c0c0c0;margin-right:10px;-webkit-flex:0 0 auto;-ms-flex:0 0 auto;flex:0 0 auto;font-size:20px;line-height:0;}#section-9c8b971e-fc4a-4d61-8e47-06eb416e4be9 .opening-with-search .search-input-container .search-input{color:#000;-webkit-flex:1 0 auto;-ms-flex:1 0 auto;flex:1 0 auto;width:50px;border:none;border-radius:0;outline:none;background:transparent;font-size:16px;padding:0;}#section-9c8b971e-fc4a-4d61-8e47-06eb416e4be9 .opening-with-search .search-results{line-height:normal;text-align:left;background-color:rgba(0, 0, 0, 0.8);position:absolute;width:100%;left:0;top:100%;border:none;overflow:hidden;-webkit-transition:height 0.3s ease;transition:height 0.3s ease;height:0;}#section-9c8b971e-fc4a-4d61-8e47-06eb416e4be9 .opening-with-search .results-container,#section-9c8b971e-fc4a-4d61-8e47-06eb416e4be9 .opening-with-search .predefined-links{padding:10px 0;}#section-9c8b971e-fc4a-4d61-8e47-06eb416e4be9 .opening-with-search .results-container a,#section-9c8b971e-fc4a-4d61-8e47-06eb416e4be9 .opening-with-search .predefined-links a{color:#b6b6b6;-webkit-transition:color 0.3s ease;transition:color 0.3s ease;}#section-9c8b971e-fc4a-4d61-8e47-06eb416e4be9 .opening-with-search .results-container a:hover,#section-9c8b971e-fc4a-4d61-8e47-06eb416e4be9 .opening-with-search .predefined-links a:hover{color:#fff;}#section-9c8b971e-fc4a-4d61-8e47-06eb416e4be9 .opening-with-search .predefined-links{max-height:400px;overflow:auto;}#section-9c8b971e-fc4a-4d61-8e47-06eb416e4be9 .opening-with-search .results-block{margin:15px 0;}#section-9c8b971e-fc4a-4d61-8e47-06eb416e4be9 .opening-with-search .results-block-title{margin:0;padding:0 48px;}#section-9c8b971e-fc4a-4d61-8e47-06eb416e4be9 .opening-with-search .results-block-title .results-title-label{margin:0 20px 10px 0;}#section-9c8b971e-fc4a-4d61-8e47-06eb416e4be9 .opening-with-search .results-block-title .search-near-container{margin-bottom:15px;}#section-9c8b971e-fc4a-4d61-8e47-06eb416e4be9 .opening-with-search .results-block:nth-child(n + 2) .search-near-container{display:none;}#section-9c8b971e-fc4a-4d61-8e47-06eb416e4be9 .opening-with-search .results-link{display:block;padding:4px 48px;width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}#section-9c8b971e-fc4a-4d61-8e47-06eb416e4be9 .opening-with-search .results-link.selected-link{color:#fff;background-color:rgba(255, 255, 255, 0.2);}#section-9c8b971e-fc4a-4d61-8e47-06eb416e4be9 .opening-with-search .search-in-progress{padding:30px 0;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-pack:center;-ms-flex-pack:center;
34-webkit-justify-content:center;justify-content:center;-webkit-align-items:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;font-size:30px;opacity:0.5;}#section-9c8b971e-fc4a-4d61-8e47-06eb416e4be9 .opening-with-search .not-visible{display:none!important;}#section-9c8b971e-fc4a-4d61-8e47-06eb416e4be9 .opening-with-search .nothing-found{width:100%;padding:20px;}@media (max-width: 560px){#section-9c8b971e-fc4a-4d61-8e47-06eb416e4be9 .opening-with-search .search-form{-webkit-box-flex-wrap:wrap;-webkit-flex-wrap:wrap;-ms-flex-wrap:wrap;flex-wrap:wrap;}#section-9c8b971e-fc4a-4d61-8e47-06eb416e4be9 .opening-with-search .search-button{width:100%;max-width:444px;}}#section-9c8b971e-fc4a-4d61-8e47-06eb416e4be9 .opening-with-search .search-near{cursor:pointer;}#section-9c8b971e-fc4a-4d61-8e47-06eb416e4be9 .loc-msg-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;-webkit-transition:background-color 0.3s ease;transition:background-color 0.3s ease;background-color:transparent;}#section-9c8b971e-fc4a-4d61-8e47-06eb416e4be9 .loc-msg-wrap{width:100%;height:100%;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-align-items:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;-webkit-justify-content:center;justify-content:center;padding:20px;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;-webkit-clip-path:polygon(50% 50%, 50% 50%, 50% 50%, 50% 50%);clip-path:polygon(50% 50%, 50% 50%, 50% 50%, 50% 50%);-webkit-transition:clip-path 0.4s ease;transition:clip-path 0.4s ease;}#section-9c8b971e-fc4a-4d61-8e47-06eb416e4be9 .loc-msg-modal{border-radius:20px;padding:10px 20px 20px;max-width:400px;min-width:100px;background-color:rgba(0,0,0,0.9);color:#fff;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;position:relative;-webkit-flex:0 1 auto;-ms-flex:0 1 auto;flex:0 1 auto;max-height:100%;}#section-9c8b971e-fc4a-4d61-8e47-06eb416e4be9 .loc-msg-close{position:absolute;width:40px;height:40px;top:10px;right:10px;cursor:pointer;-webkit-transition:all 0.3s ease;transition:all 0.3s ease;}#section-9c8b971e-fc4a-4d61-8e47-06eb416e4be9 .loc-msg-close:before{content:'';width:20px;height:2px;background-color:#fff;position:absolute;left:10px;top:19px;-webkit-transform:rotate(45deg);-moz-transform:rotate(45deg);-ms-transform:rotate(45deg);transform:rotate(45deg);}#section-9c8b971e-fc4a-4d61-8e47-06eb416e4be9 .loc-msg-close:after{content:'';width:20px;height:2px;background-color:#fff;position:absolute;left:10px;top:19px;-webkit-transform:rotate(-45deg);-moz-transform:rotate(-45deg);-ms-transform:rotate(-45deg);transform:rotate(-45deg);}#section-9c8b971e-fc4a-4d61-8e47-06eb416e4be9 .loc-msg-close:hover{-webkit-transform:scale(1.2);-moz-transform:scale(1.2);-ms-transform:scale(1.2);transform:scale(1.2);}#section-9c8b971e-fc4a-4d61-8e47-06eb416e4be9 .search-near-hidden .search-near-container{display:none;}#section-9c8b971e-fc4a-4d61-8e47-06eb416e4be9 .loc-msg-title{-webkit-flex:0 0 auto;-ms-flex:0 0 auto;flex:0 0 auto;margin-bottom:20px;}#section-9c8b971e-fc4a-4d61-8e47-06eb416e4be9 .loc-msg-container{-webkit-flex:0 1 auto;-ms-flex:0 1 auto;flex:0 1 auto;overflow:auto;}#section-9c8b971e-fc4a-4d61-8e47-06eb416e4be9 .loc-msg-container p{margin:0;}#section-9c8b971e-fc4a-4d61-8e47-06eb416e4be9 .loc-msg-container p+p{margin-top:1em;}#section-9c8b971e-fc4a-4d61-8e47-06eb416e4be9.visible-loc-msg{z-index:1000;}#section-9c8b971e-fc4a-4d61-8e47-06eb416e4be9.visible-loc-msg .loc-msg-backdrop{background-color:rgba(255,255,255,0.4);}#section-9c8b971e-fc4a-4d61-8e47-06eb416e4be9.visible-loc-msg .loc-msg-wrap{-webkit-clip-path:polygon(0 0, 0 100%, 100% 100%, 100% 0%);clip-path:polygon(0 0, 0 100%, 100% 100%, 100% 0%);}#section-9c8b971e-fc4a-4d61-8e47-06eb416e4be9 .opening__carousel .title-item{display:none;}#section-9c8b971e-fc4a-4d61-8e47-06eb416e4be9 .opening__carousel.visible .title-item{display:block;}#section-9c8b971e-fc4a-4d61-8e47-06eb416e4be9 >*:last-child{position:relative;}#section-9c8b971e-fc4a-4d61-8e47-06eb416e4be9 .overlay-component{position:absolute;top:0;bottom:0;left:0;right:0;background-color:var(--overlay-color, var(--section-overlay, rgba(0, 0, 0, 0.3)));transition-property:opacity;transition-timing-function:linear;}#section-9c8b971e-fc4a-4d61-8e47-06eb416e4be9 .video-wrapper{position:absolute;top:0;left:0;height:100%;width:100%;}@media screen and (max-width: 900px){#section-9c8b971e-fc4a-4d61-8e47-06eb416e4be9 .video-wrapper.hidable{display:none;}}#section-9c8b971e-fc4a-4d61-8e47-06eb416e4be9 .full-screen-video-component{position:relative;height:100%;overflow:hidden;}#section-9c8b971e-fc4a-4d61-8e47-06eb416e4be9 video{position:absolute;z-index:-1000;width:100%;object-fit:cover;}@media all and (-ms-high-contrast: none),(-ms-high-contrast: active){#section-9c8b971e-fc4a-4d61-8e47-06eb416e4be9 video{overflow:hidden;top:50%;left:50%;-webkit-transform:translateX(-50%) translateY(-50%);-moz-transform:translateX(-50%) translateY(-50%);-ms-transform:translateX(-50%) translateY(-50%);transform:translateX(-50%) translateY(-50%);min-width:100%;min-height:100%;width:auto;height:auto!important;}}#section-9c8b971e-fc4a-4d61-8e47-06eb416e4be9 .poster{display:none;position:absolute;top:0;left:0;bottom:0;right:0;z-index:-1000;background:center center/cover;}@media screen and (max-width: 900px){#section-9c8b971e-fc4a-4d61-8e47-06eb416e4be9 .poster{display:block;}}#section-636230b7-115c-40c0-833b-0d0151d53062{color:#000;--fontColor:#000;--fontColor_H:0;--fontColor_S:0%;--fontColor_L:0%;--fontColor_A:1;--fontColor_darkenDir:1;--bgColor:#fff;--bgColor_H:0;--bgColor_S:0%;--bgColor_L:100%;--bgColor_A:1;--bgColor_darkenDir:-1;--section-padding-bottom:calc(0 / 4 * var(--global-section-padding));background-color:#fff;}#section-636230b7-115c-40c0-833b-0d0151d53062 h2{color:#B1191E;}
34#section-636230b7-115c-40c0-833b-0d0151d53062 .collection{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-flex-wrap:wrap;-webkit-flex-wrap:wrap;-ms-flex-wrap:wrap;flex-wrap:wrap;margin:0 -10px;list-style:none;}@media (min-width: 1024px){#section-636230b7-115c-40c0-833b-0d0151d53062 .collection:not(.collection--3){max-width:968px;margin:0 auto;}#section-636230b7-115c-40c0-833b-0d0151d53062 .collection:not(.collection--3) .item{width:calc(50% - 64px);margin:0 32px 48px;}#section-636230b7-115c-40c0-833b-0d0151d53062 .collection:not(.collection--3) .item figure{height:556px;}}#section-636230b7-115c-40c0-833b-0d0151d53062 .item{position:relative;width:calc(33.33333% - 20px);margin:0 10px 48px;-webkit-text-decoration:none;text-decoration:none;color:inherit;overflow:hidden;}#section-636230b7-115c-40c0-833b-0d0151d53062 .item.filtered-out{display:none;}@media (max-width: 991px){#section-636230b7-115c-40c0-833b-0d0151d53062 .item{width:calc(50% - 20px);}}@media (max-width: 768px){#section-636230b7-115c-40c0-833b-0d0151d53062 .item{margin-bottom:24px;width:calc(100% - 20px);}}@media (hover: hover) and (pointer: fine){#section-636230b7-115c-40c0-833b-0d0151d53062 .item:hover img{-webkit-transform:scale(1.1);-moz-transform:scale(1.1);-ms-transform:scale(1.1);transform:scale(1.1);}}#section-636230b7-115c-40c0-833b-0d0151d53062 .item figure{overflow:hidden;margin-bottom:24px;background-color:#f2f2f2;height:504px;}#section-636230b7-115c-40c0-833b-0d0151d53062 .item img{-webkit-transition:all .3s;transition:all .3s;height:100%;width:100%;object-fit:cover;}#section-636230b7-115c-40c0-833b-0d0151d53062 .item >a{display:block;color:inherit;}#section-636230b7-115c-40c0-833b-0d0151d53062 .item .label{position:absolute;top:10px;right:10px;padding:6px 10px;background-color:rgba(255,255,255, .8);margin:0;max-width:100%;white-space:nowrap;text-overflow:ellipsis;overflow:hidden;}#section-636230b7-115c-40c0-833b-0d0151d53062 .item .lp-h6{display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;text-overflow:ellipsis;margin:0;text-transform:uppercase;}#section-636230b7-115c-40c0-833b-0d0151d53062 .item .lp-link{white-space:nowrap;}#section-636230b7-115c-40c0-833b-0d0151d53062 .item .bf1{margin:0;}#section-636230b7-115c-40c0-833b-0d0151d53062 .item .bf2{margin:0;}#section-636230b7-115c-40c0-833b-0d0151d53062 .item .position{color:var(--themeBeige);text-transform:uppercase;}#section-636230b7-115c-40c0-833b-0d0151d53062 .item__info{margin:4px 0 0;padding:0;-webkit-box-flex-wrap:wrap;-webkit-flex-wrap:wrap;-ms-flex-wrap:wrap;flex-wrap:wrap;}@media (max-width: 768px){#section-636230b7-115c-40c0-833b-0d0151d53062 .item__info{margin-bottom:25px;}}#section-636230b7-115c-40c0-833b-0d0151d53062 .item__info li{margin:0;}#section-636230b7-115c-40c0-833b-0d0151d53062 .item__info li:not(:last-child):after{content:'|';margin:0 5px;}#section-636230b7-115c-40c0-833b-0d0151d53062 .item-text{margin:0;}#section-636230b7-115c-40c0-833b-0d0151d53062 .lp-pagination{margin-top:16px;}@media (max-width: 767px){#section-636230b7-115c-40c0-833b-0d0151d53062 .lp-pagination{margin-top:8px;}}#section-636230b7-115c-40c0-833b-0d0151d53062 figure{margin:0;}#section-636230b7-115c-40c0-833b-0d0151d53062 .search-container{max-width:700px;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;gap:16px;margin:0 auto 48px auto;-webkit-align-items:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;-webkit-justify-content:center;justify-content:center;}@media(max-width: 768px){#section-636230b7-115c-40c0-833b-0d0151d53062 .search-container{-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;}}#section-636230b7-115c-40c0-833b-0d0151d53062 .search-container.searching .search-icon{display:none;}#section-636230b7-115c-40c0-833b-0d0151d53062 .search-container.searching .clear-icon{display:initial;}@media(max-width: 768px){#section-636230b7-115c-40c0-833b-0d0151d53062 .search-container{margin:0 auto 32px auto;width:100%;}}#section-636230b7-115c-40c0-833b-0d0151d53062 .search-container .lp-input-group{max-width:450px;}#section-636230b7-115c-40c0-833b-0d0151d53062 .search-container .search-input{padding-right:40px;}#section-636230b7-115c-40c0-833b-0d0151d53062 .search-container .search-icon{position:absolute;top:11px;right:11px;}#section-636230b7-115c-40c0-833b-0d0151d53062 .search-container .clear-icon{position:absolute;top:11px;right:11px;display:none;cursor:pointer;}#section-636230b7-115c-40c0-833b-0d0151d53062 .empty-message{text-align:center;display:none;}#section-636230b7-115c-40c0-833b-0d0151d53062 .empty-message.visible{display:initial;}#section-636230b7-115c-40c0-833b-0d0151d53062>div:last-child{padding-bottom:calc(0 / 4 * var(--global-section-padding))!important;}#section-950e58af-1ac6-48e0-b114-f7326c904100{color:#fff;--fontColor:#fff;--fontColor_H:0;--fontColor_S:0%;--fontColor_L:100%;--fontColor_A:1;--fontColor_darkenDir:-1;--bgColor:#eeede9;--bgColor_H:48;--bgColor_S:13%;--bgColor_L:92%;--bgColor_A:1;--bgColor_darkenDir:-1;--section-padding-top:calc(4 / 4 * var(--global-section-padding));--section-padding-bottom:calc(4 / 4 * var(--global-section-padding));background-color:#eeede9;--gradient-color:var(--overlay-color, rgba(0, 0, 0, 0.30));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/67976f14-49a1-479a-b2c1-dc46412f1c8b');background-repeat:no-repeat;-webkit-background-position:center;background-position:center;-webkit-background-size:cover;background-size:cover;}@media (min-width: 769px){#section-950e58af-1ac6-48e0-b114-f7326c904100 div.sliders-container{padding-left:100px;padding-right:100px;}}@media (min-width: 769px){#section-950e58af-1ac6-48e0-b114-f7326c904100 div.bottom{padding-left:100px;padding-right:100px;}}@media (min-width: 769px){#section-950e58af-1ac6-48e0-b114-f7326c904100 div.bottom p.num-pages{padding-left:74px!important;}}#section-950e58af-1ac6-48e0-b114-f7326c904100 div.avatar svg circle{fill:var
34(--accent-light);}#section-950e58af-1ac6-48e0-b114-f7326c904100 div.avatar svg path{fill:var(--accent-dark);}#section-950e58af-1ac6-48e0-b114-f7326c904100 div.avatar svg circle,#section-950e58af-1ac6-48e0-b114-f7326c904100 div.avatar svg path{fill-opacity:1;}#section-950e58af-1ac6-48e0-b114-f7326c904100 div.arrows-container button.lp-arrow{border:solid 2px var(--accent-light);background-color:var(--accent-light);}#section-950e58af-1ac6-48e0-b114-f7326c904100 div.arrows-container button.lp-arrow:before{border-color:var(--accent-dark);}#section-950e58af-1ac6-48e0-b114-f7326c904100 div.arrows-container button.lp-arrow:hover{border:solid 2px var(--accent-light);background-color:transparent;}#section-950e58af-1ac6-48e0-b114-f7326c904100 div.arrows-container button.lp-arrow:hover::before{border-color:var(--accent-light);}#section-950e58af-1ac6-48e0-b114-f7326c904100 div.bottom p.num-pages{color:var(--typography-light);}#section-950e58af-1ac6-48e0-b114-f7326c904100{overflow:hidden;}#section-950e58af-1ac6-48e0-b114-f7326c904100.parallax-enabled{-webkit-clip-path:polygon(0px -2px, 100% -2px, 100% 102%, 0% 102%);clip-path:polygon(0px -2px, 100% -2px, 100% 102%, 0% 102%);}#section-950e58af-1ac6-48e0-b114-f7326c904100.parallax-enabled>div.video-wrapper{position:fixed;left:0;top:0;width:100vw;height:100vh;z-index:-1;}#section-950e58af-1ac6-48e0-b114-f7326c904100.parallax-enabled .testimonials-section:before{display:block;content:'';left:0;top:0;width:100vw;height:100vh;background:var(--sectionBackground) center no-repeat;-webkit-background-size:cover;background-size:cover;position:fixed;z-index:-1;pointer-events:none;}#section-950e58af-1ac6-48e0-b114-f7326c904100 .testimonial-container{overflow:hidden;}#section-950e58af-1ac6-48e0-b114-f7326c904100 .sliders-container{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;padding-left:200px;padding-right:200px;outline:none;}@media (max-width: 1200px){#section-950e58af-1ac6-48e0-b114-f7326c904100 .sliders-container{padding-left:100px;padding-right:100px;}}@media (max-width: 768px){#section-950e58af-1ac6-48e0-b114-f7326c904100 .sliders-container{padding-left:0;padding-right:0;}}@media (max-width: 768px){#section-950e58af-1ac6-48e0-b114-f7326c904100 .sliders-container{-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;-webkit-align-items:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;}}#section-950e58af-1ac6-48e0-b114-f7326c904100 .sliders-container .avatar-slider{width:200px;}#section-950e58af-1ac6-48e0-b114-f7326c904100 .sliders-container .avatar-slider .slick-list,#section-950e58af-1ac6-48e0-b114-f7326c904100 .sliders-container .avatar-slider .slick-track{width:100%;height:100%;}#section-950e58af-1ac6-48e0-b114-f7326c904100 .sliders-container .avatar-slider .slick-list{overflow:visible;}#section-950e58af-1ac6-48e0-b114-f7326c904100 .sliders-container .avatar-slider .avatar-slide{opacity:0;}#section-950e58af-1ac6-48e0-b114-f7326c904100 .sliders-container .avatar-slider .avatar-slide.slick-active{opacity:1;}#section-950e58af-1ac6-48e0-b114-f7326c904100 .sliders-container .avatar-slider .avatar{position:relative;border-radius:50%;overflow:hidden;-webkit-flex-shrink:0;-ms-flex-negative:0;flex-shrink:0;width:200px;height:200px;}@media (max-width: 768px){#section-950e58af-1ac6-48e0-b114-f7326c904100 .sliders-container .avatar-slider .avatar{width:135px;height:135px;margin:0 auto 24px;}}#section-950e58af-1ac6-48e0-b114-f7326c904100 .sliders-container .avatar-slider .avatar svg{position:absolute;width:100%;height:100%;}#section-950e58af-1ac6-48e0-b114-f7326c904100 .sliders-container .avatar-slider .avatar img{position:absolute;width:100%;height:100%;object-fit:cover;}#section-950e58af-1ac6-48e0-b114-f7326c904100 .sliders-container .list-slider{width:70%;-webkit-box-flex:1;-webkit-flex-grow:1;-ms-flex-positive:1;flex-grow:1;}@media (max-width: 768px){#section-950e58af-1ac6-48e0-b114-f7326c904100 .sliders-container .list-slider{width:100%;}}#section-950e58af-1ac6-48e0-b114-f7326c904100 .testimonials-section h2{margin:0 0 29px 0;}@media (max-width: 560px){#section-950e58af-1ac6-48e0-b114-f7326c904100 .testimonials-section h2{margin:0 0 40px 0;}}#section-950e58af-1ac6-48e0-b114-f7326c904100 .testimonials-section .list:not(.slick-initialized) .list-cell{display:none;}#section-950e58af-1ac6-48e0-b114-f7326c904100 .testimonials-section .list:not(.slick-initialized) .list-cell:first-child{display:block;}#section-950e58af-1ac6-48e0-b114-f7326c904100 .testimonials-section .list .slick-list{overflow:hidden;}@media (max-width: 768px){#section-950e58af-1ac6-48e0-b114-f7326c904100 .testimonials-section .list .slick-list{overflow:hidden;}}
34#section-950e58af-1ac6-48e0-b114-f7326c904100 .testimonials-section .list-cell .quote{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex-direction:row;-ms-flex-direction:row;flex-direction:row;-webkit-align-items:flex-start;-webkit-box-align:flex-start;-ms-flex-align:flex-start;align-items:flex-start;margin-bottom:70px;}@media (max-width: 768px){#section-950e58af-1ac6-48e0-b114-f7326c904100 .testimonials-section .list-cell .quote{-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;-webkit-align-items:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;margin-bottom:20px;}}#section-950e58af-1ac6-48e0-b114-f7326c904100 .testimonials-section .list-cell .author{margin-top:48px;text-transform:uppercase;}@media (max-width: 768px){#section-950e58af-1ac6-48e0-b114-f7326c904100 .testimonials-section .list-cell .author{margin-bottom:0;}}#section-950e58af-1ac6-48e0-b114-f7326c904100 .testimonials-section .list-cell .position{color:#BDBDBD;}#section-950e58af-1ac6-48e0-b114-f7326c904100 .testimonials-section .list-cell .text{padding-left:74px;}@media (max-width: 768px){#section-950e58af-1ac6-48e0-b114-f7326c904100 .testimonials-section .list-cell .text{padding-left:0;text-align:center;}}#section-950e58af-1ac6-48e0-b114-f7326c904100 .testimonials-section .list-cell .title{font-family:var(--global-primary-font-family);text-transform:uppercase;white-space:pre-line;margin-bottom:48px;}#section-950e58af-1ac6-48e0-b114-f7326c904100 .testimonials-section .list-cell .body{white-space:pre-line;margin-bottom:48px;font-weight:400;}#section-950e58af-1ac6-48e0-b114-f7326c904100 .testimonials-section .list-cell .body:before,#section-950e58af-1ac6-48e0-b114-f7326c904100 .testimonials-section .list-cell .body:after{content:'"';}#section-950e58af-1ac6-48e0-b114-f7326c904100 .testimonials-section .bottom{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex-direction:row;-ms-flex-direction:row;flex-direction:row;-webkit-box-pack:justify;-webkit-justify-content:space-between;justify-content:space-between;-webkit-align-items:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;}@media (max-width: 768px){#section-950e58af-1ac6-48e0-b114-f7326c904100 .testimonials-section .bottom{-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;}}@media (max-width: 768px){#section-950e58af-1ac6-48e0-b114-f7326c904100 .testimonials-section .bottom .lp-btn{margin-top:48px;-webkit-order:1;-ms-flex-order:1;order:1;}}#section-950e58af-1ac6-48e0-b114-f7326c904100 .testimonials-section .bottom.no-arrows .arrows-container{display:none;}#section-950e58af-1ac6-48e0-b114-f7326c904100 .testimonials-section .bottom.no-arrows .num-pages{padding-left:0;}#section-950e58af-1ac6-48e0-b114-f7326c904100 .testimonials-section .bottom .num-pages{margin-bottom:0;-webkit-box-flex:1;-webkit-flex-grow:1;-ms-flex-positive:1;flex-grow:1;padding-left:274px;}#section-950e58af-1ac6-48e0-b114-f7326c904100 .testimonials-section .bottom .num-pages span{font-weight:bold;}@media (max-width: 1200px){#section-950e58af-1ac6-48e0-b114-f7326c904100 .testimonials-section .bottom .num-pages{padding-left:174px;}}@media (max-width: 768px){#section-950e58af-1ac6-48e0-b114-f7326c904100 .testimonials-section .bottom .num-pages{padding-left:0;margin-bottom:48px;}}#section-950e58af-1ac6-48e0-b114-f7326c904100 .testimonials-section .bottom .arrows-container{width:200px;}#section-950e58af-1ac6-48e0-b114-f7326c904100 .testimonials-section .bottom .arrows-container .lp-arrow--prev{margin-right:16px;}@media (max-width: 768px){#section-950e58af-1ac6-48e0-b114-f7326c904100 .testimonials-section .bottom .arrows-container{width:auto;-webkit-order:1;-ms-flex-order:1;order:1;}}#section-950e58af-1ac6-48e0-b114-f7326c904100>div:last-child{padding-top:calc(4 / 4 * var(--global-section-padding))!important;padding-bottom:calc(4 / 4 * var(--global-section-padding))!important;}@media (min-width: 960px) and (orientation: landscape){#section-950e58af-1ac6-48e0-b114-f7326c904100{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/67976f14-49a1-479a-b2c1-dc46412f1c8b');}}@media (min-width: 1280px) and (orientation: landscape){#section-950e58af-1ac6-48e0-b114-f7326c904100{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=1920/https://media-production.lp-cdn.com/media/67976f14-49a1-479a-b2c1-dc46412f1c8b');}}@media (min-width: 1920px) and (orientation: landscape){#section-950e58af-1ac6-48e0-b114-f7326c904100{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/67976f14-49a1-479a-b2c1-dc46412f1c8b');}}@media (min-width: 2560px) and (orientation: landscape){#section-950e58af-1ac6-48e0-b114-f7326c904100{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/67976f14-49a1-479a-b2c1-dc46412f1c8b');}}#section-a747ae9e-de7c-4dd7-a979-1a646c88a921{color:#ffffff;--fontColor:#ffffff;--fontColor_H:0;--fontColor_S:0%;--fontColor_L:100%;--fontColor_A:1;--fontColor_darkenDir:-1;--bgColor:rgba(0, 0, 0, 0.52);--bgColor_H:0;--bgColor_S:0%;--bgColor_L:0%;--bgColor_A:0.52;--bgColor_darkenDir:1;background-color:rgba(0, 0, 0, 0.52);}@media (min-width: 769px){#section-a747ae9e-de7c-4dd7-a979-1a646c88a921 div.double-image-grid>div.cta-section div.cta-section__text-block{max-width:560px;}}#section-a747ae9e-de7c-4dd7-a979-1a646c88a921 div.cta-section__content div.cta-section__lead{-webkit-line-clamp:unset;line-clamp:unset;overflow:unset;text-overflow:unset;display:block;white-space:initial;}#section-a747ae9e-de7c-4dd7-a979-1a646c88a921 div.double-image-grid div.cta-section:has(h2.lp-h2:empty):not(:has(.cta-section__lead)){aspect-ratio:1/1;}#section-a747ae9e-de7c-4dd7-a979-1a646c88a921 div.double-image-grid div.cta-section div.cta-section__lead{font-size:var(--global-body-font-size);}#section-a747ae9e-de7c-4dd7-a979-1a646c88a921 h2{color:var(--accent-dark);}#section-a747ae9e-de7c-4dd7-a979-1a646c88a921>.double-image-grid:last-child{padding-top:0!important;padding-bottom:0!important;}#section-a747ae9e-de7c-4dd7-a979-1a646c88a921 .Half-Screen{min-height:50vh;}#section-a747ae9e-de7c-4dd7-a979-1a646c88a921 .Three-Quarters{min-height:75vh;}#section-a747ae9e-de7c-4dd7-a979-1a646c88a921 .Full-Bleed{min-height:100vh;}#section-a747ae9e-de7c-4dd7-a979-1a646c88a921 .double-image-grid{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-flex-wrap:wrap;-webkit-flex-wrap:wrap;-ms-flex-wrap:wrap;flex-wrap:wrap;overflow:hidden;}@media (max-width: 768px){#section-a747ae9e-de7c-4dd7-a979-1a646c88a921 .double-image-grid{min-height:auto;}}#section-a747ae9e-de7c-4dd7-a979-1a646c88a921 .cta-section{position:relative;width:100%;padding-left:15px;padding-right:15px;background:center/cover no-repeat;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;padding-top:var(--section-padding-top, var(--global-section-padding));padding-bottom:var(--section-padding-bottom, var(--global-section-padding));-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;-webkit-box-pack:center;-ms-flex-pack:center;
34-webkit-justify-content:center;justify-content:center;}@media (min-width: 769px){#section-a747ae9e-de7c-4dd7-a979-1a646c88a921 .cta-section{max-width:50%;-webkit-flex-basis:50%;-ms-flex-preferred-size:50%;flex-basis:50%;}}#section-a747ae9e-de7c-4dd7-a979-1a646c88a921 .cta-section__content{position:relative;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;-webkit-box-pack:center;-ms-flex-pack:center;-webkit-justify-content:center;justify-content:center;-webkit-align-items:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;z-index:1;}#section-a747ae9e-de7c-4dd7-a979-1a646c88a921 .cta-section__overlay{position:absolute;left:0;right:0;bottom:0;top:0;background:#000;}#section-a747ae9e-de7c-4dd7-a979-1a646c88a921 .cta-section__text-block{width:100%;margin-left:auto;margin-right:auto;text-align:center;}@media (min-width: 769px){#section-a747ae9e-de7c-4dd7-a979-1a646c88a921 .cta-section__text-block{max-width:500px;}}#section-a747ae9e-de7c-4dd7-a979-1a646c88a921 .cta-section h2{margin-bottom:15px;}#section-a747ae9e-de7c-4dd7-a979-1a646c88a921 .cta-section__lead{margin-bottom:40px;white-space:pre-line;display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;overflow:hidden;text-overflow:ellipsis;max-width:100%;}#section-a747ae9e-de7c-4dd7-a979-1a646c88a921 .cta-section__btn-wrap{margin-top:40px;}#section-a747ae9e-de7c-4dd7-a979-1a646c88a921 .image-back{position:absolute;width:100%;height:100%;left:0;top:0;z-index:0;}#section-a747ae9e-de7c-4dd7-a979-1a646c88a921 .image-back:after{content:"";position:absolute;display:block;width:100%;height:100%;top:0;left:0;}#section-a747ae9e-de7c-4dd7-a979-1a646c88a921 .image-back img{height:100%;width:100%;object-fit:cover;}#section-a747ae9e-de7c-4dd7-a979-1a646c88a921 .background{width:100%;height:100%;}#section-64dea10a-4201-4776-9fdf-ce8691ad5271{color:#000;--fontColor:#000;--fontColor_H:0;--fontColor_S:0%;--fontColor_L:0%;--fontColor_A:1;--fontColor_darkenDir:1;--bgColor:#EBE7E0;--bgColor_H:38;--bgColor_S:22%;--bgColor_L:90%;--bgColor_A:1;--bgColor_darkenDir:-1;--section-padding-bottom:calc(4 / 4 * var(--global-section-padding));background-color:#EBE7E0;}#section-64dea10a-4201-4776-9fdf-ce8691ad5271 div.items:is(.three-columns){gap:24px;}#section-64dea10a-4201-4776-9fdf-ce8691ad5271 div.items:is(.three-columns) div.item{-webkit-flex:1 1 calc((100% / 3) - 24px + (24px / 3));-ms-flex:1 1 calc((100% / 3) - 24px + (24px / 3));flex:1 1 calc((100% / 3) - 24px + (24px / 3));max-width:calc((100% / 3) - 24px + (24px / 3));}@media (max-width: 768px){#section-64dea10a-4201-4776-9fdf-ce8691ad5271 div.items:is(.three-columns) div.item{-webkit-flex:1 1 100%;-ms-flex:1 1 100%;flex:1 1 100%;max-width:100%;}}#section-64dea10a-4201-4776-9fdf-ce8691ad5271 div.featured-neighborhoods-grid{padding-top:96px!important;}@media (max-width: 768px){#section-64dea10a-4201-4776-9fdf-ce8691ad5271 div.featured-neighborhoods-grid{padding-top:unset!important;}}#section-64dea10a-4201-4776-9fdf-ce8691ad5271 .featured-neighborhoods-grid{text-align:center;}#section-64dea10a-4201-4776-9fdf-ce8691ad5271 .featured-neighborhoods-grid .lp-text--pretitle,#section-64dea10a-4201-4776-9fdf-ce8691ad5271 .featured-neighborhoods-grid .lp-text--subtitle{margin-left:auto;margin-right:auto;}#section-64dea10a-4201-4776-9fdf-ce8691ad5271 .featured-neighborhoods-grid .items{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-flex-wrap:wrap;-webkit-flex-wrap:wrap;-ms-flex-wrap:wrap;flex-wrap:wrap;}#section-64dea10a-4201-4776-9fdf-ce8691ad5271 .featured-neighborhoods-grid .items.items-1 .item{margin-left:auto;margin-right:auto;}#section-64dea10a-4201-4776-9fdf-ce8691ad5271 .featured-neighborhoods-grid .items.three-columns .item{width:calc(100% / 3);}@media (max-width: 768px){#section-64dea10a-4201-4776-9fdf-ce8691ad5271 .featured-neighborhoods-grid .items.three-columns .item{width:100%;}}#section-64dea10a-4201-4776-9fdf-ce8691ad5271 .featured-neighborhoods-grid .items .item{width:50%;overflow:hidden;}@media (max-width: 768px){#section-64dea10a-4201-4776-9fdf-ce8691ad5271 .featured-neighborhoods-grid .items .item{width:100%;}}#section-64dea10a-4201-4776-9fdf-ce8691ad5271 .featured-neighborhoods-grid .items .item h3{margin-bottom:1.666rem;}@media (max-width: 768px){#section-64dea10a-4201-4776-9fdf-ce8691ad5271 .featured-neighborhoods-grid .items .item h3{margin-bottom:0;}}#section-64dea10a-4201-4776-9fdf-ce8691ad5271 .featured-neighborhoods-grid .items .item .item-wrapper{padding-bottom:70%;position:relative;display:block;}#section-64dea10a-4201-4776-9fdf-ce8691ad5271 .featured-neighborhoods-grid .items .item .item-wrapper .image-holder{-webkit-transition:all .4s ease-in-out;transition:all .4s ease-in-out;position:absolute;
34left:0;right:0;top:0;bottom:0;background:center no-repeat;-webkit-background-size:cover;background-size:cover;background-color:#eeeeee;}#section-64dea10a-4201-4776-9fdf-ce8691ad5271 .featured-neighborhoods-grid .items .item .item-wrapper .image-holder img{position:absolute;width:100%;height:100%;top:0;left:0;object-fit:cover;}#section-64dea10a-4201-4776-9fdf-ce8691ad5271 .featured-neighborhoods-grid .items .item .item-wrapper .overlay{position:absolute;left:0;right:0;top:0;bottom:0;background-color:rgba(0, 0, 0, 0.3);}#section-64dea10a-4201-4776-9fdf-ce8691ad5271 .featured-neighborhoods-grid .items .item .item-wrapper .content-container{color:white;position:absolute;left:50%;top:50%;-webkit-transform:translate(-50%, -50%);-moz-transform:translate(-50%, -50%);-ms-transform:translate(-50%, -50%);transform:translate(-50%, -50%);max-width:600px;width:70%;}@media (max-width: 1024px){#section-64dea10a-4201-4776-9fdf-ce8691ad5271 .featured-neighborhoods-grid .items .item .item-wrapper .content-container{width:calc(100% - 40px);}}#section-64dea10a-4201-4776-9fdf-ce8691ad5271 .featured-neighborhoods-grid .items .item .item-wrapper .content-container .button-wrapper{-webkit-transition:all linear 0.4s;transition:all linear 0.4s;}@media (min-width: 1025px){#section-64dea10a-4201-4776-9fdf-ce8691ad5271 .featured-neighborhoods-grid .items .item .item-wrapper .content-container .button-wrapper{max-height:0;opacity:0;}}@media (max-width: 1024px){#section-64dea10a-4201-4776-9fdf-ce8691ad5271 .featured-neighborhoods-grid .items .item .item-wrapper .content-container .button-wrapper{display:none;}}@media (min-width: 1025px){#section-64dea10a-4201-4776-9fdf-ce8691ad5271 .featured-neighborhoods-grid .items .item:hover .item-wrapper .image-holder{-webkit-transform:scale(1.1);-moz-transform:scale(1.1);-ms-transform:scale(1.1);transform:scale(1.1);}}@media (min-width: 1025px){#section-64dea10a-4201-4776-9fdf-ce8691ad5271 .featured-neighborhoods-grid .items .item:hover .item-wrapper .content-container .button-wrapper{max-height:90px;opacity:1;}}#section-64dea10a-4201-4776-9fdf-ce8691ad5271 .neighborhood-name{display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;overflow:hidden;text-overflow:ellipsis;}#section-64dea10a-4201-4776-9fdf-ce8691ad5271 .button-container{padding-top:60px;}@media(max-width: 768px){#section-64dea10a-4201-4776-9fdf-ce8691ad5271 .button-container{padding-top:40px;}}#section-64dea10a-4201-4776-9fdf-ce8691ad5271>div:last-child{padding-bottom:calc(4 / 4 * var(--global-section-padding))!important;}#section-fe5140df-d21d-4653-9d5d-d225eca79459{color:#000000;--fontColor:#000000;--fontColor_H:0;--fontColor_S:0%;--fontColor_L:0%;--fontColor_A:1;--fontColor_darkenDir:1;--bgColor:#EBE7E0;--bgColor_H:38;--bgColor_S:22%;--bgColor_L:90%;--bgColor_A:1;--bgColor_darkenDir:-1;--section-padding-top:calc(4 / 4 * var(--global-section-padding));--section-padding-bottom:calc(4 / 4 * var(--global-section-padding));background-color:#EBE7E0;}#section-fe5140df-d21d-4653-9d5d-d225eca79459 div.popup-modal{min-height:unset;}#section-fe5140df-d21d-4653-9d5d-d225eca79459 div.popup-modal .form-group .lp-title-group,#section-fe5140df-d21d-4653-9d5d-d225eca79459 div.popup-modal .form-group .form-inputs-wrap{max-width:100%;}#section-fe5140df-d21d-4653-9d5d-d225eca79459 .popup-modal__form{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-align-items:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;}#section-fe5140df-d21d-4653-9d5d-d225eca79459 div.lp-text--subtitle{font-size:var(--global-body-font-size);}#section-fe5140df-d21d-4653-9d5d-d225eca79459 .form-group .lp-input{color:var(--typography-dark);border-color:var(--border-dark-inactive);}#section-fe5140df-d21d-4653-9d5d-d225eca79459 .form-group .input-icon{-webkit-filter:invert(1);filter:invert(1);}#section-fe5140df-d21d-4653-9d5d-d225eca79459 .popup-modal{width:100%;background:center no-repeat;-webkit-background-size:cover;background-size:cover;min-height:100vh;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-align-items:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;color:var(--fontColor);}#section-fe5140df-d21d-4653-9d5d-d225eca79459 .popup-modal .lp-text--subtitle{max-width:100%;}#section-fe5140df-d21d-4653-9d5d-d225eca79459 .popup-modal .input-container+.input-container{margin-top:16px;}#section-fe5140df-d21d-4653-9d5d-d225eca79459 .popup-modal__container{z-index:2;width:100%;}#section-fe5140df-d21d-4653-9d5d-d225eca79459 .popup-modal .lp-title-group{text-align:center;max-width:420px;}@media (max-width: 768px){#section-fe5140df-d21d-4653-9d5d-d225eca79459 .popup-modal .lp-title-group{max-width:100%;}}#section-fe5140df-d21d-4653-9d5d-d225eca79459 .popup-modal__form{background-color:#fff;max-width:860px;margin:auto;text-align:center;min-height:463px;position:relative;}#section-fe5140df-d21d-4653-9d5d-d225eca79459 .popup-modal__form .form-group{padding:65px;
34display:block;}@media (max-width: 1200px){#section-fe5140df-d21d-4653-9d5d-d225eca79459 .popup-modal__form .form-group{margin:0;max-width:50%;}}@media (max-width: 768px){#section-fe5140df-d21d-4653-9d5d-d225eca79459 .popup-modal__form .form-group{padding:30px;max-width:100%;}}#section-fe5140df-d21d-4653-9d5d-d225eca79459 .popup-modal__form--with-image{max-width:1300px;min-height:675px;text-align:left;}@media (max-width: 768px){#section-fe5140df-d21d-4653-9d5d-d225eca79459 .popup-modal__form--with-image{-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;min-height:1px;}}#section-fe5140df-d21d-4653-9d5d-d225eca79459 .popup-modal__form--with-image .lp-title-group{text-align:left;margin-left:0;}@media (max-width: 768px){#section-fe5140df-d21d-4653-9d5d-d225eca79459 .popup-modal__form--with-image .lp-title-group{max-width:100%;text-align:center;}}#section-fe5140df-d21d-4653-9d5d-d225eca79459 .popup-modal__form--with-image .image{width:50%;height:100%;object-fit:cover;position:absolute;top:0;right:0;}@media (max-width: 768px){#section-fe5140df-d21d-4653-9d5d-d225eca79459 .popup-modal__form--with-image .image{position:static;width:100%;height:331px;}}#section-fe5140df-d21d-4653-9d5d-d225eca79459 .popup-modal__form--with-image .form-group{width:50%;}@media (max-width: 999px){#section-fe5140df-d21d-4653-9d5d-d225eca79459 .popup-modal__form--with-image .form-group{padding:40px;}}@media (max-width: 768px){#section-fe5140df-d21d-4653-9d5d-d225eca79459 .popup-modal__form--with-image .form-group{width:100%;padding:20px;}}#section-fe5140df-d21d-4653-9d5d-d225eca79459 .popup-modal__input{width:100%;display:block;}#section-fe5140df-d21d-4653-9d5d-d225eca79459 .popup-modal__submit{margin-top:16px;width:100%;}#section-fe5140df-d21d-4653-9d5d-d225eca79459 .popup-modal .form-inputs-wrap{max-width:420px;}@media (max-width: 768px){#section-fe5140df-d21d-4653-9d5d-d225eca79459 .popup-modal .form-inputs-wrap{max-width:100%;}}#section-fe5140df-d21d-4653-9d5d-d225eca79459 .popup-modal .form-inputs-wrap .lp-btn{display:block;width:100%;}#section-fe5140df-d21d-4653-9d5d-d225eca79459 .popup-modal .thank-you{display:none;padding:24px;text-align:center;min-height:675px;height:100%;}#section-fe5140df-d21d-4653-9d5d-d225eca79459 .popup-modal .thank-you .lp-title-group{margin-left:auto;margin-right:auto;text-align:center;}#section-fe5140df-d21d-4653-9d5d-d225eca79459 .popup-modal .success .thank-you{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;-webkit-align-items:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;-webkit-justify-content:center;justify-content:center;}#section-fe5140df-d21d-4653-9d5d-d225eca79459 .popup-modal .success .form-group{dis
34play:none;}#section-fe5140df-d21d-4653-9d5d-d225eca79459 .popup-modal .success .image{display:none;}#section-fe5140df-d21d-4653-9d5d-d225eca79459 .popup-modal h2{margin-bottom:8px;}#section-fe5140df-d21d-4653-9d5d-d225eca79459 .popup-modal h4{margin-bottom:48px;}@media (max-width: 768px){#section-fe5140df-d21d-4653-9d5d-d225eca79459 .popup-modal h4{margin-bottom:32px;}}#section-fe5140df-d21d-4653-9d5d-d225eca79459 .form-inputs-wrap.center-aligned{margin-left:auto;margin-right:auto;}#section-fe5140df-d21d-4653-9d5d-d225eca79459 .lp-h-pot{height:0;width:0;}#section-fe5140df-d21d-4653-9d5d-d225eca79459 .lp-disclaimer{margin-top:16px;}#section-fe5140df-d21d-4653-9d5d-d225eca79459 h1{color:#B1191E;}#section-fe5140df-d21d-4653-9d5d-d225eca79459 h2{color:#B1191E;}#section-fe5140df-d21d-4653-9d5d-d225eca79459 h3{color:#B1191E;}#section-fe5140df-d21d-4653-9d5d-d225eca79459 p{color:#000000;}#section-fe5140df-d21d-4653-9d5d-d225eca79459>div:last-child{padding-top:calc(4 / 4 * var(--global-section-padding))!important;padding-bottom:calc(4 / 4 * var(--global-section-padding))!important;}#section-382717e0-234c-406a-ac82-41111ea1b5c3{color:#ffffff;--fontColor:#ffffff;--fontColor_H:0;--fontColor_S:0%;--fontColor_L:100%;--fontColor_A:1;--fontColor_darkenDir:-1;--bgColor:#fff;--bgColor_H:0;--bgColor_S:0%;--bgColor_L:100%;--bgColor_A:1;--bgColor_darkenDir:-1;background-color:#fff;--gradient-color:var(--overlay-color, rgba(0, 0, 0, 0.3));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/752428d5-b15e-42df-8a4c-b32cfe42afbe');background-repeat:no-repeat;-webkit-background-position:center;background-position:center;-webkit-background-size:cover;background-size:cover;}#section-382717e0-234c-406a-ac82-41111ea1b5c3 div.lp-title-group{max-width:unset;}#section-382717e0-234c-406a-ac82-41111ea1b5c3 div.lp-title-group h2.lp-h2{max-width:unset;}#section-382717e0-234c-406a-ac82-41111ea1b5c3 div.lp-title-group p.pre-line{max-width:560px;margin-left:auto;margin-right:auto;}#section-382717e0-234c-406a-ac82-41111ea1b5c3.parallax-enabled{-webkit-clip-path:polygon(0px 0px, 100% 0%, 100% 100%, 0% 100%);clip-path:polygon(0px 0px, 100% 0%, 100% 100%, 0% 100%);}#section-382717e0-234c-406a-ac82-41111ea1b5c3.parallax-enabled>div.video-wrapper{position:fixed;left:0;top:0;width:100vw;height:100vh;z-index:-9999;}#section-382717e0-234c-406a-ac82-41111ea1b5c3.parallax-enabled .work-with-us:before{display:block;content:'';left:0;top:0;width:100%;background:var(--sectionBackground) center no-repeat;-webkit-background-size:cover;background-size:cover;position:fixed;z-index:-1;height:100%;}#section-382717e0-234c-406a-ac82-41111ea1b5c3 .section{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;-webkit-box-pack:center;-ms-flex-pack:center;-webkit-justify-content:center;justify-content:center;-webkit-align-items:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;min-height:780px;text-align:center;position:relative;z-index:2;}@media (max-width: 767px){#section-382717e0-234c-406a-ac82-41111ea1b5c3 .section{min-height:100vh;}}#section-382717e0-234c-406a-ac82-41111ea1b5c3 .section:before{content:'';position:absolute;bottom:0;left:0;height:50%;width:100%;pointer-events:none;z-index:3;-webkit-transform:translateZ(1px);-moz-transform:translateZ(1px);-ms-transform:translateZ(1px);transform:translateZ(1px);background:linear-gradient(180deg, rgba(0, 0, 0, 0) 0%, var(--opacityColor) 100%);}#section-382717e0-234c-406a-ac82-41111ea1b5c3 .section .btn-container{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-pack:center;-ms-flex-pack:center;-webkit-justify-content:center;justify-content:center;gap:23px;margin-top:32px;}@media(max-width: 560px){#section-382717e0-234c-406a-ac82-41111ea1b5c3 .section .btn-container{-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;gap:10px;}}#section-382717e0-234c-406a-ac82-41111ea1b5c3 .lp-container{-webkit-transform:translateZ(1px);-moz-transform:translateZ(1px);-ms-transform:translateZ(1px);transform:translateZ(1px);}@media (min-width: 960px) and (orientation: landscape){#section-382717e0-234c-406a-ac82-41111ea1b5c3{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/752428d5-b15e-42df-8a4c-b32cfe42afbe');}}@media (min-width: 1280px) and (orientation: landscape){#section-382717e0-234c-406a-ac82-41111ea1b5c3{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=1920/https://media-production.lp-cdn.com/media/752428d5-b15e-42df-8a4c-b32cfe42afbe');}}@media (min-width: 1920px) and (orientation: landscape){#section-382717e0-234c-406a-ac82-41111ea1b5c3{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/752428d5-b15e-42df-8a4c-b32cfe42afbe');}}@media (min-width: 2560px) and (orientation: landscape){#section-382717e0-234c-406a-ac82-41111ea1b5c3{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/752428d5-b15e-42df-8a4c-b32cfe42afbe');}}#modal-global-contact-us .section{width:100%;min-height:100vh;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-align-items:stretch;-webkit-box-align:stretch;-ms-flex-align:stretch;align-items:stretch;-webkit-box-pack:start!important;-ms-flex-pack:start!important;
34-webkit-justify-content:flex-start!important;justify-content:flex-start!important;}@media (max-width: 1024px){#modal-global-contact-us .section{-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;}}#modal-global-contact-us .left{width:100%;max-width:600px;padding:80px;}@media (max-width: 1200px){#modal-global-contact-us .left{padding-left:50px;padding-right:50px;}}@media (max-width: 1024px){#modal-global-contact-us .left{max-width:100%;min-height:auto;width:100%;padding:50px;}}@media (max-width: 767px){#modal-global-contact-us .left{padding:50px 24px;}}#modal-global-contact-us .left .lp-h2{margin:0 0 48px;}@media (max-width: 767px){#modal-global-contact-us .left .lp-h2{margin:0 0 32px;}}#modal-global-contact-us .contact-form__form{width:100%;min-width:440px;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-flex-wrap:wrap;-webkit-flex-wrap:wrap;-ms-flex-wrap:wrap;flex-wrap:wrap;gap:24px;margin:0;-webkit-align-items:flex-start;-webkit-box-align:flex-start;-ms-flex-align:flex-start;align-items:flex-start;}@media (max-width: 767px){#modal-global-contact-us .contact-form__form{min-width:100%;}}#modal-global-contact-us .contact-form__form .input-container{width:100%;}#modal-global-contact-us .contact-form__form .input-container:not(.textarea){width:calc(50% - 12px);}@media (max-width: 767px){#modal-global-contact-us .contact-form__form .input-container:not(.textarea){width:100%;}}#modal-global-contact-us .contact-form__form .lp-input{padding-left:24px;width:100%;}#modal-global-contact-us .button-holder{width:100%;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-pack:end;-ms-flex-pack:end;-webkit-justify-content:flex-end;justify-content:flex-end;}#modal-global-contact-us .button-holder .btn{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-pack:center;-ms-flex-pack:center;-webkit-justify-content:center;justify-content:center;}#modal-global-contact-us .sync-icon{display:none;margin-right:10px;-webkit-box-pack:center;-ms-flex-pack:center;-webkit-justify-content:center;justify-content:center;-webkit-align-items:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;}#modal-global-contact-us .success-icon{display:none;margin-right:10px;-webkit-transition:0s;transition:0s;visibility:visible;}#modal-global-contact-us .loading{pointer-events:none;}#modal-global-contact-us .loading .sync-icon{display:-webkit-inline-box;display:-webkit-inline-flex;display:-ms-inline-flexbox;display:inline-flex;}#modal-global-contact-us .success-text{display:none;}#modal-global-contact-us .success{pointer-events:none;}#modal-global-contact-us .success .success-icon{display:block;}#modal-global-contact-us .success .success-text{display:inline;}#modal-global-contact-us .input-container{width:calc(50% - 24px);}#modal-global-contact-us .input-container:last-child{width:100%;}#modal-global-contact-us .contact-form__submit{width:175px;}@media (max-width: 767px){#modal-global-contact-us .contact-form__submit{width:100%;}}#modal-global-contact-us .lp-input{width:100%;}#modal-global-contact-us .pre-title{display:inline-block;padding-right:190px;margin:0 0 48px;position:relative;max-width:440px;}@media (max-width: 767px){#modal-global-contact-us .pre-title{margin:0 0 32px;padding-right:130px;}}#modal-global-contact-us .pre-title:after{content:'';height:1px;width:165px;position:absolute;top:13px;right:0;background-color:#CCB091;}@media (max-width: 767px){#modal-global-contact-us .pre-title:after{width:118px;}}#modal-global-contact-us .right{background-color:var(--bgColor);-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;-webkit-box-pack:justify;-webkit-justify-content:space-between;justify-content:space-between;position:relative;padding:80px;-webkit-box-flex:1;-webkit-flex-grow:1;-ms-flex-positive:1;flex-grow:1;}@media (max-width: 1024px){#modal-global-contact-us .right{max-width:100%;min-height:auto;width:100%;padding:50px;}}@media (max-width: 767px){#modal-global-contact-us .right{padding:50px 24px;}}#modal-global-contact-us .right >*{position:relative;z-index:2;}#modal-global-contact-us .right .lp-h3{margin:0 0 48px;}@media (max-width: 767px){#modal-global-contact-us .right .lp-h3{margin-bottom:32px;}}#modal-global-contact-us .right .img-wrapper{overflow:hidden;}#modal-global-contact-us .agent-row{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;gap:25px;margin-bottom:40px;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;}#modal-global-contact-us .agent{word-break:break-all;}#modal-global-contact-us .agent p{margin:0;}#modal-global-contact-us .address{margin:0 0 40px;}#modal-global-contact-us .img-wrapper{position:absolute;top:0;left:0;width:100%;height:100%;}#modal-global-contact-us .img-wrapper .img{position:relative;object-fit:cover;height:100%;width:100%;}#modal-global-contact-us .img-wrapper:after{content:'';position:absolute;background-color:var(--bgOpacity);top:0;left:0;bottom:0;right:0;}#modal-global-contact-us .close img,#modal-global-contact-us .close .lp-close-icon{border-style:none;max-width:100%;}@media (max-width: 1024px){#modal-global-contact-us .close img,#modal-global-contact-us .close .lp-close-icon{margin-top:15px;-webkit-filter:invert(1);filter:invert(1);}}#modal-global-contact-us input,#modal-global-contact-us textarea{color:inherit!important;}nav{-webkit-transition:0.2s all;transition:0.2s all;}nav nav{-webkit-transition:0.2s all;transition:0.2s all;}nav .header{padding-top:30px;padding-bottom:30px;}nav .header__right{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-align-items:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-flex:1;-webkit-flex-grow:1;-ms-flex-positive:1;flex-grow:1;-webkit-flex-basis:50%;-ms-flex-preferred-size:50%;flex-basis:50%;-webkit-box-pack:end;-ms-flex-pack:end;
34-webkit-justify-content:flex-end;justify-content:flex-end;}nav .header__container{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-align-items:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;-webkit-justify-content:center;justify-content:center;}nav .header__btn-contact{border:1px solid;border-radius:3px;padding:18px 40px;-webkit-transition:0.2s all;transition:0.2s all;text-transform:none;margin-left:40px;font-size:18px;-webkit-flex-shrink:0;-ms-flex-negative:0;flex-shrink:0;}@media (hover: hover) and (pointer: fine){nav .header__btn-contact:hover{background-color:var(--themeBlack);color:var(--themeWhite);}}@media (max-width: 768px){nav .header__btn-contact{padding:10px;margin-left:5px;margin-right:12px;}}nav .header__hamburger{background-color:transparent;cursor:pointer;outline:none;position:relative;padding:0;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-align-items:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;-webkit-justify-content:center;justify-content:center;margin-left:41px;width:60px;height:60px;border:none;}@media (max-width: 768px){nav .header__hamburger{width:40px;height:40px;margin-left:15px;}}@media (hover: hover) and (pointer: fine){nav .header__hamburger:hover .line--top{width:12px;x:30px;}nav .header__hamburger:hover .line--bottom{width:24px;x:18px;}}nav .header__hamburger svg{width:100%;height:100%;}nav .header__hamburger circle{fill:var(--themeBeige);}nav .header__hamburger .line{fill:var(--themeWhite);height:2px;width:24px;x:18px;y:26px;-webkit-transition:0.2s all;transition:0.2s all;}nav .header__hamburger .line--bottom{width:12px;x:30px;y:32px;}nav .sub-nav{position:absolute;left:0;top:100%;padding:0;max-height:0;z-index:5;margin:0;list-style-type:none;overflow:hidden;visibility:hidden;-webkit-transition:visibility 0s linear 0.3s,max-height 0.3s ease;transition:visibility 0s linear 0.3s,max-height 0.3s ease;}nav .sub-nav.visible{-webkit-transition:visibility 0s linear 0s,max-height 0.3s ease-in;transition:visibility 0s linear 0s,max-height 0.3s ease-in;max-height:100vh;visibility:visible;}nav .sub-nav__item{position:relative;display:block;margin:0;text-align:left;}nav .sub-nav__link{display:block;color:var(--themeWhite);font-size:14px;font-weight:600;padding:10px 18px;position:relative;width:-webkit-max-content;width:-moz-max-content;width:max-content;max-width:178px;min-width:100%;cursor:pointer;-webkit-text-decoration:none;text-decoration:none;background-color:var(--themeBlack);}@media (hover: hover) and (pointer: fine){nav .sub-nav__link:hover{color:var(--themeWhite);background-color:rgba(75, 75, 75, 1);}}nav .logo{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-align-items:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;position:relative;outline:none;max-width:234px;height:60px;}@media (max-width: 768px){nav .logo{width:145px;}}nav .logo__img{object-fit:contain;object-position:left;max-width:100%;max-height:100%;}nav .logo__img.light{display:none;}nav .logo__img.dark{display:block;}nav .scroll .header{padding-top:22px;padding-bottom:22px;}@media (hover: hover) and (pointer: fine){nav .scroll .header__btn-contact:hover{background-color:var(--themeWhite);color:var(--themeBlack);}}nav .scroll .search__btn{color:var(--themeWhite);}nav .scroll .logo__img.dark{display:none;}nav .scroll .logo__img.light{display:block;}nav .header.theme-light{color:#fff;}nav .header.theme-light .lp-btn.lp-btn--dark.lp-btn--outline{color:var(--themeWhite, #fff);border-color:var(--themeWhite, #fff);}nav .header.theme-light .lp-btn.lp-btn--dark.lp-btn--outline:hover{color:var(--themeBlack, #fff);border-color:var(--themeBlack, #fff);background-color:var(--themeWhite, #fff);}nav .header.theme-light .logo__img.dark{display:none;}nav .header.theme-light .logo__img.light{display:block;}nav.scroll .header{padding-top:22px;padding-bottom:22px;}nav.scroll .lp-btn.lp-btn--dark.lp-btn--outline{color:var(--themeWhite, #fff);border-color:var(--themeWhite, #fff);}nav.scroll .lp-btn.lp-btn--dark.lp-btn--outline:hover{color:var(--themeBlack, #fff);border-color:var(--themeBlack, #fff);background-color:var(--themeWhite, #fff);}nav.scroll .search__btn{color:var(--themeWhite);}nav.scroll .logo__img.dark{display:none;}nav.scroll .logo__img.light{display:block;}
34nav .navigation{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-pack:end;-ms-flex-pack:end;-webkit-justify-content:flex-end;justify-content:flex-end;-webkit-align-items:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;list-style-type:none;margin:0;padding:0;-webkit-box-flex-wrap:wrap;-webkit-flex-wrap:wrap;-ms-flex-wrap:wrap;flex-wrap:wrap;-webkit-flex:1;-ms-flex:1;flex:1;}@media (max-width: 1024px){nav .navigation{display:none;}}nav .navigation__item{padding:0;margin:0;visibility:hidden;height:0;}nav .navigation__item+.navigation__item{padding-left:20px;}@media (hover: hover) and (pointer: fine){nav .navigation .navigation__item:not(.sub-nav-container) .navigation__link:after{content:'';position:absolute;display:block;width:0;height:2px;-webkit-transition:width .3s linear;transition:width .3s linear;background:var(--themeBeige);bottom:0;left:0;}nav .navigation .navigation__item:not(.sub-nav-container) .navigation__link:hover:after{width:100%;}}nav .navigation__link{display:block;position:relative;font-size:18px;line-height:18px;background-color:transparent;border:none;border-bottom:2px solid transparent;border-top:2px solid transparent;cursor:pointer;padding:21px 10px;max-width:180px;z-index:2;outline:none;color:inherit;-webkit-text-decoration:none;text-decoration:none;white-space:nowrap;-webkit-font-smoothing:unset;-webkit-transition:color .3s linear;transition:color .3s linear;}nav .navigation__link:focus,nav .navigation__link:active{outline:none;}nav .sub-nav-container{position:relative;padding-right:21px;}nav .sub-nav-container:after{display:block;content:'';width:8px;height:8px;border-right:2px solid;border-bottom:2px solid;position:absolute;right:3px;top:50%;-webkit-transform:translate(0, calc(-50% - 3px)) rotate(45deg);-moz-transform:translate(0, calc(-50% - 3px)) rotate(45deg);-ms-transform:translate(0, calc(-50% - 3px)) rotate(45deg);transform:translate(0, calc(-50% - 3px)) rotate(45deg);-webkit-transition:-webkit-transform 0.2s linear;transition:transform 0.2s linear;}nav .sub-nav-container.active:after{-webkit-transform:translate(0, calc(-50% + 3px)) rotate(-135deg);-moz-transform:translate(0, calc(-50% + 3px)) rotate(-135deg);-ms-transform:translate(0, calc(-50% + 3px)) rotate(-135deg);transform:translate(0, calc(-50% + 3px)) rotate(-135deg);}nav .search{position:relative;}nav .search__btn{border:none;background-color:transparent;margin-left:20px;cursor:pointer;display:block;color:inherit;}@media (max-width: 768px){nav .search__btn{margin-left:0;}}nav .search__btn svg{display:block;}@media (max-width: 768px){nav .hide-mobile{display:none;}}body>div.sidemenu{background-color:rgba(235, 231, 224, 1);color:rgba(0, 0, 0, 1);--fontColor:rgba(0, 0, 0, 1);--fontColor_H:0;--fontColor_S:0%;--fontColor_L:0%;--fontColor_A:1;--fontColor_darkenDir:1;--bgColor:rgba(235, 231, 224, 1);--bgColor_H:38;--bgColor_S:22%;--bgColor_L:90%;--bgColor_A:1;--bgColor_darkenDir:-1;}body>div.sidemenu{--colorLighter1:hsla(var(--fontColor_H), var(--fontColor_S), var(--fontColor_L), 0.1);width:100%;display:block;-webkit-transform:translateY(-100%);-moz-transform:translateY(-100%);-ms-transform:translateY(-100%);transform:translateY(-100%);-webkit-transition:.1s all ease-out 2s;transition:.1s all ease-out 2s;background:transparent;}body>div.sidemenu .sidemenu__container{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;}body>div.sidemenu.visible{-webkit-transition:0s all;transition:0s all;-webkit-transform:translateY(0);-moz-transform:translateY(0);-ms-transform:translateY(0);transform:translateY(0);z-index:900;background-color:transparent;}body>div.sidemenu.visible .sidemenu__img{-webkit-transform:translateY(0%);-moz-transform:translateY(0%);-ms-transform:translateY(0%);transform:translateY(0%);-webkit-transition:.7s cubic-bezier(0.645, 0.045, 0.355, 1.000);transition:.7s cubic-bezier(0.645, 0.045, 0.355, 1.000);}body>div.sidemenu.visible .sidemenu__wrapper{-webkit-transform:translateY(0%);-moz-transform:translateY(0%);-ms-transform:translateY(0%);transform:translateY(0%);-webkit-transition:1s cubic-bezier(0.645, 0.045, 0.355, 1.000);transition:1s cubic-bezier(0.645, 0.045, 0.355, 1.000);}body>div.sidemenu.visible .sidemenu__toggle{pointer-events:auto;}body>div.sidemenu .sidemenu__img{background:center no-repeat;-webkit-background-size:cover;background-size:cover;-webkit-box-flex:1;-webkit-flex-grow:1;-ms-flex-positive:1;flex-grow:1;-webkit-transform:translateY(-100%);-moz-transform:translateY(-100%);-ms-transform:translateY(-100%);transform:translateY(-100%);-webkit-transition:.7s cubic-bezier(0.645, 0.045, 0.355, 1.000) .5s;transition:.7s cubic-bezier(0.645, 0.045, 0.355, 1.000) .5s;}@media (max-width: 768px){body>div.sidemenu .sidemenu__img{display:none;}}body>div.sidemenu .sidemenu__wrapper{background-color:var(--bgColor, #211F1F);-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;-webkit-box-pack:justify;
34-webkit-justify-content:space-between;justify-content:space-between;height:100vh;padding:80px 0 66px 0;overflow-y:auto;max-width:700px;-webkit-box-flex:1;-webkit-flex-grow:1;-ms-flex-positive:1;flex-grow:1;-webkit-transform:translateY(-100%);-moz-transform:translateY(-100%);-ms-transform:translateY(-100%);transform:translateY(-100%);-webkit-transition:1s cubic-bezier(0.645, 0.045, 0.355, 1.000) .5s;transition:1s cubic-bezier(0.645, 0.045, 0.355, 1.000) .5s;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;}body>div.sidemenu .sidemenu__wrapper::-webkit-scrollbar{width:4px;background-color:transparent;}body>div.sidemenu .sidemenu__wrapper::-webkit-scrollbar-thumb{background-color:var(--fontColor, #ffffff);}body>div.sidemenu .sidemenu__wrapper::-webkit-scrollbar-track{-webkit-box-shadow:inset 0 0 4px rgba(0,0,0,0.3);background-color:var(--bgColor, #211F1F);}@media (max-width: 768px){body>div.sidemenu .sidemenu__wrapper{padding:40px 0 66px 0;}}body>div.sidemenu .sidemenu__toggle{height:48px;margin-right:auto;margin-left:70px;position:relative;border:none;background-color:transparent;outline:none;color:var(--fontColor, #ffffff);padding-right:50px;font-family:inherit;pointer-events:none;cursor:pointer;}@media (max-width: 768px){body>div.sidemenu .sidemenu__toggle{margin-left:20px;}}body>div.sidemenu .sidemenu__toggle--right{margin-left:auto;margin-right:70px;}@media (max-width: 768px){body>div.sidemenu .sidemenu__toggle--right{margin-right:20px;}}body>div.sidemenu .sidemenu__toggle:focus{outline:none;}body>div.sidemenu .sidemenu__toggle:before,body>div.sidemenu .sidemenu__toggle:after{content:'';display:block;position:absolute;width:20px;height:2px;background-color:currentColor;right:0;top:50%;}body>div.sidemenu .sidemenu__toggle:before{-webkit-transform:translate(-50%, -50%) rotate(45deg);-moz-transform:translate(-50%, -50%) rotate(45deg);-ms-transform:translate(-50%, -50%) rotate(45deg);transform:translate(-50%, -50%) rotate(45deg);}body>div.sidemenu .sidemenu__toggle:after{-webkit-transform:translate(-50%, -50%) rotate(-225deg);-moz-transform:translate(-50%, -50%) rotate(-225deg);-ms-transform:translate(-50%, -50%) rotate(-225deg);transform:translate(-50%, -50%) rotate(-225deg);}body>div.sidemenu .sidemenu__nav{margin-bottom:auto;padding:0;}body>div.sidemenu .sidemenu__socials{margin-left:auto;padding-top:30px;padding-right:70px;}@media (max-width: 768px){body>div.sidemenu .sidemenu__socials{padding-right:20px;padding-left:20px;}}body>div.sidemenu .sidemenu__socials .lp-socials li{margin-bottom:14px;}body>div.sidemenu .sidemenu__header{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-pack:justify;-webkit-justify-content:space-between;justify-content:space-between;-webkit-align-items:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;padding-bottom:93px;}@media (max-width: 768px){body>div.sidemenu .sidemenu__header{padding-bottom:59px;}}body>div.sidemenu .sidemenu__logo{display:none;}@media (max-width: 768px){body>div.sidemenu .sidemenu__logo{display:block;}}body>div.sidemenu .sidemenu__logo-img{display:block;max-height:50px;}body>div.sidemenu .navigation{list-style-type:none;margin:0;padding:0;max-width:100%;}body>div.sidemenu .navigation__item{border-bottom:1px solid var(--colorLighter1);margin:0;overflow:hidden;}body>div.sidemenu .navigation__link{padding:12px 12px 12px 70px;display:block;font-size:22px;font-weight:400;line-height:48px;color:var(--fontColor, #ffffff);-webkit-transition:0.2s all;transition:0.2s all;cursor:pointer;-webkit-text-decoration:none;text-decoration:none;font-family:var(--global-primary-font-family);-webkit-transform:translateY(100%);-moz-transform:translateY(100%);-ms-transform:translateY(100%);transform:translateY(100%);opacity:0;position:relative;transition-property:transform,opacity,color;transition-duration:0.55s,0.55s,0.3s;transition-timing-function:cubic-bezier(0.645, 0.045, 0.355, 1),ease-in-out,ease-in-out;}@media(max-width: 768px){body>div.sidemenu .navigation__link{padding-left:20px;padding-right:20px;padding-bottom:16px;}}@media (hover: hover) and (pointer: fine){body>div.sidemenu .navigation__link:hover{color:var(--fontColor, #ffffff);}}body>div.sidemenu .sub-nav{list-style-type:none;display:none;margin:0;padding:10px 0 30px 85px;}@media(max-width: 768px){body>div.sidemenu .sub-nav{padding-bottom:16px;padding-left:35px;}}body>div.sidemenu .sub-nav__item{padding-bottom:10px;margin:0;opacity:0;}body>div.sidemenu .sub-nav__item.fadeInUp{-webkit-animation-duration:1s;animation-duration:1s;-webkit-animation-timing-function:ease;animation-timing-function:ease;-webkit-animation-fill-mode:forwards;animation-fill-mode:forwards;-webkit-animation-delay:var(--delay);animation-delay:var(--delay);}body>div.sidemenu .sub-nav__link{font-size:16px;font-weight:400;cursor:pointer;color:var(--fontColor, #ffffff);font-family:var(--global-secondary-font-family);-webkit-text-decoration:none;text-decoration:none;}body>div.sidemenu .sub-nav.visible{display:block;}body>div.sidemenu .lp-socials__link{opacity:0;-webkit-transform:translateY(-50px);-moz-transform:translateY(-50px);-ms-transform:translateY(-50px);transform:translateY(-50px);position:relative;transition-property:transform,opacity,color;transition-duration:0.55s,0.55s,0.3s;transition-timing-function:cubic-bezier(0.645, 0.045, 0.355, 1),ease-in-out,ease-in-out;}body>div.sidemenu body>div.sidemenu{width:100%;}body>div.sidemenu body>div.sidemenu.visible{-webkit-transition:0s all;transition:0s all;-webkit-transform:translateY(0);-moz-transform:translateY(0);-ms-transform:translateY(0);transform:translateY(0);z-index:900;background-color:transparent;}body>div.sidemenu body>div.sidemenu.visible .sidemenu__img{-webkit-transform:translateY(0%);-moz-transform:translateY(0%);-ms-transform:translateY(0%);transform:translateY(0%);-webkit-transition:.7s cubic-bezier(0.645, 0.045, 0.355, 1.000);transition:.7s cubic-bezier(0.645, 0.045, 0.355, 1.000);}body>div.sidemenu body>div.sidemenu.visible .sidemenu__wrapper{-webkit-transform:translateY(0%);-moz-transform:translateY(0%);-ms-transform:translateY(0%);transform:translateY(0%);-webkit-transition:1s cubic-bezier(0.645, 0.045, 0.355, 1.000);transition:1s cubic-bezier(0.645, 0.045, 0.355, 1.000);}body>div.sidemenu body>div.sidemenu.visible .sidemenu__toggle{pointer-events:auto;}body>div.sidemenu .fade-in{opacity:1;-webkit-transform:translateY(0);-moz-transform:translateY(0);-ms-transform:translateY(0);transform:translateY(0);}body>div.sidemenu .arrow{vertical-align:middle;margin-bottom:10px;-webkit-transition:.4s;transition:.4s;}body>div.sidemenu .arrow.active{-webkit-transform:rotate(-180deg);-moz-transform:rotate(-180deg);-ms-transform:rotate(-180deg);transform:rotate(-180deg);-webkit-transition:.4s;transition:.4s;}footer{background-color:rgba(235, 231, 224, 1);color:rgba(0, 0, 0, 1);--fontColor:rgba(0, 0, 0, 1);--fontColor_H:0;--fontColor_S:0%;--fontColor_L:0%;--fontColor_A:1;--fontColor_darkenDir:1;--bgColor:rgba(235, 231, 224, 1);--bgColor_H:38;--bgColor_S:22%;--bgColor_L:90%;--bgColor_A:1;--bgColor_darkenDir:-1;}footer .lp-input--dark{background-color:#fff;}footer .site-footer__subscribe{padding-top:92px;padding-bottom:92px;background-color:rgba(0,0,0,0.1);}@media (max-width: 768px){footer .site-footer__subscribe{padding-top:64px;padding-bottom:64px;}}footer .site-footer__logo-bar{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-flex-wrap:wrap;-webkit-flex-wrap:wrap;-ms-flex-wrap:wrap;flex-wrap:wrap;padding-bottom:64px;-webkit-align-items:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;}@media (max-width: 768px){footer .site-footer__logo-bar{padding-bottom:24px;}}footer .site-footer__logo-container img{max-width:268px;max-height:56px;object-fit:contain;display:block;object-position:left top;}footer .site-footer__logo-container+.site-footer__logo-container{margin-left:50px;}@media (max-width: 768px){footer .site-footer__logo-container+.site-footer__logo-container{margin-left:0;margin-top:16px;width:100%;}}footer .site-footer__info{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-flex-wrap:wrap;-webkit-flex-wrap:wrap;-ms-flex-wrap:wrap;flex-wrap:wrap;-webkit-align-items:flex-start;-webkit-box-align:flex-start;-ms-flex-align:flex-start;align-items:flex-start;}@media (max-width: 768px){footer .site-footer__info{-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;}}footer .site-footer__info .lp-a a{white-space:nowrap;}
34footer .site-footer__col{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-flex-wrap:wrap;-webkit-flex-wrap:wrap;-ms-flex-wrap:wrap;flex-wrap:wrap;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;padding-right:32px;}footer .site-footer__col-title{width:100%;margin-bottom:16px;min-height:29px;}footer .site-footer__col-content{opacity:0.5;padding-bottom:16px;}footer .site-footer__col-content p{margin-bottom:0;}footer .site-footer__col-content a{color:inherit;}footer .site-footer__col-content+.site-footer__col-content{padding-left:40px;}@media (max-width: 768px){footer .site-footer__col-content+.site-footer__col-content{padding-left:0;}}footer .site-footer__navigation-wrap{width:100%;padding-top:32px;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-align-items:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:justify;-webkit-justify-content:space-between;justify-content:space-between;}@media (max-width: 992px){footer .site-footer__navigation-wrap{-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;padding-bottom:18px;}}@media (max-width: 768px){footer .site-footer__navigation-wrap{padding-top:24px;padding-bottom:0;-webkit-align-items:flex-start;-webkit-box-align:flex-start;-ms-flex-align:flex-start;align-items:flex-start;}}footer .site-footer__socials{margin-left:auto;}@media (max-width: 992px){footer .site-footer__socials{margin-left:0;}}@media (max-width: 768px){footer .site-footer__socials{margin-right:auto;}}footer .site-footer__socials li{margin-bottom:0;}footer .site-footer__disclaimer{padding-top:64px;padding-bottom:32px;position:relative;opacity:0.5;}@media (max-width: 768px){footer .site-footer__disclaimer{padding-top:18px;padding-bottom:25px;}}footer .site-footer__disclaimer:after{content:'';display:block;position:absolute;left:0;bottom:0;right:0;height:1px;background-color:currentColor;opacity:0.2;}footer .site-footer__copy{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-pack:justify;-webkit-justify-content:space-between;justify-content:space-between;-webkit-align-items:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;padding-top:32px;font-size:18px;}@media (max-width: 768px){footer .site-footer__copy{padding-top:15px;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;-webkit-align-items:flex-start;-webkit-box-align:flex-start;-ms-flex-align:flex-start;align-items:flex-start;}footer .site-footer__copy>div:first-child{margin-bottom:15px;}}footer .site-footer__copy a{color:inherit;}footer .site-footer__copy-lp span{margin-right:5px;}footer .site-footer__copy-lp img{margin-right:30px;}@media (max-width: 768px){footer .site-footer__copy-lp img{margin-right:0;margin-bottom:15px;}}@media (min-width: 769px){footer .site-footer__copy-lp{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-align-items:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;}}footer .site-footer__bottom{padding-top:96px;padding-bottom:96px;}@media (max-width: 768px){footer .site-footer__bottom{padding-top:64px;padding-bottom:64px;}}footer .footer-contact{margin-bottom:13px;}footer .footer-contact p{margin-bottom:0;opacity:.7;}footer .footer-contact ul{list-style:none;padding-left:0;margin:0;}footer .footer-subscribe{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-pack:justify;-webkit-justify-content:space-between;justify-content:space-between;-webkit-align-items:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;}@media (max-width: 768px){footer .footer-subscribe{-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;}}footer .footer-subscribe__title-wrap{padding-right:50px;}@media (max-width: 768px){footer .footer-subscribe__title-wrap{padding-right:0;padding-bottom:32px;}}footer .footer-subscribe__title{margin-bottom:8px;}footer .footer-subscribe__sub-title{margin-bottom:0;}footer .footer-subscribe__form{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;width:100%;max-width:330px;margin-bottom:0;position:relative;}footer .footer-subscribe__submit{margin-top:16px;}footer .footer-subscribe__success-msg{position:absolute;top:100%;width:100%;text-align:center;opacity:.7;padding-top:10px;}footer .footer-navigation{list-style:none;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-flex-wrap:wrap;-webkit-flex-wrap:wrap;-ms-flex-wrap:wrap;flex-wrap:wrap;padding:0 20px 0 0;margin:0 -30px;}@media (max-width: 992px){footer .footer-navigation{margin-bottom:36px;}}@media (max-width: 768px){footer .footer-navigation{-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;margin:0 0 36px;}}footer .footer-navigation__item{margin:0 30px;}@media (max-width: 768px){footer .footer-navigation__item{margin:0 0 10px;}}
34footer .footer-navigation__link{color:inherit;-webkit-text-decoration:none;text-decoration:none;font-size:14px;line-height:14px;background-color:transparent;border:none;font-family:inherit;cursor:pointer;padding-left:0;padding-right:0;-webkit-transition:0.2s all;transition:0.2s all;}@media (hover: hover) and (pointer: fine){footer .footer-navigation__link:hover{color:var(--themeBeige);}}footer .lp-socials li{margin-bottom:14px;}footer .sync-icon{display:none;margin-right:10px;-webkit-box-pack:center;-ms-flex-pack:center;-webkit-justify-content:center;justify-content:center;-webkit-align-items:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;}footer .success-icon{display:none;margin-right:10px;-webkit-transition:0s;transition:0s;visibility:visible;}footer .loading{pointer-events:none;}footer .loading .sync-icon{display:-webkit-inline-box;display:-webkit-inline-flex;display:-ms-inline-flexbox;display:inline-flex;}footer .success .success-icon{display:block;}footer .lp-footer-disclaimers{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;gap:20px;margin-top:32px;padding-top:32px;}@media (max-width: 768px){footer .lp-footer-disclaimers{padding-top:15px;margin-top:15px;}}body>div.mobile-contact-wrapper{z-index:200;position:relative;}body>div.mobile-contact-wrapper .lp-ico:before{content:'';width:1em;height:1em;background-color:currentColor;-webkit-background-size:cover;background-size:cover;display:block;-webkit-mask-position:center;-webkit-mask-position:center;mask-position:center;-webkit-mask-size:contain;-webkit-mask-size:contain;mask-size:contain;-webkit-mask-repeat:no-repeat;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-transition:all .3s;transition:all .3s;}body>div.mobile-contact-wrapper .lp-ico-search:before{-webkit-mask-image:url("data:image/svg+xml,%3Csvg width='25' height='25' viewB
34ox='0 0 25 25' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M21.6231 21.3002L17.1372 16.8143M19.5606 10.9877C19.5606 15.5441 15.8669 19.2377 11.3106 19.2377C6.75421 19.2377 3.06055 15.5441 3.06055 10.9877C3.06055 6.43135 6.75421 2.73769 11.3106 2.73769C15.8669 2.73769 19.5606 6.43135 19.5606 10.9877Z' stroke='black' stroke-width='1.82813' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");-webkit-mask-image:url("data:image/svg+xml,%3Csvg width='25' height='25' viewBox='0 0 25 25' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M21.6231 21.3002L17.1372 16.8143M19.5606 10.9877C19.5606 15.5441 15.8669 19.2377 11.3106 19.2377C6.75421 19.2377 3.06055 15.5441 3.06055 10.9877C3.06055 6.43135 6.75421 2.73769 11.3106 2.73769C15.8669 2.73769 19.5606 6.43135 19.5606 10.9877Z' stroke='black' stroke-width='1.82813' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");mask-image:url("data:image/svg+xml,%3Csvg width='25' height='25' viewBox='0 0 25 25' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M21.6231 21.3002L17.1372 16.8143M19.5606 10.9877C19.5606 15.5441 15.8669 19.2377 11.3106 19.2377C6.75421 19.2377 3.06055 15.5441 3.06055 10.9877C3.06055 6.43135 6.75421 2.73769 11.3106 2.73769C15.8669 2.73769 19.5606 6.43135 19.5606 10.9877Z' stroke='black' stroke-width='1.82813' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");}body>div.mobile-contact-wrapper .lp-ico-arrow-top:before{-webkit-mask-image:url("data:image/svg+xml,%3Csvg width='17' height='16' viewBox='0 0 17 16' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M12.8716 9.99976L8.87158 5.99976L4.87158 9.99976' stroke='white' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");-webkit-mask-image:url("data:image/svg+xml,%3Csvg width='17' height='16' viewBox='0 0 17 16' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M12.8716 9.99976L8.87158 5.99976L4.87158 9.99976' stroke='white' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");mask-image:url("data:image/svg+xml,%3Csvg width='17' height='16' viewBox='0 0 17 16' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M12.8716 9.99976L8.87158 5.99976L4.87158 9.99976' stroke='white' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");}body>div.mobile-contact-wrapper .lp-ico-scroll-top:before{-webkit-mask-image:url("data:image/svg+xml,%3Csvg width='24' height='24' viewBox='0 0 24 24' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M21 3H3M12 21V7M12 7L5 14M12 7L19 14' stroke='white' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E ");-webkit-mask-image:url("data:image/svg+xml,%3Csvg width='24' height='24' viewBox='0 0 24 24' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M21 3H3M12 21V7M12 7L5 14M12 7L19 14' stroke='white' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E ");mask-image:url("data:image/svg+xml,%3Csvg width='24' height='24' viewBox='0 0 24 24' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M21 3H3M12 21V7M12 7L5 14M12 7L19 14' stroke='white' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E ");}body>div.mobile-contact-wrapper .lp-ico-close:before{-webkit-mask-image:url("data:image/svg+xml,%3Csvg width='24' height='24' viewBox='0 0 24 24' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M17.5986 6.32617L5.59863 18.3262M5.59863 6.32617L17.5986 18.3262' stroke='white' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E ");-webkit-mask-image:url("data:image/svg+xml,%3Csvg width='24' height='24' viewBox='0 0 24 24' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M17.5986 6.32617L5.59863 18.3262M5.59863 6.32617L17.5986 18.3262' stroke='white' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E ");mask-image:url("data:image/svg+xml,%3Csvg width='24' height='24' viewBox='0 0 24 24' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M17.5986 6.32617L5.59863 18.3262M5.59863 6.32617L17.5986 18.3262' stroke='white' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E ");}body>div.mobile-contact-wrapper .lp-ico-phone:before{-webkit-mask-image:url("data:image/svg+xml,%3Csvg width='24' height='24' viewBox='0 0 24 24' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M13.8
34699 6.57142C14.7459 6.74157 15.551 7.16808 16.1821 7.79637C16.8132 8.42465 17.2416 9.22612 17.4126 10.0982M13.8699 3C15.6899 3.20128 17.3871 4.01265 18.6827 5.3009C19.9784 6.58914 20.7955 8.27768 21 10.0893M10.4412 13.592C9.36357 12.5192 8.51264 11.3061 7.88843 10.0118C7.83474 9.90047 7.80789 9.84481 7.78726 9.77437C7.71397 9.52405 7.76662 9.21668 7.91909 9.00469C7.962 8.94504 8.01326 8.894 8.11578 8.79194C8.42933 8.47979 8.58611 8.32372 8.6886 8.16677C9.07515 7.57491 9.07515 6.81189 8.6886 6.22002C8.58611 6.06308 8.42933 5.90701 8.11578 5.59486L7.94101 5.42087C7.46438 4.94637 7.22607 4.70912 6.97012 4.58024C6.4611 4.32392 5.85996 4.32392 5.35093 4.58024C5.09499 4.70912 4.85667 4.94637 4.38004 5.42087L4.23867 5.56161C3.76367 6.03449 3.52617 6.27093 3.34478 6.59239C3.1435 6.94909 2.99878 7.5031 3.00001 7.91222C3.00111 8.28092 3.07295 8.53291 3.21664 9.03688C3.98881 11.7453 5.44574 14.3009 7.58743 16.4331C9.72911 18.5652 12.2963 20.0156 15.0168 20.7843C15.523 20.9274 15.7761 20.9989 16.1465 21C16.5575 21.0012 17.114 20.8571 17.4723 20.6568C17.7952 20.4762 18.0327 20.2397 18.5077 19.7669L18.649 19.6261C19.1257 19.1516 19.364 18.9144 19.4934 18.6596C19.7509 18.1528 19.7509 17.5544 19.4934 17.0476C19.364 16.7928 19.1257 16.5556 18.649 16.0811L18.4743 15.9071C18.1607 15.5949 18.0039 15.4388 17.8463 15.3368C17.2518 14.952 16.4853 14.952 15.8908 15.3368C15.7332 15.4388 15.5764 15.5949 15.2628 15.9071C15.1603 16.0091 15.1091 16.0602 15.0491 16.1029C14.8362 16.2547 14.5274 16.3071 14.276 16.2341C14.2052 16.2136 14.1493 16.1869 14.0375 16.1334C12.7374 15.512 11.5189 14.6648 10.4412 13.592Z' stroke='white' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");-webkit-mask-image:url("data:image/svg+xml,%3Csvg width='24' height='24' viewBox='0 0 24 24' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M13.8699 6.57142C14.7459 6.74157 15.551 7.16808 16.1821 7.79637C16.8132 8.42465 17.2416 9.22612 17.4126 10.0982M13.8699 3C15.6899 3.20128 17.3871 4.01265 18.6827 5.3009C19.9784 6.58914 20.7955 8.27768 21 10.0893M10.4412 13.592C9.36357 12.5192 8.51264 11.3061 7.88843 10.0118C7.83474 9.90047 7.80789 9.84481 7.78726 9.77437C7.71397 9.52405 7.76662 9.21668 7.91909 9.00469C7.962 8.94504 8.01326 8.894 8.11578 8.79194C8.42933 8.47979 8.58611 8.32372 8.6886 8.16677C9.07515 7.57491 9.07515 6.81189 8.6886 6.22002C8.58611 6.06308 8.42933 5.90701 8.11578 5.59486L7.94101 5.42087C7.46438 4.94637 7.22607 4.70912 6.97012 4.58024C6.4611 4.32392 5.85996 4.32392 5.35093 4.58024C5.09499 4.70912 4.85667 4.94637 4.38004 5.42087L4.23867 5.56161C3.76367 6.03449 3.52617 6.27093 3.34478 6.59239C3.1435 6.94909 2.99878 7.5031 3.00001 7.91222C3.00111 8.28092 3.07295 8.53291 3.21664 9.03688C3.98881 11.7453 5.44574 14.3009 7.58743 16.4331C9.72911 18.5652 12.2963 20.0156 15.0168 20.7843C15.523 20.9274 15.7761 20.9989 16.1465 21C16.5575 21.0012 17.114 20.8571 17.4723 20.6568C17.7952 20.4762 18.0327 20.2397 18.5077 19.7669L18.649 19.6261C19.1257 19.1516 19.364 18.9144 19.4934 18.6596C19.7509 18.1528 19.7509 17.5544 19.4934 17.0476C19.364 16.7928 19.1257 16.5556 18.649 16.0811L18.4743 15.9071C18.1607 15.5949 18.0039 15.4388 17.8463 15.3368C17.2518 14.952 16.4853 14.952 15.8908 15.3368C15.7332 15.4388 15.5764 15.5949 15.2628 15.9071C15.1603 16.0091 15.1091 16.0602 15.0491 16.1029C14.8362 16.2547 14.5274 16.3071 14.276 16.2341C14.2052 16.2136 14.1493 16.1869 14.0375 16.1334C12.7374 15.512 11.5189 14.6648 10.4412 13.592Z' stroke='white' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");mask-image:url("data:image/svg+xml,%3Csvg width='24' height='24' viewBox='0 0 24 24' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M13.8699 6.57142C14.7459 6.74157 15.551 7.16808 16.1821 7.79637C16.8132 8.42465 17.2416 9.22612 17.4126 10.0982M13.8699 3C15.6899 3.20128 17.3871 4.01265 18.6827 5.3009C19.9784 6.58914 20.7955 8.27768 21 10.0893M10.4412 13.592C9.36357 12.5192 8.51264 11.3061 7.88843 10.0118C7.83474 9.90047 7.80789 9.84481 7.78726 9.77437C7.71397 9.52405 7.76662 9.21668 7.91909 9.00469C7.962 8.94504 8.01326 8.894 8.11578 8.79194C8.42933 8.47979 8.58611 8.32372 8.6886 8.16677C9.07515 7.57491 9.07515 6.81189 8.6886 6.22002C8.58611 6.06308 8.42933 5.90701 8.11578 5.59486L7.94101 5.42087C7.46438 4.94637 7.22607 4.70912 6.97012 4.58024C6.4611 4.32392 5.85996 4.32392 5.35093 4.58024C5.09499 4.70912 4.85667 4.94637 4.38004 5.42087L4.23867 5.56161C3.76367 6.03449 3.52617 6.27093 3.34478 6.59239C3.1435 6.94909 2.99878 7.5031 3.00001 7.91222C3.00111 8.28092 3.07295 8.53291 3.21664 9.03688C3.98881 11.7453 5.44574 14.3009 7.58743 16.4331C9.72911 18.5652 12.2963 20.0156 15.0168 20.7843C15.523 20.9274 15.7761 20.9989 16.1465 21C16.5575 21.0012 17.114 20.8571 17.4723 20.6568C17.7952 20.4762 18.0327 20.2397 18.5077 19.7669L18.649 19.6261C19.1257 19.1516 19.364 18.9144 19.4934 18.6596C19.7509 18.1528 19.7509 17.5544 19.4934 17.0476C19.364 16.7928 19.1257 16.5556 18.649 16.0811L18.4743 15.9071C18.1607 15.5949 18.0039 15.4388 17.8463 15.3368C17.2518 14.952 16.4853 14.952 15.8908 15.3368C15.7332 15.4388 15.5764 15.5949 15.2628 15.9071C15.1603 16.0091 15.1091 16.0602 15.0491 16.1029C14.8362 16.2547 14.5274 16.3071 14.276 16.2341C14.2052 16.2136 14.1493 16.1869 14.0375 16.1334C12.7374 15.512 11.5189 14.6648 10.4412 13.592Z' stroke='white' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");}body>div.mobile-contact-wrapper .lp-ico-sms:before{-webkit-mask-image:url("data:image/svg+xml,%3Csvg width='24' height='24' viewBox='0 0 24 24' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M21 11.5C21.0034 12.8199 20.6951 14.1219 20.1 15.3C19.3944 16.7117 18.3098 17.8992 16.9674 18.7293C15.6251 19.5594 14.0782 19.9994 12.5 20C11.1801 20.0034 9.87812 19.6951 8.7 19.1L3 21L4.9 15.3C4.30493 14.1219 3.99656 12.8199 4 11.5C4.00061 9.92176 4.44061 8.37485 5.27072 7.03255C6.10083 5.69025 7.28825 4.60557 8.7 3.9C9.87812 3.30493 11.1801 2.99656 12.5 3H13C15.0843 3.11499 17.053 3.99476 18.5291 5.47086C20.0052 6.94695 20.885 8.91565 21 11V11.5Z' stroke='white' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");-webkit-mask-image:url("data:image/svg+xml,%3Csvg width='24' height='24' viewBox='0 0 24 24' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M21 11.5C21.0034 12.8199 20.6951 14.1219 20.1 15.3C19.3944 16.7117 18.3098 17.8992 16.9674 18.7293C15.6251 19.5594 14.0782 19.9994 12.5 20C11.1801 20.0034 9.87812 19.6951 8.7 19.1L3 21L4.9 15.3C4.30493 14.1219 3.99656 12.8199 4 11.5C4.00061 9.92176 4.44061 8.37485 5.27072 7.03255C6.10083 5.69025 7.28825 4.60557 8.7 3.9C9.87812 3.30493 11.1801 2.99656 12.5 3H13C15.0843 3.11499 17.053 3.99476 18.5291 5.47086C20.0052 6.94695 20.885 8.91565 21 11V11.5Z' stroke='white' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");mask-image:url("data:image/svg+xml,%3Csvg width='24' height='24' viewBox='0 0 24 24' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M21 11.5C21.0034 12.8199 20.6951 14.1219 20.1 15.3C19.3944 16.7117 18.3098 17.8992 16.9674 18.7293C15.6251 19.5594 14.0782 19.9994 12.5 20C11.1801 20.0034 9.87812 19.6951 8.7 19.1L3 21L4.9 15.3C4.30493 14.1219 3.99656 12.8199 4 11.5C4.00061 9.92176 4.44061 8.37485 5.27072 7.03255C6.10083 5.69025 7.28825 4.60557 8.7 3.9C9.87812 3.30493 11.1801 2.99656 12.5 3H13C15.0843 3.11499 17.053 3.99476 18.5291 5.47086C20.0052 6.94695 20.885 8.91565 21 11V11.5Z' stroke='white' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");}body>div.mobile-contact-wrapper .lp-ico-email:before{-webkit-mask-image:url("data:image/svg+xml,%3Csvg width='24' height='24' viewBox='0 0 24 24' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M21.5 18L14.85
3471 12M9.14286 12L2.50003 18M2 7L10.1649 12.7154C10.8261 13.1783 11.1567 13.4097 11.5163 13.4993C11.8339 13.5785 12.1661 13.5785 12.4837 13.4993C12.8433 13.4097 13.1739 13.1783 13.8351 12.7154L22 7M6.8 20H17.2C18.8802 20 19.7202 20 20.362 19.673C20.9265 19.3854 21.3854 18.9265 21.673 18.362C22 17.7202 22 16.8802 22 15.2V8.8C22 7.11984 22 6.27976 21.673 5.63803C21.3854 5.07354 20.9265 4.6146 20.362 4.32698C19.7202 4 18.8802 4 17.2 4H6.8C5.11984 4 4.27976 4 3.63803 4.32698C3.07354 4.6146 2.6146 5.07354 2.32698 5.63803C2 6.27976 2 7.11984 2 8.8V15.2C2 16.8802 2 17.7202 2.32698 18.362C2.6146 18.9265 3.07354 19.3854 3.63803 19.673C4.27976 20 5.11984 20 6.8 20Z' stroke='white' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");-webkit-mask-image:url("data:image/svg+xml,%3Csvg width='24' height='24' viewBox='0 0 24 24' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M21.5 18L14.8571 12M9.14286 12L2.50003 18M2 7L10.1649 12.7154C10.8261 13.1783 11.1567 13.4097 11.5163 13.4993C11.8339 13.5785 12.1661 13.5785 12.4837 13.4993C12.8433 13.4097 13.1739 13.1783 13.8351 12.7154L22 7M6.8 20H17.2C18.8802 20 19.7202 20 20.362 19.673C20.9265 19.3854 21.3854 18.9265 21.673 18.362C22 17.7202 22 16.8802 22 15.2V8.8C22 7.11984 22 6.27976 21.673 5.63803C21.3854 5.07354 20.9265 4.6146 20.362 4.32698C19.7202 4 18.8802 4 17.2 4H6.8C5.11984 4 4.27976 4 3.63803 4.32698C3.07354 4.6146 2.6146 5.07354 2.32698 5.63803C2 6.27976 2 7.11984 2 8.8V15.2C2 16.8802 2 17.7202 2.32698 18.362C2.6146 18.9265 3.07354 19.3854 3.63803 19.673C4.27976 20 5.11984 20 6.8 20Z' stroke='white' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");mask-image:url("data:image/svg+xml,%3Csvg width='24' height='24' viewBox='0 0 24 24' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M21.5 18L14.8571 12M9.14286 12L2.50003 18M2 7L10.1649 12.7154C10.8261 13.1783 11.1567 13.4097 11.5163 13.4993C11.8339 13.5785 12.1661 13.5785 12.4837 13.4993C12.8433 13.4097 13.1739 13.1783 13.8351 12.7154L22 7M6.8 20H17.2C18.8802 20 19.7202 20 20.362 19.673C20.9265 19.3854 21.3854 18.9265 21.673 18.362C22 17.7202 22 16.8802 22 15.2V8.8C22 7.11984 22 6.27976 21.673 5.63803C21.3854 5.07354 20.9265 4.6146 20.362 4.32698C19.7202 4 18.8802 4 17.2 4H6.8C5.11984 4 4.27976 4 3.63803 4.32698C3.07354 4.6146 2.6146 5.07354 2.32698 5.63803C2 6.27976 2 7.11984 2 8.8V15.2C2 16.8802 2 17.7202 2.32698 18.362C2.6146 18.9265 3.07354 19.3854 3.63803 19.673C4.27976 20 5.11984 20 6.8 20Z' stroke='white' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");}body>div.mobile-contact-wrapper a{-webkit-text-decoration:none;text-decoration:none;}body>div.mobile-contact-wrapper .btn-holder{bottom:-50px;left:19px;position:fixed;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;gap:13px;opacity:0;-webkit-transition:all .3s;transition:all .3s;}@media (max-width: 1024px){body>div.mobile-contact-wrapper .btn-holder{right:19px;padding:0 48px;-webkit-box-pack:center;-ms-flex-pack:center;-webkit-justify-content:center;justify-content:center;}}body>div.mobile-contact-wrapper .btn-holder.visible{bottom:28px;opacity:1;}body>div.mobile-contact-wrapper .search-btn{width:48px;height:48px;border-radius:999px;font-size:0;line-height:0;display:-webkit-inline-box;display:-webkit-inline-flex;display:-ms-inline-flexbox;display:inline-flex;-webkit-transition:all .3s;transition:all .3s;padding:12px;color:#141414;background:#fff;}@media (max-width: 1024px){body>div.mobile-contact-wrapper .search-btn{position:absolute;left:0;bottom:0;}}body>div.mobile-contact-wrapper .search-btn:before{width:24px;height:24px;}body>div.mobile-contact-wrapper .connect-btn{height:48px;padding:0 24px;display:-webkit-inline-box;display:-webkit-inline-flex;display:-ms-inline-flexbox;display:inline-flex;-webkit-align-items:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;gap:4px;text-transform:uppercase;border-radius:25px;color:var(--pillTextColor);background:var(--pillBackgroundColor);border:1px solid rgba(255, 255, 255, 0.2);cursor:pointer;font-size:10px;font-style:normal;font-weight:500;line-height:16px;letter-spacing:0.4px;-webkit-flex-direction:row-reverse;-ms-flex-direction:row-reverse;flex-direction:row-reverse;-webkit-transition:all .4s;transition:all .4s;box-shadow:0 0 0 0 rgba(255, 255, 255, 0.2);-webkit-animation:pulsate 2s ease infinite;animation:pulsate 2s ease infinite;}body>div.mobile-contact-wrapper .connect-btn:before{width:16px;height:16px;}body>div.mobile-contact-wrapper .pop-up{display:none;position:fixed;bottom:0;left:0;width:100%;-webkit-transition:all .3s;transition:all .3s;border-radius:16px 16px 0 0;color:var(--textColor);background:var(--backgroundColor);}@media (min-width: 1025px){body>div.mobile-contact-wrapper .pop-up{max-width:637px;border-radius:16px;bottom:28px;left:19px;}}body>div.mobile-contact-wrapper .pop-up .lp-container{padding-top:24px;padding-bottom:24px;-webkit-box-pack:center;-ms-flex-pack:center;
34-webkit-justify-content:center;justify-content:center;}@media (min-width: 1025px){body>div.mobile-contact-wrapper .pop-up .lp-container{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;padding-top:40px;padding-bottom:40px;min-height:450px;padding-left:56px;padding-right:56px;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;}}body>div.mobile-contact-wrapper .pop-up .close-btn{width:44px;height:44px;background-color:transparent;border-color:transparent;display:block;margin:24px auto 0;-webkit-box-pack:center;-ms-flex-pack:center;-webkit-justify-content:center;justify-content:center;-webkit-align-items:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;color:var(--textColor);cursor:pointer;padding:10px;border:0;}body>div.mobile-contact-wrapper .pop-up .close-btn:before{width:24px;height:24px;}@media (min-width: 1025px){body>div.mobile-contact-wrapper .pop-up .close-btn{position:absolute;top:14px;right:12px;margin:0;}}body>div.mobile-contact-wrapper .pop-up .icon-btn{width:44px;height:44px;border-radius:999px;display:-webkit-inline-box;display:-webkit-inline-flex;display:-ms-inline-flexbox;display:inline-flex;-webkit-box-pack:center;-ms-flex-pack:center;-webkit-justify-content:center;justify-content:center;-webkit-align-items:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;margin-left:8px;color:var(--textColor);position:relative;}body>div.mobile-contact-wrapper .pop-up .icon-btn:after{position:absolute;bottom:0;width:100%;left:0;border-radius:999px;
34height:100%;content:'';border:solid 1px var(--textColor);opacity:.3;}body>div.mobile-contact-wrapper .pop-up .icon-btn:before{width:24px;height:24px;}body>div.mobile-contact-wrapper .success-block{display:none;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;}@media (min-width: 1025px){body>div.mobile-contact-wrapper .success-block{-webkit-align-items:flex-start;-webkit-box-align:flex-start;-ms-flex-align:flex-start;align-items:flex-start;}}body>div.mobile-contact-wrapper .success-block .lp-btn{margin-top:24px;}body>div.mobile-contact-wrapper .contact-form{margin:0;}@media (max-width: 1024px){body>div.mobile-contact-wrapper .contact-form__item{width:100%;}}body>div.mobile-contact-wrapper .contact-form__item .lp-input{width:100%;}body>div.mobile-contact-wrapper .contact-form__item .is-placeholder{color:#818181;}body>div.mobile-contact-wrapper .contact-form__item+.contact-form__item{margin-top:16px;}body>div.mobile-contact-wrapper .contact-form.show-success .contact-form__container,body>div.mobile-contact-wrapper .contact-form.success .contact-form__container{display:none;}body>div.mobile-contact-wrapper .contact-form.show-success .success-block,body>div.mobile-contact-wrapper .contact-form.success .success-block{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;}body>div.mobile-contact-wrapper .contact-form .lp-disclaimer{margin-top:16px;}body>div.mobile-contact-wrapper .contact-form .lp-h3{margin-bottom:16px;}body>div.mobile-contact-wrapper .success-message{font-size:22px;}@-webkit-keyframes pulsate{0%{box-shadow:0 0 0 0 rgba(255, 255, 255, 0.2);}50%{box-shadow:0 0 4px 6px rgba(255, 255, 255, .5);}100%{box-shadow:0 0 12px 12px rgba(255, 255, 255, 0);}} 35 </style> 36 <style id="custom-style-css">.button-style-1{color:#FFFFFF!important;background-color:#B1191E!important;border-color:#B1191E!important;}.button-style-1:hover{color:#B1191E!important;background-color:rgba(0, 0, 0, 0)!important;border-color:#B1191E!important;}.hyperlink-style-1{color:#000000!important;}.hyperlink-style-1:hover{color:#B1191E!important;}.social_icons-style-1 a{border-color:#B1191E!important;background-color:#B1191E!important;color:#FFFFFF!important;}.social_icons-style-1 a:hover{border-color:#B1191E!important;background-color:rgba(0, 0, 0, 0)!important;color:#B1191E!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:#B1191E!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:#B1191E!important;border-color:#B1191E!important;}.pagination-style-1 li.active a{color:#FFFFFF!important;background-color:#B1191E!important;border-color:#B1191E!important;}.pagination-style-2 li a{color:#FFFFFF!important;background-color:rgba(0, 0, 0, 0)!important;border-color:rgba(0, 0, 0, 0)!important;}.pagination-style-2 li a:hover{color:#B1191E!important;background-color:#FFFFFF!important;border-color:#FFFFFF!important;}.pagination-style-2 li.active a{color:#B1191E!important;background-color:#FFFFFF!important;border-color:#FFFFFF!important;}.social_icons-style-2 a{border-color:#FFFFFF!important;background-color:#FFFFFF!important;color:#B1191E!important;}.social_icons-style-2 a:hover{border-color:#FFFFFF!important;background-color:rgba(0, 0, 0, 0)!important;color:#FFFFFF!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:#B1191E!important;background-color:#FFFFFF!important;border-color:#FFFFFF!important;}.button-style-2:hover{color:#FFFFFF!important;background-color:rgba(0, 0, 0, 0)!important;border-color:#FFFFFF!important;}.hyperlink-style-2{color:#FFFFFF!important;}.hyperlink-style-2:hover{color:#FFFFFF!important;}</style> 37 38 39
39<script> 40 (function utmParse() { 41 const url = new URL(window.location.href); 42 if (url.search) { 43 const utm = url.search.split(/\?|&/g).reduce((acc, current) => { 44 const matched = current.match(/utm_(content|campaign|medium|source|term|id)|gclid/g); 45 if (matched) { 46 acc[matched[0]] = current.replace(`${matched[0]}=`, ''); 47 } 48 return acc; 49 }, {}); 50 if (Object.keys(utm).length) { 51 window.localStorage.setItem('utm_vars', JSON.stringify(utm)); 52 } else { 53 // Remove old utm vars 54 window.localStorage.removeItem('utm_vars'); 55 } 56 } 57 // Set referrer in local storage 58 // This is meant to be stored once to avoid self-referrals and to have the first referrer only. 59 if (!window.localStorage.getItem('referrer')) { 60 // Capture the Referrer URL 61 const referrer = document.referrer; 62 if (referrer) { 63 // Only store the referrer if it comes from outside the current domain (to avoid self-referrals) 64 const currentDomain = window.location.hostname; 65 const referrerDomain = new URL(referrer).hostname; 66 67 if (referrerDomain !== currentDomain) { 68 // Store it in localStorage 69 localStorage.setItem('referrer', referrer); 70 } 71 } 72 } 73 })(); 74 </script>
74 75 <!-- Bing Webmaster Tools Verification -->
76<script> 77 // Check if on the homepage before the body loads 78 if (window.location.pathname === '/' || window.location.pathname === '/index.html') { 79 document.write('<meta name="msvalidate.01" content="18166674FE9A03570D2BCFB935D429DC" />'); 80 } 81</script>
81 82 <!-- GOOGLE TRANSLATE STYLES are moved to external CSS --> 83</head> 84<body> 85 <div id="google_translate_element" style="display: none"></div> 86 87 <nav id="global-navbar" data-el-id="b395957e-29e6-4bd5-99ee-352fbee7732a"> 88 <div class="header theme-auto"> 89 <div class="lp-container header__container"> 90 <div class="header__logo"> 91 <a href="/" class="logo"> 92 <img src="https://media-production.lp-cdn.com/cdn-cgi/image/format=auto,quality=85/https://media-production.lp-cdn.com/media/f5df3921-2e67-4a84-bd6b-51eea79a151d" 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/f5df3921-2e67-4a84-bd6b-51eea79a151d 320w, https://media-production.lp-cdn.com/cdn-cgi/image/format=auto,quality=85/https://media-production.lp-cdn.com/media/f5df3921-2e67-4a84-bd6b-51eea79a151d 721w" sizes="500px" class="logo__img dark" alt=""> 93 <img src="https://media-production.lp-cdn.com/cdn-cgi/image/format=auto,quality=85/https://media-production.lp-cdn.com/media/fc3fa460-bef1-4b9d-bd34-2529f8e68afe" 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/fc3fa460-bef1-4b9d-bd34-2529f8e68afe 320w, https://media-production.lp-cdn.com/cdn-cgi/image/format=auto,quality=85/https://media-production.lp-cdn.com/media/fc3fa460-bef1-4b9d-bd34-2529f8e68afe 721w" sizes="500px" class="logo__img light" alt=""> 94 </a> 95 </div> 96 97 <div class="header__right"> 98 <ul class="navigation"> 99 <li class="navigation__item"> 100 <a href="/team" class="navigation__link">Meet The Team</a> 101 </li> 102 <li class="navigation__item"> 103 <a href="/properties/sold" class="navigation__link">Past Transactions </a> 104 </li> 105 <li class="navigation__item"> 106 <a href="/neighborhoods" class="navigation__link">Neighborhoods</a> 107 </li> 108 <li class="navigation__item"> 109 <a data-type="CONTACT_US" class="navigation__link">Contact Us</a> 110 </li> 111 <li class="navigation__item"> 112 <a href="tel:8082852486" class="navigation__link">(808) 285-2486</a> 113 </li> 114 115 </ul> 116 117 118 119 120 121 <button class="hamburger-component header__hamburger">
122 <svg width="60" height="60" viewBox="0 0 60 60" fill="none" xmlns="http://www.w3.org/2000/svg"> 123 <circle cx="30" cy="30" r="30" /> 124 <rect class="line line--top" x="18" y="26" width="24" height="2" /> 125 <rect class="line line--bottom" x="30" y="32" width="12" height="2" /> 126 </svg> 127 </button> 128 </div> 129 130 </div> 131</div> 132 </nav> 133 <div id="global-sidemenu" class="sidemenu" data-el-id="1575f740-92ae-4292-919d-936adffb0f46"> 134 <div class="sidemenu__container"> 135 136 137 <div class="sidemenu__img" style="background-image: url('https://media-production.lp-cdn.com/cdn-cgi/image/format=auto,quality=85/https://media-production.lp-cdn.com/media/9097d187-1833-427c-8239-a26814fc5221');"> 138 </div> 139 140 <div class="sidemenu__wrapper"> 141 <div class="sidemenu__header"> 142 <button class="sidemenu__toggle toggle sidemenu__toggle--right">Close Menu</button> 143 </div> 144 145 <div class="sidemenu__nav"> 146 <ul class="navigation"> 147 <li class="navigation__item" > 148 <a href="/" class="navigation__link" >Home</a> 149 </li> 150 <li class="navigation__item" > 151 <a href="/team" class="navigation__link" >Meet the Team</a> 152 </li> 153 <li class="navigation__item sub-nav-container" data-wow-delay="2.2s"> 154 <a href="#" class="navigation__link"> 155 Properties 156 <svg width="24" height="24" viewBox="0 0 24 24" fill="none" class="arrow" xmlns="http://www.w3.org/2000/svg"> 157 <path d="M7.41 8.58984L12 13.1698L16.59 8.58984L18 9.99984L12 15.9998L6 9.99984L7.41 8.58984Z" fill="currentColor"/> 158 </svg> 159 </a> 160 <ul class="sub-nav"> 161 <li class="sub-nav__item"><a href="/properties/sale" class="sub-nav__link feature">Featured Properties</a></li> 162 <li class="sub-nav__item"><a href="/properties/sold" class="sub-nav__link feature">Past Transactions</a></li> 163 </ul> 164 165 </li> 166 <li class="navigation__item" > 167 <a href="/home-search/listings" class="navigation__link" >Home Search</a> 168 </li> 169 <li class="navigation__item" > 170 <a href="/home-valuation" class="navigation__link" >Home Valuation</a> 171 </li> 172 <li class="navigation__item" > 173 <a href="/neighborhoods" class="navigation__link" >Neighborhoods</a> 174 </li> 175 <li class="navigation__item" > 176 <a href="/testimonials" class="navigation__link" >Testimonials</a> 177 </li> 178 <li class="navigation__item sub-nav-container" data-wow-delay="2.7s"> 179 <a href="#" class="navigation__link"> 180 Resources 181 <svg width="24" height="24" viewBox="0 0 24 24" fill="none" class="arrow" xmlns="http://www.w3.org/2000/svg"> 182 <path d="M7.41 8.58984L12 13.1698L16.59 8.58984L18 9.99984L12 15.9998L6 9.99984L7.41 8.58984Z" fill="currentColor"/> 183 </svg> 184 </a> 185 <ul class="sub-nav"> 186 <li class="sub-nav__item"><a href="/buyers-guide" class="sub-nav__link feature">Buyer's Guide</a></li> 187 <li class="sub-nav__item"><a href="/sellers-guide" class="sub-nav__link feature">Seller's Guide</a></li> 188 </ul> 189 190 </li> 191 <li class="navigation__item" > 192 <a href="/blog" class="navigation__link" >Market News/Blog</a> 193 </li> 194 <li class="navigation__item" > 195 <a href="/contact-us" class="navigation__link" >Contact Us</a> 196 </li> 197 <li class="navigation__item" > 198 <a href="/home-search/account" class="navigation__link" >My Search Portal</a> 199 </li> 200 </ul> 201 </div> 202 203 <div class="sidemenu__socials"> 204 <ul class="lp-socials social_icons-style-1 sidemenu-social-links"> 205 206 <li> 207 <a 208 href="https://www.instagram.com/thechoigroup/?hl=en" 209 class="lp-socials__link socials__item" 210 data-wow-delay="0s" 211 target="_blank" 212 aria-label="INSTAGRAM" 213 >
214 <span class="lp-i lp-i--instagram" aria-hidden="true"></span> 215 </a> 216 </li> 217 <li> 218 <a 219 href="https://www.linkedin.com/in/cedric-choi-b439451a2/" 220 class="lp-socials__link socials__item" 221 data-wow-delay="0.1s" 222 target="_blank" 223 aria-label="LINKEDIN" 224 > 225 <span class="lp-i lp-i--linkedin" aria-hidden="true"></span> 226 </a> 227 </li> 228 <li> 229 <a 230 href="https://www.google.com/search?sca_esv=7f9887bee47bdd15&kgmid=/g/11fh_cl5vd&q=Choi+International&shndl=30&shem=lcuae,lsptbl1,uaasie&source=sh/x/loc/uni/m1/1&kgs=1d2e6f6f219da0ad&utm_source=lcuae,lsptbl1,uaasie,sh/x/loc/uni/m1/1#lrd=0x7c006d12450816bf:0x19a98c9f1f632da2,1,,,," 231 class="lp-socials__link socials__item" 232 data-wow-delay="0.2s" 233 target="_blank" 234 aria-label="GOOGLE" 235 > 236 <span class="lp-i lp-i--google" aria-hidden="true"></span> 237 </a> 238 </li> 239 </ul> </div> 240 </div> 241</div> 242 243 </div> 244 <div id="global-mobile-contact" class="mobile-contact-wrapper" data-el-id="befe5af2-a6ca-4119-924a-a85d96d36b51"> 245 <div class="btn-holder" 246style=" 247--backgroundColor: rgba(255, 255, 255, 1); 248--textColor: rgba(0, 0, 0, 1); 249--pillTextColor: rgba(255, 255, 255, 1); 250--pillBackgroundColor: rgba(177, 25, 30, 1);"> 251 252 <a href="/home-search/listings" class="search-btn lp-ico lp-ico-search visible">Search</a> 253 <button type="button" class="connect-btn lp-ico lp-ico-arrow-top visible">Contact Us</button> 254</div> 255 256 257<div class="pop-up" style="--backgroundColor: rgba(255, 255, 255, 1); --textColor: rgba(0, 0, 0, 1);"> 258 <div class="lp-container"> 259 <form data-type="contact-form" class="contact-form"> 260 <div class="contact-form__container"> 261 <h3 class="lp-h3">Leave a Message</h3> 262 263 <div class="contact-form__content"> 264 <div class="contact-form__inputs-container"> 265 <div class="contact-form__item"> 266 <input 267 id="__FullName" 268 type="text" name="name" 269 class="lp-input lp-input--light" 270 required 271 placeholder="Full Name"> 272 </div> 273 <div class="contact-form__item"> 274 <input 275 id="__Email" 276 type="email" name="email" 277 class="lp-input js-email-input lp-input--light" 278 required 279 placeholder="Email"> 280 </div> 281 <div class="contact-form__item"> 282 <input 283 class="lp-input lp-input--light" 284 type="tel" 285 placeholder="Phone" 286 id="__Phone" 287 name="phone" 288 required> 289 </div> 290 291 <div class="contact-form__item input-group-select"> 292 <select name="interest" id="-field-interest" placeholder="Interest" 293 class="lp-input dropdown-select js-select js-form-element lp-input--light" required> 294 <option value="" disabled selected data-label="Interest">Interested in...</option> 295 <option value="Selling & Buying">Selling & Buying</option> 296 <option value="Selling">Selling</option> 297 <option value="Buying">Buying</option> 298 <option value="Renting">Renting</option> 299 <option value="Other">Other</option> 300 </select> 301 <label for="-field-interest" class="lp-label sr-only">Interest</label> 302 </div> 303 304 <div class="contact-form__item"> 305 <textarea 306 name="message" 307 rows="4" 308 id="__Message"
309 class="lp-input lp-input--light" 310 placeholder="Message" 311 required></textarea> 312 </div> 313 <div class="lp-disclaimer"> <label style='display: none;' for='-termsAccepted'>Opt In/Disclaimer Consent:</label> 314 <label class='lp-tcr-content'> 315 <span class='lp-tcr-checkbox'> 316 <input 317 type='checkbox' 318 id='-termsAccepted' 319 name='termsAccepted' 320 oninvalid="this.setCustomValidity('Please accept the terms and conditions')" 321 oninput="this.setCustomValidity('')" 322 required 323 /></span> 324 <span class='lp-tcr-message'> 325 326 I agree to be contacted by 327 The Choi Group With Hawaii Life 328 via call, email, and text for real estate services. To opt out, you can reply 'stop' at any time or reply 'help' for assistance. 329 You can also click the unsubscribe link in the emails. Message and data rates may apply. Message frequency may vary. 330 <a href='/terms-and-conditions' target='_blank'>Privacy Policy</a>. 331 </span> 332 </label> 333 334</div> 335 <button type="submit" class="lp-btn lp-btn--filled lp-btn--dark submit contact-form__item">Submit</button> 336 337 <div class="lp-h-pot"> 338 <input id="__newsSignMiddleName" name="middleName" value="" autocomplete="off" tabindex="-1"> 339 </div> 340 341 <input style="display: none;" type="text" name="source" value="CONTACT_INQUIRY" /> 342 <input type="hidden" name="button_text" value="Contact Us"> 343 </div> 344 </div> 345 </div> 346 347 348 <div class="success-block"> 349 <h3 class="lp-h3 success-message">Thank you for your message. We will be in touch with you shortly.</h3> 350 351 </div> 352 </form> 353 354 <button type="button" class="close-btn lp-ico lp-ico-close"></button> 355 356 </div> 357</div> 358 359 360 </div> 361 <section id="section-9c8b971e-fc4a-4d61-8e47-06eb416e4be9" class="video-section is-font-color-light is-background-color-dark" data-el-id="ff2fdff3-a088-41a3-b3e4-21dd3d28f032"> 362 <div class="video-wrapper"> 363 <div class="full-screen-video-component"> 364 <div class="video-wrapper hidable"> 365 <video class="lazy" 366 poster="https://res.cloudinary.com/luxuryp/videos/f_auto,q_auto/so_0,eo_0/ybtr0vzh1g5vx2csn8in/xtra-hov-honolulu-hawaii-diamond-head-crater.jpg" 367 style="height:100%;" 368 loop 369 muted 370 autoplay 371 playsinline> 372 <source data-src="https://res.cloudinary.com/luxuryp/videos/f_webm,vc_vp9,w_1920,c_limit/ybtr0vzh1g5vx2csn8in/xtra-hov-honolulu-hawaii-diamond-head-crater.webm" type="video/webm;codecs=vp9"> 373<source data-src="https://res.cloudinary.com/luxuryp/videos/f_mp4,vc_h265,w_1920,c_limit/ybtr0vzh1g5vx2csn8in/xtra-hov-honolulu-hawaii-diamond-head-crater.mp4" type="video/mp4;codecs=hvc1"> 374<source data-src="https://res.cloudinary.com/luxuryp/videos/f_mp4,vc_h264,w_1920,c_limit/ybtr0vzh1g5vx2csn8in/xtra-hov-honolulu-hawaii-diamond-head-crater.mp4" type="video/mp4"> 375 </video> 376 </div> 377 <div class="poster" style="background-image: url('https://media-production.lp-cdn.com/cdn-cgi/image/format=auto,quality=85,fit=scale-down,width=1920/https://media-production.lp-cdn.com/media/1a6365c0-d9da-471f-a248-00f5bb3c4704')"></div> 378 </div> 379 </div> 380 <div class="overlay-component"style="--section-overlay: rgba(0, 0, 0, 0.30);"></div> 381 382<div class="redesign opening-with-search height-class-Full" data-only-active="false" data-video-url="" data-show-neighborhood-names="false"> 383 <div class="center-block"> 384 <div class="container lp-container"> 385 386 <div class="lp-title-group"> 387 <div class="opening__carousel js-carousel"> 388 <div class="title-item"> 389 <h1 class="h1 lp-h1 opening-title">The CHOI Group with Hawaiâi Life</h1> 390 <div class="opening-description feature lp-pre-line ">Extraordinary Properties, Unrivalled Expertise</div> 391 </div> 392 <div class="title-item"> 393 <h2 class="h1 lp-h1 opening-title">The CHOI Group with Hawaiâi Life</h2> 394 <div class="opening-description feature lp-pre-line ">Luxury Honolulu Real Estate</div> 395 </div> 396 </div> 397 </div> 398 399 <div class="buttons"></div> 400 </div> 401 </div>
402 <div class="bottom-spacer"></div> 403</div> 404</section> 405<section id="section-636230b7-115c-40c0-833b-0d0151d53062" class="solid-section is-font-color-dark is-background-color-light" data-el-id="49287360-bf66-424e-a5c3-9f1b704226a5">
406<script type="text/x-handlebars-template" class="gridItem pagination-script"> <li class="item" data-search-string="{{firstName}} {{lastName}}"> 407 <a href="/agents/{{slug}}"> 408 <figure> 409 {{#if avatar}} 410 {{{mediaImage avatar sizes="(min-width: 991px) 33vw, (min-width: 769px) 50vw, 100vw" alt=""}}} 411 {{/if}} 412 </figure> 413 <h3 class="lp-h6 item-link">{{firstName}} {{lastName}}</h3> 414 <p class="position bf1" style="color: rgba(177, 25, 30, 1)">{{position}}</p> 415 </a> 416 <div class="item__info"> 417 {{#if phoneNumber}}{{/if}} 418 {{#if email}}{{/if}} 419 </div> 420 </li> 421</script>
421 422<div class="section lp-vertical-paddings"> 423 <div class="lp-container"> 424 <div class="lp-title-group"> 425 <h2 class="lp-h2">The CHOI Group Team</h2> 426 <p class="lp-text--subtitle">A collaborative team of dedicated professionals, combining deep Honolulu market knowledge with Hawaiâi Lifeâs statewide and global network.</p> 427 </div> 428 429 430 431 432 433 434 435 436 437 438 <ul class="collection collection--3 ordered-elements" role="region" id="section-636230b7-115c-40c0-833b-0d0151d53062-agent-grid" aria-live"polite"> 439 <li class="item" data-search-string="Patricia Choi"> 440 <a href="/agents/patricia-choi"> 441 <figure> 442 <img src="https://media-production.lp-cdn.com/cdn-cgi/image/format=auto,quality=85/https://media-production.lp-cdn.com/media/636f6bbc-289b-4276-a2a9-4d542e1035a2" srcset="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/636f6bbc-289b-4276-a2a9-4d542e1035a2 960w, https://media-production.lp-cdn.com/cdn-cgi/image/format=auto,quality=85/https://media-production.lp-cdn.com/media/636f6bbc-289b-4276-a2a9-4d542e1035a2 1280w" alt="Patricia Choi Honolulu, HI Real Estate Agent Headshot" sizes="(min-width: 991px) 33vw, (min-width: 769px) 50vw, 100vw" class="portrait" loading="lazy"> 443 </figure> 444 <h3 class="lp-h6 item-link">Patricia Choi</h3> 445 <p class="position bf1" style="color: rgba(177, 25, 30, 1)">Honorary Founder</p> 446 </a> 447 <div class="item__info"> 448 449 450 </div> 451 </li> 452 <li class="item" data-search-string="Cedric Choi"> 453 <a href="/agents/cedric-choi"> 454 <figure> 455 <img src="https://media-production.lp-cdn.com/cdn-cgi/image/format=auto,quality=85/https://media-production.lp-cdn.com/media/f35e832b-8a28-4e94-9b99-7e0b92ab03de" srcset="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/f35e832b-8a28-4e94-9b99-7e0b92ab03de 960w, https://media-production.lp-cdn.com/cdn-cgi/image/format=auto,quality=85/https://media-production.lp-cdn.com/media/f35e832b-8a28-4e94-9b99-7e0b92ab03de 1280w" alt="Cedric Choi Honolulu, HI Real Estate Agent Headshot" sizes="(min-width: 991px) 33vw, (min-width: 769px) 50vw, 100vw" class="portrait" loading="lazy"> 456 </figure> 457 <h3 class="lp-h6 item-link">Cedric Choi</h3> 458 <p class="position bf1" style="color: rgba(177, 25, 30, 1)">Director</p> 459 </a> 460 <div class="item__info"> 461 462 463 </div> 464 </li> 465 <li class="item" data-search-string="Julianna Garris"> 466 <a href="/agents/julianna-garris"> 467 <figure> 468 <img src="https://media-production.lp-cdn.com/cdn-cgi/image/format=auto,quality=85/https://media-production.lp-cdn.com/media/3327893f-4eab-4660-bc50-2abb3d16a4f2" srcset="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/3327893f-4eab-4660-bc50-2abb3d16a4f2 960w, https://media-production.lp-cdn.com/cdn-cgi/image/format=auto,quality=85/https://media-production.lp-cdn.com/media/3327893f-4eab-4660-bc50-2abb3d16a4f2 1280w" alt="Julianna Garris Honolulu, HI Real Estate Agent Headshot" sizes="(min-width: 991px) 33vw, (min-width: 769px) 50vw, 100vw" class="portrait" loading="lazy"> 469 </figure> 470 <h3 class="lp-h6 item-link">Julianna Garris</h3> 471 <p class="position bf1" style="color: rgba(177, 25, 30, 1)">Broker in Charge</p> 472 </a> 473 <div class="item__info"> 474 475 476 </div> 477 </li> 478 <li class="item" data-search-string="Cynthia Nash"> 479 <a href="/agents/cynthia-nash"> 480 <figure> 481 <img src="https://media-production.lp-cdn.com/cdn-cgi/image/format=auto,quality=85/https://media-production.lp-cdn.com/media/f013be1b-e605-41a6-9988-0d9bf07421b5" srcset="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/f013be1b-e605-41a6-9988-0d9bf07421b5 960w, https://media-production.lp-cdn.com/cdn-cgi/image/format=auto,quality=85/https://media-production.lp-cdn.com/media/f013be1b-e605-41a6-9988-0d9bf07421b5 1280w" alt="Cynthia Nash Honolulu, HI Real Estate Agent Headsh
481ot" sizes="(min-width: 991px) 33vw, (min-width: 769px) 50vw, 100vw" class="portrait" loading="lazy"> 482 </figure> 483 <h3 class="lp-h6 item-link">Cynthia Nash</h3> 484 <p class="position bf1" style="color: rgba(177, 25, 30, 1)">REALTOR®</p> 485 </a> 486 <div class="item__info"> 487 488 489 </div> 490 </li> 491 <li class="item" data-search-string="Jeanie Schmaltz"> 492 <a href="/agents/jeanie-schmaltz"> 493 <figure> 494 <img src="https://media-production.lp-cdn.com/cdn-cgi/image/format=auto,quality=85/https://media-production.lp-cdn.com/media/14c723ae-1ca0-46c0-9733-94224b4dcb4c" srcset="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/14c723ae-1ca0-46c0-9733-94224b4dcb4c 960w, https://media-production.lp-cdn.com/cdn-cgi/image/format=auto,quality=85/https://media-production.lp-cdn.com/media/14c723ae-1ca0-46c0-9733-94224b4dcb4c 1280w" alt="Jeanie Schmaltz Honolulu, HI Real Estate Agent Headshot" sizes="(min-width: 991px) 33vw, (min-width: 769px) 50vw, 100vw" class="portrait" loading="lazy"> 495 </figure> 496 <h3 class="lp-h6 item-link">Jeanie Schmaltz</h3> 497 <p class="position bf1" style="color: rgba(177, 25, 30, 1)">Broker in Charge</p> 498 </a> 499 <div class="item__info"> 500 501 502 </div> 503 </li> 504 <li class="item" data-search-string="Sanjay Arora"> 505 <a href="/agents/sanjay-arora"> 506 <figure> 507 <img src="https://media-production.lp-cdn.com/cdn-cgi/image/format=auto,quality=85/https://media-production.lp-cdn.com/media/9f996280-7405-4ef2-818a-151ca60fcbd5" srcset="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/9f996280-7405-4ef2-818a-151ca60fcbd5 960w, https://media-production.lp-cdn.com/cdn-cgi/image/format=auto,quality=85/https://media-production.lp-cdn.com/media/9f996280-7405-4ef2-818a-151ca60fcbd5 1280w" alt="Sanjay Arora Honolulu, HI Real Estate Agent Headshot" sizes="(min-width: 991px) 33vw, (min-width: 769px) 50vw, 100vw" class="portrait" loading="lazy"> 508 </figure> 509 <h3 class="lp-h6 item-link">Sanjay Arora</h3> 510 <p class="position bf1" style="color: rgba(177, 25, 30, 1)">Director, Japan Sales</p> 511 </a> 512 <div class="item__info"> 513 514 515 </div> 516 </li> 517 <li class="item" data-search-string="Liah Gingerich"> 518 <a href="/agents/liah-gingerich"> 519 <figure> 520 <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/6a63a83c-5b50-427c-9633-3124e112afab" srcset="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/6a63a83c-5b50-427c-9633-3124e112afab 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/6a63a83c-5b50-427c-9633-3124e112afab 1280w, https://media-production.lp-cdn.com/cdn-cgi/image/format=auto,quality=85,fit=scale-down,width=1920/https://media-production.lp-cdn.com/media/6a63a83c-5b50-427c-9633-3124e112afab 1920w" alt="" sizes="(min-width: 991px) 33vw, (min-width: 769px) 50vw, 100vw" class="portrait" loading="lazy"> 521 </figure> 522 <h3 class="lp-h6 item-link">Liah Gingerich</h3> 523 <p class="position bf1" style="color: rgba(177, 25, 30, 1)">REALTOR®</p> 524 </a> 525 <div class="item__info"> 526 527 528 </div> 529 </li> 530 </ul> 531 </div> 532</div> 533</section> 534<section class="image-section is-font-color-light is-background-color-light" id="section-950e58af-1ac6-48e0-b114-f7326c904100" data-el-id="60315b02-0803-4dc1-be5a-360f795dd1a2"> 535 536 <div class="testimonials-section lp-container lp-vertical-paddings testimonial-container "> 537 <div class="sliders-container"> 538 <div class="avatar-slider js-avatar-slider"> 539 <div class="avatar-slide"> 540 <div class="avatar js-animation-target"> 541 <svg width="221" height="221" viewBox="0 0 221 221" fill="none" xmlns="http://www.w3.org/2000/svg"> 542 <circle cx="110" cy="110" r="110" fill="white" fill-opacity="0.3"/> 543 <path d="M93.0909 108.726C87.4545 108.726 83.697 112.274 82.0061 115.822C82.0061 106.485 86.8909 96.029 100.982 89.8672L100.418 88C86.8909 93.4149 79 104.058 79 117.129C79 127.772 85.9515 133 93.0909 133C99.8545 133 105.303 127.398 105.303 120.863C105.303 114.141 99.8545 108.726 93.0909 108.726ZM128.788 108.726C123.152 108.726 119.394 112.274 117.703 115.822C117.703 106.485 122.588 96.029 136.679 89.8672L136.115 88C122.588 93.4149 114.697 104.058 114.697 117.129C114.697 127.772 121.648 133 128.788 133C135.552 133 141 127.398 141 120.863C141 114.141 135.552 108.726 128.788 108.726Z" fill="black" fill-opacity="0.2"/> 544 </svg> 545 </div> 546 </div> 547 <div class="avatar-slide"> 548 <div class="avatar js-animation-target"> 549 <svg width="221" height="221" viewBox="0 0 221 221" fill="none" xmlns="http://www.w3.org/2000/svg"> 550 <circle cx="110" cy="110" r="110" fill="white" fill-opacity="0.3"/> 551 <path d="M93.0909 108.726C87.4545 108.726 83.697 112.274 82.0061 115.822C82.0061 106.485 86.8909 96.029 100.982 89.8672L100.418 88C86.8909 93.4149 79 104.058 79 117.129C79 127.772 85.9515 133 93.0909 133C99.8545 133 105.303 127.398 105.303 120.863C105.303 114.141 99.8545 108.726 93.0909 108.726ZM128.788 108.726C123.152 108.726 119.394 112.274 117.703 115.822C117.703 106.485 122.588 96.029 136.679 89.8672L136.115 88C122.588 93.4149 114.697 104.058 114.697 117.129C114.697 127.772 121.648 133 128.788 133C135.552 133 141 127.398 141 120.863C141 114.141 135.552 108.726 128.788 108.726Z" fill="black" fill-opacity="0.2"/> 552 </svg> 553 </div> 554 </div> 555 <div class="avatar-slide"> 556 <div class="avatar js-animation-target"> 557 <svg width="221" height="221" viewBox="0 0 221 221" fill="none" xmlns="http://www.w3.org/2000/svg"> 558 <circle cx="110" cy="110" r="110" fill="white" fill-opacity="0.3"/> 559 <path d="M93.0909 108.726C87.4545 108.726 83.697 112.274 82.0061 115.822C82.0061 106.485 86.8909 96.029 100.982 89.8672L100.418 88C86.8909 93.4149 79 104.058 79 117.129C79 127.772 85.9515 133 93.0909 133C99.8545 133 105.303 127.398 105.303 120.863C105.303 114.141 99.8545 108.726 93.0909 108.726ZM128.788 108.726C123.152 108.726 119.394 112.274 117.703 115.822C117.703 106.485 122.588 96.029 136.679 89.8672L136.115 88C122.588 93.4149 114.697 104.058 114.697 117.129C114.697 127.772 121.648 133 128.788 133C135.552 133 141 127.398 141 120.863C141 114.141 135.552 108.726 128.788 108.726Z" fill="black" fill-opacity="0.2"/> 560 </svg> 561 </div> 562 </div> 563 <div class="avatar-slide"> 564 <div class="avatar js-animation-target"> 565 <svg width="221" height="221" viewBox="0 0 221 221" fill="none" xmlns="http://www.w3.org/2000/svg"> 566 <circle cx="110" cy="110" r="110" fill="white" fill-opacity="0.3"/> 567 <path d="M93.0909 108.726C87.4545 108.726 83.697 112.274 82.0061 115.822C82.0061 106.485 86.8909 96.029 100.982 89.8672L100.418 88C86.8909 93.4149 79 104.058 79 117.129C79 127.772 85.9515 133 93.0909 133C99.8545 133 105.303 127.398 105.303 120.863C105.303 114.141 99.8545 108.726 93.0909 108.726ZM128.788 108.726C123.152 108.726 119.394 112.274 117.703 115.822C117.703 106.485 122.588 96.029 136.679 89.8672L136.115 88C122.588 93.4149 114.697 104.058 114.697 117.129C114.697 127.772 121.648 133 128.788 133C135.552 133 141 127.398 141 120.863C141 114.141 135.552 108.726 128.788 108.726Z" fill="black" fill-opacity="0.2"/> 568 </svg> 569 </div> 570 </div> 571 <div class="avatar-slide"> 572 <div class="avatar js-animation-target"> 573 <svg width="221" height="221" viewBox="0 0 221 221" fill="none" xmlns="http://www.w3.org/2000/svg"> 574 <circle cx="110" cy="110" r="110" fill="white" fill-opacity="0.3"/> 575 <path d="M93.0909 108.726C87.4545 108.726 83.697 112.274 82.0061 115.822C82.0061 106.485 86.8909 96.029 100.982 89.8672L100.418 88C86.8909 93.4149 79 104.058 79 117.129C79 127.772 85.9515 133 93.0909 133C99.8545 133 105.303 127.398 105.303 120.863C105.303 114.141 99.8545 108.726 93.0909 108.726ZM128.788 108.726C123.152 108.726 119.394 112.274 117.703 115.822C117.703 106.485 122.588 96.029 136.679 89.8672L136.115 88C122.588 93.4149 114.697 104.058 114.697 117.129C114.697 127.772 121.648 133 128.788 133C135.552 133 141 127.398 141 120.863C141 114.141 135.552 108.726 128.788 108.726Z" fill="black" fill-opacity="0.2"/> 576 </svg> 577 </div> 578 </div> 579 <div class="avatar-slide"> 580 <div class="avatar js-animation-target"> 581 <svg width="221" height="221" viewBox="0 0 221 221" fill="none" xmlns="http://www.w3.org/2000/svg"> 582 <circle cx="110" cy="110" r="110" fill="white" fill-opacity="0.3"/> 583 <path d="M93.0909 108.726C87.4545 108.726 83.697 112.274 82.0061 115.822C82.0061 106.485 86.8909 96.029 100.982 89.8672L100.418 88C86.8909 93.4149 79 104.058 79 117.129C79 127.772 85.9515 133 93.0909 133C99.8545 133 105.303 127.398 105.303 120.863C105.303 114.141 99.8545 108.726 93.0909 108.726ZM128.788 108.726C123.152 108.726 119.394 112.274 117.703 115.822C117.703 106.485 122.588 96.029 136.679 89.8672L136.115 88C122.588 93.4149 114.697 104.058 114.697 117.129C114.697 127.772 121.648 133 128.788 133C135.552 133 141 127.398 141 120.863C141 114.141 135.552 108.726 128.788 108.726Z" fill="black" fill-opacity="0.2"/> 584 </svg> 585 </div> 586 </div> 587 </div> 588 <div class="list list-slider js-list-slider" data-arrows-style="" data-autoscroll-interval=4> 589 <div class="list-cell js-list-cell"> 590 <div class="quote"> 591 <div class="text"> 592 <h6 class="title lp-h6">What Our Clients Say</h6> 593 <p class="body">I have had the considerable good fortune, working with an exemplary team at The Choi Group with Hawaii Life. As a client, I have experienced the stellar services the team provides; from staging furnishings, showings to assistance with understanding the execution of documents, integral to every transaction. The Choi team, whether representing my best interests as a Buyer or a Seller, has been successful i...</p> 594 <p class="author bf1">— Corinne W.</p> 595 </div> 596 </div> 597 </div> 598 <div class="list-cell js-list-cell"> 599 <div class="quote"> 600 <div class="text"> 601 <h6 class="title lp-h6">What Our Clients Say</h6> 602 <p class="body">We wanted to extend our heartfelt appreciation to our realtor, Jeanie Schmaltz, for the successful sale of our condo. Jeanie is very professional, articulate, honest, thorough, sincere, efficient, and positive. We have dealt with a couple of other realtors in the past, but never did we come across someone with the knowledge and expertise in real estate that is incredible. What we really like most about h...</p> 603 <p class="author bf1">— Dr. Michael L. Eng and Myrna Eng</p> 604 </div> 605 </div> 606 </div> 607 <div class="list-cell js-list-cell"> 608 <div class="quote"> 609 <div class="text"> 610 <h6 class="title lp-h6">What Our Clients Say</h6> 611 <p class="body">
611I want to thank you and Choi for the great work that you performed on our behalf. You are a pro. And it was a pleasure working with you. Loved how you not only kept us informed on the problems, but solved them as well. If you ever need a recommendation, please consider using us. With the housing market starting to show life, I hope you have a great year with the wind at your back. Since 2008, it has cert...</p> 612 <p class="author bf1">— Paul Z.</p> 613 </div> 614 </div> 615 </div> 616 <div class="list-cell js-list-cell"> 617 <div class="quote"> 618 <div class="text"> 619 <h6 class="title lp-h6">What Our Clients Say</h6> 620 <p class="body">I wish to express my very strong appreciation for the great job you did in handling the sale of our apartment 432 at the Kahala Beach condominium. Given the complexities of the leasehold situation at the Kahala Beach, your imaginative solutions and strong initiatives resulted in closing a very satisfactory sale, I'm sure, for both our interests and the buyer's. There were unusual conditions involving tho...</p> 621 <p class="author bf1">— Martin A.</p> 622 </div> 623 </div> 624 </div> 625 <div class="list-cell js-list-cell"> 626 <div class="quote"> 627 <div class="text"> 628 <h6 class="title lp-h6">What Our Clients Say</h6> 629 <p class="body">My recent home purchase was successful and stress-free thanks to Julianna Garris. What impressed me the most was her research and follow-up. Whenever I had a question about the property or the neighborhood, Julie found the answer quickly. She often provided me with additional documentation for backup. Her thoroughness and attention to detail make her an excellent representative for her client.</p> 630 <p class="author bf1">— Linda R.</p> 631 </div> 632 </div> 633 </div> 634 <div class="list-cell js-list-cell"> 635 <div class="quote"> 636 <div class="text"> 637 <h6 class="title lp-h6">What Our Clients Say</h6> 638 <p class="body">Cindy Nash is an extraordinary person and a great realtor. âIâm very hands-on with my listings,â she told us on the first day we began discussing the sale of our house. âMy houses always look great, and I do whatever I can for my clients.â Indeed, this turned out to be 100% accurate. She gave us a list of things that needed to be done, one based on her years of experience and her knowledge of what needs ...</p> 639 <p class="author bf1">— Jonathan & Jenny P.</p> 640 </div> 641 </div> 642 </div> 643 </div> 644 </div> 645 646 <div class="bottom no-arrows"> 647 <div class="arrows-container"></div> 648 <p class="num-pages js-num-pages"></p> 649 <a 650 href="/testimonials" 651 652 class="btn btn--link lp-btn lp-btn--dark lp-btn--outline lp-btn--mob-full-width button-style-2" 653 654 655 656 657 658 > 659 View All 660 </a> 661 </div> 662 </div> 663 664</section> 665<section id="section-a747ae9e-de7c-4dd7-a979-1a646c88a921" class="solid-section is-font-color-light is-background-color-dark" data-el-id="eb10e45e-8560-43c3-9725-c920b68d3653"> 666 <div class="double-image-grid Full-Bleed redesign"> 667 <div class="cta-section"> 668 <div class="left-image image-back"> 669 <div class="image-subsection background" style="color: #ffffff; background-image: linear-gradient(rgba(0, 0, 0, 0), rgba(0, 0, 0, 0)), url('https://media-production.lp-cdn.com/cdn-cgi/image/format=auto,quality=85,fit=scale-down,width=1920/https://media-production.lp-cdn.com/media/aed9e35c-9f9f-4e62-89fe-ae900f81dc47'); background-repeat: no-repeat; background-position: center; background-size: cover;"> 670 </div> 671 672 </div> 673 <div class="cta-section__content" style="color: #ffffff"> 674 <div class="cta-section__text-block"> 675 <h2 class="lp-h2"></h2> 676 677 <div class="cta-section__btn-wrap"> 678 </div> 679 680 </div> 681 </div> 682 </div> 683 684 685 <div class="cta-section"> 686 <div class="right-image image-back"> 687 <div class="solid-subsection background" style="color: rgba(0, 0, 0, 1); background-color: rgba(235, 231, 224, 1);"> 688 </div> 689 690 </div> 691 <div class="cta-section__content" style="color: rgba(0, 0, 0, 1)"> 692 <div class="cta-section__text-block"> 693 <h2 class="lp-h2">Your Gateway to Island Living</h2> 694 <div class="cta-section__lead lp-text--subtitle"><p>From Kahala to Kailua, Diamond Head to Honolulu, our team knows the neighborhoods that define O‘ahu’s luxury lifestyle.</p></div> 695 696 <div class="cta-section__btn-wrap"> 697 <a 698 href="/neighborhoods" 699 700 class="lp-btn lp-btn--light lp-btn--filled button-style-1" 701 702 703 704 705 706 > 707 View Neighborhoods 708 </a> 709 </div> 710 711 </div> 712 </div> 713 </div> 714</div> 715</section> 716<section id="section-64dea10a-4201-4776-9fdf-ce8691ad5271" class="solid-section is-font-color-dark is-background-color-light" data-el-id="ef4a3598-d218-4978-bdc4-94d84dee161b">
717<script type="text/x-handlebars-template" class="gridItem pagination-script"><div class="item"> 718 <a href="/neighborhoods/{{slug}}" class="item-wrapper"> 719 <div class="image-holder"> 720 {{{mediaImage media.[0] sizes="(max-width: 768px) 100vw, 50vw" alt=name}}} 721 </div> 722 <div class="overlay"></div> 723 <div class="content-container"> 724 <h3 class="lp-h3 neighborhood-name">{{name}}</h3> 725 <div class="button-wrapper"> 726 <span class="lp-btn lp-btn--light lp-btn--filled lp-btn--big button-style-1">Learn More</span> 727 </div> 728 </div> 729 </a> 730</div> 731</script>
731 732 733<div class="featured-neighborhoods-grid lp-vertical-paddings redesign"> 734 735 <div class="items items-3 three-columns "> 736 <div class="item"> 737 <a href="/neighborhoods/lanikai" class="item-wrapper"> 738 <div class="image-holder"> 739 <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/a3492197-e405-4ae4-953e-359f31b228b9" srcset="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/a3492197-e405-4ae4-953e-359f31b228b9 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/a3492197-e405-4ae4-953e-359f31b228b9 1280w, https://media-production.lp-cdn.com/cdn-cgi/image/format=auto,quality=85/https://media-production.lp-cdn.com/media/a3492197-e405-4ae4-953e-359f31b228b9 1920w" alt="Lanikai" sizes="(max-width: 768px) 100vw, 50vw" loading="lazy"> 740 </div> 741 <div class="overlay"></div> 742 <div class="content-container"> 743 <h3 class="lp-h3 neighborhood-name">Lanikai</h3> 744 <div class="button-wrapper"> 745 <span class="lp-btn lp-btn--light lp-btn--filled lp-btn--big button-style-1">Learn More</span> 746 </div> 747 </div> 748 </a> 749 </div> 750 <div class="item"> 751 <a href="/neighborhoods/ala-moana" class="item-wrapper"> 752 <div class="image-holder"> 753 <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/3a531f66-88bb-4d1b-8920-65604f1d3ca8" srcset="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/3a531f66-88bb-4d1b-8920-65604f1d3ca8 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/3a531f66-88bb-4d1b-8920-65604f1d3ca8 1280w, https://media-production.lp-cdn.com/cdn-cgi/image/format=auto,quality=85/https://media-production.lp-cdn.com/media/3a531f66-88bb-4d1b-8920-65604f1d3ca8 1920w" alt="Ala Moana" sizes="(max-width: 768px) 100vw, 50vw" loading="lazy"> 754 </div> 755 <div class="overlay"></div> 756 <div class="content-container"> 757 <h3 class="lp-h3 neighborhood-name">Ala Moana</h3> 758 <div class="button-wrapper"> 759 <span class="lp-btn lp-btn--light lp-btn--filled lp-btn--big button-style-1">Learn More</span> 760 </div> 761 </div> 762 </a> 763 </div> 764 <div class="item"> 765 <a href="/neighborhoods/kailua" class="item-wrapper"> 766 <div class="image-holder"> 767 <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/5c4ba643-9832-418e-ac09-792e11cf9deb" srcset="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/5c4ba643-9832-418e-ac09-792e11cf9deb 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/5c4ba643-9832-418e-ac09-792e11cf9deb 1280w, https://media-production.lp-cdn.com/cdn-cgi/image/format=auto,quality=85/https://media-production.lp-cdn.com/media/5c4ba643-9832-418e-ac09-792e11cf9deb 1920w" alt="Kailua" sizes="(max-width: 768px) 100vw, 50vw" loading="lazy"> 768 </div> 769 <div class="overlay"></div> 770 <div class="content-container"> 771 <h3 class="lp-h3 neighborhood-name">Kailua</h3> 772 <div class="button-wrapper"> 773 <span class="lp-btn lp-btn--light lp-btn--filled lp-btn--big button-style-1">Learn More</span> 774 </div> 775 </div> 776 </a> 777 </div> 778 </div> 779 780</div> 781</section> 782<section id="section-fe5140df-d21d-4653-9d5d-d225eca79459" class="solid-section is-font-color-light is-background-color-light" data-el-id="c486d5ea-5179-43ae-904c-a0e6d0289d98"> 783 <div class="popup-modal redesign lp-vertical-paddings"> 784 <div class="lp-container popup-modal__container"> 785 <form class="popup-modal__form popup-modal__form--with-image" data-type="contact-form" style='background-color: rgba(255, 255, 255, 1);' data-delay="5000"> 786 <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/59943dec-4a00-4657-9b69-5ea791f4695e" 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/59943dec-4a00-4657-9b69-5ea791f4695e 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/59943dec-4a00-4657-9b69-5ea791f4695e 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/59943dec-4a00-4657-9b69-5ea791f4695e 1280w, https://media-production.lp-cdn.com/cdn-cgi/image/format=auto,quality=85,fit=scale-down,width=1920/https://media-production.lp-cdn.com/media/59943dec-4a00-4657-9b69-5ea791f4695e 1920w, https://media-production.lp-cdn.com/cdn-cgi/image/format=auto,quality=85/https://media-production.lp-cdn.com/media/59943dec-4a00-4657-9b69-5ea791f4695e 2400w" sizes="50vw" alt="Form Image" class="image" loading="lazy"> 787 <div class="form-group"> 788 789 <div class="lp-title-group popup-modal__left-align"> 790 <h2 class="lp-h2">Contact our experienced agents for a property valuation</h2> 791 792 </div> 793 794 <div class="form-inputs-wrap "> 795 796 <div class="input-container"> 797 <input id="section-fe5140df-d21d-4653-9d5d-d225eca79459-mp-name" name="name" type="text" class="lp-in
797put lp-input--light popup-modal__input" placeholder="Name" required> 798 <span class="input-icon"></span> 799 </div> 800 801 802 <div class="input-container"> 803 <input id="section-fe5140df-d21d-4653-9d5d-d225eca79459-mp-email" name="email" type="email" class="lp-input lp-input--light popup-modal__input" placeholder="Email" required pattern="[a-zA-Z0-9.!#$%&â*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)+"> 804 <span class="input-icon"></span> 805 </div> 806 807 <div class="input-container"> 808 <input id="section-fe5140df-d21d-4653-9d5d-d225eca79459-mp-phone" name="phone" type="tel" class="lp-input lp-input--light popup-modal__input" placeholder="Phone" required> 809 <span class="input-icon"></span> 810 </div> 811 812 <div class="lp-h-pot"> 813 <label for="section-fe5140df-d21d-4653-9d5d-d225eca79459-middleName">Middle Name</label> 814 <input id="section-fe5140df-d21d-4653-9d5d-d225eca79459-middleName" name="middleName" value="" autocomplete="off" tabindex="-1"> 815 </div> 816 <div class="lp-disclaimer"> <label style='display: none;' for='section-fe5140df-d21d-4653-9d5d-d225eca79459-termsAccepted'>Opt In/Disclaimer Consent:</label> 817 <label class='lp-tcr-content'> 818 <span class='lp-tcr-checkbox'> 819 <input 820 type='checkbox' 821 id='section-fe5140df-d21d-4653-9d5d-d225eca79459-termsAccepted' 822 name='termsAccepted' 823 oninvalid="this.setCustomValidity('Please accept the terms and conditions')" 824 oninput="this.setCustomValidity('')" 825 required 826 /></span> 827 <span class='lp-tcr-message'> 828 829 I agree to be contacted by 830 The Choi Group With Hawaii Life 831 via call, email, and text for real estate services. To opt out, you can reply 'stop' at any time or reply 'help' for assistance. 832 You can also click the unsubscribe link in the emails. Message and data rates may apply. Message frequency may vary. 833 <a href='/terms-and-conditions' target='_blank'>Privacy Policy</a>. 834 </span> 835 </label> 836 837</div> 838 839 <input type="hidden" name="source" value="CONTACT_INQUIRY"> 840 <button class="popup-modal__submit lp-btn lp-btn--light lp-btn--filled button-style-1" type="submit">Submit</button> 841 </div> 842 843 </div> 844 845 846 847 <div class="thank-you"> 848 <div class="lp-title-group"> 849 <h2 class="lp-h2">Thank You!</h2> 850 851 </div> 852 853 <a 854 href="/properties" 855 856 class="lp-btn lp-btn--dark lp-btn--outline button-style-1" 857 858 859 860 861 862 > 863 See Your Listing 864 </a> 865 </div> 866 </form> 867 868 </div> 869</div> 870 871</section> 872<section class="image-section is-font-color-light is-background-color-light" id="section-382717e0-234c-406a-ac82-41111ea1b5c3" data-el-id="60f303de-57c9-4a3b-87cf-c1b576e15623"> 873
873<script> 874 (function(){ 875 const sectionId = 'section-382717e0-234c-406a-ac82-41111ea1b5c3' 876 const section = document.querySelector(`#${sectionId}`); 877 var sectionBg = window.getComputedStyle(section, null).getPropertyValue('background-image'); 878 879 if('true' === 'true') { 880 section.classList.add('parallax-enabled'); 881 section.style.setProperty('--sectionBackground', sectionBg); 882 section.style.setProperty('background', 'none'); 883 } 884 })() 885</script>
885 886 887<div class="section work-with-us parallax" style="--opacityColor:rgba(0, 0, 0, 0);"> 888 <div class="lp-container lp-vertical-paddings"> 889 890 <div class="lp-title-group"> 891 <h2 class="lp-h2">Work With Our Team</h2> 892 893 <p class="pre-line">Partner with Honolulu luxury real estate leaders for a seamless, results-driven experience backed by global reach.</p> 894 </div> 895 896 897 898 899 900 901 902 <div class="btn-container"> 903 <a 904 role="button" 905 data-type="CONTACT_US" 906 class="btn btn--link lp-btn lp-btn--dark lp-btn--outline lp-btn--mob-full-width button-style-2" 907 908 909 910 911 912 > 913 Contact Us 914 </a> 915 </div> 916 </div> 917</div> 918</section> 919 920 <div id="modals" class="modals"></div> 921 <footer id="global-footer" class="is-font-color-dark is-background-color-light" data-el-id="f3892c1c-f128-483a-b998-cb1ea48c25c1"> 922 <div class="site-footer"> 923 924 <div class="site-footer__bottom"> 925 <div class="lp-container"> 926 927 <div class="site-footer__logo-bar"> 928 <div class="site-footer__logo-container"> 929 <img src="https://media-production.lp-cdn.com/cdn-cgi/image/format=auto,quality=85/https://media-production.lp-cdn.com/media/00779f1a-56c1-48b1-a86d-59aef84ffdd5" 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/00779f1a-56c1-48b1-a86d-59aef84ffdd5 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/00779f1a-56c1-48b1-a86d-59aef84ffdd5 960w, https://media-production.lp-cdn.com/cdn-cgi/image/format=auto,quality=85/https://media-production.lp-cdn.com/media/00779f1a-56c1-48b1-a86d-59aef84ffdd5 1080w" sizes="268px" alt=""> 930 </div> 931 <div class="site-footer__logo-container"> 932 933 </div> 934 </div> 935 936 <div class="site-footer__info"> 937 <div class="site-footer__col"> 938 <h5 class="lp-h5 site-footer__col-title">The Choi Group</h5> 939 940 <div class="site-footer__col-content lp-a"><p><a href="tel:(808) 285-2486">(808) 285-2486</a><br><a href="/cdn-cgi/l/email-protection#26454342544f4566454e494f0b5443474a525f0845494b"><span class="__cf_email__" data-cfemail="f2919796809b91b2919a9d9bdf8097939e868bdc919d9f">[email protected]</span></a></p></div> 941 </div> 942 943 944 <div class="site-footer__col"> 945 <h5 class="lp-h5 site-footer__col-title">ADDRESS</h5> 946 947 <div class="site-footer__col-content lp-a"><p>4614 KILAUEA AVE SUITE 201<br>HONOLULU HI 96816</p></div> 948 </div> 949 950 <div class="site-footer__navigation-wrap"> 951 <ul class="footer-navigation"> 952 <li class="footer-navigation__item"> 953 <a href="/" class="footer-navigation__link">Home</a> 954 </li> 955 <li class="footer-navigation__item"> 956 <a href="/team" class="footer-navigation__link">Meet The Team</a> 957 </li> 958 <li class="footer-navigation__item"> 959 <a href="/properties/sale" class="footer-navigation__link">Featured Properties</a> 960 </li> 961 <li class="footer-navigation__item"> 962 <a href="/home-search/listings" class="footer-navigation__link">Home Search</a> 963 </li> 964 <li class="footer-navigation__item"> 965 <a href="/home-valuation" class="footer-navigation__link">Home Valuation</a> 966 </li> 967 <li class="footer-navigation__item"> 968 <a href="/neighborhoods" class="footer-navigation__link">Neighborhoods</a> 969 </li> 970 <li class="footer-navigation__item"> 971 <button data-type="CONTACT_US" class="footer-navigation__link" type="button">Contact Us</button> 972 </li> 973 </ul> 974 975 <ul class="lp-socials social_icons-style-1 site-footer__socials socials"> 976 977 <li> 978 <a 979 href="https://www.instagram.com/thechoigroup/?hl=en" 980 class="lp-socials__link socials__item" 981 data-wow-delay="0s" 982 target="_blank" 983 aria-label="INSTAGRAM" 984 >
985 <span class="lp-i lp-i--instagram" aria-hidden="true"></span> 986 </a> 987 </li> 988 <li> 989 <a 990 href="https://www.linkedin.com/in/cedric-choi-b439451a2/" 991 class="lp-socials__link socials__item" 992 data-wow-delay="0.1s" 993 target="_blank" 994 aria-label="LINKEDIN" 995 > 996 <span class="lp-i lp-i--linkedin" aria-hidden="true"></span> 997 </a> 998 </li> 999 <li> 1000 <a 1001 href="https://www.google.com/search?sca_esv=7f9887bee47bdd15&kgmid=/g/11fh_cl5vd&q=Choi+International&shndl=30&shem=lcuae,lsptbl1,uaasie&source=sh/x/loc/uni/m1/1&kgs=1d2e6f6f219da0ad&utm_source=lcuae,lsptbl1,uaasie,sh/x/loc/uni/m1/1#lrd=0x7c006d12450816bf:0x19a98c9f1f632da2,1,,,," 1002 class="lp-socials__link socials__item" 1003 data-wow-delay="0.2s" 1004 target="_blank" 1005 aria-label="GOOGLE" 1006 > 1007 <span class="lp-i lp-i--google" aria-hidden="true"></span> 1008 </a> 1009 </li> 1010 </ul> 1011 1012 </div> 1013 </div> 1014 1015 <div class="site-footer__disclaimer"><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: 15px; width: 100px; height: auto; filter: invert(0);" src="https://res.cloudinary.com/luxuryp/images/zbxf1j3xdplm6lhfnqo3/realtor-eho-logo-07232021-update-dark"></p></div> 1016 1017 <div class="site-footer__copy"> 1018 <div class="lp-a site-footer__copy-lp"> 1019 <div class="site-footer__logo-container"> 1020 <img src="https://media-production.lp-cdn.com/media/f78e371f-8cef-4648-9d4d-fb6d0cdbee59" alt=""> 1021 </div> 1022 <span>Website designed and developed by</span> 1023 <a href="https://www.luxurypresence.com/" class="" target="_blank">Luxury Presence</a> 1024 </div> 1025 <div>Copyright © <span class="auto-year-update">2026</span> | <a href="/terms-and-conditions">Privacy Policy</a></div> 1026 </div> 1027 1028 </div> 1029 </div> 1030 1031 1032 1033</div> 1034 1035 </footer> 1036 1037 <div id="modal-global-contact-us" class="modal " 1038 data-el-id="" 1039 style="background-color: ;" 1040 > 1041 <div class="modal-content"> 1042 <div class="section opening" style="background-color: #ffffff; --bgOpacity: rgba(0,0,0, .6); "> 1043 <div class="left" style="color: #000000;"> 1044 <p class="lp-pre-line pre-title">GET IN TOUCH</p> 1045 <h2 class="lp-h2">GET IN TOUCH</h2> 1046 1047 <form class="contact-form__form" data-type="contact-form"> 1048 <div class="input-container"> 1049 <input id="-cmp-name" name="firstName" type="text" class="lp-input lp-input--dark contact-form__input" placeholder="First Name" required> 1050 </div> 1051 1052 <div class="input-container"> 1053 <input id="-cmp-name" name="lastName" type="text" class="lp-input lp-input--dark contact-form__input" placeholder="Last Name" required> 1054 </div> 1055 1056 <div class="input-container"> 1057 <input id="-cmp-email" name="email" type="email" class="lp-input lp-input--dark contact-form__input" placeholder="Email" required pattern="[a-zA-Z0-9.!#$%&â*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)+"> 1058 </div> 1059 1060 <div class="input-container"> 1061 <input id="-cmp-phone" name="phone" type="tel" class="lp-input lp-input--dark contact-form__input" placeholder="Phone" required> 1062 </div> 1063 1064 <div class="input-container textarea"> 1065 <textarea id="-cmp-contactMessage" placeholder="Message" name="message" required type="text" class="lp-in
1065put lp-input--dark contact-form__input"></textarea> 1066 </div> 1067 1068 <div class="h-pot"> 1069 <label for="-cmp-middleName">Middle Name</label> 1070 <input id="-cmp-middleName" name="middleName" value="" autocomplete="off" tabindex="-1"> 1071 </div> 1072 1073 <div class="lp-disclaimer"> <label style='display: none;' for='-termsAccepted'>Opt In/Disclaimer Consent:</label> 1074 <label class='lp-tcr-content'> 1075 <span class='lp-tcr-checkbox'> 1076 <input 1077 type='checkbox' 1078 id='-termsAccepted' 1079 name='termsAccepted' 1080 oninvalid="this.setCustomValidity('Please accept the terms and conditions')" 1081 oninput="this.setCustomValidity('')" 1082 required 1083 /></span> 1084 <span class='lp-tcr-message'> 1085 1086 I agree to be contacted by 1087 The Choi Group With Hawaii Life 1088 via call, email, and text for real estate services. To opt out, you can reply 'stop' at any time or reply 'help' for assistance. 1089 You can also click the unsubscribe link in the emails. Message and data rates may apply. Message frequency may vary. 1090 <a href='/terms-and-conditions' target='_blank'>Privacy Policy</a>. 1091 </span> 1092 </label> 1093 1094 </div> 1095 1096 <input type="hidden" name="source" value="CONTACT_INQUIRY"> 1097 1098 <div class="button-holder"> 1099 <button class="contact-form__submit btn lp-btn lp-btn--filled button-style-1" type="submit"> 1100 <span class="sync-icon lp-i--spin"> 1101 <svg width="20" height="15" viewBox="0 0 20 15" fill="currentColor" xmlns="http://www.w3.org/2000/svg"> 1102 <path d="M16.3636 3.66667L12.7273 7.33333H15.4545C15.4545 10.3675 13.0091 12.8333 10 12.8333C9.08182 12.8333 8.20909 12.6042 7.45455 12.1917L6.12727 13.53C7.24545 14.245 8.57273 14.6667 10 14.6667C14.0182 14.6667 17.2727 11.385 17.2727 7.33333H20L16.3636 3.66667ZM4.54545 7.33333C4.54545 4.29917 6.99091 1.83333 10 1.83333C10.9182 1.83333 11.7909 2.0625 12.5455 2.475L13.8727 1.13667C12.7545 0.421666 11.4273 0 10 0C5.98182 0 2.72727 3.28167 2.72727 7.33333H0L3.63636 11L7.27273 7.33333H4.54545Z" /> 1103 </svg> 1104 </span> 1105 1106 <span class="success-icon"> 1107 <svg width="20" height="20" viewBox="0 0 20 20" fill="currentColor" xmlns="http://www.w3.org/2000/svg"> 1108 <path d="M10 0C4.48 0 0 4.48 0 10C0 15.52 4.48 20 10 20C15.52 20 20 15.52 20 10C20 4.48 15.52 0 10 0ZM10 18C5.59 18 2 14.41 2 10C2 5.59 5.59 2 10 2C14.41 2 18 5.59 18 10C18 14.41 14.41 18 10 18ZM14.59 5.58L8 12.17L5.41 9.59L4 11L8 15L16 7L14.59 5.58Z" /> 1109 </svg> 1110 </span> 1111 1112 Submit<span class="success-text">ted</span> 1113 </button> 1114 </div> 1115 1116 </form> 1117 1118 </div> 1119 <div class="right" style="color: #ffffff;"> 1120 <div class="img-wrapper"> 1121 <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/ab29df6c-7264-493c-87e1-a27aeca76b90" 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/ab29df6c-7264-493c-87e1-a27aeca76b90 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/ab29df6c-7264-493c-87e1-a27aeca76b90 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/ab29df6c-7264-493c-87e1-a27aeca76b90 1280w, https://media-production.lp-cdn.com/cdn-cgi/image/format=auto,quality=85,fit=scale-down,width=1920/https://media-production.lp-cdn.com/media/ab29df6c-7264-493c-87e1-a27aeca76b90 1920w, https://media-production.lp-cdn.com/cdn-cgi/image/format=auto,quality=85/https://media-production.lp-cdn.com/media/ab29df6c-7264-493c-87e1-a27aeca76b90 2400w" class="img" alt="contact-form-background"> 1122 </div> 1123 1124 <h3 class="lp-h3">Contact Details</h3> 1125 1126 <div class="top-holder"> 1127 <div class="agent-row lp-a"> 1128 <div class="agent"> 1129 <p>THE CHOI GROUP WITH HAWAII LIFE</p> 1130 1131 <p class="lp-pre-line"><p><a href="/cdn-cgi/l/email-protection#e7848283958e84a7848f888eca9582868b939ec984888a"><span class="__cf_email__" data-cfemail="7b181e1f0912183b1813141256091e1a170f0255181416">[email protected]</span></a></p> 1132 <p><a href="tel:(808)%20285-2486">(808) 285-2486</a></p></p> 1133 </div> 1134 1135 1136 </div> 1137 1138 <p class="lp-pre-line address"> 1139 ADDRESS 1140 4614 KILAUEA AVE SUITE 201 1141 HONOLULU HI 96816 1142 </p> 1143 </div> 1144 1145 <ul class="lp-socials social_icons-style-2 site-footer__socials socials"> 1146 1147 <li> 1148 <a 1149 href="https://www.instagram.com/thechoigroup/?hl=en" 1150 class="lp-socials__link socials__item" 1151 data-wow-delay="0s" 1152 target="_blank" 1153 aria-label="INSTAGRAM" 1154 >
1155 <span class="lp-i lp-i--instagram" aria-hidden="true"></span> 1156 </a> 1157 </li> 1158 <li> 1159 <a 1160 href="https://www.linkedin.com/in/cedric-choi-b439451a2/" 1161 class="lp-socials__link socials__item" 1162 data-wow-delay="0.1s" 1163 target="_blank" 1164 aria-label="LINKEDIN" 1165 > 1166 <span class="lp-i lp-i--linkedin" aria-hidden="true"></span> 1167 </a> 1168 </li> 1169 <li> 1170 <a 1171 href="https://www.google.com/search?sca_esv=7f9887bee47bdd15&kgmid=/g/11fh_cl5vd&q=Choi+International&shndl=30&shem=lcuae,lsptbl1,uaasie&source=sh/x/loc/uni/m1/1&kgs=1d2e6f6f219da0ad&utm_source=lcuae,lsptbl1,uaasie,sh/x/loc/uni/m1/1#lrd=0x7c006d12450816bf:0x19a98c9f1f632da2,1,,,," 1172 class="lp-socials__link socials__item" 1173 data-wow-delay="0.2s" 1174 target="_blank" 1175 aria-label="GOOGLE" 1176 > 1177 <span class="lp-i lp-i--google" aria-hidden="true"></span> 1178 </a> 1179 </li> 1180 </ul> 1181 1182 </div> 1183 </div> 1184 1185 </div> 1186 <button class="close lp-close" type="button" aria-label="close modal"> 1187 <span class="lp-close-icon" aria-hidden="true"></span> 1188 </button> 1189 </div> 1190 <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;"> 1191 <style> 1192 #qualifying-questions-modal .custom-select-toggle { 1193 border: 1px solid #848484; 1194 border-radius: 8px; 1195 padding: 12px 16px; 1196 } 1197 1198 #qualifying-questions-modal .custom-select-toggle.is-placeholder { 1199 color: #848484; 1200 } 1201 1202 #qualifying-questions-modal #qualifying-questions-modal-phoneNumber::placeholder { 1203 color: #848484; 1204 opacity: 1; 1205 } 1206 1207 #qualifying-questions-modal .disclaimer-more { 1208 display: none; 1209 } 1210 1211 #qualifying-questions-modal #qualifying-questions-modal-disclaimer-text:not(.collapsed) .disclaimer-more { 1212 display: inline; 1213 } 1214 1215 #qualifying-questions-modal #qualifying-questions-modal-disclaimer-text:not(.collapsed) .disclaimer-ellipsis { 1216 display: none; 1217 } 1218 </style>
vendor: 119 bytes, lines 1218-1219
1218 1219 <script data-cfasync="false" src="/cdn-cgi/scripts/5c5dd728/cloudflare-static/email-decode.min.js"></script>
1219<script> 1220 function toggleDisclaimerQualifyingQuestionsModal() { 1221 const disclaimerText = document.getElementById('qualifying-questions-modal-disclaimer-text'); 1222 const toggleButton = document.getElementById('qualifying-questions-modal-toggle-disclaimer'); 1223 1224 if (disclaimerText.classList.contains('collapsed')) { 1225 disclaimerText.classList.remove('collapsed'); 1226 toggleButton.textContent = 'Read less'; 1227 toggleButton.setAttribute('aria-expanded', 'true'); 1228 } else { 1229 disclaimerText.classList.add('collapsed'); 1230 toggleButton.textContent = 'Read more'; 1231 toggleButton.setAttribute('aria-expanded', 'false'); 1232 } 1233 } 1234 </script>
1234 1235 <div style="max-width: 400px; width: 95%; margin: 0 auto;"> 1236 <div style="background: #fff; border: none; border-radius: 16px; color: black; box-shadow: none; position: relative;"> 1237 <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;"> 1238 <span aria-hidden="true">×</span> 1239 </button> 1240 <div style="padding: 40px 30px 32px;"> 1241 <div style="text-align: left; margin-bottom: 32px;"> 1242 <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;"> 1243 Thanks, please provide more information to help serve you 1244 </h2> 1245 </div> 1246 <form id="qualifying-questions-modal-form" data-type="contact-form"> 1247 <input type="hidden" name="source" value="PROFILE_UPDATE" /> 1248 <input type="hidden" name="email" value="" id="qualifying-questions-modal-email" /> 1249 <input type="hidden" name="triggerSource" id="qualifying-questions-modal-triggerSource" value="" /> 1250 <label for="qualifying-questions-modal-email" style="display:none;"> 1251 Email 1252 </label> 1253 <label for="qualifying-questions-modal-triggerSource" style="display:none;"> 1254 Trigger Source 1255 </label> 1256 1257 <div style="margin-bottom: 20px;"> 1258 <label for="qualifying-questions-modal-phoneNumber" style="display:none;"> 1259 Phone Number 1260 </label> 1261 <input 1262 type="tel" 1263 id="qualifying-questions-modal-phoneNumber" 1264 name="phoneNumber" 1265 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;" 1266 pattern="^\+?1?[-.\s]?\(?[0-9]{3}\)?[-.\s]?[0-9]{3}[-.\s]?[0-9]{4}$" 1267 required 1268 placeholder="Phone" 1269 aria-label="Phone Number" 1270 inputmode="tel" 1271 autocomplete="tel" 1272 onfocus="this.style.borderColor='black';" 1273 onblur="this.style.borderColor='#848484';" 1274 /> 1275 </div> 1276 1277 <div style="margin-bottom: 20px;"> 1278 <label for="qualifying-questions-modal-interest" style="display:none;"> 1279 Interest 1280 </label> 1281 <select 1282 id="qualifying-questions-modal-interest" 1283 name="interest" 1284 required 1285 data-label="Interest" 1286 aria-label="Interest" 1287 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;" 1288 onfocus="this.style.borderColor='black'; this.style.color='black';" 1289 onblur="this.style.borderColor='#848484'; if(this.value === '') this.style.color='#848484'; else this.style.color='black';" 1290 > 1291 <option value="" style="background: #fff; color: #848484;" disabled selected>Interested in...</option> 1292 <option value="Selling & Buying" style="background: #fff; color: black;">Selling & Buying</option> 1293 <option value="Selling" style="background: #fff; color: black;">Selling</option> 1294 <option value="Buying" style="background: #fff; color: black;">Buying</option> 1295 <option value="Renting" style="background: #fff; color: black;">Renting</option> 1296 <option value="Other" style="background: #fff; color: black;">Other</option> 1297 </select> 1298 </div> 1299 1300 <div style="margin-bottom: 32px; font-size: 12px; line-height: 1.2;"> 1301 <label style='display: none;' for='qualifying-questions-modal-qualifying-questions-termsAccepted'>Opt In/Disclaimer Consent:</label> 1302 <label class='lp-tcr-content'>
1303 <span class='lp-tcr-checkbox'> 1304 <input 1305 type='checkbox' 1306 id='qualifying-questions-modal-qualifying-questions-termsAccepted' 1307 name='termsAccepted' 1308 aria-describedby='qualifying-questions-modal-disclaimer-text' 1309 oninvalid="this.setCustomValidity('Please accept the terms and conditions')" 1310 oninput="this.setCustomValidity('')" 1311 required 1312 /></span> 1313 <span class='lp-tcr-message'> 1314 <div id="qualifying-questions-modal-disclaimer-text" class="collapsed" style="color: #848484;"> 1315 1316 I agree to be contacted by 1317 <strong>The Choi Group With Hawaii Life</strong> 1318 via call, email, and text <span class="disclaimer-more">for real estate services. 1319 To opt out, you can reply 'stop' at any time or reply 'help' for assistance. 1320 You can also click the unsubscribe link in the emails. 1321 Message and data rates may apply. Message frequency may vary.</span> 1322 <span class="disclaimer-ellipsis">...</span> 1323 <a href='/terms-and-conditions' target='_blank' rel='noopener noreferrer' style='text-decoration: underline;'>Privacy Policy</a>. 1324 </div> 1325 <button 1326 id="qualifying-questions-modal-toggle-disclaimer" 1327 type="button" 1328 style="background: none; border: none; color: #848484; font-size: inherit; font-weight: bold; padding: 0; margin-top: 4px; cursor: pointer;" 1329 aria-expanded="false" 1330 onclick="toggleDisclaimerQualifyingQuestionsModal()" 1331 > 1332 Read more 1333 </button> 1334 </span> 1335 </label> 1336 </div> 1337 1338 <div> 1339 <button 1340 type="submit" 1341 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;" 1342 onmouseover="this.style.background='transparent'; this.style.color='black';" 1343 onmouseout="this.style.background='black'; this.style.color='white';" 1344 > 1345 Submit 1346 </button> 1347 </div> 1348 </form> 1349 </div> 1350 </div> 1351 </div> 1352 </div> 1353 1354
1354<script> 1355 (function() { 1356 var lazyVideos = [].slice.call(document.querySelectorAll("video.lazy")); 1357 1358 if ("IntersectionObserver" in window && lazyVideos.length > 0) { 1359 var lazyVideoObserver = new IntersectionObserver(function(entries, observer) { 1360 entries.forEach(function(video) { 1361 if (video.isIntersecting) { 1362 for (var source in video.target.children) { 1363 var videoSource = video.target.children[source]; 1364 if (typeof videoSource.tagName === "string" && videoSource.tagName === "SOURCE") { 1365 videoSource.src = videoSource.dataset.src; 1366 } 1367 } 1368 1369 video.target.load(); 1370 video.target.classList.remove("lazy"); 1371 lazyVideoObserver.unobserve(video.target); 1372 } 1373 }); 1374 }); 1375 1376 lazyVideos.forEach(function(lazyVideo) { 1377 lazyVideoObserver.observe(lazyVideo); 1378 }); 1379 } 1380 })(); 1381 </script>
1381 1382 1383 <!-- Global site tag (gtag.js) - Google Analytics --> 1384
1384<script defer src="https://www.googletagmanager.com/gtag/js"></script>
1384 1385 1386 <!-- Google Tag Manager --> 1387
1387<script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start': 1388 new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0], 1389 j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.defer=true;j.async=false;j.src= 1390 'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f); 1391 })(window,document,'script','dataLayer','GTM-T9SFQGHZ');</script>
1391 1392 <!-- End Google Tag Manager --> 1393 1394 1395 <!-- LP Tracker (divolte.js) - LP Divolte --> 1396
1396<script>window.divolteLp=window.divolteLp||function(){(divolteLp.q=divolteLp.q||[]).push(arguments)}</script>
1396 1397 <!-- LP Tracker (eventBus) - LP EventBus --> 1398
1398<script>window.eventBusLp=window.eventBusLp||function(){(eventBusLp.q=eventBusLp.q||[]).push(arguments)}</script>
1398 1399 1400
1400<script> 1401 window.dataLayer = window.dataLayer || []; 1402 document.sendGoogleTagManagerEvent = function() { 1403 /* Honor GPC-enabled browsers and prevent GTM event if GPC is enabled. More details: https://global-privacy-control.glitch.me/ */ 1404 if (window.navigator.globalPrivacyControl) { 1405 return; 1406 } 1407 1408 dataLayer.push(arguments); 1409 } 1410 1411 document.sendGoogleTagManagerEvent('js', new Date()); 1412 document.sendGoogleTagManagerEvent('config', 'G-H7B633F91X', { page: location.pathname, location: location.href }); 1413 document.sendGoogleAnalyticsEvent = function(googleEvent) { 1414 document.sendGoogleTagManagerEvent('event', googleEvent.eventAction || googleEvent.hitType, { 1415 event_category: googleEvent.eventCategory, 1416 event_label: googleEvent.eventLabel, 1417 value: googleEvent.eventValue, 1418 page: googleEvent.page, 1419 location: googleEvent.location 1420 }) 1421 }; 1422 1423 document.addEventListener("DOMContentLoaded", function() { 1424 (function() { 1425 //If the page has an element with ID of auto-year-update the element will be populated with the current year. 1426 var date = new Date(); 1427 var elements = document.getElementsByClassName('auto-year-update'); 1428 var i; 1429 for (i = 0; i < elements.length; i++) { 1430 elements[i].innerText = date.getFullYear(); 1431 } 1432 })(); 1433 if (window.divolteLp) { 1434 window.divolteLp('sendLpPageView', {}); 1435 } 1436 if (window.eventBusLp) { 1437 window.eventBusLp('sendLpPageView', {}); 1438 } 1439 }); 1440 </script>
1440 1441 1442 1443
1443<script> 1444 // Function to dynamically load a script 1445 function loadScript(url, callback) { 1446 var script = document.createElement('script'); 1447 script.type = 'text/javascript'; 1448 script.src = url; 1449 script.defer = true; 1450 script.async = true; 1451 script.onload = callback; 1452 document.head.appendChild(script); 1453 } 1454 1455 // Function to dynamically load CSS file 1456 function loadCSS(url) { 1457 var link = document.createElement("link"); 1458 link.rel = "stylesheet"; 1459 link.href = url; 1460 document.head.appendChild(link); 1461 } 1462 1463 // Global methods / variables that can be accessed by element js 1464 window.luxuryPresence = { 1465 libs: { 1466 loadJs: loadScript, 1467 loadCss: loadCSS, 1468 jsLibsMap: { 1469 paroller: { 1470 jsSrc: 'https://d1e1jt2fj4r8r.cloudfront.net/javascript/jquery.paroller.min.js', 1471 isLoaded: false, 1472 }, 1473 ionRangeSlider: { 1474 jsSrc: 'https://cdnjs.cloudflare.com/ajax/libs/ion-rangeslider/2.3.1/js/ion.rangeSlider.min.js', 1475 cssSrc: 'https://cdnjs.cloudflare.com/ajax/libs/ion-rangeslider/2.3.1/css/ion.rangeSlider.min.css', 1476 isLoaded: false, 1477 requires: ['jquery'], 1478 }, 1479 splide: { 1480 jsSrc: 'https://cdnjs.cloudflare.com/ajax/libs/splidejs/4.1.4/js/splide.min.js', 1481 cssSrc: 'https://cdnjs.cloudflare.com/ajax/libs/splidejs/4.1.4/css/splide-core.min.css', 1482 isLoaded: false, 1483 }, 1484 wow: { 1485 jsSrc: 'https://cdnjs.cloudflare.com/ajax/libs/wow/1.1.2/wow.min.js', 1486 isLoaded: false, 1487 }, 1488 jquery: { 1489 jsSrc: 'https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js', 1490 isLoaded: false, 1491 }, 1492 slick: { 1493 jsSrc: 'https://cdnjs.cloudflare.com/ajax/libs/slick-carousel/1.8.1/slick.min.js', 1494 isLoaded: false, 1495 requires: ['jquery'], 1496 }, 1497 libphonenumber: { 1498 jsSrc: 'https://cdnjs.cloudflare.com/ajax/libs/libphonenumber-js/1.11.8/libphonenumber-js.min.js', 1499 isLoaded: false, 1500 }, 1501 }, 1502 }, 1503 }; 1504 </script>
1504 1505
1505<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>
1505 1506
1506<script defer src="https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/4.7.8/handlebars.min.js"></script>
1506 1507
1507<script defer src="https://cdn.jsdelivr.net/npm/[email protected]/dist/handlebars-intl.min.js"></script>
1507 1508
1508<script defer src="https://cdnjs.cloudflare.com/ajax/libs/superagent/4.1.0/superagent.min.js"></script>
1508 1509
1509<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>
1509 1510
1510<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>
1510 1511
1511<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>
1511 1512 1513 <!-- GOOGLE TRANSLATE SCRIPT --> 1514
1514<script> 1515 function googleTranslateElementInit() { 1516 // list of languages can be overridden by setting window.customLanguages in the head JS. example `window.customLanguages = 'en,de,fr,ro,sk,pl'` 1517 // Language codes can be taken from here https://cloud.google.com/translate/docs/languages 1518 const DEFAULT_LANGUAGES = 'en,es,zh-CN,zh-TW,tl,vi,ar,ko,ru,de,fr,hi,pt'; 1519 document.querySelectorAll('[data-type="TRANSLATE"]').forEach(function(translate){ 1520 translate.innerText = ''; 1521 new google.translate.TranslateElement({ 1522 pageLanguage: 'en', 1523 layout: google.translate.TranslateElement.InlineLayout.SIMPLE, 1524 includedLanguages: window.customLanguages || DEFAULT_LANGUAGES, 1525 }, translate); 1526 }); 1527 } 1528 </script>
1528 1529
1529<script defer src="//translate.google.com/translate_a/element.js?cb=googleTranslateElementInit"></script>
1529 1530
1530<script defer src="https://accounts.google.com/gsi/intermediate"></script>
1530 1531 1532 <!-- GOOGLE TRANSLATE SCRIPT END--> 1533
1533<script> 1534 1535 // UI helpers 1536 (function () { 1537 1538 function getSideMenu () { 1539 return document.querySelector('body > div.sidemenu'); 1540 } 1541 1542 function sideMenuHide (instant) { 1543 var sideMenu = getSideMenu(); 1544 if (!sideMenu || !sideMenu.classList.contains('visible')) { 1545 return; 1546 } 1547 sideMenu.classList.remove('visible'); 1548 sideMenu.dispatchEvent(new CustomEvent('lpui-hide', { detail: { instant: !!instant } })); 1549 window.dispatchEvent(new CustomEvent('lpui-sidemenu-hide', { detail: {instant: !!instant} })); 1550 } 1551 1552 function sideMenuShow () { 1553 var sideMenu = getSideMenu(); 1554 if (!sideMenu || sideMenu.classList.contains('visible')) { 1555 return; 1556 } 1557 sideMenu.classList.add('visible'); 1558 sideMenu.dispatchEvent(new Event('lpui-show')); 1559 window.dispatchEvent(new Event('lpui-sidemenu-show')); 1560 } 1561 1562 function sideMenuToggle () { 1563 var sideMenu = getSideMenu(); 1564 if (!sideMenu) { 1565 return; 1566 } 1567 var isSideMenuVisible = sideMenu.classList.contains('visible'); 1568 if (isSideMenuVisible) { 1569 sideMenuHide(); 1570 } else { 1571 sideMenuShow(); 1572 } 1573 } 1574 1575 var scrollLockRequests = {}; 1576 window.__debug_scrollLockRequests = scrollLockRequests; 1577 var hideScrollStyles = ''+ 1578 'body {'+ 1579 'overflow: hidden;' + 1580 '}'; 1581 var hideScrollId = 'hide-body-scroll-style'; 1582 1583 function scrollLock () { 1584 var style = document.createElement('style'); 1585 style.id = hideScrollId; 1586 style.textContent = hideScrollStyles; 1587 document.head.appendChild(style); 1588 window.dispatchEvent(new Event('lpui-scroll-locked')); 1589 } 1590 1591 function scrollUnlock () { 1592 document.getElementById(hideScrollId)?.remove(); 1593 window.dispatchEvent(new Event('lpui-scroll-unlocked')); 1594 } 1595 1596 function requestScrollLock (lockId) { 1597 var wasLocked = Object.keys(scrollLockRequests).length > 0; 1598 scrollLockRequests[lockId || '__anonymous_lock'] = true; 1599 if (!wasLocked) { 1600 scrollLock(); 1601 } 1602 } 1603 1604 function releaseScrollLock (lockId) { 1605 if (lockId) { 1606 delete scrollLockRequests[lockId]; 1607 if (!Object.keys(scrollLockRequests).length) { 1608 scrollUnlock(); 1609 } 1610 } else { 1611 scrollLockRequests = {}; 1612 window.__debug_scrollLockRequests = scrollLockRequests; 1613 scrollUnlock(); 1614 } 1615 } 1616 1617 function showModal (modalId, options) { 1618 var modal = document.getElementById(modalId); 1619 if (!modal) { 1620 return; 1621 } 1622 1623 if (!modal.classList.contains('visible')) { 1624 modal.classList.add('visible'); 1625 if (options && options.scrollLock) { 1626 window.lpUI.requestScrollLock(modalId); 1627 } 1628 modal.dispatchEvent(new Event('lpui-show')); 1629 window.dispatchEvent(new CustomEvent('lpui-modal-show', { detail: { id: modalId } })); 1630 } 1631 1632 const visibleModals = document.querySelectorAll('.modal.visible'); 1633 1634 if (visibleModals.length > 1) { 1635 1636 const maxZIndex = Array.from(visibleModals).reduce((maxZIndex, modal) => { 1637 const zIndex = window.getComputedStyle(modal).zIndex; 1638 const parsedZIndex = zIndex !== 'auto' ? parseInt(zIndex) : 0; 1639 return Math.max(maxZIndex, parsedZIndex); 1640 }, 0); 1641 1642 modal.style.zIndex = maxZIndex + 1; 1643 } 1644 1645 } 1646 1647 function _hideModal (modalEl) { 1648 if (!modalEl) { 1649 return; 1650 } 1651 if (!modalEl.classList.contains('visible')) { 1652 return; 1653 } 1654 modalEl.classList.remove('visible'); 1655 modalEl.style.zIndex = '';
1656 modalEl.dispatchEvent(new Event('lpui-hide')); 1657 var modalId = modalEl.getAttribute('id'); 1658 if (modalId) { 1659 window.lpUI.releaseScrollLock(modalId); 1660 window.dispatchEvent(new CustomEvent('lpui-modal-hide', { detail: { id: modalId } })); 1661 } 1662 } 1663 1664 function hideModal (modalId) { 1665 _hideModal(document.getElementById(modalId)); 1666 } 1667 1668 function hideClosestModal (el) { 1669 const modal = el?.closest('.modal'); 1670 if (!modal) { 1671 return; 1672 } 1673 if (modal.classList) { 1674 _hideModal(modal); 1675 return; 1676 } 1677 if (modal.length && modal[0].classList) { 1678 _hideModal(modal[0]); 1679 } 1680 } 1681 1682 window.lpUI = { 1683 sideMenuHide: sideMenuHide, 1684 sideMenuShow: sideMenuShow, 1685 sideMenuToggle: sideMenuToggle, 1686 requestScrollLock: requestScrollLock, 1687 releaseScrollLock: releaseScrollLock, 1688 showModal: showModal, 1689 hideModal: hideModal, 1690 hideClosestModal: hideClosestModal 1691 } 1692 })(); 1693 1694 var stringifyPrimitive = function(v) { 1695 switch (typeof v) { 1696 case 'string': 1697 return v; 1698 1699 case 'boolean': 1700 return v ? 'true' : 'false'; 1701 1702 case 'number': 1703 return isFinite(v) ? v : ''; 1704 1705 default: 1706 return ''; 1707 } 1708 }; 1709 1710 function objectToQuerystring(obj, sep, eq, name) { 1711 // https://github.com/Gozala/querystring/blob/master/encode.js 1712 sep = sep || '&'; 1713 eq = eq || '='; 1714 if (obj === null) { 1715 obj = undefined; 1716 } 1717 1718 if (typeof obj === 'object') { 1719 return Object.keys(obj).map(function(k) { 1720 var ks = encodeURIComponent(stringifyPrimitive(k)) + eq; 1721 if (Array.isArray(obj[k])) { 1722 return obj[k].map(function(v) { 1723 return ks + encodeURIComponent(stringifyPrimitive(v)); 1724 }).join(sep); 1725 } else { 1726 return ks + encodeURIComponent(stringifyPrimitive(obj[k])); 1727 } 1728 }).filter(Boolean).join(sep); 1729 1730 } 1731 1732 if (!name) return ''; 1733 return encodeURIComponent(stringifyPrimitive(name)) + eq + 1734 encodeURIComponent(stringifyPrimitive(obj)); 1735 } 1736 1737 function getPath(url, options) { 1738 var parser = document.createElement('a'); 1739 parser.href = url || ''; 1740 var path = parser.pathname || ''; 1741 if (path[0] !== '/') { 1742 // IE does not return path starting with a slash 1743 path = '/' + path; 1744 } 1745 1746 if (options && options.queryString) { 1747 path += parser.search; 1748 } 1749 1750 return path; 1751 } 1752 1753 function getMLSAutocomplete(keyword) { 1754 var searchURL = window.location.protocol + '//' + window.location.hostname + '/home-search/auto_complete'; 1755 var query = { 1756 kind: 'listings', 1757 query: keyword 1758 }; 1759 1760 return superagent 1761 .get(searchURL) 1762 .query(query) 1763 .then(function(res) { 1764 var data = res.body || []; 1765 /* 1766 Of the format: 1767 [{ 1768 "_index": "listing_auto_completes_production_20190723232720775", 1769 "_type": "listing_auto_complete", 1770 "_id": "235875624", 1771 "_score": 0, 1772 "status_modified_at": 1553250708, 1773 "city": "Jupiter", 1774 "price": 1335743, 1775 "display_address": "10088 Calabrese Trail Unit: 9 Jupiter FL 33478", 1776 "sold_price": null, 1777 "id": 235875624, 1778 "state": "FL", 1779 "photo_urls": [ 1780 "http://cdn.photos.sparkplatform.com/fl/20190322153144208228000000-o.jpg" 1781 ], 1782 "status": "Pending" 1783 }, ...] 1784 */ 1785 1786 return data; 1787 }); 1788 } 1789 1790 function getMLSAutocompleteWithFilters(keyword, filters = {}) { 1791 var searchURL = window.location.protocol + '//' + window.location.hostname + '/home-search/auto_complete'; 1792 1793 return superagent 1794 .get(searchURL) 1795 .query({ kind: 'listings', query: keyword, ...filters }) 1796 .then(function(res) { 1797 /* 1798 Of the format: 1799 [{ 1800 "_index": "listing_auto_completes_production_20190723232720775", 1801 "_type": "listing_auto_complete", 1802 "_id": "235875624", 1803 "_score": 0, 1804 "status_modified_at": 1553250708, 1805 "city": "Jupiter", 1806 "price": 1335743, 1807 "display_address": "10088 Calabrese Trail Unit: 9 Jupiter FL 33478", 1808 "sold_price": null, 1809 "id": 235875624, 1810 "state": "FL", 1811 "photo_urls": [ 1812 "http://cdn.photos.sparkplatform.com/fl/20190322153144208228000000-o.jpg" 1813 ], 1814 "status": "Pending" 1815 }, ...] 1816 */ 1817 return res.body || []; 1818 }); 1819 } 1820 1821 function makeMlsLink(data) { 1822 // Code Dupe from utils/googlePlace 1823 // mapping of google data type to mls data type 1824 var mapping = { 1825 neighborhood: 'neighborhood', 1826 locality: 'place', 1827 administrative_area_level_2: 'county', 1828 administrative_area_level_1: 'state', 1829 postal_code: 'postalcode', 1830 }; 1831 1832 function _filterTypes(types) { 1833 return types.filter(function(type) { return type !== 'political' }); 1834 } 1835 1836 if (!data || !Object.keys(data).length) { 1837 return '/home-search/listings'; 1838 } 1839 1840 var newData = {};
1841 _filterTypes(data.types).forEach(function(type) { 1842 if (mapping[type]) { 1843 newData.search_type = mapping[type]; 1844 } 1845 }); 1846 newData.omnibox = data.formatted_address; 1847 data.address_components.forEach(function(component) { 1848 _filterTypes(component.types).forEach(function(type) { 1849 if (mapping[type]) { 1850 newData[mapping[type]] = component.long_name; 1851 } 1852 }); 1853 }); 1854 var bounds = data.geometry.bounds; 1855 var convertedData = Object.assign(bounds, newData); 1856 1857 return '/home-search/listings?' + objectToQuerystring(convertedData); 1858 } 1859 1860 function debounce(func, wait) { 1861 let timeout; 1862 return function(...args) { 1863 const context = this; 1864 clearTimeout(timeout); 1865 timeout = setTimeout(() => func.apply(context, args), wait) 1866 } 1867 } 1868 1869 // Global methods / variables that can be accessed by element js 1870 window.luxuryPresence = { 1871 ...window.luxuryPresence, 1872 defaults: { 1873 companyId: '64eccd3e-c115-4e58-ba5c-a7d7cd11cf04', 1874 apiGatewayUrl: '/api-gw', 1875 routerUrl: '/api-nv', 1876 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\
1876n }\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 ) {\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 ) {\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 ) {\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-
1876abb7-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":"\nquery PointsOfInterest(\n $regionId: String!,\n $radius: Int,\n $offset: Int,\n $limit: Int,\n $categories: [PoiCategoryEnum!],\n $sortBy: PoiSortByEnum\n) {\n pois(\n regionId: $regi
1876onId,\n radius: $radius,\n offset: $offset,\n limit: $limit,\n categories: $categories,\n sortBy: $sortBy\n ) {\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 }\n","offices":"\n query Offices (\n $officeId: ID\n $officeIds: [ID!]\n $companyId: String\n $slug: String\n $sort: String\n $sortDir: SortDirectionEnum\n $search: String\n $tags: [String]\n $offset: Int\n $limit: Int\n ) {\n offices (\n officeId: $officeId\n officeIds: $officeIds\n companyId: $companyId\n slug: $slug\n sort: $sort\n sortDir: $sortDir\n search: $search\n tags: $tags\n offset: $offset\n limit: $limit\n ) {\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 media {\n id\n originalUrl\n thumbnailUrl\n smallUrl\n mediumUrl\n largeUrl\n xLargeUrl\n xxLargeUrl\n }\n }\n officesCount(\n companyId: $companyId\n officeId: $officeId\n officeIds: $officeIds\n slug: $slug\n search: $search\n tags: $tags\n ) {\n count\n }\n }\n","teams":"query TeamsAndCount(\n $offset: Int\n $limit: Int\n $search: String\n $sort: String\n $sortDir: SortDirectionEnum\n $companyId: ID\n) {\n teams(\n offset: $offset\n limit: $limit\n sort: $sort\n sortDir: $sortDir\n search: $search\n companyId: $companyId\n ) {\n \n id\n companyId\n name\n email\n shortBio\n fullBio\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 }\n teamsCount(\n search: $search\n companyId: $companyId\n ) {\n count\n }\n}"}, 1877 propertyPlaceholderImage: {"id":"a28f5b5f-0814-4eb7-8950-d8485ddb22e7","alt":null,"bytes":54317,"width":1920,"format":"jpeg","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/a8f0a01e-cecb-488c-8576-28d93e8b7819","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/a8f0a01e-cecb-488c-8576-28d93e8b7819","authorUrl":null,"createdAt":"2025-09-02T22:19:57.654Z","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/a8f0a01e-cecb-488c-8576-28d93e8b7819","sourceUrl":null,"updatedAt":"2025-09-02T22:19:57.654Z","xLargeUrl":"https://media-production.lp-cdn.com/cdn-cgi/image/format=auto,quality=85/https://media-production.lp-cdn.com/media/a8f0a01e-cecb-488c-8576-28d93e8b7819","__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/a8f0a01e-cecb-488c-8576-28d93e8b7819","description":null,"displayName":"property","originalUrl":"https://media-production.lp-cdn.com/cdn-cgi/image/format=auto,quality=85/https://media-production.lp-cdn.com/media/a8f0a01e-cecb-488c-8576-28d93e8b7819","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/a8f0a01e-cecb-488c-8576-28d93e8b7819","originalFileName":"Property","cloudinaryPublicId":"a8f0a01e-cecb-488c-8576-28d93e8b7819"} 1878 }, 1879 objectToQuerystring: objectToQuerystring, 1880 getPath: getPath, 1881 getMLSAutocomplete: getMLSAutocomplete, 1882 getMLSAutocompleteWithFilters: getMLSAutocompleteWithFilters, 1883 makeMlsLink: makeMlsLink, 1884 debounce: debounce, 1885 }; 1886 1887 var wowOffset = 100; 1888 </script>
1888 1889
1889<script> 1890 (() => { 1891 1892function $parcel$interopDefault(a) { 1893 return a && a.__esModule ? a.default : a; 1894} 1895// Entry point for lp-sdk bundle 1896// This file exists to ensure Parcel v2 outputs the correct filename 1897/* global window */ var $53de3c160faf5a81$exports = {}; 1898/* 1899WARNING!!! This file is used both on the server and in client's web-browser 1900please don't require/import any server-side related config/functions here 1901If we need something, let's pass it as a parameter of the factory 1902*/ var $0ce6edea77f7b81a$exports = {}; 1903$0ce6edea77f7b81a$exports = { 1904 getVideoFirstFrame (videoUrl) { 1905 // The default Cloudinary asset delivery URL has the following structure: https://res.cloudinary.com/<cloud_name>/<asset_type>/<delivery_type>/<transformations>/<version>/<public_id>.<extension> 1906 // https://cloudinary.com/documentation/video_manipulation_and_delivery#transformation_url_structure 1907 // The problem is that <transformations> and <version> are optional, and <transformations> can be chained separated by '/' 1908 // One more issue to be handled https://cloudinary.com/documentation/advanced_url_delivery_options#seo_friendly_media_asset_urls 1909 const CLOUDINARY_URL = 'https://res.cloudinary.com/'; 1910 const invalid = !videoUrl || !videoUrl.startsWith(CLOUDINARY_URL); 1911 if (invalid) return null; 1912 const urlComponents = videoUrl.replace(CLOUDINARY_URL, '').replace('f_auto:video', 'f_auto').split('/'); 1913 // checking if <asset_type> is not video or videos 1914 if (urlComponents[1].toLowerCase().indexOf('video') === -1) return null; 1915 // Filename is last urlComponent <public_id>.<extension> 1916 let filename = urlComponents[urlComponents.length - 1]; 1917 if (filename.lastIndexOf('.') > -1) filename = `${filename.slice(0, filename.lastIndexOf('.'))}.jpg`; 1918 else filename += '.jpg'; 1919 urlComponents[urlComponents.length - 1] = filename; 1920 // We need to add so_0,eo_0 transformation to the url to target the first frame. 1921 // we want to find the position at which we want to insert it as a chained transformation 1922 const firstFrameTransformation = 'so_0,eo_0'; 1923 let firstFrameTransformationPosition = 3; 1924 // checking cases for SEO-friendly URLs 1925 if (urlComponents[1].toLowerCase() === 'videos' && urlComponents[2] !== '') firstFrameTransformationPosition = 2; 1926 // checking the last transformation position in the url 1927 // https://cloudinary.com/documentation/transformation_reference 1928 // https://regex101.com/r/lE3W6u/1 1929 const transformationRegex = /^(?:[a-z]{1,3}|\$\w+)_\S+$/; 1930 while(firstFrameTransformationPosition < urlComponents.length - 1 && transformationRegex.test(urlComponents[firstFrameTransformationPosition]))firstFrameTransformationPosition++; 1931 urlComponents.splice(firstFrameTransformationPosition, 0, firstFrameTransformation); 1932 return CLOUDINARY_URL + urlComponents.join('/'); 1933 } 1934}; 1935 1936 1937/** 1938 * Create helpers with given Handlebars instance 1939 * 1940 * @param {Object} HandlebarsInstance 1941 * @param {Function} isEqual - we use different implementation if isEqual on server and on client 1942 * @return {Object} 1943 */ function $53de3c160faf5a81$var$handlebarsHelpersFactory(HandlebarsInstance, { isEqual: isEqual }) { 1944 // notice!: code below assumes list is ordered lowest to highest width 1945 const MEDIA_SIZES = [ 1946 { 1947 field: 'thumbnailUrl', 1948 w: 320 1949 }, 1950 { 1951 field: 'smallUrl', 1952 w: 960 1953 }, 1954 { 1955 field: 'mediumUrl', 1956 w: 1280 1957 }, 1958 { 1959 field: 'largeUrl', 1960 w: 1920 1961 }, 1962 { 1963 field: 'xLargeUrl', 1964 w: 2560 1965 }, 1966 { 1967 field: 'xxLargeUrl', 1968 w: 3456 1969 } 1970 ]; 1971 const MEDIA_SIZE_FOR_SRC = 1280; 1972 const lpFormatNumber = function(number, unit, precision) { 1973 const localization = 'en-US'; 1974 if (!number) return ''; 1975 if (typeof number === 'string') { 1976 const trimmed = number.trim(); 1977 if (/[^+\-0-9.]/.test(trimmed)) return number; // returning text 'as is' assuming it already has all formatting 1978 // eslint-disable-next-line 1979 number = parseFloat(trimmed); 1980 if (!number) return ''; 1981 } 1982 const currencyPrecision = typeof precision === 'number' ? precision : 0; 1983 const currencyFractionDigits = Number.isInteger(number) ? 0 : currencyPrecision; 1984 const currencyFractionOptions = { 1985 minimumFractionDigits: currencyFractionDigits, 1986 maximumFractionDigits: currencyFractionDigits 1987 };
1988 if (unit === 'USD') return `$${number.toLocaleString(localization, currencyFractionOptions)}`; 1989 if (unit === 'CAD') return `$${number.toLocaleString(localization, currencyFractionOptions)} CAD`; 1990 if (unit === 'EUR') return `\u{20AC}${number.toLocaleString(localization, currencyFractionOptions)}`; 1991 if (unit === 'GBP') return `\xa3${number.toLocaleString(localization, currencyFractionOptions)}`; 1992 if (unit === 'PERCENT') return `${number}%`; 1993 if (typeof precision === 'number' && precision > -1) return number.toLocaleString(localization, { 1994 maximumFractionDigits: precision 1995 }); 1996 return number.toLocaleString(localization); 1997 }; 1998 const formatFrequency = function(freqUnit) { 1999 switch(freqUnit){ 2000 case 'DAY': 2001 return 'daily'; 2002 case 'MONTH': 2003 return 'monthly'; 2004 case 'WEEK': 2005 return 'weekly'; 2006 case 'YEAR': 2007 return 'yearly'; 2008 case 'QUARTER': 2009 return 'quarterly'; 2010 case 'HALF_YEAR': 2011 return 'semi-annually'; 2012 case 'NIGHTLY': 2013 return 'nightly'; 2014 case 'SEASONAL': 2015 return 'seasonally'; 2016 default: 2017 return ''; 2018 } 2019 }; 2020 const formatFrequencyShort = function(freqUnit) { 2021 switch(freqUnit){ 2022 case 'DAY': 2023 return 'day'; 2024 case 'WEEK': 2025 return 'wk'; 2026 case 'MONTH': 2027 return 'mo'; 2028 case 'YEAR': 2029 return 'yr'; 2030 case 'QUARTER': 2031 return '3 mos'; 2032 case 'HALF_YEAR': 2033 return '6 mos'; 2034 case 'NIGHTLY': 2035 return 'night'; 2036 case 'SEASONAL': 2037 return 'season'; 2038 default: 2039 return ''; 2040 } 2041 }; 2042 const formatFrequencyStd = function(freqUnit) { 2043 switch(freqUnit){ 2044 case 'DAY': 2045 return 'day'; 2046 case 'WEEK': 2047 return 'week'; 2048 case 'MONTH': 2049 return 'month'; 2050 case 'YEAR': 2051 return 'year'; 2052 case 'QUARTER': 2053 return 'quarter'; 2054 case 'HALF_YEAR': 2055 return '6 months'; 2056 case 'NIGHTLY': 2057 return 'night'; 2058 case 'SEASONAL': 2059 return 'season'; 2060 default: 2061 return ''; 2062 } 2063 }; 2064 const formatFrequencyExt = function(unit, options) { 2065 let val; 2066 let separator; 2067 const opts = options || {}; 2068 switch(opts.mode){ 2069 case 'short': 2070 val = formatFrequencyShort(unit); 2071 separator = opts.frequencySeparator || '/'; 2072 break; 2073 case 'standard': 2074 case 'std': 2075 val = formatFrequencyStd(unit); 2076 separator = opts.frequencySeparator || '/'; 2077 break; 2078 case 'long': 2079 default: 2080 val = formatFrequency(unit); 2081 separator = opts.frequencySeparator || ' '; 2082 } 2083 if (!val) return ''; 2084 if (!opts.frequencyTag && !opts.frequencyClass) return separator + val; 2085 return `${separator}<${opts.frequencyTag || 'span'}${opts.frequencyClass ? ` class="${opts.frequencyClass}"` : ''}>${val}</${opts.frequencyTag || 'span'}>`; 2086 }; 2087 return { 2088 isEmpty (arg1, options) { 2089 const result = !arg1 || (typeof arg1 === 'string' ? !arg1.trim() : false) || !Object.keys(arg1).length; 2090 if (options.fn) return result ? options.fn(this) : options.inverse(this); 2091 return result; 2092 }, 2093 ifEquals (arg1, arg2, options) { 2094 return isEqual(arg1, arg2) ? options.fn(this) : options.inverse(this); 2095 }, 2096 'raw-helper': function(options) { 2097 return options.fn(); 2098 }, 2099 striptags (txt) { 2100 if (!txt) return ''; 2101 // Remove HTML Comments (escaping something to not make real html comment by expression) 2102 // eslint-disable-next-line no-useless-escape,no-param-reassign 2103 txt = txt.replace(/<\!--(.|[\r\n])*?-\->/g, ''); 2104 // Remove XML fragments and <style> and <svg> tags with content 2105 // eslint-disable-next-line 2106 txt = txt.replace(/<(xml|style|svg)("[^"]*"|'[^']*'|[^>])*>(.|[\r\n])*?<\/\1>/gi, ''); 2107 // Replace with space 2108 // eslint-disable-next-line no-param-reassign 2109 txt = txt.replace(/ /g, ' '); 2110 // Remove HTML Tags 2111 const regexp = /<\/?("[^"]*"|'[^']*'|[^>])*(>|$)/g; 2112 return txt.replace(regexp, ''); 2113 },
2114 striptagsLineBreaks (txt) { 2115 if (!txt) return ''; 2116 // Remove HTML Comments (escaping something to not make real html comment by expression) 2117 // eslint-disable-next-line no-useless-escape,no-param-reassign 2118 txt = txt.replace(/<\!--(.|[\r\n])*?-\->/g, ''); 2119 // Remove XML fragments and <style> and <svg> tags with content 2120 // eslint-disable-next-line no-param-reassign 2121 txt = txt.replace(/<(xml|style|svg)("[^"]*"|'[^']*'|[^>])*>(.|[\r\n])*?<\/\1>/gi, ''); 2122 // Replace <br> and </p> tags with line break char, skipping last </p> which is not followed by <br> or </p> 2123 // eslint-disable-next-line no-param-reassign 2124 txt = txt.replace(/(<br\s*\/?>)|(<\/p\s*>)(?=.*(<\/p\s*>|<br\s*\/?>))/g, ' '); 2125 // Replace with space 2126 // eslint-disable-next-line no-param-reassign 2127 txt = txt.replace(/ /g, ' '); 2128 // Remove HTML Tags 2129 const regexp = /<\/?("[^"]*"|'[^']*'|[^>])*(>|$)/g; 2130 return txt.replace(regexp, ''); 2131 }, 2132 truncate (text, limit, suffix) { 2133 if (!text) return ''; 2134 if (text.length > limit) return typeof suffix === 'string' ? text.slice(0, limit - suffix.length) + suffix : text.slice(0, limit); 2135 return text; 2136 }, 2137 // eslint-disable-next-line 2138 setSubArray (items = [], start, end, subArrayName, options) { 2139 // eslint-disable-next-line 2140 options.data.root[subArrayName] = items.slice(start, end); 2141 }, 2142 formatAreaUnit (areaUnit) { 2143 switch(areaUnit){ 2144 case 'ACRE': 2145 return 'Acres'; 2146 case 'SQUARE_METER': 2147 return 'Sq.M.'; 2148 case 'SQUARE_YARD': 2149 return 'Sq.Yd.'; 2150 default: 2151 return 'Sq.Ft.'; 2152 } 2153 }, 2154 formatFrequency: formatFrequency, 2155 formatFrequencyShort: formatFrequencyShort, 2156 lpFormatFrequency (freqUnit, options) { 2157 return formatFrequencyExt(freqUnit, options && options.hash); 2158 }, 2159 subArray (index, start, end, options) { 2160 return index >= start && index < end ? options.fn(this) : options.inverse(this); 2161 }, 2162 formatPropertyStatus (status, showSoldDate, date) { 2163 let formattedStatus = ''; 2164 switch(status){ 2165 case 'SOLD': 2166 formattedStatus = 'Sold'; 2167 break; 2168 case 'FOR_SALE': 2169 formattedStatus = 'For Sale'; 2170 break; 2171 case 'COMING_SOON': 2172 formattedStatus = 'Coming Soon'; 2173 break; 2174 case 'INACTIVE': 2175 formattedStatus = 'Inactive'; 2176 break; 2177 case 'POCKET_LISTING': 2178 formattedStatus = 'Pocket Listing'; 2179 break; 2180 case 'PENDING': 2181 formattedStatus = 'Pending'; 2182 break; 2183 case 'UNDER_CONTRACT': 2184 formattedStatus = 'Under Contract'; 2185 break; 2186 case 'ACTIVE_UNDER_CONTRACT': 2187 formattedStatus = 'Active Under Contract'; 2188 break; 2189 default: 2190 formattedStatus = status; 2191 } 2192 // `typeof date === 'string'` matters: when called as 2193 // `{{formatPropertyStatus status}}` Handlebars passes its options object 2194 // as `showSoldDate`/`date`, and this guard keeps the legacy output. 2195 if ((formattedStatus === 'Sold' || formattedStatus === 'Leased') && showSoldDate && typeof date === 'string' && date) formattedStatus += ` on ${date}`; 2196 return formattedStatus; 2197 }, 2198 formatGrade (gradeValue) { 2199 switch(gradeValue){ 2200 case 'UNDERGRADUATE_COURSES': 2201 return 'Undergrad'; 2202 case 'PRE_KINDERGARTEN': 2203 return 'PreK'; 2204 case 'KINDERGARTEN': 2205 return 'K'; 2206 case 'FIRST_GRADE': 2207 return '1st'; 2208 case 'SECOND_GRADE': 2209 return '2nd'; 2210 case 'THIRD_GRADE': 2211 return '3rd'; 2212 case 'FOURTH_GRADE': 2213 return '4th'; 2214 case 'FIFTH_GRADE': 2215 return '5th'; 2216 case 'SIXTH_GRADE': 2217 return '6th'; 2218 case 'SEVENTH_GRADE': 2219 return '7th'; 2220 case 'EIGHTH_GRADE': 2221 return '8th'; 2222 case 'NINTH_GRADE': 2223 return '9th'; 2224 case 'TENTH_GRADE': 2225 return '10th'; 2226 case 'ELEVENTH_GRADE': 2227 return '11th'; 2228 case 'TWELFTH_GRADE': 2229 return '12th'; 2230 default: 2231 return ''; 2232 } 2233 }, 2234 encodeURI (uri) { 2235 return encodeURI(uri); 2236 }, 2237 encodeURIComponent (txt) { 2238 return encodeURIComponent(txt); 2239 }, 2240 hbFormatDate (date, type) { 2241 if (!date) return ''; 2242 const newDate = date === 'current' ? new Date() : new Date(date); 2243 if (Number.isNaN(newDate.getTime())) return ''; 2244 const months = [ 2245 'January', 2246 'February', 2247 'March', 2248 'April', 2249 'May', 2250 'June', 2251 'July', 2252 'August', 2253 'September', 2254 'October',
2255 'November', 2256 'December' 2257 ]; 2258 if (type === 'YYYY' || type === 'year') return `${newDate.getFullYear()}`; 2259 if (type === 'MM/DD/YY') { 2260 let month = newDate.getMonth() + 1; 2261 if (month < 10) month = `0${month.toString()}`; 2262 return `${month}/${newDate.getDate()}/${newDate.getFullYear().toString().substr(-2)}`; 2263 } 2264 return `${months[newDate.getMonth()]} ${newDate.getDate()}, ${newDate.getFullYear()}`; 2265 }, 2266 openHousesFormat (startDate, endDate, timeZone) { 2267 if (!startDate || !endDate) return ''; 2268 /** 2269 * return formatted time. 2270 * @param {date} date 2271 * @return {time} time. 2272 */ const getTime = (date)=>{ 2273 let hours = date.getHours(); 2274 if (hours > 12) hours -= 12; 2275 const prefix = '0'; 2276 let time = `${hours}:${date.getMinutes() > 9 ? date.getMinutes() : prefix + date.getMinutes()}`; 2277 if (date.getHours() > 11) time += 'PM'; 2278 else time += 'AM'; 2279 return time; 2280 }; 2281 let tz = ''; 2282 let startFDate = startDate; 2283 let endFDate = endDate; 2284 if (timeZone) { 2285 startFDate = new Date(startDate).toLocaleString('en-US', { 2286 timeZone: timeZone 2287 }); 2288 endFDate = new Date(endDate).toLocaleString('en-US', { 2289 timeZone: timeZone 2290 }); 2291 [, , , tz] = new Date(endDate).toLocaleString('en-US', { 2292 timeZone: timeZone, 2293 timeZoneName: 'short' 2294 }).split(' '); 2295 } 2296 const date = new Date(startFDate); 2297 const openHouseDate = date.toLocaleDateString('en-US'); 2298 const time1 = getTime(date); 2299 const date2 = new Date(endFDate); 2300 const time2 = getTime(date2); 2301 return `${openHouseDate} ${time1} - ${time2} ${tz}`; 2302 }, 2303 math (lvalue, operator, rvalue) { 2304 // eslint-disable-next-line 2305 lvalue = parseFloat(lvalue); 2306 // eslint-disable-next-line 2307 rvalue = parseFloat(rvalue); 2308 return ({ 2309 '+': lvalue + rvalue, 2310 '-': lvalue - rvalue, 2311 '*': lvalue * rvalue, 2312 '/': lvalue / rvalue, 2313 '%': lvalue % rvalue 2314 })[operator]; 2315 }, 2316 findByKey (values, key, value, options) { 2317 const result = (values || []).find((entry)=>entry[key] === value); 2318 return options.fn(result); 2319 }, 2320 capitalize (string, split, options) { 2321 let capitalized; 2322 if (typeof string === 'string' && string.length) capitalized = string.toLowerCase().split(split).map((w)=>w.charAt(0).toUpperCase() + w.slice(1)).join(' ').toString(); 2323 return capitalized; 2324 }, 2325 and (...arg) { 2326 return Array.prototype.slice.call(arg).every(Boolean); 2327 }, 2328 or (...arg) { 2329 return Array.prototype.slice.call(arg, 0, -1).some(Boolean); 2330 }, 2331 not (arg) { 2332 return !arg; 2333 }, 2334 orderByIds (items, orderedIds, options) { 2335 let ret = ''; 2336 let data = null; 2337 if (options.data) data = HandlebarsInstance.createFrame(options.data); 2338 // When passed orderedIds, filter out empty items including empty strings and 2339 // only show items (and in that order) in a loop 2340 // If no orderedIds provided, show all items 2341 const filteredIds = (orderedIds || []).filter(Boolean); 2342 if (filteredIds && filteredIds.length) { 2343 let skippedItems = 0; 2344 ret = filteredIds.reduce((res, id, idx)=>{ 2345 const item = (items || []).find((item)=>item.id === id); 2346 // ID doesn't exist so ditch it 2347 if (!item) { 2348 skippedItems += 1; 2349 return res; 2350 } 2351 if (data) data.index = idx - skippedItems; 2352 return res + options.fn(item, { 2353 data: data 2354 }); 2355 }, ''); 2356 } else for(let i = 0, j = (items || []).length; i < j; i++){ 2357 if (data) data.index = i; 2358 ret += options.fn(items[i], { 2359 data: data 2360 }); 2361 } 2362 return ret; 2363 }, 2364 ifIn (elem, list, options) { 2365 if (list && list.indexOf(elem) > -1) return options.fn(this); 2366 return options.inverse(this); 2367 }, 2368 partial (name, options) { 2369 // Register a partial and output the content in a script for clientside rendering 2370 HandlebarsInstance.registerPartial(name, HandlebarsInstance.compile(options.fn())); 2371 // eslint-disable-next-line 2372 const script = `
2372<script type="text/x-handlebars-template" class="${name} pagination-script">${options.fn()}</${'script'}>`; 2373 return script; 2374 }, 2375 numCompare (l, r, operator, options) { 2376 const operators = { 2377 '==': function(l, r) { 2378 // eslint-disable-next-line 2379 return l == r; 2380 }, 2381 '===': function(l, r) { 2382 return l === r; 2383 }, 2384 '!=': function(l, r) { 2385 // eslint-disable-next-line 2386 return l != r; 2387 }, 2388 '<': function(l, r) { 2389 return l < r; 2390 }, 2391 '>': function(l, r) { 2392 return l > r; 2393 }, 2394 '<=': function(l, r) { 2395 return l <= r; 2396 }, 2397 '>=': function(l, r) { 2398 return l >= r; 2399 } 2400 }; 2401 const result = operators[operator](l, r); 2402 if (result) return options.fn ? options.fn(this) : true; 2403 return options.fn ? options.inverse(this) : false; 2404 }, 2405 lpFormatNumber: lpFormatNumber, 2406 arrayToHtmlList (arr, options) { 2407 // eslint-disable-next-line 2408 return arr.reduce((acc, current)=>{ 2409 try { 2410 if (typeof current === 'object' || current === null) return acc; 2411 // eslint-disable-next-line 2412 return acc += `<li>${current}</li>`; 2413 } catch (e) { 2414 /* empty */ } 2415 }, ''); 2416 }, 2417 join (array, separator, options) { 2418 if (!array || !array.join) return ''; 2419 return array.join(typeof separator === 'string' ? separator : ', '); 2420 }, 2421 dedupe (items, key, options) { 2422 let data = null; 2423 let skippedItems = 0; 2424 const list = new Set(); 2425 if (options.data) data = HandlebarsInstance.createFrame(options.data); 2426 return (items || []).reduce((res, item, idx)=>{ 2427 const val = key ? item[key] : item; 2428 // Item is unique if we're passed a key that is not defined 2429 const isUnique = !list.has(val) || key && val === undefined; 2430 if (isUnique) list.add(val); 2431 else { 2432 skippedItems += 1; 2433 return res; 2434 } 2435 if (data) data.index = idx - skippedItems; 2436 return res + options.fn(item, { 2437 data: data 2438 }); 2439 }, ''); 2440 }, 2441 JSONstringify (obj, options) { 2442 return JSON.stringify(obj); 2443 }, 2444 // eslint-disable-next-line sonarjs/cognitive-complexity 2445 formatPropertyPrice (...args) { 2446 const options = args[args.length - 1]; 2447 const opts = options.hash; 2448 const property = args.length > 1 ? args[0] : opts.property; 2449 const precision = opts.precision || 0; 2450 if (args.length > 2) // eslint-disable-next-line 2451 opts.mode = args[1]; 2452 if (!opts.mode) opts.mode = 'short'; 2453 let separator = args.length > 3 ? args[2] : opts.separator; 2454 if (typeof separator !== 'string') separator = ' | '; 2455 if (property.isPasswordProtected) return 'Exclusive Listing'; 2456 let priceUponRequestText = args.length > 4 ? args[3] : opts.priceUponRequestText; 2457 if (typeof priceUponRequestText !== 'string') priceUponRequestText = 'Price Upon Request'; 2458 if (property.priceUponRequest) return priceUponRequestText; 2459 let price = property.salesPrice; 2460 let reduced = null; 2461 // eslint-disable-next-line sonarjs/no-collapsible-if 2462 if (property.status !== 'SOLD' && property.status !== 'Leased') { 2463 if (property.reducedPrice) { 2464 if (!opts.reducedClass) price = property.reducedPrice; 2465 else reduced = property.reducedPrice; 2466 } 2467 } 2468 let result = ''; 2469 if (reduced) result += `<${opts.reducedTag || 'span'} class="${opts.reducedClass}">${lpFormatNumber(reduced, property.currency, precision)}</${opts.reducedTag || 'span'}>`; 2470 if (price) { 2471 price = lpFormatNumber(price, property.currency, precision); 2472 if (opts.priceTag || opts.priceClass) result += `<${opts.priceTag || 'span'}${opts.priceClass ? ` class="${opts.priceClass}"` : ''}>${price}</${opts.priceTag || 'span'}>`; 2473 else result += price; 2474 } 2475 const leasePrice = property.leasePrice && lpFormatNumber(property.leasePrice, property.currency, precision); 2476 if (leasePrice) { 2477 if (result) result += separator; 2478 const leaseText = `${leasePrice}${formatFrequencyExt(property.leasePeriod, opts)}`; 2479 if (opts.leaseTag || opts.leaseClass) result += `<${opts.leaseTag || 'span'}${opts.leaseClass ? ` class="${opts.leaseClass}"` : ''}>${leaseText}</${opts.leaseTag || 'span'}>`; 2480 else result += leaseText; 2481 } 2482 return new HandlebarsInstance.SafeString(result); 2483 }, 2484 // eslint-disable-next-line sonarjs/cognitive-complexity 2485 mediaImage (mediaSource, options = {}) { 2486 if (!mediaSource) return; 2487 const { lazy: lazy, ...attributes } = options.hash; 2488 let src = ''; 2489 let srcset = ''; 2490 if (typeof mediaSource === 'string') src = mediaSource; 2491 else { 2492 const srcMap = {}; 2493 const { height: height, width: width, altTagText: altTagText } = mediaSource; 2494 MEDIA_SIZES.forEach((src)=>{ 2495 if (mediaSource[src.field] && !srcMap[mediaSource[src.field]]) srcMap[mediaSource[src.field]] = src.w; 2496 }); 2497 const srcs = Object.keys(srcMap); 2498 if (!srcs.length) return; 2499 if (altTagText) attributes.alt = altTagText; 2500 if (height && width) { 2501 if (height >= width) { 2502 if (attributes.class) attributes.class += ' portrait'; 2503 else attributes.class = 'portrait'; 2504 } 2505 if ('setDims' in options.hash) { 2506 attributes.width = width; 2507 attributes.height = height; 2508 } 2509 } 2510 if (srcs.length > 1 && width && width < srcMap[srcs[srcs.length - 1]]) { 2511 const maxSrc = srcs.findIndex((src)=>
2511srcMap[src] >= width); 2512 srcs.splice(maxSrc + 1); 2513 srcMap[srcs[srcs.length - 1]] = width; 2514 } 2515 if (srcs.length > 1) { 2516 srcset = srcs.map((s)=>`${s} ${srcMap[s]}w`).join(', '); 2517 srcs.sort((a, b)=>Math.abs(srcMap[a] - MEDIA_SIZE_FOR_SRC) - Math.abs(srcMap[b] - MEDIA_SIZE_FOR_SRC)); 2518 } 2519 // eslint-disable-next-line 2520 src = srcs[0]; 2521 } 2522 delete attributes.setDims; 2523 if (!srcset) delete attributes.sizes; 2524 if (!('alt' in attributes)) attributes.alt = ''; 2525 if (!('loading' in attributes) && (options?.data?.root?.sectionIndex > 1 || lazy)) attributes.loading = 'lazy'; 2526 const attrs = Object.keys(attributes).map((k)=>`${HandlebarsInstance.escapeExpression(k)}="${HandlebarsInstance.escapeExpression(attributes[k])}"`).join(' '); 2527 return new HandlebarsInstance.SafeString(// eslint-disable-next-line sonarjs/no-nested-template-literals 2528 `<img src="${src}"${srcset ? ` srcset="${srcset}"` : ''}${attrs ? ` ${attrs}` : ''}>`); 2529 }, 2530 bgImage (mediaSource) { 2531 if (!mediaSource) return ''; 2532 if (typeof mediaSource === 'string') return mediaSource; 2533 return mediaSource.largeUrl || mediaSource.url || ''; 2534 }, 2535 concat (...rest) { 2536 let outStr = ''; 2537 // rest params are Array instances while arguments object is not 2538 for(const arg in rest)if (rest[arg] !== null && rest[arg] !== undefined && typeof rest[arg] !== 'object') outStr += rest[arg]; 2539 return outStr; 2540 }, 2541 setVideoExtension (uri, format) { 2542 if (!uri || !format) return ''; 2543 const decodedUri = new URL(uri); 2544 if (decodedUri.host !== 'res.cloudinary.com') { 2545 // Not a cloudinary uri, do not rewrite 2546 if (decodedUri.pathname.endsWith(`.${format}`)) return uri; 2547 return ''; 2548 } 2549 // Rewrite the cloudinary uri 2550 const lastPeriodIdx = decodedUri.pathname.lastIndexOf('.'); 2551 let pathWithoutFormat = decodedUri.pathname; 2552 if (lastPeriodIdx !== -1) pathWithoutFormat = decodedUri.pathname.substr(0, lastPeriodIdx); 2553 decodedUri.pathname = `${pathWithoutFormat}.${format}`; 2554 return decodedUri.toString(); 2555 }, 2556 addVideoSources (uri) { 2557 if (!uri) return ''; 2558 const VIDEO_VARIANTS = [ 2559 { 2560 format: 'webm', 2561 codec: 'vp9', 2562 htmlCodecs: ';codecs=vp9' 2563 }, 2564 { 2565 format: 'mp4', 2566 codec: 'h265', 2567 htmlCodecs: ';codecs=hvc1' 2568 }, 2569 { 2570 format: 'mp4', 2571 codec: 'h264', 2572 htmlCodecs: '' 2573 } 2574 ]; 2575 const VIDEO_EXT_REGEXP = /\.(?:3gp|mpeg|mp4|m4v|m4p|ogv|ogg|mov|webm)$/; 2576 if (!uri.includes('res.cloudinary.com')) // keep existing or no extension for non Cloudinary urls 2577 return `<source data-src="${uri}">`; 2578 // Cap delivery at 1080p (1920px wide) while preserving aspect ratio. Width-only: 2579 // c_limit never upscales, so portrait sources are left at their native size. 2580 // No q_auto: quality is left to Cloudinary's default rather than forced down. 2581 // Note the regex below consumes any q_auto in the source URL, so it is dropped 2582 // rather than carried through. 2583 const CONSTRAINED_DELIVERY = 'w_1920,c_limit'; 2584 const uriNoExt = uri.replace(VIDEO_EXT_REGEXP, ''); 2585 const sources = VIDEO_VARIANTS.map((type)=>{ 2586 const cloudinaryFormatUrl = `${uriNoExt.replace(/f_auto(?::video)?(?:,q_auto(?::\w+)?)?/, `f_${type.format},vc_${type.codec},${CONSTRAINED_DELIVERY}`)}.${type.format}`; 2587 return `<source data-src="${cloudinaryFormatUrl}" type="video/${type.format}${type.htmlCodecs}">`; 2588 }); 2589 return sources.join('\n'); 2590 }, 2591 cloudinaryVideoFirstFrame: $0ce6edea77f7b81a$exports.getVideoFirstFrame, 2592 initials (text, options) { 2593 if (!text) return ''; 2594 if (options?.hash?.endChar) // eslint-disable-next-line 2595 text = text.split(options.hash.endChar)[0]; 2596 // eslint-disable-next-line 2597 const maxChars = +options?.hash?.maxChars || 2; // we do not allow this to be 0 2598 let result = text.match(/\b\w/g)?.slice(0, maxChars); 2599 if (!result) return ''; 2600 if (options?.hash?.addText) result = result.map((item)=>item + options.hash.addText); 2601 return result.join(options?.hash?.joinWith || '').toUpperCase(); 2602 }, 2603 normalizePhone (phone) { 2604 if (!phone) return ''; 2605 const str = String(phone).trim(); 2606 return (str.startsWith('+') ? '+' : '') + str.replace(/\D/g, ''); 2607 } 2608 }; 2609} 2610$53de3c160faf5a81$exports = $53de3c160faf5a81$var$handlebarsHelpersFactory; 2611 2612 2613/* global window, document */ // eslint-disable-next-line import/prefer-default-export 2614class $dc851cf3939b6ef5$export$ff0712c26d5458f7 { 2615 /** 2616 * Initialize Modal Controller 2617 * @param {Object} options 2618 */ constructor(options){ 2619 this.isBuilder = false;
2620 this.companyId = null; 2621 this.websiteId = null; 2622 this.pageId = null; 2623 this.shareId = null; 2624 this.pageMeta = null; 2625 this.websiteApiGatewayUrl = null; 2626 this.buyerSellerServiceUrl = null; 2627 this.leadIngesterUrl = null; 2628 this.divolteServiceUrl = null; 2629 this.eventBus = null; 2630 this.templateId = null; 2631 this.myhomeDashboardEnabled = false; 2632 this.mlsEnabled = false; 2633 this.hstValuationRedirectEnabled = false; 2634 this.googleMapsAPIKey = null; 2635 } 2636 /** 2637 * Controllers must implement a "destroy" method to be called 2638 * after page navigation to remove event handlers / cleanup 2639 */ destroy() {} 2640 /** 2641 * Set page and supporting data 2642 * @param {string} pageId 2643 * @param {Object} options 2644 */ setPage(pageId, options = {}) { 2645 this.pageId = pageId; 2646 this.shareId = options.shareId || null; 2647 this.pageMeta = { 2648 sourceResource: options.sourceResource, 2649 pageElementId: options.pageElementId, 2650 pageQueryVariables: options.pageQueryVariables || {} 2651 }; 2652 } 2653 /** 2654 * Sets if we are in pagebuilder context or not 2655 * @param {boolean} isBuilder 2656 */ setIsBuilder(isBuilder) { 2657 this.isBuilder = isBuilder; 2658 } 2659 /** 2660 * Set Company and supporting data 2661 * @param {string} companyId 2662 * @param {Object} options 2663 */ setCompany(companyId, options = {}) { 2664 this.companyId = companyId; 2665 } 2666 /** 2667 * Set website and supporting data 2668 * @param {string} websiteId 2669 * @param {Object} options 2670 */ setWebsite(websiteId, options = {}) { 2671 this.websiteId = websiteId; 2672 if (options.myhomeDashboardEnabled) this.myhomeDashboardEnabled = options.myhomeDashboardEnabled; 2673 if (options.mlsEnabled) this.mlsEnabled = options.mlsEnabled; 2674 if (options.hstValuationRedirectEnabled) this.hstValuationRedirectEnabled = options.hstValuationRedirectEnabled; 2675 } 2676 /** 2677 * Set websiteApiGatewayUrl 2678 * @param {string} websiteApiGatewayUrl 2679 * @param {Object} options 2680 */ setWebsiteApiGatewayUrl(websiteApiGatewayUrl, options = {}) { 2681 this.websiteApiGatewayUrl = websiteApiGatewayUrl; 2682 } 2683 /** 2684 * Set buyerSellerServiceUrl 2685 * @param {string} buyerSellerServiceUrl 2686 * @param {Object} options 2687 */ setBuyerSellerServiceUrl(buyerSellerServiceUrl, options = {}) { 2688 this.buyerSellerServiceUrl = buyerSellerServiceUrl; 2689 } 2690 /** 2691 * Set divolteServiceUrl and supporting data 2692 * @param {string} divolteServiceUrl 2693 * @param {Object} options 2694 */ setDivolteServiceUrl(divolteServiceUrl, options = {}) { 2695 this.divolteServiceUrl = divolteServiceUrl; 2696 } 2697 /** 2698 * Set eventBusUrl 2699 * @param {string} eventBusUrl 2700 * @param {Object} options 2701 */ setEventBusUrl(eventBusUrl, options = {}) { 2702 this.eventBusUrl = eventBusUrl; 2703 } 2704 /** 2705 * Set templateId and supporting data 2706 * @param {string} templateId 2707 * @param {Object} options 2708 */ setTemplateId(templateId, options = {}) { 2709 this.templateId = templateId; 2710 } 2711 /** 2712 * Set leadIngesterUrl 2713 * @param {string} leadIngesterUrl 2714 * @param {Object} options 2715 */ setLeadIngesterUrl(leadIngesterUrl, options = {}) { 2716 this.leadIngesterUrl = leadIngesterUrl; 2717 } 2718 /** 2719 * Set googleMapsAPIKey 2720 * @param {string} googleMapsAPIKey 2721 * @param {Object} options 2722 */ setGoogleMapsAPIKey(googleMapsAPIKey, options = {}) { 2723 this.googleMapsAPIKey = googleMapsAPIKey; 2724 } 2725 /** 2726 * Set eventBus 2727 * @param {Object} options 2728 */ setEventBus({ enabled: enabled, apiKey: apiKey, url: url }) { 2729 this.eventBus = { 2730 enabled: enabled, 2731 apiKey: apiKey, 2732 url: url 2733 }; 2734 } 2735} 2736 2737 2738/* eslint-disable import/prefer-default-export */ // eslint-disable-next-line import/prefer-default-export 2739class $7c391e2f7931d60d$export$3b5bd9381a52554c { 2740 /** 2741 * Initialize Base Controller at build time 2742 * @param { ConfigController } config 2743 * @param {Window} window 2744 * @param {Document} document 2745 */ constructor(config, window, document){ 2746 // TODO: Listen for events from configController to update configuration 2747 this.config = config; 2748 this.window = window; 2749 this.document = document; 2750 if (!this.destroy) // With turbolinks, we want to ensure that on each page load we have the latest version of the sdk 2751 // Controllers are deconstructed and replaced (on the window object) per request 2752 // Controllers must use the destory method to remove event handlers to prevent undefined behavior 2753 throw new Error('Controllers must implement a deconstructor'); 2754 } 2755} 2756 2757 2758// eslint-disable-next-line import/prefer-default-export 2759const $a3e97e89bbc2a366$export$f037eeffb5561205 = 2000; 2760 2761 2762class $0ead03fdb95fa9ce$export$d507f25e63650365 extends (0, $7c391e2f7931d60d$export$3b5bd9381a52554c) { 2763 /** 2764 * Initialize Modal Controller at build time 2765 * @param { ConfigController } config 2766 * @param {Window} window 2767 * @param {Document} document 2768 */ constructor(config, window, document){ 2769 super(config, window, document); 2770 this.setLoginState = this.setLoginState.bind(this); 2771 this.pageId = null; 2772 this.modals = []; 2773 this.exitModalTriggered = {}; 2774 this.exitModalEventListeners = []; 2775 } 2776 /** 2777 * Initialize Modal Controller at run time 2778 * Attach handlers and events to DOM 2779 * @param {Object} options 2780 */ initialize(options) { 2781 try { 2782 const { config: config, document: document } = this; 2783 this.pageId = config.pageId; 2784 this.modals = document.querySelectorAll('body > #modals > .modal');
2785 options.modals.forEach((m)=>{ 2786 this._attachModalHandler(m); 2787 }); 2788 } catch (e) { 2789 // eslint-disable-next-line no-console 2790 console.log(e); 2791 } 2792 } 2793 /** 2794 * Controllers must implement a "destroy" method to be called 2795 * after page navigation to remove event handlers / cleanup 2796 */ destroy() { 2797 const { document: document } = this; 2798 this.exitModalEventListeners.forEach((listener)=>{ 2799 document.removeEventListener('mouseout', listener); 2800 }); 2801 this.modals = []; 2802 this.exitModalTriggered = {}; 2803 this.exitModalEventListeners = []; 2804 } 2805 /** 2806 * Set modal to local storage with expiration 2807 * @param {string} modalId 2808 */ setModalWithExpiry(modalId) { 2809 const { window: window } = this; 2810 const TTL = 86400000; // 24 hours 2811 const now = new Date(); 2812 const expiry = now.getTime() + TTL; 2813 const key = `modal-controller-${modalId}`; 2814 try { 2815 window.localStorage.setItem(key, JSON.stringify(expiry)); 2816 } catch (err) { 2817 // catch error when setting item to storage on iOS private browser 2818 } 2819 } 2820 /** 2821 * Save success login modal to local storage 2822 */ setLoginState() { 2823 const { window: window } = this; 2824 try { 2825 window.localStorage.setItem('isSignedUp', 'true'); 2826 } catch (err) { 2827 // catch error when setting item to storage on iOS private browser 2828 } 2829 } 2830 /** 2831 * Get modal and check expiration 2832 * @param {string} modalId 2833 * @return {boolean} 2834 */ getModalWithExpiry(modalId) { 2835 const { window: window } = this; 2836 const storage = window.localStorage; 2837 const expirationTime = storage.getItem(`modal-controller-${modalId}`); 2838 if (!expirationTime) return false; 2839 const expiry = JSON.parse(expirationTime); 2840 const now = new Date(); 2841 if (now.getTime() > expiry) { 2842 storage.removeItem(`modal-controller-${modalId}`); 2843 return false; 2844 } 2845 return true; 2846 } 2847 /** 2848 * Make a modal visible 2849 * @param {string} modalId 2850 * @param {Object} options 2851 */ show(modalId, options = {}) { 2852 // TODO: migrate logic from page.handlebars 2853 const { window: window } = this; 2854 const modalViewed = this.getModalWithExpiry(modalId); 2855 // Only show a modal if it has not been seen 2856 // Does not affect "show modal on button click" 2857 if (modalId && !modalViewed || options.forced === true) { 2858 window.lpUI.showModal(`modal-${modalId}`, options); 2859 this.setModalWithExpiry(modalId); 2860 } 2861 } 2862 /** 2863 * Attach events based on Modal type and page settings 2864 * @param {Modal} modal 2865 */ _attachModalHandler(modal) { 2866 const { pageId: pageId } = this; 2867 const { trigger: trigger, pageSpecific: pageSpecific } = modal; 2868 if (pageSpecific) { 2869 const pageIds = modal.pages.map((p)=>p.pageId); 2870 if (!pageIds.includes(pageId)) // Modal does not belong to this page 2871 return; 2872 } 2873 if (trigger === 'EXIT') this._exitModalHandler(modal); 2874 else if (trigger === 'TIMER') this._timerModalHandler(modal); 2875 else if (trigger === 'FORCED') this._forcedModalHandler(modal); 2876 } 2877 /** 2878 * Attach events for exit Modals 2879 * @param {Modal} modal 2880 */ _exitModalHandler(modal) { 2881 // eslint-disable-next-line 2882 const that = this; 2883 const { document: document, exitModalTriggered: exitModalTriggered } = this; 2884 const { modalId: modalId } = modal; 2885 /** 2886 * Handle the mouseout dom event 2887 * @param {Event} event 2888 */ function triggerExitModal(event) { 2889 if (exitModalTriggered[modalId]) return; 2890 if (!event.toElement && !event.relatedTarget) { 2891 that.show(modalId); 2892 exitModalTriggered[modalId] = true; 2893 } 2894 } 2895 document.addEventListener('mouseout', triggerExitModal); 2896 this.exitModalEventListeners.push(triggerExitModal); 2897 } 2898 /** 2899 * Attach events for timer Modals
2900 * @param {Modal} modal 2901 */ _timerModalHandler(modal) { 2902 const { modalId: modalId, timerDelay: timerDelay } = modal; 2903 setTimeout(()=>{ 2904 this.show(modalId); 2905 }, timerDelay); 2906 } 2907 /** 2908 * Attach events for Forced Modals 2909 * @param {Modal} modal 2910 */ _forcedModalHandler(modal) { 2911 const { modalId: modalId } = modal; 2912 const { window: window, document: document } = this; 2913 const isLoggedIn = window.localStorage.getItem('isSignedUp') === 'true'; 2914 if (!isLoggedIn) { 2915 // watch for event triggering success login 2916 const forcedModal = document.querySelector(`#modal-${modalId}`); 2917 const contactForm = forcedModal?.querySelector('form'); 2918 const MODAL_CLOSE_DELAY = contactForm?.dataset.delay || (0, $a3e97e89bbc2a366$export$f037eeffb5561205); 2919 // form submit autoclose is handled by contactForms controller 2920 contactForm.addEventListener('formsubmitted', ()=>{ 2921 this.setLoginState(); 2922 }); 2923 window.addEventListener('oauthSuccess', ()=>{ 2924 this.setLoginState(); 2925 contactForm.classList.add('success'); 2926 setTimeout(()=>{ 2927 window.lpUI.hideModal(`modal-${modalId}`); 2928 contactForm.reset(); 2929 contactForm.classList.remove('success'); 2930 }, MODAL_CLOSE_DELAY); 2931 }); 2932 this.show(modalId, { 2933 forced: true 2934 }); 2935 } 2936 } 2937} 2938 2939 2940/* eslint-disable import/prefer-default-export */ /* global window */ /* eslint-disable import/prefer-default-export */ /** 2941 * Handle navigation push 2942 * @param {string} href 2943 * @param {boolean} _blank 2944 */ const $fc0a058ae42af628$export$95e8d77caafa3e06 = (href, _blank = false)=>{ 2945 if (!href) return; 2946 if (_blank) { 2947 window.open(href); 2948 return; 2949 } 2950 window.location.assign(href); 2951}; 2952const $fc0a058ae42af628$export$267a1435fe18b8d4 = (obj, path, defValue)=>{ 2953 if (!path) return undefined; 2954 const pathArray = Array.isArray(path) ? path : path.match(/([^[.\]])+/g); 2955 const result = pathArray.reduce((prevObj, key)=>prevObj && prevObj[key], obj); 2956 return result === undefined ? defValue : result; 2957}; 2958 2959 2960 2961class $d48f9f944810f613$export$ca3218b7861b9cb0 extends (0, $7c391e2f7931d60d$export$3b5bd9381a52554c) { 2962 /** 2963 * Initialize Button Controller at build time 2964 * @param { ConfigController } config 2965 * @param {Window} window 2966 * @param {Document} document 2967 */ constructor(config, window, document){ 2968 super(config, window, document); 2969 this._boundClickHandler = this._buttonClickHandler.bind(this); 2970 } 2971 /** 2972 * Initialize Button Controller at run time 2973 * Attach handlers and events to DOM 2974 * @param {Object} options 2975 */ initialize(options = {}) { 2976 try { 2977 const { document: document } = this; 2978 document.body.removeEventListener('click', this._boundClickHandler); 2979 document.body.addEventListener('click', this._boundClickHandler); 2980 } catch (e) { 2981 // eslint-disable-next-line no-console 2982 console.error(e); 2983 } 2984 } 2985 /** 2986 * Controllers must implement a "destroy" method to be called 2987 * after page navigation to remove event handlers / cleanup 2988 */ destroy() { 2989 const { document: document } = this; 2990 document.body.removeEventListener('click', this._boundClickHandler); 2991 } 2992 /** 2993 * Buttons click handler 2994 * @param {event} e 2995 */ _buttonClickHandler(e) { 2996 this._handleButtonClick(e).catch((e)=>console.error(e)); 2997 } 2998 /** 2999 * Handle button click 3000 * We need special function to have easy way to detach listener 3001 * @param {Event} e 3002 */ async _handleButtonClick(e) { 3003 const { window: window, document: document } = this; 3004 const button = e.target.closest('button, a'); 3005 if (!button) return; 3006 const type = button.dataset.type || ''; 3007 const href = button.href || button.dataset.href || ''; 3008 const target = button.target || ''; 3009 const { submit: submit } = button.dataset; 3010 if (submit) { 3011 try { 3012 await this._handleSubmit(button); 3013 } catch (err) { 3014 // eslint-disable-next-line no-console 3015 console.error(err); 3016 } 3017 (0, $fc0a058ae42af628$export$95e8d77caafa3e06)(href, target === '_blank'); 3018 } 3019 this._trackConversion(button); 3020 if (type === 'VIDEO') { 3021 this._handleVideoButton(button); 3022 return; 3023 } 3024 if (type === 'CONTACT_US') { 3025 this._handleContactUsButton(button); 3026 return; 3027 } 3028 if (type === 'SUBSCRIBE') { 3029 // TODO: Extend for an overwritten subscribe 3030 window.lpUI.showModal('modal-global-subscribe'); 3031 return; 3032 } 3033 if (type === 'MY_ACCOUNT') { 3034 // TODO: Extend for an overwritten subscribe 3035 window.lpUI.showModal('modal-global-my-account'); 3036 return; 3037 } 3038 if (type === 'SHARE_POPUP') { 3039 // TODO: Extend for an overwritten subscribe 3040 const sharePopup = button.querySelector('.share-popup'); 3041 if (sharePopup) sharePopup.style.display = 'flex'; 3042 e.stopPropagation(); // this may not work in current 'delegate' mode 3043 return; 3044 } 3045 // Custom modals 3046 if (type.substring(0, 6) === 'MODAL_') { 3047 const modalId = type.substring(6); 3048 window.lpUI.showModal(`modal-${modalId}`); 3049 return; 3050 } 3051 // MAILTO / TEL Event 3052 if (href?.startsWith('mailto:')) { 3053 const email = href.substring(7); 3054 // TODO: Replace with SDK method 3055 document.sendGoogleAnalyticsEvent({ 3056 hitType: 'event', 3057 eventCategory: 'button', 3058 eventAction: 'click', 3059 eventLabel: 'Email', 3060 eventValue: email 3061 }); 3062 } else if (href?.startsWith('tel:')) { 3063 const phoneNumber = href.substring(4); 3064 // TODO: Replace with SDK method 3065 document.sendGoogleAnalyticsEvent({ 3066 hitType: 'event', 3067 eventCategory: 'button', 3068 eventAction: 'click', 3069 eventLabel: 'Phone', 3070 eventValue: phoneNumber 3071 }); 3072 } 3073 } 3074 /** 3075 * Handle submit 3076 * @param {HTMLButtonElement|HTMLAnchorElement} button 3077 */ async _handleSubmit(button) { 3078 const form = button.closest('form'); 3079 const { window: window } = this; 3080 if (form) return await window.luxuryPresence.contactForms.submitForm(form); 3081 return null; 3082 } 3083 _getVimeoSrc(link) { 3084 if (!link || !link.includes('vimeo.com') || link.length > 2000) return null; 3085 const matchResult = link.match(/(?:(?:www\.)?vimeo\.com\/(?:showcase\/\d+(?:\/video\/|\?video[=/])|channels\/(?:\w+\/)?|groups\/(?:[^/]*)\/videos\/|)|player\.vimeo\.com\/video\/)(\d+)(?:(?:\?.*&*h=|\/)+([\d,a-f]+))?/); 3086 if (!matchResult) return null; 3087 const [, vimeoId, vimeoHashParameter] = matchResult; 3088 let src = `https://player.vimeo.com/video/${vimeoId}?`; 3089 if (vimeoHashParameter) src += `h=${vimeoHashParameter}&`; 3090 src += 'autoplay=1'; 3091 return src; 3092 } 3093 /** 3094 * Handle type video click 3095 * @param {HTMLElement} button 3096 */ // eslint-disable-next-line sonarjs/cognitive-complexity 3097 _handleVideoButton(button) { 3098 const { document: document } = this; 3099 const section = button.closest('section'); 3100 // Embed youtube/vimeo video url 3101 let { src: src } = button.dataset; 3102 let youtubeId = ''; 3103 if (src && (src.includes('youtube.com') || src.includes('youtu.be')) && !src.includes('youtube.com/embed')) { 3104 // test link: https://regexr.com/3anm9 3105 const youtubeRegex = /(?:youtube\.com\/\S*(?:(?:\/e(?:mbed))?\/|watch\?(?:\S*?&?v=)
3105)|youtu.be\/)([a-zA-Z0-9_-]{6,11})/; 3106 const youtubeResult = src.match(youtubeRegex); 3107 // eslint-disable-next-line 3108 if (youtubeResult) youtubeId = youtubeResult[1]; 3109 } 3110 const vimeoSrc = this._getVimeoSrc(src); 3111 if (youtubeId) src = `https://youtube.com/embed/${youtubeId}?autoplay=1&controls=0`; 3112 else if (vimeoSrc) src = vimeoSrc; 3113 if (!src || !section) return; 3114 const isVideoFile = src.endsWith('.mp4') || src.endsWith('.webm') || src.includes('cloudinary.com'); 3115 const modalHtml = ` 3116 <div id="modal-${section.id}" class="modal visible"> 3117 <div class="modal-content"> 3118 <div class="lightbox-video"> 3119 <div class="video-player"> 3120 <div class="embed-container"> 3121 ${isVideoFile ? `<video controls autoplay src="${src}"></video>` : `<iframe frameborder="0" allowfullscreen="1" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" title="YouTube video player" width="100%" height="100%" src="${src}"></iframe>`} 3122 </div> 3123 </div> 3124 </div> 3125 </div> 3126 <button class="close lp-close" type="button" aria-label="close modal"><span class="lp-close-icon" aria-hidden="true"></span></button> 3127 </div> 3128 `; 3129 document.body.insertAdjacentHTML('beforeend', modalHtml); 3130 const modal = document.getElementById(`modal-${section.id}`); 3131 const closeModal = modal.querySelector('.close'); 3132 closeModal.addEventListener('click', ()=>{ 3133 modal.remove(); 3134 }); 3135 } 3136 /** 3137 * Handle type contact us click 3138 * @param {HTMLElement} button 3139 */ _handleContactUsButton(button) { 3140 const { window: window, document: document } = this; 3141 // TODO: Extend for an overwritten contact us 3142 document.sendGoogleAnalyticsEvent({ 3143 hitType: 'event', 3144 eventCategory: 'button', 3145 eventAction: 'click', 3146 eventLabel: 'contact' 3147 }); 3148 window.lpUI.showModal('modal-global-contact-us', { 3149 scrollLock: true 3150 }); 3151 } 3152 /** 3153 * Track conversion 3154 * @param {HTMLElement} button 3155 */ _trackConversion(button) { 3156 const { window: window, document: document } = this; 3157 // Conversion Tracking Options 3158 const { gtagConversionId: gtagConversionId, gtagConversionLabel: gtagConversionLabel, fbEventName: fbEventName } = button.dataset; 3159 if (gtagConversionId && gtagConversionLabel) { 3160 if (window.dataLayer && window.dataLayer.length) { 3161 const configured = window.dataLayer.some((layer)=>layer.length && layer[0] === 'config' && layer[1] === gtagConversionId); 3162 if (!configured) document.sendGoogleTagManagerEvent('config', gtagConversionId); 3163 } 3164 const sentTo = `${gtagConversionId}/${gtagConversionLabel}`; 3165 document.sendGoogleTagManagerEvent('event', 'conversion', { 3166 send_to: sentTo 3167 }); 3168 } 3169 if (window.fbq && fbEventName) window.fbq('track', fbEventName, {}); 3170 } 3171} 3172 3173 3174/* eslint-disable import/prefer-default-export */ 3175class $68a2d4aa500c7a05$export$61ec8f2b6291ec31 extends (0, $7c391e2f7931d60d$export$3b5bd9381a52554c) { 3176 /** 3177 * Initialize Validation Controller at build time 3178 * @param { ConfigController } config 3179 * @param {Window} window 3180 * @param {Document} document 3181 */ constructor(config, window, document){ 3182 super(config, window, document); 3183 this.validatedForms = []; 3184 } 3185 /** 3186 * Initialize Contact Form Controller at run time 3187 * Attach handlers and events to DOM 3188 * @param {Object} options 3189 */ initialize(options = {}) { 3190 try { 3191 const { window: window, document: document } = this; 3192 const { selector: selector } = options; 3193 let validatedForms = []; 3194 if (selector) validatedForms = document.querySelectorAll(`${selector} form[data-type="contact-form"]`); 3195 else validatedForms = document.querySelectorAll('form[data-type="contact-form"]'); 3196 const isPhoneValidationLibRequired = !!document.querySelectorAll('input[type="tel"]').length; 3197 // we are loading the phone validation lib from the globally defined list of libs 3198 if (isPhoneValidationLibRequired && !window.libphonenumber && window?.luxuryPresence?.libs) { 3199 const { loadJs: loadJs, jsLibsMap: jsLibsMap } = window.luxuryPresence.libs; 3200 const { libphonenumber: libphonenumber } = jsLibsMap; 3201 loadJs(libphonenumber.jsSrc, this.attachValidation.bind(this, validatedForms)); 3202 }
3202 else this.attachValidation(validatedForms); 3203 this.validatedForms = this.validatedForms.concat(validatedForms); 3204 } catch (e) { 3205 // eslint-disable-next-line no-console 3206 console.log(e); 3207 } 3208 } 3209 /** 3210 * Controllers must implement a "destroy" method to be called 3211 * after page navigation to remove event handlers / cleanup 3212 */ destroy() { 3213 this.validatedForms = []; 3214 } 3215 /** 3216 * Validate all forms in the document 3217 * @param {NodeListOf<HTMLFormElement>} forms 3218 */ attachValidation(forms) { 3219 forms.forEach((form)=>{ 3220 const inputs = form.querySelectorAll('input, select, textarea'); 3221 inputs.forEach((input)=>{ 3222 this.applyFieldValidation(input); 3223 }); 3224 }); 3225 } 3226 /** 3227 * Field Validation Handler from global context 3228 * @param {HTMLInputElement|HTMLTextAreaElement|HTMLSelectElement} inputField 3229 */ applyFieldValidation(inputField) { 3230 const fieldType = inputField.getAttribute('type'); 3231 if (fieldType === 'tel') this._applyPhoneValidation(inputField); 3232 } 3233 /** 3234 * Phone Field Validation Handler 3235 * @param {HTMLInputElement} inputField 3236 */ _applyPhoneValidation(inputField) { 3237 const { window: window } = this; 3238 if (window.libphonenumber) { 3239 // https://codepen.io/coinscope/pen/BaXBvBp?editors=1010 3240 const phoneUtil = window.libphonenumber.isValidPhoneNumber; 3241 inputField.addEventListener('keyup', function() { 3242 if (this.hasAttribute('required') || this.value.replace(/\s/g, '').length) try { 3243 const phoneNumber = this.value; 3244 const region = 'US'; 3245 const isValidNumber = phoneUtil(phoneNumber, region); 3246 if (!isValidNumber) this.setCustomValidity('Please enter a valid phone number.'); 3247 else this.setCustomValidity(''); 3248 } catch (e) { 3249 this.setCustomValidity('Please enter a valid phone number.'); 3250 } 3251 else this.setCustomValidity(''); 3252 }); 3253 } 3254 } 3255} 3256 3257 3258 3259 3260/** 3261 * Fires the `generate_lead` Google Ads conversion on any marketing-site lead 3262 * event â lead-form submits (all routed through ContactFormController) and 3263 * social sign-in (oauth.js) â at most once per session. 3264 * 3265 * Reuses the same `generate_lead` event the Home Search Tool fires; the page 3266 * template already configures the LP Google tags, so no new Google Ads setup is 3267 * needed. Cross-tab/visitor dedupe is left to the conversion action's 3268 * Count="One" â localStorage is intentionally avoided as it would suppress a 3269 * returning visitor's later lead. 3270 */ const $3ec2bf3965f87ecc$export$78b64890ea40f4e4 = 'generate_lead'; 3271const $3ec2bf3965f87ecc$export$568da12be2433b06 = 'lp_lead_form_conversion_fired'; 3272function $3ec2bf3965f87ecc$var$alreadyFired(window) { 3273 try { 3274 return window.sessionStorage.getItem($3ec2bf3965f87ecc$export$568da12be2433b06) === 'true'; 3275 } catch { 3276 // sessionStorage can throw in private browsing; fail open so we still fire. 3277 return false; 3278 } 3279} 3280function $3ec2bf3965f87ecc$var$markFired(window) { 3281 try { 3282 window.sessionStorage.setItem($3ec2bf3965f87ecc$export$568da12be2433b06, 'true'); 3283 } catch { 3284 // Ignore â see alreadyFired(). 3285 } 3286} 3287function $3ec2bf3965f87ecc$export$e70565180c0e6950(window, document) { 3288 if (!window || !document) return; 3289 // GA bootstrap may not have run yet (or no pixel on this page); don't throw, 3290 // and don't burn the once-per-session flag if we can't actually fire. 3291 if (typeof document.sendGoogleTagManagerEvent !== 'function') return; 3292 if ($3ec2bf3965f87ecc$var$alreadyFired(window)) return; 3293 try { 3294 // Mark fired only AFTER a successful send, so a thrown GTM call doesn't 3295 // permanently suppress the conversion for the rest of the session â and 3296 // never let an analytics failure break the caller's success flow. 3297 document.sendGoogleTagManagerEvent('event', $3ec2bf3965f87ecc$export$78b64890ea40f4e4); 3298 $3ec2bf3965f87ecc$var$markFired(window); 3299 } catch { 3300 // Swallow â analytics must not break form submit / sign-in. 3301 } 3302} 3303 3304 3305 3306const $2aefad83322125c9$var$homeValuationRoute = '/api/v1/home-valuation'; 3307class $2aefad83322125c9$export$2e2bcd8739ae039 extends (0, $7c391e2f7931d60d$export$3b5bd9381a52554c) { 3308 destroy() { 3309 // 3310 } 3311 /** 3312 * Clean address string by removing call time frame suffix and extra whitespace 3313 * 3314 * Handles 17 templates that append ", call time frame: [value]" to the address: 3315 * - TheTrailblazerSir_2, TheMasterpiece (2x), TheInspirationSir_1, TheIconSir_3
3316 * - Influencer, GlobalCollective, Evolution, CorcoranAgent_1 3317 * - CompassSE (2x), CompassPrestige (2x), CompassNavigator 3318 * - ColdwellBanker (2x), Classic 3319 * 3320 * @param {string} address - Raw address string that may contain time frame info 3321 * @return {string} - Cleaned address string 3322 */ _cleanAddressString(address) { 3323 if (!address || typeof address !== 'string') return ''; 3324 let cleaned = address.trim(); 3325 // Fix 1: Handle "User entered address:\n..." pattern from TheProducer templates 3326 // Extract only the user entered address, ignore geocoded part 3327 if (cleaned.includes('User entered address:')) { 3328 const match = cleaned.match(/User entered address:\s*\n?\s*([^\n]+)/); 3329 if (match && match[1]) cleaned = match[1].trim(); 3330 } 3331 // Fix 2: Remove ", call time frame: ..." suffix if present (affects 17 templates) 3332 const timeFrameIndex = cleaned.indexOf(', call time frame:'); 3333 if (timeFrameIndex !== -1) cleaned = cleaned.substring(0, timeFrameIndex).trim(); 3334 return cleaned; 3335 } 3336 /** 3337 * Transform Google Maps address components to structured format 3338 * @param {Array} addressComponents - Google Maps address components 3339 * @return {Object} - Structured address object 3340 */ _transformAddressComponents(addressComponents) { 3341 const get = (t)=>(addressComponents.find((c)=>c.componentType === t) || {}).componentName?.text; 3342 const structuredAddress = {}; 3343 const streetNumber = get('street_number'); 3344 const streetName = get('route'); 3345 const state = get('administrative_area_level_1'); 3346 const zipcode = get('postal_code'); 3347 const addressLine2 = get('subpremise'); 3348 // explicit precedence for city 3349 const city = get('locality') || get('sublocality_level_1') || get('neighborhood'); 3350 if (streetNumber) structuredAddress.streetNumber = streetNumber; 3351 if (streetName) structuredAddress.streetName = streetName; 3352 if (city) structuredAddress.city = city; 3353 if (state) structuredAddress.state = state; 3354 if (zipcode) structuredAddress.zipcode = zipcode; 3355 if (addressLine2) structuredAddress.addressLine2 = addressLine2; 3356 return structuredAddress; 3357 } 3358 /** 3359 * Extract address and coordinates from Google Maps validation data 3360 * @param {Object} validatedAddressData - Google Maps validation result 3361 * @return {Object} - Object containing structuredAddress and coordinates 3362 */ _extractAddressAndCoordinates(validatedAddressData) { 3363 let structuredAddress = null; 3364 let coordinates = null; 3365 if (validatedAddressData?.result) { 3366 const { result: result } = validatedAddressData; 3367 // Extract structured address components 3368 if (result.address?.addressComponents) structuredAddress = this._transformAddressComponents(result.address.addressComponents); 3369 // Extract coordinates 3370 if (result.geocode?.location) coordinates = { 3371 latitude: result.geocode.location.latitude, 3372 longitude: result.geocode.location.longitude 3373 }; 3374 } 3375 return { 3376 structuredAddress: structuredAddress, 3377 coordinates: coordinates 3378 }; 3379 } 3380 /** 3381 * Filter out empty string values from an object, keeping only non-empty values 3382 * @param {Object} obj - Object to filter 3383 * @return {Object} - Filtered object without empty strings 3384 */ _filterEmptyStrings(obj) { 3385 const filtered = {}; 3386 for (const [key, value] of Object.entries(obj))if (value !== '' && value !== null && value !== undefined) filtered[key] = value; 3387 return filtered; 3388 } 3389 /** 3390 * Extract raw address from form data with priority fallback chain. 3391 * Order is critical to handle 38 home valuation templates with different field naming: 3392 * 3393 * IMPORTANT: contactForms.js (line 189) stores name="message" in comments_attributes[0].text 3394 * because 'message' is in DEFAULT_LEAD_FIELDS. Fields NOT in DEFAULT_LEAD_FIELDS go to content. 3395 * 3396 * Priority (most specific â most generic): 3397 * 1. address - Pattern 1: 9 templates (explicit address, stored in content) 3398 * 2. property-address - Pattern 3: 4 templates (dash variant with JS, stored in content) 3399 * 3. property_address - Pattern 4: 3 templates (underscore, stored in content - handles VanguardProperties!) 3400 * 4. Property_Address - Pattern 5: 1 template (capital P - TheProducer/HomeValuationRansomHouse, stored in content)
3401 * 5. comments_attributes[0].text - Pattern 2: ALL 17 templates with name="message" store HERE! 3402 * (e.g., Evolution/HomeValuation, TheProducer/HomeValuation, etc.) 3403 * 6. message - Pattern 6: 4 templates with JS-populated message (rare, checked last) 3404 * 7-8. homeValuationAddress, propertyAddress - Legacy/unknown formats 3405 * 3406 * Critical: Each field is cleaned BEFORE checking, so dropdown values are skipped and we try next field 3407 * 3408 * @param {Object} data - Form data 3409 * @return {string} - Raw address string (may contain trailing text like ", call time frame: ...") 3410 */ _getRawAddress(data) { 3411 const content = data.activity.content || {}; 3412 const comments = data.activity.comments_attributes?.[0]?.text; 3413 const possibleFields = [ 3414 content.address, 3415 content['property-address'], 3416 content.property_address, 3417 content.Property_Address, 3418 comments, 3419 content.message, 3420 content.homeValuationAddress, 3421 content.propertyAddress 3422 ]; 3423 return possibleFields.map((field)=>field && this._cleanAddressString(field)).find(Boolean) || ''; 3424 } 3425 /** 3426 * Logs lead-related events for monitoring 3427 * @param {string} eventType - Type of event 3428 * @param {Object} data - Form data 3429 * @param {Error} error - Optional error object 3430 */ _logLeadEvent(eventType, data, error = null) { 3431 const eventPayload = { 3432 event: eventType, 3433 companyId: this.config.companyId, 3434 websiteId: this.config.websiteId, 3435 timestamp: new Date().toISOString(), 3436 hasEmail: !!data.activity?.author_email, 3437 hasAddress: !!this._getRawAddress(data) 3438 }; 3439 if (error) { 3440 // Use userMessage (sanitized) instead of raw message to prevent PII exposure 3441 // userMessage is set by _getUserFriendlyErrorMessage() which provides safe messages 3442 eventPayload.errorMessage = error.userMessage || (error.status ? `Request failed with status ${error.status}` : 'An error occurred'); 3443 eventPayload.errorName = error.name; 3444 eventPayload.errorStatus = error.status; 3445 } 3446 // Log to console with structured format for log aggregation 3447 // eslint-disable-next-line no-console 3448 console.error(`[HomeValuation] ${eventType}`, JSON.stringify(eventPayload)); 3449 // If PostHog is available, send event for analytics 3450 // Wrap in try/catch to prevent SDK failures from blocking submissions 3451 if (this.window.posthog) try { 3452 this.window.posthog.capture(eventType, eventPayload); 3453 } catch (posthogError) { 3454 // eslint-disable-next-line no-console 3455 console.error('[HomeValuation] PostHog capture failed:', posthogError); 3456 } 3457 } 3458 /** 3459 * Maps error types to user-friendly messages 3460 * @param {Error} error - The error object 3461 * @returns {string} - User-friendly message 3462 */ _getUserFriendlyErrorMessage(error) { 3463 if (error.name === 'AbortError' || error.message?.includes('timeout')) return 'Connection timed out. Please try again.'; 3464 if (error.name === 'TypeError' && error.message?.includes('fetch')) return 'Unable to connect. Please check your internet connection and try again.'; 3465 if (error.status === 503 || error.status === 502 || error.status === 504) return 'Service temporarily unavailable. Please try again in a moment.'; 3466 if (error.status === 400) return 'Please check your information and try again.'; 3467 return 'Something went wrong. Please try again or contact us directly.'; 3468 } 3469 /** 3470 * Build formatted address string from structured address components 3471 * @param {Object} structuredAddress - Structured address object 3472 * @return {string} - Formatted address string 3473 */ _formatStructuredAddress(structuredAddress) { 3474 if (!structuredAddress || Object.keys(structuredAddress).length === 0) return ''; 3475 const parts = []; 3476 // Line 1: streetNumber streetName addressLine2 3477 const line1 = [ 3478 structuredAddress.streetNumber, 3479 structuredAddress.streetName, 3480 structuredAddress.addressLine2
3481 ].filter(Boolean).join(' '); 3482 parts.push(line1); 3483 // Line 2: city, state zipcode 3484 const cityState = [ 3485 structuredAddress.city, 3486 structuredAddress.state 3487 ].filter(Boolean).join(', '); 3488 const line2 = [ 3489 cityState, 3490 structuredAddress.zipcode 3491 ].filter(Boolean).join(' '); 3492 parts.push(line2); 3493 return parts.filter(Boolean).join(', '); 3494 } 3495 /** 3496 * Build home valuation data object 3497 * @param {Object} data - Form data 3498 * @param {string} homeValuationEstimate - Home valuation estimate 3499 * @param {Object} structuredAddress - Structured address object 3500 * @param {Object} coordinates - Coordinates object 3501 * @return {Object} - Home valuation data object 3502 */ _buildHomeValuationData(data, homeValuationEstimate, structuredAddress, coordinates) { 3503 // Build homeValuationAddress from structured address (validated by Google) if available 3504 // Format: streetNumber streetName, city, state zipcode, addressLine2 3505 const homeValuationAddress = this._formatStructuredAddress(structuredAddress) || this._getRawAddress(data); 3506 // Validate that we have an address before proceeding 3507 if (!homeValuationAddress) throw new Error('Unable to extract home valuation address from form data'); 3508 // Required fields that must always be included 3509 const requiredFields = { 3510 companyId: data.companyId, 3511 homeValuationAddress: homeValuationAddress, 3512 homeValuationEstimate: homeValuationEstimate ? new Intl.NumberFormat('en-US', { 3513 style: 'currency', 3514 currency: 'USD', 3515 maximumFractionDigits: 0, 3516 minimumFractionDigits: 0 3517 }).format(homeValuationEstimate) : null, 3518 websiteId: data.page.websiteId, 3519 sourceUrl: data.activity.source_url 3520 }; 3521 // Optional fields that will be filtered if empty 3522 const optionalFields = { 3523 leadEmailAddress: data.activity.author_email, 3524 leadFullName: data.activity.author_name, 3525 leadPhoneNumber: data.activity.author_phone, 3526 parcelId: data.activity.content?.parcelId, 3527 resourceId: data.page.pageMeta?.resourceId, 3528 pageId: data.page.pageId,
3529 sourceResource: data.page.pageMeta?.sourceResource, 3530 pageElementId: data.page.pageMeta?.pageElementId, 3531 formId: data.page.pageMeta?.formId, 3532 utmSource: data.activity.content?.utm_source, 3533 utmMedium: data.activity.content?.utm_medium, 3534 utmCampaign: data.activity.content?.utm_campaign, 3535 utmTerm: data.activity.content?.utm_term, 3536 utmContent: data.activity.content?.utm_content, 3537 utmId: data.activity.content?.utm_id, 3538 timeToSell: data.activity.content?.timeToSell, 3539 lookingToBuy: data.activity.content?.lookingToBuy, 3540 referrer: data.referrer, 3541 content: data.activity.content || {}, 3542 fields: data.fields || {} 3543 }; 3544 // Filter out empty strings from optional fields 3545 const filteredOptionalFields = this._filterEmptyStrings(optionalFields); 3546 return { 3547 ...requiredFields, 3548 ...filteredOptionalFields, 3549 // Add structured address and coordinates if they exist 3550 ...structuredAddress && { 3551 address: structuredAddress 3552 }, 3553 ...coordinates && { 3554 coordinates: coordinates 3555 } 3556 }; 3557 } 3558 /** 3559 * Validate address with Google Maps 3560 * @param {string} rawAddress - Raw address string 3561 * @returns {Promise<Object|null>} - Validated address data or null 3562 * @throws {Error} - If validation fails, error is thrown with userMessage 3563 */ async _validateAddressSafely(rawAddress) { 3564 if (!rawAddress) { 3565 // eslint-disable-next-line no-console 3566 console.error('Home valuation: No address found in form data'); 3567 return null; 3568 } 3569 try { 3570 return await this._validateAddressWithGoogleMaps(rawAddress); 3571 } catch (error) { 3572 // eslint-disable-next-line no-console 3573 console.error('Error validating address with Google Maps:', error); 3574 // Attach user-friendly message and throw so user sees the error 3575 error.userMessage = this._getUserFriendlyErrorMessage(error); 3576 throw error; 3577 } 3578 } 3579 /** 3580 * Get home valuation estimate 3581 * @param {Object} validatedAddressData - Validated address data 3582 * @returns {Promise<number|null>} - Home valuation estimate or null 3583 * @throws {Error} - If estimate fetch fails, error is thrown with userMessage 3584 */ async _getHomeValuationEstimateSafely(validatedAddressData) { 3585 try { 3586 return await this._getHomeValuationEstimate(validatedAddressData); 3587 } catch (error) { 3588 // eslint-disable-next-line no-console 3589 console.error('Error getting home valuation estimate:', error); 3590 // Attach user-friendly message and throw so user sees the error 3591 error.userMessage = this._getUserFriendlyErrorMessage(error); 3592 throw error; 3593 } 3594 } 3595 /** 3596 * Parse response text into payload object 3597 * @param {string} text - Response text 3598 * @returns {Object} - Parsed payload 3599 */ _parseResponsePayload(text) { 3600 try { 3601 return text ? JSON.parse(text) : {}; 3602 } catch { 3603 return { 3604 raw: text 3605 }; 3606 } 3607 } 3608 /** 3609 * Create error from failed response 3610 * @param {Object} payload - Response payload 3611 * @param {number} status - HTTP status code 3612 * @returns {Error} - Error object with status and payload 3613 */ _createResponseError(payload, status) { 3614 const errorMessage = payload && typeof payload === 'object' && Object.keys(payload).length ? payload.message || JSON.stringify(payload) : 'Request failed'; 3615 const error = new Error(errorMessage); 3616 // Preserve payload properties if it's an object 3617 if (payload && typeof payload === 'object' && Object.keys(payload).length) Object.assign(error, payload); 3618 // Preserve numeric HTTP status - must be set after Object.assign 3619 // to ensure it takes precedence over any status in payload 3620 error.status = status; 3621 return error; 3622 } 3623 /** 3624 * Submit lead to API endpoint 3625 * @param {string} leadRoute - API endpoint URL 3626 * @param {Object} homeValuationData - Data to submit 3627 * @returns {Promise<Object>} - Submission result 3628 */ async _submitLead(leadRoute, homeValuationData) { 3629 const controller = new AbortController(); 3630 const timeoutId = setTimeout(()=>controller.abort(), 10000); 3631 try { 3632 const response = await fetch(leadRoute, { 3633 method: 'POST', 3634 headers: { 3635 'Content-Type': 'application/json' 3636 }, 3637 body: JSON.stringify(homeValuationData), 3638 signal: controller.signal 3639 }); 3640 clearTimeout(timeoutId); 3641 const text = await response.text(); 3642 const payload = this._parseResponsePayload(text); 3643 if (!response.ok) throw this._createResponseError(payload, response.status); 3644 if (payload && payload.status === 'success' && payload.redirectUrl) { 3645 this.window.location.href = payload.redirectUrl; 3646 return { 3647 redirected: true, 3648 url: payload.redirectUrl 3649 }; 3650 } 3651 return payload; 3652 } catch (error) { 3653 clearTimeout(timeoutId); 3654 throw error; 3655 } 3656 } 3657 /** 3658 * Submit home valuation request with Google Maps validation 3659 * @param {Object} data 3660 * @return {Promise<Object>} 3661 */ async _submitHomeValuationRequest(data) { 3662 const { config: config } = this; 3663 // Use websiteApiGatewayUrl from config 3664 if (!config.websiteApiGatewayUrl) throw new Error('websiteApiGatewayUrl not configured for home valuation'); 3665 // Log submission started 3666 this._logLeadEvent('home_valuation_submission_started', data); 3667 try { 3668 // Validate address with Google Maps 3669 const rawAddress = this._getRawAddress(data); 3670 let validatedAddressData = null; 3671 try {
3672 validatedAddressData = await this._validateAddressSafely(rawAddress); 3673 } catch (error) { 3674 // Error already has userMessage from _validateAddressSafely 3675 this._logLeadEvent('home_valuation_address_validation_failed', data, error); 3676 throw error; 3677 } 3678 // Get home valuation estimate. 3679 // The estimate is best-effort: many valid properties (e.g. commercial 3680 // parcels or addresses without AVM coverage) have no estimate and the 3681 // estimate endpoint returns a non-OK status for them. A missing estimate 3682 // must never block lead capture, so we log the failure and continue with 3683 // a null estimate rather than aborting the whole submission. 3684 let homeValuationEstimate = null; 3685 try { 3686 homeValuationEstimate = await this._getHomeValuationEstimateSafely(validatedAddressData); 3687 } catch (error) { 3688 // Error already has userMessage from _getHomeValuationEstimateSafely 3689 this._logLeadEvent('home_valuation_estimate_failed', data, error); 3690 homeValuationEstimate = null; 3691 } 3692 // Extract structured address and coordinates from Google Maps validation 3693 const { structuredAddress: structuredAddress, coordinates: coordinates } = this._extractAddressAndCoordinates(validatedAddressData); 3694 const leadRoute = `${config.websiteApiGatewayUrl}${$2aefad83322125c9$var$homeValuationRoute}`; 3695 const homeValuationData = this._buildHomeValuationData(data, homeValuationEstimate, structuredAddress, coordinates); 3696 const result = await this._submitLead(leadRoute, homeValuationData); 3697 // Log success 3698 this._logLeadEvent('home_valuation_submission_success', data); 3699 return result; 3700 } catch (error) { 3701 // Ensure error has user-friendly message (may already be set by helper methods) 3702 if (!error.userMessage) error.userMessage = this._getUserFriendlyErrorMessage(error); 3703 this._logLeadEvent('home_valuation_submission_failed', data, error); 3704 throw error; 3705 } 3706 } 3707 /** 3708 * Validate address using Google Maps Address Validation API 3709 * @param {string} addressStr - The address string to validate 3710 * @returns {Promise<Object|null>} - Validated address data or null if validation fails 3711 */ async _validateAddressWithGoogleMaps(addressStr) { 3712 const { config: config, window: window } = this; 3713 // Ensure Google Maps API key is loaded 3714 let apiKey = config.googleMapsAPIKey; 3715 if (!apiKey && window.luxuryPresence?.mapApiLoader) apiKey = await window.luxuryPresence.mapApiLoader.getMapApiKey(); 3716 if (!apiKey) throw new Error('Google Maps API key not configured'); 3717 const controller = new AbortController(); 3718 const timeoutId = setTimeout(()=>controller.abort(), 10000); 3719 try { 3720 const response = await fetch(`https://addressvalidation.googleapis.com/v1:validateAddress?key=${apiKey}`, { 3721 method: 'POST', 3722 headers: { 3723 'Content-Type': 'application/json' 3724 }, 3725 body: JSON.stringify({ 3726 address: { 3727 addressLines: [ 3728 addressStr 3729 ] 3730 }, 3731 enableUspsCass: false 3732 }), 3733 signal: controller.signal 3734 }); 3735 clearTimeout(timeoutId); 3736 if (!response.ok) { 3737 const error = new Error(`Address validation failed: ${response.status}`); 3738 error.status = response.status; 3739 throw error; 3740 } 3741 return response.json(); 3742 } catch (error) { 3743 clearTimeout(timeoutId); 3744 throw error; 3745 } 3746 } 3747 /** 3748 * Check if structured address has all mandatory fields 3749 * @param {Object} structuredAddress - Structured address object 3750 * @return {boolean} 3751 */ _hasMandatoryAddressFields(structuredAddress) { 3752 return !!(structuredAddress.streetNumber && structuredAddress.streetName && structuredAddress.city && structuredAddress.state && structuredAddress.zipcode); 3753 } 3754 /** 3755 * Log missing mandatory address fields 3756 * @param {Object} structuredAddress - Structured address object 3757 */ _logMissingAddressFields(structuredAddress) { 3758 const missingFields = [];
3759 if (!structuredAddress.streetNumber) missingFields.push('streetNumber'); 3760 if (!structuredAddress.streetName) missingFields.push('streetName'); 3761 if (!structuredAddress.city) missingFields.push('city'); 3762 if (!structuredAddress.state) missingFields.push('state'); 3763 if (!structuredAddress.zipcode) missingFields.push('zipcode'); 3764 // eslint-disable-next-line no-console 3765 console.log('Home valuation estimate skipped - missing required fields:', missingFields.join(', '), 'Available fields:', Object.keys(structuredAddress).join(', ')); 3766 } 3767 /** 3768 * Build query string from structured address 3769 * @param {Object} structuredAddress - Structured address object 3770 * @return {string} 3771 */ _buildAddressQueryString(structuredAddress) { 3772 return Object.entries(structuredAddress).map(([key, value])=>`${encodeURIComponent(key)}=${encodeURIComponent(value)}`).join('&'); 3773 } 3774 /** 3775 * GET home valuation estimate 3776 * @param {string} address 3777 * @param {Object} validatedAddressData - Google Maps validated address data 3778 * @return {Promise<number|null>} 3779 */ async _getHomeValuationEstimate(validatedAddressData = null) { 3780 const { config: config } = this; 3781 if (!config.websiteApiGatewayUrl) throw new Error('websiteApiGatewayUrl not configured'); 3782 const base = `${config.websiteApiGatewayUrl}${$2aefad83322125c9$var$homeValuationRoute}`; 3783 // Check if we have validated address data 3784 if (!validatedAddressData?.result?.address?.addressComponents) return null; 3785 const structuredAddress = this._transformAddressComponents(validatedAddressData.result.address.addressComponents); 3786 // Check if all mandatory fields exist 3787 if (!this._hasMandatoryAddressFields(structuredAddress)) { 3788 this._logMissingAddressFields(structuredAddress); 3789 return null; 3790 } 3791 // Build URL with query string 3792 const qs = this._buildAddressQueryString(structuredAddress); 3793 const homeValURL = `${base}?${qs}`; 3794 const controller = new AbortController(); 3795 const timeoutId = setTimeout(()=>controller.abort(), 10000); 3796 try { 3797 const response = await fetch(homeValURL, { 3798 headers: { 3799 Accept: 'application/json' 3800 }, 3801 signal: controller.signal 3802 }); 3803 clearTimeout(timeoutId); 3804 if (!response.ok) { 3805 const error = new Error(`Home valuation estimate failed: ${response.status}`); 3806 error.status = response.status; 3807 throw error; 3808 } 3809 const data = await response.json(); 3810 return data?.valueDollars ?? null; 3811 } catch (error) { 3812 clearTimeout(timeoutId); 3813 // Re-throw to allow retry logic to handle it 3814 throw error; 3815 } 3816 } 3817} 3818 3819 3820const $e10bd00497be8876$export$97de66002300ed70 = 'middleName'; 3821// Default lead fields 3822const $e10bd00497be8876$var$DEFAULT_LEAD_FIELDS = [ 3823 'name', 3824 'firstName', 3825 'lastName', 3826 $e10bd00497be8876$export$97de66002300ed70, 3827 'email', 3828 'phone', 3829 'phoneNumber', 3830 'source', 3831 'message', 3832 'mlsId', 3833 'sourceUrl' 3834]; 3835class $e10bd00497be8876$export$3c5380623cf51249 extends (0, $7c391e2f7931d60d$export$3b5bd9381a52554c) { 3836 /** 3837 * Initialize Contact Form Controller at build time 3838 * @param { ConfigController } config 3839 * @param {Window} window 3840 * @param {Document} document 3841 */ constructor(config, window, document){ 3842 super(config, window, document); 3843 this.forms = []; 3844 this.homeValuationService = new (0, $2aefad83322125c9$export$2e2bcd8739ae039)(config, window, document); 3845 } 3846 /** 3847 * Initialize Contact Form Controller at run time 3848 * Attach handlers and events to DOM 3849 * @param {Object} options 3850 */ initialize(options = {}) { 3851 try { 3852 const { document: document } = this; 3853 const { selector: selector } = options; 3854 let forms = []; 3855 if (selector) forms = document.querySelectorAll(`${selector} form[data-type="contact-form"]`); 3856 else forms = document.querySelectorAll('form[data-type="contact-form"]');
3857 forms.forEach((form)=>{ 3858 this._attachSubmitHandler(form); 3859 // this._attachValidation(form); 3860 }); 3861 this.forms = this.forms.concat(forms); 3862 } catch (e) { 3863 // eslint-disable-next-line no-console 3864 console.log(e); 3865 } 3866 } 3867 /** 3868 * Controllers must implement a "destroy" method to be called 3869 * after page navigation to remove event handlers / cleanup 3870 */ destroy() { 3871 this.forms = []; 3872 } 3873 /** 3874 * Handle submit from global context 3875 * @param {HTMLFormElement} form 3876 */ submitForm(form) { 3877 this._handleSubmit(null, form).catch((e)=>console.error(e)); 3878 } 3879 /** 3880 * Attach submit event for form 3881 * @param {HTMLFormElement} contactForm 3882 */ _attachValidation(contactForm) { 3883 const { window: window } = this; 3884 const inputs = contactForm.querySelectorAll('input, select, textarea'); 3885 inputs.forEach((input)=>{ 3886 window.luxuryPresence.validation.applyFieldValidation(input); 3887 }); 3888 } 3889 /** 3890 * Attach submit event for form 3891 * @param {HTMLFormElement} contactForm 3892 */ _attachSubmitHandler(contactForm) { 3893 contactForm.addEventListener('submit', (e)=>this._handleSubmit(e, contactForm)); 3894 } 3895 /** 3896 * Extract and process form values 3897 * @param {HTMLFormElement} contactForm 3898 * @return {Object} - Processed form values 3899 */ _extractFormValues(contactForm) { 3900 const formData = new FormData(contactForm); 3901 const values = {}; 3902 const nameParts = []; 3903 const nameKeyRegex = /^name(?:-\d+)?$/; 3904 formData.forEach((value, key)=>{ 3905 if (typeof value === 'string') { 3906 const curr = value.trim(); 3907 if (nameKeyRegex.test(key) && curr) nameParts.push(curr); 3908 const prev = values[key]; 3909 values[key] = prev ? `${prev} ${curr}` : curr; 3910 } else values[key] = value; 3911 }); 3912 if (nameParts.length) values.name = nameParts.join(' ').trim(); 3913 else if (!values.name && values.firstName) { 3914 values.name = values.firstName; 3915 if (values.lastName) values.name += ` ${values.lastName}`; 3916 } 3917 return values; 3918 } 3919 /** 3920 * Build submission data object 3921 * @param {Object} values - Form values 3922 * @param {Object} fields - Form fields 3923 * @param {HTMLFormElement} contactForm - Form element 3924 * @return {Object} - Submission data 3925 */ _buildSubmissionData(values, fields, contactForm) { 3926 const { window: window, config: config } = this; 3927 const content = Object.keys(values).filter((key)=>!$e10bd00497be8876$var$DEFAULT_LEAD_FIELDS.includes(key)).reduce((result, key)=>{ 3928 // eslint-disable-next-line 3929 result[key] = values[key]; 3930 return result; 3931 }, {}); 3932 const { pageId: pageId, companyId: companyId, websiteId: websiteId, pageMeta: pageMeta } = config; 3933 const utm = window.localStorage.getItem('utm_vars') || '{}'; 3934 const referrer = window.localStorage.getItem('referrer') || ''; 3935 const formId = this._getFormId(contactForm); 3936 return { 3937 activity: { 3938 author_email: values.email, 3939 author_name: values.name, 3940 author_phone: values.phone || values.phoneNumber, 3941 activity_source: values.source || 'CONTACT_INQUIRY', 3942 comments_attributes: [ 3943 { 3944 text: values.message || undefined 3945 } 3946 ], 3947 source_url: window.location.href, 3948 content: { 3949 ...content, 3950 ...JSON.parse(utm) 3951 } 3952 }, 3953 page: { 3954 pageId: pageId, 3955 companyId: companyId, 3956 websiteId: websiteId, 3957 pageMeta: { 3958 ...pageMeta, 3959 formId: formId 3960 } 3961 }, 3962 companyId: companyId, 3963 referrer: referrer, 3964 fields: fields 3965 }; 3966 } 3967 /** 3968 * Handle successful form submission 3969 * @param {HTMLFormElement} contactForm - Form element 3970 */ _handleSubmissionSuccess(contactForm) { 3971 const { window: window } = this; 3972 const closeDelay = contactForm.dataset.delay || (0, $a3e97e89bbc2a366$export$f037eeffb5561205); 3973 contactForm.classList.remove('loading'); 3974 contactForm.classList.remove('error'); 3975 contactForm.classList.add('success'); 3976 // TODO: Review producer and classic to deprecate "hide" 3977 contactForm.querySelectorAll('.success').forEach((el)=>el.classList.remove('hide')); 3978 contactForm.dispatchEvent(new Event('formsubmitted')); 3979 // Any successful lead submit credits the Google Ads lead conversion 3980 // (reuses the existing `generate_lead` action), once per session. 3981 (0, $3ec2bf3965f87ecc$export$e70565180c0e6950)(this.window, this.document); 3982 setTimeout(()=>{ 3983 window.lpUI.hideClosestModal(contactForm); 3984 contactForm.reset(); 3985 window.lpUI.sideMenuHide(); 3986 contactForm.querySelectorAll('.success').forEach((el)=>el.classList.add('hide')); 3987 contactForm.classList.remove('success'); 3988 }, closeDelay); 3989 } 3990 /** 3991 * Submit Contact Form Handler 3992 * @param {Event} e 3993 * @param {HTMLFormElement} contactForm 3994 * @return {Promise<Object>} 3995 */ async _handleSubmit(e, contactForm) { 3996 if (e) e.preventDefault(); 3997 if (contactForm.classList.contains('loading')) // Form already submitting 3998 return; 3999 // Hide any previous error message and clear error state 4000 this._hideSubmissionError(contactForm); 4001 contactForm.classList.remove('error'); 4002 contactForm.classList.add('loading'); 4003 try { 4004 // Extract form values first (before async operations) 4005 const values = this._extractFormValues(contactForm); 4006 // TODO: HoneyPot should still send request 4007 if (values.middleName) { 4008 contactForm.classList.remove('loading'); 4009 return; 4010 } 4011 const fields = this._getFields(contactForm); 4012 const data = this._buildSubmissionData(values, fields, contactForm); 4013 await this._submitLeadRequest(data); 4014 this._sendGAEvent(data); 4015 this._handleSubmissionSuccess(contactForm); 4016 } catch (err) { 4017 // eslint-disable-next-line no-console 4018 console.error('Form submission failed:', err); 4019 contactForm.dispatchEvent(new Event('formsubmiterror')); 4020 contactForm.classList.remove('loading'); 4021 contactForm.classList.add('error'); 4022 // Show user-friendly error message if available (from home valuation service)
4023 if (err.userMessage) this._showSubmissionError(contactForm, err.userMessage); 4024 else this._showSubmissionError(contactForm, 'Something went wrong. Please try again.'); 4025 } 4026 } 4027 /** 4028 * Alias anonymous user with canonical ID using event bus 4029 * Links the user's anonymous browsing session to their canonical identity 4030 * @param {Object} data - Form submission data 4031 * @private 4032 */ _aliasUserWithEventBus(data) { 4033 try { 4034 // Check if event bus library and required methods are available 4035 if (!this.window.eventBusProvider?.alias || !this.window.eventBusProvider?.isIdentified || !this.window.eventBusProvider?.getDistinctId || !this.window.LuxuryPresenceEventBus?.generateUUIDv5FromCompanyEmail) return; // Event bus or required methods not available 4036 // Skip if user is already identified 4037 if (this.window.eventBusProvider.isIdentified()) { 4038 console.log('[EventBus] User already identified, skipping alias'); 4039 return; 4040 } 4041 const email = data.activity?.author_email; 4042 const { companyId: companyId } = this.config; 4043 if (!email || !companyId) { 4044 console.warn('[EventBus] Missing required data for user alias'); 4045 return; 4046 } 4047 // Get the anonymous distinct ID from PostHog 4048 const anonId = this.window.eventBusProvider.getDistinctId(); 4049 if (!anonId) { 4050 console.warn('[EventBus] No anonymous ID available for alias'); 4051 return; 4052 } 4053 // Generate deterministic canonical ID from company + email 4054 const canonicalId = this.window.LuxuryPresenceEventBus.generateUUIDv5FromCompanyEmail(companyId, email); 4055 // Create alias linking anonymous ID to canonical ID 4056 this.window.eventBusProvider.alias({ 4057 anonId: anonId, 4058 canonicalId: canonicalId 4059 }); 4060 console.log('[EventBus] User aliased with canonical ID'); 4061 } catch (error) { 4062 // Never block form submission due to analytics errors 4063 console.error('[EventBus] User alias failed:', error); 4064 } 4065 } 4066 /** 4067 * Send Lead Request 4068 * @param {Object} data 4069 * @return {Promise<Object>} 4070 */ async _submitLeadRequest(data) { 4071 const { config: config } = this; 4072 // Alias user with event bus before submitting the lead 4073 this._aliasUserWithEventBus(data); 4074 if (config.myhomeDashboardEnabled && (data.activity?.activity_source === 'HOME_VALUATION' || data.activity?.activity_source === 'HOME_VALUE')) return this.homeValuationService._submitHomeValuationRequest(data); 4075 const leadRoute = `${config.leadIngesterUrl}/api/v1/leads`; 4076 return fetch(leadRoute, { 4077 method: 'POST', 4078 headers: { 4079 'Content-Type': 'application/json' 4080 }, 4081 body: JSON.stringify(data) 4082 }).then((response)=>{ 4083 if (!response.ok) return response.json().then((error)=>{ 4084 throw error; 4085 }); 4086 return response.json(); 4087 }); 4088 } 4089 /** 4090 * Send GA event 4091 * @param {Object} data 4092 */ _sendGAEvent(data) { 4093 const { document: document } = this; 4094 let event = { 4095 hitType: 'event', 4096 eventCategory: 'button', 4097 eventAction: 'click' 4098 }; 4099 switch(data.activity.activity_source){ 4100 case 'NEWSLETTER_SIGNUP': 4101 event.eventLabel = 'Newsletter'; 4102 document.sendGoogleTagManagerEvent('event', 'newsletter_signup'); 4103 break; 4104 case 'HOME_SEARCH': 4105 event.eventLabel = 'HomeSearch'; 4106 document.sendGoogleTagManagerEvent('event', 'home_search'); 4107 break; 4108 case 'HOME_VALUE': 4109 case 'HOME_VALUATION': 4110 event.eventLabel = 'HomeValue'; 4111 document.sendGoogleTagManagerEvent('event', 'home_valuation'); 4112 break; 4113 case 'EBOOK': 4114 event.eventLabel = 'Ebook'; 4115 document.sendGoogleTagManagerEvent('event', 'ebook_download'); 4116 break; 4117 case 'CONTACT_INQUIRY': 4118 default: 4119 // for contact forms, Marketing has requested that they see a page-view of 'thank-you' as indication of success 4120 // Note that this will break if we have actual page with url of /thank-you 4121 event = { 4122 hitType: 'pageview', 4123 page: '/thank-you' 4124 }; 4125 document.sendGoogleTagManagerEvent('event', 'contact_inquiry'); 4126 break; 4127 } 4128 document.sendGoogleAnalyticsEvent(event); 4129 } 4130 /** 4131 * Gather fields / schema for form, used for display in CRM 4132 * @param {HTMLFormElement} form 4133 * @return {Object} 4134 */ _getFields(form) { 4135 const inputs = form.querySelectorAll('input, select, textarea'); 4136 const fields = {};
4137 inputs.forEach((input)=>{ 4138 const { id: id, name: name, type: inputType } = input; 4139 if (name === $e10bd00497be8876$export$97de66002300ed70) // Honeypot continue 4140 return true; 4141 let label = ''; 4142 if (id) { 4143 const labelNode = form.querySelector(`label[for='${id}']`); 4144 if (labelNode) label = labelNode.textContent.trim(); 4145 else if (input.placeholder) label = input.placeholder.trim(); 4146 } 4147 fields[name] = { 4148 label: label, 4149 type: input.tagName, 4150 inputType: input.tagName === 'INPUT' ? inputType : 'text' 4151 }; 4152 }); 4153 return fields; 4154 } 4155 /** 4156 * Figure out relevant section or div ID for form 4157 * @param {HTMLFormElement} form 4158 * @return {string} 4159 */ _getFormId(form) { 4160 const SELECTORS = "section[id^='section-'], footer[id], .modal[id^='modal-']"; 4161 const parent = form.closest(SELECTORS); 4162 return parent?.id ? this._cleanFormId(parent.id) : ''; 4163 } 4164 /** 4165 * Remove prefix from formID 4166 * @param {string} formId 4167 * @return {string} 4168 */ _cleanFormId(formId) { 4169 const PREFIXES = [ 4170 'section-', 4171 'modal-' 4172 ]; 4173 for (const prefix of PREFIXES){ 4174 if (formId.startsWith(prefix)) return formId.substring(prefix.length); 4175 } 4176 return formId; 4177 } 4178 /** 4179 * Show submission error message on form 4180 * @param {HTMLFormElement} form 4181 * @param {string} message 4182 */ _showSubmissionError(form, message) { 4183 const errorEl = form.querySelector('.submission-error-message'); 4184 if (errorEl) { 4185 errorEl.textContent = message || 'Something went wrong. Please try again.'; 4186 errorEl.style.display = 'block'; 4187 } 4188 } 4189 /** 4190 * Hide submission error message on form 4191 * @param {HTMLFormElement} form 4192 */ _hideSubmissionError(form) { 4193 const errorEl = form.querySelector('.submission-error-message'); 4194 if (errorEl) errorEl.style.display = 'none'; 4195 } 4196} 4197 4198 4199 4200const $ea6901fdf47e0ec1$var$CALLBACK = '__googleMapsCallback'; 4201const $ea6901fdf47e0ec1$var$GOOGLE_MAPS_URL = 'https://maps.googleapis.com/maps/api/js'; 4202const $ea6901fdf47e0ec1$export$76299006759be400 = 1024; 4203const $ea6901fdf47e0ec1$var$KEY_FETCH_ATTEMPTS = 3; 4204const $ea6901fdf47e0ec1$var$KEY_RETRY_BASE_DELAY_MS = 300; 4205const $ea6901fdf47e0ec1$var$KEY_FAILURE_FLAG = 'lp_map_key_failed'; 4206class $ea6901fdf47e0ec1$export$3c19195edca3fa5c extends (0, $7c391e2f7931d60d$export$3b5bd9381a52554c) { 4207 /** 4208 * Initialize Button Controller at build time 4209 * @param { ConfigController } config 4210 * @param {Window} window 4211 * @param {Document} document 4212 */ constructor(config, window, document){ 4213 super(config, window, document); 4214 this.retries = 3; 4215 this.callbacks = []; 4216 this.id = `__googleMapsScriptId${Date.now()}`; 4217 this._apiKey = ''; 4218 this.reset(); 4219 } 4220 /** 4221 * Set the Google Maps api key 4222 * @param {string} apiKey 4223 */ set apiKey(apiKey) { 4224 this._apiKey = apiKey; 4225 this.config.setGoogleMapsAPIKey(apiKey); 4226 } 4227 /** 4228 * Get the Google Maps api key 4229 * @return {string} 4230 */ get apiKey() { 4231 return this._apiKey; 4232 } 4233 /** 4234 * Reset the loader state. 4235 */ reset() { 4236 this.deleteScript(); 4237 this.done = false; 4238 this.loading = false; 4239 this.failed = false; 4240 this.errors = []; 4241 this.onerrorEvent = null; 4242 } 4243 /** 4244 * Destroy 4245 */ destroy() { 4246 this.reset(); 4247 } 4248 /** 4249 * get map api Key 4250 * @return {Promise<string>} 4251 */ async getMapApiKey() { 4252 if (this.apiKey) return this.apiKey; 4253 const hadPreviousFailure = this._hadPreviousFailure(); 4254 let recoveryReported = false; 4255 for(let attempt = 1; attempt <= $ea6901fdf47e0ec1$var$KEY_FETCH_ATTEMPTS; attempt += 1){ 4256 const reportRecovery = hadPreviousFailure && !recoveryReported; 4257 try { 4258 // eslint-disable-next-line no-await-in-loop 4259 const response = await fetch(this._getMapKeyUrl(attempt, reportRecovery));
4260 // The request reached the server, so a recovery marker on it was 4261 // delivered and must not be repeated on later attempts or pages. A 4262 // rejected fetch never arrived, so the marker moves to the next 4263 // attempt. 4264 recoveryReported = recoveryReported || reportRecovery; 4265 this._setFailureFlag(false); 4266 if (!response.ok) throw new Error(`Failed to fetch API key: ${response.status}`); 4267 // eslint-disable-next-line no-await-in-loop 4268 const code = await response.text(); 4269 if (!code) return ''; 4270 this.apiKey = code; 4271 return this.apiKey; 4272 } catch (e) { 4273 // eslint-disable-next-line no-console 4274 console.error(e); 4275 if (attempt < $ea6901fdf47e0ec1$var$KEY_FETCH_ATTEMPTS) // eslint-disable-next-line no-await-in-loop 4276 await this._delay($ea6901fdf47e0ec1$var$KEY_RETRY_BASE_DELAY_MS * 2 ** (attempt - 1)); 4277 } 4278 } 4279 this._setFailureFlag(true); 4280 return ''; 4281 } 4282 /** 4283 * Build the map key request url. Retries carry their attempt number, and a 4284 * single request per session carries the recovery marker, so both are 4285 * countable in existing request logs without new infrastructure. 4286 * @param {number} attempt 1-based attempt number 4287 * @param {boolean} reportRecovery tag this request as following a session 4288 * in which every attempt failed 4289 * @return {string} 4290 */ _getMapKeyUrl(attempt, reportRecovery) { 4291 const params = []; 4292 if (attempt > 1) params.push(`attempt=${attempt}`); 4293 if (reportRecovery) params.push('recovered_after_failure=1'); 4294 const query = params.length ? `?${params.join('&')}` : ''; 4295 return `${this._getBaseUrl()}/api/v1/map${query}`; 4296 } 4297 /** 4298 * Whether an earlier attempt in this session exhausted all retries. 4299 * @return {boolean} 4300 */ _hadPreviousFailure() { 4301 try { 4302 return this.window.sessionStorage.getItem($ea6901fdf47e0ec1$var$KEY_FAILURE_FLAG) === '1'; 4303 } catch (e) { 4304 // Storage can throw when disabled (private browsing); absence of the 4305 // flag is the safe default. 4306 return false; 4307 } 4308 } 4309 /** 4310 * Record or clear the session-level key fetch failure flag. 4311 * @param {boolean} failed 4312 */ _setFailureFlag(failed) { 4313 try { 4314 if (failed) this.window.sessionStorage.setItem($ea6901fdf47e0ec1$var$KEY_FAILURE_FLAG, '1'); 4315 else this.window.sessionStorage.removeItem($ea6901fdf47e0ec1$var$KEY_FAILURE_FLAG); 4316 } catch (e) { 4317 // Storage unavailable - reporting is best effort, never fatal. 4318 } 4319 } 4320 /** 4321 * @param {number} ms 4322 * @return {Promise} 4323 */ _delay(ms) { 4324 return new Promise((resolve)=>{ 4325 this.window.setTimeout(resolve, ms); 4326 }); 4327 } 4328 /** 4329 * CreateUrl returns the Google Maps JavaScript API script url ]. 4330 * @return {string} 4331 */ async createUrl() { 4332 const apiKey = await this.getMapApiKey(); 4333 // Google Maps loads successfully without a key and then fails at call time 4334 // with NoApiKeys, which produces a broken map and no load error. Refuse 4335 // instead, so the failure surfaces to the caller. 4336 if (!apiKey) throw new Error('Refusing to load Google Maps without an API key'); 4337 let url = $ea6901fdf47e0ec1$var$GOOGLE_MAPS_URL; 4338 url += `?callback=${$ea6901fdf47e0ec1$var$CALLBACK}`; 4339 url += `&key=${apiKey}`; 4340 url += `&libraries=places`; 4341 return url; 4342 } 4343 /** 4344 * Set the script on document. 4345 */ async setScript() { 4346 if (this.document.getElementById(this.id)) { 4347 this.callback(); 4348 return; 4349 } 4350 let url; 4351 try { 4352 url = await this.createUrl(); 4353 } catch (e) { 4354 // eslint-disable-next-line no-console 4355 console.error(e); 4356 this.failed = true; 4357 this.loading = false; 4358 this.onerrorEvent = e; 4359 this.callback(); 4360 return; 4361 } 4362 const script = this.document.createElement('script'); 4363 script.id = this.id; 4364 script.type = 'text/javascript'; 4365 script.src = url; 4366 script.onerror = this.loadErrorCallback.bind(this); 4367 script.defer = true; 4368 script.async = true; 4369 this.document.head.appendChild(script); 4370 } 4371 /** 4372 * Function to be called if script tag fails 4373 * @param {Error} e 4374 */ loadErrorCallback(e) { 4375 this.errors.push(e); 4376 if (this.errors.length <= this.retries) {
4377 const delay = this.errors.length * 2 ** this.errors.length; 4378 // eslint-disable-next-line no-console 4379 console.log(`Failed to load Google Maps script, retrying in ${delay} ms.`); 4380 setTimeout(()=>{ 4381 this.deleteScript(); 4382 this.setScript().catch((e)=>console.error(e)); 4383 }, delay); 4384 } else { 4385 this.failed = true; 4386 this.onerrorEvent = e; 4387 this.callback(); 4388 } 4389 } 4390 /** 4391 * Delete the script tag 4392 */ deleteScript() { 4393 const script = this.document.getElementById(this.id); 4394 if (script) script.remove(); 4395 } 4396 /** 4397 * Load the Google Maps JavaScript API script and return a Promise. 4398 * @return {Promise} 4399 */ load() { 4400 return this.loadPromise(); 4401 } 4402 /** 4403 * Load the Google Maps JavaScript API script and return a Promise. 4404 * @return {Promise} 4405 */ loadPromise() { 4406 return new Promise((resolve, reject)=>{ 4407 this.loadCallback((err)=>{ 4408 if (!err) resolve(); 4409 else reject(err); 4410 }); 4411 }); 4412 } 4413 /** 4414 * Load the Google Maps JavaScript API script with a callback. 4415 * @param {Function<Event>} fn 4416 */ loadCallback(fn) { 4417 this.callbacks.push(fn); 4418 this.execute(); 4419 } 4420 /** 4421 * Sets the callback function that will be sent as part of the script URL. 4422 */ setCallback() { 4423 this.window.__googleMapsCallback = this.callback.bind(this); 4424 } 4425 /** 4426 * Reset if failed 4427 */ resetIfRetryingFailed() { 4428 if (this.failed) this.reset(); 4429 } 4430 /** 4431 * This is the function that we set the callback parameter to. 4432 */ callback() { 4433 this.done = true; 4434 this.loading = false; 4435 this.callbacks.forEach((cb)=>{ 4436 cb(this.onerrorEvent); 4437 }); 4438 this.callbacks = []; 4439 } 4440 /** 4441 * Set the script a if needed; set the callback 4442 */ execute() { 4443 if (this.window.google && this.window.google.maps && this.window.google.maps.version) // Google Maps already loaded 4444 this.callback(); 4445 this.resetIfRetryingFailed(); 4446 if (this.done) this.callback(); 4447 else if (this.loading) ; 4448 else { 4449 this.loading = true; 4450 this.setCallback(); 4451 this.setScript().catch((e)=>console.error(e)); 4452 } 4453 } 4454 /** 4455 * get base url based on current config 4456 * @return {string} 4457 */ _getBaseUrl() { 4458 // Special handling for page builder since no wag available 4459 const { isBuilder: isBuilder, websiteApiGatewayUrl: websiteApiGatewayUrl } = this.config; 4460 return isBuilder ? websiteApiGatewayUrl : ''; 4461 } 4462 /** 4463 * Resize static map requests to the max 1024px while preserving aspect ratio 4464 * @param {number} width 4465 * @param {number} height 4466 * @return {{width, height}} 4467 */ resizeStaticMapDimensions(width, height) { 4468 if (width <= $ea6901fdf47e0ec1$export$76299006759be400 && height <= $ea6901fdf47e0ec1$export$76299006759be400) return { 4469 width: width, 4470 height: height 4471 }; 4472 // Size param ignored if dimensions exceed STATIC_MAP_MAX_DIM 4473 const ratio = Math.min($ea6901fdf47e0ec1$export$76299006759be400 / width, $ea6901fdf47e0ec1$export$76299006759be400 / height); 4474 return { 4475 width: parseInt(width * ratio), 4476 height: parseInt(height * ratio) 4477 }; 4478 } 4479 /** 4480 * Convert theme to static api param object 4481 * @param {Object} theme 4482 * @return {Object} 4483 */ convertThemeToStaticApiUrl(theme) { 4484 // https://developers.google.com/maps/documentation/maps-static/style-reference 4485 // Google Maps Static API uses a special syntax for it's urls based on the interactive map styles 4486 return theme.map((t)=>{ 4487 const styles = t.stylers.reduce((acc, s)=>{ 4488 const visibility = s.visibility ? `visibility:${s.visibility}` : ''; 4489 const color = s.color ? `color:${s.color.replace('#', '0x')}` : ''; 4490 const lightness = s.lightness ? `lightness:${s.lightness}` : ''; 4491 return `${acc}|${visibility}${color}${lightness}`; 4492 }, ''); 4493 const element = t.elementType ? `|element:${t.elementType}` : ''; 4494 return `feature:${t.featureType}${element}${styles}`; 4495 }); 4496 } 4497 /** 4498 * generate digital signature for url 4499 * @param {string} queryString to sign 4500 * @return {Promise<string>} digitally signed url 4501 */ async getMapApiSignedUrl(queryString) { 4502 try { 4503 const response = await fetch(`${this._getBaseUrl()}/api/v1/map/signature?${queryString}`); 4504 if (!response.ok) throw new Error('Failed to fetch signed URL'); 4505 const { url: url } = await response.json(); 4506 return url; 4507 } catch (e) { 4508 // eslint-disable-next-line no-console 4509 console.error(e); 4510 return ''; 4511 } 4512 } 4513 /** 4514 * Generate static map url 4515 * @param {number} lat 4516 * @param {number} lng 4517 * @param {number} zoom 4518 * @param {Object} theme 4519 * @param {number} srcWidth 4520 * @param {number} srcHeight 4521 * @return {string} 4522 */ generateStaticMapUrl(lat, lng, zoom, theme, srcWidth, srcHeight) {
4523 const baseUrl = 'https://maps.googleapis.com/maps/api/staticmap'; 4524 const { width: width, height: height } = this.resizeStaticMapDimensions(srcWidth, srcHeight); 4525 const query = { 4526 center: `${lat},${lng}`, 4527 zoom: zoom, 4528 scale: 2, 4529 size: `${width}x${height}`, 4530 key: this.apiKey 4531 }; 4532 if (theme) query.style = this.convertThemeToStaticApiUrl(theme); 4533 const params = new URLSearchParams(); 4534 Object.entries(query).forEach(([key, value])=>{ 4535 if (Array.isArray(value)) value.forEach((v)=>params.append(key, v.toString())); 4536 else params.append(key, value.toString()); 4537 }); 4538 return `${baseUrl}?${params.toString()}`; 4539 } 4540 /** 4541 * Generate static map url with digital signature 4542 * @param {Object} { 4543 * srcWidth {number}, required 4544 * srcHeight {number}, required 4545 * theme {array}, 4546 * center: `${lat},${lng}`, required 4547 * zoom: number, required 4548 * markers: `${lat},${lng}`, 4549 * scale: number, 4550 * otherQueryParams... (these must match what is compatible with google maps static api query params) 4551 * } 4552 * @return {Promise<string>} digitally signed url 4553 */ async generateDigitallySignedStaticMapUrl({ srcWidth: srcWidth, srcHeight: srcHeight, theme: theme, ...query }) { 4554 const { width: width, height: height } = this.resizeStaticMapDimensions(srcWidth, srcHeight); 4555 const queryParams = { 4556 ...query, 4557 size: `${width}x${height}` 4558 }; 4559 if (theme) queryParams.style = this.convertThemeToStaticApiUrl(theme); 4560 const params = new URLSearchParams(); 4561 Object.entries(queryParams).forEach(([key, value])=>{ 4562 if (Array.isArray(value)) value.forEach((v)=>params.append(key, v.toString())); 4563 else params.append(key, value.toString()); 4564 }); 4565 const queryString = params.toString(); 4566 try { 4567 // fetch digitally signed url 4568 return await this.getMapApiSignedUrl(queryString); 4569 } catch (e) { 4570 console.error(e); 4571 return ''; 4572 } 4573 } 4574} 4575 4576 4577/* eslint-disable import/prefer-default-export */ 4578 4579class $0a0ad49f13c0b8fd$export$ba9afeb75f20f616 extends (0, $7c391e2f7931d60d$export$3b5bd9381a52554c) { 4580 // constructor is not needed as it is inherited from BaseController 4581 /** 4582 * Controllers must implement a "destroy" method to be called 4583 * after page navigation to remove event handlers / cleanup 4584 */ destroy() {} 4585 /** 4586 * Places (re-places) a value into the query string under specified key 4587 * or removes it if new value is null or undefined 4588 * @param {string} key 4589 * @param {string|null|undefined|object|array} value 4590 * @param {string} url - defaults to window.location.href 4591 * @return {string} 4592 */ // eslint-disable-next-line sonarjs/cognitive-complexity 4593 updateQueryString(key, value, url) { 4594 // https://stackoverflow.com/questions/5999118/how-can-i-add-or-update-a-query-string-parameter 4595 if (!url) // eslint-disable-next-line 4596 url = this.window.location.href; 4597 let re = new RegExp(`([?&])${key}=.*?(&|#|$)`, 'i'); 4598 let replacement = ''; 4599 if (typeof value !== 'undefined' && value !== null) { 4600 if (Array.isArray(value)) { 4601 if (value.length) replacement = this.serializeArray(key, value); 4602 } else if (typeof value === 'object') { 4603 re = new RegExp(`([?&])${key}\\[[^=]*\\]=.*?(&|#|$)`, 'i'); 4604 replacement = this.serializeObj(value, '', key); 4605 } else replacement = `${key}=${encodeURIComponent(value)}`; 4606 } 4607 const hash = url.split('#'); 4608 let result = hash[0]; 4609 if (re.test(url)) { 4610 let clean = result; 4611 replacement = replacement ? `$1${replacement}$2` : '$1'; 4612 while(re.test(clean)){ 4613 result = clean.replace(re, replacement); 4614 clean = clean.replace(re, '$1'); 4615 } 4616 result = result.replace(/(&|\?)$/, ''); 4617 } else if (replacement) { 4618 const separator = result.indexOf('?') !== -1 ? '&' : '?'; 4619 result += `${separator}${replacement}`; 4620 } 4621 if (typeof hash[1] !== 'undefined' && hash[1] !== null) result += `#${hash[1]}`; 4622 return result; 4623 } 4624 /** 4625 * Returns variable from query string specified by parameter name 4626 * @param {string} name 4627 * @param {string} url defaults to window.location.href 4628 * @return {string|null} 4629 */ getParameterByName(name, url) { 4630 if (!url) // eslint-disable-next-line 4631 url = this.window.location.href; 4632 // eslint-disable-next-line 4633 name = name.replace(/[[\]]/g, '\\$&'); 4634 const regex = new RegExp(`[?&]${name}(=([^&#]*)|&|#|$)`); 4635 let results = Array.from(RegExp.prototype[Symbol.matchAll].call(regex, url)).flat(); 4636 // check for object like syntax where the last value can be an array eg. advancedFilters[query][statusIds][in][0] or advancedFilters[query][city] 4637 if (!results.length) { 4638 const objRegex = new RegExp(`[?&]${name}(\\[.*?\\])=(.*?)(?=&|#|$)`, 'g'); 4639 results = Array.from(RegExp.prototype[Symbol.matchAll].call(objRegex, url)).flat(); 4640 return this._parseParamToObject(results, name === 'advancedFilters'); 4641 } 4642 // For multiple param values, eg arrays 4643 /* Results from regex match all come in this form: 4644 [ '&advancedFilters[query][statusIds][in]=uuid-segundo-123', '[query][statusIds][in]', 'uuid-segundo-123' ] 4645 So using every third value will be the actual value and the second value will be the path 4646 */ if (results.length > 3) return results.reduce((acc, value, index)=>{ 4647 if ((index + 1) % 3 === 0) acc.push(decodeURIComponent(value.replace(/\+/g, ' '))); 4648 return acc; 4649 }, []); 4650 if (!results[2]) return ''; 4651 return decodeURIComponent(results[2].replace(/\+/g, ' ')); 4652 } 4653 /** 4654 * Generates section specific key to store page number in the URL 4655 * @param {string} sectionDomId 4656 * @return {string} 4657 */ makePageKey(sectionDomId) { 4658 return sectionDomId.replace('section-', '').replace(/-/g, ''); 4659 } 4660 /** 4661 * Generates specific key to store parameter in the URL 4662 * uses prevsiouly calculated baseKey for section specific parameters 4663 * @param {Object} formParam 4664 * @param {string} baseKey pre-calculated base for section specific parameters 4665 * @return {string} 4666 */ makeParamKey(formParam, baseKey) { 4667 return formParam.global ? formParam.urlKey : `${baseKey}-${formParam.urlKey}`; 4668 } 4669 /** 4670 * Gets a set of parameters from window query string 4671 * always returns value of "page" parameter if present 4672 * @param {Object[]} formConfig 4673 * @param {string} sectionDomId 4674 * @return {Object} 4675 */ deserializeParams(formConfig, sectionDomId) { 4676 const params = {}; 4677 const urlPageKey = this.makePageKey(sectionDomId); 4678 const page = this.getParameterByName(urlPageKey); 4679 if (page) params.page = parseInt(page); 4680 if (Array.isArray(formConfig)) formConfig.forEach((formParam)=>{
4681 const key = this.makeParamKey(formParam, urlPageKey); 4682 let urlValue = this.getParameterByName(key); 4683 if (formParam.type === 'int') { 4684 urlValue = parseInt(urlValue); 4685 // eslint-disable-next-line 4686 if (!isFinite(urlValue)) urlValue = null; 4687 } else if (formParam.type === 'bool' || formParam.type === 'boolean') urlValue = // eslint-disable-next-line 4688 urlValue === 'true' ? true : urlValue === 'false' ? false : null; 4689 else if (formParam.type === 'json') try { 4690 urlValue = JSON.parse(urlValue); 4691 } catch (e) { 4692 // no need to do anything 4693 } 4694 params[formParam.formKey || formParam.param] = urlValue; 4695 }); 4696 return params; 4697 } 4698 /** 4699 * Serializes set of parameters defined by formConfig into the URL 4700 * adding or replacing or removing required parameters 4701 * @param {Object[]} formConfig 4702 * @param {string} sectionDomId 4703 * @param {Object} params 4704 * @return {string} 4705 */ serializeParams(formConfig, sectionDomId, params) { 4706 let result = this.window.location.href; 4707 const urlPageKey = this.makePageKey(sectionDomId); 4708 result = this.updateQueryString(urlPageKey, params.page, result); 4709 if (Array.isArray(formConfig)) formConfig.forEach((formParam)=>{ 4710 const key = this.makeParamKey(formParam, urlPageKey); 4711 let value = params[formParam.formKey || formParam.param]; 4712 if (formParam.type === 'json' && value !== null && typeof value !== 'undefined') value = JSON.stringify(value); 4713 result = this.updateQueryString(key, value, result); 4714 }); 4715 return result; 4716 } 4717 /** 4718 * Formats small number for slider values 4719 * @param {number} value 4720 * @param {string} currency 4721 * @return {string} 4722 */ formatSliderAtom(value, currency = '') { 4723 const formattedValue = Number.isInteger(value) ? value : value.toFixed(2); 4724 return currency + formattedValue; 4725 } 4726 /** 4727 * Formats a number for slider values 4728 * @param {number} value 4729 * @param {string} currency 4730 * @param {string} delimiter 4731 * @return {string} 4732 */ formatSliderValue(value, currency = '', delimiter = '') { 4733 if (value < 1000) return this.formatSliderAtom(value, currency); 4734 if (value < 1000000) return `${this.formatSliderAtom(value / 1000, currency)}${delimiter}K`; 4735 return `${this.formatSliderAtom(value / 1000000, currency)}${delimiter}M`; 4736 } 4737 /** 4738 * Format price slider range 4739 * @param {number} value 4740 * @param {Object} range 4741 * @param {string} currency 4742 * @param {Object} options 4743 * @return {string} 4744 */ formatSliderPriceRange(value, range, currency, options) { 4745 if (range.min === value && value !== 0) return `${options.lowPrefix}${this.formatSliderValue(value, currency, ' ')}`; 4746 if (range.max === value) return `${this.formatSliderValue(value, currency, ' ')}${options.highSuffix}`; 4747 return `${this.formatSliderValue(value, currency, ' ')}`; 4748 } 4749 /** 4750 * Format area slider range 4751 * @param {number} value 4752 * @param {Object} range 4753 * @param {string} suffix 4754 * @param {Object} options 4755 * @return {string} 4756 */ formatSliderAreaRange(value, range, suffix, options) { 4757 if (range.min === value && value !== 0) return `${options.lowPrefix}${this.formatSliderValue(value)} ${suffix}`; 4758 if (range.max === value) return `${this.formatSliderValue(value)}${options.highSuffix} ${suffix}`; 4759 return `${this.formatSliderValue(value)} ${suffix}`; 4760 } 4761 /** 4762 * Utility function to get properties search parameters from a set of jQuery objects 4763 * @param {Object} options 4764 * @param {jQuery|HTMLElement} [options.queryElement] 4765 * @param {jQuery|HTMLElement} [options.priceSliderElement] 4766 * @param {jQuery|HTMLElement} [options.areaSliderElement] 4767 * @param {jQuery|HTMLElement} [options.neighborhoodSelect] 4768 * @return {Object} 4769 */ // eslint-disable-next-line sonarjs/cognitive-complexity 4770 getSearchFormValues(options) { 4771 const { queryElement: queryElement, priceSliderElement: priceSliderElement, areaSli
4771derElement: areaSliderElement, neighborhoodSelect: neighborhoodSelect } = options; 4772 const result = {}; 4773 if (queryElement) result.search = queryElement.value || null; 4774 if (priceSliderElement) { 4775 const priceValues = priceSliderElement.value.split(';'); 4776 const priceMinLimit = parseInt(priceSliderElement.dataset.min); 4777 const priceMaxLimit = parseInt(priceSliderElement.dataset.max); 4778 const priceMin = parseInt(priceValues[0]); 4779 const priceMax = parseInt(priceValues[1]); 4780 result.priceMin = priceMin !== priceMinLimit ? priceMin : null; 4781 result.priceMax = priceMax !== priceMaxLimit ? priceMax : null; 4782 } 4783 if (areaSliderElement) { 4784 const areaValues = areaSliderElement.value.split(';'); 4785 const areaMinLimit = parseInt(areaSliderElement.dataset.min); 4786 const areaMaxLimit = parseInt(areaSliderElement.dataset.max); 4787 const areaMin = parseInt(areaValues[0]); 4788 const areaMax = parseInt(areaValues[1]); 4789 result.areaMin = areaMin !== areaMinLimit ? areaMin : null; 4790 result.areaMax = areaMax !== areaMaxLimit ? areaMax : null; 4791 } 4792 if (neighborhoodSelect) result.neighborhoodId = neighborhoodSelect.value || null; 4793 return result; 4794 } 4795 /** 4796 * Add supports for object typeof values to be encoded on URL 4797 * Ref: https://stackoverflow.com/questions/1714786/query-string-encoding-of-a-javascript-object 4798 * @param {*} obj 4799 * @param {string} prefix 4800 * @param {string} baseParam used for grouping in case of nested objects 4801 * @returns 4802 */ // eslint-disable-next-line sonarjs/cognitive-complexity 4803 serializeObj(obj, prefix = '', baseParam = '') { 4804 const result = []; 4805 for(const prop in obj)if (obj.hasOwnProperty(prop)) { 4806 const value = obj[prop]; 4807 let k; 4808 if (baseParam) { 4809 const newPrefix = prefix ? `[${prefix}][${prop}]` : `[${prop}]`; 4810 k = `${baseParam}${newPrefix}`; 4811 } else k = prefix ? `${prefix}[${prop}]` : prop; 4812 if (value !== null && Array.isArray(value)) { 4813 if (value.length) result.push(this.serializeArray(k, value)); 4814 } else if (value !== null && typeof value === 'object') result.push(this.serializeObj(value, k)); 4815 else result.push(`${k}=${encodeURIComponent(value)}`); 4816 } 4817 return result.join('&'); 4818 } 4819 /** 4820 * Adds support for array values to be encoded on URL 4821 * @param {string} key 4822 * @param {Array} value 4823 * @param {string} baseParam 4824 */ serializeArray(key, value, baseParam = '') { 4825 const result = []; 4826 value.forEach((val)=>{ 4827 const paramName = baseParam ? `${baseParam}${key}` : key; 4828 result.push(`${paramName}=${encodeURIComponent(val)}`); 4829 }); 4830 return result.join('&'); 4831 } 4832 /** 4833 * Private method to process string search values. 4834 * @param {Array} results comes from regex matchAll. 4835 * @param {Boolean} isAdvancedFilters flag to determine if the search is for advanced filters. 4836 * @returns 4837 */ _parseParamToObject(results, isAdvancedFilters = false) { 4838 if (!results.length) return null; 4839 const obj = {}; 4840 for(let i = 0; i < results.length; i++)/* Results from regex match all come in this form: 4841 [ '&advancedFilters[query][statusIds][in]=uuid-segundo-123', '[query][statusIds][in]', 'uuid-segundo-123' ] 4842 So using every third value will be the actual value and the second value will be the path 4843 */ if ((i + 1) % 3 === 0) { 4844 const value = decodeURIComponent(results[i].replace(/\+/g, ' ')); 4845 const path = results[i - 1]; 4846 Object.assign(obj, this._setObjectPath(path, value, obj, isAdvancedFilters)); 4847 } 4848 return obj; 4849 } 4850 /** * 4851 * Private method to set object value given a path 4852 * @param {String} path 4853 * @param {*} value 4854 * @param {Object} baseObj 4855 * @param {Boolean} isAdvancedFilters flag to determine if the search is for advanced filters. 4856 */ _setObjectPath(path, value, baseObj, isAdvancedFilters = false) { 4857 // eslint-disable-next-line
4858 const keys = path.split(/[\[\]]/).filter(Boolean); 4859 const afArrayKeys = [ 4860 'in', 4861 'nin', 4862 'btw', 4863 'ciin', 4864 'cinin' 4865 ]; 4866 const obj = {}; 4867 let ref = obj; 4868 const valueExists = (0, $fc0a058ae42af628$export$267a1435fe18b8d4)(baseObj, keys.join('.')); 4869 keys.forEach((key, index)=>{ 4870 if (index === keys.length - 1) { 4871 if (valueExists) ref[key] = Array.isArray(valueExists) ? [ 4872 ...valueExists, 4873 value 4874 ] : [ 4875 valueExists, 4876 value 4877 ]; 4878 else ref[key] = isAdvancedFilters && afArrayKeys.includes(key) ? [ 4879 value 4880 ] : value; 4881 } else { 4882 ref[key] = baseObj[key] ? baseObj[key] : {}; 4883 ref = ref[key]; 4884 } 4885 }); 4886 return obj; 4887 } 4888} 4889 4890 4891/* eslint-disable import/prefer-default-export */ 4892class $2b1790aa32f109d1$export$43e435c053a109f6 extends (0, $7c391e2f7931d60d$export$3b5bd9381a52554c) { 4893 /** 4894 * Initialize Expand Slider Controller at build time 4895 * @param { ConfigController } config 4896 * @param {Window} window 4897 * @param {Document} document 4898 */ constructor(config, window, document){ 4899 super(config, window, document); 4900 this._createLightbox = this._createLightbox.bind(this); 4901 this.expandSliders = []; 4902 } 4903 /** 4904 * Initialize Expand Slider Controller at run time 4905 * Attach handlers and events to DOM 4906 * @param {Object} options 4907 */ initialize(options = {}) { 4908 try { 4909 const { document: document } = this; 4910 const expandSliders = document.querySelectorAll('.has-mobile-expand'); 4911 expandSliders.forEach((block)=>{ 4912 this._addExpandButton(block); 4913 }); 4914 this.expandSliders = this.expandSliders.concat(expandSliders); 4915 } catch (e) { 4916 // eslint-disable-next-line no-console 4917 console.log(e); 4918 } 4919 } 4920 /** 4921 * Controllers must implement a "destroy" method to be called 4922 * after page navigation to remove event handlers / cleanup 4923 */ destroy() { 4924 const { document: document } = this; 4925 document.querySelectorAll('.lp-expand-btn, .lp-expand-btn-wrapper').forEach((element)=>element.remove()); 4926 this.expandSliders = []; 4927 } 4928 /** 4929 * Create expand button for sliders 4930 * @param {HTMLElement} slider 4931 */ _addExpandButton(slider) { 4932 const { document: document } = this; 4933 const section = slider.closest('section[id]'); 4934 const appendTargetSelector = slider.dataset.append; 4935 let expandBtn = section.querySelector('.lp-expand-btn'); 4936 if (!expandBtn) { 4937 expandBtn = document.createElement('button'); 4938 expandBtn.className = 'lp-expand-btn'; 4939 expandBtn.setAttribute('aria-label', 'Open a lightbox'); 4940 expandBtn.innerHTML = `<span class="lp-i lp-i--expand-alt" aria-hidden="true"></span>`; 4941 if (appendTargetSelector) { 4942 const appendTarget = section.querySelector(appendTargetSelector); 4943 if (appendTarget) appendTarget.appendChild(expandBtn); 4944 } else { 4945 const wrapper = document.createElement('div'); 4946 wrapper.className = 'lp-expand-btn-wrapper'; 4947 wrapper.style.position = 'relative'; 4948 wrapper.appendChild(expandBtn); 4949 slider.parentNode.insertBefore(wrapper, slider.nextSibling); 4950 } 4951 } 4952 expandBtn.addEventListener('click', ()=>{ 4953 this._createLightbox({ 4954 slider: slider, 4955 section: section 4956 }); 4957 }); 4958 } 4959 /** 4960 * Creates a lightbox modal for displaying slider images in a larger view. 4961 * 4962 * @param {Object} params - The parameters for the function. 4963 * @param {HTMLElement} params.slider - The slider element containing images. 4964 * @param {HTMLElement} params.section - The section element related to the slider. 4965 * @param {number} [params.initialIndex] - Optional index of the image to show initially. 4966 */ _createLightbox({ slider: slider, section: section, initialIndex: initialIndex }) { 4967 const { window: window, document: document } = this; 4968 const modalId = `lightbox-for-${section?.id}`; 4969 const isSlickAvailable = window.$ && window.$.fn.slick; 4970 const isSectionSlickInitialized = isSlickAvailable && slider.slick; 4971 const isSplideAvailable = typeof window.Splide === 'function'; 4972 const isSectionSplideInitialized = slider.classList.contains('is-initialized') && slider.classList.contains('splide'); 4973 if (typeof window.lpUI.requestScrollLock === 'function') window.lpUI.requestScrollLock(modalId); 4974 else document.body.style.overflow = 'hidden'; 4975 function createSlickLightbox() { 4976 const slickOverlayOptions = { 4977 arrows: true, 4978 dots: false, 4979 infinite: true, 4980 draggable: true, 4981 prevArrow: '<button type="button" class="lp-lightbox-arrow--prev lp-lightbox-arrow" aria-label="Previous"></button>', 4982 nextArrow: '<button type="button" class="lp-lightbox-arrow--next lp-lightbox-arrow" aria-label="Next"></button>' 4983 }; 4984 const modal = document.createElement('div'); 4985 modal.id = modalId; 4986 modal.className = 'lp-lightbox'; 4987 modal.setAttribute('tabindex', '-1'); 4988 modal.setAttribute('role', 'dialog'); 4989 modal.setAttribute('aria-modal', 'true'); 4990 modal.innerHTML = ` 4991 <button class="lp-lightbox__close" aria-label="close modal"></button> 4992 <div class="lp-lightbox__content"> 4993 <div class="lp-lightbox__carousel"></div> 4994 </div> 4995 `; 4996 document.body.appendChild(modal); 4997 const overlaySlider = modal.querySelector('.lp-lightbox__carousel'); 4998 modal.classList.add('show'); 4999 let sourceImages; 5000 if (isSectionSlickInitialized) { 5001 sourceImages = slider.slick.$slides.find('img'); 5002 slickOverlayOptions.initialSlide = slider.slick?.currentSlide || 0; 5003 sourceImages.each(function() { 5004 window.$('.lp-lightbox__carousel').append(window.$(this).clone(false).removeAttr('style').wrap('<div class="lp-lightbox__slide"></div>').parent()); 5005 }); 5006 } else { 5007 sourceImages = slider.querySelectorAll('img');
5008 sourceImages.forEach((img)=>{ 5009 const imgClone = img.cloneNode(true); 5010 imgClone.removeAttribute('style'); 5011 const slide = document.createElement('div'); 5012 slide.className = 'lp-lightbox__slide'; 5013 slide.appendChild(imgClone); 5014 overlaySlider.appendChild(slide); 5015 }); 5016 } 5017 window.$(overlaySlider).slick(slickOverlayOptions); 5018 // Focus modal after it's created 5019 setTimeout(()=>{ 5020 modal.focus(); 5021 }, 100); 5022 // Keyboard navigation handler 5023 const handleKeydown = (e)=>{ 5024 if (!modal.classList.contains('show')) return; 5025 const prevArrow = modal.querySelector('.lp-lightbox-arrow--prev'); 5026 const nextArrow = modal.querySelector('.lp-lightbox-arrow--next'); 5027 const closeBtn = modal.querySelector('.lp-lightbox__close'); 5028 if (e.key === 'ArrowLeft' && prevArrow) { 5029 e.preventDefault(); 5030 prevArrow.click(); 5031 } else if (e.key === 'ArrowRight' && nextArrow) { 5032 e.preventDefault(); 5033 nextArrow.click(); 5034 } else if (e.key === 'Escape' && closeBtn) { 5035 e.preventDefault(); 5036 closeBtn.click(); 5037 } 5038 }; 5039 document.addEventListener('keydown', handleKeydown); 5040 modal.querySelector('.lp-lightbox__close').addEventListener('click', ()=>{ 5041 document.removeEventListener('keydown', handleKeydown); 5042 modal.classList.remove('show'); 5043 const currentSlide = overlaySlider?.slick ? window.$(overlaySlider).slick('slickCurrentSlide') : 0; 5044 if (overlaySlider?.slick) window.$(overlaySlider).slick('unslick'); 5045 if (isSectionSlickInitialized) window.$(slider).slick('slickGoTo', currentSlide); 5046 if (typeof window.lpUI.releaseScrollLock === 'function') window.lpUI.releaseScrollLock(modalId); 5047 else document.body.style.overflow = ''; 5048 modal.remove(); 5049 }); 5050 } 5051 function createSplideLightbox() { 5052 const splideOptions = { 5053 rewind: true, 5054 arrows: true, 5055 arrowPath: '', 5056 type: 'loop', 5057 clones: 1, 5058 pagination: false, 5059 perPage: 1, 5060 perMove: 1 5061 }; 5062 const modal = document.createElement('div'); 5063 modal.id = modalId; 5064 modal.className = 'lp-lightbox'; 5065 modal.setAttribute('tabindex', '-1'); 5066 modal.setAttribute('role', 'dialog'); 5067 modal.setAttribute('aria-modal', 'true'); 5068 modal.innerHTML = ` 5069 <button class="lp-lightbox__close" aria-label="close modal"></button> 5070 <div class="lp-lightbox__content"> 5071 <div class="lp-lightbox__carousel splide" role="group" aria-label="Image Gallery"> 5072 <div class="splide__arrows"> 5073 <button class="splide__arrow splide__arrow--prev lp-lightbox-arrow lp-lightbox-arrow--prev"></button> 5074 <button class="splide__arrow splide__arrow--next lp-lightbox-arrow lp-lightbox-arrow--next"></button> 5075 </div> 5076 <div class="splide__track"> 5077 <ul class="splide__list"></ul> 5078 </div> 5079 </div> 5080 </div> 5081 `; 5082 if (typeof window.lpUI.requestScrollLock === 'function') window.lpUI.requestScrollLock(modalId); 5083 else document.body.style.overflow = 'hidden'; 5084 document.body.appendChild(modal); 5085 const overlaySlider = modal.querySelector('.lp-lightbox__carousel'); 5086 const overlaySliderList = overlaySlider.querySelector('.splide__list'); 5087 modal.classList.add('show'); 5088 let sourceImages; 5089 if (isSectionSplideInitialized) sourceImages = slider.querySelectorAll('.splide__slide:not(.splide__slide--clone) img'); 5090 else sourceImages = slider.querySelectorAll('img');
5091 sourceImages.forEach((img)=>{ 5092 const imgClone = img.cloneNode(true); 5093 imgClone.removeAttribute('style'); 5094 const slide = document.createElement('li'); 5095 slide.className = 'lp-lightbox__slide splide__slide'; 5096 slide.appendChild(imgClone); 5097 overlaySliderList.appendChild(slide); 5098 }); 5099 const overlaySplide = new window.Splide(overlaySlider, splideOptions); 5100 overlaySplide.mount(); 5101 // Navigate to initial index if provided 5102 if (typeof initialIndex !== 'undefined' && initialIndex >= 0) overlaySplide.go(initialIndex); 5103 // Focus modal after it's created and mounted 5104 setTimeout(()=>{ 5105 modal.focus(); 5106 }, 100); 5107 // Keyboard navigation handler 5108 const handleKeydown = (e)=>{ 5109 if (!modal.classList.contains('show')) return; 5110 const prevArrow = modal.querySelector('.splide__arrow--prev'); 5111 const nextArrow = modal.querySelector('.splide__arrow--next'); 5112 const closeBtn = modal.querySelector('.lp-lightbox__close'); 5113 if (e.key === 'ArrowLeft' && prevArrow) { 5114 e.preventDefault(); 5115 prevArrow.click(); 5116 } else if (e.key === 'ArrowRight' && nextArrow) { 5117 e.preventDefault(); 5118 nextArrow.click(); 5119 } else if (e.key === 'Escape' && closeBtn) { 5120 e.preventDefault(); 5121 closeBtn.click(); 5122 } 5123 }; 5124 document.addEventListener('keydown', handleKeydown); 5125 modal.querySelector('.lp-lightbox__close').addEventListener('click', ()=>{ 5126 document.removeEventListener('keydown', handleKeydown); 5127 modal.classList.remove('show'); 5128 if (overlaySplide) overlaySplide.destroy(); 5129 if (typeof window.lpUI.releaseScrollLock === 'function') window.lpUI.releaseScrollLock(modalId); 5130 else document.body.style.overflow = ''; 5131 modal.remove(); 5132 }); 5133 } 5134 if (isSlickAvailable && !isSectionSplideInitialized) createSlickLightbox(); 5135 else if (isSplideAvailable) createSplideLightbox(); 5136 else { 5137 window.luxuryPresence.libs.loadCss(window.luxuryPresence.libs.jsLibsMap.splide.cssSrc); 5138 window.luxuryPresence.libs.loadJs(window.luxuryPresence.libs.jsLibsMap.splide.jsSrc, ()=>{ 5139 createSplideLightbox(); 5140 window.luxuryPresence.libs.jsLibsMap.splide.isLoaded = true; 5141 }); 5142 } 5143 } 5144} 5145 5146 5147/* eslint-disable import/prefer-default-export */ 5148class $4f184d9bd3037f15$export$7a6215890afba974 extends (0, $7c391e2f7931d60d$export$3b5bd9381a52554c) { 5149 // constructor is not needed as it is inherited from BaseController 5150 /** 5151 * Destroy 5152 */ destroy() { 5153 if (!this.window.divolte) this.window.divolteLp = function() { 5154 (this.window.divolteLp.q = this.window.divolteLp.q || []).push(// eslint-disable-next-line prefer-rest-params 5155 arguments); 5156 }; 5157 } 5158 /** 5159 * Initialize divolte controller 5160 * @param {Object} options 5161 */ initialize(options = {}) { 5162 const { divolteServiceUrl: divolteServiceUrl } = this.config; 5163 if (!divolteServiceUrl || this.window.divolte) return; 5164 this.loadScript(); 5165 } 5166 /** 5167 * loads divolte 5168 */ loadScript() { 5169 const { divolteServiceUrl: divolteServiceUrl } = this.config; 5170 const script = this.document.createElement('script'); 5171 script.id = 'divolte-script'; 5172 script.type = 'text/javascript'; 5173 script.defer = true; 5174 script.async = true; 5175 script.src = `${divolteServiceUrl}/divolte.js`; 5176 this.document.head.appendChild(script); 5177 script.addEventListener('load', this.executeQueue.bind(this)); 5178 } 5179 /** 5180 * executeQueue 5181 */ executeQueue() { 5182 (this.window.divolteLp.q || []).forEach(([methodName, params])=>{ 5183 this[methodName](params); 5184 }); 5185 this.window.divolteLp = this.divolteLp.bind(this); 5186 } 5187 /** 5188 * divolteLp 5189 * @param {string} name 5190 * @param {Object} params 5191 */ divolteLp(name, params) { 5192 try { 5193 this[name](params); 5194 } catch (error) { 5195 // eslint-disable-next-line no-console 5196 console.log(error); 5197 } 5198 } 5199 /** 5200 * sendLpEvent 5201 * @param {string} name 5202 * @param {Object} params 5203 */ sendLpEvent(name, params) { 5204 // Events must be defined in divolte schema! To ensure correct usage, event names are validated here. 5205 if (!name.includes('pageView')) { 5206 // Event not defined 5207 // eslint-disable-next-line no-console 5208 console.log(`${name} IS NOT SUPPORTED`); 5209 return; 5210 } 5211 const { companyId: companyId, templateId: templateId, websiteId: websiteId, pageId: pageId, shareId: shareId, pageMeta: pageMeta } = this.config; 5212 const baseParams = { 5213 companyId: companyId, 5214 templateId: templateId, 5215 websiteId: websiteId, 5216 pageId: pageId, 5217 shareId: shareId, 5218 pageMeta: pageMeta 5219 }; 5220 this.window.divolte.signal(name, Object.assign(baseParams, params)); 5221 } 5222 /** 5223 * sendLpPageView 5224 */ sendLpPageView() { 5225 this.sendLpEvent('pageView', {}); 5226 } 5227} 5228 5229 5230/* eslint-disable import/prefer-default-export */ 5231const $102039871668a1df$var$EVENT_BUS_SCRIPT = 'event-bus-js.js'; 5232const $102039871668a1df$var$EVENT_BUS_VERSION = 'v2.8.0'; 5233const $102039871668a1df$var$EVENT_BUS_DEFAULT_SOURCE = 'frontend://website-render-engine'; 5234class $102039871668a1df$export$e22e4b1be0bb6c61 extends (0, $7c391e2f7931d60d$export$3b5bd9381a52554c) { 5235 // eslint-disable-next-line @typescript-eslint/no-useless-constructor 5236 constructor(config, window, document){ 5237 super(config, window, document); 5238 } 5239 /** 5240 * Initialize EventBus controller 5241 * @param {Object} options 5242 */ initialize(options = {}) { 5243 const enabled = options.enabled !== undefined ? options.enabled : this.config.eventBus?.enabled; 5244 if (enabled && this.config.eventBus?.apiKey && this.config.eventBus?.url) this.loadScript(); 5245 } 5246 /** 5247 * loads eventBus script 5248 */ loadScript() { 5249 const { eventBusUrl: eventBusUrl } = this.config; 5250 const script = this.document.createElement('script'); 5251 script.id = 'posthog-script'; 5252 script.type = 'text/javascript'; 5253 script.defer = true; 5254 script.async = true; 5255 script.src = `${eventBusUrl}/${$102039871668a1df$var$EVENT_BUS_VERSION}/${$102039871668a1df$var$EVENT_BUS_SCRIPT}`; 5256 this.document.head.appendChild(script); 5257 script.addEventListener('load', this.executeQueue.bind(this)); 5258 } 5259 /** 5260 * executeQueue - Process queued eventBusLp calls 5261 */ executeQueue() { 5262 // Initialize the event bus provider using the global EventBusJS 5263 if (this.window.LuxuryPresenceEventBus) this.window.eventBusProvider = this.window.LuxuryPresenceEventBus.createEventBusProvider({ 5264 apiKey: this.config.eventBus.apiKey, 5265 host: this.config.eventBus.url, 5266 defaultSource: $102039871668a1df$var$EVENT_BUS_DEFAULT_SOURCE 5267 });
5268 (this.window.eventBusLp.q || []).forEach(([methodName, params])=>{ 5269 this[methodName](params); 5270 }); 5271 // Replace queue function with real function 5272 this.window.eventBusLp = this.eventBusLp.bind(this); 5273 } 5274 /** 5275 * Destroy 5276 */ destroy() { 5277 if (!this.window.eventBusProvider) { 5278 // Reset to queue function if no provider exists 5279 this.window.eventBusLp = function() { 5280 (this.window.eventBusLp.q = this.window.eventBusLp.q || []).push(// eslint-disable-next-line prefer-rest-params 5281 arguments); 5282 }; 5283 return; 5284 } 5285 this.window.eventBusProvider.shutdown(); 5286 } 5287 /** 5288 * sendLpEvent 5289 * @param {string} eventType 5290 * @param {Object} data 5291 */ sendLpEvent(eventType, data) { 5292 // Events must be defined in event-bus schema! To ensure correct usage, event types are validated here. 5293 if (eventType !== 'user.activity.page.viewed') { 5294 // Event type not defined 5295 // eslint-disable-next-line no-console 5296 console.log(`${eventType} IS NOT SUPPORTED`); 5297 return; 5298 } 5299 if (!this.window.eventBusProvider) return; 5300 const { companyId: companyId } = this.config; 5301 const eventData = { 5302 company: companyId, 5303 data: {} 5304 }; 5305 try { 5306 this.window.eventBusProvider.capture(eventType, eventData); 5307 } catch (error) { 5308 // eslint-disable-next-line no-console 5309 console.log('EventBus capture error:', error); 5310 } 5311 } 5312 /** 5313 * eventBusLp 5314 * @param {string} name 5315 * @param {Object} params 5316 */ eventBusLp(name, params) { 5317 try { 5318 this[name](params); 5319 } catch (error) { 5320 // eslint-disable-next-line no-console 5321 console.log(error); 5322 } 5323 } 5324 /** 5325 * sendLpPageView 5326 */ sendLpPageView() { 5327 this.sendLpEvent('user.activity.page.viewed', {}); 5328 } 5329} 5330 5331 5332/* eslint-disable import/prefer-default-export */ 5333 5334const $0d25a3fa2e0f4f92$var$EVENT_NAME = 'oauthSuccess'; 5335class $0d25a3fa2e0f4f92$export$b718a981691eae5c extends (0, $7c391e2f7931d60d$export$3b5bd9381a52554c) { 5336 /** 5337 * Initialize Button Controller at build time 5338 * @param { ConfigController } config 5339 * @param {Window} window 5340 * @param {Document} document 5341 */ constructor(config, window, document){ 5342 super(config, window, document); 5343 this.successListeners = []; 5344 this._listenToMessages = this._listenToMessages.bind(this); 5345 this._listenToOneTapMessage = this._listenToOneTapMessage.bind(this); 5346 } 5347 /** 5348 * Initialize OAuth Controller at run time 5349 * Swap out special buttons for the iframe 5350 * @param {Object} options 5351 */ initialize(options = {}) { 5352 try { 5353 // handle google one tap 5354 if (options.googleSignOnEnabled && !options.iframe) this._renderGoogleOneTap(); 5355 // handle google sign in buttons 5356 this.document.querySelectorAll('div[data-signin-type="google-sign-in"]').forEach((el)=>this._convertGoogleSignIn(el)); 5357 // handle facebook sign in buttons 5358 this.document.querySelectorAll('div[data-signin-type="facebook-sign-in"]').forEach((el)=>this._convertFacebookSignIn(el)); 5359 } catch (e) { 5360 // eslint-disable-next-line no-console 5361 console.log(e); 5362 } 5363 } 5364 /** 5365 * Controllers must implement a "destroy" method to be called 5366 * after page navigation to remove event handlers / cleanup 5367 */ destroy() { 5368 this.successListeners.forEach((callback)=>{ 5369 this.window.removeEventListener('message', this._listenToMessages); 5370 this.window.removeEventListener('message', this._listenToOneTapMessage); 5371 this.window.removeEventListener($0d25a3fa2e0f4f92$var$EVENT_NAME, this.window[callback]); 5372 }); 5373 } 5374 /** 5375 * Convert google one tap 5376 */ _renderGoogleOneTap() { 5377 if (!this._isGoogleOneTapOnCooldown()) { 5378 const dummyEl = this.document.createElement('div'); 5379 // eslint-disable-next-line no-undef 5380 google.accounts.id.initializeIntermediate({ 5381 src: this._buildUrlAndCopyAttributes(dummyEl, dummyEl, 'googleOneTap') 5382 }); 5383 this.window.localStorage.removeItem('googleOneTapExpiration'); 5384 this.window.addEventListener('message', this._listenToOneTapMessage); 5385 } 5386 } 5387 /** 5388 * Listening for messages from BSS one tap iframes 5389 * @param {MessageEvent} e 5390 */ _listenToOneTapMessage(e) { 5391 const messageTypes = { 5392 SHOW_INTERMEDIATE_IFRAME: 'show_intermediate_iframe', 5393 HIDE_INTERMEDIATE_IFRAME: 'hide_intermediate_iframe' 5394 };
5395 const iframeId = 'onetap_google_intermediate_iframe'; 5396 const { buyerSellerServiceUrl: buyerSellerServiceUrl } = this.config; 5397 const { data: data, origin: origin } = e; 5398 const { type: type, height: height, event: event, source: source, token: token } = data; 5399 if (type === messageTypes.SHOW_INTERMEDIATE_IFRAME || type === messageTypes.HIDE_INTERMEDIATE_IFRAME) { 5400 const bssIframe = this.document.getElementById(iframeId); 5401 if (bssIframe) { 5402 bssIframe.style.height = height; 5403 bssIframe.style.display = type === messageTypes.SHOW_INTERMEDIATE_IFRAME ? 'unset' : 'none'; 5404 } 5405 } 5406 // these events are generated by our code in the bss iframe 5407 if (origin === buyerSellerServiceUrl && source === 'GOOGLE_SIGN_ON') { 5408 if (event === 'success') { 5409 this._setExpirationLsItem(86400000); // 24 hours 5410 this._handleQualifyingQuestionsModal(token); 5411 } else if (event === 'cancel') this._setExpirationLsItem(7200000); // 2 hours 5412 } 5413 } 5414 /** 5415 * Set expiration local storage item for google one tap 5416 * @param {number} ms 5417 */ _setExpirationLsItem(ms) { 5418 const expiration = new Date().getTime() + ms; 5419 this.window.localStorage.setItem('googleOneTapExpiration', expiration); 5420 } 5421 /** 5422 * Check if google one tap is expired 5423 * @return {boolean} 5424 */ _isGoogleOneTapOnCooldown() { 5425 const expirationTime = this.window.localStorage.getItem('googleOneTapExpiration'); 5426 return Number(expirationTime) > new Date().getTime(); 5427 } 5428 /** 5429 * Convert google sign in buttons 5430 * @param {HTMLElement} el 5431 */ _convertGoogleSignIn(el) { 5432 this._replaceWithIframe('googleSignIn', el); 5433 } 5434 /** 5435 * Convert google sign in buttons 5436 * @param {HTMLElement} el 5437 */ _convertFacebookSignIn(el) { 5438 this._replaceWithIframe('facebookSignIn', el); 5439 } 5440 /** 5441 * Convert google sign in buttons 5442 * @param {'googleSignIn' | 'facebookSignIn'} provider 5443 * @param {HTMLElement} el 5444 */ _replaceWithIframe(provider, el) { 5445 this._attachSuccessCallback(el); 5446 const iframe = this.document.createElement('iframe'); 5447 iframe.src = this._buildUrlAndCopyAttributes(el, iframe, provider); 5448 iframe.dataset.isSocialOauthButton = true; 5449 el.replaceWith(iframe); 5450 } 5451 /** 5452 * Listening for messages from BSS iframes 5453 * @param {Event} e 5454 */ _listenToMessages(e) { 5455 const { buyerSellerServiceUrl: buyerSellerServiceUrl } = this.config; 5456 // eslint-disable-next-line sonarjs/no-collapsible-if 5457 if (e.origin === buyerSellerServiceUrl) { 5458 if (e.data.event === 'success') { 5459 const event = new Event($0d25a3fa2e0f4f92$var$EVENT_NAME); 5460 this.window.dispatchEvent(event); 5461 // Registration / social sign-in is a lead event â credit the conversion. 5462 (0, $3ec2bf3965f87ecc$export$e70565180c0e6950)(this.window, this.document); 5463 this.successListeners.forEach((callback)=>{ 5464 if (this.window[callback]) this.window[callback](event); 5465 else // eslint-disable-next-line no-console 5466 console.error(`Callback function ${callback} not found on window object`); 5467 }); 5468 this.document.documentElement.classList.add('is-login-success'); 5469 } 5470 } 5471 } 5472 /** 5473 * Attach callback to iframe 5474 * @param {HTMLElement} el 5475 */ _attachSuccessCallback(el) { 5476 this.window.addEventListener('message', this._listenToMessages); 5477 const callbackFunction = el.dataset.onSuccessFunctionName; 5478 if (callbackFunction) this.successListeners.push(callbackFunction); 5479 } 5480 /** 5481 * Build Iframe URL 5482 * @param {HTMLElement} source 5483 * @param {HTMLElement} target 5484 * @param {string} provider 5485 * @return {string} 5486 */ _buildUrlAndCopyAttributes(source, target, provider) { 5487 const { buyerSellerServiceUrl: buyerSellerServiceUrl, companyId: companyId, websiteId: websiteId, pageId: pageId, pageMeta: pageMeta } = this.config; 5488 const { location: location } = this.window; 5489 const sourceUrl = location.origin + location.pathname; 5490 const destination = new URL(`${buyerSellerServiceUrl}/buttons/${provider}`); 5491 const utm = this.window.localStorage.getItem('utm_vars'); 5492 const referrer = this.window.localStorage.getItem('referrer'); 5493 destination.searchParams.append('companyId', companyId); 5494 destination.searchParams.append('websiteId', websiteId); 5495 destination.searchParams.append('pageId', pageId); 5496 destination.searchParams.append('sourceUrl', sourceUrl); 5497 destination.searchParams.append('pageMeta', JSON.stringify(pageMeta)); 5498 if (utm) destination.searchParams.append('utm', utm); 5499 if (referrer) destination.searchParams.append('referrer', referrer); 5500 // Google and Facebook Attributes used to style the social button 5501 const RESERVED_ATTRIBUTES = [ 5502 'data-size', 5503 'data-width', 5504 'data-text', 5505 'data-button-type', 5506 'data-shape', 5507 'data-layout', 5508 'data-max-rows', 5509 'data-use-continue-as', 5510 'data-type', 5511 'data-theme', 5512 'data-logo_alignment', 5513 'data-locale' 5514 ];
5515 Array.from(source.attributes).forEach((attribute)=>{ 5516 if (RESERVED_ATTRIBUTES.includes(attribute.nodeName)) destination.searchParams.append(attribute.nodeName, attribute.nodeValue); 5517 else target.setAttribute(attribute.nodeName === 'id' ? 'data-id' : attribute.nodeName, attribute.nodeValue); 5518 }); 5519 return destination.toString(); 5520 } 5521 /** 5522 * Decode a JWT token to extract payload information 5523 * @param {string} token - The JWT token to decode 5524 * @return {Object|null} The decoded payload object or null if decoding fails 5525 */ _decodeToken(token) { 5526 try { 5527 const parts = token.split('.'); 5528 const payload = parts[1]; 5529 const base64 = payload.replace(/-/g, '+').replace(/_/g, '/'); 5530 const padding = 4 - base64.length % 4; 5531 const padded = padding === 4 ? base64 : base64 + '='.repeat(padding); 5532 return JSON.parse(atob(padded)); 5533 } catch (error) { 5534 return null; 5535 } 5536 } 5537 /** 5538 * Handle qualifying questions modal showing for Google Sign On 5539 * Decodes the JWT token to extract user email and shows the modal if available 5540 * @param {string} token - The JWT token containing user information 5541 */ _handleQualifyingQuestionsModal(token) { 5542 if (token && this.window.luxuryPresence && this.window.luxuryPresence.qualifyingQuestionsModal) { 5543 // Decoding only for lead attribution, fine to skip signature verification 5544 const decoded = this._decodeToken(token); 5545 if (decoded && decoded.email) this.window.luxuryPresence.qualifyingQuestionsModal.showModal(decoded.email, 'Google Sign On'); 5546 } 5547 } 5548} 5549 5550 5551/* eslint-disable import/prefer-default-export */ 5552class $dcf72769f009ff26$export$b900d6fe921fd49c extends (0, $7c391e2f7931d60d$export$3b5bd9381a52554c) { 5553 /** 5554 * Initialize Qualifying Questions Modal Controller at build time 5555 * @param { ConfigController } config 5556 * @param {Window} window 5557 * @param {Document} document 5558 */ constructor(config, window, document){ 5559 super(config, window, document); 5560 this.showModal = this.showModal.bind(this); 5561 this.hasModalBeenSubmitted = this.hasModalBeenSubmitted.bind(this); 5562 this.modalId = 'qualifying-questions-modal'; 5563 this.cookieKey = 'qualifyingQuestionsModalSubmitted'; 5564 this.qualifyingQuestionsModalEnabled = true; 5565 // Store references for cleanup 5566 this.eventListeners = []; 5567 this.timeouts = []; 5568 } 5569 initialize(options = {}) { 5570 this.qualifyingQuestionsModalEnabled = options.qualifyingQuestionsModalEnabled ?? this.qualifyingQuestionsModalEnabled; 5571 if (!this.qualifyingQuestionsModalEnabled) return; 5572 this.attachModalCloseListeners(); 5573 } 5574 destroy() { 5575 // Remove all stored event listeners 5576 this.eventListeners.forEach(({ element: element, event: event, handler: handler })=>{ 5577 if (element && element.removeEventListener) element.removeEventListener(event, handler); 5578 }); 5579 this.timeouts.forEach((timeoutId)=>{ 5580 clearTimeout(timeoutId); 5581 }); 5582 this.restoreBodyScroll(); 5583 this.eventListeners = []; 5584 this.timeouts = []; 5585 } 5586 /** 5587 * Check if modal has been submitted using cookie-based deduplication 5588 * @return {boolean} 5589 */ hasModalBeenSubmitted() { 5590 const cookieValue = this.getCookie(this.cookieKey); 5591 return cookieValue === 'true'; 5592 } 5593 /** 5594 * Set cookie to prevent modal from showing again 5595 */ setModalSubmittedCookie() { 5596 this.setCookie(this.cookieKey, 'true', 365); // 1 year 5597 } 5598 showModal(email, triggerSource) { 5599 if (!this.qualifyingQuestionsModalEnabled || this.hasModalBeenSubmitted()) return; 5600 this.updateEmailValue(email); 5601 this.updateTriggerSourceValue(triggerSource); 5602 this.showModalElement(); 5603 } 5604 /** 5605 * Get modal element with error handling 5606 * @return {Element|null} 5607 */ getModalElement() { 5608 const modal = this.document.getElementById(this.modalId); 5609 if (!modal) console.error('Modal element not found:', this.modalId); 5610 return modal; 5611 } 5612 updateEmailValue(email) { 5613 const modal = this.getModalElement(); 5614 if (!modal) return; 5615 const emailInput = modal.querySelector('input[name="email"]'); 5616 if (emailInput && typeof email === 'string') emailInput.value = email; 5617 } 5618 updateTriggerSourceValue(triggerSource) { 5619 const modal = this.getModalElement(); 5620 if (!modal) return;
5621 const triggerSourceInput = modal.querySelector(`#${this.modalId}-triggerSource`); 5622 if (triggerSourceInput && typeof triggerSource === 'string') triggerSourceInput.value = triggerSource; 5623 } 5624 showModalElement() { 5625 const modal = this.getModalElement(); 5626 if (!modal) return; 5627 modal.style.display = 'flex'; 5628 this.blockBodyScroll(); 5629 } 5630 /** 5631 * Helper method to get cookie value 5632 * @param {string} name 5633 * @return {string|null} 5634 */ getCookie(name) { 5635 const value = `; ${this.document.cookie}`; 5636 const parts = value.split(`; ${name}=`); 5637 if (parts.length === 2) return decodeURIComponent(parts.pop().split(';').shift()); 5638 return null; 5639 } 5640 /** 5641 * Helper method to set cookie 5642 * @param {string} name 5643 * @param {string} value 5644 * @param {number} days 5645 */ setCookie(name, value, days) { 5646 let expires = ''; 5647 if (days) { 5648 const date = new Date(); 5649 date.setTime(date.getTime() + days * 86400000); 5650 expires = `; expires=${date.toUTCString()}`; 5651 } 5652 let attrs = '; path=/; SameSite=Lax'; 5653 if (this.window && this.window.location && this.window.location.protocol === 'https:') attrs += '; Secure'; 5654 this.document.cookie = `${name}=${encodeURIComponent(value || '')}${expires}${attrs}`; 5655 } 5656 blockBodyScroll() { 5657 this._prevOverflow = this.document.body.style.overflow; 5658 this.document.body.style.overflow = 'hidden'; 5659 } 5660 restoreBodyScroll() { 5661 this.document.body.style.overflow = this._prevOverflow || ''; 5662 this._prevOverflow = null; 5663 } 5664 attachModalCloseListeners() { 5665 const modal = this.getModalElement(); 5666 if (!modal) return; 5667 // Listen for form submission success (when modal closes automatically) 5668 const form = this.document.getElementById(`${this.modalId}-form`); 5669 if (form) { 5670 const formSubmittedHandler = ()=>{ 5671 // Set cookie to prevent showing again and hide modal 5672 this.setModalSubmittedCookie(); 5673 const timeoutId = setTimeout(()=>{ 5674 modal.style.display = 'none'; 5675 this.restoreBodyScroll(); 5676 }, 100); 5677 this.timeouts.push(timeoutId); 5678 }; 5679 form.addEventListener('formsubmitted', formSubmittedHandler); 5680 this.eventListeners.push({ 5681 element: form, 5682 event: 'formsubmitted', 5683 handler: formSubmittedHandler 5684 }); 5685 } 5686 // Listen for close button clicks 5687 const closeButton = modal.querySelector('[data-dismiss="modal"]'); 5688 if (closeButton) { 5689 const closeButtonHandler = ()=>{ 5690 modal.style.display = 'none'; 5691 this.restoreBodyScroll(); 5692 }; 5693 closeButton.addEventListener('click', closeButtonHandler); 5694 this.eventListeners.push({ 5695 element: closeButton, 5696 event: 'click', 5697 handler: closeButtonHandler 5698 }); 5699 } 5700 // Listen for backdrop clicks 5701 const backdropClickHandler = (e)=>{ 5702 // Check if click is on the modal backdrop (not on the modal content) 5703 if (e.target === modal || e.target.id === this.modalId) { 5704 modal.style.display = 'none'; 5705 this.restoreBodyScroll(); 5706 } 5707 }; 5708 modal.addEventListener('click', backdropClickHandler); 5709 this.eventListeners.push({ 5710 element: modal, 5711 event: 'click', 5712 handler: backdropClickHandler 5713 }); 5714 } 5715} 5716 5717 5718/* eslint-disable import/prefer-default-export */ 5719class $50eefeef8d1f2372$export$8a2bf06fbae4dde extends (0, $7c391e2f7931d60d$export$3b5bd9381a52554c) { 5720 constructor(configController, window, document){ 5721 super(configController, window, document); 5722 this.isValidating = false; 5723 this.lastValidationResult = null; 5724 this.lastValidationTime = null; 5725 this.validationCacheDuration = 300000; // 5 minutes 5726 } 5727 /** 5728 * Destroy 5729 */ destroy() { 5730 this.isValidating = false;
5731 this.lastValidationResult = null; 5732 this.lastValidationTime = null; 5733 } 5734 /** 5735 * Validate user session using GraphQL validSession query 5736 * @param {Object} options - Options for session validation 5737 * @param {boolean} options.useCache - Whether to use cached validation result 5738 * @returns {Promise<boolean>} - true if session is valid, false otherwise 5739 */ async validateSession(options = {}) { 5740 const { useCache: useCache = true } = options; 5741 if (useCache && this.lastValidationResult !== null && this.lastValidationTime) { 5742 const timeSinceLastValidation = Date.now() - this.lastValidationTime; 5743 if (timeSinceLastValidation < this.validationCacheDuration) return this.lastValidationResult; 5744 } 5745 if (this.isValidating) return this.lastValidationResult !== null ? this.lastValidationResult : false; 5746 this.isValidating = true; 5747 const query = ` 5748 query { 5749 validSession { 5750 success 5751 } 5752 } 5753 `; 5754 try { 5755 // query from same domain as the website 5756 const response = await this.window.fetch(`/graphql`, { 5757 method: 'POST', 5758 headers: { 5759 'Content-Type': 'application/json' 5760 }, 5761 credentials: 'include', 5762 body: JSON.stringify({ 5763 query: query 5764 }) 5765 }); 5766 if (!response.ok) { 5767 // eslint-disable-next-line no-console 5768 console.error('SessionController: HTTP error during session validation', response.status); 5769 this.lastValidationResult = false;
5770 this.lastValidationTime = Date.now(); 5771 return false; 5772 } 5773 const data = await response.json(); 5774 if (data.errors) { 5775 this.lastValidationResult = false; 5776 this.lastValidationTime = Date.now(); 5777 return false; 5778 } 5779 const isValid = data.data?.validSession?.success === true; 5780 this.lastValidationResult = isValid; 5781 this.lastValidationTime = Date.now(); 5782 return isValid; 5783 } catch (error) { 5784 // eslint-disable-next-line no-console 5785 console.error('SessionController: Error validating session', error); 5786 this.lastValidationResult = false; 5787 this.lastValidationTime = Date.now(); 5788 return false; 5789 } finally{ 5790 this.isValidating = false; 5791 } 5792 } 5793 /** 5794 * Clear validation cache 5795 */ clearCache() { 5796 this.lastValidationResult = null; 5797 this.lastValidationTime = null; 5798 } 5799} 5800 5801 5802/* eslint-disable import/prefer-default-export */ 5803function $396d9f0ca7465335$export$48d5542b9e3ba39(document) { 5804 const match = document.cookie.match(/cookieyes-consent=([^;]+)/); 5805 if (!match) return false; 5806 try { 5807 const cookieValue = decodeURIComponent(match[1]); 5808 if (cookieValue.includes(',')) { 5809 const segments = cookieValue.split(',').map((c)=>c.trim()); 5810 if (segments.some((s)=>s.includes(':'))) return segments.some((s)=>s === 'analytics:yes'); 5811 return segments.includes('analytics'); 5812 } 5813 return cookieValue.includes('analytics'); 5814 } catch (error) { 5815 return false; 5816 } 5817} 5818/** 5819 * Consent providers. Each entry isolates the only things that differ between 5820 * integrations: the change-event name, how to read the *initial* analytics 5821 * consent, and how to interpret a change event's detail. Everything else 5822 * (loading/removing trackers, lifecycle) is shared by the controller. 5823 */ const $396d9f0ca7465335$var$PROVIDERS = { 5824 // Third-party CookieYes banner. 5825 cookieyes: { 5826 eventName: 'cookieyes_consent_update', 5827 hasAnalyticsConsent: (document)=>$396d9f0ca7465335$export$48d5542b9e3ba39(document), 5828 isAnalyticsAccepted: (detail)=>Array.isArray(detail?.accepted) && detail.accepted.includes('analytics') 5829 }, 5830 // LP in-house consent widget (lp-consent-widget). It exposes resolved state on 5831 // window.__lpConsent and emits `lp_consent_update` with detail.choices. 5832 'lp-consent': { 5833 eventName: 'lp_consent_update', 5834 hasAnalyticsConsent: (_document, window)=>!!window.__lpConsent?.state?.c?.analytics, 5835 isAnalyticsAccepted: (detail)=>!!detail?.choices?.analytics 5836 } 5837}; 5838class $396d9f0ca7465335$export$bb018224876724b9 extends (0, $7c391e2f7931d60d$export$3b5bd9381a52554c) { 5839 constructor(config, window, document){ 5840 super(config, window, document); 5841 this.consentListener = null; 5842 this.eventName = null; 5843 } 5844 /** 5845 * Destroy and cleanup event listeners 5846 */ destroy() { 5847 if (this.consentListener && this.eventName) { 5848 this.document.removeEventListener(this.eventName, this.consentListener); 5849 this.consentListener = null; 5850 } 5851 } 5852 /** 5853 * Load PostHog and Divolte scripts (idempotent â guarded by element id). 5854 */ loadScript() { 5855 if (!this.document.getElementById('posthog-script')) this.window.luxuryPresence.eventBus.initialize({ 5856 enabled: this.config.eventBus?.enabled 5857 }); 5858 if (!this.document.getElementById('divolte-script')) this.window.luxuryPresence.divolte.initialize({}); 5859 } 5860 /** 5861 * Remove PostHog and Divolte scripts (used on consent withdrawal). 5862 */ removeScripts() { 5863 this.window.luxuryPresence?.eventBus?.destroy?.(); 5864 this.window.luxuryPresence?.divolte?.destroy?.(); 5865 this.document.getElementById('posthog-script')?.remove(); 5866 this.document.getElementById('divolte-script')?.remove(); 5867 } 5868 /** 5869 * Initialize the consent controller for a given provider. 5870 * @param {{ provider: 'cookieyes' | 'lp-consent' }} options 5871 */ async initialize(options = {}) { 5872 const provider = $396d9f0ca7465335$var$PROVIDERS[options.provider]; 5873 if (!provider) return; 5874 this.eventName = provider.eventName; 5875 const isAuthenticated = await this.window.luxuryPresence.session.validateSession(); 5876 if (provider.hasAnalyticsConsent(this.document, this.window) || isAuthenticated) this.loadScript(); 5877 if (!this.consentListener) { 5878 this.consentListener = (eventData)=>{ 5879 if (provider.isAnalyticsAccepted(eventData.detail)) this.loadScript(); 5880 else this.removeScripts(); 5881 }; 5882 this.document.addEventListener(provider.eventName, this.consentListener); 5883 } 5884 } 5885} 5886 5887 5888/* global window */ /* eslint-disable import/prefer-default-export */ /** 5889 * Checks if the current site should redirect homeowner valuation submissions to HST. 5890 * Returns true and performs the redirect if all three gate conditions are met. 5891 * Returns false if the redirect did not fire (caller should continue with existing flow). 5892 * 5893 * @param {string} address - The human-readable address string from the form 5894 * @returns {boolean} 5895 */ function $0ae243a777665fd3$var$checkHstRedirect(address) { 5896 const { config: config } = window.luxuryPresence; 5897 if (config.hstValuationRedirectEnabled && config.mlsEnabled && config.myhomeDashboardEnabled) { 5898 window.location.href = `/home-search/home-valuation?address=${encodeURIComponent(address)}`; 5899 return true; 5900 } 5901 return false;
5902} 5903const $0ae243a777665fd3$export$4225ff17959c7313 = { 5904 checkHstRedirect: $0ae243a777665fd3$var$checkHstRedirect, 5905 destroy () {} 5906}; 5907 5908 5909const $fa0280e505379701$export$1fd2b467a3644d37 = new (0, $dc851cf3939b6ef5$export$ff0712c26d5458f7)({}); 5910const $fa0280e505379701$export$e12694e911a05f40 = new (0, $0ead03fdb95fa9ce$export$d507f25e63650365)($fa0280e505379701$export$1fd2b467a3644d37, window, document); 5911const $fa0280e505379701$export$3de614e59a842917 = new (0, $d48f9f944810f613$export$ca3218b7861b9cb0)($fa0280e505379701$export$1fd2b467a3644d37, window, document); 5912const $fa0280e505379701$export$fb9b298fdbf31455 = new (0, $68a2d4aa500c7a05$export$61ec8f2b6291ec31)($fa0280e505379701$export$1fd2b467a3644d37, window, document); 5913const $fa0280e505379701$export$178cc3119b453c2a = new (0, $e10bd00497be8876$export$3c5380623cf51249)($fa0280e505379701$export$1fd2b467a3644d37, window, document); 5914const $fa0280e505379701$export$9376af1a01686f2 = new (0, $ea6901fdf47e0ec1$export$3c19195edca3fa5c)($fa0280e505379701$export$1fd2b467a3644d37, window, document); 5915const $fa0280e505379701$export$94d36f806562393e = new (0, $0a0ad49f13c0b8fd$export$ba9afeb75f20f616)($fa0280e505379701$export$1fd2b467a3644d37, window, document); 5916const $fa0280e505379701$export$89e66d1faf94651e = new (0, $2b1790aa32f109d1$export$43e435c053a109f6)($fa0280e505379701$export$1fd2b467a3644d37, window, document); 5917const $fa0280e505379701$export$7743b07270cd6d6c = new (0, $4f184d9bd3037f15$export$7a6215890afba974)($fa0280e505379701$export$1fd2b467a3644d37, window, document); 5918const $fa0280e505379701$export$40a7039551318c37 = new (0, $102039871668a1df$export$e22e4b1be0bb6c61)($fa0280e505379701$export$1fd2b467a3644d37, window, document); 5919const $fa0280e505379701$export$8a0bbf7e7652fcfc = new (0, $0d25a3fa2e0f4f92$export$b718a981691eae5c)($fa0280e505379701$export$1fd2b467a3644d37, window, document); 5920const $fa0280e505379701$export$b313b3f6544c97a3 = new (0, $dcf72769f009ff26$export$b900d6fe921fd49c)($fa0280e505379701$export$1fd2b467a3644d37, window, document); 5921const $fa0280e505379701$export$b526bc4ac03268ef = new (0, $50eefeef8d1f2372$export$8a2bf06fbae4dde)($fa0280e505379701$export$1fd2b467a3644d37, window, document); 5922const $fa0280e505379701$export$c0affdb0a7a2ef6 = new (0, $396d9f0ca7465335$export$bb018224876724b9)($fa0280e505379701$export$1fd2b467a3644d37, window, document); 5923 5924 5925/** 5926 * Attach controller to window for usage 5927 * @param {string} key 5928 * @param {any} controller 5929 */ function $89a8a84b77163415$var$attachController(key, controller) { 5930 if (window.luxuryPresence[key]) try { 5931 // New version of sdk, destroy previous 5932 window.luxuryPresence[key].destroy(); 5933 } catch (e) { 5934 // eslint-disable-next-line no-console 5935 console.log(e); 5936 } 5937 window.luxuryPresence[key] = controller; 5938} 5939$89a8a84b77163415$var$attachController('config', (0, $fa0280e505379701$export$1fd2b467a3644d37)); 5940$89a8a84b77163415$var$attachController('modals', (0, $fa0280e505379701$export$e12694e911a05f40)); 5941$89a8a84b77163415$var$attachController('buttons', (0, $fa0280e505379701$export$3de614e59a842917)); 5942$89a8a84b77163415$var$attachController('validation', (0, $fa0280e505379701$export$fb9b298fdbf31455)); 5943$89a8a84b77163415$var$attachController('contactForms', (0, $fa0280e505379701$export$178cc3119b453c2a)); 5944$89a8a84b77163415$var$attachController('mapApiLoader', (0, $fa0280e505379701$export$9376af1a01686f2)); 5945$89a8a84b77163415$var$attachController('pagination', (0, $fa0280e505379701$export$94d36f806562393e)); 5946$89a8a84b77163415$var$attachController('expandSliders', (0, $fa0280e505379701$export$89e66d1faf94651e)); 5947$89a8a84b77163415$var$attachController('divolte', (0, $fa0280e505379701$export$7743b07270cd6d6c)); 5948$89a8a84b77163415$var$attachController('eventBus', (0, $fa0280e505379701$export$40a7039551318c37)); 5949$89a8a84b77163415$var$attachController('oauth', (0, $fa0280e505379701$export$8a0bbf7e7652fcfc)); 5950$89a8a84b77163415$var$attachController('qualifyingQuestionsModal', (0, $fa0280e505379701$export$b313b3f6544c97a3)); 5951$89a8a84b77163415$var$attachController('session', (0, $fa0280e505379701$export$b526bc4ac03268ef)); 5952$89a8a84b77163415$var$attachController('consent', (0, $fa0280e505379701$export$c0affdb0a7a2ef6)); 5953$89a8a84b77163415$var$attachController('redirects', (0, $0ae243a777665fd3$export$4225ff17959c7313)); 5954// methods for existing elements (can be removed later after elements updated) 5955window.luxuryPresence.formatSliderPriceRange = (0, $fa0280e505379701$export$94d36f806562393e).formatSliderPriceRange.bind((0, $fa0280e505379701$export$94d36f806562393e)); 5956window.luxuryPresence.formatSliderAreaRange = (0, $fa0280e505379701$export$94d36f806562393e).formatSliderAreaRange.bind((0, $fa0280e505379701$export$94d36f806562393e)); 5957// method below doesn't use 'this', but just to be safe in case of further method updates 5958window.luxuryPresence.getSearchFormValues = (0, $fa0280e505379701$export$94d36f806562393e).getSearchFormValues.bind((0, $fa0280e505379701$export$94d36f806562393e)); 5959window.luxuryPresence.handlebarsHelpersFactory = function(handlebarsInstance) { 5960 return (0, (/*@__PURE__*/$parcel$interopDefault($53de3c160faf5a81$exports
5960)))(handlebarsInstance, { 5961 isEqual (a, b) { 5962 // eslint-disable-next-line 5963 return a == b; 5964 } 5965 }); 5966}; 5967 5968 5969 5970})(); 5971//# sourceMappingURL=lp-sdk.js.map 5972 5973 </script>
5973 5974 5975
5975<script> 5976 var lpGlobalInitialized = false; 5977 5978 function lpGlobalInit () { 5979 if (lpGlobalInitialized) { 5980 return; 5981 } 5982 lpGlobalInitialized = true; 5983 // Initialize wow animation plugin only according to turbolinks 5984 var wow; 5985 if (window.WOW) { 5986 wow = new WOW({ 5987 animateClass: 'animated', 5988 offset: wowOffset, 5989 live: false, 5990 callback: function(box) {} 5991 }); 5992 } 5993 // Initialize handlebar helpers only once 5994 function handlebarsInit() { 5995 if (!window.Handlebars) { 5996 return; 5997 } 5998 HandlebarsIntl.registerWith(Handlebars); 5999 Handlebars.registerHelper(window.luxuryPresence.handlebarsHelpersFactory(Handlebars)); 6000 } 6001 6002 handlebarsInit(); 6003 6004 var modalContentUrl = "/modals.html"; 6005 const modalsContainer = document.getElementById('modals'); 6006 if (modalsContainer) { 6007 fetch(modalContentUrl).then(function(res) { 6008 if (!res.ok) { 6009 throw new Error(`HTTP error! Status: ${res.status}`); 6010 } 6011 return res.text(); 6012 }).then(function(resultText) { 6013 modalsContainer.innerHTML = resultText; 6014 6015 // now we need to execute all script elements 6016 Array.from(modalsContainer.getElementsByTagName('script')).forEach(function (s) { 6017 const script = document.createElement("script"); 6018 script.text = s.innerHTML; 6019 Array.from(s.attributes).forEach(function (a) { 6020 script.setAttribute(a.name, a.value); 6021 }) 6022 s.parentNode.replaceChild(script, s); 6023 }); 6024 6025 try { 6026 window.luxuryPresence.modals.initialize(window.luxuryPresence._modalIncludes); 6027 window.luxuryPresence.contactForms.initialize({ selector: '#modals' }); 6028 window.luxuryPresence.validation.initialize({ selector: '#modals' }); 6029 window.luxuryPresence.oauth.initialize({ 6030 googleSignOnEnabled: true, 6031 iframe: false }); 6032 } catch (e) { 6033 console.log(e); 6034 } 6035 document.querySelectorAll('.modals > .modal .close').forEach(closeModalBtn => { 6036 closeModalBtn.addEventListener('click', function() { 6037 window.lpUI.hideClosestModal(closeModalBtn); 6038 }); 6039 }); 6040 }).catch((e) => console.log(e)); 6041 } 6042 /* Initialize Luxury Presence SDK */ 6043 try { 6044 window.luxuryPresence.config.setCompany("64eccd3e-c115-4e58-ba5c-a7d7cd11cf04", {}); 6045 window.luxuryPresence.config.setWebsite("872b2194-0788-46bf-b553-bdf3d29838c6", { 6046 myhomeDashboardEnabled: true, 6047 mlsEnabled: true, 6048 hstValuationRedirectEnabled: true 6049 }); 6050 const url = new URL(window.location.href); 6051 const shareId = url.searchParams.get('shareId'); 6052 window.luxuryPresence.config.setPage("7a028dd0-3c1d-4721-966d-7f4b76ffafb3", { 6053 sourceResource: "", 6054 pageElementId: "", 6055 pageQueryVariables: {}, 6056 shareId: "", 6057 }); 6058 window.luxuryPresence.config.setWebsiteApiGatewayUrl("https://wgw.luxurypresence.com", {}); 6059 window.luxuryPresence.config.setBuyerSellerServiceUrl("https://bss.luxurypresence.com", {}); 6060 window.luxuryPresence.config.setDivolteServiceUrl("https://t.luxurypresence.com", {}); 6061 window.luxuryPresence.config.setEventBusUrl("https://cdn.luxurypresence.com/scripts/event-bus-js", {}); 6062 window.luxuryPresence.config.setEventBus({ 6063 enabled: true, 6064 apiKey: "phc_PMYT31xlmXBaTCwpIRx1v6RrpLVZIuP47N3mOT6KLnr", 6065 url: "https://p.luxurypresence.com", 6066 }); 6067 window.luxuryPresence.config.setTemplateId("34ae9410-e2da-4ccb-947e-bdaa3b48f731", {}); 6068 window.luxuryPresence.config.setLeadIngesterUrl("https://leads.luxurypresence.com", {}); 6069 window.luxuryPresence.buttons.initialize({}); 6070 window.luxuryPresence.eventBus.initialize({}); 6071 window.luxuryPresence.contactForms.initialize({}); 6072 window.luxuryPresence.validation.initialize({}); 6073 window.luxuryPresence.expandSliders.initialize({}); 6074 window.luxuryPresence.oauth.initialize({ googleSignOnEnabled: true, iframe: false }); 6075 window.luxuryPresence.qualifyingQuestionsModal.initialize({ qualifyingQuestionsModalEnabled: true }); 6076 window.luxuryPresence.divolte.initialize({}); 6077 } catch (e) { 6078 console.log(e); 6079 } 6080 6081 /* Inject Section Specific Javascript */ 6082 try { 6083(function init(sectionDomId) { 6084 function setHttp(link) { 6085 if (link.search(/^http[s]?\:\/\/|\//) == -1) { 6086 link = 'https://' + link; 6087 } 6088 return link; 6089 } 6090 const socialLinks = document.querySelectorAll('#' + sectionDomId + ' .socials__item, #' + sectionDomId + ' .lp-socials__link'); 6091 socialLinks.forEach(linkElement => { 6092 const href = linkElement.getAttribute('href'); 6093 if (href) { 6094 linkElement.setAttribute('href', setHttp(href)); 6095 } 6096 }); 6097 $("#" + sectionDomId + " form").on("reset", function () { 6098 const redirectLink = $('#' + sectionDomId + ' .redirect-link'); 6099 if (redirectLink.length) { 6100 const rawHref = redirectLink.attr('href'); 6101 redirectLink.attr('href', setHttp(rawHref)); 6102 redirectLink[0].click(); 6103 } 6104 }); 6105})('modal-global-contact-us'); 6106} catch(e) { 6107console.log(e); 6108} 6109 6110 try { 6111(function init(sectionDomId) { 6112 var resizeMenu = function () { 6113 var widthSoFar = 0; 6114 var availableWidth = $('#' + sectionDomId + ' .navigation').width(); 6115 if ($(window).width() > 1024) { 6116 $('#' + sectionDomId + ' .navigation > li').each(function () { 6117 $(this).hide(); 6118 const currentItemWidth = $(this).outerWidth(); 6119 if (availableWidth - widthSoFar >= currentItemWidth) $(this).css({ 6120 "visibility": "visible", 6121 "display": "list-item", 6122 "height": "auto" 6123 }); 6124 widthSoFar += $(this).outerWidth(); 6125 }); 6126 } 6127 }; 6128 $(window).on('resize.lpui-auto-clean', resizeMenu); 6129 const mediaQuery = window.matchMedia('(hover: hover) and (pointer: fine)'); 6130 var $subNavTrigger = $('body > nav .sub-nav-container a'); 6131 var $subNavContainer = $('body > nav .sub-nav-container'); 6132 if (mediaQuery.matches) { 6133 $subNavTrigger.off('click'); 6134 $subNavContainer.mouseout(function () { 6135 var self = $(this); 6136 self.removeClass('active').find('.sub-nav').removeClass('visible'); 6137 }).mouseover(function () { 6138 $(this).siblings('.sub-nav-container').removeClass('active').find('.sub-nav').removeClass('visible'); 6139 $(this).addClass('active').find('.sub-nav').addClass('visible'); 6140 }).mouseup(function () { 6141 var self = $(this); 6142 self.toggleClass('active'); 6143 }); 6144 } 6145 function isSectionLight(sect) { 6146 if (!sect || !sect.style) { 6147 return true; 6148 } 6149 if (sect.style.backgroundImage) { 6150 return false;
6151 } 6152 if ($(sect).children('.video-wrapper').length) { 6153 return false; 6154 } 6155 return sect.classList.contains('is-background-color-light'); 6156 } 6157 const $header = $(`#${sectionDomId} > .header`); 6158 if ($header.hasClass('theme-auto')) { 6159 if (isSectionLight($('body > section')[0])) { 6160 $header.removeClass('theme-light'); 6161 } else { 6162 $header.addClass('theme-light'); 6163 } 6164 } 6165 var resizeTimeout; 6166 clearTimeout(resizeTimeout); 6167 resizeTimeout = setTimeout(function () { 6168 resizeMenu(); 6169 }, 1000); 6170})('global-navbar'); 6171} catch(e) { 6172console.log(e); 6173} 6174 try { 6175(function init(sectionDomId, options) { 6176 var elementSelector = options.elementSelector || 'nav'; 6177 var element = document.querySelector(elementSelector); 6178 var sections = document.querySelectorAll('body > section'); 6179 var subNavContainers = element.querySelectorAll('.sub-nav'); 6180 var initialBackgroundColor = options.initialBackgroundColor || element.style.backgroundColor; 6181 var backgroundColorScroll = options.backgroundColorScroll || '#191919'; 6182 var fontColor = options.fontColor || '#fff'; 6183 var fontColorScroll = options.fontColorScroll || '#000'; 6184 var didScroll = false; 6185 var lastScrollTop = 0; 6186 var delta = 5; 6187 6188 // eslint-disable-next-line sonarjs/no-collapsible-if 6189 if (wow) { 6190 // Wowjs might not trigger for position fixed elements 6191 if (window.scrollY > 0) { 6192 element.querySelectorAll('.wow').forEach(el => wow.show(el)); 6193 } 6194 } 6195 function adjustNavPinState() { 6196 // Remove 'visible' class from all subNavContainers 6197 for (let i = 0; i < subNavContainers.length; i++) { 6198 subNavContainers[i].classList.remove('visible'); 6199 } 6200 if (window.scrollY > 0) { 6201 element.style.backgroundColor = backgroundColorScroll; 6202 element.style.color = fontColorScroll; 6203 element.classList.add('scroll'); 6204 } else { 6205 element.style.backgroundColor = initialBackgroundColor; 6206 element.style.color = fontColor; 6207 element.classList.remove('scroll'); 6208 } 6209 } 6210 adjustNavPinState(); 6211 function onScroll() { 6212 didScroll = true; 6213 } 6214 window.addEventListener('scroll', onScroll); 6215 6216 // eslint-disable-next-line 6217 var debounceInterval = setInterval(() => { 6218 if (didScroll) { 6219 // eslint-disable-next-line 6220 hasScrolled(); 6221 didScroll = false; 6222 } 6223 }, 250); 6224 function hasScrolled() { 6225 adjustNavPinState(); 6226 // eslint-disable-next-line 6227 var navbarHeight = element.offsetHeight; 6228 // eslint-disable-next-line 6229 var st = window.scrollY; 6230 6231 // Make sure they scroll more than delta 6232 if (Math.abs(lastScrollTop - st) <= delta) { 6233 return; 6234 } 6235 if (st > lastScrollTop && st > navbarHeight) { 6236 // Scroll Down 6237 element.style.transform = `translateY(-${navbarHeight}px) scaleY(0)`; 6238 document.documentElement.style.setProperty('--global-header-height', '0px'); 6239 6240 // Some sections "follow" the navbar with class sticky (i.e. Producer search) 6241 sections.forEach(section => { 6242 if (section.classList.contains('sticky')) { 6243 section.style.top = '0px'; 6244 } 6245 }); 6246 } else { 6247 // Scroll Up 6248 // eslint-disable-next-line 6249 if (st + window.innerHeight < document.documentElement.scrollHeight) { 6250 element.style.transform = `translateY(0) scaleY(1)`; 6251 document.documentElement.style.setProperty('--global-header-height', `${navbarHeight}px`); 6252 sections.forEach(section => { 6253 if (section.classList.contains('sticky')) { 6254 section.style.top = `${navbarHeight}px`; 6255 } 6256 }); 6257 } 6258 } 6259 lastScrollTop = st; 6260 } 6261})('NAVBAR', JSON.parse("{\"fontColor\":\"#211F1F\",\"elementSelector\":\"nav\",\"fontColorScroll\":\"#ffffff\",\"backgroundColorScroll\":\"#2C3426\",\"initialBackgroundColor\":\"transparent\"}")); 6262} catch(e) { 6263console.log(e); 6264} 6265 try { 6266(function init(sectionDomId) { 6267 sectionDomId = "global-sidemenu"; 6268 var $sidemenu = $('#' + sectionDomId); 6269 const subNavItems = $('body > div.sidemenu .sub-nav-container .sub-nav__item'); 6270 const hideSubNavItems = () => { 6271 subNavItems.removeClass('fadeInUp'); 6272 }; 6273 const showSubNavItems = subnav => { 6274 if ($(subnav).find('.arrow').hasClass('active')) { 6275 $(".sidemenu").find('.arrow').removeClass('active'); 6276 } else { 6277 $(".sidemenu").find('.arrow').removeClass('active'); 6278 $(subnav).find('.arrow').addClass('active'); 6279 } 6280 let i = 0; 6281 subnav.find('.sub-nav__item').each((idx, item) => { 6282 i++; 6283 var timeout; 6284 clearTimeout(timeout); 6285 timeout = setTimeout(() => { 6286 $(item).addClass('fadeInUp'); 6287 }, 100 + i * 50); 6288 }); 6289 }; 6290 $(document).on("click", function (e) {
6291 if ($(e.target).closest(".navigation").length < 1) { 6292 hideSubNavItems(); 6293 } 6294 }); 6295 $('body > div.sidemenu .sub-nav-container > a').click(function (e) { 6296 const subnavContainer = $(this).closest('.sub-nav-container'); 6297 hideSubNavItems(); 6298 showSubNavItems(subnavContainer); 6299 }); 6300 const hideSideMenu = () => { 6301 $('body > div.sidemenu').removeClass('visible'); 6302 }; 6303 $('body > div.sidemenu .navigation .navigation__item:not(.sub-nav-container) .navigation__link').on('click', function (e) { 6304 onHide(); 6305 hideSideMenu(); 6306 }); 6307 $('body > div.sidemenu .sub-nav-container .sub-nav__link').on('click', function (e) { 6308 onHide(); 6309 setTimeout(() => { 6310 hideSideMenu(); 6311 }, 1601); 6312 }); 6313 var shown = false; 6314 function onShow() { 6315 if (shown) { 6316 return; 6317 } 6318 shown = true; 6319 window.lpUI.requestScrollLock(sectionDomId); 6320 $('#' + sectionDomId + ' .navigation__link').removeClass('fade-in'); 6321 $('#' + sectionDomId + ' .lp-socials__link').removeClass('fade-in'); 6322 let i = 0; 6323 $('#' + sectionDomId + ' .navigation__link').each(function () { 6324 i++; 6325 setTimeout(() => { 6326 $(this).addClass('fade-in'); 6327 }, 900 + i * 50); 6328 }); 6329 $('#' + sectionDomId + ' .lp-socials__link').each(function () { 6330 i++; 6331 setTimeout(() => { 6332 $(this).addClass('fade-in'); 6333 }, 900 + i * 50); 6334 }); 6335 } 6336 function onHide() { 6337 hideSubNavItems(); 6338 if (!shown) { 6339 return; 6340 } 6341 $('#' + sectionDomId + ' .lp-socials__link').removeClass('fade-in'); 6342 let i = 0; 6343 $($('#' + sectionDomId + ' .navigation__link').get().reverse()).each(function () { 6344 i++; 6345 setTimeout(() => { 6346 $(this).removeClass('fade-in'); 6347 }, i * 50); 6348 }); 6349 shown = false; 6350 setTimeout(() => { 6351 window.lpUI.releaseScrollLock(sectionDomId); 6352 }, 1600); 6353 } 6354 $sidemenu.on('lpui-show', onShow); 6355 $sidemenu.on('lpui-hide', onHide); 6356})('global-sidemenu'); 6357} catch(e) { 6358console.log(e); 6359} 6360 6361 try { 6362(function init(sectionDomId) { 6363 function setHttp(link) { 6364 if (link.search(/^http[s]?\:\/\/|\//) == -1) { 6365 link = 'http://' + link; 6366 } 6367 return link; 6368 } 6369 $(`#${sectionDomId} form`).on("reset", function () { 6370 const redirectLink = $('#' + sectionDomId + ' .redirect-link'); 6371 if (redirectLink.length) { 6372 const rawHref = redirectLink.attr('href'); 6373 redirectLink.attr('href', setHttp(rawHref)); 6374 redirectLink[0].click(); 6375 } 6376 }); 6377})('global-footer'); 6378} catch(e) { 6379console.log(e); 6380} 6381 6382 try { 6383(function init(sectionDomId) { 6384 var _section = $('#' + sectionDomId); 6385 var _footer = document.getElementById('global-footer'); 6386 const emailInput = _section.find('.js-email-input'); 6387 const emailRegex = /[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~\-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)+/; 6388 emailInput.on('input', function () { 6389 if (emailRegex.test(this.value)) { 6390 this.setCustomValidity(''); 6391 } else { 6392 this.setCustomValidity('Please enter a valid email address.'); 6393 } 6394 this.reportValidity(); 6395 }); 6396 _section.find('input[name="name"]').on("keyup", function () { 6397 if (this.value.replaceAll(/\d/g, "").trim().split(" ").length < 2 || this.value.replaceAll(/\d/g, "").trim() === "") { 6398 this.setCustomValidity("Please input your first name and last name."); 6399 } else { 6400 this.setCustomValidity(""); 6401 } 6402 }); 6403 _section.find('form').on("reset", function () { 6404 $(this).addClass("show-success"); 6405 }); 6406 $(window).scroll(function () { 6407 if ($(this).scrollTop() >= 150) _section.find('.btn-holder').addClass('visible');else _section.find('.btn-holder').removeClass('visible'); 6408 }); 6409 _section.find('.close-btn, .connect-btn').click(function () { 6410 _section.find('.pop-up').toggle(); 6411 _section.find('.btn-holder').toggle(); 6412 }); 6413 if (!_footer) return; 6414 const observer = new IntersectionObserver(entries => { 6415 entries.forEach(entry => { 6416 if (entry.isIntersecting) _section.hide();else _section.show(); 6417 }); 6418 }, { 6419 threshold: 0.1 6420 }); 6421 observer.observe(_footer); 6422 function selectToDropdown(selectItem) {
6423 let customDropdown = ` 6424 <div class="custom-select" role="presentation"> 6425 <button class="custom-select-toggle is-placeholder" type="button" aria-haspopup="true" aria-expanded="false"></button> 6426 <div class="custom-select-menu" style="display: none;max-height: none;"> 6427 <div class="custom-select-content"> 6428 </div> 6429 </div> 6430 </div>`; 6431 $(customDropdown).insertAfter(selectItem); 6432 let $dropdown = $($(selectItem).next()); 6433 let $dropdownToggle = $dropdown.find(".custom-select-toggle"); 6434 let $dropdownMenu = $dropdown.find(".custom-select-menu"); 6435 let $dropdownContent = $dropdown.find(".custom-select-content"); 6436 $dropdownToggle.addClass($(selectItem).attr("class")).removeClass("dropdown-select"); 6437 $dropdownToggle.attr("id", $(selectItem).attr("id") + "custom"); 6438 $dropdownMenu.attr("aria-labelledby", $dropdownToggle.attr("id")); 6439 appendOptionButtons(selectItem, $dropdownContent); 6440 $(selectItem).removeClass().addClass("sr-only"); 6441 var toggleText = $(selectItem).find(":selected").first().text() || $(selectItem).children().first().text(); 6442 $dropdownToggle.html(toggleText); 6443 $dropdown.on("click", ".custom-select-item", function (e) { 6444 if ($(this).hasClass("is-disabled")) return; 6445 let elementNumber = $(this).index(); 6446 let $originalSelect = $(this).closest(".custom-select").siblings("select"); 6447 let $linkedOption = $originalSelect.find("option").eq(elementNumber); 6448 if (!$linkedOption.prop("selected")) { 6449 $linkedOption.prop("selected", true); 6450 $originalSelect.change(); 6451 $(this).addClass("is-selected").siblings(".custom-select-item").removeClass("is-selected"); 6452 $dropdownToggle.html($(this).html()); 6453 closeSelectMenu($(this).closest(".custom-select-menu")); 6454 } 6455 $dropdownToggle.removeClass("is-placeholder"); 6456 }); 6457 function convertOptionToButton(option) { 6458 var optionText = $(option).text(); 6459 var optionValue = $(option).attr("value") || $(option).text(); 6460 var optionIsDisabled = $(option).prop("disabled"); 6461 var outputButton = `<button value="${optionValue}" class="custom-select-item" type="button" ${optionIsDisabled ? "disabled" : ""}>${optionText}</button>`; 6462 return outputButton; 6463 } 6464 function appendOptionButtons(selectItem, appendTarget) { 6465 $(selectItem).find("option").each(function () { 6466 let currentButton = convertOptionToButton(this); 6467 $(appendTarget).append(currentButton); 6468 }); 6469 } 6470 function showSelectMenu(selectMenu) { 6471 var customSelect = $(selectMenu).closest(".custom-select")[0]; 6472 $(customSelect).addClass("open"); 6473 $(selectMenu).slideDown(); 6474 } 6475 function closeSelectMenu(selectMenu) { 6476 var customSelect = $(selectMenu).closest(".custom-select")[0]; 6477 $(customSelect).removeClass("open"); 6478 $(selectMenu).slideUp(); 6479 } 6480 $dropdown.on("click", ".custom-select-toggle", function (e) { 6481 var $customSelect = $(this).closest(".custom-select"); 6482 var selectMenu = $customSelect.find(".custom-select-menu")[0]; 6483 if ($customSelect.hasClass("open")) { 6484 closeSelectMenu(selectMenu); 6485 } else { 6486 showSelectMenu(selectMenu); 6487 } 6488 }); 6489 $(document).on("click", function (e) { 6490 if ($(e.target).closest(".custom-select").length < 1 && $(".custom-select").hasClass("open")) { 6491 $(".custom-select.open").each(function () { 6492 var selectMenu = $(this).find(".custom-select-menu")[0]; 6493 closeSelectMenu(selectMenu); 6494 }); 6495 } 6496 }); 6497 } 6498 $(".pop-up select[name='interest']").each(function () { 6499 selectToDropdown(this); 6500 }); 6501})('global-mobile-contact'); 6502} catch(e) { 6503console.log(e); 6504} 6505 6506 try { 6507(function init(sectionDomId) { 6508 const $section = $('#' + sectionDomId); 6509 const showOnlyActive = $section.find('.opening-with-search')[0].dataset.onlyActive === 'true'; 6510 const showNeighborhoodNames = $section.find('.opening-with-search')[0].dataset.showNeighborhoodNames === 'true'; 6511 const $videoWrap = $section.find('.full-screen-video-component > .video-wrapper');
6512 const $url = $section.find('.opening-with-search').attr('data-video-url'); 6513 let $iframe = $section.find('.full-screen-video-component > .video-wrapper').find('iframe'); 6514 let $video = $section.find('.full-screen-video-component > .video-wrapper').find('video'); 6515 let $mute = true; 6516 if ($url) { 6517 $section.children('.video-wrapper:not(.video-wrapper-extended)').remove(); 6518 if ($iframe) { 6519 let src = $iframe.attr('data-src'); 6520 let youtubeId = ''; 6521 let vimeoId = ''; 6522 if (src && (src.indexOf('youtube.com') !== -1 || src.indexOf('youtu.be') > -1) && src.indexOf('youtube.com/embed') < 0) { 6523 const youtubeRegex = /(?:youtube\.com\/\S*(?:(?:\/e(?:mbed))?\/|watch\?(?:\S*?&?v=))|youtu.be\/)([a-zA-Z0-9_-]{6,11})/; 6524 const youtubeResult = src.match(youtubeRegex); 6525 if (youtubeResult) youtubeId = youtubeResult[1]; 6526 } 6527 if (src && src.indexOf('vimeo.com') !== -1) { 6528 const vimeoRegex = /(?:(www\.)?vimeo.com\/(?:channels\/(?:\w+\/)?|groups\/([^/]*)\/videos\/|)|player.vimeo.com\/video\/)(\d{6,})(?:|\/\?)/; 6529 const vimeoResult = src.match(vimeoRegex); 6530 if (vimeoResult) vimeoId = vimeoResult[3]; 6531 } 6532 if (youtubeId) src = `https://youtube.com/embed/${youtubeId}?autoplay=1&mute=1&loop=1&enablejsapi=1&showinfo=0&controls=0&playlist=${youtubeId}`;else if (vimeoId) src = `https://player.vimeo.com/video/${vimeoId}?autoplay=1&muted=1&loop=1&quality=320p&controls=false`; 6533 $iframe.attr('src', src); 6534 $iframe.on("load", function () { 6535 $iframe.parent().addClass('playing'); 6536 }); 6537 } 6538 } else if ($videoWrap.length) { 6539 const $video = $videoWrap.find('video'); 6540 $video.each(function () { 6541 var v = this; 6542 if (v.readyState > 0) { 6543 $videoWrap.addClass('playing'); 6544 } else { 6545 v.addEventListener('canplay', function () { 6546 $videoWrap.addClass('playing'); 6547 }, { 6548 once: true 6549 }); 6550 } 6551 }); 6552 } 6553 window.luxuryPresence.mapApiLoader.load().then(() => { 6554 var $dots = $section.find(".js-dots"); 6555 var $carousel = $section.find('.js-carousel'); 6556 $carousel.on('init', function () { 6557 $carousel.addClass('visible'); 6558 $carousel.find('.title-item').find('.lp-h1').css({ 6559 "transform": "translateX(-100px)", 6560 "opacity": "0" 6561 }); 6562 $carousel.find('.title-item').first().find('.lp-h1').css({ 6563 'transform': 'translateX(0)', 6564 'opacity': '1' 6565 }); 6566 }); 6567 $carousel.slick({ 6568 dots: false, 6569 arrows: false, 6570 appendDots: $dots, 6571 speed: 2000, 6572 fade: true, 6573 autoplay: true, 6574 pauseOnHover: false, 6575 slidesToShow: 1, 6576 adaptiveHeight: true 6577 }); 6578 $carousel.on('beforeChange', function () { 6579 $carousel.find('.slick-slide').find('.lp-h1').css({ 6580 "transform": "translateX(0)", 6581 "opacity": "1", 6582 "transition": "1s all" 6583 }); 6584 $carousel.find('.slick-slide.slick-current').find('.lp-h1').css({ 6585 'transform': 'translateX(100px)' 6586 }); 6587 }); 6588 $carousel.on('afterChange', function () { 6589 $carousel.find('.slick-slide').not('.slick-current').find('.lp-h1').css({ 6590 'transform': 'translateX(-100px)', 6591 "transition": "0s all", 6592 "opacity": "0" 6593 }); 6594 }); 6595 $(window).on('resize', function () { 6596 $carousel.slick('resize'); 6597 }); 6598 const $searchWrapper = $section.find('.search-wrapper'); 6599 if (!$searchWrapper.length) { 6600 return; 6601 } 6602 const searchMls = $searchWrapper[0].dataset.searchMls === 'true'; 6603 const searchProperties = $searchWrapper[0].dataset.searchProperties === 'true'; 6604 const selectedCountries = $searchWrapper[0].dataset.locations; 6605 let searchAreaBounds = null; 6606 try { 6607 const searchAreaRaw = $searchWrapper[0].dataset.searchArea; 6608 if (searchAreaRaw) { 6609 const searchArea = typeof searchAreaRaw === 'string' ? JSON.parse(searchAreaRaw) : searchAreaRaw; 6610 const bounds = searchArea?.fullData?.geometry?.bounds || searchArea?.fullData?.geometry?.viewport; 6611 if (bounds && typeof bounds.north === 'number' && typeof bounds.south === 'number' && typeof bounds.east === 'number' && typeof bounds.west === 'number') { 6612 searchAreaBounds = {
6613 north: bounds.north, 6614 south: bounds.south, 6615 east: bounds.east, 6616 west: bounds.west 6617 }; 6618 } 6619 } 6620 } catch (e) {} 6621 const searchPlaces = searchMls; 6622 const showNeighborhoods = searchMls; 6623 const searchNearEnabled = searchMls; 6624 const $searchInput = $section.find('.search-input'); 6625 const $searchResults = $section.find('.search-results'); 6626 const $resultsContainer = $section.find('.results-container'); 6627 const $loader = $section.find('.search-in-progress'); 6628 const $predefined = $section.find('.predefined-links'); 6629 const $searchBtn = $section.find('.search-button'); 6630 const $leaseFalseButton = $section.find('.jsLeaseFalse'); 6631 const $leaseTrueButton = $section.find('.jsLeaseTrue'); 6632 const $nextSection = $section.next(); 6633 const fixZIndex = $nextSection.length && parseInt($nextSection.css('z-index')) || 1; 6634 const searchDelay = 300; 6635 const maxPlaces = 5; 6636 const maxMls = 5; 6637 const leaseFilter = $leaseFalseButton.length > 0 && $leaseTrueButton.length > 0; 6638 const service = new google.maps.places.AutocompleteService(); 6639 const geocoder = new google.maps.Geocoder(); 6640 var keyword; 6641 var leasePropertyState = false; 6642 var leasePropertySearched; 6643 var searchActive = false; 6644 var isLoading = false; 6645 function showSearchResults() { 6646 searchActive = true; 6647 updateSearchDisplay(); 6648 } 6649 function hideSearchResults() { 6650 searchActive = false; 6651 updateSearchDisplay(); 6652 } 6653 function showLoading() { 6654 isLoading = true; 6655 updateSearchDisplay(); 6656 } 6657 function hideLoading() { 6658 isLoading = false; 6659 updateSearchDisplay(); 6660 } 6661 function updateSearchDisplay() { 6662 if (leaseFilter) { 6663 if (leasePropertyState) { 6664 $leaseTrueButton.addClass('active'); 6665 $leaseFalseButton.removeClass('active'); 6666 } else { 6667 $leaseFalseButton.addClass('active'); 6668 $leaseTrueButton.removeClass('active'); 6669 } 6670 } 6671 if (!searchActive) { 6672 $searchResults.removeClass('show'); 6673 $searchResults.css('height', '0'); 6674 if (fixZIndex) { 6675 $section.css('z-index', ''); 6676 } 6677 return; 6678 } 6679 var hasResults = !!$resultsContainer.html(); 6680 var hasPredefined = $predefined.find('a:not(.not-processed)').length > 0; 6681 if (hasResults) { 6682 $predefined.addClass('not-visible'); 6683 $loader.addClass('not-visible'); 6684 $resultsContainer.removeClass('not-visible'); 6685 } else if (isLoading) { 6686 $resultsContainer.addClass('not-visible'); 6687 $predefined.addClass('not-visible'); 6688 $loader.removeClass('not-visible'); 6689 } else if (hasPredefined) { 6690 $resultsContainer.addClass('not-visible'); 6691 $loader.addClass('not-visible'); 6692 $predefined.removeClass('not-visible'); 6693 } else { 6694 $resultsContainer.addClass('not-visible'); 6695 $loader.addClass('not-visible'); 6696 $predefined.addClass('not-visible'); 6697 } 6698 var height = 0; 6699 height += $predefined[0].getBoundingClientRect().height; 6700 height += $resultsContainer[0].getBoundingClientRect().height; 6701 height += $loader[0].getBoundingClientRect().height; 6702 if (height > 0) { 6703 if (fixZIndex) { 6704 $section.css('z-index', '' + (fixZIndex + 1)); 6705 } 6706 $searchResults.addClass('show'); 6707 } else { 6708 if (fixZIndex) { 6709 $section.css('z-index', ''); 6710 } 6711 $searchResults.removeClass('show'); 6712 } 6713 $searchResults.css('height', height + 'px'); 6714 } 6715 function clearSelection() { 6716 $searchResults.find('.selected-link').removeClass('selected-link'); 6717 } 6718 function moveSelection(up) { 6719 var $cont = $resultsContainer.html() ? $resultsContainer : $predefined; 6720 var $links = $cont.find('.results-link'); 6721 if (!$links.length) { 6722 return; 6723 } 6724 var idx = -1; 6725 $links.each(function (i) { 6726 if (idx === -1 && $(this).is('.selected-link')) { 6727 idx = i; 6728 } 6729 }); 6730 var newIdx; 6731 if (up) { 6732 newIdx = idx - 1; 6733 if (newIdx < 0) { 6734 newIdx = $links.length - 1; 6735 } 6736 } else { 6737 newIdx = idx + 1; 6738 if (newIdx > $links.length - 1) { 6739 newIdx = 0; 6740 } 6741 } 6742 if (newIdx !== idx) { 6743 $searchResults.find('.selected-link').removeClass('selected-link'); 6744 $($links.get(newIdx)).addClass('selected-link'); 6745 } 6746 } 6747 var newSearchTimer; 6748 function inputChanged(ev) { 6749 if (ev.which === 38) { 6750 ev.preventDefault(); 6751 moveSelection(true); 6752 return false;
6753 } 6754 if (ev.which === 40) { 6755 ev.preventDefault(); 6756 moveSelection(); 6757 return false; 6758 } 6759 if (ev.which === 13) { 6760 $lnk = $searchResults.find('.selected-link'); 6761 if ($lnk.length) { 6762 $lnk[0].click(); 6763 hideSearchResults(); 6764 } else { 6765 $searchBtn.click(); 6766 } 6767 } 6768 window.clearTimeout(newSearchTimer); 6769 newSearchTimer = window.setTimeout(startSearch, searchDelay); 6770 } 6771 function startSearch() { 6772 const term = $searchInput.val(); 6773 if (term === keyword && leasePropertySearched === leasePropertyState) { 6774 return; 6775 } 6776 leasePropertySearched = leasePropertyState; 6777 keyword = term; 6778 if (term) { 6779 doSearch(term, leasePropertySearched); 6780 } else { 6781 setResults(null); 6782 } 6783 } 6784 function mlsSearch(term, leaseProperty) { 6785 if (!leaseFilter) { 6786 if (showOnlyActive) { 6787 const filters = { 6788 'status[]': 'ACTIVE' 6789 }; 6790 return window.luxuryPresence.getMLSAutocompleteWithFilters(term, filters); 6791 } 6792 return window.luxuryPresence.getMLSAutocomplete(term); 6793 } 6794 var query = { 6795 omnibox: term, 6796 limit: maxMls 6797 }; 6798 if (showOnlyActive) query['status[]'] = ['ACTIVE']; 6799 if (leaseFilter && leaseProperty) { 6800 query.lease_property = true; 6801 } 6802 return superagent.get(window.location.protocol + '//' + window.location.host + '/home-search/listings.json').query(query).then(function (res) { 6803 if (!Array.isArray(res.body)) { 6804 return []; 6805 } 6806 return res.body; 6807 }, function (e) { 6808 console.log(e); 6809 return []; 6810 }); 6811 } 6812 function resolveGeoBounds(placeId, description) { 6813 return new Promise(function (resolve, reject) { 6814 geocoder.geocode({ 6815 'placeId': placeId 6816 }, function (results, status) { 6817 if (status != 'OK') { 6818 return resolve(null); 6819 } 6820 var result = results[0]; 6821 var bounds = new google.maps.LatLngBounds();
6822 bounds.extend(result.geometry.viewport.getSouthWest()); 6823 bounds.extend(result.geometry.viewport.getNorthEast()); 6824 result.geometry.bounds = bounds.toJSON(); 6825 var placeUrl = window.luxuryPresence.makeMlsLink(result); 6826 return resolve({ 6827 placeUrl: placeUrl, 6828 description: description 6829 }); 6830 }); 6831 }); 6832 } 6833 function addRestrictions(request) { 6834 if (searchAreaBounds) { 6835 request.locationRestriction = searchAreaBounds; 6836 } 6837 if (selectedCountries === 'Worldwide') { 6838 return request; 6839 } 6840 if (selectedCountries === 'US & Canada') { 6841 request.componentRestrictions = { 6842 country: ['us', 'ca'] 6843 }; 6844 return request; 6845 } 6846 request.componentRestrictions = { 6847 country: 'us' 6848 }; 6849 return request; 6850 } 6851 function resolveAutocomplete(term, deeper) { 6852 var request = addRestrictions({ 6853 input: term, 6854 types: deeper ? ['geocode'] : ['(regions)'], 6855 limit: 1 6856 }); 6857 return new Promise(function (resolve, reject) { 6858 service.getPlacePredictions(request, function (results, status) { 6859 if (status !== google.maps.places.PlacesServiceStatus.OK) { 6860 resolve(null); 6861 return; 6862 } 6863 if (!Array.isArray(results)) { 6864 resolve(null); 6865 return; 6866 } 6867 if (deeper) { 6868 var allowedTypes = { 6869 'route': true, 6870 'neighborhood': true, 6871 'administrative_area_level_3': true, 6872 'administrative_area_level_4': true, 6873 'administrative_area_level_5': true, 6874 'sublocality_level_1': true, 6875 'sublocality_level_2': true, 6876 'sublocality_level_3': true, 6877 "colloquial_area": true 6878 }; 6879 results = results.filter(function (res) { 6880 return res.types.find(t => allowedTypes[t]); 6881 }); 6882 } 6883 if (!results.length) { 6884 resolve(null); 6885 return; 6886 } 6887 resolve(results); 6888 }); 6889 }); 6890 } 6891 var placesResult = {}; 6892 function placesSearch(term, ignoreInput) { 6893 if (placesResult.term !== term) { 6894 placesResult = { 6895 term: term, 6896 result: Promise.all([resolveAutocomplete(term), resolveAutocomplete(term, true)]).then(function (res) { 6897 let results = []; 6898 if (res[0]) results.push(...res[0]); 6899 if (res[1]) results.push(...res[1]); 6900 if (!results) { 6901 return []; 6902 } 6903 const uniqueResults = []; 6904 const placeIds = new Set(); 6905 results.forEach(place => { 6906 if (!placeIds.has(place.place_id)) { 6907 placeIds.add(place.place_id); 6908 uniqueResults.push(place); 6909 } 6910 }); 6911 return uniqueResults; 6912 }).then(function (results) { 6913 if (!ignoreInput && term !== keyword) { 6914 return []; 6915 } 6916 if (!results) { 6917 return []; 6918 } 6919 var geoResultPromises = results.map(function (result) { 6920 return resolveGeoBounds(result.place_id, result.description); 6921 }); 6922 return Promise.all(geoResultPromises).then(function (geoLinks) { 6923 return geoLinks.filter(function (i) { 6924 return i; 6925 }).slice(0, maxPlaces); 6926 }); 6927 }) 6928 }; 6929 } 6930 return placesResult.result; 6931 } 6932 function propertiesSearch(term, leaseProperty) { 6933 var payload = { 6934 companyId: window.luxuryPresence.defaults.companyId, 6935 search: term, 6936 offset: 0, 6937 limit: 6, 6938 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"] 6939 }; 6940 if (leaseFilter) { 6941 payload.leaseProperty = !!leaseProperty; 6942 } 6943 return superagent.post(window.luxuryPresence.defaults.apiGatewayUrl + '/graphql').send({ 6944 query: window.luxuryPresence.defaults.gql.properties, 6945 variables: payload 6946 }).then(function (res) { 6947 return (res.body.data || {}).properties || []; 6948 }, function (err) { 6949 return []; 6950 }); 6951 } 6952 function doSearch(term, leaseProperty) { 6953 showLoading(); 6954 Promise.all([searchMls ? mlsSearch(term, leaseProperty) : [], searchPlaces ? placesSearch(term) : [], searchProperties ? propertiesSearch(term, leaseProperty) : []]).then(function (res) { 6955 if (term !== keyword) { 6956 return; 6957 } 6958 if (res[0].length > 0) { 6959 res[0] = res[0].map(function (item) { 6960 return { 6961 id: item.id, 6962 display_address: [item.address_1, item.city, [item.state, item.postal_code].filter(function (i) { 6963 return i; 6964 }).join(' ')].filter(function (i) { 6965 return i; 6966 }).join(', ') 6967 }; 6968 }); 6969 } 6970 setResults(res[0], res[1], res[2], leaseProperty); 6971 }); 6972 } 6973 function makeResultsBlockHtml(title, links) { 6974 if (!links || !links.length) { 6975 return ''; 6976 } 6977 var html = '<div class="results-block">'; 6978 html += '<div class="results-block-title">'; 6979 if (searchNearEnabled) { 6980 html += '<div class="search-near-container"><a class="search-near lp-link lp-link--light">Search Near Me</a></div>'; 6981 } 6982 html += '<div class="results-title-label">' + title + '</div></div>'; 6983 html += links.join('') + '</div>'; 6984 return html; 6985 } 6986 function setResults(mlsResults, placesResults, propertiesResults, leaseProperty) { 6987 clearSelection(); 6988 hideLoading(); 6989 if (!mlsResults) { 6990 $resultsContainer.html(''); 6991 updateSearchDisplay(); 6992 return; 6993 } 6994 if (!mlsResults.length && !placesResults.length && !propertiesResults.length) { 6995 $resultsContainer.html('<div class="nothing-found">No results found</div>'); 6996 updateSearchDisplay(); 6997 return; 6998 } 6999 var propertiesHtml = makeResultsBlockHtml('Featured Properties', propertiesResults.map(function (result) { 7000 return `<a href="/properties/${result.slug}" class="results-link">${result.privateAddress ? result.name : result.fullAddress}</a>`; 7001 })); 7002 var placesHtml = makeResultsBlockHtml('Locations', placesResults.map(function (result) { 7003 return `<a class="results-link" href="${result.placeUrl}${leaseProperty ? '&lease_property=true' : ''}">${result.description}</a>`; 7004 })); 7005 var mlsHtml = makeResultsBlockHtml('Address', mlsResults.map(function (result) { 7006 return `<a href="/home-search/listings/${result.id}" class="results-link">${result.display_address}</a>`; 7007 }));
7008 $resultsContainer.html(propertiesHtml + placesHtml + mlsHtml); 7009 updateSearchDisplay(); 7010 } 7011 function searchBtnClicked() { 7012 var resultsBlockHref = $("#" + sectionDomId + " .results-container .results-block .results-link").attr("href"); 7013 const term = $searchInput.val(); 7014 if (!term) { 7015 return; 7016 } 7017 if (searchMls) { 7018 if (resultsBlockHref !== undefined) window.location.href = resultsBlockHref; 7019 return; 7020 } 7021 if (searchProperties) { 7022 window.location.href = "/properties/sale"; 7023 } 7024 } 7025 var neighborhoods = null; 7026 function fetchNeighborhoods() { 7027 var url = window.luxuryPresence.defaults.apiGatewayUrl; 7028 var gqlQuery = 'query ($companyId: String, $offset: Int, $limit: Int) {' + 'neighborhoods (companyId: $companyId, offset: $offset, limit: $limit) {id, name, mlsLink, googlePlaceData}}'; 7029 var payload = {}; 7030 payload.offset = 0; 7031 payload.limit = 100; 7032 payload.companyId = window.luxuryPresence.defaults.companyId; 7033 return superagent.post(url + '/graphql').withCredentials().send({ 7034 query: gqlQuery, 7035 variables: payload 7036 }).then(function (res) { 7037 neighborhoods = res.body.data && res.body.data.neighborhoods; 7038 if (neighborhoods && !neighborhoods.length) { 7039 neighborhoods = null; 7040 } 7041 }); 7042 } 7043 function setPredefinedNeighborhoods() { 7044 return fetchNeighborhoods().then(function () { 7045 if (!neighborhoods) { 7046 return; 7047 } 7048 var leaseProperty = leaseFilter && leasePropertyState; 7049 var placesHtml = makeResultsBlockHtml('Locations', neighborhoods.map(function (item) { 7050 return `<a class="results-link" href="${item.mlsLink}${leaseProperty ? `${item.mlsLink.includes('?') ? '&lease_property=true' : '?lease_property=true'}` : ''}">${showNeighborhoodNames ? item.name : item.googlePlaceData && item.googlePlaceData.formatted_address || item.name}</a>`; 7051 })); 7052 $predefined.html(placesHtml); 7053 updateSearchDisplay(); 7054 }); 7055 } 7056 if (showNeighborhoods) { 7057 setPredefinedNeighborhoods(); 7058 } 7059 $searchInput.on('focus', showSearchResults); 7060 $searchInput.on('keyup focus', inputChanged); 7061 $searchResults.on('mouseenter', '.results-link', function (ev) { 7062 clearSelection(); 7063 }); 7064 $searchBtn.on('click', searchBtnClicked); 7065 $leaseFalseButton.on('click', function () { 7066 if (leasePropertyState) { 7067 leasePropertyState = false; 7068 if (showNeighborhoods) { 7069 setPredefinedNeighborhoods(); 7070 } 7071 updateSearchDisplay(); 7072 setResults(null); 7073 startSearch(); 7074 } 7075 }); 7076 $leaseTrueButton.on('click', function () { 7077 if (!leasePropertyState) { 7078 leasePropertyState = true; 7079 if (showNeighborhoods) { 7080 setPredefinedNeighborhoods(); 7081 } 7082 updateSearchDisplay(); 7083 setResults(null); 7084 startSearch(); 7085 } 7086 }); 7087 function createPolygonUrl(center, radius, n) { 7088 var step = Math.PI * 2 / n; 7089 var angle = 0 - step / 2; 7090 var points = []; 7091 var kx = 1.0 / Math.cos(center[1] * Math.PI / 180); 7092 for (var i = 0; i < n; i++) { 7093 points.push('[' + (center[0] + radius * Math.sin(angle) * kx).toFixed(5) + ',' + (center[1] - radius * Math.cos(angle)).toFixed(5) + ']'); 7094 angle += step; 7095 } 7096 points.push(points[0]); 7097 var str = '[[' + points.join(',') + ']]'; 7098 return '&polygon=' + encodeURIComponent(str); 7099 } 7100 function makeNearMeUrl(center, leaseProperty) { 7101 var url = '/home-search/listings?search_type=envelope'; 7102 if (leaseProperty) { 7103 url += '&lease_property=true'; 7104 } 7105 var distance = 0.11; 7106 var lat = center[1]; 7107 var lng = center[0]; 7108 url += '&south=' + (lat - distance); 7109 url += '&north=' + (lat + distance); 7110 url += '&west=' + (lng - distance); 7111 url += '&east=' + (lng + distance); 7112 url += createPolygonUrl(center, distance, 54); 7113 return url; 7114 } 7115 var $locMsg = null; 7116 $section.on('click', '.search-near', function () { 7117 showLoading(); 7118 navigator.geolocation.getCurrentPosition(function (position) { 7119 hideLoading(); 7120 hideSearchResults(); 7121 if (!position || !position.coords || !position.coords.latitude || !position.coords.longitude) { 7122 showLocationMessage('Unknown Location', '<p>Getting position of your device failed.</p><p>
7122You can type your address manually in the search field</p>'); 7123 return; 7124 } 7125 var url = makeNearMeUrl([position.coords.longitude, position.coords.latitude], leaseFilter && leasePropertyState); 7126 var link = document.createElement('a'); 7127 link.href = url; 7128 link.class = 'lp-visually-hidden'; 7129 $section[0].appendChild(link); 7130 link.click(); 7131 setTimeout(function () { 7132 $section[0].removeChild(link); 7133 }, 100); 7134 }, function (geoError) { 7135 hideLoading(); 7136 hideSearchResults(); 7137 var title = 'Unknown Location'; 7138 var msg; 7139 if (geoError.code === 1) { 7140 msg = '<p>To be able to search near you we need to know your current location.</p><p>If you don\'t want to give access to your device\'s location, you can type your address in the search field</p>'; 7141 } else { 7142 msg = '<p>Getting position of your device failed.</p><p>You can type your address manually in the search field</p>'; 7143 } 7144 showLocationMessage(title, msg); 7145 }); 7146 }); 7147 var locHideTimer = null; 7148 function showLocationMessage(title, message) { 7149 clearTimeout(locHideTimer); 7150 if (!$locMsg) { 7151 var html = `<div class="loc-msg-backdrop"> 7152 <div class="loc-msg-wrap"> 7153 <div class="loc-msg-modal"> 7154 <div class="loc-msg-close"></div> 7155 <h4 class="loc-msg-title lp-h4"></h4> 7156 <div class="loc-msg-container"></div> 7157 </div> 7158 </div> 7159 </div> 7160 `; 7161 $locMsg = $(html); 7162 $locMsg.on('click', hideLocationMessage); 7163 $section.append($locMsg); 7164 } 7165 $locMsg.find('.loc-msg-container').html(message); 7166 $locMsg.find('.loc-msg-title').html(title); 7167 setTimeout(function () { 7168 if ($locMsg) { 7169 $section.addClass('visible-loc-msg'); 7170 } 7171 }); 7172 } 7173 function hideLocationMessage() { 7174 $section.removeClass('visible-loc-msg'); 7175 locHideTimer = setTimeout(function () { 7176 $locMsg.remove(); 7177 $locMsg = null; 7178 }, 400); 7179 } 7180 function outsideClickHandler(e) { 7181 if ($searchWrapper.find($(e.target)).length > 0) { 7182 return; 7183 } 7184 hideSearchResults(); 7185 } 7186 $(document).on('click', outsideClickHandler); 7187 if ('geolocation' in navigator) { 7188 $searchWrapper.removeClass('search-near-hidden'); 7189 } 7190 updateSearchDisplay(); 7191 }); 7192})('section-9c8b971e-fc4a-4d61-8e47-06eb416e4be9', 'undefined'); 7193} catch(e) { 7194console.log(e); 7195} 7196 try { 7197(function init(sectionDomId, options) { 7198 let section = null; 7199 switch (sectionDomId) { 7200 case 'NAVBAR': 7201 section = document.querySelector('body > nav, #global-navbar'); 7202 break; 7203 case 'FOOTER': 7204 section = document.querySelector('body > footer, #global-footer'); 7205 break; 7206 case 'SIDEMENU': 7207 section = document.querySelector('body > div.sidemenu, #global-sidemenu'); 7208 break; 7209 case 'CONTACT': 7210 section = document.querySelector('#modal-global-contact-us'); 7211 break; 7212 case 'MOBILE_CONTACT': 7213 section = document.querySelector('body > div.mobile-contact-wrapper, #global-mobile-contact'); 7214 break; 7215 case 'SUBSCRIBE': 7216 // Subscribe Not Implemented 7217 section = null; 7218 break; 7219 case 'ACCOUNT': 7220 // Account Not Implemented 7221 section = null; 7222 break; 7223 default: 7224 section = document.querySelector('#' + sectionDomId); 7225 } 7226 if (!section) { 7227 return; 7228 } 7229 7230 // Where content / items are rendered 7231 const dataContainer = section.querySelector('.data-container'); 7232 if (!dataContainer) { 7233 // no place to render items = no need to query server or do anything else 7234 return; 7235 } 7236 let variables = options.variables || {}; 7237 7238 // Some variables in options may not have been injected - remove 7239 Object.keys(variables).forEach(function (variableName) { 7240 if (typeof variables[variableName] === 'string' && variables[variableName].match(/^{{.*}}$/)) { 7241 delete variables[variableName]; 7242 } 7243 }); 7244 7245 // Merge page query variables 7246 if (options.usePageResource) { 7247 variables = { 7248 ...variables, 7249 ...window.luxuryPresence.config.pageMeta.pageQueryVariables[options.resource] 7250 }; 7251 } 7252 7253 // Pagination Executes on a Numbered Pagination Container 7254 // Or via a load more button 7255 let mode = 'BUTTONLESS_PAGINATION'; 7256 const container = section.querySelector('.pagination-container');
7257 const loadMoreButton = section.querySelector(options?.loadMoreSelector); 7258 if (container) { 7259 mode = 'NUMBERED_PAGINATION'; 7260 } 7261 if (loadMoreButton) { 7262 mode = 'LOAD_MORE_BUTTON'; 7263 } 7264 let searchContainer = section.querySelector('.search-container'); 7265 const hidePaginationOnFirstPage = options.hidePaginationOnFirstPage || false; 7266 const eventPrefix = '__pagination-'; 7267 const searchFormParams = options.searchFormParams || (options.resource === 'properties' ? [{ 7268 param: 'neighborhoodId', 7269 urlKey: 'neighborhood' 7270 }, { 7271 param: 'search', 7272 urlKey: 'search' 7273 }, { 7274 param: 'salesPriceGTE', 7275 urlKey: 'pricemin', 7276 formKey: 'priceMin', 7277 type: 'int' 7278 }, { 7279 param: 'salesPriceLTE', 7280 urlKey: 'pricemax', 7281 formKey: 'priceMax', 7282 type: 'int' 7283 }, { 7284 param: 'livingSpaceSizeGTE', 7285 urlKey: 'areamin', 7286 formKey: 'areaMin', 7287 type: 'int' 7288 }, { 7289 param: 'livingSpaceSizeLTE', 7290 urlKey: 'areamax', 7291 formKey: 'areaMax', 7292 type: 'int' 7293 }] : []); 7294 7295 // Total entries - initialized in first render 7296 let totalNumber = 0; 7297 const DefaultSearchParams = { 7298 page: 1 7299 }; 7300 searchFormParams.forEach(function (formParam) { 7301 DefaultSearchParams[formParam.formKey || formParam.param] = null; 7302 }); 7303 let searchParams = { 7304 ...DefaultSearchParams 7305 }; 7306 const offset = options.variables?.offset || 0; 7307 let scrollTarget = options.scrollTargetSelector ? section.querySelector(options.scrollTargetSelector) || section : section; 7308 let scrollOffset = (parseInt(options.scrollOffset, 10) || 0) ?? 100; 7309 7310 // entries of per page 7311 // when query variables for a given section would't be filled options.pageSize can store 7312 // template string, ex: "{{variables.limit}}", below code handles that case and set 9 as a default value 7313 let pageSize = parseInt(options.pageSize, 10) || 9; 7314 7315 // Adding mobile pageRange support 7316 var NARROW_BREAKPOINT = 600; 7317 var mobileBreakpoint = options.mobileBreakpoint > 0 ? options.mobileBreakpoint : NARROW_BREAKPOINT; 7318 var paginationMql = window.matchMedia(`(max-width: ${mobileBreakpoint}px)`); 7319 7320 // Page range (pages on both sides of the current page) 7321 var mobilePageRange = options.mobilePageRange > 0 ? options.mobilePageRange : 0; 7322 var desktopPageRange = options.pageRange >= 0 ? options.pageRange : 2; 7323 var pageRange = paginationMql.matches ? mobilePageRange : desktopPageRange; 7324 7325 // Whether to display the 'Previous' button 7326 var showPrevious = true; 7327 7328 // Whether to display the 'Next' button 7329 var showNext = true; 7330 7331 // Whether to display the page buttons 7332 var showPageNumbers = true; 7333 7334 // 'Previous' text 7335 var prevText = options.prevText || '«'; 7336 7337 // 'Next' text 7338 var nextText = options.nextText || '»'; 7339 7340 // Ellipsis text 7341 var ellipsisText = '…'; 7342 var classPrefix = 'paginationjs'; 7343 7344 // Default active class 7345 var activeClassName = 'active'; 7346 7347 // Default disable class 7348 var disableClassName = 'disabled'; 7349 7350 // Pagination element's position in the container 7351 var position = 'bottom'; 7352 7353 // Whether to trigger pagination at initialization 7354 var triggerPagingOnInit = !options.skipInitialLoad; 7355 var showFirstOnEllipsisShow = true; 7356 var showLastOnEllipsisShow = true; 7357 7358 // Instantiated in init() 7359 var el = null; 7360 7361 // Controls scrollTop logic 7362 var firstRender = true; 7363 var renderBySearch = false; 7364 var disabled = false; 7365 7366 // Pagination script class selector 7367 var paginationScriptSelector = options.paginationPartialName ? `.${options.paginationPartialName}` : '.pagination-script'; 7368 7369 // Property Specific Image Override if No Image 7370 var propertyPlaceholderImage = window.luxuryPresence.defaults.propertyPlaceholderImage; 7371 7372 // Construct template 7373 var templateHTML = section.querySelector(`script${paginationScriptSelector}`)?.innerHTML || ''; 7374 var template = Handlebars.compile(templateHTML); 7375 7376 // Initialize 7377 init(); 7378 function init() { 7379 Object.assign(searchParams, window.luxuryPresence.pagination.deserializeParams(searchFormParams, sectionDomId)); 7380 7381 // Append/prepend pagination element to the pagination container 7382 if (mode === 'NUMBERED_PAGINATION') { 7383 el = document.createElement('div'); 7384 el.className = 'paginationjs';
7385 container[position === 'bottom' ? 'append' : 'prepend'](el); 7386 } 7387 if (searchContainer) { 7388 initSearchContainer({ 7389 fetchNeighborhoods: searchContainer.dataset.fetchNeighborhoods === 'true', 7390 fetchPostCategories: searchContainer.dataset.fetchPostCategories === 'true', 7391 fetchOffices: searchContainer.dataset.fetchOffices === 'true', 7392 fetchRentalRates: searchContainer.dataset.fetchRentalRates === 'true' 7393 }, function () { 7394 syncSearchFormState(searchParams); 7395 }); 7396 } 7397 7398 // Bind events 7399 observer(); 7400 } 7401 function syncSearchFormState(params) { 7402 searchContainer.dispatchEvent(new CustomEvent('sync', { 7403 detail: params 7404 })); 7405 } 7406 function initSearchContainer(options, done) { 7407 var promises = []; 7408 if (options.fetchNeighborhoods) { 7409 promises.push(fetchAllNeighborhoods(function (neighborhoods) { 7410 searchContainer.dispatchEvent(new CustomEvent('neighborhoods-data', { 7411 detail: { 7412 data: neighborhoods.map(function (item) { 7413 return { 7414 value: item.id, 7415 label: item.name 7416 }; 7417 }) 7418 } 7419 })); 7420 })); 7421 } 7422 if (options.fetchPostCategories) { 7423 promises.push(fetchPostCategories(function (categories) { 7424 searchContainer.dispatchEvent(new CustomEvent('post-categories-data', { 7425 detail: { 7426 data: categories 7427 } 7428 })); 7429 })); 7430 } 7431 if (options.fetchOffices) { 7432 promises.push(fetchOffices(function (offices) { 7433 searchContainer.dispatchEvent(new CustomEvent('offices-data', { 7434 detail: { 7435 data: offices 7436 } 7437 })); 7438 })); 7439 } 7440 if (options.fetchRentalRates) { 7441 promises.push(fetchAllRentalRates(function (rentalRates) { 7442 searchContainer.dispatchEvent(new CustomEvent('rental-rates-data', { 7443 detail: { 7444 data: rentalRates 7445 } 7446 })); 7447 })); 7448 } 7449 if (promises.length > 0) { 7450 Promise.all(promises).then(function () { 7451 done(); 7452 }); 7453 } else { 7454 // element's custom code is not executed yet, so we're scheduling callback to after element's code 7455 setTimeout(done); 7456 } 7457 } 7458 function fetchAllNeighborhoods(done) { 7459 var url = options.url || window.luxuryPresence.defaults.apiGatewayUrl; 7460 var gqlQuery = 'query ($companyId: String, $withProperties: NeighborhoodPropertiesFilter, $withBlogPosts: NeighborhoodBlogPostsFilter, $offset: Int, $limit: Int) {' + 'neighborhoods (companyId: $companyId, withProperties: $withProperties, withBlogPosts: $withBlogPosts, offset: $offset, limit: $limit) {id, name}}'; 7461 var payload = {}; 7462 payload.offset = 0; 7463 payload.limit = 100; 7464 payload.companyId = variables.companyId; 7465 if (options.resource === 'properties') { 7466 payload.withProperties = { 7467 active: true 7468 }; 7469 ['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) { 7470 if (variables[field] !== undefined && variables[field] !== null) { 7471 payload.withProperties[field] = variables[field]; 7472 } 7473 }); 7474 } 7475 if (options.resource === 'posts') { 7476 payload.withBlogPosts = { 7477 active: true 7478 }; 7479 ['search', 'postStatusId', 'featured', 'categoryId', 'categoryIds', 'neighborhoodId', 'slug'].forEach(function (field) { 7480 if (variables[field] !== undefined && variables[field] !== null) { 7481 payload.withBlogPosts[field] = variables[field]; 7482 } 7483 }); 7484 } 7485 superagent.post(url + '/graphql').withCredentials().send({ 7486 query: gqlQuery, 7487 variables: payload 7488 }).then(function (res) { 7489 var items = res.body.data && res.body.data.neighborhoods || []; 7490 done(items, items.length); 7491 }); 7492 } 7493 function fetchPostCategories(done) { 7494 var url = options.url || window.luxuryPresence.defaults.apiGatewayUrl; 7495 var featured = options.resource === 'posts' && !!variables.featured; 7496 var gqlQuery = 'query ($companyId: String, $offset: Int, $limit: Int) {' + 'postCategories (companyId: $companyId, offset: $offset, limit: $limit ' + (featured ? ' withFeaturedPublishedOnly: true' : ' withPublishedOnly: true') + ') {id, name, publishedCount, featuredPublishedCount}}'; 7497 var payload = {}; 7498 payload.offset = 0; 7499 payload.limit = 100; 7500 payload.companyId = variables.companyId; 7501 superagent.post(url + '/graphql').withCredentials().send({ 7502 query: gqlQuery, 7503 variables: payload 7504 }).then(function (res) { 7505 var items = res.body.data && res.body.data.postCategories || []; 7506 done(items.map(function (item) { 7507 item.count = featured ? item.featuredPublishedCount : item.publishedCount; 7508 return item; 7509 }), items.length); 7510 }); 7511 } 7512 function fetchOffices(done) { 7513 var url = options.url || window.luxuryPresence.defaults.apiGatewayUrl; 7514 var gqlQuery = 'query ($companyId: String, $offset: Int, $limit: Int) {' + 'offices (companyId: $companyId, offset: $offset, limit: $limit) {' + 'id, name, tags}}'; 7515 var payload = { 7516 offset: 0, 7517 limit: 100, 7518 companyId: variables.companyId 7519 }; 7520 superagent.post(url + '/graphql').withCredentials().send({ 7521 query: gqlQuery, 7522 variables: payload 7523 }).then(function (res) { 7524 var items = res.body.data && res.body.data.offices || []; 7525 done(items, items.length); 7526 }); 7527 } 7528 function fetchAllRentalRates(done) { 7529 var url = options.url || window.luxuryPresence.defaults.apiGatewayUrl; 7530 var gqlQuery = 'query ($companyId: String, $offset: Int, $limit: Int) {' + 'rentalRates (companyId: $companyId, offset: $offset, limit: $limit) {rentalRateId, label}}'; 7531 var payload = { 7532 offset: 0, 7533 limit: 100, 7534 companyId: variables.companyId 7535 }; 7536 superagent.post(url + '/graphql').withCredentials().send({ 7537 query: gqlQuery, 7538 variables: payload 7539 }).then(function (res) { 7540 var items = res.body.data && res.body.data.rentalRates || []; 7541 done(items, items.length); 7542 }).catch(function (err) { 7543 console.error('Error fetching rental rates:', err); 7544 done([], 0); 7545 }); 7546 } 7547 function fetchData(params, done) { 7548 var url = options.url || window.luxuryPresence.defaults.apiGatewayUrl; 7549 var resource = options.resource; 7550 var gqlQuery = options.query || window.luxuryPresence.defaults.gql[resource]; 7551 var payload = {}; 7552 try { 7553 payload = JSON.parse(JSON.stringify(variables)); 7554 } catch (e) {
7555 Object.keys(variables).forEach(function (variableName) { 7556 payload[variableName] = variables[variableName]; 7557 }); 7558 } 7559 payload.offset = (params.page - 1) * pageSize + offset; 7560 payload.limit = pageSize; 7561 payload.companyId = options.companyId; 7562 searchFormParams.forEach(function (formParam) { 7563 var val = params[formParam.formKey || formParam.param]; 7564 if (val !== null && val !== undefined) { 7565 objSet(payload, formParam.param, val); 7566 } 7567 }); 7568 7569 // TODO: Disabled loading due to jarring effect 7570 // dataContainer.html('Loading...'); 7571 superagent.post(url + '/graphql').withCredentials().send({ 7572 query: gqlQuery, 7573 variables: payload 7574 }).then(function (res) { 7575 if (res.body.errors) { 7576 done(res.body.errors); 7577 return; 7578 } 7579 var data = res.body.data || {}; 7580 var resources = data[resource] || []; 7581 var resourceCount = data[resource + 'Count'] && data[resource + 'Count'].count ? data[resource + 'Count'].count : 0; 7582 if (resource === 'properties' && propertyPlaceholderImage?.id) { 7583 resources = resources.map(resource => { 7584 if (resource.media?.length === 0) { 7585 return Object.assign(resource, { 7586 media: [propertyPlaceholderImage] 7587 }); 7588 } 7589 return resource; 7590 }); 7591 } 7592 done(null, resources, resourceCount); 7593 }); 7594 } 7595 function getTotalPage() { 7596 return Math.ceil(totalNumber / pageSize); 7597 } 7598 function renderPaginationList() { 7599 var totalPages = getTotalPage(); 7600 var rangeStart = searchParams.page - pageRange; 7601 var rangeEnd = searchParams.page + pageRange; 7602 if (rangeEnd > totalPages) { 7603 rangeEnd = totalPages; 7604 rangeStart = totalPages - pageRange * 2; 7605 rangeStart = rangeStart < 1 ? 1 : rangeStart; 7606 } 7607 if (rangeStart <= 1) { 7608 rangeStart = 1; 7609 rangeEnd = Math.min(pageRange * 2 + 1, totalPages); 7610 } 7611 7612 // Making sure that 1 more page button is shown in edge positions 7613 if (pageRange === 0) { 7614 if (searchParams.page === totalPages) { 7615 rangeStart = totalPages <= 1 ? 1 : totalPages - 1; 7616 } 7617 if (searchParams.page === 1) { 7618 rangeEnd = Math.min(2, totalPages); 7619 } 7620 } 7621 7622 // Render pagination list 7623 if (mode === 'NUMBERED_PAGINATION') { 7624 el.innerHTML = generateHTML({ 7625 currentPage: searchParams.page, 7626 pageRange: pageRange, 7627 rangeStart: rangeStart, 7628 rangeEnd: rangeEnd 7629 }); 7630 if (totalPages < 2 && hidePaginationOnFirstPage) { 7631 container.style.display = 'none'; 7632 } else { 7633 container.style.display = ''; 7634 } 7635 } else if (mode === 'LOAD_MORE_BUTTON') { 7636 if (searchParams.page >= totalPages) { 7637 loadMoreButton.style.display = 'none'; 7638 } else { 7639 loadMoreButton.style.display = ''; 7640 } 7641 } 7642 } 7643 function render(data) { 7644 renderPaginationList(); 7645 7646 // Render new content with handlebars 7647 var html = ''; 7648 if (mode === 'LOAD_MORE_BUTTON' && searchParams.page > 1) { 7649 // If using load more, append search results to existing results 7650 html = dataContainer.innerHTML; 7651 } 7652 for (var i = 0; i < data.length; i++) { 7653 var itemData = recursivelyParse(data[i]); 7654 itemData.sectionIndex = options.sectionIndex; 7655 itemData.renderOptions = options.renderOptions; 7656 html += template(itemData); 7657 } 7658 dataContainer.innerHTML = html; 7659 7660 // Scroll Top 7661 if (!firstRender && mode === 'NUMBERED_PAGINATION' && (!renderBySearch || !options.skipScrollOnSearch)) { 7662 // Only animate to the top if we replace dataContainer (numbered pagination) 7663 setTimeout(() => { 7664 const scrollTargetOffsetTop = scrollTarget.getBoundingClientRect().top + window.scrollY - scrollOffset; 7665 window.scrollTo({ 7666 top: scrollTargetOffsetTop, 7667 behavior: 'smooth' 7668 }); 7669 }, 250); 7670 } 7671 return true; 7672 } 7673 function generateHTML(args) { 7674 const totalPages = getTotalPage(); 7675 const { 7676 rangeStart, 7677 rangeEnd 7678 } = args; 7679 const makeLnk = page =>
7679 window.luxuryPresence.pagination.serializeParams(searchFormParams, sectionDomId, { 7680 ...searchParams, 7681 page 7682 }); 7683 let html = ''; 7684 if (showPrevious || showPageNumbers || showNext) { 7685 html += '<div class="paginationjs-pages"><ul>'; 7686 7687 // Previous button 7688 if (showPrevious) { 7689 if (searchParams.page <= 1) { 7690 html += `<li class="${classPrefix}-prev ${disableClassName}"><a>${prevText}</a></li>`; 7691 } else { 7692 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>`; 7693 } 7694 } 7695 7696 // Pages 7697 if (showPageNumbers) { 7698 if (rangeStart <= 3) { 7699 for (let i = 1; i < rangeStart; i++) { 7700 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>`; 7701 } 7702 } else { 7703 if (showFirstOnEllipsisShow) { 7704 html += `<li class="${classPrefix}-page ${classPrefix}-first J-paginationjs-page" data-num="1"><a href="${makeLnk(1)}">1</a></li>`; 7705 } 7706 html += `<li class="${classPrefix}-ellipsis ${disableClassName}"><a>${ellipsisText}</a></li>`; 7707 } 7708 for (let i = rangeStart; i <= rangeEnd; i++) { 7709 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>`; 7710 } 7711 if (rangeEnd < totalPages - 2) { 7712 html += `<li class="${classPrefix}-ellipsis ${disableClassName}"><a>${ellipsisText}</a></li>`; 7713 if (showLastOnEllipsisShow) { 7714 html += `<li class="${classPrefix}-page ${classPrefix}-last J-paginationjs-page" data-num="${totalPages}"><a href="${makeLnk(totalPages)}">${totalPages}</a></li>`; 7715 } 7716 } else { 7717 for (let i = rangeEnd + 1; i <= totalPages; i++) { 7718 html += `<li class="${classPrefix}-page J-paginationjs-page" data-num="${i}"><a href="${makeLnk(i)}">${i}</a></li>`; 7719 } 7720 } 7721 } 7722 7723 // Next button 7724 if (showNext) { 7725 if (searchParams.page >= totalPages) { 7726 html += `<li class="${classPrefix}-next ${disableClassName}"><a>${nextText}</a></li>`; 7727 } else { 7728 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>`; 7729 } 7730 } 7731 html += '</ul></div>'; 7732 } 7733 return html; 7734 } 7735 function go(newSearchParams, callback, restorationVisit) { 7736 if (disabled) return; 7737 var goToPageNumber = newSearchParams.page; 7738 7739 // Page number is out of bounds 7740 if (!goToPageNumber || goToPageNumber < 1) return; 7741 var totalPage = getTotalPage(); 7742 7743 // Page number is out of bounds 7744 if (!restorationVisit && totalNumber > 0) { 7745 if (goToPageNumber > totalPage) return; 7746 } 7747 if (!options.skipSerialization && (mode === 'NUMBERED_PAGINATION' || searchContainer)) { 7748 var newURL = window.luxuryPresence.pagination.serializeParams(searchFormParams, sectionDomId, { 7749 ...newSearchParams, 7750 page: goToPageNumber !== 1 && mode === 'NUMBERED_PAGINATION' ? newSearchParams.page : null 7751 }); 7752 if (!areQueryParamsEqual(newSearchParams, searchParams) && !restorationVisit) { 7753 window.history.pushState({}, '', window.luxuryPresence.getPath(newURL, { 7754 queryString: true 7755 })); 7756 } 7757 } 7758 7759 // Fetch and update page 7760 fetchData(newSearchParams, renderWithData); 7761 disabled = true; 7762 function renderWithData(err, data, count) { 7763 if (err) { 7764 dataContainer.dispatchEvent(new Event('error')); 7765 } else { 7766 searchParams = { 7767 ...newSearchParams 7768 }; 7769 totalNumber = count - offset; 7770 if (data.length === 0) { 7771 dataContainer.dispatchEvent(new Event('no-data')); 7772 } else {
7773 dataContainer.dispatchEvent(new Event('data')); 7774 } 7775 render(data); 7776 firstRender = false; 7777 } 7778 disabled = false; 7779 renderBySearch = false; 7780 } 7781 } 7782 function areQueryParamsEqual(first, second) { 7783 var differ = Object.keys(first).find(function (key) { 7784 return first[key] !== undefined && first[key] !== null && first[key] !== second[key]; 7785 }); 7786 if (differ) { 7787 return false; 7788 } 7789 differ = Object.keys(second).find(function (key) { 7790 return second[key] !== undefined && second[key] !== null && second[key] !== first[key]; 7791 }); 7792 return !differ; 7793 } 7794 function next(callback) { 7795 go({ 7796 ...searchParams, 7797 page: searchParams.page + 1 7798 }, callback); 7799 } 7800 function previous(callback) { 7801 go({ 7802 ...searchParams, 7803 page: searchParams.page - 1 7804 }, callback); 7805 } 7806 function refresh(callback) { 7807 go(searchParams, callback); 7808 } 7809 function update(params, callback) { 7810 renderBySearch = true; 7811 go(params, callback); 7812 } 7813 function observer() { 7814 if (searchContainer) { 7815 searchContainer.addEventListener('search', function (event, newSearchParams) { 7816 newSearchParams = event.detail || newSearchParams; 7817 update({ 7818 ...searchParams, 7819 ...newSearchParams, 7820 page: 1 7821 }); 7822 }); 7823 searchContainer.addEventListener('clear-search', function () { 7824 const defaultSearchParams = { 7825 ...DefaultSearchParams 7826 }; 7827 const isCurrentSearchFormStateDefault = areQueryParamsEqual({ 7828 ...defaultSearchParams, 7829 page: null 7830 }, { 7831 ...searchParams, 7832 page: null 7833 }); 7834 7835 // if mode is LOAD_MORE_BUTTON we should't make anything on clear 7836 // when search form have default state 7837 if (isCurrentSearchFormStateDefault && mode !== 'NUMBERED_PAGINATION') { 7838 return; 7839 } 7840 7841 // if someone initially go to some page and click 'Clear' on search from 7842 // we should't navigate to first page. We should preserve page value 7843 if (isCurrentSearchFormStateDefault) { 7844 defaultSearchParams.page = searchParams.page; 7845 } 7846 syncSearchFormState(defaultSearchParams); 7847 update(defaultSearchParams); 7848 }); 7849 } 7850 if (mode === 'LOAD_MORE_BUTTON') { 7851 loadMoreButton.addEventListener('click', function (event) { 7852 go({ 7853 ...searchParams, 7854 page: searchParams.page + 1 7855 }); 7856 return false; 7857 }); 7858 } 7859 if (mode === 'LOAD_MORE_BUTTON' || mode === 'BUTTONLESS_PAGINATION') { 7860 // Do initial render 7861 go(Object.assign({}, searchParams, { 7862 page: 1 7863 })); 7864 return; 7865 } 7866 if (mode === 'NUMBERED_PAGINATION') { 7867 // Go to specified page number 7868 container.addEventListener(eventPrefix + 'go', function (event) { 7869 let { 7870 pageNumber, 7871 done 7872 } = event.detail || {}; 7873 pageNumber = parseInt(pageNumber, 10); 7874 if (!pageNumber) { 7875 return; 7876 } 7877 go({ 7878 ...searchParams, 7879 page: pageNumber 7880 }, done); 7881 }); 7882 7883 // Page number button click 7884 el.addEventListener('click', function (event) { 7885 let clickTarget = event.target; 7886 let current = clickTarget.closest('.J-paginationjs-page, .J-paginationjs-previous, .J-paginationjs-next'); 7887 let pageNumber = current?.getAttribute('data-num'); 7888 if (!pageNumber || current.classList.contains(disableClassName) || current.classList.contains(activeClassName)) { 7889 return; 7890 } else { 7891 go({ 7892 ...searchParams, 7893 page: parseInt(pageNumber, 10) 7894 }); 7895 } 7896 event.preventDefault(); 7897 }); 7898 7899 // Whether to load the default page 7900 var defaultPageNumber = searchParams.page; 7901 if (triggerPagingOnInit) { 7902 container.dispatchEvent(new CustomEvent(eventPrefix + 'go', { 7903 detail: { 7904 pageNumber: defaultPageNumber 7905 } 7906 })); 7907 } 7908 } 7909 window.addEventListener('popstate', function (event) { 7910 var newParams = Object.assign({}, DefaultSearchParams, window.luxuryPresence.pagination.deserializeParams(searchFormParams, sectionDomId)); 7911 if (!areQueryParamsEqual(newParams, searchParams)) { 7912 go(newParams, null, true); 7913 if (searchContainer) { 7914 syncSearchFormState(newParams); 7915 } 7916 } 7917 }); 7918 } 7919 function replaceVariables(template, variables) { 7920 var formattedString; 7921 for (var key in variables) { 7922 var value = variables[key]; 7923 var regexp = new RegExp('<%=\\s*' + key + '\\s*%>', 'img'); 7924 formattedString = (formattedString || template).replace(regexp, value); 7925 } 7926 return formattedString; 7927 } 7928 function isTextEditorValue(obj) { 7929 return 'raw' in obj || 'gjs' in obj; 7930 } 7931 function getTextEditorValue(obj) { 7932 if (!obj.html) { 7933 return ''; 7934 } 7935 var data = obj.html; 7936 var css = (obj.raw || obj.gjs || {}).css; 7937 if (css) { 7938 data = data + '<style>' + css + '</style>'; 7939 } 7940 return data; 7941 } 7942 7943 /** 7944 * check rendered resources for html and raw properties that could indicate if resource is from an advanced text editor. 7945 * strips out raw proprties and returns only HTML if it exists. 7946 * @param {Object} resources a collection retrieved from the database 7947 * @return {Object} the newly modified object. 7948 */ 7949 function recursivelyParse(resources) {
7950 forEach(resources, function (value, key) { 7951 if (!value) { 7952 return; 7953 } 7954 if (value instanceof Object) { 7955 if (isTextEditorValue(value)) { 7956 resources[key] = getTextEditorValue(value); 7957 return; 7958 } else { 7959 return recursivelyParse(value); 7960 } 7961 } 7962 try { 7963 var parsedData = JSON.parse(value); 7964 if (isTextEditorValue(parsedData)) { 7965 resources[key] = getTextEditorValue(parsedData); 7966 } 7967 } catch (e) { 7968 // do nothing if we failed to parse value as JSON 7969 } 7970 }); 7971 return resources; 7972 } 7973 function forEach(collection, iteratee) { 7974 var func = Array.isArray(collection) ? arrayEach : baseFor; 7975 return func(collection, iteratee); 7976 } 7977 function baseFor(object, iteratee) { 7978 if (!object) { 7979 return; 7980 } 7981 var iterable = Object(object); 7982 var props = Object.keys(object); 7983 var length = props.length; 7984 var index = -1; 7985 while (length--) { 7986 var key = props[++index]; 7987 if (iteratee(iterable[key], key, iterable) === false) { 7988 break; 7989 } 7990 } 7991 return object; 7992 } 7993 function objSet(obj, path, value) { 7994 const pathArray = Array.isArray(path) ? path : path.match(/([^[.\]])+/g); 7995 pathArray.reduce((acc, key, i) => { 7996 if (acc[key] === undefined) acc[key] = {}; 7997 if (i === pathArray.length - 1) acc[key] = value; 7998 return acc[key]; 7999 }, obj); 8000 } 8001 function arrayEach(array, iteratee) { 8002 var index = -1; 8003 var length = array.length; 8004 while (++index < length) { 8005 if (iteratee(array[index], index, array) === false) { 8006 break; 8007 } 8008 } 8009 return array; 8010 } 8011 function selectPageRangeMode(e) { 8012 pageRange = e.matches ? mobilePageRange : desktopPageRange; 8013 renderPaginationList(); 8014 } 8015 paginationMql.addEventListener('change', selectPageRangeMode); 8016})('section-636230b7-115c-40c0-833b-0d0151d53062', JSON.parse("{\"pageSize\":\"7\",\"resource\":\"agents\",\"pageRange\":1,\"variables\":{\"limit\":7,\"offset\":0,\"sortDir\":\"{{variables.sortDir}}\",\"companyId\":\"64eccd3e-c115-4e58-ba5c-a7d7cd11cf04\",\"categoryId\":\"{{pageElement.categoryId}}\",\"agentIds\":[\"bd1a47c9-0934-49a8-8d9c-531bf8917f07\",\"bedbe2db-262f-4929-be98-e6e11b7b72e1\",\"18f19965-08c7-4e36-8b8b-85ccd0f8f8fc\",\"4e0826f4-00d4-4b34-ac61-07a8d0622121\",\"893c9fe6-fea3-40f2-9ce4-7980ca0579de\",\"d5ff112d-c35d-486a-b38f-5cca1e3320fc\",\"4ad38ed6-abf5-4798-8217-5e74b122ce76\"]},\"searchFormParams\":[{\"param\":\"search\",\"urlKey\":\"search\"},{\"param\":\"officeId\",\"global\":true,\"urlKey\":\"officeId\"}],\"hidePaginationOnFirstPage\":true,\"companyId\":\"64eccd3e-c115-4e58-ba5c-a7d7cd11cf04\",\"usePageResource\":false,\"sectionIndex\":2}")); 8017} catch(e) { 8018console.log(e); 8019} 8020 try { 8021(function init(sectionDomId) { 8022 const $section = $("#" + sectionDomId); 8023 const $searchContainer = $section.find(".search-container"); 8024 const $officeSelect = $section.find('.js-office-input'); 8025 const $searchInput = $section.find('.js-search-input'); 8026 const $emptyMsg = $section.find('.js-empty-message'); 8027 const $dataContainer = $section.find('.data-container'); 8028 const $containter = $section.find('.js-container'); 8029 const officeFilter = $searchContainer.data('fetchOffices'); 8030 $section.find('.clear-icon').click(function () { 8031 $searchInput.val(''); 8032 onSearch(); 8033 }); 8034 $dataContainer.on("data", function () { 8035 $emptyMsg.removeClass("visible"); 8036 $containter.removeClass('no-data'); 8037 }); 8038 $dataContainer.on("no-data", function () { 8039 $emptyMsg.addClass("visible"); 8040 $containter.addClass('no-data'); 8041 }); 8042 $searchInput.on('input', window.luxuryPresence.debounce(onSearch, 1000)); 8043 function onSearch() { 8044 const params = {}; 8045 if (officeFilter) params.officeId = $officeSelect.val() === '' ? null : [$officeSelect.val()]; 8046 params.search = $searchInput.val() || null; 8047 $searchContainer[0]?.dispatchEvent(new CustomEvent('search', { 8048 detail: params 8049 })); 8050 if ($searchInput.val()) $searchContainer.addClass('searching');else $searchContainer.removeClass('searching'); 8051 } 8052 if (!officeFilter) return; 8053 var previousOption = '';
8054 $searchContainer.on('offices-data', function (evt) { 8055 let { 8056 data 8057 } = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; 8058 data = evt.detail?.data || data; 8059 $officeSelect.append(data.map(office => `<option value="${office.id}">${office.name}</option>`).join("\n")); 8060 convertSelectToDropdown($officeSelect); 8061 }); 8062 function updateDropdown(select, value) { 8063 let $dropdown = $(select).next(); 8064 let $dropdownToggle = $dropdown.find(".custom-select-toggle"); 8065 let $dropdownContent = $dropdown.find(".custom-select-content"); 8066 let $selectedOption = $dropdownContent.find(`button[value="${value}"]`); 8067 function unselectOption() { 8068 $linkedOption = $originalSelect.find("option").eq(0); 8069 $linkedOption.prop("selected", true); 8070 $dropdownToggle.html($linkedOption.html()); 8071 $dropdownContent.find(".custom-select-item").removeClass("is-selected"); 8072 $originalSelect.change(); 8073 previousOption = ""; 8074 } 8075 if (!$selectedOption) return; 8076 if ($selectedOption.hasClass("is-disabled")) return; 8077 let elementNumber = $selectedOption.index(); 8078 let $originalSelect = $selectedOption.closest(".custom-select").siblings("select"); 8079 let $linkedOption = $originalSelect.find("option").eq(elementNumber); 8080 if (previousOption === value) { 8081 unselectOption(); 8082 return; 8083 } 8084 if (!$linkedOption.prop("selected")) { 8085 $linkedOption.prop("selected", true); 8086 previousOption = value; 8087 $originalSelect.change(); 8088 $selectedOption.addClass("is-selected").siblings(".custom-select-item").removeClass("is-selected"); 8089 closeSelectMenu($selectedOption.closest(".custom-select-menu")); 8090 $dropdownToggle.html($linkedOption.html()); 8091 if ($linkedOption.val()) { 8092 $dropdownToggle.removeClass("is-placeholder"); 8093 $dropdown[$linkedOption.val() !== "" ? "addClass" : "removeClass"]("has-selection"); 8094 } else { 8095 $dropdown.removeClass("has-selection"); 8096 $dropdownToggle.addClass("is-placeholder"); 8097 } 8098 } 8099 } 8100 function convertSelectToDropdown(selectItem) { 8101 let customDropdown = ` 8102 <div class="custom-select" role="presentation"> 8103 <button class="custom-select-toggle" type="button" aria-haspopup="true" aria-expanded="false"></button> 8104 <div class="custom-select-menu"> 8105 <div class="custom-select-content"> 8106 </div> 8107 </div> 8108 </div>`; 8109 $(customDropdown).insertAfter(selectItem); 8110 let $dropdown = $(selectItem).next(); 8111 let $dropdownToggle = $dropdown.find(".custom-select-toggle"); 8112 let $dropdownMenu = $dropdown.find(".custom-select-menu"); 8113 let $dropdownContent = $dropdown.find(".custom-select-content"); 8114 $dropdownToggle.addClass($(selectItem).attr("class")).removeClass("dropdown-select"); 8115 $dropdownToggle.attr("id", $(selectItem).attr("id") + "custom"); 8116 $dropdownToggle.attr("data-name", $(selectItem).attr("name")); 8117 $dropdownMenu.attr("aria-labelledby", $dropdownToggle.attr("id")); 8118 appendOptionButtons(selectItem, $dropdownContent); 8119 $(selectItem).removeClass().addClass("sr-only"); 8120 $(selectItem).on("focus", function () { 8121 showSelectMenu($dropdownMenu[0]); 8122 $dropdownToggle.focus(); 8123 }); 8124 var toggleText = $(selectItem).find(":selected").first().text() || $(selectItem).children().first().text(); 8125 $dropdownToggle.addClass("is-placeholder").html(toggleText); 8126 $dropdown.on("click", ".custom-select-item", function (e) { 8127 updateDropdown($(this).closest(".lp-input-group").find("select"), $(this).attr("value")); 8128 }); 8129 function convertOptionToButton(option) { 8130 var optionText = $(option).text(); 8131 var optionValue = $(option).attr("value") || $(option).text(); 8132 var optionIsDisabled = $(option).prop("disabled"); 8133 var outputButton = `<button value="${optionValue}" class="custom-select-item lp-icon" type="button" ${optionIsDisabled ? "disabled" : ""}>${optionText}</button>`; 8134 return outputButton; 8135 } 8136 function appendOptionButtons(selectItem, appendTarget) { 8137 $(selectItem).find("option").each(function () { 8138 let currentButton = convertOptionToButton(this); 8139 $(appendTarget).append(currentButton); 8140 }); 8141 } 8142 function showSelectMenu(selectMenu) { 8143 var customSelect = $(selectMenu).closest(".custom-select")[0]; 8144 customSelect.classList.add('open'); 8145 $(selectMenu).slideDown(); 8146 } 8147 $dropdown.on("click", ".custom-select-toggle", function (e) { 8148 var $customSelect = $(this).closest(".custom-select"); 8149 var selectMenu = $customSelect.find(".custom-select-menu")[0]; 8150 if ($customSelect.hasClass("open")) { 8151 closeSelectMenu(selectMenu); 8152 } else { 8153 showSelectMenu(selectMenu); 8154 } 8155 }); 8156 $(document.body).on("click", function (e) { 8157 if (!$(e.target).closest(`#${sectionDomId} .js-select-input`).length) { 8158 $(`#${sectionDomId} .custom-select.open`).each(function () { 8159 const selectMenu = $(this).find(".custom-select-menu")[0]; 8160 closeSelectMenu(selectMenu); 8161 }); 8162 } 8163 }); 8164 } 8165 function closeSelectMenu(selectMenu) { 8166 var customSelect = $(selectMenu).closest(".custom-select")[0]; 8167 $(customSelect).removeClass("open"); 8168 $(selectMenu).slideUp(); 8169 } 8170 $officeSelect.change(onSearch); 8171})('section-636230b7-115c-40c0-833b-0d0151d53062', 'undefined'); 8172} catch(e) { 8173console.log(e); 8174} 8175 try { 8176(function init(sectionDomId) { 8177 const $section = $('#' + sectionDomId); 8178 const slider = $('#' + sectionDomId + ' .js-list-slider'); 8179 const avatarSlider = $('#' + sectionDomId + ' .js-avatar-slider'); 8180 const autoplay = !$section.find('.testimonials-section').hasClass('js-stop-autoplay'); 8181 let $cellNumbers = $('#' + sectionDomId + ' .js-list-cell'); 8182 let cellNumbersLength = $cellNumbers.length >= 10 ? $cellNumbers.length : "0" + $cellNumbers.length; 8183 let paused = false;
8184 var sectionBackground = $section.css('background-image'); 8185 if ($section.find('.testimonials-section.parallax').length) { 8186 $section.addClass('parallax-enabled'); 8187 $section.css({ 8188 '--sectionBackground': sectionBackground, 8189 'background-image': 'none' 8190 }); 8191 } 8192 function throttle(cb, delay) { 8193 let wait = false; 8194 return function () { 8195 if (wait) { 8196 return; 8197 } 8198 cb(...arguments); 8199 wait = true; 8200 setTimeout(() => { 8201 wait = false; 8202 }, delay); 8203 }; 8204 } 8205 function pauseSlider() { 8206 const section = document.querySelector(`#${sectionDomId}`); 8207 const rect = section.getBoundingClientRect(); 8208 if (paused && rect.bottom < -30) { 8209 return; 8210 } 8211 if (!paused && rect.bottom > 0) { 8212 return; 8213 } 8214 if (rect.bottom < -30 && !paused) { 8215 paused = true; 8216 slider.slick('slickPause'); 8217 return; 8218 } 8219 paused = false; 8220 slider.slick('slickPlay'); 8221 } 8222 if (autoplay) { 8223 $(window).on('scroll.lpui-auto-clean', throttle(pauseSlider, 400)); 8224 } 8225 if (cellNumbersLength > 1) $('#' + sectionDomId + ' .js-num-pages').html('01 / <span num-pages--total>' + cellNumbersLength + '</span>'); 8226 if (slider.length > 0) { 8227 const arrows = slider.data('arrows-style') || ''; 8228 var autoscrollInterval = parseInt(slider.attr('data-autoscroll-interval'), 10) || 4; 8229 slider.slick({ 8230 dots: false, 8231 arrows: true, 8232 prevArrow: `<button class='lp-arrow lp-arrow--prev lp-arrow--dark ${arrows}'>prev</button>`, 8233 nextArrow: `<button class='lp-arrow lp-arrow--next lp-arrow--dark ${arrows}'>next</button>`, 8234 appendArrows: $section.find('.arrows-container'), 8235 infinite: true, 8236 slidesToShow: 1, 8237 slidesToScroll: 1, 8238 autoplay, 8239 autoplaySpeed: autoscrollInterval * 1000, 8240 rows: 0, 8241 adaptiveHeight: true, 8242 asNavFor: avatarSlider 8243 }); 8244 avatarSlider.slick({ 8245 dots: false, 8246 arrows: false, 8247 infinite: true, 8248 slidesToShow: 1, 8249 slidesToScroll: 1, 8250 rows: 0, 8251 asNavFor: slider, 8252 draggable: false, 8253 speed: 500, 8254 fade: true, 8255 cssEase: 'linear' 8256 }); 8257 slider.on('afterChange', function (event, slick, currentSlide, nextSlide) { 8258 let currentPageNumber = currentSlide + 1; 8259 if (currentPageNumber < 10) currentPageNumber = '0' + currentPageNumber; 8260 $('#' + sectionDomId + ' .js-num-pages').html(currentPageNumber + ' / <span num-pages--total>' + cellNumbersLength + '</span>'); 8261 }); 8262 } 8263 function setAnimations() { 8264 const $target = $section.find('.js-animation-target'); 8265 let x = 0, 8266 y = 0; 8267 let tx = 0, 8268 ty = 0; 8269 let overchk = false; 8270 let animationRun = false; 8271 let animationRequest; 8272 document.addEventListener("mousemove", function (e) { 8273 x = e.pageX; 8274 y = e.pageY; 8275 }); 8276 $target.each(function () { 8277 $(this).css('will-change', 'transform'); 8278 $(this).wrap('<div class="js-target-wrap"></div>'); 8279 $(this).closest('.js-target-wrap').append('<div class="js-target"></div>').css({ 8280 "position": "relative", 8281 "width": "auto" 8282 }); 8283 $(this).next('.js-target').css({ 8284 "position": "absolute", 8285 "left": "0", 8286 "top": "0", 8287 "width": "100%", 8288 "height": "100%" 8289 }); 8290 $(this).next('.js-target').on('click', function () { 8291 if ($section.find('button[data-type="VIDEO"]').length) { 8292 $section.find('button[data-type="VIDEO"]').trigger('click'); 8293 } 8294 }); 8295 }); 8296 function loop(el) { 8297 let targetWidth = $(el).outerWidth(); 8298 let targetHeight = $(el).outerHeight(); 8299 if (overchk) { 8300 tx += (x - $(el).closest('.js-target-wrap').offset().left - targetWidth / 2 - tx) * 0.04; 8301 ty += (y - $(el).closest('.js-target-wrap').offset().top - targetHeight / 2 - ty) * 0.04; 8302 el.style.transform = "translate(" + tx + "px, " + ty + "px)"; 8303 } else { 8304 tx -= tx * 0.3; 8305 ty -= ty * 0.3; 8306 el.style.transform = "translate(" + tx + "px, " + ty + "px)"; 8307 } 8308 animationRequest = requestAnimationFrame(function () { 8309 loop(el); 8310 }); 8311 } 8312 function runAnimation(el) { 8313 if (!animationRun) { 8314 animationRun = true; 8315 loop(el); 8316 overchk = true; 8317 } 8318 } 8319 function stopAnmation() { 8320 if (animationRun) { 8321 setTimeout(function () { 8322 window.cancelAnimationFrame(animationRequest); 8323 animationRun = false;
8324 tx = 0, ty = 0; 8325 }, 100); 8326 overchk = false; 8327 } 8328 } 8329 $section.find('.js-target').on('mouseenter', function (e) { 8330 x = e.pageX; 8331 y = e.pageY; 8332 if (autoplay) slider.slick('slickPause'); 8333 runAnimation($(this).closest('.js-target-wrap').find('.js-animation-target')[0]); 8334 }); 8335 $section.find('.js-target').on('wheel', function () { 8336 if (autoplay) slider.slick('slickPlay'); 8337 stopAnmation(); 8338 }); 8339 $section.find('.js-target').on('mouseleave', function () { 8340 if (autoplay) slider.slick('slickPlay'); 8341 stopAnmation(); 8342 }); 8343 } 8344 if (window.matchMedia('(hover: hover) and (pointer: fine)').matches) { 8345 setTimeout(setAnimations); 8346 } 8347})('section-950e58af-1ac6-48e0-b114-f7326c904100', 'undefined'); 8348} catch(e) { 8349console.log(e); 8350} 8351 try { 8352(function init(sectionDomId) { 8353 // please write all js here and use sectionDomId in selectors in order of DOM encapsulation 8354})('section-a747ae9e-de7c-4dd7-a979-1a646c88a921', 'undefined'); 8355} catch(e) { 8356console.log(e); 8357} 8358 try { 8359(function init(sectionDomId) {})('section-64dea10a-4201-4776-9fdf-ce8691ad5271', 'undefined'); 8360} catch(e) { 8361console.log(e); 8362} 8363 try { 8364(function init(sectionDomId) { 8365 const form = $("#" + sectionDomId).find("form"); 8366 const redirectLink = $('#' + sectionDomId + ' .redirect-link'); 8367 form.on("reset", function () { 8368 redirectPage(redirectLink); 8369 }); 8370 function redirectPage(link) { 8371 if (link.length) { 8372 const rawHref = link.attr('href'); 8373 link.attr('href', setHttp(rawHref)); 8374 link[0].click(); 8375 } 8376 } 8377 function setHttp(link) { 8378 if (link.search(/^http[s]?\:\/\/|\//) == -1) { 8379 link = 'http://' + link; 8380 } 8381 return link; 8382 } 8383})('section-fe5140df-d21d-4653-9d5d-d225eca79459', 'undefined'); 8384} catch(e) { 8385console.log(e); 8386} 8387 try { 8388(function init(sectionDomId) { 8389 const $section = $('#' + sectionDomId); 8390 if ($section.find('.parallax').length) $section.addClass('parallax-enabled'); 8391 if ($section.find('.video-wrapper').length) $section.addClass('video'); 8392})('section-382717e0-234c-406a-ac82-41111ea1b5c3', 'undefined'); 8393} catch(e) { 8394console.log(e); 8395} 8396 8397 var { body } = document; 8398 var globalNav = body.querySelector(':scope > nav'); 8399 var globalSideMenu = body.querySelector(':scope > div.sidemenu'); 8400 8401 document.querySelectorAll('body > .modal .close').forEach(modalCloseBtn => { 8402 modalCloseBtn.addEventListener('click', function() { 8403 window.lpUI.hideClosestModal(modalCloseBtn); 8404 }); 8405 }); 8406 8407 window.addEventListener('click', function() { 8408 document.querySelectorAll('.share-popup').forEach(sharedPopup => { 8409 sharedPopup.style.display = 'none' ; 8410 }); 8411 }); 8412 8413 /* Misc Partials / Addons */ 8414 function initWOW() { 8415 if (wow) { 8416 wow.init(); 8417 wow.scrollHandler(); 8418 setTimeout(function () { 8419 const wowHideStyle = document.getElementById('wow-hide-elements'); 8420 if(wowHideStyle) { 8421 wowHideStyle.disabled = true; 8422 } 8423 }, 1); 8424 } 8425 } 8426 8427 const wowHideElements = document.getElementById('wow-hide-elements'); 8428 if (wowHideElements) { 8429 wowHideElements.disabled = false; 8430 } 8431 8432 initWOW(); 8433 8434 /* Generic Navbar Handling */ 8435 globalNav?.querySelectorAll('.hamburger-component').forEach(hamburgerComponent => { 8436 hamburgerComponent.addEventListener('click', function() { 8437 window.lpUI.sideMenuToggle(); 8438 }); 8439 }); 8440 globalSideMenu?.querySelectorAll('.toggle').forEach(toggleEl => { 8441 toggleEl.addEventListener('click', function() { 8442 window.lpUI.sideMenuToggle(); 8443 }); 8444 }); 8445 8446 document.querySelectorAll('body > nav .sub-nav-container > a, body > div.sidemenu .sub-nav-container > a').forEach(linkEl => { 8447 linkEl.addEventListener('click', function(e) { 8448 e.preventDefault(); 8449 e.stopPropagation();
8450 var subNav = linkEl.parentElement.querySelector(':scope > .sub-nav'); 8451 if (subNav && subNav.classList.contains('visible')){ 8452 subNav.classList.remove('visible'); 8453 } else { 8454 globalNav?.querySelectorAll('.sub-nav-container .sub-nav').forEach(globalSubNav => { 8455 globalSubNav.classList.remove('visible'); 8456 }); 8457 globalSideMenu?.querySelectorAll('.sub-nav').forEach(sideMenuSubNav => { 8458 sideMenuSubNav.classList.remove('visible'); 8459 }); 8460 subNav?.classList.add('visible'); 8461 } 8462 }); 8463 }); 8464 window.addEventListener('click', function() { 8465 document.querySelectorAll('nav .sub-nav.visible, .sidemenu .sub-nav.visible').forEach(subNavEl => { 8466 subNavEl.classList.remove('visible'); 8467 }); 8468 }); 8469 8470 } 8471 8472 document.addEventListener('DOMContentLoaded', lpGlobalInit); 8473 8474 </script>
8474 8475 8476
8476<script type="module"> 8477 function selectorExists(selector) { 8478 return document.querySelector(selector) !== null; 8479 } 8480 8481 // Function to initialize the third-party library 8482 function initParoller(selector = '.my-paroller') { 8483 var $parollerElements = $(selector); 8484 if ($parollerElements.length > 0 && typeof $parollerElements.paroller === 'function') { 8485 $parollerElements.paroller(); 8486 } 8487 } 8488 8489 const parollerSelector = '.my-paroller'; 8490 8491 if (selectorExists(parollerSelector)) { 8492 window.luxuryPresence.libs.loadJs(window.luxuryPresence.libs.jsLibsMap.paroller.jsSrc, function() { 8493 initParoller(parollerSelector); 8494 window.luxuryPresence.libs.jsLibsMap.paroller.isLoaded = true; 8495 }); 8496 } 8497 </script>
8497 8498 8499 <!-- Google Tag Manager (noscript) -->
vendor: 80 bytes, lines 8499-8500
8499 8500 <noscript><iframe src="https://www.googletagmanager.com/ns.html?id=
8500GTM-T9SFQGHZ
vendor: 107 bytes, lines 8500-8502
8500" 8501 height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript> 8502
8502<!-- End Google Tag Manager --> 8503 8504
8504<script 8505 src="https://cdn.luxurypresence.com/scripts/a11y-widget/latest/widget.js" 8506 data-website-id="872b2194-0788-46bf-b553-bdf3d29838c6" 8507 8508 defer></script>
vendor: 1 bytes, line 8508
8508
8509<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:'a480eda79a74f165',t:'MTc5MTU4NjA1OQ=='};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>
8509<script type="module" src="https://static.cloudflareinsights.com/beacon.min.js/v4bc70e2c01a94c73b74392e4234840661791215815920" integrity="sha512-L0ha0OXavK/8okipN9F8BtP84dg9DUhPERbBXzwI6dgTA55d2+yweo3pn5CSFYs45/r8md2+xvUPtTdvTNRfjA==" data-cf-beacon='{"version":"2024.11.0","token":"d2a8f4b3d65944edb8fcd6ffce49ee5e","spa":2}' crossorigin="anonymous"></script>
8509 8510</body> 8511</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.