/*
 * Rundle — Redline
 * ============================================================================
 *
 * A professional profile and live CV for a senior HSE and risk manager.
 *
 * DIRECTION
 *
 * Black and red, used the way an engineer uses them: as print. Every drawing
 * this man has ever marked up was black on bone paper with red over the top,
 * because red is what you reach for when you are changing something that is
 * already approved. Revision clouds, redlines, approval stamps, the ring
 * around the thing that has to be looked at again.
 *
 * So this is not a dark interface with a red accent, which is where black and
 * red usually ends up and which looks like every other site that has tried it.
 * It is a printed document: bone stock, black ink, red overprint. The inverted
 * bands are true black rather than a tinted navy, and red appears on them for
 * the same reason it appears on paper — to mark the one thing that matters.
 *
 * RED IS A SIGNAL, NOT A THEME
 *
 * Red marks the live and the current: the availability stamp, the role he is
 * in now, the active page, the figures. Nothing decorative is red. If
 * everything on a drawing is redlined, nothing on it is.
 *
 * There is no third hue. Hierarchy below red is carried by ink weight and
 * rule weight, which is how a document does it and is harder to get right
 * than adding another colour.
 *
 * TYPE WIDTH
 *
 * No reading-measure caps anywhere in this file, and no text-wrap: balance.
 * Text runs the full width of its layout container at every breakpoint.
 * Columns come from the grid, never from capping a paragraph at a character
 * count.
 *
 * ============================================================================
 */

/* ---------------------------------------------------------------- 1. Fonts */

@font-face {
	font-family: "Rundle Display";
	src: url("../fonts/archivo-var.woff2") format("woff2-variations");
	font-weight: 100 900;
	font-stretch: 62% 125%;
	font-display: swap;
	font-style: normal;
}

@font-face {
	font-family: "Rundle Sans";
	src: url("../fonts/manrope-var.woff2") format("woff2-variations");
	font-weight: 200 800;
	font-display: swap;
	font-style: normal;
}

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

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

/* --------------------------------------------------------------- 2. Tokens */

:root {
	/* Ground and ink */
	--paper:      #f2f0ec;
	--paper-sunk: #e8e5df;
	--ink:        #141211;
	--ink-soft:   #46403c;
	--ink-faint:  #5f5852;
	--rule:       #c9c4bb;
	--rule-faint: #ddd9d1;

	/* The signal. One red, used sparingly, on everything that is live. */
	--signal:      #c8102e;
	--signal-wash: #f6e3e5;
	--on-signal:   #ffffff;
	/*
	 * The same signal, lifted for the black band. #c8102e is a print red and
	 * reads correctly on bone paper, but on #0b0a09 it measures 3.36:1 and
	 * fails AA at the sizes the stamp and the controlled-copy mark use. This
	 * is the same hue carried up to 5.9:1 rather than a second accent.
	 */
	--signal-band: #f1566a;

	/* Inverted band: true black, not a tinted one. */
	--band:      #0b0a09;
	--band-ink:  #efece7;
	--band-soft: #9c948c;
	--band-rule: #2a2724;

	/* Type */
	--display: "Rundle Display", "Archivo", "Helvetica Neue", Arial, sans-serif;
	--body:    "Rundle Sans", "Manrope", system-ui, -apple-system, "Segoe UI", sans-serif;
	--data:    "Rundle Data", "IBM Plex Mono", ui-monospace, "SFMono-Regular", Menlo, monospace;

	/* Spacing — a 4px base, named by step rather than size */
	--s1:  0.25rem;
	--s2:  0.5rem;
	--s3:  0.75rem;
	--s4:  1rem;
	--s5:  1.5rem;
	--s6:  2rem;
	--s7:  3rem;
	--s8:  4rem;
	--s9:  6rem;
	--s10: 8rem;

	/* Layout */
	--shell:  1560px;
	--gutter: clamp(1rem, 4vw, 4rem);
	/*
	 * Two column widths, and only two.
	 *
	 * Every rail on the site was a different size: 11rem here, 12 on the CV,
	 * 14 on the competencies, 5.5 on the qualifications, 6 on the early
	 * postings, 8 on the writing dates. Each was fine alone and together they
	 * meant no two sections started at the same x, so the page never settled.
	 *
	 * --rail is the label column. --col-year is the narrow date column. If a
	 * layout needs a left column it uses one of these, and the content edge
	 * runs straight from the top of the page to the bottom.
	 */
	--rail:     11rem;
	--col-year:  7rem;

	--focus: 3px solid var(--signal);
}

@media (prefers-color-scheme: dark) {
	:root:not([data-theme="light"]) {
		--paper:      #0d0c0b;
		--paper-sunk: #141211;
		--ink:        #efece7;
		--ink-soft:   #b0a89f;
		--ink-faint:  #8b837b;
		--rule:       #2e2a26;
		--rule-faint: #211e1b;

		/*
		 * The red lifts on black. #c8102e is correct ink on bone and drops to
		 * about 2:1 against a black ground, so the dark scheme uses a brighter
		 * red that holds 6:1 and keeps the same job.
		 */
		--signal:      #ff4d5e;
		--signal-wash: #2a1012;
		--on-signal:   #140405;

		--band:      #000000;
		--band-ink:  #efece7;
		--band-soft: #9c948c;
		--band-rule: #262320;

		--focus: 3px solid var(--signal);
	}
}

:root[data-theme="dark"] {
	--paper:      #0d0c0b;
	--paper-sunk: #141211;
	--ink:        #efece7;
	--ink-soft:   #b0a89f;
	--ink-faint:  #8b837b;
	--rule:       #2e2a26;
	--rule-faint: #211e1b;

	/*
	 * The red lifts on black. #c8102e is correct ink on bone and drops to
	 * about 2:1 against a black ground, so the dark scheme uses a brighter
	 * red that holds 6:1 and keeps the same job.
	 */
	--signal:      #ff4d5e;
	--signal-wash: #2a1012;
	--on-signal:   #140405;

	--band:      #000000;
	--band-ink:  #efece7;
	--band-soft: #9c948c;
	--band-rule: #262320;

	--focus: 3px solid var(--signal);
}

/* ----------------------------------------------------------------- 3. Base */

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

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

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

body {
	margin: 0;
	background: var(--paper);
	color: var(--ink);
	font-family: var(--body);
	font-size: clamp(1.0625rem, 0.98rem + 0.35vw, 1.1875rem);
	font-weight: 400;
	line-height: 1.62;
	font-variant-numeric: tabular-nums;
	-webkit-font-smoothing: antialiased;
	text-rendering: optimizeLegibility;
}

h1, h2, h3, h4 {
	font-family: var(--display);
	font-weight: 600;
	font-stretch: 112%;
	line-height: 1.08;
	letter-spacing: -0.015em;
	margin: 0;
	text-wrap: initial;
}

p { margin: 0 0 var(--s4); }
p:last-child { margin-bottom: 0; }

a { color: inherit; text-decoration-thickness: 1px; text-underline-offset: 0.18em; }
a:hover { text-decoration-thickness: 2px; }

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

img, svg { max-width: 100%; height: auto; }

hr {
	border: 0;
	border-top: 1px solid var(--rule);
	margin: var(--s7) 0;
}

::selection { background: var(--signal); color: var(--paper); }

/* --------------------------------------------------------------- 4. Layout */

.wrap {
	width: 100%;
	max-width: var(--shell);
	margin-inline: auto;
	padding-inline: var(--gutter);
}

/*
 * Not a reading measure. This is the main column of the rail grid: the same
 * full-width column body copy occupies everywhere else on the site, offset so
 * long-form pages line up with the register and the section heads.
 */
.wrap--narrow {
	width: 100%;
	max-width: var(--shell);
	margin-inline: auto;
	padding-inline: var(--gutter);
}

@media (min-width: 60rem) {
	.wrap--narrow { padding-inline: calc(var(--gutter) + var(--rail)) var(--gutter); }
}

/*
 * Band rhythm.
 *
 * This was 96px top and 96px bottom, which stacks to 192px of nothing at
 * every boundary, and with a sheet reference adding its own 48px the measured
 * gaps ran to 285px. On a dark/light alternating page that does not read as
 * breathing room, it reads as a hole.
 *
 * 72px a side at desktop still separates the bands cleanly and halves the
 * void. The grounds already do the separating; the padding only has to keep
 * content off the seam.
 */
.section { padding-block: clamp(2.25rem, 4vw, 4.5rem); }
.section--tall { padding-block: clamp(3rem, 6vw, var(--s9)); }

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

.ground-ink {
	background: var(--band);
	color: var(--band-ink);
}

.ground-ink a { color: var(--band-ink); }
.ground-ink .record { color: var(--band-soft); }
.ground-ink .record::before { background: var(--signal-band); }

.ground-sunk { background: var(--paper-sunk); }

.skip-link {
	position: absolute;
	left: -9999px;
	top: 0;
	z-index: 100;
	background: var(--signal);
	color: var(--on-signal);
	padding: var(--s3) var(--s5);
	font-family: var(--data);
	font-size: 0.875rem;
}

.skip-link:focus { left: var(--s4); top: var(--s4); }

/*
 * The rail. On desktop a fixed left column carries the mono label — a year, a
 * section number, a country code — and the content runs beside it. Below
 * 60rem the rail label simply sits above its content, which is how a register
 * reads on a narrow page anyway.
 */
.rail {
	display: grid;
	gap: var(--s4) var(--s6);
	align-items: start;
}

@media (min-width: 60rem) {
	.rail {
		grid-template-columns: var(--rail) minmax(0, 1fr);
		gap: var(--s6) var(--s6);
	}
}

/* ------------------------------------------------------ 5. Utility lettering */

/*
 * The eyebrow. A short rule then a mono label, which is how a drawing sheet
 * labels a view. The rule is red by default because a labelled thing on this
 * this site are part of the record.
 */
.record {
	display: inline-flex;
	align-items: center;
	gap: var(--s3);
	font-family: var(--data);
	font-size: 0.75rem;
	font-weight: 500;
	letter-spacing: 0.12em;
	text-transform: uppercase;
	color: var(--ink-soft);
}

.record::before {
	content: "";
	width: 1.75rem;
	height: 2px;
	background: var(--signal);
	flex: none;
}

.record--copper::before,
.record--ink::before { background: var(--ink); }

.record__sep { opacity: 0.45; margin-inline: 0.4em; }

.mono {
	font-family: var(--data);
	font-size: 0.8125rem;
	letter-spacing: 0.04em;
	color: var(--ink-soft);
}

/* --------------------------------------------------------------- 6. Header */

.site-header {
	position: sticky;
	top: 0;
	z-index: 50;
	background: color-mix(in srgb, var(--paper) 88%, transparent);
	backdrop-filter: saturate(140%) blur(10px);
	border-bottom: 1px solid var(--rule);
}

.site-header__inner {
	display: flex;
	align-items: center;
	gap: var(--s5);
	min-height: 4.5rem;
}

.brand {
	display: grid;
	text-decoration: none;
	margin-right: auto;
	line-height: 1.1;
}

.brand__name {
	font-family: var(--display);
	font-weight: 700;
	font-stretch: 118%;
	font-size: 1.0625rem;
	letter-spacing: -0.01em;
}

.brand__role {
	font-family: var(--data);
	font-size: 0.6875rem;
	letter-spacing: 0.14em;
	text-transform: uppercase;
	color: var(--ink-soft);
	margin-top: 0.15rem;
	white-space: nowrap;
}

/*
 * On a phone the CV button and the menu toggle leave about 180px for the
 * brand, and the role line needs 200. Rather than let it wrap and make the
 * header two rows tall on every page, it goes. The site says what he does
 * about four centimetres further down.
 */
@media (max-width: 30rem) {
	.brand__role { display: none; }
}

.nav__list {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: var(--s5);
	list-style: none;
	margin: 0;
	padding: 0;
}

.nav__list a,
.site-nav a {
	font-size: 0.9375rem;
	text-decoration: none;
	color: var(--ink-soft);
	padding-block: 0.4rem;
}

.nav__list a:hover,
.site-nav a:hover { color: var(--ink); }

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

/* The one persistent action: the document a recruiter came for. */
.nav-cta {
	display: inline-flex;
	align-items: center;
	gap: var(--s2);
	font-family: var(--data);
	font-size: 0.75rem;
	font-weight: 500;
	letter-spacing: 0.1em;
	text-transform: uppercase;
	text-decoration: none;
	color: var(--on-signal);
	background: var(--signal);
	padding: 0.65rem var(--s4);
	border: 1px solid var(--signal);
	white-space: nowrap;
}

.nav-cta:hover { background: transparent; color: var(--signal); }
.nav-cta__short { display: none; }

.nav-toggle {
	display: none;
	font-family: var(--data);
	font-size: 0.75rem;
	letter-spacing: 0.12em;
	text-transform: uppercase;
	background: transparent;
	color: var(--ink);
	border: 1px solid var(--rule);
	padding: 0.6rem var(--s4);
	cursor: pointer;
}

@media (max-width: 63rem) {
	.nav-toggle { display: block; order: 3; }
	.nav-cta__long { display: none; }
	.nav-cta__short { display: inline; }

	.site-nav {
		order: 4;
		flex-basis: 100%;
		display: none;
		border-top: 1px solid var(--rule);
		padding-block: var(--s4);
	}

	.site-nav[data-open="true"] { display: block; }

	.nav__list { flex-direction: column; align-items: stretch; gap: 0; }

	.nav__list a {
		display: block;
		padding-block: var(--s3);
		border-bottom: 1px solid var(--rule-faint);
		font-size: 1.0625rem;
	}
}

/* ------------------------------------------- 7. The title block (hero) */

/*
 * A drawing sheet, not a landing page.
 *
 * Every procedure and method statement this man has issued opens with a title
 * block: what the document is, who holds it, which revision, when it was
 * issued. That is the most characteristic artefact of the job, so the site
 * opens with one. The name is the document title; the control fields sit
 * under it in a ruled strip; the status stamp sits where a stamp goes.
 */

.profile {
	position: relative;
	background: var(--paper);
	padding-block: clamp(2.5rem, 6vw, 5rem) clamp(2.5rem, 5vw, 4rem);
	border-bottom: 2px solid var(--ink);
	overflow: hidden;
}

/*
 * Engineering paper. A 28px grid at about four per cent opacity: present
 * enough that the eye reads "drawn on something", faint enough that no
 * individual line is ever visible. It is the only texture on the site.
 */
.profile__paper {
	position: absolute;
	inset: 0;
	pointer-events: none;
	background-image:
		repeating-linear-gradient(to right, var(--rule) 0 1px, transparent 1px 28px),
		repeating-linear-gradient(to bottom, var(--rule) 0 1px, transparent 1px 28px);
	opacity: 0.38;
	mask-image: linear-gradient(to bottom, #000 0%, #000 55%, transparent 100%);
	-webkit-mask-image: linear-gradient(to bottom, #000 0%, #000 55%, transparent 100%);
}

.profile .wrap { position: relative; }

.profile__grid {
	display: grid;
	gap: var(--s6);
	align-items: start;
}

@media (min-width: 60rem) {
	.profile__grid {
		grid-template-columns: minmax(0, 1fr) auto;
		gap: var(--s8);
	}
}

.profile__kicker {
	font-family: var(--data);
	font-size: 0.6875rem;
	font-weight: 500;
	letter-spacing: 0.22em;
	text-transform: uppercase;
	color: var(--ink-soft);
	margin: 0 0 var(--s4);
	padding-bottom: var(--s3);
	border-bottom: 1px solid var(--rule);
}

.profile__name {
	font-size: clamp(3rem, 1.2rem + 8.2vw, 8rem);
	font-weight: 700;
	font-stretch: 122%;
	letter-spacing: -0.042em;
	line-height: 0.88;
	margin: 0;
}

.profile__name span { display: block; }

.profile__discipline {
	font-family: var(--data);
	font-size: clamp(0.8125rem, 0.72rem + 0.4vw, 1rem);
	font-weight: 500;
	letter-spacing: 0.2em;
	text-transform: uppercase;
	color: var(--signal);
	margin: var(--s4) 0 var(--s5);
}

.profile__lede {
	font-size: clamp(1.0625rem, 0.98rem + 0.5vw, 1.3125rem);
	line-height: 1.5;
	color: var(--ink-soft);
	margin: 0 0 var(--s6);
}

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

.profile__actions {
	display: flex;
	flex-wrap: wrap;
	gap: var(--s3);
}

/* ---- The status stamp ---------------------------------------------------
 *
 * The one piece of pure design on the site. Drawn, not photographed: two
 * hairline rules, a mono face, two and a half degrees off square. No ink
 * texture and no shadow, because a stamp pretending to be a scan of a stamp
 * is a costume. This one is printed.
 */
.stamp {
	margin: 0;
	transform: rotate(-2.5deg);
	transform-origin: center;
}

.stamp__inner {
	display: grid;
	justify-items: center;
	gap: 0.15rem;
	padding: var(--s4) var(--s6);
	border: 2px solid var(--signal);
	box-shadow: 0 0 0 2px var(--paper), 0 0 0 4px var(--signal);
	color: var(--signal);
	text-align: center;
}

.stamp__lead,
.stamp__foot {
	font-family: var(--data);
	font-size: 0.6875rem;
	font-weight: 500;
	letter-spacing: 0.24em;
	text-transform: uppercase;
}

.stamp__foot { opacity: 0.75; letter-spacing: 0.14em; }

.stamp__date {
	font-family: var(--display);
	font-weight: 700;
	font-stretch: 112%;
	font-size: clamp(1.125rem, 1rem + 0.7vw, 1.5rem);
	letter-spacing: -0.01em;
	line-height: 1.15;
	white-space: nowrap;
}

@media (prefers-reduced-motion: reduce) {
	.stamp { transform: none; }
}

/* ---- The document control strip ---------------------------------------- */

.doc {
	display: grid;
	grid-template-columns: repeat(2, minmax(0, 1fr));
	gap: 1px;
	margin: var(--s7) 0 0;
	background: var(--rule);
	border: 1px solid var(--rule);
}

@media (min-width: 46rem) { .doc { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@media (min-width: 72rem) { .doc--block { grid-template-columns: repeat(6, minmax(0, 1fr)); } }

.doc__field {
	background: var(--paper);
	padding: var(--s3) var(--s4);
	display: grid;
	gap: 0.15rem;
}

.doc__key {
	font-family: var(--data);
	font-size: 0.625rem;
	font-weight: 500;
	letter-spacing: 0.18em;
	text-transform: uppercase;
	color: var(--ink-faint);
}

.doc__value {
	font-family: var(--data);
	font-size: 0.8125rem;
	font-weight: 500;
	color: var(--ink);
	margin: 0;
}

/* ---- Sheet reference ---------------------------------------------------- */

.sheet {
	display: flex;
	flex-wrap: wrap;
	align-items: baseline;
	justify-content: space-between;
	gap: var(--s3);
	padding-bottom: var(--s3);
	margin-bottom: var(--s5);
	border-bottom: 1px solid var(--rule);
	font-family: var(--data);
	font-size: 0.6875rem;
	font-weight: 500;
	letter-spacing: 0.18em;
	text-transform: uppercase;
}

.sheet__title { color: var(--ink); }
.sheet__no { color: var(--ink-faint); }

.ground-ink .sheet { border-bottom-color: var(--band-rule); }
.ground-ink .sheet__title { color: var(--band-ink); }
.ground-ink .sheet__no { color: var(--band-soft); }

/* ------------------------------------------------- 8. The route strip */

/*
 * The signature element. Twelve postings, in the order they happened, on one
 * measured line. It is the whole argument of the career in a single graphic,
 * and every value in it is a date and a place — nothing modelled, nothing
 * estimated. Without CSS it is an ordered list of places and years, which is
 * still the same argument.
 */
.route { padding-block: var(--s7) var(--s8); }

.route__head {
	display: flex;
	flex-wrap: wrap;
	align-items: baseline;
	justify-content: space-between;
	gap: var(--s4);
	margin-bottom: var(--s6);
}

.route__note {
	font-family: var(--data);
	font-size: 0.75rem;
	letter-spacing: 0.06em;
	color: var(--ink-soft);
}

/*
 * Fixed column counts rather than auto-fit. auto-fit packs as many cells as
 * will fit and strands the remainder on a short final row, which reads as a
 * mistake rather than as a route.
 *
 * Every cell also carries its own top and right hairline, so when Jeremy
 * remembers another posting and the rows stop dividing evenly, the strip
 * still reads as a ruled register rather than as a broken grid. It has gone
 * from twelve entries to fifteen in the course of one afternoon.
 */
.route__list {
	list-style: none;
	margin: 0;
	padding: 0;
	display: grid;
	grid-template-columns: repeat(3, 1fr);
	border-top: 2px solid var(--ink);
}

/* Fifteen postings divide exactly by three and by five. */
@media (min-width: 46rem) { .route__list { grid-template-columns: repeat(5, 1fr); } }

.route__item {
	padding: var(--s4) var(--s3) var(--s5) 0;
	border-right: 1px solid var(--rule-faint);
	border-top: 1px solid var(--rule-faint);
	position: relative;
}

.route__item::before {
	content: "";
	position: absolute;
	top: -2px;
	left: 0;
	width: 7px;
	height: 7px;
	background: var(--ink);
	transform: translateY(-50%) rotate(45deg);
}

.route__item:last-child::before { background: var(--signal); width: 11px; height: 11px; }

.route__year {
	display: block;
	font-family: var(--data);
	font-size: 0.75rem;
	font-weight: 500;
	letter-spacing: 0.08em;
	color: var(--ink-soft);
	margin-bottom: 0.3rem;
}

.route__place {
	display: block;
	font-family: var(--display);
	font-weight: 600;
	font-stretch: 108%;
	font-size: 1rem;
	line-height: 1.2;
}

.route__item:last-child .route__place { color: var(--signal); }

/* ------------------------------------------------------- 9. Delivered */

.delivered {
	display: grid;
	gap: 1px;
	background: var(--rule);
	border: 1px solid var(--rule);
}

@media (min-width: 46rem) { .delivered { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 72rem) { .delivered { grid-template-columns: repeat(4, 1fr); } }

.delivered__cell {
	background: var(--paper);
	color: var(--ink);
	padding: var(--s5);
	display: grid;
	align-content: start;
	gap: var(--s2);
}

.delivered__figure {
	font-family: var(--display);
	font-weight: 700;
	font-stretch: 118%;
	font-size: clamp(2.25rem, 1.6rem + 2.4vw, 3.25rem);
	line-height: 1;
	letter-spacing: -0.03em;
	color: var(--signal);
}

.delivered__label {
	font-family: var(--data);
	font-size: 0.75rem;
	font-weight: 500;
	letter-spacing: 0.1em;
	text-transform: uppercase;
	color: var(--ink-soft);
}

.delivered__claim { font-size: 0.9375rem; line-height: 1.5; margin: var(--s2) 0 0; }

.delivered__where {
	font-family: var(--data);
	font-size: 0.6875rem;
	letter-spacing: 0.04em;
	color: var(--ink-faint);
	margin-top: auto;
	padding-top: var(--s3);
}

/* ------------------------------------------------ 10. The career register */

/*
 * Duration is spatial. Each role's bar is sized by the months it ran, so an
 * eight-month contract is visibly shorter than a six-year tenure and nobody
 * has to subtract two dates to see the shape of the career.
 *
 * The bar is presentational and hidden from assistive technology; the dates
 * are in the text, where they belong.
 */
.register {
	list-style: none;
	margin: 0;
	padding: 0;
	border-top: 2px solid var(--ink);
}

.register__role {
	border-bottom: 1px solid var(--rule);
	padding-block: var(--s6);
}

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

@media (min-width: 60rem) {
	.register__grid {
		grid-template-columns: var(--rail) minmax(0, 1fr);
		gap: var(--s6);
	}
}

.register__when {
	font-family: var(--data);
	font-size: 0.8125rem;
	font-weight: 500;
	letter-spacing: 0.03em;
	color: var(--ink-soft);
	line-height: 1.5;
}

.register__duration {
	display: block;
	color: var(--ink-faint);
	font-weight: 400;
	margin-top: 0.2rem;
}

.register__title {
	font-size: clamp(1.375rem, 1.1rem + 1.1vw, 1.875rem);
	margin-bottom: var(--s2);
}

.register__employer {
	font-family: var(--data);
	font-size: 0.8125rem;
	font-weight: 500;
	letter-spacing: 0.06em;
	text-transform: uppercase;
	color: var(--ink-soft);
	margin-bottom: var(--s4);
	display: flex;
	flex-wrap: wrap;
	gap: 0.4rem 0.75rem;
}

.register__employer b { color: var(--ink); font-weight: 500; }

.register__summary { margin-bottom: var(--s4); }

.register__points {
	list-style: none;
	margin: 0;
	padding: 0;
	display: grid;
	gap: var(--s2);
}

.register__points li {
	position: relative;
	padding-left: 1.35rem;
	font-size: 0.9688rem;
	line-height: 1.55;
	color: var(--ink-soft);
}

.register__points li::before {
	content: "";
	position: absolute;
	left: 0;
	top: 0.62em;
	width: 0.5rem;
	height: 1px;
	background: var(--signal);
}

.register__role--current .register__title { color: var(--signal); }

.register__flag {
	display: inline-block;
	font-family: var(--data);
	font-size: 0.6875rem;
	font-weight: 500;
	letter-spacing: 0.1em;
	text-transform: uppercase;
	color: var(--signal);
	background: var(--signal-wash);
	padding: 0.2rem 0.5rem;
	margin-left: 0.6rem;
	vertical-align: 0.3em;
}

/* Earlier posts, condensed as the CV condenses them. */
.early {
	list-style: none;
	margin: 0;
	padding: 0;
	border-top: 1px solid var(--rule);
}

.early__item {
	display: grid;
	gap: 0.2rem var(--s6);
	padding-block: var(--s4);
	border-bottom: 1px solid var(--rule-faint);
}

@media (min-width: 46rem) {
	.early__item {
		grid-template-columns: var(--col-year) minmax(0, 1.4fr) minmax(0, 1fr);
		align-items: baseline;
	}
}

.early__year {
	font-family: var(--data);
	font-size: 0.8125rem;
	font-weight: 500;
	color: var(--ink-soft);
}

.early__title { font-weight: 600; }
.early__title b { font-weight: 600; }

.early__where {
	font-family: var(--data);
	font-size: 0.8125rem;
	color: var(--ink-soft);
}

/* ----------------------------------------------------- 11. Qualifications */

/*
 * Black, with a red rule on the list head. These are the things the job
 * the things the job requires you to hold rather than the things you did.
 */
.quals {
	list-style: none;
	margin: 0;
	padding: 0;
	border-top: 2px solid var(--ink);
}

.qual {
	display: grid;
	gap: 0.2rem var(--s6);
	padding-block: var(--s4);
	border-bottom: 1px solid var(--rule);
	align-items: baseline;
}

@media (min-width: 46rem) {
	.qual { grid-template-columns: var(--col-year) minmax(0, 1.5fr) minmax(0, 1fr); }
}

.qual__year {
	font-family: var(--data);
	font-size: 0.875rem;
	font-weight: 500;
	color: var(--ink);
}

.qual__name {
	font-family: var(--display);
	font-weight: 600;
	font-stretch: 108%;
	font-size: 1.0625rem;
	line-height: 1.3;
}

.qual__issuer {
	font-family: var(--data);
	font-size: 0.8125rem;
	color: var(--ink-soft);
}

.qual__issuer a { color: var(--ink-soft); }

.qual__issuer--missing { color: var(--ink-faint); font-style: normal; }

/* Memberships */
.memberships {
	list-style: none;
	margin: 0;
	padding: 0;
	display: grid;
	gap: 1px;
	background: var(--rule);
	border: 1px solid var(--rule);
}

@media (min-width: 46rem) { .memberships { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 72rem) { .memberships { grid-template-columns: repeat(4, 1fr); } }

.membership {
	background: var(--paper);
	padding: var(--s5);
	display: grid;
	gap: var(--s2);
	align-content: start;
}

.membership__short {
	font-family: var(--display);
	font-weight: 700;
	font-stretch: 115%;
	font-size: 1.5rem;
	letter-spacing: -0.02em;
	color: var(--ink);
}

.membership__body { font-size: 0.9375rem; line-height: 1.45; }

.membership__grade {
	font-family: var(--data);
	font-size: 0.6875rem;
	font-weight: 500;
	letter-spacing: 0.1em;
	text-transform: uppercase;
	color: var(--ink-soft);
}

.membership__number {
	font-family: var(--data);
	font-size: 0.6875rem;
	letter-spacing: 0.06em;
	color: var(--ink-faint);
}

/*
 * Every membership carries a route to check it. The verify link is red
 * because it is the live, checkable part of the claim, which is exactly the
 * requires rather than the things he did.
 */
.membership__verify {
	justify-self: start;
	margin-top: var(--s2);
	font-family: var(--data);
	font-size: 0.6875rem;
	font-weight: 500;
	letter-spacing: 0.12em;
	text-transform: uppercase;
	color: var(--ink);
	text-decoration: none;
	border-bottom: 1px solid currentColor;
	padding-bottom: 1px;
}

.membership__verify:hover { border-bottom-width: 2px; }

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

/* Legacy credential markup, kept so the credentials page template still works. */
.credentials { border-top: 2px solid var(--ink); }

.credential {
	display: grid;
	gap: 0.2rem var(--s6);
	padding-block: var(--s4);
	border-bottom: 1px solid var(--rule);
	align-items: baseline;
}

@media (min-width: 46rem) {
	.credential { grid-template-columns: var(--col-year) minmax(0, 1.5fr) minmax(0, 1fr); }
}

.credential__year {
	font-family: var(--data);
	font-size: 0.875rem;
	font-weight: 500;
	color: var(--ink);
}

.credential__name {
	font-family: var(--display);
	font-weight: 600;
	font-stretch: 108%;
	font-size: 1.0625rem;
}

.credential__issuer { font-family: var(--data); font-size: 0.8125rem; color: var(--ink-soft); }

/* ----------------------------------------------------------- 11b. Skills */

.skills {
	margin: 0;
	padding: 0;
	border-top: 1px solid var(--rule);
}

.skills__group {
	display: grid;
	gap: var(--s2) var(--s6);
	padding-block: var(--s4);
	border-bottom: 1px solid var(--rule-faint);
	align-items: baseline;
}

@media (min-width: 52rem) {
	.skills__group { grid-template-columns: var(--rail) minmax(0, 1fr); }
}

.skills__term {
	font-family: var(--data);
	font-size: 0.75rem;
	font-weight: 500;
	letter-spacing: 0.1em;
	text-transform: uppercase;
	color: var(--ink-soft);
	margin: 0;
}

.skills__items {
	margin: 0;
	display: flex;
	flex-wrap: wrap;
	gap: var(--s2) var(--s4);
}

.skills__item {
	font-size: 0.9688rem;
	position: relative;
	padding-left: 0.9rem;
}

.skills__item::before {
	content: "";
	position: absolute;
	left: 0;
	top: 0.62em;
	width: 0.4rem;
	height: 1px;
	background: var(--signal);
}

/* --------------------------------------------------------- 12. Disclaimer */

/*
 * Red is used exactly once on this site, here, on the thing that is not
 * claimed. Spending the prohibition colour on a disclaimer rather than on a
 * call to action is the point.
 */
.not-claimed {
	border: 1px solid var(--signal);
	border-left-width: 4px;
	padding: var(--s5) var(--s6);
	background: var(--paper);
}

.not-claimed__label {
	display: block;
	font-family: var(--data);
	font-size: 0.6875rem;
	font-weight: 500;
	letter-spacing: 0.14em;
	text-transform: uppercase;
	color: var(--signal);
	margin-bottom: var(--s3);
}

.not-claimed p { font-size: 0.9688rem; line-height: 1.55; }

/* ------------------------------------------------------------ 13. Projects */

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

/*
 * auto-fit rather than a fixed two columns. There is one project now that the
 * self-referential "this website" card is gone, and a fixed two-column grid
 * leaves half the band empty when the count drops to one. auto-fit stretches
 * the single card instead, and still gives two across when a second is added.
 */
@media (min-width: 52rem) {
	.projects { grid-template-columns: repeat(auto-fit, minmax(min(100%, 24rem), 1fr)); }
}

.project {
	border: 1px solid var(--rule);
	padding: var(--s6);
	display: grid;
	align-content: start;
	gap: var(--s3);
	background: var(--paper);
}

.project__kind {
	font-family: var(--data);
	font-size: 0.6875rem;
	font-weight: 500;
	letter-spacing: 0.12em;
	text-transform: uppercase;
	color: var(--ink-soft);
}

.project__name {
	font-size: 1.5rem;
	line-height: 1.15;
}

.project__name a { text-decoration: none; }
.project__name a:hover { text-decoration: underline; text-decoration-color: var(--signal); }

.project__role {
	font-family: var(--data);
	font-size: 0.8125rem;
	color: var(--ink-soft);
}

.project__body { font-size: 0.9688rem; line-height: 1.55; }

/* ------------------------------------------------------------ 14. Sections */

.section__head {
	display: grid;
	gap: var(--s4);
	margin-bottom: var(--s6);
}

@media (min-width: 60rem) {
	.section__head {
		grid-template-columns: minmax(0, 1.5fr) minmax(0, 1fr);
		gap: var(--s7);
		align-items: end;
	}
}

.ground-ink .section__head p,
.ground-ink .section__head { color: var(--band-soft); }

.section__title {
	font-size: clamp(1.875rem, 1.3rem + 2.2vw, 3rem);
	margin-top: var(--s3);
}

.section__head p { color: var(--ink-soft); margin: 0; }

/* -------------------------------------------------------------- 15. Buttons */

.button {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	gap: var(--s2);
	font-family: var(--data);
	font-size: 0.8125rem;
	font-weight: 500;
	letter-spacing: 0.1em;
	text-transform: uppercase;
	text-decoration: none;
	padding: 0.85rem var(--s5);
	border: 1px solid var(--ink);
	background: var(--ink);
	color: var(--paper);
	cursor: pointer;
	transition: background 0.15s ease, color 0.15s ease;
}

.button:hover { background: transparent; color: var(--ink); }

.button--primary {
	background: var(--signal);
	border-color: var(--signal);
	color: var(--on-signal);
}

.button--primary:hover { background: transparent; color: var(--signal); }

.button--ghost {
	background: transparent;
	color: var(--ink);
	border-color: var(--rule);
}

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

.ground-ink .button--ghost { color: var(--band-ink); border-color: var(--band-rule); }
.ground-ink .button--ghost:hover { background: var(--band-ink); color: var(--band); }

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

/* ------------------------------------------------------------- 16. Entries */

.entry__header { padding-block: clamp(2.5rem, 6vw, var(--s9)) var(--s7); }

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

.entry-title {
	font-size: clamp(2rem, 1.3rem + 3vw, 3.75rem);
	letter-spacing: -0.03em;
	margin-block: var(--s3);
}

.entry-standfirst {
	font-size: clamp(1.125rem, 1rem + 0.7vw, 1.5rem);
	line-height: 1.42;
	color: var(--ink-soft);
	margin: 0;
}

.ground-ink .entry-standfirst { color: var(--band-soft); }

.entry__meta {
	display: flex;
	flex-wrap: wrap;
	gap: var(--s4);
	font-family: var(--data);
	font-size: 0.75rem;
	letter-spacing: 0.06em;
	text-transform: uppercase;
	color: var(--ink-soft);
	border-top: 1px solid var(--rule);
	padding-top: var(--s4);
	margin-top: var(--s5);
}

.ground-ink .entry__meta { border-top-color: var(--band-rule); color: var(--band-soft); }

.entry__header-aside { font-family: var(--data); font-size: 0.8125rem; color: var(--ink-soft); }

/* ---- Long-form body ------------------------------------------------------
 *
 * No width cap: the grid column is the measure.
 *
 * Long-form pages run inside .wrap--narrow, which offsets the text by one
 * rail. That rail was dead space on every article and every page on the site,
 * so the section headings now hang their number in it. A reader scrolling the
 * About page or a four-thousand word essay can see where they are in the
 * structure without a sticky nav, and the numbering is real: it counts the
 * sections that exist rather than decorating them.
 */

.entry__content {
	counter-reset: section;
	position: relative;
}

.entry__content > * { margin-block: 0 var(--s5); }

.entry__content h2 {
	counter-increment: section;
	position: relative;
	font-size: clamp(1.4375rem, 1.15rem + 1.4vw, 2.125rem);
	line-height: 1.14;
	letter-spacing: -0.02em;
	margin-top: var(--s8);
	padding-top: var(--s4);
	border-top: 2px solid var(--ink);
}

.entry__content h2::before {
	content: counter(section, decimal-leading-zero);
	font-family: var(--data);
	font-size: 0.75rem;
	font-weight: 500;
	letter-spacing: 0.14em;
	color: var(--signal);
	display: block;
	margin-bottom: var(--s3);
}

/* On a wide screen the number moves out into the rail beside the heading. */
@media (min-width: 60rem) {
	.entry__content h2::before {
		position: absolute;
		top: var(--s4);
		left: calc((var(--rail) + var(--s6)) * -1);
		width: var(--rail);
		margin: 0;
		text-align: right;
		padding-right: var(--s4);
	}
}

.entry__content h3 {
	font-size: clamp(1.125rem, 1.03rem + 0.6vw, 1.375rem);
	margin-top: var(--s6);
	color: var(--ink);
}

.entry__content ul,
.entry__content ol { padding-left: 1.3rem; }

.entry__content li { margin-bottom: var(--s2); }

.entry__content blockquote {
	margin: var(--s7) 0;
	padding: 0 0 0 var(--s5);
	border-left: 3px solid var(--signal);
	font-family: var(--display);
	font-weight: 500;
	font-stretch: 108%;
	font-size: clamp(1.25rem, 1.1rem + 0.8vw, 1.625rem);
	line-height: 1.3;
	letter-spacing: -0.015em;
}

.entry__content blockquote p:last-child { margin-bottom: 0; }

.entry__content code {
	font-family: var(--data);
	font-size: 0.875em;
	background: var(--paper-sunk);
	padding: 0.1em 0.35em;
}

.entry__content a {
	text-decoration-color: var(--signal);
	text-decoration-thickness: 2px;
}

.entry__content a:hover { color: var(--signal); }

.entry__content strong { font-weight: 600; }

/*
 * The opening paragraph, set larger. A long page needs somewhere obvious to
 * start, and on the About page the first sentence is the whole argument.
 */
.entry__content > p:first-of-type {
	font-size: clamp(1.1875rem, 1.05rem + 0.8vw, 1.5rem);
	line-height: 1.42;
	color: var(--ink);
	margin-bottom: var(--s6);
}

/* -------------------------------------------------------- 17. Writing rows */

.posts, .cases, .keynotes {
	display: grid;
	border-top: 2px solid var(--ink);
}

.ground-ink .posts,
.ground-ink .cases,
.ground-ink .keynotes { border-top-color: var(--band-rule); }

.post-row {
	display: grid;
	gap: 0.3rem var(--s6);
	padding-block: var(--s5);
	border-bottom: 1px solid var(--rule);
	text-decoration: none;
	align-items: baseline;
}

.ground-ink .post-row { border-bottom-color: var(--band-rule); }

@media (min-width: 52rem) {
	.post-row { grid-template-columns: var(--col-year) minmax(0, 1fr) 5rem; }
}

.post-row__date,
.post-row__time {
	font-family: var(--data);
	font-size: 0.75rem;
	letter-spacing: 0.06em;
	text-transform: uppercase;
	color: var(--ink-soft);
}

.ground-ink .post-row__date,
.ground-ink .post-row__time { color: var(--band-soft); }

.post-row__title {
	font-size: clamp(1.1875rem, 1.05rem + 0.8vw, 1.625rem);
	font-weight: 600;
	margin: 0;
}

.post-row:hover .post-row__title { color: var(--signal); }

/* ---------------------------------------------------------- 18. Breadcrumbs */

.breadcrumbs { padding-top: var(--s5); }

.breadcrumbs__list {
	display: flex;
	flex-wrap: wrap;
	gap: 0.4rem var(--s3);
	list-style: none;
	margin: 0;
	padding: 0;
	font-family: var(--data);
	font-size: 0.75rem;
	letter-spacing: 0.05em;
	color: var(--ink-soft);
}

.ground-ink .breadcrumbs__list { color: var(--band-soft); }

.breadcrumbs__item + .breadcrumbs__item::before {
	content: "/";
	margin-right: var(--s3);
	opacity: 0.5;
}

/* ------------------------------------------------------------- 19. Enquiry */

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

@media (min-width: 64rem) {
	.enquiry { grid-template-columns: minmax(0, 1.6fr) minmax(0, 1fr); gap: var(--s9); }
}

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

@media (min-width: 40rem) {
	.enquiry__grid { grid-template-columns: repeat(2, 1fr); }
	.field--full { grid-column: 1 / -1; }
}

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

.field label {
	font-family: var(--data);
	font-size: 0.75rem;
	font-weight: 500;
	letter-spacing: 0.1em;
	text-transform: uppercase;
	color: var(--ink-soft);
}

.ground-ink .field label { color: var(--band-soft); }

.field input,
.field textarea,
.field select {
	font-family: var(--body);
	font-size: 1rem;
	color: inherit;
	background: transparent;
	border: 1px solid var(--rule);
	padding: 0.8rem var(--s4);
	width: 100%;
	max-width: 34rem;
	border-radius: 0;
}

.field--full input,
.field--full textarea { max-width: none; }

.ground-ink .field input,
.ground-ink .field textarea,
.ground-ink .field select { border-color: var(--band-rule); }

.field input:focus,
.field textarea:focus { outline: var(--focus); outline-offset: 1px; border-color: var(--signal); }

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

.field__required { color: var(--signal); }

.field__hint { font-size: 0.8125rem; color: var(--ink-soft); }
.ground-ink .field__hint { color: var(--band-soft); }

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

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

.enquiry__privacy { font-size: 0.8125rem; color: var(--ink-soft); }
.ground-ink .enquiry__privacy { color: var(--band-soft); }

.enquiry__direct {
	border: 1px solid var(--rule);
	padding: var(--s5);
	align-self: start;
	display: grid;
	gap: var(--s3);
}

.ground-ink .enquiry__direct { border-color: var(--band-rule); }

.notice {
	padding: var(--s4) var(--s5);
	border-left: 4px solid var(--signal);
	background: var(--signal-wash);
	margin-bottom: var(--s5);
}

.notice--error { border-left-color: var(--signal); background: color-mix(in srgb, var(--signal) 10%, transparent); }

/* -------------------------------------------------------------- 20. Footer */

/*
 * The sign-off.
 *
 * A controlled document does not trail off into a list of links; it closes
 * with the same block it opened with, plus the thing the reader came for. So
 * the footer mirrors the hero: the statement, the two ways to reach him, the
 * availability stamp, and the control fields on one unbroken row.
 *
 * The control strip is four columns because it carries four fields. The three
 * column version left a dead cell the width of a third of the page, which is
 * the sort of thing that reads as unfinished however good the type is.
 */

.site-footer {
	background: var(--band);
	color: var(--band-ink);
	padding-block: clamp(3.5rem, 7vw, var(--s9)) var(--s6);
	margin-top: clamp(4rem, 8vw, var(--s10));
	border-top: 3px solid var(--signal);
}

.site-footer a { color: var(--band-ink); }

.site-footer__lead {
	display: grid;
	gap: var(--s7);
	padding-bottom: var(--s7);
	align-items: start;
}

@media (min-width: 64rem) {
	.site-footer__lead {
		grid-template-columns: minmax(0, 1fr) auto;
		gap: var(--s9);
	}
}

/*
 * With no availability stamp, the signature stands in the column the stamp
 * would have filled rather than sitting alone on a full width row below it.
 * The first attempt at this only closed the vertical gap, which left the
 * right of the footer as a tall empty rectangle, because the space that was
 * wrong was horizontal.
 */
.site-footer__lead--signed { padding-bottom: var(--s5); }

.sign--aside {
	margin-top: 0;
	padding-top: 0;
	border-top: 0;
}

@media (min-width: 64rem) {
	/*
	 * Sit the signature on the baseline of the row rather than the top of it.
	 * The lead aligns to start, which is right for the stamp, but it floated
	 * the signature up beside the heading with the contact boxes well below
	 * it. A signature belongs level with the details it signs off.
	 */
	.site-footer__lead--signed { align-items: end; }

	.sign--aside {
		justify-items: end;
		text-align: right;
	}

	.sign--aside .sign__rule { margin-left: auto; }
}

.site-footer__thesis {
	font-family: var(--display);
	font-weight: 600;
	font-stretch: 112%;
	font-size: clamp(1.625rem, 1.1rem + 2.3vw, 3rem);
	line-height: 1.08;
	letter-spacing: -0.028em;
	margin: 0 0 var(--s6);
	max-width: none;
}

/* Two actions, sized like the buttons they are rather than like table rows. */
.site-footer__actions {
	display: flex;
	flex-wrap: wrap;
	gap: var(--s4);
}

.site-footer__action {
	display: grid;
	gap: 0.25rem;
	padding: var(--s4) var(--s6) var(--s4) var(--s5);
	border: 1px solid var(--band-rule);
	border-left: 3px solid var(--signal);
	text-decoration: none;
	transition: background 0.18s ease, border-color 0.18s ease;
}

.site-footer__action:hover,
.site-footer__action:focus-visible {
	background: var(--signal);
	border-color: var(--signal);
}

.site-footer__action:hover .site-footer__action-key,
.site-footer__action:focus-visible .site-footer__action-key,
.site-footer__action:hover .site-footer__action-value,
.site-footer__action:focus-visible .site-footer__action-value { color: var(--on-signal); }

.site-footer__action-key {
	font-family: var(--data);
	font-size: 0.625rem;
	font-weight: 500;
	letter-spacing: 0.2em;
	text-transform: uppercase;
	color: var(--band-soft);
}

.site-footer__action-value {
	font-family: var(--data);
	font-size: 0.9375rem;
	font-weight: 500;
	color: var(--band-ink);
}

/* The stamp sits on black here, so it carries the red rather than the ink. */
.site-footer .stamp__inner {
	border-color: var(--signal-band);
	box-shadow: 0 0 0 2px var(--band), 0 0 0 4px var(--signal-band);
	color: var(--signal-band);
}

/* The control strip, recoloured for the black ground and sized to its fields. */
.site-footer .doc {
	background: var(--band-rule);
	border-color: var(--band-rule);
	/* The newsletter line sits directly above this and had nothing between
	   them, so the two read as one crowded block rather than two fields. */
	margin: var(--s5) 0 0;
}

.site-footer .doc__field { background: var(--band); }
.site-footer .doc__key { color: var(--band-soft); }
.site-footer .doc__value { color: var(--band-ink); }

@media (min-width: 60rem) {
	.doc--strip { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}

.site-footer__base {
	display: grid;
	gap: var(--s4);
	padding-top: var(--s6);
}

@media (min-width: 60rem) {
	.site-footer__base {
		grid-template-columns: minmax(0, 1fr) auto;
		align-items: center;
		gap: var(--s6);
	}
}

.footer-menu,
.site-footer__profiles {
	display: flex;
	flex-wrap: wrap;
	gap: var(--s3) var(--s5);
	list-style: none;
	margin: 0;
	padding: 0;
	font-size: 0.9375rem;
}

.footer-menu a,
.site-footer__profiles a {
	color: var(--band-soft);
	text-decoration: none;
	padding-block: 0.25rem;
	border-bottom: 2px solid transparent;
	transition: color 0.15s ease, border-color 0.15s ease;
}

.footer-menu a:hover,
.site-footer__profiles a:hover,
.footer-menu a:focus-visible,
.site-footer__profiles a:focus-visible {
	color: var(--band-ink);
	border-bottom-color: var(--signal);
}

.site-footer__legal {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: var(--s2) var(--s4);
	font-family: var(--data);
	font-size: 0.6875rem;
	letter-spacing: 0.06em;
	color: var(--band-soft);
	margin: var(--s6) 0 0;
	padding-top: var(--s4);
	border-top: 1px solid var(--band-rule);
}

/* The one red mark in the legal line, because it is the one claim in it. */
.site-footer__controlled {
	color: var(--signal-band);
	border: 1px solid var(--signal-band);
	padding: 0.15rem 0.5rem;
	text-transform: uppercase;
	letter-spacing: 0.16em;
	font-weight: 500;
}

@media (prefers-reduced-motion: reduce) {
	.site-footer__action,
	.footer-menu a,
	.site-footer__profiles a { transition: none; }
}

/* --------------------------------------------------------------- 21. Misc */

.notice.widefat { background: transparent; }

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

.searchform { display: flex; gap: var(--s3); flex-wrap: wrap; }

/* Reveal-on-scroll is opt-in and must not hide content when motion is off
   or when JavaScript never runs. */
/* ====================================================== 28. Motion ========
 *
 * Three rules govern everything in this block.
 *
 *  1. Nothing hides content. Every animation runs `both` from a visible end
 *     state or is wrapped so that if it never fires, the element is simply
 *     there. A missed animation must never cost a reader a paragraph.
 *
 *  2. It plays once and settles. The load sequence runs for under a second
 *     and then the page is static. Nothing loops, nothing parallaxes, nothing
 *     follows the cursor.
 *
 *  3. prefers-reduced-motion removes all of it, not some of it.
 *
 * Scroll work uses native scroll-driven animations where the browser has
 * them, which means no JavaScript, no IntersectionObserver and no work on the
 * main thread during scroll. Browsers without support get the finished state
 * immediately, which is the correct fallback rather than a degraded one.
 */

[data-reveal] { opacity: 1; }

@media (prefers-reduced-motion: no-preference) {
	.js [data-reveal] { opacity: 0; transform: translateY(12px); transition: opacity 0.5s ease, transform 0.5s ease; }
	.js [data-reveal].is-in { opacity: 1; transform: none; }
}

/* ---- The load sequence --------------------------------------------------
 *
 * The title block is issued: the rule draws, the document class appears, the
 * name is set, the stamp lands.
 *
 * The name is the one piece of real kinetic typography on the site, and it
 * earns the name. Archivo carries a width axis, so the letters genuinely
 * widen from 62% to 122% rather than being scaled by a transform. Type being
 * set is exactly what is happening on a drawing sheet, so the motion is the
 * subject rather than an effect applied to it. It runs once, in 900ms, and
 * then never again.
 */
@media (prefers-reduced-motion: no-preference) {
	.profile__kicker {
		animation: rundle-rule-in 700ms cubic-bezier(0.2, 0.8, 0.2, 1) both;
	}

	.profile__name span {
		animation: rundle-set-type 900ms cubic-bezier(0.2, 0.8, 0.2, 1) both;
		display: block;
	}

	.profile__name span:nth-child(2) { animation-delay: 110ms; }

	.profile__discipline { animation: rundle-rise 700ms cubic-bezier(0.2, 0.8, 0.2, 1) 320ms both; }
	.profile__claim      { animation: rundle-rise 700ms cubic-bezier(0.2, 0.8, 0.2, 1) 420ms both; }
	.profile__lede       { animation: rundle-rise 700ms cubic-bezier(0.2, 0.8, 0.2, 1) 500ms both; }
	.profile__actions    { animation: rundle-rise 700ms cubic-bezier(0.2, 0.8, 0.2, 1) 580ms both; }
	.profile .doc        { animation: rundle-rise 700ms cubic-bezier(0.2, 0.8, 0.2, 1) 660ms both; }

	.profile__aside .stamp {
		animation: rundle-stamp 560ms cubic-bezier(0.18, 1.1, 0.3, 1) 740ms both;
	}
}

@keyframes rundle-set-type {
	from {
		font-stretch: 62%;
		letter-spacing: 0.06em;
		opacity: 0;
	}
	to {
		font-stretch: 122%;
		letter-spacing: -0.042em;
		opacity: 1;
	}
}

@keyframes rundle-rule-in {
	from { opacity: 0; clip-path: inset(0 100% 0 0); }
	to   { opacity: 1; clip-path: inset(0 0 0 0); }
}

@keyframes rundle-rise {
	from { opacity: 0; transform: translateY(14px); }
	to   { opacity: 1; transform: none; }
}

/*
 * The stamp is struck: it arrives over-rotated and slightly large, then
 * settles onto its resting angle. One overshoot, no bounce.
 */
@keyframes rundle-stamp {
	from { opacity: 0; transform: rotate(-11deg) scale(1.28); }
	55%  { opacity: 1; }
	to   { opacity: 1; transform: rotate(-2.5deg) scale(1); }
}

/* ---- Scroll-driven work -------------------------------------------------
 *
 * Section rules draw as they enter, and the route ticks in posting by
 * posting. Because each item is driven by its own position in the viewport
 * rather than by a timer, the stagger is a property of the layout and costs
 * nothing to maintain.
 */
@supports (animation-timeline: view()) {
	@media (prefers-reduced-motion: no-preference) {
		.sheet {
			animation: rundle-rule-in linear both;
			animation-timeline: view();
			animation-range: entry 5% entry 45%;
		}

		.route__item {
			animation: rundle-tick linear both;
			animation-timeline: view();
			animation-range: entry 2% entry 38%;
		}

		.register__role,
		.qual,
		.early__item,
		.keynote,
		.post-row {
			animation: rundle-row linear both;
			animation-timeline: view();
			animation-range: entry 2% entry 32%;
		}

		.delivered__figure {
			animation: rundle-figure linear both;
			animation-timeline: view();
			animation-range: entry 5% entry 50%;
		}
	}
}

@keyframes rundle-tick {
	from { opacity: 0; transform: translateY(10px); }
	to   { opacity: 1; transform: none; }
}

@keyframes rundle-row {
	from { opacity: 0.001; transform: translateY(8px); }
	to   { opacity: 1; transform: none; }
}

/*
 * The figures arrive wide. Same width axis as the name, used once more where
 * a number wants to feel struck rather than faded in.
 */
@keyframes rundle-figure {
	from { opacity: 0; font-stretch: 75%; }
	to   { opacity: 1; font-stretch: 118%; }
}

/* ---- Hover and focus ----------------------------------------------------
 *
 * The header's underline grows from the left rather than fading, so it reads
 * as a rule being drawn under the word.
 */
.nav__list a,
.site-nav a { position: relative; }

.nav__list a::after,
.site-nav a::after {
	content: "";
	position: absolute;
	left: 0;
	right: 0;
	bottom: 0;
	height: 2px;
	background: var(--signal);
	transform: scaleX(0);
	transform-origin: left;
	transition: transform 0.22s cubic-bezier(0.2, 0.8, 0.2, 1);
}

.nav__list a:hover::after,
.site-nav a:hover::after,
.site-nav .current-menu-item > a::after,
.site-nav .current_page_item > a::after { transform: scaleX(1); }

.nav-cta { transition: background 0.18s ease, color 0.18s ease; }

@media (prefers-reduced-motion: reduce) {
	.nav__list a::after,
	.site-nav a::after,
	.nav-cta { transition: none; }
}

/* ------------------------------------------------------ 22. The live CV */

/*
 * On screen this is a document on a desk: a white sheet on the page ground,
 * at roughly the proportions of the paper it will print onto. In print the
 * sheet is the page and everything around it is gone.
 */

.cv-tools {
	padding-block: var(--s6) 0;
}

.cv-tools .wrap {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	justify-content: space-between;
	gap: var(--s4);
	padding-bottom: var(--s5);
	border-bottom: 1px solid var(--rule);
}

.cv-tools p { margin: 0; font-size: 0.9375rem; color: var(--ink-soft); }

.cv {
	width: 100%;
	max-width: var(--shell);
	margin: var(--s7) auto var(--s9);
	padding-inline: var(--gutter);
}

.cv__section:first-of-type { margin-top: 0; }

.cv__head {
	display: grid;
	gap: var(--s4);
	padding-bottom: var(--s5);
	border-bottom: 2px solid var(--ink);
}

@media (min-width: 52rem) {
	.cv__head { grid-template-columns: minmax(0, 1fr) auto; align-items: end; }
}

.cv__name {
	font-size: clamp(2.25rem, 1.6rem + 2.6vw, 3.5rem);
	font-weight: 700;
	font-stretch: 120%;
	letter-spacing: -0.03em;
	margin: 0;
}

.cv__title {
	font-family: var(--data);
	font-size: 0.875rem;
	font-weight: 500;
	letter-spacing: 0.12em;
	text-transform: uppercase;
	color: var(--signal);
	margin: var(--s2) 0 0;
}

.cv__contact {
	list-style: none;
	margin: 0;
	padding: 0;
	font-family: var(--data);
	font-size: 0.8125rem;
	line-height: 1.7;
	color: var(--ink-soft);
}

@media (min-width: 52rem) { .cv__contact { text-align: right; } }

/*
 * Section label in a rail, content beside it. This is a layout column, not a
 * cap on the text: the paragraph still fills the column it is given at every
 * width. It happens to make a four-page document readable on a wide monitor,
 * which capping the paragraph would also have done and would have done
 * wrongly.
 */
.cv__section {
	display: grid;
	gap: var(--s3) var(--s6);
	padding-block: 0;
}

@media (min-width: 64rem) {
	.cv__section { grid-template-columns: var(--rail) minmax(0, 1fr); }
}

.cv__h {
	font-family: var(--data);
	font-size: 0.75rem;
	font-weight: 500;
	letter-spacing: 0.16em;
	text-transform: uppercase;
	color: var(--ink-soft);
	border-bottom: 1px solid var(--rule);
	padding-bottom: var(--s2);
	margin: 0 0 var(--s4);
}

/*
 * On a wide screen the section rule runs the full measure and the label hangs
 * in the rail beneath it, sharing a baseline with the first line of content.
 *
 * Previously the rule was drawn on the label alone, so it stopped at the end
 * of the rail and the label text sat a line lower than the body beside it.
 * Two separate alignment faults from one declaration.
 */
@media (min-width: 64rem) {
	.cv__section {
		border-top: 2px solid var(--ink);
		padding-top: var(--s4);
		margin-top: var(--s6);
	}

	.cv__h {
		border-bottom: 0;
		border-top: 0;
		padding: 0;
		margin: 0;
		position: sticky;
		top: 6rem;
		/* Mono cap-height sits high; this drops it onto the body baseline. */
		transform: translateY(0.22em);
	}
}

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

.cv__role { margin-bottom: var(--s5); break-inside: avoid; }

.cv__role-head {
	display: flex;
	flex-wrap: wrap;
	align-items: baseline;
	justify-content: space-between;
	gap: var(--s3);
}

.cv__role-title {
	font-size: 1.25rem;
	font-weight: 600;
	margin: 0;
}

.cv__role-when {
	font-family: var(--data);
	font-size: 0.8125rem;
	color: var(--ink-soft);
	white-space: nowrap;
}

.cv__role-where {
	font-family: var(--data);
	font-size: 0.8125rem;
	letter-spacing: 0.04em;
	color: var(--signal);
	margin: 0.15rem 0 var(--s3);
}

.cv__points { margin: 0; padding-left: 1.1rem; }
.cv__points li { margin-bottom: 0.25rem; font-size: 0.9688rem; line-height: 1.5; }

.cv__note { font-family: var(--data); font-size: 0.75rem; color: var(--ink-faint); margin: 0 0 var(--s3); }

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

.cv__early li {
	display: grid;
	gap: 0.1rem var(--s5);
	padding-block: 0.4rem;
	border-bottom: 1px solid var(--rule-faint);
	font-size: 0.9688rem;
	break-inside: avoid;
}

@media (min-width: 40rem) {
	.cv__early li { grid-template-columns: var(--col-year) minmax(0, 1fr); align-items: baseline; }
}

.cv__early-when {
	font-family: var(--data);
	font-size: 0.8125rem;
	color: var(--ink-soft);
}

.cv__skills { font-size: 0.9688rem; line-height: 1.5; margin-bottom: var(--s3); }
.cv__skills strong { font-family: var(--data); font-size: 0.8125rem; letter-spacing: 0.04em; }

.cv__foot {
	margin-top: var(--s6);
	padding-top: var(--s4);
	border-top: 1px solid var(--rule);
}

@media (min-width: 64rem) {
	.cv__section > :not(.cv__h) { grid-column: 2; }
	.cv__section > .cv__h { grid-row: 1; }
}

.cv__foot p { font-family: var(--data); font-size: 0.75rem; color: var(--ink-faint); margin: 0; }

/*
 * Print. The CV page is designed to be saved as a PDF from the browser, so
 * print is a first-class rendering here rather than an afterthought: the
 * chrome goes, the document fills the sheet, and roles are kept off page
 * breaks.
 */
@media print {
	.site-header,
	.site-footer,
	.cv-tools,
	.nav-cta,
	.nav-toggle,
	.skip-link,
	.breadcrumbs,
	.enquiry { display: none !important; }

	:root {
		--paper: #fff;
		--ink: #000;
		--ink-soft: #333;
		--ink-faint: #555;
		--rule: #999;
		--rule-faint: #ccc;
		--signal: #c8102e;
	}

	body { background: #fff; color: #000; font-size: 10pt; line-height: 1.4; }

	.section { padding-block: 0.5rem; }

	.cv { margin: 0; padding: 0; max-width: none; }

	.cv__section {
		display: block;
		padding-block: 10pt 0;
	}

	.cv__h {
		position: static;
		border-top: 0;
		border-bottom: 1px solid #999;
		padding: 0 0 2pt;
		margin: 0 0 6pt;
	}
	.cv__h { break-after: avoid; }
	.cv__role, .cv__early li { break-inside: avoid; }
	.cv__name { font-size: 22pt; }

	a { text-decoration: none; color: #000; }

	@page { margin: 14mm 14mm 16mm; }
}

/* The CV page's two actions, kept together so they wrap as a pair. */
.cv-tools__actions {
	display: flex;
	flex-wrap: wrap;
	gap: var(--s3);
}

/* ------------------------------------- 24. Route returns, freelance, refs */

/*
 * A return to a country already worked. Dimmed and labelled rather than
 * dropped: four of the twenty postings are returns to South Africa between
 * international assignments, and that is the shape of a rotation, not noise.
 */
.route__item--return { opacity: 0.72; }
.route__item--return .route__place { font-weight: 500; }

.route__item--return::before {
	background: transparent;
	box-shadow: inset 0 0 0 1px var(--ink-faint);
}

.route__back {
	display: block;
	font-family: var(--data);
	font-size: 0.625rem;
	letter-spacing: 0.14em;
	text-transform: uppercase;
	color: var(--ink-faint);
	margin-top: 0.2rem;
}

/* The last posting is the current one. */
.route__item:last-child .route__place { color: var(--signal); }

.route__item:last-child::before {
	background: var(--signal);
	box-shadow: none;
	width: 11px;
	height: 11px;
}

.register__engagement {
	color: var(--signal);
	border: 1px solid var(--signal);
	padding: 0.05rem 0.4rem;
	letter-spacing: 0.1em;
}

/* ---- References ---------------------------------------------------------
 *
 * Real LinkedIn recommendations, confirmed by Jeremy. No stars: the five they
 * used to carry were never sourced from anything and fed an AggregateRating
 * node that has since been removed. A named person saying something is worth
 * more unscored than scored.
 */
/*
 * auto-fit with its own border per card, rather than a one-pixel grid gap
 * over a ruled background. Five references into a three-column grid leaves a
 * sixth cell, and with a background showing through the gaps that empty cell
 * is drawn as a box. Here the last row simply stretches to fill.
 */
/*
 * A column flow, not a grid.
 *
 * There are five references and three columns, and no grid can resolve that
 * without leaving a sixth cell empty: auto-fit collapses empty *tracks*, not
 * a short final row. Columns fill top to bottom and balance themselves, so
 * the block is solid at five references, at four, and at whatever number
 * Jeremy ends up with.
 *
 * Cards are blocks with a bottom margin rather than grid children, because a
 * grid item cannot be kept off a column break.
 */
.refs {
	list-style: none;
	margin: 0;
	padding: 0;
	columns: 1;
	column-gap: var(--s4);
}

@media (min-width: 52rem) { .refs { columns: 2; } }
@media (min-width: 84rem) { .refs { columns: 3; } }

.ref {
	border: 1px solid var(--rule);
	padding: var(--s5);
	display: block;
	break-inside: avoid;
	-webkit-column-break-inside: avoid;
	margin-bottom: var(--s4);
}

.ground-ink .ref { border-color: var(--band-rule); }

.ref__quote { margin: 0 0 var(--s4); }

.ref__quote p {
	margin: 0;
	font-size: 0.9688rem;
	line-height: 1.55;
}

.ref__quote p::before { content: "\201C"; }
.ref__quote p::after { content: "\201D"; }

.ref__who {
	display: grid;
	gap: 0.15rem;
	padding-top: var(--s3);
	border-top: 1px solid var(--rule);
	margin-top: auto;
}

.ground-ink .ref__who { border-top-color: var(--band-rule); }

.ref__name {
	font-family: var(--display);
	font-weight: 600;
	font-stretch: 108%;
	font-size: 1rem;
}

.ref__role {
	font-family: var(--data);
	font-size: 0.75rem;
	letter-spacing: 0.04em;
	color: var(--ink-soft);
}

.ground-ink .ref__role { color: var(--band-soft); }

/*
 * The claim. One sentence, set at display size, carrying the thing that is
 * actually distinctive about this career: he is the one they send when a
 * project has gone wrong. Everything under it is evidence for that sentence.
 */
.profile__claim {
	font-family: var(--display);
	font-weight: 600;
	font-stretch: 112%;
	font-size: clamp(1.375rem, 1.1rem + 1.4vw, 2.125rem);
	line-height: 1.18;
	letter-spacing: -0.02em;
	margin: 0 0 var(--s4);
}

/* --------------------------------------------------------- 25. Speaking */

/*
 * Topic sheets.
 *
 * The keynote cards were styled by the stylesheet this one replaced, so until
 * now they rendered as raw underlined links with their audience, duration and
 * a stray status chip running together into one sentence.
 *
 * Rebuilt to match the register: a number in the rail, the topic and its
 * promise in the measure, the practical detail on the right where a reader
 * scanning for "will this fit my ninety minute slot" will find it.
 */

.keynotes {
	display: grid;
	border-top: 2px solid var(--ink);
}

.ground-ink .keynotes { border-top-color: var(--band-ink); }

.keynote {
	display: grid;
	gap: var(--s2) var(--s6);
	padding-block: var(--s5);
	border-bottom: 1px solid var(--rule);
	text-decoration: none;
	align-items: start;
	transition: background 0.18s ease, padding 0.18s ease;
}

.ground-ink .keynote { border-bottom-color: var(--band-rule); }

@media (min-width: 60rem) {
	.keynote {
		grid-template-columns: var(--col-year) minmax(0, 1fr) minmax(0, 16rem);
	}
}

.keynote__no {
	font-family: var(--data);
	font-size: 0.8125rem;
	font-weight: 500;
	letter-spacing: 0.1em;
	color: var(--ink-faint);
	padding-top: 0.35rem;
}

.ground-ink .keynote__no { color: var(--band-soft); }

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

.keynote__title {
	font-family: var(--display);
	font-weight: 600;
	font-stretch: 110%;
	font-size: clamp(1.1875rem, 1.05rem + 0.8vw, 1.625rem);
	line-height: 1.18;
	letter-spacing: -0.015em;
	text-decoration: none;
}

.keynote__promise {
	font-size: 0.9688rem;
	line-height: 1.5;
	color: var(--ink-soft);
}

.ground-ink .keynote__promise { color: var(--band-soft); }

.keynote__meta {
	display: flex;
	flex-wrap: wrap;
	gap: var(--s2) var(--s3);
	align-content: start;
	padding-top: 0.2rem;
}

.keynote__tag {
	font-family: var(--data);
	font-size: 0.6875rem;
	font-weight: 500;
	letter-spacing: 0.1em;
	text-transform: uppercase;
	color: var(--ink-soft);
	border: 1px solid var(--rule);
	padding: 0.2rem 0.5rem;
}

.ground-ink .keynote__tag { color: var(--band-soft); border-color: var(--band-rule); }

.keynote__tag--time { color: var(--signal); border-color: var(--signal); }
.ground-ink .keynote__tag--time { color: var(--signal); border-color: var(--signal); }

/* The red rule arrives on hover, which is the only movement in the list. */
.keynote:hover,
.keynote:focus-visible {
	background: var(--signal-wash);
	box-shadow: inset 3px 0 0 var(--signal);
	padding-inline: var(--s4);
}

.ground-ink .keynote:hover,
.ground-ink .keynote:focus-visible { background: rgba(255, 77, 94, 0.07); }

.keynote:hover .keynote__title,
.keynote:focus-visible .keynote__title { color: var(--signal); }

@media (prefers-reduced-motion: reduce) {
	.keynote { transition: none; }
	.keynote:hover, .keynote:focus-visible { padding-inline: 0; }
}

/* ---------------------------------------------------------- 26. Contact */

.contact {
	display: grid;
	gap: var(--s7);
	align-items: start;
}

@media (min-width: 64rem) {
	.contact { grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr); gap: var(--s9); }
}

.contact__methods {
	display: grid;
	gap: 1px;
	background: var(--rule);
	border: 1px solid var(--rule);
}

.contact__method {
	display: grid;
	gap: 0.3rem;
	padding: var(--s5) var(--s6);
	background: var(--paper);
	text-decoration: none;
	border-left: 3px solid transparent;
	transition: background 0.18s ease, border-color 0.18s ease;
}

.contact__method:hover,
.contact__method:focus-visible {
	background: var(--signal-wash);
	border-left-color: var(--signal);
}

.contact__key {
	font-family: var(--data);
	font-size: 0.625rem;
	font-weight: 500;
	letter-spacing: 0.2em;
	text-transform: uppercase;
	color: var(--ink-faint);
}

.contact__value {
	font-family: var(--display);
	font-weight: 600;
	font-stretch: 110%;
	font-size: clamp(1.125rem, 1rem + 0.8vw, 1.5rem);
	letter-spacing: -0.015em;
}

.contact__method:hover .contact__value,
.contact__method:focus-visible .contact__value { color: var(--signal); }

.contact__note { font-size: 0.875rem; color: var(--ink-soft); }

.contact__aside { display: grid; gap: var(--s6); justify-items: start; }

.contact__facts {
	margin: 0;
	width: 100%;
	border-top: 2px solid var(--ink);
}

.contact__facts > div {
	display: grid;
	gap: 0.15rem var(--s5);
	padding-block: var(--s3);
	border-bottom: 1px solid var(--rule-faint);
}

@media (min-width: 30rem) {
	.contact__facts > div { grid-template-columns: 6rem minmax(0, 1fr); align-items: baseline; }
}

.contact__facts dt {
	font-family: var(--data);
	font-size: 0.6875rem;
	font-weight: 500;
	letter-spacing: 0.16em;
	text-transform: uppercase;
	color: var(--ink-faint);
}

.contact__facts dd { margin: 0; font-size: 0.9688rem; }

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

/* ------------------------------------------- 27. Card row alignment ------
 *
 * Every card grid on this site has variable-length text above a fixed set of
 * trailing elements: a grade and a verify link, a figure's provenance, a
 * project's role. Left alone, those trailing elements sit wherever the text
 * above them happens to end, so across four memberships the word MEMBER
 * appears at four different heights and the row reads as broken.
 *
 * subgrid solves it exactly: each card adopts the row track heights of the
 * grid it sits in, so equivalent elements land on the same line across every
 * card regardless of how long the body copy is.
 *
 * The fallback for browsers without subgrid is a 1fr spacer row, which pins
 * the trailing block to the bottom of the card. Not identical, but it aligns
 * the thing a reader actually scans across.
 */

.memberships,
.projects,
.delivered {
	align-items: stretch;
}

.membership,
.project,
.delivered__cell {
	/* Fallback: push everything after the flexible row to the card foot. */
	grid-template-rows: auto 1fr auto;
}

.membership__grade,
.membership__number,
.membership__verify,
.project__body,
.ref__who,
.delivered__where { margin-top: 0; }

.membership__body,
.project__body,
.ref__quote { align-self: start; }

@supports (grid-template-rows: subgrid) {
	@media (min-width: 46rem) {
		/*
		 * Four rows, named by what they hold: identifier, body, grade,
		 * verification. Every membership card uses the same four, so the
		 * grades align and the verify links align.
		 */
		.memberships { grid-template-rows: auto auto auto auto; }

		.membership {
			grid-row: span 4;
			grid-template-rows: subgrid;
			row-gap: var(--s2);
		}
	}
}

@supports (grid-template-rows: subgrid) {
	@media (min-width: 52rem) {
		.projects { grid-template-rows: auto auto auto auto; }

		.project {
			grid-row: span 4;
			grid-template-rows: subgrid;
			row-gap: var(--s3);
		}
	}


	@media (min-width: 46rem) {
		.delivered { grid-template-rows: auto auto auto auto; }

		.delivered__cell {
			grid-row: span 4;
			grid-template-rows: subgrid;
			row-gap: var(--s2);
		}
	}
}

/* The membership number is optional, so it must not collapse the row set. */
.membership__number {
	font-family: var(--data);
	font-size: 0.6875rem;
	letter-spacing: 0.08em;
	color: var(--ink-faint);
}

.membership__verify {
	justify-self: start;
	font-family: var(--data);
	font-size: 0.6875rem;
	font-weight: 500;
	letter-spacing: 0.14em;
	text-transform: uppercase;
	color: var(--signal);
	text-decoration: none;
	border-bottom: 1px solid var(--signal);
	padding-bottom: 0.1rem;
}

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

.screen-reader-text {
	position: absolute;
	width: 1px;
	height: 1px;
	padding: 0;
	margin: -1px;
	overflow: hidden;
	clip-path: inset(50%);
	white-space: nowrap;
	border: 0;
}

/* ---------------------------------------------------------- The portrait */

/*
 * A specimen on the sheet, not a headshot in a circle.
 *
 * Hard edges, a hairline frame offset by a few pixels so the photograph sits
 * slightly proud of its own border the way a print sits in a mount, and a
 * caption in the data face. The red rule under the caption is the only mark
 * the page spends on it.
 *
 * The whole block renders only when a portrait exists. There is no empty
 * frame and no silhouette: without one the grid closes up.
 */

.portrait {
	margin: 0 0 var(--s5);
	max-width: 22rem;
}

.portrait__frame {
	position: relative;
	border: 1px solid var(--rule);
	padding: 6px;
	background: var(--paper);
}

/* The mount: a thin offset rule, drawn rather than shadowed. */
.portrait__frame::after {
	content: "";
	position: absolute;
	inset: -7px;
	border: 1px solid var(--ink);
	pointer-events: none;
}

.portrait img {
	display: block;
	width: 100%;
	height: auto;
	aspect-ratio: 4 / 5;
	object-fit: cover;
	object-position: 50% 22%;
	/*
	 * The supplied photograph has a bright studio background that fights a
	 * bone-and-black page. Desaturating most of the way and lifting contrast
	 * settles it onto the sheet without pretending it is a different photo.
	 */
	filter: grayscale(0.82) contrast(1.08) brightness(1.02);
}

.portrait__caption {
	display: grid;
	gap: 0.15rem;
	margin-top: var(--s4);
	padding-top: var(--s3);
	border-top: 2px solid var(--signal);
}

.portrait__name {
	font-family: var(--display);
	font-weight: 700;
	font-stretch: 112%;
	font-size: 1rem;
	letter-spacing: -0.01em;
}

.portrait__where {
	font-family: var(--data);
	font-size: 0.6875rem;
	letter-spacing: 0.1em;
	text-transform: uppercase;
	color: var(--ink-soft);
}

/* On the dark band the frame and caption invert. */
.ground-ink .portrait__frame { background: var(--band); border-color: var(--band-rule); }
.ground-ink .portrait__frame::after { border-color: var(--band-soft); }
.ground-ink .portrait__where { color: var(--band-soft); }

/*
 * With a portrait the hero becomes three parts: the title block, the
 * photograph, and the stamp stacked under it. Without one the aside holds
 * only the stamp and the grid stays as it was.
 */
@media (min-width: 60rem) {
	.profile__grid--portrait { grid-template-columns: minmax(0, 1fr) minmax(19rem, 24rem); }
}

@media (max-width: 60rem) {
	.portrait { max-width: 16rem; }
}

/* The About page runs it the other way: portrait first, then the prose. */
.portrait--page { max-width: 18rem; float: none; }

@media (min-width: 64rem) {
	.portrait--page { max-width: 20rem; }
}

@media print {
	.portrait { display: none; }
}

/* ------------------------------------------------------------- Projects */

/*
 * The work itself. A ruled register rather than a card grid: these entries
 * have very different amounts in them, and cards would either pad the short
 * ones or crop the long ones. A rule between them does neither, and it never
 * leaves an empty cell.
 */

.work {
	list-style: none;
	margin: 0;
	padding: 0;
	border-top: 2px solid var(--ink);
}

.work__item {
	display: grid;
	gap: var(--s4);
	padding-block: var(--s6);
	border-bottom: 1px solid var(--rule);
}

@media (min-width: 64rem) {
	.work__item {
		grid-template-columns: minmax(0, 1.35fr) minmax(0, 1fr);
		gap: var(--s4) var(--s8);
		align-items: start;
	}

	.work__head { grid-column: 1; }
	.work__scope { grid-column: 1; }
	.work__figures { grid-column: 2; grid-row: 1 / span 2; }
}

.work__title {
	font-size: clamp(1.3125rem, 1.05rem + 1.2vw, 1.875rem);
	line-height: 1.12;
	margin: 0 0 var(--s2);
}

.work__title a { text-decoration: none; }
.work__title a:hover { text-decoration: underline; text-decoration-color: var(--signal); }

.work__flag {
	display: inline-block;
	font-family: var(--data);
	font-size: 0.625rem;
	font-weight: 500;
	letter-spacing: 0.14em;
	text-transform: uppercase;
	color: var(--signal);
	border: 1px solid var(--signal);
	padding: 0.1rem 0.4rem;
	margin-left: 0.5rem;
	vertical-align: 0.25em;
}

.work__where {
	display: flex;
	flex-wrap: wrap;
	gap: 0.3rem 0.9rem;
	margin: 0;
	font-family: var(--data);
	font-size: 0.75rem;
	letter-spacing: 0.08em;
	text-transform: uppercase;
	color: var(--ink-soft);
}

.work__where b { color: var(--ink); font-weight: 500; }

.work__scope { margin: 0; }

/*
 * Figures. Only the ones that exist are rendered: a confidential contract
 * with no publishable value simply has no value row, rather than a label
 * followed by "undisclosed", which only draws the eye to the gap.
 */
.work__figures {
	margin: 0;
	display: grid;
	gap: 1px;
	background: var(--rule);
	border: 1px solid var(--rule);
}

.work__figures > div {
	background: var(--paper);
	padding: var(--s3) var(--s4);
	display: grid;
	gap: 0.1rem;
}

.work__figures dt {
	font-family: var(--data);
	font-size: 0.625rem;
	font-weight: 500;
	letter-spacing: 0.16em;
	text-transform: uppercase;
	color: var(--ink-faint);
}

.work__figures dd {
	margin: 0;
	font-family: var(--display);
	font-weight: 600;
	font-stretch: 110%;
	font-size: 1.0625rem;
}

.work__item--current .work__title a { color: var(--signal); }

/* Outside work: one sentence, not a card the size of a mine. */
.outside {
	border-top: 1px solid var(--rule);
	padding-top: var(--s4);
	margin: 0;
	font-size: 0.9688rem;
	color: var(--ink-soft);
}

.outside__label {
	display: block;
	font-family: var(--data);
	font-size: 0.6875rem;
	font-weight: 500;
	letter-spacing: 0.16em;
	text-transform: uppercase;
	color: var(--ink-faint);
	margin-bottom: var(--s2);
}

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

/* On a project page the figures run across the full width rather than a rail. */
@media (min-width: 52rem) {
	.work__figures--wide { grid-template-columns: repeat(auto-fit, minmax(min(100%, 12rem), 1fr)); }
}

.project__scope { font-size: clamp(1.0625rem, 1rem + 0.4vw, 1.25rem); line-height: 1.5; }

/* ----------------------------------------------------------- Newsletter */

/*
 * The only thing on this site that asks for a second visit.
 *
 * Given the weight of a call to action rather than a card: a ruled band, the
 * name at display size, and one button. It sits at the foot of the writing
 * section because that is the moment a reader has just finished something and
 * is deciding whether this person is worth following.
 */

.news {
	display: grid;
	gap: var(--s4);
	align-items: center;
	padding: var(--s6);
	border: 1px solid var(--signal);
	border-left-width: 4px;
}

@media (min-width: 52rem) {
	.news { grid-template-columns: minmax(0, 1fr) auto; gap: var(--s7); }
}

.news__label {
	display: block;
	font-family: var(--data);
	font-size: 0.6875rem;
	font-weight: 500;
	letter-spacing: 0.18em;
	text-transform: uppercase;
	color: var(--signal);
	margin-bottom: var(--s2);
}

.news__title {
	font-size: clamp(1.375rem, 1.1rem + 1.3vw, 2rem);
	line-height: 1.12;
	margin: 0;
}

.news__note {
	margin: var(--s3) 0 0;
	font-size: 0.9688rem;
	color: var(--ink-soft);
}

.news__cta { justify-self: start; white-space: nowrap; }

.ground-ink .news__note { color: var(--band-soft); }

/* The footer carries it as one line, not a second billboard. */
.news-line {
	margin: var(--s5) 0 0;
	font-family: var(--data);
	font-size: 0.8125rem;
}

.news-line__label {
	display: block;
	font-size: 0.625rem;
	font-weight: 500;
	letter-spacing: 0.18em;
	text-transform: uppercase;
	color: var(--band-soft);
	margin-bottom: 0.2rem;
}

.news-line a {
	color: var(--band-ink);
	text-decoration: none;
	border-bottom: 1px solid var(--signal-band);
	padding-bottom: 1px;
}

@media print { .news, .news-line { display: none; } }

/* ------------------------------------------------------ The signature mark */

/*
 * The wordmark is supplied white on transparent, which is invisible on the
 * bone paper the header sits on. It is clean monochrome with a real alpha
 * channel, so inverting it on light grounds turns it to ink and leaves the
 * transparency alone. One file serves both, and no second export is needed.
 *
 * If a dark original ever replaces it, delete the two invert rules and
 * nothing else changes.
 */

.brand--mark { align-items: start; }

.brand__mark {
	display: block;
	width: auto;
	height: 2.1rem;
	filter: invert(1);
}

@media (min-width: 60rem) {
	.brand__mark { height: 2.4rem; }
}

/* On a dark ground the artwork is already the right colour. */
.ground-ink .brand__mark { filter: none; }

@media (prefers-color-scheme: dark) {
	:root:not([data-theme="light"]) .brand__mark { filter: none; }
}

:root[data-theme="dark"] .brand__mark { filter: none; }

/* ---- Signed, at the foot of the record --------------------------------- */

.sign {
	display: grid;
	justify-items: start;
	gap: var(--s2);
	margin-top: var(--s7);
	padding-top: var(--s5);
	border-top: 1px solid var(--band-rule);
}

.sign__mark {
	display: block;
	width: auto;
	height: clamp(2.75rem, 2rem + 2.4vw, 4rem);
	opacity: 0.92;
}

.sign__rule {
	display: block;
	width: min(22rem, 100%);
	height: 1px;
	background: var(--band-soft);
	margin-top: var(--s1);
}

.sign__caption {
	font-family: var(--data);
	font-size: 0.6875rem;
	letter-spacing: 0.14em;
	text-transform: uppercase;
	color: var(--band-soft);
}

@media print {
	.brand__mark { filter: invert(1); }
	.sign { display: none; }
}

/* ------------------------------------------- The signature as the headline */

/*
 * When a signature is supplied it becomes the headline, so it needs the
 * presence the type had: it fills the column rather than sitting at logo
 * size. Width-led, because the artwork is a fixed 2.51:1 and height-led
 * sizing makes it shrink on narrow screens exactly where it should not.
 *
 * Same inversion as the header mark: the file is white, the paper is bone.
 */
.profile__name--mark {
	margin: 0 0 var(--s4);
	line-height: 0;
}

.profile__mark {
	display: block;
	width: min(100%, 36rem);
	height: auto;
	filter: invert(1);
}

@media (min-width: 64rem) {
	.profile__mark { width: min(100%, 42rem); }
}

.ground-ink .profile__mark { filter: none; }

@media (prefers-color-scheme: dark) {
	:root:not([data-theme="light"]) .profile__mark { filter: none; }
}

:root[data-theme="dark"] .profile__mark { filter: none; }

@media print {
	.profile__mark { filter: invert(1); width: 12rem; }
}

/* A credential's registration number and its verification link. Both sit
   inside the issuer cell so the three column row keeps its alignment. */
.qual__number {
	display: block;
	font-variant-numeric: tabular-nums;
	color: var(--ink-faint);
}

.qual__verify {
	display: inline-block;
	margin-top: 0.15rem;
	font-size: 0.75rem;
	letter-spacing: 0.04em;
	text-transform: uppercase;
	color: var(--ink);
	text-decoration-thickness: 1px;
	text-underline-offset: 0.2em;
}

