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>Contact Us | Richard Scully & Associates | Real Estate Agents</title> 10 <meta property="og:title" content="Contact Us | Richard Scully & Associates | Real Estate Agents"> 11 <meta name="twitter:title" content="Contact Us | Richard Scully & Associates | Real Estate Agents"> 12 <meta name="description" content="Are you interested in learning more about the real estate market? Unlock the gateway to seamless communication by sharing your contact details on this page!"> 13 <meta property="og:description" content="Are you interested in learning more about the real estate market? Unlock the gateway to seamless communication by sharing your contact details on this page!"> 14 <meta name="twitter:description" content="Are you interested in learning more about the real estate market? Unlock the gateway to seamless communication by sharing your contact details on this page!"> 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/b1d6f7c0-86b6-4efb-bb8e-35bc12113699"> 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/b1d6f7c0-86b6-4efb-bb8e-35bc12113699"> 17 <meta property="og:url" content="https://muskokacottagesforsale.com/contact-us"> 18 <link rel="canonical" href="https://muskokacottagesforsale.com/contact-us"> 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=Tenor%20Sans:400"><link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Lato:100,100i,300,300i,400,400i,700,700i,900,900i"> 23 <link rel="preload" href="https://cdnjs.cloudflare.com/ajax/libs/ion-rangeslider/2.3.1/css/ion.rangeSlider.min.css" as="style" onload="this.onload=null;this.rel='stylesheet'"> 24 <noscript><link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/ion-rangeslider/2.3.1/css/ion.rangeSlider.min.css"></noscript> 25 <link rel="icon" type="image/x-icon" href="/favicon.ico"> 26 <link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png"> 27 <link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png"> 28 <link rel="icon" type="image/png" sizes="48x48" href="/favicon-48x48.png"> 29 <style id="global-css">:root{--global-primary-font-family:'Tenor Sans',sans-serif;--global-primary-font-family-short:Tenor Sans;--global-secondary-font-family:Lato,sans-serif;--global-secondary-font-family-short:Lato;--global-body-padding:0px;--global-background-color:#fff;--global-body-font-size:16px;--global-h1-font-size:50px;--global-h2-font-size:40px;--global-h3-font-size:30px;--global-h4-font-size:21px;--global-h5-font-size:17px;--global-h6-font-size:16px;--global-section-padding:96px;}@media (max-width: 768px){:root{--global-section-padding:64px;}}body{padding:0px;background-color:#fff;font-family:Lato,sans-serif;font-size:16px;}@media (min-width: 768px){body{padding:0px 0px 0px 0px;}}h1,h2,h3,h4,h5,h6,button{font-family:'Tenor Sans',sans-serif;}h1{font-size:50px;}h2{font-size:40px;}h3{font-size:30px;}h4{font-size:21px;}h5{font-size:17px;}h6{font-size:16px;}</style> 30 <link rel="stylesheet" type="text/css" href="https://styles.luxurypresence.com/producer/index.css" /> 31 <style id="website-css">ul.slick-links-nav li span.link.slick-disabled{cursor:default;}.J-paginationjs-page a{margin:0 3px;}.lp-btn,.redesign .lp-btn{padding:20px 46px;}#modal-global-contact-us .contact-form .section-message form .submitButton{padding:14px 20px;}.lp-btn,.redesign .lp-btn,.redesign .btn{border-width:1px!important;font-weight:300!important;}nav#global-navbar a{font-weight:300;}button.newsletter-signup__form-item{line-height:1;font-weight:400;}nav#global-navbar .header .navigation .sub-nav ul{background-color:transparent;}nav#global-navbar .header .navigation .sub-nav ul li{background-color:#fff;}nav#global-navbar .header .navigation .sub-nav ul li:hover{background-color:#FFF;}nav#global-navbar .header .navigation .sub-nav ul a{color:#000!important;}nav#global-navbar.scroll{background-color:#000!important;}nav#global-navbar.scroll li.navigation__item a.navigation__link{color:#FFF!important;}nav#global-navbar.scroll li.navigation__item a.navigation__link:hover{color:#FFF!important;}nav#global-navbar.scroll li.navigation__item a.navigation__link span:after{background-color:#FFF!important;}nav#global-navbar.scroll button.hamburger-component.hamburger{color:#FFF;}nav#global-navbar.scroll button.hamburger-component.hamburger:hover{color:#FFF;}nav#global-navbar.scroll .navigation__item:last-child{-webkit-filter:invert(1);filter:invert(1);}nav#global-navbar .header .navigation .sub-nav ul a:hover{color:#FFF!important;background-color:#792617!important;}nav#global-navbar .header .navigation .sub-nav ul a:hover span:hover{color:#FFF!important;}nav#global-navbar .header .navigation .sub-nav ul a:hover span:after{background-color:#FFF!important;}nav .header .navigation .sub-nav__link{max-width:400px!important;}nav .header .navigation .sub-nav ul{max-width:400px!important;}nav#global-navbar.scroll li.navigation__item a.navigation__link:hover{color:#792617;}nav#global-navbar.scroll li.navigation__item a.navigation__link span:after{background-color:#792617;}#global-navbar .header .logo .logo__img{max-height:100%;max-width:300px;}@media only screen and (max-width:768px){#global-navbar .header .logo .logo__img{max-width:250px!important;}}#global-navbar .header{padding-top:15px;padding-bottom:15px;height:auto;}.company__info__newsletter h2{font-size:0px;}.company__info__newsletter h2:before{content:"";display:block;width:auto;height:40px;background-image:url(https://media-production.lp-cdn.com/cdn-cgi/image/format=auto,quality=85/https://media-production.lp-cdn.com/media/124e6fd1-c91a-4767-8aec-b87e6e6b1291);-webkit-background-size:contain;background-size:contain;background-repeat:no-repeat;-webkit-background-position:left;background-position:left;margin:0 auto!important;}footer .footer .company__contacts{margin-top:0px!important;}
31footer .footer .newsletter__info{max-width:100%!important;}footer .luxury-link,.copyright-box .hyperlink-style-2,.info-wrap-contact_link,.ft-link{font-weight:400;position:relative!important;color:#fff!important;-webkit-text-decoration:none!important;text-decoration:none!important;-webkit-transition:0.4s;transition:0.4s;opacity:1!important;}footer .luxury-link:hover,.copyright-box .hyperlink-style-2:hover,.info-wrap-contact_link:hover,.ft-link:hover{color:#fff!important;}footer .luxury-link:before,.copyright-box .hyperlink-style-2:before,.info-wrap-contact_link:before,.ft-link:before,footer .luxury-link:after,.copyright-box .hyperlink-style-2:after,.info-wrap-contact_link:after,.ft-link:after{-webkit-transition:0.4s;transition:0.4s;-webkit-text-decoration:none!important;text-decoration:none!important;line-height:1;content:"""";height:1px;position:absolute!important;bottom:0;left:0;}footer .luxury-link:before,.copyright-box .hyperlink-style-2:before,.info-wrap-contact_link:before,.ft-link:before{width:100%!important;background-color:#ccc!important;}footer .luxury-link:after,.copyright-box .hyperlink-style-2:after,.info-wrap-contact_link:after,.ft-link:after{width:0%!important;background-color:#fff!important;}footer .luxury-link:hover:after,.copyright-box .hyperlink-style-2:hover:after,.info-wrap-contact_link:hover:after,.ft-link:hover:after{width:100%!important;}div#global-sidemenu.sidemenu a:hover{color:#FFF!important;}body>div.sidemenu .sub-nav-container .main-nav:hover::after{border-bottom:1px solid #FFF!important;border-right:1px solid #FFF!important;}body>div.sidemenu .section-options a{color:#FFF!important;}@media (max-width: 768px){body>div.sidemenu .section-options a{font-size:25px!important;}}div#modal-global-contact-us .contact-form .info-zone .info-zone_contact_cell{margin-bottom:21px;}div#modal-global-contact-us .contact-form .info-zone .type_user:nth-of-type(2){margin-bottom:21px;}@media only screen and (max-width: 768px){div#modal-global-contact-us .contact-form .info-zone .type_user{margin-bottom:25px;}}div#modal-global-contact-us .contact-form .info-zone_contact{margin-bottom:14px;}div#modal-global-contact-us .contact-form .section-message h2{font-size:38px;}@media only screen and (max-width: 768px){div#modal-global-contact-us .contact-form .section-message h2{font-size:28px;}}div#modal-global-contact-us .contact-form h2{font-size:38px;}@media only screen and (max-width: 768px){div#modal-global-contact-us .contact-form h2{font-size:28px;}}#modal-global-contact-us .contact-form .section-message label{color:#fff!important;}#modal-global-contact-us .lp-input-group .lp-input{border-color:#fff!important;}div#modal-global-contact-us a:not(.lp-socials__link):after{background-color:#FFF!important;}.info-zone_contact_cell a:hover{color:#FFF!important;}.info-zone_contact_cell a:before{background-color:#ccc!important;width:100%;}.info-zone_contact_cell a:after{background-color:#FFF!important;}#modal-global-contact-us .contact-form .section-message label{text-transform:capitalize!important;}.section-message .lp-tcr-message{text-transform:none!important;font-weight:400;}div#modal-global-contact-us .contact-form .section-message form{max-width:490px;}@media only screen and (max-width:1080px){div#modal-global-contact-us .contact-form h2{font-size:37px;}}@media only screen and (max-width:925px){div#modal-global-contact-us .contact-form h2{font-size:25px;}}div#modal-global-contact-us .close{top:4vh;}div#modal-global-contact-us div.modal-content div.contact-form.redesign:after{background-color:rgba(0, 0, 0, 0.6);}#modal-global-contact-us .lp-disclaimer a{-webkit-text-decoration:none!important;text-decoration:none!important;position:relative;display:inline-block;vertical-align:top;}#modal-global-contact-us .lp-disclaimer a:after,#modal-global-contact-us .lp-disclaimer a:before{content:"";height:1px;position:absolute;bottom:0;left:0;box-sizing:border-box;outline:none;-webkit-transition:0.4s;transition:0.4s;}#modal-global-contact-us .lp-disclaimer a:before{background-color:#ccc;width:100%;}#modal-global-contact-us .lp-disclaimer a:after{width:0%;background-color:#FFF!important;-webkit-transition:0.4s;transition:0.4s;}#modal-global-contact-us .lp-disclaimer a:hover{-webkit-text-decoration:none!important;text-decoration:none!important;}#modal-global-contact-us .lp-disclaimer a:hover:after{width:100%;}div#modal-global-contact-us .contact-form{-webkit-background-position:center;background-position:center;}div#modal-global-contact-us .contact-form h2{color:#FFF;}div.mobile-contact-wrapper .contact-form .lp-btn{background-color:#FFF;border-color:#FFF;color:#792617;}div.mobile-contact-wrapper .contact-form .lp-btn:hover{background-color:transparent;border-color:#FFF;color:#FFF;}footer .img-wrap img{-webkit-filter:invert(1);filter:invert(1);}footer .footer .first_text_footer .img_size img{-webkit-filter:invert(1);filter:invert(1);}.navbar .navigation .navigation__item:last-child{content:""!important;display:block;height:30px;background-image:url('https://media-production.lp-cdn.com/media/58cb8d09-2b18-463c-94d6-a81a57ca19b0');background-repeat:no-repeat;-webkit-background-size:contain;background-size:contain;margin-left:15px;margin-right:15px;-webkit-filter:invert(1);filter:invert(1);}.navbar .navigation .navigation__item:last-child .navigation__link span{font-size:0px!important;display:none!important;}@media (max-width: 768px){footer .footer__layout__right{margin-top:0px!important;padding-left:0;}}
31footer .footer .newsletter{margin-bottom:0px!important;}@media (max-width: 768px){body>div.sidemenu .section-options a{font-size:20px!important;}}@media (max-width: 768px){body>div.sidemenu li{margin-bottom:0!important;}}@media (max-width: 768px){nav .header .navbar{padding-left:10px;}nav .header .logo img{max-width:80%!important;}}@media (max-width: 575px){footer .footer__layout__left{margin-left:auto!important;margin-right:auto!important;}}</style> 32 <style id="wow-hide-elements">.wow{visibility:hidden!important}</style> 33 <style id="page-css"> 34 #section-1c92a0cc-d18f-4a88-96d3-a5f34620fae7{color:#000;--fontColor:#000;--fontColor_H:0;--fontColor_S:0%;--fontColor_L:0%;--fontColor_A:1;--fontColor_darkenDir:1;--bgColor:#ffffff;--bgColor_H:0;--bgColor_S:0%;--bgColor_L:100%;--bgColor_A:1;--bgColor_darkenDir:-1;--section-padding-top:calc(8 / 4 * var(--global-section-padding));--section-padding-bottom:calc(4 / 4 * var(--global-section-padding));background-color:#ffffff;--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/b1d6f7c0-86b6-4efb-bb8e-35bc12113699');background-repeat:no-repeat;-webkit-background-position:center;background-position:center;-webkit-background-size:cover;background-size:cover;}@media (min-width: 769px){#section-1c92a0cc-d18f-4a88-96d3-a5f34620fae7 .elem-row{-webkit-flex-direction:row-reverse!important;-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important;}#section-1c92a0cc-d18f-4a88-96d3-a5f34620fae7 .custom-form__header{text-align:left!important;max-width:unset!important;}#section-1c92a0cc-d18f-4a88-96d3-a5f34620fae7 .form-row{display:table-column!important;}}#section-1c92a0cc-d18f-4a88-96d3-a5f34620fae7 .custom-form__img-block{display:none;}#section-1c92a0cc-d18f-4a88-96d3-a5f34620fae7 .custom-form__download-form{background-color:#FFF;padding:50px;}@media (max-width: 768px){#section-1c92a0cc-d18f-4a88-96d3-a5f34620fae7 .custom-form__download-form{padding:30px;}}#section-1c92a0cc-d18f-4a88-96d3-a5f34620fae7 .custom-form__form-row .form-col:nth-of-type(2):before{content:"Contact Us";font-size:30px!important;font-family:var(--global-primary-font-family);color:#000!important;}#section-1c92a0cc-d18f-4a88-96d3-a5f34620fae7 .custom-form__form-row .form-col:nth-of-type(2) .lp-form-group{margin-top:32px;}#section-1c92a0cc-d18f-4a88-96d3-a5f34620fae7 .custom-form__container{max-width:100%!important;}#section-1c92a0cc-d18f-4a88-96d3-a5f34620fae7 .field-required{text-transform:uppercase!important;}#section-1c92a0cc-d18f-4a88-96d3-a5f34620fae7 .custom-form__group .lp-label{text-transform:uppercase!important;}#section-1c92a0cc-d18f-4a88-96d3-a5f34620fae7 .uf-cus-sel-item:hover{background-color:#792617!important;color:#fff!important;}#section-1c92a0cc-d18f-4a88-96d3-a5f34620fae7.modal.is-background-color-light>button.close{padding:5px;}#section-1c92a0cc-d18f-4a88-96d3-a5f34620fae7.modal.is-background-color-light>button.close:before{content:'';width:100%;display:block;padding-bottom:100%;-webkit-clip-path:polygon(8% 0, 50% 42%, 92% 0, 100% 8%, 58% 50%, 100% 92%, 92% 100%, 50% 58%, 8% 100%, 0 92%, 42% 50%, 0 8%);clip-path:polygon(8% 0, 50% 42%, 92% 0, 100% 8%, 58% 50%, 100% 92%, 92% 100%, 50% 58%, 8% 100%, 0 92%, 42% 50%, 0 8%);background-color:#000;-webkit-transition:all .3s ease;transition:all .3s ease;}#section-1c92a0cc-d18f-4a88-96d3-a5f34620fae7.modal.is-background-color-light>button.close:hover:before{-webkit-transform:scale(1.2);-moz-transform:scale(1.2);-ms-transform:scale(1.2);transform:scale(1.2);}#section-1c92a0cc-d18f-4a88-96d3-a5f34620fae7.modal.is-background-color-light>button.close>img,#section-1c92a0cc-d18f-4a88-96d3-a5f34620fae7.modal.is-background-color-light>button.close .lp-close-icon{display:none;}#section-1c92a0cc-d18f-4a88-96d3-a5f34620fae7 .elem-row{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-left:-16px;margin-right:-16px;-webkit-box-flex-wrap:nowrap;-webkit-flex-wrap:nowrap;-ms-flex-wrap:nowrap;flex-wrap:nowrap;}@media (max-width: 767px){#section-1c92a0cc-d18f-4a88-96d3-a5f34620fae7 .elem-row{-webkit-box-flex-wrap:wrap;-webkit-flex-wrap:wrap;-ms-flex-wrap:wrap;flex-wrap:wrap;}}
34#section-1c92a0cc-d18f-4a88-96d3-a5f34620fae7 .elem-col{min-height:1px;padding-left:16px;padding-right:16px;}@media (max-width: 767px){#section-1c92a0cc-d18f-4a88-96d3-a5f34620fae7 .elem-col{-webkit-flex-basis:100%;-ms-flex-preferred-size:100%;flex-basis:100%;max-width:unset;}}#section-1c92a0cc-d18f-4a88-96d3-a5f34620fae7 .form-row{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;margin-left:-16px;margin-right:-16px;}#section-1c92a0cc-d18f-4a88-96d3-a5f34620fae7 .form-col{padding-left:16px;padding-right:16px;-webkit-flex:1 1 400px;-ms-flex:1 1 400px;flex:1 1 400px;}#section-1c92a0cc-d18f-4a88-96d3-a5f34620fae7 .form-col:last-child .custom-form__group{margin-bottom:20px;}@media (min-width: 768px){#section-1c92a0cc-d18f-4a88-96d3-a5f34620fae7 .form-col:last-child .custom-form__group{margin-bottom:30px;}}#section-1c92a0cc-d18f-4a88-96d3-a5f34620fae7 .lp-label{text-transform:none;}#section-1c92a0cc-d18f-4a88-96d3-a5f34620fae7 .lp-label.field-required:after{content:' *';}#section-1c92a0cc-d18f-4a88-96d3-a5f34620fae7 .custom-form{padding-top:80px;padding-bottom:80px;}@media (max-width: 767px){#section-1c92a0cc-d18f-4a88-96d3-a5f34620fae7 .custom-form{padding-top:50px;padding-bottom:50px;}}#section-1c92a0cc-d18f-4a88-96d3-a5f34620fae7 .custom-form__img-col{margin-bottom:30px;}@media (min-width: 768px){#section-1c92a0cc-d18f-4a88-96d3-a5f34620fae7 .custom-form__img-col{margin-bottom:0;-webkit-flex:0 0 50%;-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%;}}@media (min-width: 768px){#section-1c92a0cc-d18f-4a88-96d3-a5f34620fae7 .custom-form__form-col{-webkit-flex:1 1 auto;-ms-flex:1 1 auto;flex:1 1 auto;}}@media (min-width: 992px){#section-1c92a0cc-d18f-4a88-96d3-a5f34620fae7 .custom-form__form-col{-webkit-flex:1 1 auto;-ms-flex:1 1 auto;flex:1 1 auto;}}#section-1c92a0cc-d18f-4a88-96d3-a5f34620fae7 .custom-form__header{max-width:600px;margin-left:auto;margin-right:auto;margin-bottom:50px;text-align:center;}@media (max-width: 767px){#section-1c92a0cc-d18f-4a88-96d3-a5f34620fae7 .custom-form__header{margin-bottom:30px;}}#section-1c92a0cc-d18f-4a88-96d3-a5f34620fae7 .custom-form__heading{margin-bottom:24px;}@media (max-width: 767px){#section-1c92a0cc-d18f-4a88-96d3-a5f34620fae7 .custom-form__heading{margin-bottom:13px;}}#section-1c92a0cc-d18f-4a88-96d3-a5f34620fae7 .custom-form__description{margin-bottom:0;}#section-1c92a0cc-d18f-4a88-96d3-a5f34620fae7 .custom-form__img-block{width:100%;height:430px;background:currentColor center/cover no-repeat;}#section-1c92a0cc-d18f-4a88-96d3-a5f34620fae7 .custom-form__img-block img{height:100%;width:100%;object-fit:cover;}#section-1c92a0cc-d18f-4a88-96d3-a5f34620fae7 .custom-form__download-form{width:100%;margin-bottom:0;}@media (min-width: 992px){#section-1c92a0cc-d18f-4a88-96d3-a5f34620fae7 .custom-form__download-form{height:100%;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:start;-ms-flex-pack:start;-webkit-justify-content:flex-start;justify-content:flex-start;}}@media (min-width: 768px){#section-1c92a0cc-d18f-4a88-96d3-a5f34620fae7 .custom-form__footer{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-1c92a0cc-d18f-4a88-96d3-a5f34620fae7 .custom-form__container{max-width:1160px;margin-left:auto;margin-right:auto;}#section-1c92a0cc-d18f-4a88-96d3-a5f34620fae7 .form-field-descr{opacity:0.5;margin-top:8px;font-size:82%;}#section-1c92a0cc-d18f-4a88-96d3-a5f34620fae7 .lp-btn-icons{width:0;-webkit-transition:width .3s ease;transition:width .3s ease;display:inline-block;float:left;line-height:inherit;overflow:visible;position:relative;}#section-1c92a0cc-d18f-4a88-96d3-a5f34620fae7 .lp-btn-icons:before{content:'';display:inline-block;width:0px;height:1em;vertical-align:middle;}#section-1c92a0cc-d18f-4a88-96d3-a5f34620fae7 .lp-btn-icons .lp-btn-icon-loading{position:absolute;display:block;width:0;height:17px;opacity:0;top:50%;left:0;-webkit-transform:translateY(-50%);-moz-transform:translateY(-50%);-ms-transform:translateY(-50%);transform:translateY(-50%);-webkit-transition:width .3s ease,opacity .3s ease;transition:width .3s ease,opacity .3s ease;overflow:hidden;}#section-1c92a0cc-d18f-4a88-96d3-a5f34620fae7 .lp-btn-icons .lp-btn-icon-loading:before{content:none;display:block;background-color:currentColor;width:17px;height:17px;-webkit-clip-path:path('M8.5 0A 8.5 8.5 0 1 1 1.89 3.15L 4.23 5.04A 5.5 5.5 0 1 0 8.5 3Z');clip-path:path('M8.5 0A 8.5 8.5 0 1 1 1.89 3.15L 4.23 5.04A 5.5 5.5 0 1 0 8.5 3Z');-webkit-animation:i-spin 2s linear infinite;animation:i-spin 2s linear infinite;}#section-1c92a0cc-d18f-4a88-96d3-a5f34620fae7 .lp-btn-icons .lp-btn-icon-success{position:absolute;display:block;width:0;height:17px;opacity:0;top:50%;left:0;-webkit-transform:translateY(-50%) scale(1.02);-moz-transform:translateY(-50%) scale(1.02);-ms-transform:translateY(-50%) scale(1.02);transform:translateY(-50%) scale(1.02);-webkit-transition:width .3s ease,opacity .3s ease;transition:width .3s ease,opacity .3s ease;background-color:currentColor;-webkit-clip-path:path('M0 8.5 A 8.5 8.5 0 0 1 17 8.5 A 8.5 8.5 0 0 1 0 8.5 H 1.2 A 7.3 7.3 0 1 0 15.8 8.5 A 7.3 7.3 0 1 0 1.2 8.5 H0 m5.201-.344L7.4 10.35l4.436-4.45c.311-.202.58-.191.807.033.227.225.25.488.072.788L7.85 11.59c-.14.141-.291.212-.451.212-.16 0-.31-.061-.45-.183L4.338 8.99c-.192-.311-.172-.58.06-.806.231-.226.5-.235.803-.027');clip-path:path('M0 8.5 A 8.5 8.5 0 0 1 17 8.5 A 8.5 8.5 0 0 1 0 8.5 H 1.2 A 7.3 7.3 0 1 0 15.8 8.5 A 7.3 7.3 0 1 0 1.2 8.5 H0 m5.201-.344L7.4 10.35l4.436-4.45c.311-.202.58-.191.807.033.227.225.25.488.072.788L7.85 11.59c-.14.141-.291.212-.451.212-.16 0-.31-.061-.45-.183L4.338 8.99c-.192-.311-.172-.58.06-.806.231-.226.5-.235.803-.027');}#section-1c92a0cc-d18f-4a88-96d3-a5f34620fae7 .loading input,#section-1c92a0cc-d18f-4a88-96d3-a5f34620fae7 .success input,#section-1c92a0cc-d18f-4a88-96d3-a5f34620fae7 .loading select,#section-1c92a0cc-d18f-4a88-96d3-a5f34620fae7 .success select,#section-1c92a0cc-d18f-4a88-96d3-a5f34620fae7 .loading button,#section-1c92a0cc-d18f-4a88-96d3-a5f34620fae7 .success button{pointer-events:none;}#section-1c92a0cc-d18f-4a88-96d3-a5f34620fae7 .loading .lp-btn-icons,#section-1c92a0cc-d18f-4a88-96d3-a5f34620fae7 .success .lp-btn-icons{width:27px;}#section-1c92a0cc-d18f-4a88-96d3-a5f34620fae7 .loading .lp-btn-icons .lp-btn-icon-loading,#section-1c92a0cc-d18f-4a88-96d3-a5f34620fae7 .success .lp-btn-icons .lp-btn-icon-loading{width:17px;}#section-1c92a0cc-d18f-4a88-96d3-a5f34620fae7 .loading .lp-btn-icons .lp-btn-icon-success,#section-1c92a0cc-d18f-4a88-96d3-a5f34620fae7 .success .lp-btn-icons .lp-btn-icon-success{width:17px;}#section-1c92a0cc-d18f-4a88-96d3-a5f34620fae7 .loading .lp-btn-icons .lp-btn-icon-loading{opacity:1;}#section-1c92a0cc-d18f-4a88-96d3-a5f34620fae7 .loading .lp-btn-icons .lp-btn-icon-loading:before{content:'';}#section-1c92a0cc-d18f-4a88-96d3-a5f34620fae7 .success .lp-btn-icons .lp-btn-icon-success{opacity:1;}#section-1c92a0cc-d18f-4a88-96d3-a5f34620fae7 .js-backup-email{display:none;}#section-1c92a0cc-d18f-4a88-96d3-a5f34620fae7 .lp-h-pot{height:0;overflow:hidden;visibility:hidden;position:absolute;}#section-1c92a0cc-d18f-4a88-96d3-a5f34620fae7 .visually-hidden{position:absolute;overflow:hidden;clip:rect(0 0 0 0);width:1px;height:1px;margin:-1px;padding:0;border:0;}#section-1c92a0cc-d18f-4a88-96d3-a5f34620fae7 .uf-cus-sel-content{max-height:210px;overflow-y:auto;}#section-1c92a0cc-d18f-4a88-96d3-a5f34620fae7 .lp-input-group .uf-cus-sel-toggle{width:100%;}#section-1c92a0cc-d18f-4a88-96d3-a5f34620fae7 .uf-cus-sel{position:relative;width:100%;--optionHoverColor:hsl(var(--bgColor_H), calc(var(--bgColor_S) + 6% * var(--bgColor_darkenDir)), calc(var(--bgColor_L) + 14% * var(--bgColor_darkenDir)));}#section-1c92a0cc-d18f-4a88-96d3-a5f34620fae7 .uf-cus-sel-toggle{position:relative;display:block;width:100%;font-family:inherit;text-align:left;text-transform:none;cursor:pointer;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;-webkit-transition:all .25s;transition:all .25s;}#section-1c92a0cc-d18f-4a88-96d3-a5f34620fae7 .uf-cus-sel-toggle.is-placeholder{color:#999;}#section-1c92a0cc-d18f-4a88-96d3-a5f34620fae7 .uf-cus-sel-toggle:after{box-sizing:border-box;content:"";position:absolute;right:20px;top:50%;margin-top:0;height:10px;width:10px;border-top:2px solid currentColor;border-left:2px solid currentColor;-webkit-transform:rotateZ(-135deg) translateY(10px);-moz-transform:rotateZ(-135deg) translateY(10px);-ms-transform:rotateZ(-135deg) translateY(10px);transform:rotateZ(-135deg) translateY(10px);-webkit-transition:all .2s;transition:all .2s;}@media (min-width: 992px){#section-1c92a0cc-d18f-4a88-96d3-a5f34620fae7 .uf-cus-sel-toggle:after{right:25px;}}.uf-cus-sel.open #section-1c92a0cc-d18f-4a88-96d3-a5f34620fae7 .uf-cus-sel-toggle:after{-webkit-transform:rotate(45deg);-moz-transform:rotate(45deg);-ms-transform:rotate(45deg);transform:rotate(45deg);}#section-1c92a0cc-d18f-4a88-96d3-a5f34620fae7 .uf-cus-sel-menu{display:none;position:absolute;width:calc(100% + var(--uf-border-left, 0px) + var(--uf-border-right, 0px));top:100%;left:calc(0px - var(--uf-border-left, 0px));z-index:1;}@media (max-width: 768px){#section-1c92a0cc-d18f-4a88-96d3-a5f34620fae7 .uf-cus-sel-menu{position:relative;top:auto;}}#section-1c92a0cc-d18f-4a88-96d3-a5f34620fae7 .uf-cus-sel-content{width:100%;background-color:var(--bgColor, #000);border:1px solid #979797;border-top:none;}#section-1c92a0cc-d18f-4a88-96d3-a5f34620fae7 .uf-cus-sel-item{display:block;width:100%;padding:11px 24px;border:solid 1px transparent;background:transparent;border-radius:0;color:inherit;font-family:inherit;font-size:inherit;line-height:24px;text-align:left;text-transform:none;-webkit-transition:all .25s;transition:all .25s;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;cursor:pointer;}#section-1c92a0cc-d18f-4a88-96d3-a5f34620fae7 .uf-cus-sel-item:focus,#section-1c92a0cc-d18f-4a88-96d3-a5f34620fae7 .uf-cus-sel-item:hover{background-color:var(--optionHoverColor, #262222);}#section-1c92a0cc-d18f-4a88-96d3-a5f34620fae7 .uf-cus-sel-item.is-selected{font-weight:700;}#section-1c92a0cc-d18f-4a88-96d3-a5f34620fae7 .uf-cus-sel-item.is-disabled,#section-1c92a0cc-d18f-4a88-96d3-a5f34620fae7 .uf-cus-sel-item:disabled{display:none;}#section-1c92a0cc-d18f-4a88-96d3-a5f34620fae7 .lp-text--subtitle{margin-left:auto;margin-right:auto;}#section-1c92a0cc-d18f-4a88-96d3-a5f34620fae7 .custom-form__checkbox-container{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex-direction:column-reverse;-ms-flex-direction:column-reverse;flex-direction:column-reverse;gap:8px;}#section-1c92a0cc-d18f-4a88-96d3-a5f34620fae7 .custom-form__checkbox-container .lp-label{opacity:1;}#section-1c92a0cc-d18f-4a88-96d3-a5f34620fae7 .custom-form__checkboxes-label{font-size:14px;line-height:1.43;font-weight:600;margin-bottom:0;}#section-1c92a0cc-d18f-4a88-96d3-a5f34620fae7 .custom-form__checkboxes-description{font-size:14px;line-height:1.43;font-weight:400;margin-bottom:0;}#section-1c92a0cc-d18f-4a88-96d3-a5f34620fae7 .custom-form__checkbox-item{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;gap:10px;}#section-1c92a0cc-d18f-4a88-96d3-a5f34620fae7 .custom-form__checkbox-label{cursor:pointer;font-size:inherit;line-height:1.4;margin:0;}#section-1c92a0cc-d18f-4a88-96d3-a5f34620fae7>div:last-child{padding-top:calc(8 / 4 * var(--global-section-padding))!important;padding-bottom:calc(4 / 4 * var(--global-section-padding))!important;}@media (min-width: 960px) and (orientation: landscape){#section-1c92a0cc-d18f-4a88-96d3-a5f34620fae7{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/b1d6f7c0-86b6-4efb-bb8e-35bc12113699');}}@media (min-width: 1280px) and (orientation: landscape){#section-1c92a0cc-d18f-4a88-96d3-a5f34620fae7{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/b1d6f7c0-86b6-4efb-bb8e-35bc12113699');}}@media (min-width: 1920px) and (orientation: landscape){#section-1c92a0cc-d18f-4a88-96d3-a5f34620fae7{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=2560/https://media-production.lp-cdn.com/media/b1d6f7c0-86b6-4efb-bb8e-35bc12113699');}}@media (min-width: 2560px) and (orientation: landscape){#section-1c92a0cc-d18f-4a88-96d3-a5f34620fae7{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=3456/https://media-production.lp-cdn.com/media/b1d6f7c0-86b6-4efb-bb8e-35bc12113699');}}#section-1c92a0cc-d18f-4a88-96d3-a5f34620fae7 .lp-input::-webkit-input-placeholder{text-transform:none;}#section-1c92a0cc-d18f-4a88-96d3-a5f34620fae7 .lp-input::-moz-placeholder{text-transform:none;}#section-1c92a0cc-d18f-4a88-96d3-a5f34620fae7 .lp-input:-ms-input-placeholder{text-transform:none;}#section-1c92a0cc-d18f-4a88-96d3-a5f34620fae7 .lp-input::placeholder{text-transform:none;}#section-0431207f-8ab4-4735-aa08-dfbaadfcb5dc{color:#FFF;--fontColor:#FFF;--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-padding-top:calc(4 / 4 * var(--global-section-padding));--section-padding-bottom:calc(4 / 4 * var(--global-section-padding));background-color:#000000;}#section-0431207f-8ab4-4735-aa08-dfbaadfcb5dc .ig-feed{padding:80px 0;}#section-0431207f-8ab4-4735-aa08-dfbaadfcb5dc .title{margin:0 20px 0 0;}#section-0431207f-8ab4-4735-aa08-dfbaadfcb5dc .instagram-link{display:block;margin:0 20px 0 0;color:var(--global-primary-font-color);}#section-0431207f-8ab4-4735-aa08-dfbaadfcb5dc .instagram-link .instagram-name{display:inline;margin:0;}#section-0431207f-8ab4-4735-aa08-dfbaadfcb5dc .title+.instagram-link{margin-top:5px;}#section-0431207f-8ab4-4735-aa08-dfbaadfcb5dc .top-wrapper{margin-bottom:20px;
34display:-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-end;-webkit-box-align:flex-end;-ms-flex-align:flex-end;align-items:flex-end;}#section-0431207f-8ab4-4735-aa08-dfbaadfcb5dc .top-right{margin-left:auto;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;}#section-0431207f-8ab4-4735-aa08-dfbaadfcb5dc .follow-button{margin:20px 0 0 0;display:block;}#section-0431207f-8ab4-4735-aa08-dfbaadfcb5dc .nav-button{margin-top:20px;-webkit-flex:0 0 auto;-ms-flex:0 0 auto;flex:0 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;width:42px;color:#1a1a1a;border:1px solid currentColor;cursor:pointer;transition-property:color,background-color,border-color;transition-duration:.3s;transition-timing-function:ease;}#section-0431207f-8ab4-4735-aa08-dfbaadfcb5dc .nav-button.slick-disabled{opacity:0.6;pointer-events:none;cursor:default;}#section-0431207f-8ab4-4735-aa08-dfbaadfcb5dc .nav-button:not(.slick-disabled):hover{background-color:#1a1a1a;border-color:#1a1a1a;color:#fff;}#section-0431207f-8ab4-4735-aa08-dfbaadfcb5dc .top-nav-prev{margin-left:30px;}#section-0431207f-8ab4-4735-aa08-dfbaadfcb5dc .top-nav-next{margin-left:10px;}#section-0431207f-8ab4-4735-aa08-dfbaadfcb5dc .hidden-control{display:none;}#section-0431207f-8ab4-4735-aa08-dfbaadfcb5dc .slide-single,#section-0431207f-8ab4-4735-aa08-dfbaadfcb5dc .slide-multi{position:relative;-webkit-flex:0 0 auto;-ms-flex:0 0 auto;flex:0 0 auto;width:calc(100% / 3);padding:10px;}#section-0431207f-8ab4-4735-aa08-dfbaadfcb5dc .slide-single:first-child,#section-0431207f-8ab4-4735-aa08-dfbaadfcb5dc .slide-multi:first-child{margin-left:auto;}#section-0431207f-8ab4-4735-aa08-dfbaadfcb5dc .slide-single:last-child,#section-0431207f-8ab4-4735-aa08-dfbaadfcb5dc .slide-multi:last-child{margin-right:auto;}#section-0431207f-8ab4-4735-aa08-dfbaadfcb5dc .slide-single.type2,#section-0431207f-8ab4-4735-aa08-dfbaadfcb5dc .slide-multi.type2{width:calc(100% / 5 * 2);}#section-0431207f-8ab4-4735-aa08-dfbaadfcb5dc .slide-single.type3,#section-0431207f-8ab4-4735-aa08-dfbaadfcb5dc .slide-multi.type3{width:calc(100% / 2);}#section-0431207f-8ab4-4735-aa08-dfbaadfcb5dc .slide-single.type4,#section-0431207f-8ab4-4735-aa08-dfbaadfcb5dc .slide-multi.type4{width:calc(100% / 2);}#section-0431207f-8ab4-4735-aa08-dfbaadfcb5dc .slide-single.type5,#section-0431207f-8ab4-4735-aa08-dfbaadfcb5dc .slide-multi.type5{width:100%;}@media (max-width: 440px){#section-0431207f-8ab4-4735-aa08-dfbaadfcb5dc .ig-feed-container{padding:0;width:100%;}}#section-0431207f-8ab4-4735-aa08-dfbaadfcb5dc .ig-feed-feed{width:calc(100% + 20px);position:relative;left:-10px;overflow:hidden;}#section-0431207f-8ab4-4735-aa08-dfbaadfcb5dc .ig-feed-slider{width:100%;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;}#section-0431207f-8ab4-4735-aa08-dfbaadfcb5dc .ig-feed-item__img{position:absolute;left:-1px;top:-1px;bottom:-1px;right:-1px;background:center/cover no-repeat;}#section-0431207f-8ab4-4735-aa08-dfbaadfcb5dc .ig-feed-item__info{position:absolute;top:0;left:0;bottom:0;right: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;padding:10%;overflow:hidden;color:#fff;text-align:center;background-color:rgba(0, 0, 0, 0.8);opacity:0;-webkit-transition:opacity .3s ease;transition:opacity .3s ease;}#section-0431207f-8ab4-4735-aa08-dfbaadfcb5dc .ig-feed-item:hover .ig-feed-item__info{opacity:1;}#section-0431207f-8ab4-4735-aa08-dfbaadfcb5dc .ig-feed-item__caption{text-overflow:ellipsis;-webkit-line-clamp:4;display:-webkit-box;-webkit-box-orient:vertical;overflow:hidden;}#section-0431207f-8ab4-4735-aa08-dfbaadfcb5dc .video-icon{position:absolute;width:50px;height:50px;background-color:#fff;left:50%;
34top:50%;-webkit-transform:translate(-50%, -50%);-moz-transform:translate(-50%, -50%);-ms-transform:translate(-50%, -50%);transform:translate(-50%, -50%);border-radius:50%;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-transition:opacity .2s ease;transition:opacity .2s ease;opacity:1;}#section-0431207f-8ab4-4735-aa08-dfbaadfcb5dc .video-icon::before{content:'';display:block;width:0px;height:0px;border-style:solid;position:relative;left:1px;border-width:7.5px 0px 7.5px 11px;border-color:transparent transparent transparent #292826;}#section-0431207f-8ab4-4735-aa08-dfbaadfcb5dc .ig-feed-item:hover .video-icon{opacity:0;}#section-0431207f-8ab4-4735-aa08-dfbaadfcb5dc .slide-single .ig-feed-item__info{padding:15%;}#section-0431207f-8ab4-4735-aa08-dfbaadfcb5dc .slide-single .ig-feed-item__caption{-webkit-line-clamp:7;}#section-0431207f-8ab4-4735-aa08-dfbaadfcb5dc .ig-feed-item{background-color:var(--tileColor, #000);display:block;width:100%;padding-bottom:125%;overflow:hidden;position:relative;}#section-0431207f-8ab4-4735-aa08-dfbaadfcb5dc .slide-multi::before{content:'';display:block;width:100%;padding-bottom:125%;}#section-0431207f-8ab4-4735-aa08-dfbaadfcb5dc .slide-multi-content{padding:10px;position:absolute;width:100%;height:100%;left:0;top:0;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;-webkit-box-flex-wrap:wrap;-webkit-flex-wrap:wrap;-ms-flex-wrap:wrap;flex-wrap:wrap;-webkit-box-pack:justify;-webkit-justify-content:space-between;justify-content:space-between;-webkit-align-items:flex-start;-webkit-box-align:flex-start;-ms-flex-align:flex-start;align-items:flex-start;overflow:hidden;}#section-0431207f-8ab4-4735-aa08-dfbaadfcb5dc .slide-multi-content .ig-feed-item{width:calc(50% - 10px);padding-bottom:calc(62.5% - 12.5px);}#section-0431207f-8ab4-4735-aa08-dfbaadfcb5dc .slide-multi-content .ig-feed-item:nth-child(n + 3){-webkit-align-self:flex-end;-ms-flex-item-align:end;align-self:flex-end;}#section-0431207f-8ab4-4735-aa08-dfbaadfcb5dc .bottom-controls{width:100%;display:none;-webkit-box-pack:center;-ms-flex-pack:center;-webkit-justify-content:center;justify-content:center;}#section-0431207f-8ab4-4735-aa08-dfbaadfcb5dc .bottom-nav-button{-webkit-flex:0 0 auto;-ms-flex:0 0 auto;flex:0 0 auto;margin:20px 10px 0;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;-webkit-box-pack:center;-ms-flex-pack:center;-webkit-justify-content:center;justify-content:center;width:48px;height:48px;color:#1a1a1a;border:1px solid currentColor;cursor:pointer;}#section-0431207f-8ab4-4735-aa08-dfbaadfcb5dc .bottom-nav-button.slick-disabled{opacity:0.6;pointer-events:none;cursor:default;}@media (max-width: 767px){#section-0431207f-8ab4-4735-aa08-dfbaadfcb5dc .top-right,#section-0431207f-8ab4-4735-aa08-dfbaadfcb5dc .top-left{width:100%;-webkit-box-pack:center;-ms-flex-pack:center;-webkit-justify-content:center;justify-content:center;}#section-0431207f-8ab4-4735-aa08-dfbaadfcb5dc .title,#section-0431207f-8ab4-4735-aa08-dfbaadfcb5dc .instagram-link{margin-right:0;text-align:center;}#section-0431207f-8ab4-4735-aa08-dfbaadfcb5dc .top-nav-prev,#section-0431207f-8ab4-4735-aa08-dfbaadfcb5dc .top-nav-next{display:none;}#section-0431207f-8ab4-4735-aa08-dfbaadfcb5dc .bottom-controls{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;}}#section-0431207f-8ab4-4735-aa08-dfbaadfcb5dc .ig-feed{display:none;}#section-0431207f-8ab4-4735-aa08-dfbaadfcb5dc .has-data .ig-feed{display:block;}#section-0431207f-8ab4-4735-aa08-dfbaadfcb5dc.has-data .ig-feed,#section-0431207f-8ab4-4735-aa08-dfbaadfcb5dc#section-temp .ig-feed{display:block;}#section-0431207f-8ab4-4735-aa08-dfbaadfcb5dc>div:last-child{padding-top:calc(4 / 4 * var(--global-section-padding))!important;padding-bottom:calc(4 / 4 * var(--global-section-padding))!important;}#modal-global-contact-us .close{z-index:11;}#modal-global-contact-us .description{margin-bottom:15px;}#modal-global-contact-us .close:focus{outline:0;}#modal-global-contact-us .contact-form{padding:70px 0;width:100%;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;position:relative;color:var(--colorTxt, #ffffff);-webkit-background-size:cover;background-size:cover;}#modal-global-contact-us .contact-form:after{content:'';position:absolute;display:block;background-color:rgba(2, 5, 7, 0.85);height:100%;width:100%;z-index:0;}
34#modal-global-contact-us .contact-form.loading textarea,#modal-global-contact-us .contact-form.loading input,#modal-global-contact-us .contact-form.loading label,#modal-global-contact-us .contact-form.loading button{pointer-events:none;opacity:.5;}#modal-global-contact-us .contact-form.loading .submit-icon{display:inline-block;}#modal-global-contact-us .contact-form .submit-icon{display:none;}#modal-global-contact-us .contact-form .contact-form-close{width:40px;height:50px;display:block;padding:0;border:none;background-color:transparent;cursor:pointer;position:absolute;right:50px;top:50px;z-index:110;}@media (max-width: 560px){#modal-global-contact-us .contact-form .contact-form-close{right:20px;top:20px;width:30px;height:40px;}}#modal-global-contact-us .contact-form .contact-form-close:before,#modal-global-contact-us .contact-form .contact-form-close:after{content:'';display:block;width:40px;height:1px;background-color:#fff;position:absolute;left:50%;top:50%;}@media (max-width: 560px){#modal-global-contact-us .contact-form .contact-form-close:before,#modal-global-contact-us .contact-form .contact-form-close:after{width:30px;}}#modal-global-contact-us .contact-form .contact-form-close: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);}#modal-global-contact-us .contact-form .contact-form-close:after{-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);}#modal-global-contact-us .contact-form .container{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;max-width:1440px;width:100%;padding:0 160px;margin:0 auto;z-index:10;}@media (max-width: 1320px){#modal-global-contact-us .contact-form .container{padding:0 80px;}}@media (max-width: 1200px){#modal-global-contact-us .contact-form .container{padding:0 30px;}}@media (max-width: 900px){#modal-global-contact-us .contact-form .container{-webkit-box-flex-wrap:wrap;-webkit-flex-wrap:wrap;-ms-flex-wrap:wrap;flex-wrap:wrap;}}@media (max-width: 560px){#modal-global-contact-us .contact-form .container{padding:0 20px;}}#modal-global-contact-us .contact-form h2{margin:0 0 18px 0;padding:0;}@media (max-width: 560px){#modal-global-contact-us .contact-form h2{margin:0 0 24px 0;}}#modal-global-contact-us .contact-form .section-details{width:50%;}@media (max-width: 1200px){#modal-global-contact-us .contact-form .section-details{padding-right:30px;}}@media (max-width: 900px){#modal-global-contact-us .contact-form .section-details{width:100%;padding-right:0;max-width:700px;margin-bottom:38px;}}#modal-global-contact-us .contact-form .section-message{width:50%;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;position:relative;}@media (max-width: 1200px){#modal-global-contact-us .contact-form .section-message{padding-left:30px;}}@media (max-width: 900px){#modal-global-contact-us .contact-form .section-message{width:100%;padding-left:0;-webkit-box-pack:start;-ms-flex-pack:start;-webkit-justify-content:flex-start;justify-content:flex-start;}}#modal-global-contact-us .contact-form .section-message:after{content:'';position:absolute;display:block;width:1px;background:#999999;height:calc(100% + 50px);left:0;top:-20px;}@media (max-width: 900px){#modal-global-contact-us .contact-form .section-message:after{display:none;}}#modal-global-contact-us .contact-form .section-message form{max-width:435px;width:100%;}#modal-global-contact-us .contact-form .section-message form .submitButton{width:auto;}@media (max-width: 560px){#modal-global-contact-us .contact-form .section-message form .submitButton{width:100%;}}@media (max-width: 900px){#modal-global-contact-us .contact-form .section-message form{max-width:700px;}}#modal-global-contact-us .contact-form .section-message form.loading textarea,#modal
34-global-contact-us .contact-form .section-message form.loading input,#modal-global-contact-us .contact-form .section-message form.loading label,#modal-global-contact-us .contact-form .section-message form.loading button{pointer-events:none;opacity:.5;}#modal-global-contact-us .contact-form .section-message form.loading .label-loading{display:inline;}#modal-global-contact-us .contact-form .section-message form.loading .label-success,#modal-global-contact-us .contact-form .section-message form.loading .label-initial{display:none;}#modal-global-contact-us .contact-form .section-message form.loading .submit-icon{display:inline-block;}#modal-global-contact-us .contact-form .section-message form.success .label-loading,#modal-global-contact-us .contact-form .section-message form.success .label-initial{display:none;}#modal-global-contact-us .contact-form .section-message form.success .label-success{display:inline;}#modal-global-contact-us .contact-form .section-message .label-loading,#modal-global-contact-us .contact-form .section-message .label-success{display:none;}#modal-global-contact-us .contact-form .section-message .submit-icon{display:none;}#modal-global-contact-us .contact-form .section-message h2{margin-bottom:28px;}#modal-global-contact-us .contact-form .section-message .form-group_modal{-webkit-box-flex-wrap:wrap;-webkit-flex-wrap:wrap;-ms-flex-wrap:wrap;flex-wrap:wrap;margin-bottom:39px;}#modal-global-contact-us .contact-form .section-message .form-group_modal input{border:none;border-bottom:1px solid #c0c0c0;display:block;width:100%;background:transparent;color:#ffffff;height:22px;font-size:16px;}@media (max-width: 560px){#modal-global-contact-us .contact-form .section-message .form-group_modal input{height:25px;}}#modal-global-contact-us .contact-form .section-message .form-group-textarea{margin-bottom:30px;}@media (max-width: 560px){#modal-global-contact-us .contact-form .section-message .form-group-textarea{margin-bottom:23px;}}#modal-global-contact-us .contact-form .section-message label{display:block;width:100%;line-height:1.63;letter-spacing:1px;color:#c0c0c0;font-weight:bold;text-transform:uppercase;}#modal-global-contact-us .contact-form .section-message textarea{resize:none;height:105px;width:100%;background:transparent;border:solid 1px #ffffff;margin:23px 0 0 0;color:#ffffff;padding:10px;font-size:16px;}@media (max-width: 560px){#modal-global-contact-us .contact-form .section-message textarea{margin:10px 0 0 0;height:89px;}}#modal-global-contact-us .contact-form .info-zone h4{margin:0 0 16px 0;}@media (max-width: 560px){#modal-global-contact-us .contact-form .info-zone h4{margin:0 0 13px 0;}}#modal-global-contact-us .contact-form .info-zone .type_user:nth-of-type(2){margin-bottom:30px;}@media (max-width: 560px){#modal-global-contact-us .contact-form .info-zone .type_user{margin-bottom:58px;}}#modal-global-contact-us .contact-form .info-zone_contact{margin-bottom:23px;}#modal-global-contact-us .contact-form .info-zone_contact:nth-last-child(2){margin-bottom:30px;}#modal-global-contact-us .contact-form .info-zone_contact_cell{position:relative;padding-left:45px;margin-bottom:22px;font-weight:bold;font-size:14px;max-width:90%;letter-spacing:1.5px;text-transform:uppercase;}@media (max-width: 560px){#modal-global-contact-us .contact-form .info-zone_contact_cell{padding-left:0;}}#modal-global-contact-us .contact-form .info-zone_contact_cell:last-of-type{margin-bottom:0;}#modal-global-contact-us .contact-form .info-zone_contact_cell i{position:absolute;left:0;top:0;display:block;}@media (max-width: 560px){#modal-global-contact-us .contact-form .info-zone_contact_cell i{position:relative;margin-bottom:7px;}}#modal-global-contact-us .contact-form .info-zone_contact_cell i svg path{fill:var(--colorTxt);}#modal-global-contact-us .contact-form .info-zone_contact_cell div{padding:0;}#modal-global-contact-us .contact-form .info-zone_contact_cell .contact_title{line-height:1;margin-bottom:5px;}#modal-global-contact-us .contact-form .info-zone_contact_link{color:var(--colorTxt);}@media (max-width: 560px){#modal-global-contact-us .contact-form .info-zone .type_user:nth-of-type(2){margin-bottom:58px;}}@media (max-width: 560px){#modal-global-contact-us .contact-form .hours_box{margin-bottom:52px;}}#modal-global-contact-us .contact-form .socials_zone{padding-left:45px;}@media (max-width: 560px){#modal-global-contact-us .contact-form .socials_zone{padding-left:0;}}#modal-global-contact-us .contact-form .socials_zone li{margin-bottom:10px;}#modal-global-contact-us .contact-form .socials_zone li:not(:last-child){padding-right:34px;}@media (max-width: 560px){#modal-global-contact-us .contact-form .socials_zone li:not(:last-child){padding-right:12px;}}#modal-global-contact-us .contact-form .success-message.success{margin-top:15px;color:var(--colorTxt);position:absolute;}#modal-global-contact-us .contact-form .success-message.hide{opacity:0;}#modal-global-contact-us .contact-form.one_contact .info-zone .type_user{margin-bottom:73px;}@media (max-width: 768px){#modal-global-contact-us .contact-form.one_contact .info-zone .type_user{margin-bottom:51px;}}@media (max-width: 560px){#modal-global-contact-us .contact-form.one_contact .info-zone .type_user{margin-bottom:40px;}}#modal-global-contact-us .contact-form.one_contact .hours_box{margin-bottom:38px;}#modal-global-contact-us .contact-form.no_icon .info-zone_contact_cell{padding-left:0;}#modal-global-contact-us .contact-form.no_icon .info-zone_contact_cell i{display:none;}#modal-global-contact-us .contact-form.no_icon .socials_zone{padding-left:0;}#modal-global-contact-us .contact-form .pre-line{white-space:pre-line;}#modal-global-contact-us .lp-input-group{margin-bottom:40px;}#modal-global-contact-us .lp-input-group.m16{margin-bottom:16px;}#modal-global-contact-us .lp-disclaimer{margin-top:0;}#modal-global-contact-us .lp-input{border-color:#ffffff;}#modal-global-contact-us .lp-input:focus,#modal-global-contact-us .lp-input:active{border-color:#5e5e5e;}nav{color:#fff;-webkit-transition:0.2s -webkit-transform ease;transition:0.2s transform ease;z-index:102;}nav.scroll .logo .logo__img.light{display:block;}nav.scroll .logo .logo__img.dark{display:none;}nav .header{height:100px;-webkit-transform:translateY(24px);-moz-transform:translateY(24px);-ms-transform:translateY(24px);transform:translateY(24px);}@media (max-width: 768px){nav .header{-webkit-transform:translateY(0);-moz-transform:translateY(0);-ms-transform:translateY(0);transform:translateY(0);}}nav .header .container{max-width:1440px;padding:0 15px;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;height:100%;}@media (max-width: 1024px){nav .header .container{padding:0 15px;}}nav .header .navbar{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;
34-webkit-justify-content:space-between;justify-content:space-between;min-height:70px;padding:8px 0 7px;width:100%;}nav .header .logo{z-index:911;position:relative;margin-right:10px;overflow:hidden;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex-shrink:0;-ms-flex-negative:0;flex-shrink:0;}nav .header .logo a{overflow:hidden;display:inline-block;}nav .header .logo img{max-width:100%;max-height:52px;vertical-align:top;object-fit:contain;}@media (max-width: 1024px){nav .header .logo img{max-height:50px;}}nav .header .logo img.dark{display:none;}@media(max-width: 1024px){nav .header .logo img.dark{display:none!important;}}nav .header .logo img.light{display:block;}@media(max-width: 1024px){nav .header .logo img.light{display:inline-block!important;}}nav .header .logo__link{-webkit-animation-duration:1s;animation-duration:1s;overflow:hidden;}nav .header .logo__img.light{display:block;}nav .header .logo__img.dark{display:none;}nav .header .navigation{margin-left:auto;color:inherit;}@media (max-width: 1024px){nav .header .navigation{display:none;}}nav .header .navigation ul{position:relative;list-style-type:none;padding-left:0;margin-bottom:0;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;}nav .header .navigation ul li a span,nav .header .navigation ul li button span{position:relative;white-space:nowrap;}nav .header .navigation ul li a span:after,nav .header .navigation ul li button span:after{content:'';position:absolute;display:block;height:1px;background-color:#c6c6c6;width:0;bottom:0;left:0;-webkit-transition:all .2s ease;transition:all .2s ease;}nav .header .navigation ul li a:hover span:after,nav .header .navigation ul li button:hover span:after{width:100%;-webkit-transition:all .2s ease;transition:all .2s ease;}nav .header .navigation__item{padding:0;margin:0;}nav .header .navigation__link{display:block;padding:12px 13px;color:inherit;position:relative;font-size:13px;line-height:1.08;font-weight:700;letter-spacing:1.5px;background-color:transparent;border:none;cursor:pointer;text-align:left;text-transform:uppercase;}@media (max-width: 1280px){nav .header .navigation__link{padding:19px 10px;font-size:11.5px;}}nav .header .navigation__link .sub-nav-container .navigation__link:hover{background:#000000;}nav .header .navigation .sub-nav{position:absolute;bottom:0;padding:0;opacity:0;visibility:hidden;-webkit-transition:all .2s ease;transition:all .2s ease;-webkit-transform:translate(0, calc(100% + -10px));-moz-transform:translate(0, calc(100% + -10px));-ms-transform:translate(0, calc(100% + -10px));transform:translate(0, calc(100% + -10px));}nav .header .navigation .sub-nav ul{margin:0;padding:12px 0;display:block;background-color:rgba(26, 26, 26, .75);max-width:300px;}nav .header .navigation .sub-nav__item{position:relative;display:block;margin:0;text-align:left;}nav .header .navigation .sub-nav__item:last-child{border-bottom-color:transparent;}nav .header .navigation .sub-nav__link{display:block;max-width:300px;color:#ffffff;margin:0;line-height:1;letter-spacing:1.5px;padding:19px 13px;-webkit-transition:all .2s ease;transition:all .2s ease;}nav .header .navigation .sub-nav.visible{opacity:1;visibility:visible;-webkit-transform:translate(0, 100%);-moz-transform:translate(0, 100%);-ms-transform:translate(0, 100%);transform:translate(0, 100%);}nav .header .navigation .sub-nav.visible+.navigation__link{background:#000000;}nav .header .hamburger{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;border:none;padding:28px 13px;background-color:transparent;color:inherit;vertical-align:middle;cursor:pointer;outline:none;z-index:915;opacity:1;}nav .header .hamburger__bars{position:relative;display:block;width:26px;height:2px;background-color:currentColor;-webkit-transition:0.2s -webkit-transform ease;transition:0.2s transform ease;}nav .header .hamburger__bars:before,nav .header .hamburger__bars:after{content:'';display:block;width:26px;height:2px;background-color:currentColor;position:absolute;
34left:0;top:0;-webkit-transform:translate3d(0,0,0);-moz-transform:translate3d(0,0,0);-ms-transform:translate3d(0,0,0);transform:translate3d(0,0,0);-webkit-transition:0.2s -webkit-transform ease;transition:0.2s transform ease;}nav .header .hamburger__bars:before{-webkit-transform:translateY(-9px);-moz-transform:translateY(-9px);-ms-transform:translateY(-9px);transform:translateY(-9px);}nav .header .hamburger__bars:after{-webkit-transform:translateY(9px);-moz-transform:translateY(9px);-ms-transform:translateY(9px);transform:translateY(9px);}nav .header .hamburger:hover{text-shadow:.5px 0 0,-.5px 0 0;}nav .header .hamburger:hover .hamburger__bars:before{-webkit-transform:translateY(-7px);-moz-transform:translateY(-7px);-ms-transform:translateY(-7px);transform:translateY(-7px);}nav .header .hamburger:hover .hamburger__bars:after{-webkit-transform:translateY(7px);-moz-transform:translateY(7px);-ms-transform:translateY(7px);transform:translateY(7px);}nav .header .hamburger.active .hamburger__bars{-webkit-transform:rotate(135deg);-moz-transform:rotate(135deg);-ms-transform:rotate(135deg);transform:rotate(135deg);opacity:0;}nav .header .hamburger.active .hamburger__bars:before{display:none;}nav .header .hamburger.active .hamburger__bars:after{-webkit-transform:rotate(270deg);-moz-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg);}nav .content-container{-webkit-transition:0.2s all ease;transition:0.2s all ease;}nav.scroll .content-container,nav .scroll .content-container{-webkit-transform:translateY(0);-moz-transform:translateY(0);-ms-transform:translateY(0);transform:translateY(0);}nav.scroll .content-container .navigation__link,nav .scroll .content-container .navigation__link{visibility:visible!important;}nav.scroll .content-container .navigation .sub-nav,nav .scroll .content-container .navigation .sub-nav{-webkit-transform:translate(0, calc(100% + -10px));-moz-transform:translate(0, calc(100% + -10px));-ms-transform:translate(0, calc(100% + -10px));transform:translate(0, calc(100% + -10px));}nav.scroll .content-container .navigation .sub-nav ul,nav .scroll .content-container .navigation .sub-nav ul{padding:0;}nav.scroll .content-container .navigation .sub-nav.visible,nav .scroll .content-container .navigation .sub-nav.visible{opacity:1;visibility:visible;-webkit-transform:translate(0, calc(100% + 23px));-moz-transform:translate(0, calc(100% + 23px));-ms-transform:translate(0, calc(100% + 23px));transform:translate(0, calc(100% + 23px));}nav .dark-opening{background-color:rgb(26, 26, 26)!important;}nav nav.dark-opening .header{margin-top:0;}nav #ctaShowButton,nav #ctaMessage{display:none!important;}nav #ctaShowButton{display:none;opacity:0;pointer-events:none;}nav .search{cursor:pointer;}nav .search svg{display:block;}nav .buttons-wrapper{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;-webkit-flex-shrink:0;-ms-flex-negative:0;flex-shrink:0;}body>div.sidemenu{background-color:rgba(43, 43, 45, 1);color:rgba(255, 255, 255, 1);--fontColor:rgba(255, 255, 255, 1);--fontColor_H:0;--fontColor_S:0%;--fontColor_L:100%;--fontColor_A:1;--fontColor_darkenDir:-1;--bgColor:rgba(43, 43, 45, 1);--bgColor_H:240;--bgColor_S:2%;--bgColor_L:17%;--bgColor_A:1;--bgColor_darkenDir:1;}body>div.sidemenu{background-color:transparent;}body>div.sidemenu .wrapper{--linkColor:hsla( 35 var(--fontColor_H, 0), 36 var(--fontColor_S, 0%), 37 calc(50% + var(--fontColor_darkenDir, -1) * 13%), 38 var(--fontColor_A, 1) 39 );opacity:1;-webkit-transition:-webkit-transform 0s linear,opacity 0.2s ease-out;transition:transform 0s linear,opacity 0.2s ease-out;background-color:var(--bgColor, rgba(0, 0, 0, 0.9));color:var(--fontColor, #fff);position:fixed;top:0;right:0;height:100%;width:100vw;}body>div.sidemenu .holder{font-size:20px;top:150px;left:auto;right:0;position:absolute;max-width:1200px;width:100%;height:100%;}body>div.sidemenu .title{font-size:12px;text-align:right;width:100%;padding-right:150px;margin-bottom:30px;text-transform:uppercase;letter-spacing:1px;font-weight:bold;}@media (max-width: 992px){body>div.sidemenu .title{padding-right:20px;}}body>div.sidemenu .section-options{margin:0 auto;overflow-y:scroll;height:calc(100% - 200px);}body>div.sidemenu .section-options::-webkit-scrollbar{width:7px;}body>div.sidemenu .section-options::-webkit-scrollbar-thumb{border-radius:10px;background-color:#4d5259;}body>div.sidemenu .section-options .option{text-align:right;border-bottom:2px none;}body>div.sidemenu .section-options a,body>div.sidemenu .section-options button{font-family:var(--global-primary-font-family);line-height:70px;color:var(--linkColor);font-size:35px;-webkit-text-decoration:none;text-decoration:none;display:inline-block;padding-right:150px;border:none;background-color:transparent;cursor:pointer;border-bottom:2px solid transparent;text-transform:none;-webkit-transition:.3s linear all;transition:.3s linear all;}body>div.sidemenu .section-options a:hover:not(.sub-nav-link),body>div.sidemenu .section-options button:hover:not(.sub-nav-link){color:var(--fontColor, #fff);border-bottom:2px solid var(--fontColor, #fff);}@media (max-width: 992px){body>div.sidemenu .section-options a,body>div.sidemenu .section-options button{padding-right:20px;}}body>div.sidemenu .sidemenu-close{width:24px;height:24px;display:block;padding:0;border:none;background-color:transparent;cursor:pointer;-webkit-transition:all .3s ease;transition:all .3s ease;}body>
39div.sidemenu .sidemenu-close:hover{-webkit-transform:scale(1.3);-moz-transform:scale(1.3);-ms-transform:scale(1.3);transform:scale(1.3);}body>div.sidemenu .sidemenu-close:before,body>div.sidemenu .sidemenu-close:after{content:'';display:block;width:30px;height:2px;background-color:var(--fontColor, #fff);position:absolute;left:50%;top:50%;}body>div.sidemenu .sidemenu-close: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-close:after{-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 button.toggle{border:none;background:none;position:absolute;right:76px;top:74px;}body>div.sidemenu button.toggle:focus,body>div.sidemenu button.toggle:active{outline:none;}body>div.sidemenu .sub-nav-container{position:relative;}body>div.sidemenu .sub-nav{opacity:0;visibility:hidden;-webkit-transition:all .5s ease;transition:all .5s ease;max-height:0;}body>div.sidemenu .sub-nav ul{margin:0;padding-top:10px;}body>div.sidemenu .sub-nav.visible{opacity:1;visibility:visible;max-height:10000px;}body>div.sidemenu .sub-nav a,body>div.sidemenu .sub-nav button{font-size:25px;line-height:45px;}body>div.sidemenu .sub-nav a:hover,body>div.sidemenu .sub-nav button:hover{color:var(--fontColor, #fff);}footer{background-color:rgba(43, 43, 45, 1);color:rgba(255, 255, 255, 1);--fontColor:rgba(255, 255, 255, 1);--fontColor_H:0;--fontColor_S:0%;--fontColor_L:100%;--fontColor_A:1;--fontColor_darkenDir:-1;--bgColor:rgba(43, 43, 45, 1);--bgColor_H:240;--bgColor_S:2%;--bgColor_L:17%;--bgColor_A:1;--bgColor_darkenDir:1;}footer .pre-wrap{white-space:pre-wrap;}footer ul{margin-top:0;margin-bottom:15px;}footer li{margin-bottom:0;}footer ul ul{margin-bottom:0;}footer a{background-color:transparent;color:inherit;-webkit-text-decoration:none;text-decoration:none;}footer a:-webkit-any-link{-webkit-text-decoration:none;text-decoration:none;}footer a:active,footer a:hover{outline:0;}footer a.underlined{-webkit-text-decoration:underline;text-decoration:underline;}footer a.underlined:hover{-webkit-text-decoration:none;text-decoration:none;}footer .container{width:100%;padding-right:16px;padding-left:16px;margin-right:auto;margin-left:auto;}@media (max-width: 576px){footer .container{max-width:100%;padding-right:16px;padding-left:16px;}}@media (min-width: 992px){footer .container{max-width:960px;}}@media (min-width: 1340px){footer .container{padding:0;max-width:1070px;}}footer h1,footer h2,footer h3{font-weight:normal;font-stretch:normal;font-style:normal;letter-spacing:normal;}footer h1{font-size:42px;line-height:1.19;}footer h2{font-size:30px;line-height:1.33;}footer h3{font-size:18px;line-height:1.67;}@media (max-width: 576px){footer h1{font-size:32px;line-height:1.31;}footer h2{font-size:26px;line-height:1.38;}footer h3{font-size:18px;line-height:1.56;}}footer .btn--subscribe{background-color:transparent;border:solid 2px;font-size:14px;line-height:1.5;color:var(--textColor);padding:14px 35px;cursor:pointer;font-weight:700;-webkit-transition:0.2s all;transition:0.2s all;}@media (min-width: 540px){footer .btn--subscribe:hover{color:var(--bgColor);background-color:var(--fontColor);border-color:var(--fontColor);}}footer .btn:disabled{background-color:#f4f4f4;color:#c1c1c1;}footer .btn--white{color:white;border-color:white;}@media (min-width: 540px){footer .btn--white:hover{background-color:#575757;border-color:#575757;color:white;}}footer .btn--white:disabled{background-color:#f4f4f4;color:#c1c1c1;}footer .footer{padding:91px 0 42px 0;}@media (max-width: 768px){footer .footer{padding:46px 0 100px;}}footer .footer .company-logo.light{display:none;}footer .footer.dark .company-logo.light{display:block;}footer .footer.dark .company-logo.dark{display:none;}footer .footer.light .company-logo.light{display:none;}footer .footer.light .company-logo.dark{display:block;}footer .footer .company-logo{display:block;
39max-width:200px;}footer .footer__layout{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-pack:justify;-webkit-justify-content:space-between;justify-content:space-between;}@media (max-width: 576px){footer .footer__layout{-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;}}footer .footer__layout__left{-webkit-flex-basis:57%;-ms-flex-preferred-size:57%;flex-basis:57%;-webkit-box-flex:1;-webkit-flex-grow:1;-ms-flex-positive:1;flex-grow:1;}footer .footer__layout__right{font-size:18px;-webkit-flex-basis:40%;-ms-flex-preferred-size:40%;flex-basis:40%;padding-left:15px;-webkit-box-flex:1;-webkit-flex-grow:1;-ms-flex-positive:1;flex-grow:1;}@media (max-width: 576px){footer .footer__layout__right{margin-top:53px;padding-left:0;}}footer .footer .company{padding-right:15px;}@media (max-width: 576px){footer .footer .company{padding-right:0;}}footer .footer .company__info__newsletter{max-width:430px;}footer .footer .company__info__newsletter p{margin-bottom:0;}footer .footer .company__contacts{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;margin:0 -37px;margin-top:100px;-webkit-box-flex-wrap:wrap;-webkit-flex-wrap:wrap;-ms-flex-wrap:wrap;flex-wrap:wrap;}@media (max-width: 768px){footer .footer .company__contacts{margin-top:40px;}}footer .footer .company__contacts>div{padding:0 37px 35px;}footer .footer .company__contacts p{margin-bottom:10px;}@media (max-width: 576px){footer .footer .company__contacts p{margin-bottom:0;}}@media (hover: hover) and (pointer: fine){footer .footer .company__contacts a:hover{-webkit-text-decoration:underline;text-decoration:underline;}}@media (max-width: 576px){footer .footer .company__contacts{-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;}}footer .footer .company__address{max-width:162px;margin-right:75px;}@media (max-width: 576px){footer .footer .company__address{margin-bottom:28px;}}footer .footer .newsletter{margin-bottom:58px;overflow:hidden;}footer .footer .newsletter__info{max-width:280px;}footer .footer .newsletter__subscribe{margin-top:47px;}footer .footer .newsletter__subscribe.hide{display:none;}footer .footer .newsletter__subscribe.success .btn{display:none;}footer .footer .newsletter__subscribe input[name='email']{background:none;border:none;border-bottom:solid 1px;font-size:15px;line-height:1.67;color:var(--fontColor);width:100%;margin-bottom:0;-webkit-transition:0.3s all;transition:0.3s all;border-radius:0;}footer .footer .newsletter__subscribe input[name='email']:focus{outline:none;}footer .footer .newsletter p.success{margin-top:1em;font-style:italic;-webkit-transition:all 0.4s ease;transition:all 0.4s ease;}footer .footer .newsletter p.hide{display:none;}footer .footer__middle{-webkit-align-items:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;}@media (max-width: 576px){footer .footer__middle{-webkit-align-items:flex-start;-webkit-box-align:flex-start;-ms-flex-align:flex-start;align-items:flex-start;-webkit-flex-direction:column-reverse;-ms-flex-direction:column-reverse;flex-direction:column-reverse;}}footer .footer .logos{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;}@media (max-width: 576px){footer .footer .logos{margin-bottom:30px;margin-top:36px;}}footer .footer .logos__logo{width:50px;height:50px;margin-right:45px;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 .footer .logos__logo img{max-width:100%;max-height:100%;}footer .footer .socials{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;font-size:12px;gap:15px;}@media (max-width: 576px){footer .footer .socials{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;width:100%;}}footer .footer .socials>li{padding:0;margin:0;}footer .footer .socials__item{border-radius:50%;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;
39-webkit-justify-content:center;justify-content:center;font-size:unset;position:relative;background-color:var(--fontColor, #000);color:var(--bgColor, #fff);}footer .footer .socials__item svg{width:1em;height:1em;}footer .footer .socials__item:hover{background-color:hsla(var(--fontColor_H, 0), var(--fontColor_S, 0%), var(--fontColor_L, 0%), .7);}footer .footer__bottom{margin-top:55px;padding-bottom:24px;}footer .footer__bottom-copyrights{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;}@media (max-width: 768px){footer .footer__bottom-copyrights{-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 .h-pot{height:0;width:0;overflow:hidden;visibility:hidden;}footer .img-wrap{max-width:100px;width:100%;}@media (min-width: 769px){footer .img-wrap{margin:0 20px 0 0;}}footer .img-wrap img{width:100%;object-fit:cover;}footer .logo-powered{font-size:18px;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;}@media (max-width: 768px){footer .logo-powered{-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;}}@media (max-width: 768px){footer .copyright-box{margin:5px 0 0 0;padding:0;}}footer .luxury-link{white-space:nowrap;position:relative;display:inline-block;}footer .luxury-link:after{content:"";position:absolute;display:block;width:100%;height:1px;bottom:0;left:0;-webkit-transition:.3s all linear;transition:.3s all linear;background:currentColor;}footer .luxury-link:hover:after{width:0;}footer .footer__disclaimers{padding-top:24px;padding-bottom:24px;}footer .footer .newsletter__subscribe input[name='email']::-webkit-input-placeholder{color:var(--fontColor);}footer .footer .newsletter__subscribe input[name='email']::-moz-placeholder{color:var(--fontColor);}footer .footer .newsletter__subscribe input[name='email']:-ms-input-placeholder{color:var(--fontColor);}footer .footer .newsletter__subscribe input[name='email']::placeholder{color:var(--fontColor);}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
39ox='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
39699 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
3971 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;
39-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;
39height: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);}} 40 </style> 41 <style id="custom-style-css">.hyperlink-style-1{color:rgba(0, 0, 0, 1)!important;}.hyperlink-style-1:hover{color:rgba(26, 26, 26, 1)!important;}.button-style-1{color:rgba(121, 38, 23, 1)!important;background-color:rgba(0, 0, 0, 0)!important;border-color:rgba(121, 38, 23, 1)!important;}.button-style-1:hover{color:rgba(255,255,255,1)!important;background-color:rgba(121, 38, 23, 1)!important;border-color:rgba(121, 38, 23, 1)!important;}.social_icons-style-1 a{border-color:rgba(121, 38, 23, 1)!important;background-color:rgba(121, 38, 23, 1)!important;color:rgba(255, 255, 255, 1)!important;}.social_icons-style-1 a:hover{border-color:rgba(121, 38, 23, 1)!important;background-color:rgba(0, 0, 0, 0)!important;color:rgba(121, 38, 23, 1)!important;}.social_icons-style-1 a i{color:inherit!important;}
41.social_icons-style-1 a svg,.social_icons-style-1 a svg path{fill:currentColor!important;}.pagination-style-1 li a{color:rgba(121, 38, 23, 1)!important;background-color:rgba(0, 0, 0, 0)!important;border-color:rgba(0, 0, 0, 0)!important;}.pagination-style-1 li a:hover{color:rgba(255, 255, 255, 1)!important;background-color:rgba(121, 38, 23, 1)!important;border-color:rgba(121, 38, 23, 1)!important;}.pagination-style-1 li.active a{color:rgba(255, 255, 255, 1)!important;background-color:rgba(121, 38, 23, 1)!important;border-color:rgba(121, 38, 23, 1)!important;}.hyperlink-style-2{color:rgba(255, 255, 255, 1)!important;}.hyperlink-style-2:hover{color:rgba(255, 255, 255, 0.7)!important;}.social_icons-style-2 a{border-color:rgba(255, 255, 255, 1)!important;background-color:rgba(255, 255, 255, 1)!important;color:rgba(121, 38, 23, 1)!important;}.social_icons-style-2 a:hover{border-color:rgba(255, 255, 255, 1)!important;background-color:rgba(255, 255, 255, 0)!important;color:rgba(255, 255, 255, 1)!important;}.social_icons-style-2 a i{color:inherit!important;}
41.social_icons-style-2 a svg,.social_icons-style-2 a svg path{fill:currentColor!important;}.pagination-style-2 li a{color:rgba(255, 255, 255, 1)!important;background-color:rgba(0, 0, 0, 0)!important;border-color:rgba(0, 0, 0, 0)!important;}.pagination-style-2 li a:hover{color:rgba(121, 38, 23, 1)!important;background-color:rgba(255, 255, 255, 1)!important;border-color:rgba(255,255,255,1)!important;}.pagination-style-2 li.active a{color:rgba(121, 38, 23, 1)!important;background-color:rgba(255, 255, 255, 1)!important;border-color:rgba(255,255,255,1)!important;}.button-style-2{color:rgba(255, 255, 255, 1)!important;background-color:rgba(0, 0, 0, 0)!important;border-color:rgba(255, 255, 255, 1)!important;}.button-style-2:hover{color:rgba(121, 38, 23, 1)!important;background-color:rgba(255, 255, 255, 1)!important;border-color:rgba(255, 255, 255, 1)!important;}.button-style-5{color:rgba(255, 255, 255, 1)!important;background-color:rgba(121, 38, 23, 1)!important;border-color:rgba(121, 38, 23, 1)!important;}.button-style-5:hover{color:rgba(121, 38, 23, 1)!important;background-color:rgba(0, 0, 0, 0.01)!important;border-color:rgba(121, 38, 23, 1)!important;}.button-style-6{color:rgba(121, 38, 23, 1)!important;background-color:#FFFFFF!important;border-color:rgba(255, 255, 255, 1)!important;}.button-style-6:hover{color:#FFFFFF!important;background-color:rgba(0, 0, 0, 0)!important;border-color:#FFFFFF!important;}</style> 42 43 44
44<script> 45 (function utmParse() { 46 const url = new URL(window.location.href); 47 if (url.search) { 48 const utm = url.search.split(/\?|&/g).reduce((acc, current) => { 49 const matched = current.match(/utm_(content|campaign|medium|source|term|id)|gclid/g); 50 if (matched) { 51 acc[matched[0]] = current.replace(`${matched[0]}=`, ''); 52 } 53 return acc; 54 }, {}); 55 if (Object.keys(utm).length) { 56 window.localStorage.setItem('utm_vars', JSON.stringify(utm)); 57 } else { 58 // Remove old utm vars 59 window.localStorage.removeItem('utm_vars'); 60 } 61 } 62 // Set referrer in local storage 63 // This is meant to be stored once to avoid self-referrals and to have the first referrer only. 64 if (!window.localStorage.getItem('referrer')) { 65 // Capture the Referrer URL 66 const referrer = document.referrer; 67 if (referrer) { 68 // Only store the referrer if it comes from outside the current domain (to avoid self-referrals) 69 const currentDomain = window.location.hostname; 70 const referrerDomain = new URL(referrer).hostname; 71 72 if (referrerDomain !== currentDomain) { 73 // Store it in localStorage 74 localStorage.setItem('referrer', referrer); 75 } 76 } 77 } 78 })(); 79 </script>
79 80 <!-- Bing Webmaster Tools Verification -->
81<script> 82 // Check if on the homepage before the body loads 83 if (window.location.pathname === '/' || window.location.pathname === '/index.html') { 84 document.write('<meta name="msvalidate.01" content="1E5445451A7B3C43718A1C599CEBE653" />'); 85 } 86</script>
86 87 88 89<!-- Google tag (gtag.js) -->
vendor: 65 bytes, lines 89-90
89 90<script async src="https://www.googletagmanager.com/gtag/js?id=
90G-FY363TT7G6
vendor: 13 bytes, line 90
90"></script>
91<script>
vendor: 130 bytes, lines 92-95
92window.dataLayer = window.dataLayer || []; 93function gtag(){dataLayer.push(arguments);} 94gtag('js', new Date()); 95gtag('config', '
95G-FY363TT7G6
vendor: 5 bytes, line 95
95');
96</script>
96 97 <!-- GOOGLE TRANSLATE STYLES are moved to external CSS --> 98</head> 99<body> 100 <div id="google_translate_element" style="display: none"></div> 101 102 <div id="global-loader" style="height:100%;width:100%;position:fixed;z-index:9999;left:0;top:0;background-color:rgba(43, 43, 45, 1);overflow-x:hidden;transition:1s;"> 103 <div style="position:absolute;height:100vh;text-align:center;bottom:0;left:0;right: 0;display:flex;align-items:center;"> 104 <img style="margin:0 auto;" src="https://media-production.lp-cdn.com/cdn-cgi/image/format=auto,quality=85/https://media-production.lp-cdn.com/media/3d3c6319-f7f9-41fd-bc4d-d3c27b2149fb" /> 105 </div> 106 </div> 107
107<script> 108 if (window.sessionStorage.getItem('global_loading_displayed') === 'yes') { 109 (function () { 110 document.getElementById('global-loader').remove(); 111 })(); 112 } 113 </script>
113 114 115 <nav id="global-navbar" data-el-id="43e7a874-fb1e-4cdd-91fe-eb6bf5b9179b"> 116 <div class="redesign"> 117 <div class="content-container header"> 118 <div class="container header__container"> 119 <div class="navbar"> 120 <div class="logo"> 121 <a href="/" class="logo__link"> 122 <img src="https://media-production.lp-cdn.com/cdn-cgi/image/format=auto,quality=85/https://media-production.lp-cdn.com/media/124e6fd1-c91a-4767-8aec-b87e6e6b1291" 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/124e6fd1-c91a-4767-8aec-b87e6e6b1291 320w, https://media-production.lp-cdn.com/cdn-cgi/image/format=auto,quality=85/https://media-production.lp-cdn.com/media/124e6fd1-c91a-4767-8aec-b87e6e6b1291 598w" class="logo__img light" alt=""> 123 <img src="https://media-production.lp-cdn.com/cdn-cgi/image/format=auto,quality=85/https://media-production.lp-cdn.com/media/514fa9af-ef72-414f-b660-8b8edb538dab" 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/514fa9af-ef72-414f-b660-8b8edb538dab 320w, https://media-production.lp-cdn.com/cdn-cgi/image/format=auto,quality=85/https://media-production.lp-cdn.com/media/514fa9af-ef72-414f-b660-8b8edb538dab 598w" class="logo__img dark" alt=""> 124 </a> 125 </div> 126 <div class="navigation"> 127 <ul> 128 <li class="navigation__item"> 129 <a href="/" class="navigation__link wow fadeInUp" data-wow-delay="2.0"><span>Home</span></a> 130 </li> 131 <li class="navigation__item"> 132 <a href="/properties/sale" class="navigation__link wow fadeInUp" data-wow-delay="2.1"><span>Listings</span></a> 133 </li> 134 <li class="navigation__item sub-nav-container"> 135 <a href="#" class="navigation__link wow fadeInUp">Solds</a> 136 <div class="sub-nav"> 137 <ul> 138 <li class="sub-nav__item"><a href="/recent-sales" class="nav-link sub-nav__link feature"><span>RECENT SALES</span></a></li> 139 <li class="sub-nav__item"><a href="/high-end-sales" class="nav-link sub-nav__link feature"><span>HIGH END SALES</span></a></li> 140 </ul> 141 </div> 142 </li> 143 <li class="navigation__item"> 144 <a href="/team" class="navigation__link wow fadeInUp" data-wow-delay="2.3"><span>About</span></a> 145 </li> 146 <li class="navigation__item"> 147 <a href="/contact-us" class="navigation__link wow fadeInUp" data-wow-delay="2.4"><span>CONTACT</span></a> 148 </li> 149 <li class="navigation__item"> 150 <a target="_blank" href="https://www.instagram.com/muskokacottagesforsale/" class="navigation__link wow fadeInUp" data-wow-delay="2.5"><span>IG</span></a> 151 </li> 152 </ul> 153 </div> 154 <div class="buttons-wrapper"> 155 <button type="button" class="hamburger-component hamburger"> 156 <span class="hamburger__bars"></span> 157 </button> 158 </div> 159 </div> 160 </div> 161 </div> 162</div> 163 164 </nav> 165 <div id="global-sidemenu" class="sidemenu" data-el-id="744a0561-da45-4129-9fa6-398537851996"> 166 <div class="wrapper"> 167 <div class="holder"> 168 <div class="title">RICHARD SCULLY & ASSOCIATES</div> 169 <ul class="section-options"> 170 <li class="option"><a href="/">Home</a></li> 171 <li class="option"><a href="/properties/sale">Listings</a></li> 172 <li class="option sub-nav-container"> 173 <a href="#" class="link sub-nav-heading">Solds</a> 174 <div class="sub-nav"> 175 <ul> 176 <li><a href="/recent-sales" class="link sub-nav-link">Recent Sales</a></li> 177 <li><a href="/high-end-sales" class="link sub-nav-link">High End Sales</a></li> 178 </ul> 179 </div> 180 </li> 181 <li class="option"><a href="/team">About</a></li> 182 <li class="option"><a href="/Vlog">Video Gallery</a></li> 183 <li class="option"><a href="/contact-us">Contact</a></li> 184 </ul> 185 </div> 186 <button class="toggle sidemenu-close"></button> 187</div> 188 189 </div> 190 <div id="global-mobile-contact" class="mobile-contact-wrapper" data-el-id="befe5af2-a6ca-4119-924a-a85d96d36b51"> 191 <div class="btn-holder" 192style=" 193--backgroundColor: rgba(43, 43, 45, 1); 194--textColor: rgba(255, 255, 255, 1); 195--pillTextColor: rgba(121, 38, 23, 1);
196--pillBackgroundColor: rgba(255, 255, 255, 1);"> 197 198 <button type="button" class="connect-btn lp-ico lp-ico-arrow-top visible">Contact Us</button> 199</div> 200 201 202<div class="pop-up" style="--backgroundColor: rgba(43, 43, 45, 1); --textColor: rgba(255, 255, 255, 1);"> 203 <div class="lp-container"> 204 <form data-type="contact-form" class="contact-form"> 205 <div class="contact-form__container"> 206 <h3 class="lp-h3">Leave a Message</h3> 207 208 <div class="contact-form__content"> 209 <div class="contact-form__inputs-container"> 210 <div class="contact-form__item"> 211 <input 212 id="__FullName" 213 type="text" name="name" 214 class="lp-input lp-input--light" 215 required 216 placeholder="Full Name"> 217 </div> 218 <div class="contact-form__item"> 219 <input 220 id="__Email" 221 type="email" name="email" 222 class="lp-input js-email-input lp-input--light" 223 required 224 placeholder="Email"> 225 </div> 226 <div class="contact-form__item"> 227 <input 228 class="lp-input lp-input--light" 229 type="tel" 230 placeholder="Phone" 231 id="__Phone" 232 name="phone" 233 required> 234 </div> 235 236 <div class="contact-form__item input-group-select"> 237 <select name="interest" id="-field-interest" placeholder="Interest" 238 class="lp-input dropdown-select js-select js-form-element lp-input--light" required> 239 <option value="" disabled selected data-label="Interest">Interested in...</option> 240 <option value="Selling & Buying">Selling & Buying</option> 241 <option value="Selling">Selling</option> 242 <option value="Buying">Buying</option> 243 <option value="Renting">Renting</option> 244 <option value="Other">Other</option> 245 </select> 246 <label for="-field-interest" class="lp-label sr-only">Interest</label> 247 </div> 248 249 <div class="contact-form__item"> 250 <textarea 251 name="message" 252 rows="4" 253 id="__Message" 254 class="lp-input lp-input--light" 255 placeholder="Message" 256 required></textarea> 257 </div> 258 <div class="lp-disclaimer"> <label style='display: none;' for='-termsAccepted'>Opt In/Disclaimer Consent:</label> 259 <label class='lp-tcr-content'> 260 <span class='lp-tcr-checkbox'> 261 <input 262 type='checkbox' 263 id='-termsAccepted' 264 name='termsAccepted' 265 oninvalid="this.setCustomValidity('Please accept the terms and conditions')" 266 oninput="this.setCustomValidity('')" 267 required 268 /></span> 269 <span class='lp-tcr-message'> 270 271 I agree to be contacted by 272 Richard Scully & Associates 273 via call, email, and text for real estate services. To opt out, you can reply 'stop' at any time or reply 'help' for assistance. 274 You can also click the unsubscribe link in the emails. Message and data rates may apply. Message frequency may vary. 275 <a href='/terms-and-conditions' target='_blank'>Privacy Policy</a>. 276 </span> 277 </label> 278 279</div> 280 <button type="submit" class="lp-btn lp-btn--filled lp-btn--dark submit contact-form__item">Submit</button> 281 282 <div class="lp-h-pot"> 283 <input id="__newsSignMiddleName" name="middleName" value="" autocomplete="off" tabindex="-1"> 284 </div> 285 286 <input style="display: none;" type="text" name="source" value="CONTACT_INQUIRY" /> 287 <input type="hidden" name="button_text" value="Contact Us"> 288 </div> 289 </div> 290 </div> 291 292 293 <div class="success-block"> 294 <h3 class="lp-h3 success-message">Thank you for your message. We will be in touch with you shortly.</h3> 295 296 </div> 297 </form> 298 299 <button type="button" class="close-btn lp-ico lp-ico-close"></button> 300 301 302 </div> 303</div> 304 305 306 </div> 307 <section class="image-section is-font-color-dark is-background-color-light" id="section-1c92a0cc-d18f-4a88-96d3-a5f34620fae7" data-el-id="ee03b9c1-ce78-4c15-86a1-10124375dc59"> 308 <div class="custom-form redesign" id="js-contact-form"> 309 <div class="lp-container custom-form__container"> 310 311 <div class="elem-row custom-form__row"> 312 <div class="elem-col custom-form__img-col"> 313 <div class="custom-form__img-block"> 314 <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/b1d6f7c0-86b6-4efb-bb8e-35bc12113699" 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/b1d6f7c0-86b6-4efb-bb8e-35bc12113699 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/b1d6f7c0-86b6-4efb-bb8e-35bc12113699 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/b1d6f7c0-86b6-4efb-bb8e-35bc12113699 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/b1d6f7c0-86b6-4efb-bb8e-35bc12113699 1920w, https://media-production.lp-cdn.com/cdn-cgi/image/format=auto,quality=85,fit=scale-down,width=2560/https://media-production.lp-cdn.com/media/b1d6f7c0-86b6-4efb-bb8e-35bc12113699 2560w, https://media-production.lp-cdn.com/cdn-cgi/image/format=auto,quality=85,fit=scale-down,width=3456/https://media-production.lp-cdn.com/media/b1d6f7c0-86b6-4efb-bb8e-35bc12113699 3456w" alt="Image Form" sizes="(max-width: 768px) 100vw, 50vw"> 315 </div> 316 </div> 317 <div class="elem-col custom-form__form-col"> 318 <form class="custom-form__download-form" data-type="contact-form" data-delay="6000"> 319 <div class="form-row custom-form__form-row"> 320 321 <div class="h-pot lp-h-pot"> 322 <label for="section-1c92a0cc-d18f-4a88-96d3-a5f34620fae7-middleName">Middle Name</label> 323 <input id="section-1c92a0cc-d18f-4a88-96d3-a5f34620fae7-middleName" name="middleName" value="" autocomplete="off" tabindex="-1"> 324 </div> 325 326 <div class="form-col js-backup-email"> 327 <div class="lp-form-group custom-form__group">
328 <div class="lp-input-group"> 329 <label for="section-1c92a0cc-d18f-4a88-96d3-a5f34620fae7-required-email-field" class="lp-label field-required">Email</label> 330 <input type="email" id="section-1c92a0cc-d18f-4a88-96d3-a5f34620fae7-required-email-field" name="email" class="lp-input lp-input--dark js-form-element js-backup-field" placeholder="Email *" required> 331 </div> 332 </div> 333 </div> 334 335 336 <div class="form-col"> 337 <div class="lp-form-group custom-form__group"> 338 <div class="lp-input-group"> 339 <label for="section-1c92a0cc-d18f-4a88-96d3-a5f34620fae7-field-0" class="lp-label field-required">Name</label> 340 <input type="text" id="section-1c92a0cc-d18f-4a88-96d3-a5f34620fae7-field-0" name="name" class="lp-input lp-input--dark js-form-element" placeholder="Name *" required> 341 </div> 342 </div> 343 </div> 344 <div class="form-col"> 345 <div class="lp-form-group custom-form__group"> 346 <div class="lp-input-group"> 347 <label for="section-1c92a0cc-d18f-4a88-96d3-a5f34620fae7-field-1" class="lp-label field-required">Email</label> 348 <input type="email" id="section-1c92a0cc-d18f-4a88-96d3-a5f34620fae7-field-1" name="email" class="lp-input lp-input--dark js-form-element" placeholder="Email *" required> 349 </div> 350 </div> 351 </div> 352 <div class="form-col"> 353 <div class="lp-form-group custom-form__group"> 354 <div class="lp-input-group"> 355 <label for="section-1c92a0cc-d18f-4a88-96d3-a5f34620fae7-field-2" class="lp-label">Property</label> 356 <select name="Property" id="section-1c92a0cc-d18f-4a88-96d3-a5f34620fae7-field-2" placeholder="Property" class="lp-input lp-input--dark dropdown-select js-select js-form-element" > 357 <option value="" selected disabled hidden data-label="Property "></option> 358 <option value="Any Property">Any Property</option> 359 <option value="CAMEL LAKE - $1,050,000">CAMEL LAKE - $1,050,000</option> 360 </select> 361 </div> 362 </div> 363 </div> 364 <div class="form-col"> 365 <div class="lp-form-group custom-form__group"> 366 <div class="lp-input-group"> 367 <label for="section-1c92a0cc-d18f-4a88-96d3-a5f34620fae7-field-3" class="lp-label field-required">Message</label> 368 <textarea name="message" rows="4" id="section-1c92a0cc-d18f-4a88-96d3-a5f34620fae7-field-3" class="lp-input lp-input--dark js-form-element" placeholder="Message *" required></textarea> 369 </div> 370 </div> 371 </div> 372 </div> 373 374 <div class="lp-disclaimer"> <label style='display: none;' for='section-1c92a0cc-d18f-4a88-96d3-a5f34620fae7-termsAccepted'>Opt In/Disclaimer Consent:</label> 375 <label class='lp-tcr-content'> 376 <span class='lp-tcr-checkbox'> 377 <input 378 type='checkbox' 379 id='section-1c92a0cc-d18f-4a88-96d3-a5f34620fae7-termsAccepted' 380 name='termsAccepted' 381 oninvalid="this.setCustomValidity('Please accept the terms and conditions')" 382 oninput="this.setCustomValidity('')" 383 required 384 /></span> 385 <span class='lp-tcr-message'> 386 387 I agree to be contacted by 388 Richard Scully & Associates 389 via call, email, and text for real estate services. To opt out, you can reply 'stop' at any time or reply 'help' for assistance. 390 You can also click the unsubscribe link in the emails. Message and data rates may apply. Message frequency may vary. 391 <a href='/terms-and-conditions' target='_blank'>Privacy Policy</a>. 392 </span> 393 </label> 394 395</div> 396 397 <div class="custom-form__footer"> 398 <button type="submit" class="lp-btn lp-btn--dark lp-btn--filled lp-btn--mob-full-width custom-form__submit-btn button-style-5">
399 <span class="lp-btn-icons"> 400 <span class="lp-btn-icon-loading"></span> 401 <span class="lp-btn-icon-success"></span> 402 </span> 403 Submit 404 </button> 405 </div> 406 </form> 407 </div> 408 </div> 409 </div> 410 411</div> 412 413 414</section> 415<section id="section-0431207f-8ab4-4735-aa08-dfbaadfcb5dc" class="solid-section is-font-color-light is-background-color-dark" data-el-id="5db65d6a-01e9-4ee3-babf-2748e074a284"> 416
416<script type="text/x-handlebars-template" class="jsIGItem"> 417 <a href="{{permalink}}" target="_blank" class="ig-feed-item insta-type-{{mediaType}}"> 418 <div class="ig-feed-item__img" style="background-image: url('{{#ifEquals mediaType 'VIDEO'}}{{thumbnailUrl}}{{^}}{{mediaUrl}}{{/ifEquals}}');"></div> 419 <div class="ig-feed-item__info"><div class="ig-feed-item__caption">{{caption}}</div></div> 420 {{#ifEquals mediaType 'VIDEO'}}<div class="video-icon"></div>{{/ifEquals}} 421 </a> 422</script>
422 423<div class="ig-feed redesign"> 424 <div class="ig-feed-top lp-container"> 425 <div class="top-wrapper"> 426 <div class="top-left"> 427 <h3 class="lp-h3 title">Follow Us on Instagram</h3> 428 <a href="#" target="_blank" class="instagram-link jsIGLink"><h4 class="lp-h4 instagram-name jsIGName"></h4></a> 429 </div> 430 <div class="top-right"> 431 <a href="#" target="_blank" class="lp-btn lp-btn--dark lp-btn--outline lp-btn--mob-full-width follow-button jsIGLink button-style-6">Follow Us</a> 432 <a class="nav-button top-nav-prev hidden-control button-style-6"> 433 <svg width="9" height="15" viewBox="0 0 9 15" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M7.099 13.25l-5.25-5.775L7.099 1.7" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> 434 </a> 435 <a class="nav-button top-nav-next hidden-control button-style-6"> 436 <svg width="9" height="15" viewBox="0 0 9 15" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M1.90137 1.75L7.15137 7.525L1.90137 13.3" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> 437 </a> 438 </div> 439 </div> 440 </div> 441 <div class="lp-container ig-feed-container "> 442 <div class="ig-feed-feed" data-limit="33"> 443 </div> 444 </div> 445 <div class="bottom-controls"> 446 <a class="bottom-nav-button bottom-nav-prev button-style-6"> 447 <svg width="9" height="15" viewBox="0 0 9 15" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M7.099 13.25l-5.25-5.775L7.099 1.7" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> 448 </a> 449 <a class="bottom-nav-button bottom-nav-next button-style-6"> 450 <svg width="9" height="15" viewBox="0 0 9 15" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M1.90137 1.75L7.15137 7.525L1.90137 13.3" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> 451 </a> 452 </div> 453</div> 454 455</section> 456 457 <div id="modals" class="modals"></div> 458 <footer id="global-footer" class="is-font-color-light is-background-color-dark" data-el-id="bf83e0d0-0a3d-4cf5-a94c-daba8415ab49"> 459 <div class="footer redesign"> 460 <div class="footer__layout container"> 461 <div class="footer__layout__left company"> 462 <div class="company__info__newsletter"> 463 <h2> 464 Richard Scully & Associates 465 </h2> 466 <p class="pre-wrap"></p> 467 </div> 468 <div class="company__contacts"> 469 <div> 470 <p><b>RICHARD SCULLY & ASSOCIATES</b></p> 471 <div><div> 472<p class="contact_title"><a class="ft-link" href="/cdn-cgi/l/email-protection#c0b2b3a3b5acacb980adb5b3abafaba1eea3afad"><span class="__cf_email__" data-cfemail="453736263029293c052830362e2a2e246b262a28">[email protected]</span></a><br><a class="ft-link" href="tel:7056449393">(705) 644-9393</a></p> 473</div> 474<style>* { box-sizing: border-box; } body {margin: 0;}ul{list-style:inherit;}</style></div> 475 </div> 476 <div> 477 <p><b>ADDRESS</b></p> 478 <div><div> 479<div> 480<p class="contact_title">1A Lee Valley Dr<br>Port Carling ON P0B 1J0</p> 481</div> 482</div> 483<style>* { box-sizing: border-box; } body {margin: 0;}ul{list-style:inherit;}</style> 484<p> </p> 485<style>* { box-sizing: border-box; } body {margin: 0;}ul{list-style:inherit;}</style></div> 486 </div> 487 <div> 488 <p><b></b></p> 489 <div><p style="font-size: 12px;">All information is deemed reliable but not guaranteed and should be independently reviewed and verified.</p> 490<p style="opacity: 1;"><img style="width: 60px; height: auto; filter: invert(1); margin-left: -10px; margin-top: 10px;" src="https://res.cloudinary.com/luxuryp/image/upload/v1675453847/Single_Realtor_Black_hwazek.png"></p></div> 491 </div> 492 </div> 493 </div> 494 495 <div class="footer__layout__right"> 496 <div class="newsletter"> 497 <div class="newsletter__info"> 498 <h2>NEWSLETTER</h2> 499 <p class="pre-wrap">Sign up to be the first to know about our new listings.</p> 500 </div> 501 <form class="newsletter__subscribe" data-type="contact-form"> 502 <div class="h-pot"> 503 <label for="-middleName">Middle Name</label> 504 <input id="-middleName" name="middleName" value="" autocomplete="off" tabindex="-1"> 505 </div> 506 <input type="hidden" name="source" value="NEWSLETTER_SIGNUP" /> 507 <input required placeholder="Email Address" name="email" type="email" /> 508 <div class="lp-disclaimer"> <label style='display: none;' for='-termsAccepted'>Opt In/Disclaimer Consent:</label> 509 <label class='lp-tcr-content'> 510 <span class='lp-tcr-checkbox'> 511 <input 512 type='checkbox' 513 id='-termsAccepted' 514 name='termsAccepted' 515 oninvalid="this.setCustomValidity('Please accept the terms and conditions')"
516 oninput="this.setCustomValidity('')" 517 required 518 /></span> 519 <span class='lp-tcr-message'> 520 521 I agree to be contacted by 522 Richard Scully & Associates 523 via call, email, and text for real estate services. To opt out, you can reply 'stop' at any time or reply 'help' for assistance. 524 You can also click the unsubscribe link in the emails. Message and data rates may apply. Message frequency may vary. 525 <a href='/terms-and-conditions' target='_blank'>Privacy Policy</a>. 526 </span> 527 </label> 528 529</div> 530 <input type="submit" value="SUBSCRIBE" class="btn btn--subscribe button-style-6" /> 531 <p class="success hide">Email Submitted!</p> 532 </form> 533 534 </div> 535 </div> 536 537 538 </div> 539 <div class="container footer__layout footer__middle"> 540 <div class="footer__layout__left"> 541 <ul class="lp-socials social_icons-style-2 footer__socials socials"> 542 543 <li> 544 <a 545 href="https://www.instagram.com/muskokacottagesforsale/" 546 class="lp-socials__link socials__item wow fadeInUp" 547 data-wow-delay="0s" 548 target="_blank" 549 aria-label="INSTAGRAM" 550 > 551 <span class="lp-i lp-i--instagram" aria-hidden="true"></span> 552 </a> 553 </li> 554 <li> 555 <a 556 href="https://www.youtube.com/@muskokacottagesforsale" 557 class="lp-socials__link socials__item wow fadeInUp" 558 data-wow-delay="0.1s" 559 target="_blank" 560 aria-label="YOUTUBE" 561 > 562 <span class="lp-i lp-i--youtube" aria-hidden="true"></span> 563 </a> 564 </li> 565 </ul> </div> 566 <div class="footer__layout__right"> 567 <div class="logos"> 568 </div> 569 </div> 570 </div> 571 <div class="footer__bottom"> 572 <div class="container"> 573 <div class="footer__bottom-copyrights footer__layout"> 574 <div class="footer__layout__left logo-powered"> 575 <div class="img-wrap"> 576 <img src="https://media-production.lp-cdn.com/media/b517d231-d9f5-4e15-870d-852a977c9204" alt=""> 577 </div> 578 <div> 579 Real Estate Website Design by <a href="https://www.luxurypresence.com/real-estate-website-design/" target="_blank" class="luxury-link hyperlink-style-2">Luxury Presence</a> 580 </div> 581 </div> 582 <div class="footer__layout__right copyright-box"> 583 Copyright <span class="auto-year-update">2026</span> | <a href="/terms-and-conditions" class="hyperlink-style-2">Privacy Policy</a> 584 </div> 585 </div> 586 </div> 587 </div> 588</div> 589 590 </footer> 591 592 <div id="modal-global-contact-us" class="modal " 593 data-el-id="" 594 style="background-color: ;" 595 > 596 <div class="modal-content"> 597 <div class="contact-form redesign " style="background-image: url('')"> 598 <div class="container"> 599 <div class="section-details" style="--colorTxt: #ffffff"> 600 <h2>Contact Details</h2> 601 602 <div class="info-zone"> 603 604 <div class="info-zone_contact type_user"> 605 <h4 class="serif">RICHARD SCULLY & ASSOCIATES</h4> 606 <div class="info-zone_contact_cell"> 607 <i> 608 <svg width="30" height="25" viewBox="0 0 30 25" fill="none" xmlns="http://www.w3.org/2000/svg"> 609 <path fill-rule="evenodd" clip-rule="evenodd" d="M10.7964 8.1207H19.2036V8.7279C19.2036 10.8195 20.9082 12.5259 23.001 12.5259H24.741L25.2852 20.7051C25.3776 22.1235 24.2868 23.2719 22.8822 23.2719H7.0032C5.5944 23.2719 4.5084 22.1085 4.6002 20.7039C4.782 17.9895 4.9152 15.2313 5.154 12.5265H6.9996C9.0918 12.5265 10.797 10.8207 10.797 8.7285V8.1207H10.7964ZM11.46 16.5807C11.46 14.6565 13.0188 13.0977 14.943 13.0977C16.8666 13.0977 18.4254 14.6571 18.4254 16.5807C18.4254 18.5055 16.8678 20.0631 14.943 20.0631C13.02 20.0637 11.46 18.5043 11.46 16.5807ZM14.943 12.0381C12.4344 12.0381 10.4004 14.0721 10.4004 16.5807C10.4004 19.0887 12.435 21.1233 14.943 21.1233C17.4486 21.1233 19.485 19.0869 19.485 16.5807C19.485 14.0727 17.451 12.0381 14.943 12.0381ZM9.0846 4.8363C8.22 4.8363 7.512 5.5443 7.512 6.4089V8.7285C7.512 9.0093 7.2804 9.2415 6.999 9.2415H1.5726C1.2906 9.2415 1.0596 9.0111 1.0596 8.7285V6.9285C1.0596 6.4797 1.1628 6.0945 1.3902 5.7069C1.896 4.8465 2.7294 4.1013 3.795 3.4749C9.279 0.2517 20.7192 0.2523 26.2044 3.4749C27.2706 4.1013 28.104 4.8465 28.6098 5.7069C28.8354 6.0921 28.9398 6.48
60915 28.9398 6.9285V8.7285C28.9398 9.0111 28.71 9.2415 28.4268 9.2415H23.0004C22.719 9.2415 22.4874 9.0093 22.4874 8.7285V6.4089C22.4874 5.5425 21.7806 4.8363 20.9148 4.8363H9.0846ZM8.571 6.4089C8.571 6.1263 8.8026 5.8965 9.0846 5.8965H20.9154C21.1968 5.8965 21.429 6.1275 21.429 6.4089V8.7285C21.429 9.5943 22.1352 10.3017 23.001 10.3017H28.4274C29.292 10.3017 30 9.5931 30 8.7285V6.9285C30 6.2835 29.8464 5.7291 29.5206 5.1729C28.9188 4.1493 27.9564 3.2799 26.7384 2.5641C20.9214 -0.8547 9.0798 -0.8547 3.2622 2.5641C2.0436 3.2799 1.0812 4.1487 0.48 5.1729C0.153 5.7291 0 6.2835 0 6.9279V8.7279C0 9.5925 0.7092 10.3011 1.5726 10.3011H6.999C7.8648 10.3011 8.571 9.5937 8.571 8.7279V6.4089ZM9.7368 7.5915V8.7285C9.7368 10.2387 8.5092 11.4669 6.999 11.4669H4.6686C4.3914 11.4669 4.164 11.6799 4.1412 11.9511C3.8892 14.8347 3.7386 17.7435 3.5448 20.6331C3.4116 22.6425 4.989 24.3315 7.0032 24.3315H22.8822C24.882 24.3315 26.475 22.6575 26.3406 20.6343L25.7622 11.9607C25.7436 11.6811 25.5102 11.4675 25.2342 11.4675L23.001 11.4663C21.4902 11.4663 20.2632 10.2375 20.2632 8.7279V7.5909C20.2632 7.2981 20.0262 7.0611 19.7334 7.0611H10.2666C9.9738 7.0611 9.7368 7.2987 9.7368 7.5915Z" fill="#1A1A1A"/> 610 </svg> 611 </i> 612 <p class="contact_title">Phone</p> 613 <a href="tel:(705) 644-9393" class="lp-link info-zone_contact_link link hyperlink-style-2">(705) 644-9393</a> 614 </div> 615 <div class="info-zone_contact_cell"> 616 <i> 617 <svg width="30" height="19" viewBox="0 0 30 19" fill="none" xmlns="http://www.w3.org/2000/svg"> 618 <path d="M2 0C0.903517 0 0 0.903517 0 2V16.6667C0 17.7631 0.903517 18.6667 2 18.6667H28C29.0965 18.6667 30 17.7631 30 16.6667V2C30 0.903517 29.0965 0 28 0H2ZM2.4375 1.33333H27.5625L15.6458 11.4896C15.3389 11.7512 14.6612 11.7512 14.3542 11.4896L2.4375 1.33333ZM1.33333 2.13542L10.1979 9.6979L1.33333 16.5729V2.13542ZM28.6667 2.13542V16.5729L19.8021 9.6979L28.6667 2.13542ZM11.2396 10.5833L13.4896 12.5C14.3543 13.2369 15.6457 13.2369 16.5104 12.5L18.7604 10.5833L27.4687 17.3333H2.53125L11.2396 10.5833Z" fill="#1A1A1A"/> 619 </svg> 620 </i> 621 <p class="contact_title">Email</p> 622 <a href="/cdn-cgi/l/email-protection#70020313051c1c09301d05031b1f1b115e131f1d" class="lp-link info-zone_contact_link link hyperlink-style-2"><span class="__cf_email__" data-cfemail="afdddcccdac3c3d6efc2dadcc4c0c4ce81ccc0c2">[email protected]</span></a> 623 </div> 624 </div> 625 <div class="info-zone_contact"> 626 <div class="info-zone_contact_cell"> 627 <i> 628 <svg width="31" height="30" viewBox="0 0 31 30" fill="none" xmlns="http://www.w3.org/2000/svg"> 629 <path d="M29.9821 6.49489C29.7931 6.35756 29.679 6.32046 29.4572 6.39228L20.298 9.41368L16.1952 8.03558L15.8172 9.41525L19.7644 10.7038V28.1462L9.89831 25.7069V22.1034H8.71438V25.7515L1.21616 28.2532V15.3945H0.0322266V29.2729C0.0322266 29.5077 0.130098 29.7279 0.321106 29.8645C0.445418 29.9541 0.585911 29.9999 0.735086 29.9999C0.814804 29.9999 0.890971 29.9868 0.967926 29.96L9.48433 27.029L20.1792 29.7887C20.3224 29.8254 20.4751 29.8171 20.6152 29.7654L29.8104 26.2235C30.0942 26.1177 30.0251 25.8466 30.0251 25.5431V7.08291C30.0251 6.85086 30.17 6.63144 29.9821 6.49489ZM28.8412 25.0388L20.9483 28.0361V10.7065L28.8412 8.08649V25.0388Z" fill="#1A1A1A"/> 630 <path d="M6.57515 19.1142C6.6671 19.422 6.95006 19.6323 7.2713 19.6323C7.59215 19.6323 7.87511 19.422 7.96746 19.1142C8.67742 16.7514 10.0393 15.2447 11.3559 13.788C12.9944 11.9747 14.5422 10.2615 14.5422 7.2717C14.5422 3.39788 11.1451 0 7.2713 0C3.39827 0 0 3.39748 0 7.2717C0 10.2615 1.54819 11.9747 3.18674 13.788C4.50328 15.2447 5.86479 16.7514 6.57515 19.1142ZM7.2713 1.45426C10.316 1.45426 13.0883 4.22663 13.0883 7.2717C13.0883 9.70191 11.7852 11.1435 10.2769 12.8121C9.2461 13.9534 8.10992 15.2096 7.2713 16.9199C6.43229 15.2096 5.29651 13.953 4.2657 12.8121C2.75698 11.1432 1.45426 9.70191 1.45426 7.2717C1.45426 4.22663 4.22663 1.45426 7.2713 1.45426Z" fill="#1A1A1A"/> 631 <path d="M7.26193 10.1616C8.8705 10.1616 10.1795 8.86084 10.1795 7.26175C10.1795 5.66305 8.87089 4.3623 7.26193 4.3623C5.65297 4.3623 4.34473 5.66305 4.34473 7.26175C4.34512 8.86084 5.65336 10.1616 7.26193 10.1616ZM7.26193 5.81657C8.06898 5.81657 8.72527 6.46536 8.72527 7.26175C8.72527 8.05932 8.06898 8.70733 7.26193 8.70733C6.45528 8.70733 5.79899 8.05893 5.79899 7.26175C5.79899 6.46536 6.45528 5.81657 7.26193 5.81657Z" fill="#1A1A1A"/> 632 </svg> 633 </i> 634 <p class="info-wrap-contact_title">Address</p> 635 <p class="info-zone_contact_link pre-line">1A Lee Valley Dr 636 Port Carling ON P0B 1J0</p> 637 </div> 638 </div> 639 <div class="info-zone_contact"> 640 <div class="info-zone_contact_cell"> 641 <p class="pre-line"><div></div><style>* { box-sizing: border-box; } body {margin: 0;}ul{list-style:inherit;}</style></p> 642 </div> 643 </div> 644 <ul class="lp-socials social_icons-style-2 socials socials--grey socials_zone footer__c
644ontact-column"> 645 646 <li> 647 <a 648 href="https://www.instagram.com/muskokacottagesforsale/" 649 class="lp-socials__link socials__item wow fadeInUp socials__link" 650 data-wow-delay="0s" 651 target="_blank" 652 aria-label="INSTAGRAM" 653 > 654 <span class="lp-i lp-i--instagram" aria-hidden="true"></span> 655 </a> 656 </li> 657 <li> 658 <a 659 href="https://www.youtube.com/@muskokacottagesforsale" 660 class="lp-socials__link socials__item wow fadeInUp socials__link" 661 data-wow-delay="0.1s" 662 target="_blank" 663 aria-label="YOUTUBE" 664 > 665 <span class="lp-i lp-i--youtube" aria-hidden="true"></span> 666 </a> 667 </li> 668 </ul> </div> 669 </div> 670 <div class="section-message"> 671 <form data-type="contact-form"> 672 <h2>Submit a Message</h2> 673 <div class="h-pot"> 674 <label for="-middleName">Middle Name</label> 675 <input id="-middleName" name="middleName" value="" autocomplete="off" tabindex="-1"> 676 </div> 677 678 679 <div class="lp-input-group"> 680 <label for="prod-name" class="lp-label">Name</label> 681 <input id="prod-name" name="name" value="" required="" class="lp-input lp-input--light"> 682 </div> 683 <div class="lp-input-group"> 684 <label for="prod-email" class="lp-label">Email</label> 685 <input id="prod-email" type="email" name="email" value="" required="" class="lp-input lp-input--light"> 686 </div> 687 <div class="lp-input-group"> 688 <label for="prod-phone" class="lp-label">Phone</label> 689 <input id="prod-phone" type="tel" name="phoneNumber" value="" required="" class="lp-input lp-input--light"> 690 </div> 691 <div class="lp-input-group m16"> 692 <label for="prod-message" class="lp-label">Message</label> 693 <textarea id="prod-message" name="message" value="" required="" class="lp-input"></textarea> 694 </div> 695 696 <div class="lp-disclaimer"> <label style='display: none;' for='-termsAccepted'>Opt In/Disclaimer Consent:</label> 697 <label class='lp-tcr-content'> 698 <span class='lp-tcr-checkbox'> 699 <input 700 type='checkbox' 701 id='-termsAccepted' 702 name='termsAccepted' 703 oninvalid="this.setCustomValidity('Please accept the terms and conditions')" 704 oninput="this.setCustomValidity('')" 705 required 706 /></span> 707 <span class='lp-tcr-message'> 708 709 I agree to be contacted by 710 Richard Scully & Associates 711 via call, email, and text for real estate services. To opt out, you can reply 'stop' at any time or reply 'help' for assistance. 712 You can also click the unsubscribe link in the emails. Message and data rates may apply. Message frequency may vary. 713 <a href='/terms-and-conditions' target='_blank'>Privacy Policy</a>. 714 </span> 715 </label> 716 717 </div> 718 719 <input style="display: none;" type="text" name="source" value="CONTACT_INQUIRY" /> 720 721 722 <button type="submit" class="submitButton lp-btn button-style-2">
723 <span class="label-initial">Submit</span> 724 <span class="label-loading">Submitting</span> 725 <span class="label-success">Submitted</span> 726 <span class="submit-icon lp-i lp-i--circle-notch lp-i--spin"></span> 727 </button> 728 <p class="success-message success hide"> 729 Message sent successfully! 730 </p> 731 </form> 732 733 734 </div> 735 </div> 736 </div> 737 738 </div> 739 <button class="close lp-close" type="button" aria-label="close modal"> 740 <span class="lp-close-icon" aria-hidden="true"></span> 741 </button> 742 </div> 743 <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;"> 744 <style> 745 #qualifying-questions-modal .custom-select-toggle { 746 border: 1px solid #848484; 747 border-radius: 8px; 748 padding: 12px 16px; 749 } 750 751 #qualifying-questions-modal .custom-select-toggle.is-placeholder { 752 color: #848484; 753 } 754 755 #qualifying-questions-modal #qualifying-questions-modal-phoneNumber::placeholder { 756 color: #848484; 757 opacity: 1; 758 } 759 760 #qualifying-questions-modal .disclaimer-more { 761 display: none; 762 } 763 764 #qualifying-questions-modal #qualifying-questions-modal-disclaimer-text:not(.collapsed) .disclaimer-more { 765 display: inline; 766 } 767 768 #qualifying-questions-modal #qualifying-questions-modal-disclaimer-text:not(.collapsed) .disclaimer-ellipsis { 769 display: none; 770 } 771 </style>
vendor: 119 bytes, lines 771-772
771 772 <script data-cfasync="false" src="/cdn-cgi/scripts/5c5dd728/cloudflare-static/email-decode.min.js"></script>
772<script> 773 function toggleDisclaimerQualifyingQuestionsModal() { 774 const disclaimerText = document.getElementById('qualifying-questions-modal-disclaimer-text'); 775 const toggleButton = document.getElementById('qualifying-questions-modal-toggle-disclaimer'); 776 777 if (disclaimerText.classList.contains('collapsed')) { 778 disclaimerText.classList.remove('collapsed'); 779 toggleButton.textContent = 'Read less'; 780 toggleButton.setAttribute('aria-expanded', 'true'); 781 } else { 782 disclaimerText.classList.add('collapsed'); 783 toggleButton.textContent = 'Read more'; 784 toggleButton.setAttribute('aria-expanded', 'false'); 785 } 786 } 787 </script>
787 788 <div style="max-width: 400px; width: 95%; margin: 0 auto;"> 789 <div style="background: #fff; border: none; border-radius: 16px; color: black; box-shadow: none; position: relative;"> 790 <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;"> 791 <span aria-hidden="true">×</span> 792 </button> 793 <div style="padding: 40px 30px 32px;"> 794 <div style="text-align: left; margin-bottom: 32px;"> 795 <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;"> 796 Thanks, please provide more information to help serve you 797 </h2> 798 </div> 799 <form id="qualifying-questions-modal-form" data-type="contact-form"> 800 <input type="hidden" name="source" value="PROFILE_UPDATE" /> 801 <input type="hidden" name="email" value="" id="qualifying-questions-modal-email" /> 802 <input type="hidden" name="triggerSource" id="qualifying-questions-modal-triggerSource" value="" /> 803 <label for="qualifying-questions-modal-email" style="display:none;"> 804 Email 805 </label> 806 <label for="qualifying-questions-modal-triggerSource" style="display:none;"> 807 Trigger Source 808 </label> 809 810 <div style="margin-bottom: 20px;"> 811 <label for="qualifying-questions-modal-phoneNumber" style="display:none;"> 812 Phone Number 813 </label> 814 <input 815 type="tel" 816 id="qualifying-questions-modal-phoneNumber" 817 name="phoneNumber" 818 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;" 819 pattern="^\+?1?[-.\s]?\(?[0-9]{3}\)?[-.\s]?[0-9]{3}[-.\s]?[0-9]{4}$" 820 required 821 placeholder="Phone" 822 aria-label="Phone Number" 823 inputmode="tel" 824 autocomplete="tel" 825 onfocus="this.style.borderColor='black';" 826 onblur="this.style.borderColor='#848484';" 827 /> 828 </div> 829 830 <div style="margin-bottom: 20px;"> 831 <label for="qualifying-questions-modal-interest" style="display:none;"> 832 Interest 833 </label> 834 <select 835 id="qualifying-questions-modal-interest" 836 name="interest" 837 required 838 data-label="Interest" 839 aria-label="Interest" 840 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;" 841 onfocus="this.style.borderColor='black'; this.style.color='black';" 842 onblur="this.style.borderColor='#848484'; if(this.value === '') this.style.color='#848484'; else this.style.color='black';" 843 > 844 <option value="" style="background: #fff; color: #848484;" disabled selected>Interested in...</option> 845 <option value="Selling & Buying" style="background: #fff; color: black;">Selling & Buying</option> 846 <option value="Selling" style="background: #fff; color: black;">Selling</option> 847 <option value="Buying" style="background: #fff; color: black;">Buying</option> 848 <option value="Renting" style="background: #fff; color: black;">Renting</option> 849 <option value="Other" style="background: #fff; color: black;">Other</option> 850 </select> 851 </div> 852 853 <div style="margin-bottom: 32px; font-size: 12px; line-height: 1.2;"> 854 <label style='display: none;' for='qualifying-questions-modal-qualifying-questions-termsAccepted'>Opt In/Disclaimer Consent:</label> 855 <label class='lp-tcr-content'>
856 <span class='lp-tcr-checkbox'> 857 <input 858 type='checkbox' 859 id='qualifying-questions-modal-qualifying-questions-termsAccepted' 860 name='termsAccepted' 861 aria-describedby='qualifying-questions-modal-disclaimer-text' 862 oninvalid="this.setCustomValidity('Please accept the terms and conditions')" 863 oninput="this.setCustomValidity('')" 864 required 865 /></span> 866 <span class='lp-tcr-message'> 867 <div id="qualifying-questions-modal-disclaimer-text" class="collapsed" style="color: #848484;"> 868 869 I agree to be contacted by 870 <strong>Richard Scully & Associates</strong> 871 via call, email, and text <span class="disclaimer-more">for real estate services. 872 To opt out, you can reply 'stop' at any time or reply 'help' for assistance. 873 You can also click the unsubscribe link in the emails. 874 Message and data rates may apply. Message frequency may vary.</span> 875 <span class="disclaimer-ellipsis">...</span> 876 <a href='/terms-and-conditions' target='_blank' rel='noopener noreferrer' style='text-decoration: underline;'>Privacy Policy</a>. 877 </div> 878 <button 879 id="qualifying-questions-modal-toggle-disclaimer" 880 type="button" 881 style="background: none; border: none; color: #848484; font-size: inherit; font-weight: bold; padding: 0; margin-top: 4px; cursor: pointer;" 882 aria-expanded="false" 883 onclick="toggleDisclaimerQualifyingQuestionsModal()" 884 > 885 Read more 886 </button> 887 </span> 888 </label> 889 </div> 890 891 <div> 892 <button 893 type="submit" 894 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;" 895 onmouseover="this.style.background='transparent'; this.style.color='black';" 896 onmouseout="this.style.background='black'; this.style.color='white';" 897 > 898 Submit 899 </button> 900 </div> 901 </form> 902 </div> 903 </div> 904 </div> 905 </div> 906 907
907<script> 908 (function() { 909 var lazyVideos = [].slice.call(document.querySelectorAll("video.lazy")); 910 911 if ("IntersectionObserver" in window && lazyVideos.length > 0) { 912 var lazyVideoObserver = new IntersectionObserver(function(entries, observer) { 913 entries.forEach(function(video) { 914 if (video.isIntersecting) { 915 for (var source in video.target.children) { 916 var videoSource = video.target.children[source]; 917 if (typeof videoSource.tagName === "string" && videoSource.tagName === "SOURCE") { 918 videoSource.src = videoSource.dataset.src; 919 } 920 } 921 922 video.target.load(); 923 video.target.classList.remove("lazy"); 924 lazyVideoObserver.unobserve(video.target); 925 } 926 }); 927 }); 928 929 lazyVideos.forEach(function(lazyVideo) { 930 lazyVideoObserver.observe(lazyVideo); 931 }); 932 } 933 })(); 934 </script>
934 935 936 <!-- Global site tag (gtag.js) - Google Analytics --> 937
937<script defer src="https://www.googletagmanager.com/gtag/js"></script>
937 938 939 <!-- Google Tag Manager --> 940
940<script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start': 941 new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0], 942 j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.defer=true;j.async=false;j.src= 943 'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f); 944 })(window,document,'script','dataLayer','GTM-T9SFQGHZ');</script>
944 945 <!-- End Google Tag Manager --> 946 947 948 <!-- LP Tracker (divolte.js) - LP Divolte --> 949
949<script>window.divolteLp=window.divolteLp||function(){(divolteLp.q=divolteLp.q||[]).push(arguments)}</script>
949 950 <!-- LP Tracker (eventBus) - LP EventBus --> 951
951<script>window.eventBusLp=window.eventBusLp||function(){(eventBusLp.q=eventBusLp.q||[]).push(arguments)}</script>
951 952 953
953<script> 954 window.dataLayer = window.dataLayer || []; 955 document.sendGoogleTagManagerEvent = function() { 956 /* Honor GPC-enabled browsers and prevent GTM event if GPC is enabled. More details: https://global-privacy-control.glitch.me/ */ 957 if (window.navigator.globalPrivacyControl) { 958 return; 959 } 960 961 dataLayer.push(arguments); 962 } 963 964 document.sendGoogleTagManagerEvent('js', new Date()); 965 document.sendGoogleTagManagerEvent('config', 'G-RP3QBYL2WD', { page: location.pathname, location: location.href }); 966 document.sendGoogleAnalyticsEvent = function(googleEvent) { 967 document.sendGoogleTagManagerEvent('event', googleEvent.eventAction || googleEvent.hitType, { 968 event_category: googleEvent.eventCategory, 969 event_label: googleEvent.eventLabel, 970 value: googleEvent.eventValue, 971 page: googleEvent.page, 972 location: googleEvent.location 973 }) 974 }; 975 976 document.addEventListener("DOMContentLoaded", function() { 977 (function() { 978 //If the page has an element with ID of auto-year-update the element will be populated with the current year. 979 var date = new Date(); 980 var elements = document.getElementsByClassName('auto-year-update'); 981 var i; 982 for (i = 0; i < elements.length; i++) { 983 elements[i].innerText = date.getFullYear(); 984 } 985 })(); 986 if (window.divolteLp) { 987 window.divolteLp('sendLpPageView', {}); 988 } 989 if (window.eventBusLp) { 990 window.eventBusLp('sendLpPageView', {}); 991 } 992 }); 993 </script>
993 994 995 996
996<script> 997 // Function to dynamically load a script 998 function loadScript(url, callback) { 999 var script = document.createElement('script'); 1000 script.type = 'text/javascript'; 1001 script.src = url; 1002 script.defer = true; 1003 script.async = true; 1004 script.onload = callback; 1005 document.head.appendChild(script); 1006 } 1007 1008 // Function to dynamically load CSS file 1009 function loadCSS(url) { 1010 var link = document.createElement("link"); 1011 link.rel = "stylesheet"; 1012 link.href = url; 1013 document.head.appendChild(link); 1014 } 1015 1016 // Global methods / variables that can be accessed by element js 1017 window.luxuryPresence = { 1018 libs: { 1019 loadJs: loadScript, 1020 loadCss: loadCSS, 1021 jsLibsMap: { 1022 paroller: { 1023 jsSrc: 'https://d1e1jt2fj4r8r.cloudfront.net/javascript/jquery.paroller.min.js', 1024 isLoaded: false, 1025 }, 1026 ionRangeSlider: { 1027 jsSrc: 'https://cdnjs.cloudflare.com/ajax/libs/ion-rangeslider/2.3.1/js/ion.rangeSlider.min.js', 1028 cssSrc: 'https://cdnjs.cloudflare.com/ajax/libs/ion-rangeslider/2.3.1/css/ion.rangeSlider.min.css', 1029 isLoaded: false, 1030 requires: ['jquery'], 1031 }, 1032 splide: { 1033 jsSrc: 'https://cdnjs.cloudflare.com/ajax/libs/splidejs/4.1.4/js/splide.min.js', 1034 cssSrc: 'https://cdnjs.cloudflare.com/ajax/libs/splidejs/4.1.4/css/splide-core.min.css', 1035 isLoaded: false, 1036 }, 1037 wow: { 1038 jsSrc: 'https://cdnjs.cloudflare.com/ajax/libs/wow/1.1.2/wow.min.js', 1039 isLoaded: false, 1040 }, 1041 jquery: { 1042 jsSrc: 'https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js', 1043 isLoaded: false, 1044 }, 1045 slick: { 1046 jsSrc: 'https://cdnjs.cloudflare.com/ajax/libs/slick-carousel/1.8.1/slick.min.js', 1047 isLoaded: false, 1048 requires: ['jquery'], 1049 }, 1050 libphonenumber: { 1051 jsSrc: 'https://cdnjs.cloudflare.com/ajax/libs/libphonenumber-js/1.11.8/libphonenumber-js.min.js', 1052 isLoaded: false, 1053 }, 1054 }, 1055 }, 1056 }; 1057 </script>
1057 1058
1058<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>
1058 1059
1059<script defer src="https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/4.7.8/handlebars.min.js"></script>
1059 1060
1060<script defer src="https://cdn.jsdelivr.net/npm/[email protected]/dist/handlebars-intl.min.js"></script>
1060 1061
1061<script defer src="https://cdnjs.cloudflare.com/ajax/libs/superagent/4.1.0/superagent.min.js"></script>
1061 1062
1062<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>
1062 1063
1063<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>
1063 1064
1064<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>
1064 1065 1066 <!-- GOOGLE TRANSLATE SCRIPT --> 1067
1067<script> 1068 function googleTranslateElementInit() { 1069 // list of languages can be overridden by setting window.customLanguages in the head JS. example `window.customLanguages = 'en,de,fr,ro,sk,pl'` 1070 // Language codes can be taken from here https://cloud.google.com/translate/docs/languages 1071 const DEFAULT_LANGUAGES = 'en,es,zh-CN,zh-TW,tl,vi,ar,ko,ru,de,fr,hi,pt'; 1072 document.querySelectorAll('[data-type="TRANSLATE"]').forEach(function(translate){ 1073 translate.innerText = ''; 1074 new google.translate.TranslateElement({ 1075 pageLanguage: 'en', 1076 layout: google.translate.TranslateElement.InlineLayout.SIMPLE, 1077 includedLanguages: window.customLanguages || DEFAULT_LANGUAGES, 1078 }, translate); 1079 }); 1080 } 1081 </script>
1081 1082
1082<script defer src="//translate.google.com/translate_a/element.js?cb=googleTranslateElementInit"></script>
1082 1083
1083<script defer src="https://accounts.google.com/gsi/intermediate"></script>
1083 1084 1085 <!-- GOOGLE TRANSLATE SCRIPT END--> 1086
1086<script> 1087 1088 // UI helpers 1089 (function () { 1090 1091 function getSideMenu () { 1092 return document.querySelector('body > div.sidemenu'); 1093 } 1094 1095 function sideMenuHide (instant) { 1096 var sideMenu = getSideMenu(); 1097 if (!sideMenu || !sideMenu.classList.contains('visible')) { 1098 return; 1099 } 1100 sideMenu.classList.remove('visible'); 1101 sideMenu.dispatchEvent(new CustomEvent('lpui-hide', { detail: { instant: !!instant } })); 1102 window.dispatchEvent(new CustomEvent('lpui-sidemenu-hide', { detail: {instant: !!instant} })); 1103 } 1104 1105 function sideMenuShow () { 1106 var sideMenu = getSideMenu(); 1107 if (!sideMenu || sideMenu.classList.contains('visible')) { 1108 return; 1109 } 1110 sideMenu.classList.add('visible'); 1111 sideMenu.dispatchEvent(new Event('lpui-show')); 1112 window.dispatchEvent(new Event('lpui-sidemenu-show')); 1113 } 1114 1115 function sideMenuToggle () { 1116 var sideMenu = getSideMenu(); 1117 if (!sideMenu) { 1118 return; 1119 } 1120 var isSideMenuVisible = sideMenu.classList.contains('visible'); 1121 if (isSideMenuVisible) { 1122 sideMenuHide(); 1123 } else { 1124 sideMenuShow(); 1125 } 1126 } 1127 1128 var scrollLockRequests = {}; 1129 window.__debug_scrollLockRequests = scrollLockRequests; 1130 var hideScrollStyles = ''+ 1131 'body {'+ 1132 'overflow: hidden;' + 1133 '}'; 1134 var hideScrollId = 'hide-body-scroll-style'; 1135 1136 function scrollLock () { 1137 var style = document.createElement('style'); 1138 style.id = hideScrollId; 1139 style.textContent = hideScrollStyles; 1140 document.head.appendChild(style); 1141 window.dispatchEvent(new Event('lpui-scroll-locked')); 1142 } 1143 1144 function scrollUnlock () { 1145 document.getElementById(hideScrollId)?.remove(); 1146 window.dispatchEvent(new Event('lpui-scroll-unlocked')); 1147 } 1148 1149 function requestScrollLock (lockId) { 1150 var wasLocked = Object.keys(scrollLockRequests).length > 0; 1151 scrollLockRequests[lockId || '__anonymous_lock'] = true; 1152 if (!wasLocked) { 1153 scrollLock(); 1154 } 1155 } 1156 1157 function releaseScrollLock (lockId) { 1158 if (lockId) { 1159 delete scrollLockRequests[lockId]; 1160 if (!Object.keys(scrollLockRequests).length) { 1161 scrollUnlock(); 1162 } 1163 } else { 1164 scrollLockRequests = {}; 1165 window.__debug_scrollLockRequests = scrollLockRequests; 1166 scrollUnlock(); 1167 } 1168 } 1169 1170 function showModal (modalId, options) { 1171 var modal = document.getElementById(modalId); 1172 if (!modal) { 1173 return; 1174 } 1175 1176 if (!modal.classList.contains('visible')) { 1177 modal.classList.add('visible'); 1178 if (options && options.scrollLock) { 1179 window.lpUI.requestScrollLock(modalId); 1180 } 1181 modal.dispatchEvent(new Event('lpui-show')); 1182 window.dispatchEvent(new CustomEvent('lpui-modal-show', { detail: { id: modalId } })); 1183 } 1184 1185 const visibleModals = document.querySelectorAll('.modal.visible'); 1186 1187 if (visibleModals.length > 1) { 1188 1189 const maxZIndex = Array.from(visibleModals).reduce((maxZIndex, modal) => { 1190 const zIndex = window.getComputedStyle(modal).zIndex; 1191 const parsedZIndex = zIndex !== 'auto' ? parseInt(zIndex) : 0; 1192 return Math.max(maxZIndex, parsedZIndex); 1193 }, 0); 1194 1195 modal.style.zIndex = maxZIndex + 1; 1196 } 1197 1198 } 1199 1200 function _hideModal (modalEl) { 1201 if (!modalEl) { 1202 return; 1203 } 1204 if (!modalEl.classList.contains('visible')) { 1205 return; 1206 } 1207 modalEl.classList.remove('visible'); 1208 modalEl.style.zIndex = '';
1209 modalEl.dispatchEvent(new Event('lpui-hide')); 1210 var modalId = modalEl.getAttribute('id'); 1211 if (modalId) { 1212 window.lpUI.releaseScrollLock(modalId); 1213 window.dispatchEvent(new CustomEvent('lpui-modal-hide', { detail: { id: modalId } })); 1214 } 1215 } 1216 1217 function hideModal (modalId) { 1218 _hideModal(document.getElementById(modalId)); 1219 } 1220 1221 function hideClosestModal (el) { 1222 const modal = el?.closest('.modal'); 1223 if (!modal) { 1224 return; 1225 } 1226 if (modal.classList) { 1227 _hideModal(modal); 1228 return; 1229 } 1230 if (modal.length && modal[0].classList) { 1231 _hideModal(modal[0]); 1232 } 1233 } 1234 1235 window.lpUI = { 1236 sideMenuHide: sideMenuHide, 1237 sideMenuShow: sideMenuShow, 1238 sideMenuToggle: sideMenuToggle, 1239 requestScrollLock: requestScrollLock, 1240 releaseScrollLock: releaseScrollLock, 1241 showModal: showModal, 1242 hideModal: hideModal, 1243 hideClosestModal: hideClosestModal 1244 } 1245 })(); 1246 1247 var stringifyPrimitive = function(v) { 1248 switch (typeof v) { 1249 case 'string': 1250 return v; 1251 1252 case 'boolean': 1253 return v ? 'true' : 'false'; 1254 1255 case 'number': 1256 return isFinite(v) ? v : ''; 1257 1258 default: 1259 return ''; 1260 } 1261 }; 1262 1263 function objectToQuerystring(obj, sep, eq, name) { 1264 // https://github.com/Gozala/querystring/blob/master/encode.js 1265 sep = sep || '&'; 1266 eq = eq || '='; 1267 if (obj === null) { 1268 obj = undefined; 1269 } 1270 1271 if (typeof obj === 'object') { 1272 return Object.keys(obj).map(function(k) { 1273 var ks = encodeURIComponent(stringifyPrimitive(k)) + eq; 1274 if (Array.isArray(obj[k])) { 1275 return obj[k].map(function(v) { 1276 return ks + encodeURIComponent(stringifyPrimitive(v)); 1277 }).join(sep); 1278 } else { 1279 return ks + encodeURIComponent(stringifyPrimitive(obj[k])); 1280 } 1281 }).filter(Boolean).join(sep); 1282 1283 } 1284 1285 if (!name) return ''; 1286 return encodeURIComponent(stringifyPrimitive(name)) + eq + 1287 encodeURIComponent(stringifyPrimitive(obj)); 1288 } 1289 1290 function getPath(url, options) { 1291 var parser = document.createElement('a'); 1292 parser.href = url || ''; 1293 var path = parser.pathname || ''; 1294 if (path[0] !== '/') { 1295 // IE does not return path starting with a slash 1296 path = '/' + path; 1297 } 1298 1299 if (options && options.queryString) { 1300 path += parser.search; 1301 } 1302 1303 return path; 1304 } 1305 1306 function getMLSAutocomplete(keyword) { 1307 var searchURL = window.location.protocol + '//' + window.location.hostname + '/home-search/auto_complete'; 1308 var query = { 1309 kind: 'listings', 1310 query: keyword 1311 }; 1312 1313 return superagent 1314 .get(searchURL) 1315 .query(query) 1316 .then(function(res) { 1317 var data = res.body || []; 1318 /* 1319 Of the format: 1320 [{ 1321 "_index": "listing_auto_completes_production_20190723232720775", 1322 "_type": "listing_auto_complete", 1323 "_id": "235875624", 1324 "_score": 0, 1325 "status_modified_at": 1553250708, 1326 "city": "Jupiter", 1327 "price": 1335743, 1328 "display_address": "10088 Calabrese Trail Unit: 9 Jupiter FL 33478", 1329 "sold_price": null, 1330 "id": 235875624, 1331 "state": "FL", 1332 "photo_urls": [ 1333 "http://cdn.photos.sparkplatform.com/fl/20190322153144208228000000-o.jpg" 1334 ], 1335 "status": "Pending" 1336 }, ...] 1337 */ 1338 1339 return data; 1340 }); 1341 } 1342 1343 function getMLSAutocompleteWithFilters(keyword, filters = {}) { 1344 var searchURL = window.location.protocol + '//' + window.location.hostname + '/home-search/auto_complete'; 1345 1346 return superagent 1347 .get(searchURL) 1348 .query({ kind: 'listings', query: keyword, ...filters }) 1349 .then(function(res) { 1350 /* 1351 Of the format: 1352 [{ 1353 "_index": "listing_auto_completes_production_20190723232720775", 1354 "_type": "listing_auto_complete", 1355 "_id": "235875624", 1356 "_score": 0, 1357 "status_modified_at": 1553250708, 1358 "city": "Jupiter", 1359 "price": 1335743, 1360 "display_address": "10088 Calabrese Trail Unit: 9 Jupiter FL 33478", 1361 "sold_price": null, 1362 "id": 235875624, 1363 "state": "FL", 1364 "photo_urls": [ 1365 "http://cdn.photos.sparkplatform.com/fl/20190322153144208228000000-o.jpg" 1366 ], 1367 "status": "Pending" 1368 }, ...] 1369 */ 1370 return res.body || []; 1371 }); 1372 } 1373 1374 function makeMlsLink(data) { 1375 // Code Dupe from utils/googlePlace 1376 // mapping of google data type to mls data type 1377 var mapping = { 1378 neighborhood: 'neighborhood', 1379 locality: 'place', 1380 administrative_area_level_2: 'county', 1381 administrative_area_level_1: 'state', 1382 postal_code: 'postalcode', 1383 }; 1384 1385 function _filterTypes(types) { 1386 return types.filter(function(type) { return type !== 'political' }); 1387 } 1388 1389 if (!data || !Object.keys(data).length) { 1390 return '/home-search/listings'; 1391 } 1392 1393 var newData = {};
1394 _filterTypes(data.types).forEach(function(type) { 1395 if (mapping[type]) { 1396 newData.search_type = mapping[type]; 1397 } 1398 }); 1399 newData.omnibox = data.formatted_address; 1400 data.address_components.forEach(function(component) { 1401 _filterTypes(component.types).forEach(function(type) { 1402 if (mapping[type]) { 1403 newData[mapping[type]] = component.long_name; 1404 } 1405 }); 1406 }); 1407 var bounds = data.geometry.bounds; 1408 var convertedData = Object.assign(bounds, newData); 1409 1410 return '/home-search/listings?' + objectToQuerystring(convertedData); 1411 } 1412 1413 function debounce(func, wait) { 1414 let timeout; 1415 return function(...args) { 1416 const context = this; 1417 clearTimeout(timeout); 1418 timeout = setTimeout(() => func.apply(context, args), wait) 1419 } 1420 } 1421 1422 // Global methods / variables that can be accessed by element js 1423 window.luxuryPresence = { 1424 ...window.luxuryPresence, 1425 defaults: { 1426 companyId: 'c3076ecb-c2d7-457b-a426-bcf036133f1f', 1427 apiGatewayUrl: '/api-gw', 1428 routerUrl: '/api-nv', 1429 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\
1429n }\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-
1429abb7-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
1429onId,\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}"}, 1430 propertyPlaceholderImage: {"id":"20c18a72-fa66-4e51-9f6a-0ab3003403c5","alt":null,"bytes":43683,"width":1920,"format":"png","height":1200,"pngUrl":"","shared":true,"curated":false,"duration":null,"largeUrl":"https://media-production.lp-cdn.com/cdn-cgi/image/format=auto,quality=85/https://media-production.lp-cdn.com/media/c7438ea9-abb7-47be-b004-8d4abcf471e0","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/c7438ea9-abb7-47be-b004-8d4abcf471e0","authorUrl":null,"createdAt":"2025-04-17T18:38:20.808Z","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/c7438ea9-abb7-47be-b004-8d4abcf471e0","sourceUrl":null,"updatedAt":"2025-04-17T18:38:20.808Z","xLargeUrl":"https://media-production.lp-cdn.com/cdn-cgi/image/format=auto,quality=85/https://media-production.lp-cdn.com/media/c7438ea9-abb7-47be-b004-8d4abcf471e0","__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/c7438ea9-abb7-47be-b004-8d4abcf471e0","description":null,"displayName":"property_richard-scully-and-associates","originalUrl":"https://media-production.lp-cdn.com/cdn-cgi/image/format=auto,quality=85/https://media-production.lp-cdn.com/media/c7438ea9-abb7-47be-b004-8d4abcf471e0","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/c7438ea9-abb7-47be-b004-8d4abcf471e0","originalFileName":"Property_Richard Scully & Associates","cloudinaryPublicId":"c7438ea9-abb7-47be-b004-8d4abcf471e0"} 1431 }, 1432 objectToQuerystring: objectToQuerystring, 1433 getPath: getPath, 1434 getMLSAutocomplete: getMLSAutocomplete, 1435 getMLSAutocompleteWithFilters: getMLSAutocompleteWithFilters, 1436 makeMlsLink: makeMlsLink, 1437 debounce: debounce, 1438 }; 1439 1440 var wowOffset = 100; 1441 </script>
1441 1442
1442<script> 1443 (() => { 1444 1445function $parcel$interopDefault(a) { 1446 return a && a.__esModule ? a.default : a; 1447} 1448// Entry point for lp-sdk bundle 1449// This file exists to ensure Parcel v2 outputs the correct filename 1450/* global window */ var $53de3c160faf5a81$exports = {}; 1451/* 1452WARNING!!! This file is used both on the server and in client's web-browser 1453please don't require/import any server-side related config/functions here 1454If we need something, let's pass it as a parameter of the factory 1455*/ var $0ce6edea77f7b81a$exports = {}; 1456$0ce6edea77f7b81a$exports = { 1457 getVideoFirstFrame (videoUrl) { 1458 // 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> 1459 // https://cloudinary.com/documentation/video_manipulation_and_delivery#transformation_url_structure 1460 // The problem is that <transformations> and <version> are optional, and <transformations> can be chained separated by '/' 1461 // One more issue to be handled https://cloudinary.com/documentation/advanced_url_delivery_options#seo_friendly_media_asset_urls 1462 const CLOUDINARY_URL = 'https://res.cloudinary.com/'; 1463 const invalid = !videoUrl || !videoUrl.startsWith(CLOUDINARY_URL); 1464 if (invalid) return null; 1465 const urlComponents = videoUrl.replace(CLOUDINARY_URL, '').replace('f_auto:video', 'f_auto').split('/'); 1466 // checking if <asset_type> is not video or videos 1467 if (urlComponents[1].toLowerCase().indexOf('video') === -1) return null; 1468 // Filename is last urlComponent <public_id>.<extension> 1469 let filename = urlComponents[urlComponents.length - 1]; 1470 if (filename.lastIndexOf('.') > -1) filename = `${filename.slice(0, filename.lastIndexOf('.'))}.jpg`; 1471 else filename += '.jpg'; 1472 urlComponents[urlComponents.length - 1] = filename; 1473 // We need to add so_0,eo_0 transformation to the url to target the first frame. 1474 // we want to find the position at which we want to insert it as a chained transformation 1475 const firstFrameTransformation = 'so_0,eo_0'; 1476 let firstFrameTransformationPosition = 3; 1477 // checking cases for SEO-friendly URLs 1478 if (urlComponents[1].toLowerCase() === 'videos' && urlComponents[2] !== '') firstFrameTransformationPosition = 2; 1479 // checking the last transformation position in the url 1480 // https://cloudinary.com/documentation/transformation_reference 1481 // https://regex101.com/r/lE3W6u/1 1482 const transformationRegex = /^(?:[a-z]{1,3}|\$\w+)_\S+$/; 1483 while(firstFrameTransformationPosition < urlComponents.length - 1 && transformationRegex.test(urlComponents[firstFrameTransformationPosition]))firstFrameTransformationPosition++; 1484 urlComponents.splice(firstFrameTransformationPosition, 0, firstFrameTransformation); 1485 return CLOUDINARY_URL + urlComponents.join('/'); 1486 } 1487}; 1488 1489 1490/** 1491 * Create helpers with given Handlebars instance 1492 * 1493 * @param {Object} HandlebarsInstance 1494 * @param {Function} isEqual - we use different implementation if isEqual on server and on client 1495 * @return {Object} 1496 */ function $53de3c160faf5a81$var$handlebarsHelpersFactory(HandlebarsInstance, { isEqual: isEqual }) { 1497 // notice!: code below assumes list is ordered lowest to highest width 1498 const MEDIA_SIZES = [ 1499 { 1500 field: 'thumbnailUrl', 1501 w: 320 1502 }, 1503 { 1504 field: 'smallUrl', 1505 w: 960 1506 }, 1507 { 1508 field: 'mediumUrl', 1509 w: 1280 1510 }, 1511 { 1512 field: 'largeUrl', 1513 w: 1920 1514 }, 1515 { 1516 field: 'xLargeUrl', 1517 w: 2560 1518 }, 1519 { 1520 field: 'xxLargeUrl', 1521 w: 3456 1522 } 1523 ]; 1524 const MEDIA_SIZE_FOR_SRC = 1280; 1525 const lpFormatNumber = function(number, unit, precision) { 1526 const localization = 'en-US'; 1527 if (!number) return ''; 1528 if (typeof number === 'string') { 1529 const trimmed = number.trim(); 1530 if (/[^+\-0-9.]/.test(trimmed)) return number; // returning text 'as is' assuming it already has all formatting 1531 // eslint-disable-next-line 1532 number = parseFloat(trimmed); 1533 if (!number) return ''; 1534 } 1535 const currencyPrecision = typeof precision === 'number' ? precision : 0; 1536 const currencyFractionDigits = Number.isInteger(number) ? 0 : currencyPrecision; 1537 const currencyFractionOptions = { 1538 minimumFractionDigits: currencyFractionDigits, 1539 maximumFractionDigits: currencyFractionDigits 1540 };
1541 if (unit === 'USD') return `$${number.toLocaleString(localization, currencyFractionOptions)}`; 1542 if (unit === 'CAD') return `$${number.toLocaleString(localization, currencyFractionOptions)} CAD`; 1543 if (unit === 'EUR') return `\u{20AC}${number.toLocaleString(localization, currencyFractionOptions)}`; 1544 if (unit === 'GBP') return `\xa3${number.toLocaleString(localization, currencyFractionOptions)}`; 1545 if (unit === 'PERCENT') return `${number}%`; 1546 if (typeof precision === 'number' && precision > -1) return number.toLocaleString(localization, { 1547 maximumFractionDigits: precision 1548 }); 1549 return number.toLocaleString(localization); 1550 }; 1551 const formatFrequency = function(freqUnit) { 1552 switch(freqUnit){ 1553 case 'DAY': 1554 return 'daily'; 1555 case 'MONTH': 1556 return 'monthly'; 1557 case 'WEEK': 1558 return 'weekly'; 1559 case 'YEAR': 1560 return 'yearly'; 1561 case 'QUARTER': 1562 return 'quarterly'; 1563 case 'HALF_YEAR': 1564 return 'semi-annually'; 1565 case 'NIGHTLY': 1566 return 'nightly'; 1567 case 'SEASONAL': 1568 return 'seasonally'; 1569 default: 1570 return ''; 1571 } 1572 }; 1573 const formatFrequencyShort = function(freqUnit) { 1574 switch(freqUnit){ 1575 case 'DAY': 1576 return 'day'; 1577 case 'WEEK': 1578 return 'wk'; 1579 case 'MONTH': 1580 return 'mo'; 1581 case 'YEAR': 1582 return 'yr'; 1583 case 'QUARTER': 1584 return '3 mos'; 1585 case 'HALF_YEAR': 1586 return '6 mos'; 1587 case 'NIGHTLY': 1588 return 'night'; 1589 case 'SEASONAL': 1590 return 'season'; 1591 default: 1592 return ''; 1593 } 1594 }; 1595 const formatFrequencyStd = function(freqUnit) { 1596 switch(freqUnit){ 1597 case 'DAY': 1598 return 'day'; 1599 case 'WEEK': 1600 return 'week'; 1601 case 'MONTH': 1602 return 'month'; 1603 case 'YEAR': 1604 return 'year'; 1605 case 'QUARTER': 1606 return 'quarter'; 1607 case 'HALF_YEAR': 1608 return '6 months'; 1609 case 'NIGHTLY': 1610 return 'night'; 1611 case 'SEASONAL': 1612 return 'season'; 1613 default: 1614 return ''; 1615 } 1616 }; 1617 const formatFrequencyExt = function(unit, options) { 1618 let val; 1619 let separator; 1620 const opts = options || {}; 1621 switch(opts.mode){ 1622 case 'short': 1623 val = formatFrequencyShort(unit); 1624 separator = opts.frequencySeparator || '/'; 1625 break; 1626 case 'standard': 1627 case 'std': 1628 val = formatFrequencyStd(unit); 1629 separator = opts.frequencySeparator || '/'; 1630 break; 1631 case 'long': 1632 default: 1633 val = formatFrequency(unit); 1634 separator = opts.frequencySeparator || ' '; 1635 } 1636 if (!val) return ''; 1637 if (!opts.frequencyTag && !opts.frequencyClass) return separator + val; 1638 return `${separator}<${opts.frequencyTag || 'span'}${opts.frequencyClass ? ` class="${opts.frequencyClass}"` : ''}>${val}</${opts.frequencyTag || 'span'}>`; 1639 }; 1640 return { 1641 isEmpty (arg1, options) { 1642 const result = !arg1 || (typeof arg1 === 'string' ? !arg1.trim() : false) || !Object.keys(arg1).length; 1643 if (options.fn) return result ? options.fn(this) : options.inverse(this); 1644 return result; 1645 }, 1646 ifEquals (arg1, arg2, options) { 1647 return isEqual(arg1, arg2) ? options.fn(this) : options.inverse(this); 1648 }, 1649 'raw-helper': function(options) { 1650 return options.fn(); 1651 }, 1652 striptags (txt) { 1653 if (!txt) return ''; 1654 // Remove HTML Comments (escaping something to not make real html comment by expression) 1655 // eslint-disable-next-line no-useless-escape,no-param-reassign 1656 txt = txt.replace(/<\!--(.|[\r\n])*?-\->/g, ''); 1657 // Remove XML fragments and <style> and <svg> tags with content 1658 // eslint-disable-next-line 1659 txt = txt.replace(/<(xml|style|svg)("[^"]*"|'[^']*'|[^>])*>(.|[\r\n])*?<\/\1>/gi, ''); 1660 // Replace with space 1661 // eslint-disable-next-line no-param-reassign 1662 txt = txt.replace(/ /g, ' '); 1663 // Remove HTML Tags 1664 const regexp = /<\/?("[^"]*"|'[^']*'|[^>])*(>|$)/g; 1665 return txt.replace(regexp, ''); 1666 },
1667 striptagsLineBreaks (txt) { 1668 if (!txt) return ''; 1669 // Remove HTML Comments (escaping something to not make real html comment by expression) 1670 // eslint-disable-next-line no-useless-escape,no-param-reassign 1671 txt = txt.replace(/<\!--(.|[\r\n])*?-\->/g, ''); 1672 // Remove XML fragments and <style> and <svg> tags with content 1673 // eslint-disable-next-line no-param-reassign 1674 txt = txt.replace(/<(xml|style|svg)("[^"]*"|'[^']*'|[^>])*>(.|[\r\n])*?<\/\1>/gi, ''); 1675 // Replace <br> and </p> tags with line break char, skipping last </p> which is not followed by <br> or </p> 1676 // eslint-disable-next-line no-param-reassign 1677 txt = txt.replace(/(<br\s*\/?>)|(<\/p\s*>)(?=.*(<\/p\s*>|<br\s*\/?>))/g, ' '); 1678 // Replace with space 1679 // eslint-disable-next-line no-param-reassign 1680 txt = txt.replace(/ /g, ' '); 1681 // Remove HTML Tags 1682 const regexp = /<\/?("[^"]*"|'[^']*'|[^>])*(>|$)/g; 1683 return txt.replace(regexp, ''); 1684 }, 1685 truncate (text, limit, suffix) { 1686 if (!text) return ''; 1687 if (text.length > limit) return typeof suffix === 'string' ? text.slice(0, limit - suffix.length) + suffix : text.slice(0, limit); 1688 return text; 1689 }, 1690 // eslint-disable-next-line 1691 setSubArray (items = [], start, end, subArrayName, options) { 1692 // eslint-disable-next-line 1693 options.data.root[subArrayName] = items.slice(start, end); 1694 }, 1695 formatAreaUnit (areaUnit) { 1696 switch(areaUnit){ 1697 case 'ACRE': 1698 return 'Acres'; 1699 case 'SQUARE_METER': 1700 return 'Sq.M.'; 1701 case 'SQUARE_YARD': 1702 return 'Sq.Yd.'; 1703 default: 1704 return 'Sq.Ft.'; 1705 } 1706 }, 1707 formatFrequency: formatFrequency, 1708 formatFrequencyShort: formatFrequencyShort, 1709 lpFormatFrequency (freqUnit, options) { 1710 return formatFrequencyExt(freqUnit, options && options.hash); 1711 }, 1712 subArray (index, start, end, options) { 1713 return index >= start && index < end ? options.fn(this) : options.inverse(this); 1714 }, 1715 formatPropertyStatus (status, showSoldDate, date) { 1716 let formattedStatus = ''; 1717 switch(status){ 1718 case 'SOLD': 1719 formattedStatus = 'Sold'; 1720 break; 1721 case 'FOR_SALE': 1722 formattedStatus = 'For Sale'; 1723 break; 1724 case 'COMING_SOON': 1725 formattedStatus = 'Coming Soon'; 1726 break; 1727 case 'INACTIVE': 1728 formattedStatus = 'Inactive'; 1729 break; 1730 case 'POCKET_LISTING': 1731 formattedStatus = 'Pocket Listing'; 1732 break; 1733 case 'PENDING': 1734 formattedStatus = 'Pending'; 1735 break; 1736 case 'UNDER_CONTRACT': 1737 formattedStatus = 'Under Contract'; 1738 break; 1739 case 'ACTIVE_UNDER_CONTRACT': 1740 formattedStatus = 'Active Under Contract'; 1741 break; 1742 default: 1743 formattedStatus = status; 1744 } 1745 // `typeof date === 'string'` matters: when called as 1746 // `{{formatPropertyStatus status}}` Handlebars passes its options object 1747 // as `showSoldDate`/`date`, and this guard keeps the legacy output. 1748 if ((formattedStatus === 'Sold' || formattedStatus === 'Leased') && showSoldDate && typeof date === 'string' && date) formattedStatus += ` on ${date}`; 1749 return formattedStatus; 1750 }, 1751 formatGrade (gradeValue) { 1752 switch(gradeValue){ 1753 case 'UNDERGRADUATE_COURSES': 1754 return 'Undergrad'; 1755 case 'PRE_KINDERGARTEN': 1756 return 'PreK'; 1757 case 'KINDERGARTEN': 1758 return 'K'; 1759 case 'FIRST_GRADE': 1760 return '1st'; 1761 case 'SECOND_GRADE': 1762 return '2nd'; 1763 case 'THIRD_GRADE': 1764 return '3rd'; 1765 case 'FOURTH_GRADE': 1766 return '4th'; 1767 case 'FIFTH_GRADE': 1768 return '5th'; 1769 case 'SIXTH_GRADE': 1770 return '6th'; 1771 case 'SEVENTH_GRADE': 1772 return '7th'; 1773 case 'EIGHTH_GRADE': 1774 return '8th'; 1775 case 'NINTH_GRADE': 1776 return '9th'; 1777 case 'TENTH_GRADE': 1778 return '10th'; 1779 case 'ELEVENTH_GRADE': 1780 return '11th'; 1781 case 'TWELFTH_GRADE': 1782 return '12th'; 1783 default: 1784 return ''; 1785 } 1786 }, 1787 encodeURI (uri) { 1788 return encodeURI(uri); 1789 }, 1790 encodeURIComponent (txt) { 1791 return encodeURIComponent(txt); 1792 }, 1793 hbFormatDate (date, type) { 1794 if (!date) return ''; 1795 const newDate = date === 'current' ? new Date() : new Date(date); 1796 if (Number.isNaN(newDate.getTime())) return ''; 1797 const months = [ 1798 'January', 1799 'February', 1800 'March', 1801 'April', 1802 'May', 1803 'June', 1804 'July', 1805 'August', 1806 'September', 1807 'October',
1808 'November', 1809 'December' 1810 ]; 1811 if (type === 'YYYY' || type === 'year') return `${newDate.getFullYear()}`; 1812 if (type === 'MM/DD/YY') { 1813 let month = newDate.getMonth() + 1; 1814 if (month < 10) month = `0${month.toString()}`; 1815 return `${month}/${newDate.getDate()}/${newDate.getFullYear().toString().substr(-2)}`; 1816 } 1817 return `${months[newDate.getMonth()]} ${newDate.getDate()}, ${newDate.getFullYear()}`; 1818 }, 1819 openHousesFormat (startDate, endDate, timeZone) { 1820 if (!startDate || !endDate) return ''; 1821 /** 1822 * return formatted time. 1823 * @param {date} date 1824 * @return {time} time. 1825 */ const getTime = (date)=>{ 1826 let hours = date.getHours(); 1827 if (hours > 12) hours -= 12; 1828 const prefix = '0'; 1829 let time = `${hours}:${date.getMinutes() > 9 ? date.getMinutes() : prefix + date.getMinutes()}`; 1830 if (date.getHours() > 11) time += 'PM'; 1831 else time += 'AM'; 1832 return time; 1833 }; 1834 let tz = ''; 1835 let startFDate = startDate; 1836 let endFDate = endDate; 1837 if (timeZone) { 1838 startFDate = new Date(startDate).toLocaleString('en-US', { 1839 timeZone: timeZone 1840 }); 1841 endFDate = new Date(endDate).toLocaleString('en-US', { 1842 timeZone: timeZone 1843 }); 1844 [, , , tz] = new Date(endDate).toLocaleString('en-US', { 1845 timeZone: timeZone, 1846 timeZoneName: 'short' 1847 }).split(' '); 1848 } 1849 const date = new Date(startFDate); 1850 const openHouseDate = date.toLocaleDateString('en-US'); 1851 const time1 = getTime(date); 1852 const date2 = new Date(endFDate); 1853 const time2 = getTime(date2); 1854 return `${openHouseDate} ${time1} - ${time2} ${tz}`; 1855 }, 1856 math (lvalue, operator, rvalue) { 1857 // eslint-disable-next-line 1858 lvalue = parseFloat(lvalue); 1859 // eslint-disable-next-line 1860 rvalue = parseFloat(rvalue); 1861 return ({ 1862 '+': lvalue + rvalue, 1863 '-': lvalue - rvalue, 1864 '*': lvalue * rvalue, 1865 '/': lvalue / rvalue, 1866 '%': lvalue % rvalue 1867 })[operator]; 1868 }, 1869 findByKey (values, key, value, options) { 1870 const result = (values || []).find((entry)=>entry[key] === value); 1871 return options.fn(result); 1872 }, 1873 capitalize (string, split, options) { 1874 let capitalized; 1875 if (typeof string === 'string' && string.length) capitalized = string.toLowerCase().split(split).map((w)=>w.charAt(0).toUpperCase() + w.slice(1)).join(' ').toString(); 1876 return capitalized; 1877 }, 1878 and (...arg) { 1879 return Array.prototype.slice.call(arg).every(Boolean); 1880 }, 1881 or (...arg) { 1882 return Array.prototype.slice.call(arg, 0, -1).some(Boolean); 1883 }, 1884 not (arg) { 1885 return !arg; 1886 }, 1887 orderByIds (items, orderedIds, options) { 1888 let ret = ''; 1889 let data = null; 1890 if (options.data) data = HandlebarsInstance.createFrame(options.data); 1891 // When passed orderedIds, filter out empty items including empty strings and 1892 // only show items (and in that order) in a loop 1893 // If no orderedIds provided, show all items 1894 const filteredIds = (orderedIds || []).filter(Boolean); 1895 if (filteredIds && filteredIds.length) { 1896 let skippedItems = 0; 1897 ret = filteredIds.reduce((res, id, idx)=>{ 1898 const item = (items || []).find((item)=>item.id === id); 1899 // ID doesn't exist so ditch it 1900 if (!item) { 1901 skippedItems += 1; 1902 return res; 1903 } 1904 if (data) data.index = idx - skippedItems; 1905 return res + options.fn(item, { 1906 data: data 1907 }); 1908 }, ''); 1909 } else for(let i = 0, j = (items || []).length; i < j; i++){ 1910 if (data) data.index = i; 1911 ret += options.fn(items[i], { 1912 data: data 1913 }); 1914 } 1915 return ret; 1916 }, 1917 ifIn (elem, list, options) { 1918 if (list && list.indexOf(elem) > -1) return options.fn(this); 1919 return options.inverse(this); 1920 }, 1921 partial (name, options) { 1922 // Register a partial and output the content in a script for clientside rendering 1923 HandlebarsInstance.registerPartial(name, HandlebarsInstance.compile(options.fn())); 1924 // eslint-disable-next-line 1925 const script = `
1925<script type="text/x-handlebars-template" class="${name} pagination-script">${options.fn()}</${'script'}>`; 1926 return script; 1927 }, 1928 numCompare (l, r, operator, options) { 1929 const operators = { 1930 '==': function(l, r) { 1931 // eslint-disable-next-line 1932 return l == r; 1933 }, 1934 '===': function(l, r) { 1935 return l === r; 1936 }, 1937 '!=': function(l, r) { 1938 // eslint-disable-next-line 1939 return l != r; 1940 }, 1941 '<': function(l, r) { 1942 return l < r; 1943 }, 1944 '>': function(l, r) { 1945 return l > r; 1946 }, 1947 '<=': function(l, r) { 1948 return l <= r; 1949 }, 1950 '>=': function(l, r) { 1951 return l >= r; 1952 } 1953 }; 1954 const result = operators[operator](l, r); 1955 if (result) return options.fn ? options.fn(this) : true; 1956 return options.fn ? options.inverse(this) : false; 1957 }, 1958 lpFormatNumber: lpFormatNumber, 1959 arrayToHtmlList (arr, options) { 1960 // eslint-disable-next-line 1961 return arr.reduce((acc, current)=>{ 1962 try { 1963 if (typeof current === 'object' || current === null) return acc; 1964 // eslint-disable-next-line 1965 return acc += `<li>${current}</li>`; 1966 } catch (e) { 1967 /* empty */ } 1968 }, ''); 1969 }, 1970 join (array, separator, options) { 1971 if (!array || !array.join) return ''; 1972 return array.join(typeof separator === 'string' ? separator : ', '); 1973 }, 1974 dedupe (items, key, options) { 1975 let data = null; 1976 let skippedItems = 0; 1977 const list = new Set(); 1978 if (options.data) data = HandlebarsInstance.createFrame(options.data); 1979 return (items || []).reduce((res, item, idx)=>{ 1980 const val = key ? item[key] : item; 1981 // Item is unique if we're passed a key that is not defined 1982 const isUnique = !list.has(val) || key && val === undefined; 1983 if (isUnique) list.add(val); 1984 else { 1985 skippedItems += 1; 1986 return res; 1987 } 1988 if (data) data.index = idx - skippedItems; 1989 return res + options.fn(item, { 1990 data: data 1991 }); 1992 }, ''); 1993 }, 1994 JSONstringify (obj, options) { 1995 return JSON.stringify(obj); 1996 }, 1997 // eslint-disable-next-line sonarjs/cognitive-complexity 1998 formatPropertyPrice (...args) { 1999 const options = args[args.length - 1]; 2000 const opts = options.hash; 2001 const property = args.length > 1 ? args[0] : opts.property; 2002 const precision = opts.precision || 0; 2003 if (args.length > 2) // eslint-disable-next-line 2004 opts.mode = args[1]; 2005 if (!opts.mode) opts.mode = 'short'; 2006 let separator = args.length > 3 ? args[2] : opts.separator; 2007 if (typeof separator !== 'string') separator = ' | '; 2008 if (property.isPasswordProtected) return 'Exclusive Listing'; 2009 let priceUponRequestText = args.length > 4 ? args[3] : opts.priceUponRequestText; 2010 if (typeof priceUponRequestText !== 'string') priceUponRequestText = 'Price Upon Request'; 2011 if (property.priceUponRequest) return priceUponRequestText; 2012 let price = property.salesPrice; 2013 let reduced = null; 2014 // eslint-disable-next-line sonarjs/no-collapsible-if 2015 if (property.status !== 'SOLD' && property.status !== 'Leased') { 2016 if (property.reducedPrice) { 2017 if (!opts.reducedClass) price = property.reducedPrice; 2018 else reduced = property.reducedPrice; 2019 } 2020 } 2021 let result = ''; 2022 if (reduced) result += `<${opts.reducedTag || 'span'} class="${opts.reducedClass}">${lpFormatNumber(reduced, property.currency, precision)}</${opts.reducedTag || 'span'}>`; 2023 if (price) { 2024 price = lpFormatNumber(price, property.currency, precision); 2025 if (opts.priceTag || opts.priceClass) result += `<${opts.priceTag || 'span'}${opts.priceClass ? ` class="${opts.priceClass}"` : ''}>${price}</${opts.priceTag || 'span'}>`; 2026 else result += price; 2027 } 2028 const leasePrice = property.leasePrice && lpFormatNumber(property.leasePrice, property.currency, precision); 2029 if (leasePrice) { 2030 if (result) result += separator; 2031 const leaseText = `${leasePrice}${formatFrequencyExt(property.leasePeriod, opts)}`; 2032 if (opts.leaseTag || opts.leaseClass) result += `<${opts.leaseTag || 'span'}${opts.leaseClass ? ` class="${opts.leaseClass}"` : ''}>${leaseText}</${opts.leaseTag || 'span'}>`; 2033 else result += leaseText; 2034 } 2035 return new HandlebarsInstance.SafeString(result); 2036 }, 2037 // eslint-disable-next-line sonarjs/cognitive-complexity 2038 mediaImage (mediaSource, options = {}) { 2039 if (!mediaSource) return; 2040 const { lazy: lazy, ...attributes } = options.hash; 2041 let src = ''; 2042 let srcset = ''; 2043 if (typeof mediaSource === 'string') src = mediaSource; 2044 else { 2045 const srcMap = {}; 2046 const { height: height, width: width, altTagText: altTagText } = mediaSource; 2047 MEDIA_SIZES.forEach((src)=>{ 2048 if (mediaSource[src.field] && !srcMap[mediaSource[src.field]]) srcMap[mediaSource[src.field]] = src.w; 2049 }); 2050 const srcs = Object.keys(srcMap); 2051 if (!srcs.length) return; 2052 if (altTagText) attributes.alt = altTagText; 2053 if (height && width) { 2054 if (height >= width) { 2055 if (attributes.class) attributes.class += ' portrait'; 2056 else attributes.class = 'portrait'; 2057 } 2058 if ('setDims' in options.hash) { 2059 attributes.width = width; 2060 attributes.height = height; 2061 } 2062 } 2063 if (srcs.length > 1 && width && width < srcMap[srcs[srcs.length - 1]]) { 2064 const maxSrc = srcs.findIndex((src)=>
2064srcMap[src] >= width); 2065 srcs.splice(maxSrc + 1); 2066 srcMap[srcs[srcs.length - 1]] = width; 2067 } 2068 if (srcs.length > 1) { 2069 srcset = srcs.map((s)=>`${s} ${srcMap[s]}w`).join(', '); 2070 srcs.sort((a, b)=>Math.abs(srcMap[a] - MEDIA_SIZE_FOR_SRC) - Math.abs(srcMap[b] - MEDIA_SIZE_FOR_SRC)); 2071 } 2072 // eslint-disable-next-line 2073 src = srcs[0]; 2074 } 2075 delete attributes.setDims; 2076 if (!srcset) delete attributes.sizes; 2077 if (!('alt' in attributes)) attributes.alt = ''; 2078 if (!('loading' in attributes) && (options?.data?.root?.sectionIndex > 1 || lazy)) attributes.loading = 'lazy'; 2079 const attrs = Object.keys(attributes).map((k)=>`${HandlebarsInstance.escapeExpression(k)}="${HandlebarsInstance.escapeExpression(attributes[k])}"`).join(' '); 2080 return new HandlebarsInstance.SafeString(// eslint-disable-next-line sonarjs/no-nested-template-literals 2081 `<img src="${src}"${srcset ? ` srcset="${srcset}"` : ''}${attrs ? ` ${attrs}` : ''}>`); 2082 }, 2083 bgImage (mediaSource) { 2084 if (!mediaSource) return ''; 2085 if (typeof mediaSource === 'string') return mediaSource; 2086 return mediaSource.largeUrl || mediaSource.url || ''; 2087 }, 2088 concat (...rest) { 2089 let outStr = ''; 2090 // rest params are Array instances while arguments object is not 2091 for(const arg in rest)if (rest[arg] !== null && rest[arg] !== undefined && typeof rest[arg] !== 'object') outStr += rest[arg]; 2092 return outStr; 2093 }, 2094 setVideoExtension (uri, format) { 2095 if (!uri || !format) return ''; 2096 const decodedUri = new URL(uri); 2097 if (decodedUri.host !== 'res.cloudinary.com') { 2098 // Not a cloudinary uri, do not rewrite 2099 if (decodedUri.pathname.endsWith(`.${format}`)) return uri; 2100 return ''; 2101 } 2102 // Rewrite the cloudinary uri 2103 const lastPeriodIdx = decodedUri.pathname.lastIndexOf('.'); 2104 let pathWithoutFormat = decodedUri.pathname; 2105 if (lastPeriodIdx !== -1) pathWithoutFormat = decodedUri.pathname.substr(0, lastPeriodIdx); 2106 decodedUri.pathname = `${pathWithoutFormat}.${format}`; 2107 return decodedUri.toString(); 2108 }, 2109 addVideoSources (uri) { 2110 if (!uri) return ''; 2111 const VIDEO_VARIANTS = [ 2112 { 2113 format: 'webm', 2114 codec: 'vp9', 2115 htmlCodecs: ';codecs=vp9' 2116 }, 2117 { 2118 format: 'mp4', 2119 codec: 'h265', 2120 htmlCodecs: ';codecs=hvc1' 2121 }, 2122 { 2123 format: 'mp4', 2124 codec: 'h264', 2125 htmlCodecs: '' 2126 } 2127 ]; 2128 const VIDEO_EXT_REGEXP = /\.(?:3gp|mpeg|mp4|m4v|m4p|ogv|ogg|mov|webm)$/; 2129 if (!uri.includes('res.cloudinary.com')) // keep existing or no extension for non Cloudinary urls 2130 return `<source data-src="${uri}">`; 2131 // Cap delivery at 1080p (1920px wide) while preserving aspect ratio. Width-only: 2132 // c_limit never upscales, so portrait sources are left at their native size. 2133 // No q_auto: quality is left to Cloudinary's default rather than forced down. 2134 // Note the regex below consumes any q_auto in the source URL, so it is dropped 2135 // rather than carried through. 2136 const CONSTRAINED_DELIVERY = 'w_1920,c_limit'; 2137 const uriNoExt = uri.replace(VIDEO_EXT_REGEXP, ''); 2138 const sources = VIDEO_VARIANTS.map((type)=>{ 2139 const cloudinaryFormatUrl = `${uriNoExt.replace(/f_auto(?::video)?(?:,q_auto(?::\w+)?)?/, `f_${type.format},vc_${type.codec},${CONSTRAINED_DELIVERY}`)}.${type.format}`; 2140 return `<source data-src="${cloudinaryFormatUrl}" type="video/${type.format}${type.htmlCodecs}">`; 2141 }); 2142 return sources.join('\n'); 2143 }, 2144 cloudinaryVideoFirstFrame: $0ce6edea77f7b81a$exports.getVideoFirstFrame, 2145 initials (text, options) { 2146 if (!text) return ''; 2147 if (options?.hash?.endChar) // eslint-disable-next-line 2148 text = text.split(options.hash.endChar)[0]; 2149 // eslint-disable-next-line 2150 const maxChars = +options?.hash?.maxChars || 2; // we do not allow this to be 0 2151 let result = text.match(/\b\w/g)?.slice(0, maxChars); 2152 if (!result) return ''; 2153 if (options?.hash?.addText) result = result.map((item)=>item + options.hash.addText); 2154 return result.join(options?.hash?.joinWith || '').toUpperCase(); 2155 }, 2156 normalizePhone (phone) { 2157 if (!phone) return ''; 2158 const str = String(phone).trim(); 2159 return (str.startsWith('+') ? '+' : '') + str.replace(/\D/g, ''); 2160 } 2161 }; 2162} 2163$53de3c160faf5a81$exports = $53de3c160faf5a81$var$handlebarsHelpersFactory; 2164 2165 2166/* global window, document */ // eslint-disable-next-line import/prefer-default-export 2167class $dc851cf3939b6ef5$export$ff0712c26d5458f7 { 2168 /** 2169 * Initialize Modal Controller 2170 * @param {Object} options 2171 */ constructor(options){ 2172 this.isBuilder = false;
2173 this.companyId = null; 2174 this.websiteId = null; 2175 this.pageId = null; 2176 this.shareId = null; 2177 this.pageMeta = null; 2178 this.websiteApiGatewayUrl = null; 2179 this.buyerSellerServiceUrl = null; 2180 this.leadIngesterUrl = null; 2181 this.divolteServiceUrl = null; 2182 this.eventBus = null; 2183 this.templateId = null; 2184 this.myhomeDashboardEnabled = false; 2185 this.mlsEnabled = false; 2186 this.hstValuationRedirectEnabled = false; 2187 this.googleMapsAPIKey = null; 2188 } 2189 /** 2190 * Controllers must implement a "destroy" method to be called 2191 * after page navigation to remove event handlers / cleanup 2192 */ destroy() {} 2193 /** 2194 * Set page and supporting data 2195 * @param {string} pageId 2196 * @param {Object} options 2197 */ setPage(pageId, options = {}) { 2198 this.pageId = pageId; 2199 this.shareId = options.shareId || null; 2200 this.pageMeta = { 2201 sourceResource: options.sourceResource, 2202 pageElementId: options.pageElementId, 2203 pageQueryVariables: options.pageQueryVariables || {} 2204 }; 2205 } 2206 /** 2207 * Sets if we are in pagebuilder context or not 2208 * @param {boolean} isBuilder 2209 */ setIsBuilder(isBuilder) { 2210 this.isBuilder = isBuilder; 2211 } 2212 /** 2213 * Set Company and supporting data 2214 * @param {string} companyId 2215 * @param {Object} options 2216 */ setCompany(companyId, options = {}) { 2217 this.companyId = companyId; 2218 } 2219 /** 2220 * Set website and supporting data 2221 * @param {string} websiteId 2222 * @param {Object} options 2223 */ setWebsite(websiteId, options = {}) { 2224 this.websiteId = websiteId; 2225 if (options.myhomeDashboardEnabled) this.myhomeDashboardEnabled = options.myhomeDashboardEnabled; 2226 if (options.mlsEnabled) this.mlsEnabled = options.mlsEnabled; 2227 if (options.hstValuationRedirectEnabled) this.hstValuationRedirectEnabled = options.hstValuationRedirectEnabled; 2228 } 2229 /** 2230 * Set websiteApiGatewayUrl 2231 * @param {string} websiteApiGatewayUrl 2232 * @param {Object} options 2233 */ setWebsiteApiGatewayUrl(websiteApiGatewayUrl, options = {}) { 2234 this.websiteApiGatewayUrl = websiteApiGatewayUrl; 2235 } 2236 /** 2237 * Set buyerSellerServiceUrl 2238 * @param {string} buyerSellerServiceUrl 2239 * @param {Object} options 2240 */ setBuyerSellerServiceUrl(buyerSellerServiceUrl, options = {}) { 2241 this.buyerSellerServiceUrl = buyerSellerServiceUrl; 2242 } 2243 /** 2244 * Set divolteServiceUrl and supporting data 2245 * @param {string} divolteServiceUrl 2246 * @param {Object} options 2247 */ setDivolteServiceUrl(divolteServiceUrl, options = {}) { 2248 this.divolteServiceUrl = divolteServiceUrl; 2249 } 2250 /** 2251 * Set eventBusUrl 2252 * @param {string} eventBusUrl 2253 * @param {Object} options 2254 */ setEventBusUrl(eventBusUrl, options = {}) { 2255 this.eventBusUrl = eventBusUrl; 2256 } 2257 /** 2258 * Set templateId and supporting data 2259 * @param {string} templateId 2260 * @param {Object} options 2261 */ setTemplateId(templateId, options = {}) { 2262 this.templateId = templateId; 2263 } 2264 /** 2265 * Set leadIngesterUrl 2266 * @param {string} leadIngesterUrl 2267 * @param {Object} options 2268 */ setLeadIngesterUrl(leadIngesterUrl, options = {}) { 2269 this.leadIngesterUrl = leadIngesterUrl; 2270 } 2271 /** 2272 * Set googleMapsAPIKey 2273 * @param {string} googleMapsAPIKey 2274 * @param {Object} options 2275 */ setGoogleMapsAPIKey(googleMapsAPIKey, options = {}) { 2276 this.googleMapsAPIKey = googleMapsAPIKey; 2277 } 2278 /** 2279 * Set eventBus 2280 * @param {Object} options 2281 */ setEventBus({ enabled: enabled, apiKey: apiKey, url: url }) { 2282 this.eventBus = { 2283 enabled: enabled, 2284 apiKey: apiKey, 2285 url: url 2286 }; 2287 } 2288} 2289 2290 2291/* eslint-disable import/prefer-default-export */ // eslint-disable-next-line import/prefer-default-export 2292class $7c391e2f7931d60d$export$3b5bd9381a52554c { 2293 /** 2294 * Initialize Base Controller at build time 2295 * @param { ConfigController } config 2296 * @param {Window} window 2297 * @param {Document} document 2298 */ constructor(config, window, document){ 2299 // TODO: Listen for events from configController to update configuration 2300 this.config = config; 2301 this.window = window; 2302 this.document = document; 2303 if (!this.destroy) // With turbolinks, we want to ensure that on each page load we have the latest version of the sdk 2304 // Controllers are deconstructed and replaced (on the window object) per request 2305 // Controllers must use the destory method to remove event handlers to prevent undefined behavior 2306 throw new Error('Controllers must implement a deconstructor'); 2307 } 2308} 2309 2310 2311// eslint-disable-next-line import/prefer-default-export 2312const $a3e97e89bbc2a366$export$f037eeffb5561205 = 2000; 2313 2314 2315class $0ead03fdb95fa9ce$export$d507f25e63650365 extends (0, $7c391e2f7931d60d$export$3b5bd9381a52554c) { 2316 /** 2317 * Initialize Modal Controller at build time 2318 * @param { ConfigController } config 2319 * @param {Window} window 2320 * @param {Document} document 2321 */ constructor(config, window, document){ 2322 super(config, window, document); 2323 this.setLoginState = this.setLoginState.bind(this); 2324 this.pageId = null; 2325 this.modals = []; 2326 this.exitModalTriggered = {}; 2327 this.exitModalEventListeners = []; 2328 } 2329 /** 2330 * Initialize Modal Controller at run time 2331 * Attach handlers and events to DOM 2332 * @param {Object} options 2333 */ initialize(options) { 2334 try { 2335 const { config: config, document: document } = this; 2336 this.pageId = config.pageId; 2337 this.modals = document.querySelectorAll('body > #modals > .modal');
2338 options.modals.forEach((m)=>{ 2339 this._attachModalHandler(m); 2340 }); 2341 } catch (e) { 2342 // eslint-disable-next-line no-console 2343 console.log(e); 2344 } 2345 } 2346 /** 2347 * Controllers must implement a "destroy" method to be called 2348 * after page navigation to remove event handlers / cleanup 2349 */ destroy() { 2350 const { document: document } = this; 2351 this.exitModalEventListeners.forEach((listener)=>{ 2352 document.removeEventListener('mouseout', listener); 2353 }); 2354 this.modals = []; 2355 this.exitModalTriggered = {}; 2356 this.exitModalEventListeners = []; 2357 } 2358 /** 2359 * Set modal to local storage with expiration 2360 * @param {string} modalId 2361 */ setModalWithExpiry(modalId) { 2362 const { window: window } = this; 2363 const TTL = 86400000; // 24 hours 2364 const now = new Date(); 2365 const expiry = now.getTime() + TTL; 2366 const key = `modal-controller-${modalId}`; 2367 try { 2368 window.localStorage.setItem(key, JSON.stringify(expiry)); 2369 } catch (err) { 2370 // catch error when setting item to storage on iOS private browser 2371 } 2372 } 2373 /** 2374 * Save success login modal to local storage 2375 */ setLoginState() { 2376 const { window: window } = this; 2377 try { 2378 window.localStorage.setItem('isSignedUp', 'true'); 2379 } catch (err) { 2380 // catch error when setting item to storage on iOS private browser 2381 } 2382 } 2383 /** 2384 * Get modal and check expiration 2385 * @param {string} modalId 2386 * @return {boolean} 2387 */ getModalWithExpiry(modalId) { 2388 const { window: window } = this; 2389 const storage = window.localStorage; 2390 const expirationTime = storage.getItem(`modal-controller-${modalId}`); 2391 if (!expirationTime) return false; 2392 const expiry = JSON.parse(expirationTime); 2393 const now = new Date(); 2394 if (now.getTime() > expiry) { 2395 storage.removeItem(`modal-controller-${modalId}`); 2396 return false; 2397 } 2398 return true; 2399 } 2400 /** 2401 * Make a modal visible 2402 * @param {string} modalId 2403 * @param {Object} options 2404 */ show(modalId, options = {}) { 2405 // TODO: migrate logic from page.handlebars 2406 const { window: window } = this; 2407 const modalViewed = this.getModalWithExpiry(modalId); 2408 // Only show a modal if it has not been seen 2409 // Does not affect "show modal on button click" 2410 if (modalId && !modalViewed || options.forced === true) { 2411 window.lpUI.showModal(`modal-${modalId}`, options); 2412 this.setModalWithExpiry(modalId); 2413 } 2414 } 2415 /** 2416 * Attach events based on Modal type and page settings 2417 * @param {Modal} modal 2418 */ _attachModalHandler(modal) { 2419 const { pageId: pageId } = this; 2420 const { trigger: trigger, pageSpecific: pageSpecific } = modal; 2421 if (pageSpecific) { 2422 const pageIds = modal.pages.map((p)=>p.pageId); 2423 if (!pageIds.includes(pageId)) // Modal does not belong to this page 2424 return; 2425 } 2426 if (trigger === 'EXIT') this._exitModalHandler(modal); 2427 else if (trigger === 'TIMER') this._timerModalHandler(modal); 2428 else if (trigger === 'FORCED') this._forcedModalHandler(modal); 2429 } 2430 /** 2431 * Attach events for exit Modals 2432 * @param {Modal} modal 2433 */ _exitModalHandler(modal) { 2434 // eslint-disable-next-line 2435 const that = this; 2436 const { document: document, exitModalTriggered: exitModalTriggered } = this; 2437 const { modalId: modalId } = modal; 2438 /** 2439 * Handle the mouseout dom event 2440 * @param {Event} event 2441 */ function triggerExitModal(event) { 2442 if (exitModalTriggered[modalId]) return; 2443 if (!event.toElement && !event.relatedTarget) { 2444 that.show(modalId); 2445 exitModalTriggered[modalId] = true; 2446 } 2447 } 2448 document.addEventListener('mouseout', triggerExitModal); 2449 this.exitModalEventListeners.push(triggerExitModal); 2450 } 2451 /** 2452 * Attach events for timer Modals
2453 * @param {Modal} modal 2454 */ _timerModalHandler(modal) { 2455 const { modalId: modalId, timerDelay: timerDelay } = modal; 2456 setTimeout(()=>{ 2457 this.show(modalId); 2458 }, timerDelay); 2459 } 2460 /** 2461 * Attach events for Forced Modals 2462 * @param {Modal} modal 2463 */ _forcedModalHandler(modal) { 2464 const { modalId: modalId } = modal; 2465 const { window: window, document: document } = this; 2466 const isLoggedIn = window.localStorage.getItem('isSignedUp') === 'true'; 2467 if (!isLoggedIn) { 2468 // watch for event triggering success login 2469 const forcedModal = document.querySelector(`#modal-${modalId}`); 2470 const contactForm = forcedModal?.querySelector('form'); 2471 const MODAL_CLOSE_DELAY = contactForm?.dataset.delay || (0, $a3e97e89bbc2a366$export$f037eeffb5561205); 2472 // form submit autoclose is handled by contactForms controller 2473 contactForm.addEventListener('formsubmitted', ()=>{ 2474 this.setLoginState(); 2475 }); 2476 window.addEventListener('oauthSuccess', ()=>{ 2477 this.setLoginState(); 2478 contactForm.classList.add('success'); 2479 setTimeout(()=>{ 2480 window.lpUI.hideModal(`modal-${modalId}`); 2481 contactForm.reset(); 2482 contactForm.classList.remove('success'); 2483 }, MODAL_CLOSE_DELAY); 2484 }); 2485 this.show(modalId, { 2486 forced: true 2487 }); 2488 } 2489 } 2490} 2491 2492 2493/* eslint-disable import/prefer-default-export */ /* global window */ /* eslint-disable import/prefer-default-export */ /** 2494 * Handle navigation push 2495 * @param {string} href 2496 * @param {boolean} _blank 2497 */ const $fc0a058ae42af628$export$95e8d77caafa3e06 = (href, _blank = false)=>{ 2498 if (!href) return; 2499 if (_blank) { 2500 window.open(href); 2501 return; 2502 } 2503 window.location.assign(href); 2504}; 2505const $fc0a058ae42af628$export$267a1435fe18b8d4 = (obj, path, defValue)=>{ 2506 if (!path) return undefined; 2507 const pathArray = Array.isArray(path) ? path : path.match(/([^[.\]])+/g); 2508 const result = pathArray.reduce((prevObj, key)=>prevObj && prevObj[key], obj); 2509 return result === undefined ? defValue : result; 2510}; 2511 2512 2513 2514class $d48f9f944810f613$export$ca3218b7861b9cb0 extends (0, $7c391e2f7931d60d$export$3b5bd9381a52554c) { 2515 /** 2516 * Initialize Button Controller at build time 2517 * @param { ConfigController } config 2518 * @param {Window} window 2519 * @param {Document} document 2520 */ constructor(config, window, document){ 2521 super(config, window, document); 2522 this._boundClickHandler = this._buttonClickHandler.bind(this); 2523 } 2524 /** 2525 * Initialize Button Controller at run time 2526 * Attach handlers and events to DOM 2527 * @param {Object} options 2528 */ initialize(options = {}) { 2529 try { 2530 const { document: document } = this; 2531 document.body.removeEventListener('click', this._boundClickHandler); 2532 document.body.addEventListener('click', this._boundClickHandler); 2533 } catch (e) { 2534 // eslint-disable-next-line no-console 2535 console.error(e); 2536 } 2537 } 2538 /** 2539 * Controllers must implement a "destroy" method to be called 2540 * after page navigation to remove event handlers / cleanup 2541 */ destroy() { 2542 const { document: document } = this; 2543 document.body.removeEventListener('click', this._boundClickHandler); 2544 } 2545 /** 2546 * Buttons click handler 2547 * @param {event} e 2548 */ _buttonClickHandler(e) { 2549 this._handleButtonClick(e).catch((e)=>console.error(e)); 2550 } 2551 /** 2552 * Handle button click 2553 * We need special function to have easy way to detach listener 2554 * @param {Event} e 2555 */ async _handleButtonClick(e) { 2556 const { window: window, document: document } = this; 2557 const button = e.target.closest('button, a'); 2558 if (!button) return; 2559 const type = button.dataset.type || ''; 2560 const href = button.href || button.dataset.href || ''; 2561 const target = button.target || ''; 2562 const { submit: submit } = button.dataset; 2563 if (submit) { 2564 try { 2565 await this._handleSubmit(button); 2566 } catch (err) { 2567 // eslint-disable-next-line no-console 2568 console.error(err); 2569 } 2570 (0, $fc0a058ae42af628$export$95e8d77caafa3e06)(href, target === '_blank'); 2571 } 2572 this._trackConversion(button); 2573 if (type === 'VIDEO') { 2574 this._handleVideoButton(button); 2575 return; 2576 } 2577 if (type === 'CONTACT_US') { 2578 this._handleContactUsButton(button); 2579 return; 2580 } 2581 if (type === 'SUBSCRIBE') { 2582 // TODO: Extend for an overwritten subscribe 2583 window.lpUI.showModal('modal-global-subscribe'); 2584 return; 2585 } 2586 if (type === 'MY_ACCOUNT') { 2587 // TODO: Extend for an overwritten subscribe 2588 window.lpUI.showModal('modal-global-my-account'); 2589 return; 2590 } 2591 if (type === 'SHARE_POPUP') { 2592 // TODO: Extend for an overwritten subscribe 2593 const sharePopup = button.querySelector('.share-popup'); 2594 if (sharePopup) sharePopup.style.display = 'flex'; 2595 e.stopPropagation(); // this may not work in current 'delegate' mode 2596 return; 2597 } 2598 // Custom modals 2599 if (type.substring(0, 6) === 'MODAL_') { 2600 const modalId = type.substring(6); 2601 window.lpUI.showModal(`modal-${modalId}`); 2602 return; 2603 } 2604 // MAILTO / TEL Event 2605 if (href?.startsWith('mailto:')) { 2606 const email = href.substring(7); 2607 // TODO: Replace with SDK method 2608 document.sendGoogleAnalyticsEvent({ 2609 hitType: 'event', 2610 eventCategory: 'button', 2611 eventAction: 'click', 2612 eventLabel: 'Email', 2613 eventValue: email 2614 }); 2615 } else if (href?.startsWith('tel:')) { 2616 const phoneNumber = href.substring(4); 2617 // TODO: Replace with SDK method 2618 document.sendGoogleAnalyticsEvent({ 2619 hitType: 'event', 2620 eventCategory: 'button', 2621 eventAction: 'click', 2622 eventLabel: 'Phone', 2623 eventValue: phoneNumber 2624 }); 2625 } 2626 } 2627 /** 2628 * Handle submit 2629 * @param {HTMLButtonElement|HTMLAnchorElement} button 2630 */ async _handleSubmit(button) { 2631 const form = button.closest('form'); 2632 const { window: window } = this; 2633 if (form) return await window.luxuryPresence.contactForms.submitForm(form); 2634 return null; 2635 } 2636 _getVimeoSrc(link) { 2637 if (!link || !link.includes('vimeo.com') || link.length > 2000) return null; 2638 const matchResult = link.match(/(?:(?:www\.)?vimeo\.com\/(?:showcase\/\d+(?:\/video\/|\?video[=/])|channels\/(?:\w+\/)?|groups\/(?:[^/]*)\/videos\/|)|player\.vimeo\.com\/video\/)(\d+)(?:(?:\?.*&*h=|\/)+([\d,a-f]+))?/); 2639 if (!matchResult) return null; 2640 const [, vimeoId, vimeoHashParameter] = matchResult; 2641 let src = `https://player.vimeo.com/video/${vimeoId}?`; 2642 if (vimeoHashParameter) src += `h=${vimeoHashParameter}&`; 2643 src += 'autoplay=1'; 2644 return src; 2645 } 2646 /** 2647 * Handle type video click 2648 * @param {HTMLElement} button 2649 */ // eslint-disable-next-line sonarjs/cognitive-complexity 2650 _handleVideoButton(button) { 2651 const { document: document } = this; 2652 const section = button.closest('section'); 2653 // Embed youtube/vimeo video url 2654 let { src: src } = button.dataset; 2655 let youtubeId = ''; 2656 if (src && (src.includes('youtube.com') || src.includes('youtu.be')) && !src.includes('youtube.com/embed')) { 2657 // test link: https://regexr.com/3anm9 2658 const youtubeRegex = /(?:youtube\.com\/\S*(?:(?:\/e(?:mbed))?\/|watch\?(?:\S*?&?v=)
2658)|youtu.be\/)([a-zA-Z0-9_-]{6,11})/; 2659 const youtubeResult = src.match(youtubeRegex); 2660 // eslint-disable-next-line 2661 if (youtubeResult) youtubeId = youtubeResult[1]; 2662 } 2663 const vimeoSrc = this._getVimeoSrc(src); 2664 if (youtubeId) src = `https://youtube.com/embed/${youtubeId}?autoplay=1&controls=0`; 2665 else if (vimeoSrc) src = vimeoSrc; 2666 if (!src || !section) return; 2667 const isVideoFile = src.endsWith('.mp4') || src.endsWith('.webm') || src.includes('cloudinary.com'); 2668 const modalHtml = ` 2669 <div id="modal-${section.id}" class="modal visible"> 2670 <div class="modal-content"> 2671 <div class="lightbox-video"> 2672 <div class="video-player"> 2673 <div class="embed-container"> 2674 ${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>`} 2675 </div> 2676 </div> 2677 </div> 2678 </div> 2679 <button class="close lp-close" type="button" aria-label="close modal"><span class="lp-close-icon" aria-hidden="true"></span></button> 2680 </div> 2681 `; 2682 document.body.insertAdjacentHTML('beforeend', modalHtml); 2683 const modal = document.getElementById(`modal-${section.id}`); 2684 const closeModal = modal.querySelector('.close'); 2685 closeModal.addEventListener('click', ()=>{ 2686 modal.remove(); 2687 }); 2688 } 2689 /** 2690 * Handle type contact us click 2691 * @param {HTMLElement} button 2692 */ _handleContactUsButton(button) { 2693 const { window: window, document: document } = this; 2694 // TODO: Extend for an overwritten contact us 2695 document.sendGoogleAnalyticsEvent({ 2696 hitType: 'event', 2697 eventCategory: 'button', 2698 eventAction: 'click', 2699 eventLabel: 'contact' 2700 }); 2701 window.lpUI.showModal('modal-global-contact-us', { 2702 scrollLock: true 2703 }); 2704 } 2705 /** 2706 * Track conversion 2707 * @param {HTMLElement} button 2708 */ _trackConversion(button) { 2709 const { window: window, document: document } = this; 2710 // Conversion Tracking Options 2711 const { gtagConversionId: gtagConversionId, gtagConversionLabel: gtagConversionLabel, fbEventName: fbEventName } = button.dataset; 2712 if (gtagConversionId && gtagConversionLabel) { 2713 if (window.dataLayer && window.dataLayer.length) { 2714 const configured = window.dataLayer.some((layer)=>layer.length && layer[0] === 'config' && layer[1] === gtagConversionId); 2715 if (!configured) document.sendGoogleTagManagerEvent('config', gtagConversionId); 2716 } 2717 const sentTo = `${gtagConversionId}/${gtagConversionLabel}`; 2718 document.sendGoogleTagManagerEvent('event', 'conversion', { 2719 send_to: sentTo 2720 }); 2721 } 2722 if (window.fbq && fbEventName) window.fbq('track', fbEventName, {}); 2723 } 2724} 2725 2726 2727/* eslint-disable import/prefer-default-export */ 2728class $68a2d4aa500c7a05$export$61ec8f2b6291ec31 extends (0, $7c391e2f7931d60d$export$3b5bd9381a52554c) { 2729 /** 2730 * Initialize Validation Controller at build time 2731 * @param { ConfigController } config 2732 * @param {Window} window 2733 * @param {Document} document 2734 */ constructor(config, window, document){ 2735 super(config, window, document); 2736 this.validatedForms = []; 2737 } 2738 /** 2739 * Initialize Contact Form Controller at run time 2740 * Attach handlers and events to DOM 2741 * @param {Object} options 2742 */ initialize(options = {}) { 2743 try { 2744 const { window: window, document: document } = this; 2745 const { selector: selector } = options; 2746 let validatedForms = []; 2747 if (selector) validatedForms = document.querySelectorAll(`${selector} form[data-type="contact-form"]`); 2748 else validatedForms = document.querySelectorAll('form[data-type="contact-form"]'); 2749 const isPhoneValidationLibRequired = !!document.querySelectorAll('input[type="tel"]').length; 2750 // we are loading the phone validation lib from the globally defined list of libs 2751 if (isPhoneValidationLibRequired && !window.libphonenumber && window?.luxuryPresence?.libs) { 2752 const { loadJs: loadJs, jsLibsMap: jsLibsMap } = window.luxuryPresence.libs; 2753 const { libphonenumber: libphonenumber } = jsLibsMap; 2754 loadJs(libphonenumber.jsSrc, this.attachValidation.bind(this, validatedForms)); 2755 }
2755 else this.attachValidation(validatedForms); 2756 this.validatedForms = this.validatedForms.concat(validatedForms); 2757 } catch (e) { 2758 // eslint-disable-next-line no-console 2759 console.log(e); 2760 } 2761 } 2762 /** 2763 * Controllers must implement a "destroy" method to be called 2764 * after page navigation to remove event handlers / cleanup 2765 */ destroy() { 2766 this.validatedForms = []; 2767 } 2768 /** 2769 * Validate all forms in the document 2770 * @param {NodeListOf<HTMLFormElement>} forms 2771 */ attachValidation(forms) { 2772 forms.forEach((form)=>{ 2773 const inputs = form.querySelectorAll('input, select, textarea'); 2774 inputs.forEach((input)=>{ 2775 this.applyFieldValidation(input); 2776 }); 2777 }); 2778 } 2779 /** 2780 * Field Validation Handler from global context 2781 * @param {HTMLInputElement|HTMLTextAreaElement|HTMLSelectElement} inputField 2782 */ applyFieldValidation(inputField) { 2783 const fieldType = inputField.getAttribute('type'); 2784 if (fieldType === 'tel') this._applyPhoneValidation(inputField); 2785 } 2786 /** 2787 * Phone Field Validation Handler 2788 * @param {HTMLInputElement} inputField 2789 */ _applyPhoneValidation(inputField) { 2790 const { window: window } = this; 2791 if (window.libphonenumber) { 2792 // https://codepen.io/coinscope/pen/BaXBvBp?editors=1010 2793 const phoneUtil = window.libphonenumber.isValidPhoneNumber; 2794 inputField.addEventListener('keyup', function() { 2795 if (this.hasAttribute('required') || this.value.replace(/\s/g, '').length) try { 2796 const phoneNumber = this.value; 2797 const region = 'US'; 2798 const isValidNumber = phoneUtil(phoneNumber, region); 2799 if (!isValidNumber) this.setCustomValidity('Please enter a valid phone number.'); 2800 else this.setCustomValidity(''); 2801 } catch (e) { 2802 this.setCustomValidity('Please enter a valid phone number.'); 2803 } 2804 else this.setCustomValidity(''); 2805 }); 2806 } 2807 } 2808} 2809 2810 2811 2812 2813/** 2814 * Fires the `generate_lead` Google Ads conversion on any marketing-site lead 2815 * event â lead-form submits (all routed through ContactFormController) and 2816 * social sign-in (oauth.js) â at most once per session. 2817 * 2818 * Reuses the same `generate_lead` event the Home Search Tool fires; the page 2819 * template already configures the LP Google tags, so no new Google Ads setup is 2820 * needed. Cross-tab/visitor dedupe is left to the conversion action's 2821 * Count="One" â localStorage is intentionally avoided as it would suppress a 2822 * returning visitor's later lead. 2823 */ const $3ec2bf3965f87ecc$export$78b64890ea40f4e4 = 'generate_lead'; 2824const $3ec2bf3965f87ecc$export$568da12be2433b06 = 'lp_lead_form_conversion_fired'; 2825function $3ec2bf3965f87ecc$var$alreadyFired(window) { 2826 try { 2827 return window.sessionStorage.getItem($3ec2bf3965f87ecc$export$568da12be2433b06) === 'true'; 2828 } catch { 2829 // sessionStorage can throw in private browsing; fail open so we still fire. 2830 return false; 2831 } 2832} 2833function $3ec2bf3965f87ecc$var$markFired(window) { 2834 try { 2835 window.sessionStorage.setItem($3ec2bf3965f87ecc$export$568da12be2433b06, 'true'); 2836 } catch { 2837 // Ignore â see alreadyFired(). 2838 } 2839} 2840function $3ec2bf3965f87ecc$export$e70565180c0e6950(window, document) { 2841 if (!window || !document) return; 2842 // GA bootstrap may not have run yet (or no pixel on this page); don't throw, 2843 // and don't burn the once-per-session flag if we can't actually fire. 2844 if (typeof document.sendGoogleTagManagerEvent !== 'function') return; 2845 if ($3ec2bf3965f87ecc$var$alreadyFired(window)) return; 2846 try { 2847 // Mark fired only AFTER a successful send, so a thrown GTM call doesn't 2848 // permanently suppress the conversion for the rest of the session â and 2849 // never let an analytics failure break the caller's success flow. 2850 document.sendGoogleTagManagerEvent('event', $3ec2bf3965f87ecc$export$78b64890ea40f4e4); 2851 $3ec2bf3965f87ecc$var$markFired(window); 2852 } catch { 2853 // Swallow â analytics must not break form submit / sign-in. 2854 } 2855} 2856 2857 2858 2859const $2aefad83322125c9$var$homeValuationRoute = '/api/v1/home-valuation'; 2860class $2aefad83322125c9$export$2e2bcd8739ae039 extends (0, $7c391e2f7931d60d$export$3b5bd9381a52554c) { 2861 destroy() { 2862 // 2863 } 2864 /** 2865 * Clean address string by removing call time frame suffix and extra whitespace 2866 * 2867 * Handles 17 templates that append ", call time frame: [value]" to the address: 2868 * - TheTrailblazerSir_2, TheMasterpiece (2x), TheInspirationSir_1, TheIconSir_3
2869 * - Influencer, GlobalCollective, Evolution, CorcoranAgent_1 2870 * - CompassSE (2x), CompassPrestige (2x), CompassNavigator 2871 * - ColdwellBanker (2x), Classic 2872 * 2873 * @param {string} address - Raw address string that may contain time frame info 2874 * @return {string} - Cleaned address string 2875 */ _cleanAddressString(address) { 2876 if (!address || typeof address !== 'string') return ''; 2877 let cleaned = address.trim(); 2878 // Fix 1: Handle "User entered address:\n..." pattern from TheProducer templates 2879 // Extract only the user entered address, ignore geocoded part 2880 if (cleaned.includes('User entered address:')) { 2881 const match = cleaned.match(/User entered address:\s*\n?\s*([^\n]+)/); 2882 if (match && match[1]) cleaned = match[1].trim(); 2883 } 2884 // Fix 2: Remove ", call time frame: ..." suffix if present (affects 17 templates) 2885 const timeFrameIndex = cleaned.indexOf(', call time frame:'); 2886 if (timeFrameIndex !== -1) cleaned = cleaned.substring(0, timeFrameIndex).trim(); 2887 return cleaned; 2888 } 2889 /** 2890 * Transform Google Maps address components to structured format 2891 * @param {Array} addressComponents - Google Maps address components 2892 * @return {Object} - Structured address object 2893 */ _transformAddressComponents(addressComponents) { 2894 const get = (t)=>(addressComponents.find((c)=>c.componentType === t) || {}).componentName?.text; 2895 const structuredAddress = {}; 2896 const streetNumber = get('street_number'); 2897 const streetName = get('route'); 2898 const state = get('administrative_area_level_1'); 2899 const zipcode = get('postal_code'); 2900 const addressLine2 = get('subpremise'); 2901 // explicit precedence for city 2902 const city = get('locality') || get('sublocality_level_1') || get('neighborhood'); 2903 if (streetNumber) structuredAddress.streetNumber = streetNumber; 2904 if (streetName) structuredAddress.streetName = streetName; 2905 if (city) structuredAddress.city = city; 2906 if (state) structuredAddress.state = state; 2907 if (zipcode) structuredAddress.zipcode = zipcode; 2908 if (addressLine2) structuredAddress.addressLine2 = addressLine2; 2909 return structuredAddress; 2910 } 2911 /** 2912 * Extract address and coordinates from Google Maps validation data 2913 * @param {Object} validatedAddressData - Google Maps validation result 2914 * @return {Object} - Object containing structuredAddress and coordinates 2915 */ _extractAddressAndCoordinates(validatedAddressData) { 2916 let structuredAddress = null; 2917 let coordinates = null; 2918 if (validatedAddressData?.result) { 2919 const { result: result } = validatedAddressData; 2920 // Extract structured address components 2921 if (result.address?.addressComponents) structuredAddress = this._transformAddressComponents(result.address.addressComponents); 2922 // Extract coordinates 2923 if (result.geocode?.location) coordinates = { 2924 latitude: result.geocode.location.latitude, 2925 longitude: result.geocode.location.longitude 2926 }; 2927 } 2928 return { 2929 structuredAddress: structuredAddress, 2930 coordinates: coordinates 2931 }; 2932 } 2933 /** 2934 * Filter out empty string values from an object, keeping only non-empty values 2935 * @param {Object} obj - Object to filter 2936 * @return {Object} - Filtered object without empty strings 2937 */ _filterEmptyStrings(obj) { 2938 const filtered = {}; 2939 for (const [key, value] of Object.entries(obj))if (value !== '' && value !== null && value !== undefined) filtered[key] = value; 2940 return filtered; 2941 } 2942 /** 2943 * Extract raw address from form data with priority fallback chain. 2944 * Order is critical to handle 38 home valuation templates with different field naming: 2945 * 2946 * IMPORTANT: contactForms.js (line 189) stores name="message" in comments_attributes[0].text 2947 * because 'message' is in DEFAULT_LEAD_FIELDS. Fields NOT in DEFAULT_LEAD_FIELDS go to content. 2948 * 2949 * Priority (most specific â most generic): 2950 * 1. address - Pattern 1: 9 templates (explicit address, stored in content) 2951 * 2. property-address - Pattern 3: 4 templates (dash variant with JS, stored in content) 2952 * 3. property_address - Pattern 4: 3 templates (underscore, stored in content - handles VanguardProperties!) 2953 * 4. Property_Address - Pattern 5: 1 template (capital P - TheProducer/HomeValuationRansomHouse, stored in content)
2954 * 5. comments_attributes[0].text - Pattern 2: ALL 17 templates with name="message" store HERE! 2955 * (e.g., Evolution/HomeValuation, TheProducer/HomeValuation, etc.) 2956 * 6. message - Pattern 6: 4 templates with JS-populated message (rare, checked last) 2957 * 7-8. homeValuationAddress, propertyAddress - Legacy/unknown formats 2958 * 2959 * Critical: Each field is cleaned BEFORE checking, so dropdown values are skipped and we try next field 2960 * 2961 * @param {Object} data - Form data 2962 * @return {string} - Raw address string (may contain trailing text like ", call time frame: ...") 2963 */ _getRawAddress(data) { 2964 const content = data.activity.content || {}; 2965 const comments = data.activity.comments_attributes?.[0]?.text; 2966 const possibleFields = [ 2967 content.address, 2968 content['property-address'], 2969 content.property_address, 2970 content.Property_Address, 2971 comments, 2972 content.message, 2973 content.homeValuationAddress, 2974 content.propertyAddress 2975 ]; 2976 return possibleFields.map((field)=>field && this._cleanAddressString(field)).find(Boolean) || ''; 2977 } 2978 /** 2979 * Logs lead-related events for monitoring 2980 * @param {string} eventType - Type of event 2981 * @param {Object} data - Form data 2982 * @param {Error} error - Optional error object 2983 */ _logLeadEvent(eventType, data, error = null) { 2984 const eventPayload = { 2985 event: eventType, 2986 companyId: this.config.companyId, 2987 websiteId: this.config.websiteId, 2988 timestamp: new Date().toISOString(), 2989 hasEmail: !!data.activity?.author_email, 2990 hasAddress: !!this._getRawAddress(data) 2991 }; 2992 if (error) { 2993 // Use userMessage (sanitized) instead of raw message to prevent PII exposure 2994 // userMessage is set by _getUserFriendlyErrorMessage() which provides safe messages 2995 eventPayload.errorMessage = error.userMessage || (error.status ? `Request failed with status ${error.status}` : 'An error occurred'); 2996 eventPayload.errorName = error.name; 2997 eventPayload.errorStatus = error.status; 2998 } 2999 // Log to console with structured format for log aggregation 3000 // eslint-disable-next-line no-console 3001 console.error(`[HomeValuation] ${eventType}`, JSON.stringify(eventPayload)); 3002 // If PostHog is available, send event for analytics 3003 // Wrap in try/catch to prevent SDK failures from blocking submissions 3004 if (this.window.posthog) try { 3005 this.window.posthog.capture(eventType, eventPayload); 3006 } catch (posthogError) { 3007 // eslint-disable-next-line no-console 3008 console.error('[HomeValuation] PostHog capture failed:', posthogError); 3009 } 3010 } 3011 /** 3012 * Maps error types to user-friendly messages 3013 * @param {Error} error - The error object 3014 * @returns {string} - User-friendly message 3015 */ _getUserFriendlyErrorMessage(error) { 3016 if (error.name === 'AbortError' || error.message?.includes('timeout')) return 'Connection timed out. Please try again.'; 3017 if (error.name === 'TypeError' && error.message?.includes('fetch')) return 'Unable to connect. Please check your internet connection and try again.'; 3018 if (error.status === 503 || error.status === 502 || error.status === 504) return 'Service temporarily unavailable. Please try again in a moment.'; 3019 if (error.status === 400) return 'Please check your information and try again.'; 3020 return 'Something went wrong. Please try again or contact us directly.'; 3021 } 3022 /** 3023 * Build formatted address string from structured address components 3024 * @param {Object} structuredAddress - Structured address object 3025 * @return {string} - Formatted address string 3026 */ _formatStructuredAddress(structuredAddress) { 3027 if (!structuredAddress || Object.keys(structuredAddress).length === 0) return ''; 3028 const parts = []; 3029 // Line 1: streetNumber streetName addressLine2 3030 const line1 = [ 3031 structuredAddress.streetNumber, 3032 structuredAddress.streetName, 3033 structuredAddress.addressLine2
3034 ].filter(Boolean).join(' '); 3035 parts.push(line1); 3036 // Line 2: city, state zipcode 3037 const cityState = [ 3038 structuredAddress.city, 3039 structuredAddress.state 3040 ].filter(Boolean).join(', '); 3041 const line2 = [ 3042 cityState, 3043 structuredAddress.zipcode 3044 ].filter(Boolean).join(' '); 3045 parts.push(line2); 3046 return parts.filter(Boolean).join(', '); 3047 } 3048 /** 3049 * Build home valuation data object 3050 * @param {Object} data - Form data 3051 * @param {string} homeValuationEstimate - Home valuation estimate 3052 * @param {Object} structuredAddress - Structured address object 3053 * @param {Object} coordinates - Coordinates object 3054 * @return {Object} - Home valuation data object 3055 */ _buildHomeValuationData(data, homeValuationEstimate, structuredAddress, coordinates) { 3056 // Build homeValuationAddress from structured address (validated by Google) if available 3057 // Format: streetNumber streetName, city, state zipcode, addressLine2 3058 const homeValuationAddress = this._formatStructuredAddress(structuredAddress) || this._getRawAddress(data); 3059 // Validate that we have an address before proceeding 3060 if (!homeValuationAddress) throw new Error('Unable to extract home valuation address from form data'); 3061 // Required fields that must always be included 3062 const requiredFields = { 3063 companyId: data.companyId, 3064 homeValuationAddress: homeValuationAddress, 3065 homeValuationEstimate: homeValuationEstimate ? new Intl.NumberFormat('en-US', { 3066 style: 'currency', 3067 currency: 'USD', 3068 maximumFractionDigits: 0, 3069 minimumFractionDigits: 0 3070 }).format(homeValuationEstimate) : null, 3071 websiteId: data.page.websiteId, 3072 sourceUrl: data.activity.source_url 3073 }; 3074 // Optional fields that will be filtered if empty 3075 const optionalFields = { 3076 leadEmailAddress: data.activity.author_email, 3077 leadFullName: data.activity.author_name, 3078 leadPhoneNumber: data.activity.author_phone, 3079 parcelId: data.activity.content?.parcelId, 3080 resourceId: data.page.pageMeta?.resourceId, 3081 pageId: data.page.pageId,
3082 sourceResource: data.page.pageMeta?.sourceResource, 3083 pageElementId: data.page.pageMeta?.pageElementId, 3084 formId: data.page.pageMeta?.formId, 3085 utmSource: data.activity.content?.utm_source, 3086 utmMedium: data.activity.content?.utm_medium, 3087 utmCampaign: data.activity.content?.utm_campaign, 3088 utmTerm: data.activity.content?.utm_term, 3089 utmContent: data.activity.content?.utm_content, 3090 utmId: data.activity.content?.utm_id, 3091 timeToSell: data.activity.content?.timeToSell, 3092 lookingToBuy: data.activity.content?.lookingToBuy, 3093 referrer: data.referrer, 3094 content: data.activity.content || {}, 3095 fields: data.fields || {} 3096 }; 3097 // Filter out empty strings from optional fields 3098 const filteredOptionalFields = this._filterEmptyStrings(optionalFields); 3099 return { 3100 ...requiredFields, 3101 ...filteredOptionalFields, 3102 // Add structured address and coordinates if they exist 3103 ...structuredAddress && { 3104 address: structuredAddress 3105 }, 3106 ...coordinates && { 3107 coordinates: coordinates 3108 } 3109 }; 3110 } 3111 /** 3112 * Validate address with Google Maps 3113 * @param {string} rawAddress - Raw address string 3114 * @returns {Promise<Object|null>} - Validated address data or null 3115 * @throws {Error} - If validation fails, error is thrown with userMessage 3116 */ async _validateAddressSafely(rawAddress) { 3117 if (!rawAddress) { 3118 // eslint-disable-next-line no-console 3119 console.error('Home valuation: No address found in form data'); 3120 return null; 3121 } 3122 try { 3123 return await this._validateAddressWithGoogleMaps(rawAddress); 3124 } catch (error) { 3125 // eslint-disable-next-line no-console 3126 console.error('Error validating address with Google Maps:', error); 3127 // Attach user-friendly message and throw so user sees the error 3128 error.userMessage = this._getUserFriendlyErrorMessage(error); 3129 throw error; 3130 } 3131 } 3132 /** 3133 * Get home valuation estimate 3134 * @param {Object} validatedAddressData - Validated address data 3135 * @returns {Promise<number|null>} - Home valuation estimate or null 3136 * @throws {Error} - If estimate fetch fails, error is thrown with userMessage 3137 */ async _getHomeValuationEstimateSafely(validatedAddressData) { 3138 try { 3139 return await this._getHomeValuationEstimate(validatedAddressData); 3140 } catch (error) { 3141 // eslint-disable-next-line no-console 3142 console.error('Error getting home valuation estimate:', error); 3143 // Attach user-friendly message and throw so user sees the error 3144 error.userMessage = this._getUserFriendlyErrorMessage(error); 3145 throw error; 3146 } 3147 } 3148 /** 3149 * Parse response text into payload object 3150 * @param {string} text - Response text 3151 * @returns {Object} - Parsed payload 3152 */ _parseResponsePayload(text) { 3153 try { 3154 return text ? JSON.parse(text) : {}; 3155 } catch { 3156 return { 3157 raw: text 3158 }; 3159 } 3160 } 3161 /** 3162 * Create error from failed response 3163 * @param {Object} payload - Response payload 3164 * @param {number} status - HTTP status code 3165 * @returns {Error} - Error object with status and payload 3166 */ _createResponseError(payload, status) { 3167 const errorMessage = payload && typeof payload === 'object' && Object.keys(payload).length ? payload.message || JSON.stringify(payload) : 'Request failed'; 3168 const error = new Error(errorMessage); 3169 // Preserve payload properties if it's an object 3170 if (payload && typeof payload === 'object' && Object.keys(payload).length) Object.assign(error, payload); 3171 // Preserve numeric HTTP status - must be set after Object.assign 3172 // to ensure it takes precedence over any status in payload 3173 error.status = status; 3174 return error; 3175 } 3176 /** 3177 * Submit lead to API endpoint 3178 * @param {string} leadRoute - API endpoint URL 3179 * @param {Object} homeValuationData - Data to submit 3180 * @returns {Promise<Object>} - Submission result 3181 */ async _submitLead(leadRoute, homeValuationData) { 3182 const controller = new AbortController(); 3183 const timeoutId = setTimeout(()=>controller.abort(), 10000); 3184 try { 3185 const response = await fetch(leadRoute, { 3186 method: 'POST', 3187 headers: { 3188 'Content-Type': 'application/json' 3189 }, 3190 body: JSON.stringify(homeValuationData), 3191 signal: controller.signal 3192 }); 3193 clearTimeout(timeoutId); 3194 const text = await response.text(); 3195 const payload = this._parseResponsePayload(text); 3196 if (!response.ok) throw this._createResponseError(payload, response.status); 3197 if (payload && payload.status === 'success' && payload.redirectUrl) { 3198 this.window.location.href = payload.redirectUrl; 3199 return { 3200 redirected: true, 3201 url: payload.redirectUrl 3202 }; 3203 } 3204 return payload; 3205 } catch (error) { 3206 clearTimeout(timeoutId); 3207 throw error; 3208 } 3209 } 3210 /** 3211 * Submit home valuation request with Google Maps validation 3212 * @param {Object} data 3213 * @return {Promise<Object>} 3214 */ async _submitHomeValuationRequest(data) { 3215 const { config: config } = this; 3216 // Use websiteApiGatewayUrl from config 3217 if (!config.websiteApiGatewayUrl) throw new Error('websiteApiGatewayUrl not configured for home valuation'); 3218 // Log submission started 3219 this._logLeadEvent('home_valuation_submission_started', data); 3220 try { 3221 // Validate address with Google Maps 3222 const rawAddress = this._getRawAddress(data); 3223 let validatedAddressData = null; 3224 try {
3225 validatedAddressData = await this._validateAddressSafely(rawAddress); 3226 } catch (error) { 3227 // Error already has userMessage from _validateAddressSafely 3228 this._logLeadEvent('home_valuation_address_validation_failed', data, error); 3229 throw error; 3230 } 3231 // Get home valuation estimate. 3232 // The estimate is best-effort: many valid properties (e.g. commercial 3233 // parcels or addresses without AVM coverage) have no estimate and the 3234 // estimate endpoint returns a non-OK status for them. A missing estimate 3235 // must never block lead capture, so we log the failure and continue with 3236 // a null estimate rather than aborting the whole submission. 3237 let homeValuationEstimate = null; 3238 try { 3239 homeValuationEstimate = await this._getHomeValuationEstimateSafely(validatedAddressData); 3240 } catch (error) { 3241 // Error already has userMessage from _getHomeValuationEstimateSafely 3242 this._logLeadEvent('home_valuation_estimate_failed', data, error); 3243 homeValuationEstimate = null; 3244 } 3245 // Extract structured address and coordinates from Google Maps validation 3246 const { structuredAddress: structuredAddress, coordinates: coordinates } = this._extractAddressAndCoordinates(validatedAddressData); 3247 const leadRoute = `${config.websiteApiGatewayUrl}${$2aefad83322125c9$var$homeValuationRoute}`; 3248 const homeValuationData = this._buildHomeValuationData(data, homeValuationEstimate, structuredAddress, coordinates); 3249 const result = await this._submitLead(leadRoute, homeValuationData); 3250 // Log success 3251 this._logLeadEvent('home_valuation_submission_success', data); 3252 return result; 3253 } catch (error) { 3254 // Ensure error has user-friendly message (may already be set by helper methods) 3255 if (!error.userMessage) error.userMessage = this._getUserFriendlyErrorMessage(error); 3256 this._logLeadEvent('home_valuation_submission_failed', data, error); 3257 throw error; 3258 } 3259 } 3260 /** 3261 * Validate address using Google Maps Address Validation API 3262 * @param {string} addressStr - The address string to validate 3263 * @returns {Promise<Object|null>} - Validated address data or null if validation fails 3264 */ async _validateAddressWithGoogleMaps(addressStr) { 3265 const { config: config, window: window } = this; 3266 // Ensure Google Maps API key is loaded 3267 let apiKey = config.googleMapsAPIKey; 3268 if (!apiKey && window.luxuryPresence?.mapApiLoader) apiKey = await window.luxuryPresence.mapApiLoader.getMapApiKey(); 3269 if (!apiKey) throw new Error('Google Maps API key not configured'); 3270 const controller = new AbortController(); 3271 const timeoutId = setTimeout(()=>controller.abort(), 10000); 3272 try { 3273 const response = await fetch(`https://addressvalidation.googleapis.com/v1:validateAddress?key=${apiKey}`, { 3274 method: 'POST', 3275 headers: { 3276 'Content-Type': 'application/json' 3277 }, 3278 body: JSON.stringify({ 3279 address: { 3280 addressLines: [ 3281 addressStr 3282 ] 3283 }, 3284 enableUspsCass: false 3285 }), 3286 signal: controller.signal 3287 }); 3288 clearTimeout(timeoutId); 3289 if (!response.ok) { 3290 const error = new Error(`Address validation failed: ${response.status}`); 3291 error.status = response.status; 3292 throw error; 3293 } 3294 return response.json(); 3295 } catch (error) { 3296 clearTimeout(timeoutId); 3297 throw error; 3298 } 3299 } 3300 /** 3301 * Check if structured address has all mandatory fields 3302 * @param {Object} structuredAddress - Structured address object 3303 * @return {boolean} 3304 */ _hasMandatoryAddressFields(structuredAddress) { 3305 return !!(structuredAddress.streetNumber && structuredAddress.streetName && structuredAddress.city && structuredAddress.state && structuredAddress.zipcode); 3306 } 3307 /** 3308 * Log missing mandatory address fields 3309 * @param {Object} structuredAddress - Structured address object 3310 */ _logMissingAddressFields(structuredAddress) { 3311 const missingFields = [];
3312 if (!structuredAddress.streetNumber) missingFields.push('streetNumber'); 3313 if (!structuredAddress.streetName) missingFields.push('streetName'); 3314 if (!structuredAddress.city) missingFields.push('city'); 3315 if (!structuredAddress.state) missingFields.push('state'); 3316 if (!structuredAddress.zipcode) missingFields.push('zipcode'); 3317 // eslint-disable-next-line no-console 3318 console.log('Home valuation estimate skipped - missing required fields:', missingFields.join(', '), 'Available fields:', Object.keys(structuredAddress).join(', ')); 3319 } 3320 /** 3321 * Build query string from structured address 3322 * @param {Object} structuredAddress - Structured address object 3323 * @return {string} 3324 */ _buildAddressQueryString(structuredAddress) { 3325 return Object.entries(structuredAddress).map(([key, value])=>`${encodeURIComponent(key)}=${encodeURIComponent(value)}`).join('&'); 3326 } 3327 /** 3328 * GET home valuation estimate 3329 * @param {string} address 3330 * @param {Object} validatedAddressData - Google Maps validated address data 3331 * @return {Promise<number|null>} 3332 */ async _getHomeValuationEstimate(validatedAddressData = null) { 3333 const { config: config } = this; 3334 if (!config.websiteApiGatewayUrl) throw new Error('websiteApiGatewayUrl not configured'); 3335 const base = `${config.websiteApiGatewayUrl}${$2aefad83322125c9$var$homeValuationRoute}`; 3336 // Check if we have validated address data 3337 if (!validatedAddressData?.result?.address?.addressComponents) return null; 3338 const structuredAddress = this._transformAddressComponents(validatedAddressData.result.address.addressComponents); 3339 // Check if all mandatory fields exist 3340 if (!this._hasMandatoryAddressFields(structuredAddress)) { 3341 this._logMissingAddressFields(structuredAddress); 3342 return null; 3343 } 3344 // Build URL with query string 3345 const qs = this._buildAddressQueryString(structuredAddress); 3346 const homeValURL = `${base}?${qs}`; 3347 const controller = new AbortController(); 3348 const timeoutId = setTimeout(()=>controller.abort(), 10000); 3349 try { 3350 const response = await fetch(homeValURL, { 3351 headers: { 3352 Accept: 'application/json' 3353 }, 3354 signal: controller.signal 3355 }); 3356 clearTimeout(timeoutId); 3357 if (!response.ok) { 3358 const error = new Error(`Home valuation estimate failed: ${response.status}`); 3359 error.status = response.status; 3360 throw error; 3361 } 3362 const data = await response.json(); 3363 return data?.valueDollars ?? null; 3364 } catch (error) { 3365 clearTimeout(timeoutId); 3366 // Re-throw to allow retry logic to handle it 3367 throw error; 3368 } 3369 } 3370} 3371 3372 3373const $e10bd00497be8876$export$97de66002300ed70 = 'middleName'; 3374// Default lead fields 3375const $e10bd00497be8876$var$DEFAULT_LEAD_FIELDS = [ 3376 'name', 3377 'firstName', 3378 'lastName', 3379 $e10bd00497be8876$export$97de66002300ed70, 3380 'email', 3381 'phone', 3382 'phoneNumber', 3383 'source', 3384 'message', 3385 'mlsId', 3386 'sourceUrl' 3387]; 3388class $e10bd00497be8876$export$3c5380623cf51249 extends (0, $7c391e2f7931d60d$export$3b5bd9381a52554c) { 3389 /** 3390 * Initialize Contact Form Controller at build time 3391 * @param { ConfigController } config 3392 * @param {Window} window 3393 * @param {Document} document 3394 */ constructor(config, window, document){ 3395 super(config, window, document); 3396 this.forms = []; 3397 this.homeValuationService = new (0, $2aefad83322125c9$export$2e2bcd8739ae039)(config, window, document); 3398 } 3399 /** 3400 * Initialize Contact Form Controller at run time 3401 * Attach handlers and events to DOM 3402 * @param {Object} options 3403 */ initialize(options = {}) { 3404 try { 3405 const { document: document } = this; 3406 const { selector: selector } = options; 3407 let forms = []; 3408 if (selector) forms = document.querySelectorAll(`${selector} form[data-type="contact-form"]`); 3409 else forms = document.querySelectorAll('form[data-type="contact-form"]');
3410 forms.forEach((form)=>{ 3411 this._attachSubmitHandler(form); 3412 // this._attachValidation(form); 3413 }); 3414 this.forms = this.forms.concat(forms); 3415 } catch (e) { 3416 // eslint-disable-next-line no-console 3417 console.log(e); 3418 } 3419 } 3420 /** 3421 * Controllers must implement a "destroy" method to be called 3422 * after page navigation to remove event handlers / cleanup 3423 */ destroy() { 3424 this.forms = []; 3425 } 3426 /** 3427 * Handle submit from global context 3428 * @param {HTMLFormElement} form 3429 */ submitForm(form) { 3430 this._handleSubmit(null, form).catch((e)=>console.error(e)); 3431 } 3432 /** 3433 * Attach submit event for form 3434 * @param {HTMLFormElement} contactForm 3435 */ _attachValidation(contactForm) { 3436 const { window: window } = this; 3437 const inputs = contactForm.querySelectorAll('input, select, textarea'); 3438 inputs.forEach((input)=>{ 3439 window.luxuryPresence.validation.applyFieldValidation(input); 3440 }); 3441 } 3442 /** 3443 * Attach submit event for form 3444 * @param {HTMLFormElement} contactForm 3445 */ _attachSubmitHandler(contactForm) { 3446 contactForm.addEventListener('submit', (e)=>this._handleSubmit(e, contactForm)); 3447 } 3448 /** 3449 * Extract and process form values 3450 * @param {HTMLFormElement} contactForm 3451 * @return {Object} - Processed form values 3452 */ _extractFormValues(contactForm) { 3453 const formData = new FormData(contactForm); 3454 const values = {}; 3455 const nameParts = []; 3456 const nameKeyRegex = /^name(?:-\d+)?$/; 3457 formData.forEach((value, key)=>{ 3458 if (typeof value === 'string') { 3459 const curr = value.trim(); 3460 if (nameKeyRegex.test(key) && curr) nameParts.push(curr); 3461 const prev = values[key]; 3462 values[key] = prev ? `${prev} ${curr}` : curr; 3463 } else values[key] = value; 3464 }); 3465 if (nameParts.length) values.name = nameParts.join(' ').trim(); 3466 else if (!values.name && values.firstName) { 3467 values.name = values.firstName; 3468 if (values.lastName) values.name += ` ${values.lastName}`; 3469 } 3470 return values; 3471 } 3472 /** 3473 * Build submission data object 3474 * @param {Object} values - Form values 3475 * @param {Object} fields - Form fields 3476 * @param {HTMLFormElement} contactForm - Form element 3477 * @return {Object} - Submission data 3478 */ _buildSubmissionData(values, fields, contactForm) { 3479 const { window: window, config: config } = this; 3480 const content = Object.keys(values).filter((key)=>!$e10bd00497be8876$var$DEFAULT_LEAD_FIELDS.includes(key)).reduce((result, key)=>{ 3481 // eslint-disable-next-line 3482 result[key] = values[key]; 3483 return result; 3484 }, {}); 3485 const { pageId: pageId, companyId: companyId, websiteId: websiteId, pageMeta: pageMeta } = config; 3486 const utm = window.localStorage.getItem('utm_vars') || '{}'; 3487 const referrer = window.localStorage.getItem('referrer') || ''; 3488 const formId = this._getFormId(contactForm); 3489 return { 3490 activity: { 3491 author_email: values.email, 3492 author_name: values.name, 3493 author_phone: values.phone || values.phoneNumber, 3494 activity_source: values.source || 'CONTACT_INQUIRY', 3495 comments_attributes: [ 3496 { 3497 text: values.message || undefined 3498 } 3499 ], 3500 source_url: window.location.href, 3501 content: { 3502 ...content, 3503 ...JSON.parse(utm) 3504 } 3505 }, 3506 page: { 3507 pageId: pageId, 3508 companyId: companyId, 3509 websiteId: websiteId, 3510 pageMeta: { 3511 ...pageMeta, 3512 formId: formId 3513 } 3514 }, 3515 companyId: companyId, 3516 referrer: referrer, 3517 fields: fields 3518 }; 3519 } 3520 /** 3521 * Handle successful form submission 3522 * @param {HTMLFormElement} contactForm - Form element 3523 */ _handleSubmissionSuccess(contactForm) { 3524 const { window: window } = this; 3525 const closeDelay = contactForm.dataset.delay || (0, $a3e97e89bbc2a366$export$f037eeffb5561205); 3526 contactForm.classList.remove('loading'); 3527 contactForm.classList.remove('error'); 3528 contactForm.classList.add('success'); 3529 // TODO: Review producer and classic to deprecate "hide" 3530 contactForm.querySelectorAll('.success').forEach((el)=>el.classList.remove('hide')); 3531 contactForm.dispatchEvent(new Event('formsubmitted')); 3532 // Any successful lead submit credits the Google Ads lead conversion 3533 // (reuses the existing `generate_lead` action), once per session. 3534 (0, $3ec2bf3965f87ecc$export$e70565180c0e6950)(this.window, this.document); 3535 setTimeout(()=>{ 3536 window.lpUI.hideClosestModal(contactForm); 3537 contactForm.reset(); 3538 window.lpUI.sideMenuHide(); 3539 contactForm.querySelectorAll('.success').forEach((el)=>el.classList.add('hide')); 3540 contactForm.classList.remove('success'); 3541 }, closeDelay); 3542 } 3543 /** 3544 * Submit Contact Form Handler 3545 * @param {Event} e 3546 * @param {HTMLFormElement} contactForm 3547 * @return {Promise<Object>} 3548 */ async _handleSubmit(e, contactForm) { 3549 if (e) e.preventDefault(); 3550 if (contactForm.classList.contains('loading')) // Form already submitting 3551 return; 3552 // Hide any previous error message and clear error state 3553 this._hideSubmissionError(contactForm); 3554 contactForm.classList.remove('error'); 3555 contactForm.classList.add('loading'); 3556 try { 3557 // Extract form values first (before async operations) 3558 const values = this._extractFormValues(contactForm); 3559 // TODO: HoneyPot should still send request 3560 if (values.middleName) { 3561 contactForm.classList.remove('loading'); 3562 return; 3563 } 3564 const fields = this._getFields(contactForm); 3565 const data = this._buildSubmissionData(values, fields, contactForm); 3566 await this._submitLeadRequest(data); 3567 this._sendGAEvent(data); 3568 this._handleSubmissionSuccess(contactForm); 3569 } catch (err) { 3570 // eslint-disable-next-line no-console 3571 console.error('Form submission failed:', err); 3572 contactForm.dispatchEvent(new Event('formsubmiterror')); 3573 contactForm.classList.remove('loading'); 3574 contactForm.classList.add('error'); 3575 // Show user-friendly error message if available (from home valuation service)
3576 if (err.userMessage) this._showSubmissionError(contactForm, err.userMessage); 3577 else this._showSubmissionError(contactForm, 'Something went wrong. Please try again.'); 3578 } 3579 } 3580 /** 3581 * Alias anonymous user with canonical ID using event bus 3582 * Links the user's anonymous browsing session to their canonical identity 3583 * @param {Object} data - Form submission data 3584 * @private 3585 */ _aliasUserWithEventBus(data) { 3586 try { 3587 // Check if event bus library and required methods are available 3588 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 3589 // Skip if user is already identified 3590 if (this.window.eventBusProvider.isIdentified()) { 3591 console.log('[EventBus] User already identified, skipping alias'); 3592 return; 3593 } 3594 const email = data.activity?.author_email; 3595 const { companyId: companyId } = this.config; 3596 if (!email || !companyId) { 3597 console.warn('[EventBus] Missing required data for user alias'); 3598 return; 3599 } 3600 // Get the anonymous distinct ID from PostHog 3601 const anonId = this.window.eventBusProvider.getDistinctId(); 3602 if (!anonId) { 3603 console.warn('[EventBus] No anonymous ID available for alias'); 3604 return; 3605 } 3606 // Generate deterministic canonical ID from company + email 3607 const canonicalId = this.window.LuxuryPresenceEventBus.generateUUIDv5FromCompanyEmail(companyId, email); 3608 // Create alias linking anonymous ID to canonical ID 3609 this.window.eventBusProvider.alias({ 3610 anonId: anonId, 3611 canonicalId: canonicalId 3612 }); 3613 console.log('[EventBus] User aliased with canonical ID'); 3614 } catch (error) { 3615 // Never block form submission due to analytics errors 3616 console.error('[EventBus] User alias failed:', error); 3617 } 3618 } 3619 /** 3620 * Send Lead Request 3621 * @param {Object} data 3622 * @return {Promise<Object>} 3623 */ async _submitLeadRequest(data) { 3624 const { config: config } = this; 3625 // Alias user with event bus before submitting the lead 3626 this._aliasUserWithEventBus(data); 3627 if (config.myhomeDashboardEnabled && (data.activity?.activity_source === 'HOME_VALUATION' || data.activity?.activity_source === 'HOME_VALUE')) return this.homeValuationService._submitHomeValuationRequest(data); 3628 const leadRoute = `${config.leadIngesterUrl}/api/v1/leads`; 3629 return fetch(leadRoute, { 3630 method: 'POST', 3631 headers: { 3632 'Content-Type': 'application/json' 3633 }, 3634 body: JSON.stringify(data) 3635 }).then((response)=>{ 3636 if (!response.ok) return response.json().then((error)=>{ 3637 throw error; 3638 }); 3639 return response.json(); 3640 }); 3641 } 3642 /** 3643 * Send GA event 3644 * @param {Object} data 3645 */ _sendGAEvent(data) { 3646 const { document: document } = this; 3647 let event = { 3648 hitType: 'event', 3649 eventCategory: 'button', 3650 eventAction: 'click' 3651 }; 3652 switch(data.activity.activity_source){ 3653 case 'NEWSLETTER_SIGNUP': 3654 event.eventLabel = 'Newsletter'; 3655 document.sendGoogleTagManagerEvent('event', 'newsletter_signup'); 3656 break; 3657 case 'HOME_SEARCH': 3658 event.eventLabel = 'HomeSearch'; 3659 document.sendGoogleTagManagerEvent('event', 'home_search'); 3660 break; 3661 case 'HOME_VALUE': 3662 case 'HOME_VALUATION': 3663 event.eventLabel = 'HomeValue'; 3664 document.sendGoogleTagManagerEvent('event', 'home_valuation'); 3665 break; 3666 case 'EBOOK': 3667 event.eventLabel = 'Ebook'; 3668 document.sendGoogleTagManagerEvent('event', 'ebook_download'); 3669 break; 3670 case 'CONTACT_INQUIRY': 3671 default: 3672 // for contact forms, Marketing has requested that they see a page-view of 'thank-you' as indication of success 3673 // Note that this will break if we have actual page with url of /thank-you 3674 event = { 3675 hitType: 'pageview', 3676 page: '/thank-you' 3677 }; 3678 document.sendGoogleTagManagerEvent('event', 'contact_inquiry'); 3679 break; 3680 } 3681 document.sendGoogleAnalyticsEvent(event); 3682 } 3683 /** 3684 * Gather fields / schema for form, used for display in CRM 3685 * @param {HTMLFormElement} form 3686 * @return {Object} 3687 */ _getFields(form) { 3688 const inputs = form.querySelectorAll('input, select, textarea'); 3689 const fields = {};
3690 inputs.forEach((input)=>{ 3691 const { id: id, name: name, type: inputType } = input; 3692 if (name === $e10bd00497be8876$export$97de66002300ed70) // Honeypot continue 3693 return true; 3694 let label = ''; 3695 if (id) { 3696 const labelNode = form.querySelector(`label[for='${id}']`); 3697 if (labelNode) label = labelNode.textContent.trim(); 3698 else if (input.placeholder) label = input.placeholder.trim(); 3699 } 3700 fields[name] = { 3701 label: label, 3702 type: input.tagName, 3703 inputType: input.tagName === 'INPUT' ? inputType : 'text' 3704 }; 3705 }); 3706 return fields; 3707 } 3708 /** 3709 * Figure out relevant section or div ID for form 3710 * @param {HTMLFormElement} form 3711 * @return {string} 3712 */ _getFormId(form) { 3713 const SELECTORS = "section[id^='section-'], footer[id], .modal[id^='modal-']"; 3714 const parent = form.closest(SELECTORS); 3715 return parent?.id ? this._cleanFormId(parent.id) : ''; 3716 } 3717 /** 3718 * Remove prefix from formID 3719 * @param {string} formId 3720 * @return {string} 3721 */ _cleanFormId(formId) { 3722 const PREFIXES = [ 3723 'section-', 3724 'modal-' 3725 ]; 3726 for (const prefix of PREFIXES){ 3727 if (formId.startsWith(prefix)) return formId.substring(prefix.length); 3728 } 3729 return formId; 3730 } 3731 /** 3732 * Show submission error message on form 3733 * @param {HTMLFormElement} form 3734 * @param {string} message 3735 */ _showSubmissionError(form, message) { 3736 const errorEl = form.querySelector('.submission-error-message'); 3737 if (errorEl) { 3738 errorEl.textContent = message || 'Something went wrong. Please try again.'; 3739 errorEl.style.display = 'block'; 3740 } 3741 } 3742 /** 3743 * Hide submission error message on form 3744 * @param {HTMLFormElement} form 3745 */ _hideSubmissionError(form) { 3746 const errorEl = form.querySelector('.submission-error-message'); 3747 if (errorEl) errorEl.style.display = 'none'; 3748 } 3749} 3750 3751 3752 3753const $ea6901fdf47e0ec1$var$CALLBACK = '__googleMapsCallback'; 3754const $ea6901fdf47e0ec1$var$GOOGLE_MAPS_URL = 'https://maps.googleapis.com/maps/api/js'; 3755const $ea6901fdf47e0ec1$export$76299006759be400 = 1024; 3756const $ea6901fdf47e0ec1$var$KEY_FETCH_ATTEMPTS = 3; 3757const $ea6901fdf47e0ec1$var$KEY_RETRY_BASE_DELAY_MS = 300; 3758const $ea6901fdf47e0ec1$var$KEY_FAILURE_FLAG = 'lp_map_key_failed'; 3759class $ea6901fdf47e0ec1$export$3c19195edca3fa5c extends (0, $7c391e2f7931d60d$export$3b5bd9381a52554c) { 3760 /** 3761 * Initialize Button Controller at build time 3762 * @param { ConfigController } config 3763 * @param {Window} window 3764 * @param {Document} document 3765 */ constructor(config, window, document){ 3766 super(config, window, document); 3767 this.retries = 3; 3768 this.callbacks = []; 3769 this.id = `__googleMapsScriptId${Date.now()}`; 3770 this._apiKey = ''; 3771 this.reset(); 3772 } 3773 /** 3774 * Set the Google Maps api key 3775 * @param {string} apiKey 3776 */ set apiKey(apiKey) { 3777 this._apiKey = apiKey; 3778 this.config.setGoogleMapsAPIKey(apiKey); 3779 } 3780 /** 3781 * Get the Google Maps api key 3782 * @return {string} 3783 */ get apiKey() { 3784 return this._apiKey; 3785 } 3786 /** 3787 * Reset the loader state. 3788 */ reset() { 3789 this.deleteScript(); 3790 this.done = false; 3791 this.loading = false; 3792 this.failed = false; 3793 this.errors = []; 3794 this.onerrorEvent = null; 3795 } 3796 /** 3797 * Destroy 3798 */ destroy() { 3799 this.reset(); 3800 } 3801 /** 3802 * get map api Key 3803 * @return {Promise<string>} 3804 */ async getMapApiKey() { 3805 if (this.apiKey) return this.apiKey; 3806 const hadPreviousFailure = this._hadPreviousFailure(); 3807 let recoveryReported = false; 3808 for(let attempt = 1; attempt <= $ea6901fdf47e0ec1$var$KEY_FETCH_ATTEMPTS; attempt += 1){ 3809 const reportRecovery = hadPreviousFailure && !recoveryReported; 3810 try { 3811 // eslint-disable-next-line no-await-in-loop 3812 const response = await fetch(this._getMapKeyUrl(attempt, reportRecovery));
3813 // The request reached the server, so a recovery marker on it was 3814 // delivered and must not be repeated on later attempts or pages. A 3815 // rejected fetch never arrived, so the marker moves to the next 3816 // attempt. 3817 recoveryReported = recoveryReported || reportRecovery; 3818 this._setFailureFlag(false); 3819 if (!response.ok) throw new Error(`Failed to fetch API key: ${response.status}`); 3820 // eslint-disable-next-line no-await-in-loop 3821 const code = await response.text(); 3822 if (!code) return ''; 3823 this.apiKey = code; 3824 return this.apiKey; 3825 } catch (e) { 3826 // eslint-disable-next-line no-console 3827 console.error(e); 3828 if (attempt < $ea6901fdf47e0ec1$var$KEY_FETCH_ATTEMPTS) // eslint-disable-next-line no-await-in-loop 3829 await this._delay($ea6901fdf47e0ec1$var$KEY_RETRY_BASE_DELAY_MS * 2 ** (attempt - 1)); 3830 } 3831 } 3832 this._setFailureFlag(true); 3833 return ''; 3834 } 3835 /** 3836 * Build the map key request url. Retries carry their attempt number, and a 3837 * single request per session carries the recovery marker, so both are 3838 * countable in existing request logs without new infrastructure. 3839 * @param {number} attempt 1-based attempt number 3840 * @param {boolean} reportRecovery tag this request as following a session 3841 * in which every attempt failed 3842 * @return {string} 3843 */ _getMapKeyUrl(attempt, reportRecovery) { 3844 const params = []; 3845 if (attempt > 1) params.push(`attempt=${attempt}`); 3846 if (reportRecovery) params.push('recovered_after_failure=1'); 3847 const query = params.length ? `?${params.join('&')}` : ''; 3848 return `${this._getBaseUrl()}/api/v1/map${query}`; 3849 } 3850 /** 3851 * Whether an earlier attempt in this session exhausted all retries. 3852 * @return {boolean} 3853 */ _hadPreviousFailure() { 3854 try { 3855 return this.window.sessionStorage.getItem($ea6901fdf47e0ec1$var$KEY_FAILURE_FLAG) === '1'; 3856 } catch (e) { 3857 // Storage can throw when disabled (private browsing); absence of the 3858 // flag is the safe default. 3859 return false; 3860 } 3861 } 3862 /** 3863 * Record or clear the session-level key fetch failure flag. 3864 * @param {boolean} failed 3865 */ _setFailureFlag(failed) { 3866 try { 3867 if (failed) this.window.sessionStorage.setItem($ea6901fdf47e0ec1$var$KEY_FAILURE_FLAG, '1'); 3868 else this.window.sessionStorage.removeItem($ea6901fdf47e0ec1$var$KEY_FAILURE_FLAG); 3869 } catch (e) { 3870 // Storage unavailable - reporting is best effort, never fatal. 3871 } 3872 } 3873 /** 3874 * @param {number} ms 3875 * @return {Promise} 3876 */ _delay(ms) { 3877 return new Promise((resolve)=>{ 3878 this.window.setTimeout(resolve, ms); 3879 }); 3880 } 3881 /** 3882 * CreateUrl returns the Google Maps JavaScript API script url ]. 3883 * @return {string} 3884 */ async createUrl() { 3885 const apiKey = await this.getMapApiKey(); 3886 // Google Maps loads successfully without a key and then fails at call time 3887 // with NoApiKeys, which produces a broken map and no load error. Refuse 3888 // instead, so the failure surfaces to the caller. 3889 if (!apiKey) throw new Error('Refusing to load Google Maps without an API key'); 3890 let url = $ea6901fdf47e0ec1$var$GOOGLE_MAPS_URL; 3891 url += `?callback=${$ea6901fdf47e0ec1$var$CALLBACK}`; 3892 url += `&key=${apiKey}`; 3893 url += `&libraries=places`; 3894 return url; 3895 } 3896 /** 3897 * Set the script on document. 3898 */ async setScript() { 3899 if (this.document.getElementById(this.id)) { 3900 this.callback(); 3901 return; 3902 } 3903 let url; 3904 try { 3905 url = await this.createUrl(); 3906 } catch (e) { 3907 // eslint-disable-next-line no-console 3908 console.error(e); 3909 this.failed = true; 3910 this.loading = false; 3911 this.onerrorEvent = e; 3912 this.callback(); 3913 return; 3914 } 3915 const script = this.document.createElement('script'); 3916 script.id = this.id; 3917 script.type = 'text/javascript'; 3918 script.src = url; 3919 script.onerror = this.loadErrorCallback.bind(this); 3920 script.defer = true; 3921 script.async = true; 3922 this.document.head.appendChild(script); 3923 } 3924 /** 3925 * Function to be called if script tag fails 3926 * @param {Error} e 3927 */ loadErrorCallback(e) { 3928 this.errors.push(e); 3929 if (this.errors.length <= this.retries) {
3930 const delay = this.errors.length * 2 ** this.errors.length; 3931 // eslint-disable-next-line no-console 3932 console.log(`Failed to load Google Maps script, retrying in ${delay} ms.`); 3933 setTimeout(()=>{ 3934 this.deleteScript(); 3935 this.setScript().catch((e)=>console.error(e)); 3936 }, delay); 3937 } else { 3938 this.failed = true; 3939 this.onerrorEvent = e; 3940 this.callback(); 3941 } 3942 } 3943 /** 3944 * Delete the script tag 3945 */ deleteScript() { 3946 const script = this.document.getElementById(this.id); 3947 if (script) script.remove(); 3948 } 3949 /** 3950 * Load the Google Maps JavaScript API script and return a Promise. 3951 * @return {Promise} 3952 */ load() { 3953 return this.loadPromise(); 3954 } 3955 /** 3956 * Load the Google Maps JavaScript API script and return a Promise. 3957 * @return {Promise} 3958 */ loadPromise() { 3959 return new Promise((resolve, reject)=>{ 3960 this.loadCallback((err)=>{ 3961 if (!err) resolve(); 3962 else reject(err); 3963 }); 3964 }); 3965 } 3966 /** 3967 * Load the Google Maps JavaScript API script with a callback. 3968 * @param {Function<Event>} fn 3969 */ loadCallback(fn) { 3970 this.callbacks.push(fn); 3971 this.execute(); 3972 } 3973 /** 3974 * Sets the callback function that will be sent as part of the script URL. 3975 */ setCallback() { 3976 this.window.__googleMapsCallback = this.callback.bind(this); 3977 } 3978 /** 3979 * Reset if failed 3980 */ resetIfRetryingFailed() { 3981 if (this.failed) this.reset(); 3982 } 3983 /** 3984 * This is the function that we set the callback parameter to. 3985 */ callback() { 3986 this.done = true; 3987 this.loading = false; 3988 this.callbacks.forEach((cb)=>{ 3989 cb(this.onerrorEvent); 3990 }); 3991 this.callbacks = []; 3992 } 3993 /** 3994 * Set the script a if needed; set the callback 3995 */ execute() { 3996 if (this.window.google && this.window.google.maps && this.window.google.maps.version) // Google Maps already loaded 3997 this.callback(); 3998 this.resetIfRetryingFailed(); 3999 if (this.done) this.callback(); 4000 else if (this.loading) ; 4001 else { 4002 this.loading = true; 4003 this.setCallback(); 4004 this.setScript().catch((e)=>console.error(e)); 4005 } 4006 } 4007 /** 4008 * get base url based on current config 4009 * @return {string} 4010 */ _getBaseUrl() { 4011 // Special handling for page builder since no wag available 4012 const { isBuilder: isBuilder, websiteApiGatewayUrl: websiteApiGatewayUrl } = this.config; 4013 return isBuilder ? websiteApiGatewayUrl : ''; 4014 } 4015 /** 4016 * Resize static map requests to the max 1024px while preserving aspect ratio 4017 * @param {number} width 4018 * @param {number} height 4019 * @return {{width, height}} 4020 */ resizeStaticMapDimensions(width, height) { 4021 if (width <= $ea6901fdf47e0ec1$export$76299006759be400 && height <= $ea6901fdf47e0ec1$export$76299006759be400) return { 4022 width: width, 4023 height: height 4024 }; 4025 // Size param ignored if dimensions exceed STATIC_MAP_MAX_DIM 4026 const ratio = Math.min($ea6901fdf47e0ec1$export$76299006759be400 / width, $ea6901fdf47e0ec1$export$76299006759be400 / height); 4027 return { 4028 width: parseInt(width * ratio), 4029 height: parseInt(height * ratio) 4030 }; 4031 } 4032 /** 4033 * Convert theme to static api param object 4034 * @param {Object} theme 4035 * @return {Object} 4036 */ convertThemeToStaticApiUrl(theme) { 4037 // https://developers.google.com/maps/documentation/maps-static/style-reference 4038 // Google Maps Static API uses a special syntax for it's urls based on the interactive map styles 4039 return theme.map((t)=>{ 4040 const styles = t.stylers.reduce((acc, s)=>{ 4041 const visibility = s.visibility ? `visibility:${s.visibility}` : ''; 4042 const color = s.color ? `color:${s.color.replace('#', '0x')}` : ''; 4043 const lightness = s.lightness ? `lightness:${s.lightness}` : ''; 4044 return `${acc}|${visibility}${color}${lightness}`; 4045 }, ''); 4046 const element = t.elementType ? `|element:${t.elementType}` : ''; 4047 return `feature:${t.featureType}${element}${styles}`; 4048 }); 4049 } 4050 /** 4051 * generate digital signature for url 4052 * @param {string} queryString to sign 4053 * @return {Promise<string>} digitally signed url 4054 */ async getMapApiSignedUrl(queryString) { 4055 try { 4056 const response = await fetch(`${this._getBaseUrl()}/api/v1/map/signature?${queryString}`); 4057 if (!response.ok) throw new Error('Failed to fetch signed URL'); 4058 const { url: url } = await response.json(); 4059 return url; 4060 } catch (e) { 4061 // eslint-disable-next-line no-console 4062 console.error(e); 4063 return ''; 4064 } 4065 } 4066 /** 4067 * Generate static map url 4068 * @param {number} lat 4069 * @param {number} lng 4070 * @param {number} zoom 4071 * @param {Object} theme 4072 * @param {number} srcWidth 4073 * @param {number} srcHeight 4074 * @return {string} 4075 */ generateStaticMapUrl(lat, lng, zoom, theme, srcWidth, srcHeight) {
4076 const baseUrl = 'https://maps.googleapis.com/maps/api/staticmap'; 4077 const { width: width, height: height } = this.resizeStaticMapDimensions(srcWidth, srcHeight); 4078 const query = { 4079 center: `${lat},${lng}`, 4080 zoom: zoom, 4081 scale: 2, 4082 size: `${width}x${height}`, 4083 key: this.apiKey 4084 }; 4085 if (theme) query.style = this.convertThemeToStaticApiUrl(theme); 4086 const params = new URLSearchParams(); 4087 Object.entries(query).forEach(([key, value])=>{ 4088 if (Array.isArray(value)) value.forEach((v)=>params.append(key, v.toString())); 4089 else params.append(key, value.toString()); 4090 }); 4091 return `${baseUrl}?${params.toString()}`; 4092 } 4093 /** 4094 * Generate static map url with digital signature 4095 * @param {Object} { 4096 * srcWidth {number}, required 4097 * srcHeight {number}, required 4098 * theme {array}, 4099 * center: `${lat},${lng}`, required 4100 * zoom: number, required 4101 * markers: `${lat},${lng}`, 4102 * scale: number, 4103 * otherQueryParams... (these must match what is compatible with google maps static api query params) 4104 * } 4105 * @return {Promise<string>} digitally signed url 4106 */ async generateDigitallySignedStaticMapUrl({ srcWidth: srcWidth, srcHeight: srcHeight, theme: theme, ...query }) { 4107 const { width: width, height: height } = this.resizeStaticMapDimensions(srcWidth, srcHeight); 4108 const queryParams = { 4109 ...query, 4110 size: `${width}x${height}` 4111 }; 4112 if (theme) queryParams.style = this.convertThemeToStaticApiUrl(theme); 4113 const params = new URLSearchParams(); 4114 Object.entries(queryParams).forEach(([key, value])=>{ 4115 if (Array.isArray(value)) value.forEach((v)=>params.append(key, v.toString())); 4116 else params.append(key, value.toString()); 4117 }); 4118 const queryString = params.toString(); 4119 try { 4120 // fetch digitally signed url 4121 return await this.getMapApiSignedUrl(queryString); 4122 } catch (e) { 4123 console.error(e); 4124 return ''; 4125 } 4126 } 4127} 4128 4129 4130/* eslint-disable import/prefer-default-export */ 4131 4132class $0a0ad49f13c0b8fd$export$ba9afeb75f20f616 extends (0, $7c391e2f7931d60d$export$3b5bd9381a52554c) { 4133 // constructor is not needed as it is inherited from BaseController 4134 /** 4135 * Controllers must implement a "destroy" method to be called 4136 * after page navigation to remove event handlers / cleanup 4137 */ destroy() {} 4138 /** 4139 * Places (re-places) a value into the query string under specified key 4140 * or removes it if new value is null or undefined 4141 * @param {string} key 4142 * @param {string|null|undefined|object|array} value 4143 * @param {string} url - defaults to window.location.href 4144 * @return {string} 4145 */ // eslint-disable-next-line sonarjs/cognitive-complexity 4146 updateQueryString(key, value, url) { 4147 // https://stackoverflow.com/questions/5999118/how-can-i-add-or-update-a-query-string-parameter 4148 if (!url) // eslint-disable-next-line 4149 url = this.window.location.href; 4150 let re = new RegExp(`([?&])${key}=.*?(&|#|$)`, 'i'); 4151 let replacement = ''; 4152 if (typeof value !== 'undefined' && value !== null) { 4153 if (Array.isArray(value)) { 4154 if (value.length) replacement = this.serializeArray(key, value); 4155 } else if (typeof value === 'object') { 4156 re = new RegExp(`([?&])${key}\\[[^=]*\\]=.*?(&|#|$)`, 'i'); 4157 replacement = this.serializeObj(value, '', key); 4158 } else replacement = `${key}=${encodeURIComponent(value)}`; 4159 } 4160 const hash = url.split('#'); 4161 let result = hash[0]; 4162 if (re.test(url)) { 4163 let clean = result; 4164 replacement = replacement ? `$1${replacement}$2` : '$1'; 4165 while(re.test(clean)){ 4166 result = clean.replace(re, replacement); 4167 clean = clean.replace(re, '$1'); 4168 } 4169 result = result.replace(/(&|\?)$/, ''); 4170 } else if (replacement) { 4171 const separator = result.indexOf('?') !== -1 ? '&' : '?'; 4172 result += `${separator}${replacement}`; 4173 } 4174 if (typeof hash[1] !== 'undefined' && hash[1] !== null) result += `#${hash[1]}`; 4175 return result; 4176 } 4177 /** 4178 * Returns variable from query string specified by parameter name 4179 * @param {string} name 4180 * @param {string} url defaults to window.location.href 4181 * @return {string|null} 4182 */ getParameterByName(name, url) { 4183 if (!url) // eslint-disable-next-line 4184 url = this.window.location.href; 4185 // eslint-disable-next-line 4186 name = name.replace(/[[\]]/g, '\\$&'); 4187 const regex = new RegExp(`[?&]${name}(=([^&#]*)|&|#|$)`); 4188 let results = Array.from(RegExp.prototype[Symbol.matchAll].call(regex, url)).flat(); 4189 // check for object like syntax where the last value can be an array eg. advancedFilters[query][statusIds][in][0] or advancedFilters[query][city] 4190 if (!results.length) { 4191 const objRegex = new RegExp(`[?&]${name}(\\[.*?\\])=(.*?)(?=&|#|$)`, 'g'); 4192 results = Array.from(RegExp.prototype[Symbol.matchAll].call(objRegex, url)).flat(); 4193 return this._parseParamToObject(results, name === 'advancedFilters'); 4194 } 4195 // For multiple param values, eg arrays 4196 /* Results from regex match all come in this form: 4197 [ '&advancedFilters[query][statusIds][in]=uuid-segundo-123', '[query][statusIds][in]', 'uuid-segundo-123' ] 4198 So using every third value will be the actual value and the second value will be the path 4199 */ if (results.length > 3) return results.reduce((acc, value, index)=>{ 4200 if ((index + 1) % 3 === 0) acc.push(decodeURIComponent(value.replace(/\+/g, ' '))); 4201 return acc; 4202 }, []); 4203 if (!results[2]) return ''; 4204 return decodeURIComponent(results[2].replace(/\+/g, ' ')); 4205 } 4206 /** 4207 * Generates section specific key to store page number in the URL 4208 * @param {string} sectionDomId 4209 * @return {string} 4210 */ makePageKey(sectionDomId) { 4211 return sectionDomId.replace('section-', '').replace(/-/g, ''); 4212 } 4213 /** 4214 * Generates specific key to store parameter in the URL 4215 * uses prevsiouly calculated baseKey for section specific parameters 4216 * @param {Object} formParam 4217 * @param {string} baseKey pre-calculated base for section specific parameters 4218 * @return {string} 4219 */ makeParamKey(formParam, baseKey) { 4220 return formParam.global ? formParam.urlKey : `${baseKey}-${formParam.urlKey}`; 4221 } 4222 /** 4223 * Gets a set of parameters from window query string 4224 * always returns value of "page" parameter if present 4225 * @param {Object[]} formConfig 4226 * @param {string} sectionDomId 4227 * @return {Object} 4228 */ deserializeParams(formConfig, sectionDomId) { 4229 const params = {}; 4230 const urlPageKey = this.makePageKey(sectionDomId); 4231 const page = this.getParameterByName(urlPageKey); 4232 if (page) params.page = parseInt(page); 4233 if (Array.isArray(formConfig)) formConfig.forEach((formParam)=>{
4234 const key = this.makeParamKey(formParam, urlPageKey); 4235 let urlValue = this.getParameterByName(key); 4236 if (formParam.type === 'int') { 4237 urlValue = parseInt(urlValue); 4238 // eslint-disable-next-line 4239 if (!isFinite(urlValue)) urlValue = null; 4240 } else if (formParam.type === 'bool' || formParam.type === 'boolean') urlValue = // eslint-disable-next-line 4241 urlValue === 'true' ? true : urlValue === 'false' ? false : null; 4242 else if (formParam.type === 'json') try { 4243 urlValue = JSON.parse(urlValue); 4244 } catch (e) { 4245 // no need to do anything 4246 } 4247 params[formParam.formKey || formParam.param] = urlValue; 4248 }); 4249 return params; 4250 } 4251 /** 4252 * Serializes set of parameters defined by formConfig into the URL 4253 * adding or replacing or removing required parameters 4254 * @param {Object[]} formConfig 4255 * @param {string} sectionDomId 4256 * @param {Object} params 4257 * @return {string} 4258 */ serializeParams(formConfig, sectionDomId, params) { 4259 let result = this.window.location.href; 4260 const urlPageKey = this.makePageKey(sectionDomId); 4261 result = this.updateQueryString(urlPageKey, params.page, result); 4262 if (Array.isArray(formConfig)) formConfig.forEach((formParam)=>{ 4263 const key = this.makeParamKey(formParam, urlPageKey); 4264 let value = params[formParam.formKey || formParam.param]; 4265 if (formParam.type === 'json' && value !== null && typeof value !== 'undefined') value = JSON.stringify(value); 4266 result = this.updateQueryString(key, value, result); 4267 }); 4268 return result; 4269 } 4270 /** 4271 * Formats small number for slider values 4272 * @param {number} value 4273 * @param {string} currency 4274 * @return {string} 4275 */ formatSliderAtom(value, currency = '') { 4276 const formattedValue = Number.isInteger(value) ? value : value.toFixed(2); 4277 return currency + formattedValue; 4278 } 4279 /** 4280 * Formats a number for slider values 4281 * @param {number} value 4282 * @param {string} currency 4283 * @param {string} delimiter 4284 * @return {string} 4285 */ formatSliderValue(value, currency = '', delimiter = '') { 4286 if (value < 1000) return this.formatSliderAtom(value, currency); 4287 if (value < 1000000) return `${this.formatSliderAtom(value / 1000, currency)}${delimiter}K`; 4288 return `${this.formatSliderAtom(value / 1000000, currency)}${delimiter}M`; 4289 } 4290 /** 4291 * Format price slider range 4292 * @param {number} value 4293 * @param {Object} range 4294 * @param {string} currency 4295 * @param {Object} options 4296 * @return {string} 4297 */ formatSliderPriceRange(value, range, currency, options) { 4298 if (range.min === value && value !== 0) return `${options.lowPrefix}${this.formatSliderValue(value, currency, ' ')}`; 4299 if (range.max === value) return `${this.formatSliderValue(value, currency, ' ')}${options.highSuffix}`; 4300 return `${this.formatSliderValue(value, currency, ' ')}`; 4301 } 4302 /** 4303 * Format area slider range 4304 * @param {number} value 4305 * @param {Object} range 4306 * @param {string} suffix 4307 * @param {Object} options 4308 * @return {string} 4309 */ formatSliderAreaRange(value, range, suffix, options) { 4310 if (range.min === value && value !== 0) return `${options.lowPrefix}${this.formatSliderValue(value)} ${suffix}`; 4311 if (range.max === value) return `${this.formatSliderValue(value)}${options.highSuffix} ${suffix}`; 4312 return `${this.formatSliderValue(value)} ${suffix}`; 4313 } 4314 /** 4315 * Utility function to get properties search parameters from a set of jQuery objects 4316 * @param {Object} options 4317 * @param {jQuery|HTMLElement} [options.queryElement] 4318 * @param {jQuery|HTMLElement} [options.priceSliderElement] 4319 * @param {jQuery|HTMLElement} [options.areaSliderElement] 4320 * @param {jQuery|HTMLElement} [options.neighborhoodSelect] 4321 * @return {Object} 4322 */ // eslint-disable-next-line sonarjs/cognitive-complexity 4323 getSearchFormValues(options) { 4324 const { queryElement: queryElement, priceSliderElement: priceSliderElement, areaSli
4324derElement: areaSliderElement, neighborhoodSelect: neighborhoodSelect } = options; 4325 const result = {}; 4326 if (queryElement) result.search = queryElement.value || null; 4327 if (priceSliderElement) { 4328 const priceValues = priceSliderElement.value.split(';'); 4329 const priceMinLimit = parseInt(priceSliderElement.dataset.min); 4330 const priceMaxLimit = parseInt(priceSliderElement.dataset.max); 4331 const priceMin = parseInt(priceValues[0]); 4332 const priceMax = parseInt(priceValues[1]); 4333 result.priceMin = priceMin !== priceMinLimit ? priceMin : null; 4334 result.priceMax = priceMax !== priceMaxLimit ? priceMax : null; 4335 } 4336 if (areaSliderElement) { 4337 const areaValues = areaSliderElement.value.split(';'); 4338 const areaMinLimit = parseInt(areaSliderElement.dataset.min); 4339 const areaMaxLimit = parseInt(areaSliderElement.dataset.max); 4340 const areaMin = parseInt(areaValues[0]); 4341 const areaMax = parseInt(areaValues[1]); 4342 result.areaMin = areaMin !== areaMinLimit ? areaMin : null; 4343 result.areaMax = areaMax !== areaMaxLimit ? areaMax : null; 4344 } 4345 if (neighborhoodSelect) result.neighborhoodId = neighborhoodSelect.value || null; 4346 return result; 4347 } 4348 /** 4349 * Add supports for object typeof values to be encoded on URL 4350 * Ref: https://stackoverflow.com/questions/1714786/query-string-encoding-of-a-javascript-object 4351 * @param {*} obj 4352 * @param {string} prefix 4353 * @param {string} baseParam used for grouping in case of nested objects 4354 * @returns 4355 */ // eslint-disable-next-line sonarjs/cognitive-complexity 4356 serializeObj(obj, prefix = '', baseParam = '') { 4357 const result = []; 4358 for(const prop in obj)if (obj.hasOwnProperty(prop)) { 4359 const value = obj[prop]; 4360 let k; 4361 if (baseParam) { 4362 const newPrefix = prefix ? `[${prefix}][${prop}]` : `[${prop}]`; 4363 k = `${baseParam}${newPrefix}`; 4364 } else k = prefix ? `${prefix}[${prop}]` : prop; 4365 if (value !== null && Array.isArray(value)) { 4366 if (value.length) result.push(this.serializeArray(k, value)); 4367 } else if (value !== null && typeof value === 'object') result.push(this.serializeObj(value, k)); 4368 else result.push(`${k}=${encodeURIComponent(value)}`); 4369 } 4370 return result.join('&'); 4371 } 4372 /** 4373 * Adds support for array values to be encoded on URL 4374 * @param {string} key 4375 * @param {Array} value 4376 * @param {string} baseParam 4377 */ serializeArray(key, value, baseParam = '') { 4378 const result = []; 4379 value.forEach((val)=>{ 4380 const paramName = baseParam ? `${baseParam}${key}` : key; 4381 result.push(`${paramName}=${encodeURIComponent(val)}`); 4382 }); 4383 return result.join('&'); 4384 } 4385 /** 4386 * Private method to process string search values. 4387 * @param {Array} results comes from regex matchAll. 4388 * @param {Boolean} isAdvancedFilters flag to determine if the search is for advanced filters. 4389 * @returns 4390 */ _parseParamToObject(results, isAdvancedFilters = false) { 4391 if (!results.length) return null; 4392 const obj = {}; 4393 for(let i = 0; i < results.length; i++)/* Results from regex match all come in this form: 4394 [ '&advancedFilters[query][statusIds][in]=uuid-segundo-123', '[query][statusIds][in]', 'uuid-segundo-123' ] 4395 So using every third value will be the actual value and the second value will be the path 4396 */ if ((i + 1) % 3 === 0) { 4397 const value = decodeURIComponent(results[i].replace(/\+/g, ' ')); 4398 const path = results[i - 1]; 4399 Object.assign(obj, this._setObjectPath(path, value, obj, isAdvancedFilters)); 4400 } 4401 return obj; 4402 } 4403 /** * 4404 * Private method to set object value given a path 4405 * @param {String} path 4406 * @param {*} value 4407 * @param {Object} baseObj 4408 * @param {Boolean} isAdvancedFilters flag to determine if the search is for advanced filters. 4409 */ _setObjectPath(path, value, baseObj, isAdvancedFilters = false) { 4410 // eslint-disable-next-line
4411 const keys = path.split(/[\[\]]/).filter(Boolean); 4412 const afArrayKeys = [ 4413 'in', 4414 'nin', 4415 'btw', 4416 'ciin', 4417 'cinin' 4418 ]; 4419 const obj = {}; 4420 let ref = obj; 4421 const valueExists = (0, $fc0a058ae42af628$export$267a1435fe18b8d4)(baseObj, keys.join('.')); 4422 keys.forEach((key, index)=>{ 4423 if (index === keys.length - 1) { 4424 if (valueExists) ref[key] = Array.isArray(valueExists) ? [ 4425 ...valueExists, 4426 value 4427 ] : [ 4428 valueExists, 4429 value 4430 ]; 4431 else ref[key] = isAdvancedFilters && afArrayKeys.includes(key) ? [ 4432 value 4433 ] : value; 4434 } else { 4435 ref[key] = baseObj[key] ? baseObj[key] : {}; 4436 ref = ref[key]; 4437 } 4438 }); 4439 return obj; 4440 } 4441} 4442 4443 4444/* eslint-disable import/prefer-default-export */ 4445class $2b1790aa32f109d1$export$43e435c053a109f6 extends (0, $7c391e2f7931d60d$export$3b5bd9381a52554c) { 4446 /** 4447 * Initialize Expand Slider Controller at build time 4448 * @param { ConfigController } config 4449 * @param {Window} window 4450 * @param {Document} document 4451 */ constructor(config, window, document){ 4452 super(config, window, document); 4453 this._createLightbox = this._createLightbox.bind(this); 4454 this.expandSliders = []; 4455 } 4456 /** 4457 * Initialize Expand Slider Controller at run time 4458 * Attach handlers and events to DOM 4459 * @param {Object} options 4460 */ initialize(options = {}) { 4461 try { 4462 const { document: document } = this; 4463 const expandSliders = document.querySelectorAll('.has-mobile-expand'); 4464 expandSliders.forEach((block)=>{ 4465 this._addExpandButton(block); 4466 }); 4467 this.expandSliders = this.expandSliders.concat(expandSliders); 4468 } catch (e) { 4469 // eslint-disable-next-line no-console 4470 console.log(e); 4471 } 4472 } 4473 /** 4474 * Controllers must implement a "destroy" method to be called 4475 * after page navigation to remove event handlers / cleanup 4476 */ destroy() { 4477 const { document: document } = this; 4478 document.querySelectorAll('.lp-expand-btn, .lp-expand-btn-wrapper').forEach((element)=>element.remove()); 4479 this.expandSliders = []; 4480 } 4481 /** 4482 * Create expand button for sliders 4483 * @param {HTMLElement} slider 4484 */ _addExpandButton(slider) { 4485 const { document: document } = this; 4486 const section = slider.closest('section[id]'); 4487 const appendTargetSelector = slider.dataset.append; 4488 let expandBtn = section.querySelector('.lp-expand-btn'); 4489 if (!expandBtn) { 4490 expandBtn = document.createElement('button'); 4491 expandBtn.className = 'lp-expand-btn'; 4492 expandBtn.setAttribute('aria-label', 'Open a lightbox'); 4493 expandBtn.innerHTML = `<span class="lp-i lp-i--expand-alt" aria-hidden="true"></span>`; 4494 if (appendTargetSelector) { 4495 const appendTarget = section.querySelector(appendTargetSelector); 4496 if (appendTarget) appendTarget.appendChild(expandBtn); 4497 } else { 4498 const wrapper = document.createElement('div'); 4499 wrapper.className = 'lp-expand-btn-wrapper'; 4500 wrapper.style.position = 'relative'; 4501 wrapper.appendChild(expandBtn); 4502 slider.parentNode.insertBefore(wrapper, slider.nextSibling); 4503 } 4504 } 4505 expandBtn.addEventListener('click', ()=>{ 4506 this._createLightbox({ 4507 slider: slider, 4508 section: section 4509 }); 4510 }); 4511 } 4512 /** 4513 * Creates a lightbox modal for displaying slider images in a larger view. 4514 * 4515 * @param {Object} params - The parameters for the function. 4516 * @param {HTMLElement} params.slider - The slider element containing images. 4517 * @param {HTMLElement} params.section - The section element related to the slider. 4518 * @param {number} [params.initialIndex] - Optional index of the image to show initially. 4519 */ _createLightbox({ slider: slider, section: section, initialIndex: initialIndex }) { 4520 const { window: window, document: document } = this; 4521 const modalId = `lightbox-for-${section?.id}`; 4522 const isSlickAvailable = window.$ && window.$.fn.slick; 4523 const isSectionSlickInitialized = isSlickAvailable && slider.slick; 4524 const isSplideAvailable = typeof window.Splide === 'function'; 4525 const isSectionSplideInitialized = slider.classList.contains('is-initialized') && slider.classList.contains('splide'); 4526 if (typeof window.lpUI.requestScrollLock === 'function') window.lpUI.requestScrollLock(modalId); 4527 else document.body.style.overflow = 'hidden'; 4528 function createSlickLightbox() { 4529 const slickOverlayOptions = { 4530 arrows: true, 4531 dots: false, 4532 infinite: true, 4533 draggable: true, 4534 prevArrow: '<button type="button" class="lp-lightbox-arrow--prev lp-lightbox-arrow" aria-label="Previous"></button>', 4535 nextArrow: '<button type="button" class="lp-lightbox-arrow--next lp-lightbox-arrow" aria-label="Next"></button>' 4536 }; 4537 const modal = document.createElement('div'); 4538 modal.id = modalId; 4539 modal.className = 'lp-lightbox'; 4540 modal.setAttribute('tabindex', '-1'); 4541 modal.setAttribute('role', 'dialog'); 4542 modal.setAttribute('aria-modal', 'true'); 4543 modal.innerHTML = ` 4544 <button class="lp-lightbox__close" aria-label="close modal"></button> 4545 <div class="lp-lightbox__content"> 4546 <div class="lp-lightbox__carousel"></div> 4547 </div> 4548 `; 4549 document.body.appendChild(modal); 4550 const overlaySlider = modal.querySelector('.lp-lightbox__carousel'); 4551 modal.classList.add('show'); 4552 let sourceImages; 4553 if (isSectionSlickInitialized) { 4554 sourceImages = slider.slick.$slides.find('img'); 4555 slickOverlayOptions.initialSlide = slider.slick?.currentSlide || 0; 4556 sourceImages.each(function() { 4557 window.$('.lp-lightbox__carousel').append(window.$(this).clone(false).removeAttr('style').wrap('<div class="lp-lightbox__slide"></div>').parent()); 4558 }); 4559 } else { 4560 sourceImages = slider.querySelectorAll('img');
4561 sourceImages.forEach((img)=>{ 4562 const imgClone = img.cloneNode(true); 4563 imgClone.removeAttribute('style'); 4564 const slide = document.createElement('div'); 4565 slide.className = 'lp-lightbox__slide'; 4566 slide.appendChild(imgClone); 4567 overlaySlider.appendChild(slide); 4568 }); 4569 } 4570 window.$(overlaySlider).slick(slickOverlayOptions); 4571 // Focus modal after it's created 4572 setTimeout(()=>{ 4573 modal.focus(); 4574 }, 100); 4575 // Keyboard navigation handler 4576 const handleKeydown = (e)=>{ 4577 if (!modal.classList.contains('show')) return; 4578 const prevArrow = modal.querySelector('.lp-lightbox-arrow--prev'); 4579 const nextArrow = modal.querySelector('.lp-lightbox-arrow--next'); 4580 const closeBtn = modal.querySelector('.lp-lightbox__close'); 4581 if (e.key === 'ArrowLeft' && prevArrow) { 4582 e.preventDefault(); 4583 prevArrow.click(); 4584 } else if (e.key === 'ArrowRight' && nextArrow) { 4585 e.preventDefault(); 4586 nextArrow.click(); 4587 } else if (e.key === 'Escape' && closeBtn) { 4588 e.preventDefault(); 4589 closeBtn.click(); 4590 } 4591 }; 4592 document.addEventListener('keydown', handleKeydown); 4593 modal.querySelector('.lp-lightbox__close').addEventListener('click', ()=>{ 4594 document.removeEventListener('keydown', handleKeydown); 4595 modal.classList.remove('show'); 4596 const currentSlide = overlaySlider?.slick ? window.$(overlaySlider).slick('slickCurrentSlide') : 0; 4597 if (overlaySlider?.slick) window.$(overlaySlider).slick('unslick'); 4598 if (isSectionSlickInitialized) window.$(slider).slick('slickGoTo', currentSlide); 4599 if (typeof window.lpUI.releaseScrollLock === 'function') window.lpUI.releaseScrollLock(modalId); 4600 else document.body.style.overflow = ''; 4601 modal.remove(); 4602 }); 4603 } 4604 function createSplideLightbox() { 4605 const splideOptions = { 4606 rewind: true, 4607 arrows: true, 4608 arrowPath: '', 4609 type: 'loop', 4610 clones: 1, 4611 pagination: false, 4612 perPage: 1, 4613 perMove: 1 4614 }; 4615 const modal = document.createElement('div'); 4616 modal.id = modalId; 4617 modal.className = 'lp-lightbox'; 4618 modal.setAttribute('tabindex', '-1'); 4619 modal.setAttribute('role', 'dialog'); 4620 modal.setAttribute('aria-modal', 'true'); 4621 modal.innerHTML = ` 4622 <button class="lp-lightbox__close" aria-label="close modal"></button> 4623 <div class="lp-lightbox__content"> 4624 <div class="lp-lightbox__carousel splide" role="group" aria-label="Image Gallery"> 4625 <div class="splide__arrows"> 4626 <button class="splide__arrow splide__arrow--prev lp-lightbox-arrow lp-lightbox-arrow--prev"></button> 4627 <button class="splide__arrow splide__arrow--next lp-lightbox-arrow lp-lightbox-arrow--next"></button> 4628 </div> 4629 <div class="splide__track"> 4630 <ul class="splide__list"></ul> 4631 </div> 4632 </div> 4633 </div> 4634 `; 4635 if (typeof window.lpUI.requestScrollLock === 'function') window.lpUI.requestScrollLock(modalId); 4636 else document.body.style.overflow = 'hidden'; 4637 document.body.appendChild(modal); 4638 const overlaySlider = modal.querySelector('.lp-lightbox__carousel'); 4639 const overlaySliderList = overlaySlider.querySelector('.splide__list'); 4640 modal.classList.add('show'); 4641 let sourceImages; 4642 if (isSectionSplideInitialized) sourceImages = slider.querySelectorAll('.splide__slide:not(.splide__slide--clone) img'); 4643 else sourceImages = slider.querySelectorAll('img');
4644 sourceImages.forEach((img)=>{ 4645 const imgClone = img.cloneNode(true); 4646 imgClone.removeAttribute('style'); 4647 const slide = document.createElement('li'); 4648 slide.className = 'lp-lightbox__slide splide__slide'; 4649 slide.appendChild(imgClone); 4650 overlaySliderList.appendChild(slide); 4651 }); 4652 const overlaySplide = new window.Splide(overlaySlider, splideOptions); 4653 overlaySplide.mount(); 4654 // Navigate to initial index if provided 4655 if (typeof initialIndex !== 'undefined' && initialIndex >= 0) overlaySplide.go(initialIndex); 4656 // Focus modal after it's created and mounted 4657 setTimeout(()=>{ 4658 modal.focus(); 4659 }, 100); 4660 // Keyboard navigation handler 4661 const handleKeydown = (e)=>{ 4662 if (!modal.classList.contains('show')) return; 4663 const prevArrow = modal.querySelector('.splide__arrow--prev'); 4664 const nextArrow = modal.querySelector('.splide__arrow--next'); 4665 const closeBtn = modal.querySelector('.lp-lightbox__close'); 4666 if (e.key === 'ArrowLeft' && prevArrow) { 4667 e.preventDefault(); 4668 prevArrow.click(); 4669 } else if (e.key === 'ArrowRight' && nextArrow) { 4670 e.preventDefault(); 4671 nextArrow.click(); 4672 } else if (e.key === 'Escape' && closeBtn) { 4673 e.preventDefault(); 4674 closeBtn.click(); 4675 } 4676 }; 4677 document.addEventListener('keydown', handleKeydown); 4678 modal.querySelector('.lp-lightbox__close').addEventListener('click', ()=>{ 4679 document.removeEventListener('keydown', handleKeydown); 4680 modal.classList.remove('show'); 4681 if (overlaySplide) overlaySplide.destroy(); 4682 if (typeof window.lpUI.releaseScrollLock === 'function') window.lpUI.releaseScrollLock(modalId); 4683 else document.body.style.overflow = ''; 4684 modal.remove(); 4685 }); 4686 } 4687 if (isSlickAvailable && !isSectionSplideInitialized) createSlickLightbox(); 4688 else if (isSplideAvailable) createSplideLightbox(); 4689 else { 4690 window.luxuryPresence.libs.loadCss(window.luxuryPresence.libs.jsLibsMap.splide.cssSrc); 4691 window.luxuryPresence.libs.loadJs(window.luxuryPresence.libs.jsLibsMap.splide.jsSrc, ()=>{ 4692 createSplideLightbox(); 4693 window.luxuryPresence.libs.jsLibsMap.splide.isLoaded = true; 4694 }); 4695 } 4696 } 4697} 4698 4699 4700/* eslint-disable import/prefer-default-export */ 4701class $4f184d9bd3037f15$export$7a6215890afba974 extends (0, $7c391e2f7931d60d$export$3b5bd9381a52554c) { 4702 // constructor is not needed as it is inherited from BaseController 4703 /** 4704 * Destroy 4705 */ destroy() { 4706 if (!this.window.divolte) this.window.divolteLp = function() { 4707 (this.window.divolteLp.q = this.window.divolteLp.q || []).push(// eslint-disable-next-line prefer-rest-params 4708 arguments); 4709 }; 4710 } 4711 /** 4712 * Initialize divolte controller 4713 * @param {Object} options 4714 */ initialize(options = {}) { 4715 const { divolteServiceUrl: divolteServiceUrl } = this.config; 4716 if (!divolteServiceUrl || this.window.divolte) return; 4717 this.loadScript(); 4718 } 4719 /** 4720 * loads divolte 4721 */ loadScript() { 4722 const { divolteServiceUrl: divolteServiceUrl } = this.config; 4723 const script = this.document.createElement('script'); 4724 script.id = 'divolte-script'; 4725 script.type = 'text/javascript'; 4726 script.defer = true; 4727 script.async = true; 4728 script.src = `${divolteServiceUrl}/divolte.js`; 4729 this.document.head.appendChild(script); 4730 script.addEventListener('load', this.executeQueue.bind(this)); 4731 } 4732 /** 4733 * executeQueue 4734 */ executeQueue() { 4735 (this.window.divolteLp.q || []).forEach(([methodName, params])=>{ 4736 this[methodName](params); 4737 }); 4738 this.window.divolteLp = this.divolteLp.bind(this); 4739 } 4740 /** 4741 * divolteLp 4742 * @param {string} name 4743 * @param {Object} params 4744 */ divolteLp(name, params) { 4745 try { 4746 this[name](params); 4747 } catch (error) { 4748 // eslint-disable-next-line no-console 4749 console.log(error); 4750 } 4751 } 4752 /** 4753 * sendLpEvent 4754 * @param {string} name 4755 * @param {Object} params 4756 */ sendLpEvent(name, params) { 4757 // Events must be defined in divolte schema! To ensure correct usage, event names are validated here. 4758 if (!name.includes('pageView')) { 4759 // Event not defined 4760 // eslint-disable-next-line no-console 4761 console.log(`${name} IS NOT SUPPORTED`); 4762 return; 4763 } 4764 const { companyId: companyId, templateId: templateId, websiteId: websiteId, pageId: pageId, shareId: shareId, pageMeta: pageMeta } = this.config; 4765 const baseParams = { 4766 companyId: companyId, 4767 templateId: templateId, 4768 websiteId: websiteId, 4769 pageId: pageId, 4770 shareId: shareId, 4771 pageMeta: pageMeta 4772 }; 4773 this.window.divolte.signal(name, Object.assign(baseParams, params)); 4774 } 4775 /** 4776 * sendLpPageView 4777 */ sendLpPageView() { 4778 this.sendLpEvent('pageView', {}); 4779 } 4780} 4781 4782 4783/* eslint-disable import/prefer-default-export */ 4784const $102039871668a1df$var$EVENT_BUS_SCRIPT = 'event-bus-js.js'; 4785const $102039871668a1df$var$EVENT_BUS_VERSION = 'v2.8.0'; 4786const $102039871668a1df$var$EVENT_BUS_DEFAULT_SOURCE = 'frontend://website-render-engine'; 4787class $102039871668a1df$export$e22e4b1be0bb6c61 extends (0, $7c391e2f7931d60d$export$3b5bd9381a52554c) { 4788 // eslint-disable-next-line @typescript-eslint/no-useless-constructor 4789 constructor(config, window, document){ 4790 super(config, window, document); 4791 } 4792 /** 4793 * Initialize EventBus controller 4794 * @param {Object} options 4795 */ initialize(options = {}) { 4796 const enabled = options.enabled !== undefined ? options.enabled : this.config.eventBus?.enabled; 4797 if (enabled && this.config.eventBus?.apiKey && this.config.eventBus?.url) this.loadScript(); 4798 } 4799 /** 4800 * loads eventBus script 4801 */ loadScript() { 4802 const { eventBusUrl: eventBusUrl } = this.config; 4803 const script = this.document.createElement('script'); 4804 script.id = 'posthog-script'; 4805 script.type = 'text/javascript'; 4806 script.defer = true; 4807 script.async = true; 4808 script.src = `${eventBusUrl}/${$102039871668a1df$var$EVENT_BUS_VERSION}/${$102039871668a1df$var$EVENT_BUS_SCRIPT}`; 4809 this.document.head.appendChild(script); 4810 script.addEventListener('load', this.executeQueue.bind(this)); 4811 } 4812 /** 4813 * executeQueue - Process queued eventBusLp calls 4814 */ executeQueue() { 4815 // Initialize the event bus provider using the global EventBusJS 4816 if (this.window.LuxuryPresenceEventBus) this.window.eventBusProvider = this.window.LuxuryPresenceEventBus.createEventBusProvider({ 4817 apiKey: this.config.eventBus.apiKey, 4818 host: this.config.eventBus.url, 4819 defaultSource: $102039871668a1df$var$EVENT_BUS_DEFAULT_SOURCE 4820 });
4821 (this.window.eventBusLp.q || []).forEach(([methodName, params])=>{ 4822 this[methodName](params); 4823 }); 4824 // Replace queue function with real function 4825 this.window.eventBusLp = this.eventBusLp.bind(this); 4826 } 4827 /** 4828 * Destroy 4829 */ destroy() { 4830 if (!this.window.eventBusProvider) { 4831 // Reset to queue function if no provider exists 4832 this.window.eventBusLp = function() { 4833 (this.window.eventBusLp.q = this.window.eventBusLp.q || []).push(// eslint-disable-next-line prefer-rest-params 4834 arguments); 4835 }; 4836 return; 4837 } 4838 this.window.eventBusProvider.shutdown(); 4839 } 4840 /** 4841 * sendLpEvent 4842 * @param {string} eventType 4843 * @param {Object} data 4844 */ sendLpEvent(eventType, data) { 4845 // Events must be defined in event-bus schema! To ensure correct usage, event types are validated here. 4846 if (eventType !== 'user.activity.page.viewed') { 4847 // Event type not defined 4848 // eslint-disable-next-line no-console 4849 console.log(`${eventType} IS NOT SUPPORTED`); 4850 return; 4851 } 4852 if (!this.window.eventBusProvider) return; 4853 const { companyId: companyId } = this.config; 4854 const eventData = { 4855 company: companyId, 4856 data: {} 4857 }; 4858 try { 4859 this.window.eventBusProvider.capture(eventType, eventData); 4860 } catch (error) { 4861 // eslint-disable-next-line no-console 4862 console.log('EventBus capture error:', error); 4863 } 4864 } 4865 /** 4866 * eventBusLp 4867 * @param {string} name 4868 * @param {Object} params 4869 */ eventBusLp(name, params) { 4870 try { 4871 this[name](params); 4872 } catch (error) { 4873 // eslint-disable-next-line no-console 4874 console.log(error); 4875 } 4876 } 4877 /** 4878 * sendLpPageView 4879 */ sendLpPageView() { 4880 this.sendLpEvent('user.activity.page.viewed', {}); 4881 } 4882} 4883 4884 4885/* eslint-disable import/prefer-default-export */ 4886 4887const $0d25a3fa2e0f4f92$var$EVENT_NAME = 'oauthSuccess'; 4888class $0d25a3fa2e0f4f92$export$b718a981691eae5c extends (0, $7c391e2f7931d60d$export$3b5bd9381a52554c) { 4889 /** 4890 * Initialize Button Controller at build time 4891 * @param { ConfigController } config 4892 * @param {Window} window 4893 * @param {Document} document 4894 */ constructor(config, window, document){ 4895 super(config, window, document); 4896 this.successListeners = []; 4897 this._listenToMessages = this._listenToMessages.bind(this); 4898 this._listenToOneTapMessage = this._listenToOneTapMessage.bind(this); 4899 } 4900 /** 4901 * Initialize OAuth Controller at run time 4902 * Swap out special buttons for the iframe 4903 * @param {Object} options 4904 */ initialize(options = {}) { 4905 try { 4906 // handle google one tap 4907 if (options.googleSignOnEnabled && !options.iframe) this._renderGoogleOneTap(); 4908 // handle google sign in buttons 4909 this.document.querySelectorAll('div[data-signin-type="google-sign-in"]').forEach((el)=>this._convertGoogleSignIn(el)); 4910 // handle facebook sign in buttons 4911 this.document.querySelectorAll('div[data-signin-type="facebook-sign-in"]').forEach((el)=>this._convertFacebookSignIn(el)); 4912 } catch (e) { 4913 // eslint-disable-next-line no-console 4914 console.log(e); 4915 } 4916 } 4917 /** 4918 * Controllers must implement a "destroy" method to be called 4919 * after page navigation to remove event handlers / cleanup 4920 */ destroy() { 4921 this.successListeners.forEach((callback)=>{ 4922 this.window.removeEventListener('message', this._listenToMessages); 4923 this.window.removeEventListener('message', this._listenToOneTapMessage); 4924 this.window.removeEventListener($0d25a3fa2e0f4f92$var$EVENT_NAME, this.window[callback]); 4925 }); 4926 } 4927 /** 4928 * Convert google one tap 4929 */ _renderGoogleOneTap() { 4930 if (!this._isGoogleOneTapOnCooldown()) { 4931 const dummyEl = this.document.createElement('div'); 4932 // eslint-disable-next-line no-undef 4933 google.accounts.id.initializeIntermediate({ 4934 src: this._buildUrlAndCopyAttributes(dummyEl, dummyEl, 'googleOneTap') 4935 }); 4936 this.window.localStorage.removeItem('googleOneTapExpiration'); 4937 this.window.addEventListener('message', this._listenToOneTapMessage); 4938 } 4939 } 4940 /** 4941 * Listening for messages from BSS one tap iframes 4942 * @param {MessageEvent} e 4943 */ _listenToOneTapMessage(e) { 4944 const messageTypes = { 4945 SHOW_INTERMEDIATE_IFRAME: 'show_intermediate_iframe', 4946 HIDE_INTERMEDIATE_IFRAME: 'hide_intermediate_iframe' 4947 };
4948 const iframeId = 'onetap_google_intermediate_iframe'; 4949 const { buyerSellerServiceUrl: buyerSellerServiceUrl } = this.config; 4950 const { data: data, origin: origin } = e; 4951 const { type: type, height: height, event: event, source: source, token: token } = data; 4952 if (type === messageTypes.SHOW_INTERMEDIATE_IFRAME || type === messageTypes.HIDE_INTERMEDIATE_IFRAME) { 4953 const bssIframe = this.document.getElementById(iframeId); 4954 if (bssIframe) { 4955 bssIframe.style.height = height; 4956 bssIframe.style.display = type === messageTypes.SHOW_INTERMEDIATE_IFRAME ? 'unset' : 'none'; 4957 } 4958 } 4959 // these events are generated by our code in the bss iframe 4960 if (origin === buyerSellerServiceUrl && source === 'GOOGLE_SIGN_ON') { 4961 if (event === 'success') { 4962 this._setExpirationLsItem(86400000); // 24 hours 4963 this._handleQualifyingQuestionsModal(token); 4964 } else if (event === 'cancel') this._setExpirationLsItem(7200000); // 2 hours 4965 } 4966 } 4967 /** 4968 * Set expiration local storage item for google one tap 4969 * @param {number} ms 4970 */ _setExpirationLsItem(ms) { 4971 const expiration = new Date().getTime() + ms; 4972 this.window.localStorage.setItem('googleOneTapExpiration', expiration); 4973 } 4974 /** 4975 * Check if google one tap is expired 4976 * @return {boolean} 4977 */ _isGoogleOneTapOnCooldown() { 4978 const expirationTime = this.window.localStorage.getItem('googleOneTapExpiration'); 4979 return Number(expirationTime) > new Date().getTime(); 4980 } 4981 /** 4982 * Convert google sign in buttons 4983 * @param {HTMLElement} el 4984 */ _convertGoogleSignIn(el) { 4985 this._replaceWithIframe('googleSignIn', el); 4986 } 4987 /** 4988 * Convert google sign in buttons 4989 * @param {HTMLElement} el 4990 */ _convertFacebookSignIn(el) { 4991 this._replaceWithIframe('facebookSignIn', el); 4992 } 4993 /** 4994 * Convert google sign in buttons 4995 * @param {'googleSignIn' | 'facebookSignIn'} provider 4996 * @param {HTMLElement} el 4997 */ _replaceWithIframe(provider, el) { 4998 this._attachSuccessCallback(el); 4999 const iframe = this.document.createElement('iframe'); 5000 iframe.src = this._buildUrlAndCopyAttributes(el, iframe, provider); 5001 iframe.dataset.isSocialOauthButton = true; 5002 el.replaceWith(iframe); 5003 } 5004 /** 5005 * Listening for messages from BSS iframes 5006 * @param {Event} e 5007 */ _listenToMessages(e) { 5008 const { buyerSellerServiceUrl: buyerSellerServiceUrl } = this.config; 5009 // eslint-disable-next-line sonarjs/no-collapsible-if 5010 if (e.origin === buyerSellerServiceUrl) { 5011 if (e.data.event === 'success') { 5012 const event = new Event($0d25a3fa2e0f4f92$var$EVENT_NAME); 5013 this.window.dispatchEvent(event); 5014 // Registration / social sign-in is a lead event â credit the conversion. 5015 (0, $3ec2bf3965f87ecc$export$e70565180c0e6950)(this.window, this.document); 5016 this.successListeners.forEach((callback)=>{ 5017 if (this.window[callback]) this.window[callback](event); 5018 else // eslint-disable-next-line no-console 5019 console.error(`Callback function ${callback} not found on window object`); 5020 }); 5021 this.document.documentElement.classList.add('is-login-success'); 5022 } 5023 } 5024 } 5025 /** 5026 * Attach callback to iframe 5027 * @param {HTMLElement} el 5028 */ _attachSuccessCallback(el) { 5029 this.window.addEventListener('message', this._listenToMessages); 5030 const callbackFunction = el.dataset.onSuccessFunctionName; 5031 if (callbackFunction) this.successListeners.push(callbackFunction); 5032 } 5033 /** 5034 * Build Iframe URL 5035 * @param {HTMLElement} source 5036 * @param {HTMLElement} target 5037 * @param {string} provider 5038 * @return {string} 5039 */ _buildUrlAndCopyAttributes(source, target, provider) { 5040 const { buyerSellerServiceUrl: buyerSellerServiceUrl, companyId: companyId, websiteId: websiteId, pageId: pageId, pageMeta: pageMeta } = this.config; 5041 const { location: location } = this.window; 5042 const sourceUrl = location.origin + location.pathname; 5043 const destination = new URL(`${buyerSellerServiceUrl}/buttons/${provider}`); 5044 const utm = this.window.localStorage.getItem('utm_vars'); 5045 const referrer = this.window.localStorage.getItem('referrer'); 5046 destination.searchParams.append('companyId', companyId); 5047 destination.searchParams.append('websiteId', websiteId); 5048 destination.searchParams.append('pageId', pageId); 5049 destination.searchParams.append('sourceUrl', sourceUrl); 5050 destination.searchParams.append('pageMeta', JSON.stringify(pageMeta)); 5051 if (utm) destination.searchParams.append('utm', utm); 5052 if (referrer) destination.searchParams.append('referrer', referrer); 5053 // Google and Facebook Attributes used to style the social button 5054 const RESERVED_ATTRIBUTES = [ 5055 'data-size', 5056 'data-width', 5057 'data-text', 5058 'data-button-type', 5059 'data-shape', 5060 'data-layout', 5061 'data-max-rows', 5062 'data-use-continue-as', 5063 'data-type', 5064 'data-theme', 5065 'data-logo_alignment', 5066 'data-locale' 5067 ];
5068 Array.from(source.attributes).forEach((attribute)=>{ 5069 if (RESERVED_ATTRIBUTES.includes(attribute.nodeName)) destination.searchParams.append(attribute.nodeName, attribute.nodeValue); 5070 else target.setAttribute(attribute.nodeName === 'id' ? 'data-id' : attribute.nodeName, attribute.nodeValue); 5071 }); 5072 return destination.toString(); 5073 } 5074 /** 5075 * Decode a JWT token to extract payload information 5076 * @param {string} token - The JWT token to decode 5077 * @return {Object|null} The decoded payload object or null if decoding fails 5078 */ _decodeToken(token) { 5079 try { 5080 const parts = token.split('.'); 5081 const payload = parts[1]; 5082 const base64 = payload.replace(/-/g, '+').replace(/_/g, '/'); 5083 const padding = 4 - base64.length % 4; 5084 const padded = padding === 4 ? base64 : base64 + '='.repeat(padding); 5085 return JSON.parse(atob(padded)); 5086 } catch (error) { 5087 return null; 5088 } 5089 } 5090 /** 5091 * Handle qualifying questions modal showing for Google Sign On 5092 * Decodes the JWT token to extract user email and shows the modal if available 5093 * @param {string} token - The JWT token containing user information 5094 */ _handleQualifyingQuestionsModal(token) { 5095 if (token && this.window.luxuryPresence && this.window.luxuryPresence.qualifyingQuestionsModal) { 5096 // Decoding only for lead attribution, fine to skip signature verification 5097 const decoded = this._decodeToken(token); 5098 if (decoded && decoded.email) this.window.luxuryPresence.qualifyingQuestionsModal.showModal(decoded.email, 'Google Sign On'); 5099 } 5100 } 5101} 5102 5103 5104/* eslint-disable import/prefer-default-export */ 5105class $dcf72769f009ff26$export$b900d6fe921fd49c extends (0, $7c391e2f7931d60d$export$3b5bd9381a52554c) { 5106 /** 5107 * Initialize Qualifying Questions Modal Controller at build time 5108 * @param { ConfigController } config 5109 * @param {Window} window 5110 * @param {Document} document 5111 */ constructor(config, window, document){ 5112 super(config, window, document); 5113 this.showModal = this.showModal.bind(this); 5114 this.hasModalBeenSubmitted = this.hasModalBeenSubmitted.bind(this); 5115 this.modalId = 'qualifying-questions-modal'; 5116 this.cookieKey = 'qualifyingQuestionsModalSubmitted'; 5117 this.qualifyingQuestionsModalEnabled = true; 5118 // Store references for cleanup 5119 this.eventListeners = []; 5120 this.timeouts = []; 5121 } 5122 initialize(options = {}) { 5123 this.qualifyingQuestionsModalEnabled = options.qualifyingQuestionsModalEnabled ?? this.qualifyingQuestionsModalEnabled; 5124 if (!this.qualifyingQuestionsModalEnabled) return; 5125 this.attachModalCloseListeners(); 5126 } 5127 destroy() { 5128 // Remove all stored event listeners 5129 this.eventListeners.forEach(({ element: element, event: event, handler: handler })=>{ 5130 if (element && element.removeEventListener) element.removeEventListener(event, handler); 5131 }); 5132 this.timeouts.forEach((timeoutId)=>{ 5133 clearTimeout(timeoutId); 5134 }); 5135 this.restoreBodyScroll(); 5136 this.eventListeners = []; 5137 this.timeouts = []; 5138 } 5139 /** 5140 * Check if modal has been submitted using cookie-based deduplication 5141 * @return {boolean} 5142 */ hasModalBeenSubmitted() { 5143 const cookieValue = this.getCookie(this.cookieKey); 5144 return cookieValue === 'true'; 5145 } 5146 /** 5147 * Set cookie to prevent modal from showing again 5148 */ setModalSubmittedCookie() { 5149 this.setCookie(this.cookieKey, 'true', 365); // 1 year 5150 } 5151 showModal(email, triggerSource) { 5152 if (!this.qualifyingQuestionsModalEnabled || this.hasModalBeenSubmitted()) return; 5153 this.updateEmailValue(email); 5154 this.updateTriggerSourceValue(triggerSource); 5155 this.showModalElement(); 5156 } 5157 /** 5158 * Get modal element with error handling 5159 * @return {Element|null} 5160 */ getModalElement() { 5161 const modal = this.document.getElementById(this.modalId); 5162 if (!modal) console.error('Modal element not found:', this.modalId); 5163 return modal; 5164 } 5165 updateEmailValue(email) { 5166 const modal = this.getModalElement(); 5167 if (!modal) return; 5168 const emailInput = modal.querySelector('input[name="email"]'); 5169 if (emailInput && typeof email === 'string') emailInput.value = email; 5170 } 5171 updateTriggerSourceValue(triggerSource) { 5172 const modal = this.getModalElement(); 5173 if (!modal) return;
5174 const triggerSourceInput = modal.querySelector(`#${this.modalId}-triggerSource`); 5175 if (triggerSourceInput && typeof triggerSource === 'string') triggerSourceInput.value = triggerSource; 5176 } 5177 showModalElement() { 5178 const modal = this.getModalElement(); 5179 if (!modal) return; 5180 modal.style.display = 'flex'; 5181 this.blockBodyScroll(); 5182 } 5183 /** 5184 * Helper method to get cookie value 5185 * @param {string} name 5186 * @return {string|null} 5187 */ getCookie(name) { 5188 const value = `; ${this.document.cookie}`; 5189 const parts = value.split(`; ${name}=`); 5190 if (parts.length === 2) return decodeURIComponent(parts.pop().split(';').shift()); 5191 return null; 5192 } 5193 /** 5194 * Helper method to set cookie 5195 * @param {string} name 5196 * @param {string} value 5197 * @param {number} days 5198 */ setCookie(name, value, days) { 5199 let expires = ''; 5200 if (days) { 5201 const date = new Date(); 5202 date.setTime(date.getTime() + days * 86400000); 5203 expires = `; expires=${date.toUTCString()}`; 5204 } 5205 let attrs = '; path=/; SameSite=Lax'; 5206 if (this.window && this.window.location && this.window.location.protocol === 'https:') attrs += '; Secure'; 5207 this.document.cookie = `${name}=${encodeURIComponent(value || '')}${expires}${attrs}`; 5208 } 5209 blockBodyScroll() { 5210 this._prevOverflow = this.document.body.style.overflow; 5211 this.document.body.style.overflow = 'hidden'; 5212 } 5213 restoreBodyScroll() { 5214 this.document.body.style.overflow = this._prevOverflow || ''; 5215 this._prevOverflow = null; 5216 } 5217 attachModalCloseListeners() { 5218 const modal = this.getModalElement(); 5219 if (!modal) return; 5220 // Listen for form submission success (when modal closes automatically) 5221 const form = this.document.getElementById(`${this.modalId}-form`); 5222 if (form) { 5223 const formSubmittedHandler = ()=>{ 5224 // Set cookie to prevent showing again and hide modal 5225 this.setModalSubmittedCookie(); 5226 const timeoutId = setTimeout(()=>{ 5227 modal.style.display = 'none'; 5228 this.restoreBodyScroll(); 5229 }, 100); 5230 this.timeouts.push(timeoutId); 5231 }; 5232 form.addEventListener('formsubmitted', formSubmittedHandler); 5233 this.eventListeners.push({ 5234 element: form, 5235 event: 'formsubmitted', 5236 handler: formSubmittedHandler 5237 }); 5238 } 5239 // Listen for close button clicks 5240 const closeButton = modal.querySelector('[data-dismiss="modal"]'); 5241 if (closeButton) { 5242 const closeButtonHandler = ()=>{ 5243 modal.style.display = 'none'; 5244 this.restoreBodyScroll(); 5245 }; 5246 closeButton.addEventListener('click', closeButtonHandler); 5247 this.eventListeners.push({ 5248 element: closeButton, 5249 event: 'click', 5250 handler: closeButtonHandler 5251 }); 5252 } 5253 // Listen for backdrop clicks 5254 const backdropClickHandler = (e)=>{ 5255 // Check if click is on the modal backdrop (not on the modal content) 5256 if (e.target === modal || e.target.id === this.modalId) { 5257 modal.style.display = 'none'; 5258 this.restoreBodyScroll(); 5259 } 5260 }; 5261 modal.addEventListener('click', backdropClickHandler); 5262 this.eventListeners.push({ 5263 element: modal, 5264 event: 'click', 5265 handler: backdropClickHandler 5266 }); 5267 } 5268} 5269 5270 5271/* eslint-disable import/prefer-default-export */ 5272class $50eefeef8d1f2372$export$8a2bf06fbae4dde extends (0, $7c391e2f7931d60d$export$3b5bd9381a52554c) { 5273 constructor(configController, window, document){ 5274 super(configController, window, document); 5275 this.isValidating = false; 5276 this.lastValidationResult = null; 5277 this.lastValidationTime = null; 5278 this.validationCacheDuration = 300000; // 5 minutes 5279 } 5280 /** 5281 * Destroy 5282 */ destroy() { 5283 this.isValidating = false;
5284 this.lastValidationResult = null; 5285 this.lastValidationTime = null; 5286 } 5287 /** 5288 * Validate user session using GraphQL validSession query 5289 * @param {Object} options - Options for session validation 5290 * @param {boolean} options.useCache - Whether to use cached validation result 5291 * @returns {Promise<boolean>} - true if session is valid, false otherwise 5292 */ async validateSession(options = {}) { 5293 const { useCache: useCache = true } = options; 5294 if (useCache && this.lastValidationResult !== null && this.lastValidationTime) { 5295 const timeSinceLastValidation = Date.now() - this.lastValidationTime; 5296 if (timeSinceLastValidation < this.validationCacheDuration) return this.lastValidationResult; 5297 } 5298 if (this.isValidating) return this.lastValidationResult !== null ? this.lastValidationResult : false; 5299 this.isValidating = true; 5300 const query = ` 5301 query { 5302 validSession { 5303 success 5304 } 5305 } 5306 `; 5307 try { 5308 // query from same domain as the website 5309 const response = await this.window.fetch(`/graphql`, { 5310 method: 'POST', 5311 headers: { 5312 'Content-Type': 'application/json' 5313 }, 5314 credentials: 'include', 5315 body: JSON.stringify({ 5316 query: query 5317 }) 5318 }); 5319 if (!response.ok) { 5320 // eslint-disable-next-line no-console 5321 console.error('SessionController: HTTP error during session validation', response.status); 5322 this.lastValidationResult = false;
5323 this.lastValidationTime = Date.now(); 5324 return false; 5325 } 5326 const data = await response.json(); 5327 if (data.errors) { 5328 this.lastValidationResult = false; 5329 this.lastValidationTime = Date.now(); 5330 return false; 5331 } 5332 const isValid = data.data?.validSession?.success === true; 5333 this.lastValidationResult = isValid; 5334 this.lastValidationTime = Date.now(); 5335 return isValid; 5336 } catch (error) { 5337 // eslint-disable-next-line no-console 5338 console.error('SessionController: Error validating session', error); 5339 this.lastValidationResult = false; 5340 this.lastValidationTime = Date.now(); 5341 return false; 5342 } finally{ 5343 this.isValidating = false; 5344 } 5345 } 5346 /** 5347 * Clear validation cache 5348 */ clearCache() { 5349 this.lastValidationResult = null; 5350 this.lastValidationTime = null; 5351 } 5352} 5353 5354 5355/* eslint-disable import/prefer-default-export */ 5356function $396d9f0ca7465335$export$48d5542b9e3ba39(document) { 5357 const match = document.cookie.match(/cookieyes-consent=([^;]+)/); 5358 if (!match) return false; 5359 try { 5360 const cookieValue = decodeURIComponent(match[1]); 5361 if (cookieValue.includes(',')) { 5362 const segments = cookieValue.split(',').map((c)=>c.trim()); 5363 if (segments.some((s)=>s.includes(':'))) return segments.some((s)=>s === 'analytics:yes'); 5364 return segments.includes('analytics'); 5365 } 5366 return cookieValue.includes('analytics'); 5367 } catch (error) { 5368 return false; 5369 } 5370} 5371/** 5372 * Consent providers. Each entry isolates the only things that differ between 5373 * integrations: the change-event name, how to read the *initial* analytics 5374 * consent, and how to interpret a change event's detail. Everything else 5375 * (loading/removing trackers, lifecycle) is shared by the controller. 5376 */ const $396d9f0ca7465335$var$PROVIDERS = { 5377 // Third-party CookieYes banner. 5378 cookieyes: { 5379 eventName: 'cookieyes_consent_update', 5380 hasAnalyticsConsent: (document)=>$396d9f0ca7465335$export$48d5542b9e3ba39(document), 5381 isAnalyticsAccepted: (detail)=>Array.isArray(detail?.accepted) && detail.accepted.includes('analytics') 5382 }, 5383 // LP in-house consent widget (lp-consent-widget). It exposes resolved state on 5384 // window.__lpConsent and emits `lp_consent_update` with detail.choices. 5385 'lp-consent': { 5386 eventName: 'lp_consent_update', 5387 hasAnalyticsConsent: (_document, window)=>!!window.__lpConsent?.state?.c?.analytics, 5388 isAnalyticsAccepted: (detail)=>!!detail?.choices?.analytics 5389 } 5390}; 5391class $396d9f0ca7465335$export$bb018224876724b9 extends (0, $7c391e2f7931d60d$export$3b5bd9381a52554c) { 5392 constructor(config, window, document){ 5393 super(config, window, document); 5394 this.consentListener = null; 5395 this.eventName = null; 5396 } 5397 /** 5398 * Destroy and cleanup event listeners 5399 */ destroy() { 5400 if (this.consentListener && this.eventName) { 5401 this.document.removeEventListener(this.eventName, this.consentListener); 5402 this.consentListener = null; 5403 } 5404 } 5405 /** 5406 * Load PostHog and Divolte scripts (idempotent â guarded by element id). 5407 */ loadScript() { 5408 if (!this.document.getElementById('posthog-script')) this.window.luxuryPresence.eventBus.initialize({ 5409 enabled: this.config.eventBus?.enabled 5410 }); 5411 if (!this.document.getElementById('divolte-script')) this.window.luxuryPresence.divolte.initialize({}); 5412 } 5413 /** 5414 * Remove PostHog and Divolte scripts (used on consent withdrawal). 5415 */ removeScripts() { 5416 this.window.luxuryPresence?.eventBus?.destroy?.(); 5417 this.window.luxuryPresence?.divolte?.destroy?.(); 5418 this.document.getElementById('posthog-script')?.remove(); 5419 this.document.getElementById('divolte-script')?.remove(); 5420 } 5421 /** 5422 * Initialize the consent controller for a given provider. 5423 * @param {{ provider: 'cookieyes' | 'lp-consent' }} options 5424 */ async initialize(options = {}) { 5425 const provider = $396d9f0ca7465335$var$PROVIDERS[options.provider]; 5426 if (!provider) return; 5427 this.eventName = provider.eventName; 5428 const isAuthenticated = await this.window.luxuryPresence.session.validateSession(); 5429 if (provider.hasAnalyticsConsent(this.document, this.window) || isAuthenticated) this.loadScript(); 5430 if (!this.consentListener) { 5431 this.consentListener = (eventData)=>{ 5432 if (provider.isAnalyticsAccepted(eventData.detail)) this.loadScript(); 5433 else this.removeScripts(); 5434 }; 5435 this.document.addEventListener(provider.eventName, this.consentListener); 5436 } 5437 } 5438} 5439 5440 5441/* global window */ /* eslint-disable import/prefer-default-export */ /** 5442 * Checks if the current site should redirect homeowner valuation submissions to HST. 5443 * Returns true and performs the redirect if all three gate conditions are met. 5444 * Returns false if the redirect did not fire (caller should continue with existing flow). 5445 * 5446 * @param {string} address - The human-readable address string from the form 5447 * @returns {boolean} 5448 */ function $0ae243a777665fd3$var$checkHstRedirect(address) { 5449 const { config: config } = window.luxuryPresence; 5450 if (config.hstValuationRedirectEnabled && config.mlsEnabled && config.myhomeDashboardEnabled) { 5451 window.location.href = `/home-search/home-valuation?address=${encodeURIComponent(address)}`; 5452 return true; 5453 } 5454 return false;
5455} 5456const $0ae243a777665fd3$export$4225ff17959c7313 = { 5457 checkHstRedirect: $0ae243a777665fd3$var$checkHstRedirect, 5458 destroy () {} 5459}; 5460 5461 5462const $fa0280e505379701$export$1fd2b467a3644d37 = new (0, $dc851cf3939b6ef5$export$ff0712c26d5458f7)({}); 5463const $fa0280e505379701$export$e12694e911a05f40 = new (0, $0ead03fdb95fa9ce$export$d507f25e63650365)($fa0280e505379701$export$1fd2b467a3644d37, window, document); 5464const $fa0280e505379701$export$3de614e59a842917 = new (0, $d48f9f944810f613$export$ca3218b7861b9cb0)($fa0280e505379701$export$1fd2b467a3644d37, window, document); 5465const $fa0280e505379701$export$fb9b298fdbf31455 = new (0, $68a2d4aa500c7a05$export$61ec8f2b6291ec31)($fa0280e505379701$export$1fd2b467a3644d37, window, document); 5466const $fa0280e505379701$export$178cc3119b453c2a = new (0, $e10bd00497be8876$export$3c5380623cf51249)($fa0280e505379701$export$1fd2b467a3644d37, window, document); 5467const $fa0280e505379701$export$9376af1a01686f2 = new (0, $ea6901fdf47e0ec1$export$3c19195edca3fa5c)($fa0280e505379701$export$1fd2b467a3644d37, window, document); 5468const $fa0280e505379701$export$94d36f806562393e = new (0, $0a0ad49f13c0b8fd$export$ba9afeb75f20f616)($fa0280e505379701$export$1fd2b467a3644d37, window, document); 5469const $fa0280e505379701$export$89e66d1faf94651e = new (0, $2b1790aa32f109d1$export$43e435c053a109f6)($fa0280e505379701$export$1fd2b467a3644d37, window, document); 5470const $fa0280e505379701$export$7743b07270cd6d6c = new (0, $4f184d9bd3037f15$export$7a6215890afba974)($fa0280e505379701$export$1fd2b467a3644d37, window, document); 5471const $fa0280e505379701$export$40a7039551318c37 = new (0, $102039871668a1df$export$e22e4b1be0bb6c61)($fa0280e505379701$export$1fd2b467a3644d37, window, document); 5472const $fa0280e505379701$export$8a0bbf7e7652fcfc = new (0, $0d25a3fa2e0f4f92$export$b718a981691eae5c)($fa0280e505379701$export$1fd2b467a3644d37, window, document); 5473const $fa0280e505379701$export$b313b3f6544c97a3 = new (0, $dcf72769f009ff26$export$b900d6fe921fd49c)($fa0280e505379701$export$1fd2b467a3644d37, window, document); 5474const $fa0280e505379701$export$b526bc4ac03268ef = new (0, $50eefeef8d1f2372$export$8a2bf06fbae4dde)($fa0280e505379701$export$1fd2b467a3644d37, window, document); 5475const $fa0280e505379701$export$c0affdb0a7a2ef6 = new (0, $396d9f0ca7465335$export$bb018224876724b9)($fa0280e505379701$export$1fd2b467a3644d37, window, document); 5476 5477 5478/** 5479 * Attach controller to window for usage 5480 * @param {string} key 5481 * @param {any} controller 5482 */ function $89a8a84b77163415$var$attachController(key, controller) { 5483 if (window.luxuryPresence[key]) try { 5484 // New version of sdk, destroy previous 5485 window.luxuryPresence[key].destroy(); 5486 } catch (e) { 5487 // eslint-disable-next-line no-console 5488 console.log(e); 5489 } 5490 window.luxuryPresence[key] = controller; 5491} 5492$89a8a84b77163415$var$attachController('config', (0, $fa0280e505379701$export$1fd2b467a3644d37)); 5493$89a8a84b77163415$var$attachController('modals', (0, $fa0280e505379701$export$e12694e911a05f40)); 5494$89a8a84b77163415$var$attachController('buttons', (0, $fa0280e505379701$export$3de614e59a842917)); 5495$89a8a84b77163415$var$attachController('validation', (0, $fa0280e505379701$export$fb9b298fdbf31455)); 5496$89a8a84b77163415$var$attachController('contactForms', (0, $fa0280e505379701$export$178cc3119b453c2a)); 5497$89a8a84b77163415$var$attachController('mapApiLoader', (0, $fa0280e505379701$export$9376af1a01686f2)); 5498$89a8a84b77163415$var$attachController('pagination', (0, $fa0280e505379701$export$94d36f806562393e)); 5499$89a8a84b77163415$var$attachController('expandSliders', (0, $fa0280e505379701$export$89e66d1faf94651e)); 5500$89a8a84b77163415$var$attachController('divolte', (0, $fa0280e505379701$export$7743b07270cd6d6c)); 5501$89a8a84b77163415$var$attachController('eventBus', (0, $fa0280e505379701$export$40a7039551318c37)); 5502$89a8a84b77163415$var$attachController('oauth', (0, $fa0280e505379701$export$8a0bbf7e7652fcfc)); 5503$89a8a84b77163415$var$attachController('qualifyingQuestionsModal', (0, $fa0280e505379701$export$b313b3f6544c97a3)); 5504$89a8a84b77163415$var$attachController('session', (0, $fa0280e505379701$export$b526bc4ac03268ef)); 5505$89a8a84b77163415$var$attachController('consent', (0, $fa0280e505379701$export$c0affdb0a7a2ef6)); 5506$89a8a84b77163415$var$attachController('redirects', (0, $0ae243a777665fd3$export$4225ff17959c7313)); 5507// methods for existing elements (can be removed later after elements updated) 5508window.luxuryPresence.formatSliderPriceRange = (0, $fa0280e505379701$export$94d36f806562393e).formatSliderPriceRange.bind((0, $fa0280e505379701$export$94d36f806562393e)); 5509window.luxuryPresence.formatSliderAreaRange = (0, $fa0280e505379701$export$94d36f806562393e).formatSliderAreaRange.bind((0, $fa0280e505379701$export$94d36f806562393e)); 5510// method below doesn't use 'this', but just to be safe in case of further method updates 5511window.luxuryPresence.getSearchFormValues = (0, $fa0280e505379701$export$94d36f806562393e).getSearchFormValues.bind((0, $fa0280e505379701$export$94d36f806562393e)); 5512window.luxuryPresence.handlebarsHelpersFactory = function(handlebarsInstance) { 5513 return (0, (/*@__PURE__*/$parcel$interopDefault($53de3c160faf5a81$exports
5513)))(handlebarsInstance, { 5514 isEqual (a, b) { 5515 // eslint-disable-next-line 5516 return a == b; 5517 } 5518 }); 5519}; 5520 5521 5522 5523})(); 5524//# sourceMappingURL=lp-sdk.js.map 5525 5526 </script>
5526 5527 5528
5528<script> 5529 var lpGlobalInitialized = false; 5530 5531 function lpGlobalInit () { 5532 if (lpGlobalInitialized) { 5533 return; 5534 } 5535 lpGlobalInitialized = true; 5536 // Initialize wow animation plugin only according to turbolinks 5537 var wow; 5538 if (window.WOW) { 5539 wow = new WOW({ 5540 animateClass: 'animated', 5541 offset: wowOffset, 5542 live: false, 5543 callback: function(box) {} 5544 }); 5545 } 5546 // Initialize handlebar helpers only once 5547 function handlebarsInit() { 5548 if (!window.Handlebars) { 5549 return; 5550 } 5551 HandlebarsIntl.registerWith(Handlebars); 5552 Handlebars.registerHelper(window.luxuryPresence.handlebarsHelpersFactory(Handlebars)); 5553 } 5554 5555 handlebarsInit(); 5556 5557 /* Initialize Luxury Presence SDK */ 5558 try { 5559 window.luxuryPresence.config.setCompany("c3076ecb-c2d7-457b-a426-bcf036133f1f", {}); 5560 window.luxuryPresence.config.setWebsite("ff06f655-0c64-45c8-9c55-e8468b4e5303", { 5561 myhomeDashboardEnabled: true, 5562 mlsEnabled: false, 5563 hstValuationRedirectEnabled: true 5564 }); 5565 const url = new URL(window.location.href); 5566 const shareId = url.searchParams.get('shareId'); 5567 window.luxuryPresence.config.setPage("ead1baef-d746-483e-a3f1-5e31d5204d97", { 5568 sourceResource: "", 5569 pageElementId: "", 5570 pageQueryVariables: {}, 5571 shareId: "", 5572 }); 5573 window.luxuryPresence.config.setWebsiteApiGatewayUrl("https://wgw.luxurypresence.com", {}); 5574 window.luxuryPresence.config.setBuyerSellerServiceUrl("https://bss.luxurypresence.com", {}); 5575 window.luxuryPresence.config.setDivolteServiceUrl("https://t.luxurypresence.com", {}); 5576 window.luxuryPresence.config.setEventBusUrl("https://cdn.luxurypresence.com/scripts/event-bus-js", {}); 5577 window.luxuryPresence.config.setEventBus({ 5578 enabled: true, 5579 apiKey: "phc_PMYT31xlmXBaTCwpIRx1v6RrpLVZIuP47N3mOT6KLnr", 5580 url: "https://p.luxurypresence.com", 5581 }); 5582 window.luxuryPresence.config.setTemplateId("0c2b9641-6f2a-40f2-a2e0-dabc022eb278", {}); 5583 window.luxuryPresence.config.setLeadIngesterUrl("https://leads.luxurypresence.com", {}); 5584 window.luxuryPresence.buttons.initialize({}); 5585 window.luxuryPresence.eventBus.initialize({}); 5586 window.luxuryPresence.contactForms.initialize({}); 5587 window.luxuryPresence.validation.initialize({}); 5588 window.luxuryPresence.expandSliders.initialize({}); 5589 window.luxuryPresence.oauth.initialize({ googleSignOnEnabled: true, iframe: false }); 5590 window.luxuryPresence.qualifyingQuestionsModal.initialize({ qualifyingQuestionsModalEnabled: true }); 5591 window.luxuryPresence.divolte.initialize({}); 5592 } catch (e) { 5593 console.log(e); 5594 } 5595 5596 /* Inject Section Specific Javascript */ 5597 try { 5598(function init(sectionDomId) { 5599 const disclaimerLabel = document.querySelector('#' + sectionDomId + ' .lp-tcr-content'); 5600 if (disclaimerLabel) { 5601 const input = disclaimerLabel.querySelector('input'); 5602 if (input) { 5603 input.setAttribute('id', `${sectionDomId}-termsAccepted`); 5604 input.value = 'true'; 5605 const label = document.createElement('label'); 5606 label.setAttribute('for', `${sectionDomId}-termsAccepted`); 5607 label.classList.add('lp-visually-hidden'); 5608 label.textContent = 'Opt in/Disclaimer Consent:'; 5609 disclaimerLabel.prepend(label); 5610 } 5611 } 5612 function setHttp(link) { 5613 if (link.search(/^http[s]?\:\/\/|\//) == -1) { 5614 link = 'https://' + link; 5615 } 5616 return link; 5617 } 5618 const socialLinks = document.querySelectorAll('#' + sectionDomId + ' .socials__link, #' + sectionDomId + ' .lp-socials__link'); 5619 socialLinks.forEach(linkElement => { 5620 const href = linkElement.getAttribute('href'); 5621 if (href) { 5622 linkElement.setAttribute('href', setHttp(href)); 5623 } 5624 }); 5625 const redirectLink = $('#' + sectionDomId + ' .redirect-link'); 5626 if (redirectLink.length) { 5627 const rawHref = setHttp(redirectLink.attr('href')); 5628 let formEvent; 5629 if (rawHref) { 5630 formEvent = redirectLink.data('redirect') ? 'formsubmitted' : 'reset'; 5631 $('#' + sectionDomId + ' .contact-form').find('form').on(formEvent, function () { 5632 redirectLink.attr('href', rawHref); 5633 redirectLink[0].click(); 5634 }); 5635 } 5636 } 5637})('modal-global-contact-us'); 5638} catch(e) { 5639console.log(e); 5640} 5641 5642 try { 5643(function init(sectionDomId) { 5644 const $section = $('#' + sectionDomId); 5645 const $hamburger = $section.find('.hamburger-component'); 5646 function onSideMenuShow() { 5647 if (!$hamburger.hasClass('active')) { 5648 $hamburger.addClass('active'); 5649 } 5650 } 5651 function onSideMenuHide() { 5652 $hamburger.removeClass('active'); 5653 } 5654 const $window = $(window); 5655 $window.on('lpui-sidemenu-show.lpui-auto-clean', onSideMenuShow); 5656 $window.on('lpui-sidemenu-hide.lpui-auto-clean', onSideMenuHide); 5657 const $subContainers = $section.find('.navigation__item.sub-nav-container'); 5658 $subContainers.hover(function () { 5659 $(this).find('.sub-nav').addClass('visible'); 5660 }, function () { 5661 $(this).find('.sub-nav').removeClass('visible'); 5662 }); 5663 $window.on('click.lpui-auto-clean', function () { 5664 $('.sub-nav-container.visible-dropdown').removeClass('visible-dropdown'); 5665 }); 5666 let fixedWidth = 0; 5667 const rightPad = 20; 5668 function fixSubsPosition() { 5669 const wWidth = window.innerWidth; 5670 if (innerWidth === fixedWidth) { 5671 return; 5672 } 5673 fixedWidth = wWidth; 5674 $subContainers.each(function () { 5675 const $sub = $(this).find('.sub-nav'); 5676 $sub[0].style.right = '';
5677 const sRect = $sub[0].getBoundingClientRect(); 5678 const sRight = sRect.left + sRect.width; 5679 if (sRight > wWidth - rightPad) { 5680 const parentRect = $sub[0].offsetParent.getBoundingClientRect(); 5681 const parentRight = parentRect.left + parentRect.width; 5682 $sub[0].style.right = `${parentRight - wWidth + rightPad}px`; 5683 } 5684 }); 5685 } 5686 var debouncer; 5687 $window.on('resize.lpui-auto-clean', function () { 5688 clearTimeout(debouncer); 5689 debouncer = setTimeout(fixSubsPosition, 500); 5690 }); 5691 fixSubsPosition(); 5692 if ($('.property-intro').length || $('.property-intro-2').length) { 5693 $('nav').addClass('dark-opening'); 5694 $("#global-navbar").css({ 5695 "background-color": "rgb(26, 26, 26)", 5696 "color": "rgb(255, 255, 255)" 5697 }); 5698 } 5699 function initAutoTruncateMenu() { 5700 const $navbar = $section.find('.navbar'); 5701 const $navList = $section.find('.navigation > ul'); 5702 if (!$navbar.length || !$navList.length) { 5703 return; 5704 } 5705 const $logo = $section.find('.logo'); 5706 const $buttonsWrapper = $section.find('.buttons-wrapper'); 5707 const $allItems = $navList.children('.navigation__item'); 5708 const $phoneItem = $allItems.filter(function () { 5709 return $(this).find('a[href^="tel:"]').length > 0; 5710 }); 5711 const $menuItems = $allItems.not($phoneItem); 5712 if (!$menuItems.length) { 5713 return; 5714 } 5715 const buffer = 8; 5716 function measureFixedWidth() { 5717 let width = buffer; 5718 if ($logo.length) { 5719 width += $logo.outerWidth(true) || 0; 5720 } 5721 if ($buttonsWrapper.length) { 5722 width += $buttonsWrapper.outerWidth(true) || 0; 5723 } 5724 if ($phoneItem.length) { 5725 width += $phoneItem.outerWidth(true) || 0; 5726 } 5727 return width; 5728 } 5729 function truncateMenu() { 5730 if (!$navList.is(':visible')) { 5731 return; 5732 } 5733 $menuItems.css('display', ''); 5734 const availableWidth = $navbar[0].getBoundingClientRect().width - measureFixedWidth(); 5735 let widthSoFar = 0; 5736 let overflowed = false; 5737 $menuItems.each(function () { 5738 const $item = $(this); 5739 if (overflowed) { 5740 $item.css('display', 'none'); 5741 return; 5742 } 5743 widthSoFar += $item.outerWidth(true); 5744 if (widthSoFar > availableWidth) { 5745 $item.css('display', 'none'); 5746 overflowed = true; 5747 } 5748 }); 5749 } 5750 let truncateFrame; 5751 function scheduleTruncate() { 5752 if (truncateFrame) { 5753 return; 5754 } 5755 truncateFrame = window.requestAnimationFrame(function () { 5756 truncateFrame = null; 5757 truncateMenu(); 5758 }); 5759 } 5760 scheduleTruncate(); 5761 if (window.ResizeObserver) { 5762 const resizeObserver = new ResizeObserver(scheduleTruncate); 5763 resizeObserver.observe($navbar[0]); 5764 } else { 5765 $(window).on('resize.lpui-auto-truncate', scheduleTruncate); 5766 } 5767 $section.find('img').on('load.lpui-auto-truncate', scheduleTruncate); 5768 $(window).on('load.lpui-auto-truncate', scheduleTruncate); 5769 if (document.fonts && document.fonts.ready) { 5770 document.fonts.ready.then(scheduleTruncate); 5771 } 5772 } 5773 initAutoTruncateMenu(); 5774})('global-navbar'); 5775} catch(e) { 5776console.log(e); 5777} 5778 try { 5779(function init(sectionDomId, options) { 5780 var elementSelector = options.elementSelector || 'nav'; 5781 var element = document.querySelector(elementSelector); 5782 var sections = document.querySelectorAll('body > section'); 5783 var subNavContainers = element.querySelectorAll('.sub-nav'); 5784 var initialBackgroundColor = options.initialBackgroundColor || element.style.backgroundColor; 5785 var backgroundColorScroll = options.backgroundColorScroll || '#191919'; 5786 var fontColor = options.fontColor || '#fff'; 5787 var fontColorScroll = options.fontColorScroll || '#000'; 5788 var didScroll = false; 5789 var lastScrollTop = 0; 5790 var delta = 5; 5791 5792 // eslint-disable-next-line sonarjs/no-collapsible-if 5793 if (wow) { 5794 // Wowjs might not trigger for position fixed elements 5795 if (window.scrollY > 0) { 5796 element.querySelectorAll('.wow').forEach(el => wow.show(el)); 5797 } 5798 } 5799 function adjustNavPinState() { 5800 // Remove 'visible' class from all subNavContainers 5801 for (let i = 0; i < subNavContainers.length; i++) { 5802 subNavContainers[i].classList.remove('visible'); 5803 } 5804 if (window.scrollY > 0) { 5805 element.style.backgroundColor = backgroundColorScroll; 5806 element.style.color = fontColorScroll; 5807 element.classList.add('scroll'); 5808 } else {
5809 element.style.backgroundColor = initialBackgroundColor; 5810 element.style.color = fontColor; 5811 element.classList.remove('scroll'); 5812 } 5813 } 5814 adjustNavPinState(); 5815 function onScroll() { 5816 didScroll = true; 5817 } 5818 window.addEventListener('scroll', onScroll); 5819 5820 // eslint-disable-next-line 5821 var debounceInterval = setInterval(() => { 5822 if (didScroll) { 5823 // eslint-disable-next-line 5824 hasScrolled(); 5825 didScroll = false; 5826 } 5827 }, 250); 5828 function hasScrolled() { 5829 adjustNavPinState(); 5830 // eslint-disable-next-line 5831 var navbarHeight = element.offsetHeight; 5832 // eslint-disable-next-line 5833 var st = window.scrollY; 5834 5835 // Make sure they scroll more than delta 5836 if (Math.abs(lastScrollTop - st) <= delta) { 5837 return; 5838 } 5839 if (st > lastScrollTop && st > navbarHeight) { 5840 // Scroll Down 5841 element.style.transform = `translateY(-${navbarHeight}px) scaleY(0)`; 5842 document.documentElement.style.setProperty('--global-header-height', '0px'); 5843 5844 // Some sections "follow" the navbar with class sticky (i.e. Producer search) 5845 sections.forEach(section => { 5846 if (section.classList.contains('sticky')) { 5847 section.style.top = '0px'; 5848 } 5849 }); 5850 } else { 5851 // Scroll Up 5852 // eslint-disable-next-line 5853 if (st + window.innerHeight < document.documentElement.scrollHeight) { 5854 element.style.transform = `translateY(0) scaleY(1)`; 5855 document.documentElement.style.setProperty('--global-header-height', `${navbarHeight}px`); 5856 sections.forEach(section => { 5857 if (section.classList.contains('sticky')) { 5858 section.style.top = `${navbarHeight}px`; 5859 } 5860 }); 5861 } 5862 } 5863 lastScrollTop = st; 5864 } 5865})('NAVBAR', JSON.parse("{\"fontColor\":\"#fff\",\"elementSelector\":\"nav\",\"fontColorScroll\":\"#fff\",\"initialFontColor\":\"#fff\",\"backgroundColorScroll\":\"#1a1a1a\",\"initialBackgroundColor\":\"rgba(0,0,0,0)\"}")); 5866} catch(e) { 5867console.log(e); 5868} 5869 try { 5870(function init(sectionDomId) { 5871 var $sidemenu = $('#' + sectionDomId); 5872 var shown = false; 5873 function onShow() { 5874 if (shown) { 5875 return; 5876 } 5877 shown = true; 5878 window.lpUI.requestScrollLock(sectionDomId); 5879 } 5880 function onHide() { 5881 if (!shown) { 5882 return; 5883 } 5884 shown = false; 5885 window.lpUI.releaseScrollLock(sectionDomId); 5886 $('body').removeClass('locked'); 5887 $('.hamburger.active').removeClass('active'); 5888 } 5889 $sidemenu.on('lpui-show', onShow); 5890 $sidemenu.on('lpui-hide', onHide); 5891 $sidemenu.find('a:not(.sub-nav-heading)').click(function () { 5892 onHide(); 5893 window.lpUI.sideMenuHide(true); 5894 }); 5895})('global-sidemenu'); 5896} catch(e) { 5897console.log(e); 5898} 5899 5900 try { 5901(function init(sectionDomId) { 5902 function getNums(str) { 5903 return str.match(/^rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*(\d+))?\)$/); 5904 } 5905 function getLightness(rgbaString) { 5906 let r = getNums(rgbaString)[1]; 5907 let g = getNums(rgbaString)[2]; 5908 let b = getNums(rgbaString)[3]; 5909 r /= 255; 5910 g /= 255; 5911 b /= 255; 5912 let cmin = Math.min(r, g, b), 5913 cmax = Math.max(r, g, b), 5914 delta = cmax - cmin, 5915 l = 0; 5916 l = (cmax + cmin) / 2; 5917 l = +(l * 100).toFixed(1); 5918 return l; 5919 } 5920 const $footer = $('.footer'); 5921 let footerBackgroundColor = $('footer').css('background-color'); 5922 if (getLightness(footerBackgroundColor) >= 50) { 5923 $footer.addClass('light'); 5924 } else { 5925 $footer.addClass('dark'); 5926 } 5927 var $footerWrap = $('.footer').closest("footer"); 5928 if ($footerWrap.length === 0) { 5929 $footerWrap = $('.footer').closest("body"); 5930 } 5931 var footerWrapBackgroundColor = $footerWrap.css('background-color'); 5932 var footerColor = $footerWrap.css('color'); 5933 $footerWrap.css({ 5934 "--fontColor": footerColor, 5935 "--bgColor": footerWrapBackgroundColor 5936 }); 5937 const form = $("#" + sectionDomId).find("form"); 5938 const redirectLink = $('#' + sectionDomId + ' .redirect-link'); 5939 form.on("reset", function () { 5940 redirectPage(redirectLink); 5941 }); 5942 function redirectPage(link) { 5943 if (link.length) { 5944 const rawHref = link.attr('href'); 5945 link.attr('href', setHttp(rawHref)); 5946 link[0].click(); 5947 } 5948 } 5949 function setHttp(link) { 5950 if (link.search(/^http[s]?\:\/\/|\//) == -1) { 5951 link = 'http://' + link; 5952 } 5953 return link; 5954 } 5955})('global-footer'); 5956} catch(e) { 5957console.log(e); 5958} 5959 5960 try { 5961(function init(sectionDomId) { 5962 var _section = $('#' + sectionDomId); 5963 var _footer = document.getElementById('global-footer'); 5964 const redirectLink = _section.find('.js-redirect-link'); 5965 function setHttp(link) { 5966 if (link.search(/^(http[s]?\:\/\/|\/)/) == -1) { 5967 link = 'https://' + link; 5968 } 5969 return link; 5970 } 5971 function redirectPage(link) { 5972 if (link.length) { 5973 window.location.href = setHttp(link.attr('href')); 5974 } 5975 } 5976 const emailInput = _section.find('.js-email-input'); 5977 const emailRegex = /[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~\-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)+/; 5978 emailInput.on('input', function () { 5979 if (emailRegex.test(this.value)) { 5980 this.setCustomValidity(''); 5981 } else { 5982 this.setCustomValidity('Please enter a valid email address.'); 5983 } 5984 this.reportValidity(); 5985 }); 5986 _section.find('input[name="name"]').on("keyup", function () { 5987 if (this.value.replaceAll(/\d/g, "").trim().split(" ").length < 2 || this.value.replaceAll(/\d/g, "").trim() === "") { 5988 this.setCustomValidity("Please input your first name and last name."); 5989 } else { 5990 this.setCustomValidity(""); 5991 } 5992 }); 5993 _section.find('form').on("reset", function () { 5994 $(this).addClass("show-success"); 5995 redirectPage(redirectLink); 5996 }); 5997 $(window).scroll(function () { 5998 if ($(this).scrollTop() >= 150) _section.find('.btn-holder').addClass('visible');else _section.find('.btn-holder').removeClass('visible'); 5999 }); 6000 _section.find('.close-btn, .connect-btn').click(function () { 6001 _section.find('.pop-up').toggle(); 6002 _section.find('.btn-holder').toggle(); 6003 }); 6004 if (!_footer) return; 6005 const observer = new IntersectionObserver(entries => {
6006 entries.forEach(entry => { 6007 if (entry.isIntersecting) _section.hide();else _section.show(); 6008 }); 6009 }, { 6010 threshold: 0.1 6011 }); 6012 observer.observe(_footer); 6013 function selectToDropdown(selectItem) { 6014 let customDropdown = ` 6015 <div class="custom-select" role="presentation"> 6016 <button class="custom-select-toggle is-placeholder" type="button" aria-haspopup="true" aria-expanded="false"></button> 6017 <div class="custom-select-menu" style="display: none;max-height: none;"> 6018 <div class="custom-select-content"> 6019 </div> 6020 </div> 6021 </div>`; 6022 $(customDropdown).insertAfter(selectItem); 6023 let $dropdown = $($(selectItem).next()); 6024 let $dropdownToggle = $dropdown.find(".custom-select-toggle"); 6025 let $dropdownMenu = $dropdown.find(".custom-select-menu"); 6026 let $dropdownContent = $dropdown.find(".custom-select-content"); 6027 $dropdownToggle.addClass($(selectItem).attr("class")).removeClass("dropdown-select"); 6028 $dropdownToggle.attr("id", $(selectItem).attr("id") + "custom"); 6029 $dropdownMenu.attr("aria-labelledby", $dropdownToggle.attr("id")); 6030 appendOptionButtons(selectItem, $dropdownContent); 6031 $(selectItem).removeClass().addClass("sr-only"); 6032 var toggleText = $(selectItem).find(":selected").first().text() || $(selectItem).children().first().text(); 6033 $dropdownToggle.html(toggleText); 6034 $dropdown.on("click", ".custom-select-item", function (e) { 6035 if ($(this).hasClass("is-disabled")) return; 6036 let elementNumber = $(this).index(); 6037 let $originalSelect = $(this).closest(".custom-select").siblings("select"); 6038 let $linkedOption = $originalSelect.find("option").eq(elementNumber); 6039 if (!$linkedOption.prop("selected")) { 6040 $linkedOption.prop("selected", true); 6041 $originalSelect.change(); 6042 $(this).addClass("is-selected").siblings(".custom-select-item").removeClass("is-selected"); 6043 $dropdownToggle.html($(this).html()); 6044 closeSelectMenu($(this).closest(".custom-select-menu")); 6045 } 6046 $dropdownToggle.removeClass("is-placeholder"); 6047 }); 6048 function convertOptionToButton(option) { 6049 var optionText = $(option).text(); 6050 var optionValue = $(option).attr("value") || $(option).text(); 6051 var optionIsDisabled = $(option).prop("disabled"); 6052 var outputButton = `<button value="${optionValue}" class="custom-select-item" type="button" ${optionIsDisabled ? "disabled" : ""}>${optionText}</button>`; 6053 return outputButton; 6054 } 6055 function appendOptionButtons(selectItem, appendTarget) { 6056 $(selectItem).find("option").each(function () { 6057 let currentButton = convertOptionToButton(this); 6058 $(appendTarget).append(currentButton); 6059 }); 6060 } 6061 function showSelectMenu(selectMenu) { 6062 var customSelect = $(selectMenu).closest(".custom-select")[0]; 6063 $(customSelect).addClass("open"); 6064 $(selectMenu).slideDown(); 6065 } 6066 function closeSelectMenu(selectMenu) { 6067 var customSelect = $(selectMenu).closest(".custom-select")[0]; 6068 $(customSelect).removeClass("open"); 6069 $(selectMenu).slideUp(); 6070 } 6071 $dropdown.on("click", ".custom-select-toggle", function (e) { 6072 var $customSelect = $(this).closest(".custom-select"); 6073 var selectMenu = $customSelect.find(".custom-select-menu")[0]; 6074 if ($customSelect.hasClass("open")) { 6075 closeSelectMenu(selectMenu); 6076 } else { 6077 showSelectMenu(selectMenu); 6078 } 6079 }); 6080 $(document).on("click", function (e) { 6081 if ($(e.target).closest(".custom-select").length < 1 && $(".custom-select").hasClass("open")) { 6082 $(".custom-select.open").each(function () { 6083 var selectMenu = $(this).find(".custom-select-menu")[0]; 6084 closeSelectMenu(selectMenu); 6085 }); 6086 } 6087 }); 6088 } 6089 $(".pop-up select[name='interest']").each(function () { 6090 selectToDropdown(this); 6091 }); 6092})('global-mobile-contact'); 6093} catch(e) { 6094console.log(e); 6095} 6096 6097 try { 6098(function init(sectionDomId) { 6099 const $section = $('#' + sectionDomId); 6100 const $form = $section.find('.custom-form__download-form'); 6101 const redirectLink = $section.find('.js-redirect-link'); 6102 function redirectPage(link) { 6103 if (link.length) { 6104 const rawHref = link.attr('href'); 6105 link.attr('href', setHttp(rawHref)); 6106 link[0].click(); 6107 } 6108 } 6109 function setHttp(link) { 6110 if (link.search(/^http[s]?\:\/\/|\//) == -1) { 6111 link = 'https://' + link; 6112 } 6113 return link; 6114 } 6115 $form.on('reset', function () { 6116 redirectPage(redirectLink); 6117 }); 6118 var backupEmailField = $section.find('.js-backup-email'); 6119 if (backupEmailField) { 6120 var dynEmailField = $section.find('input.js-form-element[name="email"]:not(.js-backup-field)'); 6121 if (dynEmailField.length === 0) { 6122 backupEmailField.removeClass('js-backup-email'); 6123 } else { 6124 backupEmailField.remove(); 6125 } 6126 } 6127 var $allElements = $section.find('.js-form-element'); 6128 const putAllInMessage = false;
6129 var hasCustomFields = false; 6130 const standardFields = { 6131 'name': true, 6132 'email': true, 6133 'phone': true, 6134 'message': true, 6135 'interest': true 6136 }; 6137 const busyFormNames = { 6138 'address': true, 6139 'city': true, 6140 'state': true, 6141 'postalCode': true, 6142 'mlsId': true, 6143 'sourceUrl': true 6144 }; 6145 $allElements.each(function () { 6146 var $el = $(this); 6147 var name = this.hasAttribute('data-name') ? $el.attr('data-name') : $el.attr('name'); 6148 if (!name) { 6149 return; 6150 } 6151 if (!standardFields[name]) { 6152 hasCustomFields = true; 6153 } 6154 let newName = /[^a-z\d-_]/.test(name) ? name.trim().toLowerCase().replace(/[^a-z\d-_]/g, '-') : name; 6155 if (!/^[a-z]/.test(newName)) { 6156 newName = 'fld-' + newName; 6157 } 6158 if (busyFormNames[newName]) { 6159 let idx = 1; 6160 while (busyFormNames[`${newName}-${idx}`] && idx < 100) { 6161 idx++; 6162 } 6163 newName = `${newName}-${idx}`; 6164 } 6165 busyFormNames[newName] = $el; 6166 if (name !== newName) { 6167 $el.attr('name', newName); 6168 } 6169 }); 6170 let $customFields = []; 6171 if (putAllInMessage && hasCustomFields) { 6172 $allElements.each(function () { 6173 var name = this.getAttribute('name'); 6174 if (name && (!standardFields[name] || name === 'message' && !$(this).hasClass('js-composed-message'))) { 6175 this.setAttribute('data-name', name); 6176 this.removeAttribute('name'); 6177 } 6178 if (this.hasAttribute('data-name')) { 6179 $customFields.push($(this)); 6180 } 6181 }); 6182 if (!$form[0].elements['message']) { 6183 $('<input type="hidden" name="message" class="js-composed-message" value="">').appendTo($form); 6184 } 6185 } 6186 $selects = $section.find('select.js-select'); 6187 var $checkboxGroups = $section.find('.js-checkboxes-group'); 6188 if ($checkboxGroups.length) { 6189 $checkboxGroups.each(function () { 6190 var $group = $(this); 6191 if (!$group.data('required')) return; 6192 var $checkboxes = $group.find('.js-multi-checkbox'); 6193 function updateValidity() { 6194 var anyChecked = $group.find('.js-multi-checkbox:checked').length > 0; 6195 $checkboxes.each(function () { 6196 this.setCustomValidity(anyChecked ? '' : 'Please select at least one option.'); 6197 }); 6198 } 6199 updateValidity(); 6200 $checkboxes.on('change', updateValidity); 6201 }); 6202 $form.on('submit', function () { 6203 $checkboxGroups.each(function () { 6204 var $group = $(this); 6205 var name = $group.data('name'); 6206 if (!name) return; 6207 var checked = $group.find('.js-multi-checkbox:checked').map(function () { 6208 return this.value; 6209 }).get().join(', '); 6210 $form.find('input[type="hidden"][data-checkbox-group="' + name + '"]').remove(); 6211 $('<input type="hidden">').attr('name', name).val(checked).attr('data-checkbox-group', name).appendTo($form); 6212 var $groupCheckboxes = $group.find('.js-multi-checkbox'); 6213 $groupCheckboxes.prop('disabled', true); 6214 setTimeout(function () { 6215 $groupCheckboxes.prop('disabled', false); 6216 }, 0); 6217 }); 6218 }); 6219 } 6220 let selectsInitialized = false; 6221 function initializeSelects() { 6222 if (selectsInitialized) { 6223 return; 6224 } 6225 selectsInitialized = true; 6226 $selects.each(function () { 6227 if (this.id) { 6228 $label = $section.find('[for="' + this.id + '"]'); 6229 if ($label.length) { 6230 var rect = $label[0].getBoundingClientRect(); 6231 if (rect && rect.width < 30) { 6232 var firstOption = $(this).find('option').first(); 6233 firstOption.attr('label', firstOption.data('label')); 6234 } 6235 } 6236 $gr = $(this).closest('.lp-input-group'); 6237 var b = $gr.css('border-left-width'); 6238 if (b && b !== '0px') { 6239 $gr[0].style.setProperty('--uf-border-left', b); 6240 } 6241 b = $gr.css('border-right-width'); 6242 if (b && b !== '0px') { 6243 $gr[0].style.setProperty('--uf-border-right', b); 6244 } 6245 } 6246 }); 6247 var csPrefix = 'uf-cus-sel'; 6248 function convertSelectToDropdown(selectItem) { 6249 const $selectItem = $(selectItem); 6250 const selectId = $selectItem.attr('id'); 6251 const id = $selectItem.attr('id') + '-custom-select'; 6252 let $dropdown = $selectItem.next(); 6253 let initialized = true; 6254 if ($dropdown.attr('id') !== id) { 6255 $selectItem.addClass("sr-only"); 6256 initialized = false;
6257 let customDropdown = ` 6258 <div class="${csPrefix}" role="presentation"> 6259 <button class="${csPrefix}-toggle" type="button" aria-haspopup="true" aria-expanded="false"></button> 6260 <div class="${csPrefix}-menu"> 6261 <div class="${csPrefix}-content"> 6262 </div> 6263 </div> 6264 </div>`; 6265 $(customDropdown).insertAfter(selectItem); 6266 $dropdown = $selectItem.next(); 6267 $dropdown.attr('id', id); 6268 } 6269 let $dropdownToggle = $dropdown.find('.' + csPrefix + '-toggle'); 6270 let $dropdownMenu = $dropdown.find('.' + csPrefix + '-menu'); 6271 let $dropdownContent = $dropdown.find('.' + csPrefix + '-content'); 6272 if (!initialized) { 6273 $dropdownToggle.addClass($selectItem.attr("class")).removeClass(['dropdown-select', 'js-select', 'js-form-element', 'sr-only']); 6274 $dropdownToggle.attr("id", id + '-toggle'); 6275 $dropdownMenu.attr("aria-labelledby", $dropdownToggle.attr("id")); 6276 appendOptionButtons(selectItem, $dropdownContent); 6277 } 6278 $selectItem.on("focus", function () { 6279 showSelectMenu($dropdownMenu[0]); 6280 $dropdownToggle.focus(); 6281 }); 6282 function setInitialValue() { 6283 $dropdownMenu.find('.' + csPrefix + '-item').removeClass('is-selected'); 6284 var $selectedOption = $selectItem.find(':selected').first(); 6285 if (!$selectedOption.length) { 6286 $selectedOption = $selectItem.children().first(); 6287 } 6288 var defaultText = ' '; 6289 var isPlaceholder = true; 6290 if ($selectedOption.length) { 6291 defaultText = ($selectedOption[0].hasAttribute('label') ? $selectedOption.attr('label') : $selectedOption.text()) || ' '; 6292 isPlaceholder = !!$selectedOption.prop("disabled"); 6293 } 6294 $dropdownToggle.html(defaultText); 6295 if (isPlaceholder) { 6296 $dropdownToggle.addClass('is-placeholder'); 6297 } 6298 } 6299 setInitialValue(); 6300 $dropdown.on("click", '.' + csPrefix + '-item', function (e) { 6301 const $button = $(this); 6302 if ($button.hasClass("is-disabled")) return; 6303 let elementNumber = $button.index(); 6304 let $linkedOption = $selectItem.find("option").eq(elementNumber); 6305 if (!$linkedOption.prop("selected")) { 6306 $linkedOption.prop("selected", true); 6307 $selectItem.change(); 6308 $button.addClass("is-selected").siblings('.' + csPrefix + '-item').removeClass("is-selected"); 6309 $dropdownToggle.removeClass("is-placeholder").html($button.html()); 6310 closeSelectMenu($button.closest('.' + csPrefix + '-menu')); 6311 } 6312 }); 6313 $(selectItem.form).on('reset', function () { 6314 setTimeout(function () { 6315 setInitialValue(); 6316 }); 6317 }); 6318 function convertOptionToButton(option) { 6319 const $option = $(option); 6320 var optionText = $option.text(); 6321 var optionValue = $option.attr("value"); 6322 var optionIsDisabled = $option.prop("disabled"); 6323 var outputButton = `<button value="${optionValue}" class="${csPrefix}-item" type="button" ${optionIsDisabled ? "disabled" : ""}>${optionText}</button>`; 6324 return outputButton; 6325 } 6326 function appendOptionButtons(selectItem, appendTarget) { 6327 const $appendTarget = $(appendTarget); 6328 $(selectItem).find("option").each(function () { 6329 let currentButton = convertOptionToButton(this); 6330 $appendTarget.append(currentButton); 6331 }); 6332 } 6333 function showSelectMenu(selectMenu) { 6334 var $customSelect = $(selectMenu).closest('.' + csPrefix); 6335 $customSelect.addClass("open"); 6336 $(selectMenu).slideDown(); 6337 } 6338 function closeSelectMenu(selectMenu) { 6339 var $customSelect = $(selectMenu).closest('.' + csPrefix); 6340 $customSelect.removeClass("open"); 6341 $(selectMenu).slideUp(); 6342 } 6343 $dropdown.on("click", '.' + csPrefix + '-toggle', function (e) { 6344 var $customSelect = $(this).closest('.' + csPrefix); 6345 var selectMenu = $customSelect.find('.' + csPrefix + '-menu')[0]; 6346 if ($customSelect.hasClass("open")) { 6347 closeSelectMenu(selectMenu); 6348 } else { 6349 showSelectMenu(selectMenu); 6350 } 6351 }); 6352 $(document.body).on("click", function (e) { 6353 var $closestCustomSelect = $(e.target).closest('.lp-input-group').find('.' + csPrefix); 6354 $section.find('.' + csPrefix + '.open').not($closestCustomSelect).each(function () { 6355 closeSelectMenu($(this).find('.' + csPrefix + '-menu')[0]); 6356 }); 6357 }); 6358 } 6359 $selects.each(function () { 6360 convertSelectToDropdown(this); 6361 }); 6362 if (putAllInMessage && hasCustomFields) { 6363 $allElements.on('change', function () { 6364 if (!this.hasAttribute('data-name')) { 6365 return; 6366 } 6367 var message = ''; 6368 var customMessages = []; 6369 $customFields.forEach(function ($el) { 6370 var value = $el.val(); 6371 if (!value) { 6372 return; 6373 } 6374 if ($el.attr('data-name') === 'message') { 6375 message = value; 6376 return; 6377 } 6378 var label = $section.find(`label[for='${$el[0].id}']`).text(); 6379 customMessages.push(label + ': ' + ($el[0].tagName === 'TEXTAREA' ? '\n' : '') + value); 6380 }); 6381 if (customMessages.length) { 6382 if (message) { 6383 customMessages.push('Message: \n' + message); 6384 } 6385 $form[0].elements['message'].value = customMessages.join('\n'); 6386 } else { 6387 $form[0].elements['message'].value = message; 6388 } 6389 }); 6390 } 6391 } 6392 function initModalColors() { 6393 try { 6394 if (!$section.css('--bgColor')) { 6395 var colorStr = $section.css('background-color'); 6396 var bgColor = makeColor(colorStr); 6397 var isDark = (bgColor.rgba.r * 299 + bgColor.rgba.g * 587 + bgColor.rgba.b * 114) / 1000 < 128; 6398 $section[0].style.setProperty('--bgColor', colorStr); 6399 $section[0].style.setProperty('--bgColor_H', bgColor.hsla.h + 'deg'); 6400 $section[0].style.setProperty('--bgColor_S', bgColor.hsla.s + '%'); 6401 $section[0].style.setProperty('--bgColor_L', bgColor.hsla.l + '%'); 6402 $section[0].style.setProperty('--bgColor_A', bgColor.hsla.a); 6403 $section[0].style.setProperty('--bgColor_darkenDir', isDark ? 1 : -1); 6404 $section.addClass('is-background-color-' + (isDark ? 'dark' : 'light')); 6405 } 6406 } catch (e) {} 6407 } 6408 function makeColor(str) { 6409 var color = { 6410 rgba: { 6411 r: 0, 6412 g: 0, 6413 b: 0, 6414 a: 0 6415 } 6416 }; 6417 if (str && str.match) { 6418 var m = str.match(/rgba?\s*\((\d+)\s*,\s*(\d+)\s*,\s*(\d+)(?:\s*,\s*([\.0-9]+))?\)/); 6419 if (m) { 6420 color.rgba.r = parseInt(m[1]); 6421 color.rgba.g = parseInt(m[2]); 6422 color.rgba.b = parseInt(m[3]); 6423 color.rgba.a = parseFloat(m[4]); 6424 if (isNaN(color.rgba.a)) { 6425 color.rgba.a = 1; 6426 } 6427 } 6428 } 6429 color.hsla = makeHSLA(color.rgba); 6430 return color; 6431 } 6432 function makeHSLA(rgba) { 6433 var r = rgba.r / 255, 6434 g = rgba.g / 255, 6435 b = rgba.b / 255, 6436 a = rgba.a; 6437 var min = Math.min(r, g, b), 6438 max = Math.max(r, g, b), 6439 l = (max + min) / 2, 6440 d = max - min, 6441 h, 6442 s; 6443 switch (max) { 6444 case min: 6445 h = 0; 6446 break; 6447 case r: 6448 h = 60 * (g - b) / d; 6449 break; 6450 case g: 6451 h = 60 * (b - r) / d + 120; 6452 break; 6453 case b: 6454 h = 60 * (r - g) / d + 240; 6455 break; 6456 } 6457 if (max == min) { 6458 s = 0; 6459 } else if (l < .5) { 6460 s = d / (2 * l); 6461 } else { 6462 s = d / (2 - 2 * l); 6463 } 6464 s *= 100; 6465 l *= 100; 6466 return { 6467 h: h, 6468 s: s, 6469 l: l, 6470 a: a 6471 }; 6472 } 6473 if ($section.is('.modal')) { 6474 initModalColors(); 6475 $section.on('lpui-show', function () { 6476 window.lpUI.requestScrollLock(sectionDomId); 6477 }); 6478 $section.on('lpui-hide', function () { 6479 window.lpUI.releaseScrollLock(sectionDomId); 6480 }); 6481 } 6482 initializeSelects(); 6483 var $addressInputs = $section.find('.js-address-autocomplete'); 6484 if ($addressInputs.length && window.luxuryPresence && window.luxuryPresence.mapApiLoader) { 6485 window.luxuryPresence.mapApiLoader.load().then(function () { 6486 var autocompleteOptions = { 6487 types: ['geocode'], 6488 fields: ['formatted_address'] 6489 }; 6490 $addressInputs.each(function () { 6491 var input = this; 6492 var autocomplete = new google.maps.places.Autocomplete(input, autocompleteOptions); 6493 autocomplete.addListener('place_changed', function () { 6494 var place = autocomplete.getPlace(); 6495 if (place && place.formatted_address) { 6496 input.value = place.formatted_address; 6497 } 6498 }); 6499 }); 6500 }); 6501 } 6502})('section-1c92a0cc-d18f-4a88-96d3-a5f34620fae7', 'undefined'); 6503} catch(e) { 6504console.log(e); 6505} 6506 try { 6507(function init(sectionDomId, options) { 6508 let section = null; 6509 switch (sectionDomId) { 6510 case 'NAVBAR': 6511 section = document.querySelector('body > nav, #global-navbar'); 6512 break; 6513 case 'FOOTER': 6514 section = document.querySelector('body > footer, #global-footer'); 6515 break; 6516 case 'SIDEMENU': 6517 section = document.querySelector('body > div.sidemenu, #global-sidemenu'); 6518 break; 6519 case 'CONTACT':
6520 section = document.querySelector('#modal-global-contact-us'); 6521 break; 6522 case 'MOBILE_CONTACT': 6523 section = document.querySelector('body > div.mobile-contact-wrapper, #global-mobile-contact'); 6524 break; 6525 case 'SUBSCRIBE': 6526 // Subscribe Not Implemented 6527 section = null; 6528 break; 6529 case 'ACCOUNT': 6530 // Account Not Implemented 6531 section = null; 6532 break; 6533 default: 6534 section = document.querySelector('#' + sectionDomId); 6535 } 6536 if (!section) { 6537 return; 6538 } 6539 var url = window.luxuryPresence.defaults.routerUrl; 6540 var companyId = window.luxuryPresence.defaults.companyId; 6541 var limit = options.limit || 8; 6542 if (typeof limit === 'string') { 6543 limit = parseInt(limit, 10) || 8; // this NaN protection also filters out 0 - this is expected 6544 } 6545 var templateSelector = options.templateSelector || '.jsIGTemplate'; 6546 var containerSelector = options.containerSelector || '.jsIGContainer'; 6547 var hasDataClass = options.hasDataClass || 'has-data'; 6548 var hideActivateMessage = options.hasDataClass || false; 6549 var payload = { 6550 companyId: companyId, 6551 limit: limit 6552 }; 6553 if (options.resourceId && typeof options.resourceId === 'string' && !options.resourceId.match(/^{{.*}}$/)) { 6554 payload.resourceId = options.resourceId; 6555 // Federated enum values are uppercase (AGENT/TEAM); element configs carry lowercase 6556 payload.resource = String(options.resource || 'agent').toUpperCase(); 6557 payload.fallbackToNoResource = options.fallbackToNoResource === 'true' || options.fallbackToNoResource === true; 6558 } 6559 var query = 'query InstagramFeed (\n' + ' $companyId: ID!\n' + ' $instagramAuthorizationId: ID\n' + ' $username: String\n' + (payload.resourceId ? '$resource: InstagramContentAuthResourceEnum\n $resourceId: ID\n $fallbackToNoResource: Boolean' : '') + ' $limit: Int\n' + ') {\n' + ' instagramContentFeed (\n' + ' companyId: $companyId\n' + ' instagramAuthorizationId: $instagramAuthorizationId\n' + ' username: $username\n' + (payload.resourceId ? 'resource: $resource\n resourceId: $resourceId\n fallbackToNoResource: $fallbackToNoResource' : '') + ' limit: $limit\n' + ' ) {\n' + ' active\n' + ' feedUrl\n' + ' username\n' + ' items {\n' + ' id\n' + ' caption\n' + ' commentsCount\n' + ' likeCount\n' + ' mediaType\n' + ' mediaUrl\n' + ' permalink\n' + ' shortcode\n' + ' thumbnailUrl\n' + ' timestamp\n' + ' username\n' + ' }\n' + ' }\n' + '}\n'; 6560 var templateHTML = section.querySelector(templateSelector)?.innerHTML || ''; 6561 var template = Handlebars.compile(templateHTML); 6562 var container = section.querySelector(containerSelector); 6563 if (options.variables) { 6564 Object.keys(options.variables).forEach(function (key) { 6565 if (options.variables.hasOwnProperty(key) && (typeof options.variables[key] !== 'string' || !options.variables[key].match(/^{{.*}}$/))) { 6566 payload[key] = options.variables[key]; 6567 } 6568 }); 6569 } 6570 function getData() { 6571 var feed = { 6572 error: true 6573 }; 6574 if (payload.companyId == null || payload.companyId === '') { 6575 // instagramContentFeed requires companyId (ID!) â nothing to fetch without it. 6576 // Checked after the options.variables merge, which may override the default. 6577 // Deferred so element listeners (attached after this template runs) still receive it. 6578 setTimeout(function () { 6579 section.dispatchEvent(new CustomEvent('ig-data', { 6580 detail: feed 6581 })); 6582 }, 0); 6583 return; 6584 } 6585 superagent.post(url + '/graphql').send({ 6586 query: query, 6587 variables: payload 6588 }).then(res => { 6589 try { 6590 if (!res.body.errors) { 6591 feed = res.body.data.instagramContentFeed; 6592 if (!feed.active) { 6593 if (!hideActivateMessage && sectionDomId === 'section-temp' && container) { 6594 if (payload.resourceId) { 6595 container.innerHTML = '<div style="text-align: center">Instagram account is not connected to selected ' + payload.resource + '.<br /> Connect it on the ' + payload.resource + ' page</div>'; 6596 } else { 6597 container.innerHTML = '<div style="text-align: center">Instagram account is not connected.<br /> Connect it on <a href="/profile">Profile page</a></div>'; 6598 } 6599 } 6600 } else { 6601 if (feed.items.length) { 6602 if (hasDataClass) { 6603 section.classList.add(hasDataClass); 6604 } 6605 const feedName = feed.username; 6606 section.querySelectorAll('.jsIGLink').forEach(link => link.setAttribute('href', feed.feedUrl)); 6607 section.querySelectorAll('.jsIGName').forEach(name => { 6608 name.textContent = '@' + feedName; 6609 name.setAttribute('title', '@' + feedName); 6610 }); 6611 if (container && template) { 6612 container.innerHTML = template(feed); 6613 } 6614 } 6615 } 6616 } 6617 } catch (e) {} 6618 section.dispatchEvent(new CustomEvent('ig-data', { 6619 detail: feed 6620 })); 6621 }).catch(e => { 6622 console.log('err', e); 6623 section.dispatchEvent(new CustomEvent('ig-data', { 6624 detail: feed 6625 })); 6626 }); 6627 } 6628 getData(); 6629})('section-0431207f-8ab4-4735-aa08-dfbaadfcb5dc', JSON.parse("{\"limit\":33,\"resource\":\"agent\",\"resourceId\":\"\",\"fallbackToNoResource\":\"true\",\"companyId\":\"c3076ecb-c2d7-457b-a426-bcf036133f1f\",\"usePageResource\":false,\"sectionIndex\":2}")); 6630} catch(e) { 6631console.log(e); 6632} 6633 try { 6634(function init(sectionDomId) { 6635 const $section = $('#' + sectionDomId); 6636 var templateHTML = $section.find('.jsIGItem').html(); 6637 var itemTemplate = templateHTML && Handlebars.compile(templateHTML); 6638 const $container = $section.find('.ig-feed-feed'); 6639 const $topPrev = $section.find('.top-nav-prev'); 6640 const $topNext = $section.find('.top-nav-next'); 6641 const $bottomPrev = $section.find('.bottom-nav-prev'); 6642 const $bottomNext = $section.find('.bottom-nav-next'); 6643 let limit = 33; 6644 try { 6645 limit = parseInt($container[0].dataset['limit']); 6646 } catch (e) {} 6647 if (!limit) { 6648 limit = 33; 6649 } 6650 let igFeed = {}; 6651 let usedWidth = 0; 6652 let slider = null; 6653 $section.on('ig-data', function (ev, feed) { 6654 feed = ev.detail || feed; 6655 igFeed = feed; 6656 if (feed.active && feed.items.length) { 6657 buildSlider(); 6658 } 6659 }); 6660 $(window).on('resize.lpui-auto-clean', onResize); 6661 let timer = null; 6662 function onResize() { 6663 clearTimeout(timer); 6664 timer = window.setTimeout(buildSlider, 50); 6665 } 6666 function getLayout(w, n) { 6667 if (w > 1100) { 6668 if (n > 7) { 6669 return { 6670 type: 'type1', 6671 single: 1, 6672 multi: n, 6673 slides: 3, 6674 slider: n > 9 6675 }; 6676 } 6677 if (n > 5) { 6678 return { 6679 type: 'type1', 6680 single: 2, 6681 multi: n, 6682 slides: 3, 6683 slider: false 6684 }; 6685 } 6686 return { 6687 type: 'type1', 6688 single: n, 6689 multi: n, 6690 slides: 3, 6691 slider: n > 3 6692 }; 6693 } else if (w > 880) { 6694 if (n > 4) { 6695 return { 6696 type: 'type2', 6697 single: 1, 6698 multi: n, 6699 slides: 2.5, 6700 slider: n > 7 6701 }; 6702 } 6703 return { 6704 type: 'type1', 6705 single: n, 6706 multi: n, 6707 slides: 3, 6708 slider: n > 3 6709 }; 6710 } else if (w > 767) { 6711 if (n > 4) { 6712 return { 6713 type: 'type3', 6714 single: 1, 6715 multi: n, 6716 slides: 2, 6717 slider: n > 5 6718 }; 6719 } 6720 return { 6721 type: 'type4', 6722 single: n, 6723 multi: n, 6724 slides: 2, 6725 slider: n > 2 6726 }; 6727 } else if (w > 460) { 6728 return { 6729 type: 'type4', 6730 single: n, 6731 multi: n, 6732 slides: 2, 6733 slider: n > 2 6734 }; 6735 } 6736 return { 6737 type: 'type5', 6738 single: n, 6739 multi: n, 6740 slides: 1, 6741 slider: n > 1 6742 }; 6743 } 6744 function ensureClass($el, className) { 6745 if ($el.hasClass(className)) { 6746 return; 6747 } 6748 $el.addClass(className); 6749 } 6750 function makeSquare($el) { 6751 const el = $el[0]; 6752 el.style.width = ''; 6753 el.style.height = ''; 6754 const rect = el.getBoundingClientRect(); 6755 if (rect.width !== rect.height) { 6756 el.style.width = rect.height + 'px'; 6757 } 6758 } 6759 function buildSlider() { 6760 const cWidth = $container[0].getBoundingClientRect().width; 6761 if (cWidth === usedWidth) { 6762 return; 6763 } 6764 $container.empty();
6765 slider = $('<div class="ig-feed-slider"></div>'); 6766 const items = igFeed.items.slice(0, limit); 6767 const layout = getLayout(cWidth, items.length); 6768 let i = 0; 6769 let slide = null; 6770 while (i < layout.single) { 6771 slide = $(`<div class="slide-single ${layout.type}"></div>`).append(itemTemplate(items[i])).appendTo(slider); 6772 i++; 6773 } 6774 while (i < layout.multi) { 6775 slide = $(`<div class="slide-multi ${layout.type}"></div>`).appendTo(slider); 6776 let slideContent = $('<div class="slide-multi-content"></div>').appendTo(slide); 6777 const lim = i + 4; 6778 while (i < lim && i < layout.multi) { 6779 slideContent.append(itemTemplate(items[i])); 6780 i++; 6781 } 6782 } 6783 $container.append(slider); 6784 if (layout.slider) { 6785 $topPrev.removeClass('hidden-control'); 6786 $topNext.removeClass('hidden-control'); 6787 $bottomPrev.removeClass('hidden-control'); 6788 $bottomNext.removeClass('hidden-control'); 6789 let prev, next; 6790 if ($bottomPrev.parent()[0].getBoundingClientRect().width) { 6791 prev = $bottomPrev; 6792 next = $bottomNext; 6793 } else { 6794 prev = $topPrev; 6795 next = $topNext; 6796 makeSquare($topPrev); 6797 makeSquare($topNext); 6798 } 6799 slider.children().each(function () { 6800 this.style.width = this.getBoundingClientRect().width + 'px'; 6801 }); 6802 slider.slick({ 6803 dots: false, 6804 prevArrow: prev, 6805 nextArrow: next, 6806 slidesToScroll: 1, 6807 infinite: false, 6808 slidesToShow: layout.slides 6809 }); 6810 } else { 6811 ensureClass($topPrev, 'hidden-control'); 6812 ensureClass($topNext, 'hidden-control'); 6813 ensureClass($bottomPrev, 'hidden-control'); 6814 ensureClass($bottomNext, 'hidden-control'); 6815 } 6816 } 6817})('section-0431207f-8ab4-4735-aa08-dfbaadfcb5dc', 'undefined'); 6818} catch(e) { 6819console.log(e); 6820} 6821 6822 var { body } = document; 6823 var globalNav = body.querySelector(':scope > nav'); 6824 var globalSideMenu = body.querySelector(':scope > div.sidemenu'); 6825 6826 document.querySelectorAll('body > .modal .close').forEach(modalCloseBtn => { 6827 modalCloseBtn.addEventListener('click', function() { 6828 window.lpUI.hideClosestModal(modalCloseBtn); 6829 }); 6830 }); 6831 6832 window.addEventListener('click', function() { 6833 document.querySelectorAll('.share-popup').forEach(sharedPopup => { 6834 sharedPopup.style.display = 'none' ; 6835 }); 6836 }); 6837 6838 /* Misc Partials / Addons */ 6839 function initWOW() { 6840 if (wow) { 6841 wow.init(); 6842 wow.scrollHandler(); 6843 setTimeout(function () { 6844 const wowHideStyle = document.getElementById('wow-hide-elements'); 6845 if(wowHideStyle) { 6846 wowHideStyle.disabled = true; 6847 } 6848 }, 1); 6849 } 6850 } 6851 6852 const wowHideElements = document.getElementById('wow-hide-elements'); 6853 if (wowHideElements) { 6854 wowHideElements.disabled = false; 6855 } 6856 6857 var globalLoader = document.getElementById('global-loader'); 6858 if (globalLoader) { 6859 setTimeout(function() { 6860 globalLoader.style.height = "0px"; 6861 window.sessionStorage.setItem('global_loading_displayed', 'yes'); 6862 6863 // Remove global-loader to prevent caching issues with turbolinks 6864 globalLoader.addEventListener('transitionend', 6865 function(e) { 6866 globalLoader.remove(); 6867 initWOW(); 6868 }); 6869 }, 1000); 6870 } else { 6871 initWOW(); 6872 } 6873 6874 /* Generic Navbar Handling */ 6875 globalNav?.querySelectorAll('.hamburger-component').forEach(hamburgerComponent => { 6876 hamburgerComponent.addEventListener('click', function() { 6877 window.lpUI.sideMenuToggle(); 6878 }); 6879 }); 6880 globalSideMenu?.querySelectorAll('.toggle').forEach(toggleEl => { 6881 toggleEl.addEventListener('click', function() { 6882 window.lpUI.sideMenuToggle(); 6883 }); 6884 }); 6885 6886 document.querySelectorAll('body > nav .sub-nav-container > a, body > div.sidemenu .sub-nav-container > a').forEach(linkEl => { 6887 linkEl.addEventListener('click', function(e) { 6888 e.preventDefault(); 6889 e.stopPropagation();
6890 var subNav = linkEl.parentElement.querySelector(':scope > .sub-nav'); 6891 if (subNav && subNav.classList.contains('visible')){ 6892 subNav.classList.remove('visible'); 6893 } else { 6894 globalNav?.querySelectorAll('.sub-nav-container .sub-nav').forEach(globalSubNav => { 6895 globalSubNav.classList.remove('visible'); 6896 }); 6897 globalSideMenu?.querySelectorAll('.sub-nav').forEach(sideMenuSubNav => { 6898 sideMenuSubNav.classList.remove('visible'); 6899 }); 6900 subNav?.classList.add('visible'); 6901 } 6902 }); 6903 }); 6904 window.addEventListener('click', function() { 6905 document.querySelectorAll('nav .sub-nav.visible, .sidemenu .sub-nav.visible').forEach(subNavEl => { 6906 subNavEl.classList.remove('visible'); 6907 }); 6908 }); 6909 6910 } 6911 6912 document.addEventListener('DOMContentLoaded', lpGlobalInit); 6913 6914 </script>
6914 6915 6916
6916<script type="module"> 6917 function selectorExists(selector) { 6918 return document.querySelector(selector) !== null; 6919 } 6920 6921 // Function to initialize the third-party library 6922 function initParoller(selector = '.my-paroller') { 6923 var $parollerElements = $(selector); 6924 if ($parollerElements.length > 0 && typeof $parollerElements.paroller === 'function') { 6925 $parollerElements.paroller(); 6926 } 6927 } 6928 6929 const parollerSelector = '.my-paroller'; 6930 6931 if (selectorExists(parollerSelector)) { 6932 window.luxuryPresence.libs.loadJs(window.luxuryPresence.libs.jsLibsMap.paroller.jsSrc, function() { 6933 initParoller(parollerSelector); 6934 window.luxuryPresence.libs.jsLibsMap.paroller.isLoaded = true; 6935 }); 6936 } 6937 </script>
6937 6938 6939 <!-- Google Tag Manager (noscript) -->
vendor: 80 bytes, lines 6939-6940
6939 6940 <noscript><iframe src="https://www.googletagmanager.com/ns.html?id=
6940GTM-T9SFQGHZ
vendor: 107 bytes, lines 6940-6942
6940" 6941 height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript> 6942
6942<!-- End Google Tag Manager --> 6943 6944
6944<script 6945 src="https://cdn.luxurypresence.com/scripts/a11y-widget/latest/widget.js" 6946 data-website-id="ff06f655-0c64-45c8-9c55-e8468b4e5303" 6947 6948 defer></script>
vendor: 1 bytes, line 6948
6948
6949<script type="module" src="https://static.cloudflareinsights.com/beacon.min.js/v31edd6df95cf4e85bb4c19e7a9bdbcba1788362987495" integrity="sha512-iIg7k2xntmwu6/uSb5tpc/hySgZc4eoL31yB29W6tJFo2akwjPWcEqnCEdJvGexCL0KEQwVYv5BlowfhVz26hg==" data-cf-beacon='{"version":"2024.11.0","token":"d2a8f4b3d65944edb8fcd6ffce49ee5e","spa":2}' crossorigin="anonymous"></script>
vendor: 1 bytes, line 6949
6949
6950<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:'a45fb3b9184be417',t:'MTc5MTIzNzY1Ng=='};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>
6950</body> 6951</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.