/**
 * KISS CoverFlow Slider — SCAFFOLD layer.
 *
 * Structure only: the geometry, stacking and overflow behavior the widget cannot
 * function without. Cosmetics (color, shadow, type treatment, transitions) live in
 * kiss-coverflow-theme.css, which can be switched off entirely under
 * Coverflow → Help & Settings when a project wants to own 100% of the look.
 *
 * Every value here is expressed as a CSS custom property so it can be re-pointed from
 * anywhere without !important. The three exceptions are `display: none` on elements the
 * script has hidden — a state rather than a style, and the one thing a site must not be
 * able to win; see the note by `.kiss-swiper[hidden]`. Cascade, weakest to strongest:
 *
 *   1. this file                        .kiss-coverflow                                  (0,1,0)
 *   2. kiss-coverflow-theme.css         .kiss-coverflow                                  (0,1,0)
 *   3. your site CSS                    .elementor-widget-kiss_coverflow .kiss-coverflow (0,2,0)
 *   4. Elementor Style-tab controls     .elementor-element-<id> .kiss-coverflow          (0,2,0, later)
 *
 * Site CSS therefore needs the two-class selector in (3) to beat the plugin. Leave a
 * Style-tab control blank and it emits nothing, so your CSS stays in charge.
 *
 * Swiper's own structural CSS loads separately (Elementor's copy or our vendored build).
 */

/* --------------------------------------------------------------------------
   Root + tokens
   -------------------------------------------------------------------------- */

.kiss-coverflow {
	/* Geometry */
	--kiss-slide-width: 58%;      /* of the slider's own width, so it tracks the container */
	--kiss-ratio: 16 / 9;
	--kiss-img-fit: cover;
	--kiss-img-position: center;
	--kiss-radius: 14px;
	--kiss-idle-opacity: 0.45;

	/* Tabs */
	--kiss-tab-gap-x: 40px;
	--kiss-tab-gap-y: 10px;
	--kiss-tabs-mb: 40px;
	--kiss-tab-justify: flex-start;
	--kiss-tab-rule-h: 2px;
	--kiss-tab-rule-offset: -6px;

	/* Caption */
	--kiss-cap-pad: 40px 24px 18px;
	--kiss-cap-align: left;

	/* Arrows */
	--kiss-arrow-size: 54px;
	--kiss-arrow-icon: 18px;
	--kiss-arrow-offset: 0px;

	/* Dots */
	--kiss-dot-size: 8px;
	--kiss-dot-gap: 16px;   /* 8px dot + 16px gap = 24px pitch, so tap targets don't overlap */
	--kiss-dot-hit: 24px;   /* invisible tappable area around each dot */
	--kiss-dots-mt: 26px;

	position: relative;
	width: 100%;

	/*
	 * Coverflow throws neighbors well outside the box. Clipping on X stops them creating
	 * page-level horizontal scroll (the classic mobile bug) while leaving Y visible so drop
	 * shadows still breathe. Set --kiss-overflow-x:visible to let slides bleed past the widget.
	 */
	overflow-x: var(--kiss-overflow-x, clip);
	overflow-y: visible;
}

/* `overflow: clip` is the only value that may be mixed with `visible`. Where it is
   unsupported, fall back to clipping both axes rather than spawning a scrollbar. */
@supports not (overflow: clip) {
	.kiss-coverflow {
		overflow: hidden;
	}
}

.kiss-coverflow *,
.kiss-coverflow *::before,
.kiss-coverflow *::after {
	box-sizing: border-box;
}

/* --------------------------------------------------------------------------
   Filter tabs
   -------------------------------------------------------------------------- */

.kiss-filters {
	display: flex;
	flex-wrap: wrap;
	justify-content: var(--kiss-tab-justify);
	gap: var(--kiss-tab-gap-y) var(--kiss-tab-gap-x);
	margin: 0 0 var(--kiss-tabs-mb);
	padding: 0;
}

/*
 * A filter tab is a <button>, so everything a kit aims at buttons lands on it — scoped to
 * (0,2,1) for the same reason as the frame and the load-more control. See the note on
 * `button.kiss-frame` below for the full reasoning; the list is exhaustive for the same
 * reason. The tab's own ::after is the active underline, so it is kept, not blanked.
 */
.kiss-coverflow button.kiss-filter {
	position: relative;
	display: inline-block;
	width: auto;
	min-width: 0;
	min-height: 0;
	height: auto;
	appearance: none;
	-webkit-appearance: none;
	margin: 0;
	padding: 4px 0;
	border: 0;
	border-radius: 0;
	background: none;
	box-shadow: none;
	font: inherit;
	line-height: inherit;
	text-decoration: none;
	color: inherit;
	cursor: pointer;
}

.kiss-coverflow button.kiss-filter::after {
	content: "";
	position: absolute;
	left: 0;
	right: 0;
	bottom: var(--kiss-tab-rule-offset);
	height: var(--kiss-tab-rule-h);
	background: var(--kiss-tab-rule-color, currentColor);
	transform: scaleX(var(--kiss-tab-rule-scale, 0));
	transform-origin: left center;
}

.kiss-coverflow button.kiss-filter[aria-pressed="true"] {
	--kiss-tab-rule-scale: 1;
}

/* --------------------------------------------------------------------------
   Stage + slider
   -------------------------------------------------------------------------- */

.kiss-stage {
	position: relative;
}

/* Beat Swiper's own `.swiper { overflow: hidden }` — the peeking neighbors are the point. */
.kiss-coverflow .kiss-swiper {
	position: relative;
	overflow: visible;
}

.kiss-coverflow .kiss-swiper .swiper-wrapper {
	align-items: center;
}

.kiss-coverflow .kiss-swiper .swiper-slide {
	width: var(--kiss-slide-width);
	height: auto;
	opacity: var(--kiss-idle-opacity);
}

.kiss-coverflow .kiss-swiper .swiper-slide-active {
	opacity: 1;
}

/*
 * Swiper's own rule sets `display: block`, which outranks the [hidden] UA style. The JS
 * hides the slider outright when a filter matches nothing, so this has to win.
 *
 * `!important` here and on the two rules like it below is deliberate, and is the only place
 * this stylesheet uses it. Everything else in this plugin is a *style* — a project is
 * entitled to overrule it, which is the whole point of the token model. These three are a
 * *state*: the script has determined that an element must not be on screen. No design intent
 * can legitimately want a `[hidden]` element visible, and losing the argument means a site
 * shows photos the paging deliberately withheld, or a control that has nothing left to do.
 *
 * Specificity alone cannot settle it. `.kiss-more[hidden]` is (0,2,0), and an ordinary site
 * rule like `.my-section .kiss-coverflow .kiss-more { display: flex }` is (0,3,0) and wins —
 * which is exactly how a "load more" control kept rendering after the script had hidden it.
 * Climbing the ladder just invites the next rule up.
 */
.kiss-coverflow .kiss-swiper[hidden] {
	display: none !important;
}

/* --------------------------------------------------------------------------
   Slide frame
   -------------------------------------------------------------------------- */

.kiss-frame {
	position: relative;
	display: block;
	margin: 0;
	aspect-ratio: var(--kiss-ratio);
	border-radius: var(--kiss-radius);
	overflow: hidden;
}

/*
 * The photo fills its frame — whether it sits in the frame directly or inside a <picture>.
 *
 * WebP delivery by <picture> tags (Imagify's, and most optimizers' on hosts without rewrite
 * rules, Kinsta included) wraps every image: <picture class="kiss-img"><source …><img …>.
 * A child selector then reaches nothing, the inline <picture> sizes to the <img>'s width and
 * height attributes, and the photo sat at 800 × 450 in the top-left of a far larger frame on
 * the live site, vertically centered by the <button> around it.
 *
 * Scoped to (0,2,1) and up so a kit's `.elementor img { height: auto; max-width: 100% }`
 * at (0,1,1) cannot win on load order, as it can against a bare `.kiss-frame > img`.
 */
.kiss-frame > img,
.kiss-coverflow .kiss-frame > img,
.kiss-coverflow .kiss-frame > picture > img {
	display: block;
	width: 100%;
	height: 100%;
	max-width: none;
	object-fit: var(--kiss-img-fit);
	object-position: var(--kiss-img-position);
}

.kiss-coverflow .kiss-frame > picture {
	display: block;
	width: 100%;
	height: 100%;
}

/* aspect-ratio fallback for very old engines: reserve height with a padding box.
   calc() can divide by the ratio expression, so this tracks --kiss-ratio too. */
@supports not (aspect-ratio: 16 / 9) {
	.kiss-frame {
		height: 0;
		padding-bottom: calc(100% / (var(--kiss-ratio)));
	}
	.kiss-frame > img,
	.kiss-frame > picture {
		position: absolute;
		inset: 0;
	}
}

/* Whole-slide link, when an item points somewhere. */
a.kiss-frame {
	text-decoration: none;
	color: inherit;
}

/* Whole-slide button, when "Tap to enlarge" is on and the item has no link of its own.
 *
 * This has to undo far more than the UA stylesheet. Themes and Elementor both ship
 * global button styling — `.elementor button { border-radius: 100px; padding: 14px 28px;
 * background: … }` and similar — and the moment the frame becomes a <button> it starts
 * matching those rules, turning a photo into a giant pill. A bare `button.kiss-frame`
 * is (0,1,1), the same weight as `.elementor button`, and site CSS loads later, so it
 * loses the tie. Scoping to the widget makes it (0,2,1) and wins outright.
 *
 * Every property below is one a real global button rule sets. Keep the list complete:
 * anything missing here leaks into the frame.
 */
.kiss-coverflow button.kiss-frame {
	display: block;
	position: relative;
	width: 100%;
	min-width: 0;
	min-height: 0;
	height: auto;
	margin: 0;
	padding: 0;
	border: 0;
	border-radius: var(--kiss-radius);
	appearance: none;
	-webkit-appearance: none;
	background: none;
	box-shadow: none;
	overflow: hidden;
	color: inherit;
	font: inherit;
	letter-spacing: inherit;
	line-height: inherit;
	text-align: inherit;
	text-transform: none;
	/* Kits commonly keep button labels on one line; a caption inside the frame then runs off
	   the photo's edge instead of wrapping. */
	white-space: normal;
	cursor: zoom-in;
}

/*
 * The property list above cannot reach a generated box. Themes routinely hang a hover wash
 * or a sheen on `button::after` / `button::before`, and once the frame is a <button> that
 * pseudo-element is painted full-bleed over the photo — which is reported as "there is an
 * overlay on the images" and looks nothing like a button style gone astray. It also survives
 * every reset above, because a pseudo-element is a child box rather than a property.
 *
 * `.kiss-coverflow button.kiss-frame::after` is (0,2,2) against a theme's `.elementor
 * button::after` at (0,1,2), so this wins outright regardless of load order. Nothing in this
 * plugin puts content on the frame's own pseudo-elements, so there is nothing here to lose.
 */
.kiss-coverflow button.kiss-frame::before,
.kiss-coverflow button.kiss-frame::after {
	content: none;
	display: none;
}

/* --------------------------------------------------------------------------
   Caption
   -------------------------------------------------------------------------- */

.kiss-cap {
	position: absolute;
	left: 0;
	right: 0;
	bottom: 0;
	margin: 0;
	padding: var(--kiss-cap-pad);
	text-align: var(--kiss-cap-align);
}

/*
 * Captions wrap, named on the caption itself and not only on the frame. When "Tap to enlarge"
 * is on, the caption sits inside a <button>, and a kit's `button { white-space: nowrap }` is
 * inherited by it — inheritance loses to any rule matching the element directly, so this
 * settles it at (0,2,0) whatever the frame's rule. Seen on a two-column phone grid and a
 * tablet: "Private Docks in Ocean Ridge, Aeria" cut off mid-word at the tile's edge.
 */
.kiss-coverflow .kiss-cap,
.kiss-coverflow .kiss-cap .kiss-cap-title,
.kiss-coverflow .kiss-cap .kiss-cap-sub {
	white-space: normal;
	overflow-wrap: break-word;
}

.kiss-cap-title,
.kiss-cap-sub {
	display: block;
}

/* Reveal modes. `always` is the bare default; the other two are opt-in via data attribute. */
.kiss-coverflow[data-cap-reveal="active"] .kiss-cap,
.kiss-coverflow[data-cap-reveal="hover"] .kiss-cap {
	opacity: 0;
}

.kiss-coverflow[data-cap-reveal="active"] .swiper-slide-active .kiss-cap {
	opacity: 1;
}

/*
 * "On hover / focus" means a pointer that can hover. A touchscreen cannot, and it fakes one:
 * a tap leaves the tile in :hover, and closing the photo viewer hands focus back to the tapped
 * tile, so one caption at a time got stuck on — over two-column phone tiles too narrow to hold
 * it. On a touchscreen this mode therefore shows nothing, the honest reading of "on hover".
 * Keyboard focus still reveals everywhere, through :focus-visible, which a tap or a focus
 * returned after one does not set.
 */
@media (hover: hover) {
	.kiss-coverflow[data-cap-reveal="hover"] .swiper-slide:hover .kiss-cap {
		opacity: 1;
	}
}

.kiss-coverflow[data-cap-reveal="hover"] .swiper-slide:has(:focus-visible) .kiss-cap {
	opacity: 1;
}

/* Hidden takes the caption out entirely rather than just making it transparent, so its
   scrim stops darkening the photo and screen readers aren't left reading invisible text. */
.kiss-coverflow[data-cap-reveal="none"] .kiss-cap {
	display: none;
}

/* --------------------------------------------------------------------------
   Navigation arrows
   -------------------------------------------------------------------------- */

.kiss-stage .kiss-nav {
	position: absolute;
	top: 50%;
	z-index: 2;
	width: var(--kiss-arrow-size);
	height: var(--kiss-arrow-size);
	margin: 0;
	transform: translateY(-50%);
	border-radius: 50%;
	cursor: pointer;
}

.kiss-stage .kiss-nav--prev {
	left: var(--kiss-arrow-offset);
	right: auto;
}

.kiss-stage .kiss-nav--next {
	right: var(--kiss-arrow-offset);
	left: auto;
}

.kiss-stage .kiss-nav::after {
	font-size: var(--kiss-arrow-icon);
}

/* Swiper locks arrows when there is nothing to navigate to. */
.kiss-stage .kiss-nav.swiper-button-lock {
	display: none;
}

/* --------------------------------------------------------------------------
   Pagination dots
   -------------------------------------------------------------------------- */

.kiss-coverflow .kiss-dots {
	position: static;
	width: auto;
	margin-top: var(--kiss-dots-mt);
	display: flex;
	flex-wrap: wrap;
	justify-content: center;
	gap: var(--kiss-dot-gap);
}

/*
 * The visible dot stays small; the tappable area does not.
 *
 * An 8px bullet is far under the 24x24 CSS pixels a touch target needs, and Lighthouse
 * flags it. An invisible pseudo-element carries the hit area instead, so the dot looks
 * exactly the same and a finger still gets something to land on. --kiss-dot-gap defaults
 * to 16px for the same audit: overlapping targets fail it too, and on an 8px dot the 24px
 * zones only stop overlapping once the pitch reaches 24px (8 + 16).
 */
.kiss-coverflow .kiss-dots .swiper-pagination-bullet {
	position: relative;
	width: var(--kiss-dot-size);
	height: var(--kiss-dot-size);
	margin: 0;
}

.kiss-coverflow .kiss-dots .swiper-pagination-bullet::after {
	content: "";
	position: absolute;
	top: 50%;
	left: 50%;
	width: var(--kiss-dot-hit, 24px);
	height: var(--kiss-dot-hit, 24px);
	transform: translate(-50%, -50%);
}

.kiss-coverflow .kiss-dots.swiper-pagination-lock {
	display: none;
}

/* --------------------------------------------------------------------------
   Stacked layout

   Set Layout to "Stacked" on any device and the JS skips Swiper entirely for that
   band — no instance, no clones, no transforms — leaving the slides as plain
   markup for CSS to lay out as one full-width column. Coverflow spends horizontal
   room on the peeking neighbors, which is the room a phone hasn't got; stacking
   spends vertical room instead, which a phone has plenty of.
   -------------------------------------------------------------------------- */

.kiss-coverflow--stack .kiss-swiper {
	overflow: visible;
	height: auto;
}

.kiss-coverflow--stack .kiss-swiper .swiper-wrapper {
	display: grid;
	grid-template-columns: 1fr;
	align-items: stretch;
	gap: var(--kiss-stack-gap, 16px);
	height: auto;
	/* Swiper never ran, so no inline transform was ever written here. */
	transform: none;
}

.kiss-coverflow--stack .kiss-swiper .swiper-slide {
	width: 100%;
	/* Nothing is "off-center" in a stack, so the neighbor dimming must not apply. */
	opacity: 1;
}

/* Arrows and dots are Swiper's, and Swiper isn't running in this mode. */
.kiss-coverflow--stack .kiss-nav,
.kiss-coverflow--stack .kiss-dots {
	display: none;
}

/* Note there is deliberately no rule forcing captions visible here. Stacked has no
   active slide, so "center slide only" shows nothing — which is the literal reading of
   the setting, and the right default: forcing them on instead put a dark scrim over the
   bottom 45% of every photo at once, and the whole gallery read as dimmed. Choose Always
   or Hidden per device on the Show caption control. */

/* --------------------------------------------------------------------------
   Grid layout

   Stacked is really a one-column grid, and this is the general case: same
   "don't initialize Swiper for this band" mechanism, laid out in however many
   columns the project asks for. The caption note above applies here too —
   every photo is on screen at once, so "center slide only" shows nothing.
   -------------------------------------------------------------------------- */

.kiss-coverflow--grid .kiss-swiper {
	overflow: visible;
	height: auto;
}

.kiss-coverflow--grid .kiss-swiper .swiper-wrapper {
	display: grid;
	align-items: stretch;
	/* Two declarations rather than the `gap` shorthand. A custom property is not validated
	   where it is defined, only where it is substituted — so one malformed value (a number
	   that lost its unit, say) makes the whole shorthand invalid at computed-value time and
	   `gap` reverts to `normal`, i.e. zero on BOTH axes, which reads as the spacing setting
	   being ignored entirely. Split, a bad value can only cost you its own axis. */
	column-gap: var(--kiss-grid-gap-x, var(--kiss-grid-gap, 16px));
	row-gap: var(--kiss-grid-gap-y, var(--kiss-grid-gap, 16px));
	height: auto;
	/* Swiper never ran, so no inline transform was ever written here. */
	transform: none;
}

/*
 * Two column strategies, switched by an attribute rather than a class because the
 * choice is per-placement while the numbers are per-device: `--kiss-grid-cols` and
 * `--kiss-grid-min` are responsive Elementor controls, so Elementor writes the media
 * queries and columns need no JavaScript at all.
 *
 * minmax(0, 1fr) rather than 1fr: a grid track's default minimum is auto, which refuses
 * to shrink below its content and lets a wide photo push the column past its share.
 */
.kiss-coverflow--grid[data-kiss-cols="fixed"] .kiss-swiper .swiper-wrapper {
	grid-template-columns: repeat(var(--kiss-grid-cols, 3), minmax(0, 1fr));
}

.kiss-coverflow--grid[data-kiss-cols="auto"] .kiss-swiper .swiper-wrapper {
	grid-template-columns: repeat(auto-fill, minmax(var(--kiss-grid-min, 260px), 1fr));
}

.kiss-coverflow--grid .kiss-swiper .swiper-slide {
	width: 100%;
	/* Nothing is "off-center" in a grid, so the neighbor dimming must not apply. */
	opacity: 1;
}

/* Arrows and dots are Swiper's, and Swiper isn't running in this mode either. */
.kiss-coverflow--grid .kiss-nav,
.kiss-coverflow--grid .kiss-dots {
	display: none;
}

/* --------------------------------------------------------------------------
   Masonry layout

   A grid whose tiles keep each photo's own shape, so a portrait is shown whole
   instead of cropped to the gallery's ratio. The root carries --grid as well, so
   columns, gaps, paging and everything else above apply unchanged.

   Packing keeps the hand-set order reading left to right: each photo goes into
   whichever column is shortest at that point, which is exactly what CSS grid's
   own auto-placement does once every tile spans as many 1px rows as it is tall.
   The script works those spans out from the photo's proportions (data-ratio) and
   the column width — before the first paint, from the snippet after the widget,
   and again on any resize — and only then adds .kiss-masonry-packed. Until it
   has, this is an ordinary grid of uncropped tiles: if the script never runs,
   nothing overlaps.

   CSS multi-column would need no script at all, but it fills each column top to
   bottom, so items 1-2-3 run down the left edge instead of across the top.
   -------------------------------------------------------------------------- */

.kiss-coverflow--masonry .kiss-frame {
	aspect-ratio: var(--kiss-item-ratio, var(--kiss-ratio));
}

.kiss-coverflow--masonry .kiss-swiper .swiper-wrapper {
	align-items: start;
}

.kiss-coverflow--masonry.kiss-masonry-packed .kiss-swiper .swiper-wrapper {
	grid-auto-rows: 1px;
	/* The vertical gap travels with each tile instead (below): between 1px tracks a
	   row-gap would be added hundreds of times over. */
	row-gap: 0;
}

.kiss-coverflow--masonry.kiss-masonry-packed .kiss-swiper .swiper-slide {
	/* Read back by the script, which counts it into each tile's span. */
	padding-bottom: var(--kiss-grid-gap-y, var(--kiss-grid-gap, 16px));
}

/* --------------------------------------------------------------------------
   Paging ("load more")

   Every item is in the HTML and only hidden from view, so nothing leaves the
   markup and the gallery stays crawlable however many pages deep it goes.
   Specificity has to clear Swiper's own `.swiper-slide { display: block }`.
   -------------------------------------------------------------------------- */

.kiss-coverflow .kiss-swiper .swiper-slide.kiss-paged-out {
	display: none !important;   /* state, not style — see the note on .kiss-swiper[hidden] */
}

.kiss-more {
	display: flex;
	justify-content: var(--kiss-more-justify, center);
	margin-top: var(--kiss-more-mt, 32px);
	/* Keep MORE_FADE_MS in kiss-coverflow.js in step with this. */
	transition: opacity var(--kiss-more-fade, 0.3s) ease;
}

/* The last page has landed. `hidden` is still the end state — out of the layout and out of
   the accessibility tree — but `display: none` cannot be transitioned, so the script fades
   through this class first and sets `hidden` afterward. pointer-events go immediately, so
   a second click during the fade cannot land on a control that is on its way out. */
.kiss-more--out {
	opacity: 0;
	pointer-events: none;
}

.kiss-more[hidden] {
	display: none !important;   /* state, not style — see the note on .kiss-swiper[hidden] */
}

/*
 * Scoped `.kiss-coverflow button.kiss-more-btn` (0,2,1) for exactly the reason the frame
 * reset is: an Elementor kit styles every button on the site through `.elementor-kit-N
 * button`, which is (0,1,1) and beats a bare `.kiss-more-btn` at (0,1,0) — so the control
 * rendered as the kit's pill (70px radius, uppercase, accent background) no matter what
 * this file said, and the "text link" variant was a yellow pill too.
 *
 * Same exhaustiveness rule as the frame: every property a global button rule is likely to
 * set has to be named here, because anything left out leaks in. Pseudo-elements as well —
 * kits hang hover washes on `button::after`, and that is a generated box no property list
 * can reach.
 */
.kiss-coverflow button.kiss-more-btn {
	display: inline-block;
	width: auto;
	min-width: 0;
	min-height: 0;
	height: auto;
	appearance: none;
	-webkit-appearance: none;
	margin: 0;
	padding: var(--kiss-more-pad, 14px 28px);
	border: var(--kiss-more-border, 0);
	border-radius: var(--kiss-more-radius, 999px);
	background: none;
	box-shadow: none;
	font: inherit;
	letter-spacing: inherit;
	line-height: inherit;
	text-align: center;
	text-transform: none;
	text-decoration: none;
	color: inherit;
	cursor: pointer;
}

.kiss-coverflow button.kiss-more-btn::before,
.kiss-coverflow button.kiss-more-btn::after {
	content: none;
	display: none;
}

/*
 * The "link" variant is a visual choice, not a semantic one: this reveals content
 * already on the page rather than navigating, so it stays a <button> either way.
 * Styling it as a link is what changes, never the element.
 *
 * An <a> would be the wrong element here, not merely a different one. With no href it is
 * not focusable and carries no role; with href="#" it jumps the page to the top and is
 * announced as a link, which is a promise of navigation this control does not keep.
 *
 * Equal weight to the rule above (0,2,1), so it has to come after it.
 */
.kiss-coverflow button.kiss-more-btn--link {
	padding: var(--kiss-more-link-pad, 4px 0);
	border: 0;
	border-radius: 0;
	background: none;
}

.kiss-coverflow button.kiss-more-btn:focus-visible {
	outline: 2px solid var(--kiss-focus, currentColor);
	outline-offset: 3px;
}

/* --------------------------------------------------------------------------
   Lightbox ("Tap to enlarge")

   The overlay is appended to <body>, so it sits outside .kiss-coverflow and cannot
   inherit the widget's custom properties. Its tokens are therefore read with inline
   fallbacks — set them on `.kiss-lightbox` (or :root) rather than on the widget.
   -------------------------------------------------------------------------- */

.kiss-lightbox {
	position: fixed;
	inset: 0;
	z-index: var(--kiss-lb-z, 99999);
	display: flex;
	align-items: center;
	justify-content: center;
	background: var(--kiss-lb-bg, rgba(0, 0, 0, 0.92));
}

/* The overlay is outside .kiss-coverflow, so it misses that subtree's reset — without
   this, the slide padding lands outside Swiper's inline width and the photo sits
   off-center and clipped. */
.kiss-lightbox,
.kiss-lightbox *,
.kiss-lightbox *::before,
.kiss-lightbox *::after {
	box-sizing: border-box;
}

.kiss-lightbox .kiss-lb-swiper {
	width: 100%;
	height: 100%;
	overflow: hidden;
}

/* Horizontal padding defaults to 0 for a reason. A landscape photo on a portrait phone
   is capped by the screen's width, so every pixel of side padding comes straight out of
   the photo — with 24px a side it rendered *smaller* than the same photo on the page.
   Height is what constrains a landscape photo on a wide screen, so desktop loses nothing. */
.kiss-lb-slide {
	display: flex;
	align-items: center;
	justify-content: center;
	padding: var(--kiss-lb-pad-y, var(--kiss-lb-pad, 24px)) var(--kiss-lb-pad-x, 0px);
	/* A zoomed photo must not spill into the neighboring slides. */
	overflow: hidden;
}

.kiss-lb-figure {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: var(--kiss-lb-cap-gap, 12px);
	/* width + min-width:0 together stop the figure refusing to shrink below the photo's
	   intrinsic width (a flex item's default min-width:auto), which would overflow the
	   slide on any photo wider than the viewport. */
	width: 100%;
	min-width: 0;
	max-width: 100%;
	max-height: 100%;
	margin: 0;
}

.kiss-lb-img {
	display: block;
	width: auto;
	height: auto;
	max-width: 100%;
	/* Leave room for the caption and the counter rather than filling the viewport. */
	max-height: var(--kiss-lb-img-max-h, 78vh);
	object-fit: contain;
	transform-origin: center center;
	/* The JS owns every gesture on the photo — pinch, pan and double-tap — so the browser
	   must not also try to scroll or zoom the page underneath it. */
	touch-action: none;
	cursor: zoom-in;
}

.kiss-lb-slide.is-zoomed .kiss-lb-img {
	cursor: grab;
}

.kiss-lb-cap {
	max-width: var(--kiss-lb-cap-width, 46em);
	text-align: center;
}

/* Also a <button>, and it lives on <body> rather than inside the widget — so it is scoped
   to the overlay instead, which gets it to (0,2,1) all the same. Its ::before/::after draw
   the X, so unlike the frame they are load-bearing and stay. */
.kiss-lightbox button.kiss-lb-close {
	position: absolute;
	top: var(--kiss-lb-close-offset, 14px);
	right: var(--kiss-lb-close-offset, 14px);
	z-index: 3;
	width: var(--kiss-lb-close-size, 44px);
	height: var(--kiss-lb-close-size, 44px);
	padding: 0;
	border: 0;
	border-radius: 50%;
	appearance: none;
	-webkit-appearance: none;
	cursor: pointer;
}

/* Drawn rather than typed, so the glyph never depends on a font being present. */
.kiss-lightbox button.kiss-lb-close::before,
.kiss-lightbox button.kiss-lb-close::after {
	content: "";
	position: absolute;
	top: 50%;
	left: 50%;
	width: 40%;
	height: 2px;
	background: currentColor;
}

.kiss-lightbox button.kiss-lb-close::before {
	transform: translate(-50%, -50%) rotate(45deg);
}

.kiss-lightbox button.kiss-lb-close::after {
	transform: translate(-50%, -50%) rotate(-45deg);
}

.kiss-lb-counter {
	position: absolute;
	left: 0;
	right: 0;
	bottom: var(--kiss-lb-counter-offset, 14px);
	margin: 0;
	text-align: center;
}

/* Nobody discovers a gesture they weren't told about — so say it once, then get out of
   the way for good as soon as the viewer has actually been zoomed. */
.kiss-lb-hint {
	position: absolute;
	left: 0;
	right: 0;
	bottom: calc(var(--kiss-lb-counter-offset, 14px) + var(--kiss-lb-hint-offset, 22px));
	margin: 0;
	text-align: center;
	pointer-events: none;
}

.kiss-lightbox.is-zoom-used .kiss-lb-hint {
	opacity: 0;
}

/*
 * Every piece of text in the viewer sets its own color. It must NOT inherit from
 * `.kiss-lightbox`, because inheritance is the weakest thing in the cascade: any rule that
 * matches an element directly beats a color it would otherwise inherit, at *any*
 * specificity. A theme's `figcaption { color: #333 }` or a kit's `p { color: … }` is enough,
 * and the caption, the zoom hint and the counter all rendered dark gray on a near-black
 * overlay. The close button and the arrows never had the bug precisely because they always
 * set color directly — that inconsistency was the tell.
 *
 * Scoped to (0,2,0)/(0,3,0) so a kit's `.elementor-kit-N p` at (0,1,1) loses. This lives in
 * the scaffold rather than the theme layer for the same reason the overlay's background
 * does: unreadable text in a full-screen viewer is a functional failure, not a cosmetic
 * one, so it has to survive the theme layer being switched off.
 */
.kiss-lightbox .kiss-lb-cap,
.kiss-lightbox .kiss-lb-cap .kiss-cap-title,
.kiss-lightbox .kiss-lb-cap .kiss-cap-sub,
.kiss-lightbox .kiss-lb-counter,
.kiss-lightbox .kiss-lb-hint {
	color: var(--kiss-lb-fg, #ffffff);
}

/* Keyboard focus inside the viewer must stay visible against the dark backdrop. */
.kiss-lightbox button.kiss-lb-close:focus-visible,
.kiss-lb-nav:focus-visible {
	outline: 2px solid var(--kiss-lb-focus, #ffffff);
	outline-offset: 3px;
}

/* --------------------------------------------------------------------------
   Utility / states
   -------------------------------------------------------------------------- */

.kiss-noresults {
	margin: 0;
	padding: 32px 0;
	text-align: center;
}

.kiss-noresults[hidden] {
	display: none;
}

/* Screen-reader-only live region announcing filter results. */
.kiss-sr {
	position: absolute;
	width: 1px;
	height: 1px;
	margin: -1px;
	padding: 0;
	overflow: hidden;
	clip: rect(0 0 0 0);
	clip-path: inset(50%);
	white-space: nowrap;
	border: 0;
}

/* Visible only inside the Elementor editor, when there is nothing to show yet. */
.kiss-empty {
	padding: 40px;
	border: 1px dashed currentColor;
	border-radius: 10px;
	opacity: 0.7;
	text-align: center;
	font-size: 14px;
}

/* Keyboard focus must always be visible, whatever the theme layer does. */
.kiss-coverflow button.kiss-filter:focus-visible,
.kiss-coverflow button.kiss-frame:focus-visible,
.kiss-stage .kiss-nav:focus-visible,
.kiss-coverflow .kiss-dots .swiper-pagination-bullet:focus-visible,
a.kiss-frame:focus-visible {
	outline: 2px solid var(--kiss-focus, currentColor);
	outline-offset: 3px;
}
