/*
 * Home page — section styles.
 *
 * Product Range: every value traces to Swiftheat Home v2.dc.html's
 * `#products` section, measured directly on that file with Chrome DevTools
 * (both its literal inline styles and live computed values). Uses
 * tokens.css wherever a matching token exists; the
 * handful of values with no reusable equivalent are literal, with their
 * source measurement commented, matching header.css's own convention.
 *
 * Container: --site-container (1200px, the project's locked value — the
 * design file itself uses 1640px; not used here per instruction).
 *
 * Specificity note: Elementor Pro enqueues its Default Kit's global CSS
 * (post-9.css) on every page regardless of whether that page has any
 * Elementor content, and adds `elementor-kit-9` to <body> globally too.
 * That stylesheet includes bare `.elementor-kit-9 h2` / `h3` / `a` rules
 * (specificity 0,1,1), which beat a plain single-class selector like
 * `.product-range__heading` (0,1,0) regardless of source order — confirmed
 * live via getMatchedCSSRules-style inspection (h2 was rendering at the
 * Kit's 48px instead of this file's 56px until fixed). Every selector
 * that targets a heading tag or the card's own `<a>` element is therefore
 * deliberately written as a 2-class compound selector (0,2,0) to reliably
 * win against the Kit, without `!important`.
 */

/* `.site-container` is intentionally not defined here — defining it
 * per-stylesheet only ever took effect on the front page, since home.css
 * is enqueued conditionally via is_front_page(). Every other page carrying
 * the `site-container` class (Header, Footer, Product Archive) would
 * silently render uncapped. It lives in header.css instead (one of the
 * two stylesheets this theme enqueues unconditionally on every page) as
 * the one canonical, genuinely site-wide definition. */

/* ---- Product Range ---- */

.product-range {
	background: var(--color-bg-neutral);
	background-image: radial-gradient(rgba(20, 40, 90, 0.05) 1px, transparent 1px);
	background-size: 28px 28px;
	border-top: 1px solid var(--border-light);
}

.product-range__inner {
	padding: var(--space-section-lg-top) var(--gutter-desktop) var(--space-section-lg-bottom);
	/* box-sizing: border-box is required here — same root cause as
	   .process__inner's own rule below: combining max-width (from the
	   shared .site-container class) with padding on the same element,
	   under the default content-box sizing, means max-width never actually
	   constrained this element — measured live before this fix: the
	   container rendered at the full viewport width, uncapped and
	   uncentered, at every realistic desktop width. border-box makes
	   max-width constrain the border box, so the 1200px cap now applies
	   correctly. */
	box-sizing: border-box;
}

.product-range__header {
	display: flex;
	justify-content: space-between;
	align-items: flex-end;
	/* 40px gap / 80px margin-bottom are one-off measured values (design:
	   gap:40px, margin-bottom:80px on this exact row) — not part of the
	   reusable gap/spacing scales. */
	gap: 40px;
	margin-bottom: 80px;
}

.product-range__intro {
	/* One-off measured value (design: max-width:760px on this block). */
	max-width: 760px;
}

.product-range__eyebrow {
	font-family: var(--font-mono);
	font-size: var(--fs-eyebrow);
	letter-spacing: var(--ls-eyebrow);
	color: var(--color-red);
	font-weight: var(--fw-semibold);
	/* One-off measured value. */
	margin-bottom: 20px;
}

.product-range .product-range__heading {
	font-family: var(--font-primary);
	font-size: var(--fs-h2-major);
	font-weight: var(--fw-extrabold);
	letter-spacing: var(--ls-heading);
	line-height: var(--lh-heading);
	color: var(--color-navy);
	margin: 0;
	text-wrap: balance;
}

.product-range__counter {
	flex: none;
	font-family: var(--font-mono);
	font-size: var(--fs-meta);
	color: var(--text-secondary);
	letter-spacing: var(--ls-meta);
	text-align: right;
	/* 1.9 is a one-off measured line-height (design: line-height:1.9 on this
	   exact element) — distinct from the reusable 1.7/1.8 body line-heights. */
	line-height: 1.9;
}

/* ---- Product Range: introductory story/expertise content ----
 * Added above the existing product grid. Reuses this section's own
 * existing tokens/colors/spacing scale throughout — no new visual
 * language introduced. */

.product-range__story {
	/* Sits between the header row and the grid — spacing matches the
	   header row's own existing 80px margin-bottom (see
	   .product-range__header) so the new block reads as evenly spaced
	   between title and grid rather than closer to one than the other. */
	margin-bottom: 80px;
}

.product-range__lead {
	font-family: var(--font-primary);
	font-size: var(--fs-body-large);
	line-height: var(--lh-body);
	color: var(--text-body-muted);
	/* Comfortable reading width, not the full 1200px container — matches
	   .product-range__intro's own existing 760px measure above. */
	max-width: 760px;
	margin: 0 0 48px;
	text-wrap: pretty;
}

.product-range__story-block {
	/* Comfortable reading width for body copy, same reasoning as the
	   lead paragraph above. */
	max-width: 760px;
	margin: 0 0 56px;
	/* Left rule + padding gives this block clear visual separation from
	   the lead paragraph above it without adding a full card border —
	   reuses the existing border-light token, same treatment already
	   used for Process's own step dividers. */
	padding-left: 24px;
	border-left: 3px solid var(--border-light);
}

.product-range .product-range__story-heading {
	font-family: var(--font-primary);
	font-size: var(--fs-h2-sub);
	font-weight: var(--fw-extrabold);
	line-height: var(--lh-heading);
	color: var(--color-navy);
	margin: 0 0 20px;
	text-wrap: balance;
}

.product-range__story-text {
	font-family: var(--font-primary);
	font-size: var(--fs-body);
	line-height: var(--lh-body);
	color: var(--text-body-muted);
	margin: 0;
	text-wrap: pretty;
}

.product-range__story-text + .product-range__story-text {
	margin-top: 20px;
}

.product-range .product-range__expertise-heading {
	font-family: var(--font-primary);
	font-size: var(--fs-h2-sub);
	font-weight: var(--fw-extrabold);
	line-height: var(--lh-heading);
	color: var(--color-navy);
	margin: 0 0 28px;
}

.product-range__expertise-grid {
	/* 2x2 on desktop (4 items) — reuses --gap-grid-4col, this project's
	   own existing 4-item gap token (product cards, process steps),
	   rather than inventing a new value. */
	display: grid;
	grid-template-columns: repeat(2, minmax(0, 1fr));
	gap: var(--gap-grid-4col);
}

.product-range__expertise-item {
	/* Same card language as the Product cards below (white bg, light
	   border, card radius) — reused rather than a new visual style. */
	background: var(--color-bg-white);
	border: 1px solid var(--border-light);
	border-radius: var(--radius-card);
	padding: var(--pad-card);
	box-sizing: border-box;
}

.product-range .product-range__expertise-title {
	font-family: var(--font-primary);
	font-size: var(--fs-card-title);
	font-weight: var(--fw-extrabold);
	color: var(--color-navy);
	margin: 0 0 10px;
}

.product-range__expertise-desc {
	font-family: var(--font-primary);
	font-size: 14.5px;
	line-height: 1.65;
	color: var(--text-secondary);
	margin: 0;
	text-wrap: pretty;
}

.product-range__grid {
	/* 3/2/1 responsive grid, intentionally not the 4/2/1 the original
	   design showed — a client requirement. --gap-grid-3col is this
	   project's own existing 3-column gap token, already used elsewhere (About "what
	   we do", overview strip) — reused here rather than inventing a new
	   value.
	   The grid itself now doubles as the dark "frame" the cards sit on —
	   background/radius/texture below — rather than adding an extra
	   wrapper element, keeping this a CSS-only change. --color-navy-deep
	   and --radius-frame are both existing tokens already used the same
	   way elsewhere (Quality's panel bg; Hero's own image frame radius). */
	display: grid;
	grid-template-columns: repeat(3, minmax(0, 1fr));
	gap: var(--gap-grid-3col);
	background: var(--color-navy-deep);
	border-radius: var(--radius-frame);
	/* 48px is this frame's own literal — generous breathing room around
	   the grid, consistent with this file's existing 40-56px intra-section
	   spacing scale, not a mandated token value. */
	padding: 48px;
	position: relative;
	overflow: hidden;
}

.product-range__grid::before {
	content: "";
	position: absolute;
	inset: 0;
	/* Same grid-line texture literal already established on this page's
	   own Why Swiftheat section (`.why-swiftheat::before` below) — reused
	   here rather than inventing a new one. */
	background-image: linear-gradient(rgba(143, 166, 201, 0.05) 1px, transparent 1px),
		linear-gradient(90deg, rgba(143, 166, 201, 0.05) 1px, transparent 1px);
	background-size: 56px 56px;
	pointer-events: none;
}

/* ---- Product Card ---- */

.product-range__grid .product-card {
	/* No background of its own — the image zone and info zone below each
	   paint their own full-height background, so the card's box is fully
	   covered by its two children. */
	border: 1px solid var(--color-navy-raised);
	border-radius: var(--radius-card);
	overflow: hidden;
	display: flex;
	flex-direction: column;
	box-shadow: var(--shadow-card-rest);
	transition: box-shadow var(--transition-standard), transform var(--transition-standard);
	color: inherit;
	text-decoration: none;
	position: relative;
	z-index: 1;
}

.product-range__grid .product-card:hover,
.product-range__grid .product-card:focus-visible {
	box-shadow: var(--shadow-card-hover);
	transform: translateY(var(--translate-card-hover));
	color: inherit;
}

.product-card:hover .product-card__title,
.product-card:focus-visible .product-card__title {
	color: var(--color-red);
}

.product-card:hover .product-card__img,
.product-card:focus-visible .product-card__img {
	transform: scale(1.03);
}

.product-card__image {
	/* Light image zone — unchanged from before (already the design's own
	   near-white "product tray" color, paired with mix-blend-mode:multiply
	   so photography sits cleanly on it). */
	background: var(--color-bg-tray);
	/* 290px tray height / 36px padding are one-off measured values specific
	   to this card (design: height:290px, padding:36px on this exact div). */
	height: 290px;
	display: flex;
	align-items: center;
	justify-content: center;
	padding: 36px;
	box-sizing: border-box;
}

.product-card__img {
	/* Design uses a per-image JS-computed pixel height ({{ p.h }}) to keep
	   each product photo proportional inside the fixed-height tray. The
	   same visual result — full-height-or-full-width, whichever the
	   image's own aspect ratio allows, never distorted — is achieved here
	   with plain CSS (max-width/max-height + object-fit:contain), with no
	   per-image JS computation needed. */
	max-width: 100%;
	max-height: 100%;
	display: block;
	object-fit: contain;
	mix-blend-mode: multiply;
	transition: transform var(--transition-standard);
}

.product-card__body {
	/* Dark navy info zone — --color-navy is deliberately a shade lighter
	   than the frame's own --color-navy-deep background above, so the
	   card reads as a distinct surface rather than blending into it. */
	background: var(--color-navy);
	/* 28px 30px 30px is the design's own exact padding (not uniform, so not
	   forced into the --pad-card:30px token, which documents this as one
	   of its own "varies 28-34px" instances). 10px gap is a one-off. */
	padding: 28px 30px 30px;
	display: flex;
	flex-direction: column;
	gap: 10px;
	flex: 1;
}

.product-card .product-card__title {
	font-family: var(--font-primary);
	font-size: var(--fs-card-title);
	font-weight: var(--fw-extrabold);
	color: var(--text-on-dark);
	margin: 0;
	transition: color var(--transition-standard);
}

.product-card__desc {
	font-family: var(--font-primary);
	font-size: var(--fs-body-small);
	/* 1.6 is the design's own exact line-height for this element — distinct
	   from the reusable --lh-body (1.7) token, not a substitute for it. */
	line-height: 1.6;
	color: var(--text-on-dark-body);
	flex: 1;
	margin: 0;
	text-wrap: pretty;
}

.product-card__link {
	font-family: var(--font-primary);
	font-size: var(--fs-body-small);
	font-weight: var(--fw-bold);
	/* Swiftheat red accent — already an established color on this exact
	   dark-navy family of backgrounds elsewhere (Home Hero's own eyebrow). */
	color: var(--color-red);
	/* One-off measured value. */
	margin-top: 6px;
}

/* ---- Process ("How We Work") ----
 * Every value traces to Swiftheat Home v2.dc.html's `#process` section,
 * measured the same way as Product Range.
 *
 * Heading/step-title selectors are written as 2-class compounds from the
 * start (not discovered as a bug afterward) — the Product Range build
 * found that Elementor Pro's global Kit CSS (post-9.css) is enqueued on
 * every page regardless of content and adds bare `.elementor-kit-9 h2/h3`
 * rules at specificity (0,1,1), beating a plain single-class selector
 * (0,1,0). Confirmed live again here: `.elementor-kit-9 h3` sets
 * font-size:20px/weight:800/color:navy — the same 3 values this design
 * happens to use for step titles, so a collision here would have been
 * invisible for those 3 properties but would still have silently forced
 * an unwanted line-height (1.3em) and letter-spacing (normal) from the
 * Kit. Written defensively as (0,2,0) selectors throughout. */

.process {
	background: var(--color-bg-white);
}

.process__inner {
	/* 120px bottom padding is this section's own exact value — within
	   tokens.css's documented "120-140px by section" range, but not the
	   140px --space-section-lg-bottom value itself (that is Product
	   Range's own instance), so kept literal here rather than reusing a
	   token that would be 20px too tall. */
	padding: var(--space-section-lg-top) var(--gutter-desktop) 120px;
	/* box-sizing: border-box is required here. `.site-container` (default
	   content-box, unset here) combines `max-width` with padding on the same element;
	   under content-box, a block box's `width:auto` fill algorithm solves
	   for CONTENT width = containing-block width − padding, and max-width
	   only constrains that content width — so at a 1280px viewport with
	   1200px max-width and 128px total horizontal padding (64px×2), the
	   unconstrained content width (1280−128=1152) never actually reaches
	   1200, and max-width silently never engages: the section fills the
	   full viewport instead of capping/centering at 1200px. Scoping
	   border-box to this element only (not `.site-container`, which
	   Product Range also uses) makes
	   max-width constrain the BORDER box instead, so the 1200px cap
	   applies correctly and margin:auto centers it as intended. Confirmed
	   live: without this line the rendered container was 1280px (full
	   viewport, uncentered); with it, 1200px, centered, exactly locked. */
	box-sizing: border-box;
}

.process__header {
	display: grid;
	grid-template-columns: 1fr 1fr;
	/* 80px gap / 72px margin-bottom are one-off measured values (design:
	   gap:80px, margin-bottom:72px on this exact row). */
	gap: 80px;
	align-items: end;
	margin-bottom: 72px;
}

.process__eyebrow {
	font-family: var(--font-mono);
	font-size: var(--fs-eyebrow);
	letter-spacing: var(--ls-eyebrow);
	color: var(--color-red);
	font-weight: var(--fw-semibold);
	margin-bottom: 20px;
}

.process .process__heading {
	font-family: var(--font-primary);
	font-size: var(--fs-h2-major);
	font-weight: var(--fw-extrabold);
	letter-spacing: var(--ls-heading);
	line-height: var(--lh-heading);
	color: var(--color-navy);
	margin: 0;
	text-wrap: balance;
}

.process__supporting {
	font-family: var(--font-primary);
	font-size: var(--fs-body);
	/* 1.75 is this element's own exact measured line-height — distinct
	   from --lh-body (1.7) and --lh-body-loose (1.8), not a substitute
	   for either. */
	line-height: 1.75;
	color: var(--text-body-muted);
	/* One-off measured value (design: max-width:520px on this element). */
	max-width: 520px;
	margin: 0;
	text-wrap: pretty;
}

.process__grid {
	display: grid;
	grid-template-columns: repeat(4, 1fr);
	/* No gap: the design sets none on this grid — column spacing comes
	   entirely from each step's own padding-right (32px) below, confirmed
	   live (getComputedStyle → row-gap/column-gap: normal, i.e. 0). Adding
	   an explicit gap here would double the intended spacing. */
}

.process__step {
	border-top: 1px solid var(--border-warm);
	/* 28px/32px/44px/0 is the design's own exact, non-uniform padding —
	   left:0 is deliberate (the dot marker sits flush left at the step's
	   own edge). */
	padding: 28px 32px 44px 0;
	position: relative;
	transition: border-color var(--transition-standard);
}

.process__step:hover {
	border-top-color: var(--color-red);
}

.process__step-dot {
	position: absolute;
	/* One-off measured position (design: top:-5px;left:0 — the dot pokes
	   up over the step's own border-top line). */
	top: -5px;
	left: 0;
	width: 9px;
	height: 9px;
	background: var(--color-navy);
	border-radius: var(--radius-pill);
}

.process__step-num {
	font-family: var(--font-mono);
	/* 12px is this element's own exact size — distinct from --fs-meta
	   (12.5px), not a substitute for it. */
	font-size: 12px;
	color: var(--color-red);
	font-weight: var(--fw-semibold);
	/* 0.1em is a one-off measured letter-spacing, distinct from the
	   reusable --ls-meta (0.08em)/--ls-micro (0.06em) values. */
	letter-spacing: 0.1em;
	margin-bottom: 14px;
}

.process .process__step-title {
	font-family: var(--font-primary);
	font-size: var(--fs-card-title);
	font-weight: var(--fw-extrabold);
	color: var(--color-navy);
	margin: 0 0 10px;
}

.process__step-desc {
	font-family: var(--font-primary);
	/* 14.5px is this element's own exact size — distinct from
	   --fs-body-small (14px), not a substitute for it. */
	font-size: 14.5px;
	/* 1.65 is this element's own exact measured line-height — distinct
	   from --lh-body (1.7) and Product Range's own 1.6, not a substitute
	   for either. */
	line-height: 1.65;
	color: var(--text-secondary);
	margin: 0;
	text-wrap: pretty;
}

/* ---- Why Swiftheat ----
 * Every value traces to Swiftheat Home v2.dc.html's `#about` section,
 * measured the same way as Process/Product Range, except where noted.
 *
 * .why-swiftheat__inner uses box-sizing:border-box from the start — this
 * is required whenever max-width and padding are combined on the same
 * element (see .process__inner above). Heading/title selectors are
 * written as 2-class compounds from the start too, for the same reason
 * established in Process (Elementor Kit's bare `.elementor-kit-9 h2` rule).
 *
 * Redesigned as a two-column composition: a narrow heading "anchor"
 * column on the left, an 8-item 2-column reasons grid on the right —
 * same "narrow left column + wider right column" shape as the About
 * page's own `.about-story__inner`, reused here rather than inventing a
 * new grid ratio — plus the full-width closing statement below both.
 * Replaces the previous single-column checklist capped at 640px, which
 * left roughly half the section's width empty on desktop. The section's
 * own background (`.why-swiftheat` fill + `.why-swiftheat::before`
 * grid-line texture) is unchanged. */

.why-swiftheat {
	background: var(--color-navy);
	color: var(--text-on-dark);
	overflow: hidden;
	position: relative;
}

.why-swiftheat::before {
	content: "";
	position: absolute;
	inset: 0;
	/* Design's own faint grid-line texture on this section — literal
	   rgba/size values, matching the pattern's own measured spec. */
	background-image: linear-gradient(rgba(143, 166, 201, 0.05) 1px, transparent 1px),
		linear-gradient(90deg, rgba(143, 166, 201, 0.05) 1px, transparent 1px);
	background-size: 56px 56px;
	pointer-events: none;
}

.why-swiftheat__inner {
	padding: var(--space-section-lg-top) var(--gutter-desktop);
	box-sizing: border-box;
	position: relative;
}

.why-swiftheat__top {
	display: grid;
	/* Narrow fixed-ish anchor column, same idea as .about-story__inner's
	   own left column — clamped so the heading stays "comfortably
	   contained" rather than stretching with the viewport. */
	grid-template-columns: minmax(280px, 380px) 1fr;
	gap: var(--gutter-desktop);
	align-items: start;
	margin-bottom: 56px;
}

.why-swiftheat .why-swiftheat__heading {
	font-family: var(--font-primary);
	/* fs-h2-feature (52px) is this project's own existing token for
	   dark-section feature headings — already used by About's own
	   narrow-column heading (.about-story__heading) for the identical
	   layout shape, reused here instead of the old one-off 64px value. */
	font-size: var(--fs-h2-feature);
	font-weight: var(--fw-extrabold);
	letter-spacing: var(--ls-heading);
	line-height: var(--lh-heading);
	/* Explicit color, not just inherited — found live that Elementor
	   Kit's `.elementor-kit-9 h2` rule sets `color` explicitly, and an
	   explicitly-set property always wins over inheritance regardless of
	   the inheriting selector's own specificity (inheritance isn't part
	   of the cascade competition once any matching rule sets a value).
	   The 2-class selector alone wasn't enough for this property — it had
	   to be declared, not just relied on via inheritance from the section. */
	color: var(--text-on-dark);
	margin: 0;
	text-wrap: balance;
}

.why-swiftheat__reasons {
	list-style: none;
	margin: 0;
	padding: 0;
	display: grid;
	grid-template-columns: 1fr 1fr;
	/* gap-grid-3col (28px) is this project's existing token for compact
	   item grids (About's "what we do", the overview strip) — reused for
	   both axes rather than a new one-off spacing value. */
	gap: var(--gap-grid-3col);
}

.why-swiftheat__reason {
	display: flex;
	align-items: flex-start;
	gap: 14px;
	padding-top: 20px;
	/* Same divider color already used by this section's own closing
	   statement border below — reused literal, not a new color. */
	border-top: 1px solid rgba(143, 166, 201, 0.22);
}

.why-swiftheat__reason-icon {
	flex-shrink: 0;
	/* 3px nudge aligns the icon's own visual center with the first line
	   of text (cap-height), not the line box's own full height — checked
	   live against 18px/1.6 body text. Stays correct when text wraps
	   since the icon is aligned to the li's start, not centered. */
	margin-top: 3px;
	color: var(--color-pink-accent);
}

.why-swiftheat__reason span {
	font-family: var(--font-primary);
	font-size: 18px;
	line-height: 1.6;
	color: var(--text-on-dark-body);
	text-wrap: pretty;
}

.why-swiftheat__closing {
	font-family: var(--font-primary);
	/* --fs-body-large (19px) + extrabold + the border-top divider is this
	   section's own way of making the closing statement read as visually
	   stronger than the reasons grid above it, without introducing a new
	   color or font size. Full inner width now that the old 640px cap on
	   the wrapping content column is gone. */
	font-size: var(--fs-body-large);
	font-weight: var(--fw-extrabold);
	line-height: 1.6;
	color: var(--text-on-dark);
	margin: 0;
	padding-top: 28px;
	border-top: 1px solid rgba(143, 166, 201, 0.22);
	text-wrap: pretty;
}

/* ---- Industries ----
 * Every value traces to Swiftheat Home v2.dc.html's `#industries` section,
 * measured the same way as the prior sections.
 *
 * .industries__inner uses box-sizing:border-box from the start (same
 * max-width+padding requirement as the other sections above). The heading is a
 * 2-class compound selector from the start, and its color is explicitly
 * declared (not left to inheritance) — both defensive measures already
 * learned from Product Range's specificity collision and Why Swiftheat's
 * inheritance-loses-to-explicit-value collision with Elementor's Kit CSS. */

.industries {
	background: var(--color-bg-white);
}

.industries__inner {
	padding: var(--space-section-lg-top) var(--gutter-desktop);
	box-sizing: border-box;
}

.industries__header {
	display: flex;
	justify-content: space-between;
	align-items: flex-end;
	/* 40px gap matches Product Range's own header-row gap exactly (reused
	   as a literal, since no shared token exists for it yet — see that
	   section's own CSS comment for the same value). 64px margin-bottom is
	   this section's own exact value, distinct from Product Range's 80px
	   and Process's 72px. */
	gap: 40px;
	margin-bottom: 64px;
}

.industries__intro {
	/* One-off measured value (design: max-width:680px on this block). */
	max-width: 680px;
}

.industries__eyebrow {
	font-family: var(--font-mono);
	font-size: var(--fs-eyebrow);
	letter-spacing: var(--ls-eyebrow);
	color: var(--color-red);
	font-weight: var(--fw-semibold);
	margin-bottom: 20px;
}

.industries .industries__heading {
	font-family: var(--font-primary);
	font-size: var(--fs-h2-major);
	font-weight: var(--fw-extrabold);
	letter-spacing: var(--ls-heading);
	line-height: var(--lh-heading);
	color: var(--color-navy);
	margin: 0;
	text-wrap: balance;
}

.industries__header .industries__cta {
	/* 2-class selector — `.industries__cta` is itself an `<a>`, and
	   Elementor Kit's `.elementor-kit-9 a` rule (0,1,1) would otherwise
	   beat a plain `.industries__cta` (0,1,0). It happens to declare the
	   same color value this design also uses here, which would make the
	   bug invisible rather than absent — scoped correctly regardless, per
	   the same defensive pattern already used for every heading/link in
	   this file. */
	flex: none;
	font-family: var(--font-mono);
	font-size: var(--fs-eyebrow);
	font-weight: var(--fw-semibold);
	/* 0.1em letter-spacing is this element's own exact value — the same
	   literal value Process's step-num happens to use, but kept as its
	   own literal here too rather than retrofitted into a shared token. */
	letter-spacing: 0.1em;
	color: var(--color-blue-link-dark);
	border-bottom: 2px solid var(--color-red);
	padding-bottom: 6px;
	text-decoration: none;
}

.industries__grid {
	display: grid;
	grid-template-columns: repeat(4, 1fr);
	grid-auto-rows: 300px;
	gap: 24px;
}

.industries__tile {
	position: relative;
	overflow: hidden;
	/* Design's own specified base fill for these tiles (not an invented
	   placeholder color) — see this template's own docblock for why no
	   real photography is loaded yet. */
	background: var(--color-navy);
}

.industries__tile--wide {
	grid-column: span 2;
}

.industries__tile-overlay {
	position: absolute;
	left: 0;
	right: 0;
	top: 0;
	padding: 24px 28px;
	pointer-events: none;
	background-image: linear-gradient(rgba(8, 23, 41, 0.75), rgba(8, 23, 41, 0));
}

.industries__tile-name {
	font-family: var(--font-primary);
	font-size: 22px;
	font-weight: var(--fw-extrabold);
	color: var(--text-on-dark);
	text-shadow: 0 2px 12px rgba(8, 23, 41, 0.6);
}

.industries__tile-use {
	font-family: var(--font-mono);
	/* 11.5px is this element's own exact size — same value Why Swiftheat's
	   caption happens to use, kept as its own literal per the same
	   reasoning as .industries__cta above. */
	font-size: 11.5px;
	/* 0.08em — matches --ls-meta exactly (verified via computed
	   letter-spacing 0.92px ÷ 11.5px font = 0.08em, not --ls-eyebrow's
	   0.22em, which would be the wrong token here). */
	letter-spacing: var(--ls-meta);
	color: var(--text-on-dark-caption);
	margin-top: 6px;
	text-shadow: 0 2px 8px rgba(8, 23, 41, 0.6);
}

/* ---- Quality ----
 * Every value traces to Swiftheat Home v2.dc.html's `#quality` section,
 * measured the same way as the prior sections.
 *
 * .quality__inner uses box-sizing:border-box from the start. The heading
 * and CTA link are written as 2-class compound selectors with explicit
 * color from the start too — both defensive measures already learned
 * from Product Range/Why Swiftheat/Industries' own Elementor Kit
 * collisions, applied proactively rather than discovered as bugs. */

.quality {
	background: var(--color-bg-warm);
	border-top: 1px solid var(--border-light);
}

.quality__inner {
	padding: var(--space-section-lg-top) var(--gutter-desktop);
	box-sizing: border-box;
}

.quality__header {
	/* Comfortable reading width for the heading rather than the full
	   container — gives it presence without stretching it edge to edge. */
	max-width: 760px;
	margin-bottom: 56px;
}

.quality__testing {
	display: grid;
	/* 1.15/1, not the old 1.4/1 — the report panel is now meant to read
	   as a visual anchor/"proof" of the testing claims beside it, not a
	   small box next to a dominant text column, so its column is widened
	   relative to before while the text (which still carries more
	   content: intro, checklist, traceability, Manufacturing Excellence)
	   keeps a modest lead. */
	grid-template-columns: 1.15fr 1fr;
	/* 96px gap matches Why Swiftheat's own grid gap exactly (reused as a
	   literal, since no shared token exists for it yet). */
	gap: 96px;
	/* start, not center — the panel anchors to the top of the row,
	   beside the first line of testing copy, rather than floating
	   vertically centered against the text column's full height. */
	align-items: start;
	margin-bottom: 72px;
}

.quality__materials {
	display: grid;
	/* Even split — materials copy and the documentation list are each a
	   self-contained block of comparable weight, unlike the testing
	   column's heavier content mix. */
	grid-template-columns: 1fr 1fr;
	/* --gutter-desktop (64px) reused as the column gap — an existing
	   token, not a new value. */
	gap: var(--gutter-desktop);
	padding-top: 48px;
	margin-bottom: 56px;
	border-top: 1px solid var(--border-warm);
}

.quality__materials-docs {
	/* Subtle vertical divider between the two areas, per the brief —
	   same border-warm token already used by this section's own closing
	   statement divider, not a new color. */
	border-left: 1px solid var(--border-warm);
	padding-left: 32px;
}

.quality__eyebrow {
	font-family: var(--font-mono);
	font-size: var(--fs-eyebrow);
	letter-spacing: var(--ls-eyebrow);
	color: var(--color-red);
	font-weight: var(--fw-semibold);
	margin-bottom: 20px;
}

.quality .quality__heading {
	font-family: var(--font-primary);
	font-size: var(--fs-h2-major);
	font-weight: var(--fw-extrabold);
	letter-spacing: var(--ls-heading);
	line-height: var(--lh-heading);
	color: var(--color-navy);
	margin: 0 0 28px;
	text-wrap: balance;
}

.quality__text {
	font-family: var(--font-primary);
	font-size: var(--fs-body);
	/* 1.75 is this element's own exact line-height — same value Why
	   Swiftheat's paragraphs happen to use, kept as its own literal per
	   the same reasoning used throughout this file. */
	line-height: 1.75;
	color: var(--text-body-muted);
	/* No max-width here — the ~58% content column (.quality__inner above)
	   is now the real constraint (well within a comfortable reading
	   measure on its own), so an extra fixed cap would just narrow the
	   text again for no reason. */
	margin: 0 0 20px;
	text-wrap: pretty;
}

.quality__lead {
	font-family: var(--font-primary);
	font-size: var(--fs-body);
	font-weight: var(--fw-semibold);
	color: var(--color-navy);
	margin: 0 0 16px;
	text-wrap: pretty;
}

.quality__checklist {
	list-style: none;
	margin: 0 0 28px;
	padding: 0;
}

.quality__checklist-item {
	display: flex;
	align-items: flex-start;
	gap: 12px;
	font-family: var(--font-primary);
	font-size: var(--fs-body);
	line-height: 1.5;
	color: var(--color-navy);
}

.quality__checklist-item + .quality__checklist-item {
	margin-top: 12px;
}

.quality__check-icon {
	flex-shrink: 0;
	/* 2px nudge aligns the icon with the first line's cap-height, same
	   reasoning as Why Swiftheat's own checklist icon. */
	margin-top: 2px;
	color: var(--color-red);
}

.quality__checklist-item strong {
	font-weight: var(--fw-extrabold);
}

.quality .quality__subheading {
	font-family: var(--font-primary);
	font-size: var(--fs-h2-sub);
	font-weight: var(--fw-extrabold);
	line-height: var(--lh-heading);
	color: var(--color-navy);
	margin: 8px 0 20px;
}

.quality__doclist {
	list-style: none;
	margin: 0 0 40px;
	padding: 0;
}

.quality__doclist-item {
	display: flex;
	align-items: baseline;
	/* 14px gap matches the same dash-list convention already used by the
	   Single Product page's own Features list (.product-features__item). */
	gap: 14px;
	font-family: var(--font-primary);
	font-size: var(--fs-body-small);
	line-height: 1.6;
	color: var(--text-body-muted);
}

.quality__doclist-item + .quality__doclist-item {
	margin-top: 10px;
}

.quality__doclist-dash {
	color: var(--color-blue-link);
	font-weight: var(--fw-extrabold);
	flex: none;
}

.quality__closing {
	font-family: var(--font-primary);
	font-size: var(--fs-body-large);
	font-weight: var(--fw-extrabold);
	line-height: 1.6;
	color: var(--color-navy);
	margin: 0;
	padding-top: 24px;
	border-top: 1px solid var(--border-warm);
	text-wrap: pretty;
}

.quality__panel {
	background: var(--color-navy-deep);
	border: 1px solid var(--color-navy-raised);
	box-shadow: var(--shadow-panel-deep);
	font-family: var(--font-mono);
}

.quality__panel-header {
	display: flex;
	justify-content: space-between;
	align-items: center;
	padding: 20px 36px;
	border-bottom: 1px solid var(--color-navy-raised);
	font-size: var(--fs-eyebrow);
	/* 0.1em letter-spacing is this element's own exact value — the same
	   literal Process's step-num / Industries' CTA happen to use, kept
	   as its own literal per the established pattern in this file. */
	letter-spacing: 0.1em;
}

.quality__panel-status {
	display: flex;
	align-items: center;
	gap: 10px;
	font-weight: var(--fw-semibold);
	color: var(--text-on-dark);
}

.quality__panel-dot {
	width: 8px;
	height: 8px;
	border-radius: var(--radius-pill);
	background: var(--color-success);
	display: inline-block;
}

.quality__panel-form {
	color: var(--text-on-dark-muted);
}

.quality__panel-tests {
	padding: 8px 36px 4px;
}

.quality__test {
	display: flex;
	justify-content: space-between;
	align-items: center;
	padding: 18px 0;
	border-bottom: 1px solid rgba(143, 166, 201, 0.15);
}

.quality__test:last-child {
	border-bottom: none;
}

.quality__test-name {
	/* 13.5px is this element's own exact size — no matching token. */
	font-size: 13.5px;
	font-weight: var(--fw-semibold);
	color: var(--text-on-dark);
}

.quality__test-method {
	font-size: var(--fs-micro);
	color: var(--text-on-dark-muted);
	margin-top: 5px;
	letter-spacing: var(--ls-utility);
}

.quality__test-badge {
	display: flex;
	align-items: center;
	gap: 8px;
	font-size: var(--fs-eyebrow);
	font-weight: var(--fw-semibold);
	/* 0.16em is this element's own exact letter-spacing — no matching
	   token (distinct from --ls-eyebrow 0.22em / --ls-meta 0.08em /
	   --ls-micro 0.06em). */
	letter-spacing: 0.16em;
	color: var(--color-success);
	border: 1px solid rgba(53, 208, 127, 0.45);
	background: rgba(53, 208, 127, 0.08);
	padding: 6px 14px;
}

.quality__test-badge-dot {
	width: 6px;
	height: 6px;
	border-radius: var(--radius-pill);
	background: var(--color-success);
	display: inline-block;
}

.quality__panel-footer {
	display: flex;
	justify-content: space-between;
	padding: 20px 36px;
	font-size: var(--fs-micro);
	letter-spacing: var(--ls-meta);
	color: var(--text-on-dark-muted);
}

.quality__panel-footer-summary {
	color: var(--color-success);
}

/* ---- FAQ ----
 * Every value traces to Swiftheat Home v2.dc.html's unnamed FAQ section
 * (the section between #quality and #contact).
 *
 * .faq__inner uses box-sizing:border-box from the start. The heading is a
 * 2-class compound selector with explicit color from the start too — both
 * defensive measures already learned from prior sections' own Elementor
 * Kit collisions, applied proactively.
 *
 * Interaction is native <details>/<summary> with a shared `name="faq"`
 * attribute for exclusive-open grouping and a pure-CSS `[open]` +
 * `::before` icon swap — no JavaScript anywhere in this section. See the
 * template file's own docblock for why (design tool's own declarative
 * syntax, not literal JS to copy; live-tested to confirm exclusive-accordion
 * behavior before choosing this approach). */

.faq {
	background: var(--color-bg-white);
}

.faq__inner {
	/* The design's own container for this one section is max-width:1160px
	   (every other section uses 1640px) — kept on the project's single
	   locked 1200px value anyway, for the same reason every other
	   section's own different design max-width was normalized: a
	   per-section container width would visibly misalign against every
	   other section's edges as the user scrolls, breaking the site-wide
	   consistency the container lock exists to guarantee. */
	padding: 120px var(--gutter-desktop);
	box-sizing: border-box;
}

.faq__grid {
	display: grid;
	grid-template-columns: 0.8fr 1.2fr;
	/* 80px gap is this row's own exact measured value. */
	gap: 80px;
	align-items: start;
}

.faq__intro {
	position: sticky;
	/* 120px sticky offset — the same value Why Swiftheat's own sticky
	   photo column already uses, now confirmed reused a second time in
	   actual built code (not just observed in the design source), but
	   still kept as its own literal per that section's own reasoning
	   (not retrofitted into a token, since that would mean editing
	   Why Swiftheat's already-completed, locked CSS). */
	top: 120px;
}

.faq__eyebrow {
	font-family: var(--font-mono);
	font-size: var(--fs-eyebrow);
	letter-spacing: var(--ls-eyebrow);
	color: var(--color-red);
	font-weight: var(--fw-semibold);
	margin-bottom: 20px;
}

.faq .faq__heading {
	font-family: var(--font-primary);
	font-size: var(--fs-h2-sub);
	font-weight: var(--fw-extrabold);
	letter-spacing: var(--ls-heading);
	line-height: var(--lh-heading-loose);
	color: var(--color-navy);
	margin: 0;
	text-wrap: balance;
}

.faq__list {
	display: flex;
	flex-direction: column;
}

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

.faq__question {
	/* Resets the native <summary> disclosure triangle/default styling so
	   the design's own custom "+"/"−" icon (below) is the only marker. */
	list-style: none;
	cursor: pointer;
	display: flex;
	justify-content: space-between;
	align-items: center;
	gap: 24px;
	width: 100%;
	padding: 26px 4px;
	font-family: var(--font-primary);
	/* 18px is this element's own exact size — no matching token. */
	font-size: 18px;
	font-weight: var(--fw-extrabold);
	color: var(--color-navy);
}

.faq__question::-webkit-details-marker {
	display: none;
}

.faq__icon {
	color: var(--color-red);
	font-size: 22px;
	font-weight: var(--fw-semibold);
	flex: none;
}

.faq__icon::before {
	content: "+";
}

.faq__item[open] .faq__icon::before {
	content: "\2212"; /* − (minus sign), matching the design's own glyph, not a hyphen */
}

.faq__answer {
	padding: 0 4px 28px;
	font-family: var(--font-primary);
	/* 15.5px is this element's own exact size — no matching token. */
	font-size: 15.5px;
	line-height: var(--lh-body);
	color: var(--text-body-muted);
	max-width: 640px;
	text-wrap: pretty;
}

/* ---- Contact ----
 * Every value traces to Swiftheat Home v2.dc.html's `#contact` section,
 * measured the same way as the prior sections.
 *
 * .contact__inner uses box-sizing:border-box from the start. The heading
 * and info-value links are written as 2-class compound selectors with
 * explicit color from the start too — defensive measures already learned
 * from every prior section's own Elementor Kit collisions, applied
 * proactively. */

.contact {
	background: var(--color-navy-deep);
	color: var(--text-on-dark);
	position: relative;
	overflow: hidden;
}

.contact::before {
	content: "";
	position: absolute;
	inset: 0;
	/* Design's own faint grid-line texture on this section — same pattern
	   family as Why Swiftheat's own ::before, different rgba/role. */
	background-image: linear-gradient(rgba(143, 166, 201, 0.06) 1px, transparent 1px),
		linear-gradient(90deg, rgba(143, 166, 201, 0.06) 1px, transparent 1px);
	background-size: 56px 56px;
	pointer-events: none;
}

.contact__inner {
	padding: var(--space-section-lg-top) var(--gutter-desktop);
	box-sizing: border-box;
	position: relative;
}

.contact__grid {
	display: grid;
	/* The design's own value is `repeat(auto-fit,minmax(440px,1fr))` — kept
	   as-is (not simplified to a fixed 1fr 1fr) since it is functionally
	   the responsive mechanism here: at the locked 1200px content width
	   it resolves to two equal ~560px columns, and it naturally collapses
	   to one column once available space drops below 440px×2+gap without
	   needing a media query override (verified live). */
	grid-template-columns: repeat(auto-fit, minmax(440px, 1fr));
	gap: 80px;
	align-items: center;
}

.contact__eyebrow {
	font-family: var(--font-mono);
	font-size: var(--fs-eyebrow);
	letter-spacing: var(--ls-eyebrow);
	color: var(--color-pink-accent);
	font-weight: var(--fw-semibold);
	margin-bottom: 24px;
}

.contact .contact__heading {
	font-family: var(--font-primary);
	/* 60px is this element's own exact size — distinct from every other
	   heading size already tokenized (--fs-h2-major 56px, --fs-h2-sub
	   44px), not a substitute for either. */
	font-size: 60px;
	font-weight: var(--fw-extrabold);
	letter-spacing: var(--ls-tight);
	line-height: var(--lh-tight);
	color: var(--text-on-dark);
	margin: 0 0 28px;
	text-wrap: balance;
}

.contact__text {
	font-family: var(--font-primary);
	font-size: var(--fs-body-large);
	line-height: 1.75;
	color: var(--text-on-dark-body);
	max-width: 540px;
	margin: 0 0 48px;
	text-wrap: pretty;
}

.contact__info {
	display: flex;
	flex-direction: column;
	font-family: var(--font-mono);
	/* 13.5px is this element's own exact size — no matching token. */
	font-size: 13.5px;
	max-width: 520px;
	/* Reset default <dl>/<dt>/<dd> margins — this is a definition list
	   semantically (term = label, description = value) but the design's
	   own layout is a plain flex row per pair, not indented description
	   text. */
	margin: 0;
}

.contact__info-row {
	display: flex;
	padding: 16px 0;
	border-top: 1px solid rgba(143, 166, 201, 0.25);
	margin: 0;
}

.contact__info-row--last {
	border-bottom: 1px solid rgba(143, 166, 201, 0.25);
}

.contact__info-label {
	width: 110px;
	flex: none;
	color: var(--text-on-dark-muted);
	/* 0.1em is this element's own exact measured letter-spacing (verified:
	   1.1px ÷ 11px = 0.1em) — the same literal value already used by
	   Process's step-num, Industries' CTA, and Quality's panel-header,
	   kept as its own literal per the established pattern in this file,
	   not --ls-cta (0.02em), which would be the wrong token here. */
	letter-spacing: 0.1em;
	font-size: var(--fs-micro);
	padding-top: 2px;
	font-weight: normal;
}

.contact .contact__info-value {
	color: var(--text-on-dark-value);
	text-decoration: none;
}

.contact .contact__info-value a {
	/* Explicit color, not just inherited from the .contact__info-value
	   wrapper above — found live that Elementor Kit's `.elementor-kit-9 a`
	   rule sets `color` explicitly on the `<a>` itself (the email/phone
	   links), and an explicitly-set property always wins over an
	   inherited one regardless of the inheriting selector's specificity —
	   the exact same class of bug already found on Why Swiftheat's
	   heading. The wrapper's own color was correctly applied to the WORKS
	   address (plain text, no link), but not to the EMAIL/PHONE links
	   until this rule was added. */
	color: var(--text-on-dark-value);
	text-decoration: none;
}

.contact__info-value--address {
	line-height: var(--lh-body);
}

.contact__panel {
	background: var(--color-bg-warm);
	/* 52px is within --pad-card-form's own documented "varies 48-52px"
	   range but not its 48px base value, so kept literal (same treatment
	   as Process's 120px bottom padding vs. Product Range's 140px). */
	padding: 52px;
	color: var(--text-base);
	box-shadow: var(--shadow-frame-hero);
	border: 1px solid var(--border-warm);
	min-width: 0;
}

.contact__panel-header {
	display: flex;
	justify-content: space-between;
	align-items: baseline;
	margin-bottom: 32px;
}

.contact__panel-title {
	font-family: var(--font-primary);
	font-size: 24px;
	font-weight: var(--fw-extrabold);
	color: var(--color-navy);
}

.contact__panel-response {
	font-family: var(--font-mono);
	font-size: var(--fs-micro);
	letter-spacing: 0.14em;
	color: var(--text-faint-label);
}

.contact__form-row {
	display: grid;
	grid-template-columns: 1fr 1fr;
	gap: 16px;
	margin-bottom: 16px;
}

.contact__field {
	min-width: 0;
}

.contact__field--full {
	margin-bottom: 16px;
}

/* Elementor Kit declares `.elementor-kit-9 input:not([type="button"]):not
   ([type="submit"]), .elementor-kit-9 textarea, .elementor-kit-9
   .elementor-field-textual` — specificity (0,3,1) (the class itself, plus
   each :not()'s specificity, which equals its argument's — an attribute
   selector each, so 1+1+1=3 in the class column, plus 1 for the `input`
   type). A first attempt at `.contact .contact__form input[type="text"]`
   turned out to ALSO be (0,3,1) — an exact tie, not a win, since
   `[type="text"]` counts in the class column same as `.contact`/
   `.contact__form`, and `input` itself only adds to the type column, not
   the class column (an arithmetic mistake caught by testing the actual
   rendered padding, not just trusting the selector "looked" more
   specific). Ties resolve to whichever stylesheet is printed later in
   the page, which is Kit's — confirmed live: input padding was still
   rendering as Kit's own `16px 18px` instead of this design's `15px 18px`
   after the first attempt. Adding the real `.contact__field` ancestor
   class (already present in the markup) as a third class gets to
   (0,4,1) — a genuine, decisive win by class-count alone, not a tie,
   without `!important`. */
.contact .contact__form .contact__field input[type="text"],
.contact .contact__form .contact__field input[type="email"],
.contact .contact__form .contact__field input[type="tel"],
.contact .contact__form .contact__field textarea {
	font-family: var(--font-primary);
	font-size: 15px;
	padding: 15px 18px;
	border: 1.5px solid var(--border-warm);
	background: var(--color-bg-white);
	outline: none;
	width: 100%;
	min-width: 0;
	box-sizing: border-box;
	color: var(--text-base);
}

.contact .contact__form .contact__field input[type="text"]:focus,
.contact .contact__form .contact__field input[type="email"]:focus,
.contact .contact__form .contact__field input[type="tel"]:focus,
.contact .contact__form .contact__field textarea:focus {
	border-color: var(--color-blue-link-dark);
}

.contact__form input::placeholder,
.contact__form textarea::placeholder {
	color: var(--text-secondary);
	opacity: 1;
}

.contact .contact__form .contact__field textarea {
	resize: vertical;
	display: block;
}

.contact__dropzone {
	border: 1.5px dashed var(--border-dashed);
	background: var(--color-bg-dropzone);
	padding: 18px;
	display: flex;
	align-items: center;
	justify-content: center;
	gap: 12px;
	font-family: var(--font-mono);
	font-size: var(--fs-utility);
	/* 0.08em — verified: 0.96px ÷ 12px = 0.08em, matching --ls-meta, not
	   --ls-eyebrow (0.22em), which would be the wrong token here. */
	letter-spacing: var(--ls-meta);
	color: var(--text-secondary);
	cursor: pointer;
	transition: border-color var(--transition-fast);
}

.contact__dropzone:hover {
	border-color: var(--color-blue-link-dark);
}

/* Keyboard-focus visibility for the dropzone's hidden file input — the
   design has no focus state of its own for this control (it is a
   decorative-looking mockup element there), so this reuses the same
   focus treatment already applied to the text inputs or falls to a
   sensible outline, satisfying keyboard accessibility without inventing
   a new visual language. */
.contact__field input[type="file"]:focus + .contact__dropzone,
.contact__field input[type="file"]:focus-visible ~ .contact__dropzone {
	border-color: var(--color-blue-link-dark);
}

.contact .contact__submit {
	all: unset;
	box-sizing: border-box;
	cursor: pointer;
	text-align: center;
	width: 100%;
	padding: 17px;
	background: var(--color-red);
	color: var(--text-on-dark);
	font-family: var(--font-primary);
	font-size: 16px;
	font-weight: var(--fw-extrabold);
	transition: background var(--transition-fast);
}

.contact .contact__submit:hover,
.contact .contact__submit:focus-visible {
	background: var(--color-navy);
}

/* Minimal, standard WordPress visually-hidden-but-accessible utility,
   needed for this section's own <label> elements (the design shows only
   placeholder text, so real labels exist for accessibility without
   changing the approved visual appearance). Not yet defined anywhere
   else in the theme — flagged separately as a background task to move
   into a site-wide stylesheet (header.php already uses this same class
   name for its own skip-link/menu-label but has no matching CSS rule
   anywhere, a pre-existing gap unrelated to this section, not fixed here
   beyond this section's own need for it to work). */
.screen-reader-text {
	border: 0;
	clip-path: inset(50%);
	height: 1px;
	margin: -1px;
	overflow: hidden;
	padding: 0;
	position: absolute;
	width: 1px;
	word-wrap: normal;
}

/* ---- Responsive ----
 * The approved design has zero @media queries anywhere in the whole design
 * folder (confirmed by grep) — a desktop-only static export. The 4→2→1
 * column behavior below is not invented: it matches the column
 * configuration already reviewed and approved for this exact section in
 * the earlier Elementor-based Product Range build (columns_tablet:2,
 * columns_mobile:1), reusing this project's own --bp-tablet/--bp-mobile
 * tokens. The same collapse is applied to the Process step grid for
 * consistency — there is no design signal either way, and reusing the
 * one already-reviewed pattern is safer than inventing a second, different
 * one for a visually near-identical 4-column grid. The heading-size/
 * padding reductions at mobile have no design source at all (there is
 * nothing to measure) — they are a minimal, standard, non-decorative
 * adjustment needed to avoid horizontal overflow and oversized type on
 * small screens, same rationale as the Header's own undocumented
 * hamburger-menu addition. */

@media (max-width: 1024px) {
	.product-range__grid {
		grid-template-columns: repeat(2, minmax(0, 1fr));
	}

	/* At 2 columns, .process__step's fixed 32px right-padding gutter
	   still works correctly for the left column; the right column's own
	   trailing 32px just becomes unused whitespace at the section edge,
	   which is correct, expected behavior for a padding-based gutter
	   (not a `gap`), not a bug — no override needed here. */
	.process__grid {
		grid-template-columns: repeat(2, 1fr);
	}

	/* .industries__tile--wide's own `grid-column:span 2` automatically
	   becomes "full row width" of a 2-column grid with no extra rule
	   needed — CSS Grid spans are relative to the grid's current column
	   count, not a fixed number. */
	.industries__grid {
		grid-template-columns: repeat(2, 1fr);
	}

	/* Tablet: the heading anchor and reasons grid stack (heading above,
	   reasons below) — the reasons grid itself stays at 2 columns, same
	   "reduced-column" treatment already used by Product Range/Process's
	   own 4→2 collapse at this breakpoint. */
	.why-swiftheat__top {
		grid-template-columns: 1fr;
		gap: 32px;
	}

	.why-swiftheat .why-swiftheat__heading {
		margin-bottom: 8px;
	}
}

@media (max-width: 768px) {
	.product-range__inner {
		padding: 64px var(--gutter-mobile) 72px;
	}

	.product-range__header {
		flex-wrap: wrap;
		gap: 16px;
		margin-bottom: 40px;
	}

	.product-range__intro {
		max-width: 100%;
	}

	.product-range .product-range__heading {
		font-size: 36px;
	}

	.product-range__counter {
		text-align: left;
	}

	/* Grid stays at 2 columns through this breakpoint — see the
	   @media (max-width: 480px) block at the end of this file for the
	   1-column collapse, which uses this site's existing small-phone
	   breakpoint (already established in 404.css/hero.css) rather than
	   collapsing to 1 column this early. */

	.product-range__grid {
		/* Reduced frame padding at mobile gutter widths — the desktop 48px
		   would eat too much of the available card width on narrow
		   screens. */
		padding: 24px;
	}

	.process__inner {
		padding: 64px var(--gutter-mobile) 56px;
	}

	.process__header {
		grid-template-columns: 1fr;
		gap: 24px;
		align-items: start;
		margin-bottom: 40px;
	}

	.process .process__heading {
		font-size: 36px;
	}

	.process__supporting {
		max-width: 100%;
	}

	.process__grid {
		grid-template-columns: 1fr;
	}

	.process__step {
		/* Single column: the 32px right-padding gutter is no longer
		   needed for column separation, but reducing it keeps the step
		   text from feeling cramped against the narrower mobile gutter
		   without changing any desktop value. */
		padding: 24px 0 32px;
	}

	.why-swiftheat__inner {
		padding: 64px var(--gutter-mobile);
	}

	.why-swiftheat__top {
		margin-bottom: 40px;
	}

	.why-swiftheat .why-swiftheat__heading {
		font-size: 36px;
	}

	.industries__inner {
		padding: 64px var(--gutter-mobile);
	}

	.industries__header {
		flex-wrap: wrap;
		gap: 16px;
		margin-bottom: 40px;
	}

	.industries .industries__heading {
		font-size: 36px;
	}

	.industries__grid {
		/* Single column: a 300px fixed row height per tile still applies
		   (unchanged from desktop — the design's own row height, not
		   something that needs to shrink for readability the way text
		   grids do). */
		grid-template-columns: 1fr;
	}

	.industries__tile--wide {
		/* Found live during verification: `grid-column:span 2` does NOT
		   safely clamp to a single-column grid the way it does going from
		   4→2 columns — with only 1 explicit column defined, the grid
		   engine instead creates an implicit second (0-width) column to
		   satisfy the span, which pushed every other tile that follows it
		   into alternating real/phantom columns, rendering three of the
		   seven tiles at width:0 (invisible). Explicitly resetting the
		   span to 1 at this breakpoint is the fix — confirmed by
		   re-measuring every tile's rendered width after this change. */
		grid-column: span 1;
	}

	.quality__inner {
		padding: 64px var(--gutter-mobile);
	}

	.quality__header {
		max-width: 100%;
		margin-bottom: 40px;
	}

	/* Stacks panel below text below 768px — same breakpoint already used
	   to stack Why Swiftheat's own asymmetric 2-column grid, kept
	   consistent rather than introducing a different one. */
	.quality__testing {
		grid-template-columns: 1fr;
		gap: 40px;
		margin-bottom: 48px;
	}

	.quality__materials {
		grid-template-columns: 1fr;
		gap: 32px;
		padding-top: 40px;
		margin-bottom: 40px;
	}

	.quality__materials-docs {
		/* Divider flips from a vertical rule to a horizontal one once the
		   two areas stack. */
		border-left: 0;
		padding-left: 0;
		padding-top: 32px;
		border-top: 1px solid var(--border-warm);
	}

	.quality .quality__heading {
		font-size: 36px;
	}

	.quality__text,
	.quality__lead,
	.quality__doclist,
	.quality__closing {
		max-width: 100%;
	}

	.quality__panel-header {
		flex-wrap: wrap;
		gap: 8px;
	}

	.quality__test {
		flex-wrap: wrap;
		gap: 8px;
	}

	.faq__inner {
		padding: 64px var(--gutter-mobile);
	}

	.faq__grid {
		/* Stacks heading above the FAQ list below 768px — same breakpoint
		   already used to stack Why Swiftheat's and Quality's own
		   asymmetric 2-column grids, kept consistent. */
		grid-template-columns: 1fr;
		gap: 32px;
	}

	.faq__intro {
		/* Sticky is a desktop-only affordance for a side-by-side layout;
		   once the grid stacks to a single column above, a sticky heading
		   would visually jump/overlap the list below it as the user
		   scrolls — same fix already applied to Why Swiftheat's own
		   sticky photo column. */
		position: static;
	}

	.faq .faq__heading {
		font-size: 32px;
	}

	.faq__question {
		font-size: 16px;
	}

	.contact__inner {
		padding: 64px var(--gutter-mobile);
	}

	.contact__grid {
		/* Found live during verification, not assumed safe from the
		   scrollWidth check alone: the design's own `repeat(auto-fit,
		   minmax(440px,1fr))` has a hard 440px floor per track that
		   cannot shrink further — at a 375px viewport this forced the
		   grid (and the panel inside it) to render at 440px, wider than
		   the viewport. `document.documentElement.scrollWidth` still
		   reported no overflow only because `.contact`'s own
		   `overflow:hidden` (needed for the section's decorative
		   background pattern) clipped the excess instead of causing page
		   scroll — silently cutting off part of the form rather than
		   visibly breaking the layout, which is why measuring the panel's
		   own rendered width directly (not just checking scrollWidth) was
		   necessary to catch this. Overriding to a plain `1fr` here
		   removes the 440px floor, letting the single remaining column
		   actually fill the available mobile width. */
		grid-template-columns: 1fr;
	}

	.contact .contact__heading {
		font-size: 36px;
	}

	.contact__text {
		max-width: 100%;
	}

	.contact__panel {
		/* 52px padding feels excessive against the narrower mobile gutter;
		   reduced for comfort without touching any desktop value. */
		padding: 32px;
	}

	.contact__form-row {
		/* Single column: 2 fields side-by-side (e.g. "Operating
		   temperature (°C)" alongside "Voltage (V)") would be too
		   cramped to read their placeholders at mobile widths. */
		grid-template-columns: 1fr;
	}
}

@media (max-width: 480px) {
	/* Product Grid Responsive Refinement: 2 columns are still usable down
	   to 480px (this site's existing small-phone breakpoint — see
	   404.css/hero.css), so Product Range only drops to 1 column here,
	   not at 768px. */
	.product-range__grid {
		grid-template-columns: 1fr;
	}

	/* Same reasoning as the product grid above — 2 expertise cards are
	   too cramped to read their heading/body text below 480px. */
	.product-range__expertise-grid {
		grid-template-columns: 1fr;
	}

	/* Same reasoning again — 2 reason columns get too cramped for their
	   longer phrases below 480px. */
	.why-swiftheat__reasons {
		grid-template-columns: 1fr;
	}
}
