{"id":25986166,"date":"2025-04-21T12:08:05","date_gmt":"2025-04-21T10:08:05","guid":{"rendered":"https:\/\/bostello.com\/?page_id=25980457"},"modified":"2025-10-31T15:36:12","modified_gmt":"2025-10-31T15:36:12","slug":"about","status":"publish","type":"page","link":"https:\/\/bostello.com\/pl\/about\/","title":{"rendered":"property"},"content":{"rendered":"<p>[et_pb_section fb_built=&#8221;1&#8243; _builder_version=&#8221;4.27.4&#8243; _module_preset=&#8221;default&#8221; custom_margin=&#8221;-80px||||false|false&#8221; global_colors_info=&#8221;{}&#8221; theme_builder_area=&#8221;post_content&#8221;][et_pb_row _builder_version=&#8221;4.27.4&#8243; _module_preset=&#8221;default&#8221; width=&#8221;100%&#8221; max_width=&#8221;100%&#8221; global_colors_info=&#8221;{}&#8221; theme_builder_area=&#8221;post_content&#8221;][et_pb_column type=&#8221;4_4&#8243; _builder_version=&#8221;4.27.4&#8243; _module_preset=&#8221;default&#8221; global_colors_info=&#8221;{}&#8221; theme_builder_area=&#8221;post_content&#8221;][et_pb_code _builder_version=&#8221;4.27.4&#8243; _module_preset=&#8221;default&#8221; global_colors_info=&#8221;{}&#8221; theme_builder_area=&#8221;post_content&#8221;]<!-- Anchor element -->\r\n<div id=\"vue-widget-anchor\" data-usercentrics=\"essential\"><\/div>\r\n\r\n<!-- Vue App Loader for chunked Vite build -->\r\n<script type=\"module\" data-usercentrics=\"essential\">\r\n  (async () => {\r\n    const anchor = document.getElementById('vue-widget-anchor');\r\n    if (!anchor) {\r\n      console.error('Vue widget: anchor missing');\r\n      return;\r\n    }\r\n\r\n      \/\/ 0) Ensure #app exists and render a detailed first-paint skeleton immediately\r\n      let appEl = document.getElementById('app');\r\n      if (!appEl) {\r\n        appEl = document.createElement('div');\r\n        appEl.id = 'app';\r\n        appEl.setAttribute('data-usercentrics', 'essential');\r\n        appEl.style.position = 'relative';\r\n        appEl.style.zIndex = '1';\r\n        appEl.style.minHeight = '100vh'; \/\/ Prevent layout shift\r\n        appEl.style.backgroundColor = '#ffffff'; \/\/ Prevent white flash\r\n        anchor.appendChild(appEl);\r\n      } else {\r\n        \/\/ Ensure existing app has background\r\n        appEl.style.backgroundColor = '#ffffff';\r\n      }\r\n\r\n    \/\/ Create detailed skeleton matching ContentLoadingSkeleton structure\r\n    const sk = document.createElement('div');\r\n    sk.id = 'app-skeleton';\r\n    sk.setAttribute('data-usercentrics', 'essential');\r\n    sk.innerHTML = `\r\n      <div class=\"pre-skel container-search-page\">\r\n        <!-- Search bar skeleton -->\r\n        <div class=\"pre-skel__search-bar\">\r\n          <div class=\"pre-skel__search-row\">\r\n            <div class=\"pre-skel__searchbox\"><\/div>\r\n            <div class=\"pre-skel__chip\"><\/div>\r\n            <div class=\"pre-skel__chip\"><\/div>\r\n            <div class=\"pre-skel__chip\"><\/div>\r\n            <div class=\"pre-skel__chip\"><\/div>\r\n          <\/div>\r\n          <div class=\"pre-skel__tag-row\">\r\n            <div class=\"pre-skel__tag\"><\/div>\r\n            <div class=\"pre-skel__tag\"><\/div>\r\n            <div class=\"pre-skel__tag\"><\/div>\r\n          <\/div>\r\n        <\/div>\r\n        \r\n        <!-- Main content: list + map -->\r\n        <div class=\"pre-skel__main-content\">\r\n          <!-- Property list section -->\r\n          <div class=\"pre-skel__property-list-section\">\r\n            ${Array.from({ length: 6 }, () => `\r\n              <div class=\"pre-skel__card\">\r\n                <div class=\"pre-skel__card-image-container\">\r\n                  <div class=\"pre-skel__card-main-image\">\r\n                    <div class=\"pre-skel__card-price-tag\"><\/div>\r\n                  <\/div>\r\n                  <div class=\"pre-skel__card-thumbs\">\r\n                    <div class=\"pre-skel__card-thumb\"><\/div>\r\n                    <div class=\"pre-skel__card-thumb\"><\/div>\r\n                    <div class=\"pre-skel__card-thumb\"><\/div>\r\n                  <\/div>\r\n                <\/div>\r\n                <div class=\"pre-skel__card-content\">\r\n                  <div class=\"pre-skel__card-title\"><\/div>\r\n                  <div class=\"pre-skel__card-desc\"><\/div>\r\n                <\/div>\r\n                <div class=\"pre-skel__card-footer\">\r\n                  <div class=\"pre-skel__card-features\">\r\n                    <div class=\"pre-skel__card-feature\"><\/div>\r\n                    <div class=\"pre-skel__card-feature\"><\/div>\r\n                    <div class=\"pre-skel__card-feature\"><\/div>\r\n                  <\/div>\r\n                  <div class=\"pre-skel__card-location\"><\/div>\r\n                  <div class=\"pre-skel__card-logo\"><\/div>\r\n                <\/div>\r\n              <\/div>\r\n            `).join('')}\r\n          <\/div>\r\n          \r\n          <!-- Map section -->\r\n          <div class=\"pre-skel__map-section\">\r\n            <div class=\"pre-skel__map-skeleton\">\r\n              ${Array.from({ length: 5 }, () => `\r\n                <div class=\"pre-skel__map-row\">\r\n                  ${Array.from({ length: 8 }, () => `\r\n                    <div class=\"pre-skel__map-tile\"><\/div>\r\n                  `).join('')}\r\n                <\/div>\r\n              `).join('')}\r\n            <\/div>\r\n            <div class=\"pre-skel__map-footer\">\r\n              <div class=\"pre-skel__legend\"><\/div>\r\n              <div class=\"pre-skel__legend\"><\/div>\r\n            <\/div>\r\n          <\/div>\r\n        <\/div>\r\n      <\/div>\r\n    `;\r\n    appEl.appendChild(sk);\r\n\r\n    \/\/ Inline CSS so the skeleton shows without waiting for app CSS\r\n    const skStyle = document.createElement('style');\r\n    skStyle.setAttribute('data-usercentrics', 'essential');\r\n    skStyle.textContent = `\r\n      \/* Match ContentLoadingSkeleton container *\/\r\n      .pre-skel.container-search-page {\r\n        background-color: #ffffff;\r\n        display: flex;\r\n        flex-direction: column;\r\n        min-height: 100vh;\r\n        font-family: 'Poppins', sans-serif;\r\n        padding: 0;\r\n        margin: 0 auto;\r\n      }\r\n      \r\n      @media screen and (max-width: 1200px) {\r\n        .pre-skel.container-search-page {\r\n          padding: 0;\r\n        }\r\n      }\r\n      \r\n      \/* Search bar skeleton *\/\r\n      .pre-skel__search-bar {\r\n        position: sticky;\r\n        top: 0;\r\n        z-index: 1;\r\n        background-color: #ffffff;\r\n        padding: 0.75rem;\r\n        border-bottom: 1px solid #f5f3ef;\r\n      }\r\n      \r\n      .pre-skel__search-row {\r\n        display: grid;\r\n        grid-template-columns: 1fr repeat(4, max-content);\r\n        gap: 1rem;\r\n        align-items: center;\r\n      }\r\n      \r\n      .pre-skel__searchbox {\r\n        height: 40px;\r\n        border-radius: 10px;\r\n        background: linear-gradient(90deg, #e9e9e9 25%, #f3f3f3 37%, #e9e9e9 63%);\r\n        background-size: 400% 100%;\r\n        animation: pre-skel-shimmer 1.2s infinite linear;\r\n      }\r\n      \r\n      .pre-skel__chip {\r\n        width: 90px;\r\n        height: 36px;\r\n        border-radius: 10px;\r\n        background: linear-gradient(90deg, #e9e9e9 25%, #f3f3f3 37%, #e9e9e9 63%);\r\n        background-size: 400% 100%;\r\n        animation: pre-skel-shimmer 1.2s infinite linear;\r\n      }\r\n      \r\n      .pre-skel__tag-row {\r\n        display: flex;\r\n        gap: 0.5rem;\r\n        margin-top: 0.5rem;\r\n      }\r\n      \r\n      .pre-skel__tag {\r\n        width: 100px;\r\n        height: 26px;\r\n        border-radius: 8px;\r\n        background: linear-gradient(90deg, #e9e9e9 25%, #f3f3f3 37%, #e9e9e9 63%);\r\n        background-size: 400% 100%;\r\n        animation: pre-skel-shimmer 1.2s infinite linear;\r\n      }\r\n      \r\n      \/* Main content: list + map *\/\r\n      .pre-skel__main-content {\r\n        flex: 1;\r\n        display: flex;\r\n        gap: 0;\r\n        height: calc(100vh - 180px);\r\n        position: sticky;\r\n        padding: 0;\r\n        width: 100%;\r\n      }\r\n      \r\n      @media (max-width: 1200px) {\r\n        .pre-skel__main-content {\r\n          height: auto;\r\n          top: 140px;\r\n          padding: 0;\r\n        }\r\n      }\r\n      \r\n      \/* Property list section *\/\r\n      .pre-skel__property-list-section {\r\n        overflow-y: auto;\r\n        background-color: #ffffff;\r\n        border-radius: 8px;\r\n        height: 100%;\r\n        width: 50%;\r\n        overflow-x: hidden;\r\n        overflow-y: hidden;\r\n        min-width: 0;\r\n        padding: 0.5rem;\r\n        z-index: 1000;\r\n        box-shadow: 0 5px 10px -2px rgba(0, 0, 0, 0.1);\r\n        display: flex;\r\n        flex-direction: column;\r\n        align-items: center;\r\n        gap: 1rem;\r\n      }\r\n      \r\n      @media (max-width: 1200px) {\r\n        .pre-skel__property-list-section {\r\n          width: 100%;\r\n          padding: 1rem;\r\n        }\r\n      }\r\n      \r\n      .pre-skel__card {\r\n        background-color: white;\r\n        border-radius: 1rem;\r\n        box-shadow: rgba(0, 0, 0, 0.16) 0px 1px 4px;\r\n        overflow: hidden;\r\n        display: flex;\r\n        flex-direction: column;\r\n        min-width: 0;\r\n        width: 100%;\r\n        margin-bottom: 0.5rem;\r\n      }\r\n      \r\n      .pre-skel__card-image-container {\r\n        display: grid;\r\n        grid-template-columns: 1fr;\r\n        gap: 0.2rem;\r\n      }\r\n      \r\n      @media (min-width: 1400px) {\r\n        .pre-skel__card-image-container {\r\n          grid-template-columns: 3fr 1fr;\r\n        }\r\n      }\r\n      \r\n      .pre-skel__card-main-image {\r\n        position: relative;\r\n        height: 280px;\r\n        border-top-left-radius: 10px;\r\n        border-top-right-radius: 10px;\r\n        background: linear-gradient(90deg, #e9e9e9 25%, #f3f3f3 37%, #e9e9e9 63%);\r\n        background-size: 400% 100%;\r\n        animation: pre-skel-shimmer 1.2s infinite linear;\r\n        overflow: hidden;\r\n      }\r\n      \r\n      @media (min-width: 1400px) {\r\n        .pre-skel__card-main-image {\r\n          border-top-right-radius: 0;\r\n        }\r\n      }\r\n      \r\n      .pre-skel__card-price-tag {\r\n        position: absolute;\r\n        bottom: 8px;\r\n        left: 8px;\r\n        height: 28px;\r\n        width: 100px;\r\n        border-radius: 4px;\r\n        background: rgba(255, 255, 255, 0.9);\r\n      }\r\n      \r\n      .pre-skel__card-thumbs {\r\n        display: grid;\r\n        grid-template-columns: repeat(3, 1fr);\r\n        gap: 0.2rem;\r\n      }\r\n      \r\n      @media (min-width: 1400px) {\r\n        .pre-skel__card-thumbs {\r\n          grid-template-columns: 1fr;\r\n          grid-template-rows: repeat(3, 1fr);\r\n        }\r\n      }\r\n      \r\n      .pre-skel__card-thumb {\r\n        height: 90px;\r\n        border-radius: 4px;\r\n        background: linear-gradient(90deg, #e9e9e9 25%, #f3f3f3 37%, #e9e9e9 63%);\r\n        background-size: 400% 100%;\r\n        animation: pre-skel-shimmer 1.2s infinite linear;\r\n      }\r\n      \r\n      @media (min-width: 1400px) {\r\n        .pre-skel__card-thumb {\r\n          height: 100px;\r\n        }\r\n      }\r\n      \r\n      .pre-skel__card-content {\r\n        padding: 0.8rem;\r\n        display: grid;\r\n        gap: 0.5rem;\r\n      }\r\n      \r\n      .pre-skel__card-title {\r\n        height: 20px;\r\n        width: 70%;\r\n        border-radius: 6px;\r\n        background: linear-gradient(90deg, #e9e9e9 25%, #f3f3f3 37%, #e9e9e9 63%);\r\n        background-size: 400% 100%;\r\n        animation: pre-skel-shimmer 1.2s infinite linear;\r\n      }\r\n      \r\n      .pre-skel__card-desc {\r\n        height: 16px;\r\n        width: 90%;\r\n        border-radius: 6px;\r\n        background: linear-gradient(90deg, #e9e9e9 25%, #f3f3f3 37%, #e9e9e9 63%);\r\n        background-size: 400% 100%;\r\n        animation: pre-skel-shimmer 1.2s infinite linear;\r\n      }\r\n      \r\n      .pre-skel__card-footer {\r\n        display: grid;\r\n        grid-template-columns: 2fr 1fr;\r\n        gap: 1rem;\r\n        align-items: start;\r\n        padding: 0.4rem;\r\n      }\r\n      \r\n      .pre-skel__card-features {\r\n        display: grid;\r\n        grid-template-columns: 1fr;\r\n        gap: 0.6rem;\r\n        padding: 0.5rem;\r\n      }\r\n      \r\n      .pre-skel__card-feature {\r\n        height: 14px;\r\n        width: 40%;\r\n        border-radius: 6px;\r\n        background: linear-gradient(90deg, #e9e9e9 25%, #f3f3f3 37%, #e9e9e9 63%);\r\n        background-size: 400% 100%;\r\n        animation: pre-skel-shimmer 1.2s infinite linear;\r\n      }\r\n      \r\n      .pre-skel__card-location {\r\n        height: 14px;\r\n        width: 60%;\r\n        border-radius: 6px;\r\n        background: linear-gradient(90deg, #e9e9e9 25%, #f3f3f3 37%, #e9e9e9 63%);\r\n        background-size: 400% 100%;\r\n        animation: pre-skel-shimmer 1.2s infinite linear;\r\n        margin-top: 0.5rem;\r\n      }\r\n      \r\n      .pre-skel__card-logo {\r\n        width: 130px;\r\n        height: 100px;\r\n        border-radius: 8px;\r\n        background: linear-gradient(90deg, #e9e9e9 25%, #f3f3f3 37%, #e9e9e9 63%);\r\n        background-size: 400% 100%;\r\n        animation: pre-skel-shimmer 1.2s infinite linear;\r\n        justify-self: center;\r\n        align-self: center;\r\n      }\r\n      \r\n      \/* Map section *\/\r\n      .pre-skel__map-section {\r\n        position: sticky;\r\n        top: 80px;\r\n        height: calc(100vh);\r\n        background-color: #ffffff;\r\n        border-radius: 8px;\r\n        box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);\r\n        overflow: hidden;\r\n        width: 65%;\r\n        min-width: 400px;\r\n        display: flex;\r\n        flex-direction: column;\r\n        justify-content: center;\r\n        align-items: center;\r\n        padding: 0.5rem;\r\n      }\r\n      \r\n      @media (max-width: 1200px) {\r\n        .pre-skel__map-section {\r\n          display: none;\r\n        }\r\n      }\r\n      \r\n      \/* Map grid skeleton *\/\r\n      .pre-skel__map-skeleton {\r\n        width: 100%;\r\n        height: 100%;\r\n        display: grid;\r\n        grid-template-rows: repeat(5, 1fr);\r\n        gap: 6px;\r\n        border-radius: 8px;\r\n        overflow: hidden;\r\n      }\r\n      \r\n      .pre-skel__map-row {\r\n        display: grid;\r\n        grid-template-columns: repeat(8, 1fr);\r\n        gap: 6px;\r\n      }\r\n      \r\n      .pre-skel__map-tile {\r\n        border-radius: 6px;\r\n        background: #f0f2f5;\r\n        position: relative;\r\n        overflow: hidden;\r\n      }\r\n      \r\n      .pre-skel__map-tile::after {\r\n        content: '';\r\n        position: absolute;\r\n        inset: 0;\r\n        transform: translateX(-100%);\r\n        background: linear-gradient(\r\n          90deg,\r\n          rgba(255, 255, 255, 0) 0%,\r\n          rgba(255, 255, 255, 0.6) 50%,\r\n          rgba(255, 255, 255, 0) 100%\r\n        );\r\n        animation: pre-skel-map-shimmer 1.4s infinite;\r\n      }\r\n      \r\n      .pre-skel__map-footer {\r\n        width: 100%;\r\n        display: flex;\r\n        gap: 1rem;\r\n        margin-top: 0.5rem;\r\n        padding: 0 0.25rem;\r\n      }\r\n      \r\n      .pre-skel__legend {\r\n        height: 14px;\r\n        border-radius: 6px;\r\n        flex: 1;\r\n        background: linear-gradient(90deg, #e9e9e9 25%, #f3f3f3 37%, #e9e9e9 63%);\r\n        background-size: 400% 100%;\r\n        animation: pre-skel-shimmer 1.2s infinite linear;\r\n      }\r\n      \r\n      @keyframes pre-skel-shimmer {\r\n        0% {\r\n          background-position: 100% 0;\r\n        }\r\n        100% {\r\n          background-position: 0 0;\r\n        }\r\n      }\r\n      \r\n      @keyframes pre-skel-map-shimmer {\r\n        100% {\r\n          transform: translateX(100%);\r\n        }\r\n      }\r\n      \r\n      \/* Smooth transition styles *\/\r\n      .pre-skel {\r\n        transition: opacity 0.6s ease-out;\r\n        opacity: 1;\r\n        will-change: opacity;\r\n        position: relative;\r\n        z-index: 1;\r\n        background-color: #ffffff; \/* Ensure white background *\/\r\n      }\r\n      \r\n      @media (max-width: 1200px) {\r\n        .pre-skel {\r\n          transition: opacity 0.7s ease-out;\r\n        }\r\n      }\r\n      \r\n      .pre-skel.fade-out {\r\n        opacity: 0;\r\n        pointer-events: none;\r\n        position: absolute;\r\n        top: 0;\r\n        left: 0;\r\n        right: 0;\r\n        z-index: 10;\r\n        background-color: #ffffff !important; \/* Force white background during fade *\/\r\n        min-height: 100vh;\r\n        width: 100%;\r\n      }\r\n      \r\n      @media (max-width: 1200px) {\r\n        .pre-skel.fade-out {\r\n          min-height: 100vh;\r\n          min-height: -webkit-fill-available; \/* iOS fix *\/\r\n        }\r\n      }\r\n      \r\n      \/* Ensure skeleton container stays in place during transition *\/\r\n      #app {\r\n        position: relative;\r\n        min-height: 100vh;\r\n        background-color: #ffffff; \/* Prevent white flash *\/\r\n      }\r\n      \r\n      \/* Ensure Vue content has background during transition *\/\r\n      #app > *:not(#app-skeleton) {\r\n        background-color: #ffffff;\r\n      }\r\n    `;\r\n    document.head.appendChild(skStyle);\r\n\r\n    \/\/ Helper: Wait for CSS to load\r\n    const waitForCSS = (link) => {\r\n      return new Promise((resolve, reject) => {\r\n        if (link.sheet) {\r\n          resolve(); \/\/ Already loaded\r\n          return;\r\n        }\r\n        link.onload = () => resolve();\r\n        link.onerror = () => {\r\n          console.warn('CSS load warning (non-fatal):', link.href);\r\n          resolve(); \/\/ Resolve anyway to not block mounting\r\n        };\r\n        \/\/ Timeout after 10s (increased from 5s)\r\n        setTimeout(() => {\r\n          console.warn('CSS load timeout (non-fatal):', link.href);\r\n          resolve(); \/\/ Resolve anyway to not block mounting\r\n        }, 10000);\r\n      });\r\n    };\r\n\r\n    \/\/ Helper: Normalize file paths from manifest\r\n    \/\/ Manifest paths might be relative, absolute, or already include assets\/\r\n    const normalizePath = (filePath, base) => {\r\n      if (!filePath) return null;\r\n      \r\n      \/\/ If it's already an absolute URL, return as-is (but log a warning if it's the wrong domain)\r\n      if (filePath.startsWith('http:\/\/') || filePath.startsWith('https:\/\/')) {\r\n        \/\/ Check if it's pointing to wrong domain\r\n        const url = new URL(filePath);\r\n        const baseUrl = new URL(base);\r\n        if (url.origin !== baseUrl.origin) {\r\n          console.warn(`Manifest contains absolute URL from different domain: ${filePath}, expected: ${baseUrl.origin}`);\r\n          \/\/ Try to fix it by replacing the domain\r\n          return filePath.replace(url.origin, baseUrl.origin);\r\n        }\r\n        return filePath;\r\n      }\r\n      \r\n      \/\/ If path starts with \/, it's absolute from root - prepend base\r\n      if (filePath.startsWith('\/')) {\r\n        return base + filePath.slice(1);\r\n      }\r\n      \r\n      \/\/ If path already starts with assets\/, just prepend base\r\n      if (filePath.startsWith('assets\/')) {\r\n        return base + filePath;\r\n      }\r\n      \r\n      \/\/ Otherwise, assume it needs assets\/ prefix\r\n      return base + 'assets\/' + filePath;\r\n    };\r\n\r\n    \/\/ Helper: Wait for DOM to be stable\r\n    const waitForDOMReady = () => {\r\n      if (document.readyState === 'complete' || document.readyState === 'interactive') {\r\n        return Promise.resolve();\r\n      }\r\n      return new Promise(resolve => {\r\n        if (document.readyState === 'loading') {\r\n          document.addEventListener('DOMContentLoaded', resolve, { once: true });\r\n        } else {\r\n          resolve();\r\n        }\r\n      });\r\n    };\r\n\r\n    try {\r\n      \/\/ 1) Load manifest\r\n      \/\/ https:\/\/frontend.production.platform.bostello.com\/assets\/\r\n     \/\/ const base = 'https:\/\/bostello.com\/vue\/';\r\n      const base = 'https:\/\/frontend.production.platform.bostello.com\/';\r\n      const manifestRes = await fetch(base + 'assets\/manifest.json', { cache: 'no-store' });\r\n      if (!manifestRes.ok) {\r\n        throw new Error(`Failed to load manifest: ${manifestRes.status} ${manifestRes.statusText}`);\r\n      }\r\n      const manifest = await manifestRes.json();\r\n      console.log('Manifest loaded, base URL:', base);\r\n      console.log('Sample manifest entry:', Object.keys(manifest)[0], manifest[Object.keys(manifest)[0]]);\r\n\r\n      \/\/ Vite often uses 'index.html' or 'src\/main.ts' as the manifest entry key\r\n      const entry = manifest['index.html'] || manifest['src\/main.ts'];\r\n      if (!entry || !entry.file) {\r\n        throw new Error('Vue widget: no entry found in manifest');\r\n      }\r\n      console.log('Entry file from manifest:', entry.file);\r\n      console.log('Entry imports:', entry.imports?.length || 0);\r\n      console.log('Entry dynamicImports:', entry.dynamicImports?.length || 0);\r\n\r\n      \/\/ 2) Load entry CSS first and wait for it to load\r\n      const cssPromises = [];\r\n      if (entry.css && entry.css.length > 0) {\r\n        entry.css.forEach(cssFile => {\r\n          const link = document.createElement('link');\r\n          link.rel = 'stylesheet';\r\n          link.href = normalizePath(cssFile, base);\r\n          link.crossOrigin = 'anonymous';\r\n          link.setAttribute('data-usercentrics', 'essential');\r\n          document.head.appendChild(link);\r\n          cssPromises.push(waitForCSS(link));\r\n        });\r\n      }\r\n\r\n      \/\/ 3) Preload static imports (eager chunks)\r\n      if (entry.imports && entry.imports.length > 0) {\r\n        entry.imports.forEach(key => {\r\n          const chunk = manifest[key];\r\n          if (chunk?.file) {\r\n            const normalizedPath = normalizePath(chunk.file, base);\r\n            if (!normalizedPath) {\r\n              console.warn('Skipping chunk with invalid file path:', key);\r\n              return;\r\n            }\r\n            const l = document.createElement('link');\r\n            l.rel = 'modulepreload';\r\n            l.href = normalizedPath;\r\n            l.crossOrigin = 'anonymous';\r\n            l.setAttribute('data-usercentrics', 'essential');\r\n            l.onerror = () => console.warn('Module preload failed (non-fatal):', l.href);\r\n            document.head.appendChild(l);\r\n            \r\n            \/\/ Also preload CSS for imports if available\r\n            if (chunk.css) {\r\n              chunk.css.forEach(cssFile => {\r\n                const cssLink = document.createElement('link');\r\n                cssLink.rel = 'stylesheet';\r\n                cssLink.href = normalizePath(cssFile, base);\r\n                cssLink.crossOrigin = 'anonymous';\r\n                cssLink.setAttribute('data-usercentrics', 'essential');\r\n                document.head.appendChild(cssLink);\r\n                cssPromises.push(waitForCSS(cssLink));\r\n              });\r\n            }\r\n          }\r\n        });\r\n      }\r\n\r\n      \/\/ 4) Preload critical dynamic imports (lazy-loaded chunks)\r\n      \/\/ Note: Vue Router uses lazy loading, so these are in dynamicImports\r\n      \/\/ We preload the first few likely-to-be-needed chunks (layout, search page)\r\n      if (entry.dynamicImports && entry.dynamicImports.length > 0) {\r\n        \/\/ Preload first few dynamic imports (layout, common pages)\r\n        \/\/ Limit to avoid over-preloading everything\r\n        const criticalDynamicImports = entry.dynamicImports.slice(0, 5);\r\n        criticalDynamicImports.forEach(key => {\r\n          const chunk = manifest[key];\r\n          if (chunk?.file) {\r\n            const normalizedPath = normalizePath(chunk.file, base);\r\n            if (!normalizedPath) {\r\n              console.warn('Skipping dynamic chunk with invalid file path:', key);\r\n              return;\r\n            }\r\n            const l = document.createElement('link');\r\n            l.rel = 'modulepreload';\r\n            l.href = normalizedPath;\r\n            l.crossOrigin = 'anonymous';\r\n            l.setAttribute('data-usercentrics', 'essential');\r\n            l.onerror = () => console.warn('Dynamic import preload failed (non-fatal):', l.href);\r\n            document.head.appendChild(l);\r\n            \r\n            \/\/ Also preload CSS for dynamic imports if available\r\n            if (chunk.css) {\r\n              chunk.css.forEach(cssFile => {\r\n                const cssLink = document.createElement('link');\r\n                cssLink.rel = 'stylesheet';\r\n                cssLink.href = normalizePath(cssFile, base);\r\n                cssLink.crossOrigin = 'anonymous';\r\n                cssLink.setAttribute('data-usercentrics', 'essential');\r\n                document.head.appendChild(cssLink);\r\n                cssPromises.push(waitForCSS(cssLink));\r\n              });\r\n            }\r\n          }\r\n        });\r\n      }\r\n\r\n      \/\/ 5) Wait for critical CSS to load (but don't block indefinitely)\r\n      await Promise.all(cssPromises).catch(err => {\r\n        console.warn('Some CSS files failed to load (continuing anyway):', err);\r\n      });\r\n\r\n      \/\/ 6) Ensure DOM is ready\r\n      await waitForDOMReady();\r\n\r\n      \/\/ 7) Ensure element is still connected before loading module\r\n      if (!appEl.isConnected) {\r\n        throw new Error('App element was removed from DOM before module load');\r\n      }\r\n\r\n      \/\/ 8) Wait for next frame to ensure DOM is stable\r\n      await new Promise(resolve => {\r\n        if (typeof requestAnimationFrame !== 'undefined') {\r\n          requestAnimationFrame(() => {\r\n            requestAnimationFrame(resolve);\r\n          });\r\n        } else {\r\n          setTimeout(resolve, 0);\r\n        }\r\n      });\r\n\r\n      \/\/ 9) Final check that element still exists\r\n      if (!appEl.isConnected) {\r\n        throw new Error('App element was removed from DOM after stabilization');\r\n      }\r\n\r\n      \/\/ 10) Load entry module\r\n      const entryPath = normalizePath(entry.file, base);\r\n      if (!entryPath) {\r\n        throw new Error('Invalid entry file path in manifest');\r\n      }\r\n      console.log('Loading entry module from:', entryPath);\r\n      await import(entryPath);\r\n\r\n      \/\/ 11) Wait one more frame before mounting to ensure module initialization is complete\r\n      await new Promise(resolve => {\r\n        if (typeof requestAnimationFrame !== 'undefined') {\r\n          requestAnimationFrame(resolve);\r\n        } else {\r\n          setTimeout(resolve, 0);\r\n        }\r\n      });\r\n\r\n      \/\/ 12) Final element validation\r\n      if (!appEl.isConnected) {\r\n        throw new Error('App element was removed from DOM before mount');\r\n      }\r\n\r\n      \/\/ 13) Explicitly mount (dev auto-mount still works locally)\r\n      if (!window.BostelloApp?.isMounted?.() && typeof window.BostelloApp?.mount === 'function') {\r\n        await window.BostelloApp.mount(appEl);\r\n      } else if (window.BostelloApp?.isMounted?.()) {\r\n        console.log('Vue widget: App already mounted');\r\n      } else {\r\n        throw new Error('BostelloApp.mount is not available');\r\n      }\r\n\r\n      \/\/ 14) Wait for Vue to render its skeleton\/content\r\n      \/\/ Poll for Vue's skeleton or content to appear\r\n      let vueContentReady = false;\r\n      const maxWaitTime = 5000; \/\/ Max 5 seconds\r\n      const startTime = Date.now();\r\n      const minWaitTime = 300; \/\/ Minimum 300ms wait to ensure Vue has time to render\r\n      let checkCount = 0;\r\n      \r\n      const checkForVueContent = () => {\r\n        checkCount++;\r\n        const isMobile = window.innerWidth <= 1200;\r\n        \r\n        \/\/ Check for Vue's skeleton component - look for the actual ContentLoadingSkeleton structure\r\n        const vueSkeleton = appEl.querySelector('.container-search-page:not(.pre-skel)');\r\n        const vueSearchBar = appEl.querySelector('.search-bar-component:not(.pre-skel__search-bar)');\r\n        const vuePropertyList = appEl.querySelector('.property-list-section:not(.pre-skel__property-list-section)');\r\n        \r\n        \/\/ On mobile, map section is hidden, so don't require it\r\n        const vueMapSection = !isMobile ? appEl.querySelector('.map-section:not(.pre-skel__map-section)') : null;\r\n        \r\n        \/\/ Check if elements are actually visible (have dimensions)\r\n        const isElementVisible = (el) => {\r\n          if (!el) return false;\r\n          const rect = el.getBoundingClientRect();\r\n          const style = window.getComputedStyle(el);\r\n          return rect.width > 0 && \r\n                 rect.height > 0 && \r\n                 style.display !== 'none' && \r\n                 style.visibility !== 'hidden' &&\r\n                 style.opacity !== '0';\r\n        };\r\n        \r\n        \/\/ We need at least the search bar AND property list to be visible\r\n        \/\/ This ensures Vue's skeleton is fully rendered, not just starting\r\n        const hasSearchBar = vueSearchBar && isElementVisible(vueSearchBar);\r\n        const hasPropertyList = vuePropertyList && isElementVisible(vuePropertyList);\r\n        const hasMapSection = !isMobile ? (vueMapSection && isElementVisible(vueMapSection)) : true; \/\/ Mobile doesn't need map\r\n        \r\n        \/\/ Require both search bar and property list to be visible\r\n        const hasVueContent = hasSearchBar && hasPropertyList && hasMapSection;\r\n        \r\n        if (hasVueContent) {\r\n          console.log('Vue skeleton fully visible after', checkCount, 'checks', '(mobile:', isMobile, ')');\r\n          vueContentReady = true;\r\n          return true;\r\n        }\r\n        return false;\r\n      };\r\n      \r\n      \/\/ Wait for Vue content with polling\r\n      while (!vueContentReady && (Date.now() - startTime) < maxWaitTime) {\r\n        await new Promise(resolve => {\r\n          if (typeof requestAnimationFrame !== 'undefined') {\r\n            requestAnimationFrame(() => {\r\n              requestAnimationFrame(resolve);\r\n            });\r\n          } else {\r\n            setTimeout(resolve, 16);\r\n          }\r\n        });\r\n        checkForVueContent();\r\n      }\r\n      \r\n      \/\/ Ensure minimum wait time has passed\r\n      const elapsed = Date.now() - startTime;\r\n      if (elapsed < minWaitTime) {\r\n        await new Promise(resolve => setTimeout(resolve, minWaitTime - elapsed));\r\n      }\r\n      \r\n      if (!vueContentReady) {\r\n        console.warn('Vue content not detected within timeout, proceeding with fade-out anyway');\r\n      }\r\n      \r\n      \/\/ Additional wait to ensure Vue skeleton is fully rendered and visible\r\n      \/\/ On mobile, wait a bit longer as rendering can be slower\r\n      const isMobile = window.innerWidth <= 1200;\r\n      const additionalWaitFrames = isMobile ? 5 : 3;\r\n      \r\n      await new Promise(resolve => {\r\n        if (typeof requestAnimationFrame !== 'undefined') {\r\n          let frameCount = 0;\r\n          const waitFrame = () => {\r\n            requestAnimationFrame(() => {\r\n              frameCount++;\r\n              if (frameCount >= additionalWaitFrames) {\r\n                resolve();\r\n              } else {\r\n                waitFrame();\r\n              }\r\n            });\r\n          };\r\n          waitFrame();\r\n        } else {\r\n          setTimeout(resolve, isMobile ? 150 : 100);\r\n        }\r\n      });\r\n\r\n      \/\/ 15) Smooth transition: fade out skeleton while Vue content is visible\r\n      const skeleton = document.getElementById('app-skeleton');\r\n      if (skeleton) {\r\n        \/\/ Final verification that Vue's skeleton is fully visible and rendered\r\n        const verifyVueSkeletonVisible = () => {\r\n          const vueSearchBar = appEl.querySelector('.search-bar-component:not(.pre-skel__search-bar)');\r\n          const vuePropertyList = appEl.querySelector('.property-list-section:not(.pre-skel__property-list-section)');\r\n          const isMobile = window.innerWidth <= 1200;\r\n          const vueMapSection = !isMobile ? appEl.querySelector('.map-section:not(.pre-skel__map-section)') : null;\r\n          \r\n          const isElementVisible = (el) => {\r\n            if (!el) return false;\r\n            const rect = el.getBoundingClientRect();\r\n            const style = window.getComputedStyle(el);\r\n            \/\/ Check opacity is close to 1 (not transparent)\r\n            const opacity = parseFloat(style.opacity);\r\n            return rect.width > 50 && \/\/ Must have meaningful size\r\n                   rect.height > 50 &&\r\n                   style.display !== 'none' &&\r\n                   style.visibility !== 'hidden' &&\r\n                   opacity > 0.9; \/\/ Must be nearly fully opaque\r\n          };\r\n          \r\n          const hasSearchBar = vueSearchBar && isElementVisible(vueSearchBar);\r\n          const hasPropertyList = vuePropertyList && isElementVisible(vuePropertyList);\r\n          const hasMapSection = !isMobile ? (vueMapSection && isElementVisible(vueMapSection)) : true;\r\n          \r\n          \/\/ Also check that property list has skeleton cards rendered (not just empty container)\r\n          const hasSkeletonCards = vuePropertyList && \r\n            (vuePropertyList.querySelector('.skeleton-list') || \r\n             vuePropertyList.querySelector('.property-wrapper') ||\r\n             vuePropertyList.children.length > 0);\r\n          \r\n          \/\/ Ensure Vue skeleton container has background color\r\n          const vueContainer = appEl.querySelector('.container-search-page:not(.pre-skel)');\r\n          const hasBackground = !vueContainer || \r\n            window.getComputedStyle(vueContainer).backgroundColor !== 'rgba(0, 0, 0, 0)' &&\r\n            window.getComputedStyle(vueContainer).backgroundColor !== 'transparent';\r\n          \r\n          return hasSearchBar && hasPropertyList && hasMapSection && hasSkeletonCards && hasBackground;\r\n        };\r\n        \r\n        \/\/ Wait until Vue skeleton is fully visible with meaningful dimensions\r\n        if (!verifyVueSkeletonVisible()) {\r\n          await new Promise(resolve => {\r\n            let attempts = 0;\r\n            const maxAttempts = isMobile ? 15 : 10; \/\/ More attempts to ensure it's ready\r\n            const checkVisible = () => {\r\n              if (verifyVueSkeletonVisible() || attempts >= maxAttempts) {\r\n                resolve();\r\n              } else {\r\n                attempts++;\r\n                requestAnimationFrame(checkVisible);\r\n              }\r\n            };\r\n            checkVisible();\r\n          });\r\n        }\r\n        \r\n        \/\/ Additional safety wait to ensure Vue skeleton is fully rendered and stable\r\n        await new Promise(resolve => {\r\n          if (typeof requestAnimationFrame !== 'undefined') {\r\n            \/\/ Wait 5 frames to ensure everything is stable\r\n            let frameCount = 0;\r\n            const waitFrame = () => {\r\n              requestAnimationFrame(() => {\r\n                frameCount++;\r\n                if (frameCount >= 5) {\r\n                  resolve();\r\n                } else {\r\n                  waitFrame();\r\n                }\r\n              });\r\n            };\r\n            waitFrame();\r\n          } else {\r\n            setTimeout(resolve, 100);\r\n          }\r\n        });\r\n        \r\n        \/\/ Ensure skeleton is visible and positioned correctly with white background\r\n        skeleton.style.position = 'relative';\r\n        skeleton.style.zIndex = '1';\r\n        skeleton.style.backgroundColor = '#ffffff';\r\n        \r\n        \/\/ Ensure Vue content behind has white background\r\n        const vueContainer = appEl.querySelector('.container-search-page:not(.pre-skel)');\r\n        if (vueContainer) {\r\n          vueContainer.style.backgroundColor = '#ffffff';\r\n        }\r\n        \r\n        \/\/ Force a reflow to ensure transition works\r\n        void skeleton.offsetHeight;\r\n        \r\n        \/\/ Trigger fade-out transition\r\n        requestAnimationFrame(() => {\r\n          \/\/ On mobile, use slightly longer transition for smoother effect\r\n          const transitionDuration = isMobile ? 700 : 600;\r\n          \r\n          \/\/ Ensure skeleton background stays white during fade\r\n          skeleton.style.backgroundColor = '#ffffff';\r\n          \r\n          skeleton.classList.add('fade-out');\r\n          \r\n          \/\/ Wait for transition to complete before removing\r\n          \/\/ Add extra buffer to ensure transition completes\r\n          const removalTimeout = transitionDuration + 150; \/\/ Extra buffer for safety\r\n          \r\n          setTimeout(() => {\r\n            if (skeleton.parentNode) {\r\n              \/\/ Keep background white until fully removed\r\n              skeleton.style.backgroundColor = '#ffffff';\r\n              skeleton.style.display = 'none'; \/\/ Hide first to prevent flash\r\n              setTimeout(() => {\r\n                if (skeleton.parentNode) {\r\n                  skeleton.remove();\r\n                }\r\n                skStyle.remove();\r\n              }, 50);\r\n            } else {\r\n              skStyle.remove();\r\n            }\r\n          }, removalTimeout);\r\n        });\r\n      } else {\r\n        skStyle.remove();\r\n      }\r\n    } catch (err) {\r\n      console.error('Vue widget load failed:', err);\r\n      \/\/ Show error to user\r\n      const errorEl = document.createElement('div');\r\n      errorEl.style.padding = '12px';\r\n      errorEl.style.color = '#d32f2f';\r\n      errorEl.style.backgroundColor = '#ffebee';\r\n      errorEl.style.borderRadius = '4px';\r\n      errorEl.style.margin = '8px';\r\n      errorEl.textContent = 'Failed to load widget: ' + (err.message || 'Unknown error');\r\n      appEl.appendChild(errorEl);\r\n      \/\/ Remove skeleton on error (no transition needed for errors)\r\n      const errorSkeleton = document.getElementById('app-skeleton');\r\n      if (errorSkeleton) {\r\n        errorSkeleton.remove();\r\n      }\r\n      skStyle.remove();\r\n    }\r\n  })();\r\n<\/script>\r\n\r\n[\/et_pb_code][\/et_pb_column][\/et_pb_row][\/et_pb_section]<\/p>\n","protected":false},"excerpt":{"rendered":"<!-- Anchor element -->\r\n<div id=\"vue-widget-anchor\" data-usercentrics=\"essential\"><\/div>\r\n\r\n<!-- Vue App Loader for chunked Vite build -->\r\n<script type=\"module\" data-usercentrics=\"essential\">\r\n  (async () => {\r\n    const anchor = document.getElementById('vue-widget-anchor');\r\n    if (!anchor) {\r\n      console.error('Vue widget: anchor missing');\r\n      return;\r\n    }\r\n\r\n      \/\/ 0) Ensure #app exists and render a detailed first-paint skeleton immediately\r\n      let appEl = document.getElementById('app');\r\n      if (!appEl) {\r\n        appEl = document.createElement('div');\r\n        appEl.id = 'app';\r\n        appEl.setAttribute('data-usercentrics', 'essential');\r\n        appEl.style.position = 'relative';\r\n        appEl.style.zIndex = '1';\r\n        appEl.style.minHeight = '100vh'; \/\/ Prevent layout shift\r\n        appEl.style.backgroundColor = '#ffffff'; \/\/ Prevent white flash\r\n        anchor.appendChild(appEl);\r\n      } else {\r\n        \/\/ Ensure existing app has background\r\n        appEl.style.backgroundColor = '#ffffff';\r\n      }\r\n\r\n    \/\/ Create detailed skeleton matching ContentLoadingSkeleton structure\r\n    const sk = document.createElement('div');\r\n    sk.id = 'app-skeleton';\r\n    sk.setAttribute('data-usercentrics', 'essential');\r\n    sk.innerHTML = `\r\n      <div class=\"pre-skel container-search-page\">\r\n        <!-- Search bar skeleton -->\r\n        <div class=\"pre-skel__search-bar\">\r\n          <div class=\"pre-skel__search-row\">\r\n            <div class=\"pre-skel__searchbox\"><\/div>\r\n            <div class=\"pre-skel__chip\"><\/div>\r\n            <div class=\"pre-skel__chip\"><\/div>\r\n            <div class=\"pre-skel__chip\"><\/div>\r\n            <div class=\"pre-skel__chip\"><\/div>\r\n          <\/div>\r\n          <div class=\"pre-skel__tag-row\">\r\n            <div class=\"pre-skel__tag\"><\/div>\r\n            <div class=\"pre-skel__tag\"><\/div>\r\n            <div class=\"pre-skel__tag\"><\/div>\r\n          <\/div>\r\n        <\/div>\r\n        \r\n        <!-- Main content: list + map -->\r\n        <div class=\"pre-skel__main-content\">\r\n          <!-- Property list section -->\r\n          <div class=\"pre-skel__property-list-section\">\r\n            ${Array.from({ length: 6 }, () => `\r\n              <div class=\"pre-skel__card\">\r\n                <div class=\"pre-skel__card-image-container\">\r\n                  <div class=\"pre-skel__card-main-image\">\r\n                    <div class=\"pre-skel__card-price-tag\"><\/div>\r\n                  <\/div>\r\n                  <div class=\"pre-skel__card-thumbs\">\r\n                    <div class=\"pre-skel__card-thumb\"><\/div>\r\n                    <div class=\"pre-skel__card-thumb\"><\/div>\r\n                    <div class=\"pre-skel__card-thumb\"><\/div>\r\n                  <\/div>\r\n                <\/div>\r\n                <div class=\"pre-skel__card-content\">\r\n                  <div class=\"pre-skel__card-title\"><\/div>\r\n                  <div class=\"pre-skel__card-desc\"><\/div>\r\n                <\/div>\r\n                <div class=\"pre-skel__card-footer\">\r\n                  <div class=\"pre-skel__card-features\">\r\n                    <div class=\"pre-skel__card-feature\"><\/div>\r\n                    <div class=\"pre-skel__card-feature\"><\/div>\r\n                    <div class=\"pre-skel__card-feature\"><\/div>\r\n                  <\/div>\r\n                  <div class=\"pre-skel__card-location\"><\/div>\r\n                  <div class=\"pre-skel__card-logo\"><\/div>\r\n                <\/div>\r\n              <\/div>\r\n            `).join('')}\r\n          <\/div>\r\n          \r\n          <!-- Map section -->\r\n          <div class=\"pre-skel__map-section\">\r\n            <div class=\"pre-skel__map-skeleton\">\r\n              ${Array.from({ length: 5 }, () => `\r\n                <div class=\"pre-skel__map-row\">\r\n                  ${Array.from({ length: 8 }, () => `\r\n                    <div class=\"pre-skel__map-tile\"><\/div>\r\n                  `).join('')}\r\n                <\/div>\r\n              `).join('')}\r\n            <\/div>\r\n            <div class=\"pre-skel__map-footer\">\r\n              <div class=\"pre-skel__legend\"><\/div>\r\n              <div class=\"pre-skel__legend\"><\/div>\r\n            <\/div>\r\n          <\/div>\r\n        <\/div>\r\n      <\/div>\r\n    `;\r\n    appEl.appendChild(sk);\r\n\r\n    \/\/ Inline CSS so the skeleton shows without waiting for app CSS\r\n    const skStyle = document.createElement('style');\r\n    skStyle.setAttribute('data-usercentrics', 'essential');\r\n    skStyle.textContent = `\r\n      \/* Match ContentLoadingSkeleton container *\/\r\n      .pre-skel.container-search-page {\r\n        background-color: #ffffff;\r\n        display: flex;\r\n        flex-direction: column;\r\n        min-height: 100vh;\r\n        font-family: 'Poppins', sans-serif;\r\n        padding: 0;\r\n        margin: 0 auto;\r\n      }\r\n      \r\n      @media screen and (max-width: 1200px) {\r\n        .pre-skel.container-search-page {\r\n          padding: 0;\r\n        }\r\n      }\r\n      \r\n      \/* Search bar skeleton *\/\r\n      .pre-skel__search-bar {\r\n        position: sticky;\r\n        top: 0;\r\n        z-index: 1;\r\n        background-color: #ffffff;\r\n        padding: 0.75rem;\r\n        border-bottom: 1px solid #f5f3ef;\r\n      }\r\n      \r\n      .pre-skel__search-row {\r\n        display: grid;\r\n        grid-template-columns: 1fr repeat(4, max-content);\r\n        gap: 1rem;\r\n        align-items: center;\r\n      }\r\n      \r\n      .pre-skel__searchbox {\r\n        height: 40px;\r\n        border-radius: 10px;\r\n        background: linear-gradient(90deg, #e9e9e9 25%, #f3f3f3 37%, #e9e9e9 63%);\r\n        background-size: 400% 100%;\r\n        animation: pre-skel-shimmer 1.2s infinite linear;\r\n      }\r\n      \r\n      .pre-skel__chip {\r\n        width: 90px;\r\n        height: 36px;\r\n        border-radius: 10px;\r\n        background: linear-gradient(90deg, #e9e9e9 25%, #f3f3f3 37%, #e9e9e9 63%);\r\n        background-size: 400% 100%;\r\n        animation: pre-skel-shimmer 1.2s infinite linear;\r\n      }\r\n      \r\n      .pre-skel__tag-row {\r\n        display: flex;\r\n        gap: 0.5rem;\r\n        margin-top: 0.5rem;\r\n      }\r\n      \r\n      .pre-skel__tag {\r\n        width: 100px;\r\n        height: 26px;\r\n        border-radius: 8px;\r\n        background: linear-gradient(90deg, #e9e9e9 25%, #f3f3f3 37%, #e9e9e9 63%);\r\n        background-size: 400% 100%;\r\n        animation: pre-skel-shimmer 1.2s infinite linear;\r\n      }\r\n      \r\n      \/* Main content: list + map *\/\r\n      .pre-skel__main-content {\r\n        flex: 1;\r\n        display: flex;\r\n        gap: 0;\r\n        height: calc(100vh - 180px);\r\n        position: sticky;\r\n        padding: 0;\r\n        width: 100%;\r\n      }\r\n      \r\n      @media (max-width: 1200px) {\r\n        .pre-skel__main-content {\r\n          height: auto;\r\n          top: 140px;\r\n          padding: 0;\r\n        }\r\n      }\r\n      \r\n      \/* Property list section *\/\r\n      .pre-skel__property-list-section {\r\n        overflow-y: auto;\r\n        background-color: #ffffff;\r\n        border-radius: 8px;\r\n        height: 100%;\r\n        width: 50%;\r\n        overflow-x: hidden;\r\n        overflow-y: hidden;\r\n        min-width: 0;\r\n        padding: 0.5rem;\r\n        z-index: 1000;\r\n        box-shadow: 0 5px 10px -2px rgba(0, 0, 0, 0.1);\r\n        display: flex;\r\n        flex-direction: column;\r\n        align-items: center;\r\n        gap: 1rem;\r\n      }\r\n      \r\n      @media (max-width: 1200px) {\r\n        .pre-skel__property-list-section {\r\n          width: 100%;\r\n          padding: 1rem;\r\n        }\r\n      }\r\n      \r\n      .pre-skel__card {\r\n        background-color: white;\r\n        border-radius: 1rem;\r\n        box-shadow: rgba(0, 0, 0, 0.16) 0px 1px 4px;\r\n        overflow: hidden;\r\n        display: flex;\r\n        flex-direction: column;\r\n        min-width: 0;\r\n        width: 100%;\r\n        margin-bottom: 0.5rem;\r\n      }\r\n      \r\n      .pre-skel__card-image-container {\r\n        display: grid;\r\n        grid-template-columns: 1fr;\r\n        gap: 0.2rem;\r\n      }\r\n      \r\n      @media (min-width: 1400px) {\r\n        .pre-skel__card-image-container {\r\n          grid-template-columns: 3fr 1fr;\r\n        }\r\n      }\r\n      \r\n      .pre-skel__card-main-image {\r\n        position: relative;\r\n        height: 280px;\r\n        border-top-left-radius: 10px;\r\n        border-top-right-radius: 10px;\r\n        background: linear-gradient(90deg, #e9e9e9 25%, #f3f3f3 37%, #e9e9e9 63%);\r\n        background-size: 400% 100%;\r\n        animation: pre-skel-shimmer 1.2s infinite linear;\r\n        overflow: hidden;\r\n      }\r\n      \r\n      @media (min-width: 1400px) {\r\n        .pre-skel__card-main-image {\r\n          border-top-right-radius: 0;\r\n        }\r\n      }\r\n      \r\n      .pre-skel__card-price-tag {\r\n        position: absolute;\r\n        bottom: 8px;\r\n        left: 8px;\r\n        height: 28px;\r\n        width: 100px;\r\n        border-radius: 4px;\r\n        background: rgba(255, 255, 255, 0.9);\r\n      }\r\n      \r\n      .pre-skel__card-thumbs {\r\n        display: grid;\r\n        grid-template-columns: repeat(3, 1fr);\r\n        gap: 0.2rem;\r\n      }\r\n      \r\n      @media (min-width: 1400px) {\r\n        .pre-skel__card-thumbs {\r\n          grid-template-columns: 1fr;\r\n          grid-template-rows: repeat(3, 1fr);\r\n        }\r\n      }\r\n      \r\n      .pre-skel__card-thumb {\r\n        height: 90px;\r\n        border-radius: 4px;\r\n        background: linear-gradient(90deg, #e9e9e9 25%, #f3f3f3 37%, #e9e9e9 63%);\r\n        background-size: 400% 100%;\r\n        animation: pre-skel-shimmer 1.2s infinite linear;\r\n      }\r\n      \r\n      @media (min-width: 1400px) {\r\n        .pre-skel__card-thumb {\r\n          height: 100px;\r\n        }\r\n      }\r\n      \r\n      .pre-skel__card-content {\r\n        padding: 0.8rem;\r\n        display: grid;\r\n        gap: 0.5rem;\r\n      }\r\n      \r\n      .pre-skel__card-title {\r\n        height: 20px;\r\n        width: 70%;\r\n        border-radius: 6px;\r\n        background: linear-gradient(90deg, #e9e9e9 25%, #f3f3f3 37%, #e9e9e9 63%);\r\n        background-size: 400% 100%;\r\n        animation: pre-skel-shimmer 1.2s infinite linear;\r\n      }\r\n      \r\n      .pre-skel__card-desc {\r\n        height: 16px;\r\n        width: 90%;\r\n        border-radius: 6px;\r\n        background: linear-gradient(90deg, #e9e9e9 25%, #f3f3f3 37%, #e9e9e9 63%);\r\n        background-size: 400% 100%;\r\n        animation: pre-skel-shimmer 1.2s infinite linear;\r\n      }\r\n      \r\n      .pre-skel__card-footer {\r\n        display: grid;\r\n        grid-template-columns: 2fr 1fr;\r\n        gap: 1rem;\r\n        align-items: start;\r\n        padding: 0.4rem;\r\n      }\r\n      \r\n      .pre-skel__card-features {\r\n        display: grid;\r\n        grid-template-columns: 1fr;\r\n        gap: 0.6rem;\r\n        padding: 0.5rem;\r\n      }\r\n      \r\n      .pre-skel__card-feature {\r\n        height: 14px;\r\n        width: 40%;\r\n        border-radius: 6px;\r\n        background: linear-gradient(90deg, #e9e9e9 25%, #f3f3f3 37%, #e9e9e9 63%);\r\n        background-size: 400% 100%;\r\n        animation: pre-skel-shimmer 1.2s infinite linear;\r\n      }\r\n      \r\n      .pre-skel__card-location {\r\n        height: 14px;\r\n        width: 60%;\r\n        border-radius: 6px;\r\n        background: linear-gradient(90deg, #e9e9e9 25%, #f3f3f3 37%, #e9e9e9 63%);\r\n        background-size: 400% 100%;\r\n        animation: pre-skel-shimmer 1.2s infinite linear;\r\n        margin-top: 0.5rem;\r\n      }\r\n      \r\n      .pre-skel__card-logo {\r\n        width: 130px;\r\n        height: 100px;\r\n        border-radius: 8px;\r\n        background: linear-gradient(90deg, #e9e9e9 25%, #f3f3f3 37%, #e9e9e9 63%);\r\n        background-size: 400% 100%;\r\n        animation: pre-skel-shimmer 1.2s infinite linear;\r\n        justify-self: center;\r\n        align-self: center;\r\n      }\r\n      \r\n      \/* Map section *\/\r\n      .pre-skel__map-section {\r\n        position: sticky;\r\n        top: 80px;\r\n        height: calc(100vh);\r\n        background-color: #ffffff;\r\n        border-radius: 8px;\r\n        box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);\r\n        overflow: hidden;\r\n        width: 65%;\r\n        min-width: 400px;\r\n        display: flex;\r\n        flex-direction: column;\r\n        justify-content: center;\r\n        align-items: center;\r\n        padding: 0.5rem;\r\n      }\r\n      \r\n      @media (max-width: 1200px) {\r\n        .pre-skel__map-section {\r\n          display: none;\r\n        }\r\n      }\r\n      \r\n      \/* Map grid skeleton *\/\r\n      .pre-skel__map-skeleton {\r\n        width: 100%;\r\n        height: 100%;\r\n        display: grid;\r\n        grid-template-rows: repeat(5, 1fr);\r\n        gap: 6px;\r\n        border-radius: 8px;\r\n        overflow: hidden;\r\n      }\r\n      \r\n      .pre-skel__map-row {\r\n        display: grid;\r\n        grid-template-columns: repeat(8, 1fr);\r\n        gap: 6px;\r\n      }\r\n      \r\n      .pre-skel__map-tile {\r\n        border-radius: 6px;\r\n        background: #f0f2f5;\r\n        position: relative;\r\n        overflow: hidden;\r\n      }\r\n      \r\n      .pre-skel__map-tile::after {\r\n        content: '';\r\n        position: absolute;\r\n        inset: 0;\r\n        transform: translateX(-100%);\r\n        background: linear-gradient(\r\n          90deg,\r\n          rgba(255, 255, 255, 0) 0%,\r\n          rgba(255, 255, 255, 0.6) 50%,\r\n          rgba(255, 255, 255, 0) 100%\r\n        );\r\n        animation: pre-skel-map-shimmer 1.4s infinite;\r\n      }\r\n      \r\n      .pre-skel__map-footer {\r\n        width: 100%;\r\n        display: flex;\r\n        gap: 1rem;\r\n        margin-top: 0.5rem;\r\n        padding: 0 0.25rem;\r\n      }\r\n      \r\n      .pre-skel__legend {\r\n        height: 14px;\r\n        border-radius: 6px;\r\n        flex: 1;\r\n        background: linear-gradient(90deg, #e9e9e9 25%, #f3f3f3 37%, #e9e9e9 63%);\r\n        background-size: 400% 100%;\r\n        animation: pre-skel-shimmer 1.2s infinite linear;\r\n      }\r\n      \r\n      @keyframes pre-skel-shimmer {\r\n        0% {\r\n          background-position: 100% 0;\r\n        }\r\n        100% {\r\n          background-position: 0 0;\r\n        }\r\n      }\r\n      \r\n      @keyframes pre-skel-map-shimmer {\r\n        100% {\r\n          transform: translateX(100%);\r\n        }\r\n      }\r\n      \r\n      \/* Smooth transition styles *\/\r\n      .pre-skel {\r\n        transition: opacity 0.6s ease-out;\r\n        opacity: 1;\r\n        will-change: opacity;\r\n        position: relative;\r\n        z-index: 1;\r\n        background-color: #ffffff; \/* Ensure white background *\/\r\n      }\r\n      \r\n      @media (max-width: 1200px) {\r\n        .pre-skel {\r\n          transition: opacity 0.7s ease-out;\r\n        }\r\n      }\r\n      \r\n      .pre-skel.fade-out {\r\n        opacity: 0;\r\n        pointer-events: none;\r\n        position: absolute;\r\n        top: 0;\r\n        left: 0;\r\n        right: 0;\r\n        z-index: 10;\r\n        background-color: #ffffff !important; \/* Force white background during fade *\/\r\n        min-height: 100vh;\r\n        width: 100%;\r\n      }\r\n      \r\n      @media (max-width: 1200px) {\r\n        .pre-skel.fade-out {\r\n          min-height: 100vh;\r\n          min-height: -webkit-fill-available; \/* iOS fix *\/\r\n        }\r\n      }\r\n      \r\n      \/* Ensure skeleton container stays in place during transition *\/\r\n      #app {\r\n        position: relative;\r\n        min-height: 100vh;\r\n        background-color: #ffffff; \/* Prevent white flash *\/\r\n      }\r\n      \r\n      \/* Ensure Vue content has background during transition *\/\r\n      #app > *:not(#app-skeleton) {\r\n        background-color: #ffffff;\r\n      }\r\n    `;\r\n    document.head.appendChild(skStyle);\r\n\r\n    \/\/ Helper: Wait for CSS to load\r\n    const waitForCSS = (link) => {\r\n      return new Promise((resolve, reject) => {\r\n        if (link.sheet) {\r\n          resolve(); \/\/ Already loaded\r\n          return;\r\n        }\r\n        link.onload = () => resolve();\r\n        link.onerror = () => {\r\n          console.warn('CSS load warning (non-fatal):', link.href);\r\n          resolve(); \/\/ Resolve anyway to not block mounting\r\n        };\r\n        \/\/ Timeout after 10s (increased from 5s)\r\n        setTimeout(() => {\r\n          console.warn('CSS load timeout (non-fatal):', link.href);\r\n          resolve(); \/\/ Resolve anyway to not block mounting\r\n        }, 10000);\r\n      });\r\n    };\r\n\r\n    \/\/ Helper: Normalize file paths from manifest\r\n    \/\/ Manifest paths might be relative, absolute, or already include assets\/\r\n    const normalizePath = (filePath, base) => {\r\n      if (!filePath) return null;\r\n      \r\n      \/\/ If it's already an absolute URL, return as-is (but log a warning if it's the wrong domain)\r\n      if (filePath.startsWith('http:\/\/') || filePath.startsWith('https:\/\/')) {\r\n        \/\/ Check if it's pointing to wrong domain\r\n        const url = new URL(filePath);\r\n        const baseUrl = new URL(base);\r\n        if (url.origin !== baseUrl.origin) {\r\n          console.warn(`Manifest contains absolute URL from different domain: ${filePath}, expected: ${baseUrl.origin}`);\r\n          \/\/ Try to fix it by replacing the domain\r\n          return filePath.replace(url.origin, baseUrl.origin);\r\n        }\r\n        return filePath;\r\n      }\r\n      \r\n      \/\/ If path starts with \/, it's absolute from root - prepend base\r\n      if (filePath.startsWith('\/')) {\r\n        return base + filePath.slice(1);\r\n      }\r\n      \r\n      \/\/ If path already starts with assets\/, just prepend base\r\n      if (filePath.startsWith('assets\/')) {\r\n        return base + filePath;\r\n      }\r\n      \r\n      \/\/ Otherwise, assume it needs assets\/ prefix\r\n      return base + 'assets\/' + filePath;\r\n    };\r\n\r\n    \/\/ Helper: Wait for DOM to be stable\r\n    const waitForDOMReady = () => {\r\n      if (document.readyState === 'complete' || document.readyState === 'interactive') {\r\n        return Promise.resolve();\r\n      }\r\n      return new Promise(resolve => {\r\n        if (document.readyState === 'loading') {\r\n          document.addEventListener('DOMContentLoaded', resolve, { once: true });\r\n        } else {\r\n          resolve();\r\n        }\r\n      });\r\n    };\r\n\r\n    try {\r\n      \/\/ 1) Load manifest\r\n      \/\/ https:\/\/frontend.production.platform.bostello.com\/assets\/\r\n     \/\/ const base = 'https:\/\/bostello.com\/vue\/';\r\n      const base = 'https:\/\/frontend.production.platform.bostello.com\/';\r\n      const manifestRes = await fetch(base + 'assets\/manifest.json', { cache: 'no-store' });\r\n      if (!manifestRes.ok) {\r\n        throw new Error(`Failed to load manifest: ${manifestRes.status} ${manifestRes.statusText}`);\r\n      }\r\n      const manifest = await manifestRes.json();\r\n      console.log('Manifest loaded, base URL:', base);\r\n      console.log('Sample manifest entry:', Object.keys(manifest)[0], manifest[Object.keys(manifest)[0]]);\r\n\r\n      \/\/ Vite often uses 'index.html' or 'src\/main.ts' as the manifest entry key\r\n      const entry = manifest['index.html'] || manifest['src\/main.ts'];\r\n      if (!entry || !entry.file) {\r\n        throw new Error('Vue widget: no entry found in manifest');\r\n      }\r\n      console.log('Entry file from manifest:', entry.file);\r\n      console.log('Entry imports:', entry.imports?.length || 0);\r\n      console.log('Entry dynamicImports:', entry.dynamicImports?.length || 0);\r\n\r\n      \/\/ 2) Load entry CSS first and wait for it to load\r\n      const cssPromises = [];\r\n      if (entry.css && entry.css.length > 0) {\r\n        entry.css.forEach(cssFile => {\r\n          const link = document.createElement('link');\r\n          link.rel = 'stylesheet';\r\n          link.href = normalizePath(cssFile, base);\r\n          link.crossOrigin = 'anonymous';\r\n          link.setAttribute('data-usercentrics', 'essential');\r\n          document.head.appendChild(link);\r\n          cssPromises.push(waitForCSS(link));\r\n        });\r\n      }\r\n\r\n      \/\/ 3) Preload static imports (eager chunks)\r\n      if (entry.imports && entry.imports.length > 0) {\r\n        entry.imports.forEach(key => {\r\n          const chunk = manifest[key];\r\n          if (chunk?.file) {\r\n            const normalizedPath = normalizePath(chunk.file, base);\r\n            if (!normalizedPath) {\r\n              console.warn('Skipping chunk with invalid file path:', key);\r\n              return;\r\n            }\r\n            const l = document.createElement('link');\r\n            l.rel = 'modulepreload';\r\n            l.href = normalizedPath;\r\n            l.crossOrigin = 'anonymous';\r\n            l.setAttribute('data-usercentrics', 'essential');\r\n            l.onerror = () => console.warn('Module preload failed (non-fatal):', l.href);\r\n            document.head.appendChild(l);\r\n            \r\n            \/\/ Also preload CSS for imports if available\r\n            if (chunk.css) {\r\n              chunk.css.forEach(cssFile => {\r\n                const cssLink = document.createElement('link');\r\n                cssLink.rel = 'stylesheet';\r\n                cssLink.href = normalizePath(cssFile, base);\r\n                cssLink.crossOrigin = 'anonymous';\r\n                cssLink.setAttribute('data-usercentrics', 'essential');\r\n                document.head.appendChild(cssLink);\r\n                cssPromises.push(waitForCSS(cssLink));\r\n              });\r\n            }\r\n          }\r\n        });\r\n      }\r\n\r\n      \/\/ 4) Preload critical dynamic imports (lazy-loaded chunks)\r\n      \/\/ Note: Vue Router uses lazy loading, so these are in dynamicImports\r\n      \/\/ We preload the first few likely-to-be-needed chunks (layout, search page)\r\n      if (entry.dynamicImports && entry.dynamicImports.length > 0) {\r\n        \/\/ Preload first few dynamic imports (layout, common pages)\r\n        \/\/ Limit to avoid over-preloading everything\r\n        const criticalDynamicImports = entry.dynamicImports.slice(0, 5);\r\n        criticalDynamicImports.forEach(key => {\r\n          const chunk = manifest[key];\r\n          if (chunk?.file) {\r\n            const normalizedPath = normalizePath(chunk.file, base);\r\n            if (!normalizedPath) {\r\n              console.warn('Skipping dynamic chunk with invalid file path:', key);\r\n              return;\r\n            }\r\n            const l = document.createElement('link');\r\n            l.rel = 'modulepreload';\r\n            l.href = normalizedPath;\r\n            l.crossOrigin = 'anonymous';\r\n            l.setAttribute('data-usercentrics', 'essential');\r\n            l.onerror = () => console.warn('Dynamic import preload failed (non-fatal):', l.href);\r\n            document.head.appendChild(l);\r\n            \r\n            \/\/ Also preload CSS for dynamic imports if available\r\n            if (chunk.css) {\r\n              chunk.css.forEach(cssFile => {\r\n                const cssLink = document.createElement('link');\r\n                cssLink.rel = 'stylesheet';\r\n                cssLink.href = normalizePath(cssFile, base);\r\n                cssLink.crossOrigin = 'anonymous';\r\n                cssLink.setAttribute('data-usercentrics', 'essential');\r\n                document.head.appendChild(cssLink);\r\n                cssPromises.push(waitForCSS(cssLink));\r\n              });\r\n            }\r\n          }\r\n        });\r\n      }\r\n\r\n      \/\/ 5) Wait for critical CSS to load (but don't block indefinitely)\r\n      await Promise.all(cssPromises).catch(err => {\r\n        console.warn('Some CSS files failed to load (continuing anyway):', err);\r\n      });\r\n\r\n      \/\/ 6) Ensure DOM is ready\r\n      await waitForDOMReady();\r\n\r\n      \/\/ 7) Ensure element is still connected before loading module\r\n      if (!appEl.isConnected) {\r\n        throw new Error('App element was removed from DOM before module load');\r\n      }\r\n\r\n      \/\/ 8) Wait for next frame to ensure DOM is stable\r\n      await new Promise(resolve => {\r\n        if (typeof requestAnimationFrame !== 'undefined') {\r\n          requestAnimationFrame(() => {\r\n            requestAnimationFrame(resolve);\r\n          });\r\n        } else {\r\n          setTimeout(resolve, 0);\r\n        }\r\n      });\r\n\r\n      \/\/ 9) Final check that element still exists\r\n      if (!appEl.isConnected) {\r\n        throw new Error('App element was removed from DOM after stabilization');\r\n      }\r\n\r\n      \/\/ 10) Load entry module\r\n      const entryPath = normalizePath(entry.file, base);\r\n      if (!entryPath) {\r\n        throw new Error('Invalid entry file path in manifest');\r\n      }\r\n      console.log('Loading entry module from:', entryPath);\r\n      await import(entryPath);\r\n\r\n      \/\/ 11) Wait one more frame before mounting to ensure module initialization is complete\r\n      await new Promise(resolve => {\r\n        if (typeof requestAnimationFrame !== 'undefined') {\r\n          requestAnimationFrame(resolve);\r\n        } else {\r\n          setTimeout(resolve, 0);\r\n        }\r\n      });\r\n\r\n      \/\/ 12) Final element validation\r\n      if (!appEl.isConnected) {\r\n        throw new Error('App element was removed from DOM before mount');\r\n      }\r\n\r\n      \/\/ 13) Explicitly mount (dev auto-mount still works locally)\r\n      if (!window.BostelloApp?.isMounted?.() && typeof window.BostelloApp?.mount === 'function') {\r\n        await window.BostelloApp.mount(appEl);\r\n      } else if (window.BostelloApp?.isMounted?.()) {\r\n        console.log('Vue widget: App already mounted');\r\n      } else {\r\n        throw new Error('BostelloApp.mount is not available');\r\n      }\r\n\r\n      \/\/ 14) Wait for Vue to render its skeleton\/content\r\n      \/\/ Poll for Vue's skeleton or content to appear\r\n      let vueContentReady = false;\r\n      const maxWaitTime = 5000; \/\/ Max 5 seconds\r\n      const startTime = Date.now();\r\n      const minWaitTime = 300; \/\/ Minimum 300ms wait to ensure Vue has time to render\r\n      let checkCount = 0;\r\n      \r\n      const checkForVueContent = () => {\r\n        checkCount++;\r\n        const isMobile = window.innerWidth <= 1200;\r\n        \r\n        \/\/ Check for Vue's skeleton component - look for the actual ContentLoadingSkeleton structure\r\n        const vueSkeleton = appEl.querySelector('.container-search-page:not(.pre-skel)');\r\n        const vueSearchBar = appEl.querySelector('.search-bar-component:not(.pre-skel__search-bar)');\r\n        const vuePropertyList = appEl.querySelector('.property-list-section:not(.pre-skel__property-list-section)');\r\n        \r\n        \/\/ On mobile, map section is hidden, so don't require it\r\n        const vueMapSection = !isMobile ? appEl.querySelector('.map-section:not(.pre-skel__map-section)') : null;\r\n        \r\n        \/\/ Check if elements are actually visible (have dimensions)\r\n        const isElementVisible = (el) => {\r\n          if (!el) return false;\r\n          const rect = el.getBoundingClientRect();\r\n          const style = window.getComputedStyle(el);\r\n          return rect.width > 0 && \r\n                 rect.height > 0 && \r\n                 style.display !== 'none' && \r\n                 style.visibility !== 'hidden' &&\r\n                 style.opacity !== '0';\r\n        };\r\n        \r\n        \/\/ We need at least the search bar AND property list to be visible\r\n        \/\/ This ensures Vue's skeleton is fully rendered, not just starting\r\n        const hasSearchBar = vueSearchBar && isElementVisible(vueSearchBar);\r\n        const hasPropertyList = vuePropertyList && isElementVisible(vuePropertyList);\r\n        const hasMapSection = !isMobile ? (vueMapSection && isElementVisible(vueMapSection)) : true; \/\/ Mobile doesn't need map\r\n        \r\n        \/\/ Require both search bar and property list to be visible\r\n        const hasVueContent = hasSearchBar && hasPropertyList && hasMapSection;\r\n        \r\n        if (hasVueContent) {\r\n          console.log('Vue skeleton fully visible after', checkCount, 'checks', '(mobile:', isMobile, ')');\r\n          vueContentReady = true;\r\n          return true;\r\n        }\r\n        return false;\r\n      };\r\n      \r\n      \/\/ Wait for Vue content with polling\r\n      while (!vueContentReady && (Date.now() - startTime) < maxWaitTime) {\r\n        await new Promise(resolve => {\r\n          if (typeof requestAnimationFrame !== 'undefined') {\r\n            requestAnimationFrame(() => {\r\n              requestAnimationFrame(resolve);\r\n            });\r\n          } else {\r\n            setTimeout(resolve, 16);\r\n          }\r\n        });\r\n        checkForVueContent();\r\n      }\r\n      \r\n      \/\/ Ensure minimum wait time has passed\r\n      const elapsed = Date.now() - startTime;\r\n      if (elapsed < minWaitTime) {\r\n        await new Promise(resolve => setTimeout(resolve, minWaitTime - elapsed));\r\n      }\r\n      \r\n      if (!vueContentReady) {\r\n        console.warn('Vue content not detected within timeout, proceeding with fade-out anyway');\r\n      }\r\n      \r\n      \/\/ Additional wait to ensure Vue skeleton is fully rendered and visible\r\n      \/\/ On mobile, wait a bit longer as rendering can be slower\r\n      const isMobile = window.innerWidth <= 1200;\r\n      const additionalWaitFrames = isMobile ? 5 : 3;\r\n      \r\n      await new Promise(resolve => {\r\n        if (typeof requestAnimationFrame !== 'undefined') {\r\n          let frameCount = 0;\r\n          const waitFrame = () => {\r\n            requestAnimationFrame(() => {\r\n              frameCount++;\r\n              if (frameCount >= additionalWaitFrames) {\r\n                resolve();\r\n              } else {\r\n                waitFrame();\r\n              }\r\n            });\r\n          };\r\n          waitFrame();\r\n        } else {\r\n          setTimeout(resolve, isMobile ? 150 : 100);\r\n        }\r\n      });\r\n\r\n      \/\/ 15) Smooth transition: fade out skeleton while Vue content is visible\r\n      const skeleton = document.getElementById('app-skeleton');\r\n      if (skeleton) {\r\n        \/\/ Final verification that Vue's skeleton is fully visible and rendered\r\n        const verifyVueSkeletonVisible = () => {\r\n          const vueSearchBar = appEl.querySelector('.search-bar-component:not(.pre-skel__search-bar)');\r\n          const vuePropertyList = appEl.querySelector('.property-list-section:not(.pre-skel__property-list-section)');\r\n          const isMobile = window.innerWidth <= 1200;\r\n          const vueMapSection = !isMobile ? appEl.querySelector('.map-section:not(.pre-skel__map-section)') : null;\r\n          \r\n          const isElementVisible = (el) => {\r\n            if (!el) return false;\r\n            const rect = el.getBoundingClientRect();\r\n            const style = window.getComputedStyle(el);\r\n            \/\/ Check opacity is close to 1 (not transparent)\r\n            const opacity = parseFloat(style.opacity);\r\n            return rect.width > 50 && \/\/ Must have meaningful size\r\n                   rect.height > 50 &&\r\n                   style.display !== 'none' &&\r\n                   style.visibility !== 'hidden' &&\r\n                   opacity > 0.9; \/\/ Must be nearly fully opaque\r\n          };\r\n          \r\n          const hasSearchBar = vueSearchBar && isElementVisible(vueSearchBar);\r\n          const hasPropertyList = vuePropertyList && isElementVisible(vuePropertyList);\r\n          const hasMapSection = !isMobile ? (vueMapSection && isElementVisible(vueMapSection)) : true;\r\n          \r\n          \/\/ Also check that property list has skeleton cards rendered (not just empty container)\r\n          const hasSkeletonCards = vuePropertyList && \r\n            (vuePropertyList.querySelector('.skeleton-list') || \r\n             vuePropertyList.querySelector('.property-wrapper') ||\r\n             vuePropertyList.children.length > 0);\r\n          \r\n          \/\/ Ensure Vue skeleton container has background color\r\n          const vueContainer = appEl.querySelector('.container-search-page:not(.pre-skel)');\r\n          const hasBackground = !vueContainer || \r\n            window.getComputedStyle(vueContainer).backgroundColor !== 'rgba(0, 0, 0, 0)' &&\r\n            window.getComputedStyle(vueContainer).backgroundColor !== 'transparent';\r\n          \r\n          return hasSearchBar && hasPropertyList && hasMapSection && hasSkeletonCards && hasBackground;\r\n        };\r\n        \r\n        \/\/ Wait until Vue skeleton is fully visible with meaningful dimensions\r\n        if (!verifyVueSkeletonVisible()) {\r\n          await new Promise(resolve => {\r\n            let attempts = 0;\r\n            const maxAttempts = isMobile ? 15 : 10; \/\/ More attempts to ensure it's ready\r\n            const checkVisible = () => {\r\n              if (verifyVueSkeletonVisible() || attempts >= maxAttempts) {\r\n                resolve();\r\n              } else {\r\n                attempts++;\r\n                requestAnimationFrame(checkVisible);\r\n              }\r\n            };\r\n            checkVisible();\r\n          });\r\n        }\r\n        \r\n        \/\/ Additional safety wait to ensure Vue skeleton is fully rendered and stable\r\n        await new Promise(resolve => {\r\n          if (typeof requestAnimationFrame !== 'undefined') {\r\n            \/\/ Wait 5 frames to ensure everything is stable\r\n            let frameCount = 0;\r\n            const waitFrame = () => {\r\n              requestAnimationFrame(() => {\r\n                frameCount++;\r\n                if (frameCount >= 5) {\r\n                  resolve();\r\n                } else {\r\n                  waitFrame();\r\n                }\r\n              });\r\n            };\r\n            waitFrame();\r\n          } else {\r\n            setTimeout(resolve, 100);\r\n          }\r\n        });\r\n        \r\n        \/\/ Ensure skeleton is visible and positioned correctly with white background\r\n        skeleton.style.position = 'relative';\r\n        skeleton.style.zIndex = '1';\r\n        skeleton.style.backgroundColor = '#ffffff';\r\n        \r\n        \/\/ Ensure Vue content behind has white background\r\n        const vueContainer = appEl.querySelector('.container-search-page:not(.pre-skel)');\r\n        if (vueContainer) {\r\n          vueContainer.style.backgroundColor = '#ffffff';\r\n        }\r\n        \r\n        \/\/ Force a reflow to ensure transition works\r\n        void skeleton.offsetHeight;\r\n        \r\n        \/\/ Trigger fade-out transition\r\n        requestAnimationFrame(() => {\r\n          \/\/ On mobile, use slightly longer transition for smoother effect\r\n          const transitionDuration = isMobile ? 700 : 600;\r\n          \r\n          \/\/ Ensure skeleton background stays white during fade\r\n          skeleton.style.backgroundColor = '#ffffff';\r\n          \r\n          skeleton.classList.add('fade-out');\r\n          \r\n          \/\/ Wait for transition to complete before removing\r\n          \/\/ Add extra buffer to ensure transition completes\r\n          const removalTimeout = transitionDuration + 150; \/\/ Extra buffer for safety\r\n          \r\n          setTimeout(() => {\r\n            if (skeleton.parentNode) {\r\n              \/\/ Keep background white until fully removed\r\n              skeleton.style.backgroundColor = '#ffffff';\r\n              skeleton.style.display = 'none'; \/\/ Hide first to prevent flash\r\n              setTimeout(() => {\r\n                if (skeleton.parentNode) {\r\n                  skeleton.remove();\r\n                }\r\n                skStyle.remove();\r\n              }, 50);\r\n            } else {\r\n              skStyle.remove();\r\n            }\r\n          }, removalTimeout);\r\n        });\r\n      } else {\r\n        skStyle.remove();\r\n      }\r\n    } catch (err) {\r\n      console.error('Vue widget load failed:', err);\r\n      \/\/ Show error to user\r\n      const errorEl = document.createElement('div');\r\n      errorEl.style.padding = '12px';\r\n      errorEl.style.color = '#d32f2f';\r\n      errorEl.style.backgroundColor = '#ffebee';\r\n      errorEl.style.borderRadius = '4px';\r\n      errorEl.style.margin = '8px';\r\n      errorEl.textContent = 'Failed to load widget: ' + (err.message || 'Unknown error');\r\n      appEl.appendChild(errorEl);\r\n      \/\/ Remove skeleton on error (no transition needed for errors)\r\n      const errorSkeleton = document.getElementById('app-skeleton');\r\n      if (errorSkeleton) {\r\n        errorSkeleton.remove();\r\n      }\r\n      skStyle.remove();\r\n    }\r\n  })();\r\n<\/script>\r\n\r\n\n","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"_et_pb_use_builder":"on","_et_pb_old_content":"","_et_gb_content_width":"","footnotes":""},"class_list":["post-25986166","page","type-page","status-publish","hentry"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v26.5 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>property - Bostello<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/bostello.com\/pl\/about\/\" \/>\n<meta property=\"og:locale\" content=\"pl_PL\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"property - Bostello\" \/>\n<meta property=\"og:description\" content=\"Enter your details and our team will get back to you.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/bostello.com\/pl\/about\/\" \/>\n<meta property=\"og:site_name\" content=\"Bostello\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/profile.php?id=61576560999312\" \/>\n<meta property=\"article:modified_time\" content=\"2025-10-31T15:36:12+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/bostello.com\/wp-content\/uploads\/2025\/04\/cropped-bostello.png\" \/>\n\t<meta property=\"og:image:width\" content=\"512\" \/>\n\t<meta property=\"og:image:height\" content=\"512\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\/\/bostello.com\/pl\/about\/\",\"url\":\"https:\/\/bostello.com\/pl\/about\/\",\"name\":\"property - Bostello\",\"isPartOf\":{\"@id\":\"https:\/\/bostello.com\/pl\/#website\"},\"datePublished\":\"2025-04-21T10:08:05+00:00\",\"dateModified\":\"2025-10-31T15:36:12+00:00\",\"breadcrumb\":{\"@id\":\"https:\/\/bostello.com\/pl\/about\/#breadcrumb\"},\"inLanguage\":\"pl-PL\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/bostello.com\/pl\/about\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/bostello.com\/pl\/about\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Hem\",\"item\":\"https:\/\/bostello.com\/pl\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"property\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/bostello.com\/pl\/#website\",\"url\":\"https:\/\/bostello.com\/pl\/\",\"name\":\"Bostello.com \u2013 Buy Apartments & Houses in Spain\",\"description\":\"Your gateway to dream homes in Costa Blanca &amp; Costa del Sol.\",\"publisher\":{\"@id\":\"https:\/\/bostello.com\/pl\/#organization\"},\"alternateName\":\"Bostello.com \u2013 Find Your Dream Home in Spain\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/bostello.com\/pl\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"pl-PL\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/bostello.com\/pl\/#organization\",\"name\":\"Bostello.com\",\"url\":\"https:\/\/bostello.com\/pl\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"pl-PL\",\"@id\":\"https:\/\/bostello.com\/pl\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/bostello.com\/wp-content\/uploads\/2025\/04\/cropped-bostello.png\",\"contentUrl\":\"https:\/\/bostello.com\/wp-content\/uploads\/2025\/04\/cropped-bostello.png\",\"width\":512,\"height\":512,\"caption\":\"Bostello.com\"},\"image\":{\"@id\":\"https:\/\/bostello.com\/pl\/#\/schema\/logo\/image\/\"},\"sameAs\":[\"https:\/\/www.facebook.com\/profile.php?id=61576560999312\"]}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"property - Bostello","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/bostello.com\/pl\/about\/","og_locale":"pl_PL","og_type":"article","og_title":"property - Bostello","og_description":"Enter your details and our team will get back to you.","og_url":"https:\/\/bostello.com\/pl\/about\/","og_site_name":"Bostello","article_publisher":"https:\/\/www.facebook.com\/profile.php?id=61576560999312","article_modified_time":"2025-10-31T15:36:12+00:00","og_image":[{"width":512,"height":512,"url":"https:\/\/bostello.com\/wp-content\/uploads\/2025\/04\/cropped-bostello.png","type":"image\/png"}],"twitter_card":"summary_large_image","schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/bostello.com\/pl\/about\/","url":"https:\/\/bostello.com\/pl\/about\/","name":"property - Bostello","isPartOf":{"@id":"https:\/\/bostello.com\/pl\/#website"},"datePublished":"2025-04-21T10:08:05+00:00","dateModified":"2025-10-31T15:36:12+00:00","breadcrumb":{"@id":"https:\/\/bostello.com\/pl\/about\/#breadcrumb"},"inLanguage":"pl-PL","potentialAction":[{"@type":"ReadAction","target":["https:\/\/bostello.com\/pl\/about\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/bostello.com\/pl\/about\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Hem","item":"https:\/\/bostello.com\/pl\/"},{"@type":"ListItem","position":2,"name":"property"}]},{"@type":"WebSite","@id":"https:\/\/bostello.com\/pl\/#website","url":"https:\/\/bostello.com\/pl\/","name":"Bostello.com \u2013 Buy Apartments & Houses in Spain","description":"Your gateway to dream homes in Costa Blanca &amp; Costa del Sol.","publisher":{"@id":"https:\/\/bostello.com\/pl\/#organization"},"alternateName":"Bostello.com \u2013 Find Your Dream Home in Spain","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/bostello.com\/pl\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"pl-PL"},{"@type":"Organization","@id":"https:\/\/bostello.com\/pl\/#organization","name":"Bostello.com","url":"https:\/\/bostello.com\/pl\/","logo":{"@type":"ImageObject","inLanguage":"pl-PL","@id":"https:\/\/bostello.com\/pl\/#\/schema\/logo\/image\/","url":"https:\/\/bostello.com\/wp-content\/uploads\/2025\/04\/cropped-bostello.png","contentUrl":"https:\/\/bostello.com\/wp-content\/uploads\/2025\/04\/cropped-bostello.png","width":512,"height":512,"caption":"Bostello.com"},"image":{"@id":"https:\/\/bostello.com\/pl\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/www.facebook.com\/profile.php?id=61576560999312"]}]}},"_links":{"self":[{"href":"https:\/\/bostello.com\/pl\/wp-json\/wp\/v2\/pages\/25986166","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/bostello.com\/pl\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/bostello.com\/pl\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/bostello.com\/pl\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/bostello.com\/pl\/wp-json\/wp\/v2\/comments?post=25986166"}],"version-history":[{"count":0,"href":"https:\/\/bostello.com\/pl\/wp-json\/wp\/v2\/pages\/25986166\/revisions"}],"wp:attachment":[{"href":"https:\/\/bostello.com\/pl\/wp-json\/wp\/v2\/media?parent=25986166"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}