/* The @layer wrapper below is applied in place by scripts/wrap-css-layer.js.
   Edit the rules freely — just leave the wrapper alone (re-running is a no-op).
   Everything below is inside @layer pqf-neutral, so ANY unlayered CSS
   (a consuming theme's) wins regardless of specificity or load order. */
@layer pqf-neutral {
	/**
 * Plain Query Filters — shared control language (the LAYERED half).
 *
 * ONE neutral recipe for every interactive control surface (popover trigger,
 * sort <select>, map-toggle button). Colorless by construction: every tone is
 * derived from currentColor via color-mix(), so the defaults are correct on any
 * palette — light, dark, or otherwise — without the plugin shipping a hex.
 *
 * SPLIT BY CONTRACT. Everything in this file is layered, which means it is a
 * DEFAULT: "a theme may repaint this". Paint, sizing, decoration, preference.
 * The recipe's other half — the neutralisers that must survive a foreign
 * `select {}` / `label {}` / `input {}` rule on someone else's site — is
 * deliberately UNLAYERED and lives in assets/css/defense.css (handle
 * `pqf-defense`). `.pqf-control` therefore spans two files: box-sizing, color,
 * font and line-height are over there; min-height, background, border, radius,
 * padding and cursor are here. Read that file's header before moving a
 * declaration in either direction — the boundary is a contract, not a
 * convenience.
 *
 * Theme override surface: the --pqf-control-* custom properties, or the
 * .pqf-control class itself. Browsers without color-mix() drop the invalid
 * border-color and fall back to the plain currentColor declaration above it —
 * a stronger but still correct border.
 *
 * THE SCALE TOKENS (canonical list — this is their documentation)
 * --------------------------------------------------------------
 * Space, radius, type and motion for the chrome the plugin RENDERS, read the
 * same way paint is: a --pqf-* hook first, a neutral literal behind it.
 *
 *   --pqf-space-xs / -sm / -md          gaps and padding inside plugin chrome
 *   --pqf-radius-sm / -md / -full       control, card and pill corners
 *   --pqf-font-size-2xs / -xs / -sm     chrome type (badges, tips, labels)
 *   --pqf-transition / --pqf-transition-slow   hover/state motion
 *
 * Every use carries the literal it had before as its fallback, so an install
 * that sets none of them renders exactly as it did.
 *
 * WHY NOT READ THE THEME'S PRESETS DIRECTLY. These read
 * `--wp--preset--spacing--x-small`, `--wp--preset--border-radius--medium`,
 * `--wp--preset--font-size--xs-plus` and `--wp--custom--transition--fast`
 * until 0.45.0 — none of which WordPress defines. Named spacing slugs are a
 * theme's invention (core's are numeric, 20–80), `radiusSizes` is opt-in, and
 * `--wp--custom--*` is by construction a theme's PRIVATE namespace. Every one
 * of them resolved on the one site the map was built for and fell back to the
 * literal on every other install — so the plugin looked considered there and
 * arbitrary everywhere else, and a customer inspecting the CSS was offered a
 * seam that did not exist for them. Exactly the defect 0.39.0 fixed for colour,
 * in the four namespaces that sweep did not cover. A --pqf-* token is one line
 * for a theme to set and it is the same seam on every site.
 *
 * The plugin's EDITOR sheets take the literal and no token at all: a dashed
 * placeholder box in wp-admin is the plugin's own furniture, and it should not
 * resize itself to a site's content typography.
 *
 * THE SECOND THING THIS FILE CARRIES
 * ----------------------------------
 * Further down: the navigation loading overlay, keyed on
 * `[data-wp-router-region][data-loading="true"]`. It is here rather than with
 * any one navigating block because it is REGION-level UI — the feedback a
 * control gives when you operate it — and it belongs to whichever blocks can
 * turn it on, which is more than one of them.
 *
 * That makes this sheet's dependency rule two-clause, and it is why
 * scripts/validate-contracts.js has to name a behavioural trigger alongside the
 * class ones: a block needs `pqf-controls` if it renders a class this file
 * styles OR if it binds `actions.navigate`, because nothing RENDERS
 * `data-loading` — src/shared/navigation-store.js sets it at runtime.
 * no-results is the case that proves the distinction: one link, no control
 * chrome, and it genuinely needs this stylesheet. Anything moved out of here
 * that a navigating block relies on must take that clause with it.
 */
	.pqf-control {
		/* Controls own their box math: this shared min-height floor keeps the whole
	   control row uniform even though a native <select> and an icon-bearing
	   button reach different natural heights. It only means the same thing on
	   both because of the border-box half of that box math, which is an
	   invariant and lives unlayered in assets/css/defense.css. */
		min-height: var(--pqf-control-min-height, 2.4em);
		background: var(--pqf-control-bg, transparent);
		border: 1px solid currentcolor; /* color-mix fallback */
		border-color: var(
			--pqf-control-border,
			color-mix(in srgb, currentColor 28%, transparent)
		);
		border-radius: var(--pqf-control-radius, 4px);
		cursor: pointer;
		padding: var(--pqf-control-padding, 0.4em 0.75em);
	}

	/* Optical centering: flexbox centers the label's LINE BOX, but text ink sits
   mostly above the baseline inside it, so geometrically centered text reads
   ~1px high next to icon/chevron glyphs. text-box trims the box to the ink —
   and the symmetric padding immediately gives the trimmed leading back, so:
   (a) what flexbox centers is the INK (padding is symmetric),
   (b) the label's layout height stays exactly 1lh — control heights remain
       uniform across trigger/select/toggle and width-ghost siblings, and
   (c) the padding doubles as descender headroom if a theme clips the label
       with overflow:hidden for ellipsis truncation (clipping happens at the
       padding box, so g/y descenders keep painting).
   Browsers without text-box/lh/cap keep plain line-box centering. */
	@supports (text-box: trim-both cap alphabetic) and
		(padding: calc((1lh - 1cap) / 2)) {
		.pqf-control__label {
			text-box: trim-both cap alphabetic;
			padding-block: calc((1lh - 1cap) / 2);
		}
	}

	/* Text-entry fields (typeahead search, range number inputs) share the control
   recipe but are typed into, not clicked: text cursor, and no hover wash — a
   `background` shorthand on hover would also wipe a theme's background-image
   (e.g. a magnifier icon) and a field's affordance is its caret, not a wash.
   :where() keeps the exclusion at the base (0,2,0) specificity. That's no
   longer what lets a theme's hover override win — this file is layered, so
   an unlayered theme rule wins regardless of specificity — but the low
   specificity is kept anyway so other layered rules can still out-specify it
   without a fight. */
	.pqf-control--field {
		cursor: text;
	}

	@media (hover: hover) {
		.pqf-control:hover:where(:not(.pqf-control--field)) {
			/* Two-tier naming, most- to least-specific:
		   1. --pqf-hover-bg — per-instance var written inline by render.php
		      (filter-popover / active-filters) for a single control;
		   2. --pqf-control-hover-bg — theme-facing override for all controls;
		   3. the color-mix() derivation — colorless default fallback. */
			background: var(
				--pqf-hover-bg,
				var(
					--pqf-control-hover-bg,
					color-mix(in srgb, currentColor 7%, transparent)
				)
			);
		}
	}

	.pqf-control:focus-visible {
		outline: 2px solid currentcolor;
		outline-offset: 2px;
	}

	/* Customizable Select — shared STRUCTURE for the sort <select>'s open picker.
 *
 * The sort <select> carries `.pqf-control` (render.php), so its trigger box —
 * border, radius, background, padding, cursor, focus ring from the recipe above,
 * plus font/color/box-sizing/line-height from the unlayered defensive half in
 * assets/css/defense.css — is already fully described. In browsers with
 * `appearance: base-select` the
 * <select> button AND its dropdown picker become real styleable boxes; the rules
 * below give that picker the SAME neutral structure the filter-popover panel
 * ships (src/filter-popover/style.css), driven by the SAME `--pqf-panel-*` hooks
 * with the SAME defaults — so the open sort menu matches the popover panel with
 * zero theme CSS (e.g. the naked QA harness). STRUCTURE only lives here; every
 * color / tint / shadow accent is the consuming theme's to paint. Browsers
 * without base-select keep the native widget (nothing below applies). The
 * block's own chevron / leading-icon masks + field-sizing stay in the block's
 * style.css — this section deliberately does not restate them. */
	@supports (appearance: base-select) {
		.wp-block-plainplugins-sort-dropdown__select,
		.wp-block-plainplugins-sort-dropdown__select::picker(select) {
			appearance: base-select;
		}

		/* Trigger — minimal structure; the box paint comes from `.pqf-control`,
	   whose `font: inherit` is already declared UNLAYERED in
	   assets/css/defense.css. Restating it here bought nothing: the select
	   carries `.pqf-control` (render.php), and a layered restatement could never
	   win anything the unlayered original did not already win. */
		.wp-block-plainplugins-sort-dropdown__select {
			display: inline-flex;
			align-items: center;
			gap: 0.4em;
			cursor: pointer;
		}

		/* Open picker — same var-based default paint as the filter-popover panel, on
	   the SAME `--pqf-panel-*` hooks with the SAME neutral defaults, so a theme
	   that maps the panel once covers both dropdowns. */
		.wp-block-plainplugins-sort-dropdown__select::picker(select) {
			margin-top: 6px;

			/* The UA ships ::picker(select) with a visible 1px border the popover
		   panel doesn't have — reset it to the shared hook (default none) so the
		   two panels' chrome can't drift. */
			border: var(--pqf-panel-border, none);

			/* The picker is the options' inheritance parent and the UA paints it
		   canvastext (pure black) — inherit from the select so option text
		   matches the trigger / filter items (option's own color:inherit below
		   resolves through THIS box, not the select). */
			color: inherit;
			background: var(--pqf-panel-bg, #fff);
			border-radius: var(--pqf-panel-radius, 4px);
			box-shadow: var(--pqf-panel-shadow, 0 4px 12px rgba(0, 0, 0, 0.15));
			padding: var(--pqf-panel-padding, 12px);
		}

		/* Option rows — flex so the native ::checkmark can sit at the row end as a
	   single-select affordance. MENU-ROW PARITY: padding, line-height, hover wash
	   and the bold active cue below all mirror the popover panel's filter rows
	   (the popover half lives in filter-popover/style.css via the
	   --pqf-panel-item-* defaults), so both open menus read identically on an
	   unthemed install. The line-height re-widens the rows from the trigger's
	   compact 1.2 (inherited via .pqf-control — declared in the unlayered
	   assets/css/defense.css, not in this file) to the list leading the filter
	   items use. */
		.wp-block-plainplugins-sort-dropdown__select option {
			display: flex;
			align-items: center;
			gap: 0.5em;
			padding: 0.35em 0.75em;
			font: inherit;

			/* The UA paints options canvastext (pure black) instead of inheriting —
		   inherit so option rows match the filter items' text color exactly. */
			color: inherit;
			line-height: 1.4;
			cursor: pointer;
		}

		/* Hover wash — the same var chain as .pqf-control:hover, so an unthemed
	   install gets the same faint currentColor mix the popover's rows default to,
	   and a theme's --pqf-control-hover-bg covers this too. The UA's own
	   base-select hover paint (browser-specific) is replaced so the two menus
	   can't drift. */
		@media (hover: hover) {
			.wp-block-plainplugins-sort-dropdown__select option:hover {
				background: var(
					--pqf-hover-bg,
					var(
						--pqf-control-hover-bg,
						color-mix(in srgb, currentColor 7%, transparent)
					)
				);
			}
		}

		/* Selected row — the same shape cue (not color-only, WCAG 1.4.1) the filter
	   blocks put on __item--active; the ::checkmark below is the select-specific
	   bonus affordance. */
		.wp-block-plainplugins-sort-dropdown__select option:checked {
			font-weight: 600;
		}

		/* Native checkmark — pushed to the row end (single-select affordance),
	   colorless (currentColor); themes tint it. */
		.wp-block-plainplugins-sort-dropdown__select option::checkmark {
			order: 1;
			margin-inline-start: auto;
		}
	}

	/* Navigation loading feedback on the query region (functional). Colors are kept
   neutral (currentColor / translucent white) so the theme can override freely.
   GLOBAL UI toggled by the shared navigation store — lives here, not with any
   single navigating block, so it ships wherever a pqf-controls block appears. */
	[data-wp-router-region][data-loading="true"] {
		position: relative;
	}

	[data-wp-router-region][data-loading="true"]::after {
		content: "";
		position: absolute;
		inset: 0;
		background: rgba(255, 255, 255, 0.6);
		z-index: 10;
		pointer-events: none;
	}

	[data-wp-router-region][data-loading="true"]::before {
		content: "";
		position: absolute;
		top: 50%;
		left: 50%;
		width: 24px;
		height: 24px;
		margin: -12px 0 0 -12px;
		border: 2px solid currentcolor;
		border-top-color: transparent;
		border-radius: 50%;
		opacity: 0.6;
		z-index: 11;
		animation: pqf-spin 0.8s linear infinite;
	}

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

	@media (prefers-reduced-motion: reduce) {
		[data-wp-router-region][data-loading="true"]::before {
			animation: none;
		}
	}

	/* List-filter block style variants — shared NEUTRAL structure.
 *
 * The three list-type filter blocks (taxonomy-filter, meta-filter,
 * post-type-filter) expose WP-native block styles (block.json `styles`): Text
 * (default, plain link rows — untouched here), Pills, Buttons, Underline. This
 * section gives each __item its per-variant SHAPE only — inline box, radius,
 * separator underline — with zero paint: every tone is a currentColor mix so the
 * defaults read on any palette and a consuming theme repaints wholesale. The
 * global active cue (font-weight, in each block's style.css) still applies.
 *
 * Selectors name the three blocks explicitly (no [class*=] matching); the
 * is-style-* class lands on the block root in BOTH the front end and the editor
 * canvas, so these rules cover both. Specificity is kept at (0,3,0) via an
 * :is(.is-style-pills, .is-style-buttons) group on the root — enough to sit
 * above the block's own neutral base. A theme doesn't need it kept low to
 * override: this whole file is layered, so any unlayered theme rule wins
 * regardless of specificity. */

	/* Pills + Buttons — an inline bordered chip. Buttons differ only in radius.
   The `text-decoration: none` these two variant rules used to restate now comes
   once from `.pqf-link` in assets/css/defense.css, unlayered — every __item is
   an anchor and carries that class. */
	.wp-block-plainplugins-taxonomy-filter:is(
			.is-style-pills,
			.is-style-buttons
		)
		.wp-block-plainplugins-taxonomy-filter__item,
	.wp-block-plainplugins-meta-filter:is(.is-style-pills, .is-style-buttons)
		.wp-block-plainplugins-meta-filter__item,
	.wp-block-plainplugins-post-type-filter:is(
			.is-style-pills,
			.is-style-buttons
		)
		.wp-block-plainplugins-post-type-filter__item {
		display: inline-flex;
		align-items: center;
		padding: 0.35em 0.9em;
		border: 1px solid color-mix(in srgb, currentcolor 25%, transparent);
		border-radius: 999px;
	}

	.wp-block-plainplugins-taxonomy-filter.is-style-buttons
		.wp-block-plainplugins-taxonomy-filter__item,
	.wp-block-plainplugins-meta-filter.is-style-buttons
		.wp-block-plainplugins-meta-filter__item,
	.wp-block-plainplugins-post-type-filter.is-style-buttons
		.wp-block-plainplugins-post-type-filter__item {
		border-radius: 2px;
	}

	/* Active chip — faint currentColor wash + a solid-currentColor border. */
	.wp-block-plainplugins-taxonomy-filter:is(
			.is-style-pills,
			.is-style-buttons
		)
		.wp-block-plainplugins-taxonomy-filter__item--active,
	.wp-block-plainplugins-meta-filter:is(.is-style-pills, .is-style-buttons)
		.wp-block-plainplugins-meta-filter__item--active,
	.wp-block-plainplugins-post-type-filter:is(
			.is-style-pills,
			.is-style-buttons
		)
		.wp-block-plainplugins-post-type-filter__item--active {
		background: color-mix(in srgb, currentcolor 10%, transparent);
		border-color: currentcolor;
	}

	/* Underline — a text row with a transparent 2px rule that fills when active. */
	.wp-block-plainplugins-taxonomy-filter.is-style-underline
		.wp-block-plainplugins-taxonomy-filter__item,
	.wp-block-plainplugins-meta-filter.is-style-underline
		.wp-block-plainplugins-meta-filter__item,
	.wp-block-plainplugins-post-type-filter.is-style-underline
		.wp-block-plainplugins-post-type-filter__item {
		padding: 0.15em 0;
		border-bottom: 2px solid transparent;
	}

	.wp-block-plainplugins-taxonomy-filter.is-style-underline
		.wp-block-plainplugins-taxonomy-filter__item--active,
	.wp-block-plainplugins-meta-filter.is-style-underline
		.wp-block-plainplugins-meta-filter__item--active,
	.wp-block-plainplugins-post-type-filter.is-style-underline
		.wp-block-plainplugins-post-type-filter__item--active {
		border-bottom-color: currentcolor;
	}

	/* Tabs — a SEGMENTED CONTROL, which is a different object from the other three
   variants and not just another chip shape. Pills/Buttons/Underline decorate
   items that stay in a wrapping list; Tabs makes the list itself one surface:
   square cells butted edge to edge with no gap, divided by a hairline, on a
   single line that SCROLLS instead of wrapping.
 *
 * WHY A NEW VARIANT AND NOT A PILLS TWEAK. `is-style-pills` is a published
 * block style — it is in three block.json `styles` arrays, so every install of
 * this plugin offers "Pills" in the editor's Styles panel and an unknown number
 * of sites have chosen it. Repainting it would be a silent breaking change on
 * every one of them. Adding a style is additive and reversible; that is the
 * whole reason this section exists rather than a diff to the rule above.
 *
 * THE THREE SHAPE DECISIONS, none of which are optional for a tab strip:
 *
 *   1. The row is SHRINK-TO-FIT and self-centring (`width: fit-content` +
 *      `margin-inline: auto`) rather than a full-width flex row that centres its
 *      items. Those two look identical until the tabs stop fitting, and then
 *      they diverge badly: a full-width row with `justify-content: center`
 *      overflows in BOTH directions, so the first tab sits at a negative offset
 *      and no amount of scrolling can reach it. A fit-content row clamped by
 *      `max-width: 100%` starts its content at the inline start and scrolls
 *      cleanly to the end. The matching `justify-content: flex-start` guard is
 *      unlayered in assets/css/defense.css — see that file for why it cannot
 *      live here.
 *   2. `scrollbar-width: none`. A classic scrollbar under a tab strip adds ~15px
 *      of chrome to a control whose whole job is to be one crisp band, and it
 *      only appears on the desktop widths where the tabs already fit. The tabs
 *      remain scrollable by touch, wheel, and keyboard focus.
 *   3. The divider hangs off the INLINE-START of every item after the first
 *      (`__item + __item`) rather than the inline-end of every item but the
 *      last. `:last-child` is wrong here: with `searchable` the block also
 *      renders a no-results row after the items, so the last ITEM is not the
 *      last CHILD and the trailing divider would survive. The adjacent-sibling
 *      form cannot be fooled by anything rendered before or after the list.
 *
 * PAINT. Same contract as the rest of this file — these are DEFAULTS. The
 * elevation and the divider are the two the variant cannot do without (a
 * segmented card with neither reads as loose text on desktop backgrounds), so
 * they ship with neutral literals behind `--pqf-tabs-shadow` / `--pqf-tabs-
 * divider`, exactly as `--pqf-panel-shadow` / `--pqf-panel-bg` already do for
 * the popover. Everything else — the surface colour, the outer radius, the
 * label weight — is deliberately NOT set here: those arrive per-instance from
 * the block's own color / border / typography supports, which WordPress
 * serialises inline onto the wrapper and which therefore beat this file
 * whatever it said. */
	.wp-block-plainplugins-taxonomy-filter.is-style-tabs,
	.wp-block-plainplugins-meta-filter.is-style-tabs,
	.wp-block-plainplugins-post-type-filter.is-style-tabs {
		width: fit-content;
		max-width: 100%;
		margin-inline: auto;
		overflow-x: auto;
		overflow-y: hidden;
		scrollbar-width: none;

		/* The shared scale token, not a `--pqf-tabs-radius` of its own: this is a
		   card corner, which is exactly what `--pqf-radius-sm` is documented to be
		   (see THE SCALE TOKENS at the top of this file), and query-views'
		   `__toggle` — the plugin's other segmented control — already reads it. */
		border-radius: var(--pqf-radius-sm, 4px);
		box-shadow: var(
			--pqf-tabs-shadow,
			0 1px 3px rgb(0 0 0 / 10%),
			0 1px 2px rgb(0 0 0 / 6%)
		);
	}

	/* The cell. `flex: none` keeps every tab at its natural width inside the
   scroller — without it the flex items shrink to fit and the strip silently
   stops being scrollable at all. `position: relative` is the active bar's
   containing block. */
	.wp-block-plainplugins-taxonomy-filter.is-style-tabs
		.wp-block-plainplugins-taxonomy-filter__item,
	.wp-block-plainplugins-meta-filter.is-style-tabs
		.wp-block-plainplugins-meta-filter__item,
	.wp-block-plainplugins-post-type-filter.is-style-tabs
		.wp-block-plainplugins-post-type-filter__item {
		position: relative;
		display: inline-flex;
		flex: none;
		align-items: center;
		justify-content: center;
		white-space: nowrap;
		border-radius: 0;
		padding: var(--pqf-tabs-padding, 1em 2.5em);

		/* The block's own base rule sets 1.4 so wrapped term lists breathe. A tab
		   never wraps, so it takes control leading instead — and this is what makes
		   the strip a predictable height: 1em padding + 1em padding + 1.15lh. */
		line-height: var(--pqf-tabs-line-height, 1.15);
	}

	.wp-block-plainplugins-taxonomy-filter.is-style-tabs
		.wp-block-plainplugins-taxonomy-filter__item
		+ .wp-block-plainplugins-taxonomy-filter__item,
	.wp-block-plainplugins-meta-filter.is-style-tabs
		.wp-block-plainplugins-meta-filter__item
		+ .wp-block-plainplugins-meta-filter__item,
	.wp-block-plainplugins-post-type-filter.is-style-tabs
		.wp-block-plainplugins-post-type-filter__item
		+ .wp-block-plainplugins-post-type-filter__item {
		border-inline-start: 1px solid
			var(
				--pqf-tabs-divider,
				color-mix(in srgb, currentcolor 12%, transparent)
			);
	}

	/* Active tab — a bar along the bottom edge. A SHAPE cue, not a colour-only one
   (WCAG 1.4.1), for the same reason `__item--active` ships a font-weight bump:
   the variant has to read correctly before a theme paints anything. Sized and
   coloured through `--pqf-tabs-indicator*` so a theme can tint it without
   restating the geometry. */
	.wp-block-plainplugins-taxonomy-filter.is-style-tabs
		.wp-block-plainplugins-taxonomy-filter__item--active::after,
	.wp-block-plainplugins-meta-filter.is-style-tabs
		.wp-block-plainplugins-meta-filter__item--active::after,
	.wp-block-plainplugins-post-type-filter.is-style-tabs
		.wp-block-plainplugins-post-type-filter__item--active::after {
		content: "";
		position: absolute;
		inset-inline: 0;
		bottom: 0;
		height: var(--pqf-tabs-indicator-size, 2px);
		background: var(--pqf-tabs-indicator, currentcolor);
	}

	/* The chip-variant wrap gap moved to assets/css/defense.css (handle
   `pqf-defense`). It HAD to: it re-declares the block's base gap rule with a
   variant class added, and that base rule is now unlayered — so a layered
   variant rule would lose to it by ORIGIN no matter that it carries one more
   class, and every pills/buttons list would silently take the roomier base gap.
   The two have to sit on the same side of the layer boundary to be ordered by
   specificity at all. */
}
