.kfse-app {
	--kfse-navy: #0b2545;
	--kfse-blue: #1d6fa5;
	--kfse-gold: #c8952c;
	/* Fill of the destination marker whose detail panel is open. */
	--kfse-selected: #0b2545;
	/* Ring of the Singapore hub marker. */
	--kfse-hub: #0b2545;
	--kfse-green: #2f9e5c;
	--kfse-purple: #7a4fc9;
	--kfse-border: #e1e5ea;
	--kfse-bg: #f8f9fb;
	box-sizing: border-box;
	max-width: 100%;
	margin: 1.5em 0;
	font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
	color: #1a2433;
	border: 1px solid var(--kfse-border);
	border-radius: 10px;
	overflow: hidden;
	background: #fff;
}
.kfse-app *, .kfse-app *::before, .kfse-app *::after {
	box-sizing: border-box;
}
/* The host theme's own typography rules (very commonly targeting bare h1-h6,
   p, button, input selectors) can otherwise override this widget's fonts and
   colors, since an inherited value loses to ANY rule that matches an element
   directly, regardless of specificity. Pinning font-family here for the whole
   subtree keeps the widget's look consistent no matter what theme it's
   embedded in. */
.kfse-app, .kfse-app * {
	font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif !important;
}

.kfse-header {
	position: relative;
	/* 48px sides keep every text line clear of the corner logo (left 10px +
	   30px wide = 40px), whatever the font: at 36px, a first line long enough
	   to span the header (e.g. iPhone's narrower system font fitting "SIA
	   Spontaneous Escapes (September") started under the logo. Equal on both
	   sides so the lines stay centred. */
	padding: 7px 48px 6px;
	background: linear-gradient(135deg, var(--kfse-navy), #123a6b);
}
.kfse-header, .kfse-header * {
	color: #fff;
}
.kfse-corner-logo {
	position: absolute;
	top: 8px;
	left: 10px;
	width: 30px;
	height: 30px;
	border-radius: 50%;
	background: #fff;
	padding: 3px;
	object-fit: contain;
	box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
}
/* Desktop: the logo sits right beside the map's name instead of out in the
   corner, and the logo + name pair is centred as one unit (1fr | logo |
   name | 1fr) - centring the name alone left the pair visibly right of
   centre. Phones keep the corner logo above, where the name fills the
   width anyway. */
@media (min-width: 601px) {
	.kfse-header {
		display: grid;
		grid-template-columns: 1fr auto auto 1fr;
		align-items: center;
		column-gap: 10px;
		padding: 8px 16px 7px;
	}
	.kfse-corner-logo {
		position: static;
		grid-column: 2;
		grid-row: 1;
	}
	.kfse-title {
		grid-column: 3;
		grid-row: 1;
	}
	.kfse-header > :not(.kfse-corner-logo):not(.kfse-title) {
		grid-column: 1 / -1;
	}
}
/* Themes commonly set their own margin/line-height on bare h1-h6/p selectors
   too, which — like the font-family rule above — otherwise wins over these
   plain (non-!important) margins and reopens exactly the gap they're meant
   to close. !important here, same as elsewhere in this file, guarantees the
   compact spacing actually renders regardless of the host theme. The title
   is an <h3>, and milelion.com's ".td-post-content h3 { font-size: 24px }"
   outranks a plain class rule, so its size needs !important too (without it
   the title rendered at 24px and, on phones, ran into the corner logo). */
/* Header text sizes are fixed px (Aaron's choice: 20 / 13 / 11), not em, so
   they don't shift with the host theme's body font size. */
.kfse-title {
	margin: 0 0 1px !important;
	font-size: 20px !important;
	line-height: 1.25 !important;
	color: #fff !important;
	text-align: center !important;
}
/* The bracketed part of the map's title - e.g. "October 2026" from "SIA
   Spontaneous Escapes (October 2026)" - on its own line under the name
   (split in class-kfse-shortcode.php). */
.kfse-title-month {
	margin: 0 0 2px !important;
	font-size: 17px !important;
	font-weight: 600 !important;
	line-height: 1.25 !important;
	color: #fff !important;
	text-align: center !important;
}
/* Order and size: title, month, then travel period, then the optional subtitle. */
.kfse-subtitle {
	margin: 1px 0 0 !important;
	font-size: 11px !important;
	line-height: 1.3 !important;
	color: #cfe0f5 !important;
	text-align: center !important;
}
.kfse-period {
	margin: 0 !important;
	font-size: 13px !important;
	line-height: 1.3 !important;
	color: #e8eef7 !important;
	text-align: center !important;
}
.kfse-period strong {
	color: #ffd98a !important;
}

/* Scoot's own brand yellow, black text — set here rather than in JS since
   the root element already carries data-airline (rendered server-side in
   class-kfse-shortcode.php), and every affected property is a static color,
   not something that needs to react to data. White text (the default above)
   would be close to invisible on this bright a yellow. */
.kfse-app[data-airline="scoot"] .kfse-header {
	background: #FFE900 !important;
}
.kfse-app[data-airline="scoot"] .kfse-title,
.kfse-app[data-airline="scoot"] .kfse-title-month,
.kfse-app[data-airline="scoot"] .kfse-subtitle,
.kfse-app[data-airline="scoot"] .kfse-period {
	color: #000 !important;
}
.kfse-app[data-airline="scoot"] .kfse-period strong {
	color: var(--kfse-navy) !important;
}
/* Redefining the token (rather than just the header rules above) also
   reaches the cluster bubbles and any dual-role marker, both of which pull
   their gold from this variable instead of the JS-set inline style that the
   individual destination pins use. */
.kfse-app[data-airline="scoot"] {
	--kfse-gold: #FFE900;
	--kfse-selected: #000;
	--kfse-hub: #000;
}
/* White cluster-count text (readable on the SIA gold) would be close to
   invisible on this much brighter yellow — same reasoning as the header. */
.kfse-app[data-airline="scoot"] .kfse-cluster {
	color: #000 !important;
}

.kfse-controls {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 8px 16px !important;
	padding: 8px 16px !important;
	background: var(--kfse-bg);
}
/* With no region row below it (a single-region month), the toolbar closes
   with the line the region row would otherwise draw. */
.kfse-app:not(.kfse-has-regions) .kfse-controls {
	border-bottom: 1px solid var(--kfse-border);
}
/* Count on the left, Reset on the right, in a bar under the map and its
   legend (moved there from above the map in 1.9.4, to give the map the
   room). */
.kfse-controls-footer {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 10px;
	padding: 5px 16px 6px !important;
	background: var(--kfse-bg);
	border-bottom: 1px solid var(--kfse-border);
}
/* Region buttons (built by buildRegionButtons() in frontend.js), the last
   row above the map when present. Always one row: where they don't all fit
   (phones) it scrolls sideways, and frontend.js sets can-scroll-left/-right
   so the side with more buttons fades out as a hint. */
.kfse-regions {
	display: flex;
	flex-wrap: nowrap;
	align-items: center;
	gap: 6px;
	padding: 0 16px 8px;
	overflow-x: auto;
	scrollbar-width: none;
	-webkit-overflow-scrolling: touch;
	background: var(--kfse-bg);
	border-bottom: 1px solid var(--kfse-border);
}
.kfse-regions::-webkit-scrollbar {
	display: none;
}
.kfse-regions.can-scroll-right {
	-webkit-mask-image: linear-gradient(90deg, #000 calc(100% - 32px), transparent);
	mask-image: linear-gradient(90deg, #000 calc(100% - 32px), transparent);
}
.kfse-regions.can-scroll-left {
	-webkit-mask-image: linear-gradient(90deg, transparent, #000 32px);
	mask-image: linear-gradient(90deg, transparent, #000 32px);
}
.kfse-regions.can-scroll-left.can-scroll-right {
	-webkit-mask-image: linear-gradient(90deg, transparent, #000 32px, #000 calc(100% - 32px), transparent);
	mask-image: linear-gradient(90deg, transparent, #000 32px, #000 calc(100% - 32px), transparent);
}
.kfse-regions[hidden],
.kfse-region-btn[hidden] {
	display: none !important;
}
.kfse-region-btn {
	flex: none;
	margin: 0 !important;
	padding: 2px 10px !important;
	border: 1.5px solid var(--kfse-navy) !important;
	border-radius: 999px !important;
	background: #fff !important;
	color: var(--kfse-navy) !important;
	font-size: 0.72em !important;
	font-weight: 600 !important;
	line-height: 1.4 !important;
	white-space: nowrap;
	cursor: pointer;
	box-shadow: none !important;
}
.kfse-region-btn.is-active {
	background: var(--kfse-navy) !important;
	color: #fff !important;
}
@media (hover: hover) {
	.kfse-region-btn:not(.is-active):hover {
		background: #e8edf4 !important;
	}
}
.kfse-region-btn:focus-visible {
	outline: 2px solid #1a2433;
	outline-offset: 2px;
}
.kfse-filter-label {
	font-size: 0.8em;
	font-weight: 600;
	color: #55606f;
	text-transform: uppercase;
	letter-spacing: 0.03em;
	margin-right: 4px;
}
.kfse-filter-group {
	display: flex;
	align-items: center;
	gap: 8px;
	flex-wrap: wrap;
	font-size: 0.92em;
}
.kfse-filter-cabin {
	gap: 8px !important;
}
/* Each cabin's checkbox becomes a solid-color pill instead of a bare
   checkbox + text label, colored to match that cabin's dot in the detail
   panel below. The real checkbox stays in the DOM (for keyboard/screen
   reader use) but is visually hidden — the colored label itself is the
   clickable target.

   A checkmark plus a raised, filled look signals "on"; toggling off swaps
   to a plain outline in the same color rather than just fading the pill —
   a hover/press-able button reads as clickable far more clearly than a
   static badge that merely dims. */
.kfse-cabin-chip {
	position: relative;
	display: inline-flex;
	align-items: center;
	padding: 4px 12px;
	border-radius: 999px;
	border: 1.5px solid transparent;
	color: #fff !important;
	font-weight: 600;
	font-size: 0.85em;
	line-height: 1.4;
	cursor: pointer;
	box-shadow: 0 1px 2px rgba(0,0,0,0.15);
	transition: opacity 0.15s, background-color 0.15s, color 0.15s, border-color 0.15s, box-shadow 0.15s, transform 0.1s;
}
.kfse-cabin-chip::before {
	content: "\2713";
	margin-right: 5px;
	font-weight: 700;
}
.kfse-cabin-chip.is-off::before {
	content: "";
	margin-right: 0;
}
.kfse-cabin-chip[data-cabin="economy"] {
	background: #14532d;
}
.kfse-cabin-chip[data-cabin="premiumEconomy"] {
	background: #495b57;
}
.kfse-cabin-chip[data-cabin="business"] {
	background: #0b2545;
}
.kfse-cabin-chip.is-off {
	background: #fff;
	box-shadow: none;
}
.kfse-cabin-chip[data-cabin="economy"].is-off {
	border-color: #14532d;
	color: #14532d !important;
}
.kfse-cabin-chip[data-cabin="premiumEconomy"].is-off {
	border-color: #495b57;
	color: #495b57 !important;
}
.kfse-cabin-chip[data-cabin="business"].is-off {
	border-color: #0b2545;
	color: #0b2545 !important;
}
/* Hover/press feedback only where a real pointer is available, so tapping
   on mobile doesn't leave a stuck "hover" look after the finger lifts. */
@media (hover: hover) {
	.kfse-cabin-chip:hover {
		box-shadow: 0 2px 5px rgba(0,0,0,0.3);
		transform: translateY(-1px);
	}
	.kfse-cabin-chip.is-off:hover {
		box-shadow: 0 1px 3px rgba(0,0,0,0.15);
	}
}
.kfse-cabin-chip:active {
	transform: translateY(0);
}
.kfse-cabin-chip input {
	position: absolute;
	width: 1px;
	height: 1px;
	margin: -1px;
	padding: 0;
	overflow: hidden;
	clip: rect(0, 0, 0, 0);
	white-space: nowrap;
	border: 0;
}
.kfse-cabin-chip:focus-within {
	outline: 2px solid #1a2433;
	outline-offset: 2px;
}
/* "Max miles", slider and number box always on one line: the slider takes
   whatever room is left (on desktop, beside the cabin chips; on a phone, the
   full width under them). */
.kfse-filter-price {
	flex: 1 1 240px;
	flex-wrap: nowrap !important;
	min-width: 0;
}
.kfse-filter-price .kfse-filter-label {
	white-space: nowrap;
	margin-right: 0;
}
/* Themes very commonly style form controls broadly (e.g. "input[type=text],
   input[type=number] { width: 100% }" for their own forms), which otherwise
   stretches these. The slider's flex/width and the number box's fixed width
   are reasserted with !important against exactly that kind of theme rule. */
.kfse-price-range {
	flex: 1 1 auto !important;
	width: auto !important;
	min-width: 60px;
	max-width: 260px;
}
/* Desktop: cabin chips keep their natural width so the miles controls fit
   beside them on the same row (they wrap below if the widget is narrow). */
@media (min-width: 601px) {
	.kfse-filter-cabin {
		flex: none;
	}
}
.kfse-price-input {
	flex: none !important;
	width: 78px !important;
	font-size: 0.92em;
	padding: 3px 6px;
	border: 1px solid var(--kfse-border);
	border-radius: 4px;
	font-variant-numeric: tabular-nums;
}
/* A bare <button> (or one carrying the theme's own generic .button class)
   picks up whatever blocky, square-cornered default styling the host theme
   applies to every button on the page — the same recurring class of
   host-theme-CSS problem as elsewhere in this file. Fully owning the look
   here, !important-reinforced, keeps it a small pill button matching the
   cabin filter chips regardless of what the theme would otherwise do. */
.kfse-reset {
	flex: none;
	width: 100px;
	white-space: nowrap;
	text-align: center;
	padding: 3px 0 !important;
	border: 1.5px solid var(--kfse-navy) !important;
	border-radius: 999px !important;
	background: #fff !important;
	color: var(--kfse-navy) !important;
	font-size: 0.78em !important;
	font-weight: 600 !important;
	line-height: 1.4 !important;
	cursor: pointer;
	box-shadow: none !important;
	transition: background-color 0.15s, color 0.15s;
}
@media (hover: hover) {
	.kfse-reset:hover {
		background: var(--kfse-navy) !important;
		color: #fff !important;
	}
}

/* City / IATA-code label switch, on the map's top-right corner (a Leaflet
   control added by addLabelControl() in frontend.js; before that runs it
   sits under the map). Same theme-proofing as .kfse-reset: a host theme's
   generic button styling would otherwise turn these into blocky default
   buttons. */
.kfse-label-btns {
	display: inline-flex;
	flex: none;
	width: 92px;
}
.kfse-app .kfse-label-control {
	border-radius: 999px;
	background: #fff;
	box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3);
}
.kfse-label-btn {
	flex: 1 1 0;
	margin: 0 !important;
	padding: 3px 0 !important;
	text-align: center;
	border: 1.5px solid var(--kfse-navy) !important;
	background: #fff !important;
	color: var(--kfse-navy) !important;
	font-size: 11.5px !important;
	font-weight: 600 !important;
	line-height: 1.4 !important;
	cursor: pointer;
	box-shadow: none !important;
	border-radius: 0 !important;
	transition: background-color 0.15s, color 0.15s;
}
.kfse-label-btn:first-child {
	border-radius: 999px 0 0 999px !important;
	border-right-width: 0.75px !important;
}
.kfse-label-btn:last-child {
	border-radius: 0 999px 999px 0 !important;
	border-left-width: 0.75px !important;
}
.kfse-label-btn.is-active {
	background: var(--kfse-navy) !important;
	color: #fff !important;
}
.kfse-label-btn:focus-visible {
	outline: 2px solid #1a2433;
	outline-offset: 2px;
}
@media (max-width: 600px) {
	/* All three cabin chips on one centred line. As on desktop they need
	   ~393px and a phone has ~290-320px, so the "CABIN" label is dropped (the
	   ticked, colour-coded chips read as cabin filters on their own) and the
	   chips are tighter: 263px, which fits 360px-wide phones and up. */
	.kfse-filter-cabin > .kfse-filter-label {
		display: none;
	}
	.kfse-filter-cabin {
		flex: 1 1 100%;
		justify-content: center;
		gap: 3px !important;
	}
	.kfse-cabin-chip {
		padding: 4px 6px;
		font-size: 0.76em;
	}
	.kfse-cabin-chip::before {
		margin-right: 2px;
	}
}

/* Font-size/color need !important: this element used to be a <p>, where a
   host theme's bare "p" font-size rule beat an inherited value regardless of
   specificity (the same override class of bug documented at the top of this
   file — "Showing X of Y" rendered at the theme's own paragraph size on the
   real site). It's a div now (no <p> tags anywhere in the widget, so ad
   plugins that insert after "paragraph N" can't land inside it), but the
   rules stay as insurance. */
.kfse-result-count {
	margin: 0 !important;
	font-size: 0.75em !important;
	line-height: 1.3 !important;
	color: #55606f !important;
}

.kfse-dot {
	display: inline-block;
	width: 10px;
	height: 10px;
	border-radius: 50%;
	border: 2px solid #fff;
	box-shadow: 0 0 0 1px rgba(0,0,0,0.15);
}

.kfse-body {
	display: flex;
	align-items: flex-start;
}
/* Taller than the world's own 2:1 shape, to show more at once. The opening
   view fills this height and crops the far Pacific instead (see
   setOpeningView() in frontend.js). min-height is what sets the height on
   phones, where 17:10 of the width alone would be under 200px. */
.kfse-map {
	position: relative !important;
	flex: 1 1 auto;
	width: 100%;
	aspect-ratio: 17 / 10;
	min-height: 310px;
	background: #dce6ee;
}
/* Leaflet moves the map, its layers and every marker by rewriting their CSS
   transform on each drag step and zoom. Themes that give everything a
   transition — milelion.com's child theme has "body * { transition: 0.2s }"
   — make each of those updates ease in over 0.2s instead of landing at once,
   so the map trails the cursor and markers catch up at different moments:
   the jerky, rubbery movement. Nothing inside the map should transition
   unless Leaflet itself asks for it, so switch transitions off here and
   re-allow exactly Leaflet's and the cluster plugin's own animations below
   (their selectors are more specific, so they win over this one). */
.kfse-app .leaflet-container,
.kfse-app .leaflet-container * {
	transition: none !important;
}
/* leaflet.css keeps themes' "svg { max-width: 100% }" off its own overlay
   pane only; the basemap is drawn in a pane of its own (see
   addVectorBasemap() in frontend.js), which needs the same protection or a
   theme could shrink it to the map's width. */
.kfse-app .leaflet-pane > svg {
	max-width: none !important;
	max-height: none !important;
}
.kfse-app .leaflet-zoom-anim .leaflet-zoom-animated {
	transition: transform 0.25s cubic-bezier(0, 0, 0.25, 1) !important;
}
/* Snapshot of the old view, dissolved away after a "Zoom to" jump (see
   snapshotMap() in frontend.js). Above the map panes (400-700), below the
   +/- buttons (800+), never takes clicks. */
.kfse-app .leaflet-container .kfse-jump-fade {
	position: absolute;
	inset: 0;
	z-index: 750;
	overflow: hidden;
	pointer-events: none;
}
.kfse-app .leaflet-container .kfse-jump-fade * {
	pointer-events: none !important;
}
/* "Use Ctrl + scroll to zoom the map" - shown briefly when the mouse wheel
   scrolls the page over the map (see setupWheelGate() in frontend.js).
   Never blocks the map: pointer-events off, only its opacity changes. */
.kfse-app .leaflet-container .kfse-wheel-hint {
	position: absolute;
	inset: 0;
	z-index: 1001;
	display: flex;
	align-items: center;
	justify-content: center;
	padding: 0 16px;
	background: rgba(11, 37, 69, 0.55);
	color: #fff;
	font-size: 15px;
	font-weight: 600;
	line-height: 1.3;
	text-align: center;
	pointer-events: none;
	opacity: 0;
	transition: opacity 0.2s ease !important;
}
.kfse-app .leaflet-container .kfse-wheel-hint.is-visible {
	opacity: 1;
}
/* Phones/tablets (setupTouchGate() in frontend.js): one finger scrolls the
   page up and down, two-finger gestures go to the map. Narrows Leaflet's own
   "pan-x pan-y" (map dragging off): the page never scrolls sideways. */
.kfse-app .leaflet-container.kfse-touch-gate {
	touch-action: pan-y !important;
}
@media (prefers-reduced-motion: reduce) {
	.kfse-app .leaflet-container .kfse-wheel-hint {
		transition: none !important;
	}
}
.kfse-app .leaflet-zoom-anim .leaflet-tile,
.kfse-app .leaflet-pan-anim .leaflet-tile {
	transition: none !important;
}
.kfse-app .leaflet-fade-anim .leaflet-popup {
	transition: opacity 0.2s linear !important;
}
/* The cluster plugin's own split/merge animation (from MarkerCluster.css). */
.kfse-app .leaflet-cluster-anim .leaflet-marker-icon,
.kfse-app .leaflet-cluster-anim .leaflet-marker-shadow {
	transition: transform 0.3s ease-out, opacity 0.3s ease-in !important;
}
.kfse-app .leaflet-container .leaflet-cluster-spider-leg {
	transition: stroke-dashoffset 0.3s ease-out, stroke-opacity 0.3s ease-in !important;
}

/* Explains the two marker shapes, as its own normal-flow row between the map
   and the detail panel (a static placeholder in the markup, populated by
   initMap()) — deliberately NOT an overlay on the map. An overlay needs
   position:absolute to survive, and depending on that turned out fragile
   against the host theme's CSS on the real site; a plain block row has
   nothing to fight for. */
.kfse-legend {
	display: flex;
	flex-wrap: wrap;
	gap: 6px 16px;
	padding: 3px 16px;
	background: var(--kfse-bg);
	border-top: 1px solid var(--kfse-border);
	border-bottom: 1px solid var(--kfse-border);
	font-size: 0.78em;
	color: #55606f;
}
.kfse-legend-item {
	display: inline-flex;
	align-items: center;
	gap: 5px;
	white-space: nowrap;
}
.kfse-legend-dot {
	flex: none;
	display: inline-block;
	width: 8px;
	height: 8px;
	border-radius: 50%;
	border: 1.5px solid #fff;
	box-shadow: 0 0 0 1px rgba(0,0,0,0.2);
}
.kfse-legend-diamond {
	flex: none;
	display: inline-block;
	width: 7px;
	height: 7px;
	background: var(--kfse-navy);
	border: 1.5px solid #fff;
	box-shadow: 0 0 0 1px rgba(0,0,0,0.2);
	transform: rotate(45deg);
}
/* Legend swatch for the dual-role marker: same circle-plus-diamond-badge
   construction as .kfse-marker-dual below, just wrapped in a positioned span
   since .kfse-legend-dot itself isn't a positioning context. */
.kfse-legend-dot-dual {
	position: relative;
	background: var(--kfse-gold);
}
.kfse-legend-dot-dual::after {
	content: '';
	position: absolute;
	width: 5px;
	height: 5px;
	top: -3px;
	right: -3px;
	background: var(--kfse-navy);
	border: 1px solid #fff;
	box-shadow: 0 0 0 1px rgba(0,0,0,0.2);
	transform: rotate(45deg);
}

.kfse-marker {
	border-radius: 50%;
	border: 2px solid #fff;
	box-shadow: 0 1px 4px rgba(0,0,0,0.35);
	cursor: pointer;
}
/* A destination that's also this month's fifth-freedom connection endpoint
   (e.g. Frankfurt, when SIN-FRA and FRA-JFK both run) — the plain gold
   circle plus a small navy diamond badge at its corner, instead of a second
   overlapping marker (see renderConnections() in frontend.js for why that
   used to double up). The diamond badge deliberately reuses the same shape
   as a fifth-freedom route's own marker (.kfse-connector-dot below) rather
   than introducing a third shape, so "also fifth-freedom" reads at a glance
   from the shape alone, not just the color split a plain two-tone circle
   used to rely on. */
.kfse-marker-dual {
	background: var(--kfse-gold);
	position: relative;
	overflow: visible;
}
.kfse-marker-dual::after {
	content: '';
	position: absolute;
	/* Pixels, not %: a positioned child's % is measured inside the marker's 2px
	   white border (7px, not 11px), which shrank the badge to ~1px of navy. */
	width: 6px;
	height: 6px;
	top: -4px;
	right: -4px;
	background: var(--kfse-navy);
	border: 1px solid #fff;
	box-shadow: 0 0 0 1px rgba(0,0,0,0.2);
	transform: rotate(45deg);
}
/* The destination whose detail panel is open (set by setSelected() in
   frontend.js). !important beats the marker's inline airline colour. A
   selected dual-role marker swaps its colours - navy circle, gold diamond -
   so the badge doesn't disappear into the circle. */
.kfse-marker.is-selected {
	background: var(--kfse-selected) !important;
}
.kfse-marker-dual.is-selected::after {
	background: var(--kfse-gold);
}

.kfse-marker-wrap {
	position: relative;
	display: block;
	width: 11px;
	height: 11px;
}
/* Invisible 26px touch zone centred on each dot, so a thumb doesn't have to
   land on the 11px dot itself. Where two zones overlap, tapNearest() in
   frontend.js gives the tap to the dot closest to the finger. */
.kfse-marker-wrap::before {
	content: '';
	position: absolute;
	left: 50%;
	top: 50%;
	width: 26px;
	height: 26px;
	transform: translate(-50%, -50%);
	border-radius: 50%;
}
/* Singapore hub: white centre, dark ring - reads as "home", distinct from the
   filled destination dots and the selected (filled navy/black) one. */
.kfse-marker-wrap.kfse-hub-wrap {
	width: 13px;
	height: 13px;
}
.kfse-hub {
	display: block;
	width: 13px;
	height: 13px;
	border-radius: 50%;
	background: #fff;
	border: 3px solid var(--kfse-hub);
	box-shadow: 0 1px 4px rgba(0, 0, 0, 0.35);
}
/* Hidden until zoomed in enough to read (toggled via .kfse-show-labels on
   the map container — see initMap's zoomend handler) — at the whole-world
   starting view, city names this close together would just overlap into
   noise. The layered text-shadow fakes a white outline so the dark label
   stays legible over both the tan land and blue ocean of the illustration. */
.kfse-marker-label {
	display: none;
	position: absolute;
	left: 50%;
	top: 100%;
	transform: translateX(-50%);
	margin-top: 0;
	font-size: 9.5px;
	font-weight: 700;
	letter-spacing: -0.03em;
	color: #1a2433;
	white-space: nowrap;
	pointer-events: none;
	text-shadow:
		-1px -1px 0 #fff, 1px -1px 0 #fff,
		-1px 1px 0 #fff, 1px 1px 0 #fff,
		0 0 4px #fff;
}
.kfse-map.kfse-show-labels .kfse-marker-label {
	display: block;
}
/* Set by declutterLabels() in frontend.js after every zoom/filter change:
   when the default spot below the dot would overlap another label or dot
   (or leave the map), the label moves above, right or left of its dot
   instead. No class = below. The 3px side gap matches LABEL_GAP there. */
.kfse-marker-label.kfse-label-above {
	top: auto;
	bottom: 100%;
	margin-top: 0;
	margin-bottom: 0;
}
.kfse-marker-label.kfse-label-right,
.kfse-marker-label.kfse-label-left {
	top: 50%;
	transform: translateY(-50%);
}
.kfse-marker-label.kfse-label-right {
	left: 100%;
	margin-left: 3px;
}
.kfse-marker-label.kfse-label-left {
	left: auto;
	right: 100%;
	margin-right: 3px;
}

.kfse-cluster {
	background: var(--kfse-gold);
	color: #fff;
	border: 2px solid #fff;
	border-radius: 50%;
	box-shadow: 0 1px 5px rgba(0,0,0,0.4);
	text-align: center;
	font-weight: 700;
	font-size: 0.85em;
	cursor: pointer;
}

/* Midpoint click target for a direct (non-Singapore) route line — a diamond
   rather than a circle so it reads as "different kind of thing" from a
   destination pin at a glance. */
.kfse-connector-dot {
	display: block;
	width: 10px;
	height: 10px;
	background: var(--kfse-navy);
	border: 2px solid #fff;
	box-shadow: 0 1px 4px rgba(0,0,0,0.35);
	transform: rotate(45deg);
	cursor: pointer;
}

/* ======================================================================
   Destination detail — appears inline below the map when a pin is tapped,
   instead of a dimming modal overlay.
   ====================================================================== */
.kfse-detail {
	border-top: 1px solid var(--kfse-border);
	background: #fff;
}
.kfse-detail-content {
	position: relative;
	padding: 12px 40px 12px 16px;
}
.kfse-detail-close {
	position: absolute;
	top: 14px;
	right: 14px;
	background: none;
	border: none;
	font-size: 1.5em;
	line-height: 1;
	cursor: pointer;
	color: #7a8494;
}
.kfse-detail-close:hover {
	color: #1a2433;
}
.kfse-detail-title {
	margin: 0 0 2px;
	font-size: 0.92em;
}
.kfse-detail-subtitle {
	margin: 0 0 2px !important;
	color: #7a8494;
	font-size: 0.88em;
}
/* Destination panel header: "Bangkok (BKK)" then flag + country on the same
   line, saving a line; a name too long to share it ("London (Heathrow)
   (LHR)" on a phone) wraps the country onto its own line as before. The
   right padding keeps both clear of the close button. Margins forced: the
   theme's own h4 margins outrank .kfse-detail-title. */
.kfse-app .kfse-detail-head {
	display: flex;
	flex-wrap: wrap;
	align-items: baseline;
	column-gap: 10px;
	margin-bottom: 6px;
	padding-right: 28px;
}
.kfse-app .kfse-detail-head .kfse-detail-title,
.kfse-app .kfse-detail-head .kfse-detail-subtitle {
	margin: 0 !important;
}
.kfse-app .kfse-detail-head .kfse-detail-subtitle {
	white-space: nowrap;
}
/* Sizes forced: host themes often give every <img> height:auto, margins or
   a max-width that would stretch or shift a 16px flag. */
.kfse-app img.kfse-flag {
	display: inline-block !important;
	width: 16px !important;
	height: 12px !important;
	max-width: none !important;
	margin: 0 5px 0 0 !important;
	padding: 0 !important;
	border: 0 !important;
	border-radius: 2px;
	box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.1);
	vertical-align: -1px;
	object-fit: cover;
}
.kfse-detail-cabins {
	display: flex;
	flex-direction: column;
	gap: 6px;
	margin-bottom: 8px;
}
.kfse-detail-cabin-block {
	border-radius: 6px;
	background: var(--kfse-bg);
	overflow: hidden;
}
/* A "Not offered" cabin is muted through its text colour only (below) - not
   opacity, which also washed its dot out (Premium Economy's orange read as
   peach), and every dot should match its Cabin filter chip. */
.kfse-detail-cabin-row {
	display: flex;
	justify-content: space-between;
	align-items: center;
	padding: 6px 10px;
	font-size: 0.9em;
}
.kfse-detail-cabin-block.is-unavailable .kfse-detail-cabin-row {
	color: #a3abb7;
}
.kfse-detail-cabin-name {
	display: flex;
	align-items: center;
	gap: 6px;
	font-weight: 600;
}
.kfse-detail-directions {
	display: flex;
	flex-direction: column;
	gap: 1px;
	background: var(--kfse-border);
}
/* Desktop: outbound and return side by side (about a third shorter panel);
   the 1px gap over the border-coloured background draws the divider. A
   one-way route keeps both columns - "Not discounted this direction" fills
   the other - so every route lines up the same. Phones stay stacked. */
@media (min-width: 601px) {
	.kfse-detail-directions {
		display: grid;
		grid-template-columns: 1fr 1fr;
	}
}
.kfse-detail-direction {
	background: #fff;
	padding: 5px 10px 6px;
}
.kfse-detail-direction-label {
	display: block;
	font-size: 0.72em;
	font-weight: 600;
	text-transform: uppercase;
	letter-spacing: 0.03em;
	color: #7a8494;
	margin-bottom: 2px;
}
/* Two or more legs (different flight numbers for the same direction, each
   with its own blackout dates) stack with a bit more room between them than
   the tight 1px gap within a single leg's own flight-number/blackout pair,
   so they read as distinct flights rather than one run-on block. */
.kfse-detail-legs {
	display: flex;
	flex-direction: column;
	gap: 6px;
}
.kfse-detail-leg {
	display: flex;
	flex-direction: column;
	gap: 1px;
	font-size: 0.85em;
	color: #3a4453;
}
.kfse-detail-leg.is-unavailable {
	color: #b3bac4;
	font-style: italic;
}
.kfse-detail-leg-flights {
	font-family: Consolas, Monaco, monospace;
	font-size: 0.95em;
}
.kfse-airline-logo {
	display: inline-block !important;
	width: auto !important;
	height: 11px !important;
	max-width: none !important;
	vertical-align: -1px;
	margin-right: 4px;
	border-radius: 1px;
}
.kfse-detail-leg-blackout {
	color: #7a8494;
}
.kfse-detail-leg-blackout.no-blackouts {
	color: #1e7a45;
}
.kfse-detail-leg-blackout.has-blackouts {
	color: #b42318;
}
/* Availability bar under a leg's blackout dates (availabilityHtml() in
   frontend.js): one tick per day of the travel period, red = blacked out
   (the blackout text's own colour), green = open, then "19 of 30 days open".
   Desktop: bar and count on one line. Phones (below): the bar takes the
   full width so each day stays a readable block, with the count underneath. */
.kfse-app .kfse-avail {
	display: flex;
	align-items: center;
	gap: 8px;
	margin-top: 4px;
}
.kfse-app .kfse-avail-bar {
	display: flex;
	flex: 1;
	min-width: 0;
	max-width: 330px;
	gap: 2px;
}
.kfse-app .kfse-avail-day {
	display: block;
	flex: 1;
	height: 8px;
	border-radius: 2px;
	background: #3f9e6b;
}
.kfse-app .kfse-avail-day.is-out {
	background: #b42318;
}
.kfse-app .kfse-avail-count {
	white-space: nowrap;
	color: #3a4453;
}
.kfse-app .kfse-avail-count strong {
	color: #2f8a59;
	font-weight: 700;
}
@media (max-width: 600px) {
	.kfse-app .kfse-avail {
		display: block;
	}
	.kfse-app .kfse-avail-bar {
		max-width: none;
	}
	.kfse-app .kfse-avail-count {
		display: block;
		margin-top: 3px;
	}
}
/* One blackout range ("23-25", or a month's last range with its name,
   "27-28 November"): lines break between ranges, never inside one. */
.kfse-app .kfse-nowrap {
	white-space: nowrap !important;
}
/* The Spontaneous Escapes fare itself, e.g. "5,600 miles". */
.kfse-app .kfse-now {
	font-weight: 700 !important;
}
/* Regular (pre-discount) fare, struck through before the promo fare. */
.kfse-was {
	margin-right: 1px;
	color: #7a8494;
	font-weight: 400;
	text-decoration: line-through;
}
.kfse-detail-section {
	margin-bottom: 12px;
	font-size: 0.9em;
}
.kfse-detail-section-label {
	display: block;
	font-weight: 600;
	font-size: 0.8em;
	text-transform: uppercase;
	letter-spacing: 0.03em;
	color: #7a8494;
	margin-bottom: 3px;
}
/* Appended to a destination's own detail when it's also this month's
   fifth-freedom connection endpoint — a top rule sets it apart as a second,
   distinct route rather than reading as more of the destination above. */
.kfse-detail-connection {
	padding-top: 12px;
	border-top: 1px solid var(--kfse-border);
	/* Full size: its cabin blocks must match the destination's own above
	   (.kfse-detail-section's 0.9em is meant for short extras like Notes). */
	font-size: 1em;
}
/* ...keeping the "Direct + Fifth-Freedom" label the size it had (0.9 x 0.8). */
.kfse-detail-connection .kfse-detail-section-label {
	font-size: 0.72em;
}

/* Footnote on the left, "Map by milelion.com" + logo on the right. */
.kfse-footer {
	display: flex;
	align-items: center;
	gap: 12px;
	padding: 6px 16px 8px;
	border-top: 1px solid var(--kfse-border);
}
.kfse-footnote {
	flex: 1 1 auto;
	min-width: 0;
	font-size: 0.74em;
	color: #7a8494;
}
.kfse-brand {
	display: flex;
	align-items: center;
	gap: 7px;
	flex: none;
	margin-left: auto;
	font-size: 0.7em;
	line-height: 1.25;
	color: #7a8494;
	text-align: right;
}
.kfse-brand strong {
	color: #1a2433;
}
.kfse-brand-logo {
	display: block !important;
	width: 28px !important;
	height: 28px !important;
	max-width: none !important;
	margin: 0 !important;
	border-radius: 4px;
}
/* The footnote's own text is admin-entered content run through wpautop(),
   which wraps it in <p> tags — unlike every other bit of text in this
   widget, which sits directly on an element this stylesheet styles by
   class. A host theme commonly sets its own font-size directly on bare
   "p" (e.g. for post body copy), and that wins over the size inherited
   from .kfse-footnote above regardless of either rule's specificity,
   since an inherited value always loses to any rule that matches the
   element directly. Reasserting the size (as 1em, i.e. "whatever my
   parent .kfse-footnote resolved to") on the <p> itself closes that gap. */
.kfse-footnote p,
.kfse-footnote .kfse-footnote-para {
	margin: 0 0 6px !important;
	font-size: 1em !important;
	line-height: 1.4 !important;
	color: inherit !important;
}
.kfse-footnote p:first-child,
.kfse-footnote .kfse-footnote-para:first-child {
	margin-top: 0 !important;
}
.kfse-footnote p:last-child,
.kfse-footnote .kfse-footnote-para:last-child {
	margin-bottom: 0 !important;
}
