/*
Theme Name: DeWitt Base
Theme URI: https://dewitt-studios.com
Author: DeWitt Studios
Description: Block theme for DeWitt Studios client sites. All client-specific
             design lives in theme.json, generated from that client's brand.json.
             Do not hand-edit theme.json — regenerate it.
Version: 0.1.0
Requires at least: 6.5
Tested up to: 6.8
Requires PHP: 8.0
License: GPL-2.0-or-later
Text Domain: dewitt-base
*/

/* ---------- enquiry form ---------- */
.dw-form { display: flex; flex-direction: column; gap: 18px; max-width: 34rem; }
.dw-field { display: flex; flex-direction: column; gap: 6px; margin: 0; }
.dw-field label {
	font-family: var(--wp--preset--font-family--karla);
	font-size: 13px; font-weight: 700; letter-spacing: .06em;
	text-transform: uppercase; color: var(--wp--preset--color--ink);
}
.dw-req { color: var(--wp--preset--color--action); }
.dw-form input, .dw-form select, .dw-form textarea {
	font-family: var(--wp--preset--font-family--karla);
	font-size: 16px; line-height: 1.5; color: var(--wp--preset--color--ink);
	background: var(--wp--preset--color--white);
	border: 1px solid var(--wp--preset--color--sesame);
	border-radius: var(--wp--custom--radius--control); padding: 12px 14px; width: 100%;
	/* Padding and border inside the declared width. Without this, width:100%
	   plus 14px of padding either side made every field 20px wider than its
	   column, and the contact page — her lead form — scrolled sideways on a
	   phone. */
	box-sizing: border-box;
}
.dw-form input:focus, .dw-form select:focus, .dw-form textarea:focus {
	outline: 2px solid var(--wp--preset--color--action); outline-offset: 1px;
	border-color: var(--wp--preset--color--action);
}
.dw-form textarea { resize: vertical; min-height: 120px; }
.dw-form button { align-self: flex-start; cursor: pointer; border: 0; }
.dw-hp { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }
.dw-form-notice {
	padding: 14px 16px; border-left: 3px solid var(--wp--preset--color--action);
	background: var(--wp--preset--color--cotton); max-width: 34rem;
}
.dw-form-notice--error { border-left-color: #A2452F; }

/* Two-column blocks hold their structure into the tablet band.
   WordPress stacks every columns block below 781px — a device preset, not a
   point where this content stops fitting. Two ~350px columns are perfectly
   readable at 700px; three are not, so only 2-col blocks are tagged. */
@media (min-width: 600px) and (max-width: 781px) {
	.wp-block-columns.dw-hold-2col > .wp-block-column {
		flex-basis: 0 !important;
		flex-grow: 1;
	}
}

/* CR-103 (measure) — body copy was running to 110 characters per line at the
   1140px content width. The readable range is 45–75ch, ~65 ideal; past ~75 the
   eye loses its place returning to the next line.
   brand.json has declared `textWidth: 65ch` since day one and nothing ever
   consumed it. This is that value, finally wired up.
   Scoped to prose sitting directly in a section band — paragraphs inside
   columns already measure ~69ch and must not be constrained twice. */
.wp-block-post-content .wp-block-group > p:not(.has-label-font-size) {
	max-width: var(--dw-measure, 58ch);
	/* WordPress's constrained layout emits `margin-left: auto !important` on
	   every child, so a max-width alone centres the paragraph and it drifts
	   off the heading's left edge. Overriding core's !important is the only
	   way to hold the edge without restructuring the markup.
	   Aligning to 0 would snap the paragraph to the *padding* edge, 38px left
	   of every constrained sibling. It has to start where the contentSize box
	   starts, so the offset is half the leftover width — and 0 once the
	   viewport is narrower than contentSize, which is the mobile case. */
	margin-inline-start: max(0px, calc((100% - var(--wp--style--global--content-size)) / 2)) !important;
	margin-inline-end: auto !important;
}

/* A centre-aligned paragraph has to have a centred BOX too. Pinning it to the
   start edge centres its text inside a left-hugging column, which put the
   statement section's copy 226px left of the heading it sits under — both
   "centred", on two different axes. */
.wp-block-post-content .wp-block-group > p.has-text-align-center {
	margin-inline: auto !important;
}

/* Service panels — the price is the section's visual, not a paragraph about it.
   CR-003 (whitespace before borders): the panels were 1px-bordered boxes on a
   tinted band, which is the default move rather than a decision. They now sit
   on white with a single accent rule at the top edge and real internal padding.
   KB rule on cards: internal padding must be <= the gap between cards. Panels
   use 32px inline against a `grid` gutter that reaches 32px, so they hold. */
.dw-service {
	border-top: 2px solid var(--wp--preset--color--action);
}
.dw-price {
	font-family: var(--wp--preset--font-family--lora);
	color: var(--wp--preset--color--ink);
	line-height: 1;
	margin-block: 12px 4px;
	display: flex;
	align-items: baseline;
	flex-wrap: wrap;
	gap: 10px;
}
/* the qualifier rides beside the number instead of competing with it */
.dw-price__unit {
	font-family: var(--wp--preset--font-family--karla);
	font-size: var(--wp--preset--font-size--small);
	font-weight: 400;
	letter-spacing: normal;
	color: var(--wp--preset--color--text-muted);
}
.dw-includes {
	list-style: none;
	padding-left: 0;
	margin-top: 20px;
	font-size: var(--wp--preset--font-size--small);
	color: var(--wp--preset--color--ink);
}
.dw-includes li {
	position: relative;
	padding-left: 20px;
	margin-bottom: 9px;
	line-height: 1.5;
}
/* a rule, not a bullet glyph — keeps the flat, editorial language */
.dw-includes li::before {
	content: "";
	position: absolute;
	left: 0;
	top: 0.72em;
	width: 10px;
	height: 1px;
	background: var(--wp--preset--color--action);
}
/* equal-height panels so the two prices sit on the same line */
.wp-block-columns:has(> .dw-service) { align-items: stretch; }

/* ─── Motion ──────────────────────────────────────────────────────────────
   There were no easing or duration tokens in this theme, so these establish
   them. Built-in CSS easings are too weak for UI; these are the standard
   strong curves. Nothing here animates anything a visitor sees more than a
   few times per visit. */
:root {
	--ease-out: cubic-bezier(0.23, 1, 0.32, 1);
	--ease-in-out: cubic-bezier(0.77, 0, 0.175, 1);
}

/* 1 + 3 — enquiry notices.
   Purpose: preventing a jarring change. Frequency: rare (once, after someone
   has just decided to contact her). @starting-style animates the entrance with
   no JavaScript at all. 260ms keeps it under the 300ms UI ceiling — the
   original proposal said 320ms, which is over. */
.dw-form-notice {
	transition:
		opacity 260ms var(--ease-out),
		transform 260ms var(--ease-out);
}
@starting-style {
	.dw-form-notice {
		opacity: 0;
		transform: translateY(6px);
	}
}

/* 2 — buttons. Purpose: feedback. Frequency: occasional.
   Colour on hover, a press on :active. Hover is pointer-gated because touch
   fires a false hover on tap and would leave the button stuck in its hover
   state after the finger lifts. */
.wp-element-button {
	transition:
		background-color 140ms ease,
		/* the press is deliberate, the release is the system answering — so
		   the release snaps back faster than the press goes down */
		transform 90ms var(--ease-out);
}
@media (hover: hover) and (pointer: fine) {
	.wp-element-button:hover {
		background-color: var(--wp--preset--color--ink);
	}
	/* the dark CTA reverses: white button on ink, so it warms instead */
	.has-ink-background-color .wp-element-button:hover {
		background-color: var(--wp--preset--color--sesame);
	}
}
.wp-element-button:active {
	transform: scale(0.98);
	transition: transform 120ms var(--ease-out);
}

/* 5 — links. Purpose: feedback. Only the underline colour moves; the text
   colour holds so the link never looks like it changed meaning. */
main a:not(.wp-element-button) {
	/* No resting declaration. Setting one to sesame put the underline at 1.0:1
	   against the sesame band — invisible — on the two links that matter most,
	   her phone number and her email. The underline inherits currentColor and
	   only the hover state moves. */
	transition: text-decoration-color 140ms ease;
}
@media (hover: hover) and (pointer: fine) {
	main a:not(.wp-element-button):hover {
		text-decoration-color: var(--wp--preset--color--text-muted);
	}
}

/* 4 — portfolio stagger. Purpose: delight, at the occasional tier.

   Built with a scroll-driven CSS timeline, not IntersectionObserver, and the
   reason is the failure mode. A JS reveal has to hide the images first and
   then un-hide them; if that script ever fails to run its second half, her
   portfolio is invisible — and losing the photographs is far worse than
   losing the animation.

   `animation-timeline: view()` inverts that. It is wrapped in @supports, so a
   browser without it simply never applies the animation and the images are
   visible by default. There is no hidden state to get stuck in, and no
   JavaScript on the site at all.

   The trade is honest: Safari does not support view() timelines yet, so a good
   share of her visitors will see the grid appear normally. That is the correct
   direction to fail for a delight-tier effect.

   The stagger comes from offsetting each column's entry range rather than an
   animation-delay, which a scroll timeline ignores. */
@supports (animation-timeline: view()) {
	@media (prefers-reduced-motion: no-preference) {
		[data-reveal] {
			animation: dw-rise linear both;
			animation-timeline: view();
			animation-range: entry 0% cover 35%;
		}
		[data-reveal]:nth-of-type(2) { animation-range: entry 8% cover 43%; }
		[data-reveal]:nth-of-type(3) { animation-range: entry 16% cover 51%; }
	}
}
@keyframes dw-rise {
	from { opacity: 0; transform: translateY(28px); }
	to   { opacity: 1; transform: none; }
}

/* Reduced motion: gentler, not zero. Movement goes, the fades stay, because
   the fade is what makes the change legible. */
@media (prefers-reduced-motion: reduce) {
	.dw-form-notice { transition: opacity 200ms ease; }
	@starting-style { .dw-form-notice { transform: none; } }
	.wp-element-button { transition: background-color 140ms ease; }
	.wp-element-button:active { transform: none; }
}

/* ─── Scrolling photo band ────────────────────────────────────────────────
   A continuous horizontal strip of her work. The reference site uses this
   shape for press logos; here it carries photography instead, because she has
   the photographs and does not have the press.

   CSS only. The track holds the set twice and translates exactly -50%, so the
   second copy lands where the first began and the loop is seamless.
   `aria-hidden` on the duplicate keeps a screen reader from reading the whole
   set twice. */
.dw-band {
	overflow: hidden;
	background: var(--wp--preset--color--ink);
}
.dw-band__track {
	display: flex;
	width: max-content;
	gap: 4px;
	animation: dw-marquee 64s linear infinite;
}
.dw-band__track img {
	display: block;
	height: clamp(200px, 26vw, 340px);
	width: auto;
	object-fit: cover;
}
@keyframes dw-marquee {
	from { transform: translateX(0); }
	to   { transform: translateX(-50%); }
}
/* Let someone stop it to actually look at a room. */
@media (hover: hover) and (pointer: fine) {
	.dw-band:hover .dw-band__track { animation-play-state: paused; }
}
/* Reduced motion: no crawl. It becomes a strip you scroll yourself, which is
   the same content without the movement. */
@media (prefers-reduced-motion: reduce) {
	.dw-band { overflow-x: auto; }
	.dw-band__track { animation: none; }
}

/* ─── Sticky header ───────────────────────────────────────────────────────
   The reference site pins an opaque bar and lets content scroll up and
   disappear behind it. Its header is transparent but contains a solid white
   layer — the photograph never shows through, which is why the nav stays
   legible over any image.

   Ours is already white. The problem is height: at 205px a pinned header eats
   a quarter of every screen, so it contracts as you scroll. Done with a
   scroll-driven timeline rather than a JS scroll listener — no listener firing
   on every frame, and a browser without support simply gets a header that
   pins at full height, which is still correct. */
/* The <header> element itself must be the sticky one. Sticking the inner div
   does nothing: a sticky box is constrained to its parent, and that parent is
   only as tall as the header, so it has nowhere to travel. */
.wp-site-blocks > header {
	position: sticky;
	top: 0;
	z-index: 20;
}

@supports (animation-timeline: scroll()) {
	@media (prefers-reduced-motion: no-preference) {
		.dw-header {
			animation: dw-header-shrink linear both;
			animation-timeline: scroll(root block);
			animation-range: 0 320px;
		}
		/* Not the animated lockup: that one shrinks as a whole (below), because
		   shrinking only the image would leave the drawn house at full size
		   while the wordmark under it got smaller. */
		.dw-header .wp-block-site-logo img:not(.dw-logo__word) {
			animation: dw-logo-shrink linear both;
			animation-timeline: scroll(root block);
			animation-range: 0 320px;
			transform-origin: left center;
		}
		.dw-header .dw-logo {
			animation: dw-logo-shrink linear both;
			animation-timeline: scroll(root block);
			animation-range: 0 320px;
		}
	}
}
@keyframes dw-header-shrink {
	from { padding-top: 22px; padding-bottom: 18px; }
	to   { padding-top: 10px; padding-bottom: 10px; }
}
@keyframes dw-logo-shrink {
	from { width: 170px; }
	to   { width: 96px; }
}

/* A hairline appears once you have scrolled, so the pinned bar has an edge
   against the photography passing underneath it. */
.dw-header::after {
	content: "";
	position: absolute;
	inset-inline: 0;
	bottom: 0;
	height: 1px;
	background: var(--wp--preset--color--sesame);
	opacity: 0;
}
@supports (animation-timeline: scroll()) {
	.dw-header::after {
		animation: dw-header-rule linear both;
		animation-timeline: scroll(root block);
		animation-range: 0 320px;
	}
}
@keyframes dw-header-rule {
	from { opacity: 0; }
	to   { opacity: 1; }
}

/* ─── Hero: photograph with an overlaid card ──────────────────────────────
   Replaces a 1,083px photograph stacked above the headline. In that version
   her first words landed 1,379px down an 813px screen — nearly two screens of
   room before a visitor learned she was a decorator. The card puts the promise
   on the first screen without giving up the full-bleed photograph.

   The card is opaque, not a translucent overlay on the image. A scrim dulls
   the room and makes the type's contrast depend on whichever photograph is
   loaded; an opaque card holds the same 11.3:1 ink-on-white as the rest of
   the site no matter what is behind it. */
.dw-hero{position:relative}
.dw-hero__media{
	position:relative;
	height:clamp(420px,72vh,760px);
	background:var(--wp--preset--color--sesame);
}
.dw-hero__img{
	position:absolute;inset:0;
	width:100%;height:100%;
	object-fit:cover;display:block;
}
.dw-hero__inner{
	position:absolute;inset:0;
	display:flex;align-items:flex-end;
	padding-inline:max(24px, calc((100% - var(--wp--style--global--content-size)) / 2));
	pointer-events:none;
}
.dw-hero__card{
	pointer-events:auto;
	background:var(--wp--preset--color--white);
	max-width:33rem;
	padding:34px 36px 36px;
}
.dw-hero__eyebrow{
	margin:0 0 14px;
	font-size:var(--wp--preset--font-size--label);
	font-weight:700;letter-spacing:.14em;text-transform:uppercase;
	color:var(--wp--preset--color--action);
}
.dw-hero__title{
	margin:0 0 14px;
	font-family:var(--wp--preset--font-family--lora);
	font-size:var(--wp--preset--font-size--display);
	font-weight:400;line-height:1.05;letter-spacing:-.015em;
	color:var(--wp--preset--color--ink);
	text-wrap:balance;
}
.dw-hero__body{
	margin:0 0 22px;
	font-size:var(--wp--preset--font-size--lead);
	line-height:1.55;
	color:var(--wp--preset--color--text-muted);
	max-width:34ch;
}
.dw-hero__cta{display:inline-block}

/* On a phone the card stops overlaying and simply follows the photograph —
   a card sitting on top of a 420px image leaves the room unreadable. */
@media (max-width:781px){
	.dw-hero__media{height:clamp(280px,46vh,420px)}
	.dw-hero__inner{position:static;padding:0}
	.dw-hero__card{max-width:none;padding:26px 24px 30px}
}

/* ─── "When people call me" ───────────────────────────────────────────────
   Two columns of reasons, separated by space and a hairline rather than by
   cards. Cards here would be five boxes doing the work a gap already does. */
.dw-reasons{
	list-style:none;margin:var(--wp--preset--spacing--section) 0 0;padding:0;
	display:grid;gap:0 var(--wp--preset--spacing--grid);
	grid-template-columns:1fr;
}
@media (min-width:781px){ .dw-reasons{grid-template-columns:1fr 1fr} }
.dw-reasons li{
	padding:22px 0;
	border-top:1px solid var(--wp--preset--color--sesame);
}
.dw-reasons h3{
	margin:0 0 6px;
	font-family:var(--wp--preset--font-family--lora);
	font-size:var(--wp--preset--font-size--card);
	font-weight:600;line-height:1.25;
	color:var(--wp--preset--color--ink);
}
.dw-reasons p{
	margin:0;
	font-size:var(--wp--preset--font-size--small);
	line-height:1.55;
	color:var(--wp--preset--color--text-muted);
	max-width:44ch;
}

/* ─── Project guide ───────────────────────────────────────────────────────
   Sits on snow rather than sesame so it reads as an aside to the page, not as
   another content band. The cover is drawn in CSS: a stand-in for a real cover
   is still a stand-in, and a drawn one cannot be mistaken for a finished book. */
.dw-guide{
	background:var(--wp--preset--color--snow);
	/* No border-top, for the same reason as .d3-work: a sesame hairline is
	   1.25:1 against snow. The change of ground marks the boundary. */
	padding-block:var(--wp--preset--spacing--section);
	padding-inline:24px;
}
.dw-guide__inner{
	max-width:var(--wp--style--global--content-size);
	margin-inline:auto;
	display:flex;gap:var(--wp--preset--spacing--grid);
	align-items:flex-start;flex-wrap:wrap;
}
.dw-guide__cover{
	flex:0 0 132px;height:172px;
	background:var(--wp--preset--color--ink);
	display:flex;align-items:center;justify-content:center;
	color:var(--wp--preset--color--white);
	font-family:var(--wp--preset--font-family--lora);
	font-size:19px;line-height:1.25;text-align:center;
	border-bottom:5px solid var(--wp--preset--color--action);
}
/* Spacing here is flex gap, not margins. The guide renders inside the .d3
   wrapper on the home page, and `.d3 p{margin:0}` (0,1,1) outranks this
   block's single-class rules (0,1,0) — every margin below was computing to
   zero, which is why the pitch and the form ran together. gap cannot be
   zeroed by a margin reset; the two rules that need a different value are
   scoped to .dw-guide so they outrank it. */
.dw-guide__body{
	flex:1 1 22rem;min-width:0;
	display:flex;flex-direction:column;gap:10px;
}
/* a button in the ready state must not stretch to the column width */
.dw-guide__body > .wp-element-button{align-self:flex-start}
/* the group boundary: the pitch ends here and the form begins */
.dw-guide .dw-guide__lead{margin-block-end:22px}
.dw-guide .dw-guide__pending{margin-block-end:6px}
.dw-guide__eyebrow{
	margin:0 0 8px;font-size:var(--wp--preset--font-size--label);
	font-weight:700;letter-spacing:.14em;text-transform:uppercase;
	color:var(--wp--preset--color--action);
}
.dw-guide__body h2{
	margin:0 0 10px;
	font-family:var(--wp--preset--font-family--lora);
	font-size:var(--wp--preset--font-size--heading);
	font-weight:400;line-height:1.15;
	color:var(--wp--preset--color--ink);
}
.dw-guide__lead{margin:0 0 16px;max-width:52ch;color:var(--wp--preset--color--ink)}
.dw-guide__pending{
	margin:0 0 16px;max-width:52ch;
	font-size:var(--wp--preset--font-size--small);
	color:var(--wp--preset--color--text-muted);
}
.dw-guide__label{
	display:block;margin-bottom:8px;
	font-size:var(--wp--preset--font-size--label);
	font-weight:700;letter-spacing:.1em;text-transform:uppercase;
	color:var(--wp--preset--color--text-muted);
}
.dw-guide__row{display:flex;gap:10px;flex-wrap:wrap}
.dw-guide__row input{
	flex:1 1 16rem;min-width:0;box-sizing:border-box;
	font-family:var(--wp--preset--font-family--karla);
	font-size:16px;line-height:1.5;
	color:var(--wp--preset--color--ink);
	background:var(--wp--preset--color--white);
	border:1px solid var(--wp--preset--color--sesame);
	border-radius:var(--wp--custom--radius--control);padding:12px 14px;
}
.dw-guide__row input:focus{
	outline:2px solid var(--wp--preset--color--action);outline-offset:1px;
	border-color:var(--wp--preset--color--action);
}
.dw-guide .dw-guide__note{
	margin:16px 0 0;padding:12px 14px;
	font-size:var(--wp--preset--font-size--small);
	border-left:3px solid var(--wp--preset--color--action);
	background:var(--wp--preset--color--white);max-width:46rem;
}
.dw-guide__note--error{border-left-color:#A2452F}

/* ─── Process band ────────────────────────────────────────────────────────
   The numbered band from the approved design: text and a numbered list on the
   left, two stacked photographs on the right, on the warm ground. Her process
   has five steps, not the reference's six — the numbers are the structure, so
   inventing a sixth to fill the grid would be decoration pretending to be
   information. */
.dw-process{
	background:var(--wp--preset--color--sesame);
	padding-block:var(--wp--preset--spacing--section);
	padding-inline:24px;
}
.dw-process__inner{
	max-width:var(--wp--style--global--content-size);
	margin-inline:auto;
	display:grid;gap:var(--wp--preset--spacing--section);
	grid-template-columns:1fr;
}
@media (min-width:900px){
	.dw-process__inner{grid-template-columns:1.45fr 1fr;gap:var(--wp--preset--spacing--grid)}
}
.dw-process__eyebrow{
	margin:0 0 10px;font-size:var(--wp--preset--font-size--label);
	font-weight:700;letter-spacing:.14em;text-transform:uppercase;
	color:var(--wp--preset--color--action);
}
.dw-process__text h2{
	margin:0 0 12px;
	font-family:var(--wp--preset--font-family--lora);
	font-size:var(--wp--preset--font-size--heading);
	font-weight:400;line-height:1.15;color:var(--wp--preset--color--ink);
	text-wrap:balance;
}
.dw-process__lead{
	margin:0 0 var(--wp--preset--spacing--stack);
	max-width:46ch;color:var(--wp--preset--color--ink);
}
.dw-steps{
	list-style:none;margin:0;padding:0;
	display:grid;gap:18px var(--wp--preset--spacing--grid);
	grid-template-columns:1fr;
}
@media (min-width:600px){ .dw-steps{grid-template-columns:1fr 1fr} }
.dw-steps li{display:flex;gap:12px;align-items:baseline}
.dw-step__n{
	font-family:var(--wp--preset--font-family--lora);
	font-size:var(--wp--preset--font-size--card);
	font-weight:600;color:var(--wp--preset--color--action);
	font-variant-numeric:tabular-nums;flex:none;
}
.dw-step__t{display:flex;flex-direction:column;gap:2px}
.dw-step__t strong{
	font-size:var(--wp--preset--font-size--small);
	font-weight:700;color:var(--wp--preset--color--ink);
}
.dw-step__t em{
	font-style:normal;font-size:var(--wp--preset--font-size--small);
	color:var(--wp--preset--color--text-muted);line-height:1.5;
}
.dw-process__more{margin:var(--wp--preset--spacing--stack) 0 0;font-size:var(--wp--preset--font-size--small)}
.dw-process__pics{display:flex;flex-direction:column;gap:10px}
.dw-process__pics img{
	display:block;width:100%;height:auto;
	aspect-ratio:4/3;object-fit:cover;
}

/* ─── Alternating project rows ────────────────────────────────────────────
   Replaces the three-up grid. A grid shows three thumbnails; rows give each
   project a sentence and room to be a project rather than a tile. */
.dw-rows{
	background:var(--wp--preset--color--white);
	padding-block:var(--wp--preset--spacing--pivot);
	padding-inline:24px;
}
.dw-rows__inner{max-width:var(--wp--style--global--content-size);margin-inline:auto}
.dw-rows__eyebrow{
	margin:0 0 10px;font-size:var(--wp--preset--font-size--label);
	font-weight:700;letter-spacing:.14em;text-transform:uppercase;
	color:var(--wp--preset--color--action);
}
.dw-rows__h{
	margin:0 0 var(--wp--preset--spacing--section);
	font-family:var(--wp--preset--font-family--lora);
	font-size:var(--wp--preset--font-size--heading);
	font-weight:400;line-height:1.15;color:var(--wp--preset--color--ink);
}
.dw-row{
	display:grid;gap:var(--wp--preset--spacing--grid);
	grid-template-columns:1fr;
	align-items:center;
	margin-bottom:var(--wp--preset--spacing--section);
}
@media (min-width:781px){
	.dw-row{grid-template-columns:1.1fr 1fr}
	.dw-row--flip .dw-row__pic{order:2}
	.dw-row--flip .dw-row__text{order:1}
}
.dw-row__pic img{
	display:block;width:100%;height:auto;
	aspect-ratio:3/2;object-fit:cover;
}
.dw-row__meta{
	margin:0 0 6px;font-size:var(--wp--preset--font-size--label);
	font-weight:700;letter-spacing:.12em;text-transform:uppercase;
	color:var(--wp--preset--color--text-muted);
}
.dw-row h3{
	margin:0 0 10px;
	font-family:var(--wp--preset--font-family--lora);
	font-size:var(--wp--preset--font-size--subheading);
	font-weight:600;line-height:1.2;color:var(--wp--preset--color--ink);
}
.dw-row__blurb{margin:0 0 12px;max-width:44ch;color:var(--wp--preset--color--text-muted)}
.dw-row__link{margin:0;font-size:var(--wp--preset--font-size--small)}
.dw-rows__cta{margin:0;text-align:center}

/* ═══ Home — port of Claude Design option 3a ══════════════════════════════
   Every proportion below comes from the handoff. Prototype values were drawn
   on an 800px review card; the handoff's own production figures are used here
   (headline 40->58, section heading 32->42, body 14->17, meta 12-13->14,
   gutter 40->72, section padding 44->80). Eyebrow 11px, nav and buttons 12px
   stay as authored. Radius 0 everywhere, no shadows — both deliberate. */
:root{
	--d3-gutter:72px;
	--d3-pad:80px;
	--d3-line:var(--wp--preset--color--sesame);
}
.d3{ color:var(--wp--preset--color--ink); }
.d3 h1,.d3 h2,.d3 h3,.d3 blockquote{
	font-family:var(--wp--preset--font-family--lora);
	/* 400, not 500: Lora is loaded at 400 and 600, so a 500 request resolves to
	   400 anyway. 400 is also the weight Beauty Awakened sets its headlines at,
	   which is the look Sean asked for. */
	font-weight:400;margin:0;text-wrap:balance;
}
.d3 p{margin:0}
.d3-lead{font-size:17px;font-weight:400;line-height:1.7;color:var(--wp--preset--color--text-muted);max-width:66ch}
.d3-centre{text-align:center}

/* buttons — 12px, .12em, rounded. The handoff drew these square; rounding them
   is a deliberate departure Sean asked for, and the radius is one token in
   brand.json so these and the block editor's buttons cannot drift apart (they
   were 0px and 4px). */
.d3-btn{
	display:inline-block;background:var(--wp--preset--color--ink);color:var(--wp--preset--color--white);
	padding:14px 34px;font-size:12px;font-weight:700;letter-spacing:.12em;text-transform:uppercase;
	text-decoration:none;border:1px solid var(--wp--preset--color--ink);
	border-radius:var(--wp--custom--radius--control);
	transition:background-color 160ms ease,color 160ms ease,border-color 160ms ease;
}
.d3-btn--ghost{background:transparent;color:var(--wp--preset--color--ink)}
/* the portfolio CTA — action green rather than the ink of every other button,
   because it is the one thing this section exists to get clicked.
   White on action is 7.33:1, clearing the AAA bar this project enforces. */
.d3-btn--accent{
	background:var(--wp--preset--color--action);
	border-color:var(--wp--preset--color--action);
	color:var(--wp--preset--color--white);
}
@media (hover:hover) and (pointer:fine){
	.d3-btn:hover{background:var(--wp--preset--color--action);border-color:var(--wp--preset--color--action);color:var(--wp--preset--color--white)}
	/* the accent button already is action, so it darkens to ink instead —
	   matching what the block editor's buttons do. White on ink is 11.32:1. */
	.d3-btn--accent:hover{background:var(--wp--preset--color--ink);border-color:var(--wp--preset--color--ink)}
}

/* 2 · hero — 390px proto -> 620px; card absolute, 40px in, flush bottom */
.d3-hero{position:relative}
/* Three photographs cross-fading, the way London Design Group's hero runs:
   three frames, roughly five seconds each, dissolve rather than slide. Pure CSS
   — a stacked-opacity crossfade needs no JavaScript, and a CSS animation keeps
   running off the main thread while the rest of the page is still loading.

   The media box owns the height so the section does not collapse when every
   frame is absolutely positioned. Only opacity animates, so this stays on the
   compositor. */
.d3-hero__media{position:relative;height:clamp(400px,62vh,620px)}
.d3-hero__img{
	position:absolute;inset:0;display:block;width:100%;height:100%;object-fit:cover;
	opacity:0;
	animation:dw-hero-cross 18s linear infinite;
}
/* Frame one runs a second into its own cycle so it is already at full opacity
   on load — starting it at 0% would open the page on a blank hero. Frames two
   and three wait their turn on positive delays, held invisible by the base
   opacity above. */
.d3-hero__img:nth-child(1){animation-delay:-1s}
.d3-hero__img:nth-child(2){animation-delay:5s}
.d3-hero__img:nth-child(3){animation-delay:11s}
@keyframes dw-hero-cross{
	0%     {opacity:0}
	5.5%   {opacity:1}   /* ~1s dissolve in  */
	33.3%  {opacity:1}   /* ~5s hold         */
	38.8%  {opacity:0}   /* ~1s dissolve out */
	100%   {opacity:0}
}
/* The whole effect is movement, so there is no gentler version — reduced motion
   gets the first photograph, still. */
@media (prefers-reduced-motion: reduce){
	.d3-hero__img{animation:none}
	.d3-hero__img:nth-child(1){opacity:1}
}
.d3-hero__wrap{
	position:absolute;inset:0;display:flex;align-items:flex-end;
	padding-inline:max(24px,calc((100% - 1280px)/2 + var(--d3-gutter)));
	pointer-events:none;
}
.d3-hero__card{
	pointer-events:auto;background:var(--wp--preset--color--white);
	max-width:560px;padding:44px 48px 48px;
}
.d3-hero__card h1{font-size:clamp(34px,4.6vw,58px);line-height:1.06;letter-spacing:-.015em;margin-bottom:14px}
.d3-hero__card p{font-size:17px;line-height:1.7;color:var(--wp--preset--color--text-muted);margin-bottom:26px;max-width:44ch}

/* shared section frame */
.d3-benefits,.d3-band,.d3-why,.d3-meet,.d3-work,.d3-quote,.d3-cta{
	padding:var(--d3-pad) var(--d3-gutter);
}
.d3-benefits,.d3-why,.d3-cta{max-width:1280px;margin-inline:auto}

/* 3 · three benefits — 3 cols, gap 30->48, centred */
/* Hairline separators between the three. The 48px gap goes to zero and each
   column carries 24px of padding instead, so the rule sits exactly midway
   between the columns rather than hard against the next one — a border on a
   gapped grid item lands at its edge, not in the middle of the gap. Sesame on
   white, the same hairline the project rows and the step rows already use. */
.d3-benefits{display:grid;grid-template-columns:repeat(3,1fr);gap:0;text-align:center}
.d3-benefits > div{padding-inline:24px}
.d3-benefits > div + div{border-inline-start:1px solid var(--d3-line)}
.d3-benefits strong{display:block;font-family:var(--wp--preset--font-family--lora);font-size:20px;font-weight:600;margin-bottom:8px}
.d3-benefits p{font-size:17px;line-height:1.7;color:var(--wp--preset--color--text-muted)}

/* 4 · process band — flex 1.5 / 1, gap 32->52, on the warm ground */
/* stretch, not flex-start: the single photograph matches the height of the
   text beside it, so neither column ends in blank space */
.d3-band{background:var(--wp--preset--color--sesame);display:flex;gap:52px;align-items:stretch}
.d3-band__text{flex:1.5}
.d3-band__pics{flex:1;display:flex;flex-direction:column;gap:16px;min-height:100%}
.d3-band__pics img{display:block;width:100%;height:100%;min-height:340px;object-fit:cover}
.d3-band h2{font-size:42px;line-height:1.15;margin-bottom:12px}
.d3-band .d3-lead{color:var(--wp--preset--color--ink);margin-bottom:32px}
/* One column, not two. The hairline below is a path, and a path has to be
   linear to read as one — across two columns it would zigzag. Single column
   also settles the reading order the numerals used to state outright. */
.d3-steps{list-style:none;margin:0;padding:0;display:grid;grid-template-columns:1fr;gap:0}
.d3-steps li{position:relative;display:flex;gap:14px;align-items:center;padding-bottom:22px}
.d3-steps li:last-child{padding-bottom:0}
/* The connector: a 1px rule from the bottom of one icon to the top of the next,
   centred on the 28px icon column, with a chevron at the far end so it reads as
   an arrow rather than a tick. Action green at .55 lands about 2.3:1 on the
   sesame band — visible as a thread, quiet enough that it never competes with
   the icons it joins. Both halves are pseudo-elements, so the whole thing is
   decorative and never reaches the accessibility tree. */
.d3-steps li:not(:last-child)::before{
	content:"";
	position:absolute;left:14px;transform:translateX(-50%);
	top:28px;bottom:0;width:1px;
	background:var(--wp--preset--color--action);opacity:.55;
}
/* The arrowhead: a 5px square rotated 45deg showing only two edges, so it is
   drawn at the same 1px as the shaft rather than as a filled triangle. */
.d3-steps li:not(:last-child)::after{
	content:"";
	position:absolute;left:14px;bottom:1px;
	width:5px;height:5px;
	border-right:1px solid var(--wp--preset--color--action);
	border-bottom:1px solid var(--wp--preset--color--action);
	transform:translateX(-50%) rotate(45deg);
	opacity:.55;
}
/* Icons rather than numerals here. They carry what each step IS; the order is
   still real, so the list stays an <ol> and the lead sentence still says "from
   the first visit to install day". Action green on the sesame band is 5.3:1,
   well past the 3:1 a non-text mark needs. */
.d3-steps .dw-ic{color:var(--wp--preset--color--action);flex:none}
.d3-steps em{font-style:normal;font-size:17px;line-height:1.4}

/* 5 · when people call me — 2-col grid, gap 22->36 */
.d3-why h2{font-size:42px;line-height:1.15}
.d3-why .d3-lead{margin:10px 0 0}
.d3-why__grid{display:grid;grid-template-columns:1fr 1fr;gap:36px;margin-top:38px}
.d3-why__grid>div{display:flex;gap:16px;align-items:flex-start}
.d3-why__grid strong{display:block;font-size:17px;font-weight:700;margin-bottom:4px;color:var(--wp--preset--color--ink)}
.d3-why__grid em{font-style:normal;font-size:14px;line-height:1.6;color:var(--wp--preset--color--text-muted);display:block}
.d3-why__cta{display:flex;align-items:center;justify-content:center}

/* icons — drawn inline rather than loaded from Google's icon font, so the
   privacy policy's claim that reading the site reports your visit to nobody
   stays true. Sized and coloured as the design specifies: 38px on the
   benefits row, 28px beside each reason, in the accent. */
.dw-ic{color:var(--wp--preset--color--action);flex:none;display:block}
.d3-benefits .dw-ic{margin:0 auto 16px}

/* 6 · meet — portrait 300x330 -> 480x528, flush left, text flex 1 */
.d3-meet{display:flex;gap:0;padding:0;border-top:1px solid var(--d3-line)}
.d3-meet__pic{flex:none;width:480px}
.d3-meet__pic img{display:block;width:100%;height:100%;aspect-ratio:480/528;object-fit:cover}
.d3-meet__text{flex:1;padding:58px var(--d3-gutter)}
.d3-meet h2{font-size:42px;line-height:1.15;margin-bottom:14px}
.d3-meet p{font-size:17px;line-height:1.7;color:var(--wp--preset--color--text-muted);max-width:60ch}
.d3-meet__quote{
	margin:22px 0 26px !important;padding-left:18px;
	border-left:2px solid var(--wp--preset--color--action);
	font-family:var(--wp--preset--font-family--lora);font-size:20px;line-height:1.45;
	color:var(--wp--preset--color--ink) !important;
}

/* 7 · project rows — column, gap 22->40, image left, alternating */
/* The project rows sit on the same warm ground as the project guide, so the
   page reads white - sesame - white - snow - ink - white rather than one long
   white run. Full-bleed with an inner wrapper, the way every other coloured
   band here works; painting the background straight onto the old centred
   1280px box would have left white gutters either side. */
.d3-work{
	background:var(--wp--preset--color--snow);
	/* No border-top. The hairline was the only thing marking this boundary while
	   the section was white on white; against snow it measures 1.25:1 and reads
	   as nothing. The change of ground is the boundary now. */
}
.d3-work__inner{max-width:1280px;margin-inline:auto}
.d3-work h2{font-size:42px;line-height:1.15}
.d3-work .d3-lead{margin:10px 0 0}
.d3-work__rows{
	display:flex;flex-direction:column;gap:40px;margin:42px 0 0;
	/* Two variables drive both the normal and flipped rows. They were separate
	   literals once, and the flipped row kept its old width while the others
	   changed — the middle photo rendered at 888px against its neighbours' 352px.
	
	   The columns are 220fr/145fr: the photo column's proportion is the photo's
	   own aspect ratio. This is her portfolio — her photography is the evidence
	   and the copy is its caption — so the evidence gets the dominant column. At
	   the old 352px the caption column was both wider than the photograph and
	   278px of it sat empty.
	
	   The caption has a floor because a pure ratio keeps shrinking it with the
	   photo: 276px, roughly 32 characters, by the time the rows collapse at
	   781px. The photograph absorbs the shrink instead — a smaller photo still
	   reads, cramped copy does not. */
	--d3-pic:minmax(0,220fr);
	--d3-txt:minmax(400px,145fr);
}
.d3-row{display:grid;grid-template-columns:var(--d3-pic) var(--d3-txt);gap:40px;align-items:center}
.d3-row__pic img{display:block;width:100%;height:auto;aspect-ratio:220/145;object-fit:cover}
.d3-row h3{font-size:26px;line-height:1.2;margin-bottom:10px;font-weight:600}
.d3-row p{font-size:17px;line-height:1.7;color:var(--wp--preset--color--text-muted);max-width:52ch}
.d3-row a{color:var(--wp--preset--color--action);white-space:nowrap}
/* Flipping with order alone moved the image into the 1fr column, so the middle
   row's photograph came out 888px against 352px on its neighbours. The column
   template has to reverse with it — the fixed width stays on the image. */
.d3-row--flip{grid-template-columns:var(--d3-txt) var(--d3-pic)}
.d3-row--flip .d3-row__pic{order:2}
.d3-row--flip>div:not(.d3-row__pic){order:1}
/* Centred in the gap it sits in: the space above equals the band's own bottom
   padding, so the button is midway between the last photograph and the next
   section rather than tucked up under the photo.
   Two classes because .d3-centre is a <p> and `.d3 p{margin:0}` (0,1,1) would
   otherwise zero this — the same reset that had silently killed the project
   guide's spacing. */
.d3 .d3-centre{margin-top:var(--d3-pad)}

/* 8 · quote band — dark, centred, quote 26->34, max 520->830 */
.d3-quote{background:var(--wp--preset--color--ink);color:var(--wp--preset--color--white);text-align:center}
.d3-quote blockquote{font-size:34px;line-height:1.35;max-width:830px;margin-inline:auto;color:var(--wp--preset--color--white)}
.d3-quote__by{
	font-size:11px;letter-spacing:.1em;text-transform:uppercase;margin-top:18px;
	/* Alpha rather than opacity:.75 — identical rendering, but opacity on the
	   parent would cap the link below it, so the hover could never brighten. */
	color:rgb(255 255 255 / .75);
}
/* The attribution is the home page's only route into her story now that Meet
   Leslie has gone. Resting underline in currentColor (7.2:1 on ink) so it reads
   as a link without shouting; the two-class selector is deliberate — the global
   `main a` hover sets text-decoration-color to text-muted, which is 1.7:1 here
   and would erase the underline exactly the way it once did on the sesame band. */
.d3-quote .d3-quote__by a{
	color:inherit;
	text-decoration:underline;
	text-decoration-thickness:1px;
	text-underline-offset:4px;
	text-decoration-color:currentColor;
	transition:color 140ms var(--ease-out);
}
@media (hover:hover) and (pointer:fine){
	.d3-quote .d3-quote__by a:hover{
		color:var(--wp--preset--color--white);
		text-decoration-color:currentColor;
	}
}

/* 9 · closing CTA — heading 34->46, sub max 460->740 */
.d3-cta{text-align:center}
.d3-cta h2{font-size:46px;line-height:1.1}
.d3-cta p{font-size:17px;line-height:1.7;color:var(--wp--preset--color--text-muted);max-width:740px;margin:14px auto 26px}

/* responsive — the handoff left mobile undesigned; these follow its notes */
@media (max-width:1024px){
	:root{--d3-gutter:40px;--d3-pad:56px}
	.d3-band{flex-direction:column}
	.d3-meet{flex-direction:column}
	.d3-meet__pic{width:100%}
	.d3-meet__pic img{aspect-ratio:16/10}
}
@media (max-width:781px){
	:root{--d3-gutter:24px;--d3-pad:44px}
	.d3-benefits{grid-template-columns:1fr;gap:32px}
	/* Stacked, so the rule turns horizontal. The 32px gap sits above it and 32px
	   of padding below, which keeps it centred between the two blocks. */
	.d3-benefits > div{padding-inline:0}
	.d3-benefits > div + div{
		border-inline-start:0;
		border-block-start:1px solid var(--d3-line);
		padding-top:32px;
	}
	.d3-why__grid{grid-template-columns:1fr;gap:26px}
	.d3-row,.d3-row--flip .d3-row__pic{grid-template-columns:1fr;order:0}
	.d3-hero__wrap{position:static;padding:0}
	.d3-hero__card{max-width:none;padding:30px 24px 34px}
	/* Size the media box, not the photograph inside it. This rule predates the
	   hero becoming a stack of three cross-fading frames: back then the img WAS
	   the hero, and now it is absolutely positioned inside .d3-hero__media. So
	   this was shrinking the picture to 44vh while its container stayed at 62vh,
	   leaving ~146px of blank white under the photo on a phone. */
	.d3-hero__media{height:clamp(260px,44vh,380px)}
	.d3-quote blockquote{font-size:26px}
	.d3-cta h2{font-size:34px}
	/* A 24px control is the bare AA minimum and a poor thumb target; the icon
	   stays 24px and the padding does the work. */
	.dw-header .wp-block-navigation__responsive-container-open{
		padding:10px;margin:-10px;
	}
	/* Stacked link lists need a real target height. Links inline in a sentence
	   are exempt and keep their natural size. */
	.d3-foot__col li a,.wp-block-navigation__responsive-container-content a{
		display:inline-block;padding-block:4px;
	}
}

/* ─── Footer — the handoff's dark 4-column band ───────────────────────────
   Proto is 30px/40px on the dark ground, four columns space-between, 12px
   weight-300. Scaled to the same gutter as the rest of the port. Three columns
   plus the mark, not four: the design's fourth is a social icon row and she
   has no accounts — the same call as the press strip and the credential
   badges. Inventing profiles to fill a column is worse than a shorter row.

   The mark is her ordinary logo reversed with a filter. The file is 96%
   transparent, so brightness(0) invert(1) turns the linework white cleanly
   without needing a separate reversed asset to keep in sync. */
.d3-foot{
	background:var(--wp--preset--color--ink);
	color:var(--wp--preset--color--white);
	padding:56px var(--d3-gutter, 72px);
}
.d3-foot__inner{
	max-width:1280px;margin-inline:auto;
	display:flex;justify-content:space-between;gap:48px;flex-wrap:wrap;
	align-items:flex-start;
}
.d3-foot__mark{flex:0 0 84px}
.d3-foot__mark img{
	display:block;width:84px;height:auto;
	filter:brightness(0) invert(1);opacity:.92;
}
.d3-foot__col{flex:1 1 190px;min-width:0}
.d3-foot__col--last{flex:1 1 250px}
.d3-foot__col p{
	margin:0 0 8px;font-size:14px;line-height:1.7;
	color:var(--wp--preset--color--white);opacity:.86;
}
.d3-foot__col a{color:inherit;text-decoration:none;border-bottom:1px solid transparent}
@media (hover:hover) and (pointer:fine){
	.d3-foot__col a:hover{border-bottom-color:currentColor}
}
.d3-foot__legal{font-size:12px;opacity:.62;margin-top:14px}
@media (max-width:781px){
	.d3-foot{padding:44px 24px}
	.d3-foot__inner{gap:30px}
	.d3-foot__mark{flex:0 0 100%}
}

/* ═══ Interior pages — ports of 5a, 5b, 5e ════════════════════════════════
   Same production scaling as the home page. Where Leslie has less content
   than the reference, the grid shrinks rather than being padded: two service
   tiers not three, three projects not nine, and no filter chips for a
   portfolio that fits on a single row. */

.d3-eyebrow{
	margin:0 0 12px;font-size:11px;font-weight:700;letter-spacing:.18em;
	text-transform:uppercase;color:var(--wp--preset--color--action);
}

/* 5a · split hero — 1.05fr / 1fr, photo min-height 340 -> 540 */
.d3-split{display:grid;grid-template-columns:1.05fr 1fr;align-items:stretch}
.d3-split__text{padding:var(--d3-pad) var(--d3-gutter);display:flex;flex-direction:column;justify-content:center}
.d3-split__text h1{font-size:52px;line-height:1.08;margin-bottom:14px}
.d3-split__text .d3-lead{margin-bottom:26px}
.d3-split__pic img{display:block;width:100%;height:100%;min-height:540px;object-fit:cover}

/* 5a · process — 5 columns, gap 18 -> 28 */
/* Cotton, the lightest of the warm tones. The sesame it used to carry has moved
   down to the budget band below it, so the page steps white -> cotton -> sesame
   rather than jumping straight to the dark. */
.d3-pband{background:var(--wp--preset--color--cotton);padding:var(--d3-pad) var(--d3-gutter)}
.d3-pband h2{font-size:42px;line-height:1.15;max-width:1280px;margin:0 auto}
.d3-psteps{
	max-width:1280px;margin:38px auto 0;
	display:grid;grid-template-columns:repeat(5,1fr);gap:28px;
}
/* Icons only here, no connector. The home band threads its steps with an arrow;
   this row does not, so the sequence rests on the left-to-right reading order
   and the copy. */
.d3-pstep .dw-ic{display:block;color:var(--wp--preset--color--action);margin-bottom:8px}
.d3-pstep strong{display:block;font-size:17px;margin-bottom:4px}
.d3-pstep em{font-style:normal;font-size:14px;line-height:1.6;color:var(--wp--preset--color--text-muted)}

/* 5a · tiers — 1px border, 24px padding -> 38px, hairline-topped list */
.d3-tiers{padding:var(--d3-pad) var(--d3-gutter);max-width:1280px;margin-inline:auto}
.d3-tiers h2{font-size:42px;line-height:1.15}
.d3-tiers__grid{display:grid;grid-template-columns:1fr 1fr;gap:32px;margin-top:38px}
.d3-tier{border:1px solid var(--wp--preset--color--sesame);padding:38px}
.d3-tier--lead{background:var(--wp--preset--color--cotton)}
.d3-tier h3{font-size:28px;line-height:1.2;font-weight:600;margin:14px 0 10px}
.d3-tier__price{
	font-family:var(--wp--preset--font-family--lora);font-size:34px;font-weight:600;
	line-height:1;display:flex;align-items:baseline;gap:10px;flex-wrap:wrap;margin-bottom:12px;
}
.d3-tier__price span{
	font-family:var(--wp--preset--font-family--karla);font-size:14px;font-weight:400;
	color:var(--wp--preset--color--text-muted);
}
.d3-tier__desc{font-size:17px;line-height:1.7;color:var(--wp--preset--color--text-muted);margin-bottom:18px}
.d3-tier ul{
	list-style:none;margin:0;padding:18px 0 0;
	border-top:1px solid var(--wp--preset--color--sesame);
	font-size:14px;line-height:2;
}

/* 5a · budget band — dark, flex 1.4 / 1, bordered box */
.d3-budget{
	background:var(--wp--preset--color--ink);color:var(--wp--preset--color--white);
	padding:var(--d3-pad) var(--d3-gutter);display:flex;gap:52px;align-items:center;
}
.d3-budget__text{flex:1.4;max-width:640px}
.d3-budget h2{font-size:38px;line-height:1.2;margin-bottom:12px;color:var(--wp--preset--color--white)}
.d3-budget p{font-size:17px;line-height:1.7;opacity:.88}
.d3-budget__box{
	flex:1;border:1px solid rgba(255,255,255,.35);padding:34px;
	font-size:14px;line-height:2;
}
.d3-budget__box p{display:flex;gap:12px;align-items:center;margin-bottom:10px;opacity:1}
.d3-budget__box .dw-ic{color:var(--wp--preset--color--white);opacity:.8}

/* 5b · page intro + project grid — 3 columns, gap 16 -> 26 */
.d3-pageintro{padding:var(--d3-pad) var(--d3-gutter) 0;max-width:1280px;margin-inline:auto;text-align:center}
.d3-pageintro--left{text-align:left}
.d3-pageintro h1{font-size:52px;line-height:1.08}
.d3-pageintro .d3-lead{margin:14px auto 0}
.d3-pageintro--left .d3-lead{margin-inline:0}
.d3-grid{
	padding:44px var(--d3-gutter) var(--d3-pad);max-width:1280px;margin-inline:auto;
	display:grid;grid-template-columns:repeat(3,1fr);gap:26px;
}
.d3-tile img{display:block;width:100%;height:auto;aspect-ratio:3/2;object-fit:cover}
/* The frame clips, so the photograph can grow inside it on hover without
   nudging the tiles below. */
.d3-tile__frame{display:block;overflow:hidden;margin-bottom:16px}
.d3-tile__link,.d3-row__link{display:block;color:inherit;text-decoration:none}
/* The home project rows get the same treatment as the portfolio tiles, so the
   photograph behaves the same way wherever it is the thing you click. */
.d3-row__pic{overflow:hidden}
@media (hover:hover) and (pointer:fine){
	/* Hover feedback, the way the reference does it: the image grows a little to
	   say it is the thing you are about to click. 1.04 and 400ms — enough to
	   read as deliberate on a large photograph, small enough not to draw the eye
	   while someone is just moving the pointer across the grid. transform only,
	   so it stays on the compositor. */
	.d3-tile__link img,.d3-row__link img{transition:transform 400ms var(--ease-out)}
	.d3-tile__link:hover img,.d3-row__link:hover img{transform:scale(1.04)}
	.d3-tile__link:hover h3{text-decoration:underline;text-underline-offset:4px}
}
.d3-tile__link:focus-visible,.d3-row__link:focus-visible{outline:2px solid var(--wp--preset--color--action);outline-offset:3px}
@media (prefers-reduced-motion: reduce){
	.d3-tile__link img,.d3-row__link img{transition:none}
	.d3-tile__link:hover img,.d3-row__link:hover img{transform:none}
}

/* Project pages — header, then the photographs */
.d3-projhead{padding:var(--d3-pad) var(--d3-gutter) 0;max-width:1280px;margin-inline:auto}
.d3-projhead h1{font-size:clamp(34px,4.2vw,52px);line-height:1.06;letter-spacing:-.015em;margin-bottom:16px}
.d3-projhead__meta{
	font-size:13px;letter-spacing:.14em;text-transform:uppercase;
	color:var(--wp--preset--color--action);
}
.d3 .d3-projhead__meta{margin-bottom:4px}
.d3-projhead__intro{font-size:17px;line-height:1.7;color:var(--wp--preset--color--text-muted);max-width:62ch}
.d3 .d3-projhead__intro{margin-top:18px}
.d3-projshots{
	display:flex;flex-direction:column;gap:var(--d3-pad);
	padding:var(--d3-pad) var(--d3-gutter);max-width:1280px;margin-inline:auto;
}
.d3-shot{margin:0}
.d3-shot img{display:block;width:100%;height:auto}
.d3-shot--pair{display:grid;grid-template-columns:1fr 1fr;gap:var(--d3-gutter)}
@media (max-width:781px){
	.d3-shot--pair{grid-template-columns:1fr;gap:var(--d3-gutter)}
}
.d3-tile h3{font-size:24px;line-height:1.2;font-weight:600;margin-bottom:4px}
.d3-tile__meta{
	font-size:12px;letter-spacing:.06em;text-transform:uppercase;
	color:var(--wp--preset--color--text-muted);margin-bottom:10px;
}
.d3-tile__desc{font-size:14px;line-height:1.7;color:var(--wp--preset--color--text-muted)}

/* 5b · closing band — warm, text left, button right */
.d3-endband{
	background:var(--wp--preset--color--sesame);padding:var(--d3-pad) var(--d3-gutter);
	display:flex;gap:40px;align-items:center;justify-content:space-between;flex-wrap:wrap;
}
.d3-endband h2{font-size:42px;line-height:1.15;margin-bottom:8px}
.d3-endband p{font-size:17px;color:var(--wp--preset--color--ink);opacity:.8}

/* 5e · contact — 1.25fr / 1fr, gap 36 -> 56 */
.d3-contact{
	padding:44px var(--d3-gutter) var(--d3-pad);max-width:1280px;margin-inline:auto;
	display:grid;grid-template-columns:1.25fr 1fr;gap:56px;align-items:start;
}
.d3-aside__box,.d3-aside__area{border:1px solid var(--wp--preset--color--sesame);padding:30px 32px}
.d3-aside__area{border-top:0;background:var(--wp--preset--color--sesame)}
.d3-aside__box h2{font-size:28px;line-height:1.2;margin-bottom:16px}
.d3-aside__box p{display:flex;gap:12px;align-items:center;font-size:16px;line-height:2;margin:0}
.d3-aside__area h3{font-size:20px;font-weight:600;margin-bottom:8px}
.d3-aside__area p{font-size:14px;line-height:1.7;color:var(--wp--preset--color--ink);opacity:.85;margin:0}

@media (max-width:1024px){
	.d3-split{grid-template-columns:1fr}
	.d3-split__pic img{min-height:360px}
	.d3-psteps{grid-template-columns:repeat(2,1fr)}
	.d3-budget{flex-direction:column;align-items:stretch}
	.d3-grid{grid-template-columns:1fr 1fr}
	.d3-contact{grid-template-columns:1fr;gap:38px}
}
@media (max-width:781px){
	.d3-tiers__grid{grid-template-columns:1fr}
	.d3-psteps{grid-template-columns:1fr}
	.d3-grid{grid-template-columns:1fr}
	.d3-split__text h1,.d3-pageintro h1{font-size:36px}
}

/* ═══ Process and About — no handoff design, built on its vocabulary ══════ */

/* Process · the five steps, each given room. The home page carries them as a
   compact grid; giving them space here is the reason the page exists. */
.d3-steprows{
	padding:var(--d3-pad) var(--d3-gutter);max-width:1280px;margin-inline:auto;
	display:flex;flex-direction:column;
}
.d3-steprow{
	/* 52px gutter, not 120px: that column was sized for a 52px numeral, and an
	   icon at the site's 28px would have floated in it. */
	display:grid;grid-template-columns:52px 1fr;gap:28px;align-items:start;
	padding:38px 0;border-top:1px solid var(--wp--preset--color--sesame);
}
.d3-steprow:last-child{border-bottom:1px solid var(--wp--preset--color--sesame)}
/* Was a 52px numeral; now holds the step icon. No connector on this page — the
   rows are already hairline-separated, so a thread between them would be
   drawing a boundary that is drawn twice. */
.d3-steprow__n{
	display:block;line-height:0;
	color:var(--wp--preset--color--action);
	padding-top:2px; /* optical: aligns the icon's body with the h2 cap height */
}
.d3-steprow h2{font-size:28px;line-height:1.2;font-weight:600;margin-bottom:10px}
.d3-steprow p{font-size:17px;line-height:1.7;color:var(--wp--preset--color--text-muted);max-width:62ch}

/* About · the 3a Meet-Leslie split, opened out to a full page */
.d3-meet--page .d3-meet__text{padding-block:var(--d3-pad)}
.d3-meet--page h1{font-size:42px;line-height:1.15;margin-bottom:16px}
.d3-meet--page p{margin-bottom:14px}

/* About · credentials — three across, replacing the design's four badge tiles
   with the three things she can actually evidence */
.d3-credential{
	background:var(--wp--preset--color--snow);
	padding:52px var(--d3-gutter);
	border-block:1px solid var(--wp--preset--color--sesame);
}
.d3-credential{display:grid;grid-template-columns:repeat(3,1fr);gap:38px}
.d3-cred__item{display:flex;gap:16px;align-items:flex-start;max-width:1280px}
.d3-cred__item strong{display:block;font-size:16px;line-height:1.4;margin-bottom:4px}
.d3-cred__item em{font-style:normal;font-size:14px;color:var(--wp--preset--color--text-muted)}

/* About · the personal section, image right */
.d3-personal{
	padding:var(--d3-pad) var(--d3-gutter);max-width:1280px;margin-inline:auto;
	display:grid;grid-template-columns:1.15fr 1fr;gap:56px;align-items:center;
}
.d3-personal h2{font-size:42px;line-height:1.15;margin-bottom:14px}
.d3-personal p{font-size:17px;line-height:1.7;color:var(--wp--preset--color--text-muted);margin-bottom:14px;max-width:58ch}
.d3-personal__pic img{display:block;width:100%;height:auto;aspect-ratio:4/5;object-fit:cover}

@media (max-width:1024px){
	.d3-credential{grid-template-columns:1fr;gap:24px}
	.d3-personal{grid-template-columns:1fr;gap:32px}
	.d3-meet--page h1{font-size:32px}
}
@media (max-width:781px){
	.d3-steprow{grid-template-columns:1fr;gap:10px;padding:28px 0}
}

/* ─── Logo draw ───────────────────────────────────────────────────────────
   Front page only. The house is drawn as one continuous stroke over 3s and the
   wordmark fades in across the last 30%, per the handoff.

   The resting state is the FINISHED logo — dash offset 0, wordmark opaque — and
   the animation is layered on top of it inside a no-preference query. That
   ordering is the whole safety argument: a browser that ignores the animation,
   or a visitor who asked for reduced motion, gets her complete logo rather than
   an empty header. There is no hidden state to get stuck in, the same reasoning
   the scroll reveals use.

   `both` fill plus the default single iteration is what makes it run once and
   hold, which is what Sean asked for over the prototype's loop. It replays on
   every load of the front page, so leaving and coming back plays it again. */
/* Two classes: WordPress core ships .custom-logo-link{display:inline-block} at
   the same specificity, and it was winning on source order — which left the
   house flush against the left edge instead of centred over her name. */
.custom-logo-link.dw-logo{
	display:flex;flex-direction:column;align-items:center;
	width:170px;text-decoration:none;
	/* Her PNG carries 29px of blank above and below the artwork, which renders
	   as ~13px at this size. The lockup is built from the artwork alone, so
	   without this the home header stood 16px shorter than every other page and
	   the bar jumped as you navigated. */
	/* No top padding: the SVG's viewBox already carries 42 units of space above
	   the path, which is 7.8px here and lands the drawn house within 1.6px of
	   where the static logo puts it. 6px underneath brings the lockup to the
	   same 162px box, so the header does not move as you navigate between the
	   animated logo and the plain one. */
	padding-block:0 6px;
}
/* Percentages, not pixels: the header shrinks .dw-logo from 170px to 96px on
   scroll, and these ride that down together instead of drifting apart. 43% is
   the 73px mark against the 170px lockup; 12.44% is the 21.2px gap that lands
   the wordmark exactly where the static logo has it. */
.dw-logo__mark{display:block;width:43%;height:auto;overflow:visible}
.dw-logo__word{display:block;width:100%;height:auto;margin-top:12.44%}
.dw-logo__stroke{
	stroke-dasharray:2391.329;   /* = DW_LOGO_PATH_LEN; keep the two in step */
	stroke-dashoffset:0;         /* resting state: drawn */
}
@media (prefers-reduced-motion: no-preference){
	.dw-logo__stroke{animation:dw-logo-draw 3s cubic-bezier(0.65,0,0.35,1) both}
	/* starts at 70% of the 3s draw and runs the remaining 30%, matching the
	   prototype's clamp((p - 0.70) / 0.30, 0, 1) */
	.dw-logo__word{animation:dw-logo-word .9s linear .0s both;animation-delay:2.1s}
}
@keyframes dw-logo-draw{
	from{stroke-dashoffset:2391.329}
	to  {stroke-dashoffset:0}
}
@keyframes dw-logo-word{
	from{opacity:0}
	to  {opacity:1}
}

/* The services budget band on sesame instead of ink. Everything inside it was
   built to sit on a dark ground, so all of it inverts: white text at 1.4:1 on
   sesame would have been unreadable. Ink on sesame is 8.24:1, comfortably past
   the AAA bar this project enforces, and the paragraph's .88 opacity goes with
   it — dimmed ink lands at 6.1:1, which passes AA but not the AAA this palette
   is held to. The icons take action green (5.34:1), matching every other icon
   on the site. Scoped to the modifier so the process page's own budget band
   stays dark. */
.d3-budget--warm{
	background:var(--wp--preset--color--sesame);
	color:var(--wp--preset--color--ink);
}
.d3-budget--warm h2{color:var(--wp--preset--color--ink)}
.d3-budget--warm p{opacity:1}
/* Filled white and rounded, rather than an outlined square. The fill defines
   the panel on the sesame ground (1.34:1), so the border comes off — an edge
   and a fill both drawing the same shape is one line too many. Ink on white
   inside is 11.32:1 and the icons are 7.33:1.

   It borrows the control radius rather than introducing a second value: the
   site has one radius, and one is the point. */
.d3-budget--warm .d3-budget__box{
	background:var(--wp--preset--color--white);
	border:0;
	border-radius:var(--wp--custom--radius--control);
}
.d3-budget--warm .d3-budget__box .dw-ic{
	color:var(--wp--preset--color--action);opacity:1;
}

/* The logo at phone size. 170px is 45% of a 375px screen, and the header was
   eating a quarter of the viewport before anyone saw a photograph. This has to
   sit after the base .custom-logo-link.dw-logo rule — same specificity, so
   source order decides, and the earlier media block was silently losing. The
   lockup is built in percentages, so one width carries the mark and the
   wordmark down together. */
@media (max-width:781px){
	.custom-logo-link.dw-logo{width:112px}
	.dw-header .wp-block-site-logo img{width:112px}
}
