/**
 * GEEKS Loop Carousel.
 *
 * Two jobs only. One: the custom properties the controls write into, with the
 * defaults a carousel needs before an author has touched anything. Two: the
 * slides' size BEFORE Swiper initialises — until then they are plain flex
 * children with no width, and each one would take the whole row for a frame,
 * so the carousel would paint one tall column and then snap into place. The
 * arithmetic below is the same one Swiper applies afterwards, so nothing moves
 * when it takes over.
 *
 * Nothing here styles arrows or dots: this widget has none.
 */

.elementor-widget-geeks-loop-carousel {
	--swiper-slides-gap: 10px;
	height: fit-content;
}

/* A peek of the next slide used to be padding on the frame, which took its
   width out of every slide. It is Swiper's own `slidesOffsetBefore` and
   `slidesOffsetAfter` now — part of the carousel's arithmetic rather than a
   box around it — so there is nothing left to style here. */

/* Before Swiper: lay the slides out as the carousel will, and clip the rest. */
.elementor-widget-geeks-loop-carousel .swiper:not( .swiper-initialized ) > .swiper-wrapper {
	gap: var( --swiper-slides-gap );
	overflow: hidden;
}

.elementor-widget-geeks-loop-carousel .swiper:not( .swiper-initialized ) > .swiper-wrapper > .swiper-slide {
	--number-of-gaps: max( calc( var( --swiper-slides-to-display ) - 1 ), 0 );
	--gaps-width-total: calc( var( --number-of-gaps ) * var( --swiper-slides-gap ) );

	max-width: calc( ( 100% - var( --gaps-width-total ) ) / var( --swiper-slides-to-display, 1 ) );
}

/* HELD BACK UNTIL BUILT — when the frame has to be measured, or when the
   author asked for the fade. The slides arrive at their natural
   size and are arranged a moment later, so a vertical carousel would show one
   wrong frame and jump. It is hidden rather than shown wrong, and revealed the
   moment the handler has measured it. If the script never runs, the rule below
   still lets it through after a moment rather than leaving a hole in the page. */
.elementor-widget-geeks-loop-carousel.geeks-lc-waiting .elementor-loop-container {
	opacity: 0;
	animation: geeks-lc-rescue 0s linear 2s forwards;
}

.elementor-widget-geeks-loop-carousel.geeks-lc-ready .elementor-loop-container {
	opacity: 1;
	animation: none;
}

/* The fade is the author's choice; without it the carousel simply appears the
   moment it is ready, which is what a held-back grid wants — it was hidden to
   avoid a wrong frame, not to make an entrance. */
.elementor-widget-geeks-loop-carousel.geeks-lc-fade.geeks-lc-ready .elementor-loop-container {
	transition: opacity var( --geeks-lc-fade, 300ms ) ease;
}

@keyframes geeks-lc-rescue {
	to {
		opacity: 1;
	}
}

/* A VERTICAL CAROUSEL THAT IS STILL SEVERAL ACROSS.
   Swiper sizes slides on the axis it travels along — the height, here — and
   leaves the other one to CSS. Horizontally that is fine: the slides are laid
   in a row and the height follows the content. Vertically nothing divides the
   width, so each slide takes the whole row and the reader sees one post at a
   time. So the width is divided here, by the same arithmetic Swiper uses on
   its own axis: the gaps come out of the row first, and what is left is shared.
   The two numbers are written by the handler, because only it knows how many
   are across at this breakpoint. */
.elementor-widget-geeks-loop-carousel .swiper-vertical.swiper-grid > .swiper-wrapper {
	flex-direction: row;
	flex-wrap: wrap;
	align-content: flex-start;
	column-gap: var( --geeks-lc-col-gap, var( --swiper-slides-gap, 0px ) );
}

/* The height the handler measured, held against Swiper's own arithmetic.
   Swiper sizes a grid's cells as a percentage of the wrapper, and in a vertical
   carousel laid out this way the wrapper is every row tall, so its idea of
   "half" is five frames. The class is only ever on while the two disagree, and
   `!important` is not decoration — the number it overrides is an inline style. */
.elementor-widget-geeks-loop-carousel.geeks-lc-pin-cells .swiper-vertical.swiper-grid > .swiper-wrapper > .swiper-slide {
	height: var( --geeks-lc-cell-h ) !important;
}

/* The gap belongs to the ROW, not to each slide: a margin on every slide is
   also on the last one, and the row then overflows by exactly one gap — which
   drops the last slide onto the next line and turns three across into two.
   (The obvious fix, :nth-child( var( --across ) n ), is not something CSS
   accepts: nth-child takes a number, not a custom property.) */
.elementor-widget-geeks-loop-carousel .swiper-vertical.swiper-grid > .swiper-wrapper > .swiper-slide {
	--geeks-lc-gaps: calc( ( var( --geeks-lc-across, 1 ) - 1 ) * var( --geeks-lc-col-gap, var( --swiper-slides-gap, 0px ) ) );

	width: calc( ( 100% - var( --geeks-lc-gaps ) ) / var( --geeks-lc-across, 1 ) );

	/* Swiper writes its cross-axis gap as an inline margin, and in a vertical
	   carousel that lands on the LEFT of every slide after the first row. Added
	   to the row's own gap it overflows the row by exactly that much, which
	   drops the last slide onto the next line. An inline style can only be
	   answered this way; the gap it was providing is already on the wrapper. */
	margin-left: 0 !important;
	margin-right: 0 !important;

	/* AND THE ORDER IT WRITES WITH THEM.
	   Swiper arranges a grid by giving each slide a flex `order`, counted in its
	   own axes — where rows go down the screen. Here they go across it, so those
	   numbers rearrange the slides into something that is no longer the order
	   they were published in: the second row showed the fifth and sixth posts.
	   Ignored, the slides fall back to the order they arrived in, which is the
	   order the query returned. This is also why Grid fill has nothing to say in
	   a vertical carousel — the wrap is ours, not Swiper's. */
	order: 0 !important;
}

/* ROWS IN A SIDEWAYS CAROUSEL.
   Sideways, Swiper spends its one gap on the columns — it writes a margin
   between them — and leaves the rows to fall out of how the flex lines share
   the frame. That is why one number used to do both, and it is where the second
   number goes in: the rows are given a height that leaves exactly the row gap
   over, and the wrapper hands that slack out between the lines.
   `!important` because the height it replaces is an inline style of Swiper's,
   written as a percentage of the frame with ITS gap already taken out. */
.elementor-widget-geeks-loop-carousel .swiper-horizontal.swiper-grid > .swiper-wrapper {
	align-content: space-between;
}

.elementor-widget-geeks-loop-carousel .swiper-horizontal.swiper-grid > .swiper-wrapper > .swiper-slide {
	--geeks-lc-gap-rows: var( --geeks-lc-row-gap, var( --geeks-lc-col-gap, var( --swiper-slides-gap, 0px ) ) );
	--geeks-lc-row-gaps: calc( ( var( --geeks-lc-rows, 1 ) - 1 ) * var( --geeks-lc-gap-rows ) );

	height: calc( ( 100% - var( --geeks-lc-row-gaps ) ) / var( --geeks-lc-rows, 1 ) ) !important;
}

/* EDITING THE TEMPLATE IN PLACE. The editor replaces the slides with the loop
   item itself so it can be built where it will live. That item has no slide to
   borrow a width from, so without a floor it measures zero and the carousel
   collapses to nothing — which reads as "my widget disappeared". The clipping
   the carousel needs the rest of the time also has to come off, or the item is
   cut at the container's edge while it is being worked on. */
.elementor-widget-geeks-loop-carousel .e-loop-first-edit {
	margin-block-start: 23px;
	min-width: 33%;
}

.elementor-widget-geeks-loop-carousel.elementor-in-place-template-editable .elementor-loop-container {
	overflow: visible;
	overflow-x: clip;
}

/* The frame around the item being edited. Elementor draws this itself, but
   through `div[class*="elementor-widget-loop"]` — a SUBSTRING of its own widget
   class, which ours (elementor-widget-geeks-loop-carousel) does not contain. So
   the rule is restated here rather than inherited; the colour is Elementor's, so
   editing a loop item looks the same whoever's carousel it sits in.
   Every slide is the SAME document, so every one of them carries the active
   class — the frame belongs on the one being edited, and the way to name that
   one is to ask which slide holds the way back. It used to key off
   `.swiper-slide-active` instead, which worked while the loop CLONED slides and
   the edited one could be slid to; the Swiper this widget ships moves slides
   rather than cloning them, so the edited slide is no longer necessarily the
   active one and the frame went missing. */
.elementor-widget-geeks-loop-carousel .swiper-slide.elementor-edit-area-active:has( .elementor-document-save-back-handle ),
.elementor-widget-geeks-loop-carousel .elementor-edit-area-active.e-loop-first-edit:first-of-type {
	border: 2px solid #5eead4;
}

.elementor-widget-geeks-loop-carousel .elementor-edit-area-active .elementor-document-save-back-handle {
	display: flex;
	line-height: normal;
	white-space: nowrap;
}

/* The slide being edited grows a handle above it that must be allowed out. */
.elementor-widget-geeks-loop-carousel .elementor-loop-container > .swiper-wrapper > .swiper-slide:has( .elementor-document-save-back-handle ) {
	overflow: initial;
}

/* A link used as the slide's own container keeps the display it was given. */
.elementor-widget-geeks-loop-carousel .swiper-wrapper .swiper-slide a.e-con {
	display: var( --display );
}

/* SLIDES THAT ARRIVE LATER.
   A fed carousel is printed with no slides at all, so everything below is
   about the gap between the page arriving and the posts arriving: cells that
   hold the carousel's size open, a loader where the reader is already looking,
   and the way a batch appears once its pictures are there.

   Colour is taken from `currentColor` throughout, so the waiting state follows
   whatever the author has set around it rather than imposing a grey. */
.elementor-widget-geeks-loop-carousel .geeks-lc-hold {
	position: relative;

	/* The shape comes from the control. A cell with nothing in it has no height
	   of its own, and this is how it is given one before there is anything
	   inside to give it: a ratio of its own width.
	   It yields, as it should — a slide already in the row makes the row taller,
	   every cell stretches to match, and `aspect-ratio` only ever decides a
	   height that nothing else has decided. */
	aspect-ratio: var( --geeks-lc-hold-aspect, 100 / 56.25 );
	pointer-events: none;
}

/* The turning mark, in the middle of the cell it is waiting for. Every cell
   carries its own, because every cell is waited for on its own: the one whose
   picture is ready stops, the one still fetching keeps turning. */
.elementor-widget-geeks-loop-carousel .geeks-lc-hold::after {
	position: absolute;

	/* Centred by `inset: 0` and an automatic margin, not by a half-a-height
	   offset and not by a translate: the height to take half of is the CELL's,
	   which changes with the row, and the ratio is a fraction of the width in
	   any case. The translate is the other tempting way and it is worse — the
	   turning below is a transform too, and the two would take turns. */
	inset: 0;
	width: 26px;
	height: 26px;
	margin: auto;
	border: 2px solid currentColor;
	border-inline-start-color: transparent;
	border-radius: 50%;
	opacity: 0.45;
	content: "";
}

/* The tint, over the whole cell however tall the row has made it. Its own
   element rather than a background on the cell, because what breathes is an
   opacity — and an opacity on the cell would take the mark above with it. */
.elementor-widget-geeks-loop-carousel .geeks-lc-hold-inner {
	position: absolute;
	inset: 0;
	border-radius: 4px;
	background-color: currentColor;
	opacity: 0.08;
}

/* A cell that has given up asking. It keeps the shape of the others so the row
   does not jump, and holds the way back. */
.elementor-widget-geeks-loop-carousel .geeks-lc-failed {
	pointer-events: auto;
}

.elementor-widget-geeks-loop-carousel .geeks-lc-failed::after {
	display: none;
}

/* Centred the same way the mark is, and for the same reason: the cell's height
   is the row's, which is not known here. */
.elementor-widget-geeks-loop-carousel .geeks-lc-failed .geeks-lc-retry {
	position: absolute;
	inset: 0;
	width: fit-content;
	height: fit-content;
	margin: auto;
}

.elementor-widget-geeks-loop-carousel .geeks-lc-retry {
	padding: 8px 14px;
	border: 1px solid currentColor;
	border-radius: 4px;
	background: none;
	color: inherit;
	font: inherit;
	cursor: pointer;
}

/* In, once its own pictures are there — one cell at a time, in place of the
   one that was waiting for it. Opacity alone: Swiper owns the transform on a
   slide, and an animation that also moved it would be fighting the carousel
   for the same property. */
.elementor-widget-geeks-loop-carousel .swiper-slide.geeks-lc-came {
	animation: geeks-lc-arrive var( --geeks-lc-fade, 300ms ) ease both;
}

@keyframes geeks-lc-arrive {
	from {
		opacity: 0;
	}

	to {
		opacity: 1;
	}
}

/* Movement for those who want it. Waiting is the one state that genuinely has
   to look alive — a still spinner reads as a carousel that has given up — so
   the reduced-motion version keeps a slow pulse rather than nothing at all. */
@media ( prefers-reduced-motion: no-preference ) {
	.elementor-widget-geeks-loop-carousel .geeks-lc-hold-inner {
		animation: geeks-lc-breathe 1.6s ease-in-out infinite;
	}

	.elementor-widget-geeks-loop-carousel .geeks-lc-hold::after {
		animation: geeks-lc-turn 0.8s linear infinite;
	}
}

@media ( prefers-reduced-motion: reduce ) {
	.elementor-widget-geeks-loop-carousel .geeks-lc-hold::after {
		animation: geeks-lc-breathe 1.6s ease-in-out infinite;
	}
}

@keyframes geeks-lc-breathe {
	0%,
	100% {
		opacity: 0.08;
	}

	50% {
		opacity: 0.18;
	}
}

@keyframes geeks-lc-turn {
	to {
		transform: rotate( 360deg );
	}
}

/* THE WAY BACK GOES INSIDE THE SLIDE.
 *
 * Elementor hangs Save & Back in the 30px strip over the edited slide's top
 * edge — outside the slide, where anything that clips can swallow it. The rail
 * above stops clipping while a template is open, but a carousel inside a GEEKS
 * horizontal scroll, or in any container the author set to hide its overflow,
 * is clipped from outside this widget, and that clip is not ours to lift.
 *
 * The handler sets this class whenever a template is open in place, so the way
 * back is in the same place on every carousel: 30px in from the slide's
 * top-left corner.
 *
 * Not centred, and not nudged sideways by a percentage of itself: Elementor's
 * own furniture — the container's toolbar, the drag dots, the element handles —
 * all hangs on the top edge, and a tab inset from the corner is clear of every
 * one of them at once. A percentage offset would have been measured in the
 * handle's own width, which is the width of a TRANSLATED string: "Save & Back"
 * is Pro's, and every locale sizes it differently.
 *
 * The tails go with the move: they are what makes a tab look fixed to an edge,
 * and this one is not on an edge any more.
 *
 * Nothing positions the slide here — Swiper's own stylesheet already gives
 * every slide `position: relative`, so `inset: 0` means the slide. The grid has
 * to say so itself; its items are plain static document roots.
 *
 * Four classes beat Pro's three, so no `!important` is needed. */
.elementor-widget-geeks-loop-carousel.geeks-lc-handle-inside .elementor-document-save-back-handle .elementor-document-handle__inner {
	top: 30px;
	left: 30px;
	transform: none;
	border-radius: 5px;
}

.elementor-widget-geeks-loop-carousel.geeks-lc-handle-inside .elementor-document-save-back-handle .elementor-document-handle__inner::before,
.elementor-widget-geeks-loop-carousel.geeks-lc-handle-inside .elementor-document-save-back-handle .elementor-document-handle__inner::after {
	display: none;
}
