/**
 * GEEKS Image — the frame and everything inside it.
 *
 * Three nested nodes, three owners: Elementor keeps the widget wrapper, we keep
 * `.geeks-img` (our motion node), and the picture keeps itself. Nothing here
 * writes to the Elementor wrapper, so an element-level effect can move the whole
 * widget while our own motion runs inside it.
 *
 * The ratio box is `aspect-ratio` on the frame, written per breakpoint by
 * Elementor's own selectors. When no ratio is set the frame has no definite
 * height, so the picture's `height: 100%` resolves to `auto` and the image keeps
 * its natural shape — one rule that covers both cases without a class to toggle.
 */

.geeks-img {
	position: relative;
	display: inline-block;
	width: 100%;
	max-width: 100%;
	margin: 0;
	vertical-align: top;
}

/* A single-cell grid, not a block. Two reasons, both structural: a grid row is
   a DEFINITE size, so the picture's `height: 100%` resolves against a frame
   sized by `aspect-ratio` — as a block it silently falls back to the picture's
   natural height and `cover` never crops — and every layer (both pictures, the
   mask, the overlay) can share one cell without absolute positioning. */
.geeks-img-frame {
	position: relative;
	display: grid;
	grid-template-columns: 100%;
	max-width: 100%;
	margin: 0;
	overflow: hidden;
	line-height: 0;
}

/* The link is a pass-through: it takes the cell and hands it straight on, so a
   linked picture fills its frame exactly like an unlinked one. */
.geeks-img-link {
	grid-area: 1 / 1;
	display: grid;
	grid-template-columns: 100%;
	min-height: 0;
	line-height: 0;
}

/* Both pictures are laid out identically, so the second can be brought in
   without a jump. B only ever sits on top; it is invisible until a mode asks
   for it. */
.geeks-img-a,
.geeks-img-b {
	grid-area: 1 / 1;
	align-self: stretch;
	display: block;
	width: 100%;
	height: 100%;
	min-height: 0;
	max-width: 100%;
	object-fit: cover;
	object-position: 50% 50%;
}

.geeks-img-b {
	opacity: 0;
}

/* THE PORTRAIT WRAPPER TAKES THE PICTURE'S PLACE.
 *
 * Both pictures are grid items at 1 / 1, so that they can sit on top of each
 * other. Wrapping one in <picture> makes the WRAPPER the grid item and the
 * <img> an ordinary child of it — so the placement moves out here, and the
 * picture inside simply fills it. Everything the author styles stays on the
 * img, where their own rules already point.
 *
 * Only written when a portrait file resolved, so a widget that does not use
 * this is the markup it always was. */
.geeks-img-pic {
	grid-area: 1 / 1;
	align-self: stretch;
	display: block;
	width: 100%;
	height: 100%;
	min-height: 0;
	max-width: 100%;
}

.geeks-img-ov {
	position: absolute;
	inset: 0;
	z-index: 2;
	pointer-events: none;
}

/* THE FILL LIVES HERE, not on the layer above.
   Elementor's lazy background rule strips `background-image` from every ELEMENT
   inside a late container until it is marked loaded; a pseudo-element is not an
   element, so a gradient painted here is shown from the first frame instead of
   appearing when the reader happens to scroll far enough. The old flat-colour
   overlays are unaffected either way — a background COLOUR was never stripped.

   NOT PROMOTED, and that is the whole of the second lesson. This carried
   `translateZ(0)`, `will-change` and `backface-visibility` for a few hours, to
   hand the gradient to the compositor once and stop it shimmering during a
   scroll. It bought nothing — the shimmer turned out to be the reader's monitor,
   not the page — and it cost a visible fault: a composited layer inside a
   rounded `overflow: hidden` frame loses its last row to the clip mask, so the
   bottom edge of every overlay showed one pixel of UNDIMMED picture, a pale
   line right across the card. Measured at the frame's bottom row: (206,230,244)
   promoted against (41,46,49) plain, where the gradient above reads (41,46,49).

   A gradient on a pseudo-element is composited perfectly well unasked. */
.geeks-img-ov::before {
	content: "";
	inset: 0;
	position: absolute;
}

/* An overlay caption is positioned by flex rather than by a transform: the
   transform channel belongs to the motion writer, and a caption that parked
   itself there would be erased the moment anything moved. */
.geeks-img-capbox {
	position: absolute;
	inset: 0;
	/* Above the shine: a sweep passing OVER the words reads as a mistake, and
	   the shine is built at runtime so DOM order alone would put it on top. */
	z-index: 4;
	display: flex;
	pointer-events: none;
}

.geeks-img-capbox .geeks-img-cap {
	pointer-events: auto;
}

.geeks-img-cap {
	margin: 0;
	max-width: 100%;
	line-height: 1.45;
}

.geeks-img-capbox.at-tl { align-items: flex-start;  justify-content: flex-start; }
.geeks-img-capbox.at-t  { align-items: flex-start;  justify-content: center; }
.geeks-img-capbox.at-tr { align-items: flex-start;  justify-content: flex-end; }
.geeks-img-capbox.at-l  { align-items: center;      justify-content: flex-start; }
.geeks-img-capbox.at-c  { align-items: center;      justify-content: center; }
.geeks-img-capbox.at-r  { align-items: center;      justify-content: flex-end; }
.geeks-img-capbox.at-bl { align-items: flex-end;    justify-content: flex-start; }
.geeks-img-capbox.at-b  { align-items: flex-end;    justify-content: center; }
.geeks-img-capbox.at-br { align-items: flex-end;    justify-content: flex-end; }

/* Shown on hover: focus-within counts too, so a keyboard reader reaching the
   link is told what the picture is. */
.geeks-img-cap.on-hover {
	opacity: 0;
	transition: opacity .3s ease;
}

.geeks-img:hover .geeks-img-cap.on-hover,
.geeks-img:focus-within .geeks-img-cap.on-hover {
	opacity: 1;
}

@media ( prefers-reduced-motion: reduce ) {
	.geeks-img-cap.on-hover {
		transition: none;
	}
}

/* ---------------------------------------------------------------------------
 * The reveal's first frame.
 *
 * A picture that will be uncovered must not be visible while the engine is
 * still loading, so the widget ships hidden and the handler takes over on its
 * first painted frame. The animation is the failsafe: if the engine never
 * arrives — a failed script, an error upstream — the picture appears anyway
 * after 2.5s, and a reader who prefers reduced motion never waits at all.
 * ------------------------------------------------------------------------ */

.geeks-img-hide .geeks-img-frame,
.geeks-img-hide .geeks-img-cap {
	visibility: hidden;
	animation: geeks-img-failsafe 0s 2.5s forwards;
}

@keyframes geeks-img-failsafe {
	to { visibility: visible; }
}

@media ( prefers-reduced-motion: reduce ) {
	.geeks-img-hide .geeks-img-frame,
	.geeks-img-hide .geeks-img-cap {
		visibility: visible;
		animation: none;
	}
}

/* The caption's own entrance is written by the engine; these are only the
   resting states it animates from and to. */
.geeks-img-cap.gi-wipe {
	clip-path: inset(0 0 0 0);
}

/* The colour wash greys what is BEHIND it rather than filtering the picture,
   so the picture keeps its own transform, parallax and Ken Burns throughout.
 *
 * A duotone is TWO of these layers, never one. Chromium ignores mix-blend-mode
 * on an element that also carries a backdrop-filter — the filter makes that
 * element its own backdrop root, so the blend has nothing left to blend
 * against, and the layer paints flat over the picture instead of colouring it.
 * Nesting does not rescue it either: a stacking context confines a blend to
 * itself, so a colour layer inside a wrapper blends with the wrapper's empty
 * group. The two have to be siblings of the picture, in the frame's own
 * context, which is why the engine builds a pair and sets `isolation` on the
 * frame to keep the blend from reaching the page behind it. */
.geeks-img-wash {
	position: absolute;
	inset: 0;
	z-index: 2;
	pointer-events: none;
	-webkit-backdrop-filter: grayscale(1);
	backdrop-filter: grayscale(1);
}

/* The gallery's second slide. Present for the lightbox to find, invisible and
   unreachable for everyone else — the picture itself opens the same gallery. */
.geeks-img-lb2 {
	position: absolute;
	width: 1px;
	height: 1px;
	opacity: 0;
	pointer-events: none;
}

/* The colour half of a duotone, above the greyscale one and blending with what
   it produced. It carries no backdrop-filter of its own — see the wash above
   for why the two can never share an element. */
.geeks-img-tint {
	position: absolute;
	inset: 0;
	z-index: 2;
	pointer-events: none;
	mix-blend-mode: color;
}

/* Two hover looks need a layer rather than a filter: one picture cannot be made
   two colours by a filter chain, and a sweep needs something to sweep. This is
   the duotone's greyscale half; its colour rides on a .geeks-img-tint above. */
.geeks-img-duo {
	position: absolute;
	inset: 0;
	z-index: 2;
	pointer-events: none;
	-webkit-backdrop-filter: grayscale(1);
	backdrop-filter: grayscale(1);
}

.geeks-img-shine {
	position: absolute;
	top: -20%;
	bottom: -20%;
	left: 0;
	z-index: 3;
	pointer-events: none;
	will-change: transform;
}

/* In the editor the second picture is otherwise invisible — it only shows on
   hover, and hover behaviour is deliberately not wired while editing. This lets
   an author confirm they picked the right one without changing the page. */
.elementor-editor-active .geeks-img:hover .geeks-img-b {
	opacity: 1;
	transition: opacity .2s ease;
}

/* ---------------------------------------------------------------------------
 * The browser's long press, blocked by the author's choice.
 *
 * A long press normally opens the phone's own menu and raises the selection
 * handles. Where the widget's own gesture IS a press and hold, that menu slides
 * over the mask a moment after it opens; and on plenty of sites an author simply
 * wants the picture to behave like part of the page. Both are the same setting,
 * so the reader keeps the menu wherever it is turned off.
 * ------------------------------------------------------------------------ */

.geeks-img-nomenu .geeks-img-frame {
	-webkit-touch-callout: none;
	-webkit-user-select: none;
	user-select: none;
}

.geeks-img-nomenu .geeks-img-a,
.geeks-img-nomenu .geeks-img-b {
	-webkit-touch-callout: none;
	-webkit-user-drag: none;
}

/* ---------------------------------------------------------------------------
 * The split layout: one picture drawn as several pieces.
 *
 * Every piece is a window with its own copy of the picture inside it, sized to
 * the FRAME and shifted so that piece's part shows through — so the pieces
 * reassemble into the original exactly, and the gap covers a strip of the
 * photograph rather than squeezing it. The engine writes every position, since
 * they are pixels measured from the frame's box; these are only the rules that
 * make a window a window.
 *
 * Not to be confused with the reveal's Slices and Tiles, which are a mask that
 * uncovers the picture and is then dropped. A mask cannot hold an offset; these
 * are real elements, which is the whole point of them.
 * ------------------------------------------------------------------------- */
.geeks-img-split {
	position: absolute;
	inset: 0;
	z-index: 1;
	pointer-events: none;
}

.geeks-img-piece {
	position: absolute;
	overflow: hidden;
	will-change: transform;
}

.geeks-img-piece-img {
	position: absolute;
	max-width: none;
	pointer-events: none;
	will-change: transform;
}
