/**
 * Design tokens and layout primitives.
 *
 * theme.json owns colour, type and spacing presets, because those need to appear
 * in the block editor's controls. This file owns the things theme.json cannot
 * express: the fluid gutter, the header height, the brand corner shape, and the
 * two layout mechanisms that make the site responsive without per-page tuning.
 *
 * Kyle's design export has no mobile layout at all. Zero media queries, zero
 * clamp(), zero viewport units, and every grid is repeat(N, minmax(0,1fr)) with a
 * fixed N, so a four-column grid stays four columns at 360px. Rather than write
 * breakpoints for 34 pages, almost everything here is intrinsic: it responds to
 * the space it is given, not to the size of the window.
 *
 * The numbers are taken from his files, not invented. Desktop pages use 72px
 * horizontal padding and 96px section padding almost universally; his one mobile
 * sketch (Office Fit Out Landing) uses 20px and 48px. Those are the two ends of
 * every clamp() below.
 */

/**
 * Border-box everywhere.
 *
 * Not optional and not decorative. Without it, padding is added to width rather
 * than taken out of it, so a 1440px max-width container with a 72px gutter each
 * side measures 1584px and pushes 144px of content off the side of the screen.
 * That is exactly what happened when the old stylesheet (which carried this
 * reset) was removed.
 */
*,
*::before,
*::after {
	box-sizing: border-box;
}

/**
 * Font smoothing, which is the one thing about the type we were not copying.
 *
 * Kyle's export opens with this on the body of all 38 pages:
 *
 *     body { ... -webkit-font-smoothing: antialiased; }
 *
 * We had it nowhere. Checked on the deployed page against his hosted export:
 * every rule on the site computed 'auto', his computed 'antialiased', and the
 * only 'antialiased' declarations we were serving came from WooCommerce and
 * applied to its own icon pseudo-elements.
 *
 * ON A MAC THAT IS VISIBLE AND IT MAKES OUR TEXT HEAVIER THAN HIS. macOS still
 * applies its own smoothing under 'auto', which thickens the stems; the
 * 'antialiased' keyword turns that off and leaves plain greyscale. Alex, 17 Aug
 * 2026, comparing staging against the design: "the font on the staging site
 * looks a bit thicker to me". He is on a Mac and this is why.
 *
 * IT IS NOT THE FONT FILE, which was the first thing checked and ruled out.
 * Chrome's own resolver reports Gilroy-Bold on both, and the identical string
 * rendered on both pages at the same size and pixel density differs by ZERO
 * pixels out of 84,448. The metrics are the same; only the rasterisation is
 * not.
 *
 * THE FIREFOX LINE IS OURS, NOT HIS. -webkit-font-smoothing does nothing in
 * Firefox, so with only his declaration Firefox on a Mac would be the one
 * browser still rendering the site heavy while Safari and Chrome went light.
 * That is a worse outcome than either state on its own. -moz-osx-font-smoothing
 * is the counterpart and produces the same result, so all three agree.
 *
 * NEITHER PROPERTY DOES ANYTHING ON LINUX OR WINDOWS, which is exactly why this
 * went unnoticed for a fortnight and why it cannot be verified from the dev
 * box. What was verified here: the declaration computes on the deployed page,
 * and the Work grid measures identically before and after across 28 widths in
 * three engines, so it changes rasterisation and not layout. Whether it LOOKS
 * right is Alex's call on a Mac, and the thing to look at is body copy rather
 * than headings: the site sets Gilroy Light at 300 for most running text, and
 * this makes light type lighter.
 */
body {
	-webkit-font-smoothing: antialiased;
	-moz-osx-font-smoothing: grayscale;
}

:root {
	/* Horizontal page gutter. 20px on a phone, 72px on a wide screen. */
	--d4-gutter: clamp(20px, 5vw, 72px);

	/* Vertical rhythm between sections. */
	--d4-section-y: clamp(48px, 8vw, 96px);

	/* Widest the content ever gets. The export's min-width:1080px and 1280px are
	   canvas artefacts from the design tool, not design intent, so they are not
	   carried over.

	   Must stay equal to CAP in wp-cli/d4-import/transcribe.py, which is the
	   number that actually governs the shape pages. Raising this one on its own
	   splits the site in two: prose and archive pages widen while every
	   transcribed page stays where it was. Tried on 12 Aug and reverted. */
	--d4-max: 1440px;
	--d4-max-narrow: 1200px;
	--d4-measure: 68ch; /* readable line length for body copy */

	/**
	 * Header height, derived rather than declared.
	 *
	 * It is the logo height plus the padding above and below it, which is
	 * exactly how the header is built in chrome.css, so the two cannot drift.
	 * The previous value was a guess (clamp(64px, 7vw, 96px)) and the header it
	 * described no longer exists.
	 *
	 * Kyle's header is sticky, so nothing has to be offset by this any more and
	 * the whole class of "content sits under the header" bug is gone. It is kept
	 * because the mobile drawer opens from the bottom of the bar and needs to
	 * know where that is.
	 */
	--d4-header-pad: clamp(12px, 2vw, 20px);
	--d4-logo-h: clamp(24px, 3vw, 36px);
	--d4-header-h: calc(var(--d4-logo-h) + (2 * var(--d4-header-pad)));

	/* The one-corner cut on imagery and panels. 120px and 60px in the export
	   (65 and 44 uses), scaled down on small screens so it does not eat a phone. */
	--d4-corner-lg: clamp(40px, 9vw, 120px);
	--d4-corner-sm: clamp(24px, 5vw, 60px);

	/* Default minimum column width for intrinsic grids. Blocks override it. */
	--d4-col-min: 260px;

	/**
	 * Image placeholder. Build scaffolding, not a brand colour.
	 *
	 * Kyle uses #f4f4f2 behind images 108 times. About 62 of his 78 image slots
	 * have no photo in them yet, so on his pages this shows as large pale blocks
	 * holding the space a photograph will fill.
	 *
	 * It is matched here on purpose, so our WordPress build looks like his design
	 * while the photos are still being gathered. It is NOT a fifth brand colour:
	 * once a photo is in the slot the placeholder is completely covered and never
	 * seen. The brand palette stays at the four in the guide.
	 *
	 * When the real photography lands this should end up invisible everywhere.
	 * If a pale block is still showing on a finished page, that is a missing
	 * image, not a design decision.
	 */
	--d4-placeholder: #f4f4f2;

	/**
	 * Hairline dividers.
	 *
	 * Kyle uses #3a3a3a for these 239 times, always a border and almost always
	 * a border-top separating rows. It is the one colour in his export that is
	 * not a near-miss of a brand colour, but it does not need to be: you cannot
	 * draw a visible line on #1D1D1D using #1D1D1D, so something has to give.
	 *
	 * White at 13% over the near-black computes to exactly #3A3A3A, deltaE 0.00.
	 * So his hairline is not a fifth colour at all, it is the surface with a
	 * little white bled through, and expressing it as transparency reproduces his
	 * design precisely while keeping the palette to four.
	 *
	 * Worth knowing why the obvious alternative is wrong: solid brand grey would
	 * be a heavy rule, not a whisper, and would change how every dark section
	 * reads.
	 */
	--d4-rule-on-dark: rgb(255 255 255 / 0.13);
	--d4-rule-on-light: rgb(29 29 29 / 0.13);

	--d4-ease: cubic-bezier(0.2, 0.6, 0.2, 1);
	--d4-transition: 0.25s var(--d4-ease);
}

/* ---------------------------------------------------------------------------
   Layout primitives
   --------------------------------------------------------------------------- */

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

.d4-wrap--narrow { max-width: var(--d4-max-narrow); }

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

/**
 * Two sections on the same colour do not both pay for the gap between them.
 *
 * His pages do this by hand: the first section on a colour uses "96px 72px" and
 * every one after it uses "0 72px 96px", so the space between two white sections
 * is 96px, not 192px. Applying full padding to every block turns a page of eight
 * sections into a page with 96px of extra air in seven places, which reads as a
 * different design rather than a faithful one.
 *
 * Where the colour changes, both keep their padding: a dark band starting flush
 * against the last line of a light section is a hard edge, and his pages keep
 * the breathing room there for exactly that reason.
 *
 * Written as one rule per colour because CSS cannot ask whether two elements
 * share a background. Three colours, three rules, and it stays honest.
 */
.has-paper-background-color + .d4-section.has-paper-background-color,
.has-grey-background-color + .d4-section.has-grey-background-color,
.has-ink-background-color + .d4-section.has-ink-background-color {
	padding-block-start: 0;
}

/**
 * The intrinsic grid. This is the single most useful rule in the file.
 *
 * auto-fit collapses 4 columns to 3 to 2 to 1 on its own as space runs out, so
 * the ~90 grids in the design need no breakpoints. The min() guard is
 * load-bearing: without it, a 260px minimum on a 320px screen (once the gutter
 * is taken off) overflows the viewport, which is exactly the bug we are fixing.
 */
.d4-grid {
	display: grid;
	gap: var(--wp--preset--spacing--30, 2rem);
	grid-template-columns: repeat(auto-fit, minmax(min(var(--d4-col-min), 100%), 1fr));
}

/**
 * Two-column splits (image beside text, heading beside body, question beside
 * answer). These appear both full width and nested inside other sections, so the
 * viewport is the wrong thing to measure. A container query asks how much room
 * this particular block actually has, which is the real question.
 */
.d4-split {
	container-type: inline-size;
}

.d4-split__inner {
	display: grid;
	/* 96px between the columns on his pages, easing off on narrow screens. */
	gap: clamp(2rem, 5vw, 96px);
	grid-template-columns: var(--d4-split-ratio, 1fr 1fr);
	align-items: var(--d4-split-align, center);
}

@container (max-width: 60rem) {
	.d4-split__inner {
		grid-template-columns: 1fr;
	}
}

/* Media that fills its slot without distorting. The frame carries the placeholder
   so an empty slot holds its space and the page reads like Kyle's design while
   the photography is still being gathered. */
.d4-media-frame {
	background-color: var(--d4-placeholder);
	overflow: hidden;
}

/* A slot that has been filled does not need the placeholder behind it. */
.d4-media-frame:has(img) {
	background-color: transparent;
}

.d4-media {
	display: block;
	width: 100%;
	height: 100%;
	object-fit: cover;
}

.d4-corner-lg { border-radius: 0 0 var(--d4-corner-lg) 0; overflow: hidden; }
.d4-corner-sm { border-radius: 0 0 var(--d4-corner-sm) 0; overflow: hidden; }

/* Row dividers. The design leans on these heavily: 239 hairlines, 158 of them a
   border-top separating one row of a list from the next. */
.d4-rule,
.d4-rule-top { border-top: 1px solid var(--d4-rule-on-light); }
.d4-rule-bottom { border-bottom: 1px solid var(--d4-rule-on-light); }

.d4-on-dark .d4-rule,
.d4-on-dark .d4-rule-top { border-top-color: var(--d4-rule-on-dark); }
.d4-on-dark .d4-rule-bottom { border-bottom-color: var(--d4-rule-on-dark); }

/* The small-caps label above a heading. 321 occurrences in the export, always
   letter-spacing:0.16em, always the same job. */
.d4-eyebrow {
	font-size: var(--wp--preset--font-size--xs);
	font-weight: 700;
	letter-spacing: 0.16em;
	text-transform: uppercase;
	line-height: 1.3;
}

/* The accent word at the end of a heading. In the export this is an inline
   <span style="color:#af4835">; as an editable field it becomes <mark>, which
   survives copy edits because the editor treats it as formatting. */
.d4-accent,
mark.d4-accent {
	background: none;
	color: var(--wp--preset--color--terracotta);
}

/* ---------------------------------------------------------------------------
   Body copy
   ---------------------------------------------------------------------------
   Blocks put their prose inside .d4-prose. Paragraphs get their spacing from the
   flow, not from stacked margins, so a block never has to strip a trailing
   margin off its last child.
   --------------------------------------------------------------------------- */

.d4-prose > * + * {
	margin-block-start: 1em;
}

.d4-prose > :first-child { margin-block-start: 0; }
.d4-prose > :last-child  { margin-block-end: 0; }

.d4-prose p,
.d4-prose li {
	font-size: var(--wp--preset--font-size--md);
	line-height: 1.55;
	max-width: var(--d4-measure);
}

.d4-prose ul,
.d4-prose ol {
	padding-inline-start: 1.25em;
}

.d4-prose li + li { margin-block-start: 0.4em; }

.d4-prose a {
	text-decoration: underline;
	text-underline-offset: 0.18em;
}

/* ---------------------------------------------------------------------------
   Buttons
   ---------------------------------------------------------------------------
   Two variants, which is all the design uses: a solid dark button and an
   outlined one. Sized in rem off the fluid type scale so they grow with the
   page rather than needing their own breakpoints, and given a 44px minimum
   height because anything smaller is an awkward tap target on a phone.
   --------------------------------------------------------------------------- */

.d4-btns {
	display: flex;
	flex-wrap: wrap;
	gap: 0.75rem;
	margin-top: var(--wp--preset--spacing--20, 1.5rem);
}

.d4-btn {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	min-height: 44px;
	/* 16px by 30px at a 15px label, measured off the design. Expressed in em so
	   the box grows with the text rather than needing its own breakpoints. */
	padding: 1.07em 2em;
	font-size: 15px;
	font-weight: 700;
	line-height: 1.2;
	text-decoration: none;
	border: 1px solid transparent;
	cursor: pointer;
}

/* Near black. 58 uses, mostly on the light surfaces. */
.d4-btn--dark,
.d4-btn--solid {
	background-color: var(--wp--preset--color--ink);
	color: var(--wp--preset--color--paper);
}

/* Terracotta. 67 uses, mostly on the dark surfaces and the hero overlays. */
.d4-btn--accent {
	background-color: var(--wp--preset--color--terracotta);
	color: var(--wp--preset--color--paper);
}

.d4-btn--outline {
	background-color: transparent;
	color: currentColor;
	border-color: currentColor;
}

/* Both solid buttons go white on hover, which is what the design does 63 times. */
.d4-btn--dark:hover,
.d4-btn--solid:hover,
.d4-btn--accent:hover {
	background-color: var(--wp--preset--color--paper);
	color: var(--wp--preset--color--ink);
}

/* A near-black button on a near-black panel would vanish, so it falls back to
   the accent there. That is also what the design does. */
.d4-on-dark .d4-btn--dark,
.d4-on-dark .d4-btn--solid {
	background-color: var(--wp--preset--color--terracotta);
}

/* ---------------------------------------------------------------------------
   Hover utilities
   ---------------------------------------------------------------------------
   The export carries a custom style-hover="..." attribute on 499 elements. It is
   not real HTML and only works inside the design tool's runtime. All 499 reduce
   to the six effects below, so they become classes rather than a script that
   reads the attribute at runtime and repaints on load.
   --------------------------------------------------------------------------- */

.d4-hv-link       { transition: color var(--d4-transition); }
.d4-hv-link:hover { color: var(--wp--preset--color--terracotta); }

.d4-hv-btn        { transition: background-color var(--d4-transition), color var(--d4-transition); }
.d4-hv-btn:hover  { background-color: var(--wp--preset--color--terracotta); color: var(--wp--preset--color--paper); }

.d4-hv-outline       { transition: border-color var(--d4-transition), background-color var(--d4-transition), color var(--d4-transition); }
.d4-hv-outline:hover { background-color: var(--wp--preset--color--ink); color: var(--wp--preset--color--paper); }

.d4-hv-lift        { transition: transform var(--d4-transition), box-shadow var(--d4-transition); }
.d4-hv-lift:hover  { transform: translateY(-4px); box-shadow: 0 18px 48px rgb(29 29 29 / 0.14); }

.d4-hv-zoom img       { transition: transform 0.5s var(--d4-ease); }
.d4-hv-zoom:hover img { transform: scale(1.04); }

.d4-hv-chip       { transition: border-color var(--d4-transition); }
.d4-hv-chip:hover { border-color: var(--wp--preset--color--ink); }

/* ---------------------------------------------------------------------------
   Accessibility
   ---------------------------------------------------------------------------
   The export has no focus styles at all, on any of 38 pages. Anyone navigating
   by keyboard currently cannot see where they are.
   --------------------------------------------------------------------------- */

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

/**
 * Dark surfaces need light text.
 *
 * WordPress's has-*-background-color classes set the background only, so without
 * this a section on ink renders near-black text on a near-black panel and the
 * copy is simply invisible. Only the terracotta accent showed through, which is
 * how it was spotted.
 */
.d4-on-dark {
	color: var(--wp--preset--color--paper);
}

.d4-on-dark :where(h1, h2, h3, h4, h5, h6, p, li, dt, dd) {
	color: inherit;
}

/**
 * Terracotta on the near-black surface is 3.23:1, which passes for large text but
 * fails WCAG AA for the 12px eyebrow labels, and those sit on dark backgrounds on
 * most pages. The brand grey is 10:1 there, so eyebrows on dark use grey.
 *
 * Note this is solved with a colour that is already in the brand palette. The
 * guide says "Don't use colours not in the colour palette", so lightening the
 * terracotta to clear the ratio is not an option here even though it would work.
 */
.d4-on-dark .d4-eyebrow,
.has-ink-background-color .d4-eyebrow {
	color: var(--wp--preset--color--grey);
}

/**
 * The accent on dark stays brand terracotta inside headings, where 3.23:1 is a
 * pass because heading type is large. In body copy on dark it would fail, so
 * there it falls back to grey rather than being quietly inaccessible.
 */
.d4-on-dark :where(h1, h2, h3, h4, h5, h6) .d4-accent,
.d4-on-dark :where(h1, h2, h3, h4, h5, h6) mark.d4-accent {
	color: var(--wp--preset--color--terracotta);
}

.d4-on-dark .d4-prose .d4-accent,
.d4-on-dark .d4-prose mark.d4-accent {
	color: var(--wp--preset--color--grey);
}

/**
 * Skip to content, hidden until it is focused.
 *
 * Hidden by collapsing it to a single clipped pixel rather than by parking it at
 * left:-9999px. The old trick works, but the element is still 152px wide sitting
 * ten thousand pixels off the side of the page, which is a real element outside
 * the viewport. Our own overflow check flagged it, correctly: there is no way for
 * an automated test to tell that from a genuinely broken layout, and a rule that
 * cries wolf gets ignored. A clipped pixel has nowhere to overflow to.
 */
.d4-skip-link {
	position: absolute;
	width: 1px;
	height: 1px;
	margin: -1px;
	padding: 0;
	overflow: hidden;
	clip-path: inset(50%);
	white-space: nowrap;
	background-color: var(--wp--preset--color--ink);
	color: var(--wp--preset--color--paper);
	z-index: 1000;
}

.d4-skip-link:focus-visible,
.d4-skip-link:focus {
	position: fixed;
	top: 8px;
	left: 8px;
	width: auto;
	height: auto;
	margin: 0;
	padding: 0.75rem 1.25rem;
	overflow: visible;
	clip-path: none;
}

@media (prefers-reduced-motion: reduce) {
	*, *::before, *::after {
		animation-duration: 0.01ms !important;
		animation-iteration-count: 1 !important;
		transition-duration: 0.01ms !important;
		scroll-behavior: auto !important;
	}
}

/* overflow-x: hidden used to be here, as a backstop while the pages were being
   converted. It was removed on 5 Aug 2026, once the mobile layer landed and the
   count of elements sitting past the right edge at 390px reached zero.

   Worth knowing why it had to go rather than stay as a belt and braces. It did
   not stop the overflow, it hid it: the page reported a clean scrollWidth of
   390 while 332 elements sat outside the viewport, unreachable. Anything
   checking for horizontal scroll read green on a page that was badly broken,
   which is the same trap as the one in feedback_measure_the_thing_itself. With
   it gone, overflow shows up as a scrollbar and gets fixed instead of buried. */
html, body { max-width: 100%; }
img, video, svg, iframe, table, pre { max-width: 100%; }
