/* Product page primary split — grid-aligned rather than a bare 60/40.
   The gallery track ends on column 8's right edge, so the summary panel
   starts in the col 8/9 gutter and its content — inset by exactly one
   gutter, see `.ap-pp__summary` — begins on column 9's left edge.
   `margin-bottom: 192px` owns the gap to the collection section below. */
.ap-pp {
	display: grid;
	grid-template-columns: calc(var(--grid-col9-inset) - var(--grid-gutter, 32px)) 1fr;
	align-items: start;
	margin-bottom: 192px;
}

/* Brand / tenant collection — sits below the two-column product split.
   Spans the full page width with 96px horizontal insets on both sides. */
.ap-pp__collection {
	padding: 0 96px 192px;
	box-sizing: border-box;
}
.ap-pp__collection__title {
	color: var(--text-primary, #191919);
	margin: 0;
}
/* Collection header — title on the left, slider nav buttons on the right.
   24px gap below moves off the title onto this header wrapper. */
.ap-pp__collection__header {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 15px;
	margin-bottom: 24px;
}
.ap-pp__collection__nav {
	display: flex;
	gap: 15px;
	flex-shrink: 0;
}
.ap-pp__collection__nav-button {
	display: flex;
	padding: 20px;
	align-items: center;
	justify-content: center;
	gap: 10px;
	box-sizing: border-box;
	border: 2px solid var(--icon-button-secondary-outline-enabled, #F0F0F0);
	border-radius: 999px;
	background: transparent;
	cursor: pointer;
}
.ap-pp__collection__nav-button__icon {
	display: inline-flex;
	width: 20px;
	height: 20px;
}
.ap-pp__collection__nav-button__icon img {
	display: block;
	width: 100%;
	height: 100%;
}
/* Prev mirrors the chevron-right asset 180° so we only ship one SVG. */
.ap-pp__collection__nav-button--prev .ap-pp__collection__nav-button__icon img {
	transform: rotate(180deg);
}
/* Hover: the outlined pill fills solid — border AND background both
   take the `-outline-hover` token (Figma spec: 2px stroke + fill on
   `--IconButton-secondary-outline-hover`); radius stays the base
   999px. Doubles as the brand-wash lock (scoped under `.ap-sandbox`
   (0,3,0) to beat the universal button kill (0,2,1)), so all five
   wash properties stay pinned across hover/focus/active — just to the
   hover paint instead of the resting one. */
/* Overflow lock — Swiper's watchOverflow tags the nav buttons and the
   scrollbar with `*-lock` classes when every slide already fits (nothing
   to scroll) and removes them once scrolling is needed again. The CDN
   bundle's own display:none loses to the component rules here (theme
   CSS loads after it), so the lock is re-asserted at higher specificity.
   Covers the grid-internal bar AND the clubs rail's external bar (both
   live under `.ap-pp__collection`). */
.ap-pp__collection__nav-button.swiper-button-lock {
	display: none;
}
.ap-pp__collection .swiper-scrollbar.swiper-scrollbar-lock {
	display: none;
}
.ap-sandbox .ap-pp__collection__nav-button:hover,
.ap-sandbox .ap-pp__collection__nav-button:focus,
.ap-sandbox .ap-pp__collection__nav-button:focus-visible,
.ap-sandbox .ap-pp__collection__nav-button:active {
	background-color: var(--icon-button-secondary-outline-hover, #F0F0F0);
	color: inherit;
	border-color: var(--icon-button-secondary-outline-hover, #F0F0F0);
	outline: none;
	box-shadow: none;
}
/* Product cards strip — Swiper-driven slider. The cards' flex column stack
   (media → title → price) is unchanged; Swiper just owns the horizontal
   track + per-slide width. */
.ap-pp__collection__grid { position: relative; }
.ap-pp__collection__grid .swiper-slide {
	height: auto;            /* let each card grow to its own content */
	display: flex;
	flex-direction: column;
}
/* Slider scrollbar — sits in-flow below the swiper wrapper (not overlaid on
   the cards). The track spans the full width at 5px tall, fully rounded;
   the drag handle (pin) inherits the same radius in the design token's
   dark colour. Width of the handle vs track reflects current swiper
   progress, which Swiper updates dynamically. */
.ap-pp__collection__grid .swiper-scrollbar {
	position: relative;
	left: auto;
	bottom: auto;
	width: 100%;
	height: 5px;
	margin-top: 48px;
	border-radius: var(--radius-999, 999px);
	background: var(--slider-background, #F0F0F0);
}
.ap-pp__collection__grid .swiper-scrollbar-drag {
	/* Width is owned by Swiper via `scrollbar.dragSize: 312` in the init
	   below — NOT via a CSS `!important` override. Swiper uses the same
	   number to drive the drag's per-frame translate (`progress *
	   (trackWidth - dragSize)`), so locking the visual size in CSS only
	   would leave the math using its auto-calculated size and the drag
	   would under- or over-travel the track depending on slide count.
	   Height is owned here since Swiper doesn't set it inline. */
	height: 5px;
	flex-shrink: 0;
	background: var(--slider-pin-background, #191919);
	border-radius: var(--radius-999, 999px);
}
.ap-pp__collection__item {
	display: flex;
	flex-direction: column;
}
.ap-pp__collection__item__media {
	aspect-ratio: 3 / 4;
	background: #D9D9D9;
	/* Real product tiles set a background-image inline (live PDP,
	   0017 #37); cover-crop it to the 3:4 tile. Inert on the sandbox
	   mock, which paints the gray placeholder only. */
	background-size: cover;
	background-position: center;
}
.ap-pp__collection__item__title {
	align-self: stretch;
	color: var(--text-primary, #191919);
	margin: 16px 0 0;
	padding-left: 8px;
	text-transform: none;
}
.ap-pp__collection__item__price {
	align-self: stretch;
	color: var(--text-primary, #191919);
	margin: 8px 0 0;
	padding-left: 8px;
}

/* Product title — typography comes from `.type-heading-xl` (ago-tokens.css).
   We only own the colour and the 32px gap from the breadcrumb above. The
   breadcrumb's 24px bottom margin collapses with this 32px top margin → 32px. */
.ap-pp__title {
	color: var(--text-primary, #191919);
	margin: 32px 0 0;
	/* Real product titles carry long words + the "| Brand" suffix from
	   the catalog naming convention (e.g. "Krepšinio marškinėliai |
	   Vilniaus krepšinio mokykla") — at 58px "MARŠKINĖLIAI" overflows
	   the 448px summary column. Contain instead of bleeding off-page.
	   Inert on the short mock title. Found with real data 2026-07-19. */
	overflow-wrap: break-word;
}

/* Hairline divider. Horizontal inset is owned by `.ap-pp__summary` padding
   (64px each side); this rule just paints the 1px line and owns the 32px
   gap above. */
.ap-pp__divider {
	border: 0;
	height: 1px;
	margin: 32px 0 0;
	background: var(--divider-background-light, #E4E4E4);
}

/* "Colour: <name>" row. Both spans share `.type-label-sm-regular` typography
   (Outfit 12px/115%, 1.2px tracking, uppercase) — the label is tertiary, the
   value is primary. 24px gap above (from the divider); 8px between label
   and value. */
.ap-pp__colour-row {
	display: flex;
	align-items: baseline;
	gap: 8px;
	margin-top: 24px;
}
.ap-pp__colour-row__label { color: var(--text-tertiary, #787878); }
.ap-pp__colour-row__value { color: var(--text-primary, #191919); }

/* Colour options row — each option is a 72×94 (36:47) tile with a 2px
   outline. 8px between tiles, 12px below the colour value above. The
   swatch fill (background of each tile) isn't speccd yet, so it's left
   transparent and can be set per-option later. */
.ap-pp__colour-options {
	display: flex;
	gap: 8px;
	margin-top: 12px;
}
.ap-pp__colour-option {
	width: 72px;
	height: 94px;
	aspect-ratio: 36 / 47;
	box-sizing: border-box;
	border-radius: var(--radius-10, 10px);
	/* Border slot is reserved as transparent in the default state so the
	   selected variant can colour it in without shifting the tile size. */
	border: 2px solid transparent;
	background-color: transparent;
	background-position: center;
	background-size: cover;
	padding: 0;
	cursor: pointer;
	overflow: hidden;
}
.ap-pp__colour-option[aria-pressed="true"] {
	border-color: var(--color-selector-outline, #191919);
}
.ap-pp__colour-option:disabled {
	opacity: 0.5;
	cursor: not-allowed;
}

/* Sizes section header — `SIZE` heading on the left, `Sizes table` link on
   the right. 24px top gap from the colour-options row above lives on the
   wrapper. The heading uses `.type-label-sm-regular` (tertiary); the link
   uses `.type-body-xs-regular` (primary). */
.ap-pp__sizes-head {
	display: flex;
	justify-content: space-between;
	align-items: center;
	margin-top: 24px;
}
.ap-pp__sizes-title {
	color: var(--text-tertiary, #787878);
	margin: 0;
}
.ap-pp__sizes-table {
	display: inline-flex;
	align-items: center;
	gap: 4px;
	color: var(--text-primary, #191919);
	text-decoration: none;
}
.ap-pp__sizes-table__icon {
	display: inline-flex;
	width: 16px;
	height: 16px;
}
.ap-pp__sizes-table__icon img {
	display: block;
	width: 100%;
	height: 100%;
}

/* Material select — Figma 1815:4698 (Dropdown — open state). Labelled
   custom select between the colour swatches and the size selector; 24px
   top margin mirrors `.ap-pp__sizes-head`'s block pitch, 12px label→
   trigger gap per the frame. The trigger reuses the brandbook Input
   shell (60px, radius-15, input-border-enabled) as a <button>; the open
   panel is a listbox floating 4px below it — dropdown tokens, radius-15
   clipping its own rows, layered elevation shadow, 48px rows with hover
   fill and a trailing 24px check on the selected row. Five rows visible
   max (240px) per the component spec — longer lists scroll. Chevron is
   chevron-up.svg rotated 180° at rest (one-SVG convention), upright
   while open. All interactive states pinned to rest per the brand-wash
   lock. */
.ap-pp__material {
	display: flex;
	flex-direction: column;
	gap: var(--space-12, 12px);
	margin-top: 24px;
}
.ap-pp__material__label {
	color: var(--text-tertiary, #787878);
}
.ap-pp__material__control {
	position: relative;
}
.ap-sandbox .ap-pp__material__trigger {
	-webkit-appearance: none;
	appearance: none;
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: var(--space-8, 8px);
	width: 100%;
	height: 60px;
	box-sizing: border-box;
	margin: 0;
	padding: 10px var(--space-12, 12px);
	border: 1px solid var(--input-border-enabled, #E4E4E4);
	border-radius: var(--radius-15, 15px);
	background: var(--input-background-enabled, #FFF);
	color: var(--input-text-filled, #191919);
	text-align: left;
	text-transform: none;
	letter-spacing: 0;
	cursor: pointer;
}
.ap-sandbox .ap-pp__material__trigger:hover,
.ap-sandbox .ap-pp__material__trigger:focus,
.ap-sandbox .ap-pp__material__trigger:focus-visible,
.ap-sandbox .ap-pp__material__trigger:active {
	background: var(--input-background-enabled, #FFF);
	color: var(--input-text-filled, #191919);
	border: 1px solid var(--input-border-enabled, #E4E4E4);
	outline: none;
	box-shadow: none;
}
.ap-pp__material__value {
	min-width: 0;
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}
.ap-pp__material__chevron {
	flex: 0 0 auto;
	width: 24px;
	height: 24px;
	transform: rotate(180deg);
	transition: transform 240ms ease-out;
	pointer-events: none;
}
.ap-pp__material__trigger[aria-expanded="true"] .ap-pp__material__chevron {
	transform: rotate(0deg);
}
.ap-pp__material__chevron img {
	display: block;
	width: 100%;
	height: 100%;
}
.ap-sandbox .ap-pp__material__dropdown {
	position: absolute;
	z-index: 30;
	top: calc(100% + 4px);
	left: 0;
	right: 0;
	margin: 0;
	padding: 0;
	list-style: none;
	max-height: 240px;
	overflow-y: auto;
	background: var(--dropdown-background, #FFF);
	border: 1px solid var(--dropdown-border, #E4E4E4);
	border-radius: var(--radius-15, 15px);
	box-shadow: var(--elevation-dropdown, 0 2px 4px rgba(25, 25, 25, 0.06), 0 8px 24px rgba(25, 25, 25, 0.1));
	/* Open/close motion (Apple HIG style): the panel fades + scales in
	   from its anchor point at the trigger — 240ms ease-out in, a snappier
	   160ms out. `visibility` flips after the exit transition so the
	   closed panel stays out of the tab order and a11y tree; the JS
	   drives `.is-open` (the `hidden` attribute is only the no-JS
	   default, removed at init). */
	visibility: hidden;
	opacity: 0;
	transform: translateY(-4px) scale(0.98);
	transform-origin: top center;
	transition:
		opacity 160ms ease-out,
		transform 160ms ease-out,
		visibility 0s linear 160ms;
}
.ap-sandbox .ap-pp__material__dropdown.is-open {
	visibility: visible;
	opacity: 1;
	transform: none;
	transition:
		opacity 240ms cubic-bezier(0.2, 0, 0, 1),
		transform 240ms cubic-bezier(0.2, 0, 0, 1),
		visibility 0s;
}
.ap-sandbox .ap-pp__material__dropdown[hidden] {
	display: none;
}
@media (prefers-reduced-motion: reduce) {
	.ap-sandbox .ap-pp__material__dropdown,
	.ap-sandbox .ap-pp__material__dropdown.is-open,
	.ap-pp__material__chevron {
		transition: none;
	}
}
.ap-sandbox .ap-pp__material__option {
	display: flex;
	align-items: center;
	gap: var(--space-8, 8px);
	min-height: 48px;
	box-sizing: border-box;
	margin: 0;
	padding: var(--space-12, 12px);
	background: var(--dropdown-item-background-enabled, #FFF);
	color: var(--dropdown-item-text-enabled, #191919);
	cursor: pointer;
}
.ap-sandbox .ap-pp__material__option:hover {
	background: var(--dropdown-item-background-hover, #F7F7F7);
}
.ap-pp__material__option__label {
	flex: 1 1 auto;
	min-width: 0;
}
.ap-pp__material__option[aria-selected="true"]::after {
	content: "";
	flex: 0 0 auto;
	width: 24px;
	height: 24px;
	background: url(../img/sandbox/check-dropdown.svg) center / 24px 24px no-repeat;
}

/* Size options row — tiles share the row equally via `flex: 1` on each
   child, 8px gap between tiles, 14px gap from the sizes header above.
   `flex-wrap: nowrap` is the flexbox default but pinned explicitly so a
   future style accidentally toggling it can't cause a column stack.
   Text uses `.type-label-md-medium` (default) / `.type-label-md-bold`
   semantics via the selected-state font-weight bump. */
.ap-pp__size-options {
	/* grid auto-fit (was a flex row): the mock's 5 chips fill one row —
	   auto-fit collapses unused tracks so that look is unchanged — but
	   real variable products carry ~12 size terms (104-110 … XXXL)
	   which overflowed the flex row off-screen, and a plain flex-wrap
	   left the last row's chips stretched to huge widths (flex:1 is
	   per-row). Equal-width tracks keep every chip the same size on
	   every row. Found with real data 2026-07-19. Track minimum is a
	   sixth of the row (minus 5 gaps) so a row caps at SIX chips
	   (owner call 2026-08-19); ≤6 chips still share one row via
	   auto-fit collapse. */
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(max(48px, calc((100% - 5 * 8px) / 6)), 1fr));
	gap: 8px;
	margin-top: 14px;
}
/* `.ap-pp__size-option` (the chip primitive) lives in chrome-styles.php
   so it can be reused by the tenant list's filter modal. This file owns
   only its row container `.ap-pp__size-options` above. */
/* On hover/focus/active, pin all five visible-ring properties on each
   swatch to its resting state so Astra + multi-brand-uni can't paint the
   active brand colour through. Same pattern as `.ap-pp__size-option`:
   one rule for non-selected (transparent border), one for selected
   (dark border via the same token used by the `[aria-pressed="true"]`
   resting rule above). `:disabled` keeps its own dedicated block below. */
.ap-pp__colour-option:not([aria-pressed="true"]):not(:disabled):hover,
.ap-pp__colour-option:not([aria-pressed="true"]):not(:disabled):focus,
.ap-pp__colour-option:not([aria-pressed="true"]):not(:disabled):focus-visible,
.ap-pp__colour-option:not([aria-pressed="true"]):not(:disabled):active {
	background-color: transparent;
	color: var(--text-primary, #191919);
	border-color: transparent;
	outline: none;
	box-shadow: none;
}
.ap-pp__colour-option[aria-pressed="true"]:hover,
.ap-pp__colour-option[aria-pressed="true"]:focus,
.ap-pp__colour-option[aria-pressed="true"]:focus-visible,
.ap-pp__colour-option[aria-pressed="true"]:active {
	background-color: transparent;
	color: var(--text-primary, #191919);
	border-color: var(--color-selector-outline, #191919);
	outline: none;
	box-shadow: none;
}

/* `.ap-pp__size-option:disabled` (and its hover/focus/active locks) also
   live in chrome-styles.php alongside the chip primitive. */
.ap-pp__colour-option:disabled:hover,
.ap-pp__colour-option:disabled:focus,
.ap-pp__colour-option:disabled:focus-visible,
.ap-pp__colour-option:disabled:active {
	opacity: 0.5;
	background-color: transparent;
	border-color: transparent;
	border-style: solid;
	cursor: not-allowed;
}

/* Personalisation row — optional opt-in below the size selector for
   products that support a printed name / number. Static container:
   checkbox + title/description stack on the left (wrapped in a
   `<label>` so clicking the text toggles the checkbox), dark
   price-chip button on the right. Same `--gray-50` wash + `--radius-15`
   corner as the combo card so the two grey-bg rows in the summary
   read as a family. 24px gap from the size-options row above;
   the price-title's own 48px margin owns the gap below. */
.ap-pp__personalize {
	display: flex;
	flex-wrap: wrap;
	width: 100%;
	box-sizing: border-box;
	padding: 16px;
	justify-content: space-between;
	align-items: center;
	margin: 24px 0 0;
	border-radius: var(--radius-15, 15px);
	background: var(--gray-50, #F7F7F7);
	color: var(--text-primary, #191919);
}

/* Left cluster — checkbox + text stack treated as a single click
   target via the wrapping `<label>`. `align-items: flex-start` pins
   the checkbox to the top of the text block (so it lines up with
   the title row, not the centre of the two-line stack). */
.ap-pp__personalize__opt-in {
	display: flex;
	align-items: flex-start;
	gap: 8px;
	margin: 0;
	cursor: pointer;
}

/* Native checkbox styled to spec. `appearance: none` strips the
   platform paint so the box renders as a 20×20 white square with a
   6px-rounded `--gray-200` stroke. On `:checked`, the SVG (passed in
   via a per-element `--ap-pp-checkbox-icon` CSS variable set inline
   on the input) replaces the stroke + fill entirely — the SVG is
   self-contained (dark rounded fill + white check). */
.ap-pp__personalize__checkbox {
	appearance: none;
	-webkit-appearance: none;
	accent-color: initial;
	width: 20px;
	height: 20px;
	flex-shrink: 0;
	margin: 0;
	border: 1px solid var(--gray-200, #E4E4E4);
	border-radius: 6px;
	background: #FFFFFF;
	cursor: pointer;
}
/* Brand-wash lock. Astra has `input[type="checkbox"]:checked` (0,2,1)
   painting `--ast-global-color-1` onto the box — that beats a plain
   `.ap-pp__personalize__checkbox:checked` (0,2,0) at rest, leaving the
   red wash even before hover. The universal kill at the bottom of the
   stylesheet only catches <button> and <a> so the input slips through
   on :hover/:focus too. Scope everything under `.ap-sandbox` to reach
   (0,3,1)/(0,4,1) and pin all five props to each resting state — one
   rule for unchecked (white fill, gray stroke), one for checked
   (SVG-painted, transparent box). */
.ap-sandbox .ap-pp__personalize__checkbox:checked {
	background-color: transparent;
	background-image: var(--ap-pp-checkbox-icon);
	background-repeat: no-repeat;
	background-position: center;
	background-size: 20px 20px;
	border-color: transparent;
	outline: none;
	box-shadow: none;
}
.ap-sandbox .ap-pp__personalize__checkbox:not(:checked):hover,
.ap-sandbox .ap-pp__personalize__checkbox:not(:checked):focus,
.ap-sandbox .ap-pp__personalize__checkbox:not(:checked):focus-visible,
.ap-sandbox .ap-pp__personalize__checkbox:not(:checked):active {
	background-color: #FFFFFF;
	border-color: var(--gray-200, #E4E4E4);
	color: inherit;
	outline: none;
	box-shadow: none;
}
.ap-sandbox .ap-pp__personalize__checkbox:checked:hover,
.ap-sandbox .ap-pp__personalize__checkbox:checked:focus,
.ap-sandbox .ap-pp__personalize__checkbox:checked:focus-visible,
.ap-sandbox .ap-pp__personalize__checkbox:checked:active {
	background-color: transparent;
	background-image: var(--ap-pp-checkbox-icon);
	border-color: transparent;
	color: inherit;
	outline: none;
	box-shadow: none;
}

/* Two-line text stack — title on top, supporting copy below. The
   line-heights baked into the type classes already produce the spec
   gap, so no explicit row gap is needed here. */
.ap-pp__personalize__text {
	display: flex;
	flex-direction: column;
}
.ap-pp__personalize__title {
	color: var(--text-primary, #191919);
}
.ap-pp__personalize__description {
	color: var(--text-secondary, #5C5C5C);
}

/* Expanded panel — appears below the checkbox/cta row when the
   checkbox is `:checked`. `:has()` on the parent toggles `display`
   without JS. `flex-basis: 100%` makes the panel wrap onto its own
   line inside the row's flex container; 19.5px owns the gap from
   the row content above. */
/* Reveal mechanism — "drawer unroll" via `grid-template-rows: 0fr → 1fr`
   on the wrapper plus `overflow: hidden` on the inner. Content height
   animates from 0 to its natural row size; the inner clips the content
   while the wrapper grows, so the divider + fields slide down into the
   panel as it opens (no fade — pure slide). The 19.5px top gap is
   transitioned in lockstep so the wrapper has zero footprint when
   closed (no leftover gap above an empty area). 180ms ease-out — at
   the snappy end of the project's Apple-HIG range; the panel is short
   enough that a longer duration reads as sluggish. */
.ap-pp__personalize__expanded {
	display: grid;
	grid-template-rows: 0fr;
	flex-basis: 100%;
	margin-top: 0;
	transition: grid-template-rows 180ms ease-out,
				margin-top 180ms ease-out;
}
.ap-pp__personalize__expanded__inner {
	min-height: 0;
	overflow: hidden;
	display: flex;
	flex-direction: column;
}
/* Scoped to the OPT-IN checkbox specifically, not any checkbox in the
   card. The logo opt-outs below reuse `.ap-pp__personalize__checkbox`
   for their styling, so a bare `:has(.ap-pp__personalize__checkbox
   :checked)` would unroll the name/number panel when someone ticked
   "Be TOPsport logotipo" — two unrelated controls wired together. */
.ap-pp__personalize:has(.ap-pp__personalize__opt-in .ap-pp__personalize__checkbox:checked) .ap-pp__personalize__expanded {
	grid-template-rows: 1fr;
	margin-top: 19.5px;
}
/* Reset the `.ap-pp__divider` class's default 32px top margin when
   it sits inside the expanded panel — the panel itself owns the
   19.5px gap above, so the divider lands flush with the panel's
   top edge. */
.ap-pp__personalize__expanded__inner > .ap-pp__divider {
	margin: 0;
}

/* Fields row — holds one or more field groups side by side. Owns the
   16px gap from the divider above and the 16px horizontal gap between
   adjacent fields. */
.ap-pp__personalize__fields {
	display: flex;
	gap: 16px;
	margin-top: 16px;
}

/* Logo opt-outs — "Be TOPsport logotipo" / "Be rėmėjų logotipų". Each
   is an independently priced choice that lives in the same card but
   OUTSIDE `.ap-pp__personalize__expanded`, so it stays visible whether
   or not the name/number panel is open: dropping a printed logo has
   nothing to do with adding a name, and burying these behind the
   personalization opt-in would hide them from anyone who doesn't want
   personalization. Matches the Figma layer order, where the rows are
   siblings of the opt-in row rather than children of the panel.

   `flex-basis: 100%` makes the block wrap onto its own line in the
   card's flex row, the same trick `.ap-pp__personalize__expanded`
   uses. 16px above each divider and 16px below it — the rhythm the
   expanded panel already runs on, and what the Figma frame measures
   (divider bottom edge to row top). */
.ap-pp__personalize__options {
	flex-basis: 100%;
}
/* Card WITHOUT the Personalizacija pill — a product that enables only
   the opt-out rows. The sandbox never mocks that shape, so nothing
   accounted for the first row's own 16px top margin landing on top of
   the card's 16px padding: a 32px top band against the 16px every other
   card opens with. Pull it back so the row sits exactly where the pill
   sits when there is one. No effect on the sandbox shape, where
   `__options` is never the card's first child. */
.ap-pp__personalize__options:first-child > .ap-pp__personalize__option:first-child {
	margin-top: 0;
}
.ap-pp__personalize__options > .ap-pp__divider {
	margin: 16px 0 0;
}
.ap-pp__personalize__option {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 16px;
	margin: 16px 0 0;
	cursor: pointer;
}
/* Checkbox + label are one click target (the row is a `<label>`).
   `align-items: center` lands the 20px box on the optical centre of
   the 22px label line — the same alignment the Figma frame builds by
   hand with a 21px checkbox wrapper. `min-width: 0` lets a long or
   translated label shrink instead of pushing the price pill out of
   the row (see the flex min-content trap). */
.ap-pp__personalize__option-text {
	display: flex;
	align-items: center;
	gap: 8px;
	min-width: 0;
}
.ap-pp__personalize__option-title {
	color: var(--text-primary, #191919);
}

/* `.ap-pp__personalize__field` (base) + `.ap-pp__personalize__field--number`
   (narrow modifier) lifted to chrome-styles.php alongside the input
   chrome so other sandbox pages (checkout etc.) can reuse the full
   wrapper-around-input pattern. The `-head` / `-label` / `-counter`
   rules below stay local because they're product-page specific. */

/* Field head row — input label on the left, character counter on
   the right. Both spans share `.type-body-sm-medium` (Outfit 14 / 500
   / 1.5); only the colour differs so the counter reads as supporting
   info. */
.ap-pp__personalize__field-head {
	display: flex;
	justify-content: space-between;
	align-items: center;
}
.ap-pp__personalize__field-label {
	color: var(--text-primary, #191919);
}
.ap-pp__personalize__field-counter {
	color: var(--text-tertiary, #787878);
	text-align: right;
}

/* `.ap-pp__personalize__field-input` chrome lifted to chrome-styles.php
   so other sandbox pages (checkout etc.) can reuse it as a shared
   primitive. The wrapper / label / counter rules above stay local to
   this page since they're product-page specific. */

/* Price chip — pill-shaped dark button on the right. Same colour
   tokens as `.ap-pp__add-to-cart` (primary action), but pill radius
   and 10px padding per spec. Type comes from `.type-label-md-medium`
   (Outfit 14 / 500 / 1.15 / 0.10em / uppercase) — matches the
   "+ €15.00" callout exactly. */
/* The dark price chip. Shared by the personalization opt-in (a
   `<button>`) and the logo opt-out rows (a `<span>` — the row is
   already a `<label>`, and a nested button would swallow the click
   that toggles the checkbox). One rule so the two can't drift; the
   brand-wash lock below stays button-only since nothing repaints a
   bare span. */
.ap-pp__personalize__cta,
.ap-pp__personalize__option-price {
	display: flex;
	padding: 10px;
	justify-content: center;
	align-items: center;
	gap: 10px;
	flex-shrink: 0;
	border: 0;
	border-radius: 999px;
	background: var(--gray-950, #191919);
	color: var(--text-white, #FFFFFF);
	cursor: pointer;
}
/* Same brand-wash lock pattern as `.ap-pp__add-to-cart` — beats
   Astra's `button:hover` and multi-brand-uni's CSS-variable swap by
   pinning every visible property to the resting look. */
.ap-sandbox .ap-pp__personalize__cta:hover,
.ap-sandbox .ap-pp__personalize__cta:focus,
.ap-sandbox .ap-pp__personalize__cta:focus-visible,
.ap-sandbox .ap-pp__personalize__cta:active {
	background-color: var(--gray-950, #191919);
	color: var(--text-white, #FFFFFF);
	border-color: transparent;
	outline: none;
	box-shadow: none;
}

/* Price section title — same `.type-label-sm-regular` typography as the
   sizes title (tertiary). 48px gap from the size-options row above. */
.ap-pp__price-title {
	color: var(--text-tertiary, #787878);
	margin: 48px 0 0;
}

/* Price value — `.type-price-lg` (Outfit 58px / weight 600 / 1.1 / uppercase)
   from the tokens, primary colour, sits flush below the PRICE label. */
.ap-pp__price {
	color: var(--text-primary, #191919);
	margin: 0;
}

/* Buy row — primary "Add to basket" button stretches to fill (`flex: 1 0 0`),
   wishlist heart sits on the right at a fixed 60×60. 16px between them,
   32px above (from the price value). */
.ap-pp__buy-row {
	display: flex;
	gap: 16px;
	align-items: center;
	margin-top: 32px;
}
.ap-pp__add-to-cart {
	display: flex;
	flex: 1 0 0;
	height: 60px;
	padding: 18px 20px;
	box-sizing: border-box;
	justify-content: center;
	align-items: center;
	gap: 8px;
	border: 0;
	border-radius: var(--radius-15, 15px);
	background: var(--button-primary-background-enabled, #191919);
	color: var(--text-white, #ffffff);
	cursor: pointer;
	/* Spec: Outfit 16px / weight 500 / 150% line-height, no uppercase, no
	   tracking — close to `.type-body-md-regular` but with medium weight,
	   so we set the rule directly. */
	font-family: var(--font-family-ui, 'Outfit', sans-serif);
	font-size: var(--font-size-16, 16px);
	font-weight: var(--font-weight-medium, 500);
	line-height: 1.5;
	letter-spacing: 0;
	text-transform: none;
}
.ap-pp__add-to-cart__icon {
	display: inline-flex;
	width: 20px;
	height: 20px;
	/* Glyph follows the label colour: the bag SVG is a mask painted with
	   currentColor, so a skin that changes the CTA text (UTMA: dark label
	   on the lime fill) recolours the icon in the same rule. The <img>
	   inside stays in the markup as the no-mask fallback. */
	background-color: currentColor;
	-webkit-mask: url("../img/sandbox/bag-04-white.svg") center / 20px 20px no-repeat;
	mask: url("../img/sandbox/bag-04-white.svg") center / 20px 20px no-repeat;
}
.ap-pp__add-to-cart__icon img {
	display: block;
	width: 100%;
	height: 100%;
	visibility: hidden;
}
@supports not (mask: url("")) {
	.ap-pp__add-to-cart__icon { background-color: transparent; }
	.ap-pp__add-to-cart__icon img { visibility: visible; }
}
/* Lock against the universal `.ap-sandbox button:hover` kill — scoped
   under `.ap-sandbox` (0,3,0) to beat that rule (0,2,1), and all five
   brand-wash properties pinned to resting values. */
.ap-sandbox .ap-pp__add-to-cart:hover,
.ap-sandbox .ap-pp__add-to-cart:focus,
.ap-sandbox .ap-pp__add-to-cart:focus-visible,
.ap-sandbox .ap-pp__add-to-cart:active {
	background-color: var(--button-primary-background-enabled, #191919);
	color: var(--text-white, #ffffff);
	border-color: transparent;
	outline: none;
	box-shadow: none;
}

/* Delivery info list — icon + body line per row. 32px gap above (from the
   buy row). Designed as a list so more rows can be appended later. */
.ap-pp__delivery {
	list-style: none;
	margin: 32px 0 0;
	padding: 0;
	display: flex;
	flex-direction: column;
	gap: 8px;
}
.ap-pp__delivery-item {
	display: flex;
	align-items: center;
	gap: 4px;
}
.ap-pp__delivery-item__icon {
	display: inline-flex;
	width: 24px;
	height: 24px;
	flex-shrink: 0;
}
.ap-pp__delivery-item__icon img {
	display: block;
	width: 100%;
	height: 100%;
}
.ap-pp__delivery-item__text {
	color: var(--text-primary, #191919);
}

/* Product details panel — Aprašymas / Sudėtis / Pristatymas ir grąžinimas.
   Each row shares consistent 18px vertical rhythm and uses a 1px border on
   each item as the inter-row divider, so we don't have to drop separate
   <hr>s. */
.ap-pp__details__item {
	padding: 18px 0;
	border-bottom: 1px solid var(--divider-background-light, #E4E4E4);
}

/* "Derink kartu su" — product pairings / related products heading. Spec:
   `.type-heading-sm-medium` (Outfit 20px / weight 500 / 1.1 / 0 letter-
   spacing / natural case). 48px gap above (from the details panel below). */
.ap-pp__combo-title {
	color: var(--text-primary, #191919);
	margin: 48px 0 0;
	text-transform: none;
}

/* Pairing card — holds N product tiles separated by `+` buttons, left-aligned
   with 12px gaps. Stretches to fill the summary column, capped at the 544px
   design width so it never grows past spec on wide viewports. */
.ap-pp__combo-item {
	display: flex;
	width: 100%;
	max-width: 544px;
	height: 168px;
	padding: 16px 32px 16px 16px;
	align-items: center;
	gap: 12px;
	box-sizing: border-box;
	margin: 16px 0 0;
	border-radius: var(--radius-15, 15px);
	background: var(--gray-50, #F7F7F7);
}

/* Product tile inside a combo. Background offset + scale come straight from
   the Figma export so the placeholder crop matches design until real
   per-product images land (set via inline `background-image`). */
.ap-pp__combo-item__product {
	flex-shrink: 0;
	width: 104px;
	height: 136px;
	aspect-ratio: 13 / 17;
	border-radius: var(--radius-8, 8px);
	background-color: lightgray;
	background-position: -16.244px -8.825px;
	background-size: 131.117% 138%;
	background-repeat: no-repeat;
}

/* `+` separator between two consecutive combo tiles. Pill-shaped, same grey
   wash as the wishlist heart on the buy row. */
.ap-pp__combo-item__add {
	display: flex;
	padding: 8px;
	align-items: center;
	gap: 10px;
	flex-shrink: 0;
	border: 0;
	border-radius: 999px;
	background: var(--icon-button-secondary-background-enabled, #F0F0F0);
	cursor: pointer;
}
.ap-pp__combo-item__add__icon {
	display: inline-flex;
	width: 16px;
	height: 16px;
}
.ap-pp__combo-item__add__icon img {
	display: block;
	width: 100%;
	height: 100%;
}
/* Lock against the universal `.ap-sandbox button:hover` kill — scoped
   under `.ap-sandbox` (0,3,0) to beat that rule (0,2,1), and all five
   brand-wash properties pinned to resting values. */
.ap-sandbox .ap-pp__combo-item__add:hover,
.ap-sandbox .ap-pp__combo-item__add:focus,
.ap-sandbox .ap-pp__combo-item__add:focus-visible,
.ap-sandbox .ap-pp__combo-item__add:active {
	background-color: var(--icon-button-secondary-background-enabled, #F0F0F0);
	color: inherit;
	border-color: transparent;
	outline: none;
	box-shadow: none;
}

/* Right-side stack — price row on top, CTA button below, with 16px gap.
   `margin-left: auto` pushes the whole stack past the tile group to the
   right edge; `align-self: flex-end` overrides the parent's
   `align-items: center` so the stack anchors to the bottom of the card
   while the tiles stay centred. `align-items: flex-end` keeps the price
   row right-aligned above the button if it's narrower than the button. */
.ap-pp__combo-item__cta-group {
	display: flex;
	flex-direction: column;
	gap: 16px;
	margin-left: auto;
	align-self: flex-end;
	align-items: flex-end;
}

/* Price block — "Price" label stacked directly above the price row with no
   extra margin, so the gap between them is just their natural line-heights.
   Block is left-aligned internally (title sits over the left edge of the
   price row); `cta-group`'s `align-items: flex-end` right-aligns the block
   as a whole within the right-side stack. */
.ap-pp__combo-item__price-block {
	display: flex;
	flex-direction: column;
	align-items: flex-start;
}
.ap-pp__combo-item__price-title {
	color: var(--text-tertiary, #787878);
	margin: 0;
}

/* Combo price row — current price (primary) on the left, original
   struck-through price (tertiary) on the right, 8px between them. Both
   spans use `.type-heading-sm-medium` typography (Outfit 20px / weight
   500 / 110%); only colour + strike decoration differ. */
.ap-pp__combo-item__price-row {
	display: flex;
	align-items: baseline;
	gap: 8px;
}
.ap-pp__combo-item__price { color: var(--text-primary, #191919); }
.ap-pp__combo-item__price--strike {
	color: var(--text-tertiary, #787878);
	text-decoration: line-through;
}

/* "Peržiūrėti" CTA — sits at the bottom of the right-side stack. */
.ap-pp__combo-item__cta {
	display: flex;
	height: 48px;
	padding: 12px 32px;
	justify-content: center;
	align-items: center;
	gap: 8px;
	box-sizing: border-box;
	border: 0;
	border-radius: 12px;
	background: var(--button-primary-background-enabled, #191919);
	color: var(--text-white, #FFFFFF);
	font-family: var(--font-family-ui, 'Outfit', sans-serif);
	font-size: var(--font-size-16, 16px);
	font-weight: var(--font-weight-medium, 500);
	line-height: 1.5;
	letter-spacing: 0;
	text-transform: none;
	cursor: pointer;
}
/* Lock against the universal `.ap-sandbox button:hover` kill — scoped
   under `.ap-sandbox` (0,3,0) to beat that rule (0,2,1), and all five
   brand-wash properties pinned to resting values. */
.ap-sandbox .ap-pp__combo-item__cta:hover,
.ap-sandbox .ap-pp__combo-item__cta:focus,
.ap-sandbox .ap-pp__combo-item__cta:focus-visible,
.ap-sandbox .ap-pp__combo-item__cta:active {
	background-color: var(--button-primary-background-enabled, #191919);
	color: var(--text-white, #FFFFFF);
	border-color: transparent;
	outline: none;
	box-shadow: none;
}

/* `.ap-pp__section-head`, `.ap-pp__section-title`, `.ap-pp__section-head__icon`
   live in `template-parts/sandbox/chrome-styles.php` so every sandbox page
   can reuse the accordion primitive (chevron + rotation animation + brand-
   wash lock). This file owns only the `--`-modifier-free behaviour specific
   to the product-page details panel: the next-sibling collapse for
   `.ap-pp__description` is page-local because the filter modal collapses a
   different sibling class (`.ap-tpl__filter-modal__panel`). */
/* Expand/collapse — same "drawer unroll" mechanism as
   `.ap-pp__personalize__expanded`: the item is a two-row grid whose
   body row animates `0fr → 1fr` while the description clips its own
   overflow, so the copy slides out from under the head and the page
   below reflows smoothly with it (and back — collapse is animated
   too, unlike the old `display: none` flip). Same 180ms ease-out as
   the personalize panel. The head's 16px expanded bottom margin (set
   by the chrome primitive) is transitioned in lockstep so the gap
   unrolls with the row instead of snapping. */
.ap-pp__details__item {
	display: grid;
	/* `min-content`, NOT `auto`, for the head row. While the body row
	   interpolates 0fr → 1fr it spends the whole transition at a flex
	   factor below 1, so it can only claim that fraction of the free
	   space — and a stretchable `auto` track absorbs the remainder,
	   then gives it back as the factor reaches 1. Measured with `auto`:
	   the head row bulged 24px → 42px → 24px, which dragged the
	   centre-aligned title and chevron ~9px down and back (and pushed
	   the description's top ~11px past its resting position) on every
	   expand and collapse. `min-content` isn't stretchable, so the head
	   row stays at its content height throughout and only the body row
	   animates. Unlike `align-self: start` on the head, this fixes the
	   description's overshoot too rather than just the head's. */
	grid-template-rows: min-content 0fr;
	transition: grid-template-rows 180ms ease-out;
}
.ap-pp__details__item:has(> .ap-pp__section-head[aria-expanded="true"]) {
	grid-template-rows: min-content 1fr;
}
.ap-pp__details__item > .ap-pp__section-head {
	transition: margin-bottom 180ms ease-out;
}
.ap-pp__description {
	color: var(--text-secondary, #5C5C5C);
	margin: 0;
	/* Row-collapse mechanics (mirrors `.ap-pp__personalize__expanded__inner`):
	   `min-height: 0` lets the 0fr row actually reach zero, `overflow:
	   hidden` clips the copy while the row is mid-unroll. */
	min-height: 0;
	overflow: hidden;
	/* Right padding matches the icon column (24px icon + 12px gap) so the
	   body copy ends at the title's right boundary instead of running under
	   the chevron's visual column. */
	padding-right: 36px;
}
/* Collapsed copy leaves the a11y tree/tab order once the unroll ends —
   the old `display: none` removed it outright, and live descriptions
   can carry links that must not stay focusable while invisible. The
   180ms delay keeps the text visible during the collapse animation;
   re-expanding removes this rule so visibility flips back instantly. */
.ap-pp__section-head[aria-expanded="false"] + .ap-pp__description {
	visibility: hidden;
	transition: visibility 0s 180ms;
}
@media (prefers-reduced-motion: reduce) {
	.ap-pp__details__item,
	.ap-pp__details__item > .ap-pp__section-head {
		transition: none;
	}
	.ap-pp__section-head[aria-expanded="false"] + .ap-pp__description {
		transition: none;
	}
}
.ap-pp__like {
	display: flex;
	width: 60px;
	height: 60px;
	padding: 20px;
	box-sizing: border-box;
	align-items: center;
	justify-content: center;
	gap: 10px;
	border: 0;
	border-radius: 999px;
	background: var(--icon-button-secondary-background-enabled, #F0F0F0);
	color: var(--text-primary, #191919);
	cursor: pointer;
}
.ap-pp__like svg {
	display: block;
	width: 20px;
	height: 20px;
}
/* Wishlisted (aria-pressed=true) — the heart fills, gray-900 per the
   Figma pressed IconButton (1672-4471; supersedes the provisional
   monochrome fill from task 0021). */
.ap-pp__like[aria-pressed="true"] {
	color: var(--icon-button-primary-onlight, #262626);
}
.ap-pp__like[aria-pressed="true"] svg {
	fill: currentColor;
}
/* Lock against the universal `.ap-sandbox button:hover` kill — scoped
   under `.ap-sandbox` (0,3,0) to beat that rule (0,2,1), and all five
   brand-wash properties pinned to resting values. */
.ap-sandbox .ap-pp__like:hover,
.ap-sandbox .ap-pp__like:focus,
.ap-sandbox .ap-pp__like:focus-visible,
.ap-sandbox .ap-pp__like:active {
	background-color: var(--icon-button-secondary-background-enabled, #F0F0F0);
	color: var(--text-primary, #191919);
	border-color: transparent;
	outline: none;
	box-shadow: none;
}
/* Keep the pressed fill colour through the brand-wash state pins above. */
.ap-sandbox .ap-pp__like[aria-pressed="true"]:hover,
.ap-sandbox .ap-pp__like[aria-pressed="true"]:focus,
.ap-sandbox .ap-pp__like[aria-pressed="true"]:focus-visible,
.ap-sandbox .ap-pp__like[aria-pressed="true"]:active {
	color: var(--icon-button-primary-onlight, #262626);
}

.ap-pp__gallery,
.ap-pp__summary { box-sizing: border-box; min-width: 0; }
/* Gallery — stack of full-bleed images with 2px gaps that read as the page
   background between every touching pair (vertical or horizontal).
   `min-width: 0` (above) opts the grid item out of `min-content` width so
   the natural image size doesn't push the column past its track width. */
.ap-pp__gallery {
	position: relative;
	display: flex;
	flex-direction: column;
	gap: 2px;
	background: #fff;
	/* Hide the system cursor over the gallery — the zoom pill follows
	   the pointer and serves as the visible cursor. `.ap-pp__back` and
	   the mobile zoom override (below) re-declare `cursor: pointer`
	   directly on the element, so those still get the system arrow. */
	cursor: none;
}

/* Back button — floats over the top-left of the gallery, 48px in from both
   edges. Pill-shaped, semi-transparent white so it reads on any image.

   z-index is one above `.ap-pp__zoom` (z-index: 2) on purpose: the zoom
   pill follows the cursor, so when the cursor reaches the back button
   the two pills physically overlap. Without a higher z-index, hit-testing
   would route the pointer to whichever came later in source (zoom), and
   `.ap-pp__back:hover` would never fire — leaving the zoom pill visible
   on top of the back pill and intercepting the click. With back on top,
   the sibling rule `.ap-pp__back:hover ~ .ap-pp__zoom { opacity: 0 }`
   (below) reliably fades the zoom pill out. */
.ap-pp__back {
	/* Sticky (not absolute): rests at the same 48px top-left inset, but
	   pins at its on-screen position (72px sticky header + the 48px
	   inset) while the gallery scrolls, so back-navigation stays
	   reachable. In the gallery's flex column the button is a flow
	   child now — margins recreate the inset (48px top/left) and the
	   negative bottom swallows its own footprint (48 margin + 60 pill
	   + the column's 2px gap) so the image stack starts where it always
	   did. It unsticks at the gallery's end, which is where the page
	   stops being about the images anyway. */
	position: sticky;
	top: calc(72px + 48px);
	align-self: flex-start;
	margin: 48px 0 -110px 48px;
	z-index: 3;
	display: inline-flex;
	padding: 20px;
	align-items: center;
	justify-content: center;
	gap: 10px;
	box-sizing: border-box;
	border: 0;
	border-radius: 999px;
	background: var(--icon-button-tertiary-background-enabled, rgba(255, 255, 255, 0.50));
	cursor: pointer;
}
.ap-pp__back__icon {
	display: inline-flex;
	width: 20px;
	height: 20px;
}
.ap-pp__back__icon img {
	display: block;
	width: 100%;
	height: 100%;
}
/* Lock against Astra's button:focus wash + the universal `.ap-sandbox
   button:hover` kill (specificity 0,2,1) which would otherwise force the
   resting semi-transparent white background to `transparent` on hover.
   Scoping under `.ap-sandbox` bumps this to 0,3,0 so it wins, and all
   five properties are pinned per the brand-wash memory. */
.ap-sandbox .ap-pp__back:hover,
.ap-sandbox .ap-pp__back:focus,
.ap-sandbox .ap-pp__back:focus-visible,
.ap-sandbox .ap-pp__back:active {
	background-color: var(--icon-button-tertiary-background-enabled, rgba(255, 255, 255, 0.50));
	color: inherit;
	border-color: transparent;
	outline: none;
	box-shadow: none;
}
/* Default slot — full-width, fixed 1660px height. `object-fit: cover`
   centre-crops whatever image we feed it so the slot height stays stable. */
.ap-pp__gallery__image {
	display: block;
	width: 100%;
	height: 1660px;
	object-fit: cover;
}
/* Two-up row — half-width tiles, 829px tall, with a 2px gap between them.
   `flex: 1` so each tile shares the remaining space after the gap. */
.ap-pp__gallery__row {
	display: flex;
	gap: 2px;
}
.ap-pp__gallery__row .ap-pp__gallery__image {
	flex: 1;
	min-width: 0;
	width: auto;
	height: 829px;
}
/* Summary owns the column's horizontal inset for all content. The left
   inset is one gutter: the panel's own left edge sits on column 8's
   right edge (owned by the `.ap-pp` track), so a gutter's worth of
   padding puts every child — breadcrumb, title, dividers, CTA row — on
   column 9's left edge. The right stays at 64px, off-grid on purpose:
   that's where the Figma frame ends the rail. Top padding is zero so the
   breadcrumb's own 64px margin lands exactly 64px below the site header.
   64px of bottom padding sits between the last pairing card and the
   viewport bottom while the summary is pinned there.

   Sticky position + `top` value are owned by the directional-sticky JS
   at the bottom of this file (a port of react-sticky-box). It switches
   between three modes per scroll direction:
     • `stickyTop` — `position: sticky; top: 0` (top pinned at viewport top)
     • `stickyBottom` — `position: sticky; top: viewportH - summaryH` (top
       set negative so bottom lands at viewport bottom — the Phase 2 pin)
     • `relative` — `position: relative; top: <locked-offset>` (parked at
       current page-y, scrolls naturally with the page)
   Direction transitions hand off via the `relative` mode so the element
   never visually jumps. Because scrolling is always CSS-native in the
   active mode, there's no per-frame JS compensation and no catch.

   `align-self: start` puts the flow position at the top of the grid track
   — what `relative` mode and the sticky calculations both assume.

   Mobile resets to static via the single-column stack media query below.
   The JS clears its inline `position`/`top` on mobile to let the media
   query take over. */
.ap-pp__summary {
	background: #fff;
	padding: 0 64px 64px var(--grid-gutter, 32px);
	align-self: start;
}

/* Zoom-in button — on desktop the button follows the cursor while the
   mouse is inside `.ap-pp__gallery`. Default anchor is top-right via
   the `right`/`top` properties (used on `:focus-visible` for keyboard
   users and as the resting position before JS sees any mousemove).
   JS swaps to `top`/`left` inline styles + `right: auto` to drive the
   button to the cursor coords, and clears those on mouseleave to fall
   back to the CSS default. Opacity is bound to the gallery's :hover
   state so the button fades in/out at the zone boundary. Mobile flips
   to a fixed top-right pill — see the media query below. */
.ap-pp__zoom {
	position: absolute;
	top: 48px;
	right: 48px;
	z-index: 2;
	display: inline-flex;
	padding: 20px;
	align-items: center;
	justify-content: center;
	gap: 10px;
	box-sizing: border-box;
	border: 0;
	border-radius: 999px;
	background: var(--icon-button-tertiary-background-enabled, rgba(255, 255, 255, 0.50));
	/* No system cursor on the pill itself — the pill IS the cursor on
	   desktop. Mobile override (below) restores `cursor: pointer`. */
	cursor: none;
	opacity: 0;
	pointer-events: none;
	transition: opacity 0.18s ease;
}
.ap-pp__gallery:hover .ap-pp__zoom,
.ap-pp__zoom:focus-visible {
	opacity: 1;
	pointer-events: auto;
}
/* When the cursor is on the back button the zoom button should
   disappear — otherwise (since JS centres it on the cursor) it
   overlaps the back pill and intercepts the click. `.ap-pp__back`
   precedes `.ap-pp__zoom` in the DOM, so the general-sibling
   combinator (`~`) targets the zoom while the back is hovered.
   Specificity (0,3,0) ties with the reveal rule above; this rule
   comes later in source so it wins on cascade. */
.ap-pp__back:hover ~ .ap-pp__zoom {
	opacity: 0;
	pointer-events: none;
}
.ap-pp__zoom__icon {
	display: inline-flex;
	width: 20px;
	height: 20px;
}
.ap-pp__zoom__icon img {
	display: block;
	width: 100%;
	height: 100%;
}
/* Lock against Astra's button:focus wash + the universal brand-wash
   kill so the pill keeps its semi-transparent white fill across all
   interactive states. Specificity is critical here: because this
   button follows the cursor on desktop, it's permanently in :hover —
   `.ap-sandbox button:hover` (0,2,1) would otherwise win against a
   plain `.ap-pp__zoom:hover` (0,2,0) and strip the bg to transparent.
   Prefixing with `.ap-pp__gallery` bumps us to (0,3,0). */
.ap-pp__gallery .ap-pp__zoom:hover,
.ap-pp__gallery .ap-pp__zoom:focus,
.ap-pp__gallery .ap-pp__zoom:focus-visible,
.ap-pp__gallery .ap-pp__zoom:active {
	background-color: var(--icon-button-tertiary-background-enabled, rgba(255, 255, 255, 0.50));
	border-style: solid;
}

/* Image indicators — mobile-only, same default-hide pattern as the zoom
   button. Position + look is owned by the mobile media query below. */
.ap-pp__indicators { display: none; }

/* Gallery swiper — mobile-only swipeable carousel. Hidden on desktop
   (the existing stacked images take over); revealed + styled in the
   mobile media query below. */
.ap-pp__gallery__swiper { display: none; }


/* ─────────────────────────────────────────────────────────────────────
   Lightbox — fullscreen viewer triggered by clicking any image in the
   gallery (or, on mobile, the zoom button). Hidden by default; JS adds
   `.is-open` to reveal it. Sits above page content via `position:fixed`
   + a high z-index. White backdrop matches the rest of the page; the
   image is centred and scaled with `object-fit: contain` so neither
   portrait nor landscape gets cropped.
   ───────────────────────────────────────────────────────────────────── */
.ap-pp__lightbox {
	position: fixed;
	inset: 0;
	/* Same overlay tier as the cart/search/combo modals — above the
	   sticky header (z-index: 1101) so the enlarged image covers the
	   header band on both desktop and mobile. */
	z-index: 1102;
	display: none;
	background: #fff;
	/* The lightbox is its own scroll container: the image spans the full
	   viewport width, so a portrait product photo runs taller than the
	   screen and the overflow has to be reachable. `body` keeps its own
	   `overflow: hidden` below, so the page behind never scrolls, and
	   `overscroll-behavior` stops the scroll chaining out to it at the
	   ends. Logo / close / thumbs switch to `position: fixed` so they
	   stay pinned to the viewport while the photo scrolls under them. */
	overflow-y: auto;
	overscroll-behavior: contain;
}
.ap-pp__lightbox.is-open { display: block; }
body.ap-pp__lightbox-open { overflow: hidden; }

/* AGO logo — desktop-only. Anchored to the top-left, mirroring the
   close button's top-right inset for visual symmetry. Hidden in the
   mobile media query below. Fixed 144.144×64 slot; the logo sits on
   transparent background and is fitted (not cropped) inside the box. */
.ap-pp__lightbox__logo {
	display: block;
	position: fixed;
	top: 48px;
	left: 48px;
	z-index: 2;
	width: 144.144px;
	height: 64px;
	flex-shrink: 0;
	background-color: transparent;
	background-position: 0 50%;
	background-size: contain;
	background-repeat: no-repeat;
}

/* Close button — pill-shaped, semi-transparent white. Mirrors
   `.ap-pp__back` styling but anchored to the top-right corner. Inset
   matches `.ap-pp__back`'s 48px so the two pill positions are symmetric. */
.ap-pp__lightbox__close {
	position: fixed;
	top: 48px;
	right: 48px;
	z-index: 2;
	display: inline-flex;
	padding: 20px;
	align-items: center;
	gap: 10px;
	box-sizing: border-box;
	border: 0;
	border-radius: 999px;
	background: var(--icon-button-tertiary-background-enabled, rgba(255, 255, 255, 0.50));
	cursor: pointer;
}
.ap-pp__lightbox__close__icon {
	display: inline-flex;
	width: 20px;
	height: 20px;
}
.ap-pp__lightbox__close__icon img {
	display: block;
	width: 100%;
	height: 100%;
}
/* Lock against Astra's button:focus wash + the universal `.ap-sandbox
   button:hover` kill (specificity 0,2,1) which would otherwise force the
   resting semi-transparent white background to `transparent` on hover.
   Scoping under `.ap-sandbox` bumps this to 0,3,0 so it wins, and all
   five properties are pinned per the brand-wash memory. */
.ap-sandbox .ap-pp__lightbox__close:hover,
.ap-sandbox .ap-pp__lightbox__close:focus,
.ap-sandbox .ap-pp__lightbox__close:focus-visible,
.ap-sandbox .ap-pp__lightbox__close:active {
	background-color: var(--icon-button-tertiary-background-enabled, rgba(255, 255, 255, 0.50));
	color: inherit;
	border-color: transparent;
	outline: none;
	box-shadow: none;
}

/* Main swiper — spans the viewport width; height follows the image
   rather than the screen, so the swiper grows past the fold on tall
   photos and `.ap-pp__lightbox` scrolls it. The slide keeps its flex
   centring for images shorter than the viewport. */
.ap-pp__lightbox__swiper {
	width: 100%;
	height: auto;
}
.ap-pp__lightbox__swiper .swiper-slide {
	display: flex;
	align-items: center;
	justify-content: center;
}
.ap-pp__lightbox__swiper .swiper-slide img {
	display: block;
	/* Fill the width, no letterboxing (owner call 2026-08-13). This was
	   briefly `height: 100%; object-fit: contain` after the 2026-07-19
	   "lightbox too zoomed in" report — contain fits WC's ~3:4 portrait
	   shots to the viewport HEIGHT, which left ~555px of dead white on
	   each side at 1920. Filling the width is the deliberate trade: a
	   3:4 photo is ~2.4x the viewport height, and the remainder is
	   reachable by scrolling the lightbox. `height: auto` keeps the
	   aspect ratio, so no `object-fit` is needed here. */
	width: 100%;
	height: auto;
}

/* Thumbnail strip — anchored to the bottom-centre of the lightbox, 50px
   above the bottom edge. One 70×92 tile per gallery image, glued side
   to side (gap:0), centred via the left:50% + translateX trick. JS owns
   wiring clicks to the main swiper. */
.ap-pp__lightbox__thumbs {
	position: fixed;
	left: 50%;
	bottom: 50px;
	transform: translateX(-50%);
	z-index: 2;
	display: flex;
	gap: 0;
	margin: 0;
	padding: 0;
	list-style: none;
}
.ap-pp__lightbox__thumbs li { margin: 0; padding: 0; }
.ap-pp__lightbox__thumb {
	display: block;
	width: 70px;
	height: 92px;
	flex-shrink: 0;
	padding: 0;
	border: 0;
	/* Astra applies a global border-radius on `<button>`; pin to 0 here
	   so the thumbnails stay sharp 70×92 rectangles. */
	border-radius: 0;
	background-color: #D9D9D9;
	background-size: cover;
	background-position: center;
	background-repeat: no-repeat;
	cursor: pointer;
}
/* Lock against the universal `.ap-sandbox button:hover` kill — scoped
   under `.ap-sandbox` (0,3,0) to beat that rule (0,2,1), and all five
   brand-wash properties pinned to resting values. */
.ap-sandbox .ap-pp__lightbox__thumb:hover,
.ap-sandbox .ap-pp__lightbox__thumb:focus,
.ap-sandbox .ap-pp__lightbox__thumb:focus-visible,
.ap-sandbox .ap-pp__lightbox__thumb:active {
	background-color: #D9D9D9;
	color: inherit;
	border-color: transparent;
	outline: none;
	box-shadow: none;
	filter: brightness(0.95);
}


/* ─────────────────────────────────────────────────────────────────────
   Combo modal — opens on `.ap-pp__combo-item__cta` ("Peržiūrėti") click.
   Same architecture as the cart modal above: the drawer reaches left to
   the left edge of grid column 8 (the 5th column from the right) — the
   shared left edge for every side drawer on the site — so the leftmost part of the gallery stays visible
   behind the dim scrim. The `100%` basis is the fixed inset-0 wrapper,
   which spans the viewport, so the token's own `100%` resolves against
   the same box.
   ───────────────────────────────────────────────────────────────────── */
.ap-pp__combo-modal {
	position: fixed;
	inset: 0;
	z-index: 1102; /* same overlay tier as `.ap-pp__cart-modal` — above header */
	overflow: hidden;
	pointer-events: none;
}
.ap-pp__combo-modal.is-open {
	pointer-events: auto;
}
body.ap-pp__combo-modal-open { overflow: hidden; }

.ap-pp__combo-modal__backdrop {
	position: absolute;
	inset: 0;
	/* Light dim scrim — see `feedback_backdrops_use_light_token`. */
	background: var(--backdrop-background-light, rgba(10, 10, 10, 0.30));
	cursor: pointer;
	opacity: 0;
}
.ap-pp__combo-modal.is-open .ap-pp__combo-modal__backdrop {
	opacity: 1;
}

.ap-pp__combo-modal__drawer {
	position: absolute;
	top: 0;
	right: 0;
	bottom: 0;
	width: calc(100% - var(--grid-col8-inset, 1123px));
	display: flex;
	flex-direction: column;
	min-width: 0;
	padding: 64px; /* 64px inset all around; bottom inset keeps the CTA off
	                  the drawer edge at the end of the scroll run */
	box-sizing: border-box;
	/* Scroll contract: with real data (3+ garments) the content outgrows
	   the drawer. The drawer itself is the scroll region — list, divider,
	   price row and CTA all scroll together as one column. */
	overflow-y: auto;
	background: var(--drawer-background, var(--basic-white, #FFF));
	transform: translateX(100%);
	transition: transform 360ms cubic-bezier(0.4, 0, 0.2, 1);
	will-change: transform;
}
.ap-pp__combo-modal.is-open .ap-pp__combo-modal__drawer {
	transform: translateX(0);
}

@media (prefers-reduced-motion: reduce) {
	.ap-pp__combo-modal__drawer { transition: none; }
}

/* Combo list — each row pairs a 160×212 product image (left) with a
   content column (right): title in `.type-heading-md` (Special Gothic
   Expanded One 32/1.1, 0.025em tracking, uppercase) sitting 8px below
   the row top, then the size selector 26px below the title. */
.ap-pp__combo-modal__list {
	list-style: none;
	margin: 0;
	padding: 0;
	display: flex;
	flex-direction: column;
	gap: 32px;
	flex: 0 0 auto; /* the drawer scrolls, the list keeps its natural height */
}
/* Flex items shrink to their content floor before the drawer overflows;
   the 1px divider's floor is 0, so it would silently collapse. Pin the
   in-flow children to their natural height and let the drawer scroll. */
.ap-pp__combo-modal__drawer > .ap-pp__divider,
.ap-pp__combo-modal__price-row {
	flex: 0 0 auto;
}
.ap-pp__combo-modal__row {
	display: flex;
	align-items: flex-start;
	gap: 24px;
}
.ap-pp__combo-modal__row__image {
	flex: 0 0 160px;
	width: 160px;
	height: 212px;
	background-size: cover;
	background-position: center;
	background-repeat: no-repeat;
}
.ap-pp__combo-modal__row__content {
	flex: 1 1 auto;
	min-width: 0;
}
.ap-pp__combo-modal__list .ap-pp__combo-modal__row__title {
	margin: 8px 0 0; /* 8px from row top to title's line-box top */
	color: var(--text-primary, #191919);
}
/* Sizes block reuses the global `.ap-pp__sizes-head` rule, whose
   `margin-top: 24px` controls the gap from the preceding element. In
   the combo modal that gap is 26px (per spec), so override here. */
.ap-pp__combo-modal__row .ap-pp__sizes-head {
	margin-top: 26px;
}

/* Combo price row — sits 32px below the divider above (provided by
   `margin-top` here). Label "Komplekto kaina" pinned to the left in
   `.type-heading-sm-medium`; on the right, the current price 16px to
   the left of the strike-through old price, both in `.type-price-md`.
   Baseline alignment ties the 20px label to the 32px prices cleanly. */
.ap-pp__combo-modal__price-row {
	display: flex;
	justify-content: space-between;
	align-items: baseline;
	margin-top: 32px;
}
.ap-pp__combo-modal__price-title {
	margin: 0;
	color: var(--text-tertiary, #787878);
	text-transform: none;
}
.ap-pp__combo-modal__prices {
	display: flex;
	align-items: baseline;
	gap: 16px;
}
.ap-pp__combo-modal__price { color: var(--text-primary, #191919); }
.ap-pp__combo-modal__price--strike {
	color: var(--text-tertiary, #787878);
	text-decoration: line-through;
}

/* Add-to-cart inside the combo modal: 24px below the price row. The
   `.ap-pp__add-to-cart` token owns sizing, colour and type — but its
   global `flex: 1 0 0` is intended for `.ap-pp__buy-row` (a row flex)
   and inside the drawer's column-flex parent it would stretch the
   button vertically. Pin it back to its natural 60px. */
.ap-pp__combo-modal__drawer .ap-pp__add-to-cart {
	flex: 0 0 auto;
	margin-top: 24px;
}


/* Mobile body subset for the product page (chrome mobile rules in chrome-styles.php). */
@media (max-width: 768px) {
	.ap-pp { grid-template-columns: 1fr; margin-bottom: 0; }

	.ap-pp__gallery {
		gap: 0;
	}
	/* Hide ALL the direct desktop-stack images and the 2-up row on
	   mobile — `.ap-pp__gallery__swiper` (below) replaces them with a
	   swipeable carousel. */
	.ap-pp__gallery > .ap-pp__gallery__image,
	.ap-pp__gallery__row {
		display: none;
	}

	/* Mobile gallery swiper — single full-width slide, 9:16 portrait,
	   cover-cropped per slide. `display: block` reveals the wrapper
	   (default-hidden above). `height: auto` is critical: Swiper's own
	   `.swiper { height: 100% }` in the bundle CSS otherwise wins
	   (different property than aspect-ratio, so they don't compete —
	   the explicit `height` is applied and `aspect-ratio` is ignored).
	   With `height: auto` the aspect-ratio drives the computed height
	   from the 100% width. */
	.ap-pp__gallery__swiper {
		display: block;
		width: 100%;
		height: auto;
		aspect-ratio: 9 / 16;
	}
	.ap-pp__gallery__swiper .swiper-slide img {
		display: block;
		width: 100%;
		height: 100%;
		object-fit: cover;
	}

	/* Scoped to the PDP summary — the tenant list page (.ap-tpl) shares
	   this stylesheet and keeps its mobile breadcrumbs, whose top margin
	   owns the header → content gap there. */
	.ap-pp__summary .ap-breadcrumbs { display: none; }

	/* Back button hidden on mobile — global header back navigation will
	   take over once the mobile header is wired up. */
	.ap-pp__back { display: none; }

	/* Zoom-in button on mobile — always visible (no hover on touch),
	   tighter 24px insets, and shrunk to 48×48 via explicit width/height
	   + 14px padding. The base rule outside this media query owns the
	   default pill/icon styling; this block only diffs what changes.
	   `cursor` is restored to the system default since on mobile the
	   pill is fixed (doesn't follow), so the no-cursor desktop trick
	   doesn't apply. */
	.ap-pp__gallery { cursor: auto; }
	.ap-pp__zoom {
		top: 24px;
		right: 24px;
		width: 48px;
		height: 48px;
		padding: 14px;
		opacity: 1;
		pointer-events: auto;
		cursor: pointer;
	}

	/* Image indicators — anchored to the bottom-center of the gallery,
	   24px above the bottom edge. Single horizontal row of pill bars
	   (one per image slot, 4px between bars), centered.

	   Selector is scoped under `.ap-pp__gallery` to bump specificity to
	   (0,2,0) — Swiper's bundle CSS adds
	   `.swiper-pagination-bullets.swiper-pagination-horizontal` classes
	   to our <ol> at runtime, which has the same (0,2,0) specificity
	   and would otherwise win for `bottom`, `left`, and `width`
	   (Swiper defaults: `bottom: 10px; left: 0; width: 100%`).

	   Instead of fighting Swiper's `width: 100%`, we let it stand and
	   use `justify-content: center` to center the bullet row inside the
	   full-width container — simpler than overriding it. */
	.ap-pp__gallery .ap-pp__indicators {
		position: absolute;
		bottom: 24px;
		left: 0;
		width: 100%;
		z-index: 2;
		display: flex;
		align-items: center;
		justify-content: center;
		gap: 4px;
		margin: 0;
		padding: 0;
		list-style: none;
	}
	.ap-pp__indicator {
		display: block;
		width: 24px;
		height: 2px;
		border-radius: var(--radius-999, 999px);
		background: var(--slider-background, #F0F0F0);
	}
	.ap-pp__indicator--active {
		background: var(--slider-pin-background, #191919);
	}

	/* Summary wrapper spans the full screen width on mobile, with a 24px
	   internal gutter so content doesn't sit flush against the screen
	   edges. Bottom 64px owns the details → collection gap (the
	   collection has no top margin/padding of its own, so the summary's
	   own bottom padding is the entire gap). Sticky positioning from the
	   desktop rule is reset here — mobile is a single-column stack with
	   the summary already below the gallery. */
	.ap-pp__summary {
		padding: 0 24px 64px;
		position: static;
		top: auto;
		align-self: auto;
	}

	/* Combo block hidden on mobile for now — the user's intended mobile
	   ordering is details → collection, with no combo between them. If
	   the combo should reappear after the collection (or anywhere else)
	   on mobile, drop this rule and either restructure the DOM so it can
	   sit outside `.ap-pp__summary`, or convert `.ap-sandbox` to a flex
	   column and use `order` to reposition. */
	.ap-pp__combo { display: none; }

	/* Hero → title gap = 24px (replaces the desktop 32px-from-breadcrumb
	   margin). The font-size drop is owned by the page-level token
	   overrides below — `.type-heading-xl` automatically picks up 34px
	   without us touching `font-size` here. */
	.ap-pp__title { margin: 24px 0 0; }

	/* Page-scoped mobile token overrides. Each one mirrors a row from
	   `assets/tokens/ago-tokens.css:399-401` (the brandbook's documented
	   responsive scale, currently commented out at `:root` level there).
	   By scoping to `.ap-sandbox` we get the brandbook's intended mobile
	   sizing for every typeset class used on this page (heading-xl,
	   price-lg, price-sm, heading-sm-light, heading-sm-medium) without
	   touching `font-size` on each element — letter-spacing in `em`
	   units scales automatically. `--font-size-44` is included for
	   completeness even though `.type-heading-lg` isn't on this page,
	   so anything new on the page inherits the right mobile size. When
	   the site-wide block is uncommented in the tokens file, this can
	   be removed. */
	.ap-sandbox {
		--font-size-58: 34px;
		--font-size-44: 34px;
		--font-size-22: 20px;
		--font-size-20: 16px;
	}

	/* Hide both summary dividers on mobile — section-to-section spacing
	   is owned by margins (24/32px) instead of horizontal rules. The
	   first divider sat between title and colour; the second between
	   delivery and details. Both go. */
	.ap-pp__summary > .ap-pp__divider { display: none; }

	/* Size options on mobile — 3-up grid that wraps to additional rows
	   as more sizes are added (5 sizes today → 3 on row 1, 2 on row 2
	   with the third slot empty). Row + column gap inherit the existing
	   8px from the desktop rule. The tiles already stretch on desktop
	   via `flex: 1`; switching the parent to grid keeps them stretching
	   into their grid cells. */
	.ap-pp__size-options {
		display: grid;
		grid-template-columns: repeat(3, 1fr);
	}

	/* Sizes → purchase gap = 24px on mobile (was 48px on desktop). The
	   margin lives on `.ap-pp__price-title` (first child of the purchase
	   wrapper), so overriding its `margin-top` is enough — collapsing
	   propagates the new value through the wrapper. */
	.ap-pp__price-title { margin-top: 24px; }

	/* Delivery → details gap = 32px on mobile. Desktop puts a divider
	   with `margin-top: 48px` between them; with that divider hidden,
	   we own the gap explicitly on the details wrapper. The
	   `border-top` replaces the top stroke that the now-hidden
	   `<hr class="ap-pp__divider">` used to provide above the first
	   accordion row — each `.ap-pp__details__item` has its own
	   `border-bottom` so the in-between strokes are already covered. */
	.ap-pp__details {
		margin-top: 32px;
		border-top: 1px solid var(--divider-background-light, #E4E4E4);
	}

	/* Hide the collection's prev/next nav buttons on mobile — touch users
	   swipe the slider directly. The header's `space-between` will leave
	   the title alone on the left. */
	.ap-pp__collection__nav { display: none; }

	/* Collection layout on mobile — the whole swiper zone sits on the
	   2-column grid: the scroll viewport spans 24px → vw−24px (padding-
	   left for the first-slide alignment, margin-right so the clip edge
	   lands on the grid margin instead of the screen edge — overflow
	   clips at the padding box, so a right *margin* is what moves the
	   clip line). Slides scrolling by clip at the right margin now
	   rather than peeking to the viewport edge. Title + nav-row keeps
	   its own 24px gutter so the title aligns with the summary content
	   above. The end-of-track resting position is the container edge
	   itself (`slidesOffsetAfter: 0` at the 0-breakpoint). */
	.ap-pp__collection { padding: 0 0 64px; }
	.ap-pp__collection__header { padding: 0 var(--grid-margin, 24px); }
	.ap-pp__collection__grid {
		padding-left: var(--grid-margin, 24px);
		margin-right: var(--grid-margin, 24px);
	}

	/* Scrollbar shrinks to a thinner 3px track + 3px drag on mobile.
	   Width of the drag is owned by Swiper (`dragSize: 88` at the 0
	   breakpoint); height is owned here. */
	.ap-pp__collection__grid .swiper-scrollbar,
	.ap-pp__collection__grid .swiper-scrollbar-drag {
		height: 3px;
	}

	/* The in-flow track spans the container's content box, which now
	   ends on the right grid margin (the container's own margin-right),
	   so 100% aligns the track with the summary content above. Swiper
	   reads the rendered track width to compute `(trackWidth - dragSize)
	   × progress`, so the drag's right edge lands flush with the track
	   end at scroll-end. */
	.ap-pp__collection__grid .swiper-scrollbar {
		width: 100%;
	}

	/* Lightbox controls reflow on mobile: close-button inset tightens
	   from 48 to 24, matching the mobile zoom button's positioning
	   convention. The pill also shrinks from 60×60 to 48×48 — padding
	   drops to 14px so 14+20(icon)+14 = 48. The AGO logo is
	   desktop-only — hidden here. */
	.ap-pp__lightbox__logo { display: none; }
	.ap-pp__lightbox__close { top: 24px; right: 24px; padding: 14px; }

	/* Cover the full mobile viewport. Portrait phones are taller than
	   landscape monitors relative to width, so the desktop
	   `width: 100%; height: auto` leaves whitespace below the image
	   when its natural aspect ratio isn't tall enough. `object-fit:
	   cover` with explicit `height: 100%` makes the image fill both
	   dimensions (cropping as needed).

	   The swiper's `height: 100%` has to be restored alongside it: the
	   desktop rule sets `height: auto` so tall photos can overflow and
	   scroll, and a percentage height on the image can't resolve
	   against an auto-height parent — the image would collapse back to
	   its natural ratio and the whitespace would return. Nothing
	   overflows here (the image is exactly viewport-height), so the
	   lightbox's `overflow-y: auto` stays inert on mobile. */
	.ap-pp__lightbox__swiper {
		height: 100%;
	}
	.ap-pp__lightbox__swiper .swiper-slide img {
		width: 100%;
		height: 100%;
		object-fit: cover;
	}

	/* Cart modal — desktop-only for now. Hide on mobile until the mobile
	   variant is designed; the JS still toggles `.is-open`, but the
	   `display: flex` above is suppressed here. Same for combo modal. */

	/* Combo modal — desktop-only for now (mobile pattern TBD). Cart + search hide live in chrome-styles.php. */
	.ap-pp__combo-modal,
	.ap-pp__combo-modal.is-open { display: none; }
}
