/* Property Search Results Page - Zillow-style Layout
   Component-specific layout styles
   Common utilities loaded from utilities.css */

/* Full-width container for search results.
   Sized via top:64px + bottom:0 (NOT height:100vh) so the box ends at the
   viewport bottom. With position:fixed, height:100vh + top:64px overshoots
   by 64px and pushes the map's RIGHT_BOTTOM zoom control offscreen - the
   minus button ends up behind the OS taskbar / scrollbar gutter. */
.property-search-container {
    width: 100%;
    display: flex;
    flex-direction: column;
    overflow: hidden;
    position: fixed;
    top: 64px; /* Below header */
    left: 0;
    right: 0;
    bottom: 0;
}

/* Filter bar sticky positioning */
.property-search-filters {
    flex-shrink: 0;
    z-index: 100;
    background: white;
    border-bottom: 1px solid #e0e0e0;
    box-shadow: 0 2px 4px rgba(0,0,0,0.04);
}

/* Split view container */
.property-search-split {
    display: flex;
    flex: 1;
    overflow: hidden;
    width: 100%;
    min-height: 0; /* Important for flex children */
}

/* Left side - Scrollable results */
.property-search-results {
    flex: 0 0 40%;
    max-width: 40%;
    height: 100%;
    background: #f7f7f7;
    display: flex;
    flex-direction: column;
    overflow: hidden; /* Parent doesn't scroll, only content scrolls */
    position: relative;
    isolation: isolate; /* Create stacking context */
}

/* Top refresh bar shown during map-driven refetches.
   Sits above the results content so the previous list remains visible while
   new data is fetched - much gentler than covering the panel with a white
   overlay. Height is 2px; colour comes from MudProgressLinear (Primary). */
.property-search-refresh-bar {
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    z-index: 5;
    pointer-events: none;
    opacity: 0;
    transition: opacity 0.15s ease;
}

.property-search-refresh-bar.is-visible {
    opacity: 1;
}

.property-search-refresh-bar .mud-progress-linear {
    height: 2px;
}

/* Right side - Sticky map.
   Flex column so the optional POI category toolbar (rendered above the map
   when Features.MapContextEnabled is on) and the map itself share the
   container's height. Without flex-column, the toolbar pushes the map's
   wrapper down while the wrapper still claims `height: 100%`, overflowing
   the container by the toolbar's height - and overflow:hidden clips the
   bottom strip, hiding the RIGHT_BOTTOM zoom-out (-) button and the
   Google Terms/Report attribution. */
.property-search-map-container {
    flex: 0 0 60%;
    max-width: 60%;
    height: 100%;
    overflow: hidden;
    position: relative;
    isolation: isolate; /* Create separate stacking context */
    display: flex;
    flex-direction: column;
    min-height: 0;
}

/* Results content wrapper - scrollable (scrollbar styles in utilities.css) */
.property-search-results-content {
    flex: 1;
    overflow-y: auto;
    overflow-x: hidden;
    padding: 20px;
    min-height: 0;
    position: relative;
    contain: layout style;
}

/* Results header */
.property-search-results-header {
    padding: 16px 0;
    margin-bottom: 16px;
    display: flex;
    align-items: center;
    justify-content: space-between;
}

.property-search-results-count {
    font-size: 16px;
    font-weight: 600;
    color: #333;
}

/* Footer within results - uses .footer-inline from utilities.css
   Component-specific positioning only */
.property-search-results-footer {
    /* Inherits styles from .footer-inline in utilities.css */
}

/* Property card grid - base styles live in utilities.css (.property-results-grid).
   Inside the split view the results pane is narrower than the full page, so we tighten
   the minmax so cards pack as 2-per-row whenever the pane can fit them. */
.property-search-results-content .property-results-grid {
    grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
    gap: 16px;
}

/* Search-scope card polish - tighter content, cleaner type hierarchy. */
.property-search-results-content .property-result-card {
    border-radius: 12px;
}

.property-search-results-content .property-result-card .mud-card-content {
    padding: 12px 14px !important;
}

.property-search-results-content .property-result-card .mud-typography-h6 {
    font-size: 1.05rem;
    line-height: 1.2;
    margin-bottom: 6px !important;
}

.property-search-results-content .property-result-card .mud-chip {
    height: 22px;
    font-size: 11px;
    font-weight: 600;
    padding: 0 8px;
}

/* Empty state - inherits from .empty-state utilities */
.property-search-empty {
    /* Uses .empty-state from utilities.css */
}

.property-search-empty-icon {
    /* Uses .empty-state-icon from utilities.css */
}

.property-search-empty-title {
    /* Uses .empty-state-title from utilities.css */
}

.property-search-empty-text {
    /* Uses .empty-state-text from utilities.css */
}

/* iPad landscape / small desktop (900–1199px): keep the split view but rebalance
   to 50/50 so the results pane is wide enough for a 2-column grid, and drop the
   card min-width so two tighter cards fit comfortably in the pane. */
@media (max-width: 1199px) and (min-width: 900px) {
    .property-search-results {
        flex: 0 0 50%;
        max-width: 50%;
    }

    .property-search-map-container {
        flex: 0 0 50%;
        max-width: 50%;
    }

    .property-search-results-content {
        padding: 14px;
    }

    .property-search-results-content .property-results-grid {
        grid-template-columns: repeat(auto-fill, minmax(215px, 1fr));
        gap: 14px;
    }

    /* Tighter cards - shrink the hero image so two cards per row don't feel stretched.
       Inline height:200px is set in PropertyResultCard.razor, so !important wins. */
    .property-search-results-content .property-result-card .property-image-container {
        height: 160px !important;
    }

    .property-search-results-content .property-result-card .mud-card-content {
        padding: 12px !important;
    }
}

/* The floating mobile Map/List toggle (feedback #173). Hidden on desktop (the split shows
   both panels side-by-side); revealed inside the <=899px media query below. */
.property-search-mobile-toggle {
    display: none !important;
}

/* Tablet portrait + phone (feedback #173): the search page LEADS WITH THE MAP and hides the
   listings; a floating toggle switches to the full-width list and back. Which panel shows is
   driven by .ovo-mobile-map / .ovo-mobile-list on the container.

   LIST view keeps the native-page-scroll release (position:static) so the whole page scrolls
   with momentum / URL-bar collapse. MAP view re-fixes the container to the viewport (like the
   desktop split) so the interactive map fills the space under the filter bar. */
@media (max-width: 899px) {
    .property-search-container {
        position: static;
        height: auto;
        top: auto;
        overflow: visible;
    }

    .property-search-split {
        flex-direction: column;
        overflow: visible;
    }

    .property-search-results {
        flex: 1;
        max-width: 100%;
        width: 100%;
        height: auto;
        overflow: visible;
    }

    .property-search-results-content {
        overflow: visible;
        contain: none;
    }

    .property-search-map-container {
        display: none !important;
    }

    .property-search-results-content .property-results-grid {
        grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
        gap: 16px;
    }

    /* ---- MAP VIEW (default on mobile): the map leads, the list is hidden. Re-fix the
       container to the viewport so the map fills the space below the sticky filter bar. ---- */
    .property-search-container.ovo-mobile-map {
        position: fixed;
        top: var(--mud-appbar-height, 64px);
        left: 0;
        right: 0;
        bottom: 0;
        height: auto;
        overflow: hidden;
    }

    .property-search-container.ovo-mobile-map .property-search-split {
        flex-direction: column;
        overflow: hidden;
        flex: 1;
        min-height: 0;
    }

    .property-search-container.ovo-mobile-map .property-search-results {
        display: none;
    }

    .property-search-container.ovo-mobile-map .property-search-map-container {
        display: flex !important;
        flex: 1;
        max-width: 100%;
        width: 100%;
        height: auto;
        min-height: 0;
    }

    /* Reveal the floating Map/List toggle, centred just above the bottom edge. */
    .property-search-mobile-toggle {
        display: inline-flex !important;
        position: fixed;
        bottom: 20px;
        left: 50%;
        transform: translateX(-50%);
        z-index: 1200;
        box-shadow: 0 6px 20px rgba(0, 0, 0, 0.28);
    }
}

@media (max-width: 768px) {
    .property-search-results-content {
        padding: 12px;
    }

    .property-search-results-content .property-results-grid {
        grid-template-columns: 1fr;
        gap: 14px;
    }
}

/* Dim Google's default Terms/Report attribution so it reads without shouting.
   Keyboard-shortcuts link is already suppressed via keyboardShortcuts:false in
   property-map-manager.js - no CSS needed for that. Note: we deliberately do NOT
   style .gmnoprint / .gm-bundled-control children (zoom, fullscreen) because
   Google Maps relies on very specific internal dimensions, and even innocuous
   overrides collapse the controls to 0×0. */
.property-search-map-container .gm-style-cc {
    opacity: 0.75;
}

/* Suppress Google's hidden internal toggle buttons inside the attribution
   container. Each .gm-style-cc element has a <button aria-label="Map Data">
   sibling next to its visible <span> attribution text - Google ships it with
   inline `display: none` and only flips it visible during certain map state
   transitions (basemap type changes, hybrid imagery loading), at which point
   the attribution suddenly reads "Map Data Map data ©2026 AfriGIS (Pty) Ltd".
   Pin the toggle button shut with !important so the duplicated label can never
   appear regardless of what state Google's runtime puts the element into. */
.property-search-map-container .gm-style-cc button[aria-label="Map Data"] {
    display: none !important;
}

/* Google Maps fullscreen + zoom-in + zoom-out control buttons each contain a
   stack of <img> SVG state icons (default-grey #666, hover #333, active #111,
   and on the zoom buttons a fourth disabled-grey #d1d1d1). Google's runtime
   normally injects an inline <style> tag positioning these absolutely so they
   overlap, then toggles opacity on hover / active / disabled. With our vector
   mapId-based map that runtime <style> sometimes never gets injected (or gets
   wiped during a map reload), so the state imgs render side-by-side in normal
   inline flow inside the 40×40 button - giving the "doubled / garbled icon"
   look the user has been screenshotting (3-4 copies of the same SVG smudged
   on top of each other inside the button).
   We don't size or move the buttons themselves (memory rule: never style the
   .gm-bundled-control / .gm-fullscreen-control wrappers - they collapse to 0×0).
   We only position the inner <img> children so the state-stack overlaps the
   way Google intends. The first image is the default state and stays visible;
   subsequent images are kept in the layout but transparent until hover/active. */
.property-search-map-container .gm-fullscreen-control img,
.property-search-map-container .gm-control-active img {
    position: absolute !important;
    left: 50% !important;
    top: 50% !important;
    transform: translate(-50%, -50%) !important;
}
.property-search-map-container .gm-fullscreen-control img:nth-child(n+2),
.property-search-map-container .gm-control-active img:nth-child(n+2) {
    opacity: 0;
}
.property-search-map-container .gm-fullscreen-control:hover img:nth-child(2),
.property-search-map-container .gm-control-active:hover img:nth-child(2) {
    opacity: 1;
}
.property-search-map-container .gm-fullscreen-control:active img:nth-child(3),
.property-search-map-container .gm-control-active:active img:nth-child(3) {
    opacity: 1;
}

/* Map marker info window custom styles */
.property-map-popup {
    font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
}

.property-map-popup img {
    border-radius: 4px;
}

.property-map-popup a {
    text-decoration: none;
    transition: background-color 0.2s;
}

.property-map-popup a:hover {
    background-color: var(--color-primary-hover);
}

/* Save search button - uses .button-rounded from utilities.css */
.save-search-button {
    /* Inherits from .button-rounded in utilities.css */
}

/* Property card hover - uses .property-card-hover from utilities.css */
.property-result-card {
    /* Inherits from .property-card-hover in utilities.css */
}

/* Filter badge - uses .filter-badge from utilities.css */
.filter-badge {
    /* Inherits from .filter-badge in utilities.css */
}

/* Zillow-like property type icons */
.property-type-icon {
    width: 20px;
    height: 20px;
    display: inline-block;
    vertical-align: middle;
    margin-right: 6px;
}

/* Hide scroll-to-top button completely on property search page */
body:has(.property-search-container) .mud-scroll-to-top,
.property-search-container .mud-scroll-to-top,
.property-search-split .mud-scroll-to-top,
.property-search-results .mud-scroll-to-top,
.property-search-results-footer .mud-scroll-to-top {
    display: none !important;
    visibility: hidden !important;
    opacity: 0 !important;
    pointer-events: none !important;
}

/* ──────────────────────────────────────────────────────────────────────
   First-load map skeleton overlay
   Sits on top of the Google Maps container until the JS init handshake
   completes. The wrapper gets `is-map-ready` from the Razor component,
   which fades the overlay out and disables pointer events.
   ────────────────────────────────────────────────────────────────────── */
.property-search-map-loading {
    position: absolute;
    inset: 0;
    z-index: 5;
    display: flex;
    align-items: center;
    justify-content: center;
    background: linear-gradient(135deg, #eef3ee 0%, #e3ece4 60%, #dde7df 100%);
    overflow: hidden;
    transition: opacity 480ms ease, visibility 0s linear 480ms;
    opacity: 1;
    visibility: visible;
}

.property-search-map-wrapper.is-map-ready .property-search-map-loading {
    opacity: 0;
    visibility: hidden;
    pointer-events: none;
}

/* Subtle map-style grid pattern that drifts to suggest "loading tiles" */
.property-search-map-loading__grid {
    position: absolute;
    inset: -10%;
    background-image:
        linear-gradient(rgba(47, 93, 80, 0.06) 1px, transparent 1px),
        linear-gradient(90deg, rgba(47, 93, 80, 0.06) 1px, transparent 1px),
        radial-gradient(circle at 30% 40%, rgba(47, 93, 80, 0.05) 0, transparent 60px),
        radial-gradient(circle at 70% 60%, rgba(47, 93, 80, 0.04) 0, transparent 80px);
    background-size: 40px 40px, 40px 40px, auto, auto;
    animation: psm-loading-drift 14s ease-in-out infinite alternate;
}

.property-search-map-loading__center {
    position: relative;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 14px;
    z-index: 1;
}

.property-search-map-loading__pin {
    position: relative;
    width: 56px;
    height: 56px;
    display: flex;
    align-items: center;
    justify-content: center;
    animation: psm-loading-bounce 1.6s cubic-bezier(.4, 0, .2, 1) infinite;
}

.property-search-map-loading__pin-icon {
    width: 44px;
    height: 44px;
    fill: #2F5D50;
    filter: drop-shadow(0 4px 10px rgba(47, 93, 80, 0.35));
    z-index: 1;
}

.property-search-map-loading__pulse {
    position: absolute;
    bottom: 4px;
    left: 50%;
    width: 28px;
    height: 28px;
    transform: translateX(-50%);
    border-radius: 50%;
    background: rgba(47, 93, 80, 0.25);
    animation: psm-loading-pulse 1.6s ease-out infinite;
}

.property-search-map-loading__label {
    font-family: 'Inter', system-ui, -apple-system, sans-serif;
    font-size: 14px;
    font-weight: 500;
    letter-spacing: 0.01em;
    color: #2F5D50;
    display: inline-flex;
    align-items: center;
}

.property-search-map-loading__dots {
    display: inline-flex;
    margin-left: 2px;
}

.property-search-map-loading__dots span {
    width: 4px;
    height: 4px;
    margin-left: 3px;
    border-radius: 50%;
    background: #2F5D50;
    opacity: 0.4;
    animation: psm-loading-dot 1.4s ease-in-out infinite;
}

.property-search-map-loading__dots span:nth-child(2) { animation-delay: 0.2s; }
.property-search-map-loading__dots span:nth-child(3) { animation-delay: 0.4s; }

@keyframes psm-loading-bounce {
    0%, 100% { transform: translateY(0); }
    50%      { transform: translateY(-8px); }
}

@keyframes psm-loading-pulse {
    0%   { transform: translate(-50%, 0) scale(0.6); opacity: 0.5; }
    80%  { transform: translate(-50%, 0) scale(1.6); opacity: 0; }
    100% { transform: translate(-50%, 0) scale(1.6); opacity: 0; }
}

@keyframes psm-loading-dot {
    0%, 80%, 100% { opacity: 0.25; transform: translateY(0); }
    40%           { opacity: 1;    transform: translateY(-2px); }
}

@keyframes psm-loading-drift {
    0%   { transform: translate(0, 0); }
    100% { transform: translate(-20px, -10px); }
}

/* Respect reduced-motion preferences - keep the overlay visible but stop animating. */
@media (prefers-reduced-motion: reduce) {
    .property-search-map-loading__pin,
    .property-search-map-loading__pulse,
    .property-search-map-loading__dots span,
    .property-search-map-loading__grid {
        animation: none !important;
    }
}
