/**
 * Archive layout: the filter sidebar, the toolbar and the product grid.
 *
 * Loaded only on product archives — see inc/assets.php.
 */

/* ---------- archive head ---------- */

/* Title over breadcrumb, both centred, as on the live collection page. */
.gp-archive-head {
	margin-block-end: var(--wp--preset--spacing--50);
	text-align: center;
}

/* Same :where(.is-layout-constrained) > * fight as the crumbs below: global
   styles zero every layout child's margin-block-end at the same specificity
   this loses on load order, so the small gap meant to sit under the title
   needs !important too or it never survives. */
.gp-archive-head__title {
	margin-block: 0 var(--wp--preset--spacing--20) !important;
	font-size: 22px;
	font-weight: 600;
}

/* Global styles prints :root :where(.is-layout-constrained) > * with its own
   margin-block-start (the block-gap, 24px) at the same specificity as a
   plain class selector; a :where() carries zero specificity of its own but
   this sheet still loses the tie by loading first, so the crumbs sat a full
   block-gap below the title instead of the 8px the title's own margin-bottom
   already sets. !important is the only way to actually win it. */
.gp-archive-head__crumbs {
	margin: 0 !important;
	font-size: var(--wp--preset--font-size--base);
}

.gp-archive-head__crumbs a { color: var(--wp--preset--color--ink); }

/* WooCommerce sets its own smaller size on the nav inside. */
.gp-archive-head__crumbs .woocommerce-breadcrumb {
	margin: 0;
	font-size: var(--wp--preset--font-size--base);
}

/* The category-icons pattern right below the head carries its own 48px top
   padding (tuned for sitting under the homepage hero, where this same
   pattern is reused) plus another 24px block-gap ahead of it — 72px of
   whitespace that read as a gap in the page, not a breath, directly under a
   breadcrumb this tight. Trimmed to what the live page actually leaves. */
.gp-archive-head + .gp-section {
	padding-block-start: var(--wp--preset--spacing--40) !important;
}

/* ---------- toolbar ---------- */

/* A row spanning the full width above both columns. No rules above or below
   it: the live toolbar is unruled, and the border read as a divider the live
   page does not have. */
.gp-archive-toolbar {
	gap: var(--wp--preset--spacing--40);
	margin-block: var(--wp--preset--spacing--50) 0;
	padding-block: var(--wp--preset--spacing--30);
	font-size: var(--wp--preset--font-size--sm);
}

/* The live toolbar runs its labels bold, and greys the count back so the
   controls either side of it read first. */
.gp-archive-toolbar .woocommerce-result-count {
	font-weight: 600;
	color: rgba(0, 0, 0, 0.55);
}

.gp-archive-toolbar .gp-shop-toolbar__toggle {
	font-weight: 600;
	color: #000;
}

.gp-archive-toolbar .woocommerce-result-count { margin: 0; }

.gp-archive-toolbar .woocommerce-ordering { margin: 0; }

.gp-archive-toolbar__left,
.gp-archive-toolbar__right { gap: var(--wp--preset--spacing--40); }

/* An empty category has nothing for the price slider, the publisher/author
   lists or the stock toggle to count, so Product Filters renders every one
   of them as nothing — the rail was still there at its full 300px, just
   silently empty, which read as a missing sidebar rather than an intentional
   one. The filter toggle is equally pointless with nothing behind it to
   show. woocommerce/product-collection-no-results only exists in the markup
   at all when the query came back empty, so :has() reads that as the signal
   without a class from either block. */
body:has(.gp-product-grid [data-block-name="woocommerce/product-collection-no-results"]) .gp-filters,
body:has(.gp-product-grid [data-block-name="woocommerce/product-collection-no-results"]) .gp-shop-toolbar {
	display: none;
}

.gp-archive-toolbar__sort-label {
	margin: 0;
	/* Live runs this at the body size and bold, a step up from the row. */
	font-size: var(--wp--preset--font-size--base);
	font-weight: 600;
	color: #000;
	white-space: nowrap;
}

/*
 * The rail collapses rather than disappearing: the grid keeps its place and
 * simply takes the width back, which is what the live toggle does.
 *
 * display:none cannot be animated, so above the breakpoint where the toggle
 * exists the rail keeps its box and gives up its width instead — flex-basis to
 * zero, plus a negative end margin that swallows the column gap, so the grid
 * finishes exactly where display:none used to put it. visibility follows at the
 * end of the slide, which takes the collapsed rail out of the tab order and off
 * the screen reader without cutting the animation short.
 *
 * The two lengths repeat what archive-product.html declares: the column's 300px
 * width and the columns block's 40px gap. The template keeps them so the editor
 * draws the rail at the right size, and they are restated here because CSS
 * cannot read an inline style — and because the rail's contents have to hold
 * their width while it shuts, or every label inside would rewrap on the way out.
 *
 * Below 992px there is no toggle (see the rule that hides it further down), and
 * WooCommerce puts the facets in a drawer rather than a rail. The class can
 * still arrive there from a choice made on a wider screen, so the rail still
 * has to go — but instantly, and without an overflow that could clip the
 * drawer.
 */
@media (min-width: 992px) {
	.gp-archive-columns > .gp-filters {
		--gp-rail-width: 300px;
		--gp-rail-gap: 40px;
		overflow: hidden;
		transition:
			flex-basis 320ms cubic-bezier(0.4, 0, 0.2, 1),
			margin-inline-end 320ms cubic-bezier(0.4, 0, 0.2, 1),
			opacity 180ms ease,
			visibility 0s linear 0s;
	}

	.gp-archive-columns.is-filters-hidden > .gp-filters {
		/* Important because the width is an inline style on the column. */
		flex-basis: 0 !important;
		min-inline-size: 0;
		margin-inline-end: calc(-1 * var(--gp-rail-gap));
		opacity: 0;
		visibility: hidden;
		transition:
			flex-basis 320ms cubic-bezier(0.4, 0, 0.2, 1),
			margin-inline-end 320ms cubic-bezier(0.4, 0, 0.2, 1),
			opacity 180ms ease,
			visibility 0s linear 320ms;
	}

	.gp-archive-columns > .gp-filters > .gp-filters__inner { inline-size: var(--gp-rail-width); }
}

@media (max-width: 991px) {
	.gp-archive-columns.is-filters-hidden > .gp-filters { display: none; }
}

@media (prefers-reduced-motion: reduce) {
	.gp-archive-columns > .gp-filters,
	.gp-archive-columns.is-filters-hidden > .gp-filters {
		transition: opacity 100ms linear, visibility 0s;
	}
}

/* The browser's own select chrome is nothing like the live control; this is
   the live pill, with the caret drawn as a background image because a select
   cannot carry a pseudo-element.

   Two selectors, not one: WooCommerce sizes every control on the page from
   .woocommerce-page .input-text, ... which outranks a lone class here and was
   pulling the sort control back down to its own small preset. */
.gp-archive-toolbar select,
.woocommerce-page .gp-archive-toolbar select {
	padding: 8px 40px 7px 14px;
	border: 0;
	border-radius: 8px;
	/* Not a bordered white pill: live tints the ground and draws no outline. */
	background-color: rgba(239, 244, 242, 0.31);
	background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="%23040404" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m6 9 6 6 6-6"/></svg>');
	background-repeat: no-repeat;
	background-position: right 10px center;
	background-size: 16px 16px;
	font-family: inherit;
	font-size: var(--wp--preset--font-size--base);
	appearance: none;
}

/* The rail follows the grid down, as the live one does. align-self is what
   lets a sticky child work inside a flex column, which would otherwise
   stretch it to the row's full height and leave it nothing to stick within. */
.gp-filters {
	align-self: flex-start;
	position: sticky;
	inset-block-start: var(--wp--preset--spacing--40);
}

/* Live separates the facets with a hairline above each one rather than a gap,
   so the rail reads as one list. The gap goes; the rule comes from the facet
   padding below. */
.gp-filters__inner {
	display: flex;
	flex-direction: column;
	gap: 0;
}

.gp-filters h3 {
	margin: 0;
	/* The header group is a nowrap flex row; without this the heading is only
	   as wide as its own text and the caret rides against the word instead of
	   the rail's edge. */
	flex: 1;
	inline-size: 100%;
	font-size: var(--wp--preset--font-size--base);
	font-weight: 600;
	color: #000;
}

/* The disclosure fills the heading so its caret can sit against the rail's
   right edge, which is where the live one is — not tucked against the text. */
.gp-filters h3 .gp-facet-toggle {
	display: flex;
	align-items: center;
	justify-content: space-between;
	inline-size: 100%;
	min-block-size: 34px;
	padding: 4px 0;
	font: inherit;
	color: inherit;
	text-align: start;
}

/* 15px of air above and below each facet, ruled off from the one before.
   The rule is on the top edge so the last facet does not trail one. */
.gp-filters .wp-block-woocommerce-product-filter-attribute,
.gp-filters .wp-block-woocommerce-product-filter-price,
.gp-filters .wp-block-woocommerce-product-filter-status {
	padding-block: 15px;
	border-block-start: 1px solid rgba(0, 0, 0, 0.2);
	border-block-end: 0;
}

.gp-filters__inner .wp-block-woocommerce-product-filter-attribute:first-child,
.gp-filters__inner .wp-block-woocommerce-product-filter-price:first-child,
.gp-filters__inner .wp-block-woocommerce-product-filter-status:first-child {
	border-block-start: 0;
	padding-block-start: 0;
}

/* Long Bengali author and publisher names would otherwise blow out the
   sidebar. Wrap rather than truncate — the whole name matters here. */
.gp-filters .wc-block-product-filter-checkbox-list__label {
	display: flex;
	align-items: center;
	/* 16px from the box to the name, as on the live rail. */
	gap: 16px;
	inline-size: 100%;
	min-block-size: 25px;
	font-size: 15px;
	line-height: 22px;
	overflow-wrap: anywhere;
}

/* Live draws a 20px square outlined at 70% black with a 3px radius, and
   leaves it unfilled when checked — only the tick appears, in the brand
   green. WooCommerce ships the box transparent on both edges, so nothing was
   drawn at all and the row read as a floating label. */
.gp-filters .wc-block-product-filter-checkbox-list__input-wrapper {
	inline-size: 20px;
	block-size: 20px;
	flex: none;
}

/* WooCommerce tints the box with a ::before at 10% black. The live box is
   unfilled — outline only — so the tint has to go, or every row reads as
   half-checked. */
.gp-filters .wc-block-product-filter-checkbox-list__input-wrapper::before {
	display: none;
}

.gp-filters input[type="checkbox"].wc-block-product-filter-checkbox-list__input {
	box-sizing: border-box;
	inline-size: 20px;
	block-size: 20px;
	margin: 0;
	border: 1px solid rgba(0, 0, 0, 0.7);
	border-radius: 3px;
	background: transparent;
	box-shadow: none;
	appearance: none;
	cursor: pointer;
}

/* Checked, the outline turns the brand green with the box still unfilled —
   that green edge is what reads as "on" on the live rail. */
.gp-filters input[type="checkbox"].wc-block-product-filter-checkbox-list__input:checked {
	border-color: var(--wp--preset--color--brand-green);
	background: transparent;
}

/* WooCommerce's tick is a 1.5-unit stroke in a 10-unit box, which renders
   as a thin hairline next to live's solid glyph. The element is kept — the
   block shows and hides it — but its own path is dropped and the live shape
   painted through a mask instead. */
.gp-filters .wc-block-product-filter-checkbox-list__mark {
	inset: 0;
	margin: auto;
	inline-size: 12px;
	block-size: 12px;
	color: var(--wp--preset--color--brand-green);
	background: currentColor;
	-webkit-mask: var(--gp-check) center / contain no-repeat;
	mask: var(--gp-check) center / contain no-repeat;
}

.gp-filters .wc-block-product-filter-checkbox-list__mark > * {
	display: none;
}

.gp-filters .wc-block-product-filter-checkbox-list__text-wrapper {
	display: flex;
	align-items: center;
	gap: 8px;
	inline-size: 100%;
	min-inline-size: 0;
	/* Stated again here, not inherited: WooCommerce scales this wrapper to
	   0.875em, which quietly took the name down to 13.125px. */
	font-size: 15px;
	line-height: normal;
}

/* The count belongs against the right edge, greyed back — live does not run it
   inline after the name, and does not bracket it. Zeroing the type on the
   wrapper drops the parentheses, which are its own text nodes; the number sits
   in a span of its own and is sized back up. */
.gp-filters .wc-block-product-filter-checkbox-list__count {
	margin-inline-start: auto;
	flex: none;
	font-size: 0;
	color: rgba(0, 0, 0, 0.55);
}

.gp-filters .wc-block-product-filter-checkbox-list__count > span {
	display: block;
	font-size: 15px;
	line-height: 22px;
}

/* ---------- price facet ---------- */

/* WooCommerce lays this out as a grid whose columns are sized by the two
   number inputs at their default width — 177px each, which is 366 plus the gap
   inside a 300px rail, so the track and the second field ran off the edge.
   Two shrinkable columns, and the track spanning both, keep it inside. */
.gp-filters .wc-block-product-filter-price-slider__content {
	/* Placed by hand rather than by WooCommerce's named areas. Leaving the
	   areas in place while restating the columns left a third, implicit track
	   holding most of the width, which squeezed the two fields to 39px. */
	grid-template-areas: none;
	grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
	/* The rail column clips its own overflow — see the collapse transition
	   further up this file — and the track below runs flush to this
	   container's own edges. A round handle centred on the track's end sticks
	   out past it by its own radius, so a track flush with a clipping
	   ancestor always has its handles cut off at both ends, not just cut off
	   when the rail happens to be mid-animation. Live insets its slider the
	   same 9px, for the same reason. */
	padding-inline: 10px;
}

.gp-filters .wc-block-product-filter-price-slider__range {
	grid-area: 1 / 1 / 2 / -1;
	inline-size: 100%;
}

.gp-filters .wc-block-product-filter-price-slider__left {
	grid-area: 2 / 1;
	min-inline-size: 0;
}

.gp-filters .wc-block-product-filter-price-slider__right {
	grid-area: 2 / 2;
	min-inline-size: 0;
}

/* Live sets the two amounts in tinted pills rather than bordered fields.
 *
 * The selector carries .gp-filter-price and [type="text"] to outweigh
 * WooCommerce's own `.wc-block-product-filter-price-slider .text
 * input[type=text]`, which is a 1px-bordered 60px box. That rule only began
 * to bite once the wrapper was given its proper wc-block-* class, and it
 * loads after the theme's CSS, so matching its specificity would not be
 * enough — hence one class more. max-inline-size is reset because its
 * `max-width: 60px` survives our inline-size on its own. */
.gp-filters .gp-filter-price .wc-block-product-filter-price-slider__left input[type="text"],
.gp-filters .gp-filter-price .wc-block-product-filter-price-slider__right input[type="text"] {
	box-sizing: border-box;
	inline-size: 100%;
	max-inline-size: none;
	block-size: 44px;
	padding: 0 14px;
	border: 0;
	border-radius: 22px;
	background: rgba(0, 0, 0, 0.05);
	font-size: 14px;
	text-align: center;
	color: #000;
}

/* The track is a faint grey rule with the chosen span drawn over it in the
   brand green, and round green handles — not the browser's black bar. */
.gp-filters .wc-block-product-filter-price-slider__range {
	--range-color: var(--wp--preset--color--brand-green);
	background: rgba(0, 0, 0, 0.15);
	border-radius: 2px;
}

.gp-filters .wc-block-product-filter-price-slider__range-bar {
	background: var(--wp--preset--color--brand-green);
	border-radius: 2px;
}

.gp-filters .wc-block-product-filter-price-slider__range input[type="range"]::-webkit-slider-thumb {
	inline-size: 18px;
	block-size: 18px;
	border: 0;
	border-radius: 50%;
	background: var(--wp--preset--color--brand-green);
	appearance: none;
	cursor: pointer;
}

.gp-filters .wc-block-product-filter-price-slider__range input[type="range"]::-moz-range-thumb {
	inline-size: 18px;
	block-size: 18px;
	border: 0;
	border-radius: 50%;
	background: var(--wp--preset--color--brand-green);
	cursor: pointer;
}

/* ---------- facet lists ---------- */

/* Live sets the rows 8px apart inside a list that is inset 15px from the
   heading and 10px from the facet's foot — a 33px pitch against our 26. The
   rows were sitting flush and the rail read as a dense block. */
.gp-filters .wc-block-product-filter-checkbox-list__items {
	display: flex;
	flex-direction: column;
	gap: 8px;
}

.gp-filters .wc-block-product-filter-checkbox-list__item {
	display: flex;
	min-block-size: 25px;
}

/* The facet lists get long once the real catalogue lands: six publishers is
   fine, but hundreds of authors is not. Cap and scroll. */
.gp-filters .wc-block-product-filter-checkbox-list {
	padding-block: 15px 10px;
	max-block-size: 260px;
	overflow-y: auto;
	overscroll-behavior-y: contain;
}

@media (max-width: 900px) {
	.gp-filters { flex-basis: 100% !important; position: static; }

	/*
	 * The wrapper has to be told to wrap, not only the column to fill the
	 * row. Core pins `flex-wrap: nowrap !important` on every columns block
	 * from 782px up, so between there and here the first column took the
	 * whole width and the second was left at zero — its contents spilled
	 * out of the page instead of dropping to a line of their own. Below
	 * 782px core wraps them itself, which is why only this band broke.
	 */
	.gp-archive-columns { flex-wrap: wrap !important; }

	.gp-filters .wc-block-product-filter-checkbox-list { max-block-size: 200px; }
}

/* The grid stretches its <li> items to the row height, but the card sits two
   levels down — inside the template-part wrapper — and a block-level wrapper
   with auto height breaks the chain, so height:100% on the card resolves to
   its own content. Making both links in the chain flex passes the stretch
   through, which is what keeps cards in a row the same height. */
.gp-product-grid .wc-block-product,
.gp-product-grid .wp-block-post {
	display: flex;
}

.gp-product-grid .wc-block-product > .wp-block-template-part,
.gp-product-grid .wp-block-post > .wp-block-template-part {
	display: flex;
	flex: 1;
}

/* The Product Collection block lays the grid out itself; this only tightens
   the gaps and steps the column count down on narrow screens. */
.gp-product-grid .wc-block-product-template,
.gp-product-grid .wp-block-post-template {
	gap: var(--wp--preset--spacing--40);
	margin: 0;
	padding: 0;
	list-style: none;
}

@media (max-width: 1000px) {
	.gp-product-grid .wc-block-product-template,
	.gp-product-grid .wp-block-post-template {
		grid-template-columns: repeat(3, minmax(0, 1fr)) !important;
	}
}

@media (max-width: 640px) {
	.gp-product-grid .wc-block-product-template,
	.gp-product-grid .wp-block-post-template {
		grid-template-columns: repeat(2, minmax(0, 1fr)) !important;
	}
}

/*
 * With the filter rail open the grid has the rail's width less to work with,
 * and four cards in it stop being cards: 157px each at 1024px on the shop's
 * 1280px container, against 237px with the rail collapsed — narrow enough
 * that the buy-row's two buttons clip. A fixed viewport breakpoint tuned to
 * one page's container width got this wrong on any page with a different
 * one — the search results page runs a 1440px container, so the same
 * viewport width left it with 171px cards. Reading the actual space next to
 * the rail with a container query instead of a viewport media query is what
 * makes this right on every container size at once.
 *
 * The lower bound is not decoration — the two-column rule below is (0,2,0) and
 * this is heavier, so without it this would hold three columns onto a phone.
 * The upper bound is the narrowest a container gets before four cards beside
 * an open rail are comfortable again.
 */
.gp-archive-columns { container-type: inline-size; }

@container (min-width: 641px) and (max-width: 1200px) {
	.gp-archive-columns:not(.is-filters-hidden) .gp-product-grid .wc-block-product-template,
	.gp-archive-columns:not(.is-filters-hidden) .gp-product-grid .wp-block-post-template {
		grid-template-columns: repeat(3, minmax(0, 1fr)) !important;
	}
}

/* ---------- view density ---------- */

/* gp/view-toggle sets this class. The Product Collection block writes its own
   column count inline, so the override has to be !important; it is scoped to
   the class the toggle adds, and only above the breakpoint where the grid has
   already stepped itself down. */
@media (min-width: 1001px) {
	.gp-product-grid.is-view-roomy .wc-block-product-template,
	.gp-product-grid.is-view-roomy .wp-block-post-template {
		grid-template-columns: repeat(3, minmax(0, 1fr)) !important;
	}
}

/* ---------- active filter chips ---------- */

/* Live runs these full width under the toolbar and rules a hairline beneath
   them, rather than stacking them down the filter rail. The block reads its
   state from a product-filters wrapper, so the row carries one of its own —
   the overlay chrome that wrapper also renders is hidden below. */
.gp-active-filters {
	padding-block: 20px;
	border-block-end: 1px solid rgba(0, 0, 0, 0.1);
}

/* Nothing selected: the block renders no chips, so the row should take no
   room and draw no rule. */
.gp-active-filters:not(:has(.wc-block-product-filter-removable-chips__item)) {
	padding-block: 0;
	border-block-end: 0;
}

.gp-active-filters .wc-block-product-filters__open-overlay { display: none; }

.gp-active-filters .wc-block-product-filter-removable-chips__items {
	gap: 0;
}

/* A 100px pill on a 5%-black ground with no outline, which is the live chip. */
.gp-active-filters .wc-block-product-filter-removable-chips__item {
	align-items: center;
	gap: 6px;
	/* Live wraps each pill in a 2px shell that its background does not paint:
	   that is what separates one wrapped row from the next, and it widens the
	   gap between neighbours to 19px. Same effect, in margin. */
	margin: 2px 17px 2px 2px;
	padding: 9px 14px;
	border: 0;
	border-radius: 100px;
	background: rgba(0, 0, 0, 0.05);
	font-size: 13px;
	font-weight: 400;
	line-height: 1.6;
	color: #000;
}

.gp-active-filters .wc-block-product-filter-removable-chips__label {
	line-height: inherit;
}

.gp-active-filters .wc-block-product-filter-removable-chips__remove {
	flex: none;
	inline-size: 10px;
	block-size: 13px;
	padding: 0;
	border: 0;
	background: none;
	color: #000;
	cursor: pointer;
}

.gp-active-filters .wc-block-product-filter-removable-chips__remove svg {
	inline-size: 10px;
	block-size: 13px;
}

/* Price facet subtitle — matches live's "Set your budget…" line. */
.gp-filters .gp-filter-price__desc {
	margin: 8px 0 16px;
	font-size: 14px;
	font-weight: 400;
	line-height: 1.2;
	color: rgba(0, 0, 0, 0.55);
}

/* Live's crumb underlines on hover at a 1px offset — the one place in the
   chrome where the pointer does draw a rule. */
.gp-archive-head__crumbs .woocommerce-breadcrumb a:hover {
	text-decoration: underline;
	text-underline-offset: 1px;
}

/* ---------- the filter drawer, below 992 ---------- */

/*
 * Live's collection page keeps the rail to itself above 991 and hands every
 * narrower screen a drawer: the toolbar carries a "Show filters" control
 * beside the product count, and the panel that opens holds the sort control
 * and the facets together — which is why live heads it "Filter and sort".
 *
 * WooCommerce's Product Filters block ships that drawer already, so none of it
 * is rebuilt here. Two things are changed: the breakpoint, which the block
 * puts at 600 and live puts at 991, and the chrome, which the block leaves
 * bare. gp/shop-toolbar's view.js moves the pieces; this dresses them.
 */

@media (max-width: 991px) {
	/* Our own toggle collapses the rail in place, which is a desktop idea —
	   live shows only the drawer trigger down here. */
	.gp-shop-toolbar__toggle { display: none; }

	/* The sort control and the label beside it are moved into the panel by
	   view.js; what is left in this group is the density toggle, which live
	   does not offer on a narrow screen either. */
	.gp-archive-toolbar__right { display: none; }

	/* Trigger left, product count right — live's bar. */
	.gp-archive-toolbar__left {
		flex: 1 1 100%;
		justify-content: space-between;
		inline-size: 100%;
	}

	/* The rail column now sits in the toolbar and holds only the trigger, so
	   the 300px basis the layout gives it has to go. !important because the
	   column carries its width inline and the rule at 900 sets its own. */
	.gp-archive-toolbar__left > .gp-filters {
		order: -1;
		flex: 0 0 auto !important;
		inline-size: auto !important;
		max-inline-size: none;
		padding: 0;
		/* Not only pointless in a toolbar: a sticky box is a stacking context
		   of its own at level 0, and the panel inside it — fixed, z-index 9999
		   — was being confined to it and painted under the header's nav, which
		   sits at level 2 in the page's own context. */
		position: static;
	}

	/* The trigger: live's funnel and label, at the toolbar's own weight. */
	.gp-filters .wc-block-product-filters__open-overlay {
		gap: 6px;
		font-size: 14px;
		font-weight: 600;
		color: #000;
	}

	.gp-filters .wc-block-product-filters__open-overlay span { padding: 0; }

	.gp-filters .wc-block-product-filters__open-overlay svg {
		inline-size: 18px;
		block-size: 18px;
	}

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

	/* Live slides it in from the left over a half-black scrim, at 90% of the
	   screen. The block animates it up from the bottom, so the transform is
	   restated on both states rather than only on the open one.

	   The transition belongs here, on the resting state, and not on the open
	   one: a transition declared only while open describes the way in, and the
	   way out is whatever the closed rule says — which was nothing, so the
	   panel vanished on the spot while the scrim took half a second to fade
	   behind it. Stated once here it runs in both directions.

	   The curve is the one used for panels that come in off an edge: almost
	   all of the distance is covered early and the last few pixels settle, so
	   the panel reads as being pushed rather than played back. The same 260ms
	   both ways: a quicker way out was tried, and a page that returns to full
	   brightness in 200ms is a page that looks like it flashed. */
	.gp-filters .wc-block-product-filters__overlay-dialog {
		inline-size: min(90%, 400px);
		inset-inline-end: auto;
		transform: translateX(-100%);
		background: var(--wp--preset--color--surface);
		transition: transform 260ms cubic-bezier(0.32, 0.72, 0, 1);
	}

	/*
	 * The scrim fades on opacity, not on its own background colour.
	 *
	 * Animating background-color repaints the darkened area every frame, and
	 * the darkened area is the whole page: every card on it — cover, border,
	 * shadow, price — redrawn a frame at a time for the length of the fade.
	 * That is the flash as the drawer closes. Not one bad frame: the grid
	 * being painted again and again while the black over it thins out.
	 *
	 * Opacity on a fixed element the compositor already holds as its own layer
	 * is handled without touching the page at all — what is underneath is
	 * painted once and the black sheet is faded off the top of it.
	 *
	 * The black is a layer of its own rather than the overlay's own colour:
	 * the panel is a descendant of the overlay, and fading the overlay itself
	 * would fade the panel along with it. The pseudo-element paints before any
	 * child, so it sits under the panel and over the page.
	 */
	.gp-filters .wc-block-product-filters__overlay { background-color: transparent; }

	.gp-filters .wc-block-product-filters__overlay::before {
		content: "";
		position: absolute;
		inset: 0;
		background-color: rgba(0, 0, 0, 0.5);
		opacity: 0;
		transition: opacity 260ms ease;
	}

	.gp-filters .wc-block-product-filters.is-overlay-opened .wc-block-product-filters__overlay::before {
		opacity: 1;
	}

	.gp-filters .wc-block-product-filters.is-overlay-opened .wc-block-product-filters__overlay-dialog {
		transform: translateX(0);
	}

	/* Title on the left, the close control on the right: the block's header is
	   row-reverse with nothing but the close button in it. */
	.gp-filters .wc-block-product-filters__overlay-header {
		flex-flow: row nowrap;
		align-items: flex-start;
		justify-content: space-between;
		gap: 16px;
		padding: 15px 20px;
		border-block-end: 1px solid rgba(0, 0, 0, 0.1);
	}

	.gp-filters .gp-filters__title {
		display: flex;
		flex-direction: column;
		gap: 2px;
		min-inline-size: 0;
	}

	.gp-filters .gp-filters__title-text {
		font-size: 28px;
		font-weight: 600;
		line-height: 1.1;
		color: var(--wp--preset--color--ink-heading);
	}

	.gp-filters .gp-filters__title-count {
		font-size: 13px;
		color: rgba(0, 0, 0, 0.6);
	}

	/* Live's close is the glyph alone; the block prints a label beside it. */
	.gp-filters .wc-block-product-filters__close-overlay span {
		position: absolute;
		inline-size: 1px;
		block-size: 1px;
		padding: 0;
		overflow: hidden;
		clip-path: inset(50%);
		white-space: nowrap;
	}

	.gp-filters .wc-block-product-filters__close-overlay {
		flex: none;
		margin-block-start: 4px;
		cursor: pointer;
	}

	.gp-filters .wc-block-product-filters__overlay-content {
		padding: 0 20px;
		overscroll-behavior-y: contain;
	}

	/* The sort control view.js moves in. No rule of its own beneath it: the
	   facet under it already draws one above itself, and two together read as
	   a double line. */
	.gp-filters .gp-filters__sort {
		display: flex;
		align-items: center;
		gap: 10px;
		flex-wrap: wrap;
		padding-block: 20px 0;
	}

	/* The rail keeps each facet's list in a scroller of its own so the column
	   does not run away down the page. The panel is already a scroller, and a
	   second one inside it is something to fight with a thumb. */
	.gp-filters .wc-block-product-filter-checkbox-list {
		max-block-size: none;
		overflow-y: visible;
	}

	.gp-filters .gp-filters__sort-label {
		font-size: var(--wp--preset--font-size--base);
		font-weight: 600;
		color: #000;
		white-space: nowrap;
	}

	.gp-filters .gp-filters__sort .woocommerce-ordering { margin: 0; }

	.gp-filters .gp-filters__sort select {
		padding: 8px 40px 7px 14px;
		border: 0;
		border-radius: 8px;
		background-color: rgba(239, 244, 242, 0.31);
		background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="%23040404" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m6 9 6 6 6-6"/></svg>');
		background-repeat: no-repeat;
		background-position: right 10px center;
		background-size: 16px 16px;
		font-family: inherit;
		font-size: var(--wp--preset--font-size--base);
		appearance: none;
	}

	/* ---------- the footer ---------- */

	/* Live closes the panel with two buttons of equal height: reset outlined,
	   apply filled, 10px apart. */
	.gp-filters .wc-block-product-filters__overlay-footer {
		display: flex;
		gap: 10px;
		padding: 15px 20px;
	}

	.gp-filters .gp-filters__reset,
	.gp-filters .wc-block-product-filters__apply {
		display: flex;
		align-items: center;
		justify-content: center;
		flex: 1;
		box-sizing: border-box;
		block-size: 62px;
		padding: 0 16px;
		border: 2px solid var(--wp--preset--color--brand-green);
		border-radius: 8px;
		font-family: inherit;
		font-size: 16px;
		font-weight: 600;
		text-decoration: none;
		cursor: pointer;
	}

	.gp-filters .gp-filters__reset {
		background: var(--wp--preset--color--surface);
		color: #002c22;
	}

	/* Nothing applied: live leaves the button in place and spent. */
	.gp-filters .gp-filters__reset[aria-disabled="true"] {
		border-color: rgba(6, 78, 59, 0.3);
		color: rgba(0, 44, 34, 0.4);
		pointer-events: none;
	}

	.gp-filters .wc-block-product-filters__apply {
		inline-size: auto;
		background: var(--wp--preset--color--brand-green);
		color: var(--wp--preset--color--surface);
	}

	.gp-filters .gp-filters__reset:hover,
	.gp-filters .wc-block-product-filters__apply:hover {
		background: var(--wp--preset--color--ink);
		border-color: #121212;
		color: var(--wp--preset--color--surface);
	}
}

/*
 * The block's own drawer stops at 600: above that width it lays the filters
 * out inline instead. These are its under-600 declarations restated for the
 * band between there and live's 991, so the drawer keeps working all the way
 * up. They are written against .gp-filters, which outranks the block's own
 * :where(...) rules whatever the order.
 */
@media (min-width: 601px) and (max-width: 991px) {
	.gp-filters .wc-block-product-filters__open-overlay,
	.gp-filters .wc-block-product-filters__overlay-header,
	.gp-filters .wc-block-product-filters__overlay-footer { display: flex; }

	.gp-filters .wc-block-product-filters__overlay {
		position: fixed;
		inset: 0;
		inset-block-start: var(--top-padding, 0px);
		flex-grow: 0;
		background-color: transparent;
		pointer-events: none;
		/* No transition here: the black is the pseudo-element above, and this
		   element's own colour never changes. */
		transition: none;
	}

	.gp-filters .wc-block-product-filters__overlay-wrapper {
		position: relative;
		inline-size: 100%;
		block-size: 100%;
	}

	.gp-filters .wc-block-product-filters__overlay-dialog {
		position: absolute;
		inset-block: 0;
		inset-inline-start: 0;
		max-block-size: 100%;
	}

	.gp-filters .wc-block-product-filters__overlay-content {
		flex-grow: 1;
		overflow-y: auto;
	}

	.gp-filters .wc-block-product-filters.is-overlay-opened .wc-block-product-filters__overlay {
		pointer-events: auto;
		z-index: 9999;
	}
}

@media (prefers-reduced-motion: reduce) {
	.gp-filters .wc-block-product-filters__overlay::before,
	.gp-filters .wc-block-product-filters__overlay-dialog {
		transition: none;
	}
}

/* The panel's own sort control is a copy of the toolbar's, and above the
   breakpoint the panel is not a panel at all — the block lays its contents out
   inline as the rail. The copy would show up under the facets there. */
@media (min-width: 992px) {
	.gp-filters .gp-filters__sort { display: none; }
}

/* ---------- infinite scroll ---------- */

/*
 * Live pages the collection by scrolling: a sentinel under the grid, a spinner
 * while the next page is on its way, and a line at the end saying there is no
 * more. The numbers stay in the markup for anyone without script and are what
 * the script reads each next address from — so they are hidden only once it
 * has taken over, and handed back if a fetch fails.
 */
/* The class goes on the body, not on the block: the block's class attribute is
   written back from the server whenever a facet re-renders it. */
body.gp-infinite-on .gp-product-grid .wp-block-query-pagination { display: none; }

/* Pages two and onward land in a list of their own under the block's, carrying
   the same classes so the same grid rules lay it out. The gap between the two
   is the grid's own, so the join does not read as a seam. */
.gp-infinite__wrap:has(.gp-infinite__list:empty) { display: none; }

.gp-infinite {
	display: flex;
	align-items: center;
	justify-content: center;
	min-block-size: 40px;
	margin-block-start: var(--wp--preset--spacing--50);
}

/* Idle is the resting state and shows nothing at all: the strip is a place for
   the page to notice it has been reached, not a control. */
.gp-infinite__spinner,
.gp-infinite__end { display: none; }

.gp-infinite[data-state="loading"] .gp-infinite__spinner { display: block; }

.gp-infinite[data-state="end"] .gp-infinite__end,
.gp-infinite[data-state="error"] .gp-infinite__end { display: block; }

.gp-infinite__spinner {
	inline-size: 26px;
	block-size: 26px;
	border: 2px solid rgba(0, 0, 0, 0.12);
	border-block-start-color: var(--wp--preset--color--brand-green);
	border-radius: 50%;
	animation: gp-infinite-spin 700ms linear infinite;
}

.gp-infinite__end {
	margin: 0;
	font-size: 14px;
	color: rgba(0, 0, 0, 0.65);
	text-align: center;
}

/* A failed fetch says nothing here — the pagination is back on the page by
   then, which is the answer the reader needs rather than a message. */
.gp-infinite[data-state="error"] .gp-infinite__end { display: none; }

@keyframes gp-infinite-spin {
	to { transform: rotate(360deg); }
}

@media (prefers-reduced-motion: reduce) {
	.gp-infinite__spinner { animation-duration: 2s; }
}
