/**
 * HTML-marker layer - DOM mirror of the canvas marker renderer
 * (active when the map's render mode is 'html' / `config.domMirror`).
 *
 * The pin visuals are a faithful port of the client-approved prototype
 * (scratchpad/markers.html): a white disc + EVEN colour ring for circle
 * markers, a rounded pill for labels, a semi-transparent dot, and a short
 * triangular tip (язычок) that always points at the geo-anchor from the
 * marker edge facing it. Positions are computed in JS (the overlay already
 * ran the full layout) - there is NO orbit @keyframes here.
 *
 * All classes are prefixed `w2dc-hmark-`; the w2dc build codemod rewrites
 * `w2dc-` → `w2dc-`, and the plugin's FontAwesome (`.w2dc-fa`) is rewritten
 * the same way, so `<i class="w2dc-fa w2dc-fa-*">` keeps matching its
 * stylesheet on both builds.
 */

/* Host layer - a point anchored at the origin of the map's `overlayMouseTarget`
   pane (the same pane the canvas draws into), which the layer's OverlayView
   appends it to. Pins are absolutely positioned children translated to their
   DIV-pixel spot, so the layer itself needs no size - overflow must stay visible
   or the pins would be clipped to the 0×0 box. Living in the pane keeps the pins
   UNDER the map controls (a viewport layer on map.getDiv() painted over them) and
   BELOW the auto-popup / click-popup, so an opened popup still covers its pin.
   Only the pins/clusters themselves are interactive. */
.w2dc-hmark-layer {
	position: absolute;
	top: 0;
	left: 0;
	overflow: visible;
	pointer-events: none;
	z-index: 1;
}
/* Applied for one hit-test while forwarding a wheel event: makes the whole layer
   (incl. the pointer-events:auto pins) transparent to hit-testing so
   elementFromPoint returns the MAP under the cursor, not the pin. */
.w2dc-hmark-layer.w2dc-hmark-wheelthru,
.w2dc-hmark-layer.w2dc-hmark-wheelthru * {
	pointer-events: none !important;
}

/* Marker root - stays at the layer origin; the pin inside is translated to the
   body position each frame. Listener lives here (child clicks bubble up). */
.w2dc-hmark-mk {
	position: absolute;
	left: 0;
	top: 0;
	pointer-events: none;
	z-index: 2;
}

/* pin wraps tip + body under ONE soft shadow, so the язычок shares the
   marker's silhouette shadow instead of being lit/shadowed separately. The
   body/label themselves carry NO own filter. Translated + scaled per frame. */
.w2dc-hmark-pin {
	position: absolute;
	left: 0;
	top: 0;
	pointer-events: none;
	will-change: transform;
	filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.32));
}

/* body wrapper - centred on the pin origin (= body position) via translate. */
.w2dc-hmark-body {
	position: absolute;
	left: 0;
	top: 0;
	transform: translate(-50%, -50%);
	pointer-events: none;
}

/* tip: an SVG triangle whose BASE sits on the marker outline (two points on the
   circle perimeter, or a chord of the label's edge) and whose apex is the
   geo-anchor - exactly like the canvas drawCircleTip / drawLabelTip. The base is
   painted BEHIND the body (same fill), so the marker colour "flows" into the
   point with no visible seam (a straight-based triangle tucked under a round
   marker showed its edges). Points are set in JS, in px relative to the body
   centre; overflow:visible lets them extend to the anchor. */
.w2dc-hmark-tip {
	position: absolute;
	left: 0;
	top: 0;
	width: 1px;
	height: 1px;
	overflow: visible;
	pointer-events: none;
}
.w2dc-hmark-tip polygon {
	fill: var(--tipc, var(--c, #f44336));
}

/* circle: white disc with the colour ring as a BORDER - a border is uniform by
   definition, so the ring is perfectly even (concentric-disc centring rounded a
   fractional 3.5px margin unevenly, shifting the inner disc). box-sizing is
   content-box: `width`/`height` = the white disc, the border adds the ring
   outside it. All three sizes are set per-marker in JS from the render radius so
   the marker follows the configured FA/PNG marker size. The soft shadow comes
   from .pin. Defaults below are the markerSize-50 values. */
.w2dc-hmark-circle {
	box-sizing: content-box;
	width: 28px;
	height: 28px;
	border: 3.5px solid var(--c, #f44336);
	border-radius: 50%;
	background: #fff;
	display: grid;
	place-items: center;
	pointer-events: auto;
	cursor: pointer;
}

/* FA glyph - CRITICAL: reset the `<i>` default italic (it slanted the icons).
   Centred in the white content box; font-family stays the plugin's FA. */
.w2dc-hmark-circle .w2dc-hmark-icon {
	font-style: normal;
	font-weight: normal;
	font-size: 17px;
	line-height: 1;
	color: var(--c, #f44336);
	-webkit-font-smoothing: antialiased;
}

/* label: rounded-rect pill, bold 12px Arial #333, white/custom bg. */
.w2dc-hmark-label {
	font: 700 12px/1.15 Arial, system-ui, sans-serif;
	color: #333;
	background: var(--bgc, #fff);
	border-radius: 8px;
	padding: 5px 8px;
	min-height: 26px;
	white-space: nowrap;
	display: flex;
	align-items: center;
	justify-content: center;
	box-sizing: border-box;
	pointer-events: auto;
	cursor: pointer;
}
/* label content lives in one inline span so FA glyph + text flow together
   (a rating chip is `<i class=…-fa-star> 3.8 (2)`). */
.w2dc-hmark-label__txt {
	white-space: nowrap;
}
/* label FA glyph (rating star etc.) - reset the `<i>` italic; tint is set
   inline per token (the label's own colour, or --w2dc-primary). */
.w2dc-hmark-licon {
	font-style: normal;
	font-weight: normal;
}

/* image: rounded bitmap thumb, sized from the marker radius in JS. */
.w2dc-hmark-image {
	display: block;
	border-radius: 50%;
	-o-object-fit: cover;
	   object-fit: cover;
	background: #e5e7eb;
	box-shadow: 0 0 0 2px #fff;
	pointer-events: auto;
	cursor: pointer;
}

/* dot: semi-transparent filled disc, NO ring, NO tip; size from radius. */
.w2dc-hmark-dot {
	border-radius: 50%;
	background: var(--c, #7d7d7d);
	opacity: 0.6;
	pointer-events: auto;
	cursor: pointer;
}

/* Selected / hovered - subtle white halo + lift above neighbours. */
.w2dc-hmark-mk--selected,
.w2dc-hmark-mk--hovered {
	z-index: 10;
}
.w2dc-hmark-mk--selected .w2dc-hmark-circle,
.w2dc-hmark-mk--hovered .w2dc-hmark-circle {
	box-shadow: 0 0 0 3px rgba(255, 255, 255, 0.7);
}
.w2dc-hmark-mk--selected .w2dc-hmark-label,
.w2dc-hmark-mk--hovered .w2dc-hmark-label,
.w2dc-hmark-mk--selected .w2dc-hmark-image,
.w2dc-hmark-mk--hovered .w2dc-hmark-image {
	box-shadow: 0 0 0 2px #fff, 0 0 0 5px rgba(255, 255, 255, 0.6);
}

/* cluster: filled colour circle (diameter = size*2) with white bold count. */
.w2dc-hmark-cluster {
	position: absolute;
	left: 0;
	top: 0;
	border-radius: 50%;
	display: flex;
	align-items: center;
	justify-content: center;
	pointer-events: auto;
	cursor: pointer;
	will-change: transform;
	box-shadow: 0 2px 4px rgba(0, 0, 0, 0.32);
	z-index: 1;
}
.w2dc-hmark-cluster__count {
	color: #fff;
	font: 700 12px/1 Arial, system-ui, sans-serif;
	pointer-events: none;
	-webkit-user-select: none;
	   -moz-user-select: none;
	        user-select: none;
}

/* =========================================================================
 * SCOPE MODEL - keep listing cards and map popups separate.
 *
 * build_popup_html() emits ONE markup (`.w2dc-listing-content__field` / `__value` /
 * `__images` …) that every surface reuses, so the field renderers below stay
 * SHARED on purpose: a phone / address / rating / gallery field must look
 * identical wherever it appears. Only three scopes exist, and each new rule
 * belongs to exactly one of them:
 *
 *   .w2dc-listing-content            SHARED base - typography, the field grid
 *                                     + per-type icons, value formatting, and
 *                                     the wrapping guard. On popup body, listing
 *                                     card AND sidebar card.
 *   .w2dc-react-infowindow-content   MAP POPUP only - the floating bubble's
 *                                     chrome: scrollbar, edge gutter, the
 *                                     4-line text clamp, cover image (4px
 *                                     gutter), grab-cursor guard.
 *   .w2dc-card-fields                LISTING CARD only - full-width card: cover
 *                                     image (16px gutter), slider arrow insets.
 *
 * A rule that sizes/clips/positions differently between a small floating bubble
 * and a full-width card MUST hang off one of the two surface classes, never off
 * shared `.w2dc-listing-content` - that mixing is what once clipped card text
 * to 4 lines and pulled the popup cover image the wrong way.
 * ========================================================================= */

/* Event passthrough for popup overlays.
 *
 * The popup container (`w2dc-popup-passthrough`) is set to
 * `pointer-events: none` in JS so the popup is EVENT-TRANSPARENT:
 * clicks / wheel / touch / hover land on the layer underneath (the
 * GL canvas or the Google tile pane), which means dragging or
 * pinching directly on a popup pans / zooms the map natively as if
 * the popup weren't there. This is the cleanest way to make the
 * popup behave like a visual overlay without rebuilding the engines'
 * gesture handlers on top of forwarded touch events.
 *
 * Interactive descendants (close X, links, buttons, form fields,
 * route step list, slider arrows) need to capture their own clicks
 * and key events, so we explicitly re-enable `pointer-events: auto`
 * for them below. The `.w2dc-react-infowindow-content--has-scroll`
 * branch keeps wheel-scroll + touch-scroll working when the popup
 * content overflows; the modifier class is added by PopupCard's
 * ResizeObserver only while the body genuinely overflows, so short
 * popups stay fully event-transparent. */
.w2dc-popup-passthrough a,
.w2dc-popup-passthrough button,
.w2dc-popup-passthrough input,
.w2dc-popup-passthrough textarea,
.w2dc-popup-passthrough select,
.w2dc-popup-passthrough label,
.w2dc-popup-passthrough [role="button"],
.w2dc-popup-passthrough .MuiIconButton-root,
.w2dc-popup-passthrough form,
.w2dc-popup-passthrough .w2dc-listing-content__slider-btn,
.w2dc-popup-passthrough .w2dc-listing-content__directions-btn,
.w2dc-popup-passthrough .w2dc-listing-content__images--slider,
.w2dc-popup-passthrough .w2dc-listing-content__slider-track,
.w2dc-popup-passthrough .w2dc-react-infowindow-content--has-scroll {
	pointer-events: auto;
}

/* Listing content - server-emitted HTML inside `.w2dc-listing-content`.
 * Same wrapper class on BOTH the popup body AND the sidebar listing
 * cards, so every formatting rule below applies to both contexts.
 * The popup carries an additional wrapper class
 * `.w2dc-react-infowindow-content` which scopes the popup-only
 * scrollbar + right-edge gutter (block further down); sidebar cards
 * don't have that class, so they don't pick up the popup chrome.
 */
/* SHARED - wrapping guard for EVERY node we render, not just
   `.w2dc-listing-content__value`: themes routinely ship `p { word-break: break-all }` /
   `overflow-wrap: anywhere` on their content elements, which our value-level
   rule (a sibling in specificity) does not always outrank - the visible symptom
   is ordinary sentences snapping mid-word at each line end. `break-word` still
   splits a genuinely unbreakable string (a long URL) so nothing overflows.
   Both surfaces get attacked by themes, so this stays shared. */
.w2dc-listing-content,
.w2dc-listing-content * {
	overflow-wrap: break-word;
	word-break: normal;
}
/* POPUP ONLY - force the default arrow cursor so the map's grab cursor doesn't
   bleed through the floating popup. A listing card is not over the map, so it
   keeps the browser-default cursors (this used to sit on the shared scope and
   forced `cursor: default` onto card content too). Links/buttons opt back into
   pointer via the shared rules below. */
.w2dc-react-infowindow-content,
.w2dc-react-infowindow-content * {
	cursor: default;
}
/* Vertical rhythm between the popup-field rows. Each row's own
 * `.w2dc-listing-content__field` already has `margin: 0`, so without an
 * explicit gap on the parent stack the icon column rows sit flush
 * against each other and the "Email", "Phone", "Address" lines run
 * together. Flex column with `gap: 8px` gives a consistent
 * inter-row breathing space that matches the in-row icon/value
 * gap declared on `.w2dc-listing-content__field`. Applies in both contexts
 * the class wraps - popup card body AND sidebar listing card -
 * because the markup is identical between the two surfaces. */
.w2dc-listing-content {
	display: flex;
	flex-direction: column;
	gap: 8px;
}
.w2dc-listing-content a,
.w2dc-listing-content button,
.w2dc-listing-content [role="button"] {
	cursor: pointer;
}
.w2dc-listing-content input,
.w2dc-listing-content textarea,
.w2dc-listing-content select {
	cursor: text;
}
.w2dc-listing-content input[type="checkbox"],
.w2dc-listing-content input[type="radio"] {
	cursor: pointer;
}

/* Popup-only scrollbar + right-edge gutter. The sidebar card has
 * its own scroll context (`.w2dc-react-sidebar__scroll`) and does
 * NOT need either the padding gutter or a custom scrollbar inside
 * the card itself - these rules stay scoped to the popup wrapper.
 * Popup body has BOTH classes (`w2dc-react-infowindow-content
 * w2dc-listing-content`); sidebar card has only `w2dc-listing-content`.
 *
 * Win 11-style thin scrollbar that thickens on hover. The container
 * width is always 14 px; the thumb sits inside a transparent border
 * that we shrink on hover so the thumb appears to expand smoothly
 * without the layout reflowing. Firefox honours scrollbar-width /
 * -color but cannot animate the width. */
.w2dc-react-infowindow-content {
	scrollbar-width: thin;
	scrollbar-color: rgba(0, 0, 0, 0.25) transparent;
	/* Scrollbar gutter is conditional - see the
	   `--has-scroll` rule near the bottom of this file for the
	   4 px right padding that kicks in only when the body actually
	   overflows. Without overflow we'd otherwise carry a visible
	   asymmetric gap on the right edge of every popup. */
}
.w2dc-react-infowindow-content::-webkit-scrollbar { width: 14px; height: 14px; }
.w2dc-react-infowindow-content::-webkit-scrollbar-track,
.w2dc-react-infowindow-content::-webkit-scrollbar-corner { background: transparent; }
.w2dc-react-infowindow-content::-webkit-scrollbar-thumb {
	background-color: rgba(0, 0, 0, 0.2);
	background-clip: padding-box;
	border: 5px solid transparent;
	border-radius: 7px;
	-webkit-transition: border-width 0.18s ease, background-color 0.18s ease;
	transition: border-width 0.18s ease, background-color 0.18s ease;
}
.w2dc-react-infowindow-content:hover::-webkit-scrollbar-thumb {
	border-width: 3px;
	background-color: rgba(0, 0, 0, 0.32);
}
.w2dc-react-infowindow-content::-webkit-scrollbar-thumb:hover {
	background-color: rgba(0, 0, 0, 0.5);
	border-width: 2px;
}
.w2dc-listing-content .w2dc-listing-content__body {
	display: flex;
	flex-direction: column;
	gap: 10px;
	font-size: 13px;
	color: #1f2937;
}
.w2dc-listing-content .w2dc-listing-content__title {
	/* The popup-fields "Title" row can carry a per-map font size
	 * (popup_sources row `title_fs`, 9-25) - build_popup_html then inlines
	 * `style="font-size:Npx"` on the h3, which wins over this default. */
	font-size: 16px;
	font-weight: 600;
	margin: 0;
	line-height: 1.3;
	color: #111827;
}
/* Inline edit-pencil - sits flush after the title text. Server-side
 * w2dc_render_popup_source injects this anchor inside the h3 when
 * the current user has edit permission on the listing (admin URL)
 * or owns it (dashboard URL). The cardinal sin to avoid: don't
 * underline-decorate or visually loaden the link inside a heading
 * - it should read as an icon-only action, not a hyperlink inside
 * the title. `vertical-align: baseline` keeps the icon visually
 * centred with the title's cap-height (pure middle alignment makes
 * the pencil sit ~2px too low against 16px text). The sidebar
 * card's click handler watches `data-w2dc-no-card-click` so a
 * click here doesn't ALSO open the popup. */
.w2dc-listing-content .w2dc-listing-content__edit-link {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	margin-left: 6px;
	padding: 2px;
	color: #6b7280;
	border-radius: 3px;
	vertical-align: baseline;
	text-decoration: none;
	transition: color 0.12s, background 0.12s;
	cursor: pointer;
}
.w2dc-listing-content .w2dc-listing-content__edit-link:hover,
.w2dc-listing-content .w2dc-listing-content__edit-link:focus {
	color: var(--w2dc-primary, #2563eb);
	background: #eff6ff;
	outline: none;
}
/* The universal `.w2dc-listing-content *` rule above sets
 * `cursor: default` on every descendant - including the SVG inside
 * this anchor. When the SVG covers nearly the whole 17×17 hit area
 * the cursor stays default even though the parent <a> has
 * `cursor: pointer`. Selector specificity (0,2,1) beats the
 * universal-descendant rule (0,1,0) so the override sticks without
 * `!important`. Also stops `pointer-events: none` from being
 * implicitly set so the SVG forwards clicks to the anchor.
 *
 * Sidebar parallel: when `.is-clickable` cards force `cursor:
 * pointer` on every descendant the visual is fine either way - but
 * non-clickable sidebars (popups disabled) also benefit from this
 * explicit override. */
.w2dc-listing-content .w2dc-listing-content__edit-link,
.w2dc-listing-content .w2dc-listing-content__edit-link * {
	cursor: pointer;
}
.w2dc-listing-content .w2dc-listing-content__edit-link svg {
	display: block;
	pointer-events: none;
}
.w2dc-listing-content .w2dc-listing-content__field {
	display: grid;
	grid-template-columns: 18px 1fr;
	gap: 8px;
	align-items: start;
	margin: 0;
}
/* Per-type icon glyph injected via :before - uses FontAwesome class names that
 * already ship with the plugin (w2dc-fa-*). */
.w2dc-listing-content .w2dc-listing-content__field::before {
	font-family: 'FontAwesome';
	font-weight: 400;
	font-style: normal;
	font-size: 14px;
	color: var(--w2dc-primary, #2563eb);
	line-height: 1.4;
	text-align: center;
}
.w2dc-listing-content .w2dc-listing-content__field--content::before    { content: '\f15c'; } /* file-text */
.w2dc-listing-content .w2dc-listing-content__field--excerpt::before    { content: '\f036'; } /* align-left */
.w2dc-listing-content .w2dc-listing-content__field--address::before    { content: '\f041'; } /* map-marker */
.w2dc-listing-content .w2dc-listing-content__field--categories::before { content: '\f02c'; } /* tags */
.w2dc-listing-content .w2dc-listing-content__field--tags::before       { content: '\f02b'; } /* tag */
.w2dc-listing-content .w2dc-listing-content__field--hours::before      { content: '\f017'; } /* clock-o */
.w2dc-listing-content .w2dc-listing-content__field--datetime::before   { content: '\f073'; } /* calendar */
.w2dc-listing-content .w2dc-listing-content__field--price::before      { content: '\f155'; } /* dollar */
.w2dc-listing-content .w2dc-listing-content__field--number::before     { content: '\f292'; } /* hashtag */
.w2dc-listing-content .w2dc-listing-content__field--phone::before      { content: '\f095'; } /* phone */
.w2dc-listing-content .w2dc-listing-content__field--email::before      { content: '\f0e0'; } /* envelope */
.w2dc-listing-content .w2dc-listing-content__field--website::before    { content: '\f0c1'; } /* link */
.w2dc-listing-content .w2dc-listing-content__field--fileupload::before { content: '\f0c6'; } /* paperclip */
.w2dc-listing-content .w2dc-listing-content__field--list::before,
.w2dc-listing-content .w2dc-listing-content__field--select::before,
.w2dc-listing-content .w2dc-listing-content__field--radio::before,
.w2dc-listing-content .w2dc-listing-content__field--checkbox::before   { content: '\f00b'; } /* list-ul */
.w2dc-listing-content .w2dc-listing-content__field--text::before,
.w2dc-listing-content .w2dc-listing-content__field--string::before,
.w2dc-listing-content .w2dc-listing-content__field--textarea::before   { content: '\f031'; } /* font */

/* Price is bumped two pixels above the body baseline (15 instead of
 * the popup-default 13) - applies in both the popup AND the sidebar
 * since `.w2dc-listing-content` is the shared wrapper class for
 * both surfaces. The `::before` icon keeps its own explicit
 * `font-size: 14px` rule above, so this only affects the value text. */
.w2dc-listing-content .w2dc-listing-content__field--price {
	font-size: 15px;
}

/* The rating field carries NO extra bottom margin - it uses the same 8px stack gap as every other field. */

/* Per-field custom icon override - when the admin picked an icon in the
 * form-builder Field Properties panel, PHP renders an inline <i> and
 * tags the wrapper with `--has-custom-icon`. Suppress the type-default
 * pseudo so the two don't stack, and style the inline element to fit
 * the same grid column the pseudo would have occupied. */
.w2dc-listing-content .w2dc-listing-content__field--has-custom-icon::before {
	content: none;
}
.w2dc-listing-content .w2dc-listing-content__icon {
	font-size: 14px;
	color: var(--w2dc-primary, #2563eb);
	line-height: 1.4;
	text-align: center;
}

/* Per-source "hide icon" - the admin unticked the Popups-metabox
 * "show icon" checkbox for this field. Collapse the reserved 18 px
 * icon column to a single value column and suppress BOTH icon
 * sources (the type-default `::before` glyph and any custom inline
 * `<i class="w2dc-listing-content__icon">`). PHP tags the wrapper with
 * `--no-icon` (see `w2dc_build_popup_html`). */
.w2dc-listing-content .w2dc-listing-content__field--no-icon {
	grid-template-columns: 1fr;
}
/* `!important` - necessary because per-type `::before` rules
 * (`--contact_form::before { content: '\f0e0' }`, `--email::before`,
 * `--phone::before`, ...) live LATER in this file and would otherwise
 * win on equal specificity. The `--no-icon` modifier is an explicit
 * admin opt-out, so it must override regardless of source order. */
.w2dc-listing-content .w2dc-listing-content__field--no-icon::before {
	content: none !important;
}
.w2dc-listing-content .w2dc-listing-content__field--no-icon .w2dc-listing-content__icon {
	display: none;
}

/* Title doesn't get an icon column - span both grid cells. */
.w2dc-listing-content .w2dc-listing-content__title {
	margin-bottom: 4px;
}

.w2dc-listing-content .w2dc-listing-content__label {
	font-size: 11px;
	font-weight: 600;
	text-transform: uppercase;
	color: #6b7280;
	letter-spacing: 0.4px;
	margin-bottom: 2px;
}
.w2dc-listing-content .w2dc-listing-content__value {
	line-height: 1.4;
	min-width: 0;        /* allow ellipsis inside grid cell */
	/* Force a normal block box. `display: -webkit-box` (used by the old 4-line
	   clamp, and something themes/page-builders also reach for) makes the
	   browser lay text out with its own line breaker, which IGNORES
	   overflow-wrap/word-break and splits words at the end of every line -
	   worst in Firefox. Stating `display: block` on the base value rule means
	   no `-webkit-box` on this element, from us or the site, can bring the
	   symptom back. The popup-only clamp below re-trims by height, not box. */
	display: block;
	word-wrap: break-word;
	/* break-word (not `anywhere`): wrap at spaces, and break INSIDE a word only
	   when a single word can't fit its own line. `anywhere` inserts soft-wrap
	   points mid-word, so in a narrow grid card words snapped at random chars. */
	overflow-wrap: break-word;
	word-break: normal;  /* guard against a theme's inherited word-break: break-all */
}
.w2dc-listing-content .w2dc-listing-content__value p { margin: 0; }
.w2dc-listing-content .w2dc-listing-content__value p + p { margin-top: 4px; }

/* Long text values (Content / excerpt / textarea / text / string).
 *
 * Plain block text that wraps at spaces. This used to be a `-webkit-box` +
 * `-webkit-line-clamp: 4` trim: that legacy box layout ignores
 * `overflow-wrap` / `word-break`, so words were split at the end of EVERY
 * line and no wrapping rule - ours or a site's own override - could stop it.
 * The wrapping properties are restated here so this rule, the most specific
 * one on these values, cannot be undercut. */
.w2dc-listing-content .w2dc-listing-content__field--content .w2dc-listing-content__value,
.w2dc-listing-content .w2dc-listing-content__field--excerpt .w2dc-listing-content__value,
.w2dc-listing-content .w2dc-listing-content__field--textarea .w2dc-listing-content__value,
.w2dc-listing-content .w2dc-listing-content__field--text .w2dc-listing-content__value,
.w2dc-listing-content .w2dc-listing-content__field--string .w2dc-listing-content__value {
	display: block;
	overflow-wrap: break-word;
	word-break: normal;
	hyphens: none;
}

/* Trimming these values to ~4 lines belongs to the MAP POPUP alone - a bubble
 * floating over the map has to stay small. On a listings CARD the description
 * IS the content, so it flows in full; the old rule was scoped to the shared
 * `.w2dc-listing-content` and silently clipped card text too.
 * 5.6em = 4 lines at the value's 1.4 line-height. */
.w2dc-react-infowindow-content .w2dc-listing-content__field--content .w2dc-listing-content__value,
.w2dc-react-infowindow-content .w2dc-listing-content__field--excerpt .w2dc-listing-content__value,
.w2dc-react-infowindow-content .w2dc-listing-content__field--textarea .w2dc-listing-content__value,
.w2dc-react-infowindow-content .w2dc-listing-content__field--text .w2dc-listing-content__value,
.w2dc-react-infowindow-content .w2dc-listing-content__field--string .w2dc-listing-content__value {
	max-height: 5.6em;
	overflow: hidden;
}
/* Paragraphs run together only inside the trimmed popup value, so the 4-line
 * budget is not eaten by paragraph gaps. Cards keep normal paragraphs. */
.w2dc-react-infowindow-content .w2dc-listing-content__field--content .w2dc-listing-content__value p,
.w2dc-react-infowindow-content .w2dc-listing-content__field--excerpt .w2dc-listing-content__value p,
.w2dc-react-infowindow-content .w2dc-listing-content__field--textarea .w2dc-listing-content__value p,
.w2dc-react-infowindow-content .w2dc-listing-content__field--text .w2dc-listing-content__value p,
.w2dc-react-infowindow-content .w2dc-listing-content__field--string .w2dc-listing-content__value p {
	display: inline;
}

/* Single-line truncation for fields that should never wrap to a new line. */
.w2dc-listing-content .w2dc-listing-content__field--phone .w2dc-listing-content__value,
.w2dc-listing-content .w2dc-listing-content__field--website .w2dc-listing-content__value,
.w2dc-listing-content .w2dc-listing-content__field--email .w2dc-listing-content__value {
	white-space: nowrap;
	overflow: hidden;
	text-overflow: ellipsis;
}
.w2dc-listing-content .w2dc-listing-content__field--phone .w2dc-listing-content__value p,
.w2dc-listing-content .w2dc-listing-content__field--website .w2dc-listing-content__value p,
.w2dc-listing-content .w2dc-listing-content__field--email .w2dc-listing-content__value p {
	white-space: nowrap;
	overflow: hidden;
	text-overflow: ellipsis;
}
.w2dc-listing-content .w2dc-listing-content__field--phone .w2dc-listing-content__value a,
.w2dc-listing-content .w2dc-listing-content__field--website .w2dc-listing-content__value a,
.w2dc-listing-content .w2dc-listing-content__field--email .w2dc-listing-content__value a {
	display: inline-block;
	max-width: 100%;
	overflow: hidden;
	text-overflow: ellipsis;
	vertical-align: bottom;
}

/* Images - single logo OR mini slider depending on count.
 * Both span the full popup width (icon column + value cell). */
.w2dc-listing-content .w2dc-listing-content__images {
	grid-column: 1 / -1;
}

/* CARD layout - on a listings-grid card the images field is the card's COVER
 * image: it runs flush to the card's top and side edges, with only the TOP
 * corners rounded so it meets the card body squarely (the bottom edge stays
 * flat). Scoped to `.w2dc-card-fields` - the fields-driven ListingCard - so
 * map popups and the sidebar keep their inset image.
 *
 * The negative margins cancel CardContent's padding; they mirror ListingCard's
 * `sx` (px: 2 = 16px, py: 1.5 = 12px) and must be changed together with it.
 * The image can be the first child, or the second when a sticky/featured badge
 * is prepended - that badge is the only top-level <span> in the block (fields
 * render as <div class="w2dc-listing-content__field …"> and the title as <h3>). */
.w2dc-card-fields > .w2dc-listing-content__body > .w2dc-listing-content__images:first-child,
.w2dc-card-fields > .w2dc-listing-content__body > span:first-child + .w2dc-listing-content__images {
	margin: -12px -16px 0;
	/* Matches the Card's own 8px radius (ListingCard `sx.borderRadius`). */
	border-radius: 8px 8px 0 0;
	overflow: hidden;
}
/* The single-image variant rounds the <img> itself (see `--single img` below);
 * inside a card the wrapper above owns the corners, so square the image off or
 * its rounded bottom would cut into the card body. */
.w2dc-card-fields > .w2dc-listing-content__body > .w2dc-listing-content__images:first-child img,
.w2dc-card-fields > .w2dc-listing-content__body > .w2dc-listing-content__images:first-child .sp-gallery__item,
.w2dc-card-fields > .w2dc-listing-content__body > span:first-child + .w2dc-listing-content__images img,
.w2dc-card-fields > .w2dc-listing-content__body > span:first-child + .w2dc-listing-content__images .sp-gallery__item {
	border-radius: 0;
}

/* MAP POPUP - the same cover-image treatment.
 *
 * The popup's content box is a SCROLL container (`maxHeight` + `overflow:
 * auto/hidden` in PopupCard), so an image pulled outside it with negative
 * margins is clipped, not shown. The 4px gutter therefore lives HERE, on the
 * popup's own wrapper, and the image simply cancels its parent's padding -
 * staying inside the scroll box while sitting on the card's edge. PopupCard's
 * outer Box carries no padding for the same reason; keep the two in sync.
 * Only applies when the images field is the first one in the popup's field
 * list, so a popup that leads with the title is untouched. */
.w2dc-react-infowindow-content > .w2dc-listing-content__body {
	padding: 4px;
}
.w2dc-react-infowindow-content > .w2dc-listing-content__body > .w2dc-listing-content__images:first-child,
.w2dc-react-infowindow-content > .w2dc-listing-content__body > span:first-child + .w2dc-listing-content__images {
	margin: -4px -4px 0;
	border-radius: 8px 8px 0 0;
	overflow: hidden;
}
.w2dc-react-infowindow-content > .w2dc-listing-content__body > .w2dc-listing-content__images:first-child img,
.w2dc-react-infowindow-content > .w2dc-listing-content__body > .w2dc-listing-content__images:first-child .sp-gallery__item,
.w2dc-react-infowindow-content > .w2dc-listing-content__body > span:first-child + .w2dc-listing-content__images img,
.w2dc-react-infowindow-content > .w2dc-listing-content__body > span:first-child + .w2dc-listing-content__images .sp-gallery__item {
	border-radius: 0;
}
/* A scrolling popup reserves a 4px scrollbar gutter on the right
 * (`--has-scroll`); pull the cover image over it too, otherwise it would sit
 * flush left but 4px short on the right. */
.w2dc-react-infowindow-content--has-scroll > .w2dc-listing-content__body > .w2dc-listing-content__images:first-child,
.w2dc-react-infowindow-content--has-scroll > .w2dc-listing-content__body > span:first-child + .w2dc-listing-content__images {
	margin-right: -8px;
}

/* Featured / sticky badge. w2dc prepends `<span class="w2dc-sticky-badge">` as
 * the block's first child via the `w2dc_popup_prepend_html` filter (see
 * directory_plans.php). Its BASE style must live here - with the shared popup
 * markup - not only in the w2dc FRONT stylesheet (directory-front.css), because
 * the admin Edit-Map live preview loads this react bundle but NOT the front CSS,
 * so the badge rendered there was unstyled ("Featured" as plain inline text).
 * Values mirror directory-front.css; that copy stays for front grid cards.
 * (`w2dc-sticky-badge` carries no `w2dc-` token, so the product codemod leaves
 * it verbatim - the rule is simply inert in the rocket-maps build, which has no
 * featured listings.) */
.w2dc-sticky-badge {
	position: absolute;
	top: 6px;
	right: 6px;
	z-index: 3;
	pointer-events: none;
	background: #e8452c;
	color: #fff;
	font-size: 10px;
	font-weight: 700;
	line-height: 1;
	letter-spacing: 0.04em;
	text-transform: uppercase;
	padding: 3px;
	border-radius: 3px;
	box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
}
/* Card cover image runs flush to the card edges, so shift the badge out of the
 * still-padded body box back into the image corner (mirrors directory-front.css). */
.w2dc-card-fields > .w2dc-listing-content__body > .w2dc-sticky-badge {
	top: -6px;
	right: -6px;
}

/* Single logo - fills the container width, height scales by aspect
 * ratio. Same shape works in BOTH render contexts:
 *   - popup     -> fills popup width
 *   - sidebar   -> fills card width = full sidebar (left/right vertical
 *                  layout) OR column width (top/bottom grid layout,
 *                  where `.w2dc-react-sidebar__grid--grid` auto-fills
 *                  with `repeat(auto-fill, minmax(220px, 1fr))`)
 * The earlier `height: 140px; width: auto` cropped the image to a
 * 140-px-tall band even when way more vertical space was available
 * - confusing UX, especially for portrait-oriented listing logos
 * that ended up tiny. */
.w2dc-listing-content .w2dc-listing-content__images--single {
	display: flex;
	justify-content: center;
}
/* Lightbox wraps the single logo in .sp-gallery__item, whose grid 4:3 box +
   grey fill would letterbox a natural-aspect logo (image pinned to the top,
   grey strip below). For the SINGLE image, drop the box and keep the native
   contain sizing; only the multi-image 4:3 slider wants the shared box. */
.w2dc-listing-content .w2dc-listing-content__images--single .sp-gallery__item {
	aspect-ratio: auto;
	background: none;
	overflow: visible;
	width: 100%;
}
.w2dc-listing-content .w2dc-listing-content__images--single .sp-gallery__item > img {
	height: auto;
	-o-object-fit: contain;
	   object-fit: contain;
}
.w2dc-listing-content .w2dc-listing-content__images--single img {
	width: 100%;
	height: auto;
	max-width: 100%;
	-o-object-fit: contain;
	   object-fit: contain;
	border-radius: 4px;
	display: block;
}

/* Multi-image slider - arrow-driven, scrollbar hidden. `overflow-x: auto`
 * (instead of hidden) lets users swipe between slides with their finger on
 * mobile; the scroll-snap stops naturally on each slide. The scrollbar is
 * suppressed via `scrollbar-width: none` + ::-webkit-scrollbar so the
 * gallery still looks clean. */
.w2dc-listing-content .w2dc-listing-content__images--slider {
	position: relative;
}
.w2dc-listing-content .w2dc-listing-content__slider-track {
	display: flex;
	overflow-x: auto;
	overflow-y: hidden;
	scroll-snap-type: x mandatory;
	scroll-behavior: smooth;
	-webkit-overflow-scrolling: touch;
	scrollbar-width: none;
	/* Lets the browser keep horizontal swipes on the slider while still
	 * bubbling vertical scroll up to the sidebar. Without this, a swipe that
	 * starts diagonally can be claimed by the parent and the slide never
	 * advances. */
	touch-action: pan-x pan-y;
}
.w2dc-listing-content .w2dc-listing-content__slider-track::-webkit-scrollbar { display: none; }
.w2dc-listing-content .w2dc-listing-content__slide {
	flex: 0 0 100%;
	scroll-snap-align: start;
	display: flex;
	justify-content: center;
}
.w2dc-listing-content .w2dc-listing-content__slide img {
	width: 100%;
	height: auto;
	max-width: 100%;
	-o-object-fit: contain;
	   object-fit: contain;
	border-radius: 4px;
	display: block;
}
/* MAP POPUP slider: pin every slide to a uniform 4:3 box and CROP
 * (object-fit: cover) so images with different aspect ratios all render at
 * the same height - no white letterbox bands, and the card can't change
 * height between slides. Same treatment the sidebar GRID already uses
 * (see sidebar.css). Scoped to `.w2dc-react-infowindow-content` (the map
 * popup's content class, set by PopupCard) so the sidebar LIST keeps its
 * deliberate natural-height images. Single-image popups stay `contain`
 * (one image = no ragged comparison, and logos shouldn't be cropped). */
.w2dc-react-infowindow-content .w2dc-listing-content__slide {
	aspect-ratio: 4 / 3;
}
.w2dc-react-infowindow-content .w2dc-listing-content__slide img {
	width: 100%;
	height: 100%;
	aspect-ratio: 4 / 3;
	max-width: none;
	-o-object-fit: cover;
	   object-fit: cover;
}
/* FILL mode (admin toggle on the popup's Images field): show the WHOLE image
 * (contain) inside the same 4:3 box, and fill the box behind it with a
 * blurred, zoomed copy of that same image (--w2dc-slide-bg, set per slide by
 * build_popup_html). No cropping, no empty bands, uniform height. Overrides
 * the `cover` default above. */
.w2dc-react-infowindow-content .w2dc-listing-content__images--fill .w2dc-listing-content__slide {
	position: relative;
	overflow: hidden;
}
.w2dc-react-infowindow-content .w2dc-listing-content__images--fill .w2dc-listing-content__slide::before {
	content: '';
	position: absolute;
	inset: 0;
	background-image: var(--w2dc-slide-bg);
	background-size: cover;
	background-position: center;
	filter: blur(14px);
	transform: scale(1.15);
	z-index: 0;
}
.w2dc-react-infowindow-content .w2dc-listing-content__images--fill .w2dc-listing-content__slide img {
	position: relative;
	z-index: 1;
	-o-object-fit: contain;
	   object-fit: contain;
}
/* Arrow buttons - circular overlay on track edges, symmetric inset. The
 * `touch-action: manipulation` removes the 300 ms tap delay and stops the
 * browser from interpreting a quick tap as the start of a scroll gesture
 * (which would swallow the click). */
.w2dc-listing-content .w2dc-listing-content__slider-btn {
	position: absolute;
	top: 50%;
	transform: translateY(-50%);
	width: 32px;
	height: 32px;
	border-radius: 50%;
	border: none;
	background: rgba(0, 0, 0, 0.55);
	color: #fff;
	cursor: pointer;
	display: flex;
	align-items: center;
	justify-content: center;
	z-index: 2;
	transition: background 0.15s;
	padding: 0;
	touch-action: manipulation;
	-webkit-tap-highlight-color: transparent;
}
.w2dc-listing-content .w2dc-listing-content__slider-btn:hover { background: rgba(0, 0, 0, 0.78); }
.w2dc-listing-content .w2dc-listing-content__slider-btn > .w2dc-fa {
	font-family: 'FontAwesome';
	font-style: normal;
	font-weight: 900;        /* bolder chevron stroke */
	font-size: 16px;
	line-height: 1;
}
.w2dc-listing-content .w2dc-listing-content__slider-prev { left: 8px; }
.w2dc-listing-content .w2dc-listing-content__slider-next { right: 8px; }
/* On a CARD the cover image runs to the card's own edges, so the 8px above
 * leaves the arrows hugging the card border. Give them more air there; map
 * popups and the sidebar keep their inset image and the tighter 8px. */
.w2dc-card-fields .w2dc-listing-content__slider-prev { left: 12px; }
.w2dc-card-fields .w2dc-listing-content__slider-next { right: 12px; }
/* Larger hit area on touch screens - visual button stays the same, the
 * `::before` pseudo extends the tap target without shifting layout. */
@media (hover: none) and (pointer: coarse) {
	.w2dc-listing-content .w2dc-listing-content__slider-btn {
		width: 40px;
		height: 40px;
	}
	.w2dc-listing-content .w2dc-listing-content__slider-btn::before {
		content: '';
		position: absolute;
		inset: -8px;
	}
}

/* Chips for list/select/radio/checkbox + categories/tags */
.w2dc-listing-content .w2dc-listing-content__chips {
	display: flex;
	flex-wrap: wrap;
	gap: 4px;
}
.w2dc-listing-content .w2dc-listing-content__chip {
	display: inline-flex;
	align-items: center;
	gap: 6px;
	padding: 3px 10px;
	background: #f3f4f6;
	border-radius: 12px;
	font-size: 12px;
	color: #374151;
	line-height: 1.3;
}
.w2dc-listing-content .w2dc-listing-content__chip > .w2dc-fa {
	font-family: 'FontAwesome';
	font-style: normal;
	font-weight: 400;
	font-size: 13px;
	line-height: 1;
	color: var(--w2dc-primary, #2563eb);
	display: inline-block;
}

/* Distance-to-search-centre chip - injected by Popup.tsx /
   MapSidebar's SidebarCard into the address field whenever an
   address+radius search is active. Sits inline at the end of the
   address text, picks up the same colour palette as the primary
   button, hover-title shows the entered/picked address. */
.w2dc-listing-content .w2dc-listing-content__distance {
	display: inline-block;
	margin-left: 6px;
	padding: 1px 8px;
	background: var(--w2dc-button-primary, var(--w2dc-primary, #2563eb));
	color: #ffffff;
	border-radius: 10px;
	font-size: 11px;
	font-weight: 500;
	line-height: 1.5;
	white-space: nowrap;
	cursor: help;
	vertical-align: baseline;
}

/* Hours table */
.w2dc-listing-content .w2dc-listing-content__hours,
.w2dc-listing-content .w2dc-listing-content__hours tr,
.w2dc-listing-content .w2dc-listing-content__hours td {
	border: 0;
	background: transparent;
	box-shadow: none;
}
.w2dc-listing-content .w2dc-listing-content__hours {
	width: 100%;
	border-collapse: collapse;
	font-size: 12px;
}
.w2dc-listing-content .w2dc-listing-content__hours tr + tr td {
	border-top: 1px solid #f3f4f6;   /* divider only between rows */
}
.w2dc-listing-content .w2dc-listing-content__hours td {
	padding: 2px 6px;
}
.w2dc-listing-content .w2dc-listing-content__hours td:first-child {
	color: #6b7280;
	width: 48px;
}
.w2dc-listing-content .w2dc-listing-content__hours tr.is-today td {
	font-weight: 600;
	color: #111827;
}
.w2dc-listing-content .w2dc-listing-content__hours tr.is-today td:first-child {
	color: #374151;
}

/* Status badge - sits above the 7-day schedule and mirrors the
 * marker label: green pill when "Open · HH:MM" or "N min left",
 * red pill when "Opens HH:MM" / "Opens Day HH:MM" / "Closed". */
.w2dc-listing-content .w2dc-listing-content__hours-status {
	display: inline-block;
	padding: 1px 8px;
	margin: 0 0 4px 0;
	font-size: 11px;
	font-weight: 600;
	border-radius: 999px;
	line-height: 18px;
}
.w2dc-listing-content .w2dc-listing-content__hours-status--open {
	background: #dcfce7;
	color: #166534;
}
.w2dc-listing-content .w2dc-listing-content__hours-status--closed {
	background: #fee2e2;
	color: #991b1b;
}

/* "Send message" button - replaces the inline contact form in the
 * popup body. Visual contract identical to `.w2dc-listing-content__directions-btn`
 * (same height, primary background, full-width); the two are listed
 * together below to keep the parallel obvious. Click dispatches
 * `START_CONTACT`, which mounts the ContactSidebar with the
 * extracted form HTML. */
.w2dc-listing-content .w2dc-listing-content__contact-btn {
	display: flex;
	width: 100%;
	box-sizing: border-box;
	align-items: center;
	justify-content: center;
	gap: 6px;
	height: 30px;
	min-height: 0;
	padding: 0 12px;
	font-size: 12px;
	font-weight: 600;
	line-height: 1;
	color: #fff;
	background: var(--w2dc-primary, #2563eb);
	border: 0;
	border-radius: 4px;
	cursor: pointer;
}
.w2dc-listing-content .w2dc-listing-content__contact-btn:hover { filter: brightness(1.05); }
/* Active state - symmetrical with `.w2dc-listing-content__directions-btn.is-active`
 * over in `directions.css`. PopupCard's effect adds `is-active` while
 * the contact sidebar is the live open panel for THIS marker; the
 * secondary-colour fill reads as "this is the open form's button"
 * vs the regular primary "click to open" variant. */
.w2dc-listing-content .w2dc-listing-content__contact-btn.is-active {
	background: var(--w2dc-button-secondary, var(--w2dc-secondary, #1d4ed8));
}
.w2dc-listing-content .w2dc-listing-content__contact-btn.is-active:hover { filter: brightness(1.05); }
.w2dc-listing-content .w2dc-listing-content__contact-btn .w2dc-fa {
	font-family: 'FontAwesome';
	font-style: normal;
	font-size: 12px;
}

/* Listing-name banner inside the contact form - server-injected
   `<div class="w2dc-listing-content__contact-listing">` above the inputs.
   Reads as a small "Contacting:" subtitle so the visitor sees which
   listing the message will reach without having to scroll back to
   the popup/card title. Subtle background tint (primary-colour
   wash) so it visually GROUPS into the form below it; not loud
   enough to compete with the popup's own title above. */
.w2dc-listing-content .w2dc-listing-content__contact-listing {
	font-size: 12px;
	font-weight: 600;
	color: var(--w2dc-primary, #2563eb);
	background: color-mix(in srgb, var(--w2dc-primary, #2563eb) 8%, transparent);
	padding: 6px 10px;
	border-radius: 6px;
	border-left: 3px solid var(--w2dc-primary, #2563eb);
	line-height: 1.3;
	margin: 0 0 14px;
	white-space: nowrap;
	overflow: hidden;
	text-overflow: ellipsis;
}

/* CF7 textarea - pin the rendered default to 6 rows regardless of
   what the CF7 admin stamped into the `rows="..."` attribute (often
   left at the CF7 default of 10 -> way too tall for a sidebar
   popup). Combining `height` with `resize: vertical` lets the
   visitor drag the corner handle wider when they're writing a long
   message, but the initial reveal lands at a friendly 6-row size.
   `!important` overrides any baseline rule shipped by the CF7
   stylesheet (cf7's own `.wpcf7-form-control` selector has higher
   default specificity than our `.w2dc-listing-content` scope; the
   bang keeps the contract intact without forcing every CF7 host
   site to reorder stylesheets). 6 rows × 1.5 line-height × 14 px
   inherited font ≈ 126 px. */
.w2dc-listing-content .wpcf7-form-control.wpcf7-textarea {
	height: 126px !important;
	min-height: 126px;
	resize: vertical;
}

/* Contact form */
.w2dc-listing-content .w2dc-listing-content__contact-form {
	display: flex;
	flex-direction: column;
	gap: 6px;
	/* Defensive reset against Redux Framework's admin CSS. In the
	 * admin map-edit preview the popup is rendered INSIDE the WP
	 * dashboard where redux-admin.min.css ships a `.no-js` rule
	 * (alert-danger styling: pink background, red border, red text,
	 * text-shadow) that gets applied to any `<form>` Redux's JS
	 * hasn't initialised yet. Our popup form never gets initialised
	 * by Redux, so the bleed-through is permanent until we override.
	 * Match the no-style baseline the frontend popup uses so the
	 * admin preview looks identical. */
	background: transparent !important;
	border: 0 !important;
	color: inherit !important;
	text-shadow: none !important;
	padding: 0 !important;
	margin: 0 !important;
}
/* Universal kill-switch for Redux's `.no-js` admin styling - applies to
 * ANY element with class `no-js` inside the popup, including the CF7
 * form wrapper (`.wpcf7-form`) and the RMAPS built-in form. Without
 * this, when the popup renders a CF7 shortcode (option
 * `w2dc_listing_contact_form_7`), the form's class is `wpcf7-form`,
 * not `.w2dc-listing-content__contact-form` - the more specific rules above
 * miss it. This selector catches both. The JS strip in Popup.tsx is
 * still the cleaner permanent fix (rebuild the React bundle to ship
 * it); this CSS is the no-build-required safety net. */
.w2dc-listing-content .no-js,
.w2dc-listing-content .wpcf7-form.no-js,
.w2dc-listing-content form.no-js {
	background: transparent !important;
	background-color: transparent !important;
	border: 0 !important;
	color: inherit !important;
	text-shadow: none !important;
	padding: 0 !important;
}
.w2dc-listing-content .no-js input,
.w2dc-listing-content .no-js textarea,
.w2dc-listing-content .wpcf7-form input,
.w2dc-listing-content .wpcf7-form textarea {
	text-shadow: none !important;
}
.w2dc-listing-content .w2dc-listing-content__contact-form input[type="text"],
.w2dc-listing-content .w2dc-listing-content__contact-form input[type="email"],
.w2dc-listing-content .w2dc-listing-content__contact-form textarea {
	width: 100%;
	box-sizing: border-box;
	padding: 0 8px;
	font-size: 12px;
	line-height: 1;
	border: 1px solid #d1d5db;
	border-radius: 4px;
	background: #fff;
	color: #1f2937;
	font-family: inherit;
	resize: vertical;
	/* Same defensive override as the form wrapper - Redux's
	 * `.no-js` admin CSS targets inputs / textareas too. */
	text-shadow: none !important;
}
/* Single-line inputs - explicit 30 px to match
 * `.w2dc-listing-content__contact-submit` + `.w2dc-listing-content__directions-btn`.
 * Textarea keeps its multi-line min/max height (next rule). */
.w2dc-listing-content .w2dc-listing-content__contact-form input[type="text"],
.w2dc-listing-content .w2dc-listing-content__contact-form input[type="email"] {
	height: 30px;
	min-height: 0;
}
.w2dc-listing-content .w2dc-listing-content__contact-form textarea {
	padding: 6px 8px;
	line-height: 1.4;
	min-height: 60px;
	max-height: 160px;
}
.w2dc-listing-content .w2dc-listing-content__contact-actions {
	display: flex;
	flex-direction: column;
	align-items: stretch;
	gap: 8px;
}
.w2dc-listing-content .w2dc-listing-content__contact-submit {
	display: flex;
	align-items: center;
	justify-content: center;
	width: 100%;
	box-sizing: border-box;
	/* See directions-btn - explicit height beats relying on padding +
	 * line-height that preflight's `font: inherit` can override. */
	height: 30px;
	min-height: 0;
	padding: 0 14px;
	font-size: 12px;
	font-weight: 600;
	line-height: 1;
	color: #fff;
	background: var(--w2dc-primary, #2563eb);
	border: none;
	border-radius: 4px;
	cursor: pointer;
	text-align: center;
}
.w2dc-listing-content .w2dc-listing-content__contact-submit:hover { filter: brightness(1.05); }
.w2dc-listing-content .w2dc-listing-content__contact-submit:disabled {
	opacity: 0.6;
	cursor: not-allowed;
}
.w2dc-listing-content .w2dc-listing-content__contact-status {
	font-size: 11px;
	color: #6b7280;
}
.w2dc-listing-content .w2dc-listing-content__contact-status[data-state="ok"]  { color: #15803d; }
.w2dc-listing-content .w2dc-listing-content__contact-status[data-state="err"] { color: #b91c1c; }

/* Field icon for the contact-form row uses an envelope. */
.w2dc-listing-content .w2dc-listing-content__field--contact_form::before { content: '\f0e0'; }

/* ============================================================
   Contact Form 7 - match the standard contact form's compact look
   ----------------------------------------------------------------
   When the admin pastes a CF7 shortcode into Listings -> Contact form
   -> "Contact Form 7 shortcode", `maps_manager.php :: contact_form`
   wraps `do_shortcode()`'s output in `.w2dc-listing-content__field--cf7` and
   we restyle CF7's default markup to mirror the plugin's own form
   geometry - column flex, compact 30 px buttons, full-width inputs,
   subtle status colours. Without this CF7 inherits its theme styles
   inside a 280-px popup, which usually looks oversized and drifts
   off the geometry the rest of the popup cards use.
   ============================================================ */
.w2dc-listing-content .w2dc-listing-content__field--cf7 .wpcf7-form {
	display: flex;
	flex-direction: column;
	gap: 6px;
	margin: 0;
}
/* CF7 wraps each field row in a `<p>` with bare label text + `<br>`
 * + the input span. Labels live in the paragraph's text nodes, not
 * inside `<label>` elements - so the row-level font-size/color rules
 * here are what styles "Your Name (required)" / etc. WP's admin
 * `load-styles.php` sets `p { font-size: 13px; line-height: 1.5;
 * margin: 1em 0 }` which bleeds into the in-admin map-edit popup
 * preview; `!important` keeps our overrides ahead of it without
 * having to chase specificity across two stylesheets. */
.w2dc-listing-content .w2dc-listing-content__field--cf7 .wpcf7-form p {
	margin: 0 !important;
	font-size: 12px !important;
	line-height: 1.4 !important;
	color: #374151;
}
/* Inputs / textarea / select - same baseline as the plugin's standard form */
.w2dc-listing-content .w2dc-listing-content__field--cf7 .wpcf7-form input[type="text"],
.w2dc-listing-content .w2dc-listing-content__field--cf7 .wpcf7-form input[type="email"],
.w2dc-listing-content .w2dc-listing-content__field--cf7 .wpcf7-form input[type="tel"],
.w2dc-listing-content .w2dc-listing-content__field--cf7 .wpcf7-form input[type="url"],
.w2dc-listing-content .w2dc-listing-content__field--cf7 .wpcf7-form input[type="number"],
.w2dc-listing-content .w2dc-listing-content__field--cf7 .wpcf7-form input[type="date"],
.w2dc-listing-content .w2dc-listing-content__field--cf7 .wpcf7-form select,
.w2dc-listing-content .w2dc-listing-content__field--cf7 .wpcf7-form textarea {
	width: 100%;
	box-sizing: border-box;
	padding: 0 8px;
	font-size: 12px;
	line-height: 1;
	border: 1px solid #d1d5db;
	border-radius: 4px;
	background: #fff;
	color: #1f2937;
	font-family: inherit;
	resize: vertical;
}
/* Single-line CF7 inputs - explicit 30 px to match popup buttons. */
.w2dc-listing-content .w2dc-listing-content__field--cf7 .wpcf7-form input[type="text"],
.w2dc-listing-content .w2dc-listing-content__field--cf7 .wpcf7-form input[type="email"],
.w2dc-listing-content .w2dc-listing-content__field--cf7 .wpcf7-form input[type="tel"],
.w2dc-listing-content .w2dc-listing-content__field--cf7 .wpcf7-form input[type="url"],
.w2dc-listing-content .w2dc-listing-content__field--cf7 .wpcf7-form input[type="number"],
.w2dc-listing-content .w2dc-listing-content__field--cf7 .wpcf7-form input[type="date"],
.w2dc-listing-content .w2dc-listing-content__field--cf7 .wpcf7-form select {
	height: 30px;
	min-height: 0;
}
.w2dc-listing-content .w2dc-listing-content__field--cf7 .wpcf7-form textarea {
	padding: 6px 8px;
	line-height: 1.4;
	min-height: 60px;
	max-height: 160px;
}
/* Some CF7 templates wrap the control in `<label>` instead of `<p>`
 * (admin can edit the form structure freely in the CF7 form editor).
 * Keep the same baseline as the `<p>` path so both shapes render
 * identically. */
.w2dc-listing-content .w2dc-listing-content__field--cf7 .wpcf7-form label {
	display: block;
	font-size: 12px;
	line-height: 1.4;
	color: #374151;
	margin: 0;
}
/* Submit button - explicit 30 px height matches `.w2dc-listing-content__contact-submit`.
 * `!important` defends against theme stylesheets that inject button
 * padding via low-specificity selectors. */
.w2dc-listing-content .w2dc-listing-content__field--cf7 .wpcf7-form .wpcf7-submit,
.w2dc-listing-content .w2dc-listing-content__field--cf7 .wpcf7-form input[type="submit"],
.w2dc-listing-content .w2dc-listing-content__field--cf7 .wpcf7-form button[type="submit"] {
	display: flex !important;
	align-items: center;
	justify-content: center;
	width: 100% !important;
	box-sizing: border-box;
	height: 30px !important;
	min-height: 0 !important;
	padding: 0 14px !important;
	font-size: 12px;
	font-weight: 600;
	line-height: 1;
	color: #fff !important;
	background: var(--w2dc-primary, #2563eb) !important;
	border: 0 !important;
	border-radius: 4px !important;
	cursor: pointer;
	text-align: center;
	text-transform: none;
}
.w2dc-listing-content .w2dc-listing-content__field--cf7 .wpcf7-form .wpcf7-submit:hover,
.w2dc-listing-content .w2dc-listing-content__field--cf7 .wpcf7-form input[type="submit"]:hover,
.w2dc-listing-content .w2dc-listing-content__field--cf7 .wpcf7-form button[type="submit"]:hover {
	filter: brightness(1.05);
}
.w2dc-listing-content .w2dc-listing-content__field--cf7 .wpcf7-form .wpcf7-submit:disabled,
.w2dc-listing-content .w2dc-listing-content__field--cf7 .wpcf7-form input[type="submit"]:disabled {
	opacity: 0.6;
	cursor: not-allowed;
}
/* CF7 status response (`.wpcf7-response-output`) - same colour
 * conventions as the plugin's `.w2dc-listing-content__contact-status`. CF7
 * adds class modifiers for the state, so we map all of them. */
.w2dc-listing-content .w2dc-listing-content__field--cf7 .wpcf7-response-output {
	margin: 6px 0 0 !important;
	padding: 6px 8px !important;
	font-size: 11px;
	border-radius: 4px;
	border: 1px solid transparent !important;
}
/* CF7 leaves the response container in the DOM even before the user
 * submits - pre-submit it carries our margin+padding but no text, so
 * it shows up as a lonely empty bar wedged between the form and the
 * popup edge. Collapse it to nothing whenever it has no content; CF7
 * fills it on submit, and the `:empty` selector flips back off the
 * moment any text or child node lands. `!important` defends against
 * the rule above also using `!important` for margin/padding. */
.w2dc-listing-content .w2dc-listing-content__field--cf7 .wpcf7-response-output:empty {
	display: none !important;
}
.w2dc-listing-content .w2dc-listing-content__field--cf7 form.sent .wpcf7-response-output,
.w2dc-listing-content .w2dc-listing-content__field--cf7 .wpcf7-mail-sent-ok {
	color: #15803d !important;
	background: #f0fdf4;
	border-color: #bbf7d0 !important;
}
.w2dc-listing-content .w2dc-listing-content__field--cf7 form.invalid .wpcf7-response-output,
.w2dc-listing-content .w2dc-listing-content__field--cf7 form.failed .wpcf7-response-output,
.w2dc-listing-content .w2dc-listing-content__field--cf7 .wpcf7-validation-errors,
.w2dc-listing-content .w2dc-listing-content__field--cf7 .wpcf7-mail-sent-ng,
.w2dc-listing-content .w2dc-listing-content__field--cf7 .wpcf7-spam-blocked {
	color: #b91c1c !important;
	background: #fef2f2;
	border-color: #fecaca !important;
}
/* Per-field validation tips */
.w2dc-listing-content .w2dc-listing-content__field--cf7 .wpcf7-not-valid-tip {
	font-size: 11px;
	color: #b91c1c;
	margin-top: 2px;
}
.w2dc-listing-content .w2dc-listing-content__field--cf7 .wpcf7-not-valid {
	border-color: #fca5a5 !important;
}
/* CF7's spinner sits next to the submit button - fine inline, just
 * shrink so it matches the compact button height. */
.w2dc-listing-content .w2dc-listing-content__field--cf7 .wpcf7-spinner {
	width: 18px;
	height: 18px;
	margin: 0 0 0 6px;
}

/* File list */
.w2dc-listing-content .w2dc-listing-content__files {
	margin: 0;
	padding-left: 18px;
}
.w2dc-listing-content .w2dc-listing-content__files a {
	color: var(--w2dc-primary, #2563eb);
	text-decoration: none;
}
.w2dc-listing-content .w2dc-listing-content__files a:hover { text-decoration: underline; }

/* ============================================================
   Popup-only overrides - tighter spacing and smaller fonts
   ----------------------------------------------------------------
   Every rule above is scoped to `.w2dc-listing-content`, which is
   carried by BOTH the popup body AND the sidebar listing cards.
   The popup body ALSO carries `.w2dc-react-infowindow-content`
   (sidebar cards don't), so prefixing override selectors with
   that class scopes them to popups only - sidebar listings keep
   the looser geometry the shared rules above set.

   Equal-specificity ties between
   `.w2dc-listing-content .w2dc-listing-content__X` and
   `.w2dc-react-infowindow-content .w2dc-listing-content__X` (both 0,2,0)
   resolve in CSS source order, so as long as this block stays
   AT THE BOTTOM of the file, the popup-only values win on the
   popup body and the shared values keep applying to sidebar
   cards.

   Sizing brief: popup title 14 px, chips 11 px, every margin /
   padding / gap declared above is halved here so the same
   server-rendered HTML reads denser inside the popup card while
   the sidebar listing card stays unchanged.
   ============================================================ */
/* Scrollbar gutter - only when the content actually overflows.
   `.w2dc-react-infowindow-content--has-scroll` is toggled by
   `PopupCard.tsx`'s overflow-observer effect alongside the
   `overflowY: auto/hidden` switch. */
.w2dc-react-infowindow-content.w2dc-react-infowindow-content--has-scroll {
	padding-right: 4px;
}
.w2dc-react-infowindow-content .w2dc-listing-content__body {
	gap: 5px;
}
.w2dc-react-infowindow-content .w2dc-listing-content__title {
	font-size: 14px;
}
/* The shared block sets a second `margin-bottom: 4px` on the
   title; halve it here. */
.w2dc-react-infowindow-content .w2dc-listing-content__title {
	margin-bottom: 2px;
}
.w2dc-react-infowindow-content .w2dc-listing-content__edit-link {
	margin-left: 3px;
	padding: 1px;
}
.w2dc-react-infowindow-content .w2dc-listing-content__field {
	gap: 4px;
}
.w2dc-react-infowindow-content .w2dc-listing-content__label {
	margin-bottom: 1px;
}
.w2dc-react-infowindow-content .w2dc-listing-content__value {
	font-size: 11px;
}
.w2dc-react-infowindow-content .w2dc-listing-content__value p + p {
	margin-top: 2px;
}
.w2dc-react-infowindow-content .w2dc-listing-content__chips {
	gap: 2px;
}
.w2dc-react-infowindow-content .w2dc-listing-content__chip {
	font-size: 11px;
	gap: 3px;
	padding: 2px 5px;
}
.w2dc-react-infowindow-content .w2dc-listing-content__hours td {
	padding: 1px 3px;
}
.w2dc-react-infowindow-content .w2dc-listing-content__hours-status {
	padding: 1px 4px;
	margin: 0 0 2px 0;
}
.w2dc-react-infowindow-content .w2dc-listing-content__contact-form {
	gap: 3px;
}
.w2dc-react-infowindow-content .w2dc-listing-content__contact-form input[type="text"],
.w2dc-react-infowindow-content .w2dc-listing-content__contact-form input[type="email"],
.w2dc-react-infowindow-content .w2dc-listing-content__contact-form textarea {
	padding: 3px 4px;
}
.w2dc-react-infowindow-content .w2dc-listing-content__contact-actions {
	gap: 4px;
}
.w2dc-react-infowindow-content .w2dc-listing-content__contact-submit {
	padding: 0 7px;
}
.w2dc-react-infowindow-content .w2dc-listing-content__field--cf7 .wpcf7-form {
	gap: 3px;
}
.w2dc-react-infowindow-content .w2dc-listing-content__field--cf7 .wpcf7-form input[type="text"],
.w2dc-react-infowindow-content .w2dc-listing-content__field--cf7 .wpcf7-form input[type="email"],
.w2dc-react-infowindow-content .w2dc-listing-content__field--cf7 .wpcf7-form input[type="tel"],
.w2dc-react-infowindow-content .w2dc-listing-content__field--cf7 .wpcf7-form input[type="url"],
.w2dc-react-infowindow-content .w2dc-listing-content__field--cf7 .wpcf7-form input[type="number"],
.w2dc-react-infowindow-content .w2dc-listing-content__field--cf7 .wpcf7-form input[type="date"],
.w2dc-react-infowindow-content .w2dc-listing-content__field--cf7 .wpcf7-form select,
.w2dc-react-infowindow-content .w2dc-listing-content__field--cf7 .wpcf7-form textarea {
	padding: 3px 4px;
}
.w2dc-react-infowindow-content .w2dc-listing-content__field--cf7 .wpcf7-form .wpcf7-submit,
.w2dc-react-infowindow-content .w2dc-listing-content__field--cf7 .wpcf7-form input[type="submit"],
.w2dc-react-infowindow-content .w2dc-listing-content__field--cf7 .wpcf7-form button[type="submit"] {
	padding: 0 7px !important;
}
.w2dc-react-infowindow-content .w2dc-listing-content__field--cf7 .wpcf7-response-output {
	margin: 3px 0 0 !important;
	padding: 3px 4px !important;
}
.w2dc-react-infowindow-content .w2dc-listing-content__field--cf7 .wpcf7-not-valid-tip {
	margin-top: 1px;
}
.w2dc-react-infowindow-content .w2dc-listing-content__field--cf7 .wpcf7-spinner {
	margin: 0 0 0 3px;
}
.w2dc-react-infowindow-content .w2dc-listing-content__files {
	padding-left: 9px;
}

/* Linked listing title (w2dc): when the listing's plan grants an own page,
 * the server wraps the h3 title text in .w2dc-listing-content__title-link (see
 * w2dc_popup_title_html). Coloured with the Customization → Links settings
 * vars - the same source as every other front link - falling back to the WP
 * defaults where the vars aren't emitted. Dormant on rocket-maps: nothing
 * ever emits the class there. */
.w2dc-listing-content__title-link {
	color: var(--w2dc-links, #2271b1);
	text-decoration: none;
}
.w2dc-listing-content__title-link:hover,
.w2dc-listing-content__title-link:focus {
	color: var(--w2dc-links-hover, #135e96);
	text-decoration: underline;
}

/* Click-to-pan affordance: the address inside a directory grid card / map sidebar
   item is clickable - it pans the map to the listing's marker (see
   utils/addressPan). Scoped via [data-post-id] (cards + sidebar items); the map
   popup itself has no data-post-id ancestor, so its address keeps a normal
   cursor and never pans-to-self. */
[data-post-id] .w2dc-listing-content__field--address {
	cursor: pointer;
}

/* Directions popup affordances.
 *
 * This stylesheet used to host the now-deleted top-mounted input
 * bar (`.w2dc-directions-bar*`) and the in-popup route summary
 * (`.w2dc-listing-content__route-stats`, `.w2dc-listing-content__route-steps`). The
 * rework moved both into the dedicated DirectionsSidebar (see
 * `directionsSidebar.css`). What stays here is the popup-side
 * surface area: the "Get directions" button rendered inside every
 * popup, and the global `.pac-container` z-index lift that keeps
 * the Google Places autocomplete dropdown above any other
 * map-anchored chrome.
 */

/* Google Places autocomplete dropdown - Google appends `.pac-container` to
 * <body>; bump z-index above any Material/Google overlays on the page so it
 * stays visible above the sidebar / popup / InfoWindow. */
.pac-container {
	z-index: 100000 !important;
}

.w2dc-listing-content .w2dc-listing-content__directions-btn {
	display: flex;
	width: 100%;
	box-sizing: border-box;
	align-items: center;
	justify-content: center;
	gap: 6px;
	/* Hard-pin the height (and zero out vertical padding) - relying on
	 * just `padding-y` + font/line-height made the button's final
	 * height depend on Tailwind preflight's `font: inherit` (which
	 * overrode line-height to whatever the InfoWindow container has).
	 * Explicit `height` + `padding: 0` + `line-height: 1` removes that
	 * variable, locking the button to 24 px. */
	height: 30px;
	min-height: 0;
	padding: 0 12px;
	font-size: 12px;
	font-weight: 600;
	line-height: 1;
	color: #fff;
	background: var(--w2dc-primary, #2563eb);
	border: 0;
	border-radius: 4px;
	cursor: pointer;
}
.w2dc-listing-content .w2dc-listing-content__directions-btn:hover { filter: brightness(1.05); }
/* Active state - set by PopupCard's `is-active` toggle while THIS
 * marker's directions sidebar is the live open panel. Reads as
 * "this is the button whose panel you're currently looking at",
 * not the regular "click me" CTA. Secondary palette colour so it
 * visibly STEPS DOWN from the primary call-to-action variant the
 * other popup's button still shows - admins themed for high
 * primary/secondary contrast get a clean toggle, sites with the
 * two close together still get a subtle "is open" hint. */
.w2dc-listing-content .w2dc-listing-content__directions-btn.is-active {
	background: var(--w2dc-button-secondary, var(--w2dc-secondary, #1d4ed8));
}
.w2dc-listing-content .w2dc-listing-content__directions-btn.is-active:hover { filter: brightness(1.05); }
.w2dc-listing-content .w2dc-listing-content__directions-btn .w2dc-fa {
	font-family: 'FontAwesome';
	font-style: normal;
	font-size: 12px;
}

.w2dc-listing-content .w2dc-listing-content__field--directions::before { content: '\f124'; } /* location-arrow */

/* Directions sidebar - left-anchored overlay that hosts the
 * starting-address form, a loader while the provider computes,
 * and the resulting distance/duration + step list. Visual contract
 * matches the listings sidebar (`sidebar.css`):
 *
 *   - white card + soft shadow background,
 *   - chevron toggle on the trailing edge that slides the panel
 *     off-screen via transform (no width change -> no map reflow),
 *   - same 0.25s ease transition timing,
 *   - same primary-coloured chip for the toggle and same hover
 *     darken,
 *   - thin Win 11-style scrollbar that thickens on hover for the
 *     internal scroll container.
 *
 * Differences from the listings sidebar:
 *   - always left-anchored (the design has only one slot for this
 *     panel - no admin metabox to move it elsewhere),
 *   - higher z-index (720) so it stacks above the listings sidebar
 *     (z 700) when both are open on the left edge,
 *   - mounted only while `state.directions` is set - no
 *     persistent open/closed preference in localStorage,
 *   - the toggle COLLAPSES the panel without dispatching
 *     `CLEAR_DIRECTIONS`. The × in the header is what clears the
 *     route. Distinguishing the two affordances lets the user park
 *     the directions panel off-screen to look at the map while the
 *     route is still drawn.
 */

.w2dc-react-directions-sidebar {
	position: absolute;
	top: 0;
	left: 0;
	bottom: 0;
	max-width: calc(100% - 36px);
	background: #fff;
	box-shadow: 0 0 8px rgba(0, 0, 0, 0.08);
	box-sizing: border-box;
	display: flex;
	flex-direction: column;
	/* Stacks above the listings sidebar (z 1600). Tied with ContactSidebar
	 * (also 1620) so neither panel paints OVER the other during the
	 * cross-fade swap on mobile - they cross-fade at the same z, the
	 * opacity transition (mobile @media below) handles the smooth
	 * blend instead of one eating the other from the top. The whole
	 * sidebar family sits ABOVE the click popup (z 1500) now, so a popup
	 * never covers any sidebar panel. */
	z-index: 1620;
	transform: translateX(0);
	transition: transform 0.25s ease, opacity 0.25s ease;
	will-change: transform, opacity;
	opacity: 1;
	color: #1f2937;
}
.w2dc-react-directions-sidebar.is-collapsed,
.w2dc-react-directions-sidebar.is-collapsed-up {
	transform: translateX(-100%);
}

/* Toggle button intentionally removed - the directions panel is a
 * transient task surface, not a persistent affordance the visitor
 * needs a one-click "park it" button for. The × in the header
 * dismisses the panel (and clears the route). */

/* Header - title + close X. The close is the explicit "I'm done
 * with this route" action; clicking it clears the route AND
 * unmounts the sidebar (CLEAR_DIRECTIONS resets state.directions
 * to null). */
.w2dc-react-directions-sidebar__header {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 8px;
	padding: 12px 14px;
	border-bottom: 1px solid #e5e7eb;
	background: #f9fafb;
	flex: 0 0 auto;
}
.w2dc-react-directions-sidebar__title {
	display: inline-flex;
	align-items: center;
	gap: 8px;
	font-size: 14px;
	font-weight: 600;
	color: #111827;
}
.w2dc-react-directions-sidebar__title .w2dc-fa {
	font-family: 'FontAwesome';
	font-style: normal;
	font-weight: 400;
	font-size: 14px;
	color: var(--w2dc-primary, #2563eb);
	line-height: 1;
}
button.w2dc-react-directions-sidebar__close,
button.w2dc-react-directions-sidebar__close:hover,
button.w2dc-react-directions-sidebar__close:focus,
button.w2dc-react-directions-sidebar__close:active {
	border: 0;
	background: none;
	background-color: transparent;
	font-size: 22px;
	line-height: 1;
	color: #6b7280;
	cursor: pointer;
	padding: 0 4px;
	box-shadow: none;
	outline: none;
}
button.w2dc-react-directions-sidebar__close:hover { color: #111827; }

/* Body - scroll container for the form + loader / route summary. */
.w2dc-react-directions-sidebar__body {
	flex: 1 1 auto;
	overflow-y: auto;
	overflow-x: hidden;
	padding: 14px;
	box-sizing: border-box;
	/* Win 11-style thin scrollbar - matches MapSidebar's scroll. */
	scrollbar-width: thin;
	scrollbar-color: rgba(0, 0, 0, 0.25) transparent;
}
.w2dc-react-directions-sidebar__body::-webkit-scrollbar {
	width: 14px;
	height: 14px;
}
.w2dc-react-directions-sidebar__body::-webkit-scrollbar-track,
.w2dc-react-directions-sidebar__body::-webkit-scrollbar-corner {
	background: transparent;
}
.w2dc-react-directions-sidebar__body::-webkit-scrollbar-thumb {
	background-color: rgba(0, 0, 0, 0.2);
	background-clip: padding-box;
	border: 5px solid transparent;
	border-radius: 7px;
	-webkit-transition: border-width 0.18s ease, background-color 0.18s ease;
	transition: border-width 0.18s ease, background-color 0.18s ease;
}
.w2dc-react-directions-sidebar__body:hover::-webkit-scrollbar-thumb {
	border-width: 3px;
	background-color: rgba(0, 0, 0, 0.32);
}
.w2dc-react-directions-sidebar__body::-webkit-scrollbar-thumb:hover {
	background-color: rgba(0, 0, 0, 0.5);
	border-width: 2px;
}

/* Destination row - static "To: ..." anchor above the form so the
 * visitor knows what the route is being computed TO without having
 * to look back at the marker on the map. Label uses the same
 * uppercase-tracked styling as the form field labels so the two
 * read as a single mini-table of "Destination / Starting address". */
.w2dc-react-directions-sidebar__dest {
	display: flex;
	flex-direction: column;
	gap: 4px;
	padding: 8px 10px;
	margin-bottom: 12px;
	background: #f9fafb;
	border: 1px solid #e5e7eb;
	border-radius: 6px;
}
.w2dc-react-directions-sidebar__dest-value {
	display: flex;
	align-items: flex-start;
	gap: 6px;
	font-size: 13px;
	color: #1f2937;
	line-height: 1.4;
	/* Same reasoning as `.w2dc-listing-content__value`: `anywhere` snapped addresses
	   mid-word in this narrow panel; `break-word` only splits a word that
	   cannot fit a line on its own. */
	overflow-wrap: break-word;
	word-break: normal;
}
.w2dc-react-directions-sidebar__dest-value .w2dc-fa {
	font-family: 'FontAwesome';
	font-style: normal;
	font-weight: 400;
	color: var(--w2dc-primary, #2563eb);
	font-size: 13px;
	line-height: 1.4;
	flex: 0 0 auto;
}
.w2dc-react-directions-sidebar__dest-value .w2dc-fa-map-marker::before {
	content: '\f041';
}

/* Form */
.w2dc-react-directions-sidebar__form {
	display: flex;
	flex-direction: column;
	gap: 8px;
}
.w2dc-react-directions-sidebar__field {
	display: flex;
	flex-direction: column;
	gap: 4px;
}
.w2dc-react-directions-sidebar__label {
	font-size: 11px;
	font-weight: 600;
	text-transform: uppercase;
	letter-spacing: 0.4px;
	color: #6b7280;
}
.w2dc-react-directions-sidebar__input-row {
	display: flex;
	align-items: center;
	gap: 6px;
	border: 1px solid #d1d5db;
	border-radius: 6px;
	background: #fff;
	padding: 0 6px;
	transition: border-color 0.12s, box-shadow 0.12s;
}
.w2dc-react-directions-sidebar__input-row:focus-within {
	border-color: var(--w2dc-primary, #2563eb);
	box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.16);
}
.w2dc-react-directions-sidebar__input {
	flex: 1 1 auto;
	min-width: 0;
	height: 32px;
	border: 0;
	outline: none;
	font-size: 13px;
	background: transparent;
	color: #1f2937;
	padding: 0 2px;
	font-family: inherit;
}
.w2dc-react-directions-sidebar__input::-moz-placeholder { color: #9ca3af; }
.w2dc-react-directions-sidebar__input::placeholder { color: #9ca3af; }
.w2dc-react-directions-sidebar__input:disabled { color: #6b7280; }

button.w2dc-react-directions-sidebar__find {
	border: 0;
	background: transparent;
	color: #9ca3af;
	cursor: pointer;
	width: 26px;
	height: 26px;
	padding: 0;
	border-radius: 50%;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	flex: 0 0 auto;
	transition: background 0.12s, color 0.12s;
}
button.w2dc-react-directions-sidebar__find:hover {
	background: #f3f4f6;
	color: #4b5563;
}
button.w2dc-react-directions-sidebar__find:focus {
	outline: none;
	background: #f3f4f6;
	color: #4b5563;
	box-shadow: 0 0 0 2px rgba(37, 99, 235, 0.25);
}
button.w2dc-react-directions-sidebar__find:disabled {
	opacity: 0.6;
	cursor: progress;
}

button.w2dc-react-directions-sidebar__go {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 100%;
	box-sizing: border-box;
	height: 32px;
	min-height: 0;
	padding: 0 14px;
	font-size: 13px;
	font-weight: 600;
	line-height: 1;
	color: #fff;
	background: var(--w2dc-primary, #2563eb);
	border: 0;
	border-radius: 6px;
	cursor: pointer;
	font-family: inherit;
}
button.w2dc-react-directions-sidebar__go:hover:not(:disabled) {
	filter: brightness(1.05);
}
button.w2dc-react-directions-sidebar__go:disabled {
	opacity: 0.6;
	cursor: not-allowed;
}

.w2dc-react-directions-sidebar__error {
	background: #fee2e2;
	color: #b91c1c;
	font-size: 12px;
	padding: 6px 10px;
	border-radius: 4px;
	border: 1px solid #fecaca;
}

/* Loading state - centred spinner + label while the provider
 * computes the route. Appears under the form so the user sees the
 * input they just submitted alongside the spinner (context).
 * Cleared the moment the route response lands. */
.w2dc-react-directions-sidebar__loading {
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	gap: 10px;
	padding: 24px 12px;
	color: #6b7280;
	font-size: 13px;
}

/* Route summary - distance / duration stats and step list. */
.w2dc-react-directions-sidebar__route {
	margin-top: 14px;
	border-top: 1px solid #e5e7eb;
	padding-top: 12px;
}
.w2dc-react-directions-sidebar__stats {
	display: flex;
	gap: 14px;
	font-size: 13px;
	color: #1f2937;
	margin-bottom: 10px;
}
.w2dc-react-directions-sidebar__stats > span {
	display: inline-flex;
	align-items: center;
	gap: 5px;
}
.w2dc-react-directions-sidebar__stats .w2dc-fa {
	font-family: 'FontAwesome';
	font-style: normal;
	font-weight: 400;
	color: var(--w2dc-primary, #2563eb);
	font-size: 13px;
	line-height: 1;
}

.w2dc-react-directions-sidebar__steps {
	margin: 0;
	padding-left: 22px;
	font-size: 12px;
	color: #374151;
}
.w2dc-react-directions-sidebar__steps li {
	padding: 6px 6px 6px 4px;
	margin: 2px 0;
	border-radius: 4px;
	cursor: pointer;
	transition: background 0.12s;
	line-height: 1.45;
}
.w2dc-react-directions-sidebar__steps li:hover {
	background: #fef3c7;            /* matches the amber map highlight */
}
/* Click-locked step - deeper amber + bold so the row stays visible
 * after the cursor leaves the list (the click handler keeps the map
 * highlight up until the user clicks again). */
.w2dc-react-directions-sidebar__steps li.is-selected,
.w2dc-react-directions-sidebar__steps li.is-selected:hover {
	background: #fde68a;
	color: #1f2937;
	font-weight: 600;
}
.w2dc-react-directions-sidebar__step-dist {
	color: #6b7280;
}

/* Mobile reflow - narrow viewports get a bottom drawer instead
 * of an edge panel. The configured width is replaced by full-width
 * and a fixed dynamic-viewport height, mirroring the spirit of the
 * listings sidebar's mobile drawer.
 *
 * Two iOS-Safari-driven choices here:
 *   1. `position: fixed` instead of `absolute`. Anchoring to the
 *      visual viewport means the drawer is independent of
 *      canvas-host's layout - any reflow of the map element (e.g.
 *      address-bar collapse changing the page chrome height) can't
 *      drag the drawer along, which is what produced the user's
 *      reported "map jerks UP" the instant the directions panel
 *      mounted. The drawer just stays glued to viewport bottom.
 *   2. `dvh` (dynamic viewport height) instead of `vh`. On iOS
 *      Safari `vh` is locked to the LARGEST possible viewport (no
 *      address bar), so 60vh always under-reports when the bar is
 *      visible - every state change that nudges the address bar
 *      causes the drawer to "snap" to a new height. `dvh`
 *      interpolates with the visual viewport, so the drawer's
 *      visible height stays stable across browser-chrome changes. */
@media (max-width: 768px) {
	.w2dc-react-directions-sidebar {
		position: fixed !important;
		top: auto;
		bottom: 0;
		left: 0;
		right: 0;
		width: 100% !important;
		max-width: 100%;
		height: 60dvh;
		max-height: 60dvh;
		box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.16);
	}
	/* Mobile slide animation - three states:
	 *   • `.is-open`              -> translateY(0), visible
	 *   • `.is-collapsed`         -> translateY(101%), off-screen
	 *                                BELOW. Normal close direction
	 *                                (× dismiss, no successor).
	 *   • `.is-collapsed-up`      -> translateY(-101%), off-screen
	 *                                ABOVE. Used when the other
	 *                                sidebar (contact) is taking
	 *                                over - directions slides UP off
	 *                                the top while contact slides UP
	 *                                from below at the same time.
	 *                                The two panels occupy DIFFERENT
	 *                                vertical bands during the
	 *                                transition (no overlap), so the
	 *                                visitor sees a clean conveyor-
	 *                                belt swap instead of a mashed
	 *                                cross-fade.
	 *
	 * Pinning the open state to translateY(0) is also necessary -
	 * without it the open state would inherit the desktop base's
	 * `translateX(0)`, and the browser would interpolate between two
	 * different transform functions during the slide, producing the
	 * "jumping from the top" the visitor reported. */
	.w2dc-react-directions-sidebar.is-open {
		transform: translateY(0);
		opacity: 1;
	}
	.w2dc-react-directions-sidebar.is-collapsed {
		transform: translateY(101%);
		opacity: 0;
	}
	.w2dc-react-directions-sidebar.is-collapsed-up {
		transform: translateY(-101%);
		opacity: 0;
	}
}

/* Contact-form sidebar - left-anchored overlay that hosts the
 * marker's contact form (built-in OR Contact Form 7 shortcode).
 * Structural / behavioural parallel of `directionsSidebar.css`:
 * same toggle chevron, same slide transition, same body scroll +
 * scrollbar styling. The only deltas are colour-neutral semantics
 * (the title icon is an envelope rather than a location arrow)
 * and a slightly different stacking slot - kept distinct from the
 * directions sidebar's z-index so a stale paint never lets the
 * two overlap silently.
 *
 * The sidebar's body deliberately wears the `w2dc-listing-content`
 * class so the popup-wide form styling (input height 30 px, submit
 * button chrome, CF7 overrides, response chips) applies verbatim
 * to the form rendered inside - no duplication.
 *
 * Directions + contact panels are mutually exclusive (the reducer
 * clears one when the other opens) so they're allowed to share the
 * left edge. Both render the toggle button in front of the panel
 * (z 721 / 731 below) and the sidebar slabs slightly behind (z 720
 * / 730), so the chevron always reads as the topmost reachable
 * widget regardless of which surface is in play.
 */

.w2dc-react-contact-sidebar {
	position: absolute;
	top: 0;
	left: 0;
	bottom: 0;
	max-width: calc(100% - 36px);
	background: #fff;
	box-shadow: 0 0 8px rgba(0, 0, 0, 0.08);
	box-sizing: border-box;
	display: flex;
	flex-direction: column;
	/* Same z-index as DirectionsSidebar - they swap via reducer
	 * mutual exclusion and need to cross-fade at the same level so
	 * neither paints solidly OVER the other during the mobile
	 * bottom-drawer swap. The opacity transition (mobile @media
	 * below) handles the visual blend. Both sit ABOVE the click popup
	 * (z 1500) now - the whole sidebar family stays above every popup. */
	z-index: 1620;
	transform: translateX(0);
	transition: transform 0.25s ease, opacity 0.25s ease;
	will-change: transform, opacity;
	opacity: 1;
	color: #1f2937;
}
.w2dc-react-contact-sidebar.is-collapsed,
.w2dc-react-contact-sidebar.is-collapsed-up {
	transform: translateX(-100%);
}

/* Toggle button intentionally removed - symmetrical with
 * DirectionsSidebar. The × in the header is the only dismiss
 * affordance the contact panel exposes (along with the toggle on
 * the popup's "Send message" button, which also dispatches
 * CLEAR_CONTACT when re-clicked). */

/* Header - title + ×. The × is the explicit "I'm done with this
 * form" action; it dispatches CLEAR_CONTACT which slides the panel
 * out and unmounts it. */
.w2dc-react-contact-sidebar__header {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 8px;
	padding: 12px 14px;
	border-bottom: 1px solid #e5e7eb;
	background: #f9fafb;
	flex: 0 0 auto;
}
.w2dc-react-contact-sidebar__title {
	display: inline-flex;
	align-items: center;
	gap: 8px;
	font-size: 14px;
	font-weight: 600;
	color: #111827;
}
.w2dc-react-contact-sidebar__title .w2dc-fa {
	font-family: 'FontAwesome';
	font-style: normal;
	font-weight: 400;
	font-size: 14px;
	color: var(--w2dc-primary, #2563eb);
	line-height: 1;
}
.w2dc-react-contact-sidebar__title .w2dc-fa-envelope::before { content: '\f0e0'; }

button.w2dc-react-contact-sidebar__close,
button.w2dc-react-contact-sidebar__close:hover,
button.w2dc-react-contact-sidebar__close:focus,
button.w2dc-react-contact-sidebar__close:active {
	border: 0;
	background: none;
	background-color: transparent;
	font-size: 22px;
	line-height: 1;
	color: #6b7280;
	cursor: pointer;
	padding: 0 4px;
	box-shadow: none;
	outline: none;
}
button.w2dc-react-contact-sidebar__close:hover { color: #111827; }

/* Body - scroll container for the form HTML. */
.w2dc-react-contact-sidebar__body {
	flex: 1 1 auto;
	overflow-y: auto;
	overflow-x: hidden;
	padding: 14px;
	box-sizing: border-box;
	scrollbar-width: thin;
	scrollbar-color: rgba(0, 0, 0, 0.25) transparent;
}
.w2dc-react-contact-sidebar__body::-webkit-scrollbar {
	width: 14px;
	height: 14px;
}
.w2dc-react-contact-sidebar__body::-webkit-scrollbar-track,
.w2dc-react-contact-sidebar__body::-webkit-scrollbar-corner {
	background: transparent;
}
.w2dc-react-contact-sidebar__body::-webkit-scrollbar-thumb {
	background-color: rgba(0, 0, 0, 0.2);
	background-clip: padding-box;
	border: 5px solid transparent;
	border-radius: 7px;
	-webkit-transition: border-width 0.18s ease, background-color 0.18s ease;
	transition: border-width 0.18s ease, background-color 0.18s ease;
}
.w2dc-react-contact-sidebar__body:hover::-webkit-scrollbar-thumb {
	border-width: 3px;
	background-color: rgba(0, 0, 0, 0.32);
}
.w2dc-react-contact-sidebar__body::-webkit-scrollbar-thumb:hover {
	background-color: rgba(0, 0, 0, 0.5);
	border-width: 2px;
}

/* Form-body reset - the body wears `.w2dc-listing-content` so any
 * popup-shared chrome (chips, hours table, distance badge styling,
 * etc.) renders consistently, but the form section doesn't have the
 * popup's column-of-fields layout. Drop the wrapper's flex layout
 * so the form's own block flow is unaffected. */
.w2dc-react-contact-sidebar__body.w2dc-listing-content {
	display: block;
	gap: 0;
}

/* ============================================================
 * Form styling - sidebar-scoped overrides so the contact form
 * picks up the same input chrome the rest of the plugin uses
 * (DirectionsSidebar inputs, SearchForm inputs, sidebar buttons).
 *
 * Important note on selector scoping:
 *   The form HTML is extracted from `.w2dc-listing-content__value` (inside
 *   the popup's contact field) before being passed to this
 *   sidebar - the `.w2dc-listing-content__field--contact_form` and
 *   `.w2dc-listing-content__field--cf7` parent wrappers stay in the popup
 *   and do NOT travel to the sidebar. So the popup's rules
 *   (scoped under those parents) DON'T apply here - which is the
 *   "form has no styles" symptom that motivated this block.
 *
 *   These rules therefore scope by the FORM's own classes -
 *   `.w2dc-listing-content__contact-form` (built-in) and `.wpcf7-form`
 *   (CF7) - which DO travel to the sidebar inside the extracted
 *   HTML. No `--cf7` parent prefix anywhere.
 *
 * Geometry: matches DirectionsSidebar - height 32 px, font 13 px,
 * border-radius 6 px, brand-primary focus ring; submit button is
 * a full-width "Go"-style chip.
 * ============================================================ */

/* Wrapper layout - looser column with a sensible inter-field gap. */
.w2dc-react-contact-sidebar__body .w2dc-listing-content__contact-form,
.w2dc-react-contact-sidebar__body .wpcf7-form {
	display: flex;
	flex-direction: column;
	gap: 10px;
	margin: 0;
	background: transparent !important;
	border: 0 !important;
	color: inherit !important;
	text-shadow: none !important;
	padding: 0 !important;
}

/* Field labels - CF7 puts label text in `<p>` rows (sometimes
 * `<label>`), the built-in form uses `<label>` directly. Style
 * both as the same small-grey label form factor. */
.w2dc-react-contact-sidebar__body .w2dc-listing-content__contact-form label,
.w2dc-react-contact-sidebar__body .wpcf7-form p,
.w2dc-react-contact-sidebar__body .wpcf7-form label {
	font-size: 12px !important;
	line-height: 1.4 !important;
	color: #374151;
	margin: 0 !important;
	display: block;
}

/* Inputs / textareas / select - full-width, 32 px tall, 13 px text.
 * Border + focus ring use the brand primary so the form reads as
 * part of the plugin's UI rather than a generic <form>. */
.w2dc-react-contact-sidebar__body .w2dc-listing-content__contact-form input[type="text"],
.w2dc-react-contact-sidebar__body .w2dc-listing-content__contact-form input[type="email"],
.w2dc-react-contact-sidebar__body .w2dc-listing-content__contact-form textarea,
.w2dc-react-contact-sidebar__body .wpcf7-form input[type="text"],
.w2dc-react-contact-sidebar__body .wpcf7-form input[type="email"],
.w2dc-react-contact-sidebar__body .wpcf7-form input[type="tel"],
.w2dc-react-contact-sidebar__body .wpcf7-form input[type="url"],
.w2dc-react-contact-sidebar__body .wpcf7-form input[type="number"],
.w2dc-react-contact-sidebar__body .wpcf7-form input[type="date"],
.w2dc-react-contact-sidebar__body .wpcf7-form select,
.w2dc-react-contact-sidebar__body .wpcf7-form textarea {
	width: 100%;
	box-sizing: border-box;
	font-size: 13px;
	font-family: inherit;
	color: #1f2937;
	background: #fff;
	border: 1px solid #d1d5db;
	border-radius: 6px;
	padding: 0 10px;
	line-height: 1;
	transition: border-color 0.12s, box-shadow 0.12s;
	text-shadow: none !important;
}

/* Single-line inputs - explicit 32 px to match the directions
 * sidebar input + Go button. */
.w2dc-react-contact-sidebar__body .w2dc-listing-content__contact-form input[type="text"],
.w2dc-react-contact-sidebar__body .w2dc-listing-content__contact-form input[type="email"],
.w2dc-react-contact-sidebar__body .wpcf7-form input[type="text"],
.w2dc-react-contact-sidebar__body .wpcf7-form input[type="email"],
.w2dc-react-contact-sidebar__body .wpcf7-form input[type="tel"],
.w2dc-react-contact-sidebar__body .wpcf7-form input[type="url"],
.w2dc-react-contact-sidebar__body .wpcf7-form input[type="number"],
.w2dc-react-contact-sidebar__body .wpcf7-form input[type="date"],
.w2dc-react-contact-sidebar__body .wpcf7-form select {
	height: 32px;
	min-height: 0;
}

/* Textarea - multi-line, generous height range. */
.w2dc-react-contact-sidebar__body .w2dc-listing-content__contact-form textarea,
.w2dc-react-contact-sidebar__body .wpcf7-form textarea {
	padding: 8px 10px;
	line-height: 1.4;
	min-height: 100px;
	max-height: 240px;
	resize: vertical;
}

/* Focus ring - brand-primary border + soft shadow halo. Same
 * pattern as `.w2dc-react-directions-sidebar__input-row:focus-within`,
 * applied directly to the control here since CF7 markup gives no
 * parent row container to scope :focus-within against. */
.w2dc-react-contact-sidebar__body .w2dc-listing-content__contact-form input:focus,
.w2dc-react-contact-sidebar__body .w2dc-listing-content__contact-form textarea:focus,
.w2dc-react-contact-sidebar__body .wpcf7-form input:focus,
.w2dc-react-contact-sidebar__body .wpcf7-form textarea:focus,
.w2dc-react-contact-sidebar__body .wpcf7-form select:focus {
	outline: none;
	border-color: var(--w2dc-primary, #2563eb);
	box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.16);
}

/* Placeholder copy - slightly muted but legible against white. */
.w2dc-react-contact-sidebar__body .w2dc-listing-content__contact-form input::-moz-placeholder, .w2dc-react-contact-sidebar__body .w2dc-listing-content__contact-form textarea::-moz-placeholder, .w2dc-react-contact-sidebar__body .wpcf7-form input::-moz-placeholder, .w2dc-react-contact-sidebar__body .wpcf7-form textarea::-moz-placeholder {
	color: #9ca3af;
}
.w2dc-react-contact-sidebar__body .w2dc-listing-content__contact-form input::placeholder,
.w2dc-react-contact-sidebar__body .w2dc-listing-content__contact-form textarea::placeholder,
.w2dc-react-contact-sidebar__body .wpcf7-form input::placeholder,
.w2dc-react-contact-sidebar__body .wpcf7-form textarea::placeholder {
	color: #9ca3af;
}

/* Submit button - visually the "Go" twin from DirectionsSidebar:
 * primary background, 32 px tall, full width, brand hover.
 * `!important` is necessary because CF7's theme stylesheets and
 * the host theme often ship high-specificity rules against
 * `input[type="submit"]` that would otherwise stomp ours. */
.w2dc-react-contact-sidebar__body .w2dc-listing-content__contact-form .w2dc-listing-content__contact-submit,
.w2dc-react-contact-sidebar__body .wpcf7-form .wpcf7-submit,
.w2dc-react-contact-sidebar__body .wpcf7-form input[type="submit"],
.w2dc-react-contact-sidebar__body .wpcf7-form button[type="submit"] {
	display: inline-flex !important;
	align-items: center;
	justify-content: center;
	width: 100% !important;
	box-sizing: border-box;
	height: 32px !important;
	min-height: 0 !important;
	padding: 0 14px !important;
	margin: 4px 0 0 !important;
	font-size: 13px !important;
	font-weight: 600;
	font-family: inherit;
	line-height: 1;
	color: #fff !important;
	background: var(--w2dc-primary, #2563eb) !important;
	border: 0 !important;
	border-radius: 6px !important;
	cursor: pointer;
	text-align: center;
	text-transform: none;
	text-shadow: none !important;
}
.w2dc-react-contact-sidebar__body .w2dc-listing-content__contact-form .w2dc-listing-content__contact-submit:hover:not(:disabled),
.w2dc-react-contact-sidebar__body .wpcf7-form .wpcf7-submit:hover:not(:disabled),
.w2dc-react-contact-sidebar__body .wpcf7-form input[type="submit"]:hover:not(:disabled),
.w2dc-react-contact-sidebar__body .wpcf7-form button[type="submit"]:hover:not(:disabled) {
	filter: brightness(1.05);
}
.w2dc-react-contact-sidebar__body .w2dc-listing-content__contact-form .w2dc-listing-content__contact-submit:disabled,
.w2dc-react-contact-sidebar__body .wpcf7-form .wpcf7-submit:disabled,
.w2dc-react-contact-sidebar__body .wpcf7-form input[type="submit"]:disabled {
	opacity: 0.6;
	cursor: not-allowed;
}

/* Status row (built-in form) - small chip-style message under the
 * submit button. Sender's strings ship as innerHTML; styles only
 * colour them. */
.w2dc-react-contact-sidebar__body .w2dc-listing-content__contact-status {
	font-size: 12px;
	margin-top: 6px;
	color: #6b7280;
}
.w2dc-react-contact-sidebar__body .w2dc-listing-content__contact-status[data-state="ok"]  { color: #15803d; }
.w2dc-react-contact-sidebar__body .w2dc-listing-content__contact-status[data-state="err"] { color: #b91c1c; }

/* CF7 status response - coloured background chip for sent / failed
 * states. */
.w2dc-react-contact-sidebar__body .wpcf7-response-output {
	margin: 8px 0 0 !important;
	padding: 8px 10px !important;
	font-size: 12px;
	border-radius: 6px;
	border: 1px solid transparent !important;
}
.w2dc-react-contact-sidebar__body .wpcf7-response-output:empty {
	display: none !important;
}
.w2dc-react-contact-sidebar__body .wpcf7-form.sent .wpcf7-response-output,
.w2dc-react-contact-sidebar__body .wpcf7-mail-sent-ok {
	color: #15803d !important;
	background: #f0fdf4;
	border-color: #bbf7d0 !important;
}
.w2dc-react-contact-sidebar__body .wpcf7-form.invalid .wpcf7-response-output,
.w2dc-react-contact-sidebar__body .wpcf7-form.failed .wpcf7-response-output,
.w2dc-react-contact-sidebar__body .wpcf7-validation-errors,
.w2dc-react-contact-sidebar__body .wpcf7-mail-sent-ng,
.w2dc-react-contact-sidebar__body .wpcf7-spam-blocked {
	color: #b91c1c !important;
	background: #fef2f2;
	border-color: #fecaca !important;
}

/* Per-field validation tip and invalid border highlight (CF7). */
.w2dc-react-contact-sidebar__body .wpcf7-not-valid-tip {
	font-size: 12px;
	color: #b91c1c;
	margin-top: 3px;
}
.w2dc-react-contact-sidebar__body .wpcf7-not-valid {
	border-color: #fca5a5 !important;
}
.w2dc-react-contact-sidebar__body .wpcf7-spinner {
	width: 20px;
	height: 20px;
	margin: 0 0 0 8px;
}

/* Mobile reflow - same drawer pattern as DirectionsSidebar (see
 * the long comment over there for why `position: fixed` + `dvh`
 * are necessary on iOS to keep the drawer stable across visual-
 * viewport changes). */
@media (max-width: 768px) {
	.w2dc-react-contact-sidebar {
		position: fixed !important;
		top: auto;
		bottom: 0;
		left: 0;
		right: 0;
		width: 100% !important;
		max-width: 100%;
		height: 60dvh;
		max-height: 60dvh;
		box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.16);
	}
	/* Mobile slide animation - symmetrical with DirectionsSidebar
	 * (see the comment over there for the full rationale):
	 *   • `.is-open`             -> translateY(0), visible
	 *   • `.is-collapsed`        -> translateY(101%), off-screen
	 *                               BELOW. Normal close (× dismiss).
	 *   • `.is-collapsed-up`     -> translateY(-101%), off-screen
	 *                               ABOVE. Used when DirectionsSidebar
	 *                               is taking over. Both panels slide
	 *                               UP together -> no visual overlap. */
	.w2dc-react-contact-sidebar.is-open {
		transform: translateY(0);
		opacity: 1;
	}
	.w2dc-react-contact-sidebar.is-collapsed {
		transform: translateY(101%);
		opacity: 0;
	}
	.w2dc-react-contact-sidebar.is-collapsed-up {
		transform: translateY(-101%);
		opacity: 0;
	}
}

/* Listings sidebar - absolute overlay above the map. Width (left/right) or
 * height (top/bottom) comes from the metabox. Slide animation uses transform
 * only - never width/height - so the map canvas is NEVER resized during
 * open/close (no Google Maps redraw, no jank). */

/* Single-source CSS var driving the mobile drawer's height. Declared via
 * `@property` so it's animatable through CSS transitions. Both the
 * sidebar's `height` AND the toggle's `bottom` read from this same var,
 * so every frame they resolve to identical interpolated values from one
 * source - guaranteeing the toggle stays glued to the drawer's top edge
 * with zero phase lag. Without @property, custom properties default to
 * type `*` (any value) which is non-interpolable, and a CSS transition
 * on the var would snap rather than animate. */
@property --w2dc-mobile-drawer-h {
	syntax: '<length>';
	inherits: true;
	initial-value: 0px;
}

/* Theme-independent rendering. A host theme's dark mode (e.g.
 * `html[data-theme="dark"] { color-scheme: dark }`, or a
 * `@media (prefers-color-scheme: dark)` rule) leaks into the plugin two
 * ways:
 *   1. `color-scheme: dark` CASCADES into every native form control -
 *      the plugin's checkboxes, radios, range sliders, <select>
 *      dropdowns and scroll bars render inverted (dark fills) on the
 *      plugin's own light backgrounds.
 *   2. The theme's `body { color: <light-in-dark> }` is INHERITED by
 *      the plugin's text that doesn't set its own colour (field labels,
 *      checklist item text, the price caption...), so the copy turns near-
 *      white on the plugin's light card.
 * The plugin paints its own light UI and is NOT meant to follow the
 * site theme's light/dark switch, so pin every front-end React root to
 * `color-scheme: light` AND re-assert the plugin's base text colour.
 * `color` only reaches elements that don't set their own, so explicit
 * plugin colours are untouched; the map tiles' dark style (an engine
 * style swap, unrelated to `color-scheme`/`color`) is also untouched. */
.w2dc-react-mount,
.w2dc-react-map-wrapper,
.w2dc-react-listings-standalone,
.w2dc-react-search-standalone {
	color-scheme: light;
	color: #1f2937;
}

.w2dc-react-map-wrapper {
	position: relative;
	/* Clip the listings sidebar's translated-off-screen state so the cards
	 * don't bleed onto the surrounding page background. The toggle button
	 * sits at x=0..36px (relative to wrapper) when collapsed - INSIDE the
	 * clip rect - so it stays visible/clickable. */
	overflow: hidden;
	/* Flex column lets the responsive search form (search-form.css) pop out
	 * of the corner and claim its own row above or below the canvas on
	 * narrow viewports. order:0 (form-above) / order:1 (canvas) / order:2
	 * (form-below) gives correct stacking regardless of the form's DOM
	 * position. On desktop the form is `position: absolute` and ignores
	 * flex order entirely, so the layout is unaffected. */
	display: flex;
	flex-direction: column;
}
.w2dc-react-map-canvas-host {
	position: relative;
	/* Isolate the map's stacking context. Google Maps / Mapbox / MapLibre
	 * inject marker DOM elements with high z-index values (often 4-digit)
	 * into the map div. Without `isolation: isolate`, canvas-host doesn't
	 * create a stacking context (position:relative + z-index:auto isn't
	 * enough), so those marker z-indices resolve against the WRAPPER's
	 * stacking context - and rise above the in-map search form (z 700),
	 * making clicks on the form pass through to whichever marker happens
	 * to sit under the cursor. `isolation: isolate` traps every descendant
	 * z-index inside canvas-host, so the form's 700 (in wrapper context)
	 * cleanly beats canvas-host's auto (also wrapper context). */
	isolation: isolate;
	/* Desktop: flex-grow:1 fills the wrapper (which inherits the PHP mount
	 * div's fixed height) - no empty band below the map even if the
	 * inline `height` style we pass for the mobile path is smaller than
	 * the wrapper. */
	flex: 1 1 auto;
	min-height: 0;
	width: 100%;
	order: 1;
}
/* Mobile flow: search form claims its own row above/below the map. The
 * wrapper (and the PHP mount div around it) grow to fit form +
 * canvas-host, while the canvas locks to its inline `height` (= the
 * configured map height) instead of being squeezed by the flex row.
 * `flex: 0 0 auto` disables grow/shrink so inline height is honoured
 * verbatim. */
@media (max-width: 768px) {
	.w2dc-react-mount {
		height: auto !important;
	}
	.w2dc-react-map-wrapper {
		height: auto !important;
	}

	/* Mobile architecture: drawer is a FLEX CHILD of canvas-host, not
	 * an absolute overlay. Canvas-host switches to a vertical flex
	 * column that stacks:
	 *
	 *     ┌──────────────────────────────┐
	 *     │  .w2dc-react-map-canvas     │  ← stays at the configured
	 *     │  (the actual map, locked     │     map height (e.g. 400px)
	 *     │  to --w2dc-map-area-height) │     via flex-basis = the
	 *     │                              │     CSS var emitted by
	 *     ├──────────────────────────────┤     MapApp.tsx
	 *     │  .w2dc-react-sidebar        │
	 *     │  (drawer, 50vh when open,    │  ← grows / shrinks; canvas-
	 *     │  height: 0 when collapsed)   │     host's total height
	 *     └──────────────────────────────┘     follows.
	 *
	 * Both map and drawer share the SAME visible container - canvas-
	 * host's bounding box wraps them together. No `position:
	 * absolute` outside the host, no margin/padding hack to "reserve"
	 * space below - canvas-host just grows naturally with its flex
	 * children.
	 *
	 * Flex order: the React fragment renders the toggle button
	 * first, then the sidebar div, then the map canvas. We use
	 * `order: 1` on the sidebar so the map (order: 0 default) comes
	 * first visually, then the drawer.
	 *
	 * Toggle button stays `position: absolute` (NOT a flex item) and
	 * anchors at `bottom: var(--w2dc-sidebar-open-size, 0)` so it
	 * tracks the boundary between map and drawer - at canvas-host's
	 * bottom when collapsed (sidebar height 0), and at the drawer's
	 * top edge when open (sidebar height = the var).
	 *
	 * Map controls (zoom, fullscreen, pegman, counter, search form)
	 * are absolute children of canvas-host (or its descendants) and
	 * keep their default corners - the drawer no longer covers the
	 * map, so the desktop's translate-shift would only push them
	 * out of view. All shifts cancelled via `transform: none`.
	 */
	.w2dc-react-map-wrapper {
		--w2dc-mobile-drawer-h: 0px;
		transition: --w2dc-mobile-drawer-h 0.25s ease;
	}
	.w2dc-react-map-wrapper.w2dc-sidebar-open {
		--w2dc-mobile-drawer-h: 50vh;
	}
	.w2dc-react-map-canvas-host {
		flex: 0 0 auto;
		display: flex !important;
		flex-direction: column !important;
		height: auto !important;
	}

	/* Inner map element keeps the configured pixel height (set by
	 * MapApp.tsx as `--w2dc-map-area-height` on canvas-host).
	 * Without an explicit height the inline `height: 100%` would
	 * collapse to 0 once canvas-host becomes auto-sized.
	 *
	 * PERCENTAGE-height maps: `--w2dc-map-area-height` is then "100%",
	 * and 100% of an auto-sized host resolves to 0 - the map vanished on
	 * mobile whenever the in-map search form reflowed into the stacked
	 * row. Those maps also carry `--w2dc-map-fill` (the % converted to
	 * a vh unit by the PHP mount, inherited down from the mount div) -
	 * prefer it, so a 100%-height map gets 100vh here while fixed-pixel
	 * maps (no fill var) keep their configured px exactly as before. */
	.w2dc-react-map-canvas-host .w2dc-react-map-canvas {
		flex: 0 0 var(--w2dc-map-fill, var(--w2dc-map-area-height, 400px)) !important;
		height: var(--w2dc-map-fill, var(--w2dc-map-area-height, 400px)) !important;
		width: 100% !important;
		order: 0;
	}

	/* Fullscreen on mobile - the wrapper picks up `.w2dc-fs-host`
	 * (see search-form.css), which makes it a viewport-sized flex
	 * column with the search form on top and the canvas-host below.
	 * The static-flow rules above lock canvas-host to `flex: 0 0
	 * auto` + the inner canvas to the configured pixel height so the
	 * page can scroll past the form-then-map stack; in fullscreen the
	 * viewport IS the container, no scroll, and the lock leaves a
	 * black band below the canvas where the wrapper extends beyond
	 * the map area. Override both rules so canvas-host claims the
	 * remaining viewport and the canvas fills it. The mobile drawer
	 * stays inside canvas-host so its own slice still tracks
	 * `--w2dc-mobile-drawer-h` cleanly. */
	.w2dc-fs-host .w2dc-react-map-canvas-host {
		flex: 1 1 auto !important;
		min-height: 0 !important;
	}
	.w2dc-fs-host .w2dc-react-map-canvas-host .w2dc-react-map-canvas {
		flex: 1 1 auto !important;
		height: auto !important;
	}

	/* Sidebar reflows from absolute overlay -> flex child. Sits
	 * below the map (order: 1) with full container width and 50vh
	 * height when open. Transitions on `height` animate the
	 * grow / shrink (and canvas-host's total height follows via
	 * flex layout). */
	.w2dc-react-sidebar--left,
	.w2dc-react-sidebar--right {
		position: relative !important;
		top:        auto !important;
		bottom:     auto !important;
		left:       auto !important;
		right:      auto !important;
		width:      100% !important;
		max-width:  100% !important;
		/* Height bound to the wrapper-level animated var - the var
		 * itself transitions (see @property + transition on wrapper),
		 * so we DON'T declare a separate transition on this element.
		 * One source of truth ⇒ zero phase desync with the toggle. */
		height:     var(--w2dc-mobile-drawer-h, 0px) !important;
		overflow:   hidden !important;
		flex:       0 0 auto !important;
		order:      1;
		transform:  none !important;
		transition: none !important;
	}

	/* Toggle button - `bottom` reads the SAME `--w2dc-mobile-drawer-h`
	 * the sidebar uses for its height. Since the var transitions on
	 * the wrapper element (@property animation), both elements
	 * resolve to identical interpolated values from the same source
	 * each frame - no separate per-element transition that could
	 * fall out of phase. */
	button.w2dc-react-sidebar__toggle--left,
	button.w2dc-react-sidebar__toggle--right {
		top:    auto !important;
		right:  auto !important;
		left:   50%  !important;
		bottom: var(--w2dc-mobile-drawer-h, 0px) !important;
		transform: translateX(-50%) !important;
		width:  80px !important;
		height: 36px !important;
		border-radius: 8px 8px 0 0 !important;
		transition: background-color 0.15s ease !important;
	}

	/* Chevron - drawer slides vertically, so arrow needs ▼ when
	 * OPEN (tap to push back down) and ▲ when COLLAPSED (tap to
	 * lift up from below). */
	button.w2dc-react-sidebar__toggle--left.is-collapsed .w2dc-react-sidebar__toggle-icon,
	button.w2dc-react-sidebar__toggle--right.is-collapsed .w2dc-react-sidebar__toggle-icon {
		transform: rotate(-90deg) !important;
	}
	button.w2dc-react-sidebar__toggle--left.is-open .w2dc-react-sidebar__toggle-icon,
	button.w2dc-react-sidebar__toggle--right.is-open .w2dc-react-sidebar__toggle-icon {
		transform: rotate(90deg) !important;
	}

	/* List -> grid promotion (full-width drawer fits multiple columns
	 * better than one giant card). */
	.w2dc-react-sidebar--left  .w2dc-react-sidebar__grid--list,
	.w2dc-react-sidebar--right .w2dc-react-sidebar__grid--list {
		grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)) !important;
	}

	/* Cancel desktop control shifts on mobile ONLY when sidebar is
	 * configured as left/right - those positions reflow into a
	 * bottom-attached drawer, and the controls (zoom, fullscreen,
	 * pegman, GL ctrl-corners, search form, marker counter) live
	 * INSIDE `.w2dc-react-map-canvas` (fixed-height map div), so
	 * their absolute positions are already correct. Any compensating
	 * translate would just animate them out of place.
	 *
	 * has-sidebar--bottom is INTENTIONALLY OMITTED here - that
	 * position doesn't reflow on mobile (sidebar already sits at the
	 * bottom of canvas-host as an absolute overlay on both desktop
	 * and mobile). The desktop "push bottom-anchored controls up by
	 * sidebar height" rule (lines ~395-405) continues to apply, so
	 * pegman, zoom pad, counter and the bottom-anchored search form
	 * float up by the sidebar's height when the drawer opens.
	 *
	 * has-sidebar--top is also untouched - same rationale (no mobile
	 * reflow, desktop "push top controls down" applies as expected). */
	.w2dc-react-map-wrapper.w2dc-sidebar-open.has-sidebar--left .gm-fullscreen-control,
	.w2dc-react-map-wrapper.w2dc-sidebar-open.has-sidebar--left .gm-bundled-control,
	.w2dc-react-map-wrapper.w2dc-sidebar-open.has-sidebar--left .gm-svpc,
	.w2dc-react-map-wrapper.w2dc-sidebar-open.has-sidebar--left .maplibregl-ctrl-top-left,
	.w2dc-react-map-wrapper.w2dc-sidebar-open.has-sidebar--left .maplibregl-ctrl-top-right,
	.w2dc-react-map-wrapper.w2dc-sidebar-open.has-sidebar--left .maplibregl-ctrl-bottom-left,
	.w2dc-react-map-wrapper.w2dc-sidebar-open.has-sidebar--left .maplibregl-ctrl-bottom-right,
	.w2dc-react-map-wrapper.w2dc-sidebar-open.has-sidebar--left .mapboxgl-ctrl-top-left,
	.w2dc-react-map-wrapper.w2dc-sidebar-open.has-sidebar--left .mapboxgl-ctrl-top-right,
	.w2dc-react-map-wrapper.w2dc-sidebar-open.has-sidebar--left .mapboxgl-ctrl-bottom-left,
	.w2dc-react-map-wrapper.w2dc-sidebar-open.has-sidebar--left .mapboxgl-ctrl-bottom-right,
	.w2dc-react-map-wrapper.w2dc-sidebar-open.has-sidebar--left .w2dc-map-counter,
	.w2dc-react-map-wrapper.w2dc-sidebar-open.has-sidebar--left .w2dc-search-form,
	.w2dc-react-map-wrapper.w2dc-sidebar-open.has-sidebar--right .gm-fullscreen-control,
	.w2dc-react-map-wrapper.w2dc-sidebar-open.has-sidebar--right .gm-bundled-control,
	.w2dc-react-map-wrapper.w2dc-sidebar-open.has-sidebar--right .gm-svpc,
	.w2dc-react-map-wrapper.w2dc-sidebar-open.has-sidebar--right .maplibregl-ctrl-top-left,
	.w2dc-react-map-wrapper.w2dc-sidebar-open.has-sidebar--right .maplibregl-ctrl-top-right,
	.w2dc-react-map-wrapper.w2dc-sidebar-open.has-sidebar--right .maplibregl-ctrl-bottom-left,
	.w2dc-react-map-wrapper.w2dc-sidebar-open.has-sidebar--right .maplibregl-ctrl-bottom-right,
	.w2dc-react-map-wrapper.w2dc-sidebar-open.has-sidebar--right .mapboxgl-ctrl-top-left,
	.w2dc-react-map-wrapper.w2dc-sidebar-open.has-sidebar--right .mapboxgl-ctrl-top-right,
	.w2dc-react-map-wrapper.w2dc-sidebar-open.has-sidebar--right .mapboxgl-ctrl-bottom-left,
	.w2dc-react-map-wrapper.w2dc-sidebar-open.has-sidebar--right .mapboxgl-ctrl-bottom-right,
	.w2dc-react-map-wrapper.w2dc-sidebar-open.has-sidebar--right .w2dc-map-counter,
	.w2dc-react-map-wrapper.w2dc-sidebar-open.has-sidebar--right .w2dc-search-form {
		transform: none !important;
	}
}

/* "Edit map" affordance - emitted by `map_controller::display()`
 * when the current visitor has `edit_post` capability on the map
 * post. Rendered OUTSIDE the map mount as a sibling, right-aligned
 * below the map's bottom-right corner. Doesn't compete with the GL
 * engine's on-canvas controls (attribution, zoom buttons, geolocator
 * widget) - sits in the page flow underneath the map. */
.w2dc-map-frontend-actions {
	display: flex;
	justify-content: flex-end;
	margin-top: 8px;
}
.w2dc-map-frontend-edit-link {
	display: inline-flex;
	align-items: center;
	gap: 4px;
	padding: 5px 12px;
	font-size: 12px;
	font-weight: 500;
	background: #fff;
	color: var(--w2dc-button-primary, #2563eb);
	border: 1px solid var(--w2dc-button-primary, #2563eb);
	border-radius: 4px;
	text-decoration: none;
	line-height: 1.2;
}
/* Fill ONLY on hover. Plain :focus made the button stay stuck-filled after a
 * click (it opens the editor in a new tab, and focus lingered on return). Focus
 * gets a ring instead - no sticky fill, a11y preserved. */
.w2dc-map-frontend-edit-link:hover {
	background: var(--w2dc-button-primary, #2563eb);
	color: var(--w2dc-button-text, #fff);
	text-decoration: none;
}
.w2dc-map-frontend-edit-link:focus-visible {
	outline: 2px solid var(--w2dc-button-primary, #2563eb);
	outline-offset: 2px;
}
.w2dc-map-frontend-edit-link::before {
	content: "\270E";
	font-size: 14px;
	line-height: 1;
}
.w2dc-react-map-wrapper > .w2dc-search-form--top-left,
.w2dc-react-map-wrapper > .w2dc-search-form--top-right    { order: 0; }
.w2dc-react-map-wrapper > .w2dc-search-form--bottom-left,
.w2dc-react-map-wrapper > .w2dc-search-form--bottom-right { order: 2; }

/* On mobile, in-map search forms always attach to the TOP of the map
 * (above canvas-host) regardless of the admin-configured corner -
 * bottom-anchored forms would otherwise land below the drawer or off
 * to the side, far from the map they apply to.
 *
 * `transform: none` is necessary here because the desktop bottom-
 * sidebar shift rule (lines ~395-405) applies `translateY(-var)` to
 * `.w2dc-search-form--bottom-*` so it floats above the bottom
 * sidebar. With the form now reordered to the TOP of the wrapper
 * on mobile, that upward shift would push it off-screen above the
 * viewport. Clearing the transform keeps the form at its natural
 * flex position.
 *
 * The exception is sidebar=top: that already places the sidebar
 * above the map, and the existing translateY rule (line ~386)
 * keeps the form between the sidebar and the map - preserve that
 * arrangement by leaving the desktop order in place (form-bottom
 * keeps order:2). */
@media (max-width: 768px) {
	.w2dc-react-map-wrapper:not(.has-sidebar--top) > .w2dc-search-form--bottom-left,
	.w2dc-react-map-wrapper:not(.has-sidebar--top) > .w2dc-search-form--bottom-right {
		order: 0 !important;
		transform: none !important;
	}
}

.w2dc-react-sidebar {
	position: absolute;
	background: #fff;
	box-shadow: 0 0 8px rgba(0, 0, 0, 0.08);
	overflow: visible;          /* let the toggle button bleed out */
	box-sizing: border-box;
	/* Stacking order (inside canvas-host's stacking context, scoped by
	 * `isolation: isolate`):
	 *   - canvasOverlayShim pane          z 5
	 *   - clustered marker icons          z 6
	 *   - auto-popup layer                z 600
	 *   - click popup                     z 1500
	 *   - listings sidebar                z 1600  <- sits ABOVE every popup
	 *                                                so a popup can never
	 *                                                cover the listing cards.
	 *                                                The click popup still
	 *                                                auto-pans out from under
	 *                                                the sidebar (see Popup.tsx)
	 *                                                so it stays fully visible
	 *                                                beside it on a wide map;
	 *                                                on a narrow one the sidebar
	 *                                                simply wins the overlap.
	 *   - sidebar toggle                  z 1601
	 *   - directions / contact sidebar    z 1620  (above the listings sidebar) */
	z-index: 1600;
	transition: transform 0.25s ease;
	will-change: transform;
}

/* Map-control shift while the sidebar is open. `MapSidebar` writes
 * `--w2dc-sidebar-open-size: <N>px` on the wrapper when expanded
 * (`0px` when collapsed), plus toggles the `w2dc-sidebar-open` class
 * so the rules below are scoped - controls return to their default
 * positions the moment the sidebar slides off-screen.
 *
 * Covered controls (Google Maps): fullscreen button, zoom/compass
 * pad, Pegman / Street View peg. Attribution + "Условия / Terms" are
 * NOT shifted (legal text, must remain visible verbatim where Google
 * places it).
 *
 * Covered controls (Mapbox / MapLibre GL): all four corner ctrl-
 * containers - we just shift the whole stack on the affected side.
 *
 * Custom controls: the marker counter chip (`.w2dc-map-counter`,
 * bottom-right by default) follows the same rules.
 *
 * Transition timing matches the sidebar's own 0.25s slide above, so
 * the controls glide in sync with the sidebar edge. */
.w2dc-react-map-wrapper .gm-fullscreen-control,
.w2dc-react-map-wrapper .gm-bundled-control,
.w2dc-react-map-wrapper .gm-svpc,
.w2dc-react-map-wrapper .maplibregl-ctrl-top-right,
.w2dc-react-map-wrapper .maplibregl-ctrl-bottom-right,
.w2dc-react-map-wrapper .maplibregl-ctrl-top-left,
.w2dc-react-map-wrapper .maplibregl-ctrl-bottom-left,
.w2dc-react-map-wrapper .mapboxgl-ctrl-top-right,
.w2dc-react-map-wrapper .mapboxgl-ctrl-bottom-right,
.w2dc-react-map-wrapper .mapboxgl-ctrl-top-left,
.w2dc-react-map-wrapper .mapboxgl-ctrl-bottom-left,
.w2dc-react-map-wrapper .w2dc-map-counter,
.w2dc-react-map-wrapper .w2dc-search-form,
.w2dc-react-map-wrapper .w2dc-directions-bar {
	transition: transform 0.25s ease;
}

/* Sidebar on the RIGHT -> pull right-anchored controls LEFT by sidebar width.
 * Search form gets shifted only when its OWN corner is on the right
 * (top-right / bottom-right) so a left-anchored form stays put.
 *
 * `.gm-svpc` (Street View Pegman) deliberately NOT included: its
 * position is admin-configurable via `streetViewControlOptions.position`
 * and CSS can't tell which corner Google placed it in. On setups where
 * it lives in a left or top corner, a blanket left-shift moves it
 * away from where the visitor expects to grab it. Leave it where
 * Google put it; if it lands behind a right-positioned sidebar the
 * visitor can collapse the sidebar to reach it. */
.w2dc-react-map-wrapper.w2dc-sidebar-open.has-sidebar--right .gm-fullscreen-control,
.w2dc-react-map-wrapper.w2dc-sidebar-open.has-sidebar--right .gm-bundled-control,
.w2dc-react-map-wrapper.w2dc-sidebar-open.has-sidebar--right .maplibregl-ctrl-top-right,
.w2dc-react-map-wrapper.w2dc-sidebar-open.has-sidebar--right .maplibregl-ctrl-bottom-right,
.w2dc-react-map-wrapper.w2dc-sidebar-open.has-sidebar--right .mapboxgl-ctrl-top-right,
.w2dc-react-map-wrapper.w2dc-sidebar-open.has-sidebar--right .mapboxgl-ctrl-bottom-right,
.w2dc-react-map-wrapper.w2dc-sidebar-open.has-sidebar--right .w2dc-map-counter,
.w2dc-react-map-wrapper.w2dc-sidebar-open.has-sidebar--right .w2dc-search-form--top-right,
.w2dc-react-map-wrapper.w2dc-sidebar-open.has-sidebar--right .w2dc-search-form--bottom-right {
	transform: translateX(calc(var(--w2dc-sidebar-open-size, 0px) * -1));
}

/* Sidebar on the LEFT -> push left-anchored controls (GL ctrl-containers +
 * left-corner search form) RIGHT. Google's built-in controls all live on
 * the right side by default, so there's nothing on the LEFT to shift for
 * Google maps. */
.w2dc-react-map-wrapper.w2dc-sidebar-open.has-sidebar--left .maplibregl-ctrl-top-left,
.w2dc-react-map-wrapper.w2dc-sidebar-open.has-sidebar--left .maplibregl-ctrl-bottom-left,
.w2dc-react-map-wrapper.w2dc-sidebar-open.has-sidebar--left .mapboxgl-ctrl-top-left,
.w2dc-react-map-wrapper.w2dc-sidebar-open.has-sidebar--left .mapboxgl-ctrl-bottom-left,
.w2dc-react-map-wrapper.w2dc-sidebar-open.has-sidebar--left .w2dc-search-form--top-left,
.w2dc-react-map-wrapper.w2dc-sidebar-open.has-sidebar--left .w2dc-search-form--bottom-left {
	transform: translateX(var(--w2dc-sidebar-open-size, 0px));
}

/* Sidebar on TOP -> push top-anchored controls DOWN. */
.w2dc-react-map-wrapper.w2dc-sidebar-open.has-sidebar--top .gm-fullscreen-control,
.w2dc-react-map-wrapper.w2dc-sidebar-open.has-sidebar--top .maplibregl-ctrl-top-left,
.w2dc-react-map-wrapper.w2dc-sidebar-open.has-sidebar--top .maplibregl-ctrl-top-right,
.w2dc-react-map-wrapper.w2dc-sidebar-open.has-sidebar--top .mapboxgl-ctrl-top-left,
.w2dc-react-map-wrapper.w2dc-sidebar-open.has-sidebar--top .mapboxgl-ctrl-top-right,
.w2dc-react-map-wrapper.w2dc-sidebar-open.has-sidebar--top .w2dc-search-form--top-left,
.w2dc-react-map-wrapper.w2dc-sidebar-open.has-sidebar--top .w2dc-search-form--top-right {
	transform: translateY(var(--w2dc-sidebar-open-size, 0px));
}

/* Sidebar on BOTTOM -> pull bottom-anchored controls UP.
 * `.gm-svpc` (Street View Pegman) is INTENTIONALLY EXCLUDED - Google
 * places pegman with inline `top: X` (top-anchored) by default in
 * many setups. A `translateY(-var)` would move a top-anchored pegman
 * UP off the map; for a bottom-anchored pegman the shift would be
 * correct, but we can't distinguish anchor from CSS. Admins who
 * want a clearance from the bottom sidebar should position pegman
 * elsewhere via the Street View metabox. */
.w2dc-react-map-wrapper.w2dc-sidebar-open.has-sidebar--bottom .gm-bundled-control,
.w2dc-react-map-wrapper.w2dc-sidebar-open.has-sidebar--bottom .maplibregl-ctrl-bottom-left,
.w2dc-react-map-wrapper.w2dc-sidebar-open.has-sidebar--bottom .maplibregl-ctrl-bottom-right,
.w2dc-react-map-wrapper.w2dc-sidebar-open.has-sidebar--bottom .mapboxgl-ctrl-bottom-left,
.w2dc-react-map-wrapper.w2dc-sidebar-open.has-sidebar--bottom .mapboxgl-ctrl-bottom-right,
.w2dc-react-map-wrapper.w2dc-sidebar-open.has-sidebar--bottom .w2dc-map-counter,
.w2dc-react-map-wrapper.w2dc-sidebar-open.has-sidebar--bottom .w2dc-search-form--bottom-left,
.w2dc-react-map-wrapper.w2dc-sidebar-open.has-sidebar--bottom .w2dc-search-form--bottom-right {
	transform: translateY(calc(var(--w2dc-sidebar-open-size, 0px) * -1));
}
.w2dc-react-sidebar__scroll {
	width: 100%;
	height: 100%;
	overflow: auto;
	box-sizing: border-box;
	padding: 10px;
}

/* Win 11-style thin scrollbar that thickens on hover. The container width is
 * always 14 px; the thumb sits inside a transparent border that we shrink on
 * hover so the thumb appears to expand smoothly without the layout reflowing.
 * Firefox honours scrollbar-width / -color but cannot animate the width. */
.w2dc-react-sidebar__scroll {
	scrollbar-width: thin;
	scrollbar-color: rgba(0, 0, 0, 0.25) transparent;
}
.w2dc-react-sidebar__scroll::-webkit-scrollbar { width: 14px; height: 14px; }
.w2dc-react-sidebar__scroll::-webkit-scrollbar-track,
.w2dc-react-sidebar__scroll::-webkit-scrollbar-corner { background: transparent; }
.w2dc-react-sidebar__scroll::-webkit-scrollbar-thumb {
	background-color: rgba(0, 0, 0, 0.2);
	background-clip: padding-box;
	border: 5px solid transparent;
	border-radius: 7px;
	-webkit-transition: border-width 0.18s ease, background-color 0.18s ease;
	transition: border-width 0.18s ease, background-color 0.18s ease;
}
.w2dc-react-sidebar__scroll:hover::-webkit-scrollbar-thumb {
	border-width: 3px;
	background-color: rgba(0, 0, 0, 0.32);
}
.w2dc-react-sidebar__scroll::-webkit-scrollbar-thumb:hover {
	background-color: rgba(0, 0, 0, 0.5);
	border-width: 2px;
}

/* Per-position placement (size set inline by React from sidebar_size). The
 * inline `width` / `height` is capped by max-* so the sidebar can never
 * overflow the wrapper - important on the admin Edit Map preview where the
 * map column may be narrower than the configured sidebar size. The 36 px
 * gutter leaves room for the toggle button when the sidebar is fully open. */
.w2dc-react-sidebar--left,
.w2dc-react-sidebar--right { max-width:  calc(100% - 36px); }
.w2dc-react-sidebar--top,
.w2dc-react-sidebar--bottom { max-height: calc(100% - 36px); }
.w2dc-react-sidebar--left   { top: 0;    left: 0;   bottom: 0; }
.w2dc-react-sidebar--right  { top: 0;    right: 0;  bottom: 0; }
.w2dc-react-sidebar--top    { top: 0;    left: 0;   right: 0;  }
.w2dc-react-sidebar--bottom { bottom: 0; left: 0;   right: 0;  }

/* Collapsed: slide off-screen via transform (no width/height change). */
.w2dc-react-sidebar--left.is-collapsed   { transform: translateX(-100%); }
.w2dc-react-sidebar--right.is-collapsed  { transform: translateX(100%); }
.w2dc-react-sidebar--top.is-collapsed    { transform: translateY(-100%); }
.w2dc-react-sidebar--bottom.is-collapsed { transform: translateY(100%); }

/* Toggle button - a SIBLING of the sidebar (not a child) so its hit area
 * is never affected by the sidebar's `translateX(-100%)`. Position is
 * driven by inline left/right/top/bottom (set by React from `sidebar_size`)
 * and animates via the same 0.25 s ease the sidebar uses, so visually the
 * toggle and the sidebar slide together.
 *
 * Always carries the primary brand colour as a fill - switches to secondary
 * on hover. The SVG chevron is rendered with `currentColor`, so setting
 * `color: #fff` on the button makes the arrow white. Inline <svg> (not a
 * Unicode triangle) dodges the WP emoji shim, which would otherwise replace
 * ▶◀▲▼ with a coloured PNG. */
/* Every toggle selector below is qualified with `button`, deliberately.
 *
 * A theme reset that says `[type="button"], [type="submit"], button { ... }` is
 * a single attribute selector - specificity (0,1,0), exactly the same as a lone
 * class - so at equal weight the later stylesheet wins, and plugin CSS almost
 * always loads before the theme. That is how this toggle ended up transparent
 * with the theme's link colour and a 1px theme border on a live site: our rule
 * was not overridden by something stronger, it TIED and lost on source order.
 * `button.` makes it (0,1,1), which no bare element-or-attribute reset can
 * reach, and needs no !important. */
button.w2dc-react-sidebar__toggle {
	position: absolute;
	width: 36px;
	height: 80px;
	background: var(--w2dc-button-primary, var(--w2dc-primary, #2563eb));
	border: none;
	color: var(--w2dc-button-text, #fff);
	cursor: pointer;
	/* Sits one above the sidebar (1600) so it stays clickable when the
	 * sidebar is collapsed and the toggle bleeds outside the sidebar's
	 * translated box. The sidebar family sits above every popup (the
	 * click popup tops out at z 1500), so the toggle is never covered by
	 * a popup card fanning out from a left/right-edge marker. */
	z-index: 1601;
	padding: 0;
	display: flex;
	align-items: center;
	justify-content: center;
	box-shadow: 0 2px 8px rgba(0, 0, 0, 0.22);
	touch-action: manipulation;
	-webkit-tap-highlight-color: transparent;
	transition:
		left 0.25s ease, right 0.25s ease, top 0.25s ease, bottom 0.25s ease,
		background-color 0.15s ease;
	/* Hint to compositor: keep this element on its own layer so the
	 * box-shadow renders ONCE and the layer just slides during the
	 * left/right/top/bottom transition. Without this, every frame of
	 * the 0.25 s position change re-rasters the 8 px shadow over the
	 * map canvas (heavy paint) and the toggle visibly jerks behind
	 * the sidebar's smoother transform-based slide. `transform`-only
	 * promotion is enough to opt the toggle into GPU compositing
	 * even though we're animating layout properties - the slowdown
	 * was paint, not layout. */
	will-change: transform;
	transform: translateZ(0);
}
button.w2dc-react-sidebar__toggle:hover {
	/* Hover swaps to the admin-configured `--w2dc-button-secondary`
	 * slot (Style Manager -> "Buttons" panel) so it stays in lockstep
	 * with the rest of the plugin's interactive surfaces. Fallback
	 * chain: button-secondary -> color-mix darken of button-primary ->
	 * color-mix darken of --w2dc-primary -> hard-coded blue darken.
	 * The `color-mix` fallbacks guarantee a visible shift even on
	 * installs that haven't customised the secondary slot (or set it
	 * equal to primary), which was the original failure mode that
	 * made the hover look inert. */
	background: var(
		--w2dc-button-secondary,
		color-mix(in srgb, var(--w2dc-button-primary, var(--w2dc-primary, #2563eb)) 85%, #000)
	);
}
.w2dc-react-sidebar__toggle-icon { display: block; }

/* Edge orientation - round the side that faces away from the sidebar so
 * the button visually attaches to the sidebar. Centring on the
 * perpendicular axis goes through `transform`. */
button.w2dc-react-sidebar__toggle--left,
button.w2dc-react-sidebar__toggle--right {
	top: 50%;
	transform: translateY(-50%);
}
button.w2dc-react-sidebar__toggle--left   { border-radius: 0 8px 8px 0; }
button.w2dc-react-sidebar__toggle--right  { border-radius: 8px 0 0 8px; }
button.w2dc-react-sidebar__toggle--top,
button.w2dc-react-sidebar__toggle--bottom {
	left: 50%;
	transform: translateX(-50%);
	width: 80px;
	height: 36px;
}
button.w2dc-react-sidebar__toggle--top    { border-radius: 0 0 8px 8px; }
button.w2dc-react-sidebar__toggle--bottom { border-radius: 8px 8px 0 0; }

/* Larger hit area on touch screens - visual size grows too, since a
 * primary-coloured button is meant to be visible/grabbable on phones. */
@media (hover: none) and (pointer: coarse) {
	button.w2dc-react-sidebar__toggle--left,
	button.w2dc-react-sidebar__toggle--right { width: 44px; height: 88px; }
	button.w2dc-react-sidebar__toggle--top,
	button.w2dc-react-sidebar__toggle--bottom { width: 88px; height: 44px; }
}

/* Listings grid - list when sidebar is vertical (left/right), grid when horizontal. */
.w2dc-react-sidebar__grid {
	display: grid;
	gap: 10px;
}
.w2dc-react-sidebar__grid--list { grid-template-columns: 1fr; }
.w2dc-react-sidebar__grid--grid { grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); }

/* Top/bottom sidebar lays cards out in an equal-width grid, so - like the
 * directory grid - pin each card image to 4:3 with `cover` so every row stays
 * even (the popup markup's default is `height:auto; object-fit:contain`, which
 * gave ragged, letterboxed images). The left/right VERTICAL list (`--list`)
 * keeps the natural image height. */
.w2dc-react-sidebar__grid--grid .w2dc-listing-content .w2dc-listing-content__images--single img,
.w2dc-react-sidebar__grid--grid .w2dc-listing-content .w2dc-listing-content__slide img {
	width: 100%;
	height: 100%;
	aspect-ratio: 4 / 3;
	-o-object-fit: cover;
	   object-fit: cover;
	max-width: none;
}
.w2dc-react-sidebar__grid--grid .w2dc-listing-content .w2dc-listing-content__slide {
	aspect-ratio: 4 / 3;
}

.w2dc-react-sidebar__card {
	border: 1px solid #e5e7eb;
	border-radius: 6px;
	padding: 8px 10px;
	background: #fff;
	/* Clip the flush cover image (below) to the card's rounded corners. Only
	 * the leading photo reaches the edge - text cards keep their padding, so
	 * this has no visible effect on them. box-shadow (hover) renders outside
	 * the border box and is NOT clipped by overflow, so the hover lift stays. */
	overflow: hidden;
	transition: border-color 0.12s, box-shadow 0.12s;
}
/* Pointer cursor only when clicking a card actually opens a popup
 * (enable_infowindow). Otherwise the card is informational and a pointer
 * would mis-suggest interactivity.
 *
 * Inner `.w2dc-listing-content *` has `cursor: default` from the
 * popup stylesheet (popup.css) - the universal selector wins over a parent's
 * cursor due to specificity. Force pointer on every card descendant for the
 * clickable case so hovering text/img inside the card still shows pointer. */
.w2dc-react-sidebar__card.is-clickable,
.w2dc-react-sidebar__card.is-clickable .w2dc-listing-content,
.w2dc-react-sidebar__card.is-clickable .w2dc-listing-content * {
	cursor: pointer;
}
.w2dc-react-sidebar__card:hover {
	border-color: var(--w2dc-primary, #2563eb);
	box-shadow: 0 2px 6px rgba(0, 0, 0, 0.06);
}

/* Cover image - the leading photo runs flush to the card's top edge with only
 * the top corners rounded, mirroring the listing card and map popup treatment
 * (see popup.css `.w2dc-card-fields` / `.w2dc-react-infowindow-content` cover
 * rules). The negative margins cancel the card's own 8px/10px padding; the
 * image is the block's first child, or the second when a sticky/featured badge
 * span precedes it. Layout-independent - applies to both the vertical list and
 * the top/bottom grid (the pull only cancels padding; image sizing is untouched). */
.w2dc-react-sidebar__card > .w2dc-listing-content > .w2dc-listing-content__body > .w2dc-listing-content__images:first-child,
.w2dc-react-sidebar__card > .w2dc-listing-content > .w2dc-listing-content__body > span:first-child + .w2dc-listing-content__images {
	margin: -8px -10px 0;
	border-radius: 6px 6px 0 0;   /* matches the card's own 6px radius */
	overflow: hidden;
}
/* Square off the <img> / gallery item so its own rounded bottom (single-image
 * variant, or gallery.css `.sp-gallery__item`) doesn't cut into the card body;
 * the wrapper above owns the top corners. */
.w2dc-react-sidebar__card > .w2dc-listing-content > .w2dc-listing-content__body > .w2dc-listing-content__images:first-child img,
.w2dc-react-sidebar__card > .w2dc-listing-content > .w2dc-listing-content__body > .w2dc-listing-content__images:first-child .sp-gallery__item,
.w2dc-react-sidebar__card > .w2dc-listing-content > .w2dc-listing-content__body > span:first-child + .w2dc-listing-content__images img,
.w2dc-react-sidebar__card > .w2dc-listing-content > .w2dc-listing-content__body > span:first-child + .w2dc-listing-content__images .sp-gallery__item {
	border-radius: 0;
}

.w2dc-react-sidebar__empty {
	text-align: center;
	color: #6b7280;
	font-size: 13px;
	padding: 24px 12px;
}

.w2dc-react-sidebar__overlay {
	position: absolute;
	inset: 0;
	display: flex;
	align-items: center;
	justify-content: center;
	background: rgba(255, 255, 255, 0.7);
	z-index: 4;
}
.w2dc-react-sidebar__tail-loader {
	display: flex;
	justify-content: center;
	padding: 12px;
}

/* =========================================================================
 * Standalone mode - when MapSidebar renders inside [webmap-listings]
 * (StandaloneListings.tsx). Cancels the absolute corner positioning that
 * docks it to the map edges, hides the toggle button (no map to collapse
 * over), and forces grid layout regardless of the map's configured
 * sidebar_position. Keeps internal scroll on a generous max-height so
 * infinite-scroll still triggers as the visitor scans the list.
 * ========================================================================= */
.w2dc-react-listings-standalone {
	width: 100%;
}
.w2dc-react-listings-standalone .w2dc-react-sidebar {
	position: static !important;       /* beats `position: absolute` set on the base class */
	/* MapSidebar applies an inline `style={width: 500}` for left/right positions
	 * (or `height: 500` for top/bottom). Inline styles win normal cascades, so
	 * we need !important here to flip the widget to fill the shortcode's slot. */
	max-width: 100% !important;
	max-height: none !important;
	width: 100% !important;
	height: auto !important;
	top: auto;
	left: auto;
	right: auto;
	bottom: auto;
	transform: none !important;        /* cancel any --left/--right/--top/--bottom collapse translate */
	background: transparent;           /* let the page chrome show through; cards have their own bg */
}
.w2dc-react-listings-standalone button.w2dc-react-sidebar__toggle {
	display: none;
}
.w2dc-react-listings-standalone .w2dc-react-sidebar__scroll {
	max-height: 80vh;
	overflow-y: auto;
}
/* Force grid layout - the in-map vertical sidebar uses --list (single
 * column), but the shortcode lays out horizontally across the page so
 * cards should fill the natural width via auto-fill columns. */
.w2dc-react-listings-standalone .w2dc-react-sidebar__grid,
.w2dc-react-listings-standalone .w2dc-react-sidebar__grid--list {
	grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
}

/* Mapbox / MapLibre native control containers - fullscreen, navigation
 * (compass + pitch), our custom map-type switcher. The GL libs ship
 * these with `z-index: auto` (i.e. effectively 0 inside the parent
 * stacking context), but the canvasOverlayShim's synthetic pane that
 * hosts our marker canvas sits at z-index: 5 - so any cluster that
 * happened to overlap the top-right control stack would PAINT OVER
 * the buttons (marker visible, fullscreen / compass hidden). Lift the
 * control containers above the canvas pane (>5) but keep them below
 * sidebar (10) and the directions panel (1001). All four corner
 * containers covered so future controls placed elsewhere also stay
 * clickable / visible. */
.maplibregl-ctrl-top-left,
.maplibregl-ctrl-top-right,
.maplibregl-ctrl-bottom-left,
.maplibregl-ctrl-bottom-right,
.mapboxgl-ctrl-top-left,
.mapboxgl-ctrl-top-right,
.mapboxgl-ctrl-bottom-left,
.mapboxgl-ctrl-bottom-right {
	z-index: 6;
}

/* Search-form - overlaid on the map at one of the four corners on desktop;
 * on narrow viewports it pops OUT of the map and claims its own row above
 * (top-*) or below (bottom-*) the canvas, so the form never covers map
 * content. Positioning is relative to .w2dc-react-map-wrapper, which is
 * a flex column and reorders form/canvas via order: 0 / 1. */

.w2dc-search-form {
	position: absolute;
	/* z-index hierarchy on the map:
	 *   - sidebar          z 4
	 *   - sidebar toggle   z 6
	 *   - auto-popup layer z 600 (`.w2dc-autopopups-layer`)
	 *   - search form      z 700  ← THIS (above auto-popups so the
	 *                                form is never covered when an
	 *                                auto-popup happens to land
	 *                                under it)
	 *   - click popup      z 1000
	 * The form holds input focus + autocomplete UI, so it must stay
	 * interactable even when auto-popups fan out across the map. */
	z-index: 700;
	width: 400px;
	max-width: calc(100% - 100px);
	display: flex;
	flex-direction: column;
	gap: 8px;
	padding: 14px 16px;
	background: rgba(255, 255, 255, 0.96);
	backdrop-filter: blur(2px);
	border-radius: 8px;
	box-shadow: 0 2px 10px rgba(0, 0, 0, 0.18);
	box-sizing: border-box;
	font-size: 13px;
}
.w2dc-search-form.is-submitting { opacity: 0.7; pointer-events: none; }

/* While a focused task panel (Directions / Contact) is open it owns the map
 * surface. Those panels live INSIDE canvas-host's isolated stacking context
 * (z 1620 there), while this form is a SIBLING of canvas-host in the wrapper
 * context (z 700) - so by stacking-context rules the form would paint OVER the
 * panel no matter how high the panel's z-index. Rather than fight the contexts
 * (lifting canvas-host would push the form behind the markers), fade the form
 * out while a panel is open. It stays mounted, so typed filters and the dragged
 * position survive; it fades back in the moment the panel closes.
 *
 * Desktop only: there the form is `position: absolute` and overlaps the panel.
 * On mobile (<=768px) it drops into its own row above/below the map and never
 * overlaps, so it's left alone. The 0.25s matches the panel's slide-in. */
@media (min-width: 769px) {
	/* Fade the form out/in. The opacity TRANSITION is declared on the
	 * controls-shift rule in sidebar.css (`.w2dc-react-map-wrapper
	 * .w2dc-search-form`, specificity 0,2,0) - it includes both `transform`
	 * (sidebar shift) and `opacity`. A transition set here on a plain
	 * `.w2dc-search-form` (0,1,0) would lose to it and the fade would snap
	 * instantly, so we only TOGGLE opacity here and let that rule animate it.
	 * `pointer-events: none` lets clicks reach the panel; the form is
	 * invisible at opacity 0 so it never covers the panel. */
	.w2dc-react-map-wrapper.has-overlay-panel .w2dc-search-form {
		opacity: 0;
		pointer-events: none;
	}
}

/* Corner chrome - two 22px buttons flush in the form's rounded top corners:
 * the DRAG handle (top-right) moves the form to a new pixel position (saved to
 * localStorage); the collapse TOGGLE (top-left) folds the form down to a compact
 * pill and back. Both appear together, gated by the same `enable_form_drag`
 * ("Allow form drag and toggle") admin option. Flush at 0,0 so they nest in the
 * corners clear of the fields; the position no longer mirrors per anchor because
 * both top corners are now in use. On `position: static` (mobile flow) they are
 * hidden - dragging has no effect there and the form is a full-width block. */
/* Prefixed with the always-present container class (.w2dc-search-form) so the
 * specificity (0,2,0) beats an aggressive theme reset such as hello-elementor's
 * `[type=button],[type=submit],button { border:1px solid #cc3366 }` (0,1,0) -
 * which, at equal specificity to a single-class rule, otherwise wins on load
 * order (theme CSS loads after the plugin) and paints these <button>s pink. */
.w2dc-search-form button.w2dc-search-form__drag,
.w2dc-search-form button.w2dc-search-form__toggle {
	position: absolute;
	width: 22px;
	height: 22px;
	border: 0;
	background: transparent;
	color: #9ca3af;
	padding: 0;
	display: flex;
	align-items: center;
	justify-content: center;
	-webkit-tap-highlight-color: transparent;
	transition: color 0.12s, background-color 0.12s;
	border-radius: 8px;
	z-index: 1;
}
button.w2dc-search-form__drag:hover,
button.w2dc-search-form__toggle:hover {
	color: var(--w2dc-primary, #2563eb);
	background: rgba(0, 0, 0, 0.05);
}
button.w2dc-search-form__drag {
	cursor: grab;
	touch-action: none;             /* let pointer events drive panning */
}
button.w2dc-search-form__drag:active { cursor: grabbing; }
button.w2dc-search-form__toggle { cursor: pointer; }
/* The toggle's chevron points up (collapse) when expanded; rotate it to point
 * down (expand) once the form is collapsed. */
button.w2dc-search-form__toggle svg { transition: transform 0.18s ease; }
.w2dc-search-form.is-collapsed button.w2dc-search-form__toggle svg { transform: rotate(180deg); }

button.w2dc-search-form__drag   { top: 0; right: 0; bottom: auto; left: auto; }
button.w2dc-search-form__toggle { top: 0; left:  0; bottom: auto; right: auto; }

/* Collapsed - the form folds to a compact pill showing just the two corner
 * buttons and a search glyph; the fields are unmounted (removed from the DOM,
 * not merely hidden). width:auto overrides the inline `search_form_width`. */
.w2dc-search-form.is-collapsed {
	width: auto !important;
	min-width: 64px;
	padding: 6px;
	gap: 0;
}
.w2dc-search-form__collapsed {
	display: flex;
	align-items: center;
	justify-content: center;
	min-height: 22px;
	padding: 0 22px;                /* clear the two 22px corner buttons */
	color: #6b7280;
}

/* Hide the corner chrome in the mobile in-flow path - the form is `position:
 * static` there, so drag has no effect and collapse is unnecessary. */
@media (max-width: 768px) {
	button.w2dc-search-form__drag,
	button.w2dc-search-form__toggle { display: none; }
}

.w2dc-search-form--top-left      { top: 50px;    left: 50px; }
.w2dc-search-form--top-right     { top: 50px;    right: 50px; }
.w2dc-search-form--bottom-left   { bottom: 50px; left: 50px; }
.w2dc-search-form--bottom-right  { bottom: 50px; right: 50px; }

/* Adaptive layout - at <=768px the form drops out of the corner overlay
 * and claims its own full-width row above / below the map in normal
 * flow. The wrapper-level rules (sidebar.css) switch the wrapper to
 * height:auto so it grows to fit the stacked form + canvas. The
 * `!important` overrides the inline pixel width React applies from the
 * `search_form_width` admin option.
 *
 * Using `position: relative` (rather than `static`) so the base z-index
 * 700 keeps applying - the form must stack ABOVE canvas-host even
 * after dropping out of corner-overlay mode. Without it, clicks in
 * the form area can be hit-tested against descendants of canvas-host
 * (markers, sidebar listings) when the DOM order paints them on top.
 * Anchor offsets (`top: 50px`, etc. from --top-left/--top-right) are
 * neutralised explicitly so the form sits exactly in its flex slot,
 * not shifted by the desktop anchor amounts. */
@media (max-width: 768px) {
	.w2dc-search-form {
		position: relative;
		top:    auto !important;
		left:   auto !important;
		right:  auto !important;
		bottom: auto !important;
		width: 100% !important;
		max-width: none;
		border-radius: 0;
		box-shadow: 0 1px 0 rgba(0, 0, 0, 0.08);
		flex: 0 0 auto;
	}

	/* Fullscreen - restructure the fullscreen container as a flex
	 * column so the portaled form occupies its own top row and the
	 * map content / sidebar fill the area BELOW it.
	 *
	 *   fullscreen element   -> flex column container.
	 *   > .w2dc-search-form -> order 0, flex 0 0 auto (variable
	 *                          height per content).
	 *   > everything else    -> order 1, flex 1 1 auto (claims the
	 *                          remaining space).
	 *
	 * Three separate rules - :fullscreen (standard, Chrome 71+ /
	 * Safari 16.4+ / Firefox 64+), :-webkit-full-screen (older
	 * Safari / older Chrome), :-moz-full-screen (older Firefox).
	 * CANNOT be merged into a comma-separated list because each
	 * prefix is unknown to the other browsers - one unknown
	 * selector invalidates the whole rule for that engine. */
	:fullscreen {
		display: flex !important;
		flex-direction: column !important;
	}
	:fullscreen > * {
		order: 1;
		flex: 1 1 auto;
		min-height: 0;
	}
	:fullscreen > .w2dc-search-form {
		order: 0;
		flex: 0 0 auto !important;
		width: 100% !important;
		max-width: none !important;
		border-radius: 0 !important;
		box-shadow: 0 1px 0 rgba(0, 0, 0, 0.08) !important;
	}
	:-webkit-full-screen {
		display: flex !important;
		flex-direction: column !important;
	}
	:-webkit-full-screen > * {
		order: 1;
		flex: 1 1 auto;
		min-height: 0;
	}
	:-webkit-full-screen > .w2dc-search-form {
		order: 0;
		flex: 0 0 auto !important;
		width: 100% !important;
		max-width: none !important;
		border-radius: 0 !important;
		box-shadow: 0 1px 0 rgba(0, 0, 0, 0.08) !important;
	}
	:-moz-full-screen {
		display: flex !important;
		flex-direction: column !important;
	}
	:-moz-full-screen > * {
		order: 1;
		flex: 1 1 auto;
		min-height: 0;
	}
	:-moz-full-screen > .w2dc-search-form {
		order: 0;
		flex: 0 0 auto !important;
		width: 100% !important;
		max-width: none !important;
		border-radius: 0 !important;
		box-shadow: 0 1px 0 rgba(0, 0, 0, 0.08) !important;
	}

	/* Class-based fallback - `SearchForm` adds `.w2dc-fs-host` to the
	 * fullscreen element directly (via JS in the fullscreenchange
	 * handler), bypassing any pseudo-class quirks. Same flex-column
	 * recipe as the :fullscreen rules above. */
	.w2dc-fs-host {
		display: flex !important;
		flex-direction: column !important;
	}
	.w2dc-fs-host > * {
		order: 1;
		flex: 1 1 auto;
		min-height: 0;
	}
	.w2dc-fs-host > .w2dc-search-form {
		order: 0;
		flex: 0 0 auto !important;
		width: 100% !important;
		max-width: none !important;
		border-radius: 0 !important;
		box-shadow: 0 1px 0 rgba(0, 0, 0, 0.08) !important;
	}
}

/* ----- Rows / slots ----- */
.w2dc-search-form__row {
	display: flex;
	gap: 8px;
}
.w2dc-search-form__slot {
	/* Width fixed by the row's column count (--cols-N modifier below).
	 * Without this, slots in a 3-column row that has only 2 visible
	 * children (e.g., a separator dropped from the runtime row) would
	 * split 50/50 instead of staying at 1/3 each. */
	flex: 0 0 auto;
	min-width: 0;
	display: flex;
	align-items: stretch;
}
.w2dc-search-form__slot--cols-1 { flex-basis: 100%; }
/* row gap is 8px -> subtract gap share so N slots + (N-1)*8 = 100% width */
.w2dc-search-form__slot--cols-2 { flex-basis: calc(50% - 4px); }
.w2dc-search-form__slot--cols-3 { flex-basis: calc(33.333% - 5.333px); }
/* 2/3 + 1/3 split - the 'twothirds' row's two slots. Same 8px row gap:
 * the two flex-basis subtractions (5.333 + 2.666 ≈ 8) sum to the gap so
 * the pair fills 100% - 8px, matching the --cols-N convention above. */
.w2dc-search-form__slot--w-2of3 { flex-basis: calc(66.666% - 5.333px); }
.w2dc-search-form__slot--w-1of3 { flex-basis: calc(33.333% - 2.666px); }
.w2dc-search-form__slot.is-empty { visibility: hidden; }
/* Container-prefixed (0,2,0) for the same reason as the inputs below: the theme
 * resets that ship with Hello Elementor and friends carry
 * `[type="button"], [type="submit"], button { width: auto }`, and an attribute
 * selector scores (0,1,0) - a TIE with a bare class, decided by load order,
 * which the theme wins. At (0,1,0) this rule stretched the inputs but not the
 * buttons, so Search and Reset shrank to their label widths and a stacked pair
 * came out ragged (86px next to 78px on a customer's site). */
.w2dc-search-form .w2dc-search-form__slot > * { width: 100%; }

/* ----- Inputs (directions-style) ----- */
/* Container-prefixed (0,2,0) so the resting border survives a theme reset that
 * targets bare `button`/`[type=button]` at (0,1,0) - the taxonomy dropdown
 * triggers are <button class="…__input …__taxdd-trigger"> and would otherwise
 * inherit the theme's pink border on load order. Later overrides of this rule
 * (:focus, .is-placeholder, wrap/pair) are already >=0,2,0 and declared after
 * this block, so they keep winning. */
.w2dc-search-form .w2dc-search-form__input {
	/* Theme-proof control box: an explicit height + border-box so the real
	 * <input>/<select> and the <button> __taxdd-trigger resolve to the SAME
	 * height even when a host theme styles bare input{height:50px;font-size:16px}.
	 * Explicit height (not min-height) is required - min-height can't override a
	 * theme's fixed input height, which would re-open the gap. (0,2,0) outranks a
	 * bare `input` (0,0,1). */
	box-sizing: border-box;
	height: 38px;
	border: 1px solid #e5e7eb;
	background: #fff;
	border-radius: 8px;
	padding: 8px 14px;
	font-size: 13px;
	line-height: 1.2;
	color: #1f2937;
	outline: none;
	transition: border-color 0.12s, box-shadow 0.12s;
}
.w2dc-search-form__input::-moz-placeholder { color: #9ca3af; }
.w2dc-search-form__input::placeholder { color: #9ca3af; }
.w2dc-search-form__input:focus {
	border-color: var(--w2dc-primary, #2563eb);
	box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.15);
}

/* Native form controls - checkbox / radio tick colour follows the brand
 * primary so a checked option matches the Search button and slider
 * thumb instead of the browser-default blue. `accent-color` is well-
 * supported on Chrome/Firefox/Safari ≥15.4; older browsers fall back
 * to the OS accent and ignore this rule (graceful degrade). Applies
 * across every checkbox/radio in the form: list field, opening hours,
 * categories/locations multi-select.
 *
 * `appearance: auto !important` + WP-admin overrides reset - needed
 * because the search form is also rendered inside Edit Map's live
 * preview on `?post_type=w2dc_map`, where WP-admin's `forms.css`
 * applies `appearance: none` to every checkbox and re-draws the
 * tick via a `::before` Dashicon. `accent-color` only paints the
 * NATIVE widget, so without forcing appearance back to auto the
 * primary-colour tint silently fails in admin (the rule is
 * harmless on the frontend, where the WP styles aren't loaded -
 * `appearance: auto` is the default there anyway). */
.w2dc-search-form input[type="checkbox"],
.w2dc-search-form input[type="radio"] {
	appearance: auto !important;
	-webkit-appearance: auto !important;
	-moz-appearance: auto !important;
	width: auto;
	height: auto;
	min-width: 0;
	min-height: 0;
	background: initial;
	border: initial;
	border-radius: initial;
	box-shadow: none;
	accent-color: var(--w2dc-primary, #2563eb);
	vertical-align: middle;
}
.w2dc-search-form input[type="checkbox"]::before,
.w2dc-search-form input[type="radio"]::before {
	content: none !important;
}

/* Select-as-placeholder - when the empty option is "selected" (the
 * `<option value="">` whose label IS the field name, e.g. "Methods of
 * Payment") the select should LOOK like a placeholder, not a real
 * choice. Grey text on the closed select tells the user the filter
 * is unset; the same colour on `option[value=""]` makes the dropdown's
 * first row visually distinct from the actual values below it. We
 * also italicise the empty option so admins on systems where
 * `accent-color` is the only differentiator still see the placeholder
 * as a separate kind of row.
 *
 * `.is-placeholder` is added by ListField when `value === ''`; it's
 * removed once the user picks a real option, so the closed select
 * flips back to dark text. */
.w2dc-search-form__input.is-placeholder {
	color: #9ca3af;
}
.w2dc-search-form__input option[value=""] {
	color: #9ca3af;
	font-style: italic;
}
/* Reset for real options - the rule above was inheriting onto siblings
 * because some browsers cascade the parent select's grey to every
 * <option> when the select itself is grey-coloured. Explicit reset
 * keeps the dropdown rows readable. */
.w2dc-search-form__input.is-placeholder option:not([value=""]) {
	color: #1f2937;
	font-style: normal;
}

/* Plain wrapper used by the address input so the clear (×) button can be
 * absolutely positioned on its right edge. The keywords autocomplete
 * uses `.w2dc-search-form__autocomplete` for the same job (it needs the
 * extra ARIA + dropdown DOM); this is the slimmer single-input version.
 *
 * `display: flex` + the input growing to `flex: 1 0 auto` is what
 * propagates the slot's `align-items: stretch` height through to the
 * <input>. Without flex the wrapper stretched but the input inside
 * stayed at its natural ~36px height, leaving the address field shorter
 * than its sibling radius slider on the same row (the clear × then
 * sat at the slot's bottom edge instead of vertically centred). */
.w2dc-search-form__input-wrap {
	position: relative;
	width: 100%;
	display: flex;
	flex-direction: column;
}
.w2dc-search-form__input-wrap > .w2dc-search-form__input {
	flex: 1 0 auto;
	width: 100%;
}
/* (Same height-stretch fix is folded into `.w2dc-search-form__autocomplete`
 * below - see the consolidated rule near the dropdown styles.) */

/* Reserve right padding on inputs that may carry a clear / spinner badge
 * so the user's typed text doesn't run under the icon. Applies to both
 * wrapper variants (autocomplete + plain wrap). */
.w2dc-search-form__input-wrap .w2dc-search-form__input,
.w2dc-search-form__autocomplete .w2dc-search-form__input {
	padding-right: 30px;
}

/* Clear (×) button - appears at the input's right edge when the field is
 * non-empty. Uses absolute positioning so the input layout stays
 * untouched. Subtle hover affordance (gray pill background) signals
 * clickability without screaming for attention. Aria-label set on the
 * button itself for screen readers. */
button.w2dc-search-form__clear {
	position: absolute;
	top: 50%;
	right: 6px;
	transform: translateY(-50%);
	width: 20px;
	height: 20px;
	padding: 0;
	border: 0;
	background: transparent;
	color: #9ca3af;
	cursor: pointer;
	border-radius: 50%;
	font-size: 16px;
	line-height: 1;
	display: flex;
	align-items: center;
	justify-content: center;
	transition: background 0.12s, color 0.12s;
}
button.w2dc-search-form__clear:hover {
	background: #f3f4f6;
	color: #4b5563;
}
button.w2dc-search-form__clear:focus {
	outline: none;
	background: #f3f4f6;
	color: #4b5563;
	box-shadow: 0 0 0 2px rgba(37, 99, 235, 0.25);
}

/* ----- Keywords autocomplete dropdown -----
 * Wrapper is positioned so the absolutely-positioned <ul> anchors below the
 * input. z-index sits above the form's own inputs but BELOW any modal /
 * marker popup the runtime might paint on top.
 *
 * `display: flex` + child `flex: 1 0 auto` propagates the parent slot's
 * `align-items: stretch` height down to the <input>. Without it the
 * input stayed at its natural ~36px and the field looked shorter than
 * its row neighbours (e.g. the radius slider). */
.w2dc-search-form__autocomplete {
	position: relative;
	width: 100%;
	display: flex;
	flex-direction: column;
}
.w2dc-search-form__autocomplete > .w2dc-search-form__input {
	flex: 1 0 auto;
	width: 100%;
}
.w2dc-search-form__autocomplete-list {
	position: absolute;
	top: calc(100% + 2px);
	left: 0;
	right: 0;
	z-index: 20;
	margin: 0;
	padding: 4px 0;
	list-style: none;
	background: #fff;
	border: 1px solid #e5e7eb;
	border-radius: 8px;
	box-shadow: 0 6px 20px rgba(0, 0, 0, 0.12);
	max-height: 280px;
	overflow-y: auto;
	font-size: 13px;
}
.w2dc-search-form__autocomplete-item {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 8px;
	padding: 7px 12px;
	/* Reset margin explicitly - themes / WP admin frequently set
	 * `li { margin-bottom: ... }` which would inflate the gap below the
	 * last item past the 4px list padding-bottom (visible asymmetry vs
	 * the top of the list). */
	margin: 0;
	cursor: pointer;
	color: #1f2937;
	transition: background-color 0.08s;
}
.w2dc-search-form__autocomplete-item.is-active,
.w2dc-search-form__autocomplete-item:hover {
	background: rgba(37, 99, 235, 0.08);
}
.w2dc-search-form__autocomplete-label {
	flex: 1 1 auto;
	min-width: 0;
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}
.w2dc-search-form__autocomplete-count {
	flex: 0 0 auto;
	font-size: 11px;
	color: #6b7280;
	background: #f3f4f6;
	padding: 1px 6px;
	border-radius: 10px;
}
/* Tiny spinner - single-rotation CSS keyframe, no SVG needed. Pinned to
 * the input's right edge so it sits inside the rounded corner. */
.w2dc-search-form__autocomplete-spinner {
	position: absolute;
	top: 50%;
	right: 10px;
	width: 12px;
	height: 12px;
	margin-top: -6px;
	border: 2px solid rgba(37, 99, 235, 0.25);
	border-top-color: var(--w2dc-primary, #2563eb);
	border-radius: 50%;
	animation: w2dc-search-form-spin 0.7s linear infinite;
	pointer-events: none;
}
@keyframes w2dc-search-form-spin {
	to { transform: rotate(360deg); }
}

/* ----- Range / radius slider ----- */
.w2dc-search-form__range {
	display: flex;
	flex-direction: column;
	gap: 4px;
	font-size: 12px;
	color: #4b5563;
}
.w2dc-search-form__range > span { font-weight: 500; }
/* The radius slider is drawn ENTIRELY by us - track and thumb - rather than
 * left to the browser's native widget with only accent-color set.
 *
 * A native range input is at the mercy of the active theme. Hueman Pro, for
 * one, sets `-webkit-appearance: none; -moz-appearance: none` on every
 * `.themeform input` and restores it only for checkboxes, never for range:
 * the track vanished and the slider rendered as a lone floating circle. The
 * sibling number-range widget below never had this problem because it already
 * styles its own track and thumb, so this one now does the same.
 *
 * Everything a theme is likely to bleed onto a bare `input` is reset here, and
 * the pseudo-elements are duplicated per engine because WebKit and Gecko share
 * no selector for them. */
.w2dc-search-form__range > input[type="range"] {
	width: 100%;
	display: block;
	-webkit-appearance: none;
	-moz-appearance: none;
	     appearance: none;
	height: 16px;
	margin: 0;
	padding: 0;
	border: none;
	border-radius: 0;
	background: transparent;
	box-shadow: none;
	cursor: pointer;
	/* Themes love a blanket `transition: all .2s` on inputs, which makes the
	 * thumb lag behind the pointer while dragging. */
	transition: none;
	accent-color: var(--w2dc-primary, #2563eb);
}
.w2dc-search-form__range > input[type="range"]:focus { outline: none; }
.w2dc-search-form__range > input[type="range"]:focus-visible {
	outline: 2px solid var(--w2dc-primary, #2563eb);
	outline-offset: 2px;
}
/* ----- track ----- */
.w2dc-search-form__range > input[type="range"]::-webkit-slider-runnable-track {
	height: 4px;
	border: none;
	border-radius: 2px;
	background: #d1d5db;
}
.w2dc-search-form__range > input[type="range"]::-moz-range-track {
	height: 4px;
	border: none;
	border-radius: 2px;
	background: #d1d5db;
}
/* Gecko fills the traversed part for free; WebKit has no equivalent, so the
 * track simply stays grey there. */
.w2dc-search-form__range > input[type="range"]::-moz-range-progress {
	height: 4px;
	border-radius: 2px;
	background: var(--w2dc-primary, #2563eb);
}
/* ----- thumb (same 16px disc as the number-range widget) ----- */
.w2dc-search-form__range > input[type="range"]::-webkit-slider-thumb {
	-webkit-appearance: none;
	appearance: none;
	width: 16px;
	height: 16px;
	border-radius: 50%;
	background: var(--w2dc-primary, #2563eb);
	border: 2px solid #fff;
	box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25);
	cursor: pointer;
	/* WebKit puts the thumb on the track's top edge, so pull it up by half the
	 * difference between the two heights to centre it: (4 - 16) / 2. */
	margin-top: -6px;
}
.w2dc-search-form__range > input[type="range"]::-moz-range-thumb {
	width: 16px;
	height: 16px;
	border-radius: 50%;
	background: var(--w2dc-primary, #2563eb);
	border: 2px solid #fff;
	box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25);
	cursor: pointer;
}

/* ----- Buttons -----
 * Hover deliberately keeps the SAME palette as the resting state - only a
 * very small darken/brighten via box-shadow inset so the buttons don't
 * look broken when the host theme overrides global `button` styles
 * (themes often re-color text/bg on :hover for non-namespaced buttons).
 * `!important` on color/background-color guards against those overrides. */
/* KNOWN, UNFIXED: the size properties here lose. A theme reset carrying
 * `[type="button"] { font-size: 1rem; padding: 0.5rem 1rem }` scores the same
 * (0,1,0) as this class and wins on load order, so on such a site these buttons
 * render at the theme's 16px/8px-16px rather than 13px/7px-16px (measured on a
 * customer's front page). Colour survives only because of the `!important`
 * below. Raising this block to (0,2,0) did NOT take in testing - the injected
 * rule matched the element and still lost - so the cause is not plain
 * specificity and the "obvious" fix is not one. Left alone deliberately rather
 * than shipped unverified; the layout half of the same conflict IS fixed, on
 * the `__slot > *` width rule above. */
button.w2dc-search-form__btn {
	border: 1px solid #e5e7eb !important;
	background-color: #fff !important;
	color: #1f2937 !important;
	font-weight: 500;
	font-size: 13px;
	padding: 7px 16px;
	border-radius: 8px;
	cursor: pointer;
	transition: box-shadow 0.12s, border-color 0.12s;
	text-decoration: none;
	box-shadow: none;
}
button.w2dc-search-form__btn:hover {
	background-color: #fff !important;
	color: #1f2937 !important;
	border-color: #cbd5e1 !important;
	box-shadow: 0 1px 3px rgba(0, 0, 0, 0.06);
}
button.w2dc-search-form__btn:active {
	box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.08);
}
button.w2dc-search-form__btn--primary {
	background-color: var(--w2dc-primary, #2563eb) !important;
	color: #fff !important;
	border-color: var(--w2dc-primary, #2563eb) !important;
}
button.w2dc-search-form__btn--primary:hover {
	background-color: var(--w2dc-secondary, var(--w2dc-primary, #1f82a5)) !important;
	color: #fff !important;
	border-color: var(--w2dc-secondary, var(--w2dc-primary, #1f82a5)) !important;
	box-shadow: 0 2px 6px rgba(0, 0, 0, 0.12);
}
button.w2dc-search-form__btn:disabled,
button.w2dc-search-form__btn:disabled:hover {
	opacity: 0.5;
	cursor: not-allowed;
	box-shadow: none;
}

/* ----- Separator (collapsible "more filters") -----
 * Same hover-stability strategy as the Search/Reset buttons: !important on
 * color/background guards against host-theme `button:hover` overrides that
 * would otherwise flip text/bg into something illegible. Full-width pill
 * to match the visual weight of the Search/Reset buttons stacked above. */
.w2dc-search-form__separator {
	display: flex;
	margin: 4px 0;
}
button.w2dc-search-form__separator-btn {
	width: 100%;
	border: 1px solid #e5e7eb !important;
	background-color: #fff !important;
	color: var(--w2dc-primary, #2563eb) !important;
	font-size: 13px;
	font-weight: 600;
	cursor: pointer;
	display: flex;
	align-items: center;
	justify-content: center;
	gap: 6px;
	padding: 7px 16px;
	border-radius: 8px;
	box-shadow: none;
	text-decoration: none;
	transition: box-shadow 0.12s, border-color 0.12s;
}
button.w2dc-search-form__separator-btn:hover {
	background-color: #fff !important;
	color: var(--w2dc-primary, #2563eb) !important;
	border-color: #cbd5e1 !important;
	box-shadow: 0 1px 3px rgba(0, 0, 0, 0.06);
}
button.w2dc-search-form__separator-btn:active {
	box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.08);
}
button.w2dc-search-form__separator-btn .w2dc-fa { transition: transform 0.18s ease; }
button.w2dc-search-form__separator-btn.is-open .w2dc-fa { transform: rotate(90deg); }

/* ----- Number / Price / DateTime pair (From / To inputs side-by-side) ----- */
.w2dc-search-form__pair {
	display: grid;
	grid-template-columns: 1fr 1fr;
	gap: 6px;
	min-width: 0;
}
.w2dc-search-form__pair > .w2dc-search-form__input {
	min-width: 0;
}

/* ----- Number / Price as dual-thumb range slider ----- */
.w2dc-search-form__rangepair {
	display: flex;
	flex-direction: column;
	gap: 4px;
	font-size: 12px;
	color: #4b5563;
}
.w2dc-search-form__rangepair-label { font-weight: 500; }
/* Dual-thumb slider - visible track is drawn ONCE via a gradient on the
 * parent (`::before`), with the active span between the two thumbs in
 * the brand primary colour and the rest in light grey. The two range
 * inputs themselves are transparent (no native track / fill / thumb)
 * and only contribute hit-areas for their thumbs. The thumb is
 * re-styled via the vendor pseudo-elements so it visually matches.
 *
 * The two CSS custom properties --w2dc-rp-lo and --w2dc-rp-hi are set
 * by React on the track div to position the active span (percentages
 * from `min`..`max`). */
.w2dc-search-form__rangepair-track {
	position: relative;
	height: 28px;
}
.w2dc-search-form__rangepair-track::before {
	content: '';
	position: absolute;
	left: 0;
	right: 0;
	top: 50%;
	height: 4px;
	transform: translateY(-50%);
	background: linear-gradient(
		to right,
		#e5e7eb 0%,
		#e5e7eb var(--w2dc-rp-lo, 0%),
		var(--w2dc-primary, #2563eb) var(--w2dc-rp-lo, 0%),
		var(--w2dc-primary, #2563eb) var(--w2dc-rp-hi, 100%),
		#e5e7eb var(--w2dc-rp-hi, 100%)
	);
	border-radius: 2px;
	pointer-events: none;
}
.w2dc-search-form__rangepair-track > input[type="range"] {
	position: absolute;
	left: 0;
	right: 0;
	top: 50%;
	height: 16px;
	transform: translateY(-50%);
	width: 100%;
	margin: 0;
	background: transparent;
	-webkit-appearance: none;
	-moz-appearance: none;
	     appearance: none;
	/* Track doesn't catch events - pointer events live on the thumb
	 * pseudo-element below so dragging the lower thumb still works
	 * even when the upper input is layered on top. */
	pointer-events: none;
}
/* Native track - hidden, gradient on parent provides the visual. */
.w2dc-search-form__rangepair-track > input[type="range"]::-webkit-slider-runnable-track {
	background: transparent;
	border: none;
	height: 16px;
}
.w2dc-search-form__rangepair-track > input[type="range"]::-moz-range-track {
	background: transparent;
	border: none;
	height: 16px;
}
.w2dc-search-form__rangepair-track > input[type="range"]::-moz-range-progress {
	background: transparent;
}
/* Thumb - re-skinned to match the primary palette + restored
 * hit-testing so the user can grab and drag it. */
.w2dc-search-form__rangepair-track > input[type="range"]::-webkit-slider-thumb {
	-webkit-appearance: none;
	appearance: none;
	pointer-events: auto;
	width: 16px;
	height: 16px;
	border-radius: 50%;
	background: var(--w2dc-primary, #2563eb);
	border: 2px solid #fff;
	box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25);
	cursor: pointer;
	margin-top: 0; /* thumb fills the 16px runnable track, centred on the line */
}
.w2dc-search-form__rangepair-track > input[type="range"]::-moz-range-thumb {
	pointer-events: auto;
	width: 16px;
	height: 16px;
	border-radius: 50%;
	background: var(--w2dc-primary, #2563eb);
	border: 2px solid #fff;
	box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25);
	cursor: pointer;
}

/* ----- List (checkbox / radio grid) ----- */
.w2dc-search-form__optgrid {
	display: grid;
	gap: 4px 10px;
}
.w2dc-search-form__opt {
	display: inline-flex;
	align-items: center;
	gap: 6px;
	font-size: 12.5px;
	cursor: pointer;
}
.w2dc-search-form__opt input { margin: 0; }

/* ----- Opening hours (single checkbox) ----- */
.w2dc-search-form__checkbox {
	display: inline-flex;
	align-items: center;
	gap: 8px;
	/* Vertical padding only - horizontal padding caused the option to
	 * sit indented relative to other rows in the form. */
	padding: 6px 0;
	font-size: 12.5px;
	cursor: pointer;
}
.w2dc-search-form__checkbox input { margin: 0; }

/* ----- Multi-select checklist (categories / locations with multiple = true) ----- */
.w2dc-search-form__checklist {
	display: flex;
	flex-direction: column;
	gap: 2px;
	max-height: 180px;
	overflow-y: auto;
	border: 1px solid #e5e7eb;
	border-radius: 8px;
	background: #fff;
	padding: 6px 4px;
	font-size: 12.5px;
}
/* Slim, rounded scrollbar for the checklist - matches the form's grey palette
 * instead of the chunky native bar. Firefox reads scrollbar-width/color; WebKit
 * (Chrome/Safari/Edge) uses the ::-webkit-scrollbar pseudo-elements. The 2px
 * white thumb border (against the checklist's white bg) makes it read as a slim
 * inset pill. */
.w2dc-search-form__checklist {
	scrollbar-width: thin;
	scrollbar-color: #cbd5e1 transparent;
}
.w2dc-search-form__checklist::-webkit-scrollbar { width: 10px; }
.w2dc-search-form__checklist::-webkit-scrollbar-track { background: transparent; }
.w2dc-search-form__checklist::-webkit-scrollbar-thumb {
	background: #cbd5e1;
	border-radius: 8px;
	border: 2px solid #fff;
	background-clip: padding-box;
}
.w2dc-search-form__checklist::-webkit-scrollbar-thumb:hover { background: #94a3b8; background-clip: padding-box; }
.w2dc-search-form__checklist-item {
	display: flex;
	align-items: center;
	gap: 6px;
	padding: 3px 6px;
	border-radius: 4px;
	cursor: pointer;
}
.w2dc-search-form__checklist-item:hover { background: #f3f4f6; }
.w2dc-search-form__checklist-item input[type="checkbox"] { margin: 0; }
.w2dc-search-form__checklist-item span { flex: 1 1 auto; min-width: 0; }

/* A vertically-merged (rowSpan>1) slot is as tall as the neighbour rows it
 * spans. Let a scrollable checklist fill that whole height instead of stopping
 * at its 180px cap: the list is taken out of flow (absolute) so its item count
 * can't feed back into the grid's auto row sizing - the slot keeps the height
 * its neighbour column defines, and the list scrolls inside it. */
.w2dc-search-form__slot.is-span { position: relative; }
.w2dc-search-form__slot.is-span .w2dc-search-form__checklist {
	position: absolute;
	inset: 0;
	max-height: none;
	box-sizing: border-box;
}

/* ----- Placeholders for not-yet-implemented field types ----- */
.w2dc-search-form__placeholder {
	font-size: 12px;
	color: #9ca3af;
	font-style: italic;
	padding: 6px 10px;
	background: #f9fafb;
	border: 1px dashed #d1d5db;
	border-radius: 6px;
	text-align: center;
}

/* =========================================================================
 * Standalone mode - when [webmap-search] renders outside the map (its own
 * place in the page flow). Cancels the absolute corner positioning so the
 * form behaves as a regular block element. The drag handle has no meaning
 * in static layout, so it's hidden too. Width still comes from inline
 * style (config.search_form_width) but reads as the form's natural max
 * because the host is a normal block.
 * ========================================================================= */
.w2dc-react-search-standalone {
	width: 100%;
}
.w2dc-react-search-standalone .w2dc-search-form {
	/* `relative` (not `static`) + a z-index so the form's own stacking context
	 * -- it already forms one via `backdrop-filter` -- becomes a POSITIONED
	 * context that paints above the map. Off-map, the form and the map live in
	 * separate page blocks with the map usually AFTER the form in the DOM; the
	 * map wrapper is `position: relative` + `isolation: isolate` (a positioned
	 * stacking context), so a static form's context would paint underneath it
	 * and bury this form's dropdown popups (locations/categories, keyword
	 * autocomplete). Kept low (5) so it never covers a site header / sticky nav. */
	position: relative;
	z-index: 5;
	top: auto;
	left: auto;
	right: auto;
	bottom: auto;
	max-width: 100%;
}
.w2dc-react-search-standalone button.w2dc-search-form__drag,
.w2dc-react-search-standalone button.w2dc-search-form__toggle {
	display: none;
}

/* ----- Searchable single-select dropdown (categories / locations with
 *       multiple = false) - custom replacement for the native <select>.
 *       Adds a type-to-filter search box and remembers the list's scroll
 *       position between opens; a native <select> popup is browser-owned,
 *       so its scroll can be neither read nor restored from JS. ----- */
.w2dc-search-form__taxdd { position: relative; width: 100%; display: flex; }
.w2dc-search-form__taxdd,
.w2dc-search-form__taxdd * { box-sizing: border-box; }
/* Container-prefixed (0,2,0) so width/display/appearance survive a theme reset
 * that targets bare `button`/`[type=button]` at (0,1,0) - hello-elementor's
 * `button { width:auto; appearance:button }` otherwise wins on load order and
 * shrinks these full-width dropdown triggers to their content. */
.w2dc-search-form .w2dc-search-form__taxdd-trigger {
	width: 100%;
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 8px;
	cursor: pointer;
	text-align: left;
	font-family: inherit;
	-webkit-appearance: none;
	-moz-appearance: none;
	     appearance: none;
	/* Guard against host themes that repaint `button:hover` with their
	 * accent colour - the trigger must keep its plain input look. */
	background-color: #fff !important;
	color: #1f2937 !important;
}
.w2dc-search-form__taxdd-trigger.is-placeholder { color: #9ca3af !important; }
/* Hold the resting border on hover too - themes' `button:hover` often
 * drop it. `:hover` (0,2,0) outranks the theme's `button:hover` (0,1,1)
 * without !important, so the :focus glow border is left untouched. */
.w2dc-search-form__taxdd-trigger:hover { border: 1px solid #e5e7eb; }
.w2dc-search-form__taxdd-value {
	flex: 1 1 auto;
	min-width: 0;
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}
.w2dc-search-form__taxdd-caret {
	flex: 0 0 auto;
	color: #9ca3af;
	font-size: 10px;
	line-height: 1;
}
/* Small reset (x) - shown in place of the caret while a value is
 * selected; clears the filter back to "Any" without opening the list. */
.w2dc-search-form__taxdd-clear {
	flex: 0 0 auto;
	display: flex;
	align-items: center;
	justify-content: center;
	width: 16px;
	height: 16px;
	border-radius: 50%;
	color: #9ca3af;
	font-size: 14px;
	line-height: 1;
	cursor: pointer;
	transition: background-color 0.12s, color 0.12s;
}
.w2dc-search-form__taxdd-clear:hover {
	background: #f3f4f6;
	color: #4b5563;
}
.w2dc-search-form__taxdd-panel {
	position: absolute;
	top: calc(100% + 2px);
	left: 0;
	right: 0;
	z-index: 20;
	background: #fff;
	border: 1px solid #e5e7eb;
	border-radius: 8px;
	box-shadow: 0 6px 20px rgba(0, 0, 0, 0.12);
	overflow: hidden;
}
.w2dc-search-form__taxdd-search {
	width: 100%;
	border: 0;
	border-bottom: 1px solid #e5e7eb;
	padding: 8px 12px;
	font-size: 13px;
	font-family: inherit;
	color: #1f2937;
	outline: none;
}
.w2dc-search-form__taxdd-search::-moz-placeholder { color: #9ca3af; }
.w2dc-search-form__taxdd-search::placeholder { color: #9ca3af; }
.w2dc-search-form__taxdd-list {
	max-height: 260px;
	overflow-y: auto;
	font-size: 13px;
}
/* Virtual-scroll spacer - full list height; only the windowed option
 * rows (absolutely positioned by their index) are actually mounted. */
.w2dc-search-form__taxdd-spacer {
	position: relative;
	width: 100%;
}
.w2dc-search-form__taxdd-option {
	position: absolute;
	left: 0;
	right: 0;
	height: 32px;
	line-height: 32px;
	padding: 0 12px;
	cursor: pointer;
	color: #1f2937;
	white-space: nowrap;
	overflow: hidden;
	text-overflow: ellipsis;
	transition: background-color 0.08s;
}
.w2dc-search-form__taxdd-option:hover { background: rgba(37, 99, 235, 0.08); }
.w2dc-search-form__taxdd-option.is-selected {
	background: rgba(37, 99, 235, 0.12);
	font-weight: 600;
}
.w2dc-search-form__taxdd-empty {
	padding: 10px 12px;
	color: #9ca3af;
	font-style: italic;
	text-align: center;
}

/* Host-gated star-rating filter (renders only when the rating provider
   injects cfg.ratings_search - see the 'ratings' case in SearchForm.tsx).
   The 2px flex gap is the ONLY spacing between glyphs (family standard,
   matching the compact badge) - the buttons carry no horizontal padding. */
.w2dc-search-form__ratings {
	display: flex;
	align-items: center;
	gap: 2px;
	padding: 4px 0;
}
.w2dc-search-form__ratings-label {
	margin-right: 6px;
	line-height: 1.2;
	white-space: nowrap;
}
.w2dc-search-form__ratings-star {
	background: none;
	border: 0;
	padding: 0;
	margin: 0;
	font-size: 16px; /* fallback; the slot's size option sets it inline */
	line-height: 1;
	cursor: pointer;
	transition: transform .1s ease;
}
.w2dc-search-form__ratings-star:hover { transform: scale(1.15); }
/* The chosen boundary star (SearchForm adds .is-active at `current`) stays
   enlarged so the picked "N and better" rating reads at a glance after the
   pointer leaves. Placed after :hover so hovering the active star keeps it big. */
.w2dc-search-form__ratings-star.is-active { transform: scale(1.25); }
.w2dc-search-form__ratings-star:focus-visible {
	outline: 2px solid currentColor;
	outline-offset: 1px;
	border-radius: 2px;
}

/* ---- the AI note, and the active-filter chips ------------------------------
   Two pieces in two places, belonging to two different things. The NOTE is a
   disclosure: it sits in the corner of the form and says the typed words will
   be read by a service, so it belongs where the typing happens. The CHIPS
   belong to the SEARCH rather than to AI - one per active filter, whatever set
   it - and they sit above the results because the results are what they
   explain. */

.w2dc-search-form__ai-note {
	position: absolute;
	/* Sits just OUTSIDE the form's top-right corner, like a sticker on an
	   envelope: the corner is where a label belongs, and letting it break the
	   edge is what stops it reading as one more field. */
	top: -11px;
	right: -8px;
	z-index: 1;                     /* above the form's own chrome and fields */
	padding: 3px 9px;
	border-radius: 999px;
	background: var(--w2dc-primary, #2563eb);
	color: #fff;
	font-size: 10px;
	font-weight: 700;
	line-height: 1.4;
	letter-spacing: .04em;
	text-transform: uppercase;
	white-space: nowrap;
	box-shadow: 0 1px 4px rgba(0, 0, 0, .25);
	/* The drag handle owns the same corner, and it is the control - so the
	   label never takes a click that was meant for it. */
	pointer-events: none;
}
/* The badge hangs off the FORM, so the form has to be a positioning context. It
   is one everywhere except inside a directory element, which sets it static on
   purpose (directory-front.css) - and a static form hands the badge to whatever
   ancestor is positioned, where "the top right corner" is a corner of the page
   rather than of the form. `relative` on an element already in normal flow
   moves nothing; it only gives the badge something to hang off. Scoped with
   :has() so it touches only the forms that carry one. */
.w2dc-dir-el--search .w2dc-search-form:has(.w2dc-search-form__ai-note) { position: relative; }

/* On the map the drag handle owns the same corner, so the badge steps left of
   it - the handle is 22px wide and flush to the edge. Off the map that handle
   is hidden but still in the DOM, so the two selectors below put the badge back
   on the corner; they carry an extra class to outrank the rule above them. */
.w2dc-search-form:has(.w2dc-search-form__drag) .w2dc-search-form__ai-note { right: 18px; }
.w2dc-react-search-standalone .w2dc-search-form:has(.w2dc-search-form__drag) .w2dc-search-form__ai-note,
.w2dc-dir-el--search .w2dc-search-form:has(.w2dc-search-form__drag) .w2dc-search-form__ai-note { right: -8px; }

.w2dc-search-chips {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 6px;
	margin: 0 0 10px;
	padding: 8px 10px;
	background: rgba(0, 0, 0, .03);
	border-radius: 4px;
	font-size: 13px;
	line-height: 1.4;
}
.w2dc-search-chips__lead {
	font-size: 11px;
	text-transform: uppercase;
	letter-spacing: .03em;
	opacity: .6;
	margin-right: 2px;
}
.w2dc-search-chips__chip {
	display: inline-flex;
	align-items: center;
	gap: 6px;
	padding: 3px 8px 3px 10px;
	border: 1px solid rgba(0, 0, 0, .15);
	border-radius: 999px;
	background: #fff;
	color: inherit;
	font: inherit;
	line-height: 1.5;
	cursor: pointer;
}
.w2dc-search-chips__chip:hover { border-color: rgba(0, 0, 0, .35); }
.w2dc-search-chips__x { font-size: 15px; line-height: 1; opacity: .55; }
.w2dc-search-chips__chip:hover .w2dc-search-chips__x { opacity: 1; }
.w2dc-search-chips__clear {
	border: 0;
	background: none;
	padding: 3px 4px;
	font: inherit;
	color: inherit;
	opacity: .6;
	cursor: pointer;
	text-decoration: underline;
}
.w2dc-search-chips__clear:hover { opacity: 1; }
.w2dc-search-chips__notice { opacity: .7; font-size: 12px; }

/* Auto-popup layer - DOM popups auto-placed around viewport markers. */

.w2dc-autopopups-layer {
	position: absolute;
	inset: 0;
	overflow: hidden;
	pointer-events: none; /* only the cards themselves are interactive */
	/* Above the canvas marker overlay (engine panes z 5-6) but below
	   the click-popup. Click-popup's `OverlayView` container is moved
	   OUT of floatPane and APPENDED DIRECTLY to mapDiv with z-index
	   1500 (see `Popup.tsx::onAdd`), so it sits above this layer
	   regardless of the layer's own z. Earlier this was lowered to
	   z 3 to let floatPane's z 4 win, but that put auto-popups BELOW
	   the canvas marker layer too - cluster bubbles painted on top of
	   the popup cards (the user reported "канва выше попапов?"). The
	   "move click-popup to mapDiv level" approach is the architectural
	   fix; this layer stays at z 600 (the original value), safely
	   above canvas markers. */
	z-index: 600;
}

/* Wrapper around each popup card + its tail, owns the SHARED shadow.
   `filter: drop-shadow` follows the COMBINED alpha shape of both
   children - the white Paper body and the white diamond tail merge
   into one outline (their overlap at the card edge is white-on-
   white), and the shadow traces the merged perimeter.

   Replaces the previous per-element `box-shadow` setup where the
   Paper's outward elevation 4 shadow painted OVER the tail's outer
   half (tail "barely visible under the shadow") and the tail's own
   box-shadow leaked INSIDE the card body. `filter: drop-shadow` is
   the only CSS tool that lets two shapes share one shadow outline.

   Zero-sized container: the children position themselves via
   `transform` in layer coordinates (the RAF loop sets it every
   frame). The browser's filter input region tracks the painted
   descendants no matter where they land, so drop-shadow renders
   correctly regardless of the wrapper's declared `width` / `height`.
   Using `inset: 0` would force the filter to compute over the entire
   layer area for every popup.

   `pointer-events: none` lets clicks reach the card (which has
   `pointer-events: auto` set in PopupCard's autoLayout sx); the
   wrapper itself never claims input. `filter` also creates a new
   stacking context per wrapper - `.w2dc-autopopup-tail` uses
   `z-index: 6` to paint over the Paper (z 5) INSIDE its group;
   cross-group stacking falls back to DOM order, which is fine
   because the layout solver doesn't let popups overlap. */
.w2dc-autopopup-group {
	position: absolute;
	left: 0;
	top: 0;
	width: 0;
	height: 0;
	pointer-events: none;
	filter: drop-shadow(0 2px 5px rgba(0, 0, 0, 0.25));
}

/* One DOM tail per popup - an elongated RHOMBUS clipped from a
   14×8 white box, positioned SYMMETRICALLY around its centre on the
   card-edge intersection of the card-centre->marker ray.

   The RAF loop in `AutoPopupLayer.tsx -> tailTransform()` writes the
   element's `transform` every frame:
     - `translate(...)` puts the rhombus's geometric centre on the
       card-edge intersection point (ex, ey),
     - `rotate(...)` aligns the long diagonal with the
       card-centre->marker ray,
     - `scaleX(...)` stretches the natural 14-px reference length to
       `2 × edgeToTip`, so the rhombus extends `edgeToTip` outside
       the card (outer long-axis vertex sits on the marker) AND the
       same distance INSIDE the card body (inner long-axis vertex
       hidden under the white Paper).

   `transform-origin: 50% 50%` keeps the rotate AND scaleX pivots at
   the rhombus's geometric centre, so the centre point stays on the
   card-edge anchor regardless of rotation or stretch.

   `clip-path: polygon(0% 50%, 50% 0%, 100% 50%, 50% 100%)`:
     - (0%,   50%) -> outer-left vertex   (inner long-axis tip, ends
                                          up INSIDE the card body
                                          after rotate aligns long
                                          axis with the marker ray
                                          - hidden by Paper)
     - (50%,  0%)  -> top vertex          (short-axis end)
     - (100%, 50%) -> outer-right vertex  (outer long-axis tip, on
                                          the marker pixel)
     - (50%,  100%) -> bottom vertex      (short-axis end)
   After `scaleX(s)` around 50% 50%, the outer-left/right vertices
   stretch symmetrically outward; the top/bottom vertices keep their
   x at the centre and y at the box edges. The two short-axis
   vertices stay at the card-edge line; for diagonal-direction
   markers one of them lands OUTSIDE the card (visible) while the
   opposite one lands INSIDE (hidden by Paper). Net: the user sees
   the outer long-axis tip on the marker + one short-axis vertex
   visible to the side = 2 visible vertices.

   No own `box-shadow` - the parent `.w2dc-autopopup-group` applies
   `filter: drop-shadow` over the merged card+tail alpha, producing
   one continuous outline around the rounded card plus its rhombus.
   Half of the rhombus is overlaid on the card body (white on white
   - invisible seam), so the drop-shadow contour just adds the
   visible outer half as a tear-drop protrusion. */
.w2dc-autopopup-tail {
	position: absolute;
	left: 0;
	top: 0;
	width: 14px;
	height: 8px;
	background: #ffffff;
	/* Classic rhombus tail. Tried softening it to ellipse / lens
	 * shapes - at long stretches both read worse than the sharp
	 * rhombus (ellipse became a fat sausage, lens looked like a
	 * leaf). Reverted to the original. */
	clip-path: polygon(0% 50%, 50% 0%, 100% 50%, 50% 100%);
	opacity: 0;
	pointer-events: none;
	transform-origin: 50% 50%;
	will-change: transform, opacity;
	/* BELOW the Paper (z 5) within the `.w2dc-autopopup-group`
	   stacking context, so the inner half of the rhombus (which
	   overlaps the card body after the inward-shift positioning
	   in `tailTransform()`) is OBSCURED by the Paper. Without this
	   the white rhombus painted OVER the Paper's content (title,
	   chips, etc.) - visually the rhombus extended INTO the popup
	   rather than behind it.
	   `filter: drop-shadow` lives on `.w2dc-autopopup-group` and
	   operates on the combined Paper+tail alpha output, so it
	   outlines only the visible (protruding) outer half of the
	   rhombus regardless of which child paints on top. Paper here
	   uses `elevation={0}` (see `PopupCard.tsx` autoLayout branch)
	   so it has no `box-shadow` of its own - that was the original
	   reason we'd bumped the tail to z 6 in an earlier revision;
	   with the wrapper drop-shadow taking over the shadow job, the
	   tail can sit safely under the Paper. */
	z-index: 4;
}


