/**
 * SIM Gallery — frontend stylesheet.
 *
 * The entire visual layer of the gallery: a native CSS scroll-snap track,
 * blur-up placeholders, a scrolling thumbnail strip, hover-revealed circular
 * nav, the hand-rolled lightbox and the inline zoom. No Swiper, no PhotoSwipe,
 * no icon font, no SVG — chevrons and the magnifier are drawn with borders.
 *
 * SELF-SUFFICIENT ON A STOCK THEME, TOKEN-AWARE INSIDE sim-theme.
 * Every theme design token below is consumed WITH a literal fallback
 * (e.g. `var(--space-2, 8px)`) and NONE is ever defined on :root — this
 * stylesheet must never leak values into a theme that already owns them.
 *
 * THREE MEASURED ENVIRONMENT CONSTRAINTS DRIVE THE ODD-LOOKING RULES HERE.
 * Do not "clean them up":
 *
 * 1. sim-theme main.css:2227-2236 applies
 *    `max-width:100%!important; width:100%!important; height:auto!important`
 *    to `.single-product__gallery img` at specificity (0,1,1). `height:auto`
 *    silently defeats object-fit and collapses every aspect-ratio box. EVERY
 *    rule here that sizes an image inside .simg therefore uses `!important`
 *    AND a selector of specificity >= (0,2,1).
 * 2. At >=1024px the theme makes `.single-product__gallery` position:sticky.
 *    The lightbox is appended to document.body for that reason, and NO
 *    transform / filter / will-change may ever be applied to `.simg` or any
 *    ancestor: each one creates a containing block for fixed-position
 *    descendants and would trap the lightbox inside the sticky column. The
 *    only transforms in this file are on pseudo-elements and on <img>.
 * 3. The theme sets `html{scroll-behavior:smooth}`. `scroll-behavior` is not
 *    inherited, so it never reaches the track — and this file deliberately
 *    does not declare it either. The JS picks the behaviour per scrollTo call.
 *
 * There is deliberately NO container-level loading state: no spinner, no
 * desaturation, no opacity dip, no `.is-loading` class. The single blur in this
 * file is the per-slide LQIP in section 4. Do not add another.
 *
 * FORBIDDEN in this file: any `.rtwpvg-*`, `.swiper*`, `.flex*` or
 * `.woocommerce-product-gallery__image` selector; any `box-shadow`; any
 * `!important` on `.simg` positioning; any `:root` custom-property definition.
 *
 * @package SIM_Gallery
 */

/* -------------------------------------------------------------------------
 * 1. Local tokens and root
 *
 * The tokens live on `.simg` itself, never on :root, so two galleries on one
 * page (quick view, grouped product) can hold different values. PHP overrides
 * --simg-ratio inline per product; JS may override --simg-zoom.
 *
 * No position:sticky, no float, no grid-column, no margins here: the theme's
 * --space-7/8/10 grid gaps own the outer spacing, and `.single-product__gallery`
 * belongs to the theme — this plugin never styles or positions it.
 * ---------------------------------------------------------------------- */
.simg {
	--simg-ratio: 1/1;
	--simg-gap: var(--space-2, 8px);
	--simg-cols: 4;
	--simg-zoom: 2;
	--simg-nav: 44px;
	position: relative;
	display: block;
	width: 100%;
	max-width: 100%;
}

/* -------------------------------------------------------------------------
 * 2. Stage and track
 * ---------------------------------------------------------------------- */
.simg__stage {
	position: relative;
}

/* The track is the whole slider. Native scroll gives touch momentum, rubber
   banding and swipe for free; the JS layer only syncs thumbs and calls
   scrollTo.

   NOTE — no `scroll-behavior` declaration, on purpose. Declaring it here would
   fight scrollTo({behavior}) (documented WebKit regressions) and would make an
   intentionally instant jump — the variation change — animate across the strip.

   NOTE — no `touch-action` declaration, on purpose. The browser performs this
   scroll natively; any value we set here can only take capability away. */
.simg__track {
	display: flex;
	overflow-x: auto;
	overflow-y: hidden;
	scroll-snap-type: x mandatory;
	overscroll-behavior-x: contain;
	-ms-overflow-style: none;
}

/* Scrollbar hiding is gated on is-ready. Without JS the thumbs, arrows and
   zoom button are all hidden (section 5), so the scrollbar is the only
   remaining affordance that more images exist — it must survive. */
.simg.is-ready .simg__track {
	scrollbar-width: none;
}

.simg.is-ready .simg__track::-webkit-scrollbar {
	display: none;
}

/* The track is tabbable (tabindex="0" in the markup contract) so keyboard
   users can drive it with arrow keys; it must therefore show a focus ring. */
.simg__track:focus-visible {
	outline: 2px solid var(--c-ink, #0a0a0a);
	outline-offset: 2px;
}

/* -------------------------------------------------------------------------
 * 3. Slides
 *
 * `scroll-snap-align:center` rather than `start` so a future peek/inset layout
 * stays correct without touching the JS. `scroll-snap-stop:always` so a fast
 * flick advances exactly one image instead of skimming past four.
 * ---------------------------------------------------------------------- */
.simg__slide {
	flex: 0 0 100%;
	margin: 0;
	padding: 0;
	position: relative;
	overflow: hidden;
	aspect-ratio: var(--simg-ratio);
	background: var(--c-surface, #f5f5f5);
	scroll-snap-align: center;
	scroll-snap-stop: always;
}

/* MANDATORY specificity + !important — see constraint 1 in the file docblock.
   (0,3,1) beats the theme's (0,1,1) `height:auto!important`, which would
   otherwise collapse the aspect-ratio box and disable object-fit.
   `max-width:none` is required too: the theme pins it to 100% and the image is
   already width-constrained by the flex basis.

   Sizing is UNCONDITIONAL — it must beat the theme with or without JS. Only the
   fade-in below is gated on is-ready. */
.simg .simg__track .simg__slide img {
	width: 100% !important;
	height: 100% !important;
	max-width: none !important;
	object-fit: contain;
	display: block;
}

/* NO-JS: the starting `opacity:0` is gated on is-ready, because the class that
   clears it (`is-loaded`) is only ever added by sim-gallery.js. Ungated, a
   disabled/blocked/failed script would leave EVERY slide image permanently
   invisible — the exact opposite of the documented degradation path, which
   promises a plain scrollable track of visible images. Gallery.prototype adds
   is-ready before it reveals any image, so the LQIP-to-image fade is unchanged
   whenever JS does run. */
.simg.is-ready .simg__track .simg__slide img {
	opacity: 0;
	transition: opacity var(--dur-fast, .15s) var(--ease, cubic-bezier(.2, 0, .2, 1));
}

.simg.is-ready .simg__track .simg__slide img.is-loaded {
	opacity: 1;
}

/* -------------------------------------------------------------------------
 * 4. Blur-up placeholder
 *
 * Zero extra network requests: --simg-lqip points at the very same
 * woocommerce_gallery_thumbnail file (~3KB) the thumbnail strip already
 * fetches, so it is warm in cache before the main image starts decoding.
 *
 * scale(1.08) hides the soft transparent edge that blur() bleeds inward. The
 * transform sits on the PSEUDO-ELEMENT, never on `.simg` — see constraint 2.
 *
 * When --simg-lqip is absent (the placeholder slide of an empty set) the
 * background-image declaration is invalid at computed-value time and resolves
 * to `none`, which is exactly the wanted result — no fallback rule needed.
 *
 * GATED ON is-ready for the same reason as the image fade in section 3: only
 * sim-gallery.js ever adds the `is-loaded` that fades this layer out. Ungated,
 * a page without JS would show a permanently blurred placeholder painted over
 * a perfectly sharp image (the pseudo is positioned, the <img> is not, so the
 * pseudo wins the paint order once the image is opaque).
 * ---------------------------------------------------------------------- */
.simg.is-ready .simg__slide::before {
	content: "";
	position: absolute;
	inset: 0;
	background-image: var(--simg-lqip);
	background-size: cover;
	background-position: center;
	filter: blur(12px);
	transform: scale(1.08);
	transition: opacity var(--dur-base, .25s) linear;
	/* The pseudo paints above the statically positioned <img>, so it would keep
	   swallowing hover and pointer events (and therefore the zoom pointer
	   tracking) for the whole life of the slide, long after it faded out. */
	pointer-events: none;
}

.simg.is-ready .simg__slide.is-loaded::before {
	opacity: 0;
}

/* -------------------------------------------------------------------------
 * 5. Nav buttons
 *
 * Mirrors the FlexSlider arrow language the theme already ships: a circular
 * white pill on a hairline, inverting to ink on hover. Hidden until the
 * gallery is hovered, which is why they may never carry a box-shadow — the
 * theme's token block asks for borders over shadows.
 * ---------------------------------------------------------------------- */
.simg__nav {
	position: absolute;
	top: 50%;
	transform: translateY(-50%);
	/* --simg-nav is defined on .simg; the lightbox reuses these buttons from
	   document.body, outside that scope, hence the literal fallback. */
	width: var(--simg-nav, 44px);
	height: var(--simg-nav, 44px);
	border-radius: 50%;
	background: var(--c-bg, #fff);
	color: var(--c-ink, #0a0a0a);
	border: var(--bw, 1px) solid var(--c-line, #e5e5e5);
	z-index: var(--z-elevated, 10);
	opacity: 0;
	cursor: pointer;
	padding: 0;
	box-sizing: border-box;
	-webkit-appearance: none;
	appearance: none;
	/* Flex container so the chevron's `margin:auto` centres it on the block
	   axis; in normal flow `margin-top:auto` would compute to 0 and pin the
	   chevron to the top of the button. */
	display: flex;
	transition:
		opacity var(--dur-fast, .15s) var(--ease, cubic-bezier(.2, 0, .2, 1)),
		background-color var(--dur-fast, .15s) var(--ease, cubic-bezier(.2, 0, .2, 1)),
		color var(--dur-fast, .15s) var(--ease, cubic-bezier(.2, 0, .2, 1));
}

.simg__nav--prev {
	left: var(--space-3, 12px);
}

.simg__nav--next {
	right: var(--space-3, 12px);
}

/* Revealed on hover of the whole gallery, or when focused by keyboard. While
   invisible they are also unhoverable in practice: any pointer over the stage
   satisfies `.simg:hover`, so they are never a transparent click target. */
.simg:hover .simg__nav,
.simg__nav:focus-visible {
	opacity: 1;
}

.simg__nav:hover {
	background: var(--c-ink, #0a0a0a);
	color: var(--c-bg, #fff);
	border-color: var(--c-ink, #0a0a0a);
}

.simg__nav:focus-visible {
	outline: 2px solid var(--c-ink, #0a0a0a);
	outline-offset: 2px;
}

/* Chevrons are two rotated borders. No SVG, no icon font, no extra request.

   `margin:auto` inside the flex button absorbs the free space on BOTH axes, so
   the 12px border-box lands dead centre whatever the button measures. The
   optical nudge then rides on the transform, not on a hard-coded margin-left:
   only two of the four borders are drawn, so the rotated glyph's visual mass
   sits ~2px off the geometric centre — left for `<`, right for `>`.

   The previous `margin-left:18px/14px` encoded that 2px as an absolute offset
   from a 44px button, which stopped being centred the moment the max-width:767px
   block below shrank the button to 36px (the glyph landed 6px/4px right of
   centre). Translating instead is size-independent. Ordering matters: the list
   applies right-to-left, so the box is rotated first and the translate is a
   pure horizontal shift in the button's own axes, not along the rotated one. */
.simg__nav::before {
	content: "";
	width: 10px;
	height: 10px;
	display: block;
	margin: auto;
	border-top: 2px solid currentColor;
	border-right: 2px solid currentColor;
}

.simg__nav--prev::before {
	transform: translateX(2px) rotate(-135deg);
}

.simg__nav--next::before {
	transform: translateX(-2px) rotate(45deg);
}

@media (max-width: 767px) {

	.simg__nav {
		opacity: 1;
		width: 36px;
		height: 36px;
	}
}

/* Touch devices swipe the track natively; arrows are a pointer affordance only.
   This block MUST stay after the max-width block so it wins on touch phones,
   where both media queries match. */
@media (hover: none) {

	.simg__nav {
		display: none;
	}
}

/* NO-JS DEGRADATION PATH. Until JS adds is-ready none of the JS-driven
   controls exist as far as the user is concerned: the arrows would not
   navigate, the thumbs would not select and the zoom button would not open
   anything. The track alone, with its scrollbar (section 2), still works. */
.simg:not(.is-ready) .simg__nav,
.simg:not(.is-ready) .simg__zoom,
.simg:not(.is-ready) .simg__thumbs {
	display: none;
}

/* -------------------------------------------------------------------------
 * 6. Zoom / expand button
 *
 * Anchored BOTTOM-RIGHT so it can never cover the `.onsale` badge, which the
 * theme absolutely positions top-left against `.single-product__gallery` at
 * priority 10 on the same hook we render at priority 20.
 * ---------------------------------------------------------------------- */
.simg__zoom {
	position: absolute;
	right: var(--space-3, 12px);
	bottom: var(--space-3, 12px);
	width: 36px;
	height: 36px;
	border-radius: 50%;
	background: var(--c-bg, #fff);
	color: var(--c-ink, #0a0a0a);
	border: var(--bw, 1px) solid var(--c-line, #e5e5e5);
	z-index: var(--z-elevated, 10);
	cursor: pointer;
	padding: 0;
	box-sizing: border-box;
	-webkit-appearance: none;
	appearance: none;
	transition:
		background-color var(--dur-fast, .15s) var(--ease, cubic-bezier(.2, 0, .2, 1)),
		color var(--dur-fast, .15s) var(--ease, cubic-bezier(.2, 0, .2, 1));
}

.simg__zoom:hover {
	background: var(--c-ink, #0a0a0a);
	color: var(--c-bg, #fff);
	border-color: var(--c-ink, #0a0a0a);
}

.simg__zoom:focus-visible {
	outline: 2px solid var(--c-ink, #0a0a0a);
	outline-offset: 2px;
}

/* Magnifier: an 8px ring plus a 6px handle at 45deg, both drawn in
   currentColor so they invert with the button on hover. box-sizing keeps the
   ring exactly 8px across including its 2px border. */
.simg__zoom::before {
	content: "";
	position: absolute;
	top: 12px;
	left: 12px;
	width: 8px;
	height: 8px;
	border: 2px solid currentColor;
	border-radius: 50%;
	box-sizing: border-box;
}

.simg__zoom::after {
	content: "";
	position: absolute;
	top: 19px;
	left: 20px;
	width: 6px;
	height: 2px;
	background: currentColor;
	transform: rotate(45deg);
	transform-origin: left center;
}

/* -------------------------------------------------------------------------
 * 7. Thumbnail strip — scrolls, never wraps
 *
 * MEASURED: galleries hold 1-16 images with a mode of 5. A rigid 4-column grid
 * therefore orphans a fifth thumbnail onto a second row on the single most
 * common product in the catalogue. A one-row scroller with --simg-cols
 * thumbnails in view keeps the strip a fixed height for every set size.
 * ---------------------------------------------------------------------- */
.simg__thumbs {
	display: flex;
	gap: var(--simg-gap);
	margin-top: var(--space-3, 12px);
	overflow-x: auto;
	scroll-snap-type: x proximity;
	overscroll-behavior-x: contain;
	scrollbar-width: none;
	padding: 0;
}

.simg__thumbs::-webkit-scrollbar {
	display: none;
}

/* aspect-ratio + overflow:hidden on the BUTTON is what makes object-fit work
   despite the theme's `height:auto!important` on the inner <img> — the same
   technique the theme itself already uses for .flex-control-thumbs li.
   box-sizing keeps the hairline inside the calc'd basis so exactly
   --simg-cols thumbs fit the row. */
.simg__thumb {
	flex: 0 0 calc((100% - (var(--simg-cols) - 1) * var(--simg-gap)) / var(--simg-cols));
	aspect-ratio: var(--simg-ratio);
	padding: 0;
	overflow: hidden;
	cursor: pointer;
	background: var(--c-surface-2, #fafafa);
	border: var(--bw, 1px) solid var(--c-line, #e5e5e5);
	box-sizing: border-box;
	-webkit-appearance: none;
	appearance: none;
	scroll-snap-align: start;
	transition: border-color var(--dur-fast, .15s) var(--ease, cubic-bezier(.2, 0, .2, 1));
}

/* Same mandatory specificity + !important as the slide images (constraint 1).
   `cover` here, not `contain`: a thumbnail is an index, not a reproduction. */
.simg .simg__thumbs .simg__thumb img {
	width: 100% !important;
	height: 100% !important;
	max-width: none !important;
	object-fit: cover;
	display: block;
	opacity: .6;
	transition: opacity var(--dur-fast, .15s) var(--ease, cubic-bezier(.2, 0, .2, 1));
}

.simg__thumb:hover {
	border-color: var(--c-mute-soft, #a8a8a8);
}

.simg__thumb:hover img,
.simg__thumb.is-active img {
	opacity: 1;
}

/* The active indicator is the --c-ink hairline, NOT --c-accent: the theme
   reserves #e63946 for CTAs and the sale flash, and its token block asks for
   borders over shadows. There is no box-shadow anywhere in this file. */
.simg__thumb.is-active {
	border-color: var(--c-ink, #0a0a0a);
}

.simg__thumb:focus-visible {
	outline: 2px solid var(--c-ink, #0a0a0a);
	outline-offset: 2px;
}

/* -------------------------------------------------------------------------
 * 8. State classes
 * ---------------------------------------------------------------------- */

/* One image: nothing to navigate to, and the track must not scroll at all —
   an overscrolling single slide reads as a broken slider. */
.simg--single .simg__thumbs,
.simg--single .simg__nav {
	display: none;
}

.simg--single .simg__track {
	overflow: hidden;
}

/* No image in the current set has zoom headroom (measured: 8,821 attachments
   are 800x800 originals while woocommerce_single is 812px, so those have
   exactly none). The cursor must not promise a magnification we cannot give. */
.simg--nozoom .simg__slide img {
	cursor: default;
}

.simg.is-empty .simg__slide {
	background: var(--c-surface, #f5f5f5);
}

/* Used by .simg__status, the polite live region that announces set changes. */
.simg-sr-only {
	position: absolute;
	width: 1px;
	height: 1px;
	padding: 0;
	margin: -1px;
	overflow: hidden;
	clip: rect(0 0 0 0);
	clip-path: inset(50%);
	white-space: nowrap;
	border: 0;
}

/* -------------------------------------------------------------------------
 * 9. Inline zoom — pointer devices with real headroom only
 *
 * Gated on (hover:hover) and (pointer:fine): on touch the same gesture is
 * pinch-to-zoom inside the lightbox, and a hover-driven transform-origin is
 * meaningless without a hovering pointer.
 * ---------------------------------------------------------------------- */
@media (hover: hover) and (pointer: fine) {

	/* `[data-zoom]:not([data-zoom="0"])` rather than a bare `[data-zoom]`:
	   the attribute is printed on every slide image carrying the per-image
	   `zoom` flag from the payload, so the value — not its presence — is what
	   says whether this file has headroom (full_w / w >= opts.minHeadroom).

	   The opacity transition from section 3 is restated here alongside
	   transform: a bare `transition:transform` would replace it wholesale and
	   silently kill the LQIP fade-in on exactly the images most likely to be
	   large. transform-origin is deliberately ABSENT from the list — JS
	   rewrites it on every pointermove, and transitioning it produces visible
	   rubber-banding as the origin chases the cursor. */
	.simg:not(.simg--nozoom) .simg__slide img[data-zoom]:not([data-zoom="0"]) {
		cursor: zoom-in;
		transition:
			opacity var(--dur-fast, .15s) var(--ease, cubic-bezier(.2, 0, .2, 1)),
			transform var(--dur-base, .25s) var(--ease-out, cubic-bezier(.16, 1, .3, 1));
	}

	.simg__slide.is-zoomed img {
		transform: scale(var(--simg-zoom, 2));
	}
}

/* -------------------------------------------------------------------------
 * 10. Lightbox
 *
 * The element is appended to document.body by JS — see constraint 2: above
 * 1024px the theme makes the gallery column position:sticky, and a fixed
 * overlay rendered inside it would be clipped to that column.
 *
 * The local token defaults below are NOT a :root definition: they scope to the
 * lightbox element only, and exist because --simg-nav / --simg-zoom live on
 * `.simg`, which is no longer an ancestor once the node is on <body>. JS may
 * override --simg-zoom inline per image.
 * ---------------------------------------------------------------------- */
/* position/inset/z-index and the scrim colour are declared on the element and
   NOT left to `dialog:modal` + `::backdrop` alone. Both of those apply only
   while the dialog was opened with showModal(); the lightbox contract is
   written in terms of "toggle the [open] attribute", and a dialog that is open
   but not modal falls back to the UA's `position:absolute` — which, on a
   body-appended node, parks a fully transparent overlay at the bottom of the
   document instead of over the viewport. Declaring both makes the two open
   paths identical. The ::backdrop rule below is kept so the modal path still
   paints correctly in the sliver behind a dialog that is ever sized smaller
   than the viewport; top-layer elements ignore z-index, so it is inert there. */
dialog.simg__lightbox {
	--simg-nav: 44px;
	--simg-zoom: 2;
	position: fixed;
	inset: 0;
	z-index: calc(var(--z-modal, 400) + 10);
	padding: 0;
	border: 0;
	margin: 0;
	max-width: 100vw;
	max-height: 100vh;
	width: 100vw;
	height: 100vh;
	background: rgb(10 10 10 / .92);
	overscroll-behavior: contain;
	color: var(--c-inv-ink, #fff);
}

dialog.simg__lightbox::backdrop {
	background: rgb(10 10 10 / .92);
}

/* The filling inner element is REQUIRED, not decorative: a click on ::backdrop
   reports `event.target === dialog`, and so does a click on the dialog's own
   padding box. Only a child element that fills the dialog lets the JS tell
   "clicked the scrim, close" from "clicked the image, zoom".

   touch-action:manipulation (= pan-x pan-y pinch-zoom) suppresses the
   browser's native double-tap-to-zoom so it cannot fight our own double-tap
   handler, while leaving native pinch-zoom fully intact. */
.simg__lightbox-inner {
	position: absolute;
	inset: 0;
	display: flex;
	align-items: center;
	justify-content: center;
	touch-action: manipulation;
}

/* !important + (0,2,1) here for the same reason as the gallery images: if the
   lightbox is ever opened from a page where the theme's `img` rules apply, the
   height:auto/width:100% pair would stretch the full-size image to the viewport
   width. width/height auto keeps the intrinsic ratio; the max-* pair is the
   only constraint.

   Qualified through .simg__lightbox-inner on purpose. A bare `.simg__lightbox
   img` is (0,1,1) — an exact TIE with the theme's `.single-product__gallery
   img { height:auto!important }`, which would then be settled by stylesheet
   order rather than by us, and the file docblock's constraint 1 mandates
   >= (0,2,1) for every rule that sizes an image. The inner element is required
   by the markup contract anyway, so nothing is narrowed in practice. */
.simg__lightbox .simg__lightbox-inner img {
	max-width: 100% !important;
	max-height: 100vh !important;
	width: auto !important;
	height: auto !important;
	object-fit: contain;
	transition: transform var(--dur-base, .25s) var(--ease-out, cubic-bezier(.16, 1, .3, 1));
}

.simg__lightbox-inner.is-zoomed img {
	transform: scale(var(--simg-zoom, 2));
}

.simg__close {
	position: absolute;
	top: 16px;
	right: 16px;
	width: 44px;
	height: 44px;
	border-radius: 50%;
	background: var(--c-bg, #fff);
	color: var(--c-ink, #0a0a0a);
	border: 0;
	cursor: pointer;
	padding: 0;
	box-sizing: border-box;
	-webkit-appearance: none;
	appearance: none;
	z-index: 1;
	display: flex;
	align-items: center;
	justify-content: center;
}

.simg__close:focus-visible {
	outline: 2px solid var(--c-bg, #fff);
	outline-offset: 2px;
}

/* Close glyph: two crossed 2px bars, currentColor, no SVG. */
.simg__close::before,
.simg__close::after {
	content: "";
	position: absolute;
	width: 16px;
	height: 2px;
	background: currentColor;
}

.simg__close::before {
	transform: rotate(45deg);
}

.simg__close::after {
	transform: rotate(-45deg);
}

/* Inside the lightbox the arrows anchor to the viewport, not to the stage.
   opacity:1 is required: the reveal rule in section 5 is `.simg:hover .simg__nav`,
   and `.simg` is not an ancestor here, so these buttons would otherwise stay
   permanently invisible.
   They remain hidden on touch via the (hover:none) block in section 5 — touch
   users swipe the lightbox, exactly as they swipe the track. */
.simg__lightbox .simg__nav {
	position: fixed;
	opacity: 1;
	z-index: 1;
}

/* NON-NATIVE FALLBACK. JS renders a <div class="simg__lightbox"> with
   role="dialog" aria-modal="true" and NO [data-native] attribute when
   HTMLDialogElement is unavailable, toggling the same `open` attribute. */
.simg__lightbox:not([data-native]) {
	position: fixed;
	inset: 0;
	z-index: calc(var(--z-modal, 400) + 10);
	display: none;
}

.simg__lightbox:not([data-native])[open] {
	display: block;
}

/* The div fallback has no ::backdrop, so it paints its own scrim element. */
.simg__lightbox:not([data-native]) .simg__scrim {
	position: absolute;
	inset: 0;
	background: rgb(10 10 10 / .92);
}

/* SCROLL LOCK, in CSS rather than JS.
   Toggling `overflow:hidden` on <html> from JS is the classic source of the
   lost-scroll-position bug (and of a leaked lock when an error interrupts the
   close path). :has() is Baseline since Dec 2023 and degrades to "page still
   scrolls behind the overlay", which is a cosmetic nuisance, not a break.
   Both selectors are needed: the first is the native <dialog>, the second the
   div fallback (and it also covers the first — kept explicit on purpose). */
html:has(dialog.simg__lightbox[open]) {
	overflow: hidden;
}

html:has(.simg__lightbox[open]) {
	overflow: hidden;
}

/* -------------------------------------------------------------------------
 * 11. Reduced motion
 *
 * Durations are collapsed rather than set to `none` so transitionend still
 * fires and no JS state machine stalls waiting for it.
 *
 * The theme's own reduced-motion block sets `scroll-behavior:auto!important`,
 * which neutralises CSS smooth scrolling ONLY. It has no effect whatsoever on
 * `scrollTo({behavior:'smooth'})` — the JS must consult matchMedia itself.
 * ---------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {

	.simg__slide img,
	.simg__slide::before,
	.simg__thumb,
	.simg__thumb img,
	.simg__nav,
	.simg__zoom,
	.simg__lightbox img {
		transition-duration: .01ms !important;
	}
}
