/**
 * TiorAI archive shell — the canonical shared archive chrome.
 *
 * ONE implementation of "the page around a results grid", consumed by every
 * archive that has one:
 *
 *                        this file
 *                            |
 *              +-------------+-------------+------------------+
 *              |             |                                 |
 *          /tools/      /prompts/                         /glossary/
 *              |                                               |
 *   /tool-category/{term}/                     /glossary-category/{term}/
 *
 * Plus one surface that is not an archive at all: the single glossary term page
 * carries `.tr-term` and reads the SAME token block below. It renders none of
 * this file's chrome — no masthead, no toolbar, no pager — it just needs the
 * measure, the gutter, the easing curve, the accessible link colour and the box
 * surfaces to be the ones the archives use, because a reader arrives there from
 * one of them. Declaring a second copy of those five decisions in that page's
 * own stylesheet is the alternative, and it is the drift.
 *
 * What is here is everything an archive owns REGARDLESS of what it lists: the
 * masthead band, the search field's archive sizing, the results toolbar, the
 * filter drawer's repairs, the empty state, the results pager, the
 * infinite-scroll switch and the streaming feedback. The scope is `.tr-archive`,
 * the wrapper every consuming template prints, so nothing here can reach a
 * surface that is not an archive.
 *
 * What is NOT here, and must not move here:
 *  - the result CARD. Tools and prompts list different things and their cards
 *    are deliberately different objects (assets/css/tool-card.css,
 *    assets/css/prompt-card.css).
 *  - a consumer's own editorial chrome — the tools category index, its About
 *    band and its FAQ live in assets/css/tools-archive.css under `tr-tools__*`,
 *    because only `/tools/` renders them.
 *
 * History: these rules began inside assets/css/tools-archive.css under a
 * `.tr-tools` scope, where only `/tools/` could reach them. A partial extraction
 * (assets/css/results-toolbar.css) was started for the Top 100 page and left
 * unwired; it is superseded by this file and deleted. `/prompts/` is the second
 * real consumer that proves the abstraction, which is what CLAUDE.md 9.4a asks
 * for before a pattern is consolidated.
 *
 * Loads after `unified/filters.css` and `unified/pagination.css`, so it cascades after every
 * stylesheet, and BEFORE each consumer's own chrome, which declares this handle
 * as a dependency.
 *
 * Tokens only, `--tiorai-` prefixed per CLAUDE.md 9.1.
 */

/* ==========================================================================
   Shell tokens
   Declared on the wrapper so any archive adopting `.tr-archive` carries its own
   measures. The card systems declare their own card-level tokens separately
   (`--tiorai-tools-*` on the `tr-tools-cards` body class); the two sets are
   deliberately distinct because a card can be rendered outside an archive.
   ========================================================================== */

.tr-archive,
.tr-term,
.tr-resource-single {
	/* The measure the page is laid out on, and its gutter. */
	--tiorai-archive-measure: 1280px;
	--tiorai-archive-gutter: clamp(1rem, 4vw, 2rem);

	--tiorai-archive-ease: cubic-bezier(0.22, 1, 0.36, 1);

	/* Link text needs 4.5:1, which neither brand cyan (2.49:1) nor the focus
	   colour (4.48:1) reaches on white. #00758f is the next step down the same
	   cyan ramp and measures 5.39:1. In dark, brand cyan is already 7.77:1 on
	   #0B0C10, so the token points back at it rather than inventing a second
	   dark value. */
	--tiorai-archive-link: #00758f;

	/* Box surfaces are named because the light and dark steps are not the same
	   size. In dark, --surface-base resolves to the page colour exactly, so a
	   box styled with it is an outline drawn on the page with nothing inside.
	   Dark gets its own values below. */
	--tiorai-archive-box: var(--surface-base);
	--tiorai-archive-box-hover: var(--color-bg-secondary);
	--tiorai-archive-box-border: var(--border-default);

	background: var(--surface-base);
	color: var(--color-text-primary);
}

[data-theme="dark"] .tr-archive,
[data-theme="dark"] .tr-term,
[data-theme="dark"] .tr-resource-single {
	--tiorai-archive-link: var(--color-primary);

	/* A fill, so a box is an object on the page rather than an outline drawn on
	   it. */
	--tiorai-archive-box: rgba(255, 255, 255, 0.045);
	--tiorai-archive-box-hover: rgba(255, 255, 255, 0.09);
	--tiorai-archive-box-border: rgba(255, 255, 255, 0.14);
}

/* The one measure. Named for the archive because that is where it started; used
   by `.tr-term` too, because "the width TiorAI content is laid out on" is one
   decision and this is it. */
.tr-archive__container {
	width: 100%;
	max-width: var(--tiorai-archive-measure);
	margin-inline: auto;
	padding-inline: var(--tiorai-archive-gutter);
}

/* ==========================================================================
   Focus
   The parent suppressed the outline on the sort select, both view-toggle
   buttons, the filter-panel close button, every filter-section toggle and
   every filter-option link, and replaced it with nothing. Measured before the
   redesign: outline-width 0px on all of them. One rule restores a visible
   indicator across the whole archive, panel included.
   ========================================================================== */

.tr-archive :focus-visible,
.tr-term :focus-visible,
.tr-archive .tools-offcanvas :focus-visible {
	outline: var(--tiorai-focus-ring-width, 2px) solid var(--tiorai-focus-ring-color);
	outline-offset: var(--tiorai-focus-ring-offset, 2px);
	border-radius: var(--radius-base);
}

/* ==========================================================================
   Masthead
   ========================================================================== */

/* One area, one background. The masthead and whatever navigation follows it are
   a single introduction, so the glow is painted once across both rather than
   twice with a rule between them, which cut the wash in half.

   The gradient is the archive's own, restored from `.archive-header::before` in
   assets/css/unified/page-header.css: same circle, same 30%/20% origin, same
   `--color-primary-rgb` at 0.1, same 50% falloff.

   Carried as a background layer rather than a positioned pseudo-element, which
   drops two defects the original has:
     1. That `::before` is `position: absolute` inside an unpositioned block, so
        it resolves against whatever positioned ancestor it finds instead of the
        header. The parent flags this itself and logs it as A-20h.
     2. A positioned pseudo-element paints above in-flow text, so the glow laid
        a 10% cyan veil over the headline. As a background it sits behind the
        type, where an ambient wash belongs. */
/*
 * The ambient wash behind the masthead.
 *
 * P0 — the previous value cut a hard horizontal edge across the bottom of the
 * band on any archive whose intro is short, and the reason is that its size was
 * absolute while the box is not.
 *
 * `circle at 30% 20%` with no explicit size resolves to `farthest-corner`, so
 * the radius is a function of the box's DIAGONAL, while the distance the
 * gradient has to fade over is the box's HEIGHT. On a tall intro those happen to
 * line up; on a short one the fade is still half-opaque when it reaches the
 * bottom edge and the browser clips it flat. Measured at 1440px:
 *
 *   archive     intro height   transparent at   bottom edge   result
 *   /tools/           1082px          651px         866px     fades out
 *   /prompts/         1523px          779px        1218px     fades out
 *   /glossary/         481px          523px         385px     HARD EDGE
 *   /resources/        455px          519px         364px     HARD EDGE
 *
 * An ellipse sized in PERCENTAGES of the box removes the dependency: 90% of the
 * height, fading to nothing at 70% of that, is fully transparent by roughly 63%
 * of the band whatever the band's height turns out to be. Anchored at the top
 * edge rather than 20% down, so the brightest point sits behind the H1 instead
 * of behind the deck.
 *
 * Same colour, same strength, same intent — only the geometry is now relative.
 */
.tr-archive__intro {
	background-image: radial-gradient(120% 90% at 30% 0%, rgba(var(--color-primary-rgb), 0.1) 0%, transparent 70%);
	background-repeat: no-repeat;
	padding-block-end: clamp(2rem, 4vw, 3.5rem);
}

.tr-archive__masthead {
	padding-block: clamp(2rem, 4.5vw, 3.5rem) clamp(1.5rem, 3vw, 2.25rem);
	text-align: center;
}

/* Centred, matching the rest of the masthead. The shared renderer already emits
   .tr-breadcrumbs--center, so this only sets the spacing. */
.tr-archive__masthead .tr-breadcrumbs {
	margin-bottom: var(--space-4);
}

/*
 * ONE LINE, at every phone and tablet width.
 *
 * The shared component wraps (`flex-wrap: wrap` on the list) and, below 480px,
 * clamps the last crumb to TWO lines. Both were written for a trail whose last
 * crumb is a full post title, and on an archive that is a category name — so what
 * they actually produced here was a two- or three-line trail sitting above the H1,
 * pushing the headline down on the width with the least room for it. A trail is
 * orientation, not content: it should cost one line.
 *
 * The intermediate crumbs never shrink (`flex: 0 0 auto`) — "Home" and "Free
 * Tools" are the path, and a clipped path tells you nothing. The LAST crumb is the
 * one allowed to give way, and it is the safe one to clip because it names the
 * page you are already on, which the H1 directly below states in full.
 *
 * `display: block` is required to undo the component's `-webkit-box`, which
 * ignores `text-overflow`. The DOM text, the accessible name and the
 * BreadcrumbList schema are all unchanged — only the pixels are clipped.
 *
 * Scoped to the child archive shells rather than fixed in
 * `unified/breadcrumbs.css`, because that file is the parent's and is consumed by
 * every single and page template on the site; widening this needs those checked.
 */
@media (max-width: 1024px) {
	/* Never lets the trail push the page, whatever the depth. */
	.tr-archive__masthead .tr-breadcrumbs,
	.tr-term__intro .tr-breadcrumbs {
		overflow: hidden;
	}

	.tr-archive__masthead .tr-breadcrumbs__list,
	.tr-term__intro .tr-breadcrumbs__list {
		flex-wrap: nowrap;
		min-width: 0;
	}

	/*
	 * WHICH CRUMB GIVES WAY, and the order is the whole point of this block.
	 *
	 * A two- or three-crumb trail fits, so nothing shrinks and this does nothing.
	 * A four-crumb trail does not — `/tool-category/{term}/` renders "TiorAI › AI
	 * Tools › Marketing & Advertising › AI Analytics Assistant" — and the first
	 * version of this rule pinned the intermediates and let only the last crumb
	 * shrink. Measured on a 414px phone: the current crumb got 49px of the 145 it
	 * needs, so the page you are actually on read "AI A…" while a middle crumb sat
	 * at full width. That is backwards.
	 *
	 * Three weights, in the order a reader can afford to lose them:
	 *
	 *   first crumb   never shrinks. It is the site name and the home link, and
	 *                 "Tio…" is not a link anyone recognises. It costs ~39px.
	 *   middle crumbs shrink hard (`flex-shrink: 6`). They are the path, and a
	 *                 clipped path is still a path — the reader can see how deep
	 *                 they are and the link still works.
	 *   last crumb    shrinks least (`1`), with `min-width: 8ch` as its floor. It
	 *                 names the page, which is the one thing the trail is for.
	 *
	 * Measured on a 414px phone with the deepest trail the theme renders, "TiorAI ›
	 * AI Tools › Marketing & Advertising › AI Analytics Assistant": before this the
	 * current crumb got 49px of the 145 it needs and read "AI A…" while a middle
	 * crumb sat at full width. After, it gets 135 of 145.
	 */
	.tr-archive__masthead .tr-breadcrumbs__item,
	.tr-term__intro .tr-breadcrumbs__item {
		min-width: 0;
	}

	.tr-archive__masthead .tr-breadcrumbs__item:first-child,
	.tr-term__intro .tr-breadcrumbs__item:first-child {
		flex: 0 0 auto;
	}

	.tr-archive__masthead .tr-breadcrumbs__item:not(:first-child):not(:last-child),
	.tr-term__intro .tr-breadcrumbs__item:not(:first-child):not(:last-child) {
		flex: 0 6 auto;
	}

	.tr-archive__masthead .tr-breadcrumbs__item:last-child,
	.tr-term__intro .tr-breadcrumbs__item:last-child {
		flex: 0 1 auto;
	}

	/*
	 * `display: block` is required to undo the component's `-webkit-box` on the
	 * current crumb, which ignores `text-overflow`; the links get the same
	 * treatment so an intermediate crumb ellipses rather than being cut mid-glyph.
	 */
	.tr-archive__masthead .tr-breadcrumbs__link,
	.tr-archive__masthead .tr-breadcrumbs__current,
	.tr-term__intro .tr-breadcrumbs__link,
	.tr-term__intro .tr-breadcrumbs__current {
		display: block;
		min-width: 0;
		-webkit-line-clamp: none;
		white-space: nowrap;
		overflow: hidden;
		text-overflow: ellipsis;
	}

	.tr-archive__masthead .tr-breadcrumbs__current,
	.tr-term__intro .tr-breadcrumbs__current {
		min-width: 8ch;
	}
}

.tr-archive__masthead-head {
	max-width: 62ch;
	margin-inline: auto;
}

.tr-archive__title {
	margin: 0;
	font-family: var(--font-family-heading);
	font-size: clamp(1.75rem, 1.05rem + 2.6vw, 2.75rem);
	font-weight: var(--font-weight-bold);
	line-height: 1.1;
	letter-spacing: -0.025em;

	/* Capped in characters rather than pixels so the line count holds at every
	   step of the clamp, and balanced so the break falls at a phrase boundary
	   rather than wherever the box happens to end. */
	max-width: 30ch;
	margin-inline: auto;
	text-wrap: balance;

	/* The parent painted this heading with --color-text-gradient and
	   -webkit-text-fill-color: transparent. Gradient text carries no meaning,
	   degrades to invisible where background-clip fails, and is a banned
	   pattern in the design guidance this redesign follows. Solid colour. */
	background: none;
	-webkit-text-fill-color: currentColor;
	color: var(--color-text-primary);
}

/* The count stays inside the sentence: same size, same weight, same colour as
   the words around it. Tabular figures only, so the count-up cannot reflow the
   headline while it runs. */
.tr-archive__count-value {
	font-variant-numeric: tabular-nums;
	font-feature-settings: "tnum" 1;
}

.tr-archive__deck {
	margin: var(--space-3) auto 0;
	max-width: 54ch;
	font-size: var(--font-size-lg);
	line-height: var(--line-height-normal);
	color: var(--color-text-secondary);
	text-wrap: pretty;
}

/* Nothing sits beside the search: it is the band's one action, and it is
   centred on the same axis as everything above it. */
.tr-archive__masthead-row {
	margin-top: var(--space-6);
}

.tr-archive__search {
	max-width: 40rem;
	margin-inline: auto;
}

/* Shared search component, sized up: this is the archive's primary control.
   Presentation only — the field, its submit and the autocomplete behaviour are
   the shared component's, untouched.

   One component in four states, not four inputs: the edge is the only thing
   that changes, and it changes by weight and colour rather than by growing a
   frame around the control. */
.tr-archive .ui-search-box__row {
	min-height: 56px;
	border-radius: var(--radius-default);
	box-shadow: var(--shadow-sm);
	transition:
		border-color 0.15s var(--tiorai-archive-ease),
		box-shadow 0.15s var(--tiorai-archive-ease);
}

/* Hover is quieter than focus: the border warms one neutral step, nothing else
   moves. */
.tr-archive .ui-search-box__row:hover {
	border-color: var(--color-border-hover);
}

/* Focus: one crisp 2px edge, in place.
 *
 * The shared component sets an offset `outline` on top of a border-colour
 * shift, and this layer used to stack a two-colour box-shadow ring on top of
 * that — three indicators for one state, which is what read as a heavy floating
 * cyan frame. The outline is dropped and the ring reduced to 1px flush against
 * the border, so the field's own edge simply doubles in weight and turns
 * colour. No offset, no halo, no size change.
 *
 * --tiorai-focus-ring-color measures 4.48:1 on white and 7.77:1 on the dark
 * surface, both clear of the 3:1 minimum for a non-text indicator. */
.tr-archive .ui-search-box__row:focus-within {
	outline: none;
	border-color: var(--tiorai-focus-ring-color);
	box-shadow: 0 0 0 1px var(--tiorai-focus-ring-color);
}

/* Suggestions open: the field and the list are one shape, so they share one
   continuous edge instead of the ring drawing a line between them. */
.tr-archive .ui-search-box:has(.ui-search-box__suggestions.is-visible) .ui-search-box__row {
	border-end-start-radius: 0;
	border-end-end-radius: 0;
	border-bottom-color: transparent;
	box-shadow: none;
}

.tr-archive .ui-search-box__suggestions.is-visible {
	border-color: var(--tiorai-focus-ring-color);
}

/* The shared component tints a hovered suggestion with rgba(255,255,255,0.06),
   which is invisible on the light panel. A surface token reads in both themes. */
.tr-archive .ui-search-box__suggestion:hover,
.tr-archive .ui-search-box__suggestion.is-focused {
	background: var(--color-bg-secondary);
	color: var(--color-text-primary);
}

/* The leading magnifier is dropped: the submit button on the right already
   carries the search affordance, and one field does not need two of the same
   icon. `display: none` takes its 12px margin with it, so the placeholder
   starts on the row's own inset rather than 56px in. */
.tr-archive .ui-search-box__icon {
	display: none;
}

.tr-archive .ui-search-box__input {
	font-size: var(--font-size-base);
	text-align: start;
}

.tr-archive .ui-search-box__input:focus-visible {
	outline: none;
}

/* ==========================================================================
   Body + results toolbar
   ========================================================================== */

.tr-archive__body {
	padding-block: var(--space-6) var(--space-12);
	background: var(--surface-base);
}

/*
   SCOPED for the same reason the filter button below is, and it was the second
   half of the same misalignment.

   The parent's `assets/css/unified/filters.css` declares `.tiorai-header-bar {
   padding: var(--space-2); margin-bottom: var(--space-6) }` at single-class
   specificity, and this element carries that contract class too. Where
   filters.css is enqueued last — `/glossary/` — the bar took an 8px inline
   padding this file never asked for, and on desktop that pushed "All filters"
   8px inside the container margin the cards, the masthead and the alphabet
   strip all sit on. Measured at 1440px before the fix: toolbar x=130.5, first
   card x=130.5, filter button x=138.5.

   `.tr-top100` does not lose this rule to the added ancestor: that page has no
   `.tr-archive` wrapper, does not enqueue this file at all, and carries its own
   verbatim copy of these declarations (assets/css/top-100.css, which documents
   the duplication).
*/
.tr-archive .tr-archive__toolbar {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	justify-content: space-between;
	gap: var(--space-3) var(--space-5);
	padding: var(--space-3) 0;
	margin-bottom: var(--space-5);
	border-bottom: 1px solid var(--border-default);

	/* Sticks below the site header so the controls stay reachable through a long
	   scroll. Falls back to a normal block where sticky is unsupported. */
	position: sticky;
	top: var(--tiorai-header-height, 65px);
	z-index: 20;
	background: var(--surface-base);
}

.tr-archive__toolbar-primary,
.tr-archive__toolbar-secondary {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: var(--space-2) var(--space-4);
}

/*
   SCOPED UNDER `.tr-archive`, for the same load-order reason as the narrow-
   viewport toolbar block at the foot of this file — and here it was costing a
   visible defect rather than a layout one.

   The button also carries the parent's `.filter-toggle-btn` contract class, and
   the parent's `assets/css/unified/filters.css` declares `background:
   transparent; border: none; padding: var(--space-3) var(--space-4)` on it at
   the same single-class specificity. Which one wins is enqueue order, which
   differs per archive. Measured at 390px: `/tools/` and `/prompts/` load this
   file last and kept the boxed control (1px border, `--surface-base`, 12px
   radius); `/glossary/` loads filters.css last and rendered the button with no
   border and no fill at all.

   That is what put "All filters" out of alignment with everything above it. The
   button's BOX was on the text margin — 16px, the same x as the search field,
   the alphabet's first key and the first card — but with the border and fill
   stripped, the leftmost ink became the icon, 16px further in on the button's
   own padding. A control with no visible box has nothing to align BY, so the
   row read as indented against the whole page.

   The ancestor makes these (0,2,0) and the outcome identical on all five
   consumers regardless of which file is enqueued last. `.tr-archive__filter-btn`
   only ever exists inside `.tr-archive` — the shared toolbar part is its one
   source of markup, and the Top 100 page, which hand-rolls a toolbar, has no
   filter button — so nothing loses styling to the added ancestor.
*/
.tr-archive .tr-archive__filter-btn {
	display: inline-flex;
	align-items: center;
	gap: var(--space-2);
	min-height: 40px;
	padding: 0 var(--space-4);
	border: 1px solid var(--border-default);
	border-radius: var(--radius-lg);
	background: var(--surface-base);
	color: var(--color-text-primary);
	font-size: var(--font-size-sm);
	font-weight: var(--font-weight-semibold);
	cursor: pointer;
	transition: border-color 0.15s var(--tiorai-archive-ease), background-color 0.15s var(--tiorai-archive-ease);
}

.tr-archive .tr-archive__filter-btn:hover {
	border-color: var(--color-border-hover);
	background: var(--color-bg-secondary);
}

.tr-archive__filter-count {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	min-width: 20px;
	height: 20px;
	padding-inline: 6px;
	border-radius: var(--radius-full);
	background: var(--color-primary);
	color: var(--color-text-on-brand);
	font-size: var(--font-size-xs);
	font-weight: var(--font-weight-bold);
	font-variant-numeric: tabular-nums;
}

/* Quick filters. Plain links; the active one is marked by fill, border and a
   remove glyph, never by colour alone. Each consumer decides WHICH facet earns
   a shortcut — pricing on `/tools/`, difficulty on `/prompts/` — but the
   control is one control. */
.tr-archive__quickfilters {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: var(--space-2);
	margin: 0;
	padding: 0;
	list-style: none;
}

.tr-archive__quickfilter {
	display: inline-flex;
	align-items: center;
	gap: var(--space-1);
	min-height: 40px;
	padding-inline: var(--space-3);
	white-space: nowrap;
	border: 1px solid transparent;
	border-radius: var(--radius-lg);
	color: var(--color-text-secondary);
	font-size: var(--font-size-sm);
	font-weight: var(--font-weight-medium);
	text-decoration: none;
	transition: color 0.15s var(--tiorai-archive-ease), background-color 0.15s var(--tiorai-archive-ease);
}

.tr-archive__quickfilter:hover {
	background: var(--color-bg-secondary);
	color: var(--color-text-primary);
}

.tr-archive__quickfilter.is-active {
	border-color: var(--border-brand);
	background: var(--surface-selected);
	color: var(--color-text-primary);
	font-weight: var(--font-weight-semibold);
}

.tr-archive__count {
	margin: 0;
	font-size: var(--font-size-sm);
	color: var(--color-text-secondary);
	font-variant-numeric: tabular-nums;
}

.tr-archive__count strong {
	color: var(--color-text-primary);
	font-weight: var(--font-weight-semibold);
}

.tr-archive__sort {
	display: inline-flex;
	align-items: center;
	gap: var(--space-2);
}

.tr-archive__sort-label {
	font-size: var(--font-size-sm);
	color: var(--color-text-tertiary);
}

.tr-archive .tr-archive__sort-select {
	min-height: 40px;
	padding: 0 var(--space-8) 0 var(--space-3);
	border: 1px solid var(--border-default);
	border-radius: var(--radius-lg);
	background-color: var(--surface-base);
	color: var(--color-text-primary);
	font-size: var(--font-size-sm);
	font-weight: var(--font-weight-medium);
}

/* The two buttons sit flush inside one bordered box, separated by the seam that
   `.tr-archive__view-btn + .tr-archive__view-btn` draws — not by a gap. The
   shared `.view-toggle` component in the parent theme's unified/filters.css
   ships `gap: 4px`, which opened a 4px strip of page background between the
   halves and made the group read as two controls. Zeroing it here needs the
   `.tr-archive` ancestor to out-specify that single-class rule, the same reason
   the button rules below carry it. */
.tr-archive .tr-archive__view {
	display: inline-flex;
	gap: 0;
	border: 1px solid var(--border-default);
	border-radius: var(--radius-lg);
	overflow: hidden;
}

.tr-archive .tr-archive__view-btn {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 40px;
	height: 40px;
	border: 0;
	background: var(--surface-base);
	color: var(--color-text-tertiary);
	cursor: pointer;
	transition: background-color 0.15s var(--tiorai-archive-ease), color 0.15s var(--tiorai-archive-ease);
}

.tr-archive .tr-archive__view-btn + .tr-archive__view-btn {
	border-left: 1px solid var(--border-default);
}

.tr-archive .tr-archive__view-btn:hover {
	color: var(--color-text-primary);
	background: var(--color-bg-secondary);
}

.tr-archive .tr-archive__view-btn.active {
	background: var(--surface-selected);
	color: var(--color-text-primary);
}

/* Active-filter chips */
.tr-archive__active {
	display: flex;
	margin-bottom: var(--space-5);
}

.tr-archive .active-filters-content {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: var(--space-2) var(--space-3);
	width: 100%;
}

.tr-archive__active-label {
	font-size: var(--font-size-sm);
	color: var(--color-text-tertiary);
}

.tr-archive .active-filters-tags {
	display: flex;
	flex-wrap: wrap;
	gap: var(--space-2);
}

.tr-archive .filter-tag {
	display: inline-flex;
	align-items: center;
	gap: var(--space-1);
	padding: 4px 4px 4px var(--space-3);
	border: 1px solid var(--border-default);
	border-radius: var(--radius-full);
	background: var(--surface-base);
	font-size: var(--font-size-xs);
	color: var(--color-text-primary);
}

.tr-archive .filter-tag__remove {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 22px;
	height: 22px;
	padding: 0;
	border: 0;
	border-radius: var(--radius-full);
	background: transparent;
	color: var(--color-text-tertiary);
	cursor: pointer;
}

.tr-archive .filter-tag__remove:hover {
	background: var(--surface-interactive);
	color: var(--color-text-primary);
}

.tr-archive__active-clear {
	margin-inline-start: auto;
	font-size: var(--font-size-sm);
	font-weight: var(--font-weight-medium);
	color: var(--tiorai-archive-link);
	text-decoration: none;
}

.tr-archive__active-clear:hover {
	text-decoration: underline;
}

/* ==========================================================================
   Empty state
   Spans the grid rather than inheriting one column, which is what put the
   parent's "No tools found" in a 300px strip on an otherwise blank page. Also
   covers the parent markup returned by the AJAX endpoint.
   ========================================================================== */

.tr-archive__empty-title {
	margin: 0;
	font-size: var(--font-size-2xl);
	font-weight: var(--font-weight-bold);
	letter-spacing: -0.02em;
	color: var(--color-text-primary);
}

/* Two classes, because states.css centres the paragraph with
   `.no-tools p { max-width: 400px; margin: 0 auto }` — auto side margins, so
   overriding text-align was not enough and a single class does not outrank it. */
.tr-archive .tr-archive__empty-text {
	margin: var(--space-3) 0 0;
	max-width: none;
	font-size: var(--font-size-base);
	line-height: var(--line-height-normal);
	color: var(--color-text-secondary);
}

.tr-archive__empty-actions {
	margin: var(--space-5) 0 0;
}

.tr-archive__empty-btn {
	display: inline-flex;
	align-items: center;
	min-height: 44px;
	padding-inline: var(--space-5);
	border-radius: var(--radius-lg);
	background: var(--color-primary);
	color: var(--color-text-on-brand);
	font-size: var(--font-size-sm);
	font-weight: var(--font-weight-semibold);
	text-decoration: none;
	transition: background-color 0.15s var(--tiorai-archive-ease);
}

.tr-archive__empty-btn:hover {
	background: var(--color-primary-dark);
	color: var(--color-text-inverse);
}

.tr-archive__empty-cats {
	margin-top: var(--space-8);
	padding-top: var(--space-5);
	border-top: 1px solid var(--border-default);
}

.tr-archive__empty-subhead {
	margin: 0 0 var(--space-3);
	font-size: var(--font-size-sm);
	font-weight: var(--font-weight-semibold);
	color: var(--color-text-secondary);
}

.tr-archive__empty-list {
	display: flex;
	flex-wrap: wrap;
	gap: var(--space-2);
	margin: 0;
	padding: 0;
	list-style: none;
}

.tr-archive__empty-list a {
	display: inline-flex;
	align-items: center;
	gap: var(--space-2);
	min-height: 36px;
	padding-inline: var(--space-3);
	border: 1px solid var(--border-default);
	border-radius: var(--radius-lg);
	font-size: var(--font-size-sm);
	color: var(--color-text-primary);
	text-decoration: none;
}

.tr-archive__empty-list a:hover {
	border-color: var(--border-brand);
	color: var(--tiorai-archive-link);
}

.tr-archive__empty-count {
	font-size: var(--font-size-xs);
	color: var(--color-text-tertiary);
	font-variant-numeric: tabular-nums;
}

.tr-archive .infinite-scroll-end {
	font-size: var(--font-size-sm);
	color: var(--color-text-tertiary);
}

/* ==========================================================================
   Filter drawer
   Only four changes to the shared parent panel, all scoped to archives.
   ========================================================================== */

/* The panel is moved off-screen with transform alone, so while "closed" every
   control inside it stayed in the tab order: a keyboard user tabbing past the
   filter button walked into hundreds of invisible category links. visibility
   removes it from the tab order and is transitioned so the slide-in still
   animates.

   Visibility is given its own timing, and the consequence of not doing so is
   worse than a cosmetic one.

   Under a shared `visibility 0.3s`, a discrete property flips at 50% of the
   duration: 150ms. The runtime moves focus into the panel 100ms after opening
   it, and an element inside a `visibility: hidden` subtree cannot take focus,
   so the focus call landed on nothing and the keyboard user was left outside
   the drawer they had just opened. It reproduced at exactly 1024px and passed
   at 768px and 1025px, which is what a race that close to the line looks like.

   Opening: visible immediately, so the panel is focusable before anything tries.
   Closing: hold visible for the full slide-out, then hide, so the panel is
   still painted while it animates away and leaves the tab order once gone. */
.tr-archive .tools-offcanvas {
	visibility: hidden;
	transition: transform 0.3s var(--tiorai-archive-ease), visibility 0s linear 0.3s;
}

.tr-archive .tools-offcanvas.offcanvas-open {
	visibility: visible;
	transition: transform 0.3s var(--tiorai-archive-ease), visibility 0s linear 0s;
}

.tr-archive .tools-offcanvas .filter-option__count {
	font-variant-numeric: tabular-nums;
}

/* P0 — the drawer was empty at every width the drawer actually matters at.

   Two off-canvas implementations were nested inside each other. The outer
   <aside class="tools-offcanvas"> slides in correctly when the runtime adds
   .offcanvas-open. Inside it, unified/filters.css gives .filters-sidebar its
   OWN off-canvas treatment below 1024px — `position: fixed !important`, a fixed
   320px width and `transform: translateX(-100%)` — revealed by a
   `.mobile-visible` class that nothing in this archive ever adds.
   archive-layout.css tries to cancel it with `position: static`, which loses to
   `!important` and never touches the transform at all.

   Measured before this rule at 390px: the aside sat correctly at x=0, and every
   option inside it sat at x=-300. An open drawer, painted entirely off-screen.

   `position` needs !important to outrank an !important declaration; the rest
   only needs the specificity this selector already has. */
@media (max-width: 1024px) {
	.tr-archive .tools-offcanvas .filters-sidebar {
		position: static !important;
		transform: none;
		inset: auto;
		width: 100%;
		max-width: none;
		height: 100%;
		max-height: none;
		z-index: auto;
		box-shadow: none;
		border: 0;
		border-radius: 0;
		background: transparent;
	}
}

/* Pairs with unlockFilterSections() in assets/js/archive-runtime.js, which drops
   the inline `display: none` that was stopping collapsed sections from ever
   opening. That leaves `.collapsed`'s `max-height: 0` doing the hiding —
   correct visually, but max-height alone leaves the section's checkboxes in the
   tab order, so a keyboard user tabbing through an open panel would walk
   through them without seeing them.

   The timing here is load-bearing, not decoration.

   `.filter-section__content` carries `transition: all 0.2s` from the parent, and
   `visibility` is a discrete property: under `all` it flips at 50% of the
   duration, i.e. 100ms after the class changes. Anything that expands a section
   and then moves focus into it on the next frame lands on an element that is
   still `visibility: hidden`, and a hidden element cannot take focus. That is
   exactly what "Show more" does, and it silently failed to focus the search
   field until visibility was given its own timing. */
.tr-archive .filter-section__content.collapsed {
	visibility: hidden;
	transition: max-height 0.2s ease-in-out, visibility 0s linear 0.2s;
}

.tr-archive .filter-section__content.expanded {
	visibility: visible;
	transition: max-height 0.2s ease-in-out, visibility 0s linear 0s;
}

/* Truncation notice for a facet search that matched more terms than it listed
   (see TiorAI.FacetOverflow in assets/js/archive-runtime.js). Deliberately quiet
   — it reports a fact about the list above it, it is not a control. */
.tr-archive .tools-offcanvas .tr-archive__facet-note {
	margin: var(--space-3) 0 0;
	font-size: var(--font-size-xs);
	line-height: var(--line-height-normal);
	color: var(--color-text-tertiary);
}

/* Facet "Show more" — hands the visitor to that facet's own search field.
   Styled to the weight of the control it replaces, not to a call to action:
   what is behind it is a text input, not a destination. */
.tr-archive .tr-archive__facet-more {
	display: inline-flex;
	align-items: center;
	gap: var(--space-2);
	margin-top: var(--space-3);
	padding: var(--space-2) 0;
	border: 0;
	background: none;
	color: var(--tiorai-archive-link);
	font-size: var(--font-size-sm);
	font-family: inherit;
	cursor: pointer;
}

.tr-archive .tr-archive__facet-more:hover {
	text-decoration: underline;
}

/* ==========================================================================
   Results navigation
   The footer is navigation and nothing else. It held three things — pagination,
   a "Showing 1 to 12 of 14,702 tools" range and the infinite-scroll switch —
   and two of them were answering questions the page had already answered: the
   toolbar prints the total, the pagination marks the current page. What is left
   is one centred row, so the eye leaving the last card lands on "Next".
   ========================================================================== */

/* `[hidden]` is a UA rule at specificity 0,0,1, so any component rule setting
   `display` silently outranks it. Measured with JavaScript disabled: the
   infinite-scroll switch rendered in full, because `.tr-archive__switch` sets
   `display: inline-flex` — a control offering a JS-only mode to a visitor who
   has no JavaScript. The facet "Show more" button had the same defect while a
   search query was active.

   Restoring the attribute's meaning for this archive is the fix, and it has to
   win against component rules, so it is one of the few places `!important`
   is the correct tool rather than a shortcut. */
.tr-archive [hidden] {
	display: none !important;
}

/* No seam above the pager. The clamped margin already separates it from the last
   row of cards, and every card in that row ends in its own footer rule — a
   second full-width line directly under them read as a third divider rather than
   as the boundary of the results. */
.tr-archive__pager {
	margin-top: clamp(2rem, 4vw, 3rem);
	padding-top: var(--space-6);
}

/* Zero-height marker the observer watches. It sits above the pagination so a
   batch starts arriving while the visitor is still reading, not after they have
   run out of page. */
.tr-archive__sentinel {
	height: 1px;
}

.tr-archive__pager-nav {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: var(--space-3);
}

/* The shared ui-pagination component supplies the control itself: 44px targets,
   aria-current on the current page, focus-visible handling, and the contrast
   work recorded in assets/css/unified/pagination.css. Only its rhythm on an
   archive is set here. */
.tr-archive__pager .ui-pagination {
	margin-top: 0;
}

/* Compact, and deliberately not stretched to the container: eleven controls
   spread across 1,200px stop reading as one component. `flex-wrap` stays as the
   safety net it is, but at every width where the numbers are rendered the row
   measures under 570px and never uses it. */
.tr-archive__pager .ui-pagination__list {
	gap: var(--space-2);
}

/* Prev/Next carry a word, not a glyph, so they need room the numbers do not. */
.tr-archive__pager .ui-pagination__link.prev,
.tr-archive__pager .ui-pagination__link.next {
	padding-inline: var(--space-4);
	font-weight: var(--font-weight-medium);
}

.tr-archive__pager .ui-pagination__link.dots {
	border-color: transparent;
	background: none;
	min-width: var(--space-6);
}

/* Current page.

   The shared component fills this with `--color-primary`. That was a defensible
   cyan when pagination.css was written; since the DS-2026 wave-5 rebrand the
   same token is lime, and a solid lime chip is the loudest surface on an
   archive whose job is to make thousands of cards scannable. It pulled the eye
   away from the results and competed with the toolbar.

   The replacement is not invented: it is the selected-state vocabulary this
   archive already uses for the quick filters and the layout toggle —
   brand-tinted surface, stronger border, primary ink — plus semibold, so the
   marker is not carried by colour alone. Text contrast goes up rather than down
   — 13.9:1 on the light tint against the solid fill's 5.6:1.

   The border is the solid brand colour, not the 0.35-alpha `--color-border-hover`
   used for hover. Measured: the tinted surface alone separates from the white
   toolbar at 1.2:1, which is not enough for WCAG 1.4.11 to consider the state
   identifiable, and the tinted border at 1.6:1 does not rescue it. Solid
   `--color-primary` measures 5.0:1 against white and 11.7:1 against the dark
   theme's page, so the ring is what makes the current page legible as a state
   while the fill stays quiet. */
.tr-archive__pager .ui-pagination__link.is-active,
.tr-archive__pager .ui-pagination__item .current {
	background: var(--surface-selected);
	border-color: var(--color-primary);
	color: var(--color-text-primary);
	font-weight: var(--font-weight-semibold);
}

/* Hover has to stay quieter than the current-page marker, so it gives up the
   brand fill for the same reason the marker did. Focus is untouched: the shared
   component defers to the global `:focus-visible` ring, which is the one state
   here that should be impossible to miss. */
.tr-archive__pager .ui-pagination__link:hover {
	background: var(--color-bg-secondary);
	border-color: var(--color-border-hover);
	color: var(--color-text-primary);
}

/* The narrow-viewport position line. Hidden on desktop, where the numbered
   control already says which page this is. */
.tr-archive__pager-position {
	display: none;
	margin: 0;
	font-size: var(--font-size-sm);
	color: var(--color-text-secondary);
	font-variant-numeric: tabular-nums;
}

/* --------------------------------------------------------------------------
   Infinite-scroll switch (in the results toolbar)

   Secondary by construction: no fill, no box, label-sized type, and the same
   40px height as the filter button, the sort menu and the layout toggle, so
   moving it into the toolbar costs the toolbar no height at all. It reads as a
   preference, which is what it is, rather than as an action.

   No visible On/Off word. `aria-checked` carries the state for assistive
   technology and the switch itself carries it visually in two ways at once —
   the knob moves and the track goes from outlined to filled — so the word was
   a third statement of the same fact in the densest row on the page.
   -------------------------------------------------------------------------- */

.tr-archive__switch {
	display: inline-flex;
	align-items: center;
	gap: var(--space-2);
	min-height: 40px;
	padding-inline: var(--space-2);
	border: 1px solid transparent;
	border-radius: var(--radius-lg);
	background: none;
	color: var(--color-text-secondary);
	font-size: var(--font-size-sm);
	font-family: inherit;
	white-space: nowrap;
	cursor: pointer;
	transition: color 0.15s var(--tiorai-archive-ease);
}

.tr-archive__switch:hover {
	color: var(--color-text-primary);
}

/* Outlined when off, filled when on.

   The usual pale-grey track with a white knob was measured and rejected: at
   `--color-bg-tertiary` (#f1f5f9) against a white toolbar the track is 1.05:1
   and the knob inside it is invisible — the off state was communicated by
   nothing. An outlined pill with a solid knob puts both the track boundary and
   the knob at 4.6:1 in light and 5.5:1 in dark, which is what WCAG 1.4.11 asks
   of a control that has no other visual affordance. */
.tr-archive__switch-track {
	position: relative;
	flex-shrink: 0;
	width: 32px;
	height: 18px;
	border: 1px solid var(--color-text-tertiary);
	border-radius: var(--radius-full);
	background: var(--surface-base);
	transition: background-color 0.15s var(--tiorai-archive-ease), border-color 0.15s var(--tiorai-archive-ease);
}

.tr-archive__switch-knob {
	position: absolute;
	top: 2px;
	left: 2px;
	width: 12px;
	height: 12px;
	border-radius: var(--radius-full);
	background: var(--color-text-tertiary);
	transition: transform 0.15s var(--tiorai-archive-ease), background-color 0.15s var(--tiorai-archive-ease);
}

.tr-archive__switch[aria-checked="true"] {
	color: var(--color-text-primary);
	font-weight: var(--font-weight-medium);
}

.tr-archive__switch[aria-checked="true"] .tr-archive__switch-track {
	border-color: var(--color-primary);
	background: var(--color-primary);
}

/* Ink on the brand fill, never `--surface-base`: `--color-primary` is now
   theme-paired (dark olive light, vivid lime dark), so a fixed white knob would
   vanish on the dark theme's fill. `--color-text-on-brand` is the token that
   flips with it. */
.tr-archive__switch[aria-checked="true"] .tr-archive__switch-knob {
	background: var(--color-text-on-brand);
	transform: translateX(14px);
}

/* Streaming mode: the numbered control steps out of the way.

   `display: none` rather than the parent's clip-based `--js-hidden`, and the
   difference is deliberate. Clipping leaves the links focusable, so a keyboard
   user would tab through invisible page links that no longer describe how this
   view is being read. `display: none` removes them from the tab order and the
   accessibility tree together.

   Nothing is lost for crawlers: this class is applied by JavaScript after load,
   so the HTML that a crawler is served always contains the full, visible
   pagination. */
.tr-archive__pager--stream .tr-archive__pager-nav {
	display: none;
}

/* --------------------------------------------------------------------------
   Streaming feedback
   -------------------------------------------------------------------------- */

.tr-archive__stream {
	margin: 0 0 var(--space-4);
	text-align: center;
	font-size: var(--font-size-sm);
	color: var(--color-text-tertiary);
}

.tr-archive__stream-retry {
	display: block;
	margin: 0 auto var(--space-4);
	min-height: 44px;
	padding-inline: var(--space-5);
	border: 1px solid var(--tiorai-archive-box-border);
	border-radius: var(--radius-default);
	background: var(--tiorai-archive-box);
	color: var(--color-text-primary);
	font-size: var(--font-size-sm);
	font-family: inherit;
	cursor: pointer;
}

.tr-archive__stream-retry:hover {
	border-color: var(--color-primary);
	color: var(--color-primary);
}

/* ==========================================================================
   Narrow viewports
   ========================================================================== */

@media (max-width: 768px) {
	.tr-archive__masthead-row {
		align-items: flex-start;
	}

	.tr-archive__search {
		flex: 1 1 100%;
		max-width: none;
	}

	/*
	   ONE SWIPEABLE ROW, not a stack.

	   This was two rows — count, sort and the switch on one, filter button and
	   the quick-filter scroller on the other — and at 390px the top row wrapped
	   again, so the real cost was three or four rows and ~105px: a seventh of
	   the viewport spent on chrome before a single result. Worse, the sort menu
	   went full width and read as a form field the page was asking you to fill
	   in.

	   Everything is now one line that scrolls sideways, which is the gesture a
	   phone already has for a strip of controls. At rest a 390px screen shows
	   the count, the filter button and the edge of the first quick filter —
	   enough to say the row moves. Height goes 105px to ~56px.

	   `display: contents` on the two group wrappers is what makes it possible:
	   the controls live in two divs the runtime does not touch, and dissolving
	   those divs lets all six children share one flex line and be ordered
	   independently. They are presentational wrappers with no role, so nothing
	   is lost from the accessibility tree — the `role="group"` and its label are
	   on the toolbar itself.

	   Still not sticky. One row would nearly justify it now, but stacked under
	   the site header it is 121px of permanent chrome on the device with the
	   least room, and the drawer is reachable from the top of every result set.

	   EVERY SELECTOR IN THIS BLOCK IS SCOPED UNDER `.tr-archive`, and that is
	   load-bearing rather than stylistic. The toolbar root also carries the
	   parent's `.tiorai-header-bar` contract class, and the parent's
	   `assets/css/unified/filters.css` declares `display: flex; flex-wrap: wrap`
	   on it at the same single-class specificity — so which one wins comes down
	   to enqueue order, which differs per archive. Measured: `/prompts/` and
	   `/tools/` load this file last and took the one-row layout, `/glossary/`
	   loads filters.css last and stayed wrapped at 103px. The ancestor makes
	   these rules (0,2,0) and the outcome the same on all five consumers.
	   `.tr-archive .tr-archive__view` below was already written this way, for
	   this exact reason.
	*/
	.tr-archive .tr-archive__toolbar {
		position: static;
		flex-wrap: nowrap;
		align-items: center;

		/*
		   `flex-start`, and it is the difference between a row and a broken row.
		   The desktop rule is `space-between`, which is harmless once the row
		   overflows — but the parent's `assets/css/unified/filters.css` carries
		   `@media (max-width: 590px) { .tiorai-header-bar { justify-content:
		   center } }`, and `.tiorai-header-bar` is on this same element. In an
		   OVERFLOWING flex container, centring pushes the first items out through
		   the left edge into space the browser will not scroll back to: on
		   `/glossary/` at 390px the "All filters" button — the one control people
		   look for by position — was off-screen and unreachable, so the row read
		   as if it had been reordered to start with the count.

		   It had to be stated here rather than left to the desktop rule because
		   the parent's rule is inside a media query and this one was not: at
		   ≤590px it was the only `justify-content` in play, and load order decides
		   nothing when only one declaration exists. The ancestor selector makes
		   this (0,2,0) against the parent's (0,1,0), so it wins on all five
		   consumers regardless of which file is enqueued last.
		*/
		justify-content: flex-start;
		gap: var(--space-2);
		padding-top: 0;

		overflow-x: auto;
		overscroll-behavior-x: contain;
		scrollbar-width: none;
		-ms-overflow-style: none;

		/* Runs to the screen edge: a control row that stops inside the page
		   margin reads as a clipped block rather than as a row that continues.
		   The padding puts the first control back on the text margin. */
		margin-inline: calc(var(--tiorai-archive-gutter) * -1);
		padding-inline: var(--tiorai-archive-gutter);
		scroll-padding-inline-start: var(--tiorai-archive-gutter);
	}

	.tr-archive .tr-archive__toolbar::-webkit-scrollbar {
		display: none;
	}

	.tr-archive .tr-archive__toolbar:focus-visible {
		outline: var(--tiorai-focus-ring-width, 2px) solid var(--tiorai-focus-ring-color, var(--color-primary));
		outline-offset: 2px;
	}

	.tr-archive .tr-archive__toolbar-primary,
	.tr-archive .tr-archive__toolbar-secondary {
		display: contents;
	}

	/*
	   DOM ORDER, which is the desktop order: filter button, quick filters, count,
	   sort, switch. An earlier revision reordered the row to lead with the count,
	   and it was wrong for one concrete reason — a control that sits in a
	   different place on a phone than on a desktop has to be re-learned, and the
	   filter button is the one control on this bar people look for by position.
	   `display: contents` is still what puts all five on one line; it just no
	   longer moves any of them.
	*/
	.tr-archive .tr-archive__count {
		flex: 0 0 auto;
		margin: 0;
		white-space: nowrap;
	}

	.tr-archive .tr-archive__filter-btn {
		flex: 0 0 auto;
	}

	/*
	   THE ACTIVE-FILTER BADGE COMES INSIDE THE BUTTON.

	   The parent's `.filter-count` pins it at `top: -8px; right: -8px`, i.e.
	   8px outside the button on both axes. That is fine on a static bar and
	   broken in a scroll container: `overflow-x: auto` forces `overflow-y` to
	   `auto` as well, so the top 7px of the badge was clipped off the moment a
	   filter was applied — measured, with a filter active, at 390px.

	   Two ways out, and this is the cheaper one. Padding the row to clear the
	   overhang costs ~10px of permanent height on every phone view to
	   accommodate a state most views are not in. Setting it back in flow costs
	   nothing, cannot be clipped by any future scroll container, and reads
	   better: "All filters 2" is the button saying how many, rather than a dot
	   stuck to its corner.
	*/
	.tr-archive .tr-archive__filter-count {
		position: static;
	}

	/* No longer its own scroller — the whole row scrolls now, and a scroll
	   container inside a scroll container swallows the outer gesture. */
	.tr-archive .tr-archive__quickfilters {
		flex: 0 0 auto;
		width: auto;
		min-width: 0;
		flex-wrap: nowrap;
		overflow: visible;
		margin-inline: 0;
		padding-inline: 0;
	}

	.tr-archive .tr-archive__sort {
		flex: 0 0 auto;
	}

	.tr-archive .tr-archive__switch {
		flex: 0 0 auto;
		white-space: nowrap;
	}

	.tr-archive .tr-archive__sort-label {
		display: none;
	}

	/* Grid and list differ only in card width on a phone, so the toggle buys
	   almost nothing and costs a control. */
	.tr-archive .tr-archive__view {
		display: none;
	}
}

/* Eight 44px targets need 350px of clear width before the gaps. Below 640px the
   numbers are dropped and the position sentence carries the answer instead,
   which is the same information in a form that fits: Previous, "Page 3 of
   1,226", Next — one row, three parts.

   The sentence is not a sibling that can simply be ordered between the two
   links: core renders Previous and Next inside the pagination list, so they and
   the sentence live in different elements. Two grids solve it without touching
   the markup core owns — the nav spans the full width of an outer three-column
   grid whose middle column is sized by the sentence, and inside it the list is
   a matching three-column grid with the middle column empty. The sentence then
   lands centred between the two controls, in flow, with no overlap to guard
   against.

   Column placement rather than `space-between`, because on page one core omits
   Previous entirely (it renders no disabled placeholder, which is the behaviour
   this archive wants) and `space-between` would slide Next over to the left
   where Previous used to be. */

@media (max-width: 640px) {
	.tr-archive__pager-nav {
		display: grid;
		grid-template-columns: 1fr auto 1fr;
		gap: var(--space-2);
	}

	.tr-archive__pager .ui-pagination {
		grid-column: 1 / -1;
		grid-row: 1;
		width: 100%;
	}

	.tr-archive__pager .ui-pagination__list {
		display: grid;
		grid-template-columns: 1fr auto 1fr;
		width: 100%;
	}

	.tr-archive__pager .ui-pagination__item {
		display: none;
	}

	.tr-archive__pager .ui-pagination__item:has(.prev) {
		display: block;
		grid-column: 1;
		justify-self: start;
	}

	.tr-archive__pager .ui-pagination__item:has(.next) {
		display: block;
		grid-column: 3;
		justify-self: end;
	}

	/* Both words still fit at 360px once the generous desktop inset comes off,
	   and the 44px target is set by min-width, so nothing shrinks below it. */
	.tr-archive__pager .ui-pagination__link.prev,
	.tr-archive__pager .ui-pagination__link.next {
		padding-inline: var(--space-3);
	}

	.tr-archive__pager-position {
		display: block;
		grid-column: 2;
		grid-row: 1;
		align-self: center;
		white-space: nowrap;
	}
}

@media (prefers-reduced-motion: reduce) {
	.tr-archive *,
	.tr-archive *::before,
	.tr-archive *::after {
		transition-duration: 0.01ms !important;
		animation-duration: 0.01ms !important;
		animation-iteration-count: 1 !important;
	}
}
