/**
 * SL4U — r29 map view. Stylesheet for sl4u-r29-mapview.php.
 *
 * Scoped hard to the two search archives (body.post-type-archive-lessons /
 * -schools) for everything that could leak onto a single-post map, plus the
 * popup/marker rules, which are safe anywhere because their classes only exist
 * where sl4u-r29-mapview.php emitted them.
 *
 * Design rules obeyed: no glow (drop-shadow only), grey + blue (#2145D9 /
 * navy #1B2A6B), orange only as the existing r25 hover accent is NOT reused
 * here — selected state is navy, per Mayank's r29 brief §3.
 */

/* ══════════════════════════════════════════════════════════════════════
   1 · MAP HEIGHT  (r29 item 2)
   ──────────────────────────────────────────────────────────────────────
   BEFORE: the ts-map widgets mp_les1 / mp_sch1 carry an Elementor
   `ts_map_height` of calc(100vh - 210px) with no minimum, so at the client's
   980x501 / 1280x501 review window the map was 291px tall. Measured live
   2026-09-11: 291 / 291 / 690 / 634 at 980 / 1280 / 1440x900 / 390x844.

   FIRST FIX (r29, superseded): height calc(100vh - 96px), min-height 640px,
   max-height 900px. The 640px FLOOR overflowed the client's own 501px-tall
   review window — 640 > 501, so the map's bottom edge sat 202px below the fold
   after r28-uxfix's scroll-into-view and she had to scroll to see the map she
   had just asked for.

   r29b (2026-09-11): viewport-relative with NO floor above the viewport.
   Measured live on staging, map view, /lessons/ + /schools/, both engines:
     • site header `.elementor-location-header` = 55px at 980/1024/1280/1440,
       62.5px at 390. Its inner `.elementor-element-0e93057` is position:sticky
       top:0 inside a 55px box, i.e. ZERO travel — the header scrolls away.
     • r25's compact filter bar `.sl4u-r7s-stickybar` is 56px at <=1024 (52px at
       390) but measured `visibility:hidden; opacity:0; top:-56px` in map view at
       EVERY width, and does not exist at all at >=1025. It contributes 0.
       The desktop sticky filter CARD is a sidebar (top:70px), not chrome above
       the map.
     • sl4u-r28-uxfix.php:607 scrollMapIntoFrame() parks the map top at
       min(90, headerHeight + 8) = 63px (70.5px at 390) — measured map top 62.7 /
       63.0 / 62.8 / 63.0 / 70.3 at 980 / 1280 / 1024 / 1440 / 390.
   So the subtrahend is 64px (63 + 1px of subpixel slack), NOT 96, and there is
   no floor above the shortest supported window:
     clamp( 420px, calc(100vh - 64px), 820px )
   420px floor: still a usable map on a freak 400px window; only binds below a
   484px-tall viewport, which is under her 501. 820px ceiling: at 1440x900 that
   leaves the map bottom at 883 < 900, so it still fits after the r28 scroll.
   min-height/max-height are explicitly reset — clamp() carries both bounds, and
   a leftover `min-height:640px !important` would re-break exactly this.
   ══════════════════════════════════════════════════════════════════════ */
html body.post-type-archive-lessons .elementor-widget-ts-map .ts-map,
html body.post-type-archive-schools .elementor-widget-ts-map .ts-map {
	height: clamp( 420px, calc( 100vh - 64px ), 820px ) !important;
	min-height: 0 !important;
	max-height: none !important;
}

/* Tablet uses the same rule — deliberately. A 768..1024 landscape viewport is
   exactly where the old 291px was worst, and a separate lower band there was
   measured on 2026-09-11 to give 560px at 980x501 while 1280x501 got 640px,
   i.e. two different map sizes across the client's own two review widths for
   no reason. One band. With a pure viewport-relative rule the two widths now
   agree by construction at any shared height.

   Mobile: full viewport minus the sticky header and the map/list FAB strip
   (sl4u-r25-mapchrome.php:172 reserves the bottom for the switcher FAB).
   100dvh so the iOS/Android URL bar collapsing does not leave a gap;
   100vh is kept as the fallback for engines without dvh. */
@media ( max-width: 767px ) {
	html body.post-type-archive-lessons .elementor-widget-ts-map .ts-map,
	html body.post-type-archive-schools .elementor-widget-ts-map .ts-map {
		height: calc( 100vh - 132px ) !important;
		height: calc( 100dvh - 132px ) !important;
		min-height: 460px !important;
		max-height: none !important;
	}
}

/* The r28-uxfix empty-state panel rides on .sl4u-r28ux-mapwrap, which is the
   map's own offset parent. Nothing here changes its position, but the taller
   map means the panel must stay vertically centred rather than pinned high. */
html body .sl4u-r28ux-mapwrap { position: relative; }


/* ══════════════════════════════════════════════════════════════════════
   2 · MARKER PILLS  (r29 item 3)
   ──────────────────────────────────────────────────────────────────────
   Voxel's Google marker is a custom OverlayView div whose innerHTML comes
   straight from `_post_get_marker()` (post-utils.php:572). sl4u-r29-mapview.php
   filters that via `_voxel/post_utils/post_get_marker/custom` and emits
   `.map-marker.sl4u-r29-pin` INSTEAD of `.map-marker.marker-type-icon`, so
   every one of sl4u-r25-mapstyle.php's teardrop rules — all of which require
   `.marker-type-icon` — simply does not match. r25 stays the fallback for the
   posts that get no pill (no price / no name) and for every single-post map.
   ══════════════════════════════════════════════════════════════════════ */
html body .marker-wrapper > .map-marker.sl4u-r29-pin {
	display: flex;
	align-items: center;
	justify-content: center;
	position: relative;
	width: auto !important;
	height: 30px;
	min-width: 44px;
	max-width: 156px;
	padding: 0 11px;
	box-sizing: border-box;
	background: #2145D9;
	color: #ffffff;
	border: 2px solid #ffffff;
	border-radius: 100px;
	font-family: Montserrat, 'Helvetica Neue', Arial, sans-serif;
	font-size: 12.5px;
	font-weight: 800;
	line-height: 1;
	letter-spacing: -0.1px;
	white-space: nowrap;
	overflow: visible !important;   /* beat map.css .map-marker{overflow:hidden} — the tail lives outside the box */
	cursor: pointer;
	/* drop-shadow, never a coloured glow (SL4U design rule) */
	box-shadow: 0 2px 5px rgba( 15, 23, 42, 0.32 );
	transform: none !important;     /* beat map.css .marker-focused .map-marker{transform:scale(1.25)} */
	transition: background-color 0.15s ease, box-shadow 0.15s ease;
}

/* The text itself carries the ellipsis so the pill box can stay overflow:visible. */
html body .map-marker.sl4u-r29-pin > .sl4u-r29-pin-t {
	display: block;
	max-width: 130px;
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}

/* Tail: a 5px white-bordered triangle so the pill reads as anchored, not floating. */
html body .map-marker.sl4u-r29-pin::after {
	content: '';
	position: absolute;
	left: 50%;
	bottom: -8px;
	width: 0;
	height: 0;
	transform: translateX( -50% );
	border-left: 6px solid transparent;
	border-right: 6px solid transparent;
	border-top: 8px solid #ffffff;
	pointer-events: none;
}
html body .map-marker.sl4u-r29-pin::before {
	content: '';
	position: absolute;
	left: 50%;
	bottom: -4px;
	width: 0;
	height: 0;
	transform: translateX( -50% );
	border-left: 4px solid transparent;
	border-right: 4px solid transparent;
	border-top: 6px solid #2145D9;
	pointer-events: none;
	z-index: 1;
	transition: border-top-color 0.15s ease;
}

/* ANCHOR. map.css centres every wrapper on the coordinate
   (`.ts-map:not(.leaflet-container) .marker-wrapper{transform:translate(-50%,-50%)}`),
   which is right for a disc and wrong for a tailed pill: the TAIL TIP must sit
   on the coordinate. Same maths r25 uses for its teardrop, same :has() guard
   shape so cluster wrappers keep the centred transform. */
html body .marker-wrapper:has( > .map-marker.sl4u-r29-pin ) {
	transform: translate( -50%, -100% ) !important;
}
/* Inside a non-expandable cluster bundle the wrappers are in normal flow
   (map.css sets transform:none) — re-except them, exactly as r25 does. */
html body .ts-marker-bundle .marker-wrapper,
html body .ts-marker-bundle .marker-wrapper:has( > .map-marker.sl4u-r29-pin ) {
	transform: none !important;
}
/* ...and drop the tail there, since a bundled pill is a list row, not a map pin. */
html body .ts-marker-bundle .map-marker.sl4u-r29-pin::after,
html body .ts-marker-bundle .map-marker.sl4u-r29-pin::before { display: none; }

/* Hover / selected: navy. Colour shift only — no scale, no bounce, no glow. */
html body .marker-wrapper:hover > .map-marker.sl4u-r29-pin,
html body .marker-wrapper.marker-active > .map-marker.sl4u-r29-pin,
html body .marker-wrapper.marker-focused > .map-marker.sl4u-r29-pin,
html body .map-marker.sl4u-r29-pin.marker-active,
html body .map-marker.sl4u-r29-pin.sl4u-r29-sel {
	background: #1B2A6B;
	box-shadow: 0 3px 7px rgba( 15, 23, 42, 0.4 );
}
html body .marker-wrapper:hover > .map-marker.sl4u-r29-pin::before,
html body .marker-wrapper.marker-active > .map-marker.sl4u-r29-pin::before,
html body .map-marker.sl4u-r29-pin.marker-active::before,
html body .map-marker.sl4u-r29-pin.sl4u-r29-sel::before {
	border-top-color: #1B2A6B;
}
/* A selected pill wins the stacking order against its neighbours. */
html body .marker-wrapper:has( > .map-marker.sl4u-r29-pin.sl4u-r29-sel ),
html body .marker-wrapper:has( > .map-marker.sl4u-r29-pin.marker-active ) { z-index: 30; }

/* 480 and below: a 156px pill eats a phone map. Shorter, smaller, same shape. */
@media ( max-width: 480px ) {
	html body .marker-wrapper > .map-marker.sl4u-r29-pin {
		height: 26px;
		min-width: 40px;
		padding: 0 9px;
		font-size: 11.5px;
	}
	html body .map-marker.sl4u-r29-pin > .sl4u-r29-pin-t { max-width: 92px; }
}

/* Cluster bubbles: sl4u-r25-mapstyle.php already paints these #2145D9 / 32px /
   2px white ring / no glow, which is exactly the pill palette. Only the type
   scale is nudged so a 3-digit count does not clip next to a 30px pill. */
html body .marker-wrapper .ts-marker-cluster,
html body .ts-marker-cluster.ts-marker-cluster {
	font-family: Montserrat, 'Helvetica Neue', Arial, sans-serif;
	font-size: 12.5px;
	font-weight: 800;
}


/* ══════════════════════════════════════════════════════════════════════
   3 · POPUP  (r29 item 1)
   ──────────────────────────────────────────────────────────────────────
   The popup body is Elementor template 357 (lessons) / 356 (schools), rewritten
   by round7/r29/r29-mapview-elementor.php. These rules only dress the markup
   that rewrite emits; the InfoWindow chrome rules beat map.css.
   ══════════════════════════════════════════════════════════════════════ */
html body .ts-preview-popup { width: 300px !important; }

/* Never let the InfoWindow clip or scroll the card. */
html body .ts-marker .gm-style-iw-d {
	overflow: hidden !important;
	max-height: none !important;
	max-width: none !important;
}
html body .ts-marker .gm-style-iw-c {
	max-height: none !important;
	max-width: none !important;
	overflow: visible !important;
}

html body .sl4u-r29-media {
	position: relative;
	display: block;
	width: 100%;
	aspect-ratio: 16 / 10;
	overflow: hidden;
	background: #e8edf5;   /* grey placeholder when a post has no cover image */
}
html body .sl4u-r29-media > img {
	display: block;
	width: 100%;
	height: 100%;
	object-fit: cover;
	border: 0;
}
/* A post with no cover renders src="" — hide the broken-image glyph and leave
   the grey plate. (School 1648 "Famara Wave House" has no cover today.) */
html body .sl4u-r29-media > img[src=''],
html body .sl4u-r29-media > img:not([src]) { display: none; }

/* BOTTOM-left, not top-left. Measured on staging 2026-09-11: Voxel's
   "Search as I move the map" control (`.ts-map-drag`, z-index 1000) is pinned
   to the top of the map and is a SIBLING of `.ts-map`, so nothing inside the
   Google overlay panes can be stacked above it. A top-left chip lands straight
   underneath it for every marker in the lower half of the map — verified in the
   /schools/@1280 and /lessons/@390 popup captures. Moving the chip to the
   bottom of the media block removes the collision outright instead of starting
   a z-index fight with a legitimate map control. */
html body .sl4u-r29-chip {
	position: absolute;
	bottom: 10px;
	left: 10px;
	display: inline-flex;
	align-items: center;
	gap: 4px;
	max-width: calc( 100% - 20px );
	padding: 5px 10px;
	border-radius: 100px;
	background: #1B2A6B;
	color: #ffffff;
	font-family: Montserrat, 'Helvetica Neue', Arial, sans-serif;
	font-size: 10px;
	font-weight: 800;
	line-height: 1;
	letter-spacing: 1px;
	text-transform: uppercase;
	white-space: nowrap;
	overflow: hidden;
	text-overflow: ellipsis;
	box-shadow: 0 2px 6px rgba( 15, 23, 42, 0.28 );
}

html body .sl4u-r29-loc {
	display: flex;
	align-items: center;
	gap: 5px;
	font-family: Inter, 'Helvetica Neue', Arial, sans-serif;
	font-size: 11.5px;
	line-height: 1.35;
	color: #6b7280;
	font-weight: 500;
}
html body .sl4u-r29-loc > span.material-symbols-outlined { font-size: 14px; color: #2145D9; flex: 0 0 auto; }
html body .sl4u-r29-loc > span.sl4u-r29-loc-t {
	display: -webkit-box;
	-webkit-line-clamp: 2;
	-webkit-box-orient: vertical;
	overflow: hidden;
}

html body .sl4u-r29-foot {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 10px;
	margin-top: 2px;
	padding-top: 10px;
	border-top: 1px solid rgba( 15, 23, 42, 0.07 );
	font-family: Inter, 'Helvetica Neue', Arial, sans-serif;
}
html body .sl4u-r29-price {
	font-family: Montserrat, 'Helvetica Neue', Arial, sans-serif;
	font-size: 16px;
	font-weight: 800;
	color: #2145D9;
	letter-spacing: -0.2px;
	white-space: nowrap;
}
html body .sl4u-r29-acts {
	display: inline-flex;
	align-items: center;
	gap: 4px;
	min-width: 0;
	font-size: 11.5px;
	font-weight: 700;
	color: #475569;
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}
html body .sl4u-r29-acts > span.material-symbols-outlined { font-size: 14px; color: #2145D9; flex: 0 0 auto; }
html body .sl4u-r29-cta {
	flex: 0 0 auto;
	display: inline-flex;
	align-items: center;
	gap: 3px;
	padding: 7px 13px;
	border-radius: 100px;
	background: #2145D9;
	color: #ffffff;
	font-family: Montserrat, 'Helvetica Neue', Arial, sans-serif;
	font-size: 11px;
	font-weight: 800;
	line-height: 1;
	letter-spacing: 0.6px;
	text-transform: uppercase;
	white-space: nowrap;
	transition: background-color 0.15s ease;
}
html body .ts-preview-popup:hover .sl4u-r29-cta { background: #1B2A6B; }
html body .sl4u-r29-cta > span.material-symbols-outlined { font-size: 14px; }

/* Voxel's prev/next strip sits under the card; keep it off the white plate. */
html body .ts-preview-popup > .ts-map-nav { padding: 6px 0 0; }

/* r29b: on a squat window the map is now viewport-bound (437px at 501), and a
   344px popup no longer leaves Google's InfoWindow autopan the padding it wants
   — measured 2026-09-11 at 980x501 and 1280x501, the popup settled with its top
   3.4px ABOVE the map rect, i.e. a 3px sliver of the cover image clipped by
   `.ts-map{overflow:hidden}`. (1024x768 / 1440x900 / 390x844 were all fully
   inside.) Trimming the cover to 16:9 takes 19px off the card, which is more
   than the 3.4px shortfall, and autopan then fits it. Height-scoped, so nothing
   changes on a normal-height screen. */
@media ( min-width: 768px ) and ( max-height: 560px ) {
	html body .sl4u-r29-media { aspect-ratio: 16 / 9; }
}


/* ══════════════════════════════════════════════════════════════════════
   4 · LIST <-> MAP HIGHLIGHT  (r29 item 3, second half)
   ══════════════════════════════════════════════════════════════════════ */
html body .ts-preview.sl4u-r29-cardsel {
	outline: 2px solid #2145D9;
	outline-offset: -2px;
	border-radius: 16px;
}
