PageSourceSearch

https://www.letsrentz.com/buy/Flat-for-sale-in-Sector-71-Mohali/1/30.7046/76.7179

html letsrentz.com collected 2026-10-02 06:43:24 UTC 227,826 bytes, 3,804 lines download raw bytes

1 <!DOCTYPE html>
2<html lang="en">
3<head>
4<meta charset="utf-8">
5<meta name="viewport" content="width=device-width, initial-scale=1">
6<meta name="csrf-token" content="bvRQeC7zDEZMxhJdt8x263POgOGJvjDEH4HLTaIw">
7<title>Flats/Apartments for sale in Sector 71 Mohali</title>
8<meta name="description" content="Explore Flats/Apartments for sale in Sector 71 Mohali on Lets Rentz.Check 100% verified flats in Sector 71 Mohali for sale with 0% brokerage option">
9<link rel="canonical" href="https://www.letsrentz.com/buy/Flat-for-sale-in-Sector-71-Mohali/1/30.7046/76.7179">
10
11<meta name="apple-itunes-app" content="app-id=1634945591, app-argument=https://www.letsrentz.com/buy/Flat-for-sale-in-Sector-71-Mohali/1/30.7046/76.7179">
12<link rel="shortcut icon" href="https://www.letsrentz.com/front/assets/images/favicon.svg">
13
14<meta property="og:title" content="Flats/Apartments for sale in Sector 71 Mohali"/>
15<meta property="og:description" content="Explore Flats/Apartments for sale in Sector 71 Mohali on Lets Rentz.Check 100% verified flats in Sector 71 Mohali for sale with 0% brokerage option"/>
16<meta property="og:url" content="https://www.letsrentz.com/buy/Flat-for-sale-in-Sector-71-Mohali/1/30.7046/76.7179"/>
17<meta property="og:image" content="https://images.letsrentz.com/AppBanners/fbShare_letsrentz.png"/>
18<meta property="og:image:height" content="360"/>
19<meta property="og:image:width" content="540"/>
20<meta property="og:site_name" content="LetsRentz"/>
21<meta property="og:type" content="product"/>
22<meta name="twitter:site" content="@letsrentz">
23<meta name="twitter:title" content="Flats/Apartments for sale in Sector 71 Mohali">
24<meta name="twitter:description" content="Explore Flats/Apartments for sale in Sector 71 Mohali on Lets Rentz.Check 100% verified flats in Sector 71 Mohali for sale with 0% brokerage option">
25<meta name="twitter:image" content="https://images.letsrentz.com/AppBanners/fbShare_letsrentz.png">
26
27
28
29
30<script>
31(function () {
32   try {
33      var p = new URLSearchParams(location.search), d = {}, has = false;
34      ['utm_source','utm_medium','utm_campaign','utm_content','utm_term','fbclid','gclid'].forEach(function (k) {
35         var v = p.get(k);
36         if (v) { d[k] = v.slice(0, 160); has = true; }
37      });
38      var ref = '';
39      try { ref = document.referrer ? new URL(document.referrer).hostname : ''; } catch (e) {}
40      var external = ref && ref.indexOf('letsrentz.com') === -1;
41      var existing = /(?:^|;\s*)lr_acq=/.test(document.cookie);
42      // Write when the URL carries campaign params, or when an external
43      // referrer arrives and nothing is remembered yet. Anything else (direct
44      // visit, internal navigation, organic visit while a paid touch is
45      // remembered) leaves the cookie alone.
46      if (!has && !(external && !existing)) { return; }
47      d.lp = location.pathname.slice(0, 200);
48      d.ref = external ? ref.slice(0, 120) : '';
49      d.ts = Date.now();
50      document.cookie = 'lr_acq=' + encodeURIComponent(JSON.stringify(d)) +
51         '; max-age=2592000; path=/; SameSite=Lax' + (location.protocol === 'https:' ? '; Secure' : '');
52   } catch (e) {}
53})();
54</script>
54
55<link rel="preconnect" href="https://fonts.googleapis.com">
56<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
57<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:[email protected]&display=swap">
58<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css">
59<link rel="stylesheet" href="https://www.letsrentz.com/front/assets/css/styles.css?v=1.29">
60<link rel="stylesheet" href="https://www.letsrentz.com/front/assets/css/responsive.css?v=1.29">
61<link rel="stylesheet" href="https://www.letsrentz.com/front/assets/css/custom.css?v=1.49">
62<link rel="stylesheet" href="https://www.letsrentz.com/front/assets/css/styles-locality.css?v=1.70">
63<link rel="stylesheet" href="https://www.letsrentz.com/front/assets/css/responsive-locality.css?v=1.84">
64
65<link rel="stylesheet" href="https://www.letsrentz.com/front/assets/css/loginv2.css?v=1.0">
66<link rel="stylesheet" href="https://www.letsrentz.com/front/assets/css/owl.carousel.min.css">
67<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/toastr.js/latest/toastr.min.css">
68<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.css"/>
69<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.7.2/css/all.min.css">
70<script src="https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.js"></script>
70
71
72<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
vendor: 1 bytes, line 72
72
73<script src="https://cdnjs.cloudflare.com/ajax/libs/toastr.js/latest/toastr.min.js"></script>
vendor: 1 bytes, line 73
73
74<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script>
vendor: 1 bytes, line 74
74
75<script src="https://www.letsrentz.com/front/custom.js"></script>
vendor: 1 bytes, line 75
75
76<script src="https://www.letsrentz.com/front/assets/js/owl.carousel.min.js"></script>
76
77
78
79
80<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/font/bootstrap-icons.css">
81<link rel="stylesheet" href="https://www.letsrentz.com/front/assets/css/style1.css?v=60">
82<link rel="stylesheet" href="https://www.letsrentz.com/front/assets/css/responsive1.css?v=59">
83
84<link rel="stylesheet" href="https://www.letsrentz.com/front/assets/css/onboarding-v2.css?v=2">
85
86<link rel="stylesheet" href="https://www.letsrentz.com/front/assets/css/site-header.css?v=1">
87
88
89<!-- Google Tag Manager -->
90<script>(function(w,d,s,l,i){
91   if (d.querySelector('script[src*="googletagmanager.com/gtm.js?id="]')) { return; }
92   w[l]=w[l]||[];w[l].push({'gtm.start':
93   new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
94   j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
95   'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
96})(window,document,'script','dataLayer','GTM-T7B3K67W');</script>
96
97<!-- End Google Tag Manager -->
98<!-- Global site tag (gtag.js) - Google Analytics -->
99<script>
100   (function () {
101      var hpGA4AlreadyLoaded = (typeof window.gtag === 'function');
102      window.dataLayer = window.dataLayer || [];
103      if (!hpGA4AlreadyLoaded) {
104         var gtagScript = document.createElement('script');
105         gtagScript.async = true;
106         gtagScript.src = 'https://www.googletagmanager.com/gtag/js?id=G-3HSRHHWD2P';
107         document.head.appendChild(gtagScript);
108         window.gtag = function () { dataLayer.push(arguments); };
109         gtag('js', new Date());
110         gtag('config', 'G-3HSRHHWD2P');
111         gtag('config', 'AW-11395525747');
112      }
113   })();
114
115   // Same real GA4-client-id reader as template.blade.php, so the backend's
116   // server-side sign_up event (AuthController::register()) ties back to
117   // this browser session exactly like it does on the live site.
118   function getGA4ClientId() {
119      var match = document.cookie.match(/(?:^|;\s*)_ga=([^;]+)/);
120      if (!match) return null;
121      var parts = match[1].split('.');
122      if (parts.length < 4) return null;
123      return parts.slice(-2).join('.');
124   }
125</script>
125
126
127<!-- Meta Pixel Code -->
128<script>
129   var hpFbPixelAlreadyLoaded = !!window.fbq;
130   !function(f,b,e,v,n,t,s)
131   {if(f.fbq)return;n=f.fbq=function(){n.callMethod?
132   n.callMethod.apply(n,arguments):n.queue.push(arguments)};
133   if(!f._fbq)f._fbq=n;n.push=n;n.loaded=!0;n.version='2.0';
134   n.queue=[];t=b.createElement(e);t.async=!0;
135   t.src=v;s=b.getElementsByTagName(e)[0];
136   s.parentNode.insertBefore(t,s)}(window, document,'script',
137   'https://connect.facebook.net/en_US/fbevents.js');
138   if (!hpFbPixelAlreadyLoaded) {
139      fbq('init', '824029695503400');
140      fbq('track', 'PageView');
141   }
142</script>
142
143<noscript><img height="1" width="1" style="display:none"
144src="https://www.facebook.com/tr?id=824029695503400&ev=PageView&noscript=1"
145/></noscript>
146<!-- End Meta Pixel Code -->
147
148
149<script type="application/ld+json">
150   {
151      "@context": "https://schema.org",
152      "@type": "Organization",
153      "name": "LetsRentz",
154      "url": "https://www.letsrentz.com",
155      "logo": "https://www.letsrentz.com/front/assets/images/logo.svg",
156      "contactPoint": {
157         "@type": "ContactPoint",
158         "telephone": "+91-7719669498",
159         "contactType": "customer service",
160         "areaServed": "IN",
161         "availableLanguage": "English"
162      }
163   }
164</script>
vendor: 1 bytes, line 164
164
165<script type="application/ld+json">
166{
167   "@context": "https://schema.org",
168   "@type": "WebSite",
169   "name": "LetsRentz",
170   "url": "https://www.letsrentz.com",
171   "potentialAction": {
172      "@type": "SearchAction",
173      "target": {
174            "@type": "EntryPoint",
175            "urlTemplate": "https://www.letsrentz.com/properties?address={search_term_string}"
176      },
177      "query-input": "required name=search_term_string"
178   }
179}
180</script>
180
181</head>
182<body class="">
183<!-- Google Tag Manager (noscript) -->
vendor: 68 bytes, lines 183-184
183
184<noscript><iframe src="https://www.googletagmanager.com/ns.html?id=
184GTM-T7B3K67W
vendor: 83 bytes, lines 184-185
184"
185height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
186<!-- End Google Tag Manager (noscript) -->
187<div class="hp-preview">
188
189   
190         <div class="hp-app-banner" id="hpAppBanner">
191   <div class="hp-app-banner-inner">
192      <span class="hp-app-banner-icon"><i class="bi bi-phone"></i></span>
193      <div class="hp-app-banner-copy">
194         <span class="hp-app-banner-title">Better experience on the app</span>
195         <span class="hp-app-banner-sub">
196            <span class="hp-app-banner-rating"><i class="bi bi-star-fill"></i> 4.3</span>
197            <span class="hp-app-banner-dot">&middot;</span>
198            <span id="hpAppBannerStoreLabel">Play Store</span>
199         </span>
200      </div>
201      <a href="javascript:void(0)" class="hp-app-banner-cta" id="hpAppBannerCta">Open</a>
202      <button type="button" class="hp-app-banner-close" id="hpAppBannerClose" aria-label="Dismiss">&times;</button>
203   </div>
204</div>
205
206<header class="hv2 hv2-header">
207   <div class="hv2-wrap">
208      <a class="hv2-logo" href="https://www.letsrentz.com"><img src="https://www.letsrentz.com/front/assets/images/details-images/logo.png" alt="Lets Rentz"></a>
209      <div class="hv2-spacer"></div>
210      <a class="hv2-hbtn hv2-hbtn--ghost hv2-hide-m" href="https://www.letsrentz.com/propertysearchwithpid">Search by PID</a>
211                           <button type="button" class="hv2-hbtn hv2-hide-m" data-hp-login-trigger>Log in</button>
212            <button type="button" class="hv2-hbtn" id="hv2MenuBtn" data-hv2-menu-open aria-controls="hv2Drawer" aria-expanded="false">
213         <i class="bi bi-list" style="font-size:18px"></i><span class="hv2-hide-m">Menu</span>
214      </button>
215   </div>
216</header>
217
218<div class="hv2-scrim" id="hv2Scrim"></div>
219<aside class="hv2 hv2-drawer" id="hv2Drawer" aria-hidden="true" aria-label="Menu">
220   <div class="hv2-dr-head">
221      <a class="hv2-logo" href="https://www.letsrentz.com"><img src="https://www.letsrentz.com/front/assets/images/details-images/logo.png" alt="Lets Rentz"></a>
222      <button type="button" class="hv2-dr-x" id="hv2MenuClose" aria-label="Close menu"><i class="bi bi-x-lg"></i></button>
223   </div>
224
225         <div class="hv2-dr-auth">
226         <button type="button" class="hv2-hbtn hv2-hbtn--solid" data-hp-login-trigger data-hv2-menu-close>Log in / Sign up</button>
227      </div>
228   
229   <nav>
230      
231      <p>Find a home</p>
232      <a href="https://www.letsrentz.com/propertysearch/Flat-for-rent-in-Sector-71-Mohali/2/30.7046/76.7179"><i class="bi bi-house-door"></i>Rent a home</a>
233      <a href="https://www.letsrentz.com/propertysearch/PG-in-Sector-71-Mohali/3/30.7046/76.7179"><i class="bi bi-door-open"></i>PG &amp; co-living</a>
234      <a href="https://www.letsrentz.com/buy/Flat-for-sale-in-Sector-71-Mohali/1/30.7046/76.7179"><i class="bi bi-key"></i>Buy a property</a>
235      <a href="https://www.letsrentz.com/propertysearch/Commercial-for-rent-in-Sector-71-Mohali/5/30.7046/76.7179"><i class="bi bi-building"></i>Commercial</a>
236      <a href="https://www.letsrentz.com/propertysearchwithpid"><i class="bi bi-upc-scan"></i>Search by PID</a>
237               <a href="https://www.letsrentz.com/tenant/plans"><i class="bi bi-star"></i>Premium plans</a>
238      
239      <p>For owners &amp; brokers</p>
240      <a href="https://www.letsrentz.com/post-property-for-rent" data-hv2-menu-close><i class="bi bi-plus-circle"></i>Post property <em>FREE</em></a>
241            <a href="https://www.letsrentz.com/rent-agreement"><i class="bi bi-file-earmark-text"></i>Rent agreement</a>
242      <a href="https://www.letsrentz.com/rent-agreement"><i class="bi bi-shield-check"></i>Police verification</a>
243
244      <p>More</p>
245      <a href="https://www.letsrentz.com/rent-calculator"><i class="bi bi-calculator"></i>Rent calculator</a>
246      <a href="https://www.letsrentz.com/localities"><i class="bi bi-geo-alt"></i>Explore localities</a>
247      <a href="https://blog.letsrentz.com/" target="_blank" rel="noopener"><i class="bi bi-journal-text"></i>Blog</a>
248      <a href="https://www.letsrentz.com/faq"><i class="bi bi-question-circle"></i>FAQ</a>
249      <a href="https://wa.me/918847073862" target="_blank" rel="noopener"><i class="bi bi-whatsapp"></i>Contact us on WhatsApp</a>
250
251         </nav>
252</aside>
253
254<script>
255(function () {
256   var drawer = document.getElementById('hv2Drawer');
257   var scrim = document.getElementById('hv2Scrim');
258   var menuBtn = document.getElementById('hv2MenuBtn');
259   if (!drawer || !scrim || !menuBtn) return;
260   function setMenu(open) {
261      drawer.classList.toggle('is-open', open);
262      scrim.classList.toggle('is-open', open);
263      document.body.classList.toggle('hv2-locked', open);
264      drawer.setAttribute('aria-hidden', open ? 'false' : 'true');
265      menuBtn.setAttribute('aria-expanded', open ? 'true' : 'false');
266      if (open) document.getElementById('hv2MenuClose').focus();
267   }
268   document.querySelectorAll('[data-hv2-menu-open]').forEach(function (b) { b.addEventListener('click', function () { setMenu(true); }); });
269   document.getElementById('hv2MenuClose').addEventListener('click', function () { setMenu(false); });
270   scrim.addEventListener('click', function () { setMenu(false); });
271   // Items that open a modal (login, post property, logout) close the drawer first.
272   document.querySelectorAll('[data-hv2-menu-close]').forEach(function (b) { b.addEventListener('click', function () { setMenu(false); }); });
273   document.addEventListener('keydown', function (e) { if (e.key === 'Escape' && drawer.classList.contains('is-open')) setMenu(false); });
274})();
275</script>
275
276
277
278
279<div class="hp-modal-overlay" id="hpNotifHelpOverlay" aria-hidden="true">
280   <div class="hp-modal-card" role="dialog" aria-modal="true" aria-labelledby="hpNotifHelpTitle">
281      <div class="hp-modal-icon"><i class="bi bi-bell-slash"></i></div>
282      <h3 id="hpNotifHelpTitle">Notifications are blocked</h3>
283      <p id="hpNotifHelpBody">You've blocked notifications for Lets Rentz in this browser. To turn them back on, open
284         the site settings from your address bar (click the icon next to the URL), allow notifications, then reload the
285         page.</p>
286      <div class="hp-modal-actions">
287         <button type="button" class="hp-modal-btn hp-modal-btn-ghost" id="hpNotifHelpClose">Got it</button>
288      </div>
289   </div>
290</div>
291   
292   
293<div class="bl-preview">
294
295   
296   <div class="bl-mobile-topbar">
297      <button type="button" class="bl-mobile-back" aria-label="Back" onclick="window.location.href='https://www.letsrentz.com'">
298         <svg width="16" height="16" viewBox="0 0 24 24" fill="none"><path d="M15 6l-6 6 6 6" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>
299      </button>
300      <div class="bl-mobile-search-pill">
301         <svg width="15" height="15" viewBox="0 0 24 24" fill="none"><circle cx="11" cy="11" r="7" stroke="currentColor" stroke-width="2"/><path d="m20 20-3.5-3.5" stroke="currentColor" stroke-width="2" stroke-linecap="round"/></svg>
302         <input type="text" class="autocomplete-google-listing" id="bl_address_mobile" placeholder="Choose by Location..." value="Sector 71 Mohali">
303      </div>
304      <button type="button" class="bl-mobile-icon-btn" data-bs-toggle="modal" data-bs-target="#mobileFiltersModal" aria-label="Filters">
305         <svg width="16" height="16" viewBox="0 0 24 24" fill="none"><path d="M4 6h16M7 12h10M10 18h4" stroke="currentColor" stroke-width="2" stroke-linecap="round"/></svg>
306      </button>
307            <button type="button" class="bl-mobile-icon-btn" aria-label="Log in to view saved properties" onclick="openLoginPopup(event)">
308         <svg width="16" height="16" viewBox="0 0 24 24" fill="none"><path d="M12 21s-7.5-4.9-10-9.4C.6 8 2.4 4 6.3 4c2 0 3.6 1 5.7 3.3C14.1 5 15.7 4 17.7 4c3.9 0 5.7 4 4.3 7.6C19.5 16.1 12 21 12 21Z" stroke="currentColor" stroke-width="1.6"/></svg>
309      </button>
310         </div>
311
312   <div class="bl-mobile-filter-row">
313      <button type="button" class="bl-mobile-sort-pill" data-bs-toggle="modal" data-bs-target="#sortByModal">
314         Sort By
315         <svg width="11" height="7" viewBox="0 0 24 24" fill="none"><path d="M6 9l6 6 6-6" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>
316      </button>
317   </div>
318
319   <div class="container bl-pill-search-wrap bl-desktop-search">
320      <div class="bl-pill-search">
321         <select id="listing_transaction_type" aria-label="Listing type">
322            <option value="buy" selected>Buy</option>
323            <option value="rent">Rent</option>
324         </select>
325         <input type="hidden" id="selected_property_type" value="1">
326         <input type="text" class="autocomplete-google bl-pill-loc" id="bl_address_desktop" value="Sector 71 Mohali">
327         <button type="button" class="bl-pill-search-btn btn_buy_listing_search" aria-label="Search">
328            <svg width="15" height="15" viewBox="0 0 24 24" fill="none"><circle cx="11" cy="11" r="7" stroke="#fff" stroke-width="2"/><path d="m20 20-3.5-3.5" stroke="#fff" stroke-width="2" stroke-linecap="round"/></svg>
329         </button>
330      </div>
331      <a class="bl-pill-pid" href="https://www.letsrentz.com/propertysearchforsalewithpid">Search PID
332         <svg width="12" height="12" viewBox="0 0 24 24" fill="none"><path d="M9 6l6 6-6 6" stroke="currentColor" stroke-width="2"/></svg>
333      </a>
334   </div>
335
336   <div class="container bl-breadcrumb">
337      <a href="https://www.letsrentz.com">Home</a><span class="sep">/</span><span class="current">Flats/Apartments for sale in Sector 71 Mohali</span>
338   </div>
339
340   <div class="container">
341
342      <div class="bl-results-head">
343         <h1 class="property-results">0+ Flats/Apartments for sale in Sector 71 Mohali</h1>
344      </div>
345
346      <div class="card bl-insight-banner">
347         <div class="bl-insight-copy">
348            <svg width="16" height="16" viewBox="0 0 24 24" fill="none"><path d="M12 21s-7-6.3-7-11a7 7 0 1 1 14 0c0 4.7-7 11-7 11Z" stroke="#B8860B" stroke-width="1.8"/></svg>
349            <span>Get to know more about Phase 3B2, Sector 60, Mohali</span>
350         </div>
351         <a href="https://www.letsrentz.com/localities/mohali-sas-nagar/phase-3b2">View Insights &rsaquo;</a>
352      </div>
353
354      <div class="bl-pill-row">
355         
356         <select id="updated_dates" class="bl-updated-select bl-hidden-not-in-design">
357            <option value="all">All Updated Dates</option>
358            <option value="7">Last 7 Days</option>
359            <option value="15">Last 15 Days</option>
360            <option value="30">Last 1 Month</option>
361         </select>
362         <select id="sort_by" class="bl-sort-select">
363            <option>Most relevant</option>
364            <option>Recently added</option>
365            <option>Price: Low to High</option>
366            <option>Price: High to Low</option>
367         </select>
368      </div>
369
370      <div class="bl-layout">
371
372         
373         <aside class="bl-sidebar">
374
375            <div class="card bl-filter-group">
376               <div class="bl-filter-header" data-bs-toggle="collapse" data-bs-target="#blBudget" aria-expanded="true">
377                  <span>Budget</span><span class="chev">&#9662;</span>
378               </div>
379               <div class="collapse show" id="blBudget">
380                  <div class="bl-filter-body">
381                     <div class="bl-row">
382                        <select id="budgetMin">
383                           <option value="0">No min</option>
384                           <option value="2500000">25 L</option>
385                           <option value="5000000">50 L</option>
386                           <option value="7500000">75 L</option>
387                           <option value="10000000">1 Cr</option>
388                           <option value="15000000">1.5 Cr</option>
389                           <option value="20000000">2 Cr</option>
390                           <option value="30000000">3 Cr</option>
391                           <option value="50000000">5 Cr</option>
392                        </select>
393                        <select id="budgetMax">
394                           <option value="1000000000">No max</option>
395                           <option value="5000000">50 L</option>
396                           <option value="10000000">1 Cr</option>
397                           <option value="15000000">1.5 Cr</option>
398                           <option value="20000000">2 Cr</option>
399                           <option value="30000000">3 Cr</option>
400                           <option value="50000000">5 Cr</option>
401                           <option value="100000000">10 Cr</option>
402                        </select>
403                     </div>
404                  </div>
405               </div>
406            </div>
407
408            <div class="card bl-filter-group">
409               <div class="bl-filter-header" data-bs-toggle="collapse" data-bs-target="#blCategory" aria-expanded="true">
410                  <span>Type of property</span><span class="chev">&#9662;</span>
411               </div>
412               <div class="collapse show" id="blCategory">
413                  <div class="bl-filter-body" id="category-container"></div>
414               </div>
415            </div>
416
417            <div class="card bl-filter-group">
418               <div class="bl-filter-header" data-bs-toggle="collapse" data-bs-target="#blBedrooms" aria-expanded="true">
419                  <span>No. of Bedrooms</span><span class="chev">&#9662;</span>
420               </div>
421               <div class="collapse show" id="blBedrooms">
422                  <div class="bl-filter-body">
423                     <div class="bl-chip-grid" id="bedroom-container">
424                                                   <span class="bl-chip-mini bedroom-chip" data-value="1">1 RK/1 BHK</span>
425                                                   <span class="bl-chip-mini bedroom-chip" data-value="2">2 BHK</span>
426                                                   <span class="bl-chip-mini bedroom-chip" data-value="3">3 BHK</span>
427                                                   <span class="bl-chip-mini bedroom-chip" data-value="4">4 BHK</span>
428                                                   <span class="bl-chip-mini bedroom-chip" data-value="5">5 BHK</span>
429                                                                           <span class="bl-chip-mini bedroom-chip bl-more-hidden" data-value="6" style="display:none;">6 BHK</span>
430                                                   <span class="bl-chip-mini bedroom-chip bl-more-hidden" data-value="7" style="display:none;">7 BHK</span>
431                                                   <span class="bl-chip-mini bedroom-chip bl-more-hidden" data-value="8" style="display:none;">8 BHK</span>
432                                                   <span class="bl-chip-mini bedroom-chip bl-more-hidden" data-value="9" style="display:none;">9 BHK</span>
433                                                   <span class="bl-chip-mini bedroom-chip bl-more-hidden" data-value="10" style="display:none;">10 BHK</span>
434                                             </div>
435                     <span class="more-link" id="bedroomMoreLink">+ 5 more</span>
436                  </div>
437               </div>
438            </div>
439
440            
441            <div class="card bl-filter-group bl-filter-not-in-design">
442               <div class="bl-filter-header" data-bs-toggle="collapse" data-bs-target="#blFurnish" aria-expanded="true">
443                  <span>Furnished type</span><span class="chev">&#9662;</span>
444               </div>
445               <div class="collapse show" id="blFurnish">
446                  <div class="bl-filter-body" id="furnish-container"></div>
447               </div>
448            </div>
449
450            <div class="card bl-filter-group">
451               <div class="bl-filter-header" data-bs-toggle="collapse" data-bs-target="#blStatus" aria-expanded="false">
452                  <span>Construction Status</span><span class="chev">&#9662;</span>
453               </div>
454               <div class="collapse" id="blStatus">
455                  <div class="bl-filter-body">
456                     <label class="bl-check-line"><input type="checkbox" class="construction-status-checkbox" value="under_construction">Under Construction</label>
457                     <label class="bl-check-line"><input type="checkbox" class="construction-status-checkbox" value="ready_to_move">Ready to move</label>
458                  </div>
459               </div>
460            </div>
461
462            
463            <div class="card bl-filter-group bl-filter-not-in-design">
464               <div class="bl-filter-header" data-bs-toggle="collapse" data-bs-target="#blPurchase" aria-expanded="true">
465                  <span>Purchase Type</span><span class="chev">&#9662;</span>
466               </div>
467               <div class="collapse show" id="blPurchase">
468                  <div class="bl-filter-body">
469                     <label class="bl-check-line"><input type="checkbox" class="selling-type-checkbox" value="new_booking">New Booking</label>
470                     <label class="bl-check-line"><input type="checkbox" class="selling-type-checkbox" value="resale">Resale</label>
471                  </div>
472               </div>
473            </div>
474
475            <div class="card bl-filter-group">
476               <div class="bl-filter-header" data-bs-toggle="collapse" data-bs-target="#blListedBy" aria-expanded="false">
477                  <span>Posted by</span><span class="chev">&#9662;</span>
478               </div>
479               <div class="collapse" id="blListedBy">
480                  <div class="bl-filter-body">
481                     <label class="bl-check-line"><input type="checkbox" class="listed-by-checkbox" value="user">Owner</label>
482                     <label class="bl-check-line"><input type="checkbox" class="listed-by-checkbox" value="broker">Broker</label>
483                  </div>
484               </div>
485            </div>
486
487            <div class="card bl-filter-group">
488               <div class="bl-filter-header" data-bs-toggle="collapse" data-bs-target="#blArea" aria-expanded="false">
489                  <span>Area (sqft)</span><span class="chev">&#9662;</span>
490               </div>
491               <div class="collapse" id="blArea">
492                  <div class="bl-filter-body">
493                     <div class="bl-row">
494                        <input type="number" id="minAreaInput" placeholder="Min Area" min="0">
495                        <input type="number" id="maxAreaInput" placeholder="Max Area" min="0">
496                     </div>
497                  </div>
498               </div>
499            </div>
500
501            
502            <div class="card bl-filter-group bl-filter-not-in-design">
503               <div class="bl-filter-header" data-bs-toggle="collapse" data-bs-target="#blAge" aria-expanded="true">
504                  <span>Property Age</span><span class="chev">&#9662;</span>
505               </div>
506               <div class="collapse show" id="blAge">
507                  <div class="bl-filter-body">
508                     <label class="bl-check-line"><input type="checkbox" class="property-age-checkbox" value="0-1 years">0-1 year</label>
509                     <label class="bl-check-line"><input type="checkbox" class="property-age-checkbox" value="1-5 years">1-5 year</label>
510                     <label class="bl-check-line"><input type="checkbox" class="property-age-checkbox" value="5-10 years">5-10 year</label>
511                     <label class="bl-check-line"><input type="checkbox" class="property-age-checkbox" value="10+ years">10+ year</label>
512                  </div>
513               </div>
514            </div>
515
516                        <div class="card bl-filter-group">
517               <div class="bl-filter-header" data-bs-toggle="collapse" data-bs-target="#blLocalities" aria-expanded="false">
518                  <span>Localities near you</span><span class="chev">&#9662;</span>
519               </div>
520               <div class="collapse" id="blLocalities">
521                  <div class="bl-filter-body">
522                                             <a href="https://www.letsrentz.com/flats-for-sale-in-mohali" class="bl-loc-row"><span>Apartment / Flats for Sale in Mohali - Flats in Mohali</span> &rsaquo;</a>
523                                             <a href="https://www.letsrentz.com/residential-plots-for-sale-in-mohali" class="bl-loc-row"><span>Residential Plots / Land For Sale in Mohali - Plots in Mohali</span> &rsaquo;</a>
524                                             <a href="https://www.letsrentz.com/1-bhk-flats-for-sale-in-mohali" class="bl-loc-row"><span>1 BHK Flats for Sale in Mohali | 1 BHK Flats in Mohali</span> &rsaquo;</a>
525                                             <a href="https://www.letsrentz.com/2-bhk-flats-for-sale-in-mohali" class="bl-loc-row"><span>2 BHK Flats for Sale in Mohali | 2 BHK Flats in Mohali</span> &rsaquo;</a>
526                                             <a href="https://www.letsrentz.com/3-bhk-flats-for-sale-in-mohali" class="bl-loc-row"><span>3 BHK Flats for Sale in Mohali | 3 BHK Flats in Mohali</span> &rsaquo;</a>
527                                       </div>
528               </div>
529            </div>
530            
531            <div class="card bl-filter-group">
532               <div class="bl-filter-header" data-bs-toggle="collapse" data-bs-target="#blAmenities" aria-expanded="false">
533                  <span>Amenities</span><span class="chev">&#9662;</span>
534               </div>
535               <div class="collapse" id="blAmenities">
536                  <div class="bl-filter-body" id="amenities-container">
537                     <label class="bl-check-line"><input type="checkbox" class="amenity-checkbox" value="parking">Parking</label>
538                     <label class="bl-check-line"><input type="checkbox" class="amenity-checkbox" value="gated_security">Gated Society</label>
539                     <label class="bl-check-line"><input type="checkbox" class="amenity-checkbox" value="park">Park</label>
540                     <label class="bl-check-line"><input type="checkbox" class="amenity-checkbox" value="power_backup">Power Backup</label>
541                     <label class="bl-check-line bl-more-hidden" style="display:none;"><input type="checkbox" class="amenity-checkbox" value="gym">Gym</label>
542                     <label class="bl-check-line bl-more-hidden" style="display:none;"><input type="checkbox" class="amenity-checkbox" value="club_house">Club House</label>
543                     <label class="bl-check-line bl-more-hidden" style="display:none;"><input type="checkbox" class="amenity-checkbox" value="swimming_pool">Swimming Pool</label>
544                     <label class="bl-check-line bl-more-hidden" style="display:none;"><input type="checkbox" class="amenity-checkbox" value="lift">Lift</label>
545                     <label class="bl-check-line bl-more-hidden" style="display:none;"><input type="checkbox" class="amenity-checkbox" value="fire_safety">Fire Safety</label>
546                     <label class="bl-check-line bl-more-hidden" style="display:none;"><input type="checkbox" class="amenity-checkbox" value="intercom">Intercom</label>
547                     <label class="bl-check-line bl-more-hidden" style="display:none;"><input type="checkbox" class="amenity-checkbox" value="children_play_area">Kids Play Area</label>
548                     <label class="bl-check-line bl-more-hidden" style="display:none;"><input type="checkbox" class="amenity-checkbox" value="shopping_center">Shopping Center</label>
549                     <label class="bl-check-line bl-more-hidden" style="display:none;"><input type="checkbox" class="amenity-checkbox" value="sewage_treatment_plant">Waste Treatment</label>
550                     <label class="bl-check-line bl-more-hidden" style="display:none;"><input type="checkbox" class="amenity-checkbox" value="visitor_par
550king">Visitor Parking</label>
551                     <label class="bl-check-line bl-more-hidden" style="display:none;"><input type="checkbox" class="amenity-checkbox" value="gas_pipeline">Gas Pipeline</label>
552                     <label class="bl-check-line bl-more-hidden" style="display:none;"><input type="checkbox" class="amenity-checkbox" value="internet_provider">Internet Provider</label>
553                     <label class="bl-check-line bl-more-hidden" style="display:none;"><input type="checkbox" class="amenity-checkbox" value="water_supply">Water Supply</label>
554                     <span class="more-link" id="amenitiesMoreLink">+ 13 more</span>
555                  </div>
556               </div>
557            </div>
558
559            <div class="card bl-filter-group">
560               <div class="bl-filter-header" data-bs-toggle="collapse" data-bs-target="#blRera" aria-expanded="false">
561                  <span>RERA Approved</span><span class="chev">&#9662;</span>
562               </div>
563               <div class="collapse" id="blRera">
564                  <div class="bl-filter-body">
565                     <label class="bl-check-line"><input type="checkbox" class="rera-status-checkbox" value="rera_approved">Show only RERA registered</label>
566                  </div>
567               </div>
568            </div>
569
570         </aside>
571
572         
573         <div class="results">
574            <div id="propertyListings"></div>
575            <div class="bl-show-more-wrap">
576               <button class="btn btn-ghost btn-show-more" data-offset="0">Show More</button>
577            </div>
578         </div>
579      </div>
580   </div>
581
582   
583   
584   <div class="modal fade mobile-filters-modal" id="mobileFiltersModal" tabindex="-1" aria-hidden="true">
585      <div class="modal-dialog modal-dialog-scrollable modal-fullscreen">
586         <div class="modal-content">
587            <div class="modal-header mobile-filter-modal-header">
588               <button type="button" class="btn btn-link mobile-filter-clear" id="clearFilters">Clear</button>
589               <h5 class="modal-title">Filters</h5>
590               <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
591            </div>
592            <div class="modal-body">
593               <div class="mobile-filter-section-card">
594                  <div class="mobile-filter-section-header">Property Type</div>
595                  <select id="selected_property_type_mobile" class="form-select form-select-sm mt-2">
596                                             <option value="1">Flat/Apartment</option>
597                                             <option value="2">Independent House/Villa</option>
598                                             <option value="3">Independent Builder Floor</option>
599                                             <option value="4">Plot/Land</option>
600                                             <option value="5">Farmhouse</option>
601                                             <option value="6">Commercial</option>
602                                       </select>
603               </div>
604               <div class="mobile-filter-section-card">
605                  <div class="mobile-filter-section-header">Type of property</div>
606                  <div class="bl-filter-body" id="category_container1"></div>
607               </div>
608               <div class="mobile-filter-section-card">
609                  <div class="mobile-filter-section-header">No. of Bedrooms</div>
610                  <div class="bl-chip-grid mt-2" id="bedroom-container-mobile">
611                                             <span class="bl-chip-mini bedroom-chip-mobile" data-value="1">1 RK/1 BHK</span>
612                                             <span class="bl-chip-mini bedroom-chip-mobile" data-value="2">2 BHK</span>
613                                             <span class="bl-chip-mini bedroom-chip-mobile" data-value="3">3 BHK</span>
614                                             <span class="bl-chip-mini bedroom-chip-mobile" data-value="4">4 BHK</span>
615                                             <span class="bl-chip-mini bedroom-chip-mobile" data-value="5">5 BHK</span>
616                                                                  <sp
616an class="bl-chip-mini bedroom-chip-mobile bl-more-hidden" data-value="6" style="display:none;">6 BHK</span>
617                                             <span class="bl-chip-mini bedroom-chip-mobile bl-more-hidden" data-value="7" style="display:none;">7 BHK</span>
618                                             <span class="bl-chip-mini bedroom-chip-mobile bl-more-hidden" data-value="8" style="display:none;">8 BHK</span>
619                                             <span class="bl-chip-mini bedroom-chip-mobile bl-more-hidden" data-value="9" style="display:none;">9 BHK</span>
620                                             <span class="bl-chip-mini bedroom-chip-mobile bl-more-hidden" data-value="10" style="display:none;">10 BHK</span>
621                                       </div>
622                  <span class="more-link" id="bedroomMoreLinkMobile">+ 5 more</span>
623               </div>
624               
625               <div class="mobile-filter-section-card bl-filter-not-in-design">
626                  <div class="mobile-filter-section-header">Furnished type</div>
627                  <div class="bl-filter-body" id="furnish_container1"></div>
628               </div>
629               <div class="mobile-filter-section-card">
630                  <div class="mobile-filter-section-header">Property Status</div>
631                  <label class="bl-check-line"><input type="checkbox" class="construction-status-checkbox-mobile" value="under_construction">Under Construction</label>
632                  <label class="bl-check-line"><input type="checkbox" class="construction-status-checkbox-mobile" value="ready_to_move">Ready to move</label>
633               </div>
634               
635               <div class="mobile-filter-section-card bl-filter-not-in-design">
636                  <div class="mobile-filter-section-header">Purchase Type</div>
637                  <label class="bl-check-line"><input type="checkbox" class="selling-type-checkbox-mobile" value="new_booking">New Booking</label>
638                  <label class="bl-check-line"><input type="checkbox" class="selling-type-checkbox-mobile" value="resale">Resale</label>
639               </div>
640               <div class="mobile-filter-section-card">
641                  <div class="mobile-filter-section-header">Listed By</div>
642                  <label class="bl-check-line"><input type="checkbox" class="listed-by-checkbox-mobile" value="user">Owner</label>
643                  <label class="bl-check-line"><input type="checkbox" class="listed-by-checkbox-mobile" value="broker">Broker</label>
644               </div>
645               <div class="mobile-filter-section-card">
646                  <div class="mobile-filter-section-header">Area (sqft)</div>
647                  <div class="bl-row mt-2">
648                     <input type="number" id="minAreaInput1" placeholder="Min Area" min="0">
649                     <input type="number" id="maxAreaInput1" placeholder="Max Area" min="0">
650                  </div>
651               </div>
652                              <div class="mobile-filter-section-card">
653                  <div class="mobile-filter-section-header">Localities near you</div>
654                  <div class="bl-filter-body bl-mobile-localities">
655                                             <a href="https://www.letsrentz.com/flats-for-sale-in-mohali" class="bl-loc-row"><span>Apartment / Flats for Sale in Mohali - Flats in Mohali</span> &rsaquo;</a>
656                                             <a href="https://www.letsrentz.com/residential-plots-for-sale-in-mohali" class="bl-loc-row"><span>Residential Plots / Land For Sale in Mohali - Plots in Mohali</span> &rsaquo;</a>
657                                             <a href="https://www.letsrentz.com/1-bhk-flats-for-sale-in-mohali" class="bl-loc-row"><span>1 BHK Flats for Sale in Mohali | 1 BHK Flats in Mohali</span> &rsaquo;</a>
658                                             <a href="https://www.letsrentz.com/2-bhk-flats-for-sale-in-mohali" class="bl-loc-row"><span>2 BHK Flats for Sale in Mohali | 2 BHK Flats in Mohali</span> &rsaquo;</a>
659                                             <a href="https://www.letsrentz.com/3-bhk-flats-for-sale-in-mohali" class="bl-loc-row"><span>3 BHK Flats for Sale in Mohali | 3 BHK Flats in Mohali</span> &rsaquo;</a>
660                                       </div>
661               </div>
662                              <div class="mobile-filter-section-card">
663                  <div class="mobile-filter-section-header">Amenities</div>
664                  <div class="bl-filter-body bl-mobile-amenities" id="amenities-container-mobile">
665                     <label class="bl-check-line"><input type="checkbox" class="amenity-checkbox-mobile" value="parking">Parking</label>
666                     <label class="bl-check-line"><input type="checkbox" class="amenity-checkbox-mobile" value="gated_security">Gated Society</label>
667                     <label class="bl-check-line"><input type="checkbox" class="amenity-checkbox-mobile" value="park">Park</label>
668                     <label class="bl-check-line"><input type="checkbox" class="amenity-checkbox-mobile" value="power_backup">Power Backup</label>
669                     <label class="bl-check-line bl-more-hidden" style="display:none;"><input type="checkbox" class="amenity-checkbox-mobile" value="gym">Gym</label>
670                     <label class="bl-check-line bl-more-hidden" style="display:none;"><input type="checkbox" class="amenity-checkbox-mobile" value="club_house">Club House</label>
671                     <label class="bl-check-line bl-more-hidden" style="display:none;"><input type="checkbox" class="amenity-checkbox-mobile" value="swimming_pool">Swimming Pool</label>
672                     <label class="bl-check-line bl-more-hidden" style="display:none;"><input type="checkbox" class="amenity-checkbox-mobile" value="lift">Lift</label>
673                     <label class="bl-check-line bl-more-hidden" style="display:none;"><input type="checkbox" class="amenity-checkbox-mobile" value="fire_safety">Fire Safety</label>
674                     <label class="bl-check-line bl-more-hidden" style="display:none;"><input type="checkbox" class="amenity-checkbox-mobile" value="intercom">Intercom</label>
675                     <label class="bl-check-line bl-more-hidden" style="display:none;"><input type="checkbox" class="amenity-checkbox-mobile" value="children_play_area">Kids Play Area</label>
676                     <label class="bl-check-line bl-more-hidden" style="display:none;"><input type="checkbox" class="amenity-checkbox-mobile" value="shopping_center">Shopping Center</label>
677                     <label class="bl-check-line bl-more-hidden" style="display:none;"><input type="checkbox" class="amenity-checkbox-mobile" value="sewage_treatment_plant">Waste Treatment</label>
678                     <label class="bl-check-line bl-more-hidden" style="display:none;"><input type="checkbox" class="amenity-checkbox-mobile" value="visitor_par
678king">Visitor Parking</label>
679                     <label class="bl-check-line bl-more-hidden" style="display:none;"><input type="checkbox" class="amenity-checkbox-mobile" value="gas_pipeline">Gas Pipeline</label>
680                     <label class="bl-check-line bl-more-hidden" style="display:none;"><input type="checkbox" class="amenity-checkbox-mobile" value="internet_provider">Internet Provider</label>
681                     <label class="bl-check-line bl-more-hidden" style="display:none;"><input type="checkbox" class="amenity-checkbox-mobile" value="water_supply">Water Supply</label>
682                     <span class="more-link" id="amenitiesMoreLinkMobile">+ 13 more</span>
683                  </div>
684               </div>
685               
686               <div class="mobile-filter-section-card bl-filter-not-in-design">
687                  <div class="mobile-filter-section-header">Property Age</div>
688                  <label class="bl-check-line"><input type="checkbox" class="property-age-checkbox-mobile" value="0-1 years">0-1 year</label>
689                  <label class="bl-check-line"><input type="checkbox" class="property-age-checkbox-mobile" value="1-5 years">1-5 year</label>
690                  <label class="bl-check-line"><input type="checkbox" class="property-age-checkbox-mobile" value="5-10 years">5-10 year</label>
691                  <label class="bl-check-line"><input type="checkbox" class="property-age-checkbox-mobile" value="10+ years">10+ year</label>
692               </div>
693               <div class="mobile-filter-section-card">
694                  <div class="mobile-filter-section-header">Budget</div>
695                  <div class="bl-row mt-2">
696                     <select id="budgetMinMobile">
697                        <option value="0">No min</option>
698                        <option value="2500000">25 L</option>
699                        <option value="5000000">50 L</option>
700                        <option value="7500000">75 L</option>
701                        <option value="10000000">1 Cr</option>
702                        <option value="15000000">1.5 Cr</option>
703                        <option value="20000000">2 Cr</option>
704                        <option value="30000000">3 Cr</option>
705                        <option value="50000000">5 Cr</option>
706                     </select>
707                     <select id="budgetMaxMobile">
708                        <option value="1000000000">No max</option>
709                        <option value="5000000">50 L</option>
710                        <option value="10000000">1 Cr</option>
711                        <option value="15000000">1.5 Cr</option>
712                        <option value="20000000">2 Cr</option>
713                        <option value="30000000">3 Cr</option>
714                        <option value="50000000">5 Cr</option>
715                        <option value="100000000">10 Cr</option>
716                     </select>
717                  </div>
718               </div>
719               <div class="mobile-filter-section-card">
720                  <div class="mobile-filter-section-header">RERA Status</div>
721                  <label class="bl-check-line"><input type="checkbox" class="rera-status-checkbox-mobile" value="rera_approved">RERA Approved Properties</label>
722               </div>
723            </div>
724            <div class="modal-footer">
725               <div><span id="mobileFilterResultsCount">0</span> Property found</div>
726               <button type="button" class="btn btn-view-results btn_apply_filters" data-bs-dismiss="modal">View Results</button>
727            </div>
728         </div>
729      </div>
730   </div>
731
732   
733   <div class="modal fade mobile-updated-dates-modal" id="updatedDatesModal" tabindex="-1" aria-modal="true">
734      <div class="modal-dialog modal-dialog-scrollable modal-fullscreen">
735         <div class="modal-content">
736            <div class="modal-header mobile-updated-dates-modal-header"><h5 class="modal-title">Updated Dates</h5></div>
737            <div class="modal-body">
738               <div class="form-check"><input class="form-check-input" type="radio" name="updatedDateOption" id="updatedDateAll" value="all" checked><label class="form-check-label" for="updatedDateAll">All</label></div>
739               <div class="form-check"><input class="form-check-input" type="radio" name="updatedDateOption" id="updatedDateLast7Days" value="last-7-days"><label class="form-check-label" for="updatedDateLast7Days">Last 7 Days</label></div>
740               <div class="form-check"><input class="form-check-input" type="radio" name="updatedDateOption" id="updatedDateLast15Days" value="last-15-days"><label class="form-check-label" for="updatedDateLast15Days">Last 15 Days</label></div>
741               <div class="form-check"><input class="form-check-input" type="radio" name="updatedDateOption" id="updatedDateLast1Month" value="last-1-month"><label class="form-check-label" for="updatedDateLast1Month">Last 1 Month</label></div>
742            </div>
743            <div class="modal-footer"><button type="button" class="btn-done-updated-dates" data-bs-dismiss="modal">Done</button></div>
744         </div>
745      </div>
746   </div>
747
748   
749   <div class="modal fade mobile-sort-by-modal" id="sortByModal" tabindex="-1" aria-labelledby="sortByModalLabel">
750      <div class="modal-dialog modal-dialog-scrollable modal-fullscreen">
751         <div class="modal-content">
752            <div class="modal-header mobile-sort-by-modal-header">
753               <h5 class="modal-title mobile-sort-by-modal-title" id="sortByModalLabel">Sort By</h5>
754            </div>
755            <div class="modal-body mobile-sort-by-modal-body">
756               <div class="mobile-sort-options">
757                  <div class="form-check mobile-sort-option-item"><input class="form-check-input" type="radio" name="sortOption" id="sortMostRelevant" value="most-relevant" checked><label class="form-check-label" for="sortMostRelevant">Most Relevant</label></div>
758                  <div class="form-check mobile-sort-option-item"><input class="form-check-input" type="radio" name="sortOption" id="sortPriceLow" value="price-low"><label class="form-check-label" for="sortPriceLow">Price: Low to High</label></div>
759                  <div class="form-check mobile-sort-option-item"><input class="form-check-input" type="radio" name="sortOption" id="sortPriceHigh" value="price-high"><label class="form-check-label" for="sortPriceHigh">Price: High to Low</label></div>
760                  <div class="form-check mobile-sort-option-item"><input class="form-check-input" type="radio" name="sortOption" id="sort
760Newest" value="newest"><label class="form-check-label" for="sortNewest">Newest First</label></div>
761               </div>
762            </div>
763            <div class="modal-footer mobile-sort-by-footer"><button type="button" class="btn btn-apply-sort" id="sort_by_mobile" data-bs-dismiss="modal">Done</button></div>
764         </div>
765      </div>
766   </div>
767</div>
768
769<script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyDSj3nda3aEN7QXQNaDllvJQxkwAGBbDkM&libraries=places&callback=initGoogleAutocompleteListing" async defer></script>
vendor: 1 bytes, line 769
769
770<script>
771function initGoogleAutocompleteListing() {
772   const inputs = document.querySelectorAll('.autocomplete-google-listing, .autocomplete-google');
773   inputs.forEach(input => {
774      if (window.getComputedStyle(input).display === "none") return;
775      const autocomplete = new google.maps.places.Autocomplete(input, {
776         types: ['geocode'],
777         componentRestrictions: { country: 'in' }
778      });
779      // Matches the live /buy/... page: selecting a place here fills the
780      // input via Google's widget but isn't wired to re-run the search
781      // (search-by-typed-address isn't functional there either today).
782      autocomplete.addListener('place_changed', function () {});
783   });
784}
785window.initGoogleAutocompleteListing = initGoogleAutocompleteListing;
786
787$(document).ready(function() {
788   var buySellPropertyTypes = [{"id":1,"name":"Flat\/Apartment","parent_id":0,"status":1,"created_at":"2025-05-11T18:34:09.000000Z","updated_at":"2025-05-11T18:34:09.000000Z","sub_property_type":[{"id":7,"name":"1BHK","parent_id":1,"status":1,"created_at":"2025-05-11T18:36:06.000000Z","updated_at":"2025-05-11T18:36:06.000000Z"},{"id":8,"name":"2BHK","parent_id":1,"status":1,"created_at":"2025-05-11T18:36:24.000000Z","updated_at":"2025-05-11T18:36:24.000000Z"},{"id":10,"name":"3+1 BHK","parent_id":1,"status":1,"created_at":"2025-05-11T18:36:45.000000Z","updated_at":"2025-05-11T18:36:45.000000Z"},{"id":9,"name":"3BHK","parent_id":1,"status":1,"created_at":"2025-05-11T18:36:33.000000Z","updated_at":"2025-05-11T18:36:33.000000Z"},{"id":12,"name":"4+1 BHK","parent_id":1,"status":1,"created_at":"2025-05-11T18:37:03.000000Z","updated_at":"2025-05-11T18:37:03.000000Z"},{"id":11,"name":"4BHK","parent_id":1,"status":1,"created_at":"2025-05-11T18:36:53.000000Z","updated_at":"2025-05-11T18:36:53.000000Z"},{"id":14,"name":"5+1 BHK","parent_id":1,"status":1,"created_at":"2025-05-11T18:37:23.000000Z","updated_at":"2025-05-11T18:37:23.000000Z"},{"id":13,"name":"5BHK","parent_id":1,"status":1,"created_at":"2025-05-11T18:37:13.000000Z","updated_at":"2025-05-11T18:37:13.000000Z"},{"id":15,"name":"6BHK","parent_id":1,"status":1,"created_at":"2025-05-11T18:37:34.000000Z","updated_at":"2025-05-11T18:37:34.000000Z"},{"id":16,"name":"Studio Apartment","parent_id":1,"status":1,"created_at":"2025-05-11T18:38:11.000000Z","updated_at":"2025-05-11T18:38:11.000000Z"}]},{"id":2,"name":"Independent House\/Villa","parent_id":0,"status":1,"created_at":"2025-05-11T18:34:25.000000Z","updated_at":"2025-05-11T18:34:25.000000Z","sub_property_type":[{"id":17,"name":"1BHK","parent_id":2,"status":1,"created_at":"2025-05-11T18:38:38.000000Z","updated_at":"2025-05-11T18:38:38.000000Z"},{"id":18,"name":"2BHK","parent_id":2,"status":1,"created_at":"2025-05-11T18:38:55.000000Z","updated_at":"2025-05-11T18:38:55.000000Z"},{"id":19,"name":"3BHK","parent_id":2,"status":1,"created_at":"2025-05-11T18:39:04.000000Z","updated_at":"2025-05-11T18:39:04.000000Z"},{"id":20,"name":"4BHK","parent_id":2,"status":1,"created_at":"2025-05-11T18:39:12.000000Z","updated_at":"2025-05-11T18:39:12.000000Z"},{"id":21,"name":"5BHK","parent_id":2,"status":1,"created_at":"2025-05-11T18:39:21.000000Z","updated_at":"2025-05-11T18:39:21.000000Z"},{"id":22,"name":"6BHK","parent_id":2,"status":1,"created_at":"2025-05-11T18:39:29.000000Z","updated_at":"2025-05-11T18:39:29.000000Z"},{"id":49,"name":"7BHK","parent_id":2,"status":1,"created_at":"2025-06-19T10:53:37.000000Z","updated_at":"2025-06-19T10:53:37.000000Z"},{"id":50,"name":"8BHK","parent_id":2,"status":1,"created_at":"2025-06-19T10:54:02.000000Z","updated_at":"2025-06-19T10:54:02.000000Z"},{"id":51,"name":"9BHK","parent_id":2,"status":1,"created_at":"2025-06-19T10:54:23.000000Z","updated_at":"2025-06-19T10:54:23.000000Z"}]},{"id":3,"name":"Independent Builder Floor","parent_id":0,"status":1,"created_at":"2025-05-11T18:35:15.000000Z","updated_at":"2025-05-25T09:47:30.000000Z","sub_property_type":[{"id":23,"name":"1BHK","parent_id":3,"status":1,"created_at":"2025-05-11T18:39:56.000000Z","updated_at":"2025-05-11T18:39:56.000000Z"},{"id":24,"name":"2BHK","parent_id":3,"status":1,"created_at":"2025-05-11T18:40:05.000000Z","updated_at":"2025-05-11T18:40:05.000000Z"},{"id":26,"name":"3+1 BHK","parent_id":3,"status":1,"created_at":"2025-05-11T18:40:20.000000Z","updated_at":"2025-05-11T18:40:20.000000Z"},{"id":25,"name":"3BHK","parent_id":3,"status":1,"created_at":"2025-05-11T18:40:11.000000Z","updated_at":"2025-05-11T18:40:11.000000Z"},{"id":28,"name":"4+1 BHK","parent_id":3,"status":1,"created_at":"2025-05-11T18:40:36.000000Z","updated_at":"2025-05-11T18:40:36.000000Z"},{"id":27,"name":"4BHK","parent_id":3,"status":1,"created_at":"2025-05-11T18:40:28.000000Z","updated_at":"2025-05-11T18:40:28.000000Z"},{"id":30,"name":"5+1 BHK","parent_id":3,"status":1,"created_at":"2025-05-11T18:40:53.000000Z","updated_at":"2025-05-11T18:40:53.000000Z"},{"id":29,"name":"5BHK","parent_id":3,"status":1,"created_at":"2025-05-11T18:40:45.000000Z","updated_at":"2025-05-11T18:40:45.000000Z"}
788,{"id":31,"name":"6BHK","parent_id":3,"status":1,"created_at":"2025-05-11T18:41:01.000000Z","updated_at":"2025-05-11T18:41:01.000000Z"}]},{"id":4,"name":"Plot\/Land","parent_id":0,"status":1,"created_at":"2025-05-11T18:35:22.000000Z","updated_at":"2025-05-11T18:35:22.000000Z","sub_property_type":[{"id":32,"name":"Plot","parent_id":4,"status":1,"created_at":"2025-05-11T18:41:51.000000Z","updated_at":"2025-05-11T18:41:51.000000Z"}]},{"id":5,"name":"Farmhouse","parent_id":0,"status":1,"created_at":"2025-05-11T18:35:30.000000Z","updated_at":"2025-05-11T18:35:30.000000Z","sub_property_type":[{"id":33,"name":"2BHK","parent_id":5,"status":1,"created_at":"2025-05-11T18:42:12.000000Z","updated_at":"2025-05-11T18:42:12.000000Z"},{"id":34,"name":"3BHK","parent_id":5,"status":1,"created_at":"2025-05-11T18:42:20.000000Z","updated_at":"2025-05-11T18:42:20.000000Z"},{"id":35,"name":"4BHK","parent_id":5,"status":1,"created_at":"2025-05-11T18:42:28.000000Z","updated_at":"2025-05-11T18:42:28.000000Z"},{"id":36,"name":"5BHK","parent_id":5,"status":1,"created_at":"2025-05-11T18:42:36.000000Z","updated_at":"2025-05-11T18:42:36.000000Z"},{"id":37,"name":"6BHK","parent_id":5,"status":1,"created_at":"2025-05-11T18:42:43.000000Z","updated_at":"2025-05-11T18:42:43.000000Z"},{"id":38,"name":"7BHK","parent_id":5,"status":1,"created_at":"2025-05-11T18:42:53.000000Z","updated_at":"2025-05-11T18:42:53.000000Z"},{"id":39,"name":"8BHK","parent_id":5,"status":1,"created_at":"2025-05-11T18:43:04.000000Z","updated_at":"2025-05-11T18:43:15.000000Z"},{"id":40,"name":"9BHK","parent_id":5,"status":1,"created_at":"2025-05-11T18:43:27.000000Z","updated_at":"2025-05-11T18:43:27.000000Z"}]},{"id":6,"name":"Commercial","parent_id":0,"status":1,"created_at":"2025-05-11T18:35:42.000000Z","updated_at":"2025-05-11T18:35:42.000000Z","sub_property_type":[{"id":43,"name":"Co-Working","parent_id":6,"status":1,"created_at":"2025-05-11T18:44:10.000000Z","updated_at":"2025-05-11T18:44:10.000000Z"},{"id":45,"name":"Godown\/Warehouse","parent_id":6,"status":1,"created_at":"2025-05-11T18:44:30.000000Z","updated_at":"2025-05-11T18:44:30.000000Z"},{"id":47,"name":"Hotel\/Resorts","parent_id":6,"status":1,"created_at":"2025-05-11T18:44:45.000000Z","updated_at":"2025-05-11T18:44:45.000000Z"},{"id":46,"name":"Industrial Building","parent_id":6,"status":1,"created_at":"2025-05-11T18:44:38.000000Z","updated_at":"2025-05-11T18:44:38.000000Z"},{"id":41,"name":"Office Space","parent_id":6,"status":1,"created_at":"2025-05-11T18:43:53.000000Z","updated_at":"2025-05-11T18:43:53.000000Z"},{"id":48,"name":"Other Business","parent_id":6,"status":1,"created_at":"2025-05-11T18:44:54.000000Z","updated_at":"2025-05-11T18:44:54.000000Z"},{"id":42,"name":"Plot","parent_id":6,"status":1,"created_at":"2025-05-11T18:44:02.000000Z","updated_at":"2025-05-11T18:44:02.000000Z"},{"id":44,"name":"Showroom","parent_id":6,"status":1,"created_at":"2025-05-11T18:44:22.000000Z","updated_at":"2025-05-11T18:44:22.000000Z"}]}];
789   var furnishTypes = [{"id":1,"name":"Fully Furnished","status":1,"created_at":"2022-07-17T11:02:24.000000Z","updated_at":"2022-07-17T11:02:24.000000Z"},{"id":2,"name":"Semi Furnished","status":1,"created_at":"2022-07-17T11:02:32.000000Z","updated_at":"2022-07-17T11:02:32.000000Z"},{"id":3,"name":"Unfurnished","status":1,"created_at":"2022-07-17T11:02:40.000000Z","updated_at":"2022-07-17T11:02:40.000000Z"}];
790   var selected_property_type_id = '1';
791   var offset = 0;
792   var min_price = '0';
793   var max_price = '1000000000';
794   var property_type_ids = [""+1];
795   var category_ids = [];
796   var furnished_ids = [];
797   var bedroom_ids = [];
798   var sortBy = 'distance';
799   var sortByOrder = 'asc';
800   var lati = '30.7046';
801   var longi = '76.7179';
802   var address = 'Sector 71 Mohali';
803   var seaching_title = 'Flats/Apartments for sale in Sector 71 Mohali';
804   var construction_status_ids = [];
805   var selling_type_ids = [];
806   var rera_status_ids = [];
807   var listed_by_types = [];
808   var age_of_property_ids = [];
809   var min_area = "";
810   var max_area = "";
811   var amenity_ids = [];
812
813   // Desktop transaction switcher. Keep the real property-type filter in
814   // #selected_property_type while routing Rent to the equivalent address.
815   $('#listing_transaction_type').on('change', function () {
816      if (this.value !== 'rent') return;
817      var cleanAddress = String(address || '').trim().replace(/^Flat-for-sale-in-/i, '');
818      var rentSlug = 'Flat-for-rent-in-' + cleanAddress.replace(/\s+/g, '-');
819      window.location.href = 'https://www.letsrentz.com/propertysearch/' + encodeURIComponent(rentSlug) + '/1/' + lati + '/' + longi;
820   });
821   var updated_since = "all";
822   var selected_category_id = [];
823   var selected_furnished_id = [];
824   var user_data = null;
825   var user_id = user_data ? user_data.user_id : '';
826
827   $('#bl_address_desktop, #bl_address_mobile').val(address);
828
829   // ---------- Budget (dual select, matches the new HTML design) ----------
830   $('#budgetMin, #budgetMax').val(function(){
831      return this.id === 'budgetMin' ? min_price : max_price;
832   });
833   $('#budgetMin, #budgetMax').on('change', function () {
834      min_price = parseInt($('#budgetMin').val());
835      max_price = parseInt($('#budgetMax').val());
836      offset = 0;
837      filteredProperties(offset, 'fromfunction');
838   });
839   $('#budgetMinMobile, #budgetMaxMobile').on('change', function () {
840      min_price = parseInt($('#budgetMinMobile').val());
841      max_price = parseInt($('#budgetMaxMobile').val());
842   });
843
844   // ---------- Show first 5 items, "+ N more" reveals the rest
845   // (matches the new HTML design's "+ 1 more" / "+ 5 more" pattern) ----------
846   function appendWithShowMore(containerSelector, items, limit) {
847      limit = limit || 5;
848      items.forEach(function (item, idx) {
849         if (idx >= limit) { item.hide(); }
850         $(containerSelector).append(item);
851      });
852      if (items.length > limit) {
853         var moreLink = $('<span class="more-link">+ ' + (items.length - limit) + ' more</span>');
854         moreLink.on('click', function () {
855            $(containerSelector).children().show();
856            $(this).remove();
857         });
858         $(containerSelector).append(moreLink);
859      }
860   }
861
862   // ---------- Category (sub-type) checkboxes: populate for the
863   // currently selected top-level property type ----------
864   function populateCategoryCheckboxes(containerSelector, checkboxClass) {
865      $(containerSelector).empty();
866      var items = [];
867      buySellPropertyTypes.forEach(function(property_type) {
868         if (property_type.id == property_type_ids[0]) {
869            property_type.sub_property_type.forEach(function(sub_category) {
870               var wrap = $('<label class="bl-check-line">');
871               var checkbox = $('<input type="checkbox" class="' + checkboxClass + '" value="' + sub_category.id + '">');
872               checkbox.prop('checked', selected_category_id.indexOf(String(sub_category.id)) !== -1 || selected_category_id.indexOf(sub_category.id) !== -1);
873               wrap.append(checkbox).append(document.createTextNode(sub_category.name));
874               items.push(wrap);
875            });
876         }
877      });
878      appendWithShowMore(containerSelector, items, 5);
879   }
880   populateCategoryCheckboxes('#category-container', 'sub_category_checkbox');
881   populateCategoryCheckboxes('#category_container1', 'sub_category_checkbox1');
882   if (selected_category_id.length > 0) {
883      selected_category_id.forEach(function(cat_id){ category_ids.push('' + cat_id); });
884   }
885
886   $('#category-container').on('change', '.sub_category_checkbox', function() {
887      toggleInArray(category_ids, $(this).val(), $(this).is(':checked'));
888      offset = 0;
889      filteredProperties(offset, 'fromfunction');
890   });
891   $('#category_container1').on('change', '.sub_category_checkbox1', function() {
892      toggleInArray(category_ids, $(this).val(), $(this).is(':checked'));
893   });
894
895   function refreshCategoryForPropertyType() {
896      category_ids = [];
897      populateCategoryCheckboxes('#category-container', 'sub_category_checkbox');
898      populateCategoryCheckboxes('#category_container1', 'sub_category_checkbox1');
899   }
900
901   $('#selected_property_type').val(selected_property_type_id).on('change', function() {
902      property_type_ids = ["" + $(this).val()];
903      $('#selected_property_type_mobile').val($(this).val());
904      refreshCategoryForPropertyType();
905      offset = 0;
906      filteredProperties(offset, 'fromfunction');
907   });
908   $('#selected_property_type_mobile').val(selected_property_type_id).on('change', function() {
909      property_type_ids = ["" + $(this).val()];
910      $('#selected_property_type').val($(this).val());
911      refreshCategoryForPropertyType();
912      offset = 0;
913      filteredProperties(offset, 'fromfunction');
914   });
915
916   // ---------- Furnish type checkboxes ----------
917   function populateFurnishCheckboxes(containerSelector, checkboxClass) {
918      furnishTypes.forEach(function(furnish) {
919         var wrap = $('<label class="bl-check-line">');
920         var checkbox = $('<input type="checkbox" class="' + checkboxClass + '" value="' + furnish.id + '">');
921         checkbox.prop('checked', selected_furnished_id.indexOf(String(furnish.id)) !== -1 || selected_furnished_id.indexOf(furnish.id) !== -1);
922         wrap.append(checkbox).append(document.createTextNode(furnish.name));
923         $(containerSelector).append(wrap);
924      });
925   }
926   populateFurnishCheckboxes('#furnish-container', 'furnish-checkbox');
927   populateFurnishCheckboxes('#furnish_container1', 'furnish-checkbox-mobile');
928   if (selected_furnished_id.length > 0) {
929      selected_furnished_id.forEach(function(furn_id){ furnished_ids.push('' + furn_id); });
930   }
931
932   $('#furnish-container').on('change', '.furnish-checkbox', function () {
933      toggleInArray(furnished_ids, $(this).val(), $(this).is(':checked'));
934      offset = 0;
935      filteredProperties(offset, 'fromfunction');
936   });
937   $('#furnish_container1').on('change', '.furnish-checkbox-mobile', function () {
938      toggleInArray(furnished_ids, $(this).val(), $(this).is(':checked'));
939   });
940
941   // ---------- Generic helper: toggle a value in/out of an array ----------
942   function toggleInArray(arr, value, checked) {
943      if (checked) {
944         if (arr.indexOf(value) === -1) arr.push(value);
945      } else {
946         var index = arr.indexOf(value);
947         if (index !== -1) arr.splice(index, 1);
948      }
949   }
950
951   // Property Status
952   $('.construction-status-checkbox').on('change', function () { toggleInArray(construction_status_ids, $(this).val(), $(this).is(':checked')); offset = 0; filteredProperties(offset, 'fromfunction'); });
953   $('.construction-status-checkbox-mobile').on('change', function () { toggleInArray(construction_status_ids, $(this).val(), $(this).is(':checked')); });
954   // Purchase Type
955   $('.selling-type-checkbox').on('change', function () { toggleInArray(selling_type_ids, $(this).val(), $(this).is(':checked')); offset = 0; filteredProperties(offset, 'fromfunction'); });
956   $('.selling-type-checkbox-mobile').on('change', function () { toggleInArray(selling_type_ids, $(this).val(), $(this).is(':checked')); });
957   // RERA
958   $('.rera-status-checkbox').on('change', function () { toggleInArray(rera_status_ids, $(this).val(), $(this).is(':checked')); offset = 0; filteredProperties(offset, 'fromfunction'); });
959   $('.rera-status-checkbox-mobile').on('change', function () { toggleInArray(rera_status_ids, $(this).val(), $(this).is(':checked')); });
960   // Listed By
961   $('.listed-by-checkbox').on('change', function () { toggleInArray(listed_by_types, $(this).val(), $(this).is(':checked')); offset = 0; filteredProperties(offset, 'fromfunction'); });
962   $('.listed-by-checkbox-mobile').on('change', function () { toggleInArray(listed_by_types, $(this).val(), $(this).is(':checked')); });
963   // Property Age
964   $('.property-age-checkbox').on('change', function () { toggleInArray(age_of_property_ids, $(this).val(), $(this).is(':checked')); offset = 0; filteredProperties(offset, 'fromfunction'); });
965   $('.property-age-checkbox-mobile').on('change', function () { toggleInArray(age_of_property_ids, $(this).val(), $(this).is(':checked')); });
966
967   // Amenities (real filter against buy_sell_property_amenities / covered_parking+open_parking,
968   // matches the property must have ALL selected amenities - see applyAmenityFilters() server-side)
969   $('.amenity-checkbox').on('change', function () { toggleInArray(amenity_ids, $(this).val(), $(this).is(':checked')); offset = 0; filteredProperties(offset, 'fromfunction'); });
970   $('.amenity-checkbox-mobile').on('change', function () { toggleInArray(amenity_ids, $(this).val(), $(this).is(':checked')); });
971   $('#amenitiesMoreLink').on('click', function () {
972      $('#amenities-container .bl-more-hidden').show();
973      $(this).remove();
974   });
975   $('#amenitiesMoreLinkMobile').on('click', function () {
976      $('#amenities-container-mobile .bl-more-hidden').show();
977      $(this).remove();
978   });
979
980   // "+ 5 more" reveals the hidden 6-10 BHK chips (matches the new HTML
981   // design's "+ N more" pattern)
982   $('#bedroomMoreLink').on('click', function () {
983      $('#bedroom-container .bl-more-hidden').show();
984      $(this).remove();
985   });
986   $('#bedroomMoreLinkMobile').on('click', function () {
987      $('#bedroom-container-mobile .bl-more-hidden').show();
988      $(this).remove();
989   });
990
991   // Bedrooms (new filter, wired to the real `bedroom` column)
992   $('.bedroom-chip').on('click', function () {
993      $(this).toggleClass('active');
994      toggleInArray(bedroom_ids, $(this).data('value').toString(), $(this).hasClass('active'));
995      offset = 0;
996      filteredProperties(offset, 'fromfunction');
997   });
998   $('.bedroom-chip-mobile').on('click', function () {
999      $(this).toggleClass('active');
1000      toggleInArray(bedroom_ids, $(this).data('value').toString(), $(this).hasClass('active'));
1001   });
1002
1003   // Area
1004   $('#minAreaInput, #maxAreaInput').on('change', function () { min_area = $('#minAreaInput').val(); max_area = $('#maxAreaInput').val(); offset = 0; filteredProperties(offset, 'fromfunction'); });
1005   $('#minAreaInput1, #maxAreaInput1').on('change', function () { min_area = $('#minAreaInput1').val(); max_area = $('#maxAreaInput1').val(); });
1006
1007   // Apply mobile-deferred filters
1008   $('.btn_apply_filters').on('click', function () { offset = 0; filteredProperties(offset, 'fromfunction'); });
1009
1010   // Clear all filters
1011   $('#clearFilters').on('click', function () {
1012      $('.construction-status-checkbox, .construction-status-checkbox-mobile,' +
1013        '.selling-type-checkbox, .selling-type-checkbox-mobile,' +
1014        '.rera-status-checkbox, .rera-status-checkbox-mobile,' +
1015        '.listed-by-checkbox, .listed-by-checkbox-mobile,' +
1016        '.property-age-checkbox, .property-age-checkbox-mobile,' +
1017        '.furnish-checkbox, .furnish-checkbox-mobile,' +
1018        '.sub_category_checkbox, .sub_category_checkbox1,' +
1019        '.amenity-checkbox, .amenity-checkbox-mobile').prop('checked', false);
1020      $('.bedroom-chip, .bedroom-chip-mobile').removeClass('active');
1021
1022      construction_status_ids = []; selling_type_ids = []; rera_status_ids = [];
1023      listed_by_types = []; age_of_property_ids = []; furnished_ids = [];
1024      category_ids = []; bedroom_ids = []; amenity_ids = [];
1025      min_area = ""; max_area = "";
1026      $('#minAreaInput, #minAreaInput1, #maxAreaInput, #maxAreaInput1').val('');
1027      updated_since = "all";
1028      $('#updated_dates').val('all');
1029      $('#updatedDateAll').prop('checked', true);
1030
1031      min_price = 0; max_price = 1000000000;
1032      $('#budgetMin, #budgetMinMobile').val('0');
1033      $('#budgetMax, #budgetMaxMobile').val('1000000000');
1034
1035      offset = 0;
1036      filteredProperties(offset, 'fromfunction');
1037   });
1038
1039   // Sort
1040   $('#sort_by').on('change', function () {
1041      var v = $(this).val();
1042      if (v == "Recently added") { sortBy = "updated_at"; sortByOrder = "desc"; }
1043      else if (v == "Price: Low to High") { sortBy = "price"; sortByOrder = "asc"; }
1044      else if (v == "Price: High to Low") { sortBy = "price"; sortByOrder = "desc"; }
1045      else { sortBy = "distance"; sortByOrder = "asc"; }
1046      offset = 0;
1047      filteredProperties(offset, 'fromfunction');
1048   });
1049   $('#sort_by_mobile').on('click', function () {
1050      var v = $('input[name="sortOption"]:checked').val();
1051      if (v == "newest") { sortBy = "updated_at"; sortByOrder = "desc"; }
1052      else if (v == "price-low") { sortBy = "price"; sortByOrder = "asc"; }
1053      else if (v == "price-high") { sortBy = "price"; sortByOrder = "desc"; }
1054      else { sortBy = "distance"; sortByOrder = "asc"; }
1055      offset = 0;
1056      filteredProperties(offset, 'fromfunction');
1057   });
1058
1059   // Updated Since
1060   $('#updated_dates').on('change', function () { updated_since = $(this).val(); offset = 0; filteredProperties(offset, 'fromfunction'); });
1061   $('.btn-done-updated-dates').on('click', function () {
1062      var selected = $('input[name="updatedDateOption"]:checked').val();
1063      var mobileMap = { 'all': 'all', 'last-7-days': '7', 'last-15-days': '15', 'last-1-month': '30' };
1064      updated_since = mobileMap[selected] || 'all';
1065      offset = 0;
1066      filteredProperties(offset, 'fromfunction');
1067   });
1068
1069   // Show more / results fetch
1070   $(document).on('click', '.btn-show-more', function () { filteredProperties($(this).data('offset')); });
1071
1072   function filteredProperties(offsetArg, coming_from) {
1073      $('.btn-show-more').prop('disabled', true).text('Loading...');
1074      if (offsetArg == 0) { $('#propertyListings').empty(); }
1075      $.ajax({
1076         url: 'https://www.letsrentz.com/buy/filteredProperties',
1077         method: 'GET',
1078         data: {
1079            preview: 1,
1080            property_type_ids: property_type_ids,
1081            category_ids: category_ids,
1082            furnished_id: furnished_ids,
1083            bedroom_ids: bedroom_ids,
1084            offset: offsetArg,
1085            min_price: min_price,
1086            max_price: max_price,
1087            sortBy: sortBy,
1088            sortByOrder: sortByOrder,
1089            lati: lati,
1090            longi: longi,
1091                        user_id: user_id,
1092            construction_status_ids: construction_status_ids,
1093            selling_type_ids: selling_type_ids,
1094            rera_status_ids: rera_status_ids,
1095            listed_by_types: listed_by_types,
1096            age_of_property_ids: age_of_property_ids,
1097            min_area: min_area,
1098            max_area: max_area,
1099            updated_since: updated_since,
1100            amenity_ids: amenity_ids,
1101            address: address
1102         },
1103         success: function (data) {
1104            if (data.status) {
1105               if (offsetArg == 0) { $('#propertyListings').html(data.html); }
1106               else { $('#propertyListings').append(data.html); }
1107            }
1108            $('#mobileFilterResultsCount').text(data.resultCount);
1109            $('.property-results').text(data.resultCount + "+ " + seaching_title);
1110            $('.btn-show-more').data('offset', data.offset);
1111            if (!data.has_more) { $('.btn-show-more').hide(); }
1112            else { $('.btn-show-more').prop('disabled', false).text('Show More').show(); }
1113         },
1114         error: function (error) { console.log(error); }
1115      });
1116   }
1117
1118   offset = 0;
1119   filteredProperties(offset, 'direct');
1120
1121   // Save / wishlist heart button - mirrors the working .lp-card-save
1122   // handler already live on the redesigned rent listing page
1123   // (front/property/listing.blade.php), since this button has no
1124   // handler anywhere in the current live buy-listing page.
1125   $(document).off('click', '.bl-card-save').on('click', '.bl-card-save', function (e) {
1126      e.preventDefault();
1127      e.stopPropagation();
1128      if (user_data == null) { openLoginPopup(event); return; }
1129      var btn = $(this);
1130      $.ajax({
1131         type: 'POST',
1132         url: 'https://www.letsrentz.com/wishListProperty',
1133         headers: { 'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content') },
1134         data: { property_id: btn.attr('data-property-id'), type: btn.attr('data-property-type') },
1135         success: function (response) {
1136            if (response.status) {
1137               btn.toggleClass('active', response.is_wishlist == 1);
1138               btn.attr('data-property-status', response.is_wishlist);
1139            }
1140         }
1141      });
1142   });
1143});
1144</script>
1144
1145
1146         <footer class="hp-footer">
1147      <div class="hp-footer-grid">
1148         <div>
1149            <a href="https://www.letsrentz.com" class="hp-footer-logo-link">
1150               <img src="https://www.letsrentz.com/front/assets/images/listing_images/footer.png" alt="Lets Rentz" class="hp-footer-logo">
1151            </a>
1152            <p class="hp-footer-tagline">You are one click away from your next dream property. Verified listings, zero brokerage on rent.</p>
1153            <div class="hp-footer-socials">
1154               <a href="https://www.instagram.com/letsrentz/" target="_blank" class="hp-footer-social"><i class="bi bi-instagram"></i></a>
1155               <a href="https://twitter.com/LetsRentz" target="_blank" class="hp-footer-social"><i class="bi bi-twitter"></i></a>
1156               <a href="https://www.facebook.com/letsrentz/" target="_blank" class="hp-footer-social"><i class="bi bi-facebook"></i></a>
1157               <a href="https://www.linkedin.com/company/lets-rentz/" target="_blank" class="hp-footer-social"><i class="bi bi-linkedin"></i></a>
1158               <a href="https://www.youtube.com/channel/UCr3A91ZLRcN0tFUzThC543g" target="_blank" class="hp-footer-social"><i class="bi bi-youtube"></i></a>
1159            </div>
1160         </div>
1161         <div class="hp-footer-cols-wrap">
1162            <div class="hp-footer-col-company">
1163               <div class="hp-footer-col-head">Company</div>
1164               <div class="hp-footer-links">
1165                  <a href="https://www.letsrentz.com">Home</a>
1166                  <a href="https://www.letsrentz.com/tenant/pgplan">PG Relax Plan</a>
1167               </div>
1168            </div>
1169            <div class="hp-footer-col-explore">
1170               <div class="hp-footer-col-head">Explore</div>
1171               <div class="hp-footer-links">
1172                  
1173                  <a href="https://www.letsrentz.com">Rent a home</a>
1174                  <a href="https://www.letsrentz.com/buy/Flat-for-sale-in-Sector-71-Mohali/1/30.7046/76.7179">Buy a property</a>
1175                  <a href="https://www.letsrentz.com/post-property-for-rent">List your property</a>
1176                  <a href="https://www.letsrentz.com/propertysearch/PG-in-Sector-71-Mohali/3/30.7046/76.7179">PG &amp; co-living</a>
1177                  <a href="https://www.letsrentz.com/propertysearch/Commercial-for-rent-in-Sector-71-Mohali/5/30.7046/76.7179">Commercial</a>
1178               </div>
1179            </div>
1180            <div class="hp-footer-col-legal">
1181               <div class="hp-footer-col-head">Legal</div>
1182               <div class="hp-footer-links">
1183                  <a href="https://www.letsrentz.com/terms">Terms of Use</a>
1184                  <a href="https://www.letsrentz.com/privacy">Privacy Policy</a>
1185                  <a href="https://www.letsrentz.com/sitemap">Sitemap</a>
1186                  <a href="https://www.letsrentz.com/faq">FAQ</a>
1187                  <a href="https://blog.letsrentz.com/" target="_blank">Blog</a>
1188               </div>
1189            </div>
1190         </div>
1191         <div class="hp-footer-contact-col">
1192            <div class="hp-footer-col-head">Contact us</div>
1193            <div class="hp-footer-contact"><i class="bi bi-telephone-fill"></i> +91 8847073862<br><i class="bi bi-envelope-fill"></i> <a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="4d3e383d3d223f390d2128393e3f28233937632e2220">[email&#160;protected]</a></div>
1194            <div class="hp-footer-apps">
1195               <a href="https://play.google.com/store/apps/details?id=com.decodershub.letsrentz" target="_blank" class="hp-footer-app-btn"><i class="bi bi-google-play"></i> Play</a>
1196               <a href="https://apps.apple.com/us/app/lets-rentz-make-it-easy/id1634945591" target="_blank" class="hp-footer-app-btn"><i class="bi bi-apple"></i> Store</a>
1197            </div>
1198         </div>
1199      </div>
1200      <div class="hp-footer-bottom">© 2022–2026 Lets Rentz Innovate Private Limited. All rights reserved.</div>
1201   </footer>
1202
1203   
1204      <nav class="hp-bottom-nav">
1205      <a href="https://www.letsrentz.com" class="hp-bottom-nav-item "><i class="bi bi-house-door-fill"></i><span>Home</span></a>
1206      <a href="https://www.letsrentz.com/propertysearch" class="hp-bottom-nav-item active"><i class="bi bi-search"></i><span>Search</span></a>
1207      <a href="https://www.letsrentz.com/savedpropertylisting" class="hp-bottom-nav-item "><i class="bi bi-heart"></i><span>Saved</span></a>
1208               <a href="https://www.letsrentz.com/post-property-for-rent" 
1208class="hp-bottom-nav-item "><i class="bi bi-plus-circle"></i><span>Post</span></a>
1209                     <button type="button" class="hp-bottom-nav-item" data-hp-login-trigger style="border:none;background:none;font-family:inherit;"><i class="bi bi-person-circle"></i><span>Account</span></button>
1210         </nav>
1211
1212   
1213   
1214   <!-- Phone number modal -->
1215   <div id="loginModalOverlay" class="property-selector-overlay">
1216      <div class="property-selector" onclick="event.stopPropagation()">
1217         <button class="property-close-btn" onclick="closeLoginModal(event)"><i class="bi bi-x-lg"></i></button>
1218         <div class="selector-content">
1219            <div class="mobile-header">
1220               <h5 class="login-modal-heading">Lets Find A House For You</h5>
1221            </div>
1222            <div class="mobile-card">
1223               <div class="ob-progress"></div>
1224               <div class="ob-step-kicker"></div>
1225               <h5 class="mobile-card-title">What's your number?</h5>
1226               <p class="ob-sub">We'll text a 4-digit code. Nothing else &mdash; no calls, no spam.</p>
1227               <div class="login-phone-wrapper">
1228                  <div class="country-code">+91</div>
1229                  <input type="tel" id="loginPhoneNumber" class="login-phone-input" maxlength="10" placeholder="98765 43210">
1230               </div>
1231               <div class="login-phone-error">Oh no! This mobile number is invalid</div>
1232               <button type="button" class="continue-btn btn_login" onclick="loginButton()">Send code</button>
1233               <p class="mobile-terms-text">By clicking, I accept the <a href="https://www.letsrentz.com/terms">Terms of Service</a> and <a href="https://www.letsrentz.com/privacy">Privacy Policy</a></p>
1234            </div>
1235         </div>
1236      </div>
1237   </div>
1238
1239   <!-- OTP verification modal -->
1240   <div id="otpModalOverlay" class="property-selector-overlay">
1241      <div class="property-selector" onclick="event.stopPropagation()">
1242         <button class="property-close-btn" onclick="closeOTPModal(event)"><i class="bi bi-x-lg"></i></button>
1243         <div class="selector-content">
1244            <div class="mobile-header">
1245               <h5 class="login-modal-heading">Verify Your Mobile Number</h5>
1246            </div>
1247            <div class="mobile-card">
1248               <div class="ob-progress"></div>
1249               <div class="ob-step-kicker"></div>
1250               <h5 class="mobile-card-title">Verify it's you</h5>
1251               <p class="otp-subtitle">Sent to <strong id="otpMobileNumber">+91 XXXXX XXXXX</strong> <button type="button" class="otp-edit-number-btn" onclick="editOTPPhoneNumber(event)" aria-label="Edit phone number"><i class="bi bi-pencil-fill"></i></button></p>
1252               <div class="otp-input-wrapper">
1253                  <input type="text" id="otp1" maxlength="1" inputmode="numeric" pattern="[0-9]*" class="otp-input">
1254                  <input type="text" maxlength="1" inputmode="numeric" pattern="[0-9]*" class="otp-input">
1255                  <input type="text" maxlength="1" inputmode="numeric" pattern="[0-9]*" class="otp-input">
1256                  <input type="text" maxlength="1" inputmode="numeric" pattern="[0-9]*" class="otp-input">
1257               </div>
1258               <div class="otp_verification_error" style="display:none;color:#e53935;font-size:12px;margin-top:-14px;margin-bottom:14px;"><p></p></div>
1259               <div class="otp-resend-section">
1260                  <span>Didn't receive code? </span>
1261                  <a href="javascript:void(0)" class="otp-resend-link otp-resend-btn" onclick="resendOTP(event)">Resend OTP</a>
1262               </div>
1263               <button class="otp_continue_btn continue-btn" onclick="otpVerificationBtn()">Verify</button>
1264               <p class="popup-terms-text">By clicking, I accept the <a href="https://www.letsrentz.com/terms">Terms of Service</a> and <a href="https://www.letsrentz.com/privacy">Privacy Policy</a></p>
1265            </div>
1266         </div>
1267      </div>
1268   </div>
1269
1270   <!-- Full name modal (new users only) -->
1271   <div id="nameModalOverlay" class="property-selector-overlay" onclick="closeNameModal(event)">
1272      <div class="property-selector" onclick="event.stopPropagation()">
1273         <button class="property-close-btn" onclick="closeNameModal(event)"><i class="bi bi-x-lg"></i></button>
1274         <div class="selector-content">
1275            <div class="mobile-header">
1276               <h5 class="login-modal-heading">Lets Find A House For You</h5>
1277            </div>
1278            <div class="mobile-card">
1279               <div class="ob-progress"></div>
1280               <div class="ob-step-kicker"></div>
1281               <h5 class="mobile-card-title">What should we call you?</h5>
1282               <p class="ob-sub">Owners see this when you reach out &mdash; first name is fine.</p>
1283               <div class="name-input-wrapper">
1284                  <input type="text" id="register_full_name" class="name-input" placeholder="Your name">
1285                  <div class="login-error-name-message" style="display:none;">Please enter your full name</div>
1286               </div>
1287               <button type="button" class="continue-btn name_continue_btn">Continue</button>
1288               <p class="onboarding-terms">By clicking, I accept the <a href="https://www.letsrentz.com/terms" class="onboarding-link">Terms of Service</a> and <a href="https://www.letsrentz.com/privacy" class="onboarding-link">Privacy Policy</a></p>
1289            </div>
1290         </div>
1291      </div>
1292   </div>
1293
1294   <!-- Purpose modal (search / own a property / broker - resolves
1295        user_type directly, see .purpose_continue_btn in template.blade.php.
1296        Tenant vs. Property Buyer used to be a separate account-type screen;
1297        both already posted the same 'user' value there, so folding it into
1298        this one screen doesn't lose any real distinction. -->
1299   <div id="purposeModalOverlay" class="property-selector-overlay">
1300      <div class="property-selector" onclick="event.stopPropagation()">
1301         <button class="property-close-btn" onclick="closePurposeModal(event)"><i class="bi bi-x-lg"></i></button>
1302         <div class="selector-content">
1303            <div class="mobile-header">
1304               <h5 class="login-modal-heading">Lets Find A House For You</h5>
1305            </div>
1306            <div class="mobile-card">
1307               <div class="ob-progress"></div>
1308               <div class="ob-step-kicker"></div>
1309               <h5 class="mobile-card-title">What brings you here?</h5>
1310               <p class="ob-sub">Just so the next screens ask the right things.</p>
1311               <div class="property-options">
1312                  <div class="property-item">
1313                     <input type="radio" id="purposeSearch" name="userPurpose" value="search">
1314                     <label for="purposeSearch">
1315                        <svg data-ob-icon viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" aria-hidden="true"><circle cx="10.5" cy="10.5" r="6.5"/><path d="m20 20-4.5-4.5"/></svg>
1316                        Searching for a home
1317                     </label>
1318                  </div>
1319                  <div class="property-item">
1320                     <input type="radio" id="purposeOwner" name="userPurpose" value="owner">
1321                     <label for="purposeOwner">
1322                        <svg data-ob-icon viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" aria-hidden="true"><circle cx="8" cy="15" r="3.2"/><path d="M10.3 12.7 19 4"/><path d="M15.5 8.5 18 11"/><path d="M17.5 6.5 20 9"/></svg>
1323                        I own a property to list
1324                     </label>
1325                  </div>
1326                  <div class="property-item">
1327                     <input type="radio" id="purposeBroker" name="userPurpose" value="broker">
1328                     <label for="purposeBroker">
1329                        <svg data-ob-icon viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" aria-hidden="true"><rect x="3.5" y="8" width="17" height="11" rx="1.5"/><path d="M8.5 8V6a2 2 0 0 1 2-2h3a2 2 0 0 1 2 2v2"/><path d="M3.5 13h17"/></svg>
1330                        I'm a broker / agent
1331                     </label>
1332                  </div>
1333               </div>
1334               <button class="purpose_continue_btn continue-btn">Continue</button>
1335               <p class="onboarding-terms">By clicking, I accept the <a href="https://www.letsrentz.com/terms" class="onboarding-link">Terms of Service</a> and <a href="https://www.letsrentz.com/privacy" class="onboarding-link">Privacy Policy</a></p>
1336            </div>
1337         </div>
1338      </div>
1339   </div>
1340
1341   <!-- Account type modal -->
1342   <div id="accountTypeModalOverlay" class="property-selector-overlay">
1343      <div class="property-selector" onclick="event.stopPropagation()">
1344         <button class="property-close-btn" onclick="closeAccountTypeModal(event)"><i class="bi bi-x-lg"></i></button>
1345         <div class="selector-content">
1346            <div class="mobile-header">
1347               <h5 class="login-modal-heading">Lets Find A House For You</h5>
1348            </div>
1349            <div class="mobile-card">
1350               <h5 class="mobile-card-title">Create Account as:</h5>
1351               <div class="property-options">
1352                  <div class="property-item account-type-item-search">
1353                     <input type="radio" id="accountTypeTenant" name="accountType" value="user">
1354                     <label for="accountTypeTenant">Tenant</label>
1355                  </div>
1356                  <div class="property-item account-type-item-search">
1357                     <input type="radio" id="accountTypeBuyer" name="accountType" value="user">
1358                     <label for="accountTypeBuyer">Property Buyer</label>
1359                  </div>
1360                  <div class="property-item account-type-item-post">
1361                     <input type="radio" id="accountTypeOwner" name="accountType" value="owner">
1362                     <label for="accountTypeOwner">Property Owner</label>
1363                  </div>
1364                  <div class="property-item account-type-item-post">
1365                     <input type="radio" id="accountTypeBroker" name="accountType" value="broker">
1366                     <label for="accountTypeBroker">Broker (Dealer)</label>
1367                  </div>
1368               </div>
1369               <button class="account_type_continue_btn continue-btn">Continue</button>
1370               <p class="onboarding-terms">By clicking, I accept the <a href="https://www.letsrentz.com/terms" class="onboarding-link">Terms of Service</a> and <a href="https://www.letsrentz.com/privacy" class="onboarding-link">Privacy Policy</a></p>
1371            </div>
1372         </div>
1373      </div>
1374   </div>
1375
1376   <!-- Preferred location modal -->
1377   <div id="locationModalOverlay" class="property-selector-overlay">
1378      <div class="property-selector" onclick="event.stopPropagation()">
1379         <div class="selector-content">
1380            <div class="mobile-header">
1381               <h5 class="login-modal-heading">Lets Find A House For You</h5>
1382            </div>
1383            <div class="mobile-card">
1384               <div class="ob-progress"></div>
1385               <div class="ob-step-kicker"></div>
1386               <h6 class="login-modal-title">Where are you looking?</h6>
1387               <p class="location-modal-description">A city or locality is enough to start.</p>
1388               <div class="location-search-wrapper">
1389                  <span class="location-search-icon" aria-hidden="true">
1390                     <svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
1391                        <path d="M10 18C10 18 16 12.5 16 7.5C16 4.18629 13.3137 1.5 10 1.5C6.68629 1.5 4 4.18629 4 7.5C4 12.5 10 18 10 18Z" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
1392                        <circle cx="10" cy="7.5" r="2" stroke="currentColor" stroke-width="1.5"/>
1393                     </svg>
1394                  </span>
1395                  <input type="text" id="locationInput" class="location-search-input autocomplete-google" placeholder="Sector 88A, Gurugram">
1396               </div>
1397               <form class="login-modal-form">
1398                  <button type="button" class="login-continue-btn-a" onclick="updateLocation(this);">Continue</button>
1399               </form>
1400            </div>
1401         </div>
1402      </div>
1403   </div>
1404
1405   <!-- Property type modal (tenants only) -->
1406   <div id="propertyTypeModalOverlay" class="property-selector-overlay">
1407      <div class="property-selector" onclick="event.stopPropagation()">
1408         <div class="selector-content">
1409            <div class="mobile-header">
1410               <h5 class="login-modal-heading">Lets Find A House For You</h5>
1411            </div>
1412            <div class="mobile-card">
1413               <div class="ob-progress"></div>
1414               <div class="ob-step-kicker"></div>
1415               <h2>What kind of place?</h2>
1416               <p class="ob-sub">We'll only show you these going forward.</p>
1417               <div class="property-options">
1418                                       <div class="property-item">
1419                        <input type="radio" id="property_1" name="property_type" value="1">
1420                        <label for="property_1">
1421                           <svg data-ob-icon viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" aria-hidden="true"><path d="M4 11 12 4l8 7"/><path d="M6 10v9h12v-9"/><path d="M10 19v-5h4v5"/></svg>
1422                           House
1423                        </label>
1424                     </div>
1425                                       <div class="property-item">
1426                        <input type="radio" id="property_2" name="property_type" value="2">
1427                        <label for="property_2">
1428                           <svg data-ob-icon viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" aria-hidden="true"><path d="M4 11 12 4l8 7"/><path d="M6 10v9h12v-9"/><path d="M10 19v-5h4v5"/></svg>
1429                           Flat
1430                        </label>
1431                     </div>
1432                                       <div class="property-item">
1433                        <input type="radio" id="property_3" name="property_type" value="3">
1434                        <label for="property_3">
1435                           <svg data-ob-icon viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" aria-hidden="true"><path d="M4 11 12 4l8 7"/><path d="M6 10v9h12v-9"/><path d="M10 19v-5h4v5"/></svg>
1436                           PG
1437                        </label>
1438                     </div>
1439                                       <div class="property-item">
1440                        <input type="radio" id="property_5" name="property_type" value="5">
1441                        <label for="property_5">
1442                           <svg data-ob-icon viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" aria-hidden="true"><path d="M4 11 12 4l8 7"/><path d="M6 10v9h12v-9"/><path d="M10 19v-5h4v5"/></svg>
1443                           Commercial
1444                        </label>
1445                     </div>
1446                                 </div>
1447               <button class="property_type_continue_btn continue-btn">Continue</button>
1448            </div>
1449         </div>
1450      </div>
1451   </div>
1452
1453   <!-- Category (sub property type) modal -->
1454   <div id="categoryModalOverlay" class="property-selector-overlay">
1455      <div class="property-selector" onclick="event.stopPropagation()">
1456         <div class="selector-content">
1457            <div class="mobile-header">
1458               <h5 class="login-modal-heading">Lets Find A House For You</h5>
1459            </div>
1460            <div class="mobile-card">
1461               <div class="ob-progress"></div>
1462               <div class="ob-step-kicker"></div>
1463               <h2>How big?</h2>
1464               <p class="ob-sub">Roughly is fine &mdash; you can widen this later.</p>
1465               <div id="categoryOptions" class="property-options"></div>
1466               <button class="category_continue_btn continue-btn">Continue</button>
1467            </div>
1468         </div>
1469      </div>
1470   </div>
1471
1472   <!-- Tenant type modal -->
1473   <div id="tenantTypeModalOverlay" class="property-selector-overlay">
1474      <div class="property-selector" onclick="event.stopPropagation()">
1475         <div class="selector-content">
1476            <div class="mobile-header">
1477               <h5 class="login-modal-heading">Lets Find A House For You</h5>
1478            </div>
1479            <div class="mobile-card">
1480               <div class="ob-progress"></div>
1481               <div class="ob-step-kicker"></div>
1482               <h2>Who's moving in?</h2>
1483               <p class="ob-sub">The one thing we can't tell from the listing itself.</p>
1484               <div class="property-options">
1485                  <div class="property-item">
1486                     <input type="radio" id="req_tenant_family" name="tenant_type" value="family">
1487                     <label for="req_tenant_family">
1488                        <svg data-ob-icon viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" aria-hidden="true"><circle cx="8" cy="7" r="2.6"/><circle cx="16" cy="7" r="2.6"/><path d="M3 20c0-3 2.2-5 5-5s5 2 5 5"/><path d="M11 20c0-3 2.2-5 5-5s5 2 5 5" stroke-opacity=".55"/></svg>
1489                        Family
1490                     </label>
1491                  </div>
1492                  <div class="property-item">
1493                     <input type="radio" id="req_tenant_boys" name="tenant_type" value="boys">
1494                     <label for="req_tenant_boys">
1495                        <svg data-ob-icon viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" aria-hidden="true"><circle cx="12" cy="7" r="3.2"/>
1495<path d="M5 20c0-3.8 3-6.5 7-6.5s7 2.7 7 6.5"/></svg>
1496                        Boys
1497                     </label>
1498                  </div>
1499                  <div class="property-item">
1500                     <input type="radio" id="req_tenant_girls" name="tenant_type" value="girls">
1501                     <label for="req_tenant_girls">
1502                        <svg data-ob-icon viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" aria-hidden="true"><circle cx="12" cy="7" r="3.2"/><path d="M12 10.2 8.6 20h6.8L12 10.2Z"/></svg>
1503                        Girls
1504                     </label>
1505                  </div>
1506                  <div class="property-item">
1507                     <input type="radio" id="req_tenant_livin_couple" name="tenant_type" value="livin_couple">
1508                     <label for="req_tenant_livin_couple">
1509                        <svg data-ob-icon viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" aria-hidden="true"><circle cx="9" cy="7" r="2.6"/><circle cx="15" cy="7" r="2.6"/><path d="M4 20c0-3 2.2-5 5-5M20 20c0-3-2.2-5-5-5"/></svg>
1510                        Livin Couple
1511                     </label>
1512                  </div>
1513               </div>
1514               <button class="tenant_type_continue_btn continue-btn">Finish</button>
1515            </div>
1516         </div>
1517      </div>
1518   </div>
1519
1520   <!-- Budget estimate modal -->
1521   <div id="budgetEstimateModalOverlay" class="property-selector-overlay">
1522      <div class="property-selector" onclick="event.stopPropagation()">
1523         <div class="selector-content">
1524            <div class="mobile-header">
1525               <h5 class="login-modal-heading">Lets Find A House For You</h5>
1526            </div>
1527            <div class="mobile-card">
1528               <div class="ob-progress"></div>
1529               <div class="ob-step-kicker"></div>
1530               <h2>What's your budget?</h2>
1531               <p class="ob-sub">Monthly rent, roughly.</p>
1532               <div class="property-options">
1533                  <div class="property-item"><input type="radio" id="budget_8k" name="budget_estimate" value="8000"><label for="budget_8k">Below 8K</label></div>
1534                  <div class="property-item"><input type="radio" id="budget_8k_10k" name="budget_estimate" value="10000"><label for="budget_8k_10k">8k - 10k</label></div>
1535                  <div class="property-item"><input type="radio" id="budget_10k_15k" name="budget_estimate" value="15000"><label for="budget_10k_15k">10k - 15k</label></div>
1536                  <div class="property-item"><input type="radio" id="budget_15k_20k" name="budget_estimate" value="20000"><label for="budget_15k_20k">15k - 20k</label></div>
1537                  <div class="property-item"><input type="radio" id="budget_20k_25k" name="budget_estimate" value="25000"><label for="budget_20k_25k">20k - 25k</label></div>
1538                  <div class="property-item"><input type="radio" id="budget_25k_30k" name="budget_estimate" value="30000"><label for="budget_25k_30k">25k - 30k</label></div>
1539                  <div class="property-item"><input type="radio" id="budget_above_30k" name="budget_estimate" value="100000"><label for="budget_above_30k">Above 30k</label></div>
1540               </div>
1541               <button class="budget_estimate_continue_btn continue-btn">Finish</button>
1542            </div>
1543         </div>
1544      </div>
1545   </div>
1546
1547   <div class="lrv2-modal-overlay lrv2-scope" id="lrv2ModalOverlay">
1548   <div class="lrv2-modal" role="dialog" aria-modal="true" aria-label="Log in or sign up" onclick="event.stopPropagation()">
1549      <button type="button" class="lrv2-modal-close" aria-label="Close">&times;</button>
1550      <div class="lrv2-brand">
1551         <img src="https://www.letsrentz.com/front/assets/images/loginv2-mark.png" alt="Lets Rentz">
1552         <b>LetsRentz</b>
1553      </div>
1554      <div class="lrv2-steps">
1555         <div class="lrv2-step" data-step="phone" data-cta-label="Continue">
1556   
1557   <h4 class="lrv2-h lrv2-h-desktop">Log in or <em>sign up</em></h4>
1558   <h2 class="lrv2-h lrv2-h-mobile">Your next <em>ghar</em> is closer than you think.</h2>
1559   <p class="lrv2-lede lrv2-lede-desktop">We'll text you a four-digit code. No passwords, ever.</p>
1560   <p class="lrv2-lede lrv2-lede-mobile">38,000+ homes across Tricity &amp; NCR. One number gets you in.</p>
1561   <div class="lrv2-field">
1562      <span class="lrv2-cc">+91</span>
1563      <input type="tel" class="lrv2-phone-input" inputmode="numeric" autocomplete="tel-national" maxlength="10" placeholder="98765 43210" aria-label="Mobile number">
1564   </div>
1565   
1566   <p class="lrv2-fine lrv2-fine-desktop">By continuing you agree to our <a href="https://www.letsrentz.com/terms">Terms</a> &amp; <a href="https://www.letsrentz.com/privacy">Privacy Policy</a></p>
1567</div>
1568
1569<div class="lrv2-step" data-step="otp" data-cta-label="Verify">
1570   <h4 class="lrv2-h">Almost there — <em>just one code.</em></h4>
1571   <p class="lrv2-lede">Sent by SMS to <span data-otp-number>+91 __________</span> · WhatsApp fallback on resend.</p>
1572   <div class="lrv2-otp-row" role="group" aria-label="Enter the 4-digit code">
1573      <input class="lrv2-otp" inputmode="numeric" pattern="[0-9]*" maxlength="1" autocomplete="one-time-code" aria-label="Digit 1">
1574      <input class="lrv2-otp" inputmode="numeric" pattern="[0-9]*" maxlength="1" autocomplete="off" aria-label="Digit 2">
1575      <input class="lrv2-otp" inputmode="numeric" pattern="[0-9]*" maxlength="1" autocomplete="off" aria-label="Digit 3">
1576      <input class="lrv2-otp" inputmode="numeric" pattern="[0-9]*" maxlength="1" autocomplete="off" aria-label="Digit 4">
1577   </div>
1578   <p class="lrv2-resend">Didn't get it? <button type="button" class="lrv2-resend-btn" disabled>Resend on WhatsApp · <span data-resend-timer>0:40</span></button></p>
1579   <button type="button" class="lrv2-edit-number"><i class="bi bi-pencil-fill" aria-hidden="true"></i> Edit number</button>
1580</div>
1581
1582
1583
1584
1585
1586<div class="lrv2-step" data-step="done" data-hide-cta="1">
1587   <div class="lrv2-done">
1588      <div class="lrv2-done-ring">✓</div>
1589      <h4 class="lrv2-h">You're <em>in.</em></h4>
1590      <p class="lrv2-lede">Verified — pick up right where you left off.</p>
1591      <button type="button" class="lrv2-cta lrv2-cta-block" style="margin-top:22px" data-lrv2-modal-close>Continue browsing</button>
1592   </div>
1593</div>
1594      </div>
1595      <div class="lrv2-foot">
1596         <div class="lrv2-dots" aria-hidden="true"></div>
1597         <button type="button" class="lrv2-cta">Continue <span>→</span></button>
1598      </div>
1599   </div>
1600</div>
1601   <div class="pp2-preview" id="ppInlineRoot">
1602  <div class="pp-overlay" id="ppInlineOverlay" aria-hidden="true">
1603    <div class="pp-card" role="dialog" aria-modal="true" aria-labelledby="ppInlineTitle">
1604
1605      <button type="button" class="pp-close" id="ppInlineClose" aria-label="Close">
1606        <svg width="15" height="15" viewBox="0 0 24 24" fill="none"><path d="M6 6l12 12M18 6L6 18" stroke="currentColor" stroke-width="2.4" stroke-linecap="round"/></svg>
1607      </button>
1608
1609      <div class="pp-head">
1610        <span class="eyebrow">Step 1 of 5</span>
1611                  <h2 id="ppInlineTitle">What would you like to do?</h2>
1612          <p>Pick an option to get started &mdash; we'll shape the rest of the form around it.</p>
1613              </div>
1614
1615      <div class="pp-body">
1616        <div class="pp-block">
1617          <span class="pp-label">Your purpose</span>
1618                    <div class="intent-grid cols-2" role="radiogroup" aria-label="What would you like to do?">
1619
1620                        <label class="intent is-on" data-intent="rent">
1621              <input type="radio" name="ppInlineIntent" value="rent" checked>
1622              <span class="tick"><svg width="13" height="13" viewBox="0 0 24 24" fill="none"><path d="M20 6 9 17l-5-5" stroke="currentColor" stroke-width="3.4" stroke-linecap="round" stroke-linejoin="round"/></svg></span>
1623              <span class="ic"><svg width="26" height="26" viewBox="0 0 24 24" fill="none"><path d="M3.5 10.5 12 4l8.5 6.5V19a1.5 1.5 0 0 1-1.5 1.5H5A1.5 1.5 0 0 1 3.5 19v-8.5Z" stroke="currentColor" stroke-width="1.7" stroke-linejoin="round"/><path d="M9.5 20.5v-6h5v6" stroke="currentColor" stroke-width="1.7" stroke-linejoin="round"/></svg></span>
1624              <b>Rent your Property</b>
1625              <small>Find a tenant</small>
1626            </label>
1627            
1628                        <label class="intent" data-intent="sell">
1629              <input type="radio" name="ppInlineIntent" value="sell" >
1630              <span class="tick"><svg width="13" height="13" viewBox="0 0 24 24" fill="none"><path d="M20 6 9 17l-5-5" stroke="currentColor" stroke-width="3.4" stroke-linecap="round" stroke-linejoin="round"/></svg></span>
1631              <span class="ic"><svg width="26" height="26" viewBox="0 0 24 24" fill="none"><path d="M4 20.5V9l6-4.5 6 4.5v11.5" stroke="currentColor" stroke-width="1.7" stroke-linejoin="round"/><path d="M16 12h4v8.5M2.5 20.5h19" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"/><path d="M8 12h4M8 16h4" stroke="currentColor" stroke-width="1.7" stroke-linecap="round"/></svg></span>
1632              <b>Sell your Property</b>
1633              <small>Find a buyer</small>
1634            </label>
1635            
1636            
1637          </div>
1638                  </div>
1639
1640        <div class="pp-block">
1641          <span class="pp-label">Property type</span>
1642          <h3>What are you posting?</h3>
1643          <p class="hint" id="ppInlineTypeHint">Select the type of property you want to put on rent.</p>
1644
1645                    <div class="type-list" data-for-intent="rent" id="ppInlineTypesRent" role="radiogroup" aria-label="Type of property to rent" ></div>
1646                              <div class="type-list" data-for-intent="sell" id="ppInlineTypesSell" role="radiogroup" aria-label="Type of property to sell" hidden></div>
1647                              <p class="hint" id="ppInlineLoading">Loading property types&hellip;</p>
1648        </div>
1649      </div>
1650
1651      <div class="pp-foot">
1652        <button type="button" class="btn btn-primary btn-block" id="ppInlineContinue" disabled>
1653          <span id="ppInlineContinueLabel">Continue</span>
1654          <svg width="17" height="17" viewBox="0 0 24 24" fill="none"><path d="M5 12h14M13 6l6 6-6 6" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"/></svg>
1655        </button>
1656        <p class="pp-terms">By continuing you agree to our <a href="#">Terms</a> &amp; <a href="#">Posting Policy</a>.</p>
1657      </div>
1658
1659    </div>
1660  </div>
1661</div>
vendor: 110 bytes, lines 1661-1663
1661
1662
1663<script data-cfasync="false" src="/cdn-cgi/scripts/5c5dd728/cloudflare-static/email-decode.min.js"></script>
1663<script>
1664(function(){
1665  const overlay  = document.getElementById('ppInlineOverlay');
1666  const closeBtn = document.getElementById('ppInlineClose');
1667  const contBtn  = document.getElementById('ppInlineContinue');
1668  const contLbl  = document.getElementById('ppInlineContinueLabel');
1669  const typeHint = document.getElementById('ppInlineTypeHint');
1670  const loading  = document.getElementById('ppInlineLoading');
1671  const typeLists = {
1672    rent: document.getElementById('ppInlineTypesRent'),
1673    sell: document.getElementById('ppInlineTypesSell'),
1674    flatmate: document.getElementById('ppInlineTypesFlatmate')
1675  };
1676
1677  const HINTS = {
1678    rent: 'Select the type of property you want to put on rent.',
1679    sell: 'Select the type of property you want to sell.',
1680    flatmate: 'Select the type of place you want to share.'
1681  };
1682
1683  let loaded = false;
1684
1685  /* Match each type's name to a category-appropriate icon so the list
1686     reads visually the same as the standalone /post-property page instead
1687     of a single generic icon repeated for every row. First matching rule
1688     wins; unmatched names (rare custom categories) fall back to the flat
1689     (building) icon. */
1690  const TYPE_ICONS = [
1691    { test: /\bpg\b/i, svg: '<path d="M3 17v-4a2 2 0 0 1 2-2h11a3 3 0 0 1 3 3v3" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"/><path d="M3 17h18M3 20v-3M21 20v-3" stroke="currentColor" stroke-width="1.7" stroke-linecap="round"/><path d="M6.5 11V8.5A1.5 1.5 0 0 1 8 7h3a1.5 1.5 0 0 1 1.5 1.5V11" stroke="currentColor" stroke-width="1.7" stroke-linecap="round"/>' },
1692    { test: /\broom\b/i, svg: '<path d="M4 20V6.2a1 1 0 0 1 .8-1l9-1.8a1 1 0 0 1 1.2 1V20" stroke="currentColor" stroke-width="1.7" stroke-linejoin="round"/><path d="M3 20h18M12.2 12.4v1.4" stroke="currentColor" stroke-width="1.7" stroke-linecap="round"/>' },
1693    { test: /commercial|shop|office|warehouse/i, svg: '<path d="M4 9.5h16V20H4V9.5Z" stroke="currentColor" stroke-width="1.7" stroke-linejoin="round"/><path d="M3 9.5 5 4.5h14l2 5" stroke="currentColor" stroke-width="1.7" stroke-linejoin="round"/><path d="M9.5 20v-5.5h5V20" stroke="currentColor" stroke-width="1.7" stroke-linejoin="round"/>' },
1694    { test: /plot|land/i, svg: '<rect x="3.5" y="9" width="17" height="10.5" rx="1.5" stroke="currentColor" stroke-width="1.7"/><path d="M8 9V4.5M8 4.5h7l-2 2.25 2 2.25H8" stroke="currentColor" stroke-width="1.7" stroke-linejoin="round"/>' },
1695    { test: /house|villa|farmhouse/i, svg: '<path d="M3.5 10.5 12 4l8.5 6.5V20H3.5v-9.5Z" stroke="currentColor" stroke-width="1.7" stroke-linejoin="round"/><path d="M10 20v-5h4v5" stroke="currentColor" stroke-width="1.7" stroke-linejoin="round"/>' },
1696    { test: /flat|apartment|floor/i, svg: '<rect x="4" y="3" width="16" height="18" rx="2" stroke="currentColor" stroke-width="1.7"/><path d="M8 7h3M13 7h3M8 11h3M13 11h3M8 15h3M13 15h3" stroke="currentColor" stroke-width="1.7" stroke-linecap="round"/>' }
1697  ];
1698  const DEFAULT_ICON_SVG = '<rect x="4" y="3" width="16" height="18" rx="2" stroke="currentColor" stroke-width="1.7"/><path d="M8 7h3M13 7h3M8 11h3M13 11h3M8 15h3M13 15h3" stroke="currentColor" stroke-width="1.7" stroke-linecap="round"/>';
1699
1700  function iconSvgFor(name){
1701    const match = TYPE_ICONS.find(t => t.test.test(name));
1702    return '<svg width="20" height="20" viewBox="0 0 24 24" fill="none">' + (match ? match.svg : DEFAULT_ICON_SVG) + '</svg>';
1703  }
1704
1705  function rowHtml(intent, t, i){
1706    return '<label class="type-row' + (i === 0 ? ' is-on' : '') + '" data-type="' + t.id + '">' +
1707      '<input type="radio" name="ppInlineType_' + intent + '" value="' + t.id + '"' + (i === 0 ? ' checked' : '') + '>' +
1708      '<span class="ic">' + iconSvgFor(t.name) + '</span>' +
1709      '<span class="txt"><b>' + t.name + '</b></span>' +
1710      '<span class="dot"></span>' +
1711    '</label>';
1712  }
1713
1714  function wireTypeRow(row){
1715    row.querySelector('input').addEventListener('change', () => {
1716      row.closest('.type-list').querySelectorAll('.type-row').forEach(r => r.classList.toggle('is-on', r.querySelector('input').checked));
1717    });
1718  }
1719
1720  function loadTypes(){
1721    if(loaded) return;
1722    fetch("https://www.letsrentz.com/post-property/type-options").then(r => r.json()).then(data => {
1723      if(!data.status) return;
1724      ['rent','sell','flatmate'].forEach(intent => {
1725        if(!typeLists[intent]) return; // flatmate list isn't rendered for owner/broker accounts
1726        const items = data[intent] || [];
1727        typeLists[intent].innerHTML = items.map((t, i) => rowHtml(intent, t, i)).join('');
1728        typeLists[intent].classList.toggle('odd-count', items.length % 2 !== 0);
1729        typeLists[intent].querySelectorAll('.type-row').forEach(wireTypeRow);
1730      });
1731      loaded = true;
1732      loading.style.display = 'none';
1733      contBtn.disabled = false;
1734    }).catch(() => {
1735      loading.textContent = 'Could not load property types — please refresh and try again.';
1736    });
1737  }
1738
1739  function selectIntent(intent){
1740    const input = document.querySelector('input[name="ppInlineIntent"][value="' + intent + '"]');
1741    if(!input || input.checked) return;
1742    input.checked = true;
1743    input.dispatchEvent(new Event('change'));
1744  }
1745
1746  function openPopup(intent){
1747    overlay.classList.add('show');
1748    overlay.setAttribute('aria-hidden','false');
1749    document.body.classList.add('locked');
1750    loadTypes();
1751    if(intent){ selectIntent(intent); }
1752  }
1753  function closePopup(){
1754    overlay.classList.remove('show');
1755    overlay.setAttribute('aria-hidden','true');
1756    document.body.classList.remove('locked');
1757  }
1758
1759  // Any element on the page can open this popup by adding a [data-pp-open]
1760  // attribute (header's "Post Property FREE" button, mypropertylisting's
1761  // "List Your Property" button, homepage tab CTAs, etc.) — this partial is
1762  // included once, site-wide, from front.template.blade.php. An optional
1763  // value (data-pp-open="flatmate") pre-selects that intent tab.
1764  document.querySelectorAll('[data-pp-open]').forEach(function(el){
1765    el.addEventListener('click', function(e){
1766      e.preventDefault();
1767      openPopup(el.getAttribute('data-pp-open'));
1768    });
1769  });
1770  window.ppOpenPostPropertyPopup = openPopup;
1771
1772  closeBtn.addEventListener('click', closePopup);
1773  overlay.addEventListener('mousedown', e => { if(e.target === overlay) closePopup(); });
1774  document.addEventListener('keydown', e => {
1775    if(e.key === 'Escape' && overlay.classList.contains('show')) closePopup();
1776  });
1777
1778  document.querySelectorAll('input[name="ppInlineIntent"]').forEach(input => {
1779    input.addEventListener('change', () => {
1780      document.querySelectorAll('#ppInlineOverlay .intent').forEach(c => c.classList.toggle('is-on', c.contains(input)));
1781      typeHint.textContent = HINTS[input.value];
1782      Object.keys(typeLists).forEach(intent => { if(typeLists[intent]) typeLists[intent].hidden = intent !== input.value; });
1783    });
1784  });
1785
1786  contBtn.addEventListener('click', () => {
1787    const intent = document.querySelector('input[name="ppInlineIntent"]:checked').value;
1788    const typeInput = typeLists[intent].querySelector('input:checked');
1789    if(!typeInput) return;
1790    const typeId = typeInput.value;
1791
1792    if(intent === 'sell'){
1793      window.location.href = "https://www.letsrentz.com/post-property/sell-flow?type_id=" + typeId;
1794    }else{
1795      window.location.href = "https://www.letsrentz.com/post-property/rent-flow?category=" + intent + "&type_id=" + typeId;
1796    }
1797  });
1798})();
1799</script>
1799
1800   </div>
1801
1802
1803   
1803<script>
1804      var address="";
1805      var lati="";
1806      var longi="";
1807      function initGoogleAutocomplete() {
1808         // const input = document.querySelector('.autocomplete-google');
1809         // console.log(input);
1810         
1811         // if (!input) return;
1812         const inputs = document.querySelectorAll('.autocomplete-google');
1813         inputs.forEach(input => {
1814            if (window.getComputedStyle(input).display === "none") return;
1815
1816            const autocomplete = new google.maps.places.Autocomplete(input, {
1817                     types: ['geocode'],
1818                     componentRestrictions: { country: 'in' }
1819            });
1820
1821            autocomplete.addListener('place_changed', function () {
1822                  const place = autocomplete.getPlace();
1823                  if (!place.geometry) {
1824                     alert("No details available for the selected location.");
1825                     return;
1826                  }
1827                  
1828                  const fullAddress = place.formatted_address;
1829                  const latitude = place.geometry.location.lat();
1830                  const longitude = place.geometry.location.lng();
1831
1832                  // Format address like your old logic (replace commas, spaces, #)
1833                  let replacedCommas = fullAddress.replace(/,\s+/g, '-');
1834                  let finalAddress = replacedCommas.replace(/\s+/g, '-').replace(/#/g, '');
1835
1836                  address=finalAddress;
1837                  lati=latitude;
1838                  longi=longitude;
1839                  $('#full_address').val(address);
1840                  $('#rental_latitude').val(latitude);
1841                  $('#rental_longitude').val(longitude);
1842                  // Optional: update hidden fields or trigger a search
1843            });
1844
1845            // Google's place_changed only fires when a suggestion is
1846            // actually selected (mouse click, or arrow-key highlight +
1847            // Enter). Typing an address and pressing Enter directly —
1848            // a common desktop habit, less common on mobile where
1849            // tapping a listed suggestion is the natural gesture —
1850            // never selects anything, leaving lati/longi silently
1851            // empty and the requirement flow stuck with no place
1852            // selected. If Enter is pressed with no suggestion
1853            // highlighted, select the top one instead of doing nothing.
1854            input.addEventListener('keydown', function (e) {
1855               if (e.key !== 'Enter') return;
1856               var containers = document.querySelectorAll('.pac-container');
1857               var activeContainer = null;
1858               containers.forEach(function (c) {
1859                  if (getComputedStyle(c).display !== 'none' && c.querySelector('.pac-item')) {
1860                     activeContainer = c;
1861                  }
1862               });
1863               if (!activeContainer) return;
1864               if (activeContainer.querySelector('.pac-item-selected')) return;
1865               var first = activeContainer.querySelector('.pac-item');
1866               if (!first) return;
1867               e.preventDefault();
1868               e.stopPropagation();
1869               ['mousedown', 'mouseup', 'click'].forEach(function (type) {
1870                  first.dispatchEvent(new MouseEvent(type, { bubbles: true, cancelable: true, view: window }));
1871               });
1872            }, true);
1873         });
1874
1875
1876      }
1877      window.initGoogleAutocomplete = initGoogleAutocomplete;
1878
1879      // Single shared loader (2026-08-27) — this block and the homepage's
1880      // hpLoadMapsIfNeeded below used to each guard against re-injecting
1881      // the script with their OWN separate flag (__lrMapsScriptInserted /
1882      // __hpMapsScriptInserted), so neither saw the other already had one
1883      // in flight. Both run on the same window.load+400ms timing on EVERY
1884      // page (this template is the base layout for all of them), so
1885      // whenever GTM's own independent Maps bootstrap hadn't already
1886      // finished by then, BOTH would inject their own 
1886<script src="maps/
1887      // api/js?...">, triggering Google's "included multiple times"
1888      // warning and — per the comment above, confirmed in practice —
1889      // leaving one of the two callbacks never firing. Worse on slower
1890      // connections (mobile) where GTM's own load is less likely to have
1891      // already won the race by 400ms, matching reports of location
1892      // search failing on mobile web but not desktop. One shared in-flight
1893      // flag + callback queue means at most one script tag ever gets
1894      // inserted, and every caller's callback fires once it resolves.
1895      window.__lrGoogleMapsCallbacks = window.__lrGoogleMapsCallbacks || [];
1896      window.__lrLoadGoogleMaps = window.__lrLoadGoogleMaps || function (cb) {
1897         if (window.google && window.google.maps && window.google.maps.places) {
1898            cb();
1899            return;
1900         }
1901         window.__lrGoogleMapsCallbacks.push(cb);
1902         if (window.__lrMapsScriptInserted) return;
1903         window.__lrMapsScriptInserted = true;
1904         window.__lrGoogleMapsReady = function () {
1905            window.__lrGoogleMapsCallbacks.splice(0).forEach(function (fn) { fn(); });
1906         };
1907         var s = document.createElement('script');
1908         s.src = 'https://maps.googleapis.com/maps/api/js?key=AIzaSyDSj3nda3aEN7QXQNaDllvJQxkwAGBbDkM&libraries=places&callback=__lrGoogleMapsReady';
1909         s.async = true;
1910         document.head.appendChild(s);
1911      };
1912      // Pages that set window.__lrDeferMaps = true (the property detail page)
1913      // hold the ~250 KB Maps/Places bundle back until the first real user
1914      // interaction, so it never competes with first paint or the LCP image.
1915      // Everything else keeps the original load + 400ms behaviour.
1916      window.__lrWhenMapsWanted = function (fn) {
1917         window.addEventListener('load', function () {
1918            if (!window.__lrDeferMaps) { setTimeout(fn, 400); return; }
1919            var fired = false;
1920            var go = function () { if (!fired) { fired = true; fn(); } };
1921            ['pointerdown', 'touchstart', 'keydown', 'scroll', 'focusin'].forEach(function (ev) {
1922               window.addEventListener(ev, go, { passive: true, once: true });
1923            });
1924         });
1925      };
1926      window.__lrWhenMapsWanted(function () { window.__lrLoadGoogleMaps(initGoogleAutocomplete); });
1927   </script>
1927
1928
1929   
1929<script src="https://www.gstatic.com/firebasejs/9.0.0/firebase-app-compat.js"></script>
1929
1930   
1930<script src="https://www.gstatic.com/firebasejs/9.0.0/firebase-messaging-compat.js"></script>
1930
1931   
1931<script>
1932      (function () {
1933         window.__lrFcmToken = null;
1934         var lrMessaging = null;
1935         var LR_VAPID_KEY = 'BOlWeWtMF2QcspIBdMxodt0s_o7bOmbNCZs-uDnCx5PNv2GCuqa7Iyu2oN9k3QoXa1GVQ35B4ErYfCJdIgHGyXA';
1936
1937         try {
1938            firebase.initializeApp({
1939               apiKey: "AIzaSyA6MD5GuIoAVKpcyogZstW_UR6f5JPKZ4k",
1940               authDomain: "lets-rentz.firebaseapp.com",
1941               databaseURL: "https://lets-rentz-default-rtdb.firebaseio.com",
1942               projectId: "lets-rentz",
1943               storageBucket: "lets-rentz.appspot.com",
1944               messagingSenderId: "100691140179",
1945               appId: "1:100691140179:web:75d17337ff39e3c6cef0a6",
1946               measurementId: "G-NEFBMB2PYJ"
1947            });
1948            lrMessaging = firebase.messaging();
1949
1950            // The service worker's onBackgroundMessage (firebase-messaging-sw.js)
1951            // only fires when this tab isn't focused — for an active/foreground
1952            // tab, Firebase hands the message to the page instead, so we have to
1953            // show the notification ourselves. Routed through the SW registration
1954            // (not `new Notification(...)`) so the existing notificationclick
1955            // handler in firebase-messaging-sw.js still applies to it.
1956            lrMessaging.onMessage(function (payload) {
1957               if (Notification.permission !== 'granted' || !('serviceWorker' in navigator)) { return; }
1958               var notificationTitle = (payload.notification && payload.notification.title) || 'Lets Rentz';
1959               // No icon — the browser/OS falls back to its own default.
1960               // Rebuild a clean, minimal data object instead of passing
1961               // payload.data through as-is. Firebase's own compat SDK
1962               // secretly registers its OWN notificationclick listener
1963               // in the service worker; if a notification's data carries
1964               // Firebase's internal tracking marker, that listener takes
1965               // over the click FIRST (stopImmediatePropagation), sending
1966               // the user to Firebase's own fallback destination instead
1967               // of the property page. Only copying the one field we
1968               // actually need keeps that marker out.
1969               var notificationOptions = {
1970                  body: (payload.notification && payload.notification.body) || '',
1971                  data: {
1972                     property_id: (payload.data && payload.data.property_id) || null,
1973                     click_action: (payload.data && payload.data.click_action) || null,
1974                     property_notification_id: (payload.data && payload.data.property_notification_id) || null
1975                  }
1976               };
1977               if (payload.notification && payload.notification.image) {
1978                  notificationOptions.image = payload.notification.image;
1979               }
1980               navigator.serviceWorker.ready.then(function (registration) {
1981                  registration.showNotification(notificationTitle, notificationOptions);
1982               });
1983            });
1984         } catch (e) {
1985            console.warn('Web push unavailable:', e);
1986         }
1987
1988         // Persist a freshly obtained token against this browser session
1989         // (matched server-side by login_token). Handles both the initial
1990         // permission grant and later token rotation (refresh).
1991         function lrSaveFcmToken(token) {
1992            var loginToken = localStorage.getItem('login_token');
1993            if (!token || !loginToken) { return; }
1994            if (localStorage.getItem('lr_saved_fcm_token') === token) { return; }
1995            var deviceType = detectDevice() === 'mobile' ? 'mobile_web' : 'desktop_web';
1996            $.ajax({
1997               url: 'https://www.letsrentz.com/auth/saveFcmToken',
1998               method: 'POST',
1999               data: {
2000                  fcm_token: token,
2001                  login_token: loginToken,
2002                  device_type: deviceType,
2003                  _token: 'bvRQeC7zDEZMxhJdt8x263POgOGJvjDEH4HLTaIw'
2004               },
2005               success: function (res) {
2006                  if (res && res.status) {
2007                     localStorage.setItem('lr_saved_fcm_token', token);
2008                  }
2009               }
2010            });
2011         }
2012
2013         // Fetch the token (only resolves once permission is granted) and
2014         // cache + save it. Registers the SW first.
2015         function lrFetchAndSaveToken() {
2016            if (!lrMessaging || !('serviceWorker' in navigator)) {
2017               return Promise.resolve(null);
2018            }
2019            return navigator.serviceWorker.register('/firebase-messaging-sw.js')
2020               .then(function (registration) {
2021                  return lrMessaging.getToken({
2022                     vapidKey: LR_VAPID_KEY,
2023                     serviceWorkerRegistration: registration
2024                  });
2025               })
2026               .then(function (token) {
2027                  if (token) {
2028                     window.__lrFcmToken = token;
2029                     lrSaveFcmToken(token);
2030                  }
2031                  return token;
2032               })
2033               .catch(function (err) {
2034                  console.warn('getToken failed:', err);
2035                  return null;
2036               });
2037         }
2038
2039         // On every page load: register the SW, and if permission is
2040         // already granted, refresh + re-save the token (Phase 5). Never
2041         // prompt here.
2042         window.addEventListener('load', function () {
2043            if (!('Notification' in window)) { return; }
2044            if (Notification.permission === 'granted') {
2045               lrFetchAndSaveToken();
2046            } else if ('serviceWorker' in navigator) {
2047               navigator.serviceWorker.register('/firebase-messaging-sw.js').catch(function () {});
2048            }
2049         });
2050
2051         // Called after the tenant finishes the requirement flow: prompt
2052         // for notification permission, then grab + save the token.
2053         // Resolves regardless of the user's choice so the caller can
2054         // proceed (e.g. reload) either way.
2055         window.lrRequestPushPermission = function () {
2056            if (!('Notification' in window) || !lrMessaging) {
2057               return Promise.resolve(null);
2058            }
2059            if (Notification.permission === 'granted') {
2060               return lrFetchAndSaveToken();
2061            }
2062            if (Notification.permission === 'denied') {
2063               return Promise.resolve(null);
2064            }
2065            return Notification.requestPermission()
2066               .then(function (permission) {
2067                  if (permission === 'granted') {
2068                     return lrFetchAndSaveToken();
2069                  }
2070                  return null;
2071               })
2072               .catch(function () { return null; });
2073         };
2074      })();
2075   </script>
2075
2076
2077   
2078   
2079   
2080   
2080<script>
2081      (function () {
2082         var banner = document.getElementById('hpAppBanner');
2083         if (!banner) { return; }
2084         // iPhone Safari gets Apple's native Smart App Banner instead (the
2085         // apple-itunes-app meta in <head>) - it knows whether the app is
2086         // installed and opens it on the same property. Chrome/Firefox/in-app
2087         // browsers on iPhone don't support that banner, so they keep this one.
2088         var ua = navigator.userAgent || '';
2089         if (/iPhone|iPod/.test(ua) && /Safari\//.test(ua) && !/CriOS|FxiOS|EdgiOS|OPiOS|FBAN|FBAV|Instagram/.test(ua)) {
2090            banner.classList.add('hp-app-banner-hide');
2091            return;
2092         }
2093         if (localStorage.getItem('lr_app_banner_dismissed') === '1') {
2094            banner.classList.add('hp-app-banner-hide');
2095            return;
2096         }
2097
2098         var ANDROID_LINK = 'https://play.google.com/store/apps/details?id=com.decodershub.letsrentz&hl=en_IN&utm_source=website&utm_medium=appbannertop';
2099         var IOS_LINK = 'https://apps.apple.com/in/app/lets-rentz-make-it-easy/id1634945591';
2100         var isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent);
2101
2102         var storeLabel = document.getElementById('hpAppBannerStoreLabel');
2103         var cta = document.getElementById('hpAppBannerCta');
2104         if (isIOS) {
2105            if (storeLabel) { storeLabel.textContent = 'App Store'; }
2106         }
2107
2108         if (cta) {
2109            cta.addEventListener('click', function () {
2110               if (typeof fbq === 'function') {
2111                  fbq('trackCustom', 'website_appbannertop', { platform: isIOS ? 'ios' : 'android' });
2112               }
2113               window.location.href = isIOS ? IOS_LINK : ANDROID_LINK;
2114            });
2115         }
2116
2117         var closeBtn = document.getElementById('hpAppBannerClose');
2118         if (closeBtn) {
2119            closeBtn.addEventListener('click', function () {
2120               localStorage.setItem('lr_app_banner_dismissed', '1');
2121               banner.classList.add('hp-app-banner-hide');
2122            });
2123         }
2124      })();
2125   </script>
2125
2126
2127<script>
2128   // Shares the single loader defined above (window.__lrLoadGoogleMaps) —
2129   // this used to have its own independent script-injection + guard flag,
2130   // which is what raced against the block above and caused the "included
2131   // multiple times" / unreliable-callback bug documented there. See that
2132   // comment for the full story.
2133   window.__lrWhenMapsWanted(function () {
2134      window.__lrLoadGoogleMaps(function () {
2135         if (typeof window.hpInitAutocomplete === 'function') window.hpInitAutocomplete();
2136      });
2137   });
2138</script>
vendor: 1 bytes, line 2138
2138
2139<script>
2140   // Shared across every page that extends this template: real Google
2141   // Places autocomplete init, the full OTP/register/location/property-type/
2142   // category/tenant-type/budget auth wizard, and the header's profile-menu/
2143   // logout/login-trigger wiring. Originally built only for the homepage
2144   // preview and later promoted here so every page gets a working login,
2145   // not just the homepage - see header-new.blade.php / login-modal.blade.php
2146   // for the markup this drives.
2147   var hpLati = '', hpLongi = '', hpAddress = '', hpCategory = 'rent';
2148   // Page-render-time auth flag, shared across every page (unlike
2149   // detail.blade.php's own local `isLoggedIn`, which only exists on the
2150   // property detail page) - resumePendingAction() below needs this on
2151   // every page a gated click can happen from.
2152   var lrIsLoggedIn = false;
2153
2154   function hpInitAutocomplete() {
2155      if (!window.google || !window.google.maps) return;
2156      // hpLocationInput only exists on the homepage's own search card - on
2157      // every other page this is skipped and only the shared auth-flow
2158      // field below gets wired up. (Previously this whole function bailed
2159      // out here whenever hpLocationInput was missing, which silently broke
2160      // the auth flow's own location autocomplete on every non-homepage
2161      // page - fixed as part of making this script shared across the site.)
2162      var input = document.getElementById('hpLocationInput');
2163      if (input) {
2164         var autocomplete = new google.maps.places.Autocomplete(input, {
2165            types: ['geocode'],
2166            componentRestrictions: { country: 'in' }
2167         });
2168         autocomplete.addListener('place_changed', function () {
2169            var place = autocomplete.getPlace();
2170            if (!place.geometry) { alert('No details available for the selected location.'); return; }
2171            var replacedCommas = place.formatted_address.replace(/,\s+/g, '-');
2172            hpAddress = replacedCommas.replace(/\s+/g, '-').replace(/#/g, '');
2173            hpLati = place.geometry.location.lat();
2174            hpLongi = place.geometry.location.lng();
2175         });
2176      }
2177
2178      // The real auth flow's own location modal (#locationInput) needs the
2179      // same autocomplete wiring, writing to the real global address/lati/
2180      // longi vars that updateLocation() below reads.
2181      var authLocationInput = document.getElementById('locationInput');
2182      if (authLocationInput) {
2183         var authAutocomplete = new google.maps.places.Autocomplete(authLocationInput, {
2184            types: ['geocode'],
2185            componentRestrictions: { country: 'in' }
2186         });
2187         authAutocomplete.addListener('place_changed', function () {
2188            var place = authAutocomplete.getPlace();
2189            if (!place.geometry) { showToast('No details available for the selected location.'); return; }
2190            var replacedCommas = place.formatted_address.replace(/,\s+/g, '-');
2191            address = replacedCommas.replace(/\s+/g, '-').replace(/#/g, '');
2192            lati = place.geometry.location.lat();
2193            longi = place.geometry.location.lng();
2194         });
2195      }
2196   }
2197   window.hpInitAutocomplete = hpInitAutocomplete;
2198
2199   // ==========================================================
2200   // REAL AUTH FLOW - ported verbatim (function names, endpoints, field
2201   // names, sequencing) from template.blade.php's login/OTP/register/
2202   // location/property-type/category/tenant-type/budget wizard, per
2203   // explicit decision to reuse the real, proven flow rather than
2204   // re-implement a second copy of business-critical logic. Copied (not
2205   // shared via an include) so this standalone page still doesn't load the
2206   // old site's script bundle - if the real flow changes, this needs
2207   // updating to match.
2208   //
2209   // fbq/gtag/getGA4ClientId are the real ones now (see the GTM/GA4/Meta
2210   // Pixel script block in <head>) - only toastr stays stubbed, since this
2211   // isolated page doesn't load the real toastr.js bundle and nothing here
2212   // depends on its visual styling, just the no-op call sites.
2213   // ==========================================================
2214   if (typeof toastr === 'undefined') { window.toastr = { success: function () {}, error: function () {} }; }
2215   function showToast(message) {
2216      var toast = $('<div class="toast">' + message + '</div>');
2217      $('body').append(toast);
2218      toast.addClass('show');
2219      setTimeout(function () {
2220         toast.removeClass('show');
2221         setTimeout(function () { toast.remove(); }, 600);
2222      }, 3000);
2223   }
2224
2225   const authPropertyTypes = [{"id":1,"name":"House","parent_id":0,"status":1,"created_at":"2022-07-17T11:06:00.000000Z","updated_at":"2022-07-17T11:06:00.000000Z","sub_property_type":[{"id":34,"name":"1 Room with Kitchen","parent_id":1,"status":1,"created_at":"2023-08-09T11:27:15.000000Z","updated_at":"2026-06-11T11:20:50.000000Z"},{"id":6,"name":"1BHK","parent_id":1,"status":1,"created_at":"2022-07-17T11:06:48.000000Z","updated_at":"2022-07-17T11:06:48.000000Z"},{"id":36,"name":"2 Room with Kitchen","parent_id":1,"status":1,"created_at":"2025-12-24T11:17:12.000000Z","updated_at":"2026-06-11T11:21:23.000000Z"},{"id":7,"name":"2BHK","parent_id":1,"status":1,"created_at":"2022-07-17T11:06:53.000000Z","updated_at":"2022-07-17T11:06:53.000000Z"},{"id":37,"name":"3 Room with Kitchen","parent_id":1,"status":1,"created_at":"2025-12-24T11:17:20.000000Z","updated_at":"2026-06-11T11:21:35.000000Z"},{"id":8,"name":"3BHK","parent_id":1,"status":1,"created_at":"2022-07-17T11:06:59.000000Z","updated_at":"2022-07-17T11:06:59.000000Z"},{"id":10,"name":"4+ BHK","parent_id":1,"status":1,"created_at":"2022-07-17T11:07:20.000000Z","updated_at":"2022-07-17T11:07:20.000000Z"},{"id":9,"name":"4BHK","parent_id":1,"status":1,"created_at":"2022-07-17T11:07:06.000000Z","updated_at":"2022-07-17T11:07:06.000000Z"},{"id":33,"name":"Annexy","parent_id":1,"status":1,"created_at":"2023-08-09T11:24:27.000000Z","updated_at":"2023-08-09T11:24:27.000000Z"}]},{"id":2,"name":"Flat","parent_id":0,"status":1,"created_at":"2022-07-17T11:06:13.000000Z","updated_at":"2022-07-17T11:06:13.000000Z","sub_property_type":[{"id":11,"name":"1BHK","parent_id":2,"status":1,"created_at":"2022-07-17T11:07:34.000000Z","updated_at":"2022-07-17T11:07:34.000000Z"},{"id":12,"name":"2BHK","parent_id":2,"status":1,"created_at":"2022-07-17T11:07:38.000000Z","updated_at":"2022-07-17T11:07:38.000000Z"},{"id":13,"name":"3BHK","parent_id":2,"status":1,"created_at":"2022-07-17T11:07:45.000000Z","updated_at":"2022-07-17T11:07:45.000000Z"},{"id":15,"name":"4+ BHK","parent_id":2,"status":1,"created_at":"2022-07-17T11:08:00.000000Z","updated_at":"2022-07-17T11:08:00.000000Z"},{"id":14,"name":"4BHK","parent_id":2,"status":1,"created_at":"2022-07-17T11:07:54.000000Z","updated_at":"2022-07-17T11:07:54.000000Z"},{"id":32,"name":"Annexy","parent_id":2,"status":1,"created_at":"2023-08-09T11:23:59.000000Z","updated_at":"2023-08-09T11:27:48.000000Z"},{"id":35,"name":"Studio Apartment","parent_id":2,"status":1,"created_at":"2023-08-09T11:27:54.000000Z","updated_at":"2023-08-09T11:27:54.000000Z"}]},{"id":3,"name":"PG","parent_id":0,"status":1,"created_at":"2022-07-17T11:06:21.000000Z","updated_at":"2022-07-17T11:06:21.000000Z","sub_property_type":[{"id":38,"name":"Five Sharing","parent_id":3,"status":1,"created_at":"2025-12-26T11:30:58.000000Z","updated_at":"2025-12-26T11:30:58.000000Z"},{"id":26,"name":"Four Sharing","parent_id":3,"status":1,"created_at":"2022-07-27T05:59:38.000000Z","updated_at":"2025-12-26T11:30:51.000000Z"},{"id":16,"name":"Single Room","parent_id":3,"status":1,"created_at":"2022-07-17T11:08:27.000000Z","updated_at":"2025-12-26T11:30:19.000000Z"},{"id":25,"name":"Three Sharing","parent_id":3,"status":1,"created_at":"2022-07-21T05:28:17.000000Z","updated_at":"2025-12-26T11:30:41.000000Z"},{"id":17,"name":"Two Sharing","parent_id":3,"status":1,"created_at":"2022-07-17T11:08:40.000000Z","updated_at":"2025-12-26T11:30:33.000000Z"}]},{"id":5,"name":"Commercial","parent_id":0,"status":1,"created_at":"2022-07-17T11:06:37.000000Z","updated_at":"2022-07-17T11:06:37.000000Z","sub_property_type":[{"id":20,"name":"Co-working","parent_id":5,"status":1,"created_at":"2022-07-17T11:09:59.000000Z","updated_at":"2022-07-17T11:09:59.000000Z"},{"id":22,"name":"Office","parent_id":5,"status":1,"created_at":"2022-07-17T11:10:31.000000Z","updated_at":"2022-07-17T11:10:31.000000Z"},{"id":30,"name":"PG Building","parent_id":5,"status":1,"created_at":"2023-07-03T08:04:07.000000Z","updated_at":"2023-07-03T08:04:07.000000Z"},{"id":23,"name":"Shop","parent_id":5,"status":1,"created_at":"2022-07-17T11:10:39.000000Z","updated_at":"2022-07-17T11:10:39.000000Z"},{"id":24,"name":"Showroom","parent_id":5,"status":1,"created_at":"2022-07-17T11:10:46.000000Z","updated_at":"2022-07-17T11:10:46.000000Z"},{"id":21,"name":"Single Chair","parent_id":5,"status":1,"created_at":"2022-07-17T11:10:14.000000Z","updated_at":"2022-07-17T11:10:14.000000Z"}]}];
2226   let userPropertySearchData = {
2227      selectedUserPropertyTypeId: null,
2228      selectedUserBudget: null,
2229      selectedUserTenantType: null,
2230      selectedUserCategoryId: null,
2231      selectedUserPreferenceId: null
2232   };
2233
2234   var address = "";
2235   var lati = "";
2236   var longi = "";
2237   var otp = "";
2238   var userPurpose = null;
2239   var userType = null;
2240   var user_type = "user";
2241   var user_image = "";
2242   var register_full_name = "";
2243   var register_phone_number = "";
2244   var register_email = "";
2245   var register_password = "";
2246   var social_type = "normal";
2247   var social_id = "";
2248   var referred_from = "";
2249   var request_type = "";
2250   var trail_user = "";
2251
2252   function detectDevice() {
2253      var userAgent = navigator.userAgent || navigator.vendor || window.opera;
2254      if (/android|iPhone|iPad|iPod|Windows Phone|mobile/i.test(userAgent)) return "mobile";
2255      return "desktop";
2256   }
2257
2258   // Every front page's logged-in/out chrome (header avatar, "Login"
2259   // prompts, Post Property gating, etc.) is baked into the server-rendered
2260   // HTML at the time the page loaded. The browser's back/forward cache
2261   // (bfcache) can restore that exact HTML from before a login happened
2262   // instead of re-requesting the page - eg: open a listing, tap into a
2263   // property detail, log in there (that page reloads and shows the new
2264   // state correctly), then hit back - bfcache hands back the listing page
2265   // exactly as it looked while still logged out, still prompting to log
2266   // in. `pageshow`'s `persisted` flag is true specifically for a bfcache
2267   // restore (never on a normal/first load).
2268   //
2269   // Unconditionally reloading here (the original fix) forced a full
2270   // navigation on every single back-navigation, even when auth hadn't
2271   // actually changed - which threw away client-side listing/pagination
2272   // state (infinite-scroll position, loaded pages) on the very common
2273   // "browse listing -> open detail -> back" path. Instead, check whether
2274   // the restored page's auth state actually disagrees with the real one
2275   // (via /auth/status) and only reload on a genuine mismatch.
2276   window.addEventListener('pageshow', function (event) {
2277      if (!event.persisted) return;
2278      var pageThinksGuest = !!document.querySelector('[data-hp-login-trigger]');
2279      fetch('https://www.letsrentz.com/auth/status', { credentials: 'same-origin' })
2280         .then(function (r) { return r.json(); })
2281         .then(function (data) {
2282            var actuallyLoggedIn = !!data.loggedIn;
2283            if (pageThinksGuest === actuallyLoggedIn) {
2284               window.location.reload();
2285            }
2286         })
2287         .catch(function () { /* network hiccup - don't force a reload */ });
2288   });
2289
2290   // Body scroll lock for the modal chain (phone/OTP/name/purpose/account
2291   // type/location/requirement steps). `overflow:hidden` on its own does
2292   // not reliably stop touch-drag scrolling on mobile Safari/Chrome, so
2293   // dragging on a modal (eg its drag-handle) was scrolling the page
2294   // behind it. Pinning the body with position:fixed at its current
2295   // scroll offset - restored on unlock - blocks that at the source.
2296   var hpBodyScrollLockY = 0;
2297   function hpLockBodyScroll() {
2298      hpBodyScrollLockY = window.scrollY || window.pageYOffset || 0;
2299      document.body.style.position = 'fixed';
2300      document.body.style.top = (-hpBodyScrollLockY) + 'px';
2301      document.body.style.left = '0';
2302      document.body.style.right = '0';
2303      document.body.style.width = '100%';
2304      document.body.style.overflow = 'hidden';
2305   }
2306   function hpUnlockBodyScroll() {
2307      document.body.style.position = '';
2308      document.body.style.top = '';
2309      document.body.style.left = '';
2310      document.body.style.right = '';
2311      document.body.style.width = '';
2312      document.body.style.overflow = '';
2313      // Explicit 'instant' - the site-wide `html { scroll-behavior: smooth }`
2314      // rule makes the legacy scrollTo(x, y) form animate instead of
2315      // snapping, so restoring the pre-lock offset visibly scrolled to the
2316      // top and glided back down instead of jumping straight there.
2317      window.scrollTo({ top: hpBodyScrollLockY, left: 0, behavior: 'instant' });
2318   }
2319
2320   // Keeps an open modal (and whatever input the user just focused) above
2321   // the on-screen keyboard on mobile. Resizing the overlay's own box
2322   // (via inset top/height) turned out unreliable on real iOS Safari -
2323   // the overlay would shrink to roughly where the sheet already sat
2324   // instead of down to the keyboard, leaving a visible gap between the
2325   // sheet and the keyboard. Shifting the sheet itself up by the
2326   // keyboard's height is the more direct, reliable fix: the overlay
2327   // stays a normal full-screen `position:fixed; inset:0` backdrop
2328   // (unaffected by the keyboard, which is fine - the keyboard renders
2329   // on top of it anyway), and only the sheet moves.
2330   if (window.visualViewport) {
2331      window.visualViewport.addEventListener('resize', function () {
2332         var vv = window.visualViewport;
2333         // How much of the layout viewport's bottom the keyboard now
2334         // covers. The layout viewport (and this fixed-position overlay)
2335         // doesn't shrink for the keyboard the way the visual viewport
2336         // does, so this is the gap we need to close by hand.
2337         var keyboardInset = Math.max(window.innerHeight - vv.height - vv.offsetTop, 0);
2338         document.querySelectorAll('.property-selector-overlay.show').forEach(function (overlay) {
2339            var sheet = overlay.querySelector('.property-selector');
2340            if (!sheet) return;
2341            sheet.style.transform = keyboardInset > 0 ? 'translateY(-' + keyboardInset + 'px)' : '';
2342
2343            // The mobile bottom sheet itself is capped at a static
2344            // max-height:88vh in CSS, which has no idea the keyboard just
2345            // ate part of the screen - once the keyboard opens, 88vh can
2346            // be taller than what's actually left above it, so the sheet
2347            // overflows upward past the top of the screen instead of
2348            // shrinking to fit. Override it with the real available
2349            // height so the sheet (and its Continue/Verify button) always
2350            // stays fully on-screen, above the keyboard.
2351            sheet.style.maxHeight = Math.max(vv.height - 16, 200) + 'px';
2352         });
2353      });
2354   }
2355
2356   // ==========================================================
2357   // PENDING-ACTION RESUME - remembers which gated element the tenant
2358   // clicked before the auth wall interrupted them (Contact Now,
2359   // Shortlist, WhatsApp, gallery nav, ...), and re-fires that same click
2360   // once they're authenticated and back on the page. Backed by
2361   // sessionStorage rather than a JS variable because completing auth
2362   // today always involves a full page reload (returning tenant, see
2363   // loginRequest() below) or a redirect (new tenant, see
2364   // registerRequest()/saveRequirements() below) - anything "remembered"
2365   // has to survive that.
2366   // ==========================================================
2367   var LR_PENDING_ACTION_KEY = 'lr_pending_action';
2368
2369   // Attribute names this site uses, across different card markups, to
2370   // identify which property/listing a gated button belongs to - a plain
2371   // class selector alone (e.g. ".btn_contact_owner") matches every card
2372   // on a search-results page, not just the one actually clicked. Detail
2373   // pages use data-property-id/data-property-type (see paywallScripts.
2374   // blade.php's .btn-hart-icon); listing cards use p_id/p_user_id (see
2375   // _propertyCardV2.blade.php's .btn_contact_owner). Checked as a list so
2376   // one function covers both without callers needing to know which.
2377   var LR_PENDING_ACTION_ID_ATTRS = ['data-property-id', 'data-property-type', 'p_id', 'p_user_id'];
2378
2379   function setPendingAction(el) {
2380      if (!el) return;
2381      try {
2382         var classSelector = el.className ? '.' + String(el.className).trim().split(/\s+/).join('.') : el.tagName.toLowerCase();
2383         var selector = classSelector;
2384         LR_PENDING_ACTION_ID_ATTRS.forEach(function (attr) {
2385            var value = el.getAttribute && el.getAttribute(attr);
2386            if (value) selector += '[' + attr + '="' + value + '"]';
2387         });
2388         sessionStorage.setItem(LR_PENDING_ACTION_KEY, JSON.stringify({
2389            selector: selector,
2390            return_url: window.location.href
2391         }));
2392      } catch (e) { /* sessionStorage unavailable (private mode, etc.) - fail silent, worst case the tenant just re-taps */ }
2393   }
2394
2395   function getPendingAction() {
2396      try {
2397         var raw = sessionStorage.getItem(LR_PENDING_ACTION_KEY);
2398         return raw ? JSON.parse(raw) : null;
2399      } catch (e) { return null; }
2400   }
2401
2402   function clearPendingAction() {
2403      try { sessionStorage.removeItem(LR_PENDING_ACTION_KEY); } catch (e) { /* noop */ }
2404   }
2405
2406   var LR_SHOW_MATCH_MORE_TTL_MS = 15000;
2407   var LR_RESUME_POLL_INTERVAL_MS = 300;
2408   var LR_RESUME_POLL_MAX_ATTEMPTS = 20; // ~6s total - covers the listing page's AJAX card load, not just a static page
2409
2410   function resumePendingAction() {
2411      var pending = getPendingAction();
2412      if (!pending || !pending.selector) return;
2413      // Still mid-auth (e.g. OTP modal reopened after a resend) - leave
2414      // the marker in place rather than clearing it prematurely.
2415      if (!lrIsLoggedIn) return;
2416      clearPendingAction();
2417      // Timestamped (not a plain flag) so it self-expires - read by two
2418      // places: the resumed action's own success handler
2419      // (consumeShowMatchMoreFlag(), below) to decide whether to offer the
2420      // optional "get matched" card, and the $needsRequirement composer
2421      // check further down this file, to avoid popping the full wizard
2422      // open on top of a resume that's about to self-heal via
2423      // silentSaveRequirementFromProperty() within moments. Set now,
2424      // before the element is even found - the composer check runs
2425      // synchronously at this same page load, well before the polling
2426      // below could resolve on a slow AJAX-loaded page, so it needs the
2427      // flag set up front, not only once a click actually fires.
2428      try { sessionStorage.setItem('lr_show_match_more', String(Date.now())); } catch (e) { /* noop */ }
2429
2430      // Deferred to jQuery's own ready queue, not fired directly off this
2431      // native DOMContentLoaded listener - paywallScripts.blade.php (and
2432      // every other gated handler) binds its clicks inside $(document).
2433      // ready(...), and jQuery's ready callbacks aren't guaranteed to run
2434      // in the same tick/order as native DOMContentLoaded listeners.
2435      // Confirmed the hard way: the very first version of this fired
2436      // el.click() before that handler was bound, so the click landed on
2437      // nothing - no error, just silently no-op, with the pending action
2438      // already cleared above. Queuing through $(fn) instead guarantees
2439      // this runs after every ready() handler already registered by the
2440      // time this line executes, including paywallScripts.blade.php's
2441      // (it's included earlier in the page than this script block).
2442      $(function () {
2443         // Polls rather than a single querySelector attempt: the detail
2444         // page's buttons exist by now, but search-results pages
2445         // (property/listing.blade.php) load their cards via a separate
2446         // AJAX call after ready fires (see filteredProperties()/
2447         // listingLoadV2.blade.php) - a one-shot lookup would still miss
2448         // those.
2449         var attempts = 0;
2450         (function tryResume() {
2451            var el;
2452            try { el = document.querySelector(pending.selector); } catch (e) { el = null; }
2453            if (el) {
2454               el.click();
2455               return;
2456            }
2457            attempts++;
2458            if (attempts < LR_RESUME_POLL_MAX_ATTEMPTS) {
2459               setTimeout(tryResume, LR_RESUME_POLL_INTERVAL_MS);
2460            }
2461            // Gives up silently past the timeout - worst case the tenant
2462            // just taps the button again themselves, same as today.
2463         })();
2464      });
2465   }
2466
2467   function consumeShowMatchMoreFlag() {
2468      try {
2469         var setAt = parseInt(sessionStorage.getItem('lr_show_match_more') || '0', 10);
2470         sessionStorage.removeItem('lr_show_match_more');
2471         return !!setAt && (Date.now() - setAt) < LR_SHOW_MATCH_MORE_TTL_MS;
2472      } catch (e) { return false; }
2473   }
2474
2475   // Onboarding progress bar / step-kicker (see login-modal.blade.php's
2476   // .ob-progress/.ob-step-kicker, styled in onboarding-v2.css). Driven by
2477   // a MutationObserver on the 'show' class rather than patching every
2478   // open*Modal()/addClass("show") call site individually - additive,
2479   // can't miss a path, can't interfere with the existing show/hide logic.
2480   //
2481   // Three orders: SHORT (property-triggered, pending action present),
2482   // OWNER (generic entry, owner/broker), FULL (generic entry, search
2483   // chosen). Mobile/OTP/name/purpose default to OWNER's shorter count
2484   // (a real prefix of FULL, not a guess) rather than front-loading FULL's
2485   // bigger total before anyone's said who they are - it only expands to
2486   // FULL once "Searching for a home" is actually chosen, so an owner/
2487   // broker never sees a bigger number than what they'll actually face.
2488   //
2489   // Owner/broker still see locationModalOverlay after purpose - it's not
2490   // skipped, unlike the rest of the tenant-only wizard (property type/
2491   // category/tenant type/budget). openLocationModal()'s owner/broker copy
2492   // ("Enter your house/office location") shows it captures the owner's
2493   // own property address via updateLocation(), not a search preference -
2494   // skipping it would silently drop real data this feature depends on.
2495   // A returning user's journey is two screens: sendOTP finds the number
2496   // (User::firstWhere('phone_number')) and returns the user record, accountOTP
2497   // sets request_type='login', and otpVerification() then closes the modal and
2498   // reloads. There is no third screen for them, so every longer order below
2499   // over-promises by at least three.
2500   var LR_OB_RETURNING_ORDER = ['loginModalOverlay', 'otpModalOverlay'];
2501   var LR_OB_SHORT_ORDER = ['loginModalOverlay', 'otpModalOverlay', 'nameModalOverlay', 'tenantTypeModalOverlay'];
2502   var LR_OB_OWNER_ORDER = ['loginModalOverlay', 'otpModalOverlay', 'nameModalOverlay', 'purposeModalOverlay', 'locationModalOverlay'];
2503   var LR_OB_FULL_ORDER = ['loginModalOverlay', 'otpModalOverlay', 'nameModalOverlay', 'purposeModalOverlay', 'locationModalOverlay', 'propertyTypeModalOverlay', 'categoryModalOverlay', 'tenantTypeModalOverlay', 'budgetEstimateModalOverlay'];
2504
2505   function lrUpdateOnboardingProgress(overlay) {
2506      // Opened on its own rather than as a wizard step - the homepage asking a
2507      // logged-out visitor where they are looking. There is no step 1 to have
2508      // come from and no step 6 to go to, so "STEP 5 OF 5" over a single
2509      // question is just confusing. Blank the indicator rather than leaving
2510      // whatever the last wizard run wrote into it.
2511      if (overlay.dataset && overlay.dataset.lrStandalone === '1') {
2512         var pEl = overlay.querySelector('.ob-progress');
2513         var kEl = overlay.querySelector('.ob-step-kicker');
2514         if (pEl) pEl.innerHTML = '';
2515         if (kEl) kEl.textContent = '';
2516         return;
2517      }
2518
2519      // request_type is set by accountOTP() from the sendOTP response BEFORE
2520      // otpVerification() opens the OTP screen, so it is already correct by the
2521      // time the observer fires here. Checked first: a returning user's path is
2522      // two screens regardless of pending action or purpose.
2523      var order = request_type === 'login'
2524         ? LR_OB_RETURNING_ORDER
2525         : (getPendingAction() ? LR_OB_SHORT_ORDER : (userPurpose === 'search' ? LR_OB_FULL_ORDER : LR_OB_OWNER_ORDER));
2526      var idx = order.indexOf(overlay.id);
2527      if (idx === -1) return;
2528      var progressEl = overlay.querySelector('.ob-progress');
2529      var kickerEl = overlay.querySelector('.ob-step-kicker');
2530      if (progressEl) {
2531         progressEl.innerHTML = '';
2532         for (var i = 0; i < order.length; i++) {
2533            var seg = document.createElement('i');
2534            if (i <= idx - 1) seg.classList.add('ob-done');
2535            progressEl.appendChild(seg);
2536         }
2537      }
2538      // The total is genuinely unknown on the first two screens. A RETURNING
2539      // user finishes at the OTP step - otpVerification() calls closeOTPModal()
2540      // and reloads, so there is no step 3 for them - while a new user still
2541      // faces 2 to 7 more depending on purpose. Nothing before OTP verification
2542      // distinguishes the two, so any total shown there is a guess, and for a
2543      // returning user "STEP 1 OF 5" is a guess that's wrong by three screens.
2544      //
2545      // Show the position without the claim until the path is known.
2546      // The phone screen still cannot know - nothing has looked the number up
2547      // yet. From the OTP screen on, request_type distinguishes returning from
2548      // new, so the total is real and worth showing.
2549      if (kickerEl) {
2550         kickerEl.textContent = idx >= 1
2551            ? 'STEP ' + (idx + 1) + ' OF ' + order.length
2552            : 'STEP ' + (idx + 1);
2553      }
2554   }
2555
2556   document.addEventListener('DOMContentLoaded', function () {
2557      var lrObObserver = new MutationObserver(function (mutations) {
2558         mutations.forEach(function (m) {
2559            var el = m.target;
2560            if (el.classList && el.classList.contains('property-selector-overlay') && el.classList.contains('show')) {
2561               lrUpdateOnboardingProgress(el);
2562            }
2563         });
2564      });
2565      document.querySelectorAll('.property-selector-overlay').forEach(function (el) {
2566         lrObObserver.observe(el, { attributes: true, attributeFilter: ['class'] });
2567      });
2568   });
2569
2570   // Keyboard-gap VisualViewport tracker removed - see onboarding-v2.css's
2571   // comment near the bottom for why (broke tap/focus on the phone-number
2572   // input on a real mobile device).
2573
2574   function openLoginPopup(event) {
2575      if (event) { event.stopPropagation(); event.preventDefault(); }
2576      var overlay = document.getElementById('loginModalOverlay');
2577      if (overlay) { overlay.classList.add('show'); hpLockBodyScroll(); }
2578   }
2579   function closeLoginModal(event) {
2580      if (event) event.stopPropagation();
2581      var overlay = document.getElementById('loginModalOverlay');
2582      if (overlay) { overlay.classList.remove('show'); hpUnlockBodyScroll(); }
2583   }
2584   function openOTPModal() {
2585      $("#otpModalOverlay").addClass("show");
2586      hpLockBodyScroll();
2587      setTimeout(function () { $("#otp1").focus(); }, 200);
2588   }
2589   function closeOTPModal(event) {
2590      if (event) event.stopPropagation();
2591      $("#otpModalOverlay").removeClass("show");
2592      hpUnlockBodyScroll();
2593   }
2594   // Lets a user who mistyped their number back out of the OTP screen
2595   // without starting over - closes OTP, reopens the phone number modal
2596   // with what they typed still in the box so they only need to fix it.
2597   function editOTPPhoneNumber(event) {
2598      if (event) { event.preventDefault(); event.stopPropagation(); }
2599      closeOTPModal();
2600      openLoginPopup();
2601      var phoneInput = document.getElementById('loginPhoneNumber');
2602      if (phoneInput) {
2603         phoneInput.value = register_phone_number || '';
2604         phoneInput.focus();
2605      }
2606   }
2607   function openNameModal(event) {
2608      if (event) { event.stopPropagation(); event.preventDefault(); }
2609      var overlay = document.getElementById('nameModalOverlay');
2610      if (overlay) { overlay.classList.add('show'); hpLockBodyScroll(); }
2611   }
2612   function closeNameModal(event) {
2613      if (event) event.stopPropagation();
2614      var overlay = document.getElementById('nameModalOverlay');
2615      if (overlay) { overlay.classList.remove('show'); hpUnlockBodyScroll(); }
2616   }
2617   function openAccountTypeModal() {
2618      var overlay = document.getElementById('accountTypeModalOverlay');
2619      if (overlay) { overlay.classList.add('show'); hpLockBodyScroll(); }
2620   }
2621   function closeAccountTypeModal(event) {
2622      if (event) event.stopPropagation();
2623      var overlay = document.getElementById('accountTypeModalOverlay');
2624      if (overlay) { overlay.classList.remove('show'); hpUnlockBodyScroll(); }
2625   }
2626   function closePurposeModal(event) {
2627      if (event) event.stopPropagation();
2628      var overlay = document.getElementById('purposeModalOverlay');
2629      if (overlay) { overlay.classList.remove('show'); hpUnlockBodyScroll(); }
2630   }
2631   /**
2632    * @param  standalone  Opened outside the signup wizard - the homepage asking a
2633    *                    logged-out visitor where they are looking. Suppresses the
2634    *                    step indicator and lets the visitor dismiss it, because
2635    *                    unlike a wizard step there is nothing to complete: the
2636    *                    listings are already rendered behind it on the server's
2637    *                    default location.
2638    */
2639   function openLocationModal(userTypeArg, standalone) {
2640      var overlay = document.getElementById('locationModalOverlay');
2641      var title = document.querySelector('.login-modal-title');
2642      var description = document.querySelector('.location-modal-description');
2643      if (overlay) { overlay.dataset.lrStandalone = standalone ? '1' : '0'; }
2644      if (userTypeArg === 'owner') {
2645         title.innerHTML = 'Where\'s your place?';
2646         description.innerText = 'This is what tenants and buyers will see, so get it right.';
2647      } else if (userTypeArg === 'broker') {
2648         title.innerHTML = 'Where\'s your office?';
2649         description.innerText = 'This is what tenants and buyers will see, so get it right.';
2650      } else if (userTypeArg === 'user') {
2651         title.innerHTML = 'Where are you looking?';
2652         description.innerText = 'A city or locality is enough to start.';
2653      } else {
2654         description.innerText = 'Please select your preferred location.';
2655      }
2656      if (overlay) {
2657         // Standalone only: clicking the backdrop dismisses. In the wizard this
2658         // stays trapped on purpose - there the answer is required. Here it is
2659         // not, and a first-time visitor who cannot get past it to see a single
2660         // listing is the friction this was meant to avoid. The inner card stops
2661         // propagation, so this fires only for the backdrop itself.
2662         overlay.onclick = standalone
2663            ? function (e) { if (e.target === overlay) { closeLocationModal(); } }
2664            : null;
2665         overlay.classList.add('show');
2666         hpLockBodyScroll();
2667      }
2668   }
2669   function closeLocationModal(event) {
2670      if (event) event.stopPropagation();
2671      var overlay = document.getElementById('locationModalOverlay');
2672      if (overlay) { overlay.classList.remove('show'); hpUnlockBodyScroll(); }
2673   }
2674   function closeCategoryModal(event) {
2675      if (event) event.stopPropagation();
2676      $("#categoryModalOverlay").removeClass("show");
2677      hpUnlockBodyScroll();
2678   }
2679
2680   function loginButton() {
2681      var registerPhoneNumber = $('#loginPhoneNumber').val();
2682      var btn = document.querySelector('.btn_login');
2683      if (registerPhoneNumber === '') {
2684         $('.login-phone-error').text('Please enter mobile number').show();
2685         $('.login-phone-wrapper').addClass('error');
2686         return;
2687      }
2688      if (registerPhoneNumber.length !== 10) {
2689         $('.login-phone-error').text('Mobile number must be 10 digits').show();
2690         $('.login-phone-wrapper').addClass('error');
2691         return;
2692      }
2693      $('.login-phone-error').hide();
2694      $('.login-phone-wrapper').removeClass('error');
2695      register_phone_number = registerPhoneNumber;
2696      btn.innerText = 'Processing...';
2697      btn.disabled = true;
2698      btn.style.backgroundColor = '#b0b0b0';
2699      btn.style.cursor = 'not-allowed';
2700      accountOTP(user_type, '', register_full_name, register_phone_number, register_email, register_password, 'normal', '', '');
2701   }
2702
2703   function accountOTP(user_type, user_image, register_full_name, register_phone_number, register_email, register_password, social_type, social_id, trail_user) {
2704      $.ajax({
2705         url: 'https://www.letsrentz.com/auth/sendOTP',
2706         method: 'POST',
2707         data: {
2708            user_type: user_type, full_name: register_full_name, phone_number: register_phone_number,
2709            email: register_email, password: register_password, social_type: social_type, social_id: social_id,
2710            _token: 'bvRQeC7zDEZMxhJdt8x263POgOGJvjDEH4HLTaIw'
2711         },
2712         success: function (model) {
2713            var btn = document.querySelector('.btn_login');
2714            if (model.status) {
2715               // Clear any digits left over from a previous OTP screen
2716               // (eg going back via the edit-number pencil, then
2717               // resubmitting) - otherwise old digits stay visible even
2718               // though they don't match the OTP that was just (re)sent.
2719               // Re-filled below if this response auto-fills a fresh one.
2720               $('.otp-input').val('');
2721               if (btn) { btn.innerText = 'Continue'; btn.disabled = false; btn.style.backgroundColor = '#FC4A0A'; }
2722               closeLoginModal();
2723               if (model.data == null) {
2724                  request_type = 'register';
2725                  if (model.is_phone != 1 && model.otp_verification == 1) {
2726                     if (model.automate_otp == 1 && model.otp) {
2727                        var otpStr = model.otp.toString().trim();
2728                        var inputs = document.querySelectorAll('.otp-input');
2729                        inputs.forEach(function (input, index) { input.value = otpStr[index] || ''; });
2730                        var continueBtn = document.querySelector('.otp_continue_btn');
2731                        if (continueBtn) { continueBtn.style.backgroundColor = 'rgba(255,84,3,1)'; continueBtn.style.color = '#fff'; continueBtn.disabled = false; }
2732                     }
2733                     otpVerification(user_type, user_image, register_full_name, register_phone_number, register_email, register_password, social_type, social_id, model.otp, request_type, trail_user);
2734                  }
2735               } else {
2736                  request_type = 'login';
2737                  if (model.automate_otp == 1 && model.otp) {
2738                     var otpStr2 = model.otp.toString().trim();
2739                     var inputs2 = document.querySelectorAll('.otp-input');
2740                     inputs2.forEach(function (input, index) { input.value = otpStr2[index] || ''; });
2741                     var continueBtn2 = document.querySelector('.otp_continue_btn');
2742                     if (continueBtn2) { continueBtn2.style.backgroundColor = 'rgba(255,84,3,1)'; continueBtn2.style.color = '#fff';
2742 continueBtn2.disabled = false; }
2743                  }
2744                  otpVerification(user_type, user_image, register_full_name, register_phone_number, register_email, register_password, social_type, social_id, model.otp, request_type, trail_user);
2745               }
2746            } else {
2747               if (btn) { btn.innerText = 'Continue'; btn.disabled = false; btn.style.backgroundColor = '#FC4A0A'; }
2748               showToast(model.message);
2749            }
2750         },
2751         error: function () {
2752            var btn = document.querySelector('.btn_login');
2753            if (btn) { btn.innerText = 'Continue'; btn.disabled = false; btn.style.backgroundColor = '#FC4A0A'; }
2754            showToast('Something went wrong. Please try again.');
2755         }
2756      });
2757   }
2758
2759   function otpVerification(newUserType, newUserImage, newRegisterFullName, newRegisterPhoneNumber, newRegisterEmail, newRegisterPassword, newSocialType, newSocialId, newOtp, requestTypeArg, newTrailUser) {
2760      closeLoginModal();
2761      openOTPModal();
2762      $("#otpMobileNumber").text('+91 | ' + newRegisterPhoneNumber);
2763      otp = newOtp;
2764      user_type = newUserType;
2765      register_full_name = newRegisterFullName;
2766      register_phone_number = newRegisterPhoneNumber;
2767      register_email = newRegisterEmail;
2768      register_password = newRegisterPassword;
2769      social_type = newSocialType;
2770      trail_user = newTrailUser;
2771   }
2772
2773   function resendOTP(event) {
2774      if (event) { event.preventDefault(); event.stopPropagation(); }
2775      if (!register_phone_number) return;
2776      $('.otp-input').val('');
2777      $('#otp1').focus();
2778      toastr.success('OTP resent successfully');
2779      accountOTP(user_type, '', register_full_name, register_phone_number, register_email, register_password, social_type, '', trail_user);
2780   }
2781
2782   function getOTP() {
2783      var otpInputs = $('.otp-input').get();
2784      var result = '';
2785      otpInputs.forEach(function (input) { result += input.value; });
2786      return result;
2787   }
2788
2789   function otpVerificationBtn() {
2790      var btn = document.querySelector('.otp_continue_btn');
2791      if (btn) { btn.innerText = 'Processing...'; btn.disabled = true; btn.style.backgroundColor = '#b0b0b0'; }
2792      var fullOTP = getOTP();
2793      if (otp == fullOTP) {
2794         if (request_type == 'login') {
2795            // OTP modal stays open (showing the Processing state set
2796            // above) until loginRequest's own ajax actually resolves -
2797            // closeOTPModal() used to run right here, synchronously,
2798            // before the request had even been sent (ajax is async), so
2799            // the popup vanished immediately and the tenant stared at a
2800            // blank page for the whole round-trip with no sign anything
2801            // was happening. loginRequest()'s own success/error handlers
2802            // close it (or reset the button on failure) once they
2803            // actually know the outcome.
2804            loginRequest(register_phone_number);
2805         } else if (trail_user == 'trail_user') {
2806            registerRequest(user_type, user_image, register_full_name, register_phone_number, register_email, register_password, social_type, social_id);
2807         } else if (request_type == 'register') {
2808            openNameModal();
2809            closeOTPModal();
2810         }
2811      } else {
2812         if (btn) { btn.innerText = 'Verify'; btn.disabled = false; btn.style.backgroundColor = '#FC4A0A'; }
2813         $('.otp_verification_error').show();
2814         $('.otp_verification_error p').text('Please enter valid OTP');
2815      }
2816   }
2817
2818   function loginRequest(loginPhoneNumber) {
2819      var loginDeviceType = detectDevice() === 'mobile' ? 'mobile_web' : 'desktop_web';
2820      $.ajax({
2821         url: 'https://www.letsrentz.com/auth/login',
2822         method: 'POST',
2823         data: { phone_number: loginPhoneNumber, device_type: loginDeviceType, fcm_token: '', _token: 'bvRQeC7zDEZMxhJdt8x263POgOGJvjDEH4HLTaIw' },
2824         success: function (response) {
2825            if (response.status) {
2826               // Same real login tracking as template.blade.php's loginRequest().
2827               window.dataLayer = window.dataLayer || [];
2828               window.dataLayer.push({
2829                  'event': 'login',
2830                  'authentication_method': response.data.social_type,
2831                  'user_id': response.data.user_id,
2832               });
2833               fbq('track', 'login', {
2834                  user_id: response.data.user_id,
2835                  authentication_method: response.data.social_type,
2836                  user_type: response.data.user_type,
2837               });
2838               localStorage.setItem('login_token', response.data.loginToken);
2839               closeOTPModal();
2840               if (response.data.user_type === 'owner') {
2841                  window.location.href = 'https://www.letsrentz.com/mypropertylisting';
2842               } else {
2843                  window.location.reload();
2844               }
2845            } else {
2846               lrResetOtpButton();
2847               showToast(response.message || 'Something went wrong. Please try again.');
2848            }
2849         },
2850         error: function () { lrResetOtpButton(); showToast('Something went wrong. Please try again.'); }
2851      });
2852   }
2853   // Restores the OTP screen's Verify button after a failed login/register
2854   // ajax call - otherwise it's left stuck disabled/"Processing..." with
2855   // no way to retry, since the modal now stays open through the request
2856   // (see otpVerificationBtn()) instead of closing before it's sent.
2857   function lrResetOtpButton() {
2858      var btn = document.querySelector('.otp_continue_btn');
2859      if (btn) { btn.innerText = 'Verify'; btn.disabled = false; btn.style.backgroundColor = '#FC4A0A'; }
2860   }
2861
2862   function registerRequest(user_type, user_image, register_full_name, register_phone_number, register_email, register_password, social_type, social_id) {
2863      var deviceType = detectDevice() === 'mobile' ? 'mobile_web' : 'desktop_web';
2864      $.ajax({
2865         url: 'https://www.letsrentz.com/auth/register',
2866         method: 'POST',
2867         data: {
2868            user_type: user_type, full_name: register_full_name, phone_number: register_phone_number,
2869            email: register_email, password: register_password, device_type: deviceType,
2870            social_type: social_type, social_id: social_id, referred_from: referred_from,
2871            ga_client_id: getGA4ClientId(), fcm_token: '', _token: 'bvRQeC7zDEZMxhJdt8x263POgOGJvjDEH4HLTaIw'
2872         },
2873         success: function (response) {
2874            if (response.status) {
2875               localStorage.setItem('login_token', response.data.loginToken);
2876               // A real session now exists server-side (AuthController::register()
2877               // calls Auth::attempt), but updateLocation()'s @if(!Auth::check())
2878               // was compiled when this page first rendered, before signup - it
2879               // can't see this. This flag lets that branch tell the two cases
2880               // apart at runtime instead of always assuming logged-out.
2881               window.lrJustRegistered = true;
2882               // Same real registration tracking as template.blade.php's
2883               // registerRequest() - GA4 'sign_up' fires server-side (see
2884               // AuthController::register(), using the ga_client_id sent
2885               // above) so it isn't duplicated here either.
2886               window.dataLayer = window.dataLayer || [];
2887               window.dataLayer.push({
2888                  'event': 'register',
2889                  'user_type': response.data.user_type,
2890                  'authentication_method': response.data.social_type,
2891                  'user_id': response.data.user_id,
2892               });
2893               gtag('event', 'conversion', {
2894                  'send_to': 'AW-11395525747/doGBCL2NmPYZEPPY57kq',
2895                  'value': 1.0,
2896                  'currency': 'INR'
2897               });
2898               // eventID matches the CAPI event_id sent server-side for the
2899               // same user_id, so Meta dedupes the browser + server fires
2900               // into a single counted registration instead of two.
2901               fbq('track', 'CompleteRegistration', {
2902                  user_type: response.data.user_type,
2903                  user_id: response.data.user_id
2904               }, {eventID: response.data.user_id + '_complete_registration'});
2905               if (response.data.user_type === 'owner') {
2906                  fbq('track', 'owner_register', {
2907                     user_id: response.data.user_id,
2908                     authentication_method: response.data.social_type,
2909                  }, {eventID: response.data.user_id + '_owner_register'});
2910               } else if (response.data.user_type === 'broker') {
2911                  fbq('track', 'broker_register', {
2912                     user_id: response.data.user_id,
2913                     authentication_method: response.data.social_type,
2914                  }, {eventID: response.data.user_id + '_broker_register'});
2915               } else if (response.data.user_type === 'user') {
2916                  fbq('track', 'tenant_register', {
2917                     user_id: response.data.user_id,
2918                     authentication_method: response.data.social_type,
2919                  }, {eventID: response.data.user_id + '_tenant_register'});
2920               }
2921               closeAccountTypeModal();
2922               // Also close the OTP modal - it stays open through this
2923               // request when registerRequest() is reached straight from
2924               // otpVerificationBtn()'s trail_user branch (no name/purpose
2925               // screen in between); harmless no-op otherwise, since it's
2926               // already closed by the time purpose_continue_btn calls in.
2927               closeOTPModal();
2928               var pendingAfterRegister = getPendingAction();
2929               if (trail_user == 'trail_user') {
2930                  window.location.href = 'https://www.letsrentz.com/mypropertylisting';
2931               } else if (pendingAfterRegister) {
2932                  // Registered because of a gated tap on a specific
2933                  // listing - skip the location/property-type/category/
2934                  // tenant-type/budget wizard entirely (requirement
2935                  // capture doesn't depend on it, see
2936                  // silentSaveRequirementFromProperty()) and go straight
2937                  // back to finish what the tenant actually came to do.
2938                  // register() already established a real session
2939                  // server-side (Auth::attempt in AuthController), so this
2940                  // navigation lands authenticated and resumePendingAction()
2941                  // (wired into the shared DOMContentLoaded listener) picks
2942                  // up from sessionStorage and re-fires the original click.
2943                  window.location.href = pendingAfterRegister.return_url;
2944               } else {
2945                  openLocationModal(response.data.user_type);
2946               }
2947            } else {
2948               // Whichever button actually triggered this call - name/
2949               // account-type (legacy path), purpose (current path), or
2950               // otp (trail_user, skips straight from OTP) - restore it so
2951               // the tenant isn't stuck on a permanently-disabled button.
2952               var btn = document.querySelector('.name_continue_btn:disabled') || document.querySelector('.account_type_continue_btn:disabled')
2953                  || document.querySelector('.purpose_continue_btn:disabled') || document.querySelector('.otp_continue_btn:disabled');
2954               if (btn) { btn.innerText = 'Continue'; btn.disabled = false; btn.style.backgroundColor = '#FC4A0A'; }
2955               showToast(response.message || 'Something went wrong. Please try again.');
2956            }
2957         },
2958         error: function () {
2959            var btn = document.querySelector('.name_continue_btn:disabled') || document.querySelector('.account_type_continue_btn:disabled')
2960               || document.querySelector('.purpose_continue_btn:disabled') || document.querySelector('.otp_continue_btn:disabled');
2961            if (btn) { btn.innerText = 'Continue'; btn.disabled = false; btn.style.backgroundColor = '#FC4A0A'; }
2962            showToast('Something went wrong. Please try again.');
2963         }
2964      });
2965   }
2966
2967   /*
2968    * The search location a visitor is browsing in, cached on the device.
2969    *
2970    * The apps keep this in their own storage and the account is the source of
2971    * truth once someone logs in; the web had nowhere to keep it at all, so every
2972    * logged-out visitor fell back to a hardcoded Mohali and a tenant in Delhi was
2973    * shown homes in Punjab. Same three values as the apps, written together -
2974    * a label that describes different coordinates is the bug this whole
2975    * workstream exists to remove.
2976    */
2977   var LR_SEARCH_LOCATION_KEY = 'lr_search_location';
2978
2979   function lrGetSearchLocation() {
2980      try {
2981         var raw = window.localStorage.getItem(LR_SEARCH_LOCATION_KEY);
2982         if (!raw) { return null; }
2983         var loc = JSON.parse(raw);
2984         // A partial cache entry is worse than none - it would search one place
2985         // while the header named another.
2986         if (!loc || !loc.address || !loc.lati || !loc.longi) { return null; }
2987         return loc;
2988      } catch (e) { return null; }
2989   }
2990
2991   function lrSaveSearchLocation(addressVal, latiVal, longiVal) {
2992      if (!addressVal || !latiVal || !longiVal) { return null; }
2993      var loc = { address: String(addressVal), lati: String(latiVal), longi: String(longiVal) };
2994      try { window.localStorage.setItem(LR_SEARCH_LOCATION_KEY, JSON.stringify(loc)); } catch (e) {}
2995      // Anything showing listings for this location can re-render off this.
2996      try { document.dispatchEvent(new CustomEvent('lr:search-location-changed', { detail: loc })); } catch (e) {}
2997      return loc;
2998   }
2999
3000   function updateLocation(btn) {
3001      var locationInputVal = $('#locationInput').val();
3002      if (locationInputVal == '') { showToast('Please enter your location.'); return false; }
3003
3004      // Logged out: there is no account to write to, so the pick is kept on the
3005      // device only and the page re-renders against it. Skipping the POST also
3006      // avoids sending an unauthenticated write the endpoint would reject.
3007               if (!lati || !longi) {
3008            showToast('Please select your location from the suggestions list.');
3009            return;
3010         }
3011         lrSaveSearchLocation(address, lati, longi);
3012         closeLocationModal();
3013         // This @if was compiled true at page-render time, before any signup
3014         // happened - but registerRequest() can complete mid-page-load and
3015         // establish a real session before this runs (see lrJustRegistered).
3016         // Reload so the header picks up the new logged-in state instead of
3017         // silently staying on the stale logged-out render.
3018         if (window.lrJustRegistered) {
3019            window.location.reload();
3020         }
3021         return;
3022            var originalText = btn.innerText;
3023      btn.innerText = 'Processing...';
3024      btn.disabled = true;
3025      btn.style.backgroundColor = '#b0b0b0';
3026      if (!lati || !longi) {
3027         showToast('Please select your location from the suggestions list.');
3028         btn.innerText = originalText;
3029         btn.disabled = false;
3030         btn.style.backgroundColor = '';
3031         return;
3032      }
3033      $.ajax({
3034         url: 'https://www.letsrentz.com/update-user-location',
3035         type: 'POST',
3036         data: { _token: 'bvRQeC7zDEZMxhJdt8x263POgOGJvjDEH4HLTaIw', address: address, lati: lati, longi: longi },
3037         success: function (response) {
3038            btn.innerText = 'Continue';
3039            btn.disabled = false;
3040            btn.style.backgroundColor = '#FC4A0A';
3041            if (response.status) {
3042               var pendingAfterLocation = getPendingAction();
3043               if (user_type == 'user' && pendingAfterLocation) {
3044                  // Registered because of a gated tap on a specific
3045                  // listing (Contact Now, Shortlist, ...) - the rest of
3046                  // the wizard (property type/category/tenant type/
3047                  // budget) is deferred to the optional post-success card
3048                  // instead of blocking here. Requirement capture itself
3049                  // doesn't depend on this wizard finishing - see
3050                  // silentSaveRequirementFromProperty(), fired from the
3051                  // gated actions themselves once the tenant is back.
3052                  closeLocationModal();
3053                  window.location.href = pendingAfterLocation.return_url;
3054               } else if (user_type == 'user') {
3055                  closeLocationModal();
3056                  $("#propertyTypeModalOverlay").addClass("show");
3057               } else if (user_type == 'owner') {
3058                  window.location.href = 'https://www.letsrentz.com/mypropertylisting';
3059               } else {
3060                  window.location.href = 'https://www.letsrentz.com/profiledetail';
3061               }
3062            } else {
3063               showToast('Failed to update location');
3064            }
3065         },
3066         error: function () {
3067            btn.innerText = originalText;
3068            btn.disabled = false;
3069            btn.style.backgroundColor = '';
3070            showToast('Something went wrong. Please try again.');
3071         }
3072      });
3073   }
3074
3075   // Keeps PlanRequirements current in the background from a property's
3076   // own known data, independent of whether the tenant ever runs the
3077   // location/type/category/tenant-type/budget wizard - see gated-action
3078   // call sites (Contact Now, Shortlist, WhatsApp) below. Only meant for
3079   // user_type=='user' accounts (tenant/buyer share that value, see
3080   // login-modal.blade.php) - callers are expected to check that before
3081   // calling this. Reuses AuthController::savePlanRequirements() as-is,
3082   // flagged silent=1 so it doesn't fire the broker "New Verified Tenant
3083   // Lead" push on every single click - see that method's own comment.
3084   var LR_TENANT_TYPE_MAP = { 'Family': 'family', 'Boys': 'boys', 'Girls': 'girls' };
3085   // Reads the compact data-lr-req JSON attribute that listing/search-
3086   // result cards carry (see _propertyCardV2.blade.php) - those don't have
3087   // a page-wide property_detail/lrPropertyDetail object like the detail
3088   // page does, since one page renders many cards, so the fields
3089   // silentSaveRequirementFromProperty() needs travel on the element
3090   // itself instead.
3091   function lrReqFromEl(el) {
3092      try {
3093         var raw = el && el.getAttribute && el.getAttribute('data-lr-req');
3094         return raw ? JSON.parse(raw) : null;
3095      } catch (e) { return null; }
3096   }
3097
3098   // DISABLED 2026-09-26: every Contact/WhatsApp/Shortlist tap was
3099   // overwriting the tenant's own requirement with that listing's exact
3100   // address, type, size and rent - the main source of wrong requirements.
3101   // Tenants now set it themselves in onboarding / Edit Requirement. Kept
3102   // as a no-op so the ~10 call sites don't need touching; the server also
3103   // ignores silent=1 saves (AuthController::savePlanRequirements) for
3104   // pages still cached with the old script.
3105   function silentSaveRequirementFromProperty(propertyDetail) {
3106      return;
3107      if (!propertyDetail || !propertyDetail.address) return;
3108      var payload = {
3109         silent: '1',
3110         address: propertyDetail.address,
3111         lati: propertyDetail.lati,
3112         longi: propertyDetail.longi,
3113         property_type_id: propertyDetail.property_type_id,
3114         category_id_new: propertyDetail.category_id,
3115         max_rent: propertyDetail.rent,
3116         _token: 'bvRQeC7zDEZMxhJdt8x263POgOGJvjDEH4HLTaIw'
3117      };
3118      // See lrResolveTenantType() below - the backend's filled() guard on
3119      // tenant_type means leaving this undefined here never blanks out a
3120      // value the tenant already gave through some other path.
3121      payload.tenant_type = lrResolveTenantType(propertyDetail);
3122      var url = 'https://www.letsrentz.com/saveTenantPlanRequirements';
3123      // sendBeacon when available - some of these calls (Contact Now)
3124      // happen right before the page navigates away, and a plain $.ajax
3125      // request has no guarantee of completing once that starts.
3126      if (navigator.sendBeacon) {
3127         var formData = new FormData();
3128         Object.keys(payload).forEach(function (key) {
3129            if (payload[key] !== undefined && payload[key] !== null) formData.append(key, payload[key]);
3130         });
3131         navigator.sendBeacon(url, formData);
3132      } else {
3133         $.ajax({ url: url, type: 'POST', data: payload });
3134      }
3135   }
3136
3137   // Optional, non-blocking engagement touch shown once, right after a
3138   // freshly-registered tenant's resumed action succeeds (flagged by
3139   // resumePendingAction() via consumeShowMatchMoreFlag() - never shown for
3140   // Contact Now, which navigates away before this could render, and never
3141   // shown from ordinary same-day browsing). The requirement itself is
3142   // already saved by the time this shows (silentSaveRequirementFromProperty
3143   // ran first) - this is purely "want us to actively notify you", not the
3144   // data-capture step.
3145   // Resolves tenant_type the same way for both the silent background
3146   // save and this card's own explicit submit: the tenant's own answer
3147   // (asked once, right after the name screen during property-triggered
3148   // registration - see .tenant_type_continue_btn) always wins; falls
3149   // back to the listing's own tag only when that answer was never asked
3150   // (a returning tenant's ordinary Shortlist click, say) and only when
3151   // the tag is unambiguous ("All"/"Company"/combos like "Family & Girls"
3152   // don't map to a single value).
3153   function lrResolveTenantType(propertyDetail) {
3154      var chosen = null;
3155      try { chosen = sessionStorage.getItem('lr_chosen_tenant_type'); } catch (e) { /* noop */ }
3156      if (chosen) return chosen;
3157      var tenantName = propertyDetail && propertyDetail.tenant && propertyDetail.tenant.name;
3158      return (tenantName && LR_TENANT_TYPE_MAP[tenantName]) || undefined;
3159   }
3160
3161   // DISABLED 2026-09-26 with silentSaveRequirementFromProperty() above -
3162   // its "Yes, keep me posted" also replaced the requirement the tenant
3163   // just picked with the listing's own details.
3164   function showMatchMoreCard(propertyDetail) {
3165      return;
3166      if (!propertyDetail || document.getElementById('lrMatchMoreCard')) return;
3167      var typeName = (propertyDetail.property_type && propertyDetail.property_type.name) || '';
3168      var categoryName = (propertyDetail.category && propertyDetail.category.name) || '';
3169      var budget = propertyDetail.rent ? '₹' + Number(propertyDetail.rent).toLocaleStr
3169ing('en-IN') : '';
3170
3171      var card = document.createElement('div');
3172      card.id = 'lrMatchMoreCard';
3173      card.setAttribute('role', 'dialog');
3174      card.setAttribute('aria-label', 'Get matched with more properties like this');
3175      card.style.cssText = 'position:fixed;left:16px;right:16px;bottom:16px;max-width:420px;margin:0 auto;background:#fff;border-radius:14px;box-shadow:0 8px 30px rgba(0,0,0,.18);padding:16px 18px;z-index:2100;font-family:inherit;';
3176      card.innerHTML =
3177         '<div style="display:flex;justify-content:space-between;align-items:flex-start;gap:8px;">' +
3178            '<strong style="font-size:15px;">Get matched with more like this?</strong>' +
3179            '<button type="button" data-lr-close aria-label="Dismiss" style="background:none;border:none;font-size:18px;line-height:1;cursor:pointer;color:#999;">&times;</button>' +
3180         '</div>' +
3181         '<div style="display:flex;flex-wrap:wrap;gap:6px;margin:10px 0;">' +
3182            (propertyDetail.address ? '<span style="font-size:12px;background:#f3f3f3;border-radius:6px;padding:3px 8px;">' + propertyDetail.address + '</span>' : '') +
3183            (typeName ? '<span style="font-size:12px;background:#f3f3f3;border-radius:6px;padding:3px 8px;">' + typeName + '</span>' : '') +
3184            (categoryName ? '<span style="font-size:12px;background:#f3f3f3;border-radius:6px;padding:3px 8px;">' + categoryName + '</span>' : '') +
3185            (budget ? '<span style="font-size:12px;background:#f3f3f3;border-radius:6px;padding:3px 8px;">~' + budget + '</span>' : '') +
3186         '</div>' +
3187         '<div style="display:flex;gap:10px;margin-top:4px;">' +
3188            '<button type="button" data-lr-yes style="flex:1;background:#FC4A0A;color:#fff;border:none;border-radius:8px;padding:10px;font-size:14px;cursor:pointer;">Yes, keep me posted</button>' +
3189            '<button type="button" data-lr-not-now style="background:none;border:1px solid #ddd;border-radius:8px;padding:10px 14px;font-size:14px;cursor:pointer;">Not now</button>' +
3190         '</div>';
3191      document.body.appendChild(card);
3192
3193      function dismiss() { if (card.parentNode) card.parentNode.removeChild(card); }
3194      card.querySelector('[data-lr-close]').addEventListener('click', dismiss);
3195      card.querySelector('[data-lr-not-now]').addEventListener('click', dismiss);
3196
3197      card.querySelector('[data-lr-yes]').addEventListener('click', function () {
3198         var yesBtn = card.querySelector('[data-lr-yes]');
3199         yesBtn.disabled = true; yesBtn.textContent = 'Saving...';
3200         $.ajax({
3201            url: 'https://www.letsrentz.com/saveTenantPlanRequirements',
3202            type: 'POST',
3203            data: {
3204               address: propertyDetail.address,
3205               lati: propertyDetail.lati,
3206               longi: propertyDetail.longi,
3207               property_type_id: propertyDetail.property_type_id,
3208               category_id_new: propertyDetail.category_id,
3209               max_rent: propertyDetail.rent,
3210               tenant_type: lrResolveTenantType(propertyDetail),
3211               _token: 'bvRQeC7zDEZMxhJdt8x263POgOGJvjDEH4HLTaIw'
3212            },
3213            success: function () { showToast("You're all set - we'll keep you posted."); dismiss(); },
3214            error: function () { showToast('Something went wrong. Please try again.'); yesBtn.disabled = false; yesBtn.textContent = 'Yes, keep me posted'; }
3215         });
3216      });
3217   }
3218
3219   function saveRequirements(button) {
3220      $.ajax({
3221         url: 'https://www.letsrentz.com/saveTenantPlanRequirements',
3222         type: "POST",
3223         data: {
3224            property_type_id: userPropertySearchData.selectedUserPropertyTypeId,
3225            category_id_new: userPropertySearchData.selectedUserCategoryId,
3226            tenant_type: userPropertySearchData.selectedTenantType,
3227            max_rent: userPropertySearchData.selectedUserBudget,
3228            address: address, lati: lati, longi: longi,
3229            _token: 'bvRQeC7zDEZMxhJdt8x263POgOGJvjDEH4HLTaIw'
3230         },
3231         success: function (response) {
3232            if (response.status) {
3233               $("#budgetEstimateModalOverlay").removeClass("show");
3234               // A pending action means this wizard run started from a
3235               // gated tap on a specific listing (only possible today via
3236               // the generic entry point reaching this far, e.g. the
3237               // account icon with no property in view then choosing to
3238               // fill the wizard anyway) - return to that listing instead
3239               // of the generic search results. Otherwise, unchanged:
3240               // send them straight to matching listings, same redirect
3241               // the edit-requirement page uses.
3242               var pendingAfterSave = getPendingAction();
3243               if (pendingAfterSave) {
3244                  clearPendingAction();
3245                  window.location.href = pendingAfterSave.return_url;
3246               } else {
3247                  window.location.href = response.redirect_url || window.location.href;
3248               }
3249            } else {
3250               button.prop('disabled', false).text('Continue');
3251               showToast(response.message || 'Something went wrong. Please try again.');
3252            }
3253         },
3254         error: function () {
3255            button.prop('disabled', false).text('Continue');
3256            showToast('Something went wrong. Please try again.');
3257         }
3258      });
3259   }
3260   document.addEventListener('DOMContentLoaded', function () {
3261      // Re-fire whatever gated action (Contact Now, Shortlist, ...) got
3262      // interrupted by the auth wall, now that we're back and logged in.
3263      resumePendingAction();
3264
3265      // OTP auto-advance/back, matching the real site's .otp-input handling.
3266      var otpInputs = document.querySelectorAll('.otp-input');
3267      otpInputs.forEach(function (input, index) {
3268         input.addEventListener('input', function (e) {
3269            // .ob-filled is purely visual (see onboarding-v2.css) - doesn't
3270            // change any of the existing auto-advance/validation logic.
3271            e.target.classList.toggle('ob-filled', e.target.value.length === 1);
3272            if (e.target.value.length === 1 && index < otpInputs.length - 1) otpInputs[index + 1].focus();
3273         });
3274         input.addEventListener('keydown', function (e) {
3275            if (e.key === 'Backspace' && e.target.value === '' && index > 0) otpInputs[index - 1].focus();
3276         });
3277      });
3278      $('.otp-input').on('keydown', function (e) {
3279         var index = $('.otp-input').index($(this));
3280         if (index == 3) {
3281            var continueBtn = document.querySelector('.otp-verify-btn');
3282            if (continueBtn) { continueBtn.style.backgroundColor = 'rgba(255,84,3,1)'; continueBtn.style.color = '#fff'; continueBtn.disabled = false; }
3283         }
3284      });
3285
3286      $(".name_continue_btn").on("click", function () {
3287         var fullName = $("#register_full_name").val().trim();
3288         if (!fullName) { $(".login-error-name-message").show(); return; }
3289         $(".login-error-name-message").hide();
3290         register_full_name = fullName;
3291         $("#nameModalOverlay").removeClass("show");
3292
3293         // Registering because of a gated tap on a specific listing
3294         // (Contact Now, Shortlist, ...) - Tenant and Property Buyer both
3295         // post the same user_type ('user'; only Owner/Broker differ, see
3296         // login-modal.blade.php), so there's nothing the purpose/
3297         // account-type screens would tell us that the entry point hasn't
3298         // already answered. Skip those straight to registering. Generic
3299         // entry (no pending action, e.g. the header account icon) keeps
3300         // asking both, unchanged - that's the one case with nothing to
3301         // infer from.
3302         //
3303         // tenant_type is different: unlike location/type/category/budget,
3304         // it's genuinely not inferable from the listing (most properties
3305         // are tagged "All" or a multi-audience combo, not a single clean
3306         // value - see LR_TENANT_TYPE_MAP's comment) - so it's asked here
3307         // too, reusing the same #tenantTypeModalOverlay the full wizard
3308         // uses, before resuming the original action.
3309         var pendingAfterName = getPendingAction();
3310         if (pendingAfterName) {
3311            userType = 'user';
3311 user_type = 'user';
3312            $("#tenantTypeModalOverlay").addClass("show");
3313            return;
3314         }
3315
3316         $("#purposeModalOverlay").addClass("show");
3317      });
3318
3319      // Purpose now resolves user_type directly (search -> user, owner ->
3320      // owner, broker -> broker) instead of leading into a separate
3321      // account-type screen - Tenant/Property Buyer already collapsed to
3322      // the same 'user' value there, so that second screen never told us
3323      // anything the three purpose options don't already say. Registers
3324      // immediately on continue, same as the account-type screen used to.
3325      // #accountTypeModalOverlay and its handlers are left in place but
3326      // unreferenced - dead code, removed in a later cleanup pass once
3327      // this is confirmed stable in production.
3328      $("input[name='userPurpose']").on("change", function () { userPurpose = $(this).val(); });
3329      $(".purpose_continue_btn").on("click", function () {
3330         if (!userPurpose) { toastr.success("Please select an option"); return; }
3331         userType = userPurpose === 'search' ? 'user' : userPurpose;
3332         user_type = userType;
3333         var btn = $(this);
3334         btn.text('Processing...').prop('disabled', true).css({ 'background-color': '#b0b0b0', 'cursor': 'not-allowed' });
3335         $("#purposeModalOverlay").removeClass("show");
3336         registerRequest(user_type, user_image, register_full_name, register_phone_number, register_email, register_password, social_type, social_id);
3337      });
3338
3339      $("input[name='accountType']").on("change", function () {
3340         userType = $(this).val();
3341         user_type = $(this).val();
3342      });
3343      $(".account_type_continue_btn").on("click", function () {
3344         if (!userType) { toastr.success("Please select user type"); return; }
3345         var btn = $(this);
3346         btn.text('Processing...').prop('disabled', true).css({ 'background-color': '#b0b0b0', 'cursor': 'not-allowed' });
3347         registerRequest(user_type, user_image, register_full_name, register_phone_number, register_email, register_password, social_type, social_id);
3348      });
3349
3350      $("input[name='property_type']").on("change", function () {
3351         userPropertySearchData.selectedUserPropertyTypeId = $(this).val();
3352      });
3353      $(".property_type_continue_btn").on("click", function () {
3354         var selectedUserPropertyTypeId = $("input[name='property_type']:checked").val();
3355         if (!selectedUserPropertyTypeId) { showToast("Please select property type"); return; }
3356         userPropertySearchData.selectedUserPropertyTypeId = selectedUserPropertyTypeId;
3357         var propertyIdToUse = selectedUserPropertyTypeId;
3358         if (selectedUserPropertyTypeId == 2) { propertyIdToUse = 1; }
3359         var selectedProperty = authPropertyTypes.find(function (property) { return property.id == propertyIdToUse; });
3360         var categoryHtml = '';
3361         if (selectedProperty && selectedProperty.sub_property_type && selectedProperty.sub_property_type.length) {
3362            selectedProperty.sub_property_type.forEach(function (category) {
3363               categoryHtml += '<div class="property-item"><input type="radio" id="category_' + category.id + '" name="sub_category_type" value="' + category.id + '"><label for="category_' + category.id + '">' + category.name + '</label></div>';
3364            });
3365         }
3366         $("#categoryOptions").html(categoryHtml);
3367         $("#propertyTypeModalOverlay").removeClass("show");
3368         $("#categoryModalOverlay").addClass("show");
3369      });
3370
3371      $(document).on("change", "input[name='sub_category_type']", function () {
3372         userPropertySearchData.selectedUserCategoryId = $(this).val();
3373      });
3374      $(".category_continue_btn").on("click", function () {
3375         var selectedUserCategoryId = $("input[name='sub_category_type']:checked").val();
3376         if (!selectedUserCategoryId) { showToast("Please select category"); return; }
3377         userPropertySearchData.selectedUserCategoryId = selectedUserCategoryId;
3378         $("#categoryModalOverlay").removeClass("show");
3379         $("#tenantTypeModalOverlay").addClass("show");
3380      });
3381
3382      $(document).on("change", "input[name='tenant_type']", function () {
3383         userPropertySearchData.selectedUserTenantType = $(this).val();
3384      });
3385      // Mirrors CommonLibrary::$categoryBudgetFloors server-side — kept
3386      // here too so an unrealistic budget bucket (e.g. "Below 8K" for a
3387      // 3BHK) is hidden before the tenant ever picks it, rather than only
3388      // rejected after. The server-side check is still the enforced one;
3389      // this is just the UX layer.
3390      var categoryBudgetFloors = {
3391         '1BHK': 6000, '2BHK': 9000, '3BHK': 13000, '4BHK': 20000, '4+ BHK': 18000,
3392         'Annexy': 8000, 'Studio Apartment': 6000,
3393         '1 Room with Kitchen': 2500, '2 Room with Kitchen': 5000, '3 Room with Kitchen': 8000,
3394         'Single Room': 2500, 'Two Sharing': 2000, 'Three Sharing': 2000, 'Four Sharing': 2500, 'Five Sharing': 2500
3395      };
3396
3397      function applyCategoryBudgetFloor(categoryId) {
3398         var floor = 0;
3399         (authPropertyTypes || []).forEach(function (property) {
3400            (property.sub_property_type || []).forEach(function (category) {
3401               if (String(category.id) === String(categoryId) && categoryBudgetFloors.hasOwnProperty(category.name)) {
3402                  floor = categoryBudgetFloors[category.name];
3403               }
3404            });
3405         });
3406         $("input[name='budget_estimate']").each(function () {
3407            var option = $(this).closest('.property-item');
3408            if (Number($(this).val()) < floor) {
3409               $(this).prop('checked', false);
3410               option.hide();
3411            } else {
3412               option.show();
3413            }
3414         });
3415      }
3416
3417      $(".tenant_type_continue_btn").on("click", function () {
3418         var selectedTenantType = $("input[name='tenant_type']:checked").val();
3419         if (!selectedTenantType) { showToast("Please select tenant type"); return; }
3420
3421         // Reached from the property-triggered quick path (see
3422         // .name_continue_btn above) rather than the full generic-entry
3423         // wizard - store the tenant's own answer for
3424         // silentSaveRequirementFromProperty() to use (it takes priority
3425         // there over guessing from the listing's own tenant tag), then go
3426         // straight to registering instead of the property-type/category/
3427         // budget screens, which came from the listing itself already.
3428         var pendingBeforeRegister = getPendingAction();
3429         if (pendingBeforeRegister) {
3430            try { sessionStorage.setItem('lr_chosen_tenant_type', selectedTenantType); } catch (e) { /* noop */ }
3431            $("#tenantTypeModalOverlay").removeClass("show");
3432            var tenantBtn = $(this);
3433            tenantBtn.text('Processing...').prop('disabled', true);
3434            registerRequest(user_type, user_image, register_full_name, register_phone_number, register_email, register_password, social_type, social_id);
3435            return;
3436         }
3437
3438         userPropertySearchData.selectedTenantType = selectedTenantType;
3439         applyCategoryBudgetFloor(userPropertySearchData.selectedUserCategoryId);
3440         $("#tenantTypeModalOverlay").removeClass("show");
3441         $("#budgetEstimateModalOverlay").addClass("show");
3442      });
3443
3444      $(".budget_estimate_continue_btn").on("click", function () {
3445         var selectedBudgetEstimate = $("input[name='budget_estimate']:checked").val();
3446         if (!selectedBudgetEstimate) { showToast("Please select your budget"); return; }
3447         userPropertySearchData.selectedUserBudget = selectedBudgetEstimate;
3448         var button = $(this);
3449         button.prop("disabled", true).text("Please wait...");
3450         saveRequirements(button);
3451      });
3452   });
3453
3454   document.addEventListener('DOMContentLoaded', function () {
3455      // Profile dropdown (desktop + mobile triggers share the same markup/behavior)
3456      document.querySelectorAll('[data-hp-profile-trigger]').forEach(function (btn) {
3457         var menu = btn.closest('.hp-profile-trigger').querySelector('.hp-profile-menu');
3458         btn.addEventListener('click', function (e) {
3459            e.stopPropagation();
3460            var wasOpen = menu.classList.contains('open');
3461            document.querySelectorAll('.hp-profile-menu.open').forEach(function (m) { m.classList.remove('open'); });
3462            if (!wasOpen) menu.classList.add('open');
3463         });
3464      });
3465      document.addEventListener('click', function () {
3466         document.querySelectorAll('.hp-profile-menu.open').forEach(function (m) { m.classList.remove('open'); });
3467      });
3468
3469      // Real logout - same PUT /auth/logout + login_token as the live site's
3470      // logoutRequest() in template.blade.php. Asks for confirmation in the
3471      // shared #hpLogoutOverlay modal (header.blade.php) first, then shows a
3472      // spinner on the confirm button while the request is in flight.
3473      var hpLogoutOverlay = document.getElementById('hpLogoutOverlay');
3474      var hpLogoutCancel = document.getElementById('hpLogoutCancel');
3475      var hpLogoutConfirm = document.getElementById('hpLogoutConfirm');
3476
3477      function openHpLogoutModal() { hpLogoutOverlay.classList.add('show'); hpLogoutOverlay.setAttribute('aria-hidden', 'false'); }
3478      function closeHpLogoutModal() { hpLogoutOverlay.classList.remove('show'); hpLogoutOverlay.setAttribute('aria-hidden', 'true'); }
3479
3480      function hpLogout(e) {
3481         e.preventDefault();
3482         openHpLogoutModal();
3483      }
3484
3485      if (hpLogoutOverlay) {
3486         hpLogoutCancel.addEventListener('click', closeHpLogoutModal);
3487         hpLogoutOverlay.addEventListener('click', function (e) { if (e.target === hpLogoutOverlay) closeHpLogoutModal(); });
3488         document.addEventListener('keydown', function (e) { if (e.key === 'Escape' && hpLogoutOverlay.classList.contains('show')) closeHpLogoutModal(); });
3489
3490         hpLogoutConfirm.addEventListener('click', function () {
3491            hpLogoutConfirm.disabled = true;
3492            hpLogoutConfirm.innerHTML = 'Logging out...';
3493            $.ajax({
3494               url: 'https://www.letsrentz.com/auth/logout',
3495               method: 'PUT',
3496               data: { login_token: localStorage.getItem('login_token'), _token: 'bvRQeC7zDEZMxhJdt8x263POgOGJvjDEH4HLTaIw' },
3497               success: function () {
3498                  localStorage.removeItem('login_token');
3499                  clearPendingAction();
3500                  window.location.reload();
3501               },
3502               error: function () { window.location.reload(); }
3503            });
3504         });
3505      }
3506
3507      var hpLogoutDesktop = document.getElementById('hpLogoutBtnDesktop');
3508      var hpLogoutMobile = document.getElementById('hpLogoutBtnMobile');
3509      if (hpLogoutDesktop) hpLogoutDesktop.addEventListener('click', hpLogout);
3510      if (hpLogoutMobile) hpLogoutMobile.addEventListener('click', hpLogout);
3511
3512      // Bottom-nav "Account" tab (footer.blade.php) - opens the same
3513      // account menu as the header's profile avatar, as a bottom sheet.
3514      var hpAccountSheetOverlay = document.getElementById('hpAccountSheetOverlay');
3515      var hpAccountSheetClose = document.getElementById('hpAccountSheetClose');
3516      function openHpAccountSheet(e) {
3517         if (e) e.preventDefault();
3518         if (hpAccountSheetOverlay) { hpAccountSheetOverlay.classList.add('show'); hpLockBodyScroll(); }
3519      }
3520      function closeHpAccountSheet(e) {
3521         if (e) e.stopPropagation();
3522         if (hpAccountSheetOverlay) { hpAccountSheetOverlay.classList.remove('show'); hpUnlockBodyScroll(); }
3523      }
3524      document.querySelectorAll('[data-hp-account-sheet-trigger]').forEach(function (btn) {
3525         btn.addEventListener('click', openHpAccountSheet);
3526      });
3527      if (hpAccountSheetOverlay) {
3528         if (hpAccountSheetClose) hpAccountSheetClose.addEventListener('click', closeHpAccountSheet);
3529         hpAccountSheetOverlay.addEventListener('click', function (e) { if (e.target === hpAccountSheetOverlay) closeHpAccountSheet(e); });
3530         document.addEventListener('keydown', function (e) { if (e.key === 'Escape' && hpAccountSheetOverlay.classList.contains('show')) closeHpAccountSheet(e); });
3531      }
3532      var hpLogoutBottomNav = document.getElementById('hpLogoutBtnBottomNav');
3533      if (hpLogoutBottomNav) {
3534         hpLogoutBottomNav.addEventListener('click', function (e) {
3535            closeHpAccountSheet();
3536            hpLogout(e);
3537         });
3538      }
3539
3540      // Header notification bell (header.blade.php) - persistent way back
3541      // in to enable web push once the soft-ask banner above has been
3542      // snoozed or missed. Reflects Notification.permission as its state
3543      // and, when blocked, points at the browser's own site settings
3544      // (there's no JS API to re-prompt once denied).
3545      // Instructions differ by browser (icon, menu labels) since there's
3546      // no API to open site settings for the user - the best we can do is
3547      // point at the right spot. Order matters: Edge/Samsung/Opera UAs
3548      // also contain "Chrome", so those checks must come first.
3549      function hpNotifHelpText() {
3550         var ua = navigator.userAgent || '';
3551         var isAndroid = /Android/.test(ua);
3552         var isMac = /Macintosh/.test(ua) && !isAndroid;
3553         if (/EdgA?\//.test(ua)) {
3554            return isAndroid
3555               ? 'You\'ve blocked notifications for Lets Rentz in Edge. Tap the <b>lock icon</b> next to the web address, open <b>Permissions</b>, then turn <b>Notifications</b> on. Reload this page afterward.'
3556               : 'You\'ve blocked notifications for Lets Rentz in Edge. Click the <b>lock icon</b> in the address bar, choose <b>Permissions for this site</b>, then set <b>Notifications</b> to <b>Allow</b>. Reload this page afterward.';
3557         }
3558         if (/SamsungBrowser/.test(ua)) {
3559            return 'You\'ve blocked notifications for Lets Rentz. Tap the <b>lock icon</b> next to the web address, open <b>Site settings → Notifications</b>, and turn it on. Reload this page afterward.';
3560         }
3561         if (/Firefox/.test(ua)) {
3562            return 'You\'ve blocked notifications for Lets Rentz in Firefox. Click the <b>lock icon</b> in the address bar, open <b>Permissions</b>, and set <b>Send Notifications</b> to <b>Allow</b>. Reload this page afterward.';
3563         }
3564         if (/Chrome/.test(ua)) {
3565            return isAndroid
3566               ? 'You\'ve blocked notifications for Lets Rentz in Chrome. Tap the <b>lock</b> or <b>ⓘ icon</b> just left of the web address, tap <b>Permissions → Notifications</b>, and switch it to <b>Allow</b>. Reload this page afterward.'
3567               : 'You\'ve blocked notifications for Lets Rentz in Chrome. Click the <b>lock icon</b> in the address bar, choose <b>Site settings</b>, then set <b>Notifications</b> to <b>Allow</b>. Reload this page afterward.';
3568         }
3569         if (/Safari/.test(ua) && isMac) {
3570            return 'You\'ve blocked notifications for Lets Rentz in Safari. Open the <b>Safari</b> menu → <b>Settings</b> → <b>Websites</b> → <b>Notifications</b>, find this site in the list, and set it to <b>Allow</b>.';
3571         }
3572         return 'You\'ve blocked notifications for Lets Rentz in this browser. To turn them back on, open the site settings from your address bar (click the icon next to the URL), allow notifications, then reload the page.';
3573      }
3574
3575      var hpNotifHelpOverlay = document.getElementById('hpNotifHelpOverlay');
3576      var hpNotifHelpClose = document.getElementById('hpNotifHelpClose');
3577      var hpNotifHelpBody = document.getElementById('hpNotifHelpBody');
3578      if (hpNotifHelpOverlay && hpNotifHelpClose) {
3579         hpNotifHelpClose.addEventListener('click', function () {
3580            hpNotifHelpOverlay.classList.remove('show');
3581            hpNotifHelpOverlay.setAttribute('aria-hidden', 'true');
3582         });
3583         hpNotifHelpOverlay.addEventListener('click', function (e) {
3584            if (e.target === hpNotifHelpOverlay) {
3585               hpNotifHelpOverlay.classList.remove('show');
3586               hpNotifHelpOverlay.setAttribute('aria-hidden', 'true');
3587            }
3588         });
3589      }
3590
3591      function hpInitNotifBell(btn) {
3592         if (!btn) { return; }
3593         if (!('Notification' in window)) { btn.style.display = 'none'; return; }
3594
3595         function applyState() {
3596            var perm = Notification.permission;
3597            btn.classList.remove('is-on', 'is-denied');
3598            var icon = btn.querySelector('i');
3599            if (perm === 'granted') {
3600               btn.classList.add('is-on');
3601               btn.title = 'Notifications are on';
3602               if (icon) icon.className = 'bi bi-bell-fill';
3603            } else if (perm === 'denied') {
3604               btn.classList.add('is-denied');
3605               btn.title = 'Notifications blocked - click for help';
3606               if (icon) icon.className = 'bi bi-bell-slash';
3607            } else {
3608               btn.title = 'Enable notifications';
3609               if (icon) icon.className = 'bi bi-bell';
3610            }
3611         }
3612
3613         applyState();
3614         btn.addEventListener('click', function () {
3615            var perm = Notification.permission;
3616            if (perm === 'granted') { return; }
3617            if (perm === 'denied') {
3618               if (hpNotifHelpOverlay) {
3619                  if (hpNotifHelpBody) { hpNotifHelpBody.innerHTML = hpNotifHelpText(); }
3620                  hpNotifHelpOverlay.classList.add('show');
3621                  hpNotifHelpOverlay.setAttribute('aria-hidden', 'false');
3622               }
3623               return;
3624            }
3625            if (typeof window.lrRequestPushPermission === 'function') {
3626               window.lrRequestPushPermission().then(applyState);
3627            }
3628         });
3629      }
3630      hpInitNotifBell(document.getElementById('hpNotifBellDesktop'));
3631      hpInitNotifBell(document.getElementById('hpNotifBellMobile'));
3632
3633      document.querySelectorAll('[data-hp-login-trigger]').forEach(function (btn) {
3634         btn.addEventListener('click', function (e) { openLoginPopup(e); });
3635      });
3636
3637      // Any logged-in tenant with no plan_requirements row yet (registered
3638      // and left mid-flow, or an old account that skipped it entirely, or
3639      // just logged in rather than registered) gets the requirement wizard
3640      // resumed here. loginRequest() reloads the page on success, so this
3641      // also covers "just logged in" without needing its own trigger.
3642      // Skipped when a pending action just resumed on this same load
3643      // (registerRequest()'s success handler redirected here specifically
3644      // to finish that, not to fill this wizard). By the time this runs,
3645      // resumePendingAction() (wired into the earlier DOMContentLoaded
3646      // listener above, which fires first) has already cleared the
3647      // pending-action marker itself - lr_show_match_more is the one that
3648      // survives to here (timestamped, not consumed until the resumed
3649      // click's own success handler reads it), so this check has
3650      // something to read. Bounded to LR_SHOW_MATCH_MORE_TTL_MS so a
3651      // failed/slow resume can't suppress this nudge indefinitely - it's
3652      // still the fallback safety net for every other "needs a
3653      // requirement" case, this is only a few seconds' grace to let the
3654      // resume self-heal via silentSaveRequirementFromProperty() first.
3655      
3656      lpInitVideoCards();
3657   });
3658
3659   // Listing-card video previews (99acres-style): the cover image stays as
3660   // the poster until a card scrolls into view, at which point a muted,
3661   // no-controls YouTube embed is created and capped to a short ~10s loop
3662   // via end=10&loop=1&playlist=<id> (YouTube's loop param only works
3663   // combined with playlist=<own id> for a single video). Global (not
3664   // inside the DOMContentLoaded closure above) so listing.blade.php can
3665   // call lpInitVideoCards() again after it appends more cards via AJAX -
3666   // the :not([data-video-observed]) guard means already-wired cards are
3667   // skipped on repeat calls.
3668   var lpVideoObserver = null;
3669   var LP_DEFAULT_VIDEO_ASPECT_RATIO = 16 / 9; // used until the real ratio loads
3670   var lpVideoAspectRatioCache = {}; // videoId -> ratio, so repeat cards/resizes skip the fetch
3671
3672   // Property tour videos aren't all landscape 16:9 - some are vertical
3673   // Shorts/reel-style uploads, which YouTube pillarboxes hard inside a
3674   // landscape card. YouTube's own oEmbed endpoint can't tell us which:
3675   // it returns a fixed ~16:9 size for a /watch URL and a fixed ~9:16
3676   // size for a /shorts URL regardless of the actual video, so it's
3677   // useless for detection. Our own /getVideoAspectRatio endpoint reads
3678   // the real encoded stream dimensions server-side instead (cached
3679   // after the first look-up) so the cover math below is correct for
3680   // either orientation.
3681   function lpFetchVideoAspectRatio(videoId, callback) {
3682      if (lpVideoAspectRatioCache[videoId]) {
3683         callback(lpVideoAspectRatioCache[videoId]);
3684         return;
3685      }
3686      fetch('https://www.letsrentz.com/getVideoAspectRatio/' + videoId)
3687         .then(function (res) { return res.ok ? res.json() : null; })
3688         .then(function (data) {
3689            var ratio = (data && data.status && data.ratio) ? data.ratio : LP_DEFAULT_VIDEO_ASPECT_RATIO;
3690            lpVideoAspectRatioCache[videoId] = ratio;
3691            callback(ratio);
3692         })
3693         .catch(function () {
3694            lpVideoAspectRatioCache[videoId] = LP_DEFAULT_VIDEO_ASPECT_RATIO;
3695            callback(LP_DEFAULT_VIDEO_ASPECT_RATIO);
3696         });
3697   }
3698
3699   // iframes can't use object-fit:cover (it only applies to replaced
3700   // elements like img/video), and YouTube's own embed letterboxes/
3701   // pillarboxes the video to fit whatever box the iframe is - which is
3702   // why a card box that isn't the video's own aspect ratio was showing
3703   // black bars. This is the standard "cover" trick instead: size the
3704   // iframe bigger than the visible box (by container-vs-video aspect
3705   // ratio) and center it, so the letterboxed edges land outside
3706   // .lp-card-video's overflow:hidden bounds and only the filled video
3707   // is visible - same idea whether the source is landscape or a
3708   // vertical reel.
3709   function lpSizeVideoIframe(el, iframe, videoRatio) {
3710      var w = el.offsetWidth;
3711      var h = el.offsetHeight;
3712      if (!w || !h) return;
3713      var ratio = videoRatio || LP_DEFAULT_VIDEO_ASPECT_RATIO;
3714      var containerRatio = w / h;
3715      var videoW, videoH;
3716      if (containerRatio > ratio) {
3717         videoW = w;
3718         videoH = w / ratio;
3719      } else {
3720         videoH = h;
3721         videoW = h * ratio;
3722      }
3723      iframe.style.width = videoW + 'px';
3724      iframe.style.height = videoH + 'px';
3725      iframe.style.left = ((w - videoW) / 2) + 'px';
3726      iframe.style.top = ((h - videoH) / 2) + 'px';
3727   }
3728
3729   function lpGetVideoObserver() {
3730      if (lpVideoObserver) return lpVideoObserver;
3731      lpVideoObserver = new IntersectionObserver(function (entries) {
3732         entries.forEach(function (entry) {
3733            var el = entry.target;
3734            var iframe = el.querySelector('iframe');
3735            if (entry.isIntersecting) {
3736               if (!iframe) {
3737                  var videoId = el.getAttribute('data-video-id');
3738                  iframe = document.createElement('iframe');
3739                  iframe.src = 'https://www.youtube.com/embed/' + videoId
3740                     + '?autoplay=1&mute=1&controls=0&rel=0&playsinline=1&enablejsapi=1&loop=1&end=10&playlist=' + videoId;
3741                  iframe.setAttribute('allow', 'autoplay; encrypted-media');
3742                  iframe.setAttribute('frameborder', '0');
3743                  el.insertBefore(iframe, el.firstChild);
3744                  lpSizeVideoIframe(el, iframe, lpVideoAspectRatioCache[videoId]);
3745                  el.classList.add('is-playing');
3746                  lpFetchVideoAspectRatio(videoId, function (ratio) {
3747                     if (document.body.contains(iframe)) {
3748                        lpSizeVideoIframe(el, iframe, ratio);
3749                     }
3750                  });
3751               } else {
3752                  iframe.contentWindow.postMessage(JSON.stringify({ event: 'command', func: 'playVideo', args: [] }), '*');
3753               }
3754            } else if (iframe) {
3755               iframe.contentWindow.postMessage(JSON.stringify({ event: 'command', func: 'pauseVideo', args: [] }), '*');
3756            }
3757         });
3758      }, { threshold: 0.5 });
3759      return lpVideoObserver;
3760   }
3761
3762   function lpInitVideoCards(root) {
3763      var scope = root || document;
3764      var observer = lpGetVideoObserver();
3765      scope.querySelectorAll('.lp-card-video:not([data-video-observed])').forEach(function (el) {
3766         el.setAttribute('data-video-observed', '1');
3767         observer.observe(el);
3768
3769         var muteBtn = el.querySelector('.lp-card-video-mute');
3770         if (!muteBtn) return;
3771         muteBtn.addEventListener('click', function (e) {
3772            e.preventDefault();
3773            e.stopPropagation();
3774            var iframe = el.querySelector('iframe');
3775            if (!iframe) return;
3776            var icon = muteBtn.querySelector('i');
3777            var isMuted = icon.classList.contains('bi-volume-mute-fill');
3778            iframe.contentWindow.postMessage(JSON.stringify({ event: 'command', func: isMuted ? 'unMute' : 'mute', args: [] }), '*');
3779            icon.classList.toggle('bi-volume-mute-fill', !isMuted);
3780            icon.classList.toggle('bi-volume-up-fill', isMuted);
3781         });
3782      });
3783   }
3784
3785   // Card widths change at layout breakpoints (grid columns, sidebar
3786   // collapsing) - re-run the cover sizing for any videos already
3787   // playing so they don't end up letterboxed after a resize.
3788   var lpVideoResizeTimer = null;
3789   window.addEventListener('resize', function () {
3790      clearTimeout(lpVideoResizeTimer);
3791      lpVideoResizeTimer = setTimeout(function () {
3792         document.querySelectorAll('.lp-card-video.is-playing iframe').forEach(function (iframe) {
3793            var el = iframe.closest('.lp-card-video');
3794            lpSizeVideoIframe(el, iframe, lpVideoAspectRatioCache[el.getAttribute('data-video-id')]);
3795         });
3796      }, 200);
3797   });
3798</script>
3798
3799
3800<script src="https://www.letsrentz.com/front/assets/js/loginv2.js?v=1.1"></script>
vendor: 1 bytes, line 3800
3800
3801<script>window.LR_LOGIN_V2_WEB_ENABLED = true;</script>
vendor: 1 bytes, line 3801
3801
3802<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:'a441bb85dc82f939',t:'MTc5MDkyMzQwMw=='};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>
3802<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":"36f75870209544a590e47eba55e8bc11","r":1,"spa":2}' crossorigin="anonymous"></script>
3802
3803</body>
3804</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.