/*
Theme Name:  DTC Concepts
Theme URI:   https://dtcconcepts.com
Author:      DTC Concepts
Author URI:  https://dtcconcepts.com
Description: Custom theme for DTC Concepts. Mobile first, warm base, square photo corners, transparent overlaid nav bar.
Version:     0.1.0
Requires at least: 6.5
Requires PHP: 8.1
License:     GPL-2.0-or-later
License URI: https://www.gnu.org/licenses/gpl-2.0.html
Text Domain: dtc
Tags:        custom-menu, featured-images, translation-ready
*/

/* ==========================================================================
   0. Fonts

   Braven (Toni Studio) — display serif, H1/H2 only, self-hosted under a
   commercial webfont licence. Paths are relative to this stylesheet, so they
   resolve inside themes/dtc/.

   Until the files are present the browser simply falls back to the next
   family in --dtc-font-display; nothing breaks, headings just stay in the
   substitute serif. See assets/fonts/README.md.
   ========================================================================== */

@font-face {
	font-family: "Braven";
	src:
		url("assets/fonts/BravenRegular.woff2") format("woff2"),
		url("assets/fonts/BravenRegular.woff") format("woff");
	font-weight: 400;
	font-style: normal;
	font-display: swap;
}

/* ==========================================================================
   1. Design tokens
   Colors are fixed by DTC_Design_Spec.md §2. Do not introduce new ones
   without updating that document first.
   ========================================================================== */

:root {
	/* Brand — verified from DTC_CONCEPTS.ai. Not open to revision. */
	--dtc-taupe: #716859;

	/* House choices, per design spec §2. */
	--dtc-ink: #2c2c2c;
	--dtc-base: #f7f4f0;
	--dtc-rule: #d9d4ce;
	--dtc-white: #ffffff; /* Reserved. Use sparingly, with a reason. */

	/* Derived tints, for hover states and quiet text only. */
	--dtc-taupe-deep: #5b5347;
	--dtc-ink-muted: rgba(44, 44, 44, 0.66);
	--dtc-base-tint: #f1ece6;
	--dtc-on-taupe: #f7f4f0;
	--dtc-on-taupe-muted: rgba(247, 244, 240, 0.72);

	/* Typography. Braven leads the display stack; the serifs behind it are the
	   fallback used until the webfont files are in assets/fonts/. */
	--dtc-font-display: "Braven", "Iowan Old Style", "Palatino Linotype", Palatino, Georgia, "Times New Roman", serif;
	--dtc-font-body: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;

	/* Fluid type scale. Sized from ~390px up. */
	--dtc-text-xs: 0.75rem;
	--dtc-text-sm: 0.8125rem;
	--dtc-text-base: clamp(1rem, 0.97rem + 0.15vw, 1.0625rem);
	--dtc-text-lg: clamp(1.125rem, 1.05rem + 0.35vw, 1.3125rem);
	--dtc-text-xl: clamp(1.375rem, 1.2rem + 0.8vw, 1.75rem);
	--dtc-text-2xl: clamp(1.75rem, 1.4rem + 1.6vw, 2.5rem);
	--dtc-text-3xl: clamp(2.25rem, 1.7rem + 2.6vw, 3.5rem);

	--dtc-tracking-wide: 0.14em;
	--dtc-leading-tight: 1.14;
	--dtc-leading-body: 1.6;

	/* Spacing scale. */
	--dtc-space-2xs: 0.375rem;
	--dtc-space-xs: 0.75rem;
	--dtc-space-sm: 1.25rem;
	--dtc-space-md: 2rem;
	--dtc-space-lg: 3.25rem;
	--dtc-space-xl: clamp(4rem, 3rem + 5vw, 7rem);

	/* Layout. */
	--dtc-container: 78rem;
	--dtc-container-narrow: 44rem;
	--dtc-gutter: clamp(1.25rem, 5vw, 3rem);
	--dtc-header-height: 4.25rem;
	/* Full border-box height of the bar, including its hairline. */
	--dtc-header-box: calc(var(--dtc-header-height) + 1px);

	/* Square photo corners are a settled decision. Rounding is reserved for
	   buttons and form fields only. */
	--dtc-radius-control: 2px;

	--dtc-transition: 200ms ease;
}

@media (min-width: 48em) {
	:root {
		--dtc-header-height: 5.25rem;
	}
}

/* ==========================================================================
   2. Reset & base
   ========================================================================== */

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

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

body {
	margin: 0;
	background-color: var(--dtc-base);
	color: var(--dtc-ink);
	font-family: var(--dtc-font-body);
	font-size: var(--dtc-text-base);
	line-height: var(--dtc-leading-body);
	-webkit-font-smoothing: antialiased;
	-moz-osx-font-smoothing: grayscale;
	overflow-wrap: break-word;
}

h1,
h2,
h3,
h4,
h5,
h6 {
	margin: 0 0 var(--dtc-space-sm);
	font-weight: 400;
	line-height: var(--dtc-leading-tight);
	text-wrap: balance;
}

h1,
h2 {
	font-family: var(--dtc-font-display);
	letter-spacing: -0.01em;
}

h1 {
	font-size: var(--dtc-text-3xl);
}

h2 {
	font-size: var(--dtc-text-2xl);
}

h3 {
	font-size: var(--dtc-text-xl);
}

h4,
h5,
h6 {
	font-size: var(--dtc-text-lg);
}

p,
ul,
ol,
figure,
blockquote,
pre,
table {
	margin: 0 0 var(--dtc-space-sm);
}

:where(p, li):last-child {
	margin-bottom: 0;
}

a {
	color: var(--dtc-taupe);
	text-decoration-thickness: 1px;
	text-underline-offset: 0.2em;
	transition: color var(--dtc-transition);
}

a:hover,
a:focus-visible {
	color: var(--dtc-taupe-deep);
}

/* Square corners on all imagery — settled decision #7. */
img,
picture,
video,
canvas,
svg,
iframe {
	display: block;
	max-width: 100%;
	height: auto;
	border-radius: 0;
}

figure > img {
	width: 100%;
}

figcaption {
	margin-top: var(--dtc-space-2xs);
	color: var(--dtc-ink-muted);
	font-size: var(--dtc-text-sm);
}

hr {
	height: 0;
	margin: var(--dtc-space-lg) 0;
	border: 0;
	border-top: 1px solid var(--dtc-rule);
}

:focus-visible {
	outline: 2px solid var(--dtc-taupe);
	outline-offset: 3px;
}

/* ==========================================================================
   3. Layout helpers
   ========================================================================== */

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

.dtc-container--narrow {
	max-width: var(--dtc-container-narrow);
}

.dtc-section {
	padding-block: var(--dtc-space-xl);
}

.dtc-eyebrow {
	display: block;
	margin-bottom: var(--dtc-space-xs);
	color: var(--dtc-taupe);
	font-size: var(--dtc-text-xs);
	font-weight: 500;
	letter-spacing: var(--dtc-tracking-wide);
	text-transform: uppercase;
}

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

.skip-link {
	position: absolute;
	top: -100vh;
	left: var(--dtc-space-sm);
	z-index: 200;
	padding: var(--dtc-space-xs) var(--dtc-space-sm);
	background-color: var(--dtc-taupe);
	color: var(--dtc-on-taupe);
	border-radius: var(--dtc-radius-control);
	text-decoration: none;
}

.skip-link:focus {
	top: var(--dtc-space-sm);
	color: var(--dtc-on-taupe);
}

/* ==========================================================================
   4. Buttons & form fields
   The only elements permitted rounded corners.
   ========================================================================== */

.dtc-button,
button,
input[type="submit"],
input[type="button"] {
	display: inline-block;
	padding: 0.85em 1.6em;
	border: 1px solid var(--dtc-taupe);
	border-radius: var(--dtc-radius-control);
	background-color: var(--dtc-taupe);
	color: var(--dtc-on-taupe);
	font-family: inherit;
	font-size: var(--dtc-text-sm);
	letter-spacing: var(--dtc-tracking-wide);
	text-transform: uppercase;
	text-decoration: none;
	cursor: pointer;
	transition: background-color var(--dtc-transition), border-color var(--dtc-transition), color var(--dtc-transition);
}

.dtc-button:hover,
.dtc-button:focus-visible,
button:hover,
input[type="submit"]:hover {
	background-color: var(--dtc-taupe-deep);
	border-color: var(--dtc-taupe-deep);
	color: var(--dtc-on-taupe);
}

.dtc-button--ghost {
	background-color: transparent;
	color: var(--dtc-taupe);
}

.dtc-button--ghost:hover,
.dtc-button--ghost:focus-visible {
	background-color: var(--dtc-taupe);
	color: var(--dtc-on-taupe);
}

input[type="text"],
input[type="email"],
input[type="url"],
input[type="tel"],
input[type="search"],
input[type="number"],
textarea,
select {
	width: 100%;
	padding: 0.75em 0.9em;
	border: 1px solid var(--dtc-rule);
	border-radius: var(--dtc-radius-control);
	background-color: var(--dtc-white);
	color: var(--dtc-ink);
	font-family: inherit;
	font-size: var(--dtc-text-base);
}

input:focus,
textarea:focus,
select:focus {
	border-color: var(--dtc-taupe);
}

/* ==========================================================================
   5. Site header — thin, transparent, overlaid, never a white strip.
   Design spec §3. It does not hide, stick, or animate in on scroll.
   ========================================================================== */

/*
 * Sticky, not fixed. Sticky keeps the bar in normal flow, which means content
 * never jumps when it starts sticking and no body offset has to be
 * compensated. It also fixes the admin bar at narrow widths: below 600px
 * WordPress switches its own bar to position:absolute so it scrolls away, and
 * an in-flow header simply starts beneath it and then sticks to 0 once it has
 * gone — a fixed header would either sit under it or leave a 46px gap.
 *
 * The bar never hides, reveals or animates on scroll. The only thing that
 * changes is its background, and that change is instant — no transition here
 * on purpose.
 */
.site-header {
	position: sticky;
	top: 0;
	z-index: 100;
	border-bottom: 1px solid var(--dtc-rule); /* The Eye Swoon hairline. */
	background-color: var(--dtc-base);
}

.site-header__inner {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: var(--dtc-space-sm);
	min-height: var(--dtc-header-height);
}

/* Brand */

.site-header__brand {
	display: flex;
	align-items: center;
	margin: 0;
	font-size: inherit;
	line-height: 1;
}

.site-header__brand a {
	display: inline-flex;
	align-items: center;
	color: var(--dtc-taupe);
	text-decoration: none;
}

/*
 * The wordmark is 96.9% ink vertically, so height maps almost 1:1 to optical
 * size — but it carries ~11% transparent padding on each side, so it always
 * reads smaller than its box. It sits at roughly 70% of the bar height, which
 * is enough for the hairline strokes and the CONCEPTS line to hold up while
 * leaving the bar itself thin.
 */
.site-header__brand img {
	width: auto;
	max-height: 3rem;
}

/* Both colourways ship on overlay pages; CSS picks one. Swapping display
   rather than src makes the change at the hero boundary instant. */
.custom-logo--dark {
	display: none;
}

.site-header--over-media.is-past-hero .custom-logo--light {
	display: none;
}

.site-header--over-media.is-past-hero .custom-logo--dark {
	display: block;
}

@media (min-width: 48em) {
	.site-header__brand img {
		max-height: 3.75rem;
	}
}

.site-header__wordmark {
	font-family: var(--dtc-font-display);
	font-size: 1.0625rem;
	letter-spacing: var(--dtc-tracking-wide);
	text-transform: uppercase;
}

@media (min-width: 48em) {
	.site-header__wordmark {
		font-size: 1.25rem;
	}
}

/* Toggle — mobile only, per design spec §3. Nav only, never content. */

.nav-toggle {
	display: inline-flex;
	align-items: center;
	gap: 0.55em;
	/* 44px minimum so it is a comfortable thumb target, not just a visible one. */
	min-height: 2.75rem;
	padding: 0.5em 0;
	border: 0;
	background: none;
	color: var(--dtc-taupe);
	font-size: var(--dtc-text-xs);
	letter-spacing: var(--dtc-tracking-wide);
	text-transform: uppercase;
}

.nav-toggle:hover,
.nav-toggle:focus-visible {
	background: none;
	color: var(--dtc-taupe-deep);
}

.nav-toggle__bars {
	position: relative;
	display: block;
	width: 20px;
	height: 10px;
	border-top: 1.5px solid currentColor;
	border-bottom: 1.5px solid currentColor;
}

.site-header.is-nav-open .nav-toggle__bars {
	height: 0;
	border-bottom-color: transparent;
	transform: rotate(45deg);
}

.site-header.is-nav-open .nav-toggle__bars::after {
	position: absolute;
	inset: -1.5px 0 auto 0;
	content: "";
	border-top: 1.5px solid currentColor;
	transform: rotate(-90deg);
}

/* Navigation */

.site-nav ul {
	display: flex;
	margin: 0;
	padding: 0;
	list-style: none;
}

.site-nav a {
	display: block;
	color: var(--dtc-ink);
	font-size: var(--dtc-text-sm);
	letter-spacing: var(--dtc-tracking-wide);
	text-transform: uppercase;
	text-decoration: none;
}

.site-nav a:hover,
.site-nav a:focus-visible,
.site-nav .current-menu-item > a,
.site-nav .current-menu-ancestor > a,
.site-nav .current_page_parent > a {
	color: var(--dtc-taupe);
}

/* Mobile is the base state: the panel drops beneath the bar on a warm
   surface, never stark white. Written mobile-first rather than as a
   max-width query so there is no width — not even a sub-pixel one — where
   neither branch applies and the menu becomes unreachable. */

.site-nav {
	position: absolute;
	top: 100%;
	right: 0;
	left: 0;
	display: none;
	background-color: var(--dtc-base);
	border-bottom: 1px solid var(--dtc-rule);
}

.site-header.is-nav-open .site-nav {
	display: block;
}

.site-nav ul {
	flex-direction: column;
	padding: var(--dtc-space-xs) var(--dtc-gutter) var(--dtc-space-sm);
}

.site-nav li + li {
	border-top: 1px solid var(--dtc-rule);
}

.site-nav a {
	padding-block: var(--dtc-space-xs);
}

/*
 * Nav colour is carried by a custom property set on the bar itself rather than
 * by descendant selectors. Both states then differ only by a compound on one
 * element (.site-header--over-media vs .site-header--over-media.is-past-hero),
 * which is a clean specificity step instead of a race between two long
 * descendant chains.
 */
.site-nav a {
	color: var(--dtc-nav-colour, var(--dtc-ink));
	text-shadow: var(--dtc-nav-shadow, none);
}

/* Desktop: everything visible, no toggle. */
@media (min-width: 48em) {
	.nav-toggle {
		display: none;
	}

	.site-nav {
		position: static;
		display: block;
		background-color: transparent;
		border-bottom: 0;
	}

	.site-nav ul {
		flex-direction: row;
		align-items: center;
		gap: clamp(1.25rem, 2.5vw, 2.25rem);
		padding: 0;
	}

	.site-nav li + li {
		border-top: 0;
	}

	.site-nav a {
		padding-block: 0;
	}

	/* Desktop nav sits directly on the photograph, so it goes cream. Below the
	   breakpoint the links live in the solid drop-down panel and stay ink. */
	.site-header--over-media {
		--dtc-nav-colour: var(--dtc-on-taupe);
		--dtc-nav-shadow: 0 1px 12px rgba(0, 0, 0, 0.35);
	}

	/* Back to ink once the bar has its own background. */
	.site-header--over-media.is-past-hero {
		--dtc-nav-colour: var(--dtc-ink);
		--dtc-nav-shadow: none;
	}
}

/* Overlaid on photography: the bar sits on top of the hero rather than
   above it, and switches to light text. Applied by adding
   `site-header--over-media` in a template. Used from the homepage on. */

/*
 * Overlay pages pull their hero up underneath the bar (see the body rule
 * below), so the bar itself only has to go transparent.
 */
.site-header--over-media {
	background-color: transparent;
	border-bottom-color: rgba(247, 244, 240, 0.4);
}

/*
 * Past the hero the bar takes the page background so the nav and the mark stay
 * legible over body content, and the mark switches to its taupe colourway —
 * cream on cream would vanish. No transition: the spec is emphatic that the
 * bar never animates on scroll.
 */
.site-header--over-media.is-past-hero {
	background-color: var(--dtc-base);
	border-bottom-color: var(--dtc-rule);
}

.site-header--over-media.is-past-hero::before {
	display: none;
}

.site-header--over-media.is-past-hero .site-header__brand a,
.site-header--over-media.is-past-hero .nav-toggle {
	color: var(--dtc-taupe);
	text-shadow: none;
}

.site-header--over-media.is-past-hero .nav-toggle__bars {
	filter: none;
}

/*
 * The admin bar sits above the sticky header, so the header has to stop short
 * of it. WordPress runs three regimes: 32px fixed above 782px, 46px fixed down
 * to 601px, and below that it turns absolute and scrolls away — at which point
 * the header should stick flush to 0. Logged-out visitors have no .admin-bar
 * class and are unaffected.
 */
.admin-bar .site-header {
	top: 32px;
}

@media screen and (max-width: 782px) {
	.admin-bar .site-header {
		top: 46px;
	}
}

@media screen and (max-width: 600px) {
	.admin-bar .site-header {
		top: 0;
	}
}

/*
 * Overlay pages: the hero slides up under the bar by exactly the bar's height,
 * so the photograph still starts at the top of the page while the header stays
 * in flow. Negative margin rather than a fixed header is what keeps the rest of
 * the document from shifting.
 */
.dtc-hero-overlay .site-main {
	margin-top: calc(-1 * var(--dtc-header-box));
}

/* Interior photography is bright — pale marble, white cabinetry, blown-out
   windows. Cream type laid straight onto it drops to roughly 2:1 contrast and
   the menu becomes impossible to find. This soft gradient guarantees the bar
   reads on any photo while staying a long way from the solid white strip the
   design spec rules out. */
.site-header--over-media::before {
	position: absolute;
	inset: 0 0 auto 0;
	z-index: -1;
	height: calc(var(--dtc-header-height) + 3rem);
	content: "";
	background-image: linear-gradient(
		to bottom,
		rgba(28, 26, 24, 0.58),
		rgba(28, 26, 24, 0)
	);
	pointer-events: none;
}

.site-header--over-media .site-header__brand a,
.site-header--over-media .nav-toggle {
	color: var(--dtc-on-taupe);
	text-shadow: 0 1px 12px rgba(0, 0, 0, 0.35);
}

/* The hamburger is drawn with borders, which text-shadow does not reach. */
.site-header--over-media .nav-toggle__bars {
	filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.5));
}

.site-header--over-media:not(.is-past-hero) .site-nav a:hover,
.site-header--over-media:not(.is-past-hero) .site-nav a:focus-visible {
	color: var(--dtc-white);
}

/* ==========================================================================
   6. Main content (placeholder styling until templates are built)
   ========================================================================== */

.site-main {
	min-height: 50vh;
}

.entry-header {
	margin-bottom: var(--dtc-space-md);
}

.entry-meta {
	color: var(--dtc-ink-muted);
	font-size: var(--dtc-text-sm);
	letter-spacing: 0.04em;
}

.entry-content > * + * {
	margin-top: var(--dtc-space-sm);
}

.entry-content a {
	text-decoration: underline;
}

.post-list > li + li {
	margin-top: var(--dtc-space-lg);
	padding-top: var(--dtc-space-lg);
	border-top: 1px solid var(--dtc-rule);
}

.post-list {
	padding: 0;
	list-style: none;
}

/* ==========================================================================
   6b. Placeholders
   Stand-ins for content that has not been supplied yet. Deliberately obvious.
   Each one disappears by itself once the real photo or copy is added, so
   there is nothing to remember to strip out before launch.
   ========================================================================== */

.dtc-placeholder {
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	gap: var(--dtc-space-2xs);
	width: 100%;
	height: 100%;
	min-height: 11rem;
	padding: var(--dtc-space-sm);
	background-color: var(--dtc-base-tint);
	background-image: repeating-linear-gradient(
		45deg,
		transparent 0 12px,
		rgba(113, 104, 89, 0.055) 12px 24px
	);
	border: 1px dashed rgba(113, 104, 89, 0.35);
	color: var(--dtc-taupe);
	text-align: center;
}

.dtc-placeholder__label {
	font-size: var(--dtc-text-sm);
	letter-spacing: var(--dtc-tracking-wide);
	text-transform: uppercase;
}

.dtc-placeholder__note {
	font-size: var(--dtc-text-xs);
	letter-spacing: 0.1em;
	text-transform: uppercase;
	opacity: 0.55;
}

.dtc-badge {
	display: inline-block;
	margin-top: var(--dtc-space-xs);
	padding: 0.3em 0.7em;
	border: 1px dashed rgba(113, 104, 89, 0.5);
	border-radius: var(--dtc-radius-control);
	background-color: var(--dtc-base-tint);
	color: var(--dtc-taupe);
	font-size: 0.6875rem;
	letter-spacing: 0.1em;
	line-height: 1.4;
	text-transform: uppercase;
}

.dtc-badge--block {
	display: block;
	margin-inline: auto;
	max-width: 40ch;
}

/* ==========================================================================
   6c. Homepage — scroll order fixed by design spec §4
   ========================================================================== */

/* --- 1. Hero. The work leads. ------------------------------------------- */

/*
 * Hero framing.
 *
 * --dtc-hero-fx / --dtc-hero-fy are the focal point as fractions (0-1), set
 * from the Customizer on the section element. They are what makes this survive
 * a photo swap: point them at the new subject and both breakpoints re-frame.
 *
 * The two breakpoints need different mechanisms because the geometry differs:
 *
 *   Mobile — the box is far taller than the photo, so object-fit:cover already
 *   overflows horizontally and object-position does the panning.
 *
 *   Desktop — the box is wider than the photo's 3:2, so cover crops vertically
 *   and the full width is on screen. object-position has nothing to pan. To
 *   centre an off-centre subject the photo has to be zoomed slightly and
 *   shifted, which is what --dtc-hero-zoom and the derived translate do.
 *
 * The zoom must be at least 1 / (1 - 2|fx - 0.5|) or the shift would drag an
 * empty edge into frame. At fx 0.59 that floor is 1.22, so 1.24 leaves a little margin.
 */
.home-hero {
	--dtc-hero-fx: 0.5;
	--dtc-hero-fy: 0.5;
	--dtc-hero-zoom: 1;
	--dtc-hero-pan-on: 0;

	position: relative;
	/* Every child is absolutely positioned, so the height comes entirely from
	   the ratio and the min/max below. */
	/* Mobile: a fixed tall ratio rather than a viewport fraction, so the crop
	   loosens and substantially more of the room stays in frame.
	   width must be definite for the same reason as the desktop rule below:
	   with an auto width, max-height clamping the height makes aspect-ratio
	   solve back for width and the hero stops being full-bleed. That bites on
	   short windows and in landscape — measured 392px wide in a 500px
	   viewport before this. */
	width: 100%;
	aspect-ratio: 4 / 5;
	min-height: 26rem;
	max-height: 80svh;
	overflow: hidden;
	background-color: var(--dtc-taupe);
}

/*
 * Desktop height is driven by width, not by the viewport.
 *
 * object-fit: cover behaves in two regimes. While the box is wider than the
 * photo's 3:2 it crops vertically only: the whole image width maps onto the
 * box and the zoom then takes a constant 1/1.24 of it, so the horizontal
 * composition is identical at every width. Once the box goes taller than 3:2
 * it starts cropping horizontally as well, and object-position and the pan
 * compound — which is what cropped the sconces out.
 *
 * The old min-height: 88svh made the regime depend on the visitor's window
 * height, so the same width framed differently on different screens. Holding
 * the box at 3:2 or wider keeps it in the first regime always. max-height only
 * ever makes it wider still, so it is safe; min-height would make it taller,
 * so it is set below where it can bite inside this breakpoint.
 *
 * The trade is that the hero gets shorter at narrow desktop widths.
 */
@media (min-width: 48em) {
	.home-hero {
		--dtc-hero-zoom: 1.24;
		--dtc-hero-pan-on: 1;

		/* width must be definite. With an auto width, max-height clamping the
		   height makes aspect-ratio solve back for width instead, and the hero
		   stops being full-bleed — at 1920 it collapsed to 1281px. */
		width: 100%;
		aspect-ratio: 3 / 2;
		/* Tall enough to hold the bar plus the positioning line plus its
		   bottom padding. At 22rem the line rode up through the wordmark on
		   short windows. Still below the 3:2 height at this breakpoint
		   (768 / 1.5 = 512), so the box never leaves the safe crop regime. */
		min-height: 28rem;
		max-height: min(86vh, 54rem);
		max-height: min(86svh, 54rem);
	}
}

.home-hero__media {
	position: absolute;
	inset: 0;
}

.home-hero__media img,
.home-hero__media .dtc-placeholder {
	width: 100%;
	height: 100%;
	object-fit: cover;
	object-position: calc(var(--dtc-hero-fx) * 100%) calc(var(--dtc-hero-fy) * 100%);
}

/*
 * Ken Burns drift — pure CSS, most of the perceived polish for near-zero cost
 * (design spec §6). Switched off entirely under reduced motion.
 *
 * The drift is vertical only. A horizontal component would walk the subject
 * off-centre and break the symmetry the framing is set up for.
 *
 * translateX is divided by the zoom because scale is applied after it, so the
 * scale would otherwise multiply the shift.
 */
@keyframes dtc-kenburns {
	from {
		transform:
			scale(var(--dtc-hero-zoom))
			translate3d(var(--dtc-hero-pan), 0, 0);
	}

	to {
		transform:
			scale(calc(var(--dtc-hero-zoom) * 1.07))
			translate3d(var(--dtc-hero-pan), -1.2%, 0);
	}
}

.home-hero__image {
	--dtc-hero-pan: calc(
		(0.5 - var(--dtc-hero-fx)) / var(--dtc-hero-zoom) * 100% * var(--dtc-hero-pan-on)
	);

	transform: scale(var(--dtc-hero-zoom)) translate3d(var(--dtc-hero-pan), 0, 0);
	animation: dtc-kenburns 30s ease-in-out infinite alternate;
	will-change: transform;
}

.home-hero__scrim {
	position: absolute;
	inset: 0;
	/*
	 * The bar carries its own gradient, so this only needs a light touch at the
	 * top. The weight is at the bottom, for the positioning line.
	 *
	 * Now that the hero height varies with width, the line can start as high as
	 * 37% of the hero on a short wide window and as low as 60% on a tall one.
	 * A single late ramp left the top of the line unprotected at the short end,
	 * so the gradient builds gradually from ~24% and is stepped rather than
	 * linear — that keeps the upper third of the photograph clean while still
	 * carrying contrast right through the text band.
	 */
	background-image: linear-gradient(
		to bottom,
		rgba(44, 44, 44, 0.16) 0%,
		rgba(44, 44, 44, 0.05) 16%,
		rgba(44, 44, 44, 0.02) 24%,
		rgba(44, 44, 44, 0.18) 40%,
		rgba(44, 44, 44, 0.38) 60%,
		rgba(44, 44, 44, 0.56) 80%,
		rgba(44, 44, 44, 0.7) 100%
	);
}

/*
 * Safe area for the hero text: inset below the bar and above the bottom edge,
 * and held to the site container width so the line starts on the same left
 * edge as every other section on the page.
 *
 * The text block is positioned inside this box rather than against the hero
 * itself, which is what keeps any placement inside the gutters.
 */
.home-hero__content {
	position: absolute;
	/* The bar's box is its min-height plus its 1px hairline, so the extra step
	   clears the rule rather than sitting on it when the text is set to 0.
	   Costs nothing at the default, where the block is anchored from the
	   bottom and the top inset never comes into play. */
	top: calc(var(--dtc-header-height) + var(--dtc-space-2xs));
	right: var(--dtc-gutter);
	/* Lower-left: the headline sits 8% of the hero's height up from its bottom
	   edge. A percentage rather than a fixed step so it holds its proportion as
	   the hero height changes with width. */
	bottom: 8%;
	left: var(--dtc-gutter);
	max-width: calc(var(--dtc-container) - 2 * var(--dtc-gutter));
	margin-inline: auto;
}

/*
 * Mobile: fixed bottom-left, ignoring the Customizer positions on purpose.
 * The scrim is weighted to the bottom, so on a tall crop that is the only
 * placement where the line is reliably readable. Because the desktop rule
 * lives in a min-width query, no clamping is needed — the values simply have
 * no route into this breakpoint.
 */
.home-hero__text {
	position: absolute;
	bottom: 0;
	left: 0;
	width: max-content;
	max-width: 100%;
}

/*
 * Desktop: placed by the Customizer percentages. The translate cancels the
 * element's own size, so 0 puts its leading edge at the start of the safe
 * area, 100 puts its trailing edge at the end, and 50 centres it. Because the
 * block is never wider or taller than the safe area, every value from 0 to 100
 * lands fully inside it.
 */
@media (min-width: 48em) {
	.home-hero__text {
		top: var(--dtc-hero-ty, 100%);
		right: auto;
		bottom: auto;
		left: var(--dtc-hero-tx, 0%);
		transform: translate(
			calc(-1 * var(--dtc-hero-tx, 0%)),
			calc(-1 * var(--dtc-hero-ty, 100%))
		);
	}
}

.home-hero__line {
	max-width: 20ch;
	margin: 0;
	color: var(--dtc-base);
	font-family: var(--dtc-font-display);
	font-size: var(--dtc-text-2xl);
	line-height: 1.16;
	text-shadow: 0 1px 24px rgba(0, 0, 0, 0.4);
	text-wrap: balance;
}

@media (min-width: 48em) {
	.home-hero__line {
		max-width: 26ch;
		font-size: var(--dtc-text-3xl);
	}
}

/* --- 2. Dan / the firm. ---------------------------------------------------
   .dtc-split is the shared image-beside-copy block, also used by the About
   page, so the two cannot drift apart. */

.dtc-split {
	display: grid;
	gap: var(--dtc-space-md);
}

@media (min-width: 48em) {
	.dtc-split {
		grid-template-columns: minmax(0, 5fr) minmax(0, 6fr);
		gap: var(--dtc-space-lg);
		align-items: center;
	}

	/* Quieter variant: a narrower photo column, for a section that follows
	   another of the same shape and should not compete with it. The narrower
	   column is what makes it quieter — it inherits the centred alignment,
	   because top-aligning left a block of dead space under the copy whenever
	   it was shorter than the photo. */
	.dtc-split--quiet {
		grid-template-columns: minmax(0, 3fr) minmax(0, 7fr);
	}
}

.dtc-split__media {
	margin: 0;
}

.dtc-split__media img {
	width: 100%;
}

.dtc-split__media .dtc-placeholder {
	aspect-ratio: 4 / 5;
}

.home-intro__heading {
	font-size: var(--dtc-text-2xl);
}

/* --- 3. Selected work. ----------------------------------------------------
   The grid and card below are shared with the /projects/ index, so the two
   project indexes cannot drift apart. */

/* Intro and work sit on the same page background, so their section padding
   would otherwise stack into an oversized dead gap. */
.home-work {
	padding-top: 0;
}

/* Same problem when the proof section is switched off: work and services fall
   next to each other on the same background. The adjacent-sibling selector
   only matches when proof renders nothing, so this corrects itself either
   way and needs no attention when the section comes back. */
.home-work + .home-services {
	padding-top: 0;
}

.home-work__header {
	margin-bottom: var(--dtc-space-md);
}

.home-work__header h2 {
	margin-bottom: 0;
}

.project-grid {
	display: grid;
	gap: var(--dtc-space-md);
	margin: 0;
	padding: 0;
	list-style: none;
}

@media (min-width: 37.5em) {
	.project-grid {
		grid-template-columns: repeat(2, minmax(0, 1fr));
	}
}

@media (min-width: 64em) {
	.project-grid {
		grid-template-columns: repeat(3, minmax(0, 1fr));
		column-gap: var(--dtc-space-sm);
		row-gap: var(--dtc-space-lg);
	}
}

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

/* Uniform crop across the grid — mixed ratios here read as disorganized
   (design spec §5). Native ratios belong inside a project page. */
.project-card__media {
	display: block;
	overflow: hidden;
	background-color: var(--dtc-base-tint);
}

.project-card__media img,
.project-card__media .dtc-placeholder {
	width: 100%;
	aspect-ratio: 4 / 5;
	object-fit: cover;
}

.project-card__image {
	transition: transform 700ms cubic-bezier(0.22, 1, 0.36, 1);
}

.project-card__link:hover .project-card__image,
.project-card__link:focus-visible .project-card__image {
	transform: scale(1.03);
}

.project-card__caption {
	display: block;
	margin-top: var(--dtc-space-xs);
}

.project-card__name {
	display: block;
	font-family: var(--dtc-font-display);
	font-size: var(--dtc-text-lg);
	line-height: 1.25;
}

.project-card__location {
	display: block;
	margin-top: 0.25em;
	color: var(--dtc-ink-muted);
	font-size: var(--dtc-text-xs);
	letter-spacing: var(--dtc-tracking-wide);
	text-transform: uppercase;
}

.project-card--placeholder .project-card__name {
	color: var(--dtc-ink-muted);
}

.home-work__note {
	margin-top: var(--dtc-space-md);
	color: var(--dtc-ink-muted);
	font-size: var(--dtc-text-sm);
	font-style: italic;
}

/* --- Project index (/projects/) -------------------------------------------
   Reuses the grid and card above; only the page heading and the pagination
   are specific to this template. */

.project-archive__header {
	margin-bottom: var(--dtc-space-lg);
}

/* Sized like a section heading rather than a project title — this is a label
   for the page, not the headline of a piece of work. */
.project-archive__title {
	margin-bottom: 0;
	font-size: var(--dtc-text-2xl);
}

.project-archive__empty {
	color: var(--dtc-ink-muted);
}

/* Only appears once there are more projects than fit a page. */
.pagination {
	margin-top: var(--dtc-space-xl);
	padding-top: var(--dtc-space-md);
	border-top: 1px solid var(--dtc-rule);
}

.pagination .nav-links {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: var(--dtc-space-sm);
}

.pagination .page-numbers {
	color: var(--dtc-ink-muted);
	font-size: var(--dtc-text-xs);
	letter-spacing: var(--dtc-tracking-wide);
	text-transform: uppercase;
	text-decoration: none;
}

.pagination .page-numbers:hover,
.pagination .page-numbers:focus-visible,
.pagination .page-numbers.current {
	color: var(--dtc-taupe);
}

/* --- 4. Proof. Complete with only the partnership in it. ------------------ */

.home-proof {
	background-color: var(--dtc-base-tint);
}

.home-proof__inner {
	text-align: center;
}

.home-proof__statement {
	margin: 0;
	font-family: var(--dtc-font-display);
	font-size: var(--dtc-text-xl);
	line-height: 1.4;
	text-wrap: balance;
}

.home-proof__quote {
	margin: var(--dtc-space-lg) 0 0;
	padding-top: var(--dtc-space-lg);
	border-top: 1px solid var(--dtc-rule);
}

.home-proof__quote blockquote {
	margin: 0;
	font-family: var(--dtc-font-display);
	font-size: var(--dtc-text-xl);
	line-height: 1.4;
	text-wrap: balance;
}

.home-proof__attrib {
	margin-top: var(--dtc-space-sm);
	color: var(--dtc-ink-muted);
	font-size: var(--dtc-text-xs);
	letter-spacing: var(--dtc-tracking-wide);
	text-transform: uppercase;
}

/* --- 5. Services. No prices, ever. ---------------------------------------
   .dtc-offerings is shared with the Services page so the two cannot drift. */

.home-services__header {
	margin-bottom: var(--dtc-space-md);
}

.home-services__header h2 {
	margin-bottom: 0;
}

.dtc-offerings {
	display: grid;
	gap: var(--dtc-space-lg);
}

@media (min-width: 48em) {
	.dtc-offerings {
		grid-template-columns: repeat(2, minmax(0, 1fr));
		gap: var(--dtc-space-lg) var(--dtc-space-xl);
	}
}

.service {
	padding-top: var(--dtc-space-sm);
	border-top: 1px solid var(--dtc-rule);
}

.service__title {
	font-family: var(--dtc-font-display);
	font-size: var(--dtc-text-xl);
	margin-bottom: var(--dtc-space-xs);
}

/* An optional line under the title, carrying where the work happens. It is
   the project card's location caption exactly — same size, colour, tracking
   and case — because it is doing the same job: a quiet fact beneath a name.
   Both the homepage section and the Services page use it, from the same pair
   of fields, which is why there is one rule here and not two. */
.service__subtitle {
	margin: 0 0 var(--dtc-space-xs);
	color: var(--dtc-ink-muted);
	font-size: var(--dtc-text-xs);
	letter-spacing: var(--dtc-tracking-wide);
	text-transform: uppercase;
}

/* With a subtitle beneath it the title gives up its gap, so the two read as
   one stacked label rather than two separate lines. */
.service__title:has(+ .service__subtitle) {
	margin-bottom: 0.25em;
}

/* Centred, so the ask belongs to both offerings. Left-aligned it sat under
   the first column and read as that column's button. */
.home-services__action {
	margin-top: var(--dtc-space-lg);
	text-align: center;
}

/* --- 6. Lead capture. ---------------------------------------------------- */

.home-capture {
	background-color: var(--dtc-base-tint);
	border-top: 1px solid var(--dtc-rule);
	/* Pulled in above the headline so the band reads as one connected block
	   rather than a heading floating in space. */
	padding-top: calc(var(--dtc-space-xl) - 1.5rem);
}

.home-capture__inner {
	text-align: center;
}

.home-capture__heading {
	font-size: var(--dtc-text-2xl);
}

.home-capture__text {
	max-width: 52ch;
	margin-inline: auto;
}

.home-capture__form {
	margin-top: var(--dtc-space-sm);
}

/*
 * Mailchimp form.
 *
 * Restyled here rather than in the plugin's form editor on purpose: that
 * markup lives in the database, so it would not travel with the theme or
 * survive a deploy. Everything below works against MC4WP's default output.
 *
 * That output is <p><label>Email address: <input></label></p><p><input
 * submit></p> — note the field is *inside* its label, so the label cannot
 * simply be hidden without taking the field with it.
 */

.home-capture__form .mc4wp-form-fields {
	max-width: 30rem;
	margin-inline: auto;
}

.home-capture__form .mc4wp-form-fields p {
	margin: 0;
	text-align: center;
}

.home-capture__form .mc4wp-form-fields p + p {
	margin-top: var(--dtc-space-xs);
}

/*
 * Collapse the label's own type to drop the "Email address:" text while
 * keeping the field and its association intact — the placeholder carries the
 * prompt. The input restores its own size below. Scoped to the fields wrapper
 * so it cannot reach MC4WP's honeypot label, which sits outside it.
 */
.home-capture__form .mc4wp-form-fields label {
	display: block;
	font-size: 0;
	line-height: 0;
}

.home-capture__form .mc4wp-form-fields input[type="email"] {
	width: 100%;
	padding: 0.95em 1em;
	border: 1px solid var(--dtc-rule);
	border-radius: var(--dtc-radius-control);
	background-color: transparent;
	color: var(--dtc-ink);
	font-family: inherit;
	font-size: var(--dtc-text-base);
	line-height: 1.4;
	text-align: center;
}

.home-capture__form .mc4wp-form-fields input[type="email"]::placeholder {
	color: var(--dtc-ink-muted);
	opacity: 1;
}

.home-capture__form .mc4wp-form-fields input[type="email"]:focus {
	border-color: var(--dtc-taupe);
	outline: none;
}

/*
 * Auto width, centred under the field by the parent's text-align — the submit
 * is inline-block, so nothing further is needed to centre it.
 *
 * Filled rather than outlined. The ghost treatment was tried here and read too
 * weak: sitting directly beneath an outlined input, the two boxes carry the
 * same visual weight and the hierarchy flattens, so the eye never lands on the
 * action. Ghost works for HOW IT WORKS because that is a secondary link on the
 * plain background with nothing competing beside it. This is the only email
 * capture on the page and should announce itself.
 */
.home-capture__form .mc4wp-form-fields input[type="submit"] {
	width: auto;
	padding: 0.95em 2.75em;
	letter-spacing: var(--dtc-tracking-wide);
}

/* Subscription confirmations and errors, kept quiet. */
.home-capture__form .mc4wp-response {
	margin-top: var(--dtc-space-xs);
	color: var(--dtc-ink-muted);
	font-size: var(--dtc-text-sm);
}

.home-capture__form .mc4wp-response p {
	margin: 0;
}

/* Fallback form shown when no Mailchimp form exists yet. */
.home-capture__fields {
	display: grid;
	gap: var(--dtc-space-xs);
	max-width: 30rem;
	margin-inline: auto;
	text-align: left;
}

@media (min-width: 30em) {
	.home-capture__fields {
		grid-template-columns: minmax(0, 1fr) auto;
		align-items: start;
	}
}

.home-capture__form input[disabled],
.home-capture__form button[disabled] {
	opacity: 0.6;
	cursor: not-allowed;
}

/* --- Fade-up on scroll (design spec §6). --------------------------------- */

/* The hidden state only applies when JavaScript is confirmed running, so a
   script failure leaves every section visible rather than blank. */
.js .dtc-reveal {
	opacity: 0;
	transform: translateY(1.5rem);
	transition:
		opacity 800ms cubic-bezier(0.22, 1, 0.36, 1),
		transform 800ms cubic-bezier(0.22, 1, 0.36, 1);
}

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

/* ==========================================================================
   6d. Single project page — design spec §7
   ========================================================================== */

/* Opening photo. Full-bleed, cropped tall on mobile so it reads as an image
   first rather than a banner. */

/* Single-cell grid so the photo and the scrim stack and both stretch to the
   full height. A percentage height cannot resolve against a parent that only
   has min-height, which left the image at its intrinsic height with a band of
   empty page beneath it. */
.project-cover {
	position: relative;
	display: grid;
	min-height: 62vh;
	min-height: 62svh;
	max-height: 46rem;
	overflow: hidden;
	background-color: var(--dtc-base-tint);
}

.project-cover > * {
	grid-area: 1 / 1;
}

@media (min-width: 48em) {
	.project-cover {
		min-height: 74vh;
		min-height: 74svh;
	}
}

.project-cover__image {
	width: 100%;
	height: 100%;
	object-fit: cover;
}

/* Only needs to protect the overlaid bar at the top — there is no type over
   the lower part of a project cover. */
.project-cover__scrim {
	position: absolute;
	inset: 0;
	background-image: linear-gradient(
		to bottom,
		rgba(44, 44, 44, 0.18) 0%,
		rgba(44, 44, 44, 0) 34%
	);
	pointer-events: none;
}

.project-header {
	padding-bottom: 0;
}

.project-title {
	margin-bottom: var(--dtc-space-xs);
}

.project-meta {
	color: var(--dtc-ink-muted);
	font-size: var(--dtc-text-xs);
	letter-spacing: var(--dtc-tracking-wide);
	text-transform: uppercase;
}

.project-meta__sep {
	margin-inline: 0.6em;
}

/* The short paragraph. Short — not a case study. */
.project-intro {
	max-width: 52ch;
	margin-top: var(--dtc-space-md);
	font-size: var(--dtc-text-lg);
	line-height: 1.55;
}

/*
 * Editor content. A three-track grid so photos can sit in the text column,
 * break wide, or run full-bleed, without negative margins.
 */

.project-content {
	/* Collapsed on small screens: with a fixed value the two wide tracks ate
	   the reading column at 390px and text wrapped to about a third of the
	   screen. Below the breakpoint, wide and content are the same width. */
	--dtc-wide-track: 0rem;

	display: grid;
	grid-template-columns:
		[full-start] minmax(var(--dtc-gutter), 1fr)
		[wide-start] minmax(0, var(--dtc-wide-track))
		[content-start] minmax(0, 44rem) [content-end]
		minmax(0, var(--dtc-wide-track)) [wide-end]
		minmax(var(--dtc-gutter), 1fr) [full-end];
	padding-block: var(--dtc-space-xl);
	row-gap: var(--dtc-space-md);
}

@media (min-width: 64em) {
	.project-content {
		--dtc-wide-track: 6rem;
	}
}

/*
 * Prose stays at a readable measure even though the track is sized for
 * photographs. No left rule: at 1px on cream it read as an artifact rather
 * than as art direction. The indent went with it — an indent with nothing to
 * sit against just reads as a misalignment against the photographs and the
 * headings. The taupe rule stays exclusive to the Project note.
 */
.project-content > p {
	max-width: 62ch;
}

/* Consecutive paragraphs sit closer than the section gap so a run of copy
   reads as one passage. Loosened now that they are not being closed up to
   join a rule. */
.project-content > p + p {
	margin-top: calc(var(--dtc-space-sm) - var(--dtc-space-md));
}

/* An empty paragraph block is invisible until it carries a rule, at which
   point it shows up as a bar floating in the margin. Forgotten Home has one.
   Drop them rather than rely on the copy being tidy. */
.project-content > p:empty {
	display: none;
}

/*
 * Interstitial pull-lines between photo groups. Braven, set between body size
 * and the page title so it carries without competing with it.
 * .project-section-heading is excluded — that is the small uppercase label
 * above the before/construction gallery, a different job entirely.
 */
.project-content > :is(h2, h3, h4):not(.project-section-heading) {
	max-width: 30ch;
	margin-block: var(--dtc-space-md) var(--dtc-space-xs);
	color: var(--dtc-ink);
	font-family: var(--dtc-font-display);
	line-height: 1.24;
	text-wrap: balance;
}

.project-content > h2:not(.project-section-heading) {
	font-size: var(--dtc-text-2xl);
}

.project-content > h3:not(.project-section-heading),
.project-content > h4:not(.project-section-heading) {
	font-size: var(--dtc-text-xl);
}

.project-content > * {
	grid-column: content;
	margin-block: 0;
}

/*
 * Photographs share one edge. Single images sat in the content track while
 * photo-pairs and alignwide singles broke to the wide track, so the column
 * edge wandered down the page — the opposite of art directed (design spec
 * §5). Media now defaults to the wide track; prose keeps its own measure on
 * content. Below 64em --dtc-wide-track is 0rem, so wide and content resolve
 * to the same track and mobile is unchanged. Declared before the .alignfull
 * rule so a full-bleed block still wins on source order.
 */
.project-content > .wp-block-image,
.project-content > figure,
.project-content > .wp-block-columns,
.project-content > .wp-block-gallery {
	grid-column: wide;
}

.project-content > .alignwide {
	grid-column: wide;
}

.project-content > .alignfull {
	grid-column: full;
}

.project-content > .alignfull img {
	width: 100%;
}

/* Photos keep the ratio they were shot at — this is the whole difference
   between "art directed" and "disorganised" (design spec §5). */
.project-content figure,
.project-content .wp-block-image {
	margin: 0;
}

/* Direct-child photos fill their alignment track. Deliberately scoped to
   direct children so it does not reach inside galleries and columns, which
   size their own images. */
.project-content > figure img,
.project-content > .wp-block-image img {
	width: 100%;
	height: auto;
}

.project-content .wp-block-image figcaption,
.project-content .wp-block-gallery figcaption {
	margin-top: var(--dtc-space-2xs);
	color: var(--dtc-ink-muted);
	font-size: var(--dtc-text-sm);
	text-align: left;
}

/* Tap to enlarge — core's "Expand on click". */
.project-content .wp-lightbox-container img {
	cursor: zoom-in;
}

/* The occasional inline note: a line or two on a finish selection or the
   reasoning behind a decision. Set apart without shouting. */
.project-content .is-style-dtc-note {
	max-width: 46ch;
	padding-left: var(--dtc-space-sm);
	border-left: 2px solid var(--dtc-taupe);
	color: var(--dtc-ink);
	font-size: var(--dtc-text-base);
	line-height: 1.6;
}

/* Section label, e.g. the "Before" heading above construction photos. */
.project-content .project-section-heading {
	margin-top: var(--dtc-space-lg);
	padding-top: var(--dtc-space-md);
	border-top: 1px solid var(--dtc-rule);
	font-size: var(--dtc-text-xs);
	font-family: var(--dtc-font-body);
	letter-spacing: var(--dtc-tracking-wide);
	text-transform: uppercase;
	color: var(--dtc-taupe);
}

/*
 * Galleries and column pairs keep WordPress's own layout. Core already sizes
 * nested gallery images and stacks columns on mobile; overriding that with a
 * grid here collapsed the gallery images to thumbnails. Only spacing is set.
 *
 * Construction and before photos are exported black and white in the edit, so
 * there is deliberately no CSS grayscale filter — that stays Dan's decision.
 */
.project-content .wp-block-gallery {
	margin: 0;
	gap: var(--dtc-space-xs);
}

.project-content .wp-block-columns {
	margin: 0;
	gap: var(--dtc-space-xs);
}

/*
 * Photo pairs align top and bottom.
 *
 * Core already stretches the columns to equal height, but the images inside
 * keep their own natural heights, so a 3:2 next to a 4:3 leaves one short and
 * the pair looks unaligned.
 *
 * Rather than force both to an invented shared ratio, the row height is left
 * to the naturally taller image and the shorter one is stretched to match and
 * cropped. Only one image is cropped, and the pair keeps a real ratio from the
 * photography rather than one picked here.
 *
 * Scoped to 782px and up, which is core's own columns breakpoint — below it
 * the columns stack full width and each image keeps its native ratio, exactly
 * as before.
 */
@media (min-width: 782px) {
	.project-content .wp-block-columns > .wp-block-column {
		display: flex;
		flex-direction: column;
	}

	.project-content .wp-block-columns > .wp-block-column > .wp-block-image {
		flex: 1 1 auto;
		min-height: 0;
		margin: 0;
	}

	.project-content .wp-block-columns > .wp-block-column > .wp-block-image > img {
		width: 100%;
		height: 100%;
		object-fit: cover;
	}
}

.project-footer {
	padding-top: 0;
}

/* A project with no body content yet has nothing between the header and the
   nav, which pulls the hairline up under the location line. Restore the
   section spacing in that case only, so it does not double up once photos are
   added. */
.project:not(:has(.project-content)) .project-footer {
	padding-top: var(--dtc-space-xl);
}

/* Quiet by design: a hairline, a small label, the project name. No thumbnails,
   no arrows competing with the photography above. */
.project-nav {
	display: grid;
	gap: var(--dtc-space-md);
	padding-top: var(--dtc-space-md);
	border-top: 1px solid var(--dtc-rule);
}

@media (min-width: 37.5em) {
	.project-nav {
		grid-template-columns: repeat(2, minmax(0, 1fr));
		gap: var(--dtc-space-lg);
	}
}

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

.project-nav__link--empty {
	display: none;
}

@media (min-width: 37.5em) {
	.project-nav__link--empty {
		display: block;
	}

	.project-nav__link--next {
		text-align: right;
	}
}

.project-nav__label {
	display: block;
	margin-bottom: var(--dtc-space-2xs);
	color: var(--dtc-ink-muted);
	font-size: var(--dtc-text-xs);
	letter-spacing: var(--dtc-tracking-wide);
	text-transform: uppercase;
}

.project-nav__title {
	display: block;
	font-family: var(--dtc-font-display);
	font-size: var(--dtc-text-xl);
	line-height: 1.2;
	transition: color var(--dtc-transition);
}

.project-nav__link:hover .project-nav__title,
.project-nav__link:focus-visible .project-nav__title {
	color: var(--dtc-taupe);
}

.project-footer__all {
	margin-top: var(--dtc-space-lg);
}

/* ==========================================================================
   6e. About page

   Almost everything here leans on the shared pieces — .dtc-section for
   rhythm, .dtc-container for measure, .dtc-eyebrow for the small labels,
   .dtc-split for the image-beside-copy sections, .dtc-button--ghost for the
   closing action. Only the moments that genuinely differ get their own rules.
   ========================================================================== */

/*
 * Sections sit directly on top of one another on the same background, so each
 * pair contributed two full section paddings and opened a dead zone — the
 * same fault the homepage had between intro and work. Only the section above
 * supplies the gap, which puts the page on the homepage's 112px rhythm
 * instead of 224px.
 */
.about > section + section {
	padding-top: 0;
}

/* 1. Opening. Still a little more air than a standard section, because there
   is no hero above it to open the page — but not the 144px it used to. */
.about-opening {
	padding-top: calc(var(--dtc-space-xl) + var(--dtc-space-xs));
}

.about-opening__heading {
	max-width: 24ch;
}

.about-opening__text {
	max-width: 62ch;
	margin-top: var(--dtc-space-md);
}

/*
 * 3. Statement break. The one place on the page that is pure type and space,
 * so it gets more air than a section gap — but it owns that air rather than
 * inheriting it. The section above gives up its bottom padding and the one
 * below already has no top padding, so the space is symmetric and set here in
 * one place instead of being the sum of three rules.
 */
.about > section:has(+ .about-statement) {
	padding-bottom: 0;
}

.about > .about-statement {
	padding-block: calc(var(--dtc-space-xl) + var(--dtc-space-lg));
}

.about-statement {
	text-align: center;
}

/* On a phone the desktop figure reads as a void rather than a pause. */
@media (max-width: 47.99em) {
	.about > .about-statement {
		padding-block: var(--dtc-space-lg);
	}
}

/* Measured in ch so it holds two or three lines as the type scales, rather
   than the narrow container's fixed rem width, which broke it to five. */
.about-statement__line {
	max-width: 26ch;
	margin-inline: auto;
	margin-block: 0;
	font-family: var(--dtc-font-display);
	font-size: clamp(2rem, 1.4rem + 3vw, 3.75rem);
	line-height: 1.18;
	text-wrap: balance;
}

/* 4 & 5. People. */
.about-person__name {
	margin-bottom: var(--dtc-space-2xs);
}

/* Chris sits below Dan and is deliberately the quieter of the two — the
   narrower photo column does that work. Its top padding is already collapsed
   by the adjacent-section rule above. */

/* 6. Image break. Runs the full width; the caption returns to the container.
   The Services page reuses the same break, so the photograph is set once here
   for both rather than twice — the two cannot then drift apart. Only the
   About page carries a caption. */
.about-break__media img,
.about-break__media .dtc-placeholder,
.services-break__media img,
.services-break__media .dtc-placeholder {
	width: 100%;
	aspect-ratio: 4 / 5;
	object-fit: cover;
}

@media (min-width: 48em) {
	.about-break__media img,
	.about-break__media .dtc-placeholder,
	.services-break__media img,
	.services-break__media .dtc-placeholder {
		aspect-ratio: 16 / 9;
	}
}

.about-break__caption {
	margin-top: var(--dtc-space-sm);
	color: var(--dtc-ink-muted);
	font-size: var(--dtc-text-sm);
}

/* This section carries no section padding of its own — the image runs to the
   edges — so it has to supply the gap to the section below, which no longer
   has any top padding of its own. */
.about-break {
	padding-bottom: var(--dtc-space-xl);
}

/* 7. Philosophy. */
.about-philosophy__heading {
	max-width: 24ch;
}

/* 8. Closing. Strong image, then the ask beneath it. */
.about-cta__media img,
.about-cta__media .dtc-placeholder {
	width: 100%;
	aspect-ratio: 4 / 5;
	object-fit: cover;
}

@media (min-width: 48em) {
	.about-cta__media img,
	.about-cta__media .dtc-placeholder {
		aspect-ratio: 16 / 9;
	}
}

.dtc-cta {
	padding-block: var(--dtc-space-xl);
	text-align: center;
}

.dtc-cta__text {
	margin-top: var(--dtc-space-xs);
	color: var(--dtc-ink-muted);
}

.dtc-cta__action {
	margin-top: var(--dtc-space-md);
}

/* ==========================================================================
   6g. Services page

   The offerings block, the image break and the closing block are all shared
   with the homepage and the About page; only the heading and the seams
   between the sections are set here.
   ========================================================================== */

.services-page__heading {
	font-size: var(--dtc-text-2xl);
}

.services-page__header {
	margin-bottom: var(--dtc-space-lg);
}

/* No seam rule here any more, deliberately. The image break now sits between
   the offerings and the closing block, so the offerings keep the bottom
   padding .dtc-section already gives them — without it the photograph welds
   to the copy above. Beneath the photograph nothing is needed either: the
   closing block brings its own vertical padding. That is what the old
   `padding-bottom: 0` override on this section was for, so it is gone. */

/* ==========================================================================
   6f. Contact page

   The form is Contact Form 7's output, restyled here rather than by rewriting
   the form markup — that lives in the database, so it would not travel with
   the theme or survive a deploy. Same reasoning as the Mailchimp form, and the
   field treatment matches it.
   ========================================================================== */

.contact-heading {
	font-size: var(--dtc-text-2xl);
}

.contact-header {
	margin-bottom: var(--dtc-space-lg);
}

.contact-intro {
	max-width: 52ch;
	margin-top: var(--dtc-space-sm);
}

/*
 * The form was built against the old theme and still carries Bootstrap grid
 * classes, which another plugin's stylesheet keeps alive. Neutralise them so
 * the fields simply stack, rather than editing the form and losing the change
 * on deploy.
 */
.contact-form .row {
	display: block;
	margin-inline: 0;
}

.contact-form [class*="col-md-"] {
	flex: none;
	max-width: none;
	margin-bottom: var(--dtc-space-sm);
	padding-inline: 0;
}

.contact-form p {
	margin: 0;
}

.contact-form label {
	display: block;
	color: var(--dtc-ink);
	font-size: var(--dtc-text-sm);
	letter-spacing: 0.04em;
}

.contact-form .wpcf7-form-control-wrap {
	display: block;
}

.contact-form input[type="text"],
.contact-form input[type="email"],
.contact-form input[type="tel"],
.contact-form input[type="url"],
.contact-form textarea,
.contact-form select {
	width: 100%;
	margin-top: var(--dtc-space-2xs);
	padding: 0.85em 1em;
	border: 1px solid var(--dtc-rule);
	border-radius: var(--dtc-radius-control);
	background-color: transparent;
	color: var(--dtc-ink);
	font-family: inherit;
	font-size: var(--dtc-text-base);
	line-height: 1.4;
}

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

.contact-form input:focus,
.contact-form textarea:focus,
.contact-form select:focus {
	border-color: var(--dtc-taupe);
	outline: none;
}

.contact-form ::placeholder {
	color: var(--dtc-ink-muted);
	opacity: 1;
}

/* Site button style, auto width, matching the subscribe button. */
.contact-form input[type="submit"] {
	width: auto;
	margin-top: var(--dtc-space-xs);
	padding: 0.95em 2.75em;
	letter-spacing: var(--dtc-tracking-wide);
}

/*
 * Validation and response messages. Kept on the palette — the design spec
 * fixes the colours and has no error red in it, so an invalid field is marked
 * with the brand taupe rather than a colour the system does not own.
 */
.contact-form .wpcf7-not-valid {
	border-color: var(--dtc-taupe);
}

.contact-form .wpcf7-not-valid-tip {
	margin-top: var(--dtc-space-2xs);
	color: var(--dtc-taupe-deep);
	font-size: var(--dtc-text-xs);
	letter-spacing: 0.04em;
}

.contact-form .wpcf7-response-output {
	margin: var(--dtc-space-sm) 0 0;
	padding: var(--dtc-space-xs) var(--dtc-space-sm);
	border: 1px solid var(--dtc-rule);
	border-radius: var(--dtc-radius-control);
	color: var(--dtc-ink);
	font-size: var(--dtc-text-sm);
}

.contact-form .wpcf7-spinner {
	margin-left: var(--dtc-space-xs);
}

/* ==========================================================================
   7. Site footer
   ========================================================================== */

.site-footer {
	background-color: var(--dtc-taupe);
	color: var(--dtc-on-taupe);
}

.site-footer a {
	color: var(--dtc-on-taupe);
	text-decoration: none;
}

.site-footer a:hover,
.site-footer a:focus-visible {
	color: var(--dtc-white);
	text-decoration: underline;
}

.site-footer :focus-visible {
	outline-color: var(--dtc-on-taupe);
}

.site-footer__inner {
	display: grid;
	gap: var(--dtc-space-lg);
	padding-block: var(--dtc-space-xl) var(--dtc-space-lg);
}

@media (min-width: 48em) {
	.site-footer__inner {
		grid-template-columns: 1fr auto;
		align-items: start;
	}
}

.site-footer__brand {
	display: inline-flex;
	align-items: center;
	font-family: var(--dtc-font-display);
	font-size: var(--dtc-text-xl);
	letter-spacing: var(--dtc-tracking-wide);
	text-transform: uppercase;
}

.site-footer__tagline {
	max-width: 32ch;
	margin-top: var(--dtc-space-xs);
	color: var(--dtc-on-taupe-muted);
	font-size: var(--dtc-text-sm);
}

.site-footer__links {
	display: grid;
	gap: var(--dtc-space-md);
}

@media (min-width: 30em) {
	.site-footer__links {
		grid-auto-flow: column;
		gap: var(--dtc-space-lg);
	}
}

.site-footer__links ul {
	display: grid;
	gap: var(--dtc-space-2xs);
	margin: 0;
	padding: 0;
	list-style: none;
	font-size: var(--dtc-text-sm);
	letter-spacing: 0.06em;
	text-transform: uppercase;
}

.site-footer__heading {
	margin-bottom: var(--dtc-space-xs);
	color: var(--dtc-on-taupe-muted);
	font-size: var(--dtc-text-xs);
	font-weight: 500;
	letter-spacing: var(--dtc-tracking-wide);
	text-transform: uppercase;
}

/* The copyright and the privacy link share one quiet line, and wrap onto two
   on a narrow phone rather than shrinking. */
.site-footer__legal {
	display: flex;
	flex-wrap: wrap;
	align-items: baseline;
	gap: var(--dtc-space-2xs) var(--dtc-space-md);
	padding-block: var(--dtc-space-sm);
	border-top: 1px solid rgba(247, 244, 240, 0.22);
	color: var(--dtc-on-taupe-muted);
	font-size: var(--dtc-text-xs);
	letter-spacing: 0.06em;
}

.site-footer__copyright {
	margin: 0;
}

/* Deliberately dimmer than the footer's other links: it is the one thing here
   nobody is meant to be drawn to. Scoped through the legal line so it carries
   more weight than the blanket `.site-footer a` colour above but still less
   than that rule's hover, which lifts it to full cream — so it never reads as
   disabled. */
.site-footer__legal .site-footer__privacy {
	color: var(--dtc-on-taupe-muted);
}

/* ==========================================================================
   8. Motion — design spec §6. Reduced motion is honored throughout.
   ========================================================================== */

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

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

	/* Stop the Ken Burns drift outright rather than letting the blanket rule
	   above snap it to its end frame. The framing transform stays — it is the
	   composition, not the motion. */
	.home-hero__image {
		animation: none;
		transform: scale(var(--dtc-hero-zoom)) translate3d(var(--dtc-hero-pan), 0, 0);
	}

	/* Sections are simply present, never faded in. */
	.js .dtc-reveal {
		opacity: 1;
		transform: none;
		transition: none;
	}

	.project-card__image {
		transition: none;
	}

	.project-card__link:hover .project-card__image,
	.project-card__link:focus-visible .project-card__image {
		transform: none;
	}
}
