/* ═══════════════════════════════════════════════════════════════════════
   perennial — the product page.

   Two columns that behave differently: the gallery scrolls, the buy box
   stays. Everything below it is one measure of type against hairlines.

   Scoped under .pn like every other page, so Blocksy keeps cart and
   checkout until those are designed.
   ═══════════════════════════════════════════════════════════════════════ */

/* Blocksy wraps every page in #main-container and gives it `overflow: clip`,
   which makes it the containing block for sticky descendants and pins them to
   nothing: the buy box travelled 1px for every 1px of scroll on the live site
   while sticking perfectly in the local mirror, where #main-container is a
   bare div. `overflow-x: clip` keeps the horizontal clipping Blocksy wants —
   `clip` is the one value that may pair with `visible` without being coerced
   to `auto` — and `overflow-y: visible` gives the box its scrollport back.
   Scoped to this page; the other four have nothing sticky inside #main. */
.pn-product-page #main-container {
	overflow-x: clip;
	overflow-y: visible;
}

.pn-product {
	--pn-header-h: 64px;
	--pn-box-measure: 420px;
	--pn-bar-h: 64px;

	/* The page opens on the photograph, so its top air is the same measure the
	   sections below it are spaced by — 32 / 48 / 64 as the width grows —
	   rather than the flat 32px that used to sit under a breadcrumb. */
	padding-top: var(--pn-section-pad);
	padding-bottom: var(--space-9);
}

.pn-product__notices:empty { display: none; }
.pn .pn-product__notices { margin-bottom: var(--space-5); }

/* ── The buy block ─────────────────────────────────────────────────────
   Below 1024 the gallery is a swipe carousel above the box. From 1024 the
   two sit side by side and the box sticks: the photographs are what you
   scroll, the price and the button are what stays. ────────────────────── */
.pn-buy { display: block; }

@media (min-width: 1024px) {
	.pn-buy {
		display: grid;
		grid-template-columns: 1fr 1fr;
		column-gap: var(--space-8);
		/* No align-items here. The box column has to stretch to the height of
		   the gallery beside it, because that stretched column is the box's
		   containing block and therefore the distance the sticky box is
		   allowed to travel. `align-items: start` shrinks it to the height of
		   the box itself, and the box then sticks for nought pixels — which
		   looks exactly like sticky not being supported. */
	}
}

/* ── The gallery ───────────────────────────────────────────────────────
   On desktop a flex-wrap column: every third photograph full width, the
   two between it side by side. The images butt against each other with no
   gap — it is one object photographed five times, and a gutter between
   them would read as five different things. ──────────────────────────── */
.pn-buy__media { min-width: 0; }

.pn-gallery {
	display: flex;
	gap: 0;
	overflow-x: auto;
	scroll-snap-type: x mandatory;
	scroll-behavior: smooth;
	scrollbar-width: none;
	margin-inline: calc(var(--pn-gutter) * -1);
}
.pn-gallery::-webkit-scrollbar { display: none; }

.pn-gallery__item {
	flex: 0 0 100%;
	scroll-snap-align: center;
	min-width: 0;
}

.pn .pn-gallery__open {
	display: block;
	width: 100%;
	padding: 0;
	background: none;
	border: 0;
	border-radius: 0;
	box-shadow: none;
	cursor: zoom-in;
}
.pn .pn-gallery__open:hover { opacity: 1; }

.pn-gallery__counter {
	margin-top: var(--space-3);
	font-size: var(--text-caption);
	line-height: var(--lh-caption);
	letter-spacing: var(--ls-caption);
	color: var(--text-secondary);
	text-align: center;
}

@media (min-width: 1024px) {
	.pn-gallery {
		display: flex;
		flex-wrap: wrap;
		overflow: visible;
		scroll-snap-type: none;
		margin-inline: 0;
	}
	.pn-gallery__item--full { flex: 0 0 100%; }
	.pn-gallery__item--half { flex: 0 0 50%; }
	.pn .pn-gallery__open { cursor: zoom-in; }
	.pn-gallery__counter { display: none; }
}

/* ── The buy box ───────────────────────────────────────────────────────
   The grid item stretches to the row's height; the box inside it is what
   sticks, which is what lets it travel the length of the gallery. ────── */
.pn-buy__box { min-width: 0; padding-top: var(--space-6); }

@media (min-width: 1024px) {
	.pn-buy__box { padding-top: 0; }

	.pn-box {
		position: sticky;
		top: calc(var(--pn-header-h) + var(--space-5));
		max-width: var(--pn-box-measure);
	}
}

.pn .pn-box > * + * { margin-top: var(--space-5); }

.pn .pn-box__title { margin: 0; }

/* Price and per-unit price on one baseline: the second is the one that
   answers "is this the cheap one", and it only means anything beside the
   first. It is omitted, never zeroed — see pn_listing_unit_price(). */
.pn .pn-box__price {
	display: flex;
	flex-wrap: wrap;
	align-items: baseline;
	gap: var(--space-2) var(--space-4);
	margin-top: var(--space-3);
}
.pn-box__amount {
	font-size: var(--pn-text-lead);
	line-height: 1.3;
	color: var(--text-primary);
}
.pn-box__amount del { color: var(--text-tertiary); margin-right: var(--space-2); }
.pn-box__amount ins { text-decoration: none; }
.pn-box__unit {
	font-size: var(--text-small);
	line-height: var(--lh-small);
	color: var(--text-secondary);
	font-variant-numeric: tabular-nums;
}

/* The repeat line. Hairlines above and below because this is the one part
   of the box that is about her rather than about the product — the rules
   say "this bit is yours", which is a thing worth saying. */
.pn .pn-box__repeat {
	display: flex;
	flex-wrap: wrap;
	align-items: baseline;
	gap: var(--space-2) var(--space-4);
	margin-top: var(--space-5);
	padding-block: var(--space-3);
	border-top: var(--border-hair);
	border-bottom: var(--border-hair);
	font-size: var(--text-small);
	line-height: var(--lh-small);
	color: var(--text-primary);
}
.pn-box__repeat-count { color: var(--text-secondary); }

.pn .pn-box__summary {
	font-size: var(--text-body);
	line-height: var(--lh-body);
	color: var(--text-secondary);
	max-width: var(--measure-body);
}

/* ── Options ───────────────────────────────────────────────────────────
   Pills, which the token file already sanctions for exactly this
   ("pills, dots, avatar"). The chosen one fills; it does not grow a rule
   or a tick, because a filled shape already says which it is. ────────── */
.pn-box__option + .pn-box__option { margin-top: var(--space-4); }

.pn-box__option-label {
	display: block;
	margin-bottom: var(--space-3);
	font-size: var(--text-caption);
	line-height: var(--lh-caption);
	letter-spacing: var(--ls-caption);
	color: var(--text-secondary);
}

.pn-chips { display: flex; flex-wrap: wrap; gap: var(--space-2); }

.pn .pn-chip {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	min-height: 40px;
	padding: var(--space-2) var(--space-5);
	background: transparent;
	color: var(--text-primary);
	border: var(--border-hair);
	border-radius: var(--radius-full);
	font-size: var(--text-small);
	line-height: 1.2;
	cursor: pointer;
	transition:
		background-color var(--dur-fast) var(--ease-out),
		border-color var(--dur-fast) var(--ease-out),
		color var(--dur-fast) var(--ease-out);
}
.pn .pn-chip:hover { border-color: var(--line-strong); opacity: 1; }
.pn .pn-chip[aria-pressed="true"] {
	background: var(--bg-brand);
	border-color: var(--bg-brand);
	color: var(--text-on-brand);
}
.pn .pn-chip[data-pn-unavailable="true"] {
	color: var(--text-tertiary);
	text-decoration: line-through;
	border-style: dashed;
}

/* ── Quantity and the button ───────────────────────────────────────── */
.pn .pn-box__buy {
	display: flex;
	align-items: stretch;
	gap: var(--space-3);
	margin-top: var(--space-6);
}

.pn-qty {
	display: inline-flex;
	align-items: stretch;
	border: var(--border-hair);
	border-radius: var(--radius-sm);
	overflow: hidden;
	flex: 0 0 auto;
}
.pn .pn-qty__step {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 40px;
	min-height: 44px;
	padding: 0;
	background: transparent;
	color: var(--text-primary);
	border: 0;
	border-radius: 0;
	font-size: var(--text-body);
	line-height: 1;
	cursor: pointer;
}
.pn .pn-qty__step:hover { opacity: var(--hover-opacity); }
.pn .pn-qty__input {
	width: 44px;
	padding: 0;
	background: transparent;
	color: var(--text-primary);
	border: 0;
	border-inline: var(--border-hair);
	border-radius: 0;
	font-size: var(--text-small);
	text-align: center;
	font-variant-numeric: tabular-nums;
	-moz-appearance: textfield;
	appearance: textfield;
}
.pn .pn-qty__input::-webkit-outer-spin-button,
.pn .pn-qty__input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }

.pn .pn-box__submit { flex: 1 1 auto; }

.pn .pn-box__state {
	margin-top: var(--space-3);
	font-size: var(--text-caption);
	line-height: var(--lh-caption);
	color: var(--text-secondary);
	min-height: 1em;
}
.pn .pn-box__state:empty { display: none; }
.pn .pn-box__state[data-pn-error="true"] { color: var(--error); }

.pn .pn-box__stock {
	margin-top: var(--space-3);
	font-size: var(--text-small);
	color: var(--error);
}

/* The refill: the cheaper way to buy the same thing again, under the
   button rather than three sections down. */
.pn .pn-box__refill {
	margin-top: var(--space-4);
	font-size: var(--text-small);
	line-height: var(--lh-small);
}
.pn .pn-box__refill-link {
	display: inline-flex;
	align-items: baseline;
	gap: var(--space-2);
}
.pn-box__refill-label { color: var(--text-secondary); }
.pn .pn-box__refill-link svg { width: 16px; height: 16px; align-self: center; }

.pn .pn-box__save { margin-top: var(--space-4); font-size: var(--text-small); }

.pn .pn-box__shipping { margin-top: var(--space-5); }

/* ── 主要成分 ──────────────────────────────────────────────────────────
   The reference design's arrangement, measured: intro on the left, a strip
   of small 5:6 photographs on the right, and one ingredient open below at
   a time. The strip is the navigation — that is what keeps only one
   ingredient's prose on screen, which is the whole reason the section is
   shaped this way rather than as a list. ─────────────────────────────── */
.pn-ingredients__top { display: block; }

@media (min-width: 1024px) {
	.pn-ingredients__top {
		display: grid;
		grid-template-columns: 1fr 1fr;
		column-gap: var(--space-8);
		align-items: start;
	}
}

.pn-ingredients__head { max-width: 40em; }

/* The measure is stated on the paragraph, not on .pn-ingredients__head: the
   .pn-lead component already carries max-width: var(--measure-lead), and a cap
   on the parent cannot loosen a cap the child puts on itself.

   And not --measure-lead, because `ch` is the width of a "0" in the sans face
   — about half a Chinese character — so 44ch is 44 Latin characters but only
   20 Chinese ones, which reads as a column rather than a paragraph. 32em is 32
   characters in either script. The token wants the same treatment inside the
   :lang(zh-Hant) block, which is a change across five pages and its own
   round's work. */
.pn .pn-ingredients__lead {
	max-width: 32em;
	margin-top: var(--space-4);
	color: var(--text-secondary);
}

/* ── The strip ─────────────────────────────────────────────────────────
   One to three, so it never needs to wrap or scroll at any width the site
   supports: three 140px thumbnails and their gaps fit inside the narrowest
   half we ever give them. ────────────────────────────────────────────── */
.pn-ingredients__strip {
	display: flex;
	gap: var(--space-4);
	margin-top: var(--space-7);
}

@media (min-width: 1024px) {
	.pn-ingredients__strip { margin-top: 0; }
}

.pn .pn-ingredients__thumb {
	display: block;
	flex: 0 1 140px;
	min-width: 0;
	padding: 0;
	background: none;
	border: 0;
	border-radius: 0;
	box-shadow: none;
	text-align: left;
	cursor: pointer;
	transition: opacity var(--dur-fast) var(--ease-out);
}

/* A div, not a button, when there is only one — nothing to choose. */
.pn-ingredients__strip > div.pn-ingredients__thumb { cursor: default; }

.pn-ingredients__thumb-media { transition: opacity var(--dur-base) var(--ease-out); }

.pn-ingredients__thumb-name {
	display: block;
	margin-top: var(--space-3);
	font-size: var(--text-caption);
	line-height: var(--lh-caption);
	letter-spacing: var(--ls-caption);
	color: var(--text-tertiary);
	transition: color var(--dur-fast) var(--ease-out);
}

/* Which one you are reading. The photograph carries the state and the name
   confirms it — no rule and no dot, because the panel below has already
   changed and a third marker would be saying it a third time. */
.pn .pn-ingredients__thumb:not(.is-current) .pn-ingredients__thumb-media { opacity: 0.45; }
.pn .pn-ingredients__thumb.is-current .pn-ingredients__thumb-name {
	color: var(--text-primary);
	font-weight: var(--weight-medium);
}
.pn .pn-ingredients__thumb:hover .pn-ingredients__thumb-media { opacity: 1; }
.pn .pn-ingredients__thumb:hover { opacity: 1; }
.pn .pn-ingredients__thumb:focus-visible { outline: none; box-shadow: var(--focus-ring); }

/* ── The open ingredient ───────────────────────────────────────────── */
.pn-ingredients__panels { margin-top: var(--space-8); }

/* Only meaningful with JavaScript off, where every panel is on the page at
   once. With it on, exactly one is ever visible. */
.pn-ingredients__panel + .pn-ingredients__panel { margin-top: var(--space-8); }

.pn-ingredients__panel { display: block; }

@media (min-width: 1024px) {
	.pn-ingredients__panel {
		display: grid;
		/* 3fr / 5fr, not 5fr / 7fr: measured against the reference, its
		   photograph is 329 of a 924 container — 36% — and 5fr/7fr put ours at
		   42%, which left the paragraph stranded beside an image half a screen
		   tall. This lands at 37%. */
		grid-template-columns: minmax(0, 3fr) minmax(0, 5fr);
		column-gap: var(--space-8);
		/* The prose sits on the foot of the photograph, as in the reference.
		   A short paragraph floating against the top of a 500px image reads
		   as a caption that has come adrift. */
		align-items: end;
	}
	.pn-ingredients__panel--textonly { grid-template-columns: minmax(0, 5fr); }
}

.pn-ingredients__figure { min-width: 0; }
.pn-ingredients__body { min-width: 0; padding-top: var(--space-5); }

@media (min-width: 1024px) {
	.pn-ingredients__body { padding-top: 0; padding-bottom: var(--space-6); }
}

.pn .pn-ingredients__name {
	font-family: var(--font-serif);
	font-size: var(--pn-text-h3);
	line-height: var(--lh-h3);
	font-weight: var(--weight-regular);
	color: var(--text-primary);
}

.pn .pn-ingredients__text {
	max-width: 30em;
	margin-top: var(--space-4);
	font-size: var(--pn-text-lead);
	line-height: var(--lh-lead);
	color: var(--text-primary);
}

/* ── The record ────────────────────────────────────────────────────── */
.pn-record__inner { display: block; }

@media (min-width: 1024px) {
	.pn-record__inner {
		display: grid;
		grid-template-columns: 1fr 1fr;
		column-gap: var(--space-8);
		align-items: start;
	}
	.pn-record--solo .pn-record__inner { grid-template-columns: minmax(0, 720px); }
	.pn-record__aside {
		position: sticky;
		top: calc(var(--pn-header-h) + var(--space-5));
	}
}

.pn-record__panels { min-width: 0; }
.pn-record__aside { display: none; }
@media (min-width: 1024px) { .pn-record__aside { display: block; } }

.pn-panel { border-top: var(--border-hair); }
.pn-panel:last-child { border-bottom: var(--border-hair); }

/* <summary>, not a button: the browser opens and closes it, announces the
   state and finds text inside a closed one, with no script. The two default
   markers (WebKit's ::-webkit-details-marker, everyone else's list-item
   disclosure triangle) both have to be taken off before ours can be drawn. */
.pn .pn-panel__toggle {
	display: flex;
	align-items: baseline;
	gap: var(--space-4);
	width: 100%;
	min-height: 64px;
	padding: var(--space-5) 0;
	list-style: none;
	text-align: left;
	cursor: pointer;
}
.pn .pn-panel__toggle::-webkit-details-marker { display: none; }
.pn .pn-panel__toggle::marker { content: ""; }
.pn .pn-panel__toggle:hover { opacity: var(--hover-opacity); }
.pn .pn-panel__toggle:focus-visible { outline: none; box-shadow: var(--focus-ring); }

/* The number is the signal: how many panels there are and how far in you
   are. It never renumbers around an empty one — pn_product_panels() drops
   those before it counts. */
.pn-panel__number {
	flex: 0 0 auto;
	font-size: var(--text-label);
	line-height: var(--lh-label);
	letter-spacing: var(--ls-label);
	color: var(--text-tertiary);
	font-variant-numeric: tabular-nums;
}
.pn-panel__title {
	flex: 1 1 auto;
	font-family: var(--font-serif);
	/* One step down from --pn-text-h3. The panel titles are a run of four
	   short labels, not four headings competing with the product's name. */
	font-size: var(--pn-text-lead);
	line-height: var(--lh-h3);
	font-weight: var(--weight-regular);
	color: var(--text-primary);
}

/* Two 1px lines that cross. They say open or closed and nothing else,
   which is the whole test a line has to pass on this site. */
.pn-panel__mark {
	position: relative;
	flex: 0 0 auto;
	width: 12px;
	height: 12px;
	align-self: center;
}
.pn-panel__mark::before,
.pn-panel__mark::after {
	content: "";
	position: absolute;
	inset: 50% 0 auto 0;
	height: var(--line-weight);
	background: var(--text-primary);
	transition: transform var(--dur-fast) var(--ease-out);
}
.pn-panel__mark::after { transform: rotate(90deg); }
.pn-panel[open] .pn-panel__mark::after { transform: rotate(0deg); }

.pn .pn-panel__body { padding-bottom: var(--space-6); }
.pn .pn-panel__content {
	/* Body, not small: this is the one part of the page that is read straight
	   through rather than scanned. */
	font-size: var(--text-body);
	line-height: var(--lh-body);
	color: var(--text-secondary);
	max-width: var(--measure-body);
}
.pn .pn-panel__content :is(ul, ol) { padding-left: 0; }
.pn .pn-panel__content li + li { margin-top: var(--space-2); }

.pn .pn-specs { display: block; }
.pn .pn-specs__row {
	display: grid;
	grid-template-columns: minmax(0, 10em) minmax(0, 1fr);
	gap: var(--space-4);
	padding-block: var(--space-3);
	border-bottom: var(--border-hair);
}
.pn .pn-specs__row:last-child { border-bottom: 0; }
.pn-specs__label { color: var(--text-tertiary); }
.pn-specs__value { color: var(--text-primary); font-variant-numeric: tabular-nums; }

/* ── Related ───────────────────────────────────────────────────────── */
.pn .pn-related__title { margin-bottom: var(--space-6); }

.pn .pn-related__row {
	display: grid;
	grid-template-columns: repeat(2, minmax(0, 1fr));
	column-gap: var(--grid-gap);
	row-gap: var(--space-7);
}
/* Four across only from 1024, not 768. At 768 the four cards come out 154px
   wide, which is narrower than any card on the listing — that grid runs
   2 → 3 at 900 → 4 at 1200. Two and then four keeps this row in step without
   a three-column step that would leave one of four items orphaned. */
@media (min-width: 1024px) { .pn .pn-related__row { grid-template-columns: repeat(4, minmax(0, 1fr)); } }

/* ── The bar ───────────────────────────────────────────────────────────
   Glass, like the header, so what passes under it is blurred rather than
   hidden. Off-screen until JavaScript brings it in. ──────────────────── */
.pn-productbar {
	position: fixed;
	inset: auto 0 0 0;
	z-index: 60;
	background: var(--paper-100);
	border-top: var(--border-hair);
	transform: translateY(100%);
	transition: transform var(--dur-base) var(--ease-out);
}

/* The same glass as the header, over the footer's plane rather than the
   page's. The bar closes the page the way the footer does, so it is
   --paper-100; the blur is the header's, to the letter, because two pieces of
   glass on one page that are not the same glass read as a mistake.

   The @supports test and the -webkit- prefix are copied from header.css and
   must stay in step with it: Safari needs the prefix, and a browser that
   cannot blur has to keep opaque paper rather than a 72% wash over the
   photographs passing underneath. */
@supports ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
	.pn-productbar {
		background: color-mix(in srgb, var(--paper-100) 72%, transparent);
		-webkit-backdrop-filter: blur(20px) saturate(180%);
		backdrop-filter: blur(20px) saturate(180%);
	}

	@media (prefers-reduced-transparency: reduce) {
		.pn-productbar {
			background: var(--paper-100);
			-webkit-backdrop-filter: none;
			backdrop-filter: none;
		}
	}
}

.pn-productbar.is-in { transform: translateY(0); }

.pn-productbar__inner {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: var(--space-4);
	width: 100%;
	max-width: calc(var(--page-max) + 2 * var(--pn-gutter));
	min-height: var(--pn-bar-h);
	margin-inline: auto;
	padding: var(--space-3) var(--pn-gutter);
	padding-bottom: max(var(--space-3), env(safe-area-inset-bottom));
}

.pn-productbar__what { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.pn-productbar__name {
	display: none;
	font-family: var(--font-serif);
	font-size: var(--text-body);
	line-height: 1.3;
	color: var(--text-primary);
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}
.pn-productbar__price {
	display: flex;
	align-items: baseline;
	gap: var(--space-3);
	font-size: var(--text-small);
	line-height: var(--lh-small);
	color: var(--text-primary);
}
.pn-productbar__unit { color: var(--text-secondary); font-variant-numeric: tabular-nums; }

@media (min-width: 768px) {
	.pn-productbar__name { display: block; }
	.pn .pn-productbar__submit { min-width: 200px; }
}

/* The mobile shop bar and this one cannot both hold the foot of the
   window. The product page does not render the shop bar, but a plugin or
   a later round might; this keeps the product's own bar on top. */
.pn-product-page .pn-shopbar { display: none; }

/* ── Zoom ──────────────────────────────────────────────────────────────
   One photograph, full bleed, on the page's own paper. Escape and a click
   anywhere close it; site.js is not involved because this dialog belongs
   to the gallery rather than to the header. ──────────────────────────── */
.pn-zoom {
	position: fixed;
	inset: 0;
	z-index: 80;
	padding: var(--space-6);
	background: var(--bg-page);
	border: 0;
	max-width: 100%;
	max-height: 100%;
	width: 100%;
	height: 100%;
}

/* `display` belongs on [open] and nowhere else.
   The UA stylesheet is what closes a <dialog>, with
   `dialog:not([open]) { display: none }` — and **author styles beat the UA
   sheet whatever their specificity**, so a bare `display: flex` on .pn-zoom
   overrode it and the closed dialog stayed laid out: still fixed, still
   covering the whole viewport, still swallowing every click on the page
   underneath.
   `dialog.open` read false throughout, which is exactly why it survived three
   rounds of scripted testing — the assertion was on .open rather than on
   whether the thing had actually gone. Only a real click and a screenshot
   caught it. */
.pn-zoom:not([open]) { display: none; }

.pn-zoom[open] {
	display: flex;
	align-items: center;
	justify-content: center;
}
.pn-zoom::backdrop { background: var(--bg-page); }
.pn-zoom__img { max-width: 100%; max-height: 100%; width: auto; object-fit: contain; }
/* A hairline circle rather than a bare glyph. A 16px × alone in a large field
   of paper is a 44px target nobody can see the edges of, and missing it lands
   on the photograph — which is why the dialog now also closes on any click
   inside it, including the image. */
.pn .pn-zoom__close {
	position: absolute;
	top: var(--space-5);
	right: var(--space-5);
	z-index: 1;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 44px;
	height: 44px;
	padding: 0;
	background: var(--bg-page);
	border: var(--border-hair);
	border-radius: var(--radius-full);
	color: var(--text-primary);
	font-size: var(--pn-text-lead);
	line-height: 1;
	cursor: pointer;
}
.pn .pn-zoom__close:hover,
.pn .pn-zoom__close:focus-visible {
	background: var(--bg-brand);
	border-color: var(--bg-brand);
	color: var(--text-on-brand);
	opacity: 1;
}
.pn .pn-zoom__img { cursor: zoom-out; }

@media (prefers-reduced-motion: reduce) {
	.pn-gallery { scroll-behavior: auto; }
	.pn-productbar { transition: none; }
	.pn-panel__mark::before,
	.pn-panel__mark::after { transition: none; }
}
