@charset "UTF-8";
/* ==========================================================================
   J.J. & A.W. Skeet Surfacing Contractors
   Design system: Setting Out

   The site is built from three ideas.

   1. A hard edge. Dark surface meets pale ground on a straight, unrounded
      boundary, the way tarmac meets a kerb. Nothing floats in a rounded card.
   2. The set-out line. A hairline rule across the full width of the page with
      a single yellow segment on it, whose position shifts as you descend. It
      is the divider, the brand mark and a sense of progress in one element.
   3. The job sheet. Factual information is set as hairline ruled rows, the way
      a contractor writes down a job, rather than as feature boxes.

   Contents
     01  Tokens
     02  Reset and base
     03  Typography
     04  Layout and grid
     05  The set-out line
     06  Buttons and links
     07  Header and navigation
     08  Hero
     09  Job sheet and fact rows
     10  Service index
     11  Projects
     12  Editorial sections
     13  Coverage
     14  Enquiry form
     15  Footer
     16  Editor content
     17  Utilities and motion
   ========================================================================== */

/* --------------------------------------------------------------------------
   01  Tokens
   -------------------------------------------------------------------------- */

:root {
	/* Surfaces. Warm off white is the default ground: against real tarmac
	   photography it reads as daylight and paper rather than as software. */
	--c-black: #141414;
	--c-charcoal: #232323;
	--c-graphite: #333331;
	--c-grey: #f1f1ed;
	--c-paper: #f8f7f2;
	--c-white: #ffffff;

	/* Fresh road paint, held slightly cooler than a caution yellow. */
	--c-yellow: #f2c300;
	--c-yellow-bright: #ffd21f;

	/* Ink. Both greys are checked against their ground for AA at body size. */
	--c-ink: #141414;          /* 17.2:1 on paper */
	--c-ink-soft: #55554f;     /* 7.0:1  on paper */
	--c-ash: #a8a8a2;          /* 7.7:1  on black */

	/* Hairlines. The single most used graphic element on the site. */
	--rule: rgba(20, 20, 20, 0.16);
	--rule-strong: rgba(20, 20, 20, 0.32);
	--rule-dark: rgba(255, 255, 255, 0.16);
	--rule-dark-strong: rgba(255, 255, 255, 0.3);

	/* Contextual colours, reassigned by .is-dark. */
	--ground: var(--c-paper);
	--ink: var(--c-ink);
	--ink-soft: var(--c-ink-soft);
	--hair: var(--rule);
	--hair-strong: var(--rule-strong);

	/* Type. One family, used across its full weight range. */
	--font: "Archivo", ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;

	--fs-display: clamp(2.75rem, 1.05rem + 6.6vw, 7rem);
	--fs-h1: clamp(2.25rem, 1.35rem + 3.9vw, 4.5rem);
	--fs-h2: clamp(1.75rem, 1.19rem + 2.4vw, 3.25rem);
	--fs-h3: clamp(1.3125rem, 1.12rem + 0.82vw, 1.875rem);
	--fs-h4: clamp(1.125rem, 1.06rem + 0.28vw, 1.3125rem);
	--fs-lede: clamp(1.1875rem, 1.06rem + 0.55vw, 1.625rem);
	--fs-body: clamp(1rem, 0.965rem + 0.15vw, 1.125rem);
	--fs-small: 0.9375rem;
	--fs-label: 0.8125rem;
	--fs-figure: clamp(3.5rem, 1.4rem + 8.4vw, 9.5rem);

	--lh-display: 0.92;
	--lh-heading: 1.04;
	--lh-body: 1.62;

	--tr-display: -0.028em;
	--tr-heading: -0.018em;
	--tr-label: 0.15em;

	/* Space. A 4px base, with section rhythm on its own fluid step. */
	--s-1: 0.25rem;
	--s-2: 0.5rem;
	--s-3: 0.75rem;
	--s-4: 1rem;
	--s-5: 1.5rem;
	--s-6: 2rem;
	--s-7: 2.5rem;
	--s-8: 3rem;
	--s-9: 4rem;
	--s-10: 5rem;
	--s-11: 6.5rem;
	--s-12: 8rem;

	--section-y: clamp(4rem, 2.4rem + 6vw, 8.5rem);
	--section-y-tight: clamp(2.75rem, 1.9rem + 3.4vw, 5rem);

	/* Grid. Two alignment points do most of the work: column 1 and column 6. */
	--page-max: 1560px;
	--read-max: 72ch;
	--gutter: clamp(1.25rem, 0.72rem + 2.2vw, 2.75rem);
	--gap: clamp(1rem, 0.63rem + 1.5vw, 1.75rem);

	/* Structure. Almost everything is square: a surface has an edge. */
	--radius: 0px;
	--radius-soft: 2px;
	--border: 1px;

	/* Motion. Quick, controlled, and confined to transform, opacity and colour. */
	--ease: cubic-bezier(0.22, 1, 0.36, 1);
	--t-fast: 160ms;
	--t: 220ms;
	--t-slow: 620ms;

	/* Fixed header height, read by scroll padding and the mobile panel. */
	--nav-h: 4.5rem;

	/* The x position of the grid's first column, measured from the viewport
	   edge. Lets a full bleed element line its content up with the grid. */
	--page-edge: max(var(--gutter), calc((100vw - var(--page-max)) / 2));
}

@media (min-width: 1024px) {
	:root {
		--nav-h: 5.25rem;
	}
}

/* Dark surfaces simply reassign the contextual tokens. Every component below
   is written against those tokens, so nothing needs a dark variant. */
.is-dark {
	--ground: var(--c-black);
	--ink: var(--c-paper);
	--ink-soft: var(--c-ash);
	--hair: var(--rule-dark);
	--hair-strong: var(--rule-dark-strong);
	background: var(--ground);
	color: var(--ink);
}

.is-grey {
	--ground: var(--c-grey);
	background: var(--ground);
}

/* --------------------------------------------------------------------------
   02  Reset and base
   -------------------------------------------------------------------------- */

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

html {
	-webkit-text-size-adjust: 100%;
	scroll-behavior: smooth;
	scroll-padding-top: calc(var(--nav-h) + 1rem);
}

body {
	margin: 0;
	background: var(--c-paper);
	color: var(--c-ink);
	font-family: var(--font);
	font-size: var(--fs-body);
	font-weight: 400;
	line-height: var(--lh-body);
	font-synthesis-weight: none;
	-webkit-font-smoothing: antialiased;
	-moz-osx-font-smoothing: grayscale;
	text-rendering: optimizeLegibility;
	overflow-x: hidden;
}

img,
picture,
video,
svg {
	max-width: 100%;
	display: block;
}

img {
	height: auto;
}

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

h1, h2, h3, h4, h5, h6, p, figure, blockquote, dl, dd, ul, ol {
	margin: 0;
}

ul, ol {
	padding: 0;
	list-style: none;
}

hr {
	border: 0;
	border-top: var(--border) solid var(--hair);
	margin: var(--s-6) 0;
}

::selection {
	background: var(--c-yellow);
	color: var(--c-ink);
}

:where(a, button, input, select, textarea, summary, [tabindex]):focus-visible {
	outline: 2px solid var(--c-ink);
	outline-offset: 3px;
	border-radius: var(--radius-soft);
}

.is-dark :where(a, button, input, select, textarea, summary, [tabindex]):focus-visible {
	outline-color: var(--c-yellow);
}

.skip-link {
	position: absolute;
	left: var(--gutter);
	top: 0;
	z-index: 200;
	transform: translateY(-120%);
	background: var(--c-black);
	color: var(--c-paper);
	padding: 0.85rem 1.25rem;
	font-size: var(--fs-small);
	font-weight: 600;
	text-decoration: none;
	transition: transform var(--t) var(--ease);
}

.skip-link:focus {
	transform: translateY(0);
}

.screen-reader-text {
	position: absolute !important;
	width: 1px;
	height: 1px;
	padding: 0;
	margin: -1px;
	overflow: hidden;
	clip: rect(0, 0, 0, 0);
	white-space: nowrap;
	border: 0;
}

/* --------------------------------------------------------------------------
   03  Typography
   -------------------------------------------------------------------------- */

.display,
.h1,
.h2,
.h3,
.h4 {
	font-weight: 600;
	line-height: var(--lh-heading);
	letter-spacing: var(--tr-heading);
	text-wrap: balance;
	margin: 0;
	/* Explicit, because theme.json emits a bare `h2 { color }` element rule for
	   the editor and a heading on a dark band would otherwise inherit it. */
	color: var(--ink);
}

.display {
	font-size: var(--fs-display);
	font-weight: 650;
	line-height: var(--lh-display);
	letter-spacing: var(--tr-display);
}

.h1 { font-size: var(--fs-h1); font-weight: 640; line-height: 0.98; letter-spacing: var(--tr-display); }
.h2 { font-size: var(--fs-h2); }
.h3 { font-size: var(--fs-h3); font-weight: 620; }
.h4 { font-size: var(--fs-h4); font-weight: 600; letter-spacing: -0.008em; }

/* Small uppercase labels are the only capitals on the site, and they exist to
   signpost structure, never to carry copy. */
.label {
	display: block;
	font-size: var(--fs-label);
	font-weight: 550;
	line-height: 1.3;
	letter-spacing: var(--tr-label);
	text-transform: uppercase;
	color: var(--ink-soft);
	margin: 0;
}

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

.lede {
	font-size: var(--fs-lede);
	line-height: 1.44;
	font-weight: 380;
	letter-spacing: -0.012em;
	color: var(--ink);
	max-width: 30ch;
	text-wrap: pretty;
}

.lede--wide { max-width: 44ch; }

.prose {
	max-width: var(--read-max);
	color: var(--ink-soft);
	text-wrap: pretty;
}

.prose > * + * { margin-top: 1.1em; }
.prose strong { color: var(--ink); font-weight: 600; }

.text-soft { color: var(--ink-soft); }
.text-small { font-size: var(--fs-small); }

/* The measure control that keeps headlines composed rather than merely large. */
.measure-xs { max-width: 16ch; }
.measure-s  { max-width: 22ch; }
.measure-m  { max-width: 30ch; }
.measure-l  { max-width: 42ch; }

/* --------------------------------------------------------------------------
   04  Layout and grid
   -------------------------------------------------------------------------- */

.shell {
	width: min(100% - (var(--gutter) * 2), var(--page-max));
	margin-inline: auto;
}

.shell--narrow { width: min(100% - (var(--gutter) * 2), 1180px); }

/* Mobile is four columns, tablet eight, desktop twelve. Children position
   themselves with --m, --t and --d so a layout is described in one place. */
.grid {
	display: grid;
	grid-template-columns: repeat(4, minmax(0, 1fr));
	gap: var(--gap);
}

.grid > * { grid-column: var(--m, span 4); }

@media (min-width: 680px) {
	.grid { grid-template-columns: repeat(8, minmax(0, 1fr)); }
	.grid > * { grid-column: var(--t, span 8); }
}

@media (min-width: 1024px) {
	.grid { grid-template-columns: repeat(12, minmax(0, 1fr)); }
	.grid > * { grid-column: var(--d, span 12); }
}

.grid--rows { row-gap: var(--s-8); }

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

.section--tight { padding-block: var(--section-y-tight); }
.section--flush-top { padding-top: 0; }
.section--flush-bottom { padding-bottom: 0; }

.stack > * + * { margin-top: var(--flow, var(--s-4)); }
.stack-5 > * + * { margin-top: var(--s-5); }
.stack-6 > * + * { margin-top: var(--s-6); }
.stack-8 > * + * { margin-top: var(--s-8); }

.cluster {
	display: flex;
	flex-wrap: wrap;
	gap: var(--s-3) var(--s-4);
	align-items: center;
}

/* --------------------------------------------------------------------------
   05  Rules and markings

   Yellow is a marking, not a decoration. It appears on things that do
   something: a primary action, an active state, a hover, a service number, a
   highlighted word, a list marker. It is never a short line floating on its
   own in space.

   A "set-out" device used to live here: a hairline across every section
   boundary carrying a yellow segment that shifted position down the page. In
   use those segments read as stray fragments rather than as branding, so they
   are gone, along with the vertical kerb line in the hero. The rules that
   remain separate real things.
   -------------------------------------------------------------------------- */

/* A plain full width rule between two sections of the same tone. Structural
   only: use it where two sections genuinely need separating, not for effect. */
.rule {
	height: 1px;
	background: var(--hair);
	border: 0;
	margin: 0;
}

/* --------------------------------------------------------------------------
   06  Buttons and links
   -------------------------------------------------------------------------- */

.btn {
	display: inline-flex;
	align-items: center;
	gap: 0.75rem;
	padding: 1rem 1.6rem;
	min-height: 3.25rem;
	border: var(--border) solid transparent;
	border-radius: var(--radius);
	font-size: var(--fs-small);
	font-weight: 600;
	letter-spacing: 0.005em;
	line-height: 1.2;
	text-decoration: none;
	cursor: pointer;
	background: none;
	transition: background-color var(--t) var(--ease),
		color var(--t) var(--ease),
		border-color var(--t) var(--ease);
}

/* Every arrow on the site is the same inline SVG at the same size. */
.arrow {
	flex: none;
	width: 1rem;
	height: 0.625rem;
	transition: transform var(--t) var(--ease);
}

.btn:hover .arrow,
.btn:focus-visible .arrow,
.link-out:hover .arrow,
.link-out:focus-visible .arrow {
	transform: translateX(4px);
}

/* Primary. Painted yellow, flipping to ink on hover: the same two colours,
   swapped, which is a more confident move than a tint. */
.btn--primary {
	background: var(--c-yellow);
	color: var(--c-ink);
	border-color: var(--c-yellow);
}

.btn--primary:hover,
.btn--primary:focus-visible {
	background: var(--c-ink);
	color: var(--c-yellow);
	border-color: var(--c-ink);
}

.is-dark .btn--primary:hover,
.is-dark .btn--primary:focus-visible {
	background: var(--c-paper);
	color: var(--c-ink);
	border-color: var(--c-paper);
}

/* Secondary. Outlined, filling on hover. */
.btn--line {
	border-color: var(--hair-strong);
	color: var(--ink);
}

.btn--line:hover,
.btn--line:focus-visible {
	background: var(--ink);
	color: var(--ground);
	border-color: var(--ink);
}

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

.btn--solid:hover,
.btn--solid:focus-visible {
	background: var(--c-charcoal);
	color: var(--c-yellow);
	border-color: var(--c-charcoal);
}

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

/* The telephone action, which needs to read as a number rather than a label. */
.tel-action {
	display: inline-flex;
	align-items: baseline;
	gap: 0.55rem;
	text-decoration: none;
	color: inherit;
	font-weight: 600;
	letter-spacing: -0.01em;
}

.tel-action span:first-child {
	font-size: var(--fs-label);
	font-weight: 550;
	letter-spacing: var(--tr-label);
	text-transform: uppercase;
	color: var(--ink-soft);
}

.tel-action:hover span:last-child,
.tel-action:focus-visible span:last-child {
	box-shadow: inset 0 -2px 0 var(--c-yellow);
}

/* Inline links. A permanent faint rule so the link is visible without hover,
   with a yellow rule that draws across it on hover. */
.link {
	color: inherit;
	text-decoration: none;
	background-image: linear-gradient(var(--c-yellow), var(--c-yellow));
	background-repeat: no-repeat;
	background-position: 0 100%;
	background-size: 0% 2px;
	box-shadow: inset 0 -1px 0 var(--hair-strong);
	transition: background-size var(--t) var(--ease);
	padding-bottom: 1px;
}

.link:hover,
.link:focus-visible {
	background-size: 100% 2px;
}

/* An arrowed standalone link used to leave a section. */
.link-out {
	display: inline-flex;
	align-items: center;
	gap: 0.6rem;
	font-weight: 600;
	font-size: var(--fs-small);
	color: var(--ink);
	text-decoration: none;
	padding-bottom: 0.4rem;
	border-bottom: 2px solid var(--hair-strong);
	transition: border-color var(--t) var(--ease), gap var(--t) var(--ease);
}

.link-out:hover,
.link-out:focus-visible {
	border-color: var(--c-yellow);
	gap: 0.9rem;
}

/* --------------------------------------------------------------------------
   07  Header and navigation
   -------------------------------------------------------------------------- */

.contact-band {
	background: var(--c-black);
	color: var(--c-ash);
	font-size: var(--fs-label);
	letter-spacing: 0.01em;
}

.contact-band__inner {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: var(--s-5);
	min-height: 2.5rem;
	padding-block: 0.45rem;
}

.contact-band__coverage {
	display: none;
	color: var(--c-ash);
}

.contact-band__links {
	display: flex;
	align-items: center;
	gap: var(--s-5);
	margin-left: auto;
}

.contact-band a {
	color: var(--c-paper);
	text-decoration: none;
	font-weight: 500;
	white-space: nowrap;
	transition: color var(--t) var(--ease);
}

.contact-band a:hover,
.contact-band a:focus-visible { color: var(--c-yellow); }

.contact-band__label {
	color: var(--c-ash);
	font-weight: 400;
	letter-spacing: var(--tr-label);
	text-transform: uppercase;
	margin-right: 0.4rem;
}

.contact-band__email { display: none; }

@media (min-width: 900px) {
	.contact-band__coverage { display: block; }
	.contact-band__email { display: inline; }
}

.site-header {
	position: sticky;
	top: 0;
	z-index: 90;
	background: var(--c-paper);
	border-bottom: var(--border) solid var(--rule);
}

.site-header__inner {
	display: flex;
	align-items: center;
	gap: var(--s-5);
	min-height: var(--nav-h);
}

/* Wordmark. The company name, then a yellow mark and the trade, which is the
   brand device appearing at its smallest scale. */
.wordmark {
	display: block;
	text-decoration: none;
	color: var(--c-ink);
	flex: none;
	margin-right: auto;
}

.wordmark__name {
	display: block;
	font-size: clamp(1rem, 0.86rem + 0.62vw, 1.3rem);
	font-weight: 680;
	letter-spacing: -0.015em;
	line-height: 1.05;
	white-space: nowrap;
}

.wordmark__trade {
	display: flex;
	align-items: center;
	gap: 0.45rem;
	font-size: 0.6875rem;
	font-weight: 550;
	letter-spacing: 0.14em;
	text-transform: uppercase;
	color: var(--c-ink-soft);
	margin-top: 0.28rem;
	white-space: nowrap;
}

.wordmark__trade::before {
	content: "";
	width: 1.15rem;
	height: 3px;
	background: var(--c-yellow);
	flex: none;
	transition: width var(--t) var(--ease);
}

.wordmark:hover .wordmark__trade::before,
.wordmark:focus-visible .wordmark__trade::before {
	width: 2rem;
}

@media (max-width: 400px) {
	.wordmark__trade { font-size: 0.625rem; letter-spacing: 0.1em; }
}

/* An uploaded logo, in place of the wordmark.

   The height is a definite value, not `auto`, and this matters. An SVG exported
   with only a viewBox has an intrinsic ratio but no intrinsic size. Inside a
   shrink to fit parent, `width: auto` with `height: auto` is then circular: the
   image wants to be as wide as its parent, the parent wants to be as wide as
   its contents, and CSS resolves that stand off at zero. A PNG has real pixel
   dimensions so it never hits this; an SVG logo does, and vanishes.

   Fixing the height and letting the width follow the ratio removes the
   circularity. The width cap is a backstop for an unusually wide lockup, and
   object-fit keeps it undistorted if it ever bites. */
.wordmark__image {
	display: block;
	width: auto;
	height: 1.75rem;
	max-width: min(14rem, 44vw);
	object-fit: contain;
	object-position: left center;
}

@media (min-width: 1024px) {
	.wordmark__image { height: 1.875rem; max-width: 17rem; }
}

@media (min-width: 1200px) {
	.wordmark__image { height: 2rem; max-width: 20rem; }
}

.nav-panel__head .wordmark__image { height: 1.75rem; }

/* A little larger in the footer, where it is the identity rather than one item
   in a bar competing with the navigation. */
.wordmark--footer .wordmark__image {
	height: 2.25rem;
	max-width: 22rem;
}

/* Desktop navigation */
.nav-primary { display: none; }

@media (min-width: 1024px) {
	/* The navigation fills the header's full height, so each top level item is
	   a full height column. That is what lets a dropdown open flush against the
	   bottom of the bar, and it also means there is no dead gap between a link
	   and its own dropdown for the pointer to fall through on the way down. */
	.nav-primary {
		display: block;
		align-self: stretch;
	}

	.nav-primary ul {
		display: flex;
		align-items: center;
		gap: clamp(1.1rem, 1.9vw, 2.2rem);
	}

	.nav-primary > ul {
		height: 100%;
		align-items: stretch;
	}

	.nav-primary > ul > li {
		display: flex;
		align-items: center;
	}

	.nav-primary a {
		position: relative;
		display: inline-flex;
		align-items: center;
		padding: 0.4rem 0;
		font-size: var(--fs-small);
		font-weight: 520;
		letter-spacing: -0.005em;
		color: var(--c-ink);
		text-decoration: none;
		white-space: nowrap;
	}

	.nav-primary a::after {
		content: "";
		position: absolute;
		left: 0;
		bottom: -0.3rem;
		width: 100%;
		height: 3px;
		background: var(--c-yellow);
		transform: scaleX(0);
		transform-origin: left center;
		transition: transform var(--t) var(--ease);
	}

	.nav-primary a:hover::after,
	.nav-primary a:focus-visible::after,
	.nav-primary .current-menu-item > a::after,
	.nav-primary .current-menu-ancestor > a::after,
	.nav-primary .current-page-ancestor > a::after {
		transform: scaleX(1);
	}

	.nav-primary .current-menu-item > a,
	.nav-primary .current-menu-ancestor > a,
	.nav-primary .current-page-ancestor > a {
		font-weight: 620;
	}

	/* The chevron marks the items that have a dropdown behind them. It turns
	   over when the dropdown is open, so the arrow describes the state rather
	   than just decorating the label. */
	.nav-chevron {
		flex: none;
		width: 0.625rem;
		height: 0.375rem;
		margin-left: 0.4rem;
		transition: transform var(--t) var(--ease);
	}

	.nav-primary > ul > li:hover > a .nav-chevron,
	.nav-primary > ul > li:focus-within > a .nav-chevron {
		transform: rotate(180deg);
	}

	/* Sub navigation for the services list. */
	.nav-primary li { position: relative; }

	/* Flush to the bottom of the bar. The list item runs the full height of the
	   header, so 100% lands exactly on the header's bottom rule. */
	.nav-primary .sub-menu {
		position: absolute;
		top: 100%;
		left: -1.25rem;
		min-width: 15rem;
		display: block;
		flex-direction: column;
		gap: 0;
		background: var(--c-black);
		padding: 0.6rem 0;
		border-top: 3px solid var(--c-yellow);
		opacity: 0;
		visibility: hidden;
		transform: translateY(-6px);
		transition: opacity var(--t) var(--ease), transform var(--t) var(--ease), visibility var(--t);
		z-index: 5;
	}

	.nav-primary li:hover > .sub-menu,
	.nav-primary li:focus-within > .sub-menu {
		opacity: 1;
		visibility: visible;
		transform: translateY(0);
	}

	/* Full width of the dropdown, not just the width of the words. The top
	   level links are inline-flex so they shrink to their label and sit in a
	   row; these are in a stacked list, so they fill it and the whole row is
	   the target. */
	.nav-primary .sub-menu a {
		display: flex;
		align-items: center;
		padding: 0.6rem 1.25rem;
		color: var(--c-paper);
		font-weight: 450;
	}

	.nav-primary .sub-menu a::after { display: none; }

	.nav-primary .sub-menu a:hover,
	.nav-primary .sub-menu a:focus-visible {
		color: var(--c-yellow);
		background: var(--c-charcoal);
	}
}

.header-actions {
	display: flex;
	align-items: center;
	gap: var(--s-3);
	flex: none;
}

.header-actions .btn { padding: 0.8rem 1.2rem; min-height: 2.9rem; }

.header-actions .btn--quote { display: none; }

@media (min-width: 1024px) {
	.header-actions .btn--quote { display: inline-flex; }
}

/* Mobile menu button */
.nav-toggle {
	display: inline-flex;
	align-items: center;
	gap: 0.6rem;
	border: var(--border) solid var(--rule-strong);
	background: none;
	padding: 0.75rem 1rem;
	min-height: 2.9rem;
	cursor: pointer;
	font-size: var(--fs-label);
	font-weight: 600;
	letter-spacing: var(--tr-label);
	text-transform: uppercase;
	color: var(--c-ink);
	transition: background-color var(--t) var(--ease), color var(--t) var(--ease);
}

.nav-toggle:hover { background: var(--c-ink); color: var(--c-paper); }

@media (min-width: 1024px) {
	.nav-toggle { display: none; }
}

.nav-toggle__bars {
	display: block;
	position: relative;
	width: 1.1rem;
	height: 2px;
	background: currentColor;
}

.nav-toggle__bars::before,
.nav-toggle__bars::after {
	content: "";
	position: absolute;
	left: 0;
	width: 100%;
	height: 2px;
	background: currentColor;
}

.nav-toggle__bars::before { top: -6px; }
.nav-toggle__bars::after { top: 6px; }

/* Mobile panel */
/* The panel is a dark surface, so it takes the dark token set. Without this
   the secondary text colour stays at its pale-ground value and fails AA. */
.nav-panel {
	position: fixed;
	inset: 0;
	z-index: 100;
	/* Locked to the device viewport rather than the document. dvh follows the
	   browser chrome as it retracts, so the foot is never left under the
	   address bar on a phone; the vh line above it covers anything older. */
	height: 100vh;
	height: 100dvh;
	--ground: var(--c-black);
	--ink: var(--c-paper);
	--ink-soft: var(--c-ash);
	--hair: var(--rule-dark);
	--hair-strong: var(--rule-dark-strong);
	background: var(--c-black);
	color: var(--c-paper);
	display: flex;
	flex-direction: column;
	/* Only the menu itself scrolls. The head and the foot are pinned. */
	overflow: hidden;
	overscroll-behavior: contain;
	opacity: 0;
	visibility: hidden;
	transition: opacity var(--t) var(--ease), visibility var(--t);
}

.nav-panel.is-open {
	opacity: 1;
	visibility: visible;
}

.nav-panel__head {
	flex: 0 0 auto;
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: var(--s-4);
	min-height: var(--nav-h);
	padding-top: env(safe-area-inset-top, 0px);
	border-bottom: var(--border) solid var(--rule-dark);
}

.nav-panel .wordmark { color: var(--c-paper); }
.nav-panel .wordmark__trade { color: var(--c-ash); }

.nav-close {
	border: var(--border) solid var(--rule-dark-strong);
	background: none;
	color: var(--c-paper);
	padding: 0.75rem 1rem;
	min-height: 2.9rem;
	font-size: var(--fs-label);
	font-weight: 600;
	letter-spacing: var(--tr-label);
	text-transform: uppercase;
	cursor: pointer;
}

.nav-close:hover { background: var(--c-charcoal); color: var(--c-yellow); }

.nav-panel__body {
	flex: 1 1 auto;
	/* Without this a flex child refuses to shrink below its content, and a
	   long menu would push the foot off the bottom of the screen. */
	min-height: 0;
	overflow-y: auto;
	overscroll-behavior: contain;
	-webkit-overflow-scrolling: touch;
	padding-block: var(--s-5) var(--s-6);
}

.nav-panel ul { display: block; }

.nav-panel > .shell > ul > li,
.nav-panel__body li {
	border-bottom: var(--border) solid var(--rule-dark);
}

.nav-panel__body > .shell > ul > li > a,
.nav-panel__body a {
	display: flex;
	align-items: baseline;
	gap: 1rem;
	padding: 0.9rem 0;
	color: var(--c-paper);
	text-decoration: none;
	/* Held a step below the old size: the panel is now bound by the height of
	   the screen, and the type has to leave room for the actions at the foot. */
	font-size: clamp(1.25rem, 1rem + 1.2vw, 1.75rem);
	font-weight: 600;
	letter-spacing: -0.02em;
	line-height: 1.1;
	transition: color var(--t) var(--ease), padding-left var(--t) var(--ease);
}

.nav-panel__body a:hover,
.nav-panel__body a:focus-visible {
	color: var(--c-yellow);
	padding-left: 0.5rem;
}

/* A parent item is two targets on one line: the section's own page, and the
   arrow that opens the list beneath it. The arrow follows the word rather than
   being pinned to the far edge, so it reads as belonging to that item. */
.nav-panel__row {
	display: flex;
	align-items: center;
}

/* Shrink to the words, so the arrow sits directly after them. */
.nav-panel__row > a { flex: 0 1 auto; min-width: 0; }

.nav-sub-toggle {
	flex: 0 0 auto;
	display: flex;
	align-items: center;
	justify-content: center;
	/* No border and no ground: the arrow is the control. The padding is the
	   tap target, which stays a comfortable size without being drawn. */
	border: 0;
	background: none;
	padding: 0.9rem 0.75rem;
	min-height: 2.75rem;
	color: var(--c-paper);
	cursor: pointer;
	transition: color var(--t) var(--ease);
}

.nav-sub-toggle:hover,
.nav-sub-toggle:focus-visible { color: var(--c-yellow); }

.nav-sub-toggle:focus-visible {
	outline: 2px solid var(--c-yellow);
	outline-offset: -2px;
}

.nav-sub-toggle .nav-chevron {
	display: block;
	width: 0.85rem;
	height: 0.55rem;
	transition: transform var(--t) var(--ease);
}

.nav-sub-toggle[aria-expanded="true"] .nav-chevron { transform: rotate(180deg); }

/* The collapse itself. A grid row animated from 0fr to 1fr gives a real height
   transition without measuring anything in script, and visibility keeps the
   hidden links out of the tab order while it is shut. With JavaScript off none
   of this applies and every child link is simply listed. */
.js .nav-sub {
	display: grid;
	grid-template-rows: 0fr;
	transition: grid-template-rows var(--t) var(--ease);
}

.js .nav-sub > * {
	overflow: hidden;
	min-height: 0;
	visibility: hidden;
	transition: visibility var(--t);
}

.js .nav-sub.is-open { grid-template-rows: 1fr; }
.js .nav-sub.is-open > * { visibility: visible; }

@media (prefers-reduced-motion: reduce) {
	.js .nav-sub { transition: none; }
}

/* The breathing room after the last child sits on that child, not on the list.
   Padding belongs to the list's own border box, so a collapsed list could not
   shrink below it and left 0.75rem of dead space under Services and Areas We
   Cover while they were shut. */
.nav-panel__body .sub-menu { padding-bottom: 0; }

.nav-panel__body .sub-menu > li:last-child { padding-bottom: 0.75rem; }

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

.nav-panel__body .sub-menu a {
	font-size: var(--fs-body);
	font-weight: 420;
	color: var(--c-ash);
	padding: 0.5rem 0 0.5rem 1.5rem;
	position: relative;
}

.nav-panel__body .sub-menu a::before {
	content: "";
	position: absolute;
	left: 0;
	top: 1.1em;
	width: 0.8rem;
	height: 2px;
	background: var(--rule-dark-strong);
	transition: background-color var(--t) var(--ease), width var(--t) var(--ease);
}

.nav-panel__body .sub-menu a:hover::before,
.nav-panel__body .sub-menu a:focus-visible::before {
	background: var(--c-yellow);
	width: 1.1rem;
}

/* Items enter in sequence when the panel opens. */
.js .nav-panel__body > .shell > ul > li {
	opacity: 0;
	transform: translateY(8px);
}

.js .nav-panel.is-open .nav-panel__body > .shell > ul > li {
	opacity: 1;
	transform: none;
	transition: opacity 280ms var(--ease), transform 280ms var(--ease);
	transition-delay: calc(var(--i, 0) * 34ms + 60ms);
}

.nav-panel__body > .shell > ul > li:nth-child(1) { --i: 0; }
.nav-panel__body > .shell > ul > li:nth-child(2) { --i: 1; }
.nav-panel__body > .shell > ul > li:nth-child(3) { --i: 2; }
.nav-panel__body > .shell > ul > li:nth-child(4) { --i: 3; }
.nav-panel__body > .shell > ul > li:nth-child(5) { --i: 4; }
.nav-panel__body > .shell > ul > li:nth-child(6) { --i: 5; }
.nav-panel__body > .shell > ul > li:nth-child(7) { --i: 6; }
.nav-panel__body > .shell > ul > li:nth-child(8) { --i: 7; }

.nav-panel__foot {
	flex: 0 0 auto;
	border-top: var(--border) solid var(--rule-dark);
	padding-top: var(--s-5);
	/* Clears the home indicator on a phone without leaving a gap on anything
	   that does not have one. */
	padding-bottom: calc(var(--s-5) + env(safe-area-inset-bottom, 0px));
	background: var(--c-charcoal);
}

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

.nav-panel__foot-inner {
	display: grid;
	gap: var(--s-4);
}

/* Office and mobile as two equal columns, each label sat over its number.
   Previously these were two inline-flex links in a stack, which put them on
   one line with only a word space between them and no reliable gap at all. */
.nav-panel__tels {
	display: grid;
	grid-template-columns: repeat(2, minmax(0, 1fr));
	gap: var(--s-3) var(--s-5);
}

.tel-action--stacked {
	display: flex;
	flex-direction: column;
	/* Centred on both axes: the label over its number, and the pair centred in
	   its half of the row. Grid stretches each one across its column already,
	   so centring here centres it against the column and not just the words. */
	align-items: center;
	justify-content: center;
	text-align: center;
	gap: 0.15rem;
	min-width: 0;
}

.tel-action--stacked span:last-child {
	font-size: 1.0625rem;
	letter-spacing: -0.015em;
	white-space: nowrap;
}

/* One below the other where two columns would squeeze the numbers. */
@media (max-width: 359px) {
	.nav-panel__tels { grid-template-columns: minmax(0, 1fr); }
}

body.nav-open { overflow: hidden; }

/* --------------------------------------------------------------------------
   08  Hero

   A pale content column and a photograph, split evenly. The headline is fully
   contained in the pale column: nothing crosses the seam, because text half on
   paper and half on a photograph reads as an accident rather than a decision.

   What stops an even split reading as a default is everything else: the content
   sitting on the page grid rather than centred in its own half, the photograph
   bleeding off the right edge of the page, a measured headline, and the black
   band of facts that the two columns run straight into at the bottom.
   -------------------------------------------------------------------------- */

.hero {
	position: relative;
	background: var(--c-paper);
	isolation: isolate;
}

.hero__media {
	position: relative;
	width: 100%;
	aspect-ratio: 4 / 3;
	background: var(--c-charcoal);
	overflow: hidden;
}

/* Taken out of flow so the photograph contributes no height of its own. The
   frame is sized by this stylesheet and the picture is cropped into it, which
   is what keeps the hero the same shape whatever the owner uploads: a tall
   portrait shot and a wide panorama both land in exactly this box. */
.hero__media img {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	object-fit: cover;
}

.hero__inner {
	position: relative;
	width: min(100% - (var(--gutter) * 2), var(--page-max));
	margin-inline: auto;
	padding-block: var(--s-7) var(--s-8);
}

.hero__title {
	font-size: clamp(2.375rem, 0.95rem + 4.2vw, 3.5rem);
	font-weight: 650;
	line-height: 0.98;
	letter-spacing: var(--tr-display);
	margin: 0;
}

.hero__line { display: block; }

/* The painted line. Yellow ground, ink text at 11.7:1. It is sized to its own
   words, so it reads as a marking painted over that phrase rather than as a
   block of colour filling the column. */
.hero__line--paint {
	display: block;
	width: -moz-fit-content;
	width: fit-content;
	max-width: 100%;
	background: var(--c-yellow);
	color: var(--c-ink);
	padding: 0.08em 0.32em 0.14em 0.26em;
	margin-top: 0.1em;
	margin-left: -0.26em;
}

.hero__lede {
	margin-top: var(--s-5);
	max-width: 36ch;
}

.hero__actions {
	margin-top: var(--s-6);
	display: flex;
	flex-wrap: wrap;
	gap: var(--s-3);
}

.hero__actions .btn { flex: 1 1 auto; justify-content: center; }

@media (min-width: 520px) {
	.hero__actions .btn { flex: 0 0 auto; }
}

@media (min-width: 1024px) {
	.hero {
		display: grid;
		grid-template-columns: 1fr 1fr;
		min-height: clamp(36rem, calc(100vh - var(--nav-h)), 50rem);
	}

	/* Both explicitly in row one. The photograph comes first in the markup, so
	   without this the auto-placement cursor has already passed column one by
	   the time it reaches the text and drops it into a second row. */
	.hero__inner {
		grid-column: 1;
		grid-row: 1;
		display: flex;
		flex-direction: column;
		justify-content: center;
		width: auto;
		margin: 0;
		padding: var(--s-9) var(--s-7) var(--s-9) var(--page-edge);
	}

	/* The photograph fills its column and bleeds off the right edge. The row is
	   sized by the hero's own min-height and the text beside it, never by the
	   image, which is stretched to whatever that comes out at. */
	.hero__media {
		grid-column: 2;
		grid-row: 1;
		aspect-ratio: auto;
		/* Stretched to the row rather than sized at 100%, which a grid item can
		   only resolve once the row is definite. */
		height: auto;
		align-self: stretch;
	}

	.hero__title {
		font-size: clamp(2.625rem, 0.2rem + 3.4vw, 3.875rem);
	}

	.hero__lede { max-width: 38ch; }
}

@media (min-width: 1400px) {
	.hero__title { font-size: 4rem; }
}

/* The dark band immediately beneath the hero, joined to it with no gap so the
   photograph runs straight into a black surface. */
.hero-facts {
	background: var(--c-black);
	color: var(--c-paper);
	--ground: var(--c-black);
	--ink: var(--c-paper);
	--ink-soft: var(--c-ash);
	--hair: var(--rule-dark);
	--hair-strong: var(--rule-dark-strong);
}

.hero-facts__grid {
	display: grid;
	grid-template-columns: 1fr;
}

.hero-facts__item {
	padding: var(--s-5) 0;
	border-top: var(--border) solid var(--rule-dark);
}

.hero-facts__item:first-child { border-top: 0; }

.hero-facts__item dt {
	font-size: var(--fs-label);
	font-weight: 550;
	letter-spacing: var(--tr-label);
	text-transform: uppercase;
	color: var(--c-ash);
	margin-bottom: 0.5rem;
}

.hero-facts__item dd {
	margin: 0;
	font-size: var(--fs-h4);
	font-weight: 480;
	line-height: 1.3;
	letter-spacing: -0.012em;
	color: var(--c-paper);
	max-width: 26ch;
}

@media (min-width: 680px) {
	.hero-facts__grid {
		grid-template-columns: repeat(2, minmax(0, 1fr));
		column-gap: var(--gap);
	}

	.hero-facts__item:nth-child(2) { border-top: 0; }
}

@media (min-width: 1024px) {
	.hero-facts__grid {
		grid-template-columns: repeat(4, minmax(0, 1fr));
	}

	.hero-facts__item {
		border-top: 3px solid var(--rule-dark);
		padding: var(--s-6) var(--s-5) var(--s-7) 0;
	}

	.hero-facts__item:first-child,
	.hero-facts__item:nth-child(2) { border-top: 3px solid var(--rule-dark); }
}

/* --------------------------------------------------------------------------
   09  Job sheet and fact rows
   Factual information set the way a contractor writes down a job.
   -------------------------------------------------------------------------- */

.sheet {
	border-top: var(--border) solid var(--hair-strong);
}

.sheet__row {
	display: grid;
	grid-template-columns: 1fr;
	gap: 0.35rem var(--gap);
	padding: var(--s-4) 0;
	border-bottom: var(--border) solid var(--hair);
	position: relative;
}

.sheet__row::before {
	content: "";
	position: absolute;
	left: 0;
	bottom: -1px;
	width: 0;
	height: 1px;
	background: var(--c-yellow);
	transition: width var(--t-slow) var(--ease);
}

.sheet__row:hover::before { width: 100%; }

.sheet__key {
	font-size: var(--fs-label);
	font-weight: 550;
	letter-spacing: var(--tr-label);
	text-transform: uppercase;
	color: var(--ink-soft);
}

.sheet__value {
	margin: 0;
	color: var(--ink);
	max-width: 52ch;
}

@media (min-width: 680px) {
	.sheet__row {
		grid-template-columns: minmax(9rem, 22%) 1fr;
		align-items: baseline;
	}
}

/* An unordered list of short factual points, ruled rather than bulleted. */
.ruled-list {
	border-top: var(--border) solid var(--hair);
}

.ruled-list > li {
	padding: var(--s-3) 0 var(--s-3) 1.6rem;
	border-bottom: var(--border) solid var(--hair);
	position: relative;
	color: var(--ink-soft);
}

.ruled-list > li::before {
	content: "";
	position: absolute;
	left: 0;
	top: 1.45em;
	width: 0.75rem;
	height: 2px;
	background: var(--c-yellow);
}

/* Numbered steps for a process. The number is a graphic element, the text is
   the content: they are deliberately different weights and sizes. */
.steps {
	counter-reset: step;
	border-top: var(--border) solid var(--hair-strong);
}

.steps > li {
	counter-increment: step;
	display: grid;
	grid-template-columns: 3.25rem 1fr;
	gap: 0 var(--s-4);
	padding: var(--s-5) 0;
	border-bottom: var(--border) solid var(--hair);
}

.steps > li::before {
	content: counter(step, decimal-leading-zero);
	font-size: var(--fs-small);
	font-weight: 600;
	letter-spacing: 0.06em;
	color: var(--ink-soft);
	padding-top: 0.28em;
}

.steps__title {
	font-size: var(--fs-h4);
	font-weight: 600;
	letter-spacing: -0.012em;
	color: var(--ink);
	margin-bottom: 0.3rem;
}

.steps__text {
	color: var(--ink-soft);
	max-width: 62ch;
}

@media (min-width: 680px) {
	.steps > li { grid-template-columns: 4.5rem 1fr; }
}

/* Applications: where the work is done. A ruled two column list, never cards. */
.applications {
	border-top: var(--border) solid var(--hair-strong);
	display: grid;
	grid-template-columns: 1fr;
}

.applications__item {
	padding: var(--s-5) 0;
	border-bottom: var(--border) solid var(--hair);
}

.applications__title {
	font-size: var(--fs-h4);
	font-weight: 600;
	letter-spacing: -0.012em;
	margin-bottom: 0.35rem;
	color: var(--ink);
	display: flex;
	align-items: baseline;
	gap: 0.65rem;
}

.applications__title::before {
	content: "";
	width: 0.85rem;
	height: 3px;
	background: var(--c-yellow);
	flex: none;
	transform: translateY(-0.28em);
}

.applications__text {
	color: var(--ink-soft);
	max-width: 62ch;
}

/* Two columns only when the container is genuinely wide enough for them. A
   media query cannot know this: the same component sits in a full width
   section on a service page and in half a grid on the homepage. */
.applications-wrap { container-type: inline-size; }

@container (min-width: 40rem) {
	.applications {
		grid-template-columns: repeat(2, minmax(0, 1fr));
		column-gap: clamp(2rem, 4vw, 4rem);
	}
}

/* Fallback for browsers without container queries. */
@supports not (container-type: inline-size) {
	@media (min-width: 900px) {
		.applications {
			grid-template-columns: repeat(2, minmax(0, 1fr));
			column-gap: clamp(2rem, 4vw, 4rem);
		}
	}
}

/* FAQs. Native disclosure, no JavaScript. */
.faqs {
	border-top: var(--border) solid var(--hair-strong);
}

.faq {
	border-bottom: var(--border) solid var(--hair);
}

.faq summary {
	display: flex;
	align-items: flex-start;
	justify-content: space-between;
	gap: var(--s-4);
	padding: var(--s-5) 0;
	cursor: pointer;
	list-style: none;
	font-size: var(--fs-h4);
	font-weight: 560;
	letter-spacing: -0.012em;
	color: var(--ink);
	transition: color var(--t) var(--ease);
}

.faq summary::-webkit-details-marker { display: none; }
.faq summary:hover { color: var(--ink); }

.faq__icon {
	flex: none;
	position: relative;
	width: 0.95rem;
	height: 0.95rem;
	margin-top: 0.45em;
}

.faq__icon::before,
.faq__icon::after {
	content: "";
	position: absolute;
	background: var(--c-yellow);
	transition: transform var(--t) var(--ease), opacity var(--t) var(--ease);
}

.faq__icon::before { left: 0; top: calc(50% - 1.5px); width: 100%; height: 3px; }
.faq__icon::after { top: 0; left: calc(50% - 1.5px); height: 100%; width: 3px; }

.faq[open] .faq__icon::after { transform: scaleY(0); opacity: 0; }

.faq__body {
	padding-bottom: var(--s-5);
	color: var(--ink-soft);
	max-width: 68ch;
}

/* --------------------------------------------------------------------------
   10  Service index
   Numbered rows, the way distance markers run along a road. The two services
   that carry the business get double height photographs; the rest do not.
   -------------------------------------------------------------------------- */

.service-index {
	border-top: var(--border) solid var(--hair-strong);
}

.service-row {
	position: relative;
	display: block;
	text-decoration: none;
	color: inherit;
	border-bottom: var(--border) solid var(--hair);
	padding-block: var(--s-6);
	transition: background-color var(--t) var(--ease);
}

.service-row__grid {
	display: grid;
	grid-template-columns: 1fr;
	gap: var(--s-4);
	align-items: start;
}

.service-row__num {
	font-size: var(--fs-small);
	font-weight: 600;
	letter-spacing: 0.08em;
	color: var(--ink-soft);
	transition: color var(--t) var(--ease);
}

.service-row__title {
	font-size: clamp(1.75rem, 1.15rem + 2.5vw, 3.25rem);
	font-weight: 620;
	line-height: 1.02;
	letter-spacing: var(--tr-display);
	color: var(--ink);
	transition: transform var(--t) var(--ease);
	transform-origin: left center;
}

.service-row__text {
	color: var(--ink-soft);
	max-width: 42ch;
	margin-top: var(--s-3);
}

.service-row__media {
	position: relative;
	overflow: hidden;
	background: var(--c-grey);
	aspect-ratio: 16 / 10;
}

.service-row__media img {
	width: 100%;
	height: 100%;
	object-fit: cover;
	transition: transform var(--t-slow) var(--ease);
}

.service-row__go {
	display: inline-flex;
	align-items: center;
	gap: 0.6rem;
	margin-top: var(--s-5);
	font-size: var(--fs-small);
	font-weight: 600;
	color: var(--ink);
	white-space: nowrap;
	padding-bottom: 0.35rem;
	border-bottom: 2px solid var(--hair-strong);
	transition: border-color var(--t) var(--ease);
}

.service-row:hover .service-row__go { border-color: var(--c-yellow); }
.service-row__go svg { transition: transform var(--t) var(--ease); }

.service-row:hover .service-row__media img,
.service-row:focus-visible .service-row__media img {
	transform: scale(1.04);
}

.service-row:hover .service-row__go svg,
.service-row:focus-visible .service-row__go svg {
	transform: translateX(5px);
}

/* The yellow set-out mark that travels along the bottom rule on hover. */
.service-row::after {
	content: "";
	position: absolute;
	left: 0;
	bottom: -1px;
	width: 100%;
	height: 3px;
	background: var(--c-yellow);
	transform: scaleX(0);
	transform-origin: left center;
	transition: transform var(--t-slow) var(--ease);
}

.service-row:hover::after,
.service-row:focus-visible::after {
	transform: scaleX(1);
}

@media (min-width: 900px) {
	.service-row { padding-block: var(--s-7); }

	/* Two alignment lines per row: the number, the heading and the top of the
	   photograph share one; the onward link and the bottom of the photograph
	   share the other. Nothing is left floating in between. */
	.service-row__grid {
		grid-template-columns: 4.5rem minmax(0, 4fr) minmax(0, 5fr);
		gap: var(--s-6);
		align-items: stretch;
	}

	.service-row__body {
		display: flex;
		flex-direction: column;
	}

	/* A stretched grid item with an aspect ratio takes its width from the row
	   height, which overflows the column. Let it size from its column instead. */
	.service-row__media { align-self: start; }

	/* The heading and summary stay together at the top; the onward link is
	   pushed down onto the bottom alignment line. */
	.service-row__go {
		margin-top: auto;
		padding-top: var(--s-6);
		align-self: flex-start;
	}

	/* The number sits on the heading's cap line, like a chainage marker beside
	   the thing it is numbering. */
	.service-row__num {
		font-size: clamp(1.75rem, 2.2vw, 2.5rem);
		font-weight: 250;
		letter-spacing: -0.02em;
		line-height: 1;
		padding-top: 0.16em;
		align-self: start;
	}

	.service-row:hover .service-row__num { color: var(--c-yellow); }

	/* Every row the same. The index used to give the first two a taller
	   photograph and a larger heading, on the argument that tarmac and surface
	   dressing are most of the work. It read as an inconsistency rather than as
	   emphasis, and the section intro already says which services carry the
	   business, in words, which is the clearer place to say it.

	   16:9 rather than the 21:9 the shorter rows used to take, because it is
	   also the shape of the file they are given, so the photograph now arrives
	   at its slot having been cropped once instead of twice. */
	.service-row__media { aspect-ratio: 16 / 9; }

	/* On the homepage the index is one section among many and is being
	   scanned rather than read, so the photographs are cropped back to 2:1.
	   Six rows at 16:9 made that part of the page longer than its job. The
	   services page keeps 16:9: there the index is the page. */
	.service-index--compact .service-row__media { aspect-ratio: 2 / 1; }
}

/* --------------------------------------------------------------------------
   11  Projects
   Mixed sizes, alternating emphasis. Not an equal card grid.
   -------------------------------------------------------------------------- */

.project {
	display: block;
	text-decoration: none;
	color: inherit;
	position: relative;
}

.project__media {
	position: relative;
	overflow: hidden;
	background: var(--c-grey);
	/* The default is the phone shape. Wider screens reshape by role below. */
	aspect-ratio: var(--ratio, 4 / 3);
}

.project__media img {
	width: 100%;
	height: 100%;
	object-fit: cover;
	transition: transform var(--t-slow) var(--ease);
}

.project:hover .project__media img,
.project:focus-visible .project__media img {
	transform: scale(1.035);
}

/* A yellow rule draws in beneath the photograph on hover: the set-out device
   at component scale. */
.project__media::after {
	content: "";
	position: absolute;
	left: 0;
	bottom: 0;
	width: 100%;
	height: 4px;
	background: var(--c-yellow);
	transform: scaleX(0);
	transform-origin: left center;
	transition: transform var(--t-slow) var(--ease);
	z-index: 2;
}

.project:hover .project__media::after,
.project:focus-visible .project__media::after {
	transform: scaleX(1);
}

.project__body {
	padding-top: var(--s-4);
	display: flex;
	flex-direction: column;
	gap: 0.35rem;
}

.project__meta {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 0.5rem 0.75rem;
	font-size: var(--fs-label);
	font-weight: 550;
	letter-spacing: var(--tr-label);
	text-transform: uppercase;
	color: var(--ink-soft);
}

.project__meta span + span::before {
	content: "";
	display: inline-block;
	width: 0.7rem;
	height: 1px;
	background: var(--hair-strong);
	vertical-align: middle;
	margin-right: 0.75rem;
}

.project__title {
	font-size: var(--fs-h3);
	font-weight: 600;
	letter-spacing: -0.018em;
	line-height: 1.12;
	color: var(--ink);
	max-width: 24ch;
}

.project__excerpt {
	color: var(--ink-soft);
	font-size: var(--fs-small);
	max-width: 44ch;
}

/* The archive: an asymmetric run rather than a uniform grid. Every third
   project takes the full width, which gives the page rhythm and gives wide
   landscape job photographs the room they need. */
.project-run {
	display: grid;
	grid-template-columns: 1fr;
	gap: var(--s-8) var(--gap);
}

@media (min-width: 680px) {
	.project-run { grid-template-columns: repeat(6, minmax(0, 1fr)); }
	.project-run > * { grid-column: span 3; }
	.project-run > .project--wide { grid-column: span 6; }

	.project-run > * .project__media { aspect-ratio: 3 / 2; }
	.project-run > .project--wide .project__media { aspect-ratio: 16 / 9; }
	.project-run > .project--tall .project__media { aspect-ratio: 4 / 5; }
}

@media (min-width: 1024px) {
	.project-run { grid-template-columns: repeat(12, minmax(0, 1fr)); gap: var(--s-9) var(--gap); }
	.project-run > * { grid-column: span 4; }
	.project-run > .project--wide { grid-column: span 8; }
	.project-run > .project--tall { grid-column: span 4; }
	.project-run > .project--half { grid-column: span 6; }

	.project-run > * .project__media { aspect-ratio: 4 / 3; }
	.project-run > .project--half .project__media { aspect-ratio: 3 / 2; }
	.project-run > .project--tall .project__media { aspect-ratio: 4 / 5; }
	.project-run > .project--wide .project__media { aspect-ratio: 21 / 9; }
}

/* The homepage showcase: one large, two stacked beside it. */
.showcase {
	display: grid;
	grid-template-columns: 1fr;
	gap: var(--s-7) var(--gap);
}

@media (min-width: 900px) {
	.showcase {
		grid-template-columns: repeat(12, minmax(0, 1fr));
		align-items: start;
	}

	.showcase__lead { grid-column: span 7; }
	.showcase__lead .project__media { aspect-ratio: 4 / 3; }
	.showcase__side { grid-column: 9 / span 4; display: grid; gap: var(--s-7); }
	.showcase__side .project__media { aspect-ratio: 4 / 3; }
}

/* Project gallery on a single project page. */
.gallery {
	display: grid;
	grid-template-columns: 1fr;
	gap: var(--gap);
}

.gallery figure { margin: 0; }

.gallery__item {
	position: relative;
	overflow: hidden;
	background: var(--c-grey);
	aspect-ratio: 3 / 2;
}

.gallery__item img { width: 100%; height: 100%; object-fit: cover; }

.gallery figcaption {
	font-size: var(--fs-small);
	color: var(--ink-soft);
	padding-top: 0.65rem;
}

@media (min-width: 680px) {
	.gallery { grid-template-columns: repeat(6, minmax(0, 1fr)); }
	.gallery > * { grid-column: span 3; }
	.gallery > *:nth-child(5n + 1) { grid-column: span 6; }
	.gallery > *:nth-child(5n + 1) .gallery__item { aspect-ratio: 2 / 1; }
	.gallery > *:nth-child(5n + 4) .gallery__item { aspect-ratio: 4 / 5; }
	.gallery > *:nth-child(5n + 5) .gallery__item { aspect-ratio: 4 / 5; }
}

/* Before and after. A drag handle rather than two photographs side by side,
   because the comparison is the point. */
.compare {
	position: relative;
	overflow: hidden;
	aspect-ratio: 3 / 2;
	background: var(--c-grey);
	touch-action: pan-y;
	cursor: ew-resize;
	user-select: none;
}

.compare img {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	object-fit: cover;
}

.compare__after {
	clip-path: inset(0 0 0 var(--pos, 50%));
}

.compare__handle {
	position: absolute;
	top: 0;
	bottom: 0;
	left: var(--pos, 50%);
	width: 3px;
	background: var(--c-yellow);
	transform: translateX(-1.5px);
	pointer-events: none;
	z-index: 3;
}

.compare__handle::after {
	content: "";
	position: absolute;
	top: 50%;
	left: 50%;
	width: 2.75rem;
	height: 2.75rem;
	transform: translate(-50%, -50%);
	background: var(--c-yellow);
	border: 3px solid var(--c-black);
}

.compare__label {
	position: absolute;
	bottom: var(--s-4);
	z-index: 4;
	background: var(--c-black);
	color: var(--c-paper);
	font-size: var(--fs-label);
	font-weight: 600;
	letter-spacing: var(--tr-label);
	text-transform: uppercase;
	padding: 0.4rem 0.75rem;
	pointer-events: none;
}

.compare__label--before { left: var(--s-4); }
.compare__label--after { right: var(--s-4); }

.compare__range {
	position: absolute;
	inset: auto 0 0 0;
	width: 100%;
	opacity: 0;
	height: 3rem;
	margin: 0;
	cursor: ew-resize;
	z-index: 5;
}

.compare__range:focus-visible + .compare__handle::after {
	outline: 3px solid var(--c-paper);
	outline-offset: 2px;
}

/* --------------------------------------------------------------------------
   12  Editorial sections
   -------------------------------------------------------------------------- */

/* A photograph that bleeds off one edge of the page, set against text held on
   the grid. The bleed is what stops this reading as a generic split. */
.editorial {
	display: grid;
	grid-template-columns: 1fr;
	gap: var(--s-6);
	align-items: center;
}

.editorial__media {
	position: relative;
	overflow: hidden;
	background: var(--c-grey);
	aspect-ratio: var(--ratio, 4 / 3);
}

.editorial__media img { width: 100%; height: 100%; object-fit: cover; }

.editorial__body { max-width: 46ch; }

@media (min-width: 900px) {
	.editorial {
		grid-template-columns: repeat(12, minmax(0, 1fr));
		gap: clamp(2rem, 5vw, 6rem);
	}

	.editorial__media {
		grid-column: 1 / span 6;
		aspect-ratio: var(--ratio, 4 / 5);
		/* Off the left edge of the page. */
		margin-left: calc(var(--gutter) * -1);
		width: calc(100% + var(--gutter));
	}

	.editorial__body { grid-column: 8 / span 5; }

	.editorial--flip .editorial__media {
		grid-column: 7 / span 6;
		grid-row: 1;
		margin-left: 0;
		margin-right: calc(var(--gutter) * -1);
		width: calc(100% + var(--gutter));
	}

	.editorial--flip .editorial__body { grid-column: 1 / span 5; grid-row: 1; }
}

/* A section heading with an eyebrow, held to the left edge of the grid. */
.section-head {
	display: grid;
	grid-template-columns: 1fr;
	gap: var(--s-4);
	margin-bottom: var(--s-8);
}

.section-head__title { max-width: 20ch; }
.section-head__text { color: var(--ink-soft); max-width: 44ch; }

@media (min-width: 900px) {
	.section-head {
		grid-template-columns: repeat(12, minmax(0, 1fr));
		gap: var(--gap);
		align-items: end;
	}

	.section-head__main { grid-column: 1 / span 6; }
	.section-head__aside { grid-column: 8 / span 5; }
}

/* A very large statement used once or twice per page, never more. */
.statement {
	font-size: clamp(1.5rem, 0.95rem + 2.3vw, 2.75rem);
	font-weight: 480;
	line-height: 1.16;
	letter-spacing: -0.022em;
	max-width: 22ch;
	color: var(--ink);
	text-wrap: balance;
}

.statement em {
	font-style: normal;
	background: linear-gradient(var(--c-yellow), var(--c-yellow)) no-repeat 0 88%;
	background-size: 100% 0.14em;
}

/* Page opening block, used on every page except the homepage. */
/* Breadcrumb, then the heading. Nothing in between: a small uppercase line
   above a page title was saying what the breadcrumb and the heading had
   already said between them. One rule for the spacing, used by every page. */
.page-head {
	padding-top: clamp(1.75rem, 1.2rem + 2.2vw, 3rem);
	padding-bottom: clamp(2.25rem, 1.5rem + 2.8vw, 4.25rem);
}

.page-head .breadcrumbs {
	padding-top: 0;
	padding-bottom: clamp(1.75rem, 1.1rem + 2.4vw, 3.25rem);
}

.page-head__grid {
	display: grid;
	grid-template-columns: 1fr;
	gap: var(--s-5);
}

@media (min-width: 900px) {
	.page-head__grid {
		grid-template-columns: repeat(12, minmax(0, 1fr));
		gap: var(--gap);
		/* Top aligned, not bottom aligned. Bottom aligning pushed the heading
		   down by however many lines the introduction beside it happened to
		   run to, so the gap under the breadcrumb changed from page to page. */
		align-items: start;
	}

	.page-head__main { grid-column: 1 / span 7; }

	.page-head__aside {
		grid-column: 9 / span 4;
		/* Drops the introduction onto roughly the heading's first baseline. */
		padding-top: 0.5rem;
	}
}

/* Breadcrumbs */
.breadcrumbs {
	font-size: var(--fs-label);
	letter-spacing: 0.02em;
	color: var(--ink-soft);
	padding-block: var(--s-4);
}

.breadcrumbs ol {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 0.4rem 0.65rem;
}

.breadcrumbs li { display: flex; align-items: center; gap: 0.65rem; }

.breadcrumbs li + li::before {
	content: "";
	width: 0.6rem;
	height: 2px;
	background: var(--hair-strong);
	flex: none;
}

.breadcrumbs a {
	color: var(--ink-soft);
	text-decoration: none;
	transition: color var(--t) var(--ease);
}

.breadcrumbs a:hover { color: var(--ink); }
.breadcrumbs [aria-current="page"] { color: var(--ink); font-weight: 500; }

/* Related links strip at the foot of a service or area page. */
.related {
	display: grid;
	grid-template-columns: 1fr;
	gap: 0;
	border-top: var(--border) solid var(--hair-strong);
}

.related__item {
	display: flex;
	align-items: baseline;
	justify-content: space-between;
	gap: var(--s-4);
	padding: var(--s-5) 0;
	border-bottom: var(--border) solid var(--hair);
	text-decoration: none;
	color: var(--ink);
	font-size: var(--fs-h4);
	font-weight: 560;
	letter-spacing: -0.012em;
	transition: padding-left var(--t) var(--ease);
	position: relative;
}

.related__item::after {
	content: "";
	position: absolute;
	left: 0;
	bottom: -1px;
	width: 100%;
	height: 2px;
	background: var(--c-yellow);
	transform: scaleX(0);
	transform-origin: left;
	transition: transform var(--t-slow) var(--ease);
}

.related__item:hover,
.related__item:focus-visible { padding-left: 0.5rem; }
.related__item:hover::after,
.related__item:focus-visible::after { transform: scaleX(1); }

.related__item span:last-child { color: var(--ink-soft); font-size: var(--fs-small); font-weight: 400; }

@media (min-width: 760px) {
	.related { grid-template-columns: repeat(2, minmax(0, 1fr)); column-gap: clamp(2rem, 5vw, 5rem); }
}

/* --------------------------------------------------------------------------
   13  Coverage
   -------------------------------------------------------------------------- */

.coverage {
	display: grid;
	grid-template-columns: 1fr;
	gap: var(--s-7);
}

.coverage__towns {
	border-top: var(--border) solid var(--hair-strong);
}

.coverage__town {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: var(--s-4);
	padding: var(--s-4) 0;
	border-bottom: var(--border) solid var(--hair);
	text-decoration: none;
	color: var(--ink);
	position: relative;
	transition: padding-left var(--t) var(--ease);
}

.coverage__town-name {
	font-size: clamp(1.375rem, 1.05rem + 1.3vw, 2rem);
	font-weight: 580;
	letter-spacing: -0.02em;
	line-height: 1.1;
}

/* The list is now just the places, with an arrow that moves on hover. Nothing
   grey and vague beside each one: it told the reader nothing. */
.coverage__town-arrow {
	flex: none;
	color: var(--ink-soft);
	transition: transform var(--t) var(--ease), color var(--t) var(--ease);
	align-self: center;
}

.coverage__town:hover .coverage__town-arrow,
.coverage__town:focus-visible .coverage__town-arrow {
	transform: translateX(5px);
	color: var(--ink);
}

.coverage__town::after {
	content: "";
	position: absolute;
	left: 0;
	bottom: -1px;
	height: 2px;
	width: 100%;
	background: var(--c-yellow);
	transform: scaleX(0);
	transform-origin: left;
	transition: transform var(--t-slow) var(--ease);
}

.coverage__town:hover { padding-left: 0.5rem; }
.coverage__town:hover::after,
.coverage__town:focus-visible::after { transform: scaleX(1); }

.coverage__others {
	font-size: var(--fs-small);
	color: var(--ink-soft);
	max-width: 52ch;
	margin-top: var(--s-5);
}

@media (min-width: 900px) {
	.coverage {
		grid-template-columns: repeat(12, minmax(0, 1fr));
		gap: clamp(2rem, 5vw, 5rem);
		align-items: start;
	}

	.coverage__list { grid-column: 1 / span 6; }
	.coverage__aside { grid-column: 8 / span 5; }
}

/* --------------------------------------------------------------------------
   14  Enquiry form
   -------------------------------------------------------------------------- */

.quote {
	position: relative;
}

.quote__grid {
	display: grid;
	grid-template-columns: 1fr;
	gap: var(--s-7);
}

@media (min-width: 980px) {
	.quote__grid {
		grid-template-columns: repeat(12, minmax(0, 1fr));
		gap: clamp(2.5rem, 5vw, 5rem);
		align-items: start;
	}

	.quote__intro { grid-column: 1 / span 5; }
	.quote__form { grid-column: 7 / span 6; }
}

/* Two telephone numbers, set as numbers rather than as body copy: on this
   section they are the alternative to filling the form in. */
.quote__contact {
	display: grid;
	gap: 0;
	margin-top: var(--s-7);
	border-top: var(--border) solid var(--hair-strong);
}

.quote__tel {
	position: relative;
	display: flex;
	align-items: baseline;
	gap: var(--s-4);
	padding: var(--s-4) 0;
	border-bottom: var(--border) solid var(--hair);
	text-decoration: none;
	color: inherit;
}

.quote__tel::after {
	content: "";
	position: absolute;
	left: 0;
	bottom: -1px;
	width: 100%;
	height: 2px;
	background: var(--c-yellow);
	transform: scaleX(0);
	transform-origin: left center;
	transition: transform var(--t-slow) var(--ease);
}

.quote__tel:hover::after,
.quote__tel:focus-visible::after { transform: scaleX(1); }

.quote__tel-label {
	flex: none;
	min-width: 4.5rem;
	font-size: var(--fs-label);
	font-weight: 550;
	letter-spacing: var(--tr-label);
	text-transform: uppercase;
	color: var(--ink-soft);
}

.quote__tel-number {
	font-size: clamp(1.25rem, 1.05rem + 0.7vw, 1.625rem);
	font-weight: 620;
	letter-spacing: -0.022em;
	color: var(--ink);
}

/* The email address is longer than a telephone number and does not need to
   shout as loudly, so it steps down a size. */
.quote__tel--email .quote__tel-number {
	font-size: var(--fs-body);
	font-weight: 550;
	letter-spacing: -0.008em;
	word-break: break-word;
}

/* Opening hours are information, not an action: no hover rule, no pointer. */
.quote__tel--static { cursor: default; }
.quote__tel--static::after { content: none; }

.quote__tel-hours {
	color: var(--ink-soft);
	font-size: var(--fs-small);
	align-self: center;
}

/* The form's own surface. Square, hairline bordered, one tone lighter than the
   section it sits on, so it reads as a document to fill in rather than as a
   rounded card floating on a page. */
.form-panel {
	background: var(--c-paper);
	border: var(--border) solid var(--hair-strong);
	border-top: 3px solid var(--c-ink);
	padding: clamp(1.5rem, 1rem + 2vw, 2.75rem);
}

.is-dark .form-panel {
	background: var(--c-charcoal);
	border-top-color: var(--c-yellow);
}

.form-panel__title {
	font-size: var(--fs-h3);
	font-weight: 620;
	letter-spacing: -0.018em;
	color: var(--ink);
	margin-bottom: var(--s-5);
}

.form-panel__intro {
	color: var(--ink-soft);
	max-width: 46ch;
	margin-bottom: var(--s-6);
	padding-bottom: var(--s-5);
	border-bottom: var(--border) solid var(--hair);
}

/* Where the panel has no intro paragraph the rule still belongs under the
   heading, so the heading is never sitting straight on the first field. */
.form-panel__title:not(:has(+ .form-panel__intro)) {
	padding-bottom: var(--s-5);
	margin-bottom: var(--s-6);
	border-bottom: var(--border) solid var(--hair);
}

.form {
	display: grid;
	grid-template-columns: repeat(2, minmax(0, 1fr));
	gap: var(--s-5) var(--gap);
}

.field { display: flex; flex-direction: column; gap: 0.5rem; grid-column: span 2; }
.field--half { grid-column: span 2; }

@media (min-width: 560px) {
	.field--half { grid-column: span 1; }
}

.field label {
	font-size: var(--fs-label);
	font-weight: 600;
	letter-spacing: var(--tr-label);
	text-transform: uppercase;
	color: var(--ink);
}

.field__help { font-size: var(--fs-small); color: var(--ink-soft); }

.field input,
.field select,
.field textarea {
	width: 100%;
	background: transparent;
	border: 0;
	border-bottom: 2px solid var(--hair-strong);
	border-radius: 0;
	padding: 0.8rem 0;
	font-size: var(--fs-body);
	color: var(--ink);
	transition: border-color var(--t) var(--ease);
	min-height: 3rem;
	appearance: none;
}

.field select {
	background-image: linear-gradient(45deg, transparent 50%, currentColor 50%),
		linear-gradient(135deg, currentColor 50%, transparent 50%);
	background-position: right 0.9rem center, right 0.4rem center;
	background-size: 0.42rem 0.42rem, 0.42rem 0.42rem;
	background-repeat: no-repeat;
	padding-right: 2rem;
	cursor: pointer;
}

.is-dark .field select option { color: var(--c-ink); }

.field textarea { min-height: 8rem; resize: vertical; line-height: 1.6; }

.field input::placeholder,
.field textarea::placeholder { color: var(--ink-soft); opacity: 0.7; }

.field input:focus,
.field select:focus,
.field textarea:focus {
	outline: none;
	border-bottom-color: var(--c-yellow);
}

.field input:focus-visible,
.field select:focus-visible,
.field textarea:focus-visible {
	outline: 2px solid var(--c-yellow);
	outline-offset: 4px;
}

.field--error input,
.field--error select,
.field--error textarea { border-bottom-color: #c1272d; }

.is-dark .field--error input,
.is-dark .field--error select,
.is-dark .field--error textarea { border-bottom-color: #ff8b8e; }

.field__error {
	font-size: var(--fs-small);
	font-weight: 500;
	color: #c1272d;
}

.is-dark .field__error { color: #ff8b8e; }

.field--honey {
	position: absolute;
	left: -9999px;
	width: 1px;
	height: 1px;
	overflow: hidden;
}

.form__submit {
	grid-column: span 2;
	display: flex;
	flex-wrap: wrap;
	gap: var(--s-4) var(--s-5);
	align-items: center;
	margin-top: var(--s-3);
	padding-top: var(--s-5);
	border-top: var(--border) solid var(--hair);
}

.form__submit .btn {
	min-height: 3.75rem;
	padding-inline: 2rem;
	font-size: 1rem;
}

.form__note { font-size: var(--fs-small); color: var(--ink-soft); max-width: 34ch; }

/* Labels sit tight to their field and away from the one above, so the form
   scans as a column of questions rather than an even grey texture. */
.field { gap: 0.4rem; }
.form { row-gap: var(--s-6); }

.form-message {
	border-left: 4px solid var(--c-yellow);
	padding: var(--s-5);
	background: var(--c-grey);
	margin-bottom: var(--s-6);
}

.is-dark .form-message { background: rgba(255, 255, 255, 0.05); }

.form-message__title {
	font-size: var(--fs-h4);
	font-weight: 620;
	letter-spacing: -0.012em;
	margin-bottom: 0.4rem;
	color: var(--ink);
}

.form-message--error { border-left-color: #c1272d; }
.is-dark .form-message--error { border-left-color: #ff6b6e; }

/* Sticky contact bar on small screens. Deliberately slim, and it never sits on
   top of the content: the page has matching bottom padding. */
.sticky-actions {
	position: fixed;
	inset: auto 0 0 0;
	z-index: 80;
	display: grid;
	grid-template-columns: 1fr 1fr;
	background: var(--c-black);
	border-top: 3px solid var(--c-yellow);
	transform: translateY(110%);
	transition: transform var(--t) var(--ease);
}

.sticky-actions.is-visible { transform: translateY(0); }

.sticky-actions a {
	display: flex;
	align-items: center;
	justify-content: center;
	gap: 0.6rem;
	padding: 0.95rem 0.5rem;
	min-height: 3.25rem;
	color: var(--c-paper);
	text-decoration: none;
	font-size: var(--fs-small);
	font-weight: 600;
}

.sticky-actions a + a { border-left: var(--border) solid var(--rule-dark); }
.sticky-actions a:last-child { background: var(--c-yellow); color: var(--c-ink); }

@media (min-width: 1024px) {
	.sticky-actions { display: none; }
}

/* The room the bar needs is taken out of the footer, not the body. Padding on
   the body drew the strip in the page's own pale ground, which showed as a
   white band under the black footer whenever the bar was hidden. */
body.has-sticky-actions .site-footer {
	padding-bottom: calc(3.4rem + env(safe-area-inset-bottom, 0px));
}

@media (min-width: 1024px) {
	body.has-sticky-actions .site-footer { padding-bottom: 0; }
}

/* --------------------------------------------------------------------------
   15  Footer
   -------------------------------------------------------------------------- */

/* A black footer under a pale quote section: the transition is a hard edge
   between two clearly different things, not two dark slabs running together. */
.site-footer {
	background: var(--c-black);
	color: var(--c-paper);
	--ground: var(--c-black);
	--ink: var(--c-paper);
	--ink-soft: var(--c-ash);
	--hair: var(--rule-dark);
	--hair-strong: var(--rule-dark-strong);
	padding-top: 0;
}

/* The footer identifies the company; it does not sell to it again. A giant
   headline here repeated the homepage and competed with the navigation that is
   the reason anybody looks at a footer.

   Centred, not baseline aligned. A replaced element's baseline is its bottom
   edge, so once the wordmark became an uploaded image, baseline alignment sat
   the bottom of the logo on the first line of the text beside it and the two
   ended up staggered. Centring relates them properly whether the line beside
   runs to one line or two. */
.site-footer__identity {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	justify-content: space-between;
	gap: var(--s-4) var(--s-7);
	padding-block: var(--s-7) var(--s-8);
	margin-bottom: var(--s-8);
	border-bottom: var(--border) solid var(--rule-dark);
}

.wordmark--footer { color: var(--c-paper); }

.wordmark--footer .wordmark__name {
	font-size: clamp(1.25rem, 1rem + 1vw, 1.75rem);
}

.wordmark--footer .wordmark__trade { color: var(--c-ash); }

/* Set against the right hand page edge, so the row reads as two things pinned
   to the two edges rather than one thing adrift in the middle. */
.site-footer__line {
	font-size: var(--fs-small);
	color: var(--c-ash);
	max-width: 34ch;
}

@media (min-width: 680px) {
	.site-footer__line { text-align: right; }
}

.footer-grid {
	display: grid;
	grid-template-columns: 1fr;
	gap: var(--s-7) var(--gap);
	padding-bottom: var(--s-9);
}

.footer-col__title {
	font-size: var(--fs-label);
	font-weight: 600;
	letter-spacing: var(--tr-label);
	text-transform: uppercase;
	color: var(--c-ash);
	padding-bottom: var(--s-3);
	margin-bottom: var(--s-4);
	border-bottom: var(--border) solid var(--rule-dark);
}

.footer-col ul { display: grid; gap: 0.65rem; }

.footer-col a {
	color: var(--c-paper);
	text-decoration: none;
	font-size: var(--fs-small);
	transition: color var(--t) var(--ease), padding-left var(--t) var(--ease);
}

.footer-col a:hover,
.footer-col a:focus-visible { color: var(--c-yellow); padding-left: 0.35rem; }

.footer-address { font-style: normal; display: grid; gap: 0.9rem; }

.footer-address dt {
	font-size: var(--fs-label);
	letter-spacing: var(--tr-label);
	text-transform: uppercase;
	color: var(--c-ash);
	margin-bottom: 0.2rem;
}

.footer-address dd { margin: 0; }

.footer-address a {
	color: var(--c-paper);
	text-decoration: none;
	font-size: var(--fs-h4);
	font-weight: 560;
	letter-spacing: -0.012em;
}

.footer-address a:hover { color: var(--c-yellow); }

.footer-bar {
	border-top: var(--border) solid var(--rule-dark);
	padding-block: var(--s-5);
	display: flex;
	flex-wrap: wrap;
	gap: var(--s-3) var(--s-6);
	align-items: center;
	justify-content: space-between;
	font-size: var(--fs-small);
	color: var(--c-ash);
}

.footer-bar a { color: var(--c-ash); text-decoration: none; }
.footer-bar a:hover { color: var(--c-yellow); }

@media (min-width: 680px) {
	.footer-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (min-width: 1024px) {
	.footer-grid {
		grid-template-columns: repeat(12, minmax(0, 1fr));
		gap: var(--s-8) var(--gap);
	}

	.footer-grid__contact { grid-column: span 4; }
	.footer-grid__services { grid-column: span 3; }
	.footer-grid__areas { grid-column: span 2; }
	.footer-grid__company { grid-column: span 3; }
}

/* --------------------------------------------------------------------------
   16  Editor content
   Anything the owner writes in the block editor lands here and inherits the
   design system, so added copy never looks bolted on.
   -------------------------------------------------------------------------- */

.entry-content {
	max-width: var(--read-max);
	color: var(--ink-soft);
}

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

.entry-content h2,
.entry-content h3,
.entry-content h4 {
	color: var(--ink);
	letter-spacing: var(--tr-heading);
	line-height: 1.14;
	margin-top: 1.8em;
}

.entry-content h2 { font-size: var(--fs-h2); font-weight: 600; max-width: 20ch; }
.entry-content h3 { font-size: var(--fs-h3); font-weight: 620; }
.entry-content h4 { font-size: var(--fs-h4); font-weight: 600; }

.entry-content strong { color: var(--ink); font-weight: 600; }

.entry-content a:not(.btn) {
	color: inherit;
	text-decoration: none;
	background-image: linear-gradient(var(--c-yellow), var(--c-yellow));
	background-repeat: no-repeat;
	background-position: 0 100%;
	background-size: 0% 2px;
	box-shadow: inset 0 -1px 0 var(--hair-strong);
	transition: background-size var(--t) var(--ease);
}

.entry-content a:not(.btn):hover { background-size: 100% 2px; }

.entry-content ul,
.entry-content ol { padding-left: 0; }

.entry-content ul li,
.entry-content ol li {
	position: relative;
	padding-left: 1.6rem;
	margin-bottom: 0.55em;
}

.entry-content ul li::before {
	content: "";
	position: absolute;
	left: 0;
	top: 0.72em;
	width: 0.75rem;
	height: 2px;
	background: var(--c-yellow);
}

.entry-content ol { counter-reset: li; }

.entry-content ol li {
	counter-increment: li;
	padding-left: 2.1rem;
}

.entry-content ol li::before {
	content: counter(li, decimal-leading-zero);
	position: absolute;
	left: 0;
	top: 0;
	font-size: var(--fs-small);
	font-weight: 600;
	color: var(--ink-soft);
}

.entry-content blockquote {
	border-left: 3px solid var(--c-yellow);
	padding-left: var(--s-5);
	margin-left: 0;
	font-size: var(--fs-lede);
	font-weight: 400;
	line-height: 1.4;
	letter-spacing: -0.012em;
	color: var(--ink);
}

.entry-content img,
.entry-content .wp-block-image img { border-radius: 0; }

.entry-content figcaption {
	font-size: var(--fs-small);
	color: var(--ink-soft);
	padding-top: 0.6rem;
}

.entry-content .wp-block-table table { width: 100%; border-collapse: collapse; }

.entry-content .wp-block-table th,
.entry-content .wp-block-table td {
	text-align: left;
	padding: 0.85rem 1rem 0.85rem 0;
	border-bottom: var(--border) solid var(--hair);
}

.entry-content .wp-block-table th { color: var(--ink); font-weight: 600; }

.entry-content .wp-block-separator {
	border: 0;
	border-top: var(--border) solid var(--hair);
	margin-block: var(--s-7);
}

/* --------------------------------------------------------------------------
   17  Utilities and motion
   -------------------------------------------------------------------------- */

/* An explicit width matters: with only an aspect ratio to go on, a max-height
   makes the browser derive the width from the clamped height and the frame
   shrinks sideways instead of cropping. */
.frame {
	position: relative;
	width: 100%;
	overflow: hidden;
	background: var(--c-grey);
	aspect-ratio: var(--ratio, 3 / 2);
}

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

/* One photographic treatment across the whole site, so photographs taken over
   fifty years on twenty different cameras still read as one body of work. */
.frame img,
.project__media img,
.hero__media img,
.editorial__media img,
.service-row__media img,
.gallery__item img,
.compare img {
	filter: contrast(1.04) saturate(0.95);
}

.frame::after,
.project__media::before,
.hero__media::before,
.editorial__media::before {
	content: "";
	position: absolute;
	inset: 0;
	background: var(--c-yellow);
	mix-blend-mode: soft-light;
	opacity: 0.05;
	pointer-events: none;
	z-index: 1;
}

.figure-caption {
	font-size: var(--fs-small);
	color: var(--ink-soft);
	padding-top: 0.7rem;
	max-width: 46ch;
}

/* Scroll reveal. Deliberately small: a short rise and a settle on the image,
   once, and never anything that delays reading. */
.js .reveal {
	opacity: 0;
	transform: translateY(14px);
	transition: opacity 520ms var(--ease), transform 620ms var(--ease);
	transition-delay: var(--delay, 0ms);
}

.js .reveal.is-in {
	opacity: 1;
	transform: none;
}

.js .reveal-img img {
	transform: scale(1.05);
	opacity: 0;
	transition: transform 900ms var(--ease), opacity 520ms var(--ease);
}

.js .reveal-img.is-in img {
	transform: none;
	opacity: 1;
}

.pagination {
	display: flex;
	flex-wrap: wrap;
	gap: var(--s-3);
	align-items: center;
	padding-top: var(--s-8);
	border-top: var(--border) solid var(--hair);
	margin-top: var(--s-8);
}

.pagination .page-numbers {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	min-width: 2.9rem;
	min-height: 2.9rem;
	padding: 0 0.75rem;
	border: var(--border) solid var(--hair-strong);
	color: var(--ink);
	text-decoration: none;
	font-size: var(--fs-small);
	font-weight: 550;
	transition: background-color var(--t) var(--ease), color var(--t) var(--ease);
}

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

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

@media (prefers-reduced-motion: reduce) {
	html { scroll-behavior: auto; }

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

	.js .reveal,
	.js .reveal-img img {
		opacity: 1 !important;
		transform: none !important;
	}

}

@media print {
	.site-header,
	.contact-band,
	.sticky-actions,
	.nav-panel,
	.btn { display: none !important; }

	body { background: #fff; color: #000; }
}

/* --------------------------------------------------------------------------
   18  Work filter
   Plain links with a query variable, so every filtered view is a real URL and
   the whole thing works with JavaScript turned off.
   -------------------------------------------------------------------------- */

.work-filter {
	display: flex;
	flex-wrap: wrap;
	gap: 0;
	border-top: var(--border) solid var(--hair-strong);
	border-bottom: var(--border) solid var(--hair);
	overflow-x: auto;
	scrollbar-width: none;
}

.work-filter::-webkit-scrollbar { display: none; }

.work-filter__item {
	position: relative;
	padding: 1rem 1.35rem 1rem 0;
	margin-right: 1.35rem;
	font-size: var(--fs-small);
	font-weight: 520;
	letter-spacing: -0.005em;
	color: var(--ink-soft);
	text-decoration: none;
	white-space: nowrap;
	transition: color var(--t) var(--ease);
}

.work-filter__item::after {
	content: "";
	position: absolute;
	left: 0;
	bottom: -1px;
	width: calc(100% - 1.35rem);
	height: 3px;
	background: var(--c-yellow);
	transform: scaleX(0);
	transform-origin: left center;
	transition: transform var(--t) var(--ease);
}

.work-filter__item:hover,
.work-filter__item:focus-visible { color: var(--ink); }

.work-filter__item:hover::after,
.work-filter__item:focus-visible::after,
.work-filter__item.is-active::after { transform: scaleX(1); }

.work-filter__item.is-active { color: var(--ink); font-weight: 620; }

@media (max-width: 679px) {
	.work-filter { flex-wrap: nowrap; }
}

/* --------------------------------------------------------------------------
   19  Small screen header
   The header has to carry the wordmark, a call action and the menu inside
   about 320 usable pixels, so below the desktop breakpoint the telephone
   number collapses to its icon and the second number leaves the band.
   -------------------------------------------------------------------------- */

/* Sized in CSS, not by the width and height attributes on the SVG: those are
   presentation attributes and lose to any author rule, so keeping the size here
   means there is one place to change it. */
.btn__phone {
	flex: none;
	display: block;
	width: 1.375rem;
	height: 1.375rem;
}

.btn--call .btn__phone { display: none; }

/* The full navigation, a spelled out telephone number and "Get a Free Quote"
   do not all fit between 1024 and 1200. The number collapses to its icon there
   rather than the quote button being shortened: the button is the conversion. */
@media (max-width: 1199px) {
	.site-header__inner { gap: var(--s-3); }
	.header-actions { gap: var(--s-2); }

	/* Icon only, with the number left in the accessibility tree.

	   Scoped to .header-actions deliberately: the generic .header-actions .btn
	   rule sets the padding for every button in the bar and outranks a single
	   class, so without matching its specificity the padding stayed and the
	   icon was squeezed into what was left of the content box. */
	.header-actions .btn--call {
		padding: 0;
		width: 3rem;
		justify-content: center;
	}

	.btn--call .btn__phone { display: block; }

	.btn--call .btn__number {
		position: absolute;
		width: 1px;
		height: 1px;
		padding: 0;
		margin: -1px;
		overflow: hidden;
		clip: rect(0, 0, 0, 0);
		white-space: nowrap;
		border: 0;
	}

	.nav-toggle { padding: 0.75rem 0.85rem; gap: 0.5rem; }
}

@media (min-width: 1024px) and (max-width: 1199px) {
	.nav-primary ul { gap: 1.1rem; }
	.header-actions .btn:not(.btn--call) { padding-inline: 1rem; }
}

@media (max-width: 560px) {
	/* Two numbers do not fit. The mobile is the one that gets answered. */
	.contact-band__office { display: none; }
	.contact-band__inner { justify-content: flex-end; }
}

@media (max-width: 400px) {
	.nav-toggle span:last-child { display: none; }
	.nav-toggle { padding: 0.75rem; width: 2.9rem; justify-content: center; }
	.site-header__inner { gap: var(--s-2); }
}

/* Footer links get a little more room so a fingertip is never near two. */
.footer-col ul { gap: 0.4rem; }
.footer-col a { display: inline-block; padding-block: 0.2rem; }


/* --------------------------------------------------------------------------
   20  Responsive image shapes
   Three named frames. Each changes proportion at the breakpoints, because a
   21:9 banner is a letterbox slot on a phone and a 4:5 upright is a tower.
   -------------------------------------------------------------------------- */

/* The page banner.

   On the page grid, not full bleed: its edges line up with the breadcrumb, the
   heading and the body copy, so the photograph belongs to the page rather than
   sitting behind it.

   The frame takes the photograph's own aspect ratio, passed in as --ratio by
   skeet_banner(). It does not impose one. A banner has nothing beside it to
   line up with vertically, so there is no reason to crop a good photograph into
   a shape the layout picked for it.

   The min and max heights are the only constraint. They stop an extreme
   panorama becoming a hairline and an upright photograph filling the screen,
   and they are the only circumstance in which anything is cropped at all. When
   they do bite, object-position decides which part survives, which the owner
   sets per photograph in the media library.

   The cap is a pair. The rem figure governs an ordinary desktop and is what
   keeps a squarish photograph from swallowing the first screen; the vh figure
   takes over on a short window, where a fixed height would do the same damage
   at a smaller number. Neither on its own is enough.

   With no photograph set it falls back to 5:2, which is both a sensible shape
   for a placeholder and the shape the image guide asks for, so the empty slot
   previews what belongs in it. */
.frame--banner {
	aspect-ratio: var(--ratio, 5 / 2);
	min-height: 10rem;
	max-height: 64vh;
}

@media (min-width: 560px) {
	.frame--banner {
		min-height: 14rem;
		max-height: 68vh;
	}
}

@media (min-width: 1024px) {
	.frame--banner {
		min-height: 17rem;
		max-height: min(68vh, 38rem);
	}
}

@media (min-width: 1600px) {
	.frame--banner { max-height: min(70vh, 42rem); }
}

.banner--page { margin-bottom: var(--section-y); }
.banner--project { margin-bottom: var(--section-y-tight); }

/* Upright detail shot: only upright once there is a column to be upright in. */
.frame--upright { aspect-ratio: 3 / 2; }

@media (min-width: 900px) { .frame--upright { aspect-ratio: 4 / 5; } }

/* The editorial photograph follows the same rule. */
.editorial__media { aspect-ratio: 3 / 2; }

@media (min-width: 900px) {
	.editorial__media { aspect-ratio: var(--ratio, 4 / 5); }
}

/* The coverage schematic is a square. Left at full width on a tablet it would
   be the height of the screen, so cap it and keep it on the left of the grid. */

/* Editor content starts on the same line as whatever sits beside it: the
   heading margin that separates sections must not push the first one down. */
.entry-content > *:first-child { margin-top: 0; }
.prose > *:first-child { margin-top: 0; }

/* --------------------------------------------------------------------------
   21  Large telephone display
   On the contact page the numbers are the primary action, so they are set at
   heading scale as their own block rather than as inline links.
   -------------------------------------------------------------------------- */

.tel-big {
	display: block;
	text-decoration: none;
	color: inherit;
	border-top: var(--border) solid var(--hair);
	padding-block: var(--s-4);
	position: relative;
}

.tel-big:first-child { border-top: var(--border) solid var(--hair-strong); }

.tel-big::after {
	content: "";
	position: absolute;
	left: 0;
	bottom: 0;
	width: 100%;
	height: 2px;
	background: var(--c-yellow);
	transform: scaleX(0);
	transform-origin: left center;
	transition: transform var(--t-slow) var(--ease);
}

.tel-big:hover::after,
.tel-big:focus-visible::after { transform: scaleX(1); }

.tel-big__label {
	display: block;
	font-size: var(--fs-label);
	font-weight: 550;
	letter-spacing: var(--tr-label);
	text-transform: uppercase;
	color: var(--ink-soft);
	margin-bottom: 0.4rem;
}

.tel-big__number {
	display: block;
	font-size: clamp(1.75rem, 1.15rem + 2.2vw, 2.75rem);
	font-weight: 620;
	letter-spacing: -0.028em;
	line-height: 1.05;
	color: var(--ink);
}

/* The sticky call bar is redundant on the page that is entirely about
   contacting us, and it would sit over the form. */
.page-template-contact .sticky-actions { display: none; }
.page-template-contact.has-sticky-actions .site-footer { padding-bottom: 0; }

/* The filter bar is the divider between the page head and the work, so the
   section beneath it does not add a second gap on top of it. */
.work-filter + .section { padding-top: var(--s-8); }

/* --------------------------------------------------------------------------
   22  The About page's two photographs
   One from now, one from the archive, offset from each other so the pair reads
   as a composition rather than as two images in a row.
   -------------------------------------------------------------------------- */

.about-figures { align-items: start; }
.about-figures > figure { margin: 0; }

@media (min-width: 900px) {
	.about-figures > figure:last-child {
		margin-top: clamp(2.5rem, 7vw, 7rem);
	}
}

/* On a tablet a 4:3 hero photograph fills the whole screen before a word of
   the headline is reached. Letterbox it once there is width to do so. */
@media (min-width: 560px) and (max-width: 1023px) {
	.hero__media { aspect-ratio: 16 / 9; }
}


/* --------------------------------------------------------------------------
   23  Aligned split

   A heading, a photograph under it, and a list beside the photograph. Two
   explicit grid rows rather than two columns of stacked content: the heading
   occupies the first row on its own, and the photograph and the list share the
   second. The top of the list then lines up with the top of the photograph
   exactly, whatever length the heading runs to.

   Used by "who we work for" on the homepage and by the process list on every
   service page, so the two read as the same idea.
   -------------------------------------------------------------------------- */

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

.split__media { margin: 0; }

@media (min-width: 900px) {
	.split {
		grid-template-columns: repeat(12, minmax(0, 1fr));
		column-gap: clamp(2rem, 4vw, 4.5rem);
		row-gap: var(--s-7);
	}

	.split__head  { grid-column: 1 / span 5; grid-row: 1; }
	.split__media { grid-column: 1 / span 5; grid-row: 2; }
	.split__list  { grid-column: 6 / span 7; grid-row: 2; }
}


/* --------------------------------------------------------------------------
   24  Service area map

   A real map, styled down to the site's palette with a filter on the tile
   layer so it sits in the page rather than shouting from it. Leaflet's own
   chrome is reduced to a square zoom control and a quiet attribution line,
   both of which have to stay: the attribution is a licence condition.
   -------------------------------------------------------------------------- */

.coverage-map { margin: 0; }

.coverage-map__canvas {
	position: relative;
	aspect-ratio: 4 / 3;
	background: var(--c-grey);
	border: var(--border) solid var(--hair-strong);
	overflow: hidden;
	z-index: 0;
}

.is-dark .coverage-map__canvas { background: var(--c-charcoal); }

/* Until the map loads the frame is a quiet panel rather than an empty hole. */
.coverage-map__canvas::before {
	content: "";
	position: absolute;
	inset: 0;
	background:
		repeating-linear-gradient(0deg, transparent 0 47px, var(--hair) 47px 48px),
		repeating-linear-gradient(90deg, transparent 0 47px, var(--hair) 47px 48px);
	opacity: 0.7;
	pointer-events: none;
	transition: opacity var(--t) var(--ease);
}

.coverage-map__canvas.is-ready::before { opacity: 0; }

.coverage-map__fallback {
	position: absolute;
	inset: 0;
	display: flex;
	align-items: center;
	padding: var(--s-5);
	color: var(--ink-soft);
	font-size: var(--fs-small);
	max-width: 34ch;
}

/* Bring the tiles into the site's palette: warm, low saturation, no blue. */
.coverage-map__canvas .leaflet-tile-pane {
	filter: grayscale(1) sepia(0.22) saturate(0.65) contrast(1.02) brightness(1.03);
}

.coverage-map__canvas .leaflet-container {
	background: var(--c-grey);
	font-family: var(--font);
	outline-offset: -2px;
}

/* Square controls, in the site's colours. */
.coverage-map__canvas .leaflet-bar,
.coverage-map__canvas .leaflet-bar a {
	border-radius: 0;
	border-color: var(--rule-strong);
	color: var(--c-ink);
	background: var(--c-paper);
}

.coverage-map__canvas .leaflet-bar a:hover {
	background: var(--c-ink);
	color: var(--c-paper);
}

.coverage-map__canvas .leaflet-control-attribution {
	background: rgba(248, 247, 242, 0.86);
	color: var(--c-ink-soft);
	font-size: 0.6875rem;
	font-family: var(--font);
	padding: 0.15rem 0.45rem;
}

.coverage-map__canvas .leaflet-control-attribution a { color: var(--c-ink-soft); }

.map-reset {
	border: var(--border) solid var(--rule-strong);
	background: var(--c-paper);
	color: var(--c-ink);
	font-size: 0.6875rem;
	font-weight: 600;
	letter-spacing: 0.1em;
	text-transform: uppercase;
	padding: 0.4rem 0.6rem;
	margin: 0 0 0.4rem 0.4rem;
	cursor: pointer;
}

.map-reset:hover { background: var(--c-ink); color: var(--c-paper); }

/* Markers. The base is a yellow square, everywhere else is an ink square: the
   one place with premises looks different from the places we drive to. */
.map-pin {
	position: relative;
	width: 0 !important;
	height: 0 !important;
}

/* The marker sits above its point on a short stem, rather than on it: the
   basemap prints the town's name at the point, and a square dropped straight
   on top of it blots out the word. */
.map-pin__dot {
	position: absolute;
	left: -4.5px;
	top: -20px;
	width: 9px;
	height: 9px;
	background: var(--c-ink);
	box-shadow: 0 0 0 2.5px rgba(248, 247, 242, 0.95);
}

.map-pin__dot::after {
	content: "";
	position: absolute;
	left: 50%;
	top: 100%;
	width: 1px;
	height: 11px;
	margin-left: -0.5px;
	background: var(--c-ink);
	box-shadow: 0 0 0 1.5px rgba(248, 247, 242, 0.85);
}

.map-pin--base .map-pin__dot {
	left: -6.5px;
	top: -26px;
	width: 13px;
	height: 13px;
	background: var(--c-yellow);
	box-shadow: 0 0 0 2px var(--c-ink);
}

.map-pin--base .map-pin__dot::after {
	height: 13px;
	box-shadow: 0 0 0 1.5px rgba(248, 247, 242, 0.85);
}

.map-pin__label {
	position: absolute;
	left: 50%;
	bottom: 30px;
	transform: translateX(-50%);
	white-space: nowrap;
	font-size: 0.8125rem;
	font-weight: 680;
	letter-spacing: 0.01em;
	color: var(--c-ink);
	text-shadow:
		0 1px 0 rgba(248, 247, 242, 0.95),
		0 -1px 0 rgba(248, 247, 242, 0.95),
		1px 0 0 rgba(248, 247, 242, 0.95),
		-1px 0 0 rgba(248, 247, 242, 0.95),
		0 0 4px rgba(248, 247, 242, 0.95);
}

.coverage-map__canvas .map-pin__label a {
	color: var(--c-ink);
	text-decoration: none;
	box-shadow: inset 0 -1px 0 rgba(20, 20, 20, 0.4);
}

.coverage-map__canvas .map-pin__label a:hover {
	box-shadow: inset 0 -2px 0 var(--c-yellow);
}

.coverage-map__caption {
	display: flex;
	align-items: flex-start;
	gap: 0.6rem;
	font-size: var(--fs-small);
	color: var(--ink-soft);
	padding-top: 0.85rem;
	max-width: 52ch;
}

/* A swatch of the shaded ring, so the caption explains what it is describing. */
.coverage-map__key {
	flex: none;
	width: 0.85rem;
	height: 0.85rem;
	margin-top: 0.35em;
	background: rgba(242, 195, 0, 0.35);
	border: 1px dashed var(--hair-strong);
}

@media (min-width: 900px) {
	.coverage-map__canvas { aspect-ratio: 1 / 1; }
}


/* --------------------------------------------------------------------------
   25  Service and area page columns

   The reading column and the job sheet beside it. The sheet stays with the
   reader as they go down a long service page, which is where the telephone
   number and the quote button live.
   -------------------------------------------------------------------------- */

@media (min-width: 1024px) {
	.page-aside {
		/* A stretched grid item has no room to move, so it must size to its
		   own content before sticky means anything. */
		align-self: start;
		position: sticky;
		top: calc(var(--nav-h) + var(--s-5));
	}
}

/* Sticky positioning and a scrolling page do not mix on a short viewport: the
   panel would cover the content it is meant to sit beside. */
@media (max-height: 44rem) {
	.page-aside { position: static; }
}

@media (prefers-reduced-motion: reduce) {
	.page-aside { position: static; }
}
