/**
 * Quick View.
 *
 * Every colour, radius, easing and font here comes from the tokens already on
 * :root in stonex.css — nothing is a new value. The panel should read as a
 * piece of the site that was always there, and the fastest way for a modal to
 * look bolted on is to invent its own grey.
 *
 * The two layouts are genuinely different rather than one scaled down. On
 * desktop it is a centred dialog with the gallery beside the detail; on a phone
 * it is a bottom sheet that rises from the edge the thumb is already near, with
 * the gallery above the detail. 80-90% of this site's traffic is mobile, so the
 * sheet is the default and the dialog is the enhancement.
 *
 * @package Stonex
 */

/* ------------------------------------------------------------------ shell */

.qv-modal {
	position: fixed;
	inset: 0;
	z-index: 1200; /* Above the header (100) and below the cart drawer (1300). */
	display: flex;
	align-items: flex-end;
	justify-content: center;
}

.qv-modal[hidden] { display: none; }

.qv-modal__scrim {
	position: absolute;
	inset: 0;
	background: rgba(28, 26, 23, .55);
	-webkit-backdrop-filter: blur(2px);
	backdrop-filter: blur(2px);
	opacity: 0;
	transition: opacity .26s var(--ease);
}

.qv-modal.is-open .qv-modal__scrim { opacity: 1; }

.qv-modal__dialog {
	position: relative;
	width: 100%;
	/* Caps the dialog against a wide child. The `min-width: 0` rules further
	   down are what let its children actually reflow — a grid item defaults to
	   `min-width: auto` and refuses to shrink below its content, which is what
	   pushed this to 410px at a 320px viewport.

	   `100%`, not `100vw`. On this site the listing page forces a layout
	   viewport wider than the device (410px at a 320px phone), and `vw` units
	   resolve against the VISUAL viewport — so `100vw` capped the dialog at
	   320px inside a 410px backdrop and left the grid showing down one side.
	   `100%` follows the box it actually sits in. */
	max-width: 100%;
	max-height: 92vh;
	overflow-y: auto;
	-webkit-overflow-scrolling: touch;
	background: var(--ivory);
	border-radius: 18px 18px 0 0;
	box-shadow: 0 -18px 50px -20px rgba(28, 26, 23, .5);
	transform: translateY(100%);
	transition: transform .3s var(--ease);
}

.qv-modal.is-open .qv-modal__dialog { transform: none; }
.qv-modal__dialog:focus { outline: none; }

.qv-modal__close {
	position: absolute;
	top: 12px;
	right: 12px;
	z-index: 3;
	width: 40px;
	height: 40px;
	border-radius: 50%;
	border: 1px solid var(--line);
	background: rgba(255, 255, 255, .94);
	-webkit-backdrop-filter: blur(6px);
	backdrop-filter: blur(6px);
	color: var(--ink);
	display: flex;
	align-items: center;
	justify-content: center;
	cursor: pointer;
	transition: .2s var(--ease);
}

.qv-modal__close svg { width: 16px; height: 16px; fill: none; stroke: currentColor; stroke-width: 2; }
.qv-modal__close:hover { background: var(--ink); color: var(--ivory); border-color: var(--ink); }
.qv-modal__close:focus-visible { outline: 2px solid var(--gold-cta); outline-offset: 2px; }

.qv-modal__loading,
.qv-modal__error {
	padding: 64px 24px;
	text-align: center;
	font-family: "DM Sans", sans-serif;
	color: var(--muted);
	/* Reserves roughly the height the panel will occupy, so the sheet does not
	   visibly jump when the content lands. */
	min-height: 320px;
	display: flex;
	align-items: center;
	justify-content: center;
}

.qv-modal__error { color: var(--garnet); }

/* ------------------------------------------------------------------ panel */

.qv {
	display: grid;
	grid-template-columns: minmax(0, 1fr);
	gap: 18px;
	padding: 18px 18px 26px;
	min-width: 0;
}

/* Without these a long product title, a wide price row or a row of variation
   chips sets the grid's floor and the whole sheet grows past the viewport. */
.qv > *,
.qv__info > * { min-width: 0; }

.qv__media { position: relative; min-width: 0; }

.qv__stage {
	position: relative;
	aspect-ratio: 1 / 1;
	background: #fff;
	border: 1px solid var(--line);
	border-radius: 14px;
	overflow: hidden;
	/* Centres the photo in the box. `height:100%` on the image alone does not
	   survive the global image rules, so a 358x169 key rendered 352x166 pinned
	   to the top of a 304px stage with a slab of white beneath it. Flex
	   centring is independent of whatever height the image ends up with. */
	display: flex;
	align-items: center;
	justify-content: center;
}

/* `contain`, not `cover`.
   The product card crops to a square on purpose — a uniform grid reads better
   than ragged tiles. A Quick View panel is the opposite job: it is the place a
   customer inspects the thing before buying it, and cropping is exactly what
   they opened it to escape. Tall pieces like the carved waka sterns lost their
   top and base to a square crop. The stage keeps its 1:1 box and white ground,
   so letterboxing is invisible against it. */
.qv__img {
	width: auto;
	height: auto;
	max-width: 100%;
	max-height: 100%;
	object-fit: contain;
	display: block;
}

/* ------------------------------------------------------------- lens zoom */

/* A magnifier that follows the mouse over the photograph.

   Sized, positioned and filled entirely by quick-view.js — everything here is
   the look of the thing, so the two do not both hold the geometry. It is moved
   with a transform rather than top/left so a pointer crossing the stage does
   not lay out the panel on every frame.

   The circle is clipped by the stage's own overflow at the edges of the image,
   which is correct: it is a lens over the picture, not a floating window that
   drifts onto the price beside it. */
.qv__lens {
	position: absolute;
	top: 0;
	left: 0;
	width: 160px;
	height: 160px;
	border-radius: 50%;
	/* The white ground shows through a photograph's own background, so the
	   lens needs an edge or it reads as a smudge rather than an instrument. */
	border: 1px solid var(--line);
	box-shadow: 0 6px 22px rgba(28, 26, 23, .18);
	background-color: #fff;
	background-repeat: no-repeat;
	/* Never takes a pointer event: it sits under the cursor by definition, and
	   swallowing the move would make it chase itself off the image. */
	pointer-events: none;
	opacity: 0;
	transition: opacity .16s var(--ease);
	will-change: transform;
	z-index: 3;
}

.qv__lens.is-on { opacity: 1; }

/* Crosshair only while the lens is live, so the cursor never promises a
   magnifier on a device or an image that has none. */
.qv__stage--zoomable { cursor: crosshair; }

/* Touch and coarse pointers: absent, not merely unused.

   The JS is already gated on a fine pointer that can hover, but a hybrid
   device can change its answer mid-session — a tablet whose keyboard was
   attached when the panel opened and removed while it is on screen. This is
   the backstop, and it costs nothing. */
@media (hover: none), (pointer: coarse) {
	.qv__lens { display: none; }
	.qv__stage--zoomable { cursor: default; }
}

/* Matches the card's offer badge — this one only ever shows a discount. */
.qv__badge {
	position: absolute;
	top: 10px;
	left: 10px;
	z-index: 2;
	background: var(--garnet);
	color: #fff;
	font-family: "DM Sans", sans-serif;
	font-weight: 700;
	font-size: .64rem;
	letter-spacing: .04em;
	padding: 4px 9px;
	border-radius: 6px;
}

.qv__thumbs {
	list-style: none;
	margin: 10px 0 0;
	padding: 0;
	display: flex;
	gap: 8px;
	overflow-x: auto;
	scrollbar-width: none;
}

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

.qv__thumb {
	width: 56px;
	height: 56px;
	padding: 0;
	border: 1px solid var(--line);
	border-radius: 10px;
	overflow: hidden;
	background: #fff;
	cursor: pointer;
	transition: border-color .2s var(--ease);
}

.qv__thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.qv__thumb.is-active { border-color: var(--gold-cta); }
.qv__thumb:focus-visible { outline: 2px solid var(--gold-cta); outline-offset: 2px; }

/* ------------------------------------------------------------------- info */

.qv__info { display: flex; flex-direction: column; gap: 10px; }

.qv__brand {
	font-family: "DM Sans", sans-serif;
	font-size: .68rem;
	font-weight: 700;
	letter-spacing: .1em;
	text-transform: uppercase;
	color: var(--muted);
}

.qv__title {
	font-family: "Cormorant Garamond", serif;
	font-size: clamp(1.25rem, 4vw, 1.6rem);
	font-weight: 600;
	line-height: 1.3;
	color: var(--ink);
	margin: 0;
	/*
	 * No clamp, no fixed height, no overflow clipping.
	 *
	 * This was a two-line `-webkit-box` clamp with `overflow: hidden`, meant to
	 * stop a long title pushing the buy row below the fold of the sheet. It cost
	 * more than it saved. Cormorant is a serif with tall ascenders, and at
	 * line-height 1.25 the clipping box cut the tops off the first line — so
	 * "10ct Yellow Gold 1/4ct Radiant Cluster…" rendered with its caps shaved.
	 * It also silently truncated the third line of genuinely long names, hiding
	 * the carat weight or metal that distinguishes one listing from the next.
	 *
	 * The title wraps naturally now. The sheet already scrolls (max-height with
	 * overflow-y: auto), which is the right way to handle overflow in a panel:
	 * reachable, not deleted.
	 */
	overflow-wrap: anywhere;
}

/* Original price then current price, laid out by stonex_price_html().
   `baseline` rather than `center` so the two figures sit on the same line of
   type despite their different sizes, and `wrap` so a long pair drops cleanly
   at 320px instead of overflowing — it wraps onto a second line in the same
   order, never reversed, because this is a plain row with no `order` anywhere. */
.qv__price {
	display: flex;
	align-items: baseline;
	flex-wrap: wrap;
	gap: 8px;
	font-family: "DM Sans", sans-serif;
	font-size: 1.15rem;
	font-weight: 700;
	color: var(--ink);
}

.qv__now { font-weight: 700; color: var(--ink); }
.qv__was { font-weight: 400; font-size: .88rem; color: var(--muted); text-decoration: line-through; }
/* Equal-width digits stop the two figures jittering against each other. */
.qv__now, .qv__was { font-variant-numeric: tabular-nums; }

/* One price at a time. Before a variation is chosen the headline shows the
   parent's range; once WooCommerce resolves one, its exact price appears inside
   the form and the range would be a second, contradictory-looking number. */
.qv--chosen .qv__price { display: none; }
.qv__price del { color: var(--muted); font-weight: 400; font-size: .88rem; margin-left: 6px; }
.qv__price ins { text-decoration: none; }

.qv__stock {
	margin: 0;
	font-family: "DM Sans", sans-serif;
	font-size: .78rem;
	font-weight: 600;
	letter-spacing: .02em;
}

.qv__stock--in { color: var(--pounamu); }
.qv__stock--out { color: var(--garnet); }

.qv__short {
	font-family: "Inter", sans-serif;
	font-size: .84rem;
	line-height: 1.6;
	color: var(--muted);
	/* Quick View is for a quick decision. A long description belongs on the
	   product page, and the "View Full Details" link is right below it. */
	max-height: 5.2em;
	overflow: hidden;
}

.qv__short p { margin: 0 0 .5em; }

/* ---------------------------------------------------------------- actions */

.qv__buy { display: flex; align-items: center; gap: 10px; margin-top: 4px; }
.qv__qty { flex: none; }
.qv__atc { flex: 1; min-height: 48px; display: inline-flex; align-items: center; justify-content: center; gap: 8px; text-decoration: none; }
.qv__atc svg { width: 16px; height: 16px; fill: none; stroke: currentColor; stroke-width: 1.9; }
.qv__atc[disabled] { opacity: .45; cursor: not-allowed; }

/* `configure` and `notify` have no stepper beside them, so the button owns the
   full width rather than sitting in a half-empty row.
   `flex: none` is load-bearing: .qv__atc carries `flex: 1` for the horizontal
   buy row, but here its parent is the column-flex .qv__info, where `flex: 1`
   means "grow to fill the remaining height" — which stretched Personalise & Buy
   to 236px tall on a phone. */
.qv__info > .qv__atc { width: 100%; flex: none; margin-top: 4px; }

.qv__full {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	gap: 6px;
	margin-top: 2px;
	padding: 10px 4px;
	font-family: "DM Sans", sans-serif;
	font-size: .76rem;
	font-weight: 700;
	letter-spacing: .06em;
	text-transform: uppercase;
	color: var(--ink);
	text-decoration: none;
	border-bottom: 1px solid transparent;
	transition: .2s var(--ease);
}

.qv__full svg { width: 13px; height: 13px; fill: none; stroke: currentColor; stroke-width: 2; }
.qv__full:hover { color: var(--gold-cta); }
.qv__full:focus-visible { outline: 2px solid var(--gold-cta); outline-offset: 2px; }

/* ------------------------------------------------------------- variations */

/* The native selects stay in the DOM because wc-add-to-cart-variation.js reads
   and writes them; the chips built by variations.js are what the customer sees.
   Until the chips exist the selects remain visible, so the panel is usable if
   that script fails. */
.qv__form .stonex-has-chips table.variations,
.qv__form.stonex-has-chips table.variations { display: none; }

.qv__form { min-width: 0; }
.qv__form table.variations { width: 100%; border: 0; margin: 0 0 4px; table-layout: fixed; }

/* Chips wrap instead of forcing the sheet wider. */
.qv__form .pdp-opt__row { flex-wrap: wrap; }
.qv__form table.variations td { padding: 4px 0; border: 0; display: block; }
.qv__form table.variations select {
	width: 100%;
	min-height: 44px;
	padding: 0 12px;
	border: 1px solid var(--line);
	border-radius: 10px;
	background: #fff;
	font-family: "DM Sans", sans-serif;
	font-size: .85rem;
	color: var(--ink);
}

/* stonex.css hides .woocommerce-variation-price globally, because the product
   page shows the headline price above the form and WooCommerce's duplicate
   below it is redundant there. In this panel that element is the ONLY place the
   chosen variation's price appears, so choosing a size would otherwise leave the
   parent's price range on screen. Explicitly re-shown rather than relying on
   specificity — the rule that hides it sets `display`, so a rule that only sets
   font-weight would lose. */
.qv__form .woocommerce-variation-price { display: block; font-family: "DM Sans", sans-serif; font-weight: 700; margin-bottom: 8px; }
.qv__form .woocommerce-variation-price .price { font-size: 1.05rem; color: var(--ink); }
.qv__form .woocommerce-variation-availability { font-family: "DM Sans", sans-serif; font-size: .78rem; color: var(--muted); }
.qv__form .reset_variations { font-size: .74rem; color: var(--muted); }

.qv__form--degraded .qv__buy { display: none; }

/* ------------------------------------------------------------- desktop */

@media (min-width: 781px) {
	.qv-modal { align-items: center; padding: 24px; }

	.qv-modal__dialog {
		max-width: 880px;
		max-height: 86vh;
		border-radius: 18px;
		transform: translateY(16px) scale(.98);
		opacity: 0;
		transition: transform .28s var(--ease), opacity .22s var(--ease);
		box-shadow: 0 30px 70px -30px rgba(28, 26, 23, .6);
	}

	.qv-modal.is-open .qv-modal__dialog { transform: none; opacity: 1; }

	.qv {
		grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
		gap: 28px;
		padding: 28px;
		align-items: start;
	}

	/*
	 * Keep the title clear of the close button. On desktop the info column
	 * starts at the dialog's right half, directly beneath the close control at
	 * top:12px/right:12px — so a long first line ran underneath it. Reserving
	 * the gutter is more robust than nudging the button, which would then float
	 * over the image on mobile where the layout stacks.
	 */
	.qv__title { padding-right: 48px; }
	.qv__info { gap: 12px; }
}

/* --------------------------------------------------------- small phones */

/* On a phone the sheet is a decision surface, not a gallery. Capping the image
   keeps the price, stock and buy row above the fold of the sheet; the full-size
   photography is one tap away on the product page. */
@media (max-width: 780px) {
	/* 44px on touch. Close is the control a customer reaches for when they have
	   decided against a product, and a miss re-opens the thing they are trying
	   to dismiss — the most annoying possible failure for the least valuable
	   interaction. The desktop 40px is fine for a pointer. */
	.qv-modal__close { width: 44px; height: 44px; }

	/* The stage hugs the photo rather than holding a fixed box: this catalogue
	   ranges from a 2.12-ratio key to a 0.75-ratio carving, and any single
	   height leaves one of them swimming in white on the shortest screens. */
	.qv__stage { aspect-ratio: auto; height: auto; padding: 8px; }
	.qv__stage .qv__img { max-height: 42vh; }

	/* No magnifier at phone width, whatever the pointer reports. The sheet is
	   a decision surface here and the image is capped at 42vh; a 160px circle
	   over it would cover most of the photograph it is meant to reveal. */
	.qv__lens { display: none; }
}

@media (max-width: 380px) {
	.qv { padding: 14px 14px 20px; gap: 14px; }
	.qv__thumb { width: 48px; height: 48px; }
	.qv__buy { gap: 8px; }
	/* At 320px a stepper and a full Add to Cart cannot share a row without the
	   button's label wrapping, so they stack. */
	.qv__buy { flex-wrap: wrap; }
	.qv__atc { flex: 1 0 100%; }
}

/* Honour a reduced-motion preference: the sheet appears rather than slides. */
@media (prefers-reduced-motion: reduce) {
	.qv-modal__scrim,
	.qv-modal__dialog { transition: none; }
	.qv-modal__dialog { transform: none; opacity: 1; }
}
