/* required styles */

.leaflet-pane,
.leaflet-tile,
.leaflet-marker-icon,
.leaflet-marker-shadow,
.leaflet-tile-container,
.leaflet-pane > svg,
.leaflet-pane > canvas,
.leaflet-zoom-box,
.leaflet-image-layer,
.leaflet-layer {
	position: absolute;
	left: 0;
	top: 0;
	}
.leaflet-container {
	overflow: hidden;
	}
.leaflet-tile,
.leaflet-marker-icon,
.leaflet-marker-shadow {
	-webkit-user-select: none;
	   -moz-user-select: none;
	        user-select: none;
	  -webkit-user-drag: none;
	}
/* Prevents IE11 from highlighting tiles in blue */
.leaflet-tile::selection {
	background: transparent;
}
/* Safari renders non-retina tile on retina better with this, but Chrome is worse */
.leaflet-safari .leaflet-tile {
	image-rendering: -webkit-optimize-contrast;
	}
/* hack that prevents hw layers "stretching" when loading new tiles */
.leaflet-safari .leaflet-tile-container {
	width: 1600px;
	height: 1600px;
	-webkit-transform-origin: 0 0;
	}
.leaflet-marker-icon,
.leaflet-marker-shadow {
	display: block;
	}
/* .leaflet-container svg: reset svg max-width decleration shipped in Joomla! (joomla.org) 3.x */
/* .leaflet-container img: map is broken in FF if you have max-width: 100% on tiles */
.leaflet-container .leaflet-overlay-pane svg {
	max-width: none !important;
	max-height: none !important;
	}
.leaflet-container .leaflet-marker-pane img,
.leaflet-container .leaflet-shadow-pane img,
.leaflet-container .leaflet-tile-pane img,
.leaflet-container img.leaflet-image-layer,
.leaflet-container .leaflet-tile {
	max-width: none !important;
	max-height: none !important;
	width: auto;
	padding: 0;
	}

.leaflet-container img.leaflet-tile {
	/* See: https://bugs.chromium.org/p/chromium/issues/detail?id=600120 */
	mix-blend-mode: plus-lighter;
}

.leaflet-container.leaflet-touch-zoom {
	-ms-touch-action: pan-x pan-y;
	touch-action: pan-x pan-y;
	}
.leaflet-container.leaflet-touch-drag {
	-ms-touch-action: pinch-zoom;
	/* Fallback for FF which doesn't support pinch-zoom */
	touch-action: none;
	touch-action: pinch-zoom;
}
.leaflet-container.leaflet-touch-drag.leaflet-touch-zoom {
	-ms-touch-action: none;
	touch-action: none;
}
.leaflet-container {
	-webkit-tap-highlight-color: transparent;
}
.leaflet-container a {
	-webkit-tap-highlight-color: rgba(51, 181, 229, 0.4);
}
.leaflet-tile {
	filter: inherit;
	visibility: hidden;
	}
.leaflet-tile-loaded {
	visibility: inherit;
	}
.leaflet-zoom-box {
	width: 0;
	height: 0;
	-moz-box-sizing: border-box;
	     box-sizing: border-box;
	z-index: 800;
	}
/* workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=888319 */
.leaflet-overlay-pane svg {
	-moz-user-select: none;
	}

.leaflet-pane         { z-index: 400; }

.leaflet-tile-pane    { z-index: 200; }
.leaflet-overlay-pane { z-index: 400; }
.leaflet-shadow-pane  { z-index: 500; }
.leaflet-marker-pane  { z-index: 600; }
.leaflet-tooltip-pane   { z-index: 650; }
.leaflet-popup-pane   { z-index: 700; }

.leaflet-map-pane canvas { z-index: 100; }
.leaflet-map-pane svg    { z-index: 200; }

.leaflet-vml-shape {
	width: 1px;
	height: 1px;
	}
.lvml {
	behavior: url(#default#VML);
	display: inline-block;
	position: absolute;
	}


/* control positioning */

.leaflet-control {
	position: relative;
	z-index: 800;
	pointer-events: visiblePainted; /* IE 9-10 doesn't have auto */
	pointer-events: auto;
	}
.leaflet-top,
.leaflet-bottom {
	position: absolute;
	z-index: 1000;
	pointer-events: none;
	}
.leaflet-top {
	top: 0;
	}
.leaflet-right {
	right: 0;
	}
.leaflet-bottom {
	bottom: 0;
	}
.leaflet-left {
	left: 0;
	}
.leaflet-control {
	float: left;
	clear: both;
	}
.leaflet-right .leaflet-control {
	float: right;
	}
.leaflet-top .leaflet-control {
	margin-top: 10px;
	}
.leaflet-bottom .leaflet-control {
	margin-bottom: 10px;
	}
.leaflet-left .leaflet-control {
	margin-left: 10px;
	}
.leaflet-right .leaflet-control {
	margin-right: 10px;
	}


/* zoom and fade animations */

.leaflet-fade-anim .leaflet-popup {
	opacity: 0;
	-webkit-transition: opacity 0.2s linear;
	   -moz-transition: opacity 0.2s linear;
	        transition: opacity 0.2s linear;
	}
.leaflet-fade-anim .leaflet-map-pane .leaflet-popup {
	opacity: 1;
	}
.leaflet-zoom-animated {
	-webkit-transform-origin: 0 0;
	    -ms-transform-origin: 0 0;
	        transform-origin: 0 0;
	}
svg.leaflet-zoom-animated {
	will-change: transform;
}

.leaflet-zoom-anim .leaflet-zoom-animated {
	-webkit-transition: -webkit-transform 0.25s cubic-bezier(0,0,0.25,1);
	   -moz-transition:    -moz-transform 0.25s cubic-bezier(0,0,0.25,1);
	        transition:         transform 0.25s cubic-bezier(0,0,0.25,1);
	}
.leaflet-zoom-anim .leaflet-tile,
.leaflet-pan-anim .leaflet-tile {
	-webkit-transition: none;
	   -moz-transition: none;
	        transition: none;
	}

.leaflet-zoom-anim .leaflet-zoom-hide {
	visibility: hidden;
	}


/* cursors */

.leaflet-interactive {
	cursor: pointer;
	}
.leaflet-grab {
	cursor: -webkit-grab;
	cursor:    -moz-grab;
	cursor:         grab;
	}
.leaflet-crosshair,
.leaflet-crosshair .leaflet-interactive {
	cursor: crosshair;
	}
.leaflet-popup-pane,
.leaflet-control {
	cursor: auto;
	}
.leaflet-dragging .leaflet-grab,
.leaflet-dragging .leaflet-grab .leaflet-interactive,
.leaflet-dragging .leaflet-marker-draggable {
	cursor: move;
	cursor: -webkit-grabbing;
	cursor:    -moz-grabbing;
	cursor:         grabbing;
	}

/* marker & overlays interactivity */
.leaflet-marker-icon,
.leaflet-marker-shadow,
.leaflet-image-layer,
.leaflet-pane > svg path,
.leaflet-tile-container {
	pointer-events: none;
	}

.leaflet-marker-icon.leaflet-interactive,
.leaflet-image-layer.leaflet-interactive,
.leaflet-pane > svg path.leaflet-interactive,
svg.leaflet-image-layer.leaflet-interactive path {
	pointer-events: visiblePainted; /* IE 9-10 doesn't have auto */
	pointer-events: auto;
	}

/* visual tweaks */

.leaflet-container {
	background: #ddd;
	outline-offset: 1px;
	}
.leaflet-container a {
	color: #0078A8;
	}
.leaflet-zoom-box {
	border: 2px dotted #38f;
	background: rgba(255,255,255,0.5);
	}


/* general typography */
.leaflet-container {
	font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;
	font-size: 12px;
	font-size: 0.75rem;
	line-height: 1.5;
	}


/* general toolbar styles */

.leaflet-bar {
	box-shadow: 0 1px 5px rgba(0,0,0,0.65);
	border-radius: 4px;
	}
.leaflet-bar a {
	background-color: #fff;
	border-bottom: 1px solid #ccc;
	width: 26px;
	height: 26px;
	line-height: 26px;
	display: block;
	text-align: center;
	text-decoration: none;
	color: black;
	}
.leaflet-bar a,
.leaflet-control-layers-toggle {
	background-position: 50% 50%;
	background-repeat: no-repeat;
	display: block;
	}
.leaflet-bar a:hover,
.leaflet-bar a:focus {
	background-color: #f4f4f4;
	}
.leaflet-bar a:first-child {
	border-top-left-radius: 4px;
	border-top-right-radius: 4px;
	}
.leaflet-bar a:last-child {
	border-bottom-left-radius: 4px;
	border-bottom-right-radius: 4px;
	border-bottom: none;
	}
.leaflet-bar a.leaflet-disabled {
	cursor: default;
	background-color: #f4f4f4;
	color: #bbb;
	}

.leaflet-touch .leaflet-bar a {
	width: 30px;
	height: 30px;
	line-height: 30px;
	}
.leaflet-touch .leaflet-bar a:first-child {
	border-top-left-radius: 2px;
	border-top-right-radius: 2px;
	}
.leaflet-touch .leaflet-bar a:last-child {
	border-bottom-left-radius: 2px;
	border-bottom-right-radius: 2px;
	}

/* zoom control */

.leaflet-control-zoom-in,
.leaflet-control-zoom-out {
	font: bold 18px 'Lucida Console', Monaco, monospace;
	text-indent: 1px;
	}

.leaflet-touch .leaflet-control-zoom-in, .leaflet-touch .leaflet-control-zoom-out  {
	font-size: 22px;
	}


/* layers control */

.leaflet-control-layers {
	box-shadow: 0 1px 5px rgba(0,0,0,0.4);
	background: #fff;
	border-radius: 5px;
	}
.leaflet-control-layers-toggle {
	background-image: url(images/layers.png);
	width: 36px;
	height: 36px;
	}
.leaflet-retina .leaflet-control-layers-toggle {
	background-image: url(images/layers-2x.png);
	background-size: 26px 26px;
	}
.leaflet-touch .leaflet-control-layers-toggle {
	width: 44px;
	height: 44px;
	}
.leaflet-control-layers .leaflet-control-layers-list,
.leaflet-control-layers-expanded .leaflet-control-layers-toggle {
	display: none;
	}
.leaflet-control-layers-expanded .leaflet-control-layers-list {
	display: block;
	position: relative;
	}
.leaflet-control-layers-expanded {
	padding: 6px 10px 6px 6px;
	color: #333;
	background: #fff;
	}
.leaflet-control-layers-scrollbar {
	overflow-y: scroll;
	overflow-x: hidden;
	padding-right: 5px;
	}
.leaflet-control-layers-selector {
	margin-top: 2px;
	position: relative;
	top: 1px;
	}
.leaflet-control-layers label {
	display: block;
	font-size: 13px;
	font-size: 1.08333em;
	}
.leaflet-control-layers-separator {
	height: 0;
	border-top: 1px solid #ddd;
	margin: 5px -10px 5px -6px;
	}

/* Default icon URLs */
.leaflet-default-icon-path { /* used only in path-guessing heuristic, see L.Icon.Default */
	background-image: url(images/marker-icon.png);
	}


/* attribution and scale controls */

.leaflet-container .leaflet-control-attribution {
	background: #fff;
	background: rgba(255, 255, 255, 0.8);
	margin: 0;
	}
.leaflet-control-attribution,
.leaflet-control-scale-line {
	padding: 0 5px;
	color: #333;
	line-height: 1.4;
	}
.leaflet-control-attribution a {
	text-decoration: none;
	}
.leaflet-control-attribution a:hover,
.leaflet-control-attribution a:focus {
	text-decoration: underline;
	}
.leaflet-attribution-flag {
	display: inline !important;
	vertical-align: baseline !important;
	width: 1em;
	height: 0.6669em;
	}
.leaflet-left .leaflet-control-scale {
	margin-left: 5px;
	}
.leaflet-bottom .leaflet-control-scale {
	margin-bottom: 5px;
	}
.leaflet-control-scale-line {
	border: 2px solid #777;
	border-top: none;
	line-height: 1.1;
	padding: 2px 5px 1px;
	white-space: nowrap;
	-moz-box-sizing: border-box;
	     box-sizing: border-box;
	background: rgba(255, 255, 255, 0.8);
	text-shadow: 1px 1px #fff;
	}
.leaflet-control-scale-line:not(:first-child) {
	border-top: 2px solid #777;
	border-bottom: none;
	margin-top: -2px;
	}
.leaflet-control-scale-line:not(:first-child):not(:last-child) {
	border-bottom: 2px solid #777;
	}

.leaflet-touch .leaflet-control-attribution,
.leaflet-touch .leaflet-control-layers,
.leaflet-touch .leaflet-bar {
	box-shadow: none;
	}
.leaflet-touch .leaflet-control-layers,
.leaflet-touch .leaflet-bar {
	border: 2px solid rgba(0,0,0,0.2);
	background-clip: padding-box;
	}


/* popup */

.leaflet-popup {
	position: absolute;
	text-align: center;
	margin-bottom: 20px;
	}
.leaflet-popup-content-wrapper {
	padding: 1px;
	text-align: left;
	border-radius: 12px;
	}
.leaflet-popup-content {
	margin: 13px 24px 13px 20px;
	line-height: 1.3;
	font-size: 13px;
	font-size: 1.08333em;
	min-height: 1px;
	}
.leaflet-popup-content p {
	margin: 17px 0;
	margin: 1.3em 0;
	}
.leaflet-popup-tip-container {
	width: 40px;
	height: 20px;
	position: absolute;
	left: 50%;
	margin-top: -1px;
	margin-left: -20px;
	overflow: hidden;
	pointer-events: none;
	}
.leaflet-popup-tip {
	width: 17px;
	height: 17px;
	padding: 1px;

	margin: -10px auto 0;
	pointer-events: auto;

	-webkit-transform: rotate(45deg);
	   -moz-transform: rotate(45deg);
	    -ms-transform: rotate(45deg);
	        transform: rotate(45deg);
	}
.leaflet-popup-content-wrapper,
.leaflet-popup-tip {
	background: white;
	color: #333;
	box-shadow: 0 3px 14px rgba(0,0,0,0.4);
	}
.leaflet-container a.leaflet-popup-close-button {
	position: absolute;
	top: 0;
	right: 0;
	border: none;
	text-align: center;
	width: 24px;
	height: 24px;
	font: 16px/24px Tahoma, Verdana, sans-serif;
	color: #757575;
	text-decoration: none;
	background: transparent;
	}
.leaflet-container a.leaflet-popup-close-button:hover,
.leaflet-container a.leaflet-popup-close-button:focus {
	color: #585858;
	}
.leaflet-popup-scrolled {
	overflow: auto;
	}

.leaflet-oldie .leaflet-popup-content-wrapper {
	-ms-zoom: 1;
	}
.leaflet-oldie .leaflet-popup-tip {
	width: 24px;
	margin: 0 auto;

	-ms-filter: "progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678)";
	filter: progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678);
	}

.leaflet-oldie .leaflet-control-zoom,
.leaflet-oldie .leaflet-control-layers,
.leaflet-oldie .leaflet-popup-content-wrapper,
.leaflet-oldie .leaflet-popup-tip {
	border: 1px solid #999;
	}


/* div icon */

.leaflet-div-icon {
	background: #fff;
	border: 1px solid #666;
	}


/* Tooltip */
/* Base styles for the element that has a tooltip */
.leaflet-tooltip {
	position: absolute;
	padding: 6px;
	background-color: #fff;
	border: 1px solid #fff;
	border-radius: 3px;
	color: #222;
	white-space: nowrap;
	-webkit-user-select: none;
	-moz-user-select: none;
	-ms-user-select: none;
	user-select: none;
	pointer-events: none;
	box-shadow: 0 1px 3px rgba(0,0,0,0.4);
	}
.leaflet-tooltip.leaflet-interactive {
	cursor: pointer;
	pointer-events: auto;
	}
.leaflet-tooltip-top:before,
.leaflet-tooltip-bottom:before,
.leaflet-tooltip-left:before,
.leaflet-tooltip-right:before {
	position: absolute;
	pointer-events: none;
	border: 6px solid transparent;
	background: transparent;
	content: "";
	}

/* Directions */

.leaflet-tooltip-bottom {
	margin-top: 6px;
}
.leaflet-tooltip-top {
	margin-top: -6px;
}
.leaflet-tooltip-bottom:before,
.leaflet-tooltip-top:before {
	left: 50%;
	margin-left: -6px;
	}
.leaflet-tooltip-top:before {
	bottom: 0;
	margin-bottom: -12px;
	border-top-color: #fff;
	}
.leaflet-tooltip-bottom:before {
	top: 0;
	margin-top: -12px;
	margin-left: -6px;
	border-bottom-color: #fff;
	}
.leaflet-tooltip-left {
	margin-left: -6px;
}
.leaflet-tooltip-right {
	margin-left: 6px;
}
.leaflet-tooltip-left:before,
.leaflet-tooltip-right:before {
	top: 50%;
	margin-top: -6px;
	}
.leaflet-tooltip-left:before {
	right: 0;
	margin-right: -12px;
	border-left-color: #fff;
	}
.leaflet-tooltip-right:before {
	left: 0;
	margin-left: -12px;
	border-right-color: #fff;
	}

/* Printing */

@media print {
	/* Prevent printers from removing background-images of controls. */
	.leaflet-control {
		-webkit-print-color-adjust: exact;
		print-color-adjust: exact;
		}
	}

/* ==========================================================================
   property-map-view feature UI — near-verbatim port of
   ~/projects/ss/prototype/mapview's site/styles.css. Generic, theme-agnostic
   styling for the markup a consumer's PropertyMapViewSlice supplies (see
   real-estate/slices/PropertyMapViewSlice/index.ts) and the markup
   property-map-view-init.ts builds dynamically inside it (popup cards,
   drawer cards, detail modal body, filter option lists). Shared by every
   consumer of theme-maker, so element-level resets (box-sizing, button/
   select/svg defaults) and custom properties are scoped under
   `.js-property-map-root` rather than applied globally, to coexist with a
   consumer's own site-wide styling on every other page.
   ========================================================================== */

.js-property-map-root,
.js-property-map-root *,
.js-property-map-root *::before,
.js-property-map-root *::after {
	box-sizing: border-box;
}

.js-property-map-root {
	margin: 0;
	padding: 0;
	font-family: var(--fontFamily-sans);
	color: var(--pmv-color-text);
	line-height: var(--lineHeight-normal);

	/* Synced to the design system's tokens (design-system/*.ts, compiled to
	   CSS vars by token-compiler.ts and linked on every page by builder.ts)
	   rather than this feature's own arbitrary palette, so map-view tracks
	   the rest of the site. --pmv-color-primary specifically routes through
	   the semantic --brand-DEFAULT (not the primitive --color-red-500) so
	   branding-init.ts's runtime GET /branding override actually reaches the
	   map's active markers/primary buttons — see wello-template-api-mapping
	   backlog. --pmv-color-primary-dark stays on the static primitive shade
	   (same accepted v1 limitation as branding-init.ts not recomputing
	   derived shades from a single fetched color). */
	--pmv-color-primary: var(--brand-DEFAULT);
	--pmv-color-primary-dark: var(--color-red-600);
	--pmv-color-accent: var(--color-orange-500);
	--pmv-color-accent-dark: var(--color-orange-600);
	--pmv-color-dark: var(--color-gray-900);
	--pmv-color-text: var(--color-gray-700);
	--pmv-color-muted: var(--color-gray-500);
	--pmv-color-subtle: var(--color-gray-400);
	--pmv-color-border: var(--color-gray-200);
	--pmv-color-surface-hover: var(--color-gray-100);
	--pmv-color-surface-active: var(--color-gray-200);
	--pmv-color-white: var(--color-white);
	--pmv-color-house: var(--color-blue-500);
	--pmv-color-condo: var(--color-amber-500);
	--pmv-color-commercial: var(--color-purple-500);
	--pmv-color-commercial-dark: var(--color-purple-600);
	--pmv-color-land: var(--color-green-500);
	--pmv-color-rent: var(--color-green-600);
	--pmv-radius: var(--radius-lg);
	--pmv-shadow: var(--shadow-xl);
	--pmv-transition: var(--duration-slow) var(--easing-ease);
	--pmv-footer-height: 52px;
}

/* html/body normally scroll the page; this feature is a full-viewport fixed
   map instead, so both are pinned only while it's present on the page. */
body:has(.js-property-map-root) {
	height: 100%;
	overflow: hidden;
}
html:has(body .js-property-map-root) {
	height: 100%;
	overflow: hidden;
}

.js-property-map-root button,
.js-property-map-root select,
.js-property-map-root input,
.js-property-map-root textarea {
	font-family: inherit;
}
/* :where() keeps this reset's specificity at (0,1,0) — same as a lone
   `.foo { background: ... }` component rule — instead of (0,1,1) for
   `.js-property-map-root button`. Every component button rule below (e.g.
   .round-btn, .pp-view-detail-btn, .filter-apply-btn) is a single class
   selector declared later in this file, so without :where() this reset's
   higher specificity silently won the `background`/`border` properties
   over all of them regardless of source order — visible only as "no
   background until :hover" (:hover pushes the component rule's specificity
   to (0,2,0), which does win), since :hover is the only state where the
   component rule's specificity overtakes the reset's. */
.js-property-map-root :where(button) {
	cursor: pointer;
	border: none;
	background: none;
}
.js-property-map-root svg {
	display: block;
}

/* ---------- Custom scrollbars ----------
   Applied to every scrollable panel/strip in the UI: the listing drawer and
   detail modal (vertical), and the popup/detail-modal thumbnail strip
   (horizontal). Thin, track-less, and a little more visible on hover/while
   scrolling rather than the bulky OS-default bar. (The unit-listings row is
   a separate arrow/swipe-driven carousel, so it hides its scrollbar
   entirely.) */
.drawer-list,
.js-detail-modal,
.pp-units-row,
.detail-thumb-row {
	scrollbar-width: thin;
	scrollbar-color: rgba(27, 36, 48, 0.2) transparent;
	-webkit-overflow-scrolling: touch;
}
.drawer-list::-webkit-scrollbar,
.js-detail-modal::-webkit-scrollbar,
.pp-units-row::-webkit-scrollbar,
.detail-thumb-row::-webkit-scrollbar {
	width: 3px;
	height: 3px;
}
.drawer-list::-webkit-scrollbar-track,
.js-detail-modal::-webkit-scrollbar-track,
.pp-units-row::-webkit-scrollbar-track,
.detail-thumb-row::-webkit-scrollbar-track {
	background: transparent;
}
.drawer-list::-webkit-scrollbar-thumb,
.js-detail-modal::-webkit-scrollbar-thumb,
.pp-units-row::-webkit-scrollbar-thumb,
.detail-thumb-row::-webkit-scrollbar-thumb {
	background-color: rgba(27, 36, 48, 0.18);
	border-radius: var(--radius-md);
}
.drawer-list:hover::-webkit-scrollbar-thumb,
.js-detail-modal:hover::-webkit-scrollbar-thumb,
.pp-units-row:hover::-webkit-scrollbar-thumb,
.detail-thumb-row:hover::-webkit-scrollbar-thumb {
	background-color: rgba(27, 36, 48, 0.32);
}

/* ---------- Map ---------- */
.js-property-map {
	position: fixed;
	inset: 0;
	z-index: var(--zIndex-base);
	background: var(--pmv-color-border);
}

/* ---------- Loading indicator (GET /properties in flight) ---------- */
/* Shown during the initial load and any area-change refetch — see
   property-map-view-init.ts's setLoading(). Reuses .df-carousel-arrow's
   existing .disabled treatment (opacity + pointer-events: none) for the
   location/type/filter controls it disables meanwhile, rather than
   introducing a second disabled-state convention. */
.map-loading {
	position: fixed;
	top: 18px;
	left: 50%;
	transform: translateX(-50%);
	z-index: var(--zIndex-modal);
	background: var(--pmv-color-white);
	color: var(--pmv-color-dark);
	padding: 8px 18px;
	border-radius: var(--radius-full);
	box-shadow: var(--pmv-shadow);
	font-weight: var(--fontWeight-semibold);
	font-size: var(--fontSize-sm);
	animation: pmv-loading-pulse 1.2s ease-in-out infinite;
}
@keyframes pmv-loading-pulse {
	0%, 100% { opacity: 1; }
	50% { opacity: var(--opacity-muted); }
}
.js-location-trigger.disabled,
.filter-icons-scroll [data-filter].disabled,
.js-adv-filter-btn.disabled {
	opacity: var(--opacity-muted);
	pointer-events: none;
}

/* ---------- Floating UI layer ---------- */
.map-ui {
	position: fixed;
	inset: 0;
	z-index: var(--zIndex-modal);
	pointer-events: none;
}
.map-ui > * { pointer-events: auto; }

/* ---------- Top left cluster ---------- */
.ui-top-left {
	position: absolute;
	top: 18px; left: 18px;
	display: flex;
	flex-direction: column;
	gap: 12px;
	max-width: 92vw;
}

.brand-badge {
	display: inline-flex;
	align-items: center;
	gap: 8px;
	background: var(--pmv-color-white);
	padding: 10px 16px;
	border-radius: var(--radius-full);
	box-shadow: var(--pmv-shadow);
	font-weight: var(--fontWeight-bold);
	font-size: var(--fontSize-md);
	color: var(--pmv-color-dark);
	width: fit-content;
}
/* [hidden] variants come first and must stay `display: none` — a same-specificity `display`
   declared below them would out-specificity the UA [hidden] rule otherwise (see
   real-estate/layout/style.ts's modalOverlay comment for the same gotcha). branding-init.ts toggles
   .js-brand-icon/.js-brand-logo's `hidden` attribute directly, not a class, so these must react
   to it. */
.brand-icon[hidden] { display: none; }
.brand-icon { color: var(--pmv-color-primary); display: flex; }
/* Shown instead of .brand-icon once branding-init.ts's GET /branding fetch resolves a real
   logo — see PropertyMapViewSlice/index.ts's .js-brand-logo hook. Height-capped, not
   width-capped, since logo aspect ratios vary and this badge should grow to fit a wide logo
   rather than squash it. */
.brand-logo[hidden] { display: none; }
.brand-logo { display: block; height: 32px; width: auto; }
/* Shown while GET /branding is in flight, in place of the icon+name fallback (which now both
   start `hidden` — see PropertyMapViewSlice/index.ts) — reuses .map-loading's pulse keyframe
   rather than defining a second one. Height matches .brand-logo's so the badge doesn't visibly
   resize once the real logo/name swaps in. */
.brand-skeleton[hidden] { display: none; }
.brand-skeleton {
	display: block;
	width: 72px;
	height: 32px;
	border-radius: var(--radius-sm);
	background: var(--pmv-color-border);
	animation: pmv-loading-pulse 1.2s ease-in-out infinite;
}

.filter-icons {
	position: relative; /* containing block for .filter-megamenu */
	display: flex;
	align-items: center;
	gap: 10px;
	min-width: 0; /* let it shrink inside .footer-right's flex row */
}
.filter-icons-scroll {
	display: flex;
	align-items: center;
	gap: 10px;
	flex: 1;
	flex-wrap: nowrap;
	overflow-x: auto;
	min-width: 0;
	scrollbar-width: none; /* only scrolls on very narrow screens; a visible
	                          scrollbar would just clutter the compact footer */
}
.filter-icons-scroll::-webkit-scrollbar { display: none; }
/* Flat footer-menu icons, styled like .footer-menu-btn — no shadow, no
   floating pill/circle shape, blends into the footer until hovered/active. */
.icon-btn {
	width: 38px; height: 38px;
	border-radius: var(--radius-md);
	background: transparent;
	display: flex;
	align-items: center;
	justify-content: center;
	color: var(--pmv-color-dark);
	transition: var(--pmv-transition);
	flex-shrink: 0;
}
.icon-btn:hover { background: var(--pmv-color-surface-hover); }
.icon-btn.active {
	background: var(--pmv-color-primary);
	color: var(--pmv-color-white);
}
.icon-btn.toggle.on {
	background: var(--pmv-color-accent);
	color: var(--pmv-color-white);
}
/* Icon + text label, e.g. the "Filters" button — auto width instead of the
   fixed 38px square. */
.icon-btn.labeled {
	width: auto;
	gap: 6px;
	padding: 0 12px;
	font-weight: var(--fontWeight-semibold);
	font-size: var(--fontSize-sm);
}

/* Separates the Filters button from the type/toggle icons that follow it. */
.footer-divider {
	width: 1px;
	height: 24px;
	background: var(--pmv-color-border);
	flex-shrink: 0;
}

/* Positioning wrapper for the location trigger + its popover — stays
   `position: relative` with no overflow of its own so .location-popover,
   nested inside, is never clipped. */
.location-select {
	position: relative;
}

.location-popover {
	width: 160px;
	gap: 2px;
}
.location-option {
	display: block;
	width: 100%;
	text-align: left;
	padding: 9px 10px;
	border-radius: var(--radius-md);
	font-size: var(--fontSize-sm);
	font-weight: var(--fontWeight-medium);
	color: var(--pmv-color-text);
	transition: var(--pmv-transition);
}
.location-option:hover { background: var(--pmv-color-surface-hover); }
.location-option.active { background: var(--pmv-color-primary); color: var(--pmv-color-white); font-weight: var(--fontWeight-semibold); }

/* Shared look for every floating footer dropdown panel — currently only
   used by .location-popover. Floats above the row (rather than flowing
   inline) so opening it doesn't disturb the footer's fixed height, and
   opens upward since it lives at the bottom of the screen. */
.footer-popover {
	display: none;
	position: absolute;
	bottom: calc(100% + 10px);
	left: 0;
	flex-direction: column;
	background: var(--pmv-color-white);
	padding: 10px;
	border-radius: var(--pmv-radius);
	box-shadow: var(--pmv-shadow);
	z-index: 5;
}
.footer-popover.open { display: flex; }

/* ---------- Top right cluster ---------- */
.ui-top-right {
	position: absolute;
	top: 18px; right: 18px;
	display: flex;
	flex-direction: column;
	gap: 10px;
}
.round-btn {
	width: 44px; height: 44px;
	border-radius: var(--radius-full);
	background: var(--pmv-color-white);
	box-shadow: var(--pmv-shadow);
	display: flex;
	align-items: center;
	justify-content: center;
	color: var(--pmv-color-dark);
	transition: var(--pmv-transition);
}
.round-btn:hover { transform: translateY(-2px); }

/* A flat menu item integrated into the footer bar itself — not a floating
   pill: no shadow, no rounded-pill shape, blends into the white footer
   until hovered. */
.footer-menu-btn {
	display: flex;
	align-items: center;
	gap: 6px;
	height: 100%;
	padding: 0 14px;
	border-radius: var(--radius-md);
	font-weight: var(--fontWeight-semibold);
	font-size: var(--fontSize-sm);
	color: var(--pmv-color-dark);
	transition: var(--pmv-transition);
}
.footer-menu-btn:hover { background: var(--pmv-color-surface-hover); }
.pill-count {
	background: var(--pmv-color-primary);
	color: var(--pmv-color-white);
	padding: 1px 8px;
	border-radius: var(--radius-full);
	font-size: var(--fontSize-xs);
}

/* ---------- Site footer ---------- */
/* Fixed to the bottom of the screen, above the map/floating UI (z-index 500)
   but below the drawers (900) and modals (1200) so those can still open
   over it. */
.site-footer {
	position: fixed;
	left: 0; right: 0; bottom: 0;
	height: var(--pmv-footer-height);
	background: var(--pmv-color-white);
	box-shadow: 0 -6px 20px rgba(20, 32, 45, 0.08);
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 12px;
	padding: 0 16px;
	z-index: var(--zIndex-popover);
}
/* Groups the location dropdown with the filter menu on the right side of
   the footer, so they sit beside each other while .site-footer's
   space-between keeps this whole group apart from the Listings item on the
   left. */
.footer-right {
	display: flex;
	align-items: center;
	gap: 10px;
	min-width: 0;
}

/* ---------- Zoom controls ---------- */
.zoom-controls {
	position: absolute;
	bottom: calc(var(--pmv-footer-height) + 18px); right: 18px;
	display: flex;
	flex-direction: column;
	background: var(--pmv-color-white);
	border-radius: var(--radius-md);
	box-shadow: var(--pmv-shadow);
	overflow: hidden;
}
.zoom-controls button {
	width: 44px; height: 44px;
	font-size: var(--fontSize-xl);
	color: var(--pmv-color-dark);
	transition: var(--pmv-transition);
}
.zoom-controls button:first-child { border-bottom: 1px solid var(--pmv-color-border); }
.zoom-controls button:hover { background: var(--pmv-color-surface-hover); }

/* Leaflet's own attribution control (bottom-right, inside the map) would
   otherwise sit underneath the opaque footer — keep it visible above it. */
.leaflet-bottom.leaflet-right { bottom: var(--pmv-footer-height) !important; }

/* ---------- Map markers ---------- */
.map-pin {
	width: 46px; height: 46px;
	border-radius: var(--radius-md);
	border: 3px solid var(--pmv-color-primary);
	background-color: var(--pmv-color-subtle);
	background-size: cover;
	background-position: center;
	position: relative;
	/* Heavy, high-opacity drop shadow deliberately distinct from the shared
	   --pmv-shadow (shadow-xl) card elevation — needed to lift a pin off
	   busy map tiles, no primitive shadow token matches this weight. */
	box-shadow: 0 4px 10px rgba(0,0,0,0.3);
	display: flex;
	align-items: center;
	justify-content: center;
	font-size: var(--fontSize-md);
	cursor: pointer;
	transition: transform var(--duration-normal) var(--easing-ease);
}
.map-pin::after {
	content: '';
	position: absolute;
	bottom: -9px; left: 50%;
	transform: translateX(-50%) rotate(45deg);
	width: 12px; height: 12px;
	background: var(--pmv-color-primary);
	border-radius: 0 0 3px 0;
}
.map-pin.type-house { border-color: var(--pmv-color-house); }
.map-pin.type-house::after { background: var(--pmv-color-house); }
.map-pin.type-condo { border-color: var(--pmv-color-condo); }
.map-pin.type-condo::after { background: var(--pmv-color-condo); }
.map-pin.type-commercial { border-color: var(--pmv-color-commercial); }
.map-pin.type-commercial::after { background: var(--pmv-color-commercial); }
.map-pin.type-land { border-color: var(--pmv-color-land); }
.map-pin.type-land::after { background: var(--pmv-color-land); }
.map-pin.active { transform: scale(1.18); z-index: 10; }
.map-pin.favorited::before {
	content: '♥';
	position: absolute;
	top: -8px; right: -8px;
	background: var(--pmv-color-accent);
	color: var(--pmv-color-white);
	width: 18px; height: 18px;
	border-radius: var(--radius-full);
	font-size: var(--fontSize-xs);
	display: flex;
	align-items: center;
	justify-content: center;
}
.pin-ribbon-list {
	/* A unit can be listed for multiple actions at once — one ribbon per action, stacked. */
	position: absolute;
	top: -8px; left: -10px;
	display: flex;
	flex-direction: column;
	align-items: flex-start;
	gap: 3px;
}
.pin-ribbon {
	/* "sale" is the default/base ribbon (no .sale modifier class rule needed
	   below, unlike .rent/.sold) — pinned directly to the red primitive
	   token (not --pmv-color-primary/"brand"), so it stays red regardless
	   of what the site's primary/brand color is set to. Distinct from
	   --pmv-color-accent (orange), reserved for favorites/highlights. */
	background: var(--color-red-500);
	color: var(--pmv-color-white);
	font-size: var(--fontSize-xs);
	font-weight: var(--fontWeight-bold);
	padding: 2px 6px;
	border-radius: var(--radius-xs);
	transform: rotate(-10deg);
	letter-spacing: var(--letterSpacing-wide);
	white-space: nowrap;
}
.pin-ribbon.rent { background: var(--pmv-color-rent); }
.pin-ribbon.sold { background: var(--pmv-color-muted); }

/* ---------- Property detail popup card ---------- */
.leaflet-popup.pp-popup .leaflet-popup-content-wrapper {
	padding: 0;
	border-radius: var(--radius-lg);
	overflow: hidden;
	box-shadow: var(--pmv-shadow);
}
.leaflet-popup.pp-popup .leaflet-popup-content { margin: 0; width: 380px !important; }
.leaflet-popup.pp-popup .leaflet-popup-tip-container { display: none; }
.leaflet-popup.pp-popup .leaflet-popup-close-button {
	top: 8px; right: 8px;
	width: 26px; height: 26px;
	background: rgba(255,255,255,0.9);
	border-radius: var(--radius-full);
	display: flex; align-items: center; justify-content: center;
	font-size: var(--fontSize-md);
	color: var(--pmv-color-dark);
}

.pp-card { display: flex; flex-direction: column; }

/* 1. Property image */
.pp-hero { position: relative; width: 100%; height: 200px; }
.pp-hero img { width: 100%; height: 100%; object-fit: cover; display: block; }
.pp-tag-list {
	/* A unit can be listed for multiple actions at once — one tag per action, side by side. */
	position: absolute;
	top: 10px; left: 10px;
	display: flex;
	flex-wrap: wrap;
	gap: 4px;
}
.pp-tag {
	/* "buy" is the default/base tag, same direct-to-red rationale as .dc-tag/.pin-ribbon. */
	background: var(--color-red-500);
	color: var(--pmv-color-white);
	font-size: var(--fontSize-xs);
	font-weight: var(--fontWeight-bold);
	padding: 3px 8px;
	border-radius: var(--radius-xs);
}
.pp-tag.rent { background: var(--pmv-color-rent); }
.pp-tag.sold { background: var(--pmv-color-muted); }

.pp-body { display: flex; flex-direction: column; gap: 8px; padding: 14px 16px 16px; }

/* 2. Posted time */
.pp-posted { display: flex; align-items: center; gap: 6px; font-size: var(--fontSize-xs); color: var(--pmv-color-muted); }

/* 3. Title */
.pp-title { font-size: var(--fontSize-md); font-weight: var(--fontWeight-bold); color: var(--pmv-color-dark); line-height: var(--lineHeight-snug); }

/* 4. Price */
.pp-price-row { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; }
.pp-price-original { font-size: var(--fontSize-sm); color: var(--pmv-color-subtle); text-decoration: line-through; }
.pp-price-current { font-size: var(--fontSize-md); font-weight: var(--fontWeight-bold); color: var(--pmv-color-dark); }

/* 5. Currency selector */
.pp-currency-row { display: flex; gap: 6px; align-items: center; }
.pp-currency-btn {
	min-width: 32px; height: 30px;
	padding: 0 8px;
	border-radius: var(--radius-md);
	background: var(--pmv-color-surface-hover);
	color: var(--pmv-color-text);
	font-size: var(--fontSize-sm);
	font-weight: var(--fontWeight-semibold);
	transition: var(--pmv-transition);
}
.pp-currency-btn:hover { background: var(--pmv-color-surface-active); }
.pp-currency-btn.active { background: var(--pmv-color-primary); color: var(--pmv-color-white); }
.pp-currency-more-wrap { position: relative; }
.pp-currency-menu {
	display: none;
	position: absolute;
	top: 36px; left: 0;
	background: var(--pmv-color-white);
	border-radius: var(--radius-md);
	box-shadow: var(--pmv-shadow);
	overflow: hidden;
	z-index: 20;
	min-width: 90px;
}
.pp-currency-menu.open { display: block; }
.pp-currency-menu button {
	display: block;
	width: 100%;
	text-align: left;
	padding: 8px 12px;
	font-size: var(--fontSize-sm);
	color: var(--pmv-color-text);
}
.pp-currency-menu button:hover { background: var(--pmv-color-surface-hover); }

/* 6. Unit type thumbnails */
.pp-units-label { font-size: var(--fontSize-sm); font-weight: var(--fontWeight-bold); color: var(--pmv-color-dark); text-transform: uppercase; letter-spacing: var(--letterSpacing-wide); margin-top: 2px; }
.pp-units-row {
	display: flex;
	gap: 8px;
	overflow-x: auto;
	padding-bottom: 4px;
}
.pp-unit-card {
	flex-shrink: 0;
	width: 62px;
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 4px;
	cursor: pointer;
}
.pp-unit-thumb {
	width: 62px; height: 48px;
	border-radius: var(--radius-md);
	background-size: cover;
	background-position: center;
	border: 2px solid transparent;
	transition: var(--pmv-transition);
}
.pp-unit-card.selected .pp-unit-thumb { border-color: var(--pmv-color-accent); }
.pp-unit-price { font-size: var(--fontSize-xs); font-weight: var(--fontWeight-bold); color: var(--pmv-color-text); }
.pp-unit-card.selected .pp-unit-price { color: var(--pmv-color-accent); }

/* Action icons row */
.pp-actions-row { display: flex; gap: 8px; margin-top: 2px; }
.pp-action-btn {
	flex: 1;
	height: 34px;
	border-radius: var(--radius-md);
	background: var(--pmv-color-surface-hover);
	display: flex; align-items: center; justify-content: center;
	color: var(--pmv-color-text);
	transition: var(--pmv-transition);
}
.pp-action-btn:hover { background: var(--pmv-color-surface-active); }
.pp-action-btn.on { background: var(--pmv-color-accent); color: var(--pmv-color-white); }

/* 7. View Detail button */
.pp-view-detail-btn {
	background: var(--pmv-color-primary);
	color: var(--pmv-color-white);
	padding: 11px;
	border-radius: var(--radius-md);
	font-weight: var(--fontWeight-semibold);
	font-size: var(--fontSize-sm);
	transition: var(--pmv-transition);
}
.pp-view-detail-btn:hover { background: var(--pmv-color-primary-dark); }

/* 8. Close button — only ever present when buildPopupCard() is used inside
   the mobile item drawer, never in the desktop Leaflet popup. */
.pp-close-btn {
	background: var(--pmv-color-surface-hover);
	color: var(--pmv-color-text);
	width: 100%;
	padding: 12px;
	border-radius: var(--radius-md);
	font-weight: var(--fontWeight-semibold);
	font-size: var(--fontSize-sm);
	transition: var(--pmv-transition);
}
.pp-close-btn:hover { background: var(--pmv-color-surface-active); }

/* ---------- Item drawer (mobile-only, full-screen-minus-footer for a
   tapped marker) ----------
   Desktop never shows this — the Leaflet popup (.pp-popup above) handles it
   there. Hidden entirely outside the mobile breakpoint since map markers
   only ever call openItemDrawer() when isMobileLayout() is true. */
.item-drawer { display: none; }
@media (max-width: 640px) {
	.item-drawer {
		display: flex;
		flex-direction: column;
		position: fixed;
		top: 0;
		left: 0;
		right: 0;
		/* Full screen except the footer, same convention as .listing-drawer/
		   .js-filter-megamenu, so the footer stays visible and reachable
		   underneath. */
		height: calc(100% - var(--pmv-footer-height));
		background: var(--pmv-color-white);
		border-radius: 0;
		box-shadow: 0 -6px 20px rgba(20, 32, 45, 0.16);
		z-index: 950;
		transform: translateY(110%);
		visibility: hidden;
		transition: transform var(--duration-slower) var(--easing-ease), visibility 0s linear var(--duration-slower);
	}
	.item-drawer.open {
		transform: translateY(0);
		visibility: visible;
		transition: transform var(--duration-slower) var(--easing-ease);
	}
	/* Real header row (not a floating overlay) so the close button takes up
	   its own space above the hero image instead of sitting on top of it. */
	.item-drawer-header {
		flex-shrink: 0;
		display: flex;
		justify-content: flex-end;
		padding: 10px 12px;
	}
	.item-drawer-body { flex: 1; overflow-y: auto; }
}

/* ---------- Property detail modal ---------- */
/* Double-class selector so this reliably beats .modal-box's max-width
   (420px) regardless of source order. No max-height/overflow here: the card
   grows to its full content height and the overlay itself scrolls instead —
   see below. */
.modal-box.detail-modal-box {
	max-width: 1000px;
	width: 96vw;
	padding: 0;
}
/* Sits directly above the card, same max-width so its close button lines up
   with the card's top-right corner. `position: sticky` (not fixed) keeps it
   pinned to the top of the modal's scrollport as the card scrolls under it,
   without detaching it from the card horizontally. */
.detail-modal-sticky-bar {
	position: sticky;
	top: 20px;
	width: 96vw;
	max-width: 1000px;
	height: 0;
	display: flex;
	justify-content: flex-end;
	z-index: 5;
}
.detail-modal-sticky-bar .modal-close {
	position: static;
	background: rgba(255,255,255,0.9);
	border-radius: var(--radius-full);
	width: 30px; height: 30px;
	display: flex; align-items: center; justify-content: center;
}

/* 1. Hero image + thumbnail strip */
.detail-hero-wrap { position: relative; }
.detail-hero { width: 100%; height: 340px; object-fit: cover; display: block; }
.detail-tag-list {
	/* A unit can be listed for multiple actions at once — one tag per action, side by side. */
	position: absolute;
	top: 14px; left: 14px;
	display: flex;
	flex-wrap: wrap;
	gap: 6px;
}
.detail-tag {
	background: var(--color-red-500);
	color: var(--pmv-color-white);
	font-size: var(--fontSize-sm);
	font-weight: var(--fontWeight-bold);
	padding: 4px 10px;
	border-radius: var(--radius-xs);
}
.detail-tag.rent { background: var(--pmv-color-rent); }
.detail-tag.sold { background: var(--pmv-color-muted); }
.detail-thumb-row {
	display: flex;
	gap: 6px;
	padding: 10px 26px;
	overflow-x: auto;
	background: var(--pmv-color-surface-hover);
}
.detail-thumb {
	flex-shrink: 0;
	width: 84px; height: 64px;
	border-radius: var(--radius-md);
	background-size: cover;
	background-position: center;
	border: 2px solid transparent;
	cursor: pointer;
	position: relative;
	transition: var(--pmv-transition);
}
.detail-thumb.active { border-color: var(--pmv-color-primary); }
.detail-thumb-more {
	position: absolute;
	inset: 0;
	background: rgba(15, 20, 28, 0.6);
	color: var(--pmv-color-white);
	font-weight: var(--fontWeight-bold);
	font-size: var(--fontSize-md);
	border-radius: var(--radius-sm);
	display: flex;
	align-items: center;
	justify-content: center;
}

.detail-content { padding: 22px 26px 26px; display: flex; flex-direction: column; }

/* 2. Posted time */
.detail-posted { display: flex; align-items: center; gap: 6px; font-size: var(--fontSize-sm); color: var(--pmv-color-muted); }

/* 3. Title + action icons */
.detail-head-row { display: flex; align-items: flex-start; justify-content: space-between; gap: 16px; margin-top: 6px; }
.detail-title { font-size: var(--fontSize-2xl); line-height: var(--lineHeight-snug); }
.detail-actions-row { flex-shrink: 0; margin-top: 0; }
.detail-actions-row .pp-action-btn { flex: none; width: 38px; }

/* 4. Price (full numbers) */
.detail-price { display: flex; align-items: baseline; gap: 10px; margin-top: 10px; flex-wrap: wrap; }
.detail-price .pp-price-original { font-size: var(--fontSize-base); }
.detail-price .pp-price-current { font-size: var(--fontSize-2xl); }

/* 5. Currency selector reuses .pp-currency-row */
.detail-content > .pp-currency-row { margin-top: 12px; }

.detail-content h4 { font-size: var(--fontSize-md); margin-top: 22px; margin-bottom: 8px; }
.detail-description { font-size: var(--fontSize-sm); color: var(--pmv-color-text); line-height: var(--lineHeight-relaxed); white-space: pre-line; }
.detail-see-more { display: inline-block; margin-top: 4px; font-size: var(--fontSize-sm); font-weight: var(--fontWeight-semibold); color: var(--pmv-color-primary); }
.detail-payment { font-size: var(--fontSize-sm); color: var(--pmv-color-subtle); line-height: var(--lineHeight-relaxed); white-space: pre-line; }

/* Download buttons */
.detail-download-row { display: flex; gap: 12px; margin-top: 22px; flex-wrap: wrap; }
.detail-download-btn {
	display: flex; align-items: center; gap: 8px;
	background: var(--pmv-color-primary);
	color: var(--pmv-color-white);
	padding: 11px 18px;
	border-radius: var(--radius-md);
	font-size: var(--fontSize-sm);
	font-weight: var(--fontWeight-semibold);
	transition: var(--pmv-transition);
}
.detail-download-btn:hover { background: var(--pmv-color-primary-dark); }
.detail-brochure-btn { background: var(--pmv-color-commercial); }
.detail-brochure-btn:hover { background: var(--pmv-color-commercial-dark); }

/* 6. Unit listings carousel ("N listings in this project") — shows exactly 2
   cards at a time on desktop (1 on mobile); the track is measured/
   positioned in JS (openDetailModal) and slid with a CSS transform, so
   paging is a real slide animation rather than a native scroll. */
.df-units-carousel { position: relative; }
.df-units-viewport { overflow: hidden; }
.df-units-row {
	display: flex;
	gap: 14px;
	width: max-content;
	transition: transform var(--duration-slowest) var(--easing-easeInOut);
}
.df-unit-card {
	flex: 0 0 auto;
	/* width is set inline per-card in JS (openDetailModal/sizeCards), sized
	   to the rendered viewport so exactly PAGE_SIZE cards fit */
	border: 1px solid var(--pmv-color-border);
	border-radius: var(--pmv-radius);
	padding: 12px;
	display: flex;
	flex-direction: column;
	gap: 8px;
}
.df-carousel-arrow {
	position: absolute;
	top: 50%;
	transform: translateY(-50%);
	width: 34px; height: 34px;
	border-radius: var(--radius-full);
	background: var(--pmv-color-white);
	box-shadow: var(--pmv-shadow);
	display: flex;
	align-items: center;
	justify-content: center;
	color: var(--pmv-color-dark);
	z-index: 4;
	transition: var(--pmv-transition);
}
.df-carousel-arrow:hover { transform: translateY(-50%) scale(1.06); }
.df-carousel-arrow.disabled { opacity: var(--opacity-muted); pointer-events: none; }
.df-carousel-prev { left: -14px; }
.df-carousel-next { right: -14px; }
@media (max-width: 640px) {
	.df-carousel-prev { left: -8px; }
	.df-carousel-next { right: -8px; }
}
.df-unit-thumb { width: 100%; height: 200px; border-radius: var(--radius-md); background-size: cover; background-position: center; }
.df-unit-title { font-size: var(--fontSize-base); }
.df-unit-card .pp-price-row { gap: 6px; }
.df-unit-card .pp-price-current { font-size: var(--fontSize-base); }
.df-unit-card .pp-currency-btn { min-width: 26px; height: 26px; padding: 0 6px; font-size: var(--fontSize-xs); }
.df-facts { border-top: 1px solid var(--pmv-color-border); padding-top: 8px; margin-top: 2px; }
.df-fact-row { display: flex; align-items: baseline; justify-content: space-between; font-size: var(--fontSize-sm); color: var(--pmv-color-subtle); padding: 3px 0; }
.df-fact-row span:first-child { color: var(--pmv-color-muted); }
.df-fact-row span:last-child { font-weight: var(--fontWeight-semibold); color: var(--pmv-color-text); }
.df-fact-highlight { color: var(--pmv-color-accent) !important; }

/* Project facts footer */
.detail-footer-facts {
	display: flex;
	flex-wrap: wrap;
	gap: 8px 28px;
	margin-top: 22px;
	padding-top: 18px;
	border-top: 1px solid var(--pmv-color-border);
	font-size: var(--fontSize-sm);
	color: var(--pmv-color-text);
}

/* 7. Single close button */
.detail-close-btn {
	background: var(--pmv-color-surface-hover);
	color: var(--pmv-color-text);
	width: 100%;
	padding: 12px;
	border-radius: var(--radius-md);
	font-weight: var(--fontWeight-semibold);
	font-size: var(--fontSize-sm);
	margin-top: 24px;
	transition: var(--pmv-transition);
}
.detail-close-btn:hover { background: var(--pmv-color-surface-active); }

@media (max-width: 640px) {
	.detail-hero { height: 220px; }
}

/* ---------- Listing drawer ---------- */
.listing-drawer {
	position: fixed;
	top: 0; left: -380px;
	width: 380px; height: 100%;
	background: var(--pmv-color-white);
	z-index: 900;
	box-shadow: var(--pmv-shadow);
	transition: left var(--duration-slower) var(--easing-ease);
	display: flex;
	flex-direction: column;
}
.listing-drawer.open { left: 0; }

/* Right-side counterpart to .listing-drawer, shared by the hidden-listings
   drawer and the favorites drawer — both desktop only (see the mobile
   override below) and mutually exclusive with each other via JS since they
   occupy the identical screen region. Shares .drawer-header/.drawer-list/
   .drawer-card for the header/list/card look, just mirrored horizontally. */
.right-drawer {
	position: fixed;
	top: 0; right: -380px;
	width: 380px; height: 100%;
	background: var(--pmv-color-white);
	z-index: 900;
	box-shadow: var(--pmv-shadow);
	transition: right var(--duration-slower) var(--easing-ease);
	display: flex;
	flex-direction: column;
}
.right-drawer.open { right: 0; }

.drawer-header {
	display: flex;
	align-items: center;
	justify-content: space-between;
	padding: 20px 22px;
	border-bottom: 1px solid var(--pmv-color-border);
}
.drawer-header h3 { font-size: var(--fontSize-lg); }
.drawer-header .js-drawer-count { color: var(--pmv-color-muted); font-weight: var(--fontWeight-normal); font-size: var(--fontSize-base); }
.drawer-close { font-size: var(--fontSize-2xl); color: var(--pmv-color-muted); line-height: var(--lineHeight-none); }

.drawer-list {
	flex: 1;
	overflow-y: auto;
	padding: 16px 18px;
	display: flex;
	flex-direction: column;
	gap: 14px;
}

.drawer-card {
	border: 1px solid var(--pmv-color-border);
	border-radius: var(--pmv-radius);
	overflow: hidden;
	cursor: pointer;
	transition: var(--pmv-transition);
	display: flex;
	/* .drawer-list is a column flex container with overflow-y:auto so it can
	   scroll — but a flex item's automatic minimum size collapses to 0
	   (instead of its content height) whenever the item itself has overflow
	   other than visible, which this card does. Without flex-shrink:0, once
	   enough cards are added that their combined height exceeds the visible
	   list, the flex algorithm crushes every card down to a sliver to force
	   them all to fit, rather than leaving them full-height and letting the
	   list scroll. */
	flex-shrink: 0;
}
/* Derived from --pmv-color-primary's actual value (color-red-500) since a
   box-shadow color can't reference a CSS custom property with alpha added
   on top of it without relative color syntax, which isn't used elsewhere in
   this codebase — keep this in sync by hand if --pmv-color-primary changes. */
.drawer-card:hover, .drawer-card.active { border-color: var(--pmv-color-primary); box-shadow: 0 4px 14px rgba(43,27,18,0.15); }
.drawer-card .dc-thumb {
	width: 96px;
	flex-shrink: 0;
	background-color: var(--pmv-color-subtle);
	background-size: cover;
	background-position: center;
	display: flex;
	align-items: flex-start;
	justify-content: flex-start;
	color: var(--color-gray-600);
	font-size: var(--fontSize-xs);
	text-align: center;
	padding: 6px;
	position: relative;
}
.drawer-card .dc-tag-list {
	/* A unit can be listed for multiple actions at once — one tag per action, side by side. */
	position: absolute;
	top: 6px; left: 6px;
	display: flex;
	flex-wrap: wrap;
	gap: 3px;
}
.drawer-card .dc-tag {
	/* "buy" is the default/base tag (no .buy modifier rule needed below,
	   same as .pin-ribbon above) — direct-to-red, not --pmv-color-primary or
	   --pmv-color-accent. */
	background: var(--color-red-500);
	color: var(--pmv-color-white);
	font-size: var(--fontSize-xs);
	font-weight: var(--fontWeight-bold);
	padding: 2px 6px;
	border-radius: var(--radius-xs);
}
.drawer-card .dc-tag.rent { background: var(--pmv-color-rent); }
.drawer-card .dc-tag.sold { background: var(--pmv-color-muted); }
.drawer-card .dc-body { padding: 12px 14px; flex: 1; }
.drawer-card .dc-body h4 { font-size: var(--fontSize-base); margin-bottom: 3px; }
.drawer-card .dc-location { font-size: var(--fontSize-xs); color: var(--pmv-color-muted); margin-bottom: 6px; }
.drawer-card .dc-price { font-weight: var(--fontWeight-bold); color: var(--pmv-color-primary); font-size: var(--fontSize-base); }
.drawer-card .dc-meta { font-size: var(--fontSize-xs); color: var(--pmv-color-subtle); margin-top: 3px; }
.drawer-card .dc-fav {
	margin-left: auto;
	color: var(--color-gray-300);
	font-size: var(--fontSize-md);
}
.drawer-card .dc-fav.on { color: var(--pmv-color-accent); }

/* The "restore" (open-eye) icon on the hidden-listings drawer's cards — same
   top-right slot as .dc-fav above, just always accent-colored (no on/off
   state; every card in this drawer is by definition hidden, and clicking
   anywhere on the card is what restores it). */
.drawer-card .dc-restore { margin-left: auto; color: var(--pmv-color-accent); display: flex; align-items: center; }

.drawer-empty { text-align: center; color: var(--pmv-color-muted); padding: 40px 16px; font-size: var(--fontSize-base); }

/* ---------- Shared modal base (used by the filter megamenu and detail
   modal) ---------- */
.modal-overlay {
	position: fixed;
	inset: 0;
	background: rgba(15, 20, 28, 0.55);
	z-index: 1200;
	display: none;
	align-items: center;
	justify-content: center;
}
.modal-overlay.open { display: flex; }

/* The detail modal can be taller than the viewport. Rather than scrolling
   inside the card (scrollbar on the card, map frozen underneath), the
   overlay itself scrolls: the card grows to its natural height, the map
   stays fixed behind it either way, and top/bottom padding gives it
   breathing room instead of butting flush against the viewport edges.
   Flex-column stacks the sticky close bar above the card while keeping both
   horizontally centered. justify-content MUST be flex-start here: with
   flex-direction:column it controls the vertical axis, and the base
   .modal-overlay rule sets it to `center` — on a scrollable flex container,
   centering an item taller than the container pushes its overflow equally
   above and below, and the part that overflows *above* a centered item is
   unreachable by scrolling. flex-start removes the effect entirely. */
.js-detail-modal.open {
	flex-direction: column;
	align-items: center;
	justify-content: flex-start;
	overflow-y: auto;
	padding: 20px 20px 40px;
}
.modal-box {
	background: var(--pmv-color-white);
	border-radius: var(--pmv-radius);
	padding: 34px;
	width: 90%;
	max-width: 420px;
	position: relative;
	box-shadow: var(--pmv-shadow);
}
.modal-close {
	position: absolute;
	top: 14px; right: 16px;
	font-size: var(--fontSize-2xl);
	color: var(--pmv-color-muted);
}

/* On mobile, the detail modal behaves the same as the item drawer above:
   full screen except the footer, sliding up the same way, with a real
   (non-overlapping) close-button header instead of the desktop centered-
   modal-with-backdrop treatment. openDetailModal() closes the item drawer
   in the background whenever this opens. */
@media (max-width: 640px) {
	.js-detail-modal {
		display: flex;
		position: fixed;
		top: 0;
		left: 0;
		right: 0;
		height: calc(100% - var(--pmv-footer-height));
		background: var(--pmv-color-white);
		flex-direction: column;
		align-items: stretch;
		justify-content: flex-start;
		padding: 0;
		z-index: 950;
		transform: translateY(110%);
		visibility: hidden;
		transition: transform var(--duration-slower) var(--easing-ease), visibility 0s linear var(--duration-slower);
	}
	.js-detail-modal.open {
		flex-direction: column;
		align-items: stretch;
		justify-content: flex-start;
		overflow-y: visible;
		padding: 0;
		transform: translateY(0);
		visibility: visible;
		transition: transform var(--duration-slower) var(--easing-ease);
	}
	/* Cancel the desktop sticky+height:0 overlay trick — a real header row,
	   like .item-drawer-header, so the close button never sits on top of
	   the hero image below it. */
	.detail-modal-sticky-bar {
		position: static;
		height: auto;
		width: 100%;
		max-width: none;
		padding: 10px 12px;
		flex-shrink: 0;
	}
	.detail-modal-sticky-bar .modal-close {
		background: var(--pmv-color-surface-hover); /* matches .item-drawer-close's solid chip — the
		                         semi-transparent white was for floating over
		                         an image, no longer relevant on a plain
		                         header bar */
	}
	.modal-box.detail-modal-box {
		width: 100%;
		max-width: none;
		flex: 1;
		overflow-y: auto;
		border-radius: 0;
	}
}

/* ---------- Filter mega menu ---------- */
/* An anchored dropdown ("mega menu"), not a modal — same floating/no-overflow
   positioning pattern as .footer-popover/.location-popover (floats above the
   footer, opens upward, nested in a `position: relative` ancestor with no
   overflow so it can't get clipped), just larger and with its own
   header/footer instead of reusing that shared class. No max-height/
   overflow here on purpose — the panel sizes to its content so there's
   never a scrollbar in either direction; the compact field sizing below
   (tighter padding, no per-field labels) is what keeps that content short
   enough to fit. */
.filter-megamenu {
	display: none;
	position: absolute;
	bottom: calc(100% + 10px);
	left: 0;
	width: 360px;
	max-width: 92vw;
	background: var(--pmv-color-white);
	border-radius: var(--pmv-radius);
	box-shadow: var(--pmv-shadow);
	z-index: 5;
	flex-direction: column;
}
.filter-megamenu.open { display: flex; }
/* Compact header row: "Filter" on the left, close icon on the right — a
   normal flex row, with its own gap from the field list below via the
   border/padding. */
.filter-modal-header {
	display: flex;
	align-items: center;
	justify-content: space-between;
	padding: 12px 14px;
	border-bottom: 1px solid var(--pmv-color-border);
}
.filter-modal-title { font-size: var(--fontSize-md); font-weight: var(--fontWeight-bold); color: var(--pmv-color-dark); }
.filter-modal-close,
.item-drawer-close {
	position: static; /* overrides the absolute-positioned base .modal-close —
	                      this one sits inline in the header's flex row
	                      instead */
	width: 26px; height: 26px;
	border-radius: var(--radius-full);
	background: var(--pmv-color-surface-hover);
	color: var(--pmv-color-dark);
	font-size: var(--fontSize-md);
	display: flex;
	align-items: center;
	justify-content: center;
	transition: var(--pmv-transition);
}
.filter-modal-close:hover, .item-drawer-close:hover { background: var(--pmv-color-surface-active); }

.filter-modal-body {
	padding: 14px;
	display: flex;
	flex-direction: column;
	gap: 8px;
}
.filter-row { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
.filter-modal-body input, .filter-modal-body select {
	width: 100%;
	padding: 8px 10px;
	border: 1px solid var(--pmv-color-border);
	border-radius: var(--radius-md);
	font-size: var(--fontSize-sm);
	color: var(--pmv-color-text);
	background: var(--pmv-color-white);
}
/* appearance:auto (native OS chevron) looks broken in Safari/WebKit: it
   ignores our padding/font-size entirely and keeps its own intrinsic
   control height, which clips the label text against the custom border
   instead of centering it. A drawn chevron sidesteps native theming so
   sizing stays correct and consistent everywhere. */
.filter-modal-body select {
	appearance: none;
	-webkit-appearance: none;
	/* Stroke color is --color-gray-700 (var(--pmv-color-text)'s current
	   binding) hand-encoded, since a data-URI SVG can't reference a CSS
	   custom property — keep this in sync by hand if that token changes. */
	background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23374151' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
	background-repeat: no-repeat;
	background-position: right 10px center;
	background-size: 16px;
	padding-right: 32px;
}
.filter-modal-body input::placeholder { color: var(--pmv-color-subtle); }
.filter-modal-body input:focus, .filter-modal-body select:focus {
	outline: none;
	border-color: var(--pmv-color-primary);
}

.filter-modal-footer {
	flex-shrink: 0;
	display: flex;
	border-top: 1px solid var(--pmv-color-border);
}
.filter-clear-btn, .filter-apply-btn {
	flex: 1;
	padding: 11px;
	font-weight: var(--fontWeight-bold);
	font-size: var(--fontSize-sm);
	transition: var(--pmv-transition);
}
.filter-clear-btn {
	background: var(--pmv-color-white);
	color: var(--pmv-color-dark);
}
.filter-clear-btn:hover { background: var(--pmv-color-surface-hover); }
.filter-apply-btn {
	background: var(--pmv-color-dark);
	color: var(--pmv-color-white);
}
.filter-apply-btn:hover { background: var(--color-gray-950); }

@media (max-width: 560px) {
	.filter-row { grid-template-columns: 1fr; }
}

/* ---------- Responsive ---------- */
@media (max-width: 640px) {
	.js-property-map-root { --pmv-footer-height: 112px; }

	/* Stops above the footer (rather than height:100%) instead of covering
	   it — the footer, and its Filters trigger, needs to stay visible and
	   tappable while this drawer is open so tapping it can switch straight
	   over to the Filters drawer (see the mutual-exclusion JS). */
	.listing-drawer { width: 100vw; left: -100vw; height: calc(100% - var(--pmv-footer-height)); }

	/* Hidden-listings and Favorites are desktop-only features — mobile
	   already has four full-screen drawers fighting for the same space
	   (Listings, Map Item, Filters, Item Detail) and a fifth/sixth needs its
	   own mutual-exclusion/slide design that hasn't been done, so the
	   triggers and their drawers are simply not shown here. */
	.js-hidden-listings-btn, .js-favorites-btn, .right-drawer { display: none; }

	/* Footer splits into two rows on mobile: row 1 is location + the
	   type-icon row, row 2 pairs Listings with the "Filters" trigger. A grid
	   (with named areas) is used instead of source order so elements nested
	   inside .footer-right/.filter-icons in the markup can be placed
	   independently. .footer-right and .filter-icons are both unwrapped via
	   `display: contents` so their children become direct grid items of
	   .site-footer; the type-icon row (.filter-icons-scroll) stays a real
	   box so its own overflow-x:auto scrolling still works. --pmv-footer-
	   height is bumped to match this taller, two-row footer so the zoom
	   controls and Leaflet attribution (both positioned off of it) still
	   clear it. */
	.site-footer {
		display: grid;
		grid-template-columns: auto 1fr;
		grid-template-areas:
			"location icons"
			"listings filters";
		align-items: center;
		height: var(--pmv-footer-height);
		padding: 8px 12px;
		gap: 6px 10px;
	}
	.footer-right,
	.filter-icons { display: contents; }
	.location-select { grid-area: location; }
	.footer-menu-btn { grid-area: listings; height: 44px; }
	.js-adv-filter-btn { grid-area: filters; height: 44px; justify-self: end; }
	.footer-divider { display: none; }
	.filter-icons-scroll { grid-area: icons; height: 44px; min-width: 0; }

	/* Full-screen slide-from-right drawer on mobile, mirroring
	   .listing-drawer's slide-from-left pattern for left/right symmetry.
	   Overrides the base .filter-megamenu anchored-dropdown rule entirely:
	   stays permanently `display:flex` (display can't be transitioned, same
	   reason .listing-drawer never toggles it) and slides via `right`
	   instead of the bottom/left anchoring. `position:fixed` sidesteps the
	   `display:contents` on its .filter-icons/.footer-right ancestors
	   (containing block is the viewport since none of the ancestor chain
	   uses transform/filter/perspective). */
	.filter-megamenu {
		display: flex;
		position: fixed;
		top: 0;
		right: -100vw;
		left: auto;
		bottom: auto;
		width: 100vw;
		/* Stops above the footer for the same reason as .listing-drawer
		   above — keeps the Listings trigger reachable to switch straight
		   back. */
		height: calc(100% - var(--pmv-footer-height));
		max-width: none;
		border-radius: 0;
		/* Only orders the megamenu among .site-footer's own descendants —
		   .site-footer's position:fixed makes it its own stacking context,
		   so this is NOT compared against .listing-drawer's z-index:900
		   globally. Correct ordering vs. the listing drawer is enforced by
		   the mutual-exclusion JS, not by this value. */
		z-index: 950;
		transition: right 0.3s ease;
	}
	.filter-megamenu.open { right: 0; }

	/* The "sizes to content, no scrollbar on purpose" comment on
	   .filter-megamenu applies only to the desktop anchored dropdown — a
	   full-screen fixed-height drawer needs its field list to scroll
	   independently, like .drawer-list already does in .listing-drawer. */
	.filter-modal-body {
		flex: 1;
		overflow-y: auto;
		gap: 12px;
	}
	.filter-row { gap: 10px; }
	/* Desktop's compact 13px/8px-10px fields feel cramped as touch targets
	   on a full-screen mobile drawer, so both grow here. font-size must be
	   >=16px: iOS Safari auto-zooms the whole page on focus for any
	   input/select below that. */
	.filter-modal-body input,
	.filter-modal-body select {
		padding: 13px 12px;
		font-size: var(--fontSize-md);
	}
	/* padding shorthand above resets padding-right too, so restore room for
	   the custom chevron (bumped slightly for the bigger touch target/font
	   here). */
	.filter-modal-body select {
		padding-right: 38px;
		background-size: 18px;
		background-position: right 12px center;
	}
}
