/* ==========================================================================
   Product page — "ZDESIGNER Produit"
   Loaded only on single products. Card styles come from shop.css.
   ========================================================================== */

.zd-defs {
	position: absolute;
	width: 0;
	height: 0;
	overflow: hidden;
}

.zd-star {
	fill: url(#zdGold);
	stroke: #b8892c;
	stroke-width: 0.9;
}

.zd-star--empty {
	fill: none;
	stroke: #C9C5BC;
}

.zd-stars {
	display: flex;
	gap: 3px;
}

.zd-crumbs--pdp {
	padding: 6px var(--gutter) 22px;
}

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

/* The phone's overlay bar; see the mobile block for everything about it. */
.zd-pdp__bar,
.zd-pdp__size-row,
.zd-pdp__size-close {
	display: none;
}

/*
 * The wrapper around quantity and the two actions exists so a phone can pin
 * them together. Here it is nothing at all: its children lay themselves out
 * against the panel exactly as they did before it was introduced.
 */
.zd-pdp__buybar {
	display: contents;
}

/*
 * minmax(0, …), not plain fr.
 *
 * An `fr` track carries an automatic minimum: it refuses to be narrower than
 * the widest thing inside it. The colour row on a phone is one line that slides
 * however many colours there are, so its min-content width is the whole row —
 * and a track that will not go below that stretches the grid past the viewport.
 * The row then cannot scroll, because nothing is constraining it; the page
 * scrolls sideways instead, which is the wrong thing moving.
 *
 * minmax(0, …) lets the track be as narrow as the screen, which is what hands
 * the overflow back to the row where it belongs.
 */
.zd-pdp {
	display: grid;
	grid-template-columns: minmax(0, 65fr) minmax(0, 35fr);
	align-items: stretch;
}

/* ------------------------------------------------------------ gallery --- */

.zd-pdp__gallery {
	display: grid;
	grid-template-columns: repeat(2, 1fr);
	align-items: stretch;
	align-self: stretch;
	height: 100%;
}

.zd-pdp__gallery--single,
.zd-pdp__gallery--empty {
	grid-template-columns: 1fr;
}

/* A computer keeps the lead shot and the reel side by side. */
.zd-pdp__track {
	display: contents;
}

.zd-pdp__dashes {
	display: none;
}

.zd-pdp__gallery--empty {
	min-height: 43.4vw;
	background: #F7F5F0;
}

.zd-pdp__lead,
.zd-pdp__reel-wrap {
	position: relative;
	height: 100%;
	min-height: 43.4vw;
	background: #F7F5F0;
}

.zd-pdp__zoom {
	position: absolute;
	inset: 0;
	display: block;
	width: 100%;
	height: 100%;
	padding: 0;
	border: none;
	background: #F7F5F0;
	overflow: hidden;
	cursor: zoom-in;
}

.zd-pdp__lead-img {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	object-fit: cover;
	object-position: 50% 32%;
}

.zd-pdp__reel {
	position: absolute;
	inset: 0;
	overflow-y: auto;
	display: flex;
	flex-direction: column;
	background: #F7F5F0;
	scrollbar-width: none;
	scroll-snap-type: y mandatory;
	scroll-behavior: smooth;
}

.zd-pdp__reel::-webkit-scrollbar {
	display: none;
}

.zd-pdp__shot {
	position: relative;
	display: block;
	width: 100%;
	flex: 0 0 100%;
	height: 100%;
	scroll-snap-align: start;
	padding: 0;
	border: none;
	background-color: #F7F5F0;
	background-size: cover;
	background-position: 50% 30%;
	background-repeat: no-repeat;
	overflow: hidden;
	cursor: zoom-in;
}

.zd-pdp__rail,
.zd-lb__rail {
	position: absolute;
	inset-inline-end: 0;
	top: 0;
	bottom: 0;
	width: 3px;
	background: var(--rule);
	pointer-events: none;
}

.zd-pdp__rail-thumb,
.zd-lb__rail-thumb {
	position: absolute;
	inset-inline: 0;
	top: 0;
	background: #1A1A1A;
	transition: top 0.18s ease;
}

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

.zd-pdp__panel {
	position: sticky;
	top: 180px;
	align-self: start;
	display: flex;
	flex-direction: column;
	gap: 22px;
	padding: 4px 34px 0;

	/* The same automatic minimum as the track above; see .zd-pdp. */
	min-width: 0;
}

.zd-pdp__head {
	display: flex;
	flex-direction: column;
	gap: 14px;
}

.zd-pdp__title {
	margin: 0;
	font-family: var(--font-display);
	font-weight: 500;
	font-size: clamp(15px, 1.5vw, 20px);
	line-height: 1.4;
	letter-spacing: 0.1em;
	text-transform: uppercase;
}

.zd--ar .zd-pdp__title,
.zd--ar .zd-pdp__h2,
.zd--ar .zd-benefit__title,
.zd--ar .zd-review__title,
.zd--ar .zd-ghost-btn,
.zd--ar .zd-pdp__add,
.zd--ar .zd-pdp__buy,
.zd--ar .zd-pdp__badge {
	text-transform: none;
	letter-spacing: 0.02em;
}

.zd-pdp__rating {
	display: flex;
	align-items: center;
	gap: 8px;
}

.zd-pdp__rating-text {
	font-size: 11.5px;
	color: var(--muted);
}

.zd-pdp__pricing {
	display: flex;
	flex-direction: column;
	gap: 6px;
}

.zd-pdp__price-row {
	display: flex;
	align-items: baseline;
	gap: 12px;
	flex-wrap: wrap;
}

.zd-pdp__price {
	font-size: 22px;
	white-space: nowrap;
}

.zd-pdp__price.is-sale {
	color: var(--sale);
}

.zd-pdp__old {
	font-size: 13px;
	color: var(--muted);
	text-decoration: line-through;
	white-space: nowrap;
}

.zd-pdp__badge {
	background: var(--accent);
	color: #FFFFFF;
	font-size: 10px;
	font-weight: 500;
	letter-spacing: 0.1em;
	padding: 4px 7px;
	white-space: nowrap;
}

.zd-pdp__saving {
	font-size: 11.5px;
	letter-spacing: 0.02em;
	color: var(--muted);
}

.zd-pdp__block {
	display: flex;
	flex-direction: column;
	gap: 14px;
	min-width: 0;
}

.zd-pdp__colour-line {
	display: flex;
	align-items: center;
	gap: 9px;
	font-size: 12.5px;
	color: var(--muted);
}

.zd-pdp__colour-name {
	color: var(--ink);
}

.zd-pdp__copy {
	background: none;
	border: none;
	padding: 0;
	cursor: pointer;
	line-height: 0;
}

.zd-pdp__copy svg {
	width: 13px;
	height: 13px;
	fill: none;
	stroke: var(--muted);
	stroke-width: 1.2;
}

.zd-pdp__swatches {
	display: flex;
	gap: 10px;
	flex-wrap: wrap;
}

.zd-pdp__swatch {
	width: 58px;
	height: 74px;
	background-color: #F7F5F0;
	background-size: cover;
	background-position: 50% 25%;
	background-repeat: no-repeat;
	border: 1px solid var(--rule);
}

.zd-pdp__swatch.is-current {
	border-color: #1A1A1A;
}

/* --------------------------------------------------------------- buy --- */

.zd-buy {
	display: flex;
	flex-direction: column;
	gap: 22px;
	margin: 0;
}

.zd-pdp__size-head {
	display: flex;
	align-items: baseline;
	justify-content: space-between;
	gap: 16px;
	max-width: 400px;
	font-size: 12.5px;
}

.zd-pdp__guide {
	font-size: 12px;
	color: var(--muted);
	text-decoration: underline;
	text-underline-offset: 3px;
}

.zd-sizes {
	display: grid;
	grid-template-columns: repeat(4, 1fr);
	gap: 9px;
	max-width: 400px;
}

.zd-size {
	position: relative;
	height: 42px;
	background: transparent;
	border: 1px solid var(--rule);
	font-family: var(--font-body);
	font-size: 12.5px;
	font-weight: 400;
	color: var(--ink);
	cursor: pointer;
	overflow: hidden;
}

.zd-size[aria-pressed="true"] {
	background: #1A1A1A;
	border-color: #1A1A1A;
	color: #FFFFFF;
	font-weight: 500;
}

.zd-size.is-out {
	border-color: #F0EEE9;
	color: var(--rule-strong);
	cursor: not-allowed;
}

.zd-size__out,
.zd-pdp__barinfo {
	display: none;
}

.zd-size:not(.is-out) .zd-size__strike,
.zd-size:not(.is-out) .zd-size__out {
	display: none;
}

.zd-size__strike {
	position: absolute;
	inset-inline: -6px;
	top: 50%;
	height: 1px;
	background: var(--rule-strong);
	transform: rotate(-14deg);
}

/* ------------------------------------------------------------- colours --- */

/*
 * The colour picker reads as a sibling of the size strip rather than a second
 * control vocabulary: same height, same hairline, same square corners. What it
 * adds is the chip, because a colour is the one attribute worth showing rather
 * than naming.
 */
.zd-colours {
	display: flex;
	flex-wrap: wrap;
	gap: 9px;
	max-width: 400px;
}

.zd-colour {
	display: inline-flex;
	align-items: center;
	gap: 9px;
	height: 42px;
	padding: 0 14px;
	background: transparent;
	border: 1px solid var(--rule);
	font-family: var(--font-body);
	font-size: 12.5px;
	font-weight: 400;
	color: var(--ink);
	cursor: pointer;
}

.zd-colour[aria-pressed="true"] {
	border-color: #1A1A1A;
	background: #F7F5F0;
	font-weight: 500;
}

/*
 * Muted, not erased. --rule-strong on paper is about 1.6:1, which is a border
 * colour rather than a text colour: it left the name of a sold-out piece barely
 * readable. --muted carries the difference and still passes as text.
 */
.zd-colour.is-out {
	border-color: #F0EEE9;
	color: var(--muted);
	cursor: not-allowed;
}

.zd-colour__chip {
	flex: 0 0 auto;
	width: 14px;
	height: 14px;
	/* A pale chip would vanish into the paper without its own edge. */
	box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.16);
}

/*
 * A colour that is sold out keeps its colour.
 *
 * Fading the chip was the obvious thing and the wrong one: a washed-out swatch
 * does not read as "sold out", it reads as a paler shade — so a customer looking
 * at eleven faded chips is being told the wrong colour eleven times, and the one
 * colour still in stock looks merely darker rather than available.
 *
 * So the chip stays true and is struck through instead, the same diagonal the
 * sizes already use for the same thing. The white line under it keeps the strike
 * legible on a dark chip as well as a pale one.
 */
.zd-colour.is-out .zd-colour__chip {
	position: relative;
	overflow: hidden;
}

.zd-colour.is-out .zd-colour__chip::after {
	content: "";
	position: absolute;
	inset-inline: -25%;
	top: 50%;
	height: 1px;
	background: rgba(26, 26, 26, 0.55);
	box-shadow: 0 1px 0 rgba(255, 255, 255, 0.7);
	transform: rotate(-20deg);
}

/* The name says it too, for anyone who cannot pick the strike out of a chip. */
.zd-colour.is-out .zd-colour__name {
	text-decoration: line-through;
	text-decoration-thickness: 1px;
	text-underline-offset: 2px;
}

.zd-pdp__block--qty {
	gap: 12px;
	max-width: 520px;
}

.zd-pdp__label {
	font-size: 12.5px;
	color: var(--ink);
}

.zd-pdp__actions {
	display: flex;
	gap: 10px;
	max-width: 520px;
}

.zd-pdp__add,
.zd-pdp__buy {
	font-family: var(--font-body);
	font-size: 12.5px;
	font-weight: 500;
	letter-spacing: 0.14em;
	text-transform: uppercase;
	cursor: pointer;
}

.zd-pdp__add {
	flex: 1 1 auto;
	background: #1A1A1A;
	border: 1px solid #1A1A1A;
	color: #FFFFFF;
	padding: 17px 0;
}

.zd-pdp__add[disabled],
.zd-pdp__buy[disabled] {
	opacity: 0.45;
	cursor: not-allowed;
}

/*
 * Waiting for a size.
 *
 * The buttons keep their shape and their weight — they are still live, and
 * pressing one again just asks again — so only the words change. What moves is
 * the size block, once, to say where the answer is given.
 */
.zd-pdp__block--size.is-asking .zd-sizes {
	animation: zd-size-ask 0.5s ease-out 1;
}

@keyframes zd-size-ask {
	0%, 100% { transform: translateX(0); }
	25%      { transform: translateX(-4px); }
	75%      { transform: translateX(4px); }
}

@media (prefers-reduced-motion: reduce) {
	.zd-pdp__block--size.is-asking .zd-sizes {
		animation: none;
	}

	.zd-pdp__block--size.is-asking .zd-size {
		border-color: var(--ink);
	}
}

.zd-pdp__buy {
	max-width: 520px;
	background: none;
	border: 1px solid #1A1A1A;
	color: var(--ink);
	padding: 16px 0;
}

.zd-pdp__fav {
	flex: 0 0 auto;
	width: 54px;
	background: none;
	border: 1px solid var(--rule);
	display: flex;
	align-items: center;
	justify-content: center;
	cursor: pointer;
}

.zd-pdp__fav svg {
	width: 18px;
	height: 18px;
	fill: none;
	stroke: var(--ink);
	stroke-width: 1.4;
}

.zd-pdp__fav[aria-pressed="true"] svg {
	fill: var(--ink);
}

.zd-pdp__ship {
	display: flex;
	align-items: center;
	gap: 9px;
	max-width: 520px;
	font-size: 11.5px;
	color: var(--muted);
}

.zd-pdp__ship svg {
	flex: 0 0 auto;
	width: 13px;
	height: 13px;
	fill: none;
	stroke: #1A1A1A;
	stroke-width: 1.2;
}

/* ---------------------------------------------------------- benefits --- */

.zd-benefits {
	display: grid;
	grid-template-columns: repeat(4, 1fr);
	gap: 1px;
	background: var(--rule);
	margin-top: clamp(40px, 5vw, 72px);
}

.zd-benefit {
	display: flex;
	align-items: center;
	gap: 14px;
	background: var(--paper);
	padding: 24px 26px;
}

.zd-benefit__num {
	flex: 0 0 auto;
	font-family: var(--font-display);
	font-size: 12px;
	letter-spacing: 0.14em;
	color: #9B9B96;
}

.zd-benefit__text {
	display: flex;
	flex-direction: column;
	gap: 4px;
}

.zd-benefit__title {
	font-size: 11.5px;
	letter-spacing: 0.12em;
	text-transform: uppercase;
}

.zd-benefit__note {
	font-size: 11.5px;
	color: var(--muted);
}

/* ----------------------------------------------------------- details --- */

.zd-details {
	display: grid;
	grid-template-columns: repeat(3, 1fr);
	gap: clamp(30px, 4vw, 64px);
	padding: clamp(46px, 5vw, 80px) var(--gutter) 0;
}

.zd-details__col {
	display: flex;
	flex-direction: column;
	gap: 16px;
	min-width: 0;
}

.zd-pdp__h2 {
	margin: 0;
	font-family: var(--font-display);
	font-weight: 500;
	font-size: 13px;
	letter-spacing: 0.16em;
	text-transform: uppercase;
}

.zd-details__body,
.zd-details__body p {
	margin: 0;
	font-size: 12.5px;
	line-height: 1.85;
	color: var(--muted);
}

.zd-details__empty {
	margin: 0;
	font-size: 12.5px;
	line-height: 1.75;
	color: var(--muted);
}

.zd-specs {
	display: flex;
	flex-direction: column;
	gap: 9px;
	padding-top: 6px;
}

.zd-spec {
	display: flex;
	justify-content: space-between;
	gap: 18px;
	border-bottom: 1px solid var(--rule);
	padding-bottom: 9px;
	font-size: 12px;
}

.zd-spec__k {
	color: var(--muted);
}

.zd-spec__v {
	color: var(--ink);
	text-align: end;
}

/* ----------------------------------------------------------- reviews --- */

.zd-score {
	display: flex;
	align-items: center;
	gap: 14px;
	flex-wrap: wrap;
}

.zd-score__num {
	font-family: var(--font-display);
	font-size: 34px;
	font-weight: 500;
	line-height: 1;
}

.zd-score__meta {
	display: flex;
	flex-direction: column;
	gap: 5px;
}

.zd-score__count {
	font-size: 12px;
	color: var(--muted);
}

.zd-bars {
	display: flex;
	flex-direction: column;
	gap: 8px;
}

.zd-bar {
	display: flex;
	align-items: center;
	gap: 12px;
	font-size: 11.5px;
	color: var(--muted);
}

.zd-bar__label {
	display: flex;
	align-items: center;
	gap: 4px;
	width: 30px;
}

.zd-bar__track {
	flex: 1 1 auto;
	height: 3px;
	background: var(--rule);
	position: relative;
}

.zd-bar__fill {
	position: absolute;
	inset-inline-start: 0;
	top: 0;
	bottom: 0;
	background: #1A1A1A;
}

.zd-bar__count {
	width: 26px;
	text-align: end;
}

.zd-reviews {
	display: flex;
	flex-direction: column;
	gap: 18px;
	padding-top: 6px;
}

.zd-review {
	display: flex;
	flex-direction: column;
	gap: 7px;
	border-top: 1px solid var(--rule);
	padding-top: 16px;
}

.zd-review__title {
	font-size: 12px;
	letter-spacing: 0.06em;
	text-transform: uppercase;
}

.zd-review__body {
	margin: 0;
	font-size: 12.5px;
	line-height: 1.7;
	color: var(--muted);
}

.zd-review__meta {
	display: flex;
	align-items: center;
	gap: 9px;
	font-size: 11px;
	color: #9B9B96;
}

.zd-review__verified {
	display: flex;
	align-items: center;
	gap: 4px;
	color: #1E7A3C;
	letter-spacing: 0.04em;
}

.zd-review__verified svg {
	width: 11px;
	height: 11px;
	fill: none;
	stroke: #1E7A3C;
	stroke-width: 1.6;
	stroke-linecap: round;
	stroke-linejoin: round;
}

.zd-ghost-btn {
	align-self: flex-start;
	margin-top: 6px;
	border: 1px solid var(--rule);
	padding: 14px 26px;
	font-size: 11.5px;
	letter-spacing: 0.14em;
	text-transform: uppercase;
}

.zd-ghost-btn:hover {
	text-decoration: none;
	border-color: #1A1A1A;
}

/* ----------------------------------------------------------- related --- */

.zd-related {
	padding: clamp(50px, 6vw, 88px) var(--gutter) clamp(40px, 5vw, 70px);
}

.zd-grid--related {
	grid-template-columns: repeat(5, 1fr);
	gap: 30px 16px;
}

/* ---------------------------------------------------------- lightbox --- */

.zd-lb {
	position: fixed;
	inset: 0;
	z-index: 80;
	display: flex;
	background: var(--paper);
}


.zd-lb__thumbs {
	flex: 0 0 auto;
	width: 150px;
	overflow-y: auto;
	padding: 26px 0 26px 26px;
	display: flex;
	flex-direction: column;
	gap: 12px;
}

.zd--rtl .zd-lb__thumbs {
	padding: 26px 26px 26px 0;
}

.zd-lb__thumb {
	display: block;
	width: 86px;
	aspect-ratio: 3 / 4;
	padding: 0;
	border: none;
	background-color: #F7F5F0;
	background-size: cover;
	background-position: 50% 25%;
	background-repeat: no-repeat;
	cursor: pointer;
	transition: width 0.18s ease, height 0.18s ease;
}

.zd-lb__thumb.is-current {
	width: 103px;
}

.zd-lb__stage {
	position: relative;
	flex: 1 1 auto;
	min-width: 0;
	overflow: hidden;
	cursor: ns-resize;
}

.zd-lb__pane {
	position: absolute;
	inset: 0;
	background-color: var(--paper);
	background-size: auto 139%;
	background-position: 50% var(--zd-pan, 50%);
	background-repeat: no-repeat;
	display: none;
}

.zd-lb__pane.is-current {
	display: block;
}

.zd-lb__close {
	position: absolute;
	inset-inline-end: 26px;
	top: 26px;
	background: none;
	border: none;
	padding: 10px;
	cursor: pointer;
	line-height: 0;
}

.zd-lb__close svg {
	width: 22px;
	height: 22px;
	fill: none;
	stroke: var(--ink);
	stroke-width: 1.3;
}

/* ------------------------------------------------------ text weight --- */

/*
 * Body text on this page runs 44% heavier than the site default — two
 * successive 20% steps.
 *
 * Scoped to the page's own containers, so the shared header and footer keep
 * the site weight. The product title and the two purchase buttons declare
 * their own weight and are therefore untouched by the inherited bump.
 *
 * 300 x 1.2 x 1.2 = 432 (Latin) and 400 x 1.2 x 1.2 = 576 (Arabic). Both
 * families ship static cuts, so the browser resolves 432 to Satoshi 500 and
 * 576 to IBM Plex Sans Arabic 600.
 */
.zd-crumbs--pdp,
.zd-pdp,
.zd-benefits,
.zd-details,
.zd-related,
.zd-lb {
	font-weight: 432;
}

.zd--ar .zd-crumbs--pdp,
.zd--ar .zd-pdp,
.zd--ar .zd-benefits,
.zd--ar .zd-details,
.zd--ar .zd-related,
.zd--ar .zd-lb {
	font-weight: 576;
}

/* Form controls do not inherit weight on their own. */
.zd-pdp .zd-qty__input,
.zd-pdp .zd-size,
.zd-details .zd-row__head {
	font-weight: inherit;
}

/*
 * Elements that already declare 500 — section headings, the discount badge,
 * the review score, the selected size — are left as they are. Satoshi has no
 * 600 cut (300/400/500/700), so a 20% bump would snap them to 700 and read as
 * a much larger change than the body copy got.
 *
 * Excluded by request, restated so the intent survives future edits.
 */
.zd-pdp__title,
.zd-pdp__add,
.zd-pdp__buy {
	font-weight: 500;
}

/* ------------------------------------------------------- breakpoints --- */

@media (max-width: 1199px) {
	.zd-details {
		grid-template-columns: repeat(2, 1fr);
	}

	.zd-grid--related {
		grid-template-columns: repeat(3, 1fr);
	}
}

@media (max-width: 899px) {
	/*
	 * The gallery runs to all four edges and the site's own chrome stands
	 * down: on a product page the picture is the page. What is left is the
	 * bar laid over the top of it and the buy button pinned along the foot.
	 */
	body.zd-product .zd-header,
	body.zd-product .zd-tabbar {
		display: none;
	}

	.zd-pdp__bar {
		position: fixed;
		inset-inline: 0;
		top: 0;
		z-index: 55;
		display: flex;
		align-items: center;
		justify-content: space-between;
		padding: 10px 12px;
		/* Only the buttons take taps; the gap between opens the picture. */
		pointer-events: none;
		background: transparent;
		border-bottom: 1px solid transparent;
		transition: background-color 0.2s ease, border-color 0.2s ease;
	}

	/*
	 * Past the picture the glyphs would be floating over the page's own text,
	 * so the bar takes a ground of its own. The script sets this at the point
	 * the gallery leaves the top of the screen; see product.js.
	 */
	.zd-pdp__bar.is-solid {
		background: var(--paper);
		border-bottom-color: var(--rule);
	}

	.zd-pdp__bar-btn {
		pointer-events: auto;
		display: flex;
		align-items: center;
		justify-content: center;
		width: 40px;
		height: 40px;
		padding: 0;
		border: none;
		background: none;
		color: var(--ink);
		cursor: pointer;
	}

	.zd-pdp__bar-btn:hover {
		text-decoration: none;
	}

	.zd-pdp__bar-end {
		pointer-events: none;
		display: flex;
		align-items: center;
		gap: 2px;
	}

	/* Bare glyphs: nothing behind them and nothing under them. */
	.zd-pdp__bar-btn svg {
		width: 22px;
		height: 22px;
		fill: none;
		stroke: currentColor;
		stroke-width: 1.6;
		stroke-linecap: round;
		stroke-linejoin: round;
	}

	/* The heart fills in when the piece is saved, as the shop's others do. */
	.zd-pdp__bar-fav[aria-pressed="true"] svg {
		fill: currentColor;
	}

	/*
	 * The trail reads at the foot of the page, after the piece itself: with
	 * the picture starting at the very top there is nowhere above it to put a
	 * line of small grey text. Flex order rather than new markup.
	 */
	.zd-main {
		display: flex;
		flex-direction: column;
	}

	/*
	 * Room at the foot for the buy bar.
	 *
	 * On the footer rather than on the main, because the footer is outside the
	 * main and is the last thing on the page — padding on the main left the
	 * copyright and the language under the bar. Scoped to the product page, so
	 * every other footer keeps the spacing it has.
	 *
	 * The height is measured and published as --zd-buybar-h by product.js: the
	 * bar is two rows, and how tall those rows are depends on the screen.
	 */
	body.zd-product .zd-footer {
		padding-bottom: calc(30px + var(--zd-buybar-h, 120px) + env(safe-area-inset-bottom, 0px));
	}

	/*
	 * No side padding: the trail is held off the edges by a margin, so that
	 * the crumb scrolling away is clipped a gutter in rather than against the
	 * side of the phone — see .zd-crumbs in shop.css. Padding here as well put
	 * the first crumb two gutters from the edge.
	 */
	.zd-crumbs--pdp {
		order: 6;
		padding: 24px 0 0;
	}

	/*
	 * A notice that stays on the page — an error, or a standing condition —
	 * belongs under the piece, not above it. WooCommerce prints its wrapper
	 * before the product, which on a phone put it in the strip the picture
	 * starts at and under the bar laid over that picture. Ordered after, it
	 * lands beneath the buy panel where it is read rather than sat on.
	 */
	/*
	 * The order of the page on a phone.
	 *
	 * The markup runs pdp · notices · benefits · details · related · trail,
	 * and it is read pdp · notices · details · benefits · related · trail: the
	 * four promises belong under the description rather than between it and
	 * the piece, where they came between a shopper and what they were reading.
	 * Stated for each, because a flex row with some children ordered and the
	 * rest left at nought falls back to the order in the markup.
	 */
	body.zd-product .zd-pdp {
		order: -1;
	}

	body.zd-product .woocommerce-notices-wrapper:not(:empty) {
		order: 0;
		padding: 18px var(--gutter) 0;
	}

	/*
	 * The description and the reviews are two columns of one block, so the
	 * promises cannot be put between them while that block is a box. On a
	 * phone it stops being one: `display: contents` hands its three columns
	 * straight to the page, and they take their own gutter and spacing, which
	 * the block was giving them through its padding and its grid gap.
	 */
	body.zd-product .zd-details {
		display: contents;
	}

	body.zd-product .zd-details__col {
		padding-inline: var(--gutter);
	}

	body.zd-product .zd-details__col:nth-child(1) {
		order: 1;
		padding-top: 46px;
	}

	body.zd-product .zd-benefits {
		order: 2;
	}

	/* The reviews, then the questions. */
	body.zd-product .zd-details__col:nth-child(2) {
		order: 3;
		padding-top: 30px;
		/* Clear of the bar laid over the picture when jumped to by its link. */
		scroll-margin-top: 70px;
	}

	body.zd-product .zd-details__col:nth-child(3) {
		order: 4;
		padding-top: 30px;
	}

	body.zd-product .zd-related {
		order: 5;
	}

	.zd-pdp {
		grid-template-columns: minmax(0, 1fr);
	}

	.zd-pdp__gallery {
		grid-template-columns: 1fr;
	}

	/*
	 * Every photo in one row the shopper swipes through, the browser's own
	 * scroller with snap points: the next photo follows the finger and it
	 * settles on a frame. A tap still opens the zoom; a swipe never does.
	 */
	.zd-pdp__gallery {
		position: relative;
		display: block;
	}

	.zd-pdp__track {
		display: flex;
		overflow-x: auto;
		overflow-y: hidden;
		scroll-snap-type: x mandatory;
		overscroll-behavior-x: contain;
		scrollbar-width: none;
	}

	.zd-pdp__track::-webkit-scrollbar {
		display: none;
	}

	.zd-pdp__lead {
		flex: 0 0 100%;
		scroll-snap-align: start;
		scroll-snap-stop: always;
	}

	.zd-pdp__reel-wrap,
	.zd-pdp__reel {
		display: contents;
	}

	.zd-pdp__rail {
		display: none;
	}

	.zd-pdp__shot {
		flex: 0 0 100%;
		height: auto;
		min-height: 118vw;
		scroll-snap-align: start;
		scroll-snap-stop: always;
	}

	.zd-pdp__lead,
	.zd-pdp__gallery--empty {
		min-height: 118vw;
	}

	/* Where she is in the set: short dashes near the foot, the current one dark. */
	.zd-pdp__dashes {
		position: absolute;
		inset-inline: 0;
		bottom: 48px;
		z-index: 2;
		display: flex;
		justify-content: center;
		/* One unbroken line: the pieces touch. */
		gap: 0;
		pointer-events: none;
	}

	.zd-pdp__dash {
		width: 14px;
		height: 2px;
		background: rgba(26, 26, 26, 0.22);
		transition: background-color 0.18s ease;
	}

	.zd-pdp__dash.is-current {
		background: #1A1A1A;
	}

	/*
	 * A column, so the colours can be ordered to the top of it — directly
	 * under the picture, which is where they read.
	 */
	.zd-pdp__panel {
		position: static;
		display: flex;
		flex-direction: column;
		padding: 0;
	}

	.zd-pdp__panel > * {
		padding-inline: var(--gutter);
	}

	/*
	 * The colours come first, directly under the picture.
	 *
	 * The picker lives inside the buy form, two levels down, so the form is
	 * made nothing at all here and hands its children straight to the panel —
	 * which is what lets a single `order` reach the colours. The form still
	 * posts exactly as it did; display has no bearing on that.
	 */
	.zd-buy {
		display: contents;
	}

	/*
	 * The panel's own 22px gap is all the air wanted under the tiles; the
	 * block's padding was adding to it and standing the name off the colours
	 * by twice that.
	 */
	.zd-pdp__block--colour,
	.zd-pdp__block--colourpick {
		order: -1;
		margin: 0;
		padding: 12px 0 0;
	}

	/*
	 * The same 12px when there is nothing to choose.
	 *
	 * That padding is what holds the first thing in the panel off the foot of
	 * the photograph, and the colour row is usually that thing. A piece made
	 * in one colour has no row, so its name sat flush against the picture with
	 * nothing between them at all. Here the name takes the space instead.
	 */
	.zd-pdp__panel:not(:has( .zd-pdp__block--colour )):not(:has( .zd-pdp__block--colourpick )) .zd-pdp__head {
		padding-top: 12px;
	}

	/*
	 * No heading over the colours: a row of the colours is already the whole
	 * of what it would say. The group keeps its aria-label, so the picker is
	 * still named for anyone who cannot see that.
	 */
	.zd-pdp__block--colourpick .zd-pdp__label {
		display: none;
	}

	/*
	 * One line however many colours there are, sliding rather than wrapping:
	 * a second row of them would push the name and the price off the screen.
	 */
	.zd-colours {
		flex-wrap: nowrap;
		gap: 8px;
		max-width: none;
		min-width: 0;
		overflow-x: auto;
		overscroll-behavior-x: contain;
		scrollbar-width: none;
		padding-inline: var(--gutter);
	}

	.zd-colours::-webkit-scrollbar {
		display: none;
	}

	/* A tile of the colour itself, with its name under it. */
	.zd-colour {
		flex: 0 0 auto;
		flex-direction: column;
		align-items: stretch;
		gap: 0;
		width: 66px;
		height: auto;
		padding: 0;
		border: 1px solid transparent;
	}

	.zd-colour[aria-pressed="true"] {
		border-color: var(--ink);
		background: transparent;
	}

	.zd-colour__chip {
		width: 100%;
		height: 66px;
	}

	.zd-colour__name {
		padding: 6px 4px;
		font-size: 10.5px;
		line-height: 1.3;
		text-align: center;
		color: var(--muted);
	}

	.zd-colour[aria-pressed="true"] .zd-colour__name {
		color: var(--ink);
	}

	/* A row that slides, so a long set of colours is not three short rows. */
	.zd-pdp__swatches {
		flex-wrap: nowrap;
		gap: 8px;
		min-width: 0;
		overflow-x: auto;
		overscroll-behavior-x: contain;
		scrollbar-width: none;
		padding-inline: var(--gutter);
	}

	.zd-pdp__swatches::-webkit-scrollbar {
		display: none;
	}

	.zd-pdp__swatch {
		flex: 0 0 auto;
		width: 66px;
		height: 82px;
	}

	.zd-pdp__swatch.is-current {
		outline: 1px solid var(--ink);
		outline-offset: 1px;
	}



	/* ------------------------------------------- the sizes, in the page --- */

	/*
	 * On a phone the sizes sit in the page, right under the colours, instead
	 * of in a sheet that rose when a buy button was pressed. Shoppers chose a
	 * colour without ever seeing whether their size existed (Clarity: hundreds
	 * of colour taps, a dozen size taps). Pressing a buy button with no size
	 * now scrolls here and asks; tapping a size then carries on with the press.
	 */
	.zd-pdp__size-row,
	.zd-pdp__size-close,
	.zd-pdp__size-scrim {
		display: none !important;
	}

	.zd-pdp__block--size {
		position: static;
		transform: none;
		visibility: visible;
		max-height: none;
		overflow: visible;
		margin: 18px 0 0;
		/* The panel's gutter, on both sides, like every other block in it. */
		padding: 0 var(--gutter);
		background: none;
		border: 0;
		scroll-margin-top: 90px;
		scroll-margin-bottom: 170px;
	}

	/*
	 * The size buttons themselves look exactly as they do on a computer: the
	 * same four-across grid, the same squares, the same hairline strike on a
	 * sold-out size. Only where the block sits is different on a phone.
	 */
	.zd-pdp__size-head {
		justify-content: space-between;
		padding-bottom: 0;
	}

	/* --------------------------------------------------- the buy bar --- */

	/*
	 * Quantity and both actions along the foot, where a thumb is.
	 *
	 * Two rows: the stepper beside Add to cart, and Buy now across the width
	 * beneath. The stepper is cut to the width of the three things it holds so
	 * the button beside it has room for its words on one line — at the
	 * desktop's 126px the label broke across two lines at 390px and three at
	 * 320px, and the bar grew by a line of screen each time.
	 */
	.zd-pdp__buybar {
		position: fixed;
		inset-inline: 0;
		bottom: 0;
		z-index: 54;
		display: grid;
		grid-template-columns: auto 1fr;
		gap: 8px;
		padding: 8px var(--gutter) calc(8px + env(safe-area-inset-bottom, 0px));
		background: var(--paper);
		border-top: 1px solid var(--rule);
	}

	/* Price and cash on delivery, one line above the buttons. */
	.zd-pdp__barinfo {
		grid-column: 1 / -1;
		display: flex;
		align-items: baseline;
		gap: 8px;
		margin: -2px 0 0;
		font-size: 13px;
		line-height: 1.3;
		white-space: nowrap;
		overflow: hidden;
	}

	.zd-pdp__barinfo-price {
		font-weight: 600;
		font-size: 15px;
		color: var(--ink);
	}

	.zd-pdp__barinfo-price.is-sale {
		color: #B4232A;
	}

	.zd-pdp__barinfo-old {
		color: #9A968E;
		text-decoration: line-through;
		font-size: 12px;
	}

	.zd-pdp__barinfo-colour {
		display: inline-flex;
		align-items: center;
		gap: 5px;
		margin-inline-start: auto;
		color: #5B5548;
		font-size: 12px;
	}

	.zd-pdp__barinfo-colour b {
		font-weight: 600;
		color: var(--ink, #1A1A1A);
	}

	.zd-pdp__barinfo-chip {
		width: 12px;
		height: 12px;
		border-radius: 50%;
		box-shadow: inset 0 0 0 1px rgba(0,0,0,.15);
	}

	/* Everything in the bar stands the same height, so the rows read as rows. */
	.zd-qty,
	.zd-pdp__add,
	.zd-pdp__buy {
		height: 46px;
		min-height: 46px;
	}

	.zd-qty {
		width: 104px;
	}

	.zd-pdp__block--qty {
		margin: 0;
		padding: 0;
	}

	/* The word "Quantité" has no room down here; the stepper says it itself. */
	.zd-pdp__block--qty .zd-pdp__label {
		display: none;
	}

	.zd-pdp__actions {
		display: flex;
		margin: 0;
		padding: 0;
	}

	/*
	 * Tighter tracking, centred, and no wrapping: the label is one line or it
	 * is wrong, and a button that changes height moves the whole bar with it.
	 */
	.zd-pdp__add,
	.zd-pdp__buy {
		display: flex;
		align-items: center;
		justify-content: center;
		padding: 0 10px;
		letter-spacing: 0.1em;
		white-space: nowrap;
	}

	/* The heart lives in the bar over the picture on a phone. */
	.zd-pdp__actions .zd-pdp__fav {
		display: none;
	}

	.zd-pdp__buy {
		grid-column: 1 / -1;
		margin: 0;
	}

	.zd-benefits {
		grid-template-columns: 1fr 1fr;
	}

	.zd-grid--related {
		grid-template-columns: repeat(2, 1fr);
	}

	/* -------------- the lightbox: the frame, with the others on it ------ */

	/*
	 * The picture takes the whole screen and the strip of frames lies along
	 * the foot of it rather than in a band of its own below — so nothing is
	 * taken from the piece being looked at.
	 */
	.zd-lb {
		display: block;
	}

	/*
	 * The frames lie in a row that the reader scrolls, the way a set of
	 * pictures behaves everywhere else on a phone: the next one is already
	 * there at the edge of the screen, it follows the finger, and it settles
	 * on a frame when the finger lets go.
	 *
	 * This is the browser's own horizontal scroller with snap points, not a
	 * track dragged about by script. The browser then owns the follow, the
	 * momentum and the settle — and, which matters more, the painting: a row
	 * moved by transform leaves whatever was off-screen unrasterised, and the
	 * frame arriving is blank.
	 */
	.zd-lb__stage {
		position: absolute;
		inset: 0;
		display: flex;
		overflow-x: auto;
		overflow-y: hidden;
		scroll-snap-type: x mandatory;
		overscroll-behavior-x: contain;
		scrollbar-width: none;
	}

	.zd-lb__stage::-webkit-scrollbar {
		display: none;
	}

	.zd-lb__pane {
		position: relative;
		inset: auto;
		flex: 0 0 100%;
		scroll-snap-align: center;
		scroll-snap-stop: always;
		/* Every frame is in the row; the scroll decides which is on screen. */
		display: block;
		/* Filled, not fitted: the reference crops rather than letterboxes. */
		background-size: cover;
		background-position: 50% 50%;
	}

	/* The strip rides over the track rather than travelling with it. */
	.zd-lb__thumbs {
		position: fixed;
		inset-inline: 0;
		bottom: 0;
		z-index: 2;
		width: auto;
		flex-direction: row;
		/* A common floor, so the current frame grows upward rather than
		   shunting its neighbours around. */
		align-items: flex-end;
		gap: 4px;
		padding: 8px;
		overflow-x: auto;
		scrollbar-width: none;
	}

	.zd-lb__thumbs::-webkit-scrollbar {
		display: none;
	}

	.zd-lb__thumb {
		flex: 0 0 auto;
		width: 38px;
		height: 50px;
		aspect-ratio: auto;
		/* Each frame on its own paper, so a dark one still reads as a frame. */
		box-shadow: 0 0 0 1px rgba(26, 26, 26, 0.12);
	}

	/*
	 * The frame being looked at is larger, not just outlined.
	 *
	 * On a wide screen the rail already does this (86px to 103px). On a phone
	 * the frames are small enough that a 2px outline alone is easy to lose
	 * against a pale photograph, so the current one grows by about a third —
	 * enough to find at a glance, still narrow enough that six or seven fit
	 * across the screen.
	 */
	.zd-lb__thumb.is-current {
		width: 52px;
		height: 68px;
		outline: 2px solid #FFFFFF;
		outline-offset: 1px;
	}

	.zd-lb__rail {
		display: none;
	}

	/*
	 * Over the picture, so it carries its own ground: the frame behind it is
	 * whatever the photograph happens to be at that corner.
	 */
	.zd-lb__close {
		position: fixed;
		inset-inline-start: 12px;
		inset-inline-end: auto;
		top: 12px;
		z-index: 2;
		padding: 7px;
		background: rgba(26, 26, 26, 0.28);
	}

	.zd-lb__close svg {
		stroke: #FFFFFF;
		stroke-width: 1.6;
	}
}

/* ------------------------------------------------ reviews with photos --- */

.zd-review__pics {
	display: flex;
	gap: 6px;
	margin-top: 8px;
}

.zd-review__pic {
	display: block;
	width: 64px;
	height: 64px;
	object-fit: cover;
	border-radius: 6px;
}

.zd-rev-thanks {
	margin: 12px 0;
	padding: 10px 12px;
	border-radius: 8px;
	background: #EEF5EC;
	color: #2E5A27;
	font-size: 14px;
}

.zd-rev-form {
	margin-top: 14px;
}

.zd-rev-form > summary {
	list-style: none;
	cursor: pointer;
}

.zd-rev-form > summary::-webkit-details-marker {
	display: none;
}

.zd-rev-form form {
	display: grid;
	gap: 12px;
	margin-top: 14px;
}

/*
 * The anti-spam field, hidden without moving it off the page. It used to sit
 * at left: -9999px; on an Arabic (right-to-left) page that is scrollable
 * space, so the page became 10,000px wide and the phone zoomed out to fit it.
 */
.zd-rev-form__hp {
	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;
}

.zd-rev-form__field {
	display: grid;
	gap: 5px;
	font-size: 13.5px;
}

.zd-rev-form__field input[type="text"],
.zd-rev-form__field input[type="email"],
.zd-rev-form__field textarea {
	width: 100%;
	padding: 10px 12px;
	border: 1px solid var(--rule);
	border-radius: 8px;
	background: var(--paper);
	font: inherit;
	font-size: 16px;
}

.zd-rev-form__field small {
	color: #8A857C;
	font-size: 12px;
}

/* Five stars, highest first in the markup so :checked ~ lights the lower ones. */
.zd-rev-form__stars {
	display: flex;
	flex-direction: row-reverse;
	justify-content: flex-end;
	gap: 4px;
	margin: 0;
	padding: 0;
	border: 0;
}

.zd-rev-form__stars legend {
	width: 100%;
	margin-bottom: 4px;
	font-size: 13.5px;
}

.zd-rev-form__stars {
	position: relative;
}

.zd-rev-form__stars input {
	position: absolute;
	inset-inline-start: 0;
	top: 0;
	width: 1px;
	height: 1px;
	opacity: 0;
	pointer-events: none;
}

.zd-rev-form__stars label {
	font-size: 28px;
	line-height: 1;
	color: #D9D4CA;
	cursor: pointer;
}

.zd-rev-form__stars input:checked ~ label,
.zd-rev-form__stars label:hover,
.zd-rev-form__stars label:hover ~ label {
	color: #C9A227;
}

.zd-rev-form__send {
	padding: 13px 16px;
	border: 0;
	border-radius: 8px;
	background: var(--ink);
	color: var(--paper);
	font: inherit;
	font-weight: 600;
	cursor: pointer;
}

/*
 * Keep the review form inside the screen on a phone. Grid items and a
 * fieldset default to their content's minimum width, and a file input's
 * "no file chosen" text alone is wider than a 360px column; min-width: 0 and
 * border-box let everything shrink to the column instead of pushing past it.
 */
.zd-rev-form,
.zd-rev-form form,
.zd-rev-form__field,
.zd-rev-form__stars {
	min-width: 0;
	max-width: 100%;
	box-sizing: border-box;
}

.zd-rev-form form > *,
.zd-rev-form__field input,
.zd-rev-form__field textarea {
	min-width: 0;
	max-width: 100%;
	box-sizing: border-box;
}

.zd-rev-form__field input[type="file"] {
	width: 100%;
	font-size: 13px;
	overflow: hidden;
}

.zd-rev-form > summary {
	display: block;
	box-sizing: border-box;
	max-width: 100%;
	text-align: center;
}

.zd-rev-form__send {
	width: 100%;
}


/* ===================================== quick order: the buttons around it === */

/*
 * With the form on the page the buy button is the main action — it places the
 * order — so it takes the solid black, and "add to cart" steps back to the
 * outline for the shopper who wants several pieces.
 */
.zd-pdp__panel:has(.zd-qo) .zd-pdp__buy {
	background: #1A1A1A;
	border-color: #1A1A1A;
	color: #FFFFFF;
	font-weight: 600;
}

.zd-pdp__panel:has(.zd-qo) .zd-pdp__add {
	background: none;
	color: var(--ink);
}

.zd-pdp__buy[disabled] {
	opacity: 0.7;
}

