/*
Theme Name: Leafbird
Theme URI: https://leafbirdconsulting.com
Author: Leafbird Consulting LLC
Description: Block theme for Leafbird Consulting LLC - a climate, water and solar consultancy in Davis, California. Structured and evidence-led: labelled sections, disciplined photography, and project metadata given equal billing to prose. Design tokens live in theme.json.
Requires at least: 6.6
Tested up to: 7.0
Requires PHP: 8.0
Version: 0.1.0
License: GNU General Public License v2 or later
License URI: https://www.gnu.org/licenses/gpl-2.0.html
Text Domain: leafbird
Tags: blog, portfolio, one-column, block-patterns, full-site-editing
*/

/*
 * Deliberately almost empty.
 *
 * Everything visual is defined in theme.json, so that the palette, type scale
 * and spacing are inherited by every block AND presented as the only choices in
 * the editor. That is what keeps pages consistent when someone other than the
 * theme author adds content.
 *
 * Add CSS here only for things theme.json genuinely cannot express.
 */

/*
 * Let long unbreakable strings wrap.
 *
 * Publications carries bare URLs and DOIs inside citations, and one of them - a RAND
 * report link - is a single token wider than a phone screen. It pushed the whole page
 * sideways at 375px, which is the exact bug the redesign exists to fix, reappearing on
 * the one page nobody would think to check on a phone.
 *
 * theme.json has no way to express overflow-wrap, so it lives here.
 */
.wp-block-post-content p,
.wp-block-post-content li,
.wp-block-post-content figcaption,
.wp-block-post-content td {
	overflow-wrap: break-word;
}

/*
 * The homepage hero: fixed-height photograph, caption laid over it.
 *
 * `object-fit: cover` with a fixed height is what does the work, and it does exactly what
 * a hero needs: the image fills the band, keeps its own aspect ratio, and is cropped -
 * never stretched out of shape - when the band is a different shape to the photograph.
 * `object-position: center` decides which part survives that crop. A picture smaller than
 * the band is scaled up to fill it, same rule, same aspect ratio.
 *
 * The height is capped by viewport height as well: a tall band on a short laptop screen
 * is the whole window, and the reader should be able to see that the page continues
 * below it.
 */
.wp-block-image.is-style-hero {
	position: relative;
	margin-block: 0;
}

.wp-block-image.is-style-hero img {
	display: block;
	width: 100%;
	height: min(400px, 62vh);
	object-fit: cover;
	object-position: center;
}

.wp-block-image.is-style-hero figcaption {
	position: absolute;
	left: 0;
	right: 0;
	bottom: var(--wp--preset--spacing--30);
	margin-block: 0;
	color: var(--wp--preset--color--paper);
	font-style: italic;
	/* The photograph behind the caption is not a known quantity - it changes whenever an
	   editor swaps the image - so the caption carries its own contrast. */
	text-shadow: 0 1px 5px rgb(0 0 0 / 75%);
}

@media (max-width: 782px) {
	.wp-block-image.is-style-hero img {
		height: min(320px, 45vh);
	}
}

/*
 * A full-bleed figure's caption still belongs to the text column.
 *
 * The photograph runs edge to edge; its caption should not. Without this the hero caption
 * starts at x=0, hard against the window edge, reading as a stray line of page furniture
 * rather than as words about the picture above it.
 */
.wp-block-image.alignfull > figcaption {
	max-width: var(--wp--style--global--wide-size);
	margin-inline: auto;
	padding-inline: var(--wp--preset--spacing--50);
}

/*
 * The two crops, as styles. Registered in functions.php, where the reasoning is.
 *
 * `width: 100%` matters as much as the ratio: without it a picture narrower than its column
 * sits at its own size, and the band is only as wide as the file happened to be - which is
 * the opposite of what a crop is for.
 */
.wp-block-image.is-style-crop-16-9 img,
.wp-block-image.is-style-crop-4-3 img,
.wp-block-post-featured-image.is-style-crop-16-9 img,
.wp-block-post-featured-image.is-style-crop-4-3 img {
	display: block;
	width: 100%;
	object-fit: cover;
}

.wp-block-image.is-style-crop-16-9 img,
.wp-block-post-featured-image.is-style-crop-16-9 img {
	aspect-ratio: 16 / 9;
}

.wp-block-image.is-style-crop-4-3 img,
.wp-block-post-featured-image.is-style-crop-4-3 img {
	aspect-ratio: 4 / 3;
}

/*
 * A picture in the flow of a page is never taller than a 16:9 one at the wide measure.
 *
 * The three project pages that open with a photograph crop it to a 16:9 band, and that
 * crop quietly does two jobs: it makes a set of pictures read as one sequence, and it caps
 * how much of the screen the opening image can take. Village Ecosystems opens with a map,
 * which must not be cropped - a crop takes the scale bar off it - and so it had no cap at
 * all, and stood half again as tall as every other project page's opener.
 *
 * The cap is that same height, written as the ratio rather than as the number it currently
 * works out to, so the two stay tied if the wide measure ever changes: 76rem at 16:9 is
 * 42.75rem, or 684px. A page can now open with a cropped photograph or an uncropped
 * document and both stop at the same line.
 *
 * **Height, not width, and the difference matters.** A width cap here would be circular
 * for the reason set out under the figure box below: once an image has a `srcset` the
 * browser reports its intrinsic width divided by the density it picked. A height cap is an
 * absolute length that never consults the image, so it stays honest.
 *
 * The image is not stretched or cropped to meet the cap: `width: auto` leaves the browser
 * to shrink the other side to match, which is the whole point - the picture gets smaller,
 * never distorted, never trimmed.
 *
 * The block keeps the wide measure while the picture inside it does not. Every other block
 * on the page starts at the same left edge, and a figure that shrank to its picture would
 * be the one thing that did not - a narrower block reads as a different kind of thing,
 * which this is not. So the figure stays wide, the picture is centred inside it, and the
 * caption keeps the same width it has under a full-width picture on every other page.
 *
 * The picture is therefore narrower than its caption whenever the cap bites. That is the
 * accepted cost of the block lining up, and it is the smaller of the two oddities.
 *
 * Excluded, deliberately:
 *
 * - `is-style-hero`, which is a full-bleed band with its own height and its own viewport
 *   cap, and is meant to be cropped.
 * - the two crop styles, which are deliberate crops carrying a ratio of their own. Those
 *   are already exactly this tall, so the cap would do nothing - but `width: auto` would
 *   undo the band they were cropped into. Selecting on the style name rather than on the
 *   inline `aspect-ratio` that used to sit there means the rule reads as what it means.
 * - anything nested, because the selector takes only direct children of the content: the
 *   photo strip's images and the figure box's columns size themselves.
 */
.entry-content > .wp-block-image:not(.is-style-hero):not(.is-style-crop-16-9):not(.is-style-crop-4-3) img {
	display: block;
	width: auto;
	max-height: calc( var(--wp--style--global--wide-size) * 9 / 16 );
	margin-inline: auto;
}

/*
 * A figure that sits directly in a figure box fills it, unless it says otherwise.
 *
 * The pictures inside the box's columns fill their column because a column is a flex
 * child with a width. A figure moved into the box on its own has no such constraint, so
 * it falls back to its intrinsic width and sits short of the border with white space
 * beside it - reading as a mistake rather than as part of the group.
 *
 * This is only the default. An image that carries a width of its own - the Image block's
 * ordinary width control, which writes an inline `width` - overrides it, because an
 * inline style beats a stylesheet. That is the opt-out, and it is the standard control
 * rather than something invented here, so it survives editing and needs no explanation.
 *
 * **A cap cannot be expressed here.** `width: auto` and `max-width: max-content` both
 * look like they would stop a small picture being enlarged, and neither does: once an
 * image has a `srcset`, the browser reports its intrinsic width *divided by the density
 * it picked*, which resolves to the stretched width and makes the rule circular. The
 * only honest source for "how wide is this file" is the media library, so the cap is
 * written per image by docs/tools/cap-native-width.php. See the log.
 *
 * Scoped to a direct child so it cannot reach the images inside the columns, and to the
 * figure box so the Document style, where a map is deliberately shown at its own size,
 * is untouched.
 */
.wp-block-group.is-style-figure-box > .wp-block-image img {
	width: 100%;
	max-width: 100%;
}

/*
 * A figure box can hold several rows, each row a run of pictures under its own caption.
 *
 * The caption sits close to the pictures it describes - that spacing is on the paragraph
 * itself, at spacing 20. What needs saying here is the gap *between* rows, which has to
 * be clearly larger than the gap between a row and its own caption, or a caption reads as
 * belonging to the row below it. Spacing 50 - the same 24px the theme puts between any
 * two blocks, so a row inside a card is spaced like everything else on the site rather
 * than to a rule of its own. It was spacing 40, which read tighter here than the same
 * arrangement does on Agriculture.
 *
 * Written as `* + row` rather than as a class on each row, so the rule is about position
 * rather than about markup: an editor who adds a fourth row in the block editor gets the
 * spacing without knowing this exists, and the first row never carries a stray top
 * margin.
 */
.wp-block-group.is-style-figure-box > * + .wp-block-columns,
.wp-block-group.is-style-figure-box > * + .wp-block-image {
	margin-top: var(--wp--preset--spacing--50);
}

/*
 * A card's own heading, which lives inside the card. Flush to the top: the box's padding
 * is already the space, and the heading's own margin on top of it would double it.
 */
.wp-block-group.is-style-figure-box > .wp-block-heading:first-child {
	margin-top: 0;
}

/*
 * A picture inside a row is left at its own size and never enlarged.
 *
 * This is the opposite of the rule above for a figure sitting directly in the box, and
 * deliberately so. A column already gives its picture a width to fill, so photographs -
 * all of which are far wider than any column here - fill it anyway. The exception that
 * matters is the Bengaluru groundwater animation: 600px square, against twelve 1920px
 * landscapes. Forcing it to the column width would upscale it into exactly the soft,
 * stretched rendering this page is being rebuilt to get rid of.
 *
 * So it keeps its own size and is centred in its column instead. Centring is what makes
 * it read as a deliberately small picture rather than as one that failed to load, which
 * is how it looks pushed against the left edge with a gap beside it.
 */
.wp-block-group.is-style-figure-box .wp-block-column > .wp-block-image {
	text-align: center;
}

/*
 * Tags: the capability words each service area carries, as chips.
 *
 * On the old site these words were drawn into the cover illustration in a handwriting
 * face at low contrast - unreadable, unselectable, unindexed. They are the most concrete
 * thing the cards say, so they get to be text.
 *
 * This is here rather than in styles/tags.json because a bordered core/list variation
 * makes WordPress emit an empty :not() selector, which kills the rule. See the log.
 * Every value still comes from theme.json, so the tags stay on the design system.
 */
.wp-block-list.is-style-tags {
	list-style: none;
	padding-left: 0;
	margin-block: var(--wp--preset--spacing--30);
	display: flex;
	flex-wrap: wrap;
	gap: var(--wp--preset--spacing--20);
}

.wp-block-list.is-style-tags > li {
	margin: 0;
	padding: 0.15em 0.6em;
	border: 1px solid var(--wp--preset--color--rule);
	border-radius: 2px;
	font-size: var(--wp--preset--font-size--small);
	line-height: 1.5;
	color: var(--wp--preset--color--ink-muted);
	white-space: nowrap;
}

/* A long capability name is allowed to wrap rather than push the card wider. */
@media (max-width: 782px) {
	.wp-block-list.is-style-tags > li {
		white-space: normal;
	}
}

/*
 * The photo strip: a project's photographs, three across, at the foot of its page.
 *
 * Storage is an ordinary core/gallery, so the pictures live in post content and the
 * media library where an editor already knows how to find them - not in a plugin's own
 * tables, which is how thirty photographs and their captions became invisible on the old
 * site. Everything below is presentation over that gallery. Remove the style and the
 * pictures are still there.
 *
 * Grid rather than the gallery's own flex layout, because flex distributes leftover
 * space between however many items there are: two pictures in a flex row grow to half
 * the width each, so a two-picture project would show noticeably bigger photographs than
 * a four-picture one. Three fixed columns keep every photograph on this site the same
 * size, and a short row simply leaves its third cell empty.
 *
 * Several rules here are heavier than anything else in this file, and each says why. The
 * gallery block ships a lot of opinionated CSS - captions laid over the image, images
 * flexed to fill - and a theme that wants a different arrangement has to outrank it
 * rather than hope to be loaded later, which is not something a theme controls.
 */
.wp-block-gallery.is-style-photo-strip {
	display: grid;
	grid-template-columns: repeat(3, 1fr);
	gap: var(--wp--preset--spacing--40);
	/* The gallery block ships its own flex gap variable, which grid ignores; zeroing it
	   stops the two spacings being added together on browsers that apply both. */
	--wp--style--unstable-gallery-gap: 0;
}

/*
 * Each picture is an ordinary block box, deliberately not a flex container.
 *
 * It was flex, so that the caption could grow and keep the row even. That quietly broke
 * the aspect ratio: as a flex item the image takes its height from flex layout rather
 * than from `aspect-ratio`, and since each photograph's intrinsic height is different,
 * every picture in the row came out a different height - 296px, 148px, 172px, from three
 * files that were all supposed to be 4:3. The grid row already makes the cells equal
 * height, so flex was never needed to do that job.
 */
.wp-block-gallery.is-style-photo-strip.has-nested-images figure.wp-block-image {
	display: block;
	margin: 0;
	width: auto;
}

/*
 * A picture the script has paged away is hidden, and this is what makes that true.
 *
 * The browser's own rule for the `hidden` attribute is `[hidden] { display: none }` at
 * the very bottom of the cascade, so the `display` above beats it. The result was a strip
 * where paging changed nothing visible: `figure.hidden` was correctly true, the element
 * was correctly marked, and it stayed on screen. It only showed up in a screenshot -
 * every computed style read back exactly as intended.
 */
.wp-block-gallery.is-style-photo-strip.has-nested-images figure.wp-block-image[hidden] {
	display: none;
}

/*
 * 4:3, cropped, never stretched.
 *
 * The same shape as the cards on the Projects index, so the site has one picture
 * proportion rather than a different one per page. `cover` is what makes the crop
 * honest: the photograph keeps its own aspect ratio and loses its edges, instead of being
 * squashed to fit a frame it was never shot for. A portrait photograph and a landscape
 * one therefore sit in identical frames, which is the whole point of a strip.
 *
 * Two selectors, and the reason is worth writing down. The gallery block's "Crop images"
 * setting adds `is-cropped` and with it this rule:
 *
 *   .wp-block-gallery.has-nested-images.is-cropped
 *     figure.wp-block-image:not(#individual-image) img { flex: 1 0 0%; height: 100% }
 *
 * That `:not(#individual-image)` matches nothing - there is no such element - and is
 * there purely to buy the selector an id's worth of specificity. No selector built out of
 * classes can outrank it, however many classes it has, so the obvious rule here lost
 * silently and the pictures took their height from flex layout instead of from the
 * aspect ratio: 296px, 148px and 172px from three files all meant to be 4:3.
 *
 * So: the first selector borrows the same trick and adds `is-cropped` on top, which wins
 * outright when the toggle is on. The second is the plain rule for when it is off. Both
 * are here so the strip looks the same either way and an editor can flip that switch
 * without discovering this file.
 */
.wp-block-gallery.is-style-photo-strip.has-nested-images.is-cropped figure.wp-block-image:not(#individual-image) img,
.wp-block-gallery.is-style-photo-strip.has-nested-images figure.wp-block-image img {
	display: block;
	width: 100%;
	height: auto;
	aspect-ratio: 4 / 3;
	object-fit: cover;
	object-position: center;
	/* The figure is a block box, so this is inert - but it is what undoes `flex: 1 0 0%`
	   if some future core change makes the image a flex item again. */
	flex: none;
}

/*
 * Captions are the reason this is a gallery and not a row of decoration.
 *
 * The old carousels' captions were the most substantive thing in them, so a caption here
 * is body-sized and left-aligned like the ones inside a figure box, rather than the small
 * centred grey the gallery block defaults to.
 *
 * The min-height reserves two lines whether or not a picture has a caption. Without it
 * the strip changes height as the reader pages through - the buttons move out from under
 * the cursor between one click and the next, which is the small thing that makes a
 * control feel broken.
 */
.wp-block-gallery.is-style-photo-strip.has-nested-images.is-cropped figure.wp-block-image:not(#individual-image) figcaption,
.wp-block-gallery.is-style-photo-strip.has-nested-images figure.wp-block-image figcaption {
	/* The gallery block lays its captions *over* the bottom of the photograph, white on a
	   dark gradient, absolutely positioned. That is a caption as a label on a picture;
	   this site's captions are sentences that are read, so the position is undone first
	   and the caption becomes an ordinary block under the image. Every property in this
	   group is switching off a specific thing core sets - none of it is decoration. */
	position: static;
	max-height: none;
	width: auto;
	background: none;
	padding: 0;

	margin-top: var(--wp--preset--spacing--20);
	min-height: calc(2 * 1.55 * var(--wp--preset--font-size--small));
	color: var(--wp--preset--color--ink-muted);
	font-size: var(--wp--preset--font-size--small);
	font-style: normal;
	line-height: 1.55;
	text-align: left;
	overflow-wrap: break-word;
}

/*
 * The fade a picture arrives with. Presentation only - the page is already correct
 * before it runs - so it is safe to switch off entirely for a reader who has asked for
 * less motion, which the media query below does.
 */
.wp-block-gallery.is-style-photo-strip .wp-block-image.is-entering {
	animation: leafbird-photo-strip-in 220ms ease-out both;
}

@keyframes leafbird-photo-strip-in {
	from { opacity: 0; }
	to   { opacity: 1; }
}

@media (prefers-reduced-motion: reduce) {
	.wp-block-gallery.is-style-photo-strip .wp-block-image.is-entering {
		animation: none;
	}
}

/*
 * The paging controls, which exist only when JavaScript has built them, and only when
 * there is more than one page to move between.
 */
.photo-strip-controls {
	display: flex;
	align-items: center;
	gap: var(--wp--preset--spacing--30);
	margin-top: var(--wp--preset--spacing--40);
}

.photo-strip-controls[hidden] {
	display: none;
}

/*
 * The controls line up with the left edge of the pictures, not with the text column.
 *
 * The strip is a wide block inside a constrained layout, so it is 1216px while the
 * paragraphs above it are 704px. The controls are an ordinary sibling and would take the
 * text width, leaving them floating 256px inside the strip they belong to.
 * photo-strip.js copies the gallery's alignment class onto them so the layout treats both
 * the same way.
 */
.photo-strip-button {
	display: flex;
	align-items: center;
	justify-content: center;
	width: 2.25rem;
	height: 2.25rem;
	padding: 0;
	border: 1px solid var(--wp--preset--color--rule);
	border-radius: 3px;
	background: var(--wp--preset--color--card);
	color: var(--wp--preset--color--ink-muted);
	font-size: var(--wp--preset--font-size--medium);
	line-height: 1;
	cursor: pointer;
}

.photo-strip-button:hover:not(:disabled) {
	border-color: var(--wp--preset--color--moss);
	color: var(--wp--preset--color--forest);
}

/*
 * A control at the end of the strip is disabled rather than hidden. A button that
 * disappears at the last page takes the one beside it along the row with it, and the
 * reader's next click lands on whatever moved into that spot.
 */
.photo-strip-button:disabled {
	opacity: 0.4;
	cursor: default;
}

.photo-strip-dots {
	display: flex;
	align-items: center;
	gap: var(--wp--preset--spacing--20);
}

.photo-strip-dot {
	width: 0.5rem;
	height: 0.5rem;
	padding: 0;
	border: 1px solid var(--wp--preset--color--ink-faint);
	border-radius: 50%;
	background: none;
	cursor: pointer;
}

.photo-strip-dot[aria-current="true"] {
	border-color: var(--wp--preset--color--forest);
	background: var(--wp--preset--color--forest);
}

/*
 * "Page 2 of 3", for a screen reader only. It is announced on every change so a reader
 * who cannot see the dots still knows where they are in the strip; sighted readers have
 * the dots, so it is not drawn.
 *
 * Clipped rather than `display: none` or `visibility: hidden`, both of which remove the
 * text from the accessibility tree and would make the live region silent.
 */
.photo-strip-status {
	position: absolute;
	width: 1px;
	height: 1px;
	margin: -1px;
	padding: 0;
	overflow: hidden;
	clip-path: inset(50%);
	white-space: nowrap;
	border: 0;
}

/*
 * Keyboard focus must stay visible. The controls set their own background and border,
 * which on some browsers replaces the default focus ring with nothing at all.
 */
.photo-strip-button:focus-visible,
.photo-strip-dot:focus-visible {
	outline: 2px solid var(--wp--preset--color--moss);
	outline-offset: 2px;
}

/*
 * One picture per row on a phone. Three 4:3 pictures across 390px is about 110px each,
 * which is smaller than the caption under it and reads as a row of thumbnails rather
 * than as photographs. photo-strip.js pages by one at this width - the breakpoint here
 * and the one in that file have to agree.
 */
@media (max-width: 781px) {
	.wp-block-gallery.is-style-photo-strip {
		grid-template-columns: 1fr;
	}

	/* Nothing is beside a caption at this width, so nothing has to line up with it, and
	   reserved empty space under a lone picture is just a gap. */
	.wp-block-gallery.is-style-photo-strip.has-nested-images.is-cropped figure.wp-block-image:not(#individual-image) figcaption,
	.wp-block-gallery.is-style-photo-strip.has-nested-images figure.wp-block-image figcaption {
		min-height: 0;
	}
}

/*
 * Project status badges
 * ---------------------
 * Two pills in a project's sidebar: green tick for the phase that is done, orange play
 * mark for the one ahead. The one-pagers do this with hard-coded greens and ambers; here
 * every colour is mixed from the theme's own moss and ochre, so the badges belong to the
 * same palette as the rest of the page and follow it if the palette ever changes.
 *
 * The ochre is pulled towards ink before it is used as text: ochre on its own tint is
 * about 3:1, which is below AA for text this size. Mixed 70/30 with ink it is above 5:1.
 *
 * display:flex with width:fit-content rather than inline-flex, because the field wants one
 * status per line and a pill that stops at its words.
 *
 * The glyph is a ::before rather than typed into the paragraph so that the editor writes
 * words and nothing else. The empty alt string after the slash keeps it out of the
 * accessible name - the words already say what the state is.
 */
.is-style-status-done,
.is-style-status-next,
.is-style-status-active {
	display: flex;
	width: fit-content;
	align-items: baseline;
	gap: 0.5em;
	margin-top: var(--wp--preset--spacing--20);
	padding: 0.3em 0.85em;
	border: 1px solid;
	border-radius: 999px;
	font-size: var(--wp--preset--font-size--x-small);
	font-weight: 600;
	line-height: 1.4;
}

.is-style-status-done::before,
.is-style-status-next::before,
.is-style-status-active::before {
	flex: none;
	font-size: 0.9em;
	line-height: 1;
}

.is-style-status-done {
	color: var(--wp--preset--color--forest);
	border-color: color-mix( in srgb, var(--wp--preset--color--moss) 40%, transparent );
	background-color: color-mix( in srgb, var(--wp--preset--color--moss) 10%, transparent );
}

.is-style-status-done::before {
	content: '✓' / '';
}

.is-style-status-next {
	color: color-mix( in srgb, var(--wp--preset--color--ochre) 70%, var(--wp--preset--color--ink) );
	border-color: color-mix( in srgb, var(--wp--preset--color--ochre) 45%, transparent );
	background-color: color-mix( in srgb, var(--wp--preset--color--ochre) 12%, transparent );
}

.is-style-status-next::before {
	content: '▶' / '';
}

/*
 * Ongoing sits in the same green as complete, because both are states a reader is glad to
 * see, and it would be strange to warn about a project that is simply running. The dot
 * carries the difference: a tick closes something, a dot is a thing still lit.
 */
.is-style-status-active {
	color: var(--wp--preset--color--forest);
	border-color: color-mix( in srgb, var(--wp--preset--color--moss) 40%, transparent );
	background-color: color-mix( in srgb, var(--wp--preset--color--moss) 10%, transparent );
}

.is-style-status-active::before {
	content: '●' / '';
}

/*
 * The project pages
 * -----------------
 * Both rules are scoped by the page-project body class, so they reach all four projects
 * and nothing else on the site. They were noticed on one page, but neither is about that
 * page - they are about the shape every project write-up has.
 */

/*
 * A project that opens with a wide photograph gets a little more air under it than the
 * global block gap of 24px gives. 32px, chosen by eye.
 *
 * Not a step on the theme's spacing scale, which goes 24 then 36, so it is written as 2rem
 * rather than promoted to a preset that nothing else would use. If the scale ever grows a
 * 32px step this should become that preset.
 *
 * Anchored on the picture rather than on the page, so the one project that does not open
 * with one - Lake County Agriculture, which has no photograph at all - keeps the ordinary
 * gap instead of an unexplained one.
 *
 * Village Ecosystems used to be the second such page, opening with a document box rather
 * than a picture. The box came off in review and its map is now a wide figure like the
 * others, so this rule reaches it too, which is the right outcome: the page opens with a
 * wide image and wants the same air under it.
 */
.page-template-page-project .entry-content > .wp-block-image:first-child + * {
	margin-top: 2rem;
}

/*
 * The write-up is 64% and the metadata sidebar 32%, so the pair does not fill the row.
 * Flex leaves the spare 4% at the end, which pulls the sidebar in from the wide measure
 * and leaves it hanging short of the edge every other wide block reaches. space-between
 * spends that space in the gutter instead: the sidebar lines up with the photograph above
 * it, and the two columns are further apart, which is what separates them.
 *
 * Below 782px WordPress wraps the columns to full width each, and space-between has
 * nothing left to distribute - so this rule stops applying on its own at the breakpoint
 * rather than needing a media query to switch it off.
 */
.page-template-page-project .entry-content .wp-block-columns {
	justify-content: space-between;
}

/*
 * The lead-in of a deliverable or a finding
 * -----------------------------------------
 * Each item in these two lists opens with a few bold words and then an em dash, so a
 * reader can scan the openings and stop at the one they want. Colouring the opening makes
 * that scan work at a glance, and it tells the two lists apart when a page carries both:
 * green is what we did, orange is what we found.
 *
 * The two colours are the one-pagers' own, kept as settings.custom in theme.json rather
 * than added to the palette - they belong to these two lists and offering them as swatches
 * on every block would be the opposite of what a curated palette is for.
 *
 * Scoped to the two group styles rather than to bold text generally: bold inside ordinary
 * prose is emphasis and should stay the colour of the prose around it.
 */
.is-style-deliverables li strong {
	color: var(--wp--custom--deliverable--lead);
}

.is-style-findings li strong {
	color: var(--wp--custom--finding--lead);
}

/*
 * Key terms inside the prose
 * --------------------------
 * The one-pagers pick out two kinds of thing mid-sentence, and both do a job a reader
 * feels rather than notices. A key fact or name - a figure, a funding programme, the
 * client's initials - is set in the green and bolded. A named piece of the work gets a
 * tinted ground as well, so the three components of a project can be counted straight out
 * of the paragraph that describes them, without the paragraph turning into a list.
 *
 * Written as <mark> rather than <span> because the block editor knows <mark>: it is the
 * element its own highlight format uses. An admin user editing one of these paragraphs
 * keeps the markup, instead of the editor quietly dropping an element it does not
 * recognise the next time the paragraph is touched.
 *
 * No horizontal padding on the tinted one. The one-pager pads it 3px each side, which
 * reads as a space held before the word and leaves the tint looking a step out of line
 * with the text around it. Here the tint hugs the term.
 */
mark.highlight-key,
mark.highlight-term {
	background: none;
	color: var(--wp--custom--highlight--text);
}

mark.highlight-key {
	font-weight: 700;
}

mark.highlight-term {
	padding: 0.1em 0;
	border-radius: 2px;
	background-color: var(--wp--custom--highlight--background);
	font-weight: 600;
}
