/*
 * Section library.
 *
 * One block per section type, with variants as modifier classes. Anything a template can
 * ask for is defined here — templates never carry their own styling, which is what keeps
 * a new page type from needing new CSS.
 */

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

.s-hero {
	position: relative;
	padding-block: clamp(var(--space-12), 7vw, var(--space-20));
	background: var(--colour-surface-dark);
	color: var(--colour-on-dark);
	isolation: isolate;
}

.s-hero--photo,
.s-hero--compact {
	/*
	 * Horizontal gradient, matching the homepage hero: the copy sits in the dark side and
	 * the photograph stays legible on the right. An earlier 88deg version darkened the
	 * whole frame and washed the image out.
	 */
	background-image: linear-gradient(90deg, rgb(0 0 0 / 0.78) 0%, #00000000 64%), var(--hero-image);
	background-size: cover;
	background-position: center;
}

.s-hero--photo { min-height: 450px; display: flex; align-items: center; }   /* measured */
.s-hero--compact { min-height: 26rem; display: flex; align-items: flex-end; }

/*
 * Copy is pinned left and given a measure. Without the cap it stretches across the whole
 * frame on a wide screen and runs over the subject's face.
 */
.s-hero--photo .wrap > *,
/*
 * Measured on live's careers hero at 1600: the heading runs to 600 and the standfirst to
 * 497, both at 18px. A single 41rem measure for the pair took the copy to 656 and ran the
 * standfirst across the person in the photograph.
 */
.s-hero--photo .s-hero__heading { max-width: 600px; }

.s-hero--photo .s-hero__standfirst {
	max-width: 497px;
	font-size: var(--text-body);
}

.s-hero--photo .s-hero__heading {
	font-size: var(--text-h2-lg);   /* 60.8px — measured on the live careers hero */
	line-height: 1;
}
.s-hero--plain { background: var(--colour-surface); color: var(--colour-heading); }

.s-hero__eyebrow {
	margin: 0 0 var(--space-4);
	color: var(--colour-blue-bright);
	font-size: var(--text-accent);
	font-weight: var(--weight-semibold);
	letter-spacing: 0.06em;
	text-transform: uppercase;
}

.s-hero__heading {
	max-width: 26ch;
	margin: 0;
	color: inherit;
	font-size: clamp(2.4rem, 1.6rem + 3vw, 3.8rem);
	font-weight: var(--weight-light);
	line-height: 1.05;
}

.s-hero--plain .s-hero__heading { color: var(--colour-heading); max-width: 34ch; }

/*
 * The standfirst inherits its colour from the photographic hero, where it sits on a dark
 * wash and is correctly white. On the plain variant the ground is light, so it was white on
 * white: present in the DOM, invisible on screen, and invisible to anyone reviewing the page
 * by eye. The tagline has the same inheritance.
 */
.s-hero--plain .s-hero__standfirst { color: var(--colour-body); }
.s-hero--plain .s-hero__tagline { color: var(--colour-accent, #0082CA); }

.s-hero__standfirst {
	max-width: 54ch;
	margin: var(--space-6) 0 0;
	font-size: 1.25rem;
	line-height: 1.5;
	color: inherit;
	opacity: 0.92;
}

/* Prose ----------------------------------------------------------------- */

.wrap--prose { max-width: 46rem; }

.s-prose { padding-block: clamp(var(--space-10), 5vw, var(--space-16)); }
.s-prose--grey { background: var(--colour-surface-grey); }

.s-prose__heading {
	margin-block-end: var(--space-6);
	font-size: clamp(1.6rem, 1.2rem + 1.2vw, 2.1rem);
	font-weight: var(--weight-light);
	line-height: 1.15;
}

.s-prose__body > * + * { margin-block-start: var(--space-5); }
.s-prose__body p { max-width: none; }

.s-prose__body :is(h2, h3) {
	margin-block: var(--space-8) var(--space-3);
	font-size: 1.4rem;
	font-weight: var(--weight-semibold);
}

.s-prose__body ul { padding-inline-start: 1.2em; list-style: disc; }
.s-prose__body li + li { margin-block-start: var(--space-2); }

/* Stat panel ------------------------------------------------------------ */

.s-stats {
	padding-block: clamp(var(--space-12), 6vw, var(--space-20));
	background: var(--colour-surface-grey);
}

.s-stats--dark {
	background: var(--colour-surface-dark);
	color: var(--colour-on-dark);
}

.s-stats__heading {
	margin-block-end: var(--space-10);
	color: inherit;
	font-size: clamp(1.6rem, 1.2rem + 1.2vw, 2.1rem);
	font-weight: var(--weight-light);
	text-align: center;
}

.s-stats__list {
	display: grid;
	gap: var(--space-8);
	margin: 0;
	padding: 0;
	max-width: none;
	list-style: none;
}

@media (min-width: 48rem) {
	.s-stats__list { grid-template-columns: repeat(min(var(--count), 4), 1fr); }
}

.s-stats__item {
	padding-inline-start: var(--space-5);
	border-inline-start: 2px solid var(--colour-blue-bright);
}

.s-stats__figure {
	display: block;
	color: var(--colour-blue-bright);
	font-size: clamp(2.2rem, 1.6rem + 2vw, 3.2rem);
	font-weight: var(--weight-light);
	line-height: 1;
}

.s-stats__label {
	display: block;
	margin-block-start: var(--space-3);
	font-size: var(--text-accent);
	line-height: 1.4;
	opacity: 0.85;
}

/* Quote ----------------------------------------------------------------- */

.s-quote { padding-block: clamp(var(--space-12), 6vw, var(--space-20)); }
.s-quote--grey { background: var(--colour-surface-grey); }

.s-quote__figure { max-width: 52rem; margin: 0 auto; text-align: center; }

.s-quote__text p {
	max-width: none;
	margin: 0;
	color: var(--colour-heading);
	font-size: clamp(1.4rem, 1.1rem + 1.2vw, 2rem);
	font-weight: var(--weight-light);
	line-height: 1.35;
}

.s-quote__by {
	margin-block-start: var(--space-6);
	font-size: var(--text-accent);
	font-weight: var(--weight-semibold);
	white-space: pre-line;
}

/* Card grid ------------------------------------------------------------- */

.s-grid { padding-block: clamp(var(--space-12), 6vw, var(--space-20)); }
.s-grid--grey { background: var(--colour-surface-grey); }

.s-grid__heading {
	margin-block-end: var(--space-10);
	font-size: clamp(1.6rem, 1.2rem + 1.2vw, 2.1rem);
	font-weight: var(--weight-light);
	text-align: center;
}

/* article — body beside a sidebar ------------------------------------------------ */

/*
 * Measured on live at a 1360 column: article 867, sidebar 453, 40px between them, summing
 * to the column exactly. Given as fractions so the proportions hold at any width.
 */
.s-article { padding-block: clamp(var(--space-10), 5vw, var(--space-16)); }

.s-article__inner { display: grid; gap: 40px; }

@media (min-width: 62rem) {
	.s-article__inner { grid-template-columns: minmax(0, 867fr) minmax(0, 453fr); }
	.s-article__main { padding-inline-start: 40px; }
	.s-article__aside { padding: 0 45px var(--space-10); }
}

/* The post's own image opens the article; the hero band is shared across all of them. */
.s-article__figure { margin: 0 0 var(--space-8); }

.s-article__figure img {
	display: block;
	width: 100%;
	height: auto;
	border-radius: 4px;
}

/* 18px bold on live, same size as the body — a lead, not a heading. */
.s-article__lead {
	max-width: none;
	margin-block-end: var(--space-5);
	color: var(--colour-heading);
}

.s-article__main .s-prose__body { margin-block-start: var(--space-8); }

.s-article__aside-heading {
	margin-block: var(--space-8) var(--space-5);
	padding-block-start: var(--space-6);
	border-block-start: 1px solid var(--colour-blue-bright);
	color: var(--colour-blue);
	font-size: 1.4rem;
}

/* search ------------------------------------------------------------------------- */

.s-search { display: flex; }

.s-search__field {
	flex: 1;
	min-width: 0;
	padding: var(--space-3) var(--space-4);
	border: 1px solid var(--colour-rule, #e0e0e0);
	border-inline-end: 0;
	background: var(--colour-surface);
	font: inherit;
}

.s-search__submit {
	flex: none;
	padding-inline: var(--space-5);
	border: 0;
	background: var(--colour-blue);
	color: var(--colour-on-dark);
	font-size: 1.1rem;
	cursor: pointer;
}

.s-search__submit:hover { background: var(--colour-blue-dark); }

/* recent posts -------------------------------------------------------------------- */

.s-recent { display: grid; gap: var(--space-6); margin: 0; padding: 0; list-style: none; }

.s-recent__item { display: grid; grid-template-columns: 80px 1fr; gap: var(--space-4); align-items: start; }

.s-recent__media img { width: 80px; height: 80px; object-fit: cover; border-radius: 4px; }

.s-recent__title {
	display: block;
	color: var(--colour-heading);
	font-weight: var(--weight-semibold);
	line-height: 1.35;
	text-decoration: none;
}

.s-recent__title:hover { color: var(--colour-blue); }

.s-recent__more { display: block; margin-block-start: var(--space-1); color: var(--colour-blue); font-size: 0.9375rem; }

/* banner hero — used by articles ------------------------------------------------- */

/*
 * Measured on live's article pages at 1585: a full-bleed band 300px tall carrying the
 * featured image, with the title centred in white across a 1000px measure at 67.2px.
 * Distinct from the photo hero, which is left-aligned with copy beside the subject.
 */
.s-hero--banner {
	display: flex;
	align-items: center;
	min-height: 300px;
	background-color: var(--colour-blue);
	/*
	 * Brand-blue wash at 35%, not a dark one — measured off live, which paints the same
	 * #0081C9 over the artwork at opacity 0.35. It pulls a light photograph down far enough
	 * for white type to read while keeping the band blue rather than grey. Written as a
	 * gradient layer instead of a pseudo-element so the tint travels with the background and
	 * nothing has to be stacked above it.
	 *
	 * The colour beneath is the same blue, so a post whose artwork is missing or still
	 * loading shows a brand band rather than a black one.
	 */
	background-image: linear-gradient(rgb(0 129 201 / 0.35), rgb(0 129 201 / 0.35)), var(--hero-image, none);
	background-size: cover;
	background-position: center;
	text-align: center;
}

.s-hero--banner .s-hero__heading {
	max-width: 1000px;
	margin-inline: auto;
	color: var(--colour-on-dark);
	/* Reaches live's 67.2px by the time the column is full width. */
	font-size: clamp(2.2rem, 1.2rem + 3.2vw, 4.2rem);
	font-weight: var(--weight-light);
	line-height: 1.15;
}

.s-hero--banner .s-hero__standfirst {
	max-width: 780px;
	margin-inline: auto;
	color: var(--colour-on-dark);
}

/* byline — author, date and reading time under an article heading ---------------- */

.s-byline { padding-block-start: var(--space-6); }

.s-byline__line {
	display: flex;
	flex-wrap: wrap;
	gap: var(--space-2) var(--space-5);
	margin: 0;
	color: var(--colour-muted, #6a6a6a);
	font-size: 0.9375rem;
}

.s-byline__author { font-weight: var(--weight-semibold); color: var(--colour-heading); }

/* archive hero — title beside an image, as live sets /insider-news/ out ----------- */

/*
 * Measured on live at a 1360 column: row 450 tall, split into two 680 halves. Title left,
 * inset 50, 60.8px at weight 300. Image right, 680x400, cover, background-position 100% 50%,
 * rounded 10px on its right corners only.
 */
.s-archive-hero__inner { display: grid; align-items: center; min-height: 450px; }

@media (min-width: 62rem) {
	.s-archive-hero__inner { grid-template-columns: 1fr 1fr; }
}

/*
 * The two halves are one 1360x400 card: grey panel left rounded on its left corners, image
 * right rounded on its right. Measured on live — panel #f2f2f2, radius 10px, copy inset 50.
 */
.s-archive-hero__copy {
	display: flex;
	flex-direction: column;
	justify-content: center;
	height: 400px;
	padding-inline: 50px;
	border-radius: 10px 0 0 10px;
	background: var(--colour-surface-grey);
}

.s-archive-hero__title {
	margin: 0;
	color: var(--colour-heading);
	font-size: 3.8rem;
	font-weight: 300;
	line-height: 1;
}

/* 18px / 27px on live. */
.s-archive-hero__lede {
	max-width: 26rem;
	margin-block-start: var(--space-5);
	line-height: 1.5;
}

.s-archive-hero__media {
	height: 400px;
	border-radius: 0 10px 10px 0;
	background-image: var(--hero-image);
	background-size: cover;
	background-position: 100% 50%;
}

@media (max-width: 62rem) {
	.s-archive-hero__copy { height: auto; padding: var(--space-10) var(--space-8); border-radius: 10px; }
	.s-archive-hero__title { font-size: 2.6rem; }
	.s-archive-hero__media { height: 260px; border-radius: 10px; margin-block-start: var(--space-4); }
}

/* archive head — centred, used by the Insider News and case study listings -------- */

/*
 * Measured on live's /insider-news/ at a 1360 column: heading 44.8px centred across the
 * full column, intro centred and held to 858px. Live wraps this in ~600px of empty
 * container above it, which is a defect rather than a design and is not reproduced.
 */
.s-archive-head { padding-block: clamp(var(--space-12), 6vw, var(--space-20)) 0; }

.s-archive-head__heading {
	margin: 0;
	font-size: var(--text-h2);
	font-weight: var(--weight-light);
	text-align: center;
}

.s-archive-head__intro {
	max-width: 858px;
	margin: var(--space-6) auto 0;
	text-align: center;
}

/*
 * Measured on live's listing: card 318 wide on #f2f2f2 at 10px radius, image flush to the
 * top, copy inset 22, title 22.4px/600, then a date and a read-more pinned to the foot so
 * a row lines up whatever length the summaries run to.
 */
.s-grid__item {
	display: flex;
	overflow: hidden;
	border-radius: 10px;
	background: var(--colour-surface-grey);
}

/*
 * The hidden attribute has to be honoured explicitly.
 *
 * Browsers style [hidden] as display:none in their own stylesheet, and any author rule beats a
 * UA rule, so the display:flex above silently outranked it: the filter set item.hidden on every
 * non-matching card and every card stayed on screen. Class plus attribute outranks class alone,
 * so this needs no !important.
 */
.s-grid__item[hidden] {
	display: none;
}

.s-grid__link {
	display: flex;
	flex-direction: column;
	width: 100%;
	/* The whole card is one link, so the copy inside must not take the link colour. */
	color: var(--colour-body);
	text-decoration: none;
}

.s-grid__media { display: block; }

.s-grid__media img {
	display: block;
	width: 100%;
	aspect-ratio: 16 / 9;
	object-fit: cover;
}

.s-grid__body {
	display: flex;
	flex: 1;
	flex-direction: column;
	padding: 22px;
}

.s-grid__summary { margin-block-start: var(--space-3); }

.s-grid__meta {
	display: flex;
	align-items: center;
	gap: var(--space-2);
	margin-block-start: auto;
	padding-block-start: var(--space-6);
	color: var(--colour-heading);
	font-size: 1.125rem;
}

.s-grid__meta-icon { width: 1em; height: 1em; color: var(--colour-blue); }

.s-grid__more {
	display: inline-flex;
	align-items: center;
	gap: var(--space-2);
	margin-block-start: var(--space-3);
	color: var(--colour-blue);
	font-weight: var(--weight-semibold);
}

.s-grid__more-icon { width: 0.9em; height: 0.9em; }

.s-grid__link:hover .s-grid__more { text-decoration: underline; }

.s-grid__list {
	display: grid;
	/* 33px on live's listing, measured across four 314px cards in a 1360 column. */
	gap: 33px;
	margin: 0;
	padding: 0;
	max-width: none;
	list-style: none;
}

@media (min-width: 40rem) { .s-grid__list { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 62rem) { .s-grid__list { grid-template-columns: repeat(var(--columns), 1fr); } }

/* Lift on hover; the panel itself is on .s-grid__item so nothing repaints over it. */
.s-grid__item {
	transition: transform 300ms ease, box-shadow 300ms ease;
}

.s-grid__item:hover {
	transform: translateY(-3px);
	box-shadow: 0 12px 28px rgb(0 0 0 / 0.1);
}

.s-grid__media { display: block; overflow: hidden; }

/*
 * Full colour, and 314x180 as measured on live's listing. These were greyscale until hover,
 * which is the treatment the photographs elsewhere use — but a card image is the article's
 * own picture and reads as broken when it arrives grey.
 */
.s-grid__media img {
	width: 100%;
	aspect-ratio: 314 / 180;
	object-fit: cover;
	transition: scale 500ms ease;
}

.s-grid__link:hover .s-grid__media img { scale: 1.04; }

.s-grid__eyebrow {
	color: var(--colour-blue);
	font-size: 0.8rem;
	font-weight: var(--weight-semibold);
	letter-spacing: 0.06em;
	text-transform: uppercase;
}

/* 22.4px on live's listings. */
.s-grid__title {
	color: var(--colour-heading);
	font-size: 1.4rem;
	font-weight: var(--weight-semibold);
	line-height: 1.25;
}

.s-grid__summary { font-size: var(--text-accent); line-height: 1.5; }

/* Closing CTA ----------------------------------------------------------- */

.s-cta {
	padding-block: clamp(var(--space-12), 6vw, var(--space-20));
	background: var(--colour-surface-grey);
	text-align: center;
}

.s-cta--ink { background: var(--colour-surface-dark); color: var(--colour-on-dark); }
.s-cta--ink .s-cta__heading { color: var(--colour-on-dark); }

.s-cta__heading {
	font-size: clamp(1.8rem, 1.3rem + 1.6vw, 2.6rem);
	font-weight: var(--weight-light);
}

.s-cta__body { max-width: 46ch; margin: 0 auto var(--space-8); }

/*
 * Photo variant: copy on the left, the picture occupying the right and dissolving into the
 * ink as it comes back towards the text.
 *
 * The picture is its own layer rather than a background on the section, because the fade
 * then belongs to the picture. A gradient painted across the section would be a fraction of
 * the viewport instead, so the point at which it cleared would slide across the composition
 * as the window resized and would sooner or later sit right on the sensor.
 */
.s-cta--photo {
	position: relative;
	isolation: isolate;
	overflow: hidden;
	background: var(--colour-surface-dark);
	color: var(--colour-on-dark);
	text-align: start;
}

.s-cta--photo .s-cta__heading { color: var(--colour-on-dark); }
.s-cta--photo .s-cta__body { margin-inline: 0; }

/*
 * Hidden by default. Below this width there is no room for a picture and a readable measure
 * side by side, and the honest answer is to drop the picture rather than shrink the words.
 */
.s-cta__photo { display: none; }

/*
 * Centre variant: the picture runs the full width and the copy sits on it.
 *
 * For a photograph whose subject is the middle distance rather than one side. The side
 * variant would crop this one to its right-hand 56%, which on the construction road is a
 * fence and a site office, and throw away the thing the picture is actually of.
 *
 * No wash. Measured on that photograph, the sky above the horizon runs 1.75:1 to 2.8:1
 * against white and the road below it runs 11.5:1 to 13:1, so the copy is legible if, and
 * only if, it stays on the road. The band is proportioned to put it there: at 3/1 it is
 * wider than the 2.13 picture, so `cover` scales to the width and the crop comes off the
 * top, and `bottom` anchoring keeps the road. A future picture for this slot needs the same
 * shape, dark across its lower half.
 */
.s-cta--photo-centre {
	position: relative;
	isolation: isolate;
	overflow: hidden;
	background: var(--colour-surface-dark);
	color: var(--colour-on-dark);
	text-align: center;
}

.s-cta--photo-centre .s-cta__heading { color: var(--colour-on-dark); }

@media (min-width: 64rem) {

	.s-cta--photo-centre {
		display: flex;
		align-items: flex-end;
		justify-content: center;
		/*
		 * Taller than a side-photo band, and deliberately. The road is the bottom 40% of the
		 * frame and the copy has to sit inside it; since `cover` here is height-driven, the
		 * only thing that makes that 40% taller in pixels is a taller band. At 17/10 it comes
		 * to about 300px, which fits the heading, five lines and the button with room left.
		 * Shorter bands squeeze the copy up onto the sky, which runs 1.75:1 against white.
		 */
		/*
		 * Width stated, not inferred, for the same reason the solution hero states it. With
		 * aspect-ratio and a max-height and no width, the cap wins on a wide screen and the
		 * width is then derived back from the capped height: at 2000px the band collapsed to
		 * 737px and sat in a field of white with the photograph cropped to a bare wall.
		 */
		width: 100%;
		aspect-ratio: 17 / 10;
		max-height: 46rem;
	}

	/*
	 * Sits in the lower 40%, not the middle of the band. The section's own generous block
	 * padding is what held the copy 109px clear of the bottom, which pushed the heading up to
	 * 50% of the frame, onto the treeline and sky. Dropped here and replaced with a small
	 * gap on the wrap, so the block lands between 61% and 96%: on the road, top and bottom.
	 */
	.s-cta--photo-centre {
		padding-block: 0;
	}

	.s-cta--photo-centre > .wrap {
		padding-block-end: 1.75rem;
	}

	/*
	 * Copy through the middle of the frame rather than along the bottom. The low placement
	 * above suits a photograph whose clear band is its floor, such as the construction road.
	 * The corrections closing band is the opposite: a plain block wall through the middle with
	 * a polished floor beneath it, and copy pinned low sat half on the floor and half on the
	 * skirting. Measured across the middle band it is 11.4:1 against white, so the type needs
	 * no help beyond being put in the right place.
	 */
	.s-cta--photo-middle {
		align-items: center;
	}

	.s-cta--photo-middle > .wrap {
		padding-block-end: 0;
	}

	.s-cta--photo-centre .s-cta__photo {
		display: block;
		position: absolute;
		inset: 0;
		/*
		 * Explicit, because the side variant's `width: min(56%, 1400px)` is a real width and
		 * beats the `inset` shorthand's right offset. Without this the picture stopped at 56%
		 * and the rest of the band was flat ink.
		 */
		width: auto;
		z-index: -1;
		background-image: var(--cta-image);
		background-size: cover;
		background-position: center bottom;
		-webkit-mask-image: none;
		mask-image: none;
	}
}

@media (min-width: 64rem) {
	/*
	 * Height grows with the viewport, because the picture box widens with it and a fixed
	 * height would mean an ever deeper crop. Capped, so it stays a band.
	 */
	.s-cta--photo { min-height: clamp(30rem, 26vw, 40rem); display: flex; align-items: center; }

	.s-cta--photo .wrap > * { max-width: 30rem; }

	.s-cta__photo {
		display: block;
		position: absolute;
		inset-block: 0;
		inset-inline-end: 0;
		z-index: -1;
		/*
		 * Capped in pixels as well as percent. On an ultrawide screen a percentage box gets
		 * wide enough that `cover` scales the picture past the band height and the crop eats
		 * the composition from both ends.
		 */
		width: min(56%, 1400px);
		background-image: var(--cta-image);
		background-size: cover;
		/*
		 * Anchored to the bottom. The bed is in the lowest fifth of the frame and the centre
		 * crop was cutting it off entirely, which left a picture of a wall and a sensor
		 * pointing at nothing.
		 */
		background-position: center bottom;
		/* The fade is a fraction of the picture, which is the whole reason it is here. */
		-webkit-mask-image: linear-gradient(90deg, rgb(0 0 0 / 0) 0%, rgb(0 0 0 / 1) 30%);
		mask-image: linear-gradient(90deg, rgb(0 0 0 / 0) 0%, rgb(0 0 0 / 1) 30%);
	}
}

/* Quote wall ------------------------------------------------------------ */

.s-wall { padding-block: clamp(var(--space-12), 6vw, var(--space-20)); }
.s-wall--grey { background: var(--colour-surface-grey); }

.s-wall__heading {
	margin-block-end: var(--space-10);
	font-size: clamp(1.6rem, 1.2rem + 1.2vw, 2.1rem);
	font-weight: var(--weight-light);
	text-align: center;
}

.s-wall__list {
	display: grid;
	gap: var(--space-8);
	margin: 0;
	padding: 0;
	max-width: none;
	list-style: none;
}

@media (min-width: 48rem) {
	.s-wall__list { grid-template-columns: repeat(var(--count), 1fr); }
}

.s-wall__item {
	display: flex;
	flex-direction: column;
	gap: var(--space-5);
	padding: var(--space-8);
	border-radius: var(--radius);
	background: var(--colour-surface);
	border-block-start: 3px solid var(--colour-blue-bright);
}

.s-wall__quote { margin: 0; flex: 1; }

.s-wall__quote p {
	max-width: none;
	margin: 0;
	color: var(--colour-heading);
	font-size: 1.15rem;
	font-weight: var(--weight-light);
	line-height: 1.45;
}

.s-wall__by { margin: 0; max-width: none; font-size: var(--text-accent); line-height: 1.4; }
.s-wall__name { display: block; font-weight: var(--weight-semibold); color: var(--colour-heading); }
.s-wall__org { display: block; opacity: 0.8; }

/* Logo strip ------------------------------------------------------------ */

.s-logos { padding-block: clamp(var(--space-10), 5vw, var(--space-16)); }
.s-logos--grey { background: var(--colour-surface-grey); }

.s-logos__heading {
	margin-block-end: var(--space-4);
	font-size: clamp(1.6rem, 1.2rem + 1.2vw, 2.1rem);
	font-weight: var(--weight-light);
	text-align: center;
}

.s-logos__intro { max-width: 46rem; margin: 0 auto var(--space-10); text-align: center; }

.s-logos__list {
	display: flex;
	flex-wrap: wrap;
	gap: clamp(var(--space-8), 5vw, var(--space-16));
	align-items: center;
	justify-content: center;
	margin: 0;
	padding: 0;
	max-width: none;
	list-style: none;
}

.s-logos__item img {
	width: auto;
	max-height: 44px;
	object-fit: contain;
	/*
	 * Full colour. Partner logos are the one place greyscale is the wrong call — these are
	 * other companies' marks, and Microsoft, Telstra and VMware are recognised by their
	 * colour. Photographs elsewhere still desaturate until hover.
	 */
}


/* Card row -------------------------------------------------------------- */
/*
 * One section, three variants — this replaces the homepage's pillars, dark service
 * cards, calculator band and "how we make a difference" row. They read as four
 * different designs because of the variant, not because they are four components.
 */

.s-row { padding-block: clamp(var(--space-12), 6vw, var(--space-20)); }

.s-row__head {
	max-width: 62.5rem;
	margin-inline: auto;
	margin-block-end: clamp(var(--space-8), 4vw, var(--space-16));
	text-align: center;
}

.s-row__heading {
	font-size: var(--h2-size, var(--text-h2));
	font-weight: var(--weight-light);
	line-height: var(--h2-lh, 1.15);
}

.s-row__intro { max-width: 43.75rem; margin-inline: auto; }

.s-row__list {
	display: grid;
	gap: 0;
	margin: 0;
	padding: 0;
	max-width: none;
	list-style: none;
}

.s-row__card { position: relative; display: flex; flex-direction: column; }
.s-row__body { display: flex; flex-direction: column; flex: 1; }
.s-row__card-heading { font-size: var(--text-card-h); }
.s-row__cta { margin-block-start: var(--space-10); max-width: none; text-align: center; }

/* photo-band — one photograph spanning the row, panels divided by hairlines */

/*
 * One photograph spans the band and is left undimmed. Each card is a translucent grey
 * panel laid over it, and the grid gap lets the picture through at full strength between
 * them — that bright seam is the separator. It used to be a single dark wash over the
 * whole photo with a white hairline between cards, which read as one flat slab.
 */
.s-row--photo-band .s-row__list {
	background-image: var(--band);
	/*
	 * cover, not an enlarged size. Scaling the photo to 160% of the viewport height blew
	 * the width up with it and cropped into the middle of the frame, so the band showed a
	 * zoomed-in slice of torsos rather than the picture.
	 */
	background-size: cover;
	background-position: center center;
	/* Behind the photo, in case an odd aspect leaves a sliver: dark, not page white. */
	background-color: #1c1e20;
	gap: 8px;
}

/*
 * Pinned to the viewport, so scrolling past the band walks the panels across a stationary
 * photograph. Only the sections that ask for it — the rest keep a still photo.
 */
.s-row--bleed .s-row__list { background-attachment: fixed; }

/*
 * Touch browsers — iOS Safari in particular — do not composite a fixed background against
 * a scrolling page, so it either judders or is dropped outright. Those get the ordinary
 * scrolling background instead of a broken fixed one.
 */
@media (hover: none), (max-width: 700px) {
	.s-row--bleed .s-row__list { background-attachment: scroll; }
}

@media (min-width: 55rem) {
	/*
	 * Shared row tracks, so the three cards line up with each other instead of each
	 * stacking on its own. Rows are: icon, slack, heading, copy, link. The slack row
	 * takes whatever is left over, which pins the icon to the top and the rest to the
	 * bottom; the auto rows size to the tallest card, so a card with five lines of copy
	 * no longer pushes its own heading above its neighbours'.
	 */
	.s-row--photo-band .s-row__list {
		grid-auto-flow: column;
		grid-auto-columns: 1fr;
		grid-template-rows: auto 1fr auto auto auto;
		min-height: 515px;
	}

}

.s-row--photo-band .s-row__card {
	/*
	 * Each card spans every row and borrows the parent's track sizes, so its heading and
	 * link sit on the same lines as the other cards'. Every card carries identical
	 * padding, so the inset a subgrid takes from its own padding is identical too.
	 */
	display: grid;
	grid-row: 1 / -1;
	grid-template-rows: subgrid;
	padding: var(--space-10);
	background-color: rgb(28 30 32 / 0.52);
	color: var(--colour-on-dark);
	transition: background-color 220ms ease;
}

/* Flattened so the heading, copy and link become items of the card's subgrid. */
.s-row--photo-band .s-row__body { display: contents; }

.s-row--photo-band .s-row__icon { grid-row: 1; }
.s-row--photo-band .s-row__card-heading { grid-row: 3; align-self: end; }
.s-row--photo-band .s-row__card p { grid-row: 4; }
.s-row--photo-band .s-row__card .link-arrow { grid-row: 5; align-self: end; justify-self: start; }

/*
 * focus-within as well as hover: the panels contain links, so someone tabbing through
 * gets the same feedback a mouse gives.
 */
.s-row--photo-band .s-row__card:hover,
.s-row--photo-band .s-row__card:focus-within {
	background-color: rgb(28 30 32 / 0.68);
}

@media (prefers-reduced-motion: reduce) {
	.s-row--photo-band .s-row__card { transition: none; }
}


/*
 * In flow, not absolute. Positioning it absolutely put it at the card's top-left while
 * the copy also sat at the top, so the icon landed on top of the heading. Live stacks
 * them: icon at the top, copy pushed to the bottom of the card.
 */
.s-row--photo-band .s-row__icon {
	width: 70px;
	height: 70px;
	color: var(--colour-on-dark);
}

.s-row--photo-band .s-row__card-heading { color: var(--colour-on-dark); }
.s-row--photo-band .s-row__card p { max-width: 34ch; margin-block-end: var(--space-12); }

/* Measured on live at 1600: heading 1000 wide at 60.8px, intro 636 over three lines. */
.s-row--photo-band .s-row__heading { font-size: var(--text-h2-lg); }
.s-row--photo-band .s-row__intro { max-width: 39.75rem; }

/* Live centres the button under the band; it was sitting hard left. */
.s-row--photo-band .s-row__cta { text-align: center; }
.s-row--photo-band a { color: var(--colour-on-dark); }

/* icon-dark — four cards on ink, icons above, pill link at the foot */

/* Live sets the calculator band on grey; the default is page white. */
.s-row--grey { background: var(--colour-surface-grey); }

.s-row--icon-dark { background: var(--colour-surface-dark); color: var(--colour-on-dark); }

@media (min-width: 40rem) { .s-row--icon-dark .s-row__list { grid-template-columns: repeat(2, 1fr); gap: var(--space-10) var(--space-8); } }
@media (min-width: 65rem) { .s-row--icon-dark .s-row__list { grid-template-columns: repeat(4, 1fr); } }

.s-row--icon-dark .s-row__card { align-items: flex-start; }
.s-row--icon-dark .s-row__icon { width: 80px; height: 80px; margin-block-end: var(--space-5); color: var(--colour-on-dark); }
.s-row--icon-dark .s-row__card-heading { color: var(--colour-on-dark); }
.s-row--icon-dark p { flex: 1; }

/* icon-plain — centred icons on white */

.s-row--icon-plain .s-row__list { gap: var(--space-12); text-align: center; }

@media (min-width: 55rem) {
	.s-row--icon-plain .s-row__list { grid-template-columns: repeat(3, 1fr); }
}

.s-row--icon-plain .s-row__icon {
	width: 80px;
	height: 80px;
	margin: 0 auto var(--space-5);
	color: var(--colour-blue);
}

.s-row--icon-plain p { margin-inline: auto; max-width: 34ch; }

/* In the dark variant the card link reads as a pill button, matching the live site. */
.s-row--icon-dark .link-arrow {
	align-self: flex-start;
	margin-block-start: var(--space-5);
	padding: var(--space-3) var(--space-5);
	border-radius: var(--radius-pill);
	background: var(--colour-blue);
	color: var(--colour-on-dark);
}

.s-row--icon-dark .link-arrow:hover { background: var(--colour-blue-dark); text-decoration: none; }

/* Open roles ------------------------------------------------------------ */

.s-roles { padding-block: clamp(var(--space-12), 6vw, var(--space-20)); }
.s-roles--grey { background: var(--colour-surface-grey); }

.s-roles__heading {
	margin-block-end: var(--space-4);
	font-size: clamp(1.6rem, 1.2rem + 1.2vw, 2.1rem);
	font-weight: var(--weight-light);
	text-align: center;
}

.s-roles__intro { max-width: 46rem; margin: 0 auto var(--space-10); text-align: center; }
.s-roles__empty { max-width: 44rem; margin-inline: auto; text-align: center; }

.s-roles__list { display: grid; gap: var(--space-4); margin: 0; padding: 0; max-width: 52rem; margin-inline: auto; list-style: none; }

.s-roles__link {
	display: grid;
	gap: var(--space-2);
	padding: var(--space-6) var(--space-8);
	border-radius: var(--radius);
	border-inline-start: 3px solid var(--colour-blue);
	background: var(--colour-surface);
	color: inherit;
	text-decoration: none;
	transition: transform 200ms ease, box-shadow 200ms ease;
}

.s-roles--plain .s-roles__link { background: var(--colour-surface-grey); }


.s-roles__title { color: var(--colour-heading); font-size: 1.25rem; font-weight: var(--weight-semibold); }
.s-roles__meta { color: var(--colour-blue-text); font-size: var(--text-accent); font-weight: var(--weight-semibold); }
.s-roles__summary { font-size: var(--text-accent); line-height: 1.5; }

/* Form band ------------------------------------------------------------- */

.s-form { padding-block: clamp(var(--space-12), 6vw, var(--space-20)); background: var(--colour-surface-grey); }
.s-form--ink { background: var(--colour-surface-dark); color: var(--colour-on-dark); }
.s-form--ink .s-form__heading { color: var(--colour-on-dark); }

.s-form__inner { display: grid; gap: clamp(var(--space-8), 5vw, var(--space-16)); align-items: start; }

@media (min-width: 60rem) { .s-form__inner { grid-template-columns: 1fr 1.1fr; } }

.s-form__heading {
	font-size: clamp(1.8rem, 1.3rem + 1.6vw, 2.6rem);
	font-weight: var(--weight-light);
}

.s-form__form {
	padding: clamp(var(--space-6), 3vw, var(--space-10));
	border-radius: var(--radius);
	background: var(--colour-surface);
}

/* Gravity Forms ships its own styling; these bring it in line with the design system. */
.s-form__form .gform_wrapper form { margin: 0; }
.s-form__form .gfield_label { font-size: var(--text-accent); font-weight: var(--weight-semibold); color: var(--colour-heading); }

.s-form__form input[type="text"],
.s-form__form input[type="email"],
.s-form__form input[type="tel"],
.s-form__form select,
.s-form__form textarea {
	width: 100%;
	padding: var(--space-3) var(--space-4);
	border: 1px solid var(--colour-rule);
	border-radius: var(--radius);
	background: var(--colour-surface);
	font-family: inherit;
	font-size: var(--text-accent);
}

.s-form__form input:focus-visible,
.s-form__form select:focus-visible,
.s-form__form textarea:focus-visible { outline: var(--focus-ring); outline-offset: 1px; border-color: var(--colour-blue); }

/*
 * The submit button had no top margin at all, so it sat flush against whatever the last
 * field happened to be. Invisible while that was a textarea with its own border; obvious
 * once the reCAPTCHA box went back on, which reads as a panel and left the button glued to
 * its underside.
 *
 * More than the 20px field rhythm rather than equal to it: the button is an action, not
 * another input, and the extra step says so.
 */
.s-form__form .gform_footer {
	margin-block-start: var(--space-8);
}

.s-form__form .gform_button {
	padding: var(--space-5) var(--space-8);
	border: 0;
	border-radius: var(--radius-pill);
	background: var(--colour-blue);
	color: var(--colour-on-dark);
	font-family: inherit;
	font-size: var(--text-accent);
	font-weight: var(--weight-semibold);
	cursor: pointer;
	transition: background-color 150ms ease;
}

.s-form__form .gform_button:hover { background: var(--colour-blue-dark); }
.s-form__form .gform_required_legend { font-size: 0.8rem; color: var(--colour-body); }

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

.s-people { padding-block: clamp(var(--space-12), 6vw, var(--space-20)); }
.s-people--grey { background: var(--colour-surface-grey); }

.s-people__heading {
	margin-block-end: var(--space-4);
	font-size: clamp(1.6rem, 1.2rem + 1.2vw, 2.1rem);
	font-weight: var(--weight-light);
	text-align: center;
}

.s-people__intro { max-width: 46rem; margin: 0 auto var(--space-10); text-align: center; }

.s-people__list {
	display: grid;
	/* 20px on live, which puts the four portraits at 325 across a 1360 column. */
	gap: 20px;
	margin: 0;
	padding: 0;
	max-width: none;
	list-style: none;
}

@media (min-width: 40rem) { .s-people__list { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 62rem) { .s-people__list { grid-template-columns: repeat(4, 1fr); } }

.s-people__item { display: flex; flex-direction: column; gap: var(--space-1); }

.s-people__media { display: block; overflow: hidden; border-radius: var(--radius); margin-block-end: var(--space-4); }

.s-people__media img {
	width: 100%;
	/* Measured on live: 325x352, so a shade wider than tall-portrait 4/5. */
	aspect-ratio: 325 / 352;
	object-fit: cover;
	filter: grayscale(100%);
	transition: filter 500ms ease, scale 500ms ease;
}

.s-people__item:hover .s-people__media img { filter: none; scale: 1.03; }

.s-people__name { color: var(--colour-heading); font-size: 1.1rem; font-weight: var(--weight-semibold); }
.s-people__role { color: var(--colour-blue-text); font-size: var(--text-accent); }

/* Awards band ----------------------------------------------------------- */

.s-award { padding-block: var(--space-10); text-align: center; }
.s-award img { margin-inline: auto; max-width: min(100%, 420px); height: auto; }

/* Media grid ------------------------------------------------------------ */

.s-media { padding-block: clamp(var(--space-12), 6vw, var(--space-20)); }
.s-media--grey { background: var(--colour-surface-grey); }

.s-media__heading {
	margin-block-end: var(--space-10);
	font-size: clamp(1.6rem, 1.2rem + 1.2vw, 2.1rem);
	font-weight: var(--weight-light);
	text-align: center;
}

.s-media__list {
	display: grid;
	gap: var(--space-6);
	margin: 0;
	padding: 0;
	max-width: none;
	list-style: none;
}

@media (min-width: 40rem) { .s-media__list { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 62rem) { .s-media__list { grid-template-columns: repeat(var(--columns), 1fr); } }

.s-media__item { overflow: hidden; border-radius: 12px; }

.s-media__item img {
	width: 100%;
	aspect-ratio: 10 / 7;
	object-fit: cover;
	filter: grayscale(100%);
	transition: filter 500ms ease, scale 500ms ease;
}

.s-media__item:hover img { filter: none; scale: 1.03; }

/* Video ----------------------------------------------------------------- */

.s-video { padding-block: clamp(var(--space-12), 6vw, var(--space-20)); }

.s-video__heading {
	margin-block-end: var(--space-8);
	font-size: clamp(1.6rem, 1.2rem + 1.2vw, 2.1rem);
	font-weight: var(--weight-light);
	text-align: center;
}

.s-video__frame {
	position: relative;
	overflow: hidden;
	border-radius: 12px;
	background: var(--colour-surface-dark);
	aspect-ratio: 16 / 9;
}

/*
 * The embed carries width/height attributes so the box is reserved before it loads; the
 * frame's aspect ratio governs the rendered size, so the player stretches to fill it
 * rather than sitting at 1280x720 inside a wider frame.
 */
.s-video__player {
	display: block;
	width: 100%;
	height: 100%;
	border: 0;
}






/* Split band — media one side, copy the other ---------------------------- */

.s-split { padding-block: clamp(var(--space-12), 6vw, var(--space-20)); }

/*
 * Both columns start at the same line. Centring them hung the badges 88px below the copy,
 * because the media column is centred against the copy's height rather than sharing its
 * top edge.
 */
.s-split__inner { display: grid; gap: clamp(var(--space-8), 5vw, var(--space-16)); align-items: start; }

/*
 * Measured on live at a 1360 column: badge track 120–679 with the artwork centred in it,
 * copy 679–1371. The row deliberately stops 109px short of the column's right edge, so the
 * tracks are given as percentages of the container rather than fractions that fill it.
 */
@media (min-width: 55rem) {
	.s-split__inner { grid-template-columns: 41.1% 50.9%; gap: 0; }
}

/*
 * Offset by the copy's rule and the space under it, so the badges line up with the first
 * line of text rather than with the rule sitting above it.
 */
.s-split__media {
	display: flex;
	flex-wrap: wrap;
	gap: var(--space-4);
	align-items: start;
	justify-content: center;
	padding-block-start: calc(1px + var(--space-6));
}
/* 350x112 on live; the artwork is 768x245, so height alone sets the width. */
.s-split__media img { max-height: 112px; width: auto; }

/* A hairline rule above the copy, as on the live page. */
.s-split__copy { padding-block-start: var(--space-6); border-block-start: 1px solid var(--colour-blue-bright); }
.s-split__copy p { max-width: none; }
.s-split__copy p + p { margin-block-start: var(--space-4); }

/* card-row: heading beside the cards, as on the careers page ------------- */

@media (min-width: 62rem) {
}

/*
 * Fit what will fit, rather than always two. These cards live in the narrower 2fr track of
 * the heading-left split, so a fixed two-column grid squeezed them to about 15 characters
 * a line on a mid-width window and broke the card headings over three lines. 24rem is the
 * floor: below that the grid drops a column rather than letting the cards get narrower,
 * which gives three across the content column and a 3 + 2 arrangement for five cards.
 *
 * min(24rem, 100%) rather than a bare 24rem. A bare floor is a floor the track cannot go
 * below even when the container is narrower than it, so on a 375px phone the container
 * measured 335px while the track computed to 384px and every card hung 49px past the edge,
 * taking the whole document sideways with it. Wrapping it in min() lets the floor collapse
 * to the container on the one occasion it has to, and changes nothing above 24rem.
 */
.s-row--heading-left .s-row__grid {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(min(24rem, 100%), 1fr));
	gap: var(--space-6);
	align-items: start;
}

/*
 * The list box itself gets out of the way so its cards become items of the grid above,
 * sharing the tracks with the heading. Without this the whole list would sit in one cell.
 */
.s-row--heading-left .s-row__list { display: contents; }

/*
 * The heading occupies the grid's first cell, so it is centred in the square a card would
 * fill rather than sitting at the top of it. align-self overrides the grid's start
 * alignment for this one item, leaving the cards where they are.
 */
.s-row--heading-left .s-row__head {
	display: flex;
	flex-direction: column;
	justify-content: center;
	align-items: center;
	align-self: stretch;
	margin: 0;
	max-width: none;
	text-align: center;
}

/* 6em breaks "Why you'll want in" after the second word, and scales with the type. */
.s-row--heading-left .s-row__heading { max-width: 6em; margin: 0; font-size: clamp(2rem, 1.4rem + 2vw, 3rem); line-height: 1.1; }

.s-row--heading-left .s-row__card {
	padding: var(--space-8);
	border-radius: 12px;
	background: var(--colour-surface-grey);
	text-align: start;
}

.s-row--heading-left .s-row__icon { width: 64px; height: 64px; margin-block-end: var(--space-6); color: var(--colour-blue); }
.s-row--heading-left .s-row__card-heading { font-size: 1.35rem; }
.s-row--heading-left p { max-width: none; }

/* people-grid: white label card overlapping the portrait, then a pill link */

.s-people--overlap .s-people__item { position: relative; padding-block-end: var(--space-16); }
.s-people--overlap .s-people__media { border-radius: 12px; margin-block-end: 0; }

.s-people--overlap .s-people__label {
	position: relative;
	margin-block-start: -3.5rem;
	margin-inline: var(--space-4);
	padding: var(--space-4);
	border-radius: 10px;
	background: var(--colour-surface);
	box-shadow: 0 6px 18px rgb(0 0 0 / 0.08);
	text-align: center;
}

.s-people--overlap .s-people__name { display: block; font-size: 1.2rem; font-weight: var(--weight-regular); }
.s-people--overlap .s-people__role { display: block; margin-block-start: var(--space-1); font-weight: var(--weight-semibold); }
.s-people--overlap .s-people__link { position: absolute; inset-block-end: 0; inset-inline-start: 50%; translate: -50% 0; }

/* role-list: centred, blue title, meta on one line ----------------------- */

.s-roles--centred .s-roles__list { max-width: 56rem; }
.s-roles--centred .s-roles__link { border: 0; background: none; padding: 0; text-align: center; justify-items: center; }
.s-roles--centred .s-roles__title { color: var(--colour-blue); font-size: 1.6rem; font-weight: var(--weight-semibold); }
.s-roles--centred .s-roles__meta { color: var(--colour-body); font-weight: var(--weight-regular); }

/* Each fact keeps its icon on the same line as its label. */
.s-roles__meta { display: flex; flex-wrap: wrap; justify-content: center; gap: var(--space-2) var(--space-6); }
.s-roles__fact { display: inline-flex; align-items: center; gap: var(--space-2); }
.s-roles__fact-icon { width: 1.05em; height: 1.05em; color: var(--colour-blue); }

/*
 * max-width: none because paragraphs carry a reading measure by default, which left this
 * one 662px wide inside a 1360 column — centred within its own box, but sitting well left
 * of the adverts it belongs to.
 */
/*
 * The per-role apply link. Reads as an action rather than body text, and keeps the same
 * blue the rest of the site uses for things you can click.
 */
.s-roles__apply-link {
	display: inline-flex;
	align-items: center;
	gap: 0.4rem;
	margin-top: 0.9rem;
	font-weight: var(--weight-semibold);
	font-size: var(--text-accent);
	color: var(--colour-blue);
	text-decoration: none;
}

.s-roles__apply-link::after {
	content: "\2192";
	transition: transform 0.15s ease;
}

.s-roles__apply-link:hover::after {
	transform: translateX(3px);
}

@media (prefers-reduced-motion: reduce) {
	.s-roles__apply-link::after { transition: none; }
}

.s-roles__apply { max-width: none; margin-block-start: var(--space-8); text-align: center; }
.s-roles--centred .s-roles__summary { max-width: 76rem; margin-inline: auto; }
/*
 * The rule spans the content column, not the intro's reading measure. It was a border on
 * the intro paragraph, so it stopped at 46rem — well short of live's, which runs the full
 * width of the column. Moved onto the list, which is the full-width element.
 */
.s-roles--centred .s-roles__intro { padding-block-end: var(--space-10); }

.s-roles--centred .s-roles__list {
	max-width: none;
	/* Clear air under the rule — the job title was sitting almost on top of it. */
	padding-block-start: clamp(var(--space-12), 6vw, var(--space-20));
	border-block-start: 1px solid var(--colour-blue-bright);
}

/* form-band: contact details panel beside the form ----------------------- */

.s-form__copy { padding: clamp(var(--space-6), 3vw, var(--space-10)); border-radius: 12px; background: var(--colour-surface-grey); }
.s-form--panelled { background: var(--colour-surface); }
.s-form--panelled .s-form__form { background: var(--colour-surface-grey); }

/*
 * Field rows. Live pairs the two names on one row and email with phone on the next;
 * Gravity Forms marks those fields gfield--width-half but its own grid CSS is disabled
 * (see inc/forms.php), so the layout is the theme's.
 */
.s-form__form .gform_fields {
	display: grid;
	grid-template-columns: repeat(2, 1fr);
	gap: var(--space-5) var(--space-4);
	margin: 0;
	padding: 0;
	list-style: none;
}

.s-form__form .gfield { grid-column: 1 / -1; }
.s-form__form .gfield--width-half { grid-column: span 1; }

/*
 * The accepted-types note belongs under its input, not beside it — it was running along
 * the right of the Choose file button and wrapping back underneath it.
 */
.s-form__form .gform_fileupload_rules,
.s-form__form .gfield_description {
	display: block;
	margin-block-start: var(--space-2);
	font-size: 0.9375rem;
	color: var(--colour-muted, #6a6a6a);
}

/* Paired fields stack before they get too narrow to read. */
@media (max-width: 40rem) {
	.s-form__form .gfield--width-half { grid-column: 1 / -1; }
}
.s-form__heading { color: var(--colour-blue); }
.s-form__contacts { display: grid; gap: var(--space-6); margin-block-start: var(--space-8); padding: 0; max-width: none; list-style: none; }
.s-form__contact-label { display: block; font-size: 0.85rem; font-weight: var(--weight-semibold); letter-spacing: 0.06em; text-transform: uppercase; color: var(--colour-heading); }
.s-form__contact-value { color: var(--colour-blue); font-size: 1.15rem; text-decoration: none; }

/* =================================================================================
 * Solution pages
 * ================================================================================= */

/* hero — a product, not a campaign ------------------------------------------------ */

/*
 * Darker than the article banner and left-aligned. A solution page opens with a claim that
 * has to be read, not a photograph that has to be admired, so the wash is heavy enough for
 * four lines of type to sit on comfortably.
 */
.s-hero--solution {
	display: flex;
	align-items: center;
	/*
	 * The band keeps its shape as the window widens, rather than freezing at a height while
	 * the width runs away from it. A clamped min-height did the latter: past its cap the
	 * band kept getting wider against a 2.33:1 picture and `cover` ate the difference, so
	 * resizing the window steadily cropped the photograph away.
	 *
	 * aspect-ratio holds the proportion; min-height is the floor on a narrow screen and
	 * max-height stops it becoming a wall on an ultrawide one.
	 */
	/*
	 * Width stated, not inferred. With aspect-ratio and a min-height and no explicit width,
	 * the ratio resolves the other way and derives the width from the height, which pushed
	 * these bands wider than the viewport and gave the whole page a horizontal scrollbar.
	 */
	width: 100%;
	aspect-ratio: 14 / 5;
	/*
	 * These two clamps fight the aspect ratio, and wherever one of them wins the framing changes.
	 * Measured across real widths: inside the range the ratio governs, the box stays at 2.80
	 * against an image of 2.91 and about 4% is trimmed, which nobody sees. Outside it the box
	 * aspect swung from 2.13 at 1024 to 4.48 at 3440 while the image stayed put, so 27% came off
	 * the sides on a small laptop and 13% off the top and bottom at 2560.
	 *
	 * Widened so the governed range covers roughly 1075px to 2420px, which is every common laptop
	 * and desktop including 1366, 1440, 1920 and 2560. At 2560 the trim is now under 2%.
	 *
	 * The floor cannot go much below this or the copy runs out of band to sit in; the ceiling
	 * cannot go much above it or the hero eats a whole large screen. Ultrawide is still cropped
	 * and is left that way deliberately.
	 */
	min-height: 24rem;
	max-height: 54rem;
	background-color: var(--colour-surface-dark);
	/*
	 * Weighted hard to the left and released almost entirely by the right. The copy needs a
	 * dark ground to sit on, but a solution photograph is usually carrying an argument of its
	 * own — on this page, a resident sitting undisturbed while the room is being monitored —
	 * and a wash strong enough to cover the whole frame throws that away. An earlier version
	 * ran 0.88 to 0.72 across the width with a blue cast on top and flattened the picture to
	 * grey.
	 */
	/*
	 * The wash cannot be pulled back much further than this. Measured off the artwork, the
	 * window and curtain behind the copy sit at luminance 137 to 189, which needs roughly
	 * 0.75 opacity before white body text reaches 4.5:1. The radar sweep starts at about 25%
	 * across, so it is partly under that requirement: the rings can only be fully clear from
	 * the middle of the frame rightward. Future artwork should keep its left third plain and
	 * put the sensing in the right two thirds.
	 */
	background-image:
		linear-gradient(
			90deg,
			rgb(20 22 24 / 0.88) 0%,
			rgb(20 22 24 / 0.84) 34%,
			rgb(20 22 24 / 0.72) 46%,
			rgb(20 22 24 / 0.22) 58%,
			rgb(20 22 24 / 0.04) 70%,
			rgb(20 22 24 / 0) 80%
		),
		var(--hero-image, none);
	background-size: cover;
	/* Slightly above centre: the ring structure runs from a fifth of the way down to four fifths. */
	background-position: 50% 42%;
	color: var(--colour-on-dark);
}

/*
 * Artwork that carries its own ground. The wash above is sized for the health and aged care
 * photograph, whose tagline sits over a bright window and needs 0.47 opacity before it reaches
 * 4.5:1. Applied to a photograph that is already dark where the type goes, it is pure loss:
 * measured on the construction hero, the copy clears at 10.6:1 with nothing behind it, while
 * the wash was taking 82% of the luminance off the site view and 42% off the two figures.
 *
 * So the wash is a property of the picture, set per page, not a fixed part of the band.
 */
.s-hero--solution.s-hero--wash-none {
	background-image: var(--hero-image, none);
	/*
	 * A touch taller than a washed hero, and the copy runs a narrower measure. Both follow from
	 * the same thing: with no wash the type has to stay inside the part of the frame that is
	 * already plain, and on the construction photograph that is the left 36%. At the standard
	 * 30rem the standfirst ran to 41% and two of its lines crossed the lit doorway at 1.28:1.
	 *
	 * 25rem lands the right edge at 35.5%. That costs a line, and at 14/5 the extra line clipped
	 * local government, whose introduction is the longest of the three; 13/5 gives back the 23
	 * pixels it needed. Artwork for a bare hero has to keep roughly its left third plain.
	 */
	aspect-ratio: 13 / 5;
}

.s-hero--solution.s-hero--wash-none .s-hero__inner > * {
	max-width: 25rem;
}


/*
 * Narrow widths get the wash back, because below the breakpoint the band stops holding the
 * picture's proportion and `cover` recentres on the middle of the frame. The plain left third
 * that the bare hero depends on is simply not on screen: measured at 375px, the visible slice
 * is 27% of the image width taken from the centre, which on the construction hero is the lit
 * doorway and the two figures, and the white standfirst sat straight on it.
 *
 * Flat rather than weighted, because here the copy runs the full width of the band, so a
 * gradient that releases at 80% leaves the last fifth of every line uncovered. 0.58 is the
 * measured floor: it carries the brightest 0.5% of all three hero photographs to 4.5:1.
 */
@media (max-width: 67rem) {

	.s-hero--solution.s-hero--wash-none {
		background-image:
			linear-gradient(rgb(20 22 24 / 0.58), rgb(20 22 24 / 0.58)),
			var(--hero-image, none);
		aspect-ratio: auto;
	}

	/* The narrow measure is a desktop framing device; here the band is already narrow. */
	.s-hero--solution.s-hero--wash-none .s-hero__inner > * {
		max-width: none;
	}

}

/*
 * Below about 1075px the aspect ratio stops governing and the 24rem floor becomes the height —
 * a definite one, because aspect-ratio resolves against the stated width. The band then cannot
 * grow for its contents, and since the copy is centred in a flex box, anything longer than the
 * floor spills equally out of the top and the bottom: the heading slides under the header and
 * the last line of the introduction disappears behind the next section.
 *
 * It was invisible because every hero written so far happens to fit. Measured at 375px, health
 * and aged care clears it by 3 pixels; local government, whose introduction is two lines longer,
 * overflowed by 52 and lost half its H1. That is a page-content lottery, not a layout.
 *
 * Releasing the ratio here turns min-height back into what it reads as — a floor. Short heroes
 * are unchanged at exactly 24rem; long ones grow instead of clipping. Above the breakpoint the
 * ratio governs again and nothing about the desktop framing moves.
 */
@media (max-width: 67rem) {

	.s-hero--solution {
		aspect-ratio: auto;
		block-size: auto;
	}
}

/*
 * The copy is capped at a readable measure but the container stays a .wrap, so it keeps the
 * site's gutter and stays level with the logo. An earlier version set margin-inline: 0 on
 * the wrap itself, which pinned the heading to the very edge of the viewport.
 */
.s-hero--solution .s-hero__inner > * { max-width: 44rem; }

/*
 * The body copy is held tighter than the heading. WCAG asks 4.5:1 of 18px text but only
 * 3:1 of the display sizes above it, so the heading can run out to where the wash has
 * started to lift and the standfirst cannot. Keeping the paragraph short is what lets the
 * wash release early enough for the radar sweep to survive.
 */
.s-hero--solution .s-hero__standfirst,
.s-hero--solution .s-hero__partner { max-width: 30rem; }

.s-hero--solution .s-hero__heading {
	margin: 0;
	color: var(--colour-on-dark);
	font-size: clamp(2.4rem, 1.4rem + 3vw, 4rem);
	font-weight: var(--weight-light);
	line-height: 1.05;
}

/* The tagline is the argument in four words, so it is set larger than the paragraph. */
.s-hero__tagline {
	margin-block: var(--space-4) 0;
	color: var(--colour-blue-bright);
	font-size: clamp(1.25rem, 1rem + 0.9vw, 1.75rem);
	font-weight: var(--weight-light);
}

.s-hero__standfirst {
	margin-block-start: var(--space-5);
	max-width: 62ch;
	color: var(--colour-on-dark);
}

.s-hero__partner {
	margin-block-start: var(--space-6);
	color: var(--colour-on-dark);
	font-size: 0.9rem;
	letter-spacing: 0.08em;
	text-transform: uppercase;
	opacity: 0.72;
}

/* the signal ---------------------------------------------------------------------- */

/*
 * Full-bleed artwork with the copy set over it. The band is deliberately the one dark
 * stretch in the middle of the page — the argument it carries is the product's whole
 * proposition, and giving it the page's only change of ground is what makes it land.
 *
 * The edges are hard, and that is the second attempt. Fading the dark up into the white
 * section above produced a milky ramp — a white-to-near-black gradient reads as fog, not as
 * depth, whatever length it is run over. What makes the band sit properly between two light
 * sections is room: it is the tallest thing on the page after the hero, and the artwork is
 * given the whole width rather than a column of it.
 *
 * The join is softened from the inside instead, by a vignette that settles the artwork into
 * the band's own colour at top and bottom. Dark into dark, so nothing goes hazy.
 */
.s-signal {
	display: flex;
	/*
	 * Top, not centre. Centred, the copy block sat squarely across the waveform: readable,
	 * but the type was resting on the line rather than above it. The artwork's baseline is
	 * authored low in the frame for the same reason.
	 */
	align-items: flex-start;
	/*
	 * Grows a little with the viewport so the band keeps roughly its proportions on a wide
	 * monitor, but stays a band. An earlier version tracked 36vw and turned into a 900px
	 * wall on anything large.
	 *
	 * The artwork is authored at 10:3 to suit this, rather than a 16:9 frame being cropped
	 * into a letterbox slot, and it is vector, so none of this costs any sharpness.
	 */
	/*
	 * The floor is set by the copy, not by taste: the artwork's baseline sits at 79% of the
	 * frame and the copy has to finish above it. Any shorter and the figures land back on the
	 * waveform. Above that the band keeps its proportion as the window widens.
	 */
	/*
	 * Width stated, not inferred. With aspect-ratio and a min-height and no explicit width,
	 * the ratio resolves the other way and derives the width from the height, which pushed
	 * these bands wider than the viewport and gave the whole page a horizontal scrollbar.
	 */
	width: 100%;
	aspect-ratio: 10 / 3;
	min-height: 600px;
	max-height: 46rem;
	padding-block: var(--space-16);
	/* Sampled from the artwork's own edges, so a wide viewport never shows a seam. */
	background-color: #0a1420;
	background-image:
		linear-gradient(to bottom, rgb(8 16 26 / 0.75) 0%, rgb(8 16 26 / 0) 18%, rgb(8 16 26 / 0) 82%, rgb(8 16 26 / 0.75) 100%),
		/* Enough ground for white type without touching the right half, where the art is. */
		linear-gradient(90deg, rgb(6 12 22 / 0.82) 0%, rgb(6 12 22 / 0.55) 34%, rgb(6 12 22 / 0) 62%),
		/*
		 * The template always sets --signal-image, versioned so a regenerated artwork is
		 * actually fetched. This is the fallback for anything that renders the section
		 * without it. Vector by default, drawn by scripts/build-signal-artwork.py: a raster
		 * was upscaled and soft on any monitor past about 1700px, which no crop rule fixes.
		 */
		var(--signal-image, url("../img/pobc-signal.svg"));
	background-size: auto, auto, cover;
	background-position: center;
	background-repeat: no-repeat;
	color: var(--colour-on-dark);
}

.s-signal__copy { max-width: 38rem; }

/* The heading can run the full width of the block; the prose cannot. See below. */
.s-signal__heading { max-width: 31rem; }

.s-signal__heading {
	margin: 0;
	color: var(--colour-on-dark);
	font-size: clamp(2rem, 1.4rem + 2vw, 3rem);
	font-weight: var(--weight-light);
	line-height: 1.1;
}

.s-signal__body {
	margin-block-start: var(--space-5);
	margin-block-end: 0;
	/*
	 * A measure, not `none`. This was set to none purely to defeat the global paragraph
	 * width, which left it running the full 38rem of the block: around 76 characters, which
	 * is past comfortable. The block is wide so the two figures can sit side by side, and
	 * the prose does not have to follow it out there.
	 */
	max-width: 31rem;
	color: var(--colour-on-dark);
	opacity: 0.88;
}

/* The mechanism, set brighter than the explanation above it. */
.s-signal__body + .s-signal__body {
	margin-block-start: var(--space-4);
	opacity: 1;
}

/*
 * The numbers, set at the size of the claims they are making. Two of them side by side
 * where a solution has an outcome as well as a lead time, stacking under each other before
 * either would have to shrink.
 */
.s-signal__figures {
	display: grid;
	gap: var(--space-6) var(--space-8);
	margin-block-start: var(--space-10);
	padding-block-start: var(--space-8);
	border-block-start: 1px solid rgb(255 255 255 / 0.18);
}

@media (min-width: 40rem) {
	.s-signal__figures { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

.s-signal__stat {
	display: flex;
	flex-wrap: wrap;
	align-items: baseline;
	gap: 0 var(--space-3);
	margin: 0;
	max-width: none;
}

.s-signal__figure {
	color: var(--colour-blue-bright);
	font-size: clamp(2rem, 1.5rem + 1.5vw, 2.8rem);
	font-weight: var(--weight-light);
	line-height: 1;
}

/*
 * Always its own line, so both stats have the same three-part shape and their notes line up.
 * Left to wrap naturally, "7 days of warning" fitted on one line and "up to 50% fewer
 * resident falls" did not, which put the two blocks on different baselines.
 */
.s-signal__unit {
	flex-basis: 100%;
	margin-block-start: var(--space-2);
	color: var(--colour-on-dark);
	font-size: clamp(1rem, 0.9rem + 0.3vw, 1.15rem);
	font-weight: var(--weight-light);
}

/* Its own line under the figure, so the phrase reads as one thought and not three. */
.s-signal__note {
	flex-basis: 100%;
	margin-block-start: var(--space-1);
	max-width: 20rem;
	color: var(--colour-on-dark);
	font-size: 0.85rem;
	line-height: 1.4;
	letter-spacing: 0.02em;
	opacity: 0.62;
}

.s-signal__caption {
	margin-block-start: var(--space-6);
	max-width: none;
	color: var(--colour-on-dark);
	font-size: 0.85rem;
	font-style: italic;
	opacity: 0.55;
}

/* heading-and-body rows ----------------------------------------------------------- */

.s-pairs__list {
	display: grid;
	/*
	 * Both values must be real tokens. --space-9 does not exist, and one bad value in the
	 * shorthand invalidates the whole declaration: the rows and columns were sitting flush
	 * against each other with gap computing to `normal`, which read as one long list rather
	 * than a grid of separate points.
	 */
	gap: var(--space-10) var(--space-8);
	margin: 0;
	padding: 0;
	/* The global `p, ul, ol` measure would hold a three-column grid to 700px. */
	max-width: none;
	list-style: none;
}

@media (min-width: 48rem) {
	.s-pairs__list { grid-template-columns: repeat(var(--columns, 3), minmax(0, 1fr)); }
}

.s-pairs__heading { margin: 0; font-size: var(--text-card-h); font-weight: var(--weight-semibold); line-height: 1.25; }
.s-pairs__body { margin-block-start: var(--space-3); margin-block-end: 0; max-width: none; }

/* A rule in the brand blue rather than a bullet — the shape the collateral uses. */
.s-pairs--rule .s-pairs__item {
	padding-inline-start: var(--space-5);
	border-inline-start: 3px solid var(--colour-blue);
}

/*
 * Counters rather than list markers, so the number can be styled as a display figure and
 * still be a real ordered list underneath.
 */
.s-pairs--numbered .s-pairs__list { counter-reset: pair; }

.s-pairs--numbered .s-pairs__item { counter-increment: pair; }

.s-pairs--numbered .s-pairs__heading::before {
	content: counter(pair, decimal-leading-zero);
	display: block;
	margin-block-end: var(--space-2);
	color: var(--colour-blue);
	font-size: 2.2rem;
	font-weight: var(--weight-light);
	line-height: 1;
}

/* what it is, and what it is not -------------------------------------------------- */

.s-boundary__columns { display: grid; gap: var(--space-8); }

@media (min-width: 52rem) {
	.s-boundary__columns { grid-template-columns: 1fr 1fr; gap: var(--space-10); }
}

.s-boundary__column-heading {
	margin: 0 0 var(--space-6);
	padding-block-end: var(--space-3);
	border-block-end: 1px solid var(--colour-rule);
	font-size: var(--text-card-h);
	font-weight: var(--weight-semibold);
}

.s-boundary__list { display: grid; gap: var(--space-6); margin: 0; padding: 0; max-width: none; list-style: none; }

.s-boundary__item { padding-inline-start: var(--space-5); border-inline-start: 3px solid var(--colour-rule); }

/*
 * Blue for what it does, a muted red for what it does not. The second column is the one
 * that earns trust, so it is coloured as a real statement rather than greyed out like a
 * disclaimer someone hoped you would skip.
 */
.s-boundary__column--affirm .s-boundary__item { border-inline-start-color: var(--colour-blue); }
.s-boundary__column--deny .s-boundary__item { border-inline-start-color: #c0392b; }

.s-boundary__item-heading { margin: 0; font-size: 1.0625rem; font-weight: var(--weight-semibold); }
.s-boundary__item-body { margin-block-start: var(--space-2); margin-block-end: 0; max-width: none; }

/* capabilities -------------------------------------------------------------------- */

.s-capabilities__lead {
	padding: var(--space-8);
	border-radius: 10px;
	background: var(--colour-surface);
	border-block-start: 4px solid var(--colour-blue);
}

.s-capabilities__flag {
	margin: 0;
	color: var(--colour-blue-text);
	font-size: 0.8rem;
	font-weight: var(--weight-semibold);
	letter-spacing: 0.1em;
	text-transform: uppercase;
}

.s-capabilities__lead-name {
	margin: var(--space-2) 0 0;
	font-size: clamp(1.6rem, 1.2rem + 1.2vw, 2.2rem);
	font-weight: var(--weight-light);
}

.s-capabilities__lead-body { margin: var(--space-4) 0 0; max-width: 70ch; }

.s-capabilities__list {
	display: grid;
	gap: var(--space-6);
	margin: var(--space-6) 0 0;
	padding: 0;
	max-width: none;
	list-style: none;
}

/*
 * auto-fit rather than a fixed three, because the column count has to come from how many
 * capabilities there are. Vital Signs Monitoring has seven and fills three columns; AI365
 * has three, one of which leads and is lifted out above it, which left two cards in a
 * three-column grid and a third of the section empty.
 */
.s-capabilities__list {
	grid-template-columns: repeat(auto-fit, minmax(min(20rem, 100%), 1fr));
}

.s-capabilities__item {
	padding: var(--space-6);
	border-radius: 10px;
	background: var(--colour-surface);
	border-block-start: 3px solid var(--colour-blue-bright);
}

.s-capabilities__name { margin: 0; font-size: 1.125rem; font-weight: var(--weight-semibold); }
.s-capabilities__body { margin: var(--space-3) 0 0; max-width: none; font-size: 1rem; }

/* statement ----------------------------------------------------------------------- */

.s-statement { padding-block: var(--space-12); background: var(--colour-blue); color: var(--colour-on-dark); }

.s-statement__inner { max-width: 900px; text-align: center; }

.s-statement__heading {
	margin: 0;
	color: var(--colour-on-dark);
	font-size: clamp(1.8rem, 1.3rem + 1.6vw, 2.6rem);
	font-weight: var(--weight-light);
}

/* Auto margins, or the global paragraph measure holds it left inside a centred band. */
.s-statement__body {
	max-width: 46rem;
	margin: var(--space-5) auto 0;
	color: var(--colour-on-dark);
	font-size: clamp(1.125rem, 1rem + 0.4vw, 1.35rem);
	line-height: 1.6;
}

/* specifications ------------------------------------------------------------------ */

.s-specs__heading { margin: 0 0 var(--space-6); font-size: var(--text-card-h); font-weight: var(--weight-semibold); }

.s-specs__list { margin: 0; }

.s-specs__row {
	display: grid;
	gap: var(--space-2) var(--space-6);
	padding-block: var(--space-4);
	border-block-start: 1px solid var(--colour-rule);
}

@media (min-width: 40rem) {
	.s-specs__row { grid-template-columns: minmax(0, 1fr) minmax(0, 2fr); align-items: baseline; }
}

.s-specs__label { margin: 0; color: var(--colour-heading); font-weight: var(--weight-semibold); }
.s-specs__value { margin: 0; }

/* table --------------------------------------------------------------------------- */

/*
 * For content that genuinely is tabular. The figures are right-aligned and set in the
 * heading colour so a reader can run an eye down a column, which is the entire reason to
 * use a table rather than a list of sentences.
 */
.s-table__scroll { overflow-x: auto; }

.s-table__table {
	width: 100%;
	border-collapse: collapse;
	text-align: start;
}

.s-table__table th,
.s-table__table td {
	padding: var(--space-4) var(--space-5);
	border-block-end: 1px solid var(--colour-rule);
	vertical-align: baseline;
	/*
	 * Explicit, because the browser's own stylesheet centres every th and that beats an
	 * inherited alignment from the table. The figure columns override it below.
	 */
	text-align: start;
}

.s-table__table thead th {
	padding-block-end: var(--space-3);
	border-block-end: 2px solid var(--colour-blue);
	color: var(--colour-heading);
	font-size: 0.85rem;
	font-weight: var(--weight-semibold);
	letter-spacing: 0.06em;
	text-transform: uppercase;
	white-space: nowrap;
}

.s-table__table :is(thead, tbody) :is(th, td):first-child { padding-inline-start: 0; }
.s-table__table :is(thead, tbody) :is(th, td):last-child { padding-inline-end: 0; }

.s-table__table tbody th {
	color: var(--colour-heading);
	font-size: 1.0625rem;
	font-weight: var(--weight-semibold);
}

/* Figures at a size worth scanning, and lining numerals so the columns align on the digit. */
.s-table__table th.s-table__figure,
.s-table__table td.s-table__figure {
	font-variant-numeric: tabular-nums;
	text-align: end;
	white-space: nowrap;
}

.s-table__table td.s-table__figure {
	color: var(--colour-blue-text);
	font-size: 1.25rem;
	font-weight: var(--weight-light);
}

.s-table__note {
	margin-block-start: var(--space-5);
	max-width: none;
	color: var(--colour-body);
	font-size: 0.85rem;
	font-style: italic;
	opacity: 0.75;
}

/*
 * Below this the table stops being a table: each row becomes a small block with its column
 * heading carried in as a label, because four columns squeezed into a phone is unreadable
 * however much it scrolls.
 */
@media (max-width: 40rem) {
	.s-table__table thead { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); }
	.s-table__table tr { display: block; padding-block: var(--space-4); border-block-end: 1px solid var(--colour-rule); }
	.s-table__table th,
	.s-table__table td { display: block; padding: 0; border: 0; text-align: start; }
	.s-table__table tbody th { margin-block-end: var(--space-2); }

	.s-table__table td.s-table__figure {
		display: flex;
		justify-content: space-between;
		gap: var(--space-4);
		font-size: 1.0625rem;
	}

	.s-table__table td.s-table__figure::before {
		content: attr(data-label);
		color: var(--colour-body);
		font-size: 0.85rem;
		font-weight: var(--weight-semibold);
		letter-spacing: 0.04em;
		text-transform: uppercase;
	}
}

/* thesis ---------------------------------------------------------------------------- */

/*
 * The page's opening argument, and deliberately unlike anything else on it. Every other
 * section here is a centred heading over a grid of ruled cards, which is fine once and
 * deadening five times over. This one is asymmetric, left-aligned and set at display scale,
 * so a reader arriving at the top meets a statement rather than another list.
 */
.s-thesis { padding-block: clamp(var(--space-12), 7vw, var(--space-20)); }

.s-thesis__heading {
	max-width: 16ch;
	margin: 0;
	font-size: clamp(2.4rem, 1.6rem + 3vw, 4rem);
	font-weight: var(--weight-light);
	line-height: 1.02;
	letter-spacing: -0.015em;
}

.s-thesis__lead {
	max-width: 40rem;
	margin-block-start: var(--space-8);
	font-size: clamp(1.125rem, 1rem + 0.5vw, 1.375rem);
	line-height: 1.55;
}

/*
 * Rules above and below rather than cards. The figures are the loudest thing in the section
 * and they do not need a box to say so.
 */
.s-thesis__stats {
	display: grid;
	gap: var(--space-8);
	margin-block: var(--space-12) 0;
	padding-block: var(--space-10);
	border-block: 1px solid var(--colour-rule);
	max-width: none;
	list-style: none;
	padding-inline: 0;
}

@media (min-width: 48rem) {
	/*
	 * As many columns as there are figures, up to four. Fixed at three, a fourth figure
	 * dropped onto a row of its own and read as an afterthought rather than part of the set.
	 */
	.s-thesis__stats { grid-template-columns: repeat(min(var(--count, 3), 4), minmax(0, 1fr)); gap: var(--space-10); }
}

.s-thesis__figure {
	display: block;
	color: var(--colour-blue);
	font-size: clamp(2.8rem, 2rem + 3vw, 4.5rem);
	font-weight: var(--weight-light);
	line-height: 0.95;
	letter-spacing: -0.02em;
}

.s-thesis__label {
	display: block;
	margin-block-start: var(--space-4);
	max-width: 22rem;
	font-size: var(--text-accent);
	line-height: 1.45;
}

/* The human half of the argument, after the numbers. Given its own weight. */
/*
 * Runs the width of the column rather than a 34rem measure. Held narrow it broke to five
 * lines and pushed the section down for no gain: this is one sentence delivered once, not a
 * paragraph to be read in a column.
 */
.s-thesis__close {
	max-width: none;
	margin-block-start: var(--space-10);
	color: var(--colour-heading);
	font-size: clamp(1.25rem, 1.05rem + 0.8vw, 1.75rem);
	font-weight: var(--weight-light);
	line-height: 1.4;
}

.s-thesis__source {
	margin-block-start: var(--space-8);
	max-width: none;
	color: var(--colour-body);
	font-size: 0.8rem;
	opacity: 0.65;
}

@media (min-width: 62rem) {
	/*
	 * Heading and lead side by side. Asymmetry is most of what stops this reading like the
	 * rest of the page.
	 */
	.s-thesis .wrap { display: grid; grid-template-columns: minmax(0, 5fr) minmax(0, 7fr); column-gap: var(--space-12); }
	.s-thesis__heading { grid-column: 1; }
	.s-thesis__lead { grid-column: 2; margin-block-start: var(--space-2); }
	.s-thesis__stats,
	.s-thesis__close,
	.s-thesis__source { grid-column: 1 / -1; }
}

/* figure ---------------------------------------------------------------------------- */

/*
 * A full-bleed photograph with the line it is making set over its right-hand side. The
 * mirror of the hero, which runs copy left over a wash weighted left, so the two bracket the
 * page rather than repeating it. The artwork is composed with its subject on the left and
 * the right side clear, which is what lets the wash sit where the copy is.
 *
 * Stacking is explicit and never negative. An earlier version pushed the picture to
 * z-index -1 and it disappeared behind the band's own background.
 */
.s-figure {
	position: relative;
	isolation: isolate;
	overflow: hidden;
	background: var(--colour-surface-dark);
	color: var(--colour-on-dark);
}

.s-figure__frame { margin: 0; }

.s-figure__image { display: block; width: 100%; height: auto; }

.s-figure__body { padding-block: var(--space-10); }

.s-figure__caption {
	max-width: 34rem;
	margin: 0;
	color: var(--colour-on-dark);
	font-size: clamp(1.125rem, 1rem + 0.6vw, 1.5rem);
	font-weight: var(--weight-light);
	line-height: 1.45;
}

/*
 * Stacked below this width. Overlaying copy on a photograph needs room the picture has to
 * give up, and on a phone there is none: the picture runs full width and the words sit
 * under it on the band's own colour.
 */
@media (min-width: 64rem) {
	.s-figure {
		display: flex;
		align-items: center;
		width: 100%;
		aspect-ratio: 16 / 5;
		min-height: 22rem;
		max-height: 46rem;
	}

	/* Fills the line. Without this it collapses to the width of its own text. */
	.s-figure__frame { flex: 1; min-width: 0; }

	.s-figure__image {
		position: absolute;
		inset: 0;
		z-index: 0;
		width: 100%;
		height: 100%;
		object-fit: cover;
		/*
		 * Biased up. Her head is in the top quarter of the frame and the desk in the bottom
		 * fifth, so a centre crop takes the face and keeps the floor.
		 */
		object-position: 50% 35%;
	}

	/*
	 * The wash, weighted right, mirroring the hero's. Same stop profile, which was measured
	 * against artwork rather than chosen: white body copy needs roughly 0.75 over a lit
	 * interior before it reaches 4.5:1.
	 */
	.s-figure::after {
		content: "";
		position: absolute;
		inset: 0;
		z-index: 1;
		background: linear-gradient(
			270deg,
			rgb(20 22 24 / 0.88) 0%,
			rgb(20 22 24 / 0.84) 30%,
			rgb(20 22 24 / 0.66) 44%,
			rgb(20 22 24 / 0.22) 58%,
			rgb(20 22 24 / 0.04) 72%,
			rgb(20 22 24 / 0) 84%
		);
	}

	.s-figure__body {
		position: relative;
		z-index: 2;
		display: flex;
		justify-content: flex-end;
		padding-block: var(--space-12);
	}

	.s-figure__caption {
		max-width: 25rem;
		font-size: clamp(1.25rem, 1.05rem + 0.5vw, 1.6rem);
	}
}

/* faq ----------------------------------------------------------------------------- */

/*
 * The homepage FAQ is a stack of blue bars. This one is not, deliberately: on a solution
 * page the questions sit among tables, boundaries and a price list, and a column of solid
 * colour would read as the loudest thing on a page whose argument is elsewhere. Hairlines
 * and type instead.
 *
 * Constrained to a single column at a reading measure rather than spread across the wrap.
 * Two columns of collapsibles put the reader's eye in two places and make the open answer
 * shove its neighbour down the page.
 */

.s-faq__list {
	max-width: 52rem;
	border-block-start: 1px solid var(--colour-rule);
}

.s-faq__item { border-block-end: 1px solid var(--colour-rule); }

.s-faq__summary {
	display: flex;
	align-items: baseline;
	justify-content: space-between;
	gap: var(--space-6);
	padding-block: var(--space-5);
	cursor: pointer;
	list-style: none;
}

/* Safari still needs this to lose the disclosure triangle. */
.s-faq__summary::-webkit-details-marker { display: none; }

.s-faq__summary:hover .s-faq__question { color: var(--colour-blue-text); }

/*
 * The ring goes on the summary, not the heading inside it, because the summary is the
 * button. Inset so it is not clipped by the hairline above.
 */
.s-faq__summary:focus-visible {
	outline: var(--focus-ring);
	outline-offset: -3px;
}

.s-faq__question {
	margin: 0;
	font-size: 1.1875rem;
	font-weight: var(--weight-semibold);
	line-height: 1.4;
	color: var(--colour-heading);
}

/*
 * A plus that becomes a minus. Drawn with two pseudo-elements rather than a glyph so it
 * lines up at any size and needs no font to have loaded.
 */
.s-faq__marker {
	position: relative;
	flex: 0 0 auto;
	width: 0.875rem;
	height: 0.875rem;
	margin-block-start: 0.3rem;
}

.s-faq__marker::before,
.s-faq__marker::after {
	content: "";
	position: absolute;
	inset-inline: 0;
	inset-block-start: 50%;
	height: 2px;
	background: var(--colour-blue);
	transition: transform 150ms ease;
}

.s-faq__marker::after { transform: rotate(90deg); }

.s-faq__item[open] .s-faq__marker::after { transform: rotate(0deg); }

@media (prefers-reduced-motion: reduce) {
	.s-faq__marker::before,
	.s-faq__marker::after { transition: none; }
}

.s-faq__answer { padding-block-end: var(--space-6); }

.s-faq__answer p {
	margin: 0;
	max-width: 46rem;
}

.s-faq__answer p + p { margin-block-start: var(--space-4); }

/*
 * A table column of sentences rather than measurements. Figures do not wrap, which is what
 * keeps a column of numbers aligned on the digit and what made the tier table 1,778px wide
 * inside a 997px page the first time a column of prose went through it.
 */
.s-table__table th.s-table__prose,
.s-table__table td.s-table__prose {
	text-align: start;
	white-space: normal;
}

.s-table__table td.s-table__prose {
	max-width: 34rem;
	color: var(--colour-body);
	font-size: 1rem;
	line-height: 1.45;
}

/* estate ------------------------------------------------------------------------- */

/*
 * The dark band, and then the three states on their own darker strip below it. Two elements
 * rather than one because the artwork has to be cropped by the viewport while the text
 * underneath must not be, and because the states read as a caption to the picture rather
 * than as content floating on top of it.
 */
.s-estate { background: #08111d; }

.s-estate__band {
	/*
	 * Explicit width with the aspect ratio. Without it the browser derives the WIDTH from the
	 * height, which is how the solution hero once came out 1,344px wide inside a 1,085px
	 * viewport and gave the whole page a horizontal scrollbar.
	 */
	width: 100%;
	aspect-ratio: 2400 / 1000;
	min-height: 30rem;
	max-height: 44rem;
	display: flex;
	align-items: center;

	/*
	 * The wash runs left to right so the copy has something to sit on while the dense,
	 * governed end of the picture stays clear. Mirrors the solution hero deliberately: it is
	 * the same device, and using it twice on one site reads as a system.
	 */
	background-image:
		linear-gradient(
			90deg,
			rgb(8 17 29 / 0.94) 0%,
			rgb(8 17 29 / 0.88) 28%,
			rgb(8 17 29 / 0.55) 46%,
			rgb(8 17 29 / 0.12) 62%,
			rgb(8 17 29 / 0) 76%
		),
		var(--estate-image, none);
	background-repeat: no-repeat;
	background-size: cover, cover;
	background-position: center, center;
}

.s-estate__copy { max-width: 34rem; }

.s-estate__heading {
	margin: 0 0 var(--space-5);
	color: var(--colour-on-dark);
	font-size: clamp(1.9rem, 1.3rem + 1.8vw, 3rem);
	font-weight: var(--weight-light);
	line-height: 1.1;
}

.s-estate__body {
	margin: 0;
	max-width: none;
	color: rgb(255 255 255 / 0.86);
}

.s-estate__body + .s-estate__body { margin-block-start: var(--space-4); }

.s-estate__states { padding-block: var(--space-12); border-block-start: 1px solid rgb(255 255 255 / 0.1); }

.s-estate__list {
	display: grid;
	gap: var(--space-8);
	margin: 0;
	padding: 0;
	max-width: none;
	list-style: none;
}

@media (min-width: 52rem) {
	.s-estate__list { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--space-10); }
}

.s-estate__label {
	margin: 0 0 var(--space-2);
	color: var(--colour-blue-bright);
	font-size: 0.8rem;
	font-weight: var(--weight-semibold);
	letter-spacing: 0.08em;
	text-transform: uppercase;
}

.s-estate__state-heading {
	margin: 0;
	color: var(--colour-on-dark);
	font-size: 1.0625rem;
	font-weight: var(--weight-semibold);
}

.s-estate__state-body {
	margin: var(--space-3) 0 0;
	max-width: none;
	color: rgb(255 255 255 / 0.78);
	font-size: 1rem;
}

.s-estate__note {
	margin: var(--space-10) 0 0;
	max-width: none;
	color: rgb(255 255 255 / 0.55);
	font-size: 0.85rem;
	font-style: italic;
}

/*
 * Narrow. The artwork's story runs left to right across roughly three viewport widths at this
 * size, so cropping it to the middle would show the clusters and neither of the two states
 * that matter. It drops to a flat field and the words carry it instead.
 */
@media (max-width: 46rem) {
	.s-estate__band {
		aspect-ratio: auto;
		min-height: 0;
		padding-block: var(--space-16);
		background-image: linear-gradient(180deg, #08111d 0%, #0d1b2e 100%);
	}
}

/* matrix ------------------------------------------------------------------------- */

/*
 * A real table. The prices sit in the column heads because the reader is comparing a number
 * against the column of what it buys, and that only reads if the number is at the top of it.
 */
.s-matrix__scroll { overflow-x: auto; }

.s-matrix__table {
	width: 100%;
	min-width: 44rem;
	border-collapse: collapse;
	text-align: start;
}

/*
 * Visually hidden. A table this size should carry a caption for anyone reading it out of
 * context or through a screen reader, but the section's own intro already says the same thing
 * to a sighted reader and two near-identical sentences stacked is worse than either alone.
 */
.s-matrix__caption {
	position: absolute;
	width: 1px;
	height: 1px;
	overflow: hidden;
	clip-path: inset(50%);
	white-space: nowrap;
}

.s-matrix__corner { width: 30%; border: 0; }

.s-matrix__tier {
	width: 23.33%;
	padding: 0 var(--space-5) var(--space-4);
	border-block-end: 2px solid var(--colour-blue);
	text-align: start;
	vertical-align: bottom;
}

.s-matrix__tier-name,
.s-matrix__tier-price,
.s-matrix__tier-capacity { display: block; }

.s-matrix__tier-name {
	color: var(--colour-heading);
	font-size: 0.85rem;
	font-weight: var(--weight-semibold);
	letter-spacing: 0.06em;
	text-transform: uppercase;
}

.s-matrix__tier-price {
	margin-block-start: var(--space-2);
	color: var(--colour-blue-text);
	font-size: 1.6rem;
	font-weight: var(--weight-light);
	font-variant-numeric: tabular-nums;
	line-height: 1.1;
}

.s-matrix__tier-capacity {
	margin-block-start: var(--space-1);
	color: var(--colour-body);
	font-size: 0.85rem;
	font-weight: var(--weight-regular);
}

/*
 * The workstream heading spans the table. A fourth column of repeated labels is how a matrix
 * stops being readable; a spanning row lets the eye take one workstream at a time.
 */
.s-matrix__group-head th {
	padding: var(--space-8) 0 var(--space-3);
	border-block-end: 1px solid var(--colour-rule);
	text-align: start;
}

.s-matrix__group-name {
	display: block;
	color: var(--colour-heading);
	font-size: 1.25rem;
	font-weight: var(--weight-semibold);
}

.s-matrix__group-body {
	display: block;
	margin-block-start: var(--space-2);
	max-width: 52rem;
	color: var(--colour-body);
	font-size: 1rem;
	font-weight: var(--weight-regular);
	line-height: 1.45;
}

.s-matrix__activity,
.s-matrix__cell {
	padding: var(--space-4) var(--space-5);
	border-block-end: 1px solid var(--colour-rule);
	vertical-align: baseline;
	text-align: start;
	font-size: 1rem;
}

.s-matrix__activity {
	padding-inline-start: 0;
	color: var(--colour-body-strong);
	font-weight: var(--weight-regular);
}

.s-matrix__cell { color: var(--colour-body); }

/* Not greyed into illegibility: what a tier does not include is information, not small print. */
.s-matrix__cell--absent { color: var(--colour-body); opacity: 0.55; }

.s-matrix__blank { opacity: 0.35; }

.s-matrix__note {
	margin-block-start: var(--space-6);
	max-width: none;
	color: var(--colour-body);
	font-size: 0.85rem;
	font-style: italic;
	opacity: 0.75;
}

/*
 * Below this the matrix stops being a table. Each activity becomes a block carrying its three
 * tier values as labelled lines, because three columns of sentences on a phone is unreadable
 * however far it scrolls.
 */
@media (max-width: 52rem) {
	.s-matrix__table { min-width: 0; }
	.s-matrix__scroll { overflow-x: visible; }

	.s-matrix__table thead { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); }

	.s-matrix__table :is(tbody, tr, th, td) { display: block; }

	.s-matrix__group-head th { padding-block: var(--space-8) var(--space-4); }

	.s-matrix__activity {
		padding: var(--space-5) 0 var(--space-2);
		border: 0;
		color: var(--colour-heading);
		font-weight: var(--weight-semibold);
	}

	/*
	 * Qualified with td, because `.s-matrix__table :is(tbody, tr, th, td)` above scores one
	 * class plus one element and a bare class does not beat it — so `display: block` won, the
	 * value fell out of its column, and wrapped lines returned to the left margin under the
	 * label instead of aligning with the value.
	 */
	.s-matrix__table td.s-matrix__cell {
		display: grid;
		grid-template-columns: 7.5rem 1fr;
		gap: var(--space-3);
		padding: var(--space-2) 0;
		border: 0;
	}

	.s-matrix__table td.s-matrix__cell:last-child {
		border-block-end: 1px solid var(--colour-rule);
		padding-block-end: var(--space-5);
	}

	/* Baseline-align the label with the first line of a value that runs to two. */
	.s-matrix__cell::before { padding-block-start: 0.15em; }

	.s-matrix__cell::before {
		content: attr(data-label);
		color: var(--colour-heading);
		font-size: 0.8rem;
		font-weight: var(--weight-semibold);
		letter-spacing: 0.06em;
		text-transform: uppercase;
	}
}

/* The market strip's source line. */
.s-stats__note {
	margin-block-start: var(--space-6);
	max-width: none;
	color: var(--colour-body);
	font-size: 0.85rem;
	opacity: 0.75;
}

/* A stat strip on white. Grey is the default because it usually follows white body copy. */
.s-stats--plain { background: var(--colour-surface); }

/* framework -------------------------------------------------------------------------- */

/*
 * A diagram beside the thing it diagrams. The picture is generated SVG, so it scales with its
 * column and needs no art direction at breakpoints — which is the reason it is a diagram rather
 * than a photograph of a whiteboard.
 */
.s-framework__body { display: grid; gap: var(--space-10); }

@media (min-width: 62rem) {
	/*
	 * The diagram is nearly square and the list is eight short items, so a even split wastes the
	 * list's column and crowds the picture. Measured against the artwork's own 1020x820.
	 */
	.s-framework__body { grid-template-columns: minmax(0, 5fr) minmax(0, 6fr); gap: var(--space-12); align-items: center; }
	/* Diagram alone: one column, and held short of full width so it does not become a mural. */
	.s-framework__body--figure { grid-template-columns: minmax(0, 1fr); }
	.s-framework__body--figure .s-framework__figure { max-width: 46rem; margin-inline: auto; }
}

.s-framework__figure { margin: 0; }

.s-framework__diagram { width: 100%; height: auto; }

.s-framework__list {
	display: grid;
	gap: var(--space-6) var(--space-8);
	margin: 0;
	padding: 0;
	max-width: none;
	/* The ACSC numbers these, so the numbers are shown rather than suppressed. */
	list-style: decimal;
	padding-inline-start: var(--space-6);
}

@media (min-width: 44rem) {
	.s-framework__list { grid-template-columns: repeat(2, minmax(0, 1fr)); }
	.s-framework__list--single { grid-template-columns: minmax(0, 1fr); }
}

.s-framework__list::marker { color: var(--colour-blue); }

.s-framework__item { padding-inline-start: var(--space-2); }

.s-framework__name { margin: 0; font-size: 1.0625rem; font-weight: var(--weight-semibold); }

.s-framework__text { margin: var(--space-2) 0 0; max-width: none; font-size: 1rem; }

.s-framework__note {
	margin-block-start: var(--space-10);
	max-width: 60rem;
	color: var(--colour-body);
	font-size: 0.85rem;
	font-style: italic;
	opacity: 0.8;
}

/* stepped ----------------------------------------------------------------------------- */

/*
 * A sequence, drawn as markers on one rail. This exists so that content whose order is the point
 * stops looking like content whose order is incidental: pair-row's numbered variant was carrying
 * "how an assessment runs", "how it actually goes" and "getting started" in the same grid it uses
 * for lists of features, and on a page that already had one such grid it read as the same section
 * twice.
 */
.s-stepped__list {
	position: relative;
	display: grid;
	gap: var(--space-8);
	margin: 0;
	padding: 0;
	max-width: none;
	list-style: none;
	counter-reset: step;
}

.s-stepped__step {
	counter-increment: step;
	position: relative;
	/* Narrow: the rail runs down the left and the marker sits on it. */
	padding-inline-start: var(--space-8);
}

.s-stepped__marker {
	position: absolute;
	inset-inline-start: 0;
	inset-block-start: 0.15em;
	width: 1.75rem;
	height: 1.75rem;
	border-radius: 100px;
	background: var(--colour-blue);
	color: #fff;
	font-size: 0.8rem;
	font-weight: var(--weight-semibold);
	font-variant-numeric: tabular-nums;
	display: grid;
	place-items: center;
	/* Above the rail, which passes behind it. */
	z-index: 1;
}

.s-stepped__marker::before { content: counter(step); }

/*
 * The rail. Drawn on the list rather than between items so it is one continuous line, and inset
 * from both ends so it begins and ends at a marker's centre rather than in empty space.
 */
.s-stepped__list::before {
	content: "";
	position: absolute;
	background: var(--colour-rule);
	inset-inline-start: 0.875rem;
	inset-block: 1rem;
	width: 1px;
}

@media (min-width: 52rem) {
	/*
	 * Wide: the rail turns horizontal and the steps become columns hanging off it. The inset is
	 * half a column, so the line spans marker centre to marker centre — a rail that ran the full
	 * width would imply a step before the first and after the last.
	 */
	.s-stepped__list {
		grid-template-columns: repeat(var(--steps, 3), minmax(0, 1fr));
		gap: var(--space-8);
		padding-block-start: var(--space-8);
	}

	.s-stepped__step { padding-inline-start: 0; }

	.s-stepped__marker { inset-block-start: calc(-1 * var(--space-8) - 0.5rem); }

	.s-stepped__list::before {
		inset-block: auto;
		inset-block-start: calc(0.875rem - 0.5rem);
		/*
		 * Marker centre to marker centre. The markers sit at the START of their column, not its
		 * centre, so insetting by half a column — which is what this did first — pushed the rail
		 * a full marker to the right: it began after step one and ran past step three. The right
		 * inset is one column less its own half-marker.
		 */
		inset-inline-start: 0.875rem;
		inset-inline-end: calc(
			(100% - (var(--steps, 3) - 1) * var(--space-8)) / var(--steps, 3) - 0.875rem
		);
		width: auto;
		height: 1px;
	}
}

.s-stepped__heading {
	margin: 0;
	font-size: 1.0625rem;
	font-weight: var(--weight-semibold);
}

.s-stepped__body {
	margin: var(--space-2) 0 0;
	max-width: 42ch;
	color: var(--colour-body);
	font-size: 1rem;
}

/* routes ------------------------------------------------------------------------------ */

/*
 * Alternatives, stacked full width. Not a grid, because a grid says "here are three things you
 * get" and these are three things of which the reader takes exactly one. The blue edge is the
 * branch; there are no numbers, because numbering mutually exclusive options implies a preferred
 * one and on the Essential Eight page the honest answer is that it depends on the assessment.
 */
.s-routes__list {
	display: grid;
	gap: var(--space-4);
	/*
	 * Centred, not just capped. Capping alone left a 1023px list against the left edge of a 1360px
	 * wrap under a centred heading — measured 168px off centre at every viewport width, which read
	 * as a broken section rather than a narrow one.
	 */
	margin-inline: auto;
	padding: 0;
	max-width: 64rem;
	list-style: none;
}

/*
 * The wash. Drawn on a pseudo-element and masked rather than set as the section's own background,
 * for two reasons: the artwork's top edge is close to the band colour but not identical, and a
 * mask fades it out without needing to know which surface the section landed on. An alpha mask
 * also avoids fading a light colour "to transparent", which some engines take through grey.
 *
 * If mask-image is unsupported the artwork simply shows in full, which is a faint seam rather
 * than a missing background.
 */
.s-routes--washed {
	position: relative;
	/* The wash layer is as tall as the picture, which may be taller than the band it sits in. */
	overflow: clip;
}

.s-routes--washed::before {
	content: "";
	position: absolute;
	/*
	 * Anchored to the bottom and sized by the artwork's own ratio rather than stretched over the
	 * section. Spanning the section instead put the mask's fade wherever the copy happened to end,
	 * which for a band taller than the picture meant the fade landed in empty space above it and
	 * the picture's top edge was left hard.
	 */
	inset: auto 0 0 0;
	aspect-ratio: var(--routes-wash-ratio, 3 / 1);
	background: var(--routes-wash) 50% 100% / 100% 100% no-repeat;
	-webkit-mask-image: linear-gradient(180deg, transparent 0, #000 30%);
	mask-image: linear-gradient(180deg, transparent 0, #000 30%);
	pointer-events: none;
}

/* The copy sits over the wash: positioned, so it paints after the pseudo-element. */
.s-routes--washed > .wrap { position: relative; }

/* A white card with the branch on its edge. */
.s-routes__route {
	display: grid;
	gap: var(--space-1) var(--space-8);
	padding: var(--space-5) var(--space-6);
	border-inline-start: 3px solid var(--colour-blue);
	background: var(--colour-surface);
}

/* On grey the panel needs to be the lighter of the two, or the branch reads as a hole. */
.section--grey .s-routes__route { background: #fff; }

@media (min-width: 52rem) {
	/*
	 * Two columns, with the route name held to a fixed measure so every body starts on the same
	 * vertical line. Same reasoning as the register, opposite proportion: there the name is a
	 * label and here it is the choice, so it gets a third of the row rather than a fifth.
	 */
	.s-routes__route { grid-template-columns: minmax(0, 18rem) minmax(0, 1fr); align-items: start; }
}

.s-routes__heading {
	margin: 0;
	font-size: 1.0625rem;
	font-weight: var(--weight-semibold);
}

.s-routes__body {
	margin: 0;
	max-width: 62ch;
	color: var(--colour-body);
	font-size: 1rem;
}

/* reframe ----------------------------------------------------------------------------- */

/*
 * A correction, not a comparison. The FROM is set quietly because it belongs to the reader and
 * arguing with it loudly is how a reader digs in; the TO carries the weight because that is the
 * only side that is ours to assert.
 */
.s-reframe__list {
	display: grid;
	gap: 0;
	margin: 0;
	padding: 0;
	max-width: 68rem;
	margin-inline: auto;
	border-block-start: 1px solid var(--colour-rule);
}

.s-reframe__shift {
	display: grid;
	gap: var(--space-2) var(--space-6);
	padding-block: var(--space-6);
	border-block-end: 1px solid var(--colour-rule);
	align-items: center;
	/* Narrow: stacked, with the arrow turned to point down the page. */
	grid-template-columns: minmax(0, 1fr);
	justify-items: start;
}

@media (min-width: 48rem) {
	.s-reframe__shift {
		grid-template-columns: minmax(0, 1fr) 2.5rem minmax(0, 1.15fr);
		gap: var(--space-6);
	}
}

.s-reframe__from {
	margin: 0;
	color: var(--colour-body);
	font-size: 1rem;
	font-weight: var(--weight-regular);
	line-height: 1.45;
}

.s-reframe__to {
	margin: 0;
	color: var(--colour-body-strong);
	font-size: 1.1875rem;
	font-weight: var(--weight-semibold);
	line-height: 1.4;
}

.s-reframe__arrow {
	margin: 0;
	color: var(--colour-blue);
	font-size: 1.25rem;
	line-height: 1;
	/* Down the page when stacked, across when there are columns to cross. */
	transform: rotate(90deg);
}

.s-reframe__arrow::before { content: "\2192"; }

@media (min-width: 48rem) {
	.s-reframe__arrow { transform: none; justify-self: center; }
}

/* plays ------------------------------------------------------------------------------- */

/*
 * Three blocks, not three cards. Each play states a council's situation before it names a remedy,
 * and a situation is a paragraph rather than a card's worth of words.
 */
.s-plays__list {
	display: grid;
	gap: var(--space-12);
	margin: 0;
	padding: 0;
	max-width: none;
	list-style: none;
}

.s-plays__play {
	display: grid;
	gap: var(--space-8);
	align-items: center;
}

@media (min-width: 56rem) {
	.s-plays__play--figured {
		grid-template-columns: minmax(0, 6fr) minmax(0, 5fr);
		gap: var(--space-12);
	}

	/*
	 * The alternation. Ordering the columns rather than reordering the markup, so the copy stays
	 * first in the document for a screen reader and for print regardless of which side it is on.
	 */
	.s-plays__play--flipped .s-plays__copy { order: 2; }
	.s-plays__play--flipped .s-plays__figure { order: 1; }

	/*
	 * No artwork: one column, and the copy takes a wider measure than it would beside a picture.
	 * Held short of the wrap so three stacked blocks read as an editorial column rather than as
	 * text abandoned against the left edge.
	 */
	.s-plays__play--plain .s-plays__copy { max-width: 58rem; }
	.s-plays__play--plain .s-plays__heading { max-width: 34ch; }
	.s-plays__play--plain .s-plays__problem,
	.s-plays__play--plain .s-plays__body,
	.s-plays__play--plain .s-plays__outcome { max-width: 72ch; }
}

.s-plays__eyebrow {
	margin: 0;
	color: var(--colour-blue-text);
	font-size: 0.75rem;
	font-weight: var(--weight-semibold);
	letter-spacing: 0.08em;
	text-transform: uppercase;
}

.s-plays__heading {
	margin: var(--space-2) 0 0;
	max-width: 30ch;
	font-size: clamp(1.375rem, 2.2vw, 1.75rem);
	font-weight: var(--weight-light);
	line-height: 1.2;
}

/* The council's own situation, ruled off because it is the only part that is not about Ericom. */
.s-plays__problem {
	margin: var(--space-5) 0 0;
	padding-inline-start: var(--space-5);
	border-inline-start: 2px solid var(--colour-rule);
	max-width: 54ch;
	color: var(--colour-body);
	font-size: 1rem;
}

.s-plays__body {
	margin: var(--space-5) 0 0;
	max-width: 54ch;
	font-size: 1.0625rem;
}

.s-plays__outcome {
	margin: var(--space-4) 0 0;
	max-width: 54ch;
	color: var(--colour-body);
	font-size: 0.9375rem;
}

.s-plays__figure { margin: 0; }

.s-plays__image { width: 100%; height: auto; display: block; }

/* pillars ----------------------------------------------------------------------------- */

/*
 * Columns of cases under a named heading. The heading and its one-line description are small; the
 * cases are the content and take the weight. That inversion is deliberate: a reader arriving here
 * does not need "safety" defined, they need to see that it means fall detection and duress alerts.
 */
.s-pillars__list {
	display: grid;
	gap: var(--space-10) var(--space-8);
	margin: 0;
	padding: 0;
	max-width: none;
	list-style: none;
	grid-template-columns: minmax(0, 1fr);
}

@media (min-width: 52rem) {
	/*
	 * One column per pillar rather than auto-fit. Three pillars are the model, not a coincidence
	 * of how much content exists, and a pillar wrapping to its own row would read as a fourth
	 * category rather than as the third one running long.
	 */
	.s-pillars__list { grid-template-columns: repeat(var(--pillars, 3), minmax(0, 1fr)); }
}

.s-pillars__pillar {
	display: flex;
	flex-direction: column;
	/* A hairline above rather than a box around: these are groupings, not cards to choose between. */
	padding-block-start: var(--space-5);
	border-block-start: 2px solid var(--colour-blue);
}

.s-pillars__name {
	margin: 0;
	font-size: 1.125rem;
	font-weight: var(--weight-semibold);
	line-height: 1.3;
}

.s-pillars__body {
	margin: var(--space-2) 0 0;
	color: var(--colour-body);
	font-size: 0.9375rem;
	line-height: 1.5;
}

.s-pillars__cases {
	margin: var(--space-5) 0 0;
	padding: 0;
	list-style: none;
	display: grid;
	gap: var(--space-2);
}

.s-pillars__case {
	position: relative;
	padding-inline-start: var(--space-5);
	font-size: 1rem;
	line-height: 1.45;
}

/*
 * A drawn marker rather than list-style, so it sits on the first line's baseline whatever the
 * case wraps to. Squared off, because these are items in an inventory and not bullet points in
 * an argument.
 */
.s-pillars__case::before {
	content: "";
	position: absolute;
	inset-inline-start: 0;
	inset-block-start: 0.55em;
	width: 0.4rem;
	height: 0.4rem;
	background: var(--colour-blue);
}

/* register ---------------------------------------------------------------------------- */

/*
 * A schedule, not a menu. Ruled rows with no fills and no boxes, so eight items read as one
 * document rather than eight things on offer. This exists because the Essential Eight page was
 * carrying its centrepiece in the same card grid three other pages use for capabilities, and a
 * page whose whole argument is "this is a published standard" cannot look like a product list.
 */
.s-register__list {
	display: grid;
	margin: 0;
	padding: 0;
	max-width: none;
	list-style: none;
	counter-reset: strategy;
	border-block-start: 1px solid var(--colour-rule);
}

.s-register__row {
	counter-increment: strategy;
	display: grid;
	gap: var(--space-1) var(--space-6);
	padding-block: var(--space-6);
	border-block-end: 1px solid var(--colour-rule);
	/*
	 * Narrow: the number sits beside the name and the body drops beneath both, so the row still
	 * reads as a row rather than becoming three stacked lines with a loose numeral on top.
	 */
	grid-template-columns: 2.5rem minmax(0, 1fr);
	grid-template-areas:
		"number name"
		".      body";
}

@media (min-width: 56rem) {
	/*
	 * Wide: three columns. The name column is fixed rather than fractional so every strategy
	 * name occupies the same width and the body copy starts on one vertical line down the whole
	 * register — which is what makes it scan as a schedule.
	 */
	.s-register__row {
		grid-template-columns: 3rem minmax(0, 15rem) minmax(0, 1fr);
		grid-template-areas: "number name body";
		gap: var(--space-8);
		align-items: start;
	}
}

.s-register__number {
	grid-area: number;
	margin: 0;
	color: var(--colour-blue);
	font-size: 0.95rem;
	font-weight: var(--weight-semibold);
	font-variant-numeric: tabular-nums;
	line-height: 1.5;
}

/* Drawn from the counter, so the ol keeps its real numbering for assistive technology. */
.s-register__number::before { content: counter(strategy, decimal-leading-zero); }

.s-register__name {
	grid-area: name;
	margin: 0;
	font-size: 1.0625rem;
	font-weight: var(--weight-semibold);
	line-height: 1.45;
}

.s-register__text {
	grid-area: body;
	margin: 0;
	max-width: 58ch;
	color: var(--colour-body);
	font-size: 1rem;
}

.s-register__note {
	margin-block-start: var(--space-8);
	max-width: 60rem;
	color: var(--colour-body);
	font-size: 0.85rem;
	font-style: italic;
	opacity: 0.8;
}

/* tier stack ------------------------------------------------------------------------- */

/*
 * Three tiers that inherit rather than compare. Each column carries only what it adds, so the
 * reader's eye travels left to right accumulating rather than scanning a grid for differences.
 */
.s-tiers__list {
	display: grid;
	gap: var(--space-8);
	margin: 0;
	padding: 0;
	max-width: none;
	list-style: none;
	counter-reset: tier;
}

@media (min-width: 58rem) {
	.s-tiers__list { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--space-6); }
}

.s-tiers__tier {
	display: flex;
	flex-direction: column;
	padding: var(--space-6);
	border-radius: var(--radius);
	background: var(--colour-surface);
	/*
	 * The rule thickens across the three, so the escalation is visible before a word is read.
	 * The base tier is the quietest, which is the opposite of how a pricing table usually
	 * shouts at the cheapest column.
	 */
	border-block-start: 3px solid var(--colour-blue-bright);
}

.s-tiers__tier:nth-child(2) { border-block-start-width: 5px; border-block-start-color: var(--colour-blue); }
.s-tiers__tier:nth-child(3) { border-block-start-width: 8px; border-block-start-color: var(--colour-blue-dark); }

.section--grey .s-tiers__tier { background: var(--colour-surface); }

.s-tiers__head { padding-block-end: var(--space-5); border-block-end: 1px solid var(--colour-rule); }

.s-tiers__name { margin: 0; font-size: 1.375rem; font-weight: var(--weight-semibold); }

.s-tiers__unit {
	margin: var(--space-1) 0 0;
	max-width: 22ch;
	color: var(--colour-body);
	font-size: 0.8125rem;
	line-height: 1.35;
}

.s-matrix__tier-unit {
	display: block;
	color: var(--colour-body);
	font-size: 0.75rem;
	font-weight: var(--weight-regular);
	line-height: 1.3;
}

.s-tiers__price {
	margin: var(--space-2) 0 0;
	color: var(--colour-blue-text);
	font-size: 1.6rem;
	font-weight: var(--weight-light);
	font-variant-numeric: tabular-nums;
	line-height: 1.1;
}

.s-tiers__capacity,
.s-tiers__summary { margin: var(--space-2) 0 0; max-width: none; font-size: 0.95rem; }

.s-tiers__summary { color: var(--colour-body); }

.s-tiers__inherits {
	margin: var(--space-5) 0 0;
	color: var(--colour-blue-text);
	font-size: 0.8rem;
	font-weight: var(--weight-semibold);
	letter-spacing: 0.06em;
	text-transform: uppercase;
}

.s-tiers__services {
	margin: var(--space-4) 0 0;
	padding: 0;
	max-width: none;
	list-style: none;
	display: grid;
	gap: var(--space-4);
}

.s-tiers__service { padding-inline-start: var(--space-5); border-inline-start: 2px solid var(--colour-rule); }

.s-tiers__service-name {
	display: block;
	font-size: 1rem;
	font-weight: var(--weight-semibold);
	color: var(--colour-heading);
}

.s-tiers__service-body { display: block; margin-block-start: var(--space-1); font-size: 0.95rem; }

/* The base tier has nothing to inherit, so its list starts where the others' "plus" line does. */
.s-tiers__tier--base .s-tiers__services { margin-block-start: calc(var(--space-5) + var(--space-4) + 1.1em); }

@media (max-width: 57.9375rem) {
	.s-tiers__tier--base .s-tiers__services { margin-block-start: var(--space-4); }
}

.s-tiers__note {
	margin-block-start: var(--space-8);
	max-width: none;
	color: var(--colour-body);
	font-size: 0.85rem;
	font-style: italic;
	opacity: 0.8;
}

/* catalogue -------------------------------------------------------------------------- */

/*
 * Built to be scanned rather than read. Groups run down the page and modules across, because a
 * reader looking for one capability finds it faster in a short labelled block than in a long
 * alphabetical list — and faster still if the platform name is set apart from the prose.
 */
.s-catalogue__groups { display: grid; gap: var(--space-12); }

.s-catalogue__group-name {
	margin: 0 0 var(--space-5);
	padding-block-end: var(--space-3);
	border-block-end: 1px solid var(--colour-rule);
	font-size: 1.0625rem;
	font-weight: var(--weight-semibold);
	letter-spacing: 0.04em;
	text-transform: uppercase;
	color: var(--colour-heading);
}

.s-catalogue__list {
	display: grid;
	gap: var(--space-6) var(--space-8);
	margin: 0;
	padding: 0;
	max-width: none;
	list-style: none;
}

@media (min-width: 44rem) {
	.s-catalogue__list { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (min-width: 68rem) {
	.s-catalogue__list { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

.s-catalogue__name {
	display: flex;
	flex-wrap: wrap;
	align-items: baseline;
	gap: var(--space-2) var(--space-3);
	margin: 0;
	max-width: none;
	font-size: 1.0625rem;
	font-weight: var(--weight-semibold);
	color: var(--colour-heading);
}

/*
 * The platform as a quiet badge rather than a coloured pill. It is a fact a reader is scanning
 * for, not a feature being sold, and eleven bright pills in one section reads as a toolbar.
 */
.s-catalogue__platform {
	padding: 0.15em 0.5em;
	border: 1px solid var(--colour-rule);
	border-radius: var(--radius-pill);
	color: var(--colour-blue-text);
	font-size: 0.75rem;
	font-weight: var(--weight-semibold);
	letter-spacing: 0.04em;
	white-space: nowrap;
}

.s-catalogue__text { margin: var(--space-2) 0 0; max-width: none; font-size: 0.95rem; }

.s-catalogue__note {
	margin-block-start: var(--space-12);
	max-width: 62rem;
	color: var(--colour-body);
	font-size: 0.85rem;
	font-style: italic;
	opacity: 0.8;
}

/* checklist -------------------------------------------------------------------------- */

/*
 * Dense on purpose. Forty-five items in four columns, no descriptions, because this section is
 * looked up rather than read — and because the moment it gains prose it becomes the catalogue,
 * which is a different section answering a different question.
 */
.s-checklist__group + .s-checklist__group { margin-block-start: var(--space-10); }

.s-checklist__group-name {
	display: flex;
	align-items: baseline;
	gap: var(--space-3);
	margin: 0 0 var(--space-5);
	padding-block-end: var(--space-3);
	border-block-end: 1px solid var(--colour-rule);
	font-size: 0.9rem;
	font-weight: var(--weight-semibold);
	letter-spacing: 0.06em;
	text-transform: uppercase;
	color: var(--colour-heading);
}

.s-checklist__count {
	padding: 0.1em 0.5em;
	border-radius: var(--radius-pill);
	background: var(--colour-blue);
	color: var(--colour-on-dark);
	font-size: 0.75rem;
	letter-spacing: 0;
}

.s-checklist__list {
	display: grid;
	gap: var(--space-3) var(--space-8);
	margin: 0;
	padding: 0;
	max-width: none;
	list-style: none;
}

@media (min-width: 40rem) { .s-checklist__list { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (min-width: 58rem) { .s-checklist__list { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@media (min-width: 74rem) { .s-checklist__list { grid-template-columns: repeat(4, minmax(0, 1fr)); } }

.s-checklist__item {
	position: relative;
	padding-inline-start: var(--space-6);
	font-size: 0.95rem;
	line-height: 1.4;
}

/*
 * A drawn tick, not a character. Forty-five list items each announcing "check mark" is a worse
 * experience than a bulleted list, and a pasted comparison should come out as plain names.
 */
.s-checklist__item::before {
	content: "";
	position: absolute;
	inset-inline-start: 0;
	inset-block-start: 0.35em;
	width: 0.5rem;
	height: 0.28rem;
	border-inline-start: 2px solid var(--colour-blue);
	border-block-end: 2px solid var(--colour-blue);
	transform: rotate(-45deg);
}

.s-checklist__note {
	margin-block-start: var(--space-10);
	max-width: 62rem;
	color: var(--colour-body);
	font-size: 0.85rem;
	font-style: italic;
	opacity: 0.8;
}

/* timeline --------------------------------------------------------------------------- */

/*
 * The page's one dark band. Ink rather than the blue-black used by the estate and signal bands,
 * because those two are showing you technology working and this is showing you a Tuesday.
 */
.s-timeline {
	padding-block: clamp(var(--space-16), 8vw, var(--space-24));
	background: var(--colour-surface-dark);
	color: var(--colour-on-dark);
}

.s-timeline__head { max-width: 46rem; margin-block-end: var(--space-16); }

.s-timeline__heading {
	margin: 0;
	color: var(--colour-on-dark);
	font-size: clamp(1.9rem, 1.3rem + 1.8vw, 3rem);
	font-weight: var(--weight-light);
	line-height: 1.1;
}

.s-timeline__intro {
	margin: var(--space-5) 0 0;
	max-width: none;
	color: rgb(255 255 255 / 0.8);
}

.s-timeline__list { margin: 0; padding: 0; max-width: none; list-style: none; }

/*
 * The spine. On the step rather than the list, so the last step can stop it dead instead of it
 * running on past the final event into empty space.
 */
.s-timeline__step {
	position: relative;
	padding-inline-start: var(--space-8);
	padding-block-end: var(--space-8);
	border-inline-start: 1px solid rgb(255 255 255 / 0.16);
}

.s-timeline__step:last-child { padding-block-end: 0; border-inline-start-color: transparent; }

/* The node. Drawn, so nothing has to load for the spine to read as a sequence. */
.s-timeline__step::before {
	content: "";
	position: absolute;
	inset-inline-start: -0.3125rem;
	inset-block-start: 0.45rem;
	width: 0.5625rem;
	height: 0.5625rem;
	border-radius: 50%;
	background: var(--colour-blue-bright);
}

.s-timeline__time {
	margin: 0;
	color: var(--colour-blue-bright);
	font-size: 1.05rem;
	font-weight: var(--weight-semibold);
	font-variant-numeric: tabular-nums;
	letter-spacing: 0.02em;
}

.s-timeline__body {
	margin: var(--space-2) 0 0;
	max-width: 44rem;
	color: rgb(255 255 255 / 0.84);
}

@media (min-width: 52rem) {
	/*
	 * The time moves onto its own axis, so an eye can run down the left edge and feel minutes
	 * become months without reading a word of the events.
	 */
	.s-timeline__step {
		display: grid;
		grid-template-columns: 7.5rem minmax(0, 1fr);
		gap: var(--space-8);
		padding-inline-start: var(--space-10);
	}

	.s-timeline__body { margin: 0; }
	.s-timeline__time { text-align: end; }
}

/*
 * The rewind, inverted. This is the only light panel on a dark band anywhere on the site, and it
 * is deliberate: the section spends nine steps going wrong and this is the moment it stops.
 */
.s-timeline__rewind {
	margin-block-start: var(--space-16);
	padding: clamp(var(--space-8), 4vw, var(--space-12));
	border-radius: var(--radius);
	background: var(--colour-surface);
	color: var(--colour-body);
	border-inline-start: 4px solid var(--colour-blue);
}

.s-timeline__rewind-heading {
	margin: 0 0 var(--space-4);
	color: var(--colour-heading);
	font-size: clamp(1.4rem, 1.1rem + 0.9vw, 1.9rem);
	font-weight: var(--weight-light);
}

.s-timeline__rewind-body { margin: 0; max-width: 48rem; }

.s-timeline__rewind-body + .s-timeline__rewind-body { margin-block-start: var(--space-4); }

.s-timeline__note {
	margin-block-start: var(--space-10);
	max-width: 52rem;
	color: rgb(255 255 255 / 0.55);
	font-size: 0.85rem;
	font-style: italic;
}

/*
 * A deeper hero wash, for a plate shot in daylight.
 *
 * The default gradient was measured against the Vital Signs hero, a dim aged-care bedroom whose
 * copy column reads about 80 luma. The Cyber365 hero is a sunlit office and the same column
 * measures 172 with high variance — a framed artwork and sunlight falling across the wall. Under
 * the standard wash white type sits on that at roughly 39 luma composite, which is legible but
 * leaves the picture visible behind the headline.
 *
 * This pushes the near stops up and moves the falloff later, so the copy column is genuinely
 * dark and the right hand side, where the two people are, is untouched.
 */
.s-hero--wash-deep {
	background-image:
		linear-gradient(
			90deg,
			rgb(20 22 24 / 0.95) 0%,
			rgb(20 22 24 / 0.93) 38%,
			rgb(20 22 24 / 0.82) 50%,
			rgb(20 22 24 / 0.34) 62%,
			rgb(20 22 24 / 0.06) 74%,
			rgb(20 22 24 / 0) 84%
		),
		var(--hero-image, none);
}

/*
 * Doorway: the products beneath a category.
 *
 * Two columns rather than three or four. A doorway carries at most four products, and at three
 * columns a set of four breaks 3+1, which reads as one product being demoted. Two columns take
 * 4, 2 and — with the last card spanning — 3 without ever orphaning one.
 *
 * The tagline is set above the title and larger than it, which inverts the usual order on
 * purpose. A reader on this page is choosing between things they cannot yet tell apart, and
 * "The bill nobody checks" separates two products faster than "Telecom Expense Management"
 * ever will. The title is what they click; the tagline is what makes them.
 */
.s-spokes__list {
	list-style: none;
	margin: 0;
	padding: 0;
	/*
	 * base.css caps every p, ul and ol at --measure so prose stays readable. That is right for
	 * a list of sentences and wrong for a grid of cards: it clamped this to 662px inside a
	 * 1165px wrap, so two columns rendered at 315px each against a half-empty section.
	 */
	max-width: none;
	display: grid;
	grid-template-columns: repeat(2, 1fr);
	gap: 2rem;
}

.s-spokes__item {
	display: flex;
}

.s-spokes__link {
	display: flex;
	flex-direction: column;
	text-decoration: none;
	color: inherit;
	background: var(--surface-raised, #fff);
	border: 1px solid var(--rule, rgb(0 0 0 / 0.1));
	border-radius: var(--radius, 4px);
	overflow: hidden;
	width: 100%;
	transition: border-color 0.15s ease, transform 0.15s ease;
}

.s-spokes__link:hover,
.s-spokes__link:focus-visible {
	border-color: var(--accent, #0d8bd9);
	transform: translateY(-2px);
}

.s-spokes__media {
	display: block;
	aspect-ratio: 28 / 10;
	overflow: hidden;
	background: var(--ink, #282b2c);
}

.s-spokes__image {
	width: 100%;
	height: 100%;
	object-fit: cover;
	display: block;
}

.s-spokes__text {
	display: block;
	padding: 1.75rem;
}

.s-spokes__tagline {
	display: block;
	color: var(--accent, #0d8bd9);
	font-size: 1.25rem;
	line-height: 1.25;
	margin-bottom: 0.35rem;
}

.s-spokes__title {
	display: block;
	font-weight: 600;
	font-size: 0.9rem;
	letter-spacing: 0.04em;
	text-transform: uppercase;
	color: var(--muted, #55606b);
	margin-bottom: 0.75rem;
}

.s-spokes__body {
	display: block;
	line-height: 1.6;
}

@media (max-width: 700px) {
	.s-spokes__list {
		grid-template-columns: 1fr;
	}
}

/*
 * Solutions index.
 *
 * A list, not a grid of tiles. The archive's job is to show the shape of the portfolio, and
 * a reader scanning for "do they do X" reads a left-aligned list of names far faster than
 * they scan cards — the cards were tried first and every category looked equally important,
 * which is the one thing an index must not say.
 */
.s-catalogue-index__group + .s-catalogue-index__group {
	margin-top: 3.5rem;
	padding-top: 3.5rem;
	border-top: 1px solid var(--rule, rgb(0 0 0 / 0.1));
}

.s-catalogue-index__name {
	margin: 0 0 0.25rem;
}

.s-catalogue-index__name a {
	text-decoration: none;
	color: inherit;
}

.s-catalogue-index__name a:hover,
.s-catalogue-index__name a:focus-visible {
	color: var(--accent, #0d8bd9);
}

.s-catalogue-index__tagline {
	color: var(--accent, #0d8bd9);
	margin: 0 0 0.75rem;
}

.s-catalogue-index__body {
	max-width: 46rem;
	margin: 0;
}

.s-catalogue-index__list {
	list-style: none;
	margin: 1.5rem 0 0;
	padding: 0;
	max-width: none; /* As above: the prose measure would clamp this to two columns. */
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
	gap: 0.75rem 2rem;
}

.s-catalogue-index__link {
	display: block;
	text-decoration: none;
	color: inherit;
	padding: 0.85rem 0;
	border-top: 1px solid var(--rule, rgb(0 0 0 / 0.1));
}

.s-catalogue-index__link:hover .s-catalogue-index__item-name,
.s-catalogue-index__link:focus-visible .s-catalogue-index__item-name {
	color: var(--accent, #0d8bd9);
}

.s-catalogue-index__item-name {
	display: block;
	font-weight: 600;
}

.s-catalogue-index__item-note {
	display: block;
	color: var(--muted, #55606b);
	font-size: 0.9rem;
	margin-top: 0.15rem;
}

/*
 * Industry doorways on the Solutions index.
 *
 * Set apart from the capability list and deliberately given no product grid. Listing what each
 * vertical assembles printed Vital Signs Monitoring three times and Nurse Call twice on one
 * page — the repetition made a portfolio of sixteen pages look like a portfolio of four.
 *
 * Two columns, because there are two of them and there will not be many more; a sector either
 * has a bespoke portfolio worth entering by, or it belongs in the capability list.
 */
.s-vertical-index__list {
	list-style: none;
	margin: 0;
	padding: 0;
	max-width: none;
	display: grid;
	grid-template-columns: repeat(2, 1fr);
	gap: 2rem;
}

.s-vertical-index__link {
	display: block;
	height: 100%;
	text-decoration: none;
	color: inherit;
	background: var(--surface-raised, #fff);
	border: 1px solid var(--rule, rgb(0 0 0 / 0.1));
	border-radius: var(--radius, 4px);
	padding: 1.75rem;
	transition: border-color 0.15s ease, transform 0.15s ease;
}

.s-vertical-index__link:hover,
.s-vertical-index__link:focus-visible {
	border-color: var(--accent, #0d8bd9);
	transform: translateY(-2px);
}

.s-vertical-index__name {
	display: block;
	font-size: 1.5rem;
	line-height: 1.2;
	margin-bottom: 0.25rem;
}

.s-vertical-index__tagline {
	display: block;
	color: var(--accent, #0d8bd9);
	margin-bottom: 0.75rem;
}

.s-vertical-index__body {
	display: block;
	line-height: 1.6;
}

@media (max-width: 700px) {
	.s-vertical-index__list {
		grid-template-columns: 1fr;
	}
}

/*
 * Industry portfolio map.
 *
 * A numbered list rather than cards. The reader has already decided the sector is theirs and is
 * checking coverage, so they scan names — and cards at this length would put six near-identical
 * boxes on a page whose whole job is to feel like one coherent offer rather than six products.
 */
.s-portfolio-map__list {
	list-style: none;
	margin: 0;
	padding: 0;
	max-width: none;
	counter-reset: none;
}

.s-portfolio-map__item {
	display: grid;
	grid-template-columns: 3rem 1fr;
	gap: 1rem;
	align-items: baseline;
	padding: 1.25rem 0;
	border-top: 1px solid var(--rule, rgb(0 0 0 / 0.1));
}

.s-portfolio-map__item:last-child {
	border-bottom: 1px solid var(--rule, rgb(0 0 0 / 0.1));
}

.s-portfolio-map__number {
	font-size: 1.5rem;
	line-height: 1;
	color: var(--accent, #0d8bd9);
}

.s-portfolio-map__name {
	display: block;
	font-size: 1.25rem;
	line-height: 1.3;
	text-decoration: none;
	color: inherit;
}

.s-portfolio-map__name:hover,
.s-portfolio-map__name:focus-visible {
	color: var(--accent, #0d8bd9);
}

.s-portfolio-map__note {
	display: block;
	color: var(--muted, #55606b);
	margin-top: 0.2rem;
	line-height: 1.6;
}

@media (max-width: 600px) {
	.s-portfolio-map__item {
		grid-template-columns: 2rem 1fr;
		gap: 0.75rem;
	}
}

/*
 * Care rings.
 *
 * Diagram left, layers right, and the diagram is given the larger share because it carries the
 * argument — the list is the detail underneath it. Below 900px the diagram goes first and full
 * width rather than shrinking into a stamp, since a 300px version of concentric rings with type
 * inside them is unreadable and worse than nothing.
 */
.s-rings__body {
	display: grid;
	/*
	 * The diagram takes the larger share and then some. At 1.05fr against a 34rem cap it was
	 * rendering at 544px inside a 668px column — capped below the space it had, and the supplied
	 * artwork carries icons and two labelled arrows that need the size to be legible at all.
	 */
	grid-template-columns: 1.3fr 1fr;
	gap: 3rem;
	align-items: center;
}

.s-rings__figure img {
	display: block;
	width: 100%;
	height: auto;
	max-width: 46rem;
	margin-inline: auto;
}

.s-rings__list {
	list-style: none;
	margin: 0;
	padding: 0;
	max-width: none;
}

/* No diagram: one column of content, and the list spreads across it. */
.s-rings__body--no-figure {
	grid-template-columns: 1fr;
}

@media (min-width: 60rem) {
	/*
	 * Two columns rather than three. These items are paragraphs, not labels: at three the
	 * measure drops near 30 characters and every one of them breaks into a ragged stack.
	 * Two holds a readable line and still uses the width the missing diagram left behind.
	 */
	.s-rings__body--no-figure .s-rings__list {
		display: grid;
		grid-template-columns: repeat(2, minmax(0, 1fr));
		gap: 0 var(--space-10);
		align-items: start;
	}

	/*
	 * The rule between items is what separates them in a single column, but across two it
	 * would draw a line above the first item of each row including the top one. Applied by
	 * row position instead: every item gets the spacing, and the first two get no rule.
	 */
	.s-rings__body--no-figure .s-rings__item + .s-rings__item {
		margin-top: 0;
	}

	.s-rings__body--no-figure .s-rings__item {
		margin-top: 2rem;
		padding-top: 2rem;
		border-top: 1px solid var(--colour-rule, #ddd);
	}

	.s-rings__body--no-figure .s-rings__item:nth-child(-n + 2) {
		margin-top: 0;
		padding-top: 0;
		border-top: 0;
	}
}

.s-rings__item + .s-rings__item {
	margin-top: 2rem;
	padding-top: 2rem;
	border-top: 1px solid var(--colour-rule, #ddd);
}

.s-rings__name {
	margin: 0 0 0.4rem;
	font-size: 1.25rem;
	line-height: 1.3;
}

.s-rings__text {
	margin: 0;
	line-height: 1.65;
}

@media (max-width: 900px) {
	.s-rings__body {
		grid-template-columns: 1fr;
		gap: 2.5rem;
	}
}

/*
 * The layer tag on a portfolio row. Set after the note rather than before the name: the reader
 * is scanning product names, and a column of category labels down the left would compete with
 * the thing they are actually looking for.
 */
.s-portfolio-map__layer {
	display: inline-block;
	margin-top: 0.5rem;
	font-size: 0.75rem;
	letter-spacing: 0.09em;
	text-transform: uppercase;
	color: var(--colour-blue-text, #00669e);
	background: #eaf4fb;
	border-radius: 999px;
	padding: 0.2rem 0.7rem;
}

/*
 * The line that lands after a pair-row's items. Set at reading measure and centred against the
 * columns above it, so it reads as a conclusion drawn from them rather than a fourth item.
 */
.s-pairs__note {
	margin: 2.5rem auto 0;
	max-width: var(--measure);
	font-size: 1.125rem;
	line-height: 1.65;
	text-align: center;
}

/* The route out of the capability list, offered before the reader has to scroll past it. */
.s-archive-head__aside {
	margin: 1rem auto 0;
	max-width: var(--measure);
	color: var(--colour-body, #404040);
}

/* =============================================================================
 * Capability grid
 *
 * Seven photographic doorways, replacing three abstractions over one stock photograph.
 *
 * The grid is twelve columns rather than a repeat() of equal cards, because the cards are
 * not equal. The lead spans five and the two beside it span the rest, then the remaining
 * four take three each. That asymmetry is the only thing on the page telling a reader where
 * most people start, and a row of seven identical tiles would say nothing.
 */
.s-capability {
	padding-block: var(--space-16);
}

/*
 * Surfaces, so the homepage keeps the rhythm the rest of the site has.
 *
 * Measured against the Managed IT page, which runs ten sections across four surfaces and never
 * puts two of the same next to each other: charcoal, white, grey, white, charcoal, grey, blue,
 * grey, white, charcoal. The homepage after the first cut had five consecutive white sections,
 * 3,198px of unbroken white, because the greys that had been carrying the alternation were the
 * sections that got removed.
 *
 * Solutions takes grey and industries stays white, so the two routers read as two things rather
 * than one long list.
 */
.s-capability--solutions { background: var(--colour-surface-grey); }

/*
 * The standfirst sits under the heading, not opposite it. Pinned to the right of a
 * space-between row it ended up 700px from the words it belongs to on a wide monitor, and
 * read as an unrelated caption rather than as the second half of the same thought.
 */
.s-capability__head {
	margin-block-end: var(--space-12);
}

.s-capability__eyebrow {
	margin: 0 0 var(--space-3);
	color: var(--colour-blue);
	font-size: var(--text-accent);
	font-weight: var(--weight-semibold);
	/* The one place tracking is used on the site, and only because the label is four words
	   of small caps carrying no descenders to give it shape. */
	letter-spacing: 0.14em;
	text-transform: uppercase;
}

.s-capability__heading {
	max-width: 22ch;
	margin: 0;
	font-size: var(--text-h2);
	font-weight: var(--weight-light);
	line-height: 1.05;
}

.s-capability__intro {
	max-width: 46ch;
	margin: var(--space-5) 0 0;
	color: var(--colour-body);
	font-size: var(--text-body);
	line-height: 1.6;
}

.s-capability__grid {
	display: grid;
	grid-template-columns: repeat(12, 1fr);
	gap: var(--space-4);
	margin: 0;
	padding: 0;
	list-style: none;
	/*
	 * base.css caps every p, ul and ol at --measure so prose stays readable, which clamped
	 * this to 662px inside a 1360 wrap and rendered the small cards at 154px. The same trap
	 * caught .s-spokes__list. Right for a list of sentences, wrong for a grid of cards.
	 */
	max-width: none;
}

.s-capability__card {
	/* Three of twelve is the default: four cards to a row. */
	grid-column: span 3;
	display: flex;
}

.s-capability__link {
	display: flex;
	flex: 1;
	flex-direction: column;
	color: inherit;
	text-decoration: none;
}

/*
 * The photograph, with the caption inside it.
 */
.s-capability__art {
	position: relative;
	display: block;
	overflow: hidden;
	aspect-ratio: 4 / 3;
	border-radius: 2px;
	background-color: #2f3436;
	background-image: var(--card-image);
	background-position: center center;
	background-repeat: no-repeat;
	background-size: cover;
}

/*
 * The gradient, taken from Bryce's own comp rather than invented.
 *
 * Measuring that comp against the raw source files band by band: the top third is identical
 * to the source, ratio 0.99 to 1.04; the middle sits at 0.83 to 0.91; the bottom third falls
 * to 0.63 on Infrastructure and 0.64 on Mobility. That is a bottom-up scrim of roughly 37% at
 * the foot, 14% in the middle, and nothing at all above the top third.
 *
 * Re-cut once the labels came off. With only the name and the sentence left, the caption sits
 * in the bottom 28% instead of the bottom 54%, so nothing above that needs covering at all.
 *
 * That let it go stronger and shorter at the same time. Measured across the seven, the
 * brightest patch under the name is 200 luma on the glazing behind Infrastructure; 41% at that
 * height brings white type to 4.53:1, and Mobility, the other bright one, to 4.77:1. All seven
 * now clear 4.5.
 *
 * And it releases by 45% rather than 75%, so 55% of every photograph is completely untouched
 * where before only the top quarter was. More readable and less painted over, which is not
 * usually a trade you get both ends of.
 */
.s-capability__art::before {
	content: "";
	position: absolute;
	inset: 0;
	background: linear-gradient(to top,
		rgba(16, 18, 19, 0.58) 0%,
		rgba(16, 18, 19, 0.50) 14%,
		rgba(16, 18, 19, 0.41) 28%,
		rgba(16, 18, 19, 0.20) 36%,
		rgba(16, 18, 19, 0) 45%);
}

/*
 * The lead's picture takes the height of its row rather than a ratio of its own.
 *
 * A ratio cannot work here. Six columns plus five gaps is 672px where three columns plus two
 * gaps is 328, so the lead is sixteen pixels wider than twice its neighbour, and the gap is
 * fixed while the columns are fluid. 8/3 came out six pixels tall and every label in the
 * first row sat off the others. Stretching to the row is exact at every width.
 */
.s-capability__card--lead .s-capability__art {
	aspect-ratio: auto;
	flex: 1;
}

.s-capability__body {
	position: absolute;
	inset-inline-start: var(--space-6);
	inset-block-end: var(--space-6);
	max-width: calc(100% - var(--space-6) * 2);
	/* Above the gradient. */
	z-index: 1;
}

.s-capability__verb {
	display: block;
	margin-block-end: var(--space-2);
	color: #6cc3f0;
	font-size: 0.8rem;
	font-weight: var(--weight-semibold);
	letter-spacing: 0.14em;
	text-transform: uppercase;
}

.s-capability__name {
	display: block;
	/* 1.6rem rather than --text-card-h. The token is 1.4 and the comp sets these a step up. */
	font-size: 1.6rem;
	font-weight: var(--weight-regular);
	line-height: 1.15;
	color: var(--colour-on-dark);
	/*
	 * A tight shadow, not a drop shadow. It darkens only the pixels touching each letterform,
	 * which is what stops white type dissolving into busy detail like cable trays or foliage.
	 * Unlike a scrim it costs the photograph nothing.
	 */
	text-shadow: 0 1px 3px rgba(0, 0, 0, 0.55);
}

/*
 * No size step on the lead. The lead is already twice the width of its neighbours, so the
 * width is the hierarchy and the type does not need to repeat it. Giving it a larger name
 * also broke the first row's baselines, since the cards are bottom-aligned.
 */

.s-capability__line {
	display: block;
	/*
	 * The sentence, and only the sentence, keeps clear of the chevron. The mark sits at the
	 * foot of the frame, so it can only ever collide with the last line of this paragraph;
	 * reserving the column on the whole caption instead wrapped "Monitoring and analytics"
	 * onto two lines and knocked its label out of line with the rest of the row.
	 */
	max-width: calc(100% - 2.85rem);
	margin-block-start: var(--space-2);
	color: var(--colour-on-dark);
	font-size: var(--text-accent);
	font-weight: var(--weight-regular);
	line-height: 1.45;
	text-shadow: 0 1px 3px rgba(0, 0, 0, 0.55);
	/*
	 * Two lines reserved whether the sentence needs both. The caption is anchored to the foot
	 * of the frame, so a one-line sentence beside a two-line one lifted its label a whole line
	 * above its neighbours and the row lost its baselines. A third line would break it again,
	 * which is a constraint on the writing rather than on the CSS.
	 */
	min-block-size: 2.9em;
}

.s-capability__arrow {
	position: absolute;
	inset-inline-end: var(--space-6);
	inset-block-end: var(--space-6);
	display: grid;
	place-items: center;
	width: 2.1rem;
	height: 2.1rem;
	border: 1px solid rgba(255, 255, 255, 0.6);
	border-radius: 50%;
	color: var(--colour-on-dark);
	z-index: 1;
}

.s-capability__arrow::after {
	content: "\2192";
	font-size: 0.95rem;
	line-height: 1;
}

/* No hover state. The whole card is the link and the arrow already says so. */

.s-capability__foot {
	display: flex;
	flex-wrap: wrap;
	gap: var(--space-6) var(--space-12);
	align-items: center;
	justify-content: space-between;
	margin-block-start: var(--space-12);
	padding-block-start: var(--space-10);
	border-block-start: 1px solid rgba(40, 43, 44, 0.14);
}

.s-capability__footline {
	max-width: 30ch;
	margin: 0;
	/* No token sits between --text-card-h at 22.4 and --text-h2 at 44.8, and this line wants
	   the step in between. */
	font-size: 2rem;
	font-weight: var(--weight-light);
	line-height: 1.15;
}

@media (min-width: 62rem) {
	/*
	 * Six columns for the lead and three for everything else, which is what makes the rows
	 * come out three then four rather than the four-then-one an even span produced.
	 */
	.s-capability__card--lead { grid-column: span 6; }
}

@media (max-width: 61.99rem) {
	/*
	 * Two up, with the lead taking a row of its own. Seven is odd, so leaving every card at
	 * half width stranded the last one beside an empty half. Full width for the lead makes
	 * the remaining six three tidy pairs, and it is the card that earns the space.
	 *
	 * Its picture goes to 2:1 rather than 8/3, since it is no longer sharing a row and so no
	 * longer has to finish level with anything.
	 */
	.s-capability__card { grid-column: span 6; }

	.s-capability__card--lead { grid-column: span 12; }
	.s-capability__card--lead .s-capability__art { aspect-ratio: 2 / 1; }
}

@media (max-width: 40rem) {
	.s-capability__card,
	.s-capability__card--lead { grid-column: span 12; }

	.s-capability__art,
	.s-capability__card--lead .s-capability__art { aspect-ratio: 3 / 2; }
}

/*
 * The industries variant. Two doorways rather than seven, so they split the row and take a
 * wider picture: 4/3 at half the grid would be 504px tall, which is a poster rather than a
 * card.
 */
@media (min-width: 48rem) {
	.s-capability--industries .s-capability__card { grid-column: span 6; }
	.s-capability--industries .s-capability__art { aspect-ratio: 16 / 9; }
}

/* =============================================================================
 * Calculator
 *
 * Inputs on the left, results on the right, and the results are the larger half because they
 * are the reason anyone touched the inputs.
 */
.s-calc__head { max-width: 46rem; margin-block-end: var(--space-10); }

.s-calc__heading {
	margin: 0 0 var(--space-4);
	font-size: var(--text-h2);
	font-weight: var(--weight-light);
	line-height: 1.05;
}

.s-calc__intro { margin: 0; color: var(--colour-body); font-size: var(--text-body); line-height: 1.6; }

.s-calc__panel {
	display: grid;
	gap: var(--space-10);
	padding: clamp(var(--space-6), 3vw, var(--space-10));
	border-radius: var(--radius);
	background: var(--colour-surface);
}

@media (min-width: 56rem) {
	.s-calc__panel { grid-template-columns: 20rem 1fr; align-items: start; }
	/* The note runs under both columns rather than under the inputs. */
	.s-calc__note { grid-column: 1 / -1; }
}

.s-calc__field { margin: 0 0 var(--space-6); max-width: none; }
.s-calc__field:last-child { margin-block-end: 0; }

.s-calc__label {
	display: block;
	margin-block-end: var(--space-2);
	color: var(--colour-heading);
	font-size: var(--text-accent);
	font-weight: var(--weight-semibold);
}

.s-calc__input {
	width: 100%;
	padding: var(--space-3) var(--space-4);
	border: 1px solid var(--colour-rule);
	border-radius: var(--radius);
	background: var(--colour-surface);
	font-family: inherit;
	font-size: var(--text-body);
	color: var(--colour-heading);
}

.s-calc__input:focus-visible { outline: var(--focus-ring); outline-offset: 1px; border-color: var(--colour-blue); }

.s-calc__hint {
	display: block;
	margin-block-start: var(--space-2);
	color: var(--colour-body);
	font-size: 0.9rem;
	line-height: 1.45;
}

.s-calc__rows, .s-calc__fallback { margin: 0; padding: 0; max-width: none; list-style: none; }

/*
 * Four columns: tier, the annual figure, what is in it, and the difference. The figure is the
 * widest because it is what the reader came for.
 */
.s-calc__row {
	display: grid;
	gap: var(--space-2) var(--space-4);
	align-items: baseline;
	padding-block: var(--space-5);
	border-block-start: 1px solid var(--colour-rule);
}

.s-calc__row:last-child { border-block-end: 1px solid var(--colour-rule); }

@media (min-width: 40rem) {
	.s-calc__row { grid-template-columns: 8rem 1fr 8rem 9rem; }
}

.s-calc__tier { color: var(--colour-heading); font-size: var(--text-accent); font-weight: var(--weight-semibold); }

.s-calc__annual {
	color: var(--colour-heading);
	font-size: 1.75rem;
	font-weight: var(--weight-light);
	line-height: 1;
}

.s-calc__annual small { font-size: 1rem; color: var(--colour-body); }

.s-calc__inclusions { color: var(--colour-body); font-size: 0.9rem; }

.s-calc__delta { font-size: var(--text-accent); font-weight: var(--weight-semibold); }

/*
 * Green for less and charcoal for more, not red. A tier costing more than the reader pays today
 * is frequently the correct answer on this site, because the argument is what is included, and
 * colouring it as an error would be arguing against the page it sits on.
 */
.s-calc__delta--less { color: #1d7a4c; }
.s-calc__delta--more { color: var(--colour-heading); }

.s-calc__baseline { margin: var(--space-6) 0 0; color: var(--colour-body); font-size: var(--text-body); }
.s-calc__empty { margin: 0; color: var(--colour-body); font-size: var(--text-body); }

.s-calc__note { margin: 0; color: var(--colour-body); font-size: 0.9rem; }

/* The monthly figure under the annual one, for the per-user model where the annual number is
   large enough that a reader wants the invoice-sized version beside it. */
.s-calc__monthly { display: block; margin-block-start: 2px; color: var(--colour-body); font-size: 0.9rem; }


/* Contact --------------------------------------------------------------- */

/*
 * Two columns where there is room: the direct line on the left because a reader who wants
 * to ring should not have to read past a form to find the number, and the form on the right
 * for everyone else. One column below that, direct line first for the same reason.
 */
.s-contact__inner {
	display: grid;
	gap: var(--space-10, 2.5rem);
}

@media (min-width: 52rem) {

	.s-contact__inner {
		grid-template-columns: minmax(16rem, 22rem) 1fr;
		gap: var(--space-16, 4rem);
		align-items: start;
	}
}

.s-contact__heading {
	font-size: var(--text-h2, 1.6rem);
	font-weight: var(--weight-light, 300);
	margin-block-end: var(--space-4, 1rem);
}

.s-contact__phone {
	font-size: clamp(1.5rem, 1.1rem + 1.4vw, 2.1rem);
	font-weight: var(--weight-light, 300);
	margin-block-end: var(--space-2, 0.5rem);
}

.s-contact__phone a { text-decoration: none; }
.s-contact__phone a:hover { text-decoration: underline; }

.s-contact__note {
	color: var(--colour-muted, #5a6068);
	font-size: 0.95rem;
	margin-block-end: var(--space-4, 1rem);
	max-width: none;
}

/* Offices --------------------------------------------------------------- */

.s-offices__heading {
	font-size: var(--text-h2, 1.6rem);
	font-weight: var(--weight-light, 300);
}

.s-offices__intro {
	color: var(--colour-muted, #5a6068);
	margin-block-end: var(--space-8, 2rem);
}

.s-offices__list {
	list-style: none;
	margin: 0;
	padding: 0;
	display: grid;
	gap: var(--space-8, 2rem);
	/* Not capped at --measure: base.css caps every list, and it renders these at a third
	   of their column. The same trap the capability grid and the spokes list hit. */
	max-width: none;
}

@media (min-width: 44rem) {
	.s-offices__list { grid-template-columns: repeat(2, 1fr); }
}

@media (min-width: 64rem) {
	.s-offices__list { grid-template-columns: repeat(3, 1fr); }
}

.s-offices__item {
	background: var(--colour-surface, #fff);
	padding: var(--space-6, 1.5rem);
	border-block-start: 3px solid var(--colour-accent, #0082CA);
}

.s-offices__badge,
.s-office__badge {
	text-transform: uppercase;
	letter-spacing: 0.08em;
	font-size: 0.72rem;
	font-weight: 600;
	color: var(--colour-accent, #0082CA);
	margin-block-end: var(--space-2, 0.5rem);
}

.s-offices__name {
	font-size: 1.35rem;
	font-weight: var(--weight-light, 300);
	margin-block-end: var(--space-3, 0.75rem);
}

.s-offices__name a { text-decoration: none; }
.s-offices__name a:hover { text-decoration: underline; }

.s-offices__address,
.s-office__address {
	font-style: normal;
	color: var(--colour-body);
	margin-block-end: var(--space-3, 0.75rem);
}

.s-offices__phone a { text-decoration: none; }
.s-offices__phone a:hover { text-decoration: underline; }

/* The office block on a location page ----------------------------------- */

.s-office__inner {
	display: grid;
	gap: var(--space-10, 2.5rem);
}

@media (min-width: 52rem) {

	.s-office__inner {
		grid-template-columns: minmax(18rem, 26rem) 1fr;
		gap: var(--space-16, 4rem);
		align-items: start;
	}
}

.s-office__card {
	background: var(--colour-surface, #fff);
	padding: var(--space-8, 2rem);
	border-block-start: 3px solid var(--colour-accent, #0082CA);
}

.s-office__heading {
	font-size: 1.6rem;
	font-weight: var(--weight-light, 300);
	margin-block-end: var(--space-4, 1rem);
}

.s-office__line { display: block; }

.s-office__facts {
	margin: var(--space-5, 1.25rem) 0 var(--space-5, 1.25rem);
}

.s-office__fact {
	display: grid;
	grid-template-columns: 5.5rem 1fr;
	gap: var(--space-3, 0.75rem);
	padding-block: 0.4rem;
	border-block-start: 1px solid var(--colour-rule, #e3e6e9);
}

.s-office__fact dt {
	color: var(--colour-muted, #5a6068);
	font-size: 0.9rem;
}

.s-office__fact dd { margin: 0; }
.s-office__fact a { text-decoration: none; }
.s-office__fact a:hover { text-decoration: underline; }

.s-office__areas-heading {
	font-size: 1.1rem;
	font-weight: 600;
	margin-block-end: var(--space-4, 1rem);
}

.s-office__area-list {
	list-style: none;
	margin: 0;
	padding: 0;
	max-width: none;
	display: flex;
	flex-wrap: wrap;
	gap: 0.5rem;
}

.s-office__area-list li {
	background: var(--colour-surface, #fff);
	border: 1px solid var(--colour-rule, #e3e6e9);
	border-radius: var(--radius-pill, 999px);
	padding: 0.3rem 0.85rem;
	font-size: 0.9rem;
}

/* ---------------------------------------------------------------------------
 * s-chain — the Core Code
 *
 * Two columns on wide screens: the connective phrase set quietly and flush right, the value
 * word large and flush left against a rail. That alignment is doing the work. The phrases end
 * at a common edge and the values begin at a common edge, so the nine value words stack into a
 * readable column and the sentences still read across.
 *
 * Below 60rem it collapses to one column with the lead above its value, because a right-aligned
 * narrow column of text on a phone is unreadable and the rail has nothing to align to.
 * ------------------------------------------------------------------------ */

.s-chain__list {
	position: relative;
	display: grid;
	gap: var(--space-5);
	margin: 0;
	padding: 0;
	max-width: none;
	list-style: none;
}
.s-chain__line {
	display: grid;
	gap: var(--space-2);
	margin: 0;
	max-width: none;
}
.s-chain__lead {
	color: var(--colour-body);
	font-size: 1rem;
	line-height: 1.4;
}
.s-chain__value {
	color: var(--colour-blue);
	font-size: 1.75rem;
	font-weight: var(--weight-light);
	line-height: 1.1;
}
.s-chain__coda {
	margin: var(--space-5) 0 0;
	color: var(--colour-body);
	font-size: 1rem;
}

@media (min-width: 60rem) {
	.s-chain__line {
		/*
		 * The rail sits in the gap, so the gap is a fixed measure rather than a fraction: the
		 * line has to land in the same place on every row regardless of how long the phrase is.
		 */
		grid-template-columns: minmax(0, 22rem) minmax(0, 1fr);
		gap: 0 var(--space-8);
		align-items: baseline;
	}
	.s-chain__lead {
		text-align: end;
	}
	.s-chain__value {
		font-size: 2.25rem;
	}

	/*
	 * The rail. On the list rather than between items so it is continuous, and inset by a line
	 * at each end so it starts and finishes level with a value rather than overshooting into
	 * space that implies another link.
	 */
	.s-chain__list::before {
		content: "";
		position: absolute;
		inset-block: 0.75rem;
		inset-inline-start: calc(22rem + var(--space-4));
		width: 1px;
		background: var(--colour-rule);
	}
	.s-chain__coda {
		margin-inline-start: calc(22rem + var(--space-8));
	}
}

/*
 * s-stepped--years — a chronology rather than a process.
 *
 * The counter comes off. A numbered badge beside "1993" tells a reader this is the first of ten
 * things, which the year already told them, and the two numbers then compete. What is left is a
 * plain dot on the rail and the year itself doing the marking, which is why the year grows and
 * picks up tabular figures: it is now the thing being scanned down the column.
 */
.s-stepped--years .s-stepped__step {
	padding-inline-start: var(--space-6);
}
.s-stepped--years .s-stepped__marker {
	width: 0.6875rem;
	height: 0.6875rem;
	/* Sits on the year's optical centre rather than its box top. */
	inset-block-start: 0.6em;
}
.s-stepped--years .s-stepped__marker::before {
	content: none;
}
.s-stepped--years .s-stepped__list::before {
	inset-inline-start: 0.34375rem;
	inset-block: 0.9rem;
}
.s-stepped--years .s-stepped__heading {
	color: var(--colour-heading);
	font-size: 1.5rem;
	font-weight: var(--weight-light);
	font-variant-numeric: tabular-nums;
}
.s-stepped--years .s-stepped__body {
	margin-block-start: var(--space-1);
	max-width: 62ch;
}

/*
 * The contact form is not a card.
 *
 * s-form__form carries a white panel with padding and a radius, which is right on the form band
 * where it sits on grey. Here the section is already white, so the panel is invisible and the
 * padding only indents the fields away from the column edge and out of line with the heading
 * above them. The Gravity Forms field styling is what the class is actually for, so the class
 * stays and the panel comes off.
 */
.s-contact__form {
	padding: 0;
	border-radius: 0;
	background: none;
}

/*
 * Hide the Gravity Forms honeypot.
 *
 * Gravity Forms ships this rule in its own stylesheet, which this theme does not load: the
 * field styling here exists precisely because GF's CSS is off. So the honeypot was rendering
 * as a real, visible field with a randomly chosen label and the giveaway line "this field is
 * for validation purposes and should be left unchanged" underneath it.
 *
 * That is worse than having no honeypot. A person sees a nonsense field and either fills it in,
 * which fails their submission silently, or wonders what it is. A bot reads the same rendered
 * page a person does, sees nothing marked hidden, and skips it exactly as a human would.
 *
 * Off-screen rather than display:none, which is what GF itself does: a field that is display
 * none is trivially detectable as a trap, whereas one positioned out of the viewport still has
 * to be reasoned about.
 */
.gform_wrapper .gform_validation_container,
.gform_wrapper .gf_validation_container {
	position: absolute !important;
	inset-inline-start: -9000px;
	height: 1px;
	overflow: hidden;
}

/* ---------------------------------------------------------------------------
 * s-filter — the industry filter above a card grid
 *
 * Pills on a wrapping row. They wrap rather than scroll horizontally: a hidden scroll track is
 * a control a reader cannot see the extent of, and with a dozen industries the second row is
 * cheaper than the discovery problem.
 *
 * The active state carries a background rather than only a colour, because a colour change
 * alone is a single channel and would be the only thing telling a colourblind reader which
 * filter is currently applied.
 * ------------------------------------------------------------------------ */

.s-filter {
	padding-block: var(--space-8) 0;
}
.s-filter__list {
	display: flex;
	flex-wrap: wrap;
	gap: var(--space-2);
	margin: 0;
	padding: 0;
	max-width: none;
	list-style: none;
}
.s-filter__button {
	padding: var(--space-2) var(--space-4);
	border: 1px solid var(--colour-rule);
	border-radius: 100px;
	background: var(--colour-surface);
	color: var(--colour-body);
	font-family: inherit;
	font-size: var(--text-accent);
	font-weight: var(--weight-semibold);
	line-height: 1.2;
	cursor: pointer;
}
.s-filter__button:hover {
	border-color: var(--colour-blue);
	color: var(--colour-blue-text);
}
.s-filter__button:focus-visible {
	outline: var(--focus-ring);
	outline-offset: 2px;
}
.s-filter__button.is-active {
	/*
	 * The darker blue, not the brand blue. White on #0081c9 is 4.21:1, and WCAG counts bold text
	 * as large only from 18.66px up, so a 16px label is normal text and wants 4.5:1. The darker
	 * variant clears it without changing the colour a reader perceives.
	 */
	border-color: var(--colour-blue-dark);
	background: var(--colour-blue-dark);
	color: var(--colour-on-dark);
}

/*
 * The live region. It is announced, not read: a sighted user already sees the grid change, and
 * a visible "13 case studies" line under the filter is a number nobody asked for.
 */
.s-filter__status {
	position: absolute;
	width: 1px;
	height: 1px;
	margin: -1px;
	padding: 0;
	border: 0;
	overflow: hidden;
	clip-path: inset(50%);
	white-space: nowrap;
}

/* The band that introduces the grid carries no surface of its own; it sits on the page. */
.s-archive-band {
	padding-block-end: 0;
}
.s-grid__intro {
	margin: var(--space-3) 0 0;
	max-width: var(--measure);
	color: var(--colour-body);
}

@media (max-width: 30rem) {
	/*
	 * Eleven industries wrap to seven rows at 375px, which is a third of the screen spent on a
	 * control before any content. Slightly smaller pills cut that without hiding anything behind
	 * a scroll track or a disclosure.
	 */
	.s-filter__button {
		padding: var(--space-1) var(--space-3);
		font-size: 0.875rem;
	}
}

/* ---------------------------------------------------------------------------
 * s-legal — policy documents
 *
 * One column, held to a reading measure. Legal copy is the densest text on the site and the
 * only thing that makes it survivable is a short line, so the measure is tighter than the prose
 * sections rather than looser.
 *
 * Numbered headings sit close to the paragraph they open and further from the one above, so the
 * document reads as sections rather than an even wall. That spacing is the whole design.
 * ------------------------------------------------------------------------ */

.s-legal {
	padding-block: var(--space-16) var(--space-24);
}
.s-legal__inner {
	max-width: 46rem;
}
.s-legal__heading {
	margin: 0;
	font-size: var(--text-h2);
	font-weight: var(--weight-light);
}
.s-legal__updated {
	margin: var(--space-3) 0 var(--space-12);
	color: var(--colour-body);
	font-size: 1rem;
}
.s-legal__body > * {
	max-width: none;
}
.s-legal__body h2 {
	margin: var(--space-12) 0 var(--space-3);
	font-size: 1.5rem;
	font-weight: var(--weight-semibold);
}
.s-legal__body h3 {
	margin: var(--space-8) 0 var(--space-2);
	font-size: 1.0625rem;
	font-weight: var(--weight-semibold);
}
.s-legal__body p,
.s-legal__body li {
	color: var(--colour-body);
	line-height: var(--leading-body);
}
.s-legal__body p {
	margin: 0 0 var(--space-4);
}
.s-legal__body ul {
	margin: 0 0 var(--space-4);
	padding-inline-start: var(--space-6);
}
.s-legal__body li {
	margin-block-end: var(--space-1);
}
.s-legal__body a {
	color: var(--colour-blue-text);
}

/* The document opens on a heading, so the first one must not push away from the date. */
.s-legal__body > h2:first-child {
	margin-block-start: 0;
}

/*
 * The footer legal line.
 *
 * Wraps as a row of items rather than a sentence, so the licence number and the ABN stay whole
 * when the line breaks: half an ABN on one line and half on the next reads as a typo in a number
 * people actually check.
 */
.footer-legal {
	display: flex;
	flex-wrap: wrap;
	align-items: baseline;
	gap: var(--space-2);
	max-width: none;
}
.footer-legal__sep {
	opacity: 0.5;
}

/* ---------------------------------------------------------------------------
 * Campaign landing pages
 *
 * The site navigation is hidden rather than not rendered, so the header keeps the logo, the
 * phone number and the skip link. Somebody who wants to ring instead of typing should be able
 * to, and the logo is what tells them whose page they are on. What goes is the menu: on a page
 * that exists to be answered, every navigation link is an exit.
 *
 * The footer keeps its legal line for the same reason it exists elsewhere, and loses the rest.
 * ------------------------------------------------------------------------ */

.is-landing .site-nav,
.is-landing .footer-nav,
.is-landing .utility-bar {
	display: none;
}

/*
 * Offer left, form right, and the form is visible without scrolling on a laptop. That is the
 * whole layout decision: a landing page whose form is below the fold is a page that paid for a
 * visitor and then hid the thing it wanted them to do.
 */
.s-lp {
	padding-block: var(--space-12) var(--space-16);
}
.s-lp__inner {
	display: grid;
	gap: var(--space-10);
	align-items: start;
}
.s-lp__heading {
	margin: 0;
	/*
	 * Bigger and heavier than a page heading elsewhere on the site. A landing page has about two
	 * seconds to confirm the visitor is in the right place, and the light weight that reads as
	 * confident on a page somebody chose to visit reads as tentative on a page they were sent to.
	 */
	font-size: clamp(2.4rem, 1.6rem + 3.4vw, 4rem);
	font-weight: var(--weight-semibold);
	letter-spacing: -0.02em;
	line-height: 1.02;
}
.s-lp__standfirst {
	margin: var(--space-5) 0 0;
	max-width: 48ch;
	color: var(--colour-body);
	font-size: 1.125rem;
	line-height: var(--leading-body);
}
.s-lp__form {
	background: var(--colour-surface-grey);
}
.s-lp__form-heading {
	margin: 0;
	font-size: 1.5rem;
	font-weight: var(--weight-semibold);
}
.s-lp__form-intro {
	margin: var(--space-2) 0 var(--space-6);
	color: var(--colour-body);
	font-size: 1rem;
}

@media (min-width: 62rem) {
	.s-lp__inner {
		/* The offer gets the wider column; the form only needs enough to not feel cramped. */
		grid-template-columns: minmax(0, 1.1fr) minmax(24rem, 0.9fr);
		gap: var(--space-16);
	}
}

/* ---------------------------------------------------------------------------
 * The landing page form
 *
 * The generic enquiry form is the right shape on /contact/, where somebody has decided to get in
 * touch and will fill in whatever is put in front of them. On a landing page it was taking nine
 * hundred pixels of the screen and visually outweighing the argument that was meant to persuade
 * them to use it.
 *
 * So the short fields pair up, the message box comes down to something a sentence fits in, and
 * the whole panel tightens. Nothing is removed: a shorter form would collect less, and the point
 * of these enquiries is that somebody reads them.
 * ------------------------------------------------------------------------ */

.s-lp__form {
	padding: var(--space-6);
}

/* Name and Organisation share a row, as do Email and Phone. */
.s-lp__form .gform_fields {
	gap: var(--space-4) var(--space-4);
}
.s-lp__form .gfield:not(.gfield--type-textarea):not(.gfield--type-captcha):not(.gform_validation_container) {
	grid-column: span 1;
}
.s-lp__form .gfield--type-textarea,
.s-lp__form .gfield--type-captcha {
	grid-column: 1 / -1;
}
.s-lp__form textarea {
	min-height: 0;
	height: 7rem;
}
.s-lp__form .gfield_label {
	margin-block-end: var(--space-1);
	font-size: 0.875rem;
}
.s-lp__form input[type="text"],
.s-lp__form input[type="email"],
.s-lp__form input[type="tel"],
.s-lp__form select {
	padding-block: var(--space-2);
}
.s-lp__form .gform_footer {
	margin-block-start: var(--space-5);
}

@media (max-width: 40rem) {
	/* One column on a phone: paired fields at this width are two cramped fields, not a tidy row. */
	.s-lp__form .gfield {
		grid-column: 1 / -1;
	}
}

/* The left column of a landing hero: promise, argument, points, proof. */
.s-lp__subhead {
	margin: var(--space-5) 0 0;
	max-width: 24ch;
	color: var(--colour-heading);
	font-size: clamp(1.3rem, 1rem + 1vw, 1.75rem);
	font-weight: var(--weight-semibold);
	line-height: 1.2;
}
.s-lp__points {
	margin: var(--space-8) 0 0;
	padding: 0;
	max-width: 46ch;
	list-style: none;
	display: grid;
	gap: var(--space-3);
}
.s-lp__points li {
	position: relative;
	/*
	 * space-8, not space-7, which does not exist. The scale skips 7, so var(--space-7) resolved
	 * to nothing, the padding collapsed to zero and the absolutely positioned tick landed on top
	 * of the first letter of every line. A missing custom property fails silently: no console
	 * error, no fallback, just a layout that looks like a rendering bug.
	 */
	padding-inline-start: var(--space-8);
	color: var(--colour-body-strong);
	font-size: 1.0625rem;
	line-height: 1.4;
}
/*
 * A drawn tick rather than a list marker or an icon font. It has to read as "included", which a
 * bullet does not, and it must not cost a request.
 */
.s-lp__points li::before {
	content: "";
	position: absolute;
	inset-inline-start: 0;
	inset-block-start: 0.42em;
	width: 0.85rem;
	height: 0.45rem;
	border-inline-start: 2px solid var(--colour-blue);
	border-block-end: 2px solid var(--colour-blue);
	/*
	 * Rotated about its top-left rather than its centre. A rotated box's painted area is wider
	 * than its layout box, and rotating about the centre pushes the long corner back towards the
	 * text by roughly a third of its width.
	 */
	transform-origin: 0 0;
	transform: rotate(-45deg) translate(0.1rem, 0.42rem);
}
.s-lp__trust {
	margin: var(--space-8) 0 0;
	padding-block-start: var(--space-5);
	border-block-start: 1px solid var(--colour-rule);
	max-width: 46ch;
	color: var(--colour-body);
	font-size: 0.9375rem;
}

/*
 * Honour Gravity Forms' own "this field is hidden" class.
 *
 * Second time this has bitten. Gravity Forms ships the rule that acts on this class in its own
 * stylesheet, which this theme does not load, so a field set to hidden in PHP renders in full:
 * correct value selected, label showing, taking up a row. The honeypot had exactly this problem.
 *
 * The field must stay in the DOM and stay submitted, so it is moved out of view rather than
 * removed from the layout: display:none on a field with a value is a good way to have that value
 * arrive empty in some browsers, and this one populates the notification subject line.
 */
.gform_wrapper .gfield_visibility_hidden {
	position: absolute !important;
	width: 1px;
	height: 1px;
	margin: -1px;
	padding: 0;
	border: 0;
	overflow: hidden;
	clip-path: inset(50%);
	white-space: nowrap;
}
