/*
Theme Name: Crossbar
Theme URI: https://example.com/crossbar
Author: Crossbar
Author URI: https://example.com
Description: A catalogue and quote-request theme for sports goods manufacturers and exporters. Built for balls, gloves, apparel and team kit, with export pricing that keeps the currency, the unit, the Incoterm and the MOQ travelling together. No page builder, no build step, no required plugins beyond its own companion.
Version: 1.6.3
Requires at least: 6.0
Tested up to: 6.5
Requires PHP: 7.4
License: GNU General Public License v2 or later
License URI: http://www.gnu.org/licenses/gpl-2.0.html
Text Domain: crossbar
Tags: e-commerce, portfolio, custom-colors, custom-logo, custom-menu, featured-images, translation-ready, rtl-language-support, block-styles, wide-blocks, accessibility-ready
*/

/* ==========================================================================
   Contents
   1.  Tokens
   2.  Reset
   3.  Typography
   4.  Layout
   5.  Buttons and links
   6.  Forms
   7.  Header
   8.  Hero
   9.  Sections
   10. Product cards and grids
   11. Archive filters
   12. Single product
   13. Price block
   14. Quote basket and forms
   15. Editorial and pages
   16. Footer
   17. WordPress core classes
   18. Utilities
   19. Motion and print
   ========================================================================== */

/* ==========================================================================
   1. Tokens

   The palette is built around one saturated fill (orange) and one saturated
   surface (blue). The orange reads at 6.2:1 against the ink but only 3.12:1
   against white, so it is a fill colour and never a text colour on paper:
   --brand-ink is its text-safe sibling there (6.21:1 on white). The blue is
   dark enough for white labels (6.13:1), which is why the primary button is
   blue and the orange button carries ink text.
   ========================================================================== */

:root {

	/* Neutrals */
	--ink: #0b0e14;
	--ink-2: #161b26;
	--paper: #ffffff;
	--surface: #f2f4f7;
	--surface-2: #e8ecf1;
	--line: #e2e6ec;
	--line-strong: #cbd2dc;

	/* The boundary of anything a buyer can click or type into. WCAG 1.4.11
	   asks for 3:1 on the visual information that identifies a control, and
	   --line-strong is only 1.52:1 on paper — fine for a decorative rule
	   between two blocks, not enough to tell someone where a text field
	   begins. This reads 4.05:1 on paper and 3.41:1 on the darkest surface,
	   so it clears the bar on every background the theme puts behind a
	   control. Kept as a separate token rather than darkening --line-strong,
	   because card and panel edges at 3:1 would draw the eye away from the
	   photography they are meant to frame. */
	--field-line: #767f8c;

	--muted: #5b6572;

	/* Brand
	   Sports orange, not the lime this theme started on. Lime reads as energy
	   drink and esports; orange reads as safety equipment, shipping and kit,
	   which is the shelf this factory is actually on. Dark ink stays the text
	   colour on a brand button — #0b0e14 on #ff5a1f is 6.2:1, where white on
	   the same orange is only 3.12:1 and fails on anything below 24px.

	   --brand-ink is the darkened sibling used for text and small marks on
	   paper: #b23400 is 6.21:1 there, where the brand itself is 3.12:1 and
	   would fail.
	   Change --brand in the Customizer and both siblings are recomputed. */
	--brand: #ff5a1f;
	--brand-dark: #dc4510;
	--brand-ink: #b23400;
	--accent: #1b4df5;
	--accent-dark: #1339c4;
	--accent-ink: #1b4df5;
	--accent-soft: #eef2ff;

	/* What may be written on top of a fill: black or white, whichever survives.
	   Distinct from --brand-ink, and confusing them is the mistake this pair
	   exists to stop. --brand-ink is the brand darkened until it can be read on
	   *paper*; --brand-fg is what can be read on the *brand*. Painting the first
	   on a brand button is brand on brand, which is roughly 1.5:1 and looks, at a
	   glance, like a rendering fault.

	   Recomputed whenever the palette changes, so a client who picks a dark
	   primary gets white button text without knowing there was a decision. */
	--brand-fg: #0b0e14;
	--accent-fg: #ffffff;

	/* The full-width bands, as one token set rather than a dozen rules that each
	   hard-code a translucent white. Translucent white is invisible on a light
	   grey, and the Band style setting in the Customizer swaps this set whole —
	   which it can only do if nothing downstream has its own opinion.
	   --band is the ground, --band-2 the card on it, --band-fg the heading and
	   body colour, --band-soft the captions, --band-lead the standfirst,
	   --band-line a card edge, --band-edge the heavier edge of a control, and
	   --band-link and --band-ring the two colours that have to survive being
	   read as small type and as a focus ring on whichever ground is in play.

	   The values below are the light set, because light is what the theme ships
	   as of 1.6.0. A page that alternated near-black and white every six hundred
	   pixels never settled, and a catalogue is something a buyer scrolls for a
	   long time. Dark is still one radio away and is written by the dynamic
	   stylesheet the moment it is chosen. */
	--band: #f2f4f7;
	--band-2: #ffffff;
	--band-fg: #0b0e14;
	--band-soft: #5b6572;
	--band-lead: #5b6572;
	--band-line: #e2e6ec;
	--band-edge: #767f8c;
	--band-link: #b23400;
	--band-ring: #1b4df5;

	/* Signals */
	--ok: #146c2e;
	--warn: #8a5b00;
	--bad: #a8232a;

	/* Type
	   Archivo for display, Inter for text. Anton was the earlier choice and it
	   was wrong for this buyer: a condensed poster face shouts, and a factory
	   selling on lead time and carton dimensions is not selling on shouting.
	   Archivo is wide, calm and industrial, and it holds its shape at 700 across
	   a long product name — which Anton, with no weight axis at all, could not.

	   The fallback stack matters more than usual, because until the woff2 files
	   land locally the display face arrives over the network and something has
	   to hold the line for a frame. System UI first, since it is metrically
	   closest to Archivo on both platforms. */
	--font-display: "Archivo", system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
	--font-body: "Inter", system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
	--font-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;

	/* The scale. Loosened at the top and tightened at the bottom against the
	   first cut: the old --step-5 reached 6rem, which on a 1440 screen put the
	   hero headline at a size where three words filled the viewport and the
	   sentence underneath looked like a caption. */
	--step--2: clamp( .69rem, .67rem + .1vw, .74rem );
	--step--1: clamp( .8rem, .78rem + .1vw, .86rem );
	--step-0: clamp( .97rem, .94rem + .15vw, 1.05rem );
	--step-1: clamp( 1.12rem, 1.05rem + .35vw, 1.3rem );
	--step-2: clamp( 1.35rem, 1.2rem + .7vw, 1.7rem );
	--step-3: clamp( 1.65rem, 1.4rem + 1.3vw, 2.35rem );
	--step-4: clamp( 2.1rem, 1.65rem + 2.3vw, 3.25rem );
	--step-5: clamp( 2.6rem, 1.9rem + 3.6vw, 4.6rem );

	/* Tracking. Large display type set at its default spacing reads loose;
	   small uppercase labels set at their default reads jammed. One pair of
	   tokens so the two cases are handled consistently everywhere. */
	--track-display: -.022em;
	--track-label: .08em;

	/* Space */
	--gap: clamp( 1rem, .8rem + 1vw, 1.6rem );
	--gap-lg: clamp( 1.6rem, 1.2rem + 2vw, 3rem );
	--section: clamp( 3.5rem, 2.4rem + 5vw, 7rem );
	--wrap: 1240px;
	--wrap-narrow: 760px;

	/* Shape */
	--radius: 4px;
	--radius-lg: 10px;
	--shadow: 0 1px 2px rgba( 11, 14, 20, .06 ), 0 8px 24px rgba( 11, 14, 20, .06 );
	--shadow-lg: 0 2px 4px rgba( 11, 14, 20, .08 ), 0 20px 50px rgba( 11, 14, 20, .12 );

	--speed: .22s;
	--ease: cubic-bezier( .2, .7, .3, 1 );
}

/* Self-hosted fonts.

   These are the override, not the default. functions.php looks for
   assets/fonts/archivo-700.woff2; find it and the Google Fonts request is
   never made and these rules take over, miss it and the stylesheet from
   Google supplies the same two families under the same two names. Nothing in
   the rest of this file changes either way.

   Self-hosting is the better end state — one less third party, one less DNS
   lookup, and the only arrangement that passes a GDPR review or works behind a
   firewall. Both families are open licensed (Archivo and Inter are both SIL
   OFL), so the files can simply be dropped in:

       assets/fonts/archivo-500.woff2
       assets/fonts/archivo-600.woff2
       assets/fonts/archivo-700.woff2
       assets/fonts/archivo-800.woff2
       assets/fonts/inter-400.woff2
       assets/fonts/inter-500.woff2
       assets/fonts/inter-600.woff2
       assets/fonts/inter-700.woff2

   font-display: swap on every one. A font that fails to arrive should cost a
   flash of fallback text, never a paragraph of nothing. */

@font-face {
	font-family: "Archivo";
	src: url( "assets/fonts/archivo-500.woff2" ) format( "woff2" );
	font-weight: 500;
	font-style: normal;
	font-display: swap;
}

@font-face {
	font-family: "Archivo";
	src: url( "assets/fonts/archivo-600.woff2" ) format( "woff2" );
	font-weight: 600;
	font-style: normal;
	font-display: swap;
}

@font-face {
	font-family: "Archivo";
	src: url( "assets/fonts/archivo-700.woff2" ) format( "woff2" );
	font-weight: 700;
	font-style: normal;
	font-display: swap;
}

@font-face {
	font-family: "Archivo";
	src: url( "assets/fonts/archivo-800.woff2" ) format( "woff2" );
	font-weight: 800;
	font-style: normal;
	font-display: swap;
}

@font-face {
	font-family: "Inter";
	src: url( "assets/fonts/inter-400.woff2" ) format( "woff2" );
	font-weight: 400;
	font-style: normal;
	font-display: swap;
}

@font-face {
	font-family: "Inter";
	src: url( "assets/fonts/inter-500.woff2" ) format( "woff2" );
	font-weight: 500;
	font-style: normal;
	font-display: swap;
}

@font-face {
	font-family: "Inter";
	src: url( "assets/fonts/inter-600.woff2" ) format( "woff2" );
	font-weight: 600;
	font-style: normal;
	font-display: swap;
}

@font-face {
	font-family: "Inter";
	src: url( "assets/fonts/inter-700.woff2" ) format( "woff2" );
	font-weight: 700;
	font-style: normal;
	font-display: swap;
}

/* ==========================================================================
   2. Reset
   ========================================================================== */

*,
*::before,
*::after {
	box-sizing: border-box;
}

html {
	-webkit-text-size-adjust: 100%;
	scroll-behavior: smooth;
}

body {
	margin: 0;
	background: var( --paper );
	color: var( --ink );
	font-family: var( --font-body );
	font-size: var( --step-0 );
	line-height: 1.65;
	-webkit-font-smoothing: antialiased;
	-moz-osx-font-smoothing: grayscale;
	overflow-x: hidden;
}

img,
svg,
video,
canvas,
iframe {
	max-width: 100%;
	height: auto;
	display: block;
}

figure {
	margin: 0;
}

button,
input,
select,
textarea {
	font: inherit;
	color: inherit;
}

table {
	border-collapse: collapse;
	width: 100%;
}

hr {
	border: 0;
	border-top: 1px solid var( --line );
	margin: var( --gap-lg ) 0;
}

:focus-visible {
	outline: 3px solid var( --accent );
	outline-offset: 2px;
	border-radius: 2px;
}

/* The same ring in volt wherever the ground is ink. The blue reads 6.13:1 on
   paper but only 3.15:1 on --ink and 2.81:1 on --ink-2, so on the top bar, the
   hero and the footer a keyboard user would be hunting for a ring that is
   barely there. Volt is 16.77:1 on ink, and it is already this theme's colour
   for "the thing you are on" in those places. */
.topbar :focus-visible,
.hero :focus-visible,
.page-hero :focus-visible,
.site-footer :focus-visible {
	outline-color: var( --brand );
}

/* The bands are the one dark ground that is allowed to stop being dark, so the
   ring on them follows the band rather than the list above: volt while the band
   is ink, the ordinary blue once the band is a light grey — where volt would be
   2.2:1 and effectively no ring at all. */
.section--ink :focus-visible {
	outline-color: var( --band-ring );
}

::selection {
	background: var( --brand );
	color: var( --brand-fg );
}

/* ==========================================================================
   3. Typography
   ========================================================================== */

h1,
h2,
h3,
h4,
h5,
h6 {
	margin: 0 0 .5em;
	font-family: var( --font-display );
	font-weight: 700;
	line-height: 1.08;
	letter-spacing: var( --track-display );
	text-wrap: balance;
}

/* Uppercase has been pulled off the headings and left only on the small
   labels. Set solid at 400 in a condensed face it was the one thing holding
   the design together; set at 700 in Archivo it is just harder to read, and a
   product page whose every heading shouts gives a buyer no way to tell the
   product name from the section above it. */

h1 {
	font-size: var( --step-4 );
	font-weight: 800;
	letter-spacing: -.028em;
}

h2 {
	font-size: var( --step-3 );
	letter-spacing: -.024em;
}

h3 {
	font-size: var( --step-2 );
	font-weight: 600;
	letter-spacing: -.018em;
}

/* From h4 down the job stops being display and starts being structure inside
   running text, so the body face takes over — a heading that sits two lines
   above the paragraph it labels should match it. */

h4 {
	font-size: var( --step-1 );
	font-family: var( --font-body );
	font-weight: 600;
	letter-spacing: -.008em;
	line-height: 1.35;
}

h5,
h6 {
	font-family: var( --font-body );
	font-weight: 600;
	font-size: var( --step--1 );
	text-transform: uppercase;
	letter-spacing: var( --track-label );
	line-height: 1.4;
	color: var( --muted );
}

p {
	margin: 0 0 1.1em;
	text-wrap: pretty;
}

p:last-child {
	margin-bottom: 0;
}

a {
	color: var( --accent );
	text-decoration-thickness: 1px;
	text-underline-offset: .18em;
}

a:hover {
	color: var( --accent-dark );
}

small,
.small {
	font-size: var( --step--1 );
}

strong,
b {
	font-weight: 700;
}

blockquote {
	margin: var( --gap-lg ) 0;
	padding: 0 0 0 var( --gap );
	border-left: 3px solid var( --brand );
	font-size: var( --step-1 );
	line-height: 1.45;
}

code,
kbd,
pre {
	font-family: var( --font-mono );
	font-size: .9em;
}

code {
	background: var( --surface );
	padding: .1em .35em;
	border-radius: 3px;
}

pre {
	background: var( --ink );
	color: var( --paper );
	padding: var( --gap );
	border-radius: var( --radius );
	overflow-x: auto;
}

.eyebrow {
	display: inline-block;
	margin: 0 0 .8rem;
	font-family: var( --font-body );
	font-size: var( --step--1 );
	font-weight: 700;
	letter-spacing: .16em;
	text-transform: uppercase;
	color: var( --brand-ink );
}

.eyebrow--light {
	color: var( --brand );
}

.lede {
	font-size: var( --step-1 );
	line-height: 1.5;
	color: var( --muted );
	max-width: 60ch;
}

/* ==========================================================================
   4. Layout
   ========================================================================== */

.wrap {
	width: min( 100% - 2.5rem, var( --wrap ) );
	margin-inline: auto;
}

.wrap--narrow {
	width: min( 100% - 2.5rem, var( --wrap-narrow ) );
}

.section {
	padding-block: var( --section );
}

.section--tight {
	padding-block: calc( var( --section ) * .6 );
}

.section--surface {
	background: var( --surface );
}

/* The full-width bands.
 *
 * The class is still called --ink for the sake of every template that already
 * prints it, but nothing in here names a colour any more. A band is drawn
 * entirely out of the --band* set, and that set is rewritten wholesale by the
 * Band style option in the Customizer — dark slabs, or the same layout in white
 * and light grey. That swap only works while no rule underneath has its own
 * opinion, which is why the translucent whites that used to live here are gone:
 * white at 70% is invisible on a light grey, so a band that went light would
 * have lost its captions, its card edges and its stat labels one by one. */
.section--ink {
	background: var( --band );
	color: var( --band-fg );
}

.section--ink h1,
.section--ink h2,
.section--ink h3 {
	color: var( --band-fg );
}

.section--ink a {
	color: var( --band-link );
}

.section--ink .lede {
	color: var( --band-lead );
}

.section__head {
	display: flex;
	flex-wrap: wrap;
	align-items: flex-end;
	justify-content: space-between;
	gap: var( --gap );
	margin-bottom: var( --gap-lg );
}

.section__head > div {
	max-width: 62ch;
}

.section__title {
	margin-bottom: .25em;
}

.grid {
	display: grid;
	gap: var( --gap );
}

.grid--2 {
	grid-template-columns: repeat( auto-fit, minmax( 300px, 1fr ) );
}

.grid--3 {
	grid-template-columns: repeat( auto-fill, minmax( 280px, 1fr ) );
}

.grid--4 {
	grid-template-columns: repeat( auto-fill, minmax( 230px, 1fr ) );
}

.split {
	display: grid;
	gap: var( --gap-lg );
	align-items: center;
	grid-template-columns: repeat( auto-fit, minmax( 320px, 1fr ) );
}

/* Centring is right for a picture beside a paragraph and wrong for a form
   beside a details card, where both columns should start at the same line. */
.split--top {
	align-items: start;
}

/* ==========================================================================
   5. Buttons and links
   ========================================================================== */

.btn {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	gap: .55em;
	padding: .82em 1.5em;
	border: 2px solid transparent;
	border-radius: var( --radius );
	background: var( --accent );
	color: var( --accent-fg );
	font-size: var( --step--1 );
	font-weight: 700;
	letter-spacing: .08em;
	text-transform: uppercase;
	text-decoration: none;
	cursor: pointer;
	transition: background var( --speed ) var( --ease ), color var( --speed ) var( --ease ), border-color var( --speed ) var( --ease ), transform var( --speed ) var( --ease );
}

.btn:hover {
	background: var( --accent-dark );
	color: var( --accent-fg );
	transform: translateY( -1px );
}

/* The primary fill, with its label in whichever of black or white can be read
   on it. --brand-fg rather than --ink: --ink is only the right answer while the
   primary is a bright orange, and a client who picks a navy would get near-black
   on near-navy — a button that reads, at a glance, as a rendering fault. */
.btn--brand {
	background: var( --brand );
	color: var( --brand-fg );
}

.btn--brand:hover {
	background: var( --brand-dark );
	color: var( --brand-fg );
}

.btn--ink {
	background: var( --ink );
	color: var( --paper );
}

.btn--ink:hover {
	background: var( --ink-2 );
	color: var( --paper );
}

.btn--ghost {
	background: transparent;
	color: var( --ink );
	border-color: var( --field-line );
}

.btn--ghost:hover {
	background: var( --ink );
	color: var( --paper );
	border-color: var( --ink );
}

/* The ghost button on any dark ground. The hero and the inner-page hero always
   sit over photography under a heavy scrim, so they are white here for good and
   take no part in the band switch — the base rule paints this button in --ink,
   which on the home page hero put the second call to action, "See the
   catalogue", in near-black text on a near-black overlay. The border is white
   at 35%, which resolves to 3.16:1 against the ink behind it and so clears
   WCAG 1.4.11 for a control edge. */
.hero .btn--ghost,
.page-hero .btn--ghost {
	color: var( --paper );
	border-color: rgba( 255, 255, 255, .35 );
}

.hero .btn--ghost:hover,
.page-hero .btn--ghost:hover {
	background: var( --paper );
	color: var( --ink );
	border-color: var( --paper );
}

/* The same button inside a band, where the ground is whatever the band setting
   says it is and the invert on hover has to invert to the right thing. */
.section--ink .btn--ghost {
	color: var( --band-fg );
	border-color: var( --band-edge );
}

.section--ink .btn--ghost:hover {
	background: var( --band-fg );
	color: var( --band );
	border-color: var( --band-fg );
}

.btn--sm {
	padding: .6em 1.1em;
	font-size: .75rem;
}

.btn--full {
	width: 100%;
}

.btn[disabled],
.btn[aria-disabled="true"] {
	opacity: .5;
	cursor: not-allowed;
	transform: none;
}

.btn svg {
	width: 1.05em;
	height: 1.05em;
	flex: 0 0 auto;
	fill: currentColor;
}

.link-arrow {
	display: inline-flex;
	align-items: center;
	gap: .45em;
	font-weight: 600;
	font-size: var( --step--1 );
	letter-spacing: .06em;
	text-transform: uppercase;
	text-decoration: none;
	color: var( --ink );
	border-bottom: 2px solid var( --brand );
	padding-bottom: .15em;
}

.link-arrow:hover {
	color: var( --accent );
	border-color: var( --accent );
}

.section--ink .link-arrow {
	color: var( --band-fg );
}

.section--ink .link-arrow:hover {
	color: var( --band-link );
	border-color: var( --band-link );
}

/* ==========================================================================
   6. Forms
   ========================================================================== */

.field {
	display: block;
	margin-bottom: var( --gap );
}

.field__label {
	display: block;
	margin-bottom: .4rem;
	font-size: var( --step--1 );
	font-weight: 600;
	letter-spacing: .05em;
	text-transform: uppercase;
	color: var( --ink );
}

.field__req {
	color: var( --bad );
}

.field__hint {
	display: block;
	margin-top: .35rem;
	font-size: var( --step--1 );
	color: var( --muted );
}

input[type="text"],
input[type="email"],
input[type="tel"],
input[type="url"],
input[type="number"],
input[type="date"],
input[type="search"],
input[type="password"],
select,
textarea {
	width: 100%;
	padding: .72em .9em;
	background: var( --paper );
	border: 1px solid var( --field-line );
	border-radius: var( --radius );
	font-size: var( --step-0 );
	line-height: 1.4;
	transition: border-color var( --speed ) var( --ease ), box-shadow var( --speed ) var( --ease );
}

textarea {
	min-height: 9rem;
	resize: vertical;
}

input:focus,
select:focus,
textarea:focus {
	border-color: var( --accent );
	box-shadow: 0 0 0 3px var( --accent-soft );
	outline: none;
}

select {
	appearance: none;
	background-image: url( "data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 8'%3E%3Cpath fill='%235b6572' d='M1 1l5 5 5-5'/%3E%3C/svg%3E" );
	background-repeat: no-repeat;
	background-position: right .9em center;
	background-size: 11px;
	padding-right: 2.4em;
}

.form-note {
	font-size: var( --step--1 );
	color: var( --muted );
}

.notice {
	padding: var( --gap );
	border-radius: var( --radius );
	border-left: 4px solid var( --accent );
	background: var( --accent-soft );
	margin-bottom: var( --gap-lg );
}

.notice--ok {
	border-color: var( --ok );
	background: #edf7f0;
}

.notice--bad {
	border-color: var( --bad );
	background: #fcf1f2;
}

.notice p {
	margin: 0;
}

/* The site-wide notice slot in header.php. It only exists on the one page view
   that follows a form, so it carries its own top space rather than relying on
   whatever section happens to come next. */
.wrap--notice {
	padding-top: var( --gap-lg );
}

.wrap--notice .notice:last-child {
	margin-bottom: 0;
}

/* The honeypot. Kept out of the accessibility tree as well as out of sight:
   a screen reader user filling it in would be silently discarded. */
.cb-hp {
	position: absolute;
	left: -9999px;
	width: 1px;
	height: 1px;
	overflow: hidden;
}

/* ==========================================================================
   7. Header
   ========================================================================== */

.skip-link {
	position: absolute;
	left: -9999px;
	z-index: 999;
	padding: .75rem 1.25rem;
	background: var( --ink );
	color: var( --paper );
	font-weight: 600;
}

.skip-link:focus {
	left: 1rem;
	top: 1rem;
}

.topbar {
	background: var( --ink );
	color: rgba( 255, 255, 255, .8 );
	font-size: var( --step--1 );
}

.topbar__inner {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	justify-content: space-between;
	gap: .5rem 1.5rem;
	padding-block: .55rem;
}

.topbar a {
	color: rgba( 255, 255, 255, .8 );
	text-decoration: none;
}

.topbar a:hover {
	color: var( --brand );
}

.topbar__list {
	display: flex;
	flex-wrap: wrap;
	gap: .3rem 1.4rem;
	margin: 0;
	padding: 0;
	list-style: none;
}

.topbar__list li {
	display: flex;
	align-items: center;
	gap: .4rem;
}

.topbar svg {
	width: .95em;
	height: .95em;
	fill: var( --brand );
	flex: 0 0 auto;
}

.site-header {
	position: sticky;
	top: 0;
	z-index: 100;
	background: var( --paper );
	border-bottom: 1px solid var( --line );
	transition: box-shadow var( --speed ) var( --ease ), background var( --speed ) var( --ease );
}

/* Added by script once the page has scrolled past the top.
   Only the surface changes — a header that resizes on scroll makes the whole
   page jump under the reader's eye. Frosted rather than opaque so the product
   photography below it stays faintly visible and the bar reads as a layer over
   the page rather than a lid on it. */
.site-header.is-stuck {
	background: rgba( 255, 255, 255, .82 );
	box-shadow: 0 6px 24px rgba( 11, 14, 20, .1 );
	-webkit-backdrop-filter: saturate( 180% ) blur( 14px );
	backdrop-filter: saturate( 180% ) blur( 14px );
}

/* Without support for the blur the bar has to stay opaque, or the text behind
   it shows through and nothing in the menu is readable. */
@supports not ( ( backdrop-filter: blur( 1px ) ) or ( -webkit-backdrop-filter: blur( 1px ) ) ) {

	.site-header.is-stuck {
		background: var( --paper );
	}
}

.site-header__inner {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: var( --gap );
	min-height: 84px;
}

.brand {
	display: flex;
	align-items: center;
	gap: .7rem;
	text-decoration: none;
	color: var( --ink );
	flex: 0 0 auto;
}

.brand img {
	max-height: 58px;
	width: auto;
	transition: transform var( --speed ) var( --ease );
}

.brand:hover img {
	transform: scale( 1.03 );
}

.brand__mark {
	width: 38px;
	height: 38px;
	flex: 0 0 auto;
}

.brand__name {
	font-family: var( --font-display );
	font-size: 1.35rem;
	font-weight: 800;
	line-height: 1;
	letter-spacing: -.03em;
	text-transform: uppercase;
}

.brand__tag {
	display: block;
	font-family: var( --font-body );
	font-size: .62rem;
	font-weight: 600;
	letter-spacing: .18em;
	color: var( --muted );
	text-transform: uppercase;
	margin-top: .2rem;
}

/* Navigation */

.nav {
	margin-left: auto;
}

.nav ul {
	display: flex;
	align-items: center;
	gap: .15rem;
	margin: 0;
	padding: 0;
	list-style: none;
}

.nav li {
	position: relative;
}

.nav a {
	position: relative;
	display: block;
	padding: .6rem .85rem;
	border-radius: var( --radius );
	color: var( --ink );
	font-size: var( --step--1 );
	font-weight: 600;
	letter-spacing: .06em;
	text-transform: uppercase;
	text-decoration: none;
	transition: background var( --speed ) var( --ease ), color var( --speed ) var( --ease );
}

/*
 * The marker under a top-level item is always in the markup and is scaled from
 * nothing, rather than being added on hover. A rule that only exists on hover
 * changes the height of the link, and a header that grows two pixels every
 * time the pointer crosses a menu item reads as a rendering fault.
 */
.nav > ul > li > a::after {
	content: "";
	position: absolute;
	right: .85rem;
	bottom: .3rem;
	left: .85rem;
	height: 2px;
	border-radius: 2px;
	background: var( --brand );
	transform: scaleX( 0 );
	transform-origin: left center;
	transition: transform var( --speed ) var( --ease );
}

.nav > ul > li > a:hover {
	background: var( --surface );
}

.nav > ul > li > a:hover::after,
.nav > ul > .current-menu-item > a::after,
.nav > ul > .current_page_item > a::after,
.nav > ul > .current-menu-ancestor > a::after,
.nav > ul > .current-menu-parent > a::after {
	transform: scaleX( 1 );
}

.nav .current-menu-item > a,
.nav .current_page_item > a,
.nav .current-menu-ancestor > a {
	color: var( --ink );
}

/* A caret, so an item with a dropdown looks like one before it is touched. */
.nav .menu-item-has-children > a {
	padding-right: 1.75rem;
}

.nav .menu-item-has-children > a::before {
	content: "";
	position: absolute;
	top: 50%;
	right: .8rem;
	width: 6px;
	height: 6px;
	border-right: 2px solid currentColor;
	border-bottom: 2px solid currentColor;
	opacity: .55;
	transform: translateY( -70% ) rotate( 45deg );
	transition: transform var( --speed ) var( --ease ), opacity var( --speed ) var( --ease );
}

.nav .menu-item-has-children:hover > a::before,
.nav .menu-item-has-children:focus-within > a::before {
	opacity: 1;
	transform: translateY( -30% ) rotate( -135deg );
}

.nav .sub-menu {
	position: absolute;
	top: calc( 100% + .5rem );
	left: 0;
	min-width: 224px;
	flex-direction: column;
	align-items: stretch;
	gap: 0;
	padding: .4rem;
	background: var( --paper );
	border: 1px solid var( --line );
	border-radius: calc( var( --radius ) + 4px );
	box-shadow: var( --shadow-lg );
	opacity: 0;
	visibility: hidden;
	transform: translateY( 8px );
	transition: opacity var( --speed ) var( --ease ), transform var( --speed ) var( --ease ), visibility var( --speed );
}

/*
 * The dropdown sits half a centimetre below its parent, and a pointer crossing
 * that gap leaves both elements — which closes the menu the moment someone
 * reaches for it. This bridges the gap without being visible.
 */
.nav .sub-menu::before {
	content: "";
	position: absolute;
	top: -.55rem;
	right: 0;
	left: 0;
	height: .55rem;
}

/* A touch screen has no hover, so there the first tap opens the submenu and
   the script adds this class rather than following the link. */
.nav li.is-open > .sub-menu,
.nav li:hover > .sub-menu,
.nav li:focus-within > .sub-menu {
	opacity: 1;
	visibility: visible;
	transform: none;
}

.nav .sub-menu a {
	padding: .55rem .75rem;
	text-transform: none;
	letter-spacing: 0;
	font-weight: 500;
}

.nav .sub-menu a:hover {
	background: var( --surface );
	color: var( --accent );
}

.header__actions {
	display: flex;
	align-items: center;
	gap: .6rem;
	flex: 0 0 auto;
	margin-left: .9rem;
}

/* The quote basket counter. The badge is volt on ink so it reads at a glance
   without becoming another blue thing competing with the buttons. */
/*
 * Two states on purpose. An empty list is the state a first-time visitor
 * arrives in and the button is not yet worth shouting about, so it stays a
 * quiet outline. The moment a line is added it fills in and the counter turns
 * volt — the one thing in the bar that is allowed to be loud, and only while
 * there is something waiting to be sent.
 */
.basket {
	position: relative;
	display: inline-flex;
	align-items: center;
	gap: .5rem;
	padding: .62em 1.1em;
	background: var( --ink );
	border: 2px solid var( --ink );
	border-radius: 99px;
	color: var( --paper );
	font-size: var( --step--1 );
	font-weight: 700;
	letter-spacing: .06em;
	text-transform: uppercase;
	text-decoration: none;
	transition: background var( --speed ) var( --ease ), border-color var( --speed ) var( --ease ), color var( --speed ) var( --ease ), transform var( --speed ) var( --ease );
}

.basket:hover {
	color: var( --paper );
	transform: translateY( -1px );
}

.basket--empty {
	background: transparent;
	border-color: var( --line-strong );
	color: var( --ink );
}

.basket--empty:hover {
	background: var( --ink );
	border-color: var( --ink );
	color: var( --paper );
}

.basket svg {
	width: 1.1em;
	height: 1.1em;
	fill: currentColor;
}

.basket__count {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	min-width: 1.5em;
	height: 1.5em;
	padding: 0 .35em;
	border-radius: 99px;
	background: var( --brand );
	color: var( --brand-fg );
	font-size: .72rem;
	font-weight: 800;
	line-height: 1;
}

.basket--empty .basket__count {
	background: var( --surface-2 );
	color: var( --muted );
}

.basket--empty:hover .basket__count {
	background: rgba( 255, 255, 255, .16 );
	color: var( --paper );
}

/* Mobile toggle */

/*
 * The button holds two spans: the bars, and the label for a screen reader. The
 * bar rules below are written against :not( .screen-reader-text ) because a
 * bare `.nav-toggle span` also lands on the label — .nav-toggle span beats
 * .screen-reader-text on specificity, so the label was being repainted as a
 * second 19px bar, complete with its own ::before and ::after, sitting at its
 * static position off to one side. That is what was throwing the icon out of
 * the circle: the icon was drawn twice.
 */
.nav-toggle {
	position: relative;
	display: none;
	align-items: center;
	justify-content: center;
	width: 46px;
	height: 46px;
	padding: 0;
	background: transparent;
	border: 2px solid var( --line-strong );
	border-radius: 99px;
	cursor: pointer;
	transition: background var( --speed ) var( --ease ), border-color var( --speed ) var( --ease );
}

.nav-toggle:hover,
.nav-toggle[aria-expanded="true"] {
	background: var( --surface );
	border-color: var( --ink );
}

/* The bars are centred on the button rather than laid out in it, so nothing
   else the button happens to contain can shift them off the middle. */
.nav-toggle span:not( .screen-reader-text ) {
	position: absolute;
	top: 50%;
	left: 50%;
	display: block;
	width: 19px;
	height: 2px;
	margin: -1px 0 0 -9.5px;
	background: var( --ink );
}

.nav-toggle span:not( .screen-reader-text )::before,
.nav-toggle span:not( .screen-reader-text )::after {
	content: "";
	position: absolute;
	left: 0;
	width: 19px;
	height: 2px;
	background: var( --ink );
	transition: transform var( --speed ) var( --ease ), opacity var( --speed ) var( --ease );
}

.nav-toggle span:not( .screen-reader-text )::before {
	top: -6px;
}

.nav-toggle span:not( .screen-reader-text )::after {
	top: 6px;
}

.nav-toggle[aria-expanded="true"] span:not( .screen-reader-text ) {
	background: transparent;
}

.nav-toggle[aria-expanded="true"] span:not( .screen-reader-text )::before {
	transform: translateY( 6px ) rotate( 45deg );
}

.nav-toggle[aria-expanded="true"] span:not( .screen-reader-text )::after {
	transform: translateY( -6px ) rotate( -45deg );
}

/*
 * The dimmer behind the mobile drawer. Added by the script rather than sitting
 * in the markup, because a fixed overlay cannot be a child of the drawer: the
 * drawer is moved with a transform, and a transform makes an element the
 * containing block for anything fixed inside it, so the overlay would be
 * trapped in the panel instead of covering the page.
 */
.nav-scrim {
	position: fixed;
	inset: 0;
	z-index: 110;
	background: rgba( 11, 14, 20, .5 );
	opacity: 0;
	visibility: hidden;
	transition: opacity var( --speed ) var( --ease ), visibility var( --speed );
}

.nav-scrim.is-on {
	opacity: 1;
	visibility: visible;
}

/* A drawer is scrolled instead of the page underneath it. */
.has-nav-open,
.has-nav-open body {
	overflow: hidden;
}

/* The scrolled header is frosted, which is right over the page and wrong over
   the drawer — the dark panel shows through it and the logo stops reading. */
.has-nav-open .site-header.is-stuck {
	background: var( --paper );
	-webkit-backdrop-filter: none;
	backdrop-filter: none;
}

/* ==========================================================================
   8. Hero
   ========================================================================== */

.hero {
	position: relative;
	background: var( --ink );
	color: var( --paper );
	overflow: hidden;
}

.hero__media {
	position: absolute;
	inset: 0;
}

.hero__media img,
.hero__media svg {
	width: 100%;
	height: 100%;
	object-fit: cover;
}

/* Two stops rather than a flat wash: the copy sits in the left third, so the
   right side stays legible as an image instead of being greyed out. */
.hero__media::after {
	content: "";
	position: absolute;
	inset: 0;
	background: linear-gradient( 100deg, rgba( 11, 14, 20, .93 ) 0%, rgba( 11, 14, 20, .78 ) 42%, rgba( 11, 14, 20, .45 ) 100% );
}

.hero__inner {
	position: relative;
	display: grid;
	align-content: center;
	min-height: clamp( 30rem, 72vh, 44rem );
	padding-block: var( --section );
	max-width: 46rem;
}

.hero__title {
	font-size: var( --step-5 );
	margin-bottom: .35em;
	color: var( --paper );
}

.hero__title em {
	font-style: normal;
	color: var( --brand );
}

.hero__text {
	font-size: var( --step-1 );
	line-height: 1.5;
	color: rgba( 255, 255, 255, .82 );
	max-width: 46ch;
	margin-bottom: var( --gap-lg );
}

.hero__actions {
	display: flex;
	flex-wrap: wrap;
	gap: .8rem;
}

.hero__facts {
	display: flex;
	flex-wrap: wrap;
	gap: 1.5rem 2.8rem;
	margin-top: var( --gap-lg );
	padding-top: var( --gap-lg );
	border-top: 1px solid rgba( 255, 255, 255, .16 );
}

.hero__fact strong {
	display: block;
	font-family: var( --font-display );
	font-size: var( --step-2 );
	font-weight: 700;
	line-height: 1;
	letter-spacing: -.02em;
	font-variant-numeric: tabular-nums;
	color: var( --brand );
}

.hero__fact span {
	display: block;
	margin-top: .3rem;
	font-size: var( --step--1 );
	letter-spacing: .1em;
	text-transform: uppercase;
	color: rgba( 255, 255, 255, .68 );
}

/* ---------------------------------------------------------------------------
   The hero product strip

   Four doors under the claim. The one being pointed at opens and its
   neighbours give up the width for it, which is done by transitioning
   flex-grow: the panels share a single line, so what one takes the others
   must yield, and the browser interpolates the whole row from one number per
   panel. No script, no measuring, and nothing to re-run on resize.
   ------------------------------------------------------------------------ */

/*
 * With the strip on, the hero is one screen rather than two bands stacked. The
 * panels come out of the flow and become the background of the whole section,
 * and the claim is set over them — so the visitor takes the sentence and the
 * four ranges in a single glance instead of scrolling from one to the other.
 *
 * The photograph that sits behind the copy in the plain hero is dropped for the
 * duration. The panels are already four photographs; a fifth underneath them is
 * a decode and a download for an image nobody will ever see.
 */
.hero--panels .hero__media {
	display: none;
}

.hero__panels {
	position: relative;
	display: flex;
	height: clamp( 17rem, 40vh, 27rem );
	margin: 0;
	padding: 0;
	list-style: none;
	border-top: 1px solid rgba( 255, 255, 255, .14 );
}

.hero--panels .hero__panels {
	position: absolute;
	inset: 0;
	z-index: 0;
	height: auto;
	border-top: 0;
}

/*
 * The join between the two halves. A wash laid over the panels — strong at the
 * head where the claim sits, gone by three quarters of the way down so the
 * photographs are still photographs and the labels along the foot keep their
 * own contrast rather than being dulled twice.
 *
 * Without it the headline is set on whatever happens to be in the first panel,
 * and the hero reads as two things overlapping instead of one thing.
 */
.hero--panels::before {
	content: "";
	position: absolute;
	inset: 0;
	z-index: 1;
	pointer-events: none;
	background: linear-gradient( to bottom, rgba( 11, 14, 20, .88 ) 0%, rgba( 11, 14, 20, .74 ) 30%, rgba( 11, 14, 20, .4 ) 52%, rgba( 11, 14, 20, 0 ) 74% );
}

/*
 * The copy is over the doors, so it must not stand in front of them: the block
 * is transparent to the pointer and only the buttons take it back. Move across
 * the sentence and the panel behind it opens, which is the behaviour you want —
 * the claim is not a lid on the first range.
 *
 * The cost is that the headline can no longer be selected with a drag. That is
 * a fair price for a strip that answers across its whole width instead of only
 * in the gaps around the text.
 */
.hero--panels .wrap {
	position: relative;
	z-index: 2;
	pointer-events: none;
}

.hero--panels .btn {
	pointer-events: auto;
}

/* Set high in the frame rather than centred in it: the top third is where the
   wash is strong enough to carry white text, and the foot of the screen now
   belongs to the panel labels.

   Short of the fold rather than exactly on it. The header is sticky and takes
   flow space, so a hero of 100vh puts the labels under the bottom edge; and the
   sliver of the next band that 88vh leaves showing is what tells a visitor
   there is more here than one screen. */
.hero--panels .hero__inner {
	align-content: start;
	min-height: min( 88vh, 50rem );
	padding-block: clamp( 3.5rem, 9vh, 6rem ) calc( var( --section ) * 2 );
}

/* ---------------------------------------------------------------------------
   The strip on its own

   No sentence over the panels, so no wash under the sentence. What is left is
   four photographs of what the factory makes, at the brightness they were
   uploaded at, and the only darkening on the page is the short gradient at the
   foot of each panel that the label needs in order to be readable at all.

   Taller than the strip-behind-a-claim version, because it is now the whole
   first screen rather than the bottom half of one. Short of the fold on
   purpose: the band of the next section showing under it is what tells a
   visitor to keep going.
   ------------------------------------------------------------------------ */
.hero--strip .hero__panels {
	height: min( 76vh, 44rem );
	border-top: 0;
}

/* Half the length of the default and gone by the time it reaches the middle of
   the picture, so the photograph is a photograph everywhere above the label. */
.hero--strip .hero__panel-media::after {
	background: linear-gradient( to top, rgba( 11, 14, 20, .8 ) 0%, rgba( 11, 14, 20, .3 ) 30%, rgba( 11, 14, 20, 0 ) 58% );
}

.hero--strip .hero__panel:hover .hero__panel-media::after,
.hero--strip .hero__panel:focus-within .hero__panel-media::after {
	opacity: .7;
}

.hero__panel {
	position: relative;
	flex: 1 1 0;
	min-width: 0;
	transition: flex-grow .55s var( --ease );
}

/* A hairline between doors rather than a border on each, so the first panel
   still meets the edge of the screen. Logical, so it moves in Arabic. */
.hero__panel + .hero__panel {
	border-inline-start: 1px solid rgba( 255, 255, 255, .16 );
}

/*
 * Two rules. The first shrinks every panel the moment the pointer enters the
 * strip; the second gives the width back to the one under it.
 *
 * The second is written .hero__panels .hero__panel:hover rather than the
 * shorter .hero__panel:hover it looks like it wants to be, and that is not
 * style. The shrink rule scores two classes and a pseudo-class; the plain form
 * of the open rule scores one class and a pseudo-class and therefore LOSES to
 * it, on the hovered panel as much as on its neighbours — every panel would be
 * set to .72, four equal grow factors on a zero basis would come out four equal
 * widths, and the strip would light up on hover without ever opening. The
 * leading .hero__panels puts the two on level specificity, and source order
 * then decides in favour of the one written second.
 */
.hero__panels:hover .hero__panel,
.hero__panels:focus-within .hero__panel {
	flex-grow: .72;
}

.hero__panels .hero__panel:hover,
.hero__panels .hero__panel:focus-within {
	flex-grow: 2.4;
}

.hero__panel-link {
	position: absolute;
	inset: 0;
	display: block;
	overflow: hidden;
	color: var( --paper );
	text-decoration: none;
}

/* Outlined on the inside: the panels butt against one another with no gap, so
   a ring drawn outside the box would be hidden under the neighbour. */
.hero__panel-link:focus-visible {
	outline: 3px solid var( --brand );
	outline-offset: -3px;
}

.hero__panel-media {
	position: absolute;
	inset: 0;
	display: block;
	background: var( --ink );
}

.hero__panel-media img,
.hero__panel-media svg {
	width: 100%;
	height: 100%;
	object-fit: cover;
	transition: transform .7s var( --ease );
}

.hero__panel:hover .hero__panel-media img,
.hero__panel:hover .hero__panel-media svg,
.hero__panel:focus-within .hero__panel-media img,
.hero__panel:focus-within .hero__panel-media svg {
	transform: scale( 1.05 );
}

/*
 * Heavy at the foot and clear at the head, because that is where the label
 * sits and where the photograph is worth seeing. The closed panels are darker
 * than the open one: it is the contrast between them, not the absolute
 * darkness of any one, that tells the eye which door is open.
 */
.hero__panel-media::after {
	content: "";
	position: absolute;
	inset: 0;
	background: linear-gradient( to top, rgba( 11, 14, 20, .92 ) 0%, rgba( 11, 14, 20, .42 ) 45%, rgba( 11, 14, 20, .12 ) 100% );
	transition: opacity var( --speed ) var( --ease );
}

.hero__panel:hover .hero__panel-media::after,
.hero__panel:focus-within .hero__panel-media::after {
	opacity: .55;
}

/* The brand colour as the sill of the open door. Scaled from nothing rather
   than faded in, so it reads as the panel opening rather than as a light
   coming on. */
.hero__panel-link::after {
	content: "";
	position: absolute;
	inset-inline: 0;
	bottom: 0;
	height: 4px;
	background: var( --brand );
	transform: scaleX( 0 );
	transform-origin: left center;
	transition: transform .45s var( --ease );
}

.hero__panel:hover .hero__panel-link::after,
.hero__panel:focus-within .hero__panel-link::after {
	transform: scaleX( 1 );
}

/*
 * writing-mode rather than a rotation: the glyphs are turned by the text
 * engine, so the line still wraps, still respects the script's own direction
 * and still measures its own width. A transform would have needed a hard-coded
 * box to rotate inside, and would have laid Arabic on its side.
 */
.hero__panel-label {
	position: absolute;
	inset-inline-start: 1.15rem;
	bottom: 1.4rem;
	writing-mode: vertical-rl;
	transform: rotate( 180deg );
	font-family: var( --font-display );
	font-size: var( --step--1 );
	font-weight: 700;
	letter-spacing: var( --track-label );
	text-transform: uppercase;
	white-space: nowrap;
	transition: opacity .25s var( --ease );
}

.hero__panel:hover .hero__panel-label,
.hero__panel:focus-within .hero__panel-label {
	opacity: 0;
}

/*
 * The open panel's contents. Held out of the flow with absolute positioning so
 * that the width animation never has to reflow this text — a paragraph
 * re-wrapping sixty times a second during a transition is what makes these
 * strips feel cheap.
 */
.hero__panel-open {
	position: absolute;
	inset-inline: var( --gap ) var( --gap );
	bottom: var( --gap );
	display: flex;
	flex-direction: column;
	align-items: flex-start;
	gap: .3rem;
	opacity: 0;
	transform: translateY( .7rem );
	transition: opacity .3s var( --ease ) .1s, transform .3s var( --ease ) .1s;
}

.hero__panel:hover .hero__panel-open,
.hero__panel:focus-within .hero__panel-open {
	opacity: 1;
	transform: none;
}

.hero__panel-name {
	font-family: var( --font-display );
	font-size: var( --step-2 );
	font-weight: 700;
	line-height: 1.1;
	letter-spacing: -.01em;
	white-space: nowrap;
}

.hero__panel-count {
	font-size: var( --step--1 );
	letter-spacing: .08em;
	text-transform: uppercase;
	color: rgba( 255, 255, 255, .7 );
	white-space: nowrap;
}

.hero__panel-go {
	display: inline-flex;
	align-items: center;
	gap: .4rem;
	margin-top: .35rem;
	color: var( --brand );
	font-size: var( --step--1 );
	font-weight: 700;
	letter-spacing: var( --track-label );
	text-transform: uppercase;
	white-space: nowrap;
}

.hero__panel-go svg {
	width: 16px;
	height: 16px;
}

/*
 * A touchscreen has no pointer to hover with, so the accordion would leave
 * every panel shut and every name turned on its side. Where hover is coarse or
 * absent the strip is simply four open panels of equal width.
 */
@media ( hover: none ) {

	.hero__panels:hover .hero__panel,
	.hero__panels:focus-within .hero__panel,
	.hero__panels .hero__panel:hover,
	.hero__panels .hero__panel:focus-within {
		flex-grow: 1;
	}

	.hero__panel-label {
		display: none;
	}

	.hero__panel-open {
		opacity: 1;
		transform: none;
	}

	.hero__panel-name {
		font-size: var( --step-0 );
	}

	/* A tablet in landscape is a touchscreen wider than the breakpoint below,
	   so it gets four permanently open panels of a quarter width each. The
	   single line those names are held on for the animation has to be released
	   here or the longer ones are cut off mid-word. */
	.hero__panel-name,
	.hero__panel-count,
	.hero__panel-go {
		white-space: normal;
	}
}

/*
 * Below this the strip stops being a strip. Four panels across a tablet gives
 * slivers too narrow for the shortest of these names, so the row becomes a
 * pair of rows, every panel is open, and nothing depends on a pointer.
 */
@media ( max-width: 62rem ) {

	/*
	 * And once it is two rows it cannot be the background either — a grid
	 * stretched to inset: 0 would squash four cells into whatever height the
	 * copy happened to need. The strip goes back into the flow under the claim,
	 * the wash comes off, the photograph behind the copy comes back, and the
	 * hero is two honest bands again. That is the layout a phone wants anyway.
	 */
	.hero--panels .hero__panels {
		position: relative;
		inset: auto;
	}

	.hero--panels::before {
		display: none;
	}

	.hero--panels .hero__media {
		display: block;
	}

	.hero--panels .wrap {
		pointer-events: auto;
	}

	.hero--panels .hero__inner {
		align-content: center;
		min-height: clamp( 22rem, 52vh, 34rem );
		padding-block: calc( var( --section ) * .8 );
	}

	.hero__panels {
		display: grid;
		grid-template-columns: repeat( 2, minmax( 0, 1fr ) );
		height: auto;
	}

	/* Written out again rather than left to the rule above it: two classes beat
	   one, so the fixed height set on the strip layout would otherwise survive
	   into the grid and squash four cells into one screen. */
	.hero--strip .hero__panels {
		height: auto;
	}

	.hero__panel {
		aspect-ratio: 5 / 4;
	}

	/* Hairlines in two directions now, and only between cells: the grid has an
	   outer edge of its own and does not want ruling as well. */
	.hero__panel + .hero__panel {
		border-inline-start: 0;
	}

	.hero__panel:nth-child( even ) {
		border-inline-start: 1px solid rgba( 255, 255, 255, .16 );
	}

	.hero__panel:nth-child( n + 3 ) {
		border-top: 1px solid rgba( 255, 255, 255, .16 );
	}

	.hero__panel-label {
		display: none;
	}

	.hero__panel-open {
		opacity: 1;
		transform: none;
	}

	.hero__panel-name {
		font-size: var( --step-0 );
	}

	.hero__panel-link::after {
		transform: scaleX( 1 );
		height: 3px;
	}
}

@media ( max-width: 30rem ) {

	.hero__panels {
		grid-template-columns: minmax( 0, 1fr );
	}

	.hero__panel {
		aspect-ratio: 16 / 9;
	}

	/* One column, so the vertical rule has nothing to separate and every panel
	   but the first sits under another. */
	.hero__panel:nth-child( even ) {
		border-inline-start: 0;
	}

	.hero__panel + .hero__panel {
		border-top: 1px solid rgba( 255, 255, 255, .16 );
	}
}

/* A compact hero for inner pages */

.page-hero {
	background: var( --ink );
	color: var( --paper );
	padding-block: clamp( 3rem, 2rem + 4vw, 5.5rem );
	/* Both Customizer colours in one edge: the band is the ink, the rule under
	   it is the brand. Every inner page opens with this, which is the answer to
	   "the theme colour only shows on the home page". */
	border-bottom: 4px solid var( --brand );
}

.page-hero h1 {
	color: var( --paper );
	margin-bottom: .3em;
}

.page-hero p {
	color: rgba( 255, 255, 255, .78 );
	max-width: 62ch;
	margin: 0;
}

.breadcrumbs {
	margin-bottom: 1rem;
	font-size: var( --step--1 );
	letter-spacing: .06em;
	text-transform: uppercase;
	color: rgba( 255, 255, 255, .6 );
}

.breadcrumbs a {
	color: rgba( 255, 255, 255, .8 );
	text-decoration: none;
}

.breadcrumbs a:hover {
	color: var( --brand );
}

.breadcrumbs span + span::before {
	content: "/";
	margin: 0 .5em;
	color: rgba( 255, 255, 255, .35 );
}

/* ==========================================================================
   9. Sections
   ========================================================================== */

.marquee {
	background: var( --brand );
	color: var( --brand-fg );
	padding-block: .85rem;
	overflow: hidden;
}

.marquee__inner {
	display: flex;
	align-items: center;
	justify-content: center;
	flex-wrap: wrap;
	gap: .6rem 2.4rem;
	font-family: var( --font-display );
	font-size: var( --step-0 );
	font-weight: 600;
	letter-spacing: var( --track-label );
	text-transform: uppercase;
	text-align: center;
}

.marquee__inner span {
	display: inline-flex;
	align-items: center;
	gap: .7rem;
}

.marquee__inner span::before {
	content: "";
	width: 7px;
	height: 7px;
	background: var( --ink );
	border-radius: 50%;
	flex: 0 0 auto;
}

/* Two claims across is about as much as a tablet holds before the longest of
   them starts wrapping inside itself. */
@media ( max-width: 880px ) {

	.marquee__inner {
		gap: .45rem 1.6rem;
		font-size: var( --step--1 );
	}
}

/*
 * Centred wrapping is what broke this on a phone. Five claims of five
 * different lengths, centred one under another, give five different left
 * edges and one orphan on the last row — which reads as text that has come
 * loose rather than as a row of guarantees. One claim per line against a
 * common left margin, separated by a hairline, is the same content in an
 * order the eye can follow, and it costs about the same height as the ragged
 * version did.
 */
@media ( max-width: 560px ) {

	.marquee {
		padding-block: .55rem;
	}

	.marquee__inner {
		display: grid;
		grid-template-columns: minmax( 0, 1fr );
		gap: 0;
		font-size: var( --step--2 );
		text-align: start;
	}

	.marquee__inner span {
		padding-block: .42rem;
	}

	/* Ink at low opacity rather than a token: the band's background is the
	   brand colour, so the rule has to sit on that and not on the page. */
	.marquee__inner span + span {
		border-top: 1px solid rgba( 11, 14, 20, .16 );
	}
}

.feature {
	padding: var( --gap-lg );
	background: var( --paper );
	border: 1px solid var( --line );
	border-radius: var( --radius-lg );
	transition: border-color var( --speed ) var( --ease ), transform var( --speed ) var( --ease );
}

.feature:hover {
	border-color: var( --line-strong );
	transform: translateY( -2px );
}

.feature__icon {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 48px;
	height: 48px;
	margin-bottom: 1rem;
	border-radius: var( --radius );
	background: var( --brand );
}

.feature__icon svg {
	width: 24px;
	height: 24px;
	fill: var( --brand-fg );
}

.feature h3 {
	font-family: var( --font-body );
	font-size: var( --step-1 );
	font-weight: 700;
	text-transform: none;
	letter-spacing: 0;
	line-height: 1.3;
	margin-bottom: .4em;
}

.feature p {
	color: var( --muted );
	margin: 0;
}

.section--ink .feature {
	background: var( --band-2 );
	border-color: var( --band-line );
}

.section--ink .feature p {
	color: var( --band-soft );
}

.stat {
	text-align: center;
}

.stat strong {
	display: block;
	font-family: var( --font-display );
	font-size: var( --step-4 );
	font-weight: 800;
	line-height: 1;
	letter-spacing: -.03em;
	font-variant-numeric: tabular-nums;
}

.stat span {
	display: block;
	margin-top: .4rem;
	font-size: var( --step--1 );
	letter-spacing: .1em;
	text-transform: uppercase;
	color: var( --muted );
}

.section--ink .stat span {
	color: var( --band-soft );
}

.section--ink .stat strong {
	color: var( --band-link );
}

/* Category tiles

   These used to be a dark box with the label floated over the picture under a
   heavy black scrim. That works over photography and fails over everything
   else: with a light placeholder underneath it, an 88% black gradient reads as
   a smoked-glass wash across the bottom of every tile, which is exactly the
   "glossy" look this was pulled up for.

   The label now sits on paper below the image, where it is legible whatever
   the picture turns out to be — a real photograph, a flat studio shot on
   white, or the generated placeholder — and the tile no longer has to fight
   its own contents. */

.tile {
	position: relative;
	display: flex;
	flex-direction: column;
	overflow: hidden;
	border-radius: var( --radius-lg );
	background: var( --paper );
	border: 1px solid var( --line );
	text-decoration: none;
	color: inherit;
	transition: border-color var( --speed ) var( --ease ),
		box-shadow var( --speed ) var( --ease ),
		transform var( --speed ) var( --ease );
}

.tile:hover,
.tile:focus-visible {
	border-color: var( --line-strong );
	box-shadow: var( --shadow );
	transform: translateY( -2px );
	color: inherit;
}

.tile__media {
	display: block;
	position: relative;
	overflow: hidden;
	aspect-ratio: 4 / 3;
	background: var( --surface );
}

.tile__media img,
.tile__media svg {
	width: 100%;
	height: 100%;
	object-fit: cover;
	transition: transform .5s var( --ease );
}

.tile:hover .tile__media img,
.tile:hover .tile__media svg {
	transform: scale( 1.04 );
}

/* Two rows of text on the left, one arrow on the right spanning both. A grid
   rather than nested flex boxes, so the arrow lands in the same place on every
   tile regardless of how long the category name runs — a ragged column of
   arrows is the sort of thing nobody names but everybody registers as untidy. */
.tile__body {
	display: grid;
	grid-template-columns: minmax( 0, 1fr ) auto;
	align-items: center;
	column-gap: var( --gap );
	padding: 1rem 1.1rem;
}

.tile__title {
	grid-column: 1;
	grid-row: 1;
	display: block;
	font-family: var( --font-display );
	font-size: var( --step-1 );
	font-weight: 700;
	letter-spacing: -.02em;
	line-height: 1.15;
	margin: 0;
	color: var( --ink );
}

.tile__count {
	grid-column: 1;
	grid-row: 2;
	display: block;
	margin-top: .2rem;
	font-size: var( --step--2 );
	font-weight: 600;
	letter-spacing: var( --track-label );
	text-transform: uppercase;
	color: var( --muted );
}

.tile__go {
	grid-column: 2;
	grid-row: 1 / span 2;
	display: grid;
	place-items: center;
	width: 34px;
	height: 34px;
	border-radius: 50%;
	background: var( --surface );
	color: var( --ink );
	transition: background var( --speed ) var( --ease ),
		transform var( --speed ) var( --ease );
}

.tile__go svg {
	width: 18px;
	height: 18px;
}

.tile:hover .tile__go {
	background: var( --brand );
	color: var( --brand-fg );
	transform: translateX( 3px );
}

/* Media frame — the picture half of a .split band. */

.frame {
	position: relative;
	overflow: hidden;
	border-radius: var( --radius-lg );
	background: var( --surface );
	aspect-ratio: 4 / 3;
}

.frame img,
.frame svg {
	width: 100%;
	height: 100%;
	object-fit: cover;
}

.frame--tall {
	aspect-ratio: 3 / 4;
}

/* Full width, so the ratio has to be one that stays under a screen when it is
   eleven hundred pixels across. */
.frame--banner {
	aspect-ratio: 21 / 9;
}

@media ( max-width: 40rem ) {

	.frame--banner {
		aspect-ratio: 16 / 10;
	}
}

/* ---------------------------------------------------------------------------
   The About page

   Three bands the rest of the site has no use for: the story and its portrait,
   the dated milestones, and the people. Everything else on that page is a part
   borrowed from the home page and already has its rules above.
   ------------------------------------------------------------------------ */

.story {
	display: grid;
	gap: var( --gap-lg );
}

/*
 * The photograph is the narrower column and it is second in the markup, so on a
 * phone the story is read before the picture is downloaded rather than after.
 * Below the breakpoint the modifier stops applying and the single-column rule
 * above is all that is left, which is the right order without further work.
 */
@media ( min-width: 62rem ) {

	.story--shot {
		grid-template-columns: minmax( 0, 1.55fr ) minmax( 0, 1fr );
		gap: calc( var( --gap-lg ) * 1.4 );
		align-items: start;
	}
}

/*
 * A grid child is at least as wide as its own widest content, so one oversized
 * table pasted into the editor would push this column past the photograph beside
 * it and carry the whole page sideways with it.
 */
.story__main {
	min-width: 0;
}

.story__para {
	max-width: 68ch;
	margin-bottom: 1em;
	color: var( --muted );
}

/*
 * The standfirst. Larger and in the ink colour rather than the muted one,
 * because it is doing the job the heading cannot — the heading says what kind
 * of company this is, the standfirst says for how long and for whom, and a
 * buyer who reads nothing else on the page should still have both.
 */
.story__para--lead {
	font-size: var( --step-1 );
	line-height: 1.5;
	color: var( --ink );
}

/* The client's own editor content, if they wrote any. It arrives with the
   entry rules already on it; all that is wanted here is air above. */
.story__entry {
	margin-top: var( --gap-lg );
}

.story__actions {
	display: flex;
	flex-wrap: wrap;
	gap: .8rem;
	margin-top: var( --gap-lg );
}

.story__aside {
	display: grid;
	gap: var( --gap );
	position: sticky;
	top: calc( var( --gap-lg ) + 4rem );
}

/*
 * Sticky is wrong the moment the aside is taller than the viewport — it pins
 * the top of a column whose bottom the reader is trying to reach, and the quote
 * never arrives. It is also wrong in one column, where there is nothing beside
 * it to scroll past.
 */
@media ( max-width: 62rem ), ( max-height: 44rem ) {

	.story__aside {
		position: static;
	}
}

.story__pull {
	margin: 0;
	padding: var( --gap ) 0 0;
	border-top: 3px solid var( --brand );
	font-family: var( --font-display );
	font-size: var( --step-1 );
	font-weight: 700;
	line-height: 1.35;
	letter-spacing: -.01em;
	color: var( --ink );
}

.story__pull p {
	margin: 0;
}

/* Without a photograph the quote has no column, so it is set across the foot of
   the story as a rule and a sentence instead. */
.story__pull--wide {
	max-width: 46ch;
	margin-top: var( --gap-lg );
}

/* Milestones ------------------------------------------------------------- */

.marks {
	list-style: none;
	margin: 0;
	padding: 0;
}

/*
 * A year, then a rule, then the entry. The rule is a border on the item rather
 * than a pseudo-element so it runs the full height of whichever row is tallest
 * without anything having to measure it.
 */
.marks__item {
	display: grid;
	grid-template-columns: minmax( 5.5rem, auto ) minmax( 0, 1fr );
	gap: var( --gap );
	padding-block: var( --gap );
	border-top: 1px solid var( --line );
}

.marks__item:last-child {
	border-bottom: 1px solid var( --line );
}

.marks__year {
	font-family: var( --font-display );
	font-size: var( --step-2 );
	font-weight: 700;
	line-height: 1.1;
	letter-spacing: -.02em;
	font-variant-numeric: tabular-nums;
	color: var( --brand );
}

.marks__title {
	font-size: var( --step-0 );
	margin-bottom: .3em;
}

.marks__body p {
	margin: 0;
	max-width: 62ch;
	color: var( --muted );
}

@media ( max-width: 34rem ) {

	.marks__item {
		grid-template-columns: minmax( 0, 1fr );
		gap: .4rem;
	}
}

/* People ----------------------------------------------------------------- */

.person {
	padding: var( --gap );
	background: var( --paper );
	border: 1px solid var( --line );
	border-radius: var( --radius );
}

/*
 * Initials in a square rather than a circle: a circle reads as an avatar and
 * invites the question of where the photograph went. A square with the brand
 * colour behind it reads as a mark, which is what it is.
 */
.person__mark {
	display: grid;
	place-items: center;
	width: 3rem;
	height: 3rem;
	margin-bottom: var( --gap );
	border-radius: var( --radius );
	background: var( --brand );
	color: var( --brand-fg );
	font-family: var( --font-display );
	font-size: var( --step-0 );
	font-weight: 700;
	letter-spacing: .02em;
}

.person__name {
	font-size: var( --step-0 );
	margin-bottom: .15em;
}

.person__role {
	margin-bottom: .6em;
	font-size: var( --step--1 );
	letter-spacing: var( --track-label );
	text-transform: uppercase;
	color: var( --brand );
}

.person__text {
	margin: 0;
	color: var( --muted );
}

/* Ticked list — used by the factory band and the capabilities page. */

.checklist {
	list-style: none;
	margin: 0;
	padding: 0;
	display: grid;
	gap: .7rem;
}

.checklist li {
	display: flex;
	align-items: flex-start;
	gap: .6rem;
	line-height: 1.5;
}

.checklist svg {
	flex: none;
	width: 20px;
	height: 20px;
	margin-top: .15em;
	color: var( --brand-ink );
}

.section--ink .checklist svg {
	color: var( --band-link );
}

/* Numbered steps — how ordering works. */

.steps {
	list-style: none;
	margin: 0;
	padding: 0;
	display: grid;
	gap: var( --gap );
	grid-template-columns: repeat( auto-fit, minmax( 220px, 1fr ) );
	counter-reset: crossbar-step;
}

.step {
	position: relative;
	padding: var( --gap );
	background: var( --paper );
	border: 1px solid var( --line );
	border-radius: var( --radius-lg );
}

.step__n {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 2.2rem;
	height: 2.2rem;
	margin-bottom: .8rem;
	border-radius: 99px;
	background: var( --brand );
	color: var( --brand-fg );
	font-family: var( --font-display );
	font-size: var( --step-0 );
	line-height: 1;
}

.step h3 {
	font-size: var( --step-1 );
	margin: 0 0 .4em;
}

.step p {
	margin: 0;
	color: var( --muted );
	font-size: var( --step--1 );
	line-height: 1.6;
}

.section--ink .step {
	background: var( --band-2 );
	border-color: var( --band-edge );
}

.section--ink .step p {
	color: var( --band-soft );
}

/* Certifications */

.certs {
	display: flex;
	flex-wrap: wrap;
	gap: .6rem;
	margin: 0;
	padding: 0;
	list-style: none;
}

.cert {
	display: inline-flex;
	align-items: center;
	gap: .5rem;
	padding: .45em .85em;
	border: 1px solid var( --field-line );
	border-radius: 99px;
	font-size: var( --step--1 );
	font-weight: 600;
	letter-spacing: .04em;
	text-decoration: none;
	color: var( --ink );
	background: var( --paper );
}

.cert:hover {
	border-color: var( --ink );
	color: var( --ink );
}

.cert img {
	max-height: 20px;
	width: auto;
}

.cert svg {
	width: 1.05em;
	height: 1.05em;
	flex: 0 0 auto;
	fill: currentColor;
	color: var( --muted );
}

/* "OEKO-TEX" is a hyphenated name and the chip was breaking it across two
   lines, which reads as a fault rather than a certification. */
.cert__name {
	white-space: nowrap;
}

/* Buyer references */

.quote-card {
	margin: 0;
	padding: var( --gap-lg );
	background: var( --paper );
	border: 1px solid var( --line );
	border-radius: var( --radius-lg );
	display: flex;
	flex-direction: column;
	gap: var( --gap );
}

.quote-card blockquote {
	margin: 0;
	padding: 0;
	border: 0;
	font-size: var( --step-1 );
	line-height: 1.5;
}

.quote-card__who {
	display: flex;
	align-items: center;
	gap: .8rem;
	margin-top: auto;
	font-size: var( --step--1 );
	color: var( --muted );
}

.quote-card__who strong {
	display: block;
	color: var( --ink );
	font-size: var( --step-0 );
}

/* Frequently asked questions

   Native <details>, so the open and closed state is the browser's business and
   not a class this stylesheet has to keep in sync with a script. Everything
   below is decoration on top of behaviour that already works. */

.faq {
	border-top: 1px solid var( --line );
}

.faq__item {
	border-bottom: 1px solid var( --line );
}

.faq__q {
	display: flex;
	align-items: flex-start;
	justify-content: space-between;
	gap: var( --gap );
	padding: 1.15rem 0;
	cursor: pointer;
	font-family: var( --font-display );
	font-weight: 600;
	font-size: var( --step-1 );
	line-height: 1.35;
	letter-spacing: -.012em;
	list-style: none;
	transition: color var( --speed ) var( --ease );
}

/* Safari draws its own disclosure triangle from a WebKit-only pseudo-element
   that list-style: none does not reach. */
.faq__q::-webkit-details-marker {
	display: none;
}

.faq__q:hover {
	color: var( --brand-ink );
}

.faq__item[open] .faq__q {
	color: var( --brand-ink );
}

/* Focus lands on the summary, which is the whole row, so the ring is inset a
   little rather than wrapped tight around the text. */
.faq__q:focus-visible {
	outline: 2px solid var( --accent );
	outline-offset: 3px;
	border-radius: 3px;
}

/* A plus that becomes a minus: two bars, the upright one rotated away. Drawn
   rather than iconed because it has to animate between the two states. */
.faq__mark {
	position: relative;
	flex: 0 0 auto;
	width: 1.15rem;
	height: 1.15rem;
	margin-top: .25em;
}

.faq__mark::before,
.faq__mark::after {
	content: "";
	position: absolute;
	inset-inline: 0;
	top: 50%;
	height: 2px;
	background: currentColor;
	transition: transform var( --speed ) var( --ease );
}

.faq__mark::before {
	transform: translateY( -50% );
}

.faq__mark::after {
	transform: translateY( -50% ) rotate( 90deg );
}

.faq__item[open] .faq__mark::after {
	transform: translateY( -50% ) rotate( 0deg );
}

.faq__a {
	padding: 0 0 1.3rem;
	max-width: 68ch;
}

.faq__a p {
	margin: 0;
	color: var( --muted );
}

.faq__more {
	margin: var( --gap-lg ) 0 0;
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: .6rem;
	color: var( --muted );
	font-size: var( --step--1 );
}

/* CTA band */

.cta {
	background: var( --ink );
	color: var( --paper );
	border-radius: var( --radius-lg );
	padding: var( --gap-lg );
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	justify-content: space-between;
	gap: var( --gap );
}

.cta h2 {
	color: var( --paper );
	margin: 0 0 .3em;
}

.cta p {
	color: rgba( 255, 255, 255, .76 );
	margin: 0;
	max-width: 52ch;
}

.cta__actions {
	display: flex;
	flex-wrap: wrap;
	gap: .7rem;
}

/* ==========================================================================
   10. Product cards and grids
   ========================================================================== */

/* The card carries only what a buyer scanning a grid needs in order to decide
   whether to stop — photograph, group, name, price. Everything else is in the
   drawer further down this file, which opens over the row below rather than
   growing the card, because a card that grows moves every card under it while
   the cursor is still travelling. overflow is visible for that drawer's sake,
   so the photograph now rounds its own top corners instead of being clipped by
   the card. */
.card {
	position: relative;
	display: flex;
	flex-direction: column;
	background: var( --paper );
	border: 1px solid var( --line );
	border-radius: var( --radius-lg );
	transition: border-color var( --speed ) var( --ease ), box-shadow var( --speed ) var( --ease ), transform var( --speed ) var( --ease );
}

.card:hover,
.card:focus-within {
	border-color: var( --line-strong );
	box-shadow: var( --shadow );
	transform: translateY( -2px );

	/* Above its neighbours, or the drawer opens underneath the next row. */
	z-index: 3;
}

/* Squared off while the drawer is open, so the two read as one panel. Scoped to
   cards that have a drawer: a product with no SKU, no minimum, no capabilities
   and no quote button prints none, and squaring its corners on hover for a
   panel that is never going to arrive would just look like a rendering fault. */
.card:hover:has( .card__more ),
.card:focus-within:has( .card__more ) {
	border-bottom-left-radius: 0;
	border-bottom-right-radius: 0;
}

.card__media {
	position: relative;
	display: block;
	aspect-ratio: 1 / 1;
	background: var( --surface );
	border-radius: calc( var( --radius-lg ) - 1px ) calc( var( --radius-lg ) - 1px ) 0 0;
	overflow: hidden;
}

.card__media img,
.card__media svg {
	width: 100%;
	height: 100%;
	object-fit: cover;
	transition: transform .5s var( --ease );
}

.card:hover .card__media img,
.card:focus-within .card__media img,
.card:hover .card__media svg,
.card:focus-within .card__media svg {
	transform: scale( 1.04 );
}

/* Capability flags. In the card body now, laid out in a row that wraps rather
   than a column that stacks. */
.card__flags {
	display: flex;
	flex-wrap: wrap;
	gap: .35rem;
	margin: 0;
}

/* The default flag is an outline, not a fill. It is a qualifier on a product
   and there are usually two of them per card, so it has to be readable up
   close and invisible from across the page. The filled variants below are for
   the one-off cases — a single badge on a single product page — where a fill
   is carrying real emphasis rather than just being loud. */
.flag {
	display: inline-block;
	padding: .3em .65em;
	border-radius: 999px;
	background: var( --paper );
	border: 1px solid var( --line-strong );
	color: var( --muted );
	font-size: var( --step--2 );
	font-weight: 600;
	letter-spacing: var( --track-label );
	text-transform: uppercase;
	line-height: 1.5;
	white-space: nowrap;
}

.flag--brand {
	background: var( --brand );
	border-color: var( --brand );
	color: var( --brand-fg );
}

.flag--ink {
	background: var( --ink );
	border-color: var( --ink );
	color: var( --paper );
}

.flag--accent {
	background: var( --accent );
	border-color: var( --accent );
	color: var( --accent-fg );
}

.card__body {
	display: flex;
	flex-direction: column;
	gap: .45rem;
	padding: 1.1rem;
	flex: 1 1 auto;
}

.card__cat {
	font-size: var( --step--2 );
	font-weight: 600;
	letter-spacing: var( --track-label );
	text-transform: uppercase;
	color: var( --muted );
}

.card__title {
	margin: 0;
	font-family: var( --font-body );
	font-size: var( --step-0 );
	font-weight: 600;
	line-height: 1.35;
	letter-spacing: -.006em;
	text-transform: none;

	/* Two lines, then an ellipsis. Product names in this trade run long —
	   "Sentinel Pro Goalkeeper Glove" is on the short side — and left to grow
	   they push the price out of alignment across the row, which is the one
	   thing in a catalogue grid that has to line up. */
	display: -webkit-box;
	-webkit-line-clamp: 2;
	line-clamp: 2;
	-webkit-box-orient: vertical;
	overflow: hidden;
}

.card__title a {
	color: var( --ink );
	text-decoration: none;
}

/* The whole card is clickable, but only the title is a real link, so the
   accessible name and the tab order stay sane. */
.card__title a::after {
	content: "";
	position: absolute;
	inset: 0;
	z-index: 1;
}

.card__title a:hover {
	color: var( --accent );
}

.card__meta {
	display: flex;
	flex-wrap: wrap;
	gap: .3rem .9rem;
	margin: 0;
	font-size: var( --step--1 );
	font-variant-numeric: tabular-nums;
	color: var( --muted );
}

.card__meta span {
	display: inline-flex;
	align-items: center;
	gap: .35em;
}

.card__foot {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	justify-content: space-between;
	gap: .6rem;
	margin-top: auto;
	padding-top: .85rem;
	/* The rule is pushed back out to the card edges. Inset, it drew a short
	   floating line that read as a mistake; full width it reads as the shelf
	   the price sits on. */
	margin-inline: -1.1rem;
	padding-inline: 1.1rem;
	border-top: 1px solid var( --line );
}

/* Anything interactive inside the card has to sit above the title's overlay
   or the overlay swallows the click. */
.card__foot form,
.card__foot .btn,
.card__add {
	position: relative;
	z-index: 2;
}

.card__add {
	border: 1px solid var( --field-line );
	background: var( --paper );
	border-radius: 999px;
	padding: .4em .85em;
	cursor: pointer;
	color: var( --ink );
	display: inline-flex;
	align-items: center;
	gap: .4em;
	font-size: var( --step--2 );
	font-weight: 600;
	letter-spacing: var( --track-label );
	text-transform: uppercase;
	white-space: nowrap;
	transition: background var( --speed ) var( --ease ),
		border-color var( --speed ) var( --ease ),
		color var( --speed ) var( --ease );
}

.card__add svg {
	width: 1.1em;
	height: 1.1em;
	fill: currentColor;
}

.card__add:hover {
	background: var( --ink );
	border-color: var( --ink );
	color: var( --paper );
}

.card__add[disabled] {
	background: var( --paper );
	border-color: var( --ok );
	color: var( --ok );
	cursor: default;
}

/* ---------------------------------------------------------------------------
   The card drawer

   The second half of the card: SKU, minimum order, capability flags and the
   quote button. Hung below the card at top: 100% and pulled a pixel wider at
   each side so its border sits directly on top of the card's own, which makes
   the two read as one panel rather than as a tooltip that happened to land
   there.

   Hidden with opacity rather than with display or visibility on purpose. A
   button that is display: none cannot be tabbed to, so a keyboard user could
   never reach "Add to quote" — with opacity the button keeps its place in the
   tab order, and the card's own :focus-within rule opens the drawer the moment
   focus arrives, which is the same thing the mouse gets. pointer-events is off
   while it is closed so an invisible drawer cannot swallow a click meant for
   the card underneath it.
   ------------------------------------------------------------------------ */

.card__more {
	position: absolute;
	top: 100%;
	right: -1px;
	left: -1px;
	z-index: 2;

	display: flex;
	flex-direction: column;
	align-items: stretch;
	gap: .55rem;
	padding: .85rem 1.1rem 1rem;

	background: var( --paper );
	border: 1px solid var( --line-strong );
	border-top: 0;
	border-radius: 0 0 var( --radius-lg ) var( --radius-lg );
	box-shadow: var( --shadow );

	opacity: 0;
	pointer-events: none;
	transform: translateY( -4px );
	transition: opacity var( --speed ) var( --ease ), transform var( --speed ) var( --ease );
}

.card:hover .card__more,
.card:focus-within .card__more {
	opacity: 1;
	pointer-events: auto;
	transform: none;
}

/* The button takes a line to itself rather than sharing one with the flags. It
   is the only thing in the drawer that does anything, and a full-width target
   is the difference between a buyer adding three products in a row and a buyer
   missing one. */
.card__more .card__add {
	width: 100%;
	justify-content: center;
	padding: .55em .85em;
}

/* Without a hover-capable pointer there is nothing to open the drawer with, so
   it stops being a drawer: it goes back into the flow underneath the price and
   the card is simply a taller card. Phones have the room — the grid is one or
   two columns wide there, not four. */
@media ( hover: none ) {

	.card__more {
		position: static;
		border: 0;
		border-top: 1px solid var( --line );
		border-radius: 0;
		box-shadow: none;
		opacity: 1;
		pointer-events: auto;
		transform: none;
		padding-top: .8rem;
	}

	.card:hover,
	.card:focus-within {
		border-bottom-left-radius: var( --radius-lg );
		border-bottom-right-radius: var( --radius-lg );
	}
}

/* ==========================================================================
   11. Archive filters
   ========================================================================== */

.catalogue {
	display: grid;
	gap: var( --gap-lg );
	grid-template-columns: 268px minmax( 0, 1fr );
	align-items: start;
}

/* Eleven facets is taller than most screens, so the column scrolls inside
   itself rather than running past the bottom of the viewport and stranding the
   Apply button somewhere the buyer has to scroll the page to reach. */
.filters {
	position: sticky;
	top: 96px;
	max-height: calc( 100vh - 120px );
	overflow-y: auto;
	overscroll-behavior: contain;
	padding: var( --gap );
	background: var( --surface );
	border-radius: var( --radius-lg );
	border-top: 3px solid var( --brand );
}

.filters h2 {
	font-size: var( --step-1 );
	margin-bottom: .6em;
}

.filters__group {
	padding-block: .9rem;
	border-top: 1px solid var( --line-strong );
}

.filters__group:first-of-type {
	border-top: 0;
	padding-top: 0;
}

/* The legend carries a short brand rule. On a page that is otherwise white,
   grey and ink, this and the sidebar's top edge are what tell a visitor the
   catalogue belongs to the same site as the home page. */
.filters__legend {
	display: flex;
	align-items: center;
	gap: .5rem;
	margin-bottom: .5rem;
	font-size: var( --step--1 );
	font-weight: 700;
	letter-spacing: .1em;
	text-transform: uppercase;
	color: var( --ink );
}

.filters__legend::before {
	content: "";
	width: 14px;
	height: 3px;
	flex: 0 0 auto;
	border-radius: 2px;
	background: var( --brand );
}

.filters__list {
	margin: 0;
	padding: 0;
	list-style: none;
	display: flex;
	flex-direction: column;
	gap: .35rem;
}

.filters__list a {
	display: flex;
	align-items: baseline;
	justify-content: space-between;
	gap: .5rem;
	padding: .3rem .5rem;
	margin-inline: -.5rem;
	border-radius: var( --radius );
	color: var( --ink );
	text-decoration: none;
	font-size: var( --step-0 );
}

.filters__list a:hover {
	background: var( --paper );
	color: var( --accent );
}

.filters__list .is-active > a {
	background: var( --ink );
	color: var( --paper );
	font-weight: 600;
}

.filters__list .is-active > a .filters__n {
	color: var( --brand );
}

.filters__n {
	font-size: var( --step--1 );
	color: var( --muted );
}

.filters__list ul {
	margin: .3rem 0 .3rem .8rem;
	padding: 0 0 0 .7rem;
	border-left: 1px solid var( --line-strong );
	list-style: none;
	display: flex;
	flex-direction: column;
	gap: .2rem;
}

.filters__reset {
	display: inline-block;
	font-size: var( --step--1 );
	font-weight: 600;
	letter-spacing: .06em;
	text-transform: uppercase;
}

/* --- The facet groups ---------------------------------------------------- */

/* A details element gives collapsible groups with no JavaScript at all, which
   matters here because the sidebar is the one part of the catalogue a buyer on
   a phone reaches through a button rather than by scrolling. */
.filters__group > summary {
	cursor: pointer;
	list-style: none;
}

.filters__group > summary::-webkit-details-marker {
	display: none;
}

/* The chevron is drawn from a border rather than a glyph so it inherits the
   text colour and needs no icon font. */
.filters__group > summary::after {
	content: "";
	width: .5em;
	height: .5em;
	margin-inline-start: auto;
	border-right: 2px solid var( --muted );
	border-bottom: 2px solid var( --muted );
	transform: translateY( -.15em ) rotate( 45deg );
	transition: transform var( --speed ) var( --ease );
}

.filters__group[open] > summary::after {
	transform: translateY( .1em ) rotate( -135deg );
}

.filters__group > summary:hover::after {
	border-color: var( --brand );
}

.filters__list--choice {
	gap: 0;
}

.filters__list--choice li {
	display: flex;
	align-items: flex-start;
	gap: .55rem;
}

.filters__list--choice input {
	width: 1em;
	height: 1em;
	flex: 0 0 auto;
	margin: .45rem 0 0;
	accent-color: var( --brand );
}

.filters__list--choice label {
	display: flex;
	flex: 1 1 auto;
	align-items: baseline;
	justify-content: space-between;
	gap: .5rem;
	padding-block: .3rem;
	margin: 0;
	font-size: var( --step-0 );
	font-weight: 400;
	line-height: 1.35;
	text-transform: none;
	letter-spacing: 0;
	color: var( --ink );
	cursor: pointer;
}

.filters__list--choice label:hover {
	color: var( --accent );
}

.filters__list--choice input:checked + label {
	font-weight: 600;
}

.filters__list--choice input:checked + label .filters__n {
	color: var( --brand-ink );
}

/* The focus ring belongs on the row, not on a 16px box the buyer is not aiming
   at, so it is drawn on the list item when the input inside it has focus. */
.filters__list--choice li:focus-within {
	outline: 2px solid var( --accent );
	outline-offset: 2px;
	border-radius: var( --radius );
}

.filters__list--choice .is-child label {
	padding-inline-start: .9rem;
	font-size: var( --step--1 );
}

/* Materials and colours are whatever the factory happens to use, so these
   lists have no natural ceiling. Capping the height keeps one long facet from
   burying the ten below it. */
.filters__list--tall {
	max-height: 14rem;
	overflow-y: auto;
	overscroll-behavior: contain;
}

/* Sizes are short tokens — S, M, XL, 5 — and read far better wrapped across
   the column than as thirty one-word rows. */
.filters__list--wrap {
	flex-direction: row;
	flex-wrap: wrap;
	gap: .3rem;
}

.filters__list--wrap li {
	gap: .35rem;
	padding: .2em .6em;
	background: var( --paper );
	border: 1px solid var( --line-strong );
	border-radius: 99px;
}

.filters__list--wrap input {
	margin-top: .3rem;
}

.filters__list--wrap label {
	padding-block: 0;
	font-size: var( --step--1 );
}

/* Sticky so a buyer who has scrolled to the bottom of a long facet does not
   have to scroll back up to submit. */
.filters__apply {
	position: sticky;
	bottom: 0;
	z-index: 1;
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	justify-content: space-between;
	gap: .6rem;
	margin-top: 1rem;
	padding-top: .9rem;
	background: var( --surface );
	border-top: 1px solid var( --line-strong );
}

.catalogue__bar {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	justify-content: space-between;
	gap: var( --gap );
	margin-bottom: var( --gap );
	padding-bottom: var( --gap );
	border-bottom: 1px solid var( --line );
}

.catalogue__count {
	font-size: var( --step--1 );
	letter-spacing: .06em;
	text-transform: uppercase;
	color: var( --muted );
}

.catalogue__tools {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: .6rem;
}

.catalogue__tools label {
	font-size: var( --step--1 );
	font-weight: 600;
	letter-spacing: .06em;
	text-transform: uppercase;
	color: var( --muted );
}

.catalogue__tools select,
.catalogue__tools input[type="search"] {
	width: auto;
	min-width: 190px;
	padding-block: .5em;
}

.filters-toggle {
	display: none;
}

/* --- The applied-filter row ---------------------------------------------- */

.catalogue__active {
	margin-bottom: var( --gap );
}

.catalogue__active .chips {
	margin: 0;
	padding: 0;
	list-style: none;
}

.chip--remove,
.chip--clear {
	text-decoration: none;
	transition: border-color var( --speed ) var( --ease ), background var( --speed ) var( --ease ), color var( --speed ) var( --ease );
}

.chip--remove small {
	text-transform: uppercase;
	letter-spacing: .06em;
}

/* The cross is decorative — the accessible name is the visually hidden span
   beside it, which says which filter is being removed rather than just "x". */
.chip--remove i {
	font-style: normal;
	font-size: 1.15em;
	line-height: 1;
	color: var( --muted );
}

.chip--remove:hover {
	background: var( --ink );
	border-color: var( --ink );
	color: var( --paper );
}

.chip--remove:hover small,
.chip--remove:hover i {
	color: var( --brand );
}

.chip--clear {
	background: transparent;
	border-style: dashed;
	color: var( --brand-ink );
}

.chip--clear:hover {
	border-style: solid;
	border-color: var( --brand-ink );
}

/* ==========================================================================
   12. Single product
   ========================================================================== */

.product {
	display: grid;
	gap: var( --gap-lg );
	grid-template-columns: minmax( 0, 1.1fr ) minmax( 0, 1fr );
	align-items: start;
}

/* The media column.

   Sticky above the tablet breakpoint: the right column is long — price, terms,
   certifications, a sixteen-row specification table — and scrolling the photo
   off the top means checking a dimension against the picture is a scroll up
   and a scroll back down. */
.product__media {
	position: sticky;
	top: 96px;
}

.gallery__main {
	position: relative;
	aspect-ratio: 4 / 3;
	background: var( --surface );
	border: 1px solid var( --line );
	border-radius: var( --radius-lg );
	overflow: hidden;
}

.gallery__main img,
.gallery__main svg {
	display: block;
	width: 100%;
	height: 100%;
	object-fit: cover;
}

.gallery__thumbs {
	display: grid;
	grid-template-columns: repeat( auto-fill, minmax( 84px, 1fr ) );
	gap: .55rem;
	margin-top: .55rem;
	padding: 0;
	list-style: none;
}

.gallery__thumbs button {
	display: block;
	width: 100%;
	padding: 0;
	aspect-ratio: 1 / 1;
	background: var( --surface );
	border: 1px solid var( --line );
	border-radius: var( --radius );
	overflow: hidden;
	cursor: pointer;
	transition: border-color var( --speed ) var( --ease );
}

.gallery__thumbs button:hover {
	border-color: var( --line-strong );
}

.gallery__thumbs button[aria-current="true"] {
	border-color: var( --ink );
	box-shadow: inset 0 0 0 1px var( --ink );
}

.gallery__thumbs img {
	display: block;
	width: 100%;
	height: 100%;
	object-fit: cover;
}

.product__head {
	margin-bottom: var( --gap );
}

.product__sku {
	display: inline-block;
	margin-bottom: .6rem;
	padding: .25em .6em;
	background: var( --surface );
	border-radius: 3px;
	font-family: var( --font-mono );
	font-size: var( --step--1 );
	color: var( --muted );
}

.product__title {
	font-size: var( --step-3 );
	margin-bottom: .3em;
}

.product__excerpt {
	font-size: var( --step-1 );
	line-height: 1.5;
	color: var( --muted );
	margin-bottom: var( --gap );
}

.product__terms {
	display: flex;
	flex-wrap: wrap;
	gap: .4rem;
	margin-bottom: var( --gap );
	padding: 0;
	list-style: none;
}

.product__terms a {
	display: inline-block;
	padding: .3em .75em;
	border: 1px solid var( --field-line );
	border-radius: 99px;
	font-size: var( --step--1 );
	font-weight: 600;
	color: var( --ink );
	text-decoration: none;
}

.product__terms a:hover {
	background: var( --ink );
	color: var( --paper );
	border-color: var( --ink );
}

/* Specification table */

.spec {
	margin: var( --gap-lg ) 0;
	border: 1px solid var( --line );
	border-radius: var( --radius-lg );
	overflow: hidden;
}

.spec caption {
	padding: .9rem var( --gap );
	background: var( --surface );
	border-bottom: 1px solid var( --line );
	/* The catalogue and the product page were a sea of white and grey with the
	   brand nowhere in them — the colour the client picks in the Customizer only
	   showed up on the home page, which is why it looked like it was not being
	   applied at all. A rule of it here, on the filter legends and on the price
	   box is enough to carry it through the inner pages. */
	border-left: 3px solid var( --brand );
	font-size: var( --step--1 );
	font-weight: 700;
	letter-spacing: .1em;
	text-transform: uppercase;
	text-align: left;
	color: var( --ink );
}

.spec th,
.spec td {
	padding: .72rem var( --gap );
	text-align: left;
	vertical-align: top;
	border-bottom: 1px solid var( --line );
	font-size: var( --step-0 );
}

.spec tr:last-child th,
.spec tr:last-child td {
	border-bottom: 0;
}

.spec th {
	width: 40%;
	font-weight: 600;
	color: var( --muted );
}

.spec td {
	color: var( --ink );
}

.spec tbody tr:nth-child( odd ) {
	background: #fafbfc;
}

/* Chips and swatches.

   Sizes, colours and capabilities are lists, and they were being printed as
   "S, M, L, XL, XXL" — a sentence made of a data structure. A buyer looking
   for one size in that line has to read the whole line. As chips each one is
   its own target, the stock note gets to travel with the size it belongs to,
   and the colour hex the client was asked to supply finally does something. */

.chips,
.swatches {
	display: flex;
	flex-wrap: wrap;
	gap: .4rem;
}

.chip {
	display: inline-flex;
	align-items: baseline;
	gap: .35em;
	padding: .3em .7em;
	background: var( --paper );
	border: 1px solid var( --line-strong );
	border-radius: 99px;
	font-size: var( --step--1 );
	font-weight: 600;
	line-height: 1.5;
	color: var( --ink );
	white-space: nowrap;
}

.chip small {
	font-size: var( --step--2 );
	font-weight: 500;
	color: var( --muted );
}

.swatch {
	display: inline-flex;
	align-items: center;
	gap: .45em;
	padding: .28em .7em .28em .32em;
	background: var( --paper );
	border: 1px solid var( --line-strong );
	border-radius: 99px;
	font-size: var( --step--1 );
	font-weight: 600;
	line-height: 1.5;
	color: var( --ink );
	white-space: nowrap;
}

/* The dot carries an inset ring rather than a border, so a white colourway
   still reads as a swatch on a white chip instead of disappearing. */
.swatch i {
	width: 1.05em;
	height: 1.05em;
	flex: 0 0 auto;
	border-radius: 50%;
	box-shadow: inset 0 0 0 1px rgba( 11, 14, 20, .22 );
}

.swatch__none {
	background: repeating-linear-gradient( 45deg, var( --surface ) 0 3px, var( --surface-2 ) 3px 6px );
}

/* The long description, when a product has one. It sits under the two
   columns, so it needs the gap the grid would otherwise have given it. */

.product__entry {
	margin-top: var( --gap-lg );
}

/* Size chart */

.chart {
	margin: var( --gap-lg ) 0;
	border: 1px solid var( --line );
	border-radius: var( --radius-lg );
	overflow-x: auto;
	display: block;
	max-width: 100%;
}

.chart table {
	min-width: 34rem;
}

.chart th,
.chart td {
	padding: .6rem .9rem;
	text-align: left;
	border-bottom: 1px solid var( --line );
	font-size: var( --step--1 );
	white-space: nowrap;
}

.chart thead th {
	background: var( --ink );
	color: var( --paper );
	font-weight: 600;
	letter-spacing: .06em;
	text-transform: uppercase;
	border-bottom: 0;
}

.chart tbody tr:last-child th,
.chart tbody tr:last-child td {
	border-bottom: 0;
}

/* Downloads */

.downloads {
	display: flex;
	flex-wrap: wrap;
	gap: .6rem;
	margin: var( --gap ) 0 0;
	padding: 0;
	list-style: none;
}

.download {
	display: inline-flex;
	align-items: center;
	gap: .55rem;
	padding: .6em 1em;
	border: 1px solid var( --field-line );
	border-radius: var( --radius );
	text-decoration: none;
	color: var( --ink );
	font-size: var( --step--1 );
	font-weight: 600;
}

.download:hover {
	border-color: var( --ink );
	color: var( --ink );
	background: var( --surface );
}

.download svg {
	width: 1.1em;
	height: 1.1em;
	fill: var( --accent );
}

/*
 * The file size sits inside the same anchor rather than after it, so a screen
 * reader announces "Download the catalogue, PDF, 4 MB" as one link name and a
 * buyer on a phone plan learns the cost before committing to the tap.
 */
.download__meta {
	color: var( --muted );
	font-weight: 400;
}

/* The footer is dark; the product-page border and hover would vanish on it. */
.download--footer {
	margin-top: var( --gap );
	border-color: rgba( 255, 255, 255, .28 );
	color: var( --paper );
}

.download--footer:hover {
	border-color: var( --paper );
	color: var( --paper );
	background: rgba( 255, 255, 255, .08 );
}

.download--footer svg {
	fill: var( --brand );
}

.download--footer .download__meta {
	color: rgba( 255, 255, 255, .62 );
}

/* ==========================================================================
   13. Price block

   A price in this trade is never a bare number. The block below keeps the
   amount, the unit, the Incoterm and the MOQ in one visual unit so a buyer
   cannot read the figure without also reading its terms.
   ========================================================================== */

.price-box {
	padding: var( --gap );
	background: var( --surface );
	border-radius: var( --radius-lg );
	border: 1px solid var( --line );
	/* The brand colour, on the one element on this page a buyer is here for. */
	border-top: 3px solid var( --brand );
}

.price {
	display: flex;
	flex-wrap: wrap;
	align-items: baseline;
	gap: .4rem .6rem;
	font-family: var( --font-display );
	font-size: var( --step-3 );
	font-weight: 700;
	letter-spacing: -.025em;
	line-height: 1.1;
	/* Lining tabular figures. A column of prices whose digits are different
	   widths does not scan, and scanning a price column is the single most
	   common thing a buyer does on this site. */
	font-variant-numeric: tabular-nums lining-nums;
}

/* The same price line inside a card, where a display-size number would shout
   over the product name it belongs to. */
.price--card {
	font-size: var( --step-1 );
	gap: .3rem .45rem;
	margin-top: .2rem;
}

.price--card .price__from,
.price--card .price__unit {
	font-size: var( --step--1 );
}

.price--card .price__incoterm {
	font-size: .68rem;
	padding: .2em .5em;
}

.price--card .price__quote {
	font-size: var( --step-1 );
}

.price__from {
	font-family: var( --font-body );
	font-size: var( --step-0 );
	font-weight: 600;
	letter-spacing: .1em;
	text-transform: uppercase;
	color: var( --muted );
}

.price__amount {
	color: var( --ink );
}

.price__unit {
	font-family: var( --font-body );
	font-size: var( --step-0 );
	font-weight: 600;
	color: var( --muted );
}

/* The Incoterm chip.

   This was a solid volt block, which is how it ended up looking like someone
   had run a highlighter over the page — twenty cards in a grid meant twenty
   fluorescent rectangles, and they pulled harder than the product names did.
   Volt earns its keep on the dark hero; on white it is noise.

   An Incoterm is a qualifier on the price, not a badge. It now reads as one:
   quiet outline, ink text, sitting next to the number rather than competing
   with it. */
.price__incoterm {
	font-family: var( --font-body );
	font-size: var( --step--2 );
	font-weight: 600;
	letter-spacing: var( --track-label );
	text-transform: uppercase;
	color: var( --muted );
	background: var( --surface );
	border: 1px solid var( --line-strong );
	padding: .3em .6em;
	border-radius: 999px;
	align-self: center;
	white-space: nowrap;
}

.price__term {
	border-bottom: 1px dotted currentColor;
	text-decoration: none;
	cursor: help;
}

.price__quote {
	font-family: var( --font-display );
	font-size: var( --step-2 );
	font-weight: 700;
	letter-spacing: -.02em;
	line-height: 1.1;
	color: var( --ink );
}

.price__note {
	margin: .6rem 0 0;
	font-size: var( --step--1 );
	color: var( --muted );
}

/* The MOQ pill. It was breaking into three lines — "MOQ" over "100" over
   "PIECES" — because the flex item was allowed to wrap and the icon was
   sized in ems off an already-large parent. It is one fact and it reads as
   one line. */
.price__moq {
	display: inline-flex;
	align-items: center;
	gap: .45em;
	margin-top: .7rem;
	padding: .35em .8em;
	background: var( --paper );
	border: 1px solid var( --line-strong );
	border-radius: 99px;
	font-size: var( --step--1 );
	font-weight: 600;
	letter-spacing: .04em;
	text-transform: uppercase;
	white-space: nowrap;
	max-width: 100%;
}

.price__moq svg {
	width: 1.05em;
	height: 1.05em;
	flex: 0 0 auto;
	fill: currentColor;
	color: var( --muted );
}

.price__actions {
	display: flex;
	flex-wrap: wrap;
	gap: .7rem;
	margin-top: var( --gap );
}

/* Quantity price breaks */

.tiers {
	margin: var( --gap ) 0 0;
	border: 1px solid var( --line-strong );
	border-radius: var( --radius );
	overflow: hidden;
	background: var( --paper );
}

.tiers__caption,
.tiers caption {
	padding: .6rem .9rem;
	background: var( --ink );
	color: var( --paper );
	font-size: var( --step--1 );
	font-weight: 700;
	letter-spacing: .08em;
	text-transform: uppercase;
	text-align: left;
}

.tiers th,
.tiers td {
	padding: .55rem .9rem;
	text-align: left;
	border-bottom: 1px solid var( --line );
	font-size: var( --step--1 );
}

.tiers thead th {
	font-weight: 700;
	letter-spacing: .06em;
	text-transform: uppercase;
	color: var( --muted );
	background: var( --surface );
}

.tiers tbody th {
	font-weight: 500;
	color: var( --ink );
}

.tiers tbody td {
	font-weight: 700;
	font-variant-numeric: tabular-nums;
}

.tiers tbody tr:last-child th,
.tiers tbody tr:last-child td {
	border-bottom: 0;
}

.tiers__note {
	margin: .5rem 0 0;
	font-size: var( --step--1 );
	color: var( --muted );
}

/* ==========================================================================
   14. Quote basket and forms
   ========================================================================== */

.quote-table {
	width: 100%;
	margin-bottom: var( --gap-lg );
	border: 1px solid var( --line );
	border-radius: var( --radius-lg );
	overflow: hidden;
}

.quote-table th,
.quote-table td {
	padding: var( --gap );
	text-align: left;
	vertical-align: top;
	border-bottom: 1px solid var( --line );
}

.quote-table thead th {
	background: var( --surface );
	font-size: var( --step--1 );
	font-weight: 700;
	letter-spacing: .08em;
	text-transform: uppercase;
	color: var( --muted );
}

.quote-table tbody tr:last-child th,
.quote-table tbody tr:last-child td {
	border-bottom: 0;
}

.quote-line {
	display: flex;
	gap: var( --gap );
	align-items: flex-start;
}

.quote-line__media {
	width: 84px;
	height: 84px;
	flex: 0 0 auto;
	border-radius: var( --radius );
	overflow: hidden;
	background: var( --surface );
}

.quote-line__media img,
.quote-line__media svg {
	width: 100%;
	height: 100%;
	object-fit: cover;
}

.quote-line__title {
	margin: 0 0 .2rem;
	font-family: var( --font-body );
	font-size: var( --step-0 );
	font-weight: 700;
	text-transform: none;
	letter-spacing: 0;
	line-height: 1.35;
}

.quote-line__title a {
	color: var( --ink );
	text-decoration: none;
}

.quote-line__sku {
	font-family: var( --font-mono );
	font-size: var( --step--1 );
	color: var( --muted );
}

.quote-line__opts {
	display: grid;
	gap: .5rem;
	grid-template-columns: repeat( auto-fit, minmax( 130px, 1fr ) );
	margin-top: .7rem;
}

.quote-line__opts input {
	padding: .5em .7em;
	font-size: var( --step--1 );
}

.quote-remove {
	border: 0;
	background: transparent;
	padding: .3em;
	color: var( --muted );
	font-size: var( --step--1 );
	font-weight: 600;
	letter-spacing: .06em;
	text-transform: uppercase;
	cursor: pointer;
	text-decoration: underline;
}

.quote-remove:hover {
	color: var( --bad );
}

.quote-empty {
	padding: var( --section ) var( --gap );
	text-align: center;
	background: var( --surface );
	border-radius: var( --radius-lg );
}

.quote-empty h2 {
	margin-bottom: .4em;
}

.quote-empty p {
	color: var( --muted );
	margin-bottom: var( --gap );
}

.form-grid {
	display: grid;
	gap: 0 var( --gap );
	grid-template-columns: repeat( auto-fit, minmax( 250px, 1fr ) );
}

.form-grid .field--wide {
	grid-column: 1 / -1;
}

.form-actions {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: var( --gap );
	margin-top: var( --gap );
	padding-top: var( --gap );
	border-top: 1px solid var( --line );
}

.contact-card {
	padding: var( --gap-lg );
	background: var( --surface );
	border-radius: var( --radius-lg );
}

.contact-card + .contact-card {
	margin-top: var( --gap );
}

.contact-card h2 {
	font-family: var( --font-body );
	font-size: var( --step-1 );
	font-weight: 700;
	text-transform: none;
	letter-spacing: 0;
}

.contact-list {
	margin: 0;
	padding: 0;
	list-style: none;
	display: flex;
	flex-direction: column;
	gap: .8rem;
}

.contact-list li {
	display: flex;
	align-items: flex-start;
	gap: .7rem;
}

.contact-list svg {
	width: 1.15em;
	height: 1.15em;
	margin-top: .2em;
	flex: 0 0 auto;
	fill: var( --accent );
}

.contact-list a {
	color: var( --ink );
	text-decoration: none;
}

.contact-list a:hover {
	color: var( --accent );
	text-decoration: underline;
}

/* Floating WhatsApp button */

.whatsapp {
	position: fixed;
	right: 1.1rem;
	bottom: 1.1rem;
	z-index: 90;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 54px;
	height: 54px;
	border-radius: 50%;
	background: #25d366;
	box-shadow: var( --shadow-lg );
	transition: transform var( --speed ) var( --ease );
}

.whatsapp:hover {
	transform: scale( 1.06 );
}

.whatsapp svg {
	width: 28px;
	height: 28px;
	fill: #ffffff;
}

/* ==========================================================================
   15. Editorial and pages
   ========================================================================== */

.entry {
	max-width: 72ch;
}

.entry > * + * {
	margin-top: 1.1em;
}

.entry h2 {
	margin-top: 1.8em;
}

.entry h3 {
	margin-top: 1.5em;
}

.entry ul,
.entry ol {
	padding-left: 1.3em;
}

.entry li + li {
	margin-top: .35em;
}

.entry img {
	border-radius: var( --radius-lg );
}

.entry table {
	border: 1px solid var( --line );
	border-radius: var( --radius );
}

.entry th,
.entry td {
	padding: .6rem .9rem;
	border-bottom: 1px solid var( --line );
	text-align: left;
}

.post-card {
	display: flex;
	flex-direction: column;
	background: var( --paper );
	border: 1px solid var( --line );
	border-radius: var( --radius-lg );
	overflow: hidden;
	transition: border-color var( --speed ) var( --ease ), box-shadow var( --speed ) var( --ease ), transform var( --speed ) var( --ease );
}

/* The card lifts as a whole even though only the heading inside it is a link,
   because the eye reads the tile as the target long before the pointer finds
   the words. */
.post-card:hover,
.post-card:focus-within {
	border-color: var( --line-strong );
	box-shadow: var( --shadow-lg );
	transform: translateY( -3px );
}

.post-card__media {
	aspect-ratio: 16 / 10;
	background: var( --surface );
	overflow: hidden;
}

.post-card__media img,
.post-card__media svg {
	width: 100%;
	height: 100%;
	object-fit: cover;
	transition: transform .5s var( --ease );
}

/* Slow, and only a few per cent: enough that the tile feels live under the
   pointer, not so much that the crop visibly changes. */
.post-card:hover .post-card__media img,
.post-card:hover .post-card__media svg {
	transform: scale( 1.04 );
}

.post-card__body {
	padding: var( --gap );
	display: flex;
	flex-direction: column;
	gap: .5rem;
	flex: 1 1 auto;
}

.post-card__date {
	font-size: var( --step--1 );
	letter-spacing: .08em;
	text-transform: uppercase;
	color: var( --muted );
}

.post-card h3 {
	font-family: var( --font-body );
	font-size: var( --step-1 );
	font-weight: 700;
	text-transform: none;
	letter-spacing: 0;
	line-height: 1.3;
	margin: 0;
}

.post-card h3 a {
	color: var( --ink );
	text-decoration: none;
}

.post-card h3 a:hover {
	color: var( --accent );
}

.post-card__meta {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: .4rem .7rem;
	margin: 0;
}

/* The section a post belongs to, as a label rather than as another line of
   grey text — it is the one piece of metadata a reader scans a feed for. */
.post-card__cat {
	padding: .2em .65em;
	background: var( --accent-soft );
	border-radius: 999px;
	color: var( --accent-dark );
	font-size: var( --step--2 );
	font-weight: 700;
	letter-spacing: var( --track-label );
	text-transform: uppercase;
	text-decoration: none;
	transition: background var( --speed ) var( --ease ), color var( --speed ) var( --ease );
}

.post-card__cat:hover,
.post-card__cat:focus-visible {
	background: var( --accent );
	color: var( --accent-fg );
}

.post-card__excerpt {
	margin: 0;
	color: var( --muted );
	font-size: var( --step--1 );
	line-height: 1.6;
}

/* Pushed to the foot of the card, so that across a row of cards whose
   summaries run to different lengths the prompts still line up. */
.post-card__more {
	margin-top: auto;
	padding-top: .35rem;
	color: var( --accent-dark );
	font-size: var( --step--1 );
	font-weight: 700;
}

.post-card__more::after {
	content: "\2009\2192";
	display: inline-block;
	transition: transform var( --speed ) var( --ease );
}

.post-card:hover .post-card__more::after {
	transform: translateX( 3px );
}

/*
 * The lead card spans whatever the row happens to be, and above 45rem it turns
 * on its side. Below that it is left alone: two columns inside a phone-width
 * card gives a picture too small to read and a paragraph too narrow to.
 */
.post-card--lead {
	grid-column: 1 / -1;
}

@media ( min-width: 45rem ) {

	.post-card--lead {
		flex-direction: row;
	}

	/* aspect-ratio is dropped rather than changed: as a row the picture takes
	   its height from the card beside it, and a ratio would fight that. */
	.post-card--lead .post-card__media {
		flex: 0 0 44%;
		aspect-ratio: auto;
		min-height: 17rem;
	}

	.post-card--lead .post-card__body {
		padding: var( --gap-lg );
		gap: .7rem;
	}

	.post-card--lead h3 {
		font-size: var( --step-2 );
	}

	.post-card--lead .post-card__excerpt {
		font-size: var( --step-0 );
	}
}

/* Pagination */

.pagination {
	display: flex;
	flex-wrap: wrap;
	gap: .4rem;
	justify-content: center;
	margin-top: var( --gap-lg );
}

.pagination .page-numbers {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	min-width: 42px;
	height: 42px;
	padding: 0 .7em;
	border: 1px solid var( --field-line );
	border-radius: var( --radius );
	color: var( --ink );
	text-decoration: none;
	font-weight: 600;
}

.pagination .page-numbers:hover {
	border-color: var( --ink );
	background: var( --surface );
}

.pagination .page-numbers.current {
	background: var( --ink );
	border-color: var( --ink );
	color: var( --paper );
}

.pagination .dots {
	border-color: transparent;
}

/* Blog layout. The sidebar column only exists when the client has actually
   put a widget in it — functions.php adds .no-sidebar to <body> otherwise,
   so an empty column can never leave the text stranded at half width. */

.layout {
	display: grid;
	gap: var( --gap-lg );
	grid-template-columns: minmax( 0, 1fr ) 19rem;
	align-items: start;
}

.no-sidebar .layout {
	grid-template-columns: minmax( 0, 1fr );
	max-width: 78ch;
}

/*
 * 78ch is a reading measure, and it belongs to a single post. Applied to the
 * feed it was capping a grid of cards at about seven hundred pixels and
 * stranding it against the left margin with half the page empty beside it —
 * which is what made the blog look unfinished on a desktop with no widget in
 * the sidebar. A feed is a grid, not prose, so it takes the full width.
 */
.no-sidebar .layout--feed {
	max-width: none;
}

.widget__title {
	font-family: var( --font-display );
	font-size: var( --step-1 );
	margin: 0 0 .7rem;
}

.post-meta {
	display: flex;
	flex-wrap: wrap;
	gap: .4rem .9rem;
	margin: 0 0 var( --gap );
	font-size: var( --step--1 );
	color: var( --muted );
}

.post-meta a {
	color: inherit;
}

.entry-media {
	margin: 0 0 var( --gap );
	border-radius: var( --radius-lg );
	overflow: hidden;
	background: var( --surface );
}

.entry-media img,
.entry-media svg {
	display: block;
	width: 100%;
	height: auto;
}

.post-nav {
	display: flex;
	flex-wrap: wrap;
	justify-content: space-between;
	gap: var( --gap );
	margin-top: var( --gap-lg );
	padding-top: var( --gap );
	border-top: 1px solid var( --line );
	font-weight: 600;
}

.post-nav span {
	display: block;
	font-size: var( --step--1 );
	font-weight: 400;
	color: var( --muted );
}

/* Comments */

.comments {
	margin-top: var( --section );
	padding-top: var( --gap-lg );
	border-top: 1px solid var( --line );
	max-width: 72ch;
}

.comment-list {
	margin: 0 0 var( --gap-lg );
	padding: 0;
	list-style: none;
}

.comment-list ol {
	list-style: none;
	padding-left: var( --gap );
}

.comment-body {
	padding: var( --gap ) 0;
	border-bottom: 1px solid var( --line );
}

.comment-meta {
	font-size: var( --step--1 );
	color: var( --muted );
	margin-bottom: .5rem;
}

.comment-respond .comment-reply-title {
	font-size: var( --step-2 );
}

/* Search form */

.search-form {
	display: flex;
	gap: .5rem;
}

.search-form input[type="search"] {
	flex: 1 1 auto;
}

/* ==========================================================================
   16. Footer
   ========================================================================== */

.site-footer {
	background: var( --ink );
	color: rgba( 255, 255, 255, .72 );
	padding-top: var( --section );
}

.site-footer h2,
.site-footer h3 {
	color: var( --paper );
	font-family: var( --font-body );
	font-size: var( --step-0 );
	font-weight: 700;
	letter-spacing: .12em;
	text-transform: uppercase;
	margin-bottom: 1rem;
}

.site-footer a {
	color: rgba( 255, 255, 255, .72 );
	text-decoration: none;
}

.site-footer a:hover {
	color: var( --brand );
}

.footer__grid {
	display: grid;
	gap: var( --gap-lg );
	grid-template-columns: minmax( 0, 1.4fr ) repeat( auto-fit, minmax( 160px, 1fr ) );
	padding-bottom: var( --section );
}

.footer__about p {
	max-width: 42ch;
	color: rgba( 255, 255, 255, .68 );
}

.footer__grid ul {
	margin: 0;
	padding: 0;
	list-style: none;
	display: flex;
	flex-direction: column;
	gap: .55rem;
}

.socials {
	display: flex;
	gap: .5rem;
	margin-top: var( --gap );
}

.socials a {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 40px;
	height: 40px;
	border: 1px solid rgba( 255, 255, 255, .2 );
	border-radius: var( --radius );
}

.socials a:hover {
	background: var( --brand );
	border-color: var( --brand );
	color: var( --brand-fg );
}

.socials svg {
	width: 18px;
	height: 18px;
	fill: rgba( 255, 255, 255, .8 );
}

.socials a:hover svg {
	fill: var( --ink );
}

.footer__bar {
	padding-block: 1.3rem;
	border-top: 1px solid rgba( 255, 255, 255, .12 );
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	justify-content: space-between;
	gap: .5rem var( --gap );
	font-size: var( --step--1 );
}

.footer__bar p {
	margin: 0;
}

/* ==========================================================================
   17. WordPress core classes
   ========================================================================== */

.screen-reader-text {
	border: 0;
	clip: rect( 1px, 1px, 1px, 1px );
	clip-path: inset( 50% );
	height: 1px;
	margin: -1px;
	overflow: hidden;
	padding: 0;
	position: absolute;
	width: 1px;
	word-wrap: normal !important;
}

.screen-reader-text:focus {
	background: var( --paper );
	clip: auto !important;
	clip-path: none;
	color: var( --ink );
	display: block;
	font-size: 1rem;
	height: auto;
	left: 1rem;
	line-height: normal;
	padding: 1rem 1.5rem;
	text-decoration: none;
	top: 1rem;
	width: auto;
	z-index: 100000;
}

.alignleft {
	float: left;
	margin: .3em 1.5em 1em 0;
}

.alignright {
	float: right;
	margin: .3em 0 1em 1.5em;
}

.aligncenter {
	margin-inline: auto;
	display: block;
}

.alignwide {
	width: min( 100vw - 2.5rem, 1100px );
	max-width: none;
	margin-inline: auto;
}

.alignfull {
	width: 100vw;
	max-width: none;
	margin-left: calc( 50% - 50vw );
	margin-right: calc( 50% - 50vw );
}

.wp-caption {
	max-width: 100%;
}

.wp-caption-text,
.wp-block-image figcaption {
	font-size: var( --step--1 );
	color: var( --muted );
	margin-top: .5rem;
}

.sticky .card__title::before {
	content: "★ ";
	color: var( --brand-ink );
}

.bypostauthor > .comment-body .comment-meta strong {
	color: var( --accent );
}

/* WordPress's [gallery] shortcode. Scoped to post content on purpose: this
   rule is generic enough to catch anything else called "gallery", and it did —
   it was landing on the product page's media column and turning a full-width
   photograph into one cell of a 160px grid. */
.entry .gallery {
	display: grid;
	grid-template-columns: repeat( auto-fill, minmax( 160px, 1fr ) );
	gap: .6rem;
}

.gallery-caption {
	font-size: var( --step--1 );
	color: var( --muted );
}

.widget {
	margin-bottom: var( --gap-lg );
}

.widget ul {
	margin: 0;
	padding: 0;
	list-style: none;
	display: flex;
	flex-direction: column;
	gap: .45rem;
}

/* ==========================================================================
   18. Utilities
   ========================================================================== */

.center {
	text-align: center;
}

.center .lede {
	margin-inline: auto;
}

.stack > * + * {
	margin-top: var( --gap );
}

.muted {
	color: var( --muted );
}

.nowrap {
	white-space: nowrap;
}

.hide {
	display: none !important;
}

.mt-0 {
	margin-top: 0;
}

.mb-0 {
	margin-bottom: 0;
}

/* ==========================================================================
   19. Responsive
   ========================================================================== */

@media ( max-width: 1080px ) {

	/* Tighter, but still a horizontal bar. The drawer does not arrive until
	   880px and the menu has to survive the width in between. */
	.nav a {
		padding-inline: .6rem;
	}

	.nav > ul > li > a::after {
		right: .6rem;
		left: .6rem;
	}

	.nav .menu-item-has-children > a {
		padding-right: 1.5rem;
	}

	.nav .menu-item-has-children > a::before {
		right: .6rem;
	}

	.catalogue {
		grid-template-columns: minmax( 0, 1fr );
	}

	/* Once the sidebar is a drawer rather than a column it has the whole page
	   to grow into, so the internal scroller only gets in the way. */
	.filters {
		position: static;
		max-height: none;
		overflow: visible;
	}

	.filters__apply {
		position: static;
	}

	.filters-toggle {
		display: inline-flex;
		width: 100%;
		justify-content: space-between;
	}

	.filters[hidden] {
		display: none;
	}

	.product {
		grid-template-columns: minmax( 0, 1fr );
	}

	/* Sticky is only worth anything beside a tall second column. Once the two
	   columns stack there is nothing to stay level with. */
	.product__media {
		position: static;
	}

	.layout {
		grid-template-columns: minmax( 0, 1fr );
	}
}

@media ( max-width: 880px ) {

	/* Above the drawer, so the button that opened it is also the button that
	   closes it. Left underneath, the toggle is unreachable and the only way
	   out of the menu is the Escape key. */
	.site-header {
		z-index: 130;
	}

	/*
	 * The drawer is dark on purpose. A white panel sliding out from under a
	 * white header has no visible edge at the top — the first two menu items
	 * simply disappear behind the bar and the whole thing reads as a broken
	 * layout rather than as a menu. Ink gives it a definite top edge, and it
	 * suits a catalogue whose hero and footer are already dark.
	 *
	 * --header-h is measured by the script, because the header is one height
	 * with the contact topbar switched on and another without it, and the
	 * first menu item has to clear whichever is in play. The fallback is the
	 * height with no topbar, so the drawer is still usable if the script never
	 * runs.
	 */
	.nav {
		position: fixed;
		inset: 0 0 0 auto;
		width: min( 86vw, 340px );
		padding: calc( var( --header-h, 84px ) + 1.1rem ) 1.25rem 2rem;
		background: var( --ink );
		border-left: 3px solid var( --brand );
		box-shadow: var( --shadow-lg );
		overflow-y: auto;
		overscroll-behavior: contain;
		transform: translateX( 100% );
		transition: transform var( --speed ) var( --ease );
		z-index: 120;
	}

	.nav.is-open {
		transform: none;
	}

	.nav ul {
		flex-direction: column;
		align-items: stretch;
		gap: .1rem;
	}

	.nav li {
		border-bottom: 1px solid rgba( 255, 255, 255, .1 );
	}

	.nav > ul > li:last-child {
		border-bottom: 0;
	}

	.nav a {
		padding: .95rem .75rem;
		border-radius: var( --radius );
		color: rgba( 255, 255, 255, .86 );
		font-size: var( --step-0 );
	}

	.nav > ul > li > a:hover {
		background: rgba( 255, 255, 255, .07 );
		color: var( --paper );
	}

	/* The scaled underline belongs to the desktop bar. Stacked, the current
	   item reads better as a filled row with a volt edge. */
	.nav > ul > li > a::after {
		display: none;
	}

	.nav > ul > .current-menu-item > a,
	.nav > ul > .current_page_item > a,
	.nav > ul > .current-menu-ancestor > a {
		background: rgba( 255, 255, 255, .07 );
		box-shadow: inset 3px 0 0 var( --brand );
		color: var( --paper );
	}

	.nav .menu-item-has-children > a::before {
		right: 1rem;
	}

	.nav .sub-menu {
		position: static;
		min-width: 0;
		background: transparent;
		opacity: 1;
		visibility: visible;
		transform: none;
		border: 0;
		border-radius: 0;
		box-shadow: none;
		padding: 0 0 .5rem .9rem;
	}

	.nav .sub-menu::before {
		display: none;
	}

	.nav .sub-menu li {
		border-bottom: 0;
	}

	.nav .sub-menu a {
		padding: .55rem .75rem;
		color: rgba( 255, 255, 255, .6 );
		font-size: var( --step--1 );
	}

	.nav .sub-menu a:hover {
		background: rgba( 255, 255, 255, .07 );
		color: var( --brand );
	}

	.nav-toggle {
		display: inline-flex;
		order: 3;
	}

	.basket span:not( .basket__count ) {
		display: none;
	}

	.hero__inner {
		min-height: 0;
	}

	.cta {
		flex-direction: column;
		align-items: flex-start;
	}

	.quote-table thead {
		display: none;
	}

	.quote-table,
	.quote-table tbody,
	.quote-table tr,
	.quote-table td {
		display: block;
		width: 100%;
	}

	.quote-table td {
		border-bottom: 0;
		padding-bottom: 0;
	}

	.quote-table tr {
		border-bottom: 1px solid var( --line );
		padding-bottom: var( --gap );
	}
}

@media ( max-width: 760px ) {

	/* Search and sort were sitting on one line with the result count and
	   each control keeping its 190px minimum, which overflowed the wrap. */
	.catalogue__bar {
		flex-direction: column;
		align-items: stretch;
	}

	.catalogue__tools {
		display: grid;
		grid-template-columns: minmax( 0, 1fr );
		gap: .5rem;
	}

	.catalogue__tools select,
	.catalogue__tools input[type="search"] {
		width: 100%;
		min-width: 0;
	}

	/* Three buttons of different widths on two ragged lines. Stacked and full
	   width they read as a list of ways to ask, which is what they are. */
	.price__actions {
		flex-direction: column;
		align-items: stretch;
	}

	.price__actions .btn {
		width: 100%;
		justify-content: center;
	}

	.gallery__thumbs {
		grid-template-columns: repeat( auto-fill, minmax( 66px, 1fr ) );
	}

	.spec th,
	.spec td {
		padding: .65rem .9rem;
	}

	.spec caption {
		padding: .8rem .9rem;
	}
}

@media ( max-width: 560px ) {

	.section__head {
		flex-direction: column;
		align-items: flex-start;
	}

	.hero__facts {
		gap: 1.1rem 1.8rem;
	}

	/* The specification table stops being a table.

	   Two columns in 300-odd pixels gives the label 46% and the value the
	   rest, and "Carton size" then wraps to three lines opposite a value that
	   wraps to two. Stacked, the label is a caption over its value, which is
	   how a spec sheet reads on paper anyway — and it gives the size chips and
	   colour swatches the full width they need to sit on one line. */
	.spec th,
	.spec td {
		display: block;
		width: auto;
		border-bottom: 0;
	}

	.spec th {
		padding-bottom: .1rem;
		font-size: var( --step--2 );
		letter-spacing: var( --track-label );
		text-transform: uppercase;
	}

	.spec td {
		padding-top: 0;
		padding-bottom: .8rem;
	}

	.spec tbody tr {
		display: block;
		border-bottom: 1px solid var( --line );
	}

	.spec tbody tr:last-child {
		border-bottom: 0;
	}

	.price-box {
		padding: 1rem;
	}

	.gallery__thumbs {
		grid-template-columns: repeat( 4, minmax( 0, 1fr ) );
	}

	.certs {
		gap: .45rem;
	}

	.footer__grid {
		grid-template-columns: minmax( 0, 1fr );
	}
}

@media ( max-width: 420px ) {

	/* The wrap keeps 2.5rem of gutter at every width, which is a fifth of a
	   360px screen spent on nothing. */
	.wrap {
		width: min( 100% - 1.75rem, var( --wrap ) );
	}

	.wrap--narrow {
		width: min( 100% - 1.75rem, var( --wrap-narrow ) );
	}

	/* Below this the 280px minimum on the card grid is wider than the column
	   it is in, and the grid overflows rather than dropping to one card. */
	.grid--2,
	.grid--3,
	.grid--4 {
		grid-template-columns: minmax( 0, 1fr );
	}

	.gallery__main {
		aspect-ratio: 1 / 1;
	}

	.gallery__thumbs {
		grid-template-columns: repeat( 3, minmax( 0, 1fr ) );
	}

	.card__foot {
		flex-wrap: wrap;
		gap: .5rem;
	}

	.topbar__list {
		gap: .25rem .9rem;
	}
}

/* ==========================================================================
   20. Motion and print
   ========================================================================== */

@media ( prefers-reduced-motion: reduce ) {

	html {
		scroll-behavior: auto;
	}

	*,
	*::before,
	*::after {
		animation-duration: .01ms !important;
		animation-iteration-count: 1 !important;
		transition-duration: .01ms !important;
		scroll-behavior: auto !important;
	}
}

/* ---------------------------------------------------------------------------
   The product mega panel

   Positioned against the header rather than against the menu item, because a
   panel five columns wide hanging off the left edge of a link two thirds of the
   way across the bar would run off the screen. It is a full-width sheet under
   the header, which is also what makes it usable on a laptop trackpad: the
   pointer travels straight down into it instead of along a diagonal.
   ------------------------------------------------------------------------ */

.mega-toggle {
	display: none;
}

/*
 * The panel is printed inside the PRODUCTS list item, and .nav li is relative,
 * so the full-bleed rule below was resolving against a link a hundred pixels
 * wide: five columns each with a 150px floor, plus a 260px aside, all trying to
 * fit — which is why the categories stacked into one squeezed strip, the
 * thumbnails ran over their own labels and the button escaped into a box of its
 * own. Taking this one item out of the positioning chain hands the panel back
 * to .site-header, which is sticky and therefore positioned, and is the
 * containing block the rule was written for. Only .has-mega is reset, so the
 * ordinary dropdowns keep hanging off their own parents.
 */
.nav li.has-mega {
	position: static;
}

.mega {
	position: absolute;
	top: 100%;
	right: 0;
	left: 0;
	z-index: 2;
	padding: var( --gap-lg ) 0;
	background: var( --paper );
	border-top: 1px solid var( --line );
	box-shadow: var( --shadow-lg );
	animation: cb-mega var( --speed ) var( --ease );
}

@keyframes cb-mega {
	from {
		opacity: 0;
		transform: translateY( -6px );
	}
}

.mega__inner {
	display: grid;
	grid-template-columns: 1fr 260px;
	gap: var( --gap-lg );
	align-items: start;
}

.nav .mega__cols {
	display: grid;
	grid-template-columns: repeat( auto-fit, minmax( 150px, 1fr ) );
	gap: var( --gap );
	align-items: start;
}

.nav .mega__col {
	position: static;
}

.nav .mega__head {
	display: flex;
	align-items: center;
	gap: .6rem;
	padding: 0;
	margin-bottom: .5rem;
	font-size: var( --step-0 );
	font-weight: 700;
	line-height: 1.25;
	letter-spacing: 0;
	text-transform: none;
}

.nav .mega__head:hover {
	background: transparent;
	color: var( --brand-ink );
}

/*
 * The mark is sized on the wrapper rather than on the picture, because it is
 * not always a picture: a category with no photograph falls through to an
 * inline <svg>, which an img selector never reaches — so the drawing used to
 * arrive at its own intrinsic size and sit across the heading beside it.
 */
.mega__thumb {
	display: block;
	flex: none;
	width: 44px;
	height: 44px;
	overflow: hidden;
	border-radius: var( --radius );
	background: var( --surface );
}

.mega__thumb img,
.mega__thumb svg {
	display: block;
	width: 100%;
	height: 100%;
	object-fit: cover;
}

/* Long category names wrap inside the column instead of forcing it wider. */
.mega__name {
	min-width: 0;
}

.mega__desc {
	margin: 0 0 .5rem;
	color: var( --muted );
	font-size: var( --step--1 );
	line-height: 1.5;
}

.nav .mega__links {
	display: block;
	margin: 0 0 .4rem;
	padding: 0;
	list-style: none;
}

.nav .mega__links a {
	padding: .2rem 0;
	color: var( --muted );
	font-size: var( --step--1 );
	font-weight: 500;
	letter-spacing: 0;
	text-transform: none;
}

.nav .mega__links a:hover {
	color: var( --ink );
	background: transparent;
	text-decoration: underline;
}

.mega__count {
	margin: 0;
	color: var( --muted );
	font-size: var( --step--2 );
	letter-spacing: var( --track-label );
	text-transform: uppercase;
}

.mega__aside {
	padding: var( --gap );
	background: var( --surface );
	border-radius: var( --radius-lg );
}

/* Full width of its own column, so the action reads as the end of the panel
   rather than as a stray button that has come loose from it. */
.mega__aside .btn {
	display: flex;
	width: 100%;
	justify-content: center;
}

.nav .mega__featured {
	display: block;
	margin: .6rem 0 var( --gap );
	padding: 0;
	list-style: none;
}

.nav .mega__featured a {
	display: flex;
	align-items: center;
	gap: .6rem;
	padding: .4rem 0;
	letter-spacing: 0;
	text-transform: none;
}

.mega__featured strong {
	display: block;
	font-size: var( --step--1 );
}

.mega__featured em {
	font-family: var( --font-mono );
	font-size: var( --step--2 );
	font-style: normal;
	color: var( --muted );
}

.mega__shot img {
	display: block;
	width: 52px;
	height: 52px;
	border-radius: var( --radius );
	object-fit: cover;
	background: var( --paper );
}

/* ---------------------------------------------------------------------------
   Header search, and the suggestions under it
   ------------------------------------------------------------------------ */

.header__search-toggle {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 38px;
	height: 38px;
	padding: 0;
	background: transparent;
	border: 1px solid var( --line-strong );
	border-radius: var( --radius );
	color: var( --ink );
	cursor: pointer;
	transition: background var( --speed ) var( --ease ), border-color var( --speed ) var( --ease );
}

.header__search-toggle:hover,
.header__search-toggle.is-on {
	background: var( --surface );
	border-color: var( --ink );
}

.header__search {
	padding: var( --gap ) 0;
	background: var( --surface );
	border-top: 1px solid var( --line );
}

.header__search .search-form {
	position: relative;
	max-width: 560px;
	margin-inline: auto;
}

.header__search input[type="search"] {
	flex: 1 1 auto;
}

.suggest {
	position: absolute;
	top: calc( 100% + .4rem );
	right: 0;
	left: 0;
	z-index: 60;
	max-height: 64vh;
	overflow-y: auto;
	padding: .4rem;
	background: var( --paper );
	border: 1px solid var( --line );
	border-radius: calc( var( --radius ) + 4px );
	box-shadow: var( --shadow-lg );
}

.suggest__head {
	margin: .4rem .6rem .2rem;
	color: var( --muted );
	font-size: var( --step--2 );
	font-weight: 700;
	letter-spacing: var( --track-label );
	text-transform: uppercase;
}

.suggest__row {
	display: flex;
	align-items: center;
	gap: .7rem;
	padding: .5rem .6rem;
	border-radius: var( --radius );
	color: var( --ink );
	text-decoration: none;
}

.suggest__row:hover,
.suggest__row:focus {
	background: var( --surface );
}

.suggest__row img {
	width: 40px;
	height: 40px;
	border-radius: var( --radius );
	object-fit: cover;
	background: var( --surface );
}

.suggest__row strong {
	display: block;
	font-size: var( --step--1 );
}

.suggest__row em {
	font-style: normal;
	font-size: var( --step--2 );
	color: var( --muted );
}

.suggest__all,
.suggest__none {
	display: block;
	padding: .6rem;
	color: var( --muted );
	font-size: var( --step--1 );
	text-align: center;
}

.suggest__all {
	font-weight: 600;
	color: var( --ink );
	border-top: 1px solid var( --line );
}

/* ---------------------------------------------------------------------------
   Header conversion controls
   ------------------------------------------------------------------------ */

.header-cta {
	display: inline-flex;
	align-items: center;
	gap: .5rem;
	padding: .62rem 1.1rem;
	background: var( --brand );
	border-radius: var( --radius );
	color: var( --brand-fg );
	font-size: var( --step--1 );
	font-weight: 700;
	letter-spacing: var( --track-label );
	text-transform: uppercase;
	text-decoration: none;
	white-space: nowrap;
	transition: background var( --speed ) var( --ease ), transform var( --speed ) var( --ease );
}

.header-cta:hover {
	background: var( --brand-dark );
	transform: translateY( -1px );
}

/* Sized against the label rather than in pixels, so the mark and the wording
   stay in proportion when the button text is changed in the Customizer. */
.header-cta svg {
	width: 1.15em;
	height: 1.15em;
	flex: none;
}

.header-cta__count {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	min-width: 20px;
	height: 20px;
	padding: 0 .3rem;
	background: var( --ink );
	border-radius: 999px;
	color: var( --paper );
	font-size: var( --step--2 );
}

.saved-link {
	display: inline-flex;
	align-items: center;
	gap: .35rem;
	padding: .5rem .7rem;
	border-radius: var( --radius );
	color: var( --ink );
	font-size: var( --step--1 );
	font-weight: 600;
	text-decoration: none;
}

.saved-link:hover {
	background: var( --surface );
}

.saved-link__count {
	font-family: var( --font-mono );
	color: var( --muted );
}

/* Anything that only works with a script is hidden until there is one. */
.js-only {
	display: none;
}

.js .js-only {
	display: inline-flex;
}

/* ---------------------------------------------------------------------------
   Shortlist and comparison pins
   ------------------------------------------------------------------------ */

.card__media {
	position: relative;
}

.card__pins {
	position: absolute;
	top: .5rem;
	right: .5rem;
	display: flex;
	flex-direction: column;
	gap: .35rem;
	opacity: 0;
	transform: translateY( -4px );
	transition: opacity var( --speed ) var( --ease ), transform var( --speed ) var( --ease );
}

.card:hover .card__pins,
.card:focus-within .card__pins {
	opacity: 1;
	transform: none;
}

.pin {
	display: inline-flex;
	align-items: center;
	gap: .35rem;
	padding: .4rem .6rem;
	background: rgba( 255, 255, 255, .94 );
	border: 1px solid var( --line-strong );
	border-radius: var( --radius );
	color: var( --ink );
	font-size: var( --step--2 );
	font-weight: 700;
	letter-spacing: var( --track-label );
	text-transform: uppercase;
	cursor: pointer;
	transition: background var( --speed ) var( --ease ), color var( --speed ) var( --ease );
}

.pin:hover {
	background: var( --ink );
	color: var( --paper );
	border-color: var( --ink );
}

.pin.is-on {
	background: var( --brand );
	border-color: var( --brand );
	color: var( --brand-fg );
}

.pin svg {
	width: 14px;
	height: 14px;
}

/* On a touch screen there is no hover to reveal them, so they simply stay. */
@media ( hover: none ) {

	.card__pins {
		opacity: 1;
		transform: none;
	}
}

.compare-tray {
	position: fixed;
	right: 0;
	bottom: 0;
	left: 0;
	z-index: 90;
	padding: .7rem 0;
	background: var( --ink );
	color: var( --paper );
	box-shadow: 0 -8px 30px rgba( 11, 14, 20, .28 );
}

.compare-tray__inner {
	display: flex;
	align-items: center;
	gap: var( --gap );
	flex-wrap: wrap;
}

.compare-tray__label {
	margin: 0;
	font-size: var( --step--2 );
	font-weight: 700;
	letter-spacing: var( --track-label );
	text-transform: uppercase;
	color: rgba( 255, 255, 255, .72 );
}

.compare-tray__items {
	display: flex;
	flex-wrap: wrap;
	gap: .4rem;
	margin: 0;
	padding: 0;
	list-style: none;
	flex: 1 1 auto;
}

.compare-tray__items li {
	display: inline-flex;
	align-items: center;
	gap: .4rem;
	padding: .3rem .55rem;
	background: rgba( 255, 255, 255, .1 );
	border-radius: var( --radius );
	font-size: var( --step--1 );
}

.compare-tray__drop {
	width: 16px;
	height: 16px;
	padding: 0;
	background: transparent;
	border: 0;
	color: inherit;
	cursor: pointer;
	opacity: .7;
}

.compare-tray__drop::before {
	content: "×";
	font-size: 1rem;
	line-height: 1;
}

.compare-tray__drop:hover {
	opacity: 1;
}

.compare-tray__actions {
	display: flex;
	gap: .5rem;
	flex: 0 0 auto;
}

/* ---------------------------------------------------------------------------
   Recently viewed
   ------------------------------------------------------------------------ */

.recent__title {
	font-size: var( --step-2 );
	margin-bottom: var( --gap );
}

.recent__list {
	display: grid;
	grid-template-columns: repeat( auto-fill, minmax( 190px, 1fr ) );
	gap: var( --gap );
	margin: 0;
	padding: 0;
	list-style: none;
}

.recent__list a {
	display: block;
	color: var( --ink );
	text-decoration: none;
}

.recent__list img {
	display: block;
	width: 100%;
	aspect-ratio: 1;
	object-fit: cover;
	border-radius: var( --radius );
	background: var( --surface );
	margin-bottom: .5rem;
}

.recent__list strong {
	display: block;
	font-size: var( --step--1 );
}

.recent__list em {
	font-family: var( --font-mono );
	font-size: var( --step--2 );
	font-style: normal;
	color: var( --muted );
}

/* ---------------------------------------------------------------------------
   Bottom-of-viewport furniture

   The quote bar is the mobile equivalent of the header button, so it is only
   ever shown where the header button is not — below the navigation breakpoint.

   It used to be two edge-to-edge slabs separated by a one-pixel gap, which is
   the shape of an operating-system tab bar rather than of anything this site
   owns, and it overflowed: a flex item's min-width is auto, so neither half
   could shrink past its own label and "Request a quote" ran off the right of a
   360px screen with its last two letters outside the viewport.

   What is here now is the sample the client sent, in miniature — white ground,
   a light grey secondary, one filled primary — floated off the bottom edge with
   a little padding so it reads as a control on the page rather than as part of
   the browser.
   ------------------------------------------------------------------------ */

.quotebar {
	position: fixed;
	right: 0;
	bottom: 0;
	left: 0;
	z-index: 80;
	display: none;
	align-items: center;
	gap: .5rem;
	background: var( --paper );
	border-top: 1px solid var( --line );
	box-shadow: 0 -6px 24px rgba( 11, 14, 20, .1 );

	/* The inset is in the padding rather than on the body, because a bar that
	   ends at bottom: 0 on an iPhone ends underneath the home indicator. */
	padding: .55rem .75rem calc( .55rem + env( safe-area-inset-bottom ) );
}

.quotebar__btn {
	display: flex;
	align-items: center;
	justify-content: center;
	gap: .45rem;

	/* flex: 1 1 0 alone is not enough — a flex item will not shrink below its
	   own min-content, and min-content here is the width of the longest word
	   plus an icon. This is the declaration whose absence pushed the label off
	   the screen. */
	flex: 1 1 0;
	min-width: 0;

	/* 2.9rem clears the 44px minimum touch target at every root size the theme
	   ships, without needing a magic pixel value. */
	min-height: 2.9rem;
	padding: .55rem .7rem;
	border-radius: var( --radius );
	background: var( --surface );
	color: var( --ink );
	font-size: var( --step--2 );
	font-weight: 700;
	letter-spacing: var( --track-label );
	text-transform: uppercase;
	text-decoration: none;
	transition: background var( --speed ) var( --ease );
}

.quotebar__btn:active {
	background: var( --surface-2 );
}

/* Truncation as the last resort rather than the first. Everything above is
   arranged so it should not be reached, but a translation is free to be longer
   than the English and an ellipsis is a better failure than a label lying
   across the button beside it.

   :not( .quotebar__count ) because a bare .quotebar__btn span outweighs
   .quotebar__count on specificity and would hand the pill a min-width of 0 —
   collapsing a two-digit count to the width of its own digits minus its
   padding. */
.quotebar__btn span:not( .quotebar__count ) {
	min-width: 0;
	overflow: hidden;
	white-space: nowrap;
	text-overflow: ellipsis;
}

/* The primary takes about two thirds. A quote request and a WhatsApp message
   are not the same size of decision, and giving them one half each says they
   are. */
.quotebar__btn--primary {
	flex: 1.9 1 0;
	background: var( --brand );
	color: var( --brand-fg );
}

.quotebar__btn--primary:active {
	background: var( --brand-dark );
}

.quotebar__btn svg {
	width: 17px;
	height: 17px;
	flex: 0 0 auto;
}

/* The count pill, drawn out of the two colours the button already is. Hard
   black would vanish on a dark primary; this pair inverts with it. */
.quotebar__count {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	flex: 0 0 auto;
	min-width: 1.35em;
	height: 1.35em;
	padding: 0 .3em;
	background: var( --brand-fg );
	border-radius: 999px;
	color: var( --brand );
	font-size: var( --step--2 );
	line-height: 1;
}

.wa-float {
	position: fixed;
	right: clamp( .9rem, 2vw, 1.6rem );
	bottom: clamp( .9rem, 2vw, 1.6rem );
	z-index: 70;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 52px;
	height: 52px;
	background: #25d366;
	border-radius: 999px;
	color: #fff;
	box-shadow: var( --shadow-lg );
	transition: transform var( --speed ) var( --ease );
}

.wa-float:hover {
	transform: scale( 1.06 );
}

.wa-float svg {
	width: 26px;
	height: 26px;
}

/* ---------------------------------------------------------------------------
   The trade list signup
   ------------------------------------------------------------------------ */

.footer__signup {
	display: grid;
	grid-template-columns: 1fr minmax( 280px, 420px );
	gap: var( --gap-lg );
	align-items: center;
	padding-bottom: var( --gap-lg );
	margin-bottom: var( --gap-lg );
	border-bottom: 1px solid rgba( 255, 255, 255, .14 );
}

.signup__intro p {
	margin: .3rem 0 0;
	max-width: 46ch;
	color: rgba( 255, 255, 255, .78 );
}

.signup {
	display: flex;
	gap: .5rem;
}

.signup input[type="email"] {
	flex: 1 1 auto;
	min-width: 0;
	background: rgba( 255, 255, 255, .06 );
	border-color: rgba( 255, 255, 255, .28 );
	color: var( --paper );
}

.signup input[type="email"]::placeholder {
	color: rgba( 255, 255, 255, .5 );
}

.signup__small {
	grid-column: 1 / -1;
	margin: 0;
	color: rgba( 255, 255, 255, .5 );
	font-size: var( --step--2 );
	max-width: 78ch;
}

.signup__notice {
	grid-column: 1 / -1;
	margin: 0;
	padding: .55rem .8rem;
	border-radius: var( --radius );
	font-size: var( --step--1 );
}

.signup__notice--ok {
	background: rgba( 20, 108, 46, .22 );
	color: #b9f2cb;
}

.signup__notice--bad {
	background: rgba( 168, 35, 42, .24 );
	color: #ffc9cc;
}

.cb-hp {
	position: absolute !important;
	width: 1px;
	height: 1px;
	overflow: hidden;
	clip-path: inset( 50% );
}

/* ---------------------------------------------------------------------------
   Reveal on scroll

   Eight pixels and a fade. Anything more and a long catalogue page spends its
   whole scroll animating, which is the opposite of the impression a factory
   selling on delivery time wants to leave.
   ------------------------------------------------------------------------ */

.will-reveal {
	opacity: 0;
	transform: translateY( 8px );
}

.is-in {
	opacity: 1;
	transform: none;
	transition: opacity .5s var( --ease ), transform .5s var( --ease );
}

/* ---------------------------------------------------------------------------
   Where all of this changes shape
   ------------------------------------------------------------------------ */

@media ( max-width: 1100px ) {

	.mega__inner {
		grid-template-columns: 1fr;
	}
}

@media ( max-width: 880px ) {

	/* In the drawer the panel is part of the list, not a sheet over it. */
	.mega {
		position: static;
		padding: 0 0 var( --gap );
		border: 0;
		box-shadow: none;
		animation: none;
	}

	.mega__inner {
		padding: 0;
	}

	.mega__aside {
		padding: var( --gap ) 0 0;
		background: transparent;
	}

	.mega-toggle {
		position: absolute;
		top: .35rem;
		right: 0;
		display: block;
		width: 40px;
		height: 40px;
		padding: 0;
		background: transparent;
		border: 0;
		cursor: pointer;
	}

	.mega-toggle::before {
		content: "";
		position: absolute;
		top: 50%;
		left: 50%;
		width: 8px;
		height: 8px;
		border-right: 2px solid currentColor;
		border-bottom: 2px solid currentColor;
		transform: translate( -50%, -70% ) rotate( 45deg );
	}

	.mega-toggle[aria-expanded="true"]::before {
		transform: translate( -50%, -30% ) rotate( -135deg );
	}

	.quotebar {
		display: flex;
	}

	/* The floating button would otherwise sit on top of the quote bar. */
	.wa-float {
		bottom: calc( 4.7rem + env( safe-area-inset-bottom ) );
	}

	/* .55 + 2.9 + .55 of bar, and the inset again because the bar's own bottom
	   padding is part of its height. Without the second term the last line of a
	   page sits under the home indicator on any notched phone. */
	body.has-quotebar {
		padding-bottom: calc( 4.1rem + env( safe-area-inset-bottom ) );
	}

	.header-cta__label {
		display: none;
	}

	.header-cta {
		padding: .55rem .7rem;
	}

	.header-cta::before {
		content: "RFQ";
	}

	.footer__signup {
		grid-template-columns: 1fr;
	}

	.compare-tray__label {
		flex: 1 0 100%;
	}
}

@media ( max-width: 520px ) {

	.saved-link span:not( .saved-link__count ) {
		display: none;
	}

	.signup {
		flex-wrap: wrap;
	}

	.signup input[type="email"] {
		flex: 1 0 100%;
	}
}

/* The narrowest phones still in service are 320px wide, and at that width two
   labelled buttons and their icons cannot both be read. The secondary gives up
   its label and becomes a square, which leaves the primary the whole rest of
   the bar — "Request a quote" spelled out in full is the entire point of the
   thing, and abbreviating it is not an option.

   The label is clipped rather than display: none, because it is the button's
   accessible name: hiding it outright would announce the WhatsApp button to a
   screen reader as nothing at all. */
@media ( max-width: 24rem ) {

	.quotebar__btn--alt {
		flex: 0 0 auto;
		width: 2.9rem;
		padding: 0;
	}

	.quotebar__btn--alt span:not( .quotebar__count ) {
		position: absolute;
		width: 1px;
		height: 1px;
		margin: -1px;
		padding: 0;
		border: 0;
		overflow: hidden;
		clip-path: inset( 50% );
		white-space: nowrap;
	}
}

/* -------------------------------------------------------------------------
 * The comparison page
 *
 * A wide table on a narrow screen is the one place horizontal scrolling is
 * the right answer: stacking the columns would destroy the only thing the
 * page is for, which is reading across a row.
 * ---------------------------------------------------------------------- */

.compare-scroll {
	overflow-x: auto;
	-webkit-overflow-scrolling: touch;
	margin-inline: calc( var( --gap ) * -1 );
	padding-inline: var( --gap );
}

.compare {
	width: 100%;
	border-collapse: collapse;
	min-width: 40rem;
}

.compare th,
.compare td {
	text-align: start;
	vertical-align: top;
	padding: .9rem 1rem;
	border-bottom: 1px solid var( --line, rgba( 0, 0, 0, .1 ) );
}

.compare tbody th {
	width: 12rem;
	font-size: var( --step--1 );
	letter-spacing: var( --track-label );
	text-transform: uppercase;
	color: var( --muted );
	font-weight: 600;
}

/*
 * The whole point of the page. A row where the products actually differ is
 * tinted and its label ruled, so a buyer scanning down the left column can
 * see where the decision is before reading a single cell.
 */
.compare tbody tr.is-different {
	background: var( --surface );
}

.compare tbody tr.is-different th {
	color: var( --ink );
	box-shadow: inset 3px 0 0 0 var( --brand );
}

.compare__product {
	width: 16rem;
	vertical-align: bottom;
}

.compare__shot {
	display: block;
	margin-bottom: .8rem;
}

.compare__shot img {
	width: 100%;
	height: auto;
	border-radius: var( --radius );
}

.compare__name {
	font-size: var( --step-1 );
	line-height: 1.25;
	margin: 0 0 .3rem;
}

.compare__name a {
	color: inherit;
	text-decoration: none;
}

.compare__name a:hover,
.compare__name a:focus-visible {
	color: var( --brand-ink );
}

.compare__sku {
	display: block;
	font-size: var( --step--1 );
	letter-spacing: var( --track-label );
	color: var( --muted );
	margin-bottom: .7rem;
}

.compare__drop {
	display: inline-block;
	margin-top: .6rem;
	font-size: var( --step--1 );
	color: var( --muted );
}

.compare__none {
	color: var( --muted );
}

.compare__actions {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: .8rem;
	margin-top: var( --gap );
}

.compare__actions form {
	margin: 0;
}

/* The shortlist is empty, or the catalogue found nothing. */

.empty {
	text-align: center;
	max-width: 34rem;
	margin-inline: auto;
	padding: calc( var( --gap ) * 2 ) 0;
}

.empty h2 {
	margin-top: 0;
}

.empty p {
	color: var( --muted );
	margin-bottom: 1.4rem;
}

/* -------------------------------------------------------------------------
 * Global export
 *
 * The map is a plotted grid of squares in one path, so it takes its colour
 * from currentColor and needs no second asset for the dark band it sits in.
 * It is decoration around a claim, not a chart, so it never grows a legend.
 * ---------------------------------------------------------------------- */

.export__grid {
	display: grid;
	gap: calc( var( --gap ) * 1.5 );
	grid-template-columns: minmax( 0, 24rem ) minmax( 0, 1fr );
	align-items: center;
}

.export__lede .section__title {
	margin-bottom: .8rem;
}

.export__text {
	margin: 0 0 1.6rem;
	color: rgba( 255, 255, 255, .72 );
	max-width: 34em;
}

.export__map {
	min-width: 0;
}

.worldmap {
	display: block;
	width: 100%;
	height: auto;
	color: var( --brand );
}

/*
 * Two fills in one path is not possible, so the ocean side of the picture is
 * carried by opacity instead: the land reads as brand colour at a weight that
 * does not fight the headline next to it.
 */
.worldmap path {
	fill: currentColor;
	opacity: .55;
}

@media ( prefers-reduced-motion: no-preference ) {

	.worldmap path {
		animation: cb-map 1.2s var( --ease, ease-out ) both;
	}
}

@keyframes cb-map {

	from {
		opacity: 0;
	}

	to {
		opacity: .55;
	}
}

.export__places {
	list-style: none;
	margin: calc( var( --gap ) * 1.2 ) 0 0;
	padding: 0;
	display: flex;
	flex-wrap: wrap;
	gap: .5rem;
}

.export__places li {
	font-size: var( --step--1, .85rem );
	letter-spacing: var( --track-label, .06em );
	text-transform: uppercase;
	padding: .4rem .7rem;
	border: 1px solid rgba( 255, 255, 255, .18 );
	border-radius: var( --radius, 4px );
	color: rgba( 255, 255, 255, .72 );
}

@media ( max-width: 880px ) {

	.export__grid {
		grid-template-columns: 1fr;
	}
}

@media print {

	.topbar,
	.site-header,
	.site-footer,
	.whatsapp,
	.filters,
	.price__actions,
	.card__foot,
	.comments {
		display: none !important;
	}

	body {
		font-size: 11pt;
	}

	.product {
		grid-template-columns: 1fr;
	}

	a[href^="http"]::after {
		content: " (" attr( href ) ")";
		font-size: 9pt;
		color: #555;
	}
}
